ディープラーニングで動画に自動でモザイクをかける「ディープモザイク」作ってみました

f:id:karaage:20180527185431p:plain:w480

ディープラーニングでの物体検出

 以前ディープラーニングを使った物体検出を試してみました。

 前回の成果物は以下です。 f:id:karaage:20180321151526g:plain:w640

 ただ、このままではちょっと面白いだけで何の役にも立ちません。折角なので何か自前のデータの学習をさせて、世のため人のためになるものを作ってみたいですね(無表情)。

 ふと思い出したのは、アダルドビデオのモザイク入れです。あの処理って、実は自動化されていなくて、職人さんが1フレーム毎にモザイクを入れているらしいのです。この作業を自動化すれば、きっともっと世の中に素敵な動画が溢れてみんなが幸せになるはず!

 というわけで、モザイクを自動で入れるソフトを作れないか試してみることにしました。名付けて「ディープモザイク」です。途中宇宙語が続くので、結果が気になる方は最後だけ見て下さい。

ディープラーニング(SSD)を使ってモザイクをかける方法

追記:

 この記事では、VoTTの古いバージョン(Ver1)を使用しています。Ver1もまだダウンロードできますが、最新のVer2に関して知りたい方は、以下記事に使い方書いていますので、よろしければ参照下さい。

使用するソフト

 今回使用するのは、KerasとTensorFlowでSSDを実装している「rykov8/ssd_keras」のリポジトリをフォークして、自分が改造した以下のリポジトリとなります。

Python環境構築

 最初に、以下の記事を参考にセットアップします。使用するPCはMacかLinuxを想定しています。動作確認した環境は iMac (Retina 4K, 21.5-inch, 2017) + macOS Sierra 10.12.6 です。LinuxはUbuntu 16.04で確認をしています。

 主要なライブラリのバージョンは以下です。

anaconda3==4.4.0
tensorflow==1.0.0
keras==1.2.2
opencv-python==3.3.0

 具体的な手順としては、上記記事を参考にpyenvをインストールしたあと、以下コマンドでAnaconda3と主要なライブラリをバージョン指定してインストールして下さい(最新バージョンだと動きません)。

$ pyenv install anaconda3-4.4.0
$ pyenv global anaconda3-4.4.0
$ conda create -n ssd anaconda
$ pyenv global anaconda3-4.4.0/envs/ssd 
$ pip install numpy==1.13
$ pip install tensorflow==1.0.0
$ pip install keras==1.2.2
$ pip install opencv-python==3.3.0.10

教師データ収集

 教師データを収集します。今回は卑猥なロンスタさんの動画を使用しました。フリー素材のロンスタ(id:lonestartx) さん、いつもありがとうございます!下記リポジトリのfree_dataというディレクトリにも実際に使用したデータが、アノテーションデータ付きでアップロードされています。

 もし画像を使って同じことをやりたいという人は、ネットからスクレイピングするのが良いと思います。以下にスクレイピングする方法の記事紹介しますが、この方法で集められる画像は量が少ないのでさらに一工夫必要かもしれません。

 また、画像の場合はここで後の処理のために全ての画像ファイルの拡張子をJPEG形式(.jpg)にしておいて下さい。これは、アノテーションに使用する「VoTT」というソフトがエクスポートするxmlファイルのfilenameに何故かファイルの拡張子が入れてくれないため必要な処理です。

アノテーション

 アノテーションツールとしてVoTTというマイクロソフト謹製のソフトを使います(Twitterで教えてもらいました)。他にも、labelImgImageNet_Utilslabeling_for_object_detection等のツールを比較したのですが、以下の4点でVoTTを選びました。

  • ツールの使い勝手が良い
  • Windows/Mac/Linuxのバイナリパッケージがある(ビルド不要)
  • SSD(PASCAL VOC形式)とYOLOの両方の形式のエクスポートが可能
  • 画像に加えて動画にも対応している

 実は前述したように「エクスポートするxmlファイルのタグにファイルの拡張子を入れてくれない」とか「ファイルパスの指定が絶対パス」などの細かい不満点に後で気付いたのですが、運用でカバーできるのでそこは割り切りました。

 VoTTは以下からダウンロードできます。

 私はMac用のバイナリファイル「VoTT-OSX.zip」をダウンロードしました。

 起動すると、以下のような画面が表示されます。今回は動画を使うので、右の「Video」をクリックします。 f:id:karaage:20180514111206p:plain:w640

 動画を選ぶダイアログが表示されるので、動画を選択します。なお「VoTT」で読み込める動画の形式は限られているようなので、もし動画が読み込めない場合はあらかじめ動画を変換しておきましょう。私の場合mov形式のファイルが読み込めなかったので、あらかじめiMovieでmp4形式に変換しておきました。

 動画を読み込むと以下のような画面が表示されるので、Region TypeにRectangleを選び、検出したいカテゴリのラベルを「Labels」に加えます。今回は、卑猥なロンスタさんの顔「face」と手「hand」とします。 f:id:karaage:20180527180411p:plain:w640

 ここからが地獄のアノテーション作業です。以下のように検出したい物体を四角(Bounding Box)で囲って、タグを付けていきましょう。 f:id:karaage:20180527180633p:plain:w640

 ブレた画像等、なるべく多くのパターンでアノテーションしておきます。ここでの作業が精度の肝となります。そこの人「全然自動じゃ無い!」とか言わないように! f:id:karaage:20180527180740p:plain:w640

 ちなみに「VoTT」は、動画を読み込んだ場合、タグをつけてから次のフレームに移動すると、自動で判定してタグをつけてくれます。めちゃめちゃ凄いです。もちろん失敗する場合もあるので、そのときは手動でタグ付けして下さい。

 疲れたので、100フレーム分くらいタグをつけたら良しとします。理想的には、数千から数万くらいのデータはあった方が良いようですが、今回はこの程度で勘弁してあげましょう。

 画像の場合も、基本的には同じ要領でアノテーションが可能です。

 タグ付け終わったら、一旦ここで File -> Saveで保存しておきましょう。教師データのフォルダと同じ場所にjsonファイルが生成されます。このjsonファイルがタグ付け情報なので、教師データと一緒に大切に保存しておきましょう。jsonファイルは、テキストファイルなので一度どんなものか中身を見ておくのも良いと思います。以下のようなjsonファイル用ビュワーを使うのもオススメです。

Python製のマルチプラットフォームJSONファイルビューワーPyJSONViewerを作った - MyEnigma

物体検出のソフトウェア準備

 以下コマンドでリポジトリをクローンします。

$ git clone https://github.com/karaage0703/ssd_keras

 VoTTを起動して、SSDの形式でExportします。具体的には、メニューのObject Detection->Export Tagを選択します。

 すると以下の画面が表示されるので、以下のようにExport Format:はTensorflow Pascal VOCとして、Output Pathにssd_keras/PASCAL_VOC/data を指定します。 f:id:karaage:20180603233606p:plain:w640

 ssd_keras/PASCAL_VOC/dataに以下のようなファイルが生成されたらOKです。 f:id:karaage:20180514111257p:plain:w640

 次に、以下コマンドを実行してXMLファイルからpickle(.pkl)ファイルを生成します。

$ cd ssd_keras/PASCAL__VOC
$ python get_data_from_XML.py

 今回は、「face」「hand」の2つのタグという前提ですが、もし異なる数のタグを学習させたい場合は、ssd_keras/PASCAL_VOC/get_data_from_XML.pyをあらかじめ修正しておいて下さい。具体的には、 self.num_classesにラベルの数を入れて、def _to_one_hot関数の中身を、ラベルの名前に合わせて修正して下さい。

    def __init__(self, data_path):
        self.path_prefix = data_path
        self.num_classes = 2
        self.data = dict()
        self._preprocess_XML()

...

    def _to_one_hot(self,name):
        one_hot_vector = [0] * self.num_classes
        if name == 'face':
            one_hot_vector[0] = 1
        elif name == 'hand':
            one_hot_vector[1] = 1

...

import pickle
data = XML_preprocessor('data/Annotations/').data
pickle.dump(data,open('supervised_data.pkl','wb'))

 pickleファイルが生成できたら、以下実行してpickleファイルをきちんと読み込めるか確認して下さい。以下のように検出したいラベルの座標が表示されればOKです。

$ python
>>> import pickle
>>> f = open('supervised_data.pkl', 'rb')
>>> data = pickle.load(f)
>>> print(data.keys())
dict_keys(['lonestartx_free_frame_1', 'lonestartx_free_frame_10', 'lonestartx_free_frame_11', 'lonestartx_free_frame_12', ....
>>> print(data['lonestartx_free_frame_1'])
[[ 0.43333333  0.06851852  0.515625    0.27777778  1.          0.        ]
 [ 0.35833333  0.72222222  0.409375    0.84074074  0.          1.        ]
 [ 0.565625    0.7         0.62291667  0.7962963   0.          1.        ]]

 続いて、以下サイトから重みパラメータ weights_SSD300.hdf5 を入手します。。ブラウザはGoogle Chrome推奨です。

MEGA

 ダウンロードしたweights_SSD300.hdf5 は、ssd_keras ディレクトリ直下に置きます。

ディープラーニング(SSD)の学習

 いよいよ学習ですが、その前にssd_kerasディレクトリ直下のSSD_train_custom.pyを修正して下さい。

 修正する必要があるのは25行目のクラス数のNUM_CLASSESです。分類したいクラスの数(ラベルの数)+ 1を設定します。1を加えているのは、どれにも当てはまらないクラスがあるためです。今回は、2つのラベルなので3を入れています。

 修正したらssd_kerasで以下のコマンドを実行して下さい。

$ python SSD_train_custom.py

 学習が終わると、以下のようにテスト結果が表示されます。これらの結果は、元々のソフトでは表示されなかったので私が追加したものです。

 以下はacc(精度)の結果です。詳細の説明は省きますが、val_accというのが本当(?)の精度です。0.6〜0.7あたりで収束していますね。教師データの数が少ないのでこんなものなのかもしれません。

f:id:karaage:20180527181720p:plain:w640

 最後にランダムに選択したテストデータで結果が表示されます。

f:id:karaage:20180527181649p:plain:w640

 良さそうな感じですね。

モザイク動画生成作業

 いよいよ学習したモデルを用いてモザイク動画を生成します。

 SSD_pred_video.py というファイルの以下部分を、自分の使用したラベル、学習したデータ、使用する動画ファイルのパスに修正します。学習データは、checkpointsというディレクトリにweights.xx-x.xx.hdf5 というファイルがたくさんあるので、そのファイル名を指定して下さい。基本には世代が進んだ学習モデルを用いた方がよいので 100世代目のweights.99-x.xx.hdf5 というファイルを用いるのが よいでしょう(後ろの x.xx の部分は学習を実行するたびに変わります)。

voc_classes = ['face', 'hand']
model.load_weights('./checkpoints/weights.99-2.78.hdf5', by_name=True)
vid = cv2.VideoCapture('./free_data/lonestartx_free.mp4')

 修正したら、以下実行しましょう。

$ python ./SSD_pred_video.py

 ./free_data/output.avi というモザイク処理された動画が生成されます。

モザイク動画生成結果

 ディープラーニングでのモザイク処理は以下のようになりました。

f:id:karaage:20180527174136g:plain:w640

 結構ちゃんとモザイクかけれていますね!たまにモザイク外れているけど、気にしないことにしましょう(笑)

まとめ(ディープラーニングでの自動モザイクかけ)

 ディープラーニング使って、卑猥な動画(?)に自動でモザイクをかけるソフト「ディープモザイク」を作ってみました。今回は動画を大量にアノテーションしているので、全然自動化という感じがしないかもしれませんが、大量の教師データさえ蓄えれば、実際の商業用のムフフな動画でも、かなり高精度にモザイクをかけられるのじゃないかなと思っています。

 本当に使おうとすると、今回の様な四角(Bounding Box)ではなく、ピクセル単位で範囲を指定できる、セマンティックセグメンテーションを用いた高精度な物体検出が必要になってくるので、もっと難易度は上がるのかなと思います。

 ただ、自前の動画データとディープラーニングという組み合わせに関しては可能性を感じていただけたのでは無いかと思います。この記事で興味持った方は、是非色々な応用例を試していただけたらと思います。私も世のため人のためエロのため(?)ディープラーニングの活用例を色々考えてみたいと思います!

Aidemyさんのブログコンテスト(#Aidemynote)参加

 本記事で使用したプログラム、Kerasというディープラーニングのフレームワークを使用したのですが、プログラムの動作の理解、改造にあたっては、Aidemyさんの「ディープラーニング基礎」が非常に参考になりました。

 ディープラーニングを簡単に利用できるソフトは色々出て来ていますが、やはり自前データを使って新しいことをやろうとすると、ディープラーニング自体の基礎や、Pythonの知識、TensorFlow + Kerasというディープラーニングのフレームワークの使いこなし等様々な知識が必要になることを改めて実感しました。今回も「Aidemy」さんのコースにない物体検出にチャレンジしてみたのですが、基礎の重要性を感じるとともに、学んだことは応用きくことが分かりました。もしディープラーニングで何か新しいことをやりたい人はAidemyさんがオススメです!そして、Aidemyさんには是非、物体検出のコースも作って欲しいなと思います(笑)

 本記事は、以下のAidemyさんのコンテストに参加しています。

 追記:なんと本記事が Aidemynoteの特別賞受賞しました!ありがとうございます!受賞の感想noteに書きました!

環境設定不要で画像認識を学べるチュートリアルの宣伝

 今回の物体検出の例は、環境設定が必要ですし、教師データのアノテーションも大変で、実践するにはハードル高い内容となります。そこで、もっと手軽に機械学習・ディープラーニングを学習したいという人のために「Google Colaboratory」を使って環境設定不要でブラウザだけで画像認識を学べるチュートリアルを有料noteで公開しました。

 非常に実践的な内容となっていますので、Aidemyさんのプログラムと合わせて手を動かしながら実践いただくと理解が深まるのではないのかなと思っています。興味のある方は、こちらも合わせてご参照下さい。

関連記事

変更履歴

  • 2019/08/14 VoTT Ver2に関して追記