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

Matlab通过GUI实现点云的均值滤波(附最简版)

        本节我们分享使用Matlab进行点云的均值滤波。均值滤波(Moving-Average Filter)是点云预处理中最基础、最常用的线性平滑方法之一。其思路是对每个点在其 k-邻域(或 r-半径邻域)内计算几何坐标的算术平均,用该均值替代原始点位置,达到抑制零均值随机噪声、平滑表面的目的。与高斯滤波相比,均值滤波模板权重均匀、计算量小;与中值滤波相比,它对高密度小幅度噪声更敏感,但保持边缘能力稍弱,因此常作为“粗去噪”或“预平滑”步骤出现。

一、MATLAB 主要实现流程

1. 读取与可视化

ptCloud = pcread('scene.ply');          % 支持 ply/pcd 等格式
pcshow(ptCloud); title('原始点云');

2. 添加噪声(可选,用于算法验证)

noise = normrnd(0,0.002,size(ptCloud.Location)); % N(0,σ)
noisyXYZ = ptCloud.Location + noise;
ptCloud = pointCloud(noisyXYZ);

3. 直接调用内置平滑函数(最快)

% 按“movmean”方式对 x/y/z 分别做滑动平均
smoothXYZ = smoothdata(ptCloud.Location,'movmean',k); % k 为窗宽
ptSmooth  = pointCloud(smoothXYZ);

4. 自定义邻域均值(精度高、参数灵活)

% 构建 KD-Tree 加速邻域搜索
[ids,~] = knnsearch(ptCloud.Location,ptCloud.Location,'K',k);
neighborMean = zeros(size(ptCloud.Location));
for i = 1:ptCloud.Countidx = ids(i,:);                               % k 个最近序号neighborMean(i,:) = mean(ptCloud.Location(idx,:));
end
ptSmooth = pointCloud(neighborMean);

5. 结果可视化与保存

figure;
pcshow(ptSmooth); title('均值滤波后');
pcwrite(ptSmooth,'scene_meanFilter.ply');

二、关键参数与调优建议
- k(邻域点数)或 r(搜索半径):越大则平滑越强,但会削弱细节;一般先取 20–50 或半径 5–10 mm 做试验,再按几何分辨率与噪声幅度微调。  
- 距离阈值:可叠加“统计离群剔除”,对邻域平均距离超过 μ+α·σ 的点先剔除再求均值,兼顾去噪与保边。  
- GPU/并行:若点云>10^6,使用 `knnsearch` 的 `'NSMethod', 'exhaustive', 'Distance', 'euclidean'` 并打开 `parpool` 可显著提高速度。

三、典型应用领域
1. 三维重建:平滑无序扫描数据,减少网格重建后的“毛刺”与伪孔洞。  
2. 工业检测:平滑铸件、机加件点云,提高后续配准与尺寸测量精度。  
3. 自动驾驶:对车载 LiDAR 原始扫描做“粗去噪”,为地面分割、目标聚类提供更干净输入。  
4. 室内/地下测绘:抑制手持 SLAM 点云的抖动噪声,增强可视化与制图效果。  
5. 文物与医学:在保留关键纹理的前提下削弱设备电子噪声,便于曲面拟合与特征提取。

四、小结
均值滤波因其原理简单、实现方便,在 MATLAB 环境下既可一行代码快速调用,又能基于 KD-Tree 自定义精细控制,是点云预处理链中“去随机噪、平滑表面”的首选工具;后续可串联统计滤波、体素下采样或法向重估,为配准、分割、重建等高级算法奠定高质量数据基础。

本次使用的数据是兔兔兔————窗帘!

一、点云均值滤波的程序

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. 均值滤波(半径 50 mm)
radius = 50;
cloudMean = pcdMeanFilter(ptCloud, radius);
fprintf('均值滤波(半径=%d mm)后 %d 个点\n', radius, cloudMean.Count);%% 3. 可视化
figure('Name', '原始点云', 'NumberTitle', 'off');
pcshow(ptCloud); axis on; view(3);figure('Name', '均值滤波', 'NumberTitle', 'off');
pcshow(cloudMean); axis on; view(3);function out = pcdMeanFilter(pc, radiusMm)
% 均值滤波(模仿 block-wise 风格)
% pc        : pointCloud 对象
% radiusMm  : 邻域半径,单位 mm
xyz = pc.Location;
radius = radiusMm; 
n = size(xyz, 1);
newXYZ = zeros(n, 3, 'single');kd = createns(xyz, 'NSMethod', 'kdtree');
block = 5000;                   % 按内存可调
for i = 1:block:nir = min(i + block - 1, n);idxCell = rangesearch(kd, xyz(i:ir, :), radius);for j = i:iridx = idxCell{j-i+1};newXYZ(j, :) = mean(xyz(idx, :), 1);   % 均值坐标end
end
% 重建 pointCloud,保留颜色等信息
out = pointCloud(newXYZ, 'Color', pc.Color, ...'Normal', pc.Normal);
end

2、GUI版本

function meanFilterGUI
% 均值滤波 GUI —— 2020a 兼容
% 1. 浏览选点云  2. 邻域半径/mm 滑块  3. 实时均值滤波  4. 保存结果fig = figure('Name','均值滤波工具','NumberTitle','off',...'MenuBar','none','ToolBar','none','Position',[100 100 1280 720]);%% ---------- 左侧图像区(78 %) ----------
imgWidth = 0.78;
panelW   = imgWidth/2 - 0.01;pnlOrig = uipanel('Parent',fig,'Units','normalized',...'FontSize',16,'Position',[0.02 0.02 panelW 0.96],'Title','原始点云');
pnlFilt = uipanel('Parent',fig,'Units','normalized',...'FontSize',16,'Position',[0.02+panelW+0.01 0.02 panelW 0.96],'Title','均值滤波');axOrig = axes('Parent',pnlOrig,'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]);%% ---------- 右侧控制区(22 %) ----------
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;% 1. 浏览
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;% 2. radius/mm
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;sliderR = uicontrol('Parent',pnlCtrl,'Style','slider','Min',1,'Max',100,'Value',50,...'FontSize',16,'Units','normalized','Position',[0.05 yTop-btnH 0.65 btnH],...'Callback',@refreshFilter);
txtR    = uicontrol('Parent',pnlCtrl,'Style','edit','String','50',...'FontSize',16,'Units','normalized','Position',[0.75 yTop-btnH 0.20 btnH],...'Callback',@editRCB);
yTop = yTop - btnH - gap;% 3. 保存
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;
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;endshowPointCloud(axOrig,ptCloudOrig);N = ptCloudOrig.Count;set(lblInfo,'String',sprintf('已加载:%s  (%d 点)',file,N));refreshFilter();endfunction refreshFilter(~,~)if isempty(ptCloudOrig), return; endr = get(sliderR,'Value');ptCloudFilt = pcdMeanFilter(ptCloudOrig,r);showPointCloud(axFilt,ptCloudFilt);set(txtR,'String',num2str(r));endfunction editRCB(src,~)v = str2double(get(src,'String'));if isnan(v), v = 50; endv = max(1,min(100,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)cla(ax); set(ax,'Color','w');pcshow(pointCloud(nan(0,3)),'Parent',ax);  % 2020a 暖启动pcshow(pc,'Parent',ax,'MarkerSize',35);axis(ax,'tight'); grid(ax,'on'); view(ax,3);end%% ---------- 均值滤波核心 ----------function out = pcdMeanFilter(pc,radiusMm)xyz  = pc.Location;n    = size(xyz,1);radius = radiusMm;          % 转米kd = createns(xyz,'NSMethod','kdtree');block = 10000;                   % 批查询xyzMean = zeros(n,3,'like',xyz);for i = 1:block:nir = min(i+block-1,n);idxCell = rangesearch(kd, xyz(i:ir,:), radius);for k = i:iridx = idxCell{k-i+1};if numel(idx)>0xyzMean(k,:) = mean(xyz(idx,:),1);elsexyzMean(k,:) = xyz(k,:);   % 无邻域保持原值endendendout = pointCloud(xyzMean, ...'Color', pc.Color, ...'Normal', pc.Normal);end
end

二、点云均值滤波的结果

        使用依旧是GUI(真香!!),通过调节均值半径,可以看到窗帘明显边沿噪声部分被抑制删除,感兴趣的童鞋可以自己再试试别的点云。

就酱,下次见^-^

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

相关文章:

  • 应用部署(后端)
  • 手机网站吧怎样做一个app平台
  • 用AI重塑电商,京东零售发布电商创新AI架构体系Oxygen
  • csv、pdf文件预览uniapp-H5
  • Wiley出版社WileyNJDv5_Template模板编译不能生成PDF解决办法
  • 蓝色网站配色方案贵州省城乡和住房建设厅网站首页
  • 广州微网站建设咨询网站建设500错误代码
  • 凡科建站建网站网络建设公司排行
  • 编写 GStreamer 插件2:编写插件的基础知识(二)
  • 收录网站的平台有哪些上海建设网站浦东新区污水管网工程
  • 学校门户网站建设必要性爱趣网
  • 深入用户评测:腾讯CodeBuddy打造用户首选的AI全栈开发工具
  • 深圳网站设计制作建设微信做单页的网站
  • 基于微信小程序高仿背单词消除游戏
  • 蒸汽机革命后工业生产方式的变革与AI智能名片S2B2C商城小程序的影响
  • 山东省水利建设市场信用信息平台网站网站 系统 区别
  • 寻找集团网站建设网站怎么做搜索引擎优化_
  • 从 “有人值守” 到 “少人运维”:智能巡检机器人重塑配电室管理模式
  • Docker详解(一)Docker的核心概念及基本操作
  • R²D²深度解析:NVIDIA三大神经网络突破如何变革机器人学习
  • 企业接待机器人知识库如何分钟级构建
  • Docker 从入门到精通:全方位掌握容器化技术
  • 可以做兼职笔译的网站企业查询天眼查官网
  • framer-motion:React 动画库完全指南
  • 网站开发 面试 适当吹牛建设工程质量监理协会网站
  • BI磁吸布局 (2) 基于react-grid-layout扩展的布局方式
  • python(42) : 监听本地文件夹上传到服务器指定目录
  • c 网站建设报告外贸企业网站推广
  • 成都网站建设熊掌号网站 域名 授权服务器 分布式
  • 使用python+flask设置挡板