目錄
一、形態學車牌提取(簡單:單情景)
1、讀取圖片,轉灰度圖
2、提取輪廓(Sobel算子提取y方向邊緣)
3、自適應二值化
4、閉運算處理,把影像閉合、揉團,使影像區域化
5、腐蝕/膨脹去噪得到車牌區域
5-1、橫向腐蝕、膨脹
5-2、縱向腐蝕、膨脹
6、獲取外輪廓
6-1、得到輪廓
6-2、畫出輪廓并顯示
7、截取得到車牌
二、形態學車牌提取(優化:多情景)
1、轉灰度圖
2、頂帽運算
3、Sobel算子提取Y方向邊緣
4、二值化影像
5、開運算分割(縱向分割)
6、閉運算合并
7、橫/縱方向腐蝕/膨脹
7-1、縱方向: 腐蝕+膨脹
7-2、橫方向:膨脹+腐蝕
8、腐蝕膨脹:去噪
9、獲取外輪廓
10、 根據車牌特征找到車牌輪廓
代碼
參考資料
一、形態學車牌提取(簡單:單情景)
(單圖片還不錯,但多圖片不準確)
1、讀取圖片,轉灰度圖
# 1、讀取圖片,轉灰度圖
img = cv.imread('Resource/car.jpg')
gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
cv.imshow('gray', gray)

2、提取輪廓(Sobel算子提取y方向邊緣)
為什么對Y方向取邊緣:讓影像變“瘦”,便于把車牌揉成一團,
# 2、提取輪廓(Sobel算子提取y方向)
y = cv.Sobel(gray, cv.CV_16S, 1, 0)
# 注:對x/y微分和得到x/y方向影像相反 要得到x/y方向邊緣,就要求y/x方向的微分,
absY = cv.convertScaleAbs(y)
cv.imshow('Y', absY)

3、自適應二值化
注:threshold函式回傳兩個引數,第二個引數才是二進制影像!!!
# 3、自適應二值化
ret, binary = cv.threshold(absY, 0, 255, cv.THRESH_OTSU)
cv.imshow('binary', binary)

4、閉運算處理,把影像閉合、揉團,使影像區域化
閉運算處理,影像區域化,便于找到車牌區域,進而得到輪廓
測驗多組圖片,發現(17, 5)的卷積核比較好,能達到目的,

# 4、閉運算處理,把影像閉合、揉團,使影像區域化,便于找到車牌區域,進而得到輪廓
kernel = cv.getStructuringElement(cv.MORPH_RECT, (17,5))
print('kernel= \n', kernel)
close = cv.morphologyEx(binary, cv.MORPH_CLOSE, kernel)
cv.imshow('close', close)
(這里采用矩形卷積核處理,)
5、腐蝕/膨脹去噪得到車牌區域
上面雖然得到了二進制影像,且得到了大致區域,但是噪聲(雜質)仍然太多,下面通過腐蝕、膨脹去噪,
5-1、橫向腐蝕、膨脹
# 5-1、水平方向腐蝕/膨脹
erode = cv.morphologyEx(close, cv.MORPH_ERODE, kernel_x)
cv.imshow('erode_x', erode)
dilate = cv.morphologyEx(erode, cv.MORPH_DILATE, kernel_x)
cv.imshow('dilate_x', dilate)


橫向處理完之后,繼續對縱向進行處理,
5-2、縱向腐蝕、膨脹
# 5-2、豎直方向腐蝕/膨脹
erode = cv.morphologyEx(dilate, cv.MORPH_ERODE, kernel_y)
cv.imshow('erode_y', erode)
dilate = cv.morphologyEx(erode, cv.MORPH_DILATE, kernel_y)
cv.imshow('dilate_y', dilate)


如上圖,得到了想要的結果,基本只剩下幾個輪廓了,且車牌區域保留完好,不影響車牌的定位,
6、獲取外輪廓
這里想先把包括車牌輪廓的外輪廓一起顯示,
6-1、得到輪廓
# 6-1、得到輪廓
contours, hierarchy = cv.findContours(dilate_y, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
6-2、畫出輪廓并顯示
# 6-2、畫出輪廓
cv.drawContours(img_copy, contours, -1, (255,0,255), 2)
cv.imshow('Contours', img_copy)
獲取輪廓代碼及效果:
# 6、獲取外輪廓
img_copy = img.copy()
# 6-1、得到輪廓
contours, hierarchy = cv.findContours(dilate_y, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 6-2、畫出輪廓并顯示
cv.drawContours(img_copy, contours, -1, (255,0,255), 2)
cv.imshow('Contours', img_copy)

7、截取得到車牌
首先判斷車牌的特征(比如寬:高一般在3~4),然后根據這個特征進行判斷,只保留符合特征的圖片(車牌),
# 7、遍歷所有輪廓,找到車牌輪廓
for contour in contours:
# 7-1、得到矩形區域:左頂點坐標、寬和高
rect = cv.boundingRect(contour)
# 7-2、判斷寬高比例是否符合車牌標準,截取符合圖片
if rect[2]>rect[3]*3 and rect[2]<rect[3]*5:
# 截取車牌并顯示
img = img[rect[1]:(rect[1]+rect[3]), rect[0]:(rect[0]+rect[2])]
cv.imshow('license plate', img)


二、形態學車牌提取(優化:多情景)
1、轉灰度圖
gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
cv.imshow('gray', gray)
2、頂帽運算
把較亮部分提取出來,使得車牌區域更加明顯,
# 2、頂帽運算
# gray = cv.equalizeHist(gray)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (17,17))
tophat = cv.morphologyEx(gray, cv.MORPH_TOPHAT, kernel)
cv.imshow('tophat', tophat)

3、Sobel算子提取Y方向邊緣
縱向化車牌區域,使車牌更加明顯,
# 3、Sobel算子提取y方向邊緣(揉成一坨)
y = cv.Sobel(tophat, cv.CV_16S, 1, 0)
absY = cv.convertScaleAbs(y)
cv.imshow('absY', absY)

4、二值化影像
根據自己的實際情況選取閾值,閾值越大,變成白色(255)的門檻越多,則白色部分越少, (這里閾值比較小,但閾值大也有不少利弊)
# 4、自適應二值化(閾值自己可調)
ret, binary = cv.threshold(absY, 75, 255, cv.THRESH_BINARY)
cv.imshow('binary', binary)
5、開運算分割(縱向分割)
為了防止后面的閉運算錯誤連接外部的雜質,需要先用開運算把它們分隔開,
# 5、開運算處理(縱向去噪,分隔)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (1, 15))
Open = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel)
cv.imshow('Open', Open)
分割前:
可以看出來,這寫雜質已經完全融入車牌中了,后續想要分離它們就十分困難了,

分割后:
雖然也有少部分的連接,但不是之前的那種大塊連接,相對 比較好去除,

6、閉運算合并
# 6、閉運算合并,把影像閉合、揉團,使影像區域化,便于找到車牌區域,進而得到輪廓
kernel = cv.getStructuringElement(cv.MORPH_RECT, (41, 15))
close = cv.morphologyEx(Open, cv.MORPH_CLOSE, kernel)
cv.imshow('close', close)
效果上面有,
7、橫/縱方向腐蝕/膨脹
# 7、膨脹/腐蝕(去噪得到車牌區域)
# 中遠距離車牌識別
kernel_x = cv.getStructuringElement(cv.MORPH_RECT, (25, 7))
kernel_y = cv.getStructuringElement(cv.MORPH_RECT, (1, 11))
# 近距離車牌識別
# kernel_x = cv.getStructuringElement(cv.MORPH_RECT, (91, 31))
# kernel_y = cv.getStructuringElement(cv.MORPH_RECT, (1, 31))
# 7-1、腐蝕、膨脹(去噪)
erode_y = cv.morphologyEx(close, cv.MORPH_ERODE, kernel_y)
cv.imshow('erode_y', erode_y)
dilate_y = cv.morphologyEx(erode_y, cv.MORPH_DILATE, kernel_y)
cv.imshow('dilate_y', dilate_y)
# 7-1、膨脹、腐蝕(連接)(二次縫合)
dilate_x = cv.morphologyEx(dilate_y, cv.MORPH_DILATE, kernel_x)
cv.imshow('dilate_x', dilate_x)
erode_x = cv.morphologyEx(dilate_x, cv.MORPH_ERODE, kernel_x)
cv.imshow('erode_x', erode_x)
7-1、縱方向: 腐蝕+膨脹
分割上下距離比較近的物體,

7-2、橫方向:膨脹+腐蝕
進一步連接橫向的物體 (繼上面的閉運算,再次連接車牌的內容)

8、腐蝕膨脹:去噪
# 8、腐蝕、膨脹:去噪
kernel_e = cv.getStructuringElement(cv.MORPH_RECT, (25, 11))
erode = cv.morphologyEx(erode_x, cv.MORPH_ERODE, kernel_e)
cv.imshow('erode', erode)
kernel_d = cv.getStructuringElement(cv.MORPH_RECT, (25, 13))
dilate = cv.morphologyEx(erode, cv.MORPH_DILATE, kernel_d)
cv.imshow('dilate', dilate)

9、獲取外輪廓
# 9、獲取外輪廓
img_copy = img.copy()
# 9-1、得到輪廓
contours, hierarchy = cv.findContours(dilate, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 9-2、畫出輪廓并顯示
cv.drawContours(img_copy, contours, -1, (255, 0, 255), 2)
cv.imshow('Contours', img_copy)

10、 根據車牌特征找到車牌輪廓
車牌寬高比一般為3~4之間,但我這里首先是圖比較多,有些圖附帶的雜質沒有清除干凈,所以就設定在3~6.5之間,
# 10、遍歷所有輪廓,找到車牌輪廓
i = 0
for contour in contours:
# 10-1、得到矩形區域:左頂點坐標、寬和高
rect = cv.boundingRect(contour)
# 10-2、判斷寬高比例是否符合車牌標準,截取符合圖片
if rect[2]>rect[3]*3 and rect[2]<rect[3]*6.5:
# 截取車牌并顯示
print(rect)
img = img[(rect[1]-5):(rect[1]+rect[3]+5), (rect[0]-5):(rect[0]+rect[2]+5)] #高,寬
try:
cv.imshow('license plate%d-%d' % (count, i), img)
# cv.imwrite('img%d-%d.jpg'%(count, i), img)
i += 1
except:
pass
cv.waitKey(0)
中遠距離:
正常車牌成功率:100%(測驗了11例,均成功)
傾斜車牌成功率:67%(測驗2例,,3車牌,最小的車牌識別失敗)(但是處理不是很好)
近距離車牌成功率:0%(測驗2例,均失敗)

近距離:
(近距離成功率50%,因為只做了2個)

代碼
# 車牌識別
import cv2 as cv
import numpy as np
import os
# 提取車牌(形態學)
def Morph_Distinguish(img):
# 1、轉灰度圖
gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
cv.imshow('gray', gray)
# 2、頂帽運算
# gray = cv.equalizeHist(gray)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (17,17))
tophat = cv.morphologyEx(gray, cv.MORPH_TOPHAT, kernel)
cv.imshow('tophat', tophat)
# 3、Sobel算子提取y方向邊緣(揉成一坨)
y = cv.Sobel(tophat, cv.CV_16S, 1, 0)
absY = cv.convertScaleAbs(y)
cv.imshow('absY', absY)
# 4、自適應二值化(閾值自己可調)
ret, binary = cv.threshold(absY, 75, 255, cv.THRESH_BINARY)
cv.imshow('binary', binary)
# 5、開運算分割(縱向去噪,分隔)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (1, 15))
Open = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel)
cv.imshow('Open', Open)
# 6、閉運算合并,把影像閉合、揉團,使影像區域化,便于找到車牌區域,進而得到輪廓
kernel = cv.getStructuringElement(cv.MORPH_RECT, (41, 15))
close = cv.morphologyEx(Open, cv.MORPH_CLOSE, kernel)
cv.imshow('close', close)
# 7、膨脹/腐蝕(去噪得到車牌區域)
# 中遠距離車牌識別
kernel_x = cv.getStructuringElement(cv.MORPH_RECT, (25, 7))
kernel_y = cv.getStructuringElement(cv.MORPH_RECT, (1, 11))
# 近距離車牌識別
# kernel_x = cv.getStructuringElement(cv.MORPH_RECT, (79, 15))
# kernel_y = cv.getStructuringElement(cv.MORPH_RECT, (1, 31))
# 7-1、腐蝕、膨脹(去噪)
erode_y = cv.morphologyEx(close, cv.MORPH_ERODE, kernel_y)
cv.imshow('erode_y', erode_y)
dilate_y = cv.morphologyEx(erode_y, cv.MORPH_DILATE, kernel_y)
cv.imshow('dilate_y', dilate_y)
# 7-1、膨脹、腐蝕(連接)(二次縫合)
dilate_x = cv.morphologyEx(dilate_y, cv.MORPH_DILATE, kernel_x)
cv.imshow('dilate_x', dilate_x)
erode_x = cv.morphologyEx(dilate_x, cv.MORPH_ERODE, kernel_x)
cv.imshow('erode_x', erode_x)
# 8、腐蝕、膨脹:去噪
kernel_e = cv.getStructuringElement(cv.MORPH_RECT, (25, 9))
erode = cv.morphologyEx(erode_x, cv.MORPH_ERODE, kernel_e)
cv.imshow('erode', erode)
kernel_d = cv.getStructuringElement(cv.MORPH_RECT, (25, 11))
dilate = cv.morphologyEx(erode, cv.MORPH_DILATE, kernel_d)
cv.imshow('dilate', dilate)
# 9、獲取外輪廓
img_copy = img.copy()
# 9-1、得到輪廓
contours, hierarchy = cv.findContours(dilate, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 9-2、畫出輪廓并顯示
cv.drawContours(img_copy, contours, -1, (255, 0, 255), 2)
cv.imshow('Contours', img_copy)
# 10、遍歷所有輪廓,找到車牌輪廓
i = 0
for contour in contours:
# 10-1、得到矩形區域:左頂點坐標、寬和高
rect = cv.boundingRect(contour)
# 10-2、判斷寬高比例是否符合車牌標準,截取符合圖片
if rect[2]>rect[3]*3 and rect[2]<rect[3]*7:
# 截取車牌并顯示
print(rect)
img = img[(rect[1]-5):(rect[1]+rect[3]+5), (rect[0]-5):(rect[0]+rect[2]+5)] #高,寬
try:
cv.imshow('license plate%d-%d' % (count, i), img)
cv.imwrite('car_licenses/img%d-%d.jpg'%(count, i), img)
i += 1
except:
pass
cv.waitKey(0)
if __name__ == '__main__':
global count
count=0
# 遍歷檔案夾中的每張圖片(車)
for car in os.listdir('cars'):
# 1、獲取路徑
path = 'cars/'+'car'+str(count)+'.jpg'
# 2、獲取圖片
img = cv.imread(path)
# 3、定位車牌
Morph_Distinguish(img) #形態學提取車牌
count += 1
cv.waitKey(0)
代買僅供參考借鑒,目前只能提取一定距離的汽車車牌,太遠或太近都太不行,也沒有做傾斜處理,很難應用到實際生活中,實際生活中情景復雜的多,有什么建議或者批評可以盡管說出來,(后期可能會在此加入傾斜處理)
參考資料
https://www.bilibili.com/video/BV1yg4y187kU?from=search&seid=14407715114627193868
https://blog.csdn.net/PYH1009/article/details/102951768?spm=1001.2014.3001.5501
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/297230.html
標籤:其他
