Kinect V2存储关节点位置时出现大量重复值问题求助
我来帮你拆解下这个问题的核心原因,然后给出几个针对性的解决办法:
为什么会出现重复值?
你提到数据存储是在DrawBody方法里执行的,而这个方法通常是和渲染帧同步的(比如每秒调用60次),但你的姿态识别模块(比如Kinect、MediaPipe这类)生成新关节数据的频率往往更低(比如每秒30次)。这就导致同一个关节数据会被渲染线程重复读取、存储多次,直到新的姿态数据到来——这就是你看到连续20次左右重复值的原因。
而单值存储时你觉得“数值变化更连续”,可能是因为单值场景下你无意中避开了这个同步问题,或者单值的波动阈值更明显,让你误以为没有重复。
可行的解决方案
方案1:添加数据变化检测(精准过滤重复)
在存储数据前,先和上一次存储的数值做对比,只有当数据变化超过一个极小的精度阈值时,才执行存储操作。这样就能过滤掉渲染线程重复读取的旧数据。
示例代码(C#):
// 定义类级别的变量,保存上一次存储的关键数据 private Vector3 _lastElbowLeftPos = Vector3.Zero; private float _lastSpineBaseShoulderRHO = 0f; // 设定精度阈值,避免浮点数微小波动误判 private const float _changeThreshold = 1e-6f; // 在DrawBody方法内执行存储逻辑 public void DrawBody(IReadOnlyList<Joint> joints) { // 获取当前帧的关节数据和计算值 var currentElbowPos = joints[JointType.ElbowLeft].Position; var currentSpineMidPos = joints[JointType.SpineMid].Position; var currentRHO = spineBaseShoulderRHO; // 检查数据是否有足够幅度的变化 if (Vector3.Distance(currentElbowPos, _lastElbowLeftPos) > _changeThreshold || Math.Abs(currentRHO - _lastSpineBaseShoulderRHO) > _changeThreshold) { // 只有数据变化时才存储 gesture.L.E.AddFrameDescriptor( currentElbowPos.X, currentElbowPos.Y, currentElbowPos.Z, currentSpineMidPos.X, currentSpineMidPos.Y, currentSpineMidPos.Z, currentRHO ); // 更新上一次存储的数值 _lastElbowLeftPos = currentElbowPos; _lastSpineBaseShoulderRHO = currentRHO; } // 其他绘制逻辑... }
方案2:将存储逻辑移到数据更新回调中(最优架构)
大部分姿态识别库都会提供数据更新回调(比如Kinect的FrameArrived事件、MediaPipe的OnResults回调),这些回调只会在新的关节数据生成时触发。把存储逻辑放在这里,就能从根源上避免重复存储的问题——因为每次回调都是新数据。
伪代码示例:
// 假设这是姿态识别库提供的数据更新回调 private void OnPoseDataReceived(IReadOnlyList<Joint> joints) { // 在这里计算spineBaseShoulderRHO float spineBaseShoulderRHO = CalculateSpineBaseShoulderRHO(joints); // 直接存储,因为这里每一次都是新的姿态数据 gesture.L.E.AddFrameDescriptor( joints[JointType.ElbowLeft].Position.X, joints[JointType.ElbowLeft].Position.Y, joints[JointType.ElbowLeft].Position.Z, joints[JointType.SpineMid].Position.X, joints[JointType.SpineMid].Position.Y, joints[JointType.SpineMid].Position.Z, spineBaseShoulderRHO ); // 触发绘制逻辑(如果需要的话) DrawBody(joints); }
这个方案把数据处理和渲染绘制做了分离,是更健壮、更符合工程规范的做法。
方案3:控制存储频率(简单粗暴的过滤)
如果暂时无法调整代码架构,可以设置一个固定的时间间隔,每隔一段时间才存储一次数据,过滤掉过于密集的重复帧。
示例代码:
private DateTime _lastStoreTime = DateTime.MinValue; // 设置存储间隔,比如33ms对应每秒30次存储,和常见的姿态数据更新频率匹配 private const int _storeIntervalMs = 33; public void DrawBody(IReadOnlyList<Joint> joints) { if (DateTime.Now - _lastStoreTime > TimeSpan.FromMilliseconds(_storeIntervalMs)) { gesture.L.E.AddFrameDescriptor( joints[JointType.ElbowLeft].Position.X, joints[JointType.ElbowLeft].Position.Y, joints[JointType.ElbowLeft].Position.Z, joints[JointType.SpineMid].Position.X, joints[JointType.SpineMid].Position.Y, joints[JointType.SpineMid].Position.Z, spineBaseShoulderRHO ); _lastStoreTime = DateTime.Now; } // 其他绘制逻辑... }
这个方法的缺点是可能会错过一些快速变化的细节,但适合对数据密度要求不高的场景。
总结
优先推荐方案2,从架构层面解决同步问题;如果暂时没法调整代码结构,方案1的变化检测是更精准的选择,能保留所有有效数据的同时过滤重复。
内容的提问来源于stack exchange,提问作者SoupCode0169

