開発ブログ

第30回 パペロで赤外線アレイセンサを動かしてみよう!【中編:顔の表面温度を測ってみよう】

投稿日時:2020年8月19日 14:08

こんにちは。ピャペロンです。
今回は前回の温度を取得した続きから進めていくよ!

前回のリンク第29回 パペロで赤外線アレイセンサを動かしてみよう!【前編:簡易サーモグラフィを作ろう】


今回行うこと

赤外線アレイセンサを使い顔の表面温度を取得する


顔の検出

まずは顔の検出だよ!今回はOpenCVを使って顔の検出を行うよ!

OpenCVはオープンソースのコンピュータービジョンライブラリの一つで、画像の処理以外にも画像の解析や物体検出など様々なことができるよ!

OpenCVと言えば最近OpenCV AI Kitを発表して話題になったパぺ!

まずはさっくりと顔認識するプログラムを作ってみよう!以下のプログラムを実行してみよう!

#!/usr/bin/env python3
# sample30-1.py
import numpy as np
import cv2 as cv

GREEN = (0, 255, 0)  # B(青)・G(緑)・R(赤)
WIDTH = 800  # 画面幅
HEIGHT = (int)(WIDTH*3/4)

capture = cv.VideoCapture(0)  # VideoCapture オブジェクトを取得する
face_cascade_path = './haarcascade_frontalface_default.xml'  # 学習データ
face_cascade = cv.CascadeClassifier(face_cascade_path)  # カスケード分類機

while(True):
    ret, frame = capture.read()  # 画像キャプチャ
    frame = cv.resize(frame, (WIDTH, HEIGHT))  # 画像のリサイズ
    frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)  # 画像を白黒化して顔識別を高速化
    # 顔識別をする。最小サイズは画面の1/16以上
    faces = face_cascade.detectMultiScale(
        frame_gray, scaleFactor=1.2, minNeighbors=2)

    # 見つかった顔の処理
    for x, y, w, h in faces:
        # ターゲットマーカー
        cv.rectangle(frame, (x, y), (x+w, y+h), GREEN)

    cv.imshow('FACE CAPTURE TEST', frame)
    # Qを押された際にbreakする
    if cv.waitKey(1) & 0xFF == ord('q'):
        break

capture.release()
cv.destroyAllWindows()
print("終了")

実行してカメラに顔を向けると顔に四角い枠が表示されるよ!

これで顔の認識はできるようになったね!次は温度の取得および補正をしてみよう!


温度の補正

上の内容で顔の位置がわかったので次は前回の内容と組み合わせて顔表面の温度を取得していこう!

測定値は冷風が当たる、近くに発熱減があるなどの外部の環境やセンサの個体差によって変わるから注意が必要!

ピャペロンの環境だとセンサから遠いと低めにばらつきが大きく、近いと高めに安定する傾向にあったよ。

先ほどの変動をふまえて今回の環境ではこのように温度補正をしたよ!

max_temp = max_temp / 4 - 0.01 * w + 12  # 顔の大きさが小さいほど(距離が遠いほど)補正を多く入れる

図分で動かしてみるときは例えば、体温計で測った値に近くになるように調整するなどしてみよう!


顔表面の温度取得

以下のプログラム含め下のように配置してあげるよ!

./
├── haarcascade_frontalface_default.xml
├── hogehoge.ttf
└── sample30-2.py

haarcascade_frontalface_default.xmlはGitHubのOpenCVプロジェクト内のhaarcascade_frontalface_default.xmlに、フォントは好みのものを選んでね!

#!/usr/bin/env python3
# sample30-2.py
import numpy as np
from PIL import ImageFont, ImageDraw, Image
import cv2 as cv
import time
import smbus

bus = smbus.SMBus(1)
size = 8  # 赤外線アレイセンサの列数
addr = 0x69  # 取得アドレス 0x68と選択式
GREEN = (0, 255, 0)  # B(青)・G(緑)・R(赤)
RED = (0, 0, 255)
SIZE = 8  # 一列のセンサー数
WIDTH = 800  # 画面幅
HEIGHT = (int)(WIDTH*3/4)

capture = cv.VideoCapture(0)  # VideoCapture オブジェクトを取得する

fontpath = './hogehoge.ttf'  # 任意のフォントの指定を行う
font = ImageFont.truetype(fontpath, 24)  # フォントサイズ24
small_font = ImageFont.truetype(fontpath, 12)  # フォントサイズ12
face_cascade_path = './haarcascade_frontalface_default.xml'  # 学習データ
face_cascade = cv.CascadeClassifier(face_cascade_path)  # カスケード分類機
fps = 0  # FPS初期値


def write_scale(array):
    # 文字系描写
    draw.text(((int)(WIDTH/2-120), 0), 'PRESS "Q" TO EXIT',
              font=font, fill=GREEN)  # 終了方法を表示
    draw.line(((0, 120), (fps, 120)), fill=RED, width=5)
    draw.text((0, 80), "FPS:"+str(fps), font=font, fill=GREEN)  # FPSを表示
    draw.line(((0, 160), (len(faces)*10, 160)), fill=RED, width=5)
    draw.text((0, 120), "PCS:"+str(len(faces)),
              font=font, fill=GREEN)  # 対象数を表示
    for i in range(SIZE):
        for j in range(SIZE):
            draw.text(((int)(WIDTH/SIZE*j+WIDTH/16), (int)(HEIGHT/SIZE*i+HEIGHT/16)),
                      "%2.2f" % (array[i][j]/4), font=small_font, fill=GREEN)  # 温度表示


def readdata():
    linedata = []
    for i in range(8):
        data = bus.read_i2c_block_data(addr, 0x80+0x10*i, 16)
        oneline = []
        for j in range(8):
            oneline.append((int((data[2*j+1] & 0x07) * 256 + data[2*j])))
        linedata.append(oneline)
    return(linedata)


# センサー安定のため30秒暖機運転
for wakeuptime in range(30, 0, -1):
    readdata()
    print("\r残り"+"%02d秒" % wakeuptime, end="")
    time.sleep(1)
print("\r****起動****")
while(True):
    face_no = 0
    start = time.time()  # 開始時間
    linedata = readdata()  # Grid-Eyeから温度情報を取得
    temp_array = np.asarray(linedata)  # listをnumpyarrayに
    ret, frame = capture.read()  # 画像キャプチャ
    frame = cv.resize(frame, (WIDTH, HEIGHT))  # 画像のリサイズ
    frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)  # 画像を白黒化して顔識別を高速化
    # 顔識別をする。最小サイズは画面の1/16以上
    faces = face_cascade.detectMultiScale(
        frame_gray, scaleFactor=1.2, minNeighbors=2, minSize=((int)(WIDTH/4), (int)(HEIGHT/4)))
    img_pil = Image.fromarray(frame)  # 配列の各値を8bit整数型(0~255)をPIL Imageに変換
    draw = ImageDraw.Draw(img_pil)  # drawインスタンスを生成

    write_scale(temp_array)
    # 見つかった顔の処理
    for x, y, w, h in faces:

        # ターゲットマーカー
        draw.rectangle([x, y, x+180, y-30], GREEN, width=3)
        draw.text((x, y-30), "W:"+str(w)+" H:" +
                  str(h), font=font, fill=RED)  # 座標表示
        # 枠の表示
        draw.rectangle([x, y+h/3, x+w, y+h-h/3], outline=GREEN)
        draw.rectangle([x+w/3, y, x+w-w/3, y+h], outline=GREEN)
        draw.rectangle([x, y, x+w, y+h], outline=RED, width=3)
        draw.line(((x+w/2, y), (x, y+h/2), (x+w/2, y+h),
                   (x+w, y+h/2), (x+w/2, y)), fill=RED)
        # 識別順表示
        draw.text((x+w/2, y+h), str(face_no+1), font=font, fill=GREEN)
        max_temp = 0  # 検出した最高温度表示
        # 検出範囲の温度マップ表示
        for i in range((int)(size*x/WIDTH), (int)(size*(x+w)/WIDTH+0.5)):
            for j in range((int)(y/HEIGHT*size), (int)((y+h)/HEIGHT*size+1)):
                draw.text([(int)(WIDTH/size*i+WIDTH/16), (int)(HEIGHT/size*j+HEIGHT/16)],
                          "%2.2f" % (temp_array[j][i]/4), font=small_font, fill=RED)
                if max_temp < temp_array[j][i]:
                    max_temp = temp_array[j][i]
        # 測定データから温度の値に変え補正値を追加(各自調整お願いします)
        max_temp = max_temp / 4 - 0.01 * w + 12.
        # 温度を表示
        draw.text((x+w/2+20, y+h), str("%2.1f℃" %
                                       (max_temp)), font=font, fill=GREEN)
        # 下部に検出した顔一覧を表示
        face = frame[y: y + h, x: x + w]
        face_pil = Image.fromarray(face).resize((50, 50))
        img_pil.paste(face_pil, ((0+face_no % 2*50), (200+(face_no >> 1)*70)))
        draw.text(((15+face_no % 2*50), (245+(face_no >> 1)*70)),
                  str(face_no+1), font=font, fill=GREEN)
        face_no = face_no + 1

    frame = np.array(img_pil)  # imgpilを画像にする
    cv.imshow('FACE CAPTURE TEST', frame)
    # Qを押された際にbreakする
    if cv.waitKey(1) & 0xFF == ord('q'):
        break
    # 開始時間から現在の時間をもとにFPSを算出する(0除算対策に1e-308秒加算済)
    fps = (int)(1/(time.time() - start + 1e-308))

capture.release()
cv.destroyAllWindows()
print("終了")

これを実行すると顔の下に温度が表示されるよ!

次回はこれらの取得できたデータを元にパペロと連携させていくよ!


0

最新開発ブログ一覧  (一覧で確認
2020年8月19日 14:08
開発ブログ
第30回 パペロで赤外線アレイセンサを動かしてみよう!【中編:顔の表面温度を測ってみよう】
by 管理者
2020年7月22日 16:07
開発ブログ
第29回 パペロで赤外線アレイセンサを動かしてみよう!【前編:簡易サーモグラフィを作ろう】
by 管理者
2019年11月20日 17:11
開発ブログ
第28回 パペロでJulius4.5を実行してみた
by 管理者
2019年11月13日 09:11
開発ブログ
第27回 JuliusをPaPeRoi向けにクロスコンパイルしてみた
by 管理者
2019年9月19日 17:09
開発ブログ
第26回Linaro Toolchainでクロスコンパイルしてみた
by 管理者

コメントは受け付けていません。