Kinect 开发 —— 进阶指引 (下)
运动识别
利用运动识别(motion detection)来进行近景识别是最有意思的一种方式。实现运动识别的基本原理是设置一个起始的基准RGB图像,然后将从摄像头获取的每一帧影像和这个基准图像进行比较。如果发现了差异,我们可以认为有东西进入到了摄像头的视野范围。
不难看出这种策略是有缺陷的。在现实生活中,物体是运动的。在一个房间里,某个人可能会轻微移动家具。在户外,一辆汽车可能会启动,风可能会将一些小树吹的摇摇晃晃。在这些场景中,尽然没有连续的移动动作,但是物体的状态还是发生了变化,依据之前的策略,系统会判断错误。因此,在这些情况下,我们需要间歇性的更改基准图像才能解决这一问题。
EmguCV项目的官方网站为http://www.emgu.com/wiki/index.php/Main_Page 实际的源代码和安装包放在SourceForge(http://sourceforge.net/projects/emgucv/files/ )上。本文使用的Emgu版本为2.3.0。Emgu的安装过程很简单直观,只需要点击下载好的可执行文件即可。不过有一点需要注意的是EmguCV似乎在x86架构的计算机上运行的最好。如果在64位的机器上开发,最好为Emgu库的目标平台指定为x86,如下图所示(你也可以在官网上下载源码然后自己在x64平台上编译)。
要使用Emgu库,需要添加对下面三个dll的引用:Emgu.CV、Emgu.CV.UI以及Emgu.Util。
因为Emgu是对C++类库的一个.Net包装,所以需要在dll所在的目录放一些额外的非托管的dll,使得Emgu能够找到这些dll进行处理。Emgu在应用程序的执行目录查找这些dll。如果在debug模式下面,则在bin/Debug目录下面查找。在release模式下,则在bin/Release目录下面。共有11个非托管的C++ dll需要放置在相应目录下面,他们是opencv_calib3d231.dll, opencv_conrib231.dll, opencv_core231.dll,opencv_features2d231.dll, opencv_ffmpeg.dll, opencv_highgui231.dll, opencv_imgproc231.dll,opencv_legacy231.dll, opencv_ml231.dll, opencv_objectdetect231.dll, and opencv_video231.dll。这些dll可以在Emgu的安装目录下面找到。为了方便,可以拷贝所有以opencv_开头的dll。
在我们的扩展方法库中,我们需要一些额外的扩展帮助方法。上一篇文章讨论过,每一种类库都有其自己能够理解的核心图像类型。在Emgu中,这个核心的图像类型是泛型的Image<TColor,TDepth>类型,它实现了Emgu.CV.IImage接口。下面的代码展现了一些我们熟悉的影像数据格式和Emgu特定的影像格式之间转换的扩展方法。新建一个名为EmguExtensions.cs的静态类,并将其命名空间改为ImageManipulationMethods,和我们之前ImageExtensions类的命名空间相同。我们可以将所有的的扩展方法放到同一个命名空间中。这个类负责三种不同影像数据类型之间的转换:从Microsoft.Kinect.ColorFrameImage到Emgu.CV.Image<TColor,TDepth>,从System.Drawing.Bitmap到Emgu.CV.Image<TColor,TDepth>以及Emgu.CV.Image<TColor,TDepth>到System.Windows.Media.Imaging.BitmapSource之间的转换。
使用Emgu类库来实现运动识别,我们将用到在之前文章中讲到的“拉数据”(polling)模型而不是基于事件的机制来获取数据。这是因为图像处理非常消耗系统计算和内存资源,我们希望能够调节处理的频率,而这只能通过“拉数据”这种模式来实现。需要指出的是本例子只是演示如何进行运动识别,所以注重的是代码的可读性,而不是性能,大家看了理解了之后可以对其进行改进。
因为彩色影像数据流用来更新Image控件数据源,我们使用深度影像数据流来进行运动识别。需要指出的是,我们所有用于运动追踪的数据都是通过深度影像数据流提供的。如前面文章讨论,CompositionTarget.Rendering事件通常是用来进行从彩色影像数据流中“拉”数据。但是对于深度影像数据流,我们将会创建一个BackgroundWorker对象来对深度影像数据流进行处理。BackgroundWorker对象将会调用Pulse方法来“拉”取深度影像数据,并执行一些消耗计算资源的处理。当BackgroundWorker完成了一个循环,接着从深度影像数据流中“拉”取下一幅影像继续处理。代码中声明了两个名为MotionHistory和IBGFGDetector的Emgu成员变量。这两个变量一起使用,通过相互比较来不断更新基准影像来探测运动。
KinectSensor _kinectSensor;
private MotionHistory _motionHistory;
private IBGFGDetector<Bgr> _forgroundDetector;
bool _isTracking = false; public MainWindow()
{
InitializeComponent(); this.Unloaded += delegate
{
_kinectSensor.ColorStream.Disable();
}; this.Loaded += delegate
{ _motionHistory = new MotionHistory(
1.0, //in seconds, the duration of motion history you wants to keep
0.05, //in seconds, parameter for cvCalcMotionGradient
0.5); //in seconds, parameter for cvCalcMotionGradient _kinectSensor = KinectSensor.KinectSensors[]; _kinectSensor.ColorStream.Enable();
_kinectSensor.Start(); BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (a, b) => Pulse();
bw.RunWorkerCompleted += (c, d) => bw.RunWorkerAsync();
bw.RunWorkerAsync();
};
}
下面的代码是执行图象处理来进行运动识别的关键部分。代码在Emgu的示例代码的基础上进行了一些修改。Pluse方法中的第一个任务是将彩色影像数据流产生的ColorImageFrame对象转换到Emgu中能处理的图象数据类型。_forgroundDetector对象被用来更新_motionHistory对象,他是持续更新的基准影像的容器。_forgroundDetector还被用来与基准影像进行比较,以判断是否发生变化。当从当前彩色影像数据流中获取到的影像和基准影像有不同时,创建一个影像来反映这两张图片之间的差异。然后将这张影像转换为一系列更小的图片,然后对运动识别进行分解。我们遍历这一些列运动的图像来看他们是否超过我们设定的运动识别的阈值。如果这些运动很明显,我们就在界面上显示视频影像,否则什么都不显示。
private void Pulse()
{
using (ColorImageFrame imageFrame = _kinectSensor.ColorStream.OpenNextFrame())
{
if (imageFrame == null)
return; using (Image<Bgr, byte> image = imageFrame.ToOpenCVImage<Bgr, byte>())
using (MemStorage storage = new MemStorage()) //create storage for motion components
{
if (_forgroundDetector == null)
{
_forgroundDetector = new BGStatModel<Bgr>(image
, Emgu.CV.CvEnum.BG_STAT_TYPE.GAUSSIAN_BG_MODEL);
} _forgroundDetector.Update(image); //update the motion history
_motionHistory.Update(_forgroundDetector.ForgroundMask); //get a copy of the motion mask and enhance its color
double[] minValues, maxValues;
System.Drawing.Point[] minLoc, maxLoc;
_motionHistory.Mask.MinMax(out minValues, out maxValues
, out minLoc, out maxLoc);
Image<Gray, Byte> motionMask = _motionHistory.Mask
.Mul(255.0 / maxValues[]); //create the motion image
Image<Bgr, Byte> motionImage = new Image<Bgr, byte>(motionMask.Size);
motionImage[] = motionMask; //Threshold to define a motion area
//reduce the value to detect smaller motion
double minArea = ; storage.Clear(); //clear the storage
Seq<MCvConnectedComp> motionComponents = _motionHistory.GetMotionComponents(storage);
bool isMotionDetected = false;
//iterate through each of the motion component
for (int c = ; c < motionComponents.Count(); c++)
{
MCvConnectedComp comp = motionComponents[c];
//reject the components that have small area;
if (comp.area < minArea) continue; OnDetection();
isMotionDetected = true;
break;
}
if (isMotionDetected == false)
{
OnDetectionStopped();
this.Dispatcher.Invoke(new Action(() => rgbImage.Source = null));
return;
} this.Dispatcher.Invoke(
new Action(() => rgbImage.Source = imageFrame.ToBitmapSource())
);
}
}
} private void OnDetection()
{
if (!_isTracking)
_isTracking = true;
} private void OnDetectionStopped()
{
_isTracking = false;
}
运动模板 —— 运动检测(只用到RGB信息)
<Window x:Class="KinectMovementDetection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="400" Width="525">
<Grid >
<Image Name="rgbImage" Stretch="Fill"/>
</Grid>
</Window>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Microsoft.Kinect;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using Emgu.CV;
using Emgu.CV.Structure;
using System.Windows;
using System.IO; namespace ImageManipulationExtensionMethods
{
public static class EmguImageExtensions
{
public static Image<TColor, TDepth> ToOpenCVImage<TColor, TDepth>(this ColorImageFrame image)
where TColor : struct, IColor
where TDepth : new()
{
var bitmap = image.ToBitmap();
return new Image<TColor, TDepth>(bitmap);
} public static Image<TColor, TDepth> ToOpenCVImage<TColor, TDepth>(this Bitmap bitmap)
where TColor : struct, IColor
where TDepth : new()
{
return new Image<TColor, TDepth>(bitmap);
} public static System.Windows.Media.Imaging.BitmapSource ToBitmapSource(this IImage image)
{
var source = image.Bitmap.ToBitmapSource();
return source;
}
}
}using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Microsoft.Kinect;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using Emgu.CV;
using Emgu.CV.Structure;
using System.Windows;
using System.IO; namespace ImageManipulationExtensionMethods
{
public static class EmguImageExtensions
{
public static Image<TColor, TDepth> ToOpenCVImage<TColor, TDepth>(this ColorImageFrame image)
where TColor : struct, IColor
where TDepth : new()
{
var bitmap = image.ToBitmap();
return new Image<TColor, TDepth>(bitmap);
} public static Image<TColor, TDepth> ToOpenCVImage<TColor, TDepth>(this Bitmap bitmap)
where TColor : struct, IColor
where TDepth : new()
{
return new Image<TColor, TDepth>(bitmap);
} public static System.Windows.Media.Imaging.BitmapSource ToBitmapSource(this IImage image)
{
var source = image.Bitmap.ToBitmapSource();
return source;
}
}
}using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.VideoSurveillance;
using Microsoft.Kinect;
using System.ComponentModel;
using ImageManipulationExtensionMethods; namespace KinectMovementDetection
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
KinectSensor _kinectSensor;
private MotionHistory _motionHistory; // 历史运动模板
private IBGFGDetector<Bgr> _forgroundDetector;
bool _isTracking = false; public MainWindow()
{
InitializeComponent(); this.Unloaded += delegate
{
_kinectSensor.ColorStream.Disable();
}; this.Loaded += delegate
{ _motionHistory = new MotionHistory(
1.0, //in seconds, the duration of motion history you wants to keep
0.05, //in seconds, parameter for cvCalcMotionGradient
0.5); //in seconds, parameter for cvCalcMotionGradient _kinectSensor = KinectSensor.KinectSensors[]; _kinectSensor.ColorStream.Enable();
_kinectSensor.Start(); BackgroundWorker bw = new BackgroundWorker(); // 单独线程上执行操作
bw.DoWork += (a, b) => Pulse();
bw.RunWorkerCompleted += (c, d) => bw.RunWorkerAsync();
bw.RunWorkerAsync();
};
} private void Pulse()
{
using (ColorImageFrame imageFrame = _kinectSensor.ColorStream.OpenNextFrame())
{
if (imageFrame == null)
return; using (Image<Bgr, byte> image = imageFrame.ToOpenCVImage<Bgr, byte>())
using (MemStorage storage = new MemStorage()) //create storage for motion components
{
if (_forgroundDetector == null)
{
_forgroundDetector = new BGStatModel<Bgr>(image
, Emgu.CV.CvEnum.BG_STAT_TYPE.GAUSSIAN_BG_MODEL);
} _forgroundDetector.Update(image); //update the motion history
_motionHistory.Update(_forgroundDetector.ForegroundMask); //get a copy of the motion mask and enhance its color
double[] minValues, maxValues;
System.Drawing.Point[] minLoc, maxLoc;
_motionHistory.Mask.MinMax(out minValues, out maxValues
, out minLoc, out maxLoc);
Image<Gray, Byte> motionMask = _motionHistory.Mask
.Mul(255.0 / maxValues[]); //create the motion image
Image<Bgr, Byte> motionImage = new Image<Bgr, byte>(motionMask.Size);
motionImage[] = motionMask; //Threshold to define a motion area
//reduce the value to detect smaller motion
double minArea = ; storage.Clear(); //clear the storage
Seq<MCvConnectedComp> motionComponents = _motionHistory.GetMotionComponents(storage);
bool isMotionDetected = false;
//iterate through each of the motion component
for (int c = ; c < motionComponents.Count(); c++)
{
MCvConnectedComp comp = motionComponents[c];
//reject the components that have small area;
if (comp.area < minArea) continue; OnDetection();
isMotionDetected = true;
break;
}
if (isMotionDetected == false)
{
OnDetectionStopped();
this.Dispatcher.Invoke(new Action(() => rgbImage.Source = null));
return;
} this.Dispatcher.Invoke(
new Action(() => rgbImage.Source = imageFrame.ToBitmapSource())
);
}
}
} private void OnDetection()
{
if (!_isTracking)
_isTracking = true;
} private void OnDetectionStopped()
{
_isTracking = false;
} }
}
Kinect 开发 —— 进阶指引 (下)的更多相关文章
- Kinect 开发 —— 进阶指引(上)
本文将会介绍一些第三方类库如何来帮助处理Kinect传感器提供的数据.使用不同的技术进行Kinect开发,可以发掘出Kinect应用的强大功能.另一方面如果不使用这些为了特定处理目的而开发的一些类库, ...
- Kinect for Windows SDK开发入门(15):进阶指引 下
Kinect for Windows SDK开发入门(十五):进阶指引 下 上一篇文章介绍了Kinect for Windows SDK进阶开发需要了解的一些内容,包括影像处理Coding4Fun K ...
- Kinect 开发 —— 手势识别(下)
基本手势追踪 手部追踪在技术上和手势识别不同,但是它和手势识别中用到的一些基本方法是一样的.在开发一个具体的手势控件之前,我们先建立一个可重用的追踪手部运动的类库以方便我们后续开发.这个手部追踪类库包 ...
- Kinect 开发 —— 语音识别(下)
使用定向麦克风进行波束追踪 (Beam Tracking for a Directional Microphone) 可以使用这4个麦克风来模拟定向麦克风产生的效果,这个过程称之为波束追踪(beam ...
- Kinect开发文章目录
整理了一下去年为止到现在写的和翻译的Kinect的相关文章,方便大家查看.另外,最近京东上微软在搞活动, 微软 Kinect for Windows 京东十周年专供礼包 ,如果您想从事Kinect开发 ...
- Kinect开发笔记之三Kinect开发环境配置具体解释
0.前言: 首先说一下我的开发环境,Visual Studio是2013的,系统是win8的64位版本号,SDK是Kinect for windows SDK 1.8版本 ...
- iOS开发进阶
<iOS开发进阶>基本信息作者: 唐巧 出版社:电子工业出版社ISBN:9787121247453上架时间:2014-12-26出版日期:2015 年1月开本:16开页码:268版次:1- ...
- HTML5游戏开发进阶指南(亚马逊5星畅销书,教你用HTML5和JavaScript构建游戏!)
HTML5游戏开发进阶指南(亚马逊星畅销书,教你用HTML5和JavaScript构建游戏!) [印]香卡(Shankar,A.R.)著 谢光磊译 ISBN 978-7-121-21226-0 201 ...
- Kinect开发学习笔记之(一)Kinect介绍和应用
Kinect开发学习笔记之(一)Kinect介绍和应用 zouxy09@qq.com http://blog.csdn.net/zouxy09 一.Kinect简单介绍 Kinectfor Xbox ...
随机推荐
- PostgreSQL Replication之第四章 设置异步复制(1)
执行完您的第一个即时恢复(PITR,Point-In-Time-Recovery),我们准备在一个真正的复制设置上工作.在本章,您将学会如何设置异步复制和流.我们的目标是确保您可以实现更高的高可用和更 ...
- Unified BeginFrame scheduling for Chrome
Unified BeginFrame scheduling for Chrome http://goo.gl/D1Qxrr Status: http://crbug.com/401331 and ht ...
- Rendering and compositing out of process iframes
For Developers > Design Documents > Out-of-Process iframes (OOPIFs) > Rendering and ...
- NodeJS学习笔记 (24)本地路径处理-path(ok)
模块概览 在nodejs中,path是个使用频率很高,但却让人又爱又恨的模块.部分因为文档说的不够清晰,部分因为接口的平台差异性. 将path的接口按照用途归类,仔细琢磨琢磨,也就没那么费解了. 获取 ...
- TCP的连接管理
创建连接:(三次握手) 第一步: 客户端向服务器发送一个报文,该报文不含有数据段,SYN=1,随机产生sequence number(随机产生可用于避免某些安全性攻击) 第二步: 服务器收到报文,为这 ...
- rac重新启动遭遇ORA-01078、ORA-01565、ORA-17503、ORA-12547
今天測试环境server重新启动导致一个节点集群无法重新启动,遭遇ORA-12547错误.详细例如以下: server重新启动后,rac1集群无法启动,rac2正常启动: [root@rac1 ~]# ...
- vue10 v-text v-html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- AngularJS初接触
todo.json [ { "action": "Buy Flowers", "done": false }, { "action ...
- HDU 1512 左偏树+并查集
思路: 左偏树里面掺了一些并查集的应用 这里放一份左偏树的代码模板 重点就是merge函数了-- int merge(int k1,int k2){ if(!k1||!k2)return k1+k2; ...
- HDU2665 kth number 线段树做法
题意:求区间第k小 思路: 线段树 每个节点上保存 当前区间已经排序好的序列 (归并一下就好了嘛 复杂度 O(l)的) 这样建树的时空复杂度都是 O(nlogn)的 对于 每次询问 二分一个答案 在树 ...