From 5266eb051a52440013748da35c5c3fee1416ca87 Mon Sep 17 00:00:00 2001 From: ser1zw Date: Sat, 21 May 2011 18:22:27 +0900 Subject: [PATCH] modified arguments of CvHaarClassifierCascade#detect_objects to set options more easily --- ext/opencv/cvhaarclassifiercascade.cpp | 111 ++++++++++++------------- ext/opencv/cvhaarclassifiercascade.h | 1 - ext/opencv/opencv.cpp | 3 + ext/opencv/opencv.h | 1 + test/test_cvhaarclassifiercascade.rb | 16 ++++ test/test_opencv.rb | 3 + 6 files changed, 77 insertions(+), 58 deletions(-) diff --git a/ext/opencv/cvhaarclassifiercascade.cpp b/ext/opencv/cvhaarclassifiercascade.cpp index e2d88ff..ea4669e 100644 --- a/ext/opencv/cvhaarclassifiercascade.cpp +++ b/ext/opencv/cvhaarclassifiercascade.cpp @@ -43,7 +43,6 @@ void define_ruby_class() rb_define_alloc_func(rb_klass, rb_allocate); rb_define_singleton_method(rb_klass, "load", RUBY_METHOD_FUNC(rb_load), 1); rb_define_method(rb_klass, "detect_objects", RUBY_METHOD_FUNC(rb_detect_objects), -1); - rb_define_method(rb_klass, "detect_objects_with_pruning", RUBY_METHOD_FUNC(rb_detect_objects_with_pruning), -1); } VALUE @@ -86,74 +85,72 @@ rb_load(VALUE klass, VALUE path) /* * call-seq: - * detect_objects(image[,scale_factor = 1.1, min_neighbor = 3, min_size = CvSize.new(0,0)]) -> cvseq(include CvAvgComp object) - * detect_objects(image[,scale_factor = 1.1, min_neighbor = 3, min_size = CvSize.new(0,0)]){|cmp| ... } -> cvseq(include CvAvgComp object) + * detect_objects(image[, options]) -> cvseq(include CvAvgComp object) + * detect_objects(image[, options]){|cmp| ... } -> cvseq(include CvAvgComp object) * * Detects objects in the image. This method finds rectangular regions in the * given image that are likely to contain objects the cascade has been trained * for and return those regions as a sequence of rectangles. * - * * scale_factor (should be > 1.0) - * The factor by which the search window is scaled between the subsequent scans, for example, 1.1 mean increasing window by 10%. - * * min_neighbors - * Minimum number (minus 1) of neighbor rectangles that makes up an object. - * All the groups of a smaller number of rectangles than min_neighbors - 1 are rejected. - * If min_neighbors is 0, the function does not any grouping at all and returns all the detected - * candidate rectangles, whitch many be useful if the user wants to apply a customized grouping procedure. - * * min_size - * Minimum window size. By default, it is set to size of samples the classifier has been trained on. + * * option should be Hash include these keys. + * :scale_factor (should be > 1.0) + * The factor by which the search window is scaled between the subsequent scans, + * 1.1 mean increasing window by 10%. + * :storage + * Memory storage to store the resultant sequence of the object candidate rectangles + * :flags + * Mode of operation. Currently the only flag that may be specified is CV_HAAR_DO_CANNY_PRUNING . + * If it is set, the function uses Canny edge detector to reject some image regions that contain + * too few or too much edges and thus can not contain the searched object. The particular threshold + * values are tuned for face detection and in this case the pruning speeds up the processing + * :min_neighbors + * Minimum number (minus 1) of neighbor rectangles that makes up an object. + * All the groups of a smaller number of rectangles than min_neighbors - 1 are rejected. + * If min_neighbors is 0, the function does not any grouping at all and returns all the detected + * candidate rectangles, whitch many be useful if the user wants to apply a customized grouping procedure. + * :min_size + * Minimum window size. By default, it is set to size of samples the classifier has been + * trained on (~20x20 for face detection). + * :max_size + * aximum window size to use. By default, it is set to the size of the image. */ -VALUE +VALUE rb_detect_objects(int argc, VALUE *argv, VALUE self) { - VALUE image, storage, scale_factor, min_neighbors, min_size, result; - rb_scan_args(argc, argv, "14", &image, &storage, &scale_factor, &min_neighbors, &min_size); - if (!rb_obj_is_kind_of(image, cCvMat::rb_class())) - rb_raise(rb_eTypeError, "argument 1(target-image) should be %s.", rb_class2name(cCvMat::rb_class())); - double scale = IF_DBL(scale_factor, 1.1); - if (!(scale > 1.0)) - rb_raise(rb_eArgError, "argument 2 (scale factor) must > 1.0."); - storage = CHECK_CVMEMSTORAGE(storage); - CvSeq *seq = cvHaarDetectObjects(CVMAT(image), CVHAARCLASSIFIERCASCADE(self), CVMEMSTORAGE(storage), - scale, IF_INT(min_neighbors, 3), 0, NIL_P(min_size) ? cvSize(0,0) : VALUE_TO_CVSIZE(min_size)); - result = cCvSeq::new_sequence(cCvSeq::rb_class(), seq, cCvAvgComp::rb_class(), storage); - if (rb_block_given_p()) { - for(int i = 0; i < seq->total; i++) - rb_yield(REFER_OBJECT(cCvAvgComp::rb_class(), cvGetSeqElem(seq, i), storage)); - } - return result; -} + VALUE image, options; + rb_scan_args(argc, argv, "11", &image, &options); -/* - * call-seq: - * detect_objects_with_pruning(image[,scale_factor = 1.1, min_neighbor = 3, min_size = CvSize.new(0,0)]) -> cvseq(include CvAvgComp object) - * detect_objects_with_pruning(image[,scale_factor = 1.1, min_neighbor = 3, min_size = CvSize.new(0,0)]){|cmp| ... } -> cvseq(include CvAvgComp object) - * - * Almost same to #detect_objects (Return detected objects). - * - * Before scanning to image, Canny edge detector to reject some image regions - * that contain too few or too much edges, and thus can not contain the searched object. - * - * note: The particular threshold values are tuned for face detection. - * And in this case the pruning speeds up the processing. - */ -VALUE -rb_detect_objects_with_pruning(int argc, VALUE *argv, VALUE self) -{ - VALUE image, storage, scale_factor, min_neighbors, min_size, result; - rb_scan_args(argc, argv, "14", &image, &storage, &scale_factor, &min_neighbors, &min_size); if (!rb_obj_is_kind_of(image, cCvMat::rb_class())) rb_raise(rb_eTypeError, "argument 1(target-image) should be %s.", rb_class2name(cCvMat::rb_class())); - double scale = IF_DBL(scale_factor, 1.1); - if (!(scale > 1.0)) - rb_raise(rb_eArgError, "argument 2 (scale factor) must > 1.0."); - storage = CHECK_CVMEMSTORAGE(storage); - CvSeq *seq = cvHaarDetectObjects(CVMAT(image), CVHAARCLASSIFIERCASCADE(self), CVMEMSTORAGE(storage), - scale, IF_INT(min_neighbors, 3), CV_HAAR_DO_CANNY_PRUNING, NIL_P(min_size) ? cvSize(0,0) : VALUE_TO_CVSIZE(min_size)); - result = cCvSeq::new_sequence(cCvSeq::rb_class(), seq, cCvAvgComp::rb_class(), storage); + + double scale_factor; + int flags, min_neighbors; + CvSize min_size, max_size; + VALUE storage_val; + if (NIL_P(options)) { + scale_factor = 1.1; + flags = 0; + min_neighbors = 3; + min_size = max_size = cvSize(0, 0); + storage_val = cCvMemStorage::new_object(); + } + else { + scale_factor = IF_DBL(LOOKUP_CVMETHOD(options, "scale_factor"), 1.1); + flags = IF_INT(LOOKUP_CVMETHOD(options, "flags"), 0); + min_neighbors = IF_INT(LOOKUP_CVMETHOD(options, "min_neighbors"), 3); + VALUE min_size_val = LOOKUP_CVMETHOD(options, "min_size"); + min_size = NIL_P(min_size_val) ? cvSize(0, 0) : VALUE_TO_CVSIZE(min_size_val); + VALUE max_size_val = LOOKUP_CVMETHOD(options, "max_size"); + max_size = NIL_P(max_size_val) ? cvSize(0, 0) : VALUE_TO_CVSIZE(max_size_val); + storage_val = CHECK_CVMEMSTORAGE(LOOKUP_CVMETHOD(options, "storage")); + } + + CvSeq *seq = cvHaarDetectObjects(CVMAT(image), CVHAARCLASSIFIERCASCADE(self), CVMEMSTORAGE(storage_val), + scale_factor, min_neighbors, flags, min_size, max_size); + VALUE result = cCvSeq::new_sequence(cCvSeq::rb_class(), seq, cCvAvgComp::rb_class(), storage_val); if (rb_block_given_p()) { - for(int i = 0; i < seq->total; i++) - rb_yield(REFER_OBJECT(cCvAvgComp::rb_class(), cvGetSeqElem(seq, i), storage)); + for(int i = 0; i < seq->total; ++i) + rb_yield(REFER_OBJECT(cCvAvgComp::rb_class(), cvGetSeqElem(seq, i), storage_val)); } return result; } diff --git a/ext/opencv/cvhaarclassifiercascade.h b/ext/opencv/cvhaarclassifiercascade.h index baa8f9d..a55eef7 100644 --- a/ext/opencv/cvhaarclassifiercascade.h +++ b/ext/opencv/cvhaarclassifiercascade.h @@ -26,7 +26,6 @@ VALUE rb_allocate(VALUE klass); VALUE rb_load(VALUE klass, VALUE path); VALUE rb_detect_objects(int argc, VALUE *argv, VALUE self); -VALUE rb_detect_objects_with_pruning(int argc, VALUE *argv, VALUE self); __NAMESPACE_END_CVHAARCLASSIFERCASCADE inline CvHaarClassifierCascade *CVHAARCLASSIFIERCASCADE(VALUE object) { diff --git a/ext/opencv/opencv.cpp b/ext/opencv/opencv.cpp index 28f5a9b..28eb604 100644 --- a/ext/opencv/opencv.cpp +++ b/ext/opencv/opencv.cpp @@ -412,6 +412,9 @@ define_ruby_module() /* Flags of window */ rb_define_const(rb_module, "CV_WINDOW_AUTOSIZE", INT2FIX(CV_WINDOW_AUTOSIZE)); + + /* Object detection mode */ + rb_define_const(rb_module, "CV_HAAR_DO_CANNY_PRUNING", INT2FIX(CV_HAAR_DO_CANNY_PRUNING)); VALUE inversion_method = rb_hash_new(); /* {:lu, :svd, :svd_sym(:svd_symmetric)}: Inversion method */ diff --git a/ext/opencv/opencv.h b/ext/opencv/opencv.h index 3c75569..ccec36a 100644 --- a/ext/opencv/opencv.h +++ b/ext/opencv/opencv.h @@ -145,6 +145,7 @@ extern "C"{ #define IF_DEPTH(val, ifnone) NIL_P(val) ? ifnone : FIX2INT(val) #define RESIST_CVMETHOD(hash, str, value) rb_hash_aset(hash, ID2SYM(rb_intern(str)), INT2FIX(value)) +#define LOOKUP_CVMETHOD(hash, key_as_cstr) (rb_hash_lookup(hash, ID2SYM(rb_intern(key_as_cstr)))) #define maxint(a,b) ({int _a = (a), _b = (b); _a > _b ? _a : _b; }) diff --git a/test/test_cvhaarclassifiercascade.rb b/test/test_cvhaarclassifiercascade.rb index d81feeb..1ba4d99 100755 --- a/test/test_cvhaarclassifiercascade.rb +++ b/test/test_cvhaarclassifiercascade.rb @@ -42,6 +42,22 @@ class TestCvHaarClassifierCascade < OpenCVTestCase assert_equal(CvSeq, detected.class) assert_equal(1, detected.size) assert_equal(CvAvgComp, detected[0].class) + + detected = @cascade.detect_objects(img, :scale_factor => 2.0, :flags => CV_HAAR_DO_CANNY_PRUNING, + :min_neighbors => 5, :min_size => CvSize.new(10, 10), + :max_size => CvSize.new(100, 100)) + assert_equal(CvSeq, detected.class) + assert_equal(1, detected.size) + assert_equal(CvAvgComp, detected[0].class) + assert_equal(109, detected[0].x) + assert_equal(102, detected[0].y) + assert_equal(80, detected[0].width) + assert_equal(80, detected[0].height) + assert_equal(7, detected[0].neighbors) + + assert_raise(TypeError) { + @cascade.detect_objects('foo') + } end end diff --git a/test/test_opencv.rb b/test/test_opencv.rb index dedc80b..23d4703 100755 --- a/test/test_opencv.rb +++ b/test/test_opencv.rb @@ -102,6 +102,9 @@ class TestOpenCV < OpenCVTestCase # Flags of window assert_equal(1, CV_WINDOW_AUTOSIZE) + + # Object detection mode + assert_equal(1, CV_HAAR_DO_CANNY_PRUNING) end def test_symbols