4.16. Bildförhandsvisning

Bildbuffertpoolen är där applikationen läser sina bildrutor. Medan applikationen arbetar på dessa bildrutor behöver det som är anslutet till kameran för att förhandsvisa dem också en kopia av varje bildruta. Kameran har en andra, dedikerad buffert för det ändamålet, och en enda regel för när den fylls: varje gång applikationen anropar snapshot(), kopieras den föregående fångade bildrutan in i förhandsvisningsbufferten innan den nya bildrutan lämnas tillbaka.

Applikationen och förhandsvisaren konkurrerar aldrig om samma minne. Applikationen läser sin bildruta ur poolen; förhandsvisaren läser sin bildruta ur förhandsvisningsbufferten. Båda sker parallellt.

4.16.1. Ström-bildbufferten

Förhandsvisningsbufferten – ström-bildbufferten – är ett enda RAM-område med fast storlek som är skilt från bildbuffertpoolen. Dess storlek sätts vid byggtiden för den fasta programvaran och ändras inte med framesize() eller pixformat(). Ungefär en megabyte är typiskt på nyare OpenMV Cams – stort nog att rymma en förhandsvisning med måttlig upplösning, mycket mindre än en bildruta i full upplösning vid de största sensorstorlekarna.

Applikationskoden läser eller skriver inte denna buffert direkt; kameradrivrutinen fyller den som en sidoeffekt av snapshot().

4.16.2. Vad snapshot gör för förhandsvisningen

Vid varje anrop till snapshot(), innan drivrutinen frigör applikationens föregående bildbuffert tillbaka in i poolen och lämnar ut den nya, kopierar den den föregående bildrutan vidare till förhandsvisningsbufferten – med allt som applikationen ritade ovanpå den under bearbetningen fortfarande kvar på bilden. Två grenar är möjliga. Vilken som körs väljs av förhandsvisaren, inte av kameran: konsumenten som öppnade förhandsvisningen talar om för drivrutinen om den vill ha råbilden eller en JPEG, och vilken råfönsterstorlek den kan acceptera.

  • Nedskalad råkopia. När förhandsvisaren har bett om råbildrutor kopierar drivrutinen den föregående bildrutan i dess ursprungliga pixelformat (RGB565, gråskala osv.). Om bildrutan är större än det råfönster förhandsvisaren begärde skalar drivrutinen ned den med bilinjär filtrering tills den passar; annars går pixlarna igenom oförändrade. Inga komprimeringsartefakter; förhandsvisaren ser samma pixlar som applikationen arbetade på.

  • JPEG-komprimering. När förhandsvisaren har bett om JPEG – eller när råkopian inte alls skulle få plats i strömbufferten – JPEG-komprimerar drivrutinen den föregående bildrutan i full upplösning in i strömbufferten. Kvaliteten justeras adaptivt per bildruta så att den komprimerade utdatan håller sig inom strömbuffertens kapacitet. När en bildruta får plats kryper drivrutinen upp kvaliteten ett steg mot ett tak som beror på den fångade bildrutans pixelstorlek (mindre bildrutor tillåts högre kvalitet; större bildrutor begränsas lägre så att de inte kan svämma över vid en liten innehållsförändring). När en bildruta inte får plats halverar drivrutinen den nuvarande kvaliteten, håller den på den sänkta nivån under de följande flera dussin bildrutorna så att den nya inställningen får tid att sätta sig, och kastar den överflödande bildrutan från förhandsvisningen. Applikationsloopen fortsätter att köra opåverkad; endast förhandsvisaren missar den kastade bildrutan.

Bildrutor som kameran producerar i ett format som redan är komprimerat (JPEG-pixelformatet på sensorer som sänder ut JPEG direkt) hoppar över båda grenarna: den kodade bitströmmen kopieras rakt in i förhandsvisningsbufferten som den är.

Förhandsvisaren pollar enligt sitt eget schema, i allmänhet mycket långsammare än kameran fångar, så den delsamplar råfångsthastigheten: endast de stillbilder den råkar hinna läsa ut i tid visas. Om en ny snapshot() anländer till förhandsvisningsbufferten innan förhandsvisaren har läst ut den föregående bildrutan, är bufferten fortfarande låst av förhandsvisaren och den nya förhandsvisningsuppdateringen hoppas över – den fångsten går förlorad från förhandsvisningsströmmen. Applikationens egen bildbuffertpool är opåverkad; den fångade bildrutan går fortfarande till applikationen som vanligt.

4.16.3. Skicka den sista bildrutan manuellt

Eftersom förhandsvisningen uppdateras som en sidoeffekt av snapshot(), lämnar ett skript som avslutas utan att någonsin anropa snapshot igen det som det senast skickade till förhandsvisningen liggande på förhandsvisaren i oändlighet – vilket, för ett skript som gör sitt arbete före den första stillbilden och sedan avslutas, är en tom förhandsvisning. image.Image.flush() (eller motsvarande flush()CSI-objektet) kopierar det aktuella innehållet i applikationens bildbuffert in i strömbufferten på begäran, utan att fånga en ny bildruta:

img = csi0.snapshot()
# process the image and draw on it
img.flush()                               # previewer sees the annotated frame

Samma anrop är också användbart när en långkörande operation ligger mellan stillbilder och förhandsvisaren annars skulle visa en föråldrad förhandsvisning hela tiden.

Anteckning

Förhandsvisningsapplikationen måste läsa ut bildrutan ur strömbufferten innan skriptet avslutas. En flush i slutet av ett kort skript iordningställer bara bildrutan; om skriptet sedan lämnar tillbaka kontrollen till kameran innan förhandsvisaren har pollat, återanvänds bufferten vid nästa körning och den sista bildrutan går förlorad. För förhandsvisningar i slutet av skript, ge förhandsvisaren ett ögonblick att hämta upp bildrutan (en kort sleep efter flush, eller helt enkelt att inte avsluta omedelbart) innan skriptet slutar.