Multiespectral Térmico (PAG7936)

A variante PAG7936 do Módulo de Câmara Multiespectral Térmica combina um sensor de cor de obturador global de 1 MP com um núcleo térmico FLIR Lepton, permitindo que a OpenMV Cam execute pipelines de visão por cor e processamento térmico em simultâneo.

Multispectral Thermal (PAG7936)

Para ficha técnica completa, fotografias e encomendas, consulte a página do produto Multiespectral Térmico.

Nota

Suportado apenas na OpenMV N6.

Destaques

  • PAG7936: obturador global de 1 MP

  • Aceita núcleos térmicos FLIR Lepton 1.x / 2.x / 3.x

  • Processamento térmico + cor simultâneo num único módulo

  • Vê em escuridão total, suporta medição de temperatura

  • O obturador global suporta movimento rápido sem artefactos de obturador rolante

Utilização

O sensor de cor e o FLIR Lepton obtêm cada um a sua própria instância csi.CSI. A primeira chamada assume por omissão o sensor primário (o PAG7936); a segunda vincula ao Lepton passando cid= csi.LEPTON. Efetue um reset forçado ao sensor de cor com csi.CSI.reset (hard=True) para ativar o rail, e configure o Lepton com hard=False para que o seu driver apenas reprograme o chip sem voltar a comutar o reset.

csi.CSI.framesize ( csi.QVGA ) faz corresponder a saída do Lepton à câmara de cor, de modo que cada snapshot() devolve um fotograma de 320x240. O driver do Lepton amplia internamente o seu fotograma nativo de 80x60 (1.x/2.x) ou 160x120 (3.x) para o tamanho pedido — em QVGA, cada pixel do Lepton ocupa uma célula 4x4 ou 2x2 no fotograma de cor.

Dois buffers temporários permanecem constantes ao longo do ciclo de fotogramas — uma paleta alfa de 256x1 armazenada como image.Image, para que os pixels frios do Lepton fiquem transparentes e os pixels quentes fiquem opacos (a rampa quadrática suprime detalhes de fundo sem esmagar os valores intermédios), e um buffer de fotograma do Lepton pré-alocado com image.Image para que csi.CSI.snapshot (blocking=False, image=...) possa preenchê-lo no local em cada iteração sem realocação:

import time
import csi
import image
import math

alpha_pal = image.Image(256, 1, image.GRAYSCALE)
for i in range(256):
    alpha_pal[i] = int(math.pow((i / 255), 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.LEPTON)
csi1.reset(hard=False)  # no hardware reset - just configure lepton
csi1.pixformat(csi.GRAYSCALE)
csi1.framesize(csi.QVGA)

# Optional temperature range controls for the LEPTON.
# csi1.ioctl(csi.IOCTL_LEPTON_SET_MODE, True, False)
# csi1.ioctl(csi.IOCTL_LEPTON_SET_RANGE, 20.0, 40.0)

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_IRONBOW,
                    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 Lepton não bloqueante — o Lepton funciona a 9 Hz, pelo que bloquear nele limitaria o pipeline de cor. Image.draw_image compõe então as duas: color_palette= image.PALETTE_IRONBOW mapeia a escala de cinzentos do Lepton para uma rampa de cor quente ao estilo FLIR, alpha_palette= mistura cada pixel usando o mapa alfa quadrático, e hint= image.BILINEAR suaviza a ampliação.

Medição de temperatura

Os Leptons radiométricos (Lepton 2.5 / 3.5) reportam dados de temperatura calibrados por pixel. Ative o modo de medição através de csi.CSI.ioctl com csi.IOCTL_LEPTON_SET_MODE, e depois limite a janela de temperatura com csi.IOCTL_LEPTON_SET_RANGE (min_celsius, max_celsius). O driver do Lepton mapeia linearmente o valor de pixel em escala de cinzentos 0 para min_celsius e 255 para max_celsius, pelo que cada pixel se torna numa consulta de temperatura dentro da janela configurada. Os pixels mais frios que min_celsius saturam em 0; os pixels mais quentes que max_celsius saturam em 255.

csi.IOCTL_LEPTON_SET_MODE aceita dois sinalizadores. O primeiro ativa a medição; o segundo seleciona o intervalo de temperatura do sensor:

  • Intervalo baixo(True, False) — amplitude do sensor -10 °C a +140 °C (cenas à escala ambiente). Limite a janela à área de interesse, por exemplo (20.0, 40.0) para rastreamento de calor corporal:

    csi1.ioctl(csi.IOCTL_LEPTON_SET_MODE, True, False)
    csi1.ioctl(csi.IOCTL_LEPTON_SET_RANGE, 20.0, 40.0)
    
  • Intervalo alto(True, True) — amplitude do sensor -10 °C a ~+450 °C típico (~+400 °C à temperatura ambiente) para objetos quentes. Limite para, por exemplo, (0.0, 400.0) para rastreamento de fornalhas ou elementos quentes:

    csi1.ioctl(csi.IOCTL_LEPTON_SET_MODE, True, True)
    csi1.ioctl(csi.IOCTL_LEPTON_SET_RANGE, 0.0, 400.0)
    

Para converter um pixel em escala de cinzentos de volta para Celsius:

def p_to_temp(p, min_t, max_t):
    return (p * (max_t - min_t)) / 255.0 + min_t

Isto funciona em pixels individuais ou em estatísticas agregadas (por exemplo, stats.mean() de Image.get_statistics) dentro de uma ROI quando se localizam regiões quentes/frias com Image.find_blobs.

Alinhamento acelerado por GPU

Image.draw_image aceita um argumento transform= — uma matriz de homografia 3x3 como um array ulab.numpy 2-D. Na OpenMV N6, a GPU executa a projeção por pixel durante o mesmo desenho, pelo que o fotograma do Lepton pode ser realinhado com a perspetiva da câmara de cor sem uma passagem de distorção separada. Calibre a matriz por câmara com a ferramenta thermal-overlay-calibration

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

# Calibration matrix from the thermal-overlay-calibration tool.
m = np.array([
    [3.704807, 0.257018, 37.260564],
    [0.052147, 3.609977, -7.831831],
    [0.000294, 0.000552, 1.000000],
])

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

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

csi1 = csi.CSI(cid=csi.LEPTON)
csi1.reset(hard=False)  # no hardware reset - just configure lepton
csi1.pixformat(csi.GRAYSCALE)
csi1.framesize(csi.QQVGA)

# Optional temperature range controls for the LEPTON.
# csi1.ioctl(csi.IOCTL_LEPTON_SET_MODE, True, False)
# csi1.ioctl(csi.IOCTL_LEPTON_SET_RANGE, 20.0, 40.0)

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_IRONBOW,
                    alpha_palette=alpha_pal,
                    hint=image.BILINEAR,
                    transform=m)
    print(clock.fps())

Note que esta variante executa a câmara de cor a csi.VGA (640x480) e o Lepton a csi.QQVGA (160x120) — a homografia projeta o fotograma menor do Lepton no fotograma maior de cor como parte do desenho, pelo que o fator de ampliação está incorporado na própria matriz em vez de ser aplicado separadamente.