总论中对PPStructure核心类做了概要介绍,本文旨在详解核心类之一:TableSystem类。回顾下总论中的内容,TableSystem类负责表格结构识别与表格内容匹配,代码在paddleocr/ppstructure/table/predict_table.py文件中。何谓表格结构识别?何谓表格内容匹配?
表格结构识别,就是在给定图片中识别出表头、表体等主体结构,并详细到表头包含多少列、表体包含多少行、哪些单元格跨列、哪些单元格跨行、每个单元格在什么位置。如总论中所述,开源团队选择了用html文本来表达一个表格结构。例如,用<thead>
来表示表头,用<tbody>
来表示表体,用<tr>
表示行,用<colspan><rowspan>
来表示跨行跨列。 如果希望了解到所有支持的表格要素,可以查看官方字典。
表格内容匹配,就是在给定图片中进行文本检测与识别,然后通过一定的匹配算法,将文本内容填入表格结构识别结果,形成既含结构又有内容的返回值。常用的匹配算法,是基于文本框与单元格的距离,找到距离最近的单元格,填入文本内容到格子中。
本文首先详细解释TableSystem类的__init__与__call__方法,接着通过应用场景代码实践,进一步了解该类的实现原理与用法。
构造函数__init__方法
构造函数用于构造TableSystem类的实例,是使用该类的前提。构造函数位于predict_table.py文件的第59行,定义如下:
def __init__(self, args, text_detector=None, text_recognizer=None)
传入参数有三个,分别解释如下:
-
args参数,代表模型配置,类型无约束,但也有属性最小集要求。至少应该包含如下设置:
- args.show_log代表是否显示日志信息
- args.benchmark 代表是否启用基准测试
- args.table_algorithm代表表格匹配算法
- args.det_model_dir代表文本检测模型路径
- args.rec_model_dir代表文本识别模型路径
- args.table_model_dir代表表格识别模型路径
- args.rec_char_dict_path代表文本识别模型配套字典路径
- args.table_char_dict_path代表表格识别模型配套字典路径
为了保持与源码的一致性,推荐使用parse_args函数来构造args参数实例,该函数的解释参照PPStructure核心源码研究(二)。
-
text_detector参数,代表指定文本检测模型实例,默认为None。如果未指定模型,系统将从args配置中构造文本检测模型实例,类型为predict_det.TextDetector。
-
text_recognizer参数,代表指定文本识别模型实例,默认为None。如果未指定模型,系统将从args配置中构造文本检测模型实例,类型为predict_rec.TextRecognizer。
魔法函数__call__
上节的构造函数完成了模型基础设置,真正体现功能的地方在魔法函数__call__。函数位于predict_table.py文件的第92行,定义如下:
def __call__(self, img, return_ocr_result_in_table=False)
传入参数解释如下:
- img参数,图像数据三维张量,形状为[h,w,3],分别代表图像高度、图像宽度、通道数(RGB)
- return_ocr_result_in_table参数,是否返回ocr识别结果,默认为False。如果改为True,那么不仅返回表格识别的html结果,还要返回表格中每个文本要素。文本要素包括边界框、文本、置信度等信息。
观察魔法函数__call__的代码,主干过程只有两个部分:结构识别与内容匹配。结构识别时,利用配置的表格预测模型与配套字典,完成输入图像到html结构代码的输出;内容匹配时,利用配置的文本检测与识别模型,得到所有文本框,再利用匹配算法,将文本填入html结构代码。返回值包含result与time_dict两项:
- result
第一项返回值result是解析结果,字典dict类型,最多包含cell_bbox、boxes、rec_res、html四个属性。以下详细解释各项属性:- cell_bbox属性,单元格边界框集合,列表list类型,每一项代表一个单元格位置,用八个数值的数组表示边界框四个顶点的x、y坐标
- boxes属性,文本检测结果集,列表list类型,每一项代表一个文本边界框,用四个数值的数组表示边界框的左上角与右下角x、y坐标。注意,此属性只在传入参数return_ocr_result_in_table为True时才返回。
- rec_res属性,文本识别结果集,列表list类型,每一项代表一个文本识别结果,用二元组表示。前者是文本,后者是置信度。注意,此属性只在传入参数return_ocr_result_in_table为True时才返回。
- html属性,表格结构与内容识别结果,文本str类型,用标准HTML语言描述表头、表体、单元格等。
- time_dict
第二项返回值为耗时统计,字典dict类型,包含det、rec、table、match、all五个属性,分别代表文本检测、文本识别、表格预测、表格匹配、全部耗时,用于分析模型性能瓶颈,单位为秒。
返回值的理解中,有一点值得关注,同样是返回边界框,cell_bbox属性用的8数值,boxes属性用的4数值。初看时以为,开源团队在边界框的理解上并没有统一,有时用左上角、右上角、右下角、左下角四顶点坐标,有时用左上角、右下角两顶点坐标。细细品味,其实两者的应用场景不同,四顶点坐标适用于所有边界框描述场景,包含矩形和非矩形;而两顶点坐标,只适用于矩形边界框描述场景。所以,TableSystem类描述单元格时使用的四顶点,描述文本时使用的两顶点。
实验一:获取文本结果
第一个实验的目标,是获取表格模型的返回值,通过日志的形式输出结果。主要代码如下:
args = construct_engine_params()
source_img = './img/table1.jpg'
img = cv2.imread(source_img)
ts = TableSystem(args)
result, time_dict = ts(img, return_ocr_result_in_table=True)
logger.debug(f"table system model executed,time elapse {
time_dict['all']:.3f}s,of which table={
time_dict['table']:.3f}s rec={
time_dict['rec']:.3f}s")
logger.debug(result['html'])
txt = [line[0] for line in result['rec_res']]
conf = [line[1] for line in result['rec_res']]
boxes = [b for b in result['boxes']]
for i in range(len(txt)):
logger.debug(f'extract text in bbox top&left:({
int(boxes[i][0])},{
int(boxes[i][1])}),get text:{
txt[i]} with confidence:{
conf[i]:.4f}')
目标图片与总论一致。执行后输出如下:
[2024/09/20 15:23:17] ppocr DEBUG: dt_boxes num : 78, elapse : 0.1508955955505371
[2024/09/20 15:23:19] ppocr DEBUG: rec_res num : 78, elapse : 1.687302827835083
[2024/09/20 15:23:19] ppocr DEBUG: table system model executed,time elapse 2.516s,of which table=0.522s rec=1.687s
[2024/09/20 15:23:19] ppocr DEBUG: <html><body><table><tbody><tr><td>Methods</td><td>R</td><td>P</td><td>F</td><td>FPS</td></tr><tr><td>SegLink[26]</td><td>70.0</td><td>86.0</td><td>77.0</td><td>8.9</td></tr><tr><td>PixelLink [4]</td><td>73.2</td><td>83.0</td><td>77.8</td><td></td></tr><tr><td>TextSnake[18]</td><td>73.9</td><td>83.2</td><td>78.3</td><td>1.1</td></tr><tr><td>TextField [37]</td><td>75.9</td><td>87.4</td><td>81.3</td><td>5.2</td></tr><tr><td>MSR[38]</td><td>76.7</td><td>87.4</td><td>81.7</td><td>-</td></tr><tr><td>FTSN [3]</td><td>77.1</td><td>87.6</td><td>82.0</td><td>-</td></tr><tr><td>LSE[30]</td><td>81.7</td><td>84.2</td><td>82.9</td><td>-</td></tr><tr><td>CRAFT [2]</td>