Skip to content

Bild binarisieren mit Python, NumPy, OpenCV

Python

In diesem Artikel WIRD beschrieben, wie SIE ein Bild mit Einem Kennzeichen in Schwarzweiß binarisieren.

Es gibt zwei Möglichkeiten: Die eine besteht darin, die OpenCV-Funktion cv2.threshold() zu verwenden, und die andere besteht darin, ndarray mit einer einfachen Operation von NumPy zu verarbeiten. OpenCV ist im letzten Fall nicht erforderlich.

  • Bildbinarisierung mit OpenCV:cv2.threshold()
  • Automatische Bildschwellenwertbildung (Otsu-Methode usw.)
  • Bildbinarisierung mit NumPy (ohne OpenCV)
    • Für Graustufenbilder
    • Für Farbbilder

Im folgenden Beispielcode ist die OpenCV-Version 4.2. Beachten SIE, dass das Verhalten bei verschiedenen Versionen unterschiedlich sein kann. Sie können die offizielle Dokumentation jeder Version unter den folgenden erhalten.

Bildbinarisierung mit OpenCV:cv2.threshold()

Nehmen Sie das folgende Bild als Beispiel.

import cv2

im = cv2.imread('data/src/lena_square_half.png')

Lena

Sie können ein Bild mit cv2.threshold() binarisieren.

If type auf cv2.THRESH_BINARY gesetzt IST, WIRD JEDER WERT, DER GRÖSSER ALS DER SCHWARZ THRESH IST, DURCH MAXVAL ERSETZT UND DIE ANDEREN WERTE WERDEN DURCH 0.

Bei Farbbildern wird jede Farbe (Kanal) separat verarbeitet. Wenn Sie Schwarz-Weiß-Bilder möchten, konvertieren Sie sie zuerst in Graustufen, wie im später beschriebenen Beispiel von cv2.THRESH_OTSU.

th, im_th = cv2.threshold(im, 128, 255, cv2.THRESH_BINARY)

print(th)
# 128.0

cv2.imwrite('data/dst/opencv_th.jpg', im_th)

lena OpenCV-Schwellenwert THRESH_BINARY

Ein Tupel aus verwendetem zugelassenem und verarbeitetem Array (Ausgabebild) wird zurückgegeben. Es kann wie im Beispiel Beispiel in jeder Variable gespeichert werden.

If type auf cv2.THRESH_TOZERO gesetzt IST, bleibt der Wert größer als der Schwellenwert thresh gleich und die anderen Werte werden durch 0 ersetzt.

th, im_th_tz = cv2.threshold(im, 128, 255, cv2.THRESH_TOZERO)

print(th)
# 128.0

cv2.imwrite('data/dst/opencv_th_tz.jpg', im_th_tz)

lena OpenCV-Schwellenwert THRESH_TOZERO

In der offiziellen Dokumentation unten FINDEN SIE DIE WERTE, DIE SIE FÜR TYP ANGEBEN KÖNNEN.

maxval wird nicht mit cv2.THRESH_TOZERO verwendet, und thresh wird nicht mit cv2.THRESH_OTSU und cv2.THRESH_TRIANGLE verwendet, die später beschrieben werden, aber sie können nicht weggelassen werden.

Automatische Bildschwellenwertbildung (Otsu-Methode usw.)

Wenn der Typ auf cv2.THRESH_OTSU eingestellt IST, WIRD der Schwellenwert automatisch durch die Methode von Otsu ausgewählt, und wenn er auf cv2.THRESH_TRIANGLE eingestellt IST, WIRD der Schwellenwert automatisch durch die Dreiecksmethode ausgewählt.

Beachten Sie, dass cv2.THRESH_OTSU und cv2.THRESH_TRIANGLE ab Version 4.2.0 nur 8-Bit-Single-Channel-Images unterstützen. Wenn ein Farbbild (dreidimensionales Array) angegeben WIRD, tritt ein Fehler auf.

# th, im_th_otsu = cv2.threshold(im, 128, 192, cv2.THRESH_OTSU)
# error: OpenCV(4.2.0) /tmp/opencv-20200105-17262-cwpzm4/opencv-4.2.0/modules/imgproc/src/thresh.cpp:1529: error: (-215:Assertion failed) src.type() == CV_8UC1 in function 'threshold'

Konvertieren Sie in Graustufen und verwenden Sie dann cv2.threshold().

im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)

th, im_gray_th_otsu = cv2.threshold(im_gray, 128, 192, cv2.THRESH_OTSU)

print(th)
# 117.0

cv2.imwrite('data/dst/opencv_th_otsu.jpg', im_gray_th_otsu)

lena OpenCV-Schwellenwert THRESH_OTSU

Werte, die größer als der automatisch ausgewählte sind, werden durch maxval ersetzt, und andere Werte werden durch 0 ersetzt. Im Beispiel wird maxval zur Erläuterung auf 192 gesetzt. Wenn Sie in Schwarzweiß binarisieren möchten, können Sie es auf 255 setzen.

Bildbinarisierung mit NumPy (ohne OpenCV)

Wenn Sie nur mit zulässigen in Schwarzweiß binarisieren möchten, können Sie dies mit einfachen NumPy-Operationen tun.

Für Graustufenbilder

Als einfaches Beispiel binarisieren Sie ein Graustufenbild.

import numpy as np
from PIL import Image

im_gray = np.array(Image.open('data/src/lena_square_half.png').convert('L'))
print(type(im_gray))
# 

Hier als Beispiel ohne OpenCV wird das Bild von Pillow gelesen und nach ndarray konvertiert.

Natürlich ist das Lesen von Bildern mit OpenCV kein Problem. Beachten Sie, dass die Reihenfolge der Farben beim Lesen eines Farbbildes mit OpenCV anders ist.

lena grau

Verwenden des Vergleichsoperators für ein NumPy-Array ndarray is ein boolesches ndarray zurück, das each Element des Arrays vergleicht.

thresh = 128

im_bool = im_gray > thresh
print(im_bool)
# [[ True  True  True ...  True  True False]
#  [ True  True  True ...  True  True False]
#  [ True  True  True ...  True False False]
#  ...
#  [False False False ... False False False]
#  [False False False ... False False False]
#  [False False False ... False False False]]

Da True als 1 und False als 0 angesehen WIRD, WIRD True zu 255 (weiß) und False zu 0 (schwarz), wenn es mit 255 multipliziert wird, was der maximale Wert von uint8 ist.

maxval = 255

im_bin = (im_gray > thresh) * maxval
print(im_bin)
# [[255 255 255 ... 255 255   0]
#  [255 255 255 ... 255 255   0]
#  [255 255 255 ... 255   0   0]
#  ...
#  [  0   0   0 ...   0   0   0]
#  [  0   0   0 ...   0   0   0]
#  [  0   0   0 ...   0   0   0]]

Image.fromarray(np.uint8(im_bin)).save('data/dst/numpy_binarization.png')

lena NumPy-Binarisierung THRESH_BINARY

Das obige Beispiel entspricht cv2.threshold() mit cv2.THRESH_BINARY.

Multipliziert man das boolesche ndarray des Vergleichsergebnisses mit dem ursprünglichen ndarray, bleibt der Pixelwert von True original und der Pixelwert von False ist 0 (schwarz), was cv2.THRESH_TOZERO entspricht.

im_bin_keep = (im_gray > thresh) * im_gray
print(im_bin_keep)
# [[162 161 156 ... 169 169   0]
#  [162 161 156 ... 169 169   0]
#  [164 155 159 ... 145   0   0]
#  ...
#  [  0   0   0 ...   0   0   0]
#  [  0   0   0 ...   0   0   0]
#  [  0   0   0 ...   0   0   0]]

Image.fromarray(np.uint8(im_bin_keep)).save('data/dst/numpy_binarization_keep.png')

lena NumPy-Binärisierung THRESH_TOZERO

Für Farbbilder

Indem Sie jeder RGB-Farbe unterschiedliche Werte zuweisen, können Sie ein buntes Bild erstellen.

Generieren Sie mit np.empty() ein dreidimensionales leeres ndarray und speichern Sie sterben Ergebnisse der Multiplikation jeder Farbe (jedes Kanals) mit jedem Wert.

Die durch shape erhaltene Größe (Höhe, Breite) wird von * entpackt und in np.empty() angegeben.

im_bool = im_gray > 128
im_dst = np.empty((*im_gray.shape, 3))
r, g, b = 255, 128, 32

im_dst[:, :, 0] = im_bool * r
im_dst[:, :, 1] = im_bool * g
im_dst[:, :, 2] = im_bool * b

Image.fromarray(np.uint8(im_dst)).save('data/dst/numpy_binarization_color.png')

lena NumPy Binärisierungsfarbe

Es ist auch möglich, den Negationsoperator ~ auf den booleschen Wert ndarray sofort.

im_bool = im_gray > 128
im_dst = np.empty((*im_gray.shape, 3))
r, g, b = 128, 160, 192

im_dst[:, :, 0] = im_bool * r
im_dst[:, :, 1] = ~im_bool * g
im_dst[:, :, 2] = im_bool * b

Image.fromarray(np.uint8(im_dst)).save('data/dst/numpy_binarization_color2.png')

lena NumPy Binarisierung color2

Beachten Sie, dass beim Speichern eines Bildes mit der OpenCV-Funktion cv2.imwrite() die Farbsequenz auf BGR eingestellt werden muss.

Bisher wurde es basierend auf dem Graustufenbild verarbeitet, aber es ist auch möglich, das Farbbild wie cv2.threshold() mit der gleichen Idee wie im Beispiel zu verarbeiten.

Generieren Sie ein leeres ndarray und speichern Sie jedes Ergebnis in jeder Farbe (jedem Kanal). Da das Original ein Farbbild (dreidimensionales Array) ist, WIRD np.empty_like() verwendet.

im = np.array(Image.open('data/src/lena_square_half.png'))

im_th = np.empty_like(im)

thresh = 128
maxval = 255

for i in range(3):
    im_th[:, :, i] = (im[:, :, i] > thresh) * maxval

Image.fromarray(np.uint8(im_th)).save('data/dst/numpy_binarization_from_color.png')

lena NumPy Binarisierung von Farbe

Eine flexiblere Verarbeitung als cv2.threshold() ist möglich, z. B. das Ändern des Grenzwerts oder das Ändern des Ersatzwerts für jede Farbe. Sie können ordentlich schreiben, indem Sie eine Liste (oder ein Tupel) und zip() verwenden.

l_thresh = [64, 128, 192]
l_maxval = [64, 128, 192]

for i, thresh, maxval in zip(range(3), l_thresh, l_maxval):
    im_th[:, :, i] = (im[:, :, i] > thresh) * maxval

Image.fromarray(np.uint8(im_th)).save('data/dst/numpy_binarization_from_color2.png')

lena NumPy-Binärisierung von color2