diff --git a/ext/opencv/cvmat.cpp b/ext/opencv/cvmat.cpp index e27df5d..377012e 100644 --- a/ext/opencv/cvmat.cpp +++ b/ext/opencv/cvmat.cpp @@ -406,6 +406,8 @@ void define_ruby_class() rb_define_singleton_method(rb_klass, "compute_correspond_epilines", RUBY_METHOD_FUNC(rb_compute_correspond_epilines), 3); + rb_define_method(rb_klass, "extract_surf", RUBY_METHOD_FUNC(rb_extract_surf), -1); + rb_define_method(rb_klass, "save_image", RUBY_METHOD_FUNC(rb_save_image), 1); } @@ -5251,6 +5253,75 @@ rb_compute_correspond_epilines(VALUE klass, VALUE points, VALUE which_image, VAL return correspondent_lines; } +/* + * call-seq: + * extract_surf(params[,mask,keypoints]) -> [cvseq(cvsurfpoint), array(float)] + * Extracts Speeded Up Robust Features from an image + * + * params (CvSURFParams) - Various algorithm parameters put to the structure CvSURFParams. + * mask (CvMat) - The optional input 8-bit mask. The features are only found in the areas that contain more than 50% of non-zero mask pixels. + * keypoints (CvSeq which includes CvSURFPoint) - Provided keypoints + */ +VALUE +rb_extract_surf(int argc, VALUE *argv, VALUE self) +{ + VALUE _params, _mask, _keypoints; + rb_scan_args(argc, argv, "12", &_params, &_mask, &_keypoints); + + // Prepare arguments + if (!rb_obj_is_kind_of(_params, cCvSURFParams::rb_class())) { + rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)", + rb_class2name(CLASS_OF(_params)), rb_class2name(cCvSURFParams::rb_class())); + } + CvSURFParams params = *CVSURFPARAMS(_params); + CvMat* mask; + if (NIL_P(_mask)) + mask = NULL; + else { + if (!rb_obj_is_kind_of(_mask, cCvMat::rb_class())) { + rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)", + rb_class2name(CLASS_OF(_mask)), rb_class2name(cCvMat::rb_class())); + } + mask = CVMAT(_mask); + } + CvSeq* keypoints; + if (NIL_P(_keypoints)) { + keypoints = NULL; + } + else { + if (!rb_obj_is_kind_of(_keypoints, cCvSeq::rb_class())) { + rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)", + rb_class2name(CLASS_OF(_keypoints)), rb_class2name(cCvSeq::rb_class())); + } + keypoints = CVSEQ(_keypoints); + } + + int use_provided = (keypoints == NULL) ? 0 : 1; + + VALUE storage = cCvMemStorage::new_object(); + CvSeq* descriptors = NULL; + + // Compute SURF keypoints and descriptors + cvExtractSURF(CVARR(self), mask, &keypoints, &descriptors, CVMEMSTORAGE(storage), + params, use_provided); + _keypoints = cCvSeq::new_sequence(cCvSeq::rb_class(), keypoints, cCvSURFPoint::rb_class(), storage); + + // Create descriptor array + const int DIM_SIZE = (params.extended) ? 128 : 64; + const int NUM_KEYPOINTS = keypoints->total; + VALUE _descriptors = rb_ary_new2(NUM_KEYPOINTS); + int m, n; + for (m = 0; m < NUM_KEYPOINTS; ++m) { + VALUE elem = rb_ary_new2(DIM_SIZE); + float *descriptor = (float*)cvGetSeqElem(descriptors, m); + for (n = 0; n < DIM_SIZE; ++n) { + rb_ary_store(elem, n, rb_float_new(descriptor[n])); + } + rb_ary_store(_descriptors, m, elem); + } + + return rb_assoc_new(_keypoints, _descriptors); +} VALUE new_object(int rows, int cols, int type) diff --git a/ext/opencv/cvmat.h b/ext/opencv/cvmat.h index c7b2a37..3848f60 100644 --- a/ext/opencv/cvmat.h +++ b/ext/opencv/cvmat.h @@ -264,7 +264,10 @@ VALUE rb_find_fundamental_mat_ransac(int argc, VALUE *argv, VALUE klass); VALUE rb_find_fundamental_mat_lmeds(int argc, VALUE *argv, VALUE klass); VALUE rb_find_fundamental_mat(int argc, VALUE *argv, VALUE klass); VALUE rb_compute_correspond_epilines(VALUE klass, VALUE points, VALUE which_image, VALUE fundamental_matrix); - + +/* Feature detection and description */ +VALUE rb_extract_surf(int argc, VALUE *argv, VALUE self); + // HighGUI function VALUE rb_save_image(VALUE self, VALUE filename); diff --git a/test/test_cvmat_imageprocessing.rb b/test/test_cvmat_imageprocessing.rb index e36e367..e25421b 100755 --- a/test/test_cvmat_imageprocessing.rb +++ b/test/test_cvmat_imageprocessing.rb @@ -1714,5 +1714,63 @@ class TestCvMat_imageprocessing < OpenCVTestCase curr.optical_flow_bm(prev, 'foo', 'bar') } end + + def test_extract_surf + mat0 = CvMat.load(FILENAME_LENA256x256, CV_LOAD_IMAGE_GRAYSCALE) + + # simple + keypoints1, descriptors1 = mat0.extract_surf(CvSURFParams.new(500, true, 2, 3)) + assert_equal(CvSeq, keypoints1.class) + assert_equal(254, keypoints1.size) + assert_equal(Array, descriptors1.class) + assert_equal(254, descriptors1.size) + assert_equal(Array, descriptors1[0].class) + assert_equal(128, descriptors1[0].size) + + # use mask + mask = create_cvmat(mat0.rows, mat0.cols, :cv8u, 1) { |j, i| + if i < mat0.cols / 2 + CvScalar.new(1) + else + CvScalar.new(0) + end + } + keypoints2, descriptors2 = mat0.extract_surf(CvSURFParams.new(500, false), mask) + assert_equal(CvSeq, keypoints2.class) + assert_equal(170, keypoints2.size) + assert_equal(Array, descriptors2.class) + assert_equal(170, descriptors2.size) + assert_equal(Array, descriptors2[0].class) + assert_equal(64, descriptors2[0].size) + + # use provided keypoints + keypoints3, descriptors3 = mat0.extract_surf(CvSURFParams.new(500, true), mask, keypoints1) + assert_equal(CvSeq, keypoints3.class) + assert_equal(254, keypoints3.size) + assert_equal(Array, descriptors3.class) + assert_equal(254, descriptors3.size) + + # raise exceptions because of invalid arguments + assert_raise(TypeError) { + mat0.extract_surf(CvMat.new(1, 1, :cv8u)) + } + assert_raise(TypeError) { + mat0.extract_surf(CvSURFParams.new(500), 'foobar') + } + assert_raise(TypeError) { + mat0.extract_surf(CvSURFParams.new(500), mask, mat0) + } + + ## Uncomment the following lines to show the result + # results = [] + # [keypoints1, keypoints2, keypoints3].each { |kpts| + # tmp = mat0.GRAY2BGR + # kpts.each { |kp| + # tmp.circle!(kp.pt, 3, :color => CvColor::Red, :thickness => 1, :line_type => :aa) + # } + # results << tmp + # } + # snap mat0, *results + end end