4、文本检测技术(上):传统方法(MSER、SWT)、基于连通域的文本定位、我在项目中踩过的坑
各位同学,今天咱们聊聊文本检测里的传统方法。说实话,现在深度学习大行其道,很多人觉得传统方法过时了。但我个人习惯是,不管新方法多牛,老底子功夫不能丢。你想想看,很多实际场景下,传统方法反而更稳、更快。
4.1 文本检测的本质是什么?
说白了,文本检测就是在一张图里找到「哪里写着字」。深度学习之前,大家主要靠图像特征来猜。嗯,这里要注意,传统方法的核心思路就两条:找连通域 和 找笔画规律。
我刚开始做OCR那会儿,接了个身份证识别的项目。那时候哪有GPU啊,全靠CPU硬扛。用传统方法跑一遍,虽然慢点,但效果其实还行。后来我才明白,传统方法不是不行,是看你怎么用。
4.2 MSER:最大稳定极值区域
MSER 全称是 Maximally Stable Extremal Regions。名字挺唬人,其实原理很简单——找那些在不同阈值下形状都稳定的区域。
怎么理解呢?你想象一下,把一张灰度图从黑到白不断调阈值。有些区域(比如文字)会在很宽的阈值范围内保持形状不变。这些区域就是 MSER。
MSER 的核心步骤:
- 对图像进行灰度化处理
- 从0到255遍历所有阈值
- 记录每个连通区域在不同阈值下的面积变化
- 面积变化率最小的区域,就是「最大稳定极值区域」
我在项目中遇到过一个问题:MSER 对光照不均匀特别敏感。有一次做户外广告牌识别,阳光从侧面照过来,文字一半亮一半暗。MSER 直接崩了,把亮的部分和暗的部分当成两个区域。
后来怎么解决的?我加了一步预处理——先做高斯模糊,再用局部自适应阈值。效果立竿见影。
MSER 的优缺点
| 优点 | 缺点 |
|---|---|
| 对旋转、缩放不敏感 | 对光照变化敏感 |
| 能检测多尺度文字 | 容易检测到非文字区域(如纹理) |
| 算法成熟,OpenCV 直接调用 | 速度较慢,尤其大图 |
小技巧:用 MSER 时,建议把检测到的区域做一次「宽高比过滤」。文字一般不是正方形,也不是细长条。我一般保留宽高比在 0.2 到 5 之间的区域。
4.3 SWT:笔画宽度变换
SWT 是另一个经典方法。它的思路很巧妙——文字笔画通常有近似一致的宽度。
你想想看,英文字母的横竖笔画,宽度是不是差不多?中文虽然复杂点,但大部分字的笔画宽度也相对均匀。SWT 就是利用这个特点来区分文字和非文字。
具体怎么做呢?
- 先用 Canny 边缘检测找到所有边缘
- 对每个边缘点,沿着梯度方向找对应的另一个边缘点
- 计算这两个点之间的距离,就是笔画宽度
- 把笔画宽度相近的像素归为一类
// 伪代码示意
for each edge pixel p:
ray = p + n * gradient_direction(p)
while ray not hit another edge:
ray += step
if ray hit edge q:
width = distance(p, q)
if width < max_width:
mark all pixels on ray as text
嗯,这里要注意,SWT 对参数很敏感。我记得有一次做车牌识别,SWT 死活检测不到汉字。折腾了半天,发现是 Canny 的阈值设得太高了,汉字笔画太细直接被滤掉了。
我曾经踩过的坑:SWT 对背景复杂的图像效果很差。比如树叶、砖墙这种纹理丰富的场景,SWT 会检测出大量假阳性。我的建议是:先用 MSER 粗筛,再用 SWT 精筛,两者结合效果更好。
4.4 基于连通域的文本定位
不管是 MSER 还是 SWT,最终得到的都是一堆「候选区域」。接下来要做的,就是把这些区域合并成真正的文本行。
基于连通域的方法,说白了就是把相邻的、特征相似的区域连起来。
常用的特征包括:
- 颜色相似度:文字颜色通常一致
- 距离:相邻文字间距不会太大
- 大小比例:同一行文字大小相近
- 基线对齐:文字底部基本在一条线上
我一般用「最小生成树」来做区域合并。把每个候选区域当成一个节点,区域之间的相似度作为边的权重。然后不断合并权重最小的边,直到形成完整的文本行。
合并策略示例:
1. 计算所有候选区域的中心点
2. 对每个区域,找到最近的3个邻居
3. 如果邻居满足:颜色差 < 30,距离 < 3倍区域宽度
4. 则合并这两个区域
5. 重复直到没有新的合并
4.5 我在项目中踩过的坑
做传统文本检测,坑真的不少。我挑几个印象深刻的说说。
坑一:MSER 把背景纹理当成文字
有一次做古籍数字化,书页上的水渍被 MSER 检测成了一大片「文字」。我一开始还纳闷,怎么检测出这么多区域?后来发现是水渍的灰度值变化很慢,恰好符合「稳定区域」的定义。
解决办法:加一个「凸度」过滤。文字通常是凸的,水渍形状不规则,凸度很低。
坑二:SWT 对中文不友好
中文笔画复杂,横竖撇捺的宽度差异很大。SWT 默认假设笔画宽度一致,结果中文检测效果很差。我后来改了一下算法——对每个方向分别计算笔画宽度,横笔画用一个宽度,竖笔画用另一个宽度。效果好了不少。
坑三:光照变化导致漏检
这个前面提过。我的经验是:不要只用一种方法。MSER 对光照敏感,但 SWT 对光照不敏感。两者互补,能覆盖大部分场景。
我的建议:如果你刚接触文本检测,先别急着上深度学习。用 MSER + SWT + 连通域合并,搭一个简单的 pipeline。跑通之后,你就能深刻理解文本检测的难点在哪里。这时候再学深度学习,你会知道它解决了什么问题,又带来了什么新问题。
4.6 小结
传统方法虽然老了,但思路值得学习。MSER 教我们「找稳定的区域」,SWT 教我们「利用笔画特征」,连通域合并教我们「如何把碎片拼成整体」。这些思想,放到今天的深度学习方法里依然适用。
下一节,我会讲深度学习时代的文本检测方法。到时候你会发现,很多新方法其实是在传统方法的基础上「升级」的。底层的逻辑,没变。
好了,今天就到这儿。有问题欢迎留言讨论。