4.15. Framebuffer

Sobald der Kamerasensor initialisiert ist, gibt er kontinuierlich Einzelbilder mit seiner Bildrate aus – ein neues Einzelbild pro Bildperiode, unabhängig davon, ob die Anwendung dafür bereit ist oder nicht. Jedes Einzelbild muss irgendwo im RAM landen, sonst geht es verloren. Der Framebuffer-Pool ist der Ort, an dem diese Einzelbilder leben, nachdem sie den DMA verlassen haben und bevor sie vom Benutzercode verarbeitet werden, und wie viele Framebuffer die Kamera in diesem Pool vorhält, bestimmt, wie sich DMA und Anwendung diese teilen. Die Auswahl wird über framebuffers() zugänglich gemacht, und es stehen vier Modi zur Verfügung, die über die Pufferanzahl ausgewählt werden.

4.15.1. Einzelpuffer (count = 1)

Ein Framebuffer im RAM. Der DMA füllt ihn; die Anwendung liest aus ihm; der nächste Aufruf von snapshot() kann erst beginnen, wenn die Anwendung den Puffer freigegeben hat, da derselbe Puffer für beides benötigt wird.

Kamera und Anwendung laufen im Gleichschritt. Der DMA muss warten, bis die Anwendung fertig ist, und die Anwendung muss warten, bis der DMA fertig ist, was bedeutet, dass die erreichbare Bildrate im besten Fall die Hälfte der Bildrate des Sensors beträgt – jedes zweite vom Sensor ausgegebene Einzelbild trifft ein, während der Puffer belegt ist, und geht verloren.

Dieser Modus ist der kleinste im RAM und der langsamste im Durchsatz. Verwenden Sie ihn nur, wenn der RAM zu knapp ist, um einen zweiten Puffer zu reservieren.

4.15.2. Doppelpuffer (count = 2)

Zwei Framebuffer im RAM: ein Back-Puffer, den der DMA füllt, und ein Front-Puffer, aus dem die Anwendung liest. Wenn die Anwendung den Front-Puffer fertig verarbeitet hat, tauschen die beiden Rollen, und der DMA beginnt, den frisch freigegebenen Puffer zu füllen, während die Anwendung aus dem soeben gefüllten liest.

Solange die Anwendung jedes Einzelbild in weniger als einer Kamera-Bildperiode verarbeitet, sieht die Anwendung die volle Bildrate des Sensors – das nächste Einzelbild des DMA wartet bereits im Back-Puffer, wenn die Anwendung erneut snapshot() aufruft. Sobald die Verarbeitungszeit jedoch eine Bildperiode überschreitet, halbiert sich die Rate: Die Kamera erzeugt zwei Einzelbilder in der Zeit, die die Anwendung für die Verarbeitung eines benötigt, und nur das zweite dieser beiden wird geliefert.

Jenseits dieses Punktes verschlechtert sich die Rate gleichmäßig mit der Verarbeitungszeit. Jedes Mal, wenn der DMA ein neues Back-Puffer-Einzelbild fertigstellt, während die Anwendung noch am Front-Puffer arbeitet, überschreibt das neue Einzelbild die vorherige Aufnahme an Ort und Stelle, anstatt verworfen zu werden. Die Anwendung erhält bei ihrem nächsten snapshot() immer das aktuellste Einzelbild, das die Kamera erzeugt hat, und die erreichbare Anwendungsrate wird zum Kehrwert ihrer Verarbeitungszeit.

4.15.3. Dreifachpuffer (count = 3)

Drei Framebuffer im RAM: zwei Back-Puffer, die der DMA durchläuft, und ein Front-Puffer, an dem die Anwendung gerade arbeitet. Dies ist der Standardmodus, den die OpenMV Cam wählt, wenn genügend RAM zur Verfügung steht, mit automatischem Rückgriff auf Doppel- oder Einzelpuffer, wenn dies nicht der Fall ist.

Der dritte Puffer entkoppelt die Kamera-Bildrate vollständig von der Anwendungs-Bildrate. Der DMA hat immer einen Puffer zum Schreiben; die Anwendung hat immer einen Puffer zum Lesen; bei jedem snapshot() wird der aktuellste bereite Back-Puffer zum neuen Front-Puffer und der vorherige Front-Puffer wird für den DMA freigegeben. Die Bildrate der Anwendung entspricht der Zeit, die sie tatsächlich für die Verarbeitung jedes Einzelbildes benötigt – ohne den 1/2-Schritt, in den der Doppelpuffer verfällt, wenn die Verarbeitungszeit gerade eben über eine Bildperiode hinausgeht.

4.15.4. Video-FIFO (count = 4 oder mehr)

Vier oder mehr Framebuffer im RAM, angeordnet als ein Ring von unmittelbar hintereinander aufgenommenen Einzelbildern. Jedes Einzelbild, das die Kamera liefert, wird in das FIFO eingereiht, und snapshot() gibt das älteste eingereihte Einzelbild zurück, anstatt das aktuellste. Die Anwendung durchläuft die aufgenommenen Einzelbilder in der Aufnahmereihenfolge, in der Zeit, die sie tatsächlich für jedes aufwenden kann.

Dieser Modus ist die richtige Wahl, wenn jedes Einzelbild wichtig ist und kurze Verarbeitungsstockungen zu erwarten sind: das Schreiben von Video auf eine SD-Karte, deren Speicherstapel während eines Löschvorgangs für mehrere zehn Millisekunden blockieren kann, das Streamen über USB zu einem Host, der das Lesen kurzzeitig einstellt, oder das Puffern eines kurzen Stoßes eines schnellen Ereignisses zur Untersuchung im Code.

Zwei Richtlinien behandeln den Fall, dass das FIFO sich füllt, bevor die Anwendung es geleert hat.

  • Alte Einzelbilder verwerfen (Standard). Wenn das FIFO sich füllt, werden alle eingereihten Einzelbilder außer dem aktiven verworfen, sodass das nächste snapshot() ein aktuelles Einzelbild statt eines veralteten zurückgibt. Der DMA nimmt durchgehend weiter auf, sodass die Anwendung nach einer Stockung immer frische Daten sieht. Dies ist die richtige Richtlinie, wenn das Ziel darin besteht, den aufgenommenen Stream aktuell zu halten – Videoaufzeichnung, Live-Streaming.

  • Bei Überlauf das Aufnehmen stoppen. Übergeben Sie fflush=False an den CSI-Konstruktor, und der DMA stoppt das Füllen des FIFO, wenn es voll ist, und lässt die eingereihten Einzelbilder unangetastet. snapshot() gibt weiterhin Einzelbilder in der Aufnahmereihenfolge zurück, bis die Anwendung sie geleert hat, woraufhin der DMA wieder aufnimmt. Dies ist die richtige Richtlinie, wenn das Ziel darin besteht, jedes Einzelbild eines kurzen Stoßes zu bewahren – das Aufnehmen schneller Bewegung, um sie anschließend im Code Einzelbild für Einzelbild zu untersuchen.

Siehe csi.CSI.framebuffers() für die vollständige API.

4.15.5. Getriggerter Modus

Eine Alternative zu den oben beschriebenen, ständig laufenden Modi ist die getriggerte Aufnahme, bei der der Sensor nur dann ein Einzelbild ausgibt, wenn snapshot() eines anfordert. Die Kamera ist zwischen den Schnappschüssen untätig und startet jedes Mal eine neue Belichtung, wenn die Anwendung sich meldet.

Der Preis ist der Durchsatz: Eine getriggerte Aufnahme kann sich nicht mit der vorherigen überlappen, sodass die maximal erreichbare Bildrate die Hälfte der normalen Rate des Sensors beträgt. Der Vorteil ist die Belichtungssteuerung. Der Schnappschuss steuert genau, wann die Belichtung beginnt, was eine Anwendung benötigt, wenn die Belichtung mit einem externen Ereignis abgestimmt sein muss – einem Stroboskopblitz, einem Fließband-Positionssensor, einem Impuls auf einer GPIO-Leitung – anstatt dort zu landen, wo sich gerade das freilaufende Rolling-Frame des Sensors befindet, wenn die Anwendung bereit ist, es zu lesen.

Der getriggerte Modus ist sensorspezifisch. Auf unterstützten Sensoren wird er durch den Aufruf von csi0.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True) aktiviert und durch die Übergabe von False deaktiviert.