
Questo articolo descrive come binarizzare un’immagine in bianco e nero con una soglia.
Ci sono due modi: uno è usare la funzione OpenCV cv2.threshold() e l’altro è elaborare ndarray con un’operazione di base di NumPy. OpenCV non è necessario in quest’ultimo caso.
- Binarizzazione delle immagini con OpenCV:
cv2.threshold()
- Soglia automatica dell’immagine (metodo di Otsu, ecc.)
- Binarizzazione delle immagini con NumPy (senza OpenCV)
- Per immagini in scala di grigi
- Per immagine a colori
Nel codice di esempio seguente, la versione di OpenCV è 4.2. Si noti che il comportamento potrebbe essere diverso con versioni diverse. È possibile ottenere la documentazione ufficiale di ciascuna versione al seguente.
Binarizzazione delle immagini con OpenCV:cv2.threshold()
Prendi l’immagine seguente come esempio.
import cv2
im = cv2.imread('data/src/lena_square_half.png')
Puoi binarizzare un’immagine con cv2.threshold().
Se type è impostato su cv2.THRESH_BINARY, valore maggiore della soglia thresh viene sostituito con maxval e gli altri valori vengono sostituiti con 0.
Nel caso di immagini a colori, ogni colore (canale) viene elaborato separatamente. Se vuoi immagini in bianco e nero, convertile prima in scala di grigi come nell’esempio di cv2.THRESH_OTSU più avanti.
th, im_th = cv2.threshold(im, 128, 255, cv2.THRESH_BINARY)
print(th)
# 128.0
cv2.imwrite('data/dst/opencv_th.jpg', im_th)
Viene restituita una tupla di soglia utilizzata e matrice elaborata (immagine di output). Può essere memorizzato in ogni variabile come nell’esempio sopra.
Quando type è impostato su cv2.THRESH_TOZERO, il valore maggiore della soglia Thresh rimane lo stesso e gli altri valori vengono sostituiti con 0.
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)
Vedere la documentazione ufficiale di seguito per i valori che è possibile specificare per il tipo.
maxval non viene utilizzato con cv2.THRESH_TOZERO e thresh non viene utilizzato con cv2.THRESH_OTSU e cv2.THRESH_TRIANGLE descritti in seguito, ma non possono essere omessi.
Soglia automatica dell’immagine (metodo di Otsu, ecc.)
Quando type è impostato su cv2.THRESH_OTSU, la soglia viene selezionata automaticamente dal metodo di Otsu, e se è impostato su cv2.THRESH_TRIANGLE, la soglia viene selezionata automaticamente dal metodo del triangolo.
Si noti che cv2.THRESH_OTSU e cv2.THRESH_TRIANGLE supportano solo immagini a canale singolo a 8 bit a partire dalla versione 4.2.0. Si verificherà un errore se viene specificata un’immagine a colori (array tridimensionale).
# 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'
Converti in scala di grigi e quindi usa 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)
I valori maggiori della soglia selezionata vengono automaticamente sostituiti con maxval e gli altri valori vengono sostituiti con 0. Nell’esempio precedente, maxval è impostato su 192 per la spiegazione. Se vuoi eseguire la binarizzazione in bianco e nero, puoi impostarlo su 255.
Binarizzazione delle immagini con NumPy (senza OpenCV)
Se vuoi solo eseguire il binarizzazione in bianco e nero con valori di soglia, puoi farlo con le operazioni NumPy di base.
Per immagini in scala di grigi
Come semplice esempio, binarizza un’immagine in scala di grigi.
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))
#
Qui, ad esempio senza OpenCV, l’immagine viene letta da Pillow e convertita in ndarray.
Naturalmente, non ci sono problemi a leggere le immagini con OpenCV. Nota che l’ordine dei colori è diverso quando leggi un’immagine a colori con OpenCV.
Confrontare l’operatore di su un array NumPy ndarray confronta un ndarray booleano che confronta ogni elemento dell’array.
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]]
Poiché True è considerato 1 e False è considerato 0, moltiplicato per 255 che è il valore massimo di uint8, True diventa 255 (bianco) e False diventa 0 (nero).
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')
L’esempio precedente corrisponde a cv2.threshold() con cv2.THRESH_BINARY.
Se si moltiplica il valore booleano ndarray del risultato del confronto per l’originale ndarray, il valore in pixel di True rimane originale e il valore in pixel di False è 0 (nero), che corrisponde a cv2.THRESH_TOZERO.
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')
Per immagine a colori
Applicando valori diversi a ciascun colore RGB, puoi creare un’immagine colorata.
Genera un ndarray vuoto tridimensionale con np.empty() e memorizza i risultati della moltiplicazione di ogni colore (ogni canale) per ciascun valore.
La dimensione (altezza, larghezza) ottenuta da shape viene decompressa da * e specificata in np.empty().
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')
È anche possibile applicare l’operatore di negazione ~ al booleano ndarray.
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')
Si noti che quando si salva un’immagine con la funzione OpenCV cv2.imwrite(), è necessario impostare la sequenza di colori su BGR.
Finora, è elaborato in base all’immagine in scala di grigi, ma è anche possibile elaborare l’immagine a colori come cv2.threshold() con la stessa idea dell’esempio sopra.
Genera un ndarray vuoto e memorizza ogni risultato in ogni colore (ogni canale). Poiché l’originale è un’immagine a colori (array tridimensionale), viene utilizzato np.empty_like().
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')
È possibile un’elaborazione più flessibile rispetto a cv2.threshold(), come la modifica del valore di soglia o la modifica del valore di sostituzione per ciascun colore. Puoi scrivere in modo ordinato usando una lista (o una tupla) e zip().
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')