Ombrage, Rendu et Description d`Image
Transcription
Ombrage, Rendu et Description d`Image
Analyse et Synthèse d'Images pour l'Interaction Homme Machine James L. Crowley DEA ISC 1999/00 Séance 6 : 16 novembre 1999 Plan de la Séance : Ombrage, Rendu et Description d’Image La Rendu de Formes en Maille.......................... 2 Rappel de l’Algorithme fillPoly2d() ......................................2 L'algorithme de Z-Buffer ................................. 4 Affichage des polygones en profondeur .................................6 Interpolation de la profondeur sur une arête...........................7 Calcul de l'intensité d'une surface ......................10 Cas simple : Ombrage constant sur un polygone.................... 10 Méthode de Gouraud.......................................................... 11 Estimation de la normale à des vertices................................ 11 Interpolation de l'intensité sur une arête............................... 12 Ombrage par Interpolation de Normales (Méthode de Phong).15 Description d’Image......................................17 Sources de contraste :......................................................... 18 Segmentation par seuillage.................................................. 19 Comment Choisir le seuil ?................................................. 20 Segmentation par les statistiques de la couleur ...................... 21 Application de la Règle de Bayes......................................... 22 Ombrage, Rendu et Description d’Image Séance 6 La Rendu de Formes en Maille Les techniques de la rendu des surfaces s’appui sur des variations de l’algorithme de remplissage de polygone. Rappel de l’Algorithme fillPoly2d() Le remplissage d’une polygone peut-être fait en deux étape : 1) composer une liste triée de colones d'intersection pour chaque ligne de l'image. Chaque paire de colones sur chaque ligne est un trait. 2) Tracer les très en appliquant une interpolation. Encodage en trait : y0 y1 x x x x x x x x x x x x x x x x 6-2 Ombrage, Rendu et Description d’Image Séance 6 Le Pseudo-code pour fillPoly2d() fillPoly2D(Poly2D P) { double x; int y; Atom * List[P.Ny]; /* tableau de traits, une liste per ligne Atom * L1, * L2; /* pour traiter les listes */ */ for (n=0; n<=P.Nsegs; n++) if (P.Seg[n].y1 == /* pour chaque segment du polygone */ P.seg[n].y2) /* cas special des aretes horizontal */ { insertSort(x1, List[y1]); /* le arete est un trait */ insertSort(x2, List[y1]); /* les sommets sont les limites de le trait */ } else /* cas de aretes non-horizontales */ for (y=P.Seg[n].ymin; y< P.seg[n].ymax; y++) { /* Pour chaque ligne */ /* du rectangle englobant de l'arete */ x = P.Seg[n].m * y + insertSort(x, List[y]); P.Seg[n].c ; /* calculer le colone et */ /* inserer le colone au liste */ } /* end for() */ /*affichage des traits */ for (k=0; k< P.Ny; k++) /* pour chaque ligne du rectangle englobante */ L1 = List[k].next; while (L1 != Null) { L2 = L1.next; /* draw un trait sur le ligne y = k+P.Seg[n].ymin */ drawTrait(L1.x, L2.x, k+P.Seg[n].ymin, Gray); L1 = L2.next; } } Un premier exemple est fourni par l’algorithme de Z- Buffer 6-3 Ombrage, Rendu et Description d’Image Séance 6 L'algorithme de Z-Buffer Problème : Eliminer l’affichage des surfaces cachés par d’autre surfaces. Ceci est fait après un “back-face cull” (vu la semaine dernière). ⇒ Principe : On créait une deuxième image, dans lequel on marque la valeur de "z" pour chaque pixel affiché. On n'affiche pas des pixels si leur z est derrière le pixel de jas affiché. Par exemple - pour chaque colonne : ymin ymax image Z Buffer (2 ou 4 octets per pixel) 6-4 Ombrage, Rendu et Description d’Image Séance 6 Nous prendrons la convention . z = MaxZ; Profondeur maximale. z = 0; plane de la rétine. Attention : dans certaines réalisations du z-buffer, la plane le plus profonde est notée comme z=0, avec z > 0 en approche de la caméra. L'affichage est géré par insertZ(); void insertZ(int x; int y; int z; int color;) { if (z < ZBuf[x][y] ) then /* if z is in front of current valeur de ZBuf */ { ZBuf[x][y] = z; /* then enter new value */ drawPixel(x, y, color) return true; } else return false; } void zBufferInit(void) { iamge * Zbuf = newByteImage(Xmax, Ymax); int x, y; for (y=0; y < Ymax; y++) for (x=0; x < Xmax; x++) Zbuf[x][y] = MaxZ; return Zbuf; } Calculation de z pour un polygone exprimé en repère Caméra Ax + By + Cz + D = 0 ⇒ z= –D – Ax – By C formule incrémentale : Si à (x, y), z = calculZ(x, y) alors à (x+∆x,y) A z2 = z – C (∆x) 6-5 Ombrage, Rendu et Description d’Image Séance 6 Affichage des polygones en profondeur Pour un polygone 3D, il faut calculer la profondeur (z) de chaque point afin de l'entrer dans un z buffer. Ceci peut-être fait à partir d'une interpolation linéaire des profondeurs des sommets. x1, z1 x x1 F z F x2, z2 z x2 F z Supposons que nous avons un polygone 3D composé d'une liste de sommets 3D en repère caméra. S3D = xc yc . zc 1 S2D = xcF/zc y F/z à la distance z. c c 1 La projection sur l'image se fait par division par F/z. xr xc = F zc ⇒ xr = xc zFc Lors de la projection, on associe la distance zc à chaque sommet 2D (xr, y r). Pour l'affichage, on fait une interpolation linéaire sur les arêtes et les traits. 6-6 Ombrage, Rendu et Description d’Image Séance 6 Interpolation de la profondeur sur une arête Lors de l'affichage, on fait un rasterisation pour chaque segment. z2 z z z1 y1 y y y2 La rasterisation utilise une interpolation linéaire en x. Au même temps, on fait une interpolation linéaire en z. z1 : profondeur au sommet S1. z2 : profondeur au sommet S2 z2–z1 mz : Change profondeur per ligne : mz = y –y 2 1 cz : interception de l'intensité : cz = z 1 – mz . y 1 Ensuite pour chaque ligne, y , de l'arête z = m z y + cz. Seg: int x1, y1, x2, y2; double m, c; /* les sommets 2D du segment */ / pente et intercept du segment */ int xmin, ymin, xmax, ymax ; double z1, z2, mz, cz.; /* rectangle englobant du segment */ /* parametres de l'interpolation en z */ Atom: double x; double z; Atom * next; 6-7 Ombrage, Rendu et Description d’Image Séance 6 L'algorithme d'interpolation est une extension de l'algorithme de fillPoly(); fillPolyZ(Poly2D P) { double x, z; int y; Atom * List[P.Seg[n].Ny]; for (n=0; n<=P.Nsegs; n++) if (P.Seg[n].y1 == /* pour chaque segment du polygone */ P.seg[n].y2) /* cas special des segment horizontal */ { /* le segment est un trait */ insertSort(P.Seg[n].x1, P.Seg[n].z1, List[P.Seg[n].y1]); insertSort(P.Seg[n].x2, P.Seg[n].z2, List[P.Seg[n].y1]); } else /* cas des autre segments */ for (y=P.Seg[n].ymin; y< P.seg[n].ymax; y++) /* Pour chaque ligne du seg */ { x = P.Seg[n].m * y z = + P.Seg[n].c ; P.Seg[n].mz * y + P.Seg[n].cz; insertSort(x, z, List[y]); /* calculer le colone et */ /* calculer la profondeur */ /* Inserer le colone au liste */ } for (k=0; k< P.Ny; k++) /* pour chaque ligne du rectangle englobante */ L1 = List[k].next; while (L1 != Null) { L2 = L1.next; /* draw un trait sur le ligne y = k+P.Seg[n].ymin */ drawZTrait(L1.x, L1.z, L2.x, L2.z, k+P.Seg[n].ymin, gray); L1 = L2.next; } } 6-8 Ombrage, Rendu et Description d’Image Séance 6 Pour afficher le trait, il faut une interpolation entre les profondeurs. /* affichage d'un trait sur une ligne */ drawZTrait (double x1; double z1; double x2; double z2; int y; int gray) { int z; double mz= (z2–z1)/(x2–x1); /* pente de l'intensité */ double cz = z1–mz*x1; /* intercept de l'intensité */ int c1 = floor(x1) + 1; /* round up colone 1 */ int c2 = floor(x2); /* round down colone 2 */ for (x=c1; x<=c2; x++) { z = round( i*mz+z1); insertZ(x, y, z, gray); } } 6-9 Ombrage, Rendu et Description d’Image Séance 6 Calcul de l'intensité d'une surface Rappel : Pour la reflectance Lambertian : Lumiere Camèra N V e → Pour une surface lambertienne : N L i . L→ = Cos(i). Cas simple : Ombrage constant sur un polygone. Pour un maillage de polygones 3D, il est tres simple de calculer un ombrage. Le cosinus entre la normale de la surface et une source de la lumière. Ceci repose sur les hypothèses que : → 1) la source est à l'infini. N . L→ → est constant. → 2) l'observateur est à l'infini : N . V est constant. 3) Le polygone est le veritable surface. L'intérêt est qu'on calcule une seule couleur pour le remplissage de chaque polygone. L'inconvénient est qu'il y a des discontinuités à chaque arête. Ces discontinuités sont amplifiées par une illusion visuelle : L'effet de Mach. On peut éliminer ces discontinuités par une interpolation de l'ombrage. 6 - 10 Ombrage, Rendu et Description d’Image Séance 6 Méthode de Gouraud La méthode de Gouraud consiste à interpoler l'ombrage sur chaque trait d'un polygone par un calcul différentiel. Il s'agit d'une approximation permettant d'éliminer les discontinuités d'ombrage aux arêtes. Estimation de la normale à des vertices L'algorithme de Gouraud détermine une intensité aux sommets en utilisant les normales et une réflexion Lambertienne. Il fait une interpolation de l'intensité sur les arêtes. Les intensités calculées sur les arêtes sont utilisées afin de faire une interpolation sur les traits. L'algorithme de Gouraud demande des valeurs des normales aux sommets de chaque polygone. Cependant, les sommets et les arêtes sont des discontinuités en normale! il n'y a pas de valeurs stables. Gouraud propose une approximation par un normale "moyenne" calculer avec les normales des faces qui incluent le sommet. 1 K-1 → N s= K ∑ N k k=0 → A partir de la normale de chaque sommet, on calcule une intensité par réflexion Lambertienne. → → g = ρL( N s . L ) On projet les sommets (3D) du polygone 3D vers l'image, afin d'obtenir des sommets 2D d'un polygone 2D. On obtient ainsi un polygone 2-D composé de N sommets 2-D. A chaque sommet, on associe sa "z" et son intensité, g. z et g sont ensuite interpolé sur l’arêtes et les traits. 6 - 11 Ombrage, Rendu et Description d’Image Séance 6 Interpolation de l'intensité sur une arête Pour l'affichage, on fait un rasterisation pour chaque ligne du rectangle englobant. g2 g g g1 y1 y y y2 Pour ceci il faut ajouter des paramètres d'intensité et de pente et interception aux segments du polygone 2D : g1 : g2 : Intensité au sommet S1. Intensité au sommet S2 g2–g1 Change d'intensité par ligne : mg = y –y 2 1 Interception de l'intensité : cg = g 1 – mg y 1 mg : cg : Ensuite pour chaque ligne, y , de l'arête gy = mg y + cg. Seg: int x1, y1, x2, y2; double m, c; /* les sommets 2D du segment */ / pente et intercept du segment */ int xmin, ymin, xmax, ymax ; double g1, g2, mg, cg; /* /* rectangle englobant du segment */ parametres pour l'interpolation de l'intensité */ On ajoute l'intensité aux traits, afin de faire une interpolation : 6 - 12 Ombrage, Rendu et Description d’Image Séance 6 L'algorithme de Gouraud est une extension de l'algorithme de fillPoly() semblable à l'interpolation en profondeur. Atom: double x; double z; int g; Atom * next; fillGouraud(Poly2D P) { double x, z; int g, y; for (n=0; n<=P.Nsegs; n++) if (P.Seg[n].y1 == /* pour chaque segment du polygone */ P.seg[n].y2) /* cas special des segment horizontal */ { /* le segment est un trait */ insertSort(P.Seg[n].x1, P.Seg[n].z1, P.Seg[n].g1, List[P.Seg[n].y1]); insertSort(P.Seg[n].x2, P.Seg[n].z2, P.Seg[n].g1, List[P.Seg[n].y1]); } else /* cas des autre segments */ for (y=P.Seg[n].ymin; y< P.seg[n].ymax; y++) /* Pour chaque ligne du seg */ { x = P.Seg[n].m * y + P.Seg[n].c ; /* calculer le colone et */ z = P.Seg[n].mz * y + P.Seg[n].cz; /* calculer la profondeur */ g = (int) P.Seg[n].mg * y + P.Seg[n].cg; /* calculer la intensité */ insertSort(x, z, g, List[y]); /* Inserer le colone au liste */ } for (k=0; k< P.Ny; k++) /* pour chaque ligne du rectangle englobante */ L1 = List[k].next; while (L1 != Null) { L2 = L1.next; /* draw un trait sur le ligne y = k+P.Seg[n].ymin */ drawZTrait(L1.x, L1.z, L1.g, L2.x, L2.z, L1.g, k+P.Seg[n].ymin); L1 = L2.next; } } 6 - 13 Ombrage, Rendu et Description d’Image Séance 6 Pour afficher le trait, il faut une interpolation entre les intensités. drawZTrait (double x1; double z1; double g1; double x2; double z2; double g2; int y;) { int g; int z; double mz= (z2–z1)/(x2–x1); double cz = z1–mz*x1; double mg= (g2–g1)/(x2–x1); double cg = g1–mg*x1; /* pente de l'intensité */ /* intercept de l'intensité */ /* pente de l'intensité */ /* intercept de l'intensité */ int i1 = floor(x1) + 1; /* round up x1 */ int i2 = floor(x2); /* round down x2 */ for (i=i1; i<=i2; i++) { g = round ( i*mg+g1); z = round( i*mz+z1); insertZ(x, y, z, g); } } Intérêt de l'ombrage par interpolation : Rapidité Problème avec l'ombrage par interpolation : 1) approximation "grossière". 2) Ne permettent pas de calculer les reflets. 6 - 14 Ombrage, Rendu et Description d’Image Séance 6 Ombrage par Interpolation de Normales (Méthode de Phong). La méthode de Phong remplace l'interpolation de l'intensité par l'interpolation des normales du polygone. L'intensité est calculée pour chaque pixel à partir de sa normale, N. Comme avec Gouraud, on estime une normale "moyenne" pour chacun sommet 3D. K → N s= ∑ → N k k=0 Pour chaque sommet 2D du POly2D, on associe sa normale : Seg: int x1, y1, x2, y2; double m, c; /* les sommets 2D du segment */ / pente et intercept du segment */ int xmin, ymin, xmax, ymax ; /* rectangle englobant du segment */ double a1, b1, c1; /* normale du sommet s1 */ double a2, b2, c2; /* normale du sommet s1 */ double ma, mb, mc; double ca, cb, cc; /*pente du normal sur le segment */ /*intercepte du normal sur le segment */ En effet, il s'agit d'une interpolation linéaire sur A, B, et C en fonction de y. pente : a2–a1 ma = y –y 2 1 intercepte : ca = a 1 – ma y 1 b2–b1 mb = y –y 2 1 cb = b 1 – mb y 1 c2–c1 mc = y –y 2 1 cc = c 1 – mc y 1 6 - 15 Ombrage, Rendu et Description d’Image Séance 6 Le reste de l'algorithme est la même. On ajoute les paramètres, A, B, C aux éléments des traits : élément : X, A, B, C. Puis insertSort est insertSort(x, a, b, c, List[y]); Puis en drawTrait, on a drawZTrait(List[k].x, List[k].z, List[k].a, List[k].b, List[k].c, List[k+1].x, List[k].z, List[k+1].a, List[k+1].b, List[k+1].c, y); drawZTrait ( double x1; double a1; double b1; double b1; double x2; double a2; double b2; double b2; int y;) { int a, b, c, z; double mz= (z2–z1)/(x2–x1); double cz = z1–mz*x1; /* pente de l'intensité */ /* intercept de l'intensité */ double ma= (a2–a1)/(x2–x1); /* pente de A */ double mb= (b2–b1)/(x2–x1); /* pente de B */ double mc= (c2–c1)/(x2–x1); /* pente de C */ double ca = a1–ma*x1; /* intercept de A */ double cb = b1–mb*x1; /* intercept de B */ double cc = c1–mc*x1; /* intercept de C */ int c1 = floor(x1) + 1; /* round up colone 1 */ int c2 = floor(x2); /* round down colone 2 */ for (x=c1; x<=c2; x++) { a = i*ma+a1; b = i*mb+b1; c = i*mc+c1; z = round( i*mz+z1); insertZ(x, y, z, a, b, c); } } 6 - 16 Ombrage, Rendu et Description d’Image Séance 6 Description d’Image Rôle des Indices d'image en Vision Description Symbolique Description Géometrique Indices Invariants (monde extérieure) La description du monde extérieur est basée sur les "Invariants" Les "phénomènes" observables restent stables quels que soient le point de vue et la luminosité ambiante. La première phase d'un processus de vision est l'extraction des "Indices" Les Indices d'image servent à : 1) Réduire la quantité d'informations (coût mémoire, calcul et communication) 2) Simplifier l'interprétation Le système visuel humain de perçoit mal l’intensité absolue d’une image. Une image uniforme grise resemble une image uniforme blanche. Ceci est dû au fait que l’intensité absolue varie selon l’éclairage. (Ce n’est pas un “invariant”.) Le système visuel humaine perçoit les structures de changement en intensité. Ce changement s’appelle le contraste. 6 - 17 Ombrage, Rendu et Description d’Image Séance 6 Sources de contraste : Les contours de contraste sont supposés êtres stables par rapport au point de vue et à l'éclairage. En effet, les sources de contraste dans une image sont : 1) Changement d'orientation d'une surface (Discontinuité en profondeur) 2) Bordure d'un ombrage 3) Marquage d'une surface (ex: écriture sur une feuille) 4) Reflets Le premier est utile pour les objets polyédriques. Le deuxième peut indiquer la forme 3D si on sait l'interpréter. Le troisième ne dépend pas de la forme, mais peut fournir une texture Le quatrième dépend du point de vue et de l'éclairage. 6 - 18 Ombrage, Rendu et Description d’Image Séance 6 Segmentation par seuillage Segmentation : Le découpage d’une image en région élémentaire. Les régions sont supposées de correspond aux objets d’intérêt. Les algorithmes de segmentation recherche les régions uniformes. La technique la plus simple de segmentation est de comparer les pixels à un seuil. Considère la ligne J, de l'image g(i, j). niveau de Gris 255 Seuil 0 512 Colonne 0 On remplace chaque pixel par une valeur binaire b(i, j) := niveau de Gris 1 si g(i, j) ≥ Seuil 0 Sinon 255 Seuil 0 0 512 Colonne La bordure donne les zones de contraste. pres on peut appliquer un codage en traits aux lignes. Segmentation par seuillage est bien adaptée aux problèmes de l’analyse d’images 2D tel qu’images de satellite et certaines images médicales ou le signal est constant. 6 - 19 Ombrage, Rendu et Description d’Image Séance 6 Il est très mal adapté à la vision des objet 3-D dans un environment naturel. Comment Choisir le seuil ? Le seuil peut être déterminé à partir d’un histogramme. En théorie, les modes de l’histogramme correspondent aux régions. Les seuils sont à placer dans les vallées. Nombre des Pixels 0 255 0 Niveau de gris Seuil Pratique : ou 1) choisir le seuil "interactivement" 2) Calculer une image de probabilité 6 - 20 Ombrage, Rendu et Description d’Image Séance 6 Segmentation par les statistiques de la couleur Un histogramme de couleur peut fournir une densité de probabilité afin de trouver les pixels qui sont les images d'un objet, par exemple, la peau humaine. La Luminance, L=R+V+B décrit la forme 3D d'une surface. On peut simplifier l'histogramme par une normalisation de la luminance. Pour chaque pixel P(i,j) = [R, V, B], on normalise par la luminance : R r = R+V+B V v = R+V+B Calcul d’un histogramme des valeurs h(r, v). On alloue un tableau 2D de taille Nh (exemple 32 x 32 = 1024 cellules : h(r, v). ) r(i, j) Pour chaque pixel C = c(i, j) = v(i, j) , on incrémente la cellule de l'histogramme qui correspond à (r, v) h(r, v) := h(r, v) +1 Un histogramme des couleurs, h(C), de N pixels donne une approximation de la probabilité de chaque couleur p(C) = 1 Lim { N N→ ∞ h(C)} Un histogramme des de couleurs, htot(C), de les Ntot pixels dans une l'image donne une approximation de la probabilité de chaque couleur dans l'image. 1 p(C) ≈ N tot htot(C) Un histogramme des de couleurs d’un objet , ho(C), de les No pixels dans une région d'une image de l'objet, w(i, j), donne une approximation de la probabilité de chaque couleur de l'objet . 1 p(C | objet ) ≈ N o ho(C) 6 - 21 Ombrage, Rendu et Description d’Image Séance 6 Application de la Règle de Bayes. L histogramme permet d'utiliser la règle de Bayes afin de calculer la probabilité qu'un pixel corresponde à un objet. p(objet) p(objet | C) = p( C | objet ) p(C) Soit M images de N pixels. Ceci fait Ntot Pixels. Soit htot(r, v), l'histogramme de tous les Ntot pixels. Soit ho(r, v), l'histogramme des No pixels de l'objet "o". No p(objet) = N tot 1 p(C) = N htot(r, v) tot 1 p(C | objet ) = N ho(C) o Donc p(objet) p(objet | C) = p( C | objet ) p(C) No N tot 1 = N ho(C) 1 o Ntot h tot(C) ho(C) p(objet | C) = h (C) tot Cependant, il faut assurer que htot(C) ≠ 0 !! Pour cela il faut que Ntot >> N h (nombre de cellules de l'histogramme.). Ainsi, on peut créer une image de probabilité de l'objet. En peut ensuite déterminer un seuil pour l’image de probabilité. Image d’un visage Image de probabilités de la peau Image binaire des probabilités après seuillage 6 - 22