Dmitry Uspenskiy
Dmitry Uspenskiy

Reputation: 51

OpenCV HoughCircles parameters for detecting circles (microstructure spheres)

I am creating program that helps processing microstructure images. One of the function is detecting circles with the same radius. User draws one circle, my program spots others. I've already implemented distance transform method

I am trying to create method that uses HoughCircles. However, I am confused with its parameters.

My code:

def find_circles_with_radius_haugh(path, radius):
    img = cv2.imread(path)
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    circles = cv2.HoughCircles(img_gray, cv2.HOUGH_GRADIENT, int(radius),
                               1.5,
                               param1=80, param2=40,
                               minRadius=int(radius * 0.9),
                               maxRadius=int(radius * 1.1))

    res = list()
    if circles is not None:
        for i in circles[0, :]:
            res.append((i[0], i[1], i[2]))

    return res

Original picture: enter image description here

My result of detecting circles with radius 57 pixels (+- 10%): enter image description here

Please help me with better processing images like that.

I might try findContours method, but I don't know any filters that will make borders on this picture clearer.

Upvotes: 0

Views: 518

Answers (2)

Dmitry Uspenskiy
Dmitry Uspenskiy

Reputation: 51

Rewrote @fana code in Python

import cv2
import numpy as np

img = cv2.imread('spheres1.bmp')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.resize(gray, (0, 0), gray, 0.5, 0.5, cv2.INTER_AREA)
cv2.imwrite("resized.png", gray)

radius = round(57 * 0.5)

test = cv2.medianBlur(gray, 5)
struct_elem = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
# might be better to use "I" matrix
# struct_elem = np.ones((3,3), np.uint8) 

test = cv2.morphologyEx(test, cv2.MORPH_GRADIENT, kernel=struct_elem)
cv2.imwrite("MorphologyEx.png", test)

edge_img = cv2.adaptiveThreshold(test, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, int(len(test) / 6) | 0x01, -6)
cv2.imwrite("EdgeImg.png", edge_img );

buffer_for_imwrite = edge_img.copy()

filter_radius = radius + 2
filter_size = filter_radius * 2 + 1
img_filter = np.zeros((filter_size, filter_size))

cv2.circle(img_filter, (filter_radius, filter_radius), int(radius / 2), -1, -1)
cv2.circle(img_filter, (filter_radius, filter_radius), radius, 1, 3)
# second circle better to generate with smaller width like this:
# cv2.circle(img_filter, (filter_radius, filter_radius), radius, 1, 2)
cv2.imwrite("Filter.png", img_filter)

filter_result = cv2.filter2D(edge_img, cv2.CV_32F, img_filter)
cv2.imwrite("FilterResult.png", filter_result)

min_val, max_val, _, _ = cv2.minMaxLoc(filter_result)
scale = 255 / (max_val - min_val)
show = np.uint8(filter_result * scale - min_val * scale)

cv2.imwrite("Filter2D_Result.png", show)

_, centers = cv2.threshold(filter_result, (max_val + min_val) * 0.6, 255, cv2.THRESH_BINARY)
centers = np.uint8(centers)

show = gray * 0.5
show[np.where(centers == 255)] = 255
cv2.imwrite("Centers.png", show)

Upvotes: 1

fana
fana

Reputation: 1870

I tried a little.
My idea is simply using filter2D instead of Hough-Transform.
Because detection target is the circles has specific radius, if edge of circles detected clearly, the center of the circles will be able to found by convoluting circular mask to the edge image.

I checked the filter2D(=convolution) result with following code (C++).

int main()
{
    //This source image "MicroSpheres.png" was copied from this question
    cv::Mat Src = cv::imread( "MicroSpheres.png", cv::IMREAD_GRAYSCALE );
    if( Src.empty() )return 0;
    //Test with 50% Scale
    cv::resize( Src, Src, cv::Size(0,0), 0.5, 0.5, cv::INTER_AREA );
    cv::imshow( "Src", Src );
    const int Radius = cvRound(57 * 0.5);   //So, Radius is also 50% scale

    //Trying to detect edge of circles
    cv::Mat EdgeImg;
    {
        cv::Mat Test;
        cv::medianBlur( Src, Test, 5 );
        cv::morphologyEx( Test, Test, cv::MORPH_GRADIENT, cv::Mat() );
        cv::imshow( "Test", Test );

        cv::adaptiveThreshold( Test, EdgeImg, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, (Test.rows/6)|0x01, -6 );
        cv::imshow( "EdgeImg", EdgeImg );
    }

    cv::Mat BufferFor_imwrite = EdgeImg.clone();

    //filter2D
    cv::Mat FilterResult;
    {
        const int FilterRadius = Radius + 2;
        const int FilterSize = FilterRadius*2 + 1;
        cv::Mat Filter = cv::Mat::zeros( FilterSize,FilterSize, CV_32F );
        cv::circle( Filter, cv::Point(FilterRadius,FilterRadius), Radius/2, cv::Scalar(-1), -1 );
        cv::circle( Filter, cv::Point(FilterRadius,FilterRadius), Radius, cv::Scalar(1), 3 );
    
        cv::filter2D( EdgeImg, FilterResult, CV_32F, Filter );
    }

    {//Very lazy check of the filter2D result.
        double Min, Max;
        cv::minMaxLoc( FilterResult, &Min, &Max );
        double scale = 255 / (Max-Min);
        cv::Mat Show;
        FilterResult.convertTo( Show, CV_8U, scale, -Min*scale );
        cv::imshow( "Filter2D_Result", Show );

        cv::vconcat( BufferFor_imwrite, Show, BufferFor_imwrite );

        //(Estimating center of circles based onthe  filter2D result.)
        //  Here, just only simple thresholding is implemented.
        //  At least non-maximum suppression must be done, I think.
        cv::Mat Centers;
        cv::threshold( FilterResult, Centers, (Max+Min)*0.6, 255, cv::THRESH_BINARY );
        Centers.convertTo( Centers, CV_8U );
        Show = Src * 0.5;
        Show.setTo( cv::Scalar(255), Centers );
        cv::imshow( "Centers", Show );

        cv::vconcat( BufferFor_imwrite, Show, BufferFor_imwrite );
    }
    if( cv::waitKey() == 's' ){ cv::imwrite( "Result.png", BufferFor_imwrite ); }
    return 0;
}

The following image is result. 3 images are concatenated vertically.

  • edge detection result
  • filter2D result
  • Circle center estimation result (very lazy. just binarized the filter2D result and overlapped it onto source image.)

I can't say this is perfect, but it looks like that the result roughly indicates some centers.

enter image description here

Upvotes: 3

Related Questions