【问题标题】:Kinect for Windows v2 depth to color image misalignmentKinect for Windows v2 深度到彩色图像未对齐
【发布时间】:2014-11-05 11:13:07
【问题描述】:

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

这是一个例子:

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

        }
    }
}

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

提前致谢

更新:

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

这或多或少是我预料到的,而且背景中没有重复的手。有没有办法防止在后台出现这种重复的手?

【问题讨论】:

  • 这个映射是从哪里来的?很可能您必须编辑深度图像和彩色图像之间的校准,因为预定义并不完美。因此,您必须执行自己的校准。看看:nicolas.burrus.name/index.php/Research/KinectCalibration
  • 来自kinect SDK v2。我期待使用来自固件/SDK的那个,它使用相机的内部结构来做这个计算……但我认为与其他相机固件/软件相比,错误是巨大的,比如带有openni的Primesense。我希望得到更好的结果,或者至少与其他相机相似....感谢您的链接:)
  • Afaik 固件中保存的 kinect 的自动校准数据并没有那么大。但也许我错了。
  • 您好,我也在使用 Kinect for Windows V2。您是否能够执行 kinect 的立体校准?我正在使用 MATLAB 来做到这一点,但由于相机分辨率不相等,我有点不知道该怎么做。
  • @user2441667 我不知道它在 MATLAB 中会怎样,但在 c++ 中,SDK 的执行方式是我在帖子中使用的代码 sn-p ... 与 m_pCoordinateMapper-&gt;MapDepthFrameToColorSpace功能。另一种方法是手动进行...重要的是要注意,彩色相机没有“看到”的像素会给你,上面的代码,另一个地方的重复像素(就像我向你展示的那样在图片中用重复的手)

标签: c++ opencv kinect kinect-sdk


【解决方案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 源。

【讨论】:

  • 我认为手的示例图像有点误导。我们正在尝试实现一种工具来以不同格式记录数据集。我们必须能够用这个数据集做的一件事是创建一个彩色点云,但是背景中的点颜色错误(看起来像一只复制的手)。有没有办法只删除这个“无效”(我们认为它们无效,因为不应该映射到颜色,可能是因为这些像素在彩色图像中不可见)点。
  • 哦,我明白了。在您的示例中,您使用深度帧作为基础创建 RGB 图像。因此,您将无法将其投影到 1920x1080 图像之上。在我提供给您的代码中,生成了一个 1920x1080 大小的 RGBA 位图。因此,您可以将其放置在另一个 1920x1080 位图的顶部。你试过了吗?
  • 我试过了,得到了没有重复的东西,但我认为很多无效像素都消失了,让我想知道这个映射有多好?如果您在示例中看到门上方的窗户,则它在深度图像中有很多无效像素。当我按照您的建议进行映射时,它们中的大多数实际上都消失了....所以我想知道,它们会发生什么?也许框架中有一些插值问题?
  • 不确定,除非我们有最终版本。所有这些都可能发生变化。
【解决方案2】:

终于有时间写下期待已久的答案。

让我们从一些理论开始,了解真正发生的事情,然后给出可能的答案。

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

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

基本上,Kinect SDK 就是这样做的。那么,有什么问题会导致手被重复呢?好吧,实际上不止一个点投射到同一个像素上......

换句话说,就是在问题的上下文中。

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

现在,最大的问题是,如何避免这种情况....好吧,我不确定是否使用 Kinect SDK,因为在应用外在因素之后您不知道点的 Z 值,所以不是可以使用Z buffering.. 之类的技术。但是,您可能会假设 Z 值非常相似并使用原始点云中的值(风险自负)。

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

我希望它足够清楚。

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

【讨论】:

    猜你喜欢
    • 2011-10-14
    • 1970-01-01
    • 2015-04-04
    • 2018-04-29
    • 2013-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多