Câmera de Eventos Multiespectral

O Módulo de Câmera de Eventos Multiespectral combina o sensor de eventos GENX320 com um sensor de cor PAG7936 de global-shutter de 1 MP em um único módulo — um pipeline sincronizado de eventos + cor para rastreamento de objetos em alta velocidade, rastreamento de LEDs, fluxo de fluidos e outras cenas dinâmicas.

Câmera de Eventos Multiespectral

Para o datasheet completo, fotos e informações de compra, consulte a página do produto Multispectral Event Camera.

Nota

Suportado apenas na OpenMV N6.

Destaques

  • Sensor de eventos de 320x320, faixa dinâmica >140 dB, histogramas a 375 Hz+

  • PAG7936 a cores: 1280x800 @ 120 FPS, 640x400 @ 240 FPS

  • Timestamps de eventos sincronizados com gatilho de exposição compartilhado

  • Enxerga abaixo de 5 lux sem exposição automática

  • Consumo a partir de ~3 mW para streaming de eventos

  • Voltado para rastreamento em alta velocidade, rastreamento de LEDs e fluxo de fluidos/partículas

Uso

O sensor de cor e o sensor de eventos GENX320 recebem cada um sua própria instância de csi.CSI. A primeira chamada usa por padrão o sensor primário (o PAG7936); a segunda se vincula ao GENX320 passando cid= csi.GENX320. Faça um hard-reset no sensor de cor com csi.CSI.reset (hard=True) para energizar o trilho, e configure o GENX320 com hard=False para que seu driver apenas reprograme o chip sem reativar o reset.

O GENX320 produz 320x320 em modo de histograma; o PAG7936 em csi.QVGA produz 320x200. A sobreposição básica abaixo recorta as 120 linhas inferiores do quadro do GENX320. Use a transformação de homografia (abaixo) para uma sobreposição ajustada ou um framesize maior do PAG7936.

Dois buffers de rascunho permanecem constantes ao longo do loop de quadros — uma paleta alfa de 256x1 armazenada como uma image.Image, de modo que os pixels do histograma na linha de base de cinza médio (128) se tornem transparentes e tanto os realces de eventos ON quanto as sombras de eventos OFF se tornem opacos, e um frame buffer do GENX320 pré-alocado com image.Image, para que csi.CSI.snapshot (blocking=False, image=...) possa preenchê-lo no lugar a cada iteração sem realocar:

import time
import csi
import image
import math

# V-shaped alpha: pixels far from the baseline 128 become opaque.
alpha_pal = image.Image(256, 1, image.GRAYSCALE)
for i in range(256):
    alpha_pal[i] = int(math.pow(abs(i - 128) / 128.0, 2) * 255)

# Setup the color camera sensor.
csi0 = csi.CSI()
csi0.reset(hard=True)  # force hardware reset.
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)

csi1 = csi.CSI(cid=csi.GENX320)
csi1.reset(hard=False)  # no hardware reset - just configure GENX320
csi1.pixformat(csi.GRAYSCALE)
csi1.framesize((320, 320))
csi1.brightness(128)  # histogram baseline (default)
csi1.contrast(64)     # per-event step

clock = time.clock()

img1 = image.Image(csi1.width(), csi1.height(), csi1.pixformat())

while True:
    clock.tick()
    img0 = csi0.snapshot()
    csi1.snapshot(blocking=False, image=img1)
    img0.draw_image(img1, 0, 0, color_palette=image.PALETTE_EVT_LIGHT,
                    alpha_palette=alpha_pal,
                    hint=image.BILINEAR)
    print(clock.fps())

Cada iteração captura um snapshot de cor com bloqueio e um snapshot do GENX320 sem bloqueio. Image.draw_image então compõe os dois: color_palette= image.PALETTE_EVT_LIGHT (ou image.PALETTE_EVT_DARK para um fundo escuro) mapeia o histograma em escala de cinza do GENX320 em uma rampa de cores, alpha_palette= mescla cada pixel usando o mapa alfa em forma de V para que as regiões silenciosas da cena deixem passar a imagem de cor, e hint= image.BILINEAR suaviza o aumento de escala quando o sensor de cor opera em resolução mais alta que o GENX320.

Os ioctls de presets de bias, filtro AFK, calibração de hot-pixel e filtro STC do GENX320 funcionam todos da mesma forma nesta configuração de câmera dupla — chame-os em csi1 após csi.CSI.reset. Veja as seções abaixo para detalhes.

Alinhamento acelerado por GPU

Image.draw_image aceita um argumento transform= — uma matriz de homografia 3x3 como um array 2-D de ulab.numpy. Na OpenMV N6, a GPU executa a projeção por pixel durante o mesmo desenho, de modo que o quadro do GENX320 pode ser realinhado à perspectiva da câmera de cor sem uma passagem de warp separada — útil quando os dois sensores têm óptica ou campos de visão ligeiramente diferentes, ou quando a câmera de cor opera em resolução mais alta. Calibre a matriz por câmera com a ferramenta GenX320 Overlay Calibration, que exibe um tabuleiro de xadrez piscante para que o sensor de eventos produza eventos de canto sem qualquer movimento físico:

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

# Calibration matrix from the GenX320 Overlay Calibration tool.
m = np.array([
    [2.000000, 0.000000,   0.000000],
    [0.000000, 2.000000,  80.000000],
    [0.000000, 0.000000,   1.000000],
])

alpha_pal = image.Image(256, 1, image.GRAYSCALE)
for i in range(256):
    alpha_pal[i] = int(math.pow(abs(i - 128) / 128.0, 2) * 255)

# Setup the color camera sensor.
csi0 = csi.CSI()
csi0.reset(hard=True)
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)

csi1 = csi.CSI(cid=csi.GENX320)
csi1.reset(hard=False)
csi1.pixformat(csi.GRAYSCALE)
csi1.framesize((320, 320))
csi1.brightness(128)
csi1.contrast(64)

clock = time.clock()

img1 = image.Image(csi1.width(), csi1.height(), csi1.pixformat())

while True:
    clock.tick()
    img0 = csi0.snapshot()
    csi1.snapshot(blocking=False, image=img1)
    img0.draw_image(img1, 0, 0, color_palette=image.PALETTE_EVT_LIGHT,
                    alpha_palette=alpha_pal,
                    hint=image.BILINEAR,
                    transform=m)
    print(clock.fps())

Esta variante executa a câmera de cor em csi.VGA (640x480) e o GENX320 em sua resolução nativa de 320x320 — a homografia projeta o quadro menor do GENX320 dentro do quadro maior de cor como parte do desenho, de modo que o fator de aumento de escala é incorporado à própria matriz em vez de ser aplicado separadamente.

Detalhes da câmera de eventos

O GENX320 é um sensor de visão baseado em eventos — em vez de ler toda a matriz 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 borramento por 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 de 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, de modo que todas as rotinas padrão de processamento de imagem (Image.find_blobs, paletas, etc.) funcionam diretamente.

  • Modo de evento — os eventos brutos são transmitidos para um ndarray numpy com timestamps completos em microssegundos, para aplicações que precisam do detalhe temporal em vez de um quadro pré-agrupado.

Modo de histograma

No modo de histograma, o GENX320 produz quadros em escala de cinza em que cada pixel codifica a atividade recente de eventos naquele local. Pixels acima da linha de base de brilho são eventos ON (brilho aumentando), abaixo são eventos OFF (brilho diminuindo). O brilho de linha de base 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 botões que moldam a saída do histograma.

Saída colorizada

Defina csi.CSI.color_palette como image.PALETTE_EVT_LIGHT para um fundo claro ou image.PALETTE_EVT_DARK para um escuro — o driver emite quadros RGB565 usando a paleta diretamente:

csi0.color_palette(image.PALETTE_EVT_LIGHT)

Calibração de hot-pixel

Sensores de eventos acumulam “hot pixels” que disparam espuriamente. Execute csi.IOCTL_GENX320_CALIBRATE contra uma cena estática para desabilitá-los. O driver constrói uma contagem de acertos por pixel de 320x320, calcula a média e o desvio padrão, e desabilita qualquer pixel cuja contagem esteja acima de mean + sigma * stddev — então os pixels desabilitados 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 ultrapasse 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 desabilitados); valores mais altos são mais conservadores. 0.5 é um bom padrão.

Aponte o sensor para uma cena estática primeiro, para que quaisquer eventos gerados por movimento não sejam contabilizados contra pixels que estão realmente em bom estado:

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, telas acionadas 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 via 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

Presets de bias

Cada pixel do GenX320 executa um front-end analógico com vários biases configuráveis. Eles governam em conjunto 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 aumentou nessa quantidade. Mais baixo = mais sensível a transições para mais 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 mais 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), mas 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, e 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 stream silencioso. Chame csi.IOCTL_GENX320_SET_BIASES com um preset diferente quando a aplicação precisar de mais sensibilidade ou largura de banda.

csi.IOCTL_GENX320_SET_BIASES aplica um de cinco presets:

  • 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 ajustado para 0 para que mudanças lentas de brilho ainda sejam registradas — uma cena de pouca luz gera poucos eventos por si só, então queremos que o maior número possível chegue até nós.

  • 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 piscada seja capturada consecutivamente. O resultado: um stream que é quase só 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 a DEFAULT (menos sensível) e FO reduzido (pixel mais lento = pixel mais silencioso). Melhor 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 deriva lenta de brilho, e REFR elevado para que uma única borda em 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 de forma independente — escolha um preset como ponto de partida e depois 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 de histograma é apenas uma imagem em escala de cinza, o rastreamento de blobs comum funciona diretamente. Para rastrear um LED de marcador ativo, carregue o preset 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 evento

O modo de evento contorna o histograma no chip e transmite eventos brutos para um ndarray 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 comparador DIFF_OFF foi cruzado). X/Y apontam para o pixel que disparou.

  • csi.PIX_ON_EVENT — um pixel detectou um aumento de brilho (o limiar DIFF_ON foi cruzado). X/Y apontam para o pixel.

  • csi.EXT_TRIGGER_FALLING — o pino de gatilho externo do sensor viu uma borda de descida. X/Y não são usados.

  • csi.EXT_TRIGGER_RISING — o pino de gatilho externo do sensor viu uma borda de subida. X/Y não são usados.

  • csi.RST_TRIGGER_FALLING — gatilho 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 — gatilho de reset de pixel, borda de subida. X/Y não são usados. Não é gerado pelo firmware no momento.

A entrada de gatilho externo do GENX320 está conectada à linha de frame-sync da câmera, que também é roteada para P10 tanto no processador quanto no header de pinos — acione P10 para injetar bordas de sincronização no stream de eventos e capturá-las como eventos EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING junto aos dados de pixel.

A maioria das aplicações só se importa com PIX_OFF_EVENT e PIX_ON_EVENT; os tipos de gatilho permitem correlacionar eventos com sinais de temporização externos.

Aloque o buffer de eventos com o formato (EVT_res, 6) onde EVT_res é uma potência de dois entre 1024 e 65536, depois entre no modo de evento 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())

Os ioctls de presets de bias do modo de histograma, filtro AFK e calibração de hot-pixel funcionam todos da mesma forma no modo de 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 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 em alta velocidade

Descarte a visualização para liberar CPU para o processamento de eventos. Imprima estatísticas apenas a cada N-ésima iteração — enviar uma linha de print a cada iteração se torna o gargalo em taxas de eventos altas:

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 real em movimento tende a disparar uma rajada ruidosa de eventos no mesmo pixel dentro de uma curta janela de tempo — descasamento 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 alguns) eventos por rajada e descarta o restante.

Ele implementa três estratégias, selecionadas via 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

csi.GENX320_STC_DISABLE

todos os eventos

nada

csi.GENX320_STC_ONLY

segundo evento de uma rajada

primeiro + eventos posteriores

csi.GENX320_STC_TRAIL_ONLY

primeiro evento de uma rajada

eventos subsequentes

csi.GENX320_STC_TRAIL

primeiro + 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 de stc_threshold de 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. Melhor quando você quer uma transição confirmada por ruído em vez do primeiro disparo.

  • 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é que trail_threshold tenha decorrido. Preserva a temporização precisa da borda de ataque — útil quando o momento da troca de polaridade importa mais que a confirmação da rajada.

  • csi.GENX320_STC_TRAIL — combina ambos. Parâmetros: stc_threshold e trail_threshold (ambos em ms). Mantém a borda de ataque conforme o modo Trail mais as bordas subsequentes conforme o modo STC, de modo que múltiplos eventos de uma rajada ainda passam — maior throughput de eventos que os filtros de modo único, mas 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 padrão de triplo buffer favorece o quadro mais recente e descarta os antigos. Aumente a profundidade do FIFO via 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 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 de 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 acionado por um filtro passa-banda IIR — útil para detectar sinais periódicos (ventiladores girando, LEDs piscando, etc.) diretamente no stream de eventos.

Ela vem com dois scripts de streaming na câmera:

  • Modo processado (genx320_event_mode_streaming_on_cam.py) — a câmera decodifica os eventos com csi.IOCTL_GENX320_READ_EVENTS e transmite cada linha como 12 bytes via USB ([0] tipo, [1] seg, [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â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 de csi.IOCTL_GENX320_READ_EVENTS_RAW. Isso são 4 bytes por evento contra 12 no modo processado (cerca de 3x menos dados via USB), portanto uma taxa de eventos alcançável ~3x mais alta 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, de modo que o código do visualizador a jusante é idêntico.

O modo bruto é o padrão na GUI porque o throughput de USB é a restrição limitante nas taxas que o GenX320 pode produzir; mude para o modo processado se você precisar conectar lógica de processamento ao script na câmera.