piątek, 18 listopada 2011

OpenCV + OpenGL

Podpialem kamerke w C++ korzystajac z OpenCV, przyjemnej biblioteki wspomagajacej programowanie zagadnien zwiazanych z widzeniem maszynowym. Pisalem wczesniej o projekcie FaceTrackingu. Po napisaniu programu do konwersji obrazkow z przestrzeni RGB do HSV czas na nastepny krok. Napisalem klase CImageProcessor, ktora wykorzystuje do manipulowania obrazami za pomoca shaderow. Razem z OpenCV pozwala to na przetwarzanie live obrazu z kamerki. :)

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.





wtorek, 15 listopada 2011

Matlab - LZW - kompresja i dekompresja.

Napiszemy w Matlabie kompresje i dekompresje obrazka algorytmem LZW. Takie bylo zadanie na zajeciach. ;)

Wszystkie pliki zrodlowe, troche zabalaganione, znajduja sie tutaj:
http://dl.dropbox.com/u/2545858/lzw.zip

Cel.
Ogolna idea naszej dzialalnosci, napiszmy funkcje im2fd(), ktora musi:
-wczytac obrazek w odcieniach szarosci z pliku i zapamietac jego wymiary,
-przeksztalcic z macierzy odcieni szarosci na wektor,
-przeksztalcic z wektora odcieni szarosci na wektor znakow 8-bit char,
-skompresowac wektor znakow char algorytmem lzw,
-utworzyc plik do ktorego zapisze rozmiary obrazka i otrzymany kod z kompresji lzw.

Skoro bedziemy mieli funkcje im2fd, przyda sie tez fd2im(), ktora musi:
-wczytac z pliku ktory jest efektem dzialania funkcji im2fd rozmiar obrazka i kodo z kompresji lzw,
-zdekodowac kod z kompresji lzw na wektor znakow char,
-przeksztalcic z wektora znakow char na wektor odcieni szarosci,
-przeksztalcic z wektora odcieni szarosci na macierz dwuwymiarowa korzystajac z wczytanych wymiarow obrazka.
-zapisac otrzymana macierz odcienia szarosci jako obraz do pliku.

Cel jest jasny i prosty.

Plan.

Zaprojektujmy w takim razie funkcje im2fd:

void im2fd(string imagefile, string output) {
IMAGE= imageread(imagefile); %wczytac obrazek
SIZE= size(IMAGE); %wczytac jego rozmiary
IMAGE= m2v(IMAGE); %macierz -> wektor
IMAGE= int2char(IMAGE); %konwesja na wetkor char
CODE= lzw_encode(IMAGE); %kompresja
TOFILE= vector_concatenate(SIZE,CODE); %przygotowanie danych do zapisu: polaczenie wektora rozmiaru obrazka z wektorem kodu z kompresji
filewrite(output,TOFILE); %zapisanie do pliku output
}

Rozbilismy im2fd na kroki postepowania i kazdemu przypisalismy funkcje. Przerobienie takiego ideowego pseudokodu na dzialajacy kod w Matlabie, jest raczej banalne:

%pcygan 09.11.2011
% in -> out: str, str -> void
% purpose: compress imagefile using lzw algorithm and write generated code to output text file

function im2fd(imagefile,output)
disp(['Loading image: ' imagefile]);
IMAGE= imread(imagefile);  %wczytac obrazek
PREIMAGE= IMAGE;
disp('Reading image size.');
[rows_ cols_]= size(IMAGE); %wczytac jego rozmiary
disp(['Image size: ' num2str(rows_) 'x' num2str(cols_)]);
disp('Image vectorization.');
IMAGE= IMAGE(:)'; %macierz -> wektor
[o p]= size(IMAGE);
disp(['Data vector size is: ' num2str(o) 'x' num2str(p)]);
disp(['Max value: ' num2str(double(max(IMAGE))) ', min value: ' num2str(double(min(IMAGE)))]);
disp('Converting int2char');
IMAGE= char(IMAGE); %konwesja na wetkor char
disp('Coding image.'); 
CODE= lzw_encode(IMAGE); %kompresja
disp(['Code size' num2str(size(CODE))]);
disp('Code generated.');
disp(['Saving to file ' output '.']);
FILE= fopen(output,'w');
%przygotowanie danych do zapisu: polaczenie wektora rozmiaru obrazka z wektorem kodu z kompresji + zapisanie do pliku
fprintf(FILE,num2str([rows_ cols_ double(CODE)]));  
fclose(FILE);
disp('Save ok!');
imshow(PREIMAGE);
end

Bedziemy schludni i dodamy odrobine komentarzy, zeby po czasie gdy wrocimy do kodu, bylo latwiej go ogarnac. Ponadto dopisalismy troche wypisywania wiadomosci na konsole. :) Wszystkie funkcje dostepne sa w Matlabie, oprocz lzw_encode, ktora napiszemy sami.

lzw_encode
Jak mozemy sie dowiedziec z Wikipedii, pseudokod algorytmu kodowania LZW przedstawia sie nastepujaco:
1. Wypełnij słownik alfabetem źródła informacji.
2. c := pierwszy symbol wejściowy
3. Dopóki są dane na wejściu:
   Wczytaj znak s.
   Jeżeli ciąg c + s znajduje się w słowniku, 
      przedłuż ciąg c, tj. c := c + s
   Jeśli ciągu c + s nie ma w słowniku, wówczas:
      wypisz kod dla c (c znajduje się w słowniku)
      dodaj ciąg c + s do słownika
      przypisz c := s.
4. Na końcu wypisz na wyjście kod związany c.

W prosty sposob zaimplementujemy to w Matlabie, jako zapisanie (niemal) wprost pseudokodu w kod:

%pcygan 11.2011
%in->out: char vector -> int vector
%purpose: compress string(char vector) DATA using lzw algorithm and save compressed code in CODE (int vector)
%example:
%octave:33> lzw_encode('abrakadabra!')
%ans =
%
%    98    99   115    98   108    98   101   257   259    34
%

function CODE= lzw_encode(DATA)
    DICT= lzw_initDict();
    m= length(DATA);
    c= DATA(1); %char
    CODE= [];
    %TODO HANDLE 1-ELEMENT OR 2-ELEMENT DATA
    for i= 2:m
        %get s char
        s= DATA(i); %char
        %if c + s is in dict
        if lzw_findInDict(DICT,[c s])
            c= [c s];
        else
            CODE= [CODE lzw_findInDict(DICT,c)];
            c= [c s];
            DICT= [DICT {c}];
            c= s;
        end
    end
    CODE= [CODE lzw_findInDict(DICT,c)];
end

Pozostaja dwie funkcje do zdefiniowania:
-lzw_initDict(), odpowiedzialna za inicjalizacje slownika.
-lzw_findInDict(), odpowiedzialna za wyszukiwanie stringu w slowniku i zwracanie jego indeksu (badz 0 w przypadku gdy nie ma w slowniku).

Inicjalizacja slownika polega na wypelnieniu go alfabetem danych. W naszym przypadku, alfabetem danych jest zasieg 8-bitowej zmiennej char, poniewaz taki jest nasz Cel. Daje nam to 256 mozliwych znakow. Kod w Matlabie jest krotki i prosty, z jednym malym 'ale':

%pcygan 11.2011
%in->out: void->char vector
%purpose: create 1:256 char table containing all possible char values

function DICT= lzw_initDict()
    DICT= {0:255};
    for i=1:256
        DICT(i)= {char(i-1)};
    end
    %disp(DICT)
end

Ale polega na sposobie w jakim przechowujemy slownik. Przechowujemy jakby wskazniki na dane, zamiast samych danych - dlatego ciagi znakow w slowniku moga miec rozna dlugosc, co jest konieczne zeby algorytm zadzialal. Zwroc uwage na klamry '{' i '}' zamiast '[' i ']' w DICT.

Chemy, zeby lzw_findInDict(dict,str) zwrocila indeks wystapienia stringu str w slowniku dict lub 0 w przypadku, gdy nie wystepuje. Szukanie najprosciej zrobic jako porownywanie kazdego elementu slownika z szukanym stringiem i zwroceniem wartosci indeksu w razie sukcesu:

%pcygan 11.2011
%in->out: vector of strings, string->integer
%purpose: find whether string STR(char vector) is in dictionary(strings vector) DICT, if is, return it's index, if is not, return 0

function INDEX= lzw_findInDict(DICT,STR)
    INDEX= 0;
    [n m]= size(DICT);
    for i=1:m
        if strcmp(STR,DICT(i))
            INDEX= i;
            return;
        end
    end
end

Mamy teraz wszystko co jest potrzebne zeby nasza funkcja im2fd zadzialala. :)

fd2im
Teraz zrealizujemy drugi cel, jakim jest funkcja odwrotna do im2fd. Postepujemy analogicznie jak poprzednio - najpierw plan (ktory pomine), potem implementacja glownej funkcji fd2im, nastepnie implementacja funkcji 'skladowych'.

%pcygan 09.11.2011
% in -> out: str -> void
% purpose: read output lzw_file generated using im2fd function and create image

function fd2im(lzw_file)
disp(['Opening lzw .txt file: ' lzw_file '.']);
FILE= fopen(lzw_file);
disp('Copying data to memory.');
code= str2num(fread(FILE, '*char')');
disp('Closing file.');
fclose(FILE);
disp('File closed.');
rows_= code(1);
cols_= code(2);
disp(['Image size is: ' num2str(rows_) 'x' num2str(cols_)]);
[m n]= size(code);
code= code(3:n);
[m n]= size(code);
disp(['Code size is: ' num2str(m) 'x' num2str(n)]);
data= double(lzw_decode(code));
[o p]= size(data);
disp(['Data vector size is: ' num2str(o) 'x' num2str(p)]);
disp('Recovering matrix from vector by image size.');
data= v2m(data,rows_,cols_);
[r s]= size(data);
disp(['Data size is: ' num2str(r) 'x' num2str(s)]);
imshow(data,[0,255]);
disp(['Decompressed image size is: ' num2str(o*p) ', should be: ' num2str(rows_*cols_) '.']);
end

fd2im dziala odwrotnie jak im2fd, wiec nie ma sie tu nad czym zbytnio rozwodzic. Wyjasnienia wymagaja tylko dwie funkcje: v2m i lzw_decode. v2m jest banalne, poniewaz zamienia wektor w macierz:

%%
%AUTHOR 
%%
% pcygan 2011.10.28

%%
%DECLARATION    INPUT -> OUTPUT
%%
% VECTOR, ROWS_, COLS_ -> MATRIX[ROWS_,COLS_]

%%
%PURPOSE
%%
% CREATE MATRIX X EQUAL TO MATRIX M FROM VECTOR V, 
% WHEN VECTOR V WAS CREATED FROM MATRIX M: V= M(:)
% THIS FUNCTION REVERSE OPERATION M(:)
% V= M(:), X= v2m(V,M_rows,M_columns); 
% X == M -> true, after MATRIX X IS EQUAL TO MATRIX M.

%%
%EXAMPLE
%%
% $in$ F= [1 2 3 4; 5 6 7 8]
% $out$
% F =
%
%   1   2   3   4
%   5   6   7   8
% $in$ [rows_ cols_]= size(F)
% $out$
%   rows_ =  2
%   cols_ =  4
% $in$ F= F(:)
% $out$
%
% F =
%
%   1
%   5
%   2
%   6
%   3
%   7
%   4
%   8
% $in$ v2m(F,2,4)
% $out$
% ans =
%
%   1   2   3   4
%   5   6   7   8

function MATRIX= v2m(VECTOR,r,c)
    MATRIX= ones(r,c); %preallocate memory for better performance
    m= length(VECTOR); %remember VECTOR's number of rows
    i= 0; %set starting variables
    j= 1;
    for it=1:m %iterate through VECTOR
        i= i+1; %increment row pointer
        if i > r %if row pointer is greater restored matrix* then number of rows
            i= 1; %then reset row pointer to first row
        end
        %copy data from VECTOR(it) to proper position in MATRIX
        MATRIX(i,j)= VECTOR(it);
        if mod(it,r) == 0 %this mean we went through restored matrix column
            j= j+1; %so increment column pointer
        end
    end
end

% *restored matrix is M in %EXAMPLE

Troche duzo kodu, a dowiedzialem sie ostatnio, ze nie musialem deklarowac nowej funkcji, bo wystarczylo zamiast niej napisac (:

MATRIX(:)= VECTOR;

Natomiast lzw_decode to juz inna bajka:

%pcygan 11.2011
%in->out: int vector->char vector
%purpose: decode char string(char vector) DATA from CODE (int vector). we assume CODE was generated using LZW compression algorithm
%example:
%octave:34> lzw_decode(lzw_encode('abrakadabra!'))
%ans = 
%
% abrakadabra!
%

function DATA= lzw_decode(CODE)
    DICT= lzw_initDict();
    pk= CODE(1);
    k= 1;
    [n m]= size(CODE);
    DATA= DICT{pk};
    for i= 2:m
        %wczytaj kod k
        k= CODE(i);
        %ciag skojarzony z poprzednim kodem
        pc= DICT{pk};
        %jezeli slowo o kodzie k jest w slowniku
        [o p]= size(DICT);
        if k <= p
            tmp= DICT{1,k};
            pc= [pc tmp(1)]; %dodaj do slownika pc + slownik[k][0]
            DICT= [DICT {pc}];
            DATA= [DATA tmp]; %na wyjsciu wypisz slownik[k]
        else
            pc= [pc pc(1)];
            DICT= [DICT {pc}];
  DATA= [DATA pc];
        end
    pk= k;
    end
end

wtorek, 1 listopada 2011

OpenCV - niezly kombajn!

Odnoszac sie do poprzedniego postu, poszperalem w sieci troche w poszukiwaniu cross-platform API do obslugi kamerki i natknalem sie na OpenCV. Obsluguje ona wiekszosc, jezeli nie wszystkie ficzery jakie potrzebuje w swoim programie do sledzenia twarzy. Co znaczy, ze jezeli bym sie uparl, nie musialbym samemu nic napisac. :D W kazdym razie - rzeczy wymaga glebszej analizy, haha! :)

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. :)




środa, 31 sierpnia 2011

Tower Defense Game

05.09 o 9.00 Musze oddac swoja gre, na razie prezentuje sie tak:
Co zostalo zrobione? Zarzadzanie teksturami, modelami, shaderami. Staralem sie stosowac Model-View-Controller, dlatego jest tez zarzadzanie modelami i widokami. Zrobilem proste animacje, np. obracania wiezyczki. Dodalem efekt bloom, poprawilem go troche w porownaniu do poprzedniej wersji (ktory byla testowana tylko z obrazkiem 2d ;)). Poza tym wydaje mi sie :), ze napisalem to zgodnie ze specyfikacja OpenGL 3.2 Core Profile.

A z wylaczonym bloomem, gra wyglada tak:
BTW, staralem sie jak najwiecej informacji trzymac poza kodem. To znaczy, np. czy bloom jest wlaczony czy nie decyduje o tym plik konfiguracyjny. O tym jak wyglada model (np. jego kolor) tez decyduja pliki zewnetrzne. Niby nic, a jakie fajne gdy sie testuje rozne rozwiazania. :)

Krotki film, jakze krotkiego intra. (:


Czy to wystarczy na zaliczenie? Powinno, biorac pod uwage fakt... o, i w tym momencie ugryze sie w jezyk. :)

piątek, 12 sierpnia 2011

Wavefront OBJ Model File Loader

Napisalem loader plikow .obj do swojej gry. Na szybko, niezdarnie ale dziala. :) Najpierw zczytuje dane z pliku .obj do klasy Geometry. Potem tworze obiekt GLTriangleBatch na podstawie tych danych. Miejsce na ulepszenia to przede wszystkim pozbycie sie redundancji danych i przyspieszenie dzialania, dzieki pominieciu klasy Geometry, Vertex, Normal, TextureCoordinate i ew. Face. Ale to moze kiedy, teraz "there is no time for this". :)




Link do projektu eclipse z kodem zrodlowym.



wtorek, 9 sierpnia 2011

Eclipse - Debugowanie i magiczne 57%.

W trakcie korzystania z Eclipse i pisania pod C++ mozna natknac sie na bardzo irytujacy mankament odnosnie debugowania. Gdy odpalamy aplikacje w tym trybie launcher zatrzymuje sie na dluga chwile przy 57%. W koncu sie zdenerwowalem i pogooglowalem chwile. ;) Obejscie jest proste, jak znalazlem na forum Eclipse:

I have followed the advice with regards to changing the Debug configuration and setting the preferred launcher to "Using GDB (DSF) Create Process Launcher". The debugger now launches almost instantly - thank you.

sobota, 6 sierpnia 2011

libGLTools1_static - Linux GLTools Static Library

Korzystajac z kodu biblioteki GLTools zamieszczonego na stronie książki OpenGL Superbible, zrobilem statyczna biblioteke dla linuxa x86_64. Zalaczam projekt w Eclipse, takze jezeli ktos potrzebuje moze jednym kliknieciem stworzyc dla siebie wersje dla architektury x86. Jezeli nie, to wiadomo - zassysamy libGLTools1_static i juz nie trzeba za kazdym razem kompilowac biblioteki w projektach. :)

libGLTools1_static.a
libGLTools1_static Eclipse Project

czwartek, 14 lipca 2011

Syntax Highliting in Blogger

Maly tescik, z bloga Heisencoder wykorzystalem instrukcje do podswietlania skladni, oto efekt:

int main( int argc, char * argv[] )
{
   return 0;
}

Dziala! :) No to teraz moge pisac powazne posty, haha!

niedziela, 12 czerwca 2011

OpenGL Blooom :)

W koncu udalo mi sie zrobic efekt bloom, korzystajac z GLSL. xD Screen gdzies na dole, moze jak uprzatne kod (straszny, okropny balagan) to wrzuce. Zamieszczam tez kod, ale dwie adnotacje: kod wrzucony jak leci, kod nie jest w 100% dokonczony, ale dziala na tyle na ile mi potrzeba. :) W przyszlosci poprawie (i post, i kod).

Wymaga OpenGL, GLEW (do shaderow) i GLTools.




/*
 * BloomPostProcess.h
 *
 *  Created on: 2011-06-27
 *      Author: myood
 */

/*
 * KNOWN BUGS:
 * * if new width while resizing is < 16, then GL_ERROR_INVALID_OPERATION occur
 */

#ifndef BLOOMPOSTPROCESS_H_
#define BLOOMPOSTPROCESS_H_

#include 
#include 
#include 
#include 
#include 

/*
 * Typical usage:
 * initialize once:
 * BloomPostProcess::getInstance()->init();
 *
 * in resize screen handling:
 * BloomPostProcess::getInstance()->resize();
 *
 * render loop:
 * {
 * BloomPostProcess::getInstance()->BeginRendering();
 * * --do normal rendering--
  * BloomPostProcess::getInstance()->BeginGlowParts();
 * --do rendering--
 * BloomPostProcess::getInstance()->PostProcess();
 * BloomPostProcess::getInstance()->display();
 * }
 *
 * cleanup:
 * BloomPostProcess::getInstance()->finalize();
 * BloomPostProcess::getInstance()->destroy();
 */

class BloomPostProcess {
public:
 enum BLOOM_STATE {
  NONEXISTENT,  //Before instatination or after destroy() call
  INSTANITED,  //After getInstance() call
  INITIALIZED,  //After init() or display() call
  RENDERED,  //After BeginRendering() call
  PREPROCESSED,  //After BeginGlowParts() call
  POSTPROCESSED, //After PostProcess() call
  FINALIZED,   //After cleanup() or finalize() call
  DESTROYED  //After destroy() call, next call will return NONEXISTENT
 };

 bool debug;
 static BloomPostProcess* getInstance();
 virtual ~BloomPostProcess(){};
 inline static enum BLOOM_STATE getState();

 /*
  * Initialize Bloom Post Process Effect
  */
 GLenum init(int newScreenWidth, int newScreenHeight, int RENDER_STEPS=3);

 /*
  * Do clean up - free memory etc.
  * Both functions do exactly the same.
  */
 GLenum finalize();
 inline GLenum cleanup();

 /*
  * Destroy post processing effect
  */
 GLenum destroy();

 /*
  * Resize handling
  */
 GLenum resize(int new_width, int new_height);

 /*
  * Pre and post process glow, display effect on screen
  */
 GLenum BeginRendering();
 GLenum BeginGlowParts();
 GLenum PostProcess();
 GLenum Display();

 /*
  * Resize screen function
  */
 //todo
 /*
  * LOTS OF TO DO :)
  */

protected:
 BloomPostProcess(bool doWeDebug= true)
 {
  mp_state= NONEXISTENT;
  debug= doWeDebug;
 }

private:
 static BloomPostProcess* mp_BloomPostProcess;
 BloomPostProcess( BloomPostProcess const& bps){}
 BloomPostProcess& operator=(BloomPostProcess const&){ return *this; }

 /*
  * CONFIGURATION
  */
 static BLOOM_STATE mp_state;
 int mp_iScreenWidth;
 int mp_iScreenHeight;
 int mp_iPostRenderSteps;

 GLenum* mp_fboBuffs;
 GLenum* mp_fboWindowBuff;

 //todo --- ?resize?
 /*
  * SHADERS
  */
 //This shader is used to downsampling textures
 GLint* mp_shScreenTextMVP;
 //This shader is used to find glowing parts,
 //but it is deprecated.
// GLint*     processGlowShader;
 //Blur scene horizontally
 GLint* mp_shBlurHori;
 //Blur scene verticaly
 GLint* mp_shBlurVerti;
 //This shader is used to create bloom effect -
 //it add all downsampled textures to the
 //normally rendered scene texture.
 GLint* mp_shPostProcess;

 /*
  * TEXTURES
  */
 GLuint* mp_texRendering;
 GLuint* mp_texGlowParts;
 GLuint* mp_texDownsampled;
 GLuint* mp_texHoriBlur;
 GLuint* mp_texVertBlur;
 GLuint* mp_texGlowing;

 /*
  * FBOS
  */
 GLuint* mp_fboRendering;
 GLuint* mp_fboGlowParts;
 GLuint* mp_fboDownsampled;
 GLuint* mp_fboHoriBlur;
 GLuint* mp_fboVertBlur;
 GLuint* mp_fboGlowing;

 /*
  * RBO
  */
 GLuint* mp_rboRendering;

 /*
  * PROJECTION MATRICES AND SCREEN QUADS
  */
 M3DMatrix44f* mp_OrthoMatrix;
 GLBatch* mp_ScreenQuad;


 /*
  * ACTIONS
  */
 GLenum createAndBindFBO(int* ScreenWidth, int* ScreenHeight, GLuint* FBO, GLuint* Texture, GLuint* DepthBuffer= NULL);
 GLenum renderTextureOnWholeScreen(GLuint* textureID, GLint* shader, float v4color[4], int i= 0);
};

#endif /* BLOOMPOSTPROCESS_H_ */



/*
 * BloomPostProcess.cpp
 *
 *  Created on: 2011-06-27
 *      Author: myood
 */

#include "BloomPostProcess.h"

// enum BLOOM_STATE {
//  NONEXISTENT,  //Before intatination or after destroy() call
//  INSTANITED,  //After getInstance() call
//  INITIALIZED,  //After init() call
//  FINALIZED,   //After cleanup() or finalize() call
//  DESTROYED  //After destroy() call, next call will return NONEXISTENT
// };

BloomPostProcess* BloomPostProcess::mp_BloomPostProcess= 0;
BloomPostProcess::BLOOM_STATE BloomPostProcess::mp_state= NONEXISTENT;

BloomPostProcess* BloomPostProcess::getInstance()
{
 if ( !mp_BloomPostProcess )
 {
  mp_BloomPostProcess= new BloomPostProcess();
  mp_state= INSTANITED;
 }

 return mp_BloomPostProcess;
}

BloomPostProcess::BLOOM_STATE BloomPostProcess::getState()
{
 return mp_state;
}

//todo changable postrender steps
GLenum BloomPostProcess::init(int newScreenWidth, int newScreenHeight, int POSTRENDER_STEPS)
{
 /*
  * STATE CHECK
  */
 if ( mp_state != INSTANITED )
 {
  if ( debug )
  {
   std::cerr  << "BloomPostProcess::init() Invalid operation.\n"
      << "\tObject is in wrong state - it must be INSTANITED.\n"
      << "\tYou can get current state by calling getState(). \n"
      << "\tCall getInstance() first, or finalize() and destroy() in that order."
      << std::endl;
  }

  return GL_INVALID_OPERATION;
 }

 /*
  * AVAILABLE TEXTURE UNITS CHECK
  */
 GLint iMAX_TEXT_UNITS= 0;
 glGetIntegerv(GL_MAX_TEXTURE_UNITS, &iMAX_TEXT_UNITS);
 if ( iMAX_TEXT_UNITS < POSTRENDER_STEPS+1 )
 {
  std::cerr  << "Error: Not enough available textures in multitexturing, required: "
     << POSTRENDER_STEPS+1 <<", implementation have: " << iMAX_TEXT_UNITS
     << std::endl;

  return GL_OUT_OF_MEMORY;
 }
 else
  std::cout  << "OK: Enough available textures in multitexturing, required: 4, "
     << "implementation have: " << iMAX_TEXT_UNITS
     << std::endl;

 if ( POSTRENDER_STEPS != 3 )
 {
  fprintf(stderr, "Error: Sorry, post render steps value must be 3, different render steps are not implemented yet." );
  return GL_INVALID_VALUE;
 }

 /*
  * CFG SAVE
  */
 mp_iPostRenderSteps= POSTRENDER_STEPS;
 mp_iScreenWidth= newScreenWidth;
 mp_iScreenHeight= newScreenHeight;

 /*
  * FLAT SCREEN MVP ALLOCATION
  */
 mp_OrthoMatrix= new M3DMatrix44f[mp_iPostRenderSteps];
 mp_ScreenQuad= new GLBatch[mp_iPostRenderSteps];

 /*
  * TEXTURES VARS ALLOCATION
  */
 //TODO
 //FREE MEMORY IN FINALIZE
 mp_texRendering= new GLuint;
 mp_texGlowParts= new GLuint;
 mp_texGlowing= new GLuint;
 mp_texDownsampled= new GLuint[mp_iPostRenderSteps];
 mp_texHoriBlur= new GLuint[mp_iPostRenderSteps];
 mp_texVertBlur= new GLuint[mp_iPostRenderSteps];

 /*
  * FBO & RBO VARS ALLOC
  */
 mp_fboRendering= new GLuint;
 mp_fboGlowParts= new GLuint;
 mp_fboDownsampled= new GLuint[mp_iPostRenderSteps];
 mp_fboHoriBlur= new GLuint[mp_iPostRenderSteps];
 mp_fboVertBlur= new GLuint[mp_iPostRenderSteps];
 mp_fboGlowing= new GLuint;
 mp_rboRendering= new GLuint;

 /*
  * ATTACHMENTS ALLOC AND SETUP
  */
 mp_fboBuffs= new GLenum(GL_COLOR_ATTACHMENT0);
 mp_fboWindowBuff= new GLenum(GL_BACK_LEFT);

 /*
  * SHADERS VARS ALLOC
  */
 mp_shScreenTextMVP= new GLint;
 mp_shBlurHori= new GLint;
 mp_shBlurVerti= new GLint;
 mp_shPostProcess= new GLint;

 GLenum error;

 if ( (error= createAndBindFBO(&mp_iScreenWidth,&mp_iScreenHeight,mp_fboRendering,mp_texRendering,mp_rboRendering)) != GL_NO_ERROR )
  return error;

 if ( (error= createAndBindFBO(&mp_iScreenWidth,&mp_iScreenHeight,mp_fboGlowParts,mp_texGlowParts)) != GL_NO_ERROR )
  return error;

 int l_screenWidth= mp_iScreenWidth >> 2;
 int l_screenHeight= mp_iScreenHeight >> 2;
 for ( int i= 0; i < mp_iPostRenderSteps; i++ )
 {
  gltGenerateOrtho2DMat(l_screenWidth, l_screenHeight, mp_OrthoMatrix[i], mp_ScreenQuad[i]);

  if ( (error= createAndBindFBO(&l_screenWidth,&l_screenHeight,&mp_fboDownsampled[i],&mp_texDownsampled[i])) != GL_NO_ERROR )
   return error;
  if ( (error= createAndBindFBO(&l_screenWidth,&l_screenHeight,&mp_fboHoriBlur[i],&mp_texHoriBlur[i])) != GL_NO_ERROR )
   return error;
  if ( (error= createAndBindFBO(&l_screenWidth,&l_screenHeight,&mp_fboVertBlur[i],&mp_texVertBlur[i])) != GL_NO_ERROR )
   return error;

  l_screenWidth= l_screenWidth >> 1;
  l_screenHeight= l_screenHeight >> 1;
 }

 //ladowanie shadera rysowania na ekranie
 *mp_shScreenTextMVP = gltLoadShaderPairWithAttributes(
   "shadery/textured.vp", "shadery/textured.fp", 2,
   GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "vTexCoords");

 //ladowanie shadera rozmycia horyzontalnego
 *mp_shBlurHori = gltLoadShaderPairWithAttributes(
   "shadery/blurh.vp", "shadery/blurh.fp", 2,
   GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "vTexCoords");
 //ladowanie shadera rozmycie pionowego
 *mp_shBlurVerti = gltLoadShaderPairWithAttributes(
   "shadery/blurv.vp", "shadery/blurv.fp", 2,
   GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "vTexCoords");

 //ladowanie shadera rozmycie pionowego
 *mp_shPostProcess = gltLoadShaderPairWithAttributes(
   "shadery/addition.vp", "shadery/addition.fp", 2,
   GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_TEXTURE0, "vTexCoords");

 mp_state= INITIALIZED;
 return GL_NO_ERROR;
}

GLenum BloomPostProcess::finalize()
{
 if ( mp_state != INITIALIZED )
 {
  if ( debug )
  {
   std::cerr  << "BloomPostProcess::finalize() Invalid operation.\n"
      << "\tObject is in wrong state - it must be INITIALIZED.\n"
      << "\tYou can get current state by calling getState(). \n"
      << "\tCall init() first."
      << std::endl;
  }

  return GL_INVALID_OPERATION;
 }

 // Cleanup textures
 for ( int i= 0; i < mp_iPostRenderSteps; i++ )
 {
  //TODO I HOPE ITS CORRECT :)
  glActiveTexture(GL_TEXTURE0 + i);
  glBindTexture(GL_TEXTURE_2D, 0);
 }
 glDeleteTextures(1, mp_texRendering);
 glDeleteTextures(1, mp_texGlowParts);
 glDeleteTextures(1, mp_texGlowing);
 glDeleteTextures(mp_iPostRenderSteps, mp_texDownsampled);
 glDeleteTextures(mp_iPostRenderSteps, mp_texHoriBlur);
 glDeleteTextures(mp_iPostRenderSteps, mp_texVertBlur);

 // Cleanup FBOs
 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
 glDeleteFramebuffers(mp_iPostRenderSteps, mp_fboDownsampled);
 glDeleteFramebuffers(mp_iPostRenderSteps, mp_fboHoriBlur);
 glDeleteFramebuffers(mp_iPostRenderSteps, mp_fboVertBlur);

 // Cleanup RBOs
 glDeleteRenderbuffers(1, mp_rboRendering);

 // Cleanup Progams
 glUseProgram(0);
 glDeleteProgram(*mp_shScreenTextMVP);
 glDeleteProgram(*mp_shBlurHori);
 glDeleteProgram(*mp_shBlurVerti);
 glDeleteProgram(*mp_shPostProcess);

 // Cleanup allocated memory
 delete [] mp_OrthoMatrix;
 delete [] mp_ScreenQuad;

 delete mp_texRendering;
 delete mp_texGlowParts;
 delete mp_texGlowing;
 delete [] mp_texDownsampled;
 delete [] mp_texHoriBlur;
 delete [] mp_texVertBlur;

 delete mp_fboRendering;
 delete mp_fboGlowParts;
 delete [] mp_fboDownsampled;
 delete [] mp_fboHoriBlur;
 delete [] mp_fboVertBlur;
 delete mp_fboGlowing;

 delete mp_fboBuffs;
 delete mp_fboWindowBuff;

 delete mp_rboRendering;

 delete mp_shScreenTextMVP;
 delete mp_shBlurHori;
 delete mp_shBlurVerti;
 delete mp_shPostProcess;

 mp_state= FINALIZED;

 return GL_NO_ERROR;
}
GLenum BloomPostProcess::cleanup()
{
 return finalize();
}
GLenum BloomPostProcess::destroy()
{
 if ( mp_state != FINALIZED )
 {
  if ( debug )
  {
   std::cerr  << "BloomPostProcess::destroy() Invalid operation.\n"
      << "\tObject is in wrong state - it must be FINALIZED.\n"
      << "\tYou can get current state by calling getState(). \n"
      << "\tCall finalize() first."
      << std::endl;
  }

  return GL_INVALID_OPERATION;
 }

 mp_state= DESTROYED;
 delete mp_BloomPostProcess;
 return GL_NO_ERROR;
}
GLenum BloomPostProcess::resize(int new_width, int new_height)
{
 if ( mp_state != INITIALIZED )
 {
  if ( debug )
  {
   std::cerr  << "BloomPostProcess::resize() Invalid operation.\n"
      << "\tObject is in wrong state - it must be INITIALIZED.\n"
      << "\tYou can get current state by calling getState(). \n"
      << "\tCall init() first."
      << std::endl;
  }

  return GL_INVALID_OPERATION;
 }


 if ( new_height < 16 )
  new_height= 16;
 if ( new_width < 16 )
  new_height= 16;

//New sizes
 mp_iScreenWidth= new_width;
 mp_iScreenHeight= new_height;

//DELETE PREVIOUS FBOS TEXTURES
 // Cleanup textures
 for ( int i= 0; i < mp_iPostRenderSteps; i++ )
 {
  //TODO I HOPE ITS CORRECT :)
  glActiveTexture(GL_TEXTURE0 + i);
  glBindTexture(GL_TEXTURE_2D, 0);
 }
 glDeleteTextures(1, mp_texRendering);
 glDeleteTextures(1, mp_texGlowParts);
 glDeleteTextures(1, mp_texGlowing);
 glDeleteTextures(mp_iPostRenderSteps, mp_texDownsampled);
 glDeleteTextures(mp_iPostRenderSteps, mp_texHoriBlur);
 glDeleteTextures(mp_iPostRenderSteps, mp_texVertBlur);

 // Cleanup FBOs
 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
 glDeleteFramebuffers(mp_iPostRenderSteps, mp_fboDownsampled);
 glDeleteFramebuffers(mp_iPostRenderSteps, mp_fboHoriBlur);
 glDeleteFramebuffers(mp_iPostRenderSteps, mp_fboVertBlur);

 // Cleanup RBOs
 glDeleteRenderbuffers(1, mp_rboRendering);

//APPLY NEW SIZES
 GLenum error;

 if ( (error= createAndBindFBO(&mp_iScreenWidth,&mp_iScreenHeight,mp_fboRendering,mp_texRendering,mp_rboRendering)) != GL_NO_ERROR )
  return error;

 if ( (error= createAndBindFBO(&mp_iScreenWidth,&mp_iScreenHeight,mp_fboGlowParts,mp_texGlowParts)) != GL_NO_ERROR )
  return error;

 int l_screenWidth= mp_iScreenWidth >> 2;
 int l_screenHeight= mp_iScreenHeight >> 2;
 for ( int i= 0; i < mp_iPostRenderSteps; i++ )
 {
  gltGenerateOrtho2DMat(l_screenWidth, l_screenHeight, mp_OrthoMatrix[i], mp_ScreenQuad[i]);

  if ( (error= createAndBindFBO(&l_screenWidth,&l_screenHeight,&mp_fboDownsampled[i],&mp_texDownsampled[i])) != GL_NO_ERROR )
   return error;
  if ( (error= createAndBindFBO(&l_screenWidth,&l_screenHeight,&mp_fboHoriBlur[i],&mp_texHoriBlur[i])) != GL_NO_ERROR )
   return error;
  if ( (error= createAndBindFBO(&l_screenWidth,&l_screenHeight,&mp_fboVertBlur[i],&mp_texVertBlur[i])) != GL_NO_ERROR )
   return error;

  l_screenWidth= l_screenWidth >> 1;
  l_screenHeight= l_screenHeight >> 1;
 }

 return GL_NO_ERROR;
}
GLenum BloomPostProcess::BeginRendering()
{
 if ( mp_state != INITIALIZED )
 {
  if ( debug )
  {
   std::cerr  << "BloomPostProcess::BeginRendering() Invalid operation.\n"
      << "\tObject is in wrong state - it must be INITIALIZED.\n"
      << "\tYou can get current state by calling getState(). \n"
      << "\tCall init() first."
      << std::endl;
  }

  return GL_INVALID_OPERATION;
 }

 /*
  * After calling this function you shall render your scene as usual
  */
 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, *mp_fboRendering);
 glDrawBuffers(1, mp_fboBuffs);

 mp_state= RENDERED;
 return GL_NO_ERROR;
}

GLenum BloomPostProcess::BeginGlowParts()
{
 if ( mp_state != RENDERED )
 {
  if ( debug )
  {
   std::cerr  << "BloomPostProcess::BeginGlowParts() Invalid operation.\n"
      << "\tObject is in wrong state - it must be RENDERED.\n"
      << "\tYou can get current state by calling getState(). \n"
      << "\tCall BeginRender() and render your scene first."
      << std::endl;
  }

  return GL_INVALID_OPERATION;
 }

 /*
  * After calling this function you shall render ONLY glowing parts
  * in way that you find appropriate
  */
 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, *mp_fboGlowParts);
 glDrawBuffers(1, mp_fboBuffs);

 mp_state= PREPROCESSED;
 return GL_NO_ERROR;
}

GLenum BloomPostProcess::PostProcess()
{
 if ( mp_state != PREPROCESSED )
 {
  if ( debug )
  {
   std::cerr  << "BloomPostProcess::PostProcess() Invalid operation.\n"
      << "\tObject is in wrong state - it must be PREPROCESSED.\n"
      << "\tYou can get current state by calling getState(). \n"
      << "\tCall BeginGlowParts() and render glow parts on your scene first."
      << std::endl;
  }

  return GL_INVALID_OPERATION;
 }
 //todo delete v4color
 GLfloat v4color[]= {
     1.0f, 1.0f, 1.0f, 1.0f
  };
 /*
  * DOWNSAMPLING
  */
 for ( int i= 0; i < mp_iPostRenderSteps; i++ )
 {
  glViewport(0,0, mp_iScreenWidth >> (i+2), mp_iScreenHeight >> (i+2));
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mp_fboDownsampled[i]);
  glDrawBuffers(1, mp_fboBuffs);
  {
   //rozmywanie w poziomie  >> 
   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
   {
    renderTextureOnWholeScreen(mp_texGlowParts,mp_shScreenTextMVP,v4color,i);
   }
  }
 }

 /*
  * BLURING HORIZONTALLY
  */
 for ( int i= 0; i < mp_iPostRenderSteps; i++ )
 {
  glViewport(0,0, mp_iScreenWidth >> (i+2), mp_iScreenHeight >> (i+2));
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
  //bufor do rozmywania w poziomie
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mp_fboHoriBlur[i]);
  glDrawBuffers(1, mp_fboBuffs);
  {
   //rozmywanie w poziomie  >> 
   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
   {
    renderTextureOnWholeScreen(&mp_texDownsampled[i],mp_shBlurHori,v4color,i);
   }
  }
 }

 /*
  * BLUR VERTICALLY
  */
 for ( int i= 0; i < mp_iPostRenderSteps; i++ )
 {
  glViewport(0,0, mp_iScreenWidth >> (i+2), mp_iScreenHeight >> (i+2));
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
  //bufor do rozmywania w poziomie
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mp_fboVertBlur[i]);
  glDrawBuffers(1, mp_fboBuffs);
  {

   //rozmywanie w poziomie  >> 
   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
   {
    renderTextureOnWholeScreen(&mp_texHoriBlur[i],mp_shBlurVerti,v4color,i);
   }
  }
 }

 mp_state= POSTPROCESSED;
 return GL_NO_ERROR;
}
GLenum BloomPostProcess::Display()
{
 if ( mp_state != POSTPROCESSED )
 {
  if ( debug )
  {
   std::cerr  << "BloomPostProcess::Display() Invalid operation.\n"
      << "\tObject is in wrong state - it must be POSTPROCESSED.\n"
      << "\tYou can get current state by calling getState(). \n"
      << "\tCall BeginGlowParts() and render your scene first."
      << std::endl;
  }

  return GL_INVALID_OPERATION;
 }

 GLfloat v4color[]= {
     1.0f, 1.0f, 1.0f, 1.0f
  };

 glViewport(0,0,mp_iScreenWidth,mp_iScreenHeight);
 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
 glDrawBuffers(1, mp_fboWindowBuff);
 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

 glUseProgram(*mp_shPostProcess);
 glActiveTexture(GL_TEXTURE0);
 glBindTexture(GL_TEXTURE_2D, *mp_texRendering);
 glActiveTexture(GL_TEXTURE1);
 glBindTexture(GL_TEXTURE_2D, mp_texVertBlur[0]);
 glActiveTexture(GL_TEXTURE2);
 glBindTexture(GL_TEXTURE_2D, mp_texVertBlur[1]);
 glActiveTexture(GL_TEXTURE3);
 glBindTexture(GL_TEXTURE_2D, mp_texVertBlur[2]);

 //Ustawiam zmienna uniform shadera wierzcholkow - macierz projekcji
 GLint locMVP = glGetUniformLocation(*mp_shPostProcess, "mvpMatrix");
 glUniformMatrix4fv(locMVP, 1, GL_FALSE, mp_OrthoMatrix[0]);
 GLint locColor = glGetUniformLocation(*mp_shPostProcess, "v4color");
 glUniform4fv(locColor,1,v4color);
 GLint iTextureUniform = glGetUniformLocation(*mp_shPostProcess, "colorMap");
 glUniform1i(iTextureUniform, 0);
 iTextureUniform = glGetUniformLocation(*mp_shPostProcess, "colorMap0");
 glUniform1i(iTextureUniform, 1);
//todo SHADER IS NOT COMPATIBLE WITH DIFFERENT POST RENDER STEPS
// if ( mp_iPostRenderSteps > 1 )
// {
  iTextureUniform = glGetUniformLocation(*mp_shPostProcess, "colorMap1");
  glUniform1i(iTextureUniform, 2);
//  if ( mp_iPostRenderSteps > 2 )
//  {
   iTextureUniform = glGetUniformLocation(*mp_shPostProcess, "colorMap2");
   glUniform1i(iTextureUniform, 3);
//  }
// }

 mp_ScreenQuad[0].Draw();

 mp_state= INITIALIZED;
 return GL_NO_ERROR;
}
GLenum BloomPostProcess::createAndBindFBO(int* ScreenWidth, int* ScreenHeight, GLuint* FBO, GLuint* Texture, GLuint* DepthBuffer)
{
 if ( ScreenWidth == NULL || ScreenHeight == NULL || FBO == NULL || Texture == NULL )
 {
  std::cerr  << "BloomPostProcess::createAndBindFBO:\n"
     << "\t One of the needed argument is null pointer."
     << std::endl;

  return GL_INVALID_VALUE;
 }
 // Create and bind an FBO
 glGenFramebuffers(1,FBO);
 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, *FBO);
 // Create depth renderbuffer
 if ( DepthBuffer != 0 )
 {
  glGenRenderbuffers(1, DepthBuffer);
  glBindRenderbuffer(GL_RENDERBUFFER, *DepthBuffer);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, *ScreenWidth, *ScreenHeight);
 }
 // Create the reflection texture
 glGenTextures(1, Texture);
 glBindTexture(GL_TEXTURE_2D, *Texture);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, *ScreenWidth, *ScreenHeight, 0, GL_RGBA, GL_FLOAT, NULL);
 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *Texture, 0);
 if ( DepthBuffer != 0 )
  glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *DepthBuffer);
 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

 return gltCheckErrors();
}

GLenum BloomPostProcess::renderTextureOnWholeScreen(GLuint* textureID, GLint* shader, float v4color[4], int i)
{
 glUseProgram(*shader);
 glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, *textureID);

    //Ustawiam zmienna uniform shadera fragmentow - texture
    GLint iTextureUniform = glGetUniformLocation(*shader, "colorMap");
    glUniform1i(iTextureUniform, 0);
    //Ustawiam zmienna uniform shadera wierzcholkow - macierz projekcji
    GLint locMVP = glGetUniformLocation(*shader, "mvpMatrix");
    glUniformMatrix4fv(locMVP, 1, GL_FALSE, mp_OrthoMatrix[i]);

    GLint locColor = glGetUniformLocation(*shader, "v4color");
    glUniform4fv(locColor,1,v4color);

 mp_ScreenQuad[i].Draw();

 return GL_NO_ERROR;
}

sobota, 26 marca 2011

very simple wav file management in ansi c

opening, reading, processing (normalizing, changing length), creating and writing wav files in ansi c

src.c:
http://paste.pocoo.org/show/361778/
common.h:
http://paste.pocoo.org/show/361780/

No errors found, its final. ;)

czwartek, 24 marca 2011

VC++ Express Directories

Previously in VC++ 2005 Express or 2008 it was quite simple to add new paths (directories) to IDE. Now you have to do two things:
1) first, make Property Manager visible (its hidden by default in Express).
Tools> Settings> Expert Settings
2) second, go to Property Manager, RMB on project> Properties> Configuration Properties> VC++ Directories.

Damn, it took me a while to find this and i saw lots of posts asking about how to do this.