エイバースの中の人

アプリとWEBサービスを作っているABARSのBLOGです。

Android7以降でSocialConnectorが動作しない

SocialConnectorでは以下のようにuri.fromFileでファイル共有を行います。

var uri = new AndroidJavaClass ("android.net.Uri");
var file = new AndroidJavaObject ("java.io.File", textureUrl);
intent.Call<AndroidJavaObject> ("putExtra", "android.intent.extra.STREAM", uri.CallStatic<AndroidJavaObject> ("fromFile", file));


しかし、Android7から権限管理が強化されたため、画像共有時にAndroidJavaException: android.os.FileUriExposedExceptionが発生します。この問題を解決するには、FileProviderを使用する必要があります。

int FLAG_GRANT_READ_URI_PERMISSION = intent.GetStatic<int>("FLAG_GRANT_READ_URI_PERMISSION");
intent.Call<AndroidJavaObject>("addFlags", FLAG_GRANT_READ_URI_PERMISSION);

AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject unityContext = currentActivity.Call<AndroidJavaObject>("getApplicationContext");

string packageName = unityContext.Call<string>("getPackageName");
string authority = packageName + ".fileprovider";

AndroidJavaObject fileObj = new AndroidJavaObject("java.io.File", textureUrl);
AndroidJavaClass fileProvider = new AndroidJavaClass("android.support.v4.content.FileProvider");
AndroidJavaObject uri = fileProvider.CallStatic<AndroidJavaObject>("getUriForFile", unityContext, authority, fileObj);

intent.Call<AndroidJavaObject>("putExtra", "android.intent.extra.STREAM", uri);


AndroidManifestに権限を追加します。

 <!-- Add fileprovider for android n -->
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="バンドルID.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths"></meta-data>
</provider>


Plugins/Android/res/xmlにfile_paths.xmlを作成し、以下のように共有フォルダ指定をします。

<?xml version="1.0" encoding="utf-8"?>
<paths>
      <external-cache-path name="external_files" path="."/>
      <external-path name="external_files" path="."/>
</paths>


しかし、UnityとAndroid SDK 26以降を合わせて使うと、Plugins/Android/resフォルダが存在するとREAD_PHONE_STATEパーミッションが追加されます。(READ_PHONE_STATE permission added when using SDK tools 26.0.2)Unity5.5.4f1からUnity5.5.5p2に上げても回避はできませんでした。

尚、ビルド済みのapkからManifestを抽出するにはapktoolを使用します。

apktool d input.apk

Kerasでアニメキャラ識別器を作る

すでに先行事例が多いですが、Kerasでアニメキャラ識別器を作りました。ソースコードはgithubに上げてあります。
abars/AnimeFaceClassifier

result

Hatsune Miku / Crypton Future Media inc. / CC BY-NC

OpenCVでアニメキャラの顔を検出後、CNNにかけて、キャラを当てます。データセットはanimeface-character-datasetを使用させて頂きました。

32x32に縮小した画像を入力して2層の畳み込みを行うsmall_cnnで学習をかけた場合、認識精度は70%程度です。学習はMac Pro 2013のFireProD300で2日程度かかりました。

認識精度を上げるため、VGG16を使用したファインチューニングを実験し、テストデータセットでの認識精度は82%まで上がりました。ただ、small_cnnとは異なり、初音ミクとランカ・リーを間違えることがあります。InceptionV3なども試した方がよいかもしれません。

Yoloで顔検出を行う

YOLO-version-2-Face-detectionにあるcfgファイルとweightファイルを使用すると、Yolo v1を使用して顔検出を行うことができます。

実行コマンド
./darknet yolo test yolo-face.cfg yolo-face_final.weights data/person.jpg

公式デモ


学習にはFDDB使用しているようです。顔画像のデータセットは、WiderFaceDatasetも有名ですね。

このネットはAnchorが含まれていないYolo v1の形式になっています。darknet2caffeを使用すれば、caffemodelに変換可能です。変換後のCaffeModelは178.1MBです。

python darknet2caffe.py yolo-face.cfg yolo-face_final.weights face.prototxt face.caffemodel

変換後の係数は、caffe-yoloで動作します。その際、num_class=1、grid_size=11に対応するよう、yolo_main.pyを書き換える必要があります。

python yolo_main.py -m face.prototxt -w face.caffemodel -i faces.jpg

初音ミクの顔も検出できたので、意外とアニメ顔もいけるかもしれません。

OpenCVのFace DetectionはFace Detection using Haar Cascadesというアルゴリズムを使用していますが、Yoloが動く環境であれば、OpenCVを使用しなくてもより手軽に顔検出を行うことができそうです。



また、現在、再学習をYoloKerasFaceDetectionで実験中です。

イーサリアムゲーム CryptoKittiesの流れ

CryptoKittiesは仮想通貨の一つであるイーサリアムでネコを育てるゲームです。PCブラウザのみ対応しています。

イーサリアムはビットコインとは異なり、スマートコントラクトによる付加データの保存に対応しています。そのため、ゲームのキャラクターなどのデータも保持することができます。

CryptoKittiesでサインアップします。イーサリアムはChrome PluginのMeta Maskを経由して使用するため、Meta Maskのインストールが必要です。

ログインすると、最初のネコの購入を促されます。

top


ネコにはチャートがあります。

buy


購入を選ぶとMeta Maskから支払いを求められます。

metamask


0.06ETHだと、5000円近く必要なようです。高い。

ということでゲームの開始を諦めました。仮想通貨の高騰前に遊びたかった。

Macでkerasを使う

インストール

KerasとTensorflowのインストールを行います。

pip install keras
pip install tensorflow

OpenCLバックエンド用にPlaidMLを入れます。

pip install plaidml-keras
plaidml-setup 

サンプルの実行

MNISTのサンプルコードをダウンロードします。
keras/examples/mnist_cnn.py

import kerasの前にバックエンドを選択します。

import plaidml.keras
plaidml.keras.install_backend()

実行します。

python mnist_cnn.py

学習が行われます。

python mnist_cnn.py 
x_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
INFO:plaidml:Opening device "amd_radeon_hd_-_firepro_d300_compute_engine.1"
Train on 60000 samples, validate on 10000 samples
Epoch 1/12
INFO:plaidml:Analyzing Ops: 85 of 285 operations complete
59904/60000 [============================>.] - ETA: 0s - loss: 0.3243 - acc: 0.9024INFO:plaidml:Analyzing Ops: 85 of 285 operations complete
60000/60000 [==============================] - 43s - loss: 0.3241 - acc: 0.9025 - val_loss: 0.0758 - val_acc: 0.9751
Epoch 2/12
60000/60000 [==============================] - 34s - loss: 0.1109 - acc: 0.9673 - val_loss: 0.0546 - val_acc: 0.9831
Epoch 3/12
60000/60000 [==============================] - 34s - loss: 0.0847 - acc: 0.9753 - val_loss: 0.0444 - val_acc: 0.9847
Epoch 4/12
60000/60000 [==============================] - 34s - loss: 0.0723 - acc: 0.9789 - val_loss: 0.0377 - val_acc: 0.9873
Epoch 5/12
60000/60000 [==============================] - 34s - loss: 0.0622 - acc: 0.9813 - val_loss: 0.0350 - val_acc: 0.9882
Epoch 6/12
60000/60000 [==============================] - 34s - loss: 0.0563 - acc: 0.9839 - val_loss: 0.0326 - val_acc: 0.9890
Epoch 7/12
60000/60000 [==============================] - 34s - loss: 0.0525 - acc: 0.9839 - val_loss: 0.0314 - val_acc: 0.9888
Epoch 8/12
60000/60000 [==============================] - 34s - loss: 0.0474 - acc: 0.9857 - val_loss: 0.0302 - val_acc: 0.9892
Epoch 9/12
60000/60000 [==============================] - 34s - loss: 0.0446 - acc: 0.9868 - val_loss: 0.0312 - val_acc: 0.9896
Epoch 10/12
60000/60000 [==============================] - 34s - loss: 0.0418 - acc: 0.9878 - val_loss: 0.0304 - val_acc: 0.9897
Epoch 11/12
60000/60000 [==============================] - 34s - loss: 0.0379 - acc: 0.9886 - val_loss: 0.0294 - val_acc: 0.9906
Epoch 12/12
60000/60000 [==============================] - 34s - loss: 0.0377 - acc: 0.9890 - val_loss: 0.0295 - val_acc: 0.9901
Test loss: 0.0294891050935
Test accuracy: 0.9901

重み保存と変換

重み保存と読み込みをするには、以下のコマンドを使用します。
Keras FAQ: Kerasに関するよくある質問

json_string = model.to_json()
model.save_weights('my_model_weights.h5')

from keras.models import model_from_json
model = model_from_json(json_string)
model.load_weights('my_model_weights.h5')

重み変換をするには、以下のスクリプトを使用します。
keras2caffe (MIT license)
import keras2caffe
keras2caffe.convert(model,"my_model.prototxt","my_model.caffemodel")

尚、重み変換をする場合はInputLayerが存在しないとエラーになります。また、activationは分離して記載する必要があります。KerasのSequentialモデルでInputLayerを明示的に追加するを参考に以下のようにInputLayerの追加と、activationの分離を行います。

model = Sequential()
model.add(InputLayer(input_shape=input_shape))
model.add(Conv2D(32, kernel_size=(3, 3)))
model.add(Activation('relu'))
model.add(Conv2D(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(128))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))

Fine Tuning

少ない画像から画像分類を学習させる方法(kerasで転移学習:fine tuning)
Kerasではtrainフォルダとvalidationフォルダにカテゴリ別のフォルダを作成し、画像を入れておくと、カテゴリ識別を学習させることができます。例えば、AnimeFace Character Datasetのthumbを適当なスクリプトでtrainとvalidationに分解します。また、その他のカテゴリを学習させるには、Bing Search APIが教師データ作成に便利です。

Yolo

basic-yolo-kerasを使用してYoloの係数を学習することができます。実際の学習を走らせる時はconfig.jsonのwarmup_epochsを0にします。ただし、plaidmlのバックエンドだと動作しないため、実用的な速度で動かすにはCUDAが必要です。

python train.py -c config.json
python predict.py -c config.json -w full_yolo_raccoon.h5 -i raccoon_dataset/images/raccoon-1.jpg

モナコインをマイニングしてみる

モナコインは2013年12月に誕生した日本初の暗号通貨です。

monacoin

仮想通貨は最初に正解を当てたクライアントのみが報酬を得るため、ソロマイニングをすると報酬が安定しません。そのため、複数のユーザでギルドのようなものを組み、報酬を分配することで、安定した報酬の獲得を目指します。

以下の手順でモナコインをマイニング可能です。

(1) Monappyでモナコインのウォレットを作成
(2) マイニングプール(ASICpoolやVIPpool等)のアカウントを作成
(3) cpuminerのインストール
(4) マイニングの実行

cpuminerは以下のリポジトリからCloneしてMakeします。lyra2rev2に対応している必要があるため、pooler/cpuminetではなく、tpruvot/cpuminer-multiが必要です。
https://github.com/tpruvot/cpuminer-multi

MacOSでビルドする場合、READMEに記載のCFLAGS="*-march=native*"を付けるとconfigureでgccが見つからないエラーが出るため、外します。シンボリックリンクを作成してOpenSSLにパスを通します。_neoscrypt_blkcpyでリンクエラーが発生するため、fix os x build with asm #14をマージします。

export PATH=/usr/local/Cellar/openssl/1.0.2m/bin:$PATH
cd /usr/local/include 
ln -s ../opt/openssl/include/openssl .


cpuminerは以下のように起動することができます。Webloginはユーザ名、WorkerNameはワーカー名、WorkerPasswordはワーカーパスワードが対応します。Stratumはマイニングのプロトコルです。lyra2rev2はハッシュアルゴリズムです。

./cpuminer -a lyra2rev2 -o stratum+tcp://stratum.asicpool.info:6969 -u Weblogin.WorkerName -p WorkerPassword


昔のモナコインはLiteCoinと同じscryptを使用していましたが、現在はハードフォークされており、lyra2rev2になっているため、-a scripyでは採掘できません。lyra2REv2は多くのメモリを必要とする構造になっているため、単純なSHA2を使用するビットコインに比べて、ASIC化が難しいと言われています。

モナコインの情報は、Ask Monaから取得可能です。マイニングプールはオープンソースのphp-mposを使用していることが多いようです。

[2017-12-09 20:53:28] Starting Stratum on stratum+tcp://stratum.asicpool.info:6969
[2017-12-09 20:53:28] 8 miner threads started, using 'lyra2rev2' algorithm.
[2017-12-09 20:53:32] Stratum difficulty set to 1 (0.00391)
[2017-12-09 20:53:32] lyra2rev2 block 1184360, diff 88512.700
[2017-12-09 20:53:33] CPU #0: 77.87 kH/s
[2017-12-09 20:53:33] CPU #5: 77.54 kH/s
[2017-12-09 20:53:33] CPU #7: 77.33 kH/s
[2017-12-09 20:53:33] CPU #2: 77.10 kH/s
[2017-12-09 20:53:33] CPU #1: 77.11 kH/s
[2017-12-09 20:53:33] CPU #6: 77.09 kH/s
[2017-12-09 20:53:33] CPU #3: 76.90 kH/s
[2017-12-09 20:53:33] CPU #4: 76.18 kH/s
[2017-12-09 20:53:36] accepted: 1/1 (diff 0.013), 616.59 kH/s yes!
[2017-12-09 20:53:42] CPU #1: 75.16 kH/s
[2017-12-09 20:53:42] accepted: 2/2 (diff 0.004), 615.18 kH/s yes!
[2017-12-09 20:53:53] CPU #1: 74.08 kH/s
[2017-12-09 20:53:53] accepted: 3/3 (diff 0.016), 614.10 kH/s yes!

yesが出れば掘れています。

2017年に買って良かったもの

今年もいろいろ買いました。今年はデバイス系の当たり年だった気がします。

Logicool MX Master 2S + KX1000s


マルチペアリング対応のマウスとキーボードです。複数のPCで共用できるため、デスクトップがすっきりします。Flowは動いたり動かなかったり微妙なところがあるのですが、キーボードとマウスのボタンでも切り替えられるので、Flowが使えなくても満足度は高いです。マウスについては、マラソンマウスからの乗り換えですが、ポインターの滑らかさと、スクロールホイールのメカニカルなスクロールモードの切り替えがとてもよく、会社用と家用の二台を買ってしまいました。キーボードはAppleのKeyboardからの乗り換えですが、Magic keyboard的な軽さと剛健さがあり、こちらも質感高くてよい感じです。WindowsとMacの両方の特殊キーに対応しており、なおかつ右上にある離席用のロックボタンも便利です。



iPhone8 / iPhoneX


ついにiPhoneがQiに対応しました。Xを待ちきれずに8も買ってしまいました。ローズゴールド風のゴールドの質感はとてもよかったです。XはXで重めですが、高級感があります。どちらも満足度の高い端末だと思います。



GalaxyS8


近年で最高のAndroid端末だと思います。Qiにも対応。GearVRの発熱問題がついに解消しました。



LG Ultrafine 5k display


MacPro 2013にも繋がる5k displayです。ソリッドでシンプルな形状と、Apple公式ならではのDellの4kよりも安定した表示が魅力です。なにげに、NUCなど、Thunderbolt3を搭載したWindows PCにも接続ができます。



GPD Pocket


打ち合わせ用です。Unityを動かすには流石に重すぎましたが、ブラウジングには十分です。481gでWindwos10が動くのはすごいです。



Gigabyte AorusゲームボックスGTX 1070


ディープラーニング用に購入。Thunderbolt経由でGPUが使えます。かなりコンパクトで静かなのに、CPUの240倍近い速度で学習できて便利です。現在はNUC + Windows10で使用していますが、将来的にはMac Book Pro + High Sierraにも接続してみたいです。



Kindle Oasis


物理的なページめくりボタンは便利です。Kindle Voyageの感圧ボタンよりも快適になりました。Kindle Voyageと同等の重量とサイズで、ディスプレイが大型化しており、コミックがより快適に読めるようになりました。



Bose Quiet Control 30


AirPodsではなくこちらを買いました。ノイズキャンセリングと、バッテリーの持ち、使っていない時に投げ出してもよい気軽さがよいです。飛行機などにも気軽に持っていける携帯性があります。



ニーアオートマタ


キャラデザ、世界感、音楽、全てが神ゲーでした。



銀河英雄伝説


言わずと知れた名作。いつも続刊を楽しみに待っています。

Windows10にDarknetを入れる

Gigabyte Aorusゲームボックスを使うため、Windows10にDarknetを入れたのでメモです。PCはNUCです。



Thunderbolt Driverのインストール


NUCのサイトからtbt_win10_64_17.2.71.250.zipをダウンロードしてインストール。

VisualStudio 2015のインストール


MSDNからVisual Studio 2015をダウンロード。2017のダウンロードページに飛ばされるので、最下部の以前のバージョンからダウンロード。(以前のバージョンをお使いになりたいですか?

OpenCVのインストール


opencv/opencvからopencv-3.1.0.exeを展開。opencv/build/vc14/binにパスを通す。

CUDAのインストール


CUDA Toolkit Downloadからインストール。バージョンは9.0ではなく8.0が必要。cuDNNをダウンロード。登録が必要。cuda/binにパスを通す。尚、後述するようにDarknetのcuDNN対応がまだ微妙なため、Darknetだけを使用する場合はCUDA Toolkitだけをインストールするのでもよい。Tensorflowを使用する場合は必要。

Darknetのインストール


AlexeyAB/darknetをClone。build/darknet/darknet.slnを開き、opencv/build/vc14/libとopencv/build/includeとcuda/includeとcuda/libにパスを通す。CUDNNを有効にすると正しく認識が動作しないため、CUDNNのdefineを外し、GPUのみdefineを残す。ビルドするとx64フォルダにdarknet.exeができる。

パフォーマンス


WindowsのパフォーマンスモニタではCUDAのGPU稼働率を見ることはできないが、GPUが熱を持っているので稼働はわかる。TinyYoloで100Epocが、CPU(Xeon)だと12時間、GPU(1070)だと3分。240倍の性能。

Anacondaのインストール


Anacondaの公式サイトから3.6のバージョンを入手。Python 3.5の環境に変更。

conda install python=3.5
conda install pip


Kerasのインストール


以下のコマンドでKerasとTensorflowをインストール。

pip install keras
pip install tensorflow-gpu


Pytorchのインストール


以下のコマンドでtorchをインストール。

conda install -c peterjc123 pytorch 


Caffeのインストール


BVLC/caffeからビルド済みバイナリのVisual Studio 2015, CPU only, Python 3.5: Caffe Releaseをダウンロード。Pythonから使えるようにするためにcaffe/python/caffeをc:/users/user_bane/Anaconda/Python3.5/Lib/site-packagesにコピー。import caffeでDLL load failedが発生するため、caffe-windowsからthirdparty20170624.rarをダウンロードして./windows/thirdparty/binsにパスを通す。

Darknetを使用してYoloの係数を学習する

Darknetの概要


DarknetはYoloの開発者が開発しているディープラーニングのフレームワークです。C++で記述されており、簡単にビルドすることができます。Yoloの歴史についてはYOLO9000: Better, Faster, Strongerの資料が詳しいです。

Darknetを使用してYoloの係数を学習するチュートリアルは以下が詳しいです。
How to train YOLOv2 to detect custom objects

Darknetのインストール


環境構築はMacかUbuntuが簡単です。まず、リポジトリをCloneします。

git clone https://github.com/AlexeyAB/darknet.git

GNUmakeを使用してmakeします。darknetのバイナリが生成されます。

make

nVidiaのGPUであればMakefileを書き換えることでCUDAを有効にすることができます。今回はAMDのGPUを搭載したMacなため、CPUで動かします。

学習させるデータの準備


学習させるデータをダウンロードします。上記記事のサンプルデータがテストには最適です。
https://timebutt.github.io/content/other/NFPA_dataset.zip

ただし、pos-234.jpgの拡張子が大文字のJPGになっているため、小文字のjpgに変換する必要があります。また、train.txtとtest.txtの改行コードをWindows StyleからLinux Styleに変換する必要があります。

データセットには、入力画像のjpgと、バウンディングボックスのtxtが含まれています。バウンディングボックスのtxtのフォーマットは以下のようになります。画像のどの位置にどのカテゴリのオブジェクトが存在するかが記載されています。同様の形式で、jpgとtxtのペアを準備することで、任意の画像で学習させることができます。

[category number] [object center in X] [object center in Y] [object width in X] [object width in Y]

YoloV2での学習


次に、obj.data、obj.names、yolo-obj.cfgを準備します。

obj.dataには学習のための基本情報が含まれます。上から、識別クラス数、入力画像のファイルリスト、評価画像のファイルリスト、クラス名称、係数の出力ディレクトリです。trainやvalidはdarknetのバイナリからの相対パスです。

classes=1
train=data/nfpa/train.txt
valid=data/nfpa/test.txt
names=data/nfpa/obj.names
backup=backup/

obj.namesにはカテゴリ名称が含まれます。

NFPA

yolo-obj.cfgにはYoloのネットワーク構成が含まれます。yolo-voc.cfgをコピーして、batchを64に、subdivisionsを8に、classesを識別させたいクラス数に書き換えます。最終段のfiltersを(classes + 5)*5に書き換えます。

[net]
# Testing
batch=64 #書き換える
subdivisions=8 #書き換える
# Training
# batch=64
# subdivisions=8
height=416
width=416
channels=3

省略...

[convolutional]
size=1
stride=1
pad=1
filters=30 #書き換える
activation=linear

[region]
anchors =  1.3221, 1.73145, 3.19275, 4.00944, 5.05587, 8.09892, 9.47112, 4.84053, 11.2364, 10.0071
bias_match=1
classes=1 #書き換える
coords=4
num=5
softmax=1
jitter=.3
rescore=1

省略...

収束を速めるため、初期係数列をダウンロードしておきます。
darknet19_448.conv.23

最後に、以下のコマンドで学習を行います。

./darknet detector train data/nfpa/obj.data data/nfpa/yolo-obj.cfg darknet19_448.conv.23

学習にはハイエンドのGPUで1時間かかるということで、CPUだけだと厳しい感はあります。
学習結果は以下のコマンドで確認します。

./darknet detector test data/nfpa/obj.data data/nfpa/yolo-obj.cfg yolo-obj1000.weights testimage.jpg

学習済みのTiny-Yoloを動かしてみるには、ofxDarknetからtiny-yolo-voc.weightsをダウンロードした後、以下のコマンドで確認します。

./darknet detector test cfg/voc.data cfg/tiny-yolo-voc.cfg tiny-yolo-voc.weights data/person.jpg

上記ページのCOCO版のtiny-yolo.weightは学習時のcfgが異なるためか、正しく動作しないため、digitalbrain79/pyyoloのウエイトファイルを使用します。

./darknet detector test cfg/coco.data cfg/tiny-yolo.cfg tiny-yolo.weights data/person.jpg

YoloV1での学習


係数をCaffemodelに変換するには、pytorch-caffe-darknet-convertを使用します。変換にはCaffeのInstallが必要です。

python darknet2caffe.py tiny-yolo.cfg tiny-yolo.weights out.prototxt out.caffemodel

ただし、YoloV2にはCaffeではサポートされていないRegion Layerが含まれています。そのため、Caffeで動かす場合は、cfg/yolov1を使用する必要があります。Tiny-Cocoのweightsはyolov1のページからダウンロードできます。

動作テスト
./darknet coco test cfg/yolov1/tiny-coco.cfg tiny-coco.weights data/giraffe.jpg

Caffeモデルへの変換
python darknet2caffe.py tiny-coco.cfg tiny-coco.weights out.prototxt out.caffemodel

Yolov1で学習するには以下のコマンドを使用します。使用する画像の設定はsrc/yolo.cで行う必要があります。(You Only Look Once:Unified, 統合されたリアルタイムオブジェクトの検出

char *train_images = "/home/pjreddie/data/voc/test/train.txt";
char *backup_directory = "/home/pjreddie/backup/";

cfgのclassesとconnectedのoutputを書き換えます。outputはcfgのside*side*((coords+1)*n+classes)です。nfpaの1カテゴリでtiny-yoloを使用する場合は539になります。

VOCの学習
./darknet yolo train cfg/yolov1/yolo.train.cfg

yolo.cfgとyolo.train.cfgの違いはbatchとsubdivisionsだけです。

nfpaの学習
./darknet yolo train cfg/yolov1/tiny-yolo-nfpa.cfg

学習はMac Pro 2013だとシングルスレッドで24時間で200 Epoc、OpenMPで12時間で1400Epocでした。GTX1070を使えば、1時間で2000Epoc回すことができました。Darknetで2クラスの物体をトレーニングによると8000Epoc近く回す必要があるみたいなので、CPUで8000Epoc回すには3日程度必要です。GPUだと4時間程度で終わります。

クラス数1の学習結果のテストを行うにはyolo.cのdraw_detectionsの最後の引数の20をl.classesに書き換える必要があります。

学習結果のテスト
./darknet yolo test cfg/yolov1/tiny-yolo-nfpa.cfg backup/tiny-yolo-nfpa_200.weights data/nfpa/pos-1.jpg

500Epoc
500
1000Epoc
1000
2000Epoc
2000

2000 Epocまでいけばわりと認識できるようです。

(補足)Caffeのインストール


darknet2caffe.pyの実行にはCaffeが必要です。CaffeのインストールにはMakefile.configに以下の設定が必要でした。PYTHON_LIBのパスが合ってないと、import CaffeでSegmentation Faultが起きるようです。リンカフラグへのlibstdc++の設定は不要でした。

CPU_ONLY := 1
OPENCV_VERSION := 3
WITH_PYTHON_LAYER := 1
PYTHON_LIB :=  /usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/
INCLUDE_DIRS += /usr/local/Cellar/boost@1.59/1.59.0/include /usr/local/Cellar/boost-python@1.59/1.59.0/include
LIBRARY_DIRS += /usr/local/Cellar/boost@1.59/1.59.0/lib /usr/local/Cellar/boost-python@1.59/1.59.0/lib
INCLUDE_DIRS += /usr/local/lib/python2.7/site-packages/numpy/core/include

(補足)Darknetのマルチスレッド化


Darknetはデフォルトでは1スレッドで動作します。CPUでマルチスレッド化するには、OpenMPを有効化します。XcodeのデフォルトではOpenMPが使用できないため、最新のLLVMをインストールします。
brew install llvm

Makefileを書き換えます。
OPENMP=1
CC=/usr/local/opt/llvm/bin/clang
CPP=/usr/local/opt/llvm/bin/clang++
LDFLAGS+= -L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib

UnityアプリのiPhoneX対応

UnityアプリはXcode9でビルドした場合は互換モードでは動作せず、全画面モードで動作します。Unity 5.5など、古いバージョンのUnityでビルドした場合でも同様です。そのため、縦画面想定で、Canvas Scalerでmatch=1.0などと設定していると、UIの左右が見切れることになります。

この問題を解決するには、画面のアスペクト比を見て、match=0.0に設定する必要があります。具体的に、以下のようなスクリプトを全てのCanvas Scalerを含むGameObjectにアタッチします。尚、Canvas Scalerの初期化よりも前に走らせるために、Project SettingのScript Execution OrderでCanvasScreenAutoFixの実行順を早くしておきます。

//iPhoneXの縦長画面に対応
//Canvasにアタッチ

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class CanvasScreenAutoFix : MonoBehaviour
{
	private void Awake()
	{
		if(1.0f*Screen.width/Screen.height<9/16.0f){
			GetComponent<CanvasScaler>().matchWidthOrHeight=0.0f;
		}else{
			GetComponent<CanvasScaler>().matchWidthOrHeight=1.0f;
		}
	}
}

スクリプトのアタッチを手動で行うと大変なので、Editor Scriptで自動化します。

//iPhoneX対応のため、全てのCanvasにCanvasScreenAutoFixをアタッチ

using UnityEngine;
using UnityEditor;
using UnityEngine.Networking;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;

public class iPhoneXSupport : MonoBehaviour {
    public static List<T> FindObjectsOfTypeAll<T>()
    {
        List<T> results = new List<T>();
        var s = SceneManager.GetActiveScene();
        if (s.isLoaded)
        {
            var allGameObjects = s.GetRootGameObjects();
            for (int j = 0; j < allGameObjects.Length; j++)
            {
                var go = allGameObjects[j];
                results.AddRange(go.GetComponentsInChildren<T>(true));
            }
        }
        return results;
    }

    [MenuItem ("iPhoneX/Add canvas scaler")]
    static void ChangeToProduction() {
        GameObject[] allObjects = (GameObject[])FindObjectsOfTypeAll( typeof(GameObject) );
        foreach ( GameObject obj in allObjects ){
            if(obj.GetComponent<Canvas>()!=null){
                obj.AddComponent<CanvasScreenAutoFix>();
                Debug.Log(obj.name);
                EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
            }
        }
    }
}

UIは互換表示のように黒帯を表示し、メインゲームは全画面で動作させる場合、UIのRectTransformのAnchorがCenter以外だとレイアウトが崩れる場合があるため、手動で全てCenterに書き換えます。

また、カメラの画角も変わり、大きく表示されるため、補正します。

//iPhoneXの縦長画面に対応
//Cameraにアタッチ

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraScreenAutoFix : MonoBehaviour {

	// Use this for initialization
	void Awake () {
		float initial_field_of_view=GetComponent<Camera>().fieldOfView;
		float ratio=(9.0f/16.0f)/(1.0f*Screen.width/Screen.height);
		if(ratio<1.0f){
			ratio=1.0f;
		}
		GetComponent<Camera>().fieldOfView=initial_field_of_view*ratio;
	}
}

中央寄せではなく、上寄せしているAnchorを持つUIに対しては、セーフエリアの44pxを加算する必要があるため、以下のスクリプトを当てます。

//上寄せのUIのセーフエリアを適用

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SafeArea : MonoBehaviour {
	private void Awake()
	{
		if(1.0f*Screen.width/Screen.height<9/19.0f){
			Transform target=transform;
			while(target!=null){
				if(target.GetComponent<CanvasScaler>()!=null){
					break;
				}
				target=target.parent;
			}

			CanvasScaler canvas_scaler=target.GetComponent<CanvasScaler>();
			float safe_area_y=44.0f;
			safe_area_y=safe_area_y*canvas_scaler.referenceResolution.y/Screen.height;
			Vector3 pos=GetComponent<RectTransform>().anchoredPosition;
			pos.y=pos.y-safe_area_y;
			GetComponent<RectTransform>().anchoredPosition=pos;
		}
	}
}

新規開発のアプリからは、20:9でUIの背景を作成すると共に、端寄せでUIを配置する必要があるかなと思います。

iOSアプリのiPhoneX対応

iPhoneXのノッチに対応するため、StoryboardにSafe Areaが追加されました。従来は、Viewを基準にConstraintsを指定していましたが、Xcode9以降はSafe Areaを基準にConstraintsを指定することになります。

Safe Areaを使用するには、Safe Area Relative Marginsのチェックボックスを有効にします。その後、ConstraintsのAlign Top to:をViewからSafe Areaに変更します。

storyboard

また、Xcode8までは、ステータスバーの背景色を設定することができないため、ダークモードでは以下のように背景色となるビューを追加していました。

var h : CGFloat=20.0
dark_view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: w, height: h))
dark_view!.backgroundColor=bg_color
self.window!.rootViewController!.view.addSubview(dark_view!)

しかし、iPhoneXではステータスバーの高さが44pxに上がっているため、以下のようにサイズを調整する必要があります。

var h : CGFloat=20.0
if #available(iOS 11.0, *) {
    if(UIApplication.shared.windows[0].safeAreaInsets != UIEdgeInsets.zero){
        h=44.0
    }
}

ただ、実際にやってみると、ポートレートモードでは問題ないのですが、iPhoneXのランドスケープモードではステータスバーが表示されなくなったため、追加したサブビューがUIの上に被ってしまいます。そのため、サブビューを追加する方法ではなく、ステータスバーの背景色を直接、変えるようにした方がよいようです。(参考:Changing the Status Bar Color for specific ViewControllers using Swift in iOS8

extension UIApplication {
    class var statusBarBackgroundColor: UIColor? {
        get {
            return (shared.value(forKey: "statusBar") as? UIView)?.backgroundColor
        } set {
            (shared.value(forKey: "statusBar") as? UIView)?.backgroundColor = newValue
        }
    }
}

UIApplication.statusBarBackgroundColor = bg_color;

また、ランドスケープモード時には左右のunsafeエリアが白で塗りつぶされます。この色を変えるには、viewDidLoadでview.backgroundColorに色を設定します。(参考:Changing the background color of the view, Swift

override func viewDidLoad() {
    super.viewDidLoad()
    if(dark_mode){
        let bg_color:UIColor=UIColor(red: r/255, green: r/255, blue: r/255, alpha: 1.0)
        view.backgroundColor = bg_color
    }
}

Xcodeでdouble freeを検出する

XcodeのProduct -> SchemeからAddress Sanitizerにチェックを入れると、mallocで確保したメモリのdouble freeを検出することができます。スタック破損チェックなどに便利です。

sanitizer

Swift3のNSAttributedStringでunrecognized selector例外が起きる

Swift2からコンバートしたSwift3のNSAttributedStringでunrecognized selector例外が発生します。

let attributedOptions : [String: AnyObject] = [
    NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType as AnyObject,
    NSCharacterEncodingDocumentAttribute: String.Encoding.utf8 as AnyObject
]
attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)


String.Encoding.utf8だとダメで、String.Encoding.utf8.rawValueにする必要があるようです。

let attributedOptions : [String: AnyObject] = [
    NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType as AnyObject,
    NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue as AnyObject
]
attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)


参考:Swift-3 error: '-[_SwiftValue unsignedIntegerValue]: unrecognized selector

Android NDK r8でビルドしたバイナリがAndroid6で動作しない

Android NDK r8でビルドした.soが、Android6でDllNotFoundExceptionになることがあります。logcatを見てみると、hoge.so: has text relocations、というエラーが発生しています。

調査したところ、Android 6.0 Marshmallow (API23)はテキスト再配置をサポートしておらず、この機能を持つライブラリは実行できないようです。

暫定対処としては、targetSDKVersionを23ではなく、22にするとよいようです。

抜本的には、Android NDK r10eでのリビルドを推奨なようです。

第28回IGポート株主総会レポート(2017/08/29)

場所は武蔵野劇場でした。100人程度の参加人数。最初に招集通知の説明、その後に来期の説明。来期は亜人の実写版、魔法陣グルグルなど。魔法使いの嫁は権利窓口として、グループシナジーの最大化を図る。

IMG_2414


以下、質疑応答。

ネットフリックスの制作費が10倍ということで話題だが、ネットフリックスは29期の売上か?ネットフリックス向けは制作単価を上げて質を上げる方向なのか。


B:the BeginningはPerfect Bonesから名前変更。ネットフリックス向けは会計処理が複雑。配信売上と相殺されていく。単価の記事の10倍というのは間違い。実際はそこまで高くない。

製作委員会ではなく、サブライセンスの契約になっている。売れたら売れただけ入る。短期ではなく、長期の構造。

製作委員会方式だとアニメーターに入らないという話がある。今後の方向性は?魔法使いの嫁なども一例だと思うが。


WitStudioのケース。いいものを作る。委員会方式も高度化。条件交渉や、よりよいパートナーを見つけるなど、体制を作る。

30年、基礎を作るべく積み上げてきた。基礎の上に100年続く家を建てる。皆様にわかりやすく見せるのが大事。

株主優待について。クオカードはタチコマの横に飾っている。


クオカードは、攻殻機動隊だけがアニメじゃないという苦情もきた。万人が満足する優待はできない。人的リソースがかかる。機関投資家からは使い道がないので、株価を上げて配当するのが筋ではないかとのコメントを頂いている。

スマホアプリの展開は?魔法使いの嫁など。ジョーカーゲームの続編は?


アプリ市場伸長も認識しているが、作品によりけり。ネクソンさんとやっている攻殻機動隊がCloseした。IGの作品はストーリー性が高いため、キャラがバトルして、ガチャを引くというのに向いていない。やるなら、それに向けて作品を作らないといけない。

原作元の意向があるので、作品の続編は協議で決まる。黒子のバスケは第1話から全て映像化できた。

受賞歴を見やすくできないか?


貴重な意見、ありがとうございます。IRのページを充実させていく。

IGストアとLineスタンプにSACの2ndが欲しい。


貴重な意見、ありがとうございます。

eコマース戦略、商品数が少ない。客単価設定は?


IGストアは収益性が高い方に特化している。売上高は非開示だが、ジョーカーゲームの売上がダントツ。設定資料集、原画集など。海外の人とeコマースで繋がる。WitStudioも進撃のフェアを行なって数字を残した。

GoogleやAppleのストアで過去のライブラリを活かせないか?コンテンツを探しにくいのでIGでまとめて欲しい。


昔の作品は配信の窓口権を保持していない。パッケージメーカーの事情により配信できないケースがある。USのプラットフォームは日本に不利なケースもある。

アーカイブの事業部署がある。アニメ会社の中では珍しい。大事にしており、強みであると考えている。

ファフナーはいつ見れる?


守秘義務がある。

タテアニメと、乙女向けのトキメキレストランについて。


数字は非公開。ローンチから3ヶ月、バグも解消。数字を伸ばしている。システム周りが落ち着いたので、キラーコンテンツを入れる。控えていた宣伝に力を入れる。

初の試み。DVD、BDは低迷。パッケージ市場ではアイドルものが売れている。アイドリッシュセブンなど、社内に知見が溜まっている。

孤独のグルメをタテアニメでやります。

スチームでサイコパスのゲームを見た。海外で魔法使いの嫁をどう売るか。


出資比率に応じた海外権の確保は重要。IGストアでマーケティングリサーチ中。高くて粗利の高いものでいきたい。世界がeコマースでつながる。魔法使いの嫁は海外権を持っており、クランチロールとYoukuなどと組む。昨日も商品の監修が来ていた。OVAを制作したことで、OVAの段階からプリセールスができた。

IG USAが20年。フリクリやNetflixは北米の本社とIG USAおよびIGで契約している。

出版の減収、減益の理由


あまんちゅのアニメなどで重版したが、思ったよりも返品が増えた。大阪は編集1、営業1。書店周り。大学と専門学校の新人発掘。

銀英伝の進捗


9/20にお披露目イベントをやる。ファンが多いので、ぱらぱら出すと、必ず叩かれる。ヤマトでの経験がある。戦略的に情報を抑制して、まとめて情報を出す。

BSのコンテンツ資産の中身


映像マスターは製作委員会に出資した金額。大体、1年で減価償却。昔はテープだったので固定資産。

コンテンツ資産はIGポート100%で制作したもの、もしくは多くを出資したもの。魔法使いの嫁のOVAと、魔法使いの嫁のTVシリーズがこれに当たる。他社が出資すると映像制作収入。最近はデータ納品になるため、無形固定資産になる。監査法人とも調整の結果。


その後、魔法使いの嫁のビジネスモデル説明会と第一話の上映会がありました。魔法使いの嫁への出資は60%に及び、限定コミックの付属するBDの予約は好調で、今期、魔法使いの嫁はIG全体の版権収入の35%近い値を期待しているとのことです。このビジネスモデルは全ての作品に適用できるものではなく、MAG gardenとの2007年の経営統合から10年かかって、ようやくこのビジネスモデルを適用できる作品がでてきた、という話をされていました。

おみやげ。魔法使いの嫁を期待していましたが、真田幸村でした。

IMG_2415

UnityWebRequest.Postで32KB以上のデータが送信できない

UnityWebRequest.Postに32KB以上のデータを与えると、UriFormatException: Uri is longer than the maximum {0} characters.が発生します。これは、UnityWebRequestのPostの内部でUri.EscapeDataStringを使用しており、本来は制約がないはずのPostのデータについても、Uriの32768文字制約が適用されるためです。具体的に、UnityのソースコードをデコンパイルしたSerializeSimpleFormにおいて、Uri.EscapeDataString(current.Value)を呼んでいることがわかります。

この問題を回避するには、Uri.EscapeDataStringを細かく呼ぶ以下のようなクラスを自作して、UnityWebRequest.Postを置き換える必要があります。

尚、検証に使用したUnityは5.5.4f1です。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine.Scripting;
using UnityEngineInternal;

public class UnityWebRequestUnlimited : MonoBehaviour {
	private static string EscapeLongDataString(string stringToEscape)
	{
		var sb = new StringBuilder();
		var length = stringToEscape.Length;

		// 32767以上だとエラーになるため分割する
		var limit = 32767 - 1;
		for (int i = 0; i < length; i += limit)
		{
			sb.Append(Uri.EscapeDataString(stringToEscape.Substring(i, Math.Min(limit, length - i))));
		}

		return sb.ToString();
	}

	public static byte[] SerializeSimpleForm(Dictionary formFields)
	{
		string text = "";
		foreach (KeyValuePair current in formFields)
		{
			if (text.Length > 0)
			{
				text += "&";
			}
			text = text + Uri.EscapeDataString(current.Key) + "=" + EscapeLongDataString(current.Value);
		}
		return Encoding.UTF8.GetBytes(text);
	}

	public static UnityWebRequest Post(string uri, Dictionary formFields)
	{
		UnityWebRequest unityWebRequest = new UnityWebRequest(uri, "POST");
		byte[] data = null;
		if (formFields != null && formFields.Count != 0)
		{
			data = UnityWebRequestUnlimited.SerializeSimpleForm(formFields);
		}
		unityWebRequest.uploadHandler = new UploadHandlerRaw(data)
		{
			contentType = "application/x-www-form-urlencoded"
		};
		unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
		return unityWebRequest;
	}
}

Unity Cloud BuildでUnable to merge android manifestsが出る

Unity 5.5.4f1にFirebase 4を入れようとした際、 Unable to merge android manifestsのエラーが発生しました。ローカルでビルドした場合と、Unity 5.6.3f1を使用した場合はエラーは発生しません。Unity 5.6からAndroid 4.4未満はサポートしなくなったため、Unity Cloud Buildで使用しているAndroid SDKのバージョンの問題かと考え、Androidのビルド設定のMinimum API Levelをデフォルトの2.3.1から4.4に上げたところ、問題は解消されました。

1820: [Unity] AndroidSDKToolsException: Unable to merge android manifests. See the Console for more details. 
1821: [Unity]   at UnityEditor.Android.AndroidSDKTools.DetectErrorsAndWarnings (System.String logMessages, System.String errorMsg) [0x00000] in :0 

UnityにおけるiOS版のアプリサイズの削減

AppStoreでは100MBを超えるとLTEでアプリをダウンロードできなくなります。ダウンロード数を増やすには、アプリサイズ(圧縮後)を100MB以下に抑える必要があります。

しかし、Unityを使用して開発していると、iOSのアプリサイズがAndroidよりも大幅に大きくなります。その原因の多くは、テクスチャの容量です。

Androidでアルファ付き画像を使用すると、デフォルトでETC2+ALPHAが選択され、1画素8bitになります。また、非正方形の画像も4x4画素の倍数であれば圧縮可能です。対して、iOSの場合はデフォルトでPVRTC 4bitが選択されますが、PVRTCは正方形かつ2の乗数しか扱えないため、往々にしてRGBA無圧縮が選択され、1画素32bitになります。その結果、iOSのアプリサイズがAndroidの4倍になります。

この問題を解決するには、UnityのSpritePackerを使用して、2の乗数の解像度に複数の画像をまとめる必要があります。具体的に、テクスチャのSprite ModeにPolygonに指定した上で、Packing Tagを指定すると、自動的にパッキングすることができます。

Packingによって、テクスチャの解像度は正方形になります。その結果、PVRTCでの圧縮が有効になります。アルファ値が存在しない場合は、PVRTC 4bitでも十分な画質が確保できるため、これだけで問題は解消です。しかし、UI画像など、アルファ値が存在する場合は、PVRTC 4bitが非常に汚いため、実用的ではありません。

アルファ値が存在する画像を使用する場合は、iOSのTexture CompressionでASTC 4x4を指定します。これを使用すると、1画素8bitになり、とても綺麗です。しかし、ASTC 4x4はiPhone6以降にのみ対応しており、iPhone5SではランタイムでRGBA無圧縮に展開されるため、1枚につき数百ミリ秒程度の展開コストがかかります。そのため、全ての画像にASTC 4x4を使用すると非常に重いです。そのため、全てのUI画像に適用するのではなく、Sprite Packingする画像にのみ適用することを推奨します。

推奨設定:
・画像は4x4の倍数で作成する (ETC2およびASTCの制約)
・テクスチャはSpritePackerでまとめる (PVRTCを使用できるようにする)
・アルファなしの場合はPVRTCを使用する (iPhone5Sのパフォーマンス向上のため)
・アルファ付きの場合はASTC 4x4を使用する (画質を上げるため)

注意:
・iPhone5SではASTCがソフトデコードになるので使いすぎない

YOLOの出力ベクトルからバウンディングボックスを計算

YOLOは、シンプルなCNNでオブジェクトのカテゴリとバウンディングボックスを検出することができるネットワークです。概要はChainerでYOLOからどうぞ。

YOLOの入力は448x448ピクセルの画像を、RRR...GGG...BBB...で並べて与えます。その際、入力画素のレンジは+-1.0に正規化する必要があるので、RGBが8bitの場合は、normalizedRGB=RGB/255.0f*2-1.0fとします。また、Bitmapの上下のスキャン順にも注意します。

対して、YOLOの出力は1470次元のベクトルであり、バウンディングボックスを計算するには、やや複雑な計算が必要です。

バウンディングボックスの計算方法はYOLOtiny_chainer/predict.pyのコードを参考にさせて頂きました。

YOLOの出力は、順に以下の項目で構成されます。

  probs=ans[0:980].reshape((7,7,20))     # class probabilities
  confs=ans[980:1078].reshape((7,7,2))   # confidence score for Bounding Boxes
  boxes = ans[1078:].reshape((7,7,2,4))  # Bounding Boxes positions (x,y,w,h)


probsは64x64ピクセルを1グリッドとして、画像を7x7個のグリッドに区切ったそれぞれで、以下の20カテゴリの推定確率が入っています。

  classes = ["aeroplane", "bicycle", "bird", "boat", "bottle",
             "bus", "car", "cat", "chair", "cow",
             "diningtable", "dog", "horse", "motorbike", "person",
             "pottedplant", "sheep", "sofa", "train","tvmonitor"]


confsには7x7個のグリッドにつき、2つのバウンディングボックスの信頼度が入っています。

boxesには7x7個のグリッドの2つのバウンディングボックスの座標が、(x,y,w,h)の順に入っています。wとhはsqrtがかかっています。そのため、有効なバウンディングボックスの座標は以下で計算することができます。

for(int by=0;by<7;by++){
  for(int bx=0;bx<7;bx++){
    for(int box=0;box<2;box++){
       for(int category=0;category<20;category++){
         if(confs[by][bx][box]*probs[by][bx][category]>0.1f){
           x= (bx+boxes[by][bx][box][0])*(448/7);
           y = (by+boxes[by][bx][box][1])*(448/7);
           w = pow(box[by][bx][box][2],2)*448;
           h = pow(box[by][bx][box][3],2)*448;
           x1 = x - w/2;
           x2 = x + w/2;
           y1 = y - h/2;
           y2 = y + h/2;
           class = classes[category];
        }
     }
  }
}


尚、このままでは、1つのオブジェクトに対して複数のバウンディングボックスが描画されてしまいます。この問題を解決するには、You Only Look Once:Unified, 統合されたリアルタイムオブジェクトの検出を参考に、計算したバウンディングボックスに対してNon-Maximum suppressionを適用する必要があります。Non-Maximum suppressionでは、クラスごとに推定確率の高い順にバウンディングボックスをソートし、iou関数でバウンディングボックスの重なりを検出、バウンディングボックスが重なっている場合は除外します。

    # suppress non-maximal boxes
    for c in range(nb_class):
        sorted_indices = list(reversed(np.argsort([box.classes[c] for box in boxes])))

        for i in range(len(sorted_indices)):
            index_i = sorted_indices[i]
            
            if boxes[index_i].classes[c] == 0: 
                continue
            else:
                for j in range(i+1, len(sorted_indices)):
                    index_j = sorted_indices[j]
                    
                    if bbox_iou(boxes[index_i], boxes[index_j]) >= nms_threshold:
                        boxes[index_j].classes[c] = 0

basic-yolo-kerasにおけるNon-Maximum suppressionの実装

BigQueryのStreamingAPIで日付分割する

AppEngineからBigQueryにinsertする際、templateSuffixに日付を指定すると、TABLE_IDのSchemaを使用して、自動的にテーブルを日付分割することができます。

    SUFFIX_ID = "_"+datetime.datetime.now().strftime("%Y%m%d")
    body = { 'rows':[{'json':data_hash}] ,'ignoreUnknownValues':True ,'templateSuffix':SUFFIX_ID}
...
    response = bigquery.tabledata().insertAll(projectId=PROJECT_ID,
                                              datasetId=DATASET_ID,
                                              tableId=TABLE_ID,
                                              body=body).execute()
Search
Profile

abars

アプリとWEBサービスを開発しています。最近はUnityとGAE/pyが主戦場。

ブラウザ向けMMOのメトセライズデストラクタ、イラストSNSのイラストブック、東証の適時開示情報を検索できるTDnetSearchを開発しています。

かつてエンターブレインのTECH Win誌でATULADOを連載しました。

サイト:ABARS
Twitter:abars
Github:abars

Twitter
TopHatenar
HotEntry
Counter

アクセス解析付きカウンター。あなたのBLOGにもどうですか?登録はこちらから。

TOP/ BLOG/ LECTURE/ ONLINE/ RUINA/ ADDON/ THREAD/ METHUSELAYZE/ IPHONE/ MET_IPHONE/ ENGLISH/ RANKING