add CvMat#snake_image (only for OpenCV 2)

This commit is contained in:
ser1zw 2017-06-04 16:09:39 +09:00
parent 65999bf534
commit 33e96195e2
2 changed files with 191 additions and 5 deletions

View File

@ -14,6 +14,7 @@
#ifdef IS_OPENCV2
# include "opencv2/contrib/contrib.hpp"
# include "opencv2/legacy/legacy.hpp"
#else
# include "opencv2/video/tracking_c.h"
# include "opencv2/calib3d/calib3d_c.h"
@ -5606,6 +5607,95 @@ namespace mOpenCV {
#endif
}
/*
* call-seq:
* snake_image(points, alpha, beta, gamma, window, criteria[, calc_gradient = true]) -> array(pointset)
*
* Updates snake in order to minimize its total energy that is a sum of internal energy
* that depends on contour shape (the smoother contour is, the smaller internal energy is)
* and external energy that depends on the energy field and reaches minimum at the local energy
* extremums that correspond to the image edges in case of image gradient.
* The parameter criteria.epsilon is used to define the minimal number of points that must be moved
* during any iteration to keep the iteration process running.
*
* If at some iteration the number of moved points is less than criteria.epsilon or
* the function performed criteria.max_iter iterations, the function terminates.
*
* points
* Contour points (snake).
* alpha
* Weight[s] of continuity energy, single float or array of length floats, one per each contour point.
* beta
* Weight[s] of curvature energy, similar to alpha.
* gamma
* Weight[s] of image energy, similar to alpha.
* window
* Size of neighborhood of every point used to search the minimum, both win.width and win.height must be odd.
* criteria
* Termination criteria.
* calc_gradient
* Gradient flag. If not 0, the function calculates gradient magnitude for every image pixel and consideres
* it as the energy field, otherwise the input image itself is considered.
*/
VALUE
rb_snake_image(int argc, VALUE *argv, VALUE self)
{
#ifdef IS_OPENCV2
VALUE points, alpha, beta, gamma, window, criteria, calc_gradient;
rb_scan_args(argc, argv, "61", &points, &alpha, &beta, &gamma, &window, &criteria, &calc_gradient);
CvPoint *pointset = 0;
int length = CVPOINTS_FROM_POINT_SET(points, &pointset);
int coeff = (TYPE(alpha) == T_ARRAY && TYPE(beta) == T_ARRAY && TYPE(gamma) == T_ARRAY) ? CV_ARRAY : CV_VALUE;
float *a = 0, *b = 0, *c = 0;
IplImage stub;
int i;
if (coeff == CV_VALUE) {
float buff_a, buff_b, buff_c;
buff_a = (float)NUM2DBL(alpha);
buff_b = (float)NUM2DBL(beta);
buff_c = (float)NUM2DBL(gamma);
a = &buff_a;
b = &buff_b;
c = &buff_c;
}
else { // CV_ARRAY
if ((RARRAY_LEN(alpha) != length) ||
(RARRAY_LEN(beta) != length) ||
(RARRAY_LEN(gamma) != length))
rb_raise(rb_eArgError, "alpha, beta, gamma should be same size of points");
a = ALLOCA_N(float, length);
b = ALLOCA_N(float, length);
c = ALLOCA_N(float, length);
for (i = 0; i < length; ++i) {
a[i] = (float)NUM2DBL(RARRAY_PTR(alpha)[i]);
b[i] = (float)NUM2DBL(RARRAY_PTR(beta)[i]);
c[i] = (float)NUM2DBL(RARRAY_PTR(gamma)[i]);
}
}
CvSize win = VALUE_TO_CVSIZE(window);
CvTermCriteria tc = VALUE_TO_CVTERMCRITERIA(criteria);
try {
cvSnakeImage(cvGetImage(CVARR(self), &stub), pointset, length,
a, b, c, coeff, win, tc, IF_BOOL(calc_gradient, 1, 0, 1));
}
catch (cv::Exception& e) {
if (pointset != NULL)
cvFree(&pointset);
raise_cverror(e);
}
VALUE result = rb_ary_new2(length);
for (i = 0; i < length; ++i)
rb_ary_push(result, cCvPoint::new_object(pointset[i]));
cvFree(&pointset);
return result;
#else
raise_opencv3_unsupported();
return Qnil;
#endif
}
void
init_ruby_class()
{
@ -5862,6 +5952,7 @@ namespace mOpenCV {
rb_define_method(rb_klass, "mean_shift", RUBY_METHOD_FUNC(rb_mean_shift), 2);
rb_define_method(rb_klass, "cam_shift", RUBY_METHOD_FUNC(rb_cam_shift), 2);
rb_define_method(rb_klass, "snake_image", RUBY_METHOD_FUNC(rb_snake_image), -1);
rb_define_singleton_method(rb_klass, "find_fundamental_mat",
RUBY_METHOD_FUNC(rb_find_fundamental_mat), -1);

View File

@ -225,7 +225,7 @@ class TestCvMat_imageprocessing < OpenCVTestCase
[39, 159], [79, 159], [119, 159], [159, 159]]
refined_corners = mat.find_corner_sub_pix(corners, CvSize.new(3, 3), CvSize.new(-1, -1),
CvTermCriteria.new(20, 0.03));
CvTermCriteria.new(20, 0.03));
assert_equal(expected.size, refined_corners.size)
assert(found)
expected.zip(refined_corners).each { |e, a|
@ -440,10 +440,10 @@ class TestCvMat_imageprocessing < OpenCVTestCase
def test_get_perspective_transform
from = [
OpenCV::CvPoint2D32f.new(540, 382),
OpenCV::CvPoint2D32f.new(802, 400),
OpenCV::CvPoint2D32f.new(850, 731),
OpenCV::CvPoint2D32f.new(540, 731),
OpenCV::CvPoint2D32f.new(540, 382),
OpenCV::CvPoint2D32f.new(802, 400),
OpenCV::CvPoint2D32f.new(850, 731),
OpenCV::CvPoint2D32f.new(540, 731),
]
to = [
OpenCV::CvPoint2D32f.new(0, 0),
@ -1741,4 +1741,99 @@ class TestCvMat_imageprocessing < OpenCVTestCase
assert_in_delta(0.0033327, mat_cv.match_shapes(mat_ov, method), 0.00001)
}
end
def test_snake_image
omit_unless(IS_OPENCV2, 'CvMat#snake_image is not supported in OpenCV 3 or later')
radius = 40
center = CvPoint.new(128, 128)
mat = CvMat.new(center.y * 2, center.x * 2, :cv8u, 1).zero!
mat.circle!(center, radius, :color => CvColor::White, :thickness => -1)
num_points = 10
alpha = 0.05
beta = 0.05
gamma = 0.9
arr_alpha = [alpha] * num_points
arr_beta = [beta] * num_points
arr_gamma = [gamma] * num_points
size = CvSize.new(3, 3)
term_criteria = CvTermCriteria.new(100, num_points / 2)
# initialize contours
points = []
num_points.times { |i|
x = center.x * Math.cos(2 * Math::PI * i / num_points) + center.x
y = center.y * Math.sin(2 * Math::PI * i / num_points) + center.y
points << CvPoint.new(x, y)
}
acceptable_error = 50
# test snake_image
# calc_gradient = true
[mat.snake_image(points, alpha, beta, gamma, size, term_criteria),
mat.snake_image(points, alpha, beta, gamma, size, term_criteria, true),
mat.snake_image(points, arr_alpha, arr_beta, arr_gamma, size, term_criteria),
mat.snake_image(points, arr_alpha, arr_beta, arr_gamma, size, term_criteria, true)].each { |result|
assert_equal(num_points, result.size)
result.each { |pt|
x = pt.x - center.x
y = pt.y - center.y
error = Math.sqrt((x * x + y * y - radius * radius).abs)
assert(error < acceptable_error)
}
}
# calc_gradient = false
[mat.snake_image(points, alpha, beta, gamma, size, term_criteria, false),
mat.snake_image(points, arr_alpha, arr_beta, arr_gamma, size, term_criteria, false)].each { |result|
expected_points = [[149, 102], [139, 144], [95, 144], [56, 124], [17, 105],
[25, 61], [63, 39], [101, 17], [145, 17], [158, 59]]
assert_equal(num_points, result.size)
result.each { |pt|
x = pt.x - center.x
y = pt.y - center.y
error = Math.sqrt((x * x + y * y - radius * radius).abs)
assert(error < acceptable_error)
}
}
# raise error
assert_raise(TypeError) {
mat.snake_image(DUMMY_OBJ, arr_alpha, arr_beta, arr_gamma, size, term_criteria)
}
assert_raise(TypeError) {
mat.snake_image(points, DUMMY_OBJ, arr_beta, arr_gamma, size, term_criteria)
}
assert_raise(TypeError) {
mat.snake_image(points, arr_alpha, DUMMY_OBJ, arr_gamma, size, term_criteria)
}
assert_raise(TypeError) {
mat.snake_image(points, arr_alpha, arr_beta, DUMMY_OBJ, size, term_criteria)
}
assert_raise(TypeError) {
mat.snake_image(points, arr_alpha, arr_beta, arr_gamma, DUMMY_OBJ, term_criteria)
}
assert_raise(TypeError) {
mat.snake_image(points, arr_alpha, arr_beta, arr_gamma, size, DUMMY_OBJ)
}
mat.snake_image(points, arr_alpha, arr_beta, arr_gamma, size, term_criteria, DUMMY_OBJ)
assert_raise(ArgumentError) {
mat.snake_image(points, arr_alpha[0 .. num_points / 2], arr_beta, arr_gamma, size, term_criteria)
}
assert_raise(CvBadNumChannels) {
CvMat.new(10, 10, :cv8u, 3).snake_image(points, alpha, beta, gamma, size, term_criteria)
}
# Uncomment the following lines to show the result
# result = mat.clone.GRAY2BGR
# pts = mat.snake_image(points, alpha, beta, gamma, size, term_criteria)
# w = GUI::Window.new('HoughCircle')
# result.poly_line!([pts], :color => CvColor::Red, :is_closed => true, :thickness => 2)
# result.poly_line!([points], :color => CvColor::Yellow, :is_closed => true, :thickness => 2)
# w.show result
# GUI::wait_key
end
end