I have an image with one circle like shape that contains another similar shape. I am trying find the areas of those two shapes. I am using openCv c++ Hough circle detection, but it does not detect the shapes. Is there any other functions in OpenCV can be used to detect the shapes and find the ares?
[EDIT] The image has been added.
Here is my sample code
int main()
{
Mat src, gray;
src = imread( "detect_circles_simple.jpg", 1 );resize(src,src,Size(640,480));
cvtColor( src, gray, CV_BGR2GRAY );
// Reduce the noise so we avoid false circle detection
GaussianBlur( gray, gray, Size(9, 9), 2, 2 );
vector<Vec3f> circles;
// Apply the Hough Transform to find the circles
HoughCircles( gray, circles, CV_HOUGH_GRADIENT, 1, 30, 200, 50, 0, 0 );
cout << "No. of circles : " << circles.size()<<endl;
// Draw the circles detected
for( size_t i = 0; i < circles.size(); i++ )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
circle( src, center, 3, Scalar(0,255,0), -1, 8, 0 );// circle center
circle( src, center, radius, Scalar(0,0,255), 3, 8, 0 );// circle outline
cout << "center : " << center << "\nradius : " << radius << endl;
}
exit(0);
// Show your results
namedWindow( "Hough Circle Transform Demo", CV_WINDOW_AUTOSIZE );
imshow( "Hough Circle Transform Demo", src );
waitKey(0);
return 0;
}
I have a similar approach.
img1 = cv2.imread('disc1.jpg', 1)
img2 = img1.copy()
img = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
#--- Blur the gray scale image
img = cv2.GaussianBlur(img,(5, 5),0)
#--- Perform Canny edge detection (in my case lower = 84 and upper = 255, because I resized the image, may vary in your case)
edges = cv2.Canny(img, lower, upper)
cv2.imshow('Edges', edges )
#---Find and draw all existing contours
_, contours , _= cv2.findContours(edges, cv2.RETR_TREE, 1)
rep = cv2.drawContours(img1, contours, -1, (0,255,0), 3)
cv2.imshow(Contours',rep)
Since you are analyzing the shape of a circular edge, determining the eccentricity of your contours will help in this case.
#---Determine eccentricity
cnt = contours
for i in range(0, len(cnt)):
ellipse = cv2.fitEllipse(cnt[i])
(center,axes,orientation) =ellipse
majoraxis_length = max(axes)
minoraxis_length = min(axes)
eccentricity=(np.sqrt(1-(minoraxis_length/majoraxis_length)**2))
cv2.ellipse(img2,ellipse,(0,0,255),2)
cv2.imshow('Detected ellipse', img2)
Now based on the value given by the eccentricity variable you can come to a conclusion whether your contour is circular or not. The threshold depends on what you consider to be circular or an approximate circle.
If you have complete shapes (the edge completely or very nearly joins) it is generally easier to edge detect -> contour -> analyse the contour shape.
Hough lines or circles are very useful when you only have small fragments of a line or circle, but can be tricky to tune
edit: Try cv::adaptiveThreshold to get the edges, then cv::findContours.
For each contour compare the area to the perimeter to see if it is the right size to be your target. Then do cv::fitEllipse to check if it is a circle and get the accurate center. FindCOntours also has a mode which tells you which contours are inside which others, so you can easily find one circle inside another.
You might (depending on lighting) find the same circle with 2 or more contours, ie. for the inner and outer edge.
Related
I am trying to detect the circle inside traffic light, and I am able to detect only 1 out of the 2 circle, and the size of the circle which i am getting seems to be too big
Input Image: https://i.imgur.com/VkNDt2B.png
Output image: https://i.imgur.com/BBq5tE0.png
int main()
{
Mat src, gray;
src = imread("C:\/test_image2.png", 1);
resize(src, src, Size(640, 480));
cvtColor(src, gray, CV_BGR2GRAY);
// Reduce the noise so we avoid false circle detection
GaussianBlur(gray, gray, Size(9, 9), 2, 2);
vector<Vec3f> circles;
// Apply the Hough Transform to find the circles
HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 1, 60, 200, 20, 0, 35);
// Draw the circles detected
for (size_t i = 0; i < circles.size(); i++)
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
circle(src, center, 3, Scalar(0, 255, 0), -1, 8, 0);// circle center
circle(src, center, radius, Scalar(0, 0, 255), 3, 8, 0);// circle outline
cout << "center : " << center << "\nradius : " << radius << endl;
}
// Show your results
namedWindow("Hough Circle Transform Demo", CV_WINDOW_AUTOSIZE);
imshow("Hough Circle Transform Demo", src);
waitKey(0);
return 0;
}
HoughCircles works best if you know in advance the approx size of the circles you're looking for. I suggest you give a better value for min_radius and max_radius parameters.
In any case, you need to play with param1 and param2 parameters. If circles are not perfect circles you can try to lower the image resolution using the dp parameter (f.ex. with dp = 2 the image is downscaled to half its resolution).
Basically: play with param1 and param2 until your circles are detected, no matter if other circles are detected. Use this result to find out what radius your circles are, then fix the min and max radius to remove most circles you don't want and finally play again with param1 and param2 until only your circles are left.
this is a pretty huge image
try cropping to the traffic light part first ( to get something to begin with ) and then by trying different combinations of min_distance and param_1,param_2 parameter try getting most circles ( even the wrong ones ) detected. find out what values get the most circles and what combination gets least ( or no ) circles and then fine tune the parameters to get lesser circles detected and finally find the perfect combination
1.Some Information: I would like to develop a kind of circle recognition with the help of openCV. I successfully set up a connection between Swift, objc-c++, but strangely I have some problems with the circle recognition algorithm: Not all of the circles in my image gets detected!
2.Have a look at my code:
+(UIImage *)ConvertImage:(UIImage *)image {
cv::Mat matImage;
UIImageToMat(image, matImage);
cv::Mat modImage;
cv::medianBlur(matImage, matImage, 5);
cv::cvtColor(matImage, modImage, CV_RGB2GRAY);
cv::GaussianBlur(modImage, modImage, cv::Size(9, 9), 2, 2);
vector<Vec3f> circles;
cv::HoughCircles(modImage, circles, CV_HOUGH_GRADIENT, 1, 1, 100, 50, 0, 0);
for (auto i = circles.begin(); i != circles.end(); ++i)
std::cout << *i << ' ';
for( size_t i = 0; i < circles.size(); i++ )
{
cv::Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
circle( matImage, center, 3, Scalar(0,255,0), -1, 8, 0 );
circle( matImage, center, radius, Scalar(0,0,255), 3, 8, 0 );
}
UIImage *binImg = MatToUIImage(matImage);
return binImg;
}
As you can see in the image [click] there appears this issue :
Only 3 of 7 circles gets detected!
So in the docs I found the parameters explanation for this line:
cv::HoughCircles(modImage, circles, CV_HOUGH_GRADIENT, 1, 1, 100, 50, 0, 0);
dp = 1: The inverse ratio of resolution.
min_dist = modImage.rows/8: Minimum distance between detected centers.
param_1 = 200: Upper threshold for the internal Canny edge detector.
param_2 = 100*: Threshold for center detection.
min_radius = 0: Minimum radio to be detected. If unknown, put zero as default.
max_radius = 0: Maximum radius to be detected. If unknown, put zero as default.
3.My question
How to get rid of the issue mentioned above?
Any help would be very appreciated :)
For issue number 2 : The outline should be colored, not white!
What color should it be? At any rate you draw that circle in your code with this line.
circle( matImage, center, radius, Scalar(0,0,255), 3, 8, 0 );
If you want to change the color you can change the values you have declared in Scalar(0,0,255).
If you dont want the circle there at all you can remove that line of code.
Your images seems to be noise free. If the image is to contain circle always, You can extract the contours and fit circles using Least Squares
You can get the circle fit equations here. It is a straightforward implementation. Create a structure for the circle parameters (center and radius), fit circle and store the parameters in the structure and use it to draw circle using OpenCV.
You can also generate points on the circle using "ellipse2poly" function.
I have the following code:
findContours( src, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE );
Mat drawing = Mat::zeros( src.size(), CV_8UC3 );
double largest_area = 0;
for( int i = 0; i < contours.size(); i++) { // get the largest contour
area = fabs( contourArea( contours[i] ) );
if( area >= largest_area ){
largest_area = area;
largest_contours.clear();
largest_contours.push_back( contours[i] );
}
}
if( largest_area >= 3000 ){ // draw the largest contour if exceeded minimum largest area
drawContours( drawing, largest_contours, -1, Scalar(0,0,255), 2 );
}
... which produces the following output image:
I want to get coordinates of four points (marked with green), is that possible?
Do you trying to find corners of rectangle in perspective?
You may want to try several solutions:
Use HoughLines for line detection and find their intersection.
Use Generalized Hough Transform
Use Harris corner detector. But you need to filter extra corners.
For similar task I used following procedure (it works fine in my case):
do cv::approxPolyDP for input contour with increasing epsilon parameter until it returns 4 or less polylines. If it returns 4 polylines you may get 4 corner points exact what you need. If it returns less than 4 polylines most probably something is wrong.
Let's say that I have the following output image:
Basically, I have video stream and I want to get coordinates of rectangle only in the output image. Here's my C++ code:
while(1)
{
capture >> frame;
if(frame.empty())
break;
cv::cvtColor( frame, gray, CV_BGR2GRAY ); // Grayscale image
Canny( gray, canny_output, thresh, thresh * 2, 3 );
// Find contours
findContours( canny_output, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
// Draw contours
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
}
cv::imshow( "w", drawing );
waitKey(20); // waits to display frame
}
Thanks.
Look at the definition of the find contours function in the opencv documentation and see the parameters (link):
void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
Parameters: here
Look at contours, like Rafa said each contour is stored in a vector of points and each vector of points is stored in a vector, so, by walking in the outer vector and then walking in the inner vector you'll be finding the points you wish.
However, if you want to detect only the bigger contour you might want to use CV_RETR_EXTERNAL as the mode parameter, because it'll detect only most external contour (the big rectangle).
If you still wish to maintain the smaller contours then you might use the CV_RETR_TREE and work out with the hierarchy structure: Using hierarchy contours
Looking at the documentation, the OutputArrayOfArrays contours is the key.
contours – Detected contours. Each contour is stored as a vector of points.
so, you've got a vector< vector<Point> > contours. The vector<Point>(inside) is the coordinates of a contour, and every contour is stored in a vector.
So for instance, to know the 5-th vector, it's vector<Point> fifthcontour = contours.at(4);
and you have the coordinates in that vector.
You can access to those coordinates as:
for (int i = 0; i < fifthcontour.size(); i++) {
Point coordinate_i_ofcontour = fifthcontour[i];
cout << endl << "contour with coordinates: x = " << coordinate_i_ofcontour.x << " y = " << coordinate_i_ofcontour.y;
}
I am trying to track a custom circular marker in an image, and I need to check that a circle contains a minimum number of other circles/objects. My code for finding circles is below:
void findMarkerContours( int, void* )
{
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
vector<Point> approx;
cv::Mat dst = src.clone();
cv::Mat src_gray;
cv::cvtColor(src, src_gray, CV_BGR2GRAY);
//Reduce noise with a 3x3 kernel
blur( src_gray, src_gray, Size(3,3));
//Convert to binary using canny
cv::Mat bw;
cv::Canny(src_gray, bw, thresh, 3*thresh, 3);
imshow("bw", bw);
findContours(bw.clone(), contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
Mat drawing = Mat::zeros( bw.size(), CV_8UC3 );
for (int i = 0; i < contours.size(); i++)
{
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
// contour
drawContours( drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
//Approximate the contour with accuracy proportional to contour perimeter
cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true) *0.02, true);
//Skip small or non-convex objects
if(fabs(cv::contourArea(contours[i])) < 100 || !cv::isContourConvex(approx))
continue;
if (approx.size() >= 8) //More than 6-8 vertices means its likely a circle
{
drawContours( dst, contours, i, Scalar(0,255,0), 2, 8);
}
imshow("Hopefully we should have circles! Yay!", dst);
}
namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
imshow( "Contours", drawing );
}
As you can see the code to detect circles works quite well:
But now I need to filter out markers that I do not want. My marker is the bottom one. So once I have found a contour that is a circle, I want to check if there are other circular contours that exist within the region of the first circle and finally check the color of the smallest circle.
What method can I take to say if (circle contains 3+ smaller circles || smallest circle is [color] ) -> do stuff?
Take a look at the documentation for
findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
You'll see that there's an optional hierarchy output vector which should be handy for your problem.
hierarchy – Optional output vector, containing information about the image topology. It has as many elements as the number of contours.
For each i-th contour contours[i] , the elements hierarchy[i][0] ,
hiearchyi , hiearchyi , and hiearchyi are set to
0-based indices in contours of the next and previous contours at the
same hierarchical level, the first child contour and the parent
contour, respectively. If for the contour i there are no next,
previous, parent, or nested contours, the corresponding elements of
hierarchy[i] will be negative.
When calling findCountours using CV_RETR_TREE you'll be getting the full hierarchy of each contour that was found.
This doc explains the hierarchy format pretty well.
You are already searching for circles of a certain size
//Skip small or non-convex objects
if(fabs(cv::contourArea(contours[i])) < 100 || !cv::isContourConvex(approx))
continue;
So you can use that to look for smaller circles than the one youve got, instead of looking for < 100 look for contours.size
I imagine there is the same for color also...