如何使用 Python OpenCV 优化圆形检测?
How to optimize circle detection with Python OpenCV?
我在 python 中查看了几个关于使用 opencv 优化圆检测的页面。所有这些似乎都特定于给定图片的个别情况。 cv2.HoughCircles 的每个参数的起点是什么?由于我不确定推荐值是什么,我尝试在范围内循环,但这并没有产生任何有希望的结果。为什么我检测不到这张图片中的任何圆圈?
import cv2
import numpy as np
image = cv2.imread('IMG_stack.png')
output = image.copy()
height, width = image.shape[:2]
maxWidth = int(width/10)
minWidth = int(width/20)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 20,param1=50,param2=50,minRadius=minWidth,maxRadius=maxWidth)
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circlesRound = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circlesRound:
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.imwrite(filename = 'test.circleDraw.png', img = output)
cv2.imwrite(filename = 'test.circleDrawGray.png', img = gray)
else:
print ('No circles found')
这应该是一个直接的圆圈检测,但检测到的所有圆圈都不是很接近。
通常圆检测可以使用传统的图像处理方法,如阈值+轮廓检测,hough circles, or contour fitting but since your circles are overlapping/touching, watershed segmentation may be better. Here's a good resource。
import cv2
import numpy as np
from skimage.feature import peak_local_max
from skimage.morphology import watershed
from scipy import ndimage
# Load in image, convert to gray scale, and Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Remove small noise by filtering using contour area
cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
if cv2.contourArea(c) < 1000:
cv2.drawContours(thresh,[c], 0, (0,0,0), -1)
cv2.imshow('thresh', thresh)
# Compute Euclidean distance from every binary pixel
# to the nearest zero pixel then find peaks
distance_map = ndimage.distance_transform_edt(thresh)
local_max = peak_local_max(distance_map, indices=False, min_distance=20, labels=thresh)
# Perform connected component analysis then apply Watershed
markers = ndimage.label(local_max, structure=np.ones((3, 3)))[0]
labels = watershed(-distance_map, markers, mask=thresh)
# Iterate through unique labels
for label in np.unique(labels):
if label == 0:
continue
# Create a mask
mask = np.zeros(gray.shape, dtype="uint8")
mask[labels == label] = 255
# Find contours and determine contour area
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
c = max(cnts, key=cv2.contourArea)
cv2.drawContours(image, [c], -1, (36,255,12), -1)
cv2.imshow('image', image)
cv2.waitKey()
您应该注意的主要参数是minDist
、minRadius
和maxRadius
。
首先分析半径:您有一个宽 12 圈、高 8 圈的图像,它给出每个圆的直径大约为 width/12
,或者半径为 (width/12)/2
。您使用的约束允许算法检测比必要的更大或更小的圆,因此您应该使用更适合您的图像的参数化。在这种情况下,我使用了间隔 [0.9 * radius, 1.1 * radius]
.
由于没有重叠,可以说两个圆之间的距离至少是直径,所以 minDist
可以设置为 2*minRadius
.
此实现与您的实现基本相同,只是更新了这 3 个参数:
%matplotlib inline
import cv2
import numpy as np
import matplotlib.pyplot as plt
image = cv2.imread('data/balls.jpg')
output = image.copy()
height, width = image.shape[:2]
maxRadius = int(1.1*(width/12)/2)
minRadius = int(0.9*(width/12)/2)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(image=gray,
method=cv2.HOUGH_GRADIENT,
dp=1.2,
minDist=2*minRadius,
param1=50,
param2=50,
minRadius=minRadius,
maxRadius=maxRadius
)
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circlesRound = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circlesRound:
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
plt.imshow(output)
else:
print ('No circles found')
结果是:
我在 python 中查看了几个关于使用 opencv 优化圆检测的页面。所有这些似乎都特定于给定图片的个别情况。 cv2.HoughCircles 的每个参数的起点是什么?由于我不确定推荐值是什么,我尝试在范围内循环,但这并没有产生任何有希望的结果。为什么我检测不到这张图片中的任何圆圈?
import cv2
import numpy as np
image = cv2.imread('IMG_stack.png')
output = image.copy()
height, width = image.shape[:2]
maxWidth = int(width/10)
minWidth = int(width/20)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 20,param1=50,param2=50,minRadius=minWidth,maxRadius=maxWidth)
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circlesRound = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circlesRound:
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.imwrite(filename = 'test.circleDraw.png', img = output)
cv2.imwrite(filename = 'test.circleDrawGray.png', img = gray)
else:
print ('No circles found')
这应该是一个直接的圆圈检测,但检测到的所有圆圈都不是很接近。
通常圆检测可以使用传统的图像处理方法,如阈值+轮廓检测,hough circles, or contour fitting but since your circles are overlapping/touching, watershed segmentation may be better. Here's a good resource。
import cv2
import numpy as np
from skimage.feature import peak_local_max
from skimage.morphology import watershed
from scipy import ndimage
# Load in image, convert to gray scale, and Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Remove small noise by filtering using contour area
cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
if cv2.contourArea(c) < 1000:
cv2.drawContours(thresh,[c], 0, (0,0,0), -1)
cv2.imshow('thresh', thresh)
# Compute Euclidean distance from every binary pixel
# to the nearest zero pixel then find peaks
distance_map = ndimage.distance_transform_edt(thresh)
local_max = peak_local_max(distance_map, indices=False, min_distance=20, labels=thresh)
# Perform connected component analysis then apply Watershed
markers = ndimage.label(local_max, structure=np.ones((3, 3)))[0]
labels = watershed(-distance_map, markers, mask=thresh)
# Iterate through unique labels
for label in np.unique(labels):
if label == 0:
continue
# Create a mask
mask = np.zeros(gray.shape, dtype="uint8")
mask[labels == label] = 255
# Find contours and determine contour area
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
c = max(cnts, key=cv2.contourArea)
cv2.drawContours(image, [c], -1, (36,255,12), -1)
cv2.imshow('image', image)
cv2.waitKey()
您应该注意的主要参数是minDist
、minRadius
和maxRadius
。
首先分析半径:您有一个宽 12 圈、高 8 圈的图像,它给出每个圆的直径大约为 width/12
,或者半径为 (width/12)/2
。您使用的约束允许算法检测比必要的更大或更小的圆,因此您应该使用更适合您的图像的参数化。在这种情况下,我使用了间隔 [0.9 * radius, 1.1 * radius]
.
由于没有重叠,可以说两个圆之间的距离至少是直径,所以 minDist
可以设置为 2*minRadius
.
此实现与您的实现基本相同,只是更新了这 3 个参数:
%matplotlib inline
import cv2
import numpy as np
import matplotlib.pyplot as plt
image = cv2.imread('data/balls.jpg')
output = image.copy()
height, width = image.shape[:2]
maxRadius = int(1.1*(width/12)/2)
minRadius = int(0.9*(width/12)/2)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(image=gray,
method=cv2.HOUGH_GRADIENT,
dp=1.2,
minDist=2*minRadius,
param1=50,
param2=50,
minRadius=minRadius,
maxRadius=maxRadius
)
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circlesRound = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circlesRound:
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
plt.imshow(output)
else:
print ('No circles found')
结果是: