日期:2014-05-18  浏览次数:21185 次

「玩一玩」翻译:基于AForge.Net的扑克牌视频识别程序
看到有趣的东西,忍不住翻译过来了。

----------------



© 版权所有 野比 2012
原文地址:点击查看
作者:Nazmi Altun「土耳其」

源码下载:点我

demo下载:点我

介绍

(图片上的字:方块4,方块J,黑桃2)

用机器人配上扑克牌识别系统,就可以在二十一点一类的扑克游戏中扮演荷官或是人类玩家的角色。实现这样的程序同样也是学习计算机视觉和模式识别的好途径。
本文涉及到的AForge.NET框架技术有二值化、边缘检测、仿射变换、BLOB处理和模板匹配算法等。
需要注意的是,这篇文章和文中介绍的系统是针对英美扑克设计的,可能不适用于其他种类的扑克。然而,本文描述了扑克的检测和识别的基本方法。因此,具体的识别算法需要根据扑克牌型特点而加以变化。
这里有一个视频演示。
YouTube
直接访问 通过代理访问(在代理页面输入http://www.youtube.com/watch?v=dui3ftwsuhM然后访问)

(话说我传到优酷上了怎么总是「发布中」呢?)

扑克检测
我们需要检测图像(指采集到的视频画面,下同——野比注)上的扑克对象,以便能进行下一步的识别。为了完成检测,我们会用一些图像滤镜对视频画面进行处理。
第一步,将图像去色(即灰度化——野比注)。去色是将彩色图像转换成8bit图像的一种操作。我们需要将彩色图像转换为灰度图像以便对其进行二值化。
我们把彩色图像转为灰度图像后,对其进行二值化。二值化(阈值化)是将灰度图像转换为黑白图像的过程。本文使用Otsu的方法进行全局阈值化。

C# code
Bitmap temp = source.Clone() as Bitmap; // 复制原始图像
 
 FiltersSequence seq = new FiltersSequence();
 seq.Add(Grayscale.CommonAlgorithms.BT709);  // 添加灰度滤镜
 seq.Add(new OtsuThreshold()); // 添加二值化滤镜
 temp = seq.Apply(source); // 应用滤镜




(图片上的字:原始图像、灰度图像、二值(黑白)图像)
有了二值图像后,就可以用BLOB处理法检测扑克牌了。我们使用AForge.Net的BlobCounter类完成这项任务。该类利用连通区域标记算法统计并提取出图像中的独立对象(即扑克牌——野比注)。
C# code
// 从图像中提取宽度和高度大于150的blob
 BlobCounter extractor = new BlobCounter();
 extractor.FilterBlobs = true;
 extractor.MinWidth = extractor.MinHeight = 150;
 extractor.MaxWidth = extractor.MaxHeight = 350;
 extractor.ProcessImage(temp);


执行完上述代码后,BlobCounter类会滤掉(去除)宽度和高度不在[150,350]像素之间的斑点(blob,即图块blob,图像中的独立对象。以下将改称图块——野比注)。这有助于我们区分出图像中其他物体(如果有的话)。根据测试环境的不同,我们需要改变滤镜参数。例如,假设地面和相机之间距离增大,则图像中的扑克牌会变小。此时,我们需要相应的改变最小、最大宽度和高度参数。
现在,我们可以通过调用extractor.GetObjectsInformation()方法得到所有图块的信息(边缘点、矩形区域、中心点、面积、完整度,等等)。然而,我们只需要图块的边缘点来计算矩形区域中心点,并通过调用PointsCloud.FindQuadriteralCorners函数来计算之。
C# code
foreach (Blob blob in extractor.GetObjectsInformation())
 {
  // 获取扑克牌的边缘点
  List< IntPoint > edgePoints = extractor.GetBlobsEdgePoints(blob);
  // 利用边缘点,在原始图像上找到四角
  List< IntPoint > corners =  PointsCloud.FindQuadrilateralCorners(edgePoints);
 }


(图片上的字:在图像上绘制边缘点、寻找每张扑克的角)
找到扑克牌的四角后,我们就可以从原始图像中提取出正常的扑克牌图像了。由上图可以看出,扑克牌可以横放。扑克牌是否横放是非常容易检测的。在扑克牌放下后,因为我们知道,牌的高度是大于宽度的,所以如果提取(转化)图像的宽度大于高度,那么牌必然是横放的。随后,我们用RotateFlip函数旋转扑克牌至正常位置。
注意,为了正确识别,所有的扑克应当具有相同的尺寸。不过,鉴于相机角度不同,扑克牌的尺寸是会变化的,这样容易导致识别失败。为了防止这样的问题,我们把所有变换后的扑克牌图像都调整为200x300(像素)大小。
C# code
// 用于从原始图像提取扑克牌
 QuadrilateralTransformation quadTransformer = new QuadrilateralTransformation();
 // 用于调整扑克牌大小
 ResizeBilinear resizer = new ResizeBilinear(CardWidth, CardHeight);
 
 foreach (Blob blob in extractor.GetObjectsInformation())
 {
      // 获取扑克牌边缘点
      List<IntPoint> edgePoints = extractor.GetBlobsEdgePoints(blob);
      // 利用边缘点,在原始图像上找到四角
      List<IntPoint> corners =  PointsCloud.FindQuadrilateralCorners(edgePoints);
      Bitmap cardImg = quadTransformer.Apply(source); // 提取扑克牌图像
 
      if (cardImg.Width > cardImg.Height) // 如果扑克牌横放
           cardImg.RotateFlip(RotateFlipType.Rotate90FlipNone); // 旋转之
      cardImg =  resizer.Apply(cardImg); // 归一化(重设大小)扑克牌
        .....
 }


(图片上的字:使用QuadriteralTransformation类从原始图像提取出的扑克牌。该类利用每张牌的四角进行变换。)
到目前为止,我们已经找到了原始图像上每张扑克牌的四角,并从图像中提取出了扑克牌,还调整到统一的尺寸。现在,我们可以开始进行识别了。
 © 版权所有 野比 2012
识别扑克牌
有好几种用于识别的