Skip to content

Binariser une image avec Python, NumPy, OpenCV

Cet article décrit comment binariser une image en noir et blanc avec un seuil.

Il y a deux façons : l’une consiste à utiliser la fonction OpenCV cv2.threshold(), et l’autre consiste à traiter ndarray avec une opération de base de NumPy. OpenCV n’est pas nécessaire dans ce dernier cas.

  • Binarisation d’images avec OpenCV :cv2.threshold()
  • Seuillage automatique des images (méthode d’Otsu, etc.)
  • Binarisation d’images avec NumPy (sans OpenCV)
    • Pour une image en niveaux de gris
    • Pour l’image couleur

Dans l’exemple de code suivant, la version d’OpenCV est 4.2. Notez que le comportement peut être différent avec différentes versions. Vous pouvez obtenir la documentation officielle de chaque version à l’adresse suivante.

Binarisation d’images avec OpenCV :cv2.threshold()

Prenez l’image suivante comme exemple.

import cv2

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

avec

Vous pouvez binariser une image avec cv2.threshold().

Si type est défini sur cv2.THRESH_BINARY, toute valeur supérieure au seuil seuil est remplacée par maxval et les autres valeurs sont remplacées par 0.

Dans le cas d’images couleur, chaque couleur (canal) est traitée séparément. Si vous voulez des images en noir et blanc, convertissez-les d’abord en niveaux de gris comme dans l’exemple de cv2.THRESH_OTSU décrit plus loin.

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 seuil THRESH_BINARY

Un tuple du seuil utilisé et du tableau traité (image de sortie) est renvoyé. Il peut être stocké dans chaque variable comme dans l’exemple ci-dessus.

Lorsque type est défini sur cv2.THRESH_TOZERO, la valeur supérieure au seuil seuil reste la même et les autres valeurs sont remplacées par 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)

lena OpenCV seuil THRESH_TOZERO

Consultez la documentation officielle ci-dessous pour les valeurs que vous pouvez spécifier pour le type.

maxval n’est pas utilisé avec cv2.THRESH_TOZERO, et thresh n’est pas utilisé avec cv2.THRESH_OTSU et cv2.THRESH_TRIANGLE décrits plus loin, mais ils ne peuvent pas être omis.

Seuillage automatique des images (méthode d’Otsu, etc.)

Lorsque le type est défini sur cv2.THRESH_OTSU, le seuil est automatiquement sélectionné par la méthode d’Otsu, et s’il est défini sur cv2.THRESH_TRIANGLE, le seuil est automatiquement sélectionné par la méthode du triangle.

Notez que cv2.THRESH_OTSU et cv2.THRESH_TRIANGLE ne prennent en charge que les images monocanal 8 bits à partir de la version 4.2.0. Une erreur se produit si une image couleur (tableau tridimensionnel) est spécifiée.

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

Convertissez en niveaux de gris, puis utilisez 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 seuil THRESH_OTSU

Les valeurs supérieures au seuil sélectionné automatiquement sont remplacées par maxval et les autres valeurs sont remplacées par 0. Dans l’exemple ci-dessus, maxval est défini sur 192 pour explication. Si vous souhaitez binariser en noir et blanc, vous pouvez le régler sur 255.

Binarisation d’images avec NumPy (sans OpenCV)

Si vous souhaitez simplement binariser en noir et blanc avec des valeurs de seuil, vous pouvez le faire avec les opérations NumPy de base.

Pour une image en niveaux de gris

Par exemple, binarisez une image en niveaux de gris.

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

Ici, à titre d’exemple sans OpenCV, l’image est lue par Pillow et convertie en ndarray.

Bien sûr, il n’y a aucun problème à lire les images avec OpenCV. Notez que l’ordre des couleurs est différent lors de la lecture d’une image couleur avec OpenCV.

lena gris

L’utilisation de l’opérateur de comparaison sur un tableau NumPy ndarray renvoie un ndarray booléen comparant chaque élément du tableau.

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]]

Puisque True est considéré comme 1 et False est considéré comme 0, lorsqu’il est multiplié par 255 qui est la valeur Max de uint8, True devient 255 (blanc) et False devient 0 (noir).

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 binarisation NumPy THRESH_BINARY

L’exemple ci-dessus correspond à cv2.threshold() avec cv2.THRESH_BINARY.

Si vous multipliez le ndarray booléen du résultat de la comparaison par le ndarray d’origine, la valeur de pixel de True reste originale et la valeur de pixel de False est 0 (noir), ce qui correspond à 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')

binarisation lena NumPy THRESH_TOZERO

Pour l’image couleur

En appliquant différentes valeurs à chaque couleur RVB, vous pouvez créer une image colorée.

Générez un ndarray tridimensionnel vide avec np.empty() et stockez les résultats de la multiplication de chaque couleur (chaque canal) par chaque valeur.

La taille (hauteur, largeur) obtenue par shape est décompressée par * et spécifiée dans 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')

couleur de binarisation lena NumPy

Il est également possible d’appliquer l’opérateur de négation ~ au booléen 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')

lena NumPy binarisation color2

Notez que lors de l’enregistrement d’une image avec la fonction OpenCV cv2.imwrite(), il est nécessaire de définir la séquence de couleurs sur BGR.

Jusqu’à présent, il a été traité sur la base de l’image en niveaux de gris, mais il est également possible de traiter l’image couleur comme cv2.threshold() avec la même idée que l’exemple ci-dessus.

Générez un ndarray vide et stockez chaque résultat dans chaque couleur (chaque canal). Puisque l’original est une image couleur (tableau tridimensionnel), np.empty_like() est utilisé.

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 binarisation NumPy à partir de la couleur

Un traitement plus flexible que cv2.threshold() est possible, comme changer la valeur de seuil ou changer la valeur de remplacement pour chaque couleur. Vous pouvez écrire proprement en utilisant une liste (ou tuple) et 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')

lena binarisation NumPy de color2