如何将Kinect的深度数据映射到RGB颜色?

8
我正在使用OpenCV处理给定的数据集,没有任何Kinect设备。我想将给定的深度数据映射到其RGB对应项(以便获取实际颜色和深度)。
由于我正在使用OpenCV和C++,并且没有拥有Kinect,所以遗憾的是我无法使用官方Kinect API的MapDepthFrameToColorFrame方法。
根据给定相机的内参和畸变系数,我可以将深度映射到世界坐标,并根据此处提供的算法将其转换回RGB。
Vec3f depthToW( int x, int y, float depth ){
    Vec3f result;
    result[0] = (float) (x - depthCX) * depth / depthFX;
    result[1] = (float) (y - depthCY) * depth / depthFY;
    result[2] = (float) depth;
    return result;
}

Vec2i wToRGB( const Vec3f & point ) {
    Mat p3d( point );
    p3d = extRotation * p3d + extTranslation;

    float x = p3d.at<float>(0, 0);
    float y = p3d.at<float>(1, 0);
    float z = p3d.at<float>(2, 0);

    Vec2i result;
    result[0] = (int) round( (x * rgbFX / z) + rgbCX );
    result[1] = (int) round( (y * rgbFY / z) + rgbCY );
    return result;
}

void map( Mat& rgb, Mat& depth ) {
    /* intrinsics are focal points and centers of camera */
    undistort( rgb, rgb, rgbIntrinsic, rgbDistortion );
    undistort( depth, depth, depthIntrinsic, depthDistortion );

    Mat color = Mat( depth.size(), CV_8UC3, Scalar(0) );
    ushort * raw_image_ptr;

    for( int y = 0; y < depth.rows; y++ ) {
        raw_image_ptr = depth.ptr<ushort>( y );

        for( int x = 0; x < depth.cols; x++ ) {
            if( raw_image_ptr[x] >= 2047 || raw_image_ptr[x] <= 0 )
                continue;

            float depth_value = depthMeters[ raw_image_ptr[x] ];
            Vec3f depth_coord = depthToW( y, x, depth_value );
            Vec2i rgb_coord   = wToRGB( depth_coord );
            color.at<Vec3b>(y, x) = rgb.at<Vec3b>(rgb_coord[0], rgb_coord[1]);
        }
    }

但是结果似乎不对齐。我无法手动设置翻译,因为数据集来自3个不同的Kinect,并且它们中的每一个都在不同方向上错位。您可以在下面看到其中之一(左:未失真的RGB,中间:未失真的深度,右:映射的RGB到深度)。
我的问题是,在这一点上我应该怎么做?在尝试将深度投影到世界或将世界回传到RGB时,我是否错过了一步?有没有经验丰富的立体相机用户能指出我的错误?

你正在使用OpenNI吗? - P.C.
很遗憾,只有OpenCV。 - sub_o
我建议您使用OpenNI从Kinect获取数据。 OpenNI中有一个内置函数可以为您完成此操作。 - P.C.
1
第三张图片中的数据看起来并没有错位。如果我的假设是正确的,那么它看起来是正确的。白色数据似乎是零数据,或者是深度相机无法识别为在范围内的数据。因此,两者的组合应该消除所有的零数据,因为没有深度数据可以与之相关联,从而创建了你在第三张图片中看到的“空区域”。 - Sean
4个回答

2

啊,我已经使用了其他用户已经进行过校准的参数(相机内参、畸变和外参参数)。我的主要问题似乎是对齐,可能是我错过了某个步骤。 - sub_o

0

是的,您没有考虑RGB和深度之间的转换。 但是,您可以使用cvStereoCalibrate()方法计算此矩阵,该方法只需将RGB和深度的图像序列与棋盘角落传递给它即可。 您可以在OpenCV文档中找到详细信息: http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#double stereoCalibrate(InputArrayOfArrays objectPoints,InputArrayOfArrays imagePoints1,InputArrayOfArrays imagePoints2,InputOutputArray cameraMatrix1,InputOutputArray distCoeffs1,InputOutputArray cameraMatrix2,InputOutputArray distCoeffs2,Size imageSize,OutputArray R,OutputArray T,OutputArray E,OutputArray F,TermCriteria criteria,int flags)

而这个方法背后的整个思路是:

颜色UV <- 归一化颜色 <- 颜色空间 <- DtoC转换 <- 深度空间 <- 归一化深度 <- 深度UV (uc,vc) <- <- ExtrCol * (pc) <- 立体标定MAT <- ExtrDep^-1 * (pd) <- <(ud - cx)*d / fx, (vd-cy)*d/fy, d> <- (ud, vd)

如果您想对RGB添加畸变,只需要按照以下步骤进行: http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html


0

OpenCV没有用于将深度流对齐到彩色视频流的功能。但我知道在“Kinect for Windows SDK”中有一个名为MapDepthFrameToColorFrame特殊函数

我没有示例代码,但希望这是一个好的起点。

更新: 这里有一个使用KinectSDK将彩色图像映射到深度的相同示例,并提供了与OpenCV的接口(不是我的代码)。


是的,根据原帖,我无法使用MapDepthFrameToColorFrame(数据集是pgm和ppm图像,而且我没有kinect可供使用)。不幸的是,我真正需要的是NuiImageGetColorPixelCoordinatesFromDepthPixel中的内部算法,但我找不到任何地方(它似乎不是开源的)。 - sub_o

0

看起来你的解决方案没有考虑两个相机之间的外部因素。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接