一般情况下,我们要处理是一副具有固定分辨率的图像。但是有些情况下,我们需要对同一图像的不同分辨率的子图像进行处理。比如,我们要在一幅图像中查找某个目标,比如脸,我们不知道目标在图像中的尺寸大小。这种情况下,我们需要创建一组图像,这些图像是具有不同分辨率的原始图像。我们把这组图像叫做图像金字塔(简单来说就是同一图像的不同分辨率的子图集合)。如果我们把最大的图像放在底部,最小的放在顶部,看起来像一座金字塔,故而得名图像金字塔。

cuda编程 图像高斯金字塔 opencv图像金字塔_图像金字塔

图像金字塔有两种,第一种是高斯金字塔,第二种是拉普拉斯金字塔。高斯金字塔是由底部的最大分辨率图像逐次向下采样得到的一系列图像。最下面的图像分辨率最高,越往上图像分辨率越低。高斯金字塔的向下采样过程是:对于给定的图像先做一次高斯平滑处理,也就是使用一个大小为的卷积核对图像进行卷积操作,然后再对图像采样,去除图像中的偶数行和偶数列,然后就得到一张图片,对这张图片再进行上述操作就可以得到高斯金字塔。

在opencv的官方网站上给出了这个的卷积核,如下所示:

cuda编程 图像高斯金字塔 opencv图像金字塔_卷积核_02

假设原先的图片的长和宽为M、N,经过一次采样后,图像的长和宽会分别变成[M+1]/2,[N+1]/2,由此可见,图像的面积变为了原来的1/4,图像的分辨率变成了原来的1/4。

我们可以使用函数cv2.pyrDown() 和cv2.pyrUp() 构建图像金字塔。函数cv2.pyrDown() 从一个高分辨率大尺寸的图像向上构建一个金子塔(尺寸变小,分辨率降低)。函数cv2.pyrUp() 从一个低分辨率小尺寸的图像向下构建一个金子塔(尺寸变大,但分辨率不会增加)。如果一张图片经过下采样后,那么图片的分辨率就会降低,图片里的信息就会损失,再由这张有损失的图片上采样得到的图片与原图是不相同的,其分辨率与上采样之前的图片是一致的。上采样与下采样的过程相反,上采样先在图像中插入值为0的行与列,再进行高斯模糊,高斯模糊所使用的卷积核等于下采样中使用的卷积核乘以4,也就是上面给出的卷积核乘以4。

第二种金字塔是拉普拉斯金字塔,它可以从高斯金字塔计算得来,计算公式如下:

cuda编程 图像高斯金字塔 opencv图像金字塔_cuda编程 图像高斯金字塔_03

拉普拉金字塔的图像看起来就像边界图,其中很多像素都是0。他们经常被用在图像压缩中。

我们可以通过编程实现高斯金字塔的采样过程,使用python编写程序如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt
img=cv2.imread("lalala.jpg",0)
kernel=(1/256*np.array(
            [[1,4,6,4,1],
            [4,16,24,16,4],
            [6,24,36,24,36],
            [4,16,24,16,4],
            [1,4,6,4,1]]))
x,y=img.shape
tmp=(cv2.filter2D(img,-1,kernel=kernel))
dst=tmp[::2,::2]
lower_reso_01=cv2.pyrDown(img)
plt.subplot(121),plt.imshow(dst,cmap="gray"),plt.title("dst")
plt.subplot(122),plt.imshow(lower_reso_01,cmap="gray"),plt.title("lower_reso_01")
plt.show()

程序实现结果如下:

cuda编程 图像高斯金字塔 opencv图像金字塔_cuda编程 图像高斯金字塔_04

图像金字塔的一个典型应用是图像融合。例如,在图像缝合中,你需要将两幅图叠在一起,但是由于连接区域图像像素的不连续性,整幅图的效果看起来会很差。这时图像金字塔就可以排上用场了,他可以帮你实现无缝连接。

下面是一个实现图像融合的案例:

我们有两张梨子的照片,原图分别如下:

cuda编程 图像高斯金字塔 opencv图像金字塔_高斯金字塔_05

我们期望通过金字塔来实现这两个例子的“和谐”融合,下面给出代码:

import cv2 as cv
import numpy as np,sys
import matplotlib.pyplot as plt
A = cv.imread('pear01.jpg')
B = cv.imread('pear02.jpg')
#将两张图片的大小调整至一致,这里分别取出图片的长和宽,然后再除以64取整再乘以64的原因是:
#我们在后面会构建一个6层的高斯金字塔,并由之计算出拉普拉斯金字塔,由于在向下采样的过程中,
#长度和宽度会逐次减半,当长度和宽度不能被2整除时就容易出现错误,所以我们在开始时就将图片
#的长度和宽度调整至64的倍数
x,y=B.shape[0:2]
x=(x//64)*64
y=(y//64)*64
A=cv.resize(A,(y,x))
B=cv.resize(B,(y,x))
#产生A的高斯金字塔
G = A.copy()
gpA = [G]
for i in range(6):
    #向下采样
    G = cv.pyrDown(G)
    gpA.append(G)
##产生B的高斯金字塔
G = B.copy()
gpB = [G]
for i in range(6):
    G = cv.pyrDown(G)
    gpB.append(G)
#产生A的拉普拉斯金字塔
lpA = [gpA[5]]
for i in range(5,0,-1):
    GE = cv.pyrUp(gpA[i])
    L = cv.subtract(gpA[i-1],GE)
    lpA.append(L)
#产生B的拉普拉斯金字塔
lpB = [gpB[5]]
for i in range(5,0,-1):
    GE = cv.pyrUp(gpB[i])
    L = cv.subtract(gpB[i-1],GE)
    lpB.append(L)
#将第一张图片的左半边和第二张图片的右半边相加
LS = []
for la,lb in zip(lpA,lpB):
    rows,cols,dpt = la.shape
    #numpy数组水平组合
    ls = np.hstack((la[:,0:cols//2], lb[:,cols//2:]))
    LS.append(ls)
# 图像重建
#LS[0]是具有最低分辨率的彩色图像,即高斯金字塔的最高层图像
ls_ = LS[0]
for i in range(1,6):
    #由最低分辨率图像向上采样
    ls_ = cv.pyrUp(ls_)
    #将采样后的图片与同层的拉普拉斯金字塔图像相加
    ls_ = cv.add(ls_, LS[i])

#将两幅图片直接相加
real = np.hstack((A[:,:cols//2],B[:,cols//2:]))
cv.imwrite('Pyramid_blending2.jpg',ls_)
cv.imwrite('Direct_blending.jpg',real)
#将图片的模式转变为RGB模式,因为在opencv中加载图片的方式为BGR,
#而在matplotlib中显示的模式是RGB
ls_=cv.cvtColor(ls_,cv.COLOR_BGR2RGB)
real=cv.cvtColor(real,cv.COLOR_BGR2RGB)
plt.subplot(121),plt.imshow(ls_),plt.title("Pymarid_blending")
plt.subplot(122),plt.imshow(real),plt.title("Direct_blending")
plt.show()

程序运行结果:

cuda编程 图像高斯金字塔 opencv图像金字塔_图像金字塔_06

可以从上图中看到,使用金字塔实现融合的图像过渡很平滑,而直接拼接的图像则有明显的边界,效果很差。

实现上述效果的步骤为:

1、    读入两幅不同颜色梨子的图像;

2、    构建两个梨子的高斯金字塔图像;

3、    根据高斯金字塔构建拉普拉斯金字塔图像;

4、    在拉普拉斯金字塔图像的每一层进行图像融合,图一的左半边加图二的右半边;

5、    根据融合后的图像金字塔图像重建图像。