diff --git a/ext/opencv/cvmat.cpp b/ext/opencv/cvmat.cpp index 3932120..657cfec 100644 --- a/ext/opencv/cvmat.cpp +++ b/ext/opencv/cvmat.cpp @@ -343,6 +343,7 @@ void define_ruby_class() rb_define_method(rb_klass, "corner_eigenvv", RUBY_METHOD_FUNC(rb_corner_eigenvv), -1); rb_define_method(rb_klass, "corner_min_eigen_val", RUBY_METHOD_FUNC(rb_corner_min_eigen_val), -1); rb_define_method(rb_klass, "corner_harris", RUBY_METHOD_FUNC(rb_corner_harris), -1); + rb_define_method(rb_klass, "find_chessboard_corners", RUBY_METHOD_FUNC(rb_find_chessboard_corners), -1); rb_define_private_method(rb_klass, "__find_corner_sub_pix", RUBY_METHOD_FUNC(rbi_find_corner_sub_pix), -1); rb_define_method(rb_klass, "good_features_to_track", RUBY_METHOD_FUNC(rb_good_features_to_track), -1); @@ -3698,6 +3699,49 @@ rb_corner_harris(int argc, VALUE *argv, VALUE self) return dest; } +/* + * call-seq: + * find_chessboard_corners(pattern_size, flag = CV_CALIB_CB_ADAPTIVE_THRESH) -> Array + * + * Returns the positions of internal corners of the chessboard. + * + * pattern_size (CvSize) - Number of inner corners per a chessboard row and column. + * flags (Integer) - Various operation flags that can be zero or a combination of the following values + * * CV_CALIB_CB_ADAPTIVE_THRESH Use adaptive thresholding to convert the image to black and white, + * rather than a fixed threshold level (computed from the average image brightness). + * * CV_CALIB_CB_NORMALIZE_IMAGE Normalize the image gamma with CvMat#equalize_hist() before applying fixed + * or adaptive thresholding. + * * CV_CALIB_CB_FILTER_QUADS Use additional criteria (like contour area, perimeter, square-like shape) + * to filter out false quads extracted at the contour retrieval stage. + * * CALIB_CB_FAST_CHECK Run a fast check on the image that looks for chessboard corners, and shortcut + * the call if none is found. This can drastically speed up the call in the degenerate condition + * when no chessboard is observed. + */ +VALUE +rb_find_chessboard_corners(int argc, VALUE *argv, VALUE self) +{ + VALUE pattern_size_val, flag_val; + rb_scan_args(argc, argv, "11", &pattern_size_val, &flag_val); + + int flag = NIL_P(flag_val) ? CV_CALIB_CB_ADAPTIVE_THRESH : NUM2INT(flag_val); + CvSize pattern_size = VALUE_TO_CVSIZE(pattern_size_val); + CvPoint2D32f* corners = ALLOCA_N(CvPoint2D32f, pattern_size.width * pattern_size.height); + int num_found_corners = 0; + try { + cvFindChessboardCorners(CVARR(self), pattern_size, corners, &num_found_corners, flag); + } + catch (cv::Exception& e) { + raise_cverror(e); + } + + VALUE found_corners = rb_ary_new2(num_found_corners); + for (int i = 0; i < num_found_corners; i++) { + rb_ary_store(found_corners, i, cCvPoint2D32f::new_object(corners[i])); + } + + return found_corners; +} + /* * call-seq: * find_corner_sub_pix() diff --git a/ext/opencv/cvmat.h b/ext/opencv/cvmat.h index 5c3ef26..74ce98c 100644 --- a/ext/opencv/cvmat.h +++ b/ext/opencv/cvmat.h @@ -166,6 +166,7 @@ VALUE rb_pre_corner_detect(int argc, VALUE *argv, VALUE self); VALUE rb_corner_eigenvv(int argc, VALUE *argv, VALUE self); VALUE rb_corner_min_eigen_val(int argc, VALUE *argv, VALUE self); VALUE rb_corner_harris(int argc, VALUE *argv, VALUE self); +VALUE rb_find_chessboard_corners(int argc, VALUE *argv, VALUE self); VALUE rbi_find_corner_sub_pix(int argc, VALUE *argv, VALUE self); VALUE rb_good_features_to_track(int argc, VALUE *argv, VALUE self); diff --git a/ext/opencv/opencv.cpp b/ext/opencv/opencv.cpp index af8a3db..944132d 100644 --- a/ext/opencv/opencv.cpp +++ b/ext/opencv/opencv.cpp @@ -330,6 +330,12 @@ define_ruby_module() rb_define_const(rb_module, "CV_DXT_INVERSE_SCALE", INT2FIX(CV_DXT_INVERSE_SCALE)); rb_define_const(rb_module, "CV_DXT_ROWS", INT2FIX(CV_DXT_ROWS)); + /* FindChessboardCorners flags */ + rb_define_const(rb_module, "CV_CALIB_CB_ADAPTIVE_THRESH", INT2FIX(CV_CALIB_CB_ADAPTIVE_THRESH)); + rb_define_const(rb_module, "CV_CALIB_CB_NORMALIZE_IMAGE", INT2FIX(CV_CALIB_CB_NORMALIZE_IMAGE)); + rb_define_const(rb_module, "CV_CALIB_CB_FILTER_QUADS", INT2FIX(CV_CALIB_CB_FILTER_QUADS)); + rb_define_const(rb_module, "CV_CALIB_CB_FAST_CHECK", INT2FIX(CV_CALIB_CB_FAST_CHECK)); + VALUE inversion_method = rb_hash_new(); /* {:lu, :svd, :svd_sym(:svd_symmetric)}: Inversion method */ diff --git a/test/helper.rb b/test/helper.rb index 82405bd..da58720 100755 --- a/test/helper.rb +++ b/test/helper.rb @@ -13,6 +13,7 @@ class OpenCVTestCase < Test::Unit::TestCase FILENAME_LENA_EYES = File.expand_path(File.dirname(__FILE__)) + '/samples/lena-eyes.jpg' FILENAME_FRUITS = SAMPLE_DIR + 'fruits.jpg' FILENAME_CONTOURS = File.expand_path(File.dirname(__FILE__)) + '/samples/contours.jpg' + FILENAME_CHESSBOARD = SAMPLE_DIR + 'chessboard.jpg' HAARCASCADE_FRONTALFACE_ALT = SAMPLE_DIR + 'haarcascade_frontalface_alt.xml.gz' AVI_SAMPLE = SAMPLE_DIR + 'movie_sample.avi' diff --git a/test/samples/chessboard.jpg b/test/samples/chessboard.jpg new file mode 100644 index 0000000..53ca1dd Binary files /dev/null and b/test/samples/chessboard.jpg differ diff --git a/test/test_cvmat_imageprocessing.rb b/test/test_cvmat_imageprocessing.rb index ef7129e..84444da 100755 --- a/test/test_cvmat_imageprocessing.rb +++ b/test/test_cvmat_imageprocessing.rb @@ -185,6 +185,34 @@ class TestCvMat_imageprocessing < OpenCVTestCase } end + def test_find_chessboard_corners + mat = CvMat.load(FILENAME_CHESSBOARD, CV_LOAD_IMAGE_GRAYSCALE) + pattern_size = CvSize.new(4, 4) + corners1 = mat.find_chessboard_corners(pattern_size) + corners2 = mat.find_chessboard_corners(pattern_size, CV_CALIB_CB_ADAPTIVE_THRESH) + corners3 = mat.find_chessboard_corners(pattern_size, CV_CALIB_CB_NORMALIZE_IMAGE) + corners4 = mat.find_chessboard_corners(pattern_size, CV_CALIB_CB_FILTER_QUADS) + corners5 = mat.find_chessboard_corners(pattern_size, CV_CALIB_CB_FAST_CHECK) + + expected = [[39, 39], [79, 39], [119, 39], [159, 39], [39, 79], [79, 79], + [119, 79], [159, 78], [38, 119], [79, 119], [119, 119], [158, 118], + [39, 159], [79, 159], [119, 159], [159, 159]] + [corners1, corners2, corners3, corners4, corners5].each { |corners| + assert_equal(expected.size, corners.size) + expected.zip(corners).each { |e, a| + assert_in_delta(e[0], a.x, 3.0) + assert_in_delta(e[1], a.y, 3.0) + } + } + + assert_raise(TypeError) { + mat.find_chessboard_corners(DUMMY_OBJ) + } + assert_raise(TypeError) { + mat.find_chessboard_corners(pattern_size, DUMMY_OBJ) + } + end + def test_find_corner_sub_pix flunk('FIXME: CvMat#find_corner_sub_pix is not implemented yet.') end