运动识别

利用运动识别(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 开发 —— 进阶指引 (下)的更多相关文章

  1. Kinect 开发 —— 进阶指引(上)

    本文将会介绍一些第三方类库如何来帮助处理Kinect传感器提供的数据.使用不同的技术进行Kinect开发,可以发掘出Kinect应用的强大功能.另一方面如果不使用这些为了特定处理目的而开发的一些类库, ...

  2. Kinect for Windows SDK开发入门(15):进阶指引 下

    Kinect for Windows SDK开发入门(十五):进阶指引 下 上一篇文章介绍了Kinect for Windows SDK进阶开发需要了解的一些内容,包括影像处理Coding4Fun K ...

  3. Kinect 开发 —— 手势识别(下)

    基本手势追踪 手部追踪在技术上和手势识别不同,但是它和手势识别中用到的一些基本方法是一样的.在开发一个具体的手势控件之前,我们先建立一个可重用的追踪手部运动的类库以方便我们后续开发.这个手部追踪类库包 ...

  4. Kinect 开发 —— 语音识别(下)

    使用定向麦克风进行波束追踪 (Beam Tracking for a Directional Microphone) 可以使用这4个麦克风来模拟定向麦克风产生的效果,这个过程称之为波束追踪(beam ...

  5. Kinect开发文章目录

    整理了一下去年为止到现在写的和翻译的Kinect的相关文章,方便大家查看.另外,最近京东上微软在搞活动, 微软 Kinect for Windows 京东十周年专供礼包 ,如果您想从事Kinect开发 ...

  6. Kinect开发笔记之三Kinect开发环境配置具体解释

            0.前言:        首先说一下我的开发环境,Visual Studio是2013的,系统是win8的64位版本号,SDK是Kinect for windows SDK 1.8版本 ...

  7. iOS开发进阶

    <iOS开发进阶>基本信息作者: 唐巧 出版社:电子工业出版社ISBN:9787121247453上架时间:2014-12-26出版日期:2015 年1月开本:16开页码:268版次:1- ...

  8. HTML5游戏开发进阶指南(亚马逊5星畅销书,教你用HTML5和JavaScript构建游戏!)

    HTML5游戏开发进阶指南(亚马逊星畅销书,教你用HTML5和JavaScript构建游戏!) [印]香卡(Shankar,A.R.)著 谢光磊译 ISBN 978-7-121-21226-0 201 ...

  9. Kinect开发学习笔记之(一)Kinect介绍和应用

    Kinect开发学习笔记之(一)Kinect介绍和应用 zouxy09@qq.com http://blog.csdn.net/zouxy09 一.Kinect简单介绍 Kinectfor Xbox ...

随机推荐

  1. codeforces 544 D Destroying Roads 【最短路】

    题意:给出n个点,m条边权为1的无向边,破坏最多的道路,使得从s1到t1,s2到t2的距离不超过d1,d2 因为最后s1,t1是连通的,且要破坏掉最多的道路,那么就是求s1到t1之间的最短路 用bfs ...

  2. python ORM理解、元类

    元类 参考链接:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143191 ...

  3. caioj 1082 动态规划入门(非常规DP6:火车票)

    f[i]表示从起点到第i个车站的最小费用 f[i] = min(f[j] + dist(i, j)), j < i 动规中设置起点为0,其他为正无穷 (貌似不用开long long也可以) #i ...

  4. C++ 容器(一):顺序容器简介

    C++提供了使用抽象进行高效编程的方式,标准库中定义了许多容器类以及一系列泛型函数,使程序员可以更加简洁.抽象和有效地编写程序,其中包括:顺序容器,关联容器和泛型算法.本文将简介顺序容器(vector ...

  5. HDU4565 So Easy! 矩阵高速幂外加数学

    easy 个屁啊,一点都不easy,题目就是要求公式的值,但是要求公式在最后的取模前的值向上取整.再取模,无脑的先试了高速幂 double  fmod来做,结果发现是有问题的.这题要做肯定得凑整数,凑 ...

  6. 在Unix上用 BIND建立名称服务器(naem server)

    在Unix上用 BIND建立名称服务器(naem server) 安装 apt install -y bind9 yum install -y bind bind-utils 下载源码并解压缩,htt ...

  7. 静态时序分析SAT

    1.   背景 静态时序分析的前提就是设计者先提出要求,然后时序分析工具才会根据特定的时序模型进行分析,给出正确是时序报告. 进行静态时序分析,主要目的就是为了提高系统工作主频以及增加系统的稳定性.对 ...

  8. BZOJ1045: [HAOI2008]糖果传递&BZOJ1465: 糖果传递&BZOJ3293: [Cqoi2011]分金币

    [传送门:BZOJ1045&BZOJ1465&BZOJ3293] 简要题意: 给出n个数,每个数每次可以-1使得左边或者右边的数+1,代价为1,求出使得这n个数相等的最小代价 题解: ...

  9. Install the IIS 6.0 Management Compatibility Components in Windows 7 or in Windows Vista from Control Panel

    https://technet.microsoft.com/en-us/library/bb397374(v=exchg.80).aspx Install the IIS 6.0 Management ...

  10. legend---一、如何实现js跳转到php页面

    legend---一.如何实现js跳转到php页面 一.总结 一句话总结:url还是同样的方式,只不过注意引号内嵌的时候的转义. 代码: onClick="javascript:if(con ...