Opencv——DFT变换(实现两个Mat的卷积以及显示Mat的频域图像)

DFT原理:(单变量离散傅里叶变换)

数学基础:
任何一个函数都可以转换成无数个正弦和余弦函数的和的形式。
通常观察傅里叶变换后的频域函数可以获得两个重要的信息:幅频曲线和相频曲线。
在数字图像处理中的作用:
在数字图像处理中,对一张图片进行傅里叶变换后我们获得的是:实数图像(幅度图像)+虚数图像(相位图像)
傅里叶变换在数字图像处理中将空间域信息转为频域信息。
如果需要得到图像中的几何结构信息,就要用到离散傅里叶变换

在频域中高频和低频意义:

高频代表了图像的细节、纹理信息,(噪声)
低频代表图像的轮廓信息。
所以低通滤波器可以得到轮廓。

傅里叶变换可以应用的场景:

图像的增强与图像去噪
图像分割的边缘检测
图像特征提取
图像压缩

Opencv中离散傅里叶变换函数:

dft(input,output,flags,nonzeroRows);
//flags:标识符
//nonzeroRows:默认值为0,非零时,表示你想处理的那一行C.rows,计算时更加高效

标识符

dft函数应用实例

例1:用dft函数计算两个二维实矩阵卷积

例子包含的小知识点
1、Size类型

CvSize结构表示矩形尺寸的结构,结构体中分别定义了矩形的宽度和高度,具体定义如下:
typedef struct CvSize {
int width; /* 矩形宽度,单位为象素 /
int height; /
矩形高度,单位为象素 */
}CvSize;

2、getOptimalDFTSize()函数

返回DFT最优尺寸大小:getOpimalDFTSize()
函数返回给定向量尺寸的傅里叶最优尺寸大小、
input:向量尺寸,即图像的rows\cols

3、mulSpectrums()函数

void cvMulSpectrums( const CvArr* src1, const CvArr* src2, CvArr* dst, int flags );
src1
第一输入数组
src2
第二输入数组
dst
输出数组,和输入数组有相同的类型和大小。
flags
下面列举的值的组合:
DFT_COMPLEX_OUTPUT- 把数组的每一行视为一个单独的频谱 (参见 cvDFT 的参数讨论).
DFT_REAL_OUTPUT - 在做乘法之前取第二个输入数组的共轭.
函数 cvMulSpectrums 执行两个 CCS-packed 或者实数或复数傅立叶变换的结果复数矩阵的每个元素的乘法。

4、dft只能处理浮点数,所以需要将输入图像转为float类型
全部代码:

void convolveDFT(Mat& A,Mat& B, Mat& C)
{//【1】初始化输出矩阵C.create(abs(A.rows - B.rows) + 1, abs(A.cols - B.cols) + 1, A.type());Size dftSize;	//???什么意思//【2】计算DFT变换的尺寸dftSize.width = getOptimalDFTSize(A.cols + B.cols - 1);dftSize.height = getOptimalDFTSize(A.rows + B.rows - 1);//【3】分配临时缓冲区并初始化置零Mat tempA(dftSize, A.type(), Scalar::all(0));Mat tempB(dftSize, B.type(), Scalar::all(0));//【4】分别复制A和B到tempA和tempB的左上角Mat roiA(tempA, Rect(0, 0, A.cols, A.rows));A.copyTo(roiA);Mat roiB(tempB, Rect(0, 0, B.cols, B.rows));B.copyTo(roiB);//【5】就地操作,进行快速傅里叶变换,并将nonzeroRows参数置为非零,以进行更加快速的处理???为什么dft(tempA, tempA, 0, A.rows);dft(tempB, tempB, 0, B.rows);//【6】将得到的频谱相乘,结果存放于tempA中mulSpectrums(tempA,tempB,tempA, DFT_COMPLEX_OUTPUT);//DFT_REAL_OUTPUT//【7】将结果变换为频域且尽管行结果都为非零,我们只需要其中C.rows的第一行,所以采用nonzeroRows==C.rowsdft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows);//【8】将结果复制到C中tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C);//所有的临时缓冲区将被自动释放,所以无须收尾操作
}
int main()
{SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);		//字体为绿色//载入原图Mat srcImage = imread("D:\\opencv_picture_test\\形态学操作\\coin_inv.png", 0);	//读取灰度图Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, 1, 1, 1, 1, 1);cout << kernel;Mat floatI = Mat_<float>(srcImage);// change image type into floatMat filteredI;convolveDFT(floatI, kernel, filteredI);normalize(filteredI, filteredI, 0, 1,NORM_MINMAX); // Transform the matrix with float values into a// viewable image form (float between values 0 and 1).//imshow("image", srcImage);imshow("filtered", filteredI);		//这里显示报错,但是可以用ImageWatch查看,暂时不知道原因waitKey(0);return 0;
}

参考链接:
https://blog.csdn.net/lichengyu/article/details/18848281

利用傅里叶变换卷积和利用核游走整个图像进行卷积运算的区别

一般求法中,利用核游走整个图像进行卷积运算,实际上进行的是相关运算,真正意义上的卷积,应该首先把核翻转180度,再在整个图像上进行游走。OpenCV中的filter2D实际上做的也只是相关,而非卷积。

例2:显示一幅图像傅里叶变换后的频域图像

需要用的函数:
1、扩充图像边界:copyMakeBorder()

C++: void copyMakeBorder (InputArray src, OutputArray dst, int top, int bottom, int left, int right,int borderType, const Scalar&
value=Scalar () );
top
bottom
left
right
分别表示在四个方向上扩充多少个像素。
第七个参数:边界类型,常见取值BORDER_CONSTANT;
第八个参数:默认值为0,当边界类型取值为BORDER_CONSTANT时,这个参数表示边界值。

2、计算二维矢量的幅值:magnitude()

C++: void magnitude (InputArray X,InputArray Y, OutputArray magnitude)
X:矢量实部
Y:矢量虚部
magnitude:输出幅值,与x有相同的尺寸和类型
算法原理

3、log()函数

C++: void 1og (InputArray srC, OutputArray dst)
计算每个数组元素绝对值的自然对数。
傅里叶变换的幅值范围大到不适合在屏幕上显示。
为了凸显出高低变化的连续性,可以用对数尺度来替换线性尺度。

3、矩阵归一化:normalize()函数

void cv::normalize(InputArry src,InputOutputArray dst,double alpha=1,double beta=0,int norm_type=NORM_L2,int dtype=-1,InputArray mark=noArry())
alpha 用来规范值或者规范范围,并且是下限;
beta 只用来规范范围并且是上限,因此只在NORM_MINMAX中起作用;
1.NORM_L1、NORM_INF、NORM_L2模式下归一化结果与beta无关,只与alpha有关,详见第4部分的公式说明;
2.NORM_MINMAX中alpha、beta都起作用,同时需要注意的是alpha和beta的取值顺序与归一化结果无关。即alpha=255,beta=0和alpha=0,beta=255最后的归一化结果是相同的。

归一化公式:
公式
而其中的dtype为负数时,输出数组的type与输入数组的type相同;
否则,输出数组与输入数组只是通道数相同,而tpye=CV_MAT_DEPTH(dtype).

参考链接

实现流程:
1、载入原图
2、将图像扩大到合适的尺寸(当图像的尺寸是2.3.5的整数倍时,运行速度最快)
//将输入图像延扩到最佳尺寸,边界用0补充
3、为傅里叶变换的结果(实部和虚部)分配存储空间
4、进行离散傅里叶变化
5、将复数转化为幅值
6、进行对数尺度缩放(傅里叶变换的幅值范围大到不适合在屏幕上显示。为了凸显出高低变化的连续性,可以用对数尺度来替换线性尺度。)
公式:M1 = log(1+M);
7、剪切和重分布幅度图像限
因为在第二部中延扩了图像,现在需要将添加的像素剔除
为了方便显示,也可以重新分布幅度图像象限位置(将第五步得到的幅度图从中间划开得到4张1/4子图像,将每张子图都看成幅度图的一个象限,重新分布即将4个交点重叠到图像中心)这样的话原点(0,0)就为一道图像中心了。
//剪切和重分布幅度图像限
//如有奇数行或奇数列,进行频谱裁剪
//重新排列傅里叶图像中的象限,使得原点位于图像中心。
//交换象限(左上与右下进行交换)
//交换象限(左下与右上进行交换)
8、归一化
这一步仍然是为了显示。现在有了重分布后的幅度图,但是幅度值仍然超过了可显示范围【0,1】。这里使用归一化函数。
9、显示效果图
全部代码:

#include <opencv2/opencv.hpp>
#include <iostream>
#include "windows.h"
#include <stdio.h>
//#include "My_ImageProssing_base.h"using namespace cv;
using namespace std;void convolveDFT(Mat& A,Mat& B, Mat& C)
{//【1】初始化输出矩阵C.create(abs(A.rows - B.rows) + 1, abs(A.cols - B.cols) + 1, A.type());Size dftSize;	//???什么意思//【2】计算DFT变换的尺寸dftSize.width = getOptimalDFTSize(A.cols + B.cols - 1);dftSize.height = getOptimalDFTSize(A.rows + B.rows - 1);//【3】分配临时缓冲区并初始化置零Mat tempA(dftSize, A.type(), Scalar::all(0));Mat tempB(dftSize, B.type(), Scalar::all(0));//【4】分别复制A和B到tempA和tempB的左上角Mat roiA(tempA, Rect(0, 0, A.cols, A.rows));A.copyTo(roiA);Mat roiB(tempB, Rect(0, 0, B.cols, B.rows));B.copyTo(roiB);//【5】就地操作,进行快速傅里叶变换,并将nonzeroRows参数置为非零,以进行更加快速的处理???为什么dft(tempA, tempA, 0, A.rows);dft(tempB, tempB, 0, B.rows);//【6】将得到的频谱相乘,结果存放于tempA中mulSpectrums(tempA,tempB,tempA, DFT_COMPLEX_OUTPUT);//DFT_REAL_OUTPUT//【7】将结果变换为频域且尽管行结果都为非零,我们只需要其中C.rows的第一行,所以采用nonzeroRows==C.rowsdft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows);//【8】将结果复制到C中tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C);//所有的临时缓冲区将被自动释放,所以无须收尾操作
}
int main()
{SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);		//字体为绿色//1、载入原图Mat srcImage = imread("D:\\opencv_picture_test\\形态学操作\\coin_inv.png",0);	//读取灰度图//2、将图像扩大到合适的尺寸(当图像的尺寸是2.3.5的整数倍时,运行速度最快)//【2】将输入图像延扩到最佳尺寸,边界用0补充int m = getOptimalDFTSize(srcImage.rows);int n = getOptimalDFTSize(srcImage.cols);//将添加的像素初始化为0Mat padded;copyMakeBorder(srcImage, padded, 0, m - srcImage.rows, n - srcImage.cols, BORDER_CONSTANT,0);//3、为傅里叶变换的结果(实部和虚部)分配存储空间Mat planes[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32F) };Mat complexI;merge(planes, 2, complexI);//4、进行离散傅里叶变化dft(complexI, complexI);//5、将复数转化为幅值split(complexI, planes);//将多通道数组complexI分离成几个单通道数//planes[0] = Re(DFT(I));//planes[1] = Im(DFT(I));//计算矢量幅值magnitude(planes[0], planes[1], planes[0]);//将幅值存入planes[0] Mat magnitudeImage = planes[0];//6、进行对数尺度缩放magnitudeImage += Scalar::all(1);log(magnitudeImage, magnitudeImage);//就地操作,求自然对数//7、剪切和重分布幅度图像限magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols & -2, magnitudeImage.rows & -2));//这个&-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);		//将q0与q3互换q3.copyTo(q0);tmp.copyTo(q3);//交换象限(左下与右上进行交换)q1.copyTo(tmp);		//将q1与q2互换q2.copyTo(q1);tmp.copyTo(q2);//8、归一化//这一步仍然是为了显示。现在有了重分布后的幅度图,但是幅度值仍然超过了可显示范围【0, 1】。这里使用归一化函数。normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);//9、显示效果图imshow("原图", srcImage);imshow("频谱幅值", magnitudeImage);waitKey(0);return 0;
}

srcImage:
原图
padded:
padded
magnitudeImage:
magnitudeImage
每个图像的具体性质:
每个图像的具体性质

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378683.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于(Python下的OpenCV)图像处理的喷墨墨滴形状规范检测

通过图像处理&#xff0c;分析数码印花的喷头所喷出来的墨滴形状&#xff0c;与标准墨滴形状对比分析&#xff0c;来判断墨水及其喷头设备的状态&#xff0c;由两部分构成 PS&#xff1a;获取墨滴形状照片和标准墨滴形状照片都是手绘的&#xff0c;将就的看吧&#xff0c;主要…

微机原理——指令系统——传送类指令(MOV、LEA、LDS、LES、LAHF、SAHF、XCHG、XLAT、PUSH、POP、PUSHF、POPF)

博主联系方式&#xff1a; QQ:1540984562 QQ交流群&#xff1a;892023501 群里会有往届的smarters和电赛选手&#xff0c;群里也会不时分享一些有用的资料&#xff0c;有问题可以在群里多问问。 【没事儿可以到我主页看看】https://blog.csdn.net/qq_42604176 传送类指令1&…

mysql 任务计划 /etc/cron.d_Linux /etc/cron.d增加定时任务

一般情况下我们添加计划任务时&#xff0c;都是直接修改/etc/crontab。但是&#xff0c;不建议这样做&#xff0c;/etc/cron.d目录就是为了分项目设置计划任务而创建的。例如&#xff0c;增加一项定时的备份任务&#xff0c;我们可以这样处理&#xff1a;在/etc/cron.d目录下新…

19-Harris角点检测

角点检测顾名思义&#xff0c;就是对类似顶点的检测&#xff0c;与边缘有所区别 边缘可能在某一方向上变化不是特别明显&#xff0c;但角点在任何方向上变换都很明显 cv2.cornerHarris(img,blockSize,ksize,k) cv2.cornerHarris(gray,2,3,0.04) 参数一&#xff1a;img&#xff…

微机原理——指令系统——算数运算指令(ADD、ADC、SUB、SBB、INC、DEC、NEG、CMP、MUL、IMUL、DIV、IDIV、CBW、CWD、BCD调整)

博主联系方式&#xff1a; QQ:1540984562 QQ交流群&#xff1a;892023501 群里会有往届的smarters和电赛选手&#xff0c;群里也会不时分享一些有用的资料&#xff0c;有问题可以在群里多问问。 算数运算指令1、加减法指令ADD、ADC 、SUB 、SBB 和增量减量指令INC、DEC、NEGADD…

20-SIFT算法

import cv2 import numpy as np from matplotlib import pyplot as pltdef show_photo(name,picture):#图像显示函数cv2.imshow(name,picture)cv2.waitKey(0)cv2.destroyAllWindows()img cv2.imread(E:\Jupyter_workspace\study\data/cfx.png) gray cv2.cvtColor(img,cv2.COL…

mysql 迁移 nosql_从关系型Mysql到Nosql HBase的迁移实践

2013年11月22-23日&#xff0c;作为国内唯一专注于hadoop技术与应用分享的大规模行业盛会&#xff0c;2013 Hadoop中国技术峰会(China Hadoop Summit 2013)于北京福朋喜来登集团酒店隆重举行。来自国内外各行业领域的近千名CIO、CTO、架构师、IT经理、咨询顾问、工程师、Hadoop…

21-特征匹配方法(Brute-Force蛮力匹配)

Brute-Force蛮力匹配 cv2.BFMatcher(crossCheck True) crossCheck表示两个特征点相互匹配 例如A中的第i个特征点与B中的第j个特征点最近&#xff0c;并且B中的第j个特征点到A中的第i个特征点也是 NORM_L2&#xff1a;归一化数组的(欧几里得距离)&#xff0c;如果其他特征计算…

Opencv——几何空间变换(仿射变换和投影变换)

几何空间变换【1】几何变换&#xff08;空间变换&#xff09;简述【2】变换矩阵知识简述齐次坐标的概念几何运算矩阵【3】图像的仿射变换1、平移变换2、比例缩放3、旋转4、对称变换&#xff08;不做展示&#xff09;1、关于X轴变换2、关于Y轴变换3、关于直线YX变换4、关于直线Y…

probuffer java_Protocol Buffer的使用

Probotbuf简介在网络通信和通用数据交换等应用场景中经常使用的技术是 JSON 或 XML&#xff0c;这两种技术常被用于数据的结构化呈现和序列化。我们可以从两个方面来看JSON 和 XML与protobuf的异同&#xff1a;一个是数据结构化&#xff0c;一个是数据序列化。这里的数据结构化…

22-随机抽样一致算法RANSAC

随机抽样一致算法(Random sample consensus&#xff0c;RANSAC) 看似复杂&#xff0c;其基本思想就是&#xff1a;随机选取俩点&#xff0c;然后连接&#xff0c;给定一个容忍范围&#xff0c;在这个范围内的点越多越好&#xff0c;然后不断的迭代进行找两点之间容忍范围内点最…

23-背景建模

帧差法 由于场景中的目标在运动&#xff0c;目标的影像在不同图像帧中的位置不同。该类算法对时间上连续的两帧图像进行差分运算&#xff0c;不同帧对应的像素点相减&#xff0c;判断灰度差的绝对值&#xff0c;当绝对值超过一定阈值时&#xff0c;即可判断为运动目标&#xf…

DB2 9 运用开辟(733 考试)认证指南,第 3 部门: XML 数据独霸(4)

议决运用顺序存储和检索 XMLXML 编码字符编码在汗青上&#xff0c;术语 字符集、字符编码 和 码页 都有雷同的意义&#xff1a;一个字符集和一个二进制码集&#xff0c;其中每个码示意一个字符。&#xff08;码页是来自 IBM 的一个术语&#xff0c;示意一个大型主机或 IBM PC 上…

Opencv——霍夫变换以及遇到的一些问题

目录问题1 &#xff1a;颜色空间转换函数参数问题&#xff1a;CV_BGR2GRAY vs CV_GRAY2BGR问题2&#xff1a;cvRound()、cvFloor()、cvCeil()函数用法霍夫变换的含义标准霍夫直线变换霍夫线变换函数参数讲解累计概率霍夫变换霍夫变换圆变换原理和算法步骤&#xff1a;霍夫圆变换…

java ssm如何上传图片_ssm整合-图片上传功能(转)

本文介绍 ssm (SpringSpringMVCMybatis)实现上传功能。以一个添加用户的案例介绍(主要是将上传文件)。一、需求介绍我们要实现添加用户的时候上传图片(其实任何文件都可以)。文件名&#xff1a;以 博客名日期的年月日时分秒毫秒形式命名如 言曌博客2017082516403213.png路径&am…

24-光流估计

光流是空间运动物体在观测成像平面上的像素运动的“瞬间速度”&#xff0c;根据各个像素点的速度矢量特征&#xff0c;可以对图像进行动态分析&#xff0c;例如目标跟踪 亮度恒定&#xff1a;同一点随着时间的变化&#xff0c;其亮度不会发生改变 小运动&#xff1a;随着时间的…

java公平索非公平锁_java中的非公平锁不怕有的线程一直得不到执行吗

首先来看公平锁和非公平锁&#xff0c;我们默认使用的锁是非公平锁&#xff0c;只有当我们显示设置为公平锁的情况下&#xff0c;才会使用公平锁&#xff0c;下面我们简单看一下公平锁的源码&#xff0c;如果等待队列中没有节点在等待&#xff0c;则占有锁&#xff0c;如果已经…

mybatis.net - 5 嵌入资源与引用资源

在SqlMap.config文件中可以有两种方式引入外部的文件。 一种是通过资源的方式&#xff0c;在文件中表现为 resource&#xff0c;就是引用外部的文件&#xff0c;这里需要保证文件的路径正确。 <sqlMaps><sqlMap resource"Maps/ProductMap.xml"/><sqlM…

图解MySQL数据库的陈列和把持-4

泉源&#xff1a;网海拾贝 填入一些测试数据&#xff1a; 封闭“MySQL Query Browser”&#xff0c;再从头翻开它&#xff0c;切换到testtable表&#xff0c;看到了没有&#xff1f;刚刚输出的中文变成了“&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&am…

非常好友(C++)

Bessie和其他的所有奶牛的耳朵上都戴有一个射频识别&#xff08;RFID&#xff09;序列号码牌。因此农夫John可以机械化地计算他们的数量。很多奶牛都有一个“牛友”。如果奶牛A的序列号的约数之和刚好等于奶牛B的序列号&#xff0c;那么A的牛友就是B。在这里&#xff0c;一个数…