Opencv找不到所有轮廓

时间:2018-08-08 14:49:37

标签: python opencv opencv-contour optical-mark-recognition

我正在尝试查找此图像的轮廓,但是方法 findContours 仅返回 1 轮廓,轮廓在图像2中突出显示。我正在尝试找到所有外部轮廓,例如这些数字在其中的圆圈。我究竟做错了什么?我该怎么做?

enter image description here 图片1

enter image description here 图片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)

当我将cv2.RETR_EXTERNAL更改为cv2.RETR_LIST时,似乎两次检测到相同的轮廓或类似的东西。图像3显示了何时首先检测到圆的边界,然后如图4所示再次被检测到。我试图仅查找这些圆的外边界。我该怎么办?

enter image description here图片3

enter image description here图片4

4 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:2)

我建议不要使用轮廓线,而是使用适当的参数来应用霍夫圆变换。

寻找轮廓带来了挑战。反转二进制图像后,圆圈为白色。 OpenCV沿圆的外部查找轮廓。此外,由于存在诸如“ A”和“ B”之类的字母,沿字母的外侧和孔内将再次发现轮廓。您可以使用适当的层次结构准则来找到轮廓,但这仍然很繁琐。

这是我尝试通过找到轮廓并使用层次结构进行的尝试:

代码:

#--- read the image, convert to gray and obtain inverse binary image ---
img = cv2.imread('keypad.png', 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)

#--- find contours ---
_, contours, hierarchy = cv2.findContours(binary, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)

#--- copy of original image ---
img2 = img.copy()

#--- select contours having a parent contour and append them to a list ---
l = []
for h in hierarchy[0]:
    if h[0] > -1 and h[2] > -1:
        l.append(h[2])

#--- draw those contours ---
for cnt in l:
    if cnt > 0:
        cv2.drawContours(img2, [contours[cnt]], 0, (0,255,0), 2)

cv2.imshow('img2', img2)

enter image description here

有关轮廓及其层次关系please refer this

的更多信息

更新

我有一种相当粗略的方法来忽略不需要的轮廓。在列表l中找到所有轮廓的平均面积,并绘制出高于平均值的轮廓:

代码:

img3 = img.copy()
a = 0
for j, i in enumerate(l):
    a = a + cv2.contourArea(contours[i])
mean_area = int(a/len(l))

for cnt in l:
    if (cnt > 0) & (cv2.contourArea(contours[cnt]) > mean_area):
        cv2.drawContours(img3, [contours[cnt]], 0, (0,255,0), 2)

cv2.imshow('img3', img3)

enter image description here

答案 2 :(得分:2)

尽管如此,我不确定这是否真的是您期望的,有很多方法可以帮助findContours完成工作。 这是我经常使用的方法。

  1. 将图像转换为灰色

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

gray image

  1. 阈值

背景和前景值在颜色方面看起来很均匀,但是局部上却不一样,因此我基于Otsu方法对阈值进行了二值化处理。

 _,It = cv2.threshold(Ig,0,255,cv2.THRESH_OTSU)

image thresh

  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)

sobel

  1. 稀疏(可选)

我使用在ximgproc中实现的细化功能。

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

 m = cv2.ximgproc.thinning(m,None,cv2.ximgproc.THINNING_GUOHALL)

thinned_image

  1. 最终步骤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))    
    

ctrs

希望有帮助。

我认为基于SVM或CNN的方法可能更健壮。 您可以找到示例hereThis one也可能很有趣。

-EDIT-

我找到了一种可以说更容易实现目标的方法。

与之前在应用阈值加载图像之后,请确保图像是二进制的。 通过使用按位非操作反转图像,轮廓在黑色背景上变为白色。 应用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)  

external_boundaries

P.S。在函数findContours返回的输出中,有一个层次矩阵。 通过分析该矩阵可以达到相同的结果,但是要比解释here复杂一些。

答案 3 :(得分:0)

另一种方法是在找到合适的参数后使用Hough Circle变换来找到圆。

代码:

img = cv2.imread('keypad.png', 0)


img = cv2.medianBlur(img,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)

circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=30, minRadius=10, maxRadius=50)
circles = np.uint16(np.around(circles))

for i in circles[0,:]:

    #--- draw the circle ---
    cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
    centre = (i[0],i[1])

cv2.imshow('detected circles',cimg) 

enter image description here