czwartek, 28 maja 2009

XML i YAML w OpenCV

Czasu ostatnio niewiele (zaliczenia i te sprawy), a na blogu zastój. W OpenCV pracuję nad czymś "grubszym" dlatego od czasu do czasu zarzucę jakąś krótszą notką, żeby sprawić wrażenie, że blog jeszcze żyje :P

Dzisiaj będzie o XML/YAML. Najczęściej wykorzystujemy tego typu pliki do przechowywania parametrów lub ustawień programu. Zapis do pliku wygląda następująco:

// otwieramy plik do zapisu
CvFileStorage * fs = cvOpenFileStorage("conf.xml",0,CV_STORAGE_WRITE);

// zapisujemy dane
cvWriteInt(fs,"wysokosc", 480);
cvWriteInt(fs,"szerokosc", 640);

// zwalniamy zasoby
cvReleaseFileStorage(&fs);


i w ten sposób otrzymujemy plik conf.xml:



480
640



lub, zmieniając rozszerzenie, plik conf.yml:

%YAML:1.0
wysokosc: 480
szerokosc: 640


Odczyt danych wygląda podobnie do zapisu:
// otwieramy plik do odczytu
CvFileStorage * fs = cvOpenFileStorage("conf.xml", 0, CV_STORAGE_READ);

// odczytujemy dane
int wysokosc = cvReadIntByName(fs, 0, "wysokosc");
int szerokosc = cvReadIntByName(fs, 0, "szerokosc");

// zwalniamy zasoby
cvReleaseFileStorage(&fs);


To właściwie tyle z podstaw :) Oczywiście OpenCV daje więcej możliwości manipulowania danymi. Zamiast zapisywać wartości oddzielnie, możemy to zrobić w ciągu:
cvWriteInt(fs,"fps",30);
// zapisujemy strukture
cvStartWriteStruct(fs,"rozmiar",CV_NODE_SEQ);
cvWriteInt(fs,0, 480);
cvWriteInt(fs,0, 640);
cvEndWriteStruct(fs);


Otrzymamy pliki:


30

480 640


%YAML:1.0
fps: 30
rozmiar:
- 480
- 640


taką strukturę odczytujemy tak:

// otrzymujemy sekwencje - podobnie jak w detekcji twarzy
CvSeq* seq = cvGetFileNodeByName(fsr,0,"rozmiar")->data.seq;

int wysokosc = cvReadInt((CvFileNode*)cvGetSeqElem(seq,0));
int szerokosc = cvReadInt((CvFileNode*)cvGetSeqElem(seq,1));


Do plików XML/YAML możemy zapisywać także inne rzeczy:
CvMat* macierz = cvCreateMat(5,6,CV_32F);
cvZero(macierz);

// zapisujemy komentarz...
cvWriteComment(fs,"Komentarz",0);
// ...napis...
cvWriteString(fs,"napis","zapisany lancuch znakow");
// ...liczbe rzeczywista...
cvWriteReal(fs,"liczba",3.14);
// ...strukture (np. macierz)...
cvWrite(fs,"macierz",macierz);




"zapisany lancuch znakow"
3.1400000000000001

5
6
f
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.

%YAML:1.0
# Komentarz
napis: zapisany lancuch znakow
liczba: 3.1400000000000001
macierz: !!opencv-matrix
rows: 5
cols: 6
dt: f
data: [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0. ]


odczyt:
CvMat* macierz = (CvMat*) cvReadByName(fs,0,"macierz");
double liczba = cvReadRealByName(fs,0,"liczba",0.0);
const char* napis = cvReadStringByName(fs,0,"napis","domyslny");


Do plików tych można zapisywać także skomplikowane struktury drzewiaste. Wspomnę o tym gdy będę opisywał takie struktury :)

wtorek, 26 maja 2009

Linux Mint 7 wydany

Wydano najnowszą wersję Linuksa Mint o numerze 7 i nazwie "Gloria". Więcej informacji na oficjalnym blogu dystrybucji.

niedziela, 10 maja 2009

Detekcja twarzy

Detekcja twarzy jest obecnie powszechnie wykorzystywana (np. w ustawianiu ostrości aparatów cyfrowych). Gdy dowiemy się gdzie na obrazie znajdują się twarze, możemy wykonać wiele ciekawych operacji. Najpierw jednak musimy je znaleźć :) Za proces ten w OpenCV odpowiadna jedna funkcja... Kod:

CvCapture* vid = cvCreateCameraCapture(0);
cvNamedWindow("detekcja twarzy", CV_WINDOW_AUTOSIZE);
cvQueryFrame(vid);
double fps = 25;
int odstep_miedzy_klatkami = 1000 / fps;

// kolory do zaznaczania twarzy
static CvScalar kolory[] = {
{{19.0,69.0,139.0,0.0}},
{{63.0,133.0,205.0,0.0}},
{{96.0,164.0,244.0,0.0}},
};

// deklarujemy pamiec na obliczenia
CvMemStorage * storage = cvCreateMemStorage(0);

// tworzymy klasyfikator
// jako agrument musimy podac siezke do pliku z efektem treningu klasyfikatora
CvHaarClassifierCascade * haar = (CvHaarClassifierCascade*) cvLoad("/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt.xml");

while (true)
{
IplImage* ramka = cvQueryFrame(vid);

if (ramka == 0)
break;

// czyscimy bufor
cvClearMemStorage(storage);

// skala
double skala = 1.5;

// przygotowujemy obrazy posrednie
IplImage *temp = cvCreateImage(cvSize(ramka->width, ramka->height), 8, 1);
IplImage *temp2 = cvCreateImage(cvSize(cvRound(ramka->width / skala), cvRound(ramka->height / skala)), 8, 1);

// zamieniamy kolory na skale szarosci
cvConvertImage(ramka, temp, CV_BGR2GRAY);

// zmniejszamy czarno-bialy obraz
cvResize(temp, temp2, CV_INTER_LINEAR);

// szukamy twarzy w danej ramce
CvSeq *wynik = cvHaarDetectObjects(temp2, haar, storage, 1.1, 3, CV_HAAR_DO_CANNY_PRUNING, cvSize(30,30));

// iterujemy po wszystkich wynikach
for (int i = 0; i < (wynik ? wynik->total : 0); i++)
{
// pobieramy prostokat z pozycja wskazujaca twarz
CvRect * twarz = (CvRect*) cvGetSeqElem(wynik, i);


// ustalamy dwa punkty po przekatnej prostokata
CvPoint punkt1 = cvPoint(cvRound(twarz->x * skala),cvRound(twarz->y * skala));
CvPoint punkt2 = cvPoint(cvRound((twarz->x + twarz->width)* skala),cvRound((twarz->y + twarz->height) * skala));

// rysujemy prostokat zaznaczajacy twarz na obrazie
cvRectangle(ramka,punkt1,punkt2,kolory[i%3],2);

}


cvShowImage("detekcja twarzy", ramka);

cvReleaseImage(&temp);
cvReleaseImage(&temp2);

int c = cvWaitKey(odstep_miedzy_klatkami);
if (c == 'k')
break;

}

cvReleaseHaarClassifierCascade(&haar);
cvDestroyAllWindows();
cvReleaseCapture(&vid);


Powyższy kod to modyfikacja z jednego z wcześniejszych wipsów. Na uzyskany z kamery obraz dokładane są prostokąty zaznaczające twarze.

Parę słów komentarza do kodu: pierwsza sprawa to plik xml z danymi z treningu. Plik ten zawiera dane do klasyfikatora cech Haara. Same klasyfikatory to dość rozbudowana dziedzina związana ze sztuczną inteligencją. Nas interesuje to, że klasyfikator zbudowany w oparciu o dany plik pozwoli nam wykryć twarze skierowane frontalnie do kamery. Użycie innego pliku pozwoli nam wykryć np. twarze uchwycone z profilu czy ludzką postać. W kolejnych wersjach biblioteki ma pojawić się ich więcej. Można je znaleźć w odpowiednim katalogu OpenCV.

Przygotowanie obrazu przed detekcją. Detekcja twarzy odbywa się w różnej skali, co pozwala znaleźć twarze ludzi zarówno bliżej jak i dalej obiektywu. Sam proces jest dość złożony. Podanie do wykrywania mniejszego obrazu powoduje znaczne przyspieszenie detekcji, przy niewielkim spadku jego skuteczności (jednak z utratą możliwości wykrywania mocno oddalonych twarzy). Podobnie jest z zamianą kolorów na skalę szarości.

Najważniejszym aspektem w detekcji frontalnych twarzy są oczy. Kiedy jedno oko jest przysłonięte, twarz w większości przypadków nie powinna zostać wykryta. Można też pobawić się w drugą stronę i "spreparować" twarz jak na filmiku ;-)



Zachęcam do modyfikacji parametrów. U mnie, niestety, szybkość nie powala, ale nad tym jeszcze popracuję :)

niedziela, 3 maja 2009

Dzień z kulturą japońską w Bytomiu

16.05.2009 odbędzie się w Bytomiu Dzień Kwitnącej Wiśni. Organizatorem imprezy jest Polsko-Japońska Wyższa Szkoła Technik Komputerowych WZI w Bytomiu. Poszczególne atrakcje odbywać się będą w jej budynkach.

W programie przewidziano pokazy i konkursy związane z m.in. japońskimi sztukami walki, konsolami, anime/mangą, karaoke czy japońską kuchnią. Więcej informacji na stronie organizatora. Dla każdego fana kultury dalekiego wschodu wydarzenie obowiązkowe :)

Do zobaczenia w Bytomiu za 13 dni :)