4.18. 다중 센서

일부 OpenMV Cam은 동일한 보드에 두 개의 이미지 센서를 짝지웁니다 – 가장 흔하게는 컬러 카메라와 함께 FLIR® Lepton® 열화상 센서를 두지만, 동일한 형태가 컬러 플러스 이벤트 보드와 향후의 모든 듀얼 센서 하드웨어에도 적용됩니다. 각 센서는 자체 픽셀 배열과 자체 제어 버스를 가지며, 자체 프레임 속도로 자체 파이프라인을 실행합니다. CSI API는 애플리케이션이 물리적 센서 하나당 하나의 CSI 객체를 인스턴스화할 수 있게 함으로써 이들을 포괄하도록 확장됩니다.

4.18.1. 어느 센서를 선택할지

CSI 생성자는 보드의 특정 센서를 지정하는 cid 인자를 받습니다. cid=-1 (기본값)은 기본 센서를 선택하며, 이름이 지정된 cid 상수는 칩 ID로 보조 센서를 선택합니다:

import csi

csi_rgb     = csi.CSI()                    # primary colour sensor
csi_thermal = csi.CSI(cid=csi.LEPTON)      # FLIR® Lepton®

각 인스턴스는 자체 구성 – 픽셀 형식, framesize, 노출 / 게인 조절값, 프레임 버퍼 풀 – 을 소유하며, 다른 인스턴스와는 독립적으로 리셋되고 구성되며 읽힙니다. 지원되는 보조 센서를 위한 상수(LEPTON, GENX320, 그리고 CSI 레퍼런스에 나열된 그 외의 것들)는 애플리케이션이 보조 포트에서 기대하는 칩을 지정하며, 실제 칩이 일치하지 않으면 드라이버는 생성을 실패시킵니다.

4.18.2. 양쪽 센서에서 캡처하기

각 센서는 다른 센서와 독립적으로 자체 캡처 파이프라인을 실행합니다 – 컬러 센서는 초당 30프레임을 전달하는 반면 Lepton® 은 9프레임을 전달할 수 있습니다. 이러한 불일치를 다루는 직관적인 방법은 더 빠른 센서가 루프를 주도하게 하고 더 느린 센서는 논블로킹으로 읽어, 준비된 것이 있으면 받아오고 아무것도 없을 때는 그 반복을 건너뛰는 것입니다:

import csi

csi_rgb     = csi.CSI()
csi_thermal = csi.CSI(cid=csi.LEPTON)

csi_rgb.reset()                        # powers the rail, pulses RESET
csi_rgb.pixformat(csi.RGB565)
csi_rgb.framesize(csi.QVGA)

csi_thermal.reset(hard=False)          # I2C reconfigure only
csi_thermal.pixformat(csi.GRAYSCALE)
csi_thermal.framesize(csi.QQVGA)

while True:
    rgb_img     = csi_rgb.snapshot()                  # blocks for next colour frame
    thermal_img = csi_thermal.snapshot(blocking=False)  # returns None if not ready
    if thermal_img is not None:
        # process aligned colour + thermal pair
        pass
    else:
        # process colour only on this iteration
        pass

블로킹 snapshot() 은 루프의 속도를 맞추고, 논블로킹 버전은 이전 호출 이후 새 프레임이 도착했을 때 가장 최근의 열화상 프레임을 반환하며 그렇지 않으면 None 을 반환합니다. 애플리케이션은 컬러 센서의 프레임 속도로 계속 실행되면서 Lepton® 이 프레임을 만들어 낼 때마다 열화상 프레임을 받습니다.

반대 패턴 – 두 개의 블로킹 스냅샷을 연달아 실행 – 도 동작하지만, 그러면 루프는 두 센서 속도 중 더 느린 쪽으로 실행되며, 반복 사이에 더 빠른 센서의 파이프라인은 멈춰 있게 됩니다. 애플리케이션의 다운스트림 처리가 실제로 주도하고자 하는 속도가 무엇이든 그쪽을 고르십시오.

4.18.3. 공유 전원 레일에서의 리셋

일부 듀얼 센서 보드는 두 칩을 단일 전원 레일에서 구동하거나 리셋 라인을 공유합니다. 그런 보드에서는 첫 번째 reset() 이 레일을 올리고 공유 신호를 펄스로 보냅니다. 다른 CSI 인스턴스에 대한 이후의 리셋은 이웃을 리셋으로 끌고 가지 않으면서 자체 칩만 다시 프로그래밍하도록 hard=False 를 전달해야 합니다:

csi_rgb.reset()                        # primary -- powers the rail, pulses RESET
csi_thermal.reset(hard=False)          # secondary -- I2C reconfigure only

이런 형태에서 보조 센서에 hard=True 를 주면 부작용으로 기본 센서가 다시 리셋되어, 애플리케이션이 이미 적용해 둔 설정이 되돌려집니다. 각 듀얼 센서 보드의 레퍼런스 페이지는 레일이 공유되는지 여부를 명시합니다.

4.18.4. 스트림 소스 선택하기

센서가 두 개인 카메라는 두 개의 CSI 인스턴스를 갖지만 그 사이에 여전히 스트림 프레임 버퍼는 하나뿐입니다. 생성자 인자가 어느 센서의 프레임이 프리뷰에 들어갈지를 고릅니다:

csi_rgb     = csi.CSI()                    # primary
csi_thermal = csi.CSI(cid=csi.LEPTON,
                      stream=True)         # preview source

stream=True 는 이름이 지정된 인스턴스를 소스로 만듭니다. stream= 인자가 없으면 기본 센서(cid=-1, 기본값)가 소스가 되며, 보조 센서의 cid= 로 만들어진 인스턴스는 명시적으로 stream=True 가 전달되지 않는 한 프리뷰에서는 조용히 있습니다. 선택되지 않은 센서에 대한 snapshot() 호출은 여전히 그 센서의 프레임 버퍼로 프레임을 정상적으로 캡처합니다 – 단지 프리뷰를 갱신하지 않을 뿐입니다.