/************************************************************ cvcontour.cpp - $Author: lsxi $ Copyright (C) 2007 Masakazu Yonekura ************************************************************/ #include "cvcontour.h" /* * Document-class: OpenCV::CvContour * * Contour * * @see CvMat#find_contours */ __NAMESPACE_BEGIN_OPENCV __NAMESPACE_BEGIN_CVCONTOUR #define APPROX_POLY_OPTION(op) rb_get_option_table(rb_klass, "APPROX_OPTION", op) #define APPROX_POLY_METHOD(op) CVMETHOD("APPROX_POLY_METHOD", LOOKUP_HASH(op, "method"), CV_POLY_APPROX_DP) #define APPROX_POLY_ACCURACY(op) NUM2DBL(LOOKUP_HASH(op, "accuracy")) #define APPROX_POLY_RECURSIVE(op) TRUE_OR_FALSE(LOOKUP_HASH(op, "recursive")) VALUE rb_allocate(VALUE klass); void cvcontour_free(void *ptr); VALUE rb_klass; VALUE rb_class() { return rb_klass; } VALUE rb_allocate(VALUE klass) { return Data_Wrap_Struct(klass, mark_root_object, unregister_object, NULL); } /* * Constructor * * @overload new(seq_flags = CV_SEQ_ELTYPE_POINT | CV_SEQ_KIND_GENERIC, storage = nil) * @param [Fixnum] seq_flags Flags of the created sequence, which are combinations of * the element types and sequence types. * - Element type: * - CV_SEQ_ELTYPE_POINT: {CvPoint} * - CV_32FC2: {CvPoint2D32f} * - CV_SEQ_ELTYPE_POINT3D: {CvPoint3D32f} * - CV_SEQ_ELTYPE_INDEX: Fixnum * - CV_SEQ_ELTYPE_CODE: Fixnum (Freeman code) * - Sequence type: * - CV_SEQ_KIND_GENERIC: Generic sequence * - CV_SEQ_KIND_CURVE: Curve * @param [CvMemStorage] storage Sequence location * @return [CvContour] self * @opencv_func cvCreateSeq * @example * seq = CvContour.new(CV_SEQ_ELTYPE_POINT | CV_SEQ_KIND_CURVE) * seq << CvPoint.new(1, 2) * seq << 3 #=> TypeError */ VALUE rb_initialize(int argc, VALUE *argv, VALUE self) { VALUE seq_flags_value, storage_value; rb_scan_args(argc, argv, "02", &seq_flags_value, &storage_value); int seq_flags = 0; if (NIL_P(seq_flags_value)) { seq_flags = CV_SEQ_ELTYPE_POINT | CV_SEQ_KIND_GENERIC; } else { Check_Type(seq_flags_value, T_FIXNUM); seq_flags = FIX2INT(seq_flags_value); } storage_value = CHECK_CVMEMSTORAGE(storage_value); try { DATA_PTR(self) = (CvContour*)cCvSeq::create_seq(seq_flags, sizeof(CvContour), storage_value); } catch (cv::Exception& e) { raise_cverror(e); } return self; } /* * Returns bounding box of the contour * @overload rect * @return [CvRect] Bounding box of the contour */ VALUE rb_rect(VALUE self) { return cCvRect::new_object(CVCONTOUR(self)->rect); } /* * Returns color of the contour * @overload color * @return [Number] Color of the contour */ VALUE rb_color(VALUE self) { return INT2NUM(CVCONTOUR(self)->color); } /* * Set color of the contour * @overload color=value * @param value [Number] Color of the contour */ VALUE rb_set_color(VALUE self, VALUE color) { CVCONTOUR(self)->color = NUM2INT(color); return self; } /* * Returns reserved region values of the contour * @overload reserved * @return [Array] Reserved region values of the contour */ VALUE rb_reserved(VALUE self) { return rb_ary_new3(3, INT2NUM(CVCONTOUR(self)->reserved[0]), INT2NUM(CVCONTOUR(self)->reserved[1]), INT2NUM(CVCONTOUR(self)->reserved[2])); } /* * Approximates polygonal curves with desired precision * @overload approx_poly(options) * @param options [Hash] Parameters * @option options [Symbol] :method Approximation method (default :dp) * * :dp - Douglas-Peucker algorithm. * @option options [Number] :accuracy Parameter specifying the approximation accuracy. * This is the maximum distance between the original curve and its approximation. * @option options [Boolean] :recursive Recursion flag. If true, the function approximates * all the contours accessible from curve by h_next and v_next links. * @return [CvContour] Result of the approximation * @return [nil] Approximation faied * @opencv_func cvApproxPoly */ VALUE rb_approx_poly(int argc, VALUE *argv, VALUE self) { VALUE approx_poly_option; rb_scan_args(argc, argv, "01", &approx_poly_option); approx_poly_option = APPROX_POLY_OPTION(approx_poly_option); VALUE storage = cCvMemStorage::new_object(); CvSeq *contour = cvApproxPoly(CVCONTOUR(self), sizeof(CvContour), CVMEMSTORAGE(storage), APPROX_POLY_METHOD(approx_poly_option), APPROX_POLY_ACCURACY(approx_poly_option), APPROX_POLY_RECURSIVE(approx_poly_option)); if (contour && contour->total > 0) { return cCvSeq::new_sequence(cCvContour::rb_class(), contour, cCvPoint::rb_class(), storage); } return Qnil; } /* * Calculates up-right bounding rectangle of point set. * @overload bounding_rect * @return [CvRect] Bounding rectangle * @opencv_func cvBoundingRect */ VALUE rb_bounding_rect(VALUE self) { CvRect rect; try { rect = cvBoundingRect(CVCONTOUR(self), 1); } catch (cv::Exception& e) { raise_cverror(e); } return cCvRect::new_object(rect); } /* * Creates hierarchical representation of contour * @overload create_tree(threshold = 0.0) * @param threshold [Number] If <= 0, the method creates full binary tree representation. * If > 0, the method creates representation with the precision threshold. * @return [CvContourTree] Hierarchical representation of the contour * @opencv_func cvCreateContourTree */ VALUE rb_create_tree(int argc, VALUE *argv, VALUE self) { VALUE threshold, storage; rb_scan_args(argc, argv, "01", &threshold); storage = cCvMemStorage::new_object(); CvContourTree *tree = NULL; try { tree = cvCreateContourTree(CVSEQ(self), CVMEMSTORAGE(storage), IF_DBL(threshold, 0.0)); } catch (cv::Exception& e) { raise_cverror(e); } return cCvSeq::new_sequence(cCvContourTree::rb_class(), (CvSeq*)tree, cCvPoint::rb_class(), storage); } /* * Performs a point-in-contour test. * The method determines whether the point is inside a contour, outside, * or lies on an edge (or coincides with a vertex). * @overload in?(point) * @param point [CvPoint2D32f] Point tested against the contour * @return [Boolean] If the point is inside, returns true. If outside, returns false. * If lies on an edge, returns nil. * @opencv_func cvPointPolygonTest */ VALUE rb_in_q(VALUE self, VALUE point) { double n = 0; try { n = cvPointPolygonTest(CVARR(self), VALUE_TO_CVPOINT2D32F(point), 0); } catch (cv::Exception& e) { raise_cverror(e); } return n == 0 ? Qnil : n > 0 ? Qtrue : Qfalse; } /* * Calculates distance between a point and the nearest contour edgex * @overload measure_distance(point) * @param point [CvPoint2D32f] Point tested against the contour * @return Signed distance between the point and the nearest contour edge * @opencv_func cvPointPolygonTest */ VALUE rb_measure_distance(VALUE self, VALUE point) { double distance = 0; try { distance = cvPointPolygonTest(CVARR(self), VALUE_TO_CVPOINT2D32F(point), 1); } catch (cv::Exception& e) { raise_cverror(e); } return rb_float_new(distance); } /* * Determines whether the point is inside a contour, outside, or lies on an edge (or coinsides with a vertex). * @overload point_polygon_test(point, measure_dist) * @param point [CvPoint2D32f] Point tested against the contour * @param measure_dist [Boolean] If true, the method estimates the signed distance from the point to * the nearest contour edge. Otherwise, the function only checks if the point is inside a contour or not. * @return [Number] When measure_dist = false, the return value is +1, -1 and 0, respectively. * When measure_dist = true, it is a signed distance between the point and the nearest contour edge. * @opencv_func cvPointPolygonTest */ VALUE rb_point_polygon_test(VALUE self, VALUE point, VALUE measure_dist) { int measure_dist_flag; if (measure_dist == Qtrue) measure_dist_flag = 1; else if (measure_dist == Qfalse) measure_dist_flag = 0; else measure_dist_flag = NUM2INT(measure_dist); double dist = Qnil; try { dist = cvPointPolygonTest(CVARR(self), VALUE_TO_CVPOINT2D32F(point), measure_dist_flag); } catch (cv::Exception& e) { raise_cverror(e); } /* cvPointPolygonTest returns 100, -100 or 0 when measure_dist = 0 */ if ((!measure_dist_flag) && ((int)dist) != 0) dist = (dist > 0) ? 1 : -1; return rb_float_new(dist); } /* * call-seq: * match_shapes(object, method) -> float * * Compares two shapes(self and object). object should be CvContour. * * A - object1, B - object2: * * method=CV_CONTOURS_MATCH_I1 * I1(A,B)=sumi=1..7abs(1/mAi - 1/mBi) * * method=CV_CONTOURS_MATCH_I2 * I2(A,B)=sumi=1..7abs(mAi - mBi) * * method=CV_CONTOURS_MATCH_I3 * I3(A,B)=sumi=1..7abs(mAi - mBi)/abs(mAi) */ VALUE rb_match_shapes(int argc, VALUE *argv, VALUE self) { VALUE object, method, param; rb_scan_args(argc, argv, "21", &object, &method, ¶m); int method_flag = CVMETHOD("COMPARISON_METHOD", method); if (!rb_obj_is_kind_of(object, cCvContour::rb_class())) rb_raise(rb_eTypeError, "argument 1 (shape) should be %s", rb_class2name(cCvContour::rb_class())); double result = 0; try { result = cvMatchShapes(CVARR(self), CVARR(object), method_flag); } catch (cv::Exception& e) { raise_cverror(e); } return rb_float_new(result); } VALUE new_object() { VALUE object = rb_allocate(rb_klass); rb_initialize(0, NULL, object); return object; } void init_ruby_class() { #if 0 // For documentation using YARD VALUE opencv = rb_define_module("OpenCV"); VALUE cvseq = rb_define_class_under(opencv, "CvSeq"); VALUE curve = rb_define_module_under(opencv, "Curve"); VALUE pointset = rb_define_module_under(opencv, "PointSet"); #endif if (rb_klass) return; VALUE opencv = rb_module_opencv(); VALUE cvseq = cCvSeq::rb_class(); VALUE curve = mCurve::rb_module(); VALUE pointset = mPointSet::rb_module(); rb_klass = rb_define_class_under(opencv, "CvContour", cvseq); rb_include_module(rb_klass, curve); rb_include_module(rb_klass, pointset); rb_define_alloc_func(rb_klass, rb_allocate); VALUE approx_option = rb_hash_new(); rb_define_const(rb_klass, "APPROX_OPTION", approx_option); rb_hash_aset(approx_option, ID2SYM(rb_intern("method")), INT2FIX(CV_POLY_APPROX_DP)); rb_hash_aset(approx_option, ID2SYM(rb_intern("accuracy")), rb_float_new(1.0)); rb_hash_aset(approx_option, ID2SYM(rb_intern("recursive")), Qfalse); rb_define_method(rb_klass, "initialize", RUBY_METHOD_FUNC(rb_initialize), -1); rb_define_method(rb_klass, "rect", RUBY_METHOD_FUNC(rb_rect), 0); rb_define_method(rb_klass, "color", RUBY_METHOD_FUNC(rb_color), 0); rb_define_method(rb_klass, "color=", RUBY_METHOD_FUNC(rb_set_color), 1); rb_define_method(rb_klass, "reserved", RUBY_METHOD_FUNC(rb_reserved), 0); rb_define_method(rb_klass, "approx_poly", RUBY_METHOD_FUNC(rb_approx_poly), -1); rb_define_alias(rb_klass, "approx", "approx_poly"); rb_define_method(rb_klass, "bounding_rect", RUBY_METHOD_FUNC(rb_bounding_rect), 0); rb_define_method(rb_klass, "create_tree", RUBY_METHOD_FUNC(rb_create_tree), -1); rb_define_method(rb_klass, "in?", RUBY_METHOD_FUNC(rb_in_q), 1); rb_define_method(rb_klass, "measure_distance", RUBY_METHOD_FUNC(rb_measure_distance), 1); rb_define_method(rb_klass, "point_polygon_test", RUBY_METHOD_FUNC(rb_point_polygon_test), 2); } __NAMESPACE_END_CVCONTOUR __NAMESPACE_END_OPENCV