visual Studio MFC 平台实现图片的傅里叶变换

图片的傅里叶变换

本文主要讲解傅里叶变换的基本数学概念与物理概念,并本文使用visual Studio MFC 平台实现对傅里叶变换在图片上进行了应用。

一、傅里叶变换的原理

在这里推荐一篇讲得非常形象的文章通俗讲解:图像傅里叶变换

1.1 傅里叶变换原理的说明

傅里叶变换是一种数学变换,它将一个函数(可以是时域中的信号空域中的图像)转换为频域中的表示,从而揭示了信号或图像中存在的不同频率成分。在图像处理中,我们通常使用二维傅里叶变换来处理二维图像。

对于一幅图像而言,傅里叶变换将图像从空域(像素点的位置)转换到频域(不同频率的成分)频域中的信息描述了图像中不同空间频率的变化。这对于分析图像中的纹理、边缘以及其他特征非常有用。

傅里叶变换的结果可以分解为振幅谱和相位谱。振幅谱表示了图像中不同频率成分的强度,而相位谱表示了这些频率成分的相位信息

在图像处理中,傅里叶变换常常用于频域滤波、图像增强、压缩等应用。傅里叶变换的逆变换可以将频域表示转换回空域,因此我们可以在频域中进行操作后再通过逆变换得到处理后的图像。

从纯粹的数学意义上看,傅里叶变换是将一个图像函数转换为一系列周期函数来处理的;
物理效果看,傅里叶变换是将图像从空间域转换到频率域,其逆变换是将图像从频率域转换到空间域。即傅里叶变换的物理意义是将图像的灰度分布函数变换为图像的频率分布函数,傅里叶逆变换是将图像的频率分布函数变换为灰度分布函数。

实际上对图像进行二维傅里叶变换得到频谱图就是图像梯度的分布图,傅里叶频谱图上看到的明暗不一的亮点实际上图像上某一点 与邻域点差异的强弱,即梯度的大小,即该点的频率大小
如果频谱图中暗的点数更多,则实际图像是比较柔和的;反之,如果频谱图中亮的点数多,则实际图像是比较尖锐的,边界分明且边界两边像素差异较大

以下是对一些关键概念的简要总结:

  1. 数学意义: 傅里叶变换是一种将一个信号(包括图像)从时间域(或空间域)转换为频率域的方法。它将一个信号分解为一组正弦和余弦函数(频率分量),以揭示信号中的不同频率成分。

  2. 物理效果: 傅里叶变换的物理效果是将图像从空间域(表示像素在图像中的位置)转换为频率域(表示图像中不同频率的成分)。逆傅里叶变换则将频率域的信息重新转换为空间域,从而实现对原始图像的还原。

  3. 频谱图: 傅里叶变换的结果通常以频谱图的形式呈现,其中显示了图像在频率域中的分布。频谱图中的亮度表示不同频率成分的强度,而频谱图上的点对应于不同的频率。

  4. 梯度分布: 在频谱图中,明暗不一的亮点对应于图像中不同频率的变化。较暗的区域表示较低频率的成分,而较亮的区域表示较高频率的成分。这与图像的梯度(差异)有关,因为梯度较大的区域通常对应于频谱图中较亮的部分。

总体而言,傅里叶变换为我们提供了一种在频率域中理解图像特征的工具,有助于分析图像的纹理、边缘和其他结构。这对于许多图像处理任务,如滤波、增强和压缩,都是非常有用的。

1.2 数学表达

1.2.1 傅里叶变换的数学表示

对于一个连续函数 f ( t ) f(t) f(t)其傅里叶变换 F ( w ) F(w) F(w) 定义为:

F ( w ) = ∫ − ∞ ∞ f ( t ) ⋅ e − i w t   d t F(w) = \int_{-\infty}^{\infty} f(t) \cdot e^{-iwt} \,dt F(w)=f(t)eiwtdt

其中, F ( w ) F(w) F(w)表示频率为 w w w的成分的复幅度, e − i w t e^{-iwt} eiwt 是复指数函数, t t t 是时间。

1.2.2 傅里叶逆变换的数学表示

傅里叶变换的逆过程是傅里叶逆变换,它将频率域表示还原为时间或空间域表示。对于一个频谱 F ( w ) F(w) F(w),其逆变换 f ( t ) f(t) f(t) 定义为:

f ( t ) = 1 2 π ∫ − ∞ ∞ F ( w ) ⋅ e i w t   d w f(t) = \frac{1}{2\pi} \int_{-\infty}^{\infty} F(w) \cdot e^{iwt} \,dw f(t)=2π1F(w)eiwtdw

1.2.3 离散傅里叶变换(DFT)的数学表示

在数字信号处理中,通常使用离散傅里叶变换。对于一个离散序列 x [ n ] x[n] x[n],其离散傅里叶变换 X [ k ] X[k] X[k] 定义为:

X [ k ] = ∑ n = 0 N − 1 x [ n ] ⋅ e − i 2 π N k n X[k] = \sum_{n=0}^{N-1} x[n] \cdot e^{-i\frac{2\pi}{N}kn} X[k]=n=0N1x[n]eiN2πkn

其中, N N N 是序列的长度, X [ k ] X[k] X[k] 表示频率为 k N \frac{k}{N} Nk 的成分的复幅度。

1.3 傅里叶变换的应用

  1. 频域分析: 傅里叶变换允许我们在频域中分析信号,找到不同频率的成分。

  2. 滤波: 在频域中进行滤波操作,去除或增强特定频率的成分。

  3. 信号压缩: 将信号在频域中表示通常可以更紧凑地表示信号。

  4. 图像处理: 在图像处理中,傅里叶变换常用于分析和处理图像的频率成分。

总的来说,傅里叶变换是一种非常强大的数学工具,广泛应用于信号处理、通信、图像处理等领域。

二、 傅里叶变换与快速傅里叶变换的区别

傅里叶变换(Fourier Transform)和快速傅里叶变换(Fast Fourier Transform, FFT)是两种相关但不同的数学算法,用于在信号处理、图像处理等领域分析信号的频域特征。

  1. 傅里叶变换:

    • 定义: 傅里叶变换是一种将信号从时域(时间域)转换到频域(频率域)的数学变换。对于连续信号,傅里叶变换的定义是积分形式;对于离散信号,傅里叶变换的定义是离散形式。
    • 计算复杂度: 对于 N 个数据点的信号,傅里叶变换的计算复杂度是 O(N^2),其中 N 是数据点的数量。
  2. 快速傅里叶变换(FFT):

    • 定义: FFT 是一种优化算法,用于高效地计算傅里叶变换。FFT 算法可以在 O(N log N) 的时间内完成信号的傅里叶变换,相较于传统的傅里叶变换算法,FFT 算法速度更快
    • 计算复杂度: FFT 的计算复杂度相对较低,使得在计算机上更容易处理大规模的数据。

总结区别:

  • FFT 是一种特定的傅里叶变换算法,它是为了提高傅里叶变换的计算速度而设计的。
  • 傅里叶变换是一般性的概念,可以用于描述信号在频域中的表示;FFT 是实现傅里叶变换的具体算法。
  • FFT 的主要优势在于其计算速度,尤其是对于大规模数据的情况,它比传统傅里叶变换更为高效。

在实际应用中,由于 FFT 算法的高效性,它通常是首选的频域分析工具。很多信号处理库和软件包中都包含了 FFT 的实现,使得工程师和科学家能够更方便地进行频域分析。

三、 绘制傅里叶变换的频谱

因为要使用opencv的库所以visual Studio没有配置opencv的,需要先配置一下,可以参考博客Visual Studio 配置opencv环境

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core.hpp>

using namespace cv;
// 中心化频谱函数
void fftShift(Mat& mag)
{
 int cx = mag.cols / 2;
 int cy = mag.rows / 2;

 Mat q1(mag, cv::Rect(0, 0, cx, cy));
 Mat q2(mag, cv::Rect(cx, 0, cx, cy));
 Mat q3(mag, cv::Rect(0, cy, cx, cy));
 Mat q4(mag, cv::Rect(cx, cy, cx, cy));

 Mat tmp;
 q1.copyTo(tmp);
 q4.copyTo(q1);
 tmp.copyTo(q4);

 q2.copyTo(tmp);
 q3.copyTo(q2);
 tmp.copyTo(q3);
}
//FFT傅里叶变换
void CMFCApplication1View::OnFft()
{
 // TODO: 在此添加命令处理程序代码
 
 if (gray_data != nullptr) {
  // 将灰度数据转换为OpenCV的Mat对象,图像尺寸为bmpHeight x bmpWidth,数据类型为8位无符号整数
  Mat image(bmpHeight, bmpWidth, CV_8U, gray_data);

  // 执行傅里叶变换
  Mat padded; // 扩展输入图像到最佳尺寸
  int m = getOptimalDFTSize(image.rows);
  int n = getOptimalDFTSize(image.cols);
  copyMakeBorder(image, padded, 0, m - image.rows, 0, n - image.cols, BORDER_CONSTANT, Scalar::all(0));//在图像周围填充零

  // 为频域图像分配内存
  Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };//存储频域表示的复杂图像
  Mat complexImage;
  merge(planes, 2, complexImage);

  // 执行傅里叶变换,从空间域转换到频域。
  dft(complexImage, complexImage);

  // 分离实部和虚部,存储在planes数组中
  split(complexImage, planes);

  // 计算幅度谱,将结果存储在planes数组的第一个平面
  magnitude(planes[0], planes[1], planes[0]);

  // 变换到对数尺度     //对幅度谱进行对数变换以增强可视化效果
  planes[0] += Scalar::all(1);
  log(planes[0], planes[0]);

  // 归一化到 [0, 1]
  normalize(planes[0], planes[0], 0, 1, NORM_MINMAX);

  // 显示未中心化的频谱图
  imshow("Spectrum (Uncentered)", planes[0]);

  // 中心化频谱
  fftShift(planes[0]);

  // 显示中心化后的频谱图
  imshow("Spectrum (Centered)", planes[0]);

  waitKey(0);//等待按键操作,然后关闭图像窗口
 }
 else {
  // 处理图像未加载的情况
  AfxMessageBox(_T("未加载灰度图片"));
 }
}

说明:dft(complexImage, complexImage); 这一行执行的傅里叶变换,调用了OpenCV中的dft函数,对输入的complexImage执行傅里叶变换。在这个上下文中,dft是一个通用的傅里叶变换函数,而非专门的FFT实现。由于OpenCV中的dft函数使用了优化算法,它实际上在内部采用了FFT来提高计算效率,但在代码层面上,我们通常将其称为傅里叶变换。

左:灰度图 中:进行中心化后的频谱图 右:未中心化的频谱图
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++ 二叉搜索树(BST)的实现(非递归版本与递归版本)与应用

C 二叉搜索树的实现与应用 一.二叉搜索树的特点二.我们要实现的大致框架三.Insert四.InOrder和Find1.InOrder2.Find 五.Erase六.Find,Insert,Erase的递归版本1.FindR2.InsertR3.EraseR 七.析构,拷贝构造,赋值运算符重载1.析构2.拷贝构造3.赋值运算重载 八.Key模型完整代码九.二…

Prometheus 监控笔记(1):你真的会玩监控吗?

认识Prometheus Prometheus 是一种开源的系统和服务监控工具&#xff0c;最初由 SoundCloud 开发&#xff0c;后来成为继 Kubernetes 之后云原生生态系统中的一部分。在 Kubernetes 容器管理系统中&#xff0c;通常会搭配 Prometheus 进行监控&#xff0c;同时也支持多种 Expo…

股票价格预测 | Python实现基于ARIMA和LSTM的股票预测模型(含XGBoost特征重要性衡量)

文章目录 效果一览文章概述模型描述源码设计效果一览 文章概述 Python实现基于ARIMA和LSTM的股票预测模型(Stock-Prediction) Data ExtractionFormatting data for time seriesFeature engineering(Feature Importance using X

2023NEFU实习项目解析 - 中俄贸易供需服务平台

文章目录 项目概述项目初始化搭建项目初始框架配置Tomcat建立项目数据库编写统一返回类及其工具类编写数据库工具类通过Filter解决Response返回中文乱码问题使用Filter解决权限校验问题 项目主干开发用户登录企业管理&#xff08;分页查询原生实现&#xff09;上传VIP申请书模板…

【ArkTS】生命周期

页面生命周期 通常Entry修饰的组件称为页面&#xff0c;其拥有页面生命周期 onPageShow&#xff1a;页面每次显示时触发。onPageHide&#xff1a;页面每次隐藏时触发&#xff08;通常是路由跳转到其他页面了&#xff09;。onBackPress&#xff1a;当用户点击返回按钮时时触发…

【LeetCode刷题-哈希表】--187.重复的DNA序列

187.重复的DNA序列 本题就是找到长度为10的字符出现次数大于2的 子串序列 方法&#xff1a;使用哈希表 class Solution {public List<String> findRepeatedDnaSequences(String s) {List<String> ans new ArrayList<String>();HashMap<String,Integer&g…

GitLab下载地址是127.0.0.1如何修改ip

问题&#xff1a; 在下图位置之前我的ip是127.0.0.1&#xff0c;那我是如何修改的呢&#xff1f;请看下文 解决方案&#xff1a; 配置 GitLab站点 Url和端口号 GitLab 默认的配置文件路径是 /etc/gitlab/gitlab.rb # 修改配置文件 $ sudo vi /etc/gitlab/gitlab.rb 默认的站…

【lesson14】MySQL表的基本查询retrieve(读取)1

文章目录 表的基本操作介绍retrieveselect列建表基本测试 where子句建表基本测试 表的基本操作介绍 CRUD : Create(创建), Retrieve(读取)&#xff0c;Update(更新)&#xff0c;Delete&#xff08;删除&#xff09; retrieve select列 建表 基本测试 插入数据 全列查询 …

【人工智能】实验三 A*算法求解八/十五数码问题实验与基础知识

实验三 A*算法求解八数码问题实验 实验目的 熟悉和掌握启发式搜索的定义、估价函数和算法过程&#xff0c;并利用A*算法求解N数码难题&#xff0c;理解求解流程和搜索顺序。 实验内容 以8数码问题和15数码问题为例实现A*算法的求解程序&#xff08;编程语言不限&#xff09…

Redis知识详解(超详细)

1. 背景 Redis是由意大利人Antirez&#xff08;Salvatore Sanfilippo&#xff09;在2009年创造的开源内存数据结构存储系统。Redis的名字来自意大利语“Repubblica di Redis”&#xff0c;意思是“基于字典的共和国”。它是一个基于内存的键值对存储系统&#xff0c;具有快速、…

Leetcode 491 递增子序列

题意理解&#xff1a; 输入&#xff1a;nums [4,6,7,7] 输出&#xff1a;[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]] 这里不止要找一个子序列&#xff0c;还要元素保证其在原来的集合中的前后顺序&#xff0c;且应为增序。 为保证一个增序序列&#xff0c;…

移动端Vant中的Calendar日历增加显示农历(节日、节气)功能

核心&#xff1a; 使用 js-calendar-converter 库实现 npm地址&#xff1a;js-calendar-converter 内部使用原生calendar.js&#xff0c; 中国农历&#xff08;阴阳历&#xff09;和西元阳历即公历互转JavaScript库&#xff0c;具体实现感兴趣的可自行查看其实现源码。 原日…

【人工智能】实验四:遗传算法求函数最大值实验与基础知识

实验四&#xff1a;遗传算法求函数最大值实验 实验目的 熟悉和掌握遗传算法的原理、流程和编码策略&#xff0c;并利用遗传算法求解函数优化问题&#xff0c;理解求解流程并测试主要参数对结果的影响。 实验内容 采用遗传算法求解函数最大值。 实验要求 1. 用遗传算法求解…

扁平化菜单功能制作

网页效果&#xff1a; HTML部分&#xff1a; <body><ul class"nav"><li><a href"javascript:void(0);">菜单项目一</a><ul><li>子菜单项01</li><li>子菜单项02</li><li>子菜单项03<…

【C++】optional的使用(一)

这篇文章介绍下C17引入的std::optional 为什么要有 optional 一般来说&#xff0c;如果想要一个函数返回“多个”值&#xff0c;C程序员倾向于使用结构体/类完成这个操作。即定义一个通用的结构体&#xff0c;在函数内部完成装填&#xff0c;然后返回一个实例化的结构体。 #…

解决PP材质粘合问题用PP专用UV胶水

PP材料已经广泛应用于各行各业&#xff0c;在粘接中会有不同的问题需求&#xff0c;那么使用专用于PP的UV胶水可能是解决PP材质粘合问题的一种有效方法。 主要在于&#xff1a;UV胶水在紫外线照射下可以快速固化&#xff0c;形成坚固的连接。所以使用PP专用UV胶水时可以考虑&am…

oracle Job 定时任务

目录 介绍&#xff1a; 优点&#xff1a; 缺点&#xff1a; 使用场景&#xff1a; --查看定时任务 --查询存储过程 案例&#xff1a; --创建一个名为t_oracle_job的表 在创建定时任务时&#xff0c;各个参数的含义如下&#xff1a; 1. job_name&#xff1a…

day02-报表技术POI

1、基于模板导出列表数据 1.1、需求 按照以下样式导出excel 1.2、思路 首先准备一个excel模板&#xff0c;这个模板把复杂的样式和固定的内容先准备好并且放入到项目中&#xff0c;然后读取到模板后向里面放入数据。 1.3、实现 第一步&#xff1a;准备一个excel作为导出的…

【ArkTS】入门

代码结构分析 struct Index{ } 「自定义组件&#xff1a;可复用的UI单元」 xxx 「装饰器&#xff1a;用来装饰类结构、方法、变量」 Entry 标记当前组件是入口组件&#xff08;该组件可被独立访问&#xff0c;通俗来讲&#xff1a;它自己就是一个页面&#xff09;Component 用…

LeetCode-克服链表不能随机访问的问题

1.重排链表 题目描述&#xff1a; 给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0 → L1 → … → Ln - 1 → Ln 请将其重新排列后变为&#xff1a; L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只是单纯的改变节点内部的值&#xff0…