Obsah
Konvoluční filtry v OpenGL Imaging SubsetuVýznam konvolučních filtrů při práci s rastrovým obrazem
Parametry ovlivňující nastavení konvolučních filtrů
Dvojdimenzionální konvoluční filtr
Příklad jednoduchých 2D konvolučních filtrů
Pokračování
Seznam funkcí zmíněných v této části
Nové funkce popsané v této části
Demonstrační příklady
Zkomprimovaná verze článku i s přílohami
Konvoluční filtry v OpenGL Imaging Subsetu
Při vykreslování rastrových obrázků pomocí funkce glDrawPixels() je možné na vykreslované obrázky aplikovat jednodimenzionální i dvojdimenzionální konvoluční filtry. Tyto filtry jsou však podporovány pouze pro ty implementace OpenGL, které obsahují plnou podporu Imaging Subsetu. Konvoluční filtry lze použít pouze při práci s barevnými hodnotami zapisovanými v barvových režimech RGBA (true color). To znamená, že je nelze použít ani pro změnu pixelů nesoucích index barvy (v paletovém režimu), ani pro pixely nesoucí hloubku (zapisovanou do paměti hloubky – depth bufferu) nebo šablonu (zapisovanou do paměti šablony – stencil bufferu).
Na prvním obrázku je zobrazena část vykreslovacího řetězce, který je použit při vykreslování rastrových obrazů pomocí funkce glDrawPixels(). Na tomto obrázku je také zvýrazněn modul, ve kterém mohou být na vykreslovaný obraz aplikovány konvoluční filtry. Černé šipky na prvním obrázku ukazují tok dat při povolení funkce všech modulů, šedé šipky tok dat v případě, že se činnost některých modulů zakáže. Jak je z obrázku patrné, nemusí se konvoluce provádět. Při inicializaci OpenGL je konvoluce zakázána, podobně jako další rozšiřující operace definované v Imaging Subsetu, například aplikace barvových palet.
Obrázek 1: Část vykreslovacího řetězce s vyznačeným modulem pro provádění konvoluce
Význam konvolučních filtrů při práci s rastrovým obrazem
Digitální rastrový obraz je představován obrazovými elementy (picture elements – pixels), které bývají uspořádány do určité vzorkovací mřížky. V dalším textu budeme uvažovat zdaleka nejpoužívanější čtvercovou mřížku – matici – jejíž prvky odpovídají kvantovacím úrovním jasové či barvové funkce.
Nejjednodušší reprezentací obrazových dat v paměti počítače je dvojrozměrná mřížka bodů. Každý bod mřížky odpovídá definičnímu oboru diskrétní obrazové funkce I(x, y) a příslušná hodnota pixelu (tj. jeho jas či barva) hodnotě této funkce v daném bodě. Plošné uspořádání bodů může mít podobu buď čtvercové, hexagonální, trojúhelníkové, nebo jiné mřížky. Technicky i programově se nejsnadněji realizuje čtvercová mřížka, i když pro účely filtrace je mnohdy výhodnější mřížka hexagonální (ta je použita v některých digitálních fotoaparátech).
Obrazová informace může být v mřížce uložena buď přímo hodnotou jasu (monochromatické obrázky), hodnotami barvových složek RGB, nebo indexem do palety LUT.
Pod pojmem filtrace si můžeme představit soubor lokálních transformací rastrového obrazu, kterými se v případě monochromatických obrazů převádí hodnoty jasu původního obrazu na nové hodnoty jasu obrazu výstupního. Barevný obraz si můžeme pro účely filtrace představit jako tři monochromatické obrazy, z nichž každý obsahuje jas jedné barvové složky. Podle vlastností funkčního vztahu pro výpočet jasu výsledného okolí na základě okolí O ve vstupním obrazu můžeme rozdělit metody filtrace na lineární a nelineární.
Lineární operace vyjadřují výslednou hodnotu jasu jako konvoluci okolí O příslušného bodu (i, j) a konvolučního jádra. Mezi postupy předzpracování obrazu patří i algoritmy obnovení, které se obvykle také vyjadřují ve formě konvoluce. Okolím O, které se používá k výpočtu, je ale celý obraz. Jedná se tedy o výpočetně velmi náročnou operaci. Obnovení se používá pro odstranění poruch s předem známými vlastnostmi jako například rozostření objektivu nebo rozmazání vlivem pohybu při snímání.
V dalším textu se však budeme zabývat pouze konvolučními filtry, které pracují nad poměrně malým okolím zpracovávaných pixelů. Velikost konvolučního jádra určuje i velikost zpracovávaného okolí.
Parametry ovlivňující nastavení konvolučních filtrů
Jádro konvolučního filtru v OpenGL Imaging Subsetu sestává ze čtyř vektorů či matic reálných hodnot, kde je pro každou barvovou složku R, G, B, A použit samostatný vektor nebo matice. Pro jednodimenzionální filtry se parametry zadávají ve formě vektoru, pro filtry dvojdimenzionální ve formě matice. Zadávání hodnot, které se do matice mají uložit, se provádí pomocí funkcí glConvolutionFilter1D() a glConvolutionFilter2D(). Hodnoty předávané v těchto funkcích jsou před uložením do jádra konvolučního filtru (matic) podrobeny minimálně dvěma operacím:
- Jsou vynásobeny konstantami GL_CONVOLUTION_FILTER_SCALE.
- K výsledku jsou přičteny konstanty GL_CONVOLUTION_FILTER_BIAS.
Vzhledem k tomu, že se pro každou barvovou složku R, G, B a A vytváří samostatná matice, je nutné specifikovat i čtyři multiplikativní konstanty GL_CONVOLUTION_FILTER_SCALE a čtyři aditivní konstanty GL_CONVOLUTION_FILTER_BIAS.
Všechny tyto konstanty se nastavují pomocí funkcí:
void glConvolutionParameteriv( GLenum target, GLenum pname, GLint *params ); void glConvolutionParameterfv( GLenum target, GLenum pname, GLfloat *params );
Význam argumentů těchto funkcí:
- Argument target musí být nastaven na hodnotu GL_CONVOLUTION_1D, GL_CONVOLUTION_2D neboGL_SEPARABLE_2D podle typu měněného filtru.
- Argument pname je podle měněných konstant nastaven buď na hodnotu GL_CONVOLUTION_FILTER_SCALE, nebo na hodnotu GL_CONVOLUTION_FILTER_BIAS.
- Argument params je ukazatel na pole čtyř hodnot typuGLfloat nebo GLint. Každé barvové složce R, G, B a A odpovídá jedna předávaná hodnota.
Dvojdimenzionální konvoluční filtr
Nejpoužívanějším konvolučním filtrem je při práci s rastrovými obrazy bezesporu dvojdimenzionální konvoluční filtr. Jeho užitečnost spočívá především ve velkých možnostech změny rastrového obrazu, které přesahují možnosti jednodimenzionálních filtrů. Pomocí dvojdimenzionálních konvolučních filtrů je možné provádět ostření obrazu, rozmazávání, zvýrazňování hran nebo tvorbu reliéfů (vytlačeného vzoru) ze zadaného rastrového obrazu.
Parametry dvojdimenzionálního konvolučního filtru se nastavují pomocí funkce:
void glConvolutionFilter2D( GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data );
Význam jednotlivých argumentů funkce glConvolutionFilter2D():
- Argument target musí být u této funkce vždy nastaven na hodnotuGL_CONVOLUTION_2D.
- V argumentech width a height je uložena velikost rastrového obrazu filtru v operační paměti počítače. Filtr je v této paměti uložen jako běžná pixmapa.
- Argumenty format, type a data specifikují formát uložení pixmapy filtru v operační paměti počítače. Jejich význam je stejný jako u funkce glDrawPixels() s tím rozdílem, že nejsou podporovány formátyGL_COLOR_INDEX, GL_DEPTH_COMPONENT a GL_STENCIL_INDEX, stejně jako typ obrázku (bitmapy) GL_BITMAP.
Obrázek specifikovaný pomocí funkce glConvolutionFilter2D() je přenesen z operační paměti počítače a jsou s ním provedeny podobné operace jako s pixely přenášenými pomocí funkce glDrawPixels(). Přenos se však zastaví ihned po konverzi do vnitřní reprezentace RGBA, další operace ani zápis do framebufferu se samozřejmě neprovádí.
Barvové komponenty jsou pro každý pixel vynásobeny čtyřmi konstantami GL_CONVOLUTION_FILTER_SCALE a následně jsou k nim přičteny konstanty GL_CONVOLUTION_FILTER_BIAS. Všechny tyto konstanty jsou specifikovány pomocí funkce glConvolutionParameterf() – viz text výše. Na rozdíl od barvových složek však nejsou výsledné parametry filtru (tj. hodnoty v matici) ořezány do rozsahu 0..1, proto je možné zadávat i záporné hodnoty.
Vnitřně jsou pak parametry konvoluční matice filtru převedeny tak, aby odpovídaly internímu formátu specifikovanému v parametru internalFormat. Tento interní formát je stejný jako například u textur ukládaných do texturovací paměti a může obsahovat tyto hodnoty: GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA,GL_INTENSITY, GL_RGB nebo GL_RGBA. Hodnoty barvových složek jsou uloženy ve formátu pohyblivé řádové čárky, aby byly dále prováděné filtrace zatíženy co nejmenší chybou a nedocházelo k přetečení mezivýsledků.
Příklad jednoduchých 2D konvolučních filtrů
Obyčejné průměrování filtruje obraz tím, že nová hodnota jasu se spočítá jako aritmetický průměr jasu čtvercového nebo obdélníkového okolí. Velikost skvrn šumu by měla být menší než velikost okolí a to by mělo být menší než nejmenší významný detail v obrazu. Vždy ale dojde k rozmazání hran.
Konvoluční jádro filtru velikosti 3×3 pro obyčejné průměrování má tvar:
1 |1 1 1| h= - |1 1 1| 9 |1 1 1|
Rozšířením obyčejného průměrování je průměrování s Gaussovským rozložením. Toto rozložení samozřejmě nelze použít bez dalších úprav, protože by velikost konvoluční masky byla nekonečná. Proto se konvoluční maska filtru vytvoří tak, že se zvýší váhy středového bodu masky a/nebo i jeho 4-okolí (tj. bodů, které mají se středovým bodem společnou jednu souřadnici, druhá se o jednotku liší). Jedna z možných podob konvoluční masky má tvar:
1 |1 2 1| h= -- |2 4 2| 16 |1 2 1|
Všimněte si, že součet všech položek konvoluční matice dává po vynásobení vahou před maticí výslednou hodnotu 1. To zjednodušeně znamená, že se nemění celková světlost obrázku.
Mezi filtry používané pro zvýraznění hran patří Sobelův operátor. Pomocí tohoto operátoru jsou aproximovány první parciální derivace 2D funkce představované obrazem, jedná se tedy o operátor směrově závislý. Směr se u těchto operátorů udává podle světových stran. Sobelův operátor ve směru „sever“ má například tvar:
| 1 2 1| h= | 0 0 0| |-1 -2 -1|
Sobelův operátor v jiném směru lze získat rotací této matice.
Pokračování
V dalším pokračování tohoto seriálu se budeme zabývat jednorozměrnými a separabilními konvolučními filtry. Ty sice nejsou tak flexibilní jako filtry dvojrozměrné, ale jejich výpočet je jednodušší a tím i rychlejší.
Seznam funkcí zmíněných v této části
glDrawPixels()glConvolutionFilter1D()
Nové funkce popsané v této části
glConvolutionParameteriv()glConvolutionParameterfv()
glConvolutionFilter2D()
Demonstrační příkldy
V prvním demonstračním příkladu je na vykreslovaný obrázek aplikován filtr pro obyčejné průměrování na okolí 3×3 pixely. Pomocí klávesy E je možné filtraci zapnout či vypnout.
Zdrojový kód prvního demonstračního příkladu je dostupný zde, HTML verze se zvýrazněním syntaxe zde.
Obrázek 2: Originální obrázek, na který nejsou aplikovány žádné filtry
Obrázek 3: Screenshot prvního demonstračního příkladu s povolenou filtrací
Ve druhém demonstračním příkladu je na vykreslovaný obrázek aplikován Gaussův filtr pro rozmazání. Oproti předchozímu příkladu dochází k menšímu rozmazání hran, neboť je zvýrazněna váha středového pixelu a jeho čtyřokolí v konvolučním jádru.
Zdrojový kód druhého demonstračního příkladu je dostupný zde, HTML verze se zvýrazněním syntaxe zde.
Obrázek 4: Screenshot prvního demonstračního příkladu s povolenou filtrací
Ve třetím demonstračním příkladu je na rastrový obrázek aplikován Sobelův filtr v „severním“ směru. Pomocí klávesy E je opět možné filtr zapnout či vypnout.
Zdrojový kód třetího demonstračního příkladu je dostupný zde, HTML verze se zvýrazněním syntaxe zde.
Obrázek 5: Screenshot třetího demonstračního příkladu s povolenou filtrací
Pro spuštění demonstračních příkladů je nutné mít stažený i původní obrázek Leny, který pro jistotu opět přikládám i do zkomprimované verze článku, i když je totožný s obrázkem z předchozí části.
Zkomprimovaná verze článku i s přílohami
Zkomprimovaná verze tohoto článku spolu s demonstračními příklady a obrázky je umístěna zde.