Python 14 Gestion des images Gestion des images 1 Introduction Problème : Un ordinateur traite de l’information sous forme numérique binaire. Comment coder les images à l’aide de codes binaires ? Il existe deux modes de codage d’une image numérique : - Le mode matriciel (« bitmap ») : il repose sur le principe d’une grille de pixels, - Le mode vectoriel (« vector ») : on décrit les propriétés mathématiques des formes de l’image. Une image vectorielle peut être zoomée sans que cela n’altère la qualité du rendu. Ce mode est adapté aux formes et images qui ne sont pas trop complexes : logos, typographie, plans, cartes, etc. Les formats de fichiers vectoriels sont PostScript, PDF, SVG, etc. Le nombre de pixels utilisés par unité de longueur donne un rendu plus ou moins précis des formes de l’image. Un zoom trop important fait apparaître la pixellisation de l’image. Les formats de fichiers matriciels sont BMP, GIF, PNG, JPEG, etc. On s’intéresse par la suite au codage des images par le mode matriciel. 2 Notions de colorimétrie Problème : Comment coder les couleurs à l’aide de codes binaires ? La synthèse additive est utilisée par les moniteurs et les projecteurs. Elle est constituée des trois lumières de base qui sont le Rouge, le Vert et le Bleu (RVB). Les couleurs secondaires sont plus claires que les primaires, c’est pour cela que leur synthèse est appelée additive. La synthèse soustractive est utilisée dans l’imprimerie et par les imprimantes. Les couleurs primaires sont le Cyan, le Magenta, le Jaune et le Noir (CMJN). Si on mélange deux couleurs primaires, le résultat sera une couleur secondaire plus sombre qui absorbera donc plus de lumière, c’est pour cette raison que cette synthèse est nommée soustractive. Cours Info - Jacques Delfaud - Page 1 sur 11 Python 14 Gestion des images Un modèle de colorimétrie est une manière de coder des couleurs avec des nombres. Le modèle Lab caractérise une couleur avec : - un paramètre d’intensité correspondant à la luminance : la combinaison L* est la clarté, elle va de 0% (noir) à 100% (blanc), - deux paramètres de chrominance qui décrivent la couleur : la composante a* représente la gamme de l’axe du vert (-128) au rouge (+128). La composante b* représente la gamme de l’axe du bleu (-128) au jaune (+128). Ce modèle est proche de la vision humaine. Le modèle RVB caractérise une couleur à l’aide de trois paramètres (chacun sur un octet, soit un nombre de 0 à 255) correspondant aux trois couleurs Rouge, Vert et Bleu. 00 00 00)(16) = (0,0,0) = noir FF FF FF)(16) = (255,255,255) = blanc FF 00 00)(16) = (255,0,0) = rouge 3 Palette « Paint Shop » Le mode matriciel « bitmap » Une matrice de pixels (« picture elements ») est constituée d’un ensemble de points pour former une image. Le pixel représente ainsi le plus petit élément constitutif d’une image numérique. - Formats limités à 256 couleurs : GIF, PCX, PGM, etc. - Formats acceptant différentes quantifications BMP, TIFF, TGA, PNG, etc. - Formats limités à 16 millions de couleurs JPEG, etc. Les images peuvent facilement être créées et stockées dans un tableau de pixels, la lecture et l’écriture d’un pixel sont aisées. Par contre, les fichiers peuvent être très gros (nécessité de compression). De plus, il y a des problèmes de changement d’échelle (apparition d’effets de marches d’escalier ou de flou avec interpolation). Enfin, les dimensions de l’image doivent être prévues pour la résolution de l’interface de sortie (écran, imprimante). La discrétisation d’une image se caractérise par sa définition, c’est-à-dire le nombre de pixels utilisés. La résolution de l’image établit un lien entre le nombre de pixels d’une image et sa taille réelle. Elle se caractérise par un nombre de pixels par unité de longueur. La quantification des couleurs est exprimée en bits par pixel (bpp) : - 1 (deux couleurs : noir et blanc par exemple), - 8 (niveaux de gris entre 0 et 255 par exemple, 256 couleurs), - 16 bits, etc. Cours Info - Jacques Delfaud - Page 2 sur 11 Python 14 Gestion des images Exemple 1 : - « 800 × 600 » signifie une largeur de 800 et une hauteur de 600 pixels. - « 600 dpi » (« ppp » : point par pouce, « dpi » : dots per inch) signifie 600 pixels par pouce (1 pouce = 25,4 mm). En connaissant le nombre de pixels d’une image et la mémoire nécessaire à l’affichage d’un pixel, il est alors possible de définir exactement la taille qu’occupe le fichier. Une définition du type 800 × 600 avec un codage sur 24 bits (3 octets) des couleurs, donne un fichier image de taille 1,44 Mo. La couleur peut être codée directement dans le pixel, ou dans une palette. L’organisation d’un fichier bitmap est donnée ci-contre. 4 En-tête (« header ») Identificateur du format Largeur, hauteur, nombre de couleurs, etc. Palette de couleur (éventuellement) Données d’image Suite de pixels éventuellement compressés Le format BMP Cours Info - Jacques Delfaud - Page 3 sur 11 Python 14 Gestion des images Exemple en 24 bit : Cours Info - Jacques Delfaud - Page 4 sur 11 Python 14 Gestion des images Exemple en 8 bit : Exemple 2 : Inversion des couleurs d’une image Inverser les couleurs d’une image ayant deux couleurs « noir » et « blanc » revient à changer les pixels noirs en blancs et inversement. Pour le modèle RVB, cela revient à changer chaque « valeur initiale » R, V et B en « 255 – valeur initiale ». Cours Info - Jacques Delfaud - Page 5 sur 11 Python 14 Gestion des images Algorithme 1 Inversion des couleurs d’une image BMP codée en 24 bits Données : T[1..n] : le tableau des n octets du fichier image Résultat : le tableau T avec les couleurs de chaque pixel inversées Pour i de 55 à n faire T[i][j] ← 255 - T[i][j] Fin Pour // on démarre après les entêtes du fichier et du bitmap Algorithme 2 Inversion au dessus de la diagonale des couleurs d’une image BMP codée en 24 bits Données : T[1..n] : le tableau des n octets du fichier image L, H : largeur et hauteur de l’image Résultat : le tableau T avec les couleurs de chaque pixel situés au dessus de la diagonale inversées Si L mod 4 <> 0 alors x←L + 4 - L mod 4 // Chaque ligne comporte un nombre d’octets multiple de 4 Sinon x←L Fin Si Pour i de 55 à n faire // on démarre après les entêtes du fichier et du bitmap y← ( i – 55 ) div 3 // y mod x est la colonne du pixel Si (y mod x) < (y div x)*L/H alors // y div x est la ligne du pixel T[i][j] ← 255 – T[i][j] Fin Si Fin Pour 5 AImage 3×3 image inversée image avec inversion au dessus de la diagonale Image 1600 ×1200 image inversée image avec inversion au dessus de la diagonale La compression des images « bitmap » Certains formats de fichiers « bitmap » autorisent la compression des données. Celle-ci peut se faire sans pertes (compression RLE, LZW, Huffman) ou avec pertes (compression JPEG). On définit le facteur de compression comme le rapport entre la taille du fichier initial et la taille du fichier compressé. Cours Info - Jacques Delfaud - Page 6 sur 11 Python 14 Gestion des images Les méthodes RLE, LZW et Huffman, sont utilisées pour d’autres codages que celui des images. Le format BMP autorise des compressions de type RLE, LZW ou JPEG. Les formats TIFF, PNG et GIF utilise la compression LZW. Méthode à base de redondances : la compression RLE (Run-lenght encoding) Un encodage RLE consiste à indiquer pour chaque suite de pixels d’une même couleur, le nombre de pixels de cette séquence. Le résultat comporte en général moins de caractères, bien que ça ne soit pas une obligation. Le format BMP permet d’utiliser la compression RLE pour les images en 1, 4 et 8 bits/pixel. Plus le nombre de couleurs est important, moins nombreuses sont les chances de redondance. La méthode est donc particulièrement intéressante pour les images en deux couleurs (noir et blanc). Par exemple, la liste « 0 0 0 0 F F 0 0 0 0 0 0 F F F F », peut-être encodée comme suit : « 4 0 2 F 6 0 4 F ». Si chaque caractère est codé sur 8 bits, la chaîne initiale fait 16×8=128 bits. La chaîne encodée fait 8×8=64 bits. Algorithme 3 Compression RLE Données : T[1..n] : un tableau de n caractères Résultat : le tableau L encodant la liste de caractères du tableau T selon la méthode RLE Compression_RLE(T) i←1 L←[] Tant que i<=n faire c←T[i] k←1 Tant que i+k<=n ∧ T[i+k]==c faire k←k+1 Fin Tant que L←L + [k] L←L + [c] i← i + k Fin Tant que Retourner L Méthode à base de dictionnaire : La compression LZW (Lempel Ziv Welch) Le principe général est de : - trouver des séquences de pixels qui se répètent, - construire un dictionnaire associant un code unique à chaque séquence, - remplacer la séquence de pixels par le code. Plus le nombre de couleurs est important, moins il est probable que des séquences se répètent. La méthode n’est donc pas appliquée lorsque le nombre de couleurs est élevé. Initialement, le dictionnaire comprend tous les caractères de la table ASCII. Les index de 0 à 255 sont donc occupés. Le dictionnaire est ensuite complété lorsque des suites nouvelles de caractères apparaissent. Au fur et à mesure que la taille du dictionnaire augmente, il faut augmenter le nombre de bits nécessaires au codage des éléments. Au delà du 255e , 511e indice, etc., il rajouter un bit. Par contre, le dictionnaire est reconstruit à partir du fichier compressé, il n’est pas nécessaire de le joindre à ce dernier. NOTA : Dans l’algorithme en page suivante, le dictionnaire est une liste de chaînes de caractères. Les fonctions Est_dans_dictionnaire() et Ajouter_dans_dictionnaire() ne sont pas détaillées. Cours Info - Jacques Delfaud - Page 7 sur 11 Python 14 Gestion des images Algorithme 4 Compression LZW Données : T[1..n] : un tableau de n caractères Résultat : le tableau L encodant la liste de caractères du tableau T selon la méthode LZW Compression_LZW(T) i←1 L←[ ] x←" " Tant que i<=n+1 faire Si i<n alors Si Est_dans_dictionnaire(x+T[i]) alors x←x + T[i] Sinon Ajouter_dans_dictionnaire(x+T[i]) L← L + x x←T[i] Fin Si Sinon // i=n L← L + x Fin Si i←i+1 Fin Tant que Retourner L Par exemple la chaîne « 0 0 0 0 F F 0 0 0 0 0 0 F F F F », peut-être encodée comme suit : i 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 x 0 0 00 0 F F 0 00 000 0 00 000 F FF F FF T[i] 0 0 0 0 F F 0 0 0 0 0 0 F F F F x+T[i] 0 00 00 000 0F FF F0 00 000 0000 00 000 000F FF FFF FF Ajout dans L Ajout dans dictionnaire 0 <256> = 00 <256> 0 F F <257> = 000 <258> = 0F <259> = FF <260> = F0 <257> <261> = 0000 <257> <262> = 000F <259> <263> = FFF <259> Chaîne encodée : « 0 <256> 0 F F <257> <257> <259> <259> ». Chaque caractère de la chaîne encodée est codé sur 9 bits (il faut aller au delà de 256 valeurs distinctes). Celle-ci fait donc 9×9=81 bits. Méthode statistique : La méthode de Huffman Exemple : Coder 4000 caractères de 6 valeurs différentes En ASCII, cela nécessite 8×4000 = 32000 bits. En limitant le codage à 3 bits/caractère, il faut 3×4000 = 12000 bits. Facteur de compression : 2,6. Peut-on faire encore mieux ? Cours Info - Jacques Delfaud - Page 8 sur 11 Python 14 Gestion des images Par exemple si la répartition des caractères est connue (voir tableau) : Il faut alors 3×800+3×400+3×400+4×200+1×2000+4×200 = 8400 bits. Facteur de compression : 3,8. Caractère n◦ Fréquence (%) Codage binaire de longueur fixe Codage "préfixe" de longueur variable 1 20 000 111 2 10 001 101 3 10 010 100 4 5 011 1101 5 50 100 0 6 5 101 1100 Certaines couleurs de pixels reviennent plus souvent que d’autres. L’idée est de réduire le nombre de bits utilisés pour le codage des pixels les plus fréquents et d’augmenter ce nombre pour les pixels plus rares. On utilise alors un codage de taille variable. Par contre, il faut utiliser un codage "préfixe", c’est à dire tel qu’un mot du code n’est pas le préfixe d’un autre mot. On peut alors lire une séquence codée sans ambiguïtés. Problème : Comment déterminer le codage de longueur variable optimal ? Pour représenter un codage préfixe, on utilise un arbre : - Les feuilles sont les caractères, - Les branches sont "0" (fils gauche) ou "1" (fils droit), - Chaque nœud interne contient la somme des poids de ses fils, - Chaque mot de code est donc un chemin de la racine vers une feuille contenant le caractère codé. Dans l’exemple de gauche, le caractère 1 est codé « 000 ». Dans l’exemple de droite, il est codé « 111 ». Dans l’exemple de gauche, le caractère 5 est codé « 10 ». Dans l’exemple de droite, il est codé « 0 ». L’algorithme de Huffman permet de construire l’arbre optimal, permettant d’obtenir l’encodage le plus efficace, c’est à dire minimisant la taille du fichier compressé. Chaque nœud est un objet contenant : - le fils gauche, - le fils droit, - la somme de la fréquence de ses fils. Cours Info Objet « nœud » - Jacques Delfaud - Fils gauche (nœud) Fils droit (nœud) Fréquence (réel) Page 9 sur 11 Python 14 Gestion des images Soit un ensemble E de n caractères « c » dont la fréquence d’apparition est définie par f(c). Les feuilles de l’arbre sont les caractères. Ils peuvent être modélisés comme des nœuds qui ont une fréquence, mais pas de fils droit ou gauche (sous-arbres vides). Le principe est alors le suivant : - on construit l’arbre du bas vers le haut, - on commence avec n feuilles et on effectue n-1 fusions. La fusion s’effectue sur les objets les moins fréquents. Algorithme 5 Algorithme de Huffman Données : T : la liste des n caractères c (de type nœud) Résultat : le nœud racine de l’arbre de Huffman avec tous ses fils ordonnés de façon optimale Arbre_Huffman(E) F ←T // F contient tous les caractères Pour i de 1 à n-1 faire z ← CreerNoeud() // z est un objet de type « nœud » z.gauche ← ExtraireMin(F) // la fonction retourne le nœud de fréquence minimale z.droit ← ExtraireMin(F) // il est supprimé de la liste F (deux nœuds supprimés en tout) z.frequence ← z.gauche.frequence + z.droit.frequence Inserer(F,z) // On insère le nouveau nœud dans la liste F Fin Pour // A chaque itération, la liste F perd un élément. Retourner ExtraireMin(F) // On retourne le seul nœud restant, // c’est à dire le dernier créé (de fréquence 100) NOTA : Les arbres sont implémentés par des listes chaînées (files) Comme les compressions RLE et LZW, cette méthode est inefficace lorsque le nombre de couleurs devient trop important. Toutes ces méthodes de compression sans pertes sont applicables pour des pixels, des chaînes de caractères et autres données informatiques. La compression avec pertes JPEG Pour des nombres de couleurs important (supérieur à 256), on utilise des méthodes de compression avec pertes, et notamment le format JPEG. Ce dernier utilise plusieurs techniques de compression sans pertes, mais aussi des méthodes permettant de réduire la taille de l’image en utilisant des propriétés de la vision humaine. Les étapes de la compression JPEG sont les suivantes : - Ré-échantillonnage de la chrominance, car l’œil ne peut discerner de différences de chrominance au sein d’un carré de 2×2 pixels, - Découpage de l’image en blocs de 8×8 pixels, puis application de la fonction DCT (transformation discrète en cosinus) qui décompose l’image en somme de fréquences, - Quantification de chaque bloc, c’est-à-dire application d’un coefficient de perte (fonction du ratio taille/qualité) qui annule ou diminue des valeurs de hautes fréquences, afin d’atténuer les détails en parcourant le bloc intelligemment avec un codage RLE (en zig-zag pour enlever un maximum de valeurs nulles). - Encodage de l’image puis compression avec la méthode d’Huffman. NOTA : On utilise aussi des méthodes de compression avec pertes dans les fichiers vidéo et audio. Cours Info - Jacques Delfaud - Page 10 sur 11 Python 14 Gestion des images 6 Syntaxe Python Exemple : import imageio im = imageio.imread("C :/Documents and Settings/Python/Gestion_Images/TestPython.bmp") # La taille de l’image est accessible dans le triplet (largeur, hauteur, nb composantes) suivant : print(im.shape) # On peut alors connaître le triplet de couleur du pixel (0,0) (coin supérieur gauche) print(im[0,0]) # On peut changer la couleur d’un point en donnant 3 composantes RGB : im[0,0] = (255, 0, 0) # On peut modifier simplement une composante : im[0,1][0] = 0 # on enlève le rouge # Puis on peut sauvegarder l’image résultante imageio.imsave("C :/Documents and Settings/Python/Gestion_Images/TestPythonBis.bmp", im) # Pour visualiser l’image obtenue : import matplotlib.pyplot as plt im = imageio.imread("C :/Documents and Settings/Python/Gestion_Images/TestPython.bmp") plt.imshow(im) plt.show() Les deux print nous donnerons : (392, 533, 3) [255, 255, 255] Et l’image suivante apparaitra : Cours Info - Jacques Delfaud - Page 11 sur 11