首先,该示例仅向您展示如何使用简单的近似值绘制轮廓。请记住,即使您使用简单的近似值绘制轮廓,它也会被视为在矩形周围完全绘制了一个蓝色轮廓,如左图所示。您将无法通过简单地在图像上绘制轮廓来获得正确的图像。此外,您想比较两组轮廓 - 右侧的简化版本与左侧的完整表示。具体来说,您需要将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 步 - 使用两种方法检测轮廓
使用阈值图像,我们使用完整近似和简单近似来检测轮廓。这存储在两个列表中,contours1 和 contours2。
第 2 步 - 重塑为 2D 矩阵
轮廓本身被存储为 NumPy 数组的列表。对于提供的简单图像,应该只检测到一个轮廓,因此提取列表的第一个元素,然后使用numpy.reshape 将 3D 矩阵重新整形为它们的 2D 形式,其中每行是一个(x, y) 点。
第 3 步 - 在图像中将点绘制为单独的圆圈
下一步是从每组轮廓中提取每个(x, y) 点并将它们绘制在图像上。我们以彩色形式制作原始图像的两份副本,然后使用cv2.circle 并对两组轮廓的每对(x, y) 点进行迭代,并填充两个不同的图像 - 每组轮廓一个。
现在,要获得您在上面看到的数字,有两种方法可以做到这一点:
- 创建一个图像,将这两个结果并排存储在一起,然后显示此组合图像。
- 使用
matplotlib,结合subplot和imshow,这样您就可以在一个窗口中显示两个图像。
我会告诉你如何使用这两种方法:
方法#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 格式(应该是)。
要在您的 cmets 中解决您的问题,您将采用您想要的轮廓结构(contours1 或 contours2)并搜索轮廓点。 contours 是所有可能轮廓的列表,每个轮廓内都有一个以N x 1 x 2 格式成形的 3D 矩阵。 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 轮廓点数组并提取轮廓点。