Для действительно произвольной формы, я бы порекомендовал заливку. Однако, поскольку у вас есть гарантированная выпуклая форма, вы можете сделать некоторые оптимизации. В частности, каждая строка / столбец изображения будет следовать одному из трех шаблонов:
- Весь черный
- Черный, белый, черный
- Черный, белый, черный,белый, черный
Технически существует больше опций, поскольку один или оба черных поля в опциях 2 и 3 могут отсутствовать. Цель состоит в том, чтобы заполнить среднюю черную область в варианте 3. Это можно сделать с помощью простой маскировки и необычной индексации.
Основной алгоритм:
- Вычислить началоиндекс каждого белого сегмента
- Создайте маску строки из строк, содержащих два начальных индекса
- Создайте полную маску, содержащую исходные данные, с элементами между индексами, также установленными на
True
.
def fill_convex(image):
mask = image.astype(np.bool)
# mask out elements that are 1, but the previous is 0
start = (mask[:, 1:] & ~mask[:, :-1])
# find rows that have exactly two runs of True
row_mask = (np.count_nonzero(start, axis=1) == 2)
# get the pairs of column indices that correspond to the masked elements
cols = np.nonzero(start[row_mask, :])[1].reshape(-1, 2)
# create a row of column indices the same size as a row
count = np.arange(image.shape[1])
# fill in the elements between start and stop indices for each row
# the None indices are used to trigger broadcasting
to_fill = ((count[None, :] >= cols[:, 0, None]) & (count[None, :] <= cols[:, 1, None]))
# update the mask
mask[row_mask, :] |= to_fill
# fill in the image
image[mask] = 255
return image
Время
Этот метод примерно в два раза медленнее, чем @ nathancy's , и более чем в 10 раз медленнее, чем @ MarkSetchell в . Я просто оставляю это здесь для развлечения.
$ 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)
# mask out elements that are 1, but the previous is 0
start = (mask[:, 1:] & ~mask[:, :-1])
# find rows that have exactly two runs of True
row_mask = (np.count_nonzero(start, axis=1) == 2)
# get the pairs of column indices that correspond to the masked elements
cols = np.nonzero(start[row_mask, :])[1].reshape(-1, 2)
# create a row of column indices the same size as a row
count = np.arange(image.shape[1])
# fill in the elements between start and stop indices for each row
# the None indices are used to trigger broadcasting
to_fill = ((count[None, :] >= cols[:, 0, None]) & (count[None, :] <= cols[:, 1, None]))
# update the mask
mask[row_mask, :] |= to_fill
# fill in the image
image[mask] = 255
return image
image = cv2.imread('58174115.png', 0)