简介:这是一套基于Python的OCR图片识别完整源码项目,核心由rapidOCR改造而来。面向需要快速实现图片转文字、构建OCR识别服务的开发者,内置/engine/ocr接口,可接受HTTP链接、本地文件、文件流等任意形式图片输入,识别结果输出为标记好的HTML结构,可直接嵌入其他页面或复制使用。资源共46个文件,主要包括23个Python源码文件、17个编译后的pyc文件、3个ONNX推理模型,以及HTML模板、YAML配置和依赖清单等,压缩包整体14.26MB,目录涵盖OCR检测、识别、分类及Web服务等模块。已有1739人学习下载。通过这份源码,可了解rapidOCR的工程化改造思路,掌握OCR服务的接口封装、多格式图片接收处理及HTML结果生成流程,适合有一定Python基础、希望在项目中快速集成OCR能力的开发者参考使用。
1. 这套 OCR 源码能做什么:不只能识别文字,还能直接吐出前端可用的 HTML 结构
凡是做过图片转文字的人都清楚,调用现成 OCR 接口最省事,但放到内网环境或私有化部署里就完全抓瞎。这套源码的核心价值在于:它基于 rapidOCR 改造而来,用 Python 实现了完整识别流程,并且不是简单返回一个纯文本字符串,而是把版面、坐标、置信度一起打包,最后输出结构化的 HTML——你拿到的就是能直接渲染到网页上的东西。对做档案数字化、票据解析、截图内容提取这类需求的人来说,省掉了坐标拼接、文本排序、画框标注这些最费时间的脏活。适合谁用呢?一类是有 Python 基础、想把 OCR 集成进自己项目的开发者,另一类是遇到了现成 SDK 性能不够或格式不满足要求的场景,需要自己动手改造的人。接下来我会从选型逻辑、源码模块、参数调优、踩坑记录这几个维度,把这份资源完整拆开。
2. 选型逻辑:为什么是 rapidOCR 改造,而不是重写识别引擎
2.1 rapidOCR 的技术底子与三个核心优势
rapidOCR 本质上是把 PaddleOCR 的推理模型转成 ONNX 格式后,用 OpenCV 的 DNN 模块来加载运行,所以它不依赖 PaddlePaddle 全家桶,也不需要 GPU,纯 CPU 就能跑。这对绝大多数业务场景来说意味着三件事:部署体积小、环境干净、推理速度可接受。对比一下,如果直接部署 PaddleOCR 原版,你需要安装 PaddlePaddle 框架,光是依赖就可能超过 2GB,而基于 ONNX 的方案通常几百 MB 就能搞定。
从源码的结构来看,它保留了检测、方向分类、识别这三段式流水线,也就是英文里常说的 detect -> classify -> recognize。检测模型负责找出图片里哪些区域有文字,方向分类器判断文字是正的还是旋转了 90 度、180 度,识别模型再把区域内容转成字符串。这三个模型全部由 ONNX 文件承载,加载方式统一,所以你改造起来非常顺手——换模型文件、调阈值参数、改预处理逻辑,都有明确的下手位置。
2.2 不自己训练模型的现实考虑
很多人看到 OCR 的第一反应是「我要训练一个自己的模型」,这个想法对绝大多数从业者来说都是坑。训练一个可用的文字识别模型需要大量的标注数据、GPU 资源和至少两周的调参周期,而且你训出来的模型大概率在通用场景下还不如现成的预训练模型。这套源码走的路线很务实:直接用百度开源的预训练模型权重,通过 ONNX 转换保留推理能力,然后在后处理阶段做改造——也就是把识别结果和坐标信息重新组织成 HTML。这个思路的好处是:你不需要理解模型内部的数学细节,只需要会调用接口、解析输出、按业务需求格式化结果。
我一般会这样给团队讲解这套架构的价值:它把「OCR 能力」和「业务输出格式」彻底解耦了。模型负责从图片到文字和坐标,源码负责从坐标和文字到 HTML 结构。你改前者就是换模型文件,改后者就是改 Python 代码,两边的风险互不污染。这也给了二次开发一个很清晰的边界。
2.3 与直接用 pytesseract 的对比
如果只是想把图片转成文本,pytesseract 确实是更轻的选择,但它有两个硬伤:一是对中文长文本的识别准确率明显低于 Paddle 系模型,二是它只返回纯文本,没有可靠的坐标信息,导致你无法定位每个词在图片中的具体位置。而 OCR 在实际项目里,坐标几乎和文本本身一样重要。举个例子,识别一张发票,你不仅要知道上面有「金额」两个字,还要知道这两个字在哪个位置,才能做字段映射和版面还原。这套源码因为基于 Paddle 系模型,天然输出带坐标的检测框,再结合源码里对坐标的排序和分组逻辑,就能真实还原阅读顺序,这在票据识别场景里属于刚需能力。
3. 源码模块拆解:从图片输入到 HTML 输出的完整链路
3.1 项目目录结构
拿到源码之后,第一步是看懂目录。不同类型的文件职责完全不同,理解这个结构你才知道改哪里、加哪里。一般会看到这样的布局:
ocr_project/ models/ # ONNX 模型文件存放目录 det.onnx # 文本检测模型 cls.onnx # 方向分类模型 rec.onnx # 文字识别模型 keys.txt # 识别字符集字典 src/ ocr_engine.py # OCR 推理核心封装 html_builder.py # HTML 结构生成模块 image_processor.py # 图片预处理与后处理 config.py # 全局参数配置 main.py # 命令行入口 requirements.txt # 依赖清单models 目录下的三个 ONNX 文件是整个识别能力的物理基础,没有它们,代码跑不起来。keys.txt 这个文件容易被忽略,但它里面的字符顺序必须和 rec.onnx 训练时的字典完全一致,否则识别出来的文字就是乱码。src 目录里四个 Python 文件分工不同,其中 html_builder.py 是这个项目最有特色的部分,后面会专门讲。
3.2 OCR 推理引擎的调用方式
ocr_engine.py 是整套代码的核心,它负责加载模型、预处理图片、执行推理、解析输出。它的调用方式是理解整套逻辑的钥匙:
import cv2 import numpy as np from rapidocr_onnxruntime import RapidOCR class OcrEngine: def __init__(self, det_path, cls_path, rec_path, keys_path): # 传入模型路径,实例化 RapidOCR 引擎 self.engine = RapidOCR(det_model_path=det_path, cls_model_path=cls_path, rec_model_path=rec_path, keys_path=keys_path) def recognize(self, image_path): # image_path: 本地图片路径,支持 jpg/png/bmp 等常见格式 result = self.engine(image_path) if result is None: return [] # result 是嵌套列表,每项包含 [坐标框, 识别文本, 置信度] return result这段代码展示了和 rapidOCR 对接的最简方式。RapidOCR 类接收四个模型路径参数,内部自动完成 ONNX 会话的创建。recognize 方法返回的结果结构值得注意——它是一个列表,每个元素形如[[左上角x, 左上角y, 右下角x, 右下角y], "识别出的文字", 置信度]。置信度取值范围是 0 到 1,通常高于 0.5 的结果才有实际使用价值。
参数说明:det_model_path、cls_model_path、rec_model_path 分别对应检测、分类、识别三个模型文件的位置。keys_path 对应字典文件的位置。如果你的模型文件名称不同,只要路径指正确就行,不要求文件名和上面示例完全一致。这个封装已经足够满足基本调用需求,但有个细节要留意——RapidOCR 的返回值里,坐标是整数还是浮点数,不同版本可能不一样,拼接 HTML 的时候要统一做类型转换。
3.3 HTML 结构生成:坐标怎么变成网页上的定位元素
这是这套源码里最值得研究的部分。拿到 OCR 返回的坐标和文本之后,html_builder.py 负责把它们组织成 HTML 标签。核心思路是把每个识别结果映射成一个带绝对定位的<div>标签,外层容器使用相对定位来保持坐标系一致:
def build_html(result, image_width, image_height): # 初始化 HTML 容器,使用相对定位并设置图片尺寸 html_parts = [] container_style = (f'position:relative;width:{image_width}px;' f'height:{image_height}px;') html_parts.append(f'<div style="{container_style}">') for box, text, confidence in result: # box 是四角坐标,提取左上角和右下角来计算宽高 x1, y1 = int(box[0][0]), int(box[0][1]) x2, y2 = int(box[2][0]), int(box[2][1]) box_width = x2 - x1 box_height = y2 - y1 # 每个文本块生成一个绝对定位的 div,背景半透明便于调试 style = (f'position:absolute;left:{x1}px;top:{y1}px;' f'width:{box_width}px;height:{box_height}px;' f'background:rgba(255,255,0,0.2);') html_parts.append(f'<div style="{style}">{text}</div>') html_parts.append('</div>') return '\n'.join(html_parts)这段代码的逻辑很直观:遍历 OCR 返回的每个结果块,用坐标算出左上角位置和宽高,然后输出一个绝对定位的 div。需要注意我写的是box[0][0]和box[2][0],因为 rapidOCR 返回的坐标顺序是四个角:左上、右上、右下、左下。如果你用的是其他 OCR 引擎,坐标顺序可能不同,这个要格外小心。
一个常见的改造需求是:去掉黄色背景、只保留边框或者连边框都不要,这时候改 style 字符串就行。另一个常见需求是给不同的置信度设置不同的颜色——低置信度的标红,方便人工复核,这个可以通过比较 confidence 和阈值来实现。HTML 结构的好处就是灵活,任何展示层面的改动都不需要重新跑模型。
3.4 图片预处理与缩放逻辑
image_processor.py 里最值得关注的是图片缩放逻辑。OCR 模型对输入尺寸有固定要求,一般宽度会被缩放到 960 像素,高度保持比例。但缩放比例会对最终输出的坐标产生影响——你拿到的坐标是缩放后图片上的坐标,如果要映射回原始图片尺寸,需要做个反算:
def resize_and_scale_back(image, result, target_width=960): # 记录原始尺寸 orig_height, orig_width = image.shape[:2] scale = target_width / orig_width new_width = target_width new_height = int(orig_height * scale) # 缩放图片 resized = cv2.resize(image, (new_width, new_height)) # 推理结果中的坐标需要按比例还原到原始图片 scaled_result = [] for box, text, confidence in result: orig_box = [] for point in box: orig_x = int(point[0] / scale) orig_y = int(point[1] / scale) orig_box.append([orig_x, orig_y]) scaled_result.append((orig_box, text, confidence)) return resized, scaled_result这段代码解决了实际应用中最容易踩坑的坐标偏移问题。很多人在用 OCR 的时候,直接在缩放后的图片上标注坐标,得出的位置和原图对不上。这里的核心就是用同一个 scale 值来做等比例还原。特别要注意:scale 必须是浮点数,如果用整数除法,图片宽高超过一定值后坐标就会偏。这种细节往往就是识别结果「差一点」的根源。
参数说明:target_width 默认设为 960,这是模型训练时使用的典型宽度。如果你的图片文字非常小,可以考虑把 target_width 调大到 1280,识别率会提升但速度会下降;反过来,处理大图时限制宽度可以明显提升速度,代价是极小的文字可能丢失。这个参数值得根据你的实际图片情况反复调试。
4. 避坑与排查:跑了十几个项目总结出的五条血泪经验
4.1 中文识别结果成乱码
现象:识别英文字母和数字完全正常,但中文输出全是Ã¥Â这类乱码,或者干脆是空白框。
原因:keys.txt 字典文件与 rec.onnx 模型不匹配。rapidOCR 的识别模型在训练时指定了字符集,推理时输出的类别序号对应的是字典里该位置的字,字典一旦不对,序号就映射到错误的字符上。
解决:去确认 keys.txt 的字符数量和顺序是否与 rec.onnx 配套。一个有效的做法:先跑通源码自带的 demo,如果 demo 识别中文正常,就说明模型和字典是匹配的——那就检查自己是否在无意中替换了模型或字典。如果 demo 也乱码,那就去重新下载配套的字典和模型,不要混用不同来源的文件。
4.2 坐标偏移,文字框和图片中的位置对不上
现象:识别出的文字是对的,但 HTML 渲染出来的高亮框歪了,有的框偏上、有的框偏左,整体错位严重。
原因:图片在送入模型前做了缩放,但返回的坐标用的是缩放后的坐标系,没有还原回原始尺寸。另一个高频原因是图片在传入前已经用 OpenCV 的cv2.resize改过尺寸,但缩放倍率被当成了整数。
解决:注意在ocr_engine.py或image_processor.py中查看是否有坐标还原逻辑。如果代码在得到推理结果后直接把坐标写入 HTML,那就是少了还原的一步。在recognize函数中,拿原始图片尺寸除以缩放后的尺寸得到 scale 系数,然后用 float 类型参与运算,坐标全部除以 scale 再输出。别小看这个细节——这几乎是自用 OCR 改造成 HTML 展示时出现频率最高的 bug。
4.3 识别速度越来越慢,内存持续飙升
现象:单张图片识别速度正常,但在循环处理几百张图片的批处理场景下,越跑越慢,内存占用一路走高,最后直接卡死。
原因:RapidOCR实例被反复创建,或者推理结果中的大列表对象没有释放。通常出现两种情况:一是循环内每次调用都重新加载 ONNX 模型;二是图片读取后没有释放 OpenCV 的 Mat 对象,累积成了内存碎片。
解决:全工程只保留一个全局的OcrEngine单例,初始化一次,后续识别只调recognize方法。处理完每张图片后,显式调用cv2.destroyAllWindows()或对用过的帧变量赋值 None。批量任务建议加上gc.collect()兜底。实际操作中我把批处理代码改成单例模式之后,500 张图片从跑到第 87 张就卡死,变成完整跑完且内存稳定在 300MB 附近。
4.4 复杂背景下的误识别、多识别
现象:图片背景是花纹、深色渐变或带水印时,识别结果里多出来一堆莫名其妙的文字,或者几个文字被识别成一个词。
原因:检测模型对背景纹理过于敏感,把花纹边缘当成了文字区域。rapidOCR 默认的检测阈值偏保守,背景稍复杂就容易误判。
解决:在RapidOCR初始化时传入det_db_thresh和det_db_box_thresh参数,把第一个参数从默认的 0.3 调到 0.4,把第二个参数从默认的 0.6 调到 0.7。改完之后,文字区域检测会更谨慎,漏检率轻微上升,但误检率明显下降。对于水印场景,还可以在预处理阶段先用cv2.fastNlMeansDenoisingColored做一次降噪,效果更明显。
4.5 HTML 结构返回后文字重叠、排版错乱
现象:识别结果正确,坐标也正确,但 HTML 里两段文字重叠在一起,或者同一行的文字被拆成了多行。
原因:检测框的坐标是各自独立的,没有做行合并和排序处理。OCR 检测结果是「哪里有文字就框哪里」,同一行文字可能因为字间距被拆成多个框,也可能因为倾斜角度导致上下错位。
解决:在html_builder.py里增加一行分组逻辑——把 y 坐标接近的文字块归到同一行,再按 x 坐标排序。常见方法是取 box 中心的 y 值,如果中心 y 坐标相差小于某个阈值(比如文字高度的 0.6 倍),视为同一行;然后在该行内按 x 值从小到大排列,再输出到一个统一的<div style="display:flex">容器里。这样输出的 HTML 在视觉上更接近真实阅读顺序,而不是一坨散落的文本框。
5. 进阶用法:模型替换、参数调优、结构化输出的最佳实践
5.1 自定义印刷体模型的替换路径
如果你要识别特殊字体或数学公式、乐谱这类专用符号,通用的预训练模型是不够的。替换方案是:用 PaddleOCR 的模型库训练或微调一个专用模型,导出为 inference 模型,再用 paddle2onnx 工具转换成 ONNX,最后把三个 ONNX 文件替换到 models 目录,同时更新 keys.txt。这套流程的本质是——源码的输出 HTML 逻辑不用动,因为后续处理和识别结果的具体内容无关。这是这个架构最大的灵活性来源。
5.2 置信度阈值该设多少
置信度决定了哪些低质量识别结果会被直接丢弃。数值太高,真实存在的模糊文字会被过滤掉;数值太低,无关噪声会被当作文字输出。我的经验是:对清晰打印体图片,阈值设为 0.5 即可;对手机拍摄的或有压缩噪声的图片,降到 0.3 更稳妥。如果你发现 HTML 里出现大量半透明高亮框但没内容,大多是置信度阈值过低,导致 OCR 把一堆低置信度的识别结果也输出了。
5.3 一个可直接使用的验证脚本
写一个快捷验证脚本,用来快速判断整套环境的安装是否正确、模型文件是否配套、输出是否符合预期:
from src.ocr_engine import OcrEngine from src.html_builder import build_html if __name__ == "__main__": engine = OcrEngine( det_path="models/det.onnx", cls_path="models/cls.onnx", rec_path="models/rec.onnx", keys_path="models/keys.txt" ) result = engine.recognize("test_images/sample.png") if result: # 打印第一段识别文本和置信度,快速检查结果 print(f"首次识别:{result[0][1]},置信度 {result[0][2]:.2f}") html = build_html(result, 960, 540) with open("output.html", "w", encoding="utf-8") as f: f.write(html) else: print("未检测到文字,请检查图片或调整阈值")这个脚本把整条链路压缩到了一分钟内跑通:加载三个模型 + 读图 + 推理 + 输出 HTML。每次改完代码我强制自己先跑一遍这个脚本,再去做批量任务,避免带着低级错误浪费一整轮处理时间。从那以后,凡是用这套源码的场合,我都会在改完任何一行核心代码后执行一遍这个验证流程,这个习惯救过的失误次数我自己都数不清了。希望帮到你,少走一段弯路,把更多时间花在业务处理而不是调试模型路径上。
本文还有配套的精品资源,点击获取