車牌識別--Opencv傳統圖像處理+Pytorch搭建卷積神經網絡

一.原理與步驟

可以用傳統的機器學習方式,確定圖片中車牌的位置,之後對車牌進行相應的處理,圖像分割,尺寸調整,平滑圖像等,再事先利用神經網絡,搭建網絡模型、訓練模型、保存模型參數,最後把前面分割好的圖像轉換成能適應這個網絡模型的圖像格式,傳入網絡模型,最後得到預測結果。本文用到的車牌圖片如有侵犯,請聯繫,立即刪~
流程圖如下:
在這裏插入圖片描述

二.模塊化編程調試

1. 字符識別

介紹:在本節中我用神經網絡對字符進行識別,我應用pytorch這個深度學習框架,分別進行了卷積神經網絡模型的搭建、GPU下模型訓練、模型參數保存和下載、單照片模型測試,最終實現字符識別。
具體:
a. 數據集:我用的數據集比較小,它的好處是訓練比較快,基本能滿足本項目的需求,缺點是,有些比較模糊的圖象是回識別不出來,數據集具體如下,規格是20203
在這裏插入圖片描述
在這裏插入圖片描述
b. CNN網絡模型搭建:根據項目需求,卷積神經網絡模型可以勝任這個任務,設計搭建了這個網絡模型,代碼如下:其中包含兩個卷積層、池化層、rule層、以及四個全連接層,最後輸出65個得分結果,本項目中分類器的種類有65個。

'''定義網絡模型的類'''
class Net(nn.Module):  #要繼承這個類
    def __init__(self):
        super(Net ,self).__init__() #父類初始化
        '''定義網絡結構'''
        self.conv1 = nn.Conv2d(3 , 8 , 3 ) #輸入顏色通道 :1  輸出通道:6,卷積核:5*5  卷積核默認步長是1  30*30
        self.conv2 = nn.Conv2d(8 , 16 , 2 )#這是第二個卷積層,輸入通道是:6 ,輸出通道:16 ,卷積核:3*3   30*30
        self.pool = nn.MaxPool2d(2, 2)  # 最大池化層   10*10
        #self.conv3 = nn.Conv2d(16 , 8 , 2)#第三個卷積層,輸入層的輸入通道要和上一層傳下來的通道一樣,這裏給了填充是2,這個參數默認是:0,填充可以把邊緣信息、特徵提取出來,不流失   14*14
        self.fc1 = nn.Linear(16*4*4 , 110)
        self.fc2 = nn.Linear(110 , 150)
        self.fc3 = nn.Linear(150 , 130)
        self.fc4 = nn.Linear(130 , 65)        #三個全連接層 65 是最終輸出有65個類別

    def forward(self , x):  #前向傳播,把整個網絡模型,順序連接起來,init裏面只是對層初始化(需要用到的層)
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        #x = F.relu(self.conv3(x))
        x = x.view( -1 ,16*4*4)  #轉錄成可以傳入全連接層的的形狀
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = F.relu(self.fc3(x))
        x = self.fc4(x)
        return x

可視圖如下:
在這裏插入圖片描述
在這裏插入圖片描述
c. 訓練模型,保存參數,代碼如下:具體包含前向傳播、反向傳播、計算誤差、優化器計算、更新參數、保存模型參數(權重值,卷積核值等)

def train_net(self):
    self.net = Net()  # 把模型的定義一下
    criterion = nn.CrossEntropyLoss()  # 定義一個計算損失值 賦給一個對象
    optimizer = optim.SGD(self.net.parameters(), lr=learning_rate, momentum=0.9)
    for epoch in range(epochs):
        running_loss = 0.0
        for i, data in enumerate(Setloader.trainset_loader(self), 0):
            inputs, labels = data

            optimizer.zero_grad()

            outputs = self.net(inputs)

            loss = criterion(outputs, labels)

            loss.backward()

            optimizer.step()

            running_loss += loss.item()
            if i % 200 == 199:
                print('[%d ,%5d]loss:%.3f' % (epoch + 1, i + 1, running_loss / 200))
                running_loss = 0.0
    print('finished training!')
    torch.save(self.net.state_dict(), 'zuoye_net_homewords.pkl')  # 訓

調整學習率和網絡模型epoch數,使最終訓練效果達到最優。如下圖,可以看到訓練到20個epoch時,mini-batch的loss已經逐漸下降到0.019。從一開始loss是4+下降到0.019,說明網絡模型是有在學習也學習得不錯。訓練時如果要用GPU訓練,這需要安裝cuda,以及在代碼中將網絡模型、照片、標籤傳到對應的GPU上。
在這裏插入圖片描述
d. 測試:
輸入網絡的照片
預測結果

2. 車牌定位與分割

介紹:在一張照片中必須先找到車牌的位置之後再對車牌區域ROI進行圖像分割、形態學變換。這部分對整個項目來說很重要,應該這個環節是前提。有傳統機器視覺的方法、以及深度學習兩種方法對車牌定位,深度學習的方法需要大量的樣本、應用目標識別等算法YOLO系列、SSD系列均可以實現並達到很好的效果。但考慮沒有很好的GPU以及不想做打標等費時不討好的工作,故選擇應用傳統的機器視覺的方案進行車牌定位。在這個部分,應用了傳統機器視覺識別的辦法,做到了可以識別10張以上不同場景的車牌,效果已經很好了。
具體:引用opencv,對圖像進行如下處理,形象相關調參,實現本小節的功能
a. hsv顏色變換
在這裏插入圖片描述在這裏插入圖片描述
b. 圖像平滑、形態學變換
在這裏插入圖片描述
在這裏插入圖片描述
c. 找圖像中的外接矩形
在這裏插入圖片描述
在這裏插入圖片描述
d. 根據矩形長寬比、矩形面積篩選車牌,實現車牌的定位(如下文效果所示)
e. 對車牌ROI進行透視變換,字符分割
在這裏插入圖片描述
在這裏插入圖片描述
識別效果效果:可識別出在不同場景,不同光強下的車輛車牌
在這裏插入圖片描述在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
這部分是要識別好車牌的前提,該部分要先定位好圖像中的車牌,把車牌識別好,去除車牌中存在的干擾,才能更有利於字符的分割。本部分應該針對特定場景下的,設計的算法會比較穩定識別率也會比較高。關於圖像分割部分,要儘量不字符提取出來,而且不要包含太多噪聲。深度學習網絡應該提高其泛化能力,最終才能實現更高的識別效果,甚至是產品級別。

img = cv2.imread('G:\deep_learming\pytorch\data\car_pai\jk3.jpg',1)
imgg= img.copy()

img = cv2.cvtColor(img , cv2.COLOR_BGR2HSV)
lower_blue = np.array([100, 100, 100])
upper_blue = np.array([120, 220, 255])
img2 = cv2.inRange(img, lower_blue, upper_blue)  # img1通道是HSV不是BGR了  在指定圖像中選定範圍像素點
img2 = cv2.bilateralFilter(img2 ,35 ,75 ,71)
cv2.namedWindow('show')
cv2.imshow('show', img2)
cv2.waitKey(-1)
cv2.destroyAllWindows()
kernel = np.ones((13, 13), np.uint8)
closing_img = cv2.morphologyEx(img2, cv2.MORPH_CLOSE, kernel)
closing_img = cv2.bilateralFilter(closing_img,25,75,75)
contours , hierarchy = cv2.findContours(closing_img,1,2)

for i in range(len(contours)):
    cnt = contours[i]
    rect = cv2.minAreaRect(cnt)
    box = cv2.boxPoints(rect)
    box = np.int0(box)  #左下角 左上角 右上角 右下角
    mianji = rect[1][0] * rect[1][1]  # 車牌面積不應太小 ,太小就丟棄
    # print(mianji)
    if rect[2] >= -45:
        if rect[1][1] != 0:
            bili = (rect[1][0]) / (rect[1][1])
        else:
            print('error')
            bili = 1   #這種情況是不可能的車牌不可能一邊是0,這裏做特殊處理
    else:
        if rect[1][0] != 0:
            bili = rect[1][1] / rect[1][0]
    # imgg = cv2.drawContours(imgg, [box], 0, (0, 0, 255), 2)
    if (bili > 2.15 and bili < 4) and mianji>700:
        # print(bili)
        imgg = cv2.drawContours(imgg,[box],0,(0,0,255),2)
        left_point_x = np.min(box[:, 0])
        right_point_x = np.max(box[:, 0])
        top_point_y = np.min(box[:, 1])
        bottom_point_y = np.max(box[:, 1])

三.效果

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
固定的場景下識別率還是很好的,至於第三張圖片中的渝沒有識別錯誤,這和本項目所用的數據集太小有關,並沒有包含足夠多的樣本,訓練時就無法找到足夠的特徵,針對這種情況在不改變網絡模型的條件下,有兩種方法:1.增大數據集,2.數據增強

四.源碼

想要完整源碼,請關注我,之後會整理好文件,把本次用到的數據集和圖片,源碼等打包放到我的github上。

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