OpenCV的周期性噪声去除滤波器(70)

news/2024/5/19 11:41:34
返回:OpenCV系列文章目录(持续更新中......)
上一篇:OpenCV如何通过梯度结构张量进行各向异性图像分割(69)
下一篇 :OpenCV如何为我们的应用程序添加跟踪栏(71)

目录

目标

理论

如何消除傅里叶域中的周期性噪声?

源代码

解释

结果

目标

在本教程中,您将学习:

  • 如何消除傅里叶域中的周期性噪声

理论

注意

解释基于该书[108]。此页面上的图像是真实世界的图像。

周期性噪声在傅里叶域中产生尖峰,通常可以通过视觉分析检测到。

如何消除傅里叶域中的周期性噪声?

通过频域滤波可以显著降低周期性噪声。在此页面上,我们使用具有适当半径的陷波抑制滤波器来完全封闭傅里叶域中的噪声尖峰。陷波滤波器抑制中心频率附近预定义邻域中的频率。陷波滤波器的数量是任意的。缺口区域的形状也可以是任意的(例如矩形或圆形)。在此页面上,我们使用三个圆形陷波抑制滤光片。图像的功率谱致密化用于噪声尖峰的视觉检测。

源代码

您可以在 OpenCV 源代码库中找到源代码。samples/cpp/tutorial_code/ImgProc/periodic_noise_removing_filter/periodic_noise_removing_filter.cpp

#include <iostream>
#include "opencv2/highgui.hpp"
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>using namespace cv;
using namespace std;void fftshift(const Mat& inputImg, Mat& outputImg);
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H);
void synthesizeFilterH(Mat& inputOutput_H, Point center, int radius);
void calcPSD(const Mat& inputImg, Mat& outputImg, int flag = 0);const String keys =
"{help h usage ? | | print this message }"
"{@image |period_input.jpg | input image name }"
;int main(int argc, char* argv[])
{CommandLineParser parser(argc, argv, keys);string strInFileName = parser.get<String>("@image");samples::addSamplesDataSearchSubDirectory("doc/tutorials/imgproc/periodic_noise_removing_filter/images");Mat imgIn = imread(samples::findFile(strInFileName), IMREAD_GRAYSCALE);if (imgIn.empty()) //check whether the image is loaded or not{cout << "ERROR : Image cannot be loaded..!!" << endl;return -1;}imshow("Image corrupted", imgIn);imgIn.convertTo(imgIn, CV_32F);// it needs to process even image onlyRect roi = Rect(0, 0, imgIn.cols & -2, imgIn.rows & -2);imgIn = imgIn(roi);// PSD calculation (start)Mat imgPSD;calcPSD(imgIn, imgPSD);fftshift(imgPSD, imgPSD);normalize(imgPSD, imgPSD, 0, 255, NORM_MINMAX);// PSD calculation (stop)//H calculation (start)Mat H = Mat(roi.size(), CV_32F, Scalar(1));const int r = 21;synthesizeFilterH(H, Point(705, 458), r);synthesizeFilterH(H, Point(850, 391), r);synthesizeFilterH(H, Point(993, 325), r);//H calculation (stop)// filtering (start)Mat imgOut;fftshift(H, H);filter2DFreq(imgIn, imgOut, H);// filtering (stop)imgOut.convertTo(imgOut, CV_8U);normalize(imgOut, imgOut, 0, 255, NORM_MINMAX);imwrite("result.jpg", imgOut);imwrite("PSD.jpg", imgPSD);fftshift(H, H);normalize(H, H, 0, 255, NORM_MINMAX);imshow("Debluring", imgOut);imwrite("filter.jpg", H);waitKey(0);return 0;
}void fftshift(const Mat& inputImg, Mat& outputImg)
{outputImg = inputImg.clone();int cx = outputImg.cols / 2;int cy = outputImg.rows / 2;Mat q0(outputImg, Rect(0, 0, cx, cy));Mat q1(outputImg, Rect(cx, 0, cx, cy));Mat q2(outputImg, Rect(0, cy, cx, cy));Mat q3(outputImg, Rect(cx, cy, cx, cy));Mat tmp;q0.copyTo(tmp);q3.copyTo(q0);tmp.copyTo(q3);q1.copyTo(tmp);q2.copyTo(q1);tmp.copyTo(q2);
}void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H)
{Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };Mat complexI;merge(planes, 2, complexI);dft(complexI, complexI, DFT_SCALE);Mat planesH[2] = { Mat_<float>(H.clone()), Mat::zeros(H.size(), CV_32F) };Mat complexH;merge(planesH, 2, complexH);Mat complexIH;mulSpectrums(complexI, complexH, complexIH, 0);idft(complexIH, complexIH);split(complexIH, planes);outputImg = planes[0];
}void synthesizeFilterH(Mat& inputOutput_H, Point center, int radius)
{Point c2 = center, c3 = center, c4 = center;c2.y = inputOutput_H.rows - center.y;c3.x = inputOutput_H.cols - center.x;c4 = Point(c3.x,c2.y);circle(inputOutput_H, center, radius, 0, -1, 8);circle(inputOutput_H, c2, radius, 0, -1, 8);circle(inputOutput_H, c3, radius, 0, -1, 8);circle(inputOutput_H, c4, radius, 0, -1, 8);
}// Function calculates PSD(Power spectrum density) by fft with two flags
// flag = 0 means to return PSD
// flag = 1 means to return log(PSD)
void calcPSD(const Mat& inputImg, Mat& outputImg, int flag)
{Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };Mat complexI;merge(planes, 2, complexI);dft(complexI, complexI);split(complexI, planes); // planes[0] = Re(DFT(I)), planes[1] = Im(DFT(I))planes[0].at<float>(0) = 0;planes[1].at<float>(0) = 0;// compute the PSD = sqrt(Re(DFT(I))^2 + Im(DFT(I))^2)^2Mat imgPSD;magnitude(planes[0], planes[1], imgPSD); //imgPSD = sqrt(Power spectrum density)pow(imgPSD, 2, imgPSD); //it needs ^2 in order to get PSDoutputImg = imgPSD;// logPSD = log(1 + PSD)if (flag){Mat imglogPSD;imglogPSD = imgPSD + Scalar::all(1);log(imglogPSD, imglogPSD);outputImg = imglogPSD;}
}

解释

通过频域滤波进行周期性降噪,包括功率谱密度计算(用于噪声尖峰视觉检测)、陷波抑制滤波器合成和频率滤波:

 // it needs to process even image onlyRect roi = Rect(0, 0, imgIn.cols & -2, imgIn.rows & -2);imgIn = imgIn(roi);// PSD calculation (start)Mat imgPSD;calcPSD(imgIn, imgPSD);fftshift(imgPSD, imgPSD);normalize(imgPSD, imgPSD, 0, 255, NORM_MINMAX);// PSD calculation (stop)//H calculation (start)Mat H = Mat(roi.size(), CV_32F, Scalar(1));const int r = 21;synthesizeFilterH(H, Point(705, 458), r);synthesizeFilterH(H, Point(850, 391), r);synthesizeFilterH(H, Point(993, 325), r);//H calculation (stop)// filtering (start)Mat imgOut;fftshift(H, H);filter2DFreq(imgIn, imgOut, H);// filtering (stop)

函数 calcPSD()计算图像的功率谱密度:

void calcPSD(const Mat& inputImg, Mat& outputImg, int flag)
{Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };Mat complexI;merge(planes, 2, complexI);dft(complexI, complexI);split(complexI, planes); // planes[0] = Re(DFT(I)), planes[1] = Im(DFT(I))planes[0].at<float>(0) = 0;planes[1].at<float>(0) = 0;// compute the PSD = sqrt(Re(DFT(I))^2 + Im(DFT(I))^2)^2Mat imgPSD;magnitude(planes[0], planes[1], imgPSD); //imgPSD = sqrt(Power spectrum density)pow(imgPSD, 2, imgPSD); //it needs ^2 in order to get PSDoutputImg = imgPSD;// logPSD = log(1 + PSD)if (flag){Mat imglogPSD;imglogPSD = imgPSD + Scalar::all(1);log(imglogPSD, imglogPSD);outputImg = imglogPSD;}
}

函数 synthesizeFilterH()根据中心频率和半径形成理想圆形陷波抑制滤波器的传递函数:

void synthesizeFilterH(Mat& inputOutput_H, Point center, int radius)
{Point c2 = center, c3 = center, c4 = center;c2.y = inputOutput_H.rows - center.y;c3.x = inputOutput_H.cols - center.x;c4 = Point(c3.x,c2.y);circle(inputOutput_H, center, radius, 0, -1, 8);circle(inputOutput_H, c2, radius, 0, -1, 8);circle(inputOutput_H, c3, radius, 0, -1, 8);circle(inputOutput_H, c4, radius, 0, -1, 8);
}

函数 filter2DFreq()过滤频域中的图像。函数 fftshift()和 filter2DFreq()是从教程 Out-of-focus Deblur Filter 中复制的。

结果

下图显示了被各种频率的周期性噪声严重损坏的图像。

噪声分量很容易被看作是下图所示的功率谱密度中的亮点(尖峰)。

下图显示了具有适当半径的陷波抑制滤波器,以完全封闭噪声尖峰。

使用陷波抑制滤波器处理图像的结果如下所示。

这种改进是显而易见的。与原始图像相比,此图像包含的可见周期性噪声要少得多。

您还可以在 YouTube 上找到此过滤理念的快速视频演示。


http://www.mrgr.cn/p/55765506

相关文章

linux24-网络传输 | 端口 namp netstat

linux24-网络传输 | 端口 namp netstat端口nmap 查看指定IP对外暴露的端口netstat 查看指定端口的占用情况端口 设备与外界通讯交流的出入口, 端口可分为物理端口和虚拟端口物理端口: 又称接口, 是可见的端口, 如USB接口, RJ45网口, HDMI端口等虚拟端口: 是指计算机内部的端口,…

算法学习笔记(15): Splay树

Splay树 Splay树又名伸展树, 是tarjan为LCT而发明的平衡树, 通过旋转操作维护二叉搜索树的高度平衡。 均摊复杂度 \(O(logb)\), 可以区间操作, 不能可持久化, 常数较大(大于FHQtreap), 但是可以 \(O(nlogn)\) 实现 LCT。(这是唯一比FHQtreap优秀的店...) 算法 splay树…

面试笔记——多线程使用场景

线程池使用场景&#xff08;CountDownLatch&#xff0c; Future&#xff09; CountDownLatch CountDownLatch&#xff08;闭锁/倒计时锁&#xff09;用来进行线程同步协作&#xff0c;等待所有线程完成倒计时&#xff08;一个或者多个线程&#xff0c;等待其他多个线程完成某件…

JuiceFS v1.2-beta1,Gateway 升级,多用户场景权限管理更灵活

JuiceFS v1.2-beta1 今天正式发布。在这个版本中,除了进行了大量使用体验优化和 bug 修复外,新增三个特性:Gateway 功能扩展:新增了“身份和访问管理(Identity and Access Management,IAM)” 与 “事件通知” ,为用户提供更安全、灵活和自动化的数据管理和监控能力,适…

DP32RF002—低功耗SUB-1G收发一体SOC芯片

DP32RF002是基于ARM Cortex-M0+内核的超低功耗、高性能的、单片集成(G)FSK/OOK无线收发机的32位SoC芯片。工作于200~960MHz范围内,支持灵活可设的数据包格式,支持自动应答和自动重发功能,支持跳频操作,支持FEC功能,同时内部集成了完整的射频接收机、射频发射机、频率综合器…

盘点效率工具RunFlow那些容易被忽略的功能

本文我们将带您了解RunFlow有哪些容易被忽略、但是又非常实用的功能。还不了解RunFlow?从这里开始了解。固定工作窗口您还可以通过双击 Ctrl 键来切换窗口固定状态,您也可以在 热点事件 设置页面自定义该快捷键。预览菜单内容用浏览器打开剪贴板复制的URL多行输入按 Ctrl+Ent…

Redis Zset的底层原理

Redis Zset的底层原理 ZSet也就是SortedSet&#xff0c;其中每一个元素都需要指定一个score值和member值&#xff1a; 可以根据score值排序后member必须唯一可以根据member查询分数 因此&#xff0c;zset底层数据结构必须满足键值存储、键必须唯一、可排序这几个需求。之前学…

LSTM时间序列预测中的一个常见错误以及如何修正

当使用LSTM进行时间序列预测时,人们容易陷入一个常见的陷阱。为了解释这个问题,我们需要先回顾一下回归器和预测器是如何工作的。预测算法是这样处理时间序列的:一个回归问题是这样的:因为LSTM是一个回归量,我们需要把时间序列转换成一个回归问题。有许多方法可以做到这一点…

Plumed分子模拟后分析

Plumed是一个强大的分子模拟数据处理工具,可以在模拟的过程中逐步分析,也可以保存模拟的轨迹做后分析。本文紧接前面的“增强采样软件PLUMED的安装与使用”文章,还有“直方图与核密度估计”文章。介绍了如何使用Plumed后分析工具,对输出的反应坐标的轨迹进行核密度估计。技…

C++奇迹之旅:string类对象的容量操作

文章目录 &#x1f4dd; string类的常用接口&#x1f309; string类对象的容量操作&#x1f320;size&#x1f320;length&#x1f320;capacity&#x1f320;clear&#x1f320;empty&#x1f320;reserve&#x1f309;resize &#x1f6a9;总结 &#x1f4dd; string类的常用…

PWN入门之Stack Overflow

Stack Overflow是一种程序的运行时&#xff08;runtime&#xff09;错误&#xff0c;中文翻译过来叫做“栈溢出”。栈溢出原理是指程序向栈中的某个变量中写入的字节数超过了这个变量本身所申请的字节数&#xff0c;导致与其相邻的栈中的变量值被改变。 在本篇文章中&#xff…

动手学深度学习——卷积操作

卷积 卷积概念卷积原属于信号处理中的一种运算,引入CNN中,作为从输入中提取特征的基本操作 补零:在输入端外侧填补0值使得卷积输出结果满足某种大小,在外侧的每一边都添加0值,使得输出可以达到某种预定形状 跨步:卷积核在输入上滑动时每次移动到下一步的距离使用张量实现…

MyBatis-Plus 分页查询配置

说明一下 ,使用MyBatis-Plus 进行分页查询时 ,要先进行配置添加配置 /*** @Author North* @Date 2024/5/6*/ @Configuration public class MPConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new My…

编译官方原版的openwrt并加入第三方软件包

最近又重新编译了最新的官方原版openwrt-2305&#xff08;2024.3.22&#xff09;&#xff0c;此处记录一下以待日后参考。 目录 1.源码下载 1.1 通过官网直接下载 1.2 映射github加速下载 1.2.1 使用github账号fork源码 1.2.2 创建gitee账号映射github openwrt 2.编译准…

QT5之事件——包含提升控件

事件概述 信号就是事件的一种&#xff0c;事件由用户触发&#xff1b; 鼠标点击窗口&#xff0c;也可以检测到事件&#xff1b;产生事件后&#xff0c;传给事件处理&#xff0c;判断事件类型&#xff0c;后执行事件相应函数&#xff1b; 类似单片机的中断&#xff08;中断向量…

前端奇怪面试题总结

面试题总结 不修改下面的代码进行正常结构 这道题考的是迭代器和生成器的概念 let [a,b] {a:1,b:2}答案 对象缺少迭代器&#xff0c;需要手动加上 Object.prototype[Symbol.iterator] function* (){// return Object.values(this)[Symbol.iterator]()return yeild* Object.v…

Java从菜鸟到高手②

目录 1.输入输出 1.1输出 1.2.输入 1.3.scan.nextLine()和scan.next()的区别 2.方法的使用 2.1.方法的定义 2.2.形参和实参的关系 2.3.方法的重载 2.4.方法签名 3.递归 3.1.递归有递和归两个部分组成 4.数组 4.1.数组的定义 4.2.将数组转化为字符串 4.3深入理解…

VS打包项目成.exe.msi

VS打包项目成.exe&.msi ref:https://blog.csdn.net/weixin_44790046/article/details/103016154准备工作VS 2022(VS2017无法安装Installer Projects扩展,未知原因) Installer Projects (扩展 > 管理扩展 > 联机 > 搜索 > Microsoft Visual Studio Installe…

Spring学习之——Bean加载流程

Spring IOC容器就像是一个生产产品的流水线上的机器,Spring创建出来的Bean就好像是流水线的终点生产出来的一个个精美绝伦的产品。既然是机器,总要先启动,Spring也不例外。因此Bean的加载流程总体上来说可以分为两个阶段:容器启动阶段 Bean创建阶段一、容器启动阶段: 容器…

探索快速排序算法:理解、优化与应用

在计算机科学领域&#xff0c;排序算法是一项基础而重要的技术&#xff0c;它在各种应用场景中发挥着至关重要的作用。其中&#xff0c;快速排序算法因其高效性和广泛应用而备受关注。本文将深入探讨快速排序算法的原理、优化技巧以及实际应用&#xff0c;带你深入理解这一经典…