From f8685c49c8807f9a25088d17c6a28f3a6f95c5bf Mon Sep 17 00:00:00 2001 From: ser1zw Date: Mon, 1 Aug 2011 03:33:09 +0900 Subject: [PATCH] tested and fixed PointSet --- ext/opencv/cvmemstorage.h | 2 +- ext/opencv/cvseq.cpp | 4 +- ext/opencv/pointset.cpp | 132 ++++++++++++++++++++++++++------------ ext/opencv/pointset.h | 10 +-- test/test_pointset.rb | 119 ++++++++++++++++++++++++++++++++++ 5 files changed, 219 insertions(+), 48 deletions(-) create mode 100755 test/test_pointset.rb diff --git a/ext/opencv/cvmemstorage.h b/ext/opencv/cvmemstorage.h index 33fae12..6305170 100644 --- a/ext/opencv/cvmemstorage.h +++ b/ext/opencv/cvmemstorage.h @@ -12,7 +12,7 @@ #include "opencv.h" -#define __NAMESPACE_BEGIN_CVMEMSTORAGE namespace cCvMemStorage{ +#define __NAMESPACE_BEGIN_CVMEMSTORAGE namespace cCvMemStorage { #define __NAMESPACE_END_CVMEMSTORAGE } __NAMESPACE_BEGIN_OPENCV diff --git a/ext/opencv/cvseq.cpp b/ext/opencv/cvseq.cpp index 04f7bae..dbfe234 100644 --- a/ext/opencv/cvseq.cpp +++ b/ext/opencv/cvseq.cpp @@ -147,7 +147,7 @@ rb_initialize(int argc, VALUE *argv, VALUE self) else storage = rb_cvCreateMemStorage(0); - if(!rb_obj_is_kind_of(klass, rb_cClass)) + if (!rb_obj_is_kind_of(klass, rb_cClass)) raise_typeerror(klass, rb_cClass); int type = 0, size = 0; @@ -404,7 +404,7 @@ rb_pop(VALUE self) if (seq->total == 0) return Qnil; - VALUE object; + VALUE object = Qnil; VALUE klass = seqblock_class(seq); try { if (klass == rb_cFixnum) { diff --git a/ext/opencv/pointset.cpp b/ext/opencv/pointset.cpp index 3fa5f72..f6804a2 100644 --- a/ext/opencv/pointset.cpp +++ b/ext/opencv/pointset.cpp @@ -36,13 +36,13 @@ define_ruby_module() VALUE opencv = rb_module_opencv(); module = rb_define_module_under(opencv, "PointSet"); rb_define_method(module, "contour_area", RUBY_METHOD_FUNC(rb_contour_area), -1); - rb_define_method(module, "fit_ellipse", RUBY_METHOD_FUNC(rb_fit_ellipse), 0); + rb_define_method(module, "fit_ellipse2", RUBY_METHOD_FUNC(rb_fit_ellipse2), 0); - rb_define_method(module, "convex_hull", RUBY_METHOD_FUNC(rb_convex_hull), -1); + rb_define_method(module, "convex_hull2", RUBY_METHOD_FUNC(rb_convex_hull2), -1); rb_define_method(module, "check_contour_convexity", RUBY_METHOD_FUNC(rb_check_contour_convexity), 0); rb_define_alias(module, "convexity?", "check_contour_convexity"); - rb_define_method(module, "convexity_defects", RUBY_METHOD_FUNC(rb_convexity_defects), -1); - rb_define_method(module, "min_area_rect", RUBY_METHOD_FUNC(rb_min_area_rect), 0); + rb_define_method(module, "convexity_defects", RUBY_METHOD_FUNC(rb_convexity_defects), 1); + rb_define_method(module, "min_area_rect2", RUBY_METHOD_FUNC(rb_min_area_rect2), 0); rb_define_method(module, "min_enclosing_circle", RUBY_METHOD_FUNC(rb_min_enclosing_circle), 0); } @@ -71,39 +71,59 @@ rb_contour_area(int argc, VALUE *argv, VALUE self) { VALUE slice; rb_scan_args(argc, argv, "01", &slice); - return rb_float_new(cvContourArea(CVARR(self), NIL_P(slice) ? CV_WHOLE_SEQ : VALUE_TO_CVSLICE(slice))); + double area = 0; + try { + area = cvContourArea(CVARR(self), NIL_P(slice) ? CV_WHOLE_SEQ : VALUE_TO_CVSLICE(slice)); + } + catch (cv::Exception& e) { + raise_cverror(e); + } + return rb_float_new(area); } /* * call-seq: - * fit_ellipse -> cvbox2d + * fit_ellipse2 -> cvbox2d * * Return fits ellipse to set of 2D points. */ VALUE -rb_fit_ellipse(VALUE self) +rb_fit_ellipse2(VALUE self) { - return cCvBox2D::new_object(cvFitEllipse2(CVARR(self))); + CvBox2D box; + try { + box = cvFitEllipse2(CVARR(self)); + } + catch (cv::Exception& e) { + raise_cverror(e); + } + return cCvBox2D::new_object(box); } /* * call-seq: - * convex_hull([reverse = fasle]) -> cvcontour + * convex_hull2([orientation_clockwise = true]) -> cvcontour * * Finds convex hull of 2D point set using Sklansky's algorithm. * - * reverse is desired orientation of convex hull. - * If reverse is false mean clockwise, otherwise counter clockwise. + * orientation_clockwise: Desired orientation of convex hull (true: clockwise, false: counter clockwise). */ VALUE -rb_convex_hull(int argc, VALUE *argv, VALUE self) +rb_convex_hull2(int argc, VALUE *argv, VALUE self) { - VALUE reverse, storage; - rb_scan_args(argc, argv, "01", &reverse); - storage = cCvMemStorage::new_object(); - CvSeq *hull = cvConvexHull2(CVSEQ(self), CVMEMSTORAGE(storage), TRUE_OR_FALSE(reverse, 0) ? CV_COUNTER_CLOCKWISE : CV_CLOCKWISE, 1); - if(CV_IS_SEQ_HOLE(CVSEQ(self))) - hull->flags |= CV_SEQ_FLAG_HOLE; + VALUE clockwise, return_points; + rb_scan_args(argc, argv, "02", &clockwise, &return_points); + VALUE storage = cCvMemStorage::new_object(); + CvSeq *hull = NULL; + int return_pts = TRUE_OR_FALSE(return_points, 1); + try { + hull = cvConvexHull2(CVSEQ(self), CVMEMSTORAGE(storage), + TRUE_OR_FALSE(clockwise, 1) ? CV_CLOCKWISE : CV_COUNTER_CLOCKWISE, + return_pts); + } + catch (cv::Exception& e) { + raise_cverror(e); + } return cCvSeq::new_sequence(cCvContour::rb_class(), hull, cCvPoint::rb_class(), storage); } @@ -116,38 +136,59 @@ rb_convex_hull(int argc, VALUE *argv, VALUE self) VALUE rb_check_contour_convexity(VALUE self) { - return cvCheckContourConvexity(CVARR(self)) ? Qtrue : Qfalse; + int convexity = 0; + try { + convexity = cvCheckContourConvexity(CVARR(self)); + } + catch (cv::Exception& e) { + raise_cverror(e); + } + return convexity ? Qtrue : Qfalse; } /* * call-seq: - * convexity_defects([reverse = false]) -> cvseq(include CvConvexityDefect) + * convexity_defects(hull) -> cvseq(include CvConvexityDefect) * * Finds convexity defects of contour. */ VALUE -rb_convexity_defects(int argc, VALUE *argv, VALUE self) +rb_convexity_defects(VALUE self, VALUE hull) { - VALUE reverse, storage; - rb_scan_args(argc, argv, "01", &reverse); - storage = cCvMemStorage::new_object(); - CvSeq *hull, *convex; - hull = cvConvexHull2(CVSEQ(self), CVMEMSTORAGE(storage), TRUE_OR_FALSE(reverse, 0) ? CV_COUNTER_CLOCKWISE : CV_CLOCKWISE, 0); - convex = cvConvexityDefects(CVSEQ(self), hull, CVMEMSTORAGE(storage)); - return cCvSeq::new_sequence(cCvSeq::rb_class(), convex, cCvConvexityDefect::rb_class(), storage); + CvSeq *defects = NULL; + CvSeq *hull_seq = CVSEQ_WITH_CHECK(hull); + CvMemStorage *stg = hull_seq->storage; + try { + defects = cvConvexityDefects(CVSEQ(self), hull_seq, stg); + } + catch (cv::Exception& e) { + raise_cverror(e); + } + // FIXME: This storage is shared with the argument "hull". + // This causes a severe problem that when "hull"'s memory is collected by GC, "defects"'s storage is + // also collected. + VALUE storage = Data_Wrap_Struct(cCvMemStorage::rb_class(), 0, cCvMemStorage::cvmemstorage_free, stg); + return cCvSeq::new_sequence(cCvSeq::rb_class(), defects, cCvConvexityDefect::rb_class(), storage); } /* * call-seq: - * min_area_rect -> cvbox2d + * min_area_rect2 -> cvbox2d * * Finds circumscribed rectangle of minimal area for given 2D point set. */ VALUE -rb_min_area_rect(VALUE self) +rb_min_area_rect2(VALUE self) { VALUE storage = cCvMemStorage::new_object(); - return cCvBox2D::new_object(cvMinAreaRect2(CVARR(self), CVMEMSTORAGE(storage))); + CvBox2D rect; + try { + rect = cvMinAreaRect2(CVARR(self), CVMEMSTORAGE(storage)); + } + catch (cv::Exception& e) { + raise_cverror(e); + } + return cCvBox2D::new_object(rect); } /* @@ -160,8 +201,15 @@ VALUE rb_min_enclosing_circle(VALUE self) { VALUE circle = cCvCircle32f::rb_allocate(cCvCircle32f::rb_class()); - cvMinEnclosingCircle(CVARR(self), &CVCIRCLE32F(circle)->center, &CVCIRCLE32F(circle)->radius); - return circle; + int success = 0; + try { + success = cvMinEnclosingCircle(CVARR(self), &CVCIRCLE32F(circle)->center, + &CVCIRCLE32F(circle)->radius); + } + catch (cv::Exception& e) { + raise_cverror(e); + } + return success ? circle : Qnil; } VALUE @@ -217,29 +265,33 @@ VALUE_TO_POINT_SET(VALUE object) VALUE tmp, storage; int length; CvPoint2D32f p32; - if(rb_obj_is_kind_of(object, cCvSeq::rb_class())){ + if (rb_obj_is_kind_of(object, cCvSeq::rb_class())) { seq = CVSEQ(object); - if(CV_IS_SEQ_POINT_SET(seq)){ + if (CV_IS_SEQ_POINT_SET(seq)) { return seq; - }else{ + } + else { rb_raise(rb_eTypeError, "sequence is not contain %s or %s.", rb_class2name(cCvPoint::rb_class()), rb_class2name(cCvPoint2D32f::rb_class())); } - }else if(rb_obj_is_kind_of(object, cCvMat::rb_class())){ + } + else if (rb_obj_is_kind_of(object, cCvMat::rb_class())) { /* to do */ rb_raise(rb_eNotImpError, "CvMat to CvSeq conversion not implemented."); - }else if(rb_obj_is_kind_of(object, rb_cArray)){ + } + else if (rb_obj_is_kind_of(object, rb_cArray)) { //pointset = cCvSeq::new_sequence(cCvSeq::rb_class(), ) length = RARRAY_LEN(object); storage = cCvMemStorage::new_object(); seq = cvCreateSeq(CV_SEQ_POINT_SET, sizeof(CvSeq), sizeof(CvPoint), CVMEMSTORAGE(storage)); - for(int i = 0; i < RARRAY_LEN(object); i++){ + for (int i = 0; i < RARRAY_LEN(object); i++) { p32.x = NUM2DBL(rb_funcall(rb_ary_entry(object, i), rb_intern("x"), 0)); p32.y = NUM2DBL(rb_funcall(rb_ary_entry(object, i), rb_intern("y"), 0)); cvSeqPush(seq, &p32); } tmp = cCvSeq::new_sequence(cCvSeq::rb_class(), seq, cCvPoint2D32f::rb_class(), storage); return seq; - }else{ + } + else { rb_raise(rb_eTypeError, "Can't convert CvSeq(PointSet)."); } } diff --git a/ext/opencv/pointset.h b/ext/opencv/pointset.h index 4eebc4e..34c361b 100644 --- a/ext/opencv/pointset.h +++ b/ext/opencv/pointset.h @@ -10,7 +10,7 @@ #ifndef RUBY_OPENCV_POINTSET_H #define RUBY_OPENCV_POINTSET_H -#define __NAMESPACE_BEGIN_POINT_SET namespace mPointSet{ +#define __NAMESPACE_BEGIN_POINT_SET namespace mPointSet { #define __NAMESPACE_END_POINT_SET } #include"opencv.h" @@ -22,12 +22,12 @@ VALUE rb_module(); void define_ruby_module(); VALUE rb_contour_area(int argc, VALUE *argv, VALUE self); -VALUE rb_fit_ellipse(VALUE self); +VALUE rb_fit_ellipse2(VALUE self); +VALUE rb_convex_hull2(int argc, VALUE *argv, VALUE self); VALUE rb_fit_line(int argc, VALUE *argv, VALUE self); -VALUE rb_convex_hull(int argc, VALUE *argv, VALUE self); VALUE rb_check_contour_convexity(VALUE self); -VALUE rb_convexity_defects(int argc, VALUE *argv, VALUE self); -VALUE rb_min_area_rect(VALUE self); +VALUE rb_convexity_defects(VALUE self, VALUE hull); +VALUE rb_min_area_rect2(VALUE self); VALUE rb_min_enclosing_circle(VALUE self); VALUE rb_calc_pgh(VALUE self); diff --git a/test/test_pointset.rb b/test/test_pointset.rb new file mode 100755 index 0000000..7281752 --- /dev/null +++ b/test/test_pointset.rb @@ -0,0 +1,119 @@ +#!/usr/bin/env ruby +# -*- mode: ruby; coding: utf-8-unix -*- +require 'test/unit' +require 'opencv' +require File.expand_path(File.dirname(__FILE__)) + '/helper' + +include OpenCV + +# Tests for OpenCV::Pointset +class TestPointset < OpenCVTestCase + def setup + mat0 = create_cvmat(128, 128, :cv8u, 1) { |j, i| + (j - 64) ** 2 + (i - 64) ** 2 <= (32 ** 2) ? CvColor::White : CvColor::Black + } + @contour1 = mat0.find_contours + @contour2 = CvContour.new + end + + def test_contour_area + assert_equal(3118, @contour1.contour_area.to_i) + + s = CvSlice.new(0, @contour1.size / 2) + assert_equal(1527, @contour1.contour_area(s).to_i) + + assert_raise(TypeError) { + @contour1.contour_area(DUMMY_OBJ) + } + assert_raise(CvStsBadArg) { + @contour2.contour_area + } + end + + def test_fit_ellipse2 + box = @contour1.fit_ellipse2 + + center = box.center + assert_equal(64, center.x.to_i) + assert_equal(64, center.y.to_i) + + size = box.size + assert_in_delta(63.116, size.width, 0.001) + assert_in_delta(63.116, size.height, 0.001) + assert_in_delta(180, box.angle, 0.001) + + assert_raise(CvStsBadSize) { + @contour2.fit_ellipse2 + } + end + + def test_convex_hull2 + [@contour1.convex_hull2, @contour1.convex_hull2(true)].each { |hull| + assert_equal(36, hull.size) + assert_equal(CvContour, hull.class) + assert_equal(CvPoint, hull[0].class) + assert_equal(32, hull[0].x) + assert_equal(64, hull[0].y) + } + + hull = @contour1.convex_hull2(false) + assert_equal(36, hull.size) + assert_equal(CvContour, hull.class) + assert_equal(CvPoint, hull[0].class) + assert_equal(96, hull[0].x) + assert_equal(64, hull[0].y) + + @contour1.convex_hull2(DUMMY_OBJ) + end + + def test_check_contour_convexity + assert_false(@contour1.check_contour_convexity) + end + + def test_convexity_defects + flunk('FIXME: Currently PointSet#convexity_defects does not work well.') + hull = @contour1.convex_hull2(true, false) + defects = @contour1.convexity_defects(hull) + puts defects.class + puts defects[0].start.to_a.join(', ') + assert_equal(CvSeq, defects.class) + assert_equal(CvConvexityDefect, defects[0].class) + end + + def test_min_area_rect2 + box = @contour1.min_area_rect2 + + assert_equal(CvBox2D, box.class) + center = box.center + assert_equal(64, center.x.to_i) + assert_equal(64, center.y.to_i) + + size = box.size + assert_in_delta(63.356, size.width, 0.001) + assert_in_delta(63.356, size.height, 0.001) + assert_in_delta(-8.130, box.angle, 0.001) + + flunk('FIXME: Currently PointSet#min_area_rect2 causes segmentation fault when "self" is invalid.') + assert_raise(CvStsBadSize) { + @contour2.min_area_rect2 + } + end + + def test_min_enclosing_circle + circle = @contour1.min_enclosing_circle + assert_equal(CvCircle32f, circle.class) + center = circle.center + assert_equal(64, center.x.to_i) + assert_equal(64, center.y.to_i) + assert_in_delta(32.959, circle.radius, 0.001) + + assert_raise(CvStsBadSize) { + @contour2.min_enclosing_circle + } + end + + def test_calc_pgh + flunk('FIXME: PointSet#calc_pgh is not implemented yet.') + end +end +