检测图像中的圆/椭圆
detect circle / ellipse in image
我有以下图片:
没有五线谱的图像:
我想检测图像中的圆形/椭圆形状并获取检测到的形状数组。
我想检测大圆圈和小圆圈。
我尝试了以下方法,但没有检测到任何东西:
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
我有以下图片:
没有五线谱的图像:
我想检测图像中的圆形/椭圆形状并获取检测到的形状数组。
我想检测大圆圈和小圆圈。
我尝试了以下方法,但没有检测到任何东西:
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