LoginSignup
0
3

More than 3 years have passed since last update.

サーマルカメラ(サーモ AI デバイス TiD) Python AMG8833 番外編

Last updated at Posted at 2021-01-21

Panasonic 赤外線アレイセンサ Grid-EYE AMG8833からの温度データを、Raspberr PiからI2C通信で取得します。
データの取得自体は、Adafruit_AMG88xxをimportして、self._sensor = Adafruit_AMG88xx()などとして、インスタンス化すると、self._sensor.readPixels()で取得出来ます。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
AMG8833 script.
===============
Human-detectable distance: MAX 7[m]
3.3[V] I2C(68h or 69h)
8x8 array 0~80[deg]
Nick name: Grid-EYE
"""
i2c_enable = False

# Raspberry Pi以外の環境でのディバック用です
try:
    from Adafruit_AMG88xx import Adafruit_AMG88xx
    i2c_enable = True
except:
    i2c_enable = False


class Sensor():
    def __init__(self, *args, **kwargs):
        self.enable = False
        self._sensor = None

        if i2c_enable:
            self.enable = True
            try:
                self._sensor = Adafruit_AMG88xx()
            except:
                self.enable = False

    @property
    def high(self):
        return 80.0

    @property
    def low(self):
        return 0.0

    @property
    def pixels(self):
        result = None
        if self._sensor is not None:
            result = self._sensor.readPixels()
        return result

class Sensorには、high,lowプロパティがありますが、amg8833.pyをimportするThermoクラスに必要です。

thermo.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
MH Image process Thermo script.
===============================
Temperature data processing from device.
AMG8833
D6T-44L-06
"""
import numpy as np
import random

cv2_installed = False
try:
    import cv2
    cv2_installed = True
except ModuleNotFoundError:
    cv2_installed = False

# mhipはMH ソフトウェア&サービスの画像処理用パッケージです
import mhip


class Thermo(mhip.Method):
    def __init__(self, sensor, *args, **kwargs):
        """
        sensor='amg8833' or 'd6t_44l_06'
        Temepature adjust formula is,
        temp = (a*length^2 + b*length + c) * temp
        この計算式は、距離に対する温度を補正します
        """
        # sensorを指定する事で、OMRON D6T-44L-06とPanasonic AMG8833が変更できます
        if sensor.lower() == 'd6t_44l_06':
            # 通常、importは先頭に書きますが、条件分岐がある為、ここでimportします
            # さらに、単独でこのスクリプトを動かす場合とimportされる時で、importする場所を変更しています
            if __name__ == '__main__':
                from d6t_44l_06 import Sensor
            else:
                from .d6t_44l_06 import Sensor
        elif sensor.lower() == 'amg8833':
            if __name__ == '__main__':
                from amg8833 import Sensor
            else:
                from .amg8833 import Sensor

        super().__init__(*args, **kwargs)

        self.sensor = Sensor(*args, **kwargs)

        self.a = kwargs.get('a', 0)
        self.b = kwargs.get('b', 1)
        self.c = kwargs.get('c', 0)
        self._length = kwargs.get('length', 1)

        self.map_max = kwargs.get('map_max', 35.0)
        self.map_mix = kwargs.get('map_min', 9.5)
        self._size = kwargs.get('size', (320, 320))
        self._shutdowning = False

        # 分析結果は、mhipで必要です
        self._analysis = {
            'en': {'Max Temp.': 
                self.sensor.high, 'Min Temp.': self.sensor.low},
            'ja': {'最高温度': 
                self.sensor.high, '最低温度': self.sensor.low}}

        self._min = 0
        self._max = 0

        self.gain = 10
        self.offset_x = 0.2
        self.offset_green = 0.6

    @property
    def a(self):
        return self._a
    @a.setter
    def a(self, value):
        self._a = value

    @property
    def b(self):
        return self._b
    @b.setter
    def b(self, value):
        self._b = value

    @property
    def c(self):
        return self._c
    @c.setter
    def c(self, value):
        self._c = value

    def colorBarRGB(self, x):
        # 2020/6/24: red and blue is reversed...
        x = (x * 2) - 1
        blue = self.sigmoid(x, self.gain, -1 * self.offset_x)
        red = 1 - self.sigmoid(x, self.gain, self.offset_x)
        green = self.sigmoid(x, self.gain, self.offset_green) \
            + (1 - self.sigmoid(x, self.gain, -1 * self.offset_green))
        green = green - 1.0

        blue = blue * 255
        green = green * 255
        red = red * 255

        return [blue, green, red]

    def __del__(self):
        self._shutdowning = True

    # Rapebrry Pi以外の環境でのディバック用の温度データです
    def _dummy(self):
        result_64 = [
            [22.75, 23.5, 24.75, 24.5, 24.5, 23.75, 23.0, 23.75, 22.5, 23.5, 27.75, 27.25, 24.25, 22.75, 22.5, 23.0, 22.75, 24.25, 28.25, 27.5, 26.75, 24.0, 22.25, 22.25, 24.0, 26.5, 28.5, 28.0, 27.25, 24.25, 22.75, 21.75, 27.0, 27.75, 27.75, 28.25, 28.5, 24.75, 23.25, 23.5, 28.0, 27.0, 27.0, 28.75, 29.25, 26.75, 24.0, 23.75, 28.25, 27.0, 27.0, 27.5, 28.25, 26.5, 24.25, 24.0, 27.25, 27.25, 25.75, 25.5, 26.0, 25.75, 24.5, 24.25],
            [24.5, 25.0, 25.5, 26.5, 25.5, 25.5, 24.75, 25.5, 24.25, 25.0, 27.75, 29.25, 26.75, 25.0, 24.25, 24.75, 24.75, 27.0, 29.5, 30.5, 29.25, 28.75, 26.5, 26.0, 28.5, 30.0, 31.0, 37.75, 31.0, 31.0, 31.0, 31.0, 29.75, 30.75, 31.0, 31.0, 37.75, 31.5, 37.75, 31.5, 30.5, 30.5, 37.75, 31.5, 31.5, 31.5, 31.75, 37.75, 30.25, 30.25, 30.75, 37.75, 37.75, 31.75, 37.75, 32.0, 30.25, 30.5, 37.75, 31.0, 31.5, 31.5, 31.5, 37.75],
            [22.75, 22.75, 22.75, 24.25, 24.5, 24.75, 24.0, 24.0, 22.25, 22.5, 23.0, 24.0, 24.5, 24.25, 23.75, 23.75, 22.0, 22.25, 23.0, 24.0, 24.5, 24.0, 23.5, 23.0, 21.5, 22.25, 22.75, 26.5, 25.25, 22.75, 23.25, 22.5, 21.75, 21.75, 22.5, 26.75, 25.25, 22.75, 22.25, 22.0, 22.0, 22.25, 24.75, 26.0, 25.75, 24.5, 21.75, 21.25, 21.75, 22.0, 24.25, 24.5, 24.75, 25.0, 22.0, 22.0, 21.25, 21.75, 24.0, 24.5, 25.25, 24.75, 21.5, 21.5]
            ]
        result = [
            [22.75, 23.5, 24.75, 24.5, 24.5, 23.75, 23.0, 23.75, 22.5, 23.5, 27.75, 26.25, 24.25, 22.75, 22.5, 23.0],
            [24.5, 25.0, 25.5, 26.5, 25.5, 25.5, 24.75, 25.5, 24.25, 25.0, 27.75, 27.25, 26.75, 25.0, 24.25, 24.75],
            [22.75, 22.75, 22.75, 25.25, 26.5, 26.75, 24.0, 24.0, 22.25, 22.5, 23.0, 24.0, 24.5, 24.25, 23.75, 23.75]
            ]
        for i in range(len(result)):
            for j in range(len(result[i])):
                result[i][j] = result[i][j] * 1.15
        return result[random.randint(0, 2)]

    @property
    def enable(self):
        return self.sensor.enable

    @property
    def length(self):
        return self._length
    @length.setter
    def length(self, value):
        self._length = value
        self.sensor.length = self._length

    @property
    def max(self):
        return self._max

    @property
    def min(self):
        return self._min

    class Parameter():
        def __init__(self):
            pass

    # mhipで使用する処理済画像を作成します
    @property
    def processed_image(self):
        result = None

        pixels = []
        if self.sensor.enable:
            pixels = self.sensor.pixels

        temp_width = self.map_max - self.map_mix

        if pixels is None:
            # Rainbow!!!
            #pixels = []
            #_ratio = temp_width / (self.sensor.x * self.sensor.y)
            #for i in range(self.sensor.x * self.sensor.y):
            #    #pixels.append(round(self.sensor.high * random.uniform(0.0, 0.475), 1))
            #    pixels.append(i * _ratio + self.map_mix)
            pixels = self._dummy()
        elif len(pixels) == 0:
            pixels = self._dummy()

        for i, val in enumerate(pixels):
            pixels[i] = \
                (
                    self._a * self._length ** 2 \
                    + self._b * self._length \
                    + self._c
                ) * val

        self._min = min(pixels)
        self._max = max(pixels)

        self._analysis = {
            'en': {'Max Temp.': self._max, 'Min Temp.': self._min},
            'ja': {'最高温度': self._max, '最低温度': self._min}}

        # vol.4 熱画像のバージョン
        # 色々とweb上の処理を試させていただきました ありがとうございました
        # 最終的に、自分で作ったこの処理がシンプルで、まあまあの熱画像になると思います
        # やはりnumpy処理は速いです
        temp = np.array(pixels)
        temp = temp - self.map_mix
        temp = temp / temp_width
        sqrt = int(len(temp)**0.5)
        temp = temp.reshape(sqrt, sqrt)

        img = np.zeros(
            (sqrt, sqrt, 3),
            dtype='uint8')

        img[:, :, 0] = self.colorBarRGB(temp)[0]
        img[:, :, 1] = self.colorBarRGB(temp)[1]
        img[:, :, 2] = self.colorBarRGB(temp)[2]

        img = cv2.resize(img, (self.size[0], self.size[1]), cv2.INTER_CUBIC)

        return img

    def sigmoid(self, x, gain=1, offset_x=0):
        return ((np.tanh(((x + offset_x) * gain) / 2) + 1) / 2)

    @property
    def size(self):
        return self._size
    @size.setter
    def size(self, value):
        self._size = value

    def stop(self):
        self._shutdowning = True
        self.sensor.stop()


def main():
    # 単体で動かして、カメラ画像に熱画像を足しています
    th = Thermo(
        #sensor='d6t_44l_06',
        sensor='amg8833',
        map_max=35.0,
        map_min=10.0,
        a=0.000001,
        b=0.00025,
        c=1.186,
        size=(160, 160),
    )

    vcap = cv2.VideoCapture(0)

    while True:
        _, capture = vcap.read()
        ratio = 1
        #capture = cv2.resize(
        #    capture, 
        #    (
        #        int(capture.shape[1] * ratio),
        #        int(capture.shape[0] * ratio)
        #    ))

        thermo = th.processed_image
        thermo = thermo[:,:,::-1]
        thermo = np.rot90(thermo, 1)

        # attachはカメラ画像に、引数最後の座標に熱画像を足します
        image = attach(capture, thermo, (0,0))
        # full_blendは、カメラ画像と熱画像をブレンドします
        # 熱画像のサイズを調整すると、カメラ画像と合わせられます
        #image = full_blend(capture, thermo)

        cv2.imshow('thermo', image)
        cv2.waitKey(50)
        print(f'{th.max:0.1f}', f'{th.min:0.1f}')
    pass


def attach(large, small, offset):
    result = large
    result[
        offset[1]: offset[1] + small.shape[0],
        offset[0]: offset[0] + small.shape[1]] = small
    return result


def full_blend(large, small):
    small = cv2.resize(
        small, 
        (large.shape[1], large.shape[0]))
    alpha = 0.3
    result = cv2.addWeighted(
        large, alpha, 
        small, 1 - alpha, 0)
    return result


if __name__ == '__main__':
    main()

mhip.Methodクラスです。AMG8833、D6T-44L-06を扱うThermoクラスも、画像を扱います。
Methodクラスを継承します。

class Method():
    def __init__(self, *args, **kwargs):
        self.area = [0, 0, 0, 0]
        self.judge = True
        self.output = 0
        self.analysis = {
            'en': {'Info': 'None'},
            'ja': {'検査結果': 'ありません'}
        }

        self.color_channel = kwargs.get('color_channel', 'rgb')
        self.source = kwargs.get('source', None)

        self.parameter = self.Parameter()

    @property
    def analysis(self):
        return self._analysis
    @analysis.setter
    def analysis(self, value):
        self._analysis = value

    @property
    def judge(self):
        return self._judge
    @judge.setter
    def judge(self, value):
        if type(value) == bool:
            self._judge = value

    @property
    def output(self):
        return self._output
    @output.setter
    def output(self, value):
        if mhfn.isnumeric(value):
            self._output = value

    @property
    def processed_image(self):
        return self.source

    @property
    def source(self):
        return self._source
    @source.setter
    def source(self, value):
        source = Image(source=value, color_channel=self.color_channel)
        self._source = source.numpy

こんな感じで、指の熱画像が表示されます。
for_gif_320x240_fps2.gif

YouTube: サーマルカメラ(サーモ AI デバイス TiD) Python編
web: サーモ AI デバイス TiD Python D6T編 (URLが変更されました)

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3