I draw a circle with a fixed input Point. Now I really want to get a vector of all the Points in that circle includes filled area inside. I tried the code below but it only get the border. I can not use Contours function because I have used it many times so it would be very complicated. Please give me advice thank you so much
vector<Point> allpoints;
Point center = Point(370, 200);
void getPoints()
{
Size axes(20, 20);
ellipse2Poly(center, axes, 0, 0, 360, 1, allpoints);
}
void draw(Mat &BGR_frame)
{
circle(BGR_frame, center, 20, Scalar(0, 255, 0),CV_FILLED ,2);
getPoints();
}
A simple approach is to draw the circle on a black initialized mask, and retrieve the non-black points from there:
void draw(Mat &BGR_frame)
{
circle(BGR_frame, center, 20, Scalar(0, 255, 0),CV_FILLED ,2);
// Black initialized mask, same size as 'frame'
Mat1b mask(frame.rows, frame.cols, uchar(0));
// Draw white circle on mask
circle(mask, center, 20, Scalar(255), CV_FILLED, 2);
// Find non zero points on mask, and put them in 'allpoints'
findNonZero(mask, allpoints);
}
Alternatively, you can scan all pixels of the matrix, and keep points that satisfy the equation of being internal points of the circle:
Point c(370, 200);
int r = 20;
void draw(Mat &BGR_frame)
{
circle(BGR_frame, c, r, Scalar(0, 255, 0),CV_FILLED ,2);
for (int y = 0; y < mask.rows; ++y) {
for (int x = 0; x < mask.cols; ++x) {
// Check if this is an internal point
if ((x - c.x)*(x - c.x) + (y - c.y)*(y - c.y) <= (r*r)) {
allpoints.push_back(Point(x,y));
}
}
}
}
Related
I need some help with opencv and gearwheel detection.
My task: count gearwheel teeth from images like this:
Im trying to use HoughCircles method but got bad results lile this:
Otsu threshold:
Code (on openCV Java wrapper):
Mat des = new Mat(sourceImg.rows(), sourceImg.cols(), sourceImg.type());
Imgproc.cvtColor(sourceImg, sourceImg, Imgproc.COLOR_BGR2GRAY, 4);
Imgproc.GaussianBlur(sourceImg,des, new Size(3,3),0,0);
Imgproc.threshold(des, des, 0, 255, Imgproc.THRESH_OTSU | Imgproc.THRESH_OTSU);
Imgproc.Canny(des, des, 0 , 1);
displayImage(Mat2BufferedImage(des));
Mat circles = new Mat();
Imgproc.HoughCircles(des, circles, Imgproc.CV_HOUGH_GRADIENT, 1.0, 50, 70.0, 30.0, 100, 0);
/// Draw the circles detected
for(int i = 0; i < circles.cols(); i++ )
{
double vCircle[] = circles.get(0,i);
if (vCircle == null)
break;
Point pt = new Point(Math.round(vCircle[0]), Math.round(vCircle[1]));
int radius = (int)Math.round(vCircle[2]);
// draw the found circle
Core.circle(des, pt, radius, new Scalar(255,255,255), 3);
Core.circle(des, pt, 3, new Scalar(255,0,0), 3);
}
What is right way for my task? How to count teeth? Thanks for your answers.
Here's what I tried. The code is in C++ but you can easily adapt it to Java.
load the image and resize it to half the size
erode the image, use Canny to detect edges, then dilate to connect the edges
find contours and choose the largest contour
find the convexhull of this largest contour. Number of point in the convexhull will give you a rough value for the number of teeth
Here's the largest contour and the convexhull points:
I get a value of 77 with the following code.
Mat gray = imread("16atchc.jpg", 0);
Mat small, bw, er, kernel;
resize(gray, small, Size(), .5, .5);
kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
erode(small, er, kernel);
Canny(er, bw, 50, 150);
dilate(bw, bw, kernel);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
int imax = 0, areamax = 0;
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]);
int area = rect.width * rect.height;
if (area > areamax)
{
areamax = area;
imax = idx;
}
}
vector<Point> hull;
convexHull(contours[imax], hull);
cout << contours[imax].size() << ", " << hull.size() << endl;
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'm using OpenCV 2.4.6 and Visual C++ 2010. I am trying to find the centroid of the contours of BoundingRect. Below is my code. Here I am getting the rectangles in proper positions. But the circles that i'm trying to point on the centroid of each Rectangle is shifted towards upper left side. How can i get those circle points in the centroid of the rectangles?
int x = cvFindContours(imgThresh,storage,&contours,sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
//printf("%d\n",contours->total);
for (; contours != 0; contours = contours->h_next)
{
rect = cvBoundingRect(contours); //extract bounding box for current contour
//drawing rectangle
printf("%d %d %d %d\n",rect.x, rect.y,rect.width,rect.height);
if(rect.width*rect.height<1400 && (rect.width*rect.height)>700)
{
cvRectangle(mCVImageColor,cvPoint(rect.x, rect.y),cvPoint(rect.x+rect.width, rect.y+rect.height),cvScalar(0, 0, 255),2, 8, 0);
CvPoint centroid[1];
centroid[0].x = ((rect.x+rect.width)/2);
centroid[0].y = ((rect.y+rect.height)/2);
cvCircle( mCVImageColor, centroid[0], 5, CV_RGB(255, 0, 0),-1,0,0);
}
}
This part is wrong:
centroid[0].x = ((rect.x+rect.width)/2);
centroid[0].y = ((rect.y+rect.height)/2);
the centroid is rect.x + rect.width/2 , rect.y + rect.height/2 (no parenthesis)
Given the following (canny'd) image, I'd like to grab the start/end endpoints of the full upper horizontal line.
I've tried opencv's HoughLineP function, but get segments rather than a full line. I realise that this is due to the camera calibration distortion.
Is there some other technique that is more forgiving when it comes to curvy distortions?
How does the theta parameter (HoughLineP function) work?
Alternatively, what would be a good way to join points that close to each other (with somehow similar angle)
Original:
Code:
Mat scene = imread("houghLines.png", 0);
vector<Vec4i> lines;
HoughLinesP(scene, lines, 1, CV_PI/180, 40, 100, 20 );
cvtColor(scene, scene, COLOR_GRAY2BGR); scene *= 0.5; // convert to colour
auto colours = generateColours((int)lines.size());
for(int i = 0; i < lines.size(); i++) {
auto l = lines[i];
line(scene, Point(l[0], l[1]), Point(l[2], l[3]), colours[i], 1, CV_AA);
}
imshow("scene", scene);
imwrite(getTempFilename(), scene);
waitKey();
Result:
I am drawing circles around a location point in the MapField like in the code given below:
public void drawCircleMap(int [] radius)
{
int i = 0;
Graphics graphics = null;
int x2,y2;
bmparr = new Bitmap[radius.length];
for(int j=0;j<radius.length;j++)
{
XYPoint fieldOut = new XYPoint();
convertWorldToField(mPoints[1], fieldOut);
x2 = fieldOut.x;
y2 = fieldOut.y;
bmparr[i] = new Bitmap(getWidth(), getHeight());
bmparr[i].createAlpha(Bitmap.ALPHA_BITDEPTH_8BPP);
graphics = Graphics.create( bmparr[i]);
graphics.setColor(Color.BLUE);
graphics.drawEllipse(x2, y2, x2+radius[j], y2, x2,y2+radius[j], 0, 360);
graphics.fillEllipse(x2, y2, x2+radius[j], y2, x2,y2+radius[j], 0, 360);
i++;
}
}
protected void paint(Graphics graphics) {
super.paint(graphics);
for(int i =0 ;i < bmparr.length;i++)
{
graphics.setGlobalAlpha(100);
graphics.drawBitmap(0, 0, bmparr[i].getWidth(), bmparr[i].getHeight(), bmparr[i], 0, 0);
}
}
I want to draw 4 circles, now as i draw more number of circles, the map seems to fade out, Can someone please tell me how i could solve this issue?
I have solved this problem by drawing a transparent background for every bitmap:
bmparr = new Bitmap[radius.length];
for(int j=0;j<radius.length;j++)
{
XYPoint fieldOut = new XYPoint();
convertWorldToField(mPoints[1], fieldOut);
x2 = fieldOut.x;
y2 = fieldOut.y;
bmparr[i] = new Bitmap(getWidth(), getHeight());
bmparr[i].createAlpha(Bitmap.ALPHA_BITDEPTH_8BPP);
int[] argb = new int[getWidth() * getHeight()];
bmparr[i].getARGB(argb, 0, getWidth(), 0, 0, getWidth(), getHeight());
for(int k = 0; k < argb.length; k++)
{
argb[k] = 0x00000000;
}
bmparr[i].setARGB(argb, 0, getWidth(), 0, 0, getWidth(), getHeight());
graphics = Graphics.create( bmparr[i]);
graphics.setColor(Color.BLUE);
graphics.drawEllipse(x2, y2, x2+radius[j], y2, x2,y2+radius[j], 0, 360);
graphics.fillEllipse(x2, y2, x2+radius[j], y2, x2,y2+radius[j], 0, 360);
i++;
}
I believe your problem is with alpha blending. You are drawing each image, one on top of the other, with approximately 50% alpha. So, the first radius "covers" half of the existing pixel intensity. Then the next radius covers half of the remaining intensity, or 75% of the original intensity. And so on... each time you draw an image, it is covering up more and more of the original intensity.
If you want all of your circles to have a common intensity, you will need to re-think your approach. For example, consider drawing all of your circles in a single bitmap before drawing that on top of the map. Or consider having each larger circle leave a 100% transparent "hole" where the smaller circles will be.