Skip to content

Bildverarbeitung mit Python, NumPy

Python

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.

Lena

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')
NumPy-Bildverarbeitung mit geteilter Farbe

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

Python NumPy invers

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

Python NumPy verringert die Farbe

Binarisierung

Es ist auch möglich, dem entsprechenden Schwarz und Weiß zuzuweisen.

Nichts finden Sie in den folgenden Artikeln.

Python NumPy OpenCV-Binärisierung

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')
NumPy-Bildverarbeitungs-Split-Gamma

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

numpy bild trimmen 1

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

Unschönes Bildtrimmen 2

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

Mühseliges Bildtrimmen 3

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

Numpy Image Split 0

Numpy Image Split 1

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

taube Bildpaste

dst_copy = dst.copy()
dst_copy[64:192, 64:192] = src

Image.fromarray(dst_copy).save('data/dst/lena_numpy_paste_all.jpg')

numpy image alles einfügen

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.

NumPy-Bild-Alpha-Mischungsabstufung

NumPy-Bildmischungsunschärfe

Drehen und spiegeln

Es gibt auch Funktionen, die das Array drehen und nach oben, unten, links und rechts spiegeln.

Originalbild:

Lena

Gedrehtes Bild:

numpy rot90-Bild

Gespiegeltes Bild:

nupmy flipud Bild