opencv学习笔记24-opencv图像的傅里叶变换(幅值谱)

一、函数:

a.getOptimalDFTSize 函数:

(1)函数原型:

CV_EXPORTS_W int getOptimalDFTSize(int vecsize);

(2)函数作用:

  • getOptimalDFTSize 函数用于计算给定向量大小的最优离散傅里叶变换(DFT)尺寸。由于 DFT 的性能并不是向量尺寸的单调函数,因此通常在计算两个数组的卷积或对数组进行频谱分析时,将输入数据用零填充到略大的尺寸,这样可以更快地进行变换。

(3)参数:

  • vecsize:输入参数,表示向量的大小。

(4)返回值:

  • 函数返回的 N 是大于或等于 vecsize 的最小数,使得大小为 N 的向量可以高效地进行 DFT 变换。在当前实现中,N 的形式为 N=2p×3q×5rN=2p×3q×5r,其中 p、q、r 是一些整数。

(5)特殊情况:

  • 如果 vecsize 太大(非常接近 INT_MAX),函数返回一个负数。

(6)DCT 变换:

  • 虽然该函数不能直接用于估计离散余弦变换(DCT)的最优向量大小(因为当前的 DCT 实现仅支持偶数大小的向量),但可以通过 getOptimalDFTSize((vecsize+1)/2)*2 来间接处理。

(7)优化原理:

  • 数组大小为 2 的幂(2、4、8、16、32,...)时处理速度最快。此外,大小为 2、3 和 5 的乘积的数组(例如 300 = 5 × 5 × 3 × 2 × 2)也被相当高效地处理。

(8)相关函数:

  • dft:执行离散傅里叶变换。
  • dct:执行离散余弦变换。
  • idft:执行逆离散傅里叶变换。
  • idct:执行逆离散余弦变换。
  • mulSpectrums:用于乘以两个相似大小的 DFT 或 DCT 变换结果。
b. copyMakeBorder 函数:

(1)函数原型:

CV_EXPORTS_W void copyMakeBorder(InputArray src, OutputArray dst, int top,
                         int bottom, int left, int right, int borderType,
                         const Scalar& value = Scalar() );

(2)函数作用:

  • copyMakeBorder 函数在图像周围形成边框。它将源图像 src 复制到目标图像 dst 的中间,并用外推(extrapolated)像素填充图像的左侧、右侧、上方和下方。

 (3)参数:

  • src:源图像。
  • dst:目标图像,其类型与 src 相同,尺寸为 Size(src.cols+left+right, src.rows+top+bottom)
  • topbottomleftright:分别指定在源图像矩形的每个方向上外推多少像素来构建边框。
  • borderType:边框类型,用于指定如何外推像素。不同的外推方法在 borderInterpolate 中有详细说明。
  • value:当 borderType 为 BORDER_CONSTANT 时,用于指定边框的值,默认为 Scalar()

 (4)特殊模式:

  • src 已经是 dst 中间的一部分时,函数不会复制 src 本身,而只是构建边框。这可以通过不复制数据而选择 dst 中与 src 大小相同的中间部分来实现。

(5)注意点:

  • 如果源图像是更大图像的感兴趣区域(ROI),copyMakeBorder 会尝试使用 ROI 外的像素来形成边框。要禁用此功能并始终进行外推(就好像 src 不是 ROI),可以在 borderType 中使用 BORDER_ISOLATED
c. merge 函数(重载):

(1)函数原型:

CV_EXPORTS_W void merge(InputArrayOfArrays mv, OutputArray dst);

(2)函数作用:

  • merge 函数用于将多个单一通道的矩阵合并成一个多通道的矩阵。这在处理多光谱图像或进行图像分析时非常有用,例如在傅里叶变换后合并实部和虚部矩阵。

(3)参数:

  • mv:输入参数,是一个由多个矩阵组成的向量,这些矩阵必须具有相同的大小和相同的数据类型(depth)。
  • dst:输出参数,是一个数组,其大小与 mv[0] 相同,数据类型也与 mv[0] 相同;输出数组的通道数将等于输入矩阵数组中所有矩阵的通道数之和。

(4)合并规则:

  • merge 函数会按照 mv 中矩阵的顺序,将它们一个接一个地合并到输出矩阵 dst 中。第一个矩阵成为输出矩阵的第一个通道,第二个矩阵成为第二个通道,依此类推。

(5)使用场景:

  • 合并多个图像的通道,例如将 RGB 三个单通道图像合并为一个三通道的彩色图像。
  • 在频域分析中,合并傅里叶变换后的实部和虚部,以便进行进一步处理。
 d.dft 函数:

(1)函数原型:

CV_EXPORTS_W void dft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0);

(2)函数作用:

dft 函数执行以下操作之一:

  • 一维前向变换:对 N 个元素的一维向量进行前向傅里叶变换。
  • 一维逆变换:对 N 个元素的一维向量进行逆傅里叶变换。
  • 二维前向变换:对 MxN 矩阵进行二维前向傅里叶变换。
  • 二维逆变换:对 MxN 矩阵进行二维逆傅里叶变换。

(3)参数说明:

  • src:输入数组,可以是实数或复数。
  • dst:输出数组,其大小和类型取决于变换的类型和标志。
  • flags:变换标志,表示一个组合的 DftFlags。这些标志定义了变换的类型(前向或逆)和输出的格式(实数或复数)。
  • nonzeroRows:当此参数非零时,函数假定输入数组(未设置 DFT_INVERSE)的前 nonzeroRows 行或输出数组(设置了 DFT_INVERSE)的前 nonzeroRows 行包含非零值,这可以提高处理效率。

(4)变换模式:

  • 如果设置了 DFT_ROWS 标志或输入数组只有一行或一列,则函数对矩阵的每一行执行一维变换。
  • 如果输入数组是实数且未设置 DFT_INVERSE,则执行前向变换,输出可以是复数矩阵或使用 CCS(complex-conjugate-symmetrical)格式的实数矩阵。
  • 如果输入数组是复数且未设置 DFT_INVERSE 或 DFT_REAL_OUTPUT,则执行前向变换,输出是相同大小的复数数组。
  • 如果设置了 DFT_INVERSE 且输入数组是实数或设置了 DFT_REAL_OUTPUT,则执行逆变换,输出是相同大小的实数数组。

(5)变换效率:

  • 变换仅支持那些可以分解为小质数(当前实现中为 2、3 和 5)乘积的数组大小。使用 getOptimalDFTSize 方法可以计算出高效的 DFT 大小。

(6)注意事项:

  • 函数支持任意大小的数组,但只有当数组大小可以分解为小质数时,变换才更高效。
  • 提供了一些优化方法,例如只清零必要的部分,使用 DFT 卷积的块处理,以及多线程处理。
  • 使用 matchTemplate 和 filter2D 函数可以比理论上的最优实现获得更好的性能。

(7)相关函数:

  • dct:执行离散余弦变换。
  • getOptimalDFTSize:计算最优的 DFT 大小。
  • mulSpectrums:乘以两个相似大小的 DFT 或 DCT 变换结果。
  • filter2D:对图像应用自定义的线性滤波器。
  • matchTemplate:计算模板与图像的匹配。
  • flip:翻转图像。
  • cartToPolar:将直角坐标系中的点转换为极坐标系。
  • magnitude:计算复数的幅度。
  • phase:计算复数的相位。
e.split 函数:

(1)函数原型:

CV_EXPORTS void split(const Mat& src, Mat* mvbegin); 

(2)函数作用:

  • split 函数将一个多通道数组(例如,一个三通道的彩色图像)分割成几个单独的单通道数组(例如,三个灰度图像,每个分别代表红色、绿色和蓝色通道)。

(3)公式描述:

其中, 是输出数组向量中的第 c 个数组,是输入数组在位置 I 的第 c 个通道。

(4)参数:

  • src:输入的多通道数组。
  • mvbegin:输出数组的指针,指向第一个输出数组。输出数组的数量必须与 src.channels() 相匹配,即输入数组的通道数。如果需要,输出数组将被重新分配。

(5)相关函数:

  • merge:与 split 功能相反,它将多个单通道数组合并成一个多通道数组。
  • mixChannels:用于更复杂的通道排列,可以提取单个通道或进行通道的重新排列。
  • cvtColor:用于执行颜色转换,例如从 RGB 转换到灰度图像。
f.magnitude 函数:

 (1)函数原型:

CV_EXPORTS_W void magnitude(InputArray x, InputArray y, OutputArray magnitude);

(2)函数作用:

  • magnitude 函数计算由 xy 数组对应的元素形成的二维向量的大小。向量的大小定义为向量两个分量平方和的平方根。

(3)公式描述:

  • 其中,dst 是输出的向量大小,x(I)y(I) 是在位置 Ixy 分量的值。

(4)参数:

  • x:浮点型数组,包含向量的 x-坐标。
  • y:浮点型数组,包含向量的 y-坐标。它必须与 x 有相同的大小。
  • magnitude:输出数组,其大小和类型与 x 相同,包含计算出的向量大小。

 (5)相关函数:

  • cartToPolar:将笛卡尔坐标系中的点转换为极坐标系,需要幅度和角度。
  • polarToCart:将极坐标系中的点转换回笛卡尔坐标系。
  • phase:计算复数的相位角。
  • sqrt:计算平方根,magnitude 函数内部使用此函数来计算向量的大小。

(6)使用场景:

  • magnitude 函数常用于图像处理中的梯度计算,特别是在计算图像的幅度谱时,其中 xy 分量可以来自图像的梯度或其它二维变换的结果。

 g.log 函数:

 (1)函数原型:

CV_EXPORTS_W void log(InputArray src, OutputArray dst);

(2)函数作用:

  • log 函数计算输入数组 src 中每个元素的自然对数(底数为 e),并将结果存储在输出数组 dst 中。

(3)公式描述:

  • 其中,dst(I) 是输出数组在位置 I 的元素,src(I) 是输入数组在位置 I 的元素。

(4)参数:

  • src:输入数组,其元素的自然对数将被计算。
  • dst:输出数组,其大小和类型与 src 相同,包含计算出的自然对数值。

(5)注意事项:

  • 对零、负数以及特殊值(如 NaN、Inf)计算对数是未定义的。在实际应用中,这意味着如果输入数组包含这些值,函数的行为是不确定的,可能会导致错误或不准确的结果。

(6)使用场景:

  • log 函数常用于需要对数据进行对数变换的场景,例如在图像处理中的对比度增强、信号处理中的幅度压缩,或者在数学和统计分析中处理指数分布的数据。

 (5)相关函数:

  • exp:计算每个元素的指数(自然对数的逆运算)。
  • cartToPolar 和 polarToCart:在笛卡尔坐标系和极坐标系之间转换点。
  • phase:计算复数的相位角,通常与 log 函数结合使用进行相位unwrapping。
  • pow:计算每个元素的幂次。
  • sqrt:计算每个元素的平方根。
  • magnitude:计算二维向量的大小,通常使用 sqrt 函数作为内部计算步骤。
 h.normalize 函数:

(1)函数原型:

CV_EXPORTS_W void normalize( InputArray src, InputOutputArray dst, double alpha = 1, 
double beta = 0, int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray());

 (2)函数作用:

  • normalize 函数对输入数组 src 中的元素进行缩放和平移,使得:
  • 当 normType 为 NORM_INFNORM_L1 或 NORM_L2 时,分别使得 dst 的 L_p 范数等于 alpha
  • 当 normType 为 NORM_MINMAX 时,使得 dst 的最小值为 alpha,最大值为 beta

(3)参数:

  • src:输入数组。
  • dst:输出数组,其大小与 src 相同。
  • alpha:归一化的目标范数值或在范围归一化情况下的下限范围边界。
  • beta:在范围归一化情况下的上限范围边界;在范数归一化中不使用。
  • norm_type:归一化类型,可参见 cv::NormTypes
  • dtype:当为负数时,输出数组的数据类型与 src 相同;否则,输出数组具有与 src 相同的通道数,数据深度为 CV_MAT_DEPTH(dtype)
  • mask:可选的操作掩码。

(4)范数归一化:

  • 在范数归一化中,数组的范数被缩放到 alpha 的值。范数可以是无穷大范数(NORM_INF)、L1 范数(NORM_L1)或 L2 范数(NORM_L2)。

(5)范围归一化:

  • 在范围归一化中(NORM_MINMAX),数组的值被缩放到 [alpha, beta] 范围内。

(6)稀疏矩阵处理:

  • 对于稀疏矩阵,只有非零值被分析和转换。因此,不允许对稀疏矩阵进行范围变换,因为这可能会改变零点的位置。

(7)相关函数:

  • norm:计算数组的范数。
  • Mat::convertTo:将矩阵转换为不同的数据类型,同时可以进行归一化。
  • SparseMat::convertTo:稀疏矩阵的转换函数,类似于 Mat::convertTo

二、代码示例:

#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>           
#include <opencv2/videoio.hpp>       
#include <opencv2/objdetect.hpp>        
#include <opencv2/highgui/highgui_c.h>  
#include <iostream>                     

using namespace cv;                    
using namespace std;      

 int calcVisibalMag(Mat srcMat, Mat& dstMat);

 // 主函数
 int main() {
     // 设置日志级别为不输出任何日志信息
     utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
     // 读取灰度图像
     Mat srcMat = imread("C:\\Users\\86173\\Desktop\\TI\\Q版阿离.png", 0);
     // 用于存储傅里叶变换后的可视化幅值谱
     Mat magMat;
     Mat magMat_image;

     // 检查图像是否成功读取
     if (srcMat.empty()) {
         cout << "failed to read image!:" << endl;
         return -1;
     }

     // 调用函数计算可视化的傅里叶变换幅值谱
     calcVisibalMag(srcMat, magMat_image);

     // 显示原始图像和傅里叶变换后的幅值谱
     imshow("原始图像", srcMat);
     imshow("幅度谱", magMat_image);

     // 等待用户按键
     waitKey(0);
     return 0;
 }

 // 函数用于计算图像的可视化傅里叶变换幅值谱
 int calcVisibalMag(Mat srcMat, Mat& dstMat) {
     // 检查输入图像是否为空
     if (srcMat.empty()) {
         cout << "failed to read image!" << endl;
         return -1;
     }

     // 用于存储边缘填充后的图像
     Mat Edge;

     // 获取最优的DFT尺寸
     int DFTrows = getOptimalDFTSize(srcMat.rows);
     int DFTcols = getOptimalDFTSize(srcMat.cols);

     // 对图像进行边缘填充,以适应DFT的最优尺寸
     copyMakeBorder(srcMat, Edge, 0, DFTrows - srcMat.rows, 0, DFTcols - srcMat.cols, BORDER_CONSTANT, Scalar::all(0));

     // 定义存储实部和虚部的数组
     Mat Memory[] = { Mat_<float>(Edge), Mat::zeros(Edge.size(), CV_32F) };

     Mat complex_matrix;

     // 将实部和虚部合并为一个复数矩阵
     merge(Memory, 2, complex_matrix);

     // 执行傅里叶变换
     dft(complex_matrix, complex_matrix);

     // 分离实部和虚部
     split(complex_matrix, Memory);
     // 计算幅值
     magnitude(Memory[0], Memory[1], Memory[0]);

     // 以下步骤为了显示方便
     Mat process = Memory[0];
     // 防止对数运算时取对数为负或零
     process += Scalar::all(1);
     // 取对数以增强可视化效果
     log(process, process);

     // 确保频谱的对称性
     process = process(Rect(0, 0, process.cols & -2, process.rows & -2));
     // 计算中心点
     int cx = process.cols / 2;
     int cy = process.rows / 2;
     // 将图像中心的四个象限交换位置
     Mat quadrant0(process, Rect(0, 0, cx, cy));
     Mat quadrant1(process, Rect(cx, 0, cx, cy));
     Mat quadrant2(process, Rect(0, cy, cx, cy));
     Mat quadrant3(process, Rect(cx, cy, cx, cy));
     Mat tmp;
     quadrant0.copyTo(tmp);
     quadrant3.copyTo(quadrant0);
     tmp.copyTo(quadrant3);
     quadrant1.copyTo(tmp);
     quadrant2.copyTo(quadrant1);
     tmp.copyTo(quadrant2);

     Mat image;
     //归一化并复制结果到输出参数dstMat
     normalize(process, image, 0, 1, NORM_MINMAX);
     image.copyTo(dstMat);

     return 0;
 }

三、运行结果: 

  • 18
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值