简体   繁体   中英

Drawing a circle around a coin using OpenCV

I am trying to measure areas and perimeters of objects in snapshots and the most conservative way to do it seemed to be to use a reference object. One that is easily available to everyone is a coin as shown in this image. 硬币 I have tried to use Hough Circles and contours in OpenCV but I can't seem to get a circle around the coin. I wonder if there is something basic I am missing. Below are the images, and the code for both methods. In case it matters in analyzing the problem, the original image's quality had to be lowered for me to be able to upload it here. Thanks!

# Function definitions
def show_image(window_name, image):
    cv.namedWindow(window_name)
    cv.imshow(window_name, image)
    cv.waitKey(0)
    cv.destroyAllWindows()

def scale_to(image, fraction_of_original_size):
    width = int(image.shape[1] * fraction_of_original_size)
    height = int(image.shape[0] * fraction_of_original_size)
    dim = (width, height)
    resized = cv.resize(image, dim, interpolation = cv.INTER_AREA)
    return resized

def edged_image(image):
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    gray = cv.GaussianBlur(gray, (5, 5), 0)
    gray = cv.dilate(gray, None, iterations=1)
    gray = cv.erode(gray, None, iterations=1)
    edges = cv.Canny(image=gray, threshold1=100, threshold2=200) 
    return edges

# Hough Circles method. Here we try to draw Hough circles.
scaled_image = scale_to(images[0], 0.25)
edged = edged_image(scaled_image)
circles = cv.HoughCircles(edged, cv.HOUGH_GRADIENT, 10, 40,
                           param1=120,
                           param2=60,
                           minRadius=0,
                           maxRadius=0)
circles = np.round(circles[0, :]).astype("int")
for (x, y, r) in circles:
    cv.circle(scaled_image, (x, y), r, (0, 255, 0), 2)
show_image("test", scaled_image)

# Contours method. Here we draw contours, fit with an ellipse and choose the ellipse with near-zero eccentricity.

scaled_image = scale_to(images[0], 0.25)
edged = edged_image(scaled_image)
contours_all, ret = cv.findContours(edged, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# drawn_image = cv.drawContours(scaled_image, contours_all, -1, (0, 0, 255), 4)
for cntr in contours_all:
    if len(cntr) > 4:
        ellipse = cv.fitEllipse(cntr)
        (center,axes,orientation) = ellipse
        majoraxis_length = max(axes)
        minoraxis_length = min(axes)
        eccentricity=(np.sqrt(1-(minoraxis_length/majoraxis_length)**2))
        if eccentricity < 0.08:
            cv.ellipse(scaled_image,ellipse,(0,255,0),2)
        print(eccentricity)
show_image("test", scaled_image)

But the Hough Circles method produced this image with lots of circles but none around the coin. 这个 The contour method produces this image, with a tiny dot. 轮廓结果

Here is one way in Python/OpenCV. Note that all the parameters need to be tuned carefully for this image. Also note that the rim of the coin is not detected as it is too close in brightness to the skin and the circle between the rim and center of the coin is stronger.

  • Read the input
  • Convert to grayscale
  • Apply Gaussian Blur
  • Get Canny edges
  • Get Hough Circles
  • Draw circle on input
  • Save result

Input:

在此处输入图像描述

import cv2
import numpy as np

# Read image
img = cv2.imread('coin.jpg')
hh, ww = img.shape[:2]

# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Gaussian filter
gray = cv2.GaussianBlur(gray, ksize=(7,7), sigmaX=0, sigmaY=0)

# get canny edges
canny = cv2.Canny(gray, 20, 200)

# get Hough circles
min_dist = int(ww/20)
circles = cv2.HoughCircles(canny, cv2.HOUGH_GRADIENT, 1, minDist=min_dist, param1=200, param2=50, minRadius=0, maxRadius=0)
print(circles)

# draw circles
result = img.copy()
for circle in circles[0]:
    # draw the circle in the output image
    (x,y,r) = circle
    x = int(x)
    y = int(y)
    r = int(r)
    cv2.circle(result, (x, y), r, (0, 0, 255), 1)

# save results
cv2.imwrite('coin_canny.jpg', canny)
cv2.imwrite('coin_circle2.jpg', result)

# show images
cv2.imshow('gray', gray)
cv2.imshow('canny', canny)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Canny edge image:

在此处输入图像描述

Resulting Circle:

在此处输入图像描述

X,Y,Radius = [[[396.5 390.5 80.2]]]

ADDITION:

Here is the code for the reduces size image. I used cv2.resize() since I do not know scale_to(). I had to change the Canny arguments and then the HoughCircle arguments.

import cv2
import numpy as np

# Read image
img = cv2.imread('coin.jpg')
hh, ww = img.shape[:2]

# resize image to 1/4
img = cv2.resize(img, dsize=(0,0), fx=0.25, fy=0.25)

# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Gaussian filter
gray = cv2.GaussianBlur(gray, ksize=(3,3), sigmaX=0, sigmaY=0)

# get canny edges
canny = cv2.Canny(gray, 20, 140)

# get Hough circles
min_dist = int(ww/20)
circles = cv2.HoughCircles(canny, cv2.HOUGH_GRADIENT, 1, minDist=min_dist, param1=200, param2=40, minRadius=0, maxRadius=0)
print(circles)

# draw circles
result = img.copy()
for circle in circles[0]:
    # draw the circle in the output image
    (x,y,r) = circle
    x = int(x)
    y = int(y)
    r = int(r)
    cv2.circle(result, (x, y), r, (0, 0, 255), 1)

# save results
cv2.imwrite('coin_canny.jpg', canny)
cv2.imwrite('coin_circle2.jpg', result)

# show images
cv2.imshow('gray', gray)
cv2.imshow('canny', canny)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Result:

在此处输入图像描述

ADDITION2:

Removing the canny step and adding increased contrast, does slightly better.

import cv2
import numpy as np
import skimage.exposure

# Read image
img = cv2.imread('coin.jpg')
hh, ww = img.shape[:2]

# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Gaussian filter
gray = cv2.GaussianBlur(gray, ksize=(7,7), sigmaX=0, sigmaY=0)

# increase contrast
contrast = skimage.exposure.rescale_intensity(gray, in_range=(100,200), out_range=(0,255)).clip(0,255).astype(np.uint8)

# get Hough circles
min_dist = int(ww/20)
circles = cv2.HoughCircles(contrast, cv2.HOUGH_GRADIENT, 1, minDist=min_dist, param1=400, param2=50, minRadius=0, maxRadius=0)
print(circles)

# draw circles
result = img.copy()
for circle in circles[0]:
    # draw the circle in the output image
    (x,y,r) = circle
    x = int(x)
    y = int(y)
    r = int(r)
    cv2.circle(result, (x, y), r, (0, 0, 255), 1)

# save results
cv2.imwrite('coin_contrast5.jpg', contrast)
cv2.imwrite('coin_circle5.jpg', result)

# show images
cv2.imshow('gray', gray)
cv2.imshow('contrast', contrast)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Increased contrast image:

在此处输入图像描述

Result:

在此处输入图像描述

Another way that seems to work well is to use morphology open to darken the inside of the watch circle with a 15x15 size elliptical kernel.

Result:

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM