问题 如何将kinect的深度图像与彩色图像对齐


由Kinect上的颜色和深度传感器产生的图像略微不对齐。我怎样才能改变它们以使它们排成一行?


11929
2017-07-27 12:59


起源



答案:


关键是调用'Runtime.NuiCamera.GetColorPixelCoordinatesFromDepthPixel'

这是Runtime类的扩展方法。它返回一个WriteableBitmap对象。随着新帧的进入,这个WriteableBitmap会自动更新。所以它的使用非常简单:

    kinect = new Runtime();
    kinect.Initialize(RuntimeOptions.UseColor | RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseDepthAndPlayerIndex);
    kinect.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex);
    kinect.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);
    myImageControl.Source = kinect.CreateLivePlayerRenderer(); 

这是代码本身:

public static class RuntimeExtensions
{
   public static WriteableBitmap CreateLivePlayerRenderer(this Runtime runtime)
   {
      if (runtime.DepthStream.Width == 0)
         throw new InvalidOperationException("Either open the depth stream before calling this method or use the overload which takes in the resolution that the depth stream will later be opened with.");
      return runtime.CreateLivePlayerRenderer(runtime.DepthStream.Width, runtime.DepthStream.Height);
   }
   public static WriteableBitmap CreateLivePlayerRenderer(this Runtime runtime, int depthWidth, int depthHeight)
   {
      PlanarImage depthImage = new PlanarImage();
      WriteableBitmap target = new WriteableBitmap(depthWidth, depthHeight, 96, 96, PixelFormats.Bgra32, null);
      var depthRect = new System.Windows.Int32Rect(0, 0, depthWidth, depthHeight);

      runtime.DepthFrameReady += (s, e) =>
            {
                depthImage = e.ImageFrame.Image;
                Debug.Assert(depthImage.Height == depthHeight && depthImage.Width == depthWidth);
            };

      runtime.VideoFrameReady += (s, e) =>
            {
                // don't do anything if we don't yet have a depth image
                if (depthImage.Bits == null) return;

                byte[] color = e.ImageFrame.Image.Bits;

                byte[] output = new byte[depthWidth * depthHeight * 4];

                // loop over each pixel in the depth image
                int outputIndex = 0;
                for (int depthY = 0, depthIndex = 0; depthY < depthHeight; depthY++)
                {
                    for (int depthX = 0; depthX < depthWidth; depthX++, depthIndex += 2)
                    {
                        // combine the 2 bytes of depth data representing this pixel
                        short depthValue = (short)(depthImage.Bits[depthIndex] | (depthImage.Bits[depthIndex + 1] << 8));

                        // extract the id of a tracked player from the first bit of depth data for this pixel
                        int player = depthImage.Bits[depthIndex] & 7;

                        // find a pixel in the color image which matches this coordinate from the depth image
                        int colorX, colorY;
                        runtime.NuiCamera.GetColorPixelCoordinatesFromDepthPixel(
                            e.ImageFrame.Resolution,
                            e.ImageFrame.ViewArea,
                            depthX, depthY, // depth coordinate
                            depthValue,  // depth value
                            out colorX, out colorY);  // color coordinate

                        // ensure that the calculated color location is within the bounds of the image
                        colorX = Math.Max(0, Math.Min(colorX, e.ImageFrame.Image.Width - 1));
                        colorY = Math.Max(0, Math.Min(colorY, e.ImageFrame.Image.Height - 1));

                        output[outputIndex++] = color[(4 * (colorX + (colorY * e.ImageFrame.Image.Width))) + 0];
                        output[outputIndex++] = color[(4 * (colorX + (colorY * e.ImageFrame.Image.Width))) + 1];
                        output[outputIndex++] = color[(4 * (colorX + (colorY * e.ImageFrame.Image.Width))) + 2];
                        output[outputIndex++] = player > 0 ? (byte)255 : (byte)0;
                    }
                }
                target.WritePixels(depthRect, output, depthWidth * PixelFormats.Bgra32.BitsPerPixel / 8, 0);
            };
            return target;
        }
    }

8
2017-07-28 00:05



可悲的是,它现在正以我的方式抛出黄色的死亡屏幕。但我正在研究你提到的方法 - Mr Bell
@ Mr-Bell - 我用实际代码更新了这篇文章而不是链接 - Robert Levy
这看起来很有效。看起来好像调用GetColorPixelCoordinatesFromDepthPixel会杀死我的帧速率。 - Mr Bell
是否可以打电话 GetColorPixelCoordinatesFromDepthPixel 对于少量校准角,然后在代码中进行插值或外推?那些错位主要是仿射物吗? - rwong
@rwong,我不知道 - 这是一个很好的问题。如果您在本网站上将其作为单独的问题发布,我会将其投票 - Robert Levy


答案:


关键是调用'Runtime.NuiCamera.GetColorPixelCoordinatesFromDepthPixel'

这是Runtime类的扩展方法。它返回一个WriteableBitmap对象。随着新帧的进入,这个WriteableBitmap会自动更新。所以它的使用非常简单:

    kinect = new Runtime();
    kinect.Initialize(RuntimeOptions.UseColor | RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseDepthAndPlayerIndex);
    kinect.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex);
    kinect.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);
    myImageControl.Source = kinect.CreateLivePlayerRenderer(); 

这是代码本身:

public static class RuntimeExtensions
{
   public static WriteableBitmap CreateLivePlayerRenderer(this Runtime runtime)
   {
      if (runtime.DepthStream.Width == 0)
         throw new InvalidOperationException("Either open the depth stream before calling this method or use the overload which takes in the resolution that the depth stream will later be opened with.");
      return runtime.CreateLivePlayerRenderer(runtime.DepthStream.Width, runtime.DepthStream.Height);
   }
   public static WriteableBitmap CreateLivePlayerRenderer(this Runtime runtime, int depthWidth, int depthHeight)
   {
      PlanarImage depthImage = new PlanarImage();
      WriteableBitmap target = new WriteableBitmap(depthWidth, depthHeight, 96, 96, PixelFormats.Bgra32, null);
      var depthRect = new System.Windows.Int32Rect(0, 0, depthWidth, depthHeight);

      runtime.DepthFrameReady += (s, e) =>
            {
                depthImage = e.ImageFrame.Image;
                Debug.Assert(depthImage.Height == depthHeight && depthImage.Width == depthWidth);
            };

      runtime.VideoFrameReady += (s, e) =>
            {
                // don't do anything if we don't yet have a depth image
                if (depthImage.Bits == null) return;

                byte[] color = e.ImageFrame.Image.Bits;

                byte[] output = new byte[depthWidth * depthHeight * 4];

                // loop over each pixel in the depth image
                int outputIndex = 0;
                for (int depthY = 0, depthIndex = 0; depthY < depthHeight; depthY++)
                {
                    for (int depthX = 0; depthX < depthWidth; depthX++, depthIndex += 2)
                    {
                        // combine the 2 bytes of depth data representing this pixel
                        short depthValue = (short)(depthImage.Bits[depthIndex] | (depthImage.Bits[depthIndex + 1] << 8));

                        // extract the id of a tracked player from the first bit of depth data for this pixel
                        int player = depthImage.Bits[depthIndex] & 7;

                        // find a pixel in the color image which matches this coordinate from the depth image
                        int colorX, colorY;
                        runtime.NuiCamera.GetColorPixelCoordinatesFromDepthPixel(
                            e.ImageFrame.Resolution,
                            e.ImageFrame.ViewArea,
                            depthX, depthY, // depth coordinate
                            depthValue,  // depth value
                            out colorX, out colorY);  // color coordinate

                        // ensure that the calculated color location is within the bounds of the image
                        colorX = Math.Max(0, Math.Min(colorX, e.ImageFrame.Image.Width - 1));
                        colorY = Math.Max(0, Math.Min(colorY, e.ImageFrame.Image.Height - 1));

                        output[outputIndex++] = color[(4 * (colorX + (colorY * e.ImageFrame.Image.Width))) + 0];
                        output[outputIndex++] = color[(4 * (colorX + (colorY * e.ImageFrame.Image.Width))) + 1];
                        output[outputIndex++] = color[(4 * (colorX + (colorY * e.ImageFrame.Image.Width))) + 2];
                        output[outputIndex++] = player > 0 ? (byte)255 : (byte)0;
                    }
                }
                target.WritePixels(depthRect, output, depthWidth * PixelFormats.Bgra32.BitsPerPixel / 8, 0);
            };
            return target;
        }
    }

8
2017-07-28 00:05



可悲的是,它现在正以我的方式抛出黄色的死亡屏幕。但我正在研究你提到的方法 - Mr Bell
@ Mr-Bell - 我用实际代码更新了这篇文章而不是链接 - Robert Levy
这看起来很有效。看起来好像调用GetColorPixelCoordinatesFromDepthPixel会杀死我的帧速率。 - Mr Bell
是否可以打电话 GetColorPixelCoordinatesFromDepthPixel 对于少量校准角,然后在代码中进行插值或外推?那些错位主要是仿射物吗? - rwong
@rwong,我不知道 - 这是一个很好的问题。如果您在本网站上将其作为单独的问题发布,我会将其投票 - Robert Levy


一种方法是假设颜色和深度图像在它们中具有相似的变化,并且使两个图像(或它们的较小版本)交叉相关。

  • 预先白化图像s来了解潜在的变化。
  • 互相关 预先白化的图像或它们的较小版本。
  • 互相关的峰值位置将告诉您偏移量 X 和 ÿ

2
2017-07-28 00:15



彼得,这些都是有趣的文章。但是,我认为这个解决方案可能更具实证性。我认为它可能只是一个偏移或类似的东西 - Mr Bell
:-) 好。我可能过度思考了。 我刚刚读过这种东西...... - Peter K.
在工厂中,每个kinect设备都经过校准,相机之间的偏移会被刻录到设备的内存中。诀窍是找到合适的api来利用这些数据。现在官方的kinect sdk只提供了一个这样的api,但其他人正在考虑将来的版本 - Robert Levy
@Robert:谢谢你的信息!听起来很好玩。 :-) - Peter K.