poniedziałek, 23 lutego 2009

OpenCV cz. 2. Rysujemy i piszemy

Do rysowania prostych figur przygotowano odpowiednie funkcje. Podobnie ma się rzecz z wyświetlaniem tekstu. Aby wyświetlić linię prostą skorzystamy z funkcji cvLine. Jako parametry przyjmuje ona wskaźnik na CvArr, dwa punkty oznaczające końce linii oraz kolor. Ponadto można ustawić kilka parametrów opcjonalnych pozwalających ustawić grubość linii czy sposób jej wyświetlania (np. z antyaliasingiem). Przy ustawionym ROI, linia zostanie wyświetlona tylko w jego obszarze.
// tworzy linię o końcach [1,1] oraz [100,100], koloru białego, o grubości 3 pikseli
cvLine(img,cvPoint(1,1),cvPoint(100,100),cvScalarAll(255.0),3);

Jak widać, jest ona podobna w nazewnictwie do innych funkcji tej biblioteki. Idąc dalej mamy możliwość umieszczania innych obiektów geometrycznych: prostokąta, elipsy itd. Przykładowy kod na końcu notki (aby lepiej zrozumieć działanie funkcji cvEllipse zachęcam do zapoznania się z tym obazkiem). Inne funkcje wysujące (np. wielokąty) działają podobnie, w razie potrzeby odsyłam, a jakże inaczej, do dokumentacji.

Pisanie jest czynnością podobną do rysowania, ale wcześniej należy zainicjować czcionkę.
// zainicjowanie czcionki
CvFont font = cvFont(1.0,1);
cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX,1.0,1.0);

a same napisy tworzymy funkcją cvPutText.
Poniżej kod na podsumowanie:
#include "cv.h"
#include "cxcore.h"
#include "cxtypes.h"
#include "highgui.h"

/*
* Rysowanie i pisanie
* autor: ratixu.blogspot.com
*/
int main(int argc, char** argv)
{
IplImage* img = cvCreateImage(cvSize(700, 700), IPL_DEPTH_32F, 3);

// tworzenie linii o końcach w punktach [5,1] oraz [200,100]
// o kolorze białym, grubości 5 pkseli z antyaliasingiem
cvLine(img,cvPoint(5,1),cvPoint(200,100),cvScalarAll(255.0),5,CV_AA);

// tworzenie prostokąta o przekątnej o końcach w punktach [5,110]
// oraz [200,210] grubości 4 pksele
cvRectangle(img,cvPoint(5,110),cvPoint(200, 210),cvScalar(122,121,43),4);

// tworzenie elipsy - jedna z metod
CvPoint srodek = cvPoint(200,350);
CvSize osie = cvSize(200,100);
double kat = 360.0;
double katStartowy = 0.0;
double katKoncowy = kat;
CvScalar kolor = cvScalarAll(255.0);
int grubosc = 5;
cvEllipse(img,srodek,osie,kat,katStartowy,katKoncowy,kolor,grubosc);

// tworzenie okręgu o promieniu 75 pikseli i środku w punkcie [200,600]
// oraz grubości 5 pikseli
cvCircle(img,cvPoint(200,600),75,cvScalarAll(255.0),5);

// zainicjowanie czcionki:
// skala rozciągnięcia, przyjumjąc 1 za 100%
double skala = 2.0;
// pochylenie czcionki, przyjmując 1.0 za 45 stopni
double kursywa = 0.2;
CvFont font = cvFont(skala,grubosc);
// włączamy antyalliasing, CV_FONT_XXX to typy dostępnych czcionek
cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX,skala,skala,kursywa,grubosc,CV_AA);


//podpisujemy figury
cvPutText(img,"Odcinek",cvPoint(300,100),&font,cvScalarAll(255.0));
cvPutText(img,"Prostokat",cvPoint(300,200),&font,cvScalarAll(255.0));
cvPutText(img,"Elipsa",cvPoint(450,350),&font,cvScalarAll(255.0));
cvPutText(img,"Okrag",cvPoint(300,650),&font,cvScalarAll(255.0));

cvNamedWindow("img",0);
cvShowImage("img", img);
while(true)
{
if(cvWaitKey(100) == 'k') break;
}

// zapisujemy wynik do pliku
cvSaveImage("/home/lukasz/Desktop/rysowanie.png",img);
cvDestroyWindow("img");
cvReleaseImage(&img);
return (EXIT_SUCCESS);
}

a tutaj zapisany obraz:

piątek, 13 lutego 2009

OpenCV cz. 1. Podstawy

Dzisiaj będzie trochę o strukturach w OpenCV. Podstawą jest struktura o nazwie CvArr. Jak sama nazwa wskazuje jest to jednowymiarowa tablica przechowująca dane określonego typu. Dalej znajdziemy CvMat czyli tablicę wielowymiarową oraz IplImage który jest odzwierciedleniem obrazu. Struktury wymieniono w takiej kolejności nie bez powodu: można ich generalnie używać jakby istniał schemat dziedziczenia Arr -> Mat -> Image. Sama biblioteka to w większości funkcje napisane w C i kilka klas w C++. Mamy tu zatem do czynienia z klasycznymi strukturami i ciężko mówić o dziedziczeniu obiektowym. Piszę o tym ponieważ w większości funkcji bibliotecznych które przyjmują jako parametr CvArr możemy spokojnie posłać w tym miejscu CvMat lub IplImage i dalej, jeżeli przyjmuje CvMat to przyjmuje także IplImage. Nie dotyczy to jednak metod napisanych samodzielnie... W skład tych struktur, oprócz wskaźnika na dane, wchodzą inne przydatne informacje takie jak liczba kanałów, wysokość, szerokość itp. Poniżej trochę kodu:
// macierz o 30 wierszach i 40 kolumnach, składająca się z elementów 32 bitowych zmiennoprzecinkowych
CvMat* mat = cvCreateMat(30,40,CV_32F);

// utworzenie obrazka o rozdzielczości 800x600, głębi 32 bitowej i 4 kanałach
IplImage* img = cvCreateImage(cvSize(800,600),IPL_DEPTH_32F,4);

// wierna kopia obrazka, macierz posiada analogiczną metodę cvCloneMat
IplImage* img2 = cvCloneImage(img);
CvMat* mat2 = cvCloneMat(mat);

// oczywiście sprzątamy po sobie
cvReleaseImage(&img);
cvReleaseImage(&img2);
cvReleaseMat(&mat);
cvReleaseMat(&mat2);

Kolejnymi strukturami o których warto wspomnieć są: CvSize, CvScalar i CvRect. Pojawiają się one dosyć często do określania parametrów funkcji (jak np. w w/w konstruktorach). CvSize służy do określenia wysokości i szerokości, CvScalar do przetrzymywania 4 wartości typu dobule (np. jako kolor w formacie RGBA) a CvRect do określania prostokątów (wysokość,szerokość,x,y).
// prostokąt o wymiarach 40x30 kórego pozycja startowa (lewy górny róg)
// jest w punkcie [0,0]
CvRect rect = cvRect(0,0,40,30);
// podobne do CvRect ale bez punktu zaczepienia
CvSize size = cvSize(40,30);
// Prosty skalar
CvScalar scal = cvScalar(23.2,34.2,45.7,23.5);
// odpowiada to funkcji cvScalar(0.3,0.3,0.3,0.3);
CvScalar scal2 = cvScalarAll(0.3);

Takich struktur jest jeszcze trochę. W razie problemów najlepiej sięgnąć do dokumentacji i API (znajdziecie je w dziale linki - wizja komputerowa). Pisanie ułatwia oczywiście dobre IDE. Podpowiada one składnię i można szybko zobaczyć jakie parametry posiada funkcja. Wiele struktur ma gotowe funkcje tworzące, które ułatwiają poszukiwania ;-)

Czas wrócić do macierzy i obrazów. Wypadałoby jeszcze wspomnieć jak dostać się do ich elementów. Na początek najlepiej posłużyć się cvGet2D przyjmującym dwie współrzędne i, oczywiście, wskaźnik do CvArr dzięki czemu działa na macierzach i obrazkach. Podobnie przedstawia się ustawianie wartości za pomocą funkcji cvSet2D
// pobranie wartości spod komórki [1,1]
// przyjęte parametry to: wskaźnik na strukturę, współrzędna Y, współrzędna X
CvScalar s1 = cvGet2D(img,1,1);
// działa tak jak powyższa metoda, ale zwraca tylko wartość double
double s2 = cvGetReal2D(mat,1,1);

// ustawianie wartości
// przyjęte parametry to: wskaźnik na strukturę, współrzędna Y, współrzędna X,
// CvScalar z wartością
cvSet2D(mat,1,1,cvScalarAll(0.5));
cvSet2D(img,1,1,,cvScalarAll(0.5));

Oczywiście sami musimy zadbać w tym przypadku czy nie pobieramy danych spoza zakresu. Opisana metoda nie jest najszybszą (można wręcz powiedzieć że najwolnieszją) metodą dostępu to pikseli. Później poznamy szybsze ;-)

Inną użyteczną rzeczą są tzw. ROI (Region of Interest) oraz COI (Color of Interest). Pozwalają one ograniczyć obszar (lub kolor) który jest przetwarzany. Wiele funkcji sprawdza czy podany obraz ma ustawiony ROI i stosuje algorytm tylko na nim. Z COI jest już trochę gorzej ale powiedzmy że także działa :)

// ustawienie ROI na kwadrat o boku 10, zaczynający się w [0,0]
cvSetImageROI(img,cvRect(0,0,10,10));
// ustawienie kanału COI
cvSetImageCOI(img,1);

Podsumowując:
#include "cxtypes.h"
#include "cxcore.h"
#include "highgui.h"

/*
* Manipulowanie danymi
* autor: ratixu.blogspot.com
*/
int main(int argc, char** argv)
{

IplImage* img = cvCreateImage(cvSize(300,300),IPL_DEPTH_32F,3);

for(int i = 0; i < img->width; i++)
for(int j = 0; j < img->height; j++)
{
if(i==j)
cvSet2D(img,i,j,cvScalar(255,0,255,0));
else if (i < j)
cvSet2D(img,i,j,cvScalar(0,255,255,0));
else
cvSet2D(img,i,j,cvScalar(255,255,0,0));
}

cvNamedWindow("okienko", 1);
// metoda ta wyświetla obraz. Sama dostosuje rozmiar okna
cvShowImage("okienko",img);
while(true)
{
if(cvWaitKey(100) == 'q') break;
}
cvDestroyWindow("okienko");
cvReleaseImage(&img);
return (EXIT_SUCCESS);
}

i efekt wywołania:

niedziela, 8 lutego 2009

OpenCV cz. 0. Wprowadzenie

Tym wpisem rozpoczynam uproszczony kurs OpenCV. Przyznaję bez bicia, że prawdopodobnie będzie dość chaotyczny i nieregularny, ale przynajmniej będzie można przeglądać wpisy w dowolnej kolejności ;-) Używany przeze mnie zestaw to Linux Mint + Netbeans. Konfiguracja jest zapewne podobna na innych systemach i środowiskach więc zaczynamy.

Rzeczy poniżej opisane mogą wydawać się oczywiste dla wprawionego programisty, więc takowy może spokojnie przejść do przykładowego kodu.

Gdy posiadamy już Netbeansa (ściągnąć można stąd, należy zwrócić uwagę by posiadał moduł C/C++), tworzymy nowy projekt. Wybieramy nową aplikację C++
Dalej możemy zmienić takie rzeczy jak nazwa, czy ścieżka lub po prostu kliknąć "Finish". Następnie wybieramy opcje nowo utworzonego projektuNastępnie w części Build->C++ Compiler wskazujemy ścieżkę do plików nagłówkowych (u mnie będzie to /usr/include/opencv)
Kolejnym krokiem jest wskazanie katalogu dodatkowych bibliotek dynamicznych (Additional Library Directories w części Linker). Są to pliki .so w linuksie lub .dll w windowsie, u mnie ten katalog to /usr/lib. Dalej dołączenie bibliotek statycznych (pliki .a w linuksie i bodajże .lib w windowsie, u mnie w katalogu /usr/lib), przykładowy zestaw na załączonym poniżej zrzucie.

Kolejną czynnością jest dodanie plików nagłówkowych do projektu. Klikamy prawym przyciskiem na katalog Headers a następnie na Add Exsisting Items from Folder i wskazujemy odpowiednią ścieżkę (u mnie /usr/include/opencv). Można zamiast katalogu podać bezpośrenio pliki poleceniem Add Exsisting Items, wtedy podczas includowania w kodzie wymagana będzie jedynie ich nazwa



To by było na tyle z przygotowań, można zacząć pisać kod. Tworzymy nowy plik i wpisujemy poniższy kod:
#include "opencv/highgui.h"
int main(int argc, char** argv)
{

cvNamedWindow("okienko", 1);
while(true)
{
if(cvWaitKey(100) == 'q') break;
}
cvDestroyWindow("okienko");
return 0;
}

Program ten tworzy nowe okno, następnie czeka na wciśnięcie klawisza q, po czym kończy działanie. Dalsza część kursu wkrótce ;-)

sobota, 7 lutego 2009

Linux Mint 6 64-bit oficjalnie wydany

Wczoraj wydana została wersja 64-bitowa szóstej edycji Linux Minta. Piszę o tym nie bez powodu: uważam go za najlepszy system z rodziny linuksów i sam od pewnego czasu używam go na co dzień.
Nie mam zamiaru przekonywać nikogo do wyższości Linuksa nad wiadomo czym, już mnie to nie bawi - oba systemy mają wady i zalety i każdy niech sam zdecyduje co dla niego lepsze.

Linux Mint to wersja zbudowana w oparciu o Debiana i Ubuntu. Wyróżnia się jednak tym, że bardzo luźno traktuje sprawy otwartości, przez co już na starcie ma wiele udogodnień których Ubuntu nie posiada i w które należy ten system wyposażyć własnoręcznie (np. sterowniki do kart nVidia czy flash). Ponadto przy większości problemów można korzystać z porad udzielanych w ogromnej społeczności Ubuntu, lub specjalnych forach dedykowanych Mintowi.
Zachęcam do pobrania i przetestowania:
Linux Mint 6 Download

Narzędzia

Aby rozpocząć analizę obrazu wystarczy umiejętność programowania. Praktycznie w każdym języku programowania można mozolnie przetwarzać dane binarne. Jednak nie ma powodu by utrudniać sobie życie. Dostępnych jest wiele specjalistycznych bibliotek przygotowanych do pracy z obrazem, część z nich jest wprost stworzona do zagadnień wizji komputerowej. Biblioteka taka pozwala na ukryte (bez wnikania w sam proces) otwieranie obrazu niezależnie od jego typu oraz proste i bardziej złożone operacje. I tak znajdziemy np.
  1. OpenCV - bardzo rozbudowana biblioteka zapoczątkowana przez Intel
  2. Intel® Integrated Performance Primitives - zaawansowana i zoptymalizowana dla procesorów Intela
  3. Matlab - tego pakietu przedstawiać nie trzeba ;-)
  4. OpenVIDIA - równoległa biblioteka oparta o OpenGL i CUDA
  5. NokiaCV - wizja komputerowa na telefonie komórkowym
  6. VIGRA - biblioteka algorytmów przystosowalnych (podobne do techniki użytej w STL)
  7. VXL - lekka biblioteka C++
Na blogu poruszać będę głównie sprawy związane z OpenCV. Ta specjalistyczna biblioteka do wizji komputerowej początkowo rozwijana była przez Intel, a następnie udostępniona światkowi otwartego oprogramowania. Do jej niewątpliwych zalet należą licencja BSD i implementacja wielu zaawansowanych algorytmów. Ponadto jeżeli posiadamy zainstalowany pakiet Intel IPP, możemy używać go jako bazę i tym samym przyspieszyć (i tak już szybką) pracę programów napisanych z użyciem OpenCV. Językiem biblioteki jest C. Dostępne są także wersje na niej bazujące, np. dla języka Python.