第9章 图像局部与分割

局部与分割

简单来讲,分割就是如何从图像中将目标或部分目标分割出来。处了从图像中分割出前景目标之外,在很多情况下我们也希望将感兴趣的目标区域分割出来。
本章主要研究其他用于查找、填充和分离一幅图像中的目标以及部分目标物体的算法。

背景减除(背景差分)

由于背景减除简单而且摄像机在很多情况下是固定的,在视频安全领域,背景减除也许是最基本的图像处理操作。所以,背景模型的建立至关重要。
一旦背景模型建立,将背景模型和当前的图像进行比较,然后减去这些已知的背景减除,则剩下的目标物大致就是所求的前景目标了。
当然,“背景”在不同的应用场合下是一个很难定义的问题,后续会介绍一些背景建模的方法。

  • 背景减除的缺点

背景减除的一个缺点是建立在一个不常成立的假设:所有像素点是独立的。因为这种建模方法在计算像素变化时并没有考虑它相邻的像素。
其中一种解决方式是建立一个多元模型,它把基本的像素独立模型扩展为包含了相邻像素的亮度的基本场景。在这种情况下,我们用相邻像素的亮度来区别相邻像素值的相对明暗。但是这种模型消耗两倍的内存和更多的计算量。
但是在实际应用中,由于额外的开销,通常会避免使用复杂的模型。当像素独立假设不成立情况下,我们可以更有效地把精力投入到清除那些错误的检测结果中。本章将通过使用连通域,并且后续的方法严格限制在像素变化独立的假设基础上。

  • 场景建模

通常,一个场景模型可能包含许多层次,从新的场景到旧的场景再到背景。

  • 像素片段
1
2
3
4
5
6
7
8
9
// 对任意直线上的像素进行采样
int cvInitLineIterator(
const CvArr* image,
CvPoint pt1,
CvPoint pt2,
CvLineIterator* line_iterator,
int connectivity=8,
int left_to_right=0
);
  • 帧差

最简单的背景减除方法就是用一帧减去另一帧,然后将足够大的差别标为前景。这种方法往往能捕捉运动目标的边缘。

1
2
// 计算帧差
cvAbsDiff(frameTime1, frameTime2, frameForeground);

由于像素值总会受到噪声和波动的影响,我们应该忽略很小的差异,标识其余的作为较大的差别:

1
cvThreshold(frameForeground, frameForeground, 15, 255, CV_THRESH_BINARY);
  • 平均背景法

平均背景法的基本思路是计算每个像素的平均值和标准差作为它的背景模型。

  • 累积均值、方差和协方差

在平均背景法中,一个常用的函数是累积函数cvAcc()。通过该操作,我们可以计算出整个场景或部分场景的基本统计特性(均值,方差和协方差)。
均值漂移值:通过大量图像计算每个像素的均值的最简单的方法就是调用函数cvAcc()把他们加起来再除以图像总数来获得均值。

1
2
3
4
5
6
7
8
9
10
11
12
void cvAcc(
const CvArr* image,
CvArr* sum,
const CvArr* mask=NULL
);
// 直接计算均值漂移
void cvRunningAvg(
const CvArr* image,
CvArr* acc,
double alpha,
const CvArr* mask=NULL;
)

计算方差:可以通过累积平方图像,快速计算单个像素的方差。

1
2
3
4
5
void cvSquareAcc(
const CvArr* image,
CvArr* sqsum,
const CvArr* mask=NULL
);

计算协方差:可以通过选择一个特定的时间间隔来观测图像是怎么变化的,然后用当前图像乘以和特定时间间隔相对应的图像。

1
2
3
4
5
6
7
// 计算协方差
void cvMultiplyAcc(
const CvArr* image1,
const CvArr* image2,
CvArr* acc,
const CvArr* mask=NULL
);
  • 高级背景模型

codebook由一些boxes组成,这些boxes包含很长时间不变的像素值。codebook方法能够解决像素剧烈变化的问题。
结构

1
2
3
4
5
6
// codebook结构体定义
typedef struct code_book{
code_element **cb;
int numEntries;
int t;
} codebook;

背景学习

学习有移动前景目标的背景

背景差分:寻找前景目标

使用codebook背景模型流程总结:
1.使用函数update_codebook()在几秒钟或几分钟时间内训练一个基本的背景模型。
2.调用函数clear_stale_entries()清除stale索引。
3.调整阈值minMod和maxMod对已知前景达到最好的分割。
4.保持一个更高级别的场景模型。
5.通过函数background_diff()使用训练好的模型将前景从背景中分割出来。
6.定期更新学习的背景像素。
7.在一个频率较慢的情况下,用函数clear_stale_entries()定期清理stale的codebook索引。

  • 用于前景清除的连通部分

使用连通成分分析来清理原始分割图像是另一种寻找轮廓的方法。它能从原始噪声掩模图像创建出完整的掩模图像。连通域法是一个功能强大的在背景减去中去除噪声的技术。
下面直接给出一些处理连通区域的主要功能:
1.采用多边形拟合存在的轮廓部分,或凸包设置连通轮廓有多大
2.设置连通轮廓的大小以保证不被删除
3.设置返回的连通轮廓的最大数目
4.可选返回存活的连通轮廓的外接矩形
5.可选返回存活连通轮廓的中心

分水岭算法

在许多实际情况下,我们要分割图像,但无法从背景图像中获得有用信息。分水岭算法在这方面往往是有效的。
更确切的说,分水岭算法允许用户来标记目标的某个部分为目标,或背景的某个部分为背景。

1
2
// 分水岭算法函数
void cvWatershed(const CvArr* image, CvArr* markers);

用Inpainting修补图像

图像常常被噪声腐蚀。这些噪声也许是镜头上的灰尘或水滴造成的,也可能是旧照片上的划痕,或者图像的部分已经被破坏了。Inpainting是修复这些损害的一种有效方法,它可以利用这些已经破坏区域的边缘的颜色和结构,繁殖和混合到损坏的图像里面。

1
2
3
4
5
6
7
void cvInpaint(
const CvArr* src,
const CvArr* mask,
CvArr* dst,
double inpaintRadius,
int flags
);

均值漂移分割

均值漂移能沿时间轴找到颜色空间的峰值分布。这里均值漂移分割能找到在空间上颜色分布的峰值。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 均值漂移分割
void cvPyrMeanShiftFiltering(
const CvArr* src,
CvArr* dst,
double spatialRadius,
double colorRadius,
int max_level=1,
CvTermCriteria termcrit=cvTermCriteria(
CV_TERMCRIT_ITER | CV_TERMCRIT_EPS,
5,
1
)
);