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

Tesseract OCR之单词识别与字符分类器

一、单词识别(Word Segmentation)

目标

将连续文本行分割成独立单词,处理两种场景:

  1. 固定间距文本(如打印机输出),字符宽度微小波动导致误切分
  2. 非固定间距文本(如手写或复杂排版),粘连字符与单词间距难以区分
解决方案与实例
(1) 固定间距文本处理

核心方法:基于字符宽度的统计学特征

def segment_fixed_pacing(blobs, width_var_threshold=0.1):char_widths = [b[2] for b in blobs]  # 提取所有blob宽度avg_width = np.mean(char_widths)# 判断是否是固定间距if np.std(char_widths)/avg_width < width_var_threshold:# 均匀切分split_positions = [int((i+1)*avg_width) for i in range(len(blobs)-1)]return np.split(blobs, split_positions)else:return None  # 交给非固定间距处理

案例
输入:打印机输出的单词"OCR",字母宽度均为8px(轻微误差±1px)

  • 计算平均宽度=8.1px,标准差=0.3 → 判定为固定间距
  • 切分位置:[8, 16] → 完美分割["O", "C", "R"]
(2) 非固定间距文本处理

核心方法:间隙分析与语言模型校验

def segment_variable_pacing(blobs, lang_model, gap_threshold=1.3):blobs.sort(key=lambda x: x[0])  # 按x坐标排序gaps = []for i in range(len(blobs)-1):gap = blobs[i+1][0] - (blobs[i][0] + blobs[i][2])normalized_gap = gap / np.mean([b[2] for b in blobs])gaps.append(normalized_gap)# 动态阈值选择if bimodal_test(gaps):  # 检查间隙分布是否双峰threshold = find_valley(gaps)  # 找双峰间谷底else:threshold = gap_threshold# 执行切分split_indices = [i+1 for i, g in enumerate(gaps) if g > threshold]words = np.split(blobs, split_indices)# 语言模型校验valid_words = []for word in words:candidate = "".join([recognize_char(b) for b in word])if candidate in lang_model:valid_words.append(word)elif len(word)>1:  # 尝试拆分粘连字符split_blobs = split_merged_chars(word[0])valid_words.extend(split_blobs)return valid_words

案例
输入:手写单词"hello world",非均匀间隙

  • 计算归一化间隙序列:[0.3, 0.4, 0.2, 1.5, 0.3...]
  • 动态阈值检测到1.5为分界点 → 在第五个间隙切分
  • 语言模型验证:["hello", "world"] 有效

(3)单词分割的数学公式

切分决策=I(dgapwˉ>τ)+λ⋅I(word∈D)\text{切分决策} = \mathbb{I}\left( \frac{d_{\text{gap}}}{\bar{w}} > \tau \right) + \lambda \cdot \mathbb{I}(\text{word} \in \mathcal{D}) 切分决策=I(wˉdgap>τ)+λI(wordD)

拆解说明
  • 符号解释

    • I(⋅)\mathbb{I}(\cdot)I() :指示函数(条件成立时=1,否则=0)
    • dgapd_{\text{gap}}dgap:相邻字符间的实际间隙宽度
    • KaTeX parse error: Can't use function '\)' in math mode at position 9: \bar{w} \̲)̲:当前文本的平均字符宽度
    • τ\tauτ:动态阈值(通常1.3~2.0)
    • D\mathcal{D}D:词典集合
    • λ\lambdaλ:语言模型权重(如0.5)
  • 逻辑解读

    • 前半部分:如果归一化间隙 > 阈值,则指示函数输出1(建议切分)
      if (当前间隙 / 平均字符宽度) > 1.5:在此处切分  # 例如"hello|world"中的"o"和"w"之间
      
    • 后半部分:如果候选单词在词典中存在,则指示函数输出1(反对切分)
      if "helloworld" in 词典:取消切分  # 认为是一个整体单词
      
  • 整体意义
    最终切分决策是几何间隙特征语言规则的加权和。例如:

    • 当结果为1.2(>1)时切分
    • 当结果为0.8(≤1)时不切分
类比理解

假设你在阅读模糊的手写纸条:

  • 间隙分析:发现"coffee"和"time"之间空隙较大 → 可能分开
  • 词典校验:但发现"coffeetime"是一个合法单词(如品牌名)→ 最终不切分

二、字符分类器(Character Classifier)

目标

将分割后的字符Blob准确分类为具体字符类别

存在问题
  • 字体变形导致传统模板匹配失效
  • 笔画断裂或粘连影响特征提取
解决方案与实例

(1) 多边形轮廓特征提取

几何特征工程

  1. Douglas-Peucker算法简化轮廓
  2. 提取4维边特征:[Δx, Δy, length, angle]
def extract_polygon_features(blob_image):contour = find_contours(blob_image)[0]epsilon = 0.02 * cv2.arcLength(contour, True)polygon = cv2.approxPolyDP(contour, epsilon, True)features = []for i in range(len(polygon)):p1 = polygon[i][0]p2 = polygon[(i+1)%len(polygon)][0]dx = p2[0] - p1[0]dy = p2[1] - p1[1]length = np.hypot(dx, dy)angle = np.arctan2(dy, dx)features.append([dx, dy, length, angle])return np.array(features)

案例
字母"A"的轮廓 → 简化为5条边:

  • 顶边:[0, -15, 15, -π/2]
  • 右下边:[10, 10, 14.14, π/4]
(2) 原型聚类与匹配

训练阶段

  • 对10万+样本提取特征
  • K-means聚类生成500个原型特征(视觉词典)

分类阶段

class CharClassifier:def __init__(self, prototypes, labels):self.tree = KDTree(prototypes)  # 加速最近邻搜索self.labels = labels  # 每个原型对应的字符def predict(self, blob):features = extract_polygon_features(blob)distances, indices = self.tree.query(features, k=1)voted_chars = [self.labels[i] for i in indices]return max(set(voted_chars), key=voted_chars.count)

案例
输入:手写"a"的Blob

  • 匹配到最近3个原型:[('a',0.8), ('o',0.15), ('d',0.05)]
  • 投票结果:'a'
(3) 动态分段优化

处理笔画断裂:

def adaptive_segment(features, min_len=5):total_len = sum(f[2] for f in features)seg_count = max(3, int(total_len / min_len))  # 至少分3段return np.array_split(features, seg_count)

案例
断裂的"B" → 原始7段特征 → 动态重分为12段 → 提升与原型"B"的匹配度


(4). 字符分类的数学公式

P(c∣B)∝exp⁡(−1n∑i=1n∥fi−ϕc,i∥2)P(c|B) \propto \exp\left( -\frac{1}{n}\sum_{i=1}^n \|f_i - \phi_{c,i}\|^2 \right) P(cB)exp(n1i=1nfiϕc,i2)

拆解说明
  • 符号解释

    • P(c∣B)P(c|B)P(cB):给定Blob ( B ) 时属于字符类别 ( c ) 的概率
    • fif_ifi:Blob的第 ( i ) 个轮廓特征(4维向量)
    • ϕc,i\phi_{c,i}ϕc,i:字符 ( c ) 的第 ( i ) 个原型特征(聚类中心)
    • nnn:Blob被分成的特征段数
  • 计算步骤

    1. 将Blob轮廓分成若干段,每段提取4维特征 fif_ifi
      (例如:第一段的[Δx=5, Δy=0, length=5, angle=0]
    2. 计算该特征与字符A的所有原型特征的欧氏距离
      distance = np.sqrt((5-3)**2 + (0-1)**2 + (5-4)**2 + (0-0.2)**2)
      
    3. 对所有特征段取距离平均值,再通过指数函数转化为概率
      (距离越小 → 负值越大 → 指数结果越大 → 概率越高)
类比理解

比对接头暗号:

  • 你手持一段断裂的密码条(Blob)
  • 与密码本(原型库)中的"代号A"模板逐段比对:
    • 完全匹配 → ( |f_i-\phi_{c,i}|=0 ) → 概率最高
    • 部分匹配 → 距离值中等 → 概率中等
    • 完全不匹配 → 距离值大 → 概率接近0
图示案例

假设识别字母"A"

Blob轮廓特征:   [3,1,4,0.2]  ← 第一段特征
原型库中"A"的特征: [3,1,4,0.2]  ← 匹配!距离=0[2,1,5,0.3]  ← 距离=1.1[4,0,4,0.1]  ← 距离=1.02
平均距离 = (0 + 1.1 + 1.02)/3 ≈ 0.71
概率 = e^(-0.71) ≈ 0.49  → 较高概率

三、协同工作机制案例

场景:识别倾斜手写文本"Cat"

  1. 单词分割

    • 间隙分析发现Ca间距为1.2倍均宽 → 不切分
    • at间距1.8倍 → 疑似单词边界
    • 语言模型验证"Cat"存在,否决切分
  2. 字符分类

    • C的轮廓匹配原型:[('C',0.7), ('G',0.2)]
    • a的断裂下半部分 → 动态分段后匹配度提升至0.9
    • 最终输出:"Cat"

四、关键技术创新点

环节传统方法Tesseract方案优势
单词分割固定阈值切分动态间隙分析+语言模型校验适应印刷/手写混合排版
特征提取网格像素特征自适应多边形轮廓+几何特征抗缩放旋转变形
分类决策最近邻模板匹配原型聚类+分段投票机制处理笔画断裂/粘连


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

相关文章:

  • Docker:部署Redis
  • 常见flex布局思路:flex布局上下结构
  • 2025中国生物制造科技创新论坛为何“花落”常德?
  • 新源布料厂进销存管理系统-项目分享
  • week5-[字符数组]查找
  • 木马免杀工具使用
  • 智汇云舟:视频孪生技术引领行业变革的场景应用实践
  • 第二十三天-FSMC简介
  • 技术速递|Model Context Protocol (MCP) 支持已上线 JetBrains、Eclipse 和 Xcode
  • 计算机网络:天气预报
  • SpringBoot + Redisson 实现分布式锁实战(附业务案例)
  • 【系统架构设计(一)】系统工程与信息系统基础上:系统工程基础概念
  • 29.RNN-循环神经网络
  • 微信小游戏订阅功能
  • 【SQL】深入理解MySQL存储过程:MySQL流程控制语句详解
  • SQL server 触发器的使用
  • PostgreSQL诊断系列(4/6):表空间与膨胀分析——解决“越用越大”的存储难题
  • woocommerce后台一次搜索多个ID订单的实现方法
  • 两周年创作纪念,忆笑傲江湖岁月
  • 探寻跨语言统一真理及其对NLP的未来启示
  • 项目管理软件与 Excel:哪个适合您的团队?
  • 超越MySQL:TDengine的时序数据处理革新与实践指南
  • [新启航]新启航激光频率梳 “光量子透视”:2μm 精度破除遮挡,完成 130mm 深孔 3D 建模
  • 在线提取维基百科Wikipedia文章页面及离线批处理Wikipedia XML Dump文件
  • 抽签占卜抖音快手微信小程序看广告流量主开源
  • 6.6 Element UI 加载指示器
  • 机器学习每日一题000-矩阵和向量的乘法python实现
  • Linux SSH 基于密钥交换的自动登录原理简介及配置说明
  • 数据结构第7章 查找(竟成)
  • 在 OpenLayers 中实现自定义右键菜单:基于 vue3-context-menu 的完整指南