[导入]Motion Detection Algorithms---动作捕捉算法 - song

时间:2024-03-12 18:32:24
本来想找些关于视频流方面的东西,在CodeProject上搜索了看看,没想到找到很好玩的东西:
视频中Motion Detection(动作捕捉)的一个程序,好文章,翻译一下给大家看看:




动作捕捉算法
作者:
Andrew Kirillov

一个捕捉视频流中动作的算法:

介绍

捕捉联系视频流中的动作有很多种算法。每种算法都是基于将视频流中当前一帧与前一帧(被看作背景)比较。在本篇文章中,将讨论一些最常用的动作捕捉方法 。

在描述这个算法的时候,我将用到一个图像处理类库 Image Processing Lab in C#. 这是我以前写的。所以,如果你看过那篇文章,那将会有很大的帮助。

本程序支持下面的视频格式:

  • AVI 文件 (用到windows视频,所以要用到interop库);
  • 通过网络照相机不断更新的JPEG图像;
  • 通过网络照相机传输的MJPEG (motion JPEG)流 ;
  • MMS流- Microsoft Media Services;
  • 本机视频捕捉系统(USB照相机、摄像头或其它设备).;

 

算法

在所有的动作捕捉算法中,最常用的是将当前帧与前一帧比较。当你在视频压缩中只需要记录变化,而不是完整的一帧的时候相当的有用, 但他不是动作捕捉的最好方法。所以,接下去我将详细讲述令一个方法。
假设我有一张原始的24 bpp RGB的图像叫做当前帧(图像) ,一个降色调的复制版本(当前帧的)和一个前一帧的降色调复制版本 (背景帧) 。首先,找出两帧图像的不同的regions(区域) 为了达到这个目的,我们可以用DifferenceThreshold 方法过滤。

 

// create filters
Difference differenceFilter = new Difference();
IFilter thresholdFilter 
= new Threshold(15255);
// set backgroud frame as an overlay for difference filter
differenceFilter.OverlayImage = backgroundFrame;
// apply the filters
Bitmap tmp1 = differenceFilter.Apply(currentFrame);
Bitmap tmp2 
= thresholdFilter.Apply(tmp1);


在这个步骤中,我们可以得到一张图片,当前帧与背景帧中不同的地方,都已经被着成白色。现在,已经可以数出两张图上不同像素点的数目。当这个数目大于一个事先定义好的数字时,我们便可以标注为一个动作事件。
但是,大多数相机照出的照片,都有杂点,所以很有可能在没有任何动作的时候,程序会认为有新的动作发生了。要去处照片上随即的杂点(random noisy pixels), 我们可以用一个 Erosion filter 这样,我们可以我们可以得到一个比较真实的结果。

// create filter
IFilter erosionFilter = new Erosion();
// apply the filter 
Bitmap tmp3 = erosionFilter.Apply(tmp2);

个最简单的动作捕捉器已经搞好了,如果需要的话我们可以让有动作的区域高亮显示。
// extract red channel from the original image
IFilter extrachChannel = new ExtractChannel(RGB.R);
Bitmap redChannel 
= extrachChannel.Apply(image);
//  merge red channel with motion regions
Merge mergeFilter = new Merge();
mergeFilter.OverlayImage 
= tmp3;
Bitmap tmp4 
= mergeFilter.Apply(redChannel);
// replace red channel in the original image
ReplaceChannel replaceChannel = new ReplaceChannel(RGB.R);
replaceChannel.ChannelImage 
= tmp4;
Bitmap tmp5 
= replaceChannel.Apply(image);

下面便是结果:


从上面的图象我们可以看出这个方法的不足,如果一个物体移动得比较慢而且平稳的话,我们可以看到每帧都没有比较明显的变化 ,所以很难发现这个移动的物体。当一个物体移动得非常慢的时候,事情就变得更糟,这个算法根本不可能发现这个物体,也就不会返回任何结果了。

所以,这里还有另一个算法:我们可以将当前帧和视频流的第一帧比较。 这样,如果在初始帧中没有物体,比较这两帧我们就可以发现这个移动得很慢的物体(不论它移动得有多慢)。但是,这个方法也有一个很大的缺陷,例如:有一辆车在第一帧上,但是速度很快,到第二帧上就没有了,会怎么样呢?我们可以捕捉到动作的发生,但是车已经不在了。当然,有些时候我们可以更新初始页面,去掉那些存在的物体,但是仍然不会给我们满意的结果。但是,有时候会有相反的结果, 如果在墙上挂张照片,当我在初始帧更新的时候,会捕捉到动作的发生,而这显然是不正确的结果。

在所有的算法之中,最最高效的方法是基于建立一张背景,然后将每一帧和这张背景比较。有很多中方法可以建立这个背景,但是大多数都太复杂。在这里,我将描述我建立背景的方法。这是一个比较简单而且可以迅速理解的方法。

在先前的例子中,我们假设了有一张原始的24 bpp RGB 图片,把它称作当前帧 ,一个降色调的复制版本(当前帧的)和一个前一帧的降色调复制版本 (背景帧) 。一开始,我们将视频流的第一帧作为背景帧,然后,我们将每一帧都和这张背景帧相比较。但是,只是这么做的话,会得出我上面总结出的结论,这显然不是我们想要的。我的方法是“移动”背景帧,使其趋近于当前帧(我用的是1 level/frame),我们将背景帧慢慢的按照当前帧的走向移动--我们按照1 level/frame的速度改变背景帧的像素。

// create filter
MoveTowards moveTowardsFilter = new MoveTowards();
// move background towards current frame
moveTowardsFilter.OverlayImage = currentFrame;
Bitmap tmp 
= moveTowardsFilter.Apply(backgroundFrame);
// dispose old background
backgroundFrame.Dispose();
backgroundFrame 
= tmp;

现在,我们可以用相同的方法处理下面的内容。但是我还扩展了这个方法,让它得出更好玩的一个结果。

// create processing filters sequence
FiltersSequence processingFilter = new FiltersSequence();
processingFilter.Add(
new Difference(backgroundFrame));
processingFilter.Add(
new Threshold(15255));
processingFilter.Add(
new Opening());
processingFilter.Add(
new Edges());
// apply the filter
Bitmap tmp1 = processingFilter.Apply(currentFrame);

// extract red channel from the original image
IFilter extrachChannel = new ExtractChannel(RGB.R);
Bitmap redChannel 
= extrachChannel.Apply(image);
//  merge red channel with moving object borders
Merge mergeFilter = new Merge();
mergeFilter.OverlayImage 
= tmp1;
Bitmap tmp2 
= mergeFilter.Apply(redChannel);
// replace red channel in the original image
ReplaceChannel replaceChannel = new ReplaceChannel(RGB.R);
replaceChannel.ChannelImage 
= tmp2;
Bitmap tmp3 
= replaceChannel.Apply(image);




现在看上去好多了吧!
在该想法的基础上,我还想出了另一种解决方法。在一钱的例子中,我们有前一帧、原始帧,还有两者降色调的版本。但是我们可以在处理之前先用Pixellate filter 处理这些图像

// create filter
IFilter pixellateFilter = new Pixellate();
// apply the filter
Bitmap newImage = pixellateFilter(image);

这样,我们有了像素处理后的当前帧和原始帧版本。现在,我们需要将背景帧“移向”当前帧(和以前做的一样)。

// create processing filters sequence
FiltersSequence processingFilter = new FiltersSequence();
processingFilter.Add(
new Difference(backgroundFrame));
processingFilter.Add(
new Threshold(15255));
processingFilter.Add(
new Dilatation());
processingFilter.Add(
new Edges());
// apply the filter
Bitmap tmp1 = processingFilter.Apply(currentFrame);

 

将tmp1和原始图像的红色段合并后,我们可以得到下面的结果:


可能这看起来没有上面的那张图好,但是这种方法对于程序的优化起了重大的作用。

结论

这里我只是简单讲了思想,想要将这些思想用到真正的程序中去的话,你需要优化很多东西。为了简单我用了image processing library 。它不是一个Video处理库 。但是,这个库使我很快的能够找到不同的region 。在源程序中,有一个小的优化例子。 

翻译到此结束,Download source - 114 Kb
我下载了源码,运行后,发现真的很酷,可以链接到网上的好多free camera,监测那边的情况,一有动作,就立刻会显示出来:



看见了吧,连电视机的内动的画面都能发现。不过由于网络的延迟,看了不是很爽。
顺便提一下,截图程序用的是Maxwolf的
文章来源:
http://blog.handsbrain.com/leezjs/archive/2005/08/12/9365.aspx