Python OpenCV高斯金字塔与拉普拉斯金字塔的实现
作者:梦想橡皮擦 发布时间:2021-09-02 12:31:06
基础知识铺垫
学习图像金字塔,发现网上的资料比较多,检索起来比较轻松。
图像金字塔是一张图像多尺度的表达,或者可以理解成一张图像不同分辨率展示。
金字塔越底层的图片,像素越高,越向上,像素逐步降低,分辨率逐步降低。
高斯金字塔
我们依旧不对概念做过多解释,第一遍学习应用,应用,毕竟 365 天的周期,时间长,后面补充理论知识。
高斯金字塔用于向下采样,同时它也是最基本的图像塔。
在互联网检索原理,得到最简单的说明如下:
将图像的最底层(高斯金字塔的第 0 层),例如高斯核(5x5)对其进行卷积操作,这里的卷积主要处理掉的是偶数行与列,然后得到金字塔上一层图像(即高斯金字塔第 1 层),在针对该图像重复卷积操作,得到第 2 层,反复执行下去,即可得到高斯金字塔。
每次操作之后,都会将 M×N 图像变成 M/2 × N/2 图像,即减少一半。
还有实测中发现,需要用图像的宽和高一致的图片,并且宽高要是 2 的次幂数,例如,8 像素,16 像素,32 像素等等,一会你也可以实际测试一下。
图像金字塔应用到的函数有
和
。
cv2.pyrDown 与 cv2.pyrUp 函数原型
通过 help 函数得到函数原型如下:
pyrDown(src[, dst[, dstsize[, borderType]]]) -> dst
pyrUp(src[, dst[, dstsize[, borderType]]]) -> dst
两个函数原型参数一致,参数说明如下:
:输入图像;
: 输出图像;
: 输出图像尺寸,默认值按照
((src.cols+1)/2, (src.rows+1)/2)
计算。
关于两个函数的补充说明:
从一个相对高分辨率的大尺寸的图像上构建一个金字塔,运行之后的结果是,图像变小,分辨率降低(下采样);
是一个上采样的过程,尽管相对尺寸变大,但是分辨率不会增加,图像会变得更模糊。
测试代码如下:
import cv2 as cv
src = cv.imread("./testimg.jpeg")
print(src.shape[:2])
cv.imshow("src", src)
# 向下采样
dst = cv.pyrDown(src)
print(dst.shape[:2])
cv.imshow("dst", dst)
# 再次向下采样
dst1 = cv.pyrDown(dst)
print(dst1.shape[:2])
cv.imshow("dst1", dst1)
cv.waitKey()
运行代码之后,得到三张图片,大小依次减小,分辨率降低。
通过上面运行得到的最小图,在执行向上采样之后,图片会变的模糊,这也说明上采样和下采样是非线性处理,它们是不可逆的有损处理,因此下采样后的图像是无法还原的,即使放大图片也会变模糊(后面学习到拉普拉斯金字塔可以解决该问题)。
# 向上采样
dst2 = cv.pyrUp(dst1)
print(dst2.shape[:2])
cv.imshow("dst2", dst2)
在总结一下上采样和下采样的步骤:
上采样:使用函数, 先将图像在每个方向放大为原来的两倍,新增的行和列用 0 填充,再使用先前同样的内核与放大后的图像卷积,获得新增像素的近似值;
下采样:使用函数,先对图像进行高斯内核卷积 ,再将所有偶数行和列去除。
拉普拉斯金字塔(Laplacian Pyramid, LP)
拉普拉斯金字塔主要用于重建图像,由上文我们已经知道在使用高斯金字塔的的时候,上采样和下采样会导致图像细节丢失。
拉普拉斯就是为了在放大图像的时候,可以预测残差,何为残差,即小图像放大的时候,需要插入一些像素值,在上文直接插入的是 0,拉普拉斯金字塔算法可以根据周围像素进行预测,从而实现对图像最大程度的还原。
学习到原理如下:用高斯金字塔的每一层图像,减去其上一层图像上采样并高斯卷积之后的预测图像,得到一系列的差值图像即为 LP 分解图像(其中 LP 即为拉普拉斯金字塔图像)。
关于拉普拉斯还存在一个公式(这是本系列课程第一次书写公式),其中 L 为拉普拉斯金字塔图像,G 为高斯金字塔图像
使用下面的代码进行测试。
import cv2 as cv
src = cv.imread("./testimg.jpeg")
print(src.shape[:2])
cv.imshow("src", src)
# 向下采样一次
dst = cv.pyrDown(src)
print(dst.shape[:2])
cv.imshow("dst", dst)
# 向上采样一次
dst1 = cv.pyrUp(dst)
print(dst1.shape[:2])
cv.imshow("dst1", dst1)
# 计算拉普拉斯金字塔图像
# 原图 - 向上采样一次的图
laplace = cv.subtract(src, dst1)
cv.imshow("laplace", laplace)
cv.waitKey()
运行结果如下,相关的图像已经呈现出来,重点注意最右侧的图片。
这个地方需要注意下,如果你使用函数,得到的是上图效果,但是在使用还原的时候会发现问题,建议直接使用完成,匹配公式,修改代码如下:
# cv.subtract(src, dst1)
laplace = src - dst1
代码运行效果如下。
学习过程中发现这样一段话:图像尺寸最好是 2 的整次幂,如 256,512 等,否则在金字塔向上的过程中图像的尺寸会不等,这会导致在拉普拉斯金字塔处理时由于不同尺寸矩阵相减而出错。
这个我在实测的时候发现确实如此,例如案例中使用的图像,在向下采样 2 次的时候,图像的尺寸就会发生变化,测试代码如下:
import cv2 as cv
src = cv.imread("./testimg.jpeg")
print(src.shape[:2])
cv.imshow("src", src)
# 向下采样1次
dst1 = cv.pyrDown(src)
print(dst1.shape[:2])
cv.imshow("dst", dst1)
# 向下采样2次
dst2 = cv.pyrDown(dst1)
print(dst1.shape[:2])
cv.imshow("dst2", dst2)
# 向上采样1次
up_dst1 = cv.pyrUp(dst2)
print(up_dst1.shape[:2])
cv.imshow("up_dst1", up_dst1)
# 计算拉普拉斯金字塔图像
# 采样1次 - 向上采样1次的图
laplace = dst1 - up_dst1
cv.imshow("laplace", laplace)
cv.waitKey()
注意 print(up_dst1.shape[:2])
部分的输出如下:
(710, 400)
(355, 200)
(355, 200)
(356, 200)
如果在该基础上使用拉普拉斯图像金字塔,就会出现如下错误
Sizes of input arguments do not match
在总结一下拉普拉斯图像金字塔的执行过程:
向下采样:用高斯金字塔的第 i 层减去 i+1 层做上采样的图像,得到拉普拉斯第 i 层的图像;
向上采样:用高斯金字塔的 i+1 层向上采样加上拉普拉斯的第 i 层,得到第 i 层的原始图像。
向下采样上面的代码已经实现了,但是拉普拉斯向上采样还未实现,完善一下代码如下,为了代码清晰,我们将变量命名进行修改。
import cv2 as cv
src = cv.imread("./testimg_rect.jpeg")
print(src.shape[:2])
cv.imshow("src", src)
# 高斯金字塔第 0 层
gus0 = src # 原图
# 高斯金字塔第 1 层
gus1 = cv.pyrDown(gus0)
# 高斯第 2 层
gus2 = cv.pyrDown(gus1)
# 拉普拉斯金字塔第 0 层
lap0 = gus0 - cv.pyrUp(gus1)
# 拉普拉斯金字塔第 1 层
lap1 = gus1 - cv.pyrUp(gus2)
# 显示拉普拉斯第一层代码
cv.imshow("laplace", lap1)
cv.waitKey()
下面用修改好的代码完成还原图片的操作。
import cv2 as cv
src = cv.imread("./testimg_rect.jpeg")
print(src.shape[:2])
cv.imshow("src", src)
# 高斯金字塔第 0 层
gus0 = src # 原图
# 高斯金字塔第 1 层
gus1 = cv.pyrDown(gus0)
# 高斯第 2 层
gus2 = cv.pyrDown(gus1)
# 拉普拉斯金字塔第 0 层
lap0 = gus0 - cv.pyrUp(gus1)
# 拉普拉斯金字塔第 1 层
lap1 = gus1 - cv.pyrUp(gus2)
rep = lap0 + cv.pyrUp(lap1 + cv.pyrUp(gus2))
gus_rep = cv.pyrUp(cv.pyrUp(gus2))
cv.imshow("rep", rep)
cv.imshow("gus_rep", gus_rep)
cv.waitKey()
以上代码最重要的部分为下面两句:
rep = lap0 + cv.pyrUp(lap1 + cv.pyrUp(gus2))
gus_rep = cv.pyrUp(cv.pyrUp(gus2))
第一行代码中 lap1 + cv.pyrUp(gus2)
即文字公式 【用高斯金字塔的 i+1 层向上采样加上拉普拉斯的第 i 层,得到第 i 层的原始图像】的翻译。
第二行代码是使用直接向上采样,最终得到的是损失细节的图像。
上述代码运行的结果如下,通过拉普拉斯可以完美还原图像。
学习本案例之后,你可以在复盘本文开始部分的代码,将其进行修改。
最后在学习一种技巧,可以直接将两幅图片呈现,代码如下:
import cv2 as cv
import numpy as np
src = cv.imread("./testimg_rect.jpeg")
print(src.shape[:2])
cv.imshow("src", src)
# 向下采样1次
down_dst1 = cv.pyrDown(src)
print(down_dst1.shape[:2])
cv.imshow("dst", down_dst1)
# 向上采样1次
up_dst1 = cv.pyrUp(down_dst1)
print(up_dst1.shape[:2])
cv.imshow("up_dst1", up_dst1)
res = np.hstack((up_dst1, src))
cv.imshow('res', res)
cv.waitKey()
运行之后,通过函数,将两个图像矩阵合并,实现效果如下:
来源:https://dream.blog.csdn.net/article/details/113484961


猜你喜欢
- #@project = facepalm#@file = main#@author = Maoliang Ran#@create_time
- 接口(python 中的协议)的多种不同的实现方式即为多态。多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属
- 把文件解压到一个目录下这是解压后的目录 将my.ini文件考进去 双击打开my.ini找到这两行更改成自己的解压路径保存
- 1. 利用resnet18做迁移学习import torchfrom torchvision import models if __name
- 本文实例借鉴mvc模式,核心数据为model,维护1个矩阵,0表无雷,1表雷,-1表已经检测过。本例使用python的tkinter做gui
- 一、实际场景及解决思路实际场景:比如某个班的数学成绩以字典格式存储为:student_dict = {'xiaoliang'
- 以下的文章主要向大家介绍的是实现MySQL远程访问的实际操作流程,以及在实现MySQL远程访问的过程中哪些的相关事项是十分重要的,以下就是文
- 在Https页面中,如果iframe所引入页面是非https协议的页面,或者src属性不存在都可能导致浏览器弹出安全警告。本人在网上查找相关
- step函数概述step函数用于绘制阶梯图。根据源码可知,step函数是对plot函数的轻量级封装,很多概念和用法与plot函数非常相似。d
- Go的三种安装方式Go有多种安装方式,你可以选择自己喜欢的。这里我们介绍三种最常见的安装方式:1.Go源码安装:这是一种标准的软件安装方式。
- jQuery居然都没有JSON的decode和encode,精确类型判断也没有,囧……自己动手写吧!不过这些东西在网上都已经有很好的版本了,
- #!/bin/perluse strict; use warnings; my %movies; my
- 对于各种语言的,函数与定义方法,大家可以区分下,利用以后的学习应用。由于篇幅有限,完整地址:http://demo.aspxhome.com
- 看看这个指令在ASP程序中的应用,有[delete from 歌手 where 艺名='cs2000'],删除艺名为cs20
- 输入汉字提示拼音,试试下面这个函数,不知是不是你要的那个:查询汉字便宜到词典网<%function getpychar(ch
- windows下python安装pip 简易教程,具体内容如下1.前提你要已经安装了 某个 版本的 python, 下载地址)安装后,需要配
- DRF中的Request在Django REST Framework中内置的Request类扩展了Django中的Request类,实现了很
- 有一编文章是用JavaScript对XML文件操作来实现无限级联动菜单的,我们可结合ASP来完成对数据库值的读取,然后写入XML文件,再用J
- 用Python发送126邮件,供大家参考,具体内容如下今天想做个自动化邮件提醒的功能,最近刚好在学习python,都说python那么强大,
- 1. 创建shell脚本 vim backupdb.sh 创建脚本内容如下: #!/bin/sh db_user="root&qu