本記事で行うこと
前回の記事ではWindows10のPCにUbuntuの仮想マシンを構築し、Pythonが実行できる環境を構築しました。
今回は実際にUbuntu上で人検出を行ってみようと思います。
人検出について
実際に人検出を行う前に、人検出とは何なのか、どのように行っているかということを知っておく必要があるため、それについて解説していきたいと思います。
人検出とは?
人検出とは、物体検出の中でも特に、画像や動画の中にどこに人がいるかを機械学習や深層学習を用いて検出する技術です。
人検出では主に以下のことを行い最終的に人を検出しています。
予め人手などによってどこにその人がいるか分類された大量の画像を機械学習(深層学習)させ、機械的に人の特徴を抽出し、モデルを作成する。
モデルに対して人を検出したい画像を入力する。
モデルの中では入力された画像のどの部分が人であるかを1.で抽出した特徴をもとに人である領域を検出し、最終的にその結果を出力。
1.での機械学習によるモデルの作成では、ImageNetやCOCOなど既に分類されているデータセットを用いてモデルの作成が行われる場合が多いです。 その他具体的なデータセットについてはこちらなどがまとめられているので、参考までに。
今回は、GitHub上にある既に学習済みの物体検出モデルを用いて、人が含まれる画像を入力したときにどのように検出されるかを実際に行ってみます。
物体検知のアルゴリズム
人検出を含む物体検出では、SSDやYOLOなど様々なアルゴリズムが提案されています。
物体検出では、いかに正確に、そしてリアルタイム性を求めるのであればいかに速く検出できるかということが重要になってきますが、これらはトレードオフの関係な場合が多く(解決されているものもありますが)、どのアルゴリズムを用いるかは用途に合わせて考える必要があります。
主に物体検出ではR-CNN系、YOLO系、SSD系があり、それらについて簡単に解説したいと思います。
CNN(畳み込みニューラルネットワーク)
物体検出のアルゴリズムを解説する前にまず、そこで使われる深層学習、CNNについて解説したいと思います。
CNNとは画像処理で用いられる深層学習の手法の一つです。
名前にもあるとおり、畳み込みやプーリングという処理を行ったあとに、ニューラルネットワーク(以下NN)を用いて深層学習を行い、物体の特徴抽出や識別(分類)を行います。
畳み込みとは、図1のように画像に対して例えば5×5など一定サイズのフィルターと呼ばれる格子状の数値データ群を用意し、これと画像のRGBデータなどの数値データに対して掛け算を行い、それらの和を取ることで、画像の特定領域の特徴量をまとめ上げる手法のことです。これらを画像全体に対して計算することで、特徴マップと呼ばれる格子状の特徴量の集合を作成します。
さらに特徴量を圧縮するために、プーリングという処理を行います。プーリングは畳み込みで作成された特徴マップをさらに圧縮するために、特定領域の特徴となる、例えば平均値や最大値を抽出することで、特徴量の圧縮を行います。
これらの結果をNNにかけて、物体の特徴抽出、識別を行います。
R-CNN系
R-CNN系は比較的物体検出の中でも古くからあるアルゴリズムです。
簡単に図2のようにまず、入力画像に対して物体の候補を2000個程度抽出し、その中からどの候補領域がなんの物体に当たるかをCNNで特定するというアルゴリズムであり、亜種にはFaster RCNNやR-FCNなどがあります。
このアルゴリズムでは、比較的正確に人検出ができる一方で、候補領域を2000個用意し、それぞれに対してCNNにかけてそれぞれそれが何であるか割り出す必要があるため、非常に時間がかかるという難点があるため、特に動画での物体検出には不向きです。
SSD系
R-CNN系では検出に時間がかかるという問題点があり、その原因の一つとして、前述したとおり、候補領域の提案と分類タスクが分離しているため、アルゴリズムとして複雑化し、時間がかかるということでした。
このような問題に対して領域の提案から分類までをEnd-to-Endで行えるようにしたものがこのSSD系のアルゴリズムになります。
SSDでは、まず入力画像から特徴マップを作成し、特徴マップの各格子に対してそれぞれ事前に設定したアスペクト比、すなわち四角の縦横の比率とその倍率(大きさ)ごとにそれがどの物体であるか確率を求め、高いものをその物体として検出する手法をCNNを用いて行っています。
基本的にはNNにはVGG16というアルゴリズムを用いられますが、SSD系ではそれ以外のNNを用いたSSDや、DSSDなどのアルゴリズムもあります。
YOLO系
YOLO系もSSD系と同様に速いアルゴリズムになります。YOLOにはYOLOv1、YOLOv2、YOLOv3があり、特にYOLOv3は速いだけでなく、正確に検出できると言われています。
YOLOv3では、まず入力された画像をS×S(Sは任意)に分けます。そしてS×Sのそれぞれの領域において、物体の候補領域をいくつか保持します。その候補領域は中心座標と縦横幅、そしてその候補領域がどれくらい信頼できる候補領域なのか(候補領域の信頼度)という候補領域に関する情報とを保持します。加えて、グリッドごとにそのグリッドにはどの物体が存在するかの確率を保持します。
これらの情報を用いてまず、信頼度が高い領域だけを抽出します。そして抽出された領域と、グリッドごとのそのグリッドにどの物体が存在しているかの確率を照らし合わせることで、最終的に物体の領域とその物体の名称を導き出します。
各アルゴリズムの速度と精度について
人検出におけるスループット(速度)と精度に関しては、Kim, C.E., Oghaz, M.M., Fajtl, J., Argyriou, V., & Remagnino, P. (2018). A Comparison of Embedded Deep Learning Methods for Person Detection. CoRR.では以下のような結果も言われており、前述の通り、速度と精度はトレードオフであるということも伺えます。しかしながらこの論文ではYOLOv3やSSD(VGG-300)が速度も精度もどちらもある程度両立できている最良のモデルと言われています。
Ubuntuでの人検出
それでは実際にUbuntu上で人検出を行ってみます。今回は精度が良いと言われているYOLOv3をGitHub上にある学習済みモデルを用いて人検出を行います。
keras, tensorflowのインストール
まず、anacondaにkerasとtensorflowというライブラリをインストールします。インストールには端末を開いて以下のように入力します。
1 conda install keras 2 conda install tensorflow 3 conda install opencv-python
Githubからクローン
GitHubからクローン、すなわちYOLOv3のモデルをダウンロードします。まず、Ubuntuの端末を開いて、クローンしたいフォルダ(ディレクトリ)で以下のように入力します。
1 git clone https://github.com/qqwweee/keras-yolo3.git
そしてクローンしたディレクトリに移動し、yolov3のdarknetでの学習済みモデルをダウンロード、そしてそれをconvert.pyで変換します。
1 cd keras-yolo3 2 wget https://pjreddie.com/media/files/yolov3.weights 3 python convert.py yolov3.cfg yolov3.weights model_data/yolo.h5
実際にモデルを用いて人検出
そして実際に人検出を行います。人検出を行う前に以下のようにクローンしたディレクトリにあるyolo.pyの最後に以下のようにPythonのコードを追加します。
1 def detect_img(yolo): 2 while True: 3 img = input('Input image filename:') 4 try: 5 image = Image.open(img) 6 except: 7 print('Open Error! Try again!') 8 continue 9 else: 10 r_image = yolo.detect_image(image) 11 print(type(r_image)) 12 import cv2 13 cv2.imwrite("out.jpg", np.asarray(r_image)[..., ::-1]) 14 r_image.show() 15 yolo.close_session() 16 if __name__ == '__main__': 17 detect_img(YOLO())
そしてクローンしてきたディレクトリに入力画像を置き、以下のようにyolo.pyを実行します。
するとプログラムが実行されファイルの名前が聞かれるので、ファイルの名前を入力します。
1 python yolo.py 2 Input image filename: input.jpg
すると以下のように物体が検出された画像が表示され、out.jpgというファイル名で保存されます。
もし検出するのが人のみで十分であれば、yolo.pyのdetect_image関数内の「predicted_class」が予測される物体クラスであるため、detect_image関数内のfor分以下でpersonのみ処理するようにすれば可能です。
1 for i, c in reversed(list(enumerate(out_classes))): 2 predicted_class = self.class_names[c] 3 if predicted_class == 'person': 4 box = out_boxes[i] 5 score = out_scores[i] 6 7 label = '{} {:.2f}'.format(predicted_class, score) 8 draw = ImageDraw.Draw(image) 9 label_size = draw.textsize(label, font)
また、動画を検出したい場合は以下のようにクローンしたディレクトリ上で入力し、yolo_video.pyを実行します。また、動画は画像と異なり、各フレームごとに処理するため、GPUを用いないと時間がかかります。
(以下の例でinput.mp4は入力する動画、output.mp4は出力する動画名)
1 python yolo_video.py --input input.mp4 --output output.mp4
[参考サイト]
CNN(Convolutional Neural Network)を理解しよう(TensorFlow編) - Deep Insider
R-CNNの原理とここ数年の流れ Rich Feature Hierarchies for Accurate Object Detection and Semantic Segmentation
機械の目が見たセカイ -コンピュータビジョンがつくるミライ(57) ディープラーニングによる一般物体検出(6) - SSD | マイナビニュース