5.32. Salvarea și compresia¶
Every page until now has worked with images on the cam: captured into the frame buffer or allocated on the MicroPython heap, manipulated through the image-module methods, and either displayed in the IDE preview or fed into a downstream stage in the same script. Most applications need at some point to do the opposite: take an image that is currently in RAM and put it somewhere persistent – onto the SD card, onto a USB host, over a network – where something other than the camera can read it.
The image module exposes two paths for
that work. The save path writes the
image to a file on the filesystem, with
the file format chosen by the extension
and the encoding details handled by the
method. The to-format path returns a
Image object containing the
encoded byte stream, suitable for handing
to a streaming or networking call without
ever touching the filesystem. Each fits a
different application; both build on the
same compression engine underneath.
5.32.1. Salvarea într-un fișier¶
save() scrie imaginea pe sistemul de fișiere la o cale:
img.save("/sdcard/capture.jpg")
img.save("/sdcard/capture.bmp")
img.save("/sdcard/region.jpg", roi=(40, 60, 200, 150), quality=85)
Formatul este ales din extensia fișierului. Sunt recunoscute cinci extensii: .bmp scrie un Windows bitmap (fără pierderi, fără compresie, pixelii capturați octet cu octet); .pgm scrie un portable graymap (fără pierderi, doar tonuri de gri); .ppm scrie un portable pixmap (fără pierderi, RGB); .jpg și .jpeg scriu ambele un JPEG (cu pierderi, comprimat). Imaginea destinatar trebuie să fie deja în formatul de culoare corect pentru containerul ales – o imagine color salvată ca .pgm este o eroare.
roi restricts the save to a
sub-rectangle of the image, the way every
other image-module method’s roi
keyword does. The full image is the
default. The keyword is ignored when
saving a JPEG-compressed image because
the on-disk form already covers the full
frame and re-encoding through a crop
would defeat the point of saving the
existing compressed bytes.
quality este calitatea compresiei JPEG, de la 0 la 100, și are sens doar când ieșirea este JPEG (cuvântul-cheie este ignorat pentru formatele fără pierderi). Valoarea implicită de 50 este echilibrul potrivit pentru majoritatea aplicațiilor; 70 până la 85 este intervalul pentru o calitate vizuală mai ridicată, 30 până la 50 este intervalul potrivit pentru miniaturi mici și transmisii limitate de lățime de bandă, iar 90 și peste este rezervat cazurilor în care imaginea va fi inspectată manual sau prelucrată printr-un algoritm ulterior sensibil la artefactele de compresie.
Imaginea destinatar este returnată astfel încât apelul să poată fi înlănțuit: img.save("/sdcard/x.jpg").draw_string(0, 0, "saved"). Obiectul returnat este aceeași imagine din memorie; salvarea este un efect secundar.
O utilizare tipică este tiparul capture-and-log. Un declanșator se activează (este detectat un blob, este apăsat un buton, expiră un temporizator); scriptul capturează un cadru; adaugă un marcaj temporal la numele fișierului; și apelează save() pentru a împinge imaginea pe cardul SD. Previzualizarea din IDE continuă să ruleze, următorul declanșator se activează, iar fișierele salvate se acumulează.
5.32.2. Codificarea în memorie¶
Când destinația nu este sistemul de fișiere, ci o conexiune de rețea, un port serial sau intrarea altui modul, aplicația are nevoie de fluxul de octeți codificat în memorie, nu pe disc. to_jpeg() și to_png() produc exact acest lucru:
encoded = img.to_jpeg(quality=80, copy=True)
bytes_to_send = encoded.bytearray()
sock.send(bytes_to_send)
Comportamentul implicit este conversia pe loc: destinatarul este convertit într-o imagine JPEG (sau PNG) și se returnează același obiect. Cu copy=True conversia scrie într-un obiect proaspăt alocat pe heap; cu copy_to_fb=True ieșirea ajunge în tamponul de cadre (frame buffer). Alegerea este aceeași pe care o oferă orice altă metodă de conversie – pe loc în mod implicit, copiere atunci când originalul este necesar ulterior.
quality și subsampling sunt aceleași butoane de reglare JPEG pe care le expune calea de salvare. subsampling alege schema de subeșantionare a crominanței: image.JPEG_SUBSAMPLING_AUTO o alege pe cea mai bună pentru calitatea aleasă, image.JPEG_SUBSAMPLING_444 păstrează crominanța la rezoluție completă (cel mai mare fișier, cea mai bună acuratețe a culorilor), image.JPEG_SUBSAMPLING_422 și image.JPEG_SUBSAMPLING_420 înjumătățesc rezoluția crominanței pe una sau ambele axe (fișiere mai mici, o ușoară atenuare a culorilor care este invizibilă la distanțele tipice de vizualizare). Valoarea implicită AUTO este alegerea potrivită, cu excepția cazului în care aplicația are o nevoie specifică.
PNG prin to_png() este fără pierderi, dar mai lent de codificat și produce fișiere mai mari decât JPEG pentru conținut fotografic (conținutul fotografic se comprimă slab sub schema de predicție a PNG). Folosiți PNG când imaginea este grafică liniară, o captură de ecran sau conține elemente grafice cu margini dure desenate peste un cadru capturat – codificarea fără pierderi păstrează muchiile clare pe care JPEG le-ar atenua. În rest, JPEG este valoarea implicită potrivită.
Atât to_jpeg(), cât și to_png() acceptă aceleași cuvinte-cheie poziționale și de scalare în stil de desenare pe care le iau celelalte metode de conversie – x_scale, y_scale, roi, rgb_channel, alpha, color_palette, alpha_palette, hint – astfel încât același apel poate codifica o versiune scalată, decupată sau mapată cu paletă a sursei într-un singur pas. compress() este denumirea moștenită a to_jpeg(); cele două iau aceleași argumente și produc același rezultat.
5.32.3. Ce aduce compresia¶
Cifrele din spatele compromisului JPEG-versus-brut merită parcurse o dată.
Un cadru RGB565 de 320 pe 240 are 153.600 de octeți (un cadru capturat la QVGA). Un cadru de 640 pe 480 are 614.400 de octeți; un cadru de 1280 pe 960 are 2.457.600 de octeți. Niciunul dintre acestea nu este mare în comparație cu un ecran de desktop sau de telefon, dar sunt considerabile în contextul unei camere care are în total câțiva MB de RAM, un card SD cu o lățime de bandă finită de scriere și o legătură cu gazda care de obicei rulează prin USB CDC, un UART sau un modul wireless la viteze modeste.
JPEG la quality=50 comprimă de obicei un cadru fotografic capturat de 10x până la 20x: acel cadru de 640 pe 480 de 614 KB devine un flux de octeți codificat de 30 până la 60 KB. La quality=85 compresia scade la 5x până la 10x (60 până la 120 KB pentru același cadru). La quality=10 – plin de artefacte, dar încă recognoscibil – compresia ajunge la 30x până la 50x (12 până la 20 KB).
Aceste cifre determină ce este practic de făcut cu cadrele salvate. O cale către un card SD care susține 10 MB/s gestionează 30 de cadre pe secundă de conținut VGA codificat JPEG la quality=50 cu loc de rezervă (aproximativ 1 până la 2 MB/s); salvarea aceluiași conținut necomprimat necesită peste 18 MB/s, dincolo de ceea ce susține calea sistemului de fișiere al camerei către card. O gazdă USB care extrage cadre codificate JPEG prin CDC la 1 MB/s primește cadre de 30 până la 60 KB la aproximativ 15 până la 30 de cadre pe secundă; extrăgând cadre brute la aceeași rată, obține unul sau două cadre pe secundă.
Pe scurt: metodele de compresie nu sunt doar o comoditate pentru salvare. Ele sunt cele care fac cadrul capturat utilizabil în afara camerei la ratele de cadre care contează pentru aplicație. Alegerea compresiei potrivite – JPEG quality 50 pentru jurnalizare generală, 80 pentru lucru de calitate, PNG pentru capturarea graficii liniare – face parte din munca de rutină a oricărei aplicații de cameră non-trivială.