MATLAB实战:从图像处理到字符识别全流程解析
一、引言
1.1 应用背景
车牌识别(LPR)是智能交通系统的核心技术,广泛应用于停车场自动收费、违章抓拍、高速公路ETC、城市道路监控等场景。随着智慧城市建设的推进,车牌识别技术的需求日益增长,掌握其原理对从事计算机视觉和图像处理的同学非常有价值。本文将带你从零实现一个完整的车牌识别系统,涵盖车牌定位、字符切割、字符识别全流程。
1.2 技术路线
| 步骤 | 方法 | 作用 |
|---|---|---|
| 预处理 | HSV颜色转换 + Canny边缘检测 | 突出车牌区域,抑制背景噪声 |
| 车牌定位 | 形态学处理 + Hough变换 | 精确定位并校正倾斜的车牌 |
| 字符切割 | 连通域分析 + 投影法 | 将车牌切割为单个字符 |
| 字符识别 | HOG特征 + SVM分类器 | 识别每个字符的内容 |

1.3 开源地址
本文章完整代码下载地址,测试集包含黑夜,雨天,倾斜车牌,正常拍照四种类型,适用于简单的车牌识别。
基于MATLAB的车牌识别系统
├── Main.m # 主程序入口(启动 GUI)
├── Step1_ReadImage.m # 步骤1:读取图片
├── Step2_Preprocess.m # 步骤2:图像预处理(HSV+Canny双验证)
├── Step3_Locate.m # 步骤3:车牌定位 + 倾斜校正
├── Step4_Segment.m # 步骤4:字符分割(连通域定位法)
├── Step5_Recognize.m # 步骤5:字符识别(HOG+SVM)
├── Step6_ResultGUI.m # 结果展示GUI界面
├── classifierChinese.mat # SVM 汉字分类器
├── classifierLetter.mat # SVM 字母分类器
├── classifierAlphanum.mat # SVM 字母数字分类器
├── ChePaiKu/ # 车牌图片库
├── temp_segments/ # 分割后的字符图片(运行时生成)
└── 字符模板(4020)/ # 字符模板库
二、图像预处理
2.1 为什么需要预处理?
原始图像包含大量干扰信息(如车身、树木、天空等),预处理的目标是突出车牌区域,抑制背景噪声,为后续定位做准备。
预处理流程图:
2.2 蓝色检测(HSV颜色空间)
采用HSV颜色空间进行蓝色车牌检测,相比RGB对光照变化更鲁棒。
HSV阈值设置:
| 通道 | 阈值范围 | 说明 |
|---|---|---|
| H(色调) | 0.50 ~ 0.80 | 蓝色色调范围 |
| S(饱和度) | > 0.15 | 过滤灰色区域 |
| V(亮度) | > 0.15 | 过滤过暗区域 |
核心代码:
imgHSV = rgb2hsv(imgRGB);
imgBlue = (H >= 0.50 & H <= 0.80) & (S > 0.15) & (V > 0.15);
imgBlue = imfill(imgBlue, 'holes'); % 填充孔洞
2.3 边缘检测(Canny算子)
使用Canny算子检测图像边缘,提取车牌的轮廓信息。
处理流程:
- RGB转灰度图
- Canny边缘检测(阈值0.5)
核心代码:
imgGray = rgb2gray(imgRGB);
imgEdge = edge(imgGray, 'canny', 0.5);
2.4 形态学处理
对边缘检测结果进行形态学处理,完善车牌区域。
处理步骤:
- 闭运算:使用15×15矩形结构元素,连接断裂的边缘
- 填充孔洞:填充封闭区域内的孔洞
- 面积滤除:去除小于1000像素的噪声区域
核心代码:
imgClosed = imclose(imgEdge, strel('rectangle', [15, 15]));
imgFilled = imfill(imgClosed, 'holes');
imgFiltered = bwareaopen(imgFilled, 1000);
2.5 双重验证(颜色+边缘取交集)
将蓝色检测结果与形态学处理后的边缘结果取交集,提高定位准确性。
核心代码:
imgCombined = imgFiltered & imgBlue;
imgPreprocessed = bwareaopen(imgCombined, 1000); % 再次滤除小区域
下图为正确实现上述预处理之后的结果展示:
三、车牌定位

3.1 粗定位:膨胀 + 连通域分析
通过膨胀将分散的字符连成一片,再通过连通域分析找出车牌区域。
核心代码:
imgDilated = imdilate(imgPreprocessed, strel('rectangle', [20, 20]));
cc = bwconncomp(imgDilated);
stats = regionprops(cc, 'Area', 'BoundingBox');
[~, maxIdx] = max([stats.Area]);
3.2 灰度化与二值化
对定位后的车牌图像进行灰度化和二值化处理。
核心代码:
imgCropGray = rgb2gray(imgCropRGB);
level = graythresh(imgCropGray);
imgCropBinary = im2bw(imgCropGray, level);
3.3 倾斜校正:Hough变换
倾斜校正的理论原理:
1. 边缘检测 使用Canny算子提取图像中的边缘点,这些边缘点包含了车牌的轮廓信息。
2. Hough变换映射 将图像空间中的每个边缘点映射到参数空间(ρ-θ空间)的一条曲线。图像空间中同一条直线上的点,在参数空间中会交于同一点。
3. 投票机制 在参数空间中建立累加器矩阵,对每条曲线经过的参数组合进行投票。投票数越多,说明对应的直线越显著。
4. 寻找峰值 在累加器中找到投票数最高的点,该点对应的(ρ, θ)参数就是原图像中最显著直线的参数。
5. 计算倾斜角度 筛选出接近水平的线段(|θ| < 45°),使用加权中位数法计算最终倾斜角度。长线段权重更大,更可靠。
6. 旋转校正 根据计算出的角度,使用双线性插值对图像进行旋转,使车牌水平对齐。
核心代码:
% --- Step 3.2: Hough变换检测倾斜角度 ---
% 1. 边缘检测:提取车牌轮廓
imgEdge = edge(imgCropGray, 'canny');
% 2. Hough变换:将图像空间映射到参数空间
[H, theta, rho] = hough(imgEdge);
% 3. 寻找峰值:在参数空间中找投票数最高的点
peakThreshold = ceil(0.15 * max(H(:)));
peaks = houghpeaks(H, 20, 'Threshold', peakThreshold);
% 4. 提取线段:根据峰值还原实际线段
[cropH, cropW] = size(imgCropGray);
minLen = max(10, round(cropW * 0.15));
lines = houghlines(imgEdge, theta, rho, peaks, ...
'FillGap', round(cropW * 0.05), 'MinLength', minLen);
% 5. 计算倾斜角度:使用加权中位数法
skewAngle = 0;
if ~isempty(lines)
% 只保留接近水平的线段(|theta| < 45°)
horizontalLines = lines(abs([lines.theta]) < 45);
if isempty(horizontalLines)
horizontalLines = lines;
end
% 提取每条线段的角度和权重(线段长度)
angles = zeros(1, length(horizontalLines));
weights = zeros(1, length(horizontalLines));
for k = 1:length(horizontalLines)
angles(k) = horizontalLines(k).theta;
weights(k) = norm(horizontalLines(k).point1 - horizontalLines(k).point2);
end
% 加权中位数:按角度排序,累加权重找中位数
[~, sortIdx] = sort(angles);
sortedAngles = angles(sortIdx);
sortedWeights = weights(sortIdx);
cumWeight = cumsum(sortedWeights);
medianIdx = find(cumWeight >= cumWeight(end) / 2, 1);
skewAngle = sortedAngles(medianIdx);
end
% 6. 角度过滤:超过30°认为是误检
if abs(skewAngle) > 30
skewAngle = 0;
end
% --- Step 3.3: 旋转校正 ---
% 7. 执行旋转:使用双线性插值保证图像质量
if abs(skewAngle) > 0.5
imgCropBinary = imrotate(imgCropBinary, skewAngle, 'bilinear', 'crop');
imgCropGray = imrotate(imgCropGray, skewAngle, 'bilinear', 'crop');
end
3.4 去噪处理:形态学操作
使用形态学操作去除小的噪声区域。
核心代码:
imgCropBinary = bwareaopen(imgCropBinary, 50);
3.5 投影法去边框
通过水平和垂直投影去除车牌边框,阈值为最大投影值的5%。
核心代码:
% 水平投影:找左右边界
projHorizontal = sum(imgCropBinary, 1);
projThreshold = max(projHorizontal) * 0.05;
colLeft = find(projHorizontal >= projThreshold, 1, 'first');
colRight = find(projHorizontal >= projThreshold, 1, 'last');
% 垂直投影:找上下边界
projVertical = sum(imgCropBinary, 2);
projVThreshold = max(projVertical) * 0.05;
rowTop = find(projVertical >= projVThreshold, 1, 'first');
rowBottom = find(projVertical >= projVThreshold, 1, 'last');
% 裁剪
imgLocated = imgCropBinary(rowTop:rowBottom, colLeft:colRight);
下图为正确实现上述车牌定位之后的结果展示:
四、字符切割

4.1 预处理:去除干扰
对二值化后的车牌图像进行预处理,去除铆钉和边框干扰。
核心代码:
imgReady = bwareaopen(imgLocated, 20); % 去除小于20像素的噪声
[segH, segW] = size(imgReady);
% 上下裁剪17%,去除铆钉
cropTop = round(segH * 0.17);
cropBottom = segH - round(segH * 0.17);
imgReady = imgReady(cropTop:cropBottom, :);
% 左右裁剪5%,去除边框
cropLeft = round(segW * 0.05);
cropRight = segW - round(segW * 0.05);
imgReady = imgReady(:, cropLeft:cropRight);
4.2 连通域分析
标记所有连通区域,按面积过滤保留有效字符区域。
核心代码:
cc = bwconncomp(imgReady);
stats = regionprops(cc, 'Area', 'BoundingBox', 'PixelIdxList');
areas = [stats.Area];
% 过滤:保留面积 >= 中位数15%的区域
medianArea = median(areas);
validIdx = find(areas >= medianArea * 0.15);
stats = stats(validIdx);
% 按x坐标排序(从左到右)
bboxes = cat(1, stats.BoundingBox);
[~, sortIdx] = sort(bboxes(:, 1));
stats = stats(sortIdx);
4.3 合并过近的连通域
字符笔画断裂会导致一个字符被分成多个区域,需要合并间距过小的连通域。
核心代码:
mergeGap = segW * 0.02; % 合并阈值:车牌宽度的2%
merged = true;
while merged
merged = false;
n = length(stats);
if n <= 7, break; end
newStats = [];
k = 1;
while k <= n
curBox = stats(k).BoundingBox;
curRight = curBox(1) + curBox(3);
curPixels = stats(k).PixelIdxList;
while k < n
nextBox = stats(k+1).BoundingBox;
gap = nextBox(1) - curRight;
if gap < mergeGap
% 合并两个连通域
newX = min(curBox(1), nextBox(1));
newRight = max(curRight, nextBox(1) + nextBox(3));
curBox = [newX, min(curBox(2), nextBox(2)), ...
newRight - newX, max(curBox(4), nextBox(4))];
curRight = newRight;
curPixels = union(curPixels, stats(k+1).PixelIdxList);
k = k + 1;
merged = true;
else
break;
end
end
s.BoundingBox = curBox;
s.Area = length(curPixels);
s.PixelIdxList = curPixels;
newStats = [newStats, s];
k = k + 1;
end
stats = newStats;
end
4.4 选择最佳7个字符
从候选连通域中选择7个最佳字符(中国车牌固定7位)。
核心代码:
nCandidates = length(stats);
if nCandidates == 7
charStats = stats;
elseif nCandidates > 7
% 按面积降序,取前7个
allAreas = arrayfun(@(s) s.Area, stats);
[~, areaIdx] = sort(allAreas, 'descend');
charStats = stats(areaIdx(1:7));
% 重新按x坐标排序
bboxes = cat(1, charStats.BoundingBox);
[~, xIdx] = sort(bboxes(:, 1));
charStats = charStats(xIdx);
else
charStats = stats; % 不足7个时保留所有
end
4.5 裁剪并保存
据边界框裁剪每个字符并保存到临时目录。
核心代码:
for i = 1:min(7, length(charStats))
bbox = charStats(i).BoundingBox;
x1 = max(1, round(bbox(1)));
y1 = max(1, round(bbox(2)));
x2 = min(segW, round(bbox(1) + bbox(3)));
y2 = min(segH, round(bbox(2) + bbox(4)));
imgChar = imgReady(y1:y2, x1:x2);
imwrite(imgChar, fullfile('temp_segments', strcat(int2str(i), '.jpg')));
end
下图为正确实现上述车牌切割之后的结果展示:
五、字符识别
5.1 加载SVM分类器
系统使用三个SVM分类器分别识别不同类型的字符,使用SVM向量机对大量字符模板提取特征值并训练得到下述三个训练模型,每次识别直接调用训练好的模型无需重复训练。
[注]:训练所使用的字符模板位于目录的字符模板(4020)文件夹内。
classifierChinese.mat:省份汉字(第1位)classifierLetter.mat:字母(第2位)classifierAlphanum.mat:字母数字(第3-7位)
核心代码:
models.chinese = loadSVM('classifierChinese.mat');
models.letter = loadSVM('classifierLetter.mat');
models.alphanum = loadSVM('classifierAlphanum.mat');
5.2 HOG特征提取
HOG(方向梯度直方图)通过统计图像局部区域的梯度方向分布来描述形状特征。
参数设置:
| 参数 | 值 | 说明 |
|---|---|---|
| 图像尺寸 | 40×20 | 统一输入大小 |
| Cell大小 | 4×4 | 基本单元 |
| Bins数量 | 9 | 梯度方向 bin 数 |
核心代码:
imgR = imresize(img, [40 20]);
hogFeat = extractHOGFeatures(imgR, 'NumBins', 9, 'CellSize', [4 4]);
5.3 分类预测
根据字符位置选择对应的分类器进行预测。
核心代码:
for i = 1:7
img = charImgs{i};
if ~islogical(img), img = imbinarize(img); end
switch i
case 1
label = svmPredict(img, models.chinese); % 省份汉字
case 2
label = svmPredict(img, models.letter); % 字母
otherwise
label = svmPredict(img, models.alphanum); % 字母数字
end
plateResult = [plateResult, label];
end
完整的带gui设计界面的结果展示如下:
六、总结与优化方向
6.1 本系统技术总结
| 步骤 | 方法 | 核心作用 |
|---|---|---|
| 预处理 | HSV颜色转换 + Canny边缘 | 突出车牌区域 |
| 车牌定位 | 形态学 + Hough变换 | 精确定位+倾斜校正 |
| 字符切割 | 连通域分析 + 投影法 | 分离单个字符 |
| 字符识别 | HOG特征 + SVM分类 | 识别字符内容 |
6.2 教学价值 vs 商用方案
教学价值:
本系统采用传统图像处理方法,商用系统通常采用 YOLO + PLR 的两阶段方案:YOLO负责定位,PLR负责识别,两者配合可达到99%以上的准确率。虽然本系统在复杂场景下准确率有限,但具有重要的教学意义:
- 理解图像处理基础:HSV颜色空间、边缘检测、形态学操作、Hough变换等都是图像处理的核心知识
- 掌握完整流程:从预处理到识别,涵盖车牌识别的完整技术链路
- 培养工程思维:参数调优、边界情况处理、模块化设计等工程实践能力
- 为深度学习打基础:理解传统方法的局限性,才能更好地理解深度学习的优势
更多推荐


所有评论(0)