Tengo una colección de imágenes con un círculo dibujado como un contorno blanco. Sin embargo, quiero llenar el círculo completo con color blanco. ¿Cuál es una forma rápida de hacerlo? La siguiente es la muestra de la imagen:
He intentado usar bucles anidados para lograr esto, pero lleva mucho tiempo, y tengo alrededor de 1,5 millones de imágenes. El siguiente es mi código:
roundRobinIndex = 0
new_image = np.zeros((img_w, img_h))
for row in range(540):
for column in range(800):
if image[row,column] == 255:
roundRobinIndex = (roundRobinIndex + 1) % 2
if roundRobinIndex == 1:
new_image[row, column] = 255
3 respuestas
Traté de encontrar el cuadro delimitador del contorno blanco, y obtener su centro, luego llenándome de blanco desde allí hacia afuera.
#!/usr/bin/env python3
import cv2
def findfill(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])
def me(image):
x,y,w,h = cv2.boundingRect(image)
cv2.floodFill(image,None,(int(x+w/2),int(y+h/2)),255)
return image
image = cv2.imread('BLYmz.png', 0)
%timeit findfill(image)
%timeit me(image)
Esto parece dar los mismos resultados y ejecuta 2.5 veces más rápido:
findfill
810 µs ± 2.94 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
me
343 µs ± 1.06 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Por supuesto, si tiene 1,5 millones para hacer, recomendaría también un procesamiento paralelo :-)
Para una forma verdaderamente arbitraria, recomendaría el relleno de inundación. Sin embargo, dado que tiene una forma convexa garantizada, puede hacer algunas optimizaciones. Específicamente, cada fila / columna de la imagen seguirá uno de tres patrones:
- Toda negra
- Negra, blanca, negra
- Negra, blanca, negra, blanca, negra
Técnicamente hay más opciones, ya que puede faltar uno o ambos márgenes negros en las opciones 2 y 3. El objetivo es completar la región negra del medio en la opción 3. Esto se puede hacer con un simple enmascarado numpy e indexación elegante.
El algoritmo básico es:
- Calcule el índice de inicio de cada segmento blanco
- Haga una máscara de fila de filas que contengan dos índices iniciales
- Haga una máscara completa que contenga los datos originales, con elementos entre los índices establecidos también en
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
Tiempo
Este método es aproximadamente el doble de lento que @ nathancy's y más de 10 veces más lento que @MarkSetchell's . Básicamente, lo dejo aquí por diversión en este momento.
$ 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
Aquí, q58174115.py
es
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)
Use cv2.fillPoly()
para llenar el círculo contorno
import cv2
image = cv2.imread('1.png', 0)
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])
cv2.imshow('image', image)
cv2.waitKey()
Nota: El umbral de Otsu podría eliminarse para obtener un rendimiento ligeramente más rápido, ya que la imagen de entrada ya es una imagen binaria, puede encontrar contornos directamente en la imagen en escala de grises
Preguntas relacionadas
Nuevas preguntas
python
Python es un lenguaje de programación multipropósito, de tipificación dinámica y de múltiples paradigmas. Está diseñado para ser rápido de aprender, comprender y usar, y hacer cumplir una sintaxis limpia y uniforme. Tenga en cuenta que Python 2 está oficialmente fuera de soporte a partir del 01-01-2020. Aún así, para preguntas de Python específicas de la versión, agregue la etiqueta [python-2.7] o [python-3.x]. Cuando utilice una variante de Python (por ejemplo, Jython, PyPy) o una biblioteca (por ejemplo, Pandas y NumPy), inclúyala en las etiquetas.