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

Matlab通过GUI实现点云的半径滤波(Radius Outlier Removal)

        本节我们分享使用Matlab进行点云的半径滤波。半径滤波(Radius Outlier Removal)是一种基于局部密度的去噪方法,通过设定一个球形搜索半径和最小邻居点数,剔除“孤立”或“稀疏”的离群点,从而保留结构化的主体点云。该方法实现简单、参数少,对无明显离群簇的单点噪声尤其有效。

一、核心原理
1. 对每个点 p,以半径 r 搜索球形邻域。  
2. 统计邻域内点的数量 n。  
3. 若 n < Nmin,则认为 p 为离群点并移除;否则保留。  
4. 遍历全部点后,得到滤波后点云。

二、MATLAB 实现流程
1. 读取点云
建议采用 `pcread` 或 `pointCloud` 对象,确保包含 Location 与 Intensity 等字段。  
2. 构建 KD-Tree  
使用 `KDTreeSearcher` 加速邻域搜索。  
3. 半径搜索  
调用 `rangesearch` 返回各点邻域索引,计算邻居数。  
4. 生成掩膜 
根据最小邻居阈值生成逻辑掩膜,提取对应点坐标与强度。  
5. 重构点云  
利用保留的坐标与强度新建 `pointCloud` 对象。  
6. 可视化/保存  
使用 `pcshow` 对比滤波前后效果,并用 `pcwrite` 写出结果。  

        示例代码(保留强度):

function pcOut = radiusFilter(pcIn, radius, minN)pts   = pcIn.Location;inten = pcIn.Intensity;kdt   = KDTreeSearcher(pts);nei   = rangesearch(kdt, pts, radius);mask  = cellfun(@numel, nei) >= minN;pcOut = pointCloud(pts(mask,:), 'Intensity', inten(mask));
end

三、关键参数选择
- radius:与点云平均间距相关,一般取 2–5 倍平均点距;过小易“过度”删除,过大则去噪能力下降。  
- minN:典型范围 3–10;对高密度区域可上调,对稀疏区域宜下调。  
- 建议先对样本区域手动估计点距,再在小块点云上尝试不同组合,观察去噪/保形权衡。

四、常见扩展与组合
- 统计滤波 + 半径滤波:先用统计滤波去除大偏离离群簇,再用半径滤波清理稀疏残留点。  
- 体素下采样 + 半径滤波:先降低分辨率,可加快半径搜索并减少内存占用。  
- 保留附加属性:强度、颜色、法向量等均可按同一掩膜同步滤波,保证后续算法(配准、分割、识别)可用。

五、主要应用领域
1. 自动驾驶:去除激光雷达扫描中的飞点、雨雾噪声,提升地面/障碍物分割精度。  
2. 移动测量/测绘:清理车载/机载 LiDAR 大范围场景中的孤立反射点,改善 DEM/DSM 生成质量。  
3. 工业检测:在缺陷识别或尺寸测量前剔除异常反射,保留工件真实轮廓。  
4. 室内外 SLAM:作为前端预处理步骤,减少错误特征关联,提高建图一致性。  
5. 文化遗产与建筑建模:滤除扫描文物、古建筑时产生的飘移点,为后续网格重建提供干净输入。  

        通过合理设置半径与邻居阈值,MATLAB 半径滤波能在保持几何特征的同时显著提升点云数据质量,是各类点云处理流程中不可或缺的预处理环节。

本次的数据依然是,你看,我一说依然,老同志就知道了,没错,就是兔砸!!!

一、点云半径滤波的程序

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. 半径滤波(30 mm,≥30 点)
radius   = 30;
minNeigh = 30;
cloudFilt = pcdRadiusFilter(ptCloud, minNeigh, radius);
fprintf('半径滤波(半径=%d mm,邻域≥%d)后 %d 个点\n', radius, minNeigh, cloudFilt.Count);%% 3. 可视化
figure('Name', '原始点云', 'NumberTitle', 'off');
pcshow(ptCloud); axis on; view(3);figure('Name', '半径滤波', 'NumberTitle', 'off');
pcshow(cloudFilt); axis on; view(3);function out = pcdRadiusFilter(pc, nbPoints, radiusMm)
% 半径滤波(模仿 block-wise 风格)
% pc        : pointCloud 对象
% nbPoints  : 邻域最少点数
% radiusMm  : 半径,单位 mm
xyz = pc.Location;
radius = radiusMm / 1000;       % 转米
n = size(xyz, 1);
count = zeros(n, 1, 'uint16');kd = createns(xyz, 'NSMethod', 'kdtree');
block = 5000;                   % 按内存可调
for i = 1:block:nir = min(i + block - 1, n);count(i:ir) = cellfun('length', rangesearch(kd, xyz(i:ir, :), radius));
end
mask = count >= nbPoints;
out  = select(pc, find(mask));
end

2、GUI版本

function radiusFilterGUI
% 半径滤波 GUI —— 2020a 兼容
% 1. 浏览选点云  2. nb_points 滑块  3. radius/mm 滑块  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. nb_points
uicontrol('Parent',pnlCtrl,'Style','text','String','邻域最少点数',...'FontSize',12,'FontWeight','bold','Units','normalized','Position',[0.05 yTop-txtH 0.90 txtH],...'HorizontalAlignment','left');
yTop = yTop - txtH - gap;sliderNp = uicontrol('Parent',pnlCtrl,'Style','slider','Min',5,'Max',200,'Value',30,...'FontSize',16,'Units','normalized','Position',[0.05 yTop-btnH 0.65 btnH],...'Callback',@refreshFilter);
txtNp    = uicontrol('Parent',pnlCtrl,'Style','edit','String','30',...'FontSize',16,'Units','normalized','Position',[0.75 yTop-btnH 0.20 btnH],...'Callback',@editNpCB);
yTop = yTop - btnH - gap;% 3. 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',30,...'FontSize',16,'Units','normalized','Position',[0.05 yTop-btnH 0.65 btnH],...'Callback',@refreshFilter);
txtR    = uicontrol('Parent',pnlCtrl,'Style','edit','String','30',...'FontSize',16,'Units','normalized','Position',[0.75 yTop-btnH 0.20 btnH],...'Callback',@editRCB);
yTop = yTop - btnH - gap;% 4. 保存
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; endnp = round(get(sliderNp,'Value'));r  = get(sliderR,'Value');ptCloudFilt = pcdRadiusFilter(ptCloudOrig,np,r);showPointCloud(axFilt,ptCloudFilt);set(txtNp,'String',num2str(np));set(txtR,'String',num2str(r));endfunction editNpCB(src,~)v = str2double(get(src,'String'));if isnan(v), v = 30; endv = max(5,min(200,round(v)));set(sliderNp,'Value',v);refreshFilter();endfunction editRCB(src,~)v = str2double(get(src,'String'));if isnan(v), v = 30; 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 = pcdRadiusFilter(pc,nbPoints,radiusMm)xyz = pc.Location;radius = radiusMm/1000;n = size(xyz,1);count = zeros(n,1,'uint16');kd = createns(xyz,'NSMethod','kdtree');block = 5000;            % 根据内存自己调for i = 1:block:nir = min(i+block-1,n);count(i:ir) = cellfun('length', rangesearch(kd,xyz(i:ir,:),radius));endmask = count >= nbPoints;out  = select(pc,find(mask));end
end

二、点云半径滤波的结果

        可以看到,随着邻域个数和半径的变化,滤波结果也产生了变化。不过想要好的结果,需要大量的时间调节,而且对于非标件也是不好统一处理的。感兴趣的童鞋可以试试啊。

就酱,下次见^-^

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

相关文章:

  • 基于MATLAB的8QAM调制解调仿真与BER性能分析
  • 2025年AI证书报考指南:CAIP/华为/谷歌认证
  • 合肥营销型网站建设开发河南城源建设工程有限公司网站
  • 若依 springboot websocket
  • 开源 C# 快速开发(三)复杂控件
  • Visual Studio使用C++配置OpenCV环境,同时添加模板以4.12为例
  • JUnit 4 + Spring Boot 测试依赖
  • HTML应用指南:利用POST请求获取全国索尼体验型零售店位置信息
  • html网站源码 html网页模板下载
  • 做网站接广告了解基本的php wordpress
  • 房地产手机网站模板网站推广公司ihanshi
  • 推荐一个网站
  • 前端可视化第一章:PixiJS入门指南
  • 时间序列分析新视角:单变量预训练 多变量微调
  • coqui-ai/TTS 安装
  • linux命令dd单刷镜像文件
  • 奔驰押注中国AI,国产大模型上车
  • 笔记(C++篇)—— Day 11
  • Cursor推出全新文档中心:甚至提供详细的中文版本
  • 选择合肥网站建设html的基本结构
  • Linux文件系统调用详解:底层操作到高级应用
  • 基于51单片机的供电保护系统
  • 网站建设技术交流制作公司网页价钱
  • 前端Bug实录:为什么表格筛选条件在刷新时神秘消失?
  • 关于做视频网站的一些代码网站备案号是什么样子
  • 专业定制网站开发上海手机网站建设价格
  • 《postman》软件下载_《postman》安装包下载_《postman》安装教程下载_《postman》网盘下载
  • 双模更超模!飞利浦双模办公娱乐显示器27E2N5900RW优雅登场!
  • TDengine 聚合函数 HYPERLOGLOG 用户手册
  • 威海网站优化公司济南简单的网站制作