Circular area of ​​interest in Opencv to the threshold

I am trying to detect a round object in the middle of my images. Here's a sample image:

The left half is a gray and Gaussian blurred input image; the right half is the same image after the Otsu threshold. A tiny silver shadow in the lower left corner leads to the doorstep of the Otsu Rapids. Is there a way to set a circular area of ​​interest so that corner noise can be avoided?

+3


source to share


2 answers


Using the Hough Circle Transform directly on a good threshold image, kind of works for this particular case, although the circle detected is a bit bit 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);
}

      

5YsZy.jpgrouvG.jpgLgZtW.jpg

In more complex cases, you may have to try other sill methods and also fill in the inner parts (holes) of the segments to restore them back to an elliptical shape.

The processing tube shown below performs the following operations to improve coin detection:

  • Converts the input image to grayscale,
  • Applies a threshold;
  • Performs a morphology operation to combine adjacent segments;
  • Fills holes inside a segment;
  • and finally calls cv::HoughCircles()

    to detect the circular shape.


5YsZy.jpgrouvG.jpganooP.jpg4o2Aa.jpgbTkIq.jpg

You can see that coin detection is a bit centralized with this approach. Anyway, here's a sample C ++ code for this 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;
}

      

+5


source


You can use Hough to find 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 largest circle, you can set 0 all pixels outside

+1


source







All Articles