Hough Transform failed in opencv - opencv

This is a picture above. I am using opencv to process it and I have tried to use Hough Transform, but failed. Also, I found that it is so hard to set relative parameters in Hough Transform.
The codes are as following:
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat srcImg = imread("srccenter.bmp");
Mat greyImg;
cvtColor(srcImg, greyImg, COLOR_BGR2GRAY);
std::vector<cv::Vec3f> circles;
/// Apply the Hough Transform to find the circles
HoughCircles(greyImg, circles, CV_HOUGH_GRADIENT, 1, 10, 100, 20, 0, 0);
/// 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(srcImg, center, 3, Scalar(0, 255, 255), -1);
circle(srcImg, center, radius, Scalar(0, 255, 0), 1);
}
namedWindow("srcImg", WINDOW_NORMAL);
imshow("srcImg", srcImg);
waitKey(0);
return 0;
}
But the result is I can not detect any circle.
How I can detect the inner circle?
Do you have any good ideas?

You need to change min_dist parameter to zero. This parameter is for minimum distance between detected centers. in your case, centers of the circles are so near.
And Also, you must change param_1, the parameter of the Canny edge detector.

Related

Circle detection in a Image

I am trying to use HoughCircles method to detect a circle from an Image, but it looks like that this method is not useful to detect all circle with almost the same centres. For example, if I have 3 circles with almost the same centre it detects as a single circle. Please suggest if there is any way around to find all circles. Here is the source image:
I might be wrong with HoughCircle Methos assumption.
Thanks in advance.
The reason is that when you call HoughCircles you should decide the minimum distance between the detected circle centerces. The same center you mentioned means that zero distance between them. So in this case you should set the minimum distance parameter almost 0.
void cv::HoughCircles ( InputArray image,
OutputArray circles,
int method,
double dp,
double minDist, // You should set this parameter almost zero cos 0 not accepted.
double param1 = 100,
double param2 = 100,
int minRadius = 0,
int maxRadius = 0
)
When I tried with these parameters:
HoughCircles( input, output, CV_HOUGH_GRADIENT, 1, 0.5, 60, 30, 1, 200 );
I get this:
Edit: When I tried some more playing on this, I got 12 circles but the reality is 18 circles(edge circles not included). The reason could be about the image quality. Here is my code and result:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace cv;
using namespace std;
int main()
{
/// Load source image and convert it to gray
Mat src_gray,dst,src = imread("/ur/source/image/image.jpg", 1 );
imshow("Source",src);
int i = 50;
bilateralFilter(src,dst,i,i*2,i/2);
imshow("Output",dst);
cvtColor( dst, src_gray, CV_BGR2GRAY );
vector<Vec3f> circles;
/// Apply the Hough Transform to find the circles
HoughCircles( src_gray, circles, CV_HOUGH_GRADIENT, 1, 0.01, 80, 55, 0, 100 );
Mat zero_mask = Mat::zeros(src.rows,src.cols,CV_8UC3);
/// 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 center
circle( zero_mask, center, 3, Scalar(0,255,0), -1, 8, 0 );
// circle outline
circle( zero_mask, center, radius, Scalar(0,0,255), 1, 8, 0 );
}
cout<<circles.size()<<endl;
imshow("Output2",src_gray);
imshow("outt",zero_mask);
waitKey(0);
return(0);
}
Output:

MinEnclosingCircle is not stable when object fast motion opencv android

I have a project with opencv in android, i try to detect ball and i decide use MinEnclosingCircle, But i have problem when i move object so fast. I cannot get exactly radius of ball. Because of this phenomenon.
When object stand by, I get image like below:
But when i move ball from right to left, left to right so fast i get wrong radius because of this phenomenon
Could you please tell me how to solve this problem. So many thank for your help.
EDIT 1: More detail about image that i get
This is image not draw circle that i captured
And this is image which show contour of ball
It quite a dim, so i draw contour to see easier.
this works quite well I guess:
int main(int argc, char* argv[])
{
//cv::Mat input = cv::imread("C:/StackOverflow/Input/ballMaskClean2.png");
cv::Mat input = cv::imread("C:/StackOverflow/Input/ballMaskClean1.png");
cv::Mat mask;
cv::cvtColor(input, mask, CV_BGR2GRAY);
//cv::inRange(input, cv::Scalar(200, 200, 200), cv::Scalar(255, 255, 255), mask); // was used for your provided images with red circle inside
cv::imshow("mask", mask);
std::vector<std::vector<cv::Point> > contours;
cv::findContours(mask, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
cv::Mat cleanMask = cv::Mat::zeros(input.size(), CV_8UC1);
for (unsigned int i = 0; i < contours.size(); ++i)
{
cv::drawContours(cleanMask, contours, i, 255, -1); // draw filled
}
//cv::imwrite("C:/StackOverflow/Input/ballMaskClean.png", cleanMask);
cv::Mat dt;
cv::distanceTransform(cleanMask, dt, CV_DIST_L1, 3);
double minVal, maxVal;
cv::Point minLoc, maxLoc;
cv::minMaxLoc(dt, &minVal, &maxVal, &minLoc, &maxLoc);
double radius = maxVal;
cv::Point2f center = maxLoc;
cv::circle(input, center, radius, cv::Scalar(0, 255, 0), 2);
cv::imshow("output", input);
cv::imwrite("C:/StackOverflow/Input/ballCircle.png", input);
cv::waitKey(0);
return 0;
}
using these input I get that output:

Circular Region of Interest in Opencv before thresholding

I'm trying to detect a circular object in the middle of my images. Here is a sample image:
The left half is the greyscaled and Gaussian blurred input image; the right half is the same image after Otsu thresholding. The tiny silver of shadow on the lower left corner is leading the Otsu threshold astray. Is there any way to set a circular region of interest so the corner noises can be avoided?
Using the Hough Circle Transform directly on a good thresholded image kind of works for this specific case, even though the detected circle is a little bit offset:
cv::Mat thres;
cv::threshold(gray, thres, 110, 255, cv::THRESH_BINARY);
std::vector<cv::Vec3f> circles;
cv::HoughCircles(thres, circles, cv::HOUGH_GRADIENT, 1, thres.rows/2, 20, 15);
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]);
cv::circle(input, center, 3, cv::Scalar(0, 255, 255), -1);
cv::circle(input, center, radius, cv::Scalar(0, 0, 255), 1);
}
⇨
⇨
On more complex cases you might have to try other threshold methods, as well as fill the internal parts (holes) of the segments to reconstruct them back to an elliptical form.
The processing pipeline illustrated below performs the following operations to improve the detection of the coin:
Converts the input image to grayscale;
Applies a threshold;
Executes a morphology operation to join nearby segments;
Fills the holes inside a segment;
and finally, invokes cv::HoughCircles() to detect the circular shape.
⇨
⇨
⇨
⇨
It's possible to notice that the coin detection is a little bit more centralized with this approach. Anyway, here's the C++ sample code for that magic:
// Load input image
cv::Mat input = cv::imread("coin.jpg");
if (input.empty())
{
std::cout << "!!! Failed to open image" << std::endl;
return -1;
}
// Convert it to grayscale
cv::Mat gray;
cv::cvtColor(input, gray, cv::COLOR_BGR2GRAY);
// Threshold the grayscale image for segmentation purposes
cv::Mat thres;
cv::threshold(gray, thres, 110, 255, cv::THRESH_BINARY);
//cv::imwrite("threhsold.jpg", thres);
// Dirty trick to join nearby segments
cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15));
cv::morphologyEx(thres, thres, cv::MORPH_OPEN, element);
//cv::imwrite("morph.jpg", thres);
// Fill the holes inside the segments
fillHoles(thres);
//cv::imwrite("filled.jpg", thres);
// Apply the Hough Circle Transform to detect circles
std::vector<cv::Vec3f> circles;
cv::HoughCircles(thres, circles, cv::HOUGH_GRADIENT, 1, thres.rows/2, 20, 15);
std::cout << "* Number of detected circles: " << circles.size() << std::endl;
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]);
cv::circle(input, center, 3, cv::Scalar(0,255,255), -1);
cv::circle(input, center, radius, cv::Scalar(0,0,255), 1);
}
cv::imshow("Output", input);
//cv::imwrite("output.jpg", input);
cv::waitKey(0);
Helper function:
void fillHoles(cv::Mat& img)
{
if (img.channels() > 1)
{
std::cout << "fillHoles !!! Image must be single channel" << std::endl;
return;
}
cv::Mat holes = img.clone();
cv::floodFill(holes, cv::Point2i(0,0), cv::Scalar(1));
for (int i = 0; i < (img.rows * img.cols); i++)
if (holes.data[i] == 255)
img.data[i] = 0;
}
You could use Hough for finding circles:
/// Apply the Hough Transform to find the circles
HoughCircles( src_gray, circles, CV_HOUGH_GRADIENT, 1, src_gray.rows/8, 200, 100, 0, 0 );
After you find the biggest circle, you can set to 0 all the pixels outside

Find overlapping/complex circles with OpenCV

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.

opencv - counting non directional edges from canny

can anyone help me how to count the number of non directional edge using opencv cannyedge detection? I have a cannyEdge image from opencv and I would like to have an histogram based on edge directions and there by i can count he number of directional and non directional edges.
I think you are confusing edge detection with gradient detection. Canny provides an edge map based on the gradient magnitude (normally using a Sobel operator, but it can use others) because Canny only returns the thresholded gradient magnitude information it cannot provide you with the orientation information.
EDIT : I should clarify that the Canny algorithm does use gradient orientation for the non-maximum suppression step. However, the OpenCV implementation of Canny hides this orientation information from you, and only returns an edge magnitude map.
The basic algorithm to get magnitude and orientation of the gradient is as follows:
Compute Sobel in the X direction (Sx).
Compute Sobel in the Y direction (Sy).
Compute the gradient magnitude sqrt(Sx*Sx + Sy*Sy).
Compute the gradient orientation with arctan(Sy / Sx).
This algorithm can be implemented using the following OpenCV functions: Sobel, magnitude, and phase.
Below is a sample that computes the gradient magnitude and phase as well as shows a coarse color mapping of the gradient orientations:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
Mat mat2gray(const cv::Mat& src)
{
Mat dst;
normalize(src, dst, 0.0, 255.0, cv::NORM_MINMAX, CV_8U);
return dst;
}
Mat orientationMap(const cv::Mat& mag, const cv::Mat& ori, double thresh = 1.0)
{
Mat oriMap = Mat::zeros(ori.size(), CV_8UC3);
Vec3b red(0, 0, 255);
Vec3b cyan(255, 255, 0);
Vec3b green(0, 255, 0);
Vec3b yellow(0, 255, 255);
for(int i = 0; i < mag.rows*mag.cols; i++)
{
float* magPixel = reinterpret_cast<float*>(mag.data + i*sizeof(float));
if(*magPixel > thresh)
{
float* oriPixel = reinterpret_cast<float*>(ori.data + i*sizeof(float));
Vec3b* mapPixel = reinterpret_cast<Vec3b*>(oriMap.data + i*3*sizeof(char));
if(*oriPixel < 90.0)
*mapPixel = red;
else if(*oriPixel >= 90.0 && *oriPixel < 180.0)
*mapPixel = cyan;
else if(*oriPixel >= 180.0 && *oriPixel < 270.0)
*mapPixel = green;
else if(*oriPixel >= 270.0 && *oriPixel < 360.0)
*mapPixel = yellow;
}
}
return oriMap;
}
int main(int argc, char* argv[])
{
Mat image = Mat::zeros(Size(320, 240), CV_8UC1);
circle(image, Point(160, 120), 80, Scalar(255, 255, 255), -1, CV_AA);
imshow("original", image);
Mat Sx;
Sobel(image, Sx, CV_32F, 1, 0, 3);
Mat Sy;
Sobel(image, Sy, CV_32F, 0, 1, 3);
Mat mag, ori;
magnitude(Sx, Sy, mag);
phase(Sx, Sy, ori, true);
Mat oriMap = orientationMap(mag, ori, 1.0);
imshow("magnitude", mat2gray(mag));
imshow("orientation", mat2gray(ori));
imshow("orientation map", oriMap);
waitKey();
return 0;
}
Using a circle image:
This results in the following magnitude and orientation images:
Finally, here is the gradient orientation map:
UPDATE : Abid actually asked a great question in the comments "what is meant by orientation here?", which I thought needed some further discussion. I am assuming that the phase function doesn't switch coordinate frames from the normal image processing standpoint of positive y-axis is down, and positive x-axis is right. Given this assumption that leads to following image showing the gradient orientation vectors around the circle:
This can be difficult to get used to since the axes are flipped from what we are normally used to in math class... So, gradient orientation is the angle made by the normal vector to the gradient surface in the direction of increasing change.
Hope you found that helpful!

Resources