Pixels
Pixels
1. Pixels
Un pixel peut être noir ou blanc, mais aussi en couleur (le plus souvent obtenue par l’addition des couleurs
de trois sous-pixels : un rouge, un vert, un bleu). La taille d’un pixel est généralement inférieure à 1 mm,
elle dépend du type d’écran. Plus la taille d’un pixel est petite, plus la résolution est bonne. Une autre unité
de mesure de la résolution est le nombre de pixels par pouce, abrégé par « ppp » (ou ppi pour pixels per
inch). Comme son nom l’indique, il s’agit de compter combien de pixels on peut juxtaposer sur une longueur
d’un pouce (1 pouce = 25,4 mm). L’équivalent dans le monde de l’impression est le dpi (dots per inch).
Voici quelques exemples approximatifs :
1 2π
L’œil humain a une acuité d’environ 1 minute d’arc (c’est-à-dire 1/60 de degré, soit θ = 60 360 ≃ 0.00029
radian). Ainsi à une distance d, l’œil distingue des pixels d’une taille ℓ ≃ dθ (avec ℓ et d en mètre et θ en
radian).
PIXELS 2
d θ
Ainsi en regardant un écran à 20 cm de distance, augmenter la résolution au-delà de 400 ppp ne permet de
ressentir une amélioration. Il faut alors mieux concentrer ses efforts sur la qualité de l’image (vitesse de
rafraîchissement, couleurs, rayonnements. . .) et sur l’énergie (consommation, encre électronique. . .).
Un pixel n’est pas vraiment un petit carré ! Dans ce cours nous assimilerons un pixel à un (petit) carré.
Cependant la réalité est plus compliquée. Un écran est composé d’une grille de sources lumineuses, appelées
des sous-pixels. Chaque source illumine une petite zone qui n’est pas nécessairement un carré (cela peut
être un disque ou un rectangle). Chaque source est souvent d’une seule couleur parmi rouge/vert/bleu,
mais son intensité peut varier. Il n’y a donc physiquement aucun carré dans ce processus ! Sur la photo
ci-dessous on pourrait regrouper trois sous-pixels superposés et appeler le petit carré correspondant un
pixel, en lui attribuant la couleur résultant des trois sous-pixels. Il est plus juste de dire que les pixels sont
un échantillonnage (relevé en des points d’une grille) du signal lumineux produit par l’écran.
1.2. Écran
La taille d’un écran est le couple (largeur, hauteur) exprimé en pixels, habituellement notée sous la forme
largeur × hauteur. Le résultat du produit correspond au nombre total de pixels de l’écran.
hauteur
largeur
Le repère des pixels sur un écran a son axe des « y » dirigé vers le bas, contrairement à l’usage en mathéma-
tiques (que l’on suivra dans cet ouvrage). Ainsi le pixel en haut à gauche a pour coordonnées (0, 0).
x
(0, 0)
P2 P2
P1 P1
L’idée naturelle est de calculer une équation de la droite (P1 P2 ). Une équation réduite est y = αx + β où α
est la pente et β l’ordonnée à l’origine. On va préférer écrire l’équation sous la forme :
y = α(x − x 1 ) + y1
∆y y2 − y1
où (x 1 , y1 ) est un point de la droite et la pente se calcule par α = ∆x = x 2 −x 1 .
.
P2
∆y
P1. ∆x
PIXELS 4
∆y
Hypothèse. On suppose x 1 < x 2 , y1 < y2 et α = ∆x ⩽ 1.
Autrement dit, on trace un segment ayant une pente comprise entre 0 et 1, c’est-à-dire l’angle formé entre
une horizontale et le segment est compris entre 0° et 45°. Les autres situations s’obtiennent par symétrie et
ne seront pas détaillées ici.
Les pixels à colorier ont pour abscisses successives i = x 1 , x 1 + 1, . . . , x 2 . L’ordonnée réelle correspondant à
l’abscisse i est y = α(i − x 1 ) + y1 mais nous voulons une coordonnée entière qui s’obtient par un arrondi :
j = arrondi(α(i − x 1 ) + y1 )
Remarque : si ⌊x⌋ désigne la partie entière d’un réel x (obtenue par une commande floor(x) en Python
par exemple) alors :
arrondi(x) = ⌊x + 21 ⌋
Pour minimiser les opérations on remarque que lorsque l’on passe de l’abscisse x à l’abscisse x + 1 alors
l’ordonnée passe de y à y + α.
y +α .
α
y . 1
x x +1
Exemple.
On souhaite tracer le segment reliant P1 (0, 0) et P2 (8, 3). On calcule :
∆y y2 − y1 3
α= = = = 0.375.
∆x x2 − x1 8
PIXELS 5
3
P2
2
0
P1
0 1 2 3 4 5 6 7 8
Noter que les calculs se font avec des nombres réels et sont donc assez lents.
7
P2
6
2 2
P2
1 1
0 0
P1 P1
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 8 9
4
P2
3
0
P1
0 1 2 3 4 5 6 7 8 9 10 11
PIXELS 6
Noter que p, m sont des constantes entières avec p > 0 et m < 0 ; d est le « défaut », c’est une variable qui
détermine s’il faut prendre le pixel juste à côté ou bien s’il faut monter d’un cran. Le défaut est alors mis
à jour en lui ajoutant selon le cas une valeur fixe p > 0 ou m < 0 (si d < 0, le nouveau défaut d + p a
augmenté ; si d ⩾ 0, le nouveau défaut d + m a diminué car m < 0).
+m
+p
d d
Noter qu’en plus les opérations sur les entiers sont élémentaires : ce sont des additions et un test de positivité.
Exemple.
On souhaite tracer le segment reliant P1 (0, 0) et P2 (8, 5). On calcule :
p = 10 m = −6 d0 = 2
Voici les valeurs lorsqu’on applique l’algorithme :
nouvelle
i j d monter ?
valeur de d
0 0 2 oui −4
1 1 −4 non 6
2 1 6 oui 0
3 2 0 oui −6
4 3 −6 non 4
5 3 4 oui −2
6 4 −2 non 8
7 4 8 oui 2
8 5
PIXELS 7
5
P2
4
0
P1
0 1 2 3 4 5 6 7 8
.
A(x k + 1, yk + 1)
.
A(x k + 1, yk + 1)
.
P(x k , yk )
.
B(x k + 1, yk )
.
P(x k , yk )
.
B(x k + 1, yk )
.
A′
.
A
.
B′
.
A′
.
P
.P
.
B
.
B′
Cas A. Cas B.
Cas du choix A (cas Dk ⩾ 0). Le pixel suivant a pour abscisse x k + 2 et pour ordonnée soit yk + 2 (A′ ),
soit yk + 1 (B ′ ).
.
A′ (x k + 2, yk + 2)
. .
A(x k + 1, yk + 1) B ′ (x k + 2, yk + 1)
.
P(x k , yk )
Cas A.
Calculons alors le défaut :
Dk+1 = E(A′ ) + E(B ′ ) = E(x k + 2, yk + 2) + E(x k + 2, yk + 1)
= a(x k + 2) + b( yk + 2) + c + a(x k + 2) + b( yk + 1) + c
= 2a x k + 2b yk + 4a + 3b + 2c
= Dk + 2a + 2b
PIXELS 9
.
A′ (x k + 2, yk + 1)
.
P(x k , yk )
.
B(x k + 1, yk )
.
B ′ (x k + 2, yk )
Cas B.
Calculons alors le défaut :
Dk+1 = E(A′ ) + E(B ′ ) = E(x k + 2, yk + 1) + E(x k + 2, yk )
= a(x k + 2) + b( yk + 1) + c + a(x k + 2) + b yk + c
= 2a x k + 2b yk + 4a + b + 2c
= Dk + 2a
Ainsi en posant p = 2a = 2∆ y, on obtient la formule de récurrence :
Dk+1 = Dk + p.
Conclusion : on sait calculer Dk+1 qui permet de choisir entre A′ et B ′ . Ainsi à partir de D0 et par les
formules de récurrence ci-dessus on calcule la suite des défauts Dk et on sait donc s’il faut monter le
pixel d’un cran ou pas.
De près le rendu n’est pas impressionnant mais de loin l’amélioration est sensible.
Nous allons expliquer un algorithme simple pour dessiner un segment avec anticrénelage. Nous avons donc
deux pixels l’un au-dessus de l’autre à colorier : le pixel A et le pixel B. Comment répartir 100% de couleur
entre ces deux pixels ?
Commençons par expliquer une analogie issue de la physique. Considérons deux masses ponctuelles liées
entre elles : mA centrée en A et mB centrée en B. Lorsque l’on regarde ce système d’assez loin on peut le
considérer comme un système formé d’une seule masse ponctuelle : la masse totale est m = mA + mB et est
centrée au centre de gravité G. Ce centre de gravité G est défini par la relation :
ℓA mA = ℓB mB
où ℓA = AG et ℓB = BG.
.
A
.G
.
B
.
G
mB
mA
mA + mB
ℓA ℓB
.
A
.
G
.
B
mA mB
Reprenons cette idée : nous répartissons une masse totale m = 1 (100% de la couleur) centrée exactement
sur la droite réelle en G entre deux points A et B.
Notons (x, y) le point G de la droite réelle. Soit alors :
j = ⌊ y⌋ et ℓ = y − ⌊ y⌋
PIXELS 11
respectivement la partie entière et la partie fractionnaire de y. La distance BG est ℓ et la distance AG est 1−ℓ
(car AB = 1). Ainsi pour que les couleurs satisfassent la formule du centre de gravité il faut choisir mA = ℓ et
mB = 1 − ℓ comme intensité de couleur respectivement pour A et B car on a alors bien (1 − ℓ)ℓ = ℓ(1 − ℓ).
On adapte l’algorithme du tracé élémentaire pour y ajouter l’anticrénelage.
Voici l’algorithme du tracé de l’arc de cercle de type Bresenham car il n’utilise que des entiers. Le point de
départ est le point (0, r) (point le plus haut du cercle).
Sur la portion tracée (de 90◦ à 45◦ ) il y a exactement un pixel pour chaque i. Pour obtenir le cercle complet,
en plus du pixel en (x 0 + i, y0 + j), il faut afficher par symétrie les pixels en (x 0 + j, y0 + i), (x 0 − i, y0 + j),
(x 0 − j, y0 + i) (x 0 + i, y0 − j), (x 0 + j, y0 − i), (x 0 − i, y0 − j), (x 0 − j, y0 − i).
Preuve. La preuve est similaire à celle de l’algorithme du tracé d’un segment de Bresenham.
• Équation du cercle.
On suppose que le cercle est centré à l’origine : (x 0 , y0 ) = (0, 0). Un cercle quelconque s’obtiendrait par
translation. Soit la fonction d’écart :
E(x, y) = x 2 + y 2 − r 2
PIXELS 13
Si E(x, y) = 0 le point (x, y) est sur le cercle, si E(x, y) > 0 le point est à l’extérieur, si E(x, y) < 0 le
point est à l’intérieur.
y
. . .
E(x, y) = 0
E(x, y) > 0
E(x, y) < 0
• Descendre ou pas ?
Pour un pixel centré en P déjà tracé, nous devons choisir pour le pixel suivant entre A et B. On choisit le
point le plus proche du cercle, c’est-à-dire le minimum entre |E(A)| ou |E(B)|. On définit le défaut par
D(P) = E(A) + E(B)
et on a :
Si D(P) < 0 alors on choisit A, si D(P) ⩾ 0 alors on choisit B.
.
P(x k , yk )
.
A(x k + 1, yk )
.
P(x k , yk )
.
A(x k + 1, yk )
.
B(x k + 1, yk − 1)
.
B(x k + 1, yk − 1)
.P
.A
.A′
.P
.B′
.
B
.
A′
Cas A.
.
B′
Cas B.
PIXELS 14
Cas du choix A (cas Dk < 0). Le pixel suivant a pour abscisse x k + 2 et pour ordonnée soit yk (A′ ), soit
yk − 1 (B ′ ).
.
P(x k , yk )
.
A(x k + 1, yk )
.
A′ (x k + 2, yk )
.
B ′ (x k + 2, yk − 1)
Cas A.
Le défaut se calcule ainsi :
Dk+1 = E(A′ ) + E(B ′ ) = E(x k + 2, yk ) + E(x k + 2, yk − 1)
= (x k + 2)2 + yk2 − r 2 + (x k + 2)2 + ( yk − 1)2 − r 2
= Dk + 4x k + 6
Cas du choix B (cas Dk ⩾ 0). Le pixel suivant a pour abscisse x k + 2 et pour ordonnée soit yk − 1 (A′ ),
soit yk − 2 (B ′ ).
.
P(x k , yk )
. .
B(x k + 1, yk − 1) A′ (x k + 2, yk − 1)
.
B ′ (x k + 2, yk − 1)
Cas B.
3. Colorier
graine
PIXELS 15
Principe. On part de la graine, qu’on colorie en gris. Pour chacun de ses quatre pixels voisins (gauche, bas,
droite, haut), si c’est un pixel blanc, on applique le processus à partir de ce nouveau pixel.
Ci-dessous la graine et ses quatre voisins.
Mise en œuvre. Ce procédé peut être traduit en algorithmes par différentes méthodes : algorithme récursif,
algorithme avec une pile ou algorithme avec une file. Dans tous les cas ce sont des algorithmes qui utilisent
beaucoup de mémoire afin de stocker les pixels en attente d’être traités. On choisit un algorithme avec file
(queue ou fifo, first in, first out) un peu moins gourmand en mémoire que les autres choix.
4. Animation
Nous expliquons quelques principes liés à l’animation des images. Les premiers dessins animés étaient basés
sur des calques : une image fixe pour le calque de l’arrière-plan, un calque pour le premier plan avec par
exemple un personnage à redessiner de nombreuses fois afin de simuler le mouvement et éventuellement
des calques intermédiaires. On retrouvait aussi cette technique au cinéma où certains arrières-plans étaient
peints.
0%
10 %
19 %
39 %
60 %
80 %
100 %
4.2. La 2.5D
Comme son nom le suggère, la 2.5D est un ensemble des techniques d’animation qui permettent de simuler
une scène 3D. C’est le cas de beaucoup de jeux vidéos de stratégie où le point de vue du joueur est au-dessus
du plateau de jeu. Les objets représentés en perspective (par exemple ci-dessous les immeubles) sont des
petites images 2D pré-dessinées. C’est une technique efficace mais qui nécessite de fixer ou de limiter
fortement le choix de la vue du joueur.
Pour réaliser de petits jeux animés, les modules de Pygame de Python sont très sympas. On explique ci-dessous
les principes de l’animation 2D en suivant la philosophie de Pygame.
Animation 1D.
Commençons par une animation très élémentaire en une seule dimension. Les objets en jeu sont :
• fond qui est l’image d’arrière-plan représentée ici par une liste de 0,
PIXELS 18
• un entier p qui représente la position d’un personnage, ce personnage est représenté par un 1,
• ecran qui est l’image finale affichée, et sera donc une juxtaposition de 0 et de 1.
Les étapes de l’animation sont les suivantes :
• Initialisation. On initialise ecran avec l’image d’arrière-plan fond :
0000000000
On définit une position de départ, par exemple p = 3.
• Affichage. Par ecran[p] = 1 on affiche le personnage :
0001000000
• Déplacement.
— Effacer le personnage. ecran[p] = 0
— Changer la position. p = p + 1.
— Réafficher le personnage. ecran[p] = 1
En répétant le déplacement on obtient, successivement :
0001000000
0000100000
0000010000
0000001000
0000000100
Animation 2D.
Le principe est similaire, il nous faut une image de fond, une image à afficher pour l’objet à déplacer.
L’affichage à l’écran correspond à affecter des valeurs à la mémoire graphique (screen buffer) ; on représente
ici l’écran comme une image.
L’opération fondamentale est la copie rapide ou blit (pour bit-block transfer) qui consiste à copier les données
d’une image dans la mémoire graphique. Concrètement cela signifie copier une image et l’afficher à l’écran
à une position donnée (figure (a)) ou bien copier seulement une partie d’une image (figure (b)).
blit
Sous-image
blit
Le second cas de figure nous sert à effacer le personnage avant de le déplacer, plus exactement on copie à
l’écran la portion de l’arrière-plan à la place qu’occupait le personnage.
Le processus de l’animation est alors le même que précédemment :
• (a) Initialisation. On initialise ecran avec l’image d’arrière-plan fond :
• (b) Affichage. On affiche le personnage à une position donnée.
• Déplacement. (À répéter)
— (c) Effacement du personnage. Pour cela on copie la portion de l’arrière-plan correspondant à l’empla-
cement qu’occupe le personnage.
— (d) Affichage du personnage à sa nouvelle position.
La vitesse du mouvement est contrôlée par la longueur du déplacement et la durée de la pause entre deux
répétitions.
Écran
(a)
Arrière-plan
(b)
Image
(c)
(d)