5.11. Razlikovanje sličica¶
Razlikovanje sličica uspoređuje svaku novu sličicu s pohranjenom referentnom sličicom kako bi pronašlo dijelove scene koji su se promijenili. To je radni konj aplikacija za kamere koje motre da se nešto dogodi – snimanje pokrenuto pokretom, upozorenja o upadu, „spremi videozapis kada se nešto pomakne” – a u potpunosti je izgrađeno od ranije obrađenih operacija po pikselima: apsolutne razlike, praga i pretrage područja, koje se izvode na svakoj sličici.
5.11.1. Osnovni cjevovod¶
Prva faza je dohvaćanje reference. U nekom trenutku blizu pokretanja – idealno kada je scena u stanju koje znači „nema promjene” – aplikacija snima sličicu i zadržava je. Sličica postaje osnova s kojom će se uspoređivati svaka sljedeća snimka.
reference = csi0.snapshot().copy()
.copy() je važan. Sam csi0.snapshot() vraća Image čiji međuspremnik živi u međuspremniku slike, gdje će ga sljedeći poziv funkcije snapshot prebrisati. .copy() dodjeljuje zaseban međuspremnik za referencu i omogućuje da pikseli ove sličice prežive sljedeću snimku.
Druga faza izvodi se na svakoj sličici: snimi se nova slika, a zatim se izračuna apsolutna razlika između nje i reference. To je upravo ono što radi difference():
current = csi0.snapshot()
current.difference(reference)
Nakon ovog poziva, current sadrži sliku čiji pikseli različiti od nule označavaju svaki položaj na kojem se scena promijenila otkad je referenca uzeta, pri čemu je veličina svakog piksela proporcionalna tome koliko se promijenio na tom položaju.
Treća faza primjenjuje prag na sliku razlike. Sirova razlika uvijek sadrži nešto šuma: male varijacije svjetline od šuma fotona senzora, promjene gradijenta od pomicanja osvjetljenja, podpikselno podrhtavanje od laganog pomicanja kamere. Prolaz praga – binary() s pragom postavljenim iznad te razine šuma – zadržava samo promjene dovoljno velike da se računaju kao stvarni pokret i odbacuje ostatak, stvarajući binarnu sliku čiji su pikseli različiti od nule položaji koji su se stvarno promijenili.
Četvrta faza izdvaja povezana područja te binarne maske – skupine susjednih piksela različitih od nule koji tvore neprekinute mrlje. find_blobs() to obavlja u jednom pozivu, vraćajući popis područja pokreta, svako s graničnim okvirom i brojem piksela, na koji ostatak aplikacije može reagirati.
Cjevovod za razlikovanje sličica: referentna sličica plus trenutna sličica postaju slika razlike; primjena praga pretvara razliku u binarnu masku promijenjenih položaja; korak povezanih područja pretvara masku u popis područja pokreta.¶
5.11.2. Reference u memoriji i na disku¶
Osnovni cjevovod čuva referentnu sličicu u RAM-u. To je pravi odgovor kada se referenca snima u ovom pokretanju skripte i mora preživjeti samo onoliko dugo koliko se skripta nastavlja izvoditi.
Za dugotrajnu aplikaciju – kameru koja bi trebala nastaviti otkrivanje promjena nakon ciklusa napajanja, povremenu skriptu koja mora otkriti bilo kakvu promjenu od nekog ranijeg trenutka – referentna sličica mora nadživjeti pokrenutu skriptu. Obrazac je spremiti referencu na disk:
csi0.snapshot().save("/sdcard/reference.bmp")
i učitati je natrag na početku svakog pokretanja:
reference = image.Image("/sdcard/reference.bmp")
Logika razlikovanja se ne mijenja; mijenja se samo gdje referenca živi između snimki. Nekoliko poboljšanja prirodno proširuje ovu varijantu na disku – automatsko ponovno snimanje reference na mjeraču vremena, neobavezni klizni prosjeci za praćenje sporog pomicanja osvjetljenja – ali zamjena u središtu ostaje ista.
5.11.3. Izoliranje izvora svjetlosti¶
Isti obrazac oduzimanja pojavljuje se u malo drukčijem okruženju: izoliranju izvora svjetlosti od ostatka scene. Trik je snimiti referencu „ugašenih svjetala” – sličicu uzetu kada ono što se otkriva (IR svjetionik, piksel zaslona, pokazivač stanja) nije osvijetljeno – i oduzeti tu referencu od svake sljedeće sličice. Rezultat ima nultu svjetlinu posvuda gdje je scena bila ista u obje snimke, a svjetlinu različitu od nule samo tamo gdje se izvor svjetlosti zaista upalio.
5.11.4. Odabir između difference i sub¶
Praktična napomena o tome koju aritmetičku operaciju odabrati. difference() vraća apsolutnu vrijednost promjene – bez predznaka – što je čini osjetljivom na promjenu u oba smjera (posvjetljivanje ili potamnjivanje) po cijenu toga da aplikaciji ne govori u kojem je smjeru promjena išla. Za čistu detekciju pokreta to je pravi odgovor: sve što se pomaknulo je zanimljivo, bez obzira na to u kojem se smjeru svjetlina pomaknula.
Za detekciju izvora svjetlosti, osvijetljeni piksel je uvijek svjetliji od reference s ugašenim svjetlima, pa je sub() (s ograničavanjem na nuli) iskreniji izbor. Svugdje gdje je trenutna sličica tamnija od reference (što bi bio šum senzora oko neosvijetljene vrijednosti) ograničava se na nulu umjesto da prijavljuje lažni signal „svjetlo je bilo upaljeno”.