Open CV学习 01 离散傅里叶变换DFT
适用于图像处理的二维离散傅里叶变换
通常项目使用过程中由于DFT运算耗时,基本都会采用快速傅里叶变换。
通过欧拉公式我们可以展开。
cv::Mat mat(21, 7, CV_8UC1); cv::randu(mat, cv::Scalar::all(0), cv::Scalar::all(255));//为矩阵随机填充[0,255)的随机值 std::cout << "original:\n" << mat << std::endl << std::endl; cv::Mat paddedMat; int optimalRows = cv::getOptimalDFTSize(mat.rows); int optimalCols = cv::getOptimalDFTSize(mat.cols); //最优尺寸补零 cv::copyMakeBorder(mat, paddedMat, 0, optimalRows - mat.rows, 0, optimalCols - mat.cols,cv::BORDER_CONSTANT, cv::Scalar::all(0)); cv::Mat paddedMatDouble; paddedMat.convertTo(paddedMatDouble, CV_64FC1); Mat myresMat = mydft(paddedMatDouble); cv::Mat mydft_res_real_imaginary[] = { cv::Mat::zeros(myresMat.size(),CV_64FC1), Mat::zeros(myresMat.size(), CV_64FC1) }; cv::split(myresMat, mydft_res_real_imaginary);//拆分数据 Mat dftMat; cv::dft(paddedMatDouble, dftMat, DFT_COMPLEX_OUTPUT); cv::Mat dft_res_real_imaginary[] = { cv::Mat::zeros(dftMat.size(),CV_64FC1), Mat::zeros(dftMat.size(), CV_64FC1)}; cv::split(dftMat, dft_res_real_imaginary);//拆分数据 std::cout << "------------------------" << std::endl; for (int i = 0; i < dftMat.rows; i++) { double * myreal = mydft_res_real_imaginary[0].ptr<double>(i); double * myimg = mydft_res_real_imaginary[1].ptr<double>(i); double * real = dft_res_real_imaginary[0].ptr<double>(i); double * img = dft_res_real_imaginary[1].ptr<double>(i); for (int j = 0; j < dftMat.cols; j++) { std::cout << "-" << (std::abs(myreal[j] - real[j] - myimg[j] + img[j]) < 0.0001) << "- "; } std::cout << std::endl; }
Mat mydft(const Mat& mat) { CV_Assert(mat.type() == CV_64FC1); int N = mat.cols; int M = mat.rows; Mat realMat(M, N, CV_64FC1); Mat imaginaryMat(M, N, CV_64FC1); for (int u = 0; u < M; u++) { double * ptrReal = realMat.ptr<double>(u); double * ptrImaginary = imaginaryMat.ptr<double>(u); for (int v = 0; v < N; v++) { double realSum = 0; double imaginary = 0; for (int x = 0; x < M; x++) { const double * ptrRow = mat.ptr<double>(x); for (int y = 0; y < N; y++) { realSum += ptrRow[y] * std::cos(-2. * CV_PI *(u * x * 1.0 / M + v * y * 1.0 / N)); imaginary += ptrRow[y] * std::sin(-2. * CV_PI *(u * x * 1.0 / M + v * y * 1.0 / N)); } } ptrReal[v] = realSum; ptrImaginary[v] = imaginary; } } Mat res; Mat planes[] = { realMat, imaginaryMat }; merge(planes, 2, res); return res; }
OpenCV提供的dft接口 void dft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0);
dft函数的flags参数说明:
DFT_INVERSE 傅里叶逆变换
DFT_SCALE 与DFT_INVERSE结合使用,输出结果除以元素数来进行缩放
DFT_ROWS 对输入矩阵每一行进行傅里叶变换或逆变换,同时对多个向量进行变换,可以减少三围和多维变换操作的开销
DFT_COMPLEX_OUTPUT 傅里叶变换的结果是关于原点共轭对称的,两个原点对称点的值实部相等虚部相反,所以在一个象限中存储实部,对称象限存储虚部,这样,要两个通道存储的复数就只需要一个通道,节省内存。默认,如果输入为单通道,输出也为单通道(复共轭对称压缩存储复数); 如果输入为双通道,输出也为双通道复数结果;如果指定此参数,总是使用双通道输出结果。
DFT_REAL_OUTPUT 对共轭对称矩阵(如正向傅里叶变换生成的矩阵)进行反傅里叶变换时,结果是一个实数矩阵,但函数不会判断是否共轭对称,可以通过这个参数来告诉输入矩阵是共轭对称的,从而输出实数矩阵。如果输入只有一个通道(复共轭对称压缩存储复数),函数会认为这是一个经过压缩的共轭对称矩阵,从而输出实数矩阵。
DTF_COMPLEX_INPUT 输入必须要有两个通道分别表示实部和虚部,如果输入有两个通道,默认也认为是实部和虚部通道。