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

时间:2021-09-24 23:54:58

I followed this tutorial from official documentation. I run their 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)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, -1, (0,255,0), 3)

That is ok: no errors, but nothing is displayed.I want to display the result they got as they showed it on the picture:

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

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

How can I display the result of the countours like that (just the left result or the right one) ? I know I must use cv2.imshow(something) but how in this specific case ?

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

3 个解决方案

#1


12  

First off, that example only shows you how to draw contours with the simple approximation. Bear in mind that even if you draw the contours with the simple approximation, it will be visualized as having a blue contour drawn completely around the rectangle as seen in the left image. You will not be able to get the right image by simply drawing the contours onto the image. In addition, you want to compare two sets of contours - the simplified version on the right with its full representation on the left. Specifically, you need to replace the cv2.CHAIN_APPROX_SIMPLE flag with cv2.CHAIN_APPROX_NONE to get the full representation. Take a look at the OpenCV doc on findContours for more details: http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#findcontours

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

In addition, even though you draw the contours onto the image, it doesn't display the results. You'll need to call cv2.imshow for that. However, drawing the contours themselves will not show you the difference between the full and simplified version. The tutorial mentions that you need to draw circles at each contour point so we shouldn't use cv2.drawContours for this task. What you should do is extract out the contour points and draw circles at each point.

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

As such, create two images like so:

因此,创建两个图像,如下所示:

# 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)

Take note that the above code is for OpenCV 2. For OpenCV 3, there is an additional output to cv2.findContours that is the first output which you can ignore in this case:

请注意,上面的代码适用于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)

Now let's walk through the code slowly. The first part of the code is what you provided. Now we move onto what is new.

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

Step #1 - Detect contours using both methods

Using the thresholded image, we detect contours using both the full and simple approximations. This gets stored in two lists, contours1 and contours2.

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

Step #2 - Reshape to 2D matrices

The contours themselves get stored as a list of NumPy arrays. For the simple image provided, there should only be one contour detected, so extract out the first element of the list, then use numpy.reshape to reshape the 3D matrices into their 2D forms where each row is a (x, y) point.

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

Step #3 - Draw the points as individual circles in the image

The next step would be to take each (x, y) point from each set of contours and draw them on the image. We make two copies of the original image in colour form, then we use cv2.circle and iterate through each pair of (x, y) points for both sets of contours and populate two different images - one for each set of contours.

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


Now, to get the figure you see above, there are two ways you can do this:

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

  1. Create an image that stores both of these results together side by side, then show this combined image.
  2. 创建一个将这两个结果并排存储的图像,然后显示此组合图像。

  3. Use matplotlib, combined with subplot and imshow so that you can display two images in one window.
  4. 使用matplotlib,结合子图和imshow,以便您可以在一个窗口中显示两个图像。

I'll show you how to do it using both methods:

我将告诉你如何使用这两种方法来做到这一点:

Method #1

Simply stack the two images side by side, then show the image after:

只需将两个图像并排堆叠,然后显示图像:

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

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

I stack them horizontally so that they are a combined image, then show this with cv2.imshow.

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

Method #2

You can use matplotlib:

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

This should display both images in separate subfigures within an overall figure window. If you take a look at how I'm calling imshow here, you'll see that I am swapping the RGB channels because OpenCV reads in images in BGR format. If you want to display images with matplotlib, you'll need to reverse the channels as the images are in RGB format (as they should be).

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


To address your question in your comments, you would take which contour structure you want (contours1 or contours2) and search the contour points. contours is a list of all possible contours, and within each contour is a 3D matrix that is shaped in a N x 1 x 2 format. N would be the total number of points that represent the contour. I'm going to remove the singleton second dimension so we can get this to be a N x 2 matrix. Also, let's use the full representation of the contours for now:

要在注释中解决您的问题,您可以采用所需的轮廓结构(轮廓1或轮廓2)并搜索轮廓点。轮廓是所有可能轮廓的列表,并且在每个轮廓内是3D矩阵,其形状为N×1×2格式。 N将是表示轮廓的点的总数。我将删除单个第二维,因此我们可以将其设为N x 2矩阵。另外,让我们现在使用轮廓的完整表示:

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

I am going to assume that your image only has one object, hence my indexing into contours1 with index 0. I unravel the matrix so that it becomes a single row vector, then reshape the matrix so that it becomes N x 2. Next, we can find the minimum point by:

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

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

np.argmin finds the location of the smallest value in an array that you supply. In this case, we want to operate along the x coordinate, or the columns. Once we find this location, we simply index into our 2D contour point array and extract out the contour point.

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

#2


3  

Add these 2 lines at the end:

最后添加这两行:

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

Also, be aware that you have img instead of im in your last line.

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

#3


2  

You should add cv2.imshow("Title", img) at the end of your code. It should look like this:

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

#1


12  

First off, that example only shows you how to draw contours with the simple approximation. Bear in mind that even if you draw the contours with the simple approximation, it will be visualized as having a blue contour drawn completely around the rectangle as seen in the left image. You will not be able to get the right image by simply drawing the contours onto the image. In addition, you want to compare two sets of contours - the simplified version on the right with its full representation on the left. Specifically, you need to replace the cv2.CHAIN_APPROX_SIMPLE flag with cv2.CHAIN_APPROX_NONE to get the full representation. Take a look at the OpenCV doc on findContours for more details: http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#findcontours

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

In addition, even though you draw the contours onto the image, it doesn't display the results. You'll need to call cv2.imshow for that. However, drawing the contours themselves will not show you the difference between the full and simplified version. The tutorial mentions that you need to draw circles at each contour point so we shouldn't use cv2.drawContours for this task. What you should do is extract out the contour points and draw circles at each point.

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

As such, create two images like so:

因此,创建两个图像,如下所示:

# 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)

Take note that the above code is for OpenCV 2. For OpenCV 3, there is an additional output to cv2.findContours that is the first output which you can ignore in this case:

请注意,上面的代码适用于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)

Now let's walk through the code slowly. The first part of the code is what you provided. Now we move onto what is new.

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

Step #1 - Detect contours using both methods

Using the thresholded image, we detect contours using both the full and simple approximations. This gets stored in two lists, contours1 and contours2.

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

Step #2 - Reshape to 2D matrices

The contours themselves get stored as a list of NumPy arrays. For the simple image provided, there should only be one contour detected, so extract out the first element of the list, then use numpy.reshape to reshape the 3D matrices into their 2D forms where each row is a (x, y) point.

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

Step #3 - Draw the points as individual circles in the image

The next step would be to take each (x, y) point from each set of contours and draw them on the image. We make two copies of the original image in colour form, then we use cv2.circle and iterate through each pair of (x, y) points for both sets of contours and populate two different images - one for each set of contours.

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


Now, to get the figure you see above, there are two ways you can do this:

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

  1. Create an image that stores both of these results together side by side, then show this combined image.
  2. 创建一个将这两个结果并排存储的图像,然后显示此组合图像。

  3. Use matplotlib, combined with subplot and imshow so that you can display two images in one window.
  4. 使用matplotlib,结合子图和imshow,以便您可以在一个窗口中显示两个图像。

I'll show you how to do it using both methods:

我将告诉你如何使用这两种方法来做到这一点:

Method #1

Simply stack the two images side by side, then show the image after:

只需将两个图像并排堆叠,然后显示图像:

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

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

I stack them horizontally so that they are a combined image, then show this with cv2.imshow.

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

Method #2

You can use matplotlib:

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

This should display both images in separate subfigures within an overall figure window. If you take a look at how I'm calling imshow here, you'll see that I am swapping the RGB channels because OpenCV reads in images in BGR format. If you want to display images with matplotlib, you'll need to reverse the channels as the images are in RGB format (as they should be).

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


To address your question in your comments, you would take which contour structure you want (contours1 or contours2) and search the contour points. contours is a list of all possible contours, and within each contour is a 3D matrix that is shaped in a N x 1 x 2 format. N would be the total number of points that represent the contour. I'm going to remove the singleton second dimension so we can get this to be a N x 2 matrix. Also, let's use the full representation of the contours for now:

要在注释中解决您的问题,您可以采用所需的轮廓结构(轮廓1或轮廓2)并搜索轮廓点。轮廓是所有可能轮廓的列表,并且在每个轮廓内是3D矩阵,其形状为N×1×2格式。 N将是表示轮廓的点的总数。我将删除单个第二维,因此我们可以将其设为N x 2矩阵。另外,让我们现在使用轮廓的完整表示:

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

I am going to assume that your image only has one object, hence my indexing into contours1 with index 0. I unravel the matrix so that it becomes a single row vector, then reshape the matrix so that it becomes N x 2. Next, we can find the minimum point by:

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

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

np.argmin finds the location of the smallest value in an array that you supply. In this case, we want to operate along the x coordinate, or the columns. Once we find this location, we simply index into our 2D contour point array and extract out the contour point.

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

#2


3  

Add these 2 lines at the end:

最后添加这两行:

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

Also, be aware that you have img instead of im in your last line.

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

#3


2  

You should add cv2.imshow("Title", img) at the end of your code. It should look like this:

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