Solución:
Resolví tu problema usando el algoritmo de cuenca de OpenCV. Puede encontrar la teoría y ejemplos de cuencas hidrográficas aquí.
Primero seleccioné varios puntos (marcadores) para dictar dónde está el objeto que quiero mantener y dónde está el fondo. Este paso es manual y puede variar mucho de una imagen a otra. Además, requiere cierta repetición hasta obtener el resultado deseado. Sugiero usar una herramienta para obtener las coordenadas de píxeles. Luego creé una matriz entera vacía de ceros, con el tamaño de la imagen del automóvil. Y luego asigné algunos valores (1: fondo, [255,192,128,64]: car_parts) a píxeles en las posiciones de los marcadores.
NOTA: Cuando descargué tu imagen tuve que recortarla para obtener la del auto. Después de recortar, la imagen tiene un tamaño de 400×601. Es posible que este no sea el tamaño de la imagen que tiene, por lo que los marcadores estarán apagados.
Luego utilicé el algoritmo de cuenca. La primera entrada es su imagen y la segunda entrada es la imagen del marcador (cero en todas partes excepto en las posiciones de los marcadores). El resultado se muestra en la siguiente imagen.
Configuré todos los píxeles con un valor mayor que 1 a 255 (el automóvil) y el resto (fondo) a cero. Luego dilaté la imagen obtenida con un kernel de 3×3 para no perder información sobre el contorno del coche. Finalmente, utilicé la imagen dilatada como máscara para la imagen original, usando la función cv2.bitwise_and (), y el resultado se encuentra en la siguiente imagen:
Aquí está mi código:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Load the image
img = cv2.imread("/path/to/image.png", 3)
# Create a blank image of zeros (same dimension as img)
# It should be grayscale (1 color channel)
marker = np.zeros_like(img[:,:,0]).astype(np.int32)
# This step is manual. The goal is to find the points
# which create the result we want. I suggest using a
# tool to get the pixel coordinates.
# Dictate the background and set the markers to 1
marker[204][95] = 1
marker[240][137] = 1
marker[245][444] = 1
marker[260][427] = 1
marker[257][378] = 1
marker[217][466] = 1
# Dictate the area of interest
# I used different values for each part of the car (for visibility)
marker[235][370] = 255 # car body
marker[135][294] = 64 # rooftop
marker[190][454] = 64 # rear light
marker[167][458] = 64 # rear wing
marker[205][103] = 128 # front bumper
# rear bumper
marker[225][456] = 128
marker[224][461] = 128
marker[216][461] = 128
# front wheel
marker[225][189] = 192
marker[240][147] = 192
# rear wheel
marker[258][409] = 192
marker[257][391] = 192
marker[254][421] = 192
# Now we have set the markers, we use the watershed
# algorithm to generate a marked image
marked = cv2.watershed(img, marker)
# Plot this one. If it does what we want, proceed;
# otherwise edit your markers and repeat
plt.imshow(marked, cmap='gray')
plt.show()
# Make the background black, and what we want to keep white
marked[marked == 1] = 0
marked[marked > 1] = 255
# Use a kernel to dilate the image, to not lose any detail on the outline
# I used a kernel of 3x3 pixels
kernel = np.ones((3,3),np.uint8)
dilation = cv2.dilate(marked.astype(np.float32), kernel, iterations = 1)
# Plot again to check whether the dilation is according to our needs
# If not, repeat by using a smaller/bigger kernel, or more/less iterations
plt.imshow(dilation, cmap='gray')
plt.show()
# Now apply the mask we created on the initial image
final_img = cv2.bitwise_and(img, img, mask=dilation.astype(np.uint8))
# cv2.imread reads the image as BGR, but matplotlib uses RGB
# BGR to RGB so we can plot the image with accurate colors
b, g, r = cv2.split(final_img)
final_img = cv2.merge([r, g, b])
# Plot the final result
plt.imshow(final_img)
plt.show()
Si tiene muchas imágenes, probablemente necesitará crear una herramienta para anotar los marcadores gráficamente, o incluso un algoritmo para encontrar marcadores automáticamente.
El problema es que estás restando matrices de no firmado Enteros de 8 bits. Esta operación puede desbordarse.
Demostrar
>>> import numpy as np
>>> a = np.array([[10,10]],dtype=np.uint8)
>>> b = np.array([[11,11]],dtype=np.uint8)
>>> a - b
array([[255, 255]], dtype=uint8)
Dado que está utilizando OpenCV, la forma más sencilla de lograr su objetivo es utilizar cv2.absdiff()
.
>>> cv2.absdiff(a,b)
array([[1, 1]], dtype=uint8)