光流法(optical flow)运动检测

使用C++、opencv、光流法进行运动目标检测

关于光流法的原理可参考:

https://blog.csdn.net/pannn0504/article/details/78357607

https://www.jianshu.com/p/144e6f8ca3b2

https://blog.csdn.net/qq_22194315/article/details/79347726 (较详细)

https://blog.csdn.net/qq_34531825/article/details/53382728(含公式)


相关API: 

void calcOpticalFlowFarneback( InputArray _prev0, InputArray _next0,OutputArray _flow0, double pyr_scale, int levels, int winsize,int iterations, int poly_n, double poly_sigma, int flags )

参数含义:
_prev0:输入前一帧图像
_next0:输入后一帧图像
_flow0:输出的光流
pyr_scale:金字塔上下两层之间的尺度关系,0.5为经典参数,每一层是下一层尺度的一半
levels:金字塔层数
winsize:均值窗口大小,越大越能denoise并且能够检测快速移动目标,但会引起模糊运动区域
iterations:迭代次数
poly_n:像素领域大小,一般为5,7等
poly_sigma:高斯标注差,一般为1-1.5
flags:计算方法。主要包括OPTFLOW_USE_INITIAL_FLOW和OPTFLOW_FARNEBACK_GAUSSIAN


代码如下:

#include "stdafx.h"
#include 
#include "opencv2/opencv.hpp"

using namespace cv;
using namespace std;

#define UNKNOWN_FLOW_THRESH 1e9

void makecolorwheel(vector &colorwheel)
{
	int RY = 15;
	int YG = 6;
	int GC = 4;
	int CB = 11;
	int BM = 13;
	int MR = 6;

	int i;

	for (i = 0; i < RY; i++) colorwheel.push_back(Scalar(255, 255 * i / RY, 0));
	for (i = 0; i < YG; i++) colorwheel.push_back(Scalar(255 - 255 * i / YG, 255, 0));
	for (i = 0; i < GC; i++) colorwheel.push_back(Scalar(0, 255, 255 * i / GC));
	for (i = 0; i < CB; i++) colorwheel.push_back(Scalar(0, 255 - 255 * i / CB, 255));
	for (i = 0; i < BM; i++) colorwheel.push_back(Scalar(255 * i / BM, 0, 255));
	for (i = 0; i < MR; i++) colorwheel.push_back(Scalar(255, 0, 255 - 255 * i / MR));
}

void motionToColor(Mat flow, Mat &color)
{
	if (color.empty())
		color.create(flow.rows, flow.cols, CV_8UC3);

	//定义颜色的容器
	static vector colorwheel; 
	if (colorwheel.empty())
		makecolorwheel(colorwheel);

	//确定运动范围
	float maxrad = -1;

	//找到最大流动来标准化fx和fy
	for (int i = 0; i < flow.rows; ++i)
	{
		for (int j = 0; j < flow.cols; ++j)
		{
			Vec2f flow_at_point = flow.at(i, j);
			float fx = flow_at_point[0];
			float fy = flow_at_point[1];
			if ((fabs(fx) >  UNKNOWN_FLOW_THRESH) || (fabs(fy) >  UNKNOWN_FLOW_THRESH))
				continue;
			float rad = sqrt(fx * fx + fy * fy);
			maxrad = maxrad > rad ? maxrad : rad;
		}
	}

	for (int i = 0; i < flow.rows; ++i)
	{
		for (int j = 0; j < flow.cols; ++j)
		{
			uchar *data = color.data + color.step[0] * i + color.step[1] * j;
			Vec2f flow_at_point = flow.at(i, j);

			float fx = flow_at_point[0] / maxrad;
			float fy = flow_at_point[1] / maxrad;
			if ((fabs(fx) >  UNKNOWN_FLOW_THRESH) || (fabs(fy) >  UNKNOWN_FLOW_THRESH))
			{
				data[0] = data[1] = data[2] = 0;
				continue;
			}
			float rad = sqrt(fx * fx + fy * fy);

			float angle = atan2(-fy, -fx) / CV_PI;
			float fk = (angle + 1.0) / 2.0 * (colorwheel.size() - 1);
			int k0 = (int)fk;
			int k1 = (k0 + 1) % colorwheel.size();
			float f = fk - k0;
			//f = 0; // 取消注释可查看原始色轮l

			for (int b = 0; b < 3; b++)
			{
				float col0 = colorwheel[k0][b] / 255.0;
				float col1 = colorwheel[k1][b] / 255.0;
				float col = (1 - f) * col0 + f * col1;
				if (rad <= 1)
					col = 1 - rad * (1 - col); //随半径增大饱和度
				else
					col *= .75; //超过范围处理
				data[2 - b] = (int)(255.0 * col);
			}
		}
	}
}

int main(int argc, char* argv[])
{
	//opencv中的读取视频
	VideoCapture cap;
	cap.open("D:\\1.avi");

	if (!cap.isOpened())
		return -1;

	Mat prevgray, gray, flow, cflow, frame;
	namedWindow("flow", 1);

	Mat motion2color;

	for (;;)
	{
		double t = (double)cvGetTickCount();

		cap >> frame;
		if (frame.empty()) {
			break;
		}
		cvtColor(frame, gray, CV_BGR2GRAY);
		imshow("original", frame);

		if (prevgray.data)
		{
			calcOpticalFlowFarneback(prevgray, gray, flow, 0.5, 3, 15, 3, 5, 1.2, 0);
			motionToColor(flow, motion2color);
			imshow("flow", motion2color);
		}
		if (waitKey(10) >= 0)
			break;
		std::swap(prevgray, gray);

		t = (double)cvGetTickCount() - t;
		cout << "cost time: " << t / ((double)cvGetTickFrequency()*1000.) << endl;
	}
	return 0;
}

 源视频截图:

光流法(optical flow)运动检测_第1张图片

当前帧检测结果:

光流法(optical flow)运动检测_第2张图片

你可能感兴趣的:(C++,opencv,图像处理)