四种图像平滑方法 python代码

    科技2024-06-27  65

    1.图像平滑概述

    图像平滑也称为图像去噪,是为了抑制图像噪声改善图像质量进行的处理。这种噪声可能是在图像获取和传输等过程中造成的,噪声会使图像恶化,质量下降,图像模糊,特征淹没,对图像分析很不利。 本文主要介绍四种空间域的图像平滑方法:邻域平均法(均值滤波法),超限像素平滑法,有选择保边缘平滑法和中值滤波法。并且给出案例以及python代码。

    2.邻域平均法(均值滤波法)

    这种方法直接在空间域上进行平滑处理

    思想: 假设图像由许多灰度恒定的小块组成,相邻的像素之间存在很高的空间相关性,而噪声则是统计独立的。所以可以用邻域内各像素的灰度平均值代替像素原来的灰度值,实现图像的平滑。

    假设有一副N×N的图像f(x,y),若平滑图像为g(x,y),那么我们有: 公式中: x,y=0,1,…,N-1 s是(x,y)像素邻域内像素的集合 M表示集合s内像素的个数

    邻域平均法是把当前像素邻域内各像素的灰度平均值作为像素的输出值,用这种方法对图像进行去噪。

    方法特点: 算法简单,但这种方法有一个缺点,就是在降低噪声的同时会使图像产生模糊,特别是在边缘和细节处,而且邻域越大,去噪能力增强的同时模糊程度越严重。

    3.超限像素平滑法

    这种方法对邻域平均法稍加改进。方法把f(x,y)和邻域平均得到的g(x,y)值做一个差的绝对值运算,再与一个选定的阈值进行比较,用比较结果决定像素(x,y)的灰度值g’(x,y)。表达式如下:

    方法特点: 这种方法对抑制椒盐噪声比较有效,对保护仅有微小灰度差的细节和纹理也有效。

    4.有选择保边缘平滑法

    方法对图像上任一像素(x,y)的5×5邻域,采用9个掩膜,其中包括一个3×3正方形、4个五边形和4个六边形。计算各个掩膜的均值和方差,对方差进行排序,最小方差所对应的掩膜区的灰度均值就是像素(x,y)的输出值。 这种方法用方差来测度区域的灰度均匀性。如果区域含有尖锐的边缘,它的灰度方差就会很大,如果不含边缘或者灰度均匀的区域,方差就很小,所以最小方差所对应的区域就是灰度最均匀区域。这种平滑方法既能消除噪声,又不会破坏区域边界的细节。

    另外,五边形和六边形在(x,y)处都有锐角,这样,即使像素(x,y)位于一个复杂形状区域的锐角处,也能找到均匀的区域。从而在平滑时既不会使尖锐边缘模糊,也不会破坏边缘形状。

    5.中值滤波法

    中值滤波法用一个滑动窗口,对窗口内的像素灰度排序,用中值代替窗口中心像素的灰度值,是一种非线性的图像平滑法。

    中值滤波器的窗口形状可以有多种,如线状、方形、十字形、圆形、菱形等。不同形状的窗口会产生不同的滤波效果,在使用的时候需要根据图像的内容和不同的要求进行选择。经验表明方形或圆形的窗口适宜于外轮廓线较长的物体图像,而十字形窗口对有尖顶角状的图像效果好。

    6.案例展示

    首先将原始图片进行灰度化,得到灰度图片,并且添加椒盐噪声

    对添加了椒盐噪声的图像运用邻域平均法进行处理,下图分别为3×3邻域平滑和5×5邻域平滑。可以看出5×5的邻域比3×3的邻域的图像模糊更严重。 对图像运用超限像素平滑法进行处理,取阈值T=50,下图分别为3×3超限像素平滑和5×5超限像素平滑。同邻域平滑法相比,超限像素平滑法去椒盐噪声的效果更好,而且一定程度上可以保护细节,减少图像模糊。 对图像运用有选择保边缘平滑法进行去噪处理,结果如下图所示,可以有效去椒盐噪声。 对图像运用中值滤波法进行处理,下图分别为3×3模板和5×5模板进行中值滤波的结果,可见中值滤波法能有效削弱椒盐噪声,而且比邻域、超限像素平滑法更为有效,可以保留更多的图像细节,减少图像模糊。

    7.代码

    灰度化与添加椒盐噪声

    import cv2 as cv import numpy as np img = cv.imread("image.jpg") h = img.shape[0] w = img.shape[1] # 将彩色图片转换为灰度图片 grayimage = np.zeros((h, w), np.uint8) for i in range(h): for j in range(w): grayimage[i, j] = 0.11 * img[i, j, 0] + 0.59 * img[i, j, 1] + 0.3 * img[i, j, 2] # python中以B G R存储图像 # 添加椒盐噪声 noiseimage = grayimage.copy() SNR = 0.95 # 信噪比 pixels = h * w # 计算图像像素点个数 noise_num = int(pixels * (1 - SNR)) # 计算图像椒盐噪声点个数 for i in range(noise_num): randx = np.random.randint(1, h-1) # 生成一个 1 至 h-1 之间的随机整数 randy = np.random.randint(1, w-1) # 生成一个 1 至 w-1 之间的随机整数 if np.random.random() <= 0.5: # np.random.random()生成一个 0 至 1 之间的浮点数 noiseimage[randx, randy] = 0 else: noiseimage[randx, randy] = 255 cv.imshow("image",img) cv.imshow("grayimage", grayimage) cv.imshow("noiseimage", noiseimage) cv.imwrite("grayimage.jpg", grayimage) cv.imwrite("noiseimage.jpg", noiseimage) cv.waitKey(0) cv.destroyAllWindows()

    图像平滑

    import cv2 as cv import numpy as np img = cv.imread("noiseimage.jpg", 0) h = img.shape[0] w = img.shape[1] # 均值滤波 img_Blur_3 = cv.blur(img, (3, 3)) # 3*3均值滤波 img_Blur_5 = cv.blur(img, (5, 5)) # 5*5均值滤波 # 中值滤波 img_MedianBlur_3 = cv.medianBlur(img, 3) # 3*3中值滤波 img_MedianBlur_5 = cv.medianBlur(img, 5) # 5*5中值滤波 # 超限像素平滑法 def overrun_pixel_smoothing(kernel, image): img_overrun = image.copy() filter = np.zeros((kernel, kernel), np.uint8) average = np.zeros((h - kernel + 1, w - kernel + 1), np.uint8) # 平均值矩阵 for i in range(h - kernel + 1): for j in range(w - kernel + 1): for m in range(kernel): for n in range(kernel): filter[m, n] = img_overrun[i + m, j + n] average[i, j] = 1 / (kernel * kernel) * filter.sum() # 求平均 T = 50 # 设定阈值 for i in range(h - kernel + 1): for j in range(w - kernel + 1): if abs(img[i + kernel - 2, j + kernel - 2] - average[i, j]) > T: img_overrun[i + kernel - 2, j + kernel - 2] = average[i, j] return img_overrun img_overrun_3 = overrun_pixel_smoothing(3, img) # 核大小为3*3 img_overrun_5 = overrun_pixel_smoothing(5, img) # 核大小为5*5 # 有选择保边缘平滑法 img_EdgeKeeping = img.copy() filter = np.zeros((5, 5), np.uint8) for i in range(h - 4): for j in range(w - 4): for m in range(5): for n in range(5): filter[m, n] = img_EdgeKeeping[i + m, j + n] mask = [] # 3*3掩膜 mask.append([filter[1, 1], filter[1, 2], filter[1, 3], filter[2, 1], filter[2, 2], filter[2, 3], filter[3, 1], filter[3, 2], filter[3, 3]]) # 5*5掩膜 mask.append([filter[2, 2], filter[1, 1], filter[1, 2], filter[1, 3], filter[0, 1], filter[0, 2], filter[0, 3]]) mask.append([filter[2, 2], filter[1, 1], filter[2, 1], filter[3, 1], filter[1, 0], filter[2, 0], filter[3, 0]]) mask.append([filter[2, 2], filter[3, 1], filter[3, 2], filter[3, 3], filter[4, 1], filter[4, 2], filter[4, 3]]) mask.append([filter[2, 2], filter[1, 3], filter[2, 3], filter[3, 3], filter[1, 4], filter[2, 4], filter[3, 4]]) # 6*6掩膜 mask.append([filter[2, 2], filter[3, 2], filter[2, 3], filter[3, 3], filter[4, 3], filter[3, 4], filter[4, 4]]) mask.append([filter[2, 2], filter[2, 3], filter[1, 2], filter[1, 3], filter[1, 4], filter[0, 3], filter[0, 4]]) mask.append([filter[2, 2], filter[1, 2], filter[2, 1], filter[1, 1], filter[0, 1], filter[1, 0], filter[0, 0]]) mask.append([filter[2, 2], filter[2, 1], filter[3, 2], filter[3, 1], filter[3, 0], filter[4, 1], filter[4, 0]]) # 求各掩膜的方差 var = [] for k in range(9): var.append(np.var(mask[k])) index = var.index(min(var)) # 方差最小的掩膜对应的索引号 img_EdgeKeeping[i + 2, j + 2] = np.mean(mask[index]) cv.imshow("image", img) cv.imshow("img_Blur_3", img_Blur_3) cv.imshow("img_Blur_5", img_Blur_5) cv.imshow("img_MedianBlur_3", img_MedianBlur_3) cv.imshow("img_MedianBlur_5", img_MedianBlur_5) cv.imshow("img_overrun_3", img_overrun_3) cv.imshow("img_overrun_5", img_overrun_5) cv.imshow("img_EdgeKeeping", img_EdgeKeeping) cv.waitKey(0) cv.destroyAllWindows()
    Processed: 0.019, SQL: 8