2012年4月24日火曜日

エッジを検出する その3 - OpenCV for Android


続いてCannyフィルタです。

ガウシアンフィルタとSobelフィルタを組み合わせてエッジを検出します。
まず、ガウシアンフィルタでぼけた画像を作り、その画像にSobelフィルタをかけることでエッジを検出します。
実際には処理はそれだけではなく、色々とやっているようです。

Cannyフィルタを利用するには、Imgproc.Canny()メソッドを利用します。

Imgproc.Canny(Mat image, Mat edges, double threshold1,
double threshold2, int apertureSize, boolean L2gradient)

Mat src          処理したい元画像のMat
Mat edges        変換後Mat
double threshold1   第1閾値
double threshold2   第2閾値
int apertureSize    Sobelのアパーチャサイズ
boolean L2gradient  L2ノルム利用有無


threshold1、threshold2については、値が小さいほうがエッジ同士を接続するために用いられ、大きいほうが強いエッジの初期検出に用いられます。
順番を変えても結果は同じです。

apertureSizeについては、デフォルトが3で、3、5、7が選択出来ます。

L2gradientについては、画像勾配の強度を求めるために、精度の高いL2ノルムを利用するか、高速なL1ノルムを利用するかを設定します。
デフォルトではL1ノルム(false)です。


それでは、実際にやってみましょう。

元画像です。


Imgproc.Canny(mat, dstMat, 50, 200);


Imgproc.Canny(mat, dstMat, 100, 200);


Imgproc.Canny(mat, dstMat, 100, 300);


Imgproc.Canny(mat, dstMat, 50, 200, 5);


Imgproc.Canny(mat, dstMat, 50, 200, 7);


Imgproc.Canny(mat, dstMat, 100, 200, 3, false);


Imgproc.Canny(mat, dstMat, 100, 200, 3, true);


ちなみに平滑化⇒Sobelで同じような結果になるんでしょうか?
Imgproc.GaussianBlur(mat, mat, new Size(3, 3), 0, 0);
Imgproc.Sobel(mat, dstMat, mat.type(), 1, 1, 5);


そう単純では無いようですねw


エッジを検出する その2 - OpenCV for Android


続いてラプラシアンフィルタです。

ラプラシアンフィルタは2次微分を計算するフィルタで、Imgproc.Laplacian()メソッドを利用します。

Imgproc.Laplacian(Mat src, Mat dst, int ddepth,
int ksize, double scale, double delta, int borderType)

Mat src     処理したい元画像のMat
Mat dst     変換後Mat
int ddepth    変換後のビット深度
int ksize     カーネルサイズ
double scale  計算されたデリバティブの値の任意のスケールファクタ
double delta  オプションのデルタ値
int borderType ピクセル外挿手法


引数はその1でやったImgproc.Sobel()に出てきたものと同じです。

それでは、実際にやってみましょう。

おなじみの元画像です。


Imgproc.Laplacian(mat, dstMat, mat.type(), 1);



ksizeを変更してみます。
Imgproc.Laplacian(mat, dstMat, mat.type(), 7);



scaleを設定してみます。
Imgproc.Laplacian(mat, dstMat, mat.type(), 1, 50.0);



deltaを設定してみます。
Imgproc.Laplacian(mat, dstMat, mat.type(), 1, 1.0, 100.0);


x方向、y方向が無いので、Imgproc.Sobel()より単純ですね。


2012年4月15日日曜日

エッジを検出する その1 - OpenCV for Android


いよいよ画像解析っぽくなってきました。

エッジ検出とは、画素の明るさが急激に変化する場所を探し、画像の輪郭を算出する処理を指します。
明るさの変化は、微分演算を利用することで算出し、グラディエント(1次微分)とラプラシアン(2次微分)があります。

1次微分を計算するSobelフィルタを利用するには、Imgproc.Sobel()メソッドを利用します。

imgproc.Imgproc.Sobel(Mat src, Mat dst, int ddepth,
int dx, int dy, int ksize, double scale, double delta, int borderType))

Mat src     処理したい元画像のMat
Mat dst     変換後Mat
int ddepth    変換後のビット深度
int dx       dx
int dy       dy
int ksize     カーネルサイズ
double scale  計算されたデリバティブの値の任意のスケールファクタ
double delta  オプションのデルタ値
int borderType ピクセル外挿手法

dx、dyについては、ksizeが1の場合を除き、必ずksize未満である必要があります。
ksizeが1の場合は、3 x 1もしくは1 x 3のカーネルが利用されますので、dx、dyの最大値は2です。

ksizeについては、ksizeが1の場合を除き、導関数を計算するためにksize × ksizeのカーネルが利用されます。
ksizeは奇数かつ31以下である必要があります。

ksizeが特殊な値「Imgproc.CV_SCHARR」の場合、3×3のSobelフィルタより精度が高い、3×3のScharrフィルタに対応します。
Scharrフィルタを利用する場合、dxかdyのどちらかが1、どちらかが0である必要があります。


それでは、実際にやってみましょう。

グレースケールに変換してから処理します。

まずは元画像。


Imgproc.Sobel(mat, dstMat, mat.type(), 1, 0, 1);


dxを変更してみます。
Imgproc.Sobel(mat, dstMat, mat.type(), 2, 0, 1);


ドキュメントには、このメソッドはほとんどの場合(dx = 1, dy = 0, ksize = 3)もしくは(dx = 0, dy = 1, ksize = 3) の引数で呼び出されるとあるので、それぞれ試してみます。
Imgproc.Sobel(mat, dstMat, mat.type(), 1, 0, 3);


Imgproc.Sobel(mat, dstMat, mat.type(), 0, 1, 3);


ksizeを変更してみます。
Imgproc.Sobel(mat, dstMat, mat.type(), 1, 1, 7);


Imgproc.Sobel(mat, dstMat, mat.type(), 1, 1, 21);


Scharrフィルタを利用してみます。
Imgproc.Sobel(mat, dstMat, mat.type(), 1, 0,
  Imgproc.CV_SCHARR);


Imgproc.Sobel(mat, dstMat, mat.type(), 0, 1,
  Imgproc.CV_SCHARR);


scaleを変更してみます。
Imgproc.Sobel(mat, dstMat, mat.type(), 1, 1, 3, 50.0);


deltaを変更してみます。
Imgproc.Sobel(mat, dstMat, mat.type(), 1, 1, 3, 1.0,
  100.0);


ところで微分って何?おいしいの?