当前位置: 首页 > news >正文

Tesseract OCR之基线拟合和单词检测

1. 基线拟合(Baseline Fitting)

目标

找到文本行的虚拟基准线(如英文中字母底部对齐的线),即使字符存在断裂、倾斜或噪声干扰。

(1) 行提取(Line Extraction)
  • 水平投影峰值检测
    对二值图像逐行计算黑色像素数,通过寻找局部最大值定位文本行粗位置。
    h_proj = np.sum(binary_img, axis=1)  # 假设文字为黑色(0),背景为白色(255)
    peaks = []
    for i in range(1, len(h_proj)-1):if h_proj[i] < h_proj[i-1] and h_proj[i] < h_proj[i+1]:peaks.append(i)  # 局部极小值(行间空白)
    
  • 聚类优化(解决波峰不清晰问题):
    使用DBSCAN对字符blob的纵坐标聚类,自动合并同一行的blob。
    from sklearn.cluster import DBSCAN
    y_centers = [y + h/2 for (x,y,w,h) in blobs]  # 所有blob的纵向中心
    clustering = DBSCAN(eps=5, min_samples=3).fit(np.array(y_centers).reshape(-1,1))
    line_labels = clustering.labels_  # 同一标签的blob属于同一行
    
    (2) 基线计算
  • 下边界中位数法
    对每行的所有字符blob,取其底部坐标(y_blob + height_blob)的中位数作为基线初始值。
    line_bottoms = [y + h for (x,y,w,h) in blobs if label == line_num]
    baseline_y = np.median(line_bottoms)  # 中位数抗噪声
    
  • 最小二乘法拟合直线
    若文本行有倾斜,用所有blob底部点拟合直线 y = kx + b
    bottoms = np.array([(x + w/2, y + h) for (x,y,w,h) in blobs])  # (x, y_bottom)
    A = np.vstack([bottoms[:,0], np.ones(len(bottoms))]).T
    k, b = np.linalg.lstsq(A, bottoms[:,1], rcond=None)[0]  # 解最小二乘
    

场景示例

假设有一行歪歪扭扭的手写英文(模拟真实OCR场景):

"hello world" 的实际二值图像:h   e   l   l   o       w   o   r   l   d
(每个字母的坐标和宽高如下表)
1. 字符Blob数据(模拟检测结果)
字母x (左侧)y (顶部)宽度 (w)高度 (h)底部 (y + h)
h10581217
e20771017
l29341417
l35641319
o4187917
w55291416
o66571217
r7596817
l83441418
d89671117

第一步:行提取(Line Extraction)

目标:确认哪些Blob属于同一行(本例只有一行,省略DBSCAN聚类)。


第二步:基线拟合(Baseline Fitting)

方法1:中位数法
  1. 取所有字母的底部坐标:
    [17, 17, 17, 19, 17, 16, 17, 17, 18, 17]
  2. 计算中位数:排序后第5/6个值是17基线y=17

问题:字母l的底部是19,导致基线偏高(不准确)。

方法2:最小二乘法拟合直线
  1. 计算每个字母的参考点(取blob底部中心):
    points = [(10 + 8/2, 17),  # h → (14,17)(20 + 7/2, 17),  # e → (23.5,17)(29 + 4/2, 17),  # l → (31,17)(35 + 4/2, 19),  # l → (37,19) ← 异常点(41 + 7/2, 17),  # o → (44.5,17)(55 + 9/2, 16),  # w → (59.5,16)... 
    ]
    
  2. 用最小二乘拟合直线 y = kx + b
    • 输入:所有(x, y_bottom)
    • 输出:假设拟合结果为 y = -0.02x + 17.8
      (一条轻微下斜的线,更贴合实际)

2. 单词检测(Word Segmentation)

核心问题

如何区分字符间间隙(同一单词)与单词间间隙?

解决方案
(1) 固定间距文本(如打字机字体)
  • 直接均匀切分:若字符宽度标准差 < 阈值(如平均宽度的10%),则按固定间隔切分。
    char_widths = [w for (x,y,w,h) in blobs]
    if np.std(char_widths) / np.mean(char_widths) < 0.1:words = np.split(blobs, len(blobs) // avg_char_width)
    
(2) 非固定间距文本(常见情况)
  • 归一化间隙计算
    对相邻blob,计算水平间隙与平均字符宽的比值:
    dnorm=xnext−(xprev+wprev)avg_width d_{\text{norm}} = \frac{x_{\text{next}} - (x_{\text{prev}} + w_{\text{prev}})}{\text{avg\_width}} dnorm=avg_widthxnext(xprev+wprev)

    def calc_gaps(blobs):blobs.sort(key=lambda b: b[0])  # 按x排序gaps = []for i in range(len(blobs)-1):gap = blobs[i+1][0] - (blobs[i][0] + blobs[i][2])gaps.append(gap / np.mean([b[2] for b in blobs]))return gaps
    
  • 动态阈值切分
    d_norm > threshold(通常1.3~2.0),视为单词分界:

    gaps = calc_gaps(blobs)
    word_boundaries = [i+1 for i, gap in enumerate(gaps) if gap > 1.5]
    words = np.split(blobs, word_boundaries)
    
  • 动态阈值选择技巧

  • 双峰法:对间隙分布直方图找两个峰(字符内间隙/单词间间隙),取谷底作为阈值。

场景示例

1. 计算相邻字母间隙
字母对前字母右边界 (x + w)后字母左边界 (x)间隙宽度
h-e10 + 8 = 18202
e-l20 + 7 = 27292
l-l29 + 4 = 33352
l-o35 + 4 = 39412
o-w41 + 7 = 48557
w-o55 + 9 = 64662
2. 归一化间隙宽度
  • 平均字符宽度:avg_width = (8+7+4+4+7+9+7+6+4+7)/10 ≈ 6.3
  • 归一化间隙:
    • o-w间隙 = 7 / 6.3 ≈ 1.11
    • 其他间隙 ≈ 2 / 6.3 ≈ 0.32
3. 动态阈值分割

假设设定阈值 >1.0 为单词间隔:

  • o-w间隙=1.11 > 1.0 → 在此处切分
  • 其他间隙均小于阈值 → 不切分

最终分割结果
[h e l l o][w o r l d] (成功分离两个单词)

http://www.dtcms.com/a/351838.html

相关文章:

  • 从0到1详解requests接口自动化测试
  • 遥感专业快速转行 GIS 开发的指南
  • esp32_hid_device 调试遇到的一些问题
  • Python爬虫实战:爬取链家/贝壳数据预测房价走势
  • 【大模型实战篇】基于开源视觉大模型封装多模态信息提取工具
  • 第1节: 微服务架构设计篇
  • WhisperLiveKit:实时语音转文本
  • 面试总结(1)
  • 2. LangChain4J 中的 Hello World
  • 未来几年哪些行业有潜力
  • ICBC_TDR_UShield2_Install.exe [ICBC UKEY]
  • ARP地址解析协议
  • 【AI算力平台】算力高效调度策略——GPU调度
  • mysql 执行sql流程概述
  • FreeRTOS学习笔记(四):任务执行与切换
  • ProfiNet 转 Ethernet/IP基于西门子 S7 - 1500 与罗克韦尔 PLC 的汽车零部件加工线协同案例
  • 基于微服务的水果分销系统-项目分享
  • LeetCode 3000.对角线最长的矩形的面积:一次遍历
  • 【golang长途旅行第32站】反射
  • 【机器学习深度学习】连续微调与权重合并的陷阱与最佳实践
  • 修改C盘缓存文件路径
  • MongoDB /redis/mysql 界面化的数据查看页面App
  • UCIE Specification详解(八)
  • 在MiniOB源码中学习使用Flex与Bison解析SQL语句-第一节
  • Rust 环境搭建与 SeekStorm 项目编译部署(支持中文)
  • Robrain V2.0正式登场:落地人形机器人,引爆智能进化革命
  • Ubuntu操作系统下使用mysql、mongodb、redis
  • [特殊字符] CentOS 7 升级 OpenSSH 10.0p2 完整教程(含 Telnet 备份)
  • 如果 我退休了
  • 汽车域控中Hypervisor方案极致安全原理与弊端