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. 프레임 dict¶
read_frame() 은 None (대기 중인 프레임 없음) 또는 다섯 개의 항목을 가진 dict 중 하나를 반환합니다:
키 |
의미 |
|---|---|
|
프레임 너비(픽셀 단위). |
|
프레임 높이(픽셀 단위). |
|
캠이 선언한 픽셀 포맷 식별자(캠의 |
|
압축 포맷(JPEG, PNG)의 경우 압축된 이미지의 크기(바이트 단위). 비압축 포맷에서는 사용되지 않습니다. |
|
RGB888 형식의 |
|
디코드 전에 캠이 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) 인수가 그 목표 크기를 설정합니다. 호스트는 여전히 data 필드에서 RGB888을 받습니다. 패키지가 캠이 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. 스트림 중지하기¶
중지하려면 enable=False 와 함께 streaming() 을 호출하십시오. 캠은 스크립트를 계속 실행하지만 더 이상 스트림 버퍼를 채우지 않으며, read_frame() 은 그 시점부터 그냥 None 을 반환합니다. stop() 을 호출하면 스크립트를 중단함으로써 암묵적으로 동일한 작업을 수행합니다.