5.32. Збереження та стиснення¶
На всіх попередніх сторінках ми працювали із зображеннями безпосередньо на камері: захоплювали їх у кадровий буфер або розміщували в купі MicroPython, обробляли методами модуля image та або відображали у попередньому перегляді IDE, або передавали на наступний етап того самого скрипту. Більшість застосунків рано чи пізно потребують зворотного: взяти зображення, яке зараз перебуває в оперативній пам’яті, та зберегти його десь постійно – на SD-карту, на USB-хост, через мережу – туди, де його зможе прочитати щось відмінне від камери.
Модуль image надає два шляхи для цього. Шлях save записує зображення у файл на файловій системі; формат файлу визначається розширенням, а деталі кодування обробляє метод. Шлях to-format повертає об’єкт Image, що містить закодований потік байтів, придатний для передачі у виклики потокової передачі або роботи з мережею, не торкаючись файлової системи. Кожен підхід відповідає різним застосункам, але обидва спираються на один і той самий рушій стиснення.
5.32.1. Збереження у файл¶
save() записує зображення на файлову систему за вказаним шляхом:
img.save("/sdcard/capture.jpg")
img.save("/sdcard/capture.bmp")
img.save("/sdcard/region.jpg", roi=(40, 60, 200, 150), quality=85)
Формат визначається розширенням файлу. Розпізнаються п’ять розширень: .bmp записує Windows bitmap (без втрат, без стиснення, побайтово зберігає захоплені пікселі); .pgm записує portable graymap (без втрат, лише відтінки сірого); .ppm записує portable pixmap (без втрат, RGB); .jpg і .jpeg записують JPEG (з втратами, стиснений формат). Вихідне зображення вже повинно бути у правильному кольоровому форматі для обраного контейнера – збереження кольорового зображення як .pgm є помилкою.
roi обмежує збереження підпрямокутником зображення, як і ключове слово roi будь-якого іншого методу модуля image. За замовчуванням зберігається повне зображення. Ключове слово ігнорується під час збереження JPEG-стисненого зображення, оскільки форма на диску вже охоплює повний кадр, а повторне кодування через кадрування зводить нанівець сенс збереження наявних стиснених байтів.
quality – це якість стиснення JPEG від 0 до 100, яка має значення лише тоді, коли вихідний формат – JPEG (для форматів без втрат ключове слово ігнорується). Стандартне значення 50 є оптимальним балансом для більшості застосунків; діапазон 70–85 підходить для вищої візуальної якості, 30–50 – для невеликих мініатюр та передачі з обмеженою пропускною здатністю, а значення 90 і вище залишається для випадків, коли зображення будуть переглядатись вручну або оброблятись алгоритмами, чутливими до артефактів стиснення.
Вихідне зображення повертається, тому виклики можна об’єднувати в ланцюжок: img.save("/sdcard/x.jpg").draw_string(0, 0, "saved"). Повернутий об’єкт – це те саме зображення в пам’яті; збереження є побічним ефектом.
Типове використання – патерн захоплення та журналювання. Спрацьовує тригер (виявлено пляму, натиснуто кнопку, спливає таймер); скрипт захоплює кадр; до імені файлу додається мітка часу; потім викликається save() для запису зображення на SD-карту. Попередній перегляд IDE продовжує працювати, спрацьовує наступний тригер, і збережені файли накопичуються.
5.32.2. Кодування в пам’ять¶
Коли призначенням є не файлова система, а мережеве з’єднання, послідовний порт або вхід іншого модуля, застосунку потрібен закодований потік байтів у пам’яті, а не на диску. to_jpeg() та to_png() повертають саме це:
encoded = img.to_jpeg(quality=80, copy=True)
bytes_to_send = encoded.bytearray()
sock.send(bytes_to_send)
Стандартна поведінка – конвертація на місці: вихідне зображення перетворюється на JPEG (або PNG) і повертається той самий об’єкт. При copy=True результат записується у щойно виділений об’єкт у купі; при copy_to_fb=True вихідні дані розміщуються у кадровому буфері. Вибір такий самий, як і в будь-якому іншому методі конвертації – за замовчуванням на місці, з копіюванням, якщо оригінал знадобиться пізніше.
quality та subsampling – ті самі параметри налаштування JPEG, що й у методі збереження. subsampling визначає схему субдискретизації кольоровості: image.JPEG_SUBSAMPLING_AUTO обирає найкращу для заданої якості, image.JPEG_SUBSAMPLING_444 зберігає кольоровість у повній роздільній здатності (найбільший файл, найкраща точність кольору), image.JPEG_SUBSAMPLING_422 та image.JPEG_SUBSAMPLING_420 зменшують роздільну здатність кольоровості вдвічі по одній або обох осях (менші файли, незначне розм’якшення кольору, непомітне на типових відстанях перегляду). Стандартне значення AUTO є правильним вибором, якщо у застосунку немає конкретної потреби.
PNG через to_png() є форматом без втрат, але кодується повільніше та дає більші файли, ніж JPEG, для фотографічного вмісту (фотографічний вміст погано стискається за схемою передбачення PNG). Використовуйте PNG, коли зображення є штриховою графікою, знімком екрана або містить чіткі геометричні елементи, накладені на захоплений кадр – кодування без втрат зберігає чіткі межі, які JPEG розмив би. В іншому випадку JPEG є правильним вибором за замовчуванням.
Обидва методи to_jpeg() та to_png() приймають ті самі позиційні аргументи та ключові слова масштабування, що й інші методи конвертації – x_scale, y_scale, roi, rgb_channel, alpha, color_palette, alpha_palette, hint – тому один і той самий виклик може закодувати масштабовану, обрізану або з переведеною палітрою версію джерела за один крок. compress() – це застаріла назва to_jpeg(); обидва приймають однакові аргументи та дають однаковий результат.
5.32.3. Що дає стиснення¶
Числа, що стоять за компромісом між JPEG і необробленими даними, варто розглянути детальніше.
Кадр 320×240 RGB565 займає 153 600 байт (один захоплений кадр при QVGA). Кадр 640×480 – 614 400 байт; кадр 1280×960 – 2 457 600 байт. Жодне з цих значень не є великим порівняно з настільним або мобільним дисплеєм, але вони суттєві в контексті камери, яка має лише кілька МБ оперативної пам’яті, SD-картку з обмеженою пропускною здатністю запису та з’єднання з хостом, яке зазвичай працює через USB CDC, UART або бездротовий модуль на помірних швидкостях.
JPEG при quality=50 зазвичай стискає захоплений фотографічний кадр у 10–20 разів: той кадр 614 КБ 640×480 стає закодованим потоком байтів розміром 30–60 КБ. При quality=85 ступінь стиснення знижується до 5–10 разів (60–120 КБ для того самого кадру). При quality=10 – з помітними артефактами, але все ще розпізнаваний – стиснення досягає 30–50 разів (12–20 КБ).
Ці числа визначають, що практично можна робити із збереженими кадрами. SD-карта із пропускною здатністю 10 МБ/с з легкістю впорається з 30 кадрами на секунду JPEG-кодованого VGA-вмісту при quality=50 (близько 1–2 МБ/с); збереження того самого вмісту без стиснення вимагатиме понад 18 МБ/с, що перевищує можливості файлової системи камери при записі на картку. USB-хост, що отримує JPEG-кодовані кадри через CDC зі швидкістю 1 МБ/с, отримує кадри 30–60 КБ зі швидкістю приблизно 15–30 кадрів на секунду; при отриманні необроблених кадрів з тією ж швидкістю – лише один-два кадри на секунду.
Коротко: методи стиснення – це не просто зручність для збереження. Вони забезпечують практичне використання захопленого кадру поза камерою на частотах кадрів, потрібних застосунку. Вибір правильного стиснення – JPEG якість 50 для загального журналювання, 80 для якісної роботи, PNG для захоплення штрихової графіки – є частиною рутинної роботи будь-якого нетривіального застосунку для камери.