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





5.00/5 (4投票s)
本系列文章旨在探索 Windows Kinect SDK 的使用。
引言
这是本系列文章的第二部分,记录了我使用 Windows Kinect SDK 进行实验的经历。在前两三篇文章之后,本系列应该能为初学者提供一个非常好的入门指南,也为更高级的开发者提供一个很好的参考。
第一部分 描述了 Kinect SDK 的初始化,包括图像捕获引擎的参数。有关初始化和一般背景的详细信息,请参考第一部分。
系列目录
- 初始化
- ImageStreams
- 即将推出……
背景
Kinect 设备有两个摄像头
- 视频摄像头 - 用于捕获图像的 RGB 摄像头
- 深度摄像头 - 用于捕获深度数据的红外摄像头
本文将重点介绍如何获取和处理通过摄像头获得的数据。
什么是 ImageStreams?
ImageStream
是 Kinect SDK 提供的一个类,用于访问 Kinect 摄像头捕获的数据。每个 Kinect Runtime
都有两个流:
VideoStream
- 必须使用ImageStreamType.Video
和ImageType.Color
、ImageType.ColorYuv
或ImageType.ColorYuvRaw
打开。DepthStream
- 必须使用ImageStreamType.Depth
和ImageType.Depth
或ImageType.DepthAndPlayerIndex
打开。
如第一部分所述,每个流都必须在 Runtime
初始化后使用 Open()
打开。流所需的第三个参数是 ImageResolution
- 80x60、320x240、640x480 和 1280x1024。请注意,DepthStream
的最大分辨率为 640x480,并且不同的 ImageType
值支持不同的分辨率。
ImageStream
的使用非常简单。您可以调用 GetNextFrame
方法,或者附加到运行时公开的事件:DepthFrameReady
或 VideoFrameReady
。
在使用事件的情况下,您将通过 ImageFrameReadyEventArgs.ImageFrame
获取帧数据。
访问图像数据
使用上述任何一种方法,您将获得一个 ImageFrame
,其中包含图像数据本身(存储在 Image
字段中)以及一些元数据,例如:
Type
- 包含图像类型(ImageType
)- 如果您为两种类型的ImageStream
使用相同的处理程序,则此字段非常有用。FrameNumber
时间戳
分辨率
FrameNumber
和 Timestamp
似乎非常准确,如果需要检测丢失的帧、测量帧之间的延迟、保持视频和深度数据的同步,或者在不需要每秒更新图像的情况下,它们都非常有价值。
PlanarImage
Kinect SDK 提供了自己的类来存储捕获的图像。它尽可能简单 - 它包含 Width
、Height
、BytesPerPixel
,以及存储在 byte[] Bits
中的原始数据。
视频帧以 32 位 XRGB 或 16 位 UYVY 格式保存信息。
深度帧根据选择 Depth
或 DepthAndPlayerIndex
流类型而具有两种不同的格式:
- 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:初始提交。