资讯详情

Matlab车牌识别课程设计:图像处理流水线与源码避坑

📅 2026/10/28 4:55:02 | 华诺云谱 👁 阅读
Matlab车牌识别课程设计:图像处理流水线与源码避坑
简介这份Matlab车牌识别课程设计报告面向电子信息、自动化及计算机相关专业学生服务于图像处理与模式识别课程设计、答辩准备和入门实践。内容围绕车牌定位、字符分割与字符识别三条主线展开串联原始图像读取、灰度化、背景估计与增强、二值化、候选区域筛选、垂直投影分割以及模板匹配和人工神经网络识别等方法并补充系统测试、光照与车牌质量影响、技术挑战及优化思路。包内共1个docx文件约1.37MB以完整设计报告为主体正文含设计目的、原理、详细步骤、结果分析、总结体会与参考文献并穿插Matlab实现代码与流程说明。已有116人学习读者可据此对照任务书组织报告章节、复现关键处理环节、补充实验分析与测试结论也可参考其中的算法脉络和文档结构完善课程设计或答辩材料。1. 从一张换掉就崩的脚本说起见过太多做课程设计的同学把两百行 Matlab 全塞进一个main.m跑通一张图就截图交差换张图马上报错——因为车牌位置、阈值、保存路径全写死了。这份基于 Matlab 的车牌识别课程设计报告附源代码真正值得留下来的不是报告排版的工整而是里面那条完整的图像处理流水线读彩色图、灰度化、形态学估背景、背景差分增强、自适应阈值二值化、Canny 边缘、开闭运算、连通域标记配长宽比筛选、水平投影校平、垂直投影切字、22×14 归一化、模板库比对输出。报告正文按 2.1 到 2.12 一节一段代码地拆开写适合刚上手数字图像处理、想拿一套能跑通的完整流程改造成自己作品的人也适合已经会调 API、但没系统走过一遍「车牌从像素到字符串」全链路的开发者。下面按能跑、能读、能改的顺序往下拆。2. 工程全景十二段代码怎么串成一条流水线2.1 报告结构与代码文件的对应关系这份资源的物理形态是「课程设计报告 docx 配套源码」报告正文的第三章「详细设计步骤」是主体从 2.1 到 2.12 一共十二个小节每一节的节奏高度统一先一段 Matlab 代码再一段文字解释这段代码在干什么最后贴一张figure出来的结果图。这种写法对交付报告很友好但对应的代码是散在文档里的片段直接复制粘贴是跑不起来的得先按执行顺序拼回一个脚本。按照报告里出现的顺序实际执行链路是这样的imread读原图 →rgb2gray转灰度 →strel(disk,13)加imopen估背景 →imsubtract差分 → 极值算阈值加im2bw二值化 →edge(...,canny)提边缘 → 三次imclose/imopen形态学滤波 →bwlabel标记加imfeature取特征 → 长宽比筛选出车牌候选 → 垂直投影和水平投影做峰谷分析 →fit(...,poly1)拟合算倾角 →imrotate校平 → 二次投影去上下边框 → 垂直投影找七个字符中心 →imresize归一化加imwrite存样本 → 与样本库逐张比对输出结果。常见做法是把这套东西整理成三个文件main.m只负责调度和图窗展示plate_locate.m把预处理到车牌子图这一大段封成函数plate_segment.m负责切字和识别。原报告的写法是全部平铺在一个文件里好处是变量在 workspace 里都是可见的调试时随便figure一下就能看中间结果这对刚接触图像处理的读者反而是优点——你能看见每一步到底变成了什么样。报告小节干什么关键函数产物2.1读入彩色原图imread、imshowScolor2.2灰度化rgb2graySgray2.3形态学开运算估背景strel、imopenBgray2.4背景差分增强imsubtractEgray2.5阈值二值化im2bwbw22.6边缘提取edgecannygrd2.7开闭运算滤波imclose、imopenbg22.8连通域标记与筛选bwlabel、imfeatureL、Area2.9–2.10投影分析与倾角校正sum、fit、imrotatesbw、angle2.11去边框与字符分割sum、imresizeSegBw22.12模板匹配识别矩阵相减取误差车牌字符串2.2 关键参数与它们各自影响哪一段这套算法最劝退的地方不是代码量而是参数多。同一个13放在 640×480 的图上刚好合适换成 1920×1080 就完全失效因为结构元的物理尺度变了。把参数和它影响的环节对上号调试的时候才知道该动谁。参数出现位置报告中的值影响环节调大的后果圆盘结构元半径2.3 背景估计13背景亮度场的平滑尺度车牌整体被吸进背景差分后车牌消失阈值系数2.5 二值化1/3前景占比前景变少字符笔画断裂Canny 参数2.6 边缘默认边缘密度—闭运算结构元2.7 横向连通[5,19]字符横向粘连车牌和车身连成一片开运算结构元2.7 纵向去噪[19,1]竖直线条抑制车牌上下边框被切掉字符也被切长宽比区间2.8 区域筛选约 2.2–4.2候选车牌数量候选变少漏检拟合阶数2.10 倾角poly1旋转角精度—归一化尺寸2.11 字符[22 14]模板尺寸必须和样本库一致否则没法比国内蓝牌的标准尺寸是 440mm×140mm宽高比约 3.14所以筛选区间定在 3 附近是合理的。但如果你的图是手机竖拍后裁过的或者在透视角度很大的场景下拍的实际投影出来的宽高比会偏离这个值区间就得放宽。2.3 环境准备与跑通第一张图MATLAB 版本上报告里的imfeature是 6.x 时代的老函数7.x 之后被regionprops取代fit则需要 Curve Fitting Toolbox。这两点决定了你大概率不能原样复制粘贴必须做两处替换后面避坑那一节会细说。跑通的最小路径是这样的装好 MATLAB 和 Image Processing Toolbox把配套源码里的.m文件和解码后的图片放到同一个目录把imread(3.jpg)里的文件名改成你实际有的图运行。第一次跑建议不要直接上复杂的实拍图先用一张光照均匀、车牌占画面比例较大、车牌基本水平的图把整条链路走通确认每一步的figure都能弹出来且有合理内容。% 跑通前的三处必改 Scolor imread(3.jpg); % ① 换成自己目录下真实存在的图片名 fname strcat(F:\MATLAB\work\sam\image, int2str(k), .jpg); % ② 换成存在的目录 Feastats imfeature(L,basic); % ③ 新版改为 regionprops(L,Area,BoundingBox)第 ① 处不改会直接报「文件不存在」第 ② 处不改会在imwrite那一步报「无法写入文件」而且这个报错出现在循环里前面的定位、分割、显示全都正常很容易误判成识别算法出了问题第 ③ 处不改会报Undefined function imfeature一眼就能看出来反而是最好处理的。跑通之后不要急着换图先做一件事把每个中间变量imwrite存成 png 落盘。Sgray、Bgray、Egray、bw2、grd、bg1、bg3、bg2全存一遍一共八个文件。后面换图失败的时候把新旧两组图并排一放哪一步开始不一样问题就在哪一步比在代码里反复disp快得多。这是我自己踩过坑之后养成的习惯图像处理的黑匣子属性太强不落盘就只能靠猜。3. 预处理与车牌定位从彩色像素到候选区域3.1 灰度化与形态学背景估计彩色图三通道在存储和运算上都是三倍开销而车牌定位本质上是找局部高对比区域颜色信息在定位阶段用处不大所以第一步统一转灰度。rgb2gray用的是 0.2989R 0.5870G 0.1140B 的加权比简单取平均更符合人眼亮度感知转完之后字符和底牌的亮度差保留得更好。接下来这一步是整个流程里比较巧的地方不是直接对灰度图做二值化而是先用一个大结构元做形态学开运算得到一张模糊的「背景亮度场」。开运算是先腐蚀后膨胀腐蚀会把比结构元小的亮细节字符笔画、车标、反光点全部抹掉膨胀再把剩下的亮区恢复回原来的边界结果就是一张只有车身材质和光照渐变、没有细节的图。% Step1 读原始彩色图 Scolor imread(3.jpg); % Step2 灰度化后续所有算子都在这张单通道图上跑 Sgray rgb2gray(Scolor); % Step3 半径 13 的圆盘结构元做开运算抹掉比它小的亮细节剩下低频亮度场 se strel(disk, 13); Bgray imopen(Sgray, se); figure, imshow(Bgray), title(背景图像);半径13是这个算法的核心尺度参数。它要满足两个条件明显大于字符笔画的像素宽度640×480 下笔画大概 3–5 像素这样才能把字符从背景里抹掉同时明显小于车牌的像素宽度同分辨率下车牌宽度大概 100–150 像素这样车牌整体才不会被当成「细节」一起吃掉。取 13 正好落在中间。如果你把图统一缩放到 640 宽这个值可以直接用如果保持原图 1920 宽笔画和车牌的像素尺度都放大了三倍半径也该跟着放大到 40 左右否则背景图里会残留字符轮廓差分之后就变成噪点。3.2 背景差分增强与自适应阈值二值化有了背景图下一步就是把原图和背景相减。车牌区域的字符是高频强对比减完之后数值很大车身平缓区域减完接近 0比背景暗的区域会被截断到 0imsubtract是饱和减法不做负数回绕。这一步做完车牌区域就变成了整张图里最亮的几块之一。% Step4 原图减去背景亮度场把局部高对比区域抬出来 Egray imsubtract(Sgray, Bgray); figure, imshow(Egray), title(增强黑白图像); % Step5 用图像自身的动态范围推一个相对阈值而不是写死 128 fmax1 double(max(Egray(:))); % 差分图最大值 fmin1 double(min(Egray(:))); % 差分图最小值 level (fmax1 - (fmax1 - fmin1)/3) / 255; % 取动态范围上 2/3 处归一到 [0,1] bw22 im2bw(Egray, level); % 转二值 bw2 double(bw22); % 转 double 供后续 edge 使用level这个写法是纯经验公式含义是把「最大值往下退三分之一动态范围」的位置当分界线等价于把最亮的那三分之一像素判成前景。之所以不用graythreshOtsu是因为差分之后的直方图并不是干净的双峰分布——大面积车身是 0车牌是一小撮高值中间还有各种反光和纹理Otsu 求出来的阈值经常被车身区域拉偏。报告里这个写法的好处是它跟着每张图自身的动态范围走不会因为图整体偏暗就全黑。但它也有明确的失效条件如果图里根本找不到车牌比如车牌被完全遮挡fmax1就是某个噪点的值阈值会算得非常离谱二值化结果是一片雪花。工程上我一般会加一道保险判断fmax1 - fmin1是否过小过小就直接判定为「未检测到车牌」返回不要往下走。3.3 Canny 边缘提取与双方向形态学滤波二值图拿到之后提边缘。Canny 相比 Sobel、Prewitt 的优势在于它做了非极大值抑制和双阈值连接出来的边缘是单像素宽的细线不会因为梯度响应强就糊成一片粗边。这对车牌定位很重要因为后面要靠形态学把字符连成一个实心块边缘太粗会连成一片无法区分。% Step6 Canny 提边缘得到单像素宽的车牌字符轮廓 grd edge(bw2, canny); % Step7 三次形态学操作把散落的字符轮廓拼成车牌大小的实心块 bg1 imclose(grd, strel(rectangle, [5, 19])); % 横向闭连上同一行相邻字符 bg3 imopen(bg1, strel(rectangle, [5, 19])); % 开去掉小块噪点 bg2 imopen(bg3, strel(rectangle, [19, 1])); % 纵向开压掉竖直细长干扰三次操作的分工要看清。第一次是闭运算结构元[5,19]表示 5 行高、19 列宽的横向长条闭运算先膨胀后腐蚀膨胀时这个横条会把水平方向上距离小于 19 像素的两个字符连起来七个字符连成一整块第二次用同样尺寸做开运算把面积小于结构元的孤立噪点抹掉第三次换成[19,1]的竖长条做开运算专门对付垂直方向上的细长条——比如车牌上下边框被边缘检测提出来之后形成的两条横线或者车身缝隙。这里有个容易被忽略的点strel的参数是[行, 列]不是[宽, 高]。写成[19,5]意思完全不同会变成竖长条横向连不上字符。我第一次看这段代码的时候在这个地方愣了半天因为直觉上「横向结构元」应该先写宽度。3.4 连通域标记与长宽比筛选形态学处理完之后图上剩下若干块白色区域其中一块是车牌其余可能是车灯、格栅、贴纸、后视镜。bwlabel做八连通标记给每块区域一个整数编号然后用regionprops一次性取出每块区域的面积和外接矩形。% Step8 八连通标记 提取几何特征 [L, num] bwlabel(bg2, 8); stats regionprops(L, Area, BoundingBox); % 新版替代 imfeature Area [stats.Area]; Bbox cat(1, stats.BoundingBox); % 每行 [x y w h] % Step9 用长宽比 面积双条件筛出车牌候选 cand []; for k 1:num w Bbox(k,3); h Bbox(k,4); if w 30 || h 10, continue; end % 太小的直接丢 r w / h; % 蓝牌标准 440/140 ≈ 3.14 if r 2.2 r 4.2 Area(k) 400 cand(end1) k; end endBoundingBox的顺序是[x, y, width, height]x 是列方向、y 是行方向宽高比要用w/h而不是h/w。这个顺序在老版本imfeature里也是一样的所以从旧代码迁移过来时不用担心。筛选条件里长宽比区间2.2–4.2覆盖了绝大多数正面或小角度拍摄的蓝牌面积阈值400是为了滤掉那些刚好长宽比也接近 3 的小噪点这个数字跟图像分辨率直接相关换分辨率要重算。如果候选不止一个报告的思路是取宽高比最接近 3.14 的那个或者按面积取最大的——正面拍摄时面积最大的通常就是车牌倾斜角度大时按比值更稳。3.5 水平投影峰谷分析与拟合旋转校正切出车牌子图之后还有一个必须解决的问题车牌可能带几度到十几度的倾斜。倾斜会让垂直投影的字符间隙变得模糊切字就会错位。报告的做法是先对子图逐行求和得到水平投影在投影曲线上找车牌上边框和下边框的位置再拿这些边界点做一阶线性拟合斜率的反正切就是倾角。% Step10 水平投影逐行求 1 的个数得到一条反映行方向亮度的曲线 histrow sum(sbw1, 2); % 列向量元素 i 是第 i 行白点数 histcol sum(sbw1, 1); % 行向量元素 j 是第 j 列白点数 % Step11 对车牌上边缘的点做一阶拟合斜率换算成角度 % xdata/ydata 为上边缘每列第一个白点的行号与列号 fresult fit(xdata, ydata, poly1); % 需要 Curve Fitting Toolbox angle atan(fresult.p1) * 180 / pi; % 弧度转角p1 即斜率 % Step12 反向旋转把车牌摆正crop 保证输出尺寸不变 subcol imrotate(subcol1, angle, bilinear, crop); sbw imrotate(sbw1, angle, bilinear, crop);fit返回的是cfit对象p1是poly1模型的一次项系数也就是直线斜率。角度算出后要过一遍符号MATLAB 的图像坐标系 y 轴朝下如果拟合出来车牌是「左低右高」算出的角度符号和imrotate需要的旋转方向可能相反转完会更歪。判断方法很简单转完肉眼看一眼歪了就angle -angle。imrotate的第三个参数bilinear是双线性插值比默认的最近邻平滑字符边缘不会出现锯齿台阶第四个参数crop让输出图保持和输入一样的尺寸不加的话 MATLAB 会自动扩边把旋转后的整幅图都装下尺寸一变后面的坐标全乱。这两个参数都不是可选项。4. 字符分割与识别从车牌子图到七段字符串4.1 去上下边框与字符高度估计校平之后的车牌子图还是带边框的上下两条横线和左右两条竖线如果不去掉投影曲线两端会被撑起两个高峰字符的峰谷就淹在里面了。去边框靠的还是水平投影边框在投影上表现为两端各一个又高又窄的峰字符区域在中间是一段连续的起伏。% Step13 水平投影找字符带的上下界 histrow sum(sbw, 2); % 每行白点数 markrow find(histrow mean(histrow)); % 高于均值的行视为有效行 % 取连续段中最长的一段作为字符带两端即上边框下沿与下边框上沿 maxhight rowbot - rowtop 1; % 字符带高度 sbw2 sbw(rowtop:rowbot, :); % 裁掉上下边框后的子图用「高于均值」当判据是个粗糙但有效的做法因为字符行的白点数明显多于纯边框行和空白行。mean(histrow)这个阈值在有噪声的情况下会偏低导致边框行也被算进来更稳的写法是取0.5 * max(histrow)或者用中位数值得在改造时换掉。maxhight拿到之后不要只当个中间变量它有两个实际用途一是后面切字符时校验每个字符的高度是否合理差太多说明切歪了二是作为尺度参照判断这张车牌是不是太小、压根不该进识别环节。4.2 垂直投影找字符中心与最大字符宽度去完上下边框剩下的核心工作是把七个字符的位置定出来。垂直投影逐列求白点数在字符笔画的位置形成高柱在字符间隙的位置形成低谷找谷底就能找到分割点。但直接找谷底不稳因为字符内部也有间隙比如「8」的中间报告用的是「先找下降点、再推中心、再用中心距推最大宽度」的迂回路线。% Step14 对垂直投影做峰谷分析拿到每个字符的上升点与下降点 histcol sum(sbw2, 1); % markcol 存下降点markcol1 存上升点具体由阈值扫描得到 n1 numel(markcol) - 1; % Step15 由上升点/下降点推每个字符的中心列坐标 for k 1:n1 markcol3(k) markcol(k1) - markcol1(k1); % 下降点 markcol4(k) markcol3(k) - markcol(k); % 字符宽度 markcol5(k) markcol3(k) - double(uint16(markcol4(k)/2)); % 字符中心 end % Step16 中心距最大的一处 汉字与字母的分界去掉它后剩下的最大值 最大字符宽度 markcol6 diff(markcol5); maxs max(markcol6); findmax find(markcol6 maxs); markcol6(findmax) 0; maxwidth max(markcol6);这段逻辑里最巧妙的是findmax的用法。国内蓝牌第一位是汉字省简称第二位是字母汉字和字母之间的间距明显大于后面字母数字之间的间距所以「中心距最大的那一处」几乎必然是第 1、2 个字符的分界。拿到这个分界点findmax往前往后各取几个中心位置就能把七个字符全定位出来不用去猜字符个数。markcol4(k) markcol3(k) - markcol(k)这行算的是单个字符宽度配uint16取半是为了中心位置落在整数列上。diff(markcol5)得到相邻中心的间距序列findmax是最大间距的索引。4.3 切割、归一化与样本库落盘有了七个中心位置和统一的最大宽度切割就变成简单的矩形裁剪加缩放。每个字符按「中心 ± 最大宽度/2」裁出来然后统一缩放到 22 行 14 列——这个尺寸来自报告里样本库的规格所有模板必须和待识别字符完全一致否则逐像素相减没有意义。% Step17 按中心位置切出七个字符统一归一化后落盘建库 l 1; [m2, n2] size(subcol); for k findmax-1 : findmax5 % 从汉字到第 7 位共 7 个 cleft markcol5(k) - maxwidth/2; cright markcol5(k) maxwidth/2 - 2; if cleft 1 % 左越界钳制 cleft 1; cright maxwidth; end if cright n2 % 右越界钳制 cright n2; cleft n2 - maxwidth; end SegBw1 sbw(rowtop:rowbot, cleft:cright); SegBw2 imresize(SegBw1, [22 14]); % 必须与模板尺寸一致 imwrite(SegBw2, fullfile(sampleDir, [image int2str(k) .jpg]), jpg); l l 1; end越界钳制这两段不能省。车牌最左和最右的字符经常贴着边缘cleft算出来是负数或者cright超出列数MATLAB 会直接报索引越界。钳制之后宽度可能不足maxwidth但imresize会强行拉到 22×14影响可控。imwrite那行的路径F:\MATLAB\work\sam是典型的写死路径换台机器就崩。工程上的做法是先if ~exist(sampleDir,dir), mkdir(sampleDir); end再用fullfile拼。样本库的命名也要规范image1.jpg到image7.jpg只表示位置不表示字符是什么识别的时候得靠另一张对照表或者干脆把文件名改成字符本身比如京.jpg、A.jpg、8.jpg省掉一层映射。4.4 模板匹配打分与结果输出识别环节在报告里用的是最朴素的模板匹配把待识别字符的 22×14 二值矩阵和样本库里的每个模板逐像素做差绝对值求和误差最小的那个就是识别结果。这个方法的优点是零训练、可解释、错了一眼就能看出为什么错缺点是对字体、缩放、断裂极其敏感。% Step18 模板匹配逐张相减取绝对误差最小者为识别结果 bestErr inf; bestLabel ?; for i 1:numel(samples) d sum(abs(double(charImg(:)) - double(samples{i}(:)))); if d bestErr bestErr d; bestLabel labels{i}; end end fprintf(第 %d 位识别结果%s误差 %d\n, pos, bestLabel, bestErr);误差值bestErr一定要打印出来这是判断结果可不可信的唯一依据。实践里误差明显高于同批其他字符的那一位基本就是错的——报告里提到那个失败案例把 G 认成 B、K 认成 A、0 认成 8都是形状高度相似的字符对误差往往只差几十个像素。给误差加一个上限超过就输出「?」而不是强行给一个错答案比什么都认出来更有用。样本库的规模决定了上限。报告里只用了极少数模板换个字体、换个省份简称就废。做个能用的版本至少每个汉字、每个字母、每个数字各准备 3–5 个不同来源的样本误差取最小值而不是平均值。5. 避坑与常见问题六条跑不通时的排查记录5.1 报错Undefined function imfeature现象把报告里的代码原样复制进新版 MATLAB运行到区域特征那一步直接中断报函数未定义。原因imfeature是 MATLAB 6.x Image Processing Toolbox 里的函数从 7.x 开始被regionprops取代并移除报告写作年代较早代码没跟着更新。解决imfeature(L,basic)整体替换为regionprops(L,Area,BoundingBox,Centroid)取字段的方式也从Feastats.Area改成[stats.Area]这种逗号列表拼接。字段名本身没变改完逻辑完全一致。5.2 报错Undefined function fit或提示需要许可证现象算车牌倾角那一行报错或者弹窗提示 Curve Fitting Toolbox 未授权。原因fit属于 Curve Fitting Toolbox不是基础 MATLAB 和 Image Processing Toolbox 自带的很多精简安装或学生版没装。解决换polyfit。p polyfit(xdata, ydata, 1)返回[斜率, 截距]斜率就是p(1)后面对atan的调用完全不用改。polyfit是基础函数任何版本都有。这也是我后来写图像处理代码的默认选择——能不用工具箱就不用交作业和移植都省事。5.3 换一张图二值化结果全白或全黑现象原图能跑换一张阴天拍的或者逆光的bw2出来是一片纯白后面所有步骤连锁失效。原因level (fmax1-(fmax1-fmin1)/3)/255依赖图像自身的动态范围。低对比度的图fmax1-fmin1很小算出来的level逼近 0于是几乎所有像素都判为前景反过来高对比度图里如果有个强反光点fmax1被拉到极值level抬高前景变少字符断裂。解决加一层保护——先判断fmax1 - fmin1是否小于某个下限比如 20小于就说明这张图里根本没有可用的车牌对比度直接返回失败而不是硬算。另外可以试graythresh(Egray)做兜底两个阈值各跑一遍取前景面积落在合理区间比如占全图 5%–30%的那个。这个「按前景占比反推阈值是否合理」的检查是我后来在所有二值化环节都会加的。5.4imwrite报无法写入但前面所有显示都正常现象定位、分割、figure全部正常弹窗唯独最后存样本库那一步报错程序中断。原因路径F:\MATLAB\work\sam\image写死在代码里当前机器上没有这个盘和目录或者没有写权限。解决改成sampleDir fullfile(pwd,samples); if ~exist(sampleDir,dir), mkdir(sampleDir); end然后imwrite(..., fullfile(sampleDir, [image int2str(k) .jpg]))。顺便说一句这个报错之所以容易被误判是因为它出现在循环末尾前面的subplot已经把所有图都画出来了视觉上「程序跑成功了」实际只是没存下来。5.5 字符切出七个但整体错位最后一位是车牌边框现象分割出来的图里前六位看着正常第七位是一条竖线或者空白识别结果稳定错一位。原因findmax定位的是汉字与字母的最大中心距但如果垂直投影的阈值没调好最右侧的边框被误当成一个字符峰中心位置整体右移一格或者车牌只有六位、第七位本来就空。解决先校验findmax的合理性它应该在 1 到 3 之间汉字和字母的分界不会出现在很后面再校验切出来的每个字符宽度是否都在0.6*maxwidth到1.4*maxwidth之间超出范围的位置直接标记为无效而不是强行识别。加完这两道校验错位问题基本能拦住。5.6 换了白底黑字或黄底黑字的车牌识别全乱现象蓝底白字的图跑得很好换成其他颜色的车牌定位出来的区域是反的字符全是黑的底白的字。原因整套算法的极性假设是「亮字符 暗底牌」二值化之后前景是 1。白底黑字刚好相反im2bw出来前景背景翻转后面的投影、切割全部失效。解决报告本身也提到了这一点——对白底黑字需要在定位之后加一步sbw imcomplement(sbw)把 0/1 翻转回来字符分割算法可以原样复用。判断要不要翻转看二值图里前景像素占比超过一半就说明极性反了。黄底黑字则更麻烦一些rgb2gray之后黄底和黑字的亮度差不像蓝白那么极端阈值那一步要单独调。6. 二次开发把它改成能反复验证的小工具原版是「一个脚本跑一张图输出七张字符小图和一个车牌号」这个形态用来交报告足够但想真正拿它练手或者做毕设得先做三件事。第一件事是切片成函数。把main.m里的平铺代码拆成locatePlate(img)返回车牌子图加倾角、segmentChars(sbw)返回七个 22×14 矩阵、recognize(chars, samples)返回字符串和每位误差主脚本只负责遍历目录、调用、汇总。这么改的好处是每一段都能独立喂输入测定位错了不用管切字切字错了不用管识别——不然一次跑完只知道「结果错了」不知道错在哪一段。第二件事是换掉写死的样本库。把samples/下的文件命名成字符本身比如京_01.jpg、A_03.jpg加载时用dir加extractBetween解析出标签存成一个struct数组。然后写一个小脚本把已知答案的车牌图批量跑一遍统计「定位命中率、切字正确率、字符准确率」三个数每次改参数都跑一遍看曲线往哪走。这一步没有捷径我试过只靠肉眼看单张图调参结果是调 A 图好 B 图坏来回横跳。第三件事是给识别加兜底。模板匹配的误差分布是判断结果可信度的唯一信息可以统计每类字符在样本库内部的平均类内距离识别时如果某一位的最近邻误差超过类内平均距离的 1.5 倍就输出?而不是硬给答案。整牌七位里有两位以上是?就把这张图判为拒识转人工。拒识率这个指标在做课程设计时通常没人看但它恰恰是真实场景里最重要的一个数。想再往前一步可以把模板匹配换成「HOG 特征 最近邻」或者干脆用ocr函数做对照实验看看手工模板和通用 OCR 在你这批图上的差距两者的错误模式往往互补拿来写分析很有说服力。还有一个常被忽略的优化点strel(disk,13)的半径不该是常数应该按车牌高度的估计值动态算比如半径 round(0.1 * 车牌高度)这样换分辨率就不用重新调参。从那以后我每次做图像处理相关的东西都强制自己先把中间结果全部落盘、再写校验条件、最后才看最终输出宁可多写二十行if也不让程序在错误的状态下继续往下跑。图像处理这行的错都是静默传播的前一步偏一点后面全偏但不报错。希望这些能帮你在跑这份课程设计时少走几个弯路。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑