先读入提前加了噪点的图,我是用np.random.spickle加的椒盐噪
图像去噪#DCNN去噪Python代码 双边滤波、双边滤波、巴特沃斯高通滤波、同态滤波、各项异性扩散滤波、均值滤波、维纳滤波、小波三级软阈值去噪、自适应中值滤波、高斯滤波 MATLAB程序代码前阵子翻到小时候拍的胶卷扫描照噪点密得像撒了一把细盐本来想靠手机修图APP磨个皮就完结果越磨人脸都糊成马赛克了才想起不如自己写点简单的去噪脚本试试——这就整理了最近摸鱼试的十几种常用去噪方法从傻瓜式的一键滤波到自己搭的DCNN都有代码也直接贴出来嫌麻烦的直接抄就能用。先从入门级的线性滤波玩起最基础的就是均值滤波和高斯滤波属于无脑好用的类型均值滤波就是把窗口里所有像素直接拉平均不管邻域里的像素差多少适合均匀噪点但容易糊边缘高斯滤波给邻域像素加了高斯权重离中心越近的像素越管用保留细节比均值强太多import cv2 import numpy as np noisy_img cv2.imread(old_photo.jpg) # 均值滤波5x5窗口就是周围25个像素平均 mean_filtered cv2.blur(noisy_img, (5,5)) # 高斯滤波sigmaX控制高斯分布的宽度越大越模糊 gaussian_filtered cv2.GaussianBlur(noisy_img, (5,5), sigmaX1.5) cv2.imwrite(mean_result.jpg, mean_filtered) cv2.imwrite(gaussian_result.jpg, gaussian_filtered)我当时跑自己的猫片时发现高斯滤波对付均匀的高斯白噪真的一绝拍夜景的彩噪压得干干净净还没把猫的胡须磨糊但要是碰到那种突然的黑白椒盐噪这俩直接拉胯。对付椒盐噪就靠中值滤波椒盐噪就是那种突然冒出来的黑白点像素值要么特别亮要么特别暗普通线性滤波根本搞不定这时候就得靠中值滤波——把窗口里的像素排序取中间值替换中心像素噪点直接被筛掉。图像去噪#DCNN去噪Python代码 双边滤波、双边滤波、巴特沃斯高通滤波、同态滤波、各项异性扩散滤波、均值滤波、维纳滤波、小波三级软阈值去噪、自适应中值滤波、高斯滤波 MATLAB程序代码普通中值滤波会把细线条磨平所以我更爱用自适应中值会自动调整窗口大小小窗口对付小噪点大窗口对付密集噪点还能保住发丝、边缘这类细节。def adaptive_median_filter(img, s_max7): h, w, c img.shape res np.zeros_like(img) for channel in range(c): for i in range(h): for j in range(w): s 1 while s s_max: # 自动调整窗口范围别超出图片边界 top max(0, i - s) bottom min(h-1, i s) left max(0, j - s) right min(w-1, j s) window img[top:bottom1, left:right1, channel].flatten() window_sorted np.sort(window) med window_sorted[len(window_sorted)//2] min_val, max_val window_sorted[0], window_sorted[-1] # 判断当前像素是不是噪点不是就保留原图 if min_val med max_val: if min_val img[i,j,channel] max_val: res[i,j,channel] img[i,j,channel] else: res[i,j,channel] med break else: s 1 # 到最大窗口还没搞定就直接用中值 if s s_max: res[i,j,channel] med return res # 跑起来试试 adaptive_median_result adaptive_median_filter(noisy_img) cv2.imwrite(adaptive_median.jpg, adaptive_median_result)亲测这个代码对付密集椒盐噪特别好用连猫的胡须都没被磨平比我之前用的手机修图APP强多了。保边去噪选双边滤波之前用这个给奶奶的老照片去噪她脸上的皱纹一点没被磨掉比高斯滤波靠谱太多。原理就是不仅看像素的空间距离还看像素值的差异边缘的像素值差大权重就低所以不会把边缘糊掉。# opencv自带的函数参数d是窗口直径sigmaColor和sigmaSpace控制滤波强度 bilateral_filtered cv2.bilateralFilter(noisy_img, d9, sigmaColor75, sigmaSpace75) cv2.imwrite(bilateral.jpg, bilateral_filtered)唯一的缺点就是慢毕竟要算每个像素的空间和颜色权重处理大尺寸图片得等一会儿但日常修修小图完全ok。对付光照不均的同态滤波老照片经常一边亮一边暗还带着扫描的网点噪点这时候同态滤波就派上用场了。它会把图像拆成光照分量和反射分量先把光照拉平再去掉噪点一举两得。from skimage import exposure, img_as_float def homomorphic_filter(img, cutoff30, gamma_h1.5, gamma_l0.5): img_float img_as_float(img) # 取对数分离光照和反射分量 img_log np.log1p(img_float) f np.fft.fft2(img_log) fshift np.fft.fftshift(f) # 构建巴特沃斯高通滤波器压低频光照分量 rows, cols img.shape[:2] crow, ccol rows//2, cols//2 mask 1 - np.exp(-((np.arange(rows)[:,None]-crow)**2 (np.arange(cols)-ccol)**2)/(2*cutoff**2)) fshift_filtered fshift * mask img_back np.real(np.fft.ifft2(np.fft.ifftshift(fshift_filtered))) # 取指数还原图像 img_exp np.expm1(img_back) return exposure.rescale_intensity(img_exp, out_range(0,1))*255 # 处理灰度图效果最好 homomorphic_result homomorphic_filter(cv2.cvtColor(noisy_img, cv2.COLOR_BGR2GRAY)) cv2.imwrite(homomorphic.jpg, homomorphic_result)我一般把cutoff设成30左右太大容易把细节滤掉太小又拉不平光照调起来也很省心。频域滤波选手巴特沃斯高通滤波维纳滤波巴特沃斯高通滤波专门用来去掉低频的平滑噪点比如扫描的网点纹、背景的模糊噪点也可以用来提取图像边缘from scipy import ndimage def butterworth_highpass(img, cutoff10, order2): img_gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) f np.fft.fft2(img_gray) fshift np.fft.fftshift(f) rows, cols img_gray.shape crow, ccol rows//2, cols//2 # 巴特沃斯高通掩码 mask 1 / (1 (cutoff / np.sqrt((np.arange(rows)[:,None]-crow)**2 (np.arange(cols)-ccol)**2)))**(2*order) img_back np.real(np.fft.ifft2(np.fft.ifftshift(fshift*mask))) return np.uint8(255*(img_back - img_back.min())/(img_back.max()-img_back.min()))维纳滤波比高斯滤波更智能的统计滤波会估计原始图像和噪声的功率谱适合已知噪声方差的场景比如热成像图、相机噪点from skimage.restoration import wiener img_gray cv2.cvtColor(noisy_img, cv2.COLOR_BGR2GRAY) # 先估计一下噪声方差 noise_var np.var(img_gray - cv2.GaussianBlur(img_gray, (5,5), 1.5)) wiener_result wiener(img_gray, balancenoise_var/0.1) cv2.imwrite(wiener.jpg, wiener_result*255)更硬核的保边各项异性扩散滤波也就是Perona-Malik滤波比双边滤波的保边效果还强会根据像素梯度调整扩散速度边缘梯度大的地方扩散慢不会磨糊边缘。def perona_malik_filter(img, iterations50, k50, option1): img img_as_float(img) if len(img.shape) 3: img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) h, w img.shape I img.copy() for _ in range(iterations): # 计算四个方向的像素梯度 N np.zeros_like(I); S np.zeros_like(I) E np.zeros_like(I); W np.zeros_like(I) N[1:,:] I[:-1,:] - I[1:,:] S[:-1,:] I[1:,:] - I[:-1,:] E[:,1:] I[:,:-1] - I[:,1:] W[:,:-1] I[:,1:] - I[:,:-1] # 计算权重两种可选方案 if option 1: c np.exp(-(np.abs([N,S,E,W]))/k) else: c 1/(1 (np.abs([N,S,E,W])/k)**2) # 更新图像 I I 0.25*(c[0]*N c[1]*S c[2]*E c[3]*W) return np.uint8(I*255) pm_result perona_malik_filter(noisy_img, iterations30, k30) cv2.imwrite(pm_filter.jpg, pm_result)这个处理星空图特别好用星星的边缘一点没糊噪点全没了就是迭代次数多的话会慢一点不过胜在效果自然。小波三级软阈值去噪属于频域去噪的天花板之一把图像分解成小波系数把小的噪点系数阈值掉再重构图像对高斯白噪的效果特别好。import pywt def wavelet_soft_threshold_filter(img, waveletdb4, level3, threshold0.2): img_gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 小波三级分解 coeffs pywt.wavedec2(img_gray, wavelet, levellevel) # 软阈值处理细节系数 for i in range(1, level1): cA, (cH, cV, cD) coeffs[i-1], coeffs[i] coeffs[i] (cA, (pywt.threshold(cH, threshold, soft), pywt.threshold(cV, threshold, soft), pywt.threshold(cD, threshold, soft))) # 重构图像 img_recon pywt.waverec2(coeffs, wavelet) return np.uint8(np.clip(img_recon,0,1)*255) wavelet_result wavelet_soft_threshold_filter(noisy_img, threshold0.15) cv2.imwrite(wavelet.jpg, wavelet_result)阈值别设太大不然会把细节磨没我一般用0.1到0.2之间的数小波基用db4就够用不用搞太复杂的。最后上深度学习DCNN去噪传统方法再强也不如深度学习的效果好这里贴一个极简的U-Net风格去噪网络适合新手快速上手import torch import torch.nn as nn import torch.nn.functional as F class DenoiseCNN(nn.Module): def __init__(self): super().__init__() self.conv1 nn.Conv2d(3, 64, 3, padding1) self.conv2 nn.Conv2d(64, 64, 3, padding1) self.conv3 nn.Conv2d(64, 3, 3, padding1) def forward(self, x): x F.relu(self.conv1(x)) x F.relu(self.conv2(x)) x self.conv3(x) return x # 推理的时候直接加载训练好的模型就行 def dcnn_denoise(img, model_pathdenoise_model.pth): model DenoiseCNN() model.load_state_dict(torch.load(model_path, map_locationcpu)) model.eval() img_tensor torch.from_numpy(img.transpose(2,0,1)).float()/255.0 img_tensor img_tensor.unsqueeze(0) with torch.no_grad(): out model(img_tensor) out_img np.clip(out.squeeze(0).numpy().transpose(1,2,0),0,1)*255 return np.uint8(out_img)这个需要用带噪和干净的数据集训练我之前用DIV2K训了一个小模型处理手机拍的噪点图效果比所有传统方法都好就是对新手不太友好不过套现成的预训练模型就能直接用。补一个MATLAB版的常用代码很多做图像处理的同学还是习惯用MATLAB贴个自适应中值滤波的MATLAB版function img_out adaptive_median_filter(img, s_max) if nargin 2 s_max 7; end [h, w, c] size(img); img_out zeros(h, w, c, like, img); for ch 1:c for i 1:h for j 1:w s 1; while s s_max top max(1, i - s); bottom min(h, i s); left max(1, j - s); right min(w, j s); window img(top:bottom, left:right, ch); window_sorted sort(window(:)); med window_sorted(floor(length(window_sorted)/2)1); min_val window_sorted(1); max_val window_sorted(end); if min_val med med max_val if min_val img(i,j,ch) img(i,j,ch) max_val img_out(i,j,ch) img(i,j,ch); else img_out(i,j,ch) med; end break; else s s 1; end end if s s_max img_out(i,j,ch) med; end end end end end % 调用示例 % noisy_img imread(old_photo.jpg); % img_filtered adaptive_median_filter(noisy_img); % imwrite(img_filtered, adaptive_median_matlab.jpg);其实这些方法没有绝对的好坏得看你的噪点类型是什么比如椒盐噪就用中值滤波高斯白噪就用小波或者高斯滤波光照不均的老照片就用同态滤波要是追求效果又有训练数据就上DCNN。我一般都是先试传统方法不行再上深度学习毕竟调参比训模型快多了要是你有更好的去噪方法或者调参踩过的坑欢迎在评论区唠唠。