From 3e561d8353f75427fc0641208c1287767b1939c7 Mon Sep 17 00:00:00 2001 From: Gursimar Singh Date: Tue, 16 Apr 2024 12:43:34 +0530 Subject: [PATCH] Merge pull request #25304 from gursimarsingh:geometry_sample_cpp Geometry C++ sample combining other shape detection samples #25304 Clean Samples #25006 This PR removes adds a new cpp sample (geometry) which combines different methods of finding and drawing shapes in an image. It makes separate samples for convexHull, fitellipse, minAreaRect, minAreaCircle redudant. Shapes can be changed using hotkeys after running the program ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/imgproc/include/opencv2/imgproc.hpp | 13 +- samples/cpp/contours2.cpp | 96 ------ samples/cpp/convexhull.cpp | 57 ---- samples/cpp/fitellipse.cpp | 311 -------------------- samples/cpp/geometry.cpp | 268 +++++++++++++++++ samples/cpp/minarea.cpp | 78 ----- 6 files changed, 270 insertions(+), 553 deletions(-) delete mode 100644 samples/cpp/contours2.cpp delete mode 100644 samples/cpp/convexhull.cpp delete mode 100644 samples/cpp/fitellipse.cpp create mode 100644 samples/cpp/geometry.cpp delete mode 100644 samples/cpp/minarea.cpp diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 04ed254f28..310d4bc05b 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -4173,8 +4173,6 @@ The function finds the minimal enclosing circle of a 2D point set using an itera CV_EXPORTS_W void minEnclosingCircle( InputArray points, CV_OUT Point2f& center, CV_OUT float& radius ); -/** @example samples/cpp/minarea.cpp -*/ /** @brief Finds a triangle of minimum area enclosing a 2D point set and returns its area. @@ -4209,8 +4207,8 @@ The function compares two shapes. All three implemented methods use the Hu invar CV_EXPORTS_W double matchShapes( InputArray contour1, InputArray contour2, int method, double parameter ); -/** @example samples/cpp/convexhull.cpp -An example using the convexHull functionality +/** @example samples/cpp/geometry.cpp +An example program illustrates the use of cv::convexHull, cv::fitEllipse, cv::minEnclosingTriangle, cv::minEnclosingCircle and cv::minAreaRect. */ /** @brief Finds the convex hull of a point set. @@ -4291,9 +4289,6 @@ of the other, they are not considered nested and an intersection will be found r CV_EXPORTS_W float intersectConvexConvex( InputArray p1, InputArray p2, OutputArray p12, bool handleNested = true ); -/** @example samples/cpp/fitellipse.cpp -An example using the fitEllipse technique -*/ /** @brief Fits an ellipse around a set of 2D points. @@ -4750,10 +4745,6 @@ CV_EXPORTS void polylines(InputOutputArray img, const Point* const* pts, const i int ncontours, bool isClosed, const Scalar& color, int thickness = 1, int lineType = LINE_8, int shift = 0 ); -/** @example samples/cpp/contours2.cpp -An example program illustrates the use of cv::findContours and cv::drawContours -\image html WindowsQtContoursOutput.png "Screenshot of the program" -*/ /** @example samples/cpp/segment_objects.cpp An example using drawContours to clean up a background segmentation result diff --git a/samples/cpp/contours2.cpp b/samples/cpp/contours2.cpp deleted file mode 100644 index c3653fb5d6..0000000000 --- a/samples/cpp/contours2.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "opencv2/imgproc.hpp" -#include "opencv2/highgui.hpp" -#include -#include - -using namespace cv; -using namespace std; - -static void help(char** argv) -{ - cout - << "\nThis program illustrates the use of findContours and drawContours\n" - << "The original image is put up along with the image of drawn contours\n" - << "Usage:\n"; - cout - << argv[0] - << "\nA trackbar is put up which controls the contour level from -3 to 3\n" - << endl; -} - -const int w = 500; -int levels = 3; - -vector > contours; -vector hierarchy; - -static void on_trackbar(int, void*) -{ - Mat cnt_img = Mat::zeros(w, w, CV_8UC3); - int _levels = levels - 3; - drawContours( cnt_img, contours, _levels <= 0 ? 3 : -1, Scalar(128,255,255), - 3, LINE_AA, hierarchy, std::abs(_levels) ); - - imshow("contours", cnt_img); -} - -int main( int argc, char** argv) -{ - cv::CommandLineParser parser(argc, argv, "{help h||}"); - if (parser.has("help")) - { - help(argv); - return 0; - } - Mat img = Mat::zeros(w, w, CV_8UC1); - //Draw 6 faces - for( int i = 0; i < 6; i++ ) - { - int dx = (i%2)*250 - 30; - int dy = (i/2)*150; - const Scalar white = Scalar(255); - const Scalar black = Scalar(0); - - if( i == 0 ) - { - for( int j = 0; j <= 10; j++ ) - { - double angle = (j+5)*CV_PI/21; - line(img, Point(cvRound(dx+100+j*10-80*cos(angle)), - cvRound(dy+100-90*sin(angle))), - Point(cvRound(dx+100+j*10-30*cos(angle)), - cvRound(dy+100-30*sin(angle))), white, 1, 8, 0); - } - } - - ellipse( img, Point(dx+150, dy+100), Size(100,70), 0, 0, 360, white, -1, 8, 0 ); - ellipse( img, Point(dx+115, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 ); - ellipse( img, Point(dx+185, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 ); - ellipse( img, Point(dx+115, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 ); - ellipse( img, Point(dx+185, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 ); - ellipse( img, Point(dx+115, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 ); - ellipse( img, Point(dx+185, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 ); - ellipse( img, Point(dx+150, dy+100), Size(10,5), 0, 0, 360, black, -1, 8, 0 ); - ellipse( img, Point(dx+150, dy+150), Size(40,10), 0, 0, 360, black, -1, 8, 0 ); - ellipse( img, Point(dx+27, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 ); - ellipse( img, Point(dx+273, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 ); - } - //show the faces - namedWindow( "image", 1 ); - imshow( "image", img ); - //Extract the contours so that - vector > contours0; - findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE); - - contours.resize(contours0.size()); - for( size_t k = 0; k < contours0.size(); k++ ) - approxPolyDP(Mat(contours0[k]), contours[k], 3, true); - - namedWindow( "contours", 1 ); - createTrackbar( "levels+3", "contours", &levels, 7, on_trackbar ); - - on_trackbar(0,0); - waitKey(); - - return 0; -} diff --git a/samples/cpp/convexhull.cpp b/samples/cpp/convexhull.cpp deleted file mode 100644 index d839b8061f..0000000000 --- a/samples/cpp/convexhull.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "opencv2/imgproc.hpp" -#include "opencv2/highgui.hpp" -#include - -using namespace cv; -using namespace std; - -static void help(char** argv) -{ - cout << "\nThis sample program demonstrates the use of the convexHull() function\n" - << "Call:\n" - << argv[0] << endl; -} - -int main( int argc, char** argv ) -{ - CommandLineParser parser(argc, argv, "{help h||}"); - if (parser.has("help")) - { - help(argv); - return 0; - } - Mat img(500, 500, CV_8UC3); - RNG& rng = theRNG(); - - for(;;) - { - int i, count = (unsigned)rng%100 + 1; - - vector points; - - for( i = 0; i < count; i++ ) - { - Point pt; - pt.x = rng.uniform(img.cols/4, img.cols*3/4); - pt.y = rng.uniform(img.rows/4, img.rows*3/4); - - points.push_back(pt); - } - - vector hull; - convexHull(points, hull, true); - - img = Scalar::all(0); - for( i = 0; i < count; i++ ) - circle(img, points[i], 3, Scalar(0, 0, 255), FILLED, LINE_AA); - - polylines(img, hull, true, Scalar(0, 255, 0), 1, LINE_AA); - imshow("hull", img); - - char key = (char)waitKey(); - if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC' - break; - } - - return 0; -} diff --git a/samples/cpp/fitellipse.cpp b/samples/cpp/fitellipse.cpp deleted file mode 100644 index 7d217014d5..0000000000 --- a/samples/cpp/fitellipse.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/******************************************************************************** - * - * - * This program is demonstration for ellipse fitting. Program finds - * contours and approximate it by ellipses using three methods. - * 1: OpenCV's original method fitEllipse which implements Fitzgibbon 1995 method. - * 2: The Approximate Mean Square (AMS) method fitEllipseAMS proposed by Taubin 1991 - * 3: The Direct least square (Direct) method fitEllipseDirect proposed by Fitzgibbon1999. - * - * Trackbar specify threshold parameter. - * - * White lines is contours/input points and the true ellipse used to generate the data. - * 1: Blue lines is fitting ellipses using openCV's original method. - * 2: Green lines is fitting ellipses using the AMS method. - * 3: Red lines is fitting ellipses using the Direct method. - * - * - * Original Author: Denis Burenkov - * AMS and Direct Methods Author: Jasper Shemilt - * - * - ********************************************************************************/ -#include "opencv2/imgproc.hpp" -#include "opencv2/imgcodecs.hpp" -#include "opencv2/highgui.hpp" -#include - -using namespace cv; -using namespace std; - -class canvas{ -public: - bool setupQ; - cv::Point origin; - cv::Point corner; - int minDims,maxDims; - double scale; - int rows, cols; - cv::Mat img; - - void init(int minD, int maxD){ - // Initialise the canvas with minimum and maximum rows and column sizes. - minDims = minD; maxDims = maxD; - origin = cv::Point(0,0); - corner = cv::Point(0,0); - scale = 1.0; - rows = 0; - cols = 0; - setupQ = false; - } - - void stretch(cv::Point2f min, cv::Point2f max){ - // Stretch the canvas to include the points min and max. - if(setupQ){ - if(corner.x < max.x){corner.x = (int)(max.x + 1.0);}; - if(corner.y < max.y){corner.y = (int)(max.y + 1.0);}; - if(origin.x > min.x){origin.x = (int) min.x;}; - if(origin.y > min.y){origin.y = (int) min.y;}; - } else { - origin = cv::Point((int)min.x, (int)min.y); - corner = cv::Point((int)(max.x + 1.0), (int)(max.y + 1.0)); - } - - int c = (int)(scale*((corner.x + 1.0) - origin.x)); - if(cmaxDims){ - scale = scale * (double)maxDims/(double)c; - } - } - int r = (int)(scale*((corner.y + 1.0) - origin.y)); - if(rmaxDims){ - scale = scale * (double)maxDims/(double)r; - } - } - cols = (int)(scale*((corner.x + 1.0) - origin.x)); - rows = (int)(scale*((corner.y + 1.0) - origin.y)); - setupQ = true; - } - - void stretch(vector pts) - { // Stretch the canvas so all the points pts are on the canvas. - cv::Point2f min = pts[0]; - cv::Point2f max = pts[0]; - for(size_t i=1; i < pts.size(); i++){ - Point2f pnt = pts[i]; - if(max.x < pnt.x){max.x = pnt.x;}; - if(max.y < pnt.y){max.y = pnt.y;}; - if(min.x > pnt.x){min.x = pnt.x;}; - if(min.y > pnt.y){min.y = pnt.y;}; - }; - stretch(min, max); - } - - void stretch(cv::RotatedRect box) - { // Stretch the canvas so that the rectangle box is on the canvas. - cv::Point2f min = box.center; - cv::Point2f max = box.center; - cv::Point2f vtx[4]; - box.points(vtx); - for( int i = 0; i < 4; i++ ){ - cv::Point2f pnt = vtx[i]; - if(max.x < pnt.x){max.x = pnt.x;}; - if(max.y < pnt.y){max.y = pnt.y;}; - if(min.x > pnt.x){min.x = pnt.x;}; - if(min.y > pnt.y){min.y = pnt.y;}; - } - stretch(min, max); - } - - void drawEllipseWithBox(cv::RotatedRect box, cv::Scalar color, int lineThickness) - { - if(img.empty()){ - stretch(box); - img = cv::Mat::zeros(rows,cols,CV_8UC3); - } - - box.center = scale * cv::Point2f(box.center.x - origin.x, box.center.y - origin.y); - box.size.width = (float)(scale * box.size.width); - box.size.height = (float)(scale * box.size.height); - - ellipse(img, box, color, lineThickness, LINE_AA); - - Point2f vtx[4]; - box.points(vtx); - for( int j = 0; j < 4; j++ ){ - line(img, vtx[j], vtx[(j+1)%4], color, lineThickness, LINE_AA); - } - } - - void drawPoints(vector pts, cv::Scalar color) - { - if(img.empty()){ - stretch(pts); - img = cv::Mat::zeros(rows,cols,CV_8UC3); - } - for(size_t i=0; i < pts.size(); i++){ - Point2f pnt = scale * cv::Point2f(pts[i].x - origin.x, pts[i].y - origin.y); - img.at(int(pnt.y), int(pnt.x))[0] = (uchar)color[0]; - img.at(int(pnt.y), int(pnt.x))[1] = (uchar)color[1]; - img.at(int(pnt.y), int(pnt.x))[2] = (uchar)color[2]; - }; - } - - void drawLabels( std::vector text, std::vector colors) - { - if(img.empty()){ - img = cv::Mat::zeros(rows,cols,CV_8UC3); - } - int vPos = 0; - for (size_t i=0; i < text.size(); i++) { - cv::Scalar color = colors[i]; - std::string txt = text[i]; - Size textsize = getTextSize(txt, FONT_HERSHEY_COMPLEX, 1, 1, 0); - vPos += (int)(1.3 * textsize.height); - Point org((img.cols - textsize.width), vPos); - cv::putText(img, txt, org, FONT_HERSHEY_COMPLEX, 1, color, 1, LINE_8); - } - } - -}; - -static void help(char** argv) -{ - cout << "\nThis program is demonstration for ellipse fitting. The program finds\n" - "contours and approximate it by ellipses. Three methods are used to find the \n" - "elliptical fits: fitEllipse, fitEllipseAMS and fitEllipseDirect.\n" - "Call:\n" - << argv[0] << " [image_name -- Default ellipses.jpg]\n" << endl; -} - -int sliderPos = 70; - -Mat image; - -bool fitEllipseQ, fitEllipseAMSQ, fitEllipseDirectQ; -cv::Scalar fitEllipseColor = Scalar(255, 0, 0); -cv::Scalar fitEllipseAMSColor = Scalar( 0,255, 0); -cv::Scalar fitEllipseDirectColor = Scalar( 0, 0,255); -cv::Scalar fitEllipseTrueColor = Scalar(255,255,255); - -void processImage(int, void*); - -int main( int argc, char** argv ) -{ - fitEllipseQ = true; - fitEllipseAMSQ = true; - fitEllipseDirectQ = true; - - cv::CommandLineParser parser(argc, argv,"{help h||}{@image|ellipses.jpg|}"); - if (parser.has("help")) - { - help(argv); - return 0; - } - string filename = parser.get("@image"); - image = imread(samples::findFile(filename), 0); - if( image.empty() ) - { - cout << "Couldn't open image " << filename << "\n"; - return 0; - } - - imshow("source", image); - namedWindow("result", WINDOW_NORMAL ); - - // Create toolbars. HighGUI use. - createTrackbar( "threshold", "result", &sliderPos, 255, processImage ); - - processImage(0, 0); - - // Wait for a key stroke; the same function arranges events processing - waitKey(); - return 0; -} - -inline static bool isGoodBox(const RotatedRect& box) { - //size.height >= size.width awalys,only if the pts are on a line or at the same point,size.width=0 - return (box.size.height <= box.size.width * 30) && (box.size.width > 0); -} - -// Define trackbar callback function. This function finds contours, -// draws them, and approximates by ellipses. -void processImage(int /*h*/, void*) -{ - RotatedRect box, boxAMS, boxDirect; - vector > contours; - Mat bimage = image >= sliderPos; - - findContours(bimage, contours, RETR_LIST, CHAIN_APPROX_NONE); - - canvas paper; - paper.init(int(0.8*MIN(bimage.rows, bimage.cols)), int(1.2*MAX(bimage.rows, bimage.cols))); - paper.stretch(cv::Point2f(0.0f, 0.0f), cv::Point2f((float)(bimage.cols+2.0), (float)(bimage.rows+2.0))); - - std::vector text; - std::vector color; - - if (fitEllipseQ) { - text.push_back("OpenCV"); - color.push_back(fitEllipseColor); - } - if (fitEllipseAMSQ) { - text.push_back("AMS"); - color.push_back(fitEllipseAMSColor); - } - if (fitEllipseDirectQ) { - text.push_back("Direct"); - color.push_back(fitEllipseDirectColor); - } - paper.drawLabels(text, color); - - int margin = 2; - vector< vector > points; - for(size_t i = 0; i < contours.size(); i++) - { - size_t count = contours[i].size(); - if( count < 6 ) - continue; - - Mat pointsf; - Mat(contours[i]).convertTo(pointsf, CV_32F); - - vectorpts; - for (int j = 0; j < pointsf.rows; j++) { - Point2f pnt = Point2f(pointsf.at(j,0), pointsf.at(j,1)); - if ((pnt.x > margin && pnt.y > margin && pnt.x < bimage.cols-margin && pnt.y < bimage.rows-margin)) { - if(j%20==0){ - pts.push_back(pnt); - } - } - } - points.push_back(pts); - } - - for(size_t i = 0; i < points.size(); i++) - { - vector pts = points[i]; - - //At least 5 points can fit an ellipse - if (pts.size()<5) { - continue; - } - if (fitEllipseQ) { - box = fitEllipse(pts); - if (isGoodBox(box)) { - paper.drawEllipseWithBox(box, fitEllipseColor, 3); - } - } - if (fitEllipseAMSQ) { - boxAMS = fitEllipseAMS(pts); - if (isGoodBox(boxAMS)) { - paper.drawEllipseWithBox(boxAMS, fitEllipseAMSColor, 2); - } - } - if (fitEllipseDirectQ) { - boxDirect = fitEllipseDirect(pts); - if (isGoodBox(boxDirect)){ - paper.drawEllipseWithBox(boxDirect, fitEllipseDirectColor, 1); - } - } - - paper.drawPoints(pts, fitEllipseTrueColor); - } - - imshow("result", paper.img); -} diff --git a/samples/cpp/geometry.cpp b/samples/cpp/geometry.cpp new file mode 100644 index 0000000000..f88893c9e4 --- /dev/null +++ b/samples/cpp/geometry.cpp @@ -0,0 +1,268 @@ +/******************************************************************************* + * + * This program demonstrates various shape fitting techniques using OpenCV. + * It reads an image, applies binary thresholding, and then detects contours. + * + * For each contour, it fits and draws several geometric shapes including + * convex hulls, minimum enclosing circles, rectangles, triangles, and ellipses + * using different fitting methods: + * 1: OpenCV's original method fitEllipse which implements Fitzgibbon 1995 method. + * 2: The Approximate Mean Square (AMS) method fitEllipseAMS proposed by Taubin 1991 + * 3: The Direct least square (Direct) method fitEllipseDirect proposed by Fitzgibbon1999 + * + * The results are displayed with unique colors + * for each shape and fitting method for clear differentiation. + * + * + *********************************************************************************/ + +#include "opencv2/imgproc.hpp" +#include "opencv2/highgui.hpp" +#include +#include + +using namespace cv; +using namespace std; + +const string hot_keys = + "\n\nHot keys: \n" + "\tESC - quit the program\n" + "\tq - quit the program\n" + "\tc - make the circle\n" + "\tr - make the rectangle\n" + "\th - make the convexhull\n" + "\tt - make the triangle\n" + "\te - make the ellipse\n" + "\ta - make all shapes\n" + "\t0 - use OpenCV's method for ellipse fitting\n" + "\t1 - use Approximate Mean Square (AMS) method for ellipse fitting \n" + "\t2 - use Direct least square (Direct) method for ellipse fitting\n"; + +static void help(char** argv) +{ + cout << "\nThis program demonstrates various shape fitting techniques on a set of points using functions: \n" + << "minAreaRect(), minEnclosingTriangle(), minEnclosingCircle(), convexHull(), and ellipse().\n\n" + << "Usage: " << argv[0] << " [--image_name= Default: ellipses.jpg]\n\n"; + cout << hot_keys << endl; +} + +void processImage(int, void*); +void drawShapes(Mat &img, const vector &points, int ellipseMethod, string shape); +void drawConvexHull(Mat &img, const vector &points); +void drawMinAreaRect(Mat &img, const vector &points); +void drawFittedEllipses(Mat &img, const vector &points, int ellipseMethod); +void drawMinEnclosingCircle(Mat &img, const vector &points); +void drawMinEnclosingTriangle(Mat &img, const vector &points); + +// Shape fitting options +Mat image; +enum EllipseFittingMethod { + OpenCV_Method, + AMS_Method, + Direct_Method +}; + +struct UserData { + int sliderPos = 70; + string shape = "--all"; + int ellipseMethod = OpenCV_Method; +}; + +const char* keys = + "{help h | | Show help message }" + "{@image |ellipses.jpg| Path to input image file }"; + +int main(int argc, char** argv) { + + cv::CommandLineParser parser(argc, argv, keys); + help(argv); + + if (parser.has("help")) + { + return 0; + } + + UserData userData; // variable to pass all the necessary values to trackbar callback + + string filename = parser.get("@image"); + image = imread(samples::findFile(filename), IMREAD_COLOR); // Read the image from the specified path + + if (image.empty()) { + cout << "Could not open or find the image" << endl; + return -1; + } + + namedWindow("Shapes", WINDOW_AUTOSIZE); // Create a window to display the results + createTrackbar("Threshold", "Shapes", NULL, 255, processImage, &userData); // Create a threshold trackbar + setTrackbarPos("Threshold", "Shapes", userData.sliderPos); + + for(;;) { + char key = (char)waitKey(0); // Listen for a key press + if (key == 'q' || key == 27) break; // Exit the loop if 'q' or ESC is pressed + + switch (key) { + case 'h': userData.shape = "--convexhull"; break; + case 'a': userData.shape = "--all"; break; + case 't': userData.shape = "--triangle"; break; + case 'c': userData.shape = "--circle"; break; + case 'e': userData.shape = "--ellipse"; break; + case 'r': userData.shape = "--rectangle"; break; + case '0': userData.ellipseMethod = OpenCV_Method; break; + case '1': userData.ellipseMethod = AMS_Method; break; + case '2': userData.ellipseMethod = Direct_Method; break; + default: break; // Do nothing for other keys + } + + processImage(userData.sliderPos, &userData); // Process the image with the current settings + } + + return 0; +} + +// Function to draw the minimum enclosing circle around given points +void drawMinEnclosingCircle(Mat &img, const vector &points) { + Point2f center; + float radius = 0; + minEnclosingCircle(points, center, radius); // Find the enclosing circle + // Draw the circle + circle(img, center, cvRound(radius), Scalar(0, 0, 255), 2, LINE_AA); +} + +// Function to draw the minimum enclosing triangle around given points +void drawMinEnclosingTriangle(Mat &img, const vector &points) { + vector triangle; + minEnclosingTriangle(points, triangle); // Find the enclosing triangle + + if (triangle.size() != 3) { + return; + } + // Use polylines to draw the triangle. The 'true' argument closes the triangle. + polylines(img, triangle, true, Scalar(255, 0, 0), 2, LINE_AA); + +} + +// Function to draw the minimum area rectangle around given points +void drawMinAreaRect(Mat &img, const vector &points) { + RotatedRect box = minAreaRect(points); // Find the minimum area rectangle + Point2f vtx[4]; + box.points(vtx); + // Convert Point2f to Point because polylines expects a vector of Point + vector rectPoints; + for (int i = 0; i < 4; i++) { + rectPoints.push_back(Point(cvRound(vtx[i].x), cvRound(vtx[i].y))); + } + + // Use polylines to draw the rectangle. The 'true' argument closes the loop, drawing a rectangle. + polylines(img, rectPoints, true, Scalar(0, 255, 0), 2, LINE_AA); +} + +// Function to draw the convex hull of given points +void drawConvexHull(Mat &img, const vector &points) { + vector hull; + convexHull(points, hull, false); // Find the convex hull + // Draw the convex hull + polylines(img, hull, true, Scalar(255, 255, 0), 2, LINE_AA); +} + +inline static bool isGoodBox(const RotatedRect& box) { + //size.height >= size.width awalys,only if the pts are on a line or at the same point,size.width=0 + return (box.size.height <= box.size.width * 30) && (box.size.width > 0); +} + +// Function to draw fitted ellipses using different methods +void drawFittedEllipses(Mat &img, const vector &points, int ellipseMethod) { + switch (ellipseMethod) { + case OpenCV_Method: // Standard ellipse fitting + { + RotatedRect fittedEllipse = fitEllipse(points); + if (isGoodBox(fittedEllipse)) { + ellipse(img, fittedEllipse, Scalar(255, 0, 255), 2, LINE_AA); + } + putText(img, "OpenCV", Point(img.cols - 80, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 2, LINE_AA); + } + break; + case AMS_Method: // AMS ellipse fitting + { + RotatedRect fittedEllipseAMS = fitEllipseAMS(points); + if (isGoodBox(fittedEllipseAMS)) { + ellipse(img, fittedEllipseAMS, Scalar(255, 0, 255), 2, LINE_AA); + } + putText(img, "AMS", Point(img.cols - 80, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 2, LINE_AA); + } + break; + case Direct_Method: // Direct ellipse fitting + { + RotatedRect fittedEllipseDirect = fitEllipseDirect(points); + if (isGoodBox(fittedEllipseDirect)) { + ellipse(img, fittedEllipseDirect, Scalar(255, 0, 255), 2, LINE_AA); + } + putText(img, "Direct", Point(img.cols - 80, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 2, LINE_AA); + } + break; + default: // Default case falls back to OpenCV method + { + RotatedRect fittedEllipse = fitEllipse(points); + if (isGoodBox(fittedEllipse)) { + ellipse(img, fittedEllipse, Scalar(255, 0, 255), 2, LINE_AA); + } + putText(img, "OpenCV (default)", Point(img.cols - 80, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 2, LINE_AA); + cout << "Warning: Invalid ellipseMethod value. Falling back to default OpenCV method." << endl; + } + break; + } +} + +// Function to draw shapes +void drawShapes(Mat &img, const vector &points, int ellipseMethod, string shape) { + if (shape == "--circle") { + drawMinEnclosingCircle(img, points); + } else if (shape == "--triangle") { + drawMinEnclosingTriangle(img, points); + } else if (shape == "--rectangle") { + drawMinAreaRect(img, points); + } else if (shape == "--convexhull") { + drawConvexHull(img, points); + } else if (shape == "--ellipse"){ + drawFittedEllipses(img, points, ellipseMethod); + } + else if (shape == "--all") { + drawMinEnclosingCircle(img, points); + drawMinEnclosingTriangle(img, points); + drawMinAreaRect(img, points); + drawConvexHull(img, points); + drawFittedEllipses(img, points, ellipseMethod); + } +} + +// Main function to process the image based on the current trackbar position +void processImage(int position, void* userData){ + + UserData* data = static_cast(userData); + + data->sliderPos = position; + + Mat processedImg = image.clone(); // Clone the original image for processing + Mat gray; + cvtColor(processedImg, gray, COLOR_BGR2GRAY); // Convert to grayscale + threshold(gray, gray, data->sliderPos, 255, THRESH_BINARY); // Apply binary threshold + + Mat filteredImg; + medianBlur(gray, filteredImg, 3); + + vector> contours; + findContours(filteredImg, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); // Find contours + + if (contours.empty()) { + return; + } + + imshow("Mask", filteredImg); // Show the mask + for (size_t i = 0; i < contours.size(); ++i) { + if (contours[i].size() < 5) { // Check if the contour has enough points + continue; + } + drawShapes(processedImg, contours[i], data->ellipseMethod, data->shape); + } + + imshow("Shapes", processedImg); // Display the processed image with fitted shapes +} diff --git a/samples/cpp/minarea.cpp b/samples/cpp/minarea.cpp deleted file mode 100644 index 97264721bf..0000000000 --- a/samples/cpp/minarea.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "opencv2/highgui.hpp" -#include "opencv2/imgproc.hpp" - -#include - -using namespace cv; -using namespace std; - -static void help() -{ - cout << "This program demonstrates finding the minimum enclosing box, triangle or circle of a set\n" - << "of points using functions: minAreaRect() minEnclosingTriangle() minEnclosingCircle().\n" - << "Random points are generated and then enclosed.\n\n" - << "Press ESC, 'q' or 'Q' to exit and any other key to regenerate the set of points.\n\n"; -} - -int main( int /*argc*/, char** /*argv*/ ) -{ - help(); - - Mat img(500, 500, CV_8UC3, Scalar::all(0)); - RNG& rng = theRNG(); - - for(;;) - { - int i, count = rng.uniform(1, 101); - vector points; - - // Generate a random set of points - for( i = 0; i < count; i++ ) - { - Point pt; - pt.x = rng.uniform(img.cols/4, img.cols*3/4); - pt.y = rng.uniform(img.rows/4, img.rows*3/4); - - points.push_back(pt); - } - - // Find the minimum area enclosing bounding box - Point2f vtx[4]; - RotatedRect box = minAreaRect(points); - box.points(vtx); - - // Find the minimum area enclosing triangle - vector triangle; - minEnclosingTriangle(points, triangle); - - // Find the minimum area enclosing circle - Point2f center; - float radius = 0; - minEnclosingCircle(points, center, radius); - - img = Scalar::all(0); - - // Draw the points - for( i = 0; i < count; i++ ) - circle( img, points[i], 3, Scalar(0, 0, 255), FILLED, LINE_AA ); - - // Draw the bounding box - for( i = 0; i < 4; i++ ) - line(img, vtx[i], vtx[(i+1)%4], Scalar(0, 255, 0), 1, LINE_AA); - - // Draw the triangle - for( i = 0; i < 3; i++ ) - line(img, triangle[i], triangle[(i+1)%3], Scalar(255, 255, 0), 1, LINE_AA); - - // Draw the circle - circle(img, center, cvRound(radius), Scalar(0, 255, 255), 1, LINE_AA); - - imshow( "Rectangle, triangle & circle", img ); - - char key = (char)waitKey(); - if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC' - break; - } - - return 0; -}