Éléments d'Infographie 2D

Introduction

L'infographie 2D désigne l'ensemble des techniques et méthodes qui permettent de créer, manipuler et représenter des images dans un espace à deux dimensions, et plus spécifiquement à l'aide des ordinateurs. Elle englobe la conception de formes géométriques, la gestion des couleurs, des textures, des ombrages, ainsi que l’animation d’éléments visuels sur un plan fixe. Elle est utilisée en design graphique, dans les jeux vidéo, les interfaces utilisateur ou encore l’illustration. L’infographie 2D repose sur des formats matriciels (bitmap) ou vectoriels selon les besoins de précision et de mise à l’échelle.

Ses fondements mathématiques reposent sur des objets géométriques élémentaires, indispensables à la modélisation et à la manipulation des scènes visuelles. Le point constitue l’unité de base, il est généralement défini par ses coordonnées dans un repère du plan. Le vecteur permet de coder des translations, des orientations, des vitesses, etc. Les droites et segments sont utilisés pour structurer les formes, définir des contours ou tracer des trajectoires. Les courbes, qu’elles soient polynomiales (comme les courbes de Bézier) ou paramétriques, offrent une représentation plus souple des formes complexes. À ces éléments s’ajoutent les transformations géométriques fondamentales (translation, rotation, homothétie, symétrie). Elles sont principalement réalisées grâce au calcul matriciel.

Le cours et les td

Cet enseignement de volume très limité n'a d'autre ambition que de constituer un socle rudimentaire pour l'étude plus avant des algorithmes de l'infographie. Il est constitué de trois parties :

  1. Rappels d'algèbre linéaire et de géométrie dans le plan euclidien en mettant l'accent sur les spécificités de l'infographie.
  2. Étude de tracé de droites (algorithme de Bresenham) et de cercles (Michener).
  3. Étude de tracé de courbes (Bézier et \(B\)-splines).

La représentation d'images sur un écran s'appuie sur l'arsenal géométrique développé durant des millénaires, mais pose des problèmes spécifiques à ce périphérique de sortie, c'est précisément ce qui nous occupe ici. Le tracé d'une droite ou d'un cercle, qui peuvent être considérés comme deux outils atomiques pour composer des images à l'écran, constituent les premiers exemples de ces enjeux algorithmiques.

Les courbes et les surfaces dites de Bézier ont été développées par P. Bézier dans les années 70 pour la création d'éléments de surface chez Renault pour tracer la peau des véhicules, c'est-à-dire la carrosserie. Le principe est relativement simple, on se donne un ensemble de n + 1 points du plan appelées points de contrôle et on cherche à construire une courbe plane inscrite dans l'enveloppe convexe de ces n + 1 points. Pour cela, on introduit le polynômes de Bernstein et on pondère les points de contrôle avec les valeurs des fonctions polynomiales associés. Une courbe de Bézier est alors un ensemble de barycentres. Les courbes \(B\)-splines reprennent le même principe avec beaucoup plus de souplesse.

Je n'ai pas eu le temps de transposer le polycopié en version dynamique, il faudra donc s'en contenter. Les sujets de travaux dirigés et de travaux pratiques sont inclus dans ce document.

Merci de m'indiquer les inévitables inexactitudes, coquilles et probables erreurs qui émaillent ce document.

Annales des examens

  1. 2010-2011. Session 1 et Session 2
  2. 2005-2006. Session 1
  3. 2003-2004. Session 1 et Session 2
  4. 2002-2003. Session 1 et Session 2
  5. 2001-2002. Session 1 et Session 2
  6. 2000-2001. Session 1 et Session 2
  7. 1999-2000. Session 1 et Session 2
  8. 1998-1999. Session 1 et Session 2
  9. 1997-1998. Session 1 et Session 2

Travaux pratiques

Codage écran d'un nuage de points

Écrivez un programme convert qui convertit une séquence de points du plan euclidien en une séquence de points de la fenêtre écran.  Les paramètres de la ligne de commande sont au nombre de 7:

xmin xmax ymin ymax H L fichier

Les 4 premiers paramètres sont les coordonnées des deux points A et B (respectivement coin inférieur gauche et supérieur droit) qui définissent la fenêtre d'observation dans le plan euclidien, les deux paramètres suivants sont la hauteur et la largeur de la fenêtre écran et le dernier est le nom du fichier qui contient les points à afficher. Ces points sont stockés sous forme de couples de type double (un couple par ligne du fichier) et les termes du couple sont simplement séparés par un espace. Le programme doit donc boucler sur l'ensemble des points du fichier (si le nom du fichier est absent, le fichier est l'entrée standard pour permettre de cascader les commandes avec le pipe |). Utilisez la transformation calculée en TD à partir de la matrice dans l'espace projectif, si \((x,y)\) désigne l'un des couples du fichier d'entrée, les coordonnées \((X,Y)\) de son image dans l'espace écran sont données par:

\begin{align*} X &:= L \frac{x-x_{\min}}{x_{\max}-x_{\min}}\\ X &:= H \frac{y_{\max} - y}{y_{\max}-y_{\min}} \end{align*}

Pour tester votre transformation, vous écrirez un second programme poly qui renvoie sur la sortie standard une séquence de points du plan formatés de la même façon que pour le fichier d'entrée du programme convert. Ces points seront définis par une courbe polynomiale dont les coefficients seront passés en paramètres. Plus précisément, si le polynôme est donné par l'expression

\begin{equation} P(X):=a_0+a_1X+\cdots+a_kX^k. \end{equation}

Il y aura \(k+4\) paramètres sur la ligne de commande :

xmin xmax nbpoints a0 a1 ...

Les \(2\) premiers paramètres xmin et xmax définissent l'intervalle sur lequel on veut évaluer la fonction polynomiale, nbpoints désigne le nombre de couples qu'il faudra calculer et les termes suivants correspondent aux coefficients du polynôme considéré. L'échantillonnage de l'intervalle sera uniforme. Pour évaluer une fonction polynomiale, on utilisera la factorisation de Hörner qui permettra d'écrire un algorithme de complexité linéaire :

\begin{equation} P(X)=\Big(\big(\ldots(a_k)X + a_{k-1})X + a_{k-2})X + \cdots + a_2\big)X+a_1\Big)X+a_0 \end{equation}

L'algorithme de Hörner est implanté en langage C de la manière suivante :

double eval_poly(double *P, double x, unsigned char k){
unsigned char i = 0;
double y = 0;
for (i = 0; i <= k; i++)
  y = x * y + P[k - i];
return y;

Pour afficher la courbe, vous utiliserez la bibliothèque cng. Les chemins sont :

/usr/local/lib/libcng.a (bibliothèque)
/usr/local/include/cng.h (headers)
/usr/local/src/CNG_v0.2 (Makefile et exemples)

Tracé de droite (Bresenham)

Écrivez un programme bresenham en langage C qui trace le segment [A,B] à l'aide de l'algorithme de Bresenham dans une fenêtre écran dimensionnée automatiquement à partir des coordonnées entières \((x_A,y_A)\) et \((x_B,y_B)\) des points \(A\) et \(B\). Les paramètres de la ligne de commande seront :

bresenham xA yA xB yB

Modifiez votre programme pour faire de l'antialiasing. On note B (resp. T) le pixel en-dessous du pixel L (resp. au dessus du pixel H). On considère la droite orientée (LH). Trois cas se présentent:

  1. la droite (AB) passe entre les centres des pixels L et H et les distances algébriques HI et LI satisfont HI < 0 et LI > 0; On allume L et H avec pour intensités respectives |HI| et 1 - |HI|.

  2. la droite (AB) passe au-dessus du centre du pixel H et les distances algébriques HI et LI satisfont HI > 0 et LI > 0; On allume H et T avec pour intensités respectives 1 - |HI| et |HI|.

  3. la droite (AB) passe en-dessous du centre du pixel Let les distances algébriques HI et LI satisfont HI < 0 et LI < 0.On allume L et H avec pour intensités respectives 1 - |LI| et |LI|.

Pour tracer les segments qui ne sont pas dans le premier octant, calculez les 7 symétries qui permettent de ramener la situation au bon octant.

PS. On définit la luminance Y d'un pixel à partir de son codage RVB par la pondération suivante correspondant à la sensibilité de l'œil aux trois couleurs primaires: Y = 0.30 R + 0.59V + 0.11 B

Tracé d'une courbe (Bézier)

L'objectif est d'écrire un programme qui trace des courbes de Bézier. Le programme récupère le nombre p de points à tracer dans la fenêtre écran, la dimension horizontale SX et verticale SY de la fenêtre écran ainsi que les n + 1 points de contrôle M0 = (x0, y0), M1 = (x1, y1), . . ., Mn = (xn, yn) sur la ligne de commande:

  bezier p SX SY x0 y0 x1 y1 ... xn yn

Pour réaliser le tracé, vous utiliserez la méthode directe, i.e. celle qui consiste à manipuler les polynômes de Bernstein et à les évaluer avec la factorisation de Hörner. On rappelle que le point générique de la courbe de Bézier M(u), u ∈ [0, 1] est défini par la fonction polynomiale

M(u) = Bn,0(u). M0 + Bn,1(u). M1 + Bn,2(u). M2 + . . . + Bn,n(u). Mn

Bn,p(T) est le pème polynôme de Bernstein d'ordre n:

Bn,p(T) := C(n, p) T p(1 - T)n - p

Dans un deuxième temps, intégrer ce travail dans un programme plus général qui permet de fixer les points de contrôle en cliquant à la souris dans une fenêtre graphique et qui permette également de déplacer ces points de contrôle en réaffichant la courbe au fur et à mesure des modifications. Modifier le programme pour utiliser la méthode dynamique à l'aide des barycentres successifs.

Projet

L'objectif du projet est de développer une (petite) application pour traiter des images 2D avec la bibliothèque cng. Le principe général consiste à ouvrir une fenêtre graphique 800x600 par exemple, et garder une bande horizontale ou verticale pour contenir les outils dans la liste ci-dessous.

Pour m'éviter de chercher les infos, le projet devra impérativement être placé dans le répertoire

cp votreprojet /home/profs/zanotti/projetsU521

Le fichier exécutable s'appelera pix2D, le répertoire devra inclure un fichier LisezMoi qui contiendra toutes les informations nécessaires à l'utilisation de votre programme. Vos programmes doivent être commentés. Il faudra également inclure les fichiers TestBresenham et TestMichener qui contiendront l'étude pratique de la complexité de ces algorithmes comparativement aux tracés empiriques (utilisez le profiler gprof et tracez les courbes de complexité avec Gnuplot ou avec votre propre logiciel).

Liste des outils à implanter

  1. Table des couleurs: permet de sélectionner la couleur du tracé et du remplissage s'il y a lieu (il faut conserver la possibilité de ne pas remplir).
  2. Tracé de lignes brisées avec l'algorithme de Brensenham antialiasé: à chaque clic de souris, on trace le segment entre le dernier point et le nouveau.
  3. Tracé de cercles avec l'algorithme de Michener antialiasé: le premier clic correspond au centre du cercle, le second au rayon. Eventuellement, proposer une couleur de remplissage.
  4. Tracé de parallélogrammes: utiliser bresenham sur les 4 segments.
  5. Tracé de polygones convexes avec l'algorithme de détection de l'enveloppe polygonale convexe: Chaque clic de souris définit un sommet du polygone. Une fois tous les sommets validés, on détermine l'enveloppe polygonale avec l'algo vu en cours.
  6. Tracé de courbes de Bézier avec la méthode des polynômes de Bernstein et la méthode itérative
  7. Tracé de courbes de Bézier quadratiques passant par les points de contrôles (noeuds de jonction)
  8. Tracé de courbes B-splines uniformes (le vecteur noeud u0,u1,...,un+m+1est tel que |ui+1-ui|= cte).
  9. Coloriage d'une zone avec le pot de peinture: implanter l'algorithme récursif et colorier les pixels à une distance inférieure à un seuil fixé par l'utilisateur. Faites différents essais de distance dans l'espace RVB.
  10. Remplissage d'un polygone convexe: utiliser l'algorithme avec les listes.
  11. Pour les plus courageux: implanter la lecture et l'écriture des images dans des fichiers au format gif ou jpeg. Pour info: en jpeg, il y a une simple transformée en cosinus discret (c'est la transformée de Fourier simplifiée sur R) et un codage de Huffman déjà traité et programmé en cours d'algorithmique de licence.

Enveloppe convexe d'un ensemble de points

On note \((M_i)_{\in\ab{0}{n}}\) le nuage de points dont on veut calculer l'enveloppe convexe. L'algorithme décrit informellement est le suivant :

  1. On réindexe les points : \(M_0\) est celui qui a la plus petite abscisse parmi ceux qui ont la plus petite ordonnée.
  2. Les points suivants \(M_1,\ldots, M_n\) sont déterminés de manière à ce que la suite des angles formé par la demi-droite horizontale positive \([M_0\,x)\) issue de \(M_0\) et le segment \([M_0M_i]\) pour \(i\in\ab{1}{n}\) soit croissante. Si plusieurs points donnent le même angle, on ne conserve que celui qui est le plus éloigné de \(M_0\), les autres sont éliminés.
  3. On crée une pile P sur laquelle on empile successivement les trois premiers points \(M_0\), \(M_1\) et \(M_2\).
  4. On définit deux fonctions top(P) et under(P) qui renvoient respectivement le point au sommet de la pile P et celui juste en dessous sans modifier la pile. On note \(\langle ABC\rangle\) l'angle formé par les segments \([BA]\) et \([BC]\) . Cet angle est positif s'il suit le sens trigonométrique, négatif sinon.
  5. On termine en éliminant de la pile \(P\) tous les points qui sont strictement inscrits dans l'enveloppe convexe, en les dépilant. Ils sont caractérisés par l'angle \(\langle(under(P),top(P)\rangle \gt 0\). Une fois ce procéssus achevé, la pile ne contient plus alors que les points qui décrivent l'enveloppe convexe du nuage initial de points, et dans le sens trigonométrique.

L'algorithme complet est donné ici :

ALGORITHME Convexe(M,n):Liste;
DONNEES
   n: entier;     
   M[0,n]: tableau de n + 1 points;
VARIABLES
   P: liste de points du plan;
   i: entier; 
DEBUT
   M[0] ← PPO(M)     // PPO cherche le point de plus petite ordonnee
   Ordonner(M)       // cf. explications.
   P ← ∅             // initialisation de la pile P
   Empiler(M[0], P); // empilement des 3 premiers points
   Empiler(M[1], P);
   Empiler(M[2], P);
   POUR i ∈ [3:n] FAIRE  
      TQ (⟨under(P), top(P)⟩ > 0) FAIRE
         Depiler(P)  // point strictement dans l'enveloppe
      FTQ 
      Empiler(M[i], P); // point de l'enveloppe à conserver 
   FPOUR
   RENVOYER P;
FIN