2
$\begingroup$

I am working on finding the center and radius of a laser spot, which I plan on doing by using the HoughCircle algorithm in opencv 3.2 for Visual C++.

One of my sample images is a very well defined spot, but the returned Hough circles are very different from what I expect. Below is a screenshot of the original and processed image, with a purple circle as my expectation and a red circle as the calculated Hough. I have trackbars controlling the dp, param1, and param2 of the Hough call. In the first pair of images I have only blurred and thresholded the processed image, in the second pair I have dilated once with a 5x5 ellipse mask.

original image first image first image first image first image

I have moved the sliders around for the Hough parameters, but these two circles are the best approximation I have gotten, and neither are correct. The parameters used were: Hough1 = 100, Hough2= 21, dp= 1. I don't think I need to adjust my blurring, thresholding, or dilating parameters because I think the current circle should be good enough to fit a better estimate to. Below is the Canny view of the spot without dilation:

canny

When I increase my dilation iterations to 7 and adjust my Hough parameters, I am given many circles that appear to be inaccurate. As I decrease my Hough parameter 2, eventually some circles show up that look to be a best fit, but are maybe the 10th circle to show up. Below is an image with the extra dilations and lower dp/param2 values, with the green circles representing a Hough circle that was reasonably close to my manually estimated purple circle.

What steps can I take to automatically grab and isolate the circle that a human user would accept as a reasonable best fit?

image image image

Code:

int houghParam1 = 100;
int houghParam2 = 100;
int dp = 10; //divided by 10 later
int x=616;
int y=444;
int radius = 398;
int iterations = 0;

int main()
{
namedWindow("Circled Orig");
namedWindow("Processed", 1);
namedWindow("Circles");
namedWindow("Parameters");
namedWindow("Canny");
createTrackbar("Param1", "Parameters", &houghParam1, 200);
createTrackbar("Param2", "Parameters", &houghParam2, 200);
createTrackbar("dp", "Parameters", &dp, 20);
createTrackbar("x", "Parameters", &x, 1200);
createTrackbar("y", "Parameters", &y, 1200);
createTrackbar("radius", "Parameters", &radius, 900);
createTrackbar("dilate #", "Parameters", &iterations, 20);
std::string directory = "Secret";
std::string suffix = ".pgm";
Mat processedImage;
Mat origImg;
for (int fileCounter = 2; fileCounter < 3; fileCounter++) //1, 12
{
    std::string numString = std::to_string(static_cast<long long>(fileCounter));
    std::string imageFile = directory + numString + suffix;
    testImage = imread(imageFile);
    Mat bwImage;
    cvtColor(testImage, bwImage, CV_BGR2GRAY);
    GaussianBlur(bwImage, processedImage, Size(9, 9), 9);
    threshold(processedImage, processedImage, 25, 255, THRESH_BINARY); //THRESH_OTSU
    int numberContours = -1;
    int iterations = 1;
    imshow("Processed", processedImage);
}

vector<Vec3f> circles;
Mat element = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
float dp2 = dp;
while (true)
{
    float dp2 = dp;
    Mat circleImage = processedImage.clone();
    origImg = testImage.clone();
    if (iterations > 0) dilate(circleImage, circleImage, element, Point(-1, -1), iterations);
    Mat cannyImage;
    Canny(circleImage, cannyImage, 100, 20);
    imshow("Canny", cannyImage);
    HoughCircles(circleImage, circles, HOUGH_GRADIENT, dp2/10, 5, houghParam1, houghParam2, 300, 5000);
    cvtColor(circleImage, circleImage, CV_GRAY2BGR);
    for (size_t i = 0; i < circles.size(); i++)
    {
        Scalar color = Scalar(0, 0, 255);
        Point center2(cvRound(circles[i][0]), cvRound(circles[i][1]));
        int radius2 = cvRound(circles[i][2]);
        if (abs(center2.x - x) < 10 && abs((center2.y - y) < 10) && abs(radius - radius2) < 20)  color = Scalar(0, 255, 0);
        circle(circleImage, center2, 3, color, -1, 8, 0);
        circle(circleImage, center2, radius2, color, 3, 8, 0);
        circle(origImg, center2, 3, color, -1, 8, 0);
        circle(origImg, center2, radius2,color, 3, 8, 0);
    }

    //Manual circles
    circle(circleImage, Point(x, y), 3, Scalar(128, 0, 128), -1, 8, 0);
    circle(circleImage, Point(x, y), radius, Scalar(128, 0, 128), 3, 8, 0);
    circle(origImg, Point(x, y), 3, Scalar(128, 0, 128), -1, 8, 0);
    circle(origImg, Point(x, y), radius, Scalar(128, 0, 128), 3, 8, 0);
    imshow("Circles", circleImage);
    imshow("Circled Orig", origImg);

    int x = waitKey(50);


}
Mat drawnImage;
cvtColor(processedImage, drawnImage, CV_GRAY2BGR);
return 1;
}
$\endgroup$
1
  • $\begingroup$ Why are you using Hough transform for this? If the circle is always so clearly defined, I would think there would be better methods. $\endgroup$
    – endolith
    Jan 31, 2019 at 16:06

1 Answer 1

3
$\begingroup$

The Hough Transform is "right". Because it searches for the most consistent "shape" given the accumulated values. If all you would like to do is to find the center of the spot, there are other techniques (from simple to...less simple) that you could use.

The Hough Transform produces another image that is composed of the accumulated "projections" of the image at its input at different angles. The combined result of this is that:

  1. Points map to arcs

    • Because a point, offset from the centre of rotation appears to the rotating projection as "moving" harmonically.
  2. Lines map to points

    • Because, a line is really a "moving" point and once the rotating projection gets vertical to it, it produces the maximum sum.
  3. Polygons map to points in space with distinct configurations between them.

    • Because of #1,#2 above. A polygon is "interperted" here as a shape that is composed of a set of straight line segments. So, to detect a rectangle (a hollow rectangle defined by 4 lines marking its perimeter), all that you have to do is to look for 4 points poised at specific distances between them. Similarly, to detect a circle you look for specific points in the Hough Space.

When you present a thresholded version of the "blob" to the Hough Transform, it sees a big blob of straightlines that is consistent across rotation. Each one of the "lines" is a chord that is picked out of the white-filled circle.

So, what the Hough Transform "sees" is a set of angle indices associated with a large accumulation and a "variance" around that accumulation which represents the uneveness of the blob.

This is probably throwing the circle detection part off because it is impossible for it to detect the "points" it is looking for.

Better results would be expected by running edge detection on the blob prior to sending it to the Hough Transform.

But, more generally, why use the Hough Transform to detect the center of the blob in this scenario? Here are some alternatives:

  • Threshold the image, then find the "bounding box" of the corrdinates of the white pixels (extrema points, the min,max of all white pixels in the X,Y directions), then find the center of that bounding box.

  • Threshold the image, apply a line detection or a convolution matrix to isolate perpheral points and then find the convex hull of those points. In fact, even if you didn't do the line detection or peripheral point detection, the convex hull would still work (but you would be increasing the computational complexity with lots of redundant data). The convex hull will give you the periphery you are looking for and it is also not a bad "generalisation" because the basic assumption behind its usage is that the laser spot is definitely convex...which is a reasonable assumption to make for the blob shape that makes it to the screeen (not necessarily the laser's apperture).

Hope this helps.

$\endgroup$
5
  • $\begingroup$ Thank you, I appreciate your explanations. I'll have to explore some of your suggestions further before I accept your answer, but for now I'm going to proceed with trying to convolve with different types of kernels and use the convex hull. $\endgroup$ Mar 1, 2017 at 22:10
  • $\begingroup$ @jalconvolvon You are welcome. The matrix I refer to as the "isolate peripheral pixels" is the -1 with the 8 in the centre. The convex hull will return the points at the periphery of the spot, as a list of $(x,y$) points. You would then have to derive the centre by averaging the $x$ and $y$ coordinates separately. Can I please ask what your application is? $\endgroup$
    – A_A
    Mar 2, 2017 at 13:53
  • $\begingroup$ Yes, we are using the center of the larger outline to determine how much we need to move motors to align an interferometer. My task is to make some robust code that will automatically determine the center (currently the images are fed into the imfindcircles function in MATLAB and parameters are manually chosen, although I'm not certain how they've been getting accurate results given that the imfindcircles function is a Hough circle transform.) Some of the images are pretty noisy (1 $\endgroup$ Mar 3, 2017 at 18:35
  • $\begingroup$ [2] (jakereynolds.net/wp-content/uploads/2017/03/8test.jpg) [3] (jakereynolds.net/wp-content/uploads/2017/03/10test.jpg) [thresholded] (jakereynolds.net/wp-content/uploads/2017/03/threshImage.jpg) I think right now my game plan is to isolate the good region in the thresholded image I linked and try to do a RANSAC fit of a circle to those points. If you have any suggestions on other methods, or ways to isolate the "clean" region of the circle, it would be greatly appreciated. $\endgroup$ Mar 3, 2017 at 18:38
  • $\begingroup$ @jalconvolvon Thank you. In terms of suggestions, I would mostly "stick" to my response above. It would help a bit if you were to share a bit more information about the actual application. Are the motors "focusing" the interferometer? Is the pattern visible on the circle sometimes? Do you have grayscale images or are they all saturated already? Can you add any neutral density filters to bring the intensity down so that you can obtain a nice unsaturated beam image? $\endgroup$
    – A_A
    Mar 3, 2017 at 19:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.