拉普拉斯金字塔的频谱分析

1. 基本分析

拉普拉斯金字塔分解,主要由以下步骤组成:

  1. 对输入图像 L0 进行低通滤波,其中常采用高斯滤波;
  2. 对低通滤波后的图像进行 1/2 倍率的下采样,这里的下采样通常是指直接取偶行且偶列(以 0 开始计)的像素,获得低频图像 L1;
  3. 对低频图像 L1 进行 2 倍的插零上采样,等价于直接将第 1 步的低通滤波图像的奇行或奇列像素置零;
  4. 对第 3 步上采样所得图像进行低通滤波,注意此处的低通滤波方法可与第 1 步的不一样;
  5. 输入图像减去第 4 步的低通滤波图像,即可获得输入图像的高频图像 H0;
  6. 如果对第 2 步所得低频图像 L1 重复以上步骤,即可获得 L1 的低频图像 L2 与高频图像 H1;
  7. 因为 H0 的尺寸为输入尺寸,H1 的尺寸为输入尺寸的 1/2x1/2,L2 的尺寸为输入图像的 1/4x1/4,将 H0, H1, L2 按顺序堆叠起来,就形成一个金字塔的形状,我们称之为拉普拉斯金字塔;如果对 L2 继续分解,还可获得更高层次的金字塔;
  8. 仅凭 H0, H1, L2 就能无损地恢复输入图像 L0,这是显而易见的,将 L2 进行插零上采样以及与第 4 步相同的低通滤波后,与 H1 相加即可无损恢复到 L1,同理即有 L0;
  9. 因为 H0 是 L0 的高频,H1 是 L1 的高频,L2 是 L1 的低频,我们可以将 H0,H1, L2 分别称为输入图像的高频、中频、低频分量。

在以上拉普拉斯金字塔分解方法中,具体低/中/高频的意义并没有定量的描述。为此,我们可以基于傅里叶变换进行分析,因为以上的步骤都可以在频域通过简单的处理得到等价的结果。

图 1 图像下采样示例

首先看第 1 步低通滤波的必要性,因为在硬件中低通滤波需要用到 linebuffer,如果能够不滤波直接下采样是最好的。如图 1 所示,上行的左列是输入图像,这里为了突出高频分量的变化使用了比较极端的示例,中列是左列直接(不进行低通滤波)下采样再上采样的图像,其中只有偶行且偶列的像素是有效的,右列是左列偶行且偶列像素组成的图像,通过补零填充到一样的大小;下行的左列图像则是输入图像经过高斯低通滤波的结果,中列和右列的处理和上行一样。图 2 是图 1 各图像对应的离散傅里叶变换(DFT)结果,其中每个 DFT 图像的中心对应着低频分量,越往外则是高频分量,越亮代表越大的幅度。这里的频域分析涉及到一些数字信号处理的知识。简单地说,经过下采样以及上采样,中列图像的 DFT 是左列图像 DFT 经过 1/2 周期延拓并叠加的结果,这就会导致图像的高频分量部分发生混叠,造成一定的信号失真;右列图像的 DFT 是中列图像 DFT 中心 1/4 区域放大两倍的结果。

图 2 图 1 所示图像的 DFT

因为高斯滤波等价于在频域中与高斯函数相乘,所以输入图像经过低通滤波后,其高频分量有所衰减,可以从图 2 左下角看到一个弥散圆的轮廓,其半径由高斯滤波核的方差决定,方差越大也就是滤波核半径越大,此时频域中的弥散圆半径反而越小,即有更多的高频分量被衰减。可以看到,原图像(左上角)由于纹理比较丰富,所以在高频区域也有较大的幅度。如果不进行低通滤波而直接下采样(右上角),其 DFT 相比于低通滤波后下采样(右下角)可以保留更多的高频分量,这似乎是更好的选择。但问题在于,右上角虽然保留了更多的高频分量,但相较于左上角(原图)有很大一部分高频分量是不一致的,这首先要明白右列的 DFT 只是中列 DFT 中心 1/4 区域放大的结果,也就是说左列 DFT 中心 1/4 区域以外的高频分量已经被混叠到中心 1/4 区域而消失了。对于右下角,尽管高频分量损失了很多,但正因此没有因为下采样而发生混叠,我们看到的只是一幅相对模糊的图像;而对于右上角,因为混叠而可能出现高频分量一部分被衰减而另一部分被增强的情况,反而会导致图像产生很多意想不到的伪纹理,其中主要是锯齿等等。因此,我们倾向于在下采样前首先进行低通滤波。

不过要注意的是,即便我们不进行低通滤波而直接下采样,也照样可以执行拉普拉斯分解的余下步骤,得到相应的高频图像。只要高频图像的内容不发生改变,我们就可以无损地恢复到原图像,和下采样之前是否进行低通滤波并无关系。但问题也出在此处,例如当我们需要进行图像降噪的任务时,高频分量往往是被抑制的,而最极端的情况就是拉普拉斯分解所得的高频图像完全被消除,那么重构图像只取决于下采样所得图像,这自然地又回到了前面所说的高频混叠问题。因此从实际应用的角度出发,下采样之前的低通滤波是必不可少的。另外要注意的是,低通滤波的具体实现有很多种选择,常用的包括高斯滤波与均值滤波等等。高斯滤波的优点在于其在频域同样是高斯函数,因此其幅度曲线十分平滑,不会出现均值滤波所具有的高频区域震荡;缺点是在同样的滤波核长度下对于中低频分量的衰减幅度要低于均值滤波。关于滤波核长度为 5 的高斯滤波与均值滤波的频谱曲线如图 3 所示。

图 3 高斯滤波与均值滤波的频谱曲线

在理解了为什么要在下采样之前进行低通滤波后,我们还要理解拉普拉斯分解第 4 步中为什么上采样后还要进行一次低通滤波。回到图 1,经过第 3 步的上采样后,我们得到的图像的奇行或奇列都是 0,这时我们不能直接与原图像相减,否则得到的结果就是偶行且偶列的像素几乎为 0,而奇行或奇列的像素等同于原图像像素,因此我们需要一个插值的步骤。图像插值有很多种方法,包括最近邻插值、双线性插值等等,但是这些插值方法对于频域的分析不太方便。如果对数字信号处理比较熟悉,我们就知道离散信号可通过低通滤波实现线性内插。具体地,我们可以发现图 2 的中列图像的 DFT 中心 1/4 区域与左列图像的 DFT 十分相像,只是在四个角落以及四边中点出现了与中心重复的 DFT 分量,而这些重复的 DFT 分量正是上采样图像的奇行或奇列为 0 的原因,因此我们只要使用一个理想的低通滤波器把重复的 DFT 分量去除,只保留中心 1/4 区域的 DFT 分量,就能把图 1 的中列图像恢复到左列图像,当然已经发生混叠的高频分量是无法恢复的。由于理想低通滤波器需要在频域进行操作,而在实际应用中我们不可能有足够的算力把图像转换到频域,所以一般在时域使用普通的低通滤波器即可,例如最常用的也是高斯滤波。

图 4 低通滤波实现线性内插示例

如图 4 所示,上行的左列及中列图像为图 1 下行的左列及中列,而右列为中列图像经过高斯滤波的结果,图中下行即为三者对应的 DFT 图像。可以看到,只需一个简单的高斯滤波,即可实现上采样图像中奇行或奇列像素的插值,得到的结果除了稍微模糊以外与下采样之前的图像并没有太大的区别,而稍微模糊的原因也是由于此处的高斯滤波同样会导致一部分的高频信号衰减。实际上,如前面所述使用其他的插值方法比如最近邻插值也是可行的,只是最近邻插值不是一种零相位滤波器,其对于频谱的影响要比高斯滤波等零相位滤波器要复杂得多,并且容易导致锯齿以及伪纹理的产生。实际上,如果考虑长度为 3 的高斯滤波核即 [1, 2, 1],我们可以发现此时高斯滤波插值的结果和双线性插值是一样的,这个只需简单推理即可。相比于双线性插值,高斯滤波的优点是可以轻松地扩展滤波核的长度,从而灵活地调整高频分量衰减的范围,当然代价就是其所需的计算复杂度要更高一些。至此,我们就理解了拉普拉斯分解第 4 步上采样后进行低通滤波的意义。

图 5 拉普拉斯分解的高频图像示例

可以看到,拉普拉斯分解需要进行两次低通滤波,每一次滤波都会导致一部分高频分量衰减,因此第 5 步的原图像与最后一次低通滤波图像的相减就是要提取损失的高频分量。如图 5 所示,左列是原图像及其 DFT,中列是拉普拉斯分解第 4 步所得图像及其 DFT,右列是第 5 步所得高频图像及其 DFT。从右上角可以看出,高频图像主要对应着原图像的边缘和纹理,这是一个十分良好的性质,因为人眼主观感受系统往往更加喜欢锐利的边缘,所以我们在进行图像处理,如降噪时,可以对低频图像应用更高的降噪强度以获得更加平滑的背景,而对高频图像应用相对更弱的降噪强度,尽可能保证图像边缘细节的锐度,这样就能获得更好的主观降噪质量。实际上拉普拉斯分解的名字来自拉普拉斯算子,即二阶微分算子,对图像进行二阶微分同样能够实现边缘提取,因此我们常把能够提取图像边缘的方法称为拉普拉斯方法,尽管不一定需要用到二阶微分。

在很多应用中,单层的拉普拉斯分解并不满足我们对于不同尺度纹理的质量把控,这是因为有些大尺度的纹理在计算机看来也是属于低频内容。这里涉及到一个算法的可视域,也就是算法处理当前像素时能够访问的邻域像素的范围,当我们把图像不断缩小,算法的可视域就会越来越大,这样才能发现更多大尺度的信息。举个例子,一张白纸挡在面前,我们只会看到白茫茫的一片,没有任何细节,而当我们把白纸拿远一点,白纸占据的视野面积就会越来越小,最后我们就能发现白纸的四周是有边缘以及拐角的。因此,当我们完成单层的拉普拉斯分解后,如果对下采样所得低频图像继续分解,那么在继续分解所得的低频图像及高频图像上的算法处理,就等价于在一个更大的可视域上对原图像进行处理,这往往可以获得更好的主观效果,这就是为什么我们要使用拉普拉斯金字塔的原因。如图 6 所示,当我们进行两次拉普拉斯分解以后,分别可获得第一次分解所得高频图像 H0,第二次分解所得高频图像 H1,以及第二次分解所得低频图像 L2,它们的 DFT 图像刚好大致对应了原图像 DFT 的外沿带,中间带,以及中心区,也就是 H0 主要对应了原图像的极高频分量,H1 主要对应了原图像的中高频分量,L2 主要对应了原图像的中低频分量。由此,我们可简单地将 H0,H1,L2 分别称为原图像的高频、中频以及低频分量。

图 6 两次拉普拉斯分解金字塔示例

2. 测试脚本

# -*- coding: utf-8 -*-
import numpy as np
import cv2def periodic_gaussian_blur(img, ksize, sigma=0, ktype=0):kr = ksize // 2h, w = img.shape[:2]'''# mannual wrap paddingtmp = np.zeros([h+2*kr, w+2*kr], dtype=img.dtype)tmp[kr:h+kr, kr:w+kr] = imgtmp[kr:h+kr, :kr] = img[:, -kr:]tmp[kr:h+kr, -kr:] = img[:, :kr]tmp[:kr,  :] = tmp[-2*kr:-kr, :]tmp[-kr:, :] = tmp[kr:2*kr,   :]'''tmp = np.pad(img, ((kr, kr), (kr, kr)), 'wrap')if ktype == 0:tmp = cv2.GaussianBlur(tmp, (ksize, ksize), sigma)else:tmp = cv2.boxFilter(tmp, -1, (ksize, ksize), normalize=True)img = tmp[kr:h+kr, kr:w+kr]return imgksize = 5
sigma = 0
lvs = 2img = cv2.imread('test2.png', 0).astype('float32') - 128
np.random.seed(0)
#img = img + np.random.randn(*img.shape) * 30eps = 1e-15
vmax = 50000
freqs = []
valus = []for i in range(lvs):h, w = img.shape[:2]fft_img = np.fft.fft2(img)fft_img = np.fft.fftshift(fft_img)abs_img = np.abs(fft_img)abs_img = np.clip(abs_img + eps, 0, vmax)dns1 = np.zeros_like(img)dns1[::2, ::2] = img[::2, ::2] * 4fft_dns1 = np.fft.fft2(dns1)fft_dns1 = np.fft.fftshift(fft_dns1)abs_dns1 = np.abs(fft_dns1)abs_dns1 = np.clip(abs_dns1 + eps, 0, vmax)dns2 = np.zeros_like(img)dns2[:h//2, :w//2] = img[::2, ::2] * 4fft_dns2 = np.fft.fft2(dns2)fft_dns2 = np.fft.fftshift(fft_dns2)abs_dns2 = np.abs(fft_dns2)abs_dns2 = np.clip(abs_dns2 + eps, 0, vmax)gau = periodic_gaussian_blur(img, ksize, sigma)fft_gau = np.fft.fft2(gau)fft_gau = np.fft.fftshift(fft_gau)abs_gau = np.abs(fft_gau)abs_gau = np.clip(abs_gau + eps, 0, vmax)dng1 = np.zeros_like(gau)dng1[::2, ::2] = gau[::2, ::2] * 4fft_dng1 = np.fft.fft2(dng1)fft_dng1 = np.fft.fftshift(fft_dng1)abs_dng1 = np.abs(fft_dng1)abs_dng1 = np.clip(abs_dng1 + eps, 0, vmax)dng2 = np.zeros_like(gau)dng2[:h//2, :w//2] = gau[::2, ::2] * 4fft_dng2 = np.fft.fft2(dng2)fft_dng2 = np.fft.fftshift(fft_dng2)abs_dng2 = np.abs(fft_dng2)abs_dng2 = np.clip(abs_dng2 + eps, 0, vmax)disp1 = np.hstack((img, dns1, dns2))disp2 = np.hstack((gau, dng1, dng2))disp = np.vstack((disp1, disp2))disp = np.clip(disp + 128.5, 0, 255).astype('uint8')cv2.imwrite('img_down_{}.png'.format(i), disp)disp1 = np.hstack((abs_img, abs_dns1, abs_dns2))disp2 = np.hstack((abs_gau, abs_dng1, abs_dng2))disp = np.vstack((disp1, disp2))disp = np.clip(disp, 0, vmax)disp = (disp / vmax * 255 + 0.5).astype('uint8')cv2.imwrite('fft_down_{}.png'.format(i), disp)upg1 = periodic_gaussian_blur(dng1, ksize, sigma)fft_upg1 = np.fft.fft2(upg1)fft_upg1 = np.fft.fftshift(fft_upg1)abs_upg1 = np.abs(fft_upg1)abs_upg1 = np.clip(abs_upg1 + eps, 0, vmax)disp1 = np.hstack((gau, dng1, upg1)) + 128.5disp2 = np.hstack((abs_gau, abs_dng1, abs_upg1)) / vmax * 255 + 0.5disp = np.vstack((disp1, disp2))disp = np.clip(disp, 0, 255).astype('uint8')cv2.imwrite('gau_up_{}.png'.format(i), disp)dif1 = img - upg1fft_dif1 = np.fft.fft2(dif1)fft_dif1 = np.fft.fftshift(fft_dif1)abs_dif1 = np.abs(fft_dif1)abs_dif1 = np.clip(abs_dif1 + eps, 0, vmax)if i == 0:freqs.append(abs_dif1)valus.append(np.abs(dif1))else:freq = np.zeros_like(freqs[0])valu = np.zeros_like(valus[0])h0, w0 = freq.shape[:2]freq[::(2**(i)), ::(2**(i))] = dif1 * (4**i)fft_freq1 = np.fft.fft2(freq)fft_freq1 = np.fft.fftshift(fft_freq1)abs_freq1 = np.abs(fft_freq1)abs_freq1 = np.clip(abs_freq1 + eps, 0, vmax)freq *= 0freq[h0//2-h//2:h0//2-h//2+h, w0//2-w//2:w0//2-w//2+w] = abs_freq1[h0//2-h//2:h0//2-h//2+h, w0//2-w//2:w0//2-w//2+w]freqs.append(freq)valu[h0//2-h//2:h0//2-h//2+h, w0//2-w//2:w0//2-w//2+w] = np.abs(dif1)valus.append(valu)if i == lvs - 1:freq = np.zeros_like(freqs[0])valu = np.zeros_like(valus[0])freq[::(2**(i+1)), ::(2**(i+1))] = gau[::2, ::2] * (4**(i+1))fft_freq1 = np.fft.fft2(freq)fft_freq1 = np.fft.fftshift(fft_freq1)abs_freq1 = np.abs(fft_freq1)abs_freq1 = np.clip(abs_freq1 + eps, 0, vmax)freq *= 0freq[h0//2-h//4:h0//2-h//4+h//2, w0//2-w//4:w0//2-w//4+w//2] = abs_freq1[h0//2-h//4:h0//2-h//4+h//2, w0//2-w//4:w0//2-w//4+w//2]freqs.append(freq)valu[h0//2-h//4:h0//2-h//4+h//2, w0//2-w//4:w0//2-w//4+w//2] = gau[::2, ::2] + 128valus.append(valu)disp1 = np.hstack((img+128, upg1+128, np.abs(dif1))) + 0.5disp2 = np.hstack((abs_img, abs_upg1, abs_dif1)) / vmax * 255 + 0.5disp = np.vstack((disp1, disp2))disp = np.clip(disp, 0, 255).astype('uint8')cv2.imwrite('diff_{}.png'.format(i), disp)img = gau[::2, ::2]disp1 = np.hstack(tuple(valus)) + 0.5
disp2 = np.hstack(tuple(freqs)) / vmax * 255 + 0.5
disp = np.vstack((disp1, disp2))
disp = np.clip(disp, 0, 255).astype('uint8')
cv2.imwrite('diff_low.png', disp)

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

创建影子用户

文章目录 1.认识影子用户2.创建隐藏账户并加入管理员组3.修改注册表3.删除用户4.添加管理员权限 1.认识影子用户 影子用户通常指的是那些在系统用户列表中不可见,但在某些情况下可以进行操作的用户。在内网渗透过程中,当我们拿到shell时,肯定…

微博百度热搜收集

背景 大家都有使用微博、百度吧,而每天的热搜想必大家也用的不少。微博、百度的热搜有7、8种分类,每个单独查看比较耗费时间,效率极低,大概要花费3,4分钟左右。最近闲来无事,冒出个想法,是不是有…

rmallox勒索病毒#如何防范及处理?

rmallox勒索病毒介绍 rmallox将其特定的“.rmallox”扩展名添加到每个文件的名称中。例如,您命名为“my_dog.jpeg”的照片将被转换为“ my_dog.jpeg.rmallox”,在名为“ 资料.xlsx ”的Excel表格中报告——转换为“ 资料.xlsx.rmallox”,等等…

中医圆运动规律

目录 人体圆运动营气在十二经脉的运行规律子午流注与圆运动升降结合图 人体圆运动 营气在十二经脉的运行规律 营气在脉中,卫气在脉外 这个顺序也是子午流注的顺序 子午流注与圆运动升降结合图

DBA面试总结(Mysql篇)

一、delete与trancate的区别 相同点 1.两者都是删除表中的数据,不删除表结构 不同点 1.delete支持按条件删除,TRUNCATE不支持。 2.delete 删除后自增列不会重置,而TRUNCATE会被重置。 3.delete是逐条删除(速度较慢&#xff09…

OpenBayes 在线教程|张国荣、鲁迅等老照片秒变高清!即刻上手的超火 SUPIR-AI 图像修复教程

小伙伴们,大家在生活中是不是也会遇到这样的烦恼:心心念念想要打印一张充满回忆的老照片或酷炫动漫壁纸,却发现图像糊得像打了马赛克? 市面上的图像修复工具五花八门,选择困难症人群找得快要崩溃? 终于找…

看看《MATLAB科研绘图与学术图表绘制从入门到精通》示例:绘制山鸢尾萼片长度和萼片宽度的小提琴图

使用MATLAB绘制鸢尾花数据集( fisheriris)中山鸢尾( Iris Setosa)的萼片长度和 萼片宽度的小提琴图。这将帮助我们更好地了解山鸢尾的这两个特征的数据分布情况,包括它们的 中位数、四分位范围及密度估计。这种可视化工…

antDesignVue 使用-持续更新

背景 vue3viteantdesignvuevue-router 1,全局完整注册 1.1下载antdesignvue npm i --save ant-design-vue 或者 npm install ant-design-vuenext --save 1.2在mian.ts中引入 import { createApp } from vue import { createPinia } from piniaimport App from ./App.vue …

ST-LINK Utility 4.6.0 下载安装及使用方法介绍

一、介绍 STM32 ST-LINK Utility是针对STM32全系芯片进行编程(读、写、擦除、选项字)的一款工具。 STM32 ST-LINK Utility软件主要的功能就是量产(批量下载代码的工具)。它也是比较实用的一个工具,当我们需要查看芯片F…

关于用虚拟机安装ubuntu22.04出现的问题,和之前踩过的坑

文章目录 概要如果自己分区虚拟机长时间进不去ubuntu系统建议的分区大小小结 概要 利用虚拟机安装ubuntu的好处是在ubuntu下载的东西不会因为关机而删除 今天心血来潮,想要学习一下把linux系统装到u盘来使用,搜教程但是进行到的时候,出现了错…

用ChatGPT读了几百篇文献,我总结出了文献综述这些经验

点击下方▼▼▼▼链接直达AIPaperPass ! AIPaperPass - AI论文写作指导平台 近期小编会将学术论文写作每一个流程需要了解的细节与ChatGPT在这细节的背景下如何提升我们的学术论文进行分章节讨论。最终汇总成一篇长文攻略。宝子们敬请期待哦。今天我们来详细聊聊文…

【零基础入门TypeScript】模块

目录 内部模块 内部模块语法(旧) 命名空间语法(新) 两种情况下生成的 JavaScript 是相同的 外部模块 选择模块加载器 定义外部模块 句法 例子 文件:IShape.js 文件:Circle.js 文件:…