Python 图像处理 OpenCV (14):图像金字塔

前文传送门:
「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」
「Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理」
「Python 图像处理 OpenCV (4):图像算数运算以及修改颜色空间」
「Python 图像处理 OpenCV (5):图像的几何变换」
「Python 图像处理 OpenCV (6):图像的阈值处理」
「Python 图像处理 OpenCV (7):图像平滑(滤波)处理」
「Python 图像处理 OpenCV (8):图像腐蚀与图像膨胀」
「Python 图像处理 OpenCV (9):图像处理形态学开运算、闭运算以及梯度运算」
「Python 图像处理 OpenCV (10):图像处理形态学之顶帽运算与黑帽运算」
「Python 图像处理 OpenCV (11):Canny 算子边缘检测技术」
「Python 图像处理 OpenCV (12): Roberts 算子、 Prewitt 算子、 Sobel 算子和 Laplacian 算子边缘检测技术」
「Python 图像处理 OpenCV (13): Scharr 算子和 LOG 算子边缘检测技术」
引言
前面的文章中,我们有用过图像方法或者缩小的函数 resize() ,这个函数既可以放大图像,也可以缩小图像,其中:
- 缩小图像:一版使用
CV_INETR_AREA(区域插值)来插值。 - 放大图像,一般使用
CV_INTER_LINEAR(线性插值)来插值。
图像缩放除了可以使用函数 resize() ,还有另外的一种方式 —— 「图像金字塔」。

图像金字塔是什么?
在说清楚什么事图像金字塔之前,要先介绍另一个概念:「尺度」。
尺度:先从字面意思来看说的就是尺寸和分辨率。
我们在进行图像处理的时候,会经常对源图像的尺寸进行放大或者缩小的变换,进而转换为我们需要的尺寸的目标图像。
对图像进行放大和缩小的变换的这个过程,称为尺度调整。
而图像金字塔则是图像多尺度调整表达的一种重要的方式。
图像金字塔是图像多尺度表达的一种,是一种以多分辨率来解释图像的有效但概念简单的结构。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。

图像金字塔方法的总体思想主要是是:将参加融合的的每幅图像分解为多尺度的金字塔图像序列,将低分辨率的图像在上层,高分辨率的图像在下层,上层图像的大小为前一层图像大小的 1/4 。层数为 0 , 1 , 2 …… N 。将所有图像的金字塔在相应层上以一定的规则融合,就可得到合成金字塔,再将该合成金字塔按照金字塔生成的逆过程进行重构,得到融合金字塔。
实现方式
通常而言,我们一般讨论两种图像金字塔:「高斯金字塔( Gaussian pyramid )」 和 「拉普拉斯金字塔( Laplacian pyramid )」 。
高斯金字塔( Gaussian pyramid )
高斯金字塔是由底部的最大分辨率图像逐次向下采样得到的一系列图像。最下面的图像分辨率最高,越往上图像分辨率越低。

高斯金字塔向下采样:
这个过程实际上就是一个重复高斯平滑并重新对图像采样的过程。
- 对于原始图像先进行一次高斯平滑处理,使用高斯核(
5 * 5)进行一次卷积处理。下面是5 * 5的高斯核。
\left[
\begin{matrix}
1 & 4 & 6 & 4 & 1\\
4 & 16 & 24 & 16 & 4\\
6 & 24 & 36 & 24 & 6\\
4 & 16 & 24 & 16 & 4\\
1 & 4 & 6 & 4 & 1\\
\end{matrix}
\right]
\]
- 接下来是对图像进行采样,这一步会去除图像中的偶数行和奇数列,从而得到一张图像。
- 再然后是重复上面两步,直到得到最终的目标图像为止。
从上面的步骤可以看出,再每次循环中,得到的结果图像只有原图像的 1/4 大小(横纵向均做隔行采样)。
注意:向下采样会逐渐丢失图像信息,属于非线性的处理,此过程不可逆,属于有损处理。
高斯金字塔向上采样:
- 将图像在每个方向扩大为原来的两倍,新增的行和列以 0 填充。
- 使用高斯核(
5 * 5)对得到的图像进行一次高斯平滑处理,获得 「新增像素」的近似值。
注意:此过程与向下采样的过程一样,属于非线性处理,无法逆转,属于有损处理。
此过程得到的图像为放大后的图像,与原图相比会比较模糊,因为在缩放的过程中丢失了一些图像信息,如果想在缩小和放大整个过程中减少信息的丢失。
如果在缩放过程中想要减少图像信息的丢失,这就引出了第二个图像金字塔 —— 「拉普拉斯金字塔」 。
拉普拉斯金字塔( Laplacian pyramid )
拉普拉斯金字塔可以认为是残差金字塔,用来存储下采样后图片与原始图片的差异。
上面我们介绍了基于高斯金字塔,一个原始图像 Gi ,先进行向下采样得到 G(i-1) ,再对 G(i-1) 进行向上采样得到 Up(Down(Gi)) ,最终得到的 Up(Down(Gi)) 与原始的 Gi 是存在差异的。
这是因为向下采样丢失的信息并不能由向上采样来进行恢复,高斯金字塔是一种有损的采样方式。
如果我们想要完全恢复原始图像,那么我们在进行采样的时候就需要保留差异信息。
这就是拉普拉斯金字塔的核心思想,每次向下采样后,将再次向上采样,得到向上采样的 Up(Down(Gi)) 后,记录 Up(Down(Gi)) 与 Gi 的差异信息。

下面这个公式是差异的记录过程:
\]
OpenCV 函数
OpenCV 为向上采样和向下采样提供了两个函数: pyrDown() 和 pyrUp() 。
pyrDown() 的原函数如下:
def pyrDown(src, dst=None, dstsize=None, borderType=None)
- src: 表示输入图像。
- dst: 表示输出图像,它与src类型、大小相同。
- dstsize: 表示降采样之后的目标图像的大小。
- borderType: 表示表示图像边界的处理方式。
注意:dstsize 参数是有默认值的,调用函数的时候不指定第三个参数,那么这个值是按照 Size((src.cols+1)/2, (src.rows+1)/2) 计算的。而且不管如何指定这个参数,一定必须保证满足以下关系式:|dstsize.width * 2 - src.cols| ≤ 2; |dstsize.height * 2 - src.rows| ≤ 2。也就是说降采样的意思其实是把图像的尺寸缩减一半,行和列同时缩减一半。
pyrUp() 的原函数如下:
def pyrUp(src, dst=None, dstsize=None, borderType=None)
- src: 表示输入图像。
- dst: 表示输出图像,它与src类型、大小相同。
- dstsize: 表示降采样之后的目标图像的大小。
- borderType: 表示表示图像边界的处理方式。
参数释义和上面的 pyrDown() 保持一致。
下面是高斯金字塔和拉普拉斯金字塔的代码示例:
import cv2 as cv
#高斯金字塔
def gaussian_pyramid(image):
level = 3 #设置金字塔的层数为3
temp = image.copy() #拷贝图像
gaussian_images = [] #建立一个空列表
for i in range(level):
dst = cv.pyrDown(temp) #先对图像进行高斯平滑,然后再进行降采样(将图像尺寸行和列方向缩减一半)
gaussian_images.append(dst) #在列表末尾添加新的对象
cv.imshow("gaussian"+str(i), dst)
temp = dst.copy()
return gaussian_images
#拉普拉斯金字塔
def laplacian_pyramid(image):
gaussian_images = gaussian_pyramid(image) #做拉普拉斯金字塔必须用到高斯金字塔的结果
level = len(gaussian_images)
for i in range(level-1, -1, -1):
if (i-1) < 0:
expand = cv.pyrUp(gaussian_images[i], dstsize = image.shape[:2])
laplacian = cv.subtract(image, expand)
# 展示差值图像
cv.imshow("laplacian_down_"+str(i), laplacian)
else:
expand = cv.pyrUp(gaussian_images[i], dstsize = gaussian_images[i-1].shape[:2])
laplacian = cv.subtract(gaussian_images[i-1], expand)
# 展示差值图像
cv.imshow("laplacian_down_"+str(i), laplacian)
src = cv.imread('maliao.jpg')
print(src.shape)
# 先将图像转化成正方形,否则会报错
input_image = cv.resize(src, (560, 560))
# 设置为 WINDOW_NORMAL 可以任意缩放
cv.namedWindow('input_image', cv.WINDOW_AUTOSIZE)
cv.imshow('input_image', src)
laplacian_pyramid(src)
cv.waitKey(0)
cv.destroyAllWindows()

上面这段程序有一点需要注意,我当前使用 opencv-python 的版本是 4.3.0.36 ,理论上在向上采样的过程中,目标大小只需要满足关系 |dstsize.width - src.cols * 2| ≤ (dstsize.width mod 2) 即可。
实际上经过测试,输入图像是必须使用正方形,长方形的图像会直接爆出如下错误:
error: (-215:Assertion failed) std::abs(dsize.width - ssize.width*2) == dsize.width % 2 && std::abs(dsize.height - ssize.height*2) == dsize.height % 2 in function 'cv::pyrUp_'
具体原因并没有想通,希望哪位知道的大佬可以解释下。
参考
https://blog.csdn.net/zhu_hongji/article/details/81536820
https://zhuanlan.zhihu.com/p/80362140
Python 图像处理 OpenCV (14):图像金字塔的更多相关文章
- 跟我学Python图像处理丨关于图像金字塔的图像向下取样和向上取样
摘要:本文讲述图像金字塔知识,了解专门用于图像向上采样和向下采样的pyrUp()和pyrDown()函数. 本文分享自华为云社区<[Python图像处理] 二十一.图像金字塔之图像向下取样和向上 ...
- Python 图像处理 OpenCV (15):图像轮廓
前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...
- Python 图像处理 OpenCV (16):图像直方图
前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...
- Python 图像处理 OpenCV (3):图像属性、图像感兴趣 ROI 区域及通道处理
前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 图像属性 图像 ...
- Python 图像处理 OpenCV (4):图像算数运算以及修改颜色空间
前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...
- Python 图像处理 OpenCV (5):图像的几何变换
前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...
- Python 图像处理 OpenCV (6):图像的阈值处理
前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...
- Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像
前文传送门: 「Python 图像处理 OpenCV (1):入门」 普通操作 1. 读取像素 读取像素可以通过行坐标和列坐标来进行访问,灰度图像直接返回灰度值,彩色图像则返回B.G.R三个分量. 需 ...
- Python 图像处理 OpenCV (7):图像平滑(滤波)处理
前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...
随机推荐
- Tensorflow从0到1(2)之基础知识
张量 张量是tensorflow中的基本数据结构 # 全零张量 zero_tsr = tf.zeros([row_dim, col_dim]) # 全1张量 ones_tsr = tf.ones([r ...
- (二)HttpClient Post请求
原文链接:https://blog.csdn.net/justry_deng/article/details/81042379 POST无参: /** * POST---无参测试 * * @date ...
- GitHub 热点速览 Vol.24:程序员自我增值,优雅赚零花钱
摘要:升职加薪,出任 CTO,迎娶白富美/高帅富,走向人生巅峰是很多人的梦想.在本期的热点速览中你将了解自由作者 Easy 如何优雅赚取零花钱的方法,以及定投改变命运 -- 让时间陪你慢慢变富.说到程 ...
- 详说tcp粘包和半包
tcp服务端和客户端建立连接后会长时间维持这个连接,用于互相传递数据,tcp是以流的方式传输数据的,就像一个水管里的水一样,从一头不断的流向另一头. 理想情况下,发送的数据包都是独立的, 现实要复杂一 ...
- 点击提交按钮,屏幕会出现闪烁问题,element.style问题
点击提交按钮,屏幕会出现闪烁问题 通过后台调试发现,在点击的按钮的时候会给body添加一个padding值,而且会出现怎么都修改不了的问题,会发现里面会有 element.style的值,这其实是一种 ...
- redis过期键的策略
一.过期时间设置: 127.0.0.1:6379> expire key seconds //设置键的过期时间为多少秒 127.0.0.1:6379> setex key seconds ...
- 线程间配合:Condition、Semaphore、CountDownLatch、CyclicBarrier
1 重入锁的好搭档:Condition条件 如果大家理解了Object.wait()和Object.notify()方法的话,那么就能很容易理解Condition接口了.它和wait()和notify ...
- Java并发编程:Callable、Future和FutureTask 实现龟兔赛跑
1.不清楚的看博客http://www.cnblogs.com/dolphin0520/p/3949310.html 我们使用上面的代码来实现一个龟兔赛跑 package com.weiyuan.te ...
- 网络编程 套接字socket TCP UDP
网络编程与套接字 网络编程 网络编程是什么: 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 编写基于网络的应用程序的过程序称之为网络编程. 网络编程最主要的工 ...
- python的一些基础知识
一.函数介绍 二.模块与包 三.面向对象介绍 四.网络编程基础应用了解 五.基于MySQL对数据库的理解及基础操作 六.粗浅学习的前端知识整理