基于opencv的c++接口,实现傅里叶变换。
CV_EXPORTS_W int getOptimalDFTSize(int vecsize);
函数 cv::getOptimalDFTSize 返回大于或等于 vecsize 的最小数 N,以便可以有效地处理大小为 N 的向量的 DFT。在当前的实现中 N= 2 p * 3 q * 5 r 对于某个整数 p, q, r。
如果 vecsize 太大(非常接近 INT_MAX ),该函数将返回一个负数。
虽然该函数不能直接用于估计 DCT 变换的最佳向量大小(因为当前的 DCT 实现只支持偶数大小的向量),但它可以很容易地处理为 getOptimalDFTSize((vecsize+1)/2)*2。
@param vecsize 向量大小。
CV_EXPORTS_W void copyMakeBorder(InputArray src, OutputArray dst,
int top, int bottom, int left, int right,
int borderType, const Scalar& value = Scalar() );
该函数将源图像复制到目标图像的中间。 复制的源图像左侧、右侧、上方和下方的区域将填充外插像素。 这不是基于它的过滤函数所做的(它们即时推断像素),而是其他更复杂的函数(包括您自己的函数)可以用来简化图像边界处理。
@param src 源图像。
@param dst 与 src 类型相同且大小相同的目标图像 Size(src.cols+left+right, src.rows+top+bottom) 。
@param top 顶部像素
@param bottom 底部像素
@param left 离开左边的像素
@param right 参数指定从源图像矩形的每个方向外推多少像素。 比如top=1,bottom=1,left=1,right=1表示需要建1个像素宽的边框。
@param borderType 边框类型。 有关详细信息,请参阅borderInterpolate。
@param value 边界值,如果 borderType==BORDER_CONSTANT 。
//! Various border types, image boundaries are denoted with `|`
//! @see borderInterpolate, copyMakeBorder
enum BorderTypes {
BORDER_CONSTANT = 0, //!< `iiiiii|abcdefgh|iiiiiii` with some specified `i`
BORDER_REPLICATE = 1, //!< `aaaaaa|abcdefgh|hhhhhhh`
BORDER_REFLECT = 2, //!< `fedcba|abcdefgh|hgfedcb`
BORDER_WRAP = 3, //!< `cdefgh|abcdefgh|abcdefg`
BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba`
BORDER_TRANSPARENT = 5, //!< `uvwxyz|abcdefgh|ijklmno`
BORDER_REFLECT101 = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
BORDER_DEFAULT = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
BORDER_ISOLATED = 16 //!< do not look outside of ROI
};
CV_EXPORTS void merge(const Mat* mv, size_t count, OutputArray dst);
函数cv::merge将多个阵列合并为一个多通道阵列。也就是说,输出数组的每个元素都是输入数组元素的串联,其中第i个输入数组的元素被视为mv[i]。
@param mv 要合并的矩阵输入数组;mv中的所有矩阵必须具有相同的大小和深度。
@param count 当mv是纯C数组时,计数输入矩阵的数目;它必须大于零。
@param dst 输出数组,大小和深度与mv[0]相同;通道数将等于参数计数。
CV_EXPORTS_W void dft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0);
函数cv::dft执行以下操作之一:
-对N个元素的1D矢量进行傅里叶变换
-将N个元素的1D矢量的傅里叶变换求反
-正向M x N矩阵的2D傅里叶变换
-将M x N矩阵的2D傅里叶变换求反
@param-src 输入数组,可以是真实的,也可以是复杂的。
@param dst 输出数组,其大小和类型取决于标志。
@param flags 转换标志,表示#DftFlags的组合。
@param nonzeroRows 当参数不为零时,函数假设只有输入数组的第一个nonzero行(未设置#DFT_INVERSE)或只有输出数组的第一条nonzeroSrows行(设置了#DFT_INVERSE)包含非零,因此,函数可以更有效地处理其余行并节省一些时间;这种技术对于使用DFT计算阵列互相关或卷积非常有用。
CV_EXPORTS void split(const Mat& src, Mat* mvbegin);
函数cv::split将多通道阵列拆分为单独的单通道阵列。
如果需要提取单个通道或进行其他复杂的通道置换,请使用mixChannel。
@param src 输入多通道阵列。
@param mvbegin 输出数组;数组的数目必须与src匹配。如果需要,阵列本身会被重新分配。
CV_EXPORTS_W void magnitude(InputArray x, InputArray y, OutputArray magnitude);
函数cv::importance计算由x和y数组的相应元素形成的2D矢量的大小。
@param x 向量x坐标的浮点数组。
@param y 矢量y坐标的浮点数组;它的大小必须与x相同。
@param magnitude 与x大小和类型相同的参数幅度输出阵列。
#pragma once
#include
#include
#include
#include
using namespace std;
using namespace cv;
#define PROCESS_IMG_SUCESS 0
#define PROCESS_IMG_FAIL 1
namespace ImgEnhance
{
//傅里叶变换
class FourierTransformate
{
public:
FourierTransformate() { cout << "FourierTransformate is being created" << endl; } // 这是构造函数声明
~FourierTransformate() { cout << "FourierTransformate is being deleted" << endl; } // 这是析构函数声明
int FftTransformate(cv::Mat srcImage, cv::Mat &dstImage);//傅里叶变换
};
};
#include"fourierTransform.h"
int ImgEnhance::FourierTransformate::FftTransformate(cv::Mat srcImage, cv::Mat &dstImage)
{
//判断图像是否加载成功
if (srcImage.empty())
{
cout << "图像加载失败!" << endl;
return 1;
}
//将输入图像扩展到最佳尺寸,边界用0填充
//离散傅里叶变换的运行速度与图像的大小有很大的关系,当图像的尺寸使2,3,5的整数倍时,计算速度最快
//为了达到快速计算的目的,经常通过添加新的边缘像素的方法获取最佳图像尺寸
//函数getOptimalDFTSize()用于返回最佳尺寸,copyMakeBorder()用于填充边缘像素
int m = getOptimalDFTSize(srcImage.rows);
int n = getOptimalDFTSize(srcImage.cols);
Mat padded;
copyMakeBorder(srcImage, padded, 0, m - srcImage.rows, 0, n - srcImage.cols, BORDER_CONSTANT, Scalar::all(0));
cout << padded.size() << padded.channels() << endl;
//为傅立叶变换的结果分配存储空间
//将plannes数组组合成一个多通道的数组,两个同搭配,分别保存实部和虚部
//傅里叶变换的结果使复数,这就是说对于每个图像原像素值,会有两个图像值
//此外,频域值范围远远超过图象值范围,因此至少将频域储存在float中
//所以我们将输入图像转换成浮点型,并且多加一个额外通道来存储复数部分
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI);
cout << complexI.size() << endl;
cout << planes->size() << endl;
//进行离散傅立叶变换
dft(complexI, complexI);
//将复数转化为幅值,保存在planes[0]
split(complexI, planes); // 将多通道分为几个单通道
magnitude(planes[0], planes[1], planes[0]);
Mat magnitudeImage = planes[0];
//傅里叶变换的幅值达到不适合在屏幕上显示,因此我们用对数尺度来替换线性尺度
//进行对数尺度logarithmic scale缩放
magnitudeImage += Scalar::all(1); //所有的像素都加1
log(magnitudeImage, magnitudeImage); //求自然对数
//剪切和重分布幅度图像限
//如果有奇数行或奇数列,进行频谱裁剪
magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols & -2, magnitudeImage.rows & -2));
// ---- -------- 下面的是为了显示结果 ---------------
// 一分为四,左上与右下交换,右上与左下交换
// 重新排列傅里叶图像中的象限,使原点位于图像中心
int cx = magnitudeImage.cols / 2;
int cy = magnitudeImage.rows / 2;
Mat q0(magnitudeImage, Rect(0, 0, cx, cy)); // ROI区域的左上
Mat q1(magnitudeImage, Rect(cx, 0, cx, cy)); // ROI区域的右上
Mat q2(magnitudeImage, Rect(0, cy, cx, cy)); // ROI区域的左下
Mat q3(magnitudeImage, Rect(cx, cy, cx, cy)); // ROI区域的右下
//交换象限(左上与右下进行交换)
Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
//交换象限(右上与左下进行交换)
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
// 归一化
normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);
dstImage = magnitudeImage.clone();
//显示效果图
/*imshow("频谱幅值", magnitudeImage);*/
return 0;
}
#include"fourierTransform.h"
ImgEnhance::FourierTransformate ImgFft;//傅里叶变换
int main()
{
// 读取源图像及判断
cv::Mat srcImage = cv::imread("text.jpg",0);
if (!srcImage.data)
{
return 1;
}
cv::namedWindow("原始图", 0);
cv::imshow("原始图", srcImage);
// 转化为灰度图像
cv::Mat srcGray;
if (srcImage.channels() == 3)
{
cv::cvtColor(srcImage, srcGray, COLOR_RGB2GRAY);
}
else
{
srcGray = srcImage.clone();
}
cv::namedWindow("灰度图", 0);
cv::imshow("灰度图", srcGray);
//傅里叶变换
Mat fftImage;
ImgFft.FftTransformate(srcGray, fftImage);
cv::namedWindow("傅里叶变换结果图", 0);
cv::imshow("傅里叶变换结果图", fftImage);
cv::waitKey(0);
return 0;
}