13.3.1.3. การสตรีมเฟรม¶
สคริปต์ที่จับภาพเฟรมบนกล้องสามารถสตรีมแต่ละเฟรมกลับมายังโฮสต์ผ่าน USB ได้ รูปแบบนี้ใช้การเรียกสองครั้งบน openmv.Camera ได้แก่ streaming() เพื่อเปิดหรือปิดสตรีม และ read_frame() เพื่อดึงเฟรมถัดไปออกจากช่องสัญญาณ
13.3.1.3.1. ลูปสตรีมและแสดงผลขั้นต่ำ¶
สคริปต์ฝั่งกล้องเป็นลูปสแนปช็อตปกติ สิ่งที่ใหม่คือโฮสต์เปิดสตรีมและอ่านผลลัพธ์กลับมา:
from openmv import Camera
script = """
import csi
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
while True:
csi0.snapshot()
"""
with Camera('/dev/ttyACM0') as cam:
cam.stop()
cam.exec(script)
cam.streaming(True)
while True:
if frame := cam.read_frame():
print(f"{frame['width']}x{frame['height']}, "
f"{frame['raw_size']} bytes")
กล้องจับภาพเฟรมอย่างต่อเนื่อง โฮสต์ดึงแต่ละเฟรมออกจากบัฟเฟอร์สตรีมทันทีที่มาถึง กล้องจะเขียนทับบัฟเฟอร์สตรีมทุกครั้งที่มีสแนปช็อตใหม่ ดังนั้นโฮสต์ที่โพลล์ช้ากว่าอัตราการจับภาพของกล้องจะทำให้เฟรมหายไปโดยไม่มีการแจ้งเตือน ซึ่งเป็นพฤติกรรมที่ถูกต้องสำหรับกรณีใช้งานแบบผู้ชม
13.3.1.3.2. พจนานุกรมเฟรม¶
read_frame() คืนค่า None (ไม่มีเฟรมรออยู่) หรือ dict ที่มีห้ารายการ:
คีย์ |
ความหมาย |
|---|---|
|
ความกว้างของเฟรมในหน่วยพิกเซล |
|
ความสูงของเฟรมในหน่วยพิกเซล |
|
ตัวบ่งชี้รูปแบบพิกเซลที่กล้องประกาศ (จำนวนเต็มจากค่าคงที่ |
|
สำหรับรูปแบบที่บีบอัด (JPEG, PNG) คือขนาดของภาพที่บีบอัดในหน่วยไบต์ ไม่ใช้งานสำหรับรูปแบบที่ไม่บีบอัด |
|
เฟรมในรูปแบบบัฟเฟอร์ |
|
จำนวนไบต์ที่กล้องส่งผ่าน USB ก่อนการถอดรหัส มีประโยชน์สำหรับการคำนวณปริมาณงานที่ผ่านได้จริง |
แพ็กเกจจะแปลงรูปแบบดั้งเดิมของกล้อง (GRAYSCALE, RGB565, JPEG) เป็น RGB888 ก่อนส่งคืน ทำให้โฮสต์ไม่ต้องจัดการกับ RGB565 แบบบิต-แพ็กหรือเส้นทางการแตกไฟล์ JPEG เอง เฟรมระดับสีเทาจะถูกส่งคืนโดยคัดลอกค่าลูมาไปยังทั้งสามช่องสัญญาณ
บัฟเฟอร์ data วางเรียงแถวต่อแถวจากบนลงล่าง สามารถนำไปป้อนให้ไลบรารีแสดงผลหรือบันทึกเป็นไฟล์ RGB ดิบได้โดยตรงโดยไม่ต้องสลับข้อมูลใดๆ เพิ่มเติม
13.3.1.3.3. โหมดสตรีมดิบ¶
โดยค่าเริ่มต้น กล้องจะบีบอัดแต่ละเฟรมที่จับภาพได้ด้วย JPEG ก่อนวางในช่องสัญญาณสตรีม และ read_frame() จะแตกไฟล์บนโฮสต์ สำหรับกล้องที่ไม่มีฮาร์ดแวร์รองรับ JPEG การบีบอัดด้วยซอฟต์แวร์เป็นขั้นตอนที่ช้าที่สุดในลูป การส่ง raw=True จะข้ามขั้นตอนนี้:
cam.streaming(True, raw=True, resolution=(320, 240))
กล้องจะส่งบัฟเฟอร์พิกเซลโดยไม่บีบอัด เฟรมที่ไม่บีบอัดมีขนาดใหญ่กว่าเฟรม JPEG มาก ดังนั้นกล้องจะย่อขนาดแต่ละเฟรมที่จับภาพได้ให้พอดีกับช่องสัญญาณสตรีมก่อนส่ง อาร์กิวเมนต์ resolution=(width, height) กำหนดขนาดเป้าหมายนั้น โฮสต์ยังคงได้รับ RGB888 ในช่อง data โดยแพ็กเกจแปลงจากรูปแบบพิกเซลใดก็ตามที่กล้องรายงานใน format
13.3.1.3.4. ให้อีเวนต์ขับเคลื่อนลูป¶
ลูปโพลล์ที่เรียก read_frame() เร็วกว่าที่กล้องผลิตเฟรมจะใช้เวลาส่วนใหญ่ในการรับ None กลับมา เมื่อโฮสต์มีงานอื่นต้องทำด้วย (อัปเดต UI หรือโพลล์ช่องสัญญาณอื่น) read_status() เป็นการตรวจสอบที่ประหยัดกว่า โดยคืนค่า dict ที่แมปชื่อช่องสัญญาณที่ลงทะเบียนทุกช่องไปยังค่าบูลีน "ข้อมูลพร้อม":
while True:
status = cam.read_status()
if status.get('stream'):
frame = cam.read_frame()
# ... process the frame ...
if status.get('stdout'):
text = cam.read_stdout()
print(text, end='')
if status.get('my_channel'):
data = cam.channel_read('my_channel')
# ... process custom-channel data ...
นี่คือรูปแบบลูปที่ตัวดู CLI เองใช้งาน
13.3.1.3.5. หยุดสตรีม¶
เรียก streaming() พร้อม enable=False เพื่อหยุด กล้องยังคงรันสคริปต์ต่อไปแต่ไม่เติมบัฟเฟอร์สตรีมอีกต่อไป read_frame() จะคืนค่า None นับจากจุดนั้นเป็นต้นไป การเรียก stop() ทำสิ่งเดียวกันโดยปริยายด้วยการหยุดสคริปต์