TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

ボーカル音程モニター(Volcal Pitch Monitor)のバージョンアップ

約3ヶ月ぶりに、ボーカル音程モニター(Volcal Pitch Monitor)のバージョンアップしました。

play.google.com

変更点は以下の通りです。

1.4.4
・横方向にピンチズームできるようにした
・前回の縦軸の位置を保存するようにした
・縦軸に半音を表示するオプションを追加
・ボタンを非表示にするオプションを追加
・細かいバグ修正

細かなUIの改善になります。


解析精度は通常の音声であれば、ほぼ間違わない精度になっていますが、
楽器のアタック時の音を拾ってしまうので、
どうにかできないかという要望をもらっています。

今の方式だとちょっと難しいので、
何らかの方法で2値分類できるようにしたいですが、
スマホの処理能力を考えるとできることが限られてきます。

本当はニューラルネットワークを試したいのですが、
メモリ使用量が増えるとか課題があるので、
今後のテーマとして取り組みたいと思っています。

3Dモデルから学習データを生成する その2(トゥーンレンダリング)

前回に続きBlenderを使用してMMD初音ミクの3Dモデルから、マンガキャラクターの顔パーツ検出用学習データの生成を試みる。

BlenderMMDのモデルを読み込むとそのままでは、シェーディングが有効になっており、レンダリングを行うと陰影のある画像となる。
陰影をなくして、マンガの学習データとして適した画像にしたい。

ゲームやCGアニメなどで用いられているトゥーンレンダリングの技法で、陰影をなくした画像にすることができる。
トゥーンレンダリングは、影の有り無しで2階調で表現するが、マンガ風にするには、影部分は不要なので、1階調とする。
また、輪郭を線で描画する。

Blenderでそれらを行うには、マテリアルのシェーディングをオフにして、Freestyle機能で輪郭に線を描画する。

マテリアルのシェーディングをオフにする

GUIから行うには、Compositingレイアウトで、マテリアルタブを選択し、材質を選んでShadelessにチェックする。

Pythonのコードから行うには、以下のように実行する。

for m in bpy.data.materials:
    m.use_shadeless = True

顔と背景を白色にする

グレースケール化したときに顔は白色としたいので、材質3(顔)、材質16(目の白い部分)を白色にする。

bpy.data.materials['材質3'].diffuse_color = (1.0, 1.0, 1.0)
bpy.data.materials['材質16'].diffuse_color = (1.0, 1.0, 1.0)

背景も白色にする。

bpy.data.worlds["World"].horizon_color = (1.0, 1.0, 1.0)

Freestyle機能で輪郭に線を描画する

DefaultレイアウトでSceneタブ(カメラアイコン)を選んで、Freestyleにチェックする。
またデフォルトでは線が太すぎるので、Line Thicknessを0.5pxにする。
Pythonでは以下のように実行する。

scene = bpy.data.scenes['Scene']
scene.render.use_freestyle = True
scene.render.line_thickness = 0.5

この状態でレンダリングを行うと、
f:id:TadaoYamaoka:20170329224531p:plain
このように、鼻の位置に不要な線が描画される。

この不要な線をなくすには、LineStyleタブを選んで、Freesyleのプロパティで、Crease Angleを120°に設定する。

scene.render.layers.active.freestyle_settings.crease_angle = radians(120)

レンダリングを行うと、
f:id:TadaoYamaoka:20170329224908p:plain
このようになり、グレースケール化するとこうなる。
f:id:TadaoYamaoka:20170329225126p:plain

以上のようにして、3Dモデルからマンガに近い画像が生成できる。

3Dモデルから学習データを生成する

以前よりDCNNによるマンガキャラクターの顔パーツ検出に取り組んでいるが、学習データを手作業で準備するのが大変なので自動化できる方法を模索している。

写真を線画化するする方法や、Free-Form Deformationを使う方法を試しているが、ここでは3Dモデルから2Dの学習データを生成する方法を試してみる。

3Dモデルを使用して、ランダムに顔の向きに回転を加えて2D画像を生成するれば、1つの3Dモデルから異なる学習データを大量に生成できる。
顔パーツの検出点も手動で入力しなくても回転元の座標から計算で求めることができる。

マンガの顔の表現は3Dモデルの回転とは表現が異なるため、Free-Form Deformationによる方法の方がよりマンガに適した変形を加えることができると思うが、デフォーマーを手動で作成するのは大変である。
一旦3Dモデルで顔の向きを変えてそこからデフォーマーを学習するとデフォーマーを手動で作成しなくてよくなると考えている。

使用する3Dモデルデータ

元となる3Dモデルを一から作成するにはモデリングのスキルが全くないため、フリーで公開されている3Dモデルを使用することにする。
そこで、MMD初音ミクのモデルを使用することにした。

モデリングソフト

3Dモデルに回転を加えて2D画像を生成する過程を自動的に行いたいので、Pythonによるコントロールが可能なBlenderを使用して生成を行う。

BlenderMMDのモデルを取り込むプラグイン(mmd_tools)が公開されているのでそれを使用してBlenderMMD初音ミクのモデルを取り込んだ。

さて、そこから2D画像を生成したいのだが、Blenderは元より3Dモデリングの知識がほとんどないため、以下のことを順番に模索しながら行っていきたいと思う。

  • カメラを顔の中心に向ける
  • ライトを正面向きの平行光源にする
  • トゥーンレンダリング(セルシェーディング)を行う
  • 顔の向きを変える
  • レンダリングした画像を保存する

BlenderPythonを使用する

BlenderPythonを使用するには画面上部のScreen LayoutをScriptingに変更する。
コンソールが表示され、Pythonを対話形式で使用可能になる。

f:id:TadaoYamaoka:20170325225512p:plain

カメラを顔の中心に向ける

MMD初音ミクのモデルを読み込んだ後、顔の中心にカメラを向ける。
読み込んだ初音ミクのモデルは、足元が原点となっており、正面が負のY軸方法で、頭の方向が正のZ軸方向となっている。

メッシュが体のパーツごとにグループ分けされており、顔の中心はグループ「目.L」と「目.R」の中心から求めることができる。

カメラは正面から顔に向けるので、x, y座標は0で、z座標を目の中心の位置に合わせる。

目の中心のz座標は以下のようにして求める。

eyes_index = [bpy.data.objects['初音ミク_mesh'].vertex_groups['目.L'].index, bpy.data.objects['初音ミク_mesh'].vertex_groups['目.R'].index]

eyes_z = []
for v in bpy.data.objects['初音ミク_mesh'].data.vertices:
    for g in v.groups:
        if g.group in eyes_index:
            eyes_z.append(v.co.z)

eyes_center_z = min(eyes_z) + (max(eyes_z) - min(eyes_z)) / 2

上記コードはモデルの全メッシュの頂点に振られたグループ番号が目のグループ番号と一致するものを抜き出し、そのZ座標の中間の値を求めている。


カメラの位置を上記の目の中心のZ座標に配置して、顔の正面に向けるには、

camera = bpy.data.objects['Camera']
camera.location = [0, -1.5, eyes_center_z]
camera.rotation_euler = [pi/2, 0, 0]

camera.locationで、カメラの座標を指定して、camera.rotation_eulerでカメラの向きを指定している。

カメラの座標は、顔の目の高さに合わせて、少しY軸方向に退いた位置に配置している。

カメラの向きは、オイラー角をラジアン単位で指定する。
カメラの向きは、[0, 0, 0]の時、負のZ軸方向で、スクリーンの上側が正のY軸方向を向いているので、X軸方向に90°回転することで、顔の正面に向けている。

f:id:TadaoYamaoka:20170325222138p:plain

ライトを正面向きの平行光源にする

ライトを正面向き(正のY軸方向)に向けて、光源の種類を平行光源にする。

lamp_obj = bpy.data.objects['Lamp']
lamp_obj.location = [0, -8, 0]

lamp_obj.rotation_euler = [pi/2, 0, 0]

lamp = bpy.data.lamps['Lamp']
lamp.type = 'SUN'

lamp_obj.locationでライトの座標を正面から少し退いた位置に配置している。
lamp_obj.rotation_eulerで、向きを顔の正面に向けている。
lamp.typeで、光源の種類を平行光源(SUN)に指定している。

レンダリングを行う

一旦ここで、レンダリングの結果を確かめてみる。
学習データとして顔の領域を100px×100pxに切り出すので、それよりも少し広い150px×150pxの解像度でレンダリングを行う。
レンダリングの解像度は、Python側でのコードが見つけられなかったのでBlenderのUIで設定を行った。
※2017/3/30追記
Pythonで設定するには以下のように実行する。

scene = bpy.data.scenes['Scene']
scene.render.resolution_x = 150
scene.render.resolution_y = 150

F12キーを押して、レンダリングを行うと以下のようになる。
f:id:TadaoYamaoka:20170325223328p:plain

陰影が付いており、そのままではマンガの学習データとしては適していない。
次回以降で、トゥーンレンダリング(セルシェーディング)を行い、マンガの学習データとして使用できる方法を試す予定。

SSDによる物体検出を試してみた

先日の日記でYOLOv2による物体検出を試してみたが、YOLOと同じくディープラーニングで物体の領域検出を行うアルゴリズムとしてSSD(Single Shot MultiBox Detector)がある。

YOLOv2の方が精度が高いとYOLOv2の論文に書かれているが、SSDの精度も高いようなので試してみた。

オリジナルのSSDの実装は、Caffeが使用されているが、WindowsでビルドできるCaffeとバージョンが異なるものが使用されており、トライしてみたがビルドがうまくいかなかった。

Python系のフレームワークの実装がないか調べたところ、TensorFlowの実装が2つとKerasの実装が1つとChainerの実装が1つ見つかった。

TensorFlowの実装
ssd_tensorflow
SSD-Tensorflow
Kerasの実装
ssd_keras
Chainerの実装
chainer-SSD

個人的にはChainerを使い慣れているが、他に比べて更新が古くてメンテされていないようだったので、熱心に更新されていたTensorFlowの実装を試すことにした。

github.com

生のTensorFlowは記述が冗長なので、あまりさわりたくないが、この実装ではTF-Slimが使用されており少しは読みやすくなっている。


学習済みモデルとデモも配布されていたので、README.mdの「SSD minimal example」の通り実行すると、問題なく動作した。

f:id:TadaoYamaoka:20170317233452p:plain

実行環境には、WindowsにインストールしたTensorFlowを使用した。


これを使って、先日のYOLOv2で試した動画で、物体検出を試してみた。

www.nicovideo.jp

そもそもアニメが学習データに入っていないので比較することには意味がないかもしれないが、YOLOv2と比べて検知漏れや物体でないところでの誤検知、キャラクターがaeroplaneになることが多い。

実行速度は、FPS:45.8であった。YOLOv2よりは遅いがリアルタイムに近い速度が出ている。


なお、TensorFlowの実装には、動画に対して検出するコードは含まれていなかったので、自分で作成した。
以下に作成した動画対応のコードを示しておく。

SSD minimal example」の最後の「In [11]」の部分を以下のコードに置き換える。

VOC_LABELS = {
    0: 'none',
    1: 'aeroplane',
    2: 'bicycle',
    3: 'bird',
    4: 'boat',
    5: 'bottle',
    6: 'bus',
    7: 'car',
    8: 'cat',
    9: 'chair',
    10: 'cow',
    11: 'diningtable',
    12: 'dog',
    13: 'horse',
    14: 'motorbike',
    15: 'person',
    16: 'pottedplant',
    17: 'sheep',
    18: 'sofa',
    19: 'train',
    20: 'tvmonitor',
}

colors = [(random.randint(0,255), random.randint(0,255), random.randint(0,255)) for i in range(len(VOC_LABELS))]

def write_bboxes(img, classes, scores, bboxes):
    """Visualize bounding boxes. Largely inspired by SSD-MXNET!
    """
    height = img.shape[0]
    width = img.shape[1]
    for i in range(classes.shape[0]):
        cls_id = int(classes[i])
        if cls_id >= 0:
            score = scores[i]
            ymin = int(bboxes[i, 0] * height)
            xmin = int(bboxes[i, 1] * width)
            ymax = int(bboxes[i, 2] * height)
            xmax = int(bboxes[i, 3] * width)
            cv2.rectangle(img, (xmin, ymin), (xmax, ymax),
                                 colors[cls_id],
                                 2)
            class_name = VOC_LABELS[cls_id]
            cv2.rectangle(img, (xmin, ymin-6), (xmin+180, ymin+6),
                                 colors[cls_id],
                                 -1)
            cv2.putText(img, '{:s} | {:.3f}'.format(class_name, score),
                           (xmin, ymin + 6),
                           cv2.FONT_HERSHEY_PLAIN, 1,
                           (255, 255, 255))


import time

vid = cv2.VideoCapture('path/to/movie')
if not vid.isOpened():
    raise IOError(("Couldn't open video file or webcam. If you're "
    "trying to open a webcam, make sure you video_path is an integer!"))

vidw = vid.get(cv2.CAP_PROP_FRAME_WIDTH)
vidh = vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = vid.get(cv2.CAP_PROP_FPS)

fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
out = cv2.VideoWriter('path/to/output.avi', int(fourcc), fps, (int(vidw), int(vidh)))

prev_time = time.time()
frame_cnt = 0
while True:
    retval, img = vid.read()
    if not retval:
        print("Done!")
        break
    
    rclasses, rscores, rbboxes =  process_image(img)
    write_bboxes(img, rclasses, rscores, rbboxes)
    out.write(img)
    frame_cnt += 1

curr_time = time.time()
exec_time = curr_time - prev_time
print('FPS:{0}'.format(frame_cnt/exec_time))

vid.release()
out.release()

WindowsにCaffeをインストールする

以前にWindowsでCaffeをビルドしてGPUで実行する方法について書きましたが、内容が古くなったので書き直します。

以前に日記を書いた時点では、ビルド済みバイナリが配布されていなかったため、自分でビルドを行いましたが、ビルド済みバイナリが配布されるようになりました。

公式からリンクされているGitHubWindowsブランチからダウンロードできます。

github.com

Visual Studio 2013用と2015用、GPU対応、Pythonのバージョンの組み合わせで、5種類のビルド済みバイナリが用意されています。

ここでは、
Visual Studio 2015, CUDA 8.0, Python 3.5: Caffe Release
をダウンロードした前提で記述します。
他のバージョンでもおそらく同様の手順で大丈夫です。

環境

インストール方法

ビルド済みバイナリのダウンロード

WindowsブランチのREADME.mdのPrebuilt binariesに記載されているリンクから環境に合わせたバイナリをダウンロードします。

アーカイブ展開

ダウンロードしたcaffe.zipを任意のディレクトリに展開します。

インストールは以上です。

MNISTサンプルの実行

公式の手順に従って、MNISTサンプルを実行します。

リポジトリ取得

caffe.zipにはサンプルが含まれていないので、Githubリポジトリを取得します。

任意のディレクトリで、

git clone https://github.com/BVLC/caffe.git

を実行して、リポジトリを取得します。

ディレクトリのコピー

git cloneしたローカルリポジトリから、以下のディレクトリをビルド済みのcaffeを展開したディレクトリに、コピーします。

  • data
  • examples
MNISTデータダウンロード

以下の手順で実行するシェルは、bashのシェルになっているので、Windowsで実行するにはMinGWなどでbashをインストールします。

bashを起動し、Caffeのディレクトリに移動し、

./data/mnist/get_mnist.sh

を実行し、MNISTのデータをダウンロードします。
wgetとgunzipが使われています。不足しているコマンドがあれば、MinGWのパッケージマネージャでインストールしてください。

データ準備

examples\mnist\create_mnist.shを編集します。

「BUILD=build/examples/mnist」→「BUILD=bin」に修正
「convert_mnist_data.bin」→「convert_mnist_data.exe」に修正(2か所)
編集後、

./examples/mnist/create_mnist.sh

を実行します。

訓練実行

examples\mnist\train_lenet.shを編集します。

「./build/tools/caffe」→「./bin/caffe」に修正
編集後、

./examples/mnist/train_lenet.sh

を実行します。

成功すれば以下のように出力されます。

$ ./examples/mnist/train_lenet.sh
I0315 23:17:26.000556 14060 caffe.cpp:219] Using GPUs 0
I0315 23:17:26.162506 14060 caffe.cpp:224] GPU 0: GeForce GTX 1080
I0315 23:17:26.425354 14060 common.cpp:36] System entropy source not available, using fallback algorithm to generate seed instead.
I0315 23:17:28.068279 14060 solver.cpp:44] Initializing solver from parameters:
test_iter: 100
test_interval: 500
base_lr: 0.01
display: 100
max_iter: 10000
lr_policy: "inv"
gamma: 0.0001
power: 0.75
momentum: 0.9
weight_decay: 0.0005
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet"
solver_mode: GPU
device_id: 0
net: "examples/mnist/lenet_train_test.prototxt"
train_state {
  level: 0
  stage: ""
}
I0315 23:17:28.069281 14060 solver.cpp:87] Creating training net from net file: examples/mnist/lenet_train_test.prototxt
I0315 23:17:28.071815 14060 net.cpp:296] The NetState phase (0) differed from the phase (1) specified by a rule in layer mnist
I0315 23:17:28.071815 14060 net.cpp:296] The NetState phase (0) differed from the phase (1) specified by a rule in layer accuracy
I0315 23:17:28.072289 14060 net.cpp:53] Initializing net from parameters:
(省略)
I0315 23:19:28.510016 14060 solver.cpp:219] Iteration 9700 (90.204 iter/s, 1.1086s/100 iters), loss = 0.0021487
I0315 23:19:28.510545 14060 solver.cpp:238]     Train net output #0: loss = 0.00214882 (* 1 = 0.00214882 loss)
I0315 23:19:28.511018 14060 sgd_solver.cpp:105] Iteration 9700, lr = 0.00601382
I0315 23:19:29.621973 14060 solver.cpp:219] Iteration 9800 (90.028 iter/s, 1.11077s/100 iters), loss = 0.00816679
I0315 23:19:29.622474 14060 solver.cpp:238]     Train net output #0: loss = 0.0081669 (* 1 = 0.0081669 loss)
I0315 23:19:29.622474 14060 sgd_solver.cpp:105] Iteration 9800, lr = 0.00599102
I0315 23:19:30.711405 14060 solver.cpp:219] Iteration 9900 (91.8658 iter/s, 1.08854s/100 iters), loss = 0.00674243
I0315 23:19:30.711905 14060 solver.cpp:238]     Train net output #0: loss = 0.00674254 (* 1 = 0.00674254 loss)
I0315 23:19:30.712406 14060 sgd_solver.cpp:105] Iteration 9900, lr = 0.00596843
I0315 23:19:31.783195 14060 solver.cpp:448] Snapshotting to binary proto file examples/mnist/lenet_iter_10000.caffemodel
I0315 23:19:31.795729 14060 sgd_solver.cpp:273] Snapshotting solver state to binary proto file examples/mnist/lenet_iter_10000.solverstate
I0315 23:19:31.805755 14060 solver.cpp:311] Iteration 10000, loss = 0.00296754
I0315 23:19:31.805755 14060 solver.cpp:331] Iteration 10000, Testing net (#0)
I0315 23:19:32.516114  7412 data_layer.cpp:73] Restarting data prefetching from start.
I0315 23:19:32.544718 14060 solver.cpp:398]     Test net output #0: accuracy = 0.9909
I0315 23:19:32.544718 14060 solver.cpp:398]     Test net output #1: loss = 0.0277917 (* 1 = 0.0277917 loss)
I0315 23:19:32.545192 14060 solver.cpp:316] Optimization Done.
I0315 23:19:32.545692 14060 caffe.cpp:260] Optimization Done.

YOLOv2による物体検出を試してみた

前回の日記WindowsにインストールしたDarknetを使ってYOLOv2による物体検出を試してみました。

Darknetの学習済みモデルを使用して、ニコニコ動画の上位にあった動画に対して行ってみました。

こちらの動画です。

www.nicovideo.jp


キャラクターがだいたいpersonと表示されています。
たまに、dogになったり、birdになったりcowになったりするのはある意味正しい?

検出速度はFPS=55.3でしたので、かなり高速です。

今回使用した学習済みモデルは、VOCという写真のデータセットで学習しているので、アニメ向け特化したモデルを学習すればさらに精度が上がると思われます。

DarknetをWindowsにインストールする

以前に行ったマンガの顔パーツ検出では、顔の領域の検出にdlibのHOG+SVMを使った方法で矩形領域を抽出してから、DCNNで顔パーツ検出を行っていた。
HOG+SVMである程度マンガから顔の領域を抽出できるが、少なからず検出漏れがあった。
また、スライディングウィンドウを使用しているため、処理時間がかかる問題もある。

領域の検出自体もディープラーニングで行うことができれば精度が上がるかもしれない。
ディープラーニングを使用した領域の検出にどのような方法があるか調べたら、YOLOv2というアルゴリズムが現時点で最も精度が高く高速で動作するようだ。

YOLOv2の実装は、YOLOv2の作者自身によるC言語で記述されたDarknetというフレームワークが使われている。

pjreddie.com

ビルド環境はLinux向けになっており、Windowsで試すにはプロジェクトの修正が必要になる。

すでにWindows向けにポーティングされていないか調べたら、フォークされたリポジトリがあった。

github.com

これをありがたく使用させていただき、Windowsでビルドを行った。

ビルドは、README.mdの通りに行えば問題なく成功した。

以下に実施した手順を示す。

環境

ビルド手順

Gitリポジトリのclone
git clone https://github.com/AlexeyAB/darknet.git
Visual Studioでプロジェクトを開く

git cloneしたディレクトリ配下の
darknet\build\darknet\darknet.sln
Visual Studioで開く。

ビルド構成設定

ビルド構成を「Release/x64」に変更する。

OpenCVのパス設定

README.mdに書かれているとおり、OpenCV 2.4.9をダウンロードして任意のディレクトリに展開する。
https://sourceforge.net/projects/opencvlibrary/files/opencv-win/2.4.9/opencv-2.4.9.exe/download

プロジェクトのプロパティ->VC++ディレクトリから、OpenCVを展開したディレクトリ配下のopencv\build\includeをインクルードディレクトリに追加する。

ライブラリディレクトリに、OpenCVを展開したディレクトリ配下のopencv\build\x64\vc12\libを追加する。

なお、ディレクトリ名がvc12になっているが、Visual Studio 2015でも問題なく使用できた。

ビルド

ソリューションのビルドを実行する。

成功すると、
darknet\build\darknet\x64
にdarknet.exeができる。

学習済みモデルダウンロード

README.mdに書かれているURLから学習済みモデルをダウンロードして、darknet.exeと同じディレクトリに格納する。
モデルファイルは、4種類ある。
モデルごとに使用メモリサイズ、精度、速度に違いがある。

とりあえずサンプル実行に必要な
yolo-voc.weights
をダウンロードした。
http://pjreddie.com/media/files/yolo-voc.weights

サンプル実行

画像ファイルからオブジェクトを検出するサンプルを試す。

コマンドプロンプトを起動する。

環境変数PATHにOpenCVのパスを設定する。

set path=%path%;H:\src\opencv\build\x64\vc12\bin

OpenCVをH:\srcに展開した場合の例
※システムのプロパティで環境変数PATHに追加してもよい。その場合、設定後はコマンドプロンプトの再起動が必要。

darknet.exeのあるディレクトリに移動する。

cd darknet\build\darknet\x64

サンプルを実行する。

darknet_voc.cmd

以下のような出力がされた後、画像ファイルのパスの入力プロンプトが表示される。

layer     filters    size              input                output
    0 conv     32  3 x 3 / 1   416 x 416 x   3   ->   416 x 416 x  32
    1 max          2 x 2 / 2   416 x 416 x  32   ->   208 x 208 x  32
    2 conv     64  3 x 3 / 1   208 x 208 x  32   ->   208 x 208 x  64
    3 max          2 x 2 / 2   208 x 208 x  64   ->   104 x 104 x  64
    4 conv    128  3 x 3 / 1   104 x 104 x  64   ->   104 x 104 x 128
    5 conv     64  1 x 1 / 1   104 x 104 x 128   ->   104 x 104 x  64
    6 conv    128  3 x 3 / 1   104 x 104 x  64   ->   104 x 104 x 128
    7 max          2 x 2 / 2   104 x 104 x 128   ->    52 x  52 x 128
    8 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256
    9 conv    128  1 x 1 / 1    52 x  52 x 256   ->    52 x  52 x 128
   10 conv    256  3 x 3 / 1    52 x  52 x 128   ->    52 x  52 x 256
   11 max          2 x 2 / 2    52 x  52 x 256   ->    26 x  26 x 256
   12 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512
   13 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256
   14 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512
   15 conv    256  1 x 1 / 1    26 x  26 x 512   ->    26 x  26 x 256
   16 conv    512  3 x 3 / 1    26 x  26 x 256   ->    26 x  26 x 512
   17 max          2 x 2 / 2    26 x  26 x 512   ->    13 x  13 x 512
   18 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024
   19 conv    512  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 512
   20 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024
   21 conv    512  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 512
   22 conv   1024  3 x 3 / 1    13 x  13 x 512   ->    13 x  13 x1024
   23 conv   1024  3 x 3 / 1    13 x  13 x1024   ->    13 x  13 x1024
   24 conv   1024  3 x 3 / 1    13 x  13 x1024   ->    13 x  13 x1024
   25 route  16
   26 reorg              / 2    26 x  26 x 512   ->    13 x  13 x2048
   27 route  26 24
   28 conv   1024  3 x 3 / 1    13 x  13 x3072   ->    13 x  13 x1024
   29 conv    125  1 x 1 / 1    13 x  13 x1024   ->    13 x  13 x 125
   30 detection
Loading weights from yolo-voc.weights...Done!
Enter Image Path: 

画像ファイルのパスを入力してEnterを押すと、コンソールに以下のような結果が出力され、検出された領域がウィンドウに表示される。

****.jpg: Predicted in 0.187000 seconds.
boat: 11%
person: 12%
person: 11%
person: 69%
person: 28%

 SRC output_video = 0000000000000000

 cvCreateVideoWriter, DST output_video = 000001D281CC12F0

 cvWriteFrame

また、結果が画像として、darknet.exeのあるディレクトリにpredictions.jpgというファイル名で保存される。

ウィンドウを閉じると次の画像のパスを入力するプロンプトが表示される。
Ctrl+Cで終了する。