光流法是计算机视觉中的一种技术,用于估计图像中相邻帧之间的像素位移或运动。它是一种用于追踪图像中物体运动的技术,可以在视频中检测并测量物体的运动轨迹。基本上,光流意味着计算像素的移动向量作为物体在两个相邻图像之间的位移差。光流的主要思想是估计物体运动或摄像机运动引起的物体的位移矢量。
光流法基于以下两个主要假设:
灰度恒定假设(Brightness Constancy Assumption): 在短时间内,相邻帧中的像素灰度值保持不变。这意味着在相邻帧中,同一物体的灰度值应该是相似的。
空间一致性假设(Spatial Coherence Assumption): 邻近像素点的运动是相似的。这意味着在一个小的局部区域内,像素点的运动可以通过一个共同的运动向量来描述。
基于上述两个假设,光流法的目标是计算场景中每个像素点在图像平面上的运动矢量。这些运动矢量描述了像素从一帧到下一帧的位移。
在光流法的实现中,有几种不同的方法,其中最常见的是Lucas-Kanade方法和Horn-Schunck方法。
Lucas-Kanade方法: 该方法基于灰度恒定和空间一致性假设,通过在图像上的局部区域内求解一个线性方程组来计算运动矢量。它假设邻近像素点的运动是相似的,因此在局部区域内使用最小二乘法来估计运动。
Horn-Schunck方法: 该方法通过最小化整个图像上的一个全局能量函数来计算光流场。它对整个图像施加了平滑性的约束,因此在处理相对较大的运动时效果较好。
总体而言,光流法是一种有用的技术,尤其在分析视频中物体的运动、跟踪目标或检测异常事件时。
光流法检测中稠密光流函数说明:
calcOpticalFlowPyrLK(Lucas-Kanade光流算法)函数是OpenCV库中的一个函数,用于计算图像序列中的稠密光流。 该函数基于Gunnar Farneback的算法,能够估计图像序列中每一点的运动向量。
函数原型和参数说明
void calcOpticalFlowFarneback(InputArray prev, InputArray next, OutputArray flow, double pyr_scale, int levels, int winsize, int iterations, int poly_n, double poly_sigma, int flags);
参数说明:
- prev:第一帧输入图像。
- next:第二帧输入图像,与第一帧图像大小和类型相同。
- flow:计算得到的光流图像,大小与prev相同,类型为CV_32FC2。
- pyr_scale:构建金字塔的图像尺度参数,通常设置为0.5。
- levels:金字塔的层数。
- winsize:均值窗口的大小,影响去噪效果和运动检测的灵敏度。
- iterations:迭代次数。
- poly_n:多项式展开的阶数,通常为5或7。
- poly_sigma:高斯函数的标准差。
- flags:计算方法标志,可以包括OPTFLOW_USE_INITIAL_FLOW和OPTFLOW_FARNEBACK_GAUSSIAN等。
具体使用示例函数:
//检测前后两帧图像是否有物体移动
bool MoveDetect(cv::Mat background, cv::Mat frame)
{
bool ret = false;
//1.将background和frame转为灰度图
cv::Mat gray1, gray2;
cv::cvtColor(background, gray1, cv::COLOR_BGR2GRAY);
cv::cvtColor(frame, gray2, cv::COLOR_BGR2GRAY);
//2.将background和frame做差
cv::Mat m_different;
cv::absdiff(gray1, gray2, m_different);
//3.对差值图diff_thresh进行阈值化处理
cv::Mat Dif_Thresh;
cv::threshold(m_different, Dif_Thresh, 50, 255, cv::THRESH_BINARY);
//4.腐蚀
cv::Mat kernel_erode = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::Mat kernel_dilate = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15));
cv::erode(Dif_Thresh, Dif_Thresh, kernel_erode);
//5.膨胀
cv::dilate(Dif_Thresh, Dif_Thresh, kernel_dilate);
//6.查找轮廓
std::vector<std::vector<cv::Point>> contours;
cv::findContours(Dif_Thresh, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
if(contours.size())
ret = true;
return ret;
}
//检测前后两帧图像物体移动的方向
Int MoveDirect(cv::Mat background, cv::Mat frame)
{
int m_direct = 0;
cv::Mat flow;
//1.将background和frame转为灰度图
cv::Mat gray1, gray2;
cv::cvtColor(background, gray1, cv::COLOR_BGR2GRAY);
cv::cvtColor(frame, gray2, cv::COLOR_BGR2GRAY);
// 计算稠密光流
cv::calcOpticalFlowFarneback(gray1, gray2, flow, 0.5, 3, 15, 3, 5, 1.2, 0);
// 判断物体运动方向
double sumX = 0, sumY = 0;
int count = 0;
for (int y = 0; y < flow.rows; y += 8) {
for (int x = 0; x < flow.cols; x += 8) {
const cv::Point2f &fxy = flow.at<cv::Point2f>(y, x);
sumX += fxy.x;
sumY += fxy.y;
count++;
}
}
double avgX = sumX / count;
double avgY = sumY / count;
std::cout << " MoveDirect,avgX=" << avgX <<",avgY=" << avgY << std::endl;
if (avgX > 0 && avgY > 0)
{
m_direct = 1;
}
else if (avgX < 0 && avgY < 0)
{
m_direct = -1;
}
return m_direct;
}