簡易認識

出典: Wikimura

ポリライン近似から、校正パターンのフレーム(四角形)と方向マーカ(三角形)を探し出すためには、角と曲線を区別できなくてはならない。

レンズにより、フレームもマーカも歪んでしまう。単にポリラインの頂点数が4つだから四角、3つだから三角と判定できるか不安だった。 しかし、試してみたところ意外とうまくいった。 実際のカメラ校正では広角レンズも扱うかもしれないが、まずは簡易版を作ってみる。 インターフェースさえ決めておけば、後で広角にも対応させることができるはず。

パターンの黒丸や、パターン外部に四角・三角が見つかっているが、これは別途除外する。

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

#define BOX_HOLE_COLOR      CV_RGB( 255, 125, 0)
#define BOX_OUTLINE_COLOR   CV_RGB( 255, 0, 0)
#define TRI_HOLE_COLOR      CV_RGB( 0, 125, 255)
#define TRI_OUTLINE_COLOR   CV_RGB( 0, 0, 255)

int main( void)
{
    IplImage *src, *dst, *tmp;
    IplImage *gray;
    int found ;
    int i;
    char key;

    // 輪郭・ポリライン近似
    CvMemStorage *contStorage = cvCreateMemStorage(0);
    CvSeq *contours;
    CvMemStorage *polyStorage = cvCreateMemStorage(0);
    CvSeq *polys, *poly;
    CvTreeNodeIterator polyIterator;

    cvNamedWindow( "Poly", CV_WINDOW_AUTOSIZE);

    // 画像入力
    CvCapture *capture = cvCreateCameraCapture( 0);
    if( capture == 0) return -1;
    src = cvQueryFrame( capture);

    gray = cvCreateImage( cvGetSize( src), IPL_DEPTH_8U, 1);
    tmp = cvCreateImage( cvGetSize( src), IPL_DEPTH_8U, 1);
    dst = cvCreateImage( cvGetSize( src), IPL_DEPTH_8U, 3);


    while( key != 'q')
    {
        cvQueryFrame( capture);
        cvCopy( src, dst);
        cvCvtColor( dst, gray, CV_BGR2GRAY);

        // 二値化
        cvThreshold( gray, tmp , 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);

        // 輪郭抽出
        found = cvFindContours( tmp, contStorage, &contours, sizeof( CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);

        // ポリライン近似
        // 許容誤差距離は適当、階層も適当
        polys = cvApproxPoly( contours, sizeof( CvContour), polyStorage, CV_POLY_APPROX_DP, 10, 10);

        // 表示
        cvInitTreeNodeIterator( &polyIterator, ( void*)polys, 10);
        while( (poly = (CvSeq *)cvNextTreeNode( &polyIterator)) != NULL)
        {
            switch( poly->total)
            {
            case 3:
                cvDrawContours( dst, poly, TRI_OUTLINE_COLOR, TRI_HOLE_COLOR, 0, 3);
                break;
            case 4:
                cvDrawContours( dst, poly, BOX_OUTLINE_COLOR, BOX_HOLE_COLOR, 0, 3);
                break;
            default:
                break;
            }
        }
        cvShowImage( "Poly", dst);
        key = cvWaitKey( 10);
    }

    cvDestroyAllWindows();
    cvReleaseImage( &dst);
    cvReleaseImage( &gray);
    cvReleaseImage( &tmp);
    cvReleaseCapture( &capture);
    cvReleaseMemStorage( &polyStorage);
    cvReleaseMemStorage( &contStorage);
    return 0;
}

パターン外形のみ抽出

ロバスト性は低いが、環境を整えれば十分使える程度の認識率なら良いと考え、難しい計算が不要な方法を考えた。

外形線は四角形であること、内側に四角形が1つだけあることを条件とした。

ポリライン近似と、その元となった輪郭の階層構造は一致している。 片方の構造を用いて走査しつつ、もう片方も追従させる。 発見てループを抜け、次の処理に移る際に、対応する輪郭が分かっている必要があるためこうした。

実行したところ、照明条件が悪く二値化に失敗したと思われる場合を除き、人の目で見えていると思える状態ならば、確実に認識できた。

cvInitTreeNodeIterator( &polyIterator, ( void*)polys, 10);
cvInitTreeNodeIterator( &contIterator, ( void*)contours, 10);
while( (poly = (CvSeq *)cvNextTreeNode( &polyIterator)) != NULL)
{
    contour = (CvSeq *)cvNextTreeNode( &contIterator);
    if( poly->total == 4 && poly->v_next != 0 && poly->v_next->h_next == 0 && poly->v_next->total == 4)
    {
        cvDrawContours( dst, poly, VIRTEX_COLOR, VIRTEX_COLOR, 0, 3);
    }
}