OCR文字检测模型cv_resnet18_ocr-detection:训练微调与ONNX导出教程
cv_resnet18_ocr-detection OCR文字检测模型训练微调与ONNX导出教程1. 引言当你需要从图片中提取文字时比如把一张商品标签变成可编辑的文本或者把一份扫描的合同变成电子版你会怎么做手动输入显然不现实这时候就需要OCR光学字符识别技术来帮忙。今天要介绍的这个工具cv_resnet18_ocr-detection就是一个专门用来检测图片中文字位置的模型。它就像一个智能的“文字定位器”能快速准确地找出图片里所有文字区域的位置。更棒的是这个模型不仅开箱即用还允许你用自己的数据来训练它让它变得更懂你的业务场景最后还能导出成通用的ONNX格式方便你在各种平台上使用。这篇教程将手把手带你完成三件事如何用你自己的数据训练这个模型、如何把它导出成ONNX格式、以及如何在实际项目中使用它。无论你是想处理特定行业的文档还是想把模型部署到移动端或云端这篇文章都能给你清晰的指引。2. 环境准备与快速启动2.1 启动WebUI服务这个模型提供了一个非常友好的Web界面所有操作都可以在浏览器里完成。启动服务只需要两步首先进入项目目录cd /root/cv_resnet18_ocr-detection然后运行启动脚本bash start_app.sh看到下面这个提示就说明服务启动成功了 WebUI 服务地址: http://0.0.0.0:7860 现在打开你的浏览器输入http://你的服务器IP:7860就能看到操作界面了。2.2 界面概览这个Web界面设计得很直观主要分为四个功能区域单图检测上传一张图片立即看到文字检测结果批量检测一次上传多张图片批量处理提高效率训练微调用你自己的数据训练模型让它更适应你的需求ONNX导出把训练好的模型导出成通用格式方便部署界面顶部有醒目的标题和开发者信息整个操作流程就像使用普通的网页应用一样简单。3. 训练微调让模型更懂你的数据3.1 为什么要训练微调你可能会有疑问模型不是已经能检测文字了吗为什么还要训练想象一下如果你主要处理的是医疗报告上的手写体而模型原来是在印刷体文档上训练的它的表现可能就不够理想。通过训练微调你可以让模型更适应你的文字风格比如特定的字体、字号、排版更擅长你的场景比如证件照、票据、手写笔记等更准确识别你的内容比如专业术语、特殊符号3.2 准备训练数据训练的第一步是准备数据。你需要按照特定的格式来组织你的图片和标注文件。假设你要训练一个识别商品标签的模型你的数据文件夹应该长这样custom_data/ ├── train_list.txt # 训练集列表文件 ├── train_images/ # 训练图片文件夹 │ ├── label_001.jpg # 商品标签图片1 │ ├── label_002.jpg # 商品标签图片2 │ └── label_003.jpg # 商品标签图片3 ├── train_gts/ # 训练标注文件夹 │ ├── label_001.txt # 图片1的标注文件 │ ├── label_002.txt # 图片2的标注文件 │ └── label_003.txt # 图片3的标注文件 ├── test_list.txt # 测试集列表文件 ├── test_images/ # 测试图片文件夹 │ └── label_004.jpg # 测试图片 └── test_gts/ # 测试标注文件夹 └── label_004.txt # 测试图片的标注文件标注文件格式每个.txt文件x1,y1,x2,y2,x3,y3,x4,y4,文本内容举个例子如果图片里有一行文字正品保证它的四个角坐标是(100,200)、(300,200)、(300,250)、(100,250)那么标注文件里就应该有一行100,200,300,200,300,250,100,250,正品保证列表文件格式train_list.txt和test_list.txttrain_images/label_001.jpg train_gts/label_001.txt train_images/label_002.jpg train_gts/label_002.txt train_images/label_003.jpg train_gts/label_003.txt3.3 开始训练数据准备好后就可以开始训练了。在Web界面的训练微调标签页里输入训练数据目录填写你准备好的数据文件夹路径比如/root/custom_data设置训练参数可以使用默认值也可以根据需求调整Batch Size批次大小一次训练多少张图片默认8如果你的显卡内存大可以调大一些训练轮数整个数据集训练多少遍默认5轮数据量少可以增加学习率模型学习的速度默认0.007新手建议保持默认点击开始训练按钮训练过程中你可以看到状态提示。训练完成后会显示输出路径微调后的模型就保存在workdirs/目录下。3.4 训练小贴士为了让训练效果更好这里有几个实用建议数据质量很重要图片要清晰文字要能看清楚标注要准确框要正好框住文字数据要多样包含各种角度、光照、背景参数调整有讲究如果训练损失loss下降很慢可以尝试增大学习率如果训练过程不稳定loss波动大可以减小学习率如果显卡内存够用适当增大Batch Size可以加快训练速度训练过程要监控观察loss值是否在持续下降训练几轮后用测试集验证一下效果如果效果不再提升就可以停止训练了4. ONNX导出一次导出处处可用4.1 什么是ONNX为什么要导出ONNXOpen Neural Network Exchange是一种开放的模型格式你可以把它理解为神经网络的通用语言。把模型导出成ONNX格式后跨平台使用可以在Windows、Linux、macOS上运行跨框架调用可以用PyTorch、TensorFlow、甚至移动端框架来加载性能优化可以利用ONNX Runtime进行加速部署方便直接一个文件不需要复杂的依赖4.2 导出步骤在Web界面的ONNX导出标签页里导出过程非常简单设置输入尺寸输入高度默认800像素输入宽度默认800像素范围可以在320到1536之间调整点击导出ONNX按钮等待导出完成成功后会出现下载链接点击下载ONNX模型保存到本地4.3 输入尺寸怎么选不同的尺寸适合不同的场景输入尺寸适合什么场景推理速度内存占用640×640手机拍照、简单文档很快很低800×800大多数情况平衡选择中等中等1024×1024小文字、复杂版面较慢较高1280×1280高精度需求如古籍很慢很高选择建议如果你主要处理手机拍的照片选640×640如果是扫描的文档选800×800如果需要识别很小的文字选1024×1024或更大4.4 使用导出的ONNX模型导出的ONNX模型可以直接在各种环境中使用。下面是一个Python示例import onnxruntime as ort # ONNX运行时 import cv2 # OpenCV处理图片 import numpy as np # 数值计算 # 1. 加载模型 # 这里只需要一个.onnx文件不需要其他依赖 session ort.InferenceSession(model_800x800.onnx) def detect_text(image_path): 检测图片中的文字 # 2. 读取和预处理图片 # 读取图片 image cv2.imread(image_path) # 调整大小到模型需要的尺寸 input_height 800 input_width 800 resized_image cv2.resize(image, (input_width, input_height)) # 转换颜色通道和数值范围 # 模型需要的是RGB格式数值在0-1之间 input_blob resized_image[:, :, ::-1] # BGR转RGB input_blob input_blob.transpose(2, 0, 1) # 从(H,W,C)变成(C,H,W) input_blob input_blob[np.newaxis, ...] # 增加批次维度变成(1,C,H,W) input_blob input_blob.astype(np.float32) / 255.0 # 归一化到0-1 # 3. 运行模型推理 # 输入名称需要查看模型信息一般是input outputs session.run(None, {input: input_blob}) # 4. 处理输出结果 # outputs包含检测框、置信度等信息 boxes outputs[0] # 检测框坐标 scores outputs[1] # 置信度分数 # 5. 后处理过滤低置信度的检测框 confidence_threshold 0.5 # 置信度阈值 valid_indices scores confidence_threshold final_boxes boxes[valid_indices] final_scores scores[valid_indices] return final_boxes, final_scores # 使用示例 boxes, scores detect_text(test.jpg) print(f检测到 {len(boxes)} 个文字区域) for i, (box, score) in enumerate(zip(boxes, scores)): print(f区域{i1}: 坐标{box}, 置信度{score:.2f})如果你需要在其他平台使用ONNX模型也能很好地支持在C中使用#include onnxruntime_cxx_api.h // 创建推理会话 Ort::Env env(ORT_LOGGING_LEVEL_WARNING, test); Ort::SessionOptions session_options; Ort::Session session(env, model.onnx, session_options); // 准备输入 std::vectorfloat input_tensor_values {...}; std::vectorint64_t input_shape {1, 3, 800, 800}; // 运行推理 auto output_tensors session.Run(Ort::RunOptions{nullptr}, input_names.data(), input_tensor, 1, output_names.data(), output_names.size());在浏览器中使用JavaScript// 使用ONNX Runtime Web版本 async function loadModel() { const session await ort.InferenceSession.create(./model.onnx, { executionProviders: [wasm] // 使用WebAssembly }); return session; } async function detectText(imageData) { const session await loadModel(); // 预处理图片数据 const inputTensor new ort.Tensor(float32, imageData, [1, 3, 800, 800]); // 运行推理 const outputs await session.run({ input: inputTensor }); // 处理结果 const boxes outputs.boxes.data; const scores outputs.scores.data; return { boxes, scores }; }5. 实际应用示例5.1 案例一批量处理商品标签假设你有一个电商平台每天需要处理成千上万的商品图片提取商品名称和价格信息。传统做法人工一个个看图片手动录入信息耗时耗力还容易出错。使用cv_resnet18_ocr-detection的做法import os from pathlib import Path import json class ProductLabelProcessor: def __init__(self, model_pathmodel.onnx): 初始化OCR处理器 self.session ort.InferenceSession(model_path) def process_batch(self, image_dir, output_dir): 批量处理商品标签图片 # 创建输出目录 Path(output_dir).mkdir(exist_okTrue) results [] # 遍历所有图片 for image_file in Path(image_dir).glob(*.jpg): print(f处理: {image_file.name}) # 检测文字 boxes, scores self.detect_text(str(image_file)) # 提取商品信息这里需要根据你的标签格式来解析 product_info self.extract_product_info(boxes) # 保存结果 result_file Path(output_dir) / f{image_file.stem}.json with open(result_file, w, encodingutf-8) as f: json.dump(product_info, f, ensure_asciiFalse, indent2) results.append({ file: image_file.name, info: product_info, count: len(boxes) }) # 生成汇总报告 self.generate_report(results, output_dir) return results def extract_product_info(self, boxes): 从检测结果中提取商品信息 # 这里需要根据你的标签格式来写解析逻辑 # 比如商品名称通常在顶部价格在底部等 product_info { product_name: , price: , specifications: [], other_text: [] } # 简单的规则按y坐标排序假设从上到下是名称、规格、价格 sorted_boxes sorted(boxes, keylambda b: b[1]) # 按y坐标排序 if len(sorted_boxes) 1: product_info[product_name] sorted_boxes[0][text] if len(sorted_boxes) 2: product_info[specifications] [box[text] for box in sorted_boxes[1:-1]] if len(sorted_boxes) 3: product_info[price] sorted_boxes[-1][text] return product_info # 使用示例 processor ProductLabelProcessor(model_800x800.onnx) results processor.process_batch( image_dir/path/to/product_images, output_dir/path/to/results ) print(f处理完成共处理 {len(results)} 张图片)5.2 案例二文档数字化归档对于企业来说把纸质文档变成可搜索的电子文档是个常见需求。import cv2 import pytesseract # 如果需要识别文字内容 from PIL import Image class DocumentDigitizer: def __init__(self, detection_model_pathmodel.onnx, recognition_langchi_simeng): 文档数字化处理器 self.detector ort.InferenceSession(detection_model_path) self.recognition_lang recognition_lang # 识别语言中文英文 def digitize_document(self, scan_path, output_path): 将扫描件转换为结构化文本 # 1. 读取扫描件 image cv2.imread(scan_path) # 2. 检测文字区域 boxes, _ self.detect_text_boxes(image) # 3. 按阅读顺序排序从左到右从上到下 sorted_boxes self.sort_boxes_by_reading_order(boxes) # 4. 逐个区域识别文字 document_text [] for i, box in enumerate(sorted_boxes): # 裁剪文字区域 text_region self.crop_text_region(image, box) # 识别文字这里用pytesseract你也可以用其他OCR识别引擎 text pytesseract.image_to_string( Image.fromarray(text_region), langself.recognition_lang ) document_text.append({ region_id: i, coordinates: box.tolist(), text: text.strip(), confidence: box[score] }) # 5. 保存结果 self.save_as_markdown(document_text, output_path) self.save_as_json(document_text, f{output_path}.json) return document_text def sort_boxes_by_reading_order(self, boxes): 按阅读顺序排序检测框 # 简单的排序逻辑先按y坐标行再按x坐标列 boxes_with_centers [] for box in boxes: center_x np.mean([box[0], box[2], box[4], box[6]]) center_y np.mean([box[1], box[3], box[5], box[7]]) boxes_with_centers.append((box, center_y, center_x)) # 按y坐标分组同一行 boxes_with_centers.sort(keylambda x: x[1]) # 对每一行内的框按x坐标排序 sorted_boxes [] current_row [] current_y boxes_with_centers[0][1] for box, y, x in boxes_with_centers: if abs(y - current_y) 20: # 20像素内认为是同一行 current_row.append((box, x)) else: # 对当前行排序 current_row.sort(keylambda x: x[1]) sorted_boxes.extend([b[0] for b in current_row]) current_row [(box, x)] current_y y # 处理最后一行 if current_row: current_row.sort(keylambda x: x[1]) sorted_boxes.extend([b[0] for b in current_row]) return sorted_boxes # 使用示例 digitizer DocumentDigitizer() result digitizer.digitize_document( scan_path/path/to/contract.jpg, output_path/path/to/contract_digitized ) print(f文档转换完成共识别 {len(result)} 个文字区域)5.3 案例三移动端身份证信息提取在移动端APP中用户拍照身份证自动提取姓名、身份证号等信息。# 这是简化版的移动端处理逻辑 class MobileIDCardReader: def __init__(self, model_pathmodel_mobile.onnx): 移动端身份证阅读器 # 移动端使用量化后的模型体积更小 self.session ort.InferenceSession(model_path) def extract_id_info(self, image): 提取身份证信息 # 1. 检测所有文字区域 boxes, scores self.detect_text(image) # 2. 找到关键信息区域 # 假设我们知道身份证上信息的相对位置 id_info { name: self.find_text_in_region(boxes, name_region), id_number: self.find_text_in_region(boxes, id_region), address: self.find_text_in_region(boxes, address_region), # ... 其他字段 } # 3. 验证信息格式 if self.validate_id_number(id_info[id_number]): return {success: True, data: id_info} else: return {success: False, error: 身份证号格式错误} def find_text_in_region(self, boxes, region_type): 在特定区域查找文字 # 根据区域类型定义搜索范围 if region_type name_region: search_area (100, 150, 300, 200) # (x1, y1, x2, y2) elif region_type id_region: search_area (100, 250, 400, 300) # ... 其他区域 # 在区域内查找文字 texts_in_region [] for box in boxes: if self.is_box_in_region(box, search_area): texts_in_region.append(box[text]) return .join(texts_in_region) if texts_in_region else # Android端调用示例简化 public class IDCardScanner { private OrtSession session; public IDCardScanner(Context context) { // 加载优化后的模型 OrtEnvironment env OrtEnvironment.getEnvironment(); OrtSession.SessionOptions options new OrtSession.SessionOptions(); // 使用NNAPI加速Android 8.0支持 options.addNnapi(); // 从assets加载模型 session env.createSession(loadModelFromAssets(context), options); } public String scanIDCard(Bitmap idCardImage) { // 预处理图片 float[] input preprocessImage(idCardImage); // 运行OCR检测 OrtSession.Result result session.run(Collections.singletonMap(input, input)); // 解析结果 return parseIDInfo(result); } }6. 常见问题与解决方案6.1 训练时遇到的问题问题1训练失败提示找不到数据可能原因数据路径错误或格式不对解决方案检查数据文件夹路径是否正确确认文件夹结构是否符合要求检查train_list.txt中的路径是否正确问题2训练loss不下降可能原因学习率不合适或数据有问题解决方案尝试减小学习率比如从0.007调到0.001检查数据标注是否正确增加训练轮数问题3训练速度很慢可能原因Batch Size太大或硬件性能不足解决方案减小Batch Size比如从8调到4使用GPU训练如果有的话减少输入图片尺寸6.2 导出时遇到的问题问题1导出ONNX失败可能原因模型文件损坏或版本不兼容解决方案重新训练模型检查PyTorch和ONNX版本尝试不同的输入尺寸问题2导出的ONNX模型推理结果不对可能原因输入预处理或后处理不一致解决方案确保预处理步骤与训练时一致检查输入尺寸是否正确验证输出格式6.3 使用时的常见问题问题1检测不到文字可能原因图片质量差或阈值设置太高解决方案提高图片质量清晰度、对比度降低检测阈值比如从0.5调到0.3调整输入尺寸尝试更大的尺寸问题2检测框不准可能原因文字倾斜或弯曲解决方案使用更大的输入尺寸对图片进行预处理旋转、透视变换考虑使用更先进的检测算法问题3内存不足可能原因图片太大或批量太大解决方案减小输入图片尺寸减小Batch Size使用内存更小的模型版本7. 性能优化建议7.1 推理速度优化如果你觉得模型运行速度不够快可以尝试这些优化方法调整输入尺寸# 根据需求选择合适的尺寸 def optimize_for_speed(image_path): 速度优先的优化策略 # 小尺寸速度快适合大文字 small_size (640, 640) # 中尺寸平衡选择通用场景 medium_size (800, 800) # 大尺寸精度高适合小文字 large_size (1024, 1024) # 根据图片内容自动选择 image cv2.imread(image_path) height, width image.shape[:2] # 如果图片中文字很大用小尺寸 if estimate_text_size(image) 30: return small_size # 如果图片中文字很小用大尺寸 elif estimate_text_size(image) 15: return large_size # 其他情况用中尺寸 else: return medium_size使用ONNX Runtime优化import onnxruntime as ort def create_optimized_session(model_path): 创建优化后的推理会话 options ort.SessionOptions() # 启用所有图优化 options.graph_optimization_level ort.GraphOptimizationLevel.ORT_ENABLE_ALL # 设置线程数根据CPU核心数调整 options.intra_op_num_threads 4 # 算子内并行线程数 options.inter_op_num_threads 2 # 算子间并行线程数 # 使用CUDA加速如果有GPU providers [CUDAExecutionProvider, CPUExecutionProvider] # 创建会话 session ort.InferenceSession(model_path, options, providersproviders) return session7.2 内存优化批量处理时的内存管理class MemoryEfficientProcessor: def __init__(self, model_path, max_batch_size4): self.session ort.InferenceSession(model_path) self.max_batch_size max_batch_size def process_large_dataset(self, image_paths): 处理大量图片内存友好 results [] # 分批处理避免内存溢出 for i in range(0, len(image_paths), self.max_batch_size): batch_paths image_paths[i:i self.max_batch_size] # 处理当前批次 batch_results self.process_batch(batch_paths) results.extend(batch_results) # 及时释放内存 import gc gc.collect() print(f已处理 {i len(batch_paths)}/{len(image_paths)} 张图片) return results def process_batch(self, image_paths): 处理一个批次 batch_images [] # 加载图片 for path in image_paths: img cv2.imread(path) img self.preprocess(img) batch_images.append(img) # 堆叠成批次 batch_tensor np.stack(batch_images, axis0) # 推理 outputs self.session.run(None, {input: batch_tensor}) # 处理结果 batch_results [] for i in range(len(image_paths)): result self.postprocess(outputs, i) batch_results.append(result) return batch_results7.3 精度优化后处理优化def improve_detection_accuracy(boxes, scores, image_shape): 通过后处理提高检测精度 improved_boxes [] # 1. 过滤低置信度的检测 confidence_threshold 0.3 valid_indices scores confidence_threshold boxes boxes[valid_indices] scores scores[valid_indices] # 2. 非极大值抑制NMS去除重叠框 keep_indices nms(boxes, scores, iou_threshold0.5) boxes boxes[keep_indices] scores scores[keep_indices] # 3. 根据图像边界裁剪框 for box in boxes: # 确保框在图像范围内 box[:, 0] np.clip(box[:, 0], 0, image_shape[1]) # x坐标 box[:, 1] np.clip(box[:, 1], 0, image_shape[0]) # y坐标 improved_boxes.append(box) # 4. 合并相邻的文字框可能是同一个单词被分成多个框 merged_boxes merge_nearby_boxes(improved_boxes, distance_threshold20) return merged_boxes def nms(boxes, scores, iou_threshold0.5): 非极大值抑制 # 按置信度排序 order scores.argsort()[::-1] keep [] while order.size 0: i order[0] keep.append(i) # 计算当前框与其他框的IoU ious calculate_iou(boxes[i], boxes[order[1:]]) # 保留IoU小于阈值的框 inds np.where(ious iou_threshold)[0] order order[inds 1] return keep8. 总结通过这篇教程你应该已经掌握了cv_resnet18_ocr-detection模型的三个核心技能训练微调、ONNX导出和实际应用。让我们简单回顾一下8.1 训练微调的关键点数据准备要规范按照ICDAR2015格式组织数据标注要准确参数调整要合理根据数据量和硬件条件调整Batch Size、学习率等参数监控训练过程观察loss变化及时调整策略8.2 ONNX导出的优势跨平台部署一次导出到处运行性能优化可以利用ONNX Runtime的各种优化易于集成一个文件搞定依赖简单8.3 实际应用的建议根据场景选择尺寸实时应用选小尺寸高精度需求选大尺寸合理设置阈值清晰图片用高阈值模糊图片用低阈值做好错误处理网络异常、图片损坏等情况都要考虑8.4 下一步学习方向如果你已经掌握了基础用法可以进一步探索模型压缩尝试量化、剪枝等技术让模型更小更快多语言支持训练支持多种语言的检测模型端到端系统结合文字识别模型构建完整的OCR系统云端部署将模型部署为API服务供多端调用记住最好的学习方式就是动手实践。找一个你感兴趣的应用场景收集一些数据训练一个属于自己的文字检测模型然后把它用在实际项目中。过程中遇到问题很正常多尝试、多调整你会越来越熟练的。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。