Opencv未找到所有轮廓

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_EXTERNALcv2.RETR_LIST它时,它似乎检测到相同的轮廓两次或类似的东西。图 3 显示了首次检测到圆的边界,然后再次检测到圆的边界,如图 4 所示。我试图仅找到这些圆的外边界。我怎样才能做到这一点?

在此输入图像描述图3

在此输入图像描述图4

ret*_*nda 5

问题是cv2.RETR_EXTERNAL您在函数调用中使用的标志。正如OpenCV 文档中所述,这仅返回外部轮廓。

使用该标志,cv2.RETR_LIST您可以获得图像中的所有轮廓。由于您尝试检测环,因此该列表将包含这些环的内部和外部轮廓。

要过滤圆的外边界,您可以使用cv2.contourArea()查找两个重叠轮廓中较大的一个。


Joh*_*318 5

我不确定这是否真的是您所期望的,但是在这种情况下,有很多方法可以帮助 findContours 完成其工作。这是我经常使用的一个方法。

  1. 将图像转换为灰色

    Ig = cv2.cvtColor(I,cv2.COLOR_BGR2GRAY)

灰度图像

  1. 阈值化

背景和前景值在颜色方面看起来相当均匀,但局部情况并非如此,因此我应用基于大津方法的阈值来对强度进行二值化。

 _,It = cv2.threshold(Ig,0,255,cv2.THRESH_OTSU)
Run Code Online (Sandbox Code Playgroud)

图像脱粒

  1. 索贝尔星等

为了仅提取轮廓,我处理 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)

索贝尔

  1. 细化(可选)

我使用在 中实现的细化函数ximgproc

细化的目的是将轮廓厚度减少到尽可能少的像素。

 m = cv2.ximgproc.thinning(m,None,cv2.ximgproc.THINNING_GUOHALL)
Run Code Online (Sandbox Code Playgroud)

细化图像

  1. 最后一步找到轮廓

    _,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)

ctrs

希望有帮助。

我认为基于 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有一个层次矩阵。通过分析该矩阵可以达到相同的结果,但它稍微复杂一些,如下所述