聞きかじりめも

主にC++やメディア処理技術などに関して気付いたことを書いていきます.ここが俺のメモ帳だ!

OpenCVのチェスボードコーナー検出について

キャリブレーションプログラムをデバッグしてたら気付いたのでメモ.

結論から言うと,最近のOpenCVではcv::findChessboardCorners()を使う際にcv::cornerSubPix()必要ない

内部で何が行なわれているか?

ネットに沢山転がっている典型的なチェスボードを使ったOpenCVキャリブレーションサンプルは,例えば次のようなもの.(OpenCV.jpのOpenCV2.2日本語documentationより引用)

Size patternsize(8,6); // 内部にあるコーナーの個数
Mat gray = ....; // 入力画像
vector<Point2f> corners; // 検出されたコーナーがここに入ります

// CALIB_CB_FAST_CHECK を使うと,画像中にチェスボードコーナーが
// 無かった場合に,時間を大幅に節約できます
bool patternfound = findChessboardCorners(gray, patternsize, corners,
        CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE
        + CALIB_CB_FAST_CHECK);

if(patternfound)
  cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1),
    TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));

drawChessboardCorners(img, patternsize, Mat(corners), patternfound);

さてこれをデバッグしてみると,findChessboardCorners()を通り抜けた瞬間にcornersにサブピクセル精度の座標値が入っているじゃないか!どうもおかしいと思って元々のcvFindChessboardCorners()関数の定義(src/modules/calib3d/src/calibinit.cpp)を見てみると...

CV_IMPL
int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
                             CvPoint2D32f* out_corners, int* out_corner_count,
                             int flags )
{
//-------------537行目まで中略---------------
    if( found )
    {
        cv::Ptr<CvMat> gray;
        if( CV_MAT_CN(img->type) != 1 )
        {
            gray = cvCreateMat(img->rows, img->cols, CV_8UC1);
            cvCvtColor(img, gray, CV_BGR2GRAY);
        }
        else
        {
            gray = cvCloneMat(img);
        }
        int wsize = 2;
        cvFindCornerSubPix( gray, out_corners, pattern_size.width*pattern_size.height,
            cvSize(wsize, wsize), cvSize(-1,-1), cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 15, 0.1));
    }
//------------------後略---------------------
}

やっぱり.

思った通り,コーナーを見つけた直後にcvFindCornerSubPix()が入っていました.完全にしてやられた気分.

というわけで,この記事を見つけた良い子の皆はcv::cornerSubPix()などという無駄なコードを書くのを今すぐやめましょう.