# 【图像处理笔记】傅里叶变换,【图像处理笔记】总目录,图,傅里叶变换

【图像处理笔记】总目录

## 3 应用

### 3.1 频域滤波

```#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main() {
clock_t start = clock();
imshow("src", src);
// 填充到傅里叶变换最佳尺寸 （gpu下加速明显）
int h = getOptimalDFTSize(src.rows);
int w = getOptimalDFTSize(src.cols);
copyMakeBorder(src, padded, 0, h - src.rows, 0, w - src.cols, BORDER_CONSTANT, Scalar::all(0));

// 中心化 （在时域做中心化，傅里叶变换后的频谱图低频在中心）
for (int i = 0; i < padded.rows; i++)
{
for (int j = 0; j < padded.cols; j++)
{
pMat.at<float>(i, j) = pow(-1, i + j) * padded.at<uchar>(i, j);
}
}

// 傅里叶变换
Mat planes[] = { pMat, Mat::zeros(padded.size(), CV_32F) };
Mat complexImg;
merge(planes, 2, complexImg);
dft(complexImg, complexImg, DFT_COMPLEX_OUTPUT);

// 显示频谱图
split(complexImg, planes);
Mat magMat;
magnitude(planes[0], planes[1], magMat);
magMat += 1;
log(magMat, magMat);
normalize(magMat, magMat, 0, 255, NORM_MINMAX);
magMat.convertTo(magMat, CV_8UC1);

// 低通滤波
//Mat mask(Size(w, h), CV_32FC2, Scalar(0, 0));
//circle(mask, Point(w / 2, h / 2), 50, Scalar(255, 255), -1);

//高通滤波
Mat mask(Size(w, h), CV_32FC2, Scalar(255, 255));
circle(mask, Point(w / 2, h / 2), 50, Scalar(0, 0), -1);

// 傅里叶反变换
Mat dst, tmp;
idft(complexImg, tmp, DFT_REAL_OUTPUT);

// 去中心化
for (int i = 0; i < padded.rows; i++)
{
for (int j = 0; j < padded.cols; j++)
{
pMat.at<float>(i, j) = pow(-1, i + j) * tmp.at<float>(i, j);
}
}
dst = pMat({ 0,0,src.cols ,src.rows }).clone();
normalize(dst, dst, 0, 1, NORM_MINMAX);
dst.convertTo(dst, CV_8U, 255);
imshow("dst", dst);
clock_t end = clock();
cout << "spend time:" << end - start << "ms" << endl;
waitKey(0);
return 0;
}```

### 3.3 倾斜文本校正

```#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

double point2Line(Point2f pointP, Point2f pointA, Point2f pointB)
{
//求直线方程
double A = 0, B = 0, C = 0;
A = pointA.y - pointB.y;
B = pointB.x - pointA.x;
C = pointA.x * pointB.y - pointA.y * pointB.x;
//代入点到直线距离公式
double distance = 0;
double tmp1 = abs(A * pointP.x + B * pointP.y + C);
double tmp2 = ((float)sqrtf(A * A + B * B));
distance = tmp1 / tmp2;
return distance;
}
int main() {
vector<string> filenames;
glob("./", filenames);
for (int n = 0; n < filenames.size(); n++) {
string filename = filenames[n];
// 填充到傅里叶变换的最佳尺寸
int h = getOptimalDFTSize(src.rows);
int w = getOptimalDFTSize(src.cols);
copyMakeBorder(src, padded, 0, h - src.rows, 0, w - src.cols, BORDER_CONSTANT, Scalar::all(0));
// 傅里叶变换
Mat complexImg;
merge(planes, 2, complexImg);
dft(complexImg, complexImg, DFT_COMPLEX_INPUT | DFT_COMPLEX_OUTPUT);
// 求幅值
split(complexImg, planes);
Mat magMat;
magnitude(planes[0], planes[1], magMat);
magMat += 1;
log(magMat, magMat);
normalize(magMat, magMat, 0, 255, NORM_MINMAX);
magMat.convertTo(magMat, CV_8UC1);
// 把零频移到中心
Mat magImg = magMat.clone();
int cx = magImg.cols / 2;
int cy = magImg.rows / 2;
Mat q1 = magImg({ 0, 0, cx, cy });
Mat q2 = magImg({ 0, cy, cx, cy });
Mat q3 = magImg({ cx, 0, cx, cy });
Mat q4 = magImg({ cx, cy, cx, cy });
Mat temp;
q1.copyTo(temp);
q4.copyTo(q1);
temp.copyTo(q4);
q2.copyTo(temp);
q3.copyTo(q2);
temp.copyTo(q3);
// 霍夫直线检测有角度的斜线
Mat binImg;
threshold(magImg, binImg, 150, 255, THRESH_BINARY);
Mat markImg;
cvtColor(binImg, markImg, COLOR_GRAY2BGR);
vector<Vec4i> lines;
Vec4i l;
HoughLinesP(binImg, lines, 1, CV_PI / 180.0, 30, 200, 50);
Point2f p = Point2f(magImg.cols / 2.0, magImg.rows / 2.0);
for (int i = 0; i < lines.size(); i++)
{
if (abs(lines[i][1] - lines[i][3]) > 15) {
line(markImg, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(0, 255, 0), 1, 8, 0);
l = lines[i];
}
}
float theta = atan((l[1] - l[3]) * 1.0 / (l[0] - l[2]) * src.cols / src.rows) * 180 / CV_PI;
float angle = theta <= 0 ? theta + 90 : theta - 90;
// 放射变换
Point2f center = Point2f(src.cols / 2.0, src.rows / 2.0);
Mat rotMat = getRotationMatrix2D(center, angle, 1.0);
Mat dst = Mat::ones(src.size(), CV_8UC1);
warpAffine(src, dst, rotMat, src.size(), 1, 0, Scalar(255, 255, 255));
imshow("src", src);
imshow("markImg", markImg);
imshow("dst", dst);
waitKey(0);
}
return 0;
}```

## 4 Q&A

### Q: 为什么用图像二维傅里叶变换的相位谱进行反变换，能够大致得到原图的形状，而幅度谱则不行呢？

k空间中储存的是一个复数，其幅度代表平面波的波动的大小，相位代表平面波的相位也就是偏离原点的多少。从k空间回复图像的时候，是将每个复平面乘上对应的复系数，相加而成。可以分为两步：（1）乘上波动大小（幅度）（2）移动相应的距离（相位）

```...
Mat complexImg;
merge(planes, 2, complexImg);
dft(complexImg, complexImg, DFT_COMPLEX_OUTPUT);
split(complexImg, planes);
Mat magMat;
magnitude(planes[0], planes[1], magMat);
// 相角重建
//divide(planes[0], magMat, planes[0]);
//divide(planes[1], magMat, planes[1]);
//谱重建
planes[0] = magMat;
merge(planes, 2, complexImg);
Mat dst, tmp;
idft(complexImg, tmp, DFT_REAL_OUTPUT);
...```

### Q: 傅里叶变换为什么要填充0？

A: (1)提升运算性能；(2)时域补零相当于频域插值，补零操作增加了频域的插值点数，让频域曲线看起来更加光滑，也就是增加了FFT频率分辨率。从傅里叶变换公式可以看出，频域的点数和时域的点数是一样的，时域补零后，采样点增加。详见快速傅里叶变换(FFT)中为什么要“补零”？

### Q: 什么是频率（谱）泄漏？

1. 冈萨雷斯《数字图像处理（第四版）》Chapter4（所有图片可在链接中下载）

原文作者：湾仔码农
原文地址: https://www.cnblogs.com/Fish0403/p/16938691.html
本文转自网络文章，转载此文章仅为分享知识，如有侵权，请联系博主进行删除。