
Führt Alpha-Blending und Masking mit Python, OpenCV, NumPy durch.
Es kann nur mit NumPy ohne Verwendung von OpenCV realisiert werden. Da die Array-Operation von NumPy einfacher und flexibler ist, empfehle ich sie.
Dieser Artikel hat folgenden Inhalt.
- Alpha Blending mit OpenCV:
cv2.addWeighted()
- Maskieren mit OpenCV:
cv2.bitwise_and()
- Alpha-Blending mit NumPy
- Maskieren mit NumPy
- Komplexes Alpha-Blending und -Masking mit NumPy
- Maskenbilderstellung durch OpenCV-Zeichnung
Lesen Sie den following Artikel über Alpha-Blending und Maskierung mit Pillow (PIL).
Der Beispielcode verwendet das folgende Bild.
Die OpenCV-Version des Beispielcodes ist 4.0.1. OpenCV3 und 4 sollten sich nicht viel ändern, aber OpenCV2 kann anders sein, also seien Sie vorsichtig.
Alpha Blending mit OpenCV:cv2.addWeighted()
Verwenden Sie cv2.addWeighted(), um Alpha-Blending mit OpenCV durchzuführen.
dst = cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])
Sie wird nach Parametern wie folgt berechnet.
dst = src1 * alpha + src2 * beta + gamma
Die beiden Bilder müssen die gleiche Größe haben, also ändern Sie die Größe.
import cv2
src1 = cv2.imread('data/src/lena.jpg')
src2 = cv2.imread('data/src/rocket.jpg')
src2 = cv2.resize(src2, src1.shape[1::-1])
Lesen Sie den following Artikel, um die Größe des Bildes zu erhalten, das als NumPy-Array ndarray gelesen WIRD.
Das Bild wird gemäß dem zweiten Parameter Alpha und dem vierten Parameter Beta alpha-gemischt.
Obwohl Bilder hier als Dateien gespeichert werden, können Sie cv2.imshow() verwenden, wenn Sie sie in einem anderen Fenster anzeigen möchten (zB:cv2.imshow('window_name', dst)). The same is true for the following sample code.
dst = cv2.addWeighted(src1, 0.5, src2, 0.5, 0)
cv2.imwrite('data/dst/opencv_add_weighted.jpg', dst)
Der fünfte Parameter Gamma ist der Wert, der zu allen Pixelwerten addiert werden soll.
dst = cv2.addWeighted(src1, 0.5, src2, 0.2, 128)
cv2.imwrite('data/dst/opencv_add_weighted_gamma.jpg', dst)
Wie Sie aus dem obigen Ergebnis sehen können, läuft es nicht über, selbst wenn es den Maximalwert (255 für uint8) überschreitet, aber es wird darauf hingewiesen, dass einige Datentypen möglicherweise nicht richtig verarbeitet werden.
Verwenden Sie in einem solchen Fall die Methode clip() von ndarray. Siehe den Abschnitt über Alpha-Blending mit NumPy weiter unten.
Maskieren mit OpenCV:cv2.bitwise_and()
Verwenden Sie cv2.bitwise_and(), um mit OpenCV zu maskieren.
dst = cv2.bitwise_and(src1, src2[, dst[, mask]])
cv2.bitwise_and() ist eine Funktion, die bitweise UND-Verarbeitung durchführt, wie der Name schon sagt. Das UND der Werte für jedes Pixel der Eingangsbilder src1 und src2 ist der Pixelwert des Ausgangsbildes.
Hier WIRD ein Graustufenbild als Maskenbild für src2 verwendet.
src2 = cv2.imread('data/src/horse_r.png')
src2 = cv2.resize(src2, src1.shape[1::-1])
print(src2.shape)
# (225, 400, 3)
print(src2.dtype)
# uint8
dst = cv2.bitwise_and(src1, src2)
cv2.imwrite('data/dst/opencv_bitwise_and.jpg', dst)
Wenn die Bilddatei gelesen wird, ist der Datentyp uint8 (vorzeichenlose 8-Bit-Ganzzahl: 0-255), Schwarz zeigt den Pixelwert 0 an (0b00000000 in Binärform), Weiß den Pixelwert 255 (0b11111111 in Binärform) an.
Im Fall von uint8 IST das Ergebnis der Bit-Operation leicht zu verstehen, aber im Fall der Gleitkommazahl Float WIRD angemerkt, dass die Bit-Operation in binärer Notation ausgeführt WIRD und das Ergebnis unerwartet IST.
Es kann sein, die später beschriebene Maskenverarbeitung mit NumPy zu verstehen.
Zusätzlich zu cv2.bitwise_and() enthält OpenCV auch cv2.bitwise_or(), cv2.bitwise_xor() und cv2.bitwise_not() zum Ausführen von OR-, XOR- und NOT-Operationen.
Alpha-Blending mit NumPy
Da NumPy problemlos Rechenoperationen für jeden Pixel des Arrays durchführen kann, lässt sich Alpha-Blending auch mit einem einfachen Ausdruck realisieren.
Hier werden Bilddateien als NumPy-Array ndarray mit Pillow gelesen. Die Größenänderung erfolgt auch nach der Pillow-Methode.
Bilddateien werden mit cv2.imread() von OpenCV als ndarray gelesen, es spielt also keine Rolle, welches OpenCV oder Pillow used WIRD, aber SIE beachten, dass die Farbreihenfolge unterschiedlich IST.
Da die Operation von ndarray und Skalarwert die Operation des Werts jedes Elements und des Skalarwerts ist, kann Alpha-Mischung wie folgt berechnet werden. Seien Sie vorsichtig beim Speichern als Bilddatei mit Kissen, da der Datentyp automatisch umgewandelt wird.
import numpy as np
from PIL import Image
src1 = np.array(Image.open('data/src/lena.jpg'))
src2 = np.array(Image.open('data/src/rocket.jpg').resize(src1.shape[1::-1], Image.BILINEAR))
print(src1.dtype)
# uint8
dst = src1 * 0.5 + src2 * 0.5
print(dst.dtype)
# float64
Image.fromarray(dst.astype(np.uint8)).save('data/dst/numpy_image_alpha_blend.jpg')
Beachten Sie, dass SIE beim Speichern als jpg-Datei mit der save()-Methode von Pillow die Qualität mit dem Argument quality angeben can (es WIRD im Beispiel weggelassen, bleibt also die Standardeinstellung).
Es ist auch einfach, jedem Pixel einheitliche Werte hinzuzufügen, wie der Parameter Gamma in cv2.addWeighted() von OpenCV. Jeder Farbe können wie folgt unterschiedliche Werte hinzugefügt werden. Beachten Sie, wie oben erwähnt, dass die Farbreihenfolge davon abhängig ist, wie die Bilddatei gelesen WIRD.
Verwenden Sie clip(), um Pixelwerte auf den Bereich von 0 bis 255 zu beschneiden. Beachten Sie, dass es beim Speichern als Bilddatei zu unerwarteten Ergebnissen kommt, wenn ein Wert den Maximalwert 255 von uint8 überschreitet.
dst = src1 * 0.5 + src2 * 0.2 + (96, 128, 160)
print(dst.max())
# 311.1
dst = dst.clip(0, 255)
print(dst.max())
# 255.0
Image.fromarray(dst.astype(np.uint8)).save('data/dst/numpy_image_alpha_blend_gamma.jpg')
Maskieren mit NumPy
Das Maskieren ist mit den Array-Operationen von NumPy einfach.
Die arithmetischen Operationen von Arrays in derselben Form sind Operationen für jedes Pixel an derselben Position.
Das als uint8 gelesene Graustufenbild hat 0 für Schwarz und 255 für Weiß. Durch Dividieren durch 255 wird Schwarz zu 0,0 und Weiß zu 1,0, und durch Multiplizieren mit dem Originalbild bleibt nur der Weißanteil von 1,0 übrig, und die Maskenverarbeitung kann realisiert werden.
import numpy as np
from PIL import Image
src = np.array(Image.open('data/src/lena.jpg'))
mask = np.array(Image.open('data/src/horse_r.png').resize(src.shape[1::-1], Image.BILINEAR))
print(mask.dtype, mask.min(), mask.max())
# uint8 0 255
mask = mask / 255
print(mask.dtype, mask.min(), mask.max())
# float64 0.0 1.0
dst = src * mask
Image.fromarray(dst.astype(np.uint8)).save('data/dst/numpy_image_mask.jpg')
Wenn in diesem Beispiel dst = src * mask / 255 ist, wird src * mask zuerst als uint8 berechnet, und der Wert wird gerundet und dann durch 255 dividiert, was nicht das erwartete Ergebnis ist.
Es ist in Ordnung, wenn dst = src * (mask / 255) oder dst = mask / 255 * src.
Wenn SIE die Reihenfolge nicht berücksichtigen möchten, können Sie alle Arrays auf Float werfen und dann ausführen. Es können weniger Fehler auftreten.
Seien Sie vorsichtig, wenn das Maskenbild ein Graustufenbild und ein 2D-Darray (ohne Farbdimension) ist. Wenn die Multiplikation durchgeführt WIRD, tritt ein Fehler auf.
mask = np.array(Image.open('data/src/horse_r.png').convert('L').resize(src.shape[1::-1], Image.BILINEAR))
print(mask.shape)
# (225, 400)
mask = mask / 255
# dst = src * mask
# ValueError: operands could not be broadcast together with shapes (225,400,3) (225,400)
NumPy verfügt über einen Mechanismus namens Broadcast, der Operationen durchführt, indem er automatisch Arrays mit unterschiedlichen Dimensionen und Formen entsprechend umwandelt. Jedoch WIRD gemäß der Fehlermeldung sterben Übertragung durch sterben Kombination der oben genannten Beispiele nicht angemessen durchgeführt.
Es wird gut übertragen, wenn Sie einem 2D-Ndarray eine weitere Dimension hinzufügen.
mask = mask.reshape(*mask.shape, 1)
print(mask.shape)
# (225, 400, 1)
dst = src * mask
Image.fromarray(dst.astype(np.uint8)).save('data/dst/numpy_image_mask_l.jpg')
shape des ursprünglichen Arrays wird entpackt und an reshape() übergeben.
Eine andere Möglichkeit besteht darin, np.newaxis anstelle von reshape() zu verwenden.
# mask = mask[:, :, np.newaxis]
Komplexes Alpha-Blending und -Masking mit NumPy
Im Beispiel der Alpha-Mischung wurde das Bild in einem einheitlichen Verhältnis über die gesamte Oberfläche des Bildes zusammengesetzt, aber mit NumPy ist es möglich, basierend auf einem anderen Bild (Array) zusammenzusetzen.
Verwenden Sie das folgende Abstufungsbild. Gradationsbilder können mit NumPy generiert werden.
Es kann durch einen einfachen Vorgang zusammengesetzt werden. Es wird ein Bild erzeugt, in dem sich der Alphawert (Mischungsverhältnis) entsprechend dem Pixelwert des Gradationsbildes ändert.
import numpy as np
from PIL import Image
src1 = np.array(Image.open('data/src/lena.jpg'))
src2 = np.array(Image.open('data/src/rocket.jpg').resize(src1.shape[1::-1], Image.BILINEAR))
mask1 = np.array(Image.open('data/src/gradation_h.jpg').resize(src1.shape[1::-1], Image.BILINEAR))
mask1 = mask1 / 255
dst = src1 * mask1 + src2 * (1 - mask1)
Image.fromarray(dst.astype(np.uint8)).save('data/dst/numpy_image_ab_grad.jpg')
Es ist auch einfach, wenn Sie mit einem anderen Bild maskieren möchten.
mask2 = np.array(Image.open('data/src/horse_r.png').resize(src1.shape[1::-1], Image.BILINEAR))
mask2 = mask2 / 255
dst = (src1 * mask1 + src2 * (1 - mask1)) * mask2
Image.fromarray(dst.astype(np.uint8)).save('data/dst/numpy_image_ab_mask_grad.jpg')
Maskenbilderstellung durch OpenCV-Zeichnung
Geometrische Maskenbilder können mit der Zeichenfunktion von OpenCV erstellt werden.
Es ist möglich, ein Ndarray zu erzeugen, das sterben same Form wie das von np.zeros_like() zu verarbeitende Bild hat und in dem alle Elemente 0 sind. Es entspricht einem schwarzen Bild in der gleichen Größe wie das Originalbild.
import cv2
import numpy as np
src = cv2.imread('data/src/lena.jpg')
mask = np.zeros_like(src)
print(mask.shape)
# (225, 400, 3)
print(mask.dtype)
# uint8
Sie können die Größe auch mit np.zeros() angeben.
Zeichnen Sie hier Figuren mit der Zeichenfunktion von OpenCV. Ein Rechteck verwendet cv2.rectangle(), ein Kreis verwendet cv2.circle() und ein Polygon verwendet cv2.fillConvexPoly(). Rechtecke und Kreise werden gefüllt, wenn die Dicke = -1 ist.
cv2.rectangle(mask, (50, 50), (100, 200), (255, 255, 255), thickness=-1)
cv2.circle(mask, (200, 100), 50, (255, 255, 255), thickness=-1)
cv2.fillConvexPoly(mask, np.array([[330, 50], [300, 200], [360, 150]]), (255, 255, 255))
cv2.imwrite('data/dst/opencv_draw_mask.jpg', mask)
If eine Glättungs-(Unschärfe-)Verarbeitung unter Verwendung Einer Funktion wie etwa cv2.GaussianBlur() durchgeführt WIRD, WIRD sterben Grenze glatt, so dass es möglich ist, eine glatte Synthese durch Maskierung durchzuführen.
Geben Sie die Kernelgröße in x- und y-Richtung als Tupel im zweiten Parameter von cv2.GaußianBlur() an. Wenn jeder Wert erhöht wird, wird die Unschärfebreite in dieser Richtung erhöht. Der Wert muss ungerade sein. Der dritte Parameter gibt den Gaußschen Standardabweichungswert an. Wenn es 0 ist, wird es automatisch berechnet. Beachten Sie, dass es nicht weggelassen werden kann.
Informationen zu anderen Glättungsfunktionen finden Sie im folgenden offiziellen Dokument.
mask_blur = cv2.GaussianBlur(mask, (51, 51), 0)
cv2.imwrite('data/dst/opencv_draw_mask_blur.jpg', mask_blur)
dst = src * (mask_blur / 255)
cv2.imwrite('data/dst/opencv_draw_mask_blur_result.jpg', dst)
Beachten Sie, dass das Ergebnis nicht wie erwartet sein wird, wenn der Teil dst = src * (mask_blur / 255) dst = src * mask_blur / 255 ist. Siehe Abschnitt Maskieren mit NumPy.
Auch wenn das als Maske verwendete ndarray ein zweidimensionales Array ist (keine Farbdimension), kann es nicht berechnet werden, ohne eine weitere Dimension hinzuzufügen. Siehe auch Abschnitt Maskieren mit NumPy.