在某些情况下,我们需要处理同一图像的不同分辨率的图像。例如在搜索图像中的某些对象时,比如人脸,我们不确定对象在图像中的大小,在这种情况下,我们需要创建一组不同分辨率的图像,并在所有图像中搜索对象。这些具有不同分辨率的图像集就被称为图像金字塔(因为它们保存在一个堆栈中,底部是最大的图像,顶部是最小的图像,看起来就像一个像金字塔)。

图像金字塔有两种:1.高斯金字塔和 2.拉普拉斯金字塔。

1. 高斯金字塔

高斯金字塔中较高级别的图像(低分辨率)是通过删除低级别(高分辨率)图像中的连续行和列而形成的。

首先,对较低层次图像上的每个像素计算5x5像素的高斯权值,然后删除图像的偶数行和列,这样M×N图像就会被处理成M/2×N/2的图像,面积减少到原来面积的四分之一。

用通用的方法继续计算,就可以得到分辨率不断下降的高斯金字塔。

当面积扩大时,首先在每个方向分别间隔插入一行像素,新插入的像素填充0,然后使用同样的内核进行高斯卷积,就得到了放大后的金字塔图片。

可以使用cv2.pyrdown()和cv2.pyrUp()函数计算高斯金字塔。

img = cv2.imread('test.jpg') # 750*994
img = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_CUBIC) #375*497
l1 = cv2.pyrDown(img)
l2 = cv2.pyrDown(l1)
l3 = cv2.pyrDown(l2)
display = img
for item in [l1,l2,l3]:
    pad = np.pad(item,((0, img.shape[0]-item.shape[0]),(0,0),(0,0)),'constant')
    display = np.concatenate((display, pad) , axis=1)
cv2.imshow('image', display)
cv2.waitKey(0)
cv2.destroyAllWindows()

image.png

img = cv2.imread('test.jpg') # 750*994
img = cv2.resize(img, (45, 90), interpolation=cv2.INTER_CUBIC) #45*90
up1 = cv2.pyrUp(img)
up2 = cv2.pyrUp(up1)
up3 = cv2.pyrUp(up2)
display = np.pad(img,((0,up3.shape[0]-img.shape[0]),(0,0),(0,0)),'constant')
for item in [up1, up2, up3]:
    pad = np.pad(item, ((0, up3.shape[0] - item.shape[0]), (0, 0), (0, 0)), 'constant')
    display = np.concatenate((display, pad), axis=1)
cv2.imshow('image', display)
cv2.waitKey(0)
cv2.destroyAllWindows()

image.png

2. 拉普拉斯金字塔

拉普拉斯金字塔是基于高斯金字塔生成的,它的大部分元素都是零,有点像边缘图像,一般用于图像压缩/图像融合等。

设第i层的拉普拉斯金字塔为Li,高斯金字塔为Gi,则拉普拉斯金字塔的定义如下

image.png

其中-后面的部分指将Gi+1像素映射到长宽扩大一倍的图像像,然后用5x5的内核卷积,实际上就是个pyrUp的过程,所以Li也可以表示为:

image.png

一张图先pyrDown,再pyrUp,不是可逆的操作,而会在pyrDown的过程中丢失一些细节,拉皮拉斯金字塔表示的正是这部分丢失的细节。

3.使用图像金字塔融合图片

图像金字塔的一个应用场景是图像融合。在对两张图片进行融合的时候,由于图像之间的不连续性,边界的地方像素变化会显得十分突兀。使用图像金字塔来做图像融合,则能做到无缝融合的效果。

基本思路:

  1. 为待融合的两张图像分别计算高斯金字塔(如:G0,G1,G2,G3,G4,G5 共6层)
  2. 基于高斯金字塔计算出各层的拉普拉斯金字塔(L5,L4,L3,L2,L1 共5层)
  3. 将两张图最高层级的高斯金字塔(G5)、各个层级的拉普拉斯金字塔(L1~L5)直接融合
  4. 对G5融合结果pyrUp操作,然后从L5中还原第1步中pyrDown的时候G4到G5丢失的部分细节,重复此步骤直到L1~L5的细节全部重建完成

在各级拉普拉斯金字塔融合图像中,由于边界部分分辨率降低,两幅图像的细节都有包含,因此重建之后,边界部分的融合看起来比较自然,而非边界部分还是还原了各自的细节,下面来一下分解动作。

随便在网上下载两张500x500的图,直接拼接:

def showAll(imgs,zoom=1):
    maxHeight = max([img.shape[0] for img in imgs])
    display = np.pad(imgs[0],((0,maxHeight - imgs[0].shape[0]),(0,0),(0,0)),'constant')
    for i in range(1, len(imgs)):
        pad = np.pad(imgs[i], ((0, maxHeight - imgs[i].shape[0]), (0, 0), (0, 0)), 'constant')
        display = np.concatenate((display, pad), axis=1)
    display = cv2.resize(display, None, fx=zoom,fy=zoom)
    cv2.imshow('image', display)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

img1 = cv2.imread("t1.jpg")
img2 = cv2.imread("t2.jpg")
height,width = img1.shape[0:2]
blend1 = img1.copy()
half = int(width/2)
blend1[0:height,half:width] = img2[0:height,half:width]
showAll([img1,img2,blend1],0.5)

image.png

可以看到拼接处十分的突兀。接下来用图像金字塔来融合。

步骤1,对两张图像分别计算6层高斯金字塔。

G = img1.copy()
    gp1 = [G]
    for i in range(6):
        G = cv2.pyrDown(G)
        gp1.append(G)
    G = img2.copy()
    gp2 = [G]
    for i in range(6):
        G = cv2.pyrDown(G)
        gp2.append(G)

image.png

image.png

步骤2,基于高斯金字塔计算出各层的拉普拉斯金字塔

lp1 = [gp1[5]]
    for i in range(5, 0, -1):
        GE = cv2.pyrUp(gp1[i])
        GE = cv2.resize(GE,gp1[i-1].shape[0:2])
        L = cv2.subtract(gp1[i - 1], GE)
        lp1.append(L)
    lp2 = [gp2[5]]
    for i in range(5, 0, -1):
        GE = cv2.pyrUp(gp2[i])
        GE = cv2.resize(GE, gp2[i - 1].shape[0:2])
        L = cv2.subtract(gp2[i - 1], GE)
        lp2.append(L)
    showAll(lp1+lp2,0.5)

(数组中第一个元素为高斯金字塔的最顶层的一张图)
image.png

步骤3, 拉普拉斯金字塔各级融合

LS = []
for la, lb in zip(lp1, lp2):
    rows, cols, dpt = la.shape
    ls = np.hstack((la[:, 0:int(cols / 2)], lb[:, int(cols / 2):]))
    LS.append(ls)
showAll(LS, 0.8)

image.png

步骤4,融合后的结果由低到高逐级还原细节

ls_ = LS[0]
    for i in range(1, 6):
        ls_ = cv2.pyrUp(ls_)
        ls_ = cv2.resize(ls_, LS[i].shape[0:2])
        ls_ = cv2.add(ls_, LS[i])
    showAll([ls_])

image.png

可以看到,与直接融合相比,边缘部分明显要平滑很多。

☞ 参与评论