基于MATLAB的模板匹配法电动车牌识别(多角度车牌、多车牌、有干扰车牌且不使用深度学习)
本文介绍了一个基于传统图像处理技术的车牌识别系统,主要针对电动车牌设计。系统包含四个核心模块:1)正常车牌识别模块,通过颜色阈值分割和形态学处理提取车牌区域,利用连通域质心分层处理双层车牌;2)多车牌识别模块,采用最小外接矩形和几何特征校验筛选车牌区域;3)倾斜车牌校正模块,结合区域属性分析和霍夫变换实现精准透视校正;4)抗干扰模块,运用凸包和旋转卡壳法排除背景干扰。系统采用模板匹配进行字符识别,
车牌识别其实已经有许多的开源的源码,采用深度学习的话这个项目可以简单许多,我这个版本是因为项目的要求所以没有采用深度学习,下面我来介绍分析一下各部分的处理:
一、 正常车牌识别
这是整个系统的基础框架。主要依赖颜色阈值分割出车牌区域,利用形态学操作去噪,然后通过连通域的质心(Centroid)将双层车牌分为上下两排,最后利用模板匹配(皮尔逊相关系数)进行字符识别。
关键步骤与代码:
1.颜色阈值分割:由于电动车牌白色,通过 RGB 通道的线性组合与比例关系,过滤掉非车牌颜色。
% 提取偏绿/偏蓝的像素点,剔除过暗或红/蓝比例不对的像素
rgb(rgb(:,1)+rgb(:,2)+rgb(:,3)<500,:) = 0;
rgb(rgb(:,3)./rgb(:,1)>1.1,:) = 0; % 限制蓝色比例
rgb(rgb(:,1)./rgb(:,2)>1.05,:) = 0; % 限制红色比例
2.上下双排字符切割:电动车牌的特点是地区代码在上,字母数字在下。代码通过计算所有字符连通域的 Y 坐标平均值,将字符精准分层。
y_coords = centroids(:, 2);
y_mean = mean(y_coords);
% 区分上下行
top_indices = find(y_coords < y_mean); % 上排字符
bottom_indices = find(y_coords >= y_mean); % 下排字符
二、 多车牌识别
与单车牌直接取“最大连通域”不同,多车牌识别需要保留所有潜在的车牌区域。这里的亮点是引入了最小外接矩形 (minboundrect.m) 和多重几何特征校验,精确筛除形似车牌的噪点,并遍历每一个合法的区域进行透视校正和识别。
关键步骤与代码:
1.多重几何特征校验:计算每个连通域的最小外接矩形,并通过“面积比”、“高宽比”、“长短轴比”进行严格过滤。
% 计算最小外接矩形和实际面积的比例
[rectx, recty, rect_area, rect_perimeter] = minboundrect(col, row, 'a');
area_ratio = region_area / rect_area;
% 剔除条件:面积比例过低(碎片)、过于细长(长短轴比>5)、宽高比不符
if area_ratio < area_ratio_threshold
reject_reason = '面积比例过低';
elseif major_minor_ratio > 5
reject_reason = '区域过于细长';
% ... 保留符合条件的车牌
2。遍历与透视变换:对找到的每一个合法车牌(valid_objects),利用外接矩形的4个顶点直接进行几何透视变换校正。
% 遍历每个识别到的车牌区域
for plate_idx = 1:valid_objects
% 获取角点并利用 fitgeotrans 和 imwarp 进行透视校正
tform = fitgeotrans(sorted_corners, destinationPoints, 'projective');
plate_corrected = imwarp(plate_region, tform, 'OutputView', imref2d([target_height, target_width]));
% ... 后续对每个 plate_corrected 进行字符分割与匹配
三、倾斜车牌识别
此部分专门针对存在俯仰角、偏航角或严重旋转的车牌。亮点在于结合了 Regionprops 角度检测 和 Hough(霍夫)变换直线检测,通过求交点找到车牌的真实四个角,从而实现高精度的透视校正(Perspective Transformation)。
关键步骤与代码:
1.大角度预旋转:利用 regionprops 获取 Orientation,如果车牌倒置或倾斜过大(>45度),先进行仿射旋转预处理。
stats_plate_angle = regionprops(bw_plate, 'Orientation');
plate_angle = stats_plate_angle.Orientation;
if abs(plate_angle) > 45
correction_angle = plate_angle - sign(plate_angle)*45;
bw_plate = imrotate(bw_plate, -correction_angle, 'bilinear', 'crop') > 0.5;
end
2.基于霍夫变换的角点检测与透视校正:提取边缘后检测直线,利用 K-means 将直线方向聚为两类(横向和纵向),然后计算这四条直线的数学交点,得到极为精确的车牌四个角点。
% 检测直线并用 K-means 聚成两个主要方向
[H, theta, rho] = hough(edge_img);
lines = houghlines(edge_img, theta, rho, peaks);
[idx, centers] = kmeans(normalized_angles', 2);
% 计算交点 (lineIntersection为自定义函数)
corners(1,:) = lineIntersection(line1_1, line2_1); % 左上
% ... 获取4个角点后,使用 fitgeotrans('projective') 强行拉平车牌
3.直线交点计算:已知两条直线各自的两个端点,利用行列式(克莱姆法则的变体)直接求解两条直线的精确交点坐标。这比使用斜率截距式($y = kx + b$)更稳定,因为它可以完美处理垂直于 X 轴(斜率 $k$ 趋近于无穷大)的直线。
function intersection = lineIntersection(line1, line2)
% 提取两条直线的四个端点
x1 = line1.point1(1); y1 = line1.point1(2);
x2 = line1.point2(1); y2 = line1.point2(2);
% ... (line2 的端点 x3, y3, x4, y4)
% 利用行列式求解交点,完美避开斜率无穷大的除零错误
denom = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4);
if abs(denom) > 1e-10
px = ((x1*y2 - y1*x2)*(x3-x4) - (x1-x2)*(x3*y4 - y3*x4)) / denom;
py = ((x1*y2 - y1*x2)*(y3-y4) - (y1-y2)*(x3*y4 - y3*x4)) / denom;
intersection = [px, py];
end
end
4.边界极值点提取:当霍夫变换由于图像过于模糊而无法检测出足够的直线时,作为兜底方案。它计算区域边界的凸包,然后强制取出最上、最下、最左、最右的四个极值点作为车牌的近似角点。
% 计算边界点的凸包
k = convhull(x, y);
hull_x = x(k); hull_y = y(k);
% 在凸包上寻找极值点(最上、最下、最左、最右)
[~, top_idx] = min(hull_y);
[~, bottom_idx] = max(hull_y);
[~, left_idx] = min(hull_x);
[~, right_idx] = max(hull_x);
四、干扰背景车牌识别
当车牌周围有其他文字(如车身广告、品牌标识)时,普通的 BoundingBox 会把干扰文字框进去。此代码的亮点是使用了凸包(Convex Hull)和旋转卡壳法(简化版)寻找最小面积矩形,配合 roipoly 生成精确掩膜,完美剔除外部干扰。同时在识别阶段加入了相似度阈值,拒识非车牌字符。
关键步骤与代码:
1.凸包与精确掩膜提取:计算车牌连通域的凸包,遍历边缘角度,找到完美贴合车牌的最小倾斜矩形,并用多边形掩膜(roipoly)抠出纯净车牌,屏蔽外部文字。
% 1. 计算凸包
k = convhull(points(:,1), points(:,2));
hull_points = points(k, :);
% 2. 遍历凸包边,旋转点集,寻找最小面积包围盒(逻辑详见源码旋转卡壳部分)
% 3. 使用 roipoly 创建极其精准的多边形掩膜
mask_poly = roipoly(plate_mask, min_rect.corners(:,1), min_rect.corners(:,2));
I_char = uint8(double(I) .* repmat(mask_poly, [1, 1, 3])); % 背景彻底变黑
2.置信度(相似度)阈值过滤:在字符模板匹配时,设定 similarity_threshold。如果相关系数低于该值,则认为它是车牌上的污渍或伪影,直接丢弃。
similarity_threshold = 0.65; % 设定阈值
corr_score = corr2(char_img(:), template(:));
if best_score < similarity_threshold
recognized_chars{i} = ''; % 空字符串表示不显示、拒识该干扰字符
end
五、辅助识别函数
最小外接矩形计算
这个函数用于寻找任意二维点集的最小面积(或最小周长)外接矩形。在多车牌识别和抗干扰模块中,用于精确框选车牌。 它的算法核心是旋转卡壳法(Rotating Calipers)的思想:
-
先求出所有点的凸包(Convex Hull),剔除内部无用点,大幅减少计算量。
-
最小外接矩形的一条边必然与凸包的一条边共线。因此,代码遍历凸包的每一条边,计算其倾斜角。
-
利用旋转矩阵将整个凸包旋转到该边水平的位置,然后求正交边界(最大最小 X、Y),计算面积。
-
记录并返回面积最小的那个矩形的四个顶点坐标。
% 1. 先求凸包,减少计算量 edges = convhull(x,y); x = x(edges); y = y(edges); % 2. 获取凸包每条边的角度 edgeangles = atan2(y(ind+1) - y(ind),x(ind+1) - x(ind)); edgeangles = unique(mod(edgeangles,pi/2)); % 3. 遍历每条边的角度,利用旋转矩阵寻找最小面积 for i = 1:nang rot = Rmat(-edgeangles(i)); % 旋转矩阵 xyr = xy*rot; % 将点集旋转 xymin = min(xyr,[],1); xymax = max(xyr,[],1); A_i = prod(xymax - xymin); % 计算当前外接矩形面积 if M_i<met % 找到更小的面积,更新顶点坐标 met = M_i; % ... 计算并反向旋转恢复矩形顶点坐标 end end点集旋转矩阵
这是一个标准的线性代数仿射变换函数。通过构建 2x2 的二维旋转矩阵 $R$,将输入的坐标点集围绕原点旋转指定的弧度(angle)。在抗干扰车牌提取中,用于将车牌拉平或还原位置。
function rotated_points = rotatePoints(points, angle) % 创建标准的二维旋转矩阵 R = [cos(angle), -sin(angle); sin(angle), cos(angle)]; % 矩阵相乘实现点集批量旋转 (points 是 N x 2 矩阵) rotated_points = points * R'; end目标角点排序
在进行**透视变换(Perspective Transformation)**时,原图像的 4 个角点必须与目标图像的 4 个角点严格一一对应(左上、右上、右下、左下)。否则会导致图像被扭成沙漏状。
这个函数通过以下三步确保顺序绝对正确:
1.求质心极角:计算中心点,并用atan2求每个点相对于中心的角度,按角度顺时针/逆时针排序。
2.寻找左上角:通过计算横纵坐标之和($x+y$),和最小的那个点必然是左上角。
3.叉乘校验方向:利用向量的叉积(Cross Product)判断当前是顺时针还是逆时针,确保最终输出一定是标准顺序。% 1. 计算每个点相对于中心的角度并排序 angles = atan2(corners(:,2) - center(2), corners(:,1) - center(1)); [~, idx] = sort(angles); sorted_by_angle = corners(idx, :); % 2. 找到最左上的点 (x+y 最小) 作为起始点 sums = sum(sorted_by_angle, 2); [~, top_left_idx] = min(sums); sorted_corners = circshift(sorted_by_angle, 1-top_left_idx); % 3. 计算向量叉积来判断方向,若为顺时针则翻转为逆时针顺序 cross_product = v1(1)*v2(2) - v1(2)*v2(1); if cross_product < 0 sorted_corners = sorted_corners([1,4,3,2], :); end然后还有一部分就是字符的生成和模板匹配的核对,这个比较简单就不再赘述了
总结:
以上就是该识别电动车牌项目的主要逻辑和代码,主要包括了利用颜色、形态学和模板匹配完成最基础的双层车牌识别的正常车牌识别、利用凸包和多边形掩膜车牌单独“抠”出来的干扰车牌识别、利用霍夫变换直线相交求角点 + 投影变换完成抗畸变的倾斜车牌识别以及通过长宽比、面积比特征,在复杂的全景图中同时锁定并处理多个车牌的多车牌识别。
ps:源代码已经上传github,如果有需要可以私信或者评论留言找我要链接,目前的代码是可以基本识别我拍出来的电动车牌图片的,但是偶尔也存在无法识别或者不准确的bug,但是目前我没有往这个方面继续深入研究的打算,所以就此搁置了。
更多推荐
所有评论(0)