写在前面: 刚开始接触数字图像处理频率域滤波时,很是陌生,感觉这个技术使用范围很窄,不如空域直接处理来的实在,最近看书发现有些情况又不得不在频率域中进行操作,个人感觉图像的复原与重建就是最大的应用点。特此实现一些基本的频率域滤波操作为后学习打下基础…
1. 频率域滤波步骤
前处理: 包括对图像边界填充,使之达到OpenCV傅里叶变换最佳尺寸,然后就是将
f
(
x
,
y
)
f(x,y)
f(x,y)乘以
−
1
(
x
+
y
)
-1^{(x+y)}
−1(x+y),使傅里叶变换位于填充后图像大小的频率矩形的中心。
后处理: 包括从反变换回来的图的左上象限提取一个大小为MXN(原图尺寸)的区域,得到与输入图像大小相同的滤波后的结果
g
(
x
,
y
)
g(x,y)
g(x,y)。
具体操作步骤详见《数字图像处理》第4章。
2.总结
-
H
(
u
,
v
)
H(u,v)
H(u,v)是实函数,即虚部为0。
-
G
(
u
,
v
)
G(u,v)
G(u,v) =
H
(
u
,
v
)
⋅
F
(
u
,
v
)
H(u,v)\cdot F(u,v)
H(u,v)⋅F(u,v)=
H
(
u
,
v
)
⋅
(
R
(
u
,
v
)
+
J
I
(
u
,
v
)
)
H(u,v)\cdot (R(u,v)+JI(u,v))
H(u,v)⋅(R(u,v)+JI(u,v)) =
H
(
u
,
v
)
⋅
R
(
u
,
v
)
+
H
(
u
,
v
)
⋅
J
I
(
u
,
v
)
H(u,v)\cdot R(u,v)+H(u,v)\cdot JI(u,v)
H(u,v)⋅R(u,v)+H(u,v)⋅JI(u,v)
- 反变换获得图像
g
(
x
,
y
)
g(x,y)
g(x,y)取实部即可。
- 在进行高通滤波时,滤波后的图像中会含有负值,这时直接将像素值归一化到[0, 1],得到的结果同时标定了正值和负值,图像呈现深灰色,虽然也对,但是为了呈现另一种效果(底色为黑)我将负值进行了手动截断为0,然后在进行归一化。
3.程序实现
void fftshift(const cv::Mat& src, cv::Mat& dst)
{
dst = src.clone();
if (dst.type() != CV_32F) dst.convertTo(dst, CV_32F);
for (int r = 0; r < dst.rows; ++r)
{
for (int c = 0; c < dst.cols; ++c)
{
if ((r + c) % 2 != 0)
dst.at<float>(r, c) = -dst.at<float>(r, c);
}
}
}
cv::Size Make_BetterSize(const cv::Mat src)
{
int w = cv::getOptimalDFTSize(src.cols);
int h = cv::getOptimalDFTSize(src.rows);
return Size(w, h);
}
void MinusToZero(const cv::Mat& src, cv::Mat& dst)
{
dst = src.clone();
for (int r = 0; r < dst.rows; ++r)
{
for (int c = 0; c < dst.cols; ++c)
{
if (dst.at<float>(r, c) < 0)
dst.at<float>(r, c) = 0.0;
}
}
}
void calcILPF(const cv::Mat& src, int D0, Mat& dst)
{
Size filterSize = Make_BetterSize(src);
Mat H(filterSize, CV_32FC1, Scalar::all(0));
int cy = H.rows / 2;
int cx = H.cols / 2;
for (int y = 0; y < H.rows; ++y)
{
float* H_data = H.ptr<float>(y);
for (int x = 0; x < H.cols; ++x)
{
double d = sqrt(pow(y - cy, 2) + pow(x - cx, 2));
if (d <= (double)D0)
H_data[x] = 1.f;
}
}
Mat planes[] = { H.clone(), H.clone() };
Mat complexH;
merge(planes, 2, complexH);
complexH.copyTo(dst);
}
void calcButterWorth(const cv::Mat& src, int D0, Mat& dst, float n)
{
Size filterSize = Make_BetterSize(src);
cv::Mat H = cv::Mat::zeros(filterSize, CV_32FC1);
int cy = H.rows / 2;
int cx = H.cols / 2;
for (int y = 0; y < H.rows; ++y)
{
float* H_data = H.ptr<float>(y);
for (int x = 0; x < H.cols; ++x)
{
double d = sqrt(pow(y - cy, 2) + pow(x - cx, 2));
H_data[x] = 1.0 / (1 + pow(d / D0, 2 * n));
}
}
Mat planes[] = { H.clone(), H.clone() };
Mat complexH;
merge(planes, 2, complexH);
complexH.copyTo(dst);
}
void calcGLPF(const cv::Mat& src, int D0, Mat& dst)
{
Size filterSize = Make_BetterSize(src);
cv::Mat H = cv::Mat::zeros(filterSize, CV_32FC1);
int cy = H.rows / 2;
int cx = H.cols / 2;
for (int y = 0; y < H.rows; ++y)
{
float* H_data = H.ptr<float>(y);
for (int x = 0; x < H.cols; ++x)
{
double d = sqrt(pow(y - cy, 2) + pow(x - cx, 2));
H_data[x] = expf((-d * d) / (2 * D0 * D0));
}
}
Mat planes[] = { H.clone(), H.clone() };
Mat complexH;
merge(planes, 2, complexH);
complexH.copyTo(dst);
}
void calcGHPF(const cv::Mat& src, int D0, Mat& dst)
{
Size filterSize = Make_BetterSize(src);
cv::Mat H = cv::Mat::ones(filterSize, CV_32FC1);
int cy = H.rows / 2;
int cx = H.cols / 2;
for (int y = 0; y < H.rows; ++y)
{
float* H_data = H.ptr<float>(y);
for (int x = 0; x < H.cols; ++x)
{
double d = sqrt(pow(y - cy, 2) + pow(x - cx, 2));
H_data[x] = 1.f - expf((-d * d) / (2 * D0 * D0));
}
}
Mat planes[] = { H.clone(), H.clone() };
Mat complexH;
merge(planes, 2, complexH);
complexH.copyTo(dst);
}
void frequency_filter(const cv::Mat& InputImage, const cv::Mat& H, cv::Mat& OutputImage)
{
cv::Mat padded;
int m = getOptimalDFTSize(InputImage.rows);
int n = getOptimalDFTSize(InputImage.cols);
cv::copyMakeBorder(InputImage, padded, 0, m - InputImage.rows, 0, n - InputImage.cols, BORDER_CONSTANT, Scalar::all(0));
cv::Mat planes[] = { Mat_<float>(padded), cv::Mat::zeros(padded.size(), CV_32F) };
fftshift(planes[0], planes[0]);
cv::Mat complexF;
cv::merge(planes, 2, complexF);
cv::dft(complexF, complexF);
cv::Mat complexFH;
cv::Mat ifft;
cv::multiply(complexF, H, complexFH);
cv::idft(complexFH, ifft, DFT_REAL_OUTPUT);
fftshift(ifft, ifft);
MinusToZero(ifft, ifft);
normalize(ifft, ifft, 0, 1, NORM_MINMAX);
ifft.convertTo(ifft, CV_8UC1, 255);
ifft(cv::Rect(0, 0, InputImage.cols, InputImage.rows)).copyTo(OutputImage);
}
int main()
{
string path = "blurring_test.tif";
Mat SrcImage = imread(path, IMREAD_GRAYSCALE);
if (!SrcImage.data) {
std::cout << "Could not open or find the image" << std::endl;
return -1;
}
Mat H_outimg, H_kernel_H;
Mat L_outimg, L_kernel_H;
calcGLPF(SrcImage, 60, L_kernel_H);
frequency_filter(SrcImage, L_kernel_H, L_outimg);
calcGHPF(SrcImage, 80, H_kernel_H);
frequency_filter(SrcImage, H_kernel_H, H_outimg);
imwrite("low_blurring_test.png", L_outimg);
imwrite("high_blurring_test.png", H_outimg);
cv::waitKey(0);
return 0;
}
4. 测试结果
原图:
高斯低通滤波:
高斯高通滤波:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)