这是文档结构识别(Document Structure Analysis),判断当前页是否是目录页,需要结合文本特征 + 版面特征 + 统计特征进行综合判断。
整体流程:
1 | 扫描PDF |
其中XGBoost只负责处理“规则难以覆盖的边界情况”,而不是完全替代规则。
目录页的典型特征
典型的中文目录页:
1 | 目录 |
目录页通常具有以下特点:
含有目录标题
1 | 目录 |
存在大量“标题 + 页码”模式
1 | 第一章 XXXXX .......... 12 |
可以抽象为:
1 | ^(第[一二三四五六七八九十百]+[章节篇]|[0-9]+(\.[0-9]+)*|附录) |
匹配:
1 | 第一章 系统概述........1 |
点线密集
目录中常见:
1 | ................... |
统计点线数量:
1 | dot_count = text.count(".") |
如果:
1 | (dot_count + ellipsis_count) / 文本长度 > 0.05 |
则该页为目录页的概率较高。
页码密度高
正文一页一般只存在一个页码,而目录页:
1 | …… |
可能存在
1 | 20 行 |
定义:
1 | page_number_density = 页码数量 / 行数 |
判断
1 | 正文:<0.1 |
OCR后的判定流程
OCR结果:
1 | [ |
提取特征:
1 | features = { |
目录行识别
定义目录行:
1 | def is_toc_line(text): |
统计:
1 | toc_lines = sum( |
经验值:
1 | ratio > 0.3 |
高度怀疑目录页。
版面特征分析
目录通常具有:
1 | 左侧:标题 |
例如:
1 | 第一章 总则...............1 |
OCR坐标:
1 | { |
统计:
左侧文本聚类
1 | left_x_std < threshold |
右侧数字聚类
1 | right_x_std < threshold |
如果:
1 | 大部分数字都在页面右边缘 |
目录概率增加。
跨页连续目录判断
目录通常跨越多页:
1 | 目录 第1页 |
特征:
1 | 页码递增: |
且:
1 | 当前页和下一页 |
可连续扩展:
1 | while next_page.is_toc(): |
XGBoost目录页分类器
定义分类目标
二分类问题:
1 | 0 = 非目录页 |
训练样本:
1 | 目录页 300页 |
特征设计
- 目录标题特征
判断是否出现1
2
3
4目录
目 录
contents
table of contents - 目录行占比
目录行:计算目录行的占比:1
2第一章 总则..........1
第二章 架构..........81
toc_ratio = toc_line_count / line_count
- 点线密度
统计:的占比:1
2..........
………………1
dot_density = total_dot_count / total_character_count
- 数字密度
目录页存在很多页码数字,计算页码占比:1
digit_density = digit_count / character_count
- 右侧数字聚集度
目录页的页码数字集中在页面右侧,计算:数字越小,越像目录1
page_number_x_std
- 目录行长度方差
目录的长度较一致:而正文长度差异较大,计算:1
2
3第一章.........1
第二章.........15
第三章.........1001
line_length_std
- 标题比例
目录中经常出现各级标题:匹配:1
2
3
4
51
1.1
1.2
2
2.1统计标题占比:1
^\d+(\.\d+)*$
1
heading_ratio = heading_count / line_count
- 顶部空白比例
目录页第一页顶部通常较空:计算OCR坐标:1
2
3目录
第一章……最终获得8个特征:1
top_margin = first_text_y / page_height
1
2
3
4
5
6
7
8
9
10[
has_catalog,
toc_ratio,
dot_density,
digit_density,
page_number_x_std,
line_length_std,
heading_ratio,
top_margin
]XGBoost训练
最后更新: 2026年09月10日 11:10