4.15. บัฟเฟอร์เฟรม¶
เมื่อเซนเซอร์กล้องถูกเริ่มต้นแล้ว มันจะปล่อยเฟรมออกมาต่อเนื่องตามอัตราเฟรมของมัน -- เฟรมใหม่หนึ่งเฟรมทุกช่วงเวลาเฟรม ไม่ว่าแอปพลิเคชันจะพร้อมหรือไม่ก็ตาม แต่ละเฟรมต้องการพื้นที่ใน RAM สำหรับรองรับ มิฉะนั้นจะสูญหาย พูลบัฟเฟอร์เฟรมคือที่ที่เฟรมเหล่านั้นพักระหว่างที่ออกจาก DMA และถูกประมวลผลโดยโค้ดผู้ใช้ และจำนวนบัฟเฟอร์เฟรมที่กล้องเก็บไว้ในพูลนั้นจะควบคุมว่า DMA และแอปพลิเคชันใช้งานร่วมกันอย่างไร ตัวเลือกนี้เปิดเผยผ่าน framebuffers() และมีสี่โหมดให้เลือก โดยเลือกตามจำนวนบัฟเฟอร์
4.15.1. บัฟเฟอร์เดี่ยว (count = 1)¶
บัฟเฟอร์เฟรมหนึ่งตัวใน RAM DMA เติมข้อมูลลงไป แอปพลิเคชันอ่านจากมัน และการเรียก snapshot() ครั้งถัดไปไม่สามารถเริ่มได้จนกว่าแอปพลิเคชันจะปล่อยบัฟเฟอร์ เนื่องจากบัฟเฟอร์เดียวกันนั้นจำเป็นต้องใช้ทั้งสองอย่าง
กล้องและแอปพลิเคชันทำงานแบบล็อคสเต็ป DMA ต้องรอให้แอปพลิเคชันเสร็จ และแอปพลิเคชันต้องรอให้ DMA เสร็จ ซึ่งหมายความว่าอัตราเฟรมที่ทำได้คือ ครึ่งหนึ่ง ของอัตราเฟรมของเซนเซอร์อย่างดีที่สุด -- ทุกเฟรมคี่ที่เซนเซอร์ปล่อยออกมาจะมาถึงในขณะที่บัฟเฟอร์ยุ่งอยู่และสูญหาย
โหมดนี้ใช้ RAM น้อยที่สุดและมีปริมาณงานที่ผ่านได้ช้าที่สุด ใช้เฉพาะเมื่อ RAM ไม่เพียงพอที่จะจัดสรรบัฟเฟอร์ที่สอง
4.15.2. บัฟเฟอร์คู่ (count = 2)¶
บัฟเฟอร์เฟรมสองตัวใน RAM: หนึ่งตัวเป็น back buffer ที่ DMA เติมข้อมูล และหนึ่งตัวเป็น front buffer ที่แอปพลิเคชันอ่านจาก เมื่อแอปพลิเคชันเสร็จกับ front buffer สองบทบาทจะสลับกัน และ DMA เริ่มเติมข้อมูลลงในบัฟเฟอร์ที่เพิ่งปล่อยออกมา ขณะที่แอปพลิเคชันอ่านจากบัฟเฟอร์ที่เพิ่งเติมเสร็จ
ตราบเท่าที่แอปพลิเคชันประมวลผลแต่ละเฟรมได้ภายในเวลาน้อยกว่าหนึ่งช่วงเวลาเฟรมของกล้อง แอปพลิเคชันจะเห็นอัตราเฟรมเต็มของเซนเซอร์ -- เฟรมถัดไปของ DMA รออยู่ใน back buffer แล้วเมื่อแอปพลิเคชันเรียก snapshot() อีกครั้ง แต่ทันทีที่เวลาประมวลผลเกินหนึ่งช่วงเวลาเฟรม อัตราจะลดลงครึ่งหนึ่ง: กล้องจะผลิตสองเฟรมในเวลาที่แอปพลิเคชันใช้ประมวลผลหนึ่งเฟรม และมีเพียงเฟรมที่สองของทั้งสองนั้นเท่านั้นที่จะถูกส่ง
หลังจากจุดนั้น อัตราจะลดลงอย่างราบรื่นตามเวลาประมวลผล ทุกครั้งที่ DMA เสร็จสิ้นเฟรม back-buffer ใหม่ในขณะที่แอปพลิเคชันยังทำงานกับ front buffer อยู่ เฟรมใหม่จะเขียนทับการจับภาพก่อนหน้าในที่เดิมแทนที่จะถูกทิ้ง แอปพลิเคชันจะได้รับเฟรมล่าสุดที่กล้องผลิตเสมอใน snapshot() ถัดไป และอัตราแอปพลิเคชันที่ทำได้จะกลายเป็นส่วนกลับของเวลาประมวลผล
4.15.3. บัฟเฟอร์สาม (count = 3)¶
บัฟเฟอร์เฟรมสามตัวใน RAM: back buffer สองตัวที่ DMA วนไป และ front buffer หนึ่งตัวที่แอปพลิเคชันกำลังทำงานอยู่ นี่คือโหมดเริ่มต้นที่ OpenMV Cam เลือกเมื่อมี RAM เพียงพอ โดยมีการ fall-back อัตโนมัติไปยังบัฟเฟอร์คู่หรือบัฟเฟอร์เดี่ยวเมื่อไม่มี
บัฟเฟอร์ที่สามแยกอัตราเฟรมกล้องออกจากอัตราเฟรมแอปพลิเคชันโดยสมบูรณ์ DMA มีบัฟเฟอร์ให้เขียนเสมอ แอปพลิเคชันมีบัฟเฟอร์ให้อ่านเสมอ ใน snapshot() แต่ละครั้ง back buffer ที่พร้อมล่าสุดจะกลายเป็น front buffer ใหม่ และ front buffer เดิมจะถูกปล่อยให้ DMA ใช้ อัตราเฟรมของแอปพลิเคชันตรงกับเวลาที่ใช้ประมวลผลแต่ละเฟรมจริงๆ -- โดยไม่มีขั้นตอน 1/2 ที่บัฟเฟอร์คู่จะตกเข้าไปเมื่อเวลาประมวลผลเกินหนึ่งช่วงเวลาเฟรมเพียงเล็กน้อย
4.15.4. Video FIFO (count = 4 หรือมากกว่า)¶
บัฟเฟอร์เฟรมสี่ตัวขึ้นไปใน RAM จัดเรียงเป็นวงแหวนของเฟรมที่จับภาพต่อกัน ทุกเฟรมที่กล้องส่งมาจะถูกเข้าคิวใน FIFO และ snapshot() จะคืนเฟรม เก่าที่สุด ในคิวแทนที่จะเป็นเฟรมล่าสุด แอปพลิเคชันเดินผ่านเฟรมที่จับภาพไว้ตามลำดับการจับภาพ ในเวลาที่มีจริงสำหรับแต่ละเฟรม
โหมดนี้เป็นตัวเลือกที่ถูกต้องเมื่อทุกเฟรมมีความสำคัญและคาดว่าจะมีการหยุดประมวลผลชั่วคราว: เขียนวิดีโอลงการ์ด SD ที่สแต็กพื้นที่จัดเก็บอาจบล็อกเป็นสิบมิลลิวินาทีระหว่างการลบ สตรีมผ่าน USB ไปยังโฮสต์ที่หยุดอ่านชั่วคราว หรือบัฟเฟอร์ชุดสั้นของเหตุการณ์ที่รวดเร็วเพื่อตรวจสอบในโค้ด
นโยบายสองอย่างจัดการกรณีที่ FIFO เต็มก่อนที่แอปพลิเคชันจะระบาย
ทิ้งเฟรมเก่า (ค่าเริ่มต้น) เมื่อ FIFO เต็ม เฟรมในคิวทั้งหมดยกเว้นเฟรมที่ใช้งานอยู่จะถูกทิ้ง เพื่อให้
snapshot()ถัดไปคืนเฟรมล่าสุดแทนที่จะเป็นเฟรมเก่า DMA ยังคงจับภาพต่อไป ดังนั้นแอปพลิเคชันจะเห็นข้อมูลใหม่เสมอหลังจากการหยุดชะงัก นี่คือนโยบายที่ถูกต้องเมื่อเป้าหมายคือการรักษาสตรีมที่จับภาพให้ทันสมัย -- การบันทึกวิดีโอ การสตรีมสดหยุดจับภาพเมื่อล้น ส่ง
fflush=Falseให้กับตัวสร้างCSIและ DMA จะหยุดเติม FIFO เมื่อเต็ม โดยปล่อยให้เฟรมในคิวไม่บุบสลายsnapshot()จะยังคงคืนเฟรมตามลำดับการจับภาพจนกว่าแอปพลิเคชันจะระบายหมด หลังจากนั้น DMA จะดำเนินการต่อ นี่คือนโยบายที่ถูกต้องเมื่อเป้าหมายคือการรักษาทุกเฟรมของชุดสั้น -- จับการเคลื่อนไหวเร็วเพื่อตรวจสอบทีละเฟรมในโค้ดภายหลัง
ดู csi.CSI.framebuffers() สำหรับ API เต็ม
4.15.5. โหมดทริกเกอร์¶
ทางเลือกอื่นแทนโหมดที่ทำงานตลอดเวลาข้างต้นคือการจับภาพแบบ triggered ซึ่งเซนเซอร์ปล่อยเฟรมออกมาเฉพาะเมื่อ snapshot() ร้องขอเท่านั้น กล้องจะนิ่งระหว่างการสแนปช็อตและเริ่มการรับแสงใหม่ทุกครั้งที่แอปพลิเคชันเรียก
ต้นทุนคือปริมาณงานที่ผ่านได้: การจับภาพแบบทริกเกอร์ไม่สามารถทับซ้อนกับครั้งก่อนได้ ดังนั้นอัตราเฟรมสูงสุดที่ทำได้คือครึ่งหนึ่งของอัตราปกติของเซนเซอร์ ประโยชน์คือการกำหนดเวลาการรับแสง Snapshot ควบคุมได้อย่างแม่นยำว่าการรับแสงเริ่มต้นเมื่อใด ซึ่งเป็นสิ่งที่แอปพลิเคชันต้องการเมื่อการรับแสงต้องสอดคล้องกับเหตุการณ์ภายนอก -- แฟลชสโตรบ เซนเซอร์ตำแหน่งสายพาน สัญญาณพัลส์บนพิน GPIO -- แทนที่จะตกลงที่ใดก็ตามที่เฟรมกลิ้งของเซนเซอร์ที่ทำงานอิสระเกิดขึ้นเมื่อแอปพลิเคชันพร้อมอ่าน
โหมดทริกเกอร์ขึ้นอยู่กับเซนเซอร์แต่ละรุ่น บนเซนเซอร์ที่รองรับ จะเปิดใช้งานโดยการเรียก csi0.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True) และปิดใช้งานโดยส่ง False