13.3.1.3. Truyền phát khung hình

Một tập lệnh chụp ảnh trên camera có thể truyền từng khung hình về máy chủ qua USB. Quy trình gồm hai lệnh gọi trên đối tượng openmv.Camera: streaming() để bật hoặc tắt luồng, và read_frame() để lấy khung hình tiếp theo từ kênh.

13.3.1.3.1. Vòng lặp truyền phát và hiển thị tối giản

Tập lệnh phía camera là vòng lặp chụp ảnh thông thường; điểm mới là máy chủ mở luồng và đọc kết quả trả về:

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")

Camera liên tục chụp khung hình; máy chủ lấy từng khung từ bộ đệm luồng khi khung đến. Camera ghi đè bộ đệm luồng mỗi khi chụp ảnh mới, do đó máy chủ thăm dò chậm hơn tốc độ chụp của camera sẽ bỏ lỡ khung hình một cách âm thầm -- đây là hành vi đúng cho các trường hợp sử dụng kiểu xem trực tiếp.

13.3.1.3.2. Dict khung hình

read_frame() trả về None (không có khung hình đang chờ) hoặc một dict với năm mục:

Khóa

Ý nghĩa

width

Chiều rộng khung hình tính bằng điểm ảnh.

height

Chiều cao khung hình tính bằng điểm ảnh.

format

Định danh định dạng điểm ảnh mà camera khai báo (một số nguyên từ hằng số csi của camera).

depth

Đối với định dạng nén (JPEG, PNG), đây là kích thước ảnh nén tính bằng byte. Không dùng cho định dạng không nén.

data

Khung hình dưới dạng bộ đệm bytes ở định dạng RGB888. Mỗi điểm ảnh gồm ba byte (R, G, B); tổng độ dài là width * height * 3.

raw_size

Số byte camera gửi qua USB trước khi giải mã. Hữu ích để tính thông lượng thực tế.

Gói thư viện chuyển đổi định dạng gốc của camera (GRAYSCALE, RGB565, JPEG) sang RGB888 trước khi trả về, do đó máy chủ không bao giờ phải xử lý RGB565 dạng bit-packed hay tự giải nén JPEG. Các khung hình thang xám trả về với giá trị độ sáng (luma) được sao chép vào cả ba kênh.

Bộ đệm data được trình bày theo từng hàng, từ trên xuống dưới; có thể đưa thẳng vào thư viện hiển thị hoặc lưu thành tệp RGB thô mà không cần sắp xếp thêm.

13.3.1.3.3. Chế độ truyền phát thô

Theo mặc định, camera nén JPEG từng khung hình trước khi đặt vào kênh luồng và read_frame() giải nén trên máy chủ. Với camera không có hỗ trợ JPEG phần cứng, bước nén phần mềm là bước chậm nhất trong vòng lặp. Truyền raw=True để bỏ qua bước đó:

cam.streaming(True, raw=True, resolution=(320, 240))

Khi đó camera gửi bộ đệm điểm ảnh không nén. Các khung hình không nén lớn hơn nhiều so với JPEG tương đương, vì vậy camera thu nhỏ mỗi khung hình đã chụp để vừa kênh luồng trước khi gửi; đối số resolution=(width, height) đặt kích thước đích đó. Máy chủ vẫn nhận RGB888 trong trường data -- gói thư viện chuyển đổi từ bất kỳ định dạng điểm ảnh nào camera đã báo cáo trong format.

13.3.1.3.4. Để sự kiện điều khiển vòng lặp

Một vòng lặp thăm dò gọi read_frame() nhanh hơn tốc độ camera tạo khung hình sẽ hầu hết thời gian nhận được None. Khi máy chủ còn phải làm việc khác (cập nhật giao diện người dùng, thăm dò các kênh khác), read_status() là kiểm tra rẻ hơn: nó trả về một dict ánh xạ mỗi tên kênh đã đăng ký tới giá trị boolean "dữ liệu đã sẵn sàng":

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 ...

Đây là hình dạng vòng lặp mà trình xem CLI sử dụng.

13.3.1.3.5. Dừng luồng

Gọi streaming() với enable=False để dừng. Camera tiếp tục chạy tập lệnh của nó nhưng không còn lấp đầy bộ đệm luồng nữa; read_frame() chỉ trả về None từ thời điểm đó trở đi. Gọi stop() sẽ thực hiện điều tương tự một cách ngầm định bằng cách dừng tập lệnh.