OpenCVで顕著性(Saliency)を求めてみる
概要
OpenCVのopencv_contribに入っているsaliencyモジュールをを使って画像の顕著性(Saliency)を求める方法と、使ってみた結果を紹介します。
はじめに
この記事はOpenCV Advent Calendar 2016の23日目の記事です。
(視覚的)顕著性とは人の注視の引きつけやすさを示す指標のことです。また、画像の各ピクセルにたいして注視の度合いを推定したものは顕著性マップと呼ばれています。画像の顕著性マップが求まると、例えばサムネイル表示などのための画像のクロップや、顕著性の高い領域を強調させるような色変換などができるようになります。身近なところでいうと、モバイル版Twitterではタイムラインで表示する際には画像をクロップしていますが、おそらくSaliencyを基にクロップ領域を決定していると思われます(確かめた訳ではありません)。
今回は、OpenCVのopencv_contribに入っているsaliencyモジュールを使って、画像の顕著性を求めてみます。 たくさんの写真を使って、自動クロップによるスライドショーや写真コラージュの作成、注目領域を強調する写真の自動色変換ができたらいいなぁと思い、OpenCVのsaliencyをとりあえず使ってみることにしました。
OpenCV Sailency の使い方
ビルド方法は opencv_contribに記載されているのでそちらをご参照ください。
OpenCVでは現在4つの顕著性を求めるアルゴリズムが実装されていますが、cv::saliency::Saliencyクラスを用いてそれらを共通のAPIで使うことができるようになっています。 指定した画像の顕著性マップは次のように求めることができます。
... cv::Mat image = cv::imread("test.png"); std::string saliency_algorithm = "FINE_GRAINED"; cv::Mat saliencyMap; cv::Ptr<cv::saliency::Saliency> saliencyAlgorithm = cv::saliency::Saliency::create( saliency_algorithm ); saliencyAlgorithm->computeSaliency( image, saliencyMap ); ...
基本的な使い方は公式のサンプルコードが参考になります。 github.com
現在OpenCVのsailencyモジュールには、下記4つのアルゴリズムが実装されています(2016.12.21現在)。
BING*3
物体がありそうな複数の矩形領域を算出するアルゴリズム。 BINGのプロジェクトページ
アルゴリズム別結果比較
OpenCVで実装されている上記1-3のアルゴリズムに加えて、Ming-Ming ChengさんのSaliency検出のオリジナルコード*5の結果を記載します。 Ming-Ming ChengさんのSaliencyプログラムは、以前から筆者が顕著性を求めるならコレが一番いいなぁと個人的に思っていたものです。なお、Ming-Ming ChengさんのオリジナルSaliency検出はビルドが簡単で使いやすいです。
おわりに
OpenCVのopencv_contribに入っているsaliencyモジュールを使って顕著性を求める方法とその結果を紹介しました。 用途によって欲しい結果も変わるため、OpenCVに複数のアルゴリズムが実装されたことで簡単に複数アルゴリズムを比較できるのはいいなと思いました。 BINGはOpenCVに入っている学習済みモデルを使ったのですが、結果を見てみると過検出ぎみで今回の画像ではちょっと主観に合わないかなと思いました。 今後の課題として、BINGのオリジナルコードがMing-Ming Chengさんの同じリポジトリに入っているので、こちらも試したいと思います。
*1:X. Hou and L. Zhang. Saliency detection: A spectral residual approach. In Proc. CVPR, 2007.
*2:S. Montabone and A. Soto. Human Detection Using a Mobile Platform and Novel Features Derived from a Visual Saliency Mechanism. Image and Vision Computing, vol.28, pp.391-402, 2010.
*3:M.-M. Cheng, Z. Zhang, W.-Y. Lin, and P. H. S. Torr. BING: Binarized normed gradients for objectness estimation at 300fps. In Proc. CVPR, 2014.
*4:B. Wang and P. Dudek. A fast self-tuning background subtraction algorithm. In Proc. CVPRW, 2014.
*5:M.-M. Cheng, G.-X. Zhang, N. J. Mitra, X. Huang, and S.- M. Hu. Global Contrast based Salient Region Detection. IEEE TPAMI, vol. 37, no. 3, pp.569-582, 2015.
NuGetの設定を別プロジェクトへ引き継ぐ方法 (Windows版Caffeを例に)
概要
- Visual C++でNuGetパッケージの設定を別プロジェクトへ引き継ぐ方法の紹介です。
- プロジェクトの参照をする際、NuGetパッケージの情報を記載したプロパティシートを読み込むだけで簡単に参照先のNuGetパッケージを利用できるようになります。
- Visual Studio 2013 でCaffe(libcaffe)のNuGetパッケージも含めて参照したサンプルコードを公開しました。
はじめに
この記事はVisual Studio Advent Calendar 2016の22日目の記事です。
Visual C++プロジェクトの設定に関して、GUI操作だけでなく各種設定用XMLファイル(.vcxprojや.props)を直接編集できる方を対象とした内容です。
NuGetとは.NETなどMicrosoft開発プラットフォームのライブラリ管理ツールです。Visual Studio 2012からは標準でインストールされています。
NuGetの基本的な使い方は下記をご参照ください。
ここで、NuGetを用いて利用する外部ライブラリをNuGetパッケージと呼ぶことにします。
Visual C++でNuGetパッケージをインストールする際、GUI操作でインストールすると、NuGetパッケージの設定がそのプロジェクトにしか適用されません。 それゆえ、Windows版Caffeのように、複数のNuGetパッケージを利用するものは、それを参照するすべてのプロジェクトにNuGetパッケージをインストールしなければならなくなり、面倒です。
そこで本記事は、Windows版Caffeを例に、様々なVisual C++プロジェクトで個々にGUI操作によるインストールをせずに特定のNuGetパッケージを利用する方法を紹介します。 具体的には、プロパティシートへ特定のNuGetパッケージを利用するための設定を行い、そのプロパティシートをプロジェクトに取り込むことで利用できるようにします。
なお、この方法は筆者がなんとなく思いついたものですが、他にもっと便利な方法でこの問題を解決できる方法があれば教えていただきたいです。
Windows版Caffe
Caffeは有名なディープラーニングのフレームワークです。 Windows版Caffeのライブラリプロジェクト"libcaffe"を用いて、"libcaffe"を別プロジェクト"classification"で参照する場合を例に説明します。
下記のように説明用のVisual C++ソリューションは、boostやOpenCVなど16個のNuGetパッケージを用いるCaffeのライブラリプロジェクト"libcaffe"と、"libcaffe"を参照する"classification"プロジェクトが定義されているとします。
このとき、"classification"に対して"caffelib"が依存しているNuGetパッケージをGUIインストールせずに利用できるようにします。
そもそも、NuGetパッケージをインストールするとは?
GUI操作でNuGetパッケージをインストールすると、下記のことが自動的に行われます。
(1) インストール対象プロジェクトのプロジェクトディレクトリ$(ProjectDir)にファイル"packages.config" がなければ、"packages.config"が作られる。
(2) "packages.config" がプロジェエクトに追加される。
(3) "packages.config"にインストールしたパッケージの情報(id, version,targetFramework)が書き込まれる。
(4) インストールしたNuGetパッケージのプロパティシート(.props)を読み込む記述がプロジェクトファイル(.vcxproj)に追記される(あれば)。
(5) インストールしたNuGetパッケージのtargetファイル(.targets)を読み込む記述がプロジェクトファイル(.vcxproj)に追記される。
(6) 所定の場所(デフォルトでは$(SolutionDir)/packages)の下にnugetのパッケージがダウンロードされる。
上記のうち、
参照先("caffelib")プロジェクトに必要なものは(1), (3)。
参照元と参照先の双方のプロジェクトに必要なものは(4), (5)。
必要でないものは(2),(6)。 (2)はVisual Studio上で表示するための設定。(6)は(1), (3), (4), (5)の設定があればビルド時に自動的にダウンロードされる。
提案のNuGetパッケージの利用方法
参照先("caffelib")プロジェクトへの設定
$(ProjectDir)/packages.config を作成してその中にNuGetパッケージの情報を記載しておきます(上記(1),(3)に対応)。
プロパティシートの作成
プロパティシートを作成して、NuGetパッケージ内の.propsや.targetsを読み込ませます(上記(4),(5)に対応)。 なお筆者は、GUIで一度NuGetパッケージをインストールした後に、.vcxprojへ書き込まれた内容を.propsへ移すことでプロパティシートの記述を行いました。 この時ユーザマクロでバージョン等を指定しておくと、変更に柔軟になると思います。
サンプルプロパティシート: CaffeBuildSettimgs.props
<PropertyGroup Label="UserMacros"> <NugetDir>$(SolutionDir)\packages\</NugetDir> <BoostVersion>1.59.0.0</BoostVersion> <GflagsVersion>2.1.2.1</GflagsVersion> ... </PropertyGroup> ... <ImportGroup Label="PropertySheets"> <Import Project="$(NugetDir)\glog.$(GlogVersion)\build\native\glog.props" Condition="Exists('$(NugetDir)\glog.$(GlogVersion)\build\native\glog.props')" /> <Import Project="$(NugetDir)\gflags.$(GflagsVersion)\build\native\gflags.props" Condition="Exists('$(NugetDir)\gflags.$(GflagsVersion)\build\native\gflags.props')" /> ... </ImportGroup> ... <ImportGroup Label="ExtensionTargets"> <Import Project="$(NugetDir)\OpenCV.$(OpenCVVersion)\build\native\OpenCV.targets" Condition="Exists('$(NugetDir)\OpenCV.$(OpenCVVersion)\build\native\OpenCV.targets')" /> <Import Project="$(NugetDir)\boost.$(BoostVersion)\build\native\boost.targets" Condition="Exists('$(NugetDir)\boost.$(BoostVersion)\build\native\boost.targets')" /> <Import Project="$(NugetDir)\boost_date_time-vc$(PlatformToolsetVersion).$(BoostVersion)\build\native\boost_date_time-vc$(PlatformToolsetVersion).targets" Condition="Exists('$(NugetDir)\boost_date_time-vc$(PlatformToolsetVersion).$(BoostVersion)\build\native\boost_date_time-vc$(PlatformToolsetVersion).targets')" /> ... </ImportGroup>
参照先("caffelib")と参照元("classification")のプロジェクトへの設定
プロジェクトへ先ほど作成したプロパティシートを適用します。
おまけ:NuGetパッケージのダウンロードディレクトリ変更
$(SolutionDir)/nuget.configを作成して下記のようにrepositoryPathを設定すれば、NuGetパッケージのダウンロード先を変更することができます。
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <add key="nuget.org" value="https://www.nuget.org/api/v2/" /> </packageSources> <repositoryPath>../NuGetPackages</repositoryPath> </configuration>
Visual Studio 2013のサンプルプログラム
おわりに
NuGetを用いると様々なライブラリが簡単に利用・管理できるので便利だなと思っていたのですが、依存ライブラリが多い場合に毎度GUIでポチポチするのも怠かったので、いい方法がないかなと思っていました。 紹介したような方法でNuGetパッケージ読み込みをプロパティシートへ移しておけば、Caffeなどの依存ライブラリが多いプロジェクトを参照したプロジェクトを簡単に作成できるようになります。 なお、NuGetでインストールしたライブラリの別バージョンがインストールされていた場合(例えばOpenCVなど)、NuGetの方が優先されます(出力先にdllがコピーされるため)。コードを他人と共有するときなどは、NuGetでバージョン管理しておくと便利です。
Visual Studioにはまだまだ知らない使い方があると思うので、もっと使いこなせるようになりたいなぁ。
コンピュータビジョン勉強会@関東で発表しました
コンピュータビジョン勉強会@関東で発表したときのスライドです。
第30回(前編)CVPR2015読み会
第33回 コンピュータビジョンで使えるツールLT大会
次回は 2016/06/12(Sun) 《第34回 CV最先端ガイド4 第2章コンピュテーショナルフォトグラフィ理解のための光学系入門》 で発表予定です。 これからも参加するときは発表できるように頑張ろう。
OpenCV Tracking API について
この記事はOpenCV Advent Calendar 2015の21日目の記事です。
fps計測の環境が異なったため、再実験して修正しました。(2015.12.22)
再実験してKCFのオリジナルコードを追加しました。(2015.12.26)
概要
- OpenCV Tracking APIの導入方法、使い方、パフォーマンス評価のまとめ
- OpenCV Tracking APIに入っている追跡アルゴリズムのうち、KCF*1はstate-of-the-artと言えるアルゴリズムで、速度・精度共に高パフォーマンスなので有用そう
はじめに
OpenCV 3.0 になって、opencv_contribというリポジトリが追加された。 Tracking APIはその中に入っているモジュールの1つ。物体追跡を行うモジュールで、Trackerという共通インタフェースを経由して様々な物体追跡アルゴリズムを使うことができる。OpenCVのTracking APIの存在は知っていても使ったことがなかったので簡単に調査してみた。
OpenCV Tracking APIで出来ること
OpenCV Tracking APIでは、物体追跡の中でもOnline Single Object Tracking (SOT, 一つの物体のみを対象に追跡) という問題設定に対応するアルゴリズムが実装されている。初期値として矩形領域(bounding box)を指定すると、その矩形領域の物体を逐次的に追跡してくれる。OpenCV Tracking APIでは複数物体を追跡するAPIも用意されているが、SOTの組み合わせで、対象となるそれぞれの物体へ個別にSOTが適用される。
下記はOpenCV Tracking API で用意されている5つのアルゴリズムを適用した追跡結果である。
導入方法
opencv_contribに書いてあるとおりである。<opencv_contrib>
がopencv_contribのルートディレクトリだとすると、OpenCVをcmakeを使って通常通りビルドする際に、OPENCV_EXTRA_MODULES_PATH
へ<opencv_contrib>/modules
を設定してビルドするだけでよい。opencv_contribの他のモジュールの中には依存ライブラリが面倒なものもあるので、不要なモジュールはビルドから除外したほうがいいかも。
OpenCV Tracking API の使い方
先ほど説明したとおり、物体追跡を行うインタフェースには、一つの物体を追跡するTrackerと複数物体を追跡するMultiTrackerがある。
[Trackerを使う場合]
次のようなステップで物体追跡を行う。
- Trackerインスタンスを生成(入力:アルゴリズム名)
Tracker::create(const cv::String& trackerType)
- Trackerインスタンスの初期化(入力:追跡を開始するフレームと物体領域)
Tracker::init(const cv::Mat& image, const cv::Rect2d& boundingBox)
- 更新(入力:次時刻のフレーム、出力:推定された物体領域)
Tracker::update(const cv::Mat& image, cv::Rect2d& boundingBox)
- 3を繰り返す
...たったこれだけ。
[複数物体追跡用のMultiTrackerを使う場合]
- MultiTrackerインスタンスを生成
cv::MultiTracker(const cv::String& trackerType)
- 追跡対象物体を登録
cv::MultiTracker::add(const cv::Mat& image, std::vector<cv::Rect2d> boundingBox)
- 更新
cv::MultiTracker::update(const cv::Mat& image, std::vector<cv::Rect2d> & boundingBox)
- 3を繰り返す
現在使える追跡アルゴリズム
なお、KCF はコードは入っているものの、まだ公式Referenceに記載がない。
→OpenCV3.1リリースで、KCFとTLDがopencv_contribが入ったという情報が出ましたね (公式Rreferenceには相変わらず記載がないですが)。 OpenCV - ChangeLog - OpenCV DevZone
パフォーマンス評価
評価方法
Visual Tracker Benchmark*6の51個のシーケンスを用いて評価を行った。追跡結果は初期の矩形領域に依存するため、 Visual Tracker Benchmarkの評価方法に従い、51個のシーケンスに対してそれぞれ追跡開始時刻を変えた20個のサブシーケンスを生成し、51×20=1020個のシーケンスを用いて300,000フレーム以上の追跡を行った。
評価指標
Success plot(領域オーバーラップ) 追跡結果のbounding box を、正解のbounding box をとして、オーバーラップスコアをと定義する。ただし、|・|は領域のピクセル数。Success plotは、テストを行った全シーケンスの全フレーム数のうち、オーバーラップスコアがある閾値 よりも高かった追跡結果の割合()をプロットしたもの。
Precision plot(中心位置誤差) 領域の中心位置誤差を用いた評価。Precision plotは、追跡bounding boxと正解bounding boxの中心位置誤差が閾値より小さかった割合をプロットしたもの。
処理速度 (fps) Intel Core i7-5820K CPU (3.30 GHz), RAM 32GB のPCで追跡した際の平均fps。
OpenCV Tracking API 各アルゴリズムのパフォーマンス
追跡精度・処理速度共にKCFが高パフォーマンス。
- Success plot(領域オーバーラップ)
処理速度(fps)
Boosting | MIL | TLD | MedianFlow | KCF |
---|---|---|---|---|
27.6763 | 16.8790 | 9.7450 | 134.3183 | 80.5531 |
他の実装及び最新アルゴリズムとの比較
OpenCV Tracking API 各アルゴリズムと、Boosting, MIL, TLDのオリジナル実装およびStruck, ASLA, CXT, CSKというstate-of-the-artなアルゴリズムとを比較してみた。結果は次の通りである。
- Success plot(領域オーバーラップ)
Precision plot(中心位置誤差)
処理速度 (fps)
Boosting | MIL | TLD | MedianFlow | KCF | |
---|---|---|---|---|---|
OpenCV | 27.6763 | 16.8790 | 9.7450 | 134.3183 | 80.5531 |
Original | 3.8218 | 28.6296 | 20.9542 | 42.9633 |
Struck | ASLA | CXT | CSK |
---|---|---|---|
34.0012 | 1.9253 | 7.9878 | 106.8774 |
※KCFのオリジナルコード(matlab)は今回のテストには入れてないが、少し動かしたところ40fps程度だった。さすがにC++で実装されているOpenCV版の方が速そう。
→KCFのオリジナルコードの結果を追加して再実験しました。(2015.12.26)
OpenCV版とオリジナルを比べると、TLDはOpenCV版になって精度がかなり落ちているのがわかる。デフォルトパラメータが異なるのか?
→TLDはオリジナル版では矩形領域の大きさを固定しているのに対して、OpenCV版では矩形領域の大きさの変更に対応している。これが仇となって(?)追跡精度が下がってしまったように思う。これは個人的な経験にも合致する結果である。(2015.12.26)
また、OpenCV実装のKCFと他のstate-of-the-artなアルゴリズムとを比べても、KCFは精度・速度共に引けを取らない高パフォーマンスであることがわかる。 なお、KCFは矩形領域の大きさを固定しているため、対象物体のスケールが大きく変わるとオーバーラップスコアが下がる。
映像の属性(スケール変化、低解像度、ブラー、オクルージョンなど)ごとの評価結果は下記を参考にしてください。 https://github.com/yuukicammy/opencv_tracker_performance_test/tree/master/tracker_benchmark_v1.0/figs/overall/ALL
おわりに
(単一の)物体を追跡したくなったら、OpenCVのKCFを使うのが良さそう。さすがCVPR2014にオーラルで採録されているだけある!
本当はTrackerを経由しない個々のアルゴリズムクラスの使い方とか、Tracking APIの内部構造とか、Tracking APIで用意されている探索・推定モジュールを用いて自分で追跡アルゴリズムを実装する方法などのもう少しアドバンスドな内容も書きたかったのですが、今後の課題とさせていただきます。
*1:Martin Danelljan, Fahad Shahbaz Khan, Michael Felsberg, and Joost van de Weijer. Adaptive Color Attributes for Real-Time Visual Tracking. In CVPR, 2014 (Oral).
*2:H Grabner, M Grabner, and H Bischof. Real-time tracking via on-line boosting. In BMVC, 2006.
*3:B Babenko, M-H Yang, and S Belongie. Visual Tracking with Online Multiple Instance Learning. In CVPR, 2009.
*4:Z Kalal, K Mikolajczyk, and J. Matas. Tracking-Learning-Detection. PAMI, 2011.
*5:Z Kalal, K. Mikolajczyk, and J. Matas. Forward-Backward Error: Automatic Detection of Tracking Failures. In ICPR, 2010.
*6:Yi Wu, Jongwoo Lim, and Ming-Hsuan Yang. Online Object Tracking: A Benchmark. In CVPR, 2013.
"Efficient Graph-Based Image Segmentation"の手法と実装
記事概要
1. Felzenszwalbらの提案したEfficient Graph-Based Image Segmentation
*1の手法と実装を解説
2. 手法:画像中の各画素を1つのノードとした木から、輝度が類似なノードをまとめていきセグメンテーションを行う
3. 実装:Union-Findを用いることでo(nlogn)となる
Felzenszwalbらの提案したEfficient Graph-Based Image Segmentation を解説してみる。
画像のセグメンテーション*2は1枚の画像を同じような特徴(明るさ、色、テクスチャなど)を持つ複数の領域に分割する処理で、基本的な画像処理技術の1つである。画像合成処理、物体認識、ジェスチャ認識などの前処理として幅広く利用されている。
中でも、2004年にFelzenszwalbらが提案したEfficient Graph-Based Image Segmentationは、現時点で2703回*3も引用されてる重要な論文の1つと言える。プログラムのソースコード(C++)も公開されている。
この辺りはあまり詳しくないものの、最近興味があって勉強していたあるstate-of-the-artな論文がこの論文をベースにしていたことと、これまで何度もこの論文をベースにした手法に出会ったことから、さすがに理解しておこうと思ったのがきっかけ。
手法
入力:個のノードと個のエッジからなるグラフ
出力:のセグメンテーション結果
初期化
1.1 各画素を表すノードと、画像中で隣り合うノード(画素)を結ぶエッジから構成される無向グラフを構成
1.2 エッジで結ばれるノード(画素)間の輝度差をエッジの重みとして設定
1.3 エッジを重みが昇順になるように並べを構成
1.4 各ノードにそれぞれ個別のコンポーネントを割り当て を構成に対して下記を繰り返す
2.1 で結ばれるノードがにおいてそれぞれコンポーネントに含まれているとする。この時、
かつ
を満たした場合にをマージすることでが得られ、それ以外の場合は。
ここで、 はエッジの重みであり、論文ではエッジで結ばれるノード(画素)の輝度値の差となっている。 また、は、
として定義される。 は (kは定数、|C|はCのノード数)で定義される閾値関数であり、は最小全域木の最大重み である。最終的に、が得られる
高速であることと、高い変動性を無視しながら低い変動性を残すことがポイントである。
実装
実装では、素集合としてコンポーネントを捉え、コンポーネントの統合にUnion-Find を用いることで高速な処理を実現している。各コンポーネントを1つの木として表し、ノードが属するコンポーネントの判定(Find)と木の統合(Union)で経路圧縮とランクを用いた高速化を行っている*4。
- ランク:木の高さを持っておき、低い方を高い方に繋げるようにする。
- 経路圧縮:上向きに辿って再帰的に根を調べる際に、調べたら辺を根に直接つなぎ直す。
なお、各コンポーネントに対応するは個別に保持され、木を統合する際にこの値も更新される。それゆえ最小全域木がコンポーネント間の比較の度に計算されることはない。
ソースコードが簡潔&完結!(標準ライブラリ以外にライブラリを用いない!)で、利用しやすいというのも引用回数が多いことに影響していると思う。
*1:P. F. Felzenszwalb and D. P. Huttenlocher, “Efficient Graph-Based Image egmentation,”International Journal of Computer Vision, vol. 59, no. 2, 2004.
*2:参考:領域分割 - SICE オンライン・ハンドブック - 計測自動制御学会
*3:2014年10月5日時点