Opencv图像识别从零到精通(29)-----图像金字塔,向上上下采样,resize插值

金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低

一、两个金字塔

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

高斯金字塔不同(DoG)又称为拉普拉斯金字塔,给出计算方式前,先加强一下定义

记得在上面我们定义了G0,G1,G2

G0下采样获得G1

G1上采样获得Upsample(G1),注意Upsample(G1)不等于G0,上采样和下采样不是可逆过程,这是因为下采样损失了图片信息

在此,给出计算拉普拉斯金字塔(DOG)的公式:L(i) = G(i) –Upsample(G(i+1))

 二、采样

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

    • <1>对图像G_i进行高斯内核卷积
    • <2>将所有偶数行和列去除
  • 对图像向下采样:pyrDown函数
    • <1>将图像在每个方向扩大为原来的两倍,新增的行和列以0填充
    • <2>使用先前同样的内核(乘以4)与放大后的图像卷积,获得 “新增像素”的近似值

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

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

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

 三、函数介绍

<span style="font-size:18px;">C++: void pyrUp(InputArray src, OutputArraydst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT )
</span>
  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
  • 第二个参数,OutputArray类型的dst,输出图像,和源图片有一样的尺寸和类型。
  • 第三个参数,const Size&类型的dstsize,输出图像的大小;有默认值Size(),即默认情况下,由Size(src.cols*2,src.rows*2)来进行计算,且一直需要满足下列条件:

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

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函数执行了高斯金字塔建造的向下采样的步骤。首先,它将源图像与如下内核做卷积运算:

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

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

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

<span style="font-size:18px;">C++: void resize(InputArray src,OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR )</span>
  • 第一个参数,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(效率较高,速度较快,推荐使用)。

四、插值介绍

1、最邻近元法

  这是最简单的一种插值方法,不需要计算,在待求象素的四邻象素中,将距离待求象素最近的邻象素灰度赋给待求象素。设i+u, j+v(i, j为正整数, u, v为大于零小于1的小数,下同)为待求象素坐标,则待求象素灰度的值 f(i+u, j+v) 如下图所示:

如果(i+u, j+v)落在A区,即u<0.5, v<0.5,则将左上角象素的灰度值赋给待求象素,同理,落在B区则赋予右上角的象素灰度值,落在C区则赋予左下角象素的灰度值,落在D区则赋予右下角象素的灰度值。

最邻近元法计算量较小,但可能会造成插值生成的图像灰度上的不连续,在灰度变化的地方可能出现明显的锯齿状。

2、双线性内插法

双线性内插法是利用待求象素四个邻象素的灰度在两个方向上作线性内插,如下图所示:

对于 (i, j+v),f(i, j) 到 f(i, j+1) 的灰度变化为线性关系,则有:

      f(i, j+v) = [f(i, j+1) - f(i, j)] * v + f(i, j)

同理对于 (i+1, j+v) 则有:

f(i+1, j+v) = [f(i+1, j+1) - f(i+1, j)] * v + f(i+1, j)

从f(i, j+v) 到 f(i+1, j+v) 的灰度变化也为线性关系,由此可推导出待求象素灰度的计算式如下:

f(i+u, j+v) = (1-u) * (1-v) * f(i, j) + (1-u) * v * f(i, j+1) + u * (1-v) * f(i+1, j) + u * v * f(i+1, j+1)

双线性内插法的计算比最邻近点法复杂,计算量较大,但没有灰度不连续的缺点,结果基本令人满意。它具有低通滤波性质,使高频分量受损,图像轮廓可能会有一点模糊。

3、三次内插法

该方法利用三次多项式S(x)求逼近理论上最佳插值函数sin(x)/x, 其数学表达式为:

待求像素(x, y)的灰度值由其周围16个灰度值加权内插得到,如下图:

待求像素的灰度计算式如下:

f(x, y) = f(i+u, j+v) = ABC

其中:

三次曲线插值方法计算量较大,但插值后的图像效果最好。

五、综合示例

<span style="font-size:18px;">#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
Mat g_srcImage, g_dstImage, g_tmpImage;
int main( )
{  

    ShowHelpText();
    g_srcImage = imread("lena.jpg");
    if( !g_srcImage.data ) { printf("Oh,no,读取srcImage错误~! \n"); return false; }
    namedWindow( WINDOW_NAME, CV_WINDOW_AUTOSIZE );
    imshow(WINDOW_NAME, g_srcImage);
    g_tmpImage = g_srcImage;
    g_dstImage = g_tmpImage;
     int key =0;
    while(1)
    {
        key=waitKey(9) ;
        //根据key变量的值,进行不同的操作
        switch(key)
        {  

        case 27:
            return 0;
            break;   

        case 'q':
            return 0;
            break;   

        case 'a'://按键A按下,调用pyrUp函数
            pyrUp( g_tmpImage, g_dstImage, Size( g_tmpImage.cols*2, g_tmpImage.rows*2 ) );
            printf( ">检测到按键【A】被按下,开始进行基于【pyrUp】函数的图片放大:图片尺寸×2 \n" );
            break;   

        case 'w'://按键W按下,调用resize函数
            resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols*2, g_tmpImage.rows*2 ));
            printf( ">检测到按键【W】被按下,开始进行基于【resize】函数的图片放大:图片尺寸×2 \n" );
            break;   

        case '1'://按键1按下,调用resize函数
            resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols*2, g_tmpImage.rows*2 ));
            printf( ">检测到按键【1】被按下,开始进行基于【resize】函数的图片放大:图片尺寸×2 \n" );
            break;   

        case '3': //按键3按下,调用pyrUp函数
            pyrUp( g_tmpImage, g_dstImage, Size( g_tmpImage.cols*2, g_tmpImage.rows*2 ));
            printf( ">检测到按键【3】被按下,开始进行基于【pyrUp】函数的图片放大:图片尺寸×2 \n" );
            break;
        //======================【图片缩小相关键值处理】=======================
        case 'd': //按键D按下,调用pyrDown函数
            pyrDown( g_tmpImage, g_dstImage, Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ));
            printf( ">检测到按键【D】被按下,开始进行基于【pyrDown】函数的图片缩小:图片尺寸/2\n" );
            break;   

        case  's' : //按键S按下,调用resize函数
            resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ));
            printf( ">检测到按键【S】被按下,开始进行基于【resize】函数的图片缩小:图片尺寸/2\n" );
            break;   

        case '2'://按键2按下,调用resize函数
            resize(g_tmpImage,g_dstImage,Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ),(0,0),(0,0),2);
            printf( ">检测到按键【2】被按下,开始进行基于【resize】函数的图片缩小:图片尺寸/2\n" );
            break;   

        case '4': //按键4按下,调用pyrDown函数
            pyrDown( g_tmpImage, g_dstImage, Size( g_tmpImage.cols/2, g_tmpImage.rows/2 ) );
            printf( ">检测到按键【4】被按下,开始进行基于【pyrDown】函数的图片缩小:图片尺寸/2\n" );
            break;
        }  

        //经过操作后,显示变化后的图
        imshow( WINDOW_NAME, g_dstImage );  

        //将g_dstImage赋给g_tmpImage,方便下一次循环
        g_tmpImage = g_dstImage;
    }  

    return 0;
}
</span>

                                  

六、matlab

<span style="font-size:18px;">I = imread('d:\lena.jpg');
A = imresize(I, 1.5, 'nearest');
B = imresize(I, 1.5, 'bilinear');
C = imresize(I, 1.5, 'bicubic');
subplot(2,2,1), imshow(I), title('original');
subplot(2,2,2), imshow(A), title('nearest');
subplot(2,2,3), imshow(B), title('bilinear');
subplot(2,2,4), imshow(C), title('bicubic');  </span>

图像识别算法交流 QQ群:145076161,欢迎图像识别与图像算法,共同学习与交流

时间: 2024-12-13 14:15:13

Opencv图像识别从零到精通(29)-----图像金字塔,向上上下采样,resize插值的相关文章

Opencv图像识别从零到精通(30)---重映射,仿射变换

一.序言 面对图像处理的时候,我们会旋转缩放图像,例如前面所提高的resize 插值改变,也是几何变换: 几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定为整数坐标.这时就需要灰度级差值将映射的新坐标匹配到输出像素之间.最简单的插值方法是最近邻插值,就是令输出像素的灰度值等于映射最近的位置像素,该方法可能会产生锯齿.这种方法也叫零阶插值,相应比较复杂的还有一阶和高阶插值. 除了插值算法感觉只要了解就可以了,图像处理中比较需要理

Opencv图像识别从零到精通(19)----Robert,prewitt,Sobel边缘检测

图像的边缘检测,是根据灰度的突变或者说不连续来检测,对于其中的算子有一阶导数和二价导数,这里先说基础的三种方法---Robert,prewitt,Sobel边缘检测.  一.梯度 首先介绍下梯度,梯度并非是一个数值,梯度严格意义上是一个向量,这个向量指向当前位置变化最快的方向,可以这么理解,当你站在一个山上,你有360°的方向可以选择,哪个方向下降速度最快(最陡峭),便是梯度方向,梯度的长度,表示为向量的长度,表示最大的变化速率. 梯度的数学表达: 在二维中只取前两项,也就是由x方向的偏微分和y

Opencv图像识别从零到精通(7)----图像平移、旋转、镜像

根据vc6.0c++的学习经验,如果可以很好的自己编程,让图像进行平移旋转这些操作,那么就好像能够清楚的看见图像的内部结构当然这里你怎么访问像素,这个可以自己选一种适合的,最多的是ptr指针,at也是挺多的.看着很简单的变换,可以对图像处理上手的更快,当然对于旋转可能就稍微i难了一点,不过opencv提供了resize(0,remap()等这样的函数,可以方便的让我们进行学习-特别是旋转的时候,有很多的变换,你可以任意旋转一个角度,也可能一直旋转,当然还可以保持图像大小不变的旋转和大小变换的旋转

Opencv图像识别从零到精通(31)----图像修补,分离合并通道

一.图像修复简介 图像修复是图像复原中的一个重要内容,其目的是利用图像现有的信息来恢复丢失的信息.可用于旧照片中丢失信息的恢复,视频文字去除以及视频错误隐藏等.简言之,图像修复就是对图像上信息缺损区域进行信息填充的过程,其目的就是为了对有信息缺损的图像进行复原,并且使得观察者无法察觉到图像曾经缺损或者已经修复      图像修复技术简单来说,就是利用那些被破坏区域的边缘,即是边缘的颜色和结构,繁殖和混合到损坏的图像中,来进行修复图像 目前存在两大类图像修复技术:一类是用于修复小尺度缺损的数字图像

Opencv图像识别从零到精通(26)---分水岭

分水岭是区域分割三个方法的最后一个,对于前景背景的分割有不错的效果. 分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭.分水岭的概念和形成可以通过模拟浸入过程来说明.在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭. 分水岭

Opencv图像识别从零到精通(33)----moravec角点、harris角点

一.角点 图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints).特征点(feature points) 被大量用于解决物体识别,图像识别.图像匹配.视觉跟踪.三维重建等一系列的问题.我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析.如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值. 图像特征类型可以被分为如下三种: <1>边缘                   

Opencv图像识别从零到精通(24)------漫水填充,种子填充,区域生长、孔洞填充

可以说从这篇文章开始,就结束了图像识别的入门基础,来到了第二阶段的学习.在平时处理二值图像的时候,除了要进行形态学的一些操作,还有有上一节讲到的轮廓连通区域的面积周长标记等,还有一个最常见的就是孔洞的填充,opencv这里成为漫水填充,其实也可以叫种子填充,或者区域生长,基本的原理是一样的,但是应用的时候需要注意一下,种子填充用递归的办法,回溯算法,漫水填充使用堆栈,提高效率,同时还提供了一种方式是扫描行.经常用来填充孔洞,现在来具体看看. 漫水填充:也就是用一定颜色填充联通区域,通过设置可连通

Opencv图像识别从零到精通(27)---grabcut

这是基于图论的分割方法,所以开始就先介绍了 Graph cuts,然后再到Grab cut   一. Graph cuts Graph cuts是一种十分有用和流行的能量优化算法,在计算机视觉领域普遍应用于前背景分割(Image segmentation).立体视觉(stereo vision).抠图(Image matting)等. 此类方法把图像分割问题与图的最小割(min cut)问题相关联.首先用一个无向图G=<V,E>表示要分割的图像,V和E分别是顶点(vertex)和边(edge)

Opencv图像识别从零到精通(34)---SIFI

   一.理论知识 Scale Invariant Feature Transform,尺度不变特征变换匹配算法,对于算法的理论介绍,可以参考这篇文章http://blog.csdn.net/qq_20823641/article/details/51692415,里面很详细,可以更好的学习.这里就不多介绍.后面就挑选重点的来说 二.SIFT 主要思想 SIFT算法是一种提取局部特征的算法,在尺度空间寻找极值点,提取位置,尺度,旋转不变量. 三.SIFT算法的主要特点: a) SIFT特征是图像