十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于Keras的EAST+CRNN自然场景倾斜文字检测识别实战

基于Keras的EAST+CRNN自然场景倾斜文字检测识别实战 简介这是一套基于Python与Keras后端TensorFlow实现的图像文字检测与识别项目面向希望入门OCR领域、或需要完成毕设/课程设计的学习者。项目主要包含EAST/AdvancedEAST文字检测模型与CRNNCTC文字识别模型EAST支持90°至-90°任意角度文字定位能框出中文、英文、数字、符号等目标CRNN负责不定长序列识别并通过CTC进行解码整体覆盖从检测到识别的完整OCR流程。压缩包共32个文件以Python源码19个py文件为主另含8张用于效果验证的jpg图片、3份md说明文档以及2个txt配置文件整体体积仅957KB结构紧凑适合快速查看与学习。目前已有164人学习浏览。资源提供了environment.txt等环境依赖说明方便搭建Python3.6TensorFlow1.14Keras2.1.6运行环境代码模块划分清晰可作为图像文字识别方向的入门参考也适合在此基础上扩展训练或改进模型。1. 基于Keras的EASTCRNN自然场景倾斜文字检测识别的开源实现路边随手拍下的广告牌识别结果往往不是乱码就是漏字原因在于场景文本不像扫描件那样横平竖直可能出现30度、60度甚至接近垂直的旋转。单纯套用传统OCR接口检测框先就把文字砍掉了一半。你从开源社区拿到的这套ocr_chinese-master其实拆开看是两条清晰的流水线EAST模型负责把文字区域当目标检测来做直接回归出四个角点坐标从而覆盖90°到-90°的任意角度CRNN接手识别利用CTC解决不定长序列对齐问题输出中文、英文、数字和符号的混合文本。整套代码基于KerasTensorFlow目录结构里east、crnn、data、net、model、test划分得比较规整适合想在国内做图像文字检测毕业设计的同学也适合已有深度学习基础、希望动手串一遍检测加识别全流程的工程师。2. EAST/AdvancedEAST文本检测网络的输出通道与旋转框回归实现2.1 特征融合与四边形回归EAST的算法底色EAST的核心思路是去掉传统文本检测中候选区域、分类、细化等中间步骤直接用全卷积网络输出文本与非文本的得分图以及文本框的几何参数。在east/net和east/model目录下代码先通过主干网络提取特征图再仿照FPN把不同尺度的特征图逐级上采样并拼接让浅层空间细节和深层语义信息同时出现在最后的预测图上。这样做的原因很直接自然场景里的中文招牌可能很大路面上的小字又很小单一尺度的特征图很难兼顾两个极端。经过特征融合之后网络最后接卷积层输出两类信息一个是score_map表示每个像素属于文本区域的概率另一个是geo_map在EAST原版里是四个通道表示当前像素到旋转矩形上、右、下、左边界的距离。AdvancedEAST则在这个基础上做了改动把几何输出拆成更细的像素级分割和偏移量同时保留多角度信息所以在处理倾斜文本时召回率更高。原项目描述里提到文字方向支持90°到-90°任意角度实际上就是依赖这一类四边形回归输出而不是先做图像旋转再送入识别模型。检测阶段最终把回归出的距离转换成四个点坐标再执行Locality-Aware NMS合并重叠框返回给调用方。下表是我整理出的常见输出通道含义你在阅读east包内代码时可以用它对照每一层张量的shape。输出通道典型shape含义score_map1 × H/4 × W/4每个像素属于文本区域的置信度范围0~1geo_map4 × H/4 × W/4像素到旋转框上、右、下、左四边距离EASTangle_map1 × H/4 × W/4旋转框角度信息部分实现中并入geo_mapadvanced_geo5 × H/4 × W/4文本内部分数与四个顶点偏移用于生成任意四边形2.2 加载模型并调用predict_east.py参数的含义和调整在实际运行predict_east.py时我一般会先看一眼脚本对图片的预处理。代码通常会先对输入图做等比例缩放把短边控制在640左右长边控制在2560附近然后归一化到[-1,1]区间。缩放的原因是EAST主干网络下采样4倍输入尺寸太小会直接丢失小字文本特征太大则占用过多显存训练和推理时间都会显著上升。下面这段代码可以看作predict_east.py的核心流程按注释可以把它装到自己的推理脚本里import cv2 import numpy as np from east.net.model import east_network from east.data.preprocess import resize_image, normalize def run_east_detector(image_path, score_thresh0.9, nms_thresh0.2): original cv2.imread(image_path) img, ratio resize_image(original, short_size640, long_size2560) # 构建网络并加载权重这里用east/model中的结构 score_map, geo_map east_network(trainingFalse) model.load_weights(east/weights/east_model.h5) # 归一化后增加batch维度输入网络 logits model.predict( np.expand_dims(normalize(img), axis0) ) # 后处理阈值过滤背景NMS合并重叠框 boxes lanms( score_map[0], geo_map[0], score_threshscore_thresh, nms_threshnms_thresh ) # 还原到原图坐标 return boxes / ratio这段代码里的score_thresh和nms_thresh是我调参时最关心的两个值。score_thresh控制文本区域置信度门槛设置太高容易把低对比度文字框漏掉设置太低又会把水泥纹理、树叶阴影误判成文本nms_thresh则控制重叠旋转框的合并力度值偏大时相邻的小框会被激进合并可能把两行文字框成一个框值偏小时同一行文字会被切碎。处理横幅或路牌这类大目标时我一般会把score_thresh放在0.85到0.92之间而处理密集小字时降到0.75左右这个取舍要结合后续识别效果来回调。3. CRNNCTC文字识别从卷积特征到不定长文本序列3.1 CNN特征序列化和双向RNN为什么CRNN能处理不定长检测模型只负责把文字区域框出来真正的文字内容还要交给识别模型。这套项目里的CRNN不是普通的CNN分类网络而是把卷积特征、循环网络和CTC转录组合在一起形成一个能输出变长结果的序列识别器。第一层仍是卷积层作用是提取图像中的视觉特征得到一张高度固定、宽度可变的特征图。接着把特征图按列切分每一列作为一个时间步送入双向LSTM这样网络就能学习文字之间的上下文依赖比如“北京”后面的“市”会比单独识别时更可靠。CTC在这里解决的是对齐问题。一张包含5个字符的图片经过卷积和LSTM后可能有15到20个时间步比真实字符数多得多。CTC会为每个时间步输出一个字符概率分布然后通过引入空白符号把连续重复的字符折叠成一个并去掉空白位置最终得到一条字符序列。因为这个转录过程允许网络输出序列长度大于标签长度所以识别模型不需要提前切割单字也不需要固定输入宽度这正是这套CRNN实现能够处理不定长中文文本的原因。crnn目录下同样有net、model、test子目录predict_crnn.py的推理逻辑可以压缩成下面这段from crnn.net.model import crnn_network from crnn.utils import decode_ctc # 识别前把检测到的文本框裁剪出来并缩放到固定高度 image cv2.imread(crop.jpg) h, w 32, int(image.shape[1] * (32 / image.shape[0])) # 宽度对齐到4的整数倍避免卷积下采样后出现奇偶错位 w (w // 4) * 4 image cv2.resize(image, (w, h)) # 将输入转成(W,H,C)布局对应CRNN的序列化输入 input_seq np.transpose( image[np.newaxis, :, :, :], (0, 2, 1, 3) ) model crnn_network(trainingFalse) model.load_weights(crnn/weights/crnn_ctc.h5) logits model.predict(input_seq) # CTC解码beam_width1表示贪心更大值用beam search text decode_ctc(logits, alphabet, beam_width1) print(text)逻辑上这段代码把输入图像的高度固定为32宽度跟随原始宽高比变化并取整到4的倍数之后送入CRNN网络得到每个时间步的字符概率。最后一步的decode_ctc会根据字符表alphabet把概率序列转换成最终文本。如果你要在自己的数据集上重新训练alphabet必须重新生成并和训练时保持一致否则解码出来会是乱码。3.2 CTC解码与工程调用predict_crnn.py中的参数decode_ctc里的参数直接影响识别耗时和准确率。下表是几个我实际调过的基础参数参数默认值实际建议beam_width1句长较短时设为1即可长文本设5~10提升准确率但耗时线性增加blank_index0必须和模型训练时blank字符索引一致常见为0alphabet自定义字符表需要覆盖中文常用字、英文大小写、数字以及空格等特殊符号merge_repeatedTrueCTC默认合并重复字符关闭后连续相同字会被拆开工程上最容易踩的坑是alphabet的顺序变动。很多人在模型训完后才追加新字符结果测试时decode出来的索引全部错位。另一个坑是输入宽度没有对齐导致CNN特征序列长度与RNN期望长度不一致报错维度不匹配。看到这类报错时优先检查w (w // 4) * 4这步是否生效因为卷积下采样倍数必须整除输入宽度。4. 端到端串联predict.py流程梳理与Python环境配置实测4.1 从检测框到识别图图像裁剪与透视变换如果你打开项目根目录下的predict.py会看到它把检测和识别两个阶段串成了同一条流水线。流程并不复杂读图后先交给EAST拿到文本框四个角点然后根据角点做透视变换把旋转、倾斜的四边形区域拉正成水平方向最后把矫正后的截图送给CRNN识别。关键步骤是透视变换矩阵的计算常见做法是用cv2.getPerspectiveTransform取四个顶点映射到正矩形再执行cv2.warpPerspective。这里要注意四个角点的顺序必须一致通常按左上、右上、右下、左下的顺序排列否则矫正出来的图像是旋转了90度的CRNN会把它当成竖排文字处理识别准确率会大幅下降。asset目录下放了两张测试图23_re.jpg和51_re.jpg对应检测识别后的结果图23_0_.jpg和51_0_.jpg。你可以直接在根目录执行python predict.py --image asset/23_re.jpg脚本将依次输出检测框数量和识别文本。如果只向控制台输出文本我建议自己在predict.py里加一行cv2.imwrite把透视矫正后的图像存下来这样能快速确认检测框是否把文字完整包住。4.2 environment.txt依赖安装与版本兼容性项目开发环境写的是Python 3.6、TensorFlow 1.14.0、Keras 2.1.6environment.txt里也给出了可复现的依赖清单。我实际安装时习惯先创建独立虚拟环境防止把系统里的TensorFlow 2.x环境搅乱。python -m venv ocr_env source ocr_env/bin/activate pip install -r environment.txt如果你的机器已经装过OpenCV注意它可能会自动升级numpy而Keras 2.1.6对numpy版本有隐性要求。下面是我整理出的依赖兼容性要点依赖版本建议踩坑点Python3.6.x3.8以上与TensorFlow 1.14的whl包不兼容TensorFlow1.14.0不要换成2.xKeras 2.1.6调用tf接口会报错Keras2.1.6需要设置KERAS_BACKENDtensorflowopencv-python4.1.x新版大量依赖可能与numpy版本冲突pillow6.x过高版本在Python 3.6下可能安装失败环境配置是最消耗耐心的部分。如果启动时出现AttributeError: module tensorflow has no attribute placeholder多半是Keras调用到了TensorFlow 2.x的API检查一下虚拟环境里实际安装的TensorFlow版本必要时卸载重装为1.14.0。如果加载h5权重时报结构不匹配先确认Keras版本因为不同版本对权重键名记录方式不一样。5. 进阶角度文本调参、验证方法与常见坑点5.1 score阈值与NMS参数对旋转框的影响之前提到score_thresh和nms_thresh是检测阶段两个关键参数实际测试中它们对最终结果的影响往往是牵一发动全身。score_thresh太高时低对比度的白色字体会被直接过滤掉太低时复杂背景会产生大量虚框。我先用score_thresh0.9跑一遍测试图如果某张图掉了文字再以0.05的步长往下调同时观察NMS是否把相邻文本块合并。nms_thresh则负责控制框的重叠度对弯折的灯箱文字我一般从0.2起步调低到0.1会让同一行文字被切分成多个框调高到0.3以上又可能把上下两行并成一个误检框所以最好结合检测框的可视化结果去判断。5.2 用自带测试图验证全流程项目自带的asset目录里有多张测试图其中res.jpg可能是一张已经合并检测和识别结果的可视化图23_re.jpg和51_re.jpg则是输入。验证时我建议按这个顺序做先只跑predict_east.py把检测框画到图上保存下来确认每个文本框的角点是否紧贴文字边缘再切出其中一个框单独跑predict_crnn.py确认识别结果和字符表没有错位最后跑完整predict.py对比输出文本和原图内容。如果检测框正确但识别结果是空的多半是透视矫正时高度设置不合理CRNN要求高度固定为32矫正后图像宽高比极端时会丢失横向特征。遇到这类问题可以在送入识别前对矫正图做一次高保持缩放而不是直接把原裁剪图塞进模型。另外environment.txt没有列出的lanms编译依赖也需要提前装好否则EAST后处理阶段会报缺少Cython扩展这个错误经常让初学者误以为是TensorFlow版本问题。本文还有配套的精品资源点击获取
返回列表