离散傅里叶变化

news2024/11/24 17:18:12

傅里叶变换

对傅里叶变换了解不是很清楚的朋友推荐一下这个帖子,讲得很详细 傅里叶变换

源码

先看源码链接

#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
 
#include <iostream>
 
using namespace cv;
using namespace std;
 
static void help(char ** argv)
{
    cout << endl
         << "This program demonstrated the use of the discrete Fourier transform (DFT). " << endl
         << "The dft of an image is taken and it's power spectrum is displayed." << endl << endl
         << "Usage:" << endl
         << argv[0] << " [image_name -- default lena.jpg]" << endl << endl;
}
 
int main(int argc, char ** argv)
{
    help(argv);

    const char* filename = argc >=2 ? argv[1] : "lena.jpg";

    // 读取灰度图像
    Mat I = imread(samples::findFile(filename), IMREAD_GRAYSCALE);
    if(I.empty()){
        cout << "Error opening image" << endl;
        return EXIT_FAILURE;
    }

    // 扩展输入图像到最优尺寸
    Mat padded; 
    int m = getOptimalDFTSize(I.rows);
    int n = getOptimalDFTSize(I.cols); 
    copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));

    // 创建包含实部和虚部的平面
    Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
    Mat complexI;
    merge(planes, 2, complexI); // 合并成一个复数矩阵

    // 进行DFT变换
    dft(complexI, complexI);

    // 计算幅度谱并转换为对数尺度
    split(complexI, planes); // 分离实部和虚部
    magnitude(planes[0], planes[1], planes[0]); // 计算幅度
    Mat magI = planes[0];

    magI += Scalar::all(1); // 转换为对数尺度
    log(magI, magI);

    // 如果频谱的行列是奇数,则进行裁剪
    magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));

    // 重新排列傅里叶图像的象限,使原点在图像中心
    int cx = magI.cols/2;
    int cy = magI.rows/2;

    Mat q0(magI, Rect(0, 0, cx, cy)); // 左上
    Mat q1(magI, Rect(cx, 0, cx, cy)); // 右上
    Mat q2(magI, Rect(0, cy, cx, cy)); // 左下
    Mat q3(magI, 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);

    // 归一化幅度图像到0-1之间
    normalize(magI, magI, 0, 1, NORM_MINMAX);

    // 显示原图像和频谱图像
    imshow("Input Image", I);
    imshow("spectrum magnitude", magI);
    waitKey();

    return EXIT_SUCCESS;
}
}

原理

傅里叶变换将图像分解为其正弦和余弦分量。这就意味着,傅里叶变换可以将复杂的图像表示为不同频率和振幅的正弦波和余弦波的组合,这些波的频率和相位描述了图像的周期性结构。低频成分对应图像中的大面积平滑区域,高频成分对应图像中的边缘和细节。
在这里插入图片描述

空间域和频域

  • f : 图像在空间域中的值,即图像像素的强度值。
  • F :图像在频域中的值,即通过傅里叶变换后的结果。

傅里叶变换结果

傅里叶变换将图像从空间域转换到频域,其结果是复数,这意味着每个频率分量有两个部分:实部和虚部。通过所保留地完整的复数信息,可以重新变回空间域。链接

显示频域图像

有两种方式来显示频域图像:

  1. 实部和虚部图像:分别显示傅里叶变换结果的实部和虚部
  2. 幅度和相位图像: 幅度图像和相位图像分别显示每个频率分量的强度和相位信息。

在图像处理中,通常情况下我们只会针对幅度图像做进一步研究,因为其包含了图像几何结构的所有信息。幅度图像显示了频率分量的强度,这与图像的结构和边缘有关。

对于数字图像来说,其经过傅里叶变换所获得的幅度图像是离散的,这也就意味着他们可以从指定的域值中取值,比如说,在基本的灰度图中,像素值通常在0到255之间,因此傅里叶变换也需要是离散类型的。

以下是一个以灰度图为输入例子的步骤详解。

Steps

  1. 将图像拓展到合适的大小
    DFT的性能主要取决于图像的大小,其会在当图像大小为2,3,5的倍数时达到最佳性能。因此,为了达到最佳性能,通常需要对图像的边界进行填充,使用 getOptimalDFTSize()copyMakeBorder() 进行拓展。
Mat padded;
int m = getOptimalDFTSize(I.rows);
int n = getOptimalDFTSize(I.cols);
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
  1. 存储复数和实数
    保留复数,可以通过复数来重新转换到空间域。
 Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
 Mat complexI;
 merge(planes, 2, complexI); // Add to the expanded another plane with zeros
  1. 做离散傅里叶变换
 dft(complexI, complexI); // this way the result may fit in the source matrix
  1. 将实值和复值转换为图像强度值
    在这里插入图片描述
    复数由实部和虚部组成,在图像处理和傅里叶变换中,复数表示在频域中的图像信息。
 split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
 magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
 Mat magI = planes[0];
  1. 切换到对数刻度
    结果是傅里叶系数的动态范围太大,无法在屏幕上显示。我们有一些小的和大的变化值我们不能像这样观察到。因此,高值将全部显示为白色点,而小的则显示为黑色点。为了使用灰度值进行可视化,我们可以将线性刻度转换为对数刻度:
    在这里插入图片描述
 magI += Scalar::all(1); // switch to logarithmic scale
 log(magI, magI);
  1. 裁剪和重新排列
    由于我们在一开始就拓展了图像,现在是时候将新引入的值去掉。为了可视化的目的,我们还可以重新排列结果的象限,使原点(0,0)与图像中心对应。
 // crop the spectrum, if it has an odd number of rows or columns
 magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
 
 // rearrange the quadrants of Fourier image so that the origin is at the image center
 int cx = magI.cols/2;
 int cy = magI.rows/2;
 
 Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
 Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
 Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left
 Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
 
 Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
 q0.copyTo(tmp);
 q3.copyTo(q0);
 tmp.copyTo(q3);
 
 q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
 q2.copyTo(q1);
 tmp.copyTo(q2);
  1. 归一化
    再次这样做是为了可视化。我们现在有了大小,但是它仍然在0到1的图像显示范围之外。我们使用**cv::normalize()**函数将值规范化到这个范围。
 normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a
 // viewable image form (float between values 0 and 1).

应用示例:确定图像中的几何方向

我们可以利用傅里叶变换来确定图像中的几何方向。例如,我们可以判断文本是否是水平的。观察一些文本时,你会注意到文本行形成了水平线,而字母形成了垂直线。这两个主要组成部分也可以在傅里叶变换的结果中看到。我们可以使用水平和旋转的文本图像来实现这一点。

水平方向
在这里插入图片描述
旋转方向
在这里插入图片描述
可以看到,频域最具影响力的分量(星等图像上最亮的点)遵循图像上物体的几何旋转。由此,我们可以计算偏移量并执行图像旋转以纠正最终的未对准。

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

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

相关文章

Yolov8可视化界面使用说明,含代码

⭐⭐ YOLOv8改进专栏|包含主干、模块、注意力机制、检测头等前沿创新 ​ ⭐⭐ YOLOv8可视化界面如下 使用需要安装opencv-python、torch、numpy及PySide6(python版本>3.9) pip install PySide6 pip install numpy pip install opencv-python 使用说明 运行下方代码&#xf…

《software architecture patterns》学习笔记

了解通用的架构模式并知道什么时候使用它们。 软件架构定义了软件的基本特点和行为。比如&#xff0c;有些软件架构会让软件变得可扩展&#xff0c;而有些软件架构会让软件变得易于修改。 知道每一种软件架构的特点、优缺点是非常有必要的&#xff0c;因为它们能帮助你选择一种…

LeetCode刷题之HOT100之课程表

吃完普通的食堂饭菜&#xff0c;回到实验室&#xff0c;继续做一道题&#xff01; 1、题目描述 2、逻辑分析 这道题涉及到图相关知识&#xff0c;应用到了拓扑排序。 题意解释 一共有 n 门课要上&#xff0c;编号为 0 ~ n-1。先决条件 [1, 0]&#xff0c;意思是必须先上课 0…

湖北大学2024年成人高考函授报名专升本法学专业介绍

湖北大学&#xff0c;这所承载着深厚文化底蕴和学术积淀的高等学府&#xff0c;始终致力于为广大有志之士提供多元化的学习机会。在时代的浪潮中&#xff0c;为了满足社会对于高层次法律人才的需求&#xff0c;湖北大学特别推出了成人高等继续教育项目&#xff0c;为广大在职人…

双击跳转到 BP 事务代码 CALL TRANSACTION BP AND SKIP FIRST SCREEN

BP 维护业务伙伴 BP事务代码不能像普通的VA03 这样跳转&#xff0c;下面介绍3种方法。 1. 单纯跳转到BP FORM FRM_SHOW_BP USING LV_BP TYPE BU_PARTNER.CALL METHOD CL_RMPS_ADDRESS>SHOW_BPEXPORTINGIM_BP_NUMBER LV_BP. ENDFORM. 2. 带业务伙伴角色跳转到BP&#xff…

NAND闪存巨头铠侠(Kioxia)计划最迟于10月下旬通过首次公开募股IPO

据路透社于6月26日引用消息来源的报道&#xff0c;在半导体市场条件反弹及财务业绩迅速改善的背景下&#xff0c;NAND闪存巨头铠侠&#xff08;Kioxia&#xff09;正准备尽快提交初步申请&#xff0c;并计划最迟于10月下旬通过首次公开募股&#xff08;IPO&#xff09;在东京证…

Kubernetes之Scheduler详解

本文尝试从Kubernetes Scheduler的功能介绍、交互逻辑、伪代码实现、最佳实践、自定义Scheduler举例及其历史演进6个方面进行详细阐述。希望对您有所帮助&#xff01; 一、Kubernetes Scheduler 功能 Kubernetes Scheduler 是 Kubernetes 集群的核心组件之一&#xff0c;负责…

使用jupyter打开本地ipynb文件的方法

常用方法&#xff1a; 先启动jupyter&#xff0c;然后在打开的页面点击upload&#xff0c;选择想要打开的文件上传然后打开&#xff0c;但是这样其实是先复制了一份到jupyter中&#xff0c;然后打开运行。而我不想复制。 方法二 先打开项目文件所在文件夹&#xff0c;文件夹…

M芯片 Parallels Desktop 19虚拟机安装Windows11教程

Parallels Desktop 19 for Mac 乃是一款适配于 Mac 的虚拟化软件。它能让您在 Mac 计算机上同时运行多个操作系统。您可借此创建虚拟机&#xff0c;并于其中装设不同的操作系统&#xff0c;如 Windows、Linux 或 macOS。使用 Parallels Desktop 19 mac 版时&#xff0c;您可在 …

情感分析方法与实践

第1关&#xff1a;情感分析的基本方法 情感分析简介 情感分析&#xff0c;又称意见挖掘、倾向性分析等。简单而言&#xff0c;是对带有情感色彩的主观性文本进行分析、处理、归纳和推理的过程。在日常生活中&#xff0c;情感分析的应用非常普遍&#xff0c;下面列举几种常见的…

数据库和程序 按照层级进行排序

文章目录 先上效果图(四种方式实现)前期工作创建表添加表数据 第一种方式: 具体执行SQL更深层次的sql案例 第二种方式: 使用java程序动态的生成SQL进行执行单元测试注意事项 第三种方式: 使用java程序进行排序[单字段排序]第四种方式: 使用lambda方式进行排序[多字段排序]最后的…

一个最简单的MySQL事务模拟测试

这里只是简单写了一个转账的小事务&#xff0c;模拟一下事务的过程 代码&#xff1a; 初始数据&#xff1a; 当你关闭自动提交 并且开启一个事务执行了下面的更新语句 但是没有提交时&#xff1a; 此时虽然你运行查询语句会发现他的值发生了变化 &#xff0c;但是当你运行回滚…

数据结构速成--树和二叉树

由于是速成专题&#xff0c;因此内容不会十分全面&#xff0c;只会涵盖考试重点&#xff0c;各学校课程要求不同 &#xff0c;大家可以按照考纲复习&#xff0c;不全面的内容&#xff0c;可以看一下小编主页数据结构初阶的内容&#xff0c;找到对应专题详细学习一下。 气死了…

C++ ─── vector模拟实现的扩容拷贝问题

扩容拷贝问题 源代码使用memcpy拷贝&#xff0c;在使用vector<int>存储内置类型时没有问题&#xff0c; 但是如果存储的是含有指针的类型&#xff0c;如string&#xff0c;就会发生浅拷贝问题 //3、容量相关void reserve(size_t n){if (n > capacity()){size_t old_si…

【PWN · ret2libc | protobuf】[2024CISCN · 华中赛区]protoverflow

套了一层protobuf壳&#xff0c;然后就是简单的ret2libc 参考速递&#xff1a;深入二进制安全&#xff1a;全面解析Protobuf-CSDN博客 前言 第一次遇到protobuf&#xff0c;如果没有了解过&#xff0c;是显然做不出来的。此次复现&#xff0c;也算是点亮了一个技能点 一、什么…

Linux系统编程(七)进程间通信IPC

进程间通讯的7种方式_进程间通信的几种方法-CSDN博客 管道 pipe&#xff08;命名管道和匿名管道&#xff09;&#xff1b;信号 signal&#xff1b;共享内存&#xff1b;消息队列&#xff1b;信号量 semaphore&#xff1b;套接字 socket&#xff1b; 1. 管道 内核提供&#x…

Halcon 如何根据特征过滤区域和XLD

一 如何跟进特征过滤区域和XLD dev_open_window(0,0,512,512,black,WindowHandle)read_image(Image,fabrik)threshold(Image,Region,128,255)connection(Region,ConnectedRegions)*根据面积范围[8000,9000] dev_display(Image)select_shape(ConnectedRegions,SelectedRegions,…

Python和tkinter实现的字母记忆配对游戏

Python和tkinter实现的字母记忆配对游戏 因为这个小游戏用到了tkinter&#xff0c;先简要介绍一下它。tkinter是Python的标准GUI(图形用户界面)库&#xff0c;它提供了一种简单而强大的方式来创建图形界面应用程序。它提供了创建基本图形界面所需的所有工具&#xff0c;同时保…

【Pillow】module ‘PIL.Image‘ has no attribute ‘ANTIALIAS‘问题解决

问题描述 我在使用 SummaryWriter 记录图片数据日志时&#xff0c;遇到了报错&#xff0c;如下图所示&#xff1a; 问题的原因在于&#xff0c;使用的pillow版本已经舍弃了ANTIALIAS&#xff0c;在新版本中已经改为了LANCZOS 问题解决 两种解决方式&#xff1a; 修改源码更…

C++之STL(十一)

1、迭代器适配器 2、插入迭代器 #include <iostream> #include <vector> #include <algorithm> #include <list> using namespace std;void showVec(const vector<int>& v) {for (vector<int>::const_iterator it v.begin(); it ! v.…