のんびりしているエンジニアの日記

ソフトウェアなどのエンジニア的な何かを書きます。

Keras.jsを使って、JavascriptでDeepLearningを動かしてみる

皆さんこんにちは
お元気ですか?一人で箱根温泉りらっくすできました。

Keras.jsが出て、遂にKerasをjavascriptを扱えるようになりました。
(これ公式なのかどうかが非常に不安で、きっと違う)
ということで実際に動かしてみようと思います。

Kerasについて

Keras(Python

KerasはDeepLearningのライブラリです。
Theano、TensorFlowをバックエンドとして動作し、
切り替えることが可能です。

github.com

Keras-js

今回扱うのは、Keras-jsです。
github.com

ブラウザ上で、学習したKerasのモデルを走らせ、GPUWebGLを使って
扱うことができます。Kerasのjsonファイルとhdf5から読み出すことができます。
(backendはTheano,TensorFlowおそらくどちらも可)

ディレクトリ構成

デモのディレクトリ構成を確認します。
treeコマンドを実行すると次のような出力(一部省略)となります。

├── assets
│   ├── imdb-bidirectional-lstm.png
│   ├── inception-v3.png
│   ├── mnist-cnn.png
│   ├── mnist-vae.png
│   └── resnet50.png
├── base.css
├── data
│   ├── imdb_bidirectional_lstm
│   │   ├── imdb_bidirectional_lstm.json
│   │   ├── imdb_bidirectional_lstm_metadata.json
│   │   ├── imdb_bidirectional_lstm_weights.buf
│   │   ├── imdb_dataset_test.json
│   │   ├── imdb_dataset_word_dict_top20000.json
│   │   └── imdb_dataset_word_index_top20000.json
│   ├── inception_v3
│   │   ├── inception_v3.json
│   │   ├── inception_v3_metadata.json
│   │   └── inception_v3_weights.buf
│   ├── mnist_cnn
│   │   ├── mnist_cnn.json
│   │   ├── mnist_cnn_metadata.json
│   │   └── mnist_cnn_weights.buf
│   ├── mnist_vae
│   │   ├── mnist_vae.json
│   │   ├── mnist_vae_metadata.json
│   │   └── mnist_vae_weights.buf
│   └── resnet50
│       ├── resnet50.json
│       ├── resnet50_metadata.json
│       └── resnet50_weights.buf
├── dist
│   └── bundle.js
├── notebooks
├── src
├── webpack.dev.config.js
└── webpack.prod.config.js
  1. assets・・出力例の.png
  2. data・・keras.jsonで必要なNeuralNetworkのデータ、*.json, *_metadata.json, *weights.bufの組み合わせ
  3. notebooks・・Jupyter Notebookで、学習ソースコードが入っている。
  4. src・・ソースコード(html,css)が含まれている 。

Demoを動かしてみよう

既に殆どのものは準備されているので、
実は非常に簡単です。

Clone

まずは、リポジトリをクローンします。

git clone https://github.com/transcranial/keras-js

サーバの起動

npmを使ってinstallとサーバの起動を行います。

npm install
npm run server

http://127.0.0.1:3000にアクセスすると以下の画面を表示することができます。

f:id:tereka:20161016190946p:plain

ホームには様々な画面があり、サンプルを動かすことができます。
試しにMNISTを動かしてみましょう。

f:id:tereka:20161016191158p:plain

MNISTのDemoでは、四角の枠に絵(数字)を自分で書いて、どのように認識をさせることができるのかを
見ることができます。これを応用すると自分のWebサービスを構築した場合に、
Kerasをブラウザで動作させることができます。

カスタマイズポイント

Demoでは、既に準備されている
モデルを使って、表示をしてみました。TOPのREADME.mdには
他のモデルはどう扱うかなど、カスタマイズのポイントが紹介されているので
それを紹介したいと思います。

Model もしくは、Sequentialの構成の出力

次のようにするとモデルを出力することができます。

model = Sequential()
model.add(...)
...
model.save_weights('model.hdf5')
with open('model.json', 'w') as f:
  f.write(model.to_json())

hdf5として出力したファイルを変換する。

encoder.pyはkeras-js/encoder.pyに含まれています。

python encoder.py /path/to/model.hdf5

上記を実行すると、model_weights.bufとmodel_metadata.jsonが生成されます。

javascript上でのkerasのNeural Networkの宣言

今まではPython上でしたが、javascriptでは次のように宣言します。
model, weights, metadataの3種類のファイルのパスを宣言すれば良いです。
また、gpuのtrue or falseの宣言が必要となります。

const model = new KerasJS.Model({
  filepaths: {
    model: 'url/path/to/model.json',
    weights: 'url/path/to/model_weights.buf',
    metadata: 'url/path/to/model_metadata.json'
  }
  gpu: true
})

データ形式

javascriptの形式ですが、WebGLのFloat32Arrayで扱います。
MNISTの場合は次の変数宣言がありました。784次元の配列の宣言です。

this.input = new Float32Array(784)

予測

modelにはPython同様predictメソッドがあり、Float32Arrayを入力とすることで動作します。

model.ready().then(() => {
  model.predict(inputData).then(outputData => {
     //ここにoutputを書く。
  })
})

感想

個人的には結構扱いやすいKerasをベースとした構築のため
使ってみたいが、、、実のところ私がWebサービスを作ることが少ないという。。

PyConJPで触発されてPythonのType Hints(型ヒント)について書いてみた

皆さんこんにちは
お元気ですか。ラーメン食べたくなってきた。

私自身は自他共に認めそうなPython2.7.x userのつもりなのですが、
PyConJP2016でAndrey Vlasovskikh氏の講演でType Hints(型ヒント)の話があって
Python3.5すごいとか思いながら聞いてました。

ぶっちゃけ型ヒント何か知りません。

ということで、今回はPythonにおけるType Hintsを調べてみました。

Type Hintsについて

そもそもType Hintsってなに?

型に対するアノテーションです。具体的には実例を見るとおそらくわかりやすいです。

PEP484に規定されたType Hintsを提供するらしく、Python3.5で導入されています。
何のことやらわからないので、早速読んでみようと思ったら意外に長かったので読めておりません。
www.python.org

何が嬉しいのか

調べるとどうやらこんなメリットがあるそうです。

  1. Type Hintsを使うことで静的解析を容易にする。
  2. 容易なリファクタリングドキュメンテーションの作成
  3. アノテーションの標準的な書き方の定義

調べていたのですが、このあたりであまり見慣れない名前なので、何を言っているんだろうとおもってきてました・・・
ということで、そろそろ実例を

実例

よくあるチュートリアルの例

さて、既に調べるのに疲れたのでPython3.5に書いてある例に触ってみましょう。

def greeting(name: str) -> str:
    return 'Hello ' + name

name:strが、name変数がstr型であること、 -> strが返り値がstrを期待するといったことを示しています。

変数の別名

変数は前の紹介のstrだけではなく、複数の型を定義することができます。
次にその例を掲載します。
因みに、Tuple等を使って、定義することができます。

from typing import TypeVar, Tuple

T = TypeVar('T', int, float, complex)
def typetest(var: Tuple[T, T]) -> Tuple[T, T]:
    return var

Collections

Collectionsを使う場合はtypingのListやSetを使うことができます。
以下には例を記載します。

# coding:utf-8
from typing import TypeVar, List

T = TypeVar('T', int, float, complex)

def collection_test(list_var: List[T]) -> T:
    return list_var[0]

Generics

Genericsを使うと、ユーザ定義のGenericsを書くことができます。
ここまで来るとC++のtemplate構文を思い出してきますが、
そんなものを書くこともできます。

# coding:utf-8
from typing import TypeVar, Generic

T = TypeVar('T', int, float, complex)

class GenericTest(Generic[T]):
    def __init__(self,init: T):
        self.init = init

    def get_init(self) -> T:
        return self.init

    def set_init(self, init: T):
        self.init = init

書いてみたけど、どんな効果があるのでしょう

ここまでいくつか試しに書いてみましたが、
実際にどんな効果があるのかを確認したいので、見てみましょう。

PyCharm

PyCharmのVersion5.0以降でType Hintsがあることによって、
警告を出すようになります。

f:id:tereka:20160924004700p:plain

書いている時に、実際に自分が期待していない型を入力していることがわかるので、
その地点で警告を出してくれます。超便利
C++とか書いている人だと、結構ありがたいような感じではないでしょうか。

Mypy

Mypyは経験的な静的チェックのツールで、動的、静的のタイピングの良いところ取りする為に開発したようです。
Pythonの強力かつ、便利な型チェックで、基本的なPythonプログラムをサポートしているそうです。

Mypyが先に作成されて、Type Hintsとして導入されたようです。
インストールは次のコマンドです。私は知りませんが、mypyと呼ばれるpipで取得できるソフトウェアは別の内容とのことです。

pip install mypy-lang

さて、試しに次のコードに対してmypyを書けてみようと思います。
型が合わない、greetingに42のintを使っています。

# coding:utf-8

def greeting(name:str) -> str:
    return 'Hello, {}'.format(name)

greeting(42)

次のコメントが出てきます。

mypy TypeHints/typehint.py
TypeHints/typehint.py:6: error: Argument 1 to "greeting" has incompatible type "int"; expected "str"

つまり、greeting関数に対して、第一引数はstrを期待しているが、
intを与えていると言われています。
ここで期待していない値を投入していることがわかります。

最後に

Type Hintsは面白いけれどもversion2,3適用が難しそうだから少なくとも後数年は使わないかな
どうやらAndrey Vlasovskikh氏曰く、Python2のほうがPyCharm的には利用者が多いそうなので、
Python3がそれをうわまわる人数になったときにどうなるかによってType Hintsの扱いをどうするかとか、
コーディング規約が変わっていくのかなと思ったりもします。

参考にType Hintsに触れている規約があったら見てみたいとも思っています。

高速化のためのPython Tips

皆さんこんにちは
お元気ですか?私は元気です。

Pythonにおける高速化手法を掲載してみます。
簡単なコード並びに索引のような感じで引けるようなイメージで作成しました。

本日の目次です。

Pythonにおける高速化の必要性

PythonC++Javaと比較すると非常に遅い言語です。
しかし、最近はPythonで書く用途も増えてきており、個人的にも
世間的にも(多分)需要が増えつつあります。
が、計算機に負荷をかける処理を書くことが多いので、(私だけ?)いつも速度に悩まされます。

そんなわけで、今回の記事です。

Pythonの高速化

高速化の手順

基本的にPythonの高速化は次の手順で行われます。
(参考:PythonSpeed/PerformanceTips - Python Wiki

  1. テスト(実行)
  2. 遅ければプロファイル取る
  3. 最適化する
  4. 繰り返す

基本的には遅い部分や期待通りに動いていない箇所を割り出し、その箇所に対して
対策を打つのが基本です。で、その動いていない箇所を割り出すために、Profilingをする作業があります。

Profiling

Pythonのコードにかぎらず、
基本的にはProfilingを取得することから始めます。
つまり、どこが遅いのかを特定するためです。

実行手順は簡単で、オプションを追加して実行すれば、勝手に取得できます。
以下は今回実行した例です。着目ポイントはcumtimeやncallsです。

cumtimeが高ければ実行時間が長いので、その部分に遅い処理を入れている可能性が高いです。
ncallsが高い場合は無駄に関数を呼んでいる可能性があります。

それらの可能性を踏まえながらProfileを見ると高速化の手立てを発見できるかもしれません。
※-s は時間でソートするといった意味です。

python -m cProfile -s time import_cost.py

         10012657 function calls (10012536 primitive calls) in 9.465 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    7.273    7.273    7.777    7.777 import_cost.py:14(list_append_import)
        1    1.298    1.298    1.612    1.612 import_cost.py:6(list_append)
 10001436    0.642    0.000    0.642    0.000 {method 'append' of 'list' objects}
        1    0.076    0.076    9.465    9.465 import_cost.py:1(<module>)
        3    0.053    0.018    0.225    0.075 __init__.py:1(<module>)
        1    0.010    0.010    0.011    0.011 __init__.py:88(<module>)
        1    0.009    0.009    0.013    0.013 __init__.py:15(<module>)
        1    0.009    0.009    0.176    0.176 __init__.py:106(<module>)
        2    0.008    0.004    0.020    0.010 __init__.py:45(<module>)
        1    0.006    0.006    0.016    0.016 utils.py:4(<module>)
        1    0.005    0.005    0.009    0.009 __init__.py:41(<module>)
        1    0.005    0.005    0.005    0.005 npyio.py:1(<module>)
        1    0.004    0.004    0.009    0.009 numeric.py:1(<module>)
        1    0.004    0.004    0.007    0.007 index_tricks.py:1(<module>)
        1    0.003    0.003    0.012    0.012 _internal.py:6(<module>)
        1    0.003    0.003    0.004    0.004 case.py:1(<module>)
        1    0.003    0.003    0.008    0.008 tempfile.py:18(<module>)
        1    0.003    0.003    0.019    0.019 decorators.py:15(<module>)

基本的な条件

計測コード

計測時間が書いてある者については次のような方式で行いました。
非常によくあるコードかと思われます。

start = time.time()
#処理 Ex.list_append_local_val()
print time.time() - start

Pythonの基本的な書き方部分

rangeよりxrangeを(Python2.7)

Pythonはループを2通りの記載の仕方をすることができます。
xrangeはメモリに持たないので、rangeよりも高速にループを回すことができます。

# 遅い
def sum_range():
    result = 0
    for i in range(100):
        result += i

# 早い
def sum_xrange():
    result = 0
    for i in xrange(100):
        result += i

詳しい速度比較はこちらに記載しています。

nonbiri-tereka.hatenablog.com

リストの生成

リストは内包表記で生成するのが高速です。
内包表記をするとメモリに保存する必要がないからです。
実際には次のように記載します。

# 通常の書き方
list = []
for i in xrange(1000000):
   list.append(i)

# リスト内包表記
list  = [i for i in xrange(1000000)]

文字列結合

joinを使えば簡単に結合することができ、なおかつ高速なコードを記述することができます。

def string_operator_join(join_words):
    denominator = ""
    words = ""
    for word in join_words:
        words = denominator + word
        denominator = ","
    return words


def string_join(join_words):
    return ",".join(join_words)

Import文のコスト

Pythonはimport文を関数の中に書くことができます。
しかし、importをするコストが必要となるので、importを関数内で記載する時には注意が必要です。
例えば、以下のような関数を2つ用意しました。違いは関数の途中にimport numpy as npを追加しているかどうかです。

N = 10000000

def list_append():
    result = []
    for i in xrange(N):
        if i % 2 == 0:
            result.append(i)
    return result


def list_append_import():
    result = []
    for i in xrange(N):
        import numpy as np #numpyを何度もimportする。
        if i % 2 == 0:
            result.append(i)
    return result

上記を比較すると次のようになります。

関数名 かかった時間(s)
list_append 1.25
list_append_import 7.43

関数呼び出しのコスト

関数を呼び出すのにもコストがかかります。そのため、あまりにコストが高い呼び出しは避けましょう。
追加しながらリストを生成するlist_append関数を変更してみました。

def append_number(i, number_list):
    if i % 2 == 0:
        number_list.append(i)


def list_append_local_val():
    result = []
    for i in xrange(N):
        append_number(i, result)
関数名 かかった時間(s)
list_append 1.48743391037
list_append_local_val 2.39971590042

ドットを避ける

ドットを避けることで、若干のスピード向上をすることができます。
具体的なこんなコードになります。

N = 10000000

def list_append_local_val():
    result = []
    append = result.append
    for i in xrange(N):
        if i % 2 == 0:
            append(i)
    return result

yieldを使う

最後にyieldを使います。
元々C++を使った実装をメインとしていたので
使う機会というより馴染みがなかったのですが、使ってみるととても使いやすい。

昔はあまりに馴染みがないので利用していませんでしたが、
Deep LearningのアルゴリズムのReal Time Data Augmentationや一定の処理を実施する時に特に使います。

①メモリを消耗しない
yieldのメリットは、処理を一定の間隔(yield)で元の関数で返すので、メモリを消費しにくい
②直感的にループを書ける。
Pythonだと、ループの中でyieldを呼び出す処理を書くだけなので非常にわかりやすく、使いやすい。
(これぐらいならばもっと別の書き方しそうですが・・・)

#yield なし
list = [i * 2 for i in xrange(100)]
for i in xrange(list):
    print i + 10

#yield あり
def iterate(number):
    for i in xrange(number):
        yield i * 2

for j in iterate(100):
    print j + 10

Numpyに関するTips

Numpyはいくつか高速化並びに注意する点があります。

Numpyを使用して基本演算を高速化する

Numpyは基本的にCで記述され、実行速度も非常に高速です。
そのため、Numpyで書けるところはNumpyで書くと、高速になる計算となります。
例えば、合計値を求める演算は次のようになります。

# 通常のリストでの書き方
def number_element_sum(number_list):
    result = 0.0
    for i in xrange(len(number_list)):
         result += number_list[i]
    return result

# Numpyを使うので、高速に書ける。
def numpy_sum(numpy_list):
    return np.sum(numpy_list)

Numpyの要素にアクセスする演算をしない

Numpyは通常のリスト構造と同じく、アクセスはできます。
しかし、アクセスする速度が異様に遅いため、極力アクセスは阻止し、numpyを使って演算するようにします。

import numpy as np

# アクセスを繰り返すので非常に低速
def numpy_element_sum(numpy_list):
    result = 0.0
    for i in xrange(len(numpy_list)):
         result += numpy_list[i]
    return result

#こちらが高速
def numpy_sum(numpy_list):
    return np.sum(numpy_list)

詳しくは過去の記事を参考にしてください。

Numbaで手早く高速化

Numbaを使うとアノテーションを使うのみでコードを高速化できます。
以下のコードを用いて、私の実行環境で検証してみました。

yutori-datascience.hatenablog.com

numbaを適用したいメソッドに@jit(パッケージはnumba)を付与することで
勝手に適用するすぐれものです。詳しい使い方は別途、試していたいと思います。

python: 3.78625607491
numba: 0.206073999405

その他高速化ツール

Cython

Cythonは、Pythonで書かれたコードをC、C++のようにコンパイルすることで高速化するツールです。
詳しくは以下の記事を御覧ください。


Dask

Daskは柔軟な並列分散ライブラリです。

シンプルに記述できて、非常に良い感じです。
しかし、ある程度大規模にならないと、高速化しないように感じられるので
ある程度対応する規模を見積もる必要があると思います。

Dask — dask 0.11.0 documentation

PyPy

Pythonを高速に実装したのがPyPyです。但し、簡単に実行ができる代わりに
numpy, scipy周りが動かないようなものも出ます。(そのため使っておりません・・)
詳しくは以下をご覧ください。
nonbiri-tereka.hatenablog.com

公式ページ
pypy.org

感想並びに展望

今回はPythonにおける高速化といった観点で記事を書きました。
もっとこんな便利なのがありますといった話があれば、ぜひ。

Jupyter Notebookの次世代版、JupyterLabのこれが凄いポイントの紹介

みなさんこんにちは
お元気ですか。私は元気です。

Jupyter Notebookの次世代版、JupyterLabを紹介したいと思います。

※7/17 誤字脱字、一部画像を修正

JupyterLab

JupyterLabとは

JupyterLabはJupyter Notebookをベースに拡張したものである。
所謂IDEと呼ばれるツールと同様である。
現在はAlpha版がリリースされています。

※Scipy2016のカンファレンスビデオはこちらにあります。


JupyterLab: Building Blocks for Interactive Computing | SciPy 2016 | Brian Granger

JupyterLabのインストール

公式ホームページと同じでできます。

pip install jupyterlab
jupyter serverextension enable --py jupyterlab
jupyter lab

jupyter notebookが4.2.0未満の場合はupgradeが必要です。

この画面が立ち上がれば、Jupyter Labの立ち上がりを確認することができます。

f:id:tereka:20160717111705p:plain

今日はこれ凄いと思ったポイントを紹介したいと思います。

Jupyter Labの凄い点

1.画面分割が可能

以前までのJupyter Notebookでは、1画面での操作が基本で、
2画面の同時表示ができません。

しかし、JupyterLabからはそれが可能となっています。

ファイルをドラッグアンドドロップをすることで、画面を2画面に分割することが可能です。

f:id:tereka:20160716191447p:plain

また、更に追加でこんな感じの画面を展開することができます。

f:id:tereka:20160716212102p:plain

2.タブによる画面切り替え

なんと、タブがあります
タブの切り替えで、 様々なことができます。
タブがないと様々な画面をいちいち、開いて閉じてといった
動作をしなければならないので効率が悪かったのですが、
それがサポートされるというのは非常に大きいことです。

※1にも記載されているので画像は省略

3.ファイルの操作機能

ファイルが右クリック、ドラッグアンドドロップなど
様々な方法で操作することができます。

これまでドラッグアンドドロップなど使えず、結構手間だったことを考えると
かなり助かります。

f:id:tereka:20160716223100p:plain

4.コマンドの検索機能

Jupyter Labには様々なコマンドがついています。
vimモードの実行や一部ライブラリのヘルプページの閲覧をするなど
色々とできるのですが、複数の機能があり探すのも面倒くさい時に使える機能です。

Commandタブの一番上の空白に検索するテキストを入れることができます。
そのテキストをベースにCommand一覧から探してくれます。

f:id:tereka:20160716223501p:plain

5.csvを綺麗に表示する

csvを右クリックしOpen withでTableを選択し、開くと
テーブルを綺麗に見せることができます。

f:id:tereka:20160716232905p:plain

6.Widgetが1度のみの表示がされる。

これはさくっと自分で作るのが難しかったのでビデオから取ってきました。
Widgetを操作するとJupyter Notebookでは
Widgetが増えていくような挙動を示していました。

f:id:tereka:20160716233151p:plain

しかし、Jupyter Labからは何度操作しても一度の表示となります。

f:id:tereka:20160716233228p:plain

感想

まだまだAlpha版ですが、以前までのJupyter Notebookよりも
かなり進化しているような様子が見られます。

今後の動向にも期待ですね!

dlibで画像を認識させて、遊んでみた。

皆さんこんにちは
お元気ですか。私は元気です。

ついに、今回はdlibで遊んでみました。
※dlibとは何だ?と思う方は以下のページへ

nonbiri-tereka.hatenablog.com

基本的な構成

基本的の構成は以下のようになっており、よく使いそうな項目と
あるけどExamplesが存在しない項目の紹介となっています。

画像の読み込みと表示

画像を読み込んで窓に開いてみます。
C++の以下のコードで動作します。案外短かった。

しかし、コンパイルに結構手こずりました。
ライブラリを見ながら、リンクに何が不足しているのかを考えつつ、リンクしていきました。
(むしろCMakeを書くべきか・・・)

g++ -I/Users/Tereka/Programing/dlib/ -lpthread -ldlib -lblas augmentation.cpp

ソースコードは基本的にこれに追加する形式となります。

#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>
#include <dlib/image_transforms.h>

using namespace std;
using namespace dlib;

int main(int argc, char const *argv[])
{
        array2d<rgb_pixel> img;
        //画像をファイルから読み込む。
        load_image(img, argv[1]);
        //窓を定義する。
        image_window window(img, "Original Image");
        //クローズするまで開く。
        window.wait_until_closed();
        return 0;
}

画像の処理をかけてみる。

画像処理を実施してみました。
コンパイルコマンドは前項のをお使いください。

今回使ったのは、flipとgaussian blurと呼ばれる画像の処理をかけてみました。

#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>
#include <dlib/image_transforms.h>

using namespace std;
using namespace dlib;

int main(int argc, char const *argv[])
{
     array2d<rgb_pixel> img;
    //画像をファイルから読み込む。
    load_image(img, argv[1]);
    //窓を定義する。
    image_window window(img, "Original Image");
    //クローズするまで開く。
    window.wait_until_closed();

    array2d<rgb_pixel> fliped_image;

    flip_image_up_down(img,fliped_image);
    image_window window2(fliped_image, "Fliped Image");
    window2.wait_until_closed();

    array2d<rgb_pixel> gaussian_blur_img;

    gaussian_blur(img,gaussian_blur_img);
    image_window gaussian_window(gaussian_blur_img, "Gaussian Image");
    gaussian_window.wait_until_closed();
    return 0;
}

画像特徴に使われる処理を実施してみる。

Local Binary Pattern(LBP)とHog処理を実施してみます。

Selective Searchを実施してみました。
実はこれ、PythonにはExampleがありますが、なんと、C++にはExampleを用意してくれません。
ということで、ドキュメントを読みつつ、自分でコードを書いてみました。

#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>
#include <dlib/image_transforms.h>
#include <vector>
#include <iostream>

using namespace std;
using namespace dlib;

int main(int argc, char const *argv[])
{
    array2d<rgb_pixel> img;
    //画像をファイルから読み込む。
    load_image(img, argv[1]);
    //窓を定義する。
    image_window window(img, "Original Image");
    //クローズするまで開く。
    window.wait_until_closed();
    std::vector<rectangle> rects;
    //Selective Search
    find_candidate_object_locations(img,rects);
    //四角形の描画
    for (int i = 0; i < rects.size(); i++)
    {
    	cout << rects[i].top() << endl;
    	rgb_pixel pixel(255,0,0);
    	draw_rectangle(img,rects[i],pixel);
    }
    //表示
    image_window selective_window(img, "Selective Search Image");
    selective_window.wait_until_closed();
    return 0;
}

find_candidate_object_locationsがSelective Searchの実行関数となります。
その結果は与えたrectsの中に格納されるので、その結果を描画しています。

f:id:tereka:20160714230810p:plain:w250f:id:tereka:20160714230823p:plain:w250

OpenCV連携

OpenCV連携についてです。dlibライブラリ自身でサポートしており、
OpenCVで使いたい場合はOpenCVのMat型、dlibの関数しかない場合はdlibの関数を使うなど
簡単にこの連携を実現できます。

g++ -O2 -I/Users/Tereka/Programing/dlib/ -lpthread -ldlib -lblas `pkg-config --cflags opencv` `pkg-config --libs opencv` opencv_test.cpp

基本的にはtoMatとcv_imageを使えばよいです。実装コードは以下となっています。

#include <opencv2/highgui/highgui.hpp>
#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>
#include <dlib/image_transforms.h>
#include <dlib/opencv.h>

using namespace dlib;
using namespace cv;

int main(int argc, char const *argv[])
{
	Mat temp = imread(argv[1]);
        //OpenCV→dlib変換
	cv_image<bgr_pixel> cimg(temp);
        //dlib→OpenCV変換
	Mat convertedMat = toMat(cimg);
	return 0;
}

感想

dlib使いこなせると便利ですね。deep learningも実行でき、ある程度関数が整備されているので
C++であって非常に容易に複雑なアルゴリズムを実行することができます。