このハンズオンでは、Raspberry Piとカメラを使ったAIによるモノの判別を体験します。また、開発にはisaaxを用います。 ハンズオン参加者は席についた方から事前準備の節に進み、Raspberry Piへの電源投入とIPアドレスの確認まで終わらせてください。

事前準備

ハンズオンを進めていくにあたって、はじめに必要なセットアップを行いましょう。

必要なモノ

  • Raspberry Pi 3B/3B+
  • microSDカード
  • Picameraモジュール

isaax勉強会に参加している場合は、TensorFlowやOpenCVのインストール時間を節約するためにあらかじめセットアップが完了したSDカードをお配りしていますので、そちらを使用してください。リモート枠で参加する場合は下記リンクのREADME.mdに記載のライブラリインストールをはじめに行なってください。

必要なアカウント

isaaxを使う際にGitHubのアカウントが必要となります。アカウントを持っていない方はこのタイミングで登録しておきましょう。

以下のアカウントはハンズオンの中で登録方法を説明しています。

ハードウェアのセットアップ

Raspberry Piにカメラ、SDカードを取り付けます。ハンズオンに参加している場合は、有線LANを接続してください。リモート参加の場合はネットワーク接続さえあればWiFi、有線どちらでも構いません。

カメラを取り付ける際には向きに注意してください。下図に例を示します。

picamera direction

最後にmicroUSBポートから電源を投入します。IoTっぽく、ヘッドレス(キーボード、マウス、ディスプレイを繋がない)で進めていきます。

headless rpi

IPアドレスを調べる

ハンズオンに参加している方は、Raspberry Piに電源をいれたらisaaxのスタッフをお呼びください。スタッフがIPアドレスをしらべますので、後ほどSSH接続するためにメモしておいてください。

リモート枠で参加されている方は、下記のリンクなどを参考に、Raspberry PiのIPアドレスを調べておきましょう。すでに把握している場合は次の節に進んでください。

ハンズオンの流れ

ハンズオンの流れを説明します。

はじめに、ブラウザを通してカメラの映像を確認できるサンプルコードをisaaxを使ってRaspberry Piにインストールします。

ちょっとした修正を加え、isaaxを使ったデバイスの更新方法を確認します。

次にそのサンプルコードに変更を加え、物体の認識とその結果をフレームに描画する機能を追加します。

最後に、時間の余った方は課題に取り組んでみましょう。

サンプルプログラムを動かしてみよう

さっそくサンプルプログラムを動かしてみましょう。大まかな手順は、以下のとおりです。

  1. GitHubのリポジトリをフォークする
  2. isaaxプロジェクトの作成
  3. isaaxdをRaspberry Piにインストール

isaaxdをRaspberry Piにインストールすると、自動的にアプリケーションが起動し、isaaxのダッシュボード上で動作確認ができます(GitHub上のサンプルコードがあらかじめisaaxで動作するようにセットアップされています)。

サンプルコードのフォーク

isaaxでは、Raspberry Piに配信するプログラムをGitリポジトリ(以下、リポジトリ)として管理します。今回はあらかじめ用意したサンプルコードをフォークして使います。

上記リンクを開き、右上の「Fork」ボタンをクリックして自分のGitHubアカウントにリポジトリをフォークしましょう。

isaaxプロジェクトの作成

前節で自分のGitHubアカウントにフォークしたリポジトリを使い、isaaxにプロジェクトを作成します。isaaxアカウントをまだ作成していない場合はこのタイミングで登録しましょう(GitHubアカウントを使って登録すると便利です)。

project-creation

 プロジェクト追加ボタンをクリックすると、プロジェクトの詳細情報を入力する画面が表示されます。リポジトリ名のyutashi/study-picamera-examplesは、あなたの名前/handson-object-detectionに書き換えてください。

プラットフォームはGitHubを選択し、上図のように設定し保存ボタンをクリックします。

プロジェクトトークン

作成したプロジェクトにデバイスを登録します。ここでいうデバイスとはRaspberry Piのことを指します。Raspberry Pi側にisaaxdというエージェントをインストールすことで、デバイス登録が完了します。

isaaxdはRaspberry Piなどのデバイス上で動作するソフトウェアです。isaaxクラウドとの通信とユーザーアプリケーションの管理を担っています。 isaaxdとはより引用

project token

プロジェクトトークンはデバイスの認証を行うのに必要となります。インストールスクリプトはそのトークンを引数としてisaaxdのインストールをワンコマンド実行するためのスクリプトです。下側のインストールスクリプトの文字列をコピーしてください。

コピーしたコマンドは、Raspberry Pi上で実行します。SSHをしての作業に慣れている方は、普段お使いのターミナルソフトウェアを使ってRaspberry Piにログインしてください(次節は読み飛ばしていただいて構いません)。

ユーザー名はpi、初期パスワードはraspberryです。

SSHログインいついて

ここではSSHログインに慣れていない方向けにSecure Shell Appについて紹介します。Chromeブラウザを開き、下記リンクからSecure Shell Appを追加してください。

Chromeに追加するとアプリの一覧ページが表示されるのでSecure Shell Appのアイコンをクリックします。

ssh config

 ログイン情報を設定する画面に遷移するので上図を参考に入力し、右下の接続ボタンを押下します。接続に成功すると、「Are you sure you want to continue connecting (yes/no)?」と 聞かれますので「yes」を入力し、最後にRaspberry Piのパスワードを入力して接続完了です。

isaaxdのインストール

それでは、先ほどコピーしたコマンドをRaspberry Piのターミナル内で貼り付け、実行しましょう。

install isaaxd

上図のようにisaaxd installation completeが表示されればインストール成功です。

先ほどフォークしたリポジトリのプログラムがRaspberry Piに配信され、自動的にアプリケーションを起動します。

動作の確認

ブラウザからRaspberry PiのIPアドレス:5000にアクセスするとPicameraの映像が確認できます。本来はより高い解像度、フレームレートで表示できますが、ネットワークの帯域を節約するためにフレームレートを5FPSまで落としています。

demo

サンプルは、MotionJPGでWebストリーミングするプログラムです。後ほどサンプルをベースに認識した物体の描画機能などを追加していきます。

サンプルコードが出力するログ情報はisaaxのダッシュボード上で確認することができます。右上にある、オレンジのバッジがついたアイコンをクリックしてみましょう。

ここでは、デバイスから上がってくるすべてのログを確認できます。デバイス名のリンクをクリックすることで、そのデバイスの詳細情報が確認できます。

isaaxを使ったプログラムの更新

デバイスアプリケーションの更新方法について確認するために、簡単な変更をサンプルコードに加えてみましょう。isaaxではプロジェクトに設定されたGitHubリポジトリが更新されると、自動的にデバイスにその変更が配信される仕組みとなっています。

Gitを使った開発に慣れている方はフォークしたリポジトリをローカルPCにクローンし、「フレームレートの変更」の節を参考にサンプルコードに変更を加えてGitHubに反映してみましょう。

GitHubをあまり使ったことがない方や、Gitpodを試してみたい方は次節をお読みください。

GitHubを使った開発

GitHubのようなGitホスティングサービスを使う場合、リポジトリに変更を加える方法は様々です。

  1. ローカルPCにクローンして開発・反映する
  2. GitHubのWEB画面上で直接編集する
  3. GitHubと連携したサービスを利用する

基本的には1の作業で開発を行います。2の方法はとても簡単ですが、ちょっとした変更を加える場合以外ではあまり使わない方が良いでしょう。

1の方法ではPCに環境をセットアップする必要があり、初めてGitを使って開発を行う場合は少し手間が必要となります。そこでここでは、3の方法に該当するGitpodを使うことで、ブラウザ上ですぐに開発を始める方法について紹介します。

Gitpodは公開リポジトリに対して、月100時間まで無料で使用できます。

まずは下記リンクからGitpodのWEBページを開きましょう。

トップページ右上の「Go to app」をクリックし、GitHubアカウントとGitpodを連携します。

 「Authorize gitpot-io」をクリック後、次の画面でterms of serviceにチェックを入れアカウントを作成しましょう。

ダッシュボードにログインできたら、後ほどGitHubリポジトリに変更を反映できるように、アクセス権限を変更します。右上のアイコンボタンをクリックし、ドロップダウンから「Access Control」を選択してください。

今回はプライベートリポジトリへのアクセスは必要ないため、「write public repos」のみにチェックを入れ、「Update」ボタンを押下します。すると再度GitHubへのアクセス権限の確認が表示されるので「Authorize gitpod-io」をクリックして権限の編集を完了します。

これでIDEを立ち上げる準備が整いました。

IDEを立ち上げるには、ブラウザのURLバーにhttps://gitpod.io/#{GitHubリポジトリへのURL}を入力します。例えば、筆者がフォークしたリポジトリのURLはhttps://github.com/yutashi/handson-object-detectionですので、下記のようになります。

  • https://gitpod.io/#https://github.com/yutashi/handson-object-detection

yutashiの部分をお使いのGitHubユーザー名に置き換えてください。下図のようにIDEが開かれれば成功です。見た目はVisual Studio Codeによく似ています。

この環境に次節以降の変更を加えていきましょう。

Gitpodを使い終わったあとは、メニューバーの「File」から「Stop Workspace」を選択してVMを停止しましょう。そのままにしておくと使用時間を使い切ってしまいます。

フレームレートの変更

簡単な変更として、フレームレートに変更を加えてみましょう。sample.pyの8行目で渡している引数framerateを1 ~ 10程度の範囲で書き換えてみましょう。

camera = PiVideoStream(resolution=(400, 304), framerate=1).start()

変更を終えたら、コミットを作成してGitHubに反映します。Gitpodを使っている場合はIDE下部に表示されているターミナルより作業を行なってください。

$ git add sample.py
$ git commit -m "Change frame rate"
$ git push origin master

デバイスの更新状況はisaaxのダッシュボードから確認できます。ログからRapsberry Piのサンプルアプリケーションが立ち上がっていることが確認できたら、Picamera映像のページをリロードして変更をチェックしてみましょう。

本来的には、このような小さな変更を確認するために毎回コミットを作成することはありませんが、isaaxを使えばデバイスが複数台になっても同じワークフローでデバイスの更新が可能となります。

モノの判別機能を実装しよう

サンプルアプリケーションに、物体認識の機能を追加していきます。なお、ここで実装するコードは下記の資料を参考に実装されています。

モデルデータのダウンロード

勉強会でお貸ししているSDカードを使用している場合は、この節はスキップしてください。

まずはじめに、使用する学習モデルをダウンロードします。Raspberry Pi上で下記のコマンドを実行してください。

$ sudo apt install git
$ cd ~/
$ mkdir models
$ git clone https://github.com/chuanqi305/MobileNet-SSD.git
$ cd MobileNet-SSD/
$ cp deploy.prototxt ~/models/MobileNetSSD_deploy.prototxt
$ cp mobilenet_iter_73000.caffemodel ~/models/MobileNetSSD_deploy.caffemodel

モジュールのインポート

はじめに、必要なモジュールを追加します。

import numpy as np

Numpyは科学技術計算用のライブラリです。今回は画像配列やモデルの出力結果を操作するために使います。

モデルの読み込み

モデルを読み込みます。~/models/以下に学習モデルを配置しているため、そのパスを指定します。

net = cv2.dnn.readNetFromCaffe('/home/pi/models/MobileNetSSD_deploy.prototxt',
        '/home/pi/models/MobileNetSSD_deploy.caffemodel')

1つめのテキストファイルはモデルの構造が記されています。例えば先頭に近い行には入力する際の行列の形状が記述されています。

name: "MobileNet-SSD"
input: "data"
input_shape {
  dim: 1
  dim: 3
  dim: 300
  dim: 300
}

2つ目のファイルMobileNetSSD_deploy.caffemodelは実際に学習した重みです。

入力画像の前処理

detect関数を定義し、モデルへの入力から出力結果の描画まで記述していきます。はじめにPicameraから読み込んだフレームをモデルに渡せるように前処理を行います。

def detect(frame):
    frame = cv2.resize(frame, (300, 300))
    blob = cv2.dnn.blobFromImage(
        image=frame,
        scalefactor=0.007843, 
        size=(300, 300),
        mean=127.5
    )

cv2.resizeを使用して、モデルが期待する画像サイズに変更します。次に、cv2.dnn.blobFromImageで画像から入力用の行列を作り出します。

print(blob.shape)でblob変数を出力すると、前節で確認した入力形状と同じことが確認できます。

(1, 3, 300, 300)

cv2.dnn.blobFromImageの引数で指定しているパラメータは、どのモデルでも常に指定が必要になるものではなく、またその値もモデルによって異なります。例えば今回の場合、学習時に使用した値を参照しています。

ちなみに、cv2.dnn.blobFromImageを使わずに処理する場合は、下記のようになります。

frame = frame - 127.5
fmrae = frame * 0.007843
frame = frame.astype(np.float32)
frame = frame.transpose((2, 0, 1))

データの入力と出力の受け取り

モデルにデータを入力するにはsetInput()、結果を受け取るにはforward()をそれぞれ使用します。

    net.setInput(blob)
    out = net.forward()

出力結果のパース

出力結果はnumpyの多次元配列ですが、モデルによってそれぞれのインデックスに入る値には意味があります。例えば下記のコードでは主に4次元目の配列を操作していますが、それぞれのインデックスには下記のような意味があります。

  • 0番目 … 画像id (常に0)
  • 1番目 … クラス
  • 2番目 … スコア (検出した物体の精度)
  • 3 ~ 6番目 … 検出した物体の画像内の座標
    boxes = out[0,0,:,3:7] * np.array([300, 300, 300, 300])
    classes = out[0,0,:,1]
    confidences = out[0,0,:,2]

クラスは検出可能な物体に割り当てられたIDです。例えば、今回学習に使われたデータセットにおいて、IDが15であった場合は人を検出していることを表します。クラスの一覧は下記のリンクをご確認ください。

VOC0712 ラベルマップ – GitHub

出力結果の描画

認識したボックスの数の分だけ、ループ処理を行います。はじめに検出精度をチェックし、20%に満たないものはスキップします。

    for i, box in enumerate(boxes):

        confidence = confidences[i]
        if confidence < 0.2:
            continue

次にクラスを調べます。ここでは15のみ、つまり人以外はスキップするように設定しています。

        idx = int(classes[i])
        if idx != 15:
            continue

これで検出精度が20%の人のみが残る状態となりました。下記のコードで認識した物体の座標を描画します。

        (startX, startY, endX, endY) = box.astype('int')
        cv2.rectangle(frame, (startX, startY), (endX, endY), (0, 255, 0), 2)

描画したボックスの左上に「Person 85%」のような表示になるようにOpenCVのputText関数を使って文字列の描画を行います。

        label = '{}: {:.2f}%'.format('Person', confidence * 100)
        y = startY - 15 if startY - 15 > 15 else startY + 15
        cv2.putText(frame, label, (startX, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)

    return frame

gen関数の修正

最後に、gen関数を修正して読み込んだフレームをdetect関数を通すように変更しましょう。

def gen(camera):
    while True:
        frame = camera.read()
        processed_frame = detect(frame.copy())
        ret, jpeg = cv2.imencode('.jpg', processed_frame)
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + jpeg.tobytes() + b'\r\n\r\n')

ここまで出来たらコミットを作成してGitHub/isaaxに反映し、動作を確認してください。ハンズオンの内容は以上となります。

サンプルコード 全体像 – GitHub

時間の余った方は…

課題. 1

サンプルコードでは人のみを検出するようにif文を設けましたが、モデルとしては21クラスを検出しています。コードに修正を加え、任意のクラスを表示するように変更を加えましょう。

事後学習のために

isaaxについてより深く知るためには、公式のドキュメントを読んだり、コミュニティで質問してみましょう。

無料の勉強会も開催しているので、ハンズオンしたい方はこちらにもご参加ください。

本格的にIoTの開発を学びたい場合は、有料の講座もご利用ください。

SNSでもIoTに関する情報を発信しています。フォローお願いします。


Leave a Reply

Your email address will not be published. Required fields are marked *