![](https://i-blog.csdnimg.cn/blog_migrate/8f845c4407f67afb32e5f8d2ad7582e8.png)
文章目录
一、功能实现
1.1 resize_img
def resize_image(img, pix_max, pix_min):
""" 根据图片设置的最大、最小阈值 resize 图片(同比例) """
img_size = img.shape
h_mul_w = img_size[0] * img_size[1]
im_scale = 1.0
if h_mul_w > pix_max:
im_scale = float(pix_max) / h_mul_w
elif h_mul_w < pix_min:
im_scale = float(pix_min) / h_mul_w
new_h = int(img_size[0] * im_scale)
new_w = int(img_size[1] * im_scale)
re_im = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
return re_im, (float(new_h) / img_size[0], float(new_w) / img_size[1])
1.2 四分类方向检测
- step 1: 图像输入的 shape=(224, 224) 且为灰度图。
- step 2: 以
vgg16
为 backbone 的 4 分类模型 (0, 90°, 180°, 270°),检测后根据结果旋转图片,将图片摆正。 注意这里使用的是一个onnx
模型,用 onnxruntime 框架执行运算,仅在 cpu 下,速度可达 0.06s/张。- step 3: 在方向检测摆正图片后,先对图片进行 makeborder 操作,再输入到接下来的识别逻辑中,copyMakeBorder补上一圈白色的边,防止有的图表格线是顶着边的,不利于 unet 检测。
1.3 表格线的语义分割和提取
- step 1: 对于已经旋转过的图片,通过一个 Unet 网络做语义分割,得到横线与纵线的概率矩阵,然后利用 measure 模块对两个概率矩阵进行处理分析,从而提取表格中的横线与纵线。
- step 2: 根据横线的斜率,计算图片倾斜角,并再次旋转图片,使横线正好平行于视图。
- step 3: 由于 mask 可能出现断线,故用一些形态学算法,补上这些断线,为正确提取单元格做准备。
- step 4: 此部分返回三个值,同等旋转后的 img, img_seg,以及表格的 Rows_y_merge,以便更好的是实现表格内的 cell 的定位。
![](https://i-blog.csdnimg.cn/blog_migrate/1400a1355120370896f9681351989b64.png)
![](https://i-blog.csdnimg.cn/blog_migrate/74cc22b582e84673dcb7f4b91c695a61.png)
![](https://i-blog.csdnimg.cn/blog_migrate/24475794ab9dff46427dbd31867aadf8.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/b8d26ea542fd69518cb2da7c71732a90.png)
1.4 找到图中表格与非表格区域: find_tables
- step 1: 根据 img_seg 找到图中的大包围框,使用的方法是在 mask 里 “找白色轮廓”
- step 2: 根据找到的大包围框(表格部分),将原图切成表格、非表格区域,非表格区域按普通 ocr 方法处理。
1.5 获得表格内的 cells 位置信息:find_cells
- step 1: 用于寻找表格内部每个单元格的位置坐标(xmin, ymin, xmax, ymax),便于之后截取出来识别,注意这一步仅仅是寻找单元格的坐标,至于所处的行列信息以及里面包含的文字内容识别此步不考虑。
- step 2: 由于白色线分隔出来的小黑块就是单元格位置,因此寻找黑色连通域就可以了,skimage 有个很好的包 measure,可以很方便的完成连通域的寻找以及 bbox 的获取。
1.6 获得图片中的文本位置信息:find_text
根据 tables 的坐标位置信息进行图片分割,返回文本区域位置坐标信息。
1.7 初始化 json 字典
构造形如合合的初始 AI json 字典,可以去合合官网看字典结构和每个字段的含义,以便接下来的识别信息直接填入字典中。
1.8 文本区域识别:recognize_text
- step 1: 根据文本区域的位置信息,截取 roi 文本图片;
- step 2: 对该文本区域进行 craft 检测,返回 box 坐标信息,然后根据 box 的坐标来进行排序调整;
- step 3: 因为 craft 检测的 box 可能导致一行文字被分割开来,为了保持原有的格式,进行同一行文字合并并排序;
- step 4: 文本 batch 识别
- step 5: batch_text 解析,返回识别的结果,填入到 josn 字典中。
1.9 表格区域识别
step 1: cell 所处行列的判别
- 任务是确定每个单元格的 start_column, end_column, start_row, end_row,目前的算法是做四次,依次确定 start_column, end_column, start_row, end_row。以 start_column 为例,将所有单元格按 xmin 排序,那么同一始列的 xmin 一定很接近,不同起始列的 xmin 有明显差异,如果投影在 x 轴上,同一列的会聚在一起,列转换时会有明显的突变。
- 同样的 end_column 对应 xmax,start_row 对应 ymin,end_row 对应 ymax。
- 但是在实验中发现,row 更易发生错误,那个跳变的阈值很难定,原因是有可能有下面这样的图:这种情况的行就不能这么做了,直观的想一想 1,2,3 的位置 ymin 的变化量是差不多的。故此,第二版的思路是,记住每条横线的位置(Rows_y)。
- 起始行是第一行的单元格只能夹在 1,2 之间,依次类推。因为这样相对比较麻烦,而且第一版对列的判别基本没问题,所以用 1.0 思想做 start_column, end_column,2.0 思想做 start_row, end_row。
![](https://i-blog.csdnimg.cn/blog_migrate/7edfd333af3da1ce5bc3428619eb7237.png)
![](https://i-blog.csdnimg.cn/blog_migrate/688e50a9a78a6969b2413cbd1e2bd712.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3ad06bba8ab1b86a2fea8e6c35b646c3.png)
step 2: cell 内部文字识别
- 接下来是确定每个单元格里的文字内容,因为有些单元格里面的文字不止一行,crnn 是不具备识别这种图的能力的,并且有的单元格留白太多,也不利于 crnn 识别。如果对每一个单元格都用文字检测模型去定位文字位置,则代价太高,时间耗时太长。针对表格文字比较规整这一特点。因此先做 line_split 算法,快速的将单元格内待识别的内容切割出来,送进 crnn 识别。
- 表格 cell 内的文字分割后进行 batch 处理,送入 crnnOcr2 进行文字识别。
![](https://i-blog.csdnimg.cn/blog_migrate/2c1b0fb8daba53af3a38d12fbfabbe68.jpeg)
step 3: batch_text 解析
1.10 整理成合合 AI 表格 ocr 的返回 json 格式
二、网络结构
本项目中一共用了4个网络,作用各不相同
网络名称 | 框架 | 作用 | 模型位置 |
---|---|---|---|
vgg16 | onnx | 四分类,0-90-180-270判别 | ./onnx |
unet | darknet | 语义分割,二类,一类表格横线,一类纵线 | ./darknet |
craft | pytorch | 非表格区域的文字检测 | ./craft |
crnn | pytorch | 文字识别 | ./crnn |
三、运行需要的关键性环境说明
- python 必须 3.6 版本以上!原因:2.7 不支持 onnxruntime 运算,3.5 对 scikit-image 库支持有限
- torch 1.4.0 with CUDA 10.0
- opencv 4.2.0
- skimage 0.16.2
- web 0.51
- onnx 1.6.0
- onnxruntime 1.2.0