はじめに
サーマルカメラとしては、取得した温度データをヒートマップにしたいのです。
MH ソフトウェア&サービスでは、Raspberry Piを使用して実現したいので、温度データ -> ヒートマップ変換は、4種類ほど作成してみました。(高速化が必要だったのです。)
使用機材
サーマルカメラ(サーモ AI デバイス TiD) Python AMG8833 番外編で使用したwebカメラとPanasonic AMG8833、Raspberry Piを使用して取得した温度データを使用してみます。
テスト用にwebカメラのレンズ付近に、Panasonic AMG8833を取り付けているだけの簡素な仕様です。
温度データは
この動画の一部の、指を開いている個所の温度データです。回転処理をしていませんので、ヒートマップの画像は180度回転しています。
メインクラス(HeatMap)
このクラスからversion1.py ~ version4.pyを開きます。
それぞれ10回の処理を実行して、処理速度を比較します。処理はRaspberry Pi 4を使用しています。
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import numpy as np
import PIL
import PIL.Image
import PIL.ImageTk
import time
import cv2
from version1 import Version1
from version2 import Version2
from version3 import Version3
from version4 import Version4
class HeatMap():
def __init__(self):
self.heat_map = [
None,
Version1(),
Version2(),
Version3(),
Version4()
]
def __call__(self):
img = None
timer = ProcessingTime()
# Version1から4まで処理速度を比較してみます
for version in range(1, 5):
# 計測タイマリセット
timer.get()
# 処理回数は10回です
for _ in range(10):
img = self.heat_map[version](self.parameters)
message = f'Version{version} {timer.get():.02f}[msec]'
print(message)
img = resize(img, self.parameters.size, self.parameters.size)
cv2.imshow(message, img)
cv2.waitKey(5000)
while True:
pass
class parameters():
gain = 10
max = 40
min = 20
offset_x = 0.2
offset_green = 0.6
pixels = 8 * 8
size = 320
class ProcessingTime():
"""
Return processing time [msec].
"""
def __init__(self):
self._start = time.time()
def get(self) -> float:
result = (time.time() - self._start) * 1000
self._start = time.time()
return result
def resize(image: np.ndarray, width: int, height: int) -> np.ndarray:
result = cv2.resize(image, (width, height), cv2.INTER_CUBIC)
return result
if __name__ == '__main__':
heat_map = HeatMap()
heat_map()
Version1クラス
webからサンプルを取得して作成したクラスです。ごめんなさい。引用元が不明になってしまいました。
処理時間: 3,907[msec]
ヒートマップは、指だな?と感じられます。
for文を多用しているせいか、これ以上の高速化は望めませんでした。色のブレンドはカッコ良いと思います。元ソースに感謝です。
version1.py
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import math
from colour import Color
import numpy as np
from scipy.interpolate import griddata
from temp_data import temp_data
class Version1():
# クラスにする必要は無いと思います
def __call__(self, parameters: dict) -> np.array:
color_depth = 1024
pixels = temp_data()
pixels = [
map_value(
p, parameters.min, parameters.max, 0, color_depth - 1)
for p in pixels]
sqrt = int(parameters.pixels ** 0.5)
points = [(
math.floor(ix / sqrt),
(ix % sqrt)) for ix in range(0, sqrt * sqrt)]
grid_x, grid_y = np.mgrid[
0: (sqrt - 1): 32j,
0: (sqrt - 1): 32j]
bicubic = griddata(
points,
pixels,
(grid_x, grid_y),
method='cubic')
low = Color('darkblue')
high = Color('red')
colors = list(low.range_to(high, color_depth))
img = [[[255] * 3] * bicubic.shape[0]] * bicubic.shape[1]
img = np.asarray(img)
# やはりfor文を使うと時間がかかります
for ix, row in enumerate(bicubic):
for jx, pixel in enumerate(row):
rgb = colors[
constrain(
int(pixel),
0,
color_depth- 1
)].rgb
img[ix, jx, 0] = rgb[2] * 255
img[ix, jx, 1] = rgb[1] * 255
img[ix, jx, 2] = rgb[0] * 255
img = img.astype('uint8')
return img
def constrain(val: int, min_val: int, max_val: int) -> int:
return min(max_val, max(min_val, val))
def map_value(x: int, in_min: int, in_max: int,
out_min: int, out_max: int) -> float:
result = 0
try:
result = (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
except:
pass
return result
if __name__ == '__main__':
pass
Version2クラス
Version1クラスで、高速化が望めず、それならばnumpyで処理!と意気込んで作成したクラスです。
処理時間: 25[msec]
ヒートマップは、なんとか指だな?と感じられます。
黄色が足らない・・・。と感じます。ほぼ、青と赤ですね・・・。
数値と色変換が甘く、赤成分と青成分のみ演算しています。
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import numpy as np
from temp_data import temp_data
class Version2():
# クラスにする必要は無いと思います
def __call__(self, parameters: dict) -> np.array:
sqrt = int(parameters.pixels ** 0.5)
img = np.zeros((sqrt, sqrt, 3), dtype='uint8')
pitch = (parameters.max - parameters.min) / 255
for x in range(sqrt):
for y in range(sqrt):
temp = temp_data()[x * 8 + y]
img[x, y, 2] = (temp - parameters.min) / pitch
img[x, y, 0] = (parameters.max - temp) / pitch
return img
if __name__ == '__main__':
pass
Version3クラス
処理時間: 3[msec]
もはや、何が表示されているかわかりません。
緑成分を入れようと作成を始めたのですが、温度から緑ってどうするんだろう?と悩んだ結果です。
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import numpy as np
from temp_data import temp_data
class Version3():
# クラスにする必要は無いと思います
def __call__(self, parameters: dict) -> np.array:
temp = np.array(temp_data())
temp *= 1.5
temp = temp.reshape(8, 8)
blue = temp - parameters.min
green = 0
red = parameters.max - temp
sqrt = int(parameters.pixels ** 0.5)
img = np.zeros((sqrt, sqrt, 3), dtype='uint8')
pitch = (parameters.max - parameters.min) / 255
img[:, :, 0] = 255 - blue / pitch
img[:, :, 1] = green
img[:, :, 2] = 255 - red / pitch
return img
if __name__ == '__main__':
pass
Version4クラス
処理時間: 10[msec]
ヒートマップは、指だな?と感じられます。
webでいろいろ情報を集めたところ、シグモイド関数を使うと、良いらしいことが分かりました。
画像データをnumpy.ndarrayで処理できるように、def sigmoid()を作成しました。
現在のサーモ AI デバイス TiDは、この処理を用いています。
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import numpy as np
from temp_data import temp_data
class Version4():
def __call__(self, parameters: dict) -> np.array:
self.parameters = parameters
width = self.parameters.max - self.parameters.min
temp = np.array(temp_data())
temp = temp - self.parameters.min
temp = temp / width
temp = temp.reshape(8, 8)
sqrt = int(self.parameters.pixels ** 0.5)
img = np.zeros((sqrt, sqrt, 3), dtype='uint8')
img[:, :, 0]\
= self.color_bar_rgb(temp)[0]
img[:, :, 1]\
= self.color_bar_rgb(temp)[1]
img[:, :, 2]\
= self.color_bar_rgb(temp)[2]
return img
def color_bar_rgb(self, x: np.ndarray) -> list:
"""
Return list of numpy.ndarray.
"""
x = (x * 2) - 1
red = sigmoid(x, self.parameters.gain, -1 * self.parameters.offset_x)
blue = 1 - sigmoid(x, self.parameters.gain, self.parameters.offset_x)
green = sigmoid(x, self.parameters.gain, self.parameters.offset_green)
green += (1 - sigmoid(
x, self.parameters.gain, -1 * self.parameters.offset_green))
green = green - 1.0
blue = blue * 255
green = green * 255
red = red * 255
return [blue, green, red]
def sigmoid(x: np.ndarray, gain=1, offset_x=0) -> np.ndarray:
return ((np.tanh(((x + offset_x) * gain) / 2) + 1) / 2)
if __name__ == '__main__':
pass
まとめ
Version1 | Version2 | Version3 | Version4 |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
指です | 指です | なんとなく指かも? | 指です |