离散傅里叶变换

2023-05-16

英文链接:Discrete Fourier Transform

目标

  • 什么是傅里叶变换,为什么要用它?
  • 在OpenCV中怎么做?
  • 使用诸如:copyMakeBorder()merge()dft()、getOptimalDFTSize()、log()normalize()等函数。

源码

#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;                            //expand input image to optimal size
    int m = getOptimalDFTSize( I.rows );
    int n = getOptimalDFTSize( I.cols ); // on the border add zero values
    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);         // Add to the expanded another plane with zeros
    dft(complexI, complexI);            // this way the result may fit in the source matrix
    // compute the magnitude and switch to logarithmic scale
    // => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
    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];
    magI += Scalar::all(1);                    // switch to logarithmic scale
    log(magI, magI);
    // 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);
    normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a
                                            // viewable image form (float between values 0 and 1).
    imshow("Input Image"       , I   );    // Show the result
    imshow("spectrum magnitude", magI);
    waitKey();
    return EXIT_SUCCESS;
}

解释

傅里叶变换将图像分解成正弦分量和余弦分量。换句话说,它将图像从空间域转换到频率域。它的思想是:任何函数都可以被精确地近似为无限个正弦函数和余弦函数的和。傅里叶变换就是一种方法。二维图像傅里叶变换在数学上为:
F ( k , l ) = ∑ i = 0 N − 1 ∑ j = 0 N − 1 f ( i , j ) e − i 2 π ( k i N + l j N ) F(k,l) = \displaystyle\sum\limits_{i=0}^{N-1}\sum\limits_{j=0}^{N-1} f(i,j)e^{-i2\pi(\frac{ki}{N}+\frac{lj}{N})} F(k,l)=i=0N1j=0N1f(i,j)ei2π(Nki+Nlj)
e i x = cos ⁡ x + i sin ⁡ x e^{ix} = \cos{x} + i\sin {x} eix=cosx+isinx
这里 f f f是空间域的图像值, F F F是频率域的值。转换后的结果是复数。可以通过 实域和复域幅域和相域来显示。然而,在整个图像处理算法中,只有幅值图像(即幅域)是有趣的,因为它包含了我们需要的关于图像几何结构的所有信息。然而,如果你打算对这种形式的图像做一些修改,然后再重新逆变换它。那么你需要保留这两种形式的图像。

在这个例子中,我将展示如何计算和展示傅里叶变换的幅值图像。在数字情况下图像是离散的。这意味着它们可以从给定的域值中获取值。例如,基本灰度图像的值通常在0到255之间。因此傅里叶变换也需要是离散型的,从而得到离散傅里叶变换(DFT)。当您需要从几何角度确定图像的结构时,您将希望使用此方法。以下是接下来的步骤(对于灰度输入图像 I I I):

将图像扩展到最佳大小

DFT的性能取决于图像的大小。当图像大小是数字2、3和5的倍数时,它往往是最快的。为了获得最大的性能,通常将边界值填充到图像中,得到具有这些尺寸特征的的图像。getOptimalDFTSize()返回这个最佳大小,我们可以使用copyMakeBorder()函数来扩展图像的边界(附加的像素被初始化为零)

    Mat padded;                            //expand input image to optimal size
    int m = getOptimalDFTSize( I.rows );
    int n = getOptimalDFTSize( I.cols ); // on the border add zero values
    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);         // Add to the expanded another plane with zeros

进行离散傅里叶变换

有可能就地进行计算(输入对象即输出对象):

    dft(complexI, complexI);            // this way the result may fit in the source matrix

实域和复域 的值 转换到 幅域

一个复数有实部(Re)和复部(虚部- Im)。DFT的结果是复数。DFT的 幅值部分 为:
M = R e ( D F T ( I ) ) 2 + I m ( D F T ( I ) ) 2 2 M = \sqrt[2]{ {Re(DFT(I))}^2 + {Im(DFT(I))}^2} M=2Re(DFT(I))2+Im(DFT(I))2
翻译成OpenCV代码:

    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];

切换到对数尺度

结果表明,傅里叶系数的动态范围太大,无法在屏幕上显示。我们无法观察到较低和较高的变量。因此,高值将全部变成白点,而低值将变成黑色。要将灰度值用于可视化,我们可以将线性比例转换为对数比例:
M 1 = log ⁡ ( 1 + M ) M_1 = \log{(1 + M)} M1=log(1+M)
翻译成OpenCV代码:

    magI += Scalar::all(1);                    // switch to logarithmic scale
    log(magI, magI);

裁剪和重新布局

在第一步,我们扩展了图像。现在是时候抛弃新引入的值了。
为了可视化的目的,我们还可以重新排列结果的象限,以便原点(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);

标准化

这也是为了可视化的目的。我们现在有幅值,但这仍然超出我们的图像显示范围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).

结果

一个应用的想法是确定图像中呈现的几何方向。例如,让我们看看一个文本是否水平? 看一些文本,你会发现文本行也有水平线,字母也有垂直线。在傅立叶变换的情况下,文本片段的这两个主要部分也可以看到。让我们用这个水平和旋转的图像来表示文本。

如果是水平文本:
在这里插入图片描述

如果是旋转文本:
在这里插入图片描述
您可以看到,频域最有影响力的分量(幅值图像上最亮的点)跟随图像上对象的几何旋转。由此,我们可以计算偏移量,并执行图像旋转来纠正错过的对准。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

离散傅里叶变换 的相关文章

  • 面试题21:调整数组顺序使奇数位于偶数前面

    题目 xff1a 输入一个整数数组 xff0c 实现一个函数来调整该数组中数字的顺序 xff0c 使得所有奇数位于数组的前半部分 xff0c 所有偶数位于数组的后半部分 I O描述 span class token keyword void
  • 单片机控制12864液晶显示屏静态显示(含仿真文件和代码)

    12864简介 xff1a 点我点我点我查看12864简介 显示屏幕说明图 xff1a 仿真电路图 xff1a 仿真结果展示 xff1a 汉字取模软件设置 xff1a xff08 取模软件下载链接在文章结尾 xff09 C语言程序代码 xf
  • 面试题22:链表中倒数第k个结点

    题目 xff1a 输入一个链表 xff0c 输出该链表中倒数第k个结点 为了符合大多数人的习惯 xff0c 本题从1开始计数 xff0c 即链表的尾结点是倒数第1个结点 例如一个链表有6个结点 xff0c 从头结点开始它们的值依次是1 2
  • 面试题23:链表中环的入口结点

    题目 xff1a 一个链表中包含环 xff0c 如何找出环的入口结点 xff1f 例如 xff0c 在图3 8的链表中 xff0c 环的入口结点是结点3 代码 span class token macro property span cla
  • 面试题24:反转链表

    题目 xff1a 定义一个函数 xff0c 输入一个链表的头结点 xff0c 反转该链表并输出反转后链表的头结点 code span class token macro property span class token directive
  • 面试题25:合并两个排序的链表

    题目 输入两个递增排序的链表 xff0c 合并这两个链表并使新链表中的结点仍然是按照递增排序的 例如输入图3 11中的链表1和链表2 xff0c 则合并之后的升序链表如链表3所示 代码 span class token macro prop
  • 面试题26:树的子结构

    题目 xff1a 输入两棵二叉树A和B xff0c 判断B是不是A的子结构 代码 span class token macro property span class token directive keyword include span
  • 面试题27:二叉树的镜像

    题目 xff1a 请完成一个函数 xff0c 输入一个二叉树 xff0c 该函数输出它的镜像 代码 span class token macro property span class token directive keyword inc
  • 面试题28:对称的二叉树

    题目 xff1a 请实现一个函数 xff0c 用来判断一棵二叉树是不是对称的 如果一棵二叉树和它的镜像一样 xff0c 那么它是对称的 代码 span class token macro property span class token
  • 面试题29:顺时针打印矩阵

    题目 xff1a 输入一个矩阵 xff0c 按照从外向里以顺时针的顺序依次打印出每一个数字 Code span class token macro property span class token directive keyword in
  • 看中科院大牛博士如何进行文献检索和阅读

    大家可以看看 xff0c 从其他地方看到的 xff01 导读 我是学自然科学的 xff0c 平时确实需要不少外文文献 xff0c 对于自然科学来讲英文文献检索首推Elsevier xff0c Springer等 虽然这些数据库里面文献已经不
  • 面试题32:从上往下打印二叉树

    面试题32 xff08 一 xff09 xff1a 不分行 题目 xff1a 从上往下打印出二叉树的每个结点 xff0c 同一层的结点按照从左到右的顺序打印 代码 span class token macro property span c
  • 面试题33:二叉搜索树的后序遍历序列

    题目 输入一个整数数组 xff0c 判断该数组是不是某二叉搜索树的后序遍历的结果 如果是则返回true xff0c 否则返回false 假设输入的数组的任意两个数字都互不相同 代码 span class token macro proper
  • 面试题34:二叉树中和为某一值的路径

    题目 输入一棵二叉树和一个整数 xff0c 打印出二叉树中结点值的和为输入整数的所有路径 从树的根结点开始往下一直到叶结点所经过的结点形成一条路径 代码 span class token macro property span class
  • 面试题35:复杂链表的复制

    题目 请实现函数ComplexListNode Clone ComplexListNode pHead xff0c 复 制一个复杂链表 在复杂链表中 xff0c 每个结点除了有一个m pNext指针指向下一个结点外 xff0c 还有一个m
  • 面试题36:二叉搜索树与双向链表

    题目 输入一棵二叉搜索树 xff0c 将该二叉搜索树转换成一个排序的双向链表 要求不能创建任何新的结点 xff0c 只能调整树中结点指针的指向 代码 span class token macro property span class to
  • 面试题37:序列化二叉树

    题目 请实现两个函数 xff0c 分别用来序列化和反序列化二叉树 代码 span class token macro property span class token directive keyword include span span
  • 面试题38:字符串的排列

    文章目录 字符串的排列扩展 子集扩展 组合扩展 排列 字符串的排列 题目 输入一个字符串 xff0c 打印出该字符串中字符的所有排列 例如输入字符串abc xff0c 则打印出由字符a b c所能排列出来的所有字符串abc acb bac
  • 面试题39:数组中出现次数超过一半的数字

    题目 数组中有一个数字出现的次数超过数组长度的一半 xff0c 请找出这个数字 例如输入一个长度为9的数组 1 2 3 2 2 2 5 4 2 由于数字2在数组中 出现了5次 xff0c 超过数组长度的一半 xff0c 因此输出2 I O
  • 面试题40:最小的k个数

    题目 输入n个整数 xff0c 找出其中最小的k个数 例如输入4 5 1 6 2 7 3 8这8个数字 xff0c 则最小的4个数字是1 2 3 4 code 方法一 xff1a 时间复杂度为O n 的算法 xff0c 只有当我们可以修改输

随机推荐

  • 面试题41:数据流中的中位数

    题目 如何得到一个数据流中的中位数 xff1f 如果从数据流中读出奇数个数值 xff0c 那么中位数就是所有数值排序之后位于中间的数值 如果从数据流中读出偶数个数值 xff0c 那么中位数就是所有数值排序之后中间两个数的平均值 I O nu
  • 如何由Xubuntu桌面系统还原至Ubuntu系统?

    假定读者原来的系统为ubuntu桌面系统 xff0c 并且根据如下命令更换到xubuntu桌面系统 xff1a sudo apt get install xrdp sudo apt get install vnc4server sudo a
  • 神经网络拟合函数表达式,神经网络拟合函数matlab

    1 matlab中如何用神经网络求得数据拟合函数 xff1f 我是做这个方向的 xff0c 神经网络拟合出的曲线是没有相应的函数的 xff0c 他是根据许多的权重值 xff0c 阀值和偏置值的训练确定的曲线 还有什么相关问题可以问我 xff
  • 面试题42:连续子数组的最大和

    题目 输入一个整型数组 xff0c 数组里有正数也有负数 数组中一个或连续的多个整数组成一个子数组 求所有子数组的和的最大值 要求时间复杂度为O n code 解法一 xff1a 暴力法 span class token macro pro
  • 面试题43:从1到n整数中1出现的次数

    题目 输入一个整数n xff0c 求从1到n这n个整数的十进制表示中1出现的次数 例如 输入12 xff0c 从1到12这些整数中包含1 的数字有1 xff0c 10 xff0c 11和12 xff0c 1一共出现了5次 code span
  • 面试题:数组中找出两个单数

    题目 一个数组中除了两个数是单个的 xff0c 其他的数都有两个 xff0c 请找出这两个单个的数 code span class token macro property span class token directive keywor
  • 面试题44:数字序列中某一位的数字

    题目 数字以0123456789101112131415 的格式序列化到一个字符序列中 在这个序列中 xff0c 第5位 xff08 从0开始计数 xff09 是5 xff0c 第13位是1 xff0c 第19位是4 xff0c 等等 请写
  • mvIMPACT 相机 SDK C++

    Overview 这是为想要使用mvIMPACT Acquire的c 43 43 接口的开发人员编写的文档 它基于C接口 xff0c 但是提供了一种更方便的面向对象的方法来处理设备驱动程序提供的属性和函数 SDK mvIMPACT xff0
  • 面试题:两个链表结构的数据相加,保存到新链表。

    使用STL span class token macro property span class token directive keyword include span span class token string lt iostrea
  • 软件建模基础

    摘录自某PPT 文章目录 软件建模基础0 软件质量属性0 1 如何评价代码质量0 2 软件质量属性 1 面向对象1 0 面向对象知识点1 1 面向对象四大特性1 1 xff08 封装 xff09 1 1 xff08 抽象 xff09 1 1
  • 如何扫描图像,查找表 和 用OpenCV进行时间测量

    英文版原文链接 xff1a How to scan images lookup tables and time measurement with OpenCV 文章目录 目标测试用例图像矩阵如何存储在内存之中 遍历方式一 xff1a 高效的
  • DDD开发

    内容来自某PPT 文章目录 DDD开发1 领域 限定上下文 实体 值对象1 1 领域 子域1 2 核心域 通用域 支撑域1 3 通用语言1 4 限界上下文 xff1a 定义领域边界的利器1 5 实体1 6 值对象1 7 实体 VS 值对象
  • 嵌入式软件开发工程师求职要求

    文章目录 他人感悟工作职责任职要求嵌入式软件开发涉及的知识点很多 xff0c 我仅简单说一下 xff1a 他人感悟 一线工程师告诉你嵌入式真实现状与发展前景 当我们谈论嵌入式时我们究竟在谈什么 工作职责 负责硬件平台bring up xff
  • kolla-ansible openstack登录 证书不可用

    根据官方文档配置kolla ansible之后 xff0c 创建openstack实例 xff0c 登录openstack出现证书不可用 xff0c 如图 问题排查 尝试过 更新openrc sh文件增加OS TOKEN环境变量 查看日志
  • 联发科2021笔试题:字符串中找到 出现次数 最多的单个字符

    I O描述 输入 xff1a span class token string 34 aaaaabbbbbBBBBBAAAAA 34 span 输出 xff1a A span class token punctuation span span
  • 对矩阵的 掩码运算

    英文链接 xff1a Mask operations on matrices 文章目录 测试用例代码基本函数二维滤波器函数 矩阵的掩码操作非常简单 其思想是我们根据掩码矩阵 也称为内核 重新计算图像中每个像素的值 此掩码保存的值将调整相邻像
  • 对图片的操作

    英文原文链接 xff1a Operations with images 文章目录 输入 输出图像的基本操作内存管理和引用计数基本操作可视化图像 输入 输出 从文件加载一个图像 Mat img span class token operato
  • 使用OpenCV相加(混合)两个图像

    使用OpenCV相加 混合 两个图像 xff1a Adding blending two images using OpenCV 文章目录 目标理论源码解释结果 目标 什么是线性混合 xff0c 为什么它有用 如何使用addWeighted
  • 改变图像的对比度和亮度

    英文链接 xff1a Changing the contrast and brightness of an image 文章目录 目标理论图像处理像素处理亮度和对比度调整 源码解释结果实例亮度和对比度调整图像灰度校正 xff08 Gamma
  • 离散傅里叶变换

    英文链接 xff1a Discrete Fourier Transform 目标 什么是傅里叶变换 xff0c 为什么要用它 在OpenCV中怎么做 使用诸如 copyMakeBorder merge dft getOptimalDFTSi