4.15. 影格緩衝區¶
感測器初始化之後,會以其影格率持續送出影格——每個影格週期就會產生一個新影格,無論應用程式是否已準備好接收。每個影格都需要在 RAM 中有個落腳處,否則就會遺失。影格緩衝區池就是這些影格離開 DMA 之後、在被使用者程式碼處理之前所棲身的地方,而相機在該池中保留多少個影格緩衝區,決定了 DMA 與應用程式如何共享它們。這項選擇透過 framebuffers() 公開,共有四種模式可用,依緩衝區數量選定。
4.15.1. 單緩衝區(count = 1)¶
RAM 中只有一個影格緩衝區。DMA 填入它,應用程式從中讀取;下一次呼叫 snapshot() 必須等到應用程式釋放該緩衝區之後才能開始,因為兩者需要的是同一個緩衝區。
相機與應用程式以鎖步方式運作。DMA 必須等待應用程式完成,應用程式也必須等待 DMA 完成,這代表可達到的影格率最多只有感測器影格率的一半——感測器每隔一個影格送出的影格,都會在緩衝區忙碌時抵達而遭到遺失。
此模式在 RAM 上占用最少,但吞吐量最慢。只有在 RAM 過於吃緊、無法配置第二個緩衝區時才使用它。
4.15.2. 雙緩衝區(count = 2)¶
RAM 中有兩個影格緩衝區:一個由 DMA 填入的後置(back)緩衝區,以及一個供應用程式讀取的前置(front)緩衝區。當應用程式處理完前置緩衝區後,兩者角色互換,DMA 開始填入剛釋放的緩衝區,而應用程式則讀取剛填好的那一個。
只要應用程式處理每個影格所花的時間少於一個相機影格週期,應用程式就能看到感測器的完整影格率——當應用程式再次呼叫 snapshot() 時,DMA 的下一個影格已在後置緩衝區中等待。然而一旦處理時間超過一個影格週期,影格率就會減半:在應用程式處理一個影格的時間裡,相機會產生兩個影格,而這兩者中只有第二個會被遞交。
超過該臨界點後,影格率會隨處理時間平滑下降。每當 DMA 在應用程式仍在處理前置緩衝區時完成一個新的後置緩衝區影格,新影格就會就地覆寫前一次的擷取結果,而非遭到捨棄。應用程式在下一次 snapshot() 時,永遠會取得相機產生的最新影格,而可達到的應用程式影格率即為其處理時間的倒數。
4.15.3. 三緩衝區(count = 3)¶
RAM 中有三個影格緩衝區:兩個由 DMA 輪替使用的後置(back)緩衝區,以及一個應用程式正在處理的前置(front)緩衝區。當 RAM 有餘裕時,這是 OpenMV Cam 所選用的預設模式,並在 RAM 不足時自動退回雙緩衝區或單緩衝區。
第三個緩衝區讓相機影格率與應用程式影格率完全解耦。DMA 永遠有緩衝區可寫入;應用程式永遠有緩衝區可讀取;在每次 snapshot() 時,最新就緒的後置緩衝區成為新的前置緩衝區,而先前的前置緩衝區則釋放給 DMA。應用程式的影格率與其實際處理每個影格所需的時間相符——不會出現雙緩衝區在處理時間稍微超過一個影格週期時所落入的 1/2 級跳變。
4.15.4. 影片 FIFO(count = 4 或更多)¶
RAM 中有四個或更多影格緩衝區,排列成一圈接連擷取的影格環。相機遞交的每個影格都會排入 FIFO 佇列,而 snapshot() 回傳的是佇列中最舊的影格,而非最新的那一個。應用程式依擷取順序逐一處理擷取到的影格,並擁有實際可花在每個影格上的時間。
當每個影格都很重要,且預期會出現短暫的處理停頓時,此模式是正確的選擇:將影片寫入 SD 卡(其儲存堆疊在抹除期間可能阻塞數十毫秒)、透過 USB 串流至會短暫停止讀取的主機,或緩衝一段快速事件的短暫爆發以便在程式碼中檢視。
有兩種策略可處理 FIFO 在應用程式排空之前就被填滿的情況。
捨棄舊影格(預設)。 當 FIFO 填滿時,除了使用中的影格外,所有排入佇列的影格都會被捨棄,使得下一次
snapshot()回傳的是較新的影格而非過時的影格。DMA 全程持續擷取,因此應用程式在停頓後永遠看到新鮮的資料。當目標是讓擷取的串流保持即時時——影片錄製、即時串流——這是正確的策略。溢位時停止擷取。 將
fflush=False傳給CSI建構函式,DMA 便會在 FIFO 滿時停止填入,使排入佇列的影格保持完整。snapshot()會持續依擷取順序回傳影格,直到應用程式將它們排空,之後 DMA 才會恢復。當目標是保留短暫爆發中的每一個影格時——擷取快速動作以便事後在程式碼中逐格檢視——這是正確的策略。
完整 API 請參閱 csi.CSI.framebuffers()。
4.15.5. 觸發模式¶
上述始終運行模式之外的另一種選擇是觸發式擷取,亦即感測器只在 snapshot() 提出要求時才送出一個影格。相機在兩次快照之間處於閒置狀態,並在應用程式每次呼叫時開始一段全新的曝光。
代價是吞吐量:觸發式擷取無法與前一次重疊,因此可達到的最大影格率為感測器正常影格率的一半。好處則是曝光時機。快照精確控制曝光開始的時刻,這正是當曝光必須與外部事件對齊時應用程式所需要的——例如閃光燈、輸送帶位置感測器、GPIO 線上的脈衝——而非落在應用程式準備讀取時自由運行感測器的滾動影格恰好所在的位置。
觸發模式因感測器而異。在支援的感測器上,透過呼叫 csi0.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True) 啟用,並透過傳入 False 停用。