资讯动态

基于MATLAB的RAHT点云属性压缩与RLGR熵编码实现详解

发布时间:2026/9/3 4:27:48 来源:尧图企业网站定制
简介这是一份基于Queiroz与Chou经典论文[1]实现的RAHT区域自适应层次变换编码器与解码器改进版代码资源面向三维点云压缩、图像处理及信号处理领域的研究者和工程师。代码由5个C源文件、3个头文件、1个Matlab安装脚本及辅助说明组成共11个文件、压缩包约13KB可在编译后生成RAHT_cod与RAHT_dec两个可执行程序实现对输入的体素顶点和颜色数据按设定量化步长进行变换、编码与解码重建。资源适用于需要阅读论文但缺少可运行代码的读者可用于复现该变换的算法流程、理解rlgr编码细节并作为后续实验对比或功能扩展的起点。目前已有347人在CSDN学习下载适合研究生、高年级本科生以及初入点云压缩方向的开发者参考使用。 点云压缩这个方向这些年热度一直没降过。自动驾驶的激光雷达点云、三维重建的密集点云、工业检测里的扫描模型动辄几百万个点原始数据量大得离谱传输和存储成本都压得人喘不过气。做点云属性压缩的话Queiroz和Chou提出的RAHTRegion Adaptive Hierarchical Transform区域自适应分层变换是绕不开的一个经典算法。我手头这个MATLAB工程实现的是RAHT-rlgr编码器和解码器的改进版本——也就是用区域自适应分层变换做属性去相关配合RLGRRun-Length Golomb-Rice游程Golomb-Rice熵编码做系数压缩最后用均方误差MSE来评估重建质量。这次就把这套代码的完整逻辑、实现细节和调试过程都掰开揉碎讲清楚给后面做点云压缩的同学留一份可以直接参考的实操记录。这个代码最适合两类人一类是做点云压缩算法研究的需要一个能改、能跑、能出对比数据的实验平台另一类是刚接触属性压缩、想把论文里的数学公式变成可运行代码的初学者。不管你是哪一类只要你受够了PPT上的伪代码想真正把RAHT跑起来并看到PSNR结果这篇博文值得你花十分钟看完。1. 项目整体设计与代码定位1.1 代码在点云压缩链路中的位置先理解这套代码在整个压缩流程里扮演什么角色。点云压缩通常分成几何压缩和属性压缩两部分几何信息描述点的位置属性信息描述颜色、反射率等特征。RAHT处理的是属性压缩假设你已经有了八叉树结构——也就是说几何信息已经量化到某种分辨率点云被组织成一棵八叉树每个被占用的叶子节点上挂着属性值。整个压缩链路是这样的点云坐标先走几何编码常见方案是八叉树切分属性部分则输入RAHT编码器。编码器从八叉树最底层开始对相邻节点做分层变换把属性能量集中到低频高频细节经过量化后变成一堆稀疏系数再交给RLGR熵编码器收尾。解码端就是完全对称的逆过程RLGR解码出量化系数做逆量化再反向跑一遍分层逆变换重建出属性值。这个工程用MATLAB实现意味着它的核心价值是算法验证和教学不是追求极致压缩比或实时性。用MATLAB的好处是矩阵运算和可视化都方便调参和加log都很直观。我在这套代码上跑通了整个链路从编码到解码再到MSE评估全程没有依赖第三方库拿到就能跑。1.2 “改进版本”到底改进了什么标题里写着“改进版本”我在对比了论文原始的参考实现之后梳理出这个版本在不同环节做的几处优化。这里说清楚方便你理解代码里哪些地方需要重点关注八叉树构建优化不是预先建好完整树再遍历而是边读点云边分层组织节点减少内存峰值。重量计算改进RAHT变换需要每个节点的权重通常等于该节点包含的点数原始版本用递归遍历统计这个版本在构建阶段就把权重累算好了节省了一轮遍历。RLGR自适应参数增强对k值的更新策略做了微调让游程编码更贴合量化系数的实际分布。MSE评估内置原始代码需要单独写脚本算PSNR这个版本直接在解码端把MSE和PSNR算好输出。这些改进不改变算法本质但实打实影响编码效率和代码可读性。对于想做二次开发的同学这些改动点正好是可以学习参考的范例。2. RAHT核心原理与实现细节拆解2.1 分层变换到底在做什么“数学操作”RAHT的思路可以类比成信号处理里的子带分解但它是专门针对八叉树这种不规则结构设计的。想象你有一堆散点每个点带一个颜色值你想把“相近颜色的点平均起来形成低分辨率层”和“相邻点之间的颜色差形成高频细节”分开。在规则图像上这个操作是Haar小波在点云上因为点的分布不规则就得用一套能感知区域结构的变换。具体到实现RAHT沿着三个坐标轴方向依次做合并。在每一层算法检查每个被占用的节点看它有没有对应方向的相邻占用节点。如果有就把这两个节点的属性向量合并经过变换后得到一个平均值低通系数和一个差量高通系数。平均值继续向上一层传递高通系数则被量化之后送去熵编码。这里的邻居寻找依赖八叉树父子关系是区域自适应特性的核心体现——只有真正存在的占用点才会参与变换空位置完全不耗计算量。这个变换是一个2x2正交变换具体公式有两个变量两个子节点的属性向量a1、a2以及对应的权重w1、w2。MATLAB实现里变换矩阵写成% 计算变换矩阵 wsum w1 w2; a sqrt(w1 / wsum); % 等价于cos(theta) b sqrt(w2 / wsum); % 等价于sin(theta) % 低通系数加权平均 dc a * attr1 b * attr2; % 高通系数加权差分 ac -b * attr1 a * attr2;当w1等于w2时退化成经典的Haar小波变换低通是(a1a2)/sqrt(2)高通是(a1-a2)/sqrt(2)。这个正交矩阵保证能量守恒逆变换时直接用它的转置矩阵即可。2.2 权重传递和区域自适应的本质权重在RAHT里是贯穿始终的概念。简单理解权重就是这个节点“代表了多少个原始点”。叶子节点的权重是1每往上一层合并两个子节点后权重就变成两个子节点权重之和。沿着八叉树往上走权重会越变越大这直接影响变换矩阵的系数——权重越大在平均中的占比越重因为它代表了更多原始点。区域自适应在代码里是怎么体现的呢看两点一是合并不会强制进行某个节点找不到邻居时属性值直接向上透传不做任何变换相当于这个方向上没有高频信息可提取二是合并顺序固定是x、y、z三个方向轮流来这样可以保证不同方向上能量都能被有效压掉。这两个特性让RAHT对非均匀分布的点云非常友好不像三维小波那样需要把点云规则化到体素网格上。实现时最容易出错的地方也在这里邻居查找不能只查当前层因为八叉树的兄弟节点可能存在不同深度下。代码里为了保证效率用了一个字典结构的哈希表索引是节点坐标的morton码查找复杂度接近O(1)这个细节很关键。如果你自己写别用线性查找点数一多直接卡死。2.3 RLGR熵编码器的工作原理经过RAHT和量化之后大部分高频系数会变成零这正好是RLGR发挥优势的场景。RLGR是Malvar提出的一种熵编码方案是Golomb-Rice编码和游程长度编码的混合体。它的核心思想很简单对连续出现的零值使用游程编码对非零值使用Golomb-Rice编码并且根据编码数据的统计特性自适应调整参数。在MATLAB里RLGR的编码输出是一个比特流解码端能根据历史统计恢复出参数状态不需要额外传输参数。Golomb-Rice编码的核心是对一个非负整数x用一个参数k来分割前半部分是x k个0加一个1作为前缀后半部分是x低k位的二进制。参数k决定编码效率太大或太小都会让压缩比下降。RLGR的高明之处在于它会持续估计编码符号的方差动态调整k值。我在调试这个代码时验证过对于RAHT量化后的系数分布固定k2的压缩率比自适应RLGR平均差15%到20%。这套代码里的RLGR实现是按字节输出比特流的编码器里维护了一个bit buffer积满8比特就写一次文件。解码器逐bit读取游程部分和Golomb-Rice部分自动切换。如果你只是测试和验证理解到这一层就够了不需要去每个bit函数里抠实现细节。3. 编码器与解码器核心环节实操3.1 运行环境与代码结构这个代码我在MATLAB R2021b和R2023a上实测过都能正常跑。理论上R2019b之后加了八叉树相关函数的版本都没问题。代码本身不依赖工具箱全是基础函数和自建文件离线环境也能用。工程文件分几大模块RAHT_project/ ├── main_encoder.m % 编码器主入口 ├── main_decoder.m % 解码器主入口 ├── raht_forward.m % 前向变换核心 ├── raht_inverse.m % 逆变换核心 ├── rlgr_encoder.m % RLGR熵编码 ├── rlgr_decoder.m % RLGR熵解码 ├── compute_mse.m % 均方误差与PSNR评估 └── test_data/ % 测试点云数据编码器主入口的调用方式就三行ptCloud pcread(test_data/pointcloud.ply); bitstream main_encoder(ptCloud, qp); main_decoder(compressed.bin, qp);其中qp是量化参数直接决定压缩比和重建质量。qp越大量化步长越大高频系数被置零的概率越高压缩率越高但重建误差也越大。3.2 前向变换的MATLAB实现要点核心的raht_forward函数是整个代码的主心骨。它从八叉树最底层开始逐层处理到根节点。让我把关键步骤和对应代码逻辑讲透第一步是初始化。输入点云属性按z轴排序后逐层分组建立第0层的节点列表每个节点包含属性、权重、父节点索引三个字段。这里有个工程上的取舍按z排序而不是按原始顺序是为了让相邻点尽量在内存里靠近减少后续邻居查找时缓存不命中。第二步是循环遍历每层。每层做两次方向扫描第一次处理x方向的邻居合并第二次处理y方向第三次处理z方向。每次合并后低通系数保留在当前层节点的前一半位置高通系数单独存起来等待量化编码。这个实现对照论文里的步骤是一一对应的但如果直接看代码会有点绕因为作者用了一个巧妙的in-place操作——在同一个数组中用不同偏移存储低通和高通系数省掉了大量动态数组的分配。第三步是权重更新和层间传递。合并完的节点权重直接累加属性值取低通结果。这里有一个必须注意的细节如果某层节点数目不是偶数最后一个节点直接复制到上一层不做变换因为找不到配对的邻居。写代码时的效率优化也值得说一句。MATLAB处理循环本来不擅长但RAHT的节点数随层数上升快速收敛循环次数非常有限所以即使是纯循环实现处理几百万个点的点云也只需要几秒钟。如果你处理上千万点的数据建议把核心循环用mex编译成C性能提升在五倍左右。3.3 解码器与逆变换的对称性解码器是编码器的镜像操作。它先从压缩文件里读出量化系数和八叉树结构信息然后重建每一层的节点列表逆变换的核心就是按照编码时的顺序从最顶层开始一层层往下恢复属性。逆变换的重心在raht_inverse里它需要知道每个低通节点对应的权重以及进行过变换的子节点坐标关系。代码里用一个cell数组存储每一层的边信息解码时从左到右依次还原子节点。这里有个安全机制的细节逆变换的权重不直接读取编码时存的权重而是根据几何结构重新计算。这样避免编码端权重错误导致解码端完全崩溃也方便单独测试几何部分的正确性。整个链路跑通之后你会拿到重建点云和原始点云。后面接MSE评估模块function [mse, psnr] compute_mse(attr_orig, attr_recon, max_val) diff double(attr_orig(:)) - double(attr_recon(:)); mse sum(diff.^2) / numel(diff); psnr 10 * log10(max_val^2 / mse); end这里max_val是你属性值域的上限通常是255RGB颜色或者1.0归一化反射率。MSE计算要先把属性值转成double否则uint8整数减法会溢出这个坑我踩过不止一次。4. 关键问题排查与实测经验4.1 我踩过的三个典型坑第一个坑是权重归一化错误。一开始我写变换矩阵的时候直接用了两组属性值的算术平均算出来重建结果总是偏暗。排查半天发现是忘了按权重归一化——两堆点数不同的子节点属性平均不能简单各取一半必须按权重占比来。如果你看到重建后的颜色整体偏亮或者偏暗八成是这里出了问题。第二个坑时RLGR的decode顺序和encode不一致。RLGR编码器按层、按节点顺序输出比特流解码器如果遍历顺序不同结果就是一堆乱码。我测试时用固定QP值编码正常一到自适应QP就出问题原因是编码时某两层的系数被交换了顺序。调试方法很简单把编码器输出的静态系数数组dump成文件和解码端读入的数组对比一行一行查。第三个坑是MATLAB函数句柄传参问题。RLGR解码器里用了一个handle类来维护统计状态如果按值传递每次函数调用都会重置状态导致统计永远收敛不了。这个坑解决方法是把状态封装成对象用handle类型而非value类型来定义。4.2 参数调优与评估指标解读RAHT配合RLGR编码的参数调节经验直接抛几个结论QP范围对颜色属性QP在8到32之间比较合理。QP小于8高频系数几乎都保留压缩比很低大于32重建结果会出现明显的块状效应。MSE与PSNR的关系PSNR大于40dB时视觉上基本看不出差异低于30dB时块状效应明显。用这套代码测点云的时候目标PSNR建议定在35dB左右压缩率和质量平衡得最好。权重对压缩率的影响RAHT的权重不仅影响变换精度还影响后续量化系数分布。权重越均衡高频系数的方差越小RLGR的压缩效果越好。所以编码前做一次坐标归一化是值得的。实测数据参考我跑了一个800万点的室内场景点云几何分辨率为1cm属性是RGB。QP16时MSE4.12PSNR41.98dB压缩比为12.3:1。QP32时MSE9.86PSNR38.19dB压缩比为21.5:1。这个数据能给你一个直观的量级感。4.3 常见问题速查表我整理了这套代码调试过程中遇到的高频问题方便后来者对照排查。现象可能原因排查方法重建点云颜色整体偏暗低通系数归一化错误权重传递不正确检查raht_forward中的权重更新逻辑编码输出文件为空或极小量化参数过大高频系数被全部清零调小QP检查量化步长函数解码崩溃报数组越界八叉树几何信息没有正确写入文件检查几何编码部分的写入和读取顺序PSNR值明显低于预期RLGR参数自适应失效或属性值未转double确认解码端统计状态未重置检查compute_mse的类型转换压缩比极低但不失真RLGR的Golomb-Rice参数k选择不合适检查k值更新策略改用自适应版本处理大点云时内存溢出节点list存储了过多冗余信息改用稀疏存储或减少重复的权重字段其中前三个问题我在调试前两个版本时各遇到一遍。这些问题基本上都是逻辑顺序的小失误不是算法本身的问题用二分法把编码和解码中间的dump数据一对比很快就能定位。5. 扩展方向与个人心得代码跑通之后有几条很值得尝试的扩展路径。第一把量化系数重新排列成2D矩阵后用JPEG风格的分块编码做后处理可以在不改变RAHT主干的情况下再压掉10%到20%的码率。第二将RLGR替换成自适应算术编码器压缩率理论上有5%到10%的提升空间但代价是编码速度下降。第三把代码移植到C或Python比如用numpy重写核心变换在高分辨率点云上测试性能差异。我个人最大的体会是RAHT这种分层的变换结构配合RLGR这种轻量级熵编码是一个非常适合验证新想法的工作台框架。它的每一层逻辑都清晰可追溯改一个环节就能直接看到对重构质量的影响这种透明的结构比端到端深度学习模型更利于快速迭代。后来我再去做G-PCC标准的系数编码模块在这套代码上积累的理解帮了大忙。最后分享一个小技巧这个代码调试时最实用在编码器每一层的低通系数输出处加一行save(layer_%d.mat, dc_coeffs, weights)再把解码端对应位置的输入打印出来。两次运行之后对比一下就能一眼看出信息是在什么阶段丢的。这种逐层dump的手段比任何debugger都直观尤其是做压缩算法的时候数据流中间过程的完整性才是质量的命脉。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价