基于OpenCV与SVM的车牌识别系统实现:定位、分割与分类全流程解析

系统整体架构与技术选型

1.1 分层架构设计

系统采用两层解耦架构,便于算法迭代与界面扩展:

  • 算法核心层:独立封装图像处理与识别逻辑,与界面完全解耦,包含预处理、定位、矫正、分割、分类五个子模块

  • 界面交互层:基于PyQt5实现图形化客户端,负责图像加载、结果可视化、操作控制,通过接口调用算法层能力

1.2 核心技术栈

模块 技术选型 说明
开发语言 Python 3.10 兼顾开发效率与库生态
图像处理 OpenCV 4.x 提供完整的图像运算与特征提取能力
分类算法 SVM(支持向量机) 小样本下分类效果稳定,训练成本低
图形界面 PyQt5 组件丰富,跨平台兼容性好
开发环境 PyCharm 集成调试与代码管理能力

1.3 完整识别流水线

系统按固定流水线处理输入图像,各环节可独立调参优化:

图像尺寸归一化 → 灰度化与高斯去噪 → 边缘提取与形态学处理 → 轮廓候选区筛选 → 倾斜角度矫正 → HSV颜色校验 → 字符分割与归一化 → HOG特征提取 → SVM分类识别 → 结果输出


二、车牌定位算法设计与实现

车牌定位是系统准确率的第一道关口,单一方法易受光照、背景干扰,本方案采用「边缘特征筛选 + 颜色空间校验」的双重定位策略,提升复杂场景下的召回率。

2.1 图像预处理

预处理的目标是抑制噪声、突出车牌区域特征,为后续边缘检测做准备。

  1. 尺寸归一化:统一缩放图像分辨率,降低运算量,保证后续轮廓筛选阈值的通用性

  2. 高斯模糊去噪:使用5×5高斯核平滑图像,抑制拍摄噪声,避免伪边缘干扰

  3. 顶帽变换增强:通过开运算提取图像亮区细节,抵消光照不均影响,突出车牌字符区域

复制代码

|-----------------------------------------------------------------|
| import cv2 |
| import numpy as np |
| |
| def image_preprocess(img, blur_ksize=5): |
| # 高斯模糊去噪 |
| if blur_ksize > 0: |
| img_blur = cv2.GaussianBlur(img, (blur_ksize, blur_ksize), 0) |
| else: |
| img_blur = img.copy() |
| |
| # 灰度化 |
| gray = cv2.cvtColor(img_blur, cv2.COLOR_BGR2GRAY) |
| |
| # 顶帽变换:增强亮区域,抵消光照不均 |
| kernel = np.ones((20, 20), np.uint8) |
| img_tophat = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel) |
| img_enhance = cv2.addWeighted(gray, 1, img_tophat, -1, 0) |
| |
| return gray, img_enhance |

2.2 边缘检测与候选区生成

采用Canny算子提取边缘,配合形态学闭运算连接离散边缘,形成候选连通域。

  • 阈值采用Otsu自适应二值化,适配不同光照场景

  • 闭运算使用横向长核,强化车牌的水平矩形特征

  • 开运算去除细小噪点,过滤无效边缘

复制代码

|---------------------------------------------------------------------------------------|
| def edge_extract(img_enhance): |
| # 自适应二值化 |
| _, binary = cv2.threshold(img_enhance, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) |
| |
| # Canny边缘检测 |
| edges = cv2.Canny(binary, 100, 200) |
| |
| # 形态学处理:闭运算连接边缘,开运算去噪 |
| kernel_close = np.ones((4, 19), np.uint8) |
| edges_close = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel_close) |
| edges_open = cv2.morphologyEx(edges_close, cv2.MORPH_OPEN, kernel_close) |
| |
| return edges_open |

2.3 轮廓筛选与倾斜矫正

通过轮廓特征筛选候选车牌,再通过仿射变换完成倾斜矫正:

  1. 面积筛选:过滤面积过小的无效轮廓

  2. 长宽比筛选:车牌标准长宽比约为3:1,设置2.0~5.5的容差区间

  3. 倾斜矫正:通过最小外接矩形获取倾斜角度,使用仿射变换将车牌校正为水平状态

复制代码

|---------------------------------------------------------------------------------|
| def locate_plate_candidates(edges, min_area=1000): |
| contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) |
| candidates = [] |
| |
| for cnt in contours: |
| area = cv2.contourArea(cnt) |
| if area < min_area: |
| continue |
| |
| # 获取最小外接矩形 |
| rect = cv2.minAreaRect(cnt) |
| (cx, cy), (w, h), angle = rect |
| |
| # 统一宽高方向 |
| if w < h: |
| w, h = h, w |
| angle += 90 |
| |
| # 长宽比筛选 |
| ratio = w / h |
| if 2.0 < ratio < 5.5: |
| candidates.append(rect) |
| |
| return candidates |

2.4 HSV颜色校验

将候选区转换到HSV颜色空间,通过色调、饱和度统计判断车牌颜色,进一步排除非车牌区域,同时为后续字符分割提供颜色反转依据。

  • 蓝牌:H通道 100~124

  • 绿牌:H通道 35~99

  • 黄牌:H通道 11~34


三、字符分割与特征提取

3.1 字符分割策略

字符分割采用「水平投影裁剪 + 垂直投影分割」的方案,同时处理常见干扰:

  1. 水平投影:统计每行像素和,裁剪掉上下边缘空白区域,锁定字符行范围

  2. 垂直投影:统计每列像素和,通过波峰波谷分割单个字符

  3. 干扰处理

    • 去除车牌边框与铆钉干扰

    • 跳过省份简称与字母间的分隔点

    • 处理汉字宽度大于字母数字的特性,单独适配第一个字符分割阈值

3.2 字符归一化

分割后的字符尺寸不一,需统一缩放到固定尺寸(如20×20),再提取特征。为避免缩放变形,采用等比例缩放+边界填充的方式,保证字符形态不变。

3.3 HOG特征提取

采用方向梯度直方图(HOG)作为字符特征,相比原始像素特征具备更强的鲁棒性。

  • 将字符图像划分为4个cell

  • 每个cell计算16维梯度方向直方图

  • 采用Hellinger核做归一化,提升分类器效果

复制代码

|----------------------------------------------------------------------|
| def extract_hog_features(char_imgs): |
| features = [] |
| for img in char_imgs: |
| # 计算x、y方向梯度 |
| gx = cv2.Sobel(img, cv2.CV_32F, 1, 0) |
| gy = cv2.Sobel(img, cv2.CV_32F, 0, 1) |
| mag, ang = cv2.cartToPolar(gx, gy) |
| |
| bin_n = 16 |
| bin = np.int32(bin_n * ang / (2 * np.pi)) |
| |
| # 分4块统计直方图 |
| bin_cells = bin[:10,:10], bin[10:,:10], bin[:10,10:], bin[10:,10:] |
| mag_cells = mag[:10,:10], mag[10:,:10], mag[:10,10:], mag[10:,10:] |
| |
| hists = [] |
| for b, m in zip(bin_cells, mag_cells): |
| hists.append(np.bincount(b.ravel(), m.ravel(), bin_n)) |
| hist = np.hstack(hists) |
| |
| # Hellinger归一化 |
| eps = 1e-7 |
| hist /= hist.sum() + eps |
| hist = np.sqrt(hist) |
| hist /= np.linalg.norm(hist) + eps |
| |
| features.append(hist) |
| return np.float32(features) |

相关推荐
林中青木2 小时前
OpenCV 5.0 使用方法及注意事项
人工智能·opencv·计算机视觉
大鱼>4 小时前
机器学习基础:从零理解核心概念与算法分类
算法·机器学习·分类
漂亮的摩托6 小时前
COM -Component Object Model 通用组件模型
支持向量机
江华森6 小时前
OpenCV 进阶应用实战
opencv
qingyulee1 天前
投满分项目—新闻分类
人工智能·分类·数据挖掘
兵慌码乱14 天前
基于 MediaPipe 与 PySide2 的手势交互音乐控制系统实现:轻量化视觉交互全流程解析
python·opencv·计算机视觉·人机交互·手势识别·mediapipe·pyside2
梦想三三18 天前
OpenCV银行卡数字识别项目(图像预处理与字符分割)
人工智能·opencv·计算机视觉
武子康18 天前
调查研究-180 roboflow/supervision:计算机视觉工程里的“胶水层“,为什么值得关注?
人工智能·opencv·计算机视觉·chatgpt·llm·向量化
天行健,君子而铎18 天前
自适应分类·高准确率·可视化易用——运营商数据分类分级解决方案
大数据·分类