我不确定这是否真的是你所期望的,但如果这样的话,有很多方法可以帮助 findContours 完成它的工作。
这是我经常使用的一种方式。
-
将您的图像转换为灰色
Ig = cv2.cvtColor(I,cv2.COLOR_BGR2GRAY)
- 阈值
背景和前景值在颜色方面看起来很均匀,但在局部它们不是,所以我应用了基于 Otsu 方法的阈值以对强度进行二值化。
_,It = cv2.threshold(Ig,0,255,cv2.THRESH_OTSU)
- 索贝尔震级
为了只提取轮廓,我处理了 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)
- 细化(可选)
我使用ximgproc中实现的细化功能。
细化的目的是将轮廓厚度减少到尽可能少的像素。
m = cv2.ximgproc.thinning(m,None,cv2.ximgproc.THINNING_GUOHALL)
-
最后一步findContours
_,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))
希望对您有所帮助。
我认为基于 SVM 或 CNN 的方法可能更稳健。
你可以找到一个例子here。
This one 也可能很有趣。
-编辑-
我找到了一种更简单的方法来实现您的目标。
像以前一样,在加载图像后应用阈值确保图像是二进制的。
通过使用按位非运算反转图像,轮廓在黑色背景上变为白色。
应用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)
附:在函数findContours 返回的输出中,有一个层次矩阵。
通过分析该矩阵可以达到相同的结果,但它有点复杂,如 here 的解释。