Vous êtes sur la page 1sur 14

La couleur

Informations sur le tutoriel


Auteurs : inconnu259 et NoHaR Difficult : Licence : Plus d'informations

Popularit
Visualisations : 8 571 Apprciation 2 des lecteurs :3 2 53 91 personnes souhaitent voir ce tutoriel publi en livre ! Vous aussi ?

Historique des mises jour


Le 28/10/2009 22:28:20 Version zCorrige Le 22/08/2009 14:54:47 Correction d'un bug concernant des parties manquantes Le 26/07/2009 22:42:11 Test

Partager
Imprimer Convertir en PDF Email Twitter Facebook Plus de services Vous l'attendiez, il est l, tout chaud, tout beau : le chapitre sur la couleur ! Dans ce chapitre, nous allons apprendre comment sont reprsentes les couleurs des images numriques, et voquer quelques gnralits utiles du point de vue thorique. Mais l o vous allez surtout tre servis, c'est dans la partie pratique, o nous apprendrons grer la couleur avec OpenCV, avant de raliser un superbe TP qui vous montrera comment fonctionne le chroma-keying. Vous pouvez remercier chaleureusement Xhtml_boys et lui souhaiter la bienvenue dans l'quipe de rdaction de ce tutoriel : ceci est son premier chapitre, et grce lui, vous allez dsormais pouvoir prsenter la mto dans votre salon ! Sommaire du chapitre :

[Thorie] Les espaces de couleur [OpenCV] La couleur [ TP 7 ] Segmentation d'un objet par sa couleur Correction du TP Q.C.M.

[Thorie] Les espaces de couleur


En imagerie numrique, on peut reprsenter les couleurs, dans ce que l'on appelle des espaces de couleur diffrents. Mais c'est quoi, un espace de couleur ?

Un espace de couleur (ou modle de couleur ), c'est un modle mathmatique abstrait reprsent par un tuple de couleurs (de trois ou quatre valeurs, appeles composantes ). En fait, les couleurs sont dcrites au moyen de vecteurs appartenant de tels espaces. Je vous rassure immdiatement : il est inutile de faire ce point du tutoriel une initiation aux espaces vectoriels, celle-ci serait superflue. Vous n'avez pas vraiment besoin de comprendre formellement cette notion pour utiliser les couleurs en VPO. Retenez simplement qu'une couleur se dcrit par un groupe de 3 ou 4 valeurs, suivant le modle utilis. Le plus connu et le plus usit de ces espaces reste sans aucun doute le modle RVB, ses composants de couleur sont le Rouge, le Vert, et le Bleu (en anglais, Red Green Blue, RGB). Un autre qui vous sera quasiment indispensable est le TSV, compos de la Teinte, Saturation, Valeur (brillance de la couleur). (En anglais, Hue Saturation Value, HSV.) Nous nous contenterons d'tudier ces deux espaces-l, puisque ceux-ci sont utiliss de manire largement majoritaire par rapport aux autres espaces. Mais attends, arrte-toi deux minutes ! Pourquoi avoir fait plusieurs modles de couleur ?!

Il n'y a pas trente-six mille raisons : chacun a son utilit, ses qualits et ses dfauts. C'est justement l'occasion pour moi de vous expliquer la problmatique principale de l'utilisation de la couleur en vision.

Luminance et chrominance...
L'espace RGB est trs simple dcrire. Imaginez que la lumire soit construite uniquement partir de rayons Rouges, Verts et Bleus. Chaque rayon de lumire sera alors un mlange (un peu comme en peinture) de ces couleurs. En saturant les trois composantes, on obtiendrait du blanc, et en les fermant compltement, nous n'aurions plus que du noir. Pour dcrire ces composantes de lumire, nous allons utiliser des niveaux . Jusqu'ici, vous avez manipul des niveaux de gris, qui dcrivaient la luminosit de la lumire (la luminance ). Dornavant, vous pourrez manipuler des niveaux de rouge, de vert et de bleu, qui dcriront la fois la luminance, mais aussi l'information de couleur porte par la lumire (la chrominance ).

L'espace RGB modle la fois la luminance et la chrominance de la lumire

Bien que cet espace soit normment utilis en informatique, il s'avre, du point de vue de la vision, qu'il possde un gros dfaut : RGB dcrit simultanment la luminance et la chrominance. Or ces deux informations sont pourtant bien distinctes : il serait quand mme plus utile de faire en sorte qu'elles soient spares de manire tre dcrites indpendamment l'une de l'autre, ne serait-ce que pour tudier plus finement les variations de couleur. L'espace HSV fait justement cette sparation. Les deux premires composantes du modle HSV dcrivent la chrominance : il s'agit, de la teinte ( rouge ? vert ? jaune ? orange ? ) ainsi que de la puret ( rose ptant ? ou terne ? ) de la couleur. Enfin, la troisime composante (la Valeur), dcrit la luminance ( clair ou fonc ? ). Cette sparation entre luminance et chrominance nous permet, en thorie (en pratique, c'est une autre histoire...) de pouvoir reconnatre les objets par leur couleur, peu importe les conditions d'clairage.

Schma de l'espace HSV

Pour mieux vous faire une ide du fonctionnement de l'espace HSV, prenons un parallle avec la peinture. La Teinte , c'est la couleur pure de votre peinture. Lorsque vous diluez cette peinture colore pure dans du gris, vous ajustez alors la Saturation de la couleur dans la lumire : plus vous mettez de couleur, plus celle-ci sera pure. Enfin, la clart du gris dans lequel vous diluez cette peinture colore est dcrite par la Valeur . Pour que vous puissiez visualiser un changement d'espace de couleur, le chat de NoHaR, Yoji, a pos pour vous :

Cliquez pour agrandir ! Sur cette image, vous pouvez voir l'image originale (dans l'espace BGR, semblable RGB, qu'utilise OpenCV), sa version RGB, ainsi que sa version HSV. Vous remarquerez (au-del des couleurs bizarres) que ce ne sont pas du tout les mmes informations qui sont mises en valeur entre l'espace HSV et les deux autres.

Les petites taches que vous pouvez observer sur la troisime image sont dues la compression JPEG de la photo.

[OpenCV] La couleur
Maintenant, nous allons voir tout ce que je vous ai dit, mais cette fois, avec OpenCV. Avant tout, il est une chose trs importante noter. Dans OpenCV, les couleurs sont reprsentes par dfaut en BGR.

Cela signifie que pour OpenCV, les canaux bleu et rouge sont inverss. Ainsi, lorsque vous aurez jou avec une image, si vous voulez l'afficher de manire "naturelle", pensez la reconvertir de son espace actuel vers le BGR. Si vous n'effectuez pas cette reconversion, vous obtiendrez alors des images similaires ce que vous avez pu observer sur Yoji.

Changer d'espace de couleur


Tout d'abord, sachez que les IplImage sont par dfaut en BGR. Voyons maintenant la fonction de conversion. void cvCvtColor( const CvArr* src, CvArr* dst, int code );

src : l'image que vous voulez transformer ; dst : c'est la structure qui va recevoir l'image convertie ; code : c'est une constante qui va dfinir le type de conversion.

H, une image, c'est pas un IplImage ? Car l, je vois cvArr.

Eh bien, cvArr, c'est tout simplement un typedef de void . Citation : Documentation CxCore CvArr Tableau arbitraire typedef void CvArr; Le metatype CvArr* est utilis uniquement comme paramtre de fonction pour spcifier que la fonction accepte plus de structures (tel que IplImage*, CvMat* ou mme CvSeq*) que si on prcisait le type. La structure passe sera dtermine l'excution par l'analyse des quatre premiers octets de l'entte.

Pour information, le Arr dans CvArr est tout simplement un diminutif de Array . Et pour la variable code, on va mettre quoi comme valeur ?

Ah, mais laisse-moi finir, mon bon ami ! C'est une constante qui prend la forme suivante : CV_<Espace de couleur de l'image source>2<Espace de couleur de l'image de destination>. Voici deux-trois exemples de constantes :

BGR -> RGB : CV_BGR2RGB BGR -> HSV : CV_BGR2HSV HSV -> BGR : CV_HSV2BGR

Les canaux
Vous vous souvenez de ce que je disais propos des espaces de couleur ? Je vous parlais de tuples de couleurs avec trois ou quatre valeurs gnralement. Eh bien, les canaux sont les dimensions de l'espace de couleur. Jusqu' maintenant, nous travaillions avec un seul canal (une seule valeur pour la couleur), parce que nous n'avions besoin que du niveau de gris, mais dornavant nous en utiliserons trois.

La profondeur
La profondeur d'une image est le type de donnes qui reprsente un pixel ou un scalaire. La profondeur de nos images tait jusqu' prsent 8U , signifiant que les nombres avec lesquels on remplit notre structure IplImage taient toujours des entiers positifs (U = Unsigned) cods sur 8 bits : c'est--dire des nombres compris entre 0 et 255. Elle est contenue dans la variable depth, dans notre IplImage. Les valeurs que l'on peut donner sont sous forme de constantes, en voici la liste exhautive :

IPL_DEPTH_8U : nous l'utilisons depuis le dbut du tutoriel, elle signifie entiers positifs sur 8 bits ; IPL_DEPTH_8S : entiers positifs ou ngatifs sur 8 bits ; IPL_DEPTH_16U : entiers positifs sur 16 bits ; IPL_DEPTH_16S : entiers positifs ou ngatifs sur 16 bits ; IPL_DEPTH_32S : entiers positifs ou ngatifs sur 32 bits ; IPL_DEPTH_32F : float ; IPL_DEPTH_64F : double.

Parcourir une image et la modifier


Mesdames, Messieurs, j'ai une excellente nouvelle vous annoncer : c'est quasiment la mme chose qu'avant ! La seule diffrence, c'est qu'avant, nous travaillions sur un seul canal, et nous modifiions le scalaire sur une valeur, val[0]. Dornavant, nous travaillerons sur trois canaux, nous modifierons le scalaire obtenu grce cvGet2D sur trois valeurs, val[0], val[1], val[2]. Vous voulez une image toute bleue ? C'est parti, alors ! Je suppose que vous comprendrez sans problme ce code-ci : Code : C++ - Slectionner

#include <opencv/cv.h> #include <opencv/highgui.h> int main() { IplImage *img = cvCreateImage(cvSize(800, 200), IPL_DEPTH_8U, 3); //3 canaux cvNamedWindow("img", CV_WINDOW_AUTOSIZE); CvScalar scalaire; scalaire.val[0] = 255; //val[0] = Bleu scalaire.val[1] = scalaire.val[2] = 0; //val[1] = Vert, val[2] = Rouge for(int x=0; x < img->width; x++) { for(int y=0; y < img->height; y++) { cvSet2D(img, y, x, scalaire); } } cvShowImage("img", img); cvWaitKey(0); cvReleaseImage(&img); return 0; }

Une petite remarque : tant donn que nous voulons une image monochrome, je mets la couleur dsire directement dans le scalaire, et dans la boucle, je ne fais que modifier le pixel (x, y). Le fonctionnement reste le mme qu'avant, pour obtenir le scalaire d'un pixel (cvGet2D). Remarque 2 : pour remplir une image ou une partie d'image de faon monochrome, il est mieux d'utiliser la fonction cvSet qui a pour prototype : void cvSet( CvArr* dst, CvScalar couleur, const CvArr* masque=NULL );

src : image remplir ; couleur : le scalaire de la couleur avec lequel on remplit ; masque (optionnel) : si prcis, cet argument sert de pochoir dans le sens o il nous permet d'ignorer une partie de l'image. Pour ceux qui connaissent, c'est un petit peu le mme rle que le stencil buffer d'OpenGL.

Vous voyez, ce n'tait pas si terrible que a ! Utilisons maintenant ces nouvelles fonctions en TP.

[ TP 7 ] Segmentation d'un objet par sa couleur


Dans ce TP, nous allons caractriser (segmenter, comme on dit) un objet par sa couleur. Nous allons produire un masque : les objets ayant une couleur trs proche (selon un seuil dfini) ou identique la couleur recherche seront affichs en noir sur le masque, le reste en blanc. Un TP 2 sera enfin propos en fin de partie, il reprendra le code du TP 1, dans un but prcis, utilis dans la mto par exemple : le chroma-keying.

Partie 1
Pour ce TP, nous avons besoin d'une connaissance supplmentaire : dtecter le clic de la souris. Il suffit de crer une fonction du nom que l'on veut de cette faon (tous les arguments doivent tre prsents, sans quoi la compilation chouera) : Code : C++ - Slectionner

void maFonction(int event, int x, int y, int flags, void *param=NULL) { if(event == CV_EVENT_LBUTTONUP) //Clic gauche { //Si clic de la souris il y a eu } }

Et appeler la fonction cvSetMouseCallback() dans la fonction main() de votre programme : cvSetMouseCallback("ma fentre", maFonction); Le premier argument correspond la fentre sur laquelle le clic dclenche le callback. Le second argument correspond la fonction callback : passez le nom comme une variable, pas de guillemets ! D'une faon plus gnraliste, cette mthode permet plusieurs vnements de la souris, d'o la condition. Nous verrons cela au chapitre suivant. Je vais donc expliquer les dtails du TP. Au dmarrage, nous chargeons une image quelconque, on la convertit en HSV dans une autre image, et on cre une image 1 canal (niveau de gris) qui sera le masque. chaque clic de la souris, l'algorithme de comparaison sera excut, avec la couleur du pixel cliqu. Une tolrance (rglable avec un trackbar) doit tre prise en compte dans la comparaison. Je conseille personnellement de crer une macro pour voir si les valeurs du pixel analys sont prises en compte ou non. Si oui, le pixel correspondant dans l'image masque sera noir. Sinon, il sera blanc. Vous afficherez donc dans trois fentres : l'image d'origine, sur laquelle on peut cliquer, l'image HSV, et le masque. Bonne chance !

Partie 2
Le chroma-keying est une technique consistant remplacer tous les pixels d'une certaine couleur par une image. la mto par exemple, la carte que l'on voit la tlvision avec la prsentatrice devant, est un cran bleu que l'on remplace ensuite par les images voulues. Pour continuer notre TP, vous allez prendre une image de la mme taille de celle d'origine. Au niveau code, il va falloir crer une nouvelle image, ouvrir celle que vous venez de dnicher, et parcourir l'image masque. Si le pixel est noir, vous remplacez par votre dernire image externe au programme, sinon, par l'image d'origine. Affichez le rsultat, et admirez.

Correction du TP
Partie 1 : La segmentation d'un objet par sa couleur
Tout d'abord, on inclut openCV (a aide Code : C++ - Slectionner #include <opencv/cv.h> #include <opencv/highgui.h> #include <iostream> int main(int argc, char **argv) { ) et l'on commence le main() :

tant donn qu'il y a utilisation de trackbars, le plus simple est de dclarer nos IplImage hors du main, aprs nos inclusions : Code : C++ - Slectionner // Inclusions IplImage *img; IplImage *hsv; IplImage *masque;

Et dans le main(), on vrifie qu'il y a le nom de l'image ouvrir en argument, on leur donne leurs valeurs :

normal : on ouvre l'image passe en argument ; hsv : on passe normal en HSV ; masque : on cre une image vierge avec un canal, tant donn qu'il ne prend que deux couleurs : noir ou blanc.

Code : C++ - Slectionner if(argc < 2) { std::cout << "Vous devez prciser le nom de l'image en argument !" << std::endl; return 10; } normal = cvLoadImage(argv[1]); if(normal == NULL) { std::cout << "Le chargement de l'image \"" << argv[1] << "\" a chou !" << std::endl return 20; } hsv = cvCloneImage(normal); // On copie l'image cvCvtColor(normal, hsv, CV_BGR2HSV); // BGR -> HSV masque = cvCreateImage(cvSize(normal->width, normal->height), normal->depth, 1); cvSet(masque, cvScalar(255)); // Par dfaut, on a rien trouv, on n'a pas eu la couleur.

Maintenant, on peut crer des fentres et y afficher les images : Code : C++ - Slectionner cvNamedWindow("normal", CV_WINDOW_AUTOSIZE); cvNamedWindow("hsv", CV_WINDOW_AUTOSIZE); cvNamedWindow("masque", CV_WINDOW_AUTOSIZE); cvShowImage("normal", normal); cvShowImage("hsv", hsv); cvShowImage("masque", masque);

Pour choisir la couleur, on va assigner une fonction souris les vnements de la fentre normal : Code : C++ - Slectionner cvSetMouseCallback("normal", souris);

On met le programme en pause en attendant un appui du clavier, et on termine le main() : Code : C++ - Slectionner cvWaitKey(0); return EXIT_SUCCESS; }

On passe maintenant la fonction souris, qui est appele chaque vnement de la souris ( placer avant la fonction main()). Code : C++ - Slectionner

void souris(int event, int x, int y, int flags, void *param=NULL) { if(event == CV_EVENT_LBUTTONUP) // Si c'est le bouton gauche qui est relch { CvScalar scal; // On cre un scalaire scal = cvGet2D(hsv, y, x); // On prend le pixel quivalent celui cliqu SUR l h = scal.val[0]; // On prend les couleurs s = scal.val[1]; // Dans des variables v = scal.val[2]; // Que l'on va dfinir juste aprs actualiser(); // Et on actualise (fonction qui excute l'algorithme de la segme } }

Pour les variables h, s et v, on les dfinit aprs les IplImage : Code : C++ - Slectionner // IplImage... int h = 0, s = 0, v = 0;

Il ne reste plus qu' crer la fonction actualiser, pilier central du programme. Code : C++ - Slectionner

void actualiser() { CvScalar scal; //On cree un scalaire for(int x=0; x < hsv->width; x++) // Tant que l'on n'a pas fini de parcourir l'image HS { for(int y=0; y < hsv->height; y++) // Tant que l'on n'a pas fini de parcourir l { scal = cvGet2D(hsv, y, x); // On rcupre la couleur dans l'image HSV. if(seuil(scal.val[0], h) && seuil(scal.val[1], s)) // Macro que l'on v { scal.val[0] = 0; // Pixel noir, car c'est OK. } else { scal.val[0] = 255; // Pixel blanc, car ce n'est pas la bonne c } cvSet2D(masque, y, x, scal); // On modifie l'image masque. } } cvShowImage("masque", masque); // On actualise le masque dans la fentre. }

Et la macro qui vrifie si l'lment analys de la couleur HSV est de la bonne valeur : Code : C++ - Slectionner #define seuil(nb, cherche) (nb == cherche)

Vous pouvez tester votre programme sur cette charmante demoiselle, par exemple, en cliquant sur le mur bleu qui est derrire :

Mais c'est nul, ton truc, a marche pas !

C'est un peu normal : pour l'instant, nous cherchons une couleur qui aurait exactement les mmes valeurs, ce qui est souvent dur avoir, sur une image naturelle . Pour que la lumire ne soit pas un lment drangeant, nous allons laisser une tolrance sur la couleur. Nous allons utiliser un trackbar pour la rgler. Tout d'abord, la macro change : Code : C++ - Slectionner #define seuil(nb, cherche, tol) (nb < (cherche+tol) && nb > (cherche-tol))

Prenons un exemple : la teinte que l'on cherche est 120, la tolrance est 10. Si le nombre que l'on teste est 100, ce sera faux, car 100 < (120-10). Si le nombre que l'on teste est 112, ce sera bon, car 112 > (120-10) et 112 < (120+10). Maintenant, il faut modifier la condition qui utilise la macro : Code : C++ - Slectionner if(seuil(scal.val[0], h, tol) && seuil(scal.val[1], s, tol))

Et on cre l'entier tol et le trackbar "Tolrance". On va modifier la ligne int h = 0, s = 0, v = 0; en : Code : C++ - Slectionner int h = 0, s = 0, v = 0, tol = 10;

Dans le main, il suffira de rajouter avant l'attente d'un appui du clavier : Code : C++ - Slectionner cvCreateTrackbar("Tolrance ", "masque", &tol, 255, changementTol);

Et aprs souris() : Code : C++ - Slectionner

void changementTol(int val) { tol = val; actualiser(); }

Si vous avez suivi, vous aurez un code ressemblant celui-ci : Secret (cliquez pour afficher)

Hmm, j'ai pas entendu ? Ah, vous voulez un screenshot !

En cliquant sur le bleu

Partie 2 : Le chroma-keying
Ce TP tait trs facile, dans le sens o ce n'est que quelques lignes ajoutes au prcdent. Il servait plus exploiter le masque qu' autre chose ! Il n'a rien de bien compliqu, je vais donc vous fournir directement le code, en surlignant les lignes nouvelles par rapport au prcdent TP. Code : C++ - Slectionner

#include <opencv/cv.h> #include <opencv/highgui.h> #include <iostream> #define seuil(nb, cherche, tol) (nb > (cherche-tol) && nb < (cherche+tol)) IplImage IplImage IplImage IplImage IplImage *normal; *hsv; *masque; *ck; *resultat;

int h=0, s=0, v=0, tol=10; void actualiser() { CvScalar scal; for(int x=0; x < hsv->width; x++) { for(int y=0; y < hsv->height; y++) { scal = cvGet2D(hsv, y, x); if(seuil(scal.val[0], h, tol) && seuil(scal.val[1], s, tol)) { scal.val[0] = 0; if(x < ck->width && y < ck->height) cvSet2D(resultat, y, x, cvGet2D(ck, y, x)); } else { scal.val[0] = 255; if(x < ck->width && y < ck->height) cvSet2D(resultat, y, x, cvGet2D(normal, y, x)); } cvSet2D(masque, y, x, scal); } } cvShowImage("masque", masque); cvShowImage("resultat", resultat); }

void souris(int event, int x, int y, int flags, void *param=NULL) { if(event == CV_EVENT_LBUTTONUP) { CvScalar scal; scal = cvGet2D(hsv, y, x); h = scal.val[0]; s = scal.val[1]; v = scal.val[2]; actualiser(); } } void changementTol(int val) { tol = val; actualiser(); } int main(int argc, char **argv) { if(argc < 3) { std::cout << "Vous devez preciser le nom des images en argument !" << std return 10;

La demoiselle de tout l'heure commence sa carrire dans la mto grce vous !

Par souci de taille d'image, la fentre HSV ne figure pas sur le fond d'cran

Q.C.M.
Quel est l'espace de couleur par dfaut d'OpenCV ?

j k l m n RGB. j k l m n HSV. j k l m n BGR.

Qu'est-ce que la chrominance ?


j k l m n L'information de couleur de la lumire. j k l m n L'information de "luminosit" de la lumire. j k l m n L'information de puret de la couleur.

Qu'est-ce que la luminance ?


j k l m n L'information de couleur de la lumire. j k l m n L'information de "luminosit" de la lumire. j k l m n L'information de puret de la couleur.

L'espace RGB spare la luminance et la chrominance de la lumire.


j k l m n Vrai. j k l m n Faux.

Dans l'espace HSV, quoi fait rfrence la saturation ?


j k l m n Le ton de la couleur. j k l m n La puret de la couleur. j k l m n La clart de la couleur.

Correction !
Statistiques de rponses au QCM

Avec ces deux TP, vous avez srement eu pas mal de boulot de recherche et de rflexion. Mesdames et Messieurs, c'est avec mon ton le plus solennel que je vous annonce que ceci conclut la premire partie de ce tutoriel ! En effet, on peut considrer maintenant que vous avez eu un bon aperu des diffrents traitements de base. Alors bien sr, il en existe une flope d'autres (et vous les dcouvrirez), mais ils ne font gnralement que remplir les mmes fonctions que ceux que nous avons vus jusqu'ici. Il est donc temps pour nous de nous donner rendez-vous dans la seconde partie de ce tutoriel, ddie au traitement de flux vido.