wake-up-neo.net

Entfernen Sie den Hintergrund des Bildes mit opencv Python

Ich habe zwei Bilder, eines mit nur Hintergrund und das andere mit Hintergrund + erkennbarem Objekt (in meinem Fall ist es ein Auto). Unten sehen Sie die Bilder

 enter image description here

Ich versuche den Hintergrund so zu entfernen, dass ich im Ergebnisbild nur ein Auto habe. Nachfolgend der Code, mit dem ich versuche, die gewünschten Ergebnisse zu erzielen

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)

Das resultierende Bild wird durch Subtrahieren der beiden Bilder erhalten 

 enter image description here

Hier ist das Problem. Das erwartete Bild sollte nur ein Auto sein. Wenn Sie sich die beiden Bilder genau ansehen, werden Sie feststellen, dass sie nicht genau gleich sind wenig. Meine Frage ist, dass ich mit diesen beiden Bildern den Hintergrund abziehen kann. Ich möchte den grabCut- oder backgroundSubtractorMOG-Algorithmus jetzt nicht verwenden, da ich im Moment nicht weiß, was in diesen Algorithmen vor sich geht. 

Was ich versuche zu tun, ist das folgende Ergebnisbild zu erhalten  enter image description here

Wenn möglich, leiten Sie mich bitte auf eine allgemeine Art und Weise ein, nicht nur in diesem speziellen Fall, dh ich habe einen Hintergrund in einem Bild und ein Hintergrund + Objekt in dem zweiten Bild. Was könnte der beste Weg sein, dies zu tun. Sorry für eine so lange Frage.

14
muazfaiz

Ich habe Ihr Problem mit dem Algorithmus Watershed von OpenCV gelöst. Die Theorie und Beispiele der Wasserscheide hier .

Zuerst habe ich mehrere Punkte (Markierungen) ausgewählt, um festzulegen, wo sich das Objekt befindet, das ich behalten möchte, und wo der Hintergrund ist. Dieser Schritt ist manuell und kann von Bild zu Bild sehr unterschiedlich sein. Außerdem erfordert es einige Wiederholungen, bis Sie das gewünschte Ergebnis erhalten. Ich schlage vor, ein Werkzeug zu verwenden, um die Pixelkoordinaten abzurufen. Dann erstellte ich ein leeres ganzzahliges Array von Nullen mit der Größe des Fahrzeugbildes. Und dann habe ich Pixeln an Markierungspositionen einige Werte (1: Hintergrund, [255,192,128,64]: car_parts) zugewiesen.

HINWEIS: Als ich Ihr Bild heruntergeladen habe, musste ich es beschneiden, um es mit dem Auto zu bekommen. Nach dem Zuschneiden hat das Bild eine Größe von 400x601. Möglicherweise entspricht dies nicht der Größe des Bildes, daher sind die Markierungen deaktiviert.

Danach habe ich den Algorithmus der Wasserscheide verwendet. Die erste Eingabe ist Ihr Bild und die zweite Eingabe ist das Markierungsbild (Null, außer an Markierungspositionen). Das Ergebnis wird im Bild unten gezeigt .  after watershed

Ich setze alle Pixel mit einem Wert größer als 1 bis 255 (das Auto) und den Rest (Hintergrund) auf Null. Dann erweiterte ich das erhaltene Bild mit einem 3x3-Kernel, um zu vermeiden, dass Informationen über den Umriss des Autos verloren gingen. Schließlich habe ich das erweiterte Bild als Maske für das Originalbild verwendet und dabei die Funktion cv2.bitwise_and () verwendet. Das Ergebnis liegt im folgenden Bild:  final cropped image

Hier ist mein Code:

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()

Wenn Sie viele Bilder haben, müssen Sie wahrscheinlich ein Werkzeug erstellen, um die Markierungen grafisch zu kommentieren, oder sogar einen Algorithmus, um die Markierungen automatisch zu finden.

12
TasosGlrs

Das Problem ist, dass Sie Arrays von unsigned 8-Bit-Ganzzahlen subtrahieren. Dieser Vorgang kann überlaufen.

Demonstrieren

>>> 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)

Da Sie OpenCV verwenden, ist der einfachste Weg, Ihr Ziel zu erreichen, die Verwendung von cv2.absdiff() .

>>> cv2.absdiff(a,b)
array([[1, 1]], dtype=uint8)
4
Dan Mašek

Ich empfehle den Grabcut-Algorithmus von OpenCV. Sie zeichnen zunächst einige Linien auf Vorder- und Hintergrund und machen dies so lange, bis Ihr Vordergrund ausreichend vom Hintergrund getrennt ist. Es wird hier behandelt: https://docs.opencv.org/trunk/d8/d83/tutorial_py_grabcut.html Sowie in diesem Video: https://www.youtube.com/ watch? v = kAwxLTDDAwU

0
wordsforthewise