LoginSignup
7
7

More than 3 years have passed since last update.

YOLOv3をWindowsでpythonから呼び出してみる(デスクトップアイコン識別)

Last updated at Posted at 2019-10-22

はじめに

YOLOv3をPythonから呼び出してみようと思います。

YOLOv3(darknet)をpythonから呼び出すサンプルであるdarknet.pyは、呼び出しにctypesを使っています。ctypesはpythonからCを呼び出す標準のライブラリです。

Windowsからdarknetを使用するには、yolo_cpp_dll.dllを準備しなければいけません。
ここでは、その準備と、実際にpythonから呼び出してみることをやってみます。

なぜ、Windowsを使っているかというのは、WindowsのデスクトップアイコンをYOLOv3で識別する実験をしているからです。

前提

Yolo v3+Windows 10でデスクトップのアイコンを識別してみる その1」などに従ってWindowsでYOLOv3をビルドすることができている(やり方を理解できている)ことを前提としています。

環境

  • Windows10 Pro
  • Visual Studio 2017
  • CUDA 10.0
  • cuDNN 7.3.0.29
  • OpenCV 4.1.1

DLLをビルドする

最初に、VSのソリューションを開きます。
ソリューションは、下記にあります。
[配置先]\darknet\build\darknet\yolo_cpp_dll.sln

ソリューションプラットホームをReleaseとx64に変更して、前記事と同様に、CUDAとOpenCVのインクルードディレクトリを追加します。

image.png
image.png

ビルドすると、yolo_cpp_dll.dllが出来上がります。

[配置先]\darknet\build\darknet\x64まで移動して、以下のコマンドを実行してみましょう。

python darknet.py

いつもの犬のやつが表示されたら成功です。
image.png

解説

ctypes

ctypesはC言語と互換性のあるデータ型を提供し、動的リンク/共有ライブラリをPythonでラップすることができるようにするPython標準の外部関数ライブラリです。
呼び出し方には、標準 cdecl 呼び出し規約を用いて関数をエクスポートしているライブラリをロードするCDLLと、stdcall 呼び出し規約を用いる関数を呼び出すWinDLLの2種類があります。
cdeclとstdcallの違いについては、ここでは詳しく言及しません。ざっくりいうとメモリの確保と解放を呼び出し元、呼び出し先どちらでするかの規約です。

darknet.pyでは以下のようにDLLを呼び出しています。

darnknet.py
from ctypes import *
...
...
    winGPUdll = os.path.join(cwd, "yolo_cpp_dll.dll")
...
...
    lib = CDLL(winGPUdll, RTLD_GLOBAL)

CDLLを使ってるので、cdecl規約で呼び出しています。ソリューションで、そのような設定になっているかを確認してみましょう。
image.png
確かにcdeclになっていますね。
それから、CDLLで読み込んだlibをつかって、Cを呼び出しています。

darknet.py
lib.network_width.argtypes = [c_void_p]
lib.network_width.restype = c_int

def network_width(net):
    return lib.network_width(net)

ここで呼び出されているnetwork_widthは、Cでは、以下の通り定義されています。

darknet.h
LIB_API int network_width(network *net);

これを見るとわかるように、Pythonからctypesを使う際は、まず、argtypesrestypeでそれぞれ、引数の型と戻り値の型を指定し、それから、関数を呼び出す、という手順を踏みます。
引数の型にc_void_pを、戻り値にc_intを指定しています。

darknet.py

下記の関数がまず呼ばれます。

darknet.py
def performDetect(imagePath="data/dog.jpg", thresh= 0.25, configPath = "./cfg/yolov3.cfg", weightPath = "yolov3.weights", metaPath= "./cfg/coco.data", showImage= True, makeImageOnly = False, initOnly= False):

上のほうで動作させたのはすべてデフォルトのパラメータです。
この関数では最初にパラメータのチェックをしています。チェックに成功したら、次の関数を呼び出して認識を行ってます。

darknet.py
    detections = detect(netMain, metaMain, imagePath.encode("ascii"), thresh)

この関数の中で何をやっているかというと、

darknet.py
def detect(net, meta, image, thresh=.5, hier_thresh=.5, nms=.45, debug= False):
    im = load_image(image, 0, 0)
    if debug: print("Loaded image")
    ret = detect_image(net, meta, im, thresh, hier_thresh, nms, debug)
    free_image(im)
    if debug: print("freed image")
    return ret

画像を読み込んで、detect_imageを呼び出しています。
この関数は何をしているかというと、

darknet.py
predict_image(net, im)

predict_imageを呼び出しています。ここでやっと、Cの関数が呼び出されます。predict_imageは以下の通り定義されいます。

darknet.py
predict_image = lib.network_predict_image
predict_image.argtypes = [c_void_p, IMAGE]
predict_image.restype = POINTER(c_float)

やっとこさ、ctypesでの呼び出しが出てきました。ついでにCの実装を見ておきましょう。

network.c
float *network_predict_image(network *net, image im)
{
    //image imr = letterbox_image(im, net->w, net->h);
    float *p;
    if(net->batch != 1) set_batch_network(net, 1);
    if (im.w == net->w && im.h == net->h) {
        // Input image is the same size as our net, predict on that image
        p = network_predict(*net, im.data);
    }
    else {
        // Need to resize image to the desired size for the net
        image imr = resize_image(im, net->w, net->h);
        p = network_predict(*net, imr.data);
        free_image(imr);
    }
    return p;
}

確かに、引数がポインタとイメージで戻り値がfloatですね。
pythonでdarknetを呼びだすときは、この辺りを参考にすればよさそうです。

デスクトップを識別してみる

前回訓練させたweightsを使用して、デスクトップの識別をpythonから呼び出してみましょう。

コマンドプロンプトからpythonを起動して、以下のようにperformDetectを呼び出します。フルパス指定しているので少し長いです・・。パスは適宜読み替えてください。

>>> from darknet import performDetect
>>> performDetect("D:/Projects/python/desktopyolov3/data/datasets/Desktop006.jpg", 0.25, "D:/Projects/python/desktopyolov3/cfg/yolov3-voc.cfg", "D:/Projects/python/desktopyolov3/data/backup/yolov3-voc_last.weights", "D:/Projects/python/desktopyolov3/data/desktop.data")

以下の通り識別が動作しました。

image.png
大成功です。

つづく

次回は、ここでビルドしたDLLを使ってC++からYOLOv3を呼び出して、その場所のアイコンをゴミ箱に移動させるやつを作ろうと思います。

YOLOv3シリーズは以下から参照できます。

Yolo v3+Windows 10でデスクトップのアイコンを識別してみる その1
https://qiita.com/yasunari_matsuo/items/78695e9f53bc6b589daa

(おまけ)Yolo v3+Windows 10でデスクトップのアイコンを動的に識別してみる
https://qiita.com/yasunari_matsuo/items/f0989523849f6ae40483#_reference-8747cd277d7aeccfdef1

AIでデスクトップアイコンを自動削除してくれるツールを作ったった(YOLOv3+Windows 10)
https://qiita.com/yasunari_matsuo/items/2ac2140e7ac250304d4a

参考にしたサイト

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7