Table des matières PDFPython

Traitement d'image : niveaux de gris

1. Introduction

Ce document est une introduction au traitement des images avec Python. Il explique comment lire une image dans un fichier, extraire les couches d'une image couleur, et faire des manipulations élémentaires sur les niveaux de gris. On verra enfin comment enregistrer une image dans un fichier.

Les modules utilisés sont Matplotlib et imageio, tous deux inclus dans les distributions pythonxy (python 2.7) et Pyzo (python 3.4).

2. Lecture d'une image

2.a. Codage d'une image en couleur

Les images fournies par les appareils photo sont généralement en couleur. Une image en couleur est contituée de trois couches : une couche rouge (R), une couche verte (V), une couche bleue (B). Nous n'allons pas ici expliquer comment sont représentées les couleurs. Voir à ce sujet : Espace des couleurs RVB et la simulation Espace des couleurs RVB et triangle de Maxwell.

Soit Nx le nombre de colonnes de l'image et Ny le nombre de lignes. Le nombre de pixels total est N=NxNy. Chaque couche est une matrice comportant Ny lignes et Nx colonnes. Le plus souvent, cette matrice contient des entiers codés sur 8 bits (les valeurs vont de 0 à 255). Pour l'image en couleur complète, il y a donc 24 bits par pixels, à multiplier par le nombre de pixels pour obtenir l'occupation totale en mémoire. Chaque couche peut être vue comme une image en niveaux de gris. Le niveau 0 est le noir, le niveau 255 est le blanc, le niveau 128 est un gris moyen.

../../../../figures/image/niveaux/images/couches.svgFigure pleine page

On voit sur la figure le pixel (5,4) dont les niveaux de gris des trois couches sont (100,20,200), ce qui donne une couleur violette. Par convention, on notera en premier l'indice qui repère les colonnes, conformément au repérage des coordonnées d'un point sur un plan (x,y). On remarque néanmoins que l'origine se trouve sur le coin supérieur gauche de l'image.

2.b. Lecture d'un fichier image

Voici l'image en couleur :

mer

Pour lire un fichier d'image (PNG, JPEG, etc), on utilise le module imageio :

import imageio
import numpy
from matplotlib.pyplot import *
img = imageio.imread("../../../../figures/image/niveaux/images/babouin.png")
                

Voici les dimensions du tableau et le type de données :

print(img.shape)
--> (256, 256, 3)
print(img.dtype)
--> dtype('uint8')

La valeur d'un pixel pour une couche est codée sur 8 bits : c'est un entier compris en 0 et 255.

La séparation des trois couches se fait par :

rouge = img[:,:,0]
vert = img[:,:,1]
bleu = img[:,:,2]
                

On se contentera ici de travailler sur la couche rouge, qui est une image en niveaux de gris. On peut voir la valeur d'un pixel particulier, celui d'indices (120,100) :

print(rouge[100,120])
--> 238

Remarquons que le premier indice d'accès au tableau indique la ligne.

2.c. Affichage d'une image

La fonction matplotlib.pyplot.imshow permet d'afficher un tableau sous forme d'une image :

figure(figsize=(4,4))
imshow(rouge,cmap='gray')
                
figAfigA.pdf

Le pixel de coordonnées (i,j) est l'élément rouge[j,i].

L'argument cmap='gray' permet de représenter les valeurs en niveaux de gris. La fonction matplotlib.pyplot.imshow ainsi utilisée convertit la plage de valeurs [min,max] du tableau en valeurs dans l'intervalle [0,255] pour l'affichage en image.

Il peut être intéressant de représenter l'image en niveaux de gris avec une échelle de couleurs, car nous identifions plus facilement une couleur qu'un niveau de gris :

figure(figsize=(5,5))
imshow(rouge)
colorbar()
                   
figCfigC.pdf

2.d. Extraction d'une partie d'une image

Une partie rectangulaire d'une image peut être extraite de la manière suivante, en faisant attention à indiquer les lignes sélectionnées en premier :

partie = rouge[0:50,50:150]
figure(figsize=(4,4))
imshow(partie,cmap='gray')
                
figDfigD.pdf

Une opération courante est la sélection d'une seule ligne (ou d'une seule colonne) :

ligne20 = rouge[20,:]
figure(figsize=(8,4))
plot(ligne20)
                
figEfigE.pdf

On obtient ainsi le profil de niveaux de gris sur cette ligne.

3. Étude statistique des niveaux de gris

Voici comment obtenir la moyenne des valeurs d'une image et l'écart-type :

print(rouge.mean())
--> 137.09773254394531
print(rouge.std())
--> 56.967793119151594

Pour obtenir plus d'informations, on peut calculer un histogramme. Pour chaque valeur entière comprise entre 0 et 255, on compte le nombre de pixels qui ont cette valeur.

def histogramme(image):
    h = numpy.zeros(256,dtype=numpy.uint32)
    s = image.shape
    for j in range(s[0]):
        for i in range(s[1]):
            valeur = image[j,i]
            h[valeur] += 1
    return h
    
h = histogramme(rouge)
figure(figsize=(8,6))
plot(h)
axis([0,255,0,h.max()])
xlabel("valeur")
ylabel("Nombre")
             
figFfigF.pdf

4. Modification des niveaux de gris

Voyons ce qu'il se passe si l'on divise les valeurs d'une image par 5 (division entière) :

im1 = rouge/5
figure(figsize=(4,4))
imshow(im1,cmap='gray',vmin=0,vmax=255)
            
figGfigG.pdf

On a précisé les valeurs minimale et maximale, pour éviter qu'une normalisation des valeurs soit effectuée. Comme attendu, l'image est beaucoup plus sombre.

Voici ce qu'il arrive si l'image est divisée par 50, puis multipliée par 50 pour retrouver l'image initiale :

im2 = rouge/50
im3 = im2*50
figure(figsize=(4,4))
imshow(im3,cmap='gray',vmin=0,vmax=255)
              
figHfigH.pdf

L'image finale ne comporte plus qu'un nombre restreint de niveaux de gris. C'est une conséquence de la division entière des valeurs. Voyons l'histogramme de cette image :

h = histogramme(im3)
figure(figsize=(6,5))
plot(h)
axis([0,255,0,h.max()])
xlabel("valeur")
ylabel("Nombre")
                
figIfigI.pdf

On voit ainsi que les calculs faits sur des entiers peuvent conduire à une perte d'information sur les niveaux de gris de l'image. Pour éviter cet inconvénient, il suffit de convertir l'image en tableau de flottants :

rouge = rouge*1.0
                  
print(rouge.dtype)
--> dtype('float64')

Les flottants sont codés sur 64 bits (flottants double précision), à comparer aux 8 bits de l'image initiale. Tous les traitements d'image (par exemple le filtrage) seront effectués avec des flottants, pour éviter les pertes d'informations dues aux divisions entières. Les valeurs seront converties en entiers au moment de l'enregistrement dans un fichier.

Les flottants obtenus sont dans l'intervalle [0,255]. Pour certains calculs, il est plus commode de travailler avec des flottants dans l'intervalle [0,1], ce qui s'obtient par :

rouge01 = rouge/255.0
                    

Cette convention de représentation des images est souvent utilisée, car elle donne un codage indépendant de la profondeur de l'image initiale (8 bits ou 16 bits).

Une opération courante de traitement d'image est le seuillage, qui consiste à repérer les pixels dont la valeur dépasse un certain seuil.

def seuillage(image,seuil):
    resultat = image.copy()
    s = image.shape
    for j in range(s[0]):
        for i in range(s[1]):
            if image[j,i] > seuil:
               resultat[j,i] = 255
            else:
                resultat[j,i] = 0
    return resultat
    
im4 = seuillage(rouge,150)
figure(figsize=(4,4))
imshow(im4,cmap='gray',vmin=0,vmax=255)
                     
figJfigJ.pdf

5. Enregistrement d'une image

L'image précédente peut être enregistrée avec la fonction imageio.imwrite :

imageio.imwrite("../../../../figures/image/niveaux/images/imA.png",rouge)
            

Voici l'image obtenue avec ce fichier :

mer

Il s'agit d'une image en couleur dont les trois couches sont identiques, ce qui donne des niveaux de gris.

On peut aussi reconstituer une image en couleur à partir de ses trois couches. Pour obtenir une image différente de l'image initiale, on réduit les valeurs de la couche rouge. On doit tout d'abord créer un tableau à trois dimensions et le remplir avec les couches.

s = rouge.shape
couleur = numpy.zeros((s[0],s[1],3),dtype=numpy.uint8)
couleur[:,:,0] = rouge*0.5
couleur[:,:,1] = vert
couleur[:,:,2] = bleu
imageio.imwrite("../../../../figures/image/niveaux/images/imB.png",couleur)

            
mer
Creative Commons LicenseTextes et figures sont mis à disposition sous contrat Creative Commons.