차근차근/OpenCV

3 일에 만드는 고속 특정 물체 인식 시스템 (3) SURF 추출

예쁜꽃이피었으면 2014. 7. 30. 10:15

http://aidiary.hatenablog.com/entry/20091030/1256905218

3 일에 만드는 고속 특정 물체 인식 시스템 (2) SIFT 특징 량 추출 (2009-10-24)의 연속이다. 아, 3 일 만인 버렸습니다.

이번에는 SIFT와 다른 국소 특징 량이다 SURF (Speeded Up Robust Features)을 추출하여보십시오. SURF의 F는 Features이므로 SURF 특징 량은 말하지 않는 것일까? SIFT와 추출 방법은 다르지만, 이미지에서 키포인트와 특징 벡터를 추출하는 점에서는 동일합니다. 추출 속도는 SIFT보다 몇 배 빠른 속도 라고하지만 정확도가 다소 떨어지는 것. 실시간 처리하고 싶을 때는 이쪽이 더 좋은 것 같습니다. 또한 OpenCV에도 이미 구현되어 있습니다 .SURF의 전체 알고리즘은 나중에 논문을보기로 우선 시도합니다.

이미지에서 SURF 추출

다음 프로그램은 이미지에서 SURF를 추출하여 특징점을 그 특징 량을 파일로 저장하는 프로그램입니다. 이 프로그램은 OpenCV 붙어있는 find_obj.cpp (C : \ OpenCV2.0 \ samples \ c에 있음)을 추천합니다. 이 프로그램에서 SURF 추출 부분 만 꺼내 보았습니다.

surf.exe 이미지 파일 이름] [SURF 파일 이름

라는 느낌으로 사용합니다.

# include <cv.h> 
# include <highgui.h> 
# include <iostream> 
# include <fstream>

using  namespace Std;

const  int DIM_VECTOR = 128 ;   / / 128 차원 벡터

/ ** 
* SURF 정보를 파일에 출력 
* @ param [in] filename SURF를 저장할 파일 이름 
* @ param [in] imageKeypoints SURF 키포인트 정보 
* @ param [in] imageDescriptors SURF 특징 벡터 정보 
* @ return 없음 
* / 
void writeSURF ( const  char * filename, CvSeq * imageKeypoints, CvSeq * imageDescriptors) {
    fstream fout;

    fout.open (filename, ios :: out);
    if (! fout.is_open ()) {
        cerr << "cannot open file :" << filename << endl;
         return ;
    }

    / / 첫 번째 행은 키 포인트 수와 특징 량의 차원 수를 기록 
    fout << imageKeypoints-> total << '' << DIM_VECTOR << endl;

    / / 두 번째 행에서 키포인트 정보와 특징 벡터를 기록 
    for ( int I = 0 ; i <imageKeypoints-> total; i + +) {
        CvSURFPoint * point = (CvSURFPoint *) cvGetSeqElem (imageKeypoints, i);
        float * descriptor = ( float *) cvGetSeqElem (imageDescriptors, i);
         / / 키 포인트 정보 (X 좌표, Y 좌표, 크기, 라플라시안)를 기록 
        fout << point-> pt.x << '' << point- > pt.y << '' << point-> size << '' << point-> laplacian << '' ;
         / / 특징 벡터를 기록 
        for ( int J = 0 ; j <DIM_VECTOR; j + +) {
            fout << descriptor [j] << '' ;
        }
        fout << endl;
    }

    fout.close ();
}

int Main ( int argc, char ** argv) {
     const  char * imageFile = argc == 3 ? argv [ 1 ] : "image/accordion_image_0001.jpg" ;
     const  char * surfFile = argc == 3 ? argv [ 2 ] : "image/accordion_image_0001.surf" ;

    / / SURF 추출 용 이미지를 그레이 스케일로로드
    IplImage * grayImage = cvLoadImage (imageFile, CV_LOAD_IMAGE_GRAYSCALE);
    if (! grayImage) {
        cerr << "cannot find image file :" << imageFile << endl;
         return - 1 ;
    }

    / / 키 포인트 그리기 용 색상도로드
    IplImage * colorImage = cvLoadImage (imageFile, CV_LOAD_IMAGE_COLOR);
    if (! colorImage) {
        cerr << "cannot find image file :" << imageFile << endl;
         return - 1 ;
    }

    CvMemStorage * storage = cvCreateMemStorage ( 0 );
    CvSeq * imageKeypoints = 0 ;
    CvSeq * imageDescriptors = 0 ;
    CvSURFParams params = cvSURFParams ( 500 , 1 );

    / / 이미지에서 SURF를 가져 
    cvExtractSURF (grayImage, 0 , & imageKeypoints & imageDescriptors, storage, params);
    cout << "Image Descriptors :" << imageDescriptors-> total << endl;

    / / SURF를 파일로 출력
    writeSURF (surfFile, imageKeypoints, imageDescriptors);

    / / 이미지에 키 포인트를 그리기 
    for ( int I = 0 ; i <imageKeypoints-> total; i + +) {
        CvSURFPoint * point = (CvSURFPoint *) cvGetSeqElem (imageKeypoints, i);
        CvPoint center;   / / 키포인트의 중심 좌표 
        int RADIUS;       / / 키포인트의 반경
        center.x = cvRound (point-> pt.x);
        center.y = cvRound (point-> pt.y);
        radius = cvRound (point-> size * 1.2 / 9.0 * 2.0 );
        cvCircle (colorImage, center, radius, cvScalar ( 0 , 255 , 255 ), 1 , 8 , 0 );
    }

    cvNamedWindow ( "SURF" );
    cvShowImage ( "SURF" , colorImage);
    cvWaitKey ( 0 );

    / / 뒤처리
    cvReleaseImage (& grayImage);
    cvReleaseImage (& colorImage);
    cvClearSeq (imageKeypoints);
    cvClearSeq (imageDescriptors);
    cvReleaseMemStorage (& storage);
    cvDestroyAllWindows ();

    return  0 ;
}

많은 뒤처리 해야겠다 않기 때문에 힘드네요 ... C + +를 이러니 싫어. 만약 당신이 뭔가 해제 잊어 버린 것을 주목 있으면 가르쳐주세요. 처음에는 cvClearSeq () 할 것을 모르고 백지 않았던 것이있었습니다 · · ·

SURF를 저장하는 파일은 SIFT 특징 량 추출 (2009-10-24)에서 사용한 SIFT 파일과 같은 형식입니다. 한 줄에 하나의 키 포인트 정보를 질질 끄지 않고 출력하고 있습니다. SURF 이론이 자세하게 알고 있지 않습니다 만, 크기는 SIFT 규모 것일까? 그리고 라플라시안도별로 몰라하지만 1 또는 -1의 값을 취합니다. 나중에 키포인트의 매칭을 빠르게 할 때 사용하기 때문에 우선 저장 때 있습니다.

408 128 <- 키 포인트 수 SURF의 차원 수
138.821 11.208 14 1 0.00771829 0.00876939 ... <- 첫 번째 키포인트의 X 좌표, Y 좌표, 크기, 라플라시안 다음 128 차원의 특징 벡터
121.597 17.7526 17 -1 -0.00576097 0.00586741 ... <- 두 번째 키포인트 정보
...

SIFT와 마찬가지로 추출 대상 이미지는 그레이 스케일 (흑백)로로드합니다. 다른 함수의 사용법은 위의 예제에서 대략 알 수 있다고 생각 합니다만, 키 포인트 정보 (imageKeypoints)와 키포인트의 특징 벡터 정보 (imageDescriptors)는 별도의 CvSeq에 저장되는 사양이되어있는 것 같습니다. 그러나 첨자는 대응하고 있습니다.예를 들어, imageKeypoints의 i 번째 키포인트의 특징 벡터 imageDescriptors의 i 번째에 저장되어 있습니다. 키 포인트 정보는 CvSURFPoint라는 구조체에 정리하고 있고, 특징 벡터는 float의 배열이 그대로 들어가있는 것 같습니다. SURF의 파라미터는 cvSURFParams로 지정합니다. OpenCV의 메뉴얼 (doc 폴더에있는 opencv.pdf)에 따르면 제 1 인자는 특징 량의 차원에서 0이라고 64 차원, 1이라면 128 차원입니다. 차원이 큰 편이 정확한 것일까? 두번째는 역치에서 키 포인트 hessian이 값보다 큰 점만 추출됩니다. 적절한 임계 값은 300-500 것. 크게하면 추출되는 점이 적지지는 것을 알 수 있습니다.

일부 이미지에서 시도합니다. SIFT와 비교하면 배경과 물체 사이의 선 (가장자리)가 추출되기 쉬운 것일까? 음양을 보면 차이를 잘 알 수 있습니다. 가장자리를 키포인트로하는 것은 좋지 않은 것 같아서 SIFT에 비해 조금 정밀도가 떨어진다는 것입니까? 만약 아는 사람이 있으면 조언하십시오.

f : id : aidiary : 20091030205528p : image
f : id : aidiary : 20091030205529p : image
f : id : aidiary : 20091030205530p : image
f : id : aidiary : 20091030205531p : image
f : id : aidiary : 20091030205532p : image
f : id : aidiary : 20091030205533p : image 

동영상에서 SURF를 실시간 추출

다음은 위의 프로그램을 조금 확장하여 Web 카메라에서 캡처 한 동영상에서 SURF를 실시간으로 추출하여보십시오. 이 프로그램은 Web 카메라를 접속하고 있지 않으면 움직이지 않습니다.

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

using  namespace Std;

int Main ( int argc, char ** argv) {
    CvCapture * capture;

    / / 카메라를 초기화 
    if ((capture = cvCreateCameraCapture ( 0 )) == NULL ) {
        cerr << "cannot find camera" << endl;
         return - 1 ;
    }

    / / 윈도우를 생성 
    cvNamedWindow ( "SURF" );

    IplImage * captureImage = cvQueryFrame (capture);
    while ( true ) {
        CvMemStorage * storage = cvCreateMemStorage ( 0 );
        CvSeq * imageKeypoints = 0 ;
        CvSeq * imageDescriptors = 0 ;
        CvSURFParams params = cvSURFParams ( 500 , 1 );

        captureImage = cvQueryFrame (capture);

        / / 그레이 스케일로 변환 
        IplImage * grayImage = cvCreateImage (cvGetSize (captureImage), 8 , 1 );
        cvCvtColor (captureImage, grayImage, CV_BGR2GRAY);

        / / 프레임 이미지에서 SURF를 가져 
        cvExtractSURF (grayImage, 0 , & imageKeypoints & imageDescriptors, storage, params);

        / / 이미지에 키 포인트를 그리기 
        for ( int I = 0 ; i <imageKeypoints-> total; i + +) {
            CvSURFPoint * point = (CvSURFPoint *) cvGetSeqElem (imageKeypoints, i);   / / i 번째 키포인트 
            CvPoint center;   / / 키포인트의 중심 좌표
            center.x = cvRound (point-> pt.x);
            center.y = cvRound (point-> pt.y);
            cvCircle (captureImage, center, 2 , cvScalar ( 0 , 255 , 255 ), CV_FILLED);
        }
        cvShowImage ( "SURF" , captureImage);

        / / 루프에서 생성 된 개체는 다루기
        cvReleaseImage (& grayImage);
        cvClearSeq (imageKeypoints);
        cvClearSeq (imageDescriptors);
        cvReleaseMemStorage (& storage);

        / / ESC 키를 누를 경우 루프를 빠져 
        int Key = cvWaitKey ( 30 );
         if (key == 27 ) {
             break ;
        }
    }

    / / 뒤처리
    cvReleaseCapture (& capture);
    cvDestroyAllWindows ();

    return  0 ;
}

루프에서 생성 된 개체는 루프에서 뒤처리합니다. 이것은 잊고 무서운 기세로 메모리 누수 메모리를 잠식 그중 충돌합니다 . 이것은 또한 C + +에 익숙하지 않은 경우 가끔 해버립니다. 실행하면 아래 동영상처럼 실시간으로 추출 할 수 있습니다. 정지 영상이 아닌 동영상을 포함하면 SIFT에 비해 SURF가 빠른 것이 실감 할 수 있습니다. 이제는 특히주의 쓰지 않는 한 특징점 추출에 SURF로갑니다. OpenCV 함수 있으니까 편하고 (버섯).

3 일에 만드는 고속 특정 물체 인식 시스템 (4) 특징점의 매칭 (2009/11/2)으로 계속됩니다.

참고 문헌

 

반응형