2013年3月22日金曜日

cv::GaussianBlur の使い方


cv::Mat src = cv::Mat::zeros(15,15,CV_16U);
src.at<ushort>(7,7) = cv::saturate_cast<ushort>(10000);
std::cout<<src<<std::endl;

cv::Mat src1 = cv::Mat::zeros(15,15,CV_16U);
src1.at<ushort>(7,7) = cv::saturate_cast<ushort>(10000);
cv::GaussianBlur(src1,src1,cv::Size(15,15),0.5,0,4);
std::cout<<"sigma = 0.5"<<std::endl<<src1<<std::endl;

cv::Mat src2 = cv::Mat::zeros(15,15,CV_16U);
src2.at<ushort>(7,7) = cv::saturate_cast<ushort>(10000);
cv::GaussianBlur(src2,src2,cv::Size(15,15),1.0,0,4);
std::cout<<"sigma = 1.0"<<std::endl<<src2<<std::endl;

cv::Mat src3 = cv::Mat::zeros(15,15,CV_16U);
src3.at<ushort>(7,7) = cv::saturate_cast<ushort>(10000);
cv::GaussianBlur(src3,src3,cv::Size(15,15),2.0,0,4);
std::cout<<"sigma = 2.0"<<std::endl<<src3<<std::endl;
http://ja.wikipedia.org/wiki/%E3%82%AC%E3%82%A6%E3%82%B9%E9%96%A2%E6%95%B0
二次元のガウス関数をかける方法である。結果を下に記述する

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 10000, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sigma = 0.5
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 113, 837, 113, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 2, 837, 6187, 837, 2, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 113, 837, 113, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sigma = 1.0
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 2, 11, 18, 11, 2, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 2, 29, 131, 215, 131, 29, 2, 0, 0, 0, 0;
  0, 0, 0, 0, 11, 131, 585, 965, 585, 131, 11, 0, 0, 0, 0;
  0, 0, 0, 1, 18, 215, 965, 1592, 965, 215, 18, 1, 0, 0, 0;
  0, 0, 0, 0, 11, 131, 585, 965, 585, 131, 11, 0, 0, 0, 0;
  0, 0, 0, 0, 2, 29, 131, 215, 131, 29, 2, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 2, 11, 18, 11, 2, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sigma = 2.0
[0, 0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0, 0;
  0, 0, 0, 1, 1, 3, 4, 4, 4, 3, 1, 1, 0, 0, 0;
  0, 0, 1, 2, 6, 11, 15, 17, 15, 11, 6, 2, 1, 0, 0;
  0, 1, 2, 7, 17, 33, 48, 54, 48, 33, 17, 7, 2, 1, 0;
  1, 1, 6, 17, 42, 78, 114, 129, 114, 78, 42, 17, 6, 1, 1;
  1, 3, 11, 33, 78, 146, 213, 241, 213, 146, 78, 33, 11, 3, 1;
  2, 4, 15, 48, 114, 213, 310, 351, 310, 213, 114, 48, 15, 4, 2;
  2, 4, 17, 54, 129, 241, 351, 398, 351, 241, 129, 54, 17, 4, 2;
  2, 4, 15, 48, 114, 213, 310, 351, 310, 213, 114, 48, 15, 4, 2;
  1, 3, 11, 33, 78, 146, 213, 241, 213, 146, 78, 33, 11, 3, 1;
  1, 1, 6, 17, 42, 78, 114, 129, 114, 78, 42, 17, 6, 1, 1;
  0, 1, 2, 7, 17, 33, 48, 54, 48, 33, 17, 7, 2, 1, 0;
  0, 0, 1, 2, 6, 11, 15, 17, 15, 11, 6, 2, 1, 0, 0;
  0, 0, 0, 1, 1, 3, 4, 4, 4, 3, 1, 1, 0, 0, 0;
  0, 0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0, 0]

ガウス関数型に分布しているのが分かるだろう。
半値半幅は1.17 × sigma になる。

2013年3月21日木曜日

cv::Matを初期化する方法


void Initialize()
{
// 要素がすべて 100 の width160, height100 の画像
Mat src1 = Mat::ones(100,160,CV_8U) * 100;
imshow("src1", src1);
waitKey(0);

// 要素がすべて 0 の width160, height100 の画像
Mat src2 = Mat::zeros(cv::Size(160,100), CV_8U);
imshow("src2", src2);
waitKey(0);

// 定数で初期化する1
Mat src3(100,160,CV_8U, cv::Scalar(100));
imshow("src3", src3);
waitKey(0);

// 定数で初期化する2
Mat src4(100,160,CV_8U, cv::Scalar(100));
src4 = cv::Scalar(100);
imshow("src4", src4);
waitKey(0);

// 定数で初期化する3
Mat src5 = Mat::ones(100,160,CV_8U);
src5 *= 100;
imshow("src5", src5);
waitKey(0);

// 定数で初期化する4
Mat src6 = Mat::zeros(100,160,CV_8U);
src6 += 100;
imshow("src6", src6);
waitKey(0);

// Cの配列で初期化する1
uchar data1[100*160];
for(int i=0;i<100*160;i++)
{
data1[i] = uchar(i%255);
}
Mat src7(100,160,CV_8U,data1);
CV_Assert(src7.refcount == NULL);
imshow("src7", src7);
waitKey(0);

// Cの配列で初期化する2
float data2[100*160];
for(int i=0;i<100*160;i++)
{
data2[i] = float((i%255)/255.0);
}
Mat src8(100,160,CV_32F,data2);
CV_Assert(src8.refcount == NULL);
imshow("src8", src8);
waitKey(0);
};

初期化する方法を8個記述した。

8bitの要素を持つモノクロの画像は、CV_8Uというtypeで表される。
32bit単精度浮動小数点数(float)の要素を持つモノクロ画像は、CV_32Fというtypeで表される。
画像の世界でよく使われるのは、1チャンネル(モノクロ)では以下のtypeが挙げられる。
uint8_t = unsigned char = CV_8U
uint16_t = unsigned short = CV_16U

cv::Matで作られたインスタンスは 「*=」や「+=」の演算子を使うことが可能である。
http://docs.opencv.org/modules/core/doc/basic_structures.html#matrixexpressions

Cの配列で初期化するときは、CV_Assertが必要らしい。

私が初めにつまづいたのは、最初の例の画像の幅(width)と高さ(height)の順番である。
私の知りうる多くの参考書には、width、heightの順に紹介されており、Matクラスもそうなっているものと思い込んでいた(頃があった)。Matクラスでwidthとheightをそれぞれ指定するときは、rows(行、高さ、height)、cols(列、幅、width)の順番で指定しなければならない。Matクラスは本来行・列のためのクラスなのだから、当然と言えば当然である。

ちなみに、2つ目の例にあるように、Sizeクラスを使って指定するときは、width方向(cols、列)、height方向(rows、行)の順である。後に出てくるPointクラスを使う時も同じである。注意してほしい。

2013年3月5日火曜日

OpenCV を Visual Studio で使う方法の解説

OpenCV を Visual Studio で使う方法
http://physics-station.blogspot.jp/2013/03/opencv-visual-studio.html
の解説です。最終更新:11月19日

OpenCVは非常に強力な画像処理ライブラリであり、一般的な画像処理はこのライブラリをつかえばほぼ実現できます。

いずれのライブラリにも言えることですが、導入が面倒であったり、日本語の解説サイトが少ない又は古いということがよくあります。OpenCVも導入編を解説したサイトの多くが、CMakeを使っていたり、インクルードファイルの設定が面倒だったりと、初心者にとっては辛いサイトが多い気がしていました。

なので、OpenCVの初心者でも間違いなく一発で導入できることを目標に、上の「OpenCV を Visual Studio で使う方法」を書きました(が、これでも難しい人もいるでしょう)。ここでは簡略化のために説明しきれなかった情報の補足をします。

OpenCVは執筆段階のver2.4.7で4つのプラットフォームに対応しています。また、言語もC、C++、Phytonなどに対応しています。ここでは、VC++を使う観点から、全てのコードにおいてC++のコードを使います。OpenCVでは、「cv」+大文字から始まるCの関数は使わず、名前空間cvと小文字から始まるC++の関数を使います。

opencv.orgで配布されているOpenCVのライブラリには、VC++ですぐに使えるダイナミックリンクライブラリ「.dll」が含まれています。以前はCMakeなど自分で「.dll」を作成しなければなりませんでしたが、今はその必要はありません。ただし、OpenNIやICCを使った「.dll」を作成するときなどは、CMakeで作成する方法は有効です。

2.ライブラリの展開

ライブラリの展開場所ですが、C:\以外でも構いません。この後の設定の内容を必要に応じて変えれば問題ありません。

3.静的リンクライブラリの設定

静的リンクライブラリの設定は、pragmaというプリプロセッサ命令で解決します。VCに直接指定することもできますし、毎回pragmaを書く方法もありますが、pragmaが書かれたファイルを作成し、そのファイルを使いまわすことが、最も簡単で説明しやすい方法だと思っています。

ここで、ファイル名の最後の方にある「247」は、opencvのバージョンによって違うので、バージョンが上がるたびに(筆者かあなたが)書き換える必要があります。バージョンによって梱包されている.libの名前が違うことがあるので、もし動かない場合はバージョン2.4.7を入れてみてください。

4.動的リンクライブラリの設定

動的リンクライブラリ(ダイナミックリンクライブラリ)はWindowsにPathを通す必要があります。

VS2008 32bitの場合
C:\opencv\build\x86\vc9\bin

VS2008 64bit の場合
C:\opencv\build\x64\vc10\bin

VS2010 or VS2012 32bit の場合
C:\opencv\build\x86\vc10\bin

VS2010 or VS2012 64bit の場合
C:\opencv\build\x64\vc10\bin

バージョン2.4.7ではVS2010とVS2012に対応しています。VS2008には対応しなくなったようです。

5.VisualStudioの設定

VisualStudioの設定については、いくつかの流儀があるように思います。Userファイルに設定する方法は、表からは見えない場所に各種設定をしてしまうため、再インストールしたときなどに行方不明になる可能性大です(というわけで、筆者はこの方法は使っていません)。プロジェクトを作るときに(面倒だけど)毎回能動的に設定する方法で記述しています。

ここまで設定してきたインクルードファイル、静的リンクライブラリ(ライブラリ)をVSに設定することになります。動的リンクライブラリはWindowsに設定したのでVSに設定する必要はありません。また、VSを起動した状態で動的リンクライブラリを設定したときは、VSを再起動する必要があります。

6.サンプルプログラムの作成

ここまでの作業が問題ないかを確認するための大事な作業です。プログラミングの業界ではおなじみのHello World!を画像上に描写しそれを表示するプログラムにしました。
解説は以上です。

最後に、OpenCV歴が短い筆者ではありますが、この強力なライブラリを一人でも多くの人が楽に使えることを願って、また拙筆かつ遅筆をご容赦いただければと思います。