昨年友人より, 某コワーキングスペースの混雑度を見える化したいので, 機械学習部分を手伝ってほしいとの依頼があった.
そこで, 混雑度を測るために, 各スペースの人物検出を行い, 定員に対してどの程度の人がいるか検知することにした.
関連記事:
・コワーキングスペースの「混雑度」を検出する(1)
・コワーキングスペースの「混雑度」を検出する(2)
・コワーキングスペースの「混雑度」を検出する(3)
・コワーキングスペースの「混雑度」を検出する(4)
1. はじめに
コワーキングスペースは, 現状オープンスペースの場所と, 個別に区切られた場所などがある.
区切られた方は, カメラ位置変更しないと, 人物検出は難しそう!!
人物検出を行うにあたり, 以前の案件[1]ではYOLOv3[2]を使って行ったが, 今回はSSD(Single Shot MultiBox Detector)[3]を使ってみることにした.
KerasによるSSDの実装はいくつかあり, 以下あたりが有名である.
(1) rykov8/ssd_keras[4] : Keras v1.2.2, Tensorflow v1.0.0
(2) pierluigiferrari/ssd_keras[5] : Keras v2.x, Tensorflow v1.x
現状のKeras, Tensorflowのバージョンを考慮し, (2)の実装を使うこととした.
それにしても, 最近は論文のアルゴリズムを誰かが実装してgithubなどで公開してくれるので, 最新のアルゴリズムを使って何かをやろうという側にとってはなんとも便利な時代になったものだ...
2. データセット作成
学習データには, 上記スペースに設置したカメラで一定期間収集した画像を利用することにする.
とりあえず, 実験用に3週間分ほどのデータを提供いただいたので, アノテーションすることにした.
作業にあたり, 今回はアノテーションツールにLabelImg[6]を使用してみた.
[手順]
(1) 動画から一定時間毎にスナップショットを作成する.
(2) 同じような画像は除去する.
(3) アノテーションツールを使って, 人物領域とそのラベルを生成する.
(※ 人物は椅子に座って作業している場合が多いので, 今回は上半身あたりを領域として指定するようにした.)
[アノテーションデータの例]
<annotation>train_images</folder> <filename>Schedule-2020-12-YYYY.jpg</filename> <path>/home/aska/Project/XXX/train_images/Schedule-2020-12-YYYY.jpg</path> <source> <database>Unknown</database> </source> <size> <width>640</width> <height>480</height> <depth>3</depth> </size> <segmented>0</segmented> <object> <name>person</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>503</xmin> <ymin>159</ymin> <xmax>593</xmax> <ymax>261</ymax> </bndbox> </object> <object> <name>person</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>399</xmin> <ymin>3</ymin> <xmax>443</xmax> <ymax>55</ymax> </bndbox> </object> </annotation>
一応, 学習データ336枚(837領域), 評価データ67枚(178領域)を準備した.
3. モデル学習
モデルの学習には, ssd_keras/ssd300_training.ipynbを少し修正して使用した.
主な変更箇所:
- クラス情報(クラス数, ラベル)
- train, valの画像のディレクトリ
- train, valのアノテーションデータのディレクトリ
- train, valのファイルリスト
- epoch数やsteps per epoch数
主なライブラリのバージョン:
- Keras 2.2.2
- Tensorflow 1.10.1
4. 人物検出
学習済モデルを使って, 人物検出をやってみた.
[コード]
import numpy as np import os import sys import cv2 import argparse from imageio import imread sys.path.append('./ssd_keras') from models.keras_ssd300 import ssd_300 from keras_loss_function.keras_ssd_loss import SSDLoss from keras.optimizers import Adam from keras.preprocessing import image MODEL_DIR = './trained_models' MODEL_NAME = 'ssd300_person_epoch-10_loss-2.7150_val_loss-2.9814.h5' classes = ['background', 'person'] CONFIDENCE_THRESHOLD = 0.50 # Set the image size. IMG_HEIGHT = 300 IMG_WIDTH = 300 def load_model(): global model model = ssd_300(image_size=(IMG_HEIGHT, IMG_WIDTH, 3), n_classes=1, mode='inference', l2_regularization=0.0005, scales=[0.1, 0.2, 0.37, 0.54, 0.71, 0.88, 1.05], aspect_ratios_per_layer=[[1.0, 2.0, 0.5], [1.0, 2.0, 0.5, 3.0, 1.0/3.0], [1.0, 2.0, 0.5, 3.0, 1.0/3.0], [1.0, 2.0, 0.5, 3.0, 1.0/3.0], [1.0, 2.0, 0.5], [1.0, 2.0, 0.5]], two_boxes_for_ar1=True, steps=[8, 16, 32, 64, 100, 300], offsets=[0.5, 0.5, 0.5, 0.5, 0.5, 0.5], clip_boxes=False, variances=[0.1, 0.1, 0.2, 0.2], normalize_coords=True, subtract_mean=[123, 117, 104], swap_channels=[2, 1, 0], confidence_thresh=0.5, iou_threshold=0.45, top_k=200, nms_max_output_size=400) # load weight print(model.summary()) weights_path = os.path.join(MODEL_DIR, MODEL_NAME) model.load_weights(weights_path, by_name=True) adam = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0) ssd_loss = SSDLoss(neg_pos_ratio=3, alpha=1.0) model.compile(optimizer=adam, loss=ssd_loss.compute_loss) print('Model loading completed') # 最終結果をJSON形式で格納 def set_results(y_pred, fname): res = [y_pred[k][y_pred[k,:,1] > CONFIDENCE_THRESHOLD] for k in range(y_pred.shape[0])] img = cv2.imread(fname) data = [] for r in res[0]: left = int(r[2] * img.shape[1] / IMG_WIDTH) top = int(r[3] * img.shape[0] / IMG_HEIGHT) right = int(r[4] * img.shape[1] / IMG_WIDTH) bottom = int(r[5] * img.shape[0] / IMG_HEIGHT) bbox = {'left' : str(left), 'bottom' : str(bottom), 'right' : str(right), 'top': str(top)} data.append({'label': classes[int(r[0])], 'score': str(r[1]), 'bbox': bbox}) cv2.rectangle(img, (int(left), int(top)), (int(right), int(bottom)), (0, 0, 255)) cv2.imshow('img', img) return data def main(args): input_images = [] # Store resized versions of the images here. img = image.load_img(args.input, target_size=(IMG_HEIGHT, IMG_WIDTH)) img = image.img_to_array(img) input_images.append(img) input_images = np.array(input_images) # SSD処理 y_pred = model.predict(input_images) res = set_results(y_pred, args.input) print(res) cv2.waitKey(0) cv2.destroyAllWindows() if __name__ == '__main__': parser = argparse.ArgumentParser() help_ = 'Image file' parser.add_argument('-i', '--input', help=help_) args = parser.parse_args() load_model() main(args)
[実行例]
人物検出が一応動作するようになった.
コワーキングスペースの混雑度は, 5分や10分毎でよいのでクラウド上での動作でよいかと思うが, AI展示的なこともやりたいということで, 最終的にはNvidiaのJetson Nanoで動かすことになっている.
----
参照URL:
[1] ピープルカウンタを考えてみる(1)~(8)
[2] YOLO: Real-Time Object Detection
[3] SSD: Single Shot MultiBox Detector
[4] rykov8/ssd_keras: Port of Single Shot MultiBox Detector to Keras
[5] pierluigiferrari/ssd_keras: A Keras port of Single Shot MultiBox ...
[6] tzutalin/labelImg: LabelImg is a graphical image annotation tool ann...
TensorFlow2 TensorFlow & Keras対応 プログラミング実装ハンドブック
|
|
詳解ディープラーニング 第2版 ~TensorFlow/Keras・PyTorchによる時系列データ処理~ (Compass Booksシリーズ)
|