中值滤波器(Median filter)特性及其实现

中值滤波器(Median filter)特性及其实现

信号处理时经常要做的一件事就是滤波,其中线性滤波器比如FIR、IIR 等类型都是研究的比较透彻的,实际使用中也有很好的效果。但是有时我们遇到的信号的噪声比较顽固,比如说电子信号中的爆米花噪声(popcorn noise)还有图像处理中的椒盐噪声(salt-and-pepper noise),用普通的线性滤波器只能将其压低,而无法彻底消除。这时一些非线性滤波器就体现出优势来了。比如说今天要介绍的中值滤波器。

中值滤波器在图像处理领域用的比较多,其实这种滤波器也可以用于一维信号,有时甚至能起到意想不到的效果。

基本原理

中值滤波器的想法很简单,如果一个信号是平缓变化的,那么某一点的输出值可以用这点的某个大小的邻域内的所有值的统计中值来代替。这个邻域在信号处理领域称之为窗(window)。窗开的越大,输出的结果就越平滑,但也可能会把我们有用的信号特征给抹掉。所以窗的大小要根据实际的信号和噪声特性来确定。

通常我们会选择窗的大小使得窗内的数据个数为奇数个,之所以这么选是因为奇数个数据才有唯一的中间值。

边界值的处理方法

对于有限长的数据,自然会存在两个边界,对于边界的处理有很多种方法,常见的方法有:

  • 不处理边界的数据,原样输出。
  • 将边界值重复。
  • 前后补 0。
  • 在边界时缩小窗的大小。

C++ 代码

下面给出一个 C++ 写的代码。首先既然要求中值,那么就要对数据进行排序。通常情况下我们中值滤波器的 window 不会很大,所以也不需要快速排序法这样的比较高级的排序方法。我就只简单的写了个冒泡排序法。大家也可以换用其他的排序方法。

bubbleSort 函数用来升序排序,代码如下:

template <typename Type> inline void bubbleSort(Type data[], int size)
{
    Type temp;
    while(size > 1)
    {
        for(int i = 0; i < size - 1; i++)
        {
            if(data[i] > data[i + 1])
            {
                temp = data[i];
                data[i] = data[i + 1];
                data[i + 1] = temp;
            }
        }
        size --;
    }
}

当然在排序之前还要有个数据拷贝函数,我没有直接用 memcpy 这样的函数,而是自己写了个简单的。其实用 memcpy 也挺好。

template <typename Type> static inline void copy(Type input[],  Type buf[], int left_bound, int kernel_size)
{
    for(int i = 0; i < kernel_size; i ++)
    {
        buf[i] = input[left_bound + i];
    }
}    

有了这些基础之后就可以实现我们的中值滤波器了。这里对边界数据的处理办法是在边界处缩小 window。对于第一个数据和最后一个数据,window 缩小到 1。第二个数据 window 为 3,第三个数据 window 为 5,以此类推,直到 window 达到设定大小。

/**
 * @brief medianFilter 中值滤波器
 * @param data 输入数据
 * @param size 输入数据的元素数量
 * @param kernel_size 滤波器大小,最好为奇数
 * @param buf 函数内部使用的数组,如果为 NULL 则函数内部分配空间。
 */
template <typename Type> void medianFilter(Type input[], Type output[], int size, int kernel_size, Type buf[])
{
    bool newBuf = false;
    if(buf == 0)
    {
        newBuf = true;
        buf = new Type[kernel_size];
    }
    int half = kernel_size / 2;
    int end = size - half;

    for(int pos = 0; pos < half; pos ++)
    {
        int part = 2 * pos + 1;
        copy(input, buf, 0, part);
        bubbleSort(buf, part);
        output[pos] = buf[part / 2];
    }
    int left_bound = 0;
    for(int pos = half; pos < end; pos ++)
    {
        copy(input, buf, left_bound, kernel_size);
        left_bound ++;
        bubbleSort(buf, kernel_size);
        output[pos] = buf[half];
    }
    left_bound ++;
    for(int pos = end; pos < size; pos ++)
    {
        int part = 2 * (size - pos) - 1;
        copy(input, buf, left_bound, part);
        left_bound += 2;
        bubbleSort(buf, part);
        output[pos] = buf[part / 2];
    }
    if(newBuf)
    {
        delete[] buf;
    }
}

下面是个测试代码,为了方便,用到了些 Qt 的功能。

int main(int argc, char *argv[])
{
    //QCoreApplication a(argc, argv);
    double d[40], e[40];
    for(int i = 0; i < 40; i ++)
    {
        d[i] = 0.01 * (i - 20) * (i - 20);
    }
    d[3] = 10;
    d[15] = -3;
    d[25] = 20;
    d[30] = -20;

    //bubbleSort(d, 12);
    medianFilter(d, e, 40, 9, (double *)NULL);
    QFile data("Q:\\output.txt");
    if (data.open(QFile::WriteOnly | QFile::Truncate))
    {
        QTextStream out(&data);
        for(int i = 0; i < 40; i ++)
        {
            out << d[i] << ", " << e[i] << endl;
        }
    }
    qDebug() << "Hello";
    //return a.exec();
}

滤波前后的数据如图所示。
这里写图片描述

这个代码还有个很好的特性。就是不改变线性数据的滤波结果。下面是个例子。

int main(int argc, char *argv[])
{
    double d[40], e[40];
    for(int i = 0; i < 40; i ++)
    {
        d[i] = 3 * (i - 20);
    }
    medianFilter(d, e, 40, 9, (double *)NULL);
    QFile data("Q:\\output.txt");
    if (data.open(QFile::WriteOnly | QFile::Truncate))
    {
        QTextStream out(&data);
        for(int i = 0; i < 40; i ++)
        {
            out << d[i] << ", " << e[i] << endl;
        }
    }
}

数据图像如下:
这里写图片描述

  • 12
    点赞
  • 88
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以帮您回答这个问题。不过需要说明的是,我是一个自然语言处理AI,无法直接运行 MATLAB 代码。我可以给您提供一些思路,帮助您完成这个问题。 首先,您需要编写 MATLAB 代码,实现自适应中值滤波、传统中值滤波和标准中值滤波。这些滤波实现方式可以参考 MATLAB 自带的 medfilt2 函数。其中,自适应中值滤波需要根据图像的局部方差动态调整滤波的大小。 然后,您需要加载一张图片,并对其进行滤波处理。可以使用 MATLAB 自带的 imread 函数读入图片,使用您编写的滤波对图片进行处理,然后使用 imwrite 函数将处理后的图片保存到本地。 最后,您需要使用 PSNR 和 SSIM 两个度量函数分别计算滤波前后的图像质量,并将结果输出到命令窗口。 以下是一个简单的示例代码,仅供参考: ``` img = imread('lena.png'); % 读入图片 filter_size = 3; % 滤波大小 % 自适应中值滤波 img_adaptive = adaptive_median_filter(img, filter_size); % 传统中值滤波 img_median = median_filter(img, filter_size); % 标准中值滤波 img_standard = standard_median_filter(img, filter_size); % 计算 PSNR 和 SSIM psnr_adaptive = psnr(img, img_adaptive); ssim_adaptive = ssim(img, img_adaptive); psnr_median = psnr(img, img_median); ssim_median = ssim(img, img_median); psnr_standard = psnr(img, img_standard); ssim_standard = ssim(img, img_standard); % 输出结果 fprintf('自适应中值滤波 PSNR: %.2f, SSIM: %.2f\n', psnr_adaptive, ssim_adaptive); fprintf('传统中值滤波 PSNR: %.2f, SSIM: %.2f\n', psnr_median, ssim_median); fprintf('标准中值滤波 PSNR: %.2f, SSIM: %.2f\n', psnr_standard, ssim_standard); % 保存结果图像 imwrite(img_adaptive, 'lena_adaptive.png'); imwrite(img_median, 'lena_median.png'); imwrite(img_standard, 'lena_standard.png'); ``` 其中,adaptive_median_filtermedian_filter 和 standard_median_filter 分别表示自适应中值滤波、传统中值滤波和标准中值滤波实现函数。psnr 和 ssim 分别表示计算 PSNR 和 SSIM 的函数。您需要根据自己的实现方式进行相应修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值