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
pull/25422/head
Gursimar Singh 8 months ago committed by GitHub
parent 869016d8b1
commit 3e561d8353
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 13
      modules/imgproc/include/opencv2/imgproc.hpp
  2. 96
      samples/cpp/contours2.cpp
  3. 57
      samples/cpp/convexhull.cpp
  4. 311
      samples/cpp/fitellipse.cpp
  5. 268
      samples/cpp/geometry.cpp
  6. 78
      samples/cpp/minarea.cpp

@ -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

@ -1,96 +0,0 @@
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <math.h>
#include <iostream>
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<vector<Point> > contours;
vector<Vec4i> 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<vector<Point> > 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;
}

@ -1,57 +0,0 @@
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
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<Point> 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<Point> 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;
}

@ -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 <iostream>
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(c<minDims){
scale = scale * (double)minDims/(double)c;
} else {
if(c>maxDims){
scale = scale * (double)maxDims/(double)c;
}
}
int r = (int)(scale*((corner.y + 1.0) - origin.y));
if(r<minDims){
scale = scale * (double)minDims/(double)r;
} else {
if(r>maxDims){
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<Point2f> 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<Point2f> 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<cv::Vec3b>(int(pnt.y), int(pnt.x))[0] = (uchar)color[0];
img.at<cv::Vec3b>(int(pnt.y), int(pnt.x))[1] = (uchar)color[1];
img.at<cv::Vec3b>(int(pnt.y), int(pnt.x))[2] = (uchar)color[2];
};
}
void drawLabels( std::vector<std::string> text, std::vector<cv::Scalar> 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<string>("@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<vector<Point> > 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<std::string> text;
std::vector<cv::Scalar> 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<Point2f> > 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);
vector<Point2f>pts;
for (int j = 0; j < pointsf.rows; j++) {
Point2f pnt = Point2f(pointsf.at<float>(j,0), pointsf.at<float>(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<Point2f> 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);
}

@ -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 <iostream>
#include <vector>
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=<image_path> Default: ellipses.jpg]\n\n";
cout << hot_keys << endl;
}
void processImage(int, void*);
void drawShapes(Mat &img, const vector<Point> &points, int ellipseMethod, string shape);
void drawConvexHull(Mat &img, const vector<Point> &points);
void drawMinAreaRect(Mat &img, const vector<Point> &points);
void drawFittedEllipses(Mat &img, const vector<Point> &points, int ellipseMethod);
void drawMinEnclosingCircle(Mat &img, const vector<Point> &points);
void drawMinEnclosingTriangle(Mat &img, const vector<Point> &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<string>("@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<Point> &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<Point> &points) {
vector<Point> 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<Point> &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<Point> 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<Point> &points) {
vector<Point> 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<Point> &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<Point> &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*>(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<vector<Point>> 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
}

@ -1,78 +0,0 @@
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
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<Point> 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<Point2f> 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;
}
Loading…
Cancel
Save