当前位置: 首页 > news >正文

Matlab通过GUI实现点云的双边(Bilateral)滤波(附最简版)

        本次我们分享关于Matlab实现点云的双边滤波。在三维点云数据处理中,噪声去除与特征保留往往是一对矛盾的需求 —— 传统滤波算法(如高斯滤波)在平滑噪声的同时,容易模糊点云中的边缘、棱角等关键特征。而双边滤波(Bilateral Filtering) 作为一种非线性滤波技术,通过同时考虑空间域权重与灰度(或强度)域权重,能够在抑制噪声的同时有效保留点云的细节特征,成为点云预处理中的核心算法之一。Matlab 作为工程领域常用的数值计算与可视化平台,提供了便捷的点云处理工具箱(PointCloud Toolbox),支持双边滤波的快速实现与优化。

一、点云双边滤波的核心原理与优势

1. 基本原理

        点云双边滤波的本质是对每个目标点,根据其邻域内点的空间距离属性差异(如颜色、法向量、强度值等)计算加权平均,从而更新目标点的坐标。其核心公式可表示为:

\(P_{filtered} = \frac{1}{W} \sum_{q \in N(p)} \left[ G_s(\|p - q\|) \cdot G_r(\|I_p - I_q\|) \right] \cdot q\)

其中:

  • \(p\) 为目标点,\(q\) 为 \(p\) 邻域内的点,\(N(p)\) 表示 \(p\) 的邻域集合;

  • \(G_s\) 为空间域高斯函数,根据点之间的欧氏距离分配权重,距离越近权重越大;

  • \(G_r\) 为属性域高斯函数,根据点的属性差异分配权重,差异越小权重越大;

  • \(W\) 为归一化系数,确保滤波后点的坐标范围合理。

2. 核心优势

与传统滤波算法相比,Matlab 点云双边滤波具有以下特点:

  • 特征保留能力强:通过属性域权重约束,避免边缘、棱角等特征被过度平滑;

  • 参数可控性高:可通过调整空间域标准差(控制邻域范围)、属性域标准差(控制属性差异敏感度),适配不同噪声强度的点云;

  • 兼容性广:支持 XYZ 坐标点云、带颜色(RGB)点云、带强度(Intensity)点云等多种类型数据,可与 Matlab 点云分割、配准等模块无缝衔接。

二、Matlab 点云双边滤波的主要流程

        Matlab 点云双边滤波的实现依赖于pointCloud类与bilateralFilter函数(需安装 PointCloud Toolbox),完整流程可分为数据准备→参数设置→滤波执行→结果评估四步,具体操作如下:

1. 数据准备:加载与预处理

        首先需加载点云数据,并进行基础预处理(如去除无效点、下采样),减少后续计算量。Matlab 支持多种点云格式(如 PLY、PCD、XYZ),常用函数包括pcread(读取点云)、pcremoveinvalid(去除无效点)、pcdownsample(下采样)。

示例代码

% 读取PLY格式点云(以桌面场景点云为例)ptCloud = pcread('desk_scene.ply');% 去除无效点(如NaN坐标点)ptCloud = pcremoveinvalid(ptCloud);% 下采样(体素下采样,体素大小0.01m,减少点云数量)ptCloud = pcdownsample(ptCloud, 'gridAverage', 0.01);% 可视化原始点云figure; pcshow(ptCloud); title('原始点云'); xlabel('X'); ylabel('Y'); zlabel('Z');

2. 参数设置:关键参数选择

        BilateralFilter函数的核心参数需根据点云噪声强度与特征需求调整,主要包括:

  • sigmaS(空间域标准差):控制邻域搜索范围,单位与点云坐标一致(如米)。噪声越强,需适当增大sigmaS(如 0.02~0.05),但过大易导致特征模糊;

  • sigmaR(属性域标准差):控制属性差异敏感度,若点云无颜色 / 强度信息,可基于点云法向量(需提前计算)或坐标差异设置。例如,带颜色点云可设sigmaR=0.05(RGB 值归一化后),无属性点云可基于 XYZ 差异设sigmaR=0.01;

  • kernelSize(核大小):邻域搜索的核尺寸,通常设为2*ceil(3*sigmaS)+1(确保覆盖 99.7% 的高斯分布范围),默认自动计算;

  • metric(属性度量方式):指定属性域的计算方式,如'color'(基于 RGB)、'intensity'(基于强度)、'normal'(基于法向量),无属性时用'euclidean'(基于坐标)。

参数选择原则

  • 若点云噪声密集(如激光雷达扫描的远距离点云),增大sigmaS(如 0.04),同时减小sigmaR(如 0.005),平衡平滑与特征保留;

  • 若点云含精细特征(如机械零件的棱角),减小sigmaS(如 0.02),增大sigmaR(如 0.08),避免特征丢失。

3. 滤波执行:调用函数实现

        根据点云类型调用bilateralFilter函数,输出滤波后的点云对象。若点云无颜色 / 强度属性,需先计算法向量作为属性输入。

示例代码

% 情况1:带颜色的点云(直接基于RGB属性滤波)if isfield(ptCloud.Location, 'Color')% 设置参数:空间域标准差0.03m,颜色域标准差0.05(RGB归一化后)sigmaS = 0.03;sigmaR = 0.05;% 执行双边滤波ptCloudFiltered = bilateralFilter(ptCloud, sigmaS, sigmaR, 'metric', 'color');else% 情况2:无属性点云(基于法向量滤波,需先计算法向量)% 计算法向量(邻域大小5,用于属性度量)ptCloud = pcnormals(ptCloud, 5);% 设置参数:空间域标准差0.03m,法向量域标准差0.1sigmaS = 0.03;sigmaR = 0.1;% 执行双边滤波ptCloudFiltered = bilateralFilter(ptCloud, sigmaS, sigmaR, 'metric', 'normal');end% 可视化滤波后点云figure; pcshow(ptCloudFiltered); title('双边滤波后点云'); xlabel('X'); ylabel('Y'); zlabel('Z');

4. 结果评估:定量与定性分析

        滤波效果需通过定性观察(特征保留程度)与定量指标(噪声抑制效果)结合评估:

  • 定性评估:对比原始点云与滤波后点云的边缘、棱角细节(如机械零件的孔、凸起),观察是否存在过度平滑;

  • 定量评估:计算点云的均方根误差(RMSE)(若有真值点云)、点云密度变化(滤波后点数量应与原始接近,避免过度删减)、法向量一致性(特征区域法向量方向是否连续)。

定量评估示例代码

% 计算原始点云与滤波后点云的坐标差异(假设无真值,用自身差异近似噪声)diffXYZ = ptCloud.Location - ptCloudFiltered.Location;rmse = sqrt(mean(diffXYZ(:).^2)); % 均方根误差fprintf('滤波后点云RMSE:%.6f m\n', rmse);% 计算点云密度变化originalCount = ptCloud.Count;filteredCount = ptCloudFiltered.Count;densityRatio = filteredCount / originalCount;fprintf('滤波后点数量保留比例:%.2f%%\n', densityRatio * 100);

三、Matlab 点云双边滤波的应用领域

        由于其 “噪声抑制 + 特征保留” 的核心优势,Matlab 点云双边滤波广泛应用于工业检测、自动驾驶、文化遗产保护、医疗影像等领域,具体场景如下:

1. 工业零件检测

        在机械零件三维扫描(如激光扫描、结构光扫描)中,点云易因设备精度、环境振动引入噪声,导致后续尺寸测量(如孔径、壁厚)误差。通过 Matlab 双边滤波,可在去除噪声的同时保留零件的棱角、螺纹等关键特征,为后续的尺寸标注(pcfitcylinder/pcfitsphere)缺陷检测提供高精度点云数据。

        应用案例:汽车发动机缸体的三维扫描点云处理,滤波后可准确拟合缸体孔的圆柱面,测量孔径公差是否符合标准。

2. 自动驾驶环境感知

        自动驾驶的激光雷达(LiDAR)点云常包含雨雪噪声、地面杂点,若直接用于障碍物检测(如行人、车辆),易导致误识别。Matlab 双边滤波可基于点云的强度值(LiDAR 反射强度)或法向量(地面与障碍物法向量差异大),过滤雨雪噪声与地面杂点,同时保留障碍物的轮廓特征,提升后续目标分割(pcsegplane)跟踪的准确性。

3. 文化遗产数字化保护

        在文物(如石雕、青铜器)三维重建中,需同时保留文物的纹理细节(如刻痕、花纹)与整体形态。传统滤波易模糊纹理,而 Matlab 双边滤波可通过调整颜色域(纹理颜色差异)或法向量域(纹理凹凸差异)参数,在平滑扫描噪声的同时,完整保留文物的纹理特征,为后续的数字化存档虚拟修复提供高保真点云。

4. 医疗影像三维重建

        在医学 CT、MRI 的三维点云重建(如骨骼、器官)中,点云噪声会影响手术规划的精度。Matlab 双边滤波可基于医学影像的灰度值(CT 值)作为属性域权重,过滤重建过程中的伪影噪声,同时保留骨骼的关节面、器官的轮廓等关键结构,为手术导航假体设计提供精准的三维模型。

四、总结与注意事项

        Matlab 点云双边滤波凭借其灵活的参数设置与强大的特征保留能力,成为点云预处理的关键工具。在实际应用中,需注意以下事项:

        1. 参数调试:sigmaS与sigmaR需根据点云噪声强度迭代调试,建议先从较小sigmaS(如 0.02)与中等sigmaR(如 0.05)开始,逐步优化;

        2. 计算效率:双边滤波为非线性操作,点云数量过大时(如百万级)会导致计算缓慢,建议先通过pcdownsample下采样(体素大小 0.01~0.05m),平衡效率与精度;

        3. 属性选择:优先使用点云的固有属性(如颜色、强度)作为metric参数,若无属性再使用法向量,避免仅基于 XYZ 坐标导致特征丢失。

本次使用的数据是————兔砸!!!

一、点云实现双边滤波的程序

1、最简版

%% 0. 清空环境
clear; clc; close all;%% 1. 读入点云
[file, path] = uigetfile({'*.ply;*.pcd;*.xyz','点云文件 (*.ply,*.pcd,*.xyz)'},...'请选择点云');
if file==0; return; end
fname = fullfile(path,file);
ptCloud = pcread(fname);
N = ptCloud.Count;
fprintf('原始点云有 %d 个点\n',N);%% 2. 计算平均密度(最近邻距离均值)
kdtree0 = createns(ptCloud.Location,'NSMethod','kdtree');
[~, nndist0] = knnsearch(kdtree0,ptCloud.Location,'K',2); % 第2列=最近邻距离
density = mean(nndist0(:,2));
fprintf('点云平均密度 = %.4f\n',density);%% 3. 添加高斯噪声
sigmaNoise = density * 2;
noise = sigmaNoise * randn(N,3);
noisyCloud = pointCloud(ptCloud.Location + noise,...'Color',ptCloud.Color,...'Normal',ptCloud.Normal);
fprintf('添加噪声后 %d 个点\n',noisyCloud.Count);%% 4. 双边滤波
K      = 30;          % 与原脚本一致
srMm   = 10;          % 与原脚本一致
filtCloud = pcdBilateralFilter(noisyCloud,K,srMm);
fprintf('双边滤波后 %d 个点\n',filtCloud.Count);%% 5. 可视化
figure('Name','原始点云','NumberTitle','off'); pcshow(ptCloud); axis on; view(3);
figure('Name','噪声点云','NumberTitle','off'); pcshow(noisyCloud); axis on; view(3);
figure('Name','双边滤波','NumberTitle','off'); pcshow(filtCloud); axis on; view(3);function out = pcdBilateralFilter(pc,K,srMm)K   = max(round(K),2);sr  = srMm/1000;xyz = pc.Location;n   = size(xyz,1);kd  = createns(xyz,'NSMethod','kdtree');% 自算法向量block  = 10000;normals= zeros(n,3,'like',xyz);h      = waitbar(0,'计算法向量…');for i = 1:block:nir = min(i+block-1,n);[idxMat,~] = knnsearch(kd, xyz(i:ir,:), 'K', K);for k = i:iridx = idxMat(k-i+1,:);P   = xyz(idx,:);[~,~,V] = svd(P - mean(P,1),0);nk  = V(:,3);if nk(3)>0, nk=-nk; endnormals(k,:) = nk;endwaitbar(ir/n,h);endclose(h);% 双边滤波xyzNew = zeros(n,3,'like',xyz);h = waitbar(0,'双边滤波…');for i = 1:block:nir = min(i+block-1,n);[idxMat,~] = knnsearch(kd, xyz(i:ir,:), 'K', K);for k = i:iridx = idxMat(k-i+1,:);p0  = xyz(k,:);n0  = normals(k,:);Ws  = 0; Z = 0;for t = 2:numel(idx)pj  = xyz(idx(t),:);vec = pj - p0;dd  = norm(vec);dn  = dot(vec, n0);ws  = exp(-(dd^2)/(2*(0.05)^2));wr  = exp(-(dn^2)/(2*sr^2));w   = ws*wr;Ws  = Ws + w;Z   = Z  + w*dn;endxyzNew(k,:) = p0 + (Z/(Ws+eps))*n0;endwaitbar(ir/n,h);endclose(h);out = pointCloud(xyzNew,'Color',pc.Color,'Normal',normals);
end

2、GUI版本

function bilateralFilterGUI
% 双边滤波 GUI(三栏:原始 | 加噪 | 滤波)—— 2020a 兼容
fig = figure('Name','双边滤波工具','NumberTitle','off',...'MenuBar','none','ToolBar','none','Position',[100 100 1280 720]);%% ==================== 界面布局 ====================
imgWidth = 0.78;
panelW   = imgWidth/3 - 0.005;pnlOrig = uipanel('Parent',fig,'Units','normalized',...'FontSize',16,'Position',[0.02 0.02 panelW 0.96],'Title','原始点云');
pnlNois = uipanel('Parent',fig,'Units','normalized',...'FontSize',16,'Position',[0.02+panelW+0.005 0.02 panelW 0.96],'Title','加噪声');
pnlFilt = uipanel('Parent',fig,'Units','normalized',...'FontSize',16,'Position',[0.02+2*(panelW+0.005) 0.02 panelW 0.96],'Title','双边滤波');axOrig = axes('Parent',pnlOrig,'Units','normalized','Position',[0.05 0.05 0.90 0.90]);
axNois = axes('Parent',pnlNois,'Units','normalized','Position',[0.05 0.05 0.90 0.90]);
axFilt = axes('Parent',pnlFilt,'Units','normalized','Position',[0.05 0.05 0.90 0.90]);pnlCtrl = uipanel('Parent',fig,'Units','normalized',...'FontSize',16,'Position',[0.78 0 0.22 1],'Title','控制');%% ==================== 控件 ====================
txtH  = 0.04;  btnH = 0.06;  gap = 0.02;  yTop = 0.94;uicontrol('Parent',pnlCtrl,'Style','pushbutton','String','浏览…',...'FontSize',16,'Units','normalized','Position',[0.05 yTop-btnH 0.90 btnH],...'Callback',@loadCloud);
yTop = yTop - btnH - gap;lblInfo = uicontrol('Parent',pnlCtrl,'Style','text','String','未加载点云',...'FontSize',10,'Units','normalized','Position',[0.05 yTop-txtH 0.90 txtH],...'HorizontalAlignment','left');
yTop = yTop - txtH - gap;% 噪声强度
uicontrol('Parent',pnlCtrl,'Style','text','String','噪声强度 / mm',...'FontSize',12,'FontWeight','bold','Units','normalized','Position',[0.05 yTop-txtH 0.90 txtH],...'HorizontalAlignment','left');
yTop = yTop - txtH - gap;sliderSig = uicontrol('Parent',pnlCtrl,'Style','slider','Min',0,'Max',20,'Value',5,...'FontSize',16,'Units','normalized','Position',[0.05 yTop-btnH 0.65 btnH],...'Callback',@refreshNoise);
txtSig    = uicontrol('Parent',pnlCtrl,'Style','edit','String','5',...'FontSize',16,'Units','normalized','Position',[0.75 yTop-btnH 0.20 btnH],...'Callback',@editSigCB);
yTop = yTop - btnH - gap;% 邻域点数 K
uicontrol('Parent',pnlCtrl,'Style','text','String','邻域点数 K',...'FontSize',12,'FontWeight','bold','Units','normalized','Position',[0.05 yTop-txtH 0.90 txtH],...'HorizontalAlignment','left');
yTop = yTop - txtH - gap;sliderK = uicontrol('Parent',pnlCtrl,'Style','slider','Min',2,'Max',100,'Value',30,...'FontSize',16,'Units','normalized','Position',[0.05 yTop-btnH 0.65 btnH],...'Callback',@refreshFilter);
txtK    = uicontrol('Parent',pnlCtrl,'Style','edit','String','30',...'FontSize',16,'Units','normalized','Position',[0.75 yTop-btnH 0.20 btnH],...'Callback',@editKCB);
yTop = yTop - btnH - gap;% 法向权重 σr
uicontrol('Parent',pnlCtrl,'Style','text','String','法向权重 σr',...'FontSize',12,'FontWeight','bold','Units','normalized','Position',[0.05 yTop-txtH 0.90 txtH],...'HorizontalAlignment','left');
yTop = yTop - txtH - gap;sliderR = uicontrol('Parent',pnlCtrl,'Style','slider','Min',1,'Max',50,'Value',10,...'FontSize',16,'Units','normalized','Position',[0.05 yTop-btnH 0.65 btnH],...'Callback',@refreshFilter);
txtR    = uicontrol('Parent',pnlCtrl,'Style','edit','String','10',...'FontSize',16,'Units','normalized','Position',[0.75 yTop-btnH 0.20 btnH],...'Callback',@editRCB);
yTop = yTop - btnH - gap;% 保存
uicontrol('Parent',pnlCtrl,'Style','pushbutton','String','保存滤波结果',...'FontSize',16,'Units','normalized','Position',[0.05 yTop-btnH 0.90 btnH],...'Callback',@(s,e)saveCloud(ptCloudFilt));%% ==================== 数据 ====================
ptCloudOrig = pointCloud.empty;
ptCloudNois = pointCloud.empty;
ptCloudFilt = pointCloud.empty;%% ==================== 回调 ====================function loadCloud(~,~)[file,path] = uigetfile({'*.pcd;*.ply;*.xyz','点云文件'},'选择点云');if isequal(file,0), return; endtryptCloudOrig = pcread(fullfile(path,file));catch MEerrordlg(ME.message,'读取失败'); return;end
%         disp(isvalid(axOrig))   % 必须 =1% 显示原始点云showPointCloud(axOrig,ptCloudOrig);set(lblInfo,'String',sprintf('已加载:%s  (%d 点)',file,ptCloudOrig.Count));refreshNoise();             % 自动生成噪声并滤波endfunction refreshNoise(~,~)if isempty(ptCloudOrig), return; endsigma = get(sliderSig,'Value')/1000;        % mm → mxyz   = ptCloudOrig.Location;xyzNois = xyz + sigma*randn(size(xyz));ptCloudNois = pointCloud(xyzNois,'Color',ptCloudOrig.Color);showPointCloud(axNois,ptCloudNois);set(txtSig,'String',num2str(get(sliderSig,'Value')));refreshFilter();endfunction refreshFilter(~,~)if isempty(ptCloudNois), return; endK  = round(get(sliderK,'Value'));sr = get(sliderR,'Value');ptCloudFilt = pcdBilateralFilter(ptCloudNois,K,sr);showPointCloud(axFilt,ptCloudFilt);set(txtK,'String',num2str(K));set(txtR,'String',num2str(sr));endfunction editSigCB(src,~)v = str2double(get(src,'String'));if isnan(v), v = 5; endv = max(0,min(20,v));set(sliderSig,'Value',v);refreshNoise();endfunction editKCB(src,~)v = str2double(get(src,'String'));if isnan(v), v = 30; endv = max(2,min(100,round(v)));set(sliderK,'Value',v);refreshFilter();endfunction editRCB(src,~)v = str2double(get(src,'String'));if isnan(v), v = 10; endv = max(1,min(50,v));set(sliderR,'Value',v);refreshFilter();endfunction saveCloud(cloud)if isempty(cloud)errordlg('请先完成滤波','提示'); return;end[file,path] = uiputfile({'*.pcd','PCD';'*.ply','PLY';'*.xyz','XYZ'},'保存滤波点云');if isequal(file,0), return; endtrypcwrite(cloud,fullfile(path,file),'Precision','double');msgbox('保存成功!','提示');catch MEerrordlg(ME.message,'保存失败');endendfunction showPointCloud(ax,pc)%---- 2020a 暖启动 + 强制刷新 ----cla(ax); set(ax,'Color','w');pcshow(pointCloud(nan(0,3)),'Parent',ax,'MarkerSize',1); drawnow;pcshow(pc,'Parent',ax,'MarkerSize',35);axis(ax,'tight'); grid(ax,'on'); view(ax,3);drawnow;        % 确保刷新end%% ==================== 双边滤波核心(零 pcnormals) ====================function out = pcdBilateralFilter(pc,K,srMm)K   = max(round(K),2);sr  = srMm/1000;xyz = pc.Location;n   = size(xyz,1);kd  = createns(xyz,'NSMethod','kdtree');% 自算法向量block  = 10000;normals= zeros(n,3,'like',xyz);h      = waitbar(0,'计算法向量…');for i = 1:block:nir = min(i+block-1,n);[idxMat,~] = knnsearch(kd, xyz(i:ir,:), 'K', K);for k = i:iridx = idxMat(k-i+1,:);P   = xyz(idx,:);[~,~,V] = svd(P - mean(P,1),0);nk  = V(:,3);if nk(3)>0, nk=-nk; endnormals(k,:) = nk;endwaitbar(ir/n,h);endclose(h);% 双边滤波xyzNew = zeros(n,3,'like',xyz);h = waitbar(0,'双边滤波…');for i = 1:block:nir = min(i+block-1,n);[idxMat,~] = knnsearch(kd, xyz(i:ir,:), 'K', K);for k = i:iridx = idxMat(k-i+1,:);p0  = xyz(k,:);n0  = normals(k,:);Ws  = 0; Z = 0;for t = 2:numel(idx)pj  = xyz(idx(t),:);vec = pj - p0;dd  = norm(vec);dn  = dot(vec, n0);ws  = exp(-(dd^2)/(2*(0.05)^2));wr  = exp(-(dn^2)/(2*sr^2));w   = ws*wr;Ws  = Ws + w;Z   = Z  + w*dn;endxyzNew(k,:) = p0 + (Z/(Ws+eps))*n0;endwaitbar(ir/n,h);endclose(h);out = pointCloud(xyzNew,'Color',pc.Color,'Normal',normals);end
end

二、点云双边滤波的结果

        依旧采用GUI布局,添加了噪声设置功能,双边滤波设置了邻域个数控制和法向量权重变量。总体来说性能还比较平稳。小伙伴们快来试试吧!

就酱,下次见^-^

http://www.dtcms.com/a/419103.html

相关文章:

  • 4-5〔O҉S҉C҉P҉ ◈ 研记〕❘ WEB应用攻击▸远程文件包含漏洞
  • 怎么在淘宝上做网站中交建设集团网站
  • 在线查看网站源码中国最新新闻
  • 轴状态读取(运动控制系列)
  • quic的拥塞控制
  • 【模型系列】Human-in-the-Loop
  • AI项目问题总结大全
  • 【linux内核驱动day03】
  • Accelerate基本使用
  • Day75 基本情报技术者 单词表10 ネットワーク応用
  • 企业网站美化做常州美食网站首页的背景图
  • 网站建设设计的流程wordpress的搭建教程 pdf
  • 页网站腾讯云学生机做网站
  • C++ 模板(Template)基础与应用
  • Flask实战指南:从基础到高阶的完整开发流程
  • I2C总线详解
  • 从底层到应用:开散列哈希表与_map/_set 的完整实现(附逐行注释)
  • MoonBit 异步网络库发布
  • OpenLayers地图交互 -- 章节十六:双击缩放交互详解
  • Kubernetes HPA从入门到精通
  • 株洲做网站的公司网站页面设计
  • 汕头企业网站建设价格视频作为网站背景
  • 视频抽帧完全指南:使用PowerShell批量提取与优化图片序列
  • 1、User-Service 服务设计规范文档
  • 企业网站模板购买企业级网站建设
  • 路由器设置手机网站打不开wordpress跳转二级域名
  • MySQL在线DDL:零停机改表实战指南
  • 哪个做图网站可以挣钱马鞍山网站建设公司排名
  • 杭州公司做网站电商是干什么工作的
  • 揭秘InnoDB磁盘I/O与存储空间管理