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

How do I display the contours of an image using OpenCV Python?

我按照官方 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)

没关系:没有错误,但是什么都没有 displayed.I 想要显示他们在图片上显示的结果:

我怎样才能显示这样的计数结果(只是左边的结果或右边的结果)? 我知道我必须使用 cv2.imshow(something) 但在这种特定情况下如何使用?

在末尾添加这两行:

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

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

您应该在代码末尾添加 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()

首先,该示例仅向您展示了如何使用简单的近似值绘制等高线。请记住,即使您使用简单的近似值绘制轮廓,它也会被可视化为完全围绕矩形绘制的蓝色轮廓,如左图所示。您将无法通过简单地将轮廓绘制到图像上来获得正确的图像。此外,您想要比较两组轮廓 - 右侧的简化版本和左侧的完整表示。具体来说,您需要将 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 - 重塑为二维矩阵

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

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

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


现在,要获得上图,有两种方法可以做到:

  1. 创建一个将这两个结果并排存储在一起的图像,然后显示这个组合图像。
  2. 使用matplotlib,结合subplotimshow,这样你就可以将两个图像合二为一window。

我将向您展示如何使用这两种方法:

方法一

只需将两张图片并排堆叠,然后显示之后的图片:

out = np.hstack([img1, img2])

# Now show the image
cv2.imshow('Output', out)
cv2.waitKey(0)
cv2.destroyAllWindows()

我将它们水平堆叠,使它们成为一个组合图像,然后用 cv2.imshow 显示。

方法二

您可以使用 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()

这应该将两个图像显示在一个整体图中的单独子图中 window。如果您看一下我在这里调用 imshow 的方式,您会发现我正在交换 RGB 通道,因为 OpenCV 以 BGR 格式读取图像。如果你想用 matplotlib 显示图像,你需要反转通道,因为图像是 RGB 格式(它们应该是)。


要在您的评论中解决您的问题,您可以采用您想要的轮廓结构(contours1contours2)并搜索轮廓点。 contours 是所有可能轮廓的列表,每个轮廓内都有一个 3D 矩阵,其形状为 N x 1 x 2 格式。 N 将是代表轮廓的点总数。我将删除单例第二维,这样我们就可以把它变成一个 N x 2 矩阵。另外,让我们现在使用轮廓的完整表示:

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

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

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

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