4.16. Kuvan esikatselu¶
Kehyspuskuripooli on paikka, josta sovellus lukee kehyksensä. Sillä aikaa, kun sovellus työstää näitä kehyksiä, mikä tahansa kameraan kytketty laite niiden esikatselua varten tarvitsee myös kopion jokaisesta kehyksestä. Kamerassa on toinen, tähän tarkoitukseen varattu puskuri, ja yksi sääntö siitä, milloin se täytetään: joka kerta kun sovellus kutsuu snapshot()-metodia, edellinen kaapattu kehys kopioidaan esikatselupuskuriin ennen uuden kehyksen luovuttamista takaisin.
Sovellus ja esikatselija eivät koskaan kilpaile samasta muistista. Sovellus lukee kehyksensä poolista; esikatselija lukee kehyksensä esikatselupuskurista. Molemmat tapahtuvat rinnakkain.
4.16.1. Virtakehyspuskuri¶
Esikatselupuskuri – virtakehyspuskuri – on yksittäinen kiinteäkokoinen RAM-alue erillään kehyspuskuripoolista. Sen koko asetetaan laiteohjelmiston käännösvaiheessa eikä se muutu framesize()- tai pixformat()-metodin mukaan. Noin megatavu on tyypillinen uusissa OpenMV Cam -laitteissa – riittävän suuri pitämään kohtalaisen resoluution esikatselun, paljon pienempi kuin täysresoluutioinen kehys suurimmilla sensorikoilla.
Sovelluskoodi ei lue tai kirjoita tätä puskuria suoraan; kameran ajuri täyttää sen snapshot()-metodin sivuvaikutuksena.
4.16.2. Mitä snapshot tekee esikatselua varten¶
Jokaisella snapshot()-kutsulla, ennen kuin ajuri vapauttaa sovelluksen edellisen kehyspuskurin takaisin pooliin ja luovuttaa uuden, se kopioi edellisen kehyksen esikatselupuskuriin – sen kanssa, mitä sovellus piirsi sen päälle käsittelyn aikana, edelleen kuvassa. Kaksi haaraa on mahdollista. Sen, kumpi suoritetaan, valitsee esikatselija, ei kamera: esikatselun avannut kuluttaja kertoo ajurille, haluaako se raakakuvan vai JPEG:in, ja minkä raakaikkunan koon se voi hyväksyä.
Raaka alaskaalattu kopio. Kun esikatselija on pyytänyt raakakehyksiä, ajuri kopioi edellisen kehyksen sen alkuperäisessä pikseliformaatissa (RGB565, harmaasävy jne.). Jos kehys on suurempi kuin esikatselijan pyytämä raakaikkuna, ajuri skaalaa sen alas bilineaarisella suodatuksella, kunnes se mahtuu; muutoin pikselit menevät läpi muuttumattomina. Ei pakkausartefakteja; esikatselija näkee samat pikselit, joita sovellus työsti.
JPEG-pakkaus. Kun esikatselija on pyytänyt JPEG:iä – tai kun raakakopio ei mahtuisi virtapuskuriin lainkaan – ajuri JPEG-pakkaa edellisen kehyksen täydellä resoluutiollaan virtapuskuriin. Laatua säädetään mukautuvasti kehyskohtaisesti, jotta pakattu tuloste pysyy virtapuskurin kapasiteetin sisällä. Kun kehys mahtuu, ajuri nostaa laatua vähitellen yhdellä askeleella kohti kattoa, joka riippuu kaapatun kehyksen pikselikoosta (pienemmille kehyksille sallitaan korkeampi laatu; suuremmat kehykset rajataan matalammalle, jotta ne eivät voi ylivuotaa pienessä sisältömuutoksessa). Kun kehys ei mahdu, ajuri puolittaa nykyisen laadun, pitää sen alennetulla tasolla seuraavien muutaman kymmenen kehyksen ajan, jotta uusi asetus ehtii vakiintua, ja pudottaa ylivuotaneen kehyksen esikatselusta. Sovelluksen silmukka jatkaa toimintaansa siihen vaikuttamatta; vain esikatselija menettää pudotetun kehyksen.
Kehykset, jotka kamera tuottaa jo pakatussa formaatissa (JPEG-pikseliformaatti sensoreilla, jotka lähettävät JPEG:iä suoraan), ohittavat molemmat haarat: koodattu bittivirta kopioidaan suoraan esikatselupuskuriin sellaisenaan.
Esikatselija pollaa omalla aikataulullaan, yleensä paljon hitaammin kuin kamera kaappaa, joten se alinäytteistää raakakaappausnopeuden: vain ne tilannekuvat, jotka se sattuu lukemaan ajoissa, näytetään. Jos tuore snapshot() saapuu esikatselupuskuriin ennen kuin esikatselija on lukenut edellisen kehyksen, puskuri on edelleen esikatselijan lukitsema ja uusi esikatselupäivitys ohitetaan – kyseinen kaappaus menetetään esikatseluvirrasta. Sovelluksen oma kehyspuskuripooli ei vaikutu; kaapattu kehys menee silti sovellukselle normaalisti.
4.16.3. Viimeisen kehyksen työntäminen manuaalisesti¶
Koska esikatselu päivitetään snapshot()-metodin sivuvaikutuksena, skripti, joka päättyy kutsumatta enää koskaan snapshotia, jättää sen, mitä se viimeksi lähetti esikatseluun, makaamaan esikatselijalle loputtomiin – mikä, skriptille joka tekee työnsä ennen ensimmäistä tilannekuvaa ja sitten poistuu, on tyhjä esikatselu. image.Image.flush() (tai vastaava flush() CSI-objektissa) kopioi sovelluksen kehyspuskurin nykyisen sisällön virtapuskuriin pyynnöstä, kaappaamatta uutta kehystä:
img = csi0.snapshot()
# process the image and draw on it
img.flush() # previewer sees the annotated frame
Sama kutsu on hyödyllinen myös silloin, kun pitkäkestoinen operaatio sijaitsee tilannekuvien välissä ja esikatselija muutoin näyttäisi vanhentunutta esikatselua koko sen ajan.
Muista
Esikatselusovelluksen on luettava kehys virtapuskurista ennen kuin skripti poistuu. Tyhjennys lyhyen skriptin lopussa vain valmistelee kehyksen; jos skripti sitten palauttaa ohjauksen kameralle ennen kuin esikatselija on pollannut, puskuri käytetään uudelleen seuraavalla ajolla ja tuo viimeinen kehys menetetään. Skriptin lopun esikatseluja varten anna esikatselijalle hetki poimia kehys (lyhyt sleep tyhjennyksen jälkeen tai yksinkertaisesti olemalla poistumatta välittömästi) ennen kuin skripti päättyy.