Kinect for Windows v2深度到彩色图像不对齐

时间:2014-09-11 13:18:35

标签: c++ opencv kinect kinect-sdk

目前我正在开发适用于Windows v2的Kinect工具(类似于XBOX ONE中的工具)。我尝试了一些示例,并有一个工作示例,显示相机图像,深度图像,以及使用opencv将深度映射到rgb的图像。但我发现在进行映射时它会复制我的手,我认为这是由于坐标映射器部分出错了。

这是一个例子: error

这是创建图像的代码片段(示例中的rgbd图像)

void KinectViewer::create_rgbd(cv::Mat& depth_im, cv::Mat& rgb_im, cv::Mat& rgbd_im){
    HRESULT hr = m_pCoordinateMapper->MapDepthFrameToColorSpace(cDepthWidth * cDepthHeight, (UINT16*)depth_im.data, cDepthWidth * cDepthHeight, m_pColorCoordinates);
    rgbd_im = cv::Mat::zeros(depth_im.rows, depth_im.cols, CV_8UC3);
    double minVal, maxVal;
    cv::minMaxLoc(depth_im, &minVal, &maxVal);
    for (int i=0; i < cDepthHeight; i++){
        for (int j=0; j < cDepthWidth; j++){
            if (depth_im.at<UINT16>(i, j) > 0 && depth_im.at<UINT16>(i, j) < maxVal * (max_z / 100) && depth_im.at<UINT16>(i, j) > maxVal * min_z /100){
                double a = i * cDepthWidth + j;
                ColorSpacePoint colorPoint = m_pColorCoordinates[i*cDepthWidth+j];
                int colorX = (int)(floor(colorPoint.X + 0.5));
                int colorY = (int)(floor(colorPoint.Y + 0.5));
                if ((colorX >= 0) && (colorX < cColorWidth) && (colorY >= 0) && (colorY < cColorHeight))
                {
                    rgbd_im.at<cv::Vec3b>(i, j) = rgb_im.at<cv::Vec3b>(colorY, colorX);
                }
            }

        }
    }
}

有没有人知道如何解决这个问题?如何防止这种重复?

提前致谢

更新

如果我进行简单的深度图像阈值处理,我会得到以下图像: thresholding

这或多或少是我预期发生的事情,而且背景中没有重复的手。有没有办法在后台阻止这种重复的手?

2 个答案:

答案 0 :(得分:1)

我建议您使用BodyIndexFrame来识别特定值是否属于播放器。这样,您可以拒绝任何不属于播放器的RGB像素,并保留其余的像素。我不认为CoordinateMapper在说谎。

一些注意事项:

  • 将BodyIndexFrame源包含在帧阅读器中
  • 使用MapColorFrameToDepthSpace代替MapDepthFrameToColorSpace;通过这种方式,您将获得前景的高清图像
  • 找到相应的DepthSpacePoint和depthX,depthY,而不是ColorSpacePoint和colorX,colorY

这是框架到达时的方法(它在C#中):

depthFrame.CopyFrameDataToArray(_depthData);
colorFrame.CopyConvertedFrameDataToArray(_colorData, ColorImageFormat.Bgra);
bodyIndexFrame.CopyFrameDataToArray(_bodyData);

_coordinateMapper.MapColorFrameToDepthSpace(_depthData, _depthPoints);

Array.Clear(_displayPixels, 0, _displayPixels.Length);

for (int colorIndex = 0; colorIndex < _depthPoints.Length; ++colorIndex)
{
    DepthSpacePoint depthPoint = _depthPoints[colorIndex];

    if (!float.IsNegativeInfinity(depthPoint.X) && !float.IsNegativeInfinity(depthPoint.Y))
    {
        int depthX = (int)(depthPoint.X + 0.5f);
        int depthY = (int)(depthPoint.Y + 0.5f);

        if ((depthX >= 0) && (depthX < _depthWidth) && (depthY >= 0) && (depthY < _depthHeight))
        {
            int depthIndex = (depthY * _depthWidth) + depthX;
            byte player = _bodyData[depthIndex];

            // Identify whether the point belongs to a player
            if (player != 0xff)
            {
                int sourceIndex = colorIndex * BYTES_PER_PIXEL;

                _displayPixels[sourceIndex] = _colorData[sourceIndex++];    // B
                _displayPixels[sourceIndex] = _colorData[sourceIndex++];    // G
                _displayPixels[sourceIndex] = _colorData[sourceIndex++];    // R
                _displayPixels[sourceIndex] = 0xff;                         // A
            }
        }
    }
}

这是数组的初始化:

BYTES_PER_PIXEL = (PixelFormats.Bgr32.BitsPerPixel + 7) / 8;

_colorWidth = colorFrame.FrameDescription.Width;
_colorHeight = colorFrame.FrameDescription.Height;
_depthWidth = depthFrame.FrameDescription.Width;
_depthHeight = depthFrame.FrameDescription.Height;
_bodyIndexWidth = bodyIndexFrame.FrameDescription.Width;
_bodyIndexHeight = bodyIndexFrame.FrameDescription.Height;
_depthData = new ushort[_depthWidth * _depthHeight];
_bodyData = new byte[_depthWidth * _depthHeight];
_colorData = new byte[_colorWidth * _colorHeight * BYTES_PER_PIXEL];
_displayPixels = new byte[_colorWidth * _colorHeight * BYTES_PER_PIXEL];
_depthPoints = new DepthSpacePoint[_colorWidth * _colorHeight];

请注意,_depthPoints数组的大小为1920x1080。

再一次,最重要的是使用BodyIndexFrame源。

答案 1 :(得分:0)

最后,我有时间写下期待已久的答案。

让我们从一些理论开始,了解真实情况,然后是可能的答案。

我们应该首先了解从具有深度相机作为坐标系原点的3D点云到RGB相机图像平面中的图像的方式。要做到这一点,使用相机针孔模型就足够了:

enter image description here

在此处,uv是RGB相机图像平面中的坐标。等式右边的第一个矩阵是相机矩阵,RGB相机的AKA内在函数。下面的矩阵是外部的旋转和平移,或者更好地说,需要从深度相机坐标系到RGB相机坐标系的转换。最后一部分是3D点。

基本上,类似这样的东西就是Kinect SDK的功能。那么,什么可能会导致手被重复?好吧,实际上不止一个点投射到同一个像素....

换句话说,就问题中的问题而言。

深度图像是有序点云的表示,我查询每个像素的u v值,实际上可以很容易地转换为3D点。 SDK为您提供投影,但它可以指向相同的像素(通常,两个相邻点之间的z轴距离越多,就越容易出现这个问题。

现在,最重要的问题是,你怎么能避免这种情况....好吧,我不确定使用Kinect SDK,因为你不知道应用extrinsics之后的点的Z值,所以它不是可以使用类似Z buffering的技术....但是,您可以假设Z值非常相似并使用原始pointcloud中的值(风险自负)。

如果您是手动操作而不是使用SDK,则可以将Extrinsics应用于点,并使用将它们投影到图像平面中,在另一个矩阵中标记哪个点映射到哪个像素,如果有是已映射的一个现有点,检查z值并进行比较,并始终将最近的点留给相机。然后,您将获得有效的映射,没有任何问题。这种方式是一种天真的方式,可能你可以得到更好的方式,因为问题现在很明显:)

我希望它足够清楚。

P.S .: 我目前没有Kinect 2,因此我可以尝试查看是否存在与此问题相关的更新,或者是否仍然发生相同的事情。我使用了SDK的第一个发布版本(不是预发行版)......所以,可能发生了很多变化......如果有人知道这是否已解决,请发表评论:)