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])