Manipuler des images

Zone d’évaluation de la feuille par l’enseignant.

Dans cette feuille, vous allez apprendre à effectuer quelques manipulations et traitements simples sur les images. Nous allons commencer par nous entrainer sur une image riche en couleurs (source: wikimedia).

../_images/apple1.png

Pour cela, nous la chargeons avec la bibliothèque PIL (Python Imaging Library) en précisant le nom du fichier la contenant, puis l’affectons à une variable img pour pouvoir la manipuler par la suite:

from PIL import Image
img = Image.open("apple.png")

Voici cette image:

img

Pour l’afficher avec des axes et – lorsque l’image a une basse résolution – repérer mieux les pixels individuels, on peut utiliser matplotlib:

import matplotlib.pyplot as plt
plt.imshow(img);

Pourquoi un ‘;’ à la fin de la commande précédente? Parce que plt.imshow ne renvoie pas une image, mais l’affiche par effet de bord, le ; évitant l’affichage de ce que renvoie vraiment plt.imshow.

Cette approche quelque peu datée est traditionnelle dans des systèmes comme Matlab. La bibliothèque matplotlib.pyplot l’a reproduit pour faciliter la migration d’utilisateurs de ces systèmes. Par habitude beaucoup d’exemples sur internet utilisent encore cette approche; cela peut rester pratique comme raccourci dans des exemples en une ligne comme ci-dessus.

Mais on sait depuis – et c’est ce que nous vous enseignons depuis le début de l’année – que l’on obtient du code beaucoup plus modulaire si l’on sépare proprement les traitements et calculs (par exemple construire une figure) des entrées et sorties (par exemple afficher la figure).

De ce fait, pour tout usage non trivial, il est préférable d’utiliser l’interface objet de matplotlib, comme dans l’exemple suivant:

from matplotlib.figure import Figure
fig = Figure()         # Construction d'une nouvelle figure
subplot = fig.add_subplot() # Ajout d'une zone de dessin (appelée «axes» dans matplotlib) à la figure
subplot.imshow(img)         # Ajout d'une image à la zone de dessin
fig                    # Affichage de la figure

Consultez la documentation de PIL Image sur internet, pour trouver comment obtenir la largeur et la hauteur de cette image. Stockez le résultat dans des variables width et height et vérifiez la cohérence avec la figure ci-dessus.

### BEGIN SOLUTION
width, height = img.size
### END SOLUTION
assert width == 256
assert height == 256

Images comme tableaux

On souhaite maintenant pouvoir accéder au contenu de l’image pour pouvoir calculer avec. Pour cela, nous allons convertir l’image en un tableau de nombres NumPy (voir le tutoriel de la semaine dernière sur ces derniers).

Voici le tableau associé à l’image:

import numpy as np
M = np.array(img)

En vous référant éventuellement au cours, combien de lignes, de colonnes et de couches devrait avoir ce tableau? Vérifier avec l’attribut shape:

### BEGIN SOLUTION
M.shape
### END SOLUTION

Pourquoi quatre couches? Rouge, Vert, Bleu, … et transparence!

Comprendre les couches de couleurs

Comme toujours, pour mieux comprendre des données, il faut les visualiser! Voici une figure représentant notre image et ses trois couches rouge, vert, bleu. Observez comment les couleurs de l’image de départ (blanc, vert, noir, rouge) se décomposent dans les différentes couches.

# Échelles de couleur (colormap) allant du noir à la couleur primaire correspondante
from matplotlib.colors import LinearSegmentedColormap
black_red_cmap   = LinearSegmentedColormap.from_list('black_red_cmap',   ["black", "red"])
black_green_cmap = LinearSegmentedColormap.from_list('black_green_cmap', ["black", "green"])
black_blue_cmap  = LinearSegmentedColormap.from_list('black_blue_cmap',  ["black", "blue"])

fig = Figure(figsize=(30, 5));
(subplot, subplotr, subplotg, subplotb) = fig.subplots(1, 4)  # Quatre zones de dessin
# Dessin de l'image et de ses trois couches
subplot.imshow(M)
imgr = subplotr.imshow(M[:,:,0], cmap=black_red_cmap,   vmin=0, vmax=255)
imgg = subplotg.imshow(M[:,:,1], cmap=black_green_cmap, vmin=0, vmax=255)
imgb = subplotb.imshow(M[:,:,2], cmap=black_blue_cmap,  vmin=0, vmax=255)
# Ajout des barres d'échelle de couleur aux images
fig.colorbar(imgr, ax=subplotr);
fig.colorbar(imgg, ax=subplotg);
fig.colorbar(imgb, ax=subplotb);
fig

Par la suite, nous visualiserons de même de nombreuses images. Il est donc temps d’automatiser la construction de la figure ci-dessus. Ouvrez le fichier utilities.py et complétez-y la fonction show_color_channels.

# Automatically reload code when changes are made
%load_ext autoreload
%autoreload 2
from utilities import *
show_source(show_color_channels)
show_color_channels(img)

Étudions maintenant les images de notre base de données:

from intro_science_donnees import data_dir
import os.path
dataset_dir = os.path.join(data_dir, 'apples_and_bananas_simple')
images = load_images(dataset_dir, "*.png")
image_grid(images, titles=images.index)

Observez l’image suivante et ses couches. Expliquez ce que vous voyez. Essayez d’autres exemples.

img = images[10]
show_color_channels(img)

Nous allons maintenant observer l’histogramme des couleurs apparaissant dans une image, en utilisant l’utilitaire color_histogram (vous pouvez comme d’habitude en consulter la documentation et le code par introspection avec color_histogram? et color_histogram??):

color_histogram(img)

Interprétez l’histogramme de la dixième et la troisième image:

BEGIN SOLUTION TODO END SOLUTION

img = images[9]
show_color_channels(img)
color_histogram(img)
img = images[2]
show_color_channels(img)
color_histogram(img)

Séparation des couleurs

Nous allons maintenant extraire les trois canaux, rouge, vert, bleu. Pour le canal des rouges, on extrait le sous-tableau à deux dimensions de toutes les cases d’indice \((i,j,k)\) avec \(k=0\). Le * 1.0 sert à convertir les valeurs en nombres à virgule.

R = M[:,:,0] * 1.0

Regarder le résultat directement n’est pas très informatif:

R

Comme d’habitude, il vaut mieux le visualiser:

fig = Figure()
subplot = fig.add_subplot(1,2,1)
fig.colorbar(subplot.imshow(R, cmap='Greys_r', vmin=0, vmax=255))
subplot = fig.add_subplot(1,2,2)
subplot.imshow(M)
fig
  1. Extraire de même le canal des verts et des bleus de la première image dans les variables G et B. N’hésitez pas à les visualiser!

### BEGIN SOLUTION
G = M[:,:,1] * 1.0

fig = Figure()
subplot = fig.add_subplot(1,2,1)
fig.colorbar(subplot.imshow(G, cmap='Greys_r', vmin=0, vmax=255))
subplot = fig.add_subplot(1,2,2)
subplot.imshow(M)
fig
### END SOLUTION
### BEGIN SOLUTION
B = M[:,:,2] * 1.0

fig = Figure()
subplot = fig.add_subplot(1,2,1)
fig.colorbar(subplot.imshow(B, cmap='Greys_r', vmin=0, vmax=255))
subplot = fig.add_subplot(1,2,2)
subplot.imshow(M)
fig
### END SOLUTION

Il est maintenant facile de faire de l’arithmétique sur tous les pixels. Par exemple la somme des intensités en vert et rouge s’écrit:

G + R

Exercice

  1. Calculer et visualizer la luminosité de tous les pixels de l’image, la luminosité d’un pixel \((r,g,b)\) étant définie comme la moyenne \(v=\frac{r+g+b}{3}\):

### BEGIN SOLUTION
V = (R+G+B)/3

fig = Figure() 
subplot = fig.add_subplot(1,2,1)
fig.colorbar(subplot.imshow(V, cmap='Greys_r', vmin=0, vmax=255))
subplot = fig.add_subplot(1,2,2)
subplot.imshow(M)
fig
### END SOLUTION

Vous venez de transformer l’image en niveaux de gris! Pour que cela colle au mieux avec notre perception visuelle, il faudrait en fait utiliser une moyenne légèrement pondérée; voir par exemple la Wikipedia.

Conclusion

Vous avons vu dans cette feuille comment charger une image dans Python et effectuer quelques manipulations, visualisations et calculs simples dessus. Cela a été l’occasion de mieux comprendre la décomposition d’une image en couches de couleur. Vous pouvez maintenant revenir à l’analyse de donnée pour attaquer l’extraction de features.