当前位置: 首页 > 科技观察

使用Tensorflow Object Detection API进行集装箱识别并对集装箱号进行OCR识别

时间:2023-03-13 19:38:43 科技观察

使用TensorflowObjectDetectionAPI进行集装箱识别,OCR集装箱编号每个集装箱编号与其他业务系统的数据交换,以实现特定的整体需求。当时发布了TensorflowObjectDetectionAPI,所以放弃了YOLO或者SSD的选择,考虑使用TF实现Demo进行POC验证。背景我以前从来没有接触过深度学习相关的东西。为了验证这个需求能否实现,满足交付需求,从入门到理解选型到学习到准备图片和标注到完成基本的PythonDemo验证,几乎是一个人用完。耗时2个月(当时用的是2012年的MacBookAir),验证完成后,交给C++和C#的朋友实现。在这个过程中,我感受到了DeepLearning的魅力和力量,感受到了未来的无限可能,于是带着我的研发小伙伴走上了这条路。我尝试了人脸和语音,把它们嵌入到现有的所有产品中,不会有坑,因为只有第三方算法。我最近重新安装了我的Mac并重写了一个演示来验证Inteld改进了CPUDL支持的程度。老实说,代码并不重要,一点也不复杂。我想借这个demo来谈谈AI技术工程实施的思路和实施过程。问题分析1.确定要解决的具体问题。这个要求有两个关键点。主要应用场景是在集装箱装卸过程中,只有一个摄像头监控的位置:如何准确识别集装箱,识别后如何去重,保证“计数”“准确”;如何做OCR单摄像头无约束场景下的箱号识别,现有解决方案,集装箱识别-无;箱号OCR-有,但都是有约束场景下的识别。即:集装箱与摄像头的位置关系明显受限,而且很有可能最多使用3个摄像头进行联合判断并输出结果,与实际场景不符,没有任何意义。市面上大多针对“湾边”集装箱号识别的方案是将三路摄像头固定在发放停车卡的位置,所以能否实现这个需求还是要看单路的检测和OCR天马行空的场景。2、为什么不用SSD和YOLO?这两种目标检测方法速度快,识别准确率足够。之前确实考虑过选择其中一个来实现,但是我想了解如何做到这一点,我准备了几千张图片准备验证时发布了ObjectDetectionAPI。DL是对未来有深远影响的东西。选择一个最有可能成为大生态环境的平台非常重要。“前科”但在我的理解中,无论软件还是硬件层面,DL的推理能力在未来的OS上都是最重要的,甚至可以理解为OS(比如SoC)的一部分。因此,谷歌的任性可能从这两年的发展来看,谷歌在TF上的持续投入,而成为“大厂”的标志之一似乎就是是否有开源DL相关的工作。3.你不担心我们做不对吗?在此之前,整个计票团队只有一名成员在微软参与过人脸识别相关的工作,而且是通过机器学习实现的,并不是深度学习。我很担心,真的很担心做不成,所以连1080Ti的采购需求都不敢提。所有的前期工作都是和我12岁的MBA一起完成的。就是实在怕自己扛不住,才没有玩所谓的“权术”。我是公司的研发负责人,所以理论上,我把这个demo的预研工作交给我,找两个人搞定。完成后,我的工作仍然完成。我也没闲着,产品,架构,review,project,qualityassurance,sales,没完没了的开会,但是这件事真的不确定,不知道能不能做,好安排工作,但是我不确定安排虽然出去打工是一种“管理智慧”,但做事做人却不是这样。所以我白天做工作职责范围内的事情,下班后再回来做。如果可以的话,我可以熬夜。周末?周末是不可能的。所以说2个月,其实就是2个月的业余时间。深度学习在2018年继续火爆,越来越多的同学想要搭上这趟列车。如果你有机会读到这篇文字,我可以根据我的实际情况,负责任地告诉你:只要你全心投入,一定能上车走远。基础pipeline的内容很长,先说结论吧。我把这个流水线的定义和分析过程称为“最大误差分析法”。即:首先在整个链路中找到潜在误差最大的链路,先推导出可能实现误差最大链路的最优解,然后在此最优解的基础上向前和向后寻找最优解管道的剩余链接。这样定义的pipeline很有可能是整个问题的最优解。定义的问题如上所述。要求很简单。核心只有三个:单摄像头、计数、OCR。明确解决问题的路线。作为泛安防和视频公司,对计算机视觉有一定的技术积累和理解。我从DL和CV的角度,把当时看到的数据作为一个整体来判断分析。执行度和准确度肯定比集装箱号的OCR好。整体解决项目需求,实现项目精准落地,首先要设计和寻找提高箱号OCR准确率的技术路线,因为这才是最终的项目。精度的关键瓶颈。所有的工作都必须在摄像头的视频流中完成,所以我们解决问题的路线图应该是:–>找到OCR的最优解–>基于OCR的最优解找到容器识别和计数的最优解–>从整体上分析最优解的实现,估计最终结果的可接受性。如果判断最后的结果不能接受,那就回到原点,换个思路。先说容器吧。作为一个立方体,一个容器一共有6面,底部肯定是用来展示的,不用想了,那剩下的5面有什么信息呢?一般来说,5面都会喷上集装箱号,但尺寸和细节位置的区别,其中集装箱门(标准术语:端门,门)信息最全,除集装箱外号码,其余信息都在这边。该容器设计为重量轻且能够承受堆叠负载。侧面采用波纹钢板,可大大提高轴向承载能力。此外,容器编号也喷涂在顶面上。确定OCR目标区域的集装箱侧面采用波纹钢板制作,可以大大提高轴向承载能力。对于OCR来说,这是一个很大的挑战,因为它不是一个平面,视角不同,肉眼会出错。首先排除4左上角只有一个集装箱号。其实这个位置的箱号真的很适合OCR。第一个位置比较规范,喷涂位置在集装箱的钢架上。第二个位置是平面。第三个位置很少被污损。健康)状况。如果要保证能准确采集到这个位置的集装箱号,那么摄像头必须从上方拍摄才可行。那么问题来了,如果要从上面拍摄,必须保证有安装位置。但由于业务场景的限制,装卸集装箱不一定非要用龙门吊,也有可能是前臂吊(这两个名词请自行google)。看新闻联播,曾经说过“自动化无人码头”。这里的自动化是用吊车安装摄像头做OCR,告诉无人车把箱子送到哪里)。但是,不可能为前悬架安装摄像头。就算你尝试安装,损坏的概率估计也很高。所以,最先放弃的是条件最好的位置。回头看最初废弃的四个面,只有货柜门的那一面,绝大多数货主都会把货柜号喷在比较小的平面上,所以,对于我们的场景来说,把货柜号在这个位置做OCR就是唯一可靠的解决方案。如何保证OCR的基本准确度如何理解这个“基本准确度”?熟悉OpenCV的同学都知道,使用cv2.对于块检测,初步确定应该使用集装箱门的集装箱编号进行OCR。如果把整张图片交给OpenCV去寻找文本块,再交给后端OCR来接手,理论上是可以的,但是仔细看看。对于集装箱门,文本块过多。我们先不说检测的准确性。只是箱号检测这个任务,“噪音和干扰”太多了,最后的结果没有保障。因此首先要解决集装箱号文本块的提取问题。工作,后续的OCR会更加准确。不使用OpenCV,直接用NN网络一步解决文本检测和OCR问题不是更好吗?嗯,理论上NN的效果肯定更好,但是如何做demo验证,如何找大量的数据集进行训练呢?这是一个短期内无法解决的问题。可以理解FSNS数据集的规模,作为中期的技术路线规划,可以考虑用NN做OCR。在武侠世界里,唯一不能破的就是快,但需求就在眼前。客户不会听你解释几个小时的技术方案和技术进步,然后说我收集半年的数据。半年后,我会带来一个演示。你满意吗?你认为你是客户,你同意吗?所以你必须考虑其他的解决方案。目前的困境是:让OpenCV大面积搜索字符,精度没法保证,而用NN来做,精度可能有保证,但是没有时间窗口。后者无解;那么就看前者有没有破局的解法了。目标检测的目的是什么?为什么集装箱号的文字区域检测不到“目标”?这样就得到了一个只有容器编号的小文本块,OCR无论如何都不需要考虑冗余文本的噪声干扰问题。想了想就做了,标注了一个通宵的地图,老头跑了一天的训练,测试成功了,单次检测箱号的准确率非常高。OCR的输入问题解决了,至少在这个层面上,不会对最终的准确率产生太大的影响。至此,整个流水线是这样的:目标检测识别箱号——>发送给OCR引擎——>OCR结果输出。下面就来思考一下OCR引擎是如何实现的。在选择OCR引擎之前,我已经放弃了短期内用NN来实现OCR的考虑,所以只能回归到传统的机器学习思路,简单,不选Tesseract显然是在考虑它。当时还是3.05版本。4.0的LSTM版本还没正式发布,不敢用。如何训练Tesseract可以参考我的文章:使用Tesseract训练和识别集装箱号。到了开工的时候,手头有几千张集装箱图片,可以手动扣下来再处理。仍然可以收集到数千个集装箱号码图片。训练tesseract还是够用的,但是不用考虑了。要达到80%以上的准确率并不容易,因为在Attention-basedExtractionofStructuredInformationfromStreetViewImagery:Disclaimer中有一段话这段代码是我们在论文中使用的内部模型的修改版本。目前,经过400k步的训练后,它的全序列准确率达到了83.79%。此版本与论文中使用的版本之间的主要区别-对于本文,我们使用50个GPU(K80)worker(异步更新)进行分布式训练,提供的检查点是在约6天的训练后使用此代码创建的单GPU(TitanX)(训练24小时后达到81%),默认禁用坐标编码。所以,宇宙空间之外,应该还有另外一套解决办法,否则,他就知道自己可能会死。听上去像个笑话:手头有资源和技术,还得给自己留条路?嗯,世上没有难事,只要你肯想。在训练Tesseract标注的过程中,最大的收获是基本了解了TesseractOCR的实现流水线:图像二值化->字符边框->分割->识别。一遍又一遍的训练,不管我再笨,我都想通了:关键是要有准确的边框,如果边框准确,识别就会准确很多。已经验证了用目标检测的方法求出集装箱号的区域是非常准确的,所以我再把检测到的集装箱号标记出来,然后训练一个36类的目标检测,不是为了变相练习OCR么?容器编号是有序的。如果目标检测打乱了输出,你如何把它放回原来的特定顺序?这并不难。修改visualization_utils.py中visualize_boxes_and_labels_on_image_array函数的定义,让他多返回一个box_to_color_map参数。这个返回值是一个Dictionary,其中key是我们训练定义的标签,value是一个由四个值组成的List。这四个值分别是输出边界框相对于原图坐标原点的相对偏移值。获取此列表并分别沿X轴和Y轴对其进行排序。标签的顺序是容器编号的顺序。集装箱识别与计数至此,构思中的流水线已经实现了OCR部分的设计,下面是集装箱识别与计数。之前给出了一个条件:单摄像头,现在多了一个条件:集装箱门(enddoor)必须出现在摄像头画面中,否则找不到我们需要用于OCR的集装箱编号区域。因此,容器识别很简单。理论上只需要集装箱门和集装箱号两个标签就可以了。这就引出了一个新的问题:“如何保证集装箱门始终正对摄像头位置?”。经与用户沟通和现场调查,确定在工作过程中无法要求和限制集装箱门的朝向。即使提出这样的要求,也无法保证工作人员会按照您的要求来。继续考虑单摄像头方案,意味着至少有50%的OCR识别概率是喷涂在集装箱后门上的集装箱编号(标准术语:blindend,end_door)。结果就是即使短时间做了demo,验证准确率不高也就算了,做不做都会成为一个结果。说白了,这个时候我非典型码农的复合知识结构的价值就体现出来了。现场勘察时直接与用户沟通确认,既然无法帮我识别集装箱门,那么现场只设置了两个摄像头进行拍照。只要集装箱出现,其中一个摄像机就必须显示集装箱门。然后是摄像机的安装位置、角度、光照条件(只是东西方向,早晚总有一个大背光的摄像机)、网络、供电等一系列施工要求。进一步确定;然后,描述了整个识别业务流程和程序。基本页面展示效果,将要展示的数据,以及他们对接数据对接的要求,最后明确最终效果:不可能达到100%准确率的标准,大概准确率超过80%。如何通过干预对此类结果进行人工跟进审查。一切都约定好了,回去只有一个字:去做!算法训练和产品变现重点:卖不出去的产品不是好码农。用户沟通三要素:明确需求,明确目标,明确权利,明确义务,明确交付标准,控制期望,收集数据,在产品设计前期做简单验证时,只有不到1000张图片数据,这绝对是不够的。调动所有销售和现场发货人员,创造条件,有条件的到集装箱堆场、码头拍照。我终于找回了8000张照片,一张一张筛选后,留下了7000张能用的。本来数据集就小,不做人工筛选,弄点脏数据进去,骗自己。普通的产品经理接到这样的任务后,或许能轻松搞定:你也确认了流水线、页面展示和数据,以及目标检测的标签和注解内容,那我就按你说的去做。是的,绝对正确,领导说的。嗯,真遇到这么一个普通的产品经理,一点拓展思路,提升产品价值的想法都没有,我就炒了她。这件事情你从一开始就一直在关注,你也是跟着现场看的,你也全程参与了用户怎么说,我回来的时候也跟你说了我的想法,如何做出更多的惊喜对于用户看到这个产品,但是你没看懂...好的产品经理要么是千分之一,要么是千分之一,这个比例不会更高。最后确定了6个标签,其中3个是不同位置的容器编号,3个是容器的侧面和两端。因为做的是一个用户的需求,在产品层面需要考虑的是一个行业。这件事情做完并实施之后,业内其他用户如何将这项技术介绍给其他人的需求呢?在里面,这是我在标注图片时考虑最多的。反正批图是体力活,脑子闲着。只有多定义标签,未来才有更多的利用可能性。选择一个模型并训练一个目标检测模型。我之前在一个较小的数据集上做过训练和验证,我对最终的准确性有信心。反正我是做迁移学习的,训练集不大,10万步用不了几天,你只需要在快和准之间取得一个平衡。最终决定使用faster_rcnn_inception_v2进行生产。后来用它来做人脸检测。我也把数据集从VOC格式改成YOLO格式,同时在DarkNet下训练了两个YOLOv2模型,然后把tiny版本部署在NVIDIAJetsonTX2上,运行后看到效果。如果将TetsonTX2作为边缘设备,则有可能将落地方案做成边缘版本。下一波操作是为了自娱自乐。你总是有学习新事物的乐趣吗?当然现阶段我办公室已经有2*1080TiPC和4*1080Ti服务器\4*P100服务器。现在有了,我可以边训练边开心地玩了。如果目标检测训练是游戏,那么Tesseract训练就是折磨。不出所料,一开始训练结果60%到70%,泛化能力很一般。一开始我很纳闷为什么结果这么出乎意料,准备实现36种目标检测方案。一次又一次的训练和验证,大约花了两周的时间(Tesseract3.05在这一点上做得很好,数据准备好了,训练一轮也就几分钟)。最后找到泛化不好的原因:训练图片的原始分辨率波动很大。训练好的lang文件打开的时候,里面已经有训练错误了,所以不要指望输出是正确的。于是我同时做了两件事:1.花了好几天的时间,把几千张图片按照初始分辨率分成4类,然后在每个类里再细分3类,也就是定义训练样本为12类别,每个子类别单独训练。因为Tesseract的Clustering操作有一个特点,就是可以把多个样本的训练输出数据聚类打包在一起,所以我很高兴的把这15类整理组合起来;2.让销售协调用户按最终部署场景收集了一批实际操作视频和图片。协调的结果是,销售员买了2台相机,并配备了三脚架。他问他们晚上什么时候有作业,然后拿着笔记本坐在集装箱上顶着刺骨的寒风。两个晚上的录像,然后,呃,感冒了。然后,目标检测算法就绪,用视频跑来跑去,截取集装箱号,用于Tesseract验证。痛苦的折磨了自己将近2个月,终于基本搞定了,再加上一些Engineering技巧和数据处理也基本准备好发布给用户做POC了。pipeline的demo实现代码在这里:https://github.com/lonelygo/container_detection写在最后写到这里,本篇也告一段落了。重装了电脑,装了一个Intel预编译版的TF,不知道怎么办。据说比谷歌预编译的CPU版本快多了,但是我手头没有GPU电脑,所以没法比较,所以就拿手头的部分数据重放一下过程2017年的,我还记得那时候CPU的速度,GPU的速度。这对我来说是最简单的比较。最后贴一张检测效果图。【本文为栏目组织《AiChinaTech》原创文章,微信公众号(id:tech-AI)》】点此查看作者更多好文