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



我有一个C++项目,在那里我使用OpenCV和Libfreenect。我不想包括像OpenNI这样大而重的东西,也不想在这个过程中创建OpenCV安装依赖关系。我想使用这里提供的校准信息来不失真和对齐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

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

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

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

然后,对于depthmap的每个像素,您需要计算相关的3D点,在您链接的页面中用P3D表示(请参见§"用颜色像素映射深度像素")。然后,您需要将提供的3D旋转矩阵R和3D平移向量T应用于每个3D点P3D,以获得关联的新3D点P3D',3D旋转矩阵和3D平移矢量T表示从深度相机到RGB相机的变换。最后,使用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函数。

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

最新更新