codage d'une image

Coder une image

Une image: un tableau de pixels

image pixelisée monochrome

image pixelisée monochrome

Comment représenter l’image de ce symbole de manière numérique?

Construire une image numérique

On utilise des boucles simples ou imbriquées, constituées d’une seule ou plusieurs instructions pour peindre les pixels (voir activité sur le site Algorea):

pixels sur une ligne: boucle simple

pixels sur une ligne: boucle simple

faire 6 fois:
  peindre en rouge
faire 6 fois:
  peindre en bleu
tableau de pixels. boucles imbriquées

tableau de pixels. boucles imbriquées

faire 12 fois:
  faire 6 fois:
    peindre bleu
    peindre rouge
tableau de pixels. utiliser un variant de boucle

tableau de pixels. utiliser un variant de boucle

pour i variant de 0 à 11:
  pour j variant de 0 à i:
    peindre bleu
  pour k variant de i+1 à 11:
    peindre en magenta

codage de l’image dans un fichier

Les plus petits détails de l’image sont ses pixels : des petits carrés remplis chacun avec une seule couleur. Coder les pixels de l’image revient à coder les couleurs de ses pixels. Les valeurs des couleurs des pixels sont alors mises les unes à la suite des autres, et constituent ainsi le fichier image en s’ajoutant aux métadonnées.

P1
5 6
0 1 1 1 0
0 0 0 0 1
...

Grâce aux métadonnées du fichier, l’ordinateur va traiter cette suite de chiffres selon une matrice de valeurs:

image pixelisée couleur

image pixelisée couleur

Le poids de cette image : pour 100 pixels sur 100 pixels (10 000 px) $$nombre\quad pixels \times 3 octets = 10000\times 3 = 30000 octets $$ Ce qui correspond à $$\tfrac{30000}{1024} = 29ko$$

Codage d’une image dans un tableau de données

On a vu en travaux pratiques, que le programme qui permet l’affichage des données en tableau utilise 2 boucles bornées, imbriquées:

# L est un tableau python 5 * 5
  for j in range(5):
    for i in range(5):
      if L[j][i] == 1 : 
        display.set_pixel(i, j, 9)

On obtient alors une image de type Noir et Blanc, sur une matrice de diodes 5 * 5.

Profondeur de couleur

La représentation bitmap d’une image par une matrice de pixels ne se limite pas à des images binaires en noir (0) et blanc (1).

En modifiant la profondeur de l’image, on peut coder plus de couleurs dans un pixel

niveaux de gris

(voir image plus bas).

Par exemple, le tableau suivant représente des valeurs allant du plus foncé au plus clair:

# fichier
P2
3 3
0 50 100 150 200 255
# matrice
pix = [[0,50,100],[150,200,255]]

couleur

Le codage des couleurs utilise la synthèse additive. Voir animation sur Algorea > SNT V2 > photographie numerique > ambiance lumineuse

# fichier
P3
2 2
255 0 0 0 255 0 0 0 255 255 255 255
# matrice
pix = [
[[255,0,0],[0,255,0]],
[[0,0,255],[255,255,255]]
]

Saurez-vous deviner le codage des couleurs sur cette image?

certains logiciels de design proposent de faire des essais de coloration. Ici, données en hex

certains logiciels de design proposent de faire des essais de coloration. Ici, données en hex

Caractéristiques des images

Luminosité d’une image

La luminosité d’une image représente la clarté de l’image. Pour un pixel donné, on peut établir une mesure de la luminosité de la manière suivante : Soient R, V et B les valeurs des intensités de chaque canal coloré. La luminosité L est égale à : $$L = R+V+B$$

Pour calculer la luminosité moyenne d’une image, voici l’algorithme:

s = 0
pour chaque ligne:
  pour chaque colonne:
    p <- couleur du pixel
    s = s + (p[0] + p[1] + p[2])/3
luminosite <- s / (ligne*colonne)

Pour chaque pixel, on fait la moyenne de ses 3 couleurs primaires.

On ajoute la valeur à l’accumulateur s. Puis on fait la moyenne des valeurs de s en divisant par le nombre de pixels (ligne*colonne).

Contraste

Les deux images présentées dans le paragraphe précédent, sur la luminosité, sont trop peu contrastées pour en apprécier le contenu.

En effet, l’amplitude de luminosité entre les pixels clairs et les pixels foncés est trop petite. En d’autres termes, les tons sombres et les tons clairs de l’image ont des luminosités qui ont des valeurs trop proches.

Le contraste mesure la différence de luminosité entre les tons clairs et les tons sombre.

Exemple :

image et contrastes

images faible contraste

Soient les fonctions recherche_du_max et recherche_du_min qui retournent la valeur du max ou du min dans une liste python simple.

img est un tableau de valeurs des couleurs en niveau de gris des pixels. Par exemple:

img = [[101,202,210,255,213,...], 
       [101,202,210,255,213,...], 
       [...]]

Le programme va parcourir chaque ligne, c’est à dire chaque sous-liste de img.

Si la fonction max_ligne retourne une valeur supérieure à max, c’est qu’une valeur plus grande d’intensité a été trouvée dans cette ligne. On remet à jour la valeur max de l’image avec max = max_ligne, et on passe à la ligne suivante.

(même principe pour min_ligne)

max = 0
min = 255
pour chaque ligne de img:
  max_ligne = recherche_du_max(ligne)
  min_ligne = recherche_du_min(ligne)
  si max_ligne > max: max = max_ligne
  si min_ligne < min: min = min_ligne

Qualité des images

La définition D d’une image est définie par un nombre de pixels selon la longueur et la largeur de l’image numérique. C’est une caractéristique propre de l’image.

On l’exprime le plus souvent en donnant les dimensions du plan de pixels, par exemple, une image de définition :

$$1900 \times 1700~pixels$$

La résolution R d’une image numérique correspond à sa densité en points (pixels). On mesure la densité de pixels sur l’écran en pixels par pouce (ppi) et la densité de points sur l’image imprimée en points par pouce (dpi). Ce n’est pas une caractéristique de l’image elle-même, mais de sa restitution par un écran (ou imprimé).

Selon le cas, on calculera la résolution selon l’un des côtés de l’image. Ou bien selon la diagonale de l’iamge.

La résolution peut s’exprimer en pixels par pouce. On peut en déduire la résolution en pixels par cm à l’aide de la relation suivante

entre la Définition D, la Résolution R, et le nombre de cm par pouces (2,54).

$$R(px/cm) = \tfrac{R(px/pouce)}{2,54(cm/pouce)}$$

En effet : 1 pouce vaut 2,54 cm. Un petit calcul montre que R = 72 ppi correspond à 28,3 pixels pour 1 cm.

Exemple : Soit une image de définition 800x533 que l’on imprime sur du papier photo de taille 15x10 (en cm), calculez la résolution de cette image en ppp.

Réponse : On prendra :

Définition : D = 800px et Longueur : L = 15 cm. On cherche R : resolution $$R = \tfrac{D}{L}\times 2,54 = \tfrac{800}{15}\times 2,54 = 135 ppp$$

Agrandissement et perte de qualité: lorsque le nombre de points codés dans l’image est inférieur au nombre de pixels de l’écran, il n’y a pas assez de points. L’agrandissement va déteriorer l’image, car un même point va être placé sur plusieurs pixels côte à côte. C’est ce que l’on appelle la pixelisation de l’image.

agrandissement jusqu'à pixelisation

agrandissement jusqu'à pixelisation

La selection d’une partie des points de l’image montre qu’il n’y a plus assez de points avec le zoom x12. On peut afficher une image à partir de cet agrandisement, mais il y a pixelisation. (voir la partie sur la transformation d’une image)

Transformation des images

Poids d’une image avec/sans compression

Image matricielle sans compression

En format brut.

A partir de ce qui a été vu plus haut, le poids d’une image non compressée de définition D et de profondeur de couleur C a un poids P : $$P = D \times C$$

Exemple : D = 1900 pixels * 1700 pixels et C = 3 octets

$$P = 1900 \times 1700 \times 3 = 9,7.10^6 octets = 9,7 Mo$$

En réalité, les images sont compressées. Ce qui permet d’avoir un poids moindre pour leur stockage, leur transfert…

Image matricielle avec compression

La compression d’une image c’est la réduction de la quantité d’informations nécessaires pour décrire l’image. Les idées générales sont :

  • De rassembler plusieurs pixels de même couleur, et établir une couleur moyenne des pixels sur une zone donnée.
  • supprimer des informations : par exemple en diminuant le nombre de couleurs possibles. On fait une réduction de l’espace des couleurs à celles qui sont les plus fréquentes dans l’image.

Le format PNG est un format d’image bitmap qui utilise la compression sans perte. Le facteur de compression n’est alors pas très élevé. Les données y sont réorganisées pour tenir moins de place. La compression utilise l’algorithme Deflate, basé sur le code de Huffman.

Rappelez vous que le fichier image est constitué de caractères (base 64). C’est donc les occurences de ces caractères dans le fichier qui permet la compression avec l’agorithme Deflate.

exemple de code de Huffman pour la compression d'un texte

exemple de code de Huffman pour la compression d'un texte

Le format jpg est un format compressé avec pertes. L’idée est de supprimer la différence de valeur des couleurs pour des pixels contigûs, aux couleurs proches.

La compression réduit la taille du fichier, mais dégrade la qualité de l’image.

La compression réduit la taille du fichier, mais dégrade la qualité de l’image.

image issue du site adobe.com

Transformation de la taille d’une image

Certains logiciels permettent de reduire ou agrandir la taille d’une image (resize). Cela modifie le nombre de pixels. Cette transformation se fait en conservant les proportions de l’image (homothétie).

La reduction consiste à choisir certains points de l’image d’origine pour les placer dans celle reduite.

Un agrandissement simple peut se faire en reportant la valeur d’un point sur un carré de points de l’image agrandie:

L’agrandissement peut aussi se faire en faisant une interpolation des valeurs des points (on invente les couleurs manquantes par un calcul de moyenne)

Interpoler: introduire dans une série de valeurs, de nouvelles valeurs intermédiaires.

Merci à zonensi.fr pour le travail sur cette partie du cours.

Lire/écrire dans un fichier

Les algorithmes de compression, d’agrandissement ou de reduction d’image agissent sur D (definition) ou sur C (profondeur de couleur) définis plus haut. Voyons ici la méthode pour lire et écrire dans un fichier image d’extension .ppm. Cela peut se faire à l’aide des fonctions natives de Python. Une autre option est d’utiliser la librairie PIL, comme vu en TP.

Soit le fichier image_source.ppm, dont le contenu est donné ci-dessous (4 premiers pixels)

P3
1000 801
255
87 87 87 
84 84 84 
81 81 81 
80 80 80

On peut manipuler les pixels d’une image à l’aide des instructions natives du langage python.

f1 = open('image_source.ppm','r')
for line in f1.readlines()[3:]:
    L = line.split()
    print(L)

Sortie:

# affichage des 4 premieres lignes :
['87', '87', '87']
['84', '84', '84']
['81', '81', '81']
['80', '80', '80']

Ce format en listes pour les valeurs des couleurs de pixels est utile pour appliquer un traitement.

Pour recomposer la chaine de caractère à partir de la liste, il faut faire l’opération inverse de line.split(), c’est à dire:

> ' '.join(['87', '87', '87'])
'87 87 87'

On peut rediriger les valeurs vers un nouveau fichier:

f1 = open('image_source.ppm','r')
f2 = open('image_copie.ppm','w')
largeur = 1000
hauteur = 801
f.write('P3'+'\n')
f.write(str(largeur)+' '+str(hauteur)+'\n')
f.write('255'+'\n')

for line in f1.readlines()[3:]:
    L = line.split()
    # ici: traitement sur les valeurs du pixel
    pixel = ' '.join(L)
    f2.write(pixel + '\n')

Suite du cours