如何使用OpenCV Python显示图像的轮廓?

时间:2015-02-23 15:44:18

标签: python opencv image-processing computer-vision

我从官方documentation开始学习本教程。我运行他们的代码:

import numpy as np
import cv2

im = cv2.imread('test.jpg')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, -1, (0,255,0), 3)

没关系:没有错误,但没有显示任何内容。我想显示他们在图片上显示的结果:

enter image description here

如何显示像这样的计数器的结果(只是左边的结果或右边的结果)? 我知道我必须使用cv2.imshow(something),但在这种具体情况下该怎么做?

3 个答案:

答案 0 :(得分:13)

首先,该示例仅向您展示如何使用简单近似绘制轮廓。请记住,即使您使用简单近似绘制轮廓,也会将其视为具有完全围绕矩形绘制的蓝色轮廓,如左图所示。只需将轮廓绘制到图像上,您将无法获得正确的图像。此外,您想要比较两组轮廓 - 右侧的简化版本及左侧的完整表示。具体来说,您需要将cv2.CHAIN_APPROX_SIMPLE标记替换为cv2.CHAIN_APPROX_NONE以获得完整的表示形式。有关详细信息,请查看findContours上的OpenCV文档:http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#findcontours

此外,即使您在图像上绘制轮廓,它也不会显示结果。您需要为此致电cv2.imshow。但是,绘制轮廓本身并不会显示完整版和简化版之间的区别。本教程提到您需要在每个轮廓点绘制圆,因此我们不应该使用cv2.drawContours来执行此任务。你应该做的是提取轮廓点并在每个点画圆圈。

因此,创建两个图像:

# Your code
import numpy as np
import cv2

im = cv2.imread('test.jpg')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)

## Step #1 - Detect contours using both methods on the same image
contours1, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
contours2, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

### Step #2 - Reshape to 2D matrices
contours1 = contours1[0].reshape(-1,2)
contours2 = contours2[0].reshape(-1,2)

### Step #3 - Draw the points as individual circles in the image
img1 = im.copy()
img2 = im.copy()

for (x, y) in contours1:
    cv2.circle(img1, (x, y), 1, (255, 0, 0), 3)

for (x, y) in contours2:
    cv2.circle(img2, (x, y), 1, (255, 0, 0), 3)

请注意,上面的代码适用于OpenCV 2.对于OpenCV 3,还有一个额外的输出cv2.findContours,这是第一个在这种情况下可以忽略的输出:

_, contours1, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
_, contours2, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

现在让我们慢慢浏览代码。代码的第一部分就是您提供的内容。现在我们进入新的目标。

步骤#1 - 使用两种方法检测轮廓

使用阈值图像,我们使用完整和简单近似检测轮廓。这会存储在两个列表中,contours1contours2

步骤#2 - 重塑为2D矩阵

轮廓本身存储为NumPy数组列表。对于提供的简单图像,应该只检测到一个轮廓,因此提取出列表的第一个元素,然后使用numpy.reshape将3D矩阵重新整形为2D形式,其中每行为(x, y)点。

步骤#3 - 将点绘制为图像中的各个圆

下一步是从每组轮廓中取出每个(x, y)点并在图像上绘制它们。我们以彩色形式制作原始图像的两个副本,然后我们使用cv2.circle并遍历每对(x, y)点对两组轮廓进行迭代,并填充两个不同的图像 - 每组轮廓一个。


现在,要获得上面看到的数字,有两种方法可以做到这一点:

  1. 创建一个将这两个结果并排存储的图像,然后显示此合并图像。
  2. 使用matplotlib,结合subplotimshow,以便在一个窗口中显示两个图像。
  3. 我将向您展示如何使用这两种方法:

    方法#1

    只需将两张图像并排堆叠,然后在以下后显示图像:

    out = np.hstack([img1, img2])
    
    # Now show the image
    cv2.imshow('Output', out)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    我将它们水平堆叠以便它们是组合图像,然后用cv2.imshow显示。

    方法#2

    您可以使用matplotlib

    import matplotlib.pyplot as plt
    
    # Spawn a new figure
    plt.figure()
    # Show the first image on the left column
    plt.subplot(1,2,1)
    plt.imshow(img1[:,:,::-1])
    # Turn off axis numbering
    plt.axis('off')
    
    # Show the second image on the right column
    plt.subplot(1,2,2)
    plt.imshow(img2[:,:,::-1])
    # Turn off the axis numbering
    plt.axis('off')
    
    # Show the figure
    plt.show()
    

    这应该在整个图形窗口中的不同子图中显示两个图像。如果你看看我在这里如何调用imshow,你会发现我正在交换RGB通道,因为OpenCV以BGR格式读取图像。如果要使用matplotlib显示图像,则需要反转通道,因为图像是RGB格式(应该是这样)。


    要在评论中解决您的问题,您可以采用所需的轮廓结构(contours1contours2)并搜索轮廓点。 contours是所有可能轮廓的列表,并且在每个轮廓内是以N x 1 x 2格式整形的3D矩阵。 N将是表示轮廓的点的总数。我将删除单例第二维,以便我们可以将其作为N x 2矩阵。此外,让我们暂时使用轮廓的完整表示:

    points = contours1[0].reshape(-1,2)
    

    我将假设您的图像只有一个对象,因此我索引到contours1索引为0.我解开矩阵以使其成为单行向量,然后重塑矩阵,使其变为N x 2。接下来,我们可以通过以下方式找到最小点:

    min_x = np.argmin(points[:,0])
    min_point = points[min_x,:]
    

    np.argmin找到您提供的数组中最小值的位置。在这种情况下,我们希望沿x坐标或列进行操作。一旦找到这个位置,我们只需索引到我们的2D轮廓点阵列并提取出轮廓点。

答案 1 :(得分:3)

最后添加这两行:

cv2.imshow("title", im)
cv2.waitKey()

此外,请注意,您的最后一行中有img而不是im

答案 2 :(得分:2)

您应该在代码末尾添加cv2.imshow(“Title”,img)。它应该是这样的:

import numpy as np
import cv2

im = cv2.imread('test.jpg')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(im, contours, -1, (0,255,0), 3)
cv2.imshow("title", im)
cv2.waitKey()