Hlavní navigace

OpenGL Imaging Subset (7)

13. 4. 2004
Doba čtení: 7 minut

Sdílet

Dnešní díl seriálu o OpenGL Imaging Subsetu navazuje na díl předchozí. Dnes si popíšeme jednorozměrné konvoluční filtry, které jsou výpočetně jednodušší než filtry dvojrozměrné. Popíšeme i filtry separabilní, které představují zajímavou možnost vytváření dvojrozměrných filtrů z filtrů jednorozměrných. Na závěr si popíšeme funkce pro povolení a zákaz aplikace konvolučních filtrů a operace použité pro získání hodnot konvolučních filtrů (tj. konvolučního jádra) přímo z framebufferu.

Obsah

Jednodimenzionální konvoluční filtr
Separovatelný dvojdimenzionální konvoluční filtr
Získání hodnot konvolučních filtrů přímo z framebufferu
Zpětné přečtení parametrů aktuálně nastaveného konvolučního filtru
Povolení a zákaz aplikace konvolučních filtrů na vykreslovaný rastrový obraz
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ý článek s přílohami

Jednodimenzionální konvoluční filtr

Jednodimenzionální konvoluční filtr představuje jednodušší variantu filtru dvojdimenzionál­ního. Konvoluční jádro tohoto filtru není vytvořeno z matice hodnot, ale pouze z jednorozměrného vektoru. Důvod, proč se tyto filtry používají, spočívá ve značně zrychleném výpočtu a v lokalitě dat, zejména v případě, že se filtr aplikuje v horizontálním směru, tj. po jednotlivých řádcích.

Jednodimenzionální filtr však nelze použít při práci s rastrovými obrazy, ale pouze s jednorozměrnými texturami, což poněkud omezuje jeho aplikaci.

Jednodimenzionální konvoluční filtr se nastavuje pomocí funkce:

void glConvolutionFilter1D(
    GLenum  target,
    GLenum  internalFormat,
    GLsizei width,
    GLenum  format,
    GLenum  type,
    void    *data
);

Význam jednotlivých argumentů funkce glConvolution­Filter1D():

  • Argument target musí být nastaven na hodnotu GL_CONVOLUTION_1D.
  • Argumenty internalFormat, width, format atype mají stejný význam jako u dvojrozměrného konvolučního filtru s tím rozdílem, že předávaná pixmapa je vždy jednorozměrná, proto se nemusí zadávat její výška.
  • Argument data tedy musí být ukazatel na jednorozměrné pole „pixelů“.

Podobně jako u dvojrozměrného konvolučního filtru, i zde se nejdříve provede přenos pixelů z operační paměti počítače do paměti grafické karty, přičemž se pixely rozdělují na barvové složky. Tyto složky jsou posléze vynásobeny konstantami GL_CONVOLUTION_FIL­TER_SCALE zadanými funkcí:

void glConvolutionParameterf(
    GL_CONVOLUTION_1D,
    GL_CONVOLUTION_FILTER_SCALE,
    params
);

a přičteny ke konstantám GL_CONVOLUTION_FIL­TER_BIAS, které jsou zadány pomocí funkce:

void glConvolutionParameterf(
    GL_CONVOLUTION_1D,
    GL_CONVOLUTION_FILTER_BIAS,
    params
);

Aktuálně nastavené hodnoty konstant GL_CONVOLUTION_FIL­TER_SCALE aGL_CONVOLUTI­ON_FILTER_BIAS lze získat po zavolání funkce, která vrací hodnotu konstant ve formě čísel v pohyblivé řádové čárce:

void glGetConvolutionParameterfv(
    GLenum target,
    GLenum pname,
    GLfloat *params
);

Alternativní možností je získání hodnot těchto konstant ve formě celých čísel pomocí funkce:

void glGetConvolutionParameteriv(
    GLenum target,
    GLenum pname,
    GLint *params
);

Mezi nejpoužívanější jednodimenzionální konvoluční filtry patří filtry pro rozmazání obrazu a tzv. Robertsovy operátory, pomocí nichž je možné detekovat hrany nebo ostřit obraz.

Separovatelný dvojdimenzionální konvoluční filtr

V OpenGL Imaging Subsetu lze vytvořit i separovatelný dvojdimenzionální konvoluční filtr. Jedná se o dvojdimenzionální konvoluční filtr s tím rozdílem, že parametry konvolučního jádra filtru nejsou zadány pomocí matice hodnot, ale pomocí dvojice jednodimenzio­nálních filtrů, které jsou na sebe v parametrické rovině „kolmé“. Zpracování obrazu může vypadat tak, že se v jednom směru (například horizontálním) aplikuje první jednodimenzionální filtr a na vzniklý meziobrázek se aplikuje ve druhém směru (vertikálním) druhý jednodimenzio­nální filtr.

Parametry separovatelného dvojdimenzionálního filtru se nastavují pomocí funkce:

void glSeparableFilter2D(
    GLenum  target,
    GLenum  internalFormat,
    GLsizei width,
    GLsizei height,
    GLenum  format,
    GLenum  type,
    const   GLvoid *row,
    const   GLvoid *column
);

Význam jednotlivých argumentů funkce glSeparableFil­ter2D():

  • Argument target musí být pro tento typ filtru vždy nastaven na hodnotu GL_SEPARABLE_2D.
  • Argumentem internalFormat se nastavuje vnitřní formát hodnot uložených v konvoluční matici výsledného dvojdimenzionálního filtru.
  • V argumentech width a height je předána velikost filtru, tj. počet řádků a sloupců matice představující konvoluční jádro filtru.
  • Argumenty format a type mají stejný význam jako u funkcíglCon­volutionFilter1D() a glConvolution­Filter2D().
  • Vzhledem k tomu, že se parametry separovatelného filtru zadávají pro každý směr zvlášť, je nutné vyplnit dva argumenty row a column. Jak již název těchto argumentů naznačuje, jedná se o jednorozměrné vektory specifikující řádkový a sloupcový jednodimenzio­nální filtr.

Význam konstant GL_CONVOLUTION_FIL­TER_SCALE aGL_CONVOLUTI­ON_FILTER_BIAS zůstává stejný jako u jednodimenzi­onálních a dvojdimenzionálních filtrů. Tyto parametry se zadávají pomocí funkcí glConvolution­Parameteriv() a glConvolution­Parameterfv() s argumentem target nastaveným na GL_SEPARABLE_2D.

Získání hodnot konvolučních filtrů přímo z framebufferu

Kromě přímého zadání jádra konvolučního filtru pomocí funkcí glConvolution­Filter1D(), glConvolution­Filter2D() a glSeparableFil­ter2D() je možné parametry filtru získat přímo z framebufferu. V praxi to vypadá tak, že se přečtou rastrová data z barvového bufferu uloženého ve framebufferu a tato data jsou považována za prvky konvolučního jádra filtru.

Čtení probíhá stejným způsobem jako v případě použití funkceglCopyPi­xels(). Po čtení a případných úpravách tak získáme RGBA hodnoty jednotlivých pixelů. Tyto hodnoty jsou po převodu do reprezentace v pohyblivé řádové čárce uloženy jako nové jádro konvolučního filtru.

Parametry jádra jednodimenzio­nálního konvolučního filtru se dají přečíst z framebufferu pomocí následující funkce:

void glCopyConvolutionFilter1D(
    GLenum target,
    GLenum internalFormat,
    GLint x,
    GLint y,
    GLsizei width,
);

Argument target musí obsahovat hodnotu GL_CONVOLUTION_1D. Souřadnice levého dolního rohu rastrového obrázku čteného z framebufferu jsou specifikovány pomocí hodnot argumentů x a y. V posledním argumentu je uložena šířka čteného obrázku, výška je vždy rovna jednomu pixelu, protože se jedná o jednorozměr­ný filtr.

Parametry jádra dvojdimenzionálního konvolučního filtru se dají přečíst z framebufferu pomocí funkce:

void glCopyConvolutionFilter2D(
    GLenum target,
    GLenum internalFormat,
    GLint x,
    GLint y,
    GLsizei width,
    GLsizei height
);

Argument target musí obsahovat hodnotu GL_CONVOLUTION_2D. Pomocí hodnot uložených v argumentech x a y se specifikují souřadnice levého dolního rohu rastrového obrázku ve framebufferu. Hodnoty argumentů width a height obsahují šířku a výšku rastrového obrázku, která je zadaná v pixelech. Argument internalFormat má stejný význam jako u předchozích funkcí.

Podobně jako u přímého zadání jádra konvolučních filtrů, i zde se provádí lineární transformace hodnot pomocí předem zadaných konstant GL_CONVOLUTION_FIL­TER_SCALE a GL_CONVOLUTION_FIL­TER_BIAS.

Zpětné přečtení parametrů aktuálně nastaveného konvolučního filtru

Parametry aktuálně nastaveného konvolučního filtru lze získat pomocí funkce:

void glGetConvolutionFilter(
    GLenum target,
    GLenum format,
    GLenum type,
    GLvoid *image
);

Požadovaný typ čtených dat (GL_FLOAT, GL_INT atd.) se specifikuje v argumentu format, podobně jako u zadávání parametrů filtru. Argument target může nabývat hodnot GL_CONVOLUTION_1D, GL_CONVOLUTION_2D neboGL_SEPARAB­LE_2D. Jak je z popisu argumentu target patrné, může tato funkce pracovat korektně pro všechny podporované typy filtů, tj. jednodimenzionální, dvojdimenzionální i separabilní.

Parametry separabilního konvolučního filtru lze získat pomocí funkce:

void glGetSeparableFilter(
    GLenum target,
    GLenum format,
    GLenum type,
    GLvoid *row,
    GLvoid *column,
    GLvoid *span
);

Význam parametrů této funkce je shodný s parametry funkce glSeparableFil­ter2D() s tím rozdílem, že se data přes ukazatelerow, column a span kopírují nazpět do operační paměti počítače.

Povolení a zákaz aplikace konvolučních filtrů na vykreslovaný rastrový obraz

Filtrace pomocí konvolučního filtru se musí explicitně povolit pomocí jedné z následujících operací, které se liší pouze typem zadaného filtru (1D, 2D, separabilní):

// povolení aplikace jednodimenzionálního filtru
void glEnable(
    GL_CONVOLUTION_1D
);

// povolení aplikace dvojdimenzionálního filtru
void glEnable(
    GL_CONVOLUTION_2D
);

// povolení aplikace separabilního filtru
void glEnable(
    GL_SEPARABLE_2D
);

Opětovný zákaz filtrace je možné provést jedním z následujících příkazů:

// zákaz aplikace 1D filtru
void glDisable(
    GL_CONVOLUTION_1D
);

// zákaz aplikace 2D filtru
void glDisable(
    GL_CONVOLUTION_2D
);

// zákaz aplikace separabilního filtru
void glDisable(
    GL_SEPARABLE_2D
);

Pokračování

V dalším pokračování tohoto seriálu si ukážeme práci s histogramem, pomocí nějž lze zjistit zajímavé statistické informace o vykreslovaném obraze.

Seznam funkcí zmíněných v této části

glEnable()
glDisable()
glConvolution­Parameterf()
glConvolution­Filter2D()
glCopyPixels()
 

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

glConvolution­Filter1D()
glSeparableFil­ter2D()
glGetSeparable­Filter()
glGetConvoluti­onFilter()
glGetConvoluti­onParameterfv()
glGetConvoluti­onParameteriv()
glCopyConvolu­tionFilter1D()
glCopyConvolu­tionFilter2D()
 

Demonstrační příklady

V prvním demonstračním příkladu se zobrazuje rastrový obrázek pomocí funkce glDrawPixels(). Při přenosu obrázku se na něj aplikuje separabilní konvoluční filtr, který provádí filtraci v horizontálním směru v okolí pěti pixelů – ve vertikálním směru k rozmazání obrázku nedochází. Pomocí klávesyE je možné aplikaci filtru povolit či zakázat.

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 1: Originální obrázek, na který nejsou aplikovány žádné filtry

Screenshot prvního příkladu s aplikací filtru pro horizontální rozmazání obrazu
Obrázek 2: Screenshot prvního příkladu s aplikací filtru pro horizontální rozmazání obrazu

Druhý demonstrační příklad se v mnohém podobá příkladu prvnímu s tím rozdílem, že se rastrový obraz filtruje ve směru vertikálním.

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 příkladu s aplikací filtru pro vertikální rozmazání obrazu
Obrázek 3: Screenshot druhého příkladu s aplikací filtru pro vertikální rozmazání obrazu

Ve třetím demonstračním příkladu se provádí detekce horizontálních hran na čtveřici sousedních pixelů.

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 příkladu s aplikací filtru pro detekci horizontálních hran
Obrázek 4: Screenshot třetího příkladu s aplikací filtru pro detekci horizontálních hran

Ve čtvrtém demonstračním příkladu se provádí detekce hran vertikálních.

Zdrojový kód čtvrtého demonstračního příkladu je dostupný zde, HTML verze se zvýrazněním syntaxe zde.

Screenshot čtvrtého příkladu s aplikací filtru pro detekci vertikálních hran
Obrázek 5: Screenshot čtvrtého příkladu s aplikací filtru pro detekci vertikálních hran

UX DAy - tip 2

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ázky uvedenými v předchozích dílech.

Zkomprimovaný článek 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.