Câmara de Eventos GENX320¶
O Módulo de Câmara de Eventos GENX320 é um sensor de visão baseado em eventos da Prophesee com resolução 320x320 e precisão temporal de microssegundos.
Para ficha técnica completa, fotografias e encomendas, consulte a página de produto da Câmara de Eventos GENX320.
Nota
Compatível com a OpenMV H7 Plus, RT1062 e N6.
Destaques¶
Sensor de visão baseado em eventos 320x320
Intervalo dinâmico de 140 dB, sem desfocagem de movimento
Taxa de saída de histograma de eventos de 375 Hz ou superior
Consumo proporcional à atividade da cena — começa em ~3 mW
Funciona desde <5 lux até luz solar intensa sem exposição automática
Emite fotogramas em escala de cinzentos ou fluxos de eventos em bruto
Utilização¶
O GENX320 é um sensor de visão baseado em eventos — em vez de ler todo o array 320x320 com um relógio de fotograma fixo, cada pixel reporta «eventos» assíncronos no instante em que deteta uma variação de luminosidade. Cada evento contém uma coordenada X/Y, uma polaridade ON/OFF (claro→escuro ou escuro→claro) e um carimbo temporal em microssegundos. É daí que provêm a precisão temporal em microssegundos, a ausência de desfocagem de movimento, o intervalo dinâmico muito elevado e o consumo proporcional à atividade. Cenas estáticas não geram quaisquer dados.
O firmware OpenMV expõe o GENX320 através de csi.CSI com cid= csi.GENX320. Estão disponíveis dois modos de funcionamento:
Modo histograma (predefinição) — os eventos são acumulados no chip em contadores por pixel e reportados como um fotograma em escala de cinzentos 320x320 a uma taxa configurável (~20-350 FPS). O sensor comporta-se como uma câmara normal, pelo que todas as rotinas padrão de processamento de imagem (
Image.find_blobs, paletas, etc.) funcionam diretamente.Modo evento — os eventos em bruto são transmitidos para um
ndarraynumpy com carimbos temporais completos em microssegundos, para aplicações que necessitem do detalhe temporal em vez de um fotograma pré-binado.
Modo histograma¶
No modo histograma, o GENX320 emite fotogramas em escala de cinzentos em que cada pixel codifica a atividade recente de eventos nessa localização. Pixels acima do nível de luminosidade de referência são eventos ON (luminosidade a aumentar); abaixo são eventos OFF (luminosidade a diminuir). O nível de luminosidade de referência predefinido é 128 e o passo de contraste por evento é 16 — aumente o contraste para tornar os eventos mais visíveis:
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 parâmetros que moldam a saída do histograma.
Saída colorizada¶
Defina csi.CSI.color_palette para image.PALETTE_EVT_LIGHT para um fundo claro ou image.PALETTE_EVT_DARK para um fundo escuro — o controlador emite fotogramas RGB565 utilizando a paleta diretamente:
csi0.color_palette(image.PALETTE_EVT_LIGHT)
Calibração de pixels quentes¶
Os sensores de eventos acumulam «pixels quentes» que disparam de forma espúria. Execute csi.IOCTL_GENX320_CALIBRATE apontando para uma cena estática para os desativar. O controlador constrói um mapa de contagem de ocorrências por pixel 320x320, calcula a média e o desvio padrão, e desativa qualquer pixel cuja contagem esteja acima de mean + sigma * stddev — os pixels desativados deixam de emitir eventos ao nível do sensor.
Dois parâmetros controlam a calibração:
event_count— número de eventos a contabilizar antes de calcular as estatísticas. O ciclo captura fotogramas até que o total acumulado de eventos ultrapasse este limite. Contagens mais elevadas produzem uma estimativa mais fiável ao custo de um tempo de calibração mais longo.10000é um bom ponto de partida.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 valor predefinido.
Aponte o sensor para uma cena estática primeiro, para que os eventos provocados por movimento não sejam contabilizados contra pixels que funcionam corretamente:
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-cintilação (AFK)¶
Fontes de luz periódicas (fluorescentes, ecrãs com retroiluminação LED) geram grandes volumes de eventos redundantes. O filtro AFK rejeita eventos cujos pixels comutem a uma frequência dentro de uma banda — ative-o através de csi.IOCTL_GENX320_SET_AFK com os limites 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 polarização¶
Cada pixel do GenX320 executa uma frente analógica com várias polarizações configuráveis. Em conjunto, governam a sensibilidade, o ruído, a largura de banda do pixel e a taxa de eventos — a combinação correta depende da cena. As polarizações individuais são:
DIFF_ON — o limiar de contraste do comparador positivo. Um pixel emite um evento ON quando a sua log-iluminação aumentou este valor. Mais baixo = mais sensível a transições para luminosidade.
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 escuridão.
FO — a frequência de corte passa-baixo do pixel. Mais alto = maior largura de banda do pixel (resposta mais rápida, menor latência) mas mais atividade de ruído de fundo.
HPF — a frequência de corte passa-alto. Mais alto = rejeição mais forte de variações lentas de luminosidade; apenas transições rápidas chegam aos comparadores. Útil para ignorar a deriva do ambiente.
REFR — o período refratário. Após um pixel disparar, permanece em reset durante este tempo antes de poder disparar novamente. Mais alto = maior tempo morto, útil para limitar a taxa de eventos por pixel.
Após csi.CSI.reset, o controlador aplica csi.GENX320_BIASES_LOW_NOISE, não csi.GENX320_BIASES_DEFAULT — os valores predefinidos da ficha técnica emitem uma taxa de eventos de fundo muito mais elevada, pelo que 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 necessitar de maior sensibilidade ou largura de banda.
csi.IOCTL_GENX320_SET_BIASES aplica uma de cinco predefinições:
csi.GENX320_BIASES_DEFAULT— valores predefinidos da ficha técnica do GenX320. Sensibilidade, ruído e largura de banda equilibrados para cenas gerais.csi.GENX320_BIASES_LOW_LIGHT— ambos os limiares de contraste alargados para maior sensibilidade, FO reduzido para manter o ruído baixo, e HPF a 0 para que variações lentas de luminosidade ainda sejam registadas — uma cena com pouca luz gera poucos eventos por si só, pelo que queremos que o maior número possível passe.csi.GENX320_BIASES_ACTIVE_MARKER— otimizado para rastreamento de LEDs intermitentes de alto contraste. Limiares de contraste aumentados para que apenas transições nítidas sejam acionadas; FO e HPF elevados ao máximo para maximizar a largura de banda do pixel e rejeitar qualquer deriva lenta do ambiente; REFR a 0 para que cada flanco de intermitência seja capturado consecutivamente. O resultado: um fluxo composto quase exclusivamente por flancos de LED, fácil de rastrear.csi.GENX320_BIASES_LOW_NOISE— predefinição do controlador. Ambos os limiares de contraste aumentados em relação aDEFAULT(menos sensível) e FO reduzido (pixel mais lento = pixel mais silencioso). Ideal para cenas estáticas ou lentas onde os eventos falsos dominariam de outra forma.csi.GENX320_BIASES_HIGH_SPEED— FO aumentado para que cada pixel responda mais rapidamente, HPF aumentado para rejeitar deriva lenta de luminosidade, e REFR aumentado para que um único flanco de movimento rápido não inunde a leitura — o maior tempo morto mantém o volume de eventos limitado sob movimento intenso.
Substitua polarizações individuais com csi.IOCTL_GENX320_SET_BIAS mais uma das constantes 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 DAC. Cada polarização é definida independentemente — escolha uma predefinição como ponto de partida e ajuste as polarizações que a sua cena necessitar:
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 no modo histograma é apenas uma imagem em escala de cinzentos, o rastreamento por manchas funciona diretamente. Para rastrear um LED marcador ativo, carregue a predefinição de polarização para marcador ativo e encontre manchas no extremo brilhante 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 evento¶
O modo evento contorna o histograma do chip e transmite eventos em bruto para um ndarray numpy. Cada evento é uma linha com seis colunas uint16:
[0]tipo de evento — ver abaixo[1]carimbo temporal em segundos[2]carimbo temporal em milissegundos[3]carimbo temporal em microssegundos[4]coordenada X, 0-319[5]coordenada Y, 0-319
O controlador emite seis tipos de eventos na coluna [0]:
csi.PIX_OFF_EVENT— um pixel detetou uma diminuição de luminosidade (o limiar do comparadorDIFF_OFFfoi cruzado). X/Y apontam para o pixel que disparou.csi.PIX_ON_EVENT— um pixel detetou um aumento de luminosidade (o limiarDIFF_ONfoi cruzado). X/Y apontam para o pixel.csi.EXT_TRIGGER_FALLING— o pino de acionamento externo do sensor detetou um flanco descendente. X/Y não são utilizados.csi.EXT_TRIGGER_RISING— o pino de acionamento externo do sensor detetou um flanco ascendente. X/Y não são utilizados.csi.RST_TRIGGER_FALLING— acionamento de reset de pixel, flanco descendente. X/Y não são utilizados. Não gerado pelo firmware neste momento.csi.RST_TRIGGER_RISING— acionamento de reset de pixel, flanco ascendente. X/Y não são utilizados. Não gerado pelo firmware neste momento.
A entrada de acionamento externo do GENX320 está ligada à linha de sincronização de fotogramas da câmara, que também está encaminhada para P10 tanto no processador como no conector de pinos — acione P10 para injetar flancos de sincronização no fluxo de eventos e receba-os como eventos EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING juntamente com os dados de pixel.
A maioria das aplicações apenas precisa de PIX_OFF_EVENT e PIX_ON_EVENT; os tipos de acionamento permitem correlacionar eventos com sinais de temporização externos.
Aloque o buffer de eventos com a forma (EVT_res, 6) onde EVT_res é uma potência de dois entre 1024 e 65536, depois entre no modo evento através de csi.IOCTL_GENX320_SET_MODE com csi.GENX320_MODE_EVENT e o tamanho do buffer. Leia eventos com csi.IOCTL_GENX320_READ_EVENTS, que preenche o buffer até à sua capacidade e devolve o número de linhas válidas.
Image.draw_event_histogram rasteriza eventos numa imagem em escala de cinzentos — para cada evento ON adiciona contrast ao bin; para cada evento OFF subtrai. clear=True repõe a imagem para brightness primeiro; clear=False acumula ao longo de várias 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 polarização do modo histograma, o filtro AFK e os ioctls de calibração de pixels quentes funcionam da mesma forma no modo evento — 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 brilhante) 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 a empilhar eventos na mesma imagem ao longo de muitos fotogramas — o resultado é uma visualização de rastros de movimento. Reinicie 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¶
Elimine a visualização para libertar CPU para processamento de eventos. Imprima estatísticas apenas a cada N-ésima iteração — enviar uma linha de impressão em cada iteração torna-se o bottleneck a taxas elevadas 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 espácio-temporal (STC)¶
Uma aresta de contraste em movimento real tende a acionar uma rajada de eventos ruidosos no mesmo pixel num curto intervalo de tempo — desalinhamento de pixel e ruído analógico produzem eventos extras em torno 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 poucos) eventos por rajada e descarta os restantes.
Implementa três estratégias, selecionadas através de csi.IOCTL_GENX320_SET_STC e uma constante GENX320_STC_*. Cada modo é definido pelos eventos que 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 |
|
primeiro + flancos subsequentes |
apenas ruído redundante |
Em detalhe:
csi.GENX320_STC_DISABLE— filtro desativado, todos os eventos passam (predefinição).csi.GENX320_STC_ONLY— mantém o segundo evento de uma rajada. Parâmetro:stc_threshold(ms). Se um novo evento num pixel chegar dentro destc_thresholdde um evento anterior, é considerado o «segundo» de uma rajada e é encaminhado — o primeiro evento e quaisquer eventos subsequentes na mesma rajada são filtrados. Ideal quando se pretende uma transição confirmada por ruído em vez do primeiro impacto.csi.GENX320_STC_TRAIL_ONLY— mantém o primeiro evento de uma rajada. Parâmetro:trail_threshold(ms). Após um pixel disparar, eventos subsequentes no mesmo pixel são descartados até quetrail_thresholdtenha decorrido. Preserva a temporização precisa do flanco de subida — útil quando o momento de comutação 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 o flanco de subida por modo Trail mais flancos subsequentes por modo STC, pelo que múltiplos eventos de uma rajada ainda passam — maior débito de eventos do que os filtros de modo único, mas o sinal mais rico.
Os dois limiares devem manter-se dentro de um rácio de aproximadamente 13:1 — o sensor rejeita configurações em que 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 aumentam rapidamente, o pipeline de triplo buffer predefinido favorece o fotograma mais recente e descarta os antigos. Aumente a profundidade do FIFO através de csi.CSI.framebuffers para enfileirar eventos — ao custo de processar dados ligeiramente mais antigos quando o host fica para trás:
csi0.framebuffers(10) # FIFO depth, > 3 enables queueing
Transmissão e visualização para desktop¶
Para visualização GUI em tempo real num PC, a ferramenta GenX320 Event Streaming no repositório openmv-projects combina a câmara com uma interface DearPyGui. A GUI do PC executa duas visualizações lado a lado: uma tela de acumulação de eventos (mesmo conceito que Image.draw_event_histogram mas com paletas selecionáveis e modos de janela deslizante vs. limpeza automática) e um mapa de frequência por pixel alimentado por um filtro IIR passa-banda — útil para detetar sinais periódicos (ventoinhas giratórias, LEDs intermitentes, etc.) diretamente no fluxo de eventos.
Inclui dois scripts de transmissão na câmara:
Modo processado (
genx320_event_mode_streaming_on_cam.py) — a câmara descodifica os eventos comcsi.IOCTL_GENX320_READ_EVENTSe transmite cada linha como 12 bytes via USB ([0]tipo,[1]sec,[2]ms,[3]us,[4]x,[5]y). Fácil de consumir no PC porque o formato de transmissão corresponde ao formato ndarray na câmara.Modo bruto (
genx320_raw_event_mode_streaming_on_cam.py) — a câmara transmite as palavras de evento nativas de 32 bits compactadas do chip através decsi.IOCTL_GENX320_READ_EVENTS_RAW. São 4 bytes por evento versus 12 no modo processado (cerca de 3x menos dados via USB), portanto ~3x maior taxa de eventos alcançável quando a ligação é o bottleneck. O PC descodifica as palavras compactadas para o mesmo esquema de evento de 6 colunas usando numpy vetorizado, pelo que o código do visualizador a jusante é idêntico.
O modo bruto é o predefinido na GUI porque o débito USB é o fator limitante às taxas que o GenX320 pode produzir; mude para o modo processado se precisar de incorporar lógica de processamento no script na câmara.