【问题标题】:How do I display the contours of an image using OpenCV Python?如何使用 OpenCV Python 显示图像的轮廓?
【发布时间】:2015-04-24 23:58:02
【问题描述】:

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

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

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

【问题讨论】:

    标签: python opencv image-processing computer-vision


    【解决方案1】:

    首先,该示例仅向您展示如何使用简单的近似值绘制轮廓。请记住,即使您使用简单的近似值绘制轮廓,它也会被视为在矩形周围完全绘制了一个蓝色轮廓,如左图所示。您将无法通过简单地在图像上绘制轮廓来获得正确的图像。此外,您想比较两组轮廓 - 右侧的简化版本与左侧的完整表示。具体来说,您需要将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,这样您就可以在一个窗口中显示两个图像。

    我会告诉你如何使用这两种方法:

    方法#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 中解决您的问题,您将采用您想要的轮廓结构(contours1contours2)并搜索轮廓点。 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 轮廓点数组并提取轮廓点。

    【讨论】:

    • 这是更详细和完整的答案。非常感谢
    • 哦,我的荣幸 :) 我并不是想从 runDOSrun 中窃取它,但为了我的辩护,我在他发布时正在研究这个答案。顺便说一句,祝你好运!
    • 如果我问你最后一个问题,我可能听起来很不礼貌,因为这个网站说我必须等待 90 分钟才能再次提问。我想打印最接近 Y 轴的像素的坐标(意味着它的 x 坐标最小)并且属于轮廓。可能这对你来说太简单了?我只是初学者水平
    • @Kabyle - 好的,给我几分钟来更新我的帖子。
    • @Kabyle - 我很高兴 :) 我喜欢回答 OpenCV 问题,因为我对它还是有点陌生​​,如果我能回答一个问题,它会锻炼我的肌肉。顺便说一句,请确保您再次阅读我的答案的开头。我改变了一些东西。
    【解决方案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()
    

    【讨论】:

    • 只需支持我的答案并接受另一个!干杯!
    • 我终于接受了冗长的答案,因为其中有很多细节需要我再次阅读并理解。但我赞成你的回答,前一个对我来说太难了。再次非常感谢您
    【解决方案3】:

    在末尾添加这两行:

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

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

    【讨论】:

    • 非常感谢。我支持您的正确答案,但接受了更长更详细的答案。再次感谢您,先生。
    • @Kabyle 不客气! rayryeng 的出色回答值得接受 ;)
    • @runDOSrun - 谢谢 :) 我不是故意要从你那里得到关注,但是当你发帖时我正在努力回答。感谢您发布tl;dr 答案!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-12
    • 2011-06-13
    • 1970-01-01
    • 2021-12-23
    • 2019-12-06
    • 2019-06-28
    相关资源
    最近更新 更多