You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现可变长度多字符图像识别的置信度输出(Keras/Python)

可变长度多字符图像识别实现方案(带单字符置信度)

针对你需要识别可变长度字符图像、输出每个字符置信度的需求,结合你已有的单字符Keras模型,提供两种可行方案:

方案一:字符分割+复用单字符模型

这是成本最低的方案,直接基于你已有的成果扩展,无需重新训练大模型。

核心步骤

  • 字符定位与分割:用OpenCV对多字符图像做预处理(灰度化、二值化),通过轮廓检测定位每个字符的位置,裁剪出单个字符的图像。注意按字符的x坐标排序,保证输出顺序正确。
  • 单字符置信度预测:将每个裁剪后的单字符图像输入已训练好的模型,直接获取该位置对应100种候选字符的置信度数组。
  • 结果整理:按字符位置顺序,拼接预测字符串,并同步输出每个字符的全候选置信度。

代码示例

import cv2
import numpy as np
from keras.models import load_model

# 加载已训练的单字符识别模型
char_model = load_model('single_char_model.h5')
# 100种候选字符列表,顺序需与模型输出维度对应
char_list = ['char_01', 'char_02', ..., 'char_100']

def recognize_multi_char(image_path):
    # 读取并预处理图像
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 二值化(反转背景为黑色,字符为白色)
    _, binary_img = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
    
    # 检测字符轮廓并按x轴排序
    contours, _ = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = sorted(contours, key=lambda c: cv2.boundingRect(c)[0])
    
    char_results = []
    for cnt in contours:
        x, y, w, h = cv2.boundingRect(cnt)
        # 裁剪字符区域并调整为模型输入尺寸(假设模型输入为32x32单通道)
        char_crop = binary_img[y:y+h, x:x+w]
        char_crop = cv2.resize(char_crop, (32, 32))
        char_crop = np.expand_dims(char_crop, axis=[0, -1])  # 添加batch和通道维度
        
        # 获取该字符的所有候选置信度
        confidences = char_model.predict(char_crop, verbose=0)[0]
        # 取置信度最高的字符作为预测结果
        pred_char = char_list[np.argmax(confidences)]
        # 整理为字符+置信度字典
        char_results.append({
            'character': pred_char,
            'confidences': {char: float(conf) for char, conf in zip(char_list, confidences)}
        })
    
    # 拼接最终预测字符串
    predicted_string = ''.join([res['character'] for res in char_results])
    return {
        'predicted_string': predicted_string,
        'character_details': char_results
    }

# 调用示例
result = recognize_multi_char('test_multi_char.png')
print(f"预测字符串:{result['predicted_string']}")
print("每个字符置信度详情:", result['character_details'])

注意事项

  • 分割效果直接影响最终结果,若存在字符粘连(如手写体、紧密印刷体),需调整轮廓检测参数或加入形态学操作(如膨胀/腐蚀)优化分割。

方案二:端到端CTC模型(Keras实现)

若字符分割难度大,可采用CTC(连接主义时序分类)模型,无需手动分割字符,直接从多字符图像输出可变长度结果,同时能获取每个时间步的置信度。

核心思路

  • 用CNN提取图像特征,将特征图转为序列输入RNN(LSTM/GRU)处理时序信息,最后通过CTC层实现可变长度标签的训练与解码。
  • 预测时,模型输出的每个时间步对应一个字符位置的100种候选字符置信度(含CTC所需的空白符),解码后即可得到最终字符串。

代码示例(简化版)

from keras.models import Model
from keras.layers import Input, Conv2D, MaxPooling2D, Reshape, LSTM, Dense, Lambda
import keras.backend as K
import numpy as np

# 模型参数
IMG_WIDTH = 128
IMG_HEIGHT = 32
NUM_CLASSES = 100  # 100种候选字符,CTC需额外加1个空白符
char_list = ['char_01', 'char_02', ..., 'char_100']

# 定义CTC损失函数
def ctc_loss_func(args):
    y_pred, labels, input_len, label_len = args
    return K.ctc_batch_cost(labels, y_pred, input_len, label_len)

# 构建训练模型
input_img = Input(shape=(IMG_WIDTH, IMG_HEIGHT, 1), name='input_image')

# CNN特征提取层
x = Conv2D(32, (3, 3), activation='relu', padding='same')(input_img)
x = MaxPooling2D((2, 2))(x)
x = Conv2D(64, (3, 3), activation='relu', padding='same')(x)
x = MaxPooling2D((2, 2))(x)
x = Conv2D(128, (3, 3), activation='relu', padding='same')(x)
x = MaxPooling2D((2, 2))(x)

# 将特征图转为RNN输入序列
feature_shape = x.get_shape()
x = Reshape(target_shape=(feature_shape[1], feature_shape[2] * feature_shape[3]))(x)

# RNN序列处理层
x = LSTM(128, return_sequences=True)(x)
x = LSTM(128, return_sequences=True)(x)

# 输出层:每个时间步对应NUM_CLASSES+1种概率(含空白符)
y_pred = Dense(NUM_CLASSES + 1, activation='softmax', name='output')(x)

# CTC训练所需的额外输入
labels = Input(name='labels', shape=(None,))
input_length = Input(name='input_length', shape=(1,))
label_length = Input(name='label_length', shape=(1,))

# 计算CTC损失
ctc_loss = Lambda(ctc_loss_func, output_shape=(1,), name='ctc_loss')([y_pred, labels, input_length, label_length])

# 编译训练模型
train_model = Model(inputs=[input_img, labels, input_length, label_length], outputs=ctc_loss)
train_model.compile(loss={'ctc_loss': lambda y_true, y_pred: y_pred}, optimizer='adam')

# 构建预测模型(用于推理)
pred_model = Model(inputs=input_img, outputs=y_pred)

# 解码函数:从模型输出得到字符串和置信度
def ctc_decode_predictions(pred):
    # 输入长度(每个样本的时间步数)
    input_len = np.array([pred.shape[1]] * pred.shape[0])
    # CTC贪心解码
    decoded = K.ctc_decode(pred, input_length=input_len, greedy=True)[0][0]
    decoded_indices = K.get_value(decoded)[0]  # 取第一个样本的解码结果
    
    # 转换为字符(排除空白符,空白符索引为NUM_CLASSES)
    predicted_string = ''.join([char_list[idx] for idx in decoded_indices if idx != NUM_CLASSES])
    
    # 获取每个时间步的置信度(第一个样本)
    step_confidences = pred[0]
    # 整理为每个时间步的字符置信度字典
    confidence_details = []
    for step_conf in step_confidences:
        conf_dict = {char: float(step_conf[i]) for i, char in enumerate(char_list)}
        conf_dict['blank'] = float(step_conf[NUM_CLASSES])
        confidence_details.append(conf_dict)
    
    return predicted_string, confidence_details

# 预测示例
# 假设img是预处理后的多字符图像,形状为(1, IMG_WIDTH, IMG_HEIGHT, 1)
pred_output = pred_model.predict(img, verbose=0)
pred_str, conf_details = ctc_decode_predictions(pred_output)
print(f"预测字符串:{pred_str}")
print("每个位置置信度详情:", conf_details)

注意事项

  • 需要准备带标签的多字符图像数据集,标签为对应字符串,训练时需将标签转换为字符索引序列,并传入输入长度、标签长度参数。
  • 若样本字符长度差异大,可采用动态批处理或固定图像宽度(按最长字符宽度填充)。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 12:31:08