circular hough transform misses circles

  • Last Update :
  • Techknowledgy :

As I mentioned in my comment, you'll need to run successive iterations of cv2.HoughCircles for different range of radii to ensure that you get all of the circles. With the way the Circular Hough Transform works, specifying a minimum and maximum radius that has quite a large range will be inaccurate and will also be slow. They don't tell you this in the documentation, but for the Circular Hough Transform to work successfully, the following two things need to be valid:

maxRadius < 3 * minRadius
maxRadius - minRadius < 100

Doing this requires very little modification to your code. BTW, I removed the unsharp masking it because I was getting poor results with it. I also changed a couple of parameters in cv2.HoughCircles slightly to get this to work as best as possible given your situation:

import cv2
import cv2.cv as cv
import numpy as np
import math
# Load Image
img = cv2.imread('circles1.png', 0)
cimg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

# Specify different radii
radii = np.arange(0, 310, 10)

# For each pair of radii...
   for idx in range(len(radii) - 1):
   # Get the minimum and maximum radius
# Note you need to add 1 to each minimum
# as the maximum of the previous pair covers this new minimum
minRadius = radii[idx] + 1
maxRadius = radii[idx + 1]

# Hough Transform - Change here
circles = cv2.HoughCircles(img, cv.CV_HOUGH_GRADIENT, 1, 5,
   param1 = 25, param2 = 75, minRadius = minRadius, maxRadius = maxRadius)

# Skip
if no circles are detected - Change here
if circles is None:
   continue

circles = np.uint16(np.around(circles))

# Go over circles, eliminating the ones that are not cocentric enough
height, width = img.shape
center = (width / 2, height / 2)
for i in circles[0,: ]:
   # draw the outer circle
if math.sqrt((center[0] - i[0]) ** 2 + (center[1] - i[1]) ** 2) < 15:
   cv2.circle(cimg, (i[0], i[1]), i[2], (0, 255, 0), 1)
# draw the center of the circle
cv2.circle(cimg, (i[0], i[1]), 2, (0, 0, 255), 3)

cv2.imshow('detected circles', cimg)

cv2.waitKey(0)
cv2.destroyAllWindows()

However, I wouldn't recommend using cv2.HoughCircles here. May I suggest using cv2.findContours instead? This finds all of the contours in the image. In this case, these will be the black circles. However, you need to reverse the image because cv2.findContours assumes non-zero pixels are object pixels, so we can subtract 255 from the image assuming a np.uint8 type:

# Make copy of original image
cimg2 = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

# Find contours
contours, _ = cv2.findContours(255 - img, cv2.RETR_LIST, cv.CV_CHAIN_APPROX_NONE)

# Draw all detected contours on image in green with a thickness of 1 pixel
cv2.drawContours(cimg2, contours, -1, color = (0, 255, 0), thickness = 1)

# Show the image
cv2.imshow('detected circles', cimg2)
cv2.waitKey(0)
cv2.destroyAllWindows()

Suggestion : 2

The circle Hough Transform (CHT) is a basic feature extraction technique used in digital image processing for detecting circles in imperfect images. The circle candidates are produced by “voting” in the Hough parameter space and then selecting local maxima in an accumulator matrix. ,The original picture (right) is first turned into a binary image (left) using a threshold and Gaussian filter. Then edges (mid) are found from it using canny edge detection. After this, all the edge points are used by the Circle Hough Transform to find underlying circle structure. ,Modified Hough Circle Transform (MHCT) is used on the image extracted from Digital Subtraction Angiogram (DSA) to detect and classify aneurysms type. ,Since the head would be similar to a circle in an image, CHT can be used for detecting heads in a picture, so as to count the number of persons in the image.[2]

The Incrementing for Best Candidate :

 For each A[a, b, r] = 0; // fill with zeroes initially, instantiate 3D matrix
 For each cell(x, y)
 For each theta t = 0 to 360 // the possible  theta 0 to 360
 b = y– r * sin(t * PI / 180); //polar coordinate for center (convert to radians)
 a = x– r * cos(t * PI / 180); //polar coordinate for center (convert to radians)
 A[a, b, r] += 1; //voting
 end
 end

Suggestion : 3

by Adrian Rosebrock on July 21, 2014,July 7, 2019 at 1:19 pm,July 8, 2017 at 1:35 am,July 13, 2018 at 5:07 am

Take a look at the function signature below:

cv2.HoughCircles(image, method, dp, minDist)

Great. Let’s jump into some code:

#
import the necessary packages
import numpy as np
import argparse
import cv2

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
args = vars(ap.parse_args())

Let’s go ahead and load the image:

# load the image, clone it
for output, and then convert it to grayscale
image = cv2.imread(args["image"])
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

Fire up a shell, and execute the following command:

$ python detect_circles.py--image images / simple.png

Let’s move on to something else:

$ python detect_circles.py--image images / soda.png

Suggestion : 4

Given a black circle on a white background, we first guess its radius (or a range of radii) to construct a new circle. This circle is applied on each black pixel of the original picture and the coordinates of this circle are voting in an accumulator. From this geometrical construction, the original circle center position receives the highest score.,In this second example, the aim is to detect the edge of a coffee cup. Basically, this is a projection of a circle, i.e. an ellipse. The problem to solve is much more difficult because five parameters have to be determined, instead of three for circles.,Note that the accumulator size is built to be larger than the original picture in order to detect centers outside the frame. Its size is extended by two times the larger radius.,The Hough transform in its simplest form is a method to detect straight lines but it can also be used to detect circles or ellipses. The algorithm assumes that the edge is detected and it is robust against noise or missing points.

import numpy as np
import matplotlib.pyplot as plt

from skimage
import data, color
from skimage.transform
import hough_circle, hough_circle_peaks
from skimage.feature
import canny
from skimage.draw
import circle_perimeter
from skimage.util
import img_as_ubyte

# Load picture and detect edges
image = img_as_ubyte(data.coins()[160: 230, 70: 270])
edges = canny(image, sigma = 3, low_threshold = 10, high_threshold = 50)

# Detect two radii
hough_radii = np.arange(20, 35, 2)
hough_res = hough_circle(edges, hough_radii)

# Select the most prominent 3 circles
accums, cx, cy, radii = hough_circle_peaks(hough_res, hough_radii,
   total_num_peaks = 3)

# Draw them
fig, ax = plt.subplots(ncols = 1, nrows = 1, figsize = (10, 4))
image = color.gray2rgb(image)
for center_y, center_x, radius in zip(cy, cx, radii):
   circy, circx = circle_perimeter(center_y, center_x, radius,
      shape = image.shape)
image[circy, circx] = (220, 20, 20)

ax.imshow(image, cmap = plt.cm.gray)
plt.show()
import matplotlib.pyplot as plt

from skimage
import data, color, img_as_ubyte
from skimage.feature
import canny
from skimage.transform
import hough_ellipse
from skimage.draw
import ellipse_perimeter

# Load picture, convert to grayscale and detect edges
image_rgb = data.coffee()[0: 220, 160: 420]
image_gray = color.rgb2gray(image_rgb)
edges = canny(image_gray, sigma = 2.0,
   low_threshold = 0.55, high_threshold = 0.8)

# Perform a Hough Transform
# The accuracy corresponds to the bin size of a major axis.
# The value is chosen in order to get a single high accumulator.
# The threshold eliminates low accumulators
result = hough_ellipse(edges, accuracy = 20, threshold = 250,
   min_size = 100, max_size = 120)
result.sort(order = 'accumulator')

# Estimated parameters
for the ellipse
best = list(result[-1])
yc, xc, a, b = [int(round(x)) for x in best[1: 5]]
orientation = best[5]

# Draw the ellipse on the original image
cy, cx = ellipse_perimeter(yc, xc, a, b, orientation)
image_rgb[cy, cx] = (0, 0, 255)
# Draw the edge(white) and the resulting ellipse(red)
edges = color.gray2rgb(img_as_ubyte(edges))
edges[cy, cx] = (250, 0, 0)

fig2, (ax1, ax2) = plt.subplots(ncols = 2, nrows = 1, figsize = (8, 4),
   sharex = True, sharey = True)

ax1.set_title('Original picture')
ax1.imshow(image_rgb)

ax2.set_title('Edge (white) and result (red)')
ax2.imshow(edges)

plt.show()

Suggestion : 5

The Hough Circle Transform works in a roughly analogous way to the Hough Line Transform explained in the previous tutorial.,The Hough Circle Transform works in a roughly analogous way to the Hough Line Transform explained in the previous tutorial. ,Applies the Hough Circle Transform to the blurred image .,What does this program do? Loads an image and blur it to reduce the noise Applies the Hough Circle Transform to the blurred image . Display the detected circle in a window.

The sample code that we will explain can be downloaded from here. A slightly fancier version (which shows trackbars for changing the threshold values) can be found here.

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"

#include <iostream>

using namespace cv;
using namespace std;

static void help()
{
    cout << "\nThis program demonstrates circle finding with the Hough transform.\n"
            "Usage:\n"
            "./houghcircles <image_name>, Default is ../data/board.jpg\n" << endl;
}

int main(int argc, char** argv)
{
    cv::CommandLineParser parser(argc, argv,
        "{help h ||}{@image|../data/board.jpg|}"
    );
    if (parser.has("help"))
    {
        help();
        return 0;
    }
    string filename = parser.get<string>("@image");
    Mat img = imread(filename, IMREAD_COLOR);
    if(img.empty())
    {
        help();
        cout << "can not open " << filename << endl;
        return -1;
    }

    Mat gray;
    cvtColor(img, gray, COLOR_BGR2GRAY);

    medianBlur(gray, gray, 5);

    vector<Vec3f> circles;
    HoughCircles(gray, circles, HOUGH_GRADIENT, 1,
                 gray.rows/16, // change this value to detect circles with different distances to each other
                 100, 30, 1, 30 // change the last two parameters
                                // (min_radius & max_radius) to detect larger circles
                 );

    for( size_t i = 0; i < circles.size(); i++ )
    {
        Vec3i c = circles[i];
        circle( img, Point(c[0], c[1]), c[2], Scalar(0,0,255), 3, LINE_AA);
        circle( img, Point(c[0], c[1]), 2, Scalar(0,255,0), 3, LINE_AA);
    }

    imshow("detected circles", img);
    waitKey();

    return 0;
}

Load an image

string filename = parser.get<string>("@image");
Mat img = imread(filename, IMREAD_COLOR);
if(img.empty())
{
    help();
    cout << "can not open " << filename << endl;
    return -1;
}

Convert it to grayscale:

Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);

Apply a Median blur to reduce noise and avoid false circle detection:

medianBlur(gray, gray, 5);

Proceed to apply Hough Circle Transform:

vector<Vec3f> circles;
   HoughCircles(gray, circles, HOUGH_GRADIENT, 1,
   gray.rows/16, // change this value to detect circles with different distances to each other
   100, 30, 1, 30 // change the last two parameters
   // (min_radius & max_radius) to detect larger circles
   );

Draw the detected circles:

for (size_t i = 0; i < circles.size(); i++) {
   Vec3i c = circles[i];
   circle(img, Point(c[0], c[1]), c[2], Scalar(0, 0, 255), 3, LINE_AA);
   circle(img, Point(c[0], c[1]), 2, Scalar(0, 255, 0), 3, LINE_AA);
}