特徴検出

cv::Canny

void Canny(const Mat& image, Mat& edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false)

Cannyアルゴリズムを用いて,画像のエッジを検出します.

パラメタ:
  • image – 8ビット,シングルチャンネルの入力画像
  • edges – 出力されるエッジのマップ. image と同じサイズ,同じ型
  • threshold1 – ヒステリシスが存在する処理の,1番目の閾値
  • threshold2 – ヒステリシスが存在する処理の,2番目の閾値
  • apertureSizeSobel() オペレータのアパーチャサイズ
  • L2gradient – 画像勾配の強度を求めるために,より精度の高い L_2 ノルム =\sqrt{(dI/dx)^2 + (dI/dy)^2} を利用するか, L_1 ノルム =|dI/dx|+|dI/dy| で十分( L2gradient=false )かを指定します

関数 Canny は,Cannyアルゴリズムを用いて入力画像 image 中のエッジを検出し,マップ edges に出力します. threshold1threshold2 の内,小さい方の値がエッジの接続に利用され,大きい方の値が明確なエッジの初期セグメントを検出するのに利用されます.詳しくは, http://en.wikipedia.org/wiki/Canny_edge_detector を参照してください.

cv::cornerEigenValsAndVecs

void cornerEigenValsAndVecs(const Mat& src, Mat& dst, int blockSize, int apertureSize, int borderType=BORDER_DEFAULT)

コーナーを検出するために,画像ブロックの固有値と固有ベクトルを求めます.

パラメタ:
  • src – 8ビットまたは浮動小数点型,シングルチャンネルの入力画像
  • dst – 結果を格納する画像. src と同じサイズで,型は CV_32FC(6)
  • blockSize – 近傍領域のサイズ(説明を参照してください)
  • apertureSizeSobel() オペレータで利用するアパーチャパラメータ
  • borderType – ピクセル外挿手法. borderInterpolate() を参照してください

関数 cornerEigenValsAndVecs は,各ピクセル p に対して, blockSize \times blockSize サイズの近傍領域 S(p) を考えます.そして,この近傍領域の微分画像の共分散行列を次のように求めます:

M =  \begin{bmatrix} \sum _{S(p)}(dI/dx)^2 &  \sum _{S(p)}(dI/dx dI/dy)^2  \\ \sum _{S(p)}(dI/dx dI/dy)^2 &  \sum _{S(p)}(dI/dy)^2 \end{bmatrix}

ここで,微分画像は Sobel() オペレータを利用して求められます.

その後,共分散行列 M の固有値と固有ベクトルを求め,それを出力画像に (\lambda_1, \lambda_2, x_1, y_1, x_2, y_2) という形式で格納します.ここで,

  • [ \lambda_1, \lambda_2 ] M の固有値.格納されない.
  • [ x_1, y_1 ] \lambda_1 に対する固有ベクトル
  • [ x_2, y_2 ] \lambda_2 に対する固有ベクトル

となります.

この関数の出力は,ロバストなエッジ検出やコーナー検出に利用できます.

参考: cornerMinEigenVal() , cornerHarris() , preCornerDetect()

cv::cornerHarris

void cornerHarris(const Mat& src, Mat& dst, int blockSize, int apertureSize, double k, int borderType=BORDER_DEFAULT)

Harris エッジ検出器.

パラメタ:
  • src – 8ビットまたは浮動小数点型,シングルチャンネルの入力画像
  • dst – Harris検出器の応答が格納される画像.型は CV_32FC1 で, src と同じサイズ
  • blockSize – 近傍領域のサイズ( cornerEigenValsAndVecs() の説明を参照してください)
  • apertureSizeSobel() オペレータで利用するアパーチャパラメータ
  • k – Harris 検出器のフリーパラメータ.以下の式を参照してください
  • borderType – ピクセル外挿手法. borderInterpolate() を参照してください

関数 cornerHarris は,画像に対して Harris エッジ検出器を適用します. cornerMinEigenVal()cornerEigenValsAndVecs() と同様に, \texttt{blockSize} \times \texttt{blockSize} サイズの近傍領域全体に渡って勾配の積和を計算することで,ピクセル (x, y) における 2\times2 の勾配共分散行列 M^{(x,y)} を求めます.次に,そこから次の特徴量を計算します:

\texttt{dst} (x,y) =  \mathrm{det} M^{(x,y)} - k  \cdot \left ( \mathrm{tr} M^{(x,y)} \right )^2

画像中のコーナーは,この応答マップの極大点として検出されます.

cv::cornerMinEigenVal

void cornerMinEigenVal(const Mat& src, Mat& dst, int blockSize, int apertureSize=3, int borderType=BORDER_DEFAULT)

コーナーを検出するために,勾配行列の最小の固有値を求めます.

パラメタ:
  • src – 8ビットまたは浮動小数点型,シングルチャンネルの入力画像
  • dst – 最小固有値が格納される画像.型は CV_32FC1 で, src と同じサイズ
  • blockSize – 近傍領域のサイズ( cornerEigenValsAndVecs() の説明を参照してください)
  • apertureSizeSobel() オペレータで利用するアパーチャパラメータ
  • borderType – ピクセル外挿手法. borderInterpolate() を参照してください

関数 cornerMinEigenVal は, cornerEigenValsAndVecs() と似ていますが,微分画像の共分散行列の最小固有値だけを求めます.つまり, cornerEigenValsAndVecs() の説明にある式の \min(\lambda_1, \lambda_2) だけを求めます.

cv::cornerSubPix

void cornerSubPix(const Mat& image, vector<Point2f>& corners, Size winSize, Size zeroZone, TermCriteria criteria)

コーナー位置を高精度化します.

パラメタ:
  • image – 入力画像
  • corners – 入力コーナーの初期座標.これが高精度化されたものが,再びここに出力されます
  • winSize – 探索窓のサイズの半分.例えば winSize=Size(5,5) の場合,5*2+1 \times 5*2+1 = 11 \times 11 サイズの探索窓が利用されます
  • zeroZone – 探索領域の中心に存在する対象外領域(後述の式において総和を計算する際に含まれない)の半分のサイズ.この値は,自己相関行列において発生しうる特異点を避けるために用いられます. 値が (-1,-1) の場合は,そのようなサイズはないということを意味します
  • criteria – コーナー位置高精度化のための繰り返し処理の停止基準.つまり,この繰り返し処理は,規定回数に達するか要求精度に達したときに終了します. criteria は,最大反復数と要求精度のどちらか,あるいは両方を指定します

関数 cornerSubPix は,以下の図に示されるような,サブピクセル精度のコーナー,あるいは鞍点を検出するために繰り返し処理を行います.

_images/cornersubpix.png

サブピクセル精度のコーナー位置決めは,近傍領域の中心 q から,その領域内に位置する点 p に向かう各ベクトルが, p における(画像自身と観測ノイズに従う)画像勾配と直交する,という考えに基づいています.これは,以下の式で表現されます:

\epsilon _i = {DI_{p_i}}^T  \cdot (q - p_i)

ここで {DI_{p_i}} は,近傍領域 q 内の点 p_i における画像勾配を表します. q は,この \epsilon_i を最小にする値として求められます. \epsilon_i を 0 とすることで連立方程式が得られます:

\sum _i(DI_{p_i}  \cdot {DI_{p_i}}^T) -  \sum _i(DI_{p_i}  \cdot {DI_{p_i}}^T  \cdot p_i)

ここで,画像勾配は q の近傍領域(「探索窓」)での総和をとられます.1次勾配を G ,2次勾配を b とすると,以下の関係が得られます:

q = G^{-1}  \cdot b

このアルゴリズムは,探索窓の中心をこの新しい値 q に再設定し,値の変化量が与えられた閾値内に収まるようになるまで繰り返し計算を行います.

cv::goodFeaturesToTrack

void goodFeaturesToTrack(const Mat& image, vector<Point2f>& corners, int maxCorners, double qualityLevel, double minDistance, const Mat& mask=Mat(), int blockSize=3, bool useHarrisDetector=false, double k=0.04)

画像内の強いコーナーを検出します.

パラメタ:
  • image – 8ビットまたは浮動小数点型,シングルチャンネルの入力画像
  • corners – 検出されたコーナーが出力されるベクトル
  • maxCorners – 出力されるコーナーの最大数.これより多い数のコーナーが検出された場合,より強いコーナーが出力されます
  • qualityLevel – 許容される画像コーナーの最低品質を決定します.このパラメータ値を,最良のコーナーを示す測度( cornerMinEigenVal() で述べた最小固有値や, cornerHarris() で述べた Harris 関数の応答)に乗じます.その掛け合わされた値よりも品質度が低いコーナーは,棄却されます.例えば,コーナーの最高品質度 = 1500, qualityLevel=0.01 である場合,品質度が15より小さいすべてのコーナーが棄却されます
  • minDistance – 出力されるコーナー間で許容される,最小ユークリッド距離
  • mask – オプションのROI.これが空ではない場合(型は CV_8UC1 で,サイズは image と同じ必要があります),それはコーナーの検出対象となる領域を示します
  • blockSize – ピクセル近傍領域における微分画像の共分散行列を求める際に利用されます,平均化ブロックサイズ. cornerEigenValsANdVecs() を参照してください
  • useHarrisDetector – オペレータ,または cornerMinEigenVal() のどちらを利用するかを示します
  • k – Harris 検出器のフリーパラメータ

この関数は, Shi94 で述べられるように,画像中または画像の指定領域内の最も強いコーナーを検出します:

  1. この関数はまず, cornerMinEigenVal() または cornerHarris() を利用して,入力画像の全ピクセルにおけるコーナー品質度を求めます.
  2. 次に,non-maxima suppression を行います(つまり 3\times 3 の近傍領域における極大値のみを残します)
  3. 次のステップでは, \texttt{qualityLevel} \cdot \max_{x,y} qualityMeasureMap(x,y) よりも小さい最小固有値をもつコーナーを棄却します.
  4. 残ったコーナーは,品質度の高い順番に保持されます.
  5. 最後に,この関数は,距離 minDistance 以内に,自分よりも強いコーナー pt_i ( i < j ) が存在するようなコーナー pt_j を削除します.

この関数は,点ベースの物体追跡器を初期化するために利用できます.

この関数が,パラメータ qualityLevel に異なる値 AB を与えられて呼び出され,その時に A > {B} であるとします.その場合,出力コーナー配列において qualityLevel=A で求められたコーナーの配列が, qualityLevel=B で求められたものよりも前に位置することに注意してください.

参考: cornerMinEigenVal() , cornerHarris() , calcOpticalFlowPyrLK() , estimateRigidMotion() , PlanarObjectDetector() , OneWayDescriptor()

cv::HoughCircles

void HoughCircles(Mat& image, vector<Vec3f>& circles, int method, double dp, double minDist, double param1=100, double param2=100, int minRadius=0, int maxRadius=0)

ハフ変換を用いて,グレースケール画像から円を検出します.

パラメタ:
  • image – 8ビット,シングルチャンネル,グレースケールの入力画像
  • circles – 検出された円を出力するベクトル.各ベクトルは,3要素の浮動小数点型ベクトル (x, y, radius) としてエンコードされます
  • method – 現在のところ, CV_HOUGH_GRADIENT メソッドのみが実装されている.基本的には 2段階ハフ変換 で,これについては Yuen90 で述べられています
  • dp – 画像分解能に対する投票分解能の比率の逆数.例えば, dp=1 の場合は,投票空間は入力画像と同じ分解能をもちます.また dp=2 の場合は,投票空間の幅と高さは半分になります
  • minDist – 検出される円の中心同士の最小距離.このパラメータが小さすぎると,正しい円の周辺に別の円が複数誤って検出されることになります.逆に大きすぎると,検出できない円がでてくる可能性があります
  • param1 – 手法依存の1番目のパラメータ. CV_HOUGH_GRADIENT の場合は, Canny() エッジ検出器に渡される2つの閾値の内,大きい方の閾値を表します(小さい閾値は,この値の半分になります)
  • param2 – 手法依存の2番目のパラメータ. CV_HOUGH_GRADIENT の場合は,円の中心を検出する際の投票数の閾値を表します.これが小さくなるほど,より多くの誤検出が起こる可能性があります.より多くの投票を獲得した円が,最初に出力されます
  • minRadius – 円の半径の最小値
  • maxRadius – 円の半径の最大値

関数 houghCircles は,ハフ変換の改良版を用いてグレースケール画像から円を検出します.ここでは,簡単な利用例を示します:

#include <cv.h>
#include <highgui.h>
#include <math.h>

using namespace cv;

int main(int argc, char** argv)
{
    Mat img, gray;
    if( argc != 2 && !(img=imread(argv[1], 1)).data)
        return -1;
    cvtColor(img, gray, CV_BGR2GRAY);
    // 平滑化を行います.これがないと誤検出が起こりやすくなります.
    GaussianBlur( gray, gray, Size(9, 9), 2, 2 );
    vector<Vec3f> circles;
    HoughCircles(gray, circles, CV_HOUGH_GRADIENT,
                 2, gray->rows/4, 200, 100 );
    for( size_t i = 0; i < circles.size(); i++ )
    {
         Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
         int radius = cvRound(circles[i][2]);
         // 円の中心を描画します.
         circle( img, center, 3, Scalar(0,255,0), -1, 8, 0 );
         // 円を描画します.
         circle( img, center, radius, Scalar(0,0,255), 3, 8, 0 );
    }
    namedWindow( "circles", 1 );
    imshow( "circles", img );
    return 0;
}

通常,この関数は円の中心を上手く検出しますが,円の半径については失敗することもある点に注意してください.もし事前知識があれば,半径の範囲( minRadiusmaxRadius )を指定することで,これの補助ができます.または,出力された半径は無視して中心だけを利用し,別の追加処理によって正しい半径を求めることもできます.

参考: fitEllipse() , minEnclosingCircle()

cv::HoughLines

void HoughLines(Mat& image, vector<Vec2f>& lines, double rho, double theta, int threshold, double srn=0, double stn=0)

標準ハフ変換を用いて,2値画像から直線を検出します.

パラメタ:
  • image – 8ビット,シングルチャンネルの2値入力画像.この画像は関数により書き換えられる可能性があります
  • lines – 検出された直線が出力されるベクトル.各直線は,2要素のベクトル (\rho, \theta) で表現されます. \rho は原点(画像の左上コーナー)からの距離, \theta はラジアン単位で表される直線の回転角度(0 \sim 垂直線, \pi/2 \sim 水平線)です
  • rho – ピクセル単位で表される投票空間の距離分解能
  • theta – ラジアン単位で表される投票空間の角度分解能
  • threshold – 投票の閾値パラメータ.十分な票( >\texttt{threshold} )を得た直線のみが出力されます
  • srn – マルチスケールハフ変換において,距離分解能 rho の除数となる値.投票空間の粗い距離分解能は rho となり,細かい分解能は rho/srn となります.もし srn=0 かつ stn=0 の場合は,古典的ハフ変換が利用されます.そうでない場合は,両方のパラメータが正値である必要があります
  • stn – マルチスケールハフ変換において,角度分解能 theta の除数となる値

関数 HoughLines は,直線検出のためのハフ変換,またはマルチスケールハフ変換の実装です.コードの例については, HoughLinesP() を参照してください.

cv::HoughLinesP

void HoughLinesP(Mat& image, vector<Vec4i>& lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0)

確率的ハフ変換を利用して,2値画像から線分を検出します.

パラメタ:
  • image – 8ビット,シングルチャンネルの2値入力画像.この画像は関数により書き換えられる可能性があります
  • lines – 検出された線分が出力されるベクトル.各線分は,4要素のベクトル (x_1, y_1, x_2, y_2) で表現されます.ここで (x_1,y_1) および (x_2, y_2) は,検出された各線分の端点です
  • rho – ピクセル単位で表される投票空間の距離分解能
  • theta – ラジアン単位で表される投票空間の角度分解能
  • threshold – 投票の閾値パラメータ.十分な票( >\texttt{threshold} )を得た直線のみが出力されます
  • minLineLength – 最小の線分長.これより短い線分は棄却されます
  • maxLineGap – 2点が同一線分上にあると見なす場合に許容される最大距離

関数 HoughLinesP は, Matas00 で述べられている,直線検出のための確率的ハフ変換アルゴリズムの実装です.以下に直線検出の例を示します:

/* これは,単独で動作するプログラムです.プログラムの最初の引数として画像ファイル名を渡します.
   "#if 1" を "#if 0" に変更したり戻したりすることで,
   標準的ハフ変換と確率的ハフ変換を切り替えることができます.  */
#include <cv.h>
#include <highgui.h>
#include <math.h>

using namespace cv;

int main(int argc, char** argv)
{
    Mat src, dst, color_dst;
    if( argc != 2 || !(src=imread(argv[1], 0)).data)
        return -1;

    Canny( src, dst, 50, 200, 3 );
    cvtColor( dst, color_dst, CV_GRAY2BGR );

#if 0
    vector<Vec2f> lines;
    HoughLines( dst, lines, 1, CV_PI/180, 100 );

    for( size_t i = 0; i < lines.size(); i++ )
    {
        float rho = lines[i][0];
        float theta = lines[i][1];
        double a = cos(theta), b = sin(theta);
        double x0 = a*rho, y0 = b*rho;
        Point pt1(cvRound(x0 + 1000*(-b)),
                  cvRound(y0 + 1000*(a)));
        Point pt2(cvRound(x0 - 1000*(-b)),
                  cvRound(y0 - 1000*(a)));
        line( color_dst, pt1, pt2, Scalar(0,0,255), 3, 8 );
    }
#else
    vector<Vec4i> lines;
    HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 );
    for( size_t i = 0; i < lines.size(); i++ )
    {
        line( color_dst, Point(lines[i][0], lines[i][1]),
            Point(lines[i][2], lines[i][3]), Scalar(0,0,255), 3, 8 );
    }
#endif
    namedWindow( "Source", 1 );
    imshow( "Source", src );

    namedWindow( "Detected Lines", 1 );
    imshow( "Detected Lines", color_dst );

    waitKey(0);
    return 0;
}

サンプル画像.上記のプログラムはこの画像用にパラメータを調整しています:

_images/building.jpg

上記のプログラムにおいて確率的ハフ変換を行った結果:

_images/houghp.png

cv::preCornerDetect

void preCornerDetect(const Mat& src, Mat& dst, int apertureSize, int borderType=BORDER_DEFAULT)

コーナー検出のための特徴マップを求めます.

パラメタ:
  • src – 8ビットまたは浮動小数点型,シングルチャンネルの入力画像
  • dst – タイプが CV_32Fsrc と同じサイズの出力画像
  • apertureSizeSobel() のアパーチャサイズ
  • borderType – ピクセル外挿手法. borderInterpolate() を参照してください

関数 preCornerDetect は,入力画像の,複合偏微分に基づいた写像を求めます.

\texttt{dst} = (D_x  \texttt{src} )^2  \cdot D_{yy}  \texttt{src} + (D_y  \texttt{src} )^2  \cdot D_{xx}  \texttt{src} - 2 D_x  \texttt{src} \cdot D_y  \texttt{src} \cdot D_{xy}  \texttt{src}

ここで D_x , D_y は画像の1次微分, D_{xx} , D_{yy} は画像の2次微分, D_{xy} は混合微分です.

次に示すように,コーナーは,この写像の極大値として得られます:

Mat corners, dilated_corners;
preCornerDetect(image, corners, 3);
// 3x3 の矩形構造要素で膨張させます.
dilate(corners, dilated_corners, Mat(), 1);
Mat corner_mask = corners == dilated_corners;

KeyPoint

KeyPoint

salient point 検出器のためのデータ構造.

KeyPoint
{
public:
    // デフォルトコンストラクタ
    KeyPoint();
    // 2 種類の完全なコンストラクタ
    KeyPoint(Point2f _pt, float _size, float _angle=-1,
            float _response=0, int _octave=0, int _class_id=-1);
    KeyPoint(float x, float y, float _size, float _angle=-1,
             float _response=0, int _octave=0, int _class_id=-1);
    // 点の座標
    Point2f pt;
    // 特徴サイズ
    float size;
    // 度単位であらわされる特徴方向
    // (方向が,未定義または計算されない場合は,
    //  これが負値になります)
    float angle;
    // 特徴の強さ
    // (最も著しい keypoint だけを選択する
    //  ために利用されます)
    float response;
    // 特徴が検出された,スケール-空間オクターブ.
    // これは,特徴サイズと相関をもちます.
    int octave;
    // ポイントクラス(特徴分類器や
    // 物体検出器で利用されます)
    int class_id;
};

// keypoint のベクトルをファイルストレージから読み込み,または書き込み.
void write(FileStorage& fs, const string& name, const vector<KeyPoint>& keypoints);
void read(const FileNode& node, vector<KeyPoint>& keypoints);

MSER

MSER

Maximally-Stable Extrenal Region 抽出器

class MSER : public CvMSERParams
{
public:
    // デフォルトコンストラクタ
    MSER();
    // すべてのアルゴリズムパラメータを初期化するコンストラクタ
    MSER( int _delta, int _min_area, int _max_area,
          float _max_variation, float _min_diversity,
          int _max_evolution, double _area_threshold,
          double _min_margin, int _edge_blur_size );
    // 指定した画像上でこの抽出器を走らせると,個々に輪郭
    // ( vector<Point>, findContours を参照)としてエンコードされた複数の MSER が返されます.
    // オプションマスクは, MSER を検索する対象となる領域を指定します.
    void operator()( const Mat& image, vector<vector<Point> >& msers, const Mat& mask ) const;
};

このクラスは,MSER( http://en.wikipedia.org/wiki/Maximally_stable_extremal_regions を参照してください)抽出アルゴリズムの全パラメータをカプセル化します.

SURF

SURF

画像から SURF を抽出するためのクラス.

class SURF : public CvSURFParams
{
public:
    // デフォルトコンストラクタ
    SURF();
    // すべてのアルゴリズムパラメータを初期化するコンストラクタ
    SURF(double _hessianThreshold, int _nOctaves=4,
         int _nOctaveLayers=2, bool _extended=false);
    // 各ディスクリプタの要素数を返す( 64 または 128 )
    int descriptorSize() const;
    // 高速なマルチスケール Hesian 検出器を用いて keypoint を検出します.
    void operator()(const Mat& img, const Mat& mask,
                    vector<KeyPoint>& keypoints) const;
    // keypoint を検出し,その SURF ディスクリプタを計算します.
    void operator()(const Mat& img, const Mat& mask,
                    vector<KeyPoint>& keypoints,
                    vector<float>& descriptors,
                    bool useProvidedKeypoints=false) const;
};

SURF クラスは,SURF ディスクリプタ Bay06 の実装です.(デフォルトでは)keypoint の検出に,高速マルチスケール Hessian keypoint 検出器が利用されます. しかし,ディスクリプタ自体は,ユーザが指定した他の keypoint に対しても計算することができます.この関数は,物体追跡,位置同定,画像の縫い合わせなどに利用できます. OpenCV のサンプルディレクトリにある find_obj.cpp デモを参照してください.

StarDetector

StarDetector

Star keypoint 検出器の実装.

class StarDetector : CvStarDetectorParams
{
public:
    // デフォルトコンストラクタ
    StarDetector();
    // すべてのアルゴリズムパラメータを初期化する完全なコンストラクタ
    // maxSize - 特徴の最大サイズ.以下のパラメータの値
    //         がサポートされます:
    //         4, 6, 8, 11, 12, 16, 22, 23, 32, 45, 46, 64, 90, 128
    // responseThreshold - 近似ラプラシアンに対する閾値.
    //         弱い特徴を除外するために利用されます.大きくなるほど
    //         検出される特徴が少なくなります.
    // lineThresholdProjected - ラプラシアンに対する別の閾値.
    //         エッジを除外するために利用されます.
    // lineThresholdBinarized - 特徴サイズに対する別の閾値.
    //         エッジを除外するために利用されます.
    // これら2つの閾値が大きくなるほど,検出される特徴点が増えます.
    StarDetector(int maxSize, int responseThreshold,
                 int lineThresholdProjected,
                 int lineThresholdBinarized,
                 int suppressNonmaxSize);

    // 画像中の keypoints を検出します.
    void operator()(const Mat& image, vector<KeyPoint>& keypoints) const;
};

このクラスは, Agrawal08 で述べられる CenSurE keypoint 検出器の改良版の実装です.