融合U-Net图像分割与CNN端到端识别的中文车牌智能识别系统实战

张开发
2026/4/6 5:04:31 15 分钟阅读

分享文章

融合U-Net图像分割与CNN端到端识别的中文车牌智能识别系统实战
1. 为什么选择U-NetCNN做车牌识别第一次接触车牌识别项目是在2018年当时尝试过传统图像处理方法比如边缘检测模板匹配。实测下来遇到倾斜车牌就束手无策识别率不到60%。后来看到医疗影像分割的论文里U-Net表现惊艳就想着能不能用在车牌定位上。经过3个月的数据标注和模型调优最终在测试集上达到了98%的定位准确率。U-Net的编码器-解码器结构特别适合做像素级分割。它的跳跃连接skip connection能保留不同尺度的特征这对识别各种尺寸的车牌至关重要。我做过对比实验同样的数据集下普通CNN的IOU交并比只有0.7而U-Net能达到0.92。CNN端到端识别则解决了传统OCR字符切割的痛点。以前需要先做字符分割再单独识别遇到模糊、倾斜字符就出错。现在直接用多标签分类输入整张车牌图输出7个字符概率实测识别速度提升5倍错误率降低80%。2. 数据准备从标注到增强的完整流程2.1 标注工具的选择与技巧刚开始用LabelImg标注矩形框发现对倾斜车牌不友好。后来改用LabelMe标注多边形虽然工作量增加30%但定位精度提升明显。这里有个血泪教训一定要先resize图片再标注我有次偷懒先标注后resize导致mask出现中间值不得不重标300多张图。推荐的文件结构dataset/ ├── images/ # 原始图片(512x512) ├── labels/ # 标注json文件 └── masks/ # 生成的二值mask标注时注意车牌边缘保留3-5个像素缓冲带遇到反光车牌要完整标注反光区域遮挡车牌按可见部分标注2.2 数据增强实战方案单纯靠真实数据很难覆盖所有场景我的增强方案包括几何变换旋转±15°、透视变换20%幅度光照调整过曝gamma0.5、欠曝gamma1.5噪声注入高斯噪声σ0.05、椒盐噪声2%密度def augment_image(img): if random.random() 0.5: angle random.uniform(-15, 15) img rotate(img, angle, reshapeFalse) if random.random() 0.7: gamma random.choice([0.5, 1.5]) img adjust_gamma(img, gamma) return img最终生成3.3万张训练样本各省份数据分布均衡性对比原始数据提升40%。3. U-Net模型搭建与训练技巧3.1 改进的U-Net结构在原始U-Net基础上做了三点改进卷积层后加入BatchNorm使用LeakyReLUalpha0.1代替ReLU在跳跃连接处添加Dropout0.5def conv_block(inputs, filters): x Conv2D(filters, 3, paddingsame)(inputs) x BatchNormalization()(x) x LeakyReLU(alpha0.1)(x) return x def unet_model(): # 编码器部分 c1 conv_block(inputs, 8) c1 conv_block(c1, 8) p1 MaxPooling2D()(c1) # ...中间层省略... # 解码器部分 u4 Conv2DTranspose(16, 3, strides2, paddingsame)(c5) u4 concatenate([u4, c4]) u4 Dropout(0.5)(u4) # ...其余层省略...3.2 训练参数调优心得用Adam优化器时发现初始学习率0.001会导致震荡最终设为0.0005稳定收敛。batch_size根据显存设为16在RTX 2080Ti上训练100个epoch约需6小时。关键指标变化训练集loss从1500降到250IOU从0.3提升到0.92验证集准确率达95%注意训练早期建议关闭数据增强等loss基本稳定后再开启否则可能难以收敛4. 车牌矫正的几何魔法4.1 边缘检测的坑与解决方案最初直接用cv2.findContours找轮廓遇到模糊车牌时经常检测失败。后来先做以下预处理高斯模糊kernel5×5Otsu阈值化形态学闭运算kernel3×3gray cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY) blur cv2.GaussianBlur(gray, (5,5), 0) _, thresh cv2.threshold(blur, 0, 255, cv2.THRESH_OTSU) kernel np.ones((3,3), np.uint8) closed cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)4.2 透视变换的精髓找到最小外接矩形后关键是要准确定位四边形的四个顶点。我的方法是结合点到矩形顶点的距离权重25%点到上下边的距离权重75%def get_vertices(contour, rect_points): # rect_points是排序后的矩形顶点 [左上,左下,右上,右下] vertices [] for point in contour: x,y point[0] # 计算到四条边的距离 d_top distance_to_line(x,y, rect_points[0], rect_points[2]) d_bottom distance_to_line(x,y, rect_points[1], rect_points[3]) # 综合评估 score 0.75*d_top 0.25*min( distance(x,y,rect_points[0]), distance(x,y,rect_points[2])) # 找出分数最低的点作为候选顶点 # ...省略具体实现... return vertices实测该方法对倾斜60°以内的车牌矫正成功率达98%比传统方法提升35%。5. CNN端到端识别实战5.1 多标签输出结构设计车牌识别本质是7个分类任务的组合1个省份6个字符。在keras中可以通过多输出实现inputs Input(shape(80, 240, 3)) x Conv2D(16, 3, activationrelu)(inputs) x MaxPooling2D()(x) # ...中间卷积层省略... x Flatten()(x) outputs [ Dense(65, activationsoftmax, namefchar_{i})(x) for i in range(7) ] model Model(inputsinputs, outputsoutputs) model.compile( optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy] )5.2 数据不均衡处理发现藏青等车牌识别率低采取以下措施对稀有样本过采样oversampling在loss函数中增加类别权重针对性数据增强增加模糊、倾斜class_weight { 0: 1.0, # 京 25: 3.0, # 藏 28: 2.5, # 青 # ...其他类别... } model.fit( X_train, [y1, y2, y3, y4, y5, y6, y7], class_weight[class_weight]*7 )最终各省份识别准确率差异从最高40%缩小到15%以内。6. 工程化部署建议在实际项目中还需要考虑多尺度检测用图像金字塔处理不同大小车牌后处理规则根据车牌规则校验结果如第二位必为字母模型量化将float32转为int8推理速度提升3倍异常处理对低置信度结果触发重新检测def validate_plate(chars): 校验车牌规则 provs [京,沪,津,渝,冀,晋,蒙] if chars[0] not in provs: return False if not chars[1].isupper(): return False # ...更多规则... return True这套系统在停车场实际部署时晴天识别率99.2%雨天降至97.5%后续通过增加雨天训练数据提升到98.8%。

更多文章