Termocamera multispettrale (OV5640)

La variante OV5640 del modulo Multispectral Thermal Camera abbina un sensore a colori rolling-shutter da 5MP a un nucleo termico FLIR Lepton, così la OpenMV Cam può eseguire affiancate pipeline di visione a colori ad alta risoluzione e pipeline termiche.

Termocamera multispettrale (OV5640)

Per il datasheet completo, le foto e le informazioni d’ordine, consulta la pagina prodotto Multispectral Thermal.

Nota

Supportato solo sulla OpenMV Cam RT1062.

Punti salienti

  • OV5640: rolling shutter da 5MP per colori a risoluzione più elevata

  • Accetta nuclei termici FLIR Lepton 1.x / 2.x / 3.x

  • Elaborazione termica + a colori simultanea su un unico modulo

  • Vede nel buio totale, supporta la misurazione della temperatura

  • Autofocus e apertura F2.0 sul sensore a colori

Utilizzo

Il sensore a colori e la FLIR Lepton ottengono ciascuno la propria istanza csi.CSI. La prima chiamata usa per impostazione predefinita il sensore primario (la OV5640); la seconda si associa alla Lepton passando cid= csi.LEPTON. Esegui un hard-reset del sensore a colori con csi.CSI.reset (hard=True) per attivarne l’alimentazione, e configura la Lepton con hard=False in modo che il suo driver si limiti a riprogrammare il chip senza riattivare il reset.

csi.CSI.framesize ( csi.QVGA ) adatta l’uscita della Lepton alla camera a colori, così ogni snapshot() restituisce un frame da 320x240. Il driver Lepton effettua internamente l’upscaling del suo frame nativo da 80x60 (1.x/2.x) o 160x120 (3.x) alla dimensione richiesta: a QVGA ogni pixel della Lepton copre una cella 4x4 o 2x2 sul frame a colori.

Due buffer di lavoro restano costanti per tutto il ciclo dei frame: una palette alpha da 256x1 memorizzata come image.Image, in modo che i pixel freddi della Lepton diventino trasparenti e quelli caldi opachi (la rampa quadratica attenua i dettagli di sfondo senza schiacciare i toni intermedi), e un frame buffer della Lepton pre-allocato con image.Image, così che csi.CSI.snapshot (blocking=False, image=...) possa riempirlo sul posto a ogni iterazione senza riallocare:

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())

Ogni iterazione acquisisce uno snapshot a colori bloccante e uno snapshot Lepton non bloccante: la Lepton funziona a 9 Hz, quindi bloccarsi su di essa rallenterebbe la pipeline a colori. Image.draw_image poi compone i due: color_palette= image.PALETTE_IRONBOW mappa la scala di grigi della Lepton su una rampa di colori caldi in stile FLIR, alpha_palette= fonde ogni pixel usando la mappa alpha quadratica, e hint= image.BILINEAR ammorbidisce l’upscaling.

La OV5640 ha un obiettivo con autofocus ad attuatore voice-coil. Attiva una singola passata di autofocus sulla camera a colori tramite csi.CSI.ioctl con csi.IOCTL_TRIGGER_AUTO_FOCUS: il sensore sposta il motore della messa a fuoco una volta e si blocca su qualunque cosa abbia di fronte:

csi0.ioctl(csi.IOCTL_TRIGGER_AUTO_FOCUS)

Riemetti l’ioctl ogni volta che la scena cambia: l’autofocus è a colpo singolo, non continuo.

Misurazione della temperatura

Le Lepton radiometriche (Lepton 2.5 / 3.5) riportano dati di temperatura per pixel calibrati. Abilita la modalità di misurazione tramite csi.CSI.ioctl con csi.IOCTL_LEPTON_SET_MODE, poi limita la finestra di temperatura con csi.IOCTL_LEPTON_SET_RANGE (min_celsius, max_celsius). Il driver Lepton mappa linearmente il valore di pixel in scala di grigi 0 su min_celsius e 255 su max_celsius, così ogni pixel diventa una ricerca di temperatura all’interno della finestra configurata. I pixel più freddi di min_celsius saturano a 0, quelli più caldi di max_celsius saturano a 255.

csi.IOCTL_LEPTON_SET_MODE accetta due flag. Il primo attiva la misurazione; il secondo seleziona l’intervallo di temperatura del sensore:

  • Intervallo basso(True, False) — campo del sensore da -10 °C a +140 °C (scene a scala ambiente). Limita la finestra all’area di interesse, ad es. (20.0, 40.0) per il tracciamento del calore corporeo:

    csi1.ioctl(csi.IOCTL_LEPTON_SET_MODE, True, False)
    csi1.ioctl(csi.IOCTL_LEPTON_SET_RANGE, 20.0, 40.0)
    
  • Intervallo alto(True, True) — campo del sensore da -10 °C a ~+450 °C tipico (~+400 °C a temperatura ambiente) per oggetti caldi. Limita ad es. a (0.0, 400.0) per il tracciamento di forni o elementi caldi:

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

Per riconvertire un pixel in scala di grigi in gradi Celsius:

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

Questo funziona su singoli pixel o su statistiche aggregate (ad es. stats.mean() da Image.get_statistics) all’interno di una ROI quando si individuano regioni calde/fredde con Image.find_blobs.