数字图像处理之【高斯金字塔】与【拉普拉斯金字塔】

news2025/4/27 18:04:02

数字图像处理之【高斯金字塔】与【拉普拉斯金字塔】

1.1 什么是高斯金字塔?

高斯金字塔(Gaussian Pyramid)是一种多分辨率图像表示方法,用于图像处理和计算机视觉领域。它通过对原始图像进行一系列的高斯平滑和下采样操作,生成一组分辨率逐渐降低的图像层次结构。

高斯金字塔的构建过程通常包括以下步骤:

  1. 高斯平滑(Gaussian Smoothing):对原始图像应用高斯滤波器,生成一个平滑后的图像。高斯滤波器是一种低通滤波器,用于减少图像中的高频噪声。

  2. 下采样(Downsampling):将平滑后的图像进行下采样,通常是将图像的宽度和高度各减半,得到较低分辨率的图像。

  3. 重复上述步骤:对下采样后的图像重复高斯平滑和下采样过程,直到达到预定的分辨率级别。每一个新生成的图像称为一个金字塔层。

1.2 什么是拉普拉斯金字塔?

拉普拉斯金字塔(Laplacian Pyramid)基于高斯金字塔多分辨率图像表示方法,用于图像处理和计算机视觉领域。它通过从高斯金字塔中提取(高频)细节信息,形成一系列细节图像的层次结构。拉普拉斯金字塔可以用于图像压缩、图像增强和图像融合等任务。

构建拉普拉斯金字塔的过程通常包括以下步骤:

  1. 构建高斯金字塔:首先,通过高斯平滑和下采样操作构建高斯金字塔。

  2. 生成拉普拉斯金字塔层

    • 从高斯金字塔的每一层生成下一层(低分辨率层)后,将下一层上采样(通常是通过插值方法将图像的宽度和高度各加倍)回到当前层的分辨率。
    • 计算当前层与上采样后的图像的差值,得到当前层的拉普拉斯层。
  3. 重复上述步骤:对高斯金字塔的每一层重复生成拉普拉斯层的过程,直到达到最底层。

1.3 示意图

从图中我们可以得到什么信息?拉普拉斯金字塔图像=原图的高频信息。

因此如果我们想回复原图,是不是只要用拉普拉斯金字塔图像加上上采样后的图像就能得到?我想应该是的。但是用文字描述我认为过于繁琐,我们用数学公式来进行清晰的表达。

L n \mathrm{L_n} Ln为拉普拉斯金字塔的第 n \mathrm n n层(自底向上), G n \mathrm{G_n} Gn为高斯金字塔的第 n \mathrm n n层(自底向上), G a u s s B l u r \mathrm{GaussBlur} GaussBlur为高斯平滑函数, p y r D o w n \mathrm{pyrDown} pyrDown为下采样函数, p y r U p \mathrm{pyrUp} pyrUp为上采样函数,则有
G n + 1 = p y r D o w n ( G a u s s B l u r ( G n ) ) L n = G n − p y r U p ( p y r D o w n ( G a u s s B l u r ( G n ) ) ) G n = L n + p y r U p ( p y r D o w n ( G a u s s B l u r ( G n ) ) ) \begin{aligned} &\mathrm {G_{n+1}=pyrDown(GaussBlur(G_n))}\\ &\mathrm {L_n=G_{n}-pyrUp(pyrDown(GaussBlur(G_n)))}\\ &\mathrm {G_n=L_n+pyrUp(pyrDown(GaussBlur(G_n)))} \end{aligned} Gn+1=pyrDown(GaussBlur(Gn))Ln=GnpyrUp(pyrDown(GaussBlur(Gn)))Gn=Ln+pyrUp(pyrDown(GaussBlur(Gn)))

1.4 OpenCV实现

对于以上代码,读者可将图片换成自己喜欢的图片进行测试;函数内部有一部分条件语句是因为如果原图宽或高非偶数,那么下采样后再上采样就会和原图大小不一样。

比如 801 × 801 801\times801 801×801的图像下采样后尺寸是 401 × 401 401\times401 401×401(如果原图尺寸是奇数,那么使用向上取整的除法),再上采样就变成了 802 × 802 802\times802 802×802和原图尺寸不匹配。

为了正确处理这种突发情况,我们需要对图像进行裁剪,刚好裁剪1行和1列。

import cv2
import numpy as np


def correctSize(imgOri, imgDownsample):
    imgUpsample = cv2.pyrUp(imgDownsample)
    if imgOri.shape[0] % 2 == 1:  # 检查原始图像宽度是否为奇数
        imgUpsample = imgUpsample[1:, :, :]  # 如果是,将放大后的图像宽度减少1
        # imgUpsample = imgUpsample[1:, :]  # 如果是,将放大后的图像宽度减少1
    if imgOri.shape[1] % 2 == 1:  # 检查原始图像宽度是否为奇数
        imgUpsample = imgUpsample[:, 1:, :]  # 如果是,将放大后的图像宽度减少1
        # imgUpsample = imgUpsample[1:, :]  # 如果是,将放大后的图像宽度减少1
    return imgOri, imgUpsample


img = cv2.imread('lena.png')
# img = cv2.imread('lena.png',cv2.IMREAD_GRAYSCALE) # 读取灰度图像

print(img.shape)
img12 = cv2.pyrDown(img)
img14 = cv2.pyrDown(img12)
img18 = cv2.pyrDown(img14)
img116 = cv2.pyrDown(img18)
ori, imgUpsample = correctSize(img, img12)
# imgRes = cv2.subtract(img, img12)
# 暂不清楚用subtract有什么隐藏机制,用该函数做减法会损失不少信息,所以就直接用原始减法好了
imgRes = ori - imgUpsample
imgRes = imgRes.clip(0, 255)
# imgRes = cv2.subtract(*correctSize(img12, img14))
# imgRes = cv2.subtract(*correctSize(img14, img18))
# imgRes = cv2.subtract(*correctSize(img18, img116))

cv2.imshow("高斯金字塔第n层图像的高频信息图-拉普拉斯", imgRes)
# cv2.imshow("高斯金字塔第n层图像的高频信息图-拉普拉斯", np.hstack((imgUpsample, imgRes)))

cv2.waitKey()
cv2.destroyAllWindows()

1.5 手搓代码(高斯平滑+下采样+上采样+拉普拉斯图像生成)

首先给出高斯平滑所用的高斯核
1 256 [ 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 ] \frac{1}{256}\begin{bmatrix}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{bmatrix} 2561 1464141624164624362464162416414641

然后就是手搓代码了,纯手搓,但是运行速度有点慢,如果你图像很大的话

import cv2
import numpy as np

img = cv2.imread('lena.png')

# 定义高斯核,并归一化,用来做卷积
GaussSmoothKernel = np.array([[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]], dtype=np.float32)
GaussSmoothKernel = GaussSmoothKernel / 256

Smoothedimg = np.zeros(img.shape, dtype=np.uint8)

# 下面的单数是封装好的,可用来做图像卷积
# Smoothedimg = cv2.filter2D(img, -1, GaussSmoothKernel)
for i in range(img.shape[0]):
    for j in range(img.shape[1]):
        for k in range(3):
            if i-2 < 0 or i+2 >= img.shape[0] or j-2 < 0 or j+2 >= img.shape[1]:
                # 处理图像边边角角的模糊,在else语句中的操作没法对边角进行平滑,因为边界像素没有相邻像素,无法进行卷积操作
                for m in range(5):
                    for n in range(5):
                        if i+m-2 >= 0 and i+m-2 < img.shape[0] and j+n-2 >= 0 and j+n-2 < img.shape[1]:
                            Smoothedimg[i, j, k] += GaussSmoothKernel[m, n] * img[i+m-2, j+n-2, k]
            else:
                # 图像的边界处的像素无法通过此操作进行卷积操作
                Smoothedimg[i, j, k] = np.sum(GaussSmoothKernel * img[max(i-2, 0):min(i+3, img.shape[0]), max(j-2, 0):min(j+3, img.shape[1]), k], axis=(0, 1))

downsample = Smoothedimg[::2, ::2, :]

# 生成一个0值张量,用来存储上采样后的图像
Upsample = np.zeros((downsample.shape[0]*2, downsample.shape[1]*2, 3), dtype=np.uint8)

for i in range(downsample.shape[0]):
    for j in range(downsample.shape[1]):
        Upsample[i*2, j*2, :] = downsample[i, j, :]
        # 下面为插值操作
        Upsample[i*2+1, j*2, :] = downsample[i, j, :]
        Upsample[i*2, j*2+1, :] = downsample[i, j, :]
        Upsample[i*2+1, j*2+1, :] = downsample[i, j, :]

# 用于处理图像各方向为奇数的情况:这种情况上采样后图像会比原图大一点点,因此需要给它做个裁剪
if img.shape[0] % 2 == 1:
    Upsample = Upsample[1:, :, :]
if img.shape[1] % 2 == 1:
    Upsample = Upsample[:, 1:, :]

# 获取拉普拉斯图像
Laplacian = img - Upsample

# cv2.imshow('img', img)
# cv2.imshow('Smoothedimg', Smoothedimg)
# cv2.imshow('downsample', downsample)
# cv2.imshow('Upsample', Upsample)
# cv2.imshow('Laplacian', Laplacian)
cv2.imshow('ori+smoothedimg+downsample+Upsample+Laplacian', np.vstack((np.hstack((img, Smoothedimg)), np.hstack((Upsample, Laplacian)))))

# 该语句配合上面插值语句使用,把上面的插值语句注释,然后用下面这条语句输出,你将看到下采样丢失了多少信息
# cv2.imshow('DownsampleLossInfo', (Smoothedimg - Upsample).clip(0, 255))

cv2.waitKey(0)
cv2.destroyAllWindows()

看看效果呗,感觉手搓的效果还不赖,当然肯定没法和封装好的比,封装好的函数使用了更为复杂的插值算法

在这里插入图片描述

上图从左到右边,从上到下依次为 原图 → 高斯平滑后的图 → 经过下采样后,再进行上采样恢复了分辨率的图 → 拉普拉斯图 原图\to高斯平滑后的图\to经过下采样后,再进行上采样恢复了分辨率的图\to拉普拉斯图 原图高斯平滑后的图经过下采样后,再进行上采样恢复了分辨率的图拉普拉斯图

1.6 一些后话

其实看了上面的东西我觉得读者也应该理解为什么高斯核被称为低通滤波器了:低频信息总是通过(保留),高频信息却被删除了。这使得图像看起来更加平滑,减少了噪声和细节。

相反,OpenCVSobel算子就是一个具有高通滤波器性质的算子,该算子用来做图像锐化,即边缘增强,这意味着它会增强图像的边缘和细节,而使平滑区域变暗或去除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1880526.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【例子】webpack 开发一个可以加载 markdown 文件的加载器 loader 案例

Loader 作为 Webpack 的核心机制&#xff0c;内部的工作原理却非常简单。接下来我们一起来开发一个自己的 Loader&#xff0c;通过这个开发过程再来深入了解 Loader 的工作原理。 这里我的需求是开发一个可以加载 markdown 文件的加载器&#xff0c;以便可以在代码中直接导入 m…

Pikachu靶场--Sql Inject

参考借鉴 pikachu靶场练习&#xff08;详细&#xff0c;完整&#xff0c;适合新手阅读&#xff09;-CSDN博客 数字型注入(post) 这种类型的SQL注入利用在用户输入处插入数值&#xff0c;而不是字符串。攻击者试图通过输入数字来修改SQL查询的逻辑&#xff0c;以执行恶意操作。…

前端笔记-day11

文章目录 01-空间-平移02-视距03-空间旋转Z轴04-空间旋转X轴05-空间旋转Y轴06-立体呈现07-案例-3D导航08-空间缩放10-动画实现步骤11-animation复合属性12-animation拆分写法13-案例-走马灯14-案例-精灵动画15-多组动画16-全民出游全民出游.htmlindex.css 01-空间-平移 <!D…

普元EOS学习笔记-创建精简应用

前言 本文依旧基于EOS8.3进行描述。 在上一篇文章《EOS8.3精简版安装》中&#xff0c;我们了解到普元预编译好的EOS的精简版压缩包&#xff0c;安装后&#xff0c;只能进行低开&#xff0c;而无法高开。 EOS精简版的高开方式是使用EOS开发工具提供的IDE&#xff0c;创建一个…

nginx架构学习

前言 这篇文章主要记录下对nginx架构的学习记录。 架构设计 优秀的模块化设计 高度模块化的设计是Nginx的架构基础。在Nginx中&#xff0c;除了少量的核心代码&#xff0c;其他一切皆 为模块。 在这5种模块中&#xff0c;配置模块与核心模块都是与Nginx框架密切相关的&…

【ESP32】打造全网最强esp-idf基础教程——14.VFS与SPIFFS文件系统

VFS与SPIFFS文件系统 这几天忙着搬砖&#xff0c;差点没时间更新博客了&#xff0c;所谓一日未脱贫&#xff0c;打工不能停&#xff0c;搬砖不狠&#xff0c;明天地位不稳呀。 不多说了&#xff0c;且看以下内容吧~ 一、VFS虚拟文件系统 先来看下文件系统的定义&#x…

zabbix监控进阶:如何分时段设置不同告警阈值(多阈值告警)

作者 乐维社区&#xff08;forum.lwops.cn&#xff09;乐乐 在生产环境中&#xff0c;企业的业务系统状态并不是一成不变的。在业务高峰时段&#xff0c;如节假日、促销活动或特定时间段&#xff0c;系统负载和用户访问量会大幅增加&#xff0c;此时可能需要设置更高的告警阈值…

网络基础:静态路由

静态路由是一种由网络管理员手动配置的路由方式&#xff0c;用于在网络设备&#xff08;如路由器或交换机&#xff09;之间传递数据包。与动态路由不同&#xff0c;静态路由不会根据网络状态的变化自动调整。 不同厂商的网络设备在静态路由的配置上有些许差异&#xff1b;下面…

qt文件如何打包成一个独立的exe文件

QT官方给我们安装好了打包软件&#xff0c;就在你QT安装的位置 把这个在cmd打开C:\Qt\6.7.1\mingw_64\bin\windeployqt6.exe&#xff08;或复制地址&#xff09; 然后把要打包项目的exe复制到新的空文件夹&#xff0c;再复制他的地址 按回车后生成新文件 再下载打包软件&#…

Arduino IDE 的安装与esp32项目的创建

1打开官网下载 官网 1-1下载完成后安装即可&#xff0c;会弹出一些按安装提示点击安装 2切换为中文模式 2-1点击Flie&#xff0c;在点击图中高亮的位置&#xff0c;进入 2-2选择语言 3创建esp32项目 3-1在线安装&#xff08;不一定成功&#xff0c;可以一直试&#xff09; …

树莓派3B读写EEPROM芯片AT24C256

AT24C256是一个Atmel公司的EEPROM存储芯片&#xff0c;容量是256K个bit&#xff08;也就是32K字节&#xff09;&#xff0c;I2C接口&#xff0c;而树莓派正好有I2C接口&#xff0c;如下图蓝框中的4个IO口&#xff0c; 把AT24C256和这4个口接在一起&#xff0c;这样硬件就准备好…

观察者模式在金融业务中的应用及其框架实现

引言 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;使得多个观察者对象同时监听某一个主题对象。当这个主题对象发生变化时&#xff0c;会通知所有观察者对象&#xff0c;使它们能够自动更新。…

【NodeJs】入门

目录 一、前导 二、 url模块 三、path模块 四、buffer模块 五、fs模块 六、stream流模块 七、os模块 八、crypto模块 九、util模块 十、http模块 nodejs官网 Node.js — 在任何地方运行 JavaScript nmp是Node.js包管理器&#xff0c;用来安装各种库、框架和工具&…

js自定义内容生成二维码,qrcodejs的使用

qrcodejs qrcodejs是基于原生js的文本转换成二维码的库&#xff0c;轻量且使用方法简单&#xff0c;它的实现原理是通过canvas将重新编码的内容绘制在页面元素上&#xff0c; 使用qrcodejs时可以选择引入它的cdn或者使用npm下载 <script type"text/javascript" …

Nacos配置中心客户端源码分析(一): 客户端如何初始化配置

本文收录于专栏 Nacos 推荐阅读&#xff1a;Nacos 架构 & 原理 文章目录 前言一、NacosConfigBeanDefinitionRegistrar二、NacosPropertySourcePostProcessor三、AbstractNacosPropertySourceBuilder总结「AI生成」 前言 专栏前几篇文章主要讲了Nacos作为服务注册中心相关…

vmware安装debian11

安装vmware16 下载镜像 https://repo.huaweicloud.com/debian-cd/ https://repo.huaweicloud.com/debian-cd/11.7.0/amd64/iso-dvd/ 安装 安装完成之后重启&#xff0c;输入账号密码进入&#xff0c;安装ssh服务器即可使用

park unpark

目录 一、基本使用 二、特点&#xff08;与 wait/notify 对比&#xff09; 三、park & unpark 的原理 一、基本使用 1. park 和 unpark 是 LockSupport 中的方法 2. LockSupport.park();// 暂停线程&#xff0c;线程进入 WAIT 状态 3. LockSupport.unpark(被暂停的线…

数据产品经理知识库构建

概述 数据产品经理是企业中负责管理和推动数据产品的专业人员。他们利用数据来辅助决策&#xff0c;优化产品&#xff0c;提升用户体验。用STAR法则&#xff08;Situation, Task, Action, Result&#xff09;来介绍数据产品经理的角色&#xff0c;应该学习的数据产品&#…

Linux安装Node-RED并实现后台运行及开机启动

首先确保系统中已近成功安装Node.js&#xff0c;并保证需要的合适版本&#xff1a; 关于node.js的安装可以参考我的另一篇博文:《AliyunOS安装Node.js》。 然后就可以使用npm工具安装Node-RED了&#xff0c;很简单使用如下命令&#xff1a; sudo npm install -g --unsafe-per…

各维度卷积神经网络内容收录

各维度卷积神经网络内容收录 卷积神经网络&#xff08;CNN&#xff09;&#xff0c;通常是指用于图像分类的2D CNN。但是&#xff0c;现实世界中还使用了其他两种类型的卷积神经网络&#xff0c;即1D CNN和3D CNN。 在1D CNN中&#xff0c;内核沿1个方向移动。1D CNN的输入和…