Câmara de Eventos Multispectral

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

Multispectral Event Camera

Para ficha técnica completa, fotografias e encomenda, consulte a página do produto Câmara de Eventos Multispectral.

Nota

Suportado apenas na OpenMV N6.

Destaques

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

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

  • Marcas temporais de eventos sincronizadas com gatilho de exposição partilhado

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

  • Consumo a partir de ~3 mW em modo de streaming de eventos

  • Orientado para rastreamento a alta velocidade, rastreamento de LEDs e fluxo de fluidos/partículas

Utilização

O sensor de cor e o sensor de eventos GENX320 recebem cada um a sua própria instância de csi.CSI. A primeira chamada utiliza por defeito o sensor principal (o PAG7936); a segunda associa-se ao GENX320 passando cid= csi.GENX320. Reinicialize o sensor de cor com csi.CSI.reset (hard=True) para ativar o barramento, e configure o GENX320 com hard=False para que o seu controlador apenas reprograme o chip sem voltar a acionar o reset.

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

Dois buffers de trabalho mantêm-se constantes ao longo do ciclo de fotogramas — uma paleta alfa de 256x1 armazenada como image.Image para que os pixels do histograma no nível base de cinzento médio (128) se tornem transparentes e tanto os realces de eventos ON como as sombras de eventos OFF se tornem opacos, e um buffer de fotograma do GENX320 pré-alocado com image.Image para que csi.CSI.snapshot (blocking=False, image=...) o possa preencher no lugar em 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 obtém uma captura de imagem de cor bloqueante e uma captura de imagem do GENX320 não bloqueante. Image.draw_image compõe depois as duas: color_palette= image.PALETTE_EVT_LIGHT (ou image.PALETTE_EVT_DARK para fundo escuro) mapeia o histograma em escala de cinzentos do GENX320 numa rampa de cor, alpha_palette= mistura cada pixel usando o mapa alfa em forma de V para que as regiões silenciosas da cena sejam substituídas pela imagem de cor, e hint= image.BILINEAR suaviza o aumento de escala quando o sensor de cor funciona a uma resolução superior à do GENX320.

Os ioctls de predefinições de bias, filtro AFK, calibração de pixels quentes e filtro STC do GENX320 funcionam da mesma forma nesta configuração de câmara dupla — invoque-os em csi1 após csi.CSI.reset. Consulte as secções abaixo para mais detalhes.

Alinhamento acelerado por GPU

Image.draw_image aceita um argumento transform= — uma matriz de homografia 3x3 como array 2D ulab.numpy. Na OpenMV N6, a GPU executa a projeção por pixel durante o mesmo desenho, pelo que o fotograma do GENX320 pode ser realinhado em relação à perspetiva da câmara de cor sem uma passagem de distorção separada — útil quando os dois sensores têm óticas ou campos de visão ligeiramente diferentes, ou quando a câmara de cor funciona a uma resolução superior. Calibre a matriz por câmara com a ferramenta de Calibração de Sobreposição GenX320, que apresenta um tabuleiro de xadrez intermitente 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âmara de cor em csi.VGA (640x480) e o GENX320 na sua resolução nativa 320x320 — a homografia projeta o fotograma menor do GENX320 no fotograma maior de cor como parte do desenho, pelo que o fator de aumento de escala está incorporado na própria matriz em vez de ser aplicado separadamente.

Detalhes da câmara de eventos

O GENX320 é um sensor de visão baseado em eventos — em vez de ler toda a matriz 320x320 num relógio de fotogramas fixo, cada pixel comunica «eventos» assíncronos no instante em que deteta uma mudança de brilho. Cada evento transporta uma coordenada X/Y, uma polaridade ON/OFF (claro→escuro ou escuro→claro) e uma marca temporal em microssegundos. É daí que vêm a precisão temporal em microssegundos do sensor, a ausência de desfocagem por movimento, a gama dinâmica muito elevada e o consumo de energia proporcional à atividade. Cenas estáticas não geram 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 brutos são transmitidos para um ndarray numpy com marcas temporais completas em microssegundos, para aplicações que necessitam do detalhe temporal em vez de um fotograma pré-agregado.

Modo histograma

Em modo histograma, o GENX320 emite fotogramas em escala de cinzentos onde cada pixel codifica a atividade de eventos recente nessa localização. Os pixels acima do nível base de brilho são eventos ON (brilho a aumentar), abaixo são eventos OFF (brilho a diminuir). O nível base de brilho predefinido é 128 e o passo de contraste por evento é 16 — aumente o contraste para realçar os eventos:

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 com fundo claro ou image.PALETTE_EVT_DARK com fundo escuro — o controlador emite fotogramas RGB565 usando 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 contra uma cena estática para os desativar. O controlador constrói uma 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 então de emitir eventos ao nível do sensor.

Dois parâmetros controlam a calibração:

  • event_count — quantos eventos contabilizar antes de calcular as estatísticas. O ciclo captura fotogramas até que o total acumulado de eventos ultrapasse este orçamento. Contagens mais elevadas fornecem uma estimativa mais fiável ao custo de maior tempo de calibração. 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 em pixels que estão, na verdade, a funcionar bem:

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 controlados por LED) geram enormes volumes de eventos redundantes. O filtro AFK rejeita eventos cujo pixel alterna a uma frequência dentro de uma banda — ative-o via 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 bias

Cada pixel do GenX320 executa um front-end analógico com vários biases configuráveis. Em conjunto governam 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 — limiar de contraste do comparador positivo. Um pixel emite um evento ON quando a sua log-iluminação tiver aumentado este tanto. Menor = mais sensível a transições claras.

  • DIFF_OFF — limiar de contraste do comparador negativo (o equivalente simétrico para eventos OFF). Menor = mais sensível a transições escuras.

  • FO — frequência de corte passa-baixo do pixel. Maior = maior largura de banda do pixel (resposta mais rápida, menor latência) mas mais atividade de ruído de fundo.

  • HPF — frequência de corte passa-alto. Maior = maior rejeição de variações lentas de brilho; apenas as transições rápidas chegam aos comparadores. Útil para ignorar deriva ambiental.

  • REFR — período refratário. Após um pixel disparar, permanece em reset durante este tempo antes de poder disparar novamente. Maior = 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 — as predefinições 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. Invoque csi.IOCTL_GENX320_SET_BIASES com uma predefinição diferente quando a aplicação necessitar de mais sensibilidade ou largura de banda.

csi.IOCTL_GENX320_SET_BIASES aplica uma de cinco predefinições:

  • csi.GENX320_BIASES_DEFAULT — predefinições 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 definido para 0 para que variações lentas de brilho ainda sejam registadas — uma cena com pouca luz gera poucos eventos por si só, por isso queremos que o maior número possível passe.

  • csi.GENX320_BIASES_ACTIVE_MARKER — ajustado para rastreamento de LEDs intermitentes de alto contraste. Limiares de contraste aumentados para que apenas transições abruptas disparem; FO e HPF maximizados para maximizar a largura de banda do pixel e rejeitar qualquer deriva ambiental lenta; REFR a 0 para que cada aresta do intermitente seja capturada consecutivamente. O resultado: um fluxo quase exclusivamente composto por arestas de LED, fácil de rastrear.

  • csi.GENX320_BIASES_LOW_NOISE — predefinição do controlador. Ambos os limiares de contraste aumentados face 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 rapidamente, HPF elevado para rejeitar deriva lenta de brilho, e REFR aumentado para que uma única aresta de movimento rápido não inunde a leitura — o maior tempo morto mantém o volume de eventos limitado sob movimento intenso.

Substitua 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 DAC. Cada bias é definido de forma independente — escolha uma predefinição como ponto de partida e depois ajuste os biases que a sua cena necessita:

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 em modo histograma é apenas uma imagem em escala de cinzentos, o rastreamento de manchas normal funciona diretamente. Para rastrear um LED marcador ativo, carregue a predefinição de bias de marcador ativo e encontre manchas 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 evento

O modo 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 — ver abaixo

  • [1] marca temporal em segundos

  • [2] marca temporal em milissegundos

  • [3] marca 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 brilho (o limiar do comparador DIFF_OFF foi cruzado). X/Y apontam para o pixel que disparou.

  • csi.PIX_ON_EVENT — um pixel detetou 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 detetou uma aresta descendente. X/Y não são usados.

  • csi.EXT_TRIGGER_RISING — o pino de gatilho externo do sensor detetou uma aresta ascendente. X/Y não são usados.

  • csi.RST_TRIGGER_FALLING — gatilho de reset de pixel, aresta descendente. X/Y não são usados. Não gerado pelo firmware de momento.

  • csi.RST_TRIGGER_RISING — gatilho de reset de pixel, aresta ascendente. X/Y não são usados. Não gerado pelo firmware de momento.

A entrada de gatilho 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 cabeçalho de pinos — acione P10 para injetar arestas de sincronização no fluxo de eventos e leia-as 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 gatilho 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 os 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 contentor; 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 bias do modo histograma, o filtro AFK e os ioctls de calibração de pixels quentes funcionam da mesma forma em modo evento — invoque-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 a empilhar eventos na mesma imagem ao longo de muitos fotogramas — o resultado é uma visualização de rastro 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

Remova 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 gargalo a taxas de eventos elevadas:

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 real em movimento tende a desencadear uma rajada ruidosa de eventos no mesmo pixel dentro de uma janela de tempo curta — o desfasamento dos pixels e o ruído analógico produzem eventos extra 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 os restantes.

Implementa três estratégias, selecionadas via csi.IOCTL_GENX320_SET_STC e uma constante GENX320_STC_*. Cada modo é definido pelos eventos que reencaminha 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 + arestas 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 de stc_threshold de um evento anterior, é considerado o «segundo» da rajada e é reencaminhado — o primeiro evento e quaisquer eventos subsequentes na mesma rajada são filtrados. Melhor quando se pretende uma transição confirmada por ruído em vez da primeira ocorrência.

  • 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é trail_threshold ter decorrido. Preserva a temporização precisa da aresta inicial — útil quando o momento da mudança de polaridade importa mais do que a confirmação por rajada.

  • csi.GENX320_STC_TRAIL — combina ambos. Parâmetros: stc_threshold e trail_threshold (ambos em ms). Mantém a aresta inicial segundo o modo Trail mais arestas subsequentes segundo o modo STC, pelo que vários eventos de uma rajada ainda passam — maior débito de eventos do que os filtros de modo único mas sinal mais rico.

Os dois limiares devem manter-se numa proporção 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 disparam, o pipeline de triplo buffer predefinido favorece o fotograma mais recente e descarta os antigos. Aumente a profundidade da FIFO via csi.CSI.framebuffers para colocar eventos em fila — ao custo de processar dados ligeiramente mais antigos quando o anfitrião fica para trás:

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

Streaming e visualização no ambiente de trabalho

Para visualização GUI em tempo real num PC anfitrião, a ferramenta de Streaming de Eventos GenX320 no repositório openmv-projects combina a câmara com um front-end DearPyGui. A GUI do PC executa duas visualizações lado a lado: uma tela de acumulação de eventos (mesma ideia que Image.draw_event_histogram mas com paletas selecionáveis e modos de janela deslizante vs. auto-limpar) e um mapa de frequência por pixel conduzido por um filtro passa-banda IIR — útil para detetar sinais periódicos (ventoinhas em rotação, LEDs intermitentes, etc.) diretamente no fluxo de eventos.

Inclui dois scripts de streaming na câmara:

  • Modo processado (genx320_event_mode_streaming_on_cam.py) — a câmara descodifica 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 do fio corresponde ao formato ndarray na câmara.

  • Modo bruto (genx320_raw_event_mode_streaming_on_cam.py) — a câmara transmite as palavras de eventos de 32 bits empacotadas nativas do chip através de csi.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 gargalo. O PC descodifica as palavras empacotadas de volta para o mesmo layout de eventos 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 é a restrição determinante à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.