5.32. Mentés és tömörítés

Eddig minden oldal a kamerán lévő képekkel dolgozott: a képkocka-pufferbe rögzítve vagy a MicroPython kupacon lefoglalva, az image modul metódusain keresztül feldolgozva, majd vagy az IDE előnézetében megjelenítve, vagy ugyanazon szkripten belül egy következő feldolgozási lépésnek átadva. A legtöbb alkalmazásnak egy ponton az ellenkezőjére van szüksége: fogni egy képet, amely jelenleg a RAM-ban van, és valahol tartósan elhelyezni – az SD-kártyára, egy USB-gazdagépre, hálózaton keresztül – ahol a kamerán kívül más is olvashatja.

Az image modul két utat kínál erre a munkára. A mentés út a képet egy fájlba írja a fájlrendszerre, ahol a fájlformátumot a kiterjesztés választja ki, a kódolás részleteit pedig a metódus kezeli. A formátumba alakítás út egy Image objektumot ad vissza, amely a kódolt bájtfolyamot tartalmazza, alkalmasan arra, hogy egy streamelő vagy hálózati hívásnak átadjuk anélkül, hogy valaha is hozzányúlnánk a fájlrendszerhez. Mindegyik más alkalmazáshoz illik; mindkettő ugyanarra a mögöttes tömörítőmotorra épül.

5.32.1. Mentés fájlba

A save() a képet a fájlrendszerre írja egy megadott elérési útra:

img.save("/sdcard/capture.jpg")
img.save("/sdcard/capture.bmp")
img.save("/sdcard/region.jpg", roi=(40, 60, 200, 150), quality=85)

A formátumot a fájl kiterjesztése határozza meg. Öt kiterjesztés ismert fel: a .bmp egy Windows bitmap-et ír (veszteségmentes, tömörítés nélküli, a rögzített képpontok bájtról bájtra); a .pgm egy portable graymap-et ír (veszteségmentes, csak szürkeárnyalatos); a .ppm egy portable pixmap-et ír (veszteségmentes, RGB); a .jpg és a .jpeg egyaránt JPEG-et ír (veszteséges, tömörített). A fogadó képnek már a kiválasztott konténerhez megfelelő színformátumban kell lennie – egy színes kép .pgm-ként való mentése hiba.

A roi a mentést a kép egy alterületére korlátozza, ugyanúgy, ahogy minden más image modul metódus roi kulcsszava teszi. Az alapértelmezett a teljes kép. A kulcsszót figyelmen kívül hagyja a rendszer JPEG-tömörített kép mentésekor, mivel a lemezen tárolt forma már lefedi a teljes képkockát, és egy körülvágáson keresztüli újrakódolás meghiúsítaná a meglévő tömörített bájtok mentésének értelmét.

A quality a JPEG-tömörítés minősége 0-tól 100-ig, és csak akkor van jelentősége, ha a kimenet JPEG (a kulcsszót a veszteségmentes formátumoknál figyelmen kívül hagyja a rendszer). Az alapértelmezett 50 a megfelelő egyensúly a legtöbb alkalmazáshoz; a 70-től 85-ig terjedő sáv a jobb vizuális minőséget adja, a 30-tól 50-ig terjedő tartomány a kis bélyegképekhez és a sávszélesség-korlátozott átvitelhez ideális, a 90 és afölötti pedig azokra az esetekre van fenntartva, amikor a képet kézzel vizsgálják meg, vagy egy olyan későbbi algoritmuson futtatják át, amely érzékeny a tömörítési műtermékekre.

A fogadó kép visszaadásra kerül, így a hívás láncolható: img.save("/sdcard/x.jpg").draw_string(0, 0, "saved"). A visszaadott objektum ugyanaz a memóriában lévő kép; a mentés egy mellékhatás.

Egy tipikus felhasználás a rögzítés és naplózás minta. Egy esemény bekövetkezik (egy folt észlelődik, egy gombot megnyomnak, egy időzítő lejár); a szkript rögzít egy képkockát; egy időbélyeget fűz a fájlnévhez; és meghívja a save() metódust, hogy a képet az SD-kártyára küldje. Az IDE előnézete tovább fut, a következő esemény bekövetkezik, és a mentett fájlok felhalmozódnak.

5.32.2. Kódolás memóriába

Amikor a cél nem a fájlrendszer, hanem egy hálózati kapcsolat, egy soros port vagy egy másik modul bemenete, az alkalmazásnak a kódolt bájtfolyamra a memóriában van szüksége, nem a lemezen. A to_jpeg() és a to_png() pontosan ezt állítja elő:

encoded = img.to_jpeg(quality=80, copy=True)
bytes_to_send = encoded.bytearray()
sock.send(bytes_to_send)

Az alapértelmezett viselkedés a helyben történő átalakítás: a fogadó kép JPEG (vagy PNG) képpé alakul, és ugyanaz az objektum kerül visszaadásra. A copy=True esetén az átalakítás egy frissen lefoglalt kupacobjektumba ír; a copy_to_fb=True esetén a kimenet a képkocka-pufferbe kerül. A választás ugyanaz, mint amit bármely más átalakító metódus kínál – alapértelmezetten helyben, másolat akkor, ha az eredetire utólag szükség van.

A quality és a subsampling ugyanazok a JPEG-hangolási beállítások, amelyeket a mentés út is kínál. A subsampling a króma-alulmintavételezési sémát választja ki: az image.JPEG_SUBSAMPLING_AUTO a kiválasztott minőséghez legjobbat választja, az image.JPEG_SUBSAMPLING_444 a krómát teljes felbontáson tartja (legnagyobb fájl, legjobb színpontosság), az image.JPEG_SUBSAMPLING_422 és az image.JPEG_SUBSAMPLING_420 a króma felbontását egy vagy mindkét tengely mentén felezi (kisebb fájlok, enyhe színlágyulás, amely tipikus nézési távolságból láthatatlan). Az alapértelmezett AUTO a helyes választás, hacsak az alkalmazásnak nincs egy konkrét igénye.

A PNG a to_png() metóduson keresztül veszteségmentes, de lassabban kódolható, és fényképszerű tartalom esetén nagyobb fájlokat hoz létre, mint a JPEG (a fényképszerű tartalom rosszul tömörül a PNG előrejelzési sémájával). Akkor használj PNG-t, ha a kép vonalas grafika, képernyőkép, vagy egy rögzített képkockára rajzolt éles szélű grafikákat tartalmaz – a veszteségmentes kódolás megőrzi azokat az éles éleket, amelyeket a JPEG ellágyítana. Egyébként a JPEG a helyes alapértelmezés.

Mind a to_jpeg(), mind a to_png() ugyanazokat a rajzolási stílusú pozicionális és skálázási kulcsszavakat fogadja el, amelyeket más átalakító metódusok vesznek át – x_scale, y_scale, roi, rgb_channel, alpha, color_palette, alpha_palette, hint – így ugyanaz a hívás a forrás skálázott, körülvágott vagy palettára leképezett változatát is kódolhatja egyetlen lépésben. A compress() a to_jpeg() régi elnevezése; a kettő ugyanazokat az argumentumokat veszi át, és ugyanazt az eredményt állítja elő.

5.32.3. Mit ad a tömörítés

A JPEG és a nyers közötti kompromisszum mögötti számokat érdemes egyszer végiggondolni.

Egy 320×240-es RGB565 képkocka 153 600 bájt (egy rögzített képkocka QVGA-ban). Egy 640×480-as képkocka 614 400 bájt; egy 1280×960-as képkocka 2 457 600 bájt. Ezek egyike sem nagy egy asztali vagy telefonos kijelzőhöz képest, de jelentősek egy olyan kamera kontextusában, amelynek összesen néhány MB RAM-ja van, egy SD-kártyája véges írási sávszélességgel, és egy gazdagéplinkje, amely tipikusan USB CDC-n, egy UART-on vagy egy vezeték nélküli modulon keresztül fut mérsékelt sebességgel.

A quality=50 minőségű JPEG egy fényképszerű rögzített képkockát tipikusan 10-szeresére és 20-szorosára tömörít: az a 614 KB-os 640×480-as képkocka egy 30–60 KB-os kódolt bájtfolyammá válik. A quality=85 esetén a tömörítés 5-szörösre és 10-szeresre csökken (60–120 KB ugyanahhoz a képkockához). A quality=10 esetén – műtermékekkel terhelt, de még felismerhető – a tömörítés eléri a 30-szorost és 50-szerest (12–20 KB).

Ezek a számok határozzák meg, hogy mit lehet gyakorlatilag kezdeni a mentett képkockákkal. Egy 10 MB/s-ot tartó SD-kártya út bőven elbír 30 képkockát másodpercenként quality=50 minőségű JPEG-kódolt VGA-tartalomból (kb. 1–2 MB/s); ugyanennek a tartalomnak a tömörítetlen mentése több mint 18 MB/s-ot igényelne, túl azon, amit a kamera fájlrendszer-útja a kártyára tart. Egy USB-gazdagép, amely JPEG-kódolt képkockákat húz CDC-n keresztül 1 MB/s sebességgel, 30–60 KB-os képkockákat kap nagyjából 15–30 képkocka/másodperc sebességgel; ugyanazon a sebességen nyers képkockákat húzva másodpercenként egy-két képkockát kap.

Röviden: a tömörítési metódusok nem csupán kényelmi szolgáltatások a mentéshez. Ezek teszik a rögzített képkockát a kamerán kívül használhatóvá olyan képkockasebességeken, amelyek az alkalmazás számára számítanak. A megfelelő tömörítés kiválasztása – JPEG 50-es minőség az általános naplózáshoz, 80-as a minőségi munkához, PNG a vonalas grafika rögzítéséhez – része minden nem triviális kameraalkalmazás rutinmunkájának.