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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
Zerowanie histogramu
Zerowanie danych zawartych w tablicy histogramu realizuje funkcja:
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:
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:
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:
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:
Zerowanie tablicy minimum-maksimum
Wyzerowanie danych tablicy minimum-maksimum wymaga wywołania funkcji:
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.
W programie wykorzystano kilka dotąd nieopisywanych funkcji biblioteki GLUT. Do przełączania pomiędzy oknami aplikacji służy funkcja:
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:
void glutDestroyWindow( int win )
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:
void glutDestroyMenu( int menu )
której parametr menu to unikatowy identyfikator (uchwyt) manu zwracany przez funkcję glutCreateMenu.
Plik filters.h
#ifndef __FILTERS__H__
#define __FILTERS__H__
#include <GL/gl.h>
GLfloat average[ 9 ] =
{
0.11111111, 0.11111111, 0.111111111,
0.11111111, 0.11111111, 0.111111111,
0.11111111, 0.11111111, 0.111111111
};
GLfloat lp1[ 9 ] =
{
0.1, 0.1, 0.1,
0.1, 0.2, 0.1,
0.1, 0.1, 0.1
};
GLfloat lp2[ 9 ] =
{
0.08333333, 0.08333333, 0.08333333,
0.08333333, 0.33333333, 0.08333333,
0.08333333, 0.08333333, 0.08333333
};
GLfloat lp3[ 9 ] =
{
0.05, 0.05, 0.05,
0.05, 0.60, 0.05,
0.05, 0.05, 0.05
};
GLfloat gauss[ 9 ] =
{
0.0625, 0.1250, 0.0625,
0.1250, 0.2500, 0.1250,
0.0625, 0.1250, 0.0625
};
GLfloat mean_removal[ 9 ] =
{
- 1, - 1, - 1,
- 1, 9, - 1,
- 1, - 1, - 1
};
GLfloat hp1[ 9 ] =
{
0, - 1, 0,
- 1, 5, - 1,
0, - 1, 0
};
GLfloat hp2[ 9 ] =
{
1, - 2, 1,
- 2, 5, - 2,
1, - 2, 1
};
GLfloat hp3[ 9 ] =
{
0.0000, - 0.0625, 0.0000,
- 0.0625, 1.2500, - 0.0625,
0.0000, - 0.0625, 0.0000
};
GLfloat horizontal[ 9 ] =
{
0, - 1, 0,
0, 1, 0,
0, 0, 0
};
GLfloat vertical[ 9 ] =
{
0, 0, 0,
- 1, 1, 0,
0, 0, 0
};
GLfloat horizontal_vertical[ 9 ] =
{
- 1, 0, 0,
0, 1, 0,
0, 0, 0
};
GLfloat gradient_east[ 9 ] =
{
- 1, 1, 1,
- 1, - 2, 1,
- 1, 1, 1
};
GLfloat gradient_south_east[ 9 ] =
{
- 1, - 1, 1,
- 1, - 2, 1,
1, 1, 1
};
GLfloat gradient_south[ 9 ] =
{
- 1, - 1, - 1,
1, - 2, 1,
1, 1, 1
};
GLfloat gradient_south_west[ 9 ] =
{
1, - 1, - 1,
1, - 2, - 1,
1, 1, 1
};
GLfloat gradient_west[ 9 ] =
{
1, 1, - 1,
1, - 2, - 1,
1, 1, - 1
};
GLfloat gradient_north_west[ 9 ] =
{
1, 1, 1,
1, - 2, - 1,
1, - 1, - 1
};
GLfloat gradient_north[ 9 ] =
{
1, 1, 1,
1, - 2, 1,
- 1, - 1, - 1
};
GLfloat gradient_north_east[ 9 ] =
{
1, 1, 1,
- 1, - 2, 1,
- 1, - 1, 1
};
GLfloat emboss_east[ 9 ] =
{
- 1, 0, 1,
- 1, 1, 1,
- 1, 0, 1
};
GLfloat emboss_south_east[ 9 ] =
{
- 1, - 1, 0,
- 1, 1, 1,
0, 1, 1
};
GLfloat emboss_south[ 9 ] =
{
- 1, - 1, - 1,
0, 1, 0,
1, 1, 1
};
GLfloat emboss_south_west[ 9 ] =
{
0, - 1, - 1,
1, 1, - 1,
1, 1, 0
};
GLfloat emboss_west[ 9 ] =
{
1, 0, - 1,
1, 1, - 1,
1, 0, - 1
};
GLfloat emboss_north_west[ 9 ] =
{
1, 1, 0,
1, 1, - 1,
0, - 1, - 1
};
GLfloat emboss_north[ 9 ] =
{
1, 1, 1,
0, 1, 0,
- 1, - 1, - 1
};
GLfloat emboss_north_east[ 9 ] =
{
0, 1, 1,
- 1, 1, 1,
- 1, - 1, 0
};
GLfloat laplacian_lapl1[ 9 ] =
{
0, - 1, 0,
- 1, 4, - 1,
0, - 1, 0
};
GLfloat laplacian_lapl2[ 9 ] =
{
- 1, - 1, - 1,
- 1, 8, - 1,
- 1, - 1, - 1
};
GLfloat laplacian_lapl3[ 9 ] =
{
1, - 2, 1,
- 2, 4, - 2,
1, - 2, 1
};
GLfloat laplacian_diagonal[ 9 ] =
{
- 1, 0, - 1,
0, 4, 0,
- 1, 0, - 1
};
GLfloat laplacian_horizontal[ 9 ] =
{
0, - 1, 0,
0, 2, 0,
0, - 1, 0
};
GLfloat laplacian_vertical[ 9 ] =
{
0, 0, 0,
- 1, 2, - 1,
0, 0, 0
};
GLfloat sobel_horizontal[ 9 ] =
{
1, 2, 1,
0, 0, 0,
- 1, - 2, - 1
};
GLfloat sobel_vertical[ 9 ] =
{
1, 0, - 1,
2, 0, - 2,
1, 0, - 1
};
GLfloat prewitt_horizontal[ 9 ] =
{
- 1, - 1, - 1,
0, 0, 0,
1, 1, 1
};
GLfloat prewitt_vertical[ 9 ] =
{
1, 0, - 1,
1, 0, - 1,
1, 0, - 1
};
#endif
Plik przetwarzanie_obrazow.cpp
#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"
PFNGLCOLORTABLEPROC glColorTable = NULL;
PFNGLCONVOLUTIONFILTER2DPROC glConvolutionFilter2D = NULL;
PFNGLCONVOLUTIONPARAMETERIPROC glConvolutionParameteri = NULL;
PFNGLHISTOGRAMPROC glHistogram = NULL;
PFNGLGETHISTOGRAMPROC glGetHistogram = NULL;
enum
{
LIGHTNESS_INC,
LIGHTNESS_DEC,
NEGATIVE,
HISTOGRAM,
NONE,
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,
SAVE_FILE,
CLOSE,
EXIT
};
GLsizei width;
GLsizei height;
GLenum format;
GLenum type;
GLvoid * pixels;
GLfloat lightness_scale = 1.0;
bool negative = false;
GLfloat neutral[ 9 ] =
{
0, 0, 0,
0, 1, 0,
0, 0, 0
};
GLfloat * filter = neutral;
GLint histogram[ 256 * 3 ];
int histogram_window = 0;
int histogram_menu;
void LoadTARGA( int argc, char * argv[] )
{
if( argc < 2 )
{
printf( "Brak nazwy pliku TARGA\n" );
exit( 1 );
}
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 );
}
}
void CheckImageFormat()
{
if( format == GL_BGR || format == GL_BGRA )
{
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 );
}
if( major <= 1 && minor < 2 )
{
if( !glutExtensionSupported( "GL_EXT_bgra" ) )
{
printf( "Brak rozszerzenia GL_EXT_bgra\n" );
exit( 1 );
}
}
}
}
void SaveTARGA( GLenum format )
{
glPixelStorei( GL_PACK_ALIGNMENT, 1 );
GLvoid * pixels;
GLsizei width = glutGet( GLUT_WINDOW_WIDTH );
GLsizei height = glutGet( GLUT_WINDOW_HEIGHT );
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;
if( format == GL_LUMINANCE )
{
glPixelTransferf( GL_RED_SCALE, 0.229 );
glPixelTransferf( GL_GREEN_SCALE, 0.587 );
glPixelTransferf( GL_BLUE_SCALE, 0.114 );
}
glReadPixels( 0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels );
if( format == GL_LUMINANCE )
{
glPixelTransferf( GL_RED_SCALE, 1.0 );
glPixelTransferf( GL_GREEN_SCALE, 1.0 );
glPixelTransferf( GL_BLUE_SCALE, 1.0 );
}
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 );
delete[]( unsigned char * ) pixels;
}
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_COLOR );
glLoadIdentity();
glScalef( lightness_scale, lightness_scale, lightness_scale );
glConvolutionFilter2D( GL_CONVOLUTION_2D, GL_RGB, 3, 3, GL_LUMINANCE, GL_FLOAT, filter );
glConvolutionParameteri( GL_CONVOLUTION_2D, GL_CONVOLUTION_BORDER_MODE, GL_REPLICATE_BORDER );
glEnable( GL_CONVOLUTION_2D );
if( negative == true )
{
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;
}
glColorTable( GL_COLOR_TABLE, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, negative_table );
glEnable( GL_COLOR_TABLE );
}
glRasterPos2i( 0, 0 );
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
glPixelZoom(( float ) glutGet( GLUT_WINDOW_WIDTH ) /( float ) width,
( float ) glutGet( GLUT_WINDOW_HEIGHT ) /( float ) height );
glDrawPixels( width, height, format, type, pixels );
glDisable( GL_CONVOLUTION_2D );
glDisable( GL_COLOR_TABLE );
glFinish();
glutSwapBuffers();
}
void Reshape( int Width, int Height )
{
glViewport( 0, 0, Width, Height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( 0.0, Width, 0.0, Height );
Display();
}
void DisplayHistogram()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT );
int win_height = glutGet( GLUT_WINDOW_HEIGHT );
if( format == GL_LUMINANCE )
{
GLint max = histogram[ 0 ];
for( int i = 1; i < 256; i++ )
if( histogram[ i * 3 ] > max )
max = histogram[ i * 3 ];
glColor3f( 0.0, 0.0, 0.0 );
glBegin( GL_LINES );
for( int i = 0; i < 256; i++ )
{
glVertex2f( i, 0.0 );
glVertex2f( i, win_height * histogram[ i * 3 ] /( float ) max );
}
glEnd();
}
if( format == GL_BGRA || format == GL_BGR )
{
win_height /= 3;
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 ];
}
glBegin( GL_LINES );
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 );
}
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 );
}
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();
}
glFinish();
glutSwapBuffers();
}
void ReshapeHistogram( int Width, int Height )
{
glViewport( 0, 0, Width, Height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( 0.0, Width, 0.0, Height );
DisplayHistogram();
}
void Menu( int value )
{
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 )
{
case SAVE_FILE:
SaveTARGA( format );
break;
case LIGHTNESS_INC:
lightness_scale += 0.1;
Display();
break;
case LIGHTNESS_DEC:
if( lightness_scale > 0 )
lightness_scale -= 0.1;
Display();
break;
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 ];
Display();
break;
case NEGATIVE:
negative =!negative;
Display();
break;
case HISTOGRAM:
glHistogram( GL_HISTOGRAM, 256, GL_RGB, GL_TRUE );
glEnable( GL_HISTOGRAM );
Display();
glGetHistogram( GL_HISTOGRAM, GL_TRUE, GL_RGB, GL_INT, histogram );
glDisable( GL_HISTOGRAM );
if( histogram_window == 0 )
{
if( format == GL_BGRA || format == GL_BGR )
glutInitWindowSize( 256, 256 * 3 );
else
glutInitWindowSize( 256, 256 );
histogram_window = glutCreateWindow( "Histogram" );
glutDisplayFunc( DisplayHistogram );
glutReshapeFunc( ReshapeHistogram );
histogram_menu = glutCreateMenu( Menu );
glutAddMenuEntry( "Zamknij", CLOSE );
glutAttachMenu( GLUT_RIGHT_BUTTON );
}
glutSetWindow( histogram_window );
glutPostRedisplay();
break;
case CLOSE:
glutSetWindow( histogram_window );
glutDestroyMenu( histogram_menu );
glutDestroyWindow( histogram_window );
histogram_window = 0;
break;
case EXIT:
exit( 0 );
break;
}
}
void ExtensionSetup()
{
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( 0 );
}
if( glutExtensionSupported( "GL_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[] )
{
LoadTARGA( argc, argv );
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_ALPHA );
glutInitWindowSize( width, height );
glutCreateWindow( argv[ 1 ] );
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
CheckImageFormat();
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 );
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
glutAttachMenu( GLUT_RIGHT_BUTTON );
ExtensionSetup();
glutMainLoop();
return 0;
}