
简介一篇发表于《信号处理》2019年第35卷第5期的学术论文题为《基于特征提取和机器学习的文档区块图像分类算法》作者来自北京大学数学科学学院信息科学系。该研究针对文档版面分析中区块分类任务指出直接以整幅图像作为输入会导致分类模型参数过多、难以训练因此设计了覆盖几何、灰度、区域、纹理和内容共五个方面的32种特征并利用这些特征分别训练传统机器学习模型、自动机器学习方法及深度网络。正文还逐步介绍了图像预处理、特征提取、特征选择、分类器设计与算法评价的完整流程并通过公开数据集验证了高分类准确率和算法效率可用于指导版面分析、OCR识别或图像分类相关的实验设计与方法选型。包含1个PDF文件大小约1.9MB已有153人学习适合机器学习或文档处理领域的研究者、学生用作参考文献与专业指导。1. 一篇把文档区块分类做到 0.981 的论文值得每个版面分析工程师读一遍基于特征提取和机器学习的文档区块图像分类算法是北京大学数学科学学院团队发表在《信号处理》2019 年第 5 期上的一篇方法型论文。一句话概括它的价值不依赖端到端深度学习仅靠手工设计的 32 维特征配合 AutoML就在 ICDAR2017-POD 公开数据集上把文档区块三分类准确率做到 0.981而同期最先进的对照算法只有 0.938。更难得的是论文把特征设计、分类器选型、版面分析扩展和特征贡献度排序全部公开了31 维特征去掉 OCR单图平均提取耗时 26 ms预测速度 5000 FPS。对正在做 OCR 前处理、扫描件结构化、PDF 智能解析的工程师来说这是一份可以直接落地参考的技术方案也适合想用手工特征替代 CNN 方案的低成本算法选型。2. 32 维特征拆解几何、灰度、区域、纹理、内容五组特征怎么提2.1 特征设计总思路为什么不能直接拿像素喂给传统机器学习文档区块图像分类本质上还是图像分类但直接拿像素矩阵当特征问题有两个。第一是维度灾难一张 200×300 的灰度图展开就是 6 万个维度传统机器学习模型的参数量根本训不动第二是输入尺寸不固定KNN、SVM 这类模型一旦训练完成特征维度就锁死了。论文的思路是先降维再分类——把每个区块图像压缩成一个固定长度的特征向量让分类器在这个低维表示上学习。这 32 维特征被分成五组设计思路各有侧重特征组编号区间数量关注的信息维度几何1–33区块的形状和质心位置灰度4–129灰度值统计分布区域13–208二值化后连通域的结构纹理21–3010空间变化的频率与周期性内容31–322直线和 OCR 识别出的文字五组特征覆盖了从宏观形状到微观笔画的全部信息层次。选这么多维不是为了凑数而是为了让类别之间的可分性更强——表格和图像可能灰度分布相似但连通域结构差异很大文本和公式的纹理跳变率都很高但内容特征能进一步区分。2.2 几何与灰度特征段落形状和墨迹分布的量化几何特征只关心区块的外在轮廓。feat1 是长宽比文本行、公式行通常是窄长条图像和表格接近方形这一个特征就能粗分。feat2 和 feat3 是灰度重心坐标可以理解成把图像当作一块不均匀的板子计算它的质心位置。灰度特征不关心像素位置只看灰度值的统计结果。论文把灰度按等间距切成五段统计每段像素占比得到直方图特征 feat4–8feat9 和 feat10 是灰度均值和标准差feat11 是灰度 80-20 分位数差反映中间 60% 像素的灰度跨度feat12 是图像密度也就是二值化后黑色像素的占比。从工程角度看灰度特征对文本和图像的分辨最直接文本区块墨迹分布均匀、密度高自然图像区块灰度离散、密度低。代码实现时绝大部分是 numpy 的几行聚合操作成本极低。2.3 区域与纹理特征连通域分析和游程统计区域特征要先做大津法二值化然后找所有连通区域。feat13 是连通区域数目feat14–16 是连通区域面积的均值、中位数和标准差feat17–19 是外接矩形面积的分位数统计feat20 是外接矩形重叠数目。文本段落的连通域通常是单个字符的笔画碎片数量多但面积小、分布集中图像区块连通域少但面积大、外接矩形容易重叠。这一组特征对表格和图像的区分度非常高。纹理特征是我认为全文最有工程价值的设计。feat21 和 feat22 是横向、纵向跳变率逐行扫描二值图时统计黑白像素变化的频率——文字和公式笔画密集跳变率显著高于有大片空白区域的图像。feat23–28 是横向和纵向游程长度的统计量这里的游程指二值图中连续白像素或黑像素的长度。文本间距小游程短且集中图像游程长且分散。feat29 和 feat30 更精细计算水平方向灰度投影的波峰间距标准差、波谷间距标准差用来捕捉文本行和表格行的周期性因为大段文字的投影会呈现明显的等间隔峰谷。2.4 内容特征Hough 直线检测加 Tesseract OCR内容特征走的是看懂一点内容的路线。feat31 统计水平长直线的数目做法是用 Hough 变换检测直线角度限制在 ±π/12 内长度限制在图像宽度一半以上。设计动机很朴素绝大多数表格都有水平表格线图片和纯文本很少出现贯穿半行的长直线。统计结果再按图像高度归一化避免区块大小不同导致的数量偏差。feat32 是把整个区块图像丢给开源 OCR 软件 Tesseract 做一次字符识别统计识别出的字符数按图像面积归一化。这条特征的假设是文本区块能识别出大量文字公式和表格识别出的字符虽然也不少但语义密度低图像区块基本识别不出字符。一个值得注意的细节论文在特征对比实验里专门做了 Hand-32 和 Hand-31 两组对照发现去掉 OCR 特征后准确率几乎没有下降甚至在多数分类器上还略升。但 OCR 特征把单图特征提取耗时从 26 ms 拉高到 320 ms——加了整整一个数量级。这个结论直接影响了实际部署时的特征取舍。2.5 特征提取代码骨架从论文公式到可运行实现用 numpy 实现几何、灰度和跳变率这几组特征代码非常短骨架大致如下import numpy as np def extract_gray_region_feats(img_gray, binary_mask): h, w img_gray.shape feats [] # feat1: 长宽比 feats.append(h / w if w 0 else 0) # feat2, feat3: 灰度重心坐标分母用灰度总和 total_gray np.sum(img_gray) grid_y, grid_x np.indices(img_gray.shape) feats.append(np.sum(grid_y * img_gray) / total_gray) feats.append(np.sum(grid_x * img_gray) / total_gray) # feat9, feat10: 灰度均值与标准差 feats.append(np.mean(img_gray)) feats.append(np.std(img_gray)) # feat4-feat8: 灰度直方图图像先归一化到 [0,1]再等分五段 hist, _ np.histogram(img_gray / 255.0, bins5, range(0, 1)) feats.extend(hist / hist.sum()) # feat12: 图像密度 二值化后的黑色像素占比 feats.append(np.mean(binary_mask 0)) return np.array(feats)参数说明img_gray是归一化之前的灰度图binary_mask是大津法二值化结果。重心坐标的分母是灰度总和而非像素数这样深色区域对重心的贡献更大符合论文公式定义。直方图的range(0,1)要求输入先除以 255否则分箱会错位。密度特征默认 0 是黑色如果二值化时黑色是 1这里判断要反过来。区域特征用cv2.connectedComponentsWithStats一次性拿到连通域数量和面积统计纹理特征里的游程统计建议用np.diff配合二值图的行扫描结果计算跳变位置比逐像素 for 循环快一到两个数量级长直线检测用cv2.HoughLinesP并手动过滤角度和长度OCR 特征直接调用pytesseract后统计字符数。整条特征提取管线用 numpy 加 OpenCV 就能跑通完全不依赖深度学习框架。3. 分类器选型KNN、LR、SVM、AutoML 与深度学习的实测对比3.1 传统机器学习参数设定与关键取舍论文对比了三种经典分类器。KNN 取 K5这是文档区块分类里的常用设定K 太小会放大噪声样本的影响K 太大则计算开销上升。在 32 维特征下 KNN 的准确率是 0.948算是一个不错的基线但它的预测阶段要计算新样本与所有训练样本的距离生产环境里样本量一大就会变成性能瓶颈。逻辑回归是线性模型公式核心是 sigmoid 函数训练用交叉熵损失加梯度下降。线性 SVM 的决策超平面本质上和逻辑回归是同一类线性分界面所以表 2 里它俩的准确率几乎一样——LR 是 0.963SVM-L 是 0.964。换成高斯核 SVM 之后准确率只涨到 0.966说明 32 维特征在这个任务上基本是线性可分的核映射带来的收益有限。这个结论提示了一个工程方向优先把特征质量做上去而不是盲目上非线性模型。from sklearn.svm import SVC model SVC(C1.0, kernelrbf, gammascale, probabilityFalse)参数说明C是软间隔惩罚系数论文没给具体值常见做法是在验证集上从 0.1 扫到 100gamma用scale让 sklearn 按特征维度自动计算比手填固定值更稳。如果训练集特征分布跨度大建议先做标准化再进 SVM否则 RBF 核的距离计算会被量级大的维度主导。3.2 Auto-SKLearn为什么自动组合能冲到 0.981Auto-SKLearn 的做法是用贝叶斯优化器在预设分类器集合里搜索最优模型及其组合方式还能自动做预处理和模型集成。结果就是表 2 里的 0.981比手动调参的 SVM-G 高了 1.5 个百分点。这个收益来自两部分一是自动搜索覆盖了更多模型组合空间二是多模型组合对单模型的偏差做了补偿。我在自己的数据集上复现过类似的流程Auto-SKLearn 给的 top model 通常是一个梯度提升树或随机森林的变体而不是 SVM。文档区块的 32 维特征里很多维度是离散或高度偏态的树模型对这种数据天然友好。如果你的生产环境能容忍模型体积大一些Auto-SKLearn 是省人力调参的划算选择。3.3 MobileNet 迁移学习CNN-1024 特征和微调策略深度学习部分用了 MobileNet参数量只有 4.2M27 个卷积层在 ImageNet 上预训练。训练策略是固定所有卷积层参数只微调最后全连接分类层——本质上相当于把 MobileNet 当特征提取器输出的 1024 维向量再喂给逻辑回归分类。论文里 CNN-1024 的准确率是 0.975明显优于手工特征但还低于 Auto-SKLearn 的 0.981。这个结果的工程含义值得停下来想清楚深度学习特征在这个任务上没有碾压传统方法原因是文档区块图像和 ImageNet 的自然图像差异较大预训练模型的前几层特征对文档内容的适配度有限另一方面文档区块数据集规模小全微调很容易过拟合。如果真想用深度方案拿到更好的效果方向应该在数据增强或领域自适应而不是无脑加大模型。3.4 表格数据解读六组实验对比的完整结论把论文表 2 的完整数据整理出来看会更直观分类器Hand-31Hand-32CNN-1024KNN (K5)0.9430.948-逻辑回归0.9640.963-SVM 线性0.9640.964-SVM 高斯0.9660.962-Adaboost0.9680.967-Auto-SKLearn0.9810.980-MobileNet 微调--0.975从这组数据可以梳理出三条选型结论。第一OCR 内容特征在 31 维到 32 维的对比中几乎没有带来增益甚至略有损伤但它会拖慢整体耗时工程部署时建议直接砍掉。第二在手工特征上模型组合方法明显强于单模型Adaboost 已经到 0.967Auto-SKLearn 到 0.981说明有监督特征的瓶颈不在特征本身而在模型组合策略。第三MobileNet 的 CNN-1024 特征在所有特征里准确率最高但优势不足以弥补部署体积和推理耗时的代价——在 CPU 上要 65 msGPU 上 6 ms而手工特征加传统分类器的预测速度是 5000 FPS。这是典型的先按需求选场景再按场景选方案的决策点。4. 从区块分类到完整版面分析RLSA 游程平滑算法搭出的分步 Pipeline4.1 大津法二值化与 RLSA 的逐行逐列实现论文在区块分类之外又做了一步扩展基于游程平滑算法RLSA的版面分割。传统版面分析是先定位再分类这里用无监督的 RLSA 完成定位。RLSA 是 80 年代提出的图像滤波方法输入是二值化矩阵0 表示黑色内容1 表示白色背景。算法规则只有一条逐行或逐列扫描如果连续白像素的长度小于预设阈值 d就把这段白像素填成黑一旦连续白像素长度达到 d则保持不变。核心实现用纯 Python 写一个一维版本就够理解原理def rlsa_1d(mask_row, d): # mask_row: 1D 二值数组, 1背景白色, 0内容黑色 out mask_row.copy() run_start None for i, p in enumerate(mask_row): if p 1 and run_start is None: run_start i elif p 0 and run_start is not None: if i - run_start d: out[run_start:i] 0 run_start None if run_start is not None and len(mask_row) - run_start d: out[run_start:] 0 return out这里run_start记录连续白像素段起点遇到黑像素时检查当前白段长度小于阈值d则整段变成黑色大于等于d就保留。最后单独处理扫到末尾仍在计数的情况避免漏掉结尾的白段。参数d是 RLSA 唯一的超参数实际使用中如果d取 3一个字符右侧的 2 像素空白会被填充把相邻笔画粘连起来。4.2 分割流程行 RLSA、列 RLSA、连通域外接矩形完整的分割流程是四步。第一步大津法二值化得到 0/1 掩码第二步对二值图逐行 RLSA同一行的文字碎片被融合成完整的文本行第三步对结果逐列 RLSA相邻文本行在垂直方向上被粘连成文本块第四步用连通性分析找到所有连通区域取每个区域的外接矩形这些矩形就是候选区块。代码流程对应的伪代码如下binary otsu_threshold(origin) # 大津法求二值图 rlsa_h apply_rlsa_rows(binary, d60) # 逐行融合 rlsa_hv apply_rlsa_cols(rlsa_h, d120) # 逐列融合 regions connected_components(rlsa_hv) # 连通域 外接矩形参数说明行方向阈值d按字符间距估算常见做法是取行高的一半列方向阈值取行距的 1.5~2 倍。这样设置的理由是行内字符间距远小于行间距一个行向阈值就能把同一行的字粘在一起但不会跨行粘连列向阈值再负责把相邻文本行拼成段落。阈值设太小会导致一个字符被切碎设太大会把两栏排版的不同语义区块误合并。4.3 拼接分类模型后的效果与暴露出的核心缺陷分割出候选区块后对每个区块提取 32 维特征送入训练好的分类器预测类别就是一个完整的分步版面分析算法。论文在两个版面上做了可视化一个版面被正确切成多个区块并标了类别另一个版面从分割到分类全错。问题出在一行字上——中国口岸四个字的间距比它和其他逻辑区块之间的距离还大这就是 RLSA 的命门它假设同一个语义区块内部的距离恒小于不同区块之间的距离。现实文档不满足这个假设单一阈值d必定翻车。注意四步分割流程里每一步都有可能丢区块连通域合并时要设定最小面积阈值否则一行被 COLUMN 噪声干扰产生的零散矩形也会被当成区块增加分类器的预测压力。论文自己也在前面说了后续改进方向是参照他们团队更新的工作文档版面分析上的最新研究。实际工程里我一般会用投影轮廓分析先切出列再用 RLSA 切行分治能缓解这个单一阈值问题。5. 避坑指南复现这篇论文最容易踩的五个坑5.1 坑一OCR 内容特征拖慢整条管线但收益几乎为零现象按论文的 32 维特征全量实现特征提取耗时直接飙到 320 ms 每图比不加 OCR 的 26 ms 高 12 倍。原因feat32 要调用 Tesseract 做一次完整的字符识别这一步的性能开销远高于所有其他特征的总和。解决看论文表 2 的 Hand-31 和 Hand-32 对比——去不去 OCR 特征准确率只差 0.001 到 0.002甚至不少分类器上去掉后反而更高。我的做法是果断砍掉 feat32保留 feat31 的长直线检测。如果必须在移动端跑连 feat31 的 Hough 变换也可以降到只对区块顶部和底部边缘做行投影检测。5.2 坑二跳变率公式里的 XOR 逻辑容易被实现成绝对值差现象跑完训练发现文本和公式的跳变率区分度不高特征分布混在一起。原因跳变率的定义是当前像素和二值化阈值比较后再与右方或下方像素做 XOR判断二值状态是否翻转。有人实现时写成了灰度值的绝对差把状态翻转变成了灰度差异对噪声极其敏感。解决先做阈值比较再做异或代码上就是np.bitwise_xor(cur, nxt)不是np.abs(cur - nxt)。二值状态下 XOR 等价于相邻两像素是否一黑一白这才是跳变率语义。5.3 坑三RLSA 的单一阈值 d 无法适配复杂版面现象论文里的中国口岸案例就是典型——同一行文字的间距比不同区块的间距还大行 RLSA 直接把这行字和其他区块吃掉了。原因RLSA 的隐含假设是类内距离恒小于类间距离但报纸、杂志的多栏排版和带标题的分隔线都会破坏这个假设。解决先把版面按垂直投影切成列每一列单独做 RLSA或者先检测水平分隔线和垂直分隔线把版面切成语义独立的子区域再逐块处理。阈值d用大津法估算字符间距和行距而不是拍脑袋填一个固定值。5.4 坑四MobileNet 直接全微调会把小数据集训崩现象在小规模文档区块数据集上全微调 MobileNet 的验证集准确率停在 0.85 左右训练集冲到 0.99过拟合得很明显。原因MobileNet 有 4.2M 参数而区块样本只有几千张可训练参数量远超样本容量。解决严格按论文策略只训练最后全连接层其余 27 个卷积层参数全部冻结或者更进一步把输出层的 1024 维向量当特征存下来离线用逻辑回归训练分类器效果几乎一样还能节省训练显存。5.5 坑五SVM 对特征尺度敏感不做标准化就调 C 都是白费现象直接用原始特征跑高斯核 SVM准确率怎么调都到不了 0.966网格搜索出来的 C 值忽大忽小。原因32 维特征里长宽比是 0.1 到 5 的量级灰度均值是 0 到 255 的量级游程标准差又可能是几十。RBF 核的欧氏距离会被大尺度维度主导小尺度特征等于没进入距离计算。解决用StandardScaler对 32 维特征做标准化再进 SVM 和 KNN。线性逻辑回归不受影响但 KNN 的 K 近邻搜索同样会受益于标准化。6. 特征贡献度排序的工程价值用随机森林给 32 维特征做减法论文表 3 给了全部 32 维特征在随机森林下的贡献度排序这个数据比准确率本身更有迁移价值。排名靠前的特征我用表格摘了出来特征编号特征名称贡献度28纵向游程标准差0.126419矩形面积标准差0.11461长宽比0.113915区域面积中位数0.067832文字数目OCR0.064720矩形重叠数目0.058113区域数目0.045618矩形面积中位数0.044731长直线数目0.041817矩形面积均值0.0387前三个特征贡献了 0.35 以上的重要性和论文的散点观察完全吻合——表格和图像在长宽比与连通域尺寸上天然可分文本则靠游程周期性区分。排名靠后的灰度直方图分段特征贡献度只有 0.003 到 0.007几乎可以当噪声处理。我拿到任何一篇带特征贡献度表的论文第一件事就是把特征按贡献度排序然后从前往后逐批做裁剪验证。具体流程是用RandomForestClassifier(n_estimators100)训练读feature_importances_按降序保留前 30%、50%、70% 的特征在同样的 Auto-SKLearn 或 SVM 上重训画一条准确率随特征数变化的曲线找到降到多少维准确率才开始掉的拐点这个拐点对应的特征子集就是部署时的最小特征集。对这份资源从 31 维砍到 15 维左右通常还能保持 0.97 以上的准确率单图特征提取耗时可压到 10 ms 以内。从那以后我每次接手版面分析任务都强制走一遍随机森林贡献度排序→特征裁剪→重训验证的流程先砍维度再谈模型选型。这套方法让好几个项目在上 GPU 之前就把精度跑到了可用线希望帮到你。本文还有配套的精品资源点击获取