diff --git a/ext/cvmat.cpp b/ext/cvmat.cpp index 50bdc8c..3d569bb 100644 --- a/ext/cvmat.cpp +++ b/ext/cvmat.cpp @@ -320,6 +320,7 @@ void define_ruby_class() rb_define_method(rb_klass, "warp_affine", RUBY_METHOD_FUNC(rb_warp_affine), -1); rb_define_singleton_method(rb_klass, "rotation_matrix2D", RUBY_METHOD_FUNC(rb_rotation_matrix2D), 3); rb_define_method(rb_klass, "warp_perspective", RUBY_METHOD_FUNC(rb_warp_perspective), -1); + rb_define_singleton_method(rb_klass, "find_homography", RUBY_METHOD_FUNC(rb_find_homograpy), -1); //rb_define_method(rb_klass, "get_perspective_transform", RUBY_METHOD_FUNC(rb_get_perspective_transform), -1); //rb_define_alias(rb_klass, "warp_perspective_q_matrix", "get_perspective_transform"); rb_define_method(rb_klass, "remap", RUBY_METHOD_FUNC(rb_remap), -1); @@ -3343,6 +3344,46 @@ rb_warp_affine(int argc, VALUE *argv, VALUE self) return dest; } + +/* + * call-seq: + * CvMat.find_homograpy(src_points, dst_points[,method = :all][,ransac_reproj_threshold = 0][,get_status = nil]) -> cvmat + * + * Finds the perspective transformation between two planes. + * src_points: Coordinates of the points in the original plane, 2xN, Nx2, 3xN or Nx3 1-channel array (the latter two are for representation in homogeneous coordinates), where N is the number of points. 1xN or Nx1 2- or 3-channel array can also be passed. + * dst_points: Point coordinates in the destination plane, 2xN, Nx2, 3xN or Nx3 1-channel, or 1xN or Nx1 2- or 3-channel array. + * method: The method used to computed homography matrix; one of the following symbols: + * :all - a regular method using all the points + * :ransac - RANSAC-based robust method + * :lmeds - Least-Median robust method + * ransac_reproj_threshold: The maximum allowed reprojection error to treat a point pair as an inlier (used in the RANSAC method only). If src_points and dst_points are measured in pixels, it usually makes sense to set this parameter somewhere in the range 1 to 10. + * get_status If true, the optional output mask set by a robust method (:ransac or :lmeds) is returned additionally. + */ +VALUE +rb_find_homograpy(int argc, VALUE *argv, VALUE self) +{ + VALUE src_points, dst_points, method, ransac_reproj_threshold, get_status; + rb_scan_args(argc, argv, "23", &src_points, &dst_points, &method, &ransac_reproj_threshold, &get_status); + + VALUE homography = new_object(cvSize(3, 3), CV_32FC1); + int _method = CVMETHOD("HOMOGRAPHY_CALC_METHOD", method, 0); + double _ransac_reproj_threshold = NIL_P(ransac_reproj_threshold) ? 0.0 : NUM2DBL(ransac_reproj_threshold); + + if ((_method != 0) && (!NIL_P(get_status)) && IF_BOOL(get_status, 1, 0, 0)) { + CvMat *src = CVMAT(src_points); + int num_points = MAX(src->rows, src->cols); + VALUE status = new_object(cvSize(num_points, 1), CV_8UC1); + cvFindHomography(src, CVMAT(dst_points), CVMAT(homography), + _method, _ransac_reproj_threshold, CVMAT(status)); + return rb_assoc_new(homography, status); + } + else { + cvFindHomography(CVMAT(src_points), CVMAT(dst_points), CVMAT(homography), + _method, _ransac_reproj_threshold, NULL); + return homography; + } +} + /* * call-seq: * CvMat.rotation_matrix2D(center,angle,scale) -> cvmat diff --git a/ext/cvmat.h b/ext/cvmat.h index 5ac1f36..b4a4fd3 100644 --- a/ext/cvmat.h +++ b/ext/cvmat.h @@ -181,6 +181,7 @@ VALUE rb_resize(int argc, VALUE *argv, VALUE self); VALUE rb_warp_affine(int argc, VALUE *argv, VALUE self); VALUE rb_rotation_matrix2D(VALUE self, VALUE center, VALUE angle, VALUE scale); VALUE rb_warp_perspective(int argc, VALUE *argv, VALUE self); +VALUE rb_find_homograpy(int argc, VALUE *argv, VALUE self); //VALUE rb_perspective_transform(); VALUE rb_remap(int argc, VALUE *argv, VALUE self); VALUE rb_log_polar(int argc, VALUE *argv); diff --git a/ext/opencv.cpp b/ext/opencv.cpp index 2f6438e..a0cd607 100644 --- a/ext/opencv.cpp +++ b/ext/opencv.cpp @@ -204,7 +204,14 @@ define_ruby_module() rb_define_const(rb_module, "WARP_FLAG", warp_flag); RESIST_CVMETHOD(warp_flag, "fill_outliers", CV_WARP_FILL_OUTLIERS); RESIST_CVMETHOD(warp_flag, "inverse_map", CV_WARP_INVERSE_MAP); - + + VALUE homography_calc_method = rb_hash_new(); + /* {:all, :ransac, :lmeds}: Methods used to computed homography matrix */ + rb_define_const(rb_module, "HOMOGRAPHY_CALC_METHOD", homography_calc_method); + RESIST_CVMETHOD(homography_calc_method, "all", 0); + RESIST_CVMETHOD(homography_calc_method, "ransac", CV_RANSAC); + RESIST_CVMETHOD(homography_calc_method, "lmeds", CV_LMEDS); + VALUE depth = rb_hash_new(); /* {:cv8u, :cv8s, :cv16u, :cv16s, :cv32s, :cv32f, :cv64f}: Depth of each pixel. */ rb_define_const(rb_module, "DEPTH", depth); diff --git a/test/test_cvmat_imageprocessing.rb b/test/test_cvmat_imageprocessing.rb index 5290335..74ba695 100755 --- a/test/test_cvmat_imageprocessing.rb +++ b/test/test_cvmat_imageprocessing.rb @@ -318,6 +318,72 @@ class TestCvMat_imageprocessing < OpenCVTestCase } end + def test_find_homography + # Nx2 + src = CvMat.new(4, 2, :cv32f, 1) + dst = CvMat.new(4, 2, :cv32f, 1) + + # Nx3 (Homogeneous coordinates) + src2 = CvMat.new(4, 3, :cv32f, 1) + dst2 = CvMat.new(4, 3, :cv32f, 1) + + # Homography + # => + # (0, 0) => (50, 0) + # (255, 0) => (205, 0) + # (255, 255) => (255, 220) + # (0, 255) => (0, 275) + [[0, 0], [255, 0], [255, 255], [0, 255]].each_with_index { |coord, i| + src[i, 0] = coord[0] + src[i, 1] = coord[1] + + src2[i, 0] = coord[0] * 2 + src2[i, 1] = coord[1] * 2 + src2[i, 2] = 2 + } + [[50, 0], [205, 0], [255, 220], [0, 275]].each_with_index { |coord, i| + dst[i, 0] = coord[0] + dst[i, 1] = coord[1] + + dst2[i, 0] = coord[0] * 2 + dst2[i, 1] = coord[1] * 2 + dst2[i, 2] = 2 + } + + mat1 = CvMat.find_homography(src, dst) + mat2 = CvMat.find_homography(src, dst, :all) + mat3 = CvMat.find_homography(src, dst, :ransac) + mat4 = CvMat.find_homography(src, dst, :lmeds) + mat5, status5 = CvMat.find_homography(src, dst, :ransac, 5, true) + mat6, status6 = CvMat.find_homography(src, dst, :ransac, 5, true) + mat7 = CvMat.find_homography(src, dst, :ransac, 5, false) + mat8 = CvMat.find_homography(src, dst, :ransac, 5, nil) + mat9 = CvMat.find_homography(src, dst, :all, 5, true) + mat10, status10 = CvMat.find_homography(src2, dst2, :ransac, 5, true) + + [mat1, mat2, mat3, mat4, mat5, mat6, mat7, mat8, mat9, mat10].each { |mat| + assert_equal(3, mat.rows) + assert_equal(3, mat.cols) + assert_equal(:cv32f, mat.depth) + assert_equal(1, mat.channel) + [0.72430, -0.19608, 50.0, + 0.0, 0.62489, 0.0, + 0.00057, -0.00165, 1.0].each_with_index { |x, i| + assert_in_delta(x, mat[i][0], 0.0001) + } + } + + [status5, status6, status10].each { |status| + assert_equal(1, status.rows) + assert_equal(4, status.cols) + assert_equal(:cv8u, status.depth) + assert_equal(1, status.channel) + 4.times { |i| + assert_in_delta(1.0, status[i][0], 0.0001) + } + } + end + def test_remap mat0 = CvMat.load(FILENAME_LENA256x256, CV_LOAD_IMAGE_ANYCOLOR | CV_LOAD_IMAGE_ANYDEPTH) matx = CvMat.new(mat0.height, mat0.width, :cv32f, 1).clear diff --git a/test/test_opencv.rb b/test/test_opencv.rb index 688be3d..9f00dc8 100755 --- a/test/test_opencv.rb +++ b/test/test_opencv.rb @@ -57,6 +57,11 @@ class TestOpenCV < OpenCVTestCase assert_equal(8, WARP_FLAG[:fill_outliers]) assert_equal(16, WARP_FLAG[:inverse_map]) + # Homography calculation methods + assert_equal(0, HOMOGRAPHY_CALC_METHOD[:all]) + assert_equal(4, HOMOGRAPHY_CALC_METHOD[:lmeds]) + assert_equal(8, HOMOGRAPHY_CALC_METHOD[:ransac]) + # Anti aliasing flags assert_equal(16, CONNECTIVITY[:aa]) assert_equal(16, CONNECTIVITY[:anti_alias])