OpenCVの最新版をMacで使う

MacでOpenCVを使う場合,以前の記事で書いたように,

brew tap homebrew/science
brew install opencv

すればよい.
現在,2.4.9がインストールされる.

しかし,現在の開発版は3.0.0-devで,それを使いたいときはどうするか?

brew で普通にインストールすると,gitのリポジトリだと一番新しいtagのがインストールされる…のかな?
こういう,git等で公開されているやつの最新版(masterブランチのHEAD)を取ってきてインストールしたい場合は,

brew install --HEAD opencv

という風に --HEAD オプションを付ける.
これで,めでたくOpenCV 3.0.0-devがインストールできた.

これはOpenCVに限ったことではなく,他のライブラリやツールでも同じように最新版を入れたいときは --HEAD をつければ良い.
ただし,HEADは不安定なことも多く,通常用途では,--HEADを付けない方をおすすめする.

cv::OutputArrayの使い方(効率悪い)

今日は,cv::InputArray/cv::OutputArrayを入出力とする簡単な関数の作り方.

はじめ使い方がよくわからなかったので効率の悪い使い方をしていた.
まずはそれの紹介から.

例えば,全要素に1足す関数 plus1(cv::InputArray in, cv::OutputArray out) を作るとする.

void plus1(cv::InputArray in, cv::OutputArray out){
    cv::Mat img=in.getMat();
    cv::Mat pls=img+1;
    pls.copyTo(out);
}

cv::OutputArrayには直接代入が出来ないので,copyToを使ってコピーしていた.
ただし,処理の最後にOpenCVに入っている関数を呼び出すのであればこれで良い.
例えば

void fliteredplus1(cv::InputArray in, cv::OutputArray out){
    cv::Mat img=in.getMat();
    cv::Mat pls=img+1;
    cv::medianBlur(pls, out, 3);

こんな感じであれば問題ない…と思う.

で,正しい(最後にOpenCVの関数を呼び出さない場合の)使い方は次回.

cv::InputArrayの使い方

前回,cv::InputArray/cv::OutputArrayについて書いたが,今回はcv::InputArrayを入力とする関数の作り方について書く.

cv::InputArrayで受け取ったものは,中身はcv::Matやstd::vectorだがそのままでは使えない.
cv::InputArrayのkind()という関数を使うことで,もともと何型だったのかを知ることができる.
使えるのは,
MAT
MATX
STD_VECTOR
STD_VECTOR_VECTOR
STD_VECTOR_MAT
EXPR
OPENGL_BUFFER
OPENGL_TEXTURE
GPU_MAT
OCL_MAP

使うときは,cv::InputArrayクラスのgetMat()やgetMatVector()を呼び出し,cv::Matやstd::vector型に変換して使う.

void showImg(cv::InputArray in){
    cv::Mat img=in.getMat();
    // 以降,元がstd::vectorだろうとcv::Matとして使える
    cv::imshow("img",img);
    cv::waitKey();
}

getMatVector()の方は,cv::Matの配列になるので,

void printImgs(cv::InputArray in){
    std::vector<cv::Mat> imgs;
    in.getMatVector(imgs);
    for(auto img: imgs){
        std::cout << img << std::endl;
    }
}

という感じで使える.上記関数にcv::Matを渡すと,各行からなるcv::Matが配列に格納され,行ごとに画面に出力される.

cv::InputArrayとcv::OutputArray

OpenCVの新しいバージョン(3.0)では,様々なところで変更があって,いろんなアルゴリズムが入出力としてcv::InputArrayとcv::OutputArrayを使うようになっている.

cv::InputArray/cv::OutputArrayは,std::vectorやcv::Matやstd::vectorなどを統一的に扱えるようにしたもの.

なので,例えば cv::threshold だと,cv::Matを入力として

cv::Mat img, out;
cv::threshold(img, out, 10, 255, cv::THRESH_BINARY);

このように使ったり,std::vectorを入力として

std::vector<double> img;
cv::Mat out;
cv::threshold(img, out, 10, 255, cv::THRESH_BINARY);

このように使うことができる.

また,受け取る側のcv::OutputArrayも同様なので,先ほどの例だと受け取る側を

cv::Mat img;
std::vector<unsigned char> out;
cv::threshold(img, out, 10, 255, cv::THRESH_BINARY);

のようにstd::vectorにしたりもできる.

次回はこのようなcv::InputArray/cv::OutputArrayを入出力とする関数を自作する方法について書く.

sv_countのassertで死ぬ

Fedora 20上でOpenCVをコンパイルして使っている.
cv::SVMでサポートベクトル回帰(Support Vector Regression)を使って観ようとした時にうまく行かなかったのでメモ.

#include <opencv2/opencv.hpp>

int main(){
        // 適当な入力データと適当なラベル
	cv::Mat_<float> x(100,2,CV_32FC1);
	cv::Mat_<float> l(100,1);
	for(int i=50;i<100;i++) l(i,0)=1;

        // SVRのパラメータ
	CvSVMParams params;
	CvTermCriteria criteria;

	criteria=cv::TermCriteria(CV_TERMCRIT_EPS,1000,FLT_EPSILON);
        // ここで第1引数を CvSVM::EPS_SVR にすると SVMではなく SVR になる
	params=CvSVMParams(CvSVM::EPS_SVR,CvSVM::RBF,10.0,8.0,1.0,10.0,0.5,0.1,NULL,criteria);

	cv::SVM svm;

        // 学習の実行
	svm.train_auto(x,l,cv::Mat(),cv::Mat(),params);

	return 0;
}

SVRはこういう感じで使える.
SVMの種類はC_SVRとNU_SVRがあるが,とりあえずC_SVRを使う.

パラメータの設定は結構重要だが,クロスバリデーションによって自動で良いパラメータを探索してくれる.
このtrain_autoを実行しようとした時,アサーションエラーで死ぬようになった.

問題の箇所は sv_count != 0 のアサート.
多分サポートベクトルの数が0ならダメっていうことなんだろう.
普通のtrainなら上手くいくし,学習サンプル変えたりしながら色々試したけど原因わからず….

SVRの種類をNU_SVRに変えたら動いた….

cv::Sizeのメンバ関数area()

OpenCVでは矩形の大きさを表現するときにcv::Sizeクラスが使われる.

リファレンスを読むと,

template class Size_
{
public:
    typedef _Tp value_type;

    Size_();
    Size_(_Tp _width, _Tp _height);
    Size_(const Size_& sz);
    Size_(const CvSize& sz);
    Size_(const CvSize2D32f& sz);
    Size_(const Point_<_Tp>& pt);
    Size_& operator = (const Size_& sz);
    _Tp area() const;

    operator Size_() const;
    operator Size_() const;
    operator Size_() const;
    operator CvSize() const;
    operator CvSize2D32f() const;

    _Tp width, height;
};

typedef Size_ Size2i;
typedef Size2i Size;
typedef Size_ Size2f;

となっていて,cv::Size, cv::Size2iはint型,cv::Size2fはfloat型で定義されている.そして,それぞれメンバ width と height が矩形の幅と高さを表す.

今まで,矩形の大きさ(面積)を比較する場合,例えば

cv::Size a, b;

に対して,

if( a.width*a.height < b.width*b.height){ … }

としていたが,メンバ関数の area() があることに気づいた.これを使うと,

if( a.area() < b.area() ){ … }

と書くことができ,非常にスッキリと条件式が書けた.ちなみに, area() はcv::Size を表現する型が int 型なら int 型を, float 型なら float 型を返す関数になっている.

OpenCVのradiusSearch

今日OpenCVのradiusSearchを使っていてハマったのでメモ.

radiusSearchとは,「ある点(検索クエリ)から半径(radius)いくら以内にある点を探す」という処理.例えば,画像上にいくつかの特徴点があるときにある特徴点から距離r以内にある点を探す,とか,特徴空間中のある点から距離r以内にある点を探す,といったことに使う.実装上は,前者なら画像上の座標を,後者なら特徴空間中の座標=特徴量を各行の値として入れた行列(列数=点の数,行数=座標や特徴量の次元数)に対してIndexというものを作り,そのIndexを使ってradiusSearchを行う.

普通,何も考えずに上記の処理を行おうとすると,ある点と他の全ての点との距離を計算してソートし,上位のものを返せば良いが,それは計算量が大きい.特に何度もそういった処理を繰り返す場合.

これに対して,予め索引を作っておき,高速に検索できるようにするのがIndex.例えば,(Randomized) kd-treeや,Locality-Sensitive Hashing (LSH)などの手法がある.

以下ではRandomized kd-treeの使い方を簡単に説明する.ここで検索対象のデータは,cv::Mat型の変数dataに,前述のとおり,点数(行)×次元数(列)の行列として格納されているとする.この例では,各点から距離r内にある点のうち最大maxRadius個をすべて求める例である.

// 予めIndexを作成しておく.この例ではRandomized KDTreeをTree数4で
cv::flann::Index idx(data, cv::flann::KDTreeIndexParams(4), cvflann::FLANN_DIST_EUCLIDEAN);

int maxRadius=10; // 最大10個の点を求める
for(int i=0; i<data.rows; i++){
    cv::Mat_<int> indices; // dataの何行目か
    cv::Mat_<float> dists; // それぞれどれだけの距離だったか
    idx.radiusSearch(query, indices, dists, r, maxResults);
    for(int j=0;j<indices.row; j++){
        std::cout << indices(j,0) << "[" << dists.row(j) << "]" << " " << std::endl;
    }
}

こちらの公式ドキュメントでは引数が5個だったが,本当は5個目の引数にmaxResultsが必要(6個目の引数は省略可).

Fedora 20素晴らしい

先日メインマシンをFedora 19から20へアップグレードしたが,また新たに別マシンにFedoraをインストールすることになった.

インストールも非常に簡単.DVDを入れて起動するとGUIでLive DVDとして起動するかインストールするかを聞かれ,インストールを選択すると,インストールが始まる.

インストール時にやることといえば,言語の選択,パーティションの選択と,ユーザ作成ぐらい.あとは勝手に進む.最後に再起動してDVDを取り出せばOK.

画像処理の研究をやっているとOpenCVをよく使うが,開発環境のインストールも非常に簡単.

yum groupinstall "C 開発ツールとライブラリー"
yum install opencv-devel

でOK.これでmakeとかgccとか必要なツールと,OpenCVを使った開発に必要なライブラリがインストールされる.しかも,gccのバージョンはかなり新しい4.8.2で,OpenCVのバージョンも2.4.7(最新版は2.4.8)と嬉しい.また,Pythonも2.7.5が入っている.

CentOSで開発しようとすると,gccもOpenCVも古くて,まずgccをソースコードからコンパイルして入れ,さらにOpenCVをコンパイルしていたが,Fedora 20やっぱり素晴らしい.

LinuxでOpenCV使った開発はFedora 20がとても良いと思う.

apt-getでOpenCV

10月にリリースされた、Ubuntu 12.10を入れてみた。
毎回のことだが、OpenCVインストールなどの開発環境を整える作業。

sudo apt-get install libopencv- でTabキーを押したら、
libopencv-calib3d-dev libopencv-flann-dev libopencv-legacy2.3 libopencv-calib3d2.3 libopencv-flann2.3 libopencv-ml-dev
libopencv-contrib-dev libopencv-gpu-dev libopencv-ml2.3
libopencv-contrib2.3 libopencv-gpu2.3 libopencv-objdetect-dev
libopencv-core-dev libopencv-highgui-dev libopencv-objdetect2.3
libopencv-core2.3 libopencv-highgui2.3 libopencv-video-dev
libopencv-dev libopencv-imgproc-dev libopencv-video2.3
libopencv-features2d-dev libopencv-imgproc2.3
libopencv-features2d2.3 libopencv-legacy-dev

と表示された。libopencv-gpu-devとかも入ってる!?なので、

sudo apt-get install "libopencv-.*-dev"

で一発インストール。

Mac OSX (Mountain Lion) の homebrew で OpenCV のインストール

パッケージ管理システムのHomebrewを使って,OpenCVをインストールしたときのログです.
ここでは,Homebrewの設定は済んでいるものとします.
このへんのページを参考にしてください.

まず,OpenCVに必要なライブラリをインストールします.
必要なものは

  • TBB ( Intel Threading Building Block ) 並列化ライブラリ
  • Qt GUIライブラリ

です.
それぞれ

 brew install tbb
 brew install qt
 

でインストールします.

次に,OpenCVをインストールします.
これもbrewコマンドで一発ですが,上でインストールしたライブラリを有効にするためにオプションを追加します.

 brew install opencv --with-tbb --with-qt
 

僕の環境では,これではコンパイルが通りませんでした.
コンパイルにllvmを使うように指定します.

 brew install opencv --with-tbb --with-qt --use-llvm
 

これでコンパイルが通るかと思います.

僕はインストール前に,

 brew edit opencv
 

して,--with-qtの行を参考に,--with-cudaと--with-eigenというオプションを追加して,各ライブラリを有効にしたコンパイルをしています.