OpenCV 4.11.0
开源计算机视觉库
加载中…
搜索中…
未找到匹配项
图像金字塔

目标

在本章中,

  • 我们将学习图像金字塔。
  • 我们将使用图像金字塔创建一个新的水果,“橙苹果”。
  • 我们将看到这些函数: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 个级别。

图像

现在您可以使用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 个级别。将其与原始图像进行比较。

图像

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

图像

使用金字塔进行图像融合

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

图像

请查看附加资源中的第一个参考,它包含关于图像融合、拉普拉斯金字塔等的完整图表细节。简而言之,它是这样完成的:

  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. 图像融合