65.9K
CodeProject 正在变化。 阅读更多。
Home

充分利用 C# 中的 Kinect SDK - 第 2 部分:ImageStreams

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2012年1月6日

Ms-PL

4分钟阅读

viewsIcon

37649

downloadIcon

1243

本系列文章旨在探索 Windows Kinect SDK 的使用。

引言

这是本系列文章的第二部分,记录了我使用 Windows Kinect SDK 进行实验的经历。在前两三篇文章之后,本系列应该能为初学者提供一个非常好的入门指南,也为更高级的开发者提供一个很好的参考。

第一部分 描述了 Kinect SDK 的初始化,包括图像捕获引擎的参数。有关初始化和一般背景的详细信息,请参考第一部分。

系列目录

  1. 初始化
  2. ImageStreams
  3. 即将推出……

背景

Kinect 设备有两个摄像头

  • 视频摄像头 - 用于捕获图像的 RGB 摄像头
  • 深度摄像头 - 用于捕获深度数据的红外摄像头

本文将重点介绍如何获取和处理通过摄像头获得的数据。

什么是 ImageStreams?

ImageStream 是 Kinect SDK 提供的一个类,用于访问 Kinect 摄像头捕获的数据。每个 Kinect Runtime 都有两个流:

  • VideoStream - 必须使用 ImageStreamType.VideoImageType.ColorImageType.ColorYuvImageType.ColorYuvRaw 打开。
  • DepthStream - 必须使用 ImageStreamType.DepthImageType.DepthImageType.DepthAndPlayerIndex 打开。

如第一部分所述,每个流都必须在 Runtime 初始化后使用 Open() 打开。流所需的第三个参数是 ImageResolution - 80x60、320x240、640x480 和 1280x1024。请注意,DepthStream 的最大分辨率为 640x480,并且不同的 ImageType 值支持不同的分辨率。

ImageStream 的使用非常简单。您可以调用 GetNextFrame 方法,或者附加到运行时公开的事件:DepthFrameReadyVideoFrameReady

在使用事件的情况下,您将通过 ImageFrameReadyEventArgs.ImageFrame 获取帧数据。

访问图像数据

使用上述任何一种方法,您将获得一个 ImageFrame,其中包含图像数据本身(存储在 Image 字段中)以及一些元数据,例如:

  • Type - 包含图像类型(ImageType)- 如果您为两种类型的 ImageStream 使用相同的处理程序,则此字段非常有用。
  • FrameNumber
  • 时间戳
  • 分辨率

FrameNumberTimestamp 似乎非常准确,如果需要检测丢失的帧、测量帧之间的延迟、保持视频和深度数据的同步,或者在不需要每秒更新图像的情况下,它们都非常有价值。

PlanarImage

Kinect SDK 提供了自己的类来存储捕获的图像。它尽可能简单 - 它包含 WidthHeightBytesPerPixel,以及存储在 byte[] Bits 中的原始数据。

视频帧以 32 位 XRGB 或 16 位 UYVY 格式保存信息。

深度帧根据选择 DepthDepthAndPlayerIndex 流类型而具有两种不同的格式:

  • 12 位深度数据(存储在两个字节中,高 4 位未使用)
  • 3 位玩家索引(位 0-2)和 12 位深度数据(从位 3 开始)

值为 0 的深度数据表示该位置的对象要么太近要么太远。

源代码中包含的 PlanarImageHelper 类简化了对单个像素的访问。

public class PlanarImageHelper
{
    private PlanarImage _img;
    
    public PlanarImage Image { get { return _img; } }

    public PlanarImageHelper(PlanarImage src)
    {
        _img = src;
    }

    public Byte GetRedAt(int x, int y)
    {
        return _img.Bits[y * _img.Width * _img.BytesPerPixel + x * _img.BytesPerPixel + 2];
    }

    public Byte GetGreenAt(int x, int y)
    {
        return _img.Bits[y * _img.Width * _img.BytesPerPixel + x * _img.BytesPerPixel + 1];
    }

    public Byte GetBlueAt(int x, int y)
    {
        return _img.Bits[y * _img.Width * _img.BytesPerPixel + x * _img.BytesPerPixel + 0];
    }

    public int GetPlayerAt(int x, int y)
    {
        return _img.Bits[y * _img.Width * _img.BytesPerPixel + x * _img.BytesPerPixel] & 0x07;
    }

    public int GetDepthAt(int x, int y, bool hasPlayerData)
    {
        try
        {
            int BaseByte = y * _img.Width * _img.BytesPerPixel + x * _img.BytesPerPixel;
            if (hasPlayerData)
            {
                return (_img.Bits[BaseByte + 1] << 5) | (_img.Bits[BaseByte] >> 3);
            }
            else
            {
                return (_img.Bits[BaseByte + 1] << 8) | (_img.Bits[BaseByte]);
            }
        }
        catch
        {
            return 0;
        }
    }
}

ImageStreamTest

在随附的源代码中,您会找到 ImageStreamTest 应用程序。它简单地演示了 ImageStreams 的用法和深度数据的利用。

在窗口的左侧,您可以选择基于深度数据应用于图像的效果。

  • 无 - 仅显示捕获的视频帧
  • 深度 - 视频帧中的每个像素都与同一点的深度数据进行比较,如果不在滑块设置的范围内,则替换为白色。
  • 玩家 - 所有不包含玩家索引的像素都替换为白色。
  • 背景 - 一个不太成功的尝试,仅显示背景 - 没有玩家索引的像素被复制到背景图像,有玩家索引的像素被替换为记住的背景。

如何处理图像

这取决于您的需求。正如您所见,在我的示例中,我选择了“迭代”方法,因为它非常简单易写且清晰易读。另一方面,它的性能非常差。

由于深度帧可以被视为灰度图像,您可以使用所有好的图像处理库中易于找到的滤镜——阈值和掩码——来实现与我示例中相同的效果。

首先,您需要决定您真正需要什么。如果您正在构建增强现实应用程序,那么您将需要高质量的视频和快速的图像混合。如果您只是偶尔分析图像的一部分(例如人脸识别),那么您仍然需要高分辨率图像,但不需要高帧率,这意味着您可以跳过在事件处理程序中处理每一帧,并按需获取帧。

正如您从前面的部分所看到的,Kinect SDK 以非常原始的格式提供图像。这意味着它可以轻松地转换为您需要的任何格式。大多数图形库都能够接收这个原始字节数组,并以最高效的方式创建内部图像表示。

关注点

如果您的需求主要是带有深度图辅助的图像处理,那么您应该在这里停止,并寻找一些图像处理库。

但如果您真的想充分利用 Kinect NUI,那么请继续关注下一个重要内容——骨骼跟踪引擎。

历史

  • 2012-01-06:初始提交。
© . All rights reserved.