別入門與實(shí)踐:從圖像預(yù)處理到模板匹配)
簡(jiǎn)介一份基于MATLAB的車牌識(shí)別課程論文與配套源碼資料面向數(shù)字圖像處理、智能交通相關(guān)課程設(shè)計(jì)與畢業(yè)設(shè)計(jì)人群覆蓋車牌定位、字符切割、字符識(shí)別等核心環(huán)節(jié)。文檔系統(tǒng)闡述車牌區(qū)域提取的灰度化、Roberts邊緣檢測(cè)、腐蝕平滑與邊界值計(jì)算以及字符切割中的去噪、膨脹處理和字符歸一化同時(shí)討論國內(nèi)車牌漢字、數(shù)字、字母混合和外部光照干擾等難點(diǎn)并給出數(shù)學(xué)形態(tài)法、彩色分割、垂直投影等定位與分割思路。配套部分包含圖像預(yù)處理、邊緣提取等程序流程說明、關(guān)鍵公式與運(yùn)行效果圖可直接作為課程報(bào)告、論文寫作或MATLAB算法復(fù)現(xiàn)的參考。壓縮包為單個(gè)doc文件容量約594KB整體結(jié)構(gòu)清晰目前已有314人學(xué)習(xí)適合需要快速搭建車牌識(shí)別實(shí)驗(yàn)框架的初學(xué)者。1. 一套能跑的 MATLAB 車牌識(shí)別從課程設(shè)計(jì)到實(shí)用項(xiàng)目的完整范本如果你正在做數(shù)字圖像處理課程設(shè)計(jì)或者在找一個(gè)能直接運(yùn)行、每步都能可視化的車牌識(shí)別參考實(shí)現(xiàn)這份《MATLAB車牌識(shí)別論文加源碼》值得拆開讀一遍。整套方案沒有走深度學(xué)習(xí)路線而是把經(jīng)典圖像處理三板斧練了一遍灰度化與邊緣檢測(cè)做預(yù)處理數(shù)學(xué)形態(tài)學(xué)和雙向投影做車牌定位垂直投影切割字符最后用模板匹配輸出車牌號(hào)。它最大的價(jià)值在于算法透明、參數(shù)全暴露在腳本里適合新手理解識(shí)別鏈路也適合熟手把它當(dāng)算法基線做二次改造。適用人群很明確寫圖像處理課程論文的學(xué)生、需要快速搭 Demo 驗(yàn)證效果的開發(fā)者以及想把手寫字符識(shí)別流程徹底吃透的算法初學(xué)者。2. 圖像預(yù)處理鏈路灰度化、邊緣檢測(cè)與形態(tài)學(xué)操作車牌識(shí)別第一步不是直接找車牌而是把一張彩色車輛照片處理成干凈的、車牌區(qū)域能凸顯出來的二值圖。整條鏈路在源碼里是串行執(zhí)行的每一步都落一個(gè)中間變量方便在 MATLAB 里斷開調(diào)試。這一章的輸入是一張普通的車輛照片輸出是一張理論上只剩車牌區(qū)域前景點(diǎn)的二值圖后面的定位和字符切割全部建立在這張圖上。2.1 灰度化與線性灰度增強(qiáng)先統(tǒng)一色彩空間[filename, filepath] uigetfile(.jpg, 輸入一個(gè)需要識(shí)別的車牌圖像); I imread([filepath, filename]); % 拼出完整路徑并讀圖 I1 rgb2gray(I); % RGB彩色圖轉(zhuǎn)灰度圖 figure(name,灰度處理前), subplot(1,2,1), imshow(I1); subplot(1,2,2), imhist(I1); % 顯示灰度直方圖 I1 imadjust(I1, [0.3,0.7], []); % 線性灰度變換拉伸中間灰度區(qū)間 I1 medfilt2(I1); % 3x3中值濾波去噪這段是標(biāo)準(zhǔn)的圖像預(yù)處理好習(xí)慣先看直方圖再?zèng)Q定增強(qiáng)參數(shù)。imhist 打印灰度分布能直觀看出圖像是偏亮還是偏暗——如果峰值集中在 0.3 以下說明車牌區(qū)域和背景的對(duì)比度不足后面的邊緣檢測(cè)會(huì)漏掉很多字符邊緣。imadjust 的[0.3,0.7]參數(shù)是把原圖歸一化灰度在 0.3 到 0.7 之間的像素線性拉伸到 0 到 1 的完整區(qū)間小于 0.3 的像素變成 0大于 0.7 的變成 1。效果是讓中間灰度段的對(duì)比度翻倍車牌字符和底色的差距被放大。如果光照特別強(qiáng)或特別暗這個(gè)區(qū)間都要跟著調(diào)暗圖用[0.2,0.6]亮圖用[0.4,0.8]這是比較常用的經(jīng)驗(yàn)值。medfilt2 是 3x3 中值濾波把每個(gè)像素替換成周圍 9 個(gè)像素的中值對(duì)椒鹽噪聲非常有效。它比均值濾波好在保留邊緣不模糊而邊緣正是車牌識(shí)別最依賴的特征。注意濾波放在 imadjust 之后而不是之前是因?yàn)榫€性灰度變換會(huì)放大噪聲先增強(qiáng)再濾波能一起處理掉增強(qiáng)后的殘留噪點(diǎn)。2.2 Roberts邊緣檢測(cè)找出字符與底色的跳變點(diǎn)I2 edge(I1, roberts, 0.25, both); % Roberts算子閾值0.25雙向檢測(cè) figure(name,邊緣檢測(cè)), imshow(I2);edge 函數(shù)里roberts對(duì)應(yīng) Roberts 交叉梯度算子它的思路是用對(duì)角線相鄰像素的差值近似梯度水平方向梯度 Gxf(x,y)-f(x-1,y-1)垂直方向梯度 Gyf(x,y-1)-f(x-1,y)幅值 Gsqrt(Gx^2Gy^2)幅值超過 0.25 的像素被標(biāo)為邊緣點(diǎn)。0.25 是歸一化后的閾值不是固定值——如果圖像噪聲大閾值要往上提不然邊緣圖會(huì)糊成一片如果車牌字符過淡閾值要降低。怎么判斷合不合適看邊緣圖里車牌區(qū)域是不是字符輪廓完整、背景區(qū)域有沒有大量白點(diǎn)。為什么這里選 Roberts 而不是 Sobel 或 Canny我拆這套代碼時(shí)對(duì)比過Sobel 對(duì)橫向邊緣響應(yīng)更強(qiáng)但車牌漢字橫豎筆畫都有會(huì)讓豎筆畫邊緣變?nèi)魿anny 的雙閾值會(huì)引入大量與牌照無關(guān)的紋理邊緣后續(xù)形態(tài)學(xué)處理更麻煩。Roberts 計(jì)算量最小、對(duì)細(xì)筆畫邊緣最敏感配合后續(xù)腐蝕操作反而能得到一個(gè)干凈的車牌輪廓。both參數(shù)表示水平和垂直兩個(gè)方向都做梯度運(yùn)算缺省只做垂直方向?qū)h字這種橫豎交錯(cuò)的字符必須雙向檢測(cè)。2.3 腐蝕與閉運(yùn)算把字符邊緣連成連通域se [1;1;1]; % 豎直3x1結(jié)構(gòu)元素 I3 imerode(I2, se); % 腐蝕保留連續(xù)豎直邊緣 se strel(rectangle, [20,20]); % 20x20矩形結(jié)構(gòu)元素 I4 imclose(I3, se); % 閉運(yùn)算連接鄰近前景塊第一次腐蝕的 se 是 3x1 豎直向量意思是輸出像素取輸入圖像 3x1 鄰域里的最小值只要這個(gè)豎直條里有一個(gè)像素不是前景輸出就是 0。效果是只有豎直方向上連續(xù) 3 個(gè)點(diǎn)都是邊緣才保留下來。車牌邊框的豎邊連續(xù)能留下來字符橫筆畫產(chǎn)生的橫向邊緣在豎直方向不連續(xù)被刪掉背景里零散噪點(diǎn)也基本被過濾。這一步的目的是簡(jiǎn)化畫面讓后面投影定位時(shí)干擾更少。imclose 是閉運(yùn)算先膨脹后腐蝕。20x20 矩形結(jié)構(gòu)元素決定“多近的前景會(huì)被連在一起”——兩個(gè)前景塊之間空隙小于 20 像素就會(huì)被膨脹填上再腐蝕回來時(shí)已經(jīng)連成整體。這正好匹配車牌的幾何特點(diǎn)字符間距和邊框間距通常小于 20 像素而整個(gè)車牌區(qū)域的寬遠(yuǎn)大于 20 像素。閉運(yùn)算的結(jié)果是出現(xiàn)一個(gè)或多個(gè)大塊的連通區(qū)域其中最大的那個(gè)通常是車牌。如果結(jié)構(gòu)元素太小字符邊緣連不成片太大車標(biāo)和車身裝飾條也會(huì)被連進(jìn)來。20x20 是原作者調(diào)的默認(rèn)值換數(shù)據(jù)集時(shí)值得重新試驗(yàn)。2.4 移除小對(duì)象濾掉車標(biāo)和車身文字干擾I5 bwareaopen(I4, 1000); % 刪除面積小于1000像素的前景連通域 figure(name,移除小對(duì)象), imshow(I5);bwareaopen 遍歷二值圖像的所有連通對(duì)象統(tǒng)計(jì)每個(gè)對(duì)象的前景像素?cái)?shù)小于給定值的整個(gè)填成背景。默認(rèn) 8 鄰域連通也就是斜對(duì)角相鄰也算同一個(gè)對(duì)象。1000 這個(gè)閾值和圖像分辨率有關(guān)分辨率越高車牌區(qū)域的前景像素越多閾值可以越大。源碼注釋里寫了 2000實(shí)際跑的是 1000我建議在調(diào)試時(shí)用regionprops把所有連通域面積打出來看一下車牌區(qū)域的面積通常是第二名的好幾倍這時(shí)候閾值怎么定都不會(huì)錯(cuò)。這一步處理完理論上畫面里只剩車牌這一塊前景可以直接進(jìn)入投影定位。調(diào)試這段鏈路我一般這樣定位問題灰度圖對(duì)比度不夠就調(diào) imadjust邊緣圖太花就調(diào)高 edge 閾值腐蝕后還殘留橫線就調(diào)大腐蝕結(jié)構(gòu)元素閉運(yùn)算連通不到一起就調(diào)大矩形尺寸。逐段判斷不要一上來就懷疑最后的結(jié)果。3. 車牌定位核心雙向投影與經(jīng)驗(yàn)邊界校正預(yù)處理輸出 I5 理論上只剩車牌區(qū)域的前景像素但還沒有任何幾何信息。定位這一步要回答兩個(gè)問題車牌在圖片的哪幾行、哪幾列源碼用水平加垂直雙向投影來實(shí)現(xiàn)思路非常直接把前景像素分別往 x 軸和 y 軸方向累加得到一個(gè)投影曲線曲線峰值所在的位置就是車牌中心從峰值向兩側(cè)掃描到曲線跌落的位置就是邊界。3.1 水平投影找到車牌所在的行區(qū)間[y, x, z] size(I5); % 取圖像尺寸y行數(shù) x列數(shù) myI double(I5); % 轉(zhuǎn)double便于1判斷 Blue_y zeros(y, 1); % 每行前景像素計(jì)數(shù)數(shù)組 for i 1:y for j 1:x if myI(i, j, 1) 1 % 前景點(diǎn)為1 Blue_y(i, 1) Blue_y(i, 1) 1; end end end [temp, MaxY] max(Blue_y); % 前景像素最多的行這段代碼用雙重循環(huán)逐行統(tǒng)計(jì)前景點(diǎn)數(shù)量得到行投影向量 Blue_y。max 函數(shù)返回兩個(gè)值第一返回值 temp 是最大值本身第二返回值 MaxY 是它出現(xiàn)的行號(hào)也就是車牌中心行。需要提醒的是if myI(i,j,1)1判斷的是二值圖的前景源碼注釋寫“判斷藍(lán)色像素”容易誤導(dǎo)初學(xué)者。實(shí)際上預(yù)處理后已經(jīng)是黑白二值圖沒有顏色語義這里就是在數(shù)白色前景點(diǎn)真正的顏色信息要到后面定位裁剪時(shí)才從原圖取回來。統(tǒng)計(jì)完行投影后從中心行分別向上、向下掃描邊界PY1 MaxY; while (Blue_y(PY1, 1) 5) (PY1 1) PY1 PY1 - 1; end PY2 MaxY; while (Blue_y(PY2, 1) 5) (PY2 y) PY2 PY2 1; end兩個(gè) while 循環(huán)的邏輯是從中心行往上走只要當(dāng)前行的前景點(diǎn)數(shù)不小于 5就繼續(xù)往上直到某一行前景點(diǎn)數(shù)小于 5 或到達(dá)圖像邊緣為止。這個(gè) 5 像素閾值是經(jīng)驗(yàn)值作用是容忍車牌區(qū)域內(nèi)可能出現(xiàn)的零星噪聲——正常情況下車牌每一行都有大量前景像素遠(yuǎn)大于 5而邊界外的噪聲行通常只有 1、2 個(gè)前景點(diǎn)。如果閾值設(shè)成 1邊界會(huì)非常毛躁設(shè)成 10傾斜車牌或光照不均導(dǎo)致的行稀疏區(qū)域會(huì)被切掉。PY1 和 PY2 最終圈定了車牌在圖像中的行范圍。3.2 垂直投影限定列范圍行范圍確定后垂直投影只掃描 PY1 到 PY2 之間的行既減少計(jì)算量又避免把行范圍外的干擾算進(jìn)來Blue_x zeros(1, x); for j 1:x for i PY1:PY2 % 只掃車牌行區(qū)間 if myI(i, j, 1) 1 Blue_x(1, j) Blue_x(1, j) 1; end end end PX1 1; while (Blue_x(1, PX1) 3) (PX1 x) PX1 PX1 1; end PX2 x; while (Blue_x(1, PX2) 3) (PX2 PX1) PX2 PX2 - 1; endBlue_x 統(tǒng)計(jì)每一列在車牌行區(qū)間里的前景像素總數(shù)。PX1 從左邊開始向右找第一個(gè)前景列數(shù)大于等于 3 的列PX2 從右邊向左同理。列閾值用 3 而不是 5是因?yàn)樽址g有間隙漢字和字母的筆畫在某幾列上可能只有 2、3 個(gè)前景點(diǎn)閾值太高會(huì)把字符起始列漏掉。兩個(gè)方向閾值不同的原因就在這行方向上整個(gè)車牌寬度都有字符覆蓋列方向上字符筆畫稀疏得多。到這里車牌的四個(gè)邊界都有了粗略估計(jì)。3.3 邊界校正與彩色車牌裁剪投影直接給的邊界往往太緊會(huì)切掉車牌的上邊框或字符的外沿。源碼在邊界外擴(kuò)了幾個(gè)像素再從原圖裁剪PY1 PY1 - 2; % 上邊界向上擴(kuò)2像素 PX1 PX1 - 2; % 左邊界向左擴(kuò)2像素 PX2 PX2 3; % 右邊界向右擴(kuò)3像素 PY2 PY2 10; % 下邊界向下擴(kuò)10像素 dw I(PY1:PY2-8, PX1:PX2, :); % 從原彩色圖上裁剪車牌 imwrite(dw, dw.jpg);擴(kuò)邊的思路是橫向投影時(shí)車牌下邊緣因?yàn)橛新萁z、底框?qū)е逻吔缍ㄎ黄咚韵逻吔缍鄶U(kuò) 10 像素再從底部剪掉 8 像素留下一個(gè)略寬松的車牌區(qū)域。PX2 加 3 是因?yàn)檐嚺频挠覀?cè)字符間距較大直接投影容易丟掉最后一個(gè)字符的右邊緣。這些數(shù)字沒有理論推導(dǎo)完全是原分辨率下的經(jīng)驗(yàn)值。如果你換一張分辨率不同的測(cè)試圖應(yīng)該按比例調(diào)整比如分辨率翻倍擴(kuò)邊像素也大致翻倍。定位效果直接看 dw 圖就能判斷理想結(jié)果是從原圖裁出一塊尺寸比例接近 3:1 的彩色區(qū)域包含完整的車牌字符。如果 dw 里缺字符或混入車身區(qū)域優(yōu)先回看 I5 是不是干凈的前景圖。定位最怕的是傾斜車牌純水平垂直投影對(duì)傾斜超過 5 度的車牌基本失效后面二次開發(fā)會(huì)提到怎么處理。4. 字符分割與識(shí)別從二值化到模板匹配的最后一公里定位得到彩色車牌 dw 之后剩余工作分兩步把車牌圖像切成 7 個(gè)獨(dú)立字符再用模板匹配逐個(gè)識(shí)別。這一章是整個(gè)流程里最瑣碎的部分也是參數(shù)最多、最容易翻車的地方。4.1 車牌二值化灰度閾值的選擇與自適應(yīng)膨脹腐蝕a imread(dw.jpg); % 讀回定位裁剪出的車牌 b rgb2gray(a); % 車牌彩色圖轉(zhuǎn)灰度 b imadjust(b, [0.3,0.7], []); % 再做一次線性灰度增強(qiáng) d im2bw(b, 0.4); % 閾值0.4轉(zhuǎn)二值圖 d medfilt2(d); % 中值濾波去二值化后的毛刺車牌二值化和普通灰度圖二值化不一樣車牌字符和底色對(duì)比度本來就高理論上 0.3 到 0.6 之間的閾值都能把字符分出來。但光照不均時(shí)車牌的亮點(diǎn)和陰影會(huì)讓同一張圖里字符亮度不一致固定閾值 0.4 可能讓部分字符斷筆。原作在注釋里寫了更穩(wěn)妥的方案根據(jù)灰度最大最小值自動(dòng)計(jì)算閾值Tround(g_max-(g_max-g_min)/3)也就是取灰度范圍偏亮的位置。這個(gè)公式的直覺是字符取高灰度、背景取低灰度閾值落在兩者之間偏上的位置避免把字符當(dāng)成背景。二值化后源碼做了一步“看面積比決定膨脹還是腐蝕”的操作se eye(2); % 2x2單位矩陣作結(jié)構(gòu)元素 [m, n] size(d); if bwarea(d)/m/n 0.365 d imerode(d, se); % 前景占比過大腐蝕收縮 elseif bwarea(d)/m/n 0.235 d imdilate(d, se); % 前景占比過小膨脹加粗 endbwarea 計(jì)算二值圖前景總面積m*n 是圖像總像素?cái)?shù)兩個(gè)的比值就是字符面積占車牌面積的比例。正常車牌字符筆畫面積占比集中在 0.235 到 0.365 之間。高于 0.365 說明前景太多可能是噪聲被當(dāng)成字符用 imerode 把筆畫變細(xì)低于 0.235 說明前景太少字符可能斷筆太細(xì)用 imdilate 加粗。這個(gè)自適應(yīng)的思路很實(shí)用——它不需要知道圖像分辨率只看比例就能判斷當(dāng)前二值化是否偏了。結(jié)構(gòu)元素取 eye(2) 是 2x2 單位矩陣腐蝕和膨脹的幅度很小一次處理不會(huì)過度改變筆畫粗細(xì)。4.2 垂直投影字符切割qiege與getword的配合切割是整個(gè)系統(tǒng)里最依賴圖像質(zhì)量的環(huán)節(jié)。源碼的策略是先用 qiege 子程序清理二值圖像的邊緣干擾再通過垂直投影把字符一個(gè)個(gè)切出來。qiege 在原素材里沒有單獨(dú)貼源碼但調(diào)用方式很明確它輸入二值圖輸出的是去掉上下左右多余邊框和離散噪點(diǎn)的干凈二值圖。常見實(shí)現(xiàn)是去掉四周與邊框相連的前景像素再做一次連通域篩選。getword 則是從圖左邊緣開始逐列掃描垂直投影遇到連續(xù)前景列就截成一個(gè)候選字符返回字符圖像和剩余圖像。主程序里的核心切割邏輯如下s sum(d); % 每列前景像素?cái)?shù)垂直投影 while j ~ n while s(j) 0 % 跳過字符間隙的空白列 j j 1; end k1 j; % 字符起始列 while s(j) ~ 0 j n-1 % 繼續(xù)掃到字符結(jié)束列 j j 1; end k2 j - 1; % 字符結(jié)束列 if k2 - k1 round(n/6.5) % 寬度異常大說明可能是兩個(gè)字符粘連 [val, num] min(sum(d(:, [k15:k2-5]))); % 在投影谷底切開 d(:, k1num5) 0; end endsum(d) 對(duì)二值圖逐列求和每列的值表示該列有多少個(gè)前景像素。字符筆畫列前景像素多字符間隙列接近 0。內(nèi)層第一個(gè) while 跳過空白列找到字符起始位置 k1第二個(gè) while 掃描到投影落回 0 的位置得到結(jié)束位置 k2。正常情況下 7 個(gè)字符的寬度差別不大單個(gè)字符寬度約為整張圖寬度的 1/7所以round(n/6.5)作為“這個(gè)塊是不是太寬”的判斷閾值——比 1/7 略大允許漢字和數(shù)字正常寬度范圍。如果某個(gè)塊超過這個(gè)值說明可能有兩個(gè)字符粘在一起這時(shí)在塊內(nèi)找投影最小值的位置把這一列填成 0強(qiáng)行分成兩個(gè)字符。這就是垂直投影分割的經(jīng)典補(bǔ)丁正常用投影間隙切粘連時(shí)用投影谷底強(qiáng)行斷開。字符切割完成后前幾個(gè)字符還要去掉左側(cè)的干擾塊while flag 0 [m, n] size(d); wide 0; while sum(d(:, wide1)) ~ 0 % 統(tǒng)計(jì)左起連續(xù)前景列寬度 wide wide 1; end if wide 10 % 寬度小于10像素判定為左側(cè)干擾 d(:, 1:wide) 0; % 抹掉干擾列 d qiege(d); else temp qiege(imcrop(d, [1 1 wide m])); % 檢查這個(gè)塊的中間三分之二區(qū)域前景占比漢字通常很飽滿 two_thirds sum(sum(temp([round(m/3):2*round(m/3)], :))); if two_thirds/sum(sum(temp)) 0.25 flag 1; word1 temp; % 確認(rèn)這是第一個(gè)字符 end d(:, 1:wide) 0; d qiege(d); end endwide 統(tǒng)計(jì)左側(cè)連續(xù)前景列的數(shù)量。車牌左側(cè)經(jīng)常殘留一塊定位時(shí)的背景邊緣寬度一般遠(yuǎn)小于 10 像素直接抹掉。寬度大于 10 的塊不一定是干擾可能是真的第一個(gè)字符省份漢字這時(shí)用三分之二區(qū)域前景占比做二次判斷漢字筆畫集中在中間高度字符區(qū)域中間三分之二行的前景占比超過 0.25 就認(rèn)為是合法字符。這段邏輯寫成 while 循環(huán)的原因是干擾可能有多塊需要多次切割才能碰到真正的漢字。0.25 這個(gè)值也是經(jīng)驗(yàn)值筆畫粗的車牌可以放寬到 0.2。后續(xù)幾個(gè)字符直接調(diào)用[word2,d]getword(d)依次切出邏輯與上面一致。4.3 字符歸一化與模板匹配識(shí)別切出的字符大小不一致模板匹配前必須統(tǒng)一尺寸。源碼把所有字符縮放到 40x20word1 imresize(word1, [40 20], nearest); % 高度40 寬度20最近鄰插值 imwrite(word1, 1.jpg);imresize 第二參數(shù)[40 20]是輸出尺寸40 行 20 列模板庫里所有模板也是這個(gè)尺寸。插值方式用nearest是速度最快的最近鄰但會(huì)產(chǎn)生鋸齒對(duì)匹配結(jié)果有影響實(shí)測(cè)用bilinear雙線性插值識(shí)別率能提高一點(diǎn)代價(jià)是運(yùn)行時(shí)間略微增加。每個(gè)字符還單獨(dú)寫成 1.jpg 到 7.jpg方便中途檢查切割效果。模板匹配本身就是一個(gè)逐像素比較的循環(huán)liccode char([0:9 A:Z 京遼桂陜蘇渝浙]); % 模板字符表 ... for k2 kmin:kmax fname strcat(字符模板\, liccode(k2), .jpg); SamBw2 imread(fname); % 讀一個(gè)模板 for i 1:40 for j 1:20 SubBw2(i,j) SegBw2(i,j) - SamBw2(i,j); % 逐像素做差 end end Dmax 0; for k1 1:40 for l1 1:20 if SubBw2(k1,l1) 10 || SubBw2(k1,l1) -10 Dmax Dmax 1; % 差異超過10算一個(gè)錯(cuò)誤點(diǎn) end end end Error(k2) Dmax; end Error1 Error(kmin:kmax); MinError min(Error1); findc find(Error1 MinError); % 取差異最小的模板 Code(l) liccode(findc(1) kmin - 1);這段做了三件事構(gòu)造模板索引、兩圖相減、統(tǒng)計(jì)差異點(diǎn)數(shù)。差異容限 10 像素的意思是允許字符邊緣有輕微偏移和噪聲超過 10 認(rèn)為是實(shí)質(zhì)性差異。匹配范圍按車牌規(guī)則收縮第一位是省份漢字只遍歷 37 到 43第二位是英文字母遍歷 11 到 36第三位開始字母數(shù)字混合遍歷 1 到 36。這樣既不浪費(fèi)時(shí)間也避免把數(shù)字 8 識(shí)別成漢字。Code 每識(shí)別一個(gè)字符追加一次第 3 位前插入兩個(gè)空格最終拼出類似“京A·12345”形式的字符串。模板匹配的識(shí)別率上限由模板庫質(zhì)量決定。這套源碼只提供了每個(gè)字符一張模板同一字符在不同字體、不同筆畫粗細(xì)下差異很大所以它對(duì)輸入圖像要求很苛刻——車牌必須正、清晰、字符筆畫粗細(xì)和模板相近。這也是二次開發(fā)最先要升級(jí)的地方。5. 避坑排查四個(gè)讓新手翻車的常見問題這套源碼能跑通但跑通和跑對(duì)是兩回事。我在拆它的過程中踩了不少坑整理成排查清單按現(xiàn)象到原因到解決的順序?qū)憽?.1 現(xiàn)象車牌定位框偏大或偏小甚至框到車身定位剪切后的 dw 圖里混入大量車身區(qū)域或者只剩半個(gè)車牌。原因在定位完全依賴 I5 的前景分布I5 里如果殘留車標(biāo)、車身反光條的前景塊這些塊面積大于 1000 會(huì)被保留投影峰值被拉偏如果車牌本身亮度低邊緣檢測(cè)漏檢投影峰值反而落在別處。解決先用regionprops打印所有連通域面積確認(rèn)車牌區(qū)域面積是第二名的 3 倍以上若車牌區(qū)域面積偏小把 imadjust 區(qū)間調(diào)低讓暗部細(xì)節(jié)出來若殘留干擾多把 bwareaopen 閾值從 1000 提到 3000 再試。調(diào)參順序不要亂從源頭往尾部走先看邊緣圖再?zèng)Q定動(dòng)哪里。5.2 現(xiàn)象字符分割時(shí)兩個(gè)字粘連成一個(gè)塊切割出的 7 個(gè)字符里某兩個(gè)字符被合成一個(gè)寬度明顯是其他字符的 1.5 倍以上。原因多是 imclose 的 20x20 矩形結(jié)構(gòu)元素把字符間隙堵住了二值化時(shí)字符筆畫本身又被加粗也可能是輸入圖像分辨率較高字符間隙在圖像上超過 20 像素被膨脹連上。解決把 imclose 結(jié)構(gòu)元素從[20,20]降到[12,12]讓閉運(yùn)算只連接真正的邊框和字符邊緣如果還粘連同時(shí)把 im2bw 閾值從 0.4 提高到 0.5讓字符筆畫變細(xì)、間隙更明顯。調(diào)這兩處要配對(duì)著改改完重新從定位開始跑看 I4 效果圖確認(rèn)粘連是否解除。5.3 現(xiàn)象模板匹配把“8”識(shí)別成“B”、“0”識(shí)別成“O”識(shí)別結(jié)果字符串里數(shù)字位出現(xiàn)字母、字母位出現(xiàn)數(shù)字其他字符都對(duì)。原因一是模板只有一張40x20 的 nearest 縮放讓字符產(chǎn)生鋸齒8 和 B 的差異點(diǎn)集中在左下角開口處鋸齒會(huì)掩蓋這個(gè)差異二是二值化后字符粗細(xì)不均筆畫寬的 8 和窄的 B 更接近。解決把 imresize 插值改成bilinear讓邊緣更平滑更重要的是擴(kuò)充模板庫每個(gè)字符存 2 到 3 套不同字形的模板匹配時(shí)取所有模板的最小差異。對(duì) 8 和 B 這類相似字符可以在差異點(diǎn)計(jì)數(shù)時(shí)給不同位置加權(quán)重字符下半部分的差異權(quán)重大于上半部分這樣 8 和 B 的差異就會(huì)被放大。5.4 現(xiàn)象運(yùn)行到匹配階段報(bào)錯(cuò)“找不到字符模板文件”報(bào)錯(cuò)信息顯示字符模板\*.jpg不存在程序在 imread 處中斷。原因是相對(duì)路徑問題——腳本用strcat(字符模板\, liccode(k2), .jpg)拼路徑如果 MATLAB 當(dāng)前工作目錄不在源碼目錄或者字符模板文件夾名不一致路徑就失效。解決用fullfile(pwd, 字符模板, [liccode(k2), .jpg])拼絕對(duì)路徑或者把字符模板文件夾添加到 MATLAB 路徑。更穩(wěn)妥的做法是在 main 開頭加一句cd(fileparts(mfilename(fullpath)))讓工作目錄始終切到腳本所在目錄。這個(gè)坑和算法無關(guān)但足以讓整個(gè)程序跑不起來。5.5 現(xiàn)象同一張圖跑兩次結(jié)果不穩(wěn)定有時(shí)候連續(xù)運(yùn)行兩次識(shí)別結(jié)果或定位框不一樣。排除圖片選擇差異后最常見的元兇是工作區(qū)變量殘留——上一次運(yùn)行的中間變量沒清干凈第二次運(yùn)行時(shí)定位和切割拿到的還是舊值。原代碼開頭只寫了close all; clc沒有清變量。解決腳本第一行加clear all;確保每次運(yùn)行是全新狀態(tài)。調(diào)試時(shí)養(yǎng)成習(xí)慣凡是跑圖像處理腳本clear; close all; clc三件套放開頭能省掉大量靈異現(xiàn)象的排查時(shí)間。檢查環(huán)節(jié)看什么合格標(biāo)準(zhǔn)灰度圖/直方圖車牌與背景對(duì)比度直方圖中段有明顯分峰邊緣圖字符輪廓完整性字符筆畫連續(xù)無大面積斷線腐蝕/閉運(yùn)算后前景塊分布前景集中在車牌附近移除小對(duì)象后連通域數(shù)量只剩一到兩個(gè)大連通域投影曲線峰值位置峰值對(duì)應(yīng)車牌中心二值化后字符清晰度無斷裂、無大面積粘連切割字符7個(gè)字符寬度寬度基本均勻識(shí)別結(jié)果首字省份漢字第一位為漢字、第二位為字母排查時(shí)從上往下逐行對(duì)照卡在哪一步就只調(diào)那一步的參數(shù)形成定式之后調(diào)試效率會(huì)高很多。6. 二次開發(fā)思路從模板庫到批量驗(yàn)證的升級(jí)路徑拿到源碼先別急著跑通就完事我建議按下面三個(gè)方向做改造每個(gè)都對(duì)應(yīng)一個(gè)實(shí)際應(yīng)用場(chǎng)景。第一個(gè)改造點(diǎn)模板庫擴(kuò)充與字符標(biāo)準(zhǔn)化。把字符模板文件夾從每個(gè)字符 1 張擴(kuò)到 3 張分別對(duì)應(yīng)粗筆畫、細(xì)筆畫、標(biāo)準(zhǔn)宋體三種字形。同時(shí)把匹配前的 imresize 統(tǒng)一改為雙線性插值并記錄每個(gè)測(cè)試字符與最佳模板的差異點(diǎn)數(shù)差異點(diǎn)數(shù)超過某一閾值時(shí)輸出“未識(shí)別”而不是強(qiáng)行給一個(gè)錯(cuò)誤結(jié)果。這樣系統(tǒng)就有了拒識(shí)能力識(shí)別率統(tǒng)計(jì)也會(huì)更真實(shí)。第二個(gè)改造點(diǎn)增加傾斜校正。水平垂直投影定位對(duì)傾斜車牌基本失效。可以在定位前對(duì) I5 做一輪 Radon 變換或最小外接矩形檢測(cè)估算車牌長(zhǎng)邊與水平方向的夾角再把圖像用 imrotate 旋轉(zhuǎn)回去。旋轉(zhuǎn)后注意用 imcrop 去掉黑邊重新做投影定位。傾斜校正要在預(yù)處理之后、投影定位之前插入改動(dòng)很小但能大幅提升對(duì)真實(shí)場(chǎng)景的適應(yīng)能力。第三個(gè)改造點(diǎn)批量測(cè)試與參數(shù)統(tǒng)計(jì)。寫一個(gè)循環(huán)遍歷測(cè)試圖像文件夾把每張圖的識(shí)別結(jié)果、每步中間變量、定位框坐標(biāo)、字符差異點(diǎn)數(shù)組全部存到結(jié)構(gòu)體里最后統(tǒng)一統(tǒng)計(jì)識(shí)別率和各字符的混淆矩陣。有了混淆矩陣就能精確定位是哪個(gè)字符容易錯(cuò)再針對(duì)它補(bǔ)模板。這一步是擺脫“調(diào)參靠玄學(xué)”的關(guān)鍵——你不再憑感覺改閾值而是看數(shù)據(jù)決定。驗(yàn)證方法也很直接準(zhǔn)備 10 張以上不同光照、不同角度、不同車牌類型的測(cè)試圖跑一遍批量測(cè)試記錄每次在哪一步偏離預(yù)期。如果定位失敗回看預(yù)處理圖如果分割失敗回看二值化參數(shù)如果識(shí)別失敗查模板匹配差異點(diǎn)分布。按這樣分層排查20 分鐘內(nèi)能定位到問題環(huán)節(jié)。最后聊個(gè)習(xí)慣問題。那次課程設(shè)計(jì)答辯前我用一張傾斜角度很大的測(cè)試圖把整個(gè)流程跑崩了當(dāng)時(shí)第一反應(yīng)是去改閾值來回?fù)Q了七八組參數(shù)都沒救回來。后來才意識(shí)到問題出在定位環(huán)節(jié)對(duì)傾斜完全無解不是閾值的事。從那以后我每次拿到一套圖像處理代碼都會(huì)先問一句這套算法的前提假設(shè)是什么然后專門構(gòu)造違反這個(gè)假設(shè)的用例去測(cè)它。這套車牌識(shí)別的前提假設(shè)就是“車牌基本水平、光照均勻、字符清晰”凡是不滿足這三條的照片它翻車是正常的不翻車才不正常。認(rèn)清這個(gè)邊界你對(duì)系統(tǒng)的掌控力會(huì)瞬間上一個(gè)臺(tái)階。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取