Np. konwersja rgb2hsv:
Albo wykrywanie na obrazie fragmentow, ktore przypominaja ludzka skore:
My tu gadu gadu, a kodzik sam sie nie zrobi. :) Glowny plik main.cpp przedstawia sie nastepujaco:
/* v.1.2 * author: pcygan 30.10.2011 * purpose: grab camera input and render using opengl * in/out: C-STRING[][] -> INT */ //TODO CIMAGEPROCESSOR OFFSCREEN RENDERING AND CHAINING #include//for cout, cerr... #include //for sure that glew.h is before gl.h #include //SDL library #include //OpenCV library #include //OpenCV highgui library #include "Texture.h" //Texture class for dealing with OpenGL textures #include "Shader.h" //Shader class for making life with shaders easier #include "CImageProcessor.h" //CImageProcessor for hiding implementation of rendering. #include "CEvent.h" //CEvent as base for EventHandler #include "EventHandler.h" //Handle ESC key to stop the program.
Niezbedne includy dla bibliotek i klas z jakich korzystam, czyli:
i. GLEW - do shaderow itp.
ii. SDL - do obslugi okienek i petli komunikatow.
iii. OpenCV - do przechwytywania obrazu z kamery.
iv. Texture - klasa napisana do obslugi textur OpenGL.
v. Shader - klasa napisana do obslugi shaderow OpenGL.
vi. CImageProcessor - klasa napisana do przetwarzania obrazow na karcie graficznej.
vii. CEvent - interfejs do obslugi zdarzen SDL.
viii. EventHandler - implementacja CEvent.
SDL_Surface* g_pDisplay= 0; CvCapture* g_pCapture= 0; IplImage* g_pFrame= 0; Texture* g_pTexture= 0; Shader* g_pShader= 0; CImageProcessor* g_pImgProc= 0; EventHandler* g_pEventHandler= 0; int setupSDLAttribs(); void cleanup();
Deklaracja niezbednych zmiennych i funkcji pomocniczych. g_pDisplay jest uchwytem do przestrzeni na ktorej bedzie rysowal OpenGL w okienku. g_pCapture uchwytem do kamery. g_pFrame to obrazek (klatka?) zarejestrowany przez kamere, zawiera informacje o wymiarach, typie przestrzeni kolorow, ulozeniu bitow kolorow w obrazku i wielu innych rzeczach. g_pTexture to opakowana interfejsem textura w OpenGL, korzystajac z tej klasy mozna latwo wczytac obrazek jako texture. g_pShader to opakowany dodatkowym interfejsem shader, ale o nim pozniej. gImgProc to nasz procesor do przetwarzania obrazkow. g_pEventHandler bedzie sprawdzal czy wcisnelismy klawisz ESC i wtedy zakonczy program.
Funkcja setupSDLAttribs() odpowiada za ustawienie atrybutow niezbednych do poprawnej wspolpracy SDL i OpenGL. Natomiast cleanup() wyczysci zaalokowana w trakcie pracy programu pamiec.
int main(int argc, char* argv[]) {
/*
* Set working directory and some SDL attributes.
*/
gltSetWorkingDirectory(argv[0]);
if (setupSDLAttribs())
return -1;
//Try to open camera.
g_pCapture= cvCaptureFromCAM(CV_CAP_ANY);
if (!g_pCapture) {
fprintf(stderr, "ERROR: Capture is NULL. Program stopped with error: GL_INVALID_OPERATION. cvCaptureFromCAM() failure. \n");
getchar();
return -1;
}
//Try to capture from opened camera.
g_pFrame= cvQueryFrame(g_pCapture);
if (!g_pFrame) {
fprintf(stderr, "ERROR: Frame is NULL. Program stopped with error: GL_INVALID_OPERATION. cvQueryFrame() failure. \n");
getchar();
return -1;
}
Etap inicjalizacji. Chcemy utworzyc okienko za pomoca SDL, w ktorym bedziemy mogli rysowac korzystajac z OpenGL. W dodatku, chcemy zeby okienko mialo taki sam rozmiar jak obraz zczytywany przez kamerke. Najpierw odpalam setupSDLAttribs() zeby ustawic atrybuty OpenGL w SDL. Nastepnie lopatologicznie odczytujemy rozmiar obrazkow z kamerki poprzez otworzenie kamerki, sciagniecie jednej klatki i odczytanie z niej wymiarow.
//Create new Texture class object. g_pTexture= new Texture(); //Copy captured frame to OpenGL texture via Texture class object. g_pTexture->grabIpl(g_pFrame);
Przy okazji wykorzystamy sciagnieta klatke z kamerki do zainicjowania naszej tekstury, ktora z kolei posluzy jako wejscie do klasy CImageProcessor pozniej.
//Set proper SDL flags for video mode.
GLint SDL_FLAGS = SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_OPENGL;
//Set video mode- window size same as frame size.
if ((g_pDisplay = SDL_SetVideoMode(g_pFrame->width, g_pFrame->height, 32, SDL_FLAGS))
== NULL) {
std::cerr << "SDL_SetVideoMode fail. Program stopped with error: GL_INVALID_OPERATION." << std::endl;
return GL_INVALID_OPERATION;
}
//Init GLEW library.
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "ERROR: GLEW initialization error. Error message: %s\n", glewGetErrorString(err));
return 1;
}
Wracamy do inicjalizacji - ustawiamy flagi SDL:
SDL_HWSURFACE - chcemy wsparcie sprzetowego,
SDL_DOUBLEBUF - chcemy wykorzystac podwojny bufor ramki,
SDL_OPENGL - chcemy uzyc OpenGL do renderingu.
Majac te flagi, przekazujemy je do SDL_SetVideoMode() razem wymiarami obrazu i glebokscia bitow zeby utworzyc nowe okienko.
string sh("shaders/rgb2hsv.glsl.cfg"); //Path to config file for Shader class.
g_pShader= new Shader(&sh); //Create and load new shader config file.
g_pImgProc= new CImageProcessor(); //Create new CImageProcessor class object.
g_pImgProc->set(g_pTexture); //From now on, g_pImgProc will use g_pTexture as input texture.
g_pImgProc->set(g_pShader); //From now on, g_pImgProc will use g_pShader as process shader.
g_pEventHandler= new EventHandler(); //Create new SDL Event Handler.
SDL_Event Event; //Temporary variable for events.
Inicjalizacja srodowiska jest kompletna, zostaja tylko zmienne. Tworzymy nowy obiekt Shadera - przekazujemy do konstruktora sciezke do pliku konfiguracyjnego dla Shadera. Powiedzmy, ze w ten sposob w tle wczytywany jest kod shadera fragmentow i wierzcholkow, kompilowany, linkowany i podpinane sa atrybuty shaderow do programu. Nastepne linijki tworza nowy obiekt klasy CImageProcessor (ktora mozna sobie wyobrazic jako procesor do obrobki obrazow, w bardzo ogolnym pojeciu). Przekazanie wskaznika na teksture do funkcji set wywolywanej na rzecz g_pImgProc powoduje, ze bedzie ona wykorzystywana jako wejscie dla g_pImgProc. Przekazanie wskaznika na shader do funkcji set powoduje ze g_pImgProc bedzie wykonywal dany shader na swoich danych wejsciowych. Ostatecznie otrzymujemy g_pImgProc gotowe do przetworzenia programem g_pShader tekstury g_pTexture.
while (g_pEventHandler->bRunning) {
/*
* Use SDL library to detect whether ESCAPE key was down?
* If it was pressed down, then g_bRunning will be set to false.
* It will break the loop.
*/
while (SDL_PollEvent(&Event)) {
g_pEventHandler->OnEvent(&Event);
}
/*
* Try to capture next frame. End loop if operation fail.
*/
g_pFrame = cvQueryFrame(g_pCapture);
if (!g_pFrame) {
fprintf(stderr, "ERROR: frame is null...\n");
getchar();
break;
}
/*
* We need to flip captured frame in X and Y axis using cvFlip with -1 as last argument.
* Otherwise captured frame will be shown incorrectly.
*/
cvFlip(g_pFrame, NULL, -1);
if (!g_pTexture->grabIpl(g_pFrame)) {
cout << "false" << endl;
continue;
}
/*
* We use our CImageProcessor class, to process frame with g_pShader (GLSL shader)
* and display it on the screen.
*/
g_pImgProc->process();
SDL_GL_SwapBuffers(); //Swap OpenGL buffers.
}
Glowna petla programu. Jej pseudokod moglby wygladac nastepujaco:
while (running) {
while (is event) {
handle_event();
}
g_pFrame= grabFrameFromCamera();
g_pFrame= flipFrameXYAxis(g_pFrame);
g_pImgProc->process();
display();
}
Dzialamy dopoki running jest prawdziwe. A dzialanie polega na obsluzeniu lub zignorowaniu wszystkich komunikatow ze srodowiska (wcisniete klawisze, proba zamkniecia okna itp.), zczytaniu obrazu z kamery, zaktualizowaniu tekstury obrazem z kamery, przetworzeniem tekstury za pomoca g_pImgProc i wyswietlenie. Obsluga komunikatow to tylko sprawdzenie czy wcisnieto klawisz ESC, a wtedy ustawienie running na falsz. Calkiem proste. ;)
//Close camera. cvReleaseCapture(&g_pCapture); //Close frame. cvReleaseImage(&g_pFrame); //Close SDL display. SDL_FreeSurface(g_pDisplay); //Close SDL library. SDL_Quit(); //Close OpenGL texture and clean memory allocated by Texture class object. g_pTexture->destroy(); //Close Image Processor and clean it's allocated memory. g_pImgProc->destroy(); //Clean up dynamically allocated memory. cleanup(); return 0; }Czyszczenie pamieci, zamkniecie uzywanych zrodel i zakonczenie programu.
int setupSDLAttribs() {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
cerr << "Game initializaton error: problem occured "
<< "during SDL initialization." << endl;
return GL_INVALID_OPERATION;
}
//These are all basic memory sizes for how much data OpenGL will store
//and must be called BEFORE SDL_SetVideoMode(...)
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
//Make sure your SDL_GL_DEPTH_SIZE is a power of two (16, 32 works),
//otherwise anti-aliasing may not work for you.
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, 8);
//SDL_GL_MULTISAMPLEBUFFERS is basically anti-aliasing flag
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
//SDL_GL_MULTISAMPLESAMPLES is how much to anti-alias.
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 2);
return GL_NO_ERROR;
}
void cleanup() {
delete g_pDisplay;
delete g_pTexture;
//Close OpenGL shader and clean memory allocated by Shader class object.
//It's done via destructor.
delete g_pShader;
delete g_pImgProc;
delete g_pEventHandler;
}
Definicje funkcji pomocniczych.
Podsumowanie, i co dalej?.
Z pomoca biblioteki OpenCV i OpenGL, w dosyc prosty sposob mozemy w czasie rzeczywistym przetwarzac obrazy otrzymywane z kamery. Korzystajac z wczesniej napisanych kodow (klasy Texture, Shader, CImageProcessor, CEvent) staje sie to jeszcze prostsze. A jakie perspektywy na przyszlosc? Umozliwienie laczenia obiektow CImageProcessor w lancuchy, zeby w bardziej zaawansowany sposob manipulowac na obrazie z kamery - i ostatecznie sledzic twarz.
Link to complete source. Libraries needed: GL, GLEW, GLTools, SDL, cv, highgui, cxcore.



