« Przetwarzanie obrazów, lekcja »
Rozdział 16. Opcjonalny podzbiór przetwarzania obrazów: tryby transferu pikseli, tablice kolorów, filtry splotowe, histogram, operacja minimum-maksimum, macierz koloru; program przykładowy. (lekcja)
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!
Autor: Janusz Ganczarski
Kurs OpenGL, C++

Przetwarzanie obrazów

[lekcja] Rozdział 16. Opcjonalny podzbiór przetwarzania obrazów: tryby transferu pikseli, tablice kolorów, filtry splotowe, histogram, operacja minimum-maksimum, macierz koloru; program przykładowy.
W wersji 1.2 biblioteki OpenGL dodano opcjonalny zbiór funkcji odpowiedzialnych za przetwarzanie obrazów (ang. imaging subset). Funkcjonalność tego podzbioru OpenGL obejmowały wcześniej rozszerzenia: EXT color table, EXT color subtable, EXT convolution, HP convolution border modes, SGI color matrix, EXT histogram, EXT blend color, EXT blend minmax i EXT blend subtract, przy czym dostępność całego podzbioru określa jedno rozszerzenie ARB imaging. Oczywiście niedostępność całego podzbioru przetwarzania obrazów nie wyklucza zaimplementowania jednego lub kilku z powyższych rozszerzeń. Taka sytuacja występuje w przypadku wielu sterowników do kart graficznych z procesorem firmy ATI, które nie obsługują całego podzbioru obrazowania, ale wspierają rozszerzenie SGI color matrix.
W wersji 1.4 OpenGL część funkcji z podzbioru przetwarzania obrazów przeniesiono do zasadniczej części biblioteki. Zmiany te zostały opisane w odcinku kursu poświęconego mieszaniu kolorów.
Funkcjonalność podzbioru przetwarzania obrazów jest obszerna i obejmuje m.in. operacje macierzowe na składowych kolorów, obsługę tablic kolorów, jedno i dwuwymiarowe filtry splotowe oraz histogramy. Podzbiór przetwarzania obrazów zawiera także dodatkowe tryby transferu pikseli.

Transfer pikseli

Podzbiór przetwarzania obrazów wprowadza nowe tryby transferu pikseli określane przy użyciu funkcji glPixelTransferf i glPixelTransferi. Dodatkowe tryby transferu pikseli związane są z filtrami splotowymi oraz macierzą koloru i określają współczynniki skalowania (mnożenia) i przesunięcia wartości składowych RGBA pikseli po wykonaniu tych operacji.
Dodatkowe tryby transferu pikseli określone są przez poniższe wartości parametru param funkcji z grupy glPixelTransfer:
  • GL_POST_CONVOLUTION_RED_SCALE - współczynnik skalowania składowych R pikseli po obliczeniu filtra splotowego; wartość początkowa 1,
  • GL_POST_CONVOLUTION_GREEN_SCALE - współczynnik skalowania składowych G pikseli po obliczeniu filtra splotowego; wartość początkowa 1,
  • GL_POST_CONVOLUTION_BLUE_SCALE - współczynnik skalowania składowych B pikseli po obliczeniu filtra splotowego; wartość początkowa 1,
  • GL_POST_CONVOLUTION_ALPHA_SCALE - współczynnik skalowania składowych A pikseli po obliczeniu filtra splotowego; wartość początkowa 1,
  • GL_POST_CONVOLUTION_RED_BIAS - wartość przesunięcia składowych R pikseli po obliczeniu filtra splotowego; wartość początkowa 0,
  • GL_POST_CONVOLUTION_GREEN_BIAS - wartość przesunięcia składowych G pikseli po obliczeniu filtra splotowego; wartość początkowa 0,
  • GL_POST_CONVOLUTION_BLUE_BIAS - wartość przesunięcia składowych B pikseli po obliczeniu filtra splotowego; wartość początkowa 0,
  • GL_POST_CONVOLUTION_ALPHA_BIAS - współczynnik przesunięcia składowych A pikseli po obliczeniu filtra splotowego; wartość początkowa 0,
  • GL_POST_COLOR_MATRIX_RED_SCALE - współczynnik skalowania składowych R pikseli po przemnożeniu przez macierz koloru; wartość początkowa 1,
  • GL_POST_COLOR_MATRIX_GREEN_SCALE - współczynnik skalowania składowych G pikseli po przemnożeniu przez macierz koloru; wartość początkowa 1,
  • GL_POST_COLOR_MATRIX_BLUE_SCALE - współczynnik skalowania składowych B pikseli po przemnożeniu przez macierz koloru; wartość początkowa 1,
  • GL_POST_COLOR_MATRIX_ALPHA_SCALE - współczynnik skalowania składowych A pikseli po przemnożeniu przez macierz koloru; wartość początkowa 1,
  • GL_POST_COLOR_MATRIX_RED_BIAS - wartość przesunięcia składowych R pikseli po przemnożeniu przez macierz koloru; wartość początkowa 0,
  • GL_POST_COLOR_MATRIX_GREEN_BIAS - wartość przesunięcia składowych G pikseli po przemnożeniu przez macierz koloru; wartość początkowa 0,
  • GL_POST_COLOR_MATRIX_BLUE_BIAS - wartość przesunięcia składowych B pikseli po przemnożeniu przez macierz koloru; wartość początkowa 0,
  • GL_POST_COLOR_MATRIX_ALPHA_BIAS - wartość przesunięcia składowych A pikseli po przemnożeniu przez macierz koloru; wartość początkowa 0.
Wartości początkowe współczynników skalowania i wartości przesunięcia składowych pikseli są tak dobrane, aby były neutralne dla wartości składowych pikseli. Nie ma ograniczeń co do wartości przyjmowanych przez każdy z powyższych współczynników.
Użycie filtrów splotowych lub macierzy koloru i związanych z nimi trybów transferu pikseli jest niezależne od opisanych wcześniej operacji na składowych pikseli z użyciem mapy (tablicy) przekształceń, określanych przy użyciu funkcji z grupy glPixelMap.

Tablice kolorów

Tablice kolorów służą do zmiany wartości składowych pikseli i są w zasadzie działania podobne do tablic przekształceń map pikselowych. Tablice kolorów są domyślnie wyłączone. Ich aktywacja wymaga wywołania funkcji glEnable z parametrem GL_COLOR_TABLE.

Tworzenie tablicy kolorów

Tablicę kolorów tworzy wywołanie funkcji:
C/C++
void glColorTable( GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid * table )
Parametr target określa miejsce w potoku przekształceń fragmentów obrazu (czyli elementów bufora obrazu wraz ze wszystkimi ich atrybutami) i przyjmuje jedną z następujących wartości:
  • GL_COLOR_TABLE - tablica kolorów zastosowana na początku potoku przekształceń fragmentów,
  • GL_POST_CONVOLUTION_COLOR_TABLE - tablica kolorów zastosowana po operacjach filtra splotowego,
  • GL_POST_COLOR_MATRIX_COLOR_TABLE - tablica kolorów zastosowana po operacjach macierzy koloru,
  • GL_PROXY_COLOR_TABLE - zastępcza (proxy) tablica kolorów zastosowana na początku potoku przekształceń fragmentów,
  • GL_PROXY_POST_CONVOLUTION_COLOR_TABLE - zastępcza (proxy) tablica kolorów zastosowana po operacjach filtra splotowego,
  • GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE - zastępcza (proxy) tablica kolorów zastosowana po operacjach macierzy koloru.
Wybór tablic zastępczych (proxy) faktycznie nie tworzy tablicy kolorów, ale umożliwia sprawdzenie czy do utworzenia tablicy jest wystarczająca ilość zasobów systemowych. Jest to mechanizm analogiczny do tekstur zastępczych.
Parametr internalformat przyjmuje takie same wartości jak wewnętrzne formaty tektury za wyjątkiem formatu podstawowego GL_DEPTH_COMPONENT oraz związanych z nim formatów wewnętrznych GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24 i GL_DEPTH_COMPONENT32.
Parametry format i type mają takie same znaczenie i przyjmują takie same wartości jak analogiczne parametry funkcji glDrawPixels, przy czym parametr format nie może przyjąć wartości GL_COLOR_INDEX, GL_STENCIL_INDEX i GL_DEPTH_COMPONENT. Ponadto spośród wartości parametru type wyłączona jest stała GL_BITMAP.
Właściwe dane tablicy kolorów wskazuje parametr table, a jej wielkość określana jest w parametrze width i musi być potęgą liczby 2. Maksymalna obsługiwana wielkość tablicy kolorów zależy od implementacji biblioteki
OpenGL, ale nie może być mniejsza niż 32.
Dane do tablicy kolorów można także pobrać z wybranego fragmentu bufora kolorów. Służy do tego funkcja:
C/C++
void glCopyColorTable( GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width )
której parametr target, określający rodzaj tablicy kolorów, ograniczono do trzech wartości (GL_COLOR_TABLE, GL_POST_CONVOLUTION_COLOR_TABLE i GL_POST_COLOR_MATRIX_COLOR_TABLE), a parametry internalformat i width przyjmują takie same wartości jak analogiczne parametry funkcji glColorTable. Dwa ostatnie parametry x i y stanowią współrzędne położenia pierwszego fragmentu obrazu, który zostanie pobrany jako tablica kolorów. Kopiowanie tablicy kolorów odbywa się na takich samych zasadach jak kopiowanie map pikselowych (funkcja glCopyPixels), przy czym oczywiście wysokość kopiowanego fragmentu obrazu wynosi 1.

Skalowanie i przesunięcie składowych kolorów

Podobnie jak w przypadku transferu pikseli podzbiór przetwarzania obrazów biblioteki OpenGL umożliwia przy przekształcaniu pikseli z użyciem tablicy kolorów określenie współczynników skalowania (mnożenia) i przesunięcia wartości składowych RGBA pikseli. Wartości wynikowe składowych kolorów pikseli są obcinane do przedziału [0, 1]. Ustawienie tych wartości umożliwiają funkcje z grupy glColorTableParameter:
C/C++
void glColorTableParameterfv( GLenum target, GLenum pname, const GLfloat * params )
void glColorTableParameteriv( GLenum target, GLenum pname, const GLint * params )
Parametr target określa rodzaj tablicy kolorów i może przyjąć jedną z trzech wartości: GL_COLOR_TABLE, GL_POST_CONVOLUTION_COLOR_TABLE i GL_POST_COLOR_MATRIX_COLOR_TABLE.
Rodzaj modyfikowanego parametru określa pname, który przyjmuje jedną z dwóch wartości:
  • GL_COLOR_TABLE_SCALE - współczynniki skalowania składowych pikseli tablicy kolorów; wartości początkowe (1, 1, 1, 1),
  • GL_COLOR_TABLE_BIAS - wartości przesunięcia składowych pikseli tablicy kolorów; wartości początkowe (0, 0, 0, 0).
Współczynniki skalowania i wartości przesunięcia składowych pikseli tablicy kolorów umieszczane są w tablicy params, która musi zawierać ilość elementów odpowiadającą ilości składowych elementów tablicy kolorów. Maksymalna wielkość obu tablic wynosi oczywiście cztery, a wartości domyślne oby zestawów współczynników są neutralne dla wartości składowych pikseli.

Zmiana danych tablicy kolorów

Zmianę danych już istniejącej tablicy kolorów umożliwiają dwie funkcje. Funkcja glColorSubTable:
C/C++
void glColorSubTable( GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid * data )
pobiera dane zmienianych elementów tablicy kolorów z bufora przygotowanego przez program (parametr data). Druga funkcja glCopyColorSubTable:
C/C++
void glCopyColorSubTable( GLenum target, GLsizei start, GLint x, GLint y, GLsizei width )
pobiera dane zmienianych elementów tablicy kolorów z bufora kolorów.
Zmiana elementów istniejącej tablicy kolorów nie wpływa na format wewnętrzny danych tablicy, stąd powyższe funkcje nie posiadają parametru internalformat. Parametr target określa rodzaj tablicy kolorów i przyjmuje jedną z trzech wartości: GL_COLOR_TABLE, GL_POST_CONVOLUTION_COLOR_TABLE i GL_POST_COLOR_MATRIX_COLOR_TABLE. Parametry format i type funkcji glColorSubTable przyjmują takie same wartości jak analogiczne parametry funkcji glColorTable. Parametry x i y funkcji glCopyColorSubTable określają położenie pierwszego kopiowanego fragmentu bufora kolorów. Zasady kopiowania danych z bufora kolorów są takie same jak w przypadku funkcji glCopyColorTable.
Pierwszy modyfikowany element tablicy kolorów określa wspólny dla obu funkcji parametr start przy czym elementy tablicy numerowane są od zera. Ilość zmienianych elementów tablicy wyznacza w funkcji glColorSubTable parametr count, a w funkcji glCopyColorSubTable parametr width.

Pobieranie tablicy kolorów

Pobranie wybranej tablicy kolorów umożliwia funkcja glGetColorTable:
C/C++
void glGetColorTable( GLenum target, GLenum format, GLenum type, GLvoid * table )
której parametry target, format i type mają takie same znaczenie jak analogiczne parametry funkcji glColorTable. Wyjątkiem są zastępcze tablice kolorów (proxy), z którymi nie są związane dane tablicy kolorów. Oczywiście program musi zapewnić odpowiednią ilość miejsca na dane pobieranej tablicy (parametr table).

Pobieranie właściwości tablic kolorów

Odczyt właściwości tablic kolorów umożliwiają funkcje z grupy glGetColorTableParameter:
C/C++
void glGetColorTableParameterfv( GLenum target, GLenum pname, GLfloat * params )
void glGetColorTableParameteriv( GLenum target, GLenum pname, GLint * params )
Parametr target określa rodzaj tablicy kolorów, której właściwości pobieramy. Dopuszczalne są wszystkie rodzaje tablic kolorów, włączając w to tablice zastępcze (proxy). Parametr pname wskazuje pobieraną właściwość wybranej tablicy kolorów i przyjmuje jedną z poniższych wartości:
  • GL_COLOR_TABLE_BIAS - wartości przesunięcia składowych pikseli tablicy kolorów; wartości początkowe (0, 0, 0, 0),
  • GL_COLOR_TABLE_SCALE - współczynniki skalowania składowych pikseli tablicy kolorów; wartości początkowa (1, 1, 1, 1),
  • GL_COLOR_TABLE_FORMAT - format wewnętrzny tablicy kolorów; wartość domyślna GL_RGBA,
  • GL_COLOR_TABLE_WIDTH - ilość elementów zawartych w tablicy kolorów,
  • GL_COLOR_TABLE_RED_SIZE - ilość bitów składających się na składową R elementów tablicy kolorów,
  • GL_COLOR_TABLE_GREEN_SIZE - ilość bitów składających się na składową G elementów tablicy kolorów,
  • GL_COLOR_TABLE_BLUE_SIZE - ilość bitów składających się na składową B elementów tablicy kolorów,
  • GL_COLOR_TABLE_ALPHA_SIZE - ilość bitów składających się na składową A elementów tablicy kolorów,
  • GL_COLOR_TABLE_LUMINANCE_SIZE - ilość bitów składających się na składową L elementów tablicy kolorów,
  • GL_COLOR_TABLE_INTENSITY_SIZE - ilość bitów składających się na składową I elementów tablicy kolorów.

Filtry splotowe

Filtracja to jedno z najważniejszych narzędzi w cyfrowym przetwarzaniu obrazów. Zastosowanie odpowiednich filtrów pozwala na uzyskanie informacji o obrazie, które nie są osiągalne w inny sposób.
Dostępne w bibliotece OpenGL filtry splotowe należą do najczęściej używanych filtrów. Splot oblicza nową wartość składowej piksela na podstawie wartości składowych sąsiadujących pikseli. Każda ze składowych sąsiednich pikseli wnosi swój udział w obliczeniu składowej nowego piksela w postaci procentu swojej wartości - wagi. Wagi poszczególnych pikseli (składowych) zapisywane są w postaci macierzy - maski splotu. Elementy maski splotu nazywane są współczynnikami splotu.
Działanie filtra splotowego przeanalizujemy na przykładzie filtra z maską o wymiarach 3 × 3. Są to typowe maski splotu filtrów dwuwymiarowych, w przypadku których na wartość składowej piksela wpływa wartość składowych 8 sąsiednich pikseli. Maskę filtru najwygodniej opisać w postaci macierzy:
Obliczenie wartości składowej piksela ai,j obrazu rozpoczynamy od policzenia sumy iloczynów współczynników splotu przez wartości składowych pikseli sąsiadujących z pikselem ai,j :
Następnie należy dokonać normalizacji wartości składowej piksela co sprowadza się do podzielenia jej przez współczynnik normalizujący, czyli sumę wszystkich współczynników splotu:
Normalizacji nie przeprowadza się, jeżeli suma wartości współczynników splotu wynosi 0.
Ważnym zagadnieniem związanym z filtrami splotu są warunki brzegowe. Czytelnik z pewnością zauważy, że próba zastosowania przedstawionych wyżej wzorów do pikseli znajdujących się na brzegu obrazu prowadzi do nieokreślonej sytuacji obliczeń na pikselach poza obrazem. Sytuacja ta stanie się jeszcze bardziej widoczna, gdy maska filtra będzie miała większe rozmiary.
Najczęstszym rozwiązaniem tego problemu jest pominięcie procesu filtracji, dla tych pikseli obrazu, na których proces ten nie jest wykonalny. Radykalnym (i w praktyce raczej niestosowanym) sposobem rozwiązania opisanego problemu jest zmniejszenie wymiarów obrazu po filtracji. Za kompromisowe można uznać często stosowane w praktyce rozwiązanie polegające na dodaniu do filtrowanego obrazu fałszywego brzegu, złożonego najczęściej ze zduplikowanych pikseli brzegu obrazu.

Definiowanie filtrów

Poza przedstawionymi na wstępie filtrami dwuwymiarowymi bibliotek OpenGL wspiera także filtry jednowymiarowe, które operują jedynie na wierszach obrazu. Możliwe jest także zdefiniowanie maski splotu filtra dwuwymiarowego w postaci iloczynu dwóch masek filtrów jednowymiarowych:
Utworzenie filtra jednowymiarowego umożliwia funkcja:
C/C++
void glConvolutionFilter1D( GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid * image )
Parametr target określa rodzaj tworzonego filtra jednowymiarowego ale może przyjąć jedynie wartość GL_CONVOLUTION_1D oznaczającą filtr splotowy.
Parametr internalformat przyjmuje takie same wartości jak wewnętrzne formaty tektury za wyjątkiem formatu podstawowego GL_DEPTH_COMPONENT oraz związanych z nim formatów wewnętrznych GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24 i GL_DEPTH_COMPONENT32.
Parametry format i type mają takie same znaczenie i przyjmują takie same wartości jak analogiczne parametry funkcji glDrawPixels, przy czym parametr format nie może przyjąć wartości GL_COLOR_INDEX, GL_STENCIL_INDEX i GL_DEPTH_COMPONENT. Ponadto spośród wartości parametru type wyłączona jest stała GL_BITMAP.
Ilość elementów maski filtra określa parametr width. Maksymalny rozmiar maski filtra jest zależny od implementacji (zmienna stanu GL_MAX_CONVOLUTION_WIDTH) ale nie może być on mniejszy niż 3.
Ostatni parametr image zawiera oczywiście wskaźnik do tablicy z maską filtra.
Utworzenie filtra dwuwymiarowego wymaga wywołania funkcji:
C/C++
void glConvolutionFilter2D( GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid * image )
W stosunku do poprzedniej funkcji parametr target może przyjąć jedynie wartość GL_CONVOLUTION_1D (dwuwymiarowy filtr splotowy), a dodatkowy parametr height określa oczywiście wysokość maski filtra. Maksymalna wysokość maski filtra jest zależna od implementacji (zmienna stanu GL_MAX_CONVOLUTION_HEIGHT) i podobnie jak jej szerokość nie może być mniejsza od 3.
Utworzenie filtra z dwóch masek jednowymiarowych umożliwia funkcja:
C/C++
void glSeparableFilter2D( GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid * row, const GLvoid * column )
Parametr target określa rodzaj filtra i może przyjąć jedynie wartość GL_SEPARABLE_2D. Pionowa i pozioma maska filtra przekazywane są w parametrach row i column a ich wielkości określają parametry width i height. Pozostałe parametry mają takie same znaczenie i podlegają takim samym ograniczeniom jak analogiczne parametry funkcji glConvolutionFilter1D i glConvolutionFilter2D.
Filtry splotowe są domyślnie wyłączone. Włączenie filtracji wymaga wywołania funkcji glEnable z jednym z parametrów: GL_CONVOLUTION_1D,
GL_CONVOLUTION_2D lub GL_SEPARABLE_2D.
Alternatywną metodą tworzenia filtrów jedno i dwuwymiarowych jest
pobranie maski filtra z bufora kolorów. Realizują to funkcje:
C/C++
void glCopyConvolutionFilter1D( GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width )
void glCopyConvolutionFilter2D( GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height )
których parametry target przyjmują wartości odpowiednio GL_CONVOLUTION_1D lub GL_CONVOLUTION_2D, a położenie i rozmiary kopiowanego fragmentu bufora obrazu określają parametry x, y, width i height. Kopiowanie maski filtra odbywa się na takich samych zasadach jak kopiowanie map pikselowych przy użyciu funkcji glCopyPixels). Tworzone w ten sposób filtry mają maski w formacie GL_RGBA (parametr format funkcji z grupy glConvolutionFilter).

Właściwości filtrów

Regulację właściwości filtrów splotowych umożliwiają funkcje z grupy glConvolutionParameter:
C/C++
void glConvolutionParameterf( GLenum target, GLenum pname, const GLfloat * params )
void glConvolutionParameterfv( GLenum target, GLenum pname, const GLfloat * params )
void glConvolutionParameteri( GLenum target, GLenum pname, GLint params )
void glConvolutionParameteriv( GLenum target, GLenum pname, const GLint * params )
Parametr target określa rodzaj filtra, którego właściwości są modyfikowane i przyjmuje jedną z trzech poznanych wartości: GL_CONVOLUTION_1D, GL_CONVOLUTION_2D lub GL_SEPARABLE_2D.
Parametr pname określa rodzaj modyfikowanej właściwości filtra splotowego i przyjmuje jedną z poniższych wartości:
  • GL_CONVOLUTION_BORDER_MODE - sposób obsługi brzegu obrazu przez filtr; możliwe działania to redukcja wymiarów obrazu o dwa piksele (stała GL_REDUCE), przyjęcie fikcyjnego koloru brzegu obrazu (stała GL_CONSTANT_BORDER), lub utworzenie fikcyjnego brzegu z pikseli brzegowych obrazu (stała GL_REPLICATE_BORDER); wartość domyślna to GL_REDUCE,
  • GL_CONVOLUTION_BORDER_COLOR - składowe RGBA koloru brzegu obrazu używane, gdy metodą obsługi brzegu obrazu jest GL_CONSTANT_BORDER;  domyślnie składowe koloru brzegu mają wartość (0, 0, 0, 0),
  • GL_CONVOLUTION_FILTER_SCALE - współczynniki skalowania (mnożenia) wartości maski filtra; wartości domyślne (1, 1, 1, 1),
  • GL_CONVOLUTION_FILTER_BIAS - wartości przesunięcia współczynników maski filtra; wartości domyślne (0, 0, 0, 0).
Domyślne wartości współczynników skalowania i przesunięcia maski filtra są dobrane w sposób neutralny dla działania filtra.

Pobieranie maski filtra

Pobranie maski filtra jedno lub dwuwymiarowego umożliwia funkcja:
C/C++
void glGetConvolutionFilter( GLenum target, GLenum format, GLenum type, GLvoid * image )
Parametr target oznacza rodzaj pobieranego filtra (wartość GL_CONVOLUTION_1D lub GL_CONVOLUTION_2D), a parametry format i type mają takie same znaczenia jak analogiczne parametry funkcji z grupy glConvolutionFilter. Maska filtra umieszczona jest w tablicy image, przy czym program jest odpowiedzialny za odpowiednią jej wielkość.
Pobieranie maski filtra dwuwymiarowego określonego przez dwie maski jednowymiarowe umożliwia funkcja:
C/C++
void glGetSeparableFilter( GLenum target, GLenum format, GLenum type, GLvoid * row, GLvoid * column, GLvoid * span )
której parametry target, format, type, row i column odpowiadają analogicznym parametrom funkcji glSeparableFilter2D. Ostatni parametr span nie jest używany.

Pobieranie parametrów filtrów

Pobieranie parametrów filtrów splotowych umożliwiają funkcje z grupy
glGetConvolutionParameter:
C/C++
void glGetConvolutionParameterfv( GLenum target, GLenum pname, GLfloat * params )
void glGetConvolutionParameteriv( GLenum target, GLenum pname, GLint * params )
których parametry odpowiadają analogicznym parametrom funkcji z grupy glConvolutionParameter.
Dodatkowo parametr pname może przyjąć następujące wartości:
  • GL_CONVOLUTION_FORMAT - format danych maski filtra,
  • GL_CONVOLUTION_WIDTH - szerokość maski filtra,
  • GL_CONVOLUTION_HEIGHT - wysokość maski filtra,
  • GL_MAX_CONVOLUTION_WIDTH - maksymalna szerokość maski filtra,
  • GL_MAX_CONVOLUTION_HEIGHT - maksymalna wysokość maski filtra.

Histogram

W statystyce histogramem nazywamy funkcję gęstości prawdopodobieństwa, która w przypadku obrazów cyfrowych histogram jest funkcją dyskretną. Przykładowo histogram obrazu w odcieniach szarości wyznaczany jest jako suma wszystkich pikseli o danej wartości odcienia szarości znormalizowaną (podzieloną) przez liczbę wszystkich pikseli.

Obliczanie histogramu

W bibliotece OpenGL do obliczania histogramu wykorzystywana jest funkcja:
C/C++
void glHistogram( GLenum target, GLsizei width, GLenum internalformat, GLboolean sink )
Parametr target oznacza rodzaj docelowego histogramu. Dopuszczalne są dwie wartości: GL_HISTOGRAM - histogram właściwy i GL_PROXY_HISTOGRAM - histogram zastępczy (proxy). Histogramy zastępcze przeznaczone są do testu określającego czy tablica przechowująca dane histogramu pomieści się w zasobach systemowych.
Parametr width oznacza ilość elementów w tablicy histogramu i musi być potęgą liczby dwa. Maksymalna wielkość tablicy histogramu jest zależna od implementacji, ale nie może być mniejsza niż 32.
Parametr internalformat określa wewnętrzny format tablicy z histogramem i może przyjąć takie wartości jak wewnętrzne formaty tektury za wyjątkiem formatów podstawowych GL_INTENSITY i GL_DEPTH_COMPONENT oraz związanych z nimi formatów wewnętrznych: GL_INTENSITY4, GL_INTENSITY8, GL_INTENSITY12, GL_INTENSITY16, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24 i GL_DEPTH_COMPONENT32.
Ostatni parametr sink określa czy informacje o grupach pikseli mają być dalej przekazywane do operacji minimum-maksimum (wartość GL_FALSE) czy też wykorzystane tylko do obliczenia histogramu (wartość GL_TRUE).
Obliczanie histogramu jest domyślnie wyłączone. Aktywacja tych obliczeń wymaga wywołania funkcji glEnable z parametrem GL_HISTOGRAM.

Pobieranie danych histogramu

Po obliczeniu histogramu przez bibliotekę OpenGL trzeba pobrać jego dane. Wymaga to wywołania funkcji:
C/C++
void glGetHistogram( GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid * values )
Parametr target określa rodzaj histogramu i może przyjąć wyłącznie wartość GL_HISTOGRAM. Drugi parametr reset wskazuje, czy dane zgromadzone w tablicy histogramu mają być wyzerowane po odczycie (GL_TRUE), czy też nie (GL_FALSE).
Parametry format i type przyjmują takie same wartości jak odpowiadające im parametry funkcji z grupy glGetTexImage. Jedynym wyjątkiem jest brak formatu GL_DEPTH_COMPONENT. Program musi oczywiście zapewnić w tablicy values odpowiednią ilość miejsca na dane histogramu.

Pobieranie właściwości histogramu

Pobieranie wybranych właściwości histogramu umożliwiają funkcje z grupy glGetHistogramParameter:
C/C++
void glGetHistogramParameterfv( GLenum target, GLenum pname, GLfloat * params )
void glGetHistogramParameteriv( GLenum target, GLenum pname, GLint * params )
Parametr target określa rodzaj histogramu i może przyjąć jedną z dwóch wartości: GL_HISTOGRAM - histogram właściwy i GL_PROXY_HISTOGRAM - histogram zastępczy (proxy).
Parametr pname określa rodzaj pobieranej właściwości histogramu i może przyjąć jedną z wartości:
  • GL_HISTOGRAM_WIDTH - ilość elementów w tablicy histogramu,
  • GL_HISTOGRAM_FORMAT - wewnętrzny format elementów tablicy z histogramem,
  • GL_HISTOGRAM_RED_SIZE - ilość bitów składowej R elementów tablicy histogramu,
  • GL_HISTOGRAM_GREEN_SIZE - ilość bitów składowej G elementów tablicy histogramu,
  • GL_HISTOGRAM_BLUE_SIZE - ilość bitów składowej B elementów tablicy histogramu,
  • GL_HISTOGRAM_ALPHA_SIZE - ilość bitów składowej A elementów tablicy histogramu,
  • GL_HISTOGRAM_LUMINANCE_SIZE - ilość bitów składowej L elementów tablicy histogramu,
  • GL_HISTOGRAM_SINK - znacznik przekazywania informacji o grupach pikseli.

Zerowanie histogramu

Zerowanie danych zawartych w tablicy histogramu realizuje funkcja:
C/C++
void glResetHistogram( GLenum target )
której parametr target określający rodzaj histogramu może przyjąć wyłącznie wartość GL_HISTOGRAM.

Operacja minimum-maksimum

Operacja minimum-maksimum oblicza minimalne i maksymalne wartości składowych pikseli. Wykonanie tej operacji wymaga wywołania funkcji:
C/C++
void glMinmax( GLenum target,
GLenum internalformat, GLboolean sink )
Parametr target określa rodzaj wykonywanej operacji i może przyjąć jedynie wartość GL_MINMAX. Parametr internalformat określa wewnętrzny format danych tablicy minimum-maksimum. Dopuszczalne są wszystkie wewnętrzne formaty tektury za wyjątkiem formatów podstawowych GL_INTENSITY i GL_DEPTH_COMPONENT oraz związanych z nimi formatów wewnętrznych: GL_INTENSITY4, GL_INTENSITY8, GL_INTENSITY12, GL_INTENSITY16, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24 i GL_DEPTH_COMPONENT32.
Ostatni parametr sink określa czy informacje o grupach pikseli mają być dalej przekazywane w potoku obrazowania (wartość GL_FALSE) czy też wykorzystane tylko do obliczenia tablicy minimum-maksimum (wartość GL_TRUE).
Operacja minimum-maksimum jest domyślnie wyłączona. Jej aktywacja wymaga wywołania funkcji glEnable z parametrem GL_MINMAX.

Pobranie danych tablicy minimum-maksimum

Pobranie danych tablicy minimum-maksimum umożliwia funkcja:
C/C++
void glGetMinmax( GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid * values )
Parametr target określa rodzaj operacji i może przyjąć wyłącznie wartość GL_MINMAX. Drugi parametr reset wskazuje, czy dane zgromadzone w tablicy minimum-maksimum mają być wyzerowane po odczycie (GL_TRUE), czy też nie (GL_FALSE).
Parametry format i type przyjmują takie same wartości jak odpowiadające im parametry funkcji z grupy glGetTexImage. Jedynym wyjątkiem jest brak formatu GL_DEPTH_COMPONENT.
Tablica zwracana w parametrze values ma dwa elementy. Pierwszy zawiera minimalne wartości składowych pikseli, drugi maksymalne wartości składowych pikseli.

Właściwości tablicy minimum-maksimum

Pobranie właściwości tablicy minimum-maksimum umożliwiają funkcje z grupy glGetMinmaxParameter:
C/C++
void glGetMinmaxParameterfv( GLenum target, GLenum pname, GLfloat * params )
void glGetMinmaxParameteriv( GLenum target, GLenum pname, GLint * params )
Parametr target określający rodzaj tablicy może przyjąć jedynie wartość GL_MINMAX. Drugi parametr pname wskazuje rodzaj pobieranej właściwości tablicy i przyjmuje jedną z dwóch wartości:
  • GL_MINMAX_FORMAT - format danych tablicy minimum-maksimum,
  • GL_MINMAX_SINK - znacznik przekazywania informacji o grupach pikseli.

Zerowanie tablicy minimum-maksimum

Wyzerowanie danych tablicy minimum-maksimum wymaga wywołania funkcji:
C/C++
void glResetMinmax( GLenum target )
której parametr target może przyjąć wyłącznie wartość GL_MINMAX.

Macierz koloru

Macierz koloru pozwala na wykonywanie operacji na składowych RGBA kolorów, które są wówczas traktowane jak wektor. Wybór macierzy koloru dokonuje się przy użyciu dobrze znanej funkcji glMatrixMode z parametrem GL_COLOR. Do dyspozycji programisty są oczywiście wszystkie funkcje operujące na macierzach. Minimalna głębokość stosu macierzy koloru wynosi 2.

Program przykładowy

W przykładowym programie (plik przetwarzanie_obrazow.cpp) prezentowane są wybrane możliwości podzbioru przetwarzania obrazów. Efekt zmniejszenie i zwiększenia jasności obrazu uzyskiwany jest za pomocą macierzy koloru, a negatyw obrazu tworzony jest przy użyciu tablicy kolorów. Ponadto program umożliwia przetestowanie kilkudziesięciu różnego rodzaju filtrów splotowych, których maski znajdują się w pliku filters.h. Efekty działania kilku wybranych filtrów przedstawiono na rysunkach 3 - 8.
Ostatnim elementem podzbioru przetwarzania obrazów wykorzystanym w przykładowym programie jest tworzenie histogramu obrazu. Do prezentacji histogramu program tworzy dodatkowe okno którego wielkość zależy od rodzaju obrazu. Histogram obrazu w odcieniach szarości jest prezentowany w formie pojedynczego wykresu, a histogram obrazu RGB/RGBA przedstawiany jest w formie trzech wykresów odrębnie dla każdej składowej RGB. Histogramy obrazów Lena przedstawiono na rysunkach 1 i 2.
Rysunek 1. Program Przetwarzanie obrazów - histogram obrazu Lena w odcieniach szarości
Rysunek 1. Program Przetwarzanie obrazów - histogram obrazu Lena w odcieniach szarości
W programie wykorzystano kilka dotąd nieopisywanych funkcji biblioteki GLUT. Do przełączania pomiędzy oknami aplikacji służy funkcja:
C/C++
void glutSetWindow( int win )
której parametr win zawiera unikatowy identyfikator (uchwyt) okna zwracany przy jego tworzeniu przez funkcję glutCreateWindow.
Przy usuwaniu okna z wykresem histogramu stosowana jest funkcja:
C/C++
void glutDestroyWindow( int win )
Rysunek 2. Program Przetwarzanie obrazów - histogram obrazu Lena
Rysunek 2. Program Przetwarzanie obrazów - histogram obrazu Lena
korzystająca z opisanego wcześniej uchwytu okna. Przed usunięciem okna z histogramem usuwane jest jego menu podręczne. Wymaga to wywołania funkcji:
C/C++
void glutDestroyMenu( int menu )
której parametr menu to unikatowy identyfikator (uchwyt) manu zwracany przez funkcję glutCreateMenu.
Rysunek 3. Program Przetwarzanie obrazów - filtr usuwający średną
Rysunek 3. Program Przetwarzanie obrazów - filtr usuwający średną
Rysunek 4. Program Przetwarzanie obrazów - filtr gradientowy wschód
Rysunek 4. Program Przetwarzanie obrazów - filtr gradientowy wschód

Plik filters.h

Rysunek 5. Program Przetwarzanie obrazów - filtr uwypuklający wschód
Rysunek 5. Program Przetwarzanie obrazów - filtr uwypuklający wschód
Rysunek 6. Program Przetwarzanie obrazów - filtr Laplace’a LAP1
Rysunek 6. Program Przetwarzanie obrazów - filtr Laplace’a LAP1
Rysunek 7. Program Przetwarzanie obrazów - filtr poziomy Sobela
Rysunek 7. Program Przetwarzanie obrazów - filtr poziomy Sobela
Rysunek 8. Program Przetwarzanie obrazów - filtr poziomy Prewitta
Rysunek 8. Program Przetwarzanie obrazów - filtr poziomy Prewitta
C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#ifndef __FILTERS__H__
#define __FILTERS__H__

#include <GL/gl.h>

// filtr uśredniający

GLfloat average[ 9 ] =
{
    0.11111111, 0.11111111, 0.111111111,
    0.11111111, 0.11111111, 0.111111111,
    0.11111111, 0.11111111, 0.111111111
};

// filtr LP1

GLfloat lp1[ 9 ] =
{
    0.1, 0.1, 0.1,
    0.1, 0.2, 0.1,
    0.1, 0.1, 0.1
};

// filtr LP2

GLfloat lp2[ 9 ] =
{
    0.08333333, 0.08333333, 0.08333333,
    0.08333333, 0.33333333, 0.08333333,
    0.08333333, 0.08333333, 0.08333333
};

// filtr LP3

GLfloat lp3[ 9 ] =
{
    0.05, 0.05, 0.05,
    0.05, 0.60, 0.05,
    0.05, 0.05, 0.05
};

// filtr Gaussa

GLfloat gauss[ 9 ] =
{
    0.0625, 0.1250, 0.0625,
    0.1250, 0.2500, 0.1250,
    0.0625, 0.1250, 0.0625
};

// filtr usuwający średnią

GLfloat mean_removal[ 9 ] =
{
    - 1, - 1, - 1,
    - 1, 9, - 1,
    - 1, - 1, - 1
};

// filtr HP1

GLfloat hp1[ 9 ] =
{
    0, - 1, 0,
    - 1, 5, - 1,
    0, - 1, 0
};

// filtr HP2

GLfloat hp2[ 9 ] =
{
    1, - 2, 1,
    - 2, 5, - 2,
    1, - 2, 1
};

// filtr HP3

GLfloat hp3[ 9 ] =
{
    0.0000, - 0.0625, 0.0000,
    - 0.0625, 1.2500, - 0.0625,
    0.0000, - 0.0625, 0.0000
};

// filtr poziomy

GLfloat horizontal[ 9 ] =
{
    0, - 1, 0,
    0, 1, 0,
    0, 0, 0
};

// filtr pionowy

GLfloat vertical[ 9 ] =
{
    0, 0, 0,
    - 1, 1, 0,
    0, 0, 0
};

// filtr poziomy/pionowy

GLfloat horizontal_vertical[ 9 ] =
{
    - 1, 0, 0,
    0, 1, 0,
    0, 0, 0
};

// filtr gradientowy wschód

GLfloat gradient_east[ 9 ] =
{
    - 1, 1, 1,
    - 1, - 2, 1,
    - 1, 1, 1
};

// filtr gradientowy południowy wschód

GLfloat gradient_south_east[ 9 ] =
{
    - 1, - 1, 1,
    - 1, - 2, 1,
    1, 1, 1
};

// filtr gradientowy południe

GLfloat gradient_south[ 9 ] =
{
    - 1, - 1, - 1,
    1, - 2, 1,
    1, 1, 1
};

// filtr gradientowy południowy zachód

GLfloat gradient_south_west[ 9 ] =
{
    1, - 1, - 1,
    1, - 2, - 1,
    1, 1, 1
};

// filtr gradientowy zachód

GLfloat gradient_west[ 9 ] =
{
    1, 1, - 1,
    1, - 2, - 1,
    1, 1, - 1
};

// filtr gradientowy północny zachód

GLfloat gradient_north_west[ 9 ] =
{
    1, 1, 1,
    1, - 2, - 1,
    1, - 1, - 1
};

// filtr gradientowy północ

GLfloat gradient_north[ 9 ] =
{
    1, 1, 1,
    1, - 2, 1,
    - 1, - 1, - 1
};

// filtr gradientowy północny wschód

GLfloat gradient_north_east[ 9 ] =
{
    1, 1, 1,
    - 1, - 2, 1,
    - 1, - 1, 1
};

// filtr uwypuklający wschód

GLfloat emboss_east[ 9 ] =
{
    - 1, 0, 1,
    - 1, 1, 1,
    - 1, 0, 1
};

// filtr uwypuklający południowy wschód

GLfloat emboss_south_east[ 9 ] =
{
    - 1, - 1, 0,
    - 1, 1, 1,
    0, 1, 1
};

// filtr uwypuklający południe

GLfloat emboss_south[ 9 ] =
{
    - 1, - 1, - 1,
    0, 1, 0,
    1, 1, 1
};

// filtr uwypuklający południowy zachód

GLfloat emboss_south_west[ 9 ] =
{
    0, - 1, - 1,
    1, 1, - 1,
    1, 1, 0
};

// filtr uwypuklający zachód

GLfloat emboss_west[ 9 ] =
{
    1, 0, - 1,
    1, 1, - 1,
    1, 0, - 1
};

// filtr uwypuklający północny zachód

GLfloat emboss_north_west[ 9 ] =
{
    1, 1, 0,
    1, 1, - 1,
    0, - 1, - 1
};

// filtr uwypuklający północ

GLfloat emboss_north[ 9 ] =
{
    1, 1, 1,
    0, 1, 0,
    - 1, - 1, - 1
};

// filtr uwypuklający północny wschód

GLfloat emboss_north_east[ 9 ] =
{
    0, 1, 1,
    - 1, 1, 1,
    - 1, - 1, 0
};

// filtr Laplace'a LAPL1

GLfloat laplacian_lapl1[ 9 ] =
{
    0, - 1, 0,
    - 1, 4, - 1,
    0, - 1, 0
};

// filtr Laplace'a LAPL2

GLfloat laplacian_lapl2[ 9 ] =
{
    - 1, - 1, - 1,
    - 1, 8, - 1,
    - 1, - 1, - 1
};

// filtr Laplace'a LAPL3

GLfloat laplacian_lapl3[ 9 ] =
{
    1, - 2, 1,
    - 2, 4, - 2,
    1, - 2, 1
};

// filtr Laplace'a skośny

GLfloat laplacian_diagonal[ 9 ] =
{
    - 1, 0, - 1,
    0, 4, 0,
    - 1, 0, - 1
};

// filtr Laplace'a poziomy

GLfloat laplacian_horizontal[ 9 ] =
{
    0, - 1, 0,
    0, 2, 0,
    0, - 1, 0
};

// filtr Laplace'a pionowy

GLfloat laplacian_vertical[ 9 ] =
{
    0, 0, 0,
    - 1, 2, - 1,
    0, 0, 0
};

// filtr poziomy Sobela

GLfloat sobel_horizontal[ 9 ] =
{
    1, 2, 1,
    0, 0, 0,
    - 1, - 2, - 1
};

// filtr pionowy Sobela

GLfloat sobel_vertical[ 9 ] =
{
    1, 0, - 1,
    2, 0, - 2,
    1, 0, - 1
};

// filtr poziomy Prewitta

GLfloat prewitt_horizontal[ 9 ] =
{
    - 1, - 1, - 1,
    0, 0, 0,
    1, 1, 1
};

// filtr pionowy Prewitta

GLfloat prewitt_vertical[ 9 ] =
{
    1, 0, - 1,
    1, 0, - 1,
    1, 0, - 1
};

#endif // __FILTERS__H__

Plik przetwarzanie_obrazow.cpp

C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <GL/glut.h>
#include <GL/glext.h>
#ifndef WIN32
#define GLX_GLXEXT_LEGACY
#include <GL/glx.h>
#define wglGetProcAddress glXGetProcAddressARB
#endif
#include <stdio.h>
#include <stdlib.h>
#include "targa.h"
#include "filters.h"

// wskaźnik na funkcję glColorTable

PFNGLCOLORTABLEPROC glColorTable = NULL;

// wskaźnik na funkcję glConvolutionFilter2D

PFNGLCONVOLUTIONFILTER2DPROC glConvolutionFilter2D = NULL;

// wskaźnik na funkcję glConvolutionParameteri

PFNGLCONVOLUTIONPARAMETERIPROC glConvolutionParameteri = NULL;

// wskaźnik na funkcję glHistogram

PFNGLHISTOGRAMPROC glHistogram = NULL;

// wskaźnik na funkcję glGetHistogram

PFNGLGETHISTOGRAMPROC glGetHistogram = NULL;

// stałe do obsługi menu podręcznego

enum
{
    LIGHTNESS_INC, // zwiększenie jasności obrazu
    LIGHTNESS_DEC, // zmniejszenie jasności obrazu
    NEGATIVE, // negatyw obrazu
    HISTOGRAM, // histogram obrazu
   
    // filtry splotowe
    NONE, // brak filtracji
    AVERAGE, // filtr uśredniający
    LP1, // filtr LP1
    LP2, // filtr LP2
    LP3, // filtr LP3
    GAUSS, // filtr Gaussa
    MEAN_REMOVAL, // filtr usuwający średnią
    HP1, // filtr HP1
    HP2, // filtr HP2
    HP3, // filtr HP3
    HORIZONTAL, // filtr poziomy
    VERTICAL, // filtr pionowy
    HORIZONTAL_VERTICAL, // filtr poziomy/pionowy
    GRADIENT_EAST, // filtr gradientowy wschód
    GRADIENT_SOUTH_EAST, // filtr gradientowy południowy wschód
    GRADIENT_SOUTH, // filtr gradientowy południe
    GRADIENT_SOUTH_WEST, // filtr gradientowy południowy zachód
    GRADIENT_WEST, // filtr gradientowy zachód
    GRADIENT_NORTH_WEST, // filtr gradientowy północny zachód
    GRADIENT_NORTH, // filtr gradientowy północ
    GRADIENT_NORTH_EAST, // filtr gradientowy północny wschód
    EMBOSS_EAST, // filtr uwypuklający wschód
    EMBOSS_SOUTH_EAST, // filtr uwypuklający południowy wschód
    EMBOSS_SOUTH, // filtr uwypuklający południe
    EMBOSS_SOUTH_WEST, // filtr uwypuklający południowy zachód
    EMBOSS_WEST, // filtr uwypuklający zachód
    EMBOSS_NORTH_WEST, // filtr uwypuklający północny zachód
    EMBOSS_NORTH, // filtr uwypuklający północ
    EMBOSS_NORTH_EAST, // filtr uwypuklający północny wschód
    LAPLACIAN_LAPL1, // filtr Laplace'a LAPL1
    LAPLACIAN_LAPL2, // filtr Laplace'a LAPL2
    LAPLACIAN_LAPL3, // filtr Laplace'a LAPL3
    LAPLACIAN_DIAGONAL, // filtr Laplace'a skośny
    LAPLACIAN_HORIZONTAL, // filtr Laplace'a poziomy
    LAPLACIAN_VERTICAL, // filtr Laplace'a pionowy
    SOBEL_HORIZONTAL, // filtr poziomy Sobela
    SOBEL_VERTICAL, // filtr pionowy Sobela
    PREWITT_HORIZONTAL, // filtr poziomy Prewitta
    PREWITT_VERTICAL, // filtr pionowy Prewitta
   
    SAVE_FILE, // zapis pliku TARGA
    CLOSE, // zamknięcie okna z histogramem
    EXIT // wyjście
};

// dane opisujące obraz odczytany z pliku TARGA

GLsizei width; // szerokość obrazu
GLsizei height; // wysokość obrazu
GLenum format; // format danych obrazu
GLenum type; // format danych pikseli obrazu
GLvoid * pixels; // wskaźnik na tablicę z danymi obrazu

// współczynnik liniowego skalowania wartości składowych kolorów
// czyli zwiększenie jasności obrazu

GLfloat lightness_scale = 1.0;

// znacznik rysowania obrazu w negatywie

bool negative = false;

// neutralna maska filtra splotowego

GLfloat neutral[ 9 ] =
{
    0, 0, 0,
    0, 1, 0,
    0, 0, 0
};

// wskaźnik na bieżącą maskę filtra splotowego

GLfloat * filter = neutral;

// dane histogramu

GLint histogram[ 256 * 3 ];

// uchwyt okna z wykresem histogramem

int histogram_window = 0;

// uchwyt menu okna z wykresem histogramem

int histogram_menu;

// wczytanie pliku graficznego w formacie TARGA

void LoadTARGA( int argc, char * argv[] )
{
    // sprawdzenie czy jest parametr
    if( argc < 2 )
    {
        printf( "Brak nazwy pliku TARGA\n" );
        exit( 1 );
    }
   
    // odczyt pliku TARGA i ewentualny komunikat o błędzie
    if( !load_targa( argv[ 1 ], width, height, format, type, pixels ) )
    {
        #ifdef WIN32
        printf( "Błąd odczytu lub błędny format pliku: %s\n", argv[ 1 ] );
        #else
       
        printf( "Blad odczytu lub bledny format pliku: %s\n", argv[ 1 ] );
        #endif
       
        exit( 1 );
    }
}

// sprawdzenie czy dany format pliku TARGA jest obsługiwany

void CheckImageFormat()
{
    // obraz w formacie BGR i BGRA
    if( format == GL_BGR || format == GL_BGRA )
    {
        // odczyt wersji OpenGL
        const char * version =( char * ) glGetString( GL_VERSION );
        int major = 0, minor = 0;
        if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
        {
            #ifdef WIN32
            printf( "Błędny format wersji OpenGL\n" );
            #else
           
            printf( "Bledny format wersji OpenGL\n" );
            #endif
           
            exit( 1 );
        }
       
        // sprawdzenie czy jest co najmniej wersja 1.2 OpenGL
        if( major <= 1 && minor < 2 )
        {
            // jeżeli jest starsza wersja OpenGL sprawdzenie
            // czy jest obsługa rozszerzenia GL_EXT_bgra
            if( !glutExtensionSupported( "GL_EXT_bgra" ) )
            {
                // komunikat o błędzie - w tym miejscu można wykonać
                // konwersję danych z formatu BGR/BGRA na RGB/RGBA
                printf( "Brak rozszerzenia GL_EXT_bgra\n" );
                exit( 1 );
            }
        }
    }
}

// zapis pliku graficznego w formacie TARGA

void SaveTARGA( GLenum format )
{
    // wyrównywanie wiersza mapy do pojedyńczego bajta
    glPixelStorei( GL_PACK_ALIGNMENT, 1 );
   
    // wskaźnik na bufor mapy pikselowej
    GLvoid * pixels;
   
    // szerokość i wysokość mapy pikselowej
    GLsizei width = glutGet( GLUT_WINDOW_WIDTH );
    GLsizei height = glutGet( GLUT_WINDOW_HEIGHT );
   
    // utworzenie bufora mapy pikselowej
    if( format == GL_BGRA )
         pixels = new unsigned char[ width * height * 4 ];
    else
    if( format == GL_BGR )
         pixels = new unsigned char[ width * height * 3 ];
    else
    if( format == GL_LUMINANCE )
         pixels = new unsigned char[ width * height ];
    else
         return;
   
    // tylko plik w odcieniach szarości
    if( format == GL_LUMINANCE )
    {
        // określenie przekształcenia składowych RGB na odcienie szarości
        glPixelTransferf( GL_RED_SCALE, 0.229 );
        glPixelTransferf( GL_GREEN_SCALE, 0.587 );
        glPixelTransferf( GL_BLUE_SCALE, 0.114 );
    }
   
    // skopiowanie zawartości bufora koloru do bufora mapy pikselowej
    glReadPixels( 0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels );
   
    // tylko plik w odcieniach szarości
    if( format == GL_LUMINANCE )
    {
        // powrót do neutralnego przekształcenia składowych
        glPixelTransferf( GL_RED_SCALE, 1.0 );
        glPixelTransferf( GL_GREEN_SCALE, 1.0 );
        glPixelTransferf( GL_BLUE_SCALE, 1.0 );
    }
   
    // zapis mapy pikselowej do pliku TARGA
    if( format == GL_BGRA )
         save_targa( "test_BGRA.tga", width, height, format, type, pixels );
    else
    if( format == GL_BGR )
         save_targa( "test_BGR.tga", width, height, format, type, pixels );
    else
    if( format == GL_LUMINANCE )
         save_targa( "test_LUMINANCE.tga", width, height, format, type, pixels );
   
    // porządki
    delete[]( unsigned char * ) pixels;
}

// wyświetlenie i skalowanie obrazu

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // wybór macierzy koloru
    glMatrixMode( GL_COLOR );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // liniowe skalowanie wartości składowych kolorów
    // czyli zwiększenie jasności obrazu
    glScalef( lightness_scale, lightness_scale, lightness_scale );
   
    // definicja dwuwymiarowego filtra splotowego
    glConvolutionFilter2D( GL_CONVOLUTION_2D, GL_RGB, 3, 3, GL_LUMINANCE, GL_FLOAT, filter );
   
    // określenie brzegu obrazu
    glConvolutionParameteri( GL_CONVOLUTION_2D, GL_CONVOLUTION_BORDER_MODE, GL_REPLICATE_BORDER );
   
    // włączenie dwuwymiarowego filtra splotowego
    glEnable( GL_CONVOLUTION_2D );
   
    // negatyw obrazu
    if( negative == true )
    {
       
        // przygotowanie tablicy kolorów z negatywem
        GLubyte negative_table[ 256 * 3 ];
        for( int i = 0; i < 256; i++ )
        {
            negative_table[ 3 * i + 0 ] = 255 - i;
            negative_table[ 3 * i + 1 ] = 255 - i;
            negative_table[ 3 * i + 2 ] = 255 - i;
        }
       
        // utworzenie tablicy kolorów
        glColorTable( GL_COLOR_TABLE, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, negative_table );
       
        // włączenie tablicy kolorów
        glEnable( GL_COLOR_TABLE );
    }
   
    // pozycja mapy pikselowej
    glRasterPos2i( 0, 0 );
   
    // wyrównywanie wiersza mapy pikselowej do pojedyńczego bajta
    glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
   
    // skalowanie mapy pikselowej do rozmiarów okna
    glPixelZoom(( float ) glutGet( GLUT_WINDOW_WIDTH ) /( float ) width,
    ( float ) glutGet( GLUT_WINDOW_HEIGHT ) /( float ) height );
   
    // wyświetlenie obrazu zawartego w mapie pikselowej
    glDrawPixels( width, height, format, type, pixels );
   
    // wyłączenie dwuwymiarowego filtra splotowego
    glDisable( GL_CONVOLUTION_2D );
   
    // wyłączenie tablicy kolorów
    glDisable( GL_COLOR_TABLE );
   
    // skierowanie poleceń do wykonania
    glFinish();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int Width, int Height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, Width, Height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // parametry bryły obcinania
    gluOrtho2D( 0.0, Width, 0.0, Height );
   
    // generowanie sceny 3D
    Display();
}

// wyświetlenie okna z wykresem histogramu

void DisplayHistogram()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // pobranie wysokości okna do skalowania wykresu histogramu
    int win_height = glutGet( GLUT_WINDOW_HEIGHT );
   
    // histogram obrazu w odcieniach szarości
    if( format == GL_LUMINANCE )
    {
        // obliczenie maksymalnej wartości
        GLint max = histogram[ 0 ];
        for( int i = 1; i < 256; i++ )
        if( histogram[ i * 3 ] > max )
             max = histogram[ i * 3 ];
       
        // kolor wykresu histogramu
        glColor3f( 0.0, 0.0, 0.0 );
       
        // narysowanie wykresu histogramu
        glBegin( GL_LINES );
        for( int i = 0; i < 256; i++ )
        {
            glVertex2f( i, 0.0 );
            glVertex2f( i, win_height * histogram[ i * 3 ] /( float ) max );
        }
        glEnd();
    }
   
    // histogram dla obrazów w formacie RGB/RGBA
    if( format == GL_BGRA || format == GL_BGR )
    {
        // podzielenie okna na trzy równe części
        win_height /= 3;
       
        // obliczenie maksymalnej wartości dla każdej składowej
        GLint maxr = histogram[ 0 ];
        GLint maxg = histogram[ 1 ];
        GLint maxb = histogram[ 2 ];
        for( int i = 1; i < 256; i++ )
        {
            if( histogram[ i * 3 + 0 ] > maxr )
                 maxr = histogram[ i * 3 + 0 ];
           
            if( histogram[ i * 3 + 1 ] > maxg )
                 maxg = histogram[ i * 3 + 1 ];
           
            if( histogram[ i * 3 + 2 ] > maxb )
                 maxb = histogram[ i * 3 + 2 ];
           
        }
       
        // narysowanie wykresów histogramu dla każdej składowej
        glBegin( GL_LINES );
       
        // składowa R
        glColor3f( 1.0, 0.0, 0.0 );
        for( int i = 0; i < 256; i++ )
        {
            glVertex2f( i, 0.0 );
            glVertex2f( i, win_height * histogram[ i * 3 + 0 ] /( float ) maxr );
        }
       
        // składowa G
        glColor3f( 0.0, 1.0, 0.0 );
        for( int i = 0; i < 256; i++ )
        {
            glVertex2f( i, win_height );
            glVertex2f( i, win_height + win_height * histogram[ i * 3 + 1 ] /( float ) maxg );
        }
       
        // składowa B
        glColor3f( 0.0, 0.0, 1.0 );
        for( int i = 0; i < 256; i++ )
        {
            glVertex2f( i, 2 * win_height );
            glVertex2f( i, 2 * win_height + win_height * histogram[ i * 3 + 2 ] /( float ) maxb );
        }
        glEnd();
    }
   
    // skierowanie poleceń do wykonania
    glFinish();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna z wykresem histogramu

void ReshapeHistogram( int Width, int Height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, Width, Height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // parametry bryły obcinania
    gluOrtho2D( 0.0, Width, 0.0, Height );
   
    // generowanie okna z histogramem
    DisplayHistogram();
}

// obsługa menu podręcznego

void Menu( int value )
{
    // wskaźniki na maski poszczególnych filtrów splotowych
    GLfloat * filters[ 39 ] =
    {
        neutral, average, lp1, lp2, lp3, gauss, mean_removal, hp1, hp2,
        hp3, horizontal, vertical, horizontal_vertical, gradient_east,
        gradient_south_east, gradient_south, gradient_south_west,
        gradient_west, gradient_north_west, gradient_north,
        gradient_north_east, emboss_east, emboss_south_east, emboss_south,
        emboss_south_west, emboss_west, emboss_north_west, emboss_north,
        emboss_north_east, laplacian_lapl1, laplacian_lapl2,
        laplacian_lapl3, laplacian_diagonal, laplacian_horizontal,
        laplacian_vertical, sobel_horizontal, sobel_vertical,
        prewitt_horizontal, prewitt_vertical
    };
   
    switch( value )
    {
        // zapis pliku w formacie TARGA
    case SAVE_FILE:
        SaveTARGA( format );
        break;
       
        // zwiększenie jasności obrazu
    case LIGHTNESS_INC:
        lightness_scale += 0.1;
       
        // wyświetlenie sceny
        Display();
        break;
       
        // zmniejszenie jasności obrazu
    case LIGHTNESS_DEC:
        if( lightness_scale > 0 )
             lightness_scale -= 0.1;
       
        // wyświetlenie sceny
        Display();
        break;
       
        // filtry splotowe
    case NONE:
    case AVERAGE:
    case LP1:
    case LP2:
    case LP3:
    case GAUSS:
    case MEAN_REMOVAL:
    case HP1:
    case HP2:
    case HP3:
    case HORIZONTAL:
    case VERTICAL:
    case HORIZONTAL_VERTICAL:
    case GRADIENT_EAST:
    case GRADIENT_SOUTH_EAST:
    case GRADIENT_SOUTH:
    case GRADIENT_SOUTH_WEST:
    case GRADIENT_WEST:
    case GRADIENT_NORTH_WEST:
    case GRADIENT_NORTH:
    case GRADIENT_NORTH_EAST:
    case EMBOSS_EAST:
    case EMBOSS_SOUTH_EAST:
    case EMBOSS_SOUTH:
    case EMBOSS_SOUTH_WEST:
    case EMBOSS_WEST:
    case EMBOSS_NORTH_WEST:
    case EMBOSS_NORTH:
    case EMBOSS_NORTH_EAST:
    case LAPLACIAN_LAPL1:
    case LAPLACIAN_LAPL2:
    case LAPLACIAN_LAPL3:
    case LAPLACIAN_DIAGONAL:
    case LAPLACIAN_HORIZONTAL:
    case LAPLACIAN_VERTICAL:
    case SOBEL_HORIZONTAL:
    case SOBEL_VERTICAL:
    case PREWITT_HORIZONTAL:
    case PREWITT_VERTICAL:
        filter = filters[ value - NONE ];
       
        // wyświetlenie sceny
        Display();
        break;
       
        // negatyw obrazu
    case NEGATIVE:
        negative =!negative;
       
        // wyświetlenie sceny
        Display();
        break;
       
        // histogram obrazu
    case HISTOGRAM:
       
        // ustawnienie parametrów histogramu
        glHistogram( GL_HISTOGRAM, 256, GL_RGB, GL_TRUE );
       
        // włączenie obliczeń histogramu
        glEnable( GL_HISTOGRAM );
       
        // wyświetlenie sceny
        Display();
       
        // pobranie danych histogramu
        glGetHistogram( GL_HISTOGRAM, GL_TRUE, GL_RGB, GL_INT, histogram );
       
        // wyłączenie obliczeń histogramu
        glDisable( GL_HISTOGRAM );
       
        // utworzenie okna z wykresem histogramu
        if( histogram_window == 0 )
        {
            // rozmiary okna z wykresem histogram uzależnione
            // są od formatu wyświetlenago pliku graficznego
            if( format == GL_BGRA || format == GL_BGR )
                 glutInitWindowSize( 256, 256 * 3 );
            else
                 glutInitWindowSize( 256, 256 );
           
            // utworzenie okna z wykresem histogramu
            histogram_window = glutCreateWindow( "Histogram" );
           
            // dołączenie funkcji generującej wykres histogramu
            glutDisplayFunc( DisplayHistogram );
           
            // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
            glutReshapeFunc( ReshapeHistogram );
           
            // utworzenie menu podręcznego
            histogram_menu = glutCreateMenu( Menu );
           
            // dodanie przycisku zamykającego okno z wykresem histogramu
            glutAddMenuEntry( "Zamknij", CLOSE );
           
            // określenie przycisku myszki obsługującej menu podręczne
            glutAttachMenu( GLUT_RIGHT_BUTTON );
        }
       
        // wybranie okna z wykresem histogramu
        glutSetWindow( histogram_window );
       
        // wyświetlenie okna z wykresem histogramu
        glutPostRedisplay();
        break;
       
        // zamknięcie okna z histogramem
    case CLOSE:
       
        // wybranie okna z wykresem histogramu
        glutSetWindow( histogram_window );
       
        // usunięcie menu podręcznego okna z wykresem histogramu
        glutDestroyMenu( histogram_menu );
       
        // usunięcie okna z wykresem histogramu
        glutDestroyWindow( histogram_window );
       
        // wyzerowanie uchwytu okna z wykresem histogramu
        histogram_window = 0;
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
        break;
    }
}

// sprawdzenie i przygotowanie obsługi wybranych rozszerzeń

void ExtensionSetup()
{
    // pobranie numeru wersji biblioteki OpenGL
    const char * version =( char * ) glGetString( GL_VERSION );
   
    // odczyt wersji OpenGL
    int major = 0, minor = 0;
    if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
    {
        #ifdef WIN32
        printf( "Błędny format wersji OpenGL\n" );
        #else
       
        printf( "Bledny format wersji OpenGL\n" );
        #endif
       
        exit( 0 );
    }
   
    // sprawdzenie czy jest obsługiwane rozszerzenie ARB_imaging
    if( glutExtensionSupported( "GL_ARB_imaging" ) )
    {
        // pobranie wskaźników wybranych funkcji rozszerzenia ARB_imaging
        glColorTable =( PFNGLCOLORTABLEPROC ) wglGetProcAddress( "glColorTable" );
        glConvolutionFilter2D =
        ( PFNGLCONVOLUTIONFILTER2DPROC ) wglGetProcAddress( "glConvolutionFilter2D" );
        glConvolutionParameteri =( PFNGLCONVOLUTIONPARAMETERIPROC )
        wglGetProcAddress( "glConvolutionParameteri" );
        glHistogram =( PFNGLHISTOGRAMPROC ) wglGetProcAddress( "glHistogram" );
        glGetHistogram =( PFNGLGETHISTOGRAMPROC ) wglGetProcAddress( "glGetHistogram" );
    }
    else
    {
        printf( "Brak rozszerzenia ARB_imaging!\n" );
        exit( 0 );
    }
}

int main( int argc, char * argv[] )
{
    // odczyt pliku graficznego TARGA
    LoadTARGA( argc, argv );
   
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_ALPHA );
   
    // rozmiary głównego okna programu - równe wymiarom
    // odczytanego pliku graficznego TARGA
    glutInitWindowSize( width, height );
   
    // utworzenie głównego okna programu - nazwa okna taka
    // sama jak nazwa odczytanego pliku graficznego TARGA
    glutCreateWindow( argv[ 1 ] );
   
    // dołączenie funkcji generującej scenę 3D - obraz zawarty w pliku TARGA
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // sprawdzenie czy dany format pliku TARGA jest obsługiwany
    CheckImageFormat();
   
    // podmenu "Filtry"
    int MenuFilters = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Filtr uśredniający", AVERAGE );
    glutAddMenuEntry( "Filtr LP1", LP1 );
    glutAddMenuEntry( "Filtr LP2", LP2 );
    glutAddMenuEntry( "Filtr LP3", LP3 );
    glutAddMenuEntry( "Filtr Gaussa", GAUSS );
    glutAddMenuEntry( "Filtr usuwający średnią", MEAN_REMOVAL );
    glutAddMenuEntry( "Filtr HP1", HP1 );
    glutAddMenuEntry( "Filtr HP2", HP2 );
    glutAddMenuEntry( "Filtr HP3", HP3 );
    glutAddMenuEntry( "Filtr poziomy", HORIZONTAL );
    glutAddMenuEntry( "Filtr pionowy", VERTICAL );
    glutAddMenuEntry( "Filtr poziomy/pionowy", HORIZONTAL_VERTICAL );
    glutAddMenuEntry( "Filtr gradientowy wschód", GRADIENT_EAST );
    glutAddMenuEntry( "Filtr gradientowy południowy wschód", GRADIENT_SOUTH_EAST );
    glutAddMenuEntry( "Filtr gradientowy południe", GRADIENT_SOUTH );
    glutAddMenuEntry( "Filtr gradientowy południowy zachód", GRADIENT_SOUTH_WEST );
    glutAddMenuEntry( "Filtr gradientowy zachód", GRADIENT_WEST );
    glutAddMenuEntry( "Filtr gradientowy północny zachód", GRADIENT_NORTH_WEST );
    glutAddMenuEntry( "Filtr gradientowy północ", GRADIENT_NORTH );
    glutAddMenuEntry( "Filtr gradientowy północny wschód", GRADIENT_NORTH_EAST );
    glutAddMenuEntry( "Filtr uwypuklający wschód", EMBOSS_EAST );
    glutAddMenuEntry( "Filtr uwypuklający południowy wschód", EMBOSS_SOUTH_EAST );
    glutAddMenuEntry( "Filtr uwypuklający południe", EMBOSS_SOUTH );
    glutAddMenuEntry( "Filtr uwypuklający południowy zachód", EMBOSS_SOUTH_WEST );
    glutAddMenuEntry( "Filtr uwypuklający zachód", EMBOSS_WEST );
    glutAddMenuEntry( "Filtr uwypuklający północny zachód", EMBOSS_NORTH_WEST );
    glutAddMenuEntry( "Filtr uwypuklający północ", EMBOSS_NORTH );
    glutAddMenuEntry( "Filtr uwypuklający północny wschód", EMBOSS_NORTH_EAST );
    glutAddMenuEntry( "Filtr Laplace'a LAPL1", LAPLACIAN_LAPL1 );
    glutAddMenuEntry( "Filtr Laplace'a LAPL2", LAPLACIAN_LAPL2 );
    glutAddMenuEntry( "Filtr Laplace'a LAPL3", LAPLACIAN_LAPL3 );
    glutAddMenuEntry( "Filtr Laplace'a skośny", LAPLACIAN_DIAGONAL );
    #else
   
    glutAddMenuEntry( "Filtr uśredniający", AVERAGE );
    glutAddMenuEntry( "Filtr LP1", LP1 );
    glutAddMenuEntry( "Filtr LP2", LP2 );
    glutAddMenuEntry( "Filtr LP3", LP3 );
    glutAddMenuEntry( "Filtr Gaussa", GAUSS );
    glutAddMenuEntry( "Filtr usuwajacy srednia", MEAN_REMOVAL );
    glutAddMenuEntry( "Filtr HP1", HP1 );
    glutAddMenuEntry( "Filtr HP2", HP2 );
    glutAddMenuEntry( "Filtr HP3", HP3 );
    glutAddMenuEntry( "Filtr poziomy", HORIZONTAL );
    glutAddMenuEntry( "Filtr pionowy", VERTICAL );
    glutAddMenuEntry( "Filtr poziomy/pionowy", HORIZONTAL_VERTICAL );
    glutAddMenuEntry( "Filtr gradientowy wschod", GRADIENT_EAST );
    glutAddMenuEntry( "Filtr gradientowy poludniowy wschod", GRADIENT_SOUTH_EAST );
    glutAddMenuEntry( "Filtr gradientowy poludnie", GRADIENT_SOUTH );
    glutAddMenuEntry( "Filtr gradientowy poludniowy zachod", GRADIENT_SOUTH_WEST );
    glutAddMenuEntry( "Filtr gradientowy zachod", GRADIENT_WEST );
    glutAddMenuEntry( "Filtr gradientowy polnocny zachod", GRADIENT_NORTH_WEST );
    glutAddMenuEntry( "Filtr gradientowy polnoc", GRADIENT_NORTH );
    glutAddMenuEntry( "Filtr gradientowy polnocny wschod", GRADIENT_NORTH_EAST );
    glutAddMenuEntry( "Filtr uwypuklajacy wschod", EMBOSS_EAST );
    glutAddMenuEntry( "Filtr uwypuklajacy poludniowy wschod", EMBOSS_SOUTH_EAST );
    glutAddMenuEntry( "Filtr uwypuklajacy poludnie", EMBOSS_SOUTH );
    glutAddMenuEntry( "Filtr uwypuklajacy poludniowy zachod", EMBOSS_SOUTH_WEST );
    glutAddMenuEntry( "Filtr uwypuklajacy zachod", EMBOSS_WEST );
    glutAddMenuEntry( "Filtr uwypuklajacy polnocny zachod", EMBOSS_NORTH_WEST );
    glutAddMenuEntry( "Filtr uwypuklajacy polnoc", EMBOSS_NORTH );
    glutAddMenuEntry( "Filtr uwypuklajacy polnocny wschod", EMBOSS_NORTH_EAST );
    glutAddMenuEntry( "Filtr Laplace'a LAPL1", LAPLACIAN_LAPL1 );
    glutAddMenuEntry( "Filtr Laplace'a LAPL2", LAPLACIAN_LAPL2 );
    glutAddMenuEntry( "Filtr Laplace'a LAPL3", LAPLACIAN_LAPL3 );
    glutAddMenuEntry( "Filtr Laplace'a skosny", LAPLACIAN_DIAGONAL );
    #endif
   
    glutAddMenuEntry( "Filtr Laplace'a poziomy", LAPLACIAN_HORIZONTAL );
    glutAddMenuEntry( "Filtr Laplace'a pionowy", LAPLACIAN_VERTICAL );
    glutAddMenuEntry( "Filtr poziomy Sobela", SOBEL_HORIZONTAL );
    glutAddMenuEntry( "Filtr pionowy Sobela", SOBEL_VERTICAL );
    glutAddMenuEntry( "Filtr poziomy Prewitta", PREWITT_HORIZONTAL );
    glutAddMenuEntry( "Filtr pionowy Prewitta", PREWITT_VERTICAL );
    glutAddMenuEntry( "Brak", NONE );
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    #ifdef WIN32
   
    glutAddMenuEntry( "Zwiększenie jasności obrazu", LIGHTNESS_INC );
    glutAddMenuEntry( "Zmniejszenie jasności obrazu", LIGHTNESS_DEC );
    glutAddMenuEntry( "Negatyw", NEGATIVE );
    glutAddMenuEntry( "Histogram", HISTOGRAM );
    glutAddSubMenu( "Filtry", MenuFilters );
    glutAddMenuEntry( "Zapisz", SAVE_FILE );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddMenuEntry( "Zwiekszenie jasnosci obrazu", LIGHTNESS_INC );
    glutAddMenuEntry( "Zmniejszenie jasnosci obrazu", LIGHTNESS_DEC );
    glutAddMenuEntry( "Negatyw", NEGATIVE );
    glutAddSubMenu( "Filtry", MenuFilters );
    glutAddMenuEntry( "Zapisz", SAVE_FILE );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującej menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // sprawdzenie i przygotowanie obsługi wybranych rozszerzeń
    ExtensionSetup();
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokumentNastępny dokument
Mieszanie kolorówBufor szablonowy