第十节 运动分析和对象跟踪
OpenCV的imgproc模块提供了运动分析和对象跟踪的基础函数,可以根据这些函数对视频进行前景-背景分离,从而达到运动分析和对象跟踪的目的。
1、cv::accumulate
将多幅图像累加。
void cv::accumulate(InputArray src,InputOutputArray dst,InputArraymask = noArray())
该函数将src或其某些元素添加到dst中:
dst ( x , y ) ← dst ( x , y ) + src ( x , y ) if mask ( x , y ) ≠ 0 \texttt{dst} (x,y) \leftarrow \texttt{dst} (x,y) + \texttt{src} (x,y) \quad \text{if} \quad \texttt{mask} (x,y) \ne 0 dst(x,y)←dst(x,y)+src(x,y)ifmask(x,y)=0
该函数支持多通道图像,对每个通道都是独立处理的。
函数cv :: accumulate可以用于例如收集静态相机所查看的场景背景的统计信息,以及用于进一步的前景背景分割。
参数如下:
参数名称 | 参数描述 |
---|---|
src | 输入图像,支持的类型为 CV_8UC(n), CV_16UC(n), CV_32FC(n) or CV_64FC(n), |
dst | 通道数与输入图像相同的累加器图像, 类型为 CV_32F or CV_64F. |
mask | 可选的操作掩膜。 |
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
int main()
{
// 打开摄像头
cv::VideoCapture cap(0);
if(!cap.isOpened()){
cerr << "cannot open camera.\n";
return EXIT_FAILURE;
}
cv::Mat frame;
int averageCounts = 30;
int currentCount = 0;
cv::Mat averageFrame;
cv::Mat gray;
cv::Mat frameDelta;
cv::Mat foreGround,backGround;
int key = -1;
while(true){
cap >> frame;
if(frame.empty()){
cerr << "cannot grab frame from camera.\n";
break;
}
cv::cvtColor(frame,gray,cv::COLOR_BGR2GRAY);
if(currentCount < averageCounts){
if(averageFrame.empty()){
averageFrame.create(frame.rows,frame.cols,CV_32FC1);
}
// 计算平均图像
cv::accumulate(gray,averageFrame);
currentCount++;
}else{
// 背景分离
cv::convertScaleAbs(averageFrame,averageFrame);
cv::absdiff(gray,averageFrame,frameDelta);
// 阈值分割
cv::threshold(frameDelta,foreGround,25,255,cv::THRESH_BINARY_INV);
cv::imshow("camera",frame);
cv::imshow("deltaFrame",frameDelta);
cv::imshow("foreGround",foreGround);
key = cv::waitKey(10);
if(key == 27){
break;
}
}
}
cv::destroyAllWindows();
return 0;
}
2、cv::accumulateProduct
将两个输入图像的每个元素乘积添加到累加器图像。
void cv::accumulateProduct(InputArray src1,InputArray src2,InputOutputArray dst,InputArray mask = noArray())
该函数将两个图像或其所选区域的乘积添加到累加器dst中:
dst ( x , y ) ← dst ( x , y ) + src1 ( x , y ) ⋅ src2 ( x , y ) if mask ( x , y ) ≠ 0 \texttt{dst} (x,y) \leftarrow \texttt{dst} (x,y) + \texttt{src1} (x,y) \cdot \texttt{src2} (x,y) \quad \text{if} \quad \texttt{mask} (x,y) \ne 0 dst(x,y)←dst(x,y)+src1(x,y)⋅src2(x,y)ifmask(x,y)=0
该函数支持多通道图像并且每个通道都是独立处理的。参数如下:
参数名称 | 参数名称描述 |
---|---|
src1 | 第一个输入图像,1或3通道,8位或32位浮点。 |
src2 | 与src1具有相同类型和相同大小的第二个输入图像。 |
dst | 与32位或64位浮点数具有与输入图像相同通道数的累加器图像。 |
mask | 可选的操作掩膜。 |
3、cv::accumulateWeighted
void cv::accumulateWeighted(InputArray src,InputOutputArray dst,double alpha,InputArray mask = noArray())
该函数计算输入图像src和累加器dst的加权和,以使dst成为帧序列的运行平均值:
dst ( x , y ) ← ( 1 − alpha ) ⋅ dst ( x , y ) + alpha ⋅ src ( x , y ) if mask ( x , y ) ≠ 0 \texttt{dst} (x,y) \leftarrow (1- \texttt{alpha} ) \cdot \texttt{dst} (x,y) + \texttt{alpha} \cdot \texttt{src} (x,y) \quad \text{if} \quad \texttt{mask} (x,y) \ne 0 dst(x,y)←(1−alpha)⋅dst(x,y)+alpha⋅src(x,y)ifmask(x,y)=0
也就是说,alpha调节更新速度(累加器“忘记”较早图像的速度)。 该功能支持多通道图像。 每个通道都是独立处理的。
参数名称 | 参数描述 |
---|---|
src | 输入图像为1或3通道,8位或32位浮点。 |
dst | 通道数与输入图像相同的累加器图像,32位或64位浮点。 |
alpha | 输入图像的权重。 |
mask | 可选的掩膜操作。 |
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
int main()
{
// 打开摄像头
cv::VideoCapture cap("videos/vtest.avi",cv::CAP_ANY);
if(!cap.isOpened()){
cerr << "cannot open camera.\n";
return EXIT_FAILURE;
}
cv::Mat background;
cap >> background;
if(background.empty()){
cerr << "cannot grab frame.\n";
return EXIT_FAILURE;
}
cv::cvtColor(background,background,cv::COLOR_BGR2GRAY);
cv::GaussianBlur(background,background,cv::Size(0,0),1.0);
cv::Mat fback(background.size(),CV_32FC1);
vector<vector<cv::Point> > contours;
vector<cv::Vec4i> hierarchy;
cv::Mat frame,gray,diff;
int counts = 0;
while(cap.isOpened()){
cap >> frame;
if(frame.empty()){
cerr << "cannot grab frame\n";
break;
}
// 转换成灰度图像
cv::cvtColor(frame,gray,cv::COLOR_BGR2GRAY);
// 高斯模糊
cv::GaussianBlur(gray,gray,cv::Size(0,0),1.0);
if(counts < 30){
// 对图像进行累加,读取前30帧图像,作为背景
cv::accumulateWeighted(gray,fback,0.8);
counts ++;
}else{
cv::accumulateWeighted(gray,fback,0.2);
}
// 分离前景和背景
cv::absdiff(gray,background,diff);
// 阈值化处理
cv::threshold(diff,diff,120,255,cv::THRESH_BINARY);
cv::imshow("src",frame);
cv::imshow("diff",diff);
if(cv::waitKey(10) == 27){
break;
}
}
return 0;
}
4、cv::phaseCorrelate
用于检测两个图像之间发生的平移。
Point2d cv::phaseCorrelate(InputArray src1,InputArray src2,InputArray window = noArray(),double * response = 0)
该函数运算利用傅立叶位移定理来检测频域中的平移。 它可以用于快速图像配准以及运动估计。 有关更多信息,请参见http://en.wikipedia.org/wiki/Phase_correlation
计算两个提供的源阵列的交叉功率谱。 如果需要,可以使用getOptimalDFTSize填充数组。
该函数执行以下方程式:
-
首先,它将Hanning窗口(请参见http://en.wikipedia.org/wiki/Hann_function)应用于每个图像,以消除可能的边缘效果。 缓存此窗口,直到阵列大小更改以加快处理时间为止。
-
接下来,它计算每个源数组的前向DFT: G a = F { s r c 1 } , G b = F { s r c 2 } \mathbf{G}_a = \mathcal{F}\{src_1\}, \; \mathbf{G}_b = \mathcal{F}\{src_2\} Ga=F{ src1},Gb=F{ src2},其中 F \mathcal{F} F是前向DFT。
-
然后,它计算每个频域阵列的交叉功率谱: R = G a G b ∗ ∣ G a G b ∗ ∣ R = \frac{ \mathbf{G}_a \mathbf{G}_b^*}{|\mathbf{G}_a \mathbf{G}_b^*|} R=∣GaGb∗∣GaGb∗
-
接下来,通过逆DFT将互相关转换回时域: r = F − 1 { R } r = \mathcal{F}^{-1}\{R\} r=F−1{ R}
-
最后,它计算峰值位置,并在峰值周围计算5x5加权质心,以实现亚像素精度
( Δ x , Δ y ) = weightedCentroid { arg max ( x , y ) { r } } (\Delta x, \Delta y) = \texttt{weightedCentroid} \{\arg \max_{(x, y)}\{r\}\} (Δx,Δy)=weightedCentroid{ argmax(x,y){ r}}
-
如果不为零,则将响应参数计算为峰位置周围5x5重心内r的元素之和。 归一化为最大值1(意味着有一个峰),当有多个峰时会更小。
参数如下:
参数名称 | 参数描述 |
---|---|
src1 | 源浮点数组(CV_32FC1或CV_64FC1) |
src2 | 源浮点数组(CV_32FC1或CV_64FC1) |
window | 具有开窗系数的浮点数组,可减少边缘效应(可选)。 |
response | 峰值周围5x5质心内的信号功率,介于0和1之间(可选)。 |
#include <stdio.h>
#include <iostream>
#include "opencv2/core.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
using namespace cv;
int main(int argc, char* argv[])
{
auto src_im = cv::imread(images/balloon.png", 0);
auto dst_im = cv::imread("images/balloon-2.png", 0);
cv::Mat src_im_32f, dst_im_32f, hann;
// src_im.convertTo(src_im_32f, CV_32F);
// dst_im.convertTo(dst_im_32f, CV_32F);
cv::Laplacian(src_im, src_im_32f, CV_32F);
cv::Laplacian(dst_im, dst_im_32f, CV_32F);
cv::Point2d shift = cv::phaseCorrelate(src_im_32f, dst_im_32f, hann);
std::cout << shift.x << ", " << shift.y << std::endl;
return 0;
}
5、cv::createHanningWindow
该函数计算二维的汉宁(Hanning)窗系数。
void cv::createHanningWindow(OutputArray dst,Size winSize,int type)
有关更多信息,请参见(http://en.wikipedia.org/wiki/Hann_function)和(http://en.wikipedia.org/wiki/Window_function)。
参数如下:
参数名称 | 参数描述 |
---|---|
dst | 将Hann系数放入其中的目标数组 |
winSize | 窗口大小规格(宽度和高度都必须> 1) |
type | 创建的数组类型 |
// create hanning window of size 100x100 and type CV_32F
Mat hann;
createHanningWindow(hann, Size(100, 100), CV_32F);