2014-02-18 200 views
7

我有一个C++项目,我正在使用OpenCV和Libfreenect。我不想包含像OpenNI那样大而沉重的东西,并在此过程中创建OpenCV安装依赖关系。我想使用提供的校准信息here来保持不偏振,并对齐RGB和深度图像。如何在OpenCV中对齐Kinect的RGB和深度图像?

基于相机矩阵和失真系数单独对图像进行单独失真很简单。但是现在我对如何使用校正和投影矩阵来对齐RGB和深度图像感到困惑,所以它们从相同的角度基本上向我展示了相同的东西。在搜索了很长一段时间之后,我无法确定它应该如何与OpenCV一起工作。这是一个模糊的估计,reprojectImageTo3D()warpPerspective()可能会被使用,但我不知道如何。

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

UPDATE

下面是部分代码,到目前为止,我已经写了:

// 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 

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

+0

可以使用openni代替openkinect/libfreenect? –

+0

我想你在访问'tempDst3DData'缓冲区中的数据时遇到了问题。它应该是'tempDst3DData [3 * pixel3DSize * row + 3 * col + channel]'。关于您更新的问题,我会编辑我的答案以尝试更清楚。 – AldurDisciple

+0

另外,我想你在'pixel3D_X'和'pixel3D_Y'表达式中混合了'row'和'col'。 – AldurDisciple

回答

8

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

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

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

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

编辑以下问题更新

下面是你应该以重新映射Kinect的深度图在视RGB凸轮点做一个模型:

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功能。

+0

对不起,迟到的回应。我从理论上理解了这个基本概念。问题是我如何用OpenCV编程实现这一点。提供的yaml文件具有投影矩阵。它们不能用于以某种方式加速该过程而不是用手操作像素吗? –

+0

@SubhamoySengupta如果你需要最好的准确性,那么校准你自己的Kinect而不是使用别人的值可能是一个好主意。 – AldurDisciple

+0

@Robin我发布的代码提取不会处理RGB图像,只能使用深度图。它执行将IR摄像机查看的深度图转换为由RGB摄像机查看的深度图所需的计算,该摄像机与RGB图像很好地对齐。 – AldurDisciple

1

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

+0

我回复了你对我发布的评论,因为你的问题也应该发布在那里。 – AldurDisciple

相关问题