پروژه تشخیص پلاک با استفاده از OpenCV ، YOLO و Keras

در این مقاله، هدف ما در این پروژه تشخیص پلاک خودرو در زمان واقعی است. برای این منظور، از کتابخانه های پایتون زیر استفاده می کنیم:

۳ مرحله در فرایند مورد نظر ما وجود دارد: ابتدا باید پلاک خودرو را تشخیص دهیم و سپس تقسیم بندی کاراکتر ها را انجام دهیم و در آخر پلاک مورد نظر را بخوانیم.

سخت افزار مورد استفاده در این پروژه : موتور محاسبات ابری Google (8 vCPU ، ۳۰ Go memory، تسلا K80، اوبونتو ۱۸٫۰۴)

تشخیص پلاک

۱- تشخیص پلاک

با استفاده از کتابخانه ی Darkflow ، ما یک مدل YOLO (مخفف عبارت You Only Look Once) را به همراه ۱۹۰۰ تصویر ماشین با پلاک های دارای متن آموزش داده ایم. LabelImg یک ابزاری عالی است که اجازه می دهد تصاویر خود را با فرمت Pascal VOC حاشیه نویسی کنید. این مجموعه داده از تصاویر خودرو که به صورت آنلاین پیدا کردیم ، برخی تصاویر که در خیابان گرفته شده و برخی با استفاده از داده افزایی (data augmentation) (فلیپ عمودی، اصلاح روشنایی) با استفاده از Keras ( توابع کلاس ImageDataGenerator ) جمع آوری شده اند.

کد آموزش :

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(options)
tfnet.train()
tfnet.savepb()

تشخیص پلاک
تصاویری از مجموعه داده ما با حاشیه نویسی با استفاده از LableImg

سپس نتایج به دست آمده از آموزش خود را بررسی می کنیم تا پیش بینی های جدید را انجام دهیم :

options = {"pbLoad": "yolo-plate.pb", "metaLoad": "yolo-plate.meta", "gpu": 0.9}
yoloPlate = TFNet(options)

تابع firstCrop از بهترین پیش بینی های بعمل آمده توسط مدل YOLO استفاده می کند و شماره پلاک خودرو را در اختیار ما قرار می دهد.

def firstCrop(img, predictions):
    predictions.sort(key=lambda x: x.get('confidence'))
    xtop = predictions[i].get('topleft').get('x')
    ytop = predictions[i].get('topleft').get('y')
    xbottom = predictions[i].get('bottomright').get('x')
    ybottom = predictions[i].get('bottomright').get('y')
    firstCrop = img[ytop:ybottom, xtop:xbottom]
    cv2.rectangle(img,(xtop,ytop),(xbottom,ybottom),(0,255,0),3)
    return firstCrop

تابع secondCrop از توابع OpenCV برای برش دادن بیشتر پلاک خودرو جهت جلوگیری از به وجود آمدن نویز در پس زمینه تصویر استفاده می کند.

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)
    areas = [cv2.contourArea(c) for c in contours]
    if(len(areas)!=0):
        max_index = np.argmax(areas)
        cnt=contours[max_index]
        x,y,w,h = cv2.boundingRect(cnt)
        bounds = 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

کد اصلی با استفاده از توابع توضیح داده شده در بالا:

predictions = yoloPlate.return_predict(frame)
firstCropImg = firstCrop(frame, predictions)
secondCropImg = secondCrop(firstCropImg)

تشخیص پلاک3 (1)
مثال خروجی تشخیص پلاک

۲- تقسیم بندی کاراکتر

برای دقت بیشتر از دو روش استفاده کردیم:

در حالت اول، ما از مدل YOLO دیگری استفاده کردیم که با تصاویری از پلاک هایی که در آن کاراکتر ها حاشیه نویسی شده اند آموزش دیده است. تنها یک برچسب “کارکتر” در آن وجود دارد؛ برای حدود ۱۴۰۰ کارکتر.

تشخیص پلاک4 (1)

تعیین وزن:

options = {"pbLoad": "yolo-character.pb", "metaLoad": "yolo-character.meta", "gpu":0.9}
yoloCharacter = TFNet(options)

در روش دوم، ما از توابع OpenCV برای پردازش پلاک خودرو استفاده کردیم.

def auto_canny(image, sigma=0.33):
    # compute the median of the single channel pixel intensities
    v = np.median(image)
 
    # apply automatic Canny edge detection using the computed median
    lower = int(max(0, (1.0 - sigma) * v))
    upper = int(min(255, (1.0 + sigma) * v))
    edged = cv2.Canny(image, lower, upper)
 
    # return the edged image
    return edged
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
thresh_inv = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY_INV,39,1)
edges = 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_area
if((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)

تشخیص پلاک5

۳- تشخیص کاراکتر

ما یک CNN را با کتابخانه های Tensorflow و Keras آموزش می دهیم. ۳۵ کلاس وجود دارد (۱۰ کتابخانه برای اعداد و ۲۵ کتابخانه برای الفبای انگلیسی بدون “O”).

برای هر کلاس تقریباً از ۱۰۰۰ تصویر استفاده کردیم. ما نمونه ای از تصاویر کاراکترها را جمع آوری کردیم و سپس داده افزایی (چرخش و روشنایی) را به کار بردیم.

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")

تشخیص پلاک6 (1)
داده های نمونه
تشخیص پلاک7 (1)

کد پیش بینی :

def cnnCharRecognition(img):
    dictionary = {0:'0', 1:'1', 2 :'2', 3:'3', 4:'4', 5:'5', 6:'6', 7:'7', 8:'8', 9:'9', 10:'A',
    ۱۱:'B', 12:'C', 13:'D', 14:'E', 15:'F', 16:'G', 17:'H', 18:'I', 19:'J', 20:'K',
    ۲۱:'L', 22:'M', 23:'N', 24:'P', 25:'Q', 26:'R', 27:'S', 28:'T', 29:'U',
    ۳۰:'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(image)
    char = np.argmax(new_predictions)
    return dictionary[char]

۴- نتیجه نهایی :

۵- محور های بهبود مدل

تهیه یک راه حل مستحکم که مدل درصورتیکه حتی اگر شرایط آب و هوایی نامساعد و یا نور زیادی وجود داشته باشد بتواند بخوبی کار خود را انجام دهد کار سختی است. ضعف موجود در فرایند ما تقسیم بندی کارکتر ها است. ما می توانستیم مدل YOLO را برای انجام این کار بهبود بخشیم اما به داده های بسیار بیشتری نیاز است و حاشیه نویسی این تصاویر بسیار سخت می باشد.

همچنین می توانید با افزودن پردازش تصویر بیشتر مانند عملکرد dilate یا تابع close ، تقسیم کاراکتر OpenCV را بهبود ببخشید، اما این کار بستگی به کیفیت و اندازه تصویر پلاک بازگشتی توسط پیش بینی ارائه شده توسط مدل ایجاد شده است بستگی دارد. این ممکن است در بعضی موارد خوب عمل کند اما در برخی دیگر نه، به نظر میرسد استفاده از یادگیری عمیق راه حلی قوی تر باشد.

کد کامل این آموزش را می توانید در این لینک مشاهده کنید.

بیشتر بخوانید:

منبع medium.com

5/5 (1 نظر)

درباره‌ی علی قلی زاده

همچنین ببینید

بزرگ ترین شرکت های تولید کننده پلاک خوان

بزرگ ترین شرکت های تولید کننده پلاک خوان جهان

سال های زیادی است که از نرم افزار های پلاک خوان برای مواردی از قبیل …

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *