如何在OpenCV中对齐Kinect的RGB和深度图像?

时间:2014-02-18 09:36:25

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

我有一个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

但我完全不明白我该怎么做。也许我完全走错了方向。但我找不到任何这方面的例子。

3 个答案:

答案 0 :(得分:8)

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

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

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

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

在问题更新后进行修改

以下是为了将Kinect深度图重新映射到RGB cam的观点,您应该做些什么的模型:

P3D'

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

答案 1 :(得分:1)

@AldurDisciple,根据我的理解,相机的RGB图像存储在depthmap_rgbcam及其RGB值中,但我无法看到从相机拍摄图像的位置和时间传递给变量?对我来说,它只是在depthmap_rgbcam初始化后只是一个空矩阵。

答案 2 :(得分:1)

在opencv_contrib(rgbd模块)中添加了一个RGBD注册功能,可以将深度注册到外部摄像头: https://github.com/Itseez/opencv_contrib/commit/f5ef071c117817b0e98b2bf509407f0c7a60efd7