Câmera de Eventos GENX320¶
O Módulo de Câmera de Eventos GENX320 é um sensor de visão baseado em eventos da Prophesee com resolução de 320x320 e precisão temporal de microssegundos.
Para o datasheet completo, fotos e informações de compra, consulte a página do produto Câmera de Eventos GENX320.
Nota
Compatível com a OpenMV H7 Plus, RT1062 e N6.
Destaques¶
Sensor de visão baseado em eventos de 320x320
Faixa dinâmica de 140 dB, sem desfoque de movimento
Taxa de saída de histograma de eventos de 375 Hz+
O consumo de energia escala com a atividade da cena — começa em ~3 mW
Opera de <5 lux até luz solar intensa sem exposição automática
Gera quadros em escala de cinza ou fluxos de eventos brutos
Uso¶
O GENX320 é um sensor de visão baseado em eventos — em vez de ler todo o array de 320x320 em um clock de quadro fixo, cada pixel reporta “eventos” assíncronos no instante em que detecta uma mudança de brilho. Cada evento carrega uma coordenada X/Y, uma polaridade ON/OFF (claro→escuro ou escuro→claro) e um timestamp em microssegundos. É daí que vêm a precisão temporal de microssegundos do sensor, a ausência de desfoque de movimento, a faixa dinâmica muito alta e o consumo de energia proporcional à atividade. Cenas estáticas não geram dados.
O firmware da OpenMV expõe o GENX320 através de csi.CSI com cid= csi.GENX320. Dois modos de operação estão disponíveis:
Modo histograma (padrão) — os eventos são acumulados no chip em bins por pixel e reportados como um quadro em escala de cinza de 320x320 a uma taxa configurável (~20-350 FPS). O sensor se comporta como uma câmera comum, então todas as rotinas padrão de processamento de imagem (
Image.find_blobs, paletas, etc.) funcionam diretamente.Modo de eventos — os eventos brutos são transmitidos para um
ndarraydo numpy com timestamps completos em microssegundos, para aplicações que precisam do detalhe temporal em vez de um quadro pré-binado.
Modo histograma¶
No modo histograma, o GENX320 gera quadros em escala de cinza onde cada pixel codifica a atividade de eventos recente naquela posição. Pixels acima da linha de base de brilho são eventos ON (brilho aumentando), abaixo são eventos OFF (brilho diminuindo). A linha de base de brilho padrão é 128 e o passo de contraste por evento é 16 — aumente o contraste para fazer os eventos se destacarem:
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 e csi.CSI.framerate são os três controles que moldam a saída do histograma.
Saída colorida¶
Defina csi.CSI.color_palette como image.PALETTE_EVT_LIGHT para um fundo claro ou image.PALETTE_EVT_DARK para um fundo escuro — o driver emite quadros RGB565 usando a paleta diretamente:
csi0.color_palette(image.PALETTE_EVT_LIGHT)
Calibração de pixels quentes¶
Sensores de eventos acumulam “pixels quentes” que disparam espuriamente. Execute csi.IOCTL_GENX320_CALIBRATE contra uma cena estática para desativá-los. O driver constrói uma contagem de acertos por pixel de 320x320, calcula a média e o desvio padrão, e desativa qualquer pixel cuja contagem esteja acima de mean + sigma * stddev — então os pixels desativados param de emitir eventos no nível do sensor.
Dois parâmetros controlam a calibração:
event_count— quantos eventos contabilizar antes de calcular as estatísticas. O loop captura quadros até que o total acumulado de eventos cruze esse orçamento. Contagens mais altas dão uma estimativa mais confiável ao custo de um tempo de calibração mais longo.10000é um ponto de partida razoável.sigma— multiplicador de limiar sobre o desvio padrão. Valores mais baixos são mais agressivos (mais pixels desativados); valores mais altos são mais conservadores.0.5é um bom padrão.
Aponte o sensor primeiro para uma cena estática para que quaisquer eventos causados por movimento não sejam contabilizados contra pixels que estão realmente bons:
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")
Filtro anti-flicker (AFK)¶
Fontes de luz periódicas (fluorescentes, displays acionados por LED) geram volumes enormes de eventos redundantes. O filtro AFK rejeita eventos cujo pixel alterna em uma frequência dentro de uma banda — habilite-o através de csi.IOCTL_GENX320_SET_AFK com as bordas da banda em hertz:
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 1, 130, 160) # 130-160 Hz
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 0) # disable
Predefinições de bias¶
Cada pixel no GenX320 executa um front-end analógico com vários biases configuráveis. Eles governam conjuntamente a sensibilidade, o ruído, a largura de banda do pixel e a taxa de eventos — a combinação certa depende da cena. Os biases individuais são:
DIFF_ON — o limiar de contraste do comparador positivo. Um pixel emite um evento ON quando sua log-iluminação subiu nesse valor. Mais baixo = mais sensível a transições para o claro.
DIFF_OFF — o limiar de contraste do comparador negativo (a contraparte simétrica para eventos OFF). Mais baixo = mais sensível a transições para o escuro.
FO — a frequência de corte passa-baixas do pixel. Mais alto = largura de banda do pixel mais ampla (resposta mais rápida, menor latência), porém mais atividade de ruído de fundo.
HPF — a frequência de corte passa-altas. Mais alto = rejeição mais forte de mudanças lentas de brilho; apenas transições rápidas alcançam os comparadores. Útil para ignorar a deriva ambiente.
REFR — o período refratário. Após um pixel disparar, ele permanece em reset por esse tempo antes de poder disparar novamente. Mais alto = tempo morto mais longo, útil para limitar a taxa de eventos por pixel.
Após csi.CSI.reset, o driver aplica csi.GENX320_BIASES_LOW_NOISE, não csi.GENX320_BIASES_DEFAULT — os padrões do datasheet emitem uma taxa de eventos de fundo muito mais alta, então LOW_NOISE é usado como ponto de partida para manter o fluxo silencioso. Chame csi.IOCTL_GENX320_SET_BIASES com uma predefinição diferente quando a aplicação precisar de mais sensibilidade ou largura de banda.
csi.IOCTL_GENX320_SET_BIASES aplica uma de cinco predefinições:
csi.GENX320_BIASES_DEFAULT— padrões do datasheet do GenX320. Sensibilidade, ruído e largura de banda equilibrados para cenas gerais.csi.GENX320_BIASES_LOW_LIGHT— ambos os limiares de contraste afrouxados para maior sensibilidade, FO reduzido para manter o ruído baixo, e HPF definido como 0 para que mudanças lentas de brilho ainda sejam registradas — uma cena com pouca luz gera poucos eventos por si só, então queremos que o máximo possível seja captado.csi.GENX320_BIASES_ACTIVE_MARKER— ajustado para rastrear LEDs piscantes de alto contraste. Limiares de contraste elevados para que apenas transições nítidas disparem; FO e HPF aumentados ao máximo para maximizar a largura de banda do pixel e rejeitar qualquer deriva ambiente lenta; REFR reduzido a 0 para que cada borda de pisca seja capturada uma após a outra. O resultado: um fluxo composto quase inteiramente de bordas de LED, fácil de rastrear.csi.GENX320_BIASES_LOW_NOISE— padrão do driver. Ambos os limiares de contraste elevados em relação aoDEFAULT(menos sensível) e FO reduzido (pixel mais lento = pixel mais silencioso). Ideal para cenas estáticas ou lentas onde eventos falsos dominariam de outra forma.csi.GENX320_BIASES_HIGH_SPEED— FO aumentado para que cada pixel possa responder mais rápido, HPF elevado para rejeitar a deriva lenta de brilho, e REFR elevado para que uma única borda de movimento rápido não inunde a leitura — o tempo morto mais longo mantém o volume de eventos limitado sob movimento intenso.
Sobrescreva biases individuais com csi.IOCTL_GENX320_SET_BIAS mais um de csi.GENX320_BIAS_DIFF_ON, csi.GENX320_BIAS_DIFF_OFF, csi.GENX320_BIAS_FO, csi.GENX320_BIAS_HPF ou csi.GENX320_BIAS_REFR e um valor de DAC. Cada bias é definido independentemente — escolha uma predefinição como ponto de partida e então ajuste os biases que sua cena precisar:
csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_LOW_LIGHT)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF, 20)
Rastreamento¶
Como a saída do modo histograma é apenas uma imagem em escala de cinza, o rastreamento de blob comum funciona diretamente. Para rastrear um LED de marcador ativo, carregue a predefinição de bias de marcador ativo e encontre blobs na extremidade clara do histograma:
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())
Modo de eventos¶
O modo de eventos contorna o histograma no chip e transmite eventos brutos para um ndarray do numpy. Cada evento é uma linha de seis colunas uint16:
[0]tipo de evento — veja abaixo[1]timestamp em segundos[2]timestamp em milissegundos[3]timestamp em microssegundos[4]coordenada X, 0-319[5]coordenada Y, 0-319
O driver emite seis tipos de evento na coluna [0]:
csi.PIX_OFF_EVENT— um pixel detectou uma diminuição de brilho (o limiar do comparadorDIFF_OFFfoi cruzado). X/Y apontam para o pixel que disparou.csi.PIX_ON_EVENT— um pixel detectou um aumento de brilho (o limiarDIFF_ONfoi cruzado). X/Y apontam para o pixel.csi.EXT_TRIGGER_FALLING— o pino de trigger externo do sensor viu uma borda de descida. X/Y não são usados.csi.EXT_TRIGGER_RISING— o pino de trigger externo do sensor viu uma borda de subida. X/Y não são usados.csi.RST_TRIGGER_FALLING— trigger de reset de pixel, borda de descida. X/Y não são usados. Não gerado pelo firmware no momento.csi.RST_TRIGGER_RISING— trigger de reset de pixel, borda de subida. X/Y não são usados. Não gerado pelo firmware no momento.
A entrada de trigger externo do GENX320 está conectada à linha de sincronização de quadro da câmera, que também é roteada para o P10 tanto no processador quanto no cabeçalho de pinos — acione P10 para injetar bordas de sincronização no fluxo de eventos e captá-las como eventos EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING junto com os dados de pixel.
A maioria das aplicações só se importa com PIX_OFF_EVENT e PIX_ON_EVENT; os tipos de trigger permitem correlacionar eventos com sinais de temporização externos.
Aloque o buffer de eventos com formato (EVT_res, 6) onde EVT_res é uma potência de dois entre 1024 e 65536, então entre no modo de eventos através de csi.IOCTL_GENX320_SET_MODE com csi.GENX320_MODE_EVENT e o tamanho do buffer. Leia os eventos com csi.IOCTL_GENX320_READ_EVENTS, que preenche o buffer até sua capacidade e retorna o número de linhas válidas.
Image.draw_event_histogram rasteriza eventos em uma imagem em escala de cinza — para cada evento ON ele adiciona contrast ao bin; para cada evento OFF ele subtrai. clear=True redefine a imagem para brightness primeiro; clear=False acumula ao longo de muitas chamadas:
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())
As predefinições de bias do modo histograma, o filtro AFK e os ioctls de calibração de pixels quentes funcionam todos da mesma forma no modo de eventos — chame-os após csi.IOCTL_GENX320_SET_MODE.
Filtragem por polaridade¶
Fatie o array de eventos com ulab para manter apenas eventos ON (movimento para um estado mais claro) ou apenas eventos 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)
Acumulação de longa exposição¶
Defina clear=False para continuar empilhando eventos na mesma imagem ao longo de muitos quadros — o resultado é uma visualização de rastro de movimento. Redefina periodicamente para iniciar uma nova exposição:
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
Processamento de alta velocidade¶
Descarte a visualização para liberar CPU para o processamento de eventos. Imprima as estatísticas apenas a cada N-ésima iteração — emitir uma linha de print a cada iteração se torna o gargalo em altas taxas de eventos:
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")
Filtro de contraste espaço-temporal (STC)¶
Uma borda de contraste em movimento real tende a disparar uma rajada ruidosa de eventos no mesmo pixel dentro de uma curta janela de tempo — o descasamento de pixel e o ruído analógico produzem eventos extras ao redor da transição genuína que não são úteis para a aplicação. O filtro STC é um pós-processamento no chip que mantém apenas um (ou alguns) eventos por rajada e descarta o restante.
Ele implementa três estratégias, selecionadas através de csi.IOCTL_GENX320_SET_STC e uma constante GENX320_STC_*. Cada modo é definido por quais eventos ele encaminha de uma rajada:
Modo |
Mantém |
Descarta |
|---|---|---|
todos os eventos |
nada |
|
segundo evento de uma rajada |
primeiro + eventos posteriores |
|
primeiro evento de uma rajada |
eventos subsequentes |
|
primeira borda + bordas subsequentes |
apenas ruído redundante |
Em detalhe:
csi.GENX320_STC_DISABLE— filtro desligado, todos os eventos passam (padrão).csi.GENX320_STC_ONLY— mantém o segundo evento de uma rajada. Parâmetro:stc_threshold(ms). Se um novo evento em um pixel chega dentro destc_thresholdde um evento anterior, ele é considerado o “segundo” de uma rajada e é encaminhado — o primeiro evento e quaisquer eventos subsequentes na mesma rajada são filtrados. Ideal quando você quer uma transição confirmada por ruído em vez do primeiríssimo acerto.csi.GENX320_STC_TRAIL_ONLY— mantém o primeiro evento de uma rajada. Parâmetro:trail_threshold(ms). Após um pixel disparar, os eventos subsequentes no mesmo pixel são descartados até quetrail_thresholdtenha decorrido. Preserva a temporização precisa da borda de subida — útil quando o momento da troca de polaridade importa mais do que a confirmação da rajada.csi.GENX320_STC_TRAIL— combina ambos. Parâmetros:stc_thresholdetrail_threshold(ambos em ms). Mantém a borda de subida conforme o modo Trail mais as bordas subsequentes conforme o modo STC, então múltiplos eventos de uma rajada ainda passam — throughput de eventos mais alto do que os filtros de modo único, mas com o sinal mais rico.
Os dois limiares devem permanecer dentro de uma razão de aproximadamente 13:1 — o sensor rejeita configurações onde um é mais de ~13x o outro:
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_TRAIL, 1, 2)
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_DISABLE)
Profundidade do buffer¶
Quando as taxas de eventos disparam, o pipeline de buffer triplo padrão favorece o quadro mais recente e descarta os antigos. Aumente a profundidade do FIFO através de csi.CSI.framebuffers para enfileirar eventos em vez disso — ao custo de processar dados ligeiramente mais antigos quando o host fica para trás:
csi0.framebuffers(10) # FIFO depth, > 3 enables queueing
Streaming e visualização em desktop¶
Para visualização de GUI em tempo real em um PC host, a ferramenta GenX320 Event Streaming no repositório openmv-projects combina a câmera com um front-end DearPyGui. A GUI do PC executa duas visualizações lado a lado: um canvas de acumulação de eventos (mesma ideia que Image.draw_event_histogram, mas com paletas selecionáveis e modos de janela deslizante vs. auto-limpeza) e um mapa de frequência por pixel acionado por um filtro passa-banda IIR — útil para detectar sinais periódicos (ventiladores rotativos, LEDs piscantes, etc.) diretamente no fluxo de eventos.
Ela inclui dois scripts de streaming na câmera:
Modo processado (
genx320_event_mode_streaming_on_cam.py) — a câmera decodifica eventos comcsi.IOCTL_GENX320_READ_EVENTSe transmite cada linha como 12 bytes por USB ([0]tipo,[1]seg,[2]ms,[3]us,[4]x,[5]y). Fácil de consumir no PC porque o formato de fio corresponde ao formato ndarray da câmera.Modo bruto (
genx320_raw_event_mode_streaming_on_cam.py) — a câmera transmite as palavras de evento empacotadas nativas de 32 bits do chip através decsi.IOCTL_GENX320_READ_EVENTS_RAW. Isso é 4 bytes por evento versus 12 no modo processado (cerca de 3x menos dados por USB), então uma taxa de eventos alcançável ~3x maior quando o link é o gargalo. O PC decodifica as palavras empacotadas de volta para o mesmo layout de evento de 6 colunas usando numpy vetorizado, então o código do visualizador a jusante é idêntico.
O modo bruto é o padrão na GUI porque o throughput do USB é a restrição limitante nas taxas que o GenX320 pode produzir; mude para o modo processado se você precisar inserir lógica de processamento no script da câmera.