参考:
https://blog.csdn.net/xiamentingtao/article/details/78596240
图像的上采样和下采样:
https://blog.csdn.net/majinlei121/article/details/46742339
图像金字塔的意义参考:
https://blog.csdn.net/weixin_38208741/article/details/78081810
OpenCV官方指导文档:
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_pyramids/py_pyramids.html
原理
通常情况下,我们习惯于使用大小不变的图像。但在某些情况下,我们需要处理同一图像的不同分辨率的图像。例如,当我们在图像中搜索某样东西时,比如人脸,我们不确定该物体会以什么尺寸出现在图像中。在这种情况下,我们需要创建一组不同分辨率的图像,并在所有图像中搜索对象。这些不同分辨率的图像被称为图像金字塔(因为当它们被放在一个堆栈中,最大的图像在底部,最小的图像在顶部,看起来像一个金字塔)。
图像金字塔有两种:1)高斯金字塔 和 2)拉普拉斯金字塔。
高斯金字塔
高斯金字塔中的高层次(低分辨率)图像是由低层次(高分辨率)图像中的连续行和列移除而形成的。然后,上层图像中的每一个像素都是由下层图像中的5个像素以高斯权重贡献出来的。这样一来,一个M x N的图像就变成了M/2/ x N/2的图像。所以面积减少到原始面积的四分之一。这就是所谓的 Octave。当我们在金字塔中往上走的时候,同样的模式也在继续(即,分辨率降低)。同样地,在扩大的同时,每一级的面积都会变成4倍。我们可以使用**cv2.pyrDown()和cv2.pyrUp()**函数找到高斯金字塔。
注:**两个API传入的图像宽高必须均是2的整数倍,否则报错。**两个API名字中的Down和Up分别指的是对图像做降采样(Downsampling)的Down和升采样(Upsampling)的Up。换句话说,pyrDown()是降低图像分辨率和尺寸,返回的是原图在金字塔中的上一层的图像,pyrUp()是升高图像分辨率和尺寸,返回的是原图在金字塔中的下一层的图像。
高斯金字塔:层次越低,分辨率越高,size越大;层次越高,分辨率越低,size越小。
拉普拉斯金字塔
拉普拉斯金字塔是由高斯金字塔形成的。没有专门输出拉普拉斯金字塔的API。拉普拉斯金字塔图像看起来像是边缘图像。它的大部分元素都是零。它们被用于图像压缩。拉普拉斯金字塔中的一个级别是由高斯金字塔中的该级别和高斯金字塔中的上一级的扩展版之间的差异形成的。
示例:图像融合
金字塔的一个应用是图像混合。例如,在图像拼接中,你需要将两张图像堆叠在一起,但由于图像之间的不连续性,可能看起来并不好看。在这种情况下,使用Pyramids进行图像混合,可以让你在图像中不丢失太多数据的情况下进行无缝混合。一个经典的例子是橙子和苹果这两种水果的混合。
步骤:
- 加载苹果和橙子的两张图片
- 找出苹果和橙子的高斯金字塔(在本例中,层数为6)。
- 从高斯金字塔中,找到它们的拉普拉斯金字塔。
- 现在将苹果的左半边和橙子的右半边分别加入到拉普拉斯金字塔的各个关卡中。
- 最后从这个联合图像金字塔,重建原始图像。
源码:
import cv2 as cv
import numpy as np
# 图像融合 示例
# 1. 加载苹果和橙子的两张图片
# 2. 找出苹果和橙子的高斯金字塔(在本例中,层数为6)。
# 3. 从高斯金字塔中,找到它们的拉普拉斯金字塔。
# 4. 现在将苹果的左半边和橙子的右半边分别加入到拉普拉斯金字塔的各个关卡中。
# 5. 最后从这个联合图像金字塔,重建原始图像。
A = cv.imread('C:/cv-samples/data/apple.jpg')
B = cv.imread('C:/cv-samples/data/orange.jpg')
# 生成 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 的拉普拉斯金字塔
# 注意,A 和 B 两个拉普拉斯的塔尖均是一个彩色图像
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)
# # 对拉普拉斯金字塔做图像二值化,试看效果
# for i in range(6):
# gray = cv.cvtColor(lpB[i], cv.COLOR_BGR2GRAY)
# ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
# cv.imshow("{}".format(i), binary)
# cv.waitKey(0)
# 逐层将两个拉普拉斯金字塔合并
LS = []
i = 0
for la, lb in zip(lpA, lpB): # zip() 函数将可迭代对象作为参数,将对象中的对应元素打包成一个个元组,然后返回由这些元组组成的列表
rows, cols, dpt = la.shape
ls = np.hstack((la[:, 0:int(cols/2)], lb[:, int(cols/2):]))
LS.append(ls)
# now reconstruct
# 取合并后的拉普拉斯金字塔的塔尖层记为ls_,做升采样,然后把它和拉的第2层(从上往下)相加,
# 相加后的结果记为新的ls_,不断重复(升采样,和同大小的LS层做加法),直到LS的第6层加完,
# 得到最后的 ls_
# ?这一步的意义是什么不懂
ls_ = LS[0]
for i in range(1, 6):
ls_ = cv.pyrUp(ls_)
ls_ = cv.add(ls_, LS[i])
# image with direct connecting each half
real = np.hstack((A[:, :int(cols/2)], B[:, int(cols/2):]))
cv.imshow('Pyramid_blending2.jpg', ls_)
cv.imshow('Direct_blending.jpg', real)
cv.waitKey(0)
视频示例:
import cv2 as cv
import numpy as np
def pyramid_demo(image):
level = 3
temp = image.copy()
pyramid_images = []
for i in range(level):
dst = cv.pyrDown(temp)
pyramid_images.append(dst)
cv.imshow("pyramid_down_"+str(i), dst)
temp = dst.copy()
return pyramid_images
def laplacian_demo(image):
pyramid_images = pyramid_demo(image)
level = len(pyramid_images)
for i in range(level-1, -1, -1):
if (i - 1) < 0:
expand = cv.pyrUp(pyramid_images[i], dstsize=image.shape[:2])
lpls = cv.subtract(image, expand)
cv.imshow("laplacian_demo_"+str(i), lpls)
else:
expand = cv.pyrUp(pyramid_images[i], dstsize=pyramid_images[i - 1].shape[:2])
lpls = cv.subtract(pyramid_images[i - 1], expand)
cv.imshow("laplacian_demo_"+str(i), lpls)
print("-----------Python OpenCV Tutorial--------------")
src = cv.imread("C:/cv-samples/data/lena.jpg")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
laplacian_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()