Opencv 实现图像的离散傅里叶变换(DFT)、卷积运算(相关滤波)
我是做Tracking 的,对于速度要求非常高。发现傅里叶变换能够使用。
于是学习之。
核心: 最根本的一点就是将时域内的信号转移到频域里面。这样时域里的卷积能够转换为频域内的乘积!
在分析图像信号的频率特性时,对于一幅图像,直流分量表示预想的平均灰度。低频分量代表了大面积背景区域和缓慢变化部分,高频部分代表了它的边缘,细节,跳跃部分以及颗粒噪声. 因此,我们能够做对应的锐化和模糊的处理:提出当中的高频分量做傅里叶逆变换得到的就是锐化的结果。
提出当中的低频分量做傅里叶逆变换得到的就是模糊的结果。
最不能理解的应该是:截取频域图中的不论什么一个区域相应的都是原来的整张图的区域。而不是相应的局部。
由于频域内的各个点都反映的是整张图的一个状态。
我们能够用时间和频率来理解:当你走完一段单位路程的时候。如果你花了100秒,那么你的频率就是0.01HZ。
这个0.01HZ显然体现的是一个总体的结果。而不是局部。
我们再由公式来看:
能够非常明显的知道频域内的每个点的值都是由整个图像求出来的。当然以上得出的结果,我们一般仅仅关注幅值频谱图。
也就是说真正起作用的就是前面的那个cos x而已. 于是我们能够知道。在整个范围内(0<k <N, 0<l <N),低频分量集中于四个角。
且其它地方的值仅仅可能比这个小。
在原点的傅里叶变换即等于图像的平均灰度级。由于 在原点处经常为零,F(0,0)有时称做 频率谱的直流成分。
使用:
当图像的尺寸是2,3,5的整数倍时,计算速度最快。因此opencv里面有一个函数:
它能够使得图片的尺寸能够满足这个要求。
可是这样就须要对原来的图像进行大小的处理,因此使用函数:CopyMakeBorder复制图像而且制作边界。
(处理边界卷积)
将原始的图像I 扩充为理想的大小放在padded里面。
接下来我们须要给计算出来的结果分配空间:
然后便能够进行傅里叶变换了:
得到的结果有两部分。实数部分和虚数部分,你能够分别对这两部分进行操作:
当然还能够进行:归一化:
另外重要的一个应用是: convolveDFT。
当中的 *代表的是 卷积。我认为这也是我们进行离散傅里叶变换的目的。
使得计算的速度大大的添加。
先来说一下卷积在图像中的意义:
如果图像f(x),模板是g(x),然后将模版g(x)在模版中移动,每到一个位置,就把f(x)与g(x)的定义域相交的元素进行乘积而且求和,得出新的图像一点,就是被卷积后的图像. 模版又称为卷积核.卷积核做一个矩阵的形状.(当然边缘点可能须要特殊的处理,同一时候这个操作和滤波也非常像,或许就是一回事)。
#include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> using namespace cv; using namespace std; //http://docs.opencv.org/modules/core/doc/operations_on_arrays.html#dft[2] void convolveDFT(Mat A, Mat B, Mat& C) { // reallocate the output array if needed C.create(abs(A.rows - B.rows)+1, abs(A.cols - B.cols)+1, A.type()); Size dftSize; // calculate the size of DFT transform dftSize.width = getOptimalDFTSize(A.cols + B.cols - 1); dftSize.height = getOptimalDFTSize(A.rows + B.rows - 1); // allocate temporary buffers and initialize them with 0's Mat tempA(dftSize, A.type(), Scalar::all(0));//initial 0 Mat tempB(dftSize, B.type(), Scalar::all(0)); // copy A and B to the top-left corners of tempA and tempB, respectively 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); // now transform the padded A & B in-place; // use "nonzeroRows" hint for faster processing dft(tempA, tempA, 0, A.rows); dft(tempB, tempB, 0, B.rows); // multiply the spectrums; // the function handles packed spectrum representations well mulSpectrums(tempA, tempB, tempA, DFT_COMPLEX_OUTPUT); //mulSpectrums(tempA, tempB, tempA, DFT_REAL_OUTPUT); // transform the product back from the frequency domain. // Even though all the result rows will be non-zero, // you need only the first C.rows of them, and thus you // pass nonzeroRows == C.rows dft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows); // now copy the result back to C. tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C); // all the temporary buffers will be deallocated automatically } int main(int argc, char* argv[]) { const char* filename = argc >=2 ? argv[1] : "Lenna.png"; Mat I = imread(filename, CV_LOAD_IMAGE_GRAYSCALE); if( I.empty()) return -1; Mat kernel = (Mat_<float>(3,3) << 1, 1, 1, 1, 1, 1, 1, 1, 1); cout << kernel; Mat floatI = Mat_<float>(I);// change image type into float Mat filteredI; convolveDFT(floatI, kernel, filteredI); normalize(filteredI, filteredI, 0, 1, CV_MINMAX); // Transform the matrix with float values into a // viewable image form (float between values 0 and 1). imshow("image", I); imshow("filtered", filteredI); waitKey(0); }
当中:
C.create(abs(A.rows - B.rows)+1, abs(A.cols - B.cols)+1, A.type());C 为什么是这种勒?想想一个特殊的样例就知道了:当A,B尺寸相等的时候,这个时候的高斯滤波得到的也就是中心点的那一个值(卷积核滤波的区别在于须要绕中心180度旋转)。
MulSpectrums 是对于两张频谱图中每个元素的乘法。
void cvMulSpectrums( const CvArr* src1, const CvArr* src2, CvArr* dst, int flags ); src1 第一输入数组 src2 第二输入数组 dst 输出数组,和输入数组有同样的类型和大小。 flags 以下列举的值的组合: CV_DXT_ROWS - 把数组的每一行视为一个单独的频谱 (參见 cvDFT 的參数讨论). CV_DXT_MUL_CONJ - 在做乘法之前取第二个输入数组的共轭.第四个參数flag值没有指定,应指定为DFT_COMPLEX_OUTPUT或是DFT_REAL_OUTPUT.
參考资料:
http://blog.sina.com.cn/s/blog_4bdb170b01019atv.html
http://www.cnblogs.com/xianglan/archive/2010/12/30/1922386.html
http://www.cnblogs.com/tornadomeet/archive/2012/07/26/2610414.html
http://blog.csdn.net/ubunfans/article/details/24787569
http://blog.csdn.net/lichengyu/article/details/18848281
相关文章
- OpenCv 人脸检測的学习
- 基于OpenCV实现图像的月光效果(调整图像的饱和度和亮度)
- 详解OpenCV的视频背景/前景分割(背景建模/前景提取)类cv::bgsegm::BackgroundSubtractorGMG,并利用它实现对道路监控视频前景的提取
- 详解OpenCV的视频背景/前景分割(背景建模/前景提取)类cv::BackgroundSubtractorMOG2,并利用它实现对道路监控视频前景/背景的提取
- 详解OpenCV的函数imread()和函数imshow(),并利用它们实现对图像的读取和显示
- 不用CMake编译查询OpenCV函数、类定义等的方法(全文搜索-所有文档搜索指定内容神器:CJC超级硬盘快搜索和AnyTXT Searcher)
- Python-OpenCV中不用函数split()的实现图像多通道分离的方法——ndraary对象切片实现
- 利用OpenCV的函数warpAffine()做图像的仿射变换【实现图像的平移、缩放、旋转、翻转等操作】
- 图像线性变换的原理及基于OpenCV的C++实现
- 通过圆的颜色并结合霍夫变换检测目标圆的OpenCV代码
- 利用OpenCV实现图像拼接的代码
- 详解OpenCV的函数adaptiveThreshold(),并利用它实现图像的自适应二值化阈值分割
- OpenCV实现FloodFill泛洪填充算法的代码及相关函数详解
- OpenCV中的cv::String和CString互相转换
- OpenCV图像阈值、图像滤波、双阈值、上下采样
- OpenCV cv2.putText实现字符串换行'n'
- OpenCV实现Mat与vector,Mat与数组互转
- OpenCV图像旋转,指定填充背景颜色边界颜色
- opengl学习笔记(四):openCV读入图片,openGL实现纹理贴图
- OpenCV C++双目三维重建:双目摄像头实现双目测距
- Android Opencv 之 Android Studio 进行 opencv ( 4.2.0) 环境搭建,并简单进行图片灰度处理demo
- OpenCV Open Camera 打开摄像头
- 【图像处理】——Python+opencv实现二值图像的轮廓边界跟踪以及轮廓面积周长的求解(findcontours函数和contourArea函数)
- Java实现Opencv源码以及启动appium的两种方式
- python实现opencv(清华镜像)
- (原)netbeans中添加anaconda3安装的opencv
- opencv学习笔记