2019-05-05 10:48:55 tyfwin 阅读数 755
  • 携手STM32CubeMX玩转STM32

    本课程教大家如何利用STM32CubeMX玩转STM32(STM32CubeMX支持的所有型号MCU都适用于本课程)。课程内容分为基础内容和扩展内容,例如:讲解串口时会扩展开讲Xmodem通信协议,讲解ADC/DAC时会扩展讲傅里叶计算,讲解完FLASH操作会扩展将bootloader的编写,讲解完M3的bootloader编写会扩展讲解M0的bootloader...... 内容绝对实在,对于学习以及工作都会有很大的帮助。最终的目的就是让大家学会快速开发STM32并收获与STM32有关的实用技术知识。

    755 人正在学习 去看看 李凯龙

 

傅里叶变化的平移性:

 

matlab代码验证过程实现:

%% 研究傅里叶变化 的 平移特性
%空间域 乘以exp ,频率域移动

clc;clear
I = imread('rice.jpg');
I  = rgb2gray(I);
I = im2double(I);
figure;subplot(1, 3, 1);imshow(I);title('原图');

%傅里叶变化
f = fft2(I);
f = fftshift(log(1+abs(f)));
subplot(1, 3, 2);imshow(f, []);title('频谱图');

% 图片大小为256*256
% 假设,图像大小为10mm , 那么采样频率为25.6,系统可采集最高频率为25.6/2,
% 每个像素大小为10/256 mm,单位频率是0.1

kx=25.6/2;  %频率。  如果为25.6/2,则移动到最边缘。 为25.6则为一个周期重新回来。
ky=0;  %测试只在x方向(行移动)发生平移
[M,N]=size(I); 
MM=linspace(0,10,256);%1-10 分为256个数,存在MM中。10mm的空间分为256份
NN=linspace(0,10,256);
for i=1:N
     for j=1:M    %MM(i),NN(j)相当于空间真实坐标
         r(i,j)= exp(1i.*2.*pi.*kx.*MM(i) + 1i.*2.*pi .*ky.*NN(j));  %2pi写在里面了
     end
end
I2= I .* r;
f2 = fft2(I2);
f2 = fftshift(log(1+abs(f2)));
subplot(1, 3, 3);imshow(f2, []);title('平移后-频谱图2');

结果:

中间频率的解释可以参见我的另一篇博客:

https://blog.csdn.net/tyfwin/article/details/89840154

本人的水平有限,还望批评指正。

2016-04-13 14:19:32 wenhao_ir 阅读数 8435
  • 携手STM32CubeMX玩转STM32

    本课程教大家如何利用STM32CubeMX玩转STM32(STM32CubeMX支持的所有型号MCU都适用于本课程)。课程内容分为基础内容和扩展内容,例如:讲解串口时会扩展开讲Xmodem通信协议,讲解ADC/DAC时会扩展讲傅里叶计算,讲解完FLASH操作会扩展将bootloader的编写,讲解完M3的bootloader编写会扩展讲解M0的bootloader...... 内容绝对实在,对于学习以及工作都会有很大的帮助。最终的目的就是让大家学会快速开发STM32并收获与STM32有关的实用技术知识。

    755 人正在学习 去看看 李凯龙

图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号"qxsf321",并关注!
图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号"qxsf321",并关注!
图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号"qxsf321",并关注!

灰度图像的二维傅里叶变换(cv_gray_fft2[我用C语言写成]和DFT[用C++写成]),二维傅里叶变换结果的幅值计算(cv_abs),频谱平移(cv_gray_fft2shift)【频谱平移的作用详见我的博文http://blog.csdn.net/wenhao_ir/article/details/51689960】,将数值归一化到0到255区间(cv_range_0to255)是非常常用的四个功能!所以写成四个函数,方便将来调用。

傅里叶变换的意义这里就不多说了,大家在高等数学、信号与系统中应该都学过,不清楚的可以去查阅相关资料!这里我简单说两点!①图像的频率是表征图像中灰度变化剧烈程序的指标;②图像低频分量表示背景和缓慢变化区域,高频分量表示图像的边缘、细节及相关噪声。

源码如下

源码下载链接:http://pan.baidu.com/s/1skRqaRV

源码中用到的图片下载链接:http://pan.baidu.com/s/1slilbPF

 

#include <opencv2/opencv.hpp>  
#include <opencv2/legacy/compat.hpp> 
#include <fstream>
using namespace std;  
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")  

void cv_gray_fft2(IplImage *src, IplImage *dst)  //注意:此函数仅适用灰度图像,src因为是灰度图像,所以通道数要求为1,dst的属性为:IPL_DEPTH_64F,2(两通道)
{
IplImage *image_Re = 0, *image_Im = 0, *Fourier = 0;
image_Re = cvCreateImage(cvGetSize(src), IPL_DEPTH_64F, 1);  //实部
image_Im = cvCreateImage(cvGetSize(src), IPL_DEPTH_64F, 1);  //虚部
Fourier = cvCreateImage(cvGetSize(src), IPL_DEPTH_64F, 2);//2 channels,分别存储image_Re、image_Im

// Real part conversion from u8 to 64f (double)
cvConvertScale(src, image_Re);

// Imaginary part (zeros)
cvZero(image_Im);

// Join real and imaginary parts and stock them in Fourier image
cvMerge(image_Re, image_Im, 0, 0, Fourier);

// Application of the forward Fourier transform
cvDFT(Fourier, dst, CV_DXT_FORWARD);
cvReleaseImage(&image_Re);
cvReleaseImage(&image_Im);
cvReleaseImage(&Fourier);
}

void cv_abs(IplImage *src,IplImage *fft2_out, IplImage *dst)//src就是灰度图像原始数据,fft2_out要求是cv_gray_fft2的dst数据类型,即"64F, 2";dst为"64F, 1"
{
IplImage *image_Re = 0, *image_Im = 0;
image_Re = cvCreateImage(cvGetSize(src), IPL_DEPTH_64F, 1);  //实部
image_Im = cvCreateImage(cvGetSize(src), IPL_DEPTH_64F, 1);  //虚部
cvSplit(fft2_out,image_Re,image_Im,0,0);  
cvPow(image_Re,image_Re,2);                 
    cvPow(image_Im,image_Im,2); 
cvAdd(image_Re,image_Im,image_Re,NULL);
cvPow(image_Re,dst,0.5);

cvReleaseImage(&image_Re);
cvReleaseImage(&image_Im);
}

void cv_gray_fft2shift(IplImage *src)//参数是"64F, 2"
{
int nRow, nCol, i, j, cy, cx;  
double tmp13, tmp24;

//Rearrange the quadrants of Fourier image so that the origin is at the image center  
    nRow = src->height;  
    nCol = src->width;  
    cy = nRow/2; // image center  
    cx = nCol/2;  
    //CV_IMAGE_ELEM为OpenCV定义的宏,用来读取图像的像素值,这一部分就是进行中心变换  
    for( j = 0; j < cy; j++ ){  
        for( i = 0; i < cx; i++ ){  
            //中心化,将整体份成四块进行对角交换  
            tmp13 = CV_IMAGE_ELEM( src, double, j, i);  
            CV_IMAGE_ELEM( src, double, j, i) = CV_IMAGE_ELEM(  
                src, double, j+cy, i+cx);  
            CV_IMAGE_ELEM( src, double, j+cy, i+cx) = tmp13;  
  
            tmp24 = CV_IMAGE_ELEM( src, double, j, i+cx);  
            CV_IMAGE_ELEM( src, double, j, i+cx) =  
                CV_IMAGE_ELEM( src, double, j+cy, i);  
            CV_IMAGE_ELEM( src, double, j+cy, i) = tmp24;  
        }  
    }  
}

void cv_range_0to255(IplImage *src, IplImage *dst)//两个参数都是"64F, 2"
{
double m,M; 
double scale;  
double shift;  

cvMinMaxLoc(src,&m,&M,NULL,NULL); 
scale = 255/(M - m);  
shift = -m * scale;  
cvConvertScale(src,dst,scale,shift);  
}

int main()
{
int i=0;//循环变量
//从文件中加载原图  
IplImage *pSrcImage = cvLoadImage("coins.png", CV_LOAD_IMAGE_UNCHANGED);  

//创建输出的图像
IplImage *pOutImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_64F,1);

//创建中间结果图像
IplImage *cv_gray_fft2_out = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_64F,2);//注意这里是二通道!
IplImage *cv_abs_out = cvCreateImage(cvGetSize(pSrcImage),  IPL_DEPTH_64F, 1);

cv_gray_fft2(pSrcImage,cv_gray_fft2_out);

double watch_cv_gray_fft2_out_Re[100];//利用调试时的局部变量窗口观察cvDFTOut实部的前100个值,看与MATLAB运行的结果否相同
double watch_cv_gray_fft2_out_Im[100];//利用调试时的局部变量窗口观察cvDFTOut虚部的前100个值,看与MATLAB运行的结果否相同
for(i=0;i<10;i++)//一般情况下观察10个值就够了
{
watch_cv_gray_fft2_out_Re[i]=cvGet2D(cv_gray_fft2_out,0,i).val[0];  
}
for(i=0;i<10;i++)//一般情况下观察10个值就够了
{
watch_cv_gray_fft2_out_Im[i]=cvGet2D(cv_gray_fft2_out,0,i).val[1];  
}

cv_abs(pSrcImage,cv_gray_fft2_out,cv_abs_out);
double watch_cv_abs_out[100];//利用调试时的局部变量窗口观察cv_abs_out的前100个值,看与MATLAB运行的结果否相同
for(i=0;i<10;i++)//一般情况下观察10个值就够了
{
watch_cv_abs_out[i]=cvGet2D(cv_abs_out,0,i).val[0];  
}

cv_gray_fft2shift(cv_abs_out);
double watch_cv_gray_fft2shift_out[100];//利用调试时的局部变量窗口观察cv_gray_fft2shift_out的前100个值,看与MATLAB运行的结果否相同
for(i=0;i<10;i++)//一般情况下观察10个值就够了
{
watch_cv_gray_fft2shift_out[i]=cvGet2D(cv_abs_out,0,i).val[0];  
}

cv_range_0to255(cv_abs_out,pOutImage);
double watch_cv_range_0to255_out[100];//利用调试时的局部变量窗口观察cv_range_0to255_out的前100个值,看与MATLAB运行的结果否相同
for(i=0;i<10;i++)//一般情况下观察10个值就够了
{
watch_cv_range_0to255_out[i]=cvGet2D(pOutImage,0,i).val[0];  
}
return 0;
}

 

上面四个函数与MATLAB运行的结果一致,MATLAB程序如下:

 

clear all;
clc;
I=imread('coins.png');
F=fft2(I);
MatlabFFt2Out=F;
MatlabAbsOut=abs(F);
MatlabFftshiftOut=fftshift(MatlabAbsOut);
T=MatlabFftshiftOut;
max_T=max(max(T));
min_T=min(min(T));
shift_T=-min_T*255/(max_T-min_T);
MatlabRange0to255=T.*255/(max_T-min_T)+shift_T;

 

运行结果截图如下

cv_gray_fft2

cv_abs

cv_gray_fft2shift

cv_range_0to255

下面再给出OpenCV下的C++代码实现,代码中的DFT函数已经包含了上述函数的功能

 

//OpenCV版本2.4.9    
//交流QQ2487872782   
  
# include <opencv2/core/core.hpp>  
# include <opencv2/highgui/highgui.hpp>  
# include <opencv2/imgproc/imgproc.hpp>  
#include <iostream>  
using namespace cv;  
using namespace std;  
cv::Mat DFT(cv::Mat srcImage)  
{  
       cv::Mat srcGray;  
       cvtColor(srcImage,srcGray,CV_RGB2GRAY);  
       // 将输入图像延扩到最佳的尺寸(快速傅里叶变换是基于图像尺寸是2、3或5的倍数完成的,因此对于输入源图像,首先应将其转换成DFTsize,OpenCV中提供了函数getOptimalDFTSize来实现尺寸转换)  
       int nRows = getOptimalDFTSize(srcGray.rows);  
       int nCols = getOptimalDFTSize(srcGray.cols);  
       cv::Mat resultImage;  
       // 把灰度图像放在左上角,在右边和下边扩展图像,  
       // 添加的像素初始化为0  
       copyMakeBorder(srcGray, resultImage, 0,  
            nRows - srcGray.rows,   
            0, nCols - srcGray.cols,   
            BORDER_CONSTANT, Scalar::all(0));  
       // 为傅立叶变换的结果(实部和虚部)分配存储空间  
       cv::Mat planes[] = { cv::Mat_<float>(resultImage),   
            cv::Mat::zeros(resultImage.size(), CV_32F)};//这里实际上是建立MAT数组,数组有两个成员:  
														//第一个就是sizeConvMat这个对象(只是数据类型转换成了float类型)  
                                                        //第二个是全0的类型为32F的对象  
       Mat completeI;  
       // 为延扩后的图像增添一个初始化为0的通道  
       merge(planes,2,completeI); //把groupMats的第0和第1个对象合并到mergeMat,通过这个操作mergeMat是双通道的数据阵列了  
       // 进行离散傅立叶变换  
       dft(completeI,completeI);  
       // 将复数转换为幅度  
       split(completeI,planes);  
       magnitude(planes[0],planes[1],planes[0]);//0中存的是实部值,1中存的是虚部值  
       cv::Mat dftResultImage = planes[0];   
       // 对数尺度(logarithmic scale)缩放  
       dftResultImage += 1;//阵列加1作对数变换,以扩大频域动态显示范围  
       log(dftResultImage,dftResultImage);//作对数变换  
       // 剪切和重分布幅度图象限  
       dftResultImage= dftResultImage(Rect(0,  
            0,srcGray.cols,srcGray.rows));  
       // 归一化图像  
       normalize(dftResultImage,dftResultImage,  
            0,1,CV_MINMAX);  
       int cx = dftResultImage.cols/2;  
       int cy = dftResultImage.rows/2;   
       Mat tmp;  
       // Top-Left - 为每一个象限创建ROI  
       Mat q0(dftResultImage,Rect(0,0,cx,cy));  
       // Top-Right  
       Mat q1(dftResultImage,Rect(cx,0,cx,cy));  
       // Bottom-Left  
       Mat q2(dftResultImage,Rect(0,cy,cx,cy));  
       // Bottom-Right  
       Mat q3(dftResultImage,Rect(cx,cy,cx,cy));   
       // 交换象限 (Top-Left with Bottom-Right)      
       q0.copyTo(tmp);  
       q3.copyTo(q0);  
       tmp.copyTo(q3);  
       // 交换象限 (Top-Right with Bottom-Left)  
       q1.copyTo(tmp);  
       q2.copyTo(q1);  
       tmp.copyTo(q2);  
       return dftResultImage;  
}  
int main()  
{  
      cv::Mat srcImage = imread("coins.png");  
      if(srcImage.empty())  
      return-1;  
      imshow("srcImage", srcImage);  
      cv::Mat resultImage = DFT(srcImage);  
      imshow("resultImage", resultImage);  
      cv::waitKey(0);  
      return 0;  
}  

 

运行结果如下图所示

 


 

2015-10-05 16:07:13 robin1987z 阅读数 6591
  • 携手STM32CubeMX玩转STM32

    本课程教大家如何利用STM32CubeMX玩转STM32(STM32CubeMX支持的所有型号MCU都适用于本课程)。课程内容分为基础内容和扩展内容,例如:讲解串口时会扩展开讲Xmodem通信协议,讲解ADC/DAC时会扩展讲傅里叶计算,讲解完FLASH操作会扩展将bootloader的编写,讲解完M3的bootloader编写会扩展讲解M0的bootloader...... 内容绝对实在,对于学习以及工作都会有很大的帮助。最终的目的就是让大家学会快速开发STM32并收获与STM32有关的实用技术知识。

    755 人正在学习 去看看 李凯龙

有点悲剧,在快编辑完了的时候不小心点到舍弃,结果一下午字白打了。


傅里叶变换被称为数学中的棱镜,可以将函数分解为频率不同的正弦函数和余弦函数的组合。而图像处理中的傅里叶变换一般专指二维离散傅里叶变换,它可以将图像从空间域变换到频域,拥有很多优良的特质,如线性、对称、平移、卷积等。在此,我们对于一维以及连续的傅里叶变换不做描述,只说二维离散傅里叶变换。


二维离散傅里叶变换

人们一般都在空间域来描述图像,即通过二维空间坐标上的灰度值来表征出图像的形状、纹理、尺寸等特征,这也是最为直观且被人所接受的图像表达方式。而傅里叶变换的出现给了图像分析一个全新的角度:频域。在二维图像角度,频率的高低表征的是原图灰度的变化剧烈程度,也就是空间域中所指的梯度。背景区域等灰度变化缓慢的区域,梯度较低,处于频域中的低频部分,边缘、噪声等灰度变化快的区域,梯度较高,处于频域中的高频部分。一般的图片低频的内容会占大多数部分。

二维离散傅里叶变换公式:


其中F(u,v)为傅里叶变换结果,u和v为频率分量,f(x,y)为原图,M和N为图像的宽度和高度。

特例:当u,v均为0时,F(0,0)的值为原图f(x,y)的均值,虚部为0,被称之为直流分量。

计算得到的结果为复数,分为实部和虚部,图像处理中一般求取其模(频率幅值),并反向投影回以u,v为坐标轴的二维空间进行分析。


|F(u,v)|  = sqrt(R(u,v)^2+I(u,v)^2)

其中R(u,v)和I(u,v)分别为实部和虚部。


由于求取的频率幅值跨幅巨大,并不适合直接反向投影回二维图像空间来观察,所以一般会将结果求取对数,将结果从线性尺度变换到对数尺度:


D(u,v) = log(1+|F(u,v)|)


再进行归一化后即可以灰度值的方式直观的看到幅值的高低变化,即图像的频谱图。

另外,基于傅里叶变换的对称性,为了便于观察频谱图,一般会将原点F(0,0)平移到图像中心,使得低频区域位于图像中部,高频区域位于图像外围。

实现方式有两种:

(1):将原图f(x,y)乘以(-1)^(x+y)后再做傅里叶变换

(2):直接做傅里叶变换,之后将频谱图以N/2,M/2为线划分成四个区域,位于对角线的两两区域对调位置,如图:

一张图像在变换到频域后的成分分布可以参考下图:


傅里叶变换在图像处理中的应用

个人能想到的利用场景有如下:


1.图像去噪

正如前文所述,大部分的噪声是位于图像的高频区域,所以,利用低通滤波器来过滤图像噪声是图像去噪的一个重要手段。另外,一些周期噪声反映到频谱上会呈现出孤立的亮点,利用傅里叶变换可以轻松的将这种噪声过滤掉,如下图所示:


2.简化卷积运算

利用傅里叶变换的卷积特性(即空间域中的卷积等于频域的点积,频域的卷积等于空间域的点积),可以有效的简化空间域中的卷积运算,如各类滤波器的卷积操作等,在变换到频域后采用与该滤波器等价的频域函数进行点积运算,再反傅里叶变换回空间域即完成了空间域中的卷积运算。


3.寻找直线轮廓

空间域中的直线在变换到频域后会呈现出一条与之垂直的高亮直线(有时也可能不那么明显),利用这个特性可以找到原图中疑似直线的轮廓及其角度(以前有做过一个项目就通过傅里叶变换来进行图像的倾斜矫正),效果图:

lena的帽檐在频谱图中对应了一条与之垂直的亮线。


4.图像压缩

如上文所述,图像的大部分信息集中在频域的低频部分,所以图像的压缩、提萃也是可以用到傅里叶变换的。


最后推荐个视频,可以帮助理解下傅里叶变换:

点击打开链接


2019-06-28 11:19:18 weixin_40096160 阅读数 33
  • 携手STM32CubeMX玩转STM32

    本课程教大家如何利用STM32CubeMX玩转STM32(STM32CubeMX支持的所有型号MCU都适用于本课程)。课程内容分为基础内容和扩展内容,例如:讲解串口时会扩展开讲Xmodem通信协议,讲解ADC/DAC时会扩展讲傅里叶计算,讲解完FLASH操作会扩展将bootloader的编写,讲解完M3的bootloader编写会扩展讲解M0的bootloader...... 内容绝对实在,对于学习以及工作都会有很大的帮助。最终的目的就是让大家学会快速开发STM32并收获与STM32有关的实用技术知识。

    755 人正在学习 去看看 李凯龙

Java+OpenCV实现数字图像的相关处理

前言

数字图像处理课程的一些知识,OpenCV主要还是用C++,用java的资料较少,所以就将自己的一些代码分享出来,不足之处,请指正。

几何变换

几何变换是数字图像处理中最基本的操作,包括平移,旋转,缩放等,具体公式可自行了解,直接上代码和图片了。

//旋转
public Mat rotate(Mat src,double angle) {   
		Point center = new Point(src.cols()/2,src.rows()/2);
        //double angle=60;//旋转角度
    	double scale=1;//缩放尺度
    	Mat mat = Imgproc.getRotationMatrix2D(center, angle, scale);
    	Imgproc.warpAffine(src, mat, mat, src.size());
    	return mat;
	}

旋转结果
旋转结果

//平移
public Mat move(Mat src,double x,double y) { 
		Mat mat = new Mat(2,3,CvType.CV_32FC1);
		mat.put(0,0,new double[]{1,0,x,0,1,y});
		Mat m = src;
		Imgproc.warpAffine(src,mat,mat,src.size());
		return mat;
	}

平移结果
平移

//缩放
public Mat scale(Mat src,double scale) {  
		Point center = new Point(src.cols()/2,src.rows()/2);
        double angle=0;//旋转角度
    	//double scale=0.6;//缩放尺度
    	Mat mat = Imgproc.getRotationMatrix2D(center, angle, scale);
    	Imgproc.warpAffine(src, mat, mat, src.size());
    	return mat;
	}

缩放结果
缩放结果

傅里叶变换

通过改写c++代码得到的傅里叶变换函数

//傅里叶变换
public Mat fourier(Mat src) {  
		int new_height = Core.getOptimalDFTSize(src.rows()); // 获取纵向扩充后的距离(高度)
        int new_width = Core.getOptimalDFTSize(src.cols()); // 获取横向扩充后的距离(宽度)
        Mat padded = new Mat();
        // 扩充图像边界
        Core.copyMakeBorder(src,padded, 0,new_height - src.rows(), 0, new_width - src.cols() , Core.BORDER_CONSTANT , Scalar.all(0));
               
        List<Mat> paddedMat_channels = new ArrayList<Mat>();
       
        //转float
        padded.convertTo(padded,CvType.CV_32F);
        paddedMat_channels.add(padded);
        paddedMat_channels.add(Mat.zeros(padded.size(),CvType.CV_32F));

        //合并通道
        Mat complexImage = new Mat();
        Core.merge(paddedMat_channels,complexImage);
        //离散傅里叶变换
        Core.dft(complexImage,complexImage);
        //分割通道
        Core.split(complexImage,paddedMat_channels);

        //将复数值转化为副值        
        Mat temp = new Mat();
        Core.magnitude(paddedMat_channels.get(0),paddedMat_channels.get(1),temp);
        Core.add(temp,Scalar.all(0),temp);
        Core.log(temp, temp);        
        temp = new Mat(temp,new Rect(0,0,temp.cols()&-2,temp.rows()&-2));
        
        int cx = src.cols() / 2;
        int cy = src.rows() / 2;

        Mat q0 = new Mat(temp, new Rect(0, 0, cx, cy));
        Mat q1 = new Mat(temp, new Rect(cx, 0, cx, cy));
        Mat q2 = new Mat(temp, new Rect(0, cy, cx, cy));
        Mat q3 = new Mat(temp, new Rect(cx, cy, cx, cy));

        Mat tmp = new Mat();
        q0.copyTo(tmp);
        q3.copyTo(q0);
        tmp.copyTo(q3);
        q1.copyTo(tmp);
        q2.copyTo(q1);
        tmp.copyTo(q2);
        
        Core.normalize(temp, temp, 0, 255, Core.NORM_MINMAX);
              
        Imgcodecs.imwrite(".\\src\\photo\\test.jpg", temp);  //图片保存到本地
        Mat mat = Imgcodecs.imread(".\\src\\photo\\test.jpg");   //图片读取      
        
        return mat;
	}

傅里叶变换结果
傅里叶变换结果

2019-11-29 15:04:16 cherishlicoolboy 阅读数 10
  • 携手STM32CubeMX玩转STM32

    本课程教大家如何利用STM32CubeMX玩转STM32(STM32CubeMX支持的所有型号MCU都适用于本课程)。课程内容分为基础内容和扩展内容,例如:讲解串口时会扩展开讲Xmodem通信协议,讲解ADC/DAC时会扩展讲傅里叶计算,讲解完FLASH操作会扩展将bootloader的编写,讲解完M3的bootloader编写会扩展讲解M0的bootloader...... 内容绝对实在,对于学习以及工作都会有很大的帮助。最终的目的就是让大家学会快速开发STM32并收获与STM32有关的实用技术知识。

    755 人正在学习 去看看 李凯龙

图像傅里叶变换的频谱特征 三

 

6,平移和旋转

图像的平移并不会影响图像的频谱,同时,图像的相位会随着图像的旋转而旋转。

 

Part I 平移和旋转对频谱的影响

下面我用矩形的频谱图来说明图像矩形的平移并不会对频谱有丝毫的影响

 

Matlab代码:

 

  1. clear all
  2. close all
  3. %% Author: J27
  4. % Jesus love you!
  5. Isize = 512;
  6. Rwidth = 50;
  7. Rlength = 3*Rwidth;
  8. Irect = zeros(Isize);
  9. Irect(floor((Isize - Rlength)/2) + 1:floor((Isize - Rlength)/2) + Rlength,...
  10. (floor(Isize - Rwidth)/2) + 1:floor((Isize - Rwidth)/2) + Rwidth) = 1;
  11. subplot(3,1,1)
  12. imshowpair(Irect,log(abs(fftshift(fft2(Irect)))+1),'montage')
  13. Irect = zeros(Isize);
  14. Irect(floor((Isize - Rlength)/2) + 150 + 1:floor((Isize - Rlength)/2) + 150 + Rlength,...
  15. (floor(Isize - Rwidth)/2) + 1 + 200:floor((Isize - Rwidth)/2) + Rwidth + 200) = 1;
  16. subplot(3,1,2)
  17. imshowpair(Irect,log(abs(fftshift(fft2(Irect)))+1),'montage')
  18. Irect = zeros(Isize);
  19. Irect(floor((Isize - Rlength)/2) + 1:floor((Isize - Rlength)/2) + Rlength,...
  20. (floor(Isize - Rwidth)/2) + 1 - 80:floor((Isize - Rwidth)/2) + Rwidth - 80) = 1;
  21. subplot(3,1,3)
  22. imshowpair(Irect,log(abs(fftshift(fft2(Irect)))+1),'montage')

 

再比如下面这个例子:

 

再来看看频谱随着矩形的旋转而旋转相同的角度

Matlab代码:

  1. Isize = 512;
  2. Rwidth = 50;
  3. Rlength = 3*Rwidth;
  4. Irect = zeros(Isize);
  5. Irect(floor((Isize - Rlength)/2) + 1:floor((Isize - Rlength)/2) + Rlength,...
  6. (floor(Isize - Rwidth)/2) + 1:floor((Isize - Rwidth)/2) + Rwidth) = 1;
  7. Irot = imrotate(Irect, 15, 'crop', 'bilinear');
  8. subplot(3,1,1)
  9. imshowpair(Irot,log(abs(fftshift(fft2(Irot)))+1),'montage')
  10. Irect = zeros(Isize);
  11. Irect(floor((Isize - Rlength)/2) + 1:floor((Isize - Rlength)/2) + Rlength,...
  12. (floor(Isize - Rwidth)/2) + 1:floor((Isize - Rwidth)/2) + Rwidth) = 1;
  13. Irot = imrotate(Irect, 45, 'crop', 'bilinear');
  14. subplot(3,1,2)
  15. imshowpair(Irot,log(abs(fftshift(fft2(Irot)))+1),'montage')
  16. Irect = zeros(Isize);
  17. Irect(floor((Isize - Rlength)/2) + 1:floor((Isize - Rlength)/2) + Rlength,...
  18. (floor(Isize - Rwidth)/2) + 1:floor((Isize - Rwidth)/2) + Rwidth) = 1;
  19. Irot = imrotate(Irect, 90, 'crop', 'bilinear');
  20. subplot(3,1,3)
  21. imshowpair(Irot,log(abs(fftshift(fft2(Irot)))+1),'montage')

 

 

Part II 平移和旋转对相位的影响

     先用一个简单的例子来说明图像相位的作用(所用图像为cameraman),在图像的频域分析和滤波中,相位是常常被忽略的。虽然相位分量的贡献很不直观,但是它恰恰很重要。相位是频谱中各正弦分量关于原点的位移的度量

上面的小实验充分说明了,看似无用的,且常常被忽略的相位,在DFT的频域中起到了多么重要的作用(注意区分实部和虚部(直角坐标系)VS 频谱和相位(极坐标系)!)。

Matlab代码:

  1. close all;
  2. clear all;
  3. I = im2double(imread('cameraman.tif'));
  4. DFT = fft2(I);
  5. absF = abs(DFT);
  6. Phi = atan2(imag(DFT),real(DFT));
  7. subplot(2,3,1);
  8. imshow(I,[]);
  9. title('Image(Cameraman)');
  10. subplot(2,3,2);
  11. imshow(log(fftshift(absF) + 1),[]);
  12. title('Spectrum');
  13. subplot(2,3,3);
  14. imshow(fftshift(Phi),[]);
  15. title('Phase');
  16. F = absF.*exp(1j*Phi);
  17. Rebuild = im2uint8(real(ifft2(F)));
  18. subplot(2,3,4);
  19. imshow(Rebuild,[]);
  20. title('Rebuild with orginal specturm and phase');
  21. % rebuild with spectrum only
  22. F = absF.*exp(1j*1);
  23. Rebuild = im2uint8(real(fftshift(ifft2(F))));
  24. subplot(2,3,5);
  25. imshow(Rebuild,[]);
  26. title('Rebuild with spectrum only');
  27. % rebuild with Phase only
  28. g = 1.*exp(1j*Phi);
  29. g = im2uint8(real(ifft2(g)));
  30. subplot(2,3,6);
  31. imshow(g,[]);
  32. title('Rebuild with Phase only');

     接下来我们再来看看图像在空间域中的移位和旋转对相位有什么影响。下图中,左边一列是图像,中间一列是频谱,右边一列是相位图。你必须意识到,通过肉眼,你很难从相位图中得到什么有用的信息。

(上图中最后一行打错了,不是“旋转改变了相位”而是“平移改变了相位”)

Matlab代码:

  1. clear all
  2. close all
  3. %% Author: J27
  4. % Jesus love you!
  5. % shifting and rotation
  6. Isize = 512;
  7. Rwidth = 50;
  8. Rlength = 3*Rwidth;
  9. Irect = zeros(Isize);
  10. Irect(floor((Isize - Rlength)/2) + 1:floor((Isize - Rlength)/2) + Rlength,...
  11. (floor(Isize - Rwidth)/2) + 1:floor((Isize - Rwidth)/2) + Rwidth) = 1;
  12. Idft = fft2(Irect);
  13. subplot(3,3,1);
  14. imshow(Irect);
  15. subplot(3,3,2);
  16. imshow(log(abs(fftshift(Idft))+1),[]);
  17. subplot(3,3,3);
  18. imshow((atan2(imag(Idft),real(Idft))),[]);
  19. Irot = imrotate(Irect, 45, 'crop', 'bilinear');
  20. Idft = fft2(Irot);
  21. subplot(3,3,4);
  22. imshow(Irot);
  23. subplot(3,3,5);
  24. imshow(log(abs(fftshift(Idft))+1),[]);
  25. subplot(3,3,6);
  26. imshow(atan2(imag(Idft),real(Idft)),[]);
  27. Irect = zeros(Isize);
  28. Irect(floor((Isize - Rlength)/2) + 150 + 1:floor((Isize - Rlength)/2) + 150 + Rlength,...
  29. (floor(Isize - Rwidth)/2) + 1 + 200:floor((Isize - Rwidth)/2) + Rwidth + 200) = 1;
  30. Idft = fft2(Irect);
  31. subplot(3,3,7);
  32. imshow(Irect);
  33. subplot(3,3,8);
  34. imshow(log(abs(fftshift(Idft))+1),[]);
  35. subplot(3,3,9);
  36. imshow(atan2(imag(Idft),real(Idft)),[]);

 

Matlab代码:

  1. I = im2double(imread('cameraman.tif'));
  2. Idft = fft2(I);
  3. figure;
  4. subplot(2,3,1);
  5. imshow(I,[]);
  6. subplot(2,3,2);
  7. imshow(log(abs(fftshift(Idft))+1),[]);
  8. subplot(2,3,3);
  9. imshow(atan2(imag(Idft),real(Idft)),[]);
  10. Iswap = fftshift(I);
  11. Idft = fft2(Iswap);
  12. subplot(2,3,4);
  13. imshow(Iswap,[]);
  14. subplot(2,3,5);
  15. imshow(log(abs(fftshift(Idft))+1),[]);
  16. subplot(2,3,6);
  17. imshow(atan2(imag(Idft),real(Idft)),[]);

                                                                                (全文完)

                                                                                 谢谢收看!

没有更多推荐了,返回首页