옵티컬 플로우
프레임 장면에서 객체가 어떻게 움직이고 있는지 추적
밀집 옵티컬 플로우
동영상에서 픽셀의 속도는 이전프레임과 현재 프레임사이에 픽셀이동 한 양(변위)와 관련이 있음을 이용
영상에 내부의 모든 픽셀에서 속도를 구한다.
혼-셩크 방법은 이러한 속도장을 계산하는 방법중 하나이다.
한프레임의 각 픽셀 윈도우를 설정하고 다음 프레임에서 이 윈도우와 가장 잘 일치하는 곳을 찾음
계산양이 매우 많다.
희소 옵티컬 플로우
코너와 같이 두드러진 속성을 가진 추적할 점을 미리 지정해준다.
연상량이 적은 희소 옵티컬 플로우 방법을 선호
루카스-카나데방법은 희소 옵티컬 플로우 방법을 이용한다.
루카스-카네데 방법
한프레임의 각 픽셀 윈도우를 설정하고 다음 프레임에서 이 윈도우와 가장 잘 일치하는 곳을 찾는다.
작은 지역 윈도우를 사용하기 때문에 이 윈도우보다 큰 움직임이 발생하였을 경우 움직임을 계산하지 못하는 단점이 있다.
☞ 이러한 문제점을 해결하기 위해 피라미드를 이용한다.
피라미드LK 알고리즘은 원본영상으로부터 영상 피라미드를 구성, 상위계층에서 하위계층으로 추적 => 커다란 움직임도 찾아낸다.
(잘사용안함) 루카스-카나데 코드 : vCalcOpticalFlowLK()
피라미드를 사용하지 않는 루카스-카나데 밀집 옵티컬 플로우 알고리즘
void cvCalcOpticalFlowLK(
const CvArr* prev, const CvArr* curr, CvSize win_size, CvArr* velx, CvArr* vely ); |
CvCalcOpticalFlowLK()함수는 최소 오차를 계산할 수 있는 픽셀들에 대해서만 그 결과를 산출한다. 신뢰할수 없는 오차를 발생시키는 픽셀에 대해서는 속도 백터의 값을 모두 0으로 설정한다.
피라미드 루카스-카나데 코드 : cvCalcOpticalFlowPyrLK()
추적하기 좋은 특징을 사용하고 각 점들의 추적이 얼마나 잘 수행되었는지를 알려준다.
void cvCalcOpticalFlowPyrLK(
const CvArr* prev, //이전프레임 영상(8비트 단일 채널영상) const CvArr* curr, //현재프레임 영상(8비트 단일 채널영상) CvArr* prev_pyr, //이전 프레임 피라미드 영상을 저장하기 위해 할당된 버퍼 CvArr* curr_pyr, //현재 프레임 피라미드 영상을 저장하기 위해 할당된 버퍼 const CvPoint2D32f* prev_features, //움직임을 추정할 점들의 배열 CvPoint2D32f* curr_features, //점들이 이동한 점들의 위치 int count, //prev_feature에 저장된 점들의 개수 CvSize win_size, //지역 움직임을 계산하기 위한 윈도우의 크기 int level, //피라미드에서 생성할 최대 계층개수 char* status, //움직임 추정 계산의 성공여부(1:성공, 0:실패) float* track_error, /* 실수형 배열을 가리키며 각원소는 이전프레임, 현재프레임간 특징점 사이 거리를 저장 */ CvTermCriteria criteria, //알고리즘의 종료조건 int flags //함수 실행시 내부 기록에 대한 세세한 조정을 지정하는 역할 ); |
prec_pyr, curr_pyr를 통해 피라미드 영상을 저장하기 위한 버퍼의 크기는 ((img.width+8)×(img.height/3) 바이트 이상이어야한다. 만약 두개의 포인터를 NULL로 설정하면 피라미드 영상을 위해 필요한 메모리 공간은 내부에서 할당되고 사용후 해제 되지만 성능면에서 좋지 않다.
flag : 함수 실행시 내부 기록에 대한 세세한 조정을 지정하는 역할
- CV_LKFLOW_PYR_A_READY : 함수 호출전에 이미 이전 프레임에 대한 영상 피라미드가 계산되어 prev_pyr에 저장되어있다.
- CV_LKFLOW_PYR_B_READY : 함수 호출전에 이미 현재 프레임에 대한 영상 피라미드가 계산되어 curr_pyr에 저장되어있다.
- CV_LKFLOW_INITIAL_GUESSES : 함수가 호출되었을때, prev_feature배열을 이용하여 curr_feature배열의 초기값을 설정한다.
cvCalcOpticalFlowPyrLK()함수는 연속된 프레임들을 입력으로 제공하고, prev_feature에 추적하기 원하는 점들을 저장한후 함수를 호출한다.
함수가 반환되면, status배열을 확인하여 어떤점이 성공적으로 추적되었는지를 검사하고, 그 점들의 새로운 위치를 알기위해 curr_feature배열값을 확인한다.
추적하기 원하는점 즉 추적하기 좋은 특징은 cvGoodFeatureToTrack()함수를 이용하여 코너를 추적점으로 설정하고 cvCalcOpticalFlowPyrLK()함수를 이용하여 움직임을 추적한다.
#include <cv.h> #include <cxcore.h> #include <highgui.h> #include <stdio.h>
const int MAX_CORNERS=500;
int main(int argc, char** argv) {
//초기화. 두개의 영상을 불러오고, 결과를 저장한 영상을 생성한다. IplImage* imgA=cvLoadImage("OpticalFlow0.jpg", CV_LOAD_IMAGE_GRAYSCALE); IplImage* imgB=cvLoadImage("OpticalFlow1.jpg", CV_LOAD_IMAGE_GRAYSCALE);
CvSize img_sz=cvGetSize(imgA); int win_size=10;
IplImage* imgC=cvLoadImage("OpticalFlow1.jpg", 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.01, 5.0, 0, 3, 0, 0.04);
//서브픽셀을 검출하여 정확한 서브픽셀 위치를 산출해냄 cvFindCornerSubPix(imgA, cornersA, corner_count, cvSize(win_size, win_size), cvSize(-1, -1), cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 20, 0.03));
//루카스-카나데 알고리즘 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]==0 || 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(255, 0, 0), 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; } |
'Developer > OpenCV' 카테고리의 다른 글
객체 움직임 추적 - 혼-셩크, 블록매칭, 평균이동(MeanShift) (0) | 2013.01.24 |
---|---|
OpenCV 실수형변수를 정수형으로 변환 (0) | 2013.01.24 |
코너검출 (2) | 2013.01.21 |
배경제거 (1) | 2013.01.18 |
모멘트 - 외곽선의 유사도 비교 (1) | 2013.01.15 |