【问题标题】:How to align RGB and Depth image of Kinect in OpenCV?如何在 OpenCV 中对齐 Kinect 的 RGB 和深度图像?
【发布时间】:2014-03-17 22:55:19
【问题描述】:

我有一个 C++ 项目,我在其中使用 OpenCV 和 Libfreenect。我不想包含像 OpenNI 这样大而重的东西,并在此过程中创建 OpenCV 安装依赖项。我想使用here 提供的校准信息来使 RGB 和深度图像不失真并对齐。

根据相机矩阵和畸变系数单独对图像进行去畸变很容易。但现在我很困惑如何使用校正和投影矩阵来对齐 RGB 和深度图像,以便它们从相同的角度向我展示相同的东西。在搜索了相当长的一段时间后,我无法确定它应该如何与 OpenCV 一起工作。一个模糊的估计是 reprojectImageTo3D()warpPerspective() 可能会被使用,但我不确定如何使用。

我该如何解决这个问题?我正在使用旧的 XBOX360 Kinect(原始差异值范围为 0-2047)。

更新

这是我目前写的部分代码:

// I use callback functions to get RGB (CV_8UC3) and depth (CV_16UC1)
// I undistort them and call the following method
void AlignImages(cv::Mat& pRGB, cv::Mat& pDepth) {

    rotationMat = (cv::Mat_<double_t>(3,3) << 9.9984628826577793e-01, 1.2635359098409581e-03, -1.7487233004436643e-02, -1.4779096108364480e-03, 9.9992385683542895e-01, -1.2251380107679535e-02, 1.7470421412464927e-02, 1.2275341476520762e-02, 9.9977202419716948e-01);
    translationMat = (cv::Mat_<double_t>(3,1) << 1.9985242312092553e-02, -7.4423738761617583e-04, -1.0916736334336222e-02);


    // make a copy in float to convert raw depth data to physical distance
    cv::Mat tempDst;
    pDepth.convertTo(tempDst, CV_32F);

    // create a 3 channel image of precision double for the 3D points
    cv::Mat tempDst3D = cv::Mat(cv::Size(640, 480), CV_64FC3, double(0));

    float_t* tempDstData = (float_t*)tempDst.data;
    double_t* tempDst3DData = (double_t*)tempDst3D.data;

    size_t pixelSize = tempDst.step / sizeof(float_t);
    size_t pixel3DSize = tempDst3D.step / sizeof(double_t);

    for (int row=0; row < tempDst.rows; row++) {
        for (int col=0; col < tempDst.cols; col++) {

            // convert raw depth values to physical distance (in metres)
            float_t& pixel = tempDstData[pixelSize * row + col];
            pixel = 0.1236 * tanf(pixel/2842.5 + 1.1863);

            // reproject physical distance values to 3D space
            double_t& pixel3D_X = tempDst3DData[pixel3DSize * row + col];
            double_t& pixel3D_Y = tempDst3DData[pixel3DSize * row + col +1];
            double_t& pixel3D_Z = tempDst3DData[pixel3DSize * row + col + 2];

            pixel3D_X = (row - 3.3930780975300314e+02) * pixel / 5.9421434211923247e+02;
            pixel3D_Y = (col - 2.4273913761751615e+02) * pixel / 5.9104053696870778e+02;
            pixel3D_Z = pixel;

        }
    }

    tempDst3D = rotationMat * tempDst3D + translationMat;
}

我直接使用了数字而不是将它们分配给变量,但这在理解逻辑上应该不是问题。此时,我应该做到以下几点:

P2D_rgb.x = (P3D'.x * fx_rgb / P3D'.z) + cx_rgb
P2D_rgb.y = (P3D'.y * fy_rgb / P3D'.z) + cy_rgb

但我不明白我该怎么做,确切地说。也许我完全走错了方向。但我找不到任何这样做的例子。

【问题讨论】:

  • 你能用 openni 代替 openkinect/libfreenect 吗?
  • 我认为您在访问 tempDst3DData 缓冲区中的数据时遇到了问题。应该是tempDst3DData[3*pixel3DSize*row + 3*col + channel]。关于您更新的问题,我将编辑我的答案以使其更清晰。
  • 另外,我认为你在 pixel3D_Xpixel3D_Y 表达式中混淆了 rowcol

标签: c++ opencv image-processing kinect openkinect


【解决方案1】:

基本上,您需要更改 3D 坐标系,将深度相机看到的 3D 点转换为 RGB 相机看到的 3D 点。

您不能使用函数reprojectImageTo3D(),因为它需要一个您没有的矩阵 Q。相反,您应该使用链接页面中提供的函数raw_depth_to_meters 将视差图转换为深度图。

然后,对于深度图的每个像素,您需要计算关联的 3D 点,在您链接的页面中用 P3D 表示(请参阅§“使用颜色像素映射深度像素”)。然后,您需要将提供的 3D 旋转矩阵 R 和 3D 平移向量 T(表示从深度相机到 RGB 相机的转换)应用于每个 3D 点P3D,以获得关联的新 3D 点P3D'。最后,使用 RGB 相机的校准矩阵,可以将新的 3D 点投影到 RGB 图像中,并将相关的深度分配给获得的像素,以生成与 RGB 图像对齐的新深度图。

请注意,您在此过程中必然会失去准确性,因为您需要处理遮挡(通过仅保留每个像素看到的最小深度)和图像插值(因为通常情况下,投影的 3D 点不会与RGB 图像中的整数像素坐标)。关于图像插值,我建议您使用最近邻方法,否则您可能会在深度边界处出现奇怪的行为。

根据问题更新进行编辑

这是一个模型,说明您应该如何将 Kinect 深度图重新映射到 RGB cam 视点:

cv::Mat_<float> pt(3,1), R(3,3), t(3,1);
// Initialize R & t here

depthmap_rgbcam = cv::Mat::zeros(height,width,CV_32FC1); // Initialize the depthmap to all zeros
float *depthmap_rgbcam_buffer = (float*)depthmap_rgbcam.data;
for(int row=0; row<height; ++row)
{
    for(int col=0; col<width; ++col)
    {
        // Convert kinect raw disparity to depth
        float raw_disparity = kinect_disparity_map_buffer[width*row+col];
        float depth_depthcam = disparity_to_depth(raw_disparity);

        // Map depthcam depth to 3D point
        pt(0) = depth*(col-cx_depthcam)/fx_depthcam;  // No need for a 3D point buffer
        pt(1) = depth*(row-cy_depthcam)/fy_depthcam;  // here, unless you need one.
        pt(2) = depth;

        // Rotate and translate 3D point
        pt = R*pt+t;

        // If required, apply rgbcam lens distortion to X, Y and Z here.

        // Project 3D point to rgbcam
        float x_rgbcam = fx_rgbcam*pt(0)/pt(2)+cx_rgbcam;
        float y_rgbcam = fy_rgbcam*pt(1)/pt(2)+cy_rgbcam;

        // "Interpolate" pixel coordinates (Nearest Neighbors, as discussed above)
        int px_rgbcam = cvRound(x_rgbcam);
        int py_rgbcam = cvRound(y_rgbcam);

        // Handle 3D occlusions
        float &depth_rgbcam = depthmap_rgbcam_buffer[width*py_rgbcam+px_rgbcam];
        if(depth_rgbcam==0 || depth_depthcam<depth_rgbcam)
            depth_rgbcam = depth_depthcam;
    }
}

这是想法,以可能的拼写错误为模。您还可以根据需要一致地更改数据类型。关于您的评论,我认为目前还没有任何内置的 OpenCV 函数用于此目的。

【讨论】:

  • 抱歉回复晚了。我已经理解了理论上的基本思想。问题是我如何使用 OpenCV 以编程方式做到这一点。提供的 yaml 文件具有投影矩阵。它们不能用于以某种方式加速处理过程,而不是手动操作像素吗?
  • @SubhamoySengupta 如果您需要最佳精度,最好校准您自己的 Kinect 而不是使用其他人的值。
  • @Robin 我发布的代码提取不处理 RGB 图像,只处理深度图。它执行所需的计算,将 IR 相机看到的深度图转换为 RGB 相机看到的深度图,与 RGB 图像很好地对齐。
【解决方案2】:

在 opencv_contrib(rgbd 模块)中添加了一个 RGBD 注册函数,用于注册外部相机的深度: https://github.com/Itseez/opencv_contrib/commit/f5ef071c117817b0e98b2bf509407f0c7a60efd7

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-15
    • 2014-08-17
    • 2016-07-01
    • 1970-01-01
    相关资源
    最近更新 更多