poniedziałek, 31 października 2011

GPU RGB2HSV

Maly programik, konwertujacy obrazek .TGA z przestrzeni kolorow RGB do HSV z pomoca GPU. Jest to wstep do wiekszego projektu - sledzenia twarzy przez kamere. Najpierw taka twarz trzeba znalezc. :) Mozna to osiagnac po przejsciu na przestrzen HSV i znalezieniu pikseli mieszczaczych sie w odpowiednich wartosciach:

H= <0, 0.1> u <0.9, 1>
S= <0.2, 0.7>
V= <0.4, 1>

W tym celu chce uzyc karty graficznej. Jestem na etapie konwersji rgb2hsv:
-> wczytaj obrazek i zamien na teksture,
-> w oknie o rozmiarach obrazka wyswietl prostokat w rzucie ortogonalnym z uzyciem wyspecjalizowanego shadera, z nalozona tekstura,
-> zapisz okno do pliku.
Okazuje sie, ze taki sposob dzialania jest bledny, bo wystarczy ze obrazek bedzie wiekszy niz dostepny ekran. :) Da sie to obejsc, ale na razie jest ok. ;)

Prosty shader konwersji wyglada nastepujaco:

rgb2hsv.glsl.vp:
#version 130

in vec4 vVertex;
in vec2 vTexCoords;

// Transformation Matrix
uniform mat4 mvpMatrix;

smooth out vec2 vVaryingTexCoords;

void main(void) 
{ 
    vVaryingTexCoords = vTexCoords;
    gl_Position = mvpMatrix * vVertex;
}
    

rgb2hsv.glsl.fp
#version 130

uniform sampler2D colorMap;

smooth in vec2 vVaryingTexCoords;
out vec4 vFragColor;

void main()
{
//image RGB value
 vec4 t= texture2D( colorMap, vVaryingTexCoords.st );
//max, min and diff 
 float fmax= max(max(t.x,t.y),t.z);
 float fmin= min(min(t.x,t.y),t.z);
 float diff= fmax-fmin;

//set up vFragColor with max, vFragColor represent HSV
 vFragColor.xyzw= vec4(fmax); //x->H y->S z->V

//if max is not 0, set S value
 if (fmax != 0)
  vFragColor.y= diff/fmax;

//if max and min are same, H is 0
 if (fmax == fmin)
  vFragColor.x= 0;
 else { //otherwise
  if ( fmax == t.x ) { //if R is dominant
   float tmp= 0;
   if (t.y < t.z)
    tmp= 6;
   vFragColor.x= (t.y-t.z)/diff + tmp;
  } 
  else if ( fmax == t.y ) //if G is dominant
   vFragColor.x= (t.z-t.x)/diff + 2;
  else if ( fmax == t.z ) //if B is dominant
   vFragColor.x= (t.x-t.y)/diff + 4;
   
  vFragColor.x= vFragColor.x/6;
 }
}
Jak widac, vertex shader ma tylko przepuscic geometrie i wspolrzedne tekstury do fragment shadera, w ktorym odbywa sie wlasciwa konwersja. Po szczegoly konwersji odsylam do wikipedii lub ksiazki "Techniki Biometryczne, Czesc 1, Metody Rzopoznawania Twarzy" G.Kukharev, A.Kuzminski. Poniższy screenshot pokazuje, od lewej - obraz oryginalny, obraz w HSV po przepuszczeniu przez moj program i obraz kontrolny w HSV po przepuszczeniu przez MATLABowska funkcjce rgb2hsv:

Dla Leny w calej jej krasie i w rozdzielczosci 1084x2318 widac duza roznice w szybkosci dzialania pomiedzy MATLABem a napisanym programem, i o to chodzilo. :) Lena moze nie jest najszczesliwszym obrazkiem do testow, bo wiekszosc obrazka miesci sie w przedzialach HSV, ale to nic. :)