Grafický procesor se stal běžnou a již samozřejmou součástí většiny
počítačů. Současný počítač tak představuje heterogenní výpočetní
prostředí, ze kterého velké většina programového vybavení bohužel
stále používá jen jeho, v určitém směru, méně výkonnou část. Právě
algoritmy strojového vidění jsou velmi náročné na výpočetní výkon a
často jsou také velmi dobře paralelizovatelné. Proto plné využití
potenciálu počítačů se může pro aplikace strojového vidění a vizuální
inspekce stát značnou konkurenční výhodou.
GPU mohou provádět mnoho operací s daty, které jsou tradičně
prováděny pomocí CPU, a některé z těchto operací mohou velmi významně
urychlit. Zatímco CPU mají jen několik výpočetních jader, GPU jich
mají několik set. Grafické procesory byly původně navrženy pouze pro
akceleraci počítačové grafiky a pro práci s grafickými daty. I když
dnešní systému mohou pracovat i s obecnými daty, funkčnost grafických
procesorů stále zůstává hodně omezená a zdaleka ne všechny typy úloh
je vhodné řešit prostřednictvím GPU.
Na úvod si připomeňme základní koncepce architektury GPU v
grafických adaptérech počítačů a nakonec se zaměříme na využití
programovatelných GPU s systému strojového vidění VisionLab.
Vnitřní architektury GPU hlavních výrobců NVIDIA, AMD a Intel se
navzájem hodně liší. Jedná se o velmi složité obvody. Jen jako
ilustraci složitosti a výkonnosti moderních GPU si uveďme několik
parametrů grafického procesoru GeForce GTX 680:
Počet tranzistorů |
3.5 miliardy |
Počet výpočetních jader |
1536 |
Hodinové frekvence GPU |
1006 MHz |
Výkon přístupu k texturám |
128.8 miliard pixelů za sekundu |
Šířka sběrnice grafické paměti |
384 bitů |
Datová propustnost |
192.2 GB za sekundu |
Výpočetní výkon (single presision) |
3.79 TFLOPS |
Při pohledu na tyto působivé parametry je zřejmé, jak velká škoda
by byla nechat tento potenciál ležet ladem.
Tento procesor má již uniformní architekturu shaderů a je dalším
důkazem, že vývoj v této oblasti stále více směřuje k vyšší
použitelnosti GPU pro obecné výpočty než jen pro akceleraci grafiky.
Tento trend začal u firmy NVIDIA uvedením přelomového procesoru G80 v
roce 2006. V uniformní architektuře mohou všechny procesory v GPU
podle aktuálních potřeb vystupovat v roli vertexových, fragmentových
nebo geometrických shaderů.
Cesta k dnešní uniformní architektuře nebyla krátká. S využitím
grafických akcelerátorů pro akceleraci výpočtů se dokonce
experimentovalo již době, kdy tyto čipy měly pouze pevně danou
funkčnost a nebylo je možno programovat. Využívaly se hardwarové
interpolátory a rasterizery, filtry textur, míchání vektorů, operace
s accumulator a stencil bufferem atd. Tyto postupy
vyžadovaly od programátorů hlubokou znalost principů fungování
grafického řetězce a značnou kreativitu. Dalším krokem ve vývoji byly
pak programovatelné fragmentové procesory s již standardizovanými
pseudo assemblery, které se staly součástí grafických ovladačů.
Hardware i instrukční soubory jednotlivých GPU byly a jsou doposud
odlišné a právě překladače v grafických ovladačích umožnily
přenositelnost kódů shaderů (zde máme trochu zmatení pojmů, označení
shader se používalo jak pro programovatelné fragmentové procesory, tak
pro programy těchto procesorů). Tento způsob programování GPU byl
stále pro většinu programátorů zcela nestravitelný a používala jej jen
hrstka nadšenců.
Důležitým momentem byl příchod vysokoúrovňových jazyků pro
programování shaderů. Všichni tři zástupci ( Cg ( NVIDIA pro DirectX i
OpenGL), HLSL „High-Level Shading Language“( Microsoft DirectX) a GLSL
„OpenGL Shading Language“(dodnes standardní jazyk shaderů pro OpenGL)
) mají podobné schopnosti a jejich zápis se velmi podobá jazyku C.
Tyto jazyky jsou již velmi dobře použitelné pro programování obecných
výpočtů, to znamená, že jsou jim přístupny téměř veškeré vlastnosti
současných GPU, nicméně stále ještě z programátorů nesnímají
nutnost znát vlastnosti grafického řetězce a přistupovat k GPU
prostřednictvím grafického API. Původní základní princip GPU, totiž
vytváření obrázků je i v jazycích vysoké úrovně stále
viditelný.
Dnes existuje řada softwarových prostředí a nástrojů, které
programátora izolují od této původní podstaty GPU a předkládají mu
obecně přijatelnější programovací model. Tyto nástroje, k nimž
patří např. Sh (Rapid Mind), BrookGPU, Accelerator (Microsoft),
PeakStream (Google), Ashli (nadstavba nad Cg, HLSL a GLSL), CTM ( AMD
), CUDA (NVIDIA), ArBB (Intel), OpenCL atd. Tyto nástroje více či méně
obcházejí grafické ovladače, umožňují pracovat s pamětí v podobě
proměnných a polí C jazyka a někdy dokonce
i ukazatelů (CUDA) a především zajišťují upload vstupních
dat do textur v grafické paměti, download výsledků z frame
bufferu grafické paměti a just-in-time kompilaci a optimalizaci
kódu shaderů. Vstupní i výstupní data mají obvykle podobu polí
vektorů. Objevují se systémy, kde rozhraním na GPU je virtuální stroj,
který zajistí přenositelnost kódu. V této oblasti je stále situace
velice nepřehledná, probíhá zde rychlý vývoj a chybí silné
a hlavně dlouhodobě stabilní standardy.
Naštěstí v oboru strojového vidění pracujeme převážně s obrazovými
daty a tradiční podoba grafického řetězce nám většinou velmi dobře
vyhovuje.
Obr.1: Řetězec zpracování grafických dat v GPU
Při obecných výpočtech pracuje GPU velice podobně. Pouze místo
obrazových dat se pracuje se vstupními a výstupními bloky
obecných dat a shadery jsou do jisté míry obecnými paralelními vlákny.
Obraz z kamery vstupuje do GPU jako objekt textury, který musí být na
začátku přenesen ze systémové paměti počítače do grafické paměti,
které je přístupná pro GPU. Výsledný obrazový buffer nakonec také musí
být často přenesen nazpět do systémové paměti počítače. Také bloky
obecných dat musí být takto přenášeny mezi systémovou a grafickou
pamětí. Tento princip může být pro programátora částečně zakryt
programovým rozhraním vývojového prostředí, limity z něj plynoucí však
překonat nelze.
Právě přenosy velkých objemů dat mezi systémovou a grafickou pamětí
(grafické adaptéry jsou obvykle připojeny přes sběrnici PCI-Express)
mohou představovat citelné zpomalení a stát se tím rozhodujícím
problémem v neprospěch využití GPU.
Úloha vhodná pro GPU také musí mít několik charakteristických
znaků. CPU má jen několik jader, ale každé toto jádro může pracovat na
zcela odlišné úloze. GPU má výpočetních jader velmi mnoho, ale
zjednodušeně řečeno, všechna jádra musí, byť s různými vstupními daty,
zpracovávat tutéž úlohu. Výhody takovéto masivní paralelizace se více
projeví v úlohách s vysokou výpočetní intenzitou, velkými objemy
paralelně zpracovávaných dat a s minimálními závislostmi
mezi jednotlivými těmito daty. Výsledek každého paralelního výpočtu v
GPU závisí jen na vstupních datech a nikoliv na výsledky výpočtů
ostatních paralelních procesů. Jednotlivá výpočetní jádra se tedy
v průběhu výpočtů nemohou ovlivňovat, pouze mohou číst mnoho
vstupních vektorových dat z mnoha datových polí.
Prostředky GPU
Pro zpracování obrazu nás zajímají především:
Programovatelné procesory – v uniformní architektuře
mohou vystupovat jako vertexové, fragmentové a geometrické. Při
zpracování obrazu z kamery využijeme tradiční funkce vertexových
a fragmentových shaderů:
Vertexový shader je spuštěn pro každý vertex,
vykreslujeme-li např. trojúhelník, je spušten celkem
třikrát. Jeho výsledkem je grafický element, který je předán
do rasterizeru.
Fragmentový shader je spoušten výstupem rasterizeru
pro každý fragment, tedy pro každý obrazový bod vykreslované
scény.
Rasterizer – prostřednictvím interpolace vytváří
jednotlivé fragmenty z obrazových elementů. Základním obrazovým
elementem je trojúhelník, definovaný třemi vertexy. V jeho
ploše jsou pak resterizerem interpolovány texturové souřadnice
nebo barvy fragmentů.
Textury – jsou bloky grafické paměti, které mohou být
čteny fragmentovými procesory. Data z texturových objektů lze
pouze číst, zápis do nich není fragmentovými shadery
možný.
Framebuffer – je blok paměti, do které fragmentové
procesory zapisují výsledku běhu fragmentových shaderů. Data
uvnitř framebufferu nelze fragmentovým procesorem číst
a dokonce každý s paralelně běžících fragmentových
shaderů může zapsat jen do jediné pozice v bufferu, která
odpovídá jeho instanci v rámci plochy obrazu.
Omezení textur jako zdrojů dat, do kterých není možno
zapisovat, a framebufferů jako cílů, které nelze číst, klade další
nároky na kopírování bloků dat v grafické paměti během opakovaného
vícenásobného spouštení shaderů pro daný framebuffer. Proto dnes
existuje možnost přiřazení textury jako cíle vykreslování. Tato
možnost umožňuje realizovat vícenásobné vykreslování maximálně
efektivně.
Datové typy a programovatelnost
GPU byly navrženy pro práci s obrazovými daty a
zpočátku tak byly podporovány pouze celočíselné datové typy,
které popisovaly fragmenty, tj. obrazové body – pixely. Pro
všeobecné výpočty je důležitá schopnost dnešních GPU pracovat s
poli vektorů čísel s plovoucí řádovou čárkou s jednoduchou (FP32
– 4 byte na jedno číslo s plovoucí řádovou čárkou –float) i
dvojnásobnou (FP64 – 8 byte na jedno číslo s plovoucí řádovou
čárkou - double) přesností. Vstupní a výstupní data mohou být
obvykle v těchto formátech:
8 bitů na pixel – takto je reprezentován buď index do
palety barev nebo dva bity pro červenou, tři zelenou a tři
modrou barvu
16 bitů na pixel – obvykle pět bitů určuje červenou a po
šesti bitech má k dispozici zelená a modrá barva
24 bitů na pixel – osmibitový červený, zelený a modrý
kanál
32 bitů na pixel – osmibitový červený, zelený, modrý a
alpha kanál reprezentující míru průhlednosti
1 až 4 čísla v plovoucí řádové čárce na pixel, kdy každé
číslo má přesnost na 16, 24, 32 nebo 64 bitů (FP16, FP24, FP32
nebo FP64)
Podstatnou vlastností paralelních procesorů v GPU je také
to, že pracují v jedné instrukci s celými vektory. Např. při
sečítání dvou čtyřrozměrných vektorů jsou současně sečteny všechny
jejich příslušné složky.
Ovladače grafických adaptérů
Se začleněním překladačů vysokoúrovňových jazyků se grafické
ovladače dále zkomplikovaly a jejich dlouholetá pověstná chybovost
se mohla rozšířit do nových rozsáhlých oblastí. Na počátcích
těchto technologií to opravdu programátoři neměli jednoduché.
Relativně nejméně chyb vždy bývalo v ovladačích pro grafické
adaptéry NVIDIA. Dnes již je v této oblasti situace naštěstí
celkem stabilní a software využívající GPU obvykle pracuje
spolehlivě s ovladači všech hlavních výrobců.
Využití GPU v systému strojového vidění VisionLab
Algoritmy strojového vidění často pracují s velkými objemy
dat a v těchto případech nebývá nikdy výpočetního výkonu
nazbyt. Využití GPU všude tam, kde je to rozumné, nejenže přináší
podstatnou akceleraci zpracování, ale dokonce umožňuje realizovat
takové algoritmy, které by jinak byly v akceptovatelném čase
nerealizovatelné.
V programovém systému Control Web existují dva základní typy
kamerových virtuálních přístrojů. Kromě základního přístroje
camera existuje ještě přístroj gl_camera, který pro zpracování
obrazových dat z kamery i jejich zobrazování používá
vykreslovací stroj systému Control Web, který se vyznačuje
architekturou klient-server a pracuje ve vlastním paralelním
vláknu. Tento vykreslovací server mohutně využívá programovatelné
GPU. V optimální situaci se tedy jedno jádro CPU stará o
přípravu grafických dat a jejich transport do grafické
paměti, kde jsou tato data masivně paralelně zpracována grafický
procesorem. Takové řešení přináší výjimečně vysoký výkon při
minimálním zatěžování vláken v reálném čase běžící aplikace.
Zde je několik příkladů:
Pokročilá adaptivní interpolace barevné mozaiky
Podstatnou věcí ve prospěch kvality obrazu je možnost číst
z barevných kamer syrová data a interpolovat barevnou mozaiku
až v počítači a nikoliv již v kameře. Řada
operací s obrazem je vzhledem k omezeným možnostem čipů
kamerových řídicích jednotek velmi kompromisní. Kamera při
interpolaci barevné mozaiky musí zůstat omezena na základní
bilineární interpolaci, která je řešitelná celočíselnými
výpočty. Obraz je pak zatížen všemi nežádoucími artefakty
tohoto postupu, které plynou mimo jiného ze vzájemného
prostorového posunu červeného a modrého barevného kanálu. Ve
výsledku pak pozorujeme v blízkosti kontrastních hran
šachovnicové vzory a modročervené lemování. Pokročilé
algoritmy, které produkují výrazně čistší a ostřejší
obraz, musí využívat výpočtů v plovoucí řádové čárce a
obsahují více průchodů plochou obrazu. Takový algoritmus již
není možno v reálném čase na proudu dat z kamery
řešit ani pomocí vícejádrových CPU, je však příkladem ideální
úlohy pro masivní paralelizaci v GPU. Virtuální přístroj
gl_camera umožňuje volit mezi několika algoritmy
interpolace barevné mozaiky.
Adaptivní interpolace je v CPU nerealizovatelná v reálném
čase a použití jakéhokoliv, byť "zastaralého" GPU je velkým
přínosem.
Korekce geometrických zkreslení obrazu
Programovatelná GPU umožňují velmi elegantně řešit
problematiku změn geometrie obrazu, tzv. pixel displacement.
Virtuální přístroj gl_camera tímto způsobem elegantně řeší
nejen kalibraci geometrických zkreslení objektivů, ale dokáže
např. i korigovat perspektivní zkreslení, odstranit natočení
obrazu, rozvinout obraz z povrchu válce či koule. Umožňuje
srovnat libovolně nelineárně deformovaný obraz.
Tato úloha je rozdělena na dva klíčové problémy: za prvé
jak informaci o správných pozicích získat a zadruhé jak obraz
kvalitně zpracovat v reálném čase bez neúnosné zátěže
počítače.
Nejprve musíme získat obrazovou mapu, která popisuje změny
pozic všech pixelů výsledného obrazu. Mapu získáme tak, že do
zobrazovací roviny vložíme bodový rastr a systém si uloží
kalibrační data pro daný objektiv a konfiguraci měření.
Algoritmus měření mapy objektivy pracuje v CPU s přesností na
setiny pixelu. Zde nám tolik nezáleží na rychlosti, tvorba
mapy proběhne jen jednou, ale požadujeme nejvyšší
přesnost.
Rychlost geometrických korekcí každého snímku z kamery je
již kritická a tento algoritmus musí běžet v GPU. Ukazuje se,
že kvalita hardwarových texturových filtrů a interpolátorů u
námi vyzkoušených GPU není dostatečná pro kvalitu obrazu
požadovanou pro tuto úlohu. Proto je tvorba výsledného obrazu
řešena s vysokou subpixelovou přesností programem
fragmentového shaderu, který poskytuje zaručeně kvalitní a
stabilní výsledky na veškerých GPU.
Obr. 4: Znázornění obrazové mapy pro geometrickou
kalibraci
Některé systémy strojového vidění se pokouší kalibraci
geometrie obrazového pole řešit v CPU. Výsledky ale odpovídají
tomu, že z důvodů šetření výpočetním výkonem musí být
zavedena řada omezení a kompromisů. Často se používá po
částech bilineární interpolace obrazové mapy a také
filtrace obrazu nemůže být dostatečně kvalitní. Výsledkem je
pak nespojitý schodovitý obraz.
Tato funkce je i prostředky nejvýkonnějších
vícejádrových CPU realizovatelná jen problematicky a použití i
slabšího zastaralého GPU je velkým přínosem.
Obrazové filtry
Obrazové filtry vypadají na první pohled jako optimální
úlohy pro řešení v GPU. Často tomu tak je, ale nemusí to
platit univerzálně. Potíž může být v tom, že řada jednoduchých
kernelových filtrů nemá příliš vysokou výpočetní intenzitu (a
současné vícejádrové CPU také nepočítají až tak zoufale
pomalu). Režie přenosu obrazových dat mezi systémovou a
grafickou pamětí pak může i stonásobně vyšší rychlost výpočtů
kernelu v GPU oproti CPU znehodnotit.
Obr. 5: Rychlost i velmi komplexních GPU filtrů je
omezena převážně režií přenosu dat
Situace se ale dramaticky změní v případě filtrů s nutností
složitějších výpočtů v plovoucí řádové čárce, jako je tomu
např. u transformací barevných prostorů, řešení saturační
matic, šumových filtrů atd. Pak nám může využití GPU zrychlit
tyto kroky až o dva řády.
Obr. 6: Anisotropický šumový filtr využívá výkonu
GPU ve výpočtech s plovoucí řádovou čárkou
Jednoznačnou úlohou pro GPU jsou i takové filtry, kdy pro
výpočet každého pixelu je nutno číst mnoho pixelů z jeho okolí
– příkladem mohou být algoritmy lokálního prahování obrazu.
Pak schopnost GPU načítat desítky gigapixelů za sekundu z
textur je nenahraditelná. GPU řešení může být i několiksetkrát
rychlejší než snaha o totéž v CPU.
Obr. 7: Lokálně prahovací filtr využívá mohutného
datového toku GPU při čtení pixelů textur
Ve prospěch GPU se situace dále přiklání ve všech
případech, kdy je nutno určitý filtr vícekrát opakovat. Tento
požadavek je charakteristický např. u morfologických
filtrů, které nejsou samy o sobě výpočetně příliš
náročné, ale často potřebují desítky i stovky opakovaných běhů
– příkladem může být např. hledání koster objektů nebo
segmentace ploch kolem objektů. Obrazová data během
opakovaných běhů filtru neopouští grafickou paměť a jsou stále
rychle přístupná pro GPU. Pro tyto algoritmy je masivní
paralelizace velkým přínosem.
Transformační kódování
Příkladem zde může výt dvojrozměrná Fourierova transformace
- převod obrazu z prostorové (časové) do frekvenční domény
(krok gpu_fast_fourier_transform_2D). Byť je zde použit
algoritmus rychlé Fourierovy transformace (FFT), transformace
obrazu je natolik výpočetně intenzivní, že v reálném čase není
jinak než s využitím GPU realizovatelná. Zde již není
možno použít starší GPU, potřebné jsou vektory vstupních
textur i výstupních bufferů s plovoucí řádovou čárkou.
Takže nemáme na výběr – bez moderního grafického procesoru to
v tomto případě nejde.
GPU dokáže realizovat rychlou dvourozměrnou Fourierovu
transformaci na proudu obrazových dat z kamery v reálném
čase.
Další možnosti
Veškeré dosud uváděné příklady byly celkem jednoznačné.
Existují však i problematické úlohy, kde přínosy GPU nemusejí
být takto jasné.
Příkladem takové úlohy může být např. rozpoznávání vzorů
realizované prostřednictvím GPU (krok gpu_match_monochrome v
systému VisionLab). Během hledání obrazového vzoru musí systém
mnohokrát provádět normalizovanou křížovou korelaci s velkým
počtem pixelů. To vyžaduje mnohonásobně opakované výpočty
středních jasů, směrodatných odchylek, odmocnin atd. – to vše
s plovoucí řádovou čárkou. Navíc krok hledá i pootočené nebo
zvětšené či zmenšené obrazy předloh. Veškeré výpočty tedy
navíc podléhají lineárním transformacím. Toto všechno vypadá
na první pohled jaké výborná úloha pro GPU. Je však zde
závažná potíž – výpočetní intenzita algoritmu je natolik
vysoká, že není hrubou silou rozumně proveditelný ani
nejvýkonnějšími GPU. Podobně jako v algoritmech pro CPU, musí
i zde nastoupit řada optimalizací. A právě v těchto
optimalizacích, bez kterých ale problém tak jako tak řešit
nelze, se skrývá kámen úrazu. Algoritmus je nutno rozdělit do
více fází, mezi kterými musí probíhat výměna dat mezi GPU a
CPU. Časové ztráty přináší jednak kopírování bloků dat, ale
také to, že CPU i GPU musí na sebe vzájemně mezi jednotlivými
fázemi čekat a nemohou běžet paralelně. Tento krok je tedy
výhodné použít jen s moderními velmi výkonnými GPU – čas
hledání může být až desetinásobně kratší než na vícejádrových
CPU. S méně výkonnými grafickými procesoru se ale může situace
obrátit a krok může být i pomalejší než obdobný realizovaný
čistě v CPU. Autor aplikace tedy musí vše zvážit a prakticky
vyzkoušet.
Obr. 10: Hledání vzorů realizované prostřednictvím
GPU - tento příklad trvá asi 50ms na procesoru G80
Obdobně, jako existují úlohy, pro které nelze použít
masivně paralelní algoritmy v GPU, je zde i spousta úloh,
které nelze efektivně řešit dokonce ani paralelními vlákny
v CPU. Neexistuje zde jediný lék na všechno.
Pro každou aplikaci musí její autor zvolit nejlepší způsob
řešení. A k tomuto rozhodování patří i to, kde využije
mohutného potenciálu grafických procesorů.
Závěr
Systém strojového vidění VisionLab poskytuje krokům, které
využívají GPU značný prostor. S vykreslovacím jádrem systému
Control Web i virtuálním přístrojem gl_camera komunikují jen
prostřednictvím velmi jednoduchého povelového rozhraní. Kroky
dostanou k dispozici paměťový pixel buffer dané velikosti
a formátu pixelů a mohou si zcela samostatně alokovat
libovolné prostředky GPU – mohou si vytvářet např. vlastní
texturové objekty, další frame buffer objekty, samozřejmě mohou
načítat a spouštět vlastní shadery atd. Kroky strojového
vidění tak mohou využívat veškerá nejnovější rozšíření, které
přinášení aktuální grafické ovladače. Tyto možnosti umožňují
systému strojového vidění VisionLab stále sledovat poslední vývoj
technologií a přinášet tak svým uživatelům vysoký užitek a
konkurenční výhodu.
rc
|