非局部均值降噪算法(NLM)原理及实现

news2024/11/13 14:47:38

文章目录

    • 一、概述
    • 二、算法原理
    • 三、算法流程
    • 四、MATLAB实现
    • 五、C++实现
    • 参考文献

一、概述

在日常生活中,最常见的 CT 图像噪声是高斯白噪声。目前,针对高斯白噪声的处理方法,主要有空间域中的以平滑为基本思想的均值滤波、高斯滤波、局部滤波等,此外还有频率域中的维纳滤波和小波阈值收缩等。局部强度统计特征是衡量区域内像素间的平均相似性,但这一特征难以准确辨别边缘与其邻近点之间的差异,导致了滤波结果中边缘信息的模糊。小波阈值收缩算法虽能够很好地估计信号的噪声,但是阈值和阈值函数的选择上存在不确定性,会造成信号的失真。
2005 年Buades 等提出了去除图像加性噪声的非局部均值滤波算法(Non-Local Means,NLM),该算法是利用图像非局部结构的相似性来去除噪声,恢复图像的主要几何结构。论文原文:A non-local algorithm for image denoising,还有一篇2011年的论文:Non-Local Means Denoising。

二、算法原理

在这里插入图片描述
如下所示为一般均值滤波的原理,其中权重仅通过中心像素和其邻域像素进行计算。当滤波半径过大的时候,图像的细节信息很难被保留,即使像双边滤波引入了像素间的灰度信息也会出现细节丢失严重的情况。
如下所示为双边滤波的公式及示意图:
在这里插入图片描述

非局部均值滤波是考虑到图像的非局部统计自相似性质,利用图像包含的大量重复结构进行去噪。通过计算非局部像素之间的相似度确定权值,然后加权平均来恢复图像。NLM 的主要特点 :将相似像素定义为具有相同邻域模式的像素,对像素周围固定大小的窗口内的信息进行比较,而不只是比较图像单个像素的信息,因此得到的相似性更加可靠。NLM基本原理如下 :
在这里插入图片描述
通过观察NLM的计算公式不难发现,其相对于常见的局部均值滤波,其区别主要体现在权重的计算上。在计算权重的相似度时,其通过像素间的邻域进行计算。

在这里插入图片描述
在这里插入图片描述
注意:通常非局部均值的滤波核都设置得比较大,如上图中所示的滤波核大小为9x9,因此NLM能够获得更佳的平滑度,同时更好的保留细节。

三、算法流程

在这里插入图片描述

四、MATLAB实现

无优化的实现:

function [output]=nlm_filter(input,t,f,h)

% 输入: 待平滑的图像
% t: 搜索窗口半径
% f: 相似性窗口半径
% h: 平滑参数
% NLmeans(ima,5,2,sigma);

% 图像大小
[m n]=size(input);
% 输出
output=zeros(m,n);
input2 = padarray(input,[f+t f+t],'symmetric');%边界作对称处理

% 高斯核
ksize = f*2 + 1;
kernel = fspecial('gaussian',[ksize,ksize],1);
kernel = kernel / sum(sum(kernel));
kernel(:) = 1;
h=h*h;

for i=1:m
    for j=1:n
        i1 = i+ f+t;%原始图像的像素位置 (中心像素)
        j1 = j+ f+t;
        
        W1= input2(i1-f:i1+f , j1-f:j1+f);%小窗口
        
        wmax=0;
        average=0;
        sweight=0;
        
        %rmin = max(i1-t,f+1);
        %rmax = min(i1+t,m+f);
        %smin = max(j1-t,f+1);
        %smax = min(j1+t,n+f);
        rmin=i1-t;
        rmax=i1+t;
        smin=j1-t;
        smax=j1+t;
        
        for r=rmin:1:rmax %大窗口
            for s=smin:1:smax
                
                if(r==i1 && s==j1)
                    continue;
                end
                
                W2= input2(r-f:r+f , s-f:s+f);    %大搜索窗口中的小相似性窗口
                d = sum(sum(kernel.*(W1-W2).*(W1-W2)));
                w=exp(-d/h); %权重
                
                if w>wmax
                    wmax=w;   %求最大权重
                end
                
                sweight = sweight + w;  %大窗口中的权重和
                average = average + w*input2(r,s);
            end
        end
        
        average = average + wmax*input2(i1,j1);
        sweight = sweight + wmax;
        
        if sweight > 0
            output(i,j) = average / sweight;
        else
            output(i,j) = input(i,j);
        end
    end
end

使用积分图加速(这部分还没仔细看其实现原理):

clc;
clear;
close all;

src=imread('lena.jpg');
src=rgb2gray(src);
src=double(src);
figure,imshow(src,[]),title('src image')

[m,n]=size(src);
ds=2;% block size for calculate weight
Ds=5;% search block
h=10;% decay factor
offset=ds+Ds;
PaddedImg = padarray(src,[ds+Ds,ds+Ds],'symmetric','both');% 扩展图像,便于处理

sumimage=zeros(m,n);
sumweight=zeros(m,n);
maxweight=zeros(m,n);
image=PaddedImg(1+Ds:Ds+m+ds,1+Ds:Ds+n+ds);
[M,N]=size(image);
for r=-Ds:Ds
    for s=-Ds:Ds
        
        %跳过当前点偏移
        if(r==0&&s==0)
            continue;
        end
        
        %求得差值积分图
        wimage=PaddedImg(1+Ds+r:Ds+m+ds+r,1+Ds+s:Ds+n+ds+s);
        diff=image-wimage;
        diff=diff.^2;
        J=cumsum(diff,1);
        J=cumsum(J,2);
        
        %计算距离
        distance=J(M-m+1:M,N-n+1:N)+J(1:m,1:n)-J(M-m+1:M,1:n)-J(1:m,N-n+1:N);
        distance=distance/((2*ds+1).^2);
        
        %计算权重并获得单个偏移下的加权图像
        weight=exp(-distance./(h*h));
        sumimage=sumimage+weight.*wimage(ds+1:ds+m,ds+1:ds+n);
        sumweight=sumweight+weight;
        maxweight=max(maxweight,weight);
    end
end
sumimage=sumimage+maxweight.*image(ds+1:ds+m,ds+1:ds+n);
sumweight=sumweight+maxweight;
dst=sumimage./sumweight;

figure,imshow(dst,[]),title('dst');

降噪前后的效果比较:
在这里插入图片描述

五、C++实现

//计算0~255的平方查找表
float table1[256];
static void cal_lookup_table1(void)
{
  for (int i = 0; i < 256; i++)
  {
    table1[i] = (float)(i*i);
  }
}

//计算两个0~255的数的绝对差值的查找表
uchar table2[256][256];
static void cal_lookup_table2(void)
{
  for (int i = 0; i < 256; i++)
  {
    for (int j = i; j < 256; j++)
    {
      table2[i][j] = abs(i - j);
      table2[j][i] = table2[i][j];
    }
  }
}

float MSE_block(Mat A, Mat B)
{
  float sum = 0.0;
  for (int j = 0; j < A.rows; j++)
  {
    uchar *data1 = A.ptr<uchar>(j);
    uchar *data2 = B.ptr<uchar>(j);
    for (int i = 0; i < A.cols; i++)
    {
      sum += table1[table2[data1[i]][data2[i]]];
    }
  }
  sum = sum / (A.rows*B.cols);
  return sum;
}

//sigma越大越平滑
//halfKernelSize邻域窗
//halfSearchSize搜索窗
void NL_mean(Mat src, Mat &dst, double sigma, int halfKernelSize, int halfSearchSize)
{
  Mat boardSrc;
  dst.create(src.rows, src.cols, CV_8UC1);
  int boardSize = halfKernelSize + halfSearchSize;
  copyMakeBorder(src, boardSrc, boardSize, boardSize, boardSize, boardSize, BORDER_REFLECT);   //边界扩展
  double sigma_square = sigma*sigma;

  int rows = src.rows;
  int cols = src.cols;

  cal_lookup_table1();
  cal_lookup_table2();

  for (int j = boardSize; j < boardSize + rows; j++)
  {
    uchar *dst_p = dst.ptr<uchar>(j - boardSize);
    for (int i = boardSize; i < boardSize + cols; i++)
    {
      Mat patchA = boardSrc(Range(j - halfKernelSize, j + halfKernelSize), Range(i - halfKernelSize, i + halfKernelSize));
      double w = 0;
      double p = 0;
      double sumw = 0;

      for (int sr = -halfSearchSize; sr <= halfSearchSize; sr++)   //在搜索框内滑动
      {
        uchar *boardSrc_p = boardSrc.ptr<uchar>(j + sr);
        for (int sc = -halfSearchSize; sc <= halfSearchSize; sc++)
        {
          Mat patchB = boardSrc(Range(j + sr - halfKernelSize, j + sr + halfKernelSize), Range(i + sc - halfKernelSize, i + sc + halfKernelSize));
          float d2 = MSE_block(patchA, patchB);

          w = exp(-d2 / sigma_square);
          p += boardSrc_p[i + sc] * w;
          sumw += w;
        }
      }
      dst_p[i - boardSize] = saturate_cast<uchar>(p / sumw);
    }
  }
}

参考文献

[1] 非局部均值滤波算法(NL-means)
[2] https://developer.aliyun.com/article/1156618
[3] opencv源码修改与使用:fastNlMeansDenoisingMulti()对CV_16U的支持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2079761.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

案例研究丨MaxKB+Ollama:深圳市公共信用中心探索信用服务创新

深圳市公共信用中心隶属于深圳市市场监督管理局&#xff0c;主要负责对外提供深圳市企业公共信用信息报告查询和深圳市企业注册登记档案查询等服务。作为深圳市信用信息的权威发布机构&#xff0c;深圳市公共信用中心一直致力于为公众提供准确、及时的信用信息服务。 深圳信用…

2024年医疗行业关键词:精益管理

随着医疗技术的飞速发展、患者需求的日益多元化以及医疗资源的日益紧张&#xff0c;精益管理作为一种高效、科学的管理模式&#xff0c;正逐步成为医疗行业转型升级的关键驱动力。具体表现如深圳天行健企业管理咨询公司下文所述&#xff1a; 1. 优化服务流程 首先&#xff0c;…

Windows电脑本地安装跨平台文生音乐AI应用MusicGPT详细教程

文章目录 前言1. 本地部署2. 使用方法介绍3. 内网穿透工具下载安装4. 配置公网地址5. 配置固定公网地址 前言 今天和大家分享一下在Windows系统电脑上本地快速部署一个文字生成音乐的AI创作服务MusicGPT&#xff0c;并结合cpolar内网穿透工具实现随时随地远程访问使用进行AI音…

(一) 初入MySQL 【认识和部署】

前置资源 一、数据库概述 1.1、数据库基本概念 数据(Data) 描述事物的符号记录称为数据。数字、文字、图形、图像、声音、档案记录等都是数据。数据是以“记录”的形式按照统一的格式进行存储的&#xff0c;而不是杂乱无章的。 相同格式和类型的数据统一存放在一起&#xff0…

阿里云OSS文件存储

文章目录 参考准备创建bucketendpoint 和 bucket域名的访问路径AccessKey和OSS的开发文档 Springboot整合OSS引入依赖AliyunOssConfigAliyunOssPropertiesapplicatioin.yml简单上传和下载使用签名URL进行临时授权访问生成以PUT方法访问的签名URL来上传文件通过签名URL临时授权简…

WIFI 配网

配网:指的是外部向WiFi模块提供SSID和密码&#xff0c;以便Wi-Fi模块可以连接指定的热点 常见的配网方式有:-键配网smart config、SoftAP配网、蓝牙配网、屏幕配网。 1.0 一键配网 2.0 蓝牙配网 一键配网的模式对应的厂加模式 3.0 状态机WIFI模组物联网 4.0 创建枚举结构体 ty…

女性权益之镜:印度侵害事件分析

1.项目背景 近年来&#xff0c;印度社会关于女性权利的讨论日益频繁&#xff0c;然而&#xff0c;女性受侵害的违法事件仍层出不穷&#xff0c;这些事件不仅威胁到女性的生命安全&#xff0c;还深刻影响了社会的稳定与发展&#xff0c;尽管印度政府在法律和政策层面采取了一系…

实现并发网络服务器

一&#xff0c;网络服务器 1.单循环网络服务器 —— 同一时刻只能处理一个客户端任务 2.并发服务器 —— 同一时刻能处理多个客户端任务 二&#xff0c;并发服务器 1.多线程 2.IO多路复用 3.多进程 三&#xff0c;IO模型 1.阻塞IO 阻塞IO&#xff08;Blocking IO&…

IO进程day03(获取文件属性、目录操作、库Lib)

目录 【1】获取文件属性 1》stat函数 2》获取文件类型 3》获取文件权限 练习&#xff1a;编程实现“ls -l 文件名” 的功能 4》stat、fstat、lstat的区别 【2】目录操作 【3】库 Lib 1》文件分类 1> 头文件&#xff1a; 以 .h结尾的文件 2> 源文件&#xff1a…

Qt之控件介绍

目录 控件概述 QWidget核心属性 1.enabled属性 2.geometry属性 3.windowTitle属性 4.windowIcon属性 5.windowOpacity属性 6.cursor属性 7.font属性 8.toolTip属性 9.focusPolicy属性 10.styleSheet属性 按钮类控件&#xff1a; 1.PushButton 2.RadioBu…

Leetcode 17. 电话号码的字母组合 C++实现

Leetcode 17. 电话号码的字母组合 问题&#xff1a;给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 算法&#xff1a;递归嵌…

PyTorch深度学习模型训练流程:(二、回归)

回归的流程与分类基本一致&#xff0c;只需要把评估指标改动一下就行。回归输出的是损失曲线、R^2曲线、训练集预测值与真实值折线图、测试集预测值散点图与真实值折线图。输出效果如下&#xff1a; 注意&#xff1a;预测值与真实值图像处理为按真实值排序&#xff0c;图中呈现…

机器学习概述与应用:深度学习、人工智能与经典学习方法

引言 机器学习(Machine Learning)是人工智能(AI)领域中最为核心的分支之一,其主要目的是通过数据学习和构建模型,帮助计算机系统自动完成特定任务。随着深度学习(Deep Learning)的崛起,机器学习技术在各行各业中的应用变得越来越广泛。在本文中,我们将详细介绍机器学…

Datawhale X 李宏毅苹果书 AI夏令营 Task1笔记

课程内容 学习笔记 &#xff08;一&#xff09;术语解释 一 . 机器学习&#xff08;Machine Learning&#xff0c;ML&#xff09; 机器学习&#xff0c;在本书的解释中是让机器具备找一个函数的能力。个人理解是基于所拥有的数据构建起概率统计模型来对数据进行预测与分析。…

python可视化-散点图

散点图可以了解数据之间的各种相关性&#xff0c;如正比、反比、无相关、线性、指数级、 U形等&#xff0c;而且也可以通过数据点的密度&#xff08;辅助拟合趋势线&#xff09;来确定相关性的强度。另外&#xff0c;也可以探索出异常值&#xff08;在远超出一般聚集区域的数据…

【Java】Record的使用 (简洁教程)

Java系列文章目录 补充内容 Windows通过SSH连接Linux 第一章 Linux基本命令的学习与Linux历史 文章目录 Java系列文章目录一、前言二、学习内容&#xff1a;三、问题描述四、解决方案&#xff1a;4.1 为什么引入Record4.2 Record与Class区别4.3 使用场景 五、总结&#xff1a;…

使用uni-app开发微信小程序

一、前提环境 1.1 &#xff1a;uniapp开发文档&#xff1a;https://uniapp.dcloud.net.cn/quickstart-cli.html 细节都在这一页&#xff0c;这里不过多解释 二、开发工具下载 2.1 微信开发者工具 下载链接&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/dev…

Java:Calendar类

文章目录 Calendar类常用方法代码 黑马学习笔记 Calendar类 calendar是可变对象&#xff0c;一旦修改后其对象本身表示的时间将发生变化 原始对象会跟着修改&#xff0c;造成原始对象的丢失 常用方法 代码 package Time;import java.util.Calendar; import java.util.Date;/…

【RabbitMQ高级特性】消息可靠性原理

1. 消息确认机制 1.1 介绍 我们可以看到RabbitMQ的消息流转图&#xff1a; 当消息从Broker投递给消费者的时候会存在以下两种情况&#xff1a; consumer消费消息成功consumer消费消息异常 如果说RabbitMQ在每次将消息投递给消费者的时候就将消息从Broker中删除&#xff0c…

用 like concat 不用 like,为了防止sql注入;#{}和${}的区别和用法;#{}预防SQL注入的原理

一、like concat 和 like mybatis中为了防止sql注入&#xff0c;使用like语句时并不是直接使用&#xff0c;而是使用concat函数<if test"goodName ! null and goodName ! "> and good_name like concat(%, #{goodName}, %)</if> concat()函数1、功能&a…