图像金字塔是图像中多尺度表达的一种,最主要用于图像分割,是一种以多分辨率来解释图像的有效但概念简单的结构。一幅图像的金字塔式一系列以金字塔形状排列的,分辨率逐步降低且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到到达某个终止条件才停止采样。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。层级越高图像越小,分辨率越低。
通常有两种类型的图像金字塔,分别是:
- 数字金字塔(Gaussuan pyramin)-用来向下采样,主要的图像金字塔,主要用来向下采样图像
- 拉普拉斯金字塔(Laplaican pyramid)-用来从金字塔底层图像重建上层未采样的还原,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用,是从金字塔底层图像中向上采样,重建一个图像。
要从金字塔的第i层生成第i+1层(将第i层表示为Gi),先要用高斯核对Gi进行卷积,然后删除所有偶数行和列,新得到的图像面积会变为源图像的1/4.按上述过程对输入图像G0执行操作就可以得到整个金字塔。
当图像金字塔的上层移动时,尺寸和分辨率会降低。在OpenCV中,从金字塔上一层图像生成下一级图像时可以使用PryDown,而通过PryUp将现有的图像在每个维度上放大两倍。
注意:PryDown和PryUp函数互逆的,PryUp不是降采样的逆操作。图像首先在每个维度上扩大为原来的两倍,新增的行(偶数行)以0填充,然后给指定的滤波器进行卷积(实际上是在每个维度都扩大为原来两倍的过滤器)去估计“丢失”像素的近似值。
1.高斯金字塔
高斯金字塔是通过高斯平滑和亚采样获得一些采样图像,即是第K层高斯金字塔通过平滑、亚采样就可以获得第K+1层高斯图像。高斯金字塔包括一些列的低通滤波器,其截止频率从上一层到下一层以因子2逐渐增加,所以高斯金字塔可以跨越很大的频率范围。
a.对图像向下采样
为了获得第G(i+1)的金字塔图像,采取如下方法:
(1)对图像G(i)进行高斯内核卷积
(2)将所有偶数列和偶数行去除
得到的图像即为G(i+1)的图像,很明显G(i+1)只有源图像的四分之一,通过对输入图像G(i)(原始图像)不停的迭代上述步骤就会得到整个金字塔,即向下采样会丢失图像的信息,缩小了图像
b.对图像的向上采样
如果想放大图像,则需要通过向上取样操作得到
(1)将图像在每个方向上扩大为原来的两倍,新增的行和列以0填充
(2)使用先前同样的内核(乘以4)与放大后的图像卷积,获得“新增像素”的近似值。
得到的图像即为放大后的图像,但是与源图像想必会发现比较模糊,因为在缩放中已经丢失了一些信息。如果想在缩放过程中减少信息的丢失,这些数据就形成了拉普拉斯金字塔。
2.拉普拉斯金字塔
第i层的拉普拉斯金字塔的数学定义为:
L(i) = G(i) -UP(G(i+1))&g
式中的G(i)表示第i层的图像,UP()操作是将源图像中位置为(x,y)的像素映射到目标图像的(2X+1,2Y+1)位置,即在进行向上取样,&表示卷积,g为5*5的高斯内核.
使用OpenCV中函数直接进行拉普拉斯运算: L(i) = G(i) -PryUP(G(i+1))
图像金字塔的一个重要应用就是图像分割
3.尺寸调整:resize()函数
resize()函数是OpenCV中专门用来调整图像大小的函数
此函数将源图像精确的转换为指定尺寸的目标图像。如果源图像中设置了ROI(Region Of Internet,感兴趣区域),那么resize()函数会对源图像的ROI区域进行调整图像尺寸的操作,来输出到目标图像中。若目标中已经设置了ROI区域,不难理解resize()函数将会对源图像进行尺寸调整并填充到目标图像的ROI中。
C++:void resize(InputArray src,OutputArray dst,Size dsize,double fx = 0,double fy = 0,int interpolation = INTER_LINEAR)
很多时候不用考虑第二个参数dst的初始图像尺寸和类型(即直接定义一个Mat类型的对象,不用对其初始化),因为其尺寸和类型可以由src,dsize,fx,fy这几个参数来确定。
参数一:输入图像,即源图像
参数二:输出图像,当其非零时,有着dsize的尺寸,或者由src.size()计算出来
参数三:输出图像的大小,如果为0,由下式计算
dsize = Size(round(fx*src.cols),round(fy*src.rows))
其中dsize,fx,fy都不能为0
参数四:沿水平轴的缩放系数,有默认值0,当其不为0时,由下式进行计算:
(double)dsize.width/src.cols
参数五:沿垂直轴的缩放系数,有默认值0,当其不为0时,由下式进行计算:
(double)dsize.height/src.rows
参数六:用于指定插值模式,默认为INTER_LINEAR
若要缩小图像,推荐使用CV_INTER_AREA来插值,若要放大图像,推荐使用CV_INTER_CUBIC(效率不高)或者CV_INTER_LINEAR(效率高)
resize()函数调用示例:
// resize.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
int _tmain(int argc, _TCHAR* argv[])
{
//载入源图像
Mat srcImage = imread("1.jpg");
Mat tempImage,dstImage1,dstImage2;//临时变量和目标图的定义
tempImage = srcImage;//将原始图复制临时变量
imshow("[源图像]",srcImage);
//进行尺寸调整
resize(tempImage,dstImage1,Size(tempImage.cols/2,tempImage.rows/2),(0,0),(0,0),3);
resize(tempImage,dstImage2,Size(tempImage.cols*2,tempImage.rows*2),(0,0),(0,0),3);
imshow("[效果图一]",dstImage1);
imshow("[效果图二]",dstImage2);
waitKey(0);
return 0;
}
如下图所示:
4.向上采样:PryUp()函数
PryUp()函数的作用是向上采样并模糊一张图像,说白了就是放大一张图片
C++:void pyrUp(InputArray src,OutputArray dst,const Size& dstsize = Size(),int borderType = BORDER_DEFAULT)
参数二:输出图像,和输入图像有一样的尺寸和类型
参数三:输出图像的大小,有默认值Size(),即默认情况下,由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)
参数四:边界模式,一般不用理睬
首先通过插入为零的行和列,对源图像进行向上采样操作,然后将结果与pyrDown()乘以4的内核做卷积,示例如下:
// pyrUp.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
int _tmain(int argc, _TCHAR* argv[])
{
//载入图像
Mat srcImage = imread("1.jpg");
Mat tmpImage,dstImage;
tmpImage = srcImage;
imshow("[源图像]",srcImage);
pyrUp(tmpImage,dstImage,Size(tmpImage.cols*2,tmpImage.rows*2));
imshow("[效果图]",dstImage);
waitKey(0);
return 0;
}
5.向下采样:pyrDown()函数
pyrDown()函数的作用是向下采样并模糊一张照片,就是缩小一张图片
C++:void pyrDown(InputArray src,OutputArray dst,const Size&dstsize = Size(),int borderType = BORDER_DEFAULT)
参数二:输出图像,尺寸和类型和源图像一致
参数三:输出图像的大小,有默认值Size(),即默认情况下,由Size Size((src.cols+1)/2,(src.rows+1)/2)来进行计算,且一直需要满足下列条件:
|dstsize.width*2 - src.cols| <= 2
|dstsize.height *2- src.rows| <= 2
该pryDown函数执行了高斯金字塔建造的向下采样的步骤,首先将源图像与如下内核做卷积:
接着通过对偶数行和偶数列做插值来进行向下采样操作,示例如下:
// pyrUp.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
int _tmain(int argc, _TCHAR* argv[])
{
//载入图像
Mat srcImage = imread("1.jpg");
Mat tmpImage,dstImage;
tmpImage = srcImage;
imshow("[源图像]",srcImage);
//pyrUp(tmpImage,dstImage,Size(tmpImage.cols*2,tmpImage.rows*2));
pyrDown(tmpImage,dstImage,Size(tmpImage.cols/2,tmpImage.rows/2));
imshow("[效果图]",dstImage);
waitKey(0);
return 0;
}