OpenCV-图像金字塔

简介

图像金字塔是图像中多尺度表达的一种,最主要用于图像的分割,是一种以多分辨率来解释图像的有效但概念简单的结构。

金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。

我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。

常用金字塔

一般情况下有两种类型的图像金字塔常常出现在文献和以及实际运用中。他们分别是:

  1. 高斯金字塔(Gaussianpyramid): 用来向下采样,主要的图像金字塔
  2. 拉普拉斯金字塔(Laplacianpyramid): 用来从金字塔低层图像重建上层未采样图像,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用

两者的简要区别

高斯金字塔用来向下降采样图像,而拉普拉斯金字塔则用来从金字塔底层图像中向上采样重建一个图像

OpenCV实现

图像金字塔中的向上和向下采样分别通过OpenCV函数 pyrUp 和 pyrDown 实现

  • 对图像向上采样:pyrUp 函数
  • 对图像向下采样:pyrDown 函数

这里的向下与向上采样,是对图像的尺寸而言的(和金字塔的方向相反),向上就是图像尺寸加倍,向下就是图像尺寸减半。而如果我们按上图中演示的金字塔方向来理解,金字塔向上图像其实在缩小,这样刚好是反过来了。

但需要注意的是,PyrUp 和 PyrDown 不是互逆的,即 PyrUp 不是向下采样的逆操作。这种情况下,图像首先在每个维度上扩大为原来的两倍,新增的行(偶数行)以0填充。然后给指定的滤波器进行卷积(实际上是一个在每个维度都扩大为原来两倍的过滤器)去估计“丢失”像素的近似值。

高斯金字塔

高斯金字塔是通过高斯平滑和亚采样获得一些列下采样图像,也就是说第K层高斯金字塔通过平滑、亚采样就可以获得 K+1 层高斯图像,高斯金字塔包含了一系列低通滤波器,其截至频率从上一层到下一层是以因子2逐渐增加,所以高斯金字塔可以跨越很大的频率范围。金字塔如图:

另外,每一层都按从下到上的次序编号, 层级 G_i + 1 (表示为 G_i + 1尺寸小于第i层 G_i)。

对图像的向下取样

获取G_i + 1 层的金字塔图像,进行如下步骤

  1. 对图像 G_i 进行高斯内核卷积
  2. 将所有偶数行和列去除

得到的图像即为G_i+1的图像,显而易见,结果图像只有原图的四分之一。 通过对输入图像 G_i (原始图像)不停迭代以上步骤就会得到整个金字塔。同时我们也可以看到,向下取样会逐渐丢失图像的信息。

对图像的向上取样

如果需要向上取样,则需如下步骤

  1. 将图像在每个方向扩大为原来的两倍,新增的行和列以0填充
  2. 使用先前同样的内核(乘以4)与放大后的图像卷积,获得 “新增像素”的近似值

得到的图像即为放大后的图像,但是与原来的图像相比会发觉比较模糊,因为在缩放的过程中已经丢失了一些信息,如果想在缩小和放大整个过程中减少信息的丢失,这些数据形成了拉普拉斯金字塔。

拉普拉斯金字塔

如下为拉普拉斯金字塔第 i 层的数学定义

1
L_i = G_i - UP(G_{i+1})\bigotimes g_{5×5}

式中Gi的表示第 i 层的图像。而UP()操作是将源图像中位置为(x,y)的像素映射到目标图像的(2x+1,2y+1)位置,即再进行向上取样。符号 ⨂ 表示卷积,g5×5 为5x5的高斯内核

用PyrUP表示为

1
L_i = G_i - PyrUP(G_{i+1})

也就是说,拉普拉斯金字塔是通过源图像减去先缩小后再放大的图像的一系列图像构成的。

所以,我们可以将拉普拉斯金字塔理解为高斯金字塔的逆形式。

OpenCV函数

resize

resize( )为OpenCV中专职调整图像大小的函数。

此函数将源图像精确地转换为指定尺寸的目标图像。如果源图像中设置了ROI(Region Of Interest ,感兴趣区域),那么resize( )函数会对源图像的ROI区域进行调整图像尺寸的操作,来输出到目标图像中。若目标图像中已经设置ROI区域,不难理解resize( )将会对源图像进行尺寸调整并填充到目标图像的ROI中。

函数原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
* src 输入图像
* dst 输出图像,type与输入相同,大小为dsize,如dsize为0则根据src.size()与dx,dy计算大小
* dsize 如果为0,则计算式为 dsize = Size(round(fx*src.cols), round(fy*src.rows)),disze或者fx,fy不能为0
* fx 沿水平轴的比例因子,当为0时,计算式为 fx = (double)dsize.width/src.cols
* fy 沿垂直轴的系数;当它等于0时,计算式为 fy = (double)dsize.height/src.rows
* interpolation 插值方法,可选差值方法如下
INTER_NEAREST - 最近邻插值
INTER_LINEAR - 线性插值(默认值)
INTER_AREA - 区域插值(利用像素区域关系的重采样插值)
INTER_CUBIC –三次样条插值(超过4×4像素邻域内的双三次插值)
INTER_LANCZOS4 -Lanczos插值(超过8×8像素邻域的Lanczos插值)
*/
void resize( InputArray src,
OutputArray dst,
Size dsize,
double fx = 0,
double fy = 0,
int interpolation = INTER_LINEAR );

若要缩小图像,一般情况下 interpolation 最好用INTER_AREA来插值,

而若要放大图像,一般情况下 interpolation 最好用INTER_CUBIC(效率不高,慢,不推荐使用)或INTER_LINEAR(效率较高,速度较快,推荐使用)。

实例

1
2
3
Mat mat_base = imread("fish.png");
Mat mat_dst;
resize(mat_base, mat_dst, Size(),0.5,0.5);

PyrUp

函数原型

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* src 输入图像
* dst 输出图像,type 与原始图像相同
* dstsize 输出图像的大小,默认情况下为 Size(src.cols*2,src.rows*2) 且满足以下条件
|dstsize.width - src.cols * 2| <= (dstsize.width mod 2)
|dstsize.height - src.rows * 2| <= (dstsize.height mod 2)
* borderType 推断边缘类型
*/
void pyrUp( InputArray src,
OutputArray dst,
const Size& dstsize = Size(),
int borderType = BORDER_DEFAULT );

实例

1
2
3
Mat mat_base = imread("fish.png");
Mat mat_dst;
pyrUp(mat_base, mat_dst);

PyrDown

函数原型

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* src 输入图像
* dst 输出图像,type 与原始图像相同
* dstsize 输出图像的大小,默认情况下为 Size((src.cols+1)/2, (src.rows+1)/2) 且满足以下条件
|dstsize.width * 2 - src.cols| <= 2
|dstsize.height * 2 - src.rows| <= 2
* borderType 推断边缘类型
*/
void pyrDown( InputArray src,
OutputArray dst,
const Size& dstsize = Size(),
int borderType = BORDER_DEFAULT );

实例

1
2
3
Mat mat_base = imread("fish.png");
Mat mat_dst;
pyrDown(mat_base, mat_dst);

4gwST1.png