Vous trouverez la derni�re version de ce plugin sur la page de mes plugins Gimp.
R�mi Peyronnet - F�vrier 2002
Gimp est un logiciel de traitement d'image issu du monde libre, et de plus en plus employ�. OpenSource, il profite de nombreuses contributions, en particulier gr�ce � de nombreux plugins qu'il est possible de d�velopper.
Cependant, je n'ai pas encore vu � ce jour de plugin permettant la conversion des canaux Rouge Vert Bleu (RGB), dans l'espace de couleur YUV (Luminance, Chrominances rouges et bleues). Il peut �tre souvent utile de travailler dans un tel mode, et de d�composer l'image en ces trois composantes. Ce plug-in a donc pour but de r�pondre � ce besoin.
Le probl�me se pose de savoir comment repr�senter une couleur sur un ordinateur. Il existe pour cela beaucoup de techniques. La plus utilis�e sur les ordinateurs est la repr�sentation RGB, qui d�crit une couleur par ses composantes rouges, vertes et bleues. Cette repr�sentation correspond exactement � l'affichage des couleurs sur un �cran par projection d'un flux d'�lectrons plus ou moins intense sur des pastilles rouges, vertes et bleues, qui en se m�langeant nous donne l'impression de couleur.
La repr�sentation YUV est aussi tr�s utilis�e, principalement dans tout ce qui est compression d'image. Y repr�sente la luminance de la couleur, et U et V, la chrominance de cette couleur dans le rouge et le bleu. Cette repr�sentation est utile, car l'oeil est plus sensible aux variations de luminances qu'aux variations de chrominance. S�parer ces trois composantes permettra donc de pouvoir d�grader plus les chrominances, tout en conservant mieux la luminance.
La principale difficult�, outre l'�criture du plug-in lui-m�me, a �t� de trouver les formules ad�quates pour la conversion YUV-RGB. En effet, en premi�re approche, nous pouvons prendre pour la luminance simplement la moyenne des trois composantes Rouge, Vert et Bleu. Cependant, cette fa�on de faire n'est pas tr�s juste, car elle ne tient pas compte de la sensibilit� de l'oeil aux couleurs. Comme nous pouvons le constater dans les courbes ci-dessous, l'oeil � l'impression que le vert est beaucoup plus lumineux que le bleu. Il nous faut donc en tenir compte.
J'ai donc cherch� sur Internet quelles formules utiliser, et on en trouve de tr�s diverses. La formule la plus fr�quente est : Y = 0.299*R + 0.587*G + 0.114*B
. On peut constater que cette formule tient bien compte du fait que l'oeil per�oit le vert comme plus lumineux que le rouge, qui est lui-m�me plus lumineux que le bleu.
Pour le calcul des chrominances, nous utiliseront les formules : U = -0.169*R - 0.331*G + 0.500*B + 128.0
et V = 0.500*R - 0.419*G - 0.081*B + 128.0
. Comment peut-on comprendre ces formules ? Premi�rement, nous pouvons remarquer que ces deux formules sont pr�vues pour osciller entre 0 et 255 avec comme valeur moyenne 128. Par exemple, une valeur 255 pour Bleu, avec le coefficient 0.5, sera ramen� dans les limites. De m�me pour le rouge et le vert (dans le sens n�gatif, -(0.169+0.331)=-0.500). Nous sommes ainsi assur�s qu'avec des valeurs comprises entre 0 et 255 en entr�e, nous aurons bien des valeurs entre 0 et 255 en sortie, ce qui est vital pour notre application. Il est de plus possible d'exprimer U et V en fonction de Y, et de retrouver ainsi le sens premier des chrominances (Y-R), (Y-B).
R�capitulatif :
Y = 0.299*R + 0.587*G + 0.114*B
U = -0.169*R - 0.331*G + 0.500*B + 128.0
V = 0.500*R - 0.419*G - 0.081*B + 128.0
Si les formules ci-dessus abondent, il en va autrement dans le sens inverse. J'avais en premier lieu trouv� les formules (associ�es aux pr�c�dentes) :
Ce que nous cherchons � obtenir en fait, c'est que le produit des transformations RGB vers YUV et YUV vers RGB fasse l'identit�. Il suffit donc de chercher � inverser les formules trouv�es pour le premier passage. J'ai donc cherch� � utiliser MatLab, disponible � l'�cole. Les r�sultats obtenus ont �t� tout � fait d�cevant, puisque le produit manuel des deux matrices ne donnait pas l'identit�... L'utilisation de SciLab, logiciel gratuit, s'est r�v�l�e beaucoup plus efficace. Les formules obtenues sont alors :
Il est tout � fait rassurant de constater apr�s coup que les formules obtenues sont conformes � celles utilis�es dans la norme JPEG, trouv�e ensuite. Notons qu'il serait pr�f�rable d'arrondir les 0.009267 � 0, comme dans la norme.
Avec ces formules, la transformation RGB vers YUV suivi de son inverse est invisible � l'oeil, malgr� les l�g�res erreurs d'arrondi lors de la conversion de la valeur r�elle en un octet.
I. 3. Programmation d'un Plug-in GIMP
L'int�gration dans GIMP n'a pas �t� la chose la plus facile du projet, car la documentation est assez elliptique sur ce sujet. Heureusement, il existe beaucoup de plugins pour GIMP OpenSource, ce qui m'a permis de comprendre le fonctionnement normal d'un Plugin.
Le plugin poss�de un code d'initialisation, qui se charge d'enregistrer la fonction dans GIMP, en inscrivant par exemple une entr�e dans un menu. Lorsque ce menu est appel�, la fonction principale du plugin est appel�e, en donnant en param�tre un pointeur vers l'image. GIMP poss�de une mani�re optimis�e de traiter les images, en les d�coupant par carreaux. Pour ce plugin, qui n'a pas besoin d'une connaissance compl�te de l'image mais seulement d'un pixel pour op�rer, nous avons opt� pour cette m�thode. Le traitement est donc inclus dans uns boucle, qui s'occupe de d�couper l'image en blocs, puis nous traitons ensuite, chaque pixel, ligne par ligne. GIMP s'occupe ensuite de g�rer l'affichage et la gestion des blocs, ce qui permet par exemple d'annuler l'effet appliqu�.
Ce plugin est distribu� soit sous forme de fichier source � compiler, soit en un binaire pour windows, � placer dans le r�pertoire 'plugins' de GIMP. Il faut alors relancer GIMP, et les entr�es 'RGB to YUV' et 'YUV to RGB' sont ajout�es dans le menu Image/Couleurs. Il suffit juste de cliquer sur une de ces entr�es pour lancer le plugin.
En sortie, le canal Y sera plac� dans le canal Rouge, le U dans le Vert, et le V dans le Bleu. Gr�ce � la bo�te de dialogue des canaux, vous pouvez demander de ne modifier ou voir qu'un ou plusieurs de ces canaux YUV � la fois.
/* * 2002 Peyronnet R�mi * * This plugin transforms your image RGB in YUV * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ /* Many parts of this code are borrowed from other plugins source code. */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include "libgimp/gimp.h" #define MAX(x,y) ( ((x)>(y))?(x):(y) ) #define MIN(x,y) ( ((x)<(y))?(x):(y) ) /** defines ***********************************************************/ #define PLUG_IN_NAME "plug_in_yuv" #define PLUG_IN_VERSION "Feburar 2002, 1.0" /** Plugin interface *********************************************************/ void query(void); void run(char *name, int nparams, GimpParam *param, int *nreturn_vals, GimpParam **return_vals); GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run /* run_proc */ }; MAIN() void query(void) { /* Definition of parameters */ static GimpParamDef args[] = { { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, { GIMP_PDB_IMAGE, "image", "Input image (unused)" }, { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" } }; static GimpParamDef *return_vals = NULL; static int nargs = sizeof(args) / sizeof(args[0]); static int nreturn_vals = 0; gimp_install_procedure( "plug_in_rgb_yuv", "Transform the image from RGB to YUV", "This plugin replaces the RGB channels with YUV values.", "R�mi Peyronnet", "R�mi Peyronnet", PLUG_IN_VERSION, "<Image>/Image/Colors/RGB->YUV", "RGB*", GIMP_PLUGIN, nargs, nreturn_vals, args, return_vals); gimp_install_procedure( "plug_in_yuv_rgb", "Transform the image from YUV to RGB", "This plugin replaces the RGB channels of an YUV image with the good RGB values.", "R�mi Peyronnet", "R�mi Peyronnet", PLUG_IN_VERSION, "<Image>/Image/Colors/YUV->RGB", "RGB*", GIMP_PLUGIN, nargs, nreturn_vals, args, return_vals); } void run(char *name, int nparams, GimpParam *param, int *nreturn_vals, GimpParam **return_vals) { /* Return values */ static GimpParam values[1]; gint sel_x1, sel_y1, sel_x2, sel_y2; gint img_height, img_width, img_bpp, img_has_alpha; GimpDrawable *drawable; GimpPixelRgn dest_rgn, src_rgn, *pr; GimpRunModeType run_mode; GimpPDBStatusType status; double progress, max_progress; guchar * dest_row, *src_row, *dest, *src; double r,g,b,a=0,y,u,v,m,mi; gint row, col; *nreturn_vals = 1; *return_vals = values; status = GIMP_PDB_SUCCESS; if (param[0].type!= GIMP_PDB_INT32) status=GIMP_PDB_CALLING_ERROR; if (param[2].type!=GIMP_PDB_DRAWABLE) status=GIMP_PDB_CALLING_ERROR; run_mode = param[0].data.d_int32; drawable = gimp_drawable_get(param[2].data.d_drawable); img_width = gimp_drawable_width(drawable->id); img_height = gimp_drawable_height(drawable->id); img_bpp = gimp_drawable_bpp(drawable->id); img_has_alpha = gimp_drawable_has_alpha(drawable->id); gimp_drawable_mask_bounds(drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2); max_progress = (sel_x2-sel_x1)*(sel_y2-sel_y1); if (status == GIMP_PDB_SUCCESS) { // Tile gimp_tile_cache_ntiles((drawable->width + gimp_tile_width() - 1) / gimp_tile_width()); if (strcmp("plug_in_rgb_yuv",name) == 0) { // RGB -> YUV // !!! Warning !!! Duplicated code... 'cause it'is quick'n dirty :) gimp_progress_init("Converting RGB to YUV..."); progress = 0; // Process gimp_pixel_rgn_init (&dest_rgn, drawable, sel_x1, sel_y1, (sel_x2-sel_x1), (sel_y2-sel_y1), TRUE, TRUE); gimp_pixel_rgn_init (&src_rgn, drawable, sel_x1, sel_y1, (sel_x2-sel_x1), (sel_y2-sel_y1), FALSE, FALSE); // Methode de traitement par dest_rgns ----------------------- for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn); pr != NULL; pr = gimp_pixel_rgns_process (pr)) { //Fun Goes On Here dest_row = dest_rgn.data; src_row = src_rgn.data; for (row = 0; row < dest_rgn.h; row++) { dest = dest_row; src = src_row; for (col = 0; col < dest_rgn.w; col++) { // D�but du traitement sp�cifique ************* r = *src++; //y g = *src++; //u b = *src++; //v if (img_has_alpha) a = *src++; /* First set of formula, probably not the best... ---- y = (0.257*r) + (0.504*g) + (0.098*b) + 16; u = (0.439*r) - (0.368*g) + (0.071*b) + 128; v = - (0.148*r) - (0.291*g) + (0.439*b) + 128; // YUV->RGB // r = 1.164 * (y-16) + 1.596*(v-128); // g = 1.164 * (y-16) + 0.813*(v-128) - 0.391*(u-128); // b = 1.164 * (y-16) + 2.018*(u-128); */ /* Second set, not much better...*/ y = (0.299*r) + (0.587*g) + (0.114*b); u = -(0.169*r) - (0.331*g) + (0.500*b) + 128.0; v = (0.500*r) - (0.419*g) - (0.081*b) + 128.0; // YUV->RGB^M //r = y + 1.402*(v-128.0); //g = y - 0.34414*(u-128.0) + 0.71414*(v-128.0); //b = y + 1.772*(u-128.0); // // From SciLab : This is the good one. //r = 1 * y - 0.0009267*(u-128) + 1.4016868*(v-128);^M //g = 1 * y - 0.3436954*(u-128) - 0.7141690*(v-128);^M //b = 1 * y + 1.7721604*(u-128) + 0.0009902*(v-128);^M /** Third : home-made...*/ /*y = 0.333 * r + 0.333 * g + 0.333 * b; u = r - y; v = g - y; r = y + u; g = y + v; b = y -u -v; */ *dest++ = (guchar) (y>255)?255:((y<0)?0:y); *dest++ = (guchar) (u>255)?255:((u<0)?0:u); *dest++ = (guchar) (v>255)?255:((v<0)?0:v); if (img_has_alpha) *dest++ = a; // Fin du traitement sp�cifique **************** } // for dest_row += dest_rgn.rowstride; src_row += src_rgn.rowstride; } // for // Update progress progress += dest_rgn.w * dest_rgn.h; gimp_progress_update((double) progress / max_progress); } } else if (strcmp("plug_in_yuv_rgb",name) == 0) { // RGB -> YUV // !!! Warning !!! Duplicated code... 'cause it'is quick'n dirty :) // You should consider just edit the previous version and copy/paste this one. gimp_progress_init("Converting YUV to RGB..."); progress = 0; // Process gimp_pixel_rgn_init (&dest_rgn, drawable, sel_x1, sel_y1, (sel_x2-sel_x1), (sel_y2-sel_y1), TRUE, TRUE); gimp_pixel_rgn_init (&src_rgn, drawable, sel_x1, sel_y1, (sel_x2-sel_x1), (sel_y2-sel_y1), FALSE, FALSE); // Methode de traitement par dest_rgns ----------------------- for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn); pr != NULL; pr = gimp_pixel_rgns_process (pr)) { //Fun Goes On Here dest_row = dest_rgn.data; src_row = src_rgn.data; for (row = 0; row < dest_rgn.h; row++) { dest = dest_row; src = src_row; for (col = 0; col < dest_rgn.w; col++) { // D�but du traitement sp�cifique ************* y = *src++; u = *src++; v = *src++; if (img_has_alpha) a = *src++; /* Second set, not much better...*/ //y = (0.299*r) + (0.587*g) + (0.114*b); //u = -(0.169*r) - (0.331*g) + (0.500*b) + 128.0; //v = (0.500*r) - (0.419*g) - (0.081*b) + 128.0; // From SciLab r = 1 * y - 0.0009267*(u-128) + 1.4016868*(v-128); g = 1 * y - 0.3436954*(u-128) - 0.7141690*(v-128); b = 1 * y + 1.7721604*(u-128) + 0.0009902*(v-128); *dest++ = (guchar) (r>255)?255:((r<0)?0:r); *dest++ = (guchar) (g>255)?255:((g<0)?0:g); *dest++ = (guchar) (b>255)?255:((b<0)?0:b); if (img_has_alpha) *dest++ = a; // Fin du traitement sp�cifique **************** } // for dest_row += dest_rgn.rowstride; src_row += src_rgn.rowstride; } // for // Update progress progress += dest_rgn.w * dest_rgn.h; gimp_progress_update((double) progress / max_progress); } } else { // Ouch, ugly :) printf("Plugin not found.\n"); } gimp_drawable_flush(drawable); gimp_drawable_merge_shadow(drawable->id, TRUE); gimp_drawable_update (drawable->id, sel_x1, sel_y1, (sel_x2-sel_x1), (sel_y2-sel_y1)); gimp_displays_flush(); } values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; gimp_drawable_detach(drawable); }
Voici donc un plugin Gimp compl�tement fonctionnel, permettant de convertir les images en YUV, de travailler dessus, puis de repasser dans le domaine RGB.