离散傅里叶变换
视频讲解如下:
在本章节中给大家演示离散傅里叶变换的操作,C++、C#版本是使用毛星云的代码流程,主要博主懒得写Python版本了,转换太麻烦了,就CSDN上找了一个demo,不过CSDN这个demo讲的是真清楚。大伙儿可以学习学习。C#版本CSDN上也有类似的。
CSDN上的C# OpenCv 离散傅里叶变换操作demo,这个demo和毛星云的基本上是一样的:
https://blog.csdn.net/weixin_43950082/article/details/123946606?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165798155416781435469088%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165798155416781435469088&biz_id=&utm_medium=distribute.pc_search_result.none-task-code-2~all~first_rank_ecpm_v1~pc_rank_34-2-123946606-0-null-null.142^v32^pc_rank_34,185^v2^control&utm_term=OpenCVSharp%20%E5%82%85%E7%AB%8B%E5%8F%B6%E5%8F%98%E6%8D%A2
CSDN上的Python OpenCv 离散傅里叶变换操作demo:
https://blog.csdn.net/qq_45832961/article/details/124175063
当前系列所有demo下载地址:
https://github.com/GaoRenBao/OpenCv4-Demo
不同编程语言对应的OpenCv版本以及开发环境信息如下:
语言 | OpenCv版本 | IDE |
C# | OpenCvSharp4.4.8.0.20230708 | Visual Studio 2022 |
C++ | OpenCv-4.5.5-vc14_vc15 | Visual Studio 2022 |
Python | OpenCv-Python (4.6.0.66) | PyCharm Community Edition 2022.1.3 |
首先呢,博主准备了一张测试图片,运行效果如下。
C#、C++运行效果如下:
网上找的python版本:
C#版本代码如下:
using OpenCvSharp;
namespace demo
{
internal class Program
{
static void Main(string[] args)
{
//【1】以灰度模式读取原始图像并显示
Mat srcImage = Cv2.ImRead("../../../images/girl.jpg", 0);
Cv2.ImShow("原始图像", srcImage);
//【2】将输入图像延扩到最佳的尺寸,边界用0补充
int m = Cv2.GetOptimalDFTSize(srcImage.Rows);
int n = Cv2.GetOptimalDFTSize(srcImage.Cols);
Mat padded = new Mat();
Cv2.CopyMakeBorder(srcImage, padded, 0, m - srcImage.Rows, 0, n - srcImage.Cols, BorderTypes.Constant, Scalar.All(0));
//【3】为傅立叶变换的结果(实部和虚部)分配存储空间。
//将planes数组组合合并成一个多通道的数组complexI
Mat paddedF32 = new Mat();
padded.ConvertTo(paddedF32, MatType.CV_32F);
Mat[] planes = { paddedF32, Mat.Zeros(padded.Size(), MatType.CV_32F) };
Mat complex = new Mat();
Cv2.Merge(planes, complex);
//【4】进行就地离散傅里叶变换
Cv2.Dft(complex, complex);
//【5】将复数转换为幅值,即=> log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
Cv2.Split(complex, out planes);
Mat magnitudeImage = new Mat();
Cv2.Magnitude(planes[0], planes[1], magnitudeImage);
//【6】进行对数尺度(logarithmic scale)缩放
magnitudeImage += Scalar.All(1);
Cv2.Log(magnitudeImage, magnitudeImage);//求自然对数
//【7】剪切和重分布幅度图象限
//若有奇数行或奇数列,进行频谱裁剪
magnitudeImage = magnitudeImage[new Rect(0, 0, magnitudeImage.Cols & -2, magnitudeImage.Rows & -2)];
//重新排列傅立叶图像中的象限,使得原点位于图像中心
int cx = magnitudeImage.Cols / 2;
int cy = magnitudeImage.Rows / 2;
Mat q0 = new Mat(magnitudeImage, new Rect(0, 0, cx, cy)); // ROI区域的左上
Mat q1 = new Mat(magnitudeImage, new Rect(cx, 0, cx, cy)); // ROI区域的右上
Mat q2 = new Mat(magnitudeImage, new Rect(0, cy, cx, cy)); // ROI区域的左下
Mat q3 = new Mat(magnitudeImage, new Rect(cx, cy, cx, cy)); // ROI区域的右下
//交换象限(左上与右下进行交换)
Mat tmp = new Mat();
q0.CopyTo(tmp);
q3.CopyTo(q0);
tmp.CopyTo(q3);
//交换象限(右上与左下进行交换)
q1.CopyTo(tmp);
q2.CopyTo(q1);
tmp.CopyTo(q2);
//【8】归一化,用0到1之间的浮点值将矩阵变换为可视的图像格式
Cv2.Normalize(magnitudeImage, magnitudeImage, 0, 1, NormTypes.MinMax);
//【9】显示效果图
Cv2.ImShow("频谱幅值", magnitudeImage);
Cv2.WaitKey();
}
}
}
C++版本代码如下:
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
int main()
{
//【1】以灰度模式读取原始图像并显示
Mat srcImage = imread("../images/girl.jpg", 0);
if (!srcImage.data)
{
printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n");
return false;
}
imshow("原始图像", srcImage);
//【2】将输入图像延扩到最佳的尺寸,边界用0补充
int m = getOptimalDFTSize(srcImage.rows);
int n = getOptimalDFTSize(srcImage.cols);
//将添加的像素初始化为0.
Mat padded;
copyMakeBorder(srcImage, padded, 0, m - srcImage.rows, 0, n - srcImage.cols, BORDER_CONSTANT, Scalar::all(0));
//【3】为傅立叶变换的结果(实部和虚部)分配存储空间。
//将planes数组组合合并成一个多通道的数组complexI
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI);
//【4】进行就地离散傅里叶变换
dft(complexI, complexI);
//【5】将复数转换为幅值,即=> log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); // 将多通道数组complexI分离成几个单通道数组,planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magnitudeImage = planes[0];
//【6】进行对数尺度(logarithmic scale)缩放
magnitudeImage += Scalar::all(1);
log(magnitudeImage, magnitudeImage);//求自然对数
//【7】剪切和重分布幅度图象限
//若有奇数行或奇数列,进行频谱裁剪
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);
//【8】归一化,用0到1之间的浮点值将矩阵变换为可视的图像格式
normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);
//【9】显示效果图
imshow("频谱幅值", magnitudeImage);
waitKey(0);
return 0;
}
Python版本代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 以灰度形式读入
img = cv2.imread('../images/girl.jpg', 0)
# 使用cv2.dft()进行傅里叶变换
dst = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
# 将变换后图像的低频部分转移到图像的中心
dst_center = np.fft.fftshift(dst)
# 使用cv2.magnitude将实部和虚部转换为实部,乘以20是为了使得结果更大
result = 20 * np.log(np.abs(cv2.magnitude(dst_center[:, :, 0], dst_center[:, :, 1])))
# 显示图像
plt.subplot(121)
plt.imshow(img, cmap="gray")
plt.axis("off")
plt.subplot(122)
plt.axis("off")
plt.imshow(result, cmap="gray")
plt.show()