0 前言
?? 優(yōu)質(zhì)競(jìng)賽項(xiàng)目系列,今天要分享的是
?? 基于機(jī)器學(xué)習(xí)的車牌識(shí)別系統(tǒng)
??學(xué)長這里給一個(gè)題目綜合評(píng)分(每項(xiàng)滿分5分)
- 難度系數(shù):4分
- 工作量:4分
- 創(chuàng)新點(diǎn):3分
該項(xiàng)目較為新穎,適合作為競(jìng)賽課題方向,學(xué)長非常推薦!
?? 更多資料, 項(xiàng)目分享:文章來源:http://www.zghlxwxcb.cn/news/detail-724931.html
https://gitee.com/dancheng-senior/postgraduate文章來源地址http://www.zghlxwxcb.cn/news/detail-724931.html
1 課題介紹
1.1 系統(tǒng)簡介
車牌識(shí)別這個(gè)系統(tǒng),雖然傳統(tǒng),古老,卻是包含了所有這四個(gè)特偵的一個(gè)大數(shù)據(jù)技術(shù)的縮影.
在車牌識(shí)別中,你需要處理的數(shù)據(jù)是圖像中海量的像素單元;你處理的數(shù)據(jù)不再是傳統(tǒng)的結(jié)構(gòu)化數(shù)據(jù),而是圖像這種復(fù)雜的數(shù)據(jù);如果不能在很短的時(shí)間內(nèi)識(shí)別出車牌,那么系統(tǒng)就缺少意義;雖然一副圖像中有很多的信息,但可能僅僅只有那一小塊的信息(車牌)以及車身的顏色是你關(guān)心,而且這些信息都蘊(yùn)含著巨大的價(jià)值。也就是說,車牌識(shí)別系統(tǒng)事實(shí)上就是現(xiàn)在火熱的大數(shù)據(jù)技術(shù)在某個(gè)領(lǐng)域的一個(gè)聚焦,通過了解車牌識(shí)別系統(tǒng),可以很好的幫助你理解大數(shù)據(jù)技術(shù)的內(nèi)涵,也能清楚的認(rèn)識(shí)到大數(shù)據(jù)的價(jià)值。
1.2 系統(tǒng)要求
- 它基于openCV這個(gè)開源庫,這意味著所有它的代碼都可以輕易的獲取。
- 它能夠識(shí)別中文,例如車牌為蘇EUK722的圖片,它可以準(zhǔn)確地輸出std:string類型的"蘇EUK722"的結(jié)果。
- 它的識(shí)別率較高。目前情況下,字符識(shí)別已經(jīng)可以達(dá)到90%以上的精度。
1.3 系統(tǒng)架構(gòu)
整體包含兩個(gè)系統(tǒng):
- 車牌檢測(cè)
- 車牌字體識(shí)別(中文 + 數(shù)字 + 英文)
整體架構(gòu)如下:
2 實(shí)現(xiàn)方式
2.1 車牌檢測(cè)技術(shù)
車牌檢測(cè)(Plate Detection):
對(duì)一個(gè)包含車牌的圖像進(jìn)行分析,最終截取出只包含車牌的一個(gè)圖塊。這個(gè)步驟的主要目的是降低了在車牌識(shí)別過程中的計(jì)算量。如果直接對(duì)原始的圖像進(jìn)行車牌識(shí)別,會(huì)非常的慢,因此需要檢測(cè)的過程。在本系統(tǒng)中,我們使用SVM(支持向量機(jī))這個(gè)機(jī)器學(xué)習(xí)算法去判別截取的圖塊是否是真的“車牌”。
車牌檢測(cè)這里不詳細(xì)說明, 只貼出opencv圖像處理流程, 需要代碼的可以留下郵箱
使用到的圖像處理算法
- 高斯模糊
- 灰度化處理
- Sobel算子(邊緣檢測(cè))
- 開操作
- 閉操作
- 仿射變換
- 霍姆線性檢測(cè)
- 角度矯正
2.2 車牌識(shí)別技術(shù)
字符識(shí)別(Chars Recognition):
有的書上也叫Plate
Recognition,我為了與整個(gè)系統(tǒng)的名稱做區(qū)分,所以改為此名字。這個(gè)步驟的主要目的就是從上一個(gè)車牌檢測(cè)步驟中獲取到的車牌圖像,進(jìn)行光學(xué)字符識(shí)別(OCR)這個(gè)過程。其中用到的機(jī)器學(xué)習(xí)算法是著名的人工神經(jīng)網(wǎng)絡(luò)(ANN)中的多層感知機(jī)(MLP)模型。最近一段時(shí)間非常火的“深度學(xué)習(xí)”其實(shí)就是多隱層的人工神經(jīng)網(wǎng)絡(luò),與其有非常緊密的聯(lián)系。通過了解光學(xué)字符識(shí)別(OCR)這個(gè)過程,也可以知曉深度學(xué)習(xí)所基于的人工神經(jīng)網(wǎng)路技術(shù)的一些內(nèi)容。
我們這里使用深度學(xué)習(xí)的方式來對(duì)車牌字符進(jìn)行識(shí)別, 為什么不用傳統(tǒng)的機(jī)器學(xué)習(xí)進(jìn)行識(shí)別呢, 看圖就知道了:
圖2 深度學(xué)習(xí)(右)與PCA技術(shù)(左)的對(duì)比
可以看出深度學(xué)習(xí)對(duì)于數(shù)據(jù)的分類能力的優(yōu)勢(shì)。
這里博主使用生成對(duì)抗網(wǎng)絡(luò)進(jìn)行字符識(shí)別訓(xùn)練, 效果相當(dāng)不錯(cuò), 識(shí)別精度達(dá)到了98%
2.3 SVM識(shí)別字符
定義
class SVM(StatModel):
def __init__(self, C = 1, gamma = 0.5):
self.model = cv2.ml.SVM_create()
self.model.setGamma(gamma)
self.model.setC(C)
self.model.setKernel(cv2.ml.SVM_RBF)
self.model.setType(cv2.ml.SVM_C_SVC)
#訓(xùn)練svm
def train(self, samples, responses):
self.model.train(samples, cv2.ml.ROW_SAMPLE, responses)
?
調(diào)用方法,喂數(shù)據(jù)
def train_svm(self):
#識(shí)別英文字母和數(shù)字
self.model = SVM(C=1, gamma=0.5)
#識(shí)別中文
self.modelchinese = SVM(C=1, gamma=0.5)
if os.path.exists("svm.dat"):
self.model.load("svm.dat")
訓(xùn)練,保存模型
? else:
? chars_train = []
? chars_label = []
?
for root, dirs, files in os.walk("train\\chars2"):
if len(os.path.basename(root)) > 1:
continue
root_int = ord(os.path.basename(root))
for filename in files:
filepath = os.path.join(root,filename)
digit_img = cv2.imread(filepath)
digit_img = cv2.cvtColor(digit_img, cv2.COLOR_BGR2GRAY)
chars_train.append(digit_img)
#chars_label.append(1)
chars_label.append(root_int)
chars_train = list(map(deskew, chars_train))
chars_train = preprocess_hog(chars_train)
#chars_train = chars_train.reshape(-1, 20, 20).astype(np.float32)
chars_label = np.array(chars_label)
print(chars_train.shape)
self.model.train(chars_train, chars_label)
?
車牌字符數(shù)據(jù)集如下
這些是字母的訓(xùn)練數(shù)據(jù),同樣的還有我們車牌的省份簡寫:
核心代碼
?
predict_result = []
roi = None
card_color = None
for i, color in enumerate(colors):
if color in ("blue", "yello", "green"):
card_img = card_imgs[i]
gray_img = cv2.cvtColor(card_img, cv2.COLOR_BGR2GRAY)
#黃、綠車牌字符比背景暗、與藍(lán)車牌剛好相反,所以黃、綠車牌需要反向
if color == "green" or color == "yello":
gray_img = cv2.bitwise_not(gray_img)
ret, gray_img = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
#查找水平直方圖波峰
x_histogram = np.sum(gray_img, axis=1)
x_min = np.min(x_histogram)
x_average = np.sum(x_histogram)/x_histogram.shape[0]
x_threshold = (x_min + x_average)/2
wave_peaks = find_waves(x_threshold, x_histogram)
if len(wave_peaks) == 0:
print("peak less 0:")
continue
#認(rèn)為水平方向,最大的波峰為車牌區(qū)域
wave = max(wave_peaks, key=lambda x:x[1]-x[0])
gray_img = gray_img[wave[0]:wave[1]]
#查找垂直直方圖波峰
row_num, col_num= gray_img.shape[:2]
#去掉車牌上下邊緣1個(gè)像素,避免白邊影響閾值判斷
gray_img = gray_img[1:row_num-1]
y_histogram = np.sum(gray_img, axis=0)
y_min = np.min(y_histogram)
y_average = np.sum(y_histogram)/y_histogram.shape[0]
y_threshold = (y_min + y_average)/5#U和0要求閾值偏小,否則U和0會(huì)被分成兩半
wave_peaks = find_waves(y_threshold, y_histogram)
#for wave in wave_peaks:
# cv2.line(card_img, pt1=(wave[0], 5), pt2=(wave[1], 5), color=(0, 0, 255), thickness=2)
#車牌字符數(shù)應(yīng)大于6
if len(wave_peaks) <= 6:
print("peak less 1:", len(wave_peaks))
continue
wave = max(wave_peaks, key=lambda x:x[1]-x[0])
max_wave_dis = wave[1] - wave[0]
#判斷是否是左側(cè)車牌邊緣
if wave_peaks[0][1] - wave_peaks[0][0] < max_wave_dis/3 and wave_peaks[0][0] == 0:
wave_peaks.pop(0)
#組合分離漢字
cur_dis = 0
for i,wave in enumerate(wave_peaks):
if wave[1] - wave[0] + cur_dis > max_wave_dis * 0.6:
break
else:
cur_dis += wave[1] - wave[0]
if i > 0:
wave = (wave_peaks[0][0], wave_peaks[i][1])
wave_peaks = wave_peaks[i+1:]
wave_peaks.insert(0, wave)
#去除車牌上的分隔點(diǎn)
point = wave_peaks[2]
if point[1] - point[0] < max_wave_dis/3:
point_img = gray_img[:,point[0]:point[1]]
if np.mean(point_img) < 255/5:
wave_peaks.pop(2)
if len(wave_peaks) <= 6:
print("peak less 2:", len(wave_peaks))
continue
part_cards = seperate_card(gray_img, wave_peaks)
for i, part_card in enumerate(part_cards):
#可能是固定車牌的鉚釘
if np.mean(part_card) < 255/5:
print("a point")
continue
part_card_old = part_card
w = abs(part_card.shape[1] - SZ)//2
part_card = cv2.copyMakeBorder(part_card, 0, 0, w, w, cv2.BORDER_CONSTANT, value = [0,0,0])
part_card = cv2.resize(part_card, (SZ, SZ), interpolation=cv2.INTER_AREA)
#part_card = deskew(part_card)
part_card = preprocess_hog([part_card])
if i == 0:
resp = self.modelchinese.predict(part_card)
charactor = provinces[int(resp[0]) - PROVINCE_START]
else:
resp = self.model.predict(part_card)
charactor = chr(resp[0])
#判斷最后一個(gè)數(shù)是否是車牌邊緣,假設(shè)車牌邊緣被認(rèn)為是1
if charactor == "1" and i == len(part_cards)-1:
if part_card_old.shape[0]/part_card_old.shape[1] >= 7:#1太細(xì),認(rèn)為是邊緣
continue
predict_result.append(charactor)
roi = card_img
card_color = color
break
return predict_result, roi, card_color#識(shí)別到的字符、定位的車牌圖像、車牌顏色
?
?
2.4 最終效果
最后算法部分可以和你想要的任何UI配置到一起:
可以這樣 :
也可以這樣:
甚至更加復(fù)雜一點(diǎn):
最后
?? 更多資料, 項(xiàng)目分享:
https://gitee.com/dancheng-senior/postgraduate
到了這里,關(guān)于python+opencv+機(jī)器學(xué)習(xí)車牌識(shí)別 計(jì)算機(jī)競(jìng)賽的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!