4.16. Pratinjau citra

Pool framebuffer adalah tempat aplikasi membaca bingkainya. Sementara aplikasi bekerja pada bingkai-bingkai tersebut, apa pun yang terhubung ke kamera untuk melakukan pratinjau bingkai tersebut juga membutuhkan salinan setiap bingkai. Kamera memiliki buffer kedua yang khusus untuk tujuan tersebut, dan satu aturan sederhana tentang kapan buffer tersebut diisi: setiap kali aplikasi memanggil snapshot(), bingkai yang sebelumnya di-capture disalin ke buffer pratinjau sebelum bingkai baru dikembalikan.

Aplikasi dan previewer tidak pernah bersaing untuk memori yang sama. Aplikasi membaca bingkainya dari pool; previewer membaca bingkainya dari buffer pratinjau. Keduanya terjadi secara paralel.

4.16.1. Framebuffer stream

Buffer pratinjau -- stream framebuffer -- adalah satu wilayah RAM berukuran tetap yang terpisah dari pool framebuffer. Ukurannya ditetapkan pada waktu build firmware dan tidak berubah dengan framesize() atau pixformat(). Sekitar satu megabyte adalah ukuran yang umum pada OpenMV Cam terbaru -- cukup besar untuk menampung pratinjau resolusi sedang, jauh lebih kecil dari bingkai resolusi penuh pada ukuran sensor terbesar.

Kode aplikasi tidak membaca atau menulis buffer ini secara langsung; driver kamera mengisinya sebagai efek samping dari snapshot().

4.16.2. Apa yang dilakukan snapshot untuk pratinjau

Pada setiap panggilan ke snapshot(), sebelum driver melepaskan framebuffer aplikasi sebelumnya kembali ke pool dan menyerahkan yang baru, driver menyalin bingkai sebelumnya ke buffer pratinjau -- dengan apa pun yang digambar aplikasi di atasnya selama pemrosesan masih ada pada citra. Dua cabang dimungkinkan. Cabang mana yang dijalankan dipilih oleh previewer, bukan oleh kamera: konsumen yang membuka pratinjau memberi tahu driver apakah ia menginginkan citra mentah atau JPEG, dan ukuran jendela mentah apa yang dapat diterimanya.

  • Salinan mentah yang diperkecil. Ketika previewer meminta bingkai mentah, driver menyalin bingkai sebelumnya dalam format piksel aslinya (RGB565, skala abu-abu, dll.). Jika bingkai lebih besar dari jendela mentah yang diminta previewer, driver memperkecilnya dengan pemfilteran bilinear hingga sesuai; jika tidak, piksel dilewatkan tanpa perubahan. Tanpa artefak kompresi; previewer melihat piksel yang sama yang sedang dikerjakan aplikasi.

  • Kompresi JPEG. Ketika previewer meminta JPEG -- atau ketika salinan mentah tidak muat dalam stream buffer sama sekali -- driver mengompresi JPEG bingkai sebelumnya pada resolusi penuhnya ke dalam stream buffer. Kualitasnya disesuaikan secara adaptif per-bingkai sehingga output yang dikompres tetap dalam kapasitas stream buffer. Ketika sebuah bingkai muat, driver secara bertahap meningkatkan kualitas satu langkah ke arah batas atas yang bergantung pada ukuran piksel bingkai yang di-capture (bingkai yang lebih kecil diizinkan kualitas lebih tinggi; bingkai yang lebih besar dibatasi lebih rendah agar tidak bisa overflow pada perubahan konten kecil). Ketika sebuah bingkai tidak muat, driver mengurangi kualitas saat ini menjadi setengah, mempertahankannya pada level yang dikurangi selama beberapa lusin bingkai berikutnya agar pengaturan baru sempat stabil, dan membuang bingkai yang meluap dari pratinjau. Loop aplikasi terus berjalan tanpa terpengaruh; hanya previewer yang melewatkan bingkai yang dibuang.

Bingkai yang dihasilkan kamera dalam format yang sudah dikompres (format piksel JPEG pada sensor yang memancarkan JPEG secara langsung) melewati kedua cabang: bitstream yang sudah dikodekan disalin langsung ke buffer pratinjau apa adanya.

Previewer melakukan polling pada jadwalnya sendiri, umumnya jauh lebih lambat dari capture kamera, sehingga sub-sampling laju capture mentah: hanya snapshot yang sempat dibacanya yang ditampilkan. Jika snapshot() baru tiba di buffer pratinjau sebelum previewer membaca bingkai sebelumnya, buffer masih dikunci oleh previewer dan pembaruan pratinjau baru dilewati -- capture tersebut hilang dari aliran pratinjau. Pool framebuffer aplikasi sendiri tidak terpengaruh; bingkai yang di-capture tetap dikirimkan ke aplikasi secara normal.

4.16.3. Mendorong bingkai terakhir secara manual

Karena pratinjau diperbarui sebagai efek samping dari snapshot(), sebuah skrip yang selesai tanpa pernah memanggil snapshot lagi membiarkan apa pun yang terakhir dikirimnya ke pratinjau tetap berada di previewer tanpa batas waktu -- yang, untuk skrip yang melakukan pekerjaannya sebelum snapshot pertama dan kemudian keluar, adalah pratinjau yang kosong. image.Image.flush() (atau setara flush() pada objek CSI) menyalin isi saat ini dari framebuffer aplikasi ke stream buffer sesuai permintaan, tanpa mengambil bingkai baru:

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

Panggilan yang sama juga berguna ketika operasi yang berjalan lama berada di antara snapshot dan previewer akan menampilkan pratinjau yang usang sepanjang waktu.

Catatan

Aplikasi pratinjau harus membaca bingkai dari stream buffer sebelum skrip keluar. Flush di akhir skrip pendek hanya menyiapkan bingkai; jika skrip kemudian mengembalikan kontrol ke kamera sebelum previewer melakukan polling, buffer digunakan kembali pada run berikutnya dan bingkai terakhir tersebut hilang. Untuk pratinjau akhir skrip, beri previewer waktu sejenak untuk mengambil bingkai (jeda singkat setelah flush, atau dengan tidak langsung keluar) sebelum skrip berakhir.