对于一个真正的任意形状,我建议使用泛洪填充算法。但是,由于您有保证凸形状,因此可以进行一些优化。具体来说,图像的每行/列将遵循以下三种模式之一:
- 全部为黑色
- 黑、白、黑
- 黑、白、黑、白、黑
从技术上讲,选项2和3中的黑色边缘可能会单独或同时丢失,因此有更多的选项。目标是填充选项3中的中间黑色区域。这可以通过一些简单的numpy掩码和高级索引来完成。
基本算法如下:
- 计算每个白色段的起始索引
- 创建包含两个起始索引的行掩码
- 创建一个完整的掩码,其中包含原始数据,并将索引之间的元素设置为
True
def fill_convex(image):
mask = image.astype(np.bool)
start = (mask[:, 1:] & ~mask[:, :-1])
row_mask = (np.count_nonzero(start, axis=1) == 2)
cols = np.nonzero(start[row_mask, :])[1].reshape(-1, 2)
count = np.arange(image.shape[1])
to_fill = ((count[None, :] >= cols[:, 0, None]) & (count[None, :] <= cols[:, 1, None]))
mask[row_mask, :] |= to_fill
image[mask] = 255
return image
时间
这种方法比@nathancy的方法慢大约两倍,比@MarkSetchell的方法慢超过10倍。我基本上是出于好玩而将其保留在这里。
$ python -m timeit -s 'import q58174115' 'q58174115.nathancy(q58174115.image)'
500 loops, best of 5: 437 usec per loop
$ python -m timeit -s 'import q58174115' 'q58174115.MarkSetchell(q58174115.image.copy())'
5000 loops, best of 5: 62.9 usec per loop
$ python -m timeit -s 'import q58174115' 'q58174115.MadPhysicist(q58174115.image.copy())'
500 loops, best of 5: 779 usec per loop
在这里,q58174115.py
表示
import cv2
import numpy as np
def nathancy(image):
thresh = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cv2.fillPoly(image, cnts, [255,255,255])
return image
def MarkSetchell(image):
x,y,w,h = cv2.boundingRect(image)
cv2.floodFill(image,None,(int(x+w/2),int(y+h/2)),255)
return image
def MadPhysicist(image):
mask = image.astype(np.bool)
start = (mask[:, 1:] & ~mask[:, :-1])
row_mask = (np.count_nonzero(start, axis=1) == 2)
cols = np.nonzero(start[row_mask, :])[1].reshape(-1, 2)
count = np.arange(image.shape[1])
to_fill = ((count[None, :] >= cols[:, 0, None]) & (count[None, :] <= cols[:, 1, None]))
mask[row_mask, :] |= to_fill
image[mask] = 255
return image
image = cv2.imread('58174115.png', 0)