void Reader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
{
var reference = e.FrameReference.AcquireFrame();
using (var colorFrame = reference.ColorFrameReference.AcquireFrame())
using (var depthFrame = reference.DepthFrameReference.AcquireFrame())
using (var bodyIndexFrame = reference.BodyIndexFrameReference.AcquireFrame())
{
if (colorFrame != null && depthFrame != null && bodyIndexFrame != null)
{
// 3) Update the image source.
camera.Source = _backgroundRemovalTool.GreenScreen(colorFrame, depthFrame, bodyIndexFrame);
}
}
}
MainWindow中主要就是backgroundRemovalTool这个类在起到去除背景的作用。
代码中主要有以下几个类:
WriteableBitmap _bitmap: 带有裁剪背景的最终图像
ushort[] _depthData: 深度帧的深度值
byte[] _bodyData: 有关站在传感器前面的身体的信息
byte[] _colorData: 颜色框的RGB值
byte[] _displayPixels: 映射帧的RGB值
ColorSpacePoint[] _colorPoints: 我们需要映射的color point
还使用图像源(WriteableBitmap)来创建最终的位图图像。 CoordinateMapper作为连接的Kinect传感器的参数传递。
先看GrennScreen函数。
首先,我们需要获得每个框架的尺寸(框架具有不同的宽度和高度):
int colorWidth = colorFrame.FrameDescription.Width;
int colorHeight = colorFrame.FrameDescription.Height;
int depthWidth = depthFrame.FrameDescription.Width;
int depthHeight = depthFrame.FrameDescription.Height;
int bodyIndexWidth = bodyIndexFrame.FrameDescription.Width;
int 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[depthWidth * depthHeight * BYTES_PER_PIXEL];
_colorPoints = new ColorSpacePoint[depthWidth * depthHeight];
_bitmap = new WriteableBitmap(depthWidth, depthHeight, DPI, DPI, FORMAT, null);
我们现在需要用新的帧数据填充数组。 在此之前,我们检查数组长度是否与我们之前找到的维度相对应:
if (((depthWidth * depthHeight) == _depthData.Length) && ((colorWidth * colorHeight * BYTES_PER_PIXEL) == _colorData.Length) && ((bodyIndexWidth * bodyIndexHeight) == _bodyData.Length))
{
depthFrame.CopyFrameDataToArray(_depthData);
if (colorFrame.RawColorImageFormat == ColorImageFormat.Bgra)
{
colorFrame.CopyRawFrameDataToArray(_colorData);
}
else
{
colorFrame.CopyConvertedFrameDataToArray(_colorData, ColorImageFormat.Bgra);
}
现在使用坐标映射器。 坐标映射器将深度值映射到_colorPoints数组:
_coordinateMapper.MapDepthFrameToColorSpace(_depthData, _colorPoints);
映射已完成。 我们要做的是指定哪些像素属于人体并将它们添加到_displayPixels数组。 因此,我们循环遍历深度值并相应地更新_displayPixels数组。
for (int y = 0; y < depthHeight; ++y)
{
for (int x = 0; x < depthWidth; ++x)
{
int depthIndex = (y * depthWidth) + x;
byte player = _bodyData[depthIndex];
if (player != 0xff)
{
ColorSpacePoint colorPoint = _colorPoints[depthIndex];
int colorX = (int)Math.Floor(colorPoint.X + 0.5);
int colorY = (int)Math.Floor(colorPoint.Y + 0.5);
if ((colorX >= 0) && (colorX < colorWidth) && (colorY >= 0) && (colorY < colorHeight))
{
int colorIndex = ((colorY * colorWidth) + colorX) * BYTES_PER_PIXEL;
int displayIndex = depthIndex * BYTES_PER_PIXEL;
_displayPixels[displayIndex + 0] = _colorData[colorIndex];
_displayPixels[displayIndex + 1] = _colorData[colorIndex + 1];
_displayPixels[displayIndex + 2] = _colorData[colorIndex + 2];
_displayPixels[displayIndex + 3] = 0xff;
}
}
}
}
这将产生具有透明像素的位图,用于背景和人体的彩色像素。 最后,这是WriteableBitmap的更新方式:
_bitmap.Lock();
Marshal.Copy(_displayPixels, 0, _bitmap.BackBuffer, _displayPixels.Length);
_bitmap.AddDirtyRect(new Int32Rect(0, 0, depthWidth, depthHeight));
_bitmap.Unlock();

该博客详细介绍了如何利用Kinect传感器进行背景去除,以实现绿幕效果。通过处理深度帧、身体信息和颜色帧数据,使用CoordinateMapper映射深度值到颜色空间,并更新WriteableBitmap来创建带有透明背景的图像。主要涉及的关键步骤包括初始化数组、填充帧数据、映射深度值以及更新位图缓冲区。

1万+

被折叠的 条评论
为什么被折叠?



