옵티컬 플로우 Optical Flow -3

컴퓨터비전/영상처리 2015. 4. 5. 02:54

현재 가장 많이 사용하고 있는 옵티컬 플로우 방법은 Lucas와 Kanade방법이다.


[Lucas81] Lucas, Bruce D., and Takeo Kanade. "An iterative image registration technique with an application 

to stereo vision." IJCAI. Vol. 81. 1981.



위 영상을 보면 추적 영상의 외곡에도 상당히 추적을 잘하는 것을 볼수 있다.

각 화소에 대하여 특정 범위 안 윈도우 크기 내의 이웃 화소 들에 대해서 광류식(Optical Flow Equation)의 에러 제곱을

최소화하는 벡터를 구하기 위해 벡터를 지역 최소자승법(local least squares)로 계산한다.


예를 들면 윈도우 크기 3x3일 떄 9개의 화소에 대하여 에러 제곱을 최소화하는 (u,v)을 의사 역행렬(pseudo inverse matrix)을

최소자승법으로 계산한다.



위와 같이 광류식을 새워놓고 에러 제곱을 최소화 하는 광류 벡터(u,v)를 구하이 위해 u와 v로 각각 미분한다.

(에러 제곱을 최소화 하는 광류 벡터)


각각 미분한 식을 0으로 놓고 계산하면 아래 수식을 얻을 수 있다.


을 아래 수식으로 변형한다.



OpenCV에는 Lucas와 Kanade의 Optical Flow함수를 2가지 제공한다.

첫번째는 위 수식을 그대로 적용한 기본 방법이고,

1
void cvCalcOpticalFlowLK(const CvArr* prev, const CvArr* curr, CvSize win_size, CvArr* velx, CvArr* vely)
cs



두번째는 탐색시 피라미드 구조를 적용하여 부화소 단위까지 광류 속도 벡터를 계산하여 좀더 정확한 추적이 
가능하도록 구성한 방법이다.

1
void cvCalcOpticalFlowPyrLK(const CvArr* prev, const CvArr* curr, CvArr* prev_pyr, CvArr* curr_pyr, const CvPoint2D32f* prev_features, CvPoint2D32f* curr_features, int count, CvSize win_size, int level, char* status, float* track_error, CvTermCriteria criteria, int flags)
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
 
const int MAX_CORNERS = 200;
 
int main(int argc, char** argv) 
{
    //초기화. 두개의 영상을 불러오고, 결과를 저장한 영상을 생성한다.
    IplImage* imgA = cvLoadImage("img/prev.bmp", CV_LOAD_IMAGE_GRAYSCALE);
    IplImage* imgB = cvLoadImage("img/curr.bmp", CV_LOAD_IMAGE_GRAYSCALE);
 
    CvSize img_sz = cvGetSize(imgA);
    int win_size = 10;
 
    IplImage* imgC = cvLoadImage("img/curr.bmp", CV_LOAD_IMAGE_UNCHANGED);
 
    //추적할 특징을 검출한다
    IplImage* eig_image = cvCreateImage(img_sz, IPL_DEPTH_32F, 1);
    IplImage* tmp_image = cvCreateImage(img_sz, IPL_DEPTH_32F, 1);
 
    int corner_count = MAX_CORNERS;
    CvPoint2D32f* cornersA = new CvPoint2D32f[MAX_CORNERS];
 
    // 이미지에서 추적하기 적절한 코너 추출
    cvGoodFeaturesToTrack(imgA, eig_image, tmp_image,
        cornersA, &corner_count, 0.015.00300.04);
 
    // 좀더 정확한 서브 픽셀 코너 계산
    cvFindCornerSubPix(imgA, cornersA, corner_count, cvSize(win_size, win_size),
        cvSize(-1-1), cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 200.03));
 
    // Lucas-Kanade 옵티컬 플로우
    char feature_found[MAX_CORNERS];
    float feature_errors[MAX_CORNERS];
    CvSize pyr_sz = cvSize(imgA->width + 8, imgB->height / 3);
 
    IplImage* pyrA = cvCreateImage(pyr_sz, IPL_DEPTH_32F, 1);
    IplImage* pyrB = cvCreateImage(pyr_sz, IPL_DEPTH_32F, 1);
 
    CvPoint2D32f* cornersB = new CvPoint2D32f[MAX_CORNERS];
 
    //추출한 코너(cornerA)를 추적함 -> 이동한 점들의 위치는 cornerB에 저장된다.
    cvCalcOpticalFlowPyrLK(imgA, imgB, pyrA, pyrB, cornersA, cornersB, corner_count,
        cvSize(win_size, win_size), 5, feature_found, feature_errors,
        cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, .3), 0);
 
    for (int i = 0; i<corner_count; i++) {
        if (feature_found[i] == || feature_errors[i] > 550) {
            //feature_found[i]값이 0이 리턴이 되면 대응점을 발견하지 못함
            //feature_errors[i] 현재 프레임과 이전프레임 사이의 거리가 550이 넘으면 예외로 처리
            printf("Error is %f\n", feature_errors[i]);
            continue;
        }
 
        printf("Got it\n");
        CvPoint p0 = cvPoint(cvRound(cornersA[i].x), cvRound(cornersA[i].y));
        CvPoint p1 = cvPoint(cvRound(cornersB[i].x), cvRound(cornersB[i].y));
        cvLine(imgC, p0, p1, CV_RGB(25500), 2);
    }
 
    cvNamedWindow("ImageA"0);
    cvNamedWindow("ImageB"0);
    cvNamedWindow("Lkpyr_OpticalFlow"0);
 
    cvShowImage("ImageA", imgA);
    cvShowImage("ImageB", imgB);
    cvShowImage("Lkpyr_OpticalFlow", imgC);
 
    cvWaitKey(0);
 
    cvDestroyAllWindows();
 
    cvReleaseImage(&imgA);
    cvReleaseImage(&imgB);
    cvReleaseImage(&imgC);
    cvReleaseImage(&eig_image);
    cvReleaseImage(&tmp_image);
    cvReleaseImage(&pyrA);
    cvReleaseImage(&pyrB);
 
    return 0;
}
cs

소스 참조 : http://lueseypid.tistory.com/archive/20130122