GolfBot
GolfBot spis projektu (pripravuje sa) ...
Contents
Program
Prepojenie s kamerou a určovanie orientačných bodov
Obraz sme získavali pomocou Kinectu pre XBox 360. Máme dve triedy, Kinect
(odvodená od edu.ufl.digitalworlds.j4k.J4KSDK
) a Camera
(implementuje Runnable
).
Hlavný program spúšťa vlákno kamery, ktoré následne pri inicializácii spustí vlákno kinectu v móde J4KSDK.COLOR
. Inštancii tredy Kinect
je potom poslaná referencia na kameru. Trieda Kinect
prekrýva metódu udalosti onColorFrameEvent
, ktorá nastáva po zosnímaní farebného obrazu kinectom, pričom tento obraz posiela inštancii kamery cez referenciu volaním jej verejnej metódy setImageFromKinect
.
Hlavné vlákno kamery vykonáva cyklicky tieto akcie:
Najprv sa otestuje, či existuje snímka z kamery.
- Ak nie, ukončí iteráciu cyklu a pokračuje ďalšou iteráciou.
- Ak áno:
- Vytvorí si novú kópiu obrázku (pre účely spracovania).
- Aplikuje gaussovo rozmazanie pracovného obrázka z polomerom 3px.
- Skonvertuje obrázok z farebného formátu BGR do LAB.
- Pomocou prahovania, nájdeniu najväčšieho spojitého komponentu a priemerovania x, y pozícií pixelov najväčšieho spoločného komponentu nájde stredy kruhových značiek a lopty.
- Určia sa polohy cieľa (stred obrázka), lopty (oranžová značka na obrázku), polomer lopty a pozicia robota.
- Spočíta sa uhol natočenia robota (podľa zelenej a modrej značky).
- Vykreslia sa získané body do náhľadového obrázka.
- Hlavné vlákno programu je notifikované o dokončení cyklu kamery (teda sú spočítané potrebné body pre ďalšiu fázu programu hlavného vlákna).
Gaussovo rozmazanie obrázku
Na rozmazanie obrázka (blur) je použítá aproximácia gaussovho rozmazávacieho algoritmu, ktorý dokáže pracovať v lineárnom čase. Použili sme port algoritmu z JavaScriptu z blogu Ivana Kuckira, nájsť ho je možné na stránke Fastest Gaussian Blur (in linear time) (jedná sa o algoritmus 4 na tejto stránke).
Keďže algoritmus počíta s tým, že obrázok je tvorený polom bajtov, kde jeden bajt reprezentuje jeden pixel, bolo nutné obrázok rozdeliť na tri obrázky, ktoré reprezentovali jeho červenú, zelenú a modrú farebnú zložku samostatne. Potom, ako algoritmus rozmazania dokončil svoju prácu na každom z týchto rozdelených obrázkov, bolo treba ich opätovne spojiť do jedného obrázka. Na toto nám slúžia metódy separateColor
a putColor
.
Prevod z RGB do LAB
Keďže zosnímaný obrázok z kamery je vo formáte, kde jeden pixel je určený štyrmi bajtmi s významom: Blue, Green, Red a posledný je vždy nulový (hodnota 0), pričom tieto hodnoty sú v rozsahu od 0 do 255, je treba najprv každý pixel preformátovať do tvaru RGB, kde každá hodnota je float
s hodnotou od 0 do 1. Na to máme metódu getPixel
s následným predelením každej zložky hodnotou 255.
Každý pixel je potom vložený do statickej metódy triedy CIELab
s názvom fromRGB
, ktorá vráti hodnotu pixelu vo formáte farieb LAB. Trieda CIELab
na prevod z RGB do LAB ešte používa medzikrok, kde každý RGB pixel prevedie do formátu XYZ, z ktorého následne vytvorí LAB formát.
Prahovanie
Prahovanie používame na získanie masky informácií o prítomnosti farebnej informácie v LAB formáte v spracovávanom obrázku. Tento algoritmus (metóda prahuj(float[] from, float[] to)
) dostáva minimálnu a maximálnu hodnotu prahu farby v LAB formáte a v obrázku zisťuje, pre každý pixel, či je v rozsahu tejto hodnoty alebo nie je. Ak pixel v rozsahu je, dostane výstupná maska na pozícii pixelu hodnotu 1, v opačnom prípade je použitá hodnota 0.
int[] o = new int[potrebná veľkosť] for každé x for každé y if pixel na x a y je medzi from a to zapíš do o 1 else zapíš do o 0 return o
Najväčší spojitý komponent
Výstupom z prahovania je maska obsahujúca nuly a jednotky, na pozíciách, kde bola zistená vhodná farba. Algoritmus nájdenia najväčšieho spojitého komponentu prehľadáva pôvodnú masku, v prípade nájdenia hodnoty jedna spustí prehľadávanie do šírky, pričom každý susedný pixel (v každom z 8 smerov) označí hodnotou N. N začína na hodnote 2 a je zvyšovaný po dokončení označenia komponentu, tento číselný údaj je vpisovaný do vstupnej masky. Tento komponent sa hľadá metódou int flood(int px, int py, int[] in, int n)
, ktorá dostáva súradnice prvého bodu komponentu, masku a číselné označenie komponentu, označí komponent a spočíta počet pixelov v tomto komponente, ktorý následne vráti.
Každý komponent je zapísaný do HashMap
, kde kľúč je číslo komponentu a hodnota je počet pixelov v komponente. Po nájdení všetkých komponentov sa určí najväčší z nich a maska sa opäť preznačí na hodnoty 0 a jednoa takto:
- Ak pixel má hodnotu čísla najväčšieho komponentu, bude nastavený na hodnotu 1.
- Ak pixel má inú hodnotu, bude nastavený na 0.
Metóda hľadajúca najväčší spojitý komponent má názov najSpoj
.
Určenie stredu a AABB ohraničenia komponentu
Na získanie stredu a AABB ohraničenia komponentu sa používa metóda drawX
. Metóda dostáva masku s určeným najväčším spojitým komponentom, pričom pre každý pixel, ktorého hodnota je rovná 1, je pozícia pixelu x a y pripočítaná k ceľkovému súčtu. Rovnako sú pre každé x a y upravené hodnoty premenných maxX
, minX
, maxY
a minY
. Výsledný stred je vypočítaný ako pomer súčtu x ku počtu pixlov a súctu y k počtu pixlov.
int[] stred = new int[6] long sx = 0, sy = 0 int maxX = 0, maxY = 0, minX = width, minY = height, pocet = 0 for každý x for každý y if pixel x,y patrí komponentu sx += x, sy += y, pocet++ maxX = max(maxX, x) maxY = max(maxY, y) minX = min(minX, x) minY = min(minY, y) if pocet je 0 return stred stred[0] = round(sx / pocet) stred[1] = round(sy / pocet) stred[2] = minX stred[3] = minY stred[4] = maxX stred[5] = maxY return stred