こんにちは!BFT名古屋支店の猫です。梅雨が明けたら一気に暑くなって溶けてしまいました。
今日はラズパイで物体検出をしてみた話です! ながいよ!
はじめに
ラズパイで物体検出したいな~と思ってググった(死語?)ところ、物体検出の手法にはR-CNN、YOLO、SSDの3つがよく使われていることが分かりました。
私は機械学習ド素人のため、3つのうちで「やってみた」系の記事が多く見つかった SSD ( Single Shot MultiBox Detector ) の学習済みモデルを使ってまずは物体検出を体験してみることに。
今回はその環境構築の手順と検証結果をご紹介したいと思います。
SSDがどのようなアルゴリズムなのかについては、猫には説明が難しいため分かりやすく説明してくださっているサイトを参考に貼っておきます。
ソースは rykov8 さんが公開されている ssd_keras を使用しました。
github.com
使用環境
Raspberry Pi
- Raspberry Pi 4 ModelB (8GB)
- Raspberry Pi OS 10 (buster)
- kernel 5.10.17-v7l+
$ uname -a Linux raspberrypi 5.10.17-v7l+ #1421 SMP Thu May 27 14:00:13 BST 2021 armv7l GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Raspbian Description: Raspbian GNU/Linux 10 (buster) Release: 10 Codename: buster
Webカメラ
パッケージ等
version | |
---|---|
Python | 3.7.3 |
pip | 21.1.3 |
tensorflow | 1.15.0 |
Keras | 1.2.2 |
numpy | 1.21.1 |
matplotlib | 3.4.2 |
scipy | 1.1.0 |
opencv-python | 4.5.3.56 |
前提
- Raspberry Pi にOSがインストールされていること
- Raspberry Pi がインターネットに接続していること
- 実行ユーザがsudo権限を持っていること
環境構築~物体検出実行手順
1.環境構築
まずはTensorflow+Kerasの環境を構築していきます。
ssd_kerasはtensorflow1系でしか動かないため、tensorflow1.15.0とそれに合わせたパッケージをインストールしました。
(tensorflow2系で動くように修正した!という方を真似してやってみたのですが上手くいかず断念しました)
また、pipでtensorflowをインストールしようとするとtensorflow1.14.0までしかインストールできなかったので、tensorflow1.15.0をインストールするためにPINT0309 さんのビルド済パッケージを使用しました。 手順1-2,1-3は下記サイトのUsageを参考にしています。
1-1.パッケージ一覧・パッケージアップデート
$ sudo apt update $ sudo apt upgrade
1-2.Tensorflow前提パッケージインストール&余分なパッケージをアンインストール
Usageの手順のままだと手順1-3.で
ERROR: Cannot uninstall 'wrapt'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.
というエラーが出るため、wraptのアンインストールを追加しています。
Kerasやmatplotlibのインストールは一気にやるより一つずつ実行する方が成功率高めでした。
$ sudo apt install python3-dev python3-pip python3-venv $ sudo apt-get install -y libhdf5-dev libc-ares-dev libeigen3-dev gcc gfortran libgfortran5 \ libatlas3-base libatlas-base-dev libopenblas-dev libopenblas-base libblas-dev \ liblapack-dev cython3 openmpi-bin libopenmpi-dev libatlas-base-dev python3-dev $ sudo apt-get install python3-scipy $ sudo apt-get purge python3-wrapt $ sudo apt autoremove $ sudo pip3 install pip --upgrade $ sudo pip3 install keras_applications==1.0.8 --no-deps $ sudo pip3 install keras_preprocessing==1.1.0 --no-deps $ sudo pip3 install h5py==2.9.0 $ sudo pip3 install pybind11 $ sudo pip3 install -U --user six wheel mock
1-3.Tensorflowインストール
gitからファイルをダウンロードするのでお好みでディレクトリを作成するなどしてください。
実行の際は$ wget ~
の対象ファイル名をご自身の環境に合わせて書き換えてください。
$ mkdir /tmp/tensorflow1.15 #お好みで $ cd /tmp/tensorflow1.15 #お好みで $ sudo pip3 uninstall tensorflow $ wget "https://raw.githubusercontent.com/PINTO0309/Tensorflow-bin/master/tensorflow-1.15.0-cp37-cp37m-linux_armv7l_download.sh" $ sudo chmod +x tensorflow-1.15.0-cp37-cp37m-linux_armv7l_download.sh $ ./tensorflow-1.15.0-cp37-cp37m-linux_armv7l_download.sh $ sudo pip3 install tensorflow-1.15.0-cp37-cp37m-linux_armv7l.whl
1-4.その他SSD_Kerasに必要なパッケージインストール
$ sudo pip3 install -U Keras==1.2.2 $ sudo pip3 install -U opencv-python $ sudo pip3 install -U matplotlib $ sudo pip3 install -U numpy
2.SSD_Kerasの準備
続いてSSD_Kerasの実行に必要なソースコードたちを配置していきます。
ソースコードや重みファイルの配置はラズパイ上で直接ダウンロードしても、ご自身のPCでダウンロードしたものをラズパイに配置しても大丈夫です。お好みの方法で配置してください。
今回はラズパイ上でダウンロードする手順を記載しています。
2-1.ソースコードダウンロード
gitからファイルをダウンロードするのでお好みでworkディレクトリを作成するなどしてください。
$ sudo apt-get install git $ mkdir /path/to/work/directory #お好みで $ cd /path/to/work/directory #お好みで $ git clone https://github.com/rykov8/ssd_keras.git
gitコマンドを実行したディレクトリ下にssd_kerasディレクトリが作成されていることを確認します。
$ ls -la ./ssd_keras/ total 207084 drwxrwxrwx 1 user_name user_name 512 Jul 6 11:44 . drwxrwxrwx 1 user_name user_name 512 Jul 17 11:31 .. drwxrwxrwx 1 user_name user_name 512 Jun 23 09:27 .git -rwxrwxrwx 1 user_name user_name 1070 Jun 23 09:27 .gitignore drwxrwxrwx 1 user_name user_name 512 Jun 23 13:48 .ipynb_checkpoints -rwxrwxrwx 1 user_name user_name 6 Jul 2 14:00 .python-version ・ ・ ・
2-2.重みファイルダウンロード
ブラウザでこちらのサイトにアクセスし、[ here ]をクリックします (画像1)。
リンク先のページで[ weights_SSD300.hdf5 ]を選択し、[ … ]>[ ダウンロード ]>[ 標準ダウンロード ]をクリックします (画像2)。(結構大きいファイルなので、お好みでZIPダウンロードを選択してください。その場合は解凍してから使用してください。)
ダウンロードした重みファイルをssd_kerasディレクトリ下に配置します。
2-3.実行プログラムの作成
SSD_Kerasには物体検出お試し用の「SSD.ipynb」というソースコードが用意されています。
「.ipynb」はJupyterというツールで実行する際のファイル形式ですが、今回はJupyterを使用しないため、こちらのコード (SSD.ipynb) をmain.py (任意の名前のpyファイル) にコピー&ペーストし、ssd_kerasディレクトリ下に配置します。
その際%matplotlib inline
と%%time
はJupyter特有の書き方ですのでコメントアウトします。
また、今回は最終行の標準出力させる処理をファイル出力に変更しました。
- plt.show() + fname = 'recognized_' + str(i) + '.jpg' + plt.savefig(fname)
3.物体検出実行
さあ、いよいよ実行するときがやってきました! ssd_kerasディレクトリで以下のコマンドを実行すると……
$ python3 main.py
おお~!!
おお~??
画像4のPersonが謎ですが、一応動いているようです!
Webカメラで撮影した画像で検出してみた
ついでにWebカメラで撮影した画像でも物体検出を行ってみました!
座面と足しか見えていなくても椅子だと検出できました!すごいすごい!
画角は何?って感じですが、遠くのモニターもちゃんと検出してます!すごい!あんたえらいよ~!
~ そしてその日の夜 ~
何?!誰もいないはずなのに人が検出されてる!!こわ!!
おわりに
環境構築は苦労しましたが、オープンソースだけでそれなりの精度の物体検出をすることができました!(歓喜)
最後の暗闇Human検出は暗いと精度が下がってしまうということなんでしょうか……? 引き続き調査・検証していきたいと思います。
今回の 遊び 検証で使用したSSD+Kerasによる物体検出は、現在BFT名古屋支店で作っている「混雑状況可視化サービス」で使用する予定です。
混雑状況可視化サービスで使用する他の技術については山口さんが記事にしてくださっているので、そちらもぜひご覧ください!
最後まで読んでくださりありがとうございました!
「混雑状況可視化サービス」の関連記事
bftnagoya.hateblo.jp bftnagoya.hateblo.jp
参考
avinton.com techblog.cccmk.co.jp qiita.com
メインプログラム
以下 main.py の全文です。
import cv2 import keras from keras.applications.imagenet_utils import preprocess_input from keras.backend.tensorflow_backend import set_session from keras.models import Model from keras.preprocessing import image import matplotlib.pyplot as plt import numpy as np from scipy.misc import imread import tensorflow as tf from ssd import SSD300 from ssd_utils import BBoxUtility #%matplotlib inline plt.rcParams['figure.figsize'] = (8, 8) plt.rcParams['image.interpolation'] = 'nearest' np.set_printoptions(suppress=True) config = tf.ConfigProto() config.gpu_options.per_process_gpu_memory_fraction = 0.45 set_session(tf.Session(config=config)) voc_classes = ['Aeroplane', 'Bicycle', 'Bird', 'Boat', 'Bottle', 'Bus', 'Car', 'Cat', 'Chair', 'Cow', 'Diningtable', 'Dog', 'Horse','Motorbike', 'Person', 'Pottedplant', 'Sheep', 'Sofa', 'Train', 'Tvmonitor'] NUM_CLASSES = len(voc_classes) + 1 input_shape=(300, 300, 3) model = SSD300(input_shape, num_classes=NUM_CLASSES) model.load_weights('weights_SSD300.hdf5', by_name=True) bbox_util = BBoxUtility(NUM_CLASSES) inputs = [] images = [] img_path = './pics/fish-bike.jpg' img = image.load_img(img_path, target_size=(300, 300)) img = image.img_to_array(img) images.append(imread(img_path)) inputs.append(img.copy()) img_path = './pics/cat.jpg' img = image.load_img(img_path, target_size=(300, 300)) img = image.img_to_array(img) images.append(imread(img_path)) inputs.append(img.copy()) img_path = './pics/boys.jpg' img = image.load_img(img_path, target_size=(300, 300)) img = image.img_to_array(img) images.append(imread(img_path)) inputs.append(img.copy()) img_path = './pics/car_cat.jpg' img = image.load_img(img_path, target_size=(300, 300)) img = image.img_to_array(img) images.append(imread(img_path)) inputs.append(img.copy()) img_path = './pics/car_cat2.jpg' img = image.load_img(img_path, target_size=(300, 300)) img = image.img_to_array(img) images.append(imread(img_path)) inputs.append(img.copy()) inputs = preprocess_input(np.array(inputs)) preds = model.predict(inputs, batch_size=1, verbose=1) results = bbox_util.detection_out(preds) #%%time a = model.predict(inputs, batch_size=1) b = bbox_util.detection_out(preds) for i, img in enumerate(images): # Parse the outputs. det_label = results[i][:, 0] det_conf = results[i][:, 1] det_xmin = results[i][:, 2] det_ymin = results[i][:, 3] det_xmax = results[i][:, 4] det_ymax = results[i][:, 5] # Get detections with confidence higher than 0.6. top_indices = [i for i, conf in enumerate(det_conf) if conf >= 0.6] top_conf = det_conf[top_indices] top_label_indices = det_label[top_indices].tolist() top_xmin = det_xmin[top_indices] top_ymin = det_ymin[top_indices] top_xmax = det_xmax[top_indices] top_ymax = det_ymax[top_indices] colors = plt.cm.hsv(np.linspace(0, 1, 21)).tolist() plt.imshow(img / 255.) currentAxis = plt.gca() for i in range(top_conf.shape[0]): xmin = int(round(top_xmin[i] * img.shape[1])) ymin = int(round(top_ymin[i] * img.shape[0])) xmax = int(round(top_xmax[i] * img.shape[1])) ymax = int(round(top_ymax[i] * img.shape[0])) score = top_conf[i] label = int(top_label_indices[i]) label_name = voc_classes[label - 1] display_txt = '{:0.2f}, {}'.format(score, label_name) coords = (xmin, ymin), xmax-xmin+1, ymax-ymin+1 color = colors[label] currentAxis.add_patch(plt.Rectangle(*coords, fill=False, edgecolor=color, linewidth=2)) currentAxis.text(xmin, ymin, display_txt, bbox={'facecolor':color, 'alpha':0.5}) #plt.show() fname = 'recognized' + str(i) + '.jpg' plt.savefig(fname)