Bru*_*ito 3 python opencv contour omr
我试图找到该图像的轮廓,但方法findContours只返回1 个轮廓,该轮廓在图像 2中突出显示。我试图找到所有外部轮廓,例如这些数字位于内部的圆圈。我究竟做错了什么?我可以做什么来实现它?
图1:
图2:
以下是我的代码的相关部分。
thresh = cv2.threshold(image, 0, 255,
cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
Run Code Online (Sandbox Code Playgroud)
当我更改cv2.RETR_EXTERNAL为cv2.RETR_LIST它时,它似乎检测到相同的轮廓两次或类似的东西。图 3 显示了首次检测到圆的边界,然后再次检测到圆的边界,如图 4 所示。我试图仅找到这些圆的外边界。我怎样才能做到这一点?
问题是cv2.RETR_EXTERNAL您在函数调用中使用的标志。正如OpenCV 文档中所述,这仅返回外部轮廓。
使用该标志,cv2.RETR_LIST您可以获得图像中的所有轮廓。由于您尝试检测环,因此该列表将包含这些环的内部和外部轮廓。
要过滤圆的外边界,您可以使用cv2.contourArea()查找两个重叠轮廓中较大的一个。
我不确定这是否真的是您所期望的,但是在这种情况下,有很多方法可以帮助 findContours 完成其工作。这是我经常使用的一个方法。
将图像转换为灰色
Ig = cv2.cvtColor(I,cv2.COLOR_BGR2GRAY)
背景和前景值在颜色方面看起来相当均匀,但局部情况并非如此,因此我应用基于大津方法的阈值来对强度进行二值化。
_,It = cv2.threshold(Ig,0,255,cv2.THRESH_OTSU)
Run Code Online (Sandbox Code Playgroud)
为了仅提取轮廓,我处理 Sobel 边缘检测器的幅度。
sx = cv2.Sobel(It,cv2.CV_32F,1,0)
sy = cv2.Sobel(It,cv2.CV_32F,0,1)
m = cv2.magnitude(sx,sy)
m = cv2.normalize(m,None,0.,255.,cv2.NORM_MINMAX,cv2.CV_8U)
Run Code Online (Sandbox Code Playgroud)
我使用在 中实现的细化函数ximgproc。
细化的目的是将轮廓厚度减少到尽可能少的像素。
m = cv2.ximgproc.thinning(m,None,cv2.ximgproc.THINNING_GUOHALL)
Run Code Online (Sandbox Code Playgroud)
最后一步找到轮廓
_,contours,hierarchy = cv2.findContours(m,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
disp = cv2.merge((m,m,m)
disp = cv2.drawContours(disp,contours,-1,hierarchy=hierarchy,color=(255,0,0))
Run Code Online (Sandbox Code Playgroud)希望有帮助。
我认为基于 SVM 或 CNN 的方法可能更稳健。您可以在此处找到示例。 这个可能也很有趣。
-编辑-
我找到了一种更简单的方法来实现你的目标。
就像之前一样,加载图像后应用阈值确保图像是二进制的。通过使用按位非运算反转图像,轮廓在黑色背景上变成白色。应用cv2.connectedComponentsWithStats返回(除其他外)一个标签矩阵,其中源中每个连接的白色区域都被分配了一个唯一的标签。然后findContours根据标签应用,可以给出每个区域的外部轮廓。
import numpy as np
import cv2
from matplotlib import pyplot as plt
I = cv2.imread('/home/smile/Downloads/ext_contours.png',cv2.IMREAD_GRAYSCALE)
_,I = cv2.threshold(I,0.,255.,cv2.THRESH_OTSU)
I = cv2.bitwise_not(I)
_,labels,stats,centroid = cv2.connectedComponentsWithStats(I)
result = np.zeros((I.shape[0],I.shape[1],3),np.uint8)
for i in range(0,labels.max()+1):
mask = cv2.compare(labels,i,cv2.CMP_EQ)
_,ctrs,_ = cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
result = cv2.drawContours(result,ctrs,-1,(0xFF,0,0))
plt.figure()
plt.imshow(result)
Run Code Online (Sandbox Code Playgroud)
PS 在函数返回的输出中findContours有一个层次矩阵。通过分析该矩阵可以达到相同的结果,但它稍微复杂一些,如下所述。