视频中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(区域)。 为了达到这个目的,我们可以用Difference
和Threshold
方法过滤。
Difference differenceFilter = new Difference();
IFilter thresholdFilter = new Threshold(15, 255);
// 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。 这样,我们可以我们可以得到一个比较真实的结果。
IFilter erosionFilter = new Erosion();
// apply the filter
Bitmap tmp3 = erosionFilter.Apply(tmp2);
一个最简单的动作捕捉器已经搞好了,如果需要的话我们可以让有动作的区域高亮显示。
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);
下面便是结果:
从上面的图象我们可以看出这个方法的不足,如果一个物体移动得比较慢而且平稳的话,我们可以看到每帧都没有比较明显的变化 ,所以很难发现这个移动的物体。当一个物体移动得非常慢的时候,事情就变得更糟,这个算法根本不可能发现这个物体,也就不会返回任何结果了。 // 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(15, 255)); 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);
// 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(15, 255)); 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