opencv 2.x学习笔记(十四)离散傅里叶变换

本文介绍了如何使用OpenCV库进行离散傅里叶变换,从空间域转换到频域处理图像。主要步骤包括:图像扩张、分配存储空间、傅里叶变换、转换成幅度图像、对数缩放和归一化。提供了详细的代码示例,并展示了运行结果。
摘要由CSDN通过智能技术生成

在前面所讲述的图像处理的方法中,我们都是在空间域对图像进行处理。但是,在实际过程中,我们经常需要从空间域转换到频域上对图像进行处理。而最常用的变化方法即是傅里叶变换。下面我们就简单的讲一下,如何使用opencv为我们提供的函数来进行傅里叶变化。

简介

我们知道,任意函数都可以表示成无数个正弦和余弦函数和的形式。图像的傅里叶变换就是将图像分解成正弦和余弦两部分。二维图像的傅里叶变换公式如下:


其中对e的指数项利用欧拉公式即可达到目标。

上述公式中,f(x,y)是空间域的值,F(u, v)则频域的值。转换之后的频域值是负数。故傅里叶变换之后的结果有两个,即实数图像和虚数图像。即:


其中R(u,v)即为实数图像,I(u,v)为虚数图像。但我们一般使用极坐标的形式,即:


其中

称为幅度。


称为相角。

在实际的图像处理过程中,我们仅仅使用幅度图像,因为幅度图像几乎包含了所有原图像中我们想要的几何信息。

那么如何获得一副图像的幅度图像呢?一般来讲有一下几步。

1、扩张图像

为了离散傅里叶变换的运行速度更快。我们一般需要对原图像进行添凑新的边缘像素的方法来获取最佳的图像尺寸。

我们可以使用getOptimalDFTSize()函数来返回最佳的尺寸,利用copyMarkBorder函数来进行填充。如下所示:

	Mat padded;
	int m = getOptimalDFTSize( I.rows );
	int n = getOptimalDFTSize( I.cols );
	copyMakeBorder( I, padded, 0, m-I.rows, 0, n-I.cols, BORDER_CONSTANT, Scalar::all(0) );

copyMakeBorder函数的第一个参数表示输入图像,第二个参数表示输出图像,之后四个参数分别代表上下左右要填充的数目。紧接着的参数表示边界类型,这里表示的是用最后一个参数所表示的常数值进行填充。

2、分配存储空间

我们知道傅里叶变换的结果是复数,而且数值范围一般超过空间值范围,所以我们需要分配一个32位浮点类型的双通道图像,以用来存储实数图像和虚数图像

	Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
	Mat complexI;
	merge( planes, 2, complexI );

3、傅里叶变换

	dft( complexI, complexI );
第一个参数为输入图像,第二个参数为变换后的输出图像。(支持原地变换)。

4、转换成幅度图像

即根据幅度的计算公式来计算出幅度图像。如下所示:

	split( complexI, planes );
	magnitude( planes[0], planes[1], planes[0] );
	Mat magI = planes[0];

5、对数缩放

这一步的目的是为了使傅里叶变换的幅度值范围适合在屏幕上显示。变换的公式为:


	magI += Scalar::all(1);
	log( magI, magI );

6、重分布和归一化图像

为了更好地显示图像,我们需要对图像进行重新分布,我们通过划分和交换幅度图像的位置来实现。同时,我们还需要将幅度的图像进行归一化到显示范围[0,1]上去,在32位浮点数类型的图像中,它的取值范围是[0,1],在使用imshow函数来显示图像时,将为我们自动乘以255,以扩展到[0,255]。

    magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));

    int cx = magI.cols/2;
    int cy = magI.rows/2;

    Mat q0(magI, Rect(0, 0, cx, cy));  
    Mat q1(magI, Rect(cx, 0, cx, cy));  
    Mat q2(magI, Rect(0, cy, cx, cy));  
    Mat q3(magI, Rect(cx, cy, cx, cy)); 

    Mat tmp;                          
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);

    q1.copyTo(tmp);             
    q2.copyTo(q1);
    tmp.copyTo(q2);

    normalize(magI, magI, 0, 1, CV_MINMAX); 

完整代码

注:该代码为opencv为我们提供的示例代码:

#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>

using namespace cv;
using namespace std;

int main( int argc, char ** argv )
{
	if( argc != 2 )
	{
		cerr << "参数个数错误!" << endl;
		return -1;
	}
	const char * filename = argv[1];
	Mat I = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
    if( I.empty())
        return -1;

	Mat padded;
	int m = getOptimalDFTSize( I.rows );
	int n = getOptimalDFTSize( I.cols );
	copyMakeBorder( I, padded, 0, m-I.rows, 0, n-I.cols, BORDER_CONSTANT, Scalar::all(0) );

	Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
	Mat complexI;
	merge( planes, 2, complexI );

	dft( complexI, complexI );

	split( complexI, planes );
	magnitude( planes[0], planes[1], planes[0] );
	Mat magI = planes[0];

	magI += Scalar::all(1);
	log( magI, magI );

    magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));

    int cx = magI.cols/2;
    int cy = magI.rows/2;

    Mat q0(magI, Rect(0, 0, cx, cy));  
    Mat q1(magI, Rect(cx, 0, cx, cy));  
    Mat q2(magI, Rect(0, cy, cx, cy));  
    Mat q3(magI, Rect(cx, cy, cx, cy)); 

    Mat tmp;                          
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);

    q1.copyTo(tmp);             
    q2.copyTo(q1);
    tmp.copyTo(q2);

    normalize(magI, magI, 0, 1, CV_MINMAX); 

    imshow("Input Image"       , I   ); 
    imshow("spectrum magnitude", magI);
    waitKey();
	return 0;
}

运行结果:





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值