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

问题描述 投票:15回答:2

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

这是一个例子:

这是创建图像的代码片段(示例中的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);
                }
            }

        }
    }
}

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

提前致谢

更新:

如果我做一个简单的深度图像阈值处理,我会得到以下图像:

这或多或少是我预期会发生的事情,并且在后台没有重复的手。有没有办法在后台防止这个重复的手?

c++ opencv kinect kinect-sdk
2个回答
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源。


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的第一个发布版本(不是预发行版)......所以,可能发生了很多变化...如果有人知道这是否解决只是留下评论:)

© www.soinside.com 2019 - 2024. All rights reserved.