OpenCV学习笔记-图像金字塔

版权声明:原创部分都是自己总结的,如果转载请指明出处。觉得有帮助的老铁,请双击666! https://blog.csdn.net/qq_36387683/article/details/80346863

一、关于图像金字塔

以多个分辨率来表示图像的一种有效且概念简单的结构是图像金字塔,一个图像金字塔是一系列以金子塔形状排列的、分辨率逐渐降低的图像集合。——《数字图像处理》。


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


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


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

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

二、原理
2.1 高斯金字塔


高斯金字塔是通过高斯模糊和下采样获得一系列采样图像,也就是说第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)与放大后的图像卷积,获得“新增图像”的近似值
得到的图像即为放大后的图像,但是与原来的图像相比会发现比较模糊,因为在缩放的过程中已经丢失了一些信息,如果想在缩小和放大整个过程中减少信息的丢失,那么就需要用到拉普拉斯金字塔。

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

式中的 Gi 表示第i层的图像。而UP()操作是将源图像中位置为(x, y)的像素映射到目标图像的(2x+1, 2y+1)位置,即在进行向上取样。符号叉表示卷积,G5x5 为5x5的高斯内核。
我们可以直接用OpenCV进行拉普拉斯运算:

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

整个拉普拉斯金字塔运算过程可以通过下图来概括:
所以,我们可以将拉普拉斯金字塔理解为高斯金字塔的逆形式。

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

三、代码实现

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

#下采样
def pyramid_down_demo(img):
    level = 3 # 迭代次数
    temp = img.copy()
    pyramid_down_imgs = [] # 存放图像
    for i in range(level):
        dst = cv.pyrDown(temp)
        '''
        pyrDown(src, dst=None, dstsize=None, borderType=None)
        dstsize: 目标图像的大小,默认为 ((cols+1)/2, (rows+1)/2),在任何情况下,下面的条件都必须满足:
        |dstsize.width * 2 - src.cols|<=2
        |dstsize.height * 2 - src.rows|<=2
        '''
        pyramid_down_imgs.append(dst)
        cv.imshow('pyramid_down_'+str(i), dst)
        temp = dst

    return pyramid_down_imgs

#上采样
def pyramid_up_demo(img):
    pyramid_down_imgs = pyramid_down_demo(img)
    level = len(pyramid_down_imgs)
    for i in range(level-1, -1, -1):
        if (i-1) < 0:
            expand = cv.pyrUp(pyramid_down_imgs[i], dstsize=img.shape[:2])
            '''
            pyrUp(src, dst=None, dstsize=None, borderType=None)
            src: 源图像
            dst: 目标图像,和源图像有同样的大小和类型
            dstsize: 目标图像的大小,默认为(src.cols*2, src.rows*2) ,在任何情况下,下面的条件都必须满足:
            |dstsize.width - src.cols*2|<=(dstsize.width % 2)
            |dstsize.height - src.rows*2|<=(dstsize.height % 2)
            '''
            lpls = cv.subtract(img, expand)
            # 用源图像减去先缩小再放大的图像  得到拉普拉斯金字塔
            cv.imshow('pyramid_up_'+str(i), lpls)
        else:
            expand = cv.pyrUp(pyramid_down_imgs[i], dstsize=pyramid_down_imgs[i-1].shape[:2])
            lpls = cv.subtract(pyramid_down_imgs[i-1], expand)
            # 用源图像减去先缩小再放大的图像  得到拉普拉斯金字塔
            cv.imshow('pyramid_up_'+str(i), lpls)


img = cv.imread('img/lena.png')
cv.namedWindow('img',cv.WINDOW_AUTOSIZE)
cv.imshow('img',img)

pyramid_up_demo(img)

cv.waitKey(0)
cv.destroyAllWindows()

具体效果:
高斯金字塔:

上采样效果:

拉普拉斯金字塔:

[原创&交流]OpenCV学习笔记(一)

07-22

出处:http://blog.csdn.net/clever101/archive/2008/07/22/2693101.aspxrnrn 因为要对一个矩阵求特征向量矩阵和特征值,本想使用MTL库,因为MTL从成熟程度和运算效率都有保证,没想到使用MTL库求特征向量矩阵和特征值还要依赖其它库,只好另辟蹊径了。幸好我找到了OpenCV,一个由intel资助的开源库,全称叫Open Source Computer Vision Library(大意就是开源计算机视觉库吧)。rnrn要下载OpenCV库,可以从OpenCV中文站上下载,网址是:http://www.opencv.org.cn/index.php/Download。下载下来,我发现OpenCV库做得真的是很人性化——可以通过安装包进行安装。安装好后已经有支持VS 2005的解决方案文件了,你基本不用配置编译选项了。因为我只想里面的矩阵运算,因此这次我只编译了cxcore工程(呵呵,更深的学习还有待以后)。运行VS 2005的生成命令后生成库文件,debug模式下生成:cxcored.lib和cxcore100d.dll,release模式下生成cxcore100.dll和cxcore.lib(可以看到debug文件比release文件多了一个字母d)。rnrn现在正式开始使用OpenCV库,要使用它,当然得把相关头文件拷出来,这里使用它的矩阵运算就是把安装目录下的cv文件夹拷出来,然后在你的程序中include。rnrn使用库的第一个问题就是实现标准类型向库类型的转化(有时我就搞不懂,为何库不使用标准C的类型呢?比如char、int之类的,也许这个是出于效率方面的考虑吧)。OpenCV库求特征值和特征向量的函数是:rnvoid cvEigenVV( CvArr* mat, CvArr* evects, CvArr* evals, double eps=0 );rnmat rn输入对称方阵。在处理过程中将被改变。 rnevects rn特征向量输出矩阵, 连续按行存储 rnevals rn特征值输出矩阵,按降序存储(当然特征值和特征向量的排序是同步的)。 rneps rn对角化的精确度 (典型地, DBL_EPSILON=≈10-15 就足够了)。rnrn测试开始,建一个控制台程序,写一个测试函数:rn[code=C/C++]rnvoid TestOpenCV(void)rnrn // a为输入对称方阵rn double a[5][5] = rn rn 10.0, 1.0, 2.0, 3.0, 4.0,rn 1.0, 9.0, -1.0, 2.0, -3.0,rn 2.0, -1.0, 7.0, 3.0, -5.0,rn 3.0, 2.0, 3.0, 12.0, -1.0,rn 4.0, -3.0, -5.0, -1.0, 15.0 rn ;rn// 构造输入方阵rn CvMat SrcMatrix =cvMat(5,5, CV_64FC1,a);rnrn double b[5][5] = rn rn 0.0, 0.0, 0.0, 0.0, 0.0,rn 0.0, 0.0, 0.0, 0.0, 0.0,rn 0.0, 0.0, 0.0, 0.0, 0.0,rn 0.0, 0.0, 0.0, 0.0, 0.0,rn 0.0, 0.0, 0.0, 0.0, 0.0 rn ;rn// 构造输出特征向量矩阵rn CvMat ProVector =cvMat(5, 5, CV_64FC1, b);rn// 构造输出特征值矩阵rn double c[5] = 0.0, 0.0, 0.0, 0.0, 0.0;rnrn CvMat ProValue = cvMat(5,1, CV_64FC1, c); rnrn cvEigenVV(&SrcMatrix,&ProVector,&ProValue,1.0e-6F);rnrn printf("\n");rnrn int i =0;rn int j =0;rn for(i=0;i<5;i++)rn rn for(j=0;j<5;j++)rn printf("%f\t",cvmGet(&ProVector,i,j));rn printf("\n");rn rn for(i=0;i<5;i++)rn rn printf("%f\n",cvmGet(&ProValue,i,0));rn rnrn[/code]rnrn使用cvEigenVV函数要注意以下三点:rn1. 构造对称方阵、特征向量输出矩阵和特征值输出矩阵必须分静态数组和动态开辟内存的数组,如果是静态数组如上面测试程序就可以使用上面的构造矩阵的方式,如果是动态开辟内存的数组,那么在构造矩阵类时就要使用下面的方式:rn [code=C/C++]rn double** pfMatrix =new double*[nInBandNum];rn double** pfVector=new double*[nInBandNum];rnrn double* pProValue =new double[nInBandNum];rnrnrn for(i=0;i

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试