4.16. Aperçu de l’image

Le pool de tampons d’image est l’endroit où l’application lit ses trames. Pendant que l’application travaille sur ces trames, tout ce qui est connecté à la caméra pour les prévisualiser a également besoin d’une copie de chaque trame. La caméra dispose d’un second tampon dédié à cet effet, et d’une règle unique quant au moment où il est rempli : à chaque fois que l’application appelle snapshot(), la trame capturée précédente est copiée dans le tampon d’aperçu avant que la nouvelle trame ne soit retournée.

L’application et le prévisualiseur ne se disputent jamais la même mémoire. L’application lit sa trame depuis le pool ; le prévisualiseur lit sa trame depuis le tampon d’aperçu. Les deux se produisent en parallèle.

4.16.1. Le tampon d’image de flux

Le tampon d’aperçu – le tampon d’image de flux – est une région unique de RAM de taille fixe distincte du pool de tampons d’image. Sa taille est définie au moment de la compilation du micrologiciel et ne change pas avec framesize() ni pixformat(). Environ un mégaoctet est typique sur les OpenMV Cams récentes – assez grand pour contenir un aperçu de résolution modérée, bien plus petit qu’une trame en pleine résolution aux plus grandes tailles de capteur.

Le code de l’application ne lit ni n’écrit ce tampon directement ; le pilote de la caméra le remplit comme effet de bord de snapshot().

4.16.2. Ce que fait la capture pour l’aperçu

À chaque appel à snapshot(), avant que le pilote ne relâche le tampon d’image précédent de l’application dans le pool et ne distribue le nouveau, il copie la trame précédente vers le tampon d’aperçu – avec tout ce que l’application a dessiné par-dessus pendant le traitement toujours présent sur l’image. Deux branches sont possibles. Celle qui s’exécute est choisie par le prévisualiseur, et non par la caméra : le consommateur qui a ouvert l’aperçu indique au pilote s’il veut l’image brute ou un JPEG, et quelle taille de fenêtre brute il peut accepter.

  • Copie brute réduite. Lorsque le prévisualiseur a demandé des trames brutes, le pilote copie la trame précédente dans son format de pixels natif (RGB565, niveaux de gris, etc.). Si la trame est plus grande que la fenêtre brute demandée par le prévisualiseur, le pilote la réduit par filtrage bilinéaire jusqu’à ce qu’elle s’ajuste ; sinon les pixels passent inchangés. Aucun artefact de compression ; le prévisualiseur voit les mêmes pixels que ceux sur lesquels l’application travaillait.

  • Compression JPEG. Lorsque le prévisualiseur a demandé du JPEG – ou lorsque la copie brute ne tiendrait pas du tout dans le tampon de flux – le pilote compresse en JPEG la trame précédente à sa pleine résolution dans le tampon de flux. La qualité est ajustée de manière adaptative trame par trame afin que la sortie compressée reste dans la capacité du tampon de flux. Lorsqu’une trame tient, le pilote remonte progressivement la qualité d’un cran vers un plafond qui dépend de la taille en pixels de la trame capturée (les trames plus petites sont autorisées à une qualité supérieure ; les trames plus grandes sont plafonnées plus bas afin qu’elles ne puissent pas déborder lors d’un petit changement de contenu). Lorsqu’une trame ne tient pas, le pilote divise par deux la qualité actuelle, la maintient à ce niveau réduit pendant les quelques dizaines de trames suivantes afin que le nouveau réglage ait le temps de se stabiliser, et écarte de l’aperçu la trame ayant débordé. La boucle de l’application continue de s’exécuter sans être affectée ; seul le prévisualiseur manque la trame écartée.

Les trames que la caméra produit dans un format déjà compressé (le format de pixels JPEG sur les capteurs qui émettent directement du JPEG) sautent les deux branches : le flux binaire encodé est copié tel quel directement dans le tampon d’aperçu.

Le prévisualiseur interroge selon son propre rythme, généralement bien plus lent que la cadence de capture de la caméra, de sorte qu’il sous-échantillonne la cadence de capture brute : seules les captures qu’il parvient à lire à temps sont affichées. Si une nouvelle snapshot() arrive au tampon d’aperçu avant que le prévisualiseur n’ait lu la trame précédente, le tampon est encore verrouillé par le prévisualiseur et la nouvelle mise à jour d’aperçu est ignorée – cette capture est perdue du flux d’aperçu. Le propre pool de tampons d’image de l’application n’est pas affecté ; la trame capturée parvient quand même normalement à l’application.

4.16.3. Envoyer manuellement la dernière trame

Comme l’aperçu est mis à jour comme effet de bord de snapshot(), un script qui se termine sans jamais rappeler snapshot laisse ce qu’il a envoyé en dernier à l’aperçu posé indéfiniment sur le prévisualiseur – ce qui, pour un script qui effectue son travail avant la première capture puis se termine, est un aperçu vide. image.Image.flush() (ou l’équivalent flush() sur l’objet CSI) copie le contenu actuel du tampon d’image de l’application dans le tampon de flux à la demande, sans capturer de nouvelle trame

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

Le même appel est également utile lorsqu’une opération de longue durée se situe entre des captures et que le prévisualiseur afficherait sinon un aperçu périmé pendant tout ce temps.

Note

L’application d’aperçu doit lire la trame depuis le tampon de flux avant que le script ne se termine. Un flush à la fin d’un court script ne fait que préparer la trame ; si le script rend ensuite la main à la caméra avant que le prévisualiseur n’ait interrogé, le tampon est réutilisé à l’exécution suivante et cette dernière trame est perdue. Pour les aperçus de fin de script, laissez au prévisualiseur un moment pour récupérer la trame (une brève pause après le flush, ou simplement ne pas quitter immédiatement) avant la fin du script.