2026/4/16 23:31:02
网站建设
项目流程
企业网站建设训,黑客零基础入门,网站开发人员结构配比,北京有名气的设计事务所C部署参考#xff1a;OCR三大模块推理代码解析
1. 引言
1.1 OCR系统架构概述
光学字符识别#xff08;OCR#xff09;技术在现代文档处理、图像理解与自动化信息提取中扮演着关键角色。一个完整的端到端OCR系统通常由三个核心模块构成#xff1a;文字检测#xff08;Te…C部署参考OCR三大模块推理代码解析1. 引言1.1 OCR系统架构概述光学字符识别OCR技术在现代文档处理、图像理解与自动化信息提取中扮演着关键角色。一个完整的端到端OCR系统通常由三个核心模块构成文字检测Text Detection、方向分类Text Classification和文字识别Text Recognition。这三个模块协同工作实现从原始图像到结构化文本的完整转换流程。本文基于cv_resnet18_ocr-detection镜像所提供的OCR模型能力深入解析其在C环境下的推理实现逻辑。该镜像集成了基于ResNet18骨干网络的DBNet文字检测模型并支持ONNX导出和WebUI交互界面适用于工业级部署场景。我们将重点剖析三大功能模块的C推理代码实现细节涵盖预处理、模型调用、后处理及结果整合等关键环节。1.2 技术选型背景本系统采用以下经典轻量级模型组合DBNet用于高精度、实时性要求高的文本区域检测ShuffleNetV2作为方向分类器在保证低延迟的同时具备良好的分类性能CRNN结合CNN与BiLSTMCTC的序列识别架构适合短文本识别任务。所有模型均已通过PyTorch训练并导出为ONNX格式可在跨平台环境中使用ONNX Runtime进行高效推理。本文将围绕这三类ONNX模型的C集成方式展开详细分析。2. 文字检测模块DBNet推理实现2.1 模型输入预处理DBNet模型接收固定通道数的RGB图像作为输入需完成归一化与张量布局转换。以下是典型的预处理函数实现cv::Mat TextDetector::preprocess(const cv::Mat srcimg) { cv::Mat dstimg; cv::resize(srcimg, dstimg, cv::Size(inpWidth, inpHeight)); dstimg.convertTo(dstimg, CV_32F, 1.0 / 255.0); return dstimg; } void TextDetector::normalize_(const cv::Mat img, float* blob) { int channels 3; int height img.rows; int width img.cols; blob new float[channels * height * width]; std::vectorcv::Mat chw(channels); for (int i 0; i channels; i) { chw[i] cv::Mat(height, width, CV_32F, blob i * height * width); } cv::split(img, chw); }上述代码实现了图像尺寸缩放、归一化/255并通过cv::split将HWC格式转为CHW格式符合ONNX模型输入要求。2.2 ONNX Runtime推理执行使用ONNX Runtime加载DBNet模型并执行前向推理std::vectorstd::vectorcv::Point2f TextDetector::detect(cv::Mat srcimg) { float* blob nullptr; int h srcimg.rows; int w srcimg.cols; cv::Mat dstimg this-preprocess(srcimg); this-normalize_(dstimg, blob); std::vectorint64_t inputTensorShape{ 1, 3, dstimg.rows, dstimg.cols }; size_t inputTensorSize utils::vectorProduct(inputTensorShape); std::vectorfloat inputTensorValues(blob, blob inputTensorSize); Ort::MemoryInfo memoryInfo Ort::MemoryInfo::CreateCpu( OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); auto inputTensor Ort::Value::CreateTensorfloat( memoryInfo, inputTensorValues.data(), inputTensorSize, inputTensorShape.data(), inputTensorShape.size()); std::vectorOrt::Value outputTensors session.Run(Ort::RunOptions{nullptr}, inputNames.data(), inputTensor, 1, outputNames.data(), outputNames.size());其中session.Run()触发模型推理返回概率图输出。2.3 后处理轮廓提取与坐标还原对模型输出的概率图进行阈值化、轮廓查找与坐标映射const float* floatArray outputTensors[0].GetTensorMutableDatafloat(); cv::Mat binary(dstimg.rows, dstimg.cols, CV_32FC1); std::memcpy(binary.data, floatArray, inputTensorSize * sizeof(float)); cv::Mat bitmap; cv::threshold(binary, bitmap, binaryThreshold, 255, cv::THRESH_BINARY); std::vectorstd::vectorcv::Point contours; cv::findContours(bitmap, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE); float scaleHeight static_castfloat(h) / dstimg.rows; float scaleWidth static_castfloat(w) / dstimg.cols; std::vectorstd::vectorcv::Point2f results; for (const auto contour : contours) { if (contour.size() 4) continue; std::vectorcv::Point2f polygon; for (const auto pt : contour) { polygon.emplace_back(pt.x * scaleWidth, pt.y * scaleHeight); } cv::RotatedRect box cv::minAreaRect(polygon); float longSide std::max(box.size.width, box.size.height); if (longSide longSideThresh) { results.push_back(polygon); } } delete[] blob; return results; }最终返回的是原始图像尺度下的多边形顶点集合可用于后续裁剪或可视化。3. 方向分类模块ShuffleNetV2推理实现3.1 文本框裁剪与透视校正对每个检测到的四点文本框进行规整化裁剪cv::Mat TextClassifier::get_rotate_crop_image(const cv::Mat frame, std::vectorcv::Point2f vertices) { std::sort(vertices.begin(), vertices.end(), [](const cv::Point2f a, const cv::Point2f b) { return a.y b.y; }); if (vertices[0].x vertices[1].x) std::swap(vertices[0], vertices[1]); if (vertices[2].x vertices[3].x) std::swap(vertices[2], vertices[3]); std::vectorcv::Point2f correctedVertices { vertices[0], vertices[2], vertices[3], vertices[1] }; cv::Rect rect cv::boundingRect(cv::Mat(correctedVertices)); cv::Mat crop_img frame(rect).clone(); for (auto v : correctedVertices) { v - cv::Point2f(rect.x, rect.y); } cv::Size outputSize(rect.width, rect.height); std::vectorcv::Point2f targetVertices { cv::Point2f(0, 0), cv::Point2f(0, outputSize.height - 1), cv::Point2f(outputSize.width - 1, outputSize.height - 1), cv::Point2f(outputSize.width - 1, 0) }; cv::Mat M cv::getPerspectiveTransform(correctedVertices, targetVertices); cv::Mat result; cv::warpPerspective(crop_img, result, M, outputSize, cv::BORDER_CONSTANT, 0); return result; }此函数确保无论输入顶点顺序如何均能正确生成水平对齐的矩形图像。3.2 分类推理与结果输出将裁剪后的图像送入ShuffleNetV2模型进行方向预测int TextClassifier::predict(cv::Mat cv_image) { cv::Mat resized; cv::resize(cv_image, resized, cv::Size(inpWidth, inpHeight)); resized.convertTo(resized, CV_32F, 1.0 / 255.0); float* blob new float[3 * inpHeight * inpWidth]; // HWC to CHW std::vectorcv::Mat chw(3); for (int c 0; c 3; c) { chw[c] cv::Mat(inpHeight, inpWidth, CV_32F, blob c * inpHeight * inpWidth); } cv::split(resized, chw); std::vectorint64_t inputShape{1, 3, inpHeight, inpWidth}; auto inputTensor Ort::Value::CreateTensorfloat( Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault), blob, 3 * inpHeight * inpWidth, inputShape.data(), inputShape.size()); auto outputTensors session.Run(Ort::RunOptions{nullptr}, inputNames.data(), inputTensor, 1, outputNames.data(), outputNames.size()); const float* probs outputTensors[0].GetTensorMutableDatafloat(); int max_id 0; float max_prob probs[0]; for (int i 1; i num_classes; i) { if (probs[i] max_prob) { max_prob probs[i]; max_id i; } } delete[] blob; return max_id; // 返回角度类别索引如0°,90°,180°,270° }返回值可用于控制是否旋转原图以供后续识别。4. 文字识别模块CRNN推理实现4.1 图像预处理与张量构造CRNN模型要求输入为固定高度如48的灰度图像cv::Mat TextRecognizer::preprocess(const cv::Mat srcimg) { cv::Mat gray; if (srcimg.channels() 3) { cv::cvtColor(srcimg, gray, cv::COLOR_BGR2GRAY); } else { gray srcimg.clone(); } cv::Mat resized; cv::resize(gray, resized, cv::Size(inpWidth, inpHeight)); resized.convertTo(resized, CV_32F, 1.0 / 255.0); return resized; }4.2 序列解码与CTC后处理CRNN输出为时间步上的字符概率分布需通过CTC解码获取最终文本std::string TextRecognizer::predict_text(cv::Mat cv_image) { float* blob nullptr; cv::Mat dstimg this-preprocess(cv_image); this-normalize_(dstimg, blob); std::vectorint64_t inputShape{1, 1, inpHeight, inpWidth}; // 单通道输入 size_t inputSize utils::vectorProduct(inputShape); auto inputTensor Ort::Value::CreateTensorfloat( Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault), blob, inputSize, inputShape.data(), inputShape.size()); auto outputTensors session.Run(Ort::RunOptions{nullptr}, inputNames.data(), inputTensor, 1, outputNames.data(), outputNames.size()); const float* logits outputTensors[0].GetTensorMutableDatafloat(); int seq_len outputTensors[0].GetTensorTypeAndShapeInfo().GetShape()[1]; int num_classes outputTensors[0].GetTensorTypeAndShapeInfo().GetShape()[2]; std::vectorint pred_labels; for (int t 0; t seq_len; t) { int max_idx 0; float max_val logits[t * num_classes]; for (int c 1; c num_classes; c) { if (logits[t * num_classes c] max_val) { max_val logits[t * num_classes c]; max_idx c; } } if (max_idx ! 0 !(pred_labels.size() 0 pred_labels.back() max_idx)) { pred_labels.push_back(max_idx); } } std::string text; for (int idx : pred_labels) { text alphabet[idx - 1]; // 假设alphabet不含blank } delete[] blob; return text; }该过程模拟了CTC Greedy Decoding行为移除重复字符与空白符。5. 系统整合与工程优化建议5.1 多模块串联流程设计完整的OCR流水线应按如下顺序执行使用DBNet检测所有文本区域对每个检测框调用get_rotate_crop_image裁剪将裁剪图像送入ShuffleNetV2判断方向必要时旋转将规整后的图像送入CRNN进行识别按空间位置排序输出文本行。for (auto box : detected_boxes) { cv::Mat cropped classifier.get_rotate_crop_image(frame, box); int angle_id classifier.predict(cropped); if (angle_id 1 || angle_id 3) { // 90 or 270 cv::rotate(cropped, cropped, angle_id 1 ? cv::ROTATE_90_CLOCKWISE : cv::ROTATE_90_COUNTERCLOCKWISE); } std::string text recognizer.predict_text(cropped); results.push_back({box, text}); }5.2 性能优化实践建议内存复用避免频繁new/delete可使用对象池管理blob缓冲区批处理支持对于批量图片可合并输入张量提升GPU利用率异步推理利用ONNX Runtime的多线程能力实现流水线并发模型量化将FP32模型转换为INT8以降低计算开销输入尺寸适配根据实际场景调整inpWidth/inpHeight平衡速度与精度。6. 总结本文系统解析了基于ONNX Runtime的OCR三大核心模块——DBNet文字检测、ShuffleNetV2方向分类与CRNN文字识别——在C环境中的推理实现方法。通过对预处理、模型调用、后处理各阶段的代码拆解展示了如何将深度学习模型无缝集成至高性能生产系统中。关键要点包括 1. DBNet输出为概率图需通过阈值化与轮廓提取获得文本框 2. ShuffleNetV2用于快速判断文本方向提升识别准确率 3. CRNN依赖CTC机制实现端到端序列识别需正确解码输出 4. 多模块协同需注意坐标映射、图像裁剪与顺序恢复。该方案已在cv_resnet18_ocr-detection镜像中验证可用具备良好的可移植性与扩展潜力适用于嵌入式设备、边缘服务器等多种部署场景。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。