Aller au contenu

Le module matplotlib.image⚓︎

Nous utilisons cette version de la Joconde : MonaLisa.jpg

Le module matplotlib.image permet de transformer une image en une matrice de taille (hauteur x largeur) de composantes couleurs (format [r v b]).

Python
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np

img = mpimg.imread("MonaLisa.jpg")


# Exemple de manipulation avec une transformation en niveau de gris
def bw(img):
    nouvelle_image = np.ndarray(img.shape)
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            gris = (img[i][j][0] + ... + ...) /3 / 255 # on divise par 255 pour avoir un flottant entre 0 et 1
            nouvelle_image[i][j] = np.array([...,...,...])

    return nouvelle_image

plt.imshow(bw(img))
plt.show()

La peinture, remplacée par un nuage de points isolés.⚓︎

Nous allons garder seulement un nombre restreint de pixel de l'image. Ces pixels sont choisis aléatoirement avec en moyenne un point sur 100 retenu, en parcourant le rectangle de l'image de gauche à droite, et de haut en bas.

Voici le code de sélection :

Python
from random import randint

nuage = {}
ligne = 0
colonne = 0

# A chaque fois qu'un pixel est choisi, on passe au suivant en sautant un nombre de pixel aléatoire entre 1 et 200 (d'où en moyenne 1/100)
saut = randint(1,200) 

# calcul des coordonnées du nouveau pixel
nelle_colonne = (colonne + saut) % img.shape[1] # le % pour revenir en début de ligne quand on atteint la droite

nelle_ligne = ligne + (colonne + saut) // img.shape[1] # en cas de changement de ligne

# et en boucle
while nelle_ligne < img.shape[0] and nelle_colonne < img.shape[1]:
    nuage[(nelle_ligne,nelle_colonne)] = img[nelle_ligne][nelle_colonne]
    saut = randint(1,200)
    colonne = nelle_colonne
    ligne = nelle_ligne
    nelle_colonne = (colonne + saut) % img.shape[1]
    nelle_ligne = ligne + (colonne + saut) // img.shape[1]

Maintenant, nous avons un dictionnaire nuage, dont les clés sont les coordonnées du pixel et les valeurs la couleur du pixel correspondant dans l'image initiale.

En représentant ce nuage obtenu alétoirement, voici ce que l'on obtient :

Reconstruction d'une image avec l'algorithme des k plus proches voisins.⚓︎

Évidemment, on se demande si l'image va nous évoquer quelque chose qui ressembleau tableau de Léonard de Vinci.

Pour ce qui est de faire tourner l'algorithme, plusieurs questions se posent.

Nous avons en gros 3730 points dans le nuage, et nous voulons pour chacun des points de la taille de l'image Ă  reconstruire (746 x 500) trouver les k plus proches voisins.

En voulant faire tourner l'algorithme sans optimisation nous aurions : - le tri des 3730 points du nuages par rapport Ă  la distance pour chaque pixel - choix des k plus proches et calcul de la couleur moyenne de ces k points.

Le calcul peut ĂŞtre long...

D'où l'idée de ne pas trier sur tous les points du nuage mais sur une sélection carrée (un cercle pourrait aussi convenir, le carré simplifie les calculs avec la distance de Manhattan) autour du point suffisament grande pour contenir toujours k points, et suffisament petite pour réduire le nuage.

Voici donc notre première étape, écrire une fonction qui retourne une sélection du nuage des points les plus proches du point concerné.

Python
def selection(nuage, point):
    """
    selection des points dont la différence de abscisses et la différence des ordonnées est inférieure à 40
    (le choix de 40 est pour garantir un carré d'au moins 1600 (40 x 40) pixels même dans les coins et donc 
    surement 1600/100 points dans cette sélection du nuage
    """
    sel = {} # contient la sélection
    for k,v in nuage.items():
        if .......:
            sel[k] = v
    return sel

Nous avons aussi besoin de la fonction qui calcule une distance pour trier les points du sous-nuage.

Le choix de la distance de Manhattan est pour accélérer le traitement.

Text Only
1
2
3
4
5
def distance(p1,p2):
    """
    Retourne la distance de Manhattan entre p1 et p2
    """
    return abs(p1[0] - p2[0]) + abs(...)

Et, au coeur de ce code des k plus proches voisins, une fonction qui prend en entrée le nuage et un point, calcule la sélection du nuage autour de ce point, trie cette sélection et retourne la valeur moyenne (ici la fonction np.mean() est utilisée, elle permet de calculer des moyennes sur les 3 composante de la couleur en manipulant les couleur comme des vecteurs).

Python
# choix de la valeur de k
k = 10 # 10 est une bonne valeur mais il est possible de tatônner pour trouver la valeur la plus adaptée

def knn_color(nuage, point):
    """
    Récupère la sélection du nuage
    Transforme la sélection en une liste
    La trie suivant la distance au point
    retourne la couleur moyenne, des couleurs des k plus proches
    """
    sel = selection(nuage, point)
    liste_sel = list(sel)
    liste_sel.sort(key = lambda x: distance(x,point))
    moyenne_knn = np.mean([ sel[liste_sel[i]] for i in range(k)], axis= 0)
    return moyenne_knn/255

Et enfin la boucle pour construire l'image:

Python
1
2
3
4
5
6
7
8
9
for ligne in range(img.shape[0]):

    # Comme les calculs prennent un certains temps, on affiche une information toutes les 10 lignes traitées
    if not ligne % 10:
        print("ligne:", ligne)

    for colonne in range(img.shape[1]):
        point = (ligne, colonne)
        mona_knn[ligne][colonne] = knn_color(nuage, point)

Ce code montre une utilisation de l'algorithme des k plus proches voisins (KNN).

Un défi peut être de chercher (avec les connaissances de terminale NSI) quelles structures de données permettent de réduire sa compléxité temporelle.

code complet