图像金字塔
图像处理中,通常需将一幅图像转换成不同大小进行处理。在OpenCV中,主要提供了两种方式:1)resize();2)图像金字塔。这次主要总结OpenCV中图像金字塔的相关知识。
图像金字塔是一系列图像的集合,由一个原始图像依次向下采样获得。常见的图像金字塔主要有:Gaussian和Laplacian金字塔。高斯金字塔是向下采样图像,Laplacian金字塔则是向上采样重建图像。在OpenCV中,pyrDown ()和 pyrUp()则分别用于实现向下采样和向上采样二个功能。
一、图像金字塔原理
1、Guassian金字塔
Gaussian金字塔是是通过依次地向下迭代采样获得整个金字塔,如下图,随着依次地采样,图像越来越小。第(i+1)层Gi+1,是由第i层Gi 和高斯核进行卷积,然后去除每个偶数行和列,得到的采样图像是前一层的(1/4)。由其实现过程可知,向下采样是有损的操作,会丢弃了部分信息;图像的层次越高,对应的图像越小,分辨率也越低。
2、Laplacian金字塔
在downsampling的过程中,丢失了部分的信息;而为保存原始的图像,我们又需利用这些丢弃的信息,这些丢失的信息就形成了Laplacian金字塔。第i层的金字塔信息定义为:
由底层图像向上采样重建图像,向上取样的具体步骤分为2步:
1)将原图像在每一维上扩大2倍,将原图像(x,y)处的像素映射到目标图像的(2x+1,2y+1)处,新的偶数行则以0填充;
2)将其与给定的滤波器进行卷积来近似“丢失”像素的值。
则Laplacian金字塔可直接定义为:
注:Laplacian金字塔利用低层分辨率低的图像重建一个上取样的图像,但pyrUp()和pyrDown()是不可逆的,向下取样时会丢失信息的。详细的描述可参考:
http://docs.opencv.org/doc/tutorials/imgproc/pyramids/pyramids.html 和《学习OpenCV》。
二、pyrUp()和pyrDown()分析
1)pyrUp()
1. 函数定义
void cv::pyrUp( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType )
参数:_src - 输入图像
_dst - 输出图像
_dsz - 输出图像的大小。默认情况下:size(src.cols*2,src.rows*2)
borderType - 像素外插的方法,BORDER_DEFAULT,逐步查找得:BORDER_DEFAULT=BORDER_REFLECT_101, BORDER_REFLECT_101=IPL_BORDER_REFLECT_101,#define IPL_BORDER_REFLECT_101 4 ,额外的边界模式。
2. 函数实现
void cv::pyrUp( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType )
{
Mat src = _src.getMat();
Size dsz = _dsz == Size() ? Size(src.cols*2, src.rows*2) : _dsz; //目标图像的大小
_dst.create( dsz, src.type() );
Mat dst = _dst.getMat();
#ifdef HAVE_TEGRA_OPTIMIZATION
if(borderType == BORDER_DEFAULT && tegra::pyrUp(src, dst))
return;
#endif
int depth = src.depth();
PyrFunc func = 0;
if( depth == CV_8U ) //根据原图像深度进行不同的处理
func = pyrUp_<FixPtCast<uchar, 6>, NoVec<int, uchar> >;
else if( depth == CV_16S )
func = pyrUp_<FixPtCast<short, 6>, NoVec<int, short> >;
else if( depth == CV_16U )
func = pyrUp_<FixPtCast<ushort, 6>, NoVec<int, ushort> >;
else if( depth == CV_32F )
func = pyrUp_<FltCast<float, 6>, NoVec<float, float> >;
else if( depth == CV_64F )
func = pyrUp_<FltCast<double, 6>, NoVec<double, double> >;
else
CV_Error( CV_StsUnsupportedFormat, "" );
func( src, dst, borderType );
}
pyrUp()的实现主要由pyrUp_()实现,其为一个模板函数,相应的声明如下:
template<class CastOp, class VecOp> void pyrUp_( const Mat& _src, Mat& _dst, int)
其中func定义如下:
typedef void (*PyrFunc)(const Mat&, Mat&, int);
具体的实现在源代码目录\sources\modules\imgproc\src下的pyramids.cpp文件。
2)pyrDown()
1. 函数定义
void cv::pyrDown( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType )
参数: _src - 输入图像
_dst - 输出图像
_dsz - 输出图像尺寸,默认:Size((src.cols+1)/2,(src.rows+1)/2)。
borderType - 和pyrUp()中相同。
2. 实现代码
void cv::pyrDown( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType )
{
Mat src = _src.getMat();
Size dsz = _dsz == Size() ? Size((src.cols + 1)/2, (src.rows + 1)/2) : _dsz;
_dst.create( dsz, src.type() );
Mat dst = _dst.getMat();
#ifdef HAVE_TEGRA_OPTIMIZATION
if(borderType == BORDER_DEFAULT && tegra::pyrDown(src, dst))
return;
#endif
int depth = src.depth();
PyrFunc func = 0;
if( depth == CV_8U )
func = pyrDown_<FixPtCast<uchar, 8>, PyrDownVec_32s8u>;
else if( depth == CV_16S )
func = pyrDown_<FixPtCast<short, 8>, NoVec<int, short> >;
else if( depth == CV_16U )
func = pyrDown_<FixPtCast<ushort, 8>, NoVec<int, ushort> >;
else if( depth == CV_32F )
func = pyrDown_<FltCast<float, 8>, PyrDownVec_32f>;
else if( depth == CV_64F )
func = pyrDown_<FltCast<double, 8>, NoVec<double, double> >;
else
CV_Error( CV_StsUnsupportedFormat, "" );
func( src, dst, borderType );
}
三、实例分析
实验平台:VS2010 + OpenCV2.4.9
#include<iostream>
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<opencv2\video\background_segm.hpp>
using namespace cv;
using namespace std;
void helpProcess();
int main()
{
helpProcess();
Mat src,tmp,dst;
src=imread("Car.jpg");
if (src.empty()){
cvNamedWindow("图像读取失败,请确认路径是否正确!\n");
waitKey();
return -1;
}
tmp=src;
dst=tmp;
cvNamedWindow("图像金字塔");
imshow("图像金字塔",dst);
while(1)
{
int c;
c=waitKey(10);
switch(c)
{
case 27:
return 1;
break;
case 'u':
pyrUp(tmp,dst,Size(tmp.cols*2,tmp.rows*2));
cout<<"进行图像放大,图像尺寸*2!\n";
break;
case 'd':
pyrDown(tmp,dst,Size(tmp.cols/2,tmp.rows/2));
cout<<"进行图像缩小,图像尺寸/2!\n";
break;
}
imshow("图像金字塔",dst);
tmp=dst;
}
return 1;
}
void helpProcess()
{
cout<<"Opencv中图像金字塔的实例程序!\n";
cout<<"--------------------------------\n";
cout<<" [u]->向上采样 \n";
cout<<" [d]->向下采样 \n";
cout<<" [esc]->退出程序 \n";
cout<<"--------------------------------\n";
}
向下取样会损失部分信息。图像金字塔在图像处理中应用广泛,主要有图像分割,将金字塔相邻图像层次G(i+1) <--> G(i) 的像素联想成父子关系的系统,在金字塔的高层图像中进行快速分割,然后一级一级精炼进行精准分割。OpenCV中也提供了相应的函数PyrSegmentation()。