检测图像中的圆/椭圆

detect circle / ellipse in image

我有以下图片:

https://imgur.com/a/V196dBG

没有五线谱的图像:

https://imgur.com/a/B8VbQtc

我想检测图像中的圆形/椭圆形状并获取检测到的形状数组。

我想检测大圆圈和小圆圈。

我尝试了以下方法,但没有检测到任何东西:

img = cv2.imread("1.png", 0)
circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1.2, 1, param1=50, param2=30, minRadius=0, maxRadius=0)

但圈子 returns None。 谢谢。

感谢 fmw42,但他的解决方案并未消除下图中的所有噪点:

当前解决方案不适用于:

填充椭圆的解法:

def fill_ellipse(image):
    copy = np.copy(image)
    if len(copy.shape) > 2:
        copy = cv2.cvtColor(copy, cv2.COLOR_BGR2GRAY)
    des = cv2.bitwise_not(copy)
    _, contour, hier = cv2.findContours(des, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

    for cnt in contour:
        cv2.drawContours(des, [cnt], 0, 255, -1)
    res = cv2.bitwise_not(des)
    return res

现在我在填充图像上使用@fmw42 给出的解决方案(只有当我已经知道图像中有一个空心椭圆时才会失败)

这是一种使用 Python/OpenCV 的方法。基本上,删除水平和垂直线。然后在区域上获取和过滤轮廓。

输入:

import cv2
import numpy as np

# read image
img = cv2.imread('notes.png')

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

# threshold
thresh = cv2.threshold(gray,128,255,cv2.THRESH_BINARY)[1]

# do morphology remove horizontal lines
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,5))
lines1 = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, kernel, iterations = 1)

# do morphology to remove vertical lines
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,1))
lines2 = cv2.morphologyEx(lines1, cv2.MORPH_CLOSE, kernel, iterations = 1)
lines2 = cv2.threshold(lines2,128,255,cv2.THRESH_BINARY)[1]

# invert lines2
lines2 = 255 - lines2

# get contours
cntrs = cv2.findContours(lines2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]

# filter contours on area and draw good ones as black filled on white background
result = np.full_like(img, (255,255,255))
for cntr in cntrs:
    area = cv2.contourArea(cntr)
    if area > 9 and area < 400:
        # get centroid
        M = cv2.moments(cntr)
        cx = M["m10"] / M["m00"]
        cy = M["m01"] / M["m00"]
        pt="(" + str(cx) + "," + str(cy) + ")"
        print("area:",area,"center:",pt)
        cv2.drawContours(result, [cntr], 0, (0,0,0), -1)

# write result to disk
cv2.imwrite("notes_lines_removed.png", result)

# display it
cv2.imshow("lines1", lines1)
cv2.imshow("lines2", lines2)
cv2.imshow("result", result)
cv2.waitKey(0)

结果:

area: 360.0 center: (12.710648148148147,19.85972222222222)
area: 309.0 center: (64.38619201725997,10.55393743257821)

Python/OpenCV 有一个更好的变体。除了按面积过滤外,我们还可以过滤轮廓与拟合椭圆的匹配程度。

输入 1:

输入 2:

import cv2
import numpy as np

# read image
img = cv2.imread('notes.png')
#img = cv2.imread('notes2.png')

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

# threshold
thresh = cv2.threshold(gray,128,255,cv2.THRESH_BINARY)[1]

# do morphology remove horizontal lines
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,5))
lines1 = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, kernel, iterations = 1)

# do morphology to remove vertical lines
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,1))
lines2 = cv2.morphologyEx(lines1, cv2.MORPH_CLOSE, kernel, iterations = 1)
lines2 = cv2.threshold(lines2,128,255,cv2.THRESH_BINARY)[1]

# invert lines2
lines2 = 255 - lines2

# get contours
cntrs = cv2.findContours(lines2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]

# filter contours on area and draw good ones as black filled on white background
result = np.full_like(img, (255,255,255))
for cntr in cntrs:
    area = cv2.contourArea(cntr)
    if area > 9 and area < 400:
        # get centroid
        M = cv2.moments(cntr)
        cx = M["m10"] / M["m00"]
        cy = M["m01"] / M["m00"]
        pt="(" + str(cx) + "," + str(cy) + ")"
        # fit ellipse
        ellipse = cv2.fitEllipse(cntr)
        (x, y), (minor_axis, major_axis), angle = ellipse
        poly = cv2.ellipse2Poly((int(x), int(y)), (int(major_axis / 2), int(minor_axis / 2)), int(angle), 0, 360, 1)
        similarity = cv2.matchShapes(poly.reshape((poly.shape[0], 1, poly.shape[1])), cntr, cv2.CONTOURS_MATCH_I1, 0)
        if similarity < 0.2:
            print("area:",area, "center:",pt, "major_axis:",major_axis, "minor_axis:",minor_axis, "similarity:",similarity)
            cv2.drawContours(result, [cntr], 0, (0,0,0), -1)

# write result to disk
cv2.imwrite("notes_lines_removed.png", result)
#cv2.imwrite("notes2_lines_removed.png", result)

# display it
cv2.imshow("lines1", lines1)
cv2.imshow("lines2", lines2)
cv2.imshow("result", result)
cv2.waitKey(0)

结果 1:

area: 360.0 center: (12.710648148148147,19.85972222222222) major_axis: 30.517166137695312 minor_axis: 18.602985382080078 similarity: 0.0965383677685383
area: 309.0 center: (64.38619201725997,10.55393743257821) major_axis: 24.558181762695312 minor_axis: 16.582275390625 similarity: 0.026244617612192156

结果 2:

area: 317.0 center: (12.773922187171397,10.570452155625656) major_axis: 25.237092971801758 minor_axis: 16.553726196289062 similarity: 0.008516408232928263