Opencv 图像识别Android实战(识别扑克牌 6.如何获取候选区)

第一节:再谈单步法和两步法

        前面我们说图像识别领域通常有两类方法:一类是单步法,另一类是基于候选区的两步法,常用的单步法多数是基于DNN的方式,能解决更为复杂的问题,但是对设备要求较高,也需要更多的样本,在很多自然场景下对单个类别的样本数据要求可能达到数千张,比较典型的算法是YOLO。基于候选区典型算法比如faster rcnn,目前也是比较成功的,但是目前相对而言YOLO优势更大,速度快,应用广,应用起来也比较简单。 

       上面提到的两类常用算法,都是基于深度神经网络的实现,这样做很多好处,比如更加准确,泛化能力更强等等。但是一旦用上了深度学习就意味着,你的程序会更加复杂,对硬件设备要求更高,否则在某些实时应用场合没有太大的意义,所以深度学习框架一般都是要支持GPU运算来提升速度的。然而在嵌入式设备上通常是没有GPU来加速的,在本例中也是一样,我们还是运用传统的算法来解决这个比较简单而实际的问题,而并不是通过构建神经网络来解决这个问题。

       这样做由很多优势:一、针对这种单一环境下的图像识别处理,本身就只需要用传统算法解决问题,不必要复杂化,二、这种环境下我么得到的样本质量较高,只需要少量样本就可以解决问题,不需要太强的泛化能力也能搞定识别,三、简单的算法和处理过程能提高帧率,使用起来更流畅。事实证明这样做是非常明智的,尤其是在嵌入式设备上,效果是极好的。

      到底用不用深度学习网络,我是这样看的:如果能用传统的算法解决问题就不用DNN的方式,优缺点前面已经提到过了,有些场合必须要用DNN的实现这无可厚非(使用DNN进行图像识别绝非必须的手段,只是很多现成的实现都是基于DNN的)。我觉得再CV领域很多论文都在堆砌网络来实现图像识别,说实话我并不觉得这是一个多么明智的方法,我觉得相比堆砌网络,更为重要的是图像分割技术,只有好的图像分割技术才能更加优雅的解决问题,模仿神经网络的方式到底是不是图像识别的终极方法,或者说是不是一个明智的方向这任然是一个疑问,不少学者已经对深度学习这个方向产生了质疑,认为这门学科是玄学,已经遇到了瓶颈,其实我也有类似的看法:-),深度学习貌似是学术界的时髦产物大家都在研究而已。

第二节:使用Opencv提取候选区域

      这小节将结合实例源码以及Opencv API 来探讨两步法的第一步候选区域的提取。

在这里我们会用到Imgproc.findContours 函数,这个函数在做图像识别的时候经常会被调用到,他主要用来寻找联通区域(或者叫轮廓),他会根据颜色的灰度把输入图像分割成不同大小的小区域,大多数情况下这些区域就是我们要的候选区。比如在本例中,经过findContours 函数拿到的小区域可能就是我们要识别的扑克牌上面的数字或者花色。下面看一下这个函数的C++原型(直接看Demo代码会更好):

cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])

返回两个值:contours:hierarchy。

参数

第一个参数是寻找轮廓的图像;

第二个参数表示轮廓的检索模式,有四种(本文介绍的都是新的cv2接口):

    cv2.RETR_EXTERNAL表示只检测外轮廓

    cv2.RETR_LIST检测的轮廓不建立等级关系

    cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。

    cv2.RETR_TREE建立一个等级树结构的轮廓。

第三个参数method为轮廓的近似办法

    **cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1**,即max(abs(x1-x2),abs(y2-y1))==1

    cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息

    cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

cv2.findContours()函数返回两个值,一个是轮廓本身,还有一个是每条轮廓对应的属性。

在这里我要重点介绍第二个参数,前两种  cv2.RETR_EXTERNAL和cv2.RETR_LIST,在我实践的过程中我发现只有在传入cv2.RETR_LIST的时候,才能得到足够多的轮廓,从参数的解释来看,这也是合理的,第三个参数选择显得不是那么重要,在本例中我选择了CHAIN_APPROX_SIMPLE,起始选择其他的参数,对本例来说效果上几乎没有差别。

需要注意的是在执行findContours函数的之前还做了一些其他的处理,最主要的就是灰度处理和二值化,其中二值化是findContours函数的要求,必须传入二值化图像,灰度处理则是减少图像干扰的的手段,详细代码见

com.myopencvdemo.PokerRecActivity

经过这一步处理后得到的图像是这样的(待续。。。)

你可能感兴趣的:(Opencv 图像识别Android实战(识别扑克牌 6.如何获取候选区))