光流法(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 1e9void 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和fyfor (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; // 取消注释可查看原始色轮lfor (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); //随半径增大饱和度elsecol *= .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;
}
源视频截图:

当前帧检测结果:

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
