Riconoscimento targhe utilizzando OpenCV, YOLO e Keras
L’obiettivo era Riconoscimento targhe utilizzando OpenCV, YOLO e Keras.
A tale scopo, abbiamo utilizzato le seguenti librerie Python :
Ci sono 3 passaggi nel nostro processo: prima dobbiamo rilevare la targa , quindi eseguire la segmentazione dei caratteri e infine leggere la targa .
Hardware: Google Cloud Compute Engine (8 vCPU, 30 GB di memoria, Tesla K80, Ubuntu 18.04)
1. Rilevamento della targa
Utilizzando Darkflow, abbiamo addestrato un modello YOLO (You Only Look Once), con 1900 immagini di auto con targa annotata. LabelImg è un ottimo strumento che ci ha permesso di annotare le nostre immagini in formato Pascal VOC. Questo set di dati era composto da immagini di auto che abbiamo trovato online, alcune che abbiamo scattato per strada e l’aumento dei dati (Vertical Flip, modifica della luminosità) utilizzando Keras (ImageDataGenerator).
Codice allenamento:
import numpy as np from darkflow.net.build import TFNet import cv2 options = {"model": "cfg/yolo-1c.cfg", "load": "bin/yolo.weights", "batch": 8, " epoch": 100, "gpu": 0.9, "train": True, "annotation": "./data/AnnotationsXML/007/", "dataset": "./data/Images/007/"} tfnet = TFNet (opzioni) tfnet.train() tfnet.savepb() Per il riconoscimento targhe utilizzando OpenCV, YOLO e KerasQuindi, carichiamo i pesi dal nostro set di dati di allenamento per fare nuove previsioni: opzioni = {"pbLoad": "yolo-plate.pb", "metaLoad": "yolo-plate.meta", "gpu": 0.9} yoloPlate = TFNet(opzioni) La funzione firstCrop utilizza la migliore previsione dal nostro modello YOLO e restituisce la targa. def firstCrop(img, forecasts): forecasts.sort(key=lambda x: x.get('confidence')) xtop = forecasts[i].get('topleft').get('x') ytop = forecasts[ i].get('topleft').get('y') xbottom = previsioni[i].get('bottomright').get('x') ybottom = previsioni[i].get('bottomright'). prendi('y') firstCrop = img[ytop:ybottom, xtop:xbottom] cv2.rectangle(img,(xtop,ytop),(xbottom,ybottom),(0,255,0),3) return firstCrop La funzione secondCrop utilizza le funzioni OpenCV per ritagliare un po' di più la lastra per evitare il rumore sullo sfondo . def secondCrop(img): gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(gray,127,255,0) contours,_ = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE) aree = [cv2.contourArea(c) for c in contorni] if(len(aree)!=0): max_index = np.argmax(aree) cnt=contours[max_index] x,y,w,h = cv2.boundingRect (cnt) limiti = cv2.boundingRect(cnt) cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2) secondCrop = img[y:y+h ,x:x+w] else: secondCrop = img return secondCrop Codice principale che utilizza le funzioni spiegate sopra: previsioni = yoloPlate.return_predict(frame) firstCropImg = firstCrop(frame, forecasts) secondCropImg = secondCrop(firstCropImg)
2. Segmentazione dei caratteri
Abbiamo utilizzato due metodi per una maggiore precisione:
Nel primo, abbiamo utilizzato un altro modello YOLO addestrato con immagini di targhe in cui i caratteri sono stati annotati. C’è solo un’etichetta “personaggio”. Circa 1400 caratteri.
Caricamento pesi:
options = {"pbLoad": "yolo-character.pb", "metaLoad": "yolo-character.meta", "gpu":0.9}
yoloCharacter = TFNet(opzioni)
Nel secondo metodo , abbiamo utilizzato le funzioni di OpenCV per elaborare la lastra.
def auto_canny(image, sigma=0.33): # calcola la mediana delle intensità dei pixel del singolo canale v = np.median(image) # applica il rilevamento automatico dei bordi di Canny usando la mediana calcolata lower = int(max(0, (1.0 - sigma ) * v)) upper = int(min(255, (1.0 + sigma) * v)) edged = cv2.Canny(image, lower, upper) # restituisce l'immagine con bordi return edgedgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) thresh_inv = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY_INV,39,1) edge = auto_canny(thresh_inv)ctrs, _ = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0]) img_area = img.shape[0 ]*img.shape[1]for i, ctr in enumerate(sorted_ctrs): x, y, w, h = cv2.boundingRect(ctr) roi_area = w*h roi_ratio = roi_area/img_areaif((roi_ratio >= 0.015) and (roi_ratio < 0.09)): if ((h>1.2*w) and (3*w>=h)): cv2.rectangle(img,(x,y),( x + w, y + h ),(90,0,255),2)
3. Riconoscimento dei caratteri
Formiamo una CNN con librerie Tensorflow e Keras . Ci sono 35 classi (10 per i numeri e 25 per l’alfabeto senza “O”) . Abbiamo usato circa 1000 immagini per ogni classe . Abbiamo raccolto un campione di immagini dei personaggi e poi abbiamo praticato l’aumento dei dati (rotazione e luminosità).
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(height, width, channel)))
model.add(layers.MaxPooling2D((2 , 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D (64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense (35, activation='softmax'))
model.summary()
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=8 )
test_loss, test_acc = model.evaluate(test_images, test_labels)
print(test_acc)
model.save("model_char_recognition.h5")
Codice previsioni:
def cnnCharRecognition(img): dizionario = {0:'0', 1:'1', 2 :'2', 3:'3', 4:'4', 5:'5', 6:'6' , 7:'7', 8:'8', 9:'9', 10:'LA', 11:' SI', 12:'DO', 13:'RE', 14:'MI', 15 :'F', 16:'G', 17:'H', 18:'I', 19:'J', 20:'K', 21:'L', 22:'M', 23: ' N', 24:'P', 25:'Q', 26:'R', 27:'S', 28:'T', 29:'U', 30:'V', 31:'W ' , 32:'X', 33:'Y', 34:'Z'} blackAndWhiteChar= cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blackAndWhiteChar = cv2.resize(blackAndWhiteChar,(75,100)) image = blackAndWhiteChar.reshape((1, 100,75, 1)) image = image / 255.0 new_predictions = characterRecognition.predict (immagine) char = np.argmax(nuove_predizioni) return dictionary[char]
4. Miglioramenti
In questo articolo sul riconoscimento targhe utilizzando OpenCV, YOLO e Keras possiamo dire che è difficile realizzare una soluzione robusta e che funzioni anche in caso di maltempo o molta luce. La debolezza del nostro processo è la segmentazione del carattere . Avremmo potuto migliorare il nostro modello YOLO responsabile di ciò, ma avrebbe bisogno di molti più dati ed è molto doloroso annotare queste immagini.
Puoi anche migliorare la segmentazione dei caratteri OpenCV aggiungendo più elaborazione delle immagini come la funzione dilata o chiudi , tuttavia dipende dalla qualità e dalle dimensioni dell’immagine della targa restituita dalla previsione. Questo può funzionare bene in alcuni casi ma non in altri, pensiamo che una soluzione di deep learning sia più robusta .
Abbiamo anche provato ad addestrare un modello OCR con YOLO ma non disponiamo di dati di qualità sufficienti per farlo funzionare correttamente. Abbiamo usato le stesse immagini del nostro secondo modello YOLO annotando ogni carattere con la lettera o il numero corrispondente.
codice completo: https://github.com/TheophileBuy/LicensePlateRecognition
apprendimento darkflow dataset labelimg machine learning numpy opencv python tensorflow YOLO