【Android】adbを使ってAlarmManagerの実行予定時間を確認する
開発中にAlarmが発火するまで待機するのも馬鹿らしいので、adbのコマンドを利用して指定した時間にAlarmが設定できているのか確認を行う。
$ adb shell dumpsys alarm | grep 【hoge】 RTC_WAKEUP #4: Alarm{7700c50 type 0 when 1498210200398 com.hoge.exampleapp} tag=*walarm*:com.hoge.exampleapp/.ExampleReceiver operation=PendingIntent{455ce49: PendingIntentRecord{45bb25c com.hoge.exampleapp broadcastIntent}}
【hoge】には自分のプロジェクトで使っているPackage nameの一部を入れる。
whenの後に表示されているunix timeを適当な変換器で日本時間に直してあげればOK。
参考
Androidアプリ開発者なら知っておくべきdumpsysコマンド | Money Forward Engineers' Blog
HoI4の実績を全て解除した
4月の頭辺りにHoI4が発売されていることを知り(遅い)、Steamで即購入後遊びまくった。
一通り主要国で遊んだあと、今作のゲームシステムへの理解を深めるために全実績解除を目指すことにした。
Achievements - Hearts of Iron 4 Wiki
英語版HoI4wikiに各実績の補足みたいなことが書いてあるので参考になった。
現時点で発売されている唯一のDLCであるTogether for Victoryで追加された実績を含め、全実績を無事解除できたので、軽く感想を記録しておく。
バージョンは全て1.3.3で行った。
個人的に面倒だったのは、Freegyptだと思う。
これはただただレンドリース設定後の待機が面倒なのと、枢軸北アフリカ軍に本土占領されることがあったのでストレスが溜まった。
実績解除に手こずったのは、恐らく定番のBearer of ArtilleryとCrusader Kings 2だった。
Bearer of Artilleryはダンツィヒをドイツに譲ったあとソ連に勝てば実質終了なので、試行1回で解除できた。
ただCrusader Kings 2の方は、解除にTake10以上の試行が必要で、丸3日間くらいかかった(下手糞!)。
プレイ方針は、NFと政治顧問で共産化目指しつつ、軍事工場2建設の後造船所4建設と潜水艦の研究を進める。
政治系NFが落ち着いた段階で海軍系NFを進め、次世代潜水艦のNF研究ボーナスで早めに1940型潜水艦の研究を終え、生産を急ぐ。
あとは幅16ぐらいの海兵隊(陸軍経験値で可能な限り海兵隊大隊を増やす)を8部隊用意し、NFで得た宣戦事由でポルトガルに宣戦。
初期配備の歩兵でアフリカ本土を守りつつ、海兵隊でポルトガルの島嶼と飛び地を占領する。
40年式の潜水艦が10程度いれば、上陸部隊の援護はぎりぎり可能なはずだが、運が悪いと敵駆逐艦隊に殲滅される(リセット案件)。
島嶼を制圧後、本土への上陸作戦を計画する。
ここで、リスボンではなく、ポルトに上陸し、河川にそって防衛線を敷きつつ、一部隊だけリスボンへの上陸作戦を命令すると、敵本土部隊とほぼ交戦せず降伏まで追い込める。
ポルトガル占領後はベルギー占領を行う方法も試行したが、あまり得にならない上に、ベルギーが連合に入るまでの時間が結構シビアなので、今回はやらなかった。
あとは普通に軍備を増強し、枢軸が連合に喧嘩売るのを待ち、英本土に上陸作戦をしかける。
この頃には潜水艦隊が40〜50隻ぐらいはいるはずなので、どうにか英本土に上陸することは可能なはず。
上陸後はほとんど英軍の抵抗は無いので、ロンドンの占領を目指す。
ただし、イギリスが降伏してしまうと和平会議が開催されてしまうため、いくつかビクポの高いプロビを残しつつ、アフリカ戦線に移る。
占領を進めていくと各アフリカ諸国を独立させるイベントが発生するが、これが完全にランダムイベントなため、かなりイライラが募る。
連合のアフリカ領侵攻を進めるときは、南アの直轄地になるように、軍は自国から進軍させるよう心がけたほうが良い気がする。
先に独立した国の領土になっていると、ランダム独立イベントが発生しない気がする(1948辺りまで待機しても独立しなかったので……)。
一応アメリカの介入を防ぐには、共産化してすぐに政治工作をかければ間に合うはず。
アメリカが参戦してくると、アフリカ諸国の独立が遅い場合英本土が守りきれない可能性があるので、可能ならば工作で共産化させたい。
Hearts Of Iron 4 - Crusader Kings 2 Achievement Guide - YouTube
基本的にはこの人のプレイを参考にやったが、微妙にバージョンが違うので、同じやり方ではベルギー戦あたりが厳しい。
実績解除自体は途中から作業感が出てきて気持ちが萎える部分もあったが、AIの挙動が分かったり、プレイングの幅が広がる点では面白かった。
NBA LEAGUE PASSを解約する方法
シーズンも終わったし(ファーストラウンドスウィープ)NBA league passを解約しようとアカウントの設定関係を見て回ったんですが、Subscription停止がどこにもない。
解約に一手間必要だったので一応メモ。
これは2017年5月時点での方法なので、変更されている可能性があります。
規約
How can I cancel my NBA LEAGUE PASS subscription?
To cancel your NBA LEAGUE PASS subscription, please email nbasupport@neulion.com with your cancellation request.
とのこと。
簡単に訳すと、Subscriptionの解約するにはnbasupport@neulion.comにメールしてね!ということらしい。
解約するためだけに今時メールさせるの!?という驚きはあったが、仕様がないので指示に従う。
メール送付
簡易な英語で問題ないようなので、Youtubeにて解約方法を紹介していた人の例に倣う。
以下文例。
件名:Please cancel my subscription Hello Please cancel my NBA league pass subscription. My username is [自分のアカウントのユーザ名]. Thanks, [自分の名前].
結果
数日後にSubscription停止の返信が来ていた。
解約させたくないのは分かるけど、現代のサービスでこの仕様はあんまりだと思う。
調べてみたところ、アカウントの設定から解約できたという人がいたり、分割払いだから途中では解約できないよという人がいたり、若干情報が錯綜している感じがあった。
確かにNBA league passの契約内容はシーズン毎に多少なり差があるようなので、その時々で微妙に差異があるのかもしれない。
参考
【逃げ恥】恋ダンスでただガッキーだけを見ていたかったのでディープラーニングする(後編)
上記記事の続きです。
復習は面倒だが役に立つ
前回の記事では、ガッキーの顔とそうでないものの二値分類を行うモデルを作成し、実際に動画への適用を試みました。
残念ながら上手くいきませんでした。
詳細は前編をどうぞ。
忍耐は辛いが役に立つ②
前回の取り組みで顕在化した問題に対処していきます。
対処方法として、分類するクラスを増やすことで解決を図ります。
- ガッキー
- その他出演者4名
- その他顔
- その他顔として検出されてしまう物体
上記の7クラスの分類を行う顔判別機を作る方針でいきます。
本来であれば、各々の顔を前編でやったような手法で収集しなければいけませんが、面倒だったので、恋ダンス動画自体から顔データの収集を行います。
顔画像取得のコードは下記の通りです。
import cv2 import datetime import numpy as np from keras.models import load_model # 動画関係 target = "input.mp4" movie = cv2.VideoCapture(target) # 顔認識準備 model = load_model('./Models/gakky_face_model.hdf5') cascade_path = '/path/to/your/opencv3/haarcascade_frontalface_default.xml' cascade = cv2.CascadeClassifier(cascade_path) gakky_n = 0 if movie.isOpened() is True: ret, frame = movie.read() else: ret = False count = 0 while ret: # 顔検出 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) frontfaces = cascade.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=5) # 顔認識 for (x, y, w, h) in frontfaces: dst = frame[y:y + h, x:x + w] image = cv2.resize(dst, (64, 64)) image = image.transpose(2, 0, 1) image = image / 255. image = image.reshape(1, 3, 64, 64) if model.predict_classes(np.array(image)) == 0: cv2.imwrite('./FaceImagesFromVideo/Gakky/' + str(count) + '.jpg', dst) else: cv2.imwrite('./FaceImagesFromVideo/Others/' + str(count) + '.jpg', dst) count += 1 ret, frame = movie.read() # 50フレームごとに経過を出力 if movie.get(cv2.CAP_PROP_POS_FRAMES) % 50 == 0: print(datetime.datetime.now().strftime('%H:%M:%S'), '現在フレーム数:' + str(int(movie.get(cv2.CAP_PROP_POS_FRAMES))))
前回作成したモデルは、ガッキーかそうでないかを判別する能力はありませんでしたが、顔検出した箇所が、顔っぽいかそうでないかを判別する能力はありそうだったので、その予測値を利用して保存するディレクトリを選択しています。
この事前分類をしておくことで、後の分類負担を多少緩和できます。
処理が終わると、恋ダンスの動画中で顔だと思われる箇所が全て保存されるはずです。
あとは、冒頭で示した7つのクラスに合致するように、手動で画像を各々のディレクトリに分類していきます。
ディレクトリ名は下の画像のように、数字の接頭辞を付けておくとよいです。
最終的に動画から切り出した顔画像の枚数は、
- ガッキー:1516
- 出演者A:349
- 出演者B:398
- 出演者C:295
- 出演者D:452
- その他顔:85
- その他物:2591
となりました(アンバランス!)。
前編で用意したデータと統合すると、顔画像データは合計で7,525枚になりました。
繰り返しは気怠いが役に立つ
新しい顔画像データで再度学習を行っていきます。
修正箇所は下記の2ヵ所です。
- 顔データのNumPy配列出力を行う処理の箇所で、
data_dir_path
を新しい顔画像データのあるディレクトリに修正 - モデルオプションの指定を
nb_classes = 7
に修正
枚数が多くなったせいかそれなりに学習に時間がかかります。
最終的な学習経過はこのようになりました。
若干学習途中のような気がしないでもないですが、とりあえずこれで良しとします。
結果は怖いが役に立つ
では新しいモデルでの処理結果をいくつかご紹介します。
動画中の顔データを使っているので当たり前といえば当たり前ですが、ある程度求めていた処理が行われているように思います。
では、全く異なる動画中の顔を正しく判別することはできるでしょうか。
検証のため、予告動画に対して同様の処理を試みます。
多少うまくいっているパターン
ダメダメなパターン
上手く判別が行えている箇所も散見されましたが、上出来というには程遠い状態でした。
やはり顔データの学習画像が少なすぎるかつ似たパターンばかりという問題は大きいようで、きちんとした予測モデルを構築するためには、個々人の顔データを別途集め直す必要がありそうです。
DataAugmentationの訳は分からないが役に立つ
おまけとして、Kerasでお手軽にできるData Augmentation処理をしたモデルも作成してみます。
正直なところ、各パラメータの真偽でどのように扱いが変わるのかが分かりかねるので(勉強不足!)、設定に関してはいい加減です。
元々のソースコードのmodel.fit()
部分を下記のように書き換えて試みます。
from keras.preprocessing.image import ImageDataGenerator datagen = ImageDataGenerator( rotation_range=20, width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True) datagen.fit(X_train) hist = model.fit_generator(datagen.flow(X_train, Y_train, batch_size=batch_size), samples_per_epoch=X_train.shape[0], nb_epoch=nb_epoch, validation_data=(X_test, Y_test), verbose=1, callbacks=[checkpointer])
先程上手く判別できていなかった箇所はこのようになりました。
おおよそ正しく判別できています。
このシーンだけに着目するとかなり改善されたように思われますが、実際には誤判別も増えていたので、今回の場合はrecall増えてprecision減ったという印象でした。
逃げるは恥だが役に立つ
前編から引き続き、判別モデルの問題点解消に取り組みました。
当初の目的として掲げた、「恋ダンスでただガッキーだけを見ていたい」という目標はいくらか達成されたように思います。
記事中の検証用gifでは顔検出の枠線が多すぎてかなり気が散りますが、顔の上書きだけを行った動画を見ると、それなりに快適度がアップしており、個人的には満足しております。
とはいえ、画像収集に実施した手法(動画からの切り出し!)がイマイチだったため、モデルの汎用性にはかなりの問題が見られました。
学習に使用するデータの重要性が再認識できたように思います。
ガッキーかわいい!
参考は別サイトだが役に立つ
モチベーション
新垣結衣 | アーティスト | レプロエンタテインメント
ご注文はDeep Learningですか? - kivantium活動日記
Keras
Keras Documentation
Are there any codes for AlexNet, ZF Net, GoogLeNet, VGGNet in Keras · Issue #1568 · fchollet/keras · GitHub
Error when checking model target: expected activation_2 to have shape (None, 10) but got array with shape (3, 1) · Issue #3109 · fchollet/keras · GitHub
Kerasでアニメキャラの顔認識 - Qiita
続・深層学習でアニメ顔を分類する with Keras - Qiita
動画
Python + OpenCV で雑コラ動画を作成する③ 雑コラ動画作成 - Qiita
overlay a smaller image on a larger image python OpenCv - Stack Overflow
Gif
【逃げ恥】恋ダンスでただガッキーだけを見ていたかったのでディープラーニングする(前編)
ガッキー視聴を毎週の楽しみにしている方も多いのではないでしょうか。
かくいう私もその一人です。
現代社会の荒波に揉まれては心が荒む日々。
灼熱地獄の砂漠に与えられた一縷の望み、オアシス。
私にとって、恋ダンスを踊るガッキーとはそのような存在といえるでしょう。
そういうわけで、踊っているガッキーを毎日のように眺めているわけですが、ここで一つ不都合があります。
そう、ギャラリーが多すぎるということ。
私はただガッキーだけを見ていたいのですが、残念ながらそうは問屋が卸しません。 他の方々が踊っていたり、最悪の場合、ガッキーが全く画面に出ていないタイミングもあります(何を考えて編集しているんだ……)。
というわけで、オアシスの水を濾過するが如く、ガッキー以外の登場人物には退場願うこととします。
作業の流れとしては、
- ガッキーの顔かそうでないかを二値分類する判別機をつくる
- ガッキー以外の人の顔をガッキーで上書きする
という感じになります。
随分と前口上が長くなりましたが、早速取り掛かります。
書くのは恥だが役に立つ
……はず。
注意は頭にくるが役に立つ
- 記載しているソースコードは、公開用に一部修正して載せているので、もしかすると動かない場合があるかもしれません。
- ディープラーニングおよび画像処理に関しては全くのド素人なので、誤った記述が含まれている恐れがあります。
環境は人それぞれ違うが役に立つ
OpenCV3の導入は面倒だが役に立つ
下記記事を参考にOpenCV3をPythonから使用できるように準備します。
Mac OS X で OpenCV 3 + Python 2/3 の開発環境を整備する方法 – ymyzk’s blog
自分の環境下では、2016年12月の段階で
brew install opencv3 --with-python3
の実行時にエラーが発生していました。
もし同様の症状であれば、下記記事の解決策で解消できるはずです。
【macOS Sierra】OpenCV 3をbrewでインストールできない - プログラムは、用いる言葉の選択で決まる
ともかく、最終的にOpenCV3をインポートしてバージョンが確認できればOKです。
$ python >>> import cv2 >>> cv2.__version__ '3.1.0-dev'
容量は食うが役に立つ
何はともあれ、学習に使用するガッキーのお顔が無ければ始まりません。
画像収集の方法はいくつか考えられるかと思いますが、今回はTumblr
の投稿の中から、新垣結衣
のタグがついた画像付きの投稿を収集することにしました。
ちなみに、Tumblrにどんな画像が上がっているのか、というのは実際に見に行けばよく分かりますので、リンクを置いておきます。
https://www.tumblr.com/search/新垣結衣
見始めると一日が終わってしまうので気をつけてください。
実際の画像収集ですが、下記の2ステップで行いました。
- 投稿されている画像のURLを保存
- URLのリストから画像の保存
画像ダウンロードは並行して行っても問題無いとは思いますが、念のため処理を分けました。
画像URL取得のコードは下記の通りです。
TumblrのAPI_KEYが必要なので、各自でご用意ください。
また、requests
を使用するので、pip install requests
も事前に行ってください。
import requests import time num = 50 url = 'https://api.tumblr.com/v2/tagged' payload = { 'api_key': 'YOUR_API_KEY', 'tag': '新垣結衣', 'before': '' } photo_urls = [] for i in range(num): r = requests.get(url, params=payload) r_json = r.json() for data in r_json['response']: if data['type'] != 'photo': continue for photo in data['photos']: photo_urls.append(photo['original_size']['url']) payload['before'] = r_json['response'][len(r_json['response']) - 1]['timestamp'] time.sleep(1) with open('photo_urls.txt', 'w') as file: for url in photo_urls: file.write('%s\n' % url)
一度のリクエストにつき20件の投稿が取得できるので、num = 50
でおよそ1000件分の投稿から画像URLを取得したことになります。
次に、生成したURLリストから、実際に画像を取得します。
import requests import time photo_urls = [] with open('photo_urls.txt', 'r') as f: line = f.readline() while line: photo_urls.append(line.strip()) line = f.readline() for i, url in enumerate(photo_urls): extension = url[-3:] if extension != 'jpg' and extension != 'png': continue with open('./TumblrImages/' + str(i) + '.jpg', 'wb') as handler: response = requests.get(url) if not response.ok: continue handler.write(response.content) time.sleep(1)
URLから順次画像を取得し、TumblrImagesディレクトリに格納します。
ひとまずこれで画像の収集作業は終了です。
自分の場合は、最終的に1931枚の画像が集まりました。
忍耐は辛いが役に立つ
一生懸命収集してきた画像ですが、このままでは顔の特徴データとしては使えません。
なので、各々の画像に対してOpenCVを用いて顔を検出し、顔の範囲を切り出す処理を行います。
import os import cv2 data_dir_path = './TumblrImages/' image_list = os.listdir(data_dir_path) # 恐らくglobで取得した方が良い front_cascade = cv2.CascadeClassifier('/path/to/your/opencv3/haarcascade_frontalface_default.xml') count = 0 for i, image_name in enumerate(image_list): image = cv2.imread(data_dir_path + image_name) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) frontfaces = front_cascade.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=5) for (x, y, w, h) in frontfaces: dst = image[y:y + h, x:x + w] cv2.imwrite('./FaceImages/' + str(count) + '.jpg', dst) count += 1
detectMultiScale
で顔検出の許容度合を大きくしてしまうと、かなりゴミが紛れ込んでくるので適当なパラメータを設定する必要があります。
余談ですが、個人的にはOpenCVの顔検出の精度は余り高くないように思いました。
前処理等の工夫が必要なのかなあという印象です。
最終的な切り抜き後の画像は、1839枚でした。
さて、ここまでの作業が終わると、FaceImages
ディレクトリはガッキーの顔で満たされているはずです。
しかし残念なことに、OpenCVでの誤検出画像や、別人の顔が一部紛れ込んでいるため、取り除く作業が必要になります。
何か効率的な手段があれば良かったのですが、残念ながら妙案が思い浮かばなかったので、目視で分類します。
import glob import shutil import cv2 data_dir_path = './FaceImages/' image_list = glob.glob(data_dir_path + '*.jpg') gakkies = [] others = [] for i, path in enumerate(image_list): image = cv2.imread(path) cv2.imshow('image', image) # Left arrow is not gakky: 63234 # Right arrow is gakky: 63235 key = 0 while key != 63234 and key != 63235: key = cv2.waitKey(0) if key == 63234: others.append(path) else: gakkies.append(path) cv2.destroyAllWindows() for gakky in gakkies: shutil.move(gakky, data_dir_path + 'Gakky/.') for other in others: shutil.move(other, data_dir_path + 'Other/.')
一枚ずつ画像を表示しつつ、矢印キーの入力でガッキーとその他を分類します。
最終的に、それぞれGakky
ディレクトリとOther
ディレクトリに画像を移動します。
分類後の枚数は、ガッキーが887枚、その他が952枚でした。
分類後は、無事ガッキーの顔でディレクトリが満たされていることを確認できます。
これでようやく学習データの準備が完了です。
NumPyは苦しいが役に立つ
学習を行っていくにあたり、画像データをNumPy配列として保存しておきます。 実際の学習はこのデータを基に行われます。
import os import glob import numpy as np import cv2 data_dir_path = './FaceImages/' tmp = os.listdir(data_dir_path) dir_list = sorted([x for x in tmp if os.path.isdir(data_dir_path + x)]) X_data = [] Y_data = [] for i, dir_name in enumerate(dir_list): images = glob.glob(data_dir_path + dir_name + '/*.jpg') for path in images: image = cv2.imread(path) image = cv2.resize(image, (64, 64)) image = image.transpose(2, 0, 1) image = image / 255. X_data.append(image) Y_data.append(i) X_ary = np.array(X_data) np.save('X_data.npy', X_ary) Y_ary = np.array(Y_data) np.save('Y_data.npy', Y_ary)
X_data.npy
が画像データの配列で、Y_data.npy
が正解クラスの配列ですね。
画像の適切なサイズが分からなかったので、とりあえず64*64にしています。
Kerasは角だが役に立つ
ここから、本題のKerasを使った深層学習に入っていきます。
応用するだけのスキルも知識も無いので、Kerasドキュメントの公式に掲載されているVGG風CNNのモデル構造を微修正しながら利用させて貰います。
Kerasでは、TensorFlowをバックエンドで使用します。
今回はGPUを使わないので、インストールはシンプルです。
ついでにいくつかの必要なパッケージも入れてしまいましょう。
$ pip install scikit-learn scipy pandas matplotlib $ pip install tensorflow keras h5py $ python >>> import keras Using TensorFlow backend.
このようにバックエンドがTensorFlowになっていることが確認できれば準備完了です。
では実際に学習を行います。
from keras.models import Sequential from keras.layers import Dense, Dropout, Activation, Flatten from keras.layers import Convolution2D, MaxPooling2D from keras.optimizers import SGD from keras.callbacks import ModelCheckpoint import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split # 学習データの用意 X_data = np.load('X_data.npy') Y_data = np.load('Y_data.npy') X_train, X_test, Y_train, Y_test = train_test_split( X_data, Y_data, test_size=0.15, random_state=42) # オプション指定 batch_size = 32 nb_classes = 2 nb_epoch = 50 # モデル定義 model = Sequential() model.add(Convolution2D(32, 3, 3, border_mode='same', input_shape=X_train.shape[1:])) model.add(Activation('relu')) model.add(Convolution2D(32, 3, 3)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Convolution2D(64, 3, 3, border_mode='same')) model.add(Activation('relu')) model.add(Convolution2D(64, 3, 3)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(256)) model.add(Activation('relu')) model.add(Dropout(0.5)) model.add(Dense(nb_classes)) model.add(Activation('softmax')) sgd = SGD(lr=2e-2, momentum=0.9, decay=0.0, nesterov=True) model.compile(loss='sparse_categorical_crossentropy', optimizer=sgd, metrics=['accuracy']) checkpointer = ModelCheckpoint(filepath="./Models/gakky_face_model.hdf5", verbose=1, save_best_only=True) # モデル学習 hist = model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch, validation_data=(X_test, Y_test), shuffle=True, verbose=1, callbacks=[checkpointer]) # 学習経過のプロット plt.style.use("ggplot") df = pd.DataFrame(hist.history) df.index += 1 df.index.name = "epoch" df[["acc", "val_acc"]].plot(linewidth=2) plt.savefig("acc_history.pdf") df[["loss", "val_loss"]].plot(linewidth=2) plt.savefig("loss_history.pdf")
もし、
Exception: Error when checking model target: expected activation_ to have shape
というようなエラーが出ている場合、~/.keras/keras.json
を開き、"image_dim_ordering": "th"
と修正すると上手くいくかもしれません。
バックエンドがTensorFlowなのかTheanoなのか云々で発生する問題らしいですが、詳細は分かりません。
コードの方では、モデルを定義し、学習させ、最後にmodel.fit
の返り値のhistory
情報を使って、学習経過をPDFで保管しています。
学習時のcallback
にModelCheckpoint
を指定することで、全エポック中で最も良い指標になったモデルを自動的に保存するように設定しました。
保存先のディレクトリは事前に用意していないとエラーになるのでご注意ください。
ちなみに、学習経過はこのようになりました。
今回は第20epochが最も優秀だったようです。
人工知能は人類最悪にして最後の発明だが役に立つ
モデルが完成したので、試しに対話環境でロードして遊んでみます。
$ python >>> from keras.models import load_model Using TensorFlow backend. >>> import numpy as np >>> import cv2 >>> model = load_model('./Models/gakky_face_model.hdf5') >>> def predict(path): ... image = cv2.imread(path) ... image = cv2.resize(image, (64, 64)) ... image = image.transpose(2, 0, 1) ... image = image / 255. ... image = image.reshape(1, 3, 64, 64) ... print(model.predict(np.array(image))) >>> predict('gakky.png') [[ 0.96610594 0.03389405]] >>> predict('yama.png') [[ 0.21037847 0.78962159]]
上記のような適当に用意したガッキーの顔画像とSierraの山並み(一部!)に対して予測すると、確かに結果は正しそうです。
動画処理は重いが役に立つ
ここからは、用意したモデルを使用しながら、実際に恋ダンス動画の加工処理を行います。
とりあえず恋ダンスの動画が必要なので、TBSの公式Youtubeから頂戴します。
冒頭から述べている通り、判別機がガッキーの顔だと判断すればそのまま、その他の顔だと判断すれば下記のガッキーフェイスで上書きします。
動画加工のためのコードは下記の通りです。
import cv2 import datetime import numpy as np from keras.models import load_model # 動画関係準備 target = 'input.mp4' # 恋ダンス動画 result = 'output.m4v' movie = cv2.VideoCapture(target) fps = movie.get(cv2.CAP_PROP_FPS) height = movie.get(cv2.CAP_PROP_FRAME_HEIGHT) width = movie.get(cv2.CAP_PROP_FRAME_WIDTH) fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') out = cv2.VideoWriter(result, int(fourcc), fps, (int(width), int(height))) # 顔認識準備 model = load_model('./Models/gakky_face_model.hdf5') cascade_path = '/path/to/your/opencv3/haarcascade_frontalface_default.xml' cascade = cv2.CascadeClassifier(cascade_path) gakky_n = 0 # モデル予測時のクラス番号 # 書き込み画像準備 ol_imgae_path = "gakky.png" ol_image = cv2.imread(ol_imgae_path) # 各フレームへの処理 if movie.isOpened() is True: ret, frame = movie.read() f_h, f_w = frame.shape[:2] else: ret = False while ret: # 顔検出 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) frontfaces = cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=2) # 顔認識 for (x, y, w, h) in frontfaces: dst = frame[y:y + h, x:x + w] image = cv2.resize(dst, (64, 64)) image = image.transpose(2, 0, 1) image = image / 255. image = image.reshape(1, 3, 64, 64) face_class = model.predict_classes(np.array(image), verbose=0) # ガッキーじゃなければ上書き if face_class != gakky_n: resized = cv2.resize(ol_image, (h, w)) frame[y:y + h, x:x + w] = resized cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2) # フレーム書き込み out.write(frame) ret, frame = movie.read() # 50フレームごとに経過を出力 if movie.get(cv2.CAP_PROP_POS_FRAMES) % 50 == 0: print(datetime.datetime.now().strftime('%H:%M:%S'), '現在フレーム数:' + str(int(movie.get(cv2.CAP_PROP_POS_FRAMES)))) # 途中で終了する場合コメントイン # if movie.get(cv2.CAP_PROP_POS_FRAMES) > 500: # break
検証段階では、分かりやすいように顔検出したエリアに枠を描写しています。
つまり、枠のみは判別機がガッキーだと判断したエリアで、枠+ガッキーは判別機がガッキーでないと判断したエリアです。
実際の処理結果はこのようになります。
なんということでしょう。
顔の誤検出が多すぎて酷い仕上がりです。
更にいうと、他の人の顔をガッキーに変換するという当初の目的すら達成できていません。
これは、ガッキーの顔かそうでないかという二値分類ではなく、顔っぽいかそうでないかという形で学習をしてしまったからだと考えられます。
冷静に考えれば当たり前ですね。
まとめは雑だが役に立つ
この記事では、ガッキーの顔画像を収集し、分類し、学習し、動画を加工するまでの過程を記載しました。
残念ながら芳しい結果は伴いませんでしたが、問題の原因は明らかなように思われます。
というわけで、その辺りの問題を解消していきたいのですが、だいぶ長くなってしまったので、続きは後編で。
参考は別サイトだが役に立つ
モチベーション
新垣結衣 | アーティスト | レプロエンタテインメント
ご注文はDeep Learningですか? - kivantium活動日記
Keras
Keras Documentation
Are there any codes for AlexNet, ZF Net, GoogLeNet, VGGNet in Keras · Issue #1568 · fchollet/keras · GitHub
Error when checking model target: expected activation_2 to have shape (None, 10) but got array with shape (3, 1) · Issue #3109 · fchollet/keras · GitHub
Kerasでアニメキャラの顔認識 - Qiita
続・深層学習でアニメ顔を分類する with Keras - Qiita
動画
Python + OpenCV で雑コラ動画を作成する③ 雑コラ動画作成 - Qiita
overlay a smaller image on a larger image python OpenCv - Stack Overflow
Gif
brewでインストールしたOpenCV 3をpyenvのvirtualenv環境で使う
brewでOpenCV 3を入れた場合、pyenvで作成したvirtualenv環境では、デフォルトでimport cv2
ができない。
numpyのインストールと、OpenCV 3へのシンボリックリンク作成が必要なようなのでメモ。
環境
手順
まずはOpenCV 3を使いたいpythonのvirtualenv環境を有効化する。
便宜的に、環境作成からの手順を載せる。
$ pyenv virtualenv 3.5.2 opencv-test $ pyenv local opencv-test
環境を有効化したら、pipでnumpyを導入する。
$ pip install numpy
次にbrewでインストールされたOpenCV 3の場所を確認する。
恐らくは下記辺りに落ちているはず。
インストール時のバージョン等によって細部は異なる。
/usr/local/Cellar/opencv3/3.0.0/lib/python3.4/site-packages/cv2.so
自分の場合はHEADからインストールを行ったので、下記のようになった。
/usr/local/Cellar/opencv3/HEAD-300f923_4/lib/python3.5/site-packages/cv2.cpython-35m-darwin.so
ここで、おもむろにPythonの対話型シェルを起動し、virtualenv環境のsite-packages
の場所を調べる。
$ python >>> import site; site.getsitepackages() ['/Users/hoge/.pyenv/versions/opencv-test/lib/python3.5/site-packages']
恐らく上記のような感じで表示されるはず。
上記フォルダに移動し、OpenCV 3へのシンボリックリンクを生成する。
$ cd /Users/hoge/.pyenv/versions/opencv-test/lib/python3.5/site-packages $ ln -s /usr/local/Cellar/opencv3/HEAD-300f923_4/lib/python3.5/site-packages/cv2.cpython-35m-darwin.so ./
最後に、きちんとインポートできるかおよびOpenCVのバージョンを確認する。
virtualenvの環境が有効な箇所に移動し、Pythonの対話型シェルを起動。
$ python >>> import cv2 >>> cv2.__version__ '3.1.0-dev'
これでOpenCV 3が、pyenvのvirtualenv環境で使用できるようになった。
参照
Mac OS X で OpenCV 3 + Python 2/3 の開発環境を整備する方法 – ymyzk’s blog
site-packagesの場所を確認する - プログラムは、用いる言葉の選択で決まる
【macOS Sierra】OpenCV 3をbrewでインストールできない
よくある手順通りに進めていたところインストールでだいぶ躓いたのでメモ。
環境
問題
Python3で使用したかったので、追加のオプションを入れつつインストール命令実行。
$ brew install opencv3 --with-python3
実行の結果出てきたエラーっぽいやつら。
/tmp/opencv3-20161207-28854-41wt3t/opencv-3.1.0/modules/videoio/src/cap_qtkit.mm:46:9: fatal error: 'QTKit/QTKit.h' file not found #import <QTKit/QTKit.h> ^ 1 error generated. make[2]: *** [modules/videoio/CMakeFiles/opencv_videoio.dir/src/cap_qtkit.mm.o] Error 1 make[2]: *** Waiting for unfinished jobs.... make[1]: *** [modules/videoio/CMakeFiles/opencv_videoio.dir/all] Error 2 make: *** [all] Error 2 READ THIS: https://git.io/brew-troubleshooting These open issues may also help: opencv3 missing xz dependency in linuxbrew https://github.com/Homebrew/homebrew-science/issues/2642 opencv3: fix build with vtk https://github.com/Homebrew/homebrew-science/pull/3749 opencv3 python3 wrappers can not be generated https://github.com/Homebrew/homebrew-science/issues/3302 opencv3 misconfigures cmake because brew incorrectly sets $PYTHONPATH https://github.com/Homebrew/homebrew-science/issues/3329 OpenCV and OpenCV3 fail to build with ximea camera support https://github.com/Homebrew/homebrew-science/issues/3395 import cv2 fails and cannot find module once OpenCV3 has been installed via homebrew on Mac OS Sierra with xcode 8.1 https://github.com/Homebrew/homebrew-science/issues/4571 opencv3 PYTHON_PATH and other variables set incorrectly in some cases https://github.com/Homebrew/homebrew-science/issues/2846 opencv3 build fails without clear error message on OSX 10.10.5 https://github.com/Homebrew/homebrew-science/issues/3119
エラーメッセージで検索した結果出てきたissueを参考に、オプション追加。
$ brew install --HEAD opencv3 --with-python3
無事成功。
インストール後のbrew info
の結果。
$ brew info opencv3 homebrew/science/opencv3: stable 3.1.0, HEAD [keg-only] Open source computer vision library, version 3 http://opencv.org/ /usr/local/Cellar/opencv3/HEAD-300f923_4 (286 files, 48.8M) Built from source on 2016-12-07 at 18:09:15 with: --with-python3
恐らく最新版では修正済みの期間限定エラーだったのだと思われる。
やめて頂きたい。
参照
Updated PR #7159 (OSX AVFoundation support) by alalek · Pull Request #7266 · opencv/opencv · GitHub
【Django REST framework】viewの中で使用するserializerを変更する
ModelViewSetなんかを使用していると、それぞれのアクション毎にserializerを変更したいことが多々あると思います。
日本語情報が少なくてベストプラクティスなのかどうかは疑わしいですが、クラスベースviewの中でserializerを変更する方法をとりあえずメモしておきます。
環境
Python 3.5.2
Django 1.9.7
djangorestframework 3.4.0
方法
get_serializer_class
メソッドをオーバーライドし、アクション毎に異なるserializerを返却するようにします。
以下にlist
とretrieve
で返却するフィールドを変えたserializers.py
とviews.py
の簡単なコード例を示します。
serializers.py
class HogeSerializer(serializers.ModelSerializer): class Meta: model = Hoge fields = ('id', 'name') class HogeRetrieveSerializer(serializers.ModelSerializer): class Meta: model = Hoge fields = '__all__'
views.py
from .serializers import HogeSerializer, HogeRetrieveSerializer class HogeView(viewsets.ModelViewSet): queryset = Hoge.objects.all() def get_serializer_class(self): if self.action == 'retrieve': return HogeRetrieveSerializer return HogeSerializer
上記の例ですと、list
の時はidとnameカラムのみが返却され、retrieve
の時には、モデルの全てのフィールドが返却されます。
このように、get_serializer_class
メソッドをオーバーライドすることで、アクションに応じたserializerを設定することができます。
今回はlist
とretrieve
の例でしたが、他のアクションも同様にして、serializerを分けることが可能です。
参考
Generic views - Django REST framework
Django rest framework, use different serializers in the same ModelViewSet-open source projects tomchristie/django-rest-framework
Google グループ
【MySQL】一時的に外部キー制約を無効にする
下記のコマンドで無効化し、
SET FOREIGN_KEY_CHECKS=0;
必要な処理が終了したら、
SET FOREIGN_KEY_CHECKS=1;
これで元に戻す。
【MySQL】外部キー制約を削除する
制約の設定をしたものの、テストデータの投入段階でinsertを阻止してくる邪魔なforeign keyに悩まされたので、一時的に参照整合性の制約を削除します。
環境
MySQL 5.6
手順
まずは、テーブルに設定されている参照制約を確認しましょう。
mysql> SHOW CREATE TABLE something\G *************************** 1. row *************************** Table: something Create Table: CREATE TABLE `something` ( `id` int(11) NOT NULL AUTO_INCREMENT, `another_id` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `somedb_something_a08cee2d` (`another_id`), CONSTRAINT `somedb_another_id_3a4999a1_fk_somedb_another_id` FOREIGN KEY (`another_id`) REFERENCES `another_table` (`id`) ) ENGINE=InnoDB CHARSET=utf8mb4 1 row in set (0.00 sec)
上記の結果を見ると、something
テーブルの外部キーanother_id
が、another_table
のid
に外部キーとして制約が設定されています。
これを削除するには、CONSTRAINT
の次に書いてある文字列を指定して下記のコマンドを実行します。
mysql> ALTER TABLE something DROP FOREIGN KEY `somedb_another_id_3a4999a1_fk_somedb_another_id`;
もう一度SHOW CREATE TABLE
を実行すると、外部キー制約の項がなくなっていることが確認できます。