运动识别

利用运动识别(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. inceptionnet

    inception发展历程 v1 mlp多层感知器层 上面两个观看孔径,尺寸不一样,可以抽取不同分辨率,不同尺度的邻域范围的信息作为特征,这样就可以观察到输入数据的不同层次,不同分辨率的特征 因为这个 ...

  2. affe(8) solver 优化方法

    上文提到,到目前为止,caffe总共提供了六种优化方法: Stochastic Gradient Descent (type: "SGD"), AdaDelta (type: &q ...

  3. 【Git 二】Windows|Mac 安装 Git

    Windows 或 Mac 上安装 Git 相对于 Linux 上安装来说步骤是简便一些的.Linux 安装步骤见:[Git 一]Linux安装Git 一.Windows 安装 Git 直接下载对应 ...

  4. 浅谈Python Web的五大框架

    说到Web Framework,Ruby的世界Rails一统江湖,而Python则是一个百花齐放的世界.各种micro-framework.framework不可胜数. 尽管还有一大脚本语言PHP也有 ...

  5. android 选取部分 log 的两种方法

    Grep多个条件: android logcat -v time | grep -e A -e B 选取多个android log tag: android logcat -v time -s TAG ...

  6. 设计模式之九:建造者模式(Builder)

    建造者模式: 将一个复杂对象的建造过程和它的表示分离开来,这样同样的建造过程能够创建不同的表示. Separate the construction of a complex object from ...

  7. Dubbo源代码分析(三):Dubbo之服务端(Service)

    如上图所看到的的Dubbo的暴露服务的过程,不难看出它也和消费者端非常像,也须要一个像reference的对象来维护service关联的全部对象及其属性.这里的reference就是provider. ...

  8. [Java][log4j]支持同一时候按日期和文件大小切割日志

    依据DailyRollingFileAppender和RollingFileAppender改编,支持按日期和文件大小切割日志.  源文件: package com.bao.logging; impo ...

  9. linux大于2T的磁盘使用GPT分区的方法分享

    (parted)表示在parted中输入的命令,其他为自动打印的信息 1.首先类似fdisk一样,先选择要分区的硬盘,此处为/dev/sdb ey: parted /dev/sdb 2.选择了/dev ...

  10. 消息推送学习一、原生Socket的使用

    消息推送也是客户端和服务器连接然后进行交互的一种形式,但是不同于HTTP的连接,这种连接需要长时间的进行,当有消息时可以及时推送到客户端.除此之外还有多个用户,可能需要针对其身份进行不同的推送等等要求 ...