基于STM32与OV7670的车牌识别系统:从图像采集到字符识别的全流程解析

发布时间:2026/7/10 0:07:24

基于STM32与OV7670的车牌识别系统:从图像采集到字符识别的全流程解析 1. 硬件选型与系统架构设计在车牌识别系统的硬件选型上STM32F103系列单片机搭配OV7670摄像头模块是个经典组合。我当年第一次做这个项目时为了省成本选了最便宜的F103C8T6结果发现内存根本不够用后来换成F103ZET6才解决问题。这里分享几个硬件选型的经验MCU选择建议至少选择带有256KB Flash和64KB RAM的型号像STM32F103ZET6或者F407系列。图像处理需要大量内存来存储中间结果小内存型号跑起来会很吃力。摄像头模块OV7670虽然分辨率只有30万像素640x480但对于车牌识别已经够用。实测在3米距离内这个分辨率可以清晰捕捉到车牌字符。要注意选择带FIFO的版本否则图像采集会丢帧。显示设备2.4寸以上的TFT LCD屏比较合适既能显示实时图像又能展示识别结果。我用的是ILI9341驱动的240x320屏幕效果不错。系统架构上整个流程可以分为三个主要部分图像采集层OV7670负责拍摄原始图像通过DCMI接口传输到STM32图像处理层STM32进行二值化、车牌定位、字符分割等操作识别输出层将识别结果显示在LCD屏上也可以通过串口发送到上位机2. 图像采集与预处理实战OV7670的配置是第一个难点。这个摄像头模块有近百个寄存器需要配置新手很容易在这里卡住。我建议直接使用现成的初始化代码重点调整以下几个参数// 关键配置示例 OV7670_WriteReg(0x12, 0x80); // 复位所有寄存器 OV7670_WriteReg(0x3a, 0x04); // 设置输出格式为RGB565 OV7670_WriteReg(0x40, 0xd0); // 设置图像尺寸为QVGA OV7670_WriteReg(0x1e, 0x30); // 手动调整曝光采集到的原始图像通常会有噪点和光照不均的问题需要先做预处理。这里分享一个实用的二值化处理方法灰度转换将RGB565格式转换为灰度值uint8_t gray (R*30 G*59 B*11) / 100; // 经典权重公式自适应阈值不要简单用固定阈值127我推荐用大津法Otsu自动计算最佳阈值去噪处理用3x3中值滤波消除孤立噪点实测发现在光照条件变化大的场景自适应阈值的效果比固定阈值好很多。有一次我在停车场测试早上和下午的光照差异导致固定阈值方法完全失效改用大津法后识别率提升了40%。3. 车牌区域定位技巧定位车牌是整个系统最关键的环节。经过多次实验我总结出两种可靠的定位方法3.1 基于跳变点的定位法这种方法特别适合蓝底白字的车牌。原理是利用车牌区域特有的颜色跳变特征水平扫描统计每行的黑白跳变次数for(int y0; y240; y){ int changes 0; for(int x1; x320; x){ if(pixel[x][y] ! pixel[x-1][y]) changes; } if(changes THRESHOLD) // 可能是车牌行 }垂直扫描在确定的水平范围内找左右边界颜色验证检查候选区域是否符合车牌颜色特征蓝底3.2 基于HSV色彩空间的定位对于特殊颜色的车牌如黄底黑字HSV方法更可靠将RGB转换到HSV空间根据色调(H)值筛选蓝色或黄色区域用形态学操作膨胀/腐蚀连接相邻区域我在项目中同时实现了这两种方法通过一个标志位来切换。实际测试表明在晴天HSV方法更稳定而在阴天跳变点方法表现更好。4. 字符分割与识别优化成功定位车牌后接下来要把字符一个个分开。这里有几个容易踩的坑字符粘连问题特别是数字8和0容易连在一起。我的解决方案是先做垂直投影找到明显的分割点对于模糊的位置再用连通域分析。倾斜校正车牌不水平时需要先做旋转校正。我用的是Hough变换找倾斜角度实测在±15度内效果不错。归一化处理把分割出的字符统一缩放到24x50像素方便后续识别。字符识别方面最简单的就是模板匹配法。我收集了各省简称、字母和数字的字模存储在数组中const uint8_t CHAR_A[150] {0x00,0x78,0x84,...}; // A的字模数据 const uint8_t CHAR_B[150] {0x00,0xFC,0x84,...}; // B的字模数据识别时计算相似度int match_score 0; for(int i0; i150; i){ uint8_t input_byte char_image[i]; uint8_t template_byte CHAR_A[i]; for(int j0; j8; j){ if((input_byte (1j)) (template_byte (1j))) match_score; } }为了提高识别率我加了两个优化加权匹配字符中心区域的像素匹配权重更高动态阈值根据环境光照自动调整匹配阈值5. 性能优化与实战经验在资源有限的STM32上跑图像处理性能优化是必须的。以下是我总结的几个关键点内存管理使用DMA传输图像数据减少CPU占用合理分配内存池避免频繁动态分配将大数组声明为静态变量算法优化采用窗口扫描代替全图处理使用查表法加速RGB到灰度的转换对二值化图像使用游程编码压缩实时性保障设置超时机制单帧处理不超过200ms分级处理先快速定位车牌再精细识别字符关键代码用汇编重写记得有一次调试时系统总是运行几分钟就死机。后来发现是内存泄漏导致的通过在每次识别完成后强制清空缓冲区解决了问题。这也提醒我在嵌入式开发中要特别注意资源管理。6. 完整代码框架解析整个项目的代码结构如下├── Drivers │ ├── STM32F10x_StdPeriph_Driver // 标准外设库 │ └── OV7670_Driver // 摄像头驱动 ├── Application │ ├── algorithm.c // 核心算法 │ ├── lcd.c // 显示驱动 │ └── main.c // 主程序 └── Utilities ├── delay.c // 延时函数 └── debug.c // 调试工具主程序的工作流程while(1){ if(按键触发 || 定时到达){ OV7670_CaptureFrame(); // 采集一帧图像 Image_Preprocess(); // 预处理 LicensePlate_Locate(); // 车牌定位 if(定位成功){ Chars_Segment(); // 字符分割 Chars_Recognize(); // 字符识别 Display_Result(); // 显示结果 } } }在algorithm.c中最重要的三个函数是ChangePoint_Analysis_240()- 水平方向分析ChangePoint_Analysis_Blue()- 垂直方向分析CharacterRecognition()- 字符识别7. 常见问题与解决方案在实际部署中我遇到过各种奇葩问题这里分享几个典型案例问题1雨天识别率骤降现象下雨时车牌反光严重误识别率升高解决方案增加图像增强环节使用直方图均衡化改善对比度问题2夜间无法工作现象光线不足时OV7670采集的图像全是噪点解决方案外接红外补光灯切换到黑白模式问题3字符E和F容易混淆现象这两个字母的模板相似度高解决方案增加特征点检测重点比较中间横杠的位置还有一个实用技巧是设置调试模式通过串口输出中间处理结果。我在代码中预留了很多调试接口比如#define DEBUG_MODE 1 #if DEBUG_MODE printf(Current threshold: %d\n, threshold); Display_Intermediate_Image(); #endif最后要提醒的是STM32的车牌识别毕竟性能有限适合作为学习项目或者对实时性要求不高的场景。如果需要更高性能可以考虑移植到STM32H7系列或者树莓派等平台。

相关新闻