반응형

Optical Flow

  • Optical flow는 연속적인 프레임(Video) 사이에서 물체나 카메라의 움직임으로 인해 발생하는 차이.
  • 이동을 나타내는 2D vector field이다.

Optical Flow

  • optical flow는 몇 가지 가정에 따라 작동한다.
    1. 개체의 픽셀 강도는 연속 프레임 간에 변경되지 않는다.
    2. 인접한 이웃 픽셀은 비슷한 움직임을 갖는다.
  • 첫 번째 Frame의 pixel I(x, y, t)
  • dt 시간 이후에 찍은 다음 Frame에서 거리 (dx, dy) 만큼 이동.
  • 위 가정에서 해당 픽셀은 동일하고 강도는 변경되지 않음으로 아래 식과 같다.

첫 번째 Fame의 픽셀 I(x, y, t)와 dt동안 dx, dy만큼 이동한 I(x+dx, y+dy, t+dt)는 같은 강도를 가진다

  • 우변에 테일러 급수를 취하고 공통항을 제거하고 dt로 나누면 아래와 같다.

Oprical Flow 방정식

  • 위 방정식을 Optical Flow 방정식이라고 한다. (fx, fy는 이미지 gradient, ft는 시간에 따른 기울기)
  • u, v의 값은 알 수 없다. → Lucas-Kanade 방법으로 알 수 있다.

 

 

Lucas-Kanade method

  • 이웃한 픽셀이 유사한 움직임을 가질 것이라는 가정.
  • 3x3 patch 사용. → 9개의 점은 모두 같은 움직임을 갖는다.
  • u, v를 변수로 최소자승법을 이용해 9개의 방정식을 풀 수 있다.
    행렬을 전개하면 Optical Flow 방정식이 나옴
  • 추적할 몇 가지 포인트를 제공하고 해당 포인트의 광학 흐름 벡터를 수신한다.
  • 작은 움직임을 다루다 보니 큰 움직임이 있으면 실패한다. → 이를 해결하기 위해 pyramid 사용.
  • 피라미드에 오르면 작은 움직임은 제거되고 큰 움직임은 작은 움직임이 된다.

 

 

Lucas-Kanade Optical Flow in OpneCV

  • calcOpticalFlowPyrLK() 함수에서 기능 제공.
  • 비디오의 일부 지점을 추적하는 예제.
  • 일부 지점은 goodFeaturesToTracks() 함수를 이용하여 선택.
  CString szFilter = _T("Video (*.MP4) | *.MP4;*.jpg | All Files(*.*)|*.*||");
	CFileDialog dlg(FALSE, NULL, NULL, OFN_HIDEREADONLY, szFilter);

	CString path;
	if (IDOK == dlg.DoModal()) {
		path = dlg.GetPathName();
	}
	else {
		return;
	}

	cv::String strPaht = cv::format("%S", path);
	cv::VideoCapture cap(strPaht);
	if (!cap.isOpened())
		return;

	// 색상표
	vector<Scalar> colors;
	RNG rng;
	for (int i = 0; i < 100; i++)
	{
		int r = rng.uniform(0, 256);
		int g = rng.uniform(0, 256);
		int b = rng.uniform(0, 256);
		colors.push_back(Scalar(r, g, b));
	}

	// 1. get first frame feature.
	Mat old_frame, old_gray;
	vector<Point2f> p0, p1;
	cap >> old_frame;
	cvtColor(old_frame, old_gray, COLOR_BGR2GRAY);
	goodFeaturesToTrack(old_gray, p0, 100, 0.3, 7, Mat(), 7, false, 0.04);	// 100개의 feature point -> p0

	// Create a mask image for drawing purposes
	Mat mask = Mat::zeros(old_frame.size(), old_frame.type());

	while (true) {
		Mat frame, frame_gray;
		cap >> frame;
		if (frame.empty())
			break;

		cvtColor(frame, frame_gray, COLOR_BGR2GRAY);
		
		vector<uchar> status;
		vector<float> err;
		
		// 2. calculate optical flow (old gray -> frame gray : p0 -> p1)
		TermCriteria criteria = TermCriteria((TermCriteria::COUNT) + (TermCriteria::EPS), 10, 0.03);
		calcOpticalFlowPyrLK(old_gray, frame_gray, p0, p1, status, err, Size(15, 15), 2, criteria);

		// 3. status보고 good point만 선택
		vector<Point2f> good_new;
		for (uint i = 0; i < p0.size(); i++)
		{
			// Select good points
			if (status[i] == 1) {
				good_new.push_back(p1[i]);
				// draw the tracks
				line(mask, p1[i], p0[i], colors[i], 2);
				circle(frame, p1[i], 5, colors[i], -1);
			}
		}
		Mat img;
		add(frame, mask, img);
		imshow("Frame", img);
		int keyboard = waitKey(30);
		if (keyboard == 'q' || keyboard == 27)
			break;

		// 4. Now update the previous frame and previous points
		old_gray = frame_gray.clone();
		p0 = good_new;
	}

 

 

Dense Optical Flow in OpenCV

  • Lucas-Kanade method는 (goodFeaturesToTrack에 의해) 감지된 point에 대한 Optical Flow 계산.
  • OpenCV는 프레임의 모든 점에 대한 Optical Flow 알고리즘을 제공.
  • Gunnar Farneback의 알고리즘을 사용.
  CString szFilter = _T("Video (*.MP4) | *.MP4;*.jpg | All Files(*.*)|*.*||");
	CFileDialog dlg(FALSE, NULL, NULL, OFN_HIDEREADONLY, szFilter);

	CString path;
	if (IDOK == dlg.DoModal()) {
		path = dlg.GetPathName();
	}
	else {
		return;
	}

	cv::String strPaht = cv::format("%S", path);
	cv::VideoCapture cap(strPaht);
	if (!cap.isOpened())
		return;

	Mat frame1, prvs;
	cap >> frame1;
	cvtColor(frame1, prvs, COLOR_BGR2GRAY);

	while (true) {
		Mat frame2, next;
		cap >> frame2;
		if (frame2.empty())
			break;

		cvtColor(frame2, next, COLOR_BGR2GRAY);
		Mat flow(prvs.size(), CV_32FC2);
		calcOpticalFlowFarneback(prvs, next, flow, 0.5, 3, 15, 3, 5, 1.2, 0);

		// visualization
		Mat flow_parts[2];
		split(flow, flow_parts);
		Mat magnitude, angle, magn_norm;
		cartToPolar(flow_parts[0], flow_parts[1], magnitude, angle, true);
		normalize(magnitude, magn_norm, 0.0f, 1.0f, NORM_MINMAX);
		angle *= ((1.f / 360.f) * (180.f / 255.f));

		//build hsv image
		Mat _hsv[3], hsv, hsv8, bgr;
		_hsv[0] = angle;
		_hsv[1] = Mat::ones(angle.size(), CV_32F);
		_hsv[2] = magn_norm;
		merge(_hsv, 3, hsv);
		hsv.convertTo(hsv8, CV_8U, 255.0);
		cvtColor(hsv8, bgr, COLOR_HSV2BGR);

		imshow("frame2", bgr);

		int keyboard = waitKey(30);
		if (keyboard == 'q' || keyboard == 27)
			break;

		prvs = next;
	}

 

 

 

 

반응형
woongs_93