这是对于 OpenCV 官方文档中 图像处理 的学习笔记。学习笔记中会记录官方给出的例子,也会给出自己根据官方的例子完成的更改代码,同样彩蛋的实现也会结合多个知识点一起实现一些小功能,来帮助我们对学会的知识点进行结合应用。
如果有喜欢我笔记的请麻烦帮我关注、点赞、评论。谢谢诸位。
学习笔记:
学习笔记目录里面会收录我关于OpenCV系列学习笔记博文,大家如果有什么不懂的可以通过阅读我的学习笔记进行学习。
【OpenCV学习笔记】- 学习笔记目录
通常,我们通常使用固定大小的图像。但在某些情况下,我们需要以不同的分辨率处理(相同的)图像。例如,在搜索图像中的某些内容(例如面部)时,我们不确定该对象将在所述图像中出现的大小。在这种情况下,我们需要创建一组具有不同分辨率的相同图像,并在所有图像中搜索对象。这些具有不同分辨率的图像集称为图像金字塔(因为当它们保存在堆栈中时,最高分辨率的图像在底部,最低分辨率的图像在顶部,它看起来像金字塔)。
有两种图像金字塔。
高斯金字塔中的较高级别(低分辨率)是通过删除较低级别(较高分辨率)图像中的连续行和列而形成的。然后,较高层的每个像素由具有高斯权重的底层 5 个像素的贡献组成。通过这样做,一个M× N图像变成(M/ 2)×(N/2)图像。这样面积就减少到原来面积的四分之一。它被称为八度。当我们进入金字塔的上部时,相同的模式继续存在(即分辨率降低)。同样,扩张时,每层面积变为4倍。我们可以使用 cv2.pyrDown() 和 cv2.pyrUp() 函数找到高斯金字塔。
示例代码:
# 图像金字塔
import cv2
higher_reso = cv2.imread('../image/3.8.1.png')
cv2.imshow('higher_reso', higher_reso)
cv2.waitKey(0)
lower_reso = cv2.pyrDown(higher_reso)
cv2.imshow('lower_reso', lower_reso)
cv2.waitKey(0)
lower_reso2 = cv2.pyrDown(lower_reso)
cv2.imshow('lower_reso2', lower_reso2)
cv2.waitKey(0)
lower_reso3 = cv2.pyrDown(lower_reso2)
cv2.imshow('lower_reso3', lower_reso3)
cv2.waitKey(0)
cv2.destroyAllWindows()
以下是图像金字塔中的 4 个级别。
效果图:
现在你可以用 cv2.pyrUp() 函数处理图像金字塔。
请记住,higher_reso2不等于higher_reso,因为一旦降低了分辨率,就会丢失信息。下图为将上述金字塔中最高级别图像下降三个级别后的结果(即重构三次)。将其与原始图像进行比较,可以发现分辨率相同,但丢失了许多信息:
示例代码:
# 图像金字塔
import cv2
higher_reso = cv2.imread('../image/3.8.1.png')
cv2.imshow('higher_reso', higher_reso)
cv2.waitKey(0)
lower_reso = cv2.pyrDown(higher_reso)
lower_reso2 = cv2.pyrDown(lower_reso)
lower_reso3 = cv2.pyrDown(lower_reso2)
higher_reso2 = cv2.pyrUp(lower_reso3)
higher_reso3 = cv2.pyrUp(higher_reso2)
higher_reso4 = cv2.pyrUp(higher_reso3)
cv2.imshow('higher_reso4', higher_reso4)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果图:
拉普拉斯金字塔由高斯金字塔转化而来,没有专属功能。拉普拉斯金字塔图像与边缘图像相似,它的大部分元素都是零,可以用于图像压缩。拉普拉斯金字塔中的某一层次即高斯金字塔中的该层次与高斯金字塔中的上层的扩展版本之间的差异。拉普拉斯级别的三个级别如下所示(效果为调整对比度以凸显内容):
效果图:
调整对比度进行边缘检测:
示例代码:
# 图像金字塔
import cv2
higher_reso = cv2.imread('../image/3.8.1.png')
lower_reso = cv2.pyrDown(higher_reso)
lower_reso2 = cv2.pyrDown(lower_reso)
lower_reso3 = cv2.pyrDown(lower_reso2)
higher_reso2 = cv2.pyrUp(lower_reso3)
edges2 = cv2.convertScaleAbs(higher_reso2, alpha=2.0, beta=0)
edges2 = cv2.Canny(edges2, 0, 40)
cv2.imshow('higher_reso2', edges2)
cv2.waitKey(0)
higher_reso3 = cv2.pyrUp(higher_reso2)
edges3 = cv2.convertScaleAbs(higher_reso3, alpha=2.0, beta=0)
edges3 = cv2.Canny(edges3, 0, 40)
cv2.imshow('higher_reso3', edges3)
cv2.waitKey(0)
higher_reso4 = cv2.pyrUp(higher_reso3)
edges4 = cv2.convertScaleAbs(higher_reso4, alpha=2.0, beta=0)
edges4 = cv2.Canny(edges4, 0, 40)
cv2.imshow('higher_reso4', edges4)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果图:
金字塔的一个应用是图像混合。例如,在图像拼接中,您需要将两个图像堆叠在一起,但由于图像之间的不连续性,它可能看起来不太好。在这种情况下,使用金字塔进行图像混合可以实现无缝混合,而不会在图像中留下多余数据。典型的例子是混合橙子和苹果图像。在查看下面的效果图就明白什么含义。
请在其他资源中查看第一个参考资料,它有关于图像混合,拉普拉斯金字塔等的完整图表细节。简单的表述如下:
这里的代码把官方的例子简单的优化了一下。在图像数据相互处理的时候,进行了尺寸归一处理。
示例代码:
# 图像金字塔
# 使用金字塔的图像混合
import cv2
import numpy as np
# apple
A = cv2.imread('../image/3.8.2.png')
# orange
B = cv2.imread('../image/3.8.3.png')
# 让A的尺寸和B的尺寸保持一致
height, width = B.shape[:2]
A = cv2.resize(A, (width, height))
# generate Gaussian pyramid for A
G = A.copy()
gpA = [G]
# generate Gaussian pyramid for B
G2 = B.copy()
gpB = [G2]
for i in range(6):
G = cv2.pyrDown(G)
gpA.append(G)
G2 = cv2.pyrDown(G2)
gpB.append(G2)
# generate Laplacian Pyramid for A
lpA = [gpA[5]]
# generate Laplacian Pyramid for B
lpB = [gpB[5]]
for i in range(5, 0, -1):
GE_A = cv2.pyrUp(gpA[i])
# 保证 GE_A 和 gpA[i - 1] 尺寸一致
height_A, width_A = gpA[i - 1].shape[:2]
GE_A = GE_A[:height_A, :width_A]
L_A = cv2.subtract(gpA[i - 1], GE_A)
lpA.append(L_A)
GE_B = cv2.pyrUp(gpB[i])
# 保证 GE_B 和 gpB[i - 1] 尺寸一致
height_B, width_B = gpB[i - 1].shape[:2]
GE_B = GE_B[:height_B, :width_B]
L_B = cv2.subtract(gpB[i - 1], GE_B)
lpB.append(L_B)
# Now add left and right halves of images in each level
LS = []
for la, lb in zip(lpA, lpB):
rows, cols, dpt = la.shape
# 确保 cols / 2 是整数
ls = np.hstack((la[:, 0: int(cols / 2)], lb[:, int(cols / 2):]))
LS.append(ls)
# now reconstruct
ls_ = LS[0]
for i in range(1, 6):
ls_ = cv2.pyrUp(ls_)
# 保证 ls_ 和 LS[i] 尺寸一致
height, width = LS[i].shape[:2]
ls_ = ls_[:height, :width]
# 这里如果 ls_ 和 LS[i] 尺寸不一致,没办法相加
ls_ = cv2.add(ls_, LS[i])
# image with direct connecting each half
real = np.hstack((A[:, :int(cols / 2)], B[:, int(cols / 2):]))
# cv2.imwrite('Pyramid_blending2.jpg', ls_)
# cv2.imwrite('Direct_blending.jpg', real)
cv2.imshow('Apple', A)
cv2.waitKey(0)
cv2.imshow('Orange', B)
cv2.waitKey(0)
cv2.imshow('Direct Connection', real)
cv2.waitKey(0)
cv2.imshow('Pyramid Blending2', ls_)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果图: