GENX320 이벤트 카메라

GENX320 이벤트 카메라 모듈은 320x320 해상도와 마이크로초 단위의 시간 정밀도를 제공하는 Prophesee 이벤트 기반 비전 센서입니다.

GENX320 이벤트 카메라

전체 데이터시트, 사진, 주문 정보는 GENX320 이벤트 카메라 제품 페이지 를 참고하세요.

참고

OpenMV H7 Plus, RT1062, N6에서 지원됩니다.

주요 특징

  • 320x320 이벤트 기반 비전 센서

  • 140 dB 다이내믹 레인지, 모션 블러 없음

  • 375 Hz 이상의 이벤트 히스토그램 출력 속도

  • 장면의 활동량에 따라 전력이 조정됨 — 약 3 mW에서 시작

  • 자동 노출 없이 5 lux 미만부터 밝은 햇빛까지 동작

  • 그레이스케일 프레임 또는 원시 이벤트 스트림 출력

사용법

GENX320은 이벤트 기반 비전 센서입니다 — 고정된 프레임 클록으로 전체 320x320 배열을 읽어내는 대신, 각 픽셀이 밝기 변화를 감지하는 즉시 비동기적으로 “이벤트”를 보고합니다. 모든 이벤트는 X/Y 좌표, ON/OFF 극성(밝음→어두움 또는 어두움→밝음), 마이크로초 단위 타임스탬프를 담고 있습니다. 바로 여기서 이 센서의 마이크로초 단위 시간 정밀도, 모션 블러 없음, 매우 높은 다이내믹 레인지, 활동량에 따라 조정되는 전력 소비가 비롯됩니다. 정적인 장면에서는 데이터가 생성되지 않습니다.

OpenMV 펌웨어는 GENX320을 csi.CSIcid= csi.GENX320 를 통해 노출합니다. 두 가지 동작 모드를 사용할 수 있습니다:

  • 히스토그램 모드 (기본값) — 이벤트가 칩 내부에서 픽셀별 빈(bin)에 누적되어 설정 가능한 속도(약 20~350 FPS)로 320x320 그레이스케일 프레임으로 보고됩니다. 센서가 일반 카메라처럼 동작하므로 모든 표준 이미지 처리 루틴(Image.find_blobs, 팔레트 등)이 그대로 동작합니다.

  • 이벤트 모드 — 미리 빈으로 묶인 프레임이 아니라 시간 세부 정보가 필요한 응용을 위해, 원시 이벤트가 완전한 마이크로초 타임스탬프와 함께 numpy ndarray 로 스트리밍됩니다.

히스토그램 모드

히스토그램 모드에서 GENX320은 각 픽셀이 해당 위치의 최근 이벤트 활동을 인코딩하는 그레이스케일 프레임을 출력합니다. 밝기 기준선 위의 픽셀은 ON 이벤트(밝기 상승), 아래의 픽셀은 OFF 이벤트(밝기 하강)입니다. 기본 기준선 밝기는 128이고 이벤트당 대비 단계는 16입니다 — 이벤트가 두드러지게 하려면 대비를 높이세요:

import csi
import time

csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize((320, 320))
csi0.brightness(128)  # baseline (default 128)
csi0.contrast(16)     # per-event step
csi0.framerate(50)    # 20-350 FPS

clock = time.clock()
while True:
    clock.tick()
    img = csi0.snapshot()
    print(clock.fps())

csi.CSI.brightness, csi.CSI.contrast, csi.CSI.framerate 는 히스토그램 출력을 형성하는 세 가지 조절 항목입니다.

컬러화 출력

밝은 배경에는 csi.CSI.color_paletteimage.PALETTE_EVT_LIGHT 로, 어두운 배경에는 image.PALETTE_EVT_DARK 로 설정하세요 — 드라이버가 팔레트를 직접 사용하여 RGB565 프레임을 출력합니다:

csi0.color_palette(image.PALETTE_EVT_LIGHT)

핫 픽셀 보정

이벤트 센서는 가짜로 발화하는 “핫 픽셀”을 누적합니다. 정적인 장면을 대상으로 csi.IOCTL_GENX320_CALIBRATE 를 실행하여 이를 비활성화하세요. 드라이버는 320x320 픽셀별 히트 카운트를 만들고 평균과 표준편차를 계산한 다음, 카운트가 mean + sigma * stddev 를 초과하는 픽셀을 비활성화합니다 — 그러면 비활성화된 픽셀은 센서 수준에서 이벤트 방출을 멈춥니다.

보정을 제어하는 두 개의 매개변수가 있습니다:

  • event_count — 통계를 계산하기 전에 집계할 이벤트 수입니다. 루프는 누적 이벤트 총합이 이 예산을 넘어설 때까지 프레임을 캡처합니다. 카운트가 높을수록 보정 시간이 길어지는 대신 더 신뢰할 수 있는 추정값을 얻습니다. 10000 이 적절한 시작점입니다.

  • sigma — 표준편차에 대한 임계값 배수입니다. 값이 낮을수록 더 공격적(더 많은 픽셀 비활성화)이고, 높을수록 더 보수적입니다. 0.5 가 좋은 기본값입니다.

모션으로 인한 이벤트가 실제로는 정상인 픽셀에 불리하게 집계되지 않도록, 먼저 센서를 정적인 장면을 향하게 하세요:

csi0.snapshot(time=5000)  # let the user steady the camera
disabled = csi0.ioctl(csi.IOCTL_GENX320_CALIBRATE, 10000, 0.5)
print(f"disabled {disabled} hot pixels")

안티 플리커(AFK) 필터

주기적인 광원(형광등, LED 구동 디스플레이)은 막대한 양의 중복 이벤트를 생성합니다. AFK 필터는 픽셀이 특정 대역 내의 주파수로 토글되는 이벤트를 거부합니다 — csi.IOCTL_GENX320_SET_AFK 를 통해 대역 경계를 헤르츠 단위로 지정하여 활성화하세요:

csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 1, 130, 160)  # 130-160 Hz
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 0)            # disable

바이어스 프리셋

GenX320의 각 픽셀은 여러 개의 설정 가능한 바이어스를 갖춘 아날로그 프런트엔드를 실행합니다. 이들은 함께 감도, 노이즈, 픽셀 대역폭, 이벤트 속도를 조절합니다 — 올바른 조합은 장면에 따라 다릅니다. 개별 바이어스는 다음과 같습니다:

  • DIFF_ON — 양의 비교기 대비 임계값입니다. 픽셀의 로그 조도가 이만큼 상승하면 ON 이벤트를 방출합니다. 낮을수록 밝아지는 변화에 더 민감합니다.

  • DIFF_OFF — 음의 비교기 대비 임계값입니다(OFF 이벤트에 대한 대칭 상대값). 낮을수록 어두워지는 변화에 더 민감합니다.

  • FO — 픽셀의 저역 차단 주파수입니다. 높을수록 픽셀 대역폭이 넓어져(응답이 빠르고 지연 시간이 낮음) 배경 노이즈 활동이 더 많아집니다.

  • HPF — 고역 차단 주파수입니다. 높을수록 느린 밝기 변화를 더 강하게 거부하여 빠른 변화만 비교기에 도달합니다. 주변광 드리프트를 무시하는 데 유용합니다.

  • REFR — 불응기입니다. 픽셀이 발화한 후, 다시 발화할 수 있기까지 이만큼 리셋 상태를 유지합니다. 높을수록 데드 타임이 길어져 픽셀별 이벤트 속도를 제한하는 데 유용합니다.

csi.CSI.reset 이후 드라이버는 csi.GENX320_BIASES_DEFAULT아니라 csi.GENX320_BIASES_LOW_NOISE 를 적용합니다 — 데이터시트 기본값은 훨씬 높은 배경 이벤트 속도를 방출하므로, 스트림을 조용하게 유지하기 위해 LOW_NOISE 가 시작점으로 사용됩니다. 응용에 더 높은 감도나 대역폭이 필요하면 다른 프리셋으로 csi.IOCTL_GENX320_SET_BIASES 를 호출하세요.

csi.IOCTL_GENX320_SET_BIASES 는 다섯 가지 프리셋 중 하나를 적용합니다:

  • csi.GENX320_BIASES_DEFAULT — GenX320 데이터시트 기본값입니다. 일반적인 장면에 균형 잡힌 감도, 노이즈, 대역폭을 제공합니다.

  • csi.GENX320_BIASES_LOW_LIGHT — 더 높은 감도를 위해 두 대비 임계값을 모두 완화하고, 노이즈를 줄이기 위해 FO를 낮추며, 느린 밝기 변화도 여전히 기록되도록 HPF를 0으로 설정합니다 — 저조도 장면은 자체적으로 생성하는 이벤트가 적으므로 가능한 한 많은 이벤트가 통과되기를 원합니다.

  • csi.GENX320_BIASES_ACTIVE_MARKER — 고대비로 깜빡이는 LED 추적에 맞춰 조정되었습니다. 날카로운 변화만 트리거되도록 대비 임계값을 높이고, 픽셀 대역폭을 최대화하고 느린 주변광 드리프트를 거부하도록 FO와 HPF를 높게 올리며, 모든 깜빡임 에지를 연달아 포착하도록 REFR을 0으로 낮춥니다. 그 결과 거의 전부 LED 에지로 이루어져 추적하기 쉬운 스트림이 됩니다.

  • csi.GENX320_BIASES_LOW_NOISE — 드라이버 기본값입니다. DEFAULT 대비 두 대비 임계값을 모두 높이고(감도가 낮아짐) FO를 낮춥니다(픽셀이 느려질수록 조용해짐). 그렇지 않으면 거짓 이벤트가 지배할 정적이거나 느린 장면에 가장 적합합니다.

  • csi.GENX320_BIASES_HIGH_SPEED — 각 픽셀이 더 빠르게 응답할 수 있도록 FO를 올리고, 느린 밝기 드리프트를 거부하도록 HPF를 높이며, 빠르게 움직이는 단일 에지가 판독을 넘치게 하지 않도록 REFR을 높입니다 — 더 긴 데드 타임이 심한 움직임에서도 이벤트 양을 제한합니다.

csi.IOCTL_GENX320_SET_BIAScsi.GENX320_BIAS_DIFF_ON, csi.GENX320_BIAS_DIFF_OFF, csi.GENX320_BIAS_FO, csi.GENX320_BIAS_HPF, csi.GENX320_BIAS_REFR 중 하나, 그리고 DAC 값으로 개별 바이어스를 재정의하세요. 각 바이어스는 독립적으로 설정됩니다 — 프리셋을 시작점으로 선택한 다음 장면에 필요한 바이어스를 조정하세요:

csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_LOW_LIGHT)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF, 20)

추적

히스토그램 모드 출력은 그저 그레이스케일 이미지이므로 일반적인 블롭 추적이 그대로 동작합니다. 액티브 마커 LED를 추적하려면 액티브 마커 바이어스 프리셋을 로드하고 히스토그램의 밝은 끝에서 블롭을 찾으세요:

import csi
import time

csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize((320, 320))
csi0.brightness(128)
csi0.contrast(16)
csi0.framerate(200)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_ACTIVE_MARKER)

clock = time.clock()
while True:
    clock.tick()
    img = csi0.snapshot()
    for blob in img.find_blobs([(120, 140)], invert=True,
                               pixels_threshold=2, area_threshold=4,
                               merge=True):
        img.draw_detection(blob)
    print(clock.fps())

이벤트 모드

이벤트 모드는 칩 내부 히스토그램을 우회하고 원시 이벤트를 numpy ndarray 로 스트리밍합니다. 각 이벤트는 여섯 개의 uint16 열로 이루어진 한 행입니다:

  • [0] 이벤트 유형 — 아래 참조

  • [1] 초 단위 타임스탬프

  • [2] 밀리초 단위 타임스탬프

  • [3] 마이크로초 단위 타임스탬프

  • [4] X 좌표, 0-319

  • [5] Y 좌표, 0-319

드라이버는 열 [0] 에 여섯 가지 이벤트 유형을 방출합니다:

  • csi.PIX_OFF_EVENT — 픽셀이 밝기 감소를 감지했습니다(DIFF_OFF 비교기 임계값을 넘음). X/Y는 발화한 픽셀을 가리킵니다.

  • csi.PIX_ON_EVENT — 픽셀이 밝기 증가를 감지했습니다(DIFF_ON 임계값을 넘음). X/Y는 해당 픽셀을 가리킵니다.

  • csi.EXT_TRIGGER_FALLING — 센서의 외부 트리거 핀이 하강 에지를 감지했습니다. X/Y는 사용되지 않습니다.

  • csi.EXT_TRIGGER_RISING — 센서의 외부 트리거 핀이 상승 에지를 감지했습니다. X/Y는 사용되지 않습니다.

  • csi.RST_TRIGGER_FALLING — 픽셀 리셋 트리거, 하강 에지입니다. X/Y는 사용되지 않습니다. 현재 펌웨어에서는 생성되지 않습니다.

  • csi.RST_TRIGGER_RISING — 픽셀 리셋 트리거, 상승 에지입니다. X/Y는 사용되지 않습니다. 현재 펌웨어에서는 생성되지 않습니다.

GENX320의 외부 트리거 입력은 카메라의 프레임 동기 라인에 연결되어 있으며, 이 라인은 프로세서와 핀 헤더 양쪽의 P10 으로도 라우팅됩니다 — P10을 구동하여 동기 에지를 이벤트 스트림에 주입하고, 픽셀 데이터와 함께 EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING 이벤트로 받아오세요.

대부분의 응용은 PIX_OFF_EVENTPIX_ON_EVENT 에만 관심을 둡니다. 트리거 유형은 이벤트를 외부 타이밍 신호와 연관 짓는 데 사용됩니다.

이벤트 버퍼를 (EVT_res, 6) 형태로 할당하세요. 여기서 EVT_res 는 1024와 65536 사이의 2의 거듭제곱입니다. 그런 다음 csi.IOCTL_GENX320_SET_MODEcsi.GENX320_MODE_EVENT 와 버퍼 크기를 지정하여 이벤트 모드로 진입하세요. csi.IOCTL_GENX320_READ_EVENTS 로 이벤트를 읽으면 버퍼를 용량까지 채우고 유효한 행 수를 반환합니다.

Image.draw_event_histogram 는 이벤트를 그레이스케일 이미지로 래스터화합니다 — 각 ON 이벤트에 대해 빈에 contrast 를 더하고, 각 OFF 이벤트에 대해서는 뺍니다. clear=True 는 먼저 이미지를 brightness 로 리셋하고, clear=False 는 여러 호출에 걸쳐 누적합니다:

import csi
import image
import time
from ulab import numpy as np

img = image.Image(320, 320, image.GRAYSCALE)
events = np.zeros((2048, 6), dtype=np.uint16)

csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0])

clock = time.clock()
while True:
    clock.tick()
    n = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events)
    img.draw_event_histogram(events[:n], clear=True, brightness=128, contrast=64)
    img.flush()
    print(n, clock.fps())

히스토그램 모드의 바이어스 프리셋, AFK 필터, 핫 픽셀 보정 ioctl은 모두 이벤트 모드에서도 동일하게 동작합니다 — csi.IOCTL_GENX320_SET_MODE 이후에 호출하세요.

극성으로 필터링하기

ulab으로 이벤트 배열을 슬라이싱하여 ON 이벤트(더 밝은 상태로의 움직임) 또는 OFF 이벤트만 남기세요:

TARGET = csi.PIX_ON_EVENT  # or csi.PIX_OFF_EVENT

events_slice = events[:n]
indices = np.nonzero(events_slice[:, 0] == TARGET)[0]
if len(indices):
    target_events = np.take(events_slice, indices, axis=0)
    img.draw_event_histogram(target_events, clear=True,
                             brightness=128, contrast=64)

장노출 누적

clear=False 로 설정하여 여러 프레임에 걸쳐 같은 이미지에 이벤트를 계속 쌓으세요 — 그 결과는 모션 트레일 시각화입니다. 새 노출을 시작하려면 주기적으로 리셋하세요:

EXPOSURE_FRAMES = 30
i = 0
while True:
    n = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events)
    clear = (i % EXPOSURE_FRAMES) == 0
    img.draw_event_histogram(events[:n], clear=clear, brightness=128, contrast=64)
    img.flush()
    i += 1

고속 처리

이벤트 처리에 CPU를 확보하기 위해 시각화를 생략하세요. N번째 반복마다만 통계를 출력하세요 — 매 반복마다 출력 줄을 내보내는 것은 높은 이벤트 속도에서 병목이 됩니다:

csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0])

clock = time.clock()
i = 0
while True:
    clock.tick()
    n = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events)
    i += 1
    if not i % 10:
        print(f"{n} events  {clock.fps()} fps")

시공간 대비(STC) 필터

실제로 움직이는 대비 에지는 짧은 시간 창 내에 같은 픽셀에서 노이즈가 섞인 이벤트 버스트 를 트리거하는 경향이 있습니다 — 픽셀 불일치와 아날로그 노이즈가 실제 변화 주변에 응용에 쓸모없는 추가 이벤트를 만들어냅니다. STC 필터는 버스트당 하나(또는 몇 개)의 이벤트만 유지하고 나머지는 버리는 칩 내부 후처리입니다.

이 필터는 csi.IOCTL_GENX320_SET_STCGENX320_STC_* 상수로 선택되는 세 가지 전략을 구현합니다. 각 모드는 버스트에서 어떤 이벤트를 전달하는지로 정의됩니다:

모드

유지

버림

csi.GENX320_STC_DISABLE

모든 이벤트

없음

csi.GENX320_STC_ONLY

버스트의 두 번째 이벤트

첫 번째 + 이후 이벤트

csi.GENX320_STC_TRAIL_ONLY

버스트의 첫 번째 이벤트

이후 이벤트

csi.GENX320_STC_TRAIL

첫 번째 + 이후 에지

중복된 노이즈만

자세히 살펴보면:

  • csi.GENX320_STC_DISABLE — 필터 꺼짐, 모든 이벤트가 통과합니다(기본값).

  • csi.GENX320_STC_ONLY — 버스트의 두 번째 이벤트를 유지합니다. 매개변수: stc_threshold (ms). 한 픽셀에서 이전 이벤트로부터 stc_threshold 이내에 새 이벤트가 도착하면 버스트의 “두 번째”로 간주되어 전달됩니다 — 첫 번째 이벤트와 같은 버스트 내의 이후 이벤트는 걸러집니다. 맨 처음 히트보다 노이즈로 확인된 변화를 원할 때 가장 적합합니다.

  • csi.GENX320_STC_TRAIL_ONLY — 버스트의 첫 번째 이벤트를 유지합니다. 매개변수: trail_threshold (ms). 픽셀이 발화한 후, trail_threshold 가 경과할 때까지 같은 픽셀의 이후 이벤트는 버려집니다. 선두 에지의 정확한 타이밍을 보존합니다 — 버스트 확인보다 극성 전환 순간이 더 중요할 때 유용합니다.

  • csi.GENX320_STC_TRAIL — 두 가지를 결합합니다. 매개변수: stc_thresholdtrail_threshold (둘 다 ms). Trail 모드처럼 선두 에지를 유지하고 STC 모드처럼 이후 에지도 유지하므로 버스트의 여러 이벤트가 여전히 통과됩니다 — 단일 모드 필터보다 이벤트 처리량은 높지만 가장 풍부한 신호를 제공합니다.

두 임계값은 대략 13:1 비율 이내로 유지되어야 합니다 — 센서는 한쪽이 다른 쪽의 약 13배를 초과하는 설정을 거부합니다:

csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_TRAIL, 1, 2)
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_DISABLE)

버퍼 깊이

이벤트 속도가 급증하면 기본 삼중 버퍼 파이프라인은 최신 프레임을 선호하고 오래된 프레임을 버립니다. 대신 이벤트를 큐에 넣으려면 csi.CSI.framebuffers 를 통해 FIFO 깊이를 늘리세요 — 호스트가 뒤처질 때 약간 오래된 데이터를 처리하게 되는 대가가 따릅니다:

csi0.framebuffers(10)  # FIFO depth, > 3 enables queueing

데스크톱 스트리밍 및 시각화

호스트 PC에서의 실시간 GUI 시각화를 위해, openmv-projects 저장소의 GenX320 이벤트 스트리밍 도구 가 카메라를 DearPyGui 프런트엔드와 연결합니다. PC GUI는 두 개의 시각화를 나란히 실행합니다: 이벤트 누적 캔버스(Image.draw_event_histogram 와 같은 발상이지만 선택 가능한 팔레트와 슬라이딩 윈도우 대 자동 클리어 모드를 갖춤)와 IIR 대역 통과 필터로 구동되는 픽셀별 주파수 맵 — 이벤트 스트림에서 주기적 신호(회전하는 팬, 깜빡이는 LED 등)를 바로 찾아내는 데 유용합니다.

두 개의 온캠 스트리밍 스크립트가 함께 제공됩니다:

  • 처리 모드 (genx320_event_mode_streaming_on_cam.py) — 카메라가 csi.IOCTL_GENX320_READ_EVENTS 로 이벤트를 디코딩하고 각 행을 USB를 통해 12바이트로 스트리밍합니다([0] 유형, [1] 초, [2] ms, [3] us, [4] x, [5] y). 와이어 형식이 온캠 ndarray 형식과 일치하므로 PC에서 소비하기 쉽습니다.

  • 원시 모드 (genx320_raw_event_mode_streaming_on_cam.py) — 카메라가 csi.IOCTL_GENX320_READ_EVENTS_RAW 를 통해 칩의 기본 32비트 패킹 이벤트 워드를 스트리밍합니다. 이는 처리 모드의 12바이트 대비 이벤트당 4바이트(USB를 통한 데이터가 약 3배 적음)이므로, 링크가 병목일 때 달성 가능한 이벤트 속도가 약 3배 높아집니다. PC는 벡터화된 numpy를 사용하여 패킹된 워드를 동일한 6열 이벤트 레이아웃으로 다시 디코딩하므로, 다운스트림 시각화 코드는 동일합니다.

GenX320이 생성할 수 있는 속도에서는 USB 처리량이 결정적 제약이기 때문에 GUI에서는 원시 모드가 기본값입니다. 온캠 스크립트에 처리 로직을 끼워 넣어야 한다면 처리 모드로 전환하세요.