一、函数:
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)
。top
、bottom
、left
、right
:分别指定在源图像矩形的每个方向上外推多少像素来构建边框。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
函数计算由x
和y
数组对应的元素形成的二维向量的大小。向量的大小定义为向量两个分量平方和的平方根。
(3)公式描述:
- 其中,
dst
是输出的向量大小,x(I)
和y(I)
是在位置I
的x
和y
分量的值。
(4)参数:
x
:浮点型数组,包含向量的 x-坐标。y
:浮点型数组,包含向量的 y-坐标。它必须与x
有相同的大小。magnitude
:输出数组,其大小和类型与x
相同,包含计算出的向量大小。
(5)相关函数:
cartToPolar
:将笛卡尔坐标系中的点转换为极坐标系,需要幅度和角度。polarToCart
:将极坐标系中的点转换回笛卡尔坐标系。phase
:计算复数的相位角。sqrt
:计算平方根,magnitude
函数内部使用此函数来计算向量的大小。
(6)使用场景:
-
magnitude
函数常用于图像处理中的梯度计算,特别是在计算图像的幅度谱时,其中x
和y
分量可以来自图像的梯度或其它二维变换的结果。
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_INF
、NORM_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;
}