多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

基于Keras的自然场景文字检测识别:EAST与CRNN实战解析

基于Keras的自然场景文字检测识别:EAST与CRNN实战解析 简介这套基于Python实现的图像文字检测与识别工程以Keras和TensorFlow搭建整合EAST/AdvancedEAST文字检测模型与CRNNCTC文字识别模型。适合希望学习OCR技术的小白及进阶学习者也可用于毕业设计、课程设计、工程实训或初期项目立项帮助理解自然场景下的文字定位与识别流程。压缩包共32个文件包含19个Python源码、8张样例图片、3份说明文档和2个环境配置文件整体仅957KB模块划分清楚便于按需查看。EAST模型可检测90°到-90°间任意角度的中英文、数字及符号采用目标检测框形式返回坐标CRNN模型联合CTC算法支持不定长文字识别覆盖数据、网络、模型、测试等常用模块环境配置文本可帮助快速准备运行依赖。已有164人学习浏览适合作为参考资料进行调试、排错和二次开发。1. 自然场景文字检测与识别这份Keras资源到底能干什么做了几年OCR相关的落地项目我越来越认同一个判断自然场景下的文字检测和识别从来不是调个库就能解决的问题。光线、倾斜、遮挡、模糊任何一样都能让传统的图像处理方案当场翻车。这份基于Python实现的资源用Keras TensorFlow组合实现了完整的检测 识别两段式方案——EAST/AdvancedEAST负责检测CRNNCTC负责识别代码全是Python依赖清晰适合做毕设、课程设计、工程实训的起点。和那些开箱即用的黑盒OCR不同它把模型结构、预测脚本、测试图片全部摊开放在你面前。你拿到的不是成品是完整可改的实验框架前提是你得会看代码、会调参。2. 环境搭建与工程结构Python 3.6 TensorFlow 1.14 的兼容性细节2.1 environment.txt 与版本组合的底层逻辑这个资源的开发环境写得很明确Python 3.6 TensorFlow 1.14.0 Keras 2.1.6。这三个版本号不是随手写的它们之间存在强绑定关系。Keras 2.1.6发布于2018年左右走的是独立Keras 后端TensorFlow路线和后来内置在TensorFlow 2.x里的tf.keras完全是两种生态。TF 1.x时代用的是静态图 Session.run()模式而很多从Python 3.8以后才入行的开发者可能连tf.Session长什么样都没见过。先看依赖安装项目里带了environment.txt按官方README的说法pip install -r environment.txt我的建议是不要直接在全局环境里执行先建虚拟环境。常见做法是先用conda把Python版本锁死conda create -n ocr_env python3.6 conda activate ocr_env pip install -r environment.txt这里有一个必须提前说明的坑这个环境组合在Python 3.6上才能稳定跑通。TensorFlow 1.14官方支持的是Python 3.4到3.7Python 3.8及以上版本直接装不上1.14.0轮子。你要是机器上只有Python 3.10第一步就卡住了。我一般会先确认python --version再决定是装3.6还是用Docker拉一份旧镜像。资源正文里出现的environment.txt在压缩包里是真实存在的装完依赖后可以用pip list | grep tensorflow核对版本不是1.14开头就说明装错环境了。还有一点如果你想在这个环境里用OpenCV读图注意装的是opencv-python而不是opencv-contrib-python两个包的API在基础用法上差别不大但contrib版本体积更大、且会和某些预编译的TensorFlow轮子产生依赖冲突。我在复现时用的是opencv-python4.5.4.60这个版本在Python 3.6下还能正常装。2.2 工程目录east、crnn、asset各管什么解压开ocr_chinese-master.zip之后你能看到一个分得很清楚的结构。整个项目不复杂但目录划分是典型的检测/识别独立成包模式。把文件列表整理成这张表目录/文件职责east/文字检测子模块含data、net、model、test、predict_east.pycrnn/文字识别子模块含data、net、model、test、predict_crnn.pypredict.py主入口脚本串联检测与识别environment.txtpip依赖清单asset/测试图片如23_re.jpg、51_re.jpg、res.jpgREADME.md项目说明east目录下的net和model一般分别放网络结构定义和权重加载逻辑test目录里是单元测试或调试脚本。crnn目录结构类似。asset里的23_re.jpg、51_re.jpg是原始测试图51_0_.jpg、23_0_.jpg是检测后的裁剪图res.jpg是最终可视化结果我拿到后会先看这几张图——能看出项目作者预期的输出长什么样比先读代码高效得多。2.3 跑通predict.py从测试图到标注框环境装好后第一步不是改代码而是先用测试图跑一遍完整流程。一般来说项目根目录下直接执行python predict.py --image_path asset/23_re.jpg这个命令的含义是用默认权重对23_re.jpg执行完整的检测→识别流程。如果你在Windows环境注意路径分隔符如果报找不到模型文件多半是权重路径写死了相对路径。这时打开predict.py看一眼把模型加载部分改成os.path.join(os.path.dirname(__file__), east/model/xxx.h5)这种形式避免从其他目录启动时报错。跑通之后你的屏幕上应该会打印检测框坐标和识别出的文字或者在asset/下生成一张带框的结果图。这一步的意义在于验证环境不代表项目没问题——很多人在这一步能跑通但换成自己的图片就开始出各种幺蛾子后面的章节会把典型的坑一个个拆开。3. EAST/AdvancedEAST文字检测四坐标框输出与角度回归3.1 EAST网络结构U型特征融合与RBOX输出EASTEfficient and Accurate Scene Text Detector解决的核心问题是在自然场景下文字可能以任意角度出现普通的目标检测矩形框装不下倾斜文本。EAST的思路很直接——不搞候选区域不做文本框修正直接做像素级预测输出文本框的几何信息。网络主干是一个类似U-Net的结构通过卷积层逐级提取特征再在多个尺度的特征图上做融合输出的特征图最后分成两条分支一条是分类分支Score Map判断每个像素是否属于文本框内部另一条是几何分支Geometry Map预测文本框的位置和角度。在RBOX模式下一个文本区域会输出4个距离值上、右、下、左到文本框边界的距离加上1个旋转角度一共5个通道。这也是EAST的资源文件里预测结果经常是文本框四个顶点坐标的原因——从RBOX解码时把中心点和距离、角度换算回四角坐标就行了。实际在predict_east.py里加载模型后输入一张图中间会做一次resize。EAST默认的输入尺寸常见是320×320或512×512代码里通常用cv2.resize把长边缩到固定值保持宽高比。这里有个细节如果你的测试图长宽比很极端比如超宽横幅直接resize会导致文字被压扁。我一般会改成按长边等比例缩放做完检测后再把坐标映射回原图尺寸。3.2 AdvancedEAST角度回归的稳定性改进AdvancedEAST在原始EAST基础上做了几个改进最核心的一条是把角度回归的表示方式调整了。原始EAST用单个角度值来代表文本框旋转角但在接近90°和接近-90°的边界区域角度值会发生跳变导致模型训练不稳定。AdvancedEAST改用两个正交距离或改用四个距离值配合更平滑的角度编码来缓解这个问题。这份资源标题里写的是EAST/AdvancedEAST说明两个版本都能在当前代码里切换。你可以在east/net目录下找网络定义文件里的advanced开关或者在predict_east.py里看到两个分支。我的建议是直接默认用AdvancedEAST分支它在倾斜文本上的鲁棒性更好尤其是处理中文招牌、广告条幅这类歪着写的文字时。一个直观的经验用原始EAST分支识别45°左右倾斜文字框往往贴不紧文本切到AdvancedEAST后框的四个顶点能更精准地贴合文字四角。损失函数的差别主要在几何分支的平滑项上。用这个项目做毕设时AdvancedEAST对倾斜文本的回归优化本身就是一个很好的论文切入点。3.3 predict_east.py调用方式与坐标输出说明east模块的推理入口在east/predict_east.py。看文件名就知道它独立于识别模块可以单独跑。调用方式通常是python east/predict_east.py --img asset/51_re.jpg如果没有传参逻辑你也可以在脚本内部改成直接调用predict函数。我复现时的习惯是写一个小的封装# 参考结构实际以代码为准 import cv2 from east.model import EASTModel model EASTModel(weight_patheast/model/weights.h5) img cv2.imread(asset/51_re.jpg) # 假设模型提供 predict 方法返回文本框坐标和置信度 boxes, scores model.predict(img, threshold0.5) for box in boxes: # box 是4个顶点的 (x, y) 集合 print(box)threshold是置信度阈值决定哪些候选框会保留下来。这个参数在自然场景下非常敏感调太高如0.9容易漏检调太低如0.1会多出一堆背景误检。我一般从0.5起步根据测试图的效果上下浮动。坐标输出的格式EAST系列通常是[左上, 右上, 右下, 左下]四个点的顺序但不同实现可能不同拿到输出后建议先打印第一个框确认顶点顺序再画框验证。画框验证这一步至关重要。用cv2.polylines就能画但如果你直接用cv2.rectangle画外接正矩形就把EAST的角度信息丢了看不出效果。正确的画法是用cv2.polylines把四边形的四条边连起来。4. CRNNCTC文字识别不定长文本序列解码的完整链路4.1 CRNN三件套CNN特征 → BiLSTM → CTCEAST把文字区域框出来了接下来要做的就是把框里的内容转成字符串。CRNNConvolutional Recurrent Neural Network是这里的选择它的结构可以拆成三段理解。第一段是卷积层相当于视觉特征抽取器把输入图片转成一系列特征列。图片被竖直切分成多个片每片对应一个时间步的特征。第二段是双向LSTM对特征序列做上下文建模。第三段是CTC损失/解码层负责解决特征序列和文本序列长度不对齐的问题。这套组合在自然场景OCR里是经典方案字符识别只是它能力的子集。由于这是不定长文本识别没有预先定义文本长度所以训练时用的是CTC Loss而不是普通的交叉熵。CTC的核心思想是引入一个空白符blank token允许模型在每帧上独立预测然后在解码阶段用合并规则把重复字符和blank去掉恢复出原始文本。4.2 CTC解码pred_idx到文本的映射过程CTC解码是识别模型里最容易让人困惑的部分。模型输出的是一串帧级预测每帧预测的是某个字符的概率分布但相邻帧可能预测同一个字符CTC的处理规则是先把相邻帧中重复的字符合并再把blank位置删除举个例子模型输出的字符序列是[空, 中, 中, 文, 空, 文, 个, 个]其中空表示blank。先去重合并相邻项得到[空, 中, 文, 空, 文, 个]再删除blank最终结果是中文文个。注意如果序列里同一个字符被blank隔开它们不能合并这是一步关键逻辑。predict_crnn.py里解码部分一般长这样# 伪代码CTC贪心解码 def ctc_decode(pred_seq): # pred_seq 形状为 [time_steps, num_classes] result [] prev None for frame in pred_seq: char_idx frame.argmax(axis-1) # 每一帧取概率最大的字符 if char_idx blank_index: # blank 跳过 prev None continue if char_idx ! prev: # 相邻去重 result.append(char_idx) prev char_idx return .join([index_to_char[i] for i in result])这段代码的argmax是贪心解码简单直接适合做流程验证。如果要更高精度需要用beam search但在这个资源里贪心够用。参数方面blank_index一般设为0或最后一个类别索引具体看字符集文件的定义顺序。我用过一个更稳妥的调试方法把模型输出的pred_seq维度打印出来time_steps应该和输入图像的宽度正相关如果time_steps太小比如小于文本长度那多半是输入图像在宽度方向被压得太狠信息丢了。4.3 检测识别串联predict.py的完整工作流项目根目录的predict.py是真正的入口它把east和crnn串了起来。流程不复杂先加载EAST模型读入原图得到文本框坐标再对每个框做透视变换把倾斜框拉成水平矩形然后把裁剪后的矩形区域送入CRNN识别得到文本最后把文本字符串和坐标画回原图。这里有一个常见错误认知很多人以为识别模型的输入是原始检测框的像素截图模型负责智能地处理倾斜。实际上CRNN的输入必须是水平方向排列的文本行因为它的特征提取方式是按列切分的。所以倾斜的检测框一定要先做仿射变换转正这一步通常在predict.py里完成。串联时的关键参数有两个一个是检测阶段的threshold决定框的数量另一个是识别阶段的max_text_length决定识别序列最大长度。如果检测框漏了识别再好也没用如果max_text_length设太小长句子会被截断。我在复现时一般把max_text_length设为32遇到套餐名、地址这种一长串的中文再往上调。5. 避坑指南环境、模型、图片预处理三个方向的踩坑记录5.1 现象TensorFlow 2.x环境直接跑代码报AttributeError: module tensorflow has no attribute Session原因代码里大量使用tf.Session()、tf.placeholder等TF 1.x APITensorFlow 2.0移除了这些接口。很多人在新环境里装了个tensorflow 2.x跑项目秒挂。解决严格按resources里要求的版本来。创建Python 3.6环境pip install tensorflow1.14.0 keras2.1.6。如果你必须在TF 2.x下运行可以在程序开头加import tensorflow.compat.v1 as tf tf.disable_v2_behavior()但这只是能跑层面的应急方案模型权重的兼容性、Keras后端绑定都可能出问题。我的建议是直接用旧环境不要跟版本较劲。5.2 现象检测框角度明显不对水平文字框成了斜框或者斜体文字框成了正框原因EAST对角度回归的敏感度受输入图片resize方式影响大。直接cv2.resize改变了文本的长宽比模型在训练时见过的是等比缩放后的特征分布一旦比例失配角度预测就偏。解决修改检测脚本按长边等比缩放记录缩放比例检测完再把坐标还原。伪代码h, w img.shape[:2] scale 512.0 / max(h, w) resized cv2.resize(img, (int(w * scale), int(h *scale))) boxes model.predict(resized) boxes / scale # 坐标映射回原图5.3 现象CRNN识别结果全是空白或者输出了一堆不相关的符号原因输入图像的高度不满足CRNN的卷积下采样要求。CRNN经过卷积层后特征图高度会被压缩到一个很小的值比如原本32像素高变成1像素高如果输入图高度不足16像素特征高度直接归零模型输出全是无效帧。解决在送入CRNN之前强制把文本行图片高度resize到32像素宽度按比例缩放。此外检查检测框是否把文字裁得太紧留出的边距不够。我一般会在裁剪矩形上下各多切4个像素。5.4 现象中文字符识别准确率低常见字都错原因模型权重是在特定字符集上训练的如果项目里的字符集文件通常是label.txt或char_dict.json覆盖不全生僻字、特殊符号就会映射到错误的索引位置。还有一个常见原因图片没有做灰度预处理CRNN的输入通道数不符合训练时的设定。解决先确认识别模型训练时的输入格式是单通道灰度还是三通道RGB。然后查看字符集长度和模型输出层的类别数是否匹配。字符集不足时要么换权重要么用现有权重在自己的数据上做微调。5.5 现象运行中内存持续增长批量预测时直接OOM原因EAST和CRNN两个模型同时常驻内存再加上TensorFlow 1.x的图模式会缓存中间结果批量处理时图片叠堆很容易把显存或内存吃满。解决限制TensorFlow的显存占用按需增长而不是一次性占满import tensorflow as tf config tf.ConfigProto() config.gpu_options.allow_growth True sess tf.Session(configconfig)当然很多时候你根本不是在GPU上跑——CPU版本不会显存OOM但会在RAM上爆。这时候把一次处理的图片数量降下来每次只处理一张图并且调用完及时del大数组。这是最朴素也最管用的方案。6. 进阶使用批量推理、自定义字符集与性能调优最后说点能直接抄的进阶操作。我拿到项目后的第二个习惯是把它的单张测试脚本改造成目录级批量推理这样一整批测试图就能一次跑完。常见做法是加一段遍历逻辑import os import cv2 img_dir asset for name in os.listdir(img_dir): if not name.endswith((.jpg, .png)): continue path os.path.join(img_dir, name) img cv2.imread(path) boxes, texts ocr_pipeline(img) print(name, texts)这里的ocr_pipeline是封装好的检测透视变换识别函数。如果原项目只提供了单张入口你就需要自己写这个封装正好把前面章节讲到的串联逻辑练一遍。自定义字符集是很多人忽略的一个点。crnn/data下的字符集文件决定了模型能识别哪些字如果你做的是中文项目但默认字符集只有几千个常用字地名、人名里的生僻字就会全部变成口或。换字符集之后模型输出层的类别数会改变必须重新训练或微调才能生效不要指望直接换文件就能用。性能调优方面如果纯CPU上跑觉得慢优先检查是不是每次预测都在重复加载权重。正确做法是进程启动时加载一次模型后续对所有图片复用同一个模型实例。另外把图片缩放参数统一起来避免每张图都触发重新计算scale逻辑。这些小改动合在一起吞吐量能提升好几倍。这套项目做完我最大的一个习惯变化是拿到OCR资源后先看环境版本再逐行读预测脚本最后才跑测试图。顺序反过来的话报错会让你误以为模型有问题其实只是环境不匹配。血泪经验换来的教训。希望帮到你。本文还有配套的精品资源点击获取
返回列表