获取轮廓Opencv Python内的区域?

Posted

技术标签:

【中文标题】获取轮廓Opencv Python内的区域?【英文标题】:Get area within contours Opencv Python? 【发布时间】:2015-02-02 15:36:13 【问题描述】:

我使用了一种自适应阈值技术来创建如下图所示的图片:

我使用的代码是:

image = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 45, 0)

然后,我使用此代码获取轮廓:

cnt = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]

我的目标是使用外轮廓内的所有像素生成蒙版,因此我想将对象内的所有像素填充为白色。我该怎么做?

我已尝试使用以下代码创建蒙版,但生成的蒙版似乎与应用自适应阈值后的图像没有什么不同

mask = np.zeros(image.shape[:2], np.uint8)
cv2.drawContours(mask, cnt, -1, 255, -1)

【问题讨论】:

【参考方案1】:

你所拥有的几乎是正确的。如果您查看您的阈值图像,它无法正常工作的原因是因为您的鞋子对象在图像中有间隙。具体来说,您所追求的是您希望鞋子的周边是all连接的。如果发生这种情况,那么如果您提取最外部的轮廓(这是您的代码正在执行的操作),您应该只有一个代表对象外周长的轮廓。填充轮廓后,您的鞋子应该是完全实心的。

由于鞋子的周边不完整且破损,这会导致白色区域断开连接。如果您使用findContours 查找所有轮廓,它只会找到每个白色形状的轮廓,而不是最外周。因此,如果您尝试使用findContours,它会得到与原始图像相同的结果,因为您只需找到图像内每个白色区域的周长,然后用findContours 填充这些区域.


您需要做的是确保图像完全关闭。我建议您使用morphology 将所有断开连接的区域一起关闭,然后对这个新图像运行findContours 调用。具体来说,执行二进制形态关闭。这样做的目的是,它需要靠近在一起的断开的白色区域并确保它们是连接的。使用形态闭合,或者使用类似 7 x 7 方形结构元素的东西来闭合鞋子。您可以将此结构化元素视为白色区域之间的最小间隔,以将它们视为已连接。

因此,请执行以下操作:

import numpy as np
import cv2 
image = cv2.imread('...') # Load your image in here
# Your code to threshold
image = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 45, 0)    

# Perform morphology
se = np.ones((7,7), dtype='uint8')
image_close = cv2.morphologyEx(image, cv2.MORPH_CLOSE, se)

# Your code now applied to the closed image
cnt = cv2.findContours(image_close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
mask = np.zeros(image.shape[:2], np.uint8)
cv2.drawContours(mask, cnt, -1, 255, -1)

此代码本质上是获取您的阈值图像,并将形态关闭应用于此图像。之后,我们找到这张图片的外部轮廓,并用白色填充它们。 FWIW,我下载了你的阈值图像,并自己尝试了这个。这就是我从你的图片中得到的:

【讨论】:

这太棒了,我不敢相信这行得通!在一个下午,我能够模糊图像的背景。 Opencv 很酷。 @emschorsch - 感谢您的支持 :) 是的,OpenCV 是一个很棒的平台。我每天都用它来工作。学习愉快! @rayryeng 我尝试了这段代码,但在最后一行它抛出了以下错误drawing.cpp:2380: error: (-215) npoints > 0 in function cv::drawContours - 我使用的是 OpenCV 3.1 和 Python 2.7。有什么想法吗? @g491。这意味着没有要绘制的轮廓。 哇,好简单!但是,今天(2020 年)thickness=cv2.FILLED 在您已经隔离了单个轮廓时不会像那样工作。在这种情况下,我们必须将该轮廓作为...drawContours(..., [cnt]...) 传递,以使其再次成为一个数组。见this answer。【参考方案2】:

一种简单的方法是用cv2.morphologyEx()cv2.MORPH_CLOSE 关闭前景中的孔以形成单个轮廓

现在外轮廓已经被填充了,我们可以找到带有cv2.findContours()的外轮廓,并使用cv2.fillPoly()将所有像素填充为白色

import cv2

# Load in image, convert to grayscale, and threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Close contour
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=1)

# Find outer contour and fill with white
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cv2.fillPoly(close, cnts, [255,255,255])

cv2.imshow('close', close)
cv2.waitKey()

【讨论】:

以上是关于获取轮廓Opencv Python内的区域?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用opencv绘制轮廓内的区域? (图片是从 dxf 文件导出的)[关闭]

如何在 Python OpenCV 中删除轮廓内的轮廓?

使用OpenCV(Python)找到具有最大封闭区域的轮廓

在 Python 中使用 OpenCV 访问轮廓边界内的像素值

怎么用opencv和python,只保留深蓝色部分,其他区域变为黑色

OpenCV在轮廓边界内获取黑色像素