こんにちは。アプリケーション共同開発部エンジニアの石島です。
昨年の 11 月に Keras を使って Droid くんを認識してみた と題して Deep Learning の一種である Convolutional Neural Network(CNN)を用いて画像内の Droid くんの認識を行なってみました。今回は画像中の Droid くんを探してみようと思います!
今回は Droid くんのみの検出なので大量の画像と学習時間を必要とする Deep Learning ではなく、以前からある画像特徴量の HOG(Histograms of Oriented Gradients)特徴量と機械学習手法の SVM(Support Vector Machine)を用いて Droid くんの検出を行います。
この組み合わせは Deep Learning の登場前にはよく用いられていた手法です。こちらを簡単に使用できるライブラリが Dlib と呼ばれる機械学習ライブラリです。詳しくは参考リンクをご覧ください。
Dlib
Dlib は現実の問題を解決するために複雑なソフトウェアを C++ で作成するための機械学習アルゴリズムとツールを含む最新の C++ ツールキットです。ロボット、組み込み機器、携帯電話、大規模な高性能コンピューティング環境など、幅広い分野の業界および学術界で使用されています。Dlib では Boost Software License が採用されており、どのアプリケーションでも無料で使用できます。
Dlib のサンプルコード内に顔認識と顔の特徴点を検出するものがあるのですが、前者は OpenCV のものより精度が高い気がします。
後者の特徴点は顔の目や眉、口といった部分の座標を得ることができます。また、Dlib 内で使用される HOG 特徴量というのは画像中の局所領域に対し、領域内の輝度勾配をヒストグラム化することで算出される画像特徴量のことです。
一方、SVM というのはマージン最大化とカーネルトリックと呼ばれるテクニックでうまくデータを分ける境界線(関数)を見つけ出す手法です。
準備
では早速行なっていきます。まずは学習したい画像を準備します。こちらは以前の Droid くん認識で使用した約 140 枚の画像を使いまわします。
imglab
次に Dlib 内に imglab というツール(要ビルド)を使って学習画像から検出したい領域を指定します。
はじめに学習画像リストの作成のために以下のコマンドを実行します。(imglab は exe ファイルです)selected_droid.xml は学習する画像の一覧が記されたファイルとなります。
また droids は学習に使用する画像を含むフォルダです。コマンドを実行すると selected_droid.xml と image_metadata_stylesheet.xsl というファイルが作成されます。
./imglab -c selected_droid.xml droids
次に作成された selected_droid.xml に画像内の Droid くんの位置を指定する作業を行います。以下のコマンドを実行すると学習用の画像全てが読み込まれて imglab が起動します。
操作方法は Shift を押しながらドラックすることで領域指定できます。また、修正したいときは指定した領域をダブルクリックして delete キーで領域を削除した後で再び領域指定してください。
1 枚の画像中に検出したい物体が複数ある場合は複数領域指定してください。全ての画像に対して領域指定を終えたら、保存を実行してください。
./imglab selected_droid.xml
画像左が imglab を起動した画面で右が領域指定した画面です。
保存した xml ファイルの中身は以下のようになっているはずです。
<?xml version='1.0' encoding='ISO-8859-1'?> <?xml-stylesheet type='text/xsl' href='image_metadata_stylesheet.xsl'?> <dataset> <name>imglab dataset</name> <comment>Created by imglab tool.</comment> <images> <image file='droid/001.jpg'> <box top='25' left='220' width='238' height='318'/> </image> <image file='droid/002.jpg'> <box top='31' left='212' width='219' height='327'/> </image> <image file='droid/003.jpg'> <box top='52' left='180' width='258' height='285'/> </image> <image file='droid/004.jpg'> <box top='86' left='210' width='245' height='257'/> </image> <image file='droid/005.jpg'> <box top='16' left='174' width='222' height='275'/> </image>
学習
では領域指定した xml ファイル をもとに Dlib で学習を行います。以下が学習するコードです。学習が完了すると svm ファイルが作成されているはずです。これが学習済みの分類器となります。学習のオプションは他にもありますが、今回は以下のオプションのみ使用しました。
import dlib if __name__ == "__main__": train_xml = "selected_droid.xml" svm_file = "droid_detector.svm" options = dlib.simple_object_detector_training_options() # 学習時に画像の左右の反転を行うか options.add_left_right_image_flips = True # コストパラメータ options.C = 5 # スレッド数 options.num_threads = 4 # 学習中に詳細を表示するか options.be_verbose = True dlib.train_simple_object_detector(train_xml, svm_file, options)
Droid くん検出
では、実際に学習した分類器で画像内の Droid くんを探してみようと思います。ソースコードは以下の通りです。画像の読み込み等には OpenCV を使用しています。
import dlib import cv2 import numpy as np import sys if __name__ == "__main__": param = sys.argv # dlibで学習したモデルを読み込む detector = dlib.simple_object_detector("droid_detector.svm") # 画像の読み込み img = cv2.imread(param[1]) # 検出箇所を矩形で描画 rectangles = detector(img) for rect in rectangles: cv2.rectangle(img, (rect.left(), rect.top()), (rect.right(), rect.bottom()), (147, 20, 255), 2) cv2.imshow("image", img) cv2.waitKey(0) cv2.destroyAllWindows()
うまく Droid くんを検出できました。Dlib を使用することで数行で Droid くんを探す機能を作ることができました!次に以前 Keras で作成した Droid くん認識モデルも合わせて検出した Droid くんが Normal Droid くんなのか、Queen Droid くんであるかの認識までを行なってみます。
Droid くん検出 & 認識
流れとしては HOG と SVM で画像内の Droid くんの位置を検出します。
検出した領域のみを認識対象とし、CNN で認識を実行するといった流れです。画像内を左上から右下まで順に任意の大きさの矩形領域を切り出して、その領域に対して認識を行うことでどこに何が存在するのかを判定することは可能です。
しかし、認識する物体数や物体の大きさが大きくなればなるほど、様々な大きさの矩形領域を切り出す作業と各領域において認識を行うので処理時間が増加するという問題があります。
よって、物体を検出した後にその領域にのみ認識を行うことで処理時間の増加を防ぐことができます。では実際に組みわせてみましょう!
import dlib import cv2 import numpy as np import sys from keras.models import load_model # モデルデータ MODEL_DATA = "droids_model_best.hdf5" # カテゴリー CATEGORIES = ["Normal Droid", "Queen Droid"] """ 画素値の正規化 @param[in] data 画像データリスト @return data 画像データリスト """ def normalization(data): data = data.astype("float32") data = data / 255.0 return data if __name__ == "__main__": param = sys.argv # dlibで学習したモデルを読み込む detector = dlib.simple_object_detector("droid_detector.svm") img = cv2.imread(param[1]) # モデルの読み込み model = load_model(MODEL_DATA) # 検出箇所を矩形で描画 rectangles = detector(img) for rect in rectangles: rect_img = img[rect.top():rect.bottom(), rect.left():rect.right()] cv2.rectangle(img, (rect.left(), rect.top()), (rect.right(), rect.bottom()), (147, 20, 255), 2) # 検出画像をリサイズ resize_rect_img = cv2.resize(rect_img, (64, 64)) # 畳み込みニューラルネットワークに入力する形に変形 reshape_rect_img = np.reshape(resize_rect_img, (1, resize_rect_img.shape[0], resize_rect_img.shape[1], 3)) # 正規化 X = normalization(reshape_rect_img) # 予測(分類) predict = model.predict_classes(X) for i, p in enumerate(predict): cv2.putText(img, str(CATEGORIES[p]), (rect.left()+10, rect.top()-10), cv2.FONT_HERSHEY_PLAIN, 1.25, (147, 20, 255)) cv2.imshow("image", img) cv2.waitKey(0) cv2.destroyAllWindows()
左がテスト画像、右が結果となります。Droid くんを検出し、どちらの種類の Droid くんなのかが矩形の外に表示されていることがわかると思います。
まとめ
Dlib と Keras を組み合わせることで比較的簡単に複数物体がある中でも Droid くんを探し出し、どちらが Normal Droid くん、Queen Droid くんであるかの認識までできました!
最近では画像認識系のサービスが増え、ノンプラグラミングで試せるのが多いですが、何もかも Deep Learning の技術に頼るのではなく、条件次第で従来の技術で対応できることもあるのでその場に応じて選択することが重要だと思います。
参考リンク
画像からHOG特徴量の抽出
サポートベクターマシン(support vector machine:SVM)の基礎
フェンリルのオフィシャル Twitter アカウントでは、フェンリルプロダクトの最新情報などをつぶやいています。よろしければフォローしてください!
フェンリルの Facebook ページでは、最新トピックをお知らせしています。よろしければいいね!してください!