python - segmentacion - ¿Cómo elimino el fondo de este tipo de imagen?
ruido gaussiano python (5)
Quiero eliminar el fondo de esta imagen para obtener solo a la persona. Tengo miles de imágenes como esta, básicamente, una persona y un fondo algo blanquecino.
Lo que he hecho es usar un detector de bordes como un detector de bordes
skimage
o un filtro
skimage
(de la biblioteca de
skimage
).
Entonces, lo que creo que es posible hacer es blanquear los píxeles dentro de los bordes y ennegrecer los píxeles sin ellos.
Luego, la imagen original se puede enmascarar para obtener solo la imagen de la persona.
Sin embargo, es difícil obtener un límite cerrado con el detector de bordes de canny. El resultado usando el filtro Sobel no es tan malo, sin embargo, no sé cómo proceder desde allí.
EDITAR:
¿Es posible eliminar también el fondo entre la mano derecha y la falda y entre los pelos?
El siguiente código debería ayudarlo a comenzar. Es posible que desee jugar con los parámetros en la parte superior del programa para ajustar su extracción:
import cv2
import numpy as np
#== Parameters =======================================================================
BLUR = 21
CANNY_THRESH_1 = 10
CANNY_THRESH_2 = 200
MASK_DILATE_ITER = 10
MASK_ERODE_ITER = 10
MASK_COLOR = (0.0,0.0,1.0) # In BGR format
#== Processing =======================================================================
#-- Read image -----------------------------------------------------------------------
img = cv2.imread(''C:/Temp/person.jpg'')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#-- Edge detection -------------------------------------------------------------------
edges = cv2.Canny(gray, CANNY_THRESH_1, CANNY_THRESH_2)
edges = cv2.dilate(edges, None)
edges = cv2.erode(edges, None)
#-- Find contours in edges, sort by area ---------------------------------------------
contour_info = []
_, contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
# Previously, for a previous version of cv2, this line was:
# contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
# Thanks to notes from commenters, I''ve updated the code but left this note
for c in contours:
contour_info.append((
c,
cv2.isContourConvex(c),
cv2.contourArea(c),
))
contour_info = sorted(contour_info, key=lambda c: c[2], reverse=True)
max_contour = contour_info[0]
#-- Create empty mask, draw filled polygon on it corresponding to largest contour ----
# Mask is black, polygon is white
mask = np.zeros(edges.shape)
cv2.fillConvexPoly(mask, max_contour[0], (255))
#-- Smooth mask, then blur it --------------------------------------------------------
mask = cv2.dilate(mask, None, iterations=MASK_DILATE_ITER)
mask = cv2.erode(mask, None, iterations=MASK_ERODE_ITER)
mask = cv2.GaussianBlur(mask, (BLUR, BLUR), 0)
mask_stack = np.dstack([mask]*3) # Create 3-channel alpha mask
#-- Blend masked img into MASK_COLOR background --------------------------------------
mask_stack = mask_stack.astype(''float32'') / 255.0 # Use float matrices,
img = img.astype(''float32'') / 255.0 # for easy blending
masked = (mask_stack * img) + ((1-mask_stack) * MASK_COLOR) # Blend
masked = (masked * 255).astype(''uint8'') # Convert back to 8-bit
cv2.imshow(''img'', masked) # Display
cv2.waitKey()
#cv2.imwrite(''C:/Temp/person-masked.jpg'', masked) # Save
Ouput:
Si desea rellenar el fondo no con un color rojo sino hacerlo transparente, puede agregar las siguientes líneas a la solución:
# split image into channels
c_red, c_green, c_blue = cv2.split(img)
# merge with mask got on one of a previous steps
img_a = cv2.merge((c_red, c_green, c_blue, mask.astype(''float32'') / 255.0))
# show on screen (optional in jupiter)
%matplotlib inline
plt.imshow(img_a)
plt.show()
# save to disk
cv2.imwrite(''girl_1.png'', img_a*255)
# or the same using plt
plt.imsave(''girl_2.png'', img_a)
Si lo desea, puede modificar algunos parámetros de compresión png para hacer el archivo más pequeño.
Imagen sobre un fondo blanco a continuación. O en uno negro: http://imgur.com/a/4NwmH
Ejemplo de trabajo con vs2017.
Establece el fondo rojo pero guarda el azul.
También se agregó el ejemplo transperent en.
¿Cómo puedo quitar el cuerpo de las chicas y dejar solo el vestido en la imagen? ¿Algunas ideas?
# == https://.com/questions/29313667/how-do-i-remove-the-background-from-this-kind-of-image
import cv2
import numpy as np
from matplotlib import pyplot as plt
#== Parameters =======================================================================
BLUR = 21
CANNY_THRESH_1 = 10
CANNY_THRESH_2 = 200
MASK_DILATE_ITER = 10
MASK_ERODE_ITER = 10
MASK_COLOR = (0.0,0.0,1.0) # In BGR format
#== Processing =======================================================================
#-- Read image -----------------------------------------------------------------------
img = cv2.imread(''img/SYxmp.jpg'')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#-- Edge detection -------------------------------------------------------------------
edges = cv2.Canny(gray, CANNY_THRESH_1, CANNY_THRESH_2)
edges = cv2.dilate(edges, None)
edges = cv2.erode(edges, None)
#-- Find contours in edges, sort by area ---------------------------------------------
contour_info = []
_, contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
for c in contours:
contour_info.append((
c,
cv2.isContourConvex(c),
cv2.contourArea(c),
))
contour_info = sorted(contour_info, key=lambda c: c[2], reverse=True)
max_contour = contour_info[0]
#-- Create empty mask, draw filled polygon on it corresponding to largest contour ----
# Mask is black, polygon is white
mask = np.zeros(edges.shape)
cv2.fillConvexPoly(mask, max_contour[0], (255))
#-- Smooth mask, then blur it --------------------------------------------------------
mask = cv2.dilate(mask, None, iterations=MASK_DILATE_ITER)
mask = cv2.erode(mask, None, iterations=MASK_ERODE_ITER)
mask = cv2.GaussianBlur(mask, (BLUR, BLUR), 0)
mask_stack = np.dstack([mask]*3) # Create 3-channel alpha mask
#-- Blend masked img into MASK_COLOR background --------------------------------------
mask_stack = mask_stack.astype(''float32'') / 255.0 # Use float matrices,
img = img.astype(''float32'') / 255.0 # for easy blending
masked = (mask_stack * img) + ((1-mask_stack) * MASK_COLOR) # Blend
masked = (masked * 255).astype(''uint8'') # Convert back to 8-bit
plt.imsave(''img/girl_blue.png'', masked)
# split image into channels
c_red, c_green, c_blue = cv2.split(img)
# merge with mask got on one of a previous steps
img_a = cv2.merge((c_red, c_green, c_blue, mask.astype(''float32'') / 255.0))
# show on screen (optional in jupiter)
#%matplotlib inline
plt.imshow(img_a)
plt.show()
# save to disk
cv2.imwrite(''img/girl_1.png'', img_a*255)
# or the same using plt
plt.imsave(''img/girl_2.png'', img_a)
cv2.imshow(''img'', masked) # Displays red, saves blue
cv2.waitKey()
-
Después de obtener sus bordes incompletos (como lo ha hecho), puede ejecutar una morfología de cierre (una secuencia de dilatación y erosión) (tendrá que establecer el tamaño y las iteraciones según las necesidades / estado de los bordes).
-
Ahora, suponiendo que tiene un borde constante alrededor del sujeto, use cualquier tipo de algoritmo de relleno (blob) para combinar todos los puntos fuera del objeto con bordes, luego tome el negativo de eso para darle la máscara del interior del objeto .