I have a little problem and i want some help. I have a image with two circles and i want to get the coordinates of the centers. For one circle i solved the problem. I don't know hot to get the second one circle. Any ideas?
Here is my code :
Mat canny_output;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
Canny(BGRFilter,canny_output,100,200,3);
findContours(canny_output,contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE,Point(0,0));
vector<Moments> mu(contours.size());
for (int i=0;i<contours.size();i++)
{
mu[i]=moments(contours[i],false);
}
vector<Point2f> mc(contours.size());
for (int i=0;i<contours.size();i++)
{
mc[i]=Point2f(mu[i].m10/mu[i].m00,mu[i].m01/mu[i].m00);
}
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( 121, 100, 90 );
drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
circle( drawing, mc[i], 4, color, -1, 8, 0 );
}
Here is the binary image
Here is the image with the contours.
You can use Hough Circles for finding circles.
if hough Circles not find circle
try minAreaRect or minEnclosingCircle
I've solved it with Hough circle detection. Thanks.
Related
I have found out the centroid of multiple objects in my image using the code provided here OpenCV examples
Here is the code which found the centroid and stored them in a vector.
cv::Mat InputImage;
cv::Mat CannyOutput;
vector<vector<cv::Point> > contours;
vector<Vec4i> hierarchy;
RNG rng(12345);
InputImage = cv::imread("Untitled.jpg");
//Edge detection
Canny(InputImage, CannyOutput, 100, 150);
//Contour detection
cv::findContours(CannyOutput, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
//Finding Moments
vector<Moments> mu(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mu[i] = moments(contours[i], false);
}
//Calculating Centroid
vector<Point2f> mc(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
}
// Drawing
Mat drawing = Mat::zeros(CannyOutput.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));
//Drawing contour
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point());
//Drawing circles with centroid as centre
circle(drawing, mc[i], 4, color, -1, 8, 0);
}
What I want to do is draw a polygon with the centroid as the vertices. I used drawcontours, polyline and line functions but not getting the desired result. Is there a way to achieve this? . I need it to be achieved in C++
Output image
Desired image
Also, on another note, the code doesnt seem to be displaying the centroid if am replacing the 'color' variable with BGR value. Seems like both contour and centroid has to be of same color if i want to see the centroid. When i gave (0,255,255) for contour and (255,255,0) for centroid, the centroid was not displaying .
For the given sample image, you can use convexhull to obtain the order of centers, and then draw them with polylines.
I'm trying to draw a box around a foot in a photo. I can do it with other objects, including ones that aren't fully in the image (I have pasted my code below). However, when I try to use it with a foot, it draws the contour perfectly but the rectangle is always the around the full image. I have attached pictures below of the result. Is there any way to change my code to fix this, or is there any other method possible of drawing a box around the foot?
Original Image
Contour and Bounding Box
RNG rng(12345);
Mat threshold_output;
vector<vector<cv::Point> > contours;
vector<Vec4i> hierarchy;
Mat srcImage;
UIImageToMat(image, srcImage);
cvtColor(srcImage, srcImage, CV_BGR2GRAY);
blur(srcImage, srcImage, cv::Size(3,3));
threshold(srcImage, threshold_output, 100, 255, THRESH_BINARY );
findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );
vector<vector<cv::Point> > contours_poly( contours.size() );
vector<cv::Rect> boundRect( contours.size() );
for( int i = 0; i < contours.size(); i++ ) {
approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
boundRect[i] = boundingRect( Mat(contours_poly[i]) );
}
Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
if (contourArea(contours[i]) < 1000) {
continue;
}
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours(drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, cv::Point());
rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
cout << "Height:" << boundRect[i].height << " Width: " << boundRect[i].width << endl;
}
return MatToUIImage(drawing);
Since the bottom part of the foot reaches the image boundary, the contour seems to wrap around the perimeter of the image and thereby create a large bounding box that includes the entire image.
A quick and dirty solution will be to set the border pixels of the image to zero before applying findContours(). For a better solution, you have to think about the actual problem that you are trying to solve.
I want to compute the red circles radius (fig 2). I have troubles finding these circles using HoughCircles from OpenCV. As you can see in fig. 2 I can only find the little circles in center which are shown in black using HoughCircles.
original fig 2.
Since I know the center of the red circles (which are the same as the red ones), is there a way to compute simply the radius of the red circles ?
Is it also possible to have a generic way of computing radius of circles on a more complex image such as this one :
Edit : Here the interesting part of my code after obtaining fig 2 :
threshold(maskedImage, maskedImage, thresh, 255, THRESH_BINARY_INV | THRESH_OTSU);
std::vector<Vec3f> circles;
// Canny(maskedImage, maskedImage, thresh, thresh * 2, 3);
HoughCircles(maskedImage, circles, CV_HOUGH_GRADIENT, 1, src_gray.rows / 4, cannyThreshold, accumulatorThreshold, 0, 0);
Mat display = src_display.clone();
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 center
circle(display, center, 3, Scalar(0, 255, 0), -1, 8, 0);
// circle outline
circle(display, center, radius, Scalar(0, 0, 255), 3, 8, 0);
}
I have tried to use play with cannyThreshold and accumulator without results. Real images are 5x biggers. Here a link for example 1 after threshold.
Thanks
You already know the smaller circles in the image(which you have drawn in black).
Prepare a mask image using these circles so the areas having smaller circles will have non-zero pixels. We'll call it mask:
In the original image, fill these circle areas in a dark color(say black). This will result in an image like your fig 2. We'll call it filled
Threshold the filled image to obtain the dark areas. We'll call it binary. You can use Otsu thresholding for this. Result will look something like this:
Take the distance transform of this binary image. Use an accurate distance estimation method for this. We'll call this dist. It'll look something like this. The colored one is just a heat map for more clarity:
Use the mask to obtain the peak regions from dist. The max value of each such region should give you the radius of the larger circle. You can also do some processing on these regions to arrive at a more reasonable value for radius rather than just picking up the max.
For selecting the regions, you can either find the contours of the mask and then extract that region from dist image, or, since you already know the smaller circles from applying hough-circle transform, prepare a mask from each of those circles and extract that region from dist image. I'm not sure if you can calculate max or other stats by giving a mask. Max will definitely work because the rest of the pixels are 0. You might be able calculate the stats of the region if you extract those pixels to another array.
Figures below show such mask and the extracted region from dist. For this I get a max around 29 which is consistent with the radius of that circle. Note that the images are not to scale.
mask for a circle, extracted region from dist
Here's the code (I'm not using hough-circles transform):
Mat im = imread(INPUT_FOLDER_PATH + string("ex1.jpg"));
Mat gray;
cvtColor(im, gray, CV_BGR2GRAY);
Mat bw;
threshold(gray, bw, 0, 255, CV_THRESH_BINARY|CV_THRESH_OTSU);
// filtering smaller circles: not using hough-circles transform here.
// you can replace this part with you hough-circles code.
vector<int> circles;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(bw, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
{
Rect rect = boundingRect(contours[idx]);
if (abs(1.0 - ((double)rect.width/rect.height) < .1))
{
Mat mask = Mat::zeros(im.rows, im.cols, CV_8U);
drawContours(mask, contours, idx, Scalar(255, 255, 255), -1);
double area = sum(mask).val[0]/255;
double rad = (rect.width + rect.height)/4.0;
double circArea = CV_PI*rad*rad;
double dif = abs(1.0 - area/circArea);
if (dif < .5 && rad < 50 && rad > 30) // restrict the radius
{
circles.push_back(idx); // store smaller circle contours
drawContours(gray, contours, idx, Scalar(0, 0, 0), -1); // fill circles
}
}
}
threshold(gray, bw, 0, 255, CV_THRESH_BINARY_INV|CV_THRESH_OTSU);
Mat dist, distColor, color;
distanceTransform(bw, dist, CV_DIST_L2, 5);
double max;
Point maxLoc;
minMaxLoc(dist, NULL, &max);
dist.convertTo(distColor, CV_8U, 255.0/max);
applyColorMap(distColor, color, COLORMAP_JET);
imshow("", color);
waitKey();
// extract dist region corresponding to each smaller circle and find max
for(int idx = 0; idx < (int)circles.size(); idx++)
{
Mat masked;
Mat mask = Mat::zeros(im.rows, im.cols, CV_8U);
drawContours(mask, contours, circles[idx], Scalar(255, 255, 255), -1);
dist.copyTo(masked, mask);
minMaxLoc(masked, NULL, &max, NULL, &maxLoc);
circle(im, maxLoc, 4, Scalar(0, 255, 0), -1);
circle(im, maxLoc, (int)max, Scalar(0, 0, 255), 2);
cout << "rad: " << max << endl;
}
imshow("", im);
waitKey();
Results(scaled):
Hope this helps.
I am just starting out with opencv and I am trying to make a program that puts squares around a picture of rocks on some sand. The documentation for the function here includes an example of how to use it.
findContours( src, contours, hierarchy,
CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
The prototype of findContours is
void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point()) ;
I have two questions.
1. The third argument in the example hierarchy is a vector<Vec4i> does not match the type findContours expects. Why is that?
2. How does one use the data stored in contours to find where the contours are to create a bounding box?
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours( mask, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_TC89_KCOS);
for ( size_t i=0; i<contours.size(); ++i )
{
cv::drawContours( img, contours, i, Scalar(200,0,0), 1, 8, hierarchy, 0, Point() );
cv::Rect brect = cv::boundingRect(contours[i]);
cv::rectangle(img, brect, Scalar(255,0,0));
}
I've been trying to find the best way to detect the 4 black squares on the paper and use them to isolate the paper in its own image.
It seems that on your image there are only 4 black squares so what you have to do is:
Convert image to grays
Do threshold
Find black contours (before doing this in OpenCV you have to invert your image, because by default OpenCV finds white contours)
Cycle through these contours and find bounding rectangle.
Do the check:
A) Rectangle's area is bigger that some constant (in my solution it was 100)
B) Rectangle's width/height is near 1.0 (in my soultion it was [0.9, 1.1] range)
The code:
Mat img = imread("test.jpg"), gray;
vector<Vec4i> hierarchy;
vector<vector<Point2i> > contours;
cvtColor(img, gray, CV_BGR2GRAY);
threshold(gray, gray, 100, 255, THRESH_BINARY);
bitwise_not(gray, gray);
findContours(gray, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
for(size_t i=0; i<contours.size(); i++)
{
Rect rect = boundingRect(contours[i]);
double k = (rect.height+0.0)/rect.width;
if (0.9<k && k<1.1 && rect.area()>100)
{
drawContours(img, contours, i, Scalar(0,0,255));
}
}
imshow("result", img);
waitKey();
Result:
Also read this SO discussion - you don't need that 4 squares to detect paper.