基于opencv的c++图像处理(傅里叶变换)

前言

基于opencv的c++接口,实现傅里叶变换。

相关的opencv接口解析

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大小和类型相同的参数幅度输出阵列。

示例代码

fourierTransform.h

#pragma once
#include <iostream>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>

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);//傅里叶变换	

	};

};

fourierTransform.cpp

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

}

test.cpp

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

}

结果展示

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40118285/article/details/127083072