图像处理中的金字塔原理

2019-10-13 20:44:28 qq_32172681 阅读数 87
  • 图像金字塔

    学完本课程,可以牢固掌握人工智能计算机视觉的基础知识与常见算法,对图像处理、图像分析可以理解与应用 掌握python opencv框架编程与相关算法原理与函数使用,可以完成基础的图像处理与图像分析项目任务 为进一步...

    165人学习 贾志刚
    免费试看

一、图像缩小

先高斯模糊,再降采样,需要一次次重复,不能一次到底

 

二、图像扩大

先扩大,再卷积或者使用拉普拉斯金字塔

 

三、金字塔类型

1、高斯金字塔

用于下采样。

高斯金字塔是最基本的图像塔。

原理:首先将原图像作为最底层图像G0(高斯金字塔的第0层),利用高斯核(5*5)对其进行卷积,然后对卷积后的图像进行下采样(去除偶数行和列)得到上一层图像G1, 将此图像作为输入,重复卷积和下采样操作得到更上一层图像,反复迭代多次,形成一个金字塔形的图像数据结构,即高斯金字塔。

2、拉普拉斯金字塔

用于重建图像,也就是预测残差,对图像进行最大程度的还原。比如一幅小图像重建为一幅大图。

 原理:用高斯金字塔的每一层图像减去其上一层图像上采样并高斯卷积之后的预测图像,得到一系列的差值图像即为 LP 分解图像。

 

四、采样类型

1、上采样

就是图片放大(所谓上嘛,就是变大),使用PryUp函数。 上采样步骤:先将图像在每个方向放大为原来的两倍,新增的行和列用0填充,再使用先前同样的内核与放大后的图像卷积,获得新增像素的近似值。

2、下采样

就是图片缩小(所谓下嘛,就是变小),使用PryDown函数。下采样将步骤:先对图像进行高斯内核卷积 ,再将所有偶数行和列去除。

总之,上、下采样都存在一个严重的问题,那就是图像变模糊了,因为缩放的过程中发生了信息丢失的问题。要解决这个问题,就得用拉普拉斯金字塔。

 

2016-09-22 23:29:09 xbcReal 阅读数 23955
  • 图像金字塔

    学完本课程,可以牢固掌握人工智能计算机视觉的基础知识与常见算法,对图像处理、图像分析可以理解与应用 掌握python opencv框架编程与相关算法原理与函数使用,可以完成基础的图像处理与图像分析项目任务 为进一步...

    165人学习 贾志刚
    免费试看

在接触高斯金字塔的时候,对于它的作用疑惑的时候有人这么跟我讲..它就是模拟人类的视觉,近处的东西看着大,并且能够看到东西的细节所在,当把这东西从眼前拿到几米外,虽然还是能看到东西,但也只能窥见它的轮廓了,对于细节无从得知。高斯金字塔就是模拟了这样的一种视觉特性,当对图像进行下采样的时候,图像的分辨率降低,就好比把东西从近处拿到了远处。。以下内容为转载的,转自http://blog.csdn.net/poem_qianmo/article/details/26157633,开始一直迷惑的地方是下采样的下以及上采样的上到底是指的什么,这篇博客给了明确的回答,即这里的向下与向上采样,是对图像的尺寸而言的(和金字塔的方向相反),向上就是图像尺寸加倍,向下就是图像尺寸减半。另外找到一个教程讲高斯金字塔上下采样的,链接http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/pyramids/pyramids.html

这篇文章里,我们将一起探讨图像金字塔的一些基本概念,如何使用OpenCV函数 pyrUp 和 pyrDown 对图像进行向上和向下采样,以及了解了专门用于缩放图像尺寸的resize函数的用法。此博文一共有四个配套的简短的示例程序,其详细注释过的代码都在文中贴出,且文章最后提供了综合示例程序的下载。

先尝鲜一下其中一个示例程序的运行截图:


 

 

 


一、引言

 


我们经常会将某种尺寸的图像转换为其他尺寸的图像,如果放大或者缩小图片的尺寸,笼统来说的话,可以使用OpenCV为我们提供的如下两种方式:

 

<1>resize函数。这是最直接的方式,

<2>pyrUp( )、pyrDown( )函数。即图像金字塔相关的两个函数,对图像进行向上采样,向下采样的操作。

 

pyrUp、pyrDown其实和专门用作放大缩小图像尺寸的resize在功能上差不多,披着图像金字塔的皮,说白了还是在对图像进行放大和缩小操作。另外需要指出的是,pyrUp、pyrDown在OpenCV的imgproc模块中的Image Filtering子模块里。而resize在imgproc 模块的Geometric Image Transformations子模块里。

 

这篇文章中,我们将先介绍图像金字塔的原理,接着介绍resize函数,然后是pyrUp和pyrDown函数,最后是一个综合示例程序。

 

 

 


二、关于图像金字塔



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

图像金字塔最初用于机器视觉和图像压缩,一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。

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

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

 

 





素材图是帅气的美剧《绿箭侠》里面的绿箭侠Oliver Queen。

 


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

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

两者的简要区别:高斯金字塔用来向下降采样图像,而拉普拉斯金字塔则用来从金字塔底层图像中向上采样重建一个图像。

要从金字塔第i层生成第i+1层(我们表示第i+1层为G_i+1),我们先要用高斯核对G_1进行卷积,然后删除所有偶数行和偶数列。当然的是,新得到图像面积会变为源图像的四分之一。按上述过程对输入图像G_0执行操作就可产生出整个金字塔。


当图像向金字塔的上层移动时,尺寸和分辨率就降低。OpenCV中,从金字塔中上一级图像生成下一级图像的可以用PryDown。而通过PryUp将现有的图像在每个维度都放大两遍。

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

概括起来就是:

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

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

 

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

PryDown( )是一个会丢失信息的函数。为了恢复原来更高的分辨率的图像,我们要获得由降采样操作丢失的信息,这些数据就和拉普拉斯金字塔有关系了。

 

 



2.1 高斯金字塔




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

 

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




2.1.1 对图像的向下取样



为了获取层级为 G_i+1 的金字塔图像,我们采用如下方法:


<1>对图像G_i进行高斯内核卷积

<2>将所有偶数行和列去除


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


以上就是对图像的向下取样操作,即缩小图像。





2.1.2 对图像的向上取样



如果想放大图像,则需要通过向上取样操作得到,具体做法如下:


<1>将图像在每个方向扩大为原来的两倍,新增的行和列以0填充

<2>使用先前同样的内核(乘以4)与放大后的图像卷积,获得 “新增像素”的近似值


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

那么,我们接下来一起看一看拉普拉斯金字塔的概念吧。





2.2 拉普拉斯金字塔



下式是拉普拉斯金字塔第i层的数学定义:

 

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

我们下文将要介绍的pryUp,就是在进行上面这个式子的运算。

因此,我们可以直接用OpenCV进行拉普拉斯运算:

 

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

 

整个拉普拉斯金字塔运算过程可以通过下图来概括:

 

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

 

另外再提一点,关于图像金字塔非常重要的一个应用就是实现图像分割。图像分割的话,先要建立一个图像金字塔,然后在G_i和G_i+1的像素直接依照对应的关系,建立起”父与子“关系。而快速初始分割可以先在金字塔高层的低分辨率图像上完成,然后逐层对分割加以优化。

 





三、resize( )函数剖析



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

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

很多时候,我们并不用考虑第二个参数dst的初始图像尺寸和类型(即直接定义一个Mat类型,不用对其初始化),因为其尺寸和类型可以由src,dsize,fx和fy这其他的几个参数来确定。

 

看一下它的函数原型:

  1. C++: void resize(InputArray src,OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR )  


  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
  • 第二个参数,OutputArray类型的dst,输出图像,当其非零时,有着dsize(第三个参数)的尺寸,或者由src.size()计算出来。
  • 第三个参数,Size类型的dsize,输出图像的大小;如果它等于零,由下式进行计算:


其中,dsize,fx,fy都不能为0。

  • 第四个参数,double类型的fx,沿水平轴的缩放系数,有默认值0,且当其等于0时,由下式进行计算:

 

  • 第五个参数,double类型的fy,沿垂直轴的缩放系数,有默认值0,且当其等于0时,由下式进行计算:

  • 第六个参数,int类型的interpolation,用于指定插值方式,默认为INTER_LINEAR(线性插值)。

可选的插值方式如下:

  • INTER_NEAREST - 最近邻插值
  • INTER_LINEAR - 线性插值(默认值)
  • INTER_AREA - 区域插值(利用像素区域关系的重采样插值)
  • INTER_CUBIC –三次样条插值(超过4×4像素邻域内的双三次插值)
  • INTER_LANCZOS4 -Lanczos插值(超过8×8像素邻域的Lanczos插值)

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

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

 

关于插值,我们看几张图就能更好地理解。先看原图:



当进行6次图像缩小接着6次图像放大操作后,两种不同的插值方式得到的效果图:

         


效果很明显,第一张全是一个个的像素,非常影响美观。另外一张却有雾化的朦胧美感,所以插值方式的选择,对经过多次放大缩小的图片最终得到的效果是有很大影响的。

 

接着我们来看两种resize的调用范例。

 

方式一,调用范例:

  1. Mat dst=Mat::zeros(512 ,512, CV_8UC3 );//新建一张512x512尺寸的图片  
  2. Mat src=imread(“1.jpg”);  
  3. //显式指定dsize=dst.size(),那么fx和fy会其计算出来,不用额外指定。  
  4. resize(src, dst, dst.size());  

方式二、调用范例:

  1. Mat dst;  
  2. Mat src=imread(“1.jpg”)  
  3.  //指定fx和fy,让函数计算出目标图像的大小。  
  4. resize(src, dst, Size(), 0.5, 0.5);  


接着我们看看完整的示例程序:

 

  1. //-----------------------------------【头文件包含部分】---------------------------------------  
  2. //      描述:包含程序所依赖的头文件  
  3. //----------------------------------------------------------------------------------------------   
  4. #include <opencv2/opencv.hpp>  
  5. #include <opencv2/imgproc/imgproc.hpp>  
  6.   
  7. //-----------------------------------【命名空间声明部分】---------------------------------------  
  8. //      描述:包含程序所使用的命名空间  
  9. //-----------------------------------------------------------------------------------------------   
  10. using namespace cv;  
  11. //-----------------------------------【main( )函数】--------------------------------------------  
  12. //      描述:控制台应用程序的入口函数,我们的程序从这里开始  
  13. //-----------------------------------------------------------------------------------------------  
  14. int main( )  
  15. {  
  16.     //载入原始图     
  17.     Mat srcImage = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
  18.     Mat tmpImage,dstImage1,dstImage2;//临时变量和目标图的定义  
  19.     tmpImage=srcImage;//将原始图赋给临时变量  
  20.   
  21.     //显示原始图    
  22.     imshow("【原始图】", srcImage);    
  23.   
  24.     //进行尺寸调整操作  
  25.     resize(tmpImage,dstImage1,Size( tmpImage.cols/2, tmpImage.rows/2 ),(0,0),(0,0),3);  
  26.     resize(tmpImage,dstImage2,Size( tmpImage.cols*2, tmpImage.rows*2 ),(0,0),(0,0),3);  
  27.   
  28.     //显示效果图    
  29.     imshow("【效果图】之一", dstImage1);    
  30.     imshow("【效果图】之二", dstImage2);    
  31.   
  32.     waitKey(0);    
  33.     return 0;    
  34. }  

 

程序运行截图:

 

 

 

 


 

四、pyrUp()函数剖析



pyrUp( )函数的作用是向上采样并模糊一张图像,说白了就是放大一张图片。

 

  1. C++: void pyrUp(InputArray src, OutputArraydst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT )  

  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
  • 第二个参数,OutputArray类型的dst,输出图像,和源图片有一样的尺寸和类型。
  • 第三个参数,const Size&类型的dstsize,输出图像的大小;有默认值Size(),即默认情况下,由Size(src.cols*2,src.rows*2)来进行计算,且一直需要满足下列条件:

  • 第四个参数,int类型的borderType,又来了,边界模式,一般我们不用去管它。

 

pyrUp函数执行高斯金字塔的采样操作,其实它也可以用于拉普拉斯金字塔的。

首先,它通过插入可为零的行与列,对源图像进行向上取样操作,然后将结果与pyrDown()乘以4的内核做卷积,就是这样。

 

直接看完整的示例程序:

 

  1. //-----------------------------------【头文件包含部分】---------------------------------------  
  2. //      描述:包含程序所依赖的头文件  
  3. //----------------------------------------------------------------------------------------------   
  4. #include <opencv2/opencv.hpp>  
  5. #include <opencv2/imgproc/imgproc.hpp>  
  6.   
  7. //-----------------------------------【命名空间声明部分】---------------------------------------  
  8. //      描述:包含程序所使用的命名空间  
  9. //-----------------------------------------------------------------------------------------------   
  10. using namespace cv;  
  11. //-----------------------------------【main( )函数】--------------------------------------------  
  12. //      描述:控制台应用程序的入口函数,我们的程序从这里开始  
  13. //-----------------------------------------------------------------------------------------------  
  14. int main( )  
  15. {  
  16.     //载入原始图     
  17.     Mat srcImage = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
  18.     Mat tmpImage,dstImage;//临时变量和目标图的定义  
  19.     tmpImage=srcImage;//将原始图赋给临时变量  
  20.   
  21.     //显示原始图    
  22.     imshow("【原始图】", srcImage);    
  23.     //进行向上取样操作  
  24.     pyrUp( tmpImage, dstImage, Size( tmpImage.cols*2, tmpImage.rows*2 ) );  
  25.     //显示效果图    
  26.     imshow("【效果图】", dstImage);    
  27.   
  28.     waitKey(0);    
  29.   
  30.     return 0;    
  31. }  


程序运行截图:

  

 





五、pyrDown()函数剖析



pyrDown( )函数的作用是向下采样并模糊一张图片,说白了就是缩小一张图片。

 

  1. C++: void pyrDown(InputArray src,OutputArray dst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT)  


  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
  • 第二个参数,OutputArray类型的dst,输出图像,和源图片有一样的尺寸和类型。
  • 第三个参数,const Size&类型的dstsize,输出图像的大小;有默认值Size(),即默认情况下,由Size Size((src.cols+1)/2, (src.rows+1)/2)来进行计算,且一直需要满足下列条件:



该pyrDown函数执行了高斯金字塔建造的向下采样的步骤。首先,它将源图像与如下内核做卷积运算:

 

 

接着,它便通过对图像的偶数行和列做插值来进行向下采样操作。

 

依然是看看完整的示例程序:

  1. //-----------------------------------【头文件包含部分】---------------------------------------  
  2. //      描述:包含程序所依赖的头文件  
  3. //----------------------------------------------------------------------------------------------   
  4. #include <opencv2/opencv.hpp>  
  5. #include <opencv2/imgproc/imgproc.hpp>  
  6.   
  7. //-----------------------------------【命名空间声明部分】---------------------------------------  
  8. //      描述:包含程序所使用的命名空间  
  9. //-----------------------------------------------------------------------------------------------   
  10. using namespace cv;  
  11. //-----------------------------------【main( )函数】--------------------------------------------  
  12. //      描述:控制台应用程序的入口函数,我们的程序从这里开始  
  13. //-----------------------------------------------------------------------------------------------  
  14. int main( )  
  15. {  
  16.     //载入原始图     
  17.     Mat srcImage = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
  18.     Mat tmpImage,dstImage;//临时变量和目标图的定义  
  19.     tmpImage=srcImage;//将原始图赋给临时变量  
  20.   
  21.     //显示原始图    
  22.     imshow("【原始图】", srcImage);    
  23.     //进行向下取样操作  
  24.     pyrDown( tmpImage, dstImage, Size( tmpImage.cols/2, tmpImage.rows/2 ) );  
  25.     //显示效果图    
  26.     imshow("【效果图】", dstImage);    
  27.   
  28.     waitKey(0);    
  29.   
  30.     return 0;    
  31. }  


程序运行截图:

  

 





六、综合示例篇——在实战中熟稔

 

 

依然是每篇文章都会配给大家的一个详细注释的博文配套示例程序,把这篇文章中介绍的知识点以代码为载体,展现给大家。

这个示例程序中,分别演示了用resize,pryUp,pryDown来让源图像进行放大缩小的操作,分别用键盘按键1、2、3、4、A、D、W、S来控制图片的放大与缩小:

 


OK,上详细注释的代码吧:

  1. //-----------------------------------【程序说明】----------------------------------------------  
  2. //      程序名称::《 【OpenCV入门教程之十三】OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放》 博文配套源码   
  3. //      开发所用IDE版本:Visual Studio 2010  
  4. //              开发所用OpenCV版本:   2.4.9  
  5. //      2014年5月18日 Create by 浅墨  
  6. //----------------------------------------------------------------------------------------------  
  7.   
  8. //-----------------------------------【头文件包含部分】---------------------------------------  
  9. //      描述:包含程序所依赖的头文件  
  10. //----------------------------------------------------------------------------------------------   
  11. #include <opencv2/opencv.hpp>  
  12. #include <opencv2/highgui/highgui.hpp>  
  13. #include <opencv2/imgproc/imgproc.hpp>  
  14.   
  15. //-----------------------------------【宏定义部分】--------------------------------------------  
  16. //  描述:定义一些辅助宏  
  17. //------------------------------------------------------------------------------------------------  
  18. #define WINDOW_NAME "【程序窗口】"        //为窗口标题定义的宏  
  19.   
  20.   
  21. //-----------------------------------【命名空间声明部分】--------------------------------------  
  22. //      描述:包含程序所使用的命名空间  
  23. //-----------------------------------------------------------------------------------------------   
  24. using namespace std;  
  25. using namespace cv;  
  26.   
  27.   
  28. //-----------------------------------【全局变量声明部分】--------------------------------------  
  29. //      描述:全局变量声明  
  30. //-----------------------------------------------------------------------------------------------  
  31. Mat g_srcImage, g_dstImage, g_tmpImage;  
  32.   
  33.   
  34. //-----------------------------------【全局函数声明部分】--------------------------------------  
  35. //      描述:全局函数声明  
  36. //-----------------------------------------------------------------------------------------------  
  37. static void ShowHelpText();  
  38.   
  39.   
  40. //-----------------------------------【ShowHelpText( )函数】----------------------------------  
  41. //      描述:输出一些帮助信息  
  42. //----------------------------------------------------------------------------------------------  
  43. static void ShowHelpText()  
  44. {  
  45.     //输出一些帮助信息  
  46.     printf("\n\n\n\t欢迎来到OpenCV图像金字塔和resize示例程序~\n\n");  
  47.     printf( "\n\n\t按键操作说明: \n\n"  
  48.         "\t\t键盘按键【ESC】或者【Q】- 退出程序\n"  
  49.         "\t\t键盘按键【1】或者【W】- 进行基于【resize】函数的图片放大\n"  
  50.         "\t\t键盘按键【2】或者【S】- 进行基于【resize】函数的图片缩小\n"  
  51.         "\t\t键盘按键【3】或者【A】- 进行基于【pyrUp】函数的图片放大\n"  
  52.         "\t\t键盘按键【4】或者【D】- 进行基于【pyrDown】函数的图片缩小\n"  
  53.         "\n\n\t\t\t\t\t\t\t\t by浅墨\n\n\n"  
  54.         );  
  55. }  
  56.   
  57. //-----------------------------------【main( )函数】--------------------------------------------  
  58. //      描述:控制台应用程序的入口函数,我们的程序从这里开始  
  59. //-----------------------------------------------------------------------------------------------  
  60. int main( )  
  61. {  
  62.     //改变console字体颜色  
  63.     system("color 2F");    
  64.   
  65.     //显示帮助文字  
  66.     ShowHelpText();  
  67.   
  68.     //载入原图  
  69.     g_srcImage = imread("1.jpg");//工程目录下需要有一张名为1.jpg的测试图像,且其尺寸需被2的N次方整除,N为可以缩放的次数  
  70.     if( !g_srcImage.data ) { printf("Oh,no,读取srcImage错误~! \n"); return false; }  
  71.   
  72.     // 创建显示窗口  
  73.     namedWindow( WINDOW_NAME, CV_WINDOW_AUTOSIZE );  
  74.     imshow(WINDOW_NAME, g_srcImage);  
  75.   
  76.     //参数赋值  
  77.     g_tmpImage = g_srcImage;  
  78.     g_dstImage = g_tmpImage;  
  79.   
  80.      int key =0;  
  81.   
  82.     //轮询获取按键信息  
  83.     while(1)  
  84.     {  
  85.         key=waitKey(9) ;//读取键值到key变量中  
  86.   
  87.         //根据key变量的值,进行不同的操作  
  88.         switch(key)  
  89.         {  
  90.          //======================【程序退出相关键值处理】=======================    
  91.         case 27://按键ESC  
  92.             return 0;  
  93.             break;   
  94.   
  95.         case 'q'://按键Q  
  96.             return 0;  
  97.             break;   
  98.   
  99.          //======================【图片放大相关键值处理】=======================    
  100.         case 'a'://按键A按下,调用pyrUp函数  
  101.             pyrUp( g_tmpImage, g_dstImage, Size( g_tmpImage.cols*2, g_tmpImage.rows*2 ) );  
  102.             printf( ">检测到按键【A】被按下,开始进行基于【pyrUp】函数的图片放大:图片尺寸×2 \n" );       
  103.             break;   
  104.   
  105.         case 'w'://按键W按下,调用resize函数  
  106.             resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols*2, g_tmpImage.rows*2 ));  
  107.             printf( ">检测到按键【W】被按下,开始进行基于【resize】函数的图片放大:图片尺寸×2 \n" );          
  108.             break;   
  109.   
  110.         case '1'://按键1按下,调用resize函数  
  111.             resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols*2, g_tmpImage.rows*2 ));  
  112.             printf( ">检测到按键【1】被按下,开始进行基于【resize】函数的图片放大:图片尺寸×2 \n" );  
  113.             break;   
  114.   
  115.         case '3'//按键3按下,调用pyrUp函数  
  116.             pyrUp( g_tmpImage, g_dstImage, Size( g_tmpImage.cols*2, g_tmpImage.rows*2 ));  
  117.             printf( ">检测到按键【3】被按下,开始进行基于【pyrUp】函数的图片放大:图片尺寸×2 \n" );  
  118.             break;   
  119.         //======================【图片缩小相关键值处理】=======================    
  120.         case 'd'//按键D按下,调用pyrDown函数  
  121.             pyrDown( g_tmpImage, g_dstImage, Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ));  
  122.             printf( ">检测到按键【D】被按下,开始进行基于【pyrDown】函数的图片缩小:图片尺寸/2\n" );  
  123.             break;   
  124.   
  125.         case  's' : //按键S按下,调用resize函数  
  126.             resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ));  
  127.             printf( ">检测到按键【S】被按下,开始进行基于【resize】函数的图片缩小:图片尺寸/2\n" );  
  128.             break;   
  129.   
  130.         case '2'://按键2按下,调用resize函数  
  131.             resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ),(0,0),(0,0),2);  
  132.             printf( ">检测到按键【2】被按下,开始进行基于【resize】函数的图片缩小:图片尺寸/2\n" );  
  133.             break;   
  134.   
  135.         case '4'//按键4按下,调用pyrDown函数  
  136.             pyrDown( g_tmpImage, g_dstImage, Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ) );  
  137.             printf( ">检测到按键【4】被按下,开始进行基于【pyrDown】函数的图片缩小:图片尺寸/2\n" );  
  138.             break;   
  139.         }  
  140.           
  141.         //经过操作后,显示变化后的图  
  142.         imshow( WINDOW_NAME, g_dstImage );  
  143.   
  144.         //将g_dstImage赋给g_tmpImage,方便下一次循环  
  145.         g_tmpImage = g_dstImage;  
  146.     }  
  147.   
  148.     return 0;  
  149. }  


放一些程序运行截图。

 原始图:


经过多次按键后的效果图:






另外,还可以放大图像到很大的尺寸,上图的话会很凶残而且不美观。所以就不放出超过原图尺寸的截图了。


好的,就放出这些效果图,具体更多的运行效果大家就自己下载示例程序回去玩~

 

本篇文章的配套源代码请点击这里下载:

 

【浅墨OpenCV入门教程之十三】配套源代码下载

 

 

OK,今天的内容大概就是这些,我们下篇文章见:)


2014-12-11 17:06:59 xw20084898 阅读数 13797
  • 图像金字塔

    学完本课程,可以牢固掌握人工智能计算机视觉的基础知识与常见算法,对图像处理、图像分析可以理解与应用 掌握python opencv框架编程与相关算法原理与函数使用,可以完成基础的图像处理与图像分析项目任务 为进一步...

    165人学习 贾志刚
    免费试看

图像金字塔

图像金字塔是通过将原始图像经过平滑、下采样所生成一系列具有不同分辨率的图像的集合。金字塔结构(Pyramid)适于多分辨率处理的一种图像存储数据结构。

最常用的生成图像金字塔的方法是采用高斯函数平滑图像,每次将分辨率降低为原来的一半,由此得到一个图像序列{ML,ML-1,……,M0},图像金字塔的存储量为N^2*(1+1/4+1/16+...)=(4*N^2)/3

example

如上图:最右边为原始图像,从右到左依次为使用高斯平滑1次、2次和3次后的图像,这些图像共同组成了图像金字塔。

图像金字塔这种结构在图像处理中有广泛的用途。最著名的特征匹配算子SIFT就是通过构造图像金字塔来完成的。有关图像金字塔在SIFT特征提取算法中的应用可参见Rachel Zhang的博文"SIFT特征提取分析"

程序分析

/*
 * FileName : pyramids.cpp
 * Author   : xiahouzuoxin @163.com
 * Version  : v1.0
 * Date     : Sat 20 Sep 2014 07:04:29 PM CST
 * Brief    : 
 * 
 * Copyright (C) MICL,USTB
 */
#include <iostream>
#include "cv.h" 
#include "highgui.h"
#include "opencv2/imgproc/imgproc.hpp"

using namespace std;
using namespace cv;

const char *wn = "Pyramids Demo";

int main(int argc, char *argv[])
{
    if (argc < 2) {
        cout<<"Usage: ./pyramids [file name]"<<endl;
        return -1;
    }

    Mat src = imread(argv[1]);
    if (!src.data) {
        cout<<"Error: read image error."<<endl;
        return -1;
    }

    /* Size of input image must be 2^n */
    if ( src.cols & (src.cols-1) ) {  // src.cols > 0 first
        cout<<"Error: input image's column must be 2^n"<<endl;
        return -1;
    }
    if ( src.rows & (src.rows-1) ) {  // src.cols > 0 first
        cout<<"Error: input image's row must be 2^n"<<endl;
        return -1;
    }

    cout<<"User Guide:"<<endl;
    cout<<"---------------------"<<endl;
    cout<<"u   -> Zoom out"<<endl;
    cout<<"d   -> Zoom in"<<endl;
    cout<<"ESC -> Exit program"<<endl;

    namedWindow(wn, WINDOW_AUTOSIZE);
    imshow(wn, src);

    Mat cur = src;
    Mat dst = cur;

    int end_while = 0;
    while(!end_while) {
        char c;

        c = waitKey(10);
        switch (c) {
        case 27:   /* ESC */
            end_while = 1;
            break;
        case 'u':
            pyrUp(cur, dst, Size(cur.cols*2, cur.rows*2));
            imshow(wn, dst);
            cur = dst;
            break;
        case 'd':
            pyrDown(cur, dst, Size(cur.cols/2, cur.rows/2));
            imshow(wn, dst);
            cur = dst;
            break;
        default:
            break;
        }

    }
}
  1. 使用高斯图像金字塔进行降采样和插值的函数分别是pyrDownpyrUp,参数依次为原图像、采样结果图像、采样后的图像尺寸。

  2. 上述程序中的降采样操作因为都是按2为倍数进行的,因此要求输入图像的长宽都必须是2^n。if ( src.cols & (src.cols-1) )是用来判断原图像的列是否为2^n的语句。请仔细体会这种判断某个数是否是2^n的方法——x*(x-1)返回0表示x是2^n,否则不是。

结果显示

下面一系列图片展现的是先将原图像通过图像金字塔降采样(会存在数据丢失),再通过金字塔插值恢复图像过程中图像的变化过程。由于降采样过程中存在数据丢失,所以可以看到恢复到原图像大小后的图像比原图像模糊。

origin

图注 原图像

div2

图注 1次降采样后的图像

div4

图注 2次降采样后的图像

MUL2

图注 2次降采样后,再经过1次图像金字塔插值操作后的图像,大小与1次降采样后图像相同,但变得模糊

MUL4

图注 2次降采样后,再经过2次图像金字塔插值操作后的图像,大小与原图相同,但变得模糊

2019-07-19 22:43:36 qq_29507011 阅读数 682
  • 图像金字塔

    学完本课程,可以牢固掌握人工智能计算机视觉的基础知识与常见算法,对图像处理、图像分析可以理解与应用 掌握python opencv框架编程与相关算法原理与函数使用,可以完成基础的图像处理与图像分析项目任务 为进一步...

    165人学习 贾志刚
    免费试看

高斯滤波的核就是一个求平均值的卷积核
高斯金字塔也是一个高斯核,只是步长为2,卷积后图片缩小二倍。高斯金字塔的目的是在图像不同大小时可以获得不同的信息,比如说图像较小时可以获得轮廓,图像较大时可以获得细节
使用函数cv2.pyrDown(image)
拉普拉斯金字塔作用后的效果就是不同层次下提取边缘,原理就是基于高斯金字塔下,从高斯金字塔塔尖开始,每层图片扩大二倍,与下一层做差,得到的新图
使用函数cv2.pyrUp(image) cv2.subtract(image1,image2)

值得注意的坑是拉普拉斯金字塔的原图必须是2的n次方,不然会出现BUG

实现代码如下:
高斯模糊卷积核写错了,纠正方法见图像处理(十六)对高斯模糊进行了重新定义,这个卷积核是均值模糊

import numpy as np
import cv2
import random

#高斯模糊未考虑权重
# gausf = np.asarray([[1/9,1/9,1/9],[1/9,1/9,1/9],[1/9,1/9,1/9]])
# image = cv2.imread('test.jpg',0)
# hight,width = image.shape
# for i in range(5000):
# 	h = random.randint(1,hight-1)
# 	w = random.randint(1,width-1)
# 	image[h,w] = 255
# img = np.zeros((hight,width))

# for i in range(1,hight-1):
# 	for j in range(1,width-1):
# 		img[i-1,j-1] = np.sum(image[i-1:i+2,j-1:j+2]*gausf)

# cv2.imshow('image',image)
# cv2.imshow('test',img.astype(np.uint8))
# cv2.waitKey(0)
# cv2.destroyWindow()

#高斯金字塔
path = 'test.jpg'
image = cv2.imread(path,0)
image = cv2.resize(image,(512,512))
step = 5
def gaussian(image,step):
	image_list = []
	temp = image.copy()

	for i in range(step):
		img = cv2.pyrDown(temp)
		temp = img.copy()
		image_list.append(temp)

	return image_list
# for i in range(step):
# 	img = image_list[i]
# 	cv2.imshow('test'+str(i),img)


def lapulasi(image,step):
	lapu_list = []
	image_list = gaussian(image,step)
	le = len(image_list)
	for i in range(le-1,-1,-1):
		if (i-1)<0:
			ext = cv2.pyrUp(image_list[i])
			result = cv2.subtract(image,ext)
			lapu_list.append(result)
		else:
			ext = cv2.pyrUp(image_list[i])
			print(ext.shape)
			print(image_list[i-1].shape)
			result = cv2.subtract(image_list[i-1],ext)
			lapu_list.append(result)

	return lapu_list

lapu_list = lapulasi(image,step)

for i in lapu_list:
	cv2.imshow('test'+str(i),i)

cv2.waitKey(0)
cv2.destroyWindows()

效果图如下:
在这里插入图片描述
这是高斯金字塔

下面是拉普拉斯金字塔
在这里插入图片描述

2013-08-27 09:53:20 taoyanbian1022 阅读数 2854
  • 图像金字塔

    学完本课程,可以牢固掌握人工智能计算机视觉的基础知识与常见算法,对图像处理、图像分析可以理解与应用 掌握python opencv框架编程与相关算法原理与函数使用,可以完成基础的图像处理与图像分析项目任务 为进一步...

    165人学习 贾志刚
    免费试看

以多分辨率来解释图像的一种有效但概念简单的结构就是图像金字塔。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低的图像集合。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。当向金字塔的上层移动时,尺寸和分辨率就降低。
对于一幅尺寸为2^J*2^J的图像,则其图像金字塔中间级的尺寸是2^j*2^j,其中j的理论取值返回是0,1,2,...,(J-1),J。但是由于当j很小时,图像将丢失大部分信息,所以一般限制只用P+1级来减少原来图像近似值的尺寸,也就是j的实际取值是(J-P),(J-P+1),...(J-1),J。
而一般处理这种分辨率变化,简单的方法就是隔行取值来生成低分辨率的图像,复杂点的方法就涉及到图像的内插和过滤等步骤了,当然复杂点的方法使得生成的低分辨率下的图像更加平滑和真实。对某幅原始图像生成了一系列的低分辨率图像之后,这些图像和原始图像一起就形成了图像金字塔。
例如,对于512*512的图像,该图像的4级图像金字塔就是:

  1. 金字塔顶层   64*64
  2.     |     128*128
  3.     |     256*256
  4. 金字塔底层 512*512

当然,有时候也会对原图像进行扩展,生成分辨率更高的图像,然后把这个图像放到金字塔的更底层。这时候就需要更多处理,使得生成的图像保持真实性,避免色块和噪声等的产生。

在matlab里面,函数impyramid专门用于生成图像金字塔,直接调用就可以了。
格式:B = impyramid(A, direction)
作用:对A进行Gaussian金字塔变换,生成的图像是B。direction为“reduce”和“expand”,分别对应着分解和扩张。
注:
1.m*n的A“reduce”和“expand”后的维数分别对应ceil(M/2)*ceil(N/2)和(2*M-1)*(2*N-1)。
2.impyramid只对前两维有效。