首页 > 解决方案 > 从 DEPTH16 生成世界坐标点云

问题描述

在具有单独相机传感器和深度传感器的 android 设备上,我试图将 DEPTH16 输出映射到 ARcore 中可用的世界坐标点云。执行此操作所需的主要计算是将深度输出映射到主相机的坐标系(ARcore 可以从那里平移)。

据我了解,我需要应用深度相机内在函数(焦距、主点偏移等),然后将镜头姿势旋转应用于主相机坐标系统,然后应用解释深度传感器这一事实的平移距离相机镜头几毫米。这主要记录在相机特性 API 文档中,但在实践中似乎对我不起作用。

这就是我正在做的事情:

  1. 从深度传感器的 CameraCharacteristics 中提取内部函数、镜头姿态旋转和镜头姿态平移。这允许填充以下参数:
    public class Intrinsics {
        float width;
        float height;
        float focalLength;
        // intrinsic matrix:
        float f_x;
        float f_y;
        float c_x;
        float c_y;
        float s;
        // lens pose rotation
        float r_x;
        float r_y;
        float r_z;
        float r_w;
        // lens pose translation
        float t_x;
        float t_y;
        float t_z;
    }
  1. 应用内在转换。从传感器坐标系来看,假设我的右下角像素记录了 1m 的深度。这给出了 x, y, z 的 [239, 179, 1.00] 坐标。

如果我根据焦距和主要点执行以下算术,我似乎会得到看起来正确的输出:

double[] intrinsicAdjusted = new double[]{
  (x - i.c_x) * depth / i.f_x,
  (y - i.c_y) * depth / i.f_y,
  depth
};

但是,我也尝试过使用 Camera2 文档中建议的转换矩阵(但相反,因为我想要相机到世界,而不是世界到相机):

Mat point = new Mat(3, 1, CvType.CV_32F);
point.put(0, 0, (double)x, (double)y, depth);
Mat K = new Mat(3, 3, CvType.CV_32F);
K.put(0,0,
  i.f_x, i.s,   i.c_x,
  0,     i.f_y, i.c_y,
  0,     0,     1.0);
Mat Kinverse = K.inv();
Mat instrinsicAdjusted = new Mat(1, 3, CvType.CV_32F);
Core.gemm(Kinverse, point, 1, new Mat(), 0, instrinsicAdjusted, 0);

偏斜为零,所以我认为这些方法应该是一样的,但事实并非如此。

  1. 应用 API 文档建议的旋转将坐标更改为主相机的坐标。我从以下(使用 OpenCV)得到了非常虚假的结果:
Mat point = new Mat(3, 1, CvType.CV_32F);
point.put(0, 0, instrinsicAdjusted[0], instrinsicAdjusted[1], instrinsicAdjusted[2]);
Mat R = new Mat(3, 3, CvType.CV_32F);
R.put(0, 0,
  1 - 2*(i.r_y*i.r_y) - 2*(i.r_z*i.r_z), 2*i.r_x*i.r_y - 2*i.r_z*i.r_w, 2*i.r_x*i.r_z + 2*i.r_y*i.r_w,
  2*i.r_x*i.r_y + 2*i.r_z*i.r_w, 1 - 2*i.r_x*i.r_x - 2*i.r_z*i.r_z, 2*i.r_y*i.r_z - 2*i.r_x*i.r_w,
  2*i.r_x*i.r_z - 2*i.r_y*i.r_w, 2*i.r_y*i.r_z + 2*i.r_x*i.r_w, 1 - 2*i.r_x*i.r_x - 2*i.r_y*i.r_y);
Mat rotated = new Mat(1, 3, CvType.CV_32F);
Core.gemm(R, point, 1, new Mat(), 0, rotated, 0);
  1. 如果事情在轮换中没有出错,大概翻译会起作用:
Mat translated = new Mat(3, 1, CvType.CV_32F);
translated.put(0, 0, i.t_x, i.t_y, i.t_z);
Core.add(translated, rotated, translated);

因此,这种总体方法是否正确?为什么我的内在调整会得到两个不同的结果?为什么轮换似乎不起作用?

标签: androidarcoreandroid-camera2

解决方案


推荐阅读