Multispektralna termowizja (PAG7936)

Wariant PAG7936 modułu kamery multispektralnej termowizyjnej łączy 1-megapikselowy kolorowy sensor z migawką typu global shutter z rdzeniem termowizyjnym FLIR Lepton, dzięki czemu OpenMV Cam może równolegle uruchamiać potoki wizji kolorowej oraz termowizji.

Multispektralna termowizja (PAG7936)

Pełną dokumentację techniczną, zdjęcia oraz informacje o zamówieniu znajdziesz na stronie produktu Multispectral Thermal.

Informacja

Obsługiwana wyłącznie na OpenMV N6.

Najważniejsze cechy

  • PAG7936: global shutter 1 MP

  • Akceptuje rdzenie termowizyjne FLIR Lepton 1.x / 2.x / 3.x

  • Jednoczesne przetwarzanie termowizji i koloru na jednym module

  • Widzi w całkowitej ciemności, obsługuje pomiar temperatury

  • Global shutter radzi sobie z szybkim ruchem bez artefaktów charakterystycznych dla rolling shutter

Użycie

Sensor kolorowy oraz FLIR Lepton otrzymują każdy własną instancję csi.CSI. Pierwsze wywołanie domyślnie korzysta z głównego sensora (PAG7936); drugie wiąże się z Leptonem poprzez przekazanie cid= csi.LEPTON. Wykonaj twardy reset sensora kolorowego za pomocą csi.CSI.reset (hard=True), aby podnieść zasilanie szyny, i skonfiguruj Lepton z hard=False, aby jego sterownik jedynie przeprogramował układ bez ponownego przełączania resetu.

csi.CSI.framesize ( csi.QVGA ) dopasowuje wyjście Leptona do kamery kolorowej, tak że każdy snapshot() zwraca ramkę 320x240. Sterownik Leptona wewnętrznie skaluje w górę swoją natywną ramkę 80x60 (1.x/2.x) lub 160x120 (3.x) do żądanego rozmiaru — przy QVGA każdy piksel Leptona pokrywa komórkę 4x4 lub 2x2 na ramce kolorowej.

Dwa bufory robocze pozostają stałe przez całą pętlę ramki — paleta alfa 256x1 przechowywana jako image.Image, dzięki czemu chłodne piksele Leptona stają się przezroczyste, a gorące nieprzezroczyste (kwadratowa rampa tłumi szczegóły tła bez spłaszczania środka zakresu), oraz bufor ramki Leptona wstępnie zaalokowany za pomocą image.Image, tak aby csi.CSI.snapshot (blocking=False, image=...) mógł wypełniać go w miejscu przy każdej iteracji bez ponownej alokacji:

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

Każda iteracja wykonuje blokujący zrzut obrazu w kolorze oraz nieblokujący zrzut obrazu z Leptona — Lepton działa z częstotliwością 9 Hz, więc blokowanie na nim ograniczyłoby przepustowość potoku kolorowego. Image.draw_image następnie komponuje oba: color_palette= image.PALETTE_IRONBOW mapuje skalę szarości Leptona na ciepłą rampę kolorów w stylu FLIR, alpha_palette= miesza każdy piksel za pomocą kwadratowej mapy alfa, a hint= image.BILINEAR wygładza skalowanie w górę.

Pomiar temperatury

Radiometryczne Leptony (Lepton 2.5 / 3.5) raportują skalibrowane dane temperaturowe dla każdego piksela. Włącz tryb pomiarowy poprzez csi.CSI.ioctl z csi.IOCTL_LEPTON_SET_MODE, a następnie ogranicz okno temperaturowe za pomocą csi.IOCTL_LEPTON_SET_RANGE (min_celsius, max_celsius). Sterownik Leptona liniowo mapuje wartość piksela w skali szarości 0 na min_celsius oraz 255 na max_celsius, dzięki czemu każdy piksel staje się odczytem temperatury w skonfigurowanym oknie. Piksele chłodniejsze niż min_celsius nasycają się do 0, a piksele cieplejsze niż max_celsius nasycają się do 255.

csi.IOCTL_LEPTON_SET_MODE przyjmuje dwie flagi. Pierwsza włącza pomiar; druga wybiera zakres temperatury sensora:

  • Niski zakres(True, False) — zakres sensora od -10 °C do +140 °C (sceny w skali pomieszczenia). Ogranicz okno do obszaru zainteresowania, np. (20.0, 40.0) do śledzenia ciepła ciała:

    csi1.ioctl(csi.IOCTL_LEPTON_SET_MODE, True, False)
    csi1.ioctl(csi.IOCTL_LEPTON_SET_RANGE, 20.0, 40.0)
    
  • Wysoki zakres(True, True) — typowy zakres sensora od -10 °C do ~+450 °C (~+400 °C w temperaturze pokojowej) dla gorących obiektów. Ogranicz np. do (0.0, 400.0) do śledzenia pieców lub gorących elementów:

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

Aby przekonwertować piksel w skali szarości z powrotem na stopnie Celsjusza:

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

Działa to na pojedynczych pikselach lub na zagregowanych statystykach (np. stats.mean() z Image.get_statistics) wewnątrz ROI podczas lokalizowania gorących/chłodnych regionów za pomocą Image.find_blobs.

Wyrównanie przyspieszane przez GPU

Image.draw_image akceptuje argument transform= — macierz homografii 3x3 w postaci dwuwymiarowej tablicy ulab.numpy. Na OpenMV N6 GPU wykonuje projekcję per-piksel podczas tego samego rysowania, dzięki czemu ramkę Leptona można ponownie wyrównać względem perspektywy kamery kolorowej bez osobnego przebiegu zniekształcania. Skalibruj macierz dla każdej kamery za pomocą narzędzia 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())

Zwróć uwagę, że ten wariant uruchamia kamerę kolorową w csi.VGA (640x480), a Lepton w csi.QQVGA (160x120) — homografia rzutuje mniejszą ramkę Leptona na większą ramkę kolorową w ramach rysowania, więc współczynnik skalowania w górę jest wbudowany w samą macierz, zamiast być stosowany osobno.