OpenCV 4.12.0
开源计算机视觉
加载中...
搜索中...
无匹配项
图像金字塔

目标

在本章中,

  • 我们将学习图像金字塔
  • 我们将使用图像金字塔来创造一种新的水果,“Orapple”(橙苹)。
  • 我们将学习以下函数:cv.pyrUp(), cv.pyrDown()

理论

通常,我们习惯于处理恒定大小的图像。但在某些情况下,我们需要处理不同分辨率的(相同)图像。例如,在图像中搜索人脸之类的物体时,我们不确定该物体在图像中会呈现什么尺寸。在这种情况下,我们需要创建一组具有不同分辨率的相同图像,并在所有这些图像中搜索物体。这组具有不同分辨率的图像被称为图像金字塔(因为当它们堆叠在一起时,最高分辨率的图像在底部,最低分辨率的图像在顶部,看起来像一个金字塔)。

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

高斯金字塔中的较高层(低分辨率)是通过移除较低层(较高分辨率)图像中连续的行和列形成的。然后,较高层中的每个像素由底层中的 5 个像素以高斯权重贡献形成。通过这样做,一个 \(M \times N\) 的图像变成 \(M/2 \times N/2\) 的图像。因此,面积减少到原始面积的四分之一。这被称为一个八度。当我们向上移动金字塔时(即,分辨率降低),相同的模式会继续。类似地,在扩展时,面积在每一层变为 4 倍。我们可以使用 cv.pyrDown()cv.pyrUp() 函数来找到高斯金字塔。

img = cv.imread('messi5.jpg')
assert img is not None, "无法读取文件,请检查 os.path.exists()"
lower_reso = cv.pyrDown(higher_reso)
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
从文件加载图像。
void pyrDown(InputArray src, OutputArray dst, const Size &dstsize=Size(), int borderType=BORDER_DEFAULT)
模糊图像并对其进行下采样。

以下是图像金字塔中的 4 个层级。

image

现在你可以使用 cv.pyrUp() 函数向上移动图像金字塔。

higher_reso2 = cv.pyrUp(lower_reso)
void pyrUp(InputArray src, OutputArray dst, const Size &dstsize=Size(), int borderType=BORDER_DEFAULT)
对图像进行上采样然后进行模糊。

请记住,higher_reso2 不等于 higher_reso,因为一旦你降低分辨率,你就会丢失信息。下图是从前一种情况下的最小图像创建的金字塔向下 3 个层级。将其与原始图像进行比较

image

拉普拉斯金字塔由高斯金字塔形成。没有专门的函数来实现它。拉普拉斯金字塔图像就像边缘图像一样。它的大部分元素都是零。它们用于图像压缩。拉普拉斯金字塔中的一个层级是通过高斯金字塔中该层级与其高斯金字塔中较高层级的扩展版本之间的差异形成的。拉普拉斯层级的三个层级将如下所示(对比度已调整以增强内容)

image

使用金字塔进行图像混合

金字塔的一个应用是图像混合。例如,在图像拼接中,你需要将两个图像堆叠在一起,但由于图像之间的不连续性,它可能看起来不太好。在这种情况下,使用金字塔进行图像混合可以让你实现无缝混合,而不会在图像中留下太多数据。一个经典的例子是将两种水果,橙子和苹果混合在一起。现在就看结果,了解我在说什么

image

请查看其他资源中的第一个参考文献,它包含了图像混合、拉普拉斯金字塔等的完整图解细节。简单来说,它的步骤如下:

  1. 加载苹果和橙子的两张图像
  2. 找到苹果和橙子的高斯金字塔(在这个特定的例子中,层级数为 6)
  3. 从高斯金字塔中,找到它们的拉普拉斯金字塔
  4. 现在将拉普拉斯金字塔每个层级的苹果的左半部分和橙子的右半部分连接起来
  5. 最后,从这个连接的图像金字塔中,重建原始图像。

以下是完整的代码。(为了简单起见,每个步骤都是单独完成的,这可能会占用更多的内存。如果需要,您可以对其进行优化)。

import cv2 as cv
import numpy as np,sys
A = cv.imread('apple.jpg')
B = cv.imread('orange.jpg')
assert A is not None, "无法读取文件,请检查 os.path.exists()"
assert B is not None, "无法读取文件,请检查 os.path.exists()"
# 为 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
ls = np.hstack((la[:,0:cols//2], lb[:,cols//2:]))
LS.append(ls)
# 现在重建
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)
void add(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray(), int dtype=-1)
计算两个数组或一个数组和一个标量的逐元素和。
void subtract(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray(), int dtype=-1)
计算两个数组或一个数组和一个标量之间的逐元素差。
CV_EXPORTS_W bool imwrite(const String &filename, InputArray img, const std::vector< int > &params=std::vector< int >())
将图像保存到指定文件。

附加资源

  1. 图像混合