4.16. Попередній перегляд зображення

Пул кадрових буферів – це місце, звідки програма зчитує свої кадри. Поки програма працює з цими кадрами, усьому, що підключене до камери для їх попереднього перегляду, також потрібна копія кожного кадру. Для цього камера має окремий буфер, і одне просте правило для його заповнення: щоразу, коли програма викликає snapshot(), попередній захоплений кадр копіюється до буфера попереднього перегляду перед тим, як повертається новий кадр.

Програма та система попереднього перегляду ніколи не змагаються за одну й ту саму пам’ять. Програма зчитує свій кадр із пулу; система попереднього перегляду зчитує свій кадр із буфера попереднього перегляду. Обидві операції відбуваються паралельно.

4.16.1. Потоковий кадровий буфер

Буфер попереднього перегляду – потоковий кадровий буфер – це єдина фіксована ділянка оперативної пам’яті, відокремлена від пулу кадрових буферів. Її розмір задається під час збірки мікропрограми і не змінюється викликами framesize() або pixformat(). На нещодавніх OpenMV Cam типовий розмір – близько мегабайта: достатньо для зберігання попереднього перегляду помірної роздільної здатності, але значно менше, ніж повнорозмірний кадр при найбільших розмірах датчика.

Код програми не читає і не записує цей буфер безпосередньо; драйвер камери заповнює його як побічний ефект snapshot().

4.16.2. Що робить snapshot для попереднього перегляду

При кожному виклику snapshot() – до того, як драйвер повертає попередній кадровий буфер програми назад до пулу і видає новий – він копіює попередній кадр до буфера попереднього перегляду разом із усім, що програма намалювала поверх нього під час обробки. Можливі два варіанти. Який із них виконується, визначає система попереднього перегляду, а не камера: споживач, що відкрив попередній перегляд, повідомляє драйверу, чи потребує він сирого зображення чи JPEG, і який максимальний розмір вікна для сирих даних він може прийняти.

  • Необроблена зменшена копія. Коли система попереднього перегляду запросила сирі кадри, драйвер копіює попередній кадр у його рідному форматі пікселів (RGB565, відтінки сірого тощо). Якщо кадр більший за вікно для сирих даних, запрошене системою попереднього перегляду, драйвер масштабує його за допомогою білінійної фільтрації до потрібного розміру; інакше пікселі передаються без змін. Без артефактів стиснення; система попереднього перегляду бачить ті самі пікселі, з якими працювала програма.

  • JPEG-стиснення. Коли система попереднього перегляду запросила JPEG – або коли необроблена копія взагалі не вміщується в потоковий буфер – драйвер JPEG-стискає попередній кадр у повній роздільній здатності до потокового буфера. Якість адаптивно підлаштовується для кожного кадру, щоб стиснений результат не виходив за межі місткості потокового буфера. Коли кадр вміщується, драйвер поступово підвищує якість на один крок до стелі, що залежить від розміру пікселів захопленого кадру (для менших кадрів дозволена вища якість; для більших встановлено нижню межу, щоб вони не переповнились при невеликій зміні вмісту). Коли кадр не вміщується, драйвер вдвічі знижує поточну якість, утримує її на зниженому рівні протягом наступних кількох десятків кадрів, щоб нове налаштування встигло стабілізуватися, та відкидає переповнений кадр із попереднього перегляду. Цикл програми продовжує виконуватися без змін; система попереднього перегляду лише пропускає відкинутий кадр.

Кадри, які камера виробляє у форматі, що вже є стисненим (формат пікселів JPEG на датчиках, що безпосередньо видають JPEG), обминають обидва варіанти: закодований бітовий потік копіюється безпосередньо до буфера попереднього перегляду без змін.

Система попереднього перегляду опитує за власним розкладом, зазвичай значно повільніше, ніж відбувається захоплення камерою, тому вона виконує децимацію сирої частоти захоплення: відображаються лише ті знімки, які вона встигає зчитати вчасно. Якщо новий snapshot() надходить до буфера попереднього перегляду до того, як система попереднього перегляду зчитала попередній кадр, буфер ще заблокований системою перегляду і нове оновлення попереднього перегляду пропускається – цей знімок втрачається з потоку попереднього перегляду. Власний пул кадрових буферів програми не зачіпається; захоплений кадр як зазвичай надходить до програми.

4.16.3. Ручна відправка останнього кадру

Оскільки попередній перегляд оновлюється як побічний ефект snapshot(), скрипт, що завершує роботу без повторного виклику snapshot, залишає у системі попереднього перегляду останнє надіслане зображення на невизначений термін – а для скрипта, що виконує роботу до першого знімка і потім завершується, це означає порожній попередній перегляд. image.Image.flush() (або еквівалентний flush() на об’єкті CSI) копіює поточний вміст кадрового буфера програми до потокового буфера на вимогу, не захоплюючи новий кадр:

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

Той самий виклик також корисний, коли тривала операція розміщена між знімками і система попереднього перегляду інакше показувала б застарілий попередній перегляд увесь цей час.

Примітка

Програма попереднього перегляду повинна зчитати кадр із потокового буфера до завершення скрипта. Flush наприкінці короткого скрипта лише ставить кадр у чергу; якщо скрипт після цього повертає управління камері до того, як система попереднього перегляду встигла зробити опитування, буфер буде повторно використаний при наступному запуску і цей останній кадр буде втрачений. Для попереднього перегляду наприкінці скрипта дайте системі попереднього перегляду момент для зчитування кадру (коротка затримка після flush або просто не завершуйте скрипт одразу) перед тим, як скрипт завершиться.