Tengo dos imágenes, una con solo fondo y la otra con fondo + objeto detectable (en mi caso es un automóvil). Abajo están las imágenes

enter image description here

Estoy tratando de eliminar el fondo de modo que solo tenga el automóvil en la imagen resultante. El siguiente es el código con el que estoy tratando de obtener los resultados deseados.

import numpy as np
import cv2


original_image = cv2.imread('IMG1.jpg', cv2.IMREAD_COLOR)
gray_original = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
background_image = cv2.imread('IMG2.jpg', cv2.IMREAD_COLOR)
gray_background = cv2.cvtColor(background_image, cv2.COLOR_BGR2GRAY)

foreground = np.absolute(gray_original - gray_background)
foreground[foreground > 0] = 255

cv2.imshow('Original Image', foreground)
cv2.waitKey(0)

La imagen resultante restando las dos imágenes es

enter image description here

Aquí está el problema. La imagen resultante esperada debe ser solo un automóvil. Además, si observa en profundidad las dos imágenes, verá que no son exactamente iguales, es decir, la cámara se movió un poco, por lo que el fondo se había alterado un poco. Mi pregunta es que con estas dos imágenes, ¿cómo puedo restar el fondo? No quiero usar el algoritmo grabCut o backgroundSubtractorMOG en este momento porque no sé qué está pasando dentro de esos algoritmos.

Lo que intento hacer es obtener la siguiente imagen resultante ingrese la descripción de la imagen aquí

Además, si es posible, guíeme con una forma general de hacerlo, no solo en este caso específico, es decir, tengo un fondo en una imagen y fondo + objeto en la segunda imagen. ¿Cuál podría ser la mejor manera posible de hacer esto? Perdón por una pregunta tan larga.

20
muazfaiz 17 feb. 2017 a las 12:26

3 respuestas

La mejor respuesta

Resolví tu problema usando watershed"> watershed"> de OpenCV. un> algoritmo. 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é un conjunto entero vacío 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é su imagen, tuve que recortarla para obtener la que tenía el automóvil. Después de recortar, la imagen tiene un tamaño de 400x601. Puede que este no sea el tamaño de la imagen que tiene, por lo que los marcadores estarán apagados.

Después 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 del marcador). El resultado se muestra en la siguiente imagen. después de la cuenca

Configuré todos los píxeles con un valor mayor de 1 a 255 (el automóvil) y el resto (fondo) a cero. Luego dilaté la imagen obtenida con un núcleo de 3x3 para evitar perder información sobre el contorno del automóvil. Finalmente, usé la imagen dilatada como una máscara para la imagen original, usando la función cv2.bitwise_and (), y el resultado se encuentra en la siguiente imagen: imagen final recortada

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.

17
TasosGlrs 21 abr. 2017 a las 15:19

Recomiendo usar el algoritmo grabcut de OpenCV. Primero dibuja unas pocas líneas en el primer plano y el fondo, y sigue haciendo esto hasta que el primer plano esté suficientemente separado del fondo. Está cubierto aquí: https://docs.opencv.org/trunk/d8 /d83/tutorial_py_grabcut.html así como en este video: https://www.youtube.com/watch?v=kAwxLTDDAwU

0
wordsforthewise 23 ene. 2019 a las 18:57

El problema es que estás restando matrices de enteros de 8 bits sin signo . 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)

Como 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)
6
Dan Mašek 17 feb. 2017 a las 15:04