我是靠谱客的博主 调皮仙人掌,最近开发中收集的这篇文章主要介绍目标检测算法(二)——具体原理以及实现一、光流法,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、光流法

  1.1光流法原理 

   光流的概念是Gibson1950年首先提出来的。它是空间运动物体在观察成像平面上的像素运动的瞬时速度,是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法。一般而言,光流是由于场景中前景目标本身的移动、相机的运动,或者两者的共同运动所产生的。其计算方法可以分为三类:

       (1)基于区域或者基于特征的匹配方法;

       (2)基于频域的方法;

       (3)基于梯度的方法;

    简单来说,光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”。光流的研究是利用图像序列中的像素强度数据的时域变化和相关性来确定各自像素位置的“运动”。研究光流场的目的就是为了从图片序列中近似得到不能直接得到的运动场。

光流法的前提假设:

      (1)相邻帧之间的亮度恒定;

      (2)相邻视频帧的取帧时间连续,或者,相邻帧之间物体的运动比较“微小”;

      (3)保持空间一致性;即,同一子图像的像素点具有相同的运动

这里有两个概念需要解释:

运动场,其实就是物体在三维真实世界中的运动;

光流场,是运动场在二维图像平面上的投影。




如上图所示,H中的像素点(x,y)I中的移动到了(x+u,y+v)的位置,偏移量为(u,v)

光流法用于目标检测的原理:给图像中的每个像素点赋予一个速度矢量,这样就形成了一个运动矢量场。在某一特定时刻,图像上的点与三维物体上的点一一对应,这种对应关系可以通过投影来计算得到。根据各个像素点的速度矢量特征,可以对图像进行动态分析。如果图像中没有运动目标,则光流矢量在整个图像区域是连续变化的。当图像中有运动物体时,目标和背景存在着相对运动。运动物体所形成的速度矢量必然和背景的速度矢量有所不同,如此便可以计算出运动物体的位置。需要提醒的是,利用光流法进行运动物体检测时,计算量较大,无法保证实时性和实用性。

光流法用于目标跟踪的原理:

(1)对一个连续的视频帧序列进行处理;

(2)针对每一个视频序列,利用一定的目标检测方法,检测可能出现的前景目标;

(3)如果某一帧出现了前景目标,找到其具有代表性的关键特征点(可以随机产生,也可以利用角点来做特征点);

(4)对之后的任意两个相邻视频帧而言,寻找上一帧中出现的关键特征点在当前帧中的最佳位置,从而得到前景目标在当前帧中的位置坐标;

(5)如此迭代进行,便可实现目标的跟踪;


1.2 光流法实现

 1.2.1、opencv下的光流L-K算法

opencv的光流实现由好几个方法可以(也就是说有好几个函数可以用),每个函数当然也对应着不同的原理,那么它的效果以及算法的速度等等就会有一些差别。主要包括以下几种: 
calcOpticalFlowPyrLK 
calcOpticalFlowFarneback 
calcOpticalFlowBM 
calcOpticalFlowHS 
calcOpticalFlowSF

参见opencv文档介绍

这里先简单介绍下calcOpticalFlowPyrLK函数 
calcOpticalFlowPyrLK函数使用形式: 
calcOpticalFlowPyrLK(prevImg,nextImg,prevPts,nextPts,status,err) 
这个函数还有一些可选参数,一大堆,详细的说明可以看opencv介绍:

这里只是贴出了主要的几个输入输出: 
prevImg:就是你需要输入计算光流的前一帧图像

nextImg就是下一帧图像(可以看到一次光流就是在两针图像之间找不同)。

prevPts是前一帧图像中的特征点,这个特征点必须自己去找,所以在使用calcOpticalFlowPyrLK函数的时候,前面需要有一个找特征点的操作,那么一般就是找图像的角点,就是一个像素点与周围像素点都不同的那个点,这个角点特征点的寻找,opencv也提供夜歌函数:goodFeatureToTrack()(后面再介绍这个函数)。那么关于特征点有没有其他的方式呢?肯定是有的而且还很多吧。

nextPts参数就是计算特征点在第二幅图像中的新的位置,然后输出。特征点的新位置可能变化了,也可能没有变化,那么这种状态就存放在后一个参数status中。err就是新旧两个特征点位置的误差了,也是一个输出矩阵。 
其他参数默认吧。

关于特征角点检测函数goodFeatureToTrack() 
goodFeaturesToTrack(image,corners,maxCorners,qualityLevel,minDistance) 
函数的参数: 
image输入图像; 
corners输出的特征点系列,每一个元素就是一个特征点的位置。 
maxCorners规定的特征点最大数目,比如一副图像你可以找到很多特征点,但是只是取前maxCorners个具有最大特征的那些点作为最后的特征点,至于怎么判断哪些点的特征更好了,opencv自有一个机制,随便一个方法,比如计算一下这个点与周围一定领域的点的灰度相差求和,认为这个和越大的那些点是不是越属于特征点。 
qualityLevel是一个特征点的取到水平,其实也是控制特征点的选取的,看结果适当选取吧。 
minDistance是特征点与点之间的最小距离,一般我们如果想特征点尽量分散一些,太密集了肯定不好,那么我们可以通过这个参数。

比如说一个例子如下:

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat I1;
Mat I2;
vector<Point2f> features;
int maxCout = 50;//定义最大个数
double minDis = 20;//定义最小距离
double qLevel = 0.01;//定义质量水平
I1 = imread("I1.jpg",0);//读取为灰度图像
goodFeaturesToTrack(I1,features,maxCout,qLevel,minDis);
for(int i=0;i<features.size();i++)
{
//将特征点画一个小圆出来--粗细为2
circle(I1,features[i],3,Scalar(255),2);
}
imshow("features",I1);
waitKey(0);
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

这里写图片描述

修改下 int maxCout = 100;//定义最大个数 
double minDis = 10;//定义最小距离

这里写图片描述

下面通过这种特征点来以及L-K光流法来检测两幅相邻帧图像之间的移动点。首先自己准备两幅图吧,图中要有运动的目标才好,然后我们就这两幅图简单的看下里面的运动目标的特征点的运动吧,一个简单程序如下:

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/video.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/types_c.h>
#include <iostream>
#include <cstdio>
using namespace std;
using namespace cv;
int main()
{
Mat I1;
Mat I2;
vector<Point2f> features;
vector<Point2f> features_after;
vector<uchar> status;
vector<float> err;
int maxCout = 300;//定义最大个数
double minDis = 10;//定义最小距离
double qLevel = 0.01;//定义质量水平
//读取两个图像---相邻帧
I1 = imread("I1.jpg",0);//读取为灰度图像
I2 = imread("I2.jpg",0);
//检测第一帧的特征点
goodFeaturesToTrack(I1,features,maxCout,qLevel,minDis);
//计算出第二帧的特征点
calcOpticalFlowPyrLK(I1,I2,features,features_after,status,err);
//判别哪些属于运动的特征点
int k = 0;
for(int i=0;i<features_after.size();i++)
{
//状态要是1,并且坐标要移动下的那些点
if(status[i]&&((abs(features[i].x-features_after[i].x)+
abs(features[i].y-features_after[i].y))>4))
{
features_after[k++] = features_after[i];
}
}
features_after.resize(k);//截取
cout<<k<<endl;
for(int i=0;i<features_after.size();i++)
{
//将特征点画一个小圆出来--粗细为2
circle(I1,features_after[i],3,Scalar(255),2);
}
imshow("features",I1);
waitKey(0);
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

这里写图片描述

我的这两幅树叶图是存在这一些变化的,尤其树存在的地方,由于需要动态的来回切换着看才能看出变化,所以这里不太好显示这两幅图的变化,这里吧他们相差的图做出来如下: 
这里写图片描述

可以看到的是越白的地方就是存在着不一致,也就是存在着明显运动的地方。和上面角点检测的还是有点符合的。 
当然,我这可能只是连续两帧运动情况下的跟踪的特征点,如果是一个视频(连续的很多帧),那么把每两帧之间的运动点连接起来,就可以发现运动的物体的整个轨迹了。像网上博客有人做过的贴几个: Opencv学习笔记(九)光流法

1.2.2、opencv下光流Farneback法

上节说到过的calcOpticalFlowPyrLK光流算法,可以看到它实际上是一种稀疏特征点的光流算法,也就是说我们先找到那些(特征)点需要进行处理,然后再处理,该节介绍下一个全局性的密集光流算法,也就是对每一个点都进行光流计算,函数为calcOpticalFlowFarneback。

首先介绍参数,详细的介绍 参见OpenCV手册

参数一大推,得看一会。有些参数可能带来的影响不是很大,那么使用它推荐的参数即可。完整的参数达10个。按顺序:

prevImg:输入第一个图 
nextImg:输入第二个图 
Flow:输出的光流矩阵。矩阵大小同输入的图像一样大,但是矩阵中的每一个元素可不是一个值,而是两个值,分别表示这个点在x方向与y方向的运动量(偏移量)。所以要把这个光流场矩阵显示出来还真的需要费点力。那么上面说的两幅图像与这个光流场是什么关系呢?如下:
这里写图片描述 
pyrScale:一个构造图像金字塔的参数,一般就认为是0.5最好了,也就是将图像缩小一半。那么为什么要构造金字塔呢?这应该是与算法本身的设计有关,其实很多地方在检测特征的时候都会涉及到图像的金字塔,设想下如果有个特征点在原始尺寸与其缩小的尺寸下都是特征点的话,那么这个特征点就很有效了吧。
Levels:依然是与金字塔有关参数,常设值1. 
Winsize:相当于一个均值滤波的作用,窗口大小决定了其噪声的抑制能力什么的。 
Iterations:在每层金字塔上的迭代次数。 
polyN:点与附近领域点之间的联系作用,一般为5,7等等即可。 
polySigma :像素点的一个平滑水平,一般1-1.5即可。 
Flags:一个标记,决定计算方法。

具体怎么影响结果的,可以自己去尝试。 
下面对上节使用到的两幅图,通过这个方法来计算这两帧图像中存在的光流场,也就是把上述的Flow找出来,那些参数也决定了Flow找出来的不一样。简单的程序如下:

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/video.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/types_c.h>
#include <iostream>
#include <cstdio>
using namespace std;
using namespace cv;
#define UNKNOWN_FLOW_THRESH 1e9

void makecolorwheel(vector<Scalar> &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<Scalar> colorwheel; //Scalar r,g,b

if (colorwheel.empty())
makecolorwheel(colorwheel);
// determine motion range:

float maxrad = -1;
// Find max flow to normalize fx and fy

for (int i= 0; i < flow.rows; ++i)
{
for (int j = 0; j < flow.cols; ++j)
{
Vec2f flow_at_point = flow.at<Vec2f>(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<Vec2f>(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; // uncomment to see original color wheel

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); // increase saturation with radius

else
col *= .75; // out of range

data[2 - b] = (int)(255.0 * col);
}
}
}
}
int main()
{
Mat I1;
Mat I2;
Mat flow;
//读取两个图像---相邻帧
I1 = imread("I1.jpg",0);//读取为灰度图像
I2 = imread("I2.jpg",0);
calcOpticalFlowFarneback(I1,I2,flow,0.5,3,20,3,5,1.2,0);
//cout<<I1.size()<<flow.size()<<flow.at<Vec2f>(10,10)<<endl;
//flow = abs(flow);
Mat motion2color;
motionToColor(flow, motion2color);
imshow("flow",motion2color);
waitKey(0);
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114

可以看到程序的大部分是在如何把计算的光流flow可视化出来,虽然我们已经知道它是一个矩阵,而且矩阵中每个元素都包括2个值。但是显示出来也比较费劲,显示的函数是参考博客:

光流Optical Flow介绍与OpenCV实现

该博客里面的内容也是值得一看的。 
那么我们这里的一个结果如下(原始图见上篇): 
这里写图片描述

颜色越深表示该部分存在的运动变化越大。

早点版本(3.0之前)的OpenCV中还有好几个光流计算函数,什么块匹配BM法,HS法,LK法等等,每一种方法几乎都对应一篇相关文章而来。后几种方法的结果无非也都是计算出光流场(在x方向的光流与在y方向的光流)。3.0的opencv似乎把块匹配、HS等方法舍弃了?没有找到,想使用那些方法的恐怕还要使用以前的opencv版本才行

二、背景减法

1、原理

       对于一个稳定的监控场景而言,在没有运动目标,光照没有变化的情况下,视频图像中各个像素点的灰度值是符合随机概率分布的。由于摄像机在采集图像的过程中,会不可避免地引入噪声,这些灰度值以某一个均值为基准线,在附近做一定范围内的随机振荡,这种场景就是所谓的“背景”。

        背景减法(Background subtraction)是当前运动目标检测技术中应用较为广泛的一类方法,它的基本思想和帧间差分法相类似,都是利用不同图像的差分运算提取目标区域。不过与帧间差分法不同的是,背景减法不是将当前帧图像与相邻帧图像相减,而是将当前帧图像与一个不断更新的背景模型相减,在差分图像中提取运动目标。

                                

    背景减法的运算过程如图2-6所示。首先利用数学建模的方法建立一幅背景图像帧B,记当前图像帧为fn,背景帧和当前帧对应像素点的灰度值分别记为B(x,)fn(x , ,按照式2.17将两帧图像对应像素点的灰度值进行相减,并取其绝对值,得到差分图像D n

                                      

    设定阈值 ,按照式2.18逐个对像素点进行二值化处理,得到二值化图像 Rn' 。其中,灰度值为255的点即为前景(运动目标)点,灰度值为0的点即为背景点;对图像 Rn'进行连通性分析,最终可得到含有完整运动目标的图像Rn 

                                     

    背景减法计算较为简单,由于背景图像中没有运动目标,当前图像中有运动目标,将两幅图像相减,显然可以提取出完整的运动目标,解决了帧间差分法提取的目标内部含有“空洞”的问题。

    利用背景减法实现目标检测主要包括四个环节:背景建模,背景更新,目标检测,后期处理。其中,背景建模和背景更新是背景减法中的核心问题。背景模型建立的好坏直接影响到目标检测的效果。所谓背景建模,就是通过数学方法,构建出一种可以表征“背景”的模型。获取背景的最理想方法是在没有运动目标的情况下获取一帧“纯净”的图像作为背景,但是,在实际情况中,由于光照变化、雨雪天气、目标运动等诸多因素的影响,这种情况是很难实现。

2、背景建模

 2.1  多阵平均法

     

当实际的监控场景不是太复杂时,可以采用多帧平均法建立背景模型。多帧平均法实质上是一种统计滤波的思想。在一段时间内,将采集到的多帧图像相加,求其平均值,这个平均值就作为参考的背景模型。具体计算式如下:

                                           

       上式中,Bn为采集到第n帧图像时系统建立的背景模型,N为进行平均的帧数,(fn,fn− 1 ...fn N +1 为包含当前帧在内的系统所保存的连续N帧图像。由于受到场景中光照变化等因素的影响,得到的背景模型Bn 每隔一段时间需要更新一次,更新计算式为:                                                                       

                                     

2.20说明新的一帧背景模型可以由上一次计算得到的背景模型Bn1、当前帧fn以及fn -N帧递推得到,这样就实现了背景模型的更新。显然,N值越大,得到的背景模型Bn就越接近于真实背景。


                                             


    图 2-7 是采用多帧平均法对标准序列 hall 序列进行目标检测的实验结果。观察图(b)可以发现,使用多帧平均法提取背景图像,当场景中存在运动目标时,提取的背景中会存在“鬼影”(ghosts),这是由目标在连续多帧图像中的运动造成的。当N值增大时,“鬼影”效应会有所减弱,但是很难消除。
    多帧平均法计算方法简单,对场景中背景变化的适应性较好,但是该方法需要保存前N帧图像信息,增加了系统的开销,特别是在实时监控系统中,这样大的内存开销是很浪费的。因此,多帧平均法在实际的监控系统中应用较少。

  2.2.2 高斯混合模型   

    使用统计的方法实现背景建模及运动目标检测是当前目标检测方法中效果较好的一类方法。该类方法利用统计值表征背景,建立背景模型。

    不同的监控场景有着不同的特性,背景模型因此分为单模态和多模态两种。单模态的场景中,每个背景像素点的颜色值分布比较集中,可以用单一分布的概率模型来描述背景;而在多模态的场景中,每个背景像素点的颜色值分布非常分散,需要用多个分布的概率模型相拟合描述背景。在实际的场景中,由于光照变化、背景中轻微扰动(如户外场景中树枝的轻微摇摆)等因素的影响,背景模型往往是多模态的。

    利用高斯混合模型实现背景建模就是用来描述多模态场景的一种方法。该方法能在不断更新背景模型的同时,在新一帧图像中直接检测出属于目标的像素点,减少了计算差分图像和二值化图像等环节,有效地提高了检测速度。算法的基本思想是:对图像中每一个像素点的颜色值建立混合高斯模型,通过一段时间的采样观测,根据各个高斯分布的持续性和变动性的差异,判断哪一个分布更加接近于真实背景,该高斯分布就作为背景模型。如果图像中像素点的颜色值不符合该高斯分布,则被认为是目标点。具体计算方法如下。
    设图像中位置为(x0, y0)的像素点在一段时间内的观测值为:

                                 
    利用多个高斯分布对式2.21中的观测值进行建模,可以得到当前像素点的颜色值概率为:

                                 
    其中,K为高斯分布的个数(通常取3-5);ωi,t,为权重的估计值,即t时刻该像素点属于第i个高斯分布的可能性的大小;μi,t,t时刻第i个高斯分布的均值;Σi,t为第i个高斯分布的协方差矩阵;η为高斯分布概率密度函数:

                                
    为了计算简便,假定像素点颜色值的三个分量(R,G,B)相互独立,并且具有相同的方差,则式2.23中的协方差矩阵可以写为:
                                         

    这样,就建立起了被观察像素点(x0,y0)颜色值的高斯混合模型。对于输入图像中的像素点(x0,y0,t),将它的颜色值与已存在的个高斯分布相比较,判断其是否和已经存在的高斯分布相匹配,如果匹配,则该像素点为背景点。所谓“匹配”,即满足式2.25
                                     
    其中,μi,t-1 ,为第i个高斯分布在t1时刻的均值,TH通常取2.5σi t-1 ,为第i个高斯分布在t1时刻的标准偏差。
    如果没有找到匹配的高斯分布,则将输入像素的颜色值作为均值,建立一个新的高斯分布,代替之前K个高斯分布中概率最小、权重最低的分布,该分布具有较大的方差和较低的权重,从而重新建立背景模型。
    如果存在匹配的高斯分布,则背景模型中各个参数进行如下更新:

                                     
   其中,α是模型学习速率,1/α表征了模型参数变化的速率;对于匹配的高斯分布 Mk,t 1,其余不匹配的高斯分布,M k,t0;式2.27 和式 2.28 只针对于匹配的高斯分布,其余不匹配的高斯分布对应的参数保持不变;ρ为参数学习速率,定义为:

                                      
    这样,通过式 2.26--2.29,就实现了高斯混合模型的更新。高斯混合模型的方法,能够充分表征场景的多模态特性;可以很快适应背景的变化,即使是当场景中有光照变化、小幅度重复运动(如户外场景中树枝的轻微扰动)时,也能检测出运动目标;如果场景中有目标进入,长时间停留而成为背景,高斯混合模型方法同样可以及时地更新背景模型。
                    
    图2-8是采用高斯混合模型方法对自拍序列 lab 序列进行目标检测的实验结果。由图可以看出,该方法提取的背景较为“纯净”,最终可以获得完整的运动目标。但是该方法受光照变化的影响较大,提取的目标往往存在阴影;提取的前景(目标)图像中,含有较多的噪声,这都需要相应的后期处理操作。

三、帧间差分法

1、原理

    摄像机采集的视频序列具有连续性的特点。如果场景内没有运动目标,则连续帧的变化很微弱,如果存在运动目标,则连续的帧和帧之间会有明显地变化。

    帧间差分法(Temporal Difference)就是借鉴了上述思想。由于场景中的目标在运动,目标的影像在不同图像帧中的位置不同。该类算法对时间上连续的两帧或三帧图像进行差分运算,不同帧对应的像素点相减,判断灰度差的绝对值,当绝对值超过一定阈值时,即可判断为运动目标,从而实现目标的检测功能。

                      

    两帧差分法的运算过程如图2-2所示。记视频序列中第n帧和第n1帧图像为fnfn1,两帧对应像素点的灰度值记为fn(x,y)fn1(x , y),按照式2.13将两帧图像对应像素点的灰度值进行相减,并取其绝对值,得到差分图像Dn

                                                                                  

    设定阈值T,按照式2.14逐个对像素点进行二值化处理,得到二值化图像Rn'。其中,灰度值为255的点即为前景(运动目标)点,灰度值为0的点即为背景点;对图像Rn'进行连通性分析,最终可得到含有完整运动目标的图像Rn

                                         

2、三帧差分法

    两帧差分法适用于目标运动较为缓慢的场景,当运动较快时,由于目标在相邻帧图像上的位置相差较大,两帧图像相减后并不能得到完整的运动目标,因此,人们在两帧差分法的基础上提出了三帧差分法。

            

   三帧差分法的运算过程如图2-3所示。记视频序列中第n+1帧、第n帧和第n1帧的图像分别为fn+1fnfn1,三帧对应像素点的灰度值记为fn+1(x , y) fn(x , y) fn1(x , y) , 按照式2.13分别得到差分图像Dn+1Dn,对差分图像Dn+1Dn按照式2.15进行与操作,得到图像Dn',然后再进行阈值处理、连通性分析,最终提取出运动目标。 

                   

    在帧间差分法中,阈值 的选择非常重要。如果阈值T选取的值太小,则无法抑制差分图像中的噪声;如果阈值T选取的值太大,又有可能掩盖差分图像中目标的部分信息;而且固定的阈值T无法适应场景中光线变化等情况。为此,有人提出了在判决条件中加入对整体光照敏感的添加项的方法,将判决条件修改为:

                               

    其中, N A为待检测区域中像素的总数目,λ为光照的抑制系数,A可设为整帧图像。添加项表达了整帧图像中光照的变化情况。如果场景中的光照变化较小,则该项的值趋向于零;如果场景中的光照变化明显,则该项的值明显增大,导致式2.16右侧判决条件自适应地增大,最终的判决结果为没有运动目标,这样就有效地抑制了光线变化对运动目标检测结果的影响。


3、两帧差分和三帧差分的比较

    图 2-5 是采用帧间差分法对自拍序列 lab 序列进行运动目标检测的实验结果,(b)图是采用两帧差分法的检测结果,(c)图是采用三帧差分法的检测结果。lab序列中的目标运动较快,在这种情况下,运动目标在不同图像帧内的位置明显不同,采用两帧差分法检测出的目标会出现“重影”的现象,采用三帧差分法,可以检测出较为完整的运动目标。

                

    综上所述,帧间差分法的原理简单,计算量小,能够快速检测出场景中的运动目标。但由实验结果可以看出,帧间差分法检测的目标不完整,内部含有“空洞”,这是因为运动目标在相邻帧之间的位置变化缓慢,目标内部在不同帧图像中相重叠的部分很难检测出来。帧间差分法通常不单独用在目标检测中,往往与其它的检测算法结合使用。







最后

以上就是调皮仙人掌为你收集整理的目标检测算法(二)——具体原理以及实现一、光流法的全部内容,希望文章能够帮你解决目标检测算法(二)——具体原理以及实现一、光流法所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(33)

评论列表共有 0 条评论

立即
投稿
返回
顶部