4.16. Previzualizarea imaginii¶
Pool-ul de framebuffere este locul de unde aplicația își citește cadrele. În timp ce aplicația lucrează la acele cadre, orice este conectat la cameră pentru a le previzualiza are nevoie și el de o copie a fiecărui cadru. Camera dispune de un al doilea tampon, dedicat acestui scop, și de o singură regulă privind momentul în care este umplut: de fiecare dată când aplicația apelează snapshot(), cadrul capturat anterior este copiat în tamponul de previzualizare înainte ca noul cadru să fie returnat.
Aplicația și componenta de previzualizare nu concurează niciodată pentru aceeași memorie. Aplicația își citește cadrul din pool; componenta de previzualizare își citește cadrul din tamponul de previzualizare. Ambele au loc în paralel.
4.16.1. Framebufferul de flux¶
Tamponul de previzualizare – framebufferul de flux – este o singură regiune de RAM cu dimensiune fixă, separată de pool-ul de framebuffere. Dimensiunea sa este stabilită la momentul construirii firmware-ului și nu se modifică în funcție de framesize() sau pixformat(). Aproximativ un megaoctet este tipic pe modelele recente de OpenMV Cam – suficient de mare pentru a păstra o previzualizare de rezoluție moderată, mult mai mic decât un cadru la rezoluție completă la cele mai mari dimensiuni de senzor.
Codul aplicației nu citește și nu scrie direct în acest tampon; driverul camerei îl umple ca efect secundar al snapshot().
4.16.2. Ce face snapshot pentru previzualizare¶
La fiecare apel la snapshot(), înainte ca driverul să elibereze framebufferul anterior al aplicației înapoi în pool și să-l predea pe cel nou, acesta copiază cadrul anterior în tamponul de previzualizare – cu tot ce a desenat aplicația deasupra lui în timpul procesării încă prezent pe imagine. Sunt posibile două ramuri. Care dintre ele rulează este ales de componenta de previzualizare, nu de cameră: consumatorul care a deschis previzualizarea îi spune driverului dacă dorește imaginea brută sau un JPEG și ce dimensiune a ferestrei brute poate accepta.
Copie brută redusă. Atunci când componenta de previzualizare a cerut cadre brute, driverul copiază cadrul anterior în formatul său nativ de pixeli (RGB565, tonuri de gri etc.). Dacă cadrul este mai mare decât fereastra brută solicitată de componenta de previzualizare, driverul îl reduce prin filtrare biliniară până când se încadrează; altfel, pixelii sunt transmiși nemodificați. Niciun artefact de compresie; componenta de previzualizare vede aceiași pixeli la care lucra aplicația.
Compresie JPEG. Atunci când componenta de previzualizare a cerut JPEG – sau atunci când copia brută nu ar încăpea deloc în tamponul de flux – driverul comprimă în JPEG cadrul anterior la rezoluția sa completă în tamponul de flux. Calitatea este ajustată adaptiv la fiecare cadru, astfel încât ieșirea comprimată să rămână în limitele capacității tamponului de flux. Când un cadru încape, driverul ridică treptat calitatea cu un pas spre un plafon care depinde de dimensiunea în pixeli a cadrului capturat (cadrelor mai mici li se permite o calitate mai ridicată; cadrele mai mari sunt plafonate mai jos pentru a nu putea provoca o depășire la o mică modificare de conținut). Când un cadru nu încape, driverul înjumătățește calitatea curentă, o menține la nivelul redus pentru următoarele câteva zeci de cadre, astfel încât noua setare să aibă timp să se stabilizeze, și elimină cadrul depășit din previzualizare. Bucla aplicației continuă să ruleze neafectată; doar componenta de previzualizare ratează cadrul eliminat.
Cadrele pe care camera le produce într-un format deja comprimat (formatul de pixeli JPEG pe senzorii care emit JPEG direct) ocolesc ambele ramuri: fluxul de biți codificat este copiat direct în tamponul de previzualizare, ca atare.
Componenta de previzualizare interoghează după propriul program, în general mult mai lent decât capturează camera, așa că subeșantionează rata de captură brută: sunt afișate doar instantaneele pe care reușește să le citească la timp. Dacă un nou snapshot() ajunge la tamponul de previzualizare înainte ca componenta de previzualizare să fi citit cadrul anterior, tamponul este încă blocat de componenta de previzualizare, iar noua actualizare a previzualizării este omisă – acea captură se pierde din fluxul de previzualizare. Pool-ul propriu de framebuffere al aplicației nu este afectat; cadrul capturat ajunge totuși la aplicație în mod normal.
4.16.3. Trimiterea manuală a ultimului cadru¶
Deoarece previzualizarea este actualizată ca efect secundar al snapshot(), un script care se încheie fără a mai apela vreodată snapshot lasă pe componenta de previzualizare, la nesfârșit, tot ce a trimis ultima dată – ceea ce, pentru un script care își face treaba înainte de primul snapshot și apoi iese, înseamnă o previzualizare goală. image.Image.flush() (sau echivalentul flush() pe obiectul CSI) copiază la cerere conținutul curent al framebufferului aplicației în tamponul de flux, fără a captura un cadru nou:
img = csi0.snapshot()
# process the image and draw on it
img.flush() # previewer sees the annotated frame
Același apel este util și atunci când o operație de lungă durată se află între instantanee, iar componenta de previzualizare ar afișa altfel o previzualizare învechită tot timpul.
Notă
Aplicația de previzualizare trebuie să citească cadrul din tamponul de flux înainte ca scriptul să iasă. Un flush la finalul unui script scurt doar pregătește cadrul; dacă scriptul returnează apoi controlul către cameră înainte ca componenta de previzualizare să fi interogat, tamponul este reutilizat la următoarea rulare, iar acel cadru final se pierde. Pentru previzualizările de la finalul scriptului, oferiți componentei de previzualizare un moment pentru a prelua cadrul (o scurtă pauză sleep după flush sau pur și simplu evitând ieșirea imediată) înainte ca scriptul să se încheie.