OpenCV 5.0.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 5.0.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
Sobel微分

前のチュートリアル: 画像への境界の追加
次のチュートリアル: Laplace演算子

原著者Ana Huamán
互換性OpenCV >= 3.0

目的

このチュートリアルでは、以下の方法を学ぶ:

  • OpenCVの関数 Sobel() を使って、画像から導関数を計算する。
  • OpenCVの関数 Scharr() を使って、サイズ \(3 \cdot 3\) のカーネルに対するより正確な導関数を計算する。

理論

覚え書き
以下の説明は、BradskiとKaehlerによる書籍 Learning OpenCV に基づくものである。
  1. 直前の2つのチュートリアルでは、畳み込みの応用例を見てきた。最も重要な畳み込みの一つが、画像における導関数(またはその近似)の計算である。
  2. なぜ画像において導関数の計算が重要になるのか。たとえば、画像中に存在する エッジ を検出したいとしよう。

エッジ では、ピクセル強度が顕著に 変化 することが容易に見て取れる。変化 を表現する良い方法は、導関数 を用いることである。勾配の大きな変化は、画像における大きな変化を示す。

  1. より視覚的に説明するために、1次元の画像を考えてみよう。エッジは、以下のプロットにおける強度の「ジャンプ」として現れる。
  1. エッジの「ジャンプ」は、1次導関数を取るとより容易に見て取れる(実際には、ここでは極大値として現れる)。
  1. 以上の説明から、画像中のエッジを検出する方法は、勾配が近傍よりも大きい(一般化すれば、しきい値よりも大きい)ピクセル位置を見つけることで実現できると推測できる。
  2. より詳細な説明については、BradskiとKaehlerによる Learning OpenCV を参照のこと。

Sobel演算子

  1. Sobel演算子は離散微分演算子である。画像強度関数の勾配の近似を計算する。
  2. Sobel演算子は、ガウシアン平滑化と微分を組み合わせたものである。

定式化

操作対象の画像を \(I\) とすると、

  1. 2つの導関数を計算する。

    1. 水平方向の変化: これは \(I\) を奇数サイズのカーネル \(G_{x}\) で畳み込むことで計算される。たとえばカーネルサイズが3の場合、\(G_{x}\) は次のように計算される。

    \[G_{x} = \begin{bmatrix} -1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{bmatrix} * I\]

    1. 垂直方向の変化: これは \(I\) を奇数サイズのカーネル \(G_{y}\) で畳み込むことで計算される。たとえばカーネルサイズが3の場合、\(G_{y}\) は次のように計算される。

    \[G_{y} = \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{bmatrix} * I\]

  2. 画像の各点で、上記2つの結果を組み合わせることで、その点における 勾配 の近似を計算する。

    \[G = \sqrt{ G_{x}^{2} + G_{y}^{2} }\]

    ただし、次のより単純な式が用いられることもある。

    \[G = |G_{x}| + |G_{y}|\]

覚え書き
When the size of the kernel is 3, the Sobel kernel shown above may produce noticeable inaccuracies (after all, Sobel is only an approximation of the derivative). OpenCV addresses this inaccuracy for kernels of size 3 by using the Scharr() function. This is as fast but more accurate than the standard Sobel function. It implements the following kernels:

\[G_{x} = \begin{bmatrix} -3 & 0 & +3 \\ -10 & 0 & +10 \\ -3 & 0 & +3 \end{bmatrix}\]

\[G_{y} = \begin{bmatrix} -3 & -10 & -3 \\ 0 & 0 & 0 \\ +3 & +10 & +3 \end{bmatrix}\]

この関数の詳細は、OpenCVリファレンスの Scharr() で確認できる。また、以下のサンプルコードでは、Sobel() 関数のコードの上に Scharr() 関数のコードがコメントアウトされていることに気づくだろう。そのコメントを外す(そしてもちろんSobelの部分をコメントアウトする)ことで、この関数がどのように動作するかを理解できるはずである。

コード

  1. What does this program do?
    • Sobel演算子 を適用し、検出された エッジ が暗い背景の上に明るく表示された画像を出力として生成する。
  2. チュートリアルのコードを以下の行に示す。

解説

変数の宣言

// First we declare the variables we are going to use
Mat image,src, src_gray;
Mat grad;
const String window_name = "Sobel Demo - Simple Edge Detector";
int ksize = parser.get<int>("ksize");
int scale = parser.get<int>("scale");
int delta = parser.get<int>("delta");
int ddepth = CV_16S;

元画像の読み込み

String imageName = parser.get<String>("@input");
// As usual we load our source image (src)
image = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
// Check if image is loaded fine
if( image.empty() )
{
printf("Error opening image: %s\n", imageName.c_str());
return EXIT_FAILURE;
}

ノイズの低減

// Remove noise by blurring with a Gaussian filter ( kernel size = 3 )
GaussianBlur(image, src, Size(3, 3), 0, 0, BORDER_DEFAULT);

グレースケール

// Convert the image to grayscale
cvtColor(src, src_gray, COLOR_BGR2GRAY);

Sobel演算子

Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y;
Sobel(src_gray, grad_x, ddepth, 1, 0, ksize, scale, delta, BORDER_DEFAULT);
Sobel(src_gray, grad_y, ddepth, 0, 1, ksize, scale, delta, BORDER_DEFAULT);
  • x 方向と y 方向の「導関数」を計算する。このために、以下に示すように Sobel() 関数を使用する。この関数は次の引数を取る。

    • src_gray: この例では入力画像。ここでは CV_8U である。
    • grad_x / grad_y : 出力画像。
    • ddepth: 出力画像のビット深度。オーバーフローを避けるため、CV_16S に設定する。
    • x_order: x 方向の導関数の階数。
    • y_order: y 方向の導関数の階数。
    • scaledeltaBORDER_DEFAULT: デフォルト値を使用する。

    x 方向の勾配を計算するには \(x_{order}= 1\) と \(y_{order} = 0\) を使うことに注意。y 方向についても同様に行う。

出力をCV_8U画像に変換する

// converting back to CV_8U
convertScaleAbs(grad_x, abs_grad_x);
convertScaleAbs(grad_y, abs_grad_y);

勾配

addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad);

両方向の勾配を加算することで 勾配 を近似する(これは決して厳密な計算ではないことに注意。しかし本目的には十分である)。

結果の表示

imshow(window_name, grad);
char key = (char)waitKey(0);

結果

  1. 基本的な検出器を lena.jpg に適用した出力を以下に示す。