Indem das Bild als NumPy-Array ndarray gelesen wird, können mithilfe von NumPy-Funktionen verschiedene Bildverarbeitungen durchgeführt werden.
Durch die Bedienung von ndarray can SIE Pixelwerte abrufen und setzen (ändern), Bilder trimmen, Bilder verketten usw. Diejenigen, die mit NumPy vertraut sind, können verschiedene Bildverarbeitungen durchführen, ohne Bibliotheken wie OpenCV zu verwenden.
Auch bei Verwendung von OpenCV behandelt OpenCV für Python Bilddaten als ndarray, daher ist es hilfreich zu wissen, wie NumPy (ndarray) used WIRD. Zusätzlich zu OpenCV behandeln viele Bibliotheken wie scikit-image Bilder als ndarray.
Dieser Artikel hat folgenden Inhalt.
Bilder lesen und schreiben:
- So lesen Sie eine Bilddatei als NumPy-Array ndarray
- So speichern Sie das NumPy-Array ndarray als Bilddatei
Beispiele zur Bildverarbeitung mit NumPy (ndarray):
- Pixelwerte abrufen und festlegen (ändern).
- Generierung eines einfarbigen Bildes und Verkettung
- Negativ-Positiv-Umkehrung (Umkehrung des Pixelwerts)
- Farbreduktion
- Binarisierung
- Gamma-Korrektur
- Trimmen mit Scheibe
- Split mit Slice oder Funktion
- Paste mit Scheibe
- Alpha Blending und Maskierung
- Drehen und spiegeln
Beispielcodes zu diesem Artikel verwenden Kissen zum Lesen und Speichern von Bilddateien. Wenn Sie OpenCV verwenden möchten, lesen Sie den following Artikel.
Siehe auch den folgenden Artikel über Kissen. Einfache Vorgänge wie Lesen, Speichern, Ändern der Größe und Drehen von Bildern können allein von Pillow durchgeführt werden.
So lesen Sie eine Bilddatei als ndarray
Nehmen Sie das folgende Bild als Beispiel.
Das Übergeben der von PIL.Image.open() gelesenen Bilddaten an np.array() gibt 3D-ndarray zurück, dessen Form (Zeile (Höhe), Spalte (Breite), Farbe (Kanal)) ist.
from PIL import Image
import numpy as np
im = np.array(Image.open('data/src/lena.jpg'))
print(type(im))
#
print(im.dtype)
# uint8
print(im.shape)
# (225, 400, 3)
Die Reihenfolge der Farben (Kanäle) ist RGB (Rot, Grün, Blau). Beachten Sie, dass dies anders ist als beim Lesen mit cv2.imread() von OpenCV.
Wenn SIE das Bild mit convert(‚L‘) in Graustufen konvertieren und es dann an np.array() übergeben, gibt es 2D ndarray zurück, dessen Form (Zeile (Höhe), Spalte (Breite)) ist.
im_gray = np.array(Image.open('data/src/lena.jpg').convert('L'))
print(im_gray.shape)
# (225, 400)
Sie können ndarray auch von PIL.Image mit np.asarray() erhalten. np.array() gibt ein überschreibbares ndarray zurück, während np.asarray() ein nicht überschreibbares ndarray zurückgibt.
Für np.array() können Sie den Wert des Elements (Pixel) ändern.
print(im.flags.writeable)
# True
print(im[0, 0, 0])
# 109
im[0, 0, 0] = 0
print(im[0, 0, 0])
# 0
Für np.asarray() can SIE den Wert nicht ändern, da das Umschreiben verboten ist. Es ist möglich, basierend auf dem gelesenen Ndarray ein neues Ndarray zu erstellen.
im_as = np.asarray(Image.open('data/src/lena.jpg'))
print(type(im_as))
#
print(im_as.flags.writeable)
# False
# im_as[0, 0, 0] = 0
# ValueError: assignment destination is read-only
Der Datentyp dtype des gelesenen ndarray ist uint8 (8-Bit unsigned Integer).
If SIE es als Fließkommazahl Float verarbeiten möchten, can SIE es mit astype() konvertieren oder den Datentyp im zweiten Argument von np.array() und np.asarray() angeben.
im_f = im.astype(np.float64)
print(im_f.dtype)
# float64
im_f = np.array(Image.open('data/src/lena.jpg'), np.float64)
print(im_f.dtype)
# float64
Weitere Informationen zum Datentyp dtype in NumPy finden Sie im following Artikel.
So speichern Sie das NumPy-Array ndarray als Bilddatei
Die Übergabe von ndarray an Image.fromarray() gibt PIL.Image zurück. Es kann mit der Methode save() als Bilddatei gespeichert werden. Das Format der thermischen Datei WIRD automatisch aus der Erweiterung des Pfades ermittelt, der im Argument von save() übergeben WIRD.
pil_img = Image.fromarray(im)
print(pil_img.mode)
# RGB
pil_img.save('data/temp/lena_save_pillow.jpg')
An Image.fromarray() kann auch ein Graustufenbild (2D-Array) übergeben werden. Modus wird automatisch zu ‚L‘ (Graustufen). Es kann mit save() gespeichert werden.
pil_img_gray = Image.fromarray(im_gray)
print(pil_img_gray.mode)
# L
pil_img_gray.save('data/temp/lena_save_pillow_gray.jpg')
Wenn Sie es nur speichern möchten, können Sie es in einer Zeile schreiben.
Image.fromarray(im).save('data/temp/lena_save_pillow.jpg')
Image.fromarray(im_gray).save('data/temp/lena_save_pillow_gray.jpg')
Wenn der Datentyp dtype von ndarray Float usw. IST, tritt ein Fehler auf, sodass eine Konvertierung in uint8 erforderlich ist.
# pil_img = Image.fromarray(im_f)
# TypeError: Cannot handle this data type
pil_img = Image.fromarray(im_f.astype(np.uint8))
pil_img.save('data/temp/lena_save_pillow.jpg')
Beachten Sie, dass, wenn der Pixelwert durch 0,0 bis 1,0 dargestellt wird, mit 255 multipliziert und in uint8 konvertiert und gespeichert werden müssen.
Mit save() can Parameter entsprechend dem Format als Argumente übergeben werden. Nichts finden Sie unter Bilddateiformat .
Bei JPG können Sie beispielsweise die Qualität des Bildes an das Argument quality übergeben. Er reicht von 1 (am niedrigsten) bis 95 (am höchsten) und ist standardmäßig auf 75 eingestellt.
Pixelwerte abrufen und festlegen (ändern).
Sie können den Wert eines Pixels erhalten, indem Sie die Koordinaten am Index [Zeile, Spalten] von ndarray angeben. Beachten Sie, dass die Reihenfolge y, x in xy-Koordinaten ist. Der Ursprung ist oben Links.
from PIL import Image
import numpy as np
im = np.array(Image.open('data/src/lena.jpg'))
print(im.shape)
# (225, 400, 3)
print(im[100, 150])
# [111 81 109]
print(type(im[100, 150]))
#
Das obige Beispiel zeigt den Wert bei (y, x) = (100, 150), dh die 100. Zeile und 150. Spalte von Pixeln. Wie oben erwähnt, sind die Farben des mit Kissen erhaltenen Ndarray in RGB-Reihenfolge, sodass das Ergebnis (R, G, B) = (111, 81, 109) ist.
Sie können sie auch durch Entpacken separater Variablen zuweisen.
R, G, B = im[100, 150]
print(R)
# 111
print(G)
# 81
print(B)
# 109
Es ist auch möglich, den Wert durch Angabe der Farbe zu erhalten.
print(im[100, 150, 0])
# 111
print(im[100, 150, 1])
# 81
print(im[100, 150, 2])
# 109
Sie können auch zu einem neuen Wert wechseln. Sie können RGB auf einmal oder mit nur einer einzigen Farbe ändern.
im[100, 150] = (0, 50, 100)
print(im[100, 150])
# [ 0 50 100]
im[100, 150, 0] = 150
print(im[100, 150])
# [150 50 100]
Generierung eines einfarbigen Bildes und Verkettung
Generieren Sie einfarbige Bilder, indem Sie andere Farbwerte auf 0 setzen, und verketten Sie sie horizontal mit np.concatenate(). Sie können Bilder auch mit np.hstack() oder np.c_[] verketten
from PIL import Image
import numpy as np
im = np.array(Image.open('data/src/lena_square.png'))
im_R = im.copy()
im_R[:, :, (1, 2)] = 0
im_G = im.copy()
im_G[:, :, (0, 2)] = 0
im_B = im.copy()
im_B[:, :, (0, 1)] = 0
im_RGB = np.concatenate((im_R, im_G, im_B), axis=1)
# im_RGB = np.hstack((im_R, im_G, im_B))
# im_RGB = np.c_['1', im_R, im_G, im_B]
pil_img = Image.fromarray(im_RGB)
pil_img.save('data/dst/lena_numpy_split_color.jpg')
Negativ-Positiv-Inversion (Pixelwert invertieren)
Es ist auch einfach, Pixelwerte zu berechnen und zu manipulieren.
Ein Negativ-Positiv-Umkehrbild kann durch Subtrahieren des Pixelwerts vom Maximalwert (255 für uint8) erzeugt werden.
import numpy as np
from PIL import Image
im = np.array(Image.open('data/src/lena_square.png').resize((256, 256)))
im_i = 255 - im
Image.fromarray(im_i).save('data/dst/lena_numpy_inverse.jpg')
Da die Originalgröße zu groß ist, WIRD SIE der Einfachheit halber mit resize() angepasst. Gleiches gilt für die folgenden Beispiele.
Farbreduktion
Schneiden Sie den Rest der Teilung mit // ab und multiplizieren Sie erneut, die Pixelwerte werden diskret, und die Anzahl der Farben kann reduziert werden.
import numpy as np
from PIL import Image
im = np.array(Image.open('data/src/lena_square.png').resize((256, 256)))
im_32 = im // 32 * 32
im_128 = im // 128 * 128
im_dec = np.concatenate((im, im_32, im_128), axis=1)
Image.fromarray(im_dec).save('data/dst/lena_numpy_dec_color.png')
Binarisierung
Es ist auch möglich, dem entsprechenden Schwarz und Weiß zuzuweisen.
Nichts finden Sie in den folgenden Artikeln.
Gamma-Korrektur
Sie können mit Pixelwerten alles machen, was Sie wollen, wie Multiplikation, Division, Potenzierung usw.
Sie müssen die for-Schleife nicht verwenden, da das gesamte Bild so berechnet werden kann, wie es ist.
from PIL import Image
import numpy as np
im = np.array(Image.open('data/src/lena_square.png'))
im_1_22 = 255.0 * (im / 255.0)**(1 / 2.2)
im_22 = 255.0 * (im / 255.0)**2.2
im_gamma = np.concatenate((im_1_22, im, im_22), axis=1)
pil_img = Image.fromarray(np.uint8(im_gamma))
pil_img.save('data/dst/lena_numpy_gamma.jpg')
Als Ergebnis der Berechnung WIRD der Datentyp dtype von numpy.ndarray in die Gleitkommazahl float umgewandelt. Beachten Sie, dass SIE es beim Speichern in uint8 konvertieren müssen.
Trimmen mit Scheibe
Indem Sie einen Bereich mit Slice angeben, können Sie ihn auf ein Rechteck trimmen.
from PIL import Image
import numpy as np
im = np.array(Image.open('data/src/lena_square.png'))
print(im.shape)
# (512, 512, 3)
im_trim1 = im[128:384, 128:384]
print(im_trim1.shape)
# (256, 256, 3)
Image.fromarray(im_trim1).save('data/dst/lena_numpy_trim.jpg')
Weitere Informationen zum Slicing für numpy.ndarray finden Sie im following Artikel.
Es kann praktisch sein, eine Funktion zu definieren, die sterben oberen linken Koordinaten und die Breite und Höhe des zu trimmenden Bereichs angibt.
def trim(array, x, y, width, height):
return array[y:y + height, x:x+width]
im_trim2 = trim(im, 128, 192, 256, 128)
print(im_trim2.shape)
# (128, 256, 3)
Image.fromarray(im_trim2).save('data/dst/lena_numpy_trim2.jpg')
Wenn SIE außerhalb der Größe des Bildes angeben, wird es ignoriert.
im_trim3 = trim(im, 128, 192, 512, 128)
print(im_trim3.shape)
# (128, 384, 3)
Image.fromarray(im_trim3).save('data/dst/lena_numpy_trim3.jpg')
Split mit Slice oder Funktion
Sie können das Bild auch durch Slicen teilen.
from PIL import Image
import numpy as np
im = np.array(Image.open('data/src/lena_square.png').resize((256, 256)))
print(im.shape)
# (256, 256, 3)
im_0 = im[:, :100]
im_1 = im[:, 100:]
print(im_0.shape)
# (256, 100, 3)
print(im_1.shape)
# (256, 156, 3)
Image.fromarray(im_0).save('data/dst/lena_numpy_split_0.jpg')
Image.fromarray(im_1).save('data/dst/lena_numpy_split_1.jpg')
Es ist auch möglich, das Bild mit der NumPy-Funktion zu teilen.
np.hsplit() teilt ndarray horizontal. Wenn für das zweite Argument ein ganzzahliger Wert angegeben WIRD, WIRD Ndarray gleichmäßig aufgeteilt.
im_0, im_1 = np.hsplit(im, 2)
print(im_0.shape)
# (256, 128, 3)
print(im_1.shape)
# (256, 128, 3)
Wenn als zweites Argument eine Liste angegeben wird, wird ndarray an der Position dieses Werts geteilt.
im_0, im_1, im_2 = np.hsplit(im, [100, 150])
print(im_0.shape)
# (256, 100, 3)
print(im_1.shape)
# (256, 50, 3)
print(im_2.shape)
# (256, 106, 3)
np.vsplit() teilt ndarray vertikal. Die Verwendung von np.vsplit() ist die gleiche wie bei np.hsplit().
If ein ganzzahliger Wert als zweites Argument mit np.hsplit() oder np.vsplit() angegeben WIRD, WIRD EIN Fehler ausgelöst, WENN er nicht gleichmäßig aufgeteilt werden kann. np.array_split() passt die Größe entsprechend an und teilt sie auf.
# im_0, im_1, im_2 = np.hsplit(im, 3)
# ValueError: array split does not result in an equal division
im_0, im_1, im_2 = np.array_split(im, 3, axis=1)
print(im_0.shape)
# (256, 86, 3)
print(im_1.shape)
# (256, 85, 3)
print(im_2.shape)
# (256, 85, 3)
Paste mit Scheibe
Mit Slices kann ein Array-Rechteck durch ein anderes Array-Rechteck ersetzt werden.
Dadurch kann ein Teil des Bildes oder das gesamte Bild in ein anderes Bild eingefügt werden.
import numpy as np
from PIL import Image
src = np.array(Image.open('data/src/lena_square.png').resize((128, 128)))
dst = np.array(Image.open('data/src/lena_square.png').resize((256, 256))) // 4
dst_copy = dst.copy()
dst_copy[64:128, 128:192] = src[32:96, 32:96]
Image.fromarray(dst_copy).save('data/dst/lena_numpy_paste.jpg')
dst_copy = dst.copy()
dst_copy[64:192, 64:192] = src
Image.fromarray(dst_copy).save('data/dst/lena_numpy_paste_all.jpg')
Beachten Sie, dass ein Fehlerauftritt, wenn die Größe des auf der linken Seite angegebenen Bereichs von der Größe des auf der rechten Seite angegebenen Bereichs abweicht.
Alpha Blending und Maskierung
Durch die Operation für jedes Element (= Pixel) des Arrays können zwei Bilder basierend auf einem Maskenbild alpha-gemischt oder zusammengesetzt werden. Nichts finden Sie in den folgenden Artikeln.
Drehen und spiegeln
Es gibt auch Funktionen, die das Array drehen und nach oben, unten, links und rechts spiegeln.
Originalbild:
Gedrehtes Bild:
Gespiegeltes Bild: