基於OpenCv的SVM實現車牌檢測與識別(一)

都說深度學習的出現極力地打壓着傳統機器學習算法的地位,作爲一個二刷機器學習經典算法的小夥伴告訴你:還真多半是這樣,咳,畢竟還是差距較大,深度學習處理真實世界多維度的問題更權威!不過,有的事情還是機器學習能做的,經典永不過時,下面我們來做一個實踐。

上期,有埋下伏筆,我們來回顧一下:

在這裏插入圖片描述

在這裏插入圖片描述

我們來看看基本的識別流程:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-CqHoKjIl-1588643461656)(D:\CSDN\pic\車牌檢測(一)\1.png)]

我們使用的是OpenCv自帶的SVM模型,我們上篇也說了,由於SVM的突出表現,得到了更多官方的青睞,就誕生出了很多方便使用的封裝,正如Opencv的SVM封裝。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-iOqMrvF1-1588643461659)(D:\CSDN\pic\車牌檢測(一)\1588637654566.png)]

核心代碼介紹:

調用Opencv的SVM

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)

這邊的cv2.ml.SVM_create(生成一個SVM模型

setGamma(gamma),設置Gamma參數,demo中是0.5

setC(C), 設置懲罰項, 爲:1

setKernel(cv2.ml.SVM_RBF):設置核函數:RBF

setType(cv2.ml.SVM_C_SVC):設置SVM的模型類型:SVC是分類模型,SVR是迴歸模型

以上,看過我以前文章的朋友應該秒懂,

接下來繼續走:
訓練svm

1:定義

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)
#訓練svm
	def train(self, samples, responses):
		self.model.train(samples, cv2.ml.ROW_SAMPLE, responses)

2:

調用方法,並且喂數據:

	def train_svm(self):
		#識別英文字母和數字
		self.model = SVM(C=1, gamma=0.5)
		#識別中文
		self.modelchinese = SVM(C=1, gamma=0.5)
		if os.path.exists("svm.dat"):
			self.model.load("svm.dat")

這邊已經訓練好模型就執行IF語句中的load操作(常用的調取持久化模型的方法),否則要是沒有模型就開始訓練(喂數據):

		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)

調用的相對路徑是:"train\chars2是數據集:

這是從1-9,A到Z的數據集~

有點像MNIST手寫數字體有木有!

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-DpynOKRX-1588643461661)(D:\CSDN\pic\車牌檢測(一)\1588602809104.png)]

比如A:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VuCuNJrh-1588643461669)(D:\CSDN\pic\車牌檢測(一)\1588602724268.png)]

這些是字母的訓練數據,同樣的還有我們車牌的省份簡寫:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5sgvr6Xc-1588643461671)(D:\CSDN\pic\車牌檢測(一)\1588638135115.png)]

**來看一個:廣西的簡稱:桂

**

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5wcw3rgw-1588643461672)(D:\CSDN\pic\車牌檢測(一)\1588638324603.png)]

在此分成了SVC分別訓練省份簡稱和右邊的英文字符和數字

	def train_svm(self):
		#識別英文字母和數字
		self.model = SVM(C=1, gamma=0.5)
		#識別中文
		self.modelchinese = SVM(C=1, gamma=0.5)
		if os.path.exists("svm.dat"):
			self.model.load("svm.dat")
		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)
		if os.path.exists("svmchinese.dat"):
			self.modelchinese.load("svmchinese.dat")
		else:
			chars_train = []
			chars_label = []
			for root, dirs, files in os.walk("train\\charsChinese"):
				if not os.path.basename(root).startswith("zh_"):
					continue
				pinyin = os.path.basename(root)
				index = provinces.index(pinyin) + PROVINCE_START + 1 #1是拼音對應的漢字
				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(index)
			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.modelchinese.train(chars_train, chars_label)

同上的,先判斷我們本地是否訓練好了,免得多此一舉(需要改善的是,訓練和測試應該分離),

解讀一下:

os.walk方法,主要用來遍歷一個目錄內各個子目錄和子文件。

os.walk(top, topdown=True, onerror=None, followlinks=False) 

可以得到一個三元tupple(dirpath, dirnames, filenames), 我們這裏換名了: root, dirs, files

第一個爲起始路徑,第二個爲起始路徑下的文件夾,第三個是起始路徑下的文件。

dirpath 是一個string,代表目錄的路徑,

dirnames 是一個list,包含了dirpath下所有子目錄的名字。

filenames 是一個list,包含了非目錄文件的名字。

這些名字不包含路徑信息,如果需要得到全路徑,需要使用os.path.join(dirpath, name).

os.path.basename(),返回path最後的文件名。若path以/或\結尾,那麼就會返回空值。

標好了訓練集和標籤,就可以“喂”給分類器了:

self.modelchinese.train(chars_train, chars_label)

特徵提取:獲取車牌的可能位置(以下爲根據車牌顏色再定位,縮小邊緣非車牌邊界)
	def accurate_place(self, card_img_hsv, limit1, limit2, color):
		row_num, col_num = card_img_hsv.shape[:2]
		xl = col_num
		xr = 0
		yh = 0
		yl = row_num
		#col_num_limit = self.cfg["col_num_limit"]
		row_num_limit = self.cfg["row_num_limit"]
		col_num_limit = col_num * 0.8 if color != "green" else col_num * 0.5#綠色有漸變
		for i in range(row_num):
			count = 0
			for j in range(col_num):
				H = card_img_hsv.item(i, j, 0)
				S = card_img_hsv.item(i, j, 1)
				V = card_img_hsv.item(i, j, 2)
				if limit1 < H <= limit2 and 34 < S and 46 < V:
					count += 1
			if count > col_num_limit:
				if yl > i:
					yl = i
				if yh < i:
					yh = i
		for j in range(col_num):
			count = 0
			for i in range(row_num):
				H = card_img_hsv.item(i, j, 0)
				S = card_img_hsv.item(i, j, 1)
				V = card_img_hsv.item(i, j, 2)
				if limit1 < H <= limit2 and 34 < S and 46 < V:
					count += 1
			if count > row_num - row_num_limit:
				if xl > j:
					xl = j
				if xr < j:
					xr = j
		print('size111', xl, xr, yh, yl)

		return xl, xr, yh, yl

接下來:測試

	def predict(self, car_pic):
		if type(car_pic) == type(""):
			img = imreadex(car_pic)
		else:
			img = car_pic
		pic_hight, pic_width = img.shape[:2]

		if pic_width > MAX_WIDTH:
			resize_rate = MAX_WIDTH / pic_width
			img = cv2.resize(img, (MAX_WIDTH, int(pic_hight*resize_rate)), interpolation=cv2.INTER_AREA)
			print('tuxing', img.shape[0],img.shape[1])

傳入車子圖片 ——>判定圖片完整性——>處理或重讀圖片——>使用img.shape方式獲取圖片的高和寬——>超出自定義最大高寬,就resize操作,接下來 邊緣計算:

blur = self.cfg["blur"]
		#高斯去噪
		if blur > 0:
			img = cv2.GaussianBlur(img, (blur, blur), 0)#圖片分辨率調整
		oldimg = img
		img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
		#equ = cv2.equalizeHist(img)
		#img = np.hstack((img, equ))
		#去掉圖像中不會是車牌的區域
		kernel = np.ones((20, 20), np.uint8)
		img_opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
		img_opening = cv2.addWeighted(img, 1, img_opening, -1, 0);

		#找到圖像邊緣
		ret, img_thresh = cv2.threshold(img_opening, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
		img_edge = cv2.Canny(img_thresh, 100, 200)
		#使用開運算和閉運算讓圖像邊緣成爲一個整體
		kernel = np.ones((self.cfg["morphologyr"], self.cfg["morphologyc"]), np.uint8)
		img_edge1 = cv2.morphologyEx(img_edge, cv2.MORPH_CLOSE, kernel)
		img_edge2 = cv2.morphologyEx(img_edge1, cv2.MORPH_OPEN, kernel)

高斯去噪——>圖片分辨率調整——>圖像裁剪(去掉圖像中不會是車牌的區域)——>找到圖像邊緣——>使用開運算和閉運算讓圖像邊緣成爲一個整體

先上圖:
這個是我使用了中文包的顯示,Opencv不支持的是中文,不論是中文路徑還是中文顯示,都不行,天無絕人之路,我使用了PIL的方法來解決:看圖:

在這裏插入圖片描述

上面這兩步是視覺的底層常用的基本操作,經常用於數據(圖像)預處理,大家可以做筆記,在實踐的情況下,會事半功倍喲!

篇幅有限,暫時介紹這麼多,還有很多代碼,下期介紹吧~,另外值得說明的是,代碼不是我寫的,我只幫朋友調試修改了一下,時間繁忙,剩餘精華,靜待下期分解!

至此,可知,機器學習傳統算法比較迅速!

最後歡迎大家與我聯繫,多多交流,歡迎大家進羣交流,機器學習,深度學習交流羣,附上我的微信~~
在這裏插入圖片描述
上海第二工業大學 智能科學與技術大二 周小夏(CV調包俠)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章