Мультиспектральная тепловизионная камера (PAG7936)

Вариант PAG7936 модуля мультиспектральной тепловизионной камеры сочетает 1-мегапиксельный цветной датчик с глобальным затвором (global shutter) и тепловизионное ядро FLIR Lepton, поэтому OpenMV Cam может одновременно выполнять конвейеры цветного машинного зрения и тепловизионной обработки.

Мультиспектральная тепловизионная камера (PAG7936)

Полную техническую документацию, фотографии и сведения о заказе см. на странице продукта Multispectral Thermal.

Примечание

Поддерживается только на OpenMV N6.

Ключевые особенности

  • PAG7936: 1 МП, глобальный затвор

  • Поддерживает тепловизионные ядра FLIR Lepton 1.x / 2.x / 3.x

  • Одновременная тепловизионная и цветная обработка на одном модуле

  • Видит в полной темноте, поддерживает измерение температуры

  • Глобальный затвор справляется с быстрым движением без артефактов прогрессивной развертки

Использование

Цветной датчик и FLIR Lepton получают каждый свой экземпляр csi.CSI. Первый вызов по умолчанию обращается к основному датчику (PAG7936); второй привязывается к Lepton при передаче cid= csi.LEPTON. Выполните аппаратный сброс цветного датчика с помощью csi.CSI.reset (hard=True), чтобы поднять питание, и настройте Lepton с hard=False, чтобы его драйвер только перепрограммировал чип без повторного переключения сброса.

csi.CSI.framesize ( csi.QVGA ) согласует вывод Lepton с цветной камерой, поэтому каждый snapshot() возвращает кадр 320x240. Драйвер Lepton внутренне масштабирует свой нативный кадр 80x60 (1.x/2.x) или 160x120 (3.x) до запрошенного размера — при QVGA каждый пиксель Lepton покрывает ячейку 4x4 или 2x2 на цветном кадре.

Два вспомогательных буфера остаются неизменными на протяжении цикла кадров — альфа-палитра 256x1, хранимая как image.Image, чтобы холодные пиксели Lepton становились прозрачными, а горячие — непрозрачными (квадратичная кривая подавляет фоновые детали, не сжимая средний диапазон), и буфер кадра Lepton, заранее выделенный с помощью image.Image, чтобы csi.CSI.snapshot (blocking=False, image=...) мог заполнять его на месте на каждой итерации без повторного выделения памяти:

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

Каждая итерация выполняет блокирующий снимок цветного кадра и неблокирующий снимок Lepton — Lepton работает на частоте 9 Гц, поэтому блокировка на нём замедлила бы цветной конвейер. Затем Image.draw_image накладывает оба: color_palette= image.PALETTE_IRONBOW отображает оттенки серого Lepton на тёплую цветовую кривую в стиле FLIR, alpha_palette= смешивает каждый пиксель с использованием квадратичной альфа-карты, а hint= image.BILINEAR сглаживает увеличение.

Измерение температуры

Радиометрические Lepton (Lepton 2.5 / 3.5) сообщают калиброванные данные о температуре по каждому пикселю. Включите режим измерения через csi.CSI.ioctl с csi.IOCTL_LEPTON_SET_MODE, затем ограничьте температурное окно с помощью csi.IOCTL_LEPTON_SET_RANGE (min_celsius, max_celsius). Драйвер Lepton линейно отображает значение пикселя оттенков серого 0 на min_celsius, а 255 на max_celsius, поэтому каждый пиксель становится поиском температуры в пределах настроенного окна. Пиксели холоднее min_celsius насыщаются до 0, пиксели горячее max_celsius насыщаются до 255.

csi.IOCTL_LEPTON_SET_MODE принимает два флага. Первый включает измерение; второй выбирает температурный диапазон датчика:

  • Низкий диапазон(True, False) — диапазон датчика от -10 °C до +140 °C (сцены комнатного масштаба). Ограничьте окно интересующей областью, например (20.0, 40.0) для отслеживания тепла тела:

    csi1.ioctl(csi.IOCTL_LEPTON_SET_MODE, True, False)
    csi1.ioctl(csi.IOCTL_LEPTON_SET_RANGE, 20.0, 40.0)
    
  • Высокий диапазон(True, True) — диапазон датчика обычно от -10 °C до ~+450 °C (~+400 °C при комнатной температуре) для горячих объектов. Ограничьте, например, (0.0, 400.0) для отслеживания печей или горячих элементов:

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

Чтобы преобразовать пиксель оттенков серого обратно в градусы Цельсия:

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

Это работает для отдельных пикселей или для агрегированной статистики (например, stats.mean() из Image.get_statistics) внутри ROI при локализации горячих/холодных областей с помощью Image.find_blobs.

Выравнивание с GPU-ускорением

Image.draw_image принимает аргумент transform= — матрицу гомографии 3x3 в виде 2-мерного массива ulab.numpy. На OpenMV N6 GPU выполняет попиксельное проецирование во время того же рисования, поэтому кадр Lepton может быть повторно выровнен относительно перспективы цветной камеры без отдельного прохода деформации. Откалибруйте матрицу для каждой камеры с помощью инструмента 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())

Обратите внимание, что этот вариант запускает цветную камеру в csi.VGA (640x480), а Lepton в csi.QQVGA (160x120) — гомография проецирует меньший кадр Lepton в больший цветной кадр как часть рисования, поэтому коэффициент масштабирования заложен в саму матрицу, а не применяется отдельно.