You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Python机器学习的OCR开发:轮廓检测后的文本提取问题

解决从轮廓提取文本并训练自定义OCR模型的问题

嘿,你已经把OCR预处理的关键步骤都搞定了——灰度转换、Otsu阈值分割、轮廓检测框出文本区域,这已经成功了一半!接下来要解决的核心问题就是从这些轮廓里抠出文本区域,再用KNN/SVM/CNN来做字符识别对吧?我给你拆解成几个具体步骤,一步步来:

一、从轮廓中提取文本ROI(感兴趣区域)

你已经用轮廓检测得到了文本的矩形框,现在需要把每个矩形框对应的图像区域单独抠出来,这是后续训练和预测的基础。具体可以这么做:

  • 遍历每个检测到的轮廓,先过滤掉太小的轮廓(避免噪声干扰,比如单个像素的噪点),可以通过轮廓的面积或者矩形框的宽高来判断。
  • 获取轮廓的边界矩形:用cv2.boundingRect()得到每个轮廓的(x, y, w, h),也就是矩形的左上角坐标和宽高。
  • 裁剪ROI区域:从预处理后的二值图中截取这个矩形区域,代码示例如下:
import cv2
import numpy as np

# 假设你已经得到了二值化图像binary_img,以及轮廓列表contours
for cnt in contours:
    # 过滤小轮廓,比如面积小于50的跳过
    if cv2.contourArea(cnt) < 50:
        continue
    x, y, w, h = cv2.boundingRect(cnt)
    # 裁剪ROI
    roi = binary_img[y:y+h, x:x+w]
    # 统一尺寸(模型需要固定输入大小,这里以28x28为例)
    roi_resized = cv2.resize(roi, (28, 28), interpolation=cv2.INTER_AREA)
    # 反转颜色(根据你的文本/背景颜色调整,确保字符与背景对比度符合训练数据集要求)
    roi_resized = cv2.bitwise_not(roi_resized)

小提示:统一尺寸非常关键,因为机器学习模型需要固定大小的输入——KNN/SVM需要把图像转成一维向量,CNN需要固定的输入维度。

二、准备标注好的训练数据集

要训练自己的字符识别模型,你需要一套标注好的字符数据集:

  • 如果是识别数字或英文字母,可以直接用公开数据集,比如MNIST(数字)、EMNIST(字母+数字)。
  • 如果是中文或自定义字符,就得自己制作数据集:收集大量包含目标字符的图像,用上面的ROI提取方法抠出每个字符,然后手动标注对应的标签(比如字符'A'对应标签0,'B'对应1,以此类推)。
  • 把数据集分成训练集和测试集,比例建议7:3或8:2。

三、选择模型并训练

1. KNN(适合简单场景,快速上手)

KNN是最容易上手的机器学习模型,适合字符特征明显、变形较少的场景:

from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler

# 假设X_train是训练集的图像向量(每个28x28图像转成784维一维数组),y_train是对应标签
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 训练KNN模型
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train_scaled, y_train)

# 预测你的ROI
test_sample = roi_resized.flatten().reshape(1, -1)
test_sample_scaled = scaler.transform(test_sample)
predicted_label = knn.predict(test_sample_scaled)
# 根据提前建立的标签-字符映射字典,转成对应字符

2. SVM(适合中等复杂度的字符识别)

SVM在分类任务中稳定性很强,尤其是在特征维度不太高的时候:

from sklearn.svm import SVC

# 先对数据做标准化
svm = SVC(kernel='rbf', C=1.0)
svm.fit(X_train_scaled, y_train)

# 预测
predicted_label = svm.predict(test_sample_scaled)

3. CNN(适合复杂场景,比如手写体、变形字符)

如果字符存在变形、模糊等情况,CNN的表现会更出色,用TensorFlow/Keras实现很方便:

import tensorflow as tf
from tensorflow.keras import layers, models

# 构建简单的CNN模型
model = models.Sequential([
    layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
    layers.MaxPooling2D((2, 2)),
    layers.Conv2D(64, (3, 3), activation='relu'),
    layers.MaxPooling2D((2, 2)),
    layers.Flatten(),
    layers.Dense(64, activation='relu'),
    layers.Dense(10, activation='softmax') # 根据你的标签数量调整,这里以10个数字为例
])

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 训练模型,注意把图像转成(样本数, 28, 28, 1)的形状
model.fit(X_train.reshape(-1,28,28,1), y_train, epochs=10, validation_split=0.1)

# 预测
prediction = model.predict(roi_resized.reshape(1,28,28,1))
predicted_label = np.argmax(prediction)

四、额外注意事项

  • 轮廓排序:检测到的轮廓可能是乱序的,比如文本是从左到右,但轮廓顺序随机,所以需要对轮廓按照x坐标(从左到右)或y坐标(从上到下)排序,保证识别出的文本顺序正确。
  • 去噪优化:如果预处理后的图像还有噪声,可以用cv2.morphologyEx()做开运算或闭运算,去掉小噪点。
  • 标签映射:训练时要建立标签与字符的映射字典(比如label_map = {0:'0', 1:'1', ..., 25:'Z'}),这样预测出标签后能直接转成对应字符。

希望这些步骤能帮你顺利解决问题,动手试试吧!

内容的提问来源于stack exchange,提问作者Arshad Ahmad

火山引擎 最新活动