写点什么

快速入门开发实现订单类图片识别结果抽象解析

  • 2020-02-06
  • 本文字数:3645 字

    阅读完需:约 12 分钟

快速入门开发实现订单类图片识别结果抽象解析

一、背景

面对订单数据纸质文件或图片,紧靠人眼识别效率低,需引入机器学习来识别图片并解析来提高效率。当前市面已有收费的图片识别服务,包括阿里、百度等,识别效果较好,但针对订单类图片,不仅关注图片上的文字,还关注文字所在的行列,来分出每条数据和数据详细字段。


本篇内容主要介绍一种针对识别完结果进行行列解析的抽象流程和方案,来提高开发效率。


本文只提供思路,不提供源码。另外,不是搞人工智能专业,图片识别太深奥,不过有兴趣的同学请关注大神们的文章,这里推荐“宜信技术学院官网文章 AI 板块”。

二、解析流程

对于图像处理,opencv 算是比较优秀的,所以选做本文图像处理首选软件。为了使图片识别率更高,需要先做图片矫正,这里采用较为简单的霍夫变换加去噪声点算法矫正图片。图片矫正后,调用图片识别服务获取结果,一般结果格式包括响应码、错误描述、文字块列表(文字和四点坐标)等。根据识别结果使用抽象的俄罗斯方块法来识别结果获取行列信息。最后根据行列信息组装每一行数据并显示。

三、细节处理

3.1、opencv 安装概要

opencv 安装简单提示,这里不细说,以后有时间单独发文。


windows


  1. 下载编译好的包,https://opencv.org/releases/

  2. 解压缩到自定义文件夹


linux


  1. 推荐使用 ubuntu,并且最好是全新的系统,因为 opencv 会依赖很多包,对版本要求也高,解决冲突会很麻烦

  2. 下载源码

  3. 安装依赖包

  4. 编译安装


我们使用 java 调用 opencv,这里需要安装获取到开发包,windows 为 opencv_javaxxx.dll,linux 为 libopencv_javaxxx.so,程序初始化时需要加载到 jvm。 详细代码如下:



System.load(PropertieUtil.getPropertie("这里是dll或so的完整路径");
复制代码

3.2、图片矫正

3.2.1、矫正探索

图片矫正探索之路较为艰辛,起初我们想了一个比较简单的方案,先调用图片识别服务,获取到结果,然后根据每一个字块的四角坐标判断出每个字块的倾斜角,然后再根据去噪算法算出平均的倾斜角,理论这个方案是可行的,但实践证明我们是错的,因为图片识别服务返回的坐标图片不准确,多数的图片算出的结果都是错误的。


经查霍夫变换有可能解决这个问题,于是开始尝试学习霍夫变换和去燥算法,最终发现可行,并抽象出公共方法,仅需简单配置一些参数就能完成矫正。


图片矫正分为两步:


  • 第一步:正反矫正,判断图片倾斜角度是 90°、180°、270°、0°,这个通过数学方法是无法判断的,需要引用机器学习来判断。

  • 第二步:角度微调,一般为确定图片是正的,且倾斜角度在±30°左右。


需要注意:上面说的办法不可能通过一套参数来对所有图片进行微调,但是线上数据证明,针对一类图片,一套参数基本能让大多数图片都矫正正确。

3.2.2、霍夫变换概要

霍夫变换是数学界经典空间变换算法,用于检测直线,通过大量检测到的直线的斜率就能计算出图片倾斜角度。需要先进行二值化和边缘检测,再进行霍夫变换效果更佳。详细算法内容请自行搜索,本文不细聊。

3.2.3、去噪声点算法

基本公式:



上限=均值+n*标准差 下限=均值-n*标准差
复制代码


其中 n 取值一般为 1-4,数值越大表示筛选率越高。 最后再将符合的数据再求均值。


核心代码如下:



/** * 利用标准差筛选 * @param values * @return */ private static double[] calcBestCornList(double[] values) { // 计算标准差 StandardDeviation variance = new StandardDeviation(); double evaluate = variance.evaluate(values); Mean mean = new Mean(); double meanValue = mean.evaluate(values); double biggerValue = meanValue + CHOOSE_POWER * evaluate; double smallerValue = meanValue - CHOOSE_POWER * evaluate; List<double> selected = Lists.newArrayList(); for (double value : values) { if (value >= smallerValue && value <= biggerValue) { selected.add(value); } } double[] selectedValue = new double[selected.size()]; for (int i = 0; i < selected.size(); i++) { selectedValue[i] = selected.get(i); } logger.info("占比:{}%,筛选后角度数组:{}", (selectedValue.length / (float)values.length) * 100F, selected); return selectedValue; }</double>
复制代码

3.2.4、霍夫变化抽象封装

基本流程:


  1. 义相关参数

  2. 读取图片

  3. 灰度二值化处理

  4. 使用 opencv 画出轮廓

  5. 根据参数要求多次画霍夫变换线,直到线数量满足参数为止

  6. 遍历画出的线,分出横线和竖线,根据配置计算出每条线角度

  7. 使用去噪声算法(需要根据非 0 数自动重复计算)算出平均倾斜角度

  8. 使用 opencv 旋转图片


核心代码如下:



/** * 矫正图片,通过霍夫变换矫正 * @param oldImg 原始图片 * @param rotateParam 旋转参数 * @return */ public static String rotateHoughLines(File oldFile, String oldImg, RotateParam rotateParam, String cid, String bankCode) throws Exception {
Mat src= Imgcodecs.imread(oldFile.getAbsolutePath()); //读取图像到矩阵中 if(src.empty()){ throw new Exception("no file " + oldFile.getAbsolutePath()); } // 用于计算的图片矩阵 Mat mathImg = src.clone(); // 灰度化 Imgproc.cvtColor(src, mathImg, Imgproc.COLOR_BGR2GRAY); logger.info("二值化完成"); // 获取轮廓 Imgproc.Canny(src, mathImg, rotateParam.getCvtThreshould1(), rotateParam.getCvtThreshould2()); logger.info("轮廓完成"); // 霍夫变换获取角度,详细代码略 double corn = houghLines(mathImg, rotateParam, cid); logger.info("霍夫变换完成,角度:{}", corn); if(corn == 0) { return oldImg; } return rotateOpenv(oldFile, corn, cid, bankCode); }
复制代码

3.3、常用图片识别方案

阿里、百度都有提供图片识别服务,另外如果有实力也可以自己实现,当然不建议,样本需求量巨大,时间成本过高。

3.4、识别结果解析

3.4.1、探索之路

本文重点内容在这,因为前面所提到的都是较为基础的服务和算法,大量开发内容都在本章。前期要开发的订单图片类型巨量(大于 100 种),每一类图片区别很大,我们有几个人分类型开发,但是每个人方法都不同,且张三开发出来的李四看不懂,毕竟是面对的图片,比较抽象,是可以理解的。开发一段时间,发现了问题。每种类型最快也要一周才能开发完成,而且解析成功率极低。我们发现开发出一套抽象的方法来把行列数据提取出来迫在眉睫。


调研发现大家常用两种方法来提取行列数据,分别为坐标法和标题法,但是这两种方法解析率都不高。经过几周思考,终于想出了一套较好的方法,命名为俄罗斯方块法,解决了问题。

3.4.2、俄罗斯方块法

思路概要:


  1. 拿到识别结果数据

  2. 先把所有数据的 y 坐标进行排序

  3. 遍历排序结果,先把第一条放入第一列结果集中

  4. 从第二条开始和第一列结果集对比

  5. 对比方法:


  • 如果在第一列结果集其中一条数据的右侧,则认为是新列。

  • 如果在 y 轴方法和第一列结果其中某些数据重叠了,则认为是新列。


  1. 如果以上两条都不是,则认为本条数据还在当前列中,放入第一列结果集

  2. 以此类推,继续对比直到对比到最后一列最后一条数据

  3. 按照上面方法,反过来,以 x 轴为标准,能够得到行结果集


思路图如下:


1573468661345056731.jpeg


概要代码如下:



// 按照最左上角的x坐标排序 OcrWordInfo[] sortL = NoTableParseResult.ParseUtil.bubbleSortX(ocrResponse.getPrism_wordsInfo(), false); NoTableParseResult ntpr = new NoTableParseResult(param); ntpr.setHeight(converImg.height()); ntpr.setWight(converImg.width()); for (int i = 0; i < sortL.length; i++) { // 当前要比较的数据 OcrWordInfo ocrWordInfo = sortL[i]; // 处理当前列数据 ntpr.getUtil().testCurColData(ocrWordInfo); } // 处理最后一列 ntpr.lastCol();
/** * 判断是否为下一列,并处理 * @param ocrWordInfo * @return */ public void testCurColData(OcrWordInfo ocrWordInfo) {
// 遍历当前列已存在的所有数据 int size = this.test.getCol().size(); if(size == 0) { this.test.addCol(ocrWordInfo); return; } for (int i = 0; i < size; i++) { OcrWordInfo temp = this.test.getCol().get(i); // 最右边的数据 int x1 = temp.getPos().get(1).getX(); int x2 = temp.getPos().get(2).getX(); // 当前数据最左边 int xx0 = ocrWordInfo.getPos().get(0).getX(); int xx3 = ocrWordInfo.getPos().get(3).getX();
int threholdx = this.test.param == null ? 0 : this.test.param.getCoverColXThrehold(); if(xx0 >= (x1 - threholdx) && xx0 >= (x2 - threholdx) && xx3 >= (x1 - threholdx) && xx3 >= (x2 - threholdx)) { // 当前数据在右边,说明换列了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this.test.colAdd(); this.test.addCol(ocrWordInfo); return; } else { // 判断是否覆盖坐标 int y0 = temp.getPos().get(0).getY(); int y3 = temp.getPos().get(3).getY(); int yy0 = ocrWordInfo.getPos().get(0).getY(); int yy3 = ocrWordInfo.getPos().get(3).getY(); int threhold = (int)Math.round((y3 - y0) * (this.test.param == null ? 0.25 : this.test.param.getCoverThrehold())); if(!(yy3 <= (y0 + threhold) || yy0 >= (y3 - threhold))) { // 当前列表数据重叠,说明换列了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this.test.colAdd(); this.test.addCol(ocrWordInfo); return; } } } // 执行到这说明没覆盖 this.test.addCol(ocrWordInfo); }
复制代码

3.4.3、解析行数据技巧

技巧总结:


  1. 俄罗斯方块法提供去除干扰项的参数,可以根据图片特点,去除上下左右干扰数据来减少串行列现象。

  2. 解析数据大致有两种方法:

  3. 第一种方法:根据标题列号来判断数据,这种方法不通用,比较简单,比较规范的图片识别率高,但是无法适配乱的图。

  4. 第二种方法:把每一行数据以间隔符号分割拼到一起,使用正则表达式来‘扣’数据,因为一般同类型订单图片,关键字段的位置是有特点的,例如金额格式、借贷方向、日期等,这种方法通用,但识别率不高。

  5. *具体使用哪种方法,还需要根据图片特点进行取舍。

  6. 俄罗斯方块法提供一些微调参数,用于适配一些特殊场景,例如换行列阀值之类的。

  7. 中间需要保存一些过程图片,例如矫正过程的若干张图、俄罗斯方块法识别结果的连线图等,毕竟这种项目,查问题时靠日志是没用的,还得靠这些中间图才能更快查到问题。

  8. 四、总结


  9. 本文提到方案不能完全解决所有订单类图片解析问题,可以做到新手快速入门快速开发,如果您有更好思路欢迎交流。


本文转载自宜信技术学院网站。


原文链接:http://college.creditease.cn/detail/323


2020-02-06 21:331166

评论

发布
暂无评论
发现更多内容

走进龙芯中科交流会圆满结束!深入探讨未来合作规划 | 理事长走进系列

OpenAnolis小助手

操作系统 国产操作系统 龙蜥社区

龙蜥社区第四届理事大会圆满召开!中兴、英特尔、浪潮成为副理事长单位!龙蜥高级顾问团成立!

OpenAnolis小助手

开源 操作系统 国产操作系统 龙蜥社区

龙年新目标!龙蜥安全联盟第三次月会圆满结束

OpenAnolis小助手

开源 操作系统 国产操作系统 龙蜥社区

龙蜥社区荣获 2023 年度龙芯“十佳基础软件合作伙伴”奖

OpenAnolis小助手

开源 操作系统 国产操作系统 龙蜥社区

产学研用全覆盖!信通院、中兴通讯、复旦大学等 12 家厂商共同成立龙蜥社区系统运维联盟(SOMA)

OpenAnolis小助手

操作系统 国产操作系统 龙蜥社区

解决过期苹果App应用的方法

ai制图软件有哪些?这5款自动生成绘画工具值得推荐!

彭宏豪95

人工智能 在线白板 办公软件 AIGC AI绘画

走进 Intel,深度探讨合作发展规划交流会圆满结束 | 理事长走进系列

OpenAnolis小助手

开源 操作系统 国产操作系统 龙蜥社区

龙蜥社区第 22 次运营委员会圆满结束!

OpenAnolis小助手

开源 操作系统 国产操作系统 龙蜥社区

使用selenium轻松实现元素拖拽

霍格沃兹测试开发学社

可观测性平台如何助推保险行业数智化转型与升级

博睿数据

小程序开放平台:开启企业数字化新时代

FinFish

小程序管理平台 开放平台 小程序开放平台

谈谈我对 AIGC 趋势下软件工程重塑的理解

阿里巴巴云原生

阿里云 云原生 AIGC

龙智亮相2024国际集成电路展览会暨研讨会(IIC Shanghai),分享芯片研发及管理解决方案与技术实践

龙智—DevSecOps解决方案

芯片研发

Selenium Headless模式:无头浏览器的使用与优势

霍格沃兹测试开发学社

龙蜥系统运维联盟第二次会议圆满召开,深度探讨联盟发展方向

OpenAnolis小助手

开源 操作系统 国产操作系统 龙蜥社区

运维人少,如何批量管理上百个微服务、上千条流水线?

阿里巴巴云原生

阿里云 云原生 云效

Lambda 表达式及线程安全最佳实践

伤感汤姆布利柏

iPaaS平台能帮助企业解决什么问题?

RestCloud

数据集成 应用集成 ipaas

使用Docker快速搭建Web服务器Nginx

霍格沃兹测试开发学社

龙蜥操作系统荣登开放原子开源基金会“2023 生态开源项目”奖项榜单

OpenAnolis小助手

开源 操作系统 国产操作系统 龙蜥社区

Optimism Hackathon: 加速 AI 与 Blockchain Data 发展

Footprint Analytics

大数据 gamefi #人工智能

GOPS全球运维大会2024深圳站亮点抢先看!

博睿数据

运维人少,如何批量管理上百个微服务、上千条流水线?

阿里云云效

阿里云 云原生 云效

详解CloudBees CI,助力Jenkins用户顺利迁移并构建高效CI/CD平台

龙智—DevSecOps解决方案

ci 持续集成 CD

走进浪潮信息,深入探讨社区发展规划交流会圆满结束 | 理事长走进系列

OpenAnolis小助手

开源 操作系统 国产操作系统 龙蜥社区

使用Selenium执行JavaScript脚本:探索Web自动化的新领域

霍格沃兹测试开发学社

59 人参会,探讨新年发展!龙蜥社区技术委员会、运营委员会会议圆满结束

OpenAnolis小助手

操作系统 国产操作系统 龙蜥社区

使用Selenium模拟鼠标滚动操作的技巧

霍格沃兹测试开发学社

快速入门开发实现订单类图片识别结果抽象解析_行业深度_刘鹏飞_InfoQ精选文章