Raspberry piで超簡単！物体認識カメラ

[ラクス Advent Calendar2017](https://qiita.com/advent-calendar/2017/rakus)も2桁突入の10回目です。
昨日はfu-jiさんの[CUIに苦手意識がある人でも使えるコマンド](https://qiita.com/fu-ji/items/bc6d8483829b2dde3334)でした。
自分が使うコマンドにも偏りがありそうだなぁ、と思わせてくれる面白い記事でした。

さて、今回はラクスの**自称**IoT部長としての責務を果たすべく、Raspberry piとAIにチャレンジしてみたいと思います。
Web系の会社に所属しながらデバイスが持つ魅力に取り憑かれ、色々なデバイスに手を出しています。
ラズパイだけで言うと、Raspberry pi B+が発表された2013年頃からラズパイに魅せられ、B+を2台、pi2を1台、pi3を1台と~~ムダ~~順調に増え続けています。
Zeroも欲しいのですが、残念ながらRaspberry pi Zero Wを未だに手に入れることができていません。人気があってなのか在庫が無いようです。安定して供給されるようになるまでもう少し我慢中。

今回のテーマはラズパイを使った物体認識のお話です。
昨今だと、Google Vision APIなどのクラウドサービスを使えば簡単にできそうですが、クラウドを使わずにやります。
とはいえ、難しいことに手を出すとモチベーションが続かないので、できるだけ簡単な方法を考えました。

# 前提
物体認識と言うは易し。実はめちゃくちゃ難しいので、識者が公開してくれている学習済みのデータ等を利用する。

# 今回準備したデバイス
* Raspberry pi3
* 3.5インチ LCD TFT Touch screen（今回はlandzoのディスプレイを利用）
* Raspberry pi Camera board rev1.3

こんな感じ。
![top.jpg](https://qiita-image-store.s3.amazonaws.com/0/208639/4b0d85c1-b289-13fb-184a-5152df672742.jpeg)


# ソフトウェア
* RASPBIAN STRETCH WITH DESKTOP
* LCDのドライバー（後述）
* [DeepBeliefSDK](https://github.com/jetpacapp/DeepBeliefSDK) （後述）
* Python Pygame

# LCDのセットアップ
今回用意したLCDは比較的簡単にセットアップできます。
基本的には下記サイトにあるドライバーを使ってセットアップ手順に従うだけ。

https://www.waveshare.com/wiki/3.5inch_RPi_LCD_(A)

利用したドライバーは[LCD-show-161112.tar.gz](http://www.waveshare.com/w/upload/4/4b/LCD-show-161112.tar.gz)です。
セットアップ手順はドライバーをダウンロードして解凍後、`LCD35-show`コマンドを実行するだけ。

```bash
tar xvf LCD-show-*.tar.gz
cd LCD-show/
chmod +x LCD35-show
./LCD35-show
```
XWindowを起動するとこんな感じ。
![CAF9F9ED-FB6A-41BB-BDCD-8687463F4F40.jpeg](https://qiita-image-store.s3.amazonaws.com/0/208639/cb6f5b9b-e338-705f-9506-635412861719.jpeg)

ディスプレイを180度回転したい場合は、下記のようにします。

```bash
./LCD35-show 180
```

# DeepBeliefSDKのセットアップ
`DeepBeliefSDK`とはラズパイだけでなく、iOSやAndroidなどでも動作する深層学習済みの画像認識フレームワークです。
では早速セットアップしましょう。

1. `/boot/config.txt`の最終行に`gpu_mem=128`を追加してreboot
2. 下記の通りDeepBelliefSDKを[GitHub](https://github.com/jetpacapp/DeepBeliefSDK)からcloneしてmake  
参考：https://github.com/jetpacapp/DeepBeliefSDK#getting-started-on-a-raspberry-pi-2

```bash
mkdir ~/src
cd ~/src
sudo apt-get install -y mercurial
hg clone https://bitbucket.org/eigen/eigen
ln -s ~/src/eigen ~/projects/DeepBeliefSDK/eigen
cd ~/src/DeepBeliefSDK/source
make clean
sudo apt-get install gcc-4.8 g++-4.8
sudo rm -rf /usr/bin/gcc
sudo rm -rf /usr/bin/g++
sudo ln -s /usr/bin/gcc-4.8 /usr/bin/gcc
sudo ln -s /usr/bin/g++-4.8 /usr/bin/g++
make GEMM=eigen TARGET=pi3
```


# pygameのインストール

```bash
sudo apt-get install python-pygame
```
pipが良い方は`pip install pygame`ですね。

# カメラのセットアップ
1. `raspi-config` を実行してCameraを有効にする。
2. `pygame.camera`で`/dev/video0`のデバイスを使うために  
   `/etc/modules-loaded.d/modules.conf`に`bcm2835-v412`を追記する。

```bash
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.

i2c-dev
bcm2835-v4l2
```

# プログラム

```python
# -*- coding: utf-8 -*-
import pygame
import pygame.camera
import io
import os
import subprocess

def main():

    # Init framebuffer/touchscreen environment variables
    os.putenv('SDL_VIDEODRIVER', 'fbcon')
    os.putenv('SDL_FBDEV'      , '/dev/fb1')
    os.putenv('SDL_MOUSEDRV'   , 'TSLIB')
    os.putenv('SDL_MOUSEDEV'   , '/dev/input/touchscreen')
    (x,y) = (0,0)

    pygame.init()
    pygame.camera.init()
    pygame.font.init()
    pygame.mouse.set_visible(False)

    font = pygame.font.SysFont('Comic Sans MS', 24)
    screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
    camera = pygame.camera.Camera("/dev/video0", (640,480))
    camera.start()

    while True:
        snapshot = camera.get_image()
        screen.blit(snapshot, (x, y))

        for event in pygame.event.get():
            # take picture 
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                filename = "/tmp/camera.jpg"
                pygame.image.save(snapshot, filename)
                cmd = '/home/pi/src/DeepBeliefSDK/source/jpcnn -i ' + filename + ' -n /home/pi/src/DeepBeliefSDK/networks/jetpac.ntwk  -m s'
                proc = subprocess.Popen(cmd, shell=True,stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
                stdout, stderr = proc.communicate()
                result = ["0.0",""]
                for line in stdout.splitlines():
                    data = line.split("\t")
                    if float(result[0]) < float(data[0]):
                        result = data
                textsurface = font.render(result[1], False, (0, 0, 0))
                screen.blit(textsurface,(0,0))
                pygame.display.update()
                pygame.time.wait(2000)
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        pygame.display.update()
        pygame.time.wait(30)

if __name__ == "__main__":
        main()
```

# 実行
コンソールから上述のpythonコードをrootで実行する。

![recog.jpg](https://qiita-image-store.s3.amazonaws.com/0/208639/1e35c636-db0e-1787-5926-f678f837dddb.jpeg)


![76A2C441-F0CC-439A-B2D3-7A7E1E0D9036.jpeg](https://qiita-image-store.s3.amazonaws.com/0/208639/951ba4b4-5a75-515f-2b99-a65c4bb871f2.jpeg)


おおおおお！画面左上に`coffee mug`の文字が！

# 感想
認識にかかる時間はもうちょっと早いかなと思っていましたが、約10秒ほどかかります（遅っ！）。
しかし、学習済みデータを使うので、深層学習について深く知る必要はなく、ここまで簡単に試すことができました。
今回試した限りだとマグカップ以外に、PCのキーボード、iPodを認識しました。実際はiPodではなく、iPadだったんですが:scream:写真から物体の大きさが読み取れないので間違っても仕方ないかもしれません。
認識率で言うと決して高く無いようですが、深層学習をここまで手軽に試せるようになったことには驚きました。
これから色々なデバイスが今以上に進化していくと思うと楽しみでなりません。
Raspberry pi4の噂もちらほら聞きますので、今から待ち遠しいですね！