ext/spyglass/contour.cc in spyglass-0.0.2 vs ext/spyglass/contour.cc in spyglass-0.0.3

- old
+ new

@@ -9,11 +9,16 @@ ContourClass = rb_define_class_under(Spyglass::get_ruby_module(), "Contour", rb_cObject); rb_define_alloc_func(ContourClass, rb_alloc); rb_define_method(ContourClass, "initialize", RUBY_METHOD_FUNC(rb_initialize), -1); // Instance methods + rb_define_method(ContourClass, "corners", RUBY_METHOD_FUNC(rb_get_corners), 0); + rb_define_method(ContourClass, "convex?", RUBY_METHOD_FUNC(rb_is_convex), 0); rb_define_method(ContourClass, "rect", RUBY_METHOD_FUNC(rb_get_rect), 0); + + // Aliases + rb_define_alias(ContourClass, "closed?", "convex?"); } VALUE get_ruby_class() { return ContourClass; } @@ -48,9 +53,60 @@ cv::Point *_point = new cv::Point(* SG_GET_POINT(point)); contour->push_back(_point); } return self; + } + + static VALUE rb_get_corners(VALUE self) { + // This method is a bit of a misnomer in terms of how OpenCV works, + // seen as it's not a simple getter. It's implemented for ease of + // use. This code was blatantly based on: + // + // http://opencv-code.com/tutorials/automatic-perspective-correction-for-quadrilateral-objects/ + std::vector<cv::Point> contour = to_value_vector(SG_GET_CONTOUR(self)); + cv::approxPolyDP(contour, contour, cv::arcLength(cv::Mat(contour), true) * 0.02, true); + if (contour.size() != 4) { + // TODO: Throw exception here? + std::cout << "The object is not quadrilateral!" << std::endl; + return Qnil; + } + + // Get mass center + cv::Point center(0,0); + for (int i = 0; i < contour.size(); i++) + center += contour[i]; + center *= (1. / contour.size()); + + // Grab TL, TR, BL, and BR corners. + std::vector<cv::Point> top, bot; + for (int i = 0; i < contour.size(); i++) { + if (contour[i].y < center.y) + top.push_back(contour[i]); + else + bot.push_back(contour[i]); + } + + cv::Point tl = top[0].x > top[1].x ? top[1] : top[0]; + cv::Point tr = top[0].x > top[1].x ? top[0] : top[1]; + cv::Point bl = bot[0].x > bot[1].x ? bot[1] : bot[0]; + cv::Point br = bot[0].x > bot[1].x ? bot[0] : bot[1]; + + contour.clear(); + contour.push_back(tl); + contour.push_back(tr); + contour.push_back(br); + contour.push_back(bl); + + return from_cvpoint_vector(contour); + } + + static VALUE rb_is_convex(VALUE self) { + std::vector<cv::Point> contour = to_value_vector(SG_GET_CONTOUR(self)); + std::vector<cv::Point> simplified; + cv::approxPolyDP(contour, simplified, cv::arcLength(cv::Mat(contour), true) * 0.02, true); + + return cv::isContourConvex(simplified) ? Qtrue : Qfalse; } static VALUE rb_get_rect(VALUE self) { std::vector<cv::Point *> *contour = SG_GET_CONTOUR(self); cv::Rect rect = cv::boundingRect(to_value_vector(contour));