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

资讯详情

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

基于OpenCV的车牌号码识别Python代码:HSV定位与模板匹配实战

基于OpenCV的车牌号码识别Python代码:HSV定位与模板匹配实战 简介面向计算机视觉课程设计与期末大作业场景基于OpenCV的车牌号码识别项目提供了可直接运行的Python源码适合需要快速搭建完整识别流程的本科学生也适合作为项目实战练习的入门范例。代码覆盖车牌定位、字符分割、SVM分类识别等关键环节并附带训练好的svm.dat与svmchinese.dat模型文件使用者无需自行准备训练数据集解压后即可直接跑通推理过程。压缩包共25个文件以py代码为核心配有jpg/png测试图片、7z数据集分包、dat模型文件、js配置及txt文档说明整体大小14.56MB目录结构清晰、模块划分明确。目前已有192人下载学习predict.py负责推理、surface.py提供可视化界面test目录下多张真实车牌照片可直接验证效果便于快速生成课程报告或答辩演示结果。资源内置的chars7z与charsChinese数据集也可供有进阶需求的读者重新训练或优化模型。1. 为什么“可直接运行”的车牌号码识别Python代码关键在定位搜“车牌号码识别”能找到大量教程但下载后能立刻跑起来的很少。多数人拿到的代码缺图片样张、缺依赖版本说明或者定位算法本身就没调过参数一运行就是 ModuleNotFoundError或者弹个黑窗口什么也不输出。真正能直接运行的 OpenCV 车牌识别 Python 代码不是把函数抄齐而是把环境假设和图片假设都缩到最小只依赖 opencv-python 和 numpy自带模拟测试图复制保存后就能看到“定位 → 分割 → 识别”的完整链路。下面这份实现适合第一次接触 OpenCV 图像处理、需要快速验证方案的读者有五年以上经验的人也能从 HSV 阈值和形态学核参数里看出真实场景的可调空间。2. 车牌号码识别流程拆解HSV 颜色空间与形态学定位原理2.1 为什么车牌定位优先用 HSV 颜色阈值而不是 Sobel 边缘网上最常见的传统方案是灰度化后提取 Sobel 纵向边缘再做闭运算和轮廓筛选。它依赖边缘密度可车头的散热格栅、车灯、玻璃倒影都有密集竖边候选框很容易漂到进气口或前挡风玻璃上。更稳定的先验是颜色——国内蓝底白字车牌有固定色调HSV 空间下色相不会随光照强度剧烈变化比 BGR 更接近人对“这是蓝色”的认知。HSV 把颜色拆成 H色调、S饱和度、V明度三个通道BGR 下蓝值是(255, 0, 0)一旦光线变暗就变成(100, 0, 0)而 HSV 里的 H 基本还在 100 到 130 之间。这是定位环节最值得信耐的一条路径。2.2 HSV 阈值参数表与不同光照下的调整OpenCV 的 HSV 取值范围和用户习惯不同H 是 0 到 179S 和 V 是 0 到 255。蓝牌对应的常用区间如下。车牌类型H 范围S 范围V 范围识别场景蓝色车牌100–13080–25560–255白天正常光照黄色车牌15–3580–25580–255驾校、教练车新能源渐变绿40–8080–25580–255绿牌车提示调参时先动 H再动 S。画面偏暗就降低 V 的下界S 下界设太低会把白色车身反光也放进来。实际项目中我一般先把 mask 用cv2.imshow单独显示出来确认阈值能看到完整车牌再继续。2.3 形态学闭运算与开运算的组合把字符区焊成一块颜色阈值得到的 mask 里字符笔画之间是断开的需要形态学处理把它连成完整矩形。闭运算用cv2.MORPH_CLOSE先膨胀后腐蚀能把字符间隙填平开运算是先腐蚀后膨胀负责去掉面积小的噪点。核的选择很关键宽 15、高 5 的矩形核宽度略大于单个字符笔画间距高度接近车牌字符高度才能既填住缝隙又不把周围区域涂黑。对小尺寸车牌核太宽会把旁边色块也并进来核太窄则缝隙合不上。先闭后开是车牌定位最常用的组合顺序不要调反。2.4 候选框筛选面积与宽高比双条件形态学处理后的轮廓还包含干扰色块必须用车牌几何先验过滤。车牌长宽比通常在 1.8 到 6.0 之间小于 1.8 的是方块色块大于 6.0 的可能是路牌或横幅。简单做法是遍历所有轮廓先用cv2.boundingRect拿到外接矩形再对面积和比例打分满足比例区间的候选给高权重最终取分数最高的那个而不是contours[0]因为轮廓列表的先后顺序和面积无关。for cnt in contours: x, y, w, h cv2.boundingRect(cnt) if w 60 or h 20: continue area w * h ratio w / h score area * (1.0 if 2.0 ratio 5.5 else 0.2)3. 基于 OpenCV 的车牌号码识别 Python 代码定位、分割与模板匹配3.1 完整可运行脚本下面是整份代码保存为plate_recognition.py运行python plate_recognition.py即可。没有测试图时会自动生成一张模拟场景图保证首次运行也能看到完整流程。import cv2 import numpy as np import string TEMPLATE_CHARS string.digits string.ascii_uppercase def make_demo_image(): 生成一张包含蓝色车牌的模拟场景图用于在没有真实照片时跑通流程 bg np.random.randint(80, 120, size(360, 640, 3), dtypenp.uint8) plate np.zeros((80, 260, 3), dtypenp.uint8) plate[:] (255, 0, 0) # BGR 蓝色 cv2.putText(plate, SU 12345, (14, 52), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255), 2) bg[120:200, 190:450] plate noise np.random.normal(0, 8, bg.shape) bg np.clip(bg.astype(np.int16) noise, 0, 255).astype(np.uint8) return bg def locate_plate(img): 通过 HSV 颜色阈值 形态学处理 轮廓筛选定位车牌区域 hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) mask cv2.inRange(hsv, (100, 80, 60), (130, 255, 255)) kernel cv2.getStructuringElement(cv2.MORPH_RECT, (15, 5)) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) mask cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) best, best_score None, 0 for cnt in contours: x, y, w, h cv2.boundingRect(cnt) if w 60 or h 20: continue area w * h ratio w / h score area * (1.0 if 2.0 ratio 5.5 else 0.2) if score best_score: best_score score best (x, y, w, h) if best is None: return None, None x, y, w, h best return img[y:y h, x:x w], (x, y, w, h) def split_chars(plate): 对定位到的车牌区域做二值化用连通域切出单个字符 gray cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY) blur cv2.GaussianBlur(gray, (3, 3), 0) _, thresh cv2.threshold(blur, 127, 255, cv2.THRESH_BINARY_INV) contours, _ cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) chars [] for cnt in contours: x, y, w, h cv2.boundingRect(cnt) if h plate.shape[0] * 0.3 and w 2 and w / h 0.8: chars.append((x, y, w, h, thresh[y:y h, x:x w])) chars.sort(keylambda c: c[0]) return chars def generate_templates(): 用 OpenCV 自带字体生成数字与字母模板 templates {} for ch in TEMPLATE_CHARS: img np.zeros((60, 40), dtypenp.uint8) cv2.putText(img, ch, (5, 44), cv2.FONT_HERSHEY_SIMPLEX, 1.2, 255, 2) templates[ch] img return templates def match_char(cell, templates): 将待识别字符与每个模板求绝对差取均值最小的作为结果 cell cv2.resize(cell, (40, 60)) best_char, best_score ?, float(inf) for ch, tpl in templates.items(): tpl cv2.resize(tpl, (40, 60)) diff cv2.absdiff(cell, tpl).mean() if diff best_score: best_score diff best_char ch return best_char if __name__ __main__: img cv2.imread(car.jpg) if img is None: print([提示] 未找到 car.jpg使用内置模拟图跑通流程) img make_demo_image() plate, rect locate_plate(img) if plate is None: print(未定位到车牌请检查 HSV 阈值) else: templates generate_templates() chars split_chars(plate) plate_no .join(match_char(cell, templates) for _, _, _, _, cell in chars) print(f定位坐标: {rect}) print(f分割字符数: {len(chars)}) print(f识别结果: {plate_no}) cv2.rectangle(img, (rect[0], rect[1]), (rect[0] rect[2], rect[1] rect[3]), (0, 0, 255), 2) cv2.imshow(result, img) cv2.imshow(plate, cv2.resize(plate, (plate.shape[1] * 2, plate.shape[0] * 2))) cv2.waitKey(0) cv2.destroyAllWindows()3.2 定位函数的筛选参数说明参数当前取值作用调参建议w 6060 像素过滤宽度太小的噪点图像小就降到 40h 2020 像素过滤高度太小的色块车牌在画面中占比较小时重点调这里宽高比区间2.0–5.5匹配车牌长宽比新能源车牌比例更大可放宽到 6.0形态学核(15, 5)横向填平字符缝隙核宽约为图像中车牌宽度的 1/203.3 模板匹配识别的工作原理与局限代码先把分割出的字符统一resize成 40×60再用cv2.putText生成 0-9、A-Z 的模板逐对计算cv2.absdiff的均值差值最小就是识别结果。这个方法不依赖第三方 OCR 库所以能“直接运行”。但要认清边界模拟图的字符和模板都出自同一个FONT_HERSHEY_SIMPLEX识别率自然高真实车牌使用专用印刷字体笔画粗细和模板差异明显模板匹配的准确率会明显下降。所以这份代码的意义在于把定位和分割的关键步骤跑通字符识别部分到真实场景再替换为 OCR 或深度模型。4. 运行这份车牌识别 Python 代码环境准备、测试图片与常见报错排查4.1 最小环境安装命令需要 Python 3.7 及以上版本然后安装两个依赖包。opencv-python提供cv2模块numpy负责数组操作模板匹配里大量用到np.zeros和np.absdiff的底层计算这两个缺一不可。pip install opencv-python numpy安装速度慢时可以临时指定国内镜像源。装完用一条命令确认版本能打印出 4.x 就说明环境正常。python -c import cv2; print(cv2.__version__)4.2 首次运行的三种场景把代码保存为plate_recognition.py当前目录下放一张清晰的车辆前脸照片并命名为car.jpg直接运行python plate_recognition.py。如果目录里没有这张图程序会自动生成模拟场景图继续执行这是这份代码能做到“直接运行”的关键设计。运行后终端会依次输出定位坐标、分割字符数和识别结果屏幕上会弹出两个窗口一个画了红色矩形框的整图一个放大的车牌区域。按任意键关闭窗口程序正常退出。4.3 常见报错与排查思路现象原因处理方式ModuleNotFoundError: No module named cv2依赖没装或 pip 装到了另一个 Python 环境重新执行pip install opencv-python用pip -V检查当前环境路径输出“未定位到车牌”HSV 阈值不匹配或被测图不是蓝牌单独显示mask中间结果观察蓝色区域是否完整分割字符数超过 8 个铆钉、边框、车牌汉字被切碎收紧split_chars中h plate.shape[0] * 0.3的高度条件窗口弹出但按键没反应终端焦点不在图像窗口上先点击图像窗口再按键或改用cv2.imwrite保存结果代替窗口显示注意cv2.waitKey(0)会无限等待按键这是 OpenCV 程序“看起来卡住”的最常见原因。调试阶段建议改为cv2.waitKey(2000)让窗口自动关闭。5. 从模拟图到真实车牌三个立马上手的扩展方向5.1 老牌的扩展黄色车牌与新能源绿牌蓝色阈值只能覆盖蓝牌真实停车场里还有黄色和新能源绿色车牌。做法是把cv2.inRange执行两次分别用黄色区间(15, 80, 80)-(35, 255, 255)和绿色区间(40, 80, 80)-(80, 255, 255)最后把两个 mask 用cv2.bitwise_or合并。注意黄牌是黑字黄底二值化时不能直接套THRESH_BINARY_INV需要先判断车牌主色黑字场景改用THRESH_BINARY否则字符区域会反白模板匹配全盘失效。5.2 中文省份字的处理模板匹配的边界国内车牌第一位是省份简称汉字第二位是发牌机关字母后面五位是数字和字母。模板匹配不擅长处理汉字因为字模库要覆盖几十个省份简称工作量成倍增长。常见做法是做一套省份汉字模板利用“第一位必是省份”的位置先验把匹配范围限制在 31 个汉字里而不是全部字符集。更省力的方案是只保留 OpenCV 定位与分割识别部分换成 PaddleOCR 或 Tesseract由 OCR 直接吃字符序列这一步替换后准确率会有一个明显跃升。5.3 实时视频流先缩图再跳帧要处理摄像头视频把主流程包进VideoCapture循环即可但要注意性能。常见做法是先把每一帧缩到 640 宽度再送入定位函数这会显著减少 HSV 转换和形态学计算量定位到车牌后再把裁剪区域放大做字符分割等于把算力集中在 ROI 上。识别帧率不需要拉满每 5 帧做一次完整识别已经足够覆盖大多数车辆通行场景。这样组合下来普通笔记本 CPU 也能跑到 10 帧以上的处理速度并且省下的算力还能留出一部分给后续的追踪模块。本文还有配套的精品资源点击获取
返回列表