Hlavní navigace

OpenGL Imaging Subset (6)

6. 4. 2004
Doba čtení: 8 minut

Sdílet

V dnešním dílu seriálu o OpenGL Imaging Subsetu si popíšeme práci s konvolučními filtry. Aplikace konvolučních filtrů na vykreslované pixmapy představuje velmi silný nástroj pro tvorbu různých grafických efektů, jako je rozmazání obrazu, zvýraznění hran nebo vykreslení reliéfních obrazů (emboss).

Obsah

Konvoluční filtry v OpenGL Imaging Subsetu
Vý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.

Část vykreslovacího řetězce s vyznačeným modulem pro provádění konvoluce
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 o­kolí.

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í glConvolution­Filter1D() a glConvolution­Filter2D(). 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:

  1. Jsou vynásobeny konstantami GL_CONVOLUTION_FIL­TER_SCALE.
  2. K výsledku jsou přičteny konstanty GL_CONVOLUTION_FIL­TER_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_FIL­TER_SCALE a čtyři aditivní konstanty GL_CONVOLUTION_FIL­TER_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í:

  1. Argument target musí být nastaven na hodnotu GL_CONVOLUTION_1D, GL_CONVOLUTION_2D neboGL_SEPARAB­LE_2D podle typu měněného filtru.
  2. Argument pname je podle měněných konstant nastaven buď na hodnotu GL_CONVOLUTION_FIL­TER_SCALE, nebo na hodnotu GL_CONVOLUTION_FIL­TER_BIAS.
  3. 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 jednodimenzio­ná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 glConvolution­Filter2D():

  1. Argument target musí být u této funkce vždy nastaven na hodnotuGL_CON­VOLUTION_2D.
  2. 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.
  3. 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_IN­DEX, GL_DEPTH_COMPO­NENT a GL_STENCIL_INDEX, stejně jako typ obrázku (bitmapy) GL_BITMAP.

Obrázek specifikovaný pomocí funkce glConvolution­Filter2D() 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_FIL­TER_SCALE a následně jsou k nim přičteny konstanty GL_CONVOLUTION_FIL­TER_BIAS. Všechny tyto konstanty jsou specifikovány pomocí funkce glConvolution­Parameterf() – 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_AL­PHA,GL_INTENSI­TY, 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()
glConvolution­Filter1D()

Nové funkce popsané v této části

glConvolution­Parameteriv()
glConvolution­Parameterfv()
glConvolution­Filter2D()
 

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.

Originální obrázek, na který nejsou aplikovány žádné filtry
Obrázek 2: Originální obrázek, na který nejsou aplikovány žádné filtry

Screenshot prvního demonstračního příkladu s povolenou filtrací
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.

Screenshot druhého demonstračního příkladu s povolenou filtrací
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.

Screenshot třetího demonstračního příkladu s povolenou filtrací
Obrázek 5: Screenshot třetího demonstračního příkladu s povolenou filtrací

root_podpora

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.

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.