opencv传统方法用形态学+边缘检测定位车牌,因其不依赖标注数据、cpu实时性好、抗光照变化强;而yolo需大量标注、推理慢、部署门槛高,不适用于嵌入式或老旧工控机。

车牌定位为什么常用形态学+边缘检测而不是直接用YOLO
OpenCV传统方法做车牌定位,核心是利用车牌区域在HSV或灰度图中的颜色/纹理规律性,而不是端到端检测。YOLO类模型虽准,但需要大量标注数据、推理慢、部署门槛高——对嵌入式或老旧工控机不友好。而cv::Canny配合cv::morphologyEx能跑在树莓派上,且逻辑可控:先提亮车牌区域(比如用cv::inRange过滤蓝色/黄色车牌),再腐蚀膨胀连通字符块,最后用cv::findContours找外接矩形。
常见错误是直接对BGR图做Canny——噪声太大。必须先转灰度,再用高斯模糊cv::GaussianBlur降噪,否则边缘断裂,轮廓连不上。
- 蓝色车牌建议用HSV空间,阈值设为
lower = (100, 43, 46)、upper = (124, 255, 255)(注意OpenCV的HSV范围不是0–360) - 避免用
cv::threshold全局二值化,光照不均时车牌边缘会断开;改用cv::adaptiveThreshold更鲁棒 - 形态学核大小别硬写
cv::Size(15, 3)——横向长核用于连接字符,但过长会把上下两行车牌粘连,实际应根据图像分辨率动态算:比如宽度>1280时用cv::Size(21, 5)
字符分割时为什么不能直接按列投影切分
投影法(cv::reduce求列和)对标准清晰车牌有效,但遇到倾斜、污损、LED反光车牌极易切错。真正稳定的做法是:先对定位出的车牌ROI做透视校正(cv::getPerspectiveTransform + cv::warpPerspective),再用连通域分析(cv::connectedComponentsWithStats)逐个提取字符区域。
关键点在于连通域的筛选条件——不能只看面积。中文车牌的汉字宽高比约0.8–1.2,字母数字约0.4–0.7,高度通常占ROI高度的70%–95%。漏掉这个判断,雨滴、铆钉、边框线都会被误认为字符。
- 调用
cv::connectedComponentsWithStats后,stats第4列是高度(CC_STAT_HEIGHT),第2列是x坐标(CC_STAT_LEFT),需手动排序而非依赖返回顺序 - 中文字符“粤”“京”等笔画多,可能被切分成多个连通域,此时要合并相邻小区域:若两区域x距离60%,就merge
- 避免用
cv::boundingRect套每个轮廓——它包含太多空隙,导致字符粘连;cv::connectedComponentsWithStats给出的CC_STAT_WIDTH/CC_STAT_HEIGHT更准
OpenCV 4.5+里cv::dnn::Net加载ONNX车牌检测模型要注意什么
如果想过渡到轻量级深度学习方案,OpenCV的DNN模块能直接加载ONNX,但车牌场景有特殊约束:输入尺寸必须固定(如320×320),且预处理必须和训练时完全一致。很多公开模型用RGB通道+ImageNet归一化(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225]),但车牌图通常偏暗,直接套用会导致置信度暴跌。
- 务必检查模型文档里的
input_shape和preprocess说明;若没写,用Netron打开ONNX看输入节点的scale/mean属性 - OpenCV DNN默认使用CPU后端,想用GPU需编译时开启
WITH_CUDA并调用net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA)和net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA) - 输出层解析容易出错:
net.forward()返回的是blob,需用cv::dnn::NMSBoxes做非极大抑制,参数score_threshold=0.3、nms_threshold=0.4比默认0.5更适应车牌小目标
字符识别前为什么一定要做归一化和二值化
OpenCV本身不带OCR,后续接tesseract或自研CNN时,输入图像质量直接影响识别率。车牌字符对比度低、有阴影、反光时,cv::threshold常把“川”字中间一横吃掉。正确流程是:先用cv::createCLAHE增强局部对比度(clipLimit=2.0),再用cv::adaptiveThreshold(blockSize=51, C=-5)二值化——负C值能更好保留细笔画。
归一化尺寸也关键:tesseract对60×80像素的字符识别最稳,不是越大越好。双线性插值cv::resize比最近邻更平滑,但缩放倍数别超过2.0,否则引入新锯齿。
-
cv::createCLAHE必须传入8位灰度图,若车牌ROI是彩色,得先cv::cvtColor(..., cv::COLOR_BGR2GRAY) - 二值化前务必去椒盐噪声:
cv::medianBlur窗口设为3,太大则模糊字符边缘 - 别用
cv::imwrite保存中间结果再读取——PNG无损,但JPEG压缩会引入块效应,tesseract直接崩溃
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











