4.16. การแสดงตัวอย่างภาพ¶
พูลบัฟเฟอร์เฟรมคือที่ที่แอปพลิเคชันอ่านเฟรมของมัน ขณะที่แอปพลิเคชันกำลังทำงานกับเฟรมเหล่านั้น สิ่งใดก็ตามที่เชื่อมต่อกับกล้องเพื่อแสดงตัวอย่างจำเป็นต้องมีสำเนาของแต่ละเฟรมด้วย กล้องมีบัฟเฟอร์ที่สองที่เฉพาะเจาะจงสำหรับจุดประสงค์นั้น และกฎเดียวสำหรับเมื่อมันถูกเติม: ทุกครั้งที่แอปพลิเคชันเรียก snapshot(), เฟรมที่จับภาพก่อนหน้าจะถูกคัดลอกไปยังบัฟเฟอร์แสดงตัวอย่าง ก่อนที่จะส่งเฟรมใหม่กลับมา
แอปพลิเคชันและตัวแสดงตัวอย่างไม่เคยแย่งหน่วยความจำเดียวกัน แอปพลิเคชันอ่านเฟรมจากพูล ตัวแสดงตัวอย่างอ่านเฟรมจากบัฟเฟอร์แสดงตัวอย่าง ทั้งสองเกิดขึ้นพร้อมกัน
4.16.1. บัฟเฟอร์เฟรมสตรีม¶
บัฟเฟอร์แสดงตัวอย่าง -- stream framebuffer -- คือภูมิภาค RAM ขนาดคงที่เดียวที่แยกจากพูลบัฟเฟอร์เฟรม ขนาดของมันถูกกำหนดในเวลาสร้างเฟิร์มแวร์และไม่เปลี่ยนแปลงด้วย framesize() หรือ pixformat() ประมาณหนึ่งเมกะไบต์เป็นค่าทั่วไปบน OpenMV Cams รุ่นใหม่ -- ใหญ่พอที่จะรองรับการแสดงตัวอย่างที่ความละเอียดปานกลาง เล็กกว่าเฟรมเต็มความละเอียดที่ขนาดเซนเซอร์ใหญ่ที่สุดมาก
โค้ดแอปพลิเคชันไม่ได้อ่านหรือเขียนบัฟเฟอร์นี้โดยตรง ไดรเวอร์กล้องเติมข้อมูลเป็นผลข้างเคียงของ snapshot()
4.16.2. สิ่งที่ snapshot ทำสำหรับการแสดงตัวอย่าง¶
ในแต่ละการเรียก snapshot() ก่อนที่ไดรเวอร์จะคืน framebuffer เดิมของแอปพลิเคชันกลับเข้าพูลและส่งอันใหม่ออกมา มันจะคัดลอกเฟรมก่อนหน้าไปยังบัฟเฟอร์แสดงตัวอย่าง -- พร้อมกับสิ่งที่แอปพลิเคชันวาดทับระหว่างการประมวลผลที่ยังคงอยู่บนภาพ สองสาขาเป็นไปได้ ว่าสาขาใดทำงานนั้น ถูกเลือกโดยตัวแสดงตัวอย่าง ไม่ใช่กล้อง: ผู้บริโภคที่เปิดการแสดงตัวอย่างจะบอกไดรเวอร์ว่าต้องการภาพ raw หรือ JPEG และขนาดหน้าต่าง raw ที่รับได้
การคัดลอก raw แบบย่อขนาด เมื่อตัวแสดงตัวอย่างขอเฟรม raw ไดรเวอร์จะคัดลอกเฟรมก่อนหน้าในรูปแบบพิกเซลดั้งเดิม (RGB565 ระดับสีเทา ฯลฯ) หากเฟรมใหญ่กว่าหน้าต่าง raw ที่ตัวแสดงตัวอย่างร้องขอ ไดรเวอร์จะย่อขนาดด้วยการกรองไบลิเนียร์จนพอดี มิฉะนั้นพิกเซลจะผ่านไปไม่เปลี่ยนแปลง ไม่มีรอยอาร์ทิแฟกต์การบีบอัด ตัวแสดงตัวอย่างเห็นพิกเซลเดียวกับที่แอปพลิเคชันกำลังทำงานอยู่
การบีบอัด JPEG เมื่อตัวแสดงตัวอย่างขอ JPEG -- หรือเมื่อการคัดลอก raw ไม่พอดีในบัฟเฟอร์สตรีมเลย -- ไดรเวอร์จะบีบอัด JPEG เฟรมก่อนหน้าที่ความละเอียดเต็มลงในบัฟเฟอร์สตรีม คุณภาพจะถูกปรับ แบบปรับตัว ต่อเฟรมเพื่อให้ผลลัพธ์ที่บีบอัดอยู่ภายในความจุของบัฟเฟอร์สตรีม เมื่อเฟรมพอดี ไดรเวอร์จะค่อยๆ เพิ่มคุณภาพขึ้นหนึ่งขั้นไปยังเพดานที่ขึ้นอยู่กับขนาดพิกเซลของเฟรมที่จับภาพ (เฟรมเล็กได้รับอนุญาตให้มีคุณภาพสูงกว่า เฟรมใหญ่ถูกจำกัดต่ำกว่าเพื่อไม่ให้ล้นจากการเปลี่ยนแปลงเนื้อหาเล็กน้อย) เมื่อเฟรม ไม่ พอดี ไดรเวอร์จะลดคุณภาพปัจจุบันลงครึ่งหนึ่ง ยึดที่ระดับที่ลดลงสำหรับเฟรมถัดไปหลายสิบเฟรมเพื่อให้การตั้งค่าใหม่มีเวลาเสถียร และทิ้งเฟรมที่ล้นออกจากการแสดงตัวอย่าง ลูปแอปพลิเคชันยังคงทำงานโดยไม่ได้รับผลกระทบ มีเพียงตัวแสดงตัวอย่างเท่านั้นที่พลาดเฟรมที่ถูกทิ้ง
เฟรมที่กล้องผลิตในรูปแบบที่บีบอัดแล้ว (รูปแบบพิกเซล JPEG บนเซนเซอร์ที่ปล่อย JPEG โดยตรง) จะข้ามทั้งสองสาขา: บิตสตรีมที่เข้ารหัสแล้วจะถูกคัดลอกตรงเข้าบัฟเฟอร์แสดงตัวอย่างตามที่เป็น
ตัวแสดงตัวอย่างสำรวจตามกำหนดการของตัวเอง โดยทั่วไปช้ากว่าที่กล้องจับภาพมาก ดังนั้นมันจึง sub-sample อัตราการจับภาพ raw: เฉพาะสแนปช็อตที่อ่านออกมาทันเวลาเท่านั้นที่จะแสดง หากมี snapshot() ใหม่มาถึงที่บัฟเฟอร์แสดงตัวอย่างก่อนที่ตัวแสดงตัวอย่างจะอ่านเฟรมก่อนหน้าออก บัฟเฟอร์ยังคงถูกล็อคโดยตัวแสดงตัวอย่าง และการอัปเดตการแสดงตัวอย่างใหม่จะถูกข้าม -- การจับภาพนั้นสูญหายจากสตรีมแสดงตัวอย่าง พูลบัฟเฟอร์เฟรมของแอปพลิเคชันเองไม่ได้รับผลกระทบ เฟรมที่จับได้ยังคงส่งไปยังแอปพลิเคชันตามปกติ
4.16.3. การผลักเฟรมสุดท้ายด้วยตนเอง¶
เนื่องจากการแสดงตัวอย่างถูกอัปเดตเป็นผลข้างเคียงของ snapshot() สคริปต์ที่สิ้นสุดโดยไม่เรียก snapshot อีกเลยจะทิ้งสิ่งที่ส่งไปยังการแสดงตัวอย่างล่าสุดไว้บนตัวแสดงตัวอย่างอย่างไม่มีกำหนด -- ซึ่งสำหรับสคริปต์ที่ทำงานก่อน snapshot แรกแล้วออก จะเป็นการแสดงตัวอย่างว่างเปล่า image.Image.flush() (หรือ flush() ที่เทียบเท่าบนออบเจ็กต์ CSI) จะคัดลอกเนื้อหาปัจจุบันของ framebuffer แอปพลิเคชันไปยังบัฟเฟอร์สตรีมตามต้องการ โดยไม่จับภาพเฟรมใหม่:
img = csi0.snapshot()
# process the image and draw on it
img.flush() # previewer sees the annotated frame
การเรียกเดียวกันนี้ยังมีประโยชน์เมื่อการดำเนินการที่ใช้เวลานานอยู่ระหว่างสแนปช็อต และตัวแสดงตัวอย่างจะแสดงการแสดงตัวอย่างที่ล้าสมัยตลอดเวลา
Note
แอปพลิเคชันแสดงตัวอย่างต้องอ่านเฟรมออกจากบัฟเฟอร์สตรีมก่อนที่สคริปต์จะออก การ flush ในตอนท้ายของสคริปต์สั้นเพียงแค่เตรียมเฟรมไว้ หากสคริปต์ส่งคืนการควบคุมให้กล้องก่อนที่ตัวแสดงตัวอย่างจะสำรวจ บัฟเฟอร์จะถูกนำมาใช้ใหม่ในการรันถัดไปและเฟรมสุดท้ายนั้นจะสูญหาย สำหรับการแสดงตัวอย่างสิ้นสุดสคริปต์ ให้ตัวแสดงตัวอย่างมีเวลาหยิบเฟรมขึ้น (การ sleep สั้นๆ หลัง flush หรือเพียงแค่ไม่ออกทันที) ก่อนสคริปต์สิ้นสุด