13.1.10. 독립형 터미널 창¶
Tools → Open Terminal은 독립적인 터미널 창을 엽니다. 각 창은 자체 창 안에 있는 소형 OpenMV IDE 세션으로, 프레임 버퍼 뷰어, 히스토그램, 대화형 터미널을 갖추고 있으며 사용자가 선택한 전송 방식으로 연결됩니다. 메인 창의 연결은 영향을 받지 않으므로, 독립형 터미널을 사용하면 첫 번째 카메라를 연결한 상태로 두 번째 카메라를 관찰하거나, 네트워크 반대편에 있는 카메라를 디버깅할 수 있습니다.
시리얼 포트를 통한 독립형 터미널 창: 왼쪽에는 run / stop / reset 툴바가 있는 대화형 터미널, 오른쪽에는 프레임 버퍼와 히스토그램이 있습니다. 카메라가 인밴드(in-band)로 프레임을 스트리밍하면 이들이 활성화됩니다.¶
New Terminal은 세 가지 전송 방식 중 하나를 묻습니다:
Serial port – 임의의 보드 레이트(기본값 115,200)의 모든 시리얼 포트입니다. 여기에는 두 번째 카메라의 USB 포트, USB-to-UART 브리지로 연결된 카메라, 블루투스 시리얼 링크(일반 시리얼 포트로 표시됨), 또는 터미널이 필요한 OpenMV가 아닌 모든 시리얼 장치가 포함됩니다. USB 포트에서는 보드 레이트가 속도를 제한하지 않으며, 데이터는 항상 USB 링크의 속도로 전송됩니다. 다만 카메라의 USB 포트에서는 921,600과 12,000,000을 피하십시오. 이 값들은 카메라를 REPL에서 IDE의 디버그 프로토콜로 전환시킵니다.
TCP – 선택한 호스트와 포트의 서버에 연결하거나, 선택한 포트에서 서버로서 수신 대기합니다.
UDP – 동일한 두 가지 역할을 UDP로 수행합니다.
IDE는 최근 열 개의 구성을 기억하여 한 번의 클릭으로 다시 열 수 있도록 Open Terminal 하위 메뉴에 나열합니다. Clear Menu는 이들을 잊어버립니다.
메인 창의 출력 전용 창과 달리, 독립형 터미널은 완전히 대화형입니다. 즉 REPL입니다. 프롬프트에 입력하면 연결된 카메라에서 Python이 한 줄씩 실행되며, MicroPython 자체가 제공하는 히스토리와 탭 완성 기능을 사용할 수 있습니다. 툴바는 일반적인 제어 시퀀스의 원클릭 등가 기능을 추가합니다. 현재 에디터 스크립트 실행, 실행 중인 스크립트 중지, 소프트 리셋과 더불어 메인 터미널 창과 동일한 지우기, 저장, 줄 바꿈 컨트롤도 제공합니다.
터미널 위의 프레임 버퍼도 실시간으로 동작합니다. 연결된 카메라가 인밴드(in-band)로, 즉 print 출력과 동일한 스트림에 압축된 프레임을 끼워 넣어 스트리밍하면, 터미널은 이를 디코딩하여 표시하며 Record와 Zoom 버튼은 메인 창에서와 똑같이 작동합니다. 이 조합이 바로 IDE의 네트워크 디버깅 방식입니다. Wi-Fi를 통해 REPL을 노출하는 카메라는 USB 케이블 없이도 완전한 편집-실행-미리보기 루프를 얻습니다.
13.1.10.1. 프레임을 인밴드로 스트리밍하기¶
터미널이 이해하는 인코딩은 단순합니다. 0xFE 바이트가 프레임을 열고, 두 번째 0xFE가 프레임을 닫으며, 그 사이의 모든 페이로드 바이트는 최상위 비트가 설정되어 있고 압축된 이미지의 6비트를 담습니다. 즉 이미지 바이트 3개가 페이로드 바이트 4개가 됩니다. 일반 텍스트는 이러한 바이트 값을 절대 사용하지 않으므로, 프레임과 print() 출력은 충돌 없이 스트림을 공유합니다. 터미널은 텍스트를 표시하고 프레임을 보여줍니다.
아래 스크립트는 캡처한 후 각 프레임을 JPEG로 변환하여 그 형식으로 출력합니다. 비트 패킹은 ulab을 통해 수행되며(시프트 연산자가 없으므로 시프트는 곱셈과 나눗셈으로 작성됨), 카메라를 따라잡을 수 있을 만큼 충분히 빠릅니다:
import csi
import sys
import time
from ulab import numpy as np
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
clock = time.clock()
def encode_for_ide(data):
n = len(data)
n3 = n - (n % 3)
m = (n3 // 3) * 4
out = bytearray(((n * 8) + 5) // 6 + 2)
out[0] = 0xFE
out[-1] = 0xFE
if n3:
src = np.frombuffer(data, dtype=np.uint8)
dst = np.frombuffer(out, dtype=np.uint8)
b0 = src[0:n3:3]
b1 = src[1:n3:3]
b2 = src[2:n3:3]
dst[1:m + 1:4] = (b0 & 0x3F) | 0x80
dst[2:m + 2:4] = (b0 // 64) | ((b1 & 0x0F) * 4) | 0x80
dst[3:m + 3:4] = (b1 // 16) | ((b2 & 0x03) * 16) | 0x80
dst[4:m + 4:4] = (b2 // 4) | 0x80
if n % 3 == 2:
x = data[n - 2] | (data[n - 1] << 8)
out[m + 1] = 0x80 | (x & 0x3F)
out[m + 2] = 0x80 | ((x >> 6) & 0x3F)
out[m + 3] = 0x80 | ((x >> 12) & 0x3F)
elif n % 3 == 1:
out[m + 1] = 0x80 | (data[n - 1] & 0x3F)
out[m + 2] = 0x80 | (data[n - 1] >> 6)
return out
while True:
clock.tick()
img = csi0.snapshot().to_jpeg(quality=80)
sys.stdout.write(encode_for_ide(img.bytearray()))
print(clock.fps())
이것이 독립형 터미널에서 동작하는 이유는 REPL 출력이 대기하기 때문입니다. 카메라는 터미널이 데이터를 가져갈 때까지 블록되므로 프레임의 모든 바이트가 도착하며, JPEG는 USB 풀 스피드 또는 하이 스피드 링크를 통해 빠르게 전송됩니다. 동일한 스크립트가 TCP 터미널에서도 수정 없이 동작하며, 이것이 앞서 설명한 네트워크 디버깅 경로입니다. 유일하게 동작하지 않는 곳은 IDE의 메인 디버그 연결인데, 이 연결의 출력 채널은 오버플로 시 대기하는 대신 스스로 재설정합니다. 거기서는 프레임 버퍼 뷰어가 이미 카메라의 프레임을 기본적으로 보여주므로 놓치는 것이 없습니다.