如何使用Python OpenCV在图像中获取泛洪区域及其边界?

如何使用Python OpenCV在图像中获取泛洪区域及其边界?,python,opencv,Python,Opencv,我有一个像这样的图像,它只是黑白的: 我想使用cv2.floodfill,仅获取图像的泛光区域和边框,就像这样(请原谅我的绘画技巧): 以下是我当前的代码: #复制图像。 im_floodfill=cv2.resize(实际地图图像,(500500)).copy() #从点(X,Y)开始的洪水填充 cv2.漫灌(im_漫灌,无,(X,Y),(255,255,255)) #显示图像。 cv2.imshow(“泛光填充图像”,im_泛光填充) cv2.等待键(0) 我得到的输出等于原始图像。如

我有一个像这样的图像,它只是黑白的:

我想使用
cv2.floodfill
,仅获取图像的泛光区域和边框,就像这样(请原谅我的绘画技巧):

以下是我当前的代码:

#复制图像。
im_floodfill=cv2.resize(实际地图图像,(500500)).copy()
#从点(X,Y)开始的洪水填充
cv2.漫灌(im_漫灌,无,(X,Y),(255,255,255))
#显示图像。
cv2.imshow(“泛光填充图像”,im_泛光填充)
cv2.等待键(0)
我得到的输出等于原始图像。如何仅获取有边界的淹没区

编辑:我想从“竞技场”内的任何白色点进行泛光填充,如图像中的红点(X,Y)。我只希望竞技场内小圆圈的外边界和外墙的内边界

EDIT2:我已经说了一半了:

#出于测试目的调整大小
实际映射图像=cv2。调整大小(实际映射图像,(1000,1000))
实际映射图像=cv2.CVT颜色(实际映射图像,cv2.COLOR\BGR2GRAY)
h、 w=实际地图图像。形状[:2]
flood_mask=np.zero((h+2,w+2),dtype=np.uint8)
连接性=8

flood_fill_flags=(连接性| cv2.FLOODFILL_固定|范围| cv2.FLOODFILL_掩码|仅| 255扩容和异或如何

kernel = np.ones((3,3), np.uint8)
dilated = cv2.dilate(actual_map_image, kernel, iterations = 1)
borders = cv2.bitwise_xor(dilated, actual_map_image)
这只会给你边界,我不清楚你是想要圆形边界还是内部边界,你应该能够根据大小删除你不想要的边界


可以使用大小阈值删除外部边框,定义如下函数:

def size_threshold(bw, minimum, maximum):
    retval, labels, stats, centroids = cv.connectedComponentsWithStats(bw)
    for val in np.where((stats[:, 4] < minimum) + (stats[:, 4] > maximum))[0]:
      labels[labels==val] = 0
    return (labels > 0).astype(np.uint8) * 255

result = size_threshold(borders, 0, 500)
def大小_阈值(bw、最小值、最大值):
retval、标签、统计信息、质心=cv.connectedComponentsWithStats(bw)
对于np中的val,其中((stats[:,4]<最小值)+(stats[:,4]>最大值))[0]:
标签[labels==val]=0
返回(标签>0).aType(np.uint8)*255
结果=大小\阈值(边框,0,500)

用一个大于您想要保留的边界、小于您想要丢失的边界的数字替换500。

我必须创建自己的泛洪填充实现来获得我想要的。我基于自己

def fill(数据、开始坐标、填充值、边界值、连接性=8):
"""
洪水填充算法
参数
----------
数据:(M,N)uint8型数据阵列
要填充洪水的图像。就地修改。
启动坐标:元组
长度—定义(行、列)起始坐标的整数的2元组。
填充值:int
填充后淹没区域将采用的值。
边界值:int
用于绘制填充区域边界的颜色值。
连通性:4或8
我们用于洪水填充算法的连通性(4路或8路)。
退换商品
-------
填充数据:ndarray
包含填充区域的数据。
边界:Ndaray
填充区域的边界绘制为边界值颜色。
"""
在[4,8]中断言连接性
filled_data=data.copy()
xsize,ysize=填充的_data.shape
原始值=填充的数据[开始坐标[0],开始坐标[1]]
stack=set((开始坐标[0],开始坐标[1]))
如果填充值==原始值:
raise VALUERROR(“不支持使用已存在的相同值填充区域。是否已填充此区域?”)
边界点=[]
堆栈时:
x、 y=stack.pop()
如果填充的_数据[x,y]==原始值:
填充数据[x,y]=填充值
如果x>0:
叠加((x-1,y))
如果x<(x尺寸-1):
叠加((x+1,y))
如果y>0:
叠加((x,y-1))
如果y<(y尺寸-1):
叠加((x,y+1))
如果连接性==8:
如果x>0且y>0:
叠加((x-1,y-1))
如果x>0且y<(y大小-1):
叠加((x-1,y+1))
如果x<(xsize-1)和y>0:
叠加((x+1,y-1))
如果x<(xsize-1)和y<(ysize-1):
叠加((x+1,y+1))
其他:
如果填充数据[x,y]!=填充值:
border_points.append([x,y])
#用白色填充所有图像
borders=填充的数据。复制()
边框填充(255)
#画边框
对于边界点中的x、y:
边框[x,y]=边框_值
返回填充的数据、边框
我所做的唯一一件事就是添加
else
条件。如果该点的值不等于
orig\u value
fill\u value
,则它是一个边框,因此我将其附加到包含所有边框点的列表中。然后我只绘制边框

使用此代码,我可以获得以下图像:

#出于测试目的调整大小
实际映射图像=cv2。调整大小(实际映射图像,(500500))
实际映射图像=cv2.CVT颜色(实际映射图像,cv2.COLOR\BGR2GRAY)
h、 w=实际地图图像。形状[:2]
填充数据,边界=填充(实际地图图像[h/2+20,w/2+20],127,0,连通性=8)
cv2.imshow(“原始图像”,实际地图图像)
cv2.imshow(“填充图像”,填充数据)
cv2.imshow(“边界”,边界)


右边的那个就是我的目标。谢谢大家!

我认为最简单、最快的方法是用中灰色填满竞技场。然后只提取灰色像素并找到它们的边缘。看起来是这样的,但记住一半以上的线条是注释和调试语句:-)

以下是调试输出的3个步骤,显示了处理顺序:

result-1.png如下所示:

def size_threshold(bw, minimum, maximum):
    retval, labels, stats, centroids = cv.connectedComponentsWithStats(bw)
    for val in np.where((stats[:, 4] < minimum) + (stats[:, 4] > maximum))[0]:
      labels[labels==val] = 0
    return (labels > 0).astype(np.uint8) * 255

result = size_threshold(borders, 0, 500)

result-2.png如下所示:

def size_threshold(bw, minimum, maximum):
    retval, labels, stats, centroids = cv.connectedComponentsWithStats(bw)
    for val in np.where((stats[:, 4] < minimum) + (stats[:, 4] > maximum))[0]:
      labels[labels==val] = 0
    return (labels > 0).astype(np.uint8) * 255

result = size_threshold(borders, 0, 500)

result-3.png如下所示:

def size_threshold(bw, minimum, maximum):
    retval, labels, stats, centroids = cv.connectedComponentsWithStats(bw)
    for val in np.where((stats[:, 4] < minimum) + (stats[:, 4] > maximum))[0]:
      labels[labels==val] = 0
    return (labels > 0).astype(np.uint8) * 255

result = size_threshold(borders, 0, 500)


顺便说一下