この記事は、IoTLT Advent Calendar 2016 の10日目の記事です。

IoTLT Advent Calendar 2016
http://qiita.com/advent-calendar/2016/iotlt

今年(2016年)の8/12に開催されたIoTLTでLTした、
「TensorFlowで遊びながら学んだ、はじめてのLED点滅」
の準備をする中で得た知識や参考にしたサイトを、この記事で整理しておこうと思います。

LTを準備する中で、
TensorFlow、OpenCVとArduinoを扱ったのですが、
私自身、3つともはじめて扱ったこともありいろいろと勉強になりました。

IoT縛りの勉強会! IoTLT vol.18 @ Amazon
https://iotlt.connpass.com/event/35436/

TensorFlowで遊びながら学んだ、はじめてのLED点滅

LT発表資料

SlideShareにLT資料をアップしています。
# 「アイドルマスタープラチナスターズ」から
# キャプチャした画像を含むスライドは省略しています。

システムの概要

システムの概要はこんな感じです。

iotlt20160812_1

Python及びJupyterNotebookの環境構築

まずはじめに、Pythonの環境を構築します。
以下を参考にして、anyenv, pyenv, pyenv-virtualenv をインストールします。

pythonの開発環境の切り替え用に anyenv, pyenv, pyenv-virtualenv, anacondaを入れる流れ
http://takemikami.com/2016/10/20/python-anyenv-pyenv-pyenvvirtualenv-anaconda.html

以下のように、anacondaをインストールし切り替えます。

$ mkdir ~/iotlt20160812 && cd $_
$ pyenv install anaconda3-4.2.0
$ pyenv local anaconda3-4.2.0

opencv, tensorflow用に環境を作成し、切り替えます。

$ conda create -n iotlt20160812 python=3.5
$ pyenv activate iotlt20160812

jupyterをインストールし、起動します。

$ conda install jupyter
$ jupyter notebook

ブラウザが起動して以下のような画面が表示されます。

iotlt20160812_2

OpenCVでの顔認識

環境構築

以下のように、homebrewでopencvをインストールします。

$ brew install homebrew/science/opencv

anaconda環境にOpenCV, matplotlibをインストールします。

$ conda install -c https://conda.anaconda.org/menpo opencv3
$ pip install matplotlib

顔認識の実行

以下のようなコードでjpgファイルをjupyter notebook上にinline表示することが出来ます。
画像は適当なjpegファイルを用意してください。

# アイドルマスター プラチナスターズの画像を使いたい場合は、
# PS4のシェア機能を使ってTwitterなどに上げれば良いと思います。

import cv2, matplotlib
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

img = cv2.imread('※ここにファイル名※.jpg') # ゆきほ エクストリームバースト画像読込
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # convert image to RGB color for matplotlib
plt.imshow(img) # show image with matplotlib

参考: 画像処理入門講座 : OpenCVとPythonで始める画像処理 | プログラミング | POSTD
http://postd.cc/image-processing-101/

次に、lbpcascade_animeface.xmlを使って、顔認識をします。
以下のようなコードになります。
下記の参考サイトから、lbpcascade_animeface.xmlをダウンロードしておいてください。

# アニメ顔認識器読み込み
cascade_path = "./lbpcascade_animeface.xml"
cascade = cv2.CascadeClassifier(cascade_path)

gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) # convert image to grayscale
cv2.equalizeHist(gray_img, gray_img) # ヒストグラムの平坦化
facerect = cascade.detectMultiScale(gray_img, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1)) # 顔認識

color = (255, 255, 255)
image = img

#検出した顔を囲む矩形の作成
if len(facerect) > 0:
    for rect in facerect:
        cv2.rectangle(image, tuple(rect[0:2]),tuple(rect[0:2]+rect[2:4]), color, thickness=2)

plt.imshow(image)

参考: OpenCVによるアニメ顔検出ならlbpcascade_animeface.xml | デー
http://ultraist.hatenablog.com/entry/20110718/1310965532

TensorFlowでの分類

環境構築

anaconda環境にTensorFlowをインストールします。

$ conda install -c conda-forge tensorflow

教師データの切り出し

教師データは、YouTubeで公開されている以下のキャラクターPVから切り出しました。

PS4「アイドルマスター プラチナスターズ」キャラクターPV ~萩原雪歩~ | YouTube
https://www.youtube.com/watch?v=o7WCt196x6Y

PS4「アイドルマスター プラチナスターズ」キャラクターPV ~星井美希~ | YouTube
https://www.youtube.com/watch?v=i-jjWfJytso

PS4「アイドルマスター プラチナスターズ」キャラクターPV ~高槻やよい~ | YouTube
https://www.youtube.com/watch?v=v0jUg6r2_u0

以下のように、FRAMEを少しずつ動かしてイメージを読み込み、
顔認識してその部分を教師データとして切り出していきます。

import cv2, matplotlib
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

cap = cv2.VideoCapture("※動画ファイル名※.mp4")

max_frame = cap.get(cv2.CAP_PROP_FRAME_COUNT);
print(max_frame)

image_idx = 0

for i in range(110):
    print(i)
    cap.set(cv2.CAP_PROP_POS_FRAMES,10*(i+1));
    ret, img = cap.read()

    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # convert image to grayscale

    ※以降は顔認識のコードと同様※

学習と予測の実行

学習と予測は、以下の参考ページをそのまま持ってきて実行したので、
参考サイトを上げるだけにしておきます。

参考: TensorFlowでアニメゆるゆりの制作会社を識別する | kivantium活動日記 http://kivantium.hateblo.jp/entry/2015/11/18/233834

畳み込みニューラルネットについては、以下の記事がわかりやすいので参考にして下さい。

参考: 畳み込みニューラルネットワークの仕組み | コンピュータサイエンス | POSTD
http://postd.cc/how-do-convolutional-neural-networks-work/?1479446220158=1

ArduinoでのLED点滅

環境構築

anaconda環境に、pyserialをインストールします。

$ pip install pyserial

LED点滅用の信号送出の実行

以下のように、pyserialでusbポートにつないだarduionoに0または1の信号を送出し、
LEDのOn/Offを切り替えます(Arduiono側のコードは省略しますが)。

# 0を送出
import serial
ser = serial.Serial(port='/dev/cu.usbmodem141121', baudrate=9600)

output_bytes = b'0' # 1を送出する場合は、ここを1に

if ser.isOpen():
    ser.write(output_bytes)
    print("send: " + str(output_bytes))

肝心の学習と予測の部分が参考サイト上げるだけになってしまいましたが、
この記事で全体の流れの雰囲気(意外と簡単に出来るということ)は
わかっていただけるのかなと思います。

皆さんもこんな感じでカジュアルに遊んでみてください。