コンテンツにスキップ

Top

Flir Lepton 3.5

Flir Lepton 3.5 + Raspberry Pi 3でサーモグラフィー

出来上がり

サーモグラフィー画像

注意点①

この記事で取り扱っているのは
Flir Lepton 3.5
Flir Lepton 3.5
です。

3.5のピクセル数は 160 x 120 になります。(3.0も同様です)
それに対し、Flir Lepton 2.5 は 80 x 60 になります。
その場合、以下に記載しているプログラムはサイズの違いによりそのままでは動きませんので、ご注意ください。

また、通常のカメラ(Raspberry Pi Camera Module V2)は 640 x 480 としています。こちらもお使いのカメラに合わせて適宜コードを修正してください。
(どこかのタイミングでソースコードをきれいにしようとは思っていますが時間がないので)

注意点②

本サイトでは
Lepton Breakout Board v1.4
Lepton Breakout Board v1.4
を用いていますが、

Flir社からは推奨されていない

とのことです。確かにちょっと動作不安定なところがあります。

推奨されている
PureThermal2
をできれば用いましょう。
PURETHERMAL 2
https://www.digikey.jp/product-detail/ja/groupgets-llc/PURETHERMAL-2/2077-PURETHERMAL-2-ND/9866290

ただし、PureThermal2とLepton Breakout Boardでは接続の仕方からプログラムから異なりますので本記事は参考になりません
別のサイトをググりましょう。
(追記)
一応、PureThermal2 でのプログラムを書いてみたよ! https://jitaku.work/it/product/flir/lepton35-purethermal2-opencv/

Breakout Board買っちった(ノω・)テヘ
という人は続きを。

準備物

  • Raspberry Pi 3 Model B
  • Flir Lepton 3.5
  • FLIR Lepton Breakout Board v1.4
  • メス-メス ジャンパワイヤ
  • Raspberry Pi Camera Module V2

配線

以下の通り配線する

CS -> Pin 24, CE0 or Pin 26, CE1
MOSI -> Pin 19, MOSI
MISO -> Pin 21, MISO
CLK -> Pin 23, CLK
GND -> Pin 25, GND
VIN -> Pin 1, 3V3
SDA -> Pin 3, SDA
SCL -> Pin 5, SCL

Raspberry Pi準備

1. NOOBS等でRaspbianをインストール

2. CameraとSSHとSPIとI2Cを有効にする

1
2
$ sudo raspi-config
$ sudo reboot

3. パッケージを最新にする

1
2
$ sudo apt-get update && sudo apt-get upgrade
$ sudo reboot

4. swap領域増加

1
2
3
4
5
6
7
$ sudo vi /etc/dphys-swapfile
CONF_SWAPSIZE=2048
CONF_MAXSWAP=2048

設定を反映  
$ sudo service dphys-swapfile stop
$ sudo service dphys-swapfile start

5. おまじない

1
2
3
4
$ sudo vi /boot/cmdline.txt

先頭に記載  
spidev.bufsiz=131072

6. pythonとpython-opencvを入れる

1
2
$ sudo apt-get install -y python3-dev
$ sudo apt-get install -y python-opencv

7. OpenCVを入れる(すごい時間掛かる)

まず、makeやcmake、libgtk2.0-devなど必要なパッケージを入れる。

1
$ sudo apt-get install -y libgtk2.0-dev build-essential cmake pkg-config

OpenCVをダウンロードしてインストールする。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ wget -O opencv.zip https://github.com/Itseez/opencv/archive/3.3.0.zip
$ unzip opencv.zip
$ wget -O opencv_contrib.zip https://github.com/Itseez/opencv_contrib/archive/3.3.0.zip
$ unzip opencv_contrib
$ cd opencv-3.3.0/
$ mkdir build
$ cd build
$ cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D INSTALL_PYTHON_EXAMPLES=ON -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib-3.3.0/modules -D BUILD_EXAMPLES=ON ..
$ make -j4
$ sudo make install
$ sudo ldconfig

pylepton

pyleptonは有志が作ってくれたFlir Lepton用のライブラリです。MIT Licenseなので商用利用でも問題ありません。

1. pyleptonを入れる

branchはlepton3-devを用いること

1
2
3
4
$ git clone https://github.com/groupgets/pylepton.git
$ cd pylepton/
$ git checkout lepton3-dev
$ sudo python setup.py install

2. サンプルコードを試してみる

1
$ pylepton_overlay -a 200

もしくは、

1
$ pylepton_overlay -a 200 -d "/dev/spidev0.1"

・・・ノイズだらけ。
pylepton_overlayのwhileループの中にsleepを入れたらややノイズが落ち着いた。

サーモグラフィーを作成する

Flir Lepton 3.5を用いてサーモグラフィーを作成する。
名前はmylepton.pyとする。

1. mylepton.pyの作成

1
$ vi mylepton.py
# -*- coding: utf-8 -*-

import os
import cv2
import io
import numpy as np
import picamera
import time
import traceback
from pylepton.Lepton3 import Lepton3

def setColorBar(lepton_buf, _low, _high):

    d = (_high - _low) / 120.0
    i = 0 
    for i in range(120):
        if i == 0:
            continue
        if i == 119:
            break
        _d = d * i
        lepton_buf[i][152] = _high - int(_d)
        lepton_buf[i][151] = _high - int(_d)
        #print("%d,%d" % (i,lepton_buf[i][152]))

def main(alpha = 0.5, device = "/dev/spidev0.0", high = 70, low = 10):

    alpha = float(alpha)
    high  = int(high)
    low   = int(low)

    if high < low:
        exit

    _high = int(high) * 100 + 27315
    _low  = int(low)  * 100 + 27315

    stream = io.BytesIO()

    cv2.namedWindow('image', cv2.WND_PROP_FULLSCREEN)
    cv2.setWindowProperty('image', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)

    lepton_buf = np.zeros((120, 160, 1), dtype=np.uint16)

    with picamera.PiCamera() as camera:
        camera.resolution = (640, 480)
        camera.framerate  = 10
        try:
            with Lepton3(device) as l:
                last_nr = 0

                while True:
                    lepton_buf, nr = l.capture()
                    if nr == last_nr:
                        k = cv2.waitKey(1)
                        if k==27:
                            break
                        continue

                    stream.seek(0)
                    camera.capture(stream, format='jpeg')
                    data = np.fromstring(stream.getvalue(), dtype=np.uint8)
                    image = cv2.imdecode(data, 1)
                    #im = image[38+18:38+18+384, 51+35:51+35+512]
                    #image = cv2.resize(im, (640, 480))

                    last_nr = nr

                    # error skip
                    data_1d = lepton_buf.ravel()
                    if np.min(data_1d) == 0:
                        k = cv2.waitKey(1)
                        if k==27:
                            break
                        continue

                    # backup for temperature
                    lepton_temp = np.copy(lepton_buf)

                    # for color bar
                    lepton_buf = np.clip(lepton_buf, _low, _high)
                    setColorBar(lepton_buf, _low, _high)

                    cv2.normalize(lepton_buf, lepton_buf, 0, 65535, cv2.NORM_MINMAX)
                    np.right_shift(lepton_buf, 8, lepton_buf)

                    h, w, _ = image.shape
                    _image  = np.asarray(image, np.uint8)
                    _lepton = np.asarray(lepton_buf, np.uint8)
                    _lepton_gray = cv2.cvtColor(_lepton, cv2.COLOR_GRAY2RGB)
                    _lepton_gray = cv2.resize(_lepton_gray, (w, h))
                    _lepton_gray = cv2.applyColorMap(_lepton_gray, cv2.COLORMAP_JET)
                    res = cv2.addWeighted(_image, alpha, _lepton_gray, 1.0 - alpha, 0)

                    h = 2
                    w = 2
                    x1 = 160 / 4 * 1 - w
                    y1 = 120 / 4 * 1 - h 
                    x2 = 160 / 4 * 3 - w
                    y2 = 120 / 4 * 1 - h
                    x3 = 160 / 4 * 2 - w  
                    y3 = 120 / 4 * 2 - h 
                    x4 = 160 / 4 * 1 - w 
                    y4 = 120 / 4 * 3 - h
                    x5 = 160 / 4 * 3 - w
                    y5 = 120 / 4 * 3 - h

                    cv2.rectangle(res, (x1 * 4, y1 * 4), ((x1 + w) * 4, (y1 + h) * 4), (0, 255, 0), -1)
                    cv2.rectangle(res, (x2 * 4, y2 * 4), ((x2 + w) * 4, (y2 + h) * 4), (0, 255, 0), -1)
                    cv2.rectangle(res, (x3 * 4, y3 * 4), ((x3 + w) * 4, (y3 + h) * 4), (0, 255, 0), -1)
                    cv2.rectangle(res, (x4 * 4, y4 * 4), ((x4 + w) * 4, (y4 + h) * 4), (0, 255, 0), -1)
                    cv2.rectangle(res, (x5 * 4, y5 * 4), ((x5 + w) * 4, (y5 + h) * 4), (0, 255, 0), -1)

                    lepton_temp1 = lepton_temp[y1:y1+h, x1:x1+w]
                    lepton_temp2 = lepton_temp[y2:y2+h, x2:x2+w]
                    lepton_temp3 = lepton_temp[y3:y3+h, x3:x3+w]
                    lepton_temp4 = lepton_temp[y4:y4+h, x4:x4+w]
                    lepton_temp5 = lepton_temp[y5:y5+h, x5:x5+w]

                    tmp1 = (np.max(lepton_temp1.ravel()) - 27315) / 100.0
                    tmp2 = (np.max(lepton_temp2.ravel()) - 27315) / 100.0
                    tmp3 = (np.max(lepton_temp3.ravel()) - 27315) / 100.0
                    tmp4 = (np.max(lepton_temp4.ravel()) - 27315) / 100.0
                    tmp5 = (np.max(lepton_temp5.ravel()) - 27315) / 100.0

                    cv2.putText(res, str(tmp1), (x1 * 4 - 40, y1 * 4 + 40), cv2.FONT_HERSHEY_COMPLEX, 1.0, (255, 255, 255))
                    cv2.putText(res, str(tmp2), (x2 * 4 - 40, y2 * 4 + 40), cv2.FONT_HERSHEY_COMPLEX, 1.0, (255, 255, 255))
                    cv2.putText(res, str(tmp3), (x3 * 4 - 40, y3 * 4 + 40), cv2.FONT_HERSHEY_COMPLEX, 1.0, (255, 255, 255))
                    cv2.putText(res, str(tmp4), (x4 * 4 - 40, y4 * 4 + 40), cv2.FONT_HERSHEY_COMPLEX, 1.0, (255, 255, 255))
                    cv2.putText(res, str(tmp5), (x5 * 4 - 40, y5 * 4 + 40), cv2.FONT_HERSHEY_COMPLEX, 1.0, (255, 255, 255))

                    j = 10
                    d = (high - low) / 5.0
                    for i in range(6):
                        _d = d * i
                        _m = "-" + str(high - int(_d))
                        _n = j + (i * 92)
                        cv2.putText(res, _m, (610,  _n), cv2.FONT_HERSHEY_COMPLEX, 0.4, (255,255,255))

                    cv2.imshow('image', res)
                    k = cv2.waitKey(33)
                    if k==27:    # Esc key to stop
                        break
                    if k==ord('s'):
                        cmd = "scrot"
                        os.system(cmd)
        except Exception:
            traceback.print_exc()
        finally:
            camera.close()

if __name__ == '__main__':
    from optparse import OptionParser

    usage = "usage: %prog [options] output_file[.format]"
    parser = OptionParser(usage=usage)
    parser.add_option("-a", "--alpha",  dest="alpha",  default=0.5,              help="set lepton overlay opacity")
    parser.add_option("-d", "--device", dest="device", default="/dev/spidev0.0", help="specify the spi device node (might be /dev/spidev0.1 on a newer device)")
    parser.add_option("-H", "--high",   dest="high",   default=70,               help="highest value on display")
    parser.add_option("-L", "--low",    dest="low",    default=10,               help="lowest value on display")

    options = parser.parse_args()[0]

    main(alpha = options.alpha, device = options.device, high = options.high, low = options.low)

2. myleptonの実行

$ python mylepton.py -d "/dev/spidev0.1" -H 70 -L 10 -a 0.5

以上!