[toc]
实验:自适应均值滤波器

1、自适应均值滤波器的工作原理

自适应中值滤波器的核心思想是根据当前像素的噪声情况动态调整滤波窗口的大小,并且根据窗口内像素值的分布决定是否执行中值滤波。具体步骤如下:

  1. 定义滤波窗口大小:通常从最小的窗口开始(如 3x3),然后逐步增大窗口尺寸。
  2. 窗口内的中值计算
    • 找出窗口内的最小值 (Z_min)、最大值 (Z_max) 和中值 (Z_med)。
    • 判断是否满足以下条件: Z_min < Z_med < Z_max。如果满足,继续下一步;否则,增大窗口尺寸重新计算。
  3. 判断当前像素是否为噪声
    • 如果 Z_min < 当前像素值 < Z_max,保留当前像素值;
    • 如果不满足该条件,则将当前像素值替换为窗口的中值值 Z_med。
  4. 调整窗口大小
    • 如果窗口达到了预设的最大尺寸还未满足噪声判断条件,则返回窗口的中值值。

2、滤波公式

f^(x,y)=g(x,y)−ση2σL2[g(x,y)−mL] \hat{f}(x, y) = g(x, y) - \frac{\sigma^2_{\eta}}{\sigma^2_{L}} \left[g(x, y) - m_L\right] f^(x,y)=g(x,y)σL2ση2[g(x,y)mL]

  • g(x,y)g(x,y)g(x,y): 输入图像的灰度值。
  • ση2\sigma^2_{\eta}ση2: 噪声方差。
  • σL2\sigma^2_{L}σL2: 局部区域的方差。
  • mLm_LmL: 局部区域的均值。

2.1 情况分析

当(ση2=0)时,(f^(x,y)=g(x,y)); 当 (\sigma^2_{\eta} = 0) 时,(\hat{f}(x, y) = g(x, y)); (ση2=0)时,(f^(x,y)=g(x,y));

当(ση2=σL2)时,(f^(x,y)=mL),即算术均值 当 (\sigma^2_{\eta} = \sigma^2_{L}) 时,(\hat{f}(x, y) = m_L),即算术均值 (ση2=σL2)时,(f^(x,y)=mL),即算术均值
当(ση2<σL2)时,(f^(x,y)=(1−λ)g(x,y)+λmL),加权平均。 当 (\sigma^2_{\eta} < \sigma^2_{L}) 时,(\hat{f}(x, y) = (1 - \lambda)g(x, y) + \lambda m_L),加权平均。 (ση2<σL2)时,(f^(x,y)=(1λ)g(x,y)+λmL),加权平均。

λλλ: 权重因子,用于加权平均。

2.2 加权因子

λ=ση2σL2 \lambda = \frac{\sigma^2_{\eta}}{\sigma^2_{L}} λ=σL2ση2

在公式中的“λλλ”是一个加权因子,用于控制输入图像的灰度值 g(x,y)g(x,y)g(x,y)和局部均值 mLm_LmL 的权重。它通常定义为:

通过调节 λλλ,我们可以控制滤波后的结果,使其更接近原始灰度值$ g(x,y)$ 或均值mLm_LmL

3.手动实现自适应均值滤波器

3.1 主体部分算法流程

符号及算法流程

符号定义:(zmin,zmax,zmed,zxy,Smax)( z_{\text{min}}, z_{\text{max}}, z_{\text{med}}, z_{xy}, S_{\text{max}} )(zmin,zmax,zmed,zxy,Smax)


阶段 A: 找到一个非脉冲的中值

  1. Do {
  2. A1=zmed−zmin A_1 = z_{\text{med}} - z_{\text{min}} A1=zmedzmin
  3. A2=zmed−zmax A_2 = z_{\text{med}} - z_{\text{max}} A2=zmedzmax
  4. if zmin<zmed<zmax z_{\text{min}} < z_{\text{med}} < z_{\text{max}} zmin<zmed<zmax,则转到阶段 B
  5. else Sxy=Sxy+2 S_{xy} = S_{xy} + 2 Sxy=Sxy+2
  6. while Sxy≤Smax S_{xy} \leq S_{\text{max}} SxySmax
  7. return zmed z_{\text{med}} zmed

阶段 B: 输出原值或中值

  1. B1=zxy−zmin B_1 = z_{xy} - z_{\text{min}} B1=zxyzmin
  2. B2=zxy−zmax B_2 = z_{xy} - z_{\text{max}} B2=zxyzmax
  3. if zmin<zxy<zmax z_{\text{min}} < z_{xy} < z_{\text{max}} zmin<zxy<zmax,则 return zxy z_{xy} zxy
  4. else return zmed z_{\text{med}} zmed

3.2 读取图片数据

这里为了方便使用Opencv的库函数来读取图片数据。

为了防止图片读取时为RGB三通道模式(需按通道滤波,较为麻烦),这里无论什么图片,在读取时统一转换为灰度图格式

import cv2 as cv
import numpy as np

# 读取灰度图像
ori_image = cv.imread('test.png', cv.IMREAD_GRAYSCALE)

在这里插入图片描述

3.3 定义自适应均值滤波器函数

定义一个滤波器函数记作adaptive_median_filter

adaptive_median_filter(img, max_filter_size)

参数:

  • ​ img (numpy.ndarray): 输入的灰度图像。

  • ​ max_filter_size (int): 滤波器的最大尺寸(必须为奇数)。

返回:

  • ​ output(numpy.ndarray): 经过滤波的输出图像

1、首先定义一个和输入相同大小矩阵作为输出矩阵,并且将输入图片的长宽通过.shape方式取出来

output = np.copy(img)  
height, width = img.shape

2、遍历输入图像的每一个像素

for i in range(height):
        for j in range(width):

3、在循环里定义初始窗口的大小(3x3)

filter_size = 3  # 初始窗口大小

4、判断窗口大小是否超过最大窗口值 max_filter_size

  while filter_size <= max_filter_size:

5、确定滤波窗口内的元素

提取了图像中以某个像素(i,j)(i,j)(i,j) 为中心的一个子窗口,用于进行滤波操作。它确保窗口在图像边界之外不会溢出,这样即使窗口中心像素靠近图像边界,窗口的尺寸也会自动调整,避免越界。

 half_filter_size = filter_size // 2
#对于3×3 的窗口,half_size 为 1                
# 获取当前像素(i, j)的滤波窗口区域
filter_window = img[max(i - half_filter_size, 0):min(i + half_filter_size + 1, height),
                    max(j - half_filter_size, 0):min(j + half_filter_size + 1, width)]

行范围max(i - half_size, 0):min(i + half_size + 1, height)

  • i - half_size 计算窗口上边界位置,如果上边界超出图像(负数),则使用 max 函数将其限制在 0 以上,防止越界。
  • i + half_size + 1 计算窗口下边界位置,如果下边界超出图像底部,则使用 min 函数限制在图像高度以内(height)。

列范围max(j - half_size, 0):min(j + half_size + 1, width)

  • j - half_size 计算窗口左边界位置,使用 max 函数防止越界(负数)。
  • j + half_size + 1 计算窗口右边界位置,使用 min 函数限制在图像宽度以内(width)。

6、主体算法实现部分

 # 计算窗口的最小值、中值和最大值
    Z_min = filter_window.min()
    Z_max = filter_window.max()
    Z_med = np.median(filter_window)
    Z_xy = img[i, j]

    # 阶段 A:找到一个非脉冲的中值
    if Z_min < Z_med < Z_max:
        # 阶段 B:判断当前像素是否为脉冲
        if Z_min < Z_xy < Z_max:
            output[i, j] = Z_xy  # 当前像素不是脉冲,保持原值
        else:
            output[i, j] = Z_med  # 当前像素为脉冲,替换为中值
            break  # 跳出 while 循环
        else:
            # 增加窗口尺寸
            filter_size += 2

       # 如果窗口达到最大尺寸,仍未找到合适的中值,则使用当前窗口的中值
     if filter_size > max_filter_size:
         output[i, j] = Z_med

7、总的函数实现

def adaptive_median_filter(img, max_filter_size):

    output = np.copy(img)  # 初始化输出图像为输入图像的副本
    height, width = img.shape
    # 遍历图像的每个像素
    for i in range(height):
        for j in range(width):
            
            filter_size = 3  # 初始窗口大小
            while filter_size <= max_filter_size:
                half_filter_size = filter_size // 2
                
                # 获取当前像素(i, j)的滤波窗口区域
                filter_window = img[max(i - half_filter_size, 0):min(i + half_filter_size + 1, height),
                                    max(j - half_filter_size, 0):min(j + half_filter_size + 1, width)]
                
                # 计算窗口的最小值、中值和最大值
                Z_min = filter_window.min()
                Z_max = filter_window.max()
                Z_med = np.median(filter_window)
                Z_xy = img[i, j]
                
                # 阶段 A:找到一个非脉冲的中值
                if Z_min < Z_med < Z_max:
                    # 阶段 B:判断当前像素是否为脉冲
                    if Z_min < Z_xy < Z_max:
                        output[i, j] = Z_xy  # 当前像素不是脉冲,保持原值
                    else:
                        output[i, j] = Z_med  # 当前像素为脉冲,替换为中值
                    break  # 跳出 while 循环
                else:
                    # 增加窗口尺寸
                    filter_size += 2
            
            # 如果窗口达到最大尺寸,仍未找到合适的中值,则使用当前窗口的中值
            if filter_size > max_filter_size:
                output[i, j] = Z_med

    return output

3.4 应用滤波器并保存图像

# 应用自适应中值滤波器
filtered_img = adaptive_median_filter(img, max_filter_size=7)

# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Filtered', filtered_img)

# 保存图像
cv2.imwrite('filtered_image.jpg', filtered_img)
print("滤波后的图像已保存为 'filtered_image.jpg'")

cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

4.总结

自适应中值滤波器是一种图像去噪算法,主要用于去除椒盐噪声(salt-and-pepper noise)。相比于传统的中值滤波器,自适应中值滤波器通过动态调整滤波窗口的大小,能更有效地处理不同程度的噪声。

优点

  • 自适应性:在不同噪声密度下能够自动调整窗口大小,适应性更强。

  • 保边性:较好地保留图像细节,不容易模糊边缘。

应用

自适应中值滤波器在医学影像处理、卫星图像处理等需要保留细节的领域中有广泛应用。

附页:代码部分

# -*- coding: utf-8 -*-
"""
Created on Sat Nov  9 01:57:00 2024

@author: zhaoyi
"""
import cv2
import numpy as np

def adaptive_median_filter(img, max_filter_size):
    
    height, width = img.shape
    output = np.copy(img)  # 初始化输出图像为输入图像的副本

    # 遍历图像的每个像素
    for i in range(height):
        for j in range(width):
            
            filter_size = 3  # 初始窗口大小
            while filter_size <= max_filter_size:
                half_filter_size = filter_size // 2
                
                # 获取当前像素(i, j)的滤波窗口区域
                filter_window = img[max(i - half_filter_size, 0):min(i + half_filter_size + 1, height),
                                    max(j - half_filter_size, 0):min(j + half_filter_size + 1, width)]
                
                # 计算窗口的最小值、中值和最大值
                Z_min = filter_window.min()
                Z_max = filter_window.max()
                Z_med = np.median(filter_window)
                Z_xy = img[i, j]
                
                # 阶段 A:找到一个非脉冲的中值
                if Z_min < Z_med < Z_max:
                    # 阶段 B:判断当前像素是否为脉冲
                    if Z_min < Z_xy < Z_max:
                        output[i, j] = Z_xy  # 当前像素不是脉冲,保持原值
                    else:
                        output[i, j] = Z_med  # 当前像素为脉冲,替换为中值
                    break  # 跳出 while 循环
                else:
                    # 增加窗口尺寸
                    filter_size += 2
            
            # 如果窗口达到最大尺寸,仍未找到合适的中值,则使用当前窗口的中值
            if filter_size > max_filter_size:
                output[i, j] = Z_med

    return output

# 读取灰度图像
ori_image = cv2.imread('11.jpg', cv2.IMREAD_GRAYSCALE)

# 应用自适应中值滤波器
filtered_img = adaptive_median_filter(ori_image, 7)

# 显示结果
cv2.imshow('Original', ori_image)
cv2.imshow('Filtered', filtered_img)

# 保存图像
cv2.imwrite('filtered_image.jpg', filtered_img)
print("滤波后的图像已保存为 'filtered_image.jpg'")

cv2.waitKey(0)
cv2.destroyAllWindows()

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐