4.18. Múltiplos sensores

Algumas OpenMV Cams combinam dois sensores de imagem na mesma placa – mais comumente uma câmera colorida ao lado de um sensor térmico FLIR® Lepton®, mas o mesmo formato se aplica às placas de cor mais eventos e a qualquer hardware futuro de sensor duplo. Cada sensor tem sua própria matriz de pixels, seu próprio barramento de controle e executa seu próprio pipeline em sua própria taxa de quadros. A API CSI se estende para abrangê-los permitindo que a aplicação instancie um objeto CSI por sensor físico.

4.18.1. Selecionando qual sensor

O construtor de CSI recebe um argumento cid que nomeia um sensor específico na placa. cid=-1 (o padrão) seleciona o sensor primário; as constantes cid nomeadas selecionam um secundário pelo ID do chip:

import csi

csi_rgb     = csi.CSI()                    # primary colour sensor
csi_thermal = csi.CSI(cid=csi.LEPTON)      # FLIR® Lepton®

Cada instância possui sua própria configuração – formato de pixel, framesize, ajustes de exposição / ganho, pool de framebuffer – e é resetada, configurada e lida de forma independente da outra. As constantes para os sensores secundários suportados (LEPTON, GENX320 e as outras listadas na referência de CSI) nomeiam o chip que a aplicação espera na porta secundária; o driver faz a construção falhar se o chip real não corresponder.

4.18.2. Capturando de ambos os sensores

Cada sensor executa seu pipeline de captura de forma independente do outro – o sensor colorido pode entregar trinta quadros por segundo enquanto o Lepton® entrega nove. A maneira direta de lidar com essa diferença é deixar o sensor mais rápido conduzir o laço e ler o sensor mais lento de forma não bloqueante, pegando o que estiver pronto e pulando a iteração quando não houver nada:

import csi

csi_rgb     = csi.CSI()
csi_thermal = csi.CSI(cid=csi.LEPTON)

csi_rgb.reset()                        # powers the rail, pulses RESET
csi_rgb.pixformat(csi.RGB565)
csi_rgb.framesize(csi.QVGA)

csi_thermal.reset(hard=False)          # I2C reconfigure only
csi_thermal.pixformat(csi.GRAYSCALE)
csi_thermal.framesize(csi.QQVGA)

while True:
    rgb_img     = csi_rgb.snapshot()                  # blocks for next colour frame
    thermal_img = csi_thermal.snapshot(blocking=False)  # returns None if not ready
    if thermal_img is not None:
        # process aligned colour + thermal pair
        pass
    else:
        # process colour only on this iteration
        pass

O snapshot() bloqueante dita o ritmo do laço; o não bloqueante retorna o quadro térmico mais recente quando um novo chegou desde a chamada anterior, e None caso contrário. A aplicação continua executando na taxa de quadros do sensor colorido e obtém um quadro térmico sempre que o Lepton® produz um.

O padrão oposto – dois snapshots bloqueantes em sequência – também funciona, mas o laço passa a rodar na taxa do mais lento dos dois sensores, com o pipeline do sensor mais rápido travando entre as iterações. Escolha a taxa que o processamento subsequente da aplicação realmente quiser conduzir.

4.18.3. Reset em barramentos de alimentação compartilhados

Algumas placas de sensor duplo alimentam ambos os chips a partir de um único barramento de alimentação ou compartilham uma linha de reset. Nessas, o primeiro reset() liga o barramento e pulsa o sinal compartilhado; resets subsequentes nas outras instâncias de CSI devem passar hard=False para que reprogramem apenas o próprio chip sem arrastar o vizinho por um reset:

csi_rgb.reset()                        # primary -- powers the rail, pulses RESET
csi_thermal.reset(hard=False)          # secondary -- I2C reconfigure only

Um hard=True em um secundário neste formato resetaria novamente o primário como efeito colateral, desfazendo qualquer configuração que a aplicação já tivesse aplicado. A página de referência de cada placa de sensor duplo indica se os barramentos são compartilhados.

4.18.4. Selecionando a fonte do stream

Câmeras com dois sensores têm duas instâncias de CSI, mas ainda assim apenas um framebuffer de stream entre elas. Um argumento do construtor escolhe os quadros de qual sensor alimentam a pré-visualização:

csi_rgb     = csi.CSI()                    # primary
csi_thermal = csi.CSI(cid=csi.LEPTON,
                      stream=True)         # preview source

stream=True torna a instância nomeada a fonte. Sem um argumento stream=, o sensor primário (cid=-1, o padrão) é a fonte; instâncias construídas com cid= de um sensor secundário permanecem silenciosas na pré-visualização a menos que stream=True seja passado explicitamente. Chamadas a snapshot() no sensor não selecionado ainda capturam quadros normalmente para os framebuffers daquele sensor – elas apenas não atualizam a pré-visualização.