Kinect SDK图像流处理入门

Kinectfor WindowsSDK是一个强大的工具,它允许开发者访问Kinect传感器的高级功能。本文是一系列教程的一部分,旨在为初学者提供指导,并为高级开发者提供参考。在第一部分中,介绍了Kinect SDK的初始化过程,包括图像捕获引擎的参数设置。本文将专注于通过Kinect摄像头获取和处理数据。

Kinect设备概述

Kinect设备配备了两个摄像头:

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

本文将重点介绍如何获取和处理这些摄像头捕获的数据。

什么是ImageStream

ImageStream是KinectSDK提供的一个类,用于访问Kinect摄像头捕获的数据。每个Kinect运行时有两个流:

  • VideoStream - 必须使用ImageStreamType.Video和ImageType.Color、ImageType.ColorYuv或ImageType.ColorYuvRaw打开。
  • DepthStream - 必须使用ImageStreamType.Depth和ImageType.Depth或ImageType.DepthAndPlayerIndex打开。

如第一部分所述,每个流在运行时初始化后都必须调用Open()方法。流所需的第三个参数是ImageResolution,可选值有80x60、320x240、640x480和1280x1024。请注意,DepthStream的最大分辨率为640x480,不同的ImageType支持不同的分辨率。

ImageStream的使用

使用ImageStream非常简单。可以调用GetNextFrame方法,或者订阅运行时暴露的事件:DepthFrameReady或VideoFrameReady。如果使用事件,将通过ImageFrameReadyEventArgs.ImageFrame获取帧数据。

访问图像数据

使用上述任何方法,将获得一个ImageFrame,它在Image字段中持有图像数据本身,以及一些元数据,如:

  • Type - 包含图像类型(ImageType),如果使用相同的处理程序处理两种类型的ImageStream,这将非常有用。
  • FrameNumber
  • Timestamp
  • Resolution

FrameNumber和Timestamp相当准确,如果需要检测丢失的帧、测量帧之间的时间、保持视频和深度之间的同步,或者如果不需要每秒获取新图像超过一次,它们将非常有价值。

PlanarImage

KinectSDK提供了自己的类来保存捕获的图像。它非常简单,只包含Width、Height、BytesPerPixel和原始数据byte[] Bits。

视频帧以32位XRGB或16位UYVY格式保存信息。深度帧根据选择的Depth或DepthAndPlayerIndex流类型有两种不同的格式:

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

值为0的深度数据意味着该位置的物体要么太近要么太远。

PlanarImageHelper类

源代码中包含的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的使用和深度数据的利用。

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

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

如何处理图像

这取决于需求。正如在例子中看到的,选择了“迭代”方法,因为它非常简单易写,也非常清晰易读。

另一方面,它的性能非常差。由于深度帧可以被视为灰度图像,可以使用所有好的图像处理库中容易找到的过滤器(如阈值和掩码)来实现与例子相同的效果。

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

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

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485