前言

图像金字塔是由一幅图像的多个不同分辨率的子图所构成的图像集合。该组图像是由单个图像通过不断地降采样所产生的,最小的图像可能仅仅有一个像素点。

由此看出,图像金字塔就是一系列以金字塔形状排列的、自底向上分辨率逐渐降低的图像集合

通常情况下,图像金字塔的底部是待处理的高分辨率图像(原始图像),而顶部则为其低分辨率的近似图像。向金字塔的顶部移动时,图像的尺寸和分辨率都不断地降低。

一般每向上移动一级,图像的宽和高都降低为原来的二分之一

高斯金字塔是通过对一幅图像不断的向下采样所产生的,如果对金字塔中的小图像进行向上采样以获取完整的大尺寸高分辨率图像,这时就需要用到拉普拉斯金字塔。

高斯金字塔

上采样:每次的长和高都降低为原来的两倍,但实际的分辨率是会变低的。

下采样:图片由高分辨率到低分辨率,每次的宽和高都降低为原来的二分之一。

下采样

得到某张高分辨率图片的图像金字塔有两种方式:

方法一:直接通过不断地删除图像的偶数行和偶数列得到。例如,有一幅图像,其大小是N x N,删除其偶数行和偶数列后得到一幅(N/2) x (N/2)大小的图像。经过上述处理后,图像大小变为原来的四分之一,不断地重复该过程,就可以得到该图像的图像金字塔。

方法二:先对原始图像滤波,得到原始图像的近似图像,然后将近似图像的偶数行和偶数列删除以获取向下采样的结果。有多种滤波器可以选择。例如:

● 邻域滤波器:采用邻域平均技术求原始图像的近似图像。该滤波器能够产生平均金字塔。

● 高斯滤波器:采用高斯滤波器对原始图像进行滤波,得到高斯金字塔。这是OpenCV函数cv2.pyrDown()所采用的方式。

如下图所示,高斯金字塔是通过不断使用高斯滤波和向下采样所产生的,最终可以得到图一的结果。

上采样

在向上采样的过程中,通常将图像的宽度和高度都变为原来的2倍。这意味着,向上采样的结果图像的大小是原始图像的4倍。因此,要在结果图像中补充大量的像素点。对新生成的像素点进行赋值,称为插值处理。

例如可以使用最临近插值,用最邻近的像素点给当前还没有值的像素点赋值。

有一种常见的向上采样,对像素点以补零的方式完成插值。通常是在每列像素点的右侧插入值为零的列,在每行像素点的下方插入值为零的行。

接着使用向下采样使用的高斯滤波器对补零后的图像进行滤波处理,以获取向上采样的结果图像。由于此时像素中四分之三像素点的值都是零,所以要将使用的高斯滤波器系数乘以4,以保证得到的像素值范围在其原有像素值范围内

所以,向上采样和向下采样是相反的两种操作,但是,由于向下采样会丢失像素值,所以这两种操作并不是可逆的。也就是说,对一幅图像先向上采样再向下采样,是无法恢复原始状态的,反之也是一样。

函数介绍

openCV提供了函数cv2.pyrDown(),用于实现高斯金字塔操作中的向下采样,而cv2.pyrUp()用于实现向上采样。

1
2
dst = cv2.pyrDown(src[, dstsize[, boderType])
dst = cv2.pyrUp(src[, dstsize[, boderType])

其中:

  • dst为目标图像
  • src为原始图像
  • dstsize为目标图像的大小
  • boderType为边界类型,默认为BORDER_DEFAULT。

默认情况下,输出图像的大小为原来的二分之一。

该函数首先对原始图像进行高斯滤波变换,以获取原始图像的近似图像,通常使用的是5 x 5 的滤波核。在获取到近似图像后,再通过抛弃偶数行和偶数列来实现向下采样。

示例说明

单独下采样:

接下来对于火箭图连续进行三次向下采样,并打印出每次采样结果图像的大小,并显示最终的采样后结果图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import cv2 as cv

image = cv.imread("img/rocket.jpg", cv.COLOR_BGR2GRAY)

result1 = cv.pyrDown(image) # 下采样
result2 = cv.pyrDown(result1)
result3 = cv.pyrDown(result2)

print("image's shape", image.shape)
print("result1's shape", result1.shape)
print("result2's shape", result2.shape)
print("result3's shape", result3.shape)

cv.imshow("origin", image)
cv.imshow("result1", result1)
cv.imshow("result2", result2)
cv.imshow("result3", result3)

cv.waitKey()
cv.destroyAllWindows()

程序运行后得到如下结果:

1
2
3
4
image's shape (450, 720, 3)
result1's shape (225, 360, 3)
result2's shape (113, 180, 3)
result3's shape (57, 90, 3)

发现每次的尺寸大小都减少一半,即行列变为原来的二分之一,图像整体的大小会变为原来的四分之一。

![image-20200825165841759](/Users/sonata/Library/Application Support/typora-user-images/image-20200825165841759.png)

上下采样结合:

按理说对图片先进行下采样再进行上采样,虽然图像的大小不会改变,但是会模糊许多,我们可以试一试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import cv2 as cv

image = cv.imread("img/rocket.jpg", cv.COLOR_BGR2GRAY)

result1 = cv.pyrDown(image) # 下采样
result2 = cv.pyrUp(result1) # 上采样

print("image's shape", image.shape)
print("result1's shape", result1.shape)
print("result2's shape", result2.shape)

cv.imshow("origin", image)
cv.imshow("result1", result1)
cv.imshow("result2", result2)

cv.waitKey()
cv.destroyAllWindows()

程序运行后,得到结果如下图所示。上方的左右两张图分别是原图和上下采样结合后的图片,两者清晰度明显不同,因此有理由认为上下采样的结果是不可逆的,这是很明显的,因为向下采样时在使用高斯滤波器处理后还要抛弃偶数行和偶数列,不可避免地要丢失一些信息。

![image-20200825170021835](/Users/sonata/Library/Application Support/typora-user-images/image-20200825170021835.png)

拉普拉斯金字塔

为了使得小图像进行上采样后的结果能够获取比较完整的大尺寸高分辨率图像,我们需要使用到拉普拉斯金字塔。拉普拉斯金字塔的定义形式为:

Li=GipyrUp(Gi+1)L_i=G_i- pyrUp(G_i+ 1)

式中:

  • Li表示拉普拉斯金字塔中的第i层。
  • Gi表示高斯金字塔中的第i层。

拉普拉斯金字塔中的第i层,等于“高斯金字塔中的第i层”与“高斯金字塔中的第i+1层的向上采样结果”之差。

得到的结果相当于把偶数行和列的结果给计算出来了。

因此首先可以看看拉普拉斯金字塔中的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
image = cv.imread("img/IMG.jpg", cv.COLOR_BGR2GRAY)

# 构建高斯金字塔
result1 = cv.pyrDown(image) # 下采样
result2 = cv.pyrDown(result1)
result3 = cv.pyrDown(result2)

# 拉普拉斯金字塔
L0 = image - cv.pyrUp(result1)
L1 = result1 - cv.pyrUp(result2)
L2 = result2 - cv.pyrUp(result3)

print("image's shape", image.shape)
print("result1's shape", result1.shape)
print("result2's shape", result2.shape)
print("result3's shape", result3.shape)
print("L0's shape", L0.shape)
print("L1's shape", L1.shape)
print("L2's shape", L2.shape)

cv.imshow("origin", image)
cv.imshow("L0", L0)

cv.waitKey()
cv.destroyAllWindows()

运行后的打印结果有:

1
2
3
4
5
6
7
image's shape (1440, 2560, 3)
result1's shape (720, 1280, 3)
result2's shape (360, 640, 3)
result3's shape (180, 320, 3)
L0's shape (1440, 2560, 3)
L1's shape (720, 1280, 3)
L2's shape (360, 640, 3)

前面的公式是如何计算出拉普拉斯金字塔的每一层,那么想要向上采样恢复高分辨率图像,就需要对公式变形,即

Li+pyrUp(Gi+1)=GiL_i + pyrUp(G_i+ 1) = G_i

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 构建高斯金字塔
result1 = cv.pyrDown(image) # 下采样

# 拉普拉斯金字塔
L0 = image - cv.pyrUp(result1)

# 恢复原始图像
origin = L0 + cv.pyrUp(result1)

print("image's shape", image.shape)
print("L0's shape", L0.shape)
print("origin's shape", origin.shape)

cv.imshow("image", image)
cv.imshow("L0", L0)
cv.imshow("origin", origin)

cv.waitKey()
cv.destroyAllWindows()

展示结果如下,可以发现右边上下两幅图是一模一样的,没有清晰度的差异,这时候的上下采样之间,过程是可逆的。