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


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


1 komentarz:
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ę?
Prześlij komentarz