【发布时间】:2021-01-15 23:28:33
【问题描述】:
我试图找到用 python 绘制的任意形状的闭合曲线内的区域(下图示例)。到目前为止,我已经尝试使用 alphashape 和多边形方法来实现这一点,但都失败了。我现在正在尝试使用 OpenCV 和 Floodfill 方法来计算曲线内的像素数,然后我将稍后将其转换为给定单个像素在图上包围的区域的区域。 示例图像: testplot.jpg
为了做到这一点,我正在做以下事情,我改编自另一篇关于 OpenCV 的帖子。
import cv2
import numpy as np
# Input image
img = cv2.imread('testplot.jpg', cv2.IMREAD_GRAYSCALE)
# Dilate to better detect contours
temp = cv2.dilate(temp, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)))
# Find largest contour
cnts, _ = cv2.findContours(255-temp, cv2.RETR_TREE , cv2.CHAIN_APPROX_NONE) #255-img and cv2.RETR_TREE is to account for how cv2 expects the background to be black, not white, so I convert the background to black.
largestCnt = [] #I expect this to yield the blue contour
for cnt in cnts:
if (len(cnt) > len(largestCnt)):
largestCnt = cnt
# Determine center of area of largest contour
M = cv2.moments(largestCnt)
x = int(M["m10"] / M["m00"])
y = int(M["m01"] / M["m00"])
# Initial mask for flood filling, should cover entire figure
width, height = temp.shape
mask = img2 = np.ones((width + 2, height + 2), np.uint8) * 255
mask[1:width, 1:height] = 0
# Generate intermediate image, draw largest contour onto it, flood fill this contour
temp = np.zeros(temp.shape, np.uint8)
temp = cv2.drawContours(temp, largestCnt, -1, 255, cv2.FILLED)
_, temp, mask, _ = cv2.floodFill(temp, mask, (x, y), 255)
temp = cv2.morphologyEx(temp, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)))
area = cv2.countNonZero(temp) #Number of pixels encircled by blue line
我希望由此得到一个与上面相同图像的地方,但轮廓的中心填充为白色,背景和原始的蓝色轮廓为黑色。我最终得到了这个:
虽然乍一看似乎准确地将轮廓内的区域变为白色,但白色区域实际上大于轮廓内的区域,因此我得到的结果是高估了其中的像素数。 对此的任何意见将不胜感激。我对 OpenCV 还很陌生,所以我可能误解了一些东西。
编辑: 感谢下面的评论,我进行了一些编辑,现在这是我的代码,并注明了编辑:
import cv2
import numpy as np
# EDITED INPUT IMAGE: Input image
img = cv2.imread('testplot2.jpg', cv2.IMREAD_GRAYSCALE)
# EDIT: threshold
_, temp = cv2.threshold(img, 250, 255, cv2.THRESH_BINARY_INV)
# EDIT, REMOVED: Dilate to better detect contours
# Find largest contour
cnts, _ = cv2.findContours(temp, cv2.RETR_EXTERNAL , cv2.CHAIN_APPROX_NONE)
largestCnt = [] #I expect this to yield the blue contour
for cnt in cnts:
if (len(cnt) > len(largestCnt)):
largestCnt = cnt
# Determine center of area of largest contour
M = cv2.moments(largestCnt)
x = int(M["m10"] / M["m00"])
y = int(M["m01"] / M["m00"])
# Initial mask for flood filling, should cover entire figure
width, height = temp.shape
mask = img2 = np.ones((width + 2, height + 2), np.uint8) * 255
mask[1:width, 1:height] = 0
# Generate intermediate image, draw largest contour, flood filled
temp = np.zeros(temp.shape, np.uint8)
temp = cv2.drawContours(temp, largestCnt, -1, 255, cv2.FILLED)
_, temp, mask, _ = cv2.floodFill(temp, mask, (x, y), 255)
temp = cv2.morphologyEx(temp, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)))
area = cv2.countNonZero(temp) #Number of pixels encircled by blue line
我输入了一个不同的image,为方便起见,python 默认添加的轴和框架被删除。我在第二步得到了我的期望,所以this image。但是,在enter image description here 中,原始轮廓和它包围的区域似乎都变成了白色,而我希望原始轮廓是黑色的,只有它包围的区域是白色的。我怎样才能做到这一点?
【问题讨论】:
-
在我看来,如果您转换为灰度和阈值,使曲线为白色,外部为黑色,您应该能够在 findContours 中使用 cv2.RETR_EXTERNAL,然后获取其 cv2.contourArea。轮廓最适合二值图像。如果这对您不起作用,请告诉我,我将自己编写代码。
-
您好,谢谢您的回答!我稍微修改了脚本,但它似乎还没有工作。我将编辑我的问题,以便您了解我现在在做什么以及我得到的结果
-
你不需要所有的时刻和洪水填充。你可以简单地得到轮廓,然后 cv2.contour(area) 应该给你你想要的。但请确保在阈值后轮廓为白色,背景为黑色。您可能需要反转阈值图像。查看它以确定。如果您需要将其视为黑色填充的白色,则创建一个输入大小的新黑色图像,然后使用 drawContours() 在其上绘制白色填充轮廓并将线条粗细设置为 -1。