掘金 人工智能 07月26日 12:00
学习 RAGFlow 的 DeepDoc 技术之视觉处理
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入剖析了DeepDoc的视觉处理核心技术,包括光学字符识别(OCR)、文档布局识别(DLR)和表格结构识别(TSR)。文章详细介绍了OCR流程中的文本检测(DB算法)和文本识别(CTC算法),并展示了如何使用测试脚本实现这些功能。此外,还阐述了DLR如何识别文档中的标题、正文、图片、表格等组件,以及TSR如何解析表格的行、列、单元格等结构。通过对这些关键技术的解读,揭示了DeepDoc在PDF文档解析方面的强大能力,并提及了相关模型的轻量级特性和可定制性,为理解RAGFlow处理PDF文档提供了技术视角。

✨ **OCR技术详解**:DeepDoc的OCR能力通过文本检测(使用DB算法)和文本识别(基于CTC算法)实现。文本检测利用可微分二值化处理概率图,精确定位文本区域;文本识别则处理裁剪出的文本图像,将图像序列转换为文本序列,两者均通过ONNX Runtime高效运行,并具备预处理、模型推理和后处理的完整流程。

📄 **文档布局分析(DLR)**:DLR通过YOLOv10模型识别文档的多种布局组件,如标题、正文、图片、表格等,并用不同颜色的边框和标签进行标注。这有助于理解文档结构,区分文本、图表等信息,为后续的精细化处理奠定基础。

📊 **表格结构识别(TSR)**:TSR专注于识别表格内的行、列、标题和合并单元格等结构信息,将表格区域输出为更精细化的可视化图像和HTML格式。这对于从表格数据中提取信息、进行问答至关重要,直接影响RAG领域的问答效果。

⚙️ **模型轻量与定制**:文章指出,DeepDoc使用的OCR、DLR和TSR模型都非常轻量级,易于下载和部署。同时,官方还提供了针对法律文书、手册、论文等特定领域的定制化布局识别模型,增加了其在不同场景下的适用性。

我们之前已经学过,DeepDoc解析器(parser)视觉处理(vision) 两个部分组成。解析器提供了不同格式文档的通用解析方法,我们花了两天时间,对这 10 个解析器的源码做了深入分析;今天我们将学习 DeepDoc 的视觉处理部分,包括 光学字符识别(OCR,Optical Character Recognition)文档布局识别(DLR,Document Layout Recognition)表格结构识别(TSR,Table Structure Recognition) 等高级特性,可以对 PDF 文件实现更好的识别效果。

测试脚本

为了更直观的体验 OCR、DLR 和 TSR 的效果,DeepDoc 提供了两个测试脚本,位于 deepdoc/vision 目录下:

拿一个简单的 PDF 文档测试一下 OCR 功能:

$ python deepdoc/vision/t_ocr.py --inputs /path/to/demo.pdf

运行之后会在当前目录下生成一个 ./ocr_outputs 目录,里面包含了 OCR 的结果。每个页面生成两个文件:一个是 jpg 图片,对应文本检测结果,使用黑框将检测到的文本区域框了起来;另一个是 txt 文本,包含文本识别的结果:

表格页面的识别结果:

继续使用下面的命令测试一下 DLR 功能(注意后面的 --mode layout 参数,表示布局分析模式):

$ python deepdoc/vision/t_recognizer.py --inputs /path/to/demo.pdf --mode layout

--inputs 输入可以是 PDF 文件,也可以是图片文件,还可以是包含 PDF 或图片的目录。另外,它还有一个 --output_dir OUTPUT_DIR 参数,可以指定输出位置。

运行之后会在当前目录下生成一个 ./layout_outputs 目录,里面包含了 DLR 的结果。每个页面生成一个 jpg 图片,识别到区域使用不同颜色的框框了出来:

可以注意到框的左上角还打上了标签,比如 title 标题,text 正文。下面是表格页面的识别结果:

左上角的标签为 table 表格,表示模型成功识别出了表格区域。

最后将上面的表格区域抠出来,另存成一个张独立的图片,测试一下 TSR 功能(注意后面的 ``--mode tsr` 参数,表示表格结构识别模式):

$ python deepdoc/vision/t_recognizer.py --inputs /path/to/table.png --mode tsr

运行之后的结果同样保存在 ./layout_outputs 目录,里面有一个 jpg 图片,标识出了更加详细的表格区域:

上面绿色的框左上角标签为 table row 表示行,红色的框左上角标签为 table column 表示列。另外,该目录下还生成了一个 html 文件,以 HTML 形式展示了表格的结构:

<html><head><style>  ...</style></head><body><table><tr><th  >姓名</th><th  >学号</th><th  >年龄</th><th  >性别</th><th  >成绩</th></tr><tr><td  >张三</td><td  >001</td><td  >18</td><td  ></td><td  >100</td></tr><tr><td  >李四</td><td  >002</td><td  >19</td><td  ></td><td  >99</td></tr><tr><td  >王五</td><td  >003</td><td  >20</td><td  ></td><td  >98</td></tr></table></body></html>

文本检测

文本检测是 OCR 流水线的第一步,为后续的文本识别提供准确的文本区域定位。它的代码逻辑位于 deepdoc/vision/ocr.py 文件中,实现类 TextDetector 如下:

它的核心流程分为三个部分:预处理阶段,模型推理阶段,后处理阶段。

预处理阶段

data = transform(data, self.preprocess_op)

这里的 self.preprocess_op 有点意思,它是通过下面这个 create_operators() 函数创建:

def create_operators(op_param_list, global_config=None):  ops = []  for operator in op_param_list:    op_name = list(operator)[0]    param = {} if operator[op_name] is None else operator[op_name]    if global_config is not None:      param.update(global_config)    op = getattr(operators, op_name)(**param)    ops.append(op)  return ops

这个函数接受一个 JSON 数组,数组中的每个元素表示一个操作(这些操作定义在 deepdoc/vision/operators.py 文件中),操作的名称为 JSON 对象的键,操作的参数为 JSON 对象的值。比如下面这个 JSON 数组,表示了四个预处理操作:

pre_process_list = [{    # 第一步:图像缩放    # 将图像最大边长限制为 960 像素,确保输入尺寸适合模型处理    'DetResizeForTest': {        'limit_side_len': 960,        'limit_type': "max",    }}, {    # 第二步:图像归一化    # 将像素值缩放到 [0,1] 范围(也就是 'scale': '1./255.')    # 使用 ImageNet 预训练参数进行标准化(这里的 std 和 mean 这组参数并非随机设定,而是在 ImageNet 数据集上统计得到的)    'NormalizeImage': {        'std': [0.229, 0.224, 0.225],        'mean': [0.485, 0.456, 0.406],        'scale': '1./255.',        'order': 'hwc'    }}, {    # 第三步:通道转换    # 将图像的通道顺序从 HWC(高、宽、通道)转换为 CHW(通道、高、宽),符合模型的输入要求    'ToCHWImage': None}, {    # 第四步:数据筛选    # 只保留 'image' 和 'shape' 两个关键字段    'KeepKeys': {        'keep_keys': ['image', 'shape']    }}]

模型推理阶段

for i in range(100000):  try:    outputs = self.predictor.run(None, input_dict, self.run_options)    break  except Exception as e:    if i >= 3:      raise e    time.sleep(5)

调用 self.predictor.run() 进行推理,包含重试机制,最多重试 3 次。这里的 self.predictor 是通过 load_model() 函数加载的 det.onnx 文本检测模型:

self.predictor, self.run_options = load_model(model_dir, 'det', device_id)

它会根据机器上是否安装 CUDA 库来选择使用 GPU 还是 CPU 进行推理:

import onnxruntime as ortdef load_model(model_dir, nm, device_id: int | None = None):  # ...  run_options = ort.RunOptions()  if cuda_is_available():    cuda_provider_options = {      "device_id": device_id, # Use specific GPU      "gpu_mem_limit": 512 * 1024 * 1024, # Limit gpu memory      "arena_extend_strategy": "kNextPowerOfTwo",  # gpu memory allocation strategy    }    sess = ort.InferenceSession(      model_file_path,      options=options,      providers=['CUDAExecutionProvider'],      provider_options=[cuda_provider_options]      )    run_options.add_run_config_entry("memory.enable_memory_arena_shrinkage", "gpu:" + str(device_id))    logging.info(f"load_model {model_file_path} uses GPU")  else:    sess = ort.InferenceSession(      model_file_path,      options=options,      providers=['CPUExecutionProvider'])    run_options.add_run_config_entry("memory.enable_memory_arena_shrinkage", "cpu")    logging.info(f"load_model {model_file_path} uses CPU")  loaded_model = (sess, run_options)  return loaded_model

可以看出,模型推理使用的是 ONNX Runtime 库。

后处理阶段

post_result = self.postprocess_op({"maps": outputs[0]}, shape_list)dt_boxes = post_result[0]['points']dt_boxes = self.filter_tag_det_res(dt_boxes, ori_im.shape)

这里的 self.postprocess_op 和上面的预处理操作类似,也是通过一个 JSON 配置创建的:

postprocess_params = {  "name": "DBPostProcess",  # 二值化阈值,用于将概率图转换为二值图  "thresh": 0.3,  # 文本框置信度阈值,过滤低质量检测框  "box_thresh": 0.5,  # 最大候选框数量  "max_candidates": 1000,  # 文本框扩展比例,防止文本被裁切  "unclip_ratio": 1.5,  "use_dilation": False,  # 评分模式选择,支持 fast 和 slow 两种  "score_mode": "fast",  "box_type": "quad"}

这个后处理操作 DBPostProcess 定义在 deepdoc/vision/postprocess.py 文件中。DeepDoc 的文本检测模型使用了 DB(Differentiable Binarization)算法,最终输出文本区域的 概率图 (Probability Map),表示每个像素是文本的概率。因此后处理阶段的任务就是从概率图中提取出文本区域的坐标框。

DB(Differentiable Binarization)算法介绍

DB(Differentiable Binarization)算法即可微分二值化算法,由百度的 PaddleOCR 团队提出,主要用于场景文本检测领域。以下是具体介绍:

文本识别

文本检测之后,我们得到了图片中各个文本区域的坐标框,然后我们可以根据这些坐标,将文本区域从原图中裁剪出来,这些裁剪出来的子图,就可以丢给文本识别模型进行识别了。

文本识别的代码逻辑同样位于 deepdoc/vision/ocr.py 文件中,实现类 TextRecognizer 如下:

它接受的参数是子图列表,根据 batch_num 按批次进行识别,默认 16 张图一个批次。针对每个批次,它的核心流程也可以分为三个部分:

CTC(Connectionist Temporal Classification)算法介绍

CTC(Connectionist Temporal Classification)算法即联结主义时序分类算法,是一种常用于语音识别、文本识别等领域的算法,主要用于解决输入和输出序列长度不一、无法对齐的问题。以下是其详细介绍:

文档布局分析

来自不同领域的文档可能有不同的布局,如报纸、杂志、书籍和简历在布局方面往往是不同的。只有当机器有准确的布局分析时,它才能决定这些文本部分是连续的还是不连续的,或者这个部分需要使用表结构识别来处理,或者这个部分是一个配图并用这个标题来描述。DeepDoc 支持 10 个基本布局组件,涵盖了大多数情况:

布局分析的代码逻辑位于 deepdoc/vision/layout_recognizer.py 文件中,实现类 LayoutRecognizer4YOLOv10 如下:

从类名可以看出,它是一个基于 YOLOv10 模型的文档布局识别器,通过加载 layout.onnx 模型文件,对输入图片进行推理,得到检测到的布局区域坐标和类型。

表结构识别

表格是一种常用的结构,经常出现在各类文档中,它的结构可能非常复杂,比如层次结构标题、跨单元格和投影行标题等。在 RAG 领域,对表格的处理和理解,直接影响着对表格内数据的问答效果。我们一般先通过布局分析,定位到文档中的表格区域,然后将其裁剪出来,使用专门的表结构识别模型进行识别。DeepDoc 支持 5 中不同的表结构:

表结构识别的代码逻辑位于 deepdoc/vision/table_structure_recognizer.py 文件中,实现类 TableStructureRecognizer 如下:

它的调用流程和之前的模型基本一致,前处理,模型推理,后处理,这里使用的是 tsr.onnx 这个表结构识别模型,最后返回的是表格区域的坐标和类型。

小结

本文深入探讨了 DeepDoc 中的视觉处理部分,包括光学字符识别(OCR)、文档布局识别(DLR)和表格结构识别(TSR)的实现原理及技术细节。这几个类的实现都比较复杂,特别是调用每个模型的前处理和后处理部分,非常烧脑,有不少逻辑我也没看懂,但是这并不妨碍我们了解 RAGFlow 解析 PDF 文件的大致思路,权当是扩展下课外知识吧。

本文介绍的四个视觉模型都可以从 HuggingFace 下载

而且可以看到,这几个模型都很轻量级,最大的布局检测模型也就只有 75MB 而已。另外,官方还提供了几个领域定制的布局识别模型,比如 layout.laws.onnx 用于法律文书,layout.manual.onnx 用于手册,layout.paper.onnx 用于论文,感兴趣的朋友可以自己尝试下。

欢迎关注

如果这篇文章对您有所帮助,欢迎关注我的同名公众号:日习一技,每天学一点新技术

我会每天花一个小时,记录下我学习的点点滴滴。内容包括但不限于:

目标是让大家用5分钟读完就能有所收获,不需要太费劲,但却可以轻松获取一些干货。不管你是技术新手还是老鸟,欢迎给我提建议,如果有想学习的技术,也欢迎交流!

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

DeepDoc OCR DLR TSR AI
相关文章