opencvgeometryhough-transform

Detect semicircle in OpenCV


I am trying to detect full circles and semicircles in an image.enter image description here

I am following the below mentioned procedure: Process image (including Canny edge detection). Find contours and draw them on an empty image, so that I can eliminate unwanted components (The processed image is exactly what I want). Detect circles using HoughCircles. And, this is what I get:

enter image description here

I tried varying the parameters in HoughCircles but the results are not consistent as it varies based on lighting and the position of circles in the image. I accept or reject a circle based on its size. So, the result is not acceptable. Also, I have a long list of "acceptable" circles. So, I need some allowance in the HoughCircle params. As for the full circles, it's easy - I can simply find the "roundness" of the contour. The problem is semicircles!

Please find the edited image before Hough transformenter image description here


Solution

  • Use houghCircle directly on your image, don't extract edges first. Then test for each detected circle, how much percentage is really present in the image:

    int main()
    {
        cv::Mat color = cv::imread("../houghCircles.png");
        cv::namedWindow("input"); cv::imshow("input", color);
    
        cv::Mat canny;
    
        cv::Mat gray;
        /// Convert it to gray
        cv::cvtColor( color, gray, CV_BGR2GRAY );
    
        // compute canny (don't blur with that image quality!!)
        cv::Canny(gray, canny, 200,20);
        cv::namedWindow("canny2"); cv::imshow("canny2", canny>0);
    
        std::vector<cv::Vec3f> circles;
    
        /// Apply the Hough Transform to find the circles
        cv::HoughCircles( gray, circles, CV_HOUGH_GRADIENT, 1, 60, 200, 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]);
            cv::circle( color, center, 3, Scalar(0,255,255), -1);
            cv::circle( color, center, radius, Scalar(0,0,255), 1 );
        }
    
        //compute distance transform:
        cv::Mat dt;
        cv::distanceTransform(255-(canny>0), dt, CV_DIST_L2 ,3);
        cv::namedWindow("distance transform"); cv::imshow("distance transform", dt/255.0f);
    
        // test for semi-circles:
        float minInlierDist = 2.0f;
        for( size_t i = 0; i < circles.size(); i++ ) 
        {
            // test inlier percentage:
            // sample the circle and check for distance to the next edge
            unsigned int counter = 0;
            unsigned int inlier = 0;
    
            cv::Point2f center((circles[i][0]), (circles[i][1]));
            float radius = (circles[i][2]);
    
            // maximal distance of inlier might depend on the size of the circle
            float maxInlierDist = radius/25.0f;
            if(maxInlierDist<minInlierDist) maxInlierDist = minInlierDist;
    
            //TODO: maybe paramter incrementation might depend on circle size!
            for(float t =0; t<2*3.14159265359f; t+= 0.1f) 
            {
                counter++;
                float cX = radius*cos(t) + circles[i][0];
                float cY = radius*sin(t) + circles[i][1];
    
                if(dt.at<float>(cY,cX) < maxInlierDist) 
                {
                    inlier++;
                    cv::circle(color, cv::Point2i(cX,cY),3, cv::Scalar(0,255,0));
                } 
               else
                    cv::circle(color, cv::Point2i(cX,cY),3, cv::Scalar(255,0,0));
            }
            std::cout << 100.0f*(float)inlier/(float)counter << " % of a circle with radius " << radius << " detected" << std::endl;
        }
    
        cv::namedWindow("output"); cv::imshow("output", color);
        cv::imwrite("houghLinesComputed.png", color);
    
        cv::waitKey(-1);
        return 0;
    }
    

    For this input:

    enter image description here

    It gives this output:

    enter image description here

    The red circles are Hough results.

    The green sampled dots on the circle are inliers.

    The blue dots are outliers.

    Console output:

    100 % of a circle with radius 27.5045 detected
    100 % of a circle with radius 25.3476 detected
    58.7302 % of a circle with radius 194.639 detected
    50.7937 % of a circle with radius 23.1625 detected
    79.3651 % of a circle with radius 7.64853 detected
    

    If you want to test RANSAC instead of Hough, have a look at this.