HOWTO · Python
Bildmaskierung in OpenCV
In diesem Artikel werden wir sehen, wie man eine Bildmaskierung mit Bitwise AND in OpenCV Python durchführt.
Auf dieser Seite
Mit der Bildmaskierung behalten Sie einen Teil eines Bildes bei und kombinieren ihn mit einem anderen Bild. In OpenCV basiert die Maskierung auf der bitweisen AND-Operation: Die Funktion cv.bitwise_and() berechnet das elementweise bitweise UND zweier Arrays, und eine optionale Maske entscheidet, welche Pixel dieses Ergebnisses in der Ausgabe landen.
Dieser Artikel führt durch den üblichen Maskierungs-Workflow in Python. Anhand eines Autofotos und einer Logo-Datei bereiten wir eine Region of Interest (ROI) vor, erstellen aus dem Logo eine Binärmaske und ihre Umkehrung, wenden beide Masken an und fügen das Logo in die obere linke Ecke des Autobildes ein.
So wendet cv.bitwise_and() eine Maske an
cv.bitwise_and() ist eine Funktion auf Modulebene im Paket cv2, keine Methode eines Bildobjekts. Ihre Signatur in Python sieht so aus:
cv.bitwise_and(src1, src2[, dst[, mask]])
src1undsrc2sind die beiden Eingabearrays. Sie müssen dieselbe Größe und denselben Datentyp haben. Um ein einzelnes Bild zu maskieren, übergeben Sie für beide dasselbe Array.dstist ein optionales Ausgabearray derselben Größe und desselben Typs; wenn Sie es weglassen, reserviert die Funktion den Speicher und gibt das Ergebnis zurück.maskist ein optionales 8-Bit-Einkanal-Array in der Größe der Quellen. Wo die Maske einen Wert ungleich null enthält, ist das Ausgabepixelsrc1 & src2; wo sie0enthält, ist das Ausgabepixel0. Die Maske wählt also aus, welche Pixel der Operation im Ergebnis erscheinen – genau diese Auswahl ist die Grundidee der Bildmaskierung.
Führen Sie dieses Beispiel aus: Es ruft cv.bitwise_and() auf, wenn OpenCV installiert ist, und führt andernfalls dasselbe elementweise UND mit reinen Python-Listen aus. Der unveränderte Beispielcode gibt die berechneten Pixelwerte als Liste aus:
Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.
import importlib.util
if importlib.util.find_spec("cv2") is not None:
import cv2 as cv
import numpy as np
pixels = np.array([[200, 120, 60], [30, 90, 250]], dtype=np.uint8)
mask = np.array([[255, 255, 0], [0, 255, 255]], dtype=np.uint8)
result = cv.bitwise_and(pixels, pixels, mask=mask).tolist()
else:
pixels = [[200, 120, 60], [30, 90, 250]]
mask = [[255, 255, 0], [0, 255, 255]]
result = [[v & m for v, m in zip(pr, mr)] for pr, mr in zip(pixels, mask)]
print(result)
[[200, 120, 0], [0, 90, 250]]
Jedes Pixel unter einem Maskenwert von 255 bleibt erhalten; jedes Pixel unter einem Wert von 0 wird gelöscht. In einer echten Maske entstehen die Werte durch Schwellwertbildung statt von Hand – das ist der nächste Schritt.
Die Region of Interest des Bildes vorbereiten
Ziel ist es, das Logo in der oberen linken Ecke des Autobildes zu platzieren. Der Code liest daher beide Dateien und schneidet eine passende Region aus dem Autobild heraus:
car = cv.imread("car.jpg")undlogo = cv.imread("logo.png")laden die Bilder als BGR-Arrays.cv.imreadgibtNonezurück, wenn eine Datei nicht geladen werden kann; prüfen Sie daher beide Ergebnisse, bevor Sie fortfahren.logo.shapeliefert(rows, cols, channels); die ersten beiden Einträge geben die Abmessungen des Logos an.roi = car[0:rows, 0:cols]erzeugt eine NumPy-View der oberen linken Ecke des Autobildes, die genau der Logogröße entspricht. Da es sich um eine View handelt, aktualisiert das Zurückschreiben des zusammengesetzten Bildes incar[0:rows, 0:cols]das Originalbild.
Das Logo muss in das Autobild passen. Ist das Logo breiter oder höher als das Foto, verkleinert sich der Slice stillschweigend, und cv.bitwise_and(roi, roi, mask=mask_inv) schlägt später mit einem Größenfehler fehl, weil mask_inv zur Größe des Logos passt, nicht zur geschrumpften ROI. Skalieren Sie das Logo daher zuerst oder wählen Sie eine passende Platzierung. Beachten Sie außerdem: cv.imread lädt ein PNG standardmäßig als dreikanaliges BGR-Bild – der Alphakanal geht verloren –, sodass Logo und ROI am Ende dieselbe Kanalzahl haben.
Eine Binärmaske für das Logo erstellen
Aus dem Logo entstehen zwei Masken: mask, die die Pixel des Logos selbst auswählt, und mask_inv, die alles andere auswählt.
gray_logo = cv.cvtColor(logo, cv.COLOR_BGR2GRAY)wandelt das Logo in Graustufen um; mehr zur Farbkonvertierung finden Sie unter der Funktioncv.cvtColor()._, mask = cv.threshold(gray_logo, 10, 255, cv.THRESH_BINARY)erzeugt die Binärmaske: Pixel heller als10werden zu255, dunklere Pixel zu0. Bei einem Logo auf dunklem oder schwarzem Hintergrund isoliert dies die Form des Logos; passen Sie die10an Ihren eigenen Hintergrund an.mask_inv = cv.bitwise_not(mask)kippt jedes Bit und liefert die komplementäre Maske, die stattdessen den Hintergrundbereich auswählt.
Logo und Region of Interest kombinieren
Mit beiden Masken teilen zwei maskierte UND-Operationen die Arbeit auf: cv.bitwise_and(roi, roi, mask=mask_inv) behält den Autohintergrund und schwärzt die Logoform, während cv.bitwise_and(logo, logo, mask=mask) nur die Logopixel behält. cv.add() summiert dann beide Ergebnisse zum Composite, das zurück in das Autobild geschrieben wird.
Das vollständige Skript unten führt jeden Schritt aus. Wenn OpenCV oder die Bilddateien nicht vorhanden sind, gibt es eine kurze Meldung aus und endet ohne Traceback. Die im unveränderten Code verbleibenden englischen Meldungen weisen auf ein fehlendes Paket, fehlende Bilddateien oder ein zu großes Logo hin und bestätigen zuletzt das Schreiben von car-with-logo.png:
Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.
import importlib.util
if importlib.util.find_spec("cv2") is None:
print("This example needs OpenCV installed: pip install opencv-python.")
raise SystemExit
import cv2 as cv
car = cv.imread("car.jpg")
logo = cv.imread("logo.png")
if car is None or logo is None:
print("car.jpg and logo.png must sit next to this script.")
raise SystemExit
rows, cols = logo.shape[:2]
if rows > car.shape[0] or cols > car.shape[1]:
print("The logo is larger than the car image; resize it first.")
raise SystemExit
roi = car[0:rows, 0:cols]
gray_logo = cv.cvtColor(logo, cv.COLOR_BGR2GRAY)
_, mask = cv.threshold(gray_logo, 10, 255, cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)
background = cv.bitwise_and(roi, roi, mask=mask_inv)
foreground = cv.bitwise_and(logo, logo, mask=mask)
composite = cv.add(background, foreground)
car[0:rows, 0:cols] = composite
cv.imwrite("car-with-logo.png", car)
print("Wrote car-with-logo.png")
Das Skript speichert das fertige Bild mit cv.imwrite(), sodass es auch auf Rechnern ohne Bildschirm läuft; die Anzeigeaufrufe folgen im nächsten Abschnitt.
Das Maskierungsergebnis prüfen
Das zusammengesetzte Bild platziert das Logo in der oberen linken Ecke des Autobildes, während der Rest des Fotos unverändert bleibt:
Um zu sehen, was jede Stufe erzeugt hat, zeigen oder speichern Sie die Zwischenergebnisse – cv.imshow("mask", mask), cv.imshow("mask_inv", mask_inv), cv.imshow("background", background) und cv.imshow("foreground", foreground) gefolgt von cv.waitKey(0) zeigen sie auf dem Bildschirm, während cv.imwrite() sie auf Rechnern ohne Anzeige auf die Festplatte schreibt. cv.imshow() benötigt eine GUI-Anzeige; deshalb speichert das obige Skript stattdessen Dateien.
In mask sollten Sie die weiße Silhouette des Logos auf Schwarz sehen, und mask_inv ist ihr Spiegelbild. background zeigt die Autoecke mit ausgeschnittener Logoform, foreground nur das Logo. Das ist das gesamte Maskierungsmuster: einmal den Schwellwert bilden, cv.bitwise_and() zweimal anwenden, die Ergebnisse addieren – und die ROI trägt das zusammengefügte Bild.