天天看點

使用Lenet5卷積神經網絡,簡單識别車牌

前言

最近課上用到卷積神經網絡搭建車牌識别,是以花費了很多精力進行這個學習。lenet是1987的神經網絡,相對簡單,沒有dropout層,是以使用這個新手搭起來比較簡單。

主要python檔案有兩個NeuralNetworkConstruction.py,Detected.py。

NeuralNetworkConstruction.py主要負責資料的預處理,神經網絡的搭建。lenet5網絡有兩個卷積層,兩個池化層,一個拉直層和3個全連接配接層。

開始訓練

訓練時,使用的是百度AI平台上的訓練資料,data檔案夾已分類好各種字元,像素為20×20的灰階圖像。NeuralNetworkConstruction.py中,讀取圖檔集并寫出訓練名單的代碼,生成了所需的訓練集名單和測試集名單。每隔10張,訓練集一張圖檔放入測試集。

需要使用時,将圖檔資料生成npy,若沒有,則使用generateds函數生成。

神經網絡的搭建,我是用class搭建法。第一層卷積層為6個5×5的卷積核,激活函數sigmoid。池化層第一層步長為2。第二層卷積層為16個5×5的卷積核,激活函數sigmoid。池化層第二層步長為2。然後進入拉直層,再經曆3個全連接配接層。如果大家的訓練圖檔大小或者類型不同,可深入了解lenet每一層的參數進行修改。由于我車牌識别共用到65個字元的識别,是以最後一層使用了softmax輸出。

訓練完模型後,将模型斷點儲存下來,下一次可以從這加載。最後可以針對模型的準确度和loss進行輸出

import tensorflow as tf
import os
import numpy as np
from matplotlib import pyplot as plt
from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation, MaxPool2D, Dropout, Flatten, Dense
from tensorflow.keras import Model
from PIL import Image
import cv2

np.set_printoptions(threshold=np.inf)


# ==========================================讀取圖檔集并寫出訓練名單
data_path = './characterData/data'
character_folders = os.listdir(data_path)
label = 0
LABEL_temp = {}
if(os.path.exists('train_data.list')):                          #有則删除,無則生成txt
    os.remove('train_data.list')
if(os.path.exists('test_data.list')):
    os.remove('test_data.list')
for character_folder in character_folders:
    with open('train_data.list', 'a') as f_train:
        with open('test_data.list', 'a') as f_test:
            if character_folder == '.DS_Store' or character_folder == '.ipynb_checkpoints' or character_folder == 'data23617':
                continue
            print(character_folder + " " + str(label))          #label是自增的,和在data中的檔案夾的先後關系對應
            LABEL_temp[str(label)] = character_folder #存儲一下标簽的對應關系
            character_imgs = os.listdir(os.path.join(data_path, character_folder))
            for i in range(len(character_imgs)):
                if i%10 == 0:
                    f_test.write(os.path.join(os.path.join(data_path, character_folder), character_imgs[i]) + "@" + str(label) + '\n')
                else:
                    f_train.write(os.path.join(os.path.join(data_path, character_folder), character_imgs[i]) + "@" + str(label) + '\n')
    label = label + 1
print('圖像清單已生成')


#=============================================加載自制資料集部分

train_path = ''                #txt中路徑齊全,是以将這個改成空串,友善運作
train_txt = './train_data.list'     #寫了訓練圖檔對應标簽路徑的txt
x_train_savepath = './mnist_image_label/mnist_x_train.npy'
y_train_savepath = './mnist_image_label/mnist_y_train.npy'

test_path = ''
test_txt = './test_data.list'
x_test_savepath = './mnist_image_label/mnist_x_test.npy'
y_test_savepath = './mnist_image_label/mnist_y_test.npy'


def generateds(path, txt):                          #将圖檔和标簽分别讀入x,y_,傳回
    f = open(txt, 'r')  # 以隻讀形式打開txt檔案
    contents = f.readlines()  # 讀取檔案中所有行
    f.close()  # 關閉txt檔案
    x, y_ = [], []  # 建立空清單
    for content in contents:  # 逐行取出
        value = content.split("@")  # 以空格分開,圖檔路徑為value[0] , 标簽為value[1] , 存入清單
        img_path = path + value[0]  # 拼出圖檔路徑和檔案名
        print(img_path)                 #調試,列印出檔案路徑
        print("value[1]:",value[1])         #調試,列印出标簽
        img = Image.open(img_path)  # 讀入圖檔
        img = np.array(img.convert('RGB'))  # 圖檔變為8位寬灰階值的np.array格式
        img = cv2.resize(img,(32, 32))
        img = img / 255.  # 資料歸一化 (實作預處理)
        x.append(img)  # 歸一化後的資料,貼到清單x
        y_.append(value[1])  # 标簽貼到清單y_
        print('loading : ' + content)  # 列印狀态提示

    x = np.array(x)  # 變為np.array格式
    y_ = np.array(y_)  # 變為np.array格式
    y_ = y_.astype(np.int64)  # 變為64位整型
    return x, y_  # 傳回輸入特征x,傳回标簽y_


if os.path.exists(x_train_savepath) and os.path.exists(y_train_savepath) and os.path.exists(            #加載資料集,若沒有,就調用generateds,生成
        x_test_savepath) and os.path.exists(y_test_savepath):
    print('-------------Load Datasets-----------------')
    x_train_save = np.load(x_train_savepath)
    y_train = np.load(y_train_savepath)
    x_test_save = np.load(x_test_savepath)
    y_test = np.load(y_test_savepath)
    x_train = np.reshape(x_train_save, (len(x_train_save), 32, 32))
    x_test = np.reshape(x_test_save, (len(x_test_save), 32, 32))
else:
    print('-------------Generate Datasets-----------------')
    x_train, y_train = generateds(train_path, train_txt)
    x_test, y_test = generateds(test_path, test_txt)

    print('-------------Save Datasets-----------------')
    x_train_save = np.reshape(x_train, (len(x_train), -1))
    x_test_save = np.reshape(x_test, (len(x_test), -1))
    np.save(x_train_savepath, x_train_save)
    np.save(y_train_savepath, y_train)
    np.save(x_test_savepath, x_test_save)
    np.save(y_test_savepath, y_test)


#===============================================神經網絡搭建部分
class LeNet5(Model):
    def __init__(self):
        super(LeNet5, self).__init__()
        self.c1 = Conv2D(filters=6, kernel_size=(5, 5),
                         activation='sigmoid')
        self.p1 = MaxPool2D(pool_size=(2, 2), strides=2)

        self.c2 = Conv2D(filters=16, kernel_size=(5, 5),
                         activation='sigmoid')
        self.p2 = MaxPool2D(pool_size=(2, 2), strides=2)

        self.flatten = Flatten()
        self.f1 = Dense(120, activation='sigmoid')
        self.f2 = Dense(84, activation='sigmoid')
        self.f3 = Dense(65, activation='softmax')

    def call(self, x):
        x = self.c1(x)
        x = self.p1(x)

        x = self.c2(x)
        x = self.p2(x)

        x = self.flatten(x)
        x = self.f1(x)
        x = self.f2(x)
        y = self.f3(x)
        return y


model = LeNet5()

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

checkpoint_save_path = "./checkpoint/LeNet5.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
    print('-------------load the model-----------------')
    model.load_weights(checkpoint_save_path)

cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
                                                 save_weights_only=True,
                                                 save_best_only=True)

print("x_train.shape", x_train.shape)
history = model.fit(x_train, y_train, batch_size=32, epochs=10, validation_data=(x_test, y_test), validation_freq=1,
                    callbacks=[cp_callback])
model.summary()

# print(model.trainable_variables)
file = open('./weights.txt', 'w')
for v in model.trainable_variables:
    file.write(str(v.name) + '\n')
    file.write(str(v.shape) + '\n')
    file.write(str(v.numpy()) + '\n')
file.close()

###############################################    show   ###############################################

# 顯示訓練集和驗證集的acc和loss曲線
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']

plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()

           

開始識别

使用模型進行識别的代碼Detected.py。讀取了所需識别的車牌照片,灰階處理後,進行二值化處理。然後對圖檔進行切割成每個字元。将每個字元儲存DivideFromPhoto裡面。

接下來複現模型,加載參數。由于我這個網絡原型是用于處理cifar資料集的識别。而cifar是像素為32×32,RGB圖像,是以我為了盡快使用,将圖檔變成和cifar一樣。使用labels數組,對識别出的資料儲存。建立match數組,使特征和字元對應起來,最後輸出

Detected.py:

import tensorflow as tf
import os
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image
from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation, MaxPool2D, Dropout, Flatten, Dense
from tensorflow.keras import Model
import cv2
import paddle as paddle

 # ================================================對車牌圖檔進行處理,分割出車牌中的每一個字元并儲存
license_plate = cv2.imread('./palate.png')                                      #加載需要識别的車牌圖檔
gray_plate = cv2.cvtColor(license_plate, cv2.COLOR_RGB2GRAY)
ret, binary_plate = cv2.threshold(gray_plate, 175, 255, cv2.THRESH_BINARY)
result = []
for col in range(binary_plate.shape[1]):
    result.append(0)
    for row in range(binary_plate.shape[0]):
        result[col] = result[col] + binary_plate[row][col] / 255
character_dict = {}
num = 0
i = 0
while i < len(result):
    if result[i] == 0:
        i += 1
    else:
        index = i + 1
        while result[index] != 0:
            index += 1
        character_dict[num] = [i, index - 1]
        num += 1
        i = index

for i in range(8):
    if i == 2:
        continue
    padding = (170 - (character_dict[i][1] - character_dict[i][0])) / 2
    ndarray = np.pad(binary_plate[:, character_dict[i][0]:character_dict[i][1]], ((0, 0), (int(padding), int(padding))),
                     'constant', constant_values=(0, 0))
    ndarray = cv2.resize(ndarray, (20, 20))
    cv2.imwrite('./' +'/DivideFromPhoto/'+ str(i) + '.png', ndarray)                #将分割的字元寫入DivideFromPhoto檔案夾中


#==============================================================================加載模型
model_save_path = 'C:/Users/US/Downloads/Pai Shibie/checkpoint/LeNet5.ckpt'
class LeNet5(Model):
    def __init__(self):
        super(LeNet5, self).__init__()
        self.c1 = Conv2D(filters=6, kernel_size=(5, 5),
                         activation='sigmoid')
        self.p1 = MaxPool2D(pool_size=(2, 2), strides=2)

        self.c2 = Conv2D(filters=16, kernel_size=(5, 5),
                         activation='sigmoid')
        self.p2 = MaxPool2D(pool_size=(2, 2), strides=2)

        self.flatten = Flatten()
        self.f1 = Dense(120, activation='sigmoid')
        self.f2 = Dense(84, activation='sigmoid')
        self.f3 = Dense(65, activation='softmax')

    def call(self, x):
        x = self.c1(x)
        x = self.p1(x)

        x = self.c2(x)
        x = self.p2(x)

        x = self.flatten(x)
        x = self.f1(x)
        x = self.f2(x)
        y = self.f3(x)
        return y

model = LeNet5()
model.load_weights(model_save_path)

#===============================================================識别
character_folders = os.listdir('./DivideFromPhoto')
# for character_folder in character_folders:
#     print("character_folder",character_folder)
preNum=len(character_folders)

labels = []
for i in range(preNum):
    image_path =character_folders[i]
    img = Image.open('./DivideFromPhoto/'+image_path)                           #讀取劃分好的車牌字元
    img = img.resize((32, 32), Image.ANTIALIAS)             #将圖檔轉為32,32,才能放入神經網絡
    img_arr = np.array(img.convert('RGB'))              #将圖檔轉為RGB,才能放入神經網絡

    img_arr = img_arr / 255.0
    print("img_arr:", img_arr.shape)
    x_predict = img_arr[tf.newaxis, ...]                #tensor增加一維,用于輸出預測結果
    print("x_predict:", x_predict.shape)
    result = model.predict(x_predict)                   #模型進行識别
    # print(result)
    pred = tf.argmax(result, axis=1)                    #橫方向進行輸出
    res=pred[0]                                 #取出結果
    result = res.numpy()                            #轉化為numpy
    labels.append(result)
    # print("result:",result)                     #測試
    print("識别結果标簽為:",res)                           #測試
    print("labels:",labels)                         #放入标簽數組
    #tf.print(pred)


#車牌字元數組
match = {0: '0', 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: 'A', 11: 'B', 12: 'C',
            13: '川', 14: 'D', 15: 'E', 16: '鄂',
            17: 'F', 18: 'G', 19: '贛', 20: '甘', 21: '貴', 22: '桂', 23: 'H', 24: '黑', 25: '滬', 26: 'J', 27: '冀', 28: '津',
            29: '京', 30: '吉', 31: 'K', 32: 'L', 33: '遼',
            34: '魯', 35: 'M', 36: '蒙', 37: '閩', 38: 'N', 39: '甯', 40: 'P', 41: 'Q', 42: '青', 43: '瓊', 44: 'R', 45: 'S',
            46: '陝', 47: '蘇', 48: '晉', 49: 'T', 50: 'U',
            51: 'V', 52: 'W ', 53: '皖', 54: 'X', 55: '湘', 56: '新', 57: 'Y', 58: '豫', 59: '渝', 60: '粵', 61: '雲', 62: 'Z',
            63: '藏', 64: '浙'
            }

print('\n車牌識别結果為:',end='')
for i in range(len(labels)):                    #對字元數組每一個元素與車牌字元數組比對
    print(match[labels[i]], end='')