傅里叶变换去除图像噪声(横纹)(附C++、Python代码)

傅里叶变换去除图像噪声(横纹)

在图像处理领域,噪声的存在通常会降低图像质量,影响视觉效果和后续处理效果。傅里叶变换作为一种强大的数学工具,被广泛应用于图像处理中,特别是在噪声去除方面表现出色。本文将展示如何利用C++、Python中的Opencv来进行傅里叶变换去除图像噪声。

一、傅里叶变换

傅里叶变换是一种将时域(或空间域)信号转换为频域信号的方法。在图像处理中,这意味着将图像从其空间表现(像素值)转换为表征图像中不同频率成分的频谱(P1到P2)。傅里叶变换能够揭示图像中的周期性和非周期性结构。

二、傅里叶变换图像去噪原理

  1. 噪声的频率特性

    • 噪声可以分为高频噪声低频噪声
    • 高频噪声表现为图像中的尖锐、突然变化,例如盐和胡椒噪声(图像中随机分布的黑白像素点)。
    • 低频噪声表现为图像中的缓慢、渐变变化,例如高斯噪声(最常见的噪声类型,其幅度分布符合高斯分布(正态分布)。高斯噪声在自然和工业环境中普遍存在)。
  2. 噪声在频域中的表现

    在进行傅里叶变换时,不同类型的噪声会在频域中以不同的方式表现。高频噪声会在频谱的高频区域中显现,而低频噪声则主要分布在低频区域。

    低频区域-P2中心区域:

    • 低频区域包含了图像的主要信息,如整体结构和形状。
    • 在傅里叶变换的频谱图中,低频成分通常位于频谱的中心区域。
    • 低频区域代表图像中的缓慢变化部分,如渐变的背景或平滑的颜色过渡。

    高频区域-p2周边区域

    • 高频区域包含了图像的细节信息,如边缘、纹理和噪声。
    • 在傅里叶变换的频谱图中,高频成分通常位于频谱的边缘区域。
    • 高频区域代表图像中的快速变化部分,如锐利的边缘、精细的纹理或噪声。
  3. 去噪方法

    • 低通滤波器:保留图像中的低频成分(中心区域),同时滤除高频成分。应用低通滤波器处理图像,通常会得到一幅更加平滑且略显模糊的效果。这是因为图像中的高频细节,诸如尖锐边缘和细微纹理,被有效移除。低通滤波处理赋予图像以柔和、均匀的视觉感受,适合于减少图像噪点和平滑处理。

    • 高通滤波器:保留图像的高频成分(周围区域),同时去除低频部分。当对图像施加高通滤波处理时,往往会显著增强图像的细节和边缘。这种处理方式使图像看起来更为清晰和锐利。但是也会增加图像的噪声,特别是在图像的高对比度区域。

    • 带阻滤波器:根据图像来设计和应用滤波,阻止(滤除)特定频率范围内的信号,同时允许其他频率的信号通过。这种滤波器在去除不需要的频率成分(例如噪声或干扰)时特别有用。(P3就是掩膜,黑色部分为修改像素值,白色部分不变)。

  4. 实施步骤

    1. 将图像进行傅里叶变换,转换到频域。
    2. 应用滤波器去除噪声。
    3. 执行逆傅里叶变换,将处理后的图像还原到空间域。

三、代码展示

分析噪声和频谱图可以发现 横纹部分是 在X=0,Y轴 的有规律的间隔白点,所以建立掩膜将其覆盖。即可去除噪声

代码运行结果如上图所示。

python 复制代码
import numpy as np
import cv2
import matplotlib.pyplot as plt

# 读取图像 - 以灰度模式读取原始图像和C++处理的结果图像
original_img = cv2.imread("D:/fft/img.png", 0)
cpp_result_img = cv2.imread("D:/fft/Reconstructed_Image.jpg", 0)

# 将原始图像转换为浮点型,以便进行傅里叶变换
img_float32 = np.float32(original_img)

# 执行傅里叶变换,并获取复数输出
dft_complex_output = cv2.dft(img_float32, flags=cv2.DFT_COMPLEX_OUTPUT)

# 将傅里叶变换的结果中心化
dft_shifted = np.fft.fftshift(dft_complex_output)

# 计算幅度
magnitude_spectrum = cv2.magnitude(dft_shifted[:,:,0], dft_shifted[:,:,1])

# 对数变换以增强可视化效果
magnitude_spectrum = np.log(magnitude_spectrum + 1)

# 归一化到 [0,255] 范围
magnitude_spectrum = cv2.normalize(magnitude_spectrum, None, 0, 255, cv2.NORM_MINMAX)
magnitude_spectrum = np.uint8(magnitude_spectrum)

# 创建带阻滤波器掩膜
rows, cols = original_img.shape
crows, ccol = int(rows/2), int(cols/2)
high_pass_mask = np.ones((rows, cols, 2), dtype=np.uint8)
high_pass_mask[27:33, ccol-8:ccol+8] = 0
high_pass_mask[57:63, ccol-8:ccol+8] = 0
high_pass_mask[91:97, ccol-8:ccol+8] = 0
high_pass_mask[156:162, ccol-8:ccol+8] = 0
high_pass_mask[187:193, ccol-8:ccol+8] = 0
high_pass_mask[219:225, ccol-8:ccol+8] = 0

# 应用掩膜到傅里叶变换结果
fshift = dft_shifted  * high_pass_mask

# 逆傅里叶变换
f_ishift = np.fft.ifftshift(fshift)

magnitude_spectrum_result = cv2.magnitude(f_ishift[:,:,0], f_ishift[:,:,1])

# 对数变换以增强可视化效果
magnitude_spectrum_result  = np.log(magnitude_spectrum_result + 1)

# 归一化到 [0,255] 范围
magnitude_spectrum_normalized  = cv2.normalize(magnitude_spectrum_result , None, 0, 255, cv2.NORM_MINMAX)
magnitude_spectrum_normalized  = np.uint8(magnitude_spectrum_normalized )
img_back = cv2.idft(f_ishift)

# 将复数转换回实数
img_back_magnitude = cv2.magnitude(img_back[:,:,0], img_back[:,:,1])

# 归一化图像到 [0,255] 范围
img_back_normalized  = cv2.normalize(img_back_magnitude, None, 0, 255, cv2.NORM_MINMAX)
img_back_normalized  = np.uint8(img_back_normalized )



# 使用Matplotlib显示所有图像
plt.figure(figsize=(16, 16))
plt.subplot(2, 3, 1), plt.imshow(original_img, cmap='gray'), plt.title('P1 Original Image')
plt.subplot(2, 3, 2), plt.imshow(magnitude_spectrum, cmap='gray'), plt.title('P2 Original Magnitude Spectrum')
plt.subplot(2, 3, 3), plt.imshow(high_pass_mask[:,:,0], cmap='gray'), plt.title('P3 High Pass Filter Mask')
plt.subplot(2, 3, 4), plt.imshow(magnitude_spectrum_result, cmap='gray'), plt.title('P4 Filtered Magnitude Spectrum')
plt.subplot(2, 3, 5), plt.imshow(cpp_result_img, cmap='gray'), plt.title('P5 Filtered Image (C++)')
plt.subplot(2, 3, 6), plt.imshow(img_back_normalized, cmap='gray'), plt.title('P6 Filtered Image(Python)')
plt.show()
c++ 复制代码
#include <opencv2/opencv.hpp>  
  
using namespace cv;  
using namespace std;  
  
int main() {  
// 读取图像  
Mat img = imread("D:/fft/img.png", IMREAD_GRAYSCALE);  
Mat img1 = imread("D:/fft/processed_and_resized_img.png", IMREAD_GRAYSCALE);  
  
// 傅里叶变换  
Mat planes[] = {Mat_<float>(img), Mat::zeros(img.size(), CV_32F)};  
Mat complexImg;  
merge(planes, 2, complexImg);  
dft(complexImg, complexImg);  
  
// 中心化  
int cx = complexImg.cols / 2;  
int cy = complexImg.rows / 2;  
Mat q0(complexImg, Rect(0, 0, cx, cy));  
Mat q1(complexImg, Rect(cx, 0, cx, cy));  
Mat q2(complexImg, Rect(0, cy, cx, cy));  
Mat q3(complexImg, Rect(cx, cy, cx, cy));  
Mat tmp;  
q0.copyTo(tmp);  
q3.copyTo(q0);  
tmp.copyTo(q3);  
q1.copyTo(tmp);  
q2.copyTo(q1);  
tmp.copyTo(q2);  
  
// 计算幅度  
split(complexImg, planes);  
magnitude(planes[0], planes[1], planes[0]);  
Mat mag = planes[0];  
  
// 对数变换  
mag += Scalar::all(1);  
log(mag, mag);  
  
// 归一化  
normalize(mag, mag, 0, 255, NORM_MINMAX);  
convertScaleAbs(mag, mag);  
  
// 创建高通滤波器掩膜  
Mat mask = Mat::ones(mag.size(), CV_32F);  
int x=cx-6;  
int width=16;  
int height=8;  
mask(Rect(x, 27, width, height)) = 0;  
mask(Rect(x, 57, width, height)) = 0;  
mask(Rect(x, 91, width, height)) = 0;  
mask(Rect(x, 156,width, height)) = 0;  
mask(Rect(x, 187,width, height)) = 0;  
mask(Rect(x, 219,width, height)) = 0;  
Mat visualizedMask;  
normalize(mask, visualizedMask, 0, 255, NORM_MINMAX);  
visualizedMask.convertTo(visualizedMask, CV_8U);  
// 应用掩膜到傅里叶变换结果  
Mat toMerge[] = {mask, mask};  
Mat complexMask;  
merge(toMerge, 2, complexMask);  
mulSpectrums(complexImg, complexMask, complexImg, 0);  
  
// 逆傅里叶变换  
idft(complexImg, complexImg);  
  
// 将复数转换回实数  
split(complexImg, planes);  
magnitude(planes[0], planes[1], planes[0]);  
  
// 归一化图像到 [0,255] 范围  
normalize(planes[0], planes[0], 0, 255, NORM_MINMAX);  
convertScaleAbs(planes[0], planes[0]);  
imshow("Original Image", img);  
imshow("Spectrum", mag);  
imshow("Visualized Mask", visualizedMask);  
imshow("Reconstructed Image", planes[0]);  
cv::imwrite("Reconstructed_Image.jpg", planes[0]);  
waitKey();  
  
return 0;  
}
相关推荐
yuanbenshidiaos7 分钟前
C++----------函数的调用机制
java·c++·算法
唐叔在学习12 分钟前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA31 分钟前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo33 分钟前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc40 分钟前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
小陈phd2 小时前
OpenCV学习——图像融合
opencv·计算机视觉·cv
游是水里的游2 小时前
【算法day20】回溯:子集与全排列问题
算法
yoyobravery2 小时前
c语言大一期末复习
c语言·开发语言·算法
Jiude2 小时前
算法题题解记录——双变量问题的 “枚举右,维护左”
python·算法·面试
被AI抢饭碗的人2 小时前
算法题(13):异或变换
算法