手部关节点角度计算:Python转C++实现优化咨询
优化C++手部关节角度计算代码(减少循环)
用Eigen库替代手动循环
Python中numpy的向量/矩阵批量操作,在C++里可以用Eigen库直接实现,避免逐元素遍历的循环。比如批量计算多组向量的点积:// 假设A、B每行对应一组关节向量(3维) Eigen::MatrixXd A(num_joint_pairs, 3); Eigen::MatrixXd B(num_joint_pairs, 3); // 批量计算所有向量对的点积,结果存为一维向量 Eigen::VectorXd dot_products = (A.array() * B.array()).rowwise().sum();Eigen底层会自动用SIMD指令优化,效率远高于手动循环。
预批量生成关节向量,统一计算角度
不要逐个处理关节对,先一次性提取所有需要的向量,再批量完成归一化、点积和角度计算:// landmarks存储所有关节点,每行是(x,y,z)坐标 Eigen::MatrixXd landmarks(total_landmarks, 3); // 提前定义所有需要计算角度的关节三元组索引(比如[j1,j2,j3]表示计算j1-j2-j3的夹角) std::vector<int> j1_indices = {0, 1, 2, ...}; // 所有三元组的第一个关节索引 std::vector<int> j2_indices = {1, 2, 3, ...}; // 所有三元组的中间关节索引 std::vector<int> j3_indices = {2, 3, 4, ...}; // 所有三元组的第三个关节索引 // 批量提取并计算向量:vec1 = j2 - j1,vec2 = j3 - j2 Eigen::MatrixXd vec1 = landmarks(j2_indices, Eigen::all) - landmarks(j1_indices, Eigen::all); Eigen::MatrixXd vec2 = landmarks(j3_indices, Eigen::all) - landmarks(j2_indices, Eigen::all); // 批量归一化向量 Eigen::VectorXd norms1 = vec1.rowwise().norm(); Eigen::VectorXd norms2 = vec2.rowwise().norm(); Eigen::MatrixXd vec1_norm = vec1.array().colwise() / norms1.array(); Eigen::MatrixXd vec2_norm = vec2.array().colwise() / norms2.array(); // 批量计算点积并转换为角度 Eigen::VectorXd dots = (vec1_norm.array() * vec2_norm.array()).rowwise().sum(); dots = dots.array().max(-1.0).min(1.0); // 修正数值误差,避免acos越界 Eigen::VectorXd angles = dots.array().acos() * 180.0 / M_PI;这种方式把多次循环合并成少数几次矩阵操作,大幅减少循环次数。
时序数据用Eigen Tensor批量处理
如果是给LSTM输入的多帧时序数据,用Eigen的Tensor模块处理三维张量(帧数×关节数×3),直接跨帧批量运算:#include <Eigen/Tensor> using Tensor3D = Eigen::Tensor<double, 3>; // 张量shape:[总帧数, 关节数, 3] Tensor3D landmarks_tensor(num_frames, total_landmarks, 3); // 批量提取某关节对的向量(所有帧) Tensor3D vec1 = landmarks_tensor.slice(Eigen::array<int,3>{0, j2_idx, 0}, Eigen::array<int,3>{num_frames, 1, 3}) - landmarks_tensor.slice(Eigen::array<int,3>{0, j1_idx, 0}, Eigen::array<int,3>{num_frames, 1, 3}); // 后续的归一化、点积、角度计算都可以批量完成,无需逐帧循环无第三方库时用SIMD手动优化
如果不能引入Eigen,可以用CPU的SIMD指令(比如AVX/SSE)批量处理多个向量。比如用AVX一次计算4组3维向量的点积:// 假设vec1_x、vec1_y、vec1_z存储多组向量的x/y/z分量(float类型) // vec2_x、vec2_y、vec2_z同理 float dot_products[num_joint_pairs]; for (int i = 0; i < num_joint_pairs; i += 4) { __m256 v1x = _mm256_load_ps(&vec1_x[i]); __m256 v1y = _mm256_load_ps(&vec1_y[i]); __m256 v1z = _mm256_load_ps(&vec1_z[i]); __m256 v2x = _mm256_load_ps(&vec2_x[i]); __m256 v2y = _mm256_load_ps(&vec2_y[i]); __m256 v2z = _mm256_load_ps(&vec2_z[i]); __m256 dot = _mm256_add_ps( _mm256_add_ps(_mm256_mul_ps(v1x, v2x), _mm256_mul_ps(v1y, v2y)), _mm256_mul_ps(v1z, v2z) ); _mm256_store_ps(&dot_products[i], dot); }这种方式比普通单元素循环效率提升3-4倍左右。
内容的提问来源于stack exchange,提问作者M.Akyuzlu
相关产品推荐
相关产品推荐

