最近经朋友介绍,做了一个高铁路边上杆 自动识别的项目,项目自我感觉难度非常大,因为有大量的特殊场景,涉及到图像二值化、分割、识别问题也非常多。项目时间又很紧,成功交付以后感触颇多,再想想这么多年 上查资料的多,共享的少,所以想通过这篇文章把整个项目的解决思路和大家共享一下。
说实话,目前 上与模式识别相关的文章,大部分都只是些知识点的介绍,或者用例的简单介绍,而所谓项目就是客户提供了一系列的照片,由你自己分析特征、尝试提取、根据结果调整算法,最终生成的可执行程序提供给客户,而且这个结果如果很粗糙或者错误率很高,客户根本是不会接受的。写这篇文章的目的就是和大家共享下这个过程,特别很多刚接触这块的,如何把图像处理、模式识别书中孤立的知识点组合起来,达到最佳效果,从而解决实际问题的。
这个项目大的方面是高铁6C项目的一个子模块。给我们的具体要求就是客户会有很多组照片,每组照片是一台相机同步高速拍摄,在实际客运列车运行中进行,时速300/250/200,相机有海量的存储器,整个过程拍摄下来后,一组内就有超过2万张照片,这样靠人工分类的工作量非常大,所以需要将每张照片中线杆的杆 能够自动识别出来从而实现将海量照片以杆子的编 进行分类。限于篇幅,这篇文章里面主要介绍线杆的提取部分,其他部分以后再陆续介绍。
接下来结合实际图,谈谈整个项目过程吧。有个比较有意思的事可以说下,在项目最初洽谈阶段,对方提供了一个小图集给我们。并且还告知我们以前有个另外公司的软件做相同的事情,但是识别率很低,基本上不能接受。我们看了样图之后,认为可以做这个项目。虽然以前也做过不少的工程项目,知道最后要实际应用的图集和样图肯定有偏差,但是当最后拿到实际数据后,复杂程度还是有些比较出乎我们意料的。
(a) 车站内出发,无合适目标 (b) 雨中拍摄的照片 (c) 雨中拍摄的照片,文字被遮挡
(g) 火车转弯中且无合适杆子 (h)大斜率的转弯且有玻璃投影,且多个文字区域 (i) 清晨照片,二值化很难
(3) 杆子不是竖直的。很多情况下杆子会倾斜,特别在转弯情况下倾斜度会很大,之前说过竖直投影找杆子的方法,也完全可以否定了。倾斜的杆子还带来另外一个问题,直接把倾斜文字区域给tesseract识别的话,识别率会大幅下降,所以找杆子现在还多了一个新任务,计算斜率,供文字区域纠偏使用。
(4) 杆子背景复杂。由于火车是移动的,背景也是变的,还有隔音板等情况,很多图中不再是一根孤零零的竖直杆子,特别杆子底部也很难确定。另外客户提供的图是灰度图也增加了难度,如果彩色图就会相对简单很多,可以提取的特征值也会更多,在多个特征值情况下可以夹逼出想要的区域。还有一个情况就是会存在多个杆子并列情况。
(5) 文字区域数量变化大。文字区域不再是例图中的一个明显区域,有可能同时有2个或更多,有可能杆子上没有文字区域。
(6) 不能完全利用数字的前后规律。杆子虽然是按一部分规律前进的,但这个规律经常会被打断,所以还是要识别尽可能准确,规律只能用于少数文字被污渍遮挡时的结果优化。
在确定上述几条困难后,重新审视下之前提到的方法,从思路看还是应该那样,先找到线杆,再定位出文字区域,最后结合前面杆子的识别文字区域内的数字。只是现在可以确定的是,这三大步步骤没错,但每一步都很难做到结果百分之百正确,特别由于污渍、光照2个因素影响,定位文字区域和字符识别两步的准确率肯定会受影响。而且原来设想的简单通过二值化方法已经行不通了。经过进一步的分析后,虽然找杆子也有很多困难和挑战,但相对来说特征还是更多点,只有这一步做到接近百分之百的成功,后续才可能总的准确率超过5成,所以我先介绍如何在上述那些困难中准确的找到杆子。
在动手编程找杆子之前,就需要先总结下这个项目中杆子的特征,总结如下:
(1) 杆子都是相对地面竖直的黑色柱体
(2) 大部分情况下,杆子的左右两侧会有大部分空白或者部分空白
(3) 图中会有多个这样的柱子,离最近的是希望识别的柱子,远的是干扰。
因为采用直接二值化方法把线杆分割出来的方法已经不可行了,所以最后我们换了一种思路,就是把线杆的竖直边缘寻找出来,然后根据两条平行边缘的灰度值与背景灰度值之差来确定线杆。最后设计的搜索杆子方案如下:
(1) 利用Sobel算子增强出线杆的竖直边缘。
(2) 对(1)的结果进行二值化处理。
(3) 使用hough变换检测直线。
(4) 利用上面提到的特征1和2,标记疑似的杆子两边的线
(5) 因为会有多个平行杆子,我们选择了最粗的杆子并从中提取杆 。
我们之所以采用这种方法提取边缘,而不是采用比如canny之类的边缘提取方法,是因为Canny之类的边缘提取结果对于我们这个项目有两个弊端,一是提取的结果包含了太多我们不需要的边缘,二是提取出来的边缘即使在线杆两侧也未必是直线。而Sobel算子一方面增强了边缘,另外,方法具有方向性,所以可以只提取我们所需要的竖直线。另一个优点是这种方向性也可以帮我们甄别提取的边缘是线杆的左边缘还是右边缘,最后的结果也证明了这种方法给我们的识别带来了极大的便利性。
经过Sobel算子增强后的边缘经过二值化处理后可以直接利用hough变换检测直线。说到hough变换检测直线,这也是让我萌生写这篇分享的原因之一,hough的原理是将图像空间的像素转变到参数空间,然后在参数空间中对直线/圆/椭圆的点进行统计,最后通过阈值判决是否是符合要求的形状,所以其实hough出来的结果是相对杂乱无章的,不可能一个hough就能得到想要的结果,只能说想要的结果在hough检测中,需要再找出来,而目前国内相关技术文章只介绍这个方法,如何做下一步就不谈了。
以下各图显示了线杆的提取过程:
(a) 通过Sobel算子对竖直边缘增强 (b) 二值化增强结果
(c) 提取出来所有的直线,可以看出主要都是竖线 (d) 经过进一步优化处理后提取的线杆,其中绿色的左边缘,红色的是右边缘。
以上大概就是线杆提取的过程。 限于篇幅,文字区域提取将在今后在进一步详细介绍。
项目采用的编程环境是vs2013的opencv+tesseract,由于时间紧,来不及训练一个识别器,否则还可以提供下识别的准确性,因为所需识别字符相对来说比较简单,做起来其实也不复杂,可用的方法也很多,比如决策树,SVM,神经 络以及最近比较流行的深度学习,效果应该都不错。但是因为时间关系,我们先是直接试了一下Tesseract。Tesseract大家都比较熟悉了,识别的结果可以接受,再加上调用方便,这样为项目节省了很多时间。使用opencv的好处,也在于opencv很多算法可以迅速调用,结果不理想时替换也方便,毕竟自己从头写每个算法还是太耗时了。这里说给题外话,所谓实际项目接触的一般都是非图像处理、模式识别相关方面人士,专业性会比较差,哪怕对方也是计算机相关的,后来会提到开始给的测试图,还是产生了不少误会,导致最终算法走了些弯路。而做过实际项目的都知道,非专业人士,很多需求他们觉得很轻松随口就提了下,而有时候这些需求根本是无法实现的,因为毕竟目前模式识别技术还是有瓶颈,识别能力有限。像我这个项目问对方有没有对性能有要求,对方回复是没有要求,几个小时内一组做出来就行,但计算下就知道了,哪怕8个小时,换成时间是3600×8=28800秒。 而一组是2万张照片的话,每张这样处理时间是1.44秒。这意味着,每张照片图像分割到最后字符识别,总的时间耗费不能超过1.44秒,而做过图像相关的项目都知道,很多算法是非常耗时的,所以选择用opencv+tesseract+自己编写算法,这样才可能在处理速度上达到用户的需求,其实还有不少加速处理的技巧,我们最后结果是一个小时内可以跑4万到5万张图片。 而且这个结果是单进程,单线程条件下实现的,相信如果采用多线程等方法后,速度还可以进一步提高。。处理时间还好,用户最关心的还是识别率问题,用户找过来时提到一点,其实他们已经找过其他公司尝试做过,但出来的识别率3成不到,所以客户放弃了他们。这个就是模式识别的残酷性,可能对方也花了大量时间和精力,但用户这些不会考虑这些的,人家只会看最终结果。所以建议在项目开始之前还是好好评估下自己能力和用户需求,如果达不到不必强求,毕竟我也经常遇到并不靠谱的需求。
其实这些需求里面,还有一个隐藏的需求就是代码稳定性,当然所有客户是默认这点没有问题,但如果最终算法很完美,处理时间也能控制在一秒之内,但一组就有2万张图,可能跑过几十张异常了就挂了,或者内存泄漏跑了几千张malloc失败导致挂了或者处理不下去了(做c/c++都知道会有内存泄漏问题),这样同样客户是不会接受的。说到这个,从我读书到实际工作这么多年,国内学校的研究生导师和博导对编程能力这块很经常忽视,说不客气点甚至有的是轻视,认为只要算法出来就行,我接触的有人甚至这么说,算法出来了找几个有经验的工程师优化下就行了,对这点我实在无法苟同。诚然图像处理、模式识别算法的设计很重要,但程序是实现你思维的途径,最基本编程能力都没,比如写几行一堆错debug都要半天,怎么实现你思维;复杂的项目必须由很多工作组成,代码能力不够怎么驾驭几千行甚至上万行代码;或者代码由不懂你算法的工程师来优化,怎么保证他优化后的代码和你原来执行结果百分之百相同,而且当有不一致时如何定位错在哪里;再比如大部分图像处理书为了方便理解,取某一个点都是用pixel[y*width+ x]这种方法来表示的,学生按这样写出来的算法,光遍历个图像就有大量的乘法,前面提到的一秒的处理要求如何能达到以编程能力和算法设计是同等重要的,像这个项目,我在项目之初就注意到这点,所以在内存分配和释放上非常当心,并且出错处理都加了打印和保护( 上经常有人说,大学老师教出来的都是只有if没else的,出错处理都要到公司才学会),可以接受某个图无识别结果但不能跑挂,因为我这个项目时间很紧,只有2个半月的时间,基本上最终代码出来自己试跑几次后就要给客户了,事实上我也在试跑几次后就开始没有任何内存泄漏,在处理几十张图像后内存始终在很小范围内波动而不是累加的,然后又解了几个中间跑挂的问题后,后期当客户测试时都是使用新的图集进行测试的,没有收到任何跑挂的反馈。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!