sobota, 11 lipca 2009

Histogram

Histogram jest dość istotny w analizie obrazu. Pozwala nam on określić pewne cechy obrazu i np. ustalić wartości progowania. Histogram w OpenCV wiąże się ze strukturą CvHistogram. Poniżej przykładowa funkcja tworząca histogram na podstawie obrazu (w odcieniach szarości):
CvHistogram* rysuj_histogram1(IplImage * obraz)
{
// tworzenie histogramu z obrazka
int rozm = 256;
// pierwszy parametr mowi o liczbie wymiarow histogramu
// drugi to wskaznik na liczbe przedzialow w poszczegolnych wymiarach
// my chcemy tylko jeden histogram o liczbie przedzialow 256
// trzeci parmater to rodzaj histogramu
CvHistogram * hist = cvCreateHist(1, &rozm, CV_HIST_ARRAY);

for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
{
int wartosc = (int) cvGet2D(obraz, y, x).val[0];
// uzyskiwanie wskaznika na wartosc z danego przedzialu
// dzieki niemu mozemy ta wartosc modyfikowac
(*cvGetHistValue_1D(hist, wartosc))++;
}
wyswietl_histogram(hist);
}
Funkcja tworzącą histogram posiada jeszcze dwa opcjonalne parametry float** ranges = NULL oraz int uniform = 1. Uniform ustawione na 1 mówi o tym, że jako ranges podajemy tablicę par liczb ograniczających kolejne przedziały, a uniform == 0, że jako ranges podamy wartości graniczne pomiędzy kolejnymi przedziałami. W pierwszym przypadku podajemy N par, a w drugim N+1 wartości, gdzie N to liczba przedziałów. Parametry te możemy początkowo pominąć, a później ustawić je za pomocą funkcji
void cvSetHistBinRanges(CvHistogram* histogram,float** ranges, int uniform = 1);
Drugą nową funkcją w przykładzie jest
float* cvGetHistValue_1D(CvHistogram * h, int ind);
która zwraca nam wskaźnik do wybranego przedziału. Dzięki temu możemy zarówno odczytać jak i nadpisać wartość z nim związaną. Funkcja ta ma jeszcze kilka odmian. Z końcówką 2D oraz 3D pozawala odczytać z dwóch i trzech wymiarów (wtedy podajmy odpowiednio int ind1, int ind2 oraz int ind1, int ind2, int ind3 zamiast pojedynczego indeksu), a z końcówką nD z większej liczby wymiarów (zamiast pojedynczego indeksu przekazujemy tablicę indeksów int* ind). Podobną funkcją jest cvQueryHistValue_XD, gdzie w miejsce X wstawiamy 1,2,3 lub n. Parametry są identyczne jak w cvGetHistValue_XD, ale 'Query' zwraca wartość typu double i służy wyłącznie do odczytu. Obliczanie histogramu można sobie uprościć ;-) Przykład drugi
CvHistogram * rysuj_histogram2(IplImage * obraz)
{
int rozm = 256;
CvHistogram * hist = cvCreateHist(1, &rozm, CV_HIST_ARRAY);
// tutaj latwiejsza metoda ;-)
// obraz musi posiadac jeden kanal
cvCalcHist(&obraz, hist);
wyswietl_histogram(hist);
}
Dla formalności, poniżej kod wyświetlający histogramy
void wyswietl_histogram(CvHistogram * hist)
{
int wysokosc = 200;
IplImage * wynik = cvCreateImage(cvSize(255, wysokosc), 8, 1);

// pobieranie wartosci maksymalnej i minimalnej
float min, max = 0;
cvGetMinMaxHistValue(hist, &min, &max);
double skala = (double) wysokosc / max;
int i = 0;
for (i = 0; i < 255; i++)
{
// pobieranie ilosci w danym przedziale, tym razem tylko do odczytu
double wartosc = cvQueryHistValue_1D(hist, i);
cvLine(wynik, cvPoint(i, wysokosc - 1), cvPoint(i, wysokosc - (int) (wartosc * skala) + 1), cvScalarAll(150.0));
}

cvNamedWindow("histogram", CV_WINDOW_AUTOSIZE);

cvShowImage("histogram", wynik);

while (true)
{
if (cvWaitKey(250) == 'k')
break;
}

cvReleaseImage(&wynik);
cvReleaseHist(&hist);
}
Działanie funkcji wyswietl_histogram pokazują przykłady (ponownie wracamy do Parku Vigelanda): Zdjęcie Histogram Zostawiając na boku strukturę CvHistogram (w kolejnych wpisach do niej wrócę ;-) warto przedstawić jedną z ważnych operacji jaką jest wyrównanie histogramu. Pozawala ono poprawić zdjęcia, które mają bardzo zły kontrast, przez co nasz algorytm może niepoprawnie interpretować zawartość obrazu. Rzeźba z poprzedniego zdjęcia jest właśnie przykładem zdjęcia z nierównym histogramem. Na zdjęciu tym np. nie można rozróżnić chmur (chyba, że ktoś posiada naprawdę wprawne oko :) ). Histogram wyrówujemy poleceniem
cvEqualizeHist(const CvArr* src,CvArr* dst);
Jako źródło i wynik możemy podać tą samą strukturę. Na koniec zdjęcie i histogram po wyrównaniu: Zdjęcie Histogram

1 komentarz:

Anonimowy pisze...

Witam,

Siedzę właśnie nad projektem na uczelnię (zastosowanie openCV w widzeniu maszynowym generalnie) i wpadłem na tę stronkę. Wynik pierwszej funkcji liczącej histogram nie pokrywa się z wynikiem funkcji bibliotecznej calcHist ?? Wynikiem tego jest błąd przy rysowaniu histogramu obliczonego wg funkcji pierwszej. Czy mylę się?