都說深度學習的出現極力地打壓着傳統機器學習算法的地位,作爲一個二刷機器學習經典算法的小夥伴告訴你:還真多半是這樣,咳,畢竟還是差距較大,深度學習處理真實世界多維度的問題更權威!不過,有的事情還是機器學習能做的,經典永不過時,下面我們來做一個實踐。
上期,有埋下伏筆,我們來回顧一下:
我們來看看基本的識別流程:
我們使用的是OpenCv自帶的SVM模型,我們上篇也說了,由於SVM的突出表現,得到了更多官方的青睞,就誕生出了很多方便使用的封裝,正如Opencv的SVM封裝。
核心代碼介紹:
調用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手寫數字體有木有!
比如A:
這些是字母的訓練數據,同樣的還有我們車牌的省份簡寫:
**來看一個:廣西的簡稱:桂
**
在此分成了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調包俠)