RaspberryPiで機械学習をやってみた

# RaspberryPiで機械学習をやってみた

[Raspberry Pi Advent Calendar 2019](https://qiita.com/advent-calendar/2019/raspberry-pi)の21日目です。

なんとなく機械学習を触ってみたい方、
画像処理に機械学習を取り入れてみたい方、
ラズパイ4を買ってみたけど特にすることがない方向けに記事を書きます。

画像処理に初めて機械学習を使った時に「[dlib](http://dlib.net/)」というライブラリを使ったのですが、
これが~~中身を理解しなくても~~使いやすかったのでご紹介させていただきます。

## 概要

ラズパイにwebカメラをつなげ、取得した画像データから任意の物体の位置を検出します。

物体の位置は物体の形状（エッジ）を学習することで検出します。

形状認識で用いられる特徴量にはHOG・SIFT・SURFなどがあるのですが、
dlibではHOG特徴量を用いています。

- 特徴
    - HOG(Histograms of Oriented Gradients)…輪郭がはっきりと映っている画像に有効
    - SIFT…ぼやけている画像にも有効。回転・スケールの変更にも強く、ロバスト性が高い。
    - SURF…ぼやけている画像にも有効。SIFTの高速処理バージョン。

以下にHOG特徴量の例を示します。

（なんとなくエッジが見えると思うのですが、これが輝度の勾配をベクトルで示したものとなります）

![icon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266068/24d5bdce-a684-c567-d3f5-94736598b8f6.png)

これをヒストグラム化して物体の特徴とするのですが、今回は説明しません.


機械学習のフレームワークにはSVM（Support Vector Machine）を用いています。

- 特徴
    - SVM…いい感じの判別器（雑）。先ほどの作成したHOG特徴量を元に物体を判別する。
  

## 環境
- ラズパイ4(メモリ4GB)
    - OS:Raspbian GNU/Linux 10  
    - python(3.7.3)…使用言語
    - OpenCV(3.2.0)…画像処理用のライブラリ
    - dlib(19.0.0)…機械学習用のライブラリ


- [C270n HD Webcam](https://www.amazon.co.jp/%E3%83%AD%E3%82%B8%E3%82%AF%E3%83%BC%E3%83%AB-%E3%82%A6%E3%82%A7%E3%83%96%E3%82%AB%E3%83%A1%E3%83%A9-C270n-%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%9F%E3%83%B3%E3%82%B0-2%E5%B9%B4%E9%96%93%E3%83%A1%E3%83%BC%E3%82%AB%E3%83%BC%E4%BF%9D%E8%A8%BC/dp/B07QMKND9M/ref=sr_1_4?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1YNPN42CH4ZVW&keywords=webcam+logicool&qid=1576419105&sprefix=webcam+%2Caps%2C245&sr=8-4)…お手頃なロジクールのカメラですが、USBカメラならなんでもいいです。



## 作業
1. 写真を取る
1. 写真からターゲットを学習させる
1. 学習させる
1. 学習した結果を使う

![process.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266068/b0053fff-8f73-38eb-3417-a33c522dedcc.png)


### 環境構築

#### python3のインストール

[Raspberry piにPython3.6を入れてみた（Raspberry Pi Advent Calendar 2019 1日目）](https://qiita.com/K_M95/items/6980f27e7424c890c89e)

上記にもあるように、pythonは3系を入れましょう！！

#### OpenCV…画像処理用のライブラリ
```	
$ pip install opencv-contrib-python
```

#### dlib…機械学習用のライブラリ
```
$ pip install dlib
```

#### 確認（重要）
```
$ python
>import cv2
>import dlib
```
pythonと打ち込んでインタラクティブモードに入ればOKです。cv2とdlibのimport時もエラーが出なければOKです。


### 画像を撮影する

```webcam_take_picture.py
# -*- coding: utf-8 -*-
import numpy as np
import cv2
import os  # rename用モジュール

i = 0 #  保存した写真名の初期値番号。

cap = cv2.VideoCapture(0)

while(True):
    # フレームをキャプチャする
    ret, frame = cap.read()

    # 画面に表示する
    cv2.imshow('frame',frame)

    # キーボード入力待ち
    key = cv2.waitKey(1) & 0xFF

    # [q]が押された場合は終了する
    if key == ord('q'):
        break

    # [s]が押された場合は保存する
    if key == ord('s'):
        i = i + 1
        path = str("{0:06d}".format(i)) + '.jpg'

        cv2.imwrite(path,frame)

# キャプチャの後始末と，ウィンドウをすべて消す
cap.release()
cv2.destroyAllWindows()
```

##### 使い方
```
1. 写真を撮影するプログラムを実行。
　　　　$ python3 webcam_take_picture.py
2. 学習させたい対象にカメラを向け[s]キーで写真を撮る(Save)。撮影枚数は50枚程度でいい。
3. [q]キーでウインドウを閉じる(Quit)。
```

homeの直下に「/owl」というフォルダを作り、
撮影した画像を「owl」に置く。



### 物体を選択する

写真中の物体はimglabを使って選択します。

imglabをビルドする

$ cd ~/dlib/tools/imglab/
$ sudo mkdir build
$ cd build
$ sudo cmake ..
$ sudo cmake --build . --config Release

撮影したowl内の写真をxmlにまとめる。
$ ./imglab -c ~/owl/all.xml ~/owl

imglabの起動
$./imglab ~/owl/all.xml

1. Labelをつける
2. 対象を選択する
3. 「training.xml」として保存する。

![imglab.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266068/830f8c31-8e41-d4ba-b505-1572f1e17aa5.png)


### 学習させる


```webcam_dlib_learning.py
import dlib

if __name__ == "__main__":

    TrainingXml = "~/owl/training.xml"
    SvmFile = "~/owl/detector.svm"
    options = dlib.simple_object_detector_training_options()
    options.add_left_right_image_flips = True
    options.C = 5
    options.num_threads = 4
    options.be_verbose = True

    dlib.train_simple_object_detector(TrainingXml, SvmFile, options)
```

```ターミナル
1. 学習させる
　　　　$ webcam_dlib_learning.py
```

学習結果はdetector.svmとして出力されます。

今回は学習に150枚の画像を用いたのですが、
ラズパイ4で学習させたら6分15秒で終わりました。
（ちなみにPC（core i7）で学習すると27秒で終わりました。PCで学習させて、学習させたものをラズパイで使うことも可能です）


### 学習した結果を使う

```webcam_dlib.py
import dlib
import cv2

if __name__ == "__main__":

    SvmFile = "~/owl/detector.svm"
    detector = dlib.simple_object_detector(SvmFile)
    cap = cv2.VideoCapture(0)
    if cap.isOpened() is not True:
        raise("IO Error")

    WinName = "Capture"
    cv2.namedWindow(WinName, cv2.WINDOW_AUTOSIZE)
    win_det = dlib.image_window()
    win_det.set_image(detector)

    while True:
        ret, img = cap.read()

        if ret:
            rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            dets = detector(rgb)
            if len(dets):
                d = dets[0]
                cv2.rectangle(img, (d.left(), d.top()), (d.right(), d.bottom()), (0, 0, 255), 2)
                x = (d.left()+d.right())/2
                y = (d.top()+d.bottom())/2
                cv2.putText(img,"Ex:"+str(x),(40,70),2,0.8,(0,255,0))
                cv2.putText(img,"Ey:"+str(y),(160,70),2,0.8,(0,255,0))
            cv2.imshow(WinName, img)

        key = cv2.waitKey(1)
        if key & 0x00FF  == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()

```
```ターミナル
1. 学習結果を使って、任意の物体を検出する。
　　　　$ python3 webcam_dlib.py
```

#*処理が重すぎる*

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ラズパイ4で機械学習をやってみた💃💃💃💃<br><br>が処理が重すぎた😇😇😇😇 <a href="https://t.co/XVIhkvFwVD">pic.twitter.com/XVIhkvFwVD</a></p>&mdash; 星ざめ (@hosizame) <a href="https://twitter.com/hosizame/status/1207685666756104204?ref_src=twsrc%5Etfw">December 19, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



## 参考
[HOG特徴とSVMによる物体検出/有限会社 石島電子技研](http://ishi.main.jp/ros/ros_obj_detection.html)
[pythonによる機械学習入門/株式会社システム計画研究所](https://www.amazon.co.jp/Python%E3%81%AB%E3%82%88%E3%82%8B%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E5%85%A5%E9%96%80-%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BE%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E8%A8%88%E7%94%BB%E7%A0%94%E7%A9%B6%E6%89%80/dp/4274219631)