基于相机矩阵和失真系数单独地去除图像非常容易.但是现在我对如何使用校正和投影矩阵来对齐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
但我完全不明白我是怎么做到的.也许我完全走错了方向.但我找不到任何这样做的例子.
解决方法
你不能使用函数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功能.