Coder une image numérique
Qu’est ce qu’une image numériques?
Rappels de SNT: Une image numérique est constituée de pixels colorés (ou noirs et blanc). Le fichier comportant l’image numérique contient alors une succession de valeurs qui codent la couleur de chaque pixels: lire le cours de SNT, paragraphe codage de l'image
Librairie PIL
Pour manipuler les images, nous pouvons utiliser le module PIL
de Python. Celui-ci permet de traiter les pixels d’une image, un par un. Souvent, il faudra parcourir l’image pixel par pixel, sur toute la largeur et toute la hauteur. Cela est possible avec deux boucles imbriquées, à condition de connaitre ses dimensions largeur, hauteur
:
from PIL import Image
imageSource=Image.open("crabePortrait.bmp")
largeur,hauteur=imageSource.size
for x in range(largeur): # x varie de 0 à largeur - 1
for y in range(hauteur): # x varie de 0 à hauteur - 1
# traitement pixel (x,y)
imageSource est une instance de la classe Image
et possède une methode getpixel((x,y))
qui renvoie la valeur du pixel pour l’argument (x,y). S’utilise de la manière suivante:
imageSource.getpixel((x,y))
Nous allons travailler sur l’image suivante: crabePortrait.bmp

image de dimension 247 * 330 en format bitmap, RGB 3 octets
A vous de jouer: une fois l’image téléchargée, ouvrir un notebook dans le même dossier que l’image, puis, à partir du script précédent:
- Le script à compléter donné plus haut va créer une double boucle sur x (position en largeur qui varie de 0 à largeur) et sur y (position en hauteur qui varie de 0 à hauteur). Le pixel (0,0) est en haut sur le coin gauche. Copier-coller ce script
- dans la boucle: stocker dans p la valeur RGB du pixel de coordonnée (x,y) de imageSource:
p=imageSource.getpixel((x,y))
- Vérifier le contenu des données pour quelques une des points de l’image: afficher la valeur de p si x < largeur//10 et y < hauteur //10
Laquelle des structures de données représente le mieux le format des données:
[124,90,120]
(124,90,120)
#7C5A78
?
On souhaite maintenant diminuer le contraste, en divisant par 2 chacune de composantes RGB de chacun des pixels de l’image. L’image sera egalement plus sombre. On va créer cette fois une nouvelle instance, planPixels
, de la classe Image
, de la même dimension que celle d’origine, pour y copier les pixels un-à-un:
planPixels=Image.new("RGB",(largeur,hauteur))
On va placer les pixels de imageSource (lecture) dans la nouvelle image planPixels (écriture).
Debut du 2e programme:
from PIL import Image
imageSource=Image.open("crabePortrait.bmp")
largeur,hauteur=imageSource.size
planPixels=Image.new("RGB",(largeur,hauteur))
for x ...
for y ...
# traitement pixel (x,y)
# a completer ...
planPixels.save("crabe2.jpg")
planPixels.show()
On remplira cette image avec les pixels de l’image d’origine de la manière suivante:
A vous de jouer: Compléter le script pour…
- parcourir tous les pixels de l’image, largeur * hauteur.
- lire la valeur RGB de chaque pixel (
getpixel
) et modifier cette valeur, en diminuant le contraste de couleur: p=(p[0]//2, p[1]//2, p[2]//2)- placer ce pixel sur la nouvelle image en construction planPixels. Utiliser pour cela la methode putpixel((x,y),p) de l’objet
planPixels
où x et y sont les nouvelles coordonnées du pixel p que l’on veut dessiner:planPixels.putpixel((x,y),p)
Rotation d’une image: permutation de pixels
On va cherche à écrire un script Python qui réalise la rotation d’un quart de tour dans le sens horaire, comme sur l’image ci-dessous:

principe de la rotation horaire d'un quart de tour
Questions:
- Quelles sont les coordonnées des points A et B avant transformation?
- Quelles sont les nouvelles coordonnées des points A' et B' après transformation?
- pour un pixel situé initialement en (x,y), quelles sont ses coodonnées (x',y') après transformation? (la reponse est donnée ci-dessous. Cherchez avant de la lire…)
- Ecrire le script du programme qui devra réaliser la transformation de crabePortrait.bmp, d’un quart de tour, dans le sens horaire.
- Donner la complexité de cet algorithme.
Réponse à la question 3: x′=−y+hauteur−1, y′=x.
Rotation par la méthode Diviser pour Régner
On cherche maintenant à effectuer cette transformation, SANS utiliser de nouvelle image planPixels comme précédemment.
On utilisera l’image suivante (carrée) pour cette méthode: woody.jpg

image woody.jpg
Compléter la fonction echange_pix
suivante
def echange_pix(image,x0,y0,x1,y1):
"""procedure qui echange les pixels d'une image entre une position
de depart start et d'arrivée end
Params:
------
image : objet de la classe Image
x0,y0: int, int: coordonnées du pixel de depart
x1,y1: int, int: coordonnées du pixel d'arrivée
Exemple: echange du pixel (0,0) avec celui (120,120)
--------
>>> echange_pix(imageSource,0,0,120,120)
"""
start = image.getpixel((x0,y0))
end = image.getpixel((x1,y1))
image.putpixel((x0,y0),end)
image.putpixel((x1,y1),...)
Compléter la fonction echange_quadrant
suivante
Cette fonction permet d’echanger les pixels de 2 zones carrées de même dimensions.

exemple: echange des blocs A et B
def echange_quadrant(image,x0,y0,x1,y1,n):
"""procedure qui echange tous les pixels du bloc de pixels A
avec ceux du bloc B, de même dimension n*n.
L'image doit être carrée, de largeur et hauteur égaux à n
A et B occupent une position quelconque parmi les 4 quarts de l'image
Params:
-------
image : objet de la classe Image
x0,y0: int, int: coordonnées du pixel du coin superieur gauche de A
x1,y1: int, int: coordonnées du pixel du coin superieur gauche de B
n : int : largeur ou hauteur de l'image, en nombre de pixels
Example: echange du quart d'image en haut à gauche (A) avec celui
------------ en haut à droite (B) sur une image de largeur 120
>>> echange_quadrant(imageSource,0,0,120,0,120)
"""
for x in range(n):
for y in range(n):
echange_pix(image, x0+x,...# à compléter
Questions:
- On veut echanger les blocs A et D, qui font chacun 120*120 pixels. Quelle instruction faut-il écrire, utilisant la procedure
echange_quadrant
.
- Même question pour echanger les blocs A et C.
analyser la fonction rotate
La fonction rotate
permet de faire tourner l’image d’un quart de tour par une méthode de type diviser pour régner:
Tourner l’image d’un quart de tour, c’est permuter les 4 parties d’une image dont on a permuté l’interieur de ses 4 parties…
Une fois la partie divisée executée (appels recursifs), lorsque les subdivisions de l’image sont constituées d’un seul pixel, les pixels sont déplacés (règne) à l’aide de 3 permutations successives, selon le schéma suivant:

trois permutations réalisées sur les subdivisions de l'image
def rotate(image,x0,y0,n):
"""procedure recursive qui tourne d'un quart de tour un carré
de l'image de dimension n.
à chaque appel recursif, la taille de l'image est divisée par 2.
Si l'image fait plus d'un seul pixel, la rotation se fait par
permutation des (zones de) pixels A<=>B, B<=>D, D<=>C
Params:
-------
image
x0,y0: int, int: coordonnées du pixel du coin superieur gauche du carré
n: dimansion du carré
Example:
--------
rotate(imageSource,0,0,420)
"""
if n>=2:
m = n//2
rotate(image,x0,y0,m)
rotate(image,x0,y0+m,m)
rotate(image,x0+m,y0,m)
rotate(image,x0+m,y0+m,m)
echange_quadrant(image,x0,y0,x0+m,y0,m)
echange_quadrant(image,x0,y0,x0+m,y0+m,m)
echange_quadrant(image,x0,y0,x0,y0+m,m)
Analysez la procedure: On considère l’image de woody, quadrillée de la manière suivante. Cette image fait 512×512 pixels

image d'origine
- Lors du premier appel de la fonction: que vaut m?
A la fin de la remontée, lorsque m vaut 256, l’image, si on l’affiche est celle-ci:

image finale
- Compléter la grille avec les lettres A, B, C, D pour montrer la permutation des blocs sur le dessin.
On peut suivre la construction de l’image à différents niveaux de la pile d’appels recursifs, lors de la remontée. Pour faire ceci, ajouter les instructions suivantes en fin de la fonction rotate
:
if m > 100:
image.show()
Puis ajouter et executer les lignes suivantes:
from PIL import Image
def quart_tour(image):
largeur,hauteur=image.size
assert largeur == hauteur
rotate(image,0,0,largeur)
img = Image.open("woody.jpg")
quart_tour(img)
img.save("woody_endroit.jpg")
img.show()
Pour suivre la construction de l’image, nous allons placer les repères suivants sur l’image d’origine:

image quadrillée
- Pour chacune des images suivantes:
- quel est la valeur de m lorsque l’image est affichée?
- compléter les quadrants montrant la permutation dans chaque sous-partie
- dans quel ordre ces images sont elles affichées?

image a

image b

image c

image d
efficacité de la méthode
Evaluons l’efficacité de cet algorithme
- Est-il plus rapide ou plus lent que votre premier algorithme?
- Le nombre d’opérations significatives suit une loi de recurence: T(n)=4×T(n2)+C×n2 Cela confirme t-il votre reponse à la question pécédente?
On rappelle que l’intérêt de la méthode est surtout de ne PAS utiliser de nouvelle image planPixels comme précédemment. Il s’agit d’une transformation en place.
Liens et aides
- En cas de difficulté, consulter la page et les videos du site: monlyceenumerique.fr, aller à la partie sur la rotation des images.