[影象處理] Python+OpenCV實現車牌區域識別及Sobel運算元

Eastmount發表於2018-08-06

由於最近太忙,這篇文章只給出相關程式碼供大家學習,過一段時間會詳細的寫一些列Python影象處理的文章,包括各種演算法原理、影象識別、影象增強、影象分類、深度學習等。本篇文章主要呼叫OpenCV庫(cv2)進行車牌區域識別,具體步驟包括:
    1.灰度轉換:將彩色圖片轉換為灰度影象,常見的R=G=B=畫素平均值。
    2.高斯平滑和中值濾波:去除噪聲。
    3.Sobel運算元:提取影象邊緣輪廓,X方向和Y方向平方和開跟。
    4.二值化處理:影象轉換為黑白兩色,通常畫素大於127設定為255,小於設定為0。
    5.膨脹和細化:放大影象輪廓,轉換為一個個區域,這些區域內包含車牌。
    6.通過演算法選擇合適的車牌位置,通常將較小的區域過濾掉或尋找藍色底的區域。
    7.標註車牌位置,如果是花兒、人臉、牛角,可能需要特徵提取和訓練。

 



本篇文章為基礎性文章,希望對你有所幫助,主要提供些思路,也是自己教學的內容。如果文章中存在錯誤或不足之處,還請海涵。同時,推薦大家閱讀我以前的文章瞭解其他知識。
推薦我C++影象系列基礎知識:
https://blog.csdn.net/column/details/eastmount-mfc.html

 

PSS:2019年1~2月作者參加了CSDN2018年部落格評選,希望您能投出寶貴的一票。我是59號,Eastmount,楊秀璋。投票地址:https://bss.csdn.net/m/topic/blog_star2018/index


五年來寫了314篇部落格,12個專欄,是真的熱愛分享,熱愛CSDN這個平臺,也想幫助更多的人,專欄包括Python、資料探勘、網路爬蟲、影象處理、C#、Android等。現在也當了兩年老師,更是覺得有義務教好每一個學生,讓貴州學子好好寫點程式碼,學點技術,"師者,傳到授業解惑也",提前祝大家新年快樂。2019我們攜手共進,為愛而生。
 

 

 

一、讀取影象及灰度轉換

程式碼如下:

#encoding:utf-8
import cv2  
import numpy as np  
import matplotlib.pyplot as plt

#讀取圖片
imagePath = '10.jpg'
img = cv2.imread(imagePath)

#opencv預設的imread是以BGR的方式進行儲存的
#而matplotlib的imshow預設則是以RGB格式展示
#所以此處我們必須對圖片的通道進行轉換
lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

#灰度影象處理
GrayImage = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
print(u"讀入lenna圖的shape為", GrayImage.shape)

#顯示圖形
titles = ['Source Image','Gray Image']  
images = [lenna_img, GrayImage]  
for i in xrange(2):  
   plt.subplot(1,2,i+1),plt.imshow(images[i],'gray')  
   plt.title(titles[i])  
   plt.xticks([]),plt.yticks([])  
plt.show()  

cv2.imshow('Gray.bmp', GrayImage)
cv2.waitKey(0)

輸出結果如下圖所示:
 

 

二、高斯平滑和中值濾波去噪

這裡原理推薦我以前C++影象處理的文章,如下:https://blog.csdn.net/column/details/eastmount-mfc.html


完整程式碼如下所示:

#encoding:utf-8
import cv2  
import numpy as np  
import matplotlib.pyplot as plt

#讀取圖片
imagePath = '10.jpg'
img = cv2.imread(imagePath)

#opencv預設的imread是以BGR的方式進行儲存的
#而matplotlib的imshow預設則是以RGB格式展示
#所以此處我們必須對圖片的通道進行轉換
lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

#灰度影象處理
GrayImage = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
print(u"讀入lenna圖的shape為", GrayImage.shape)

#直方圖均衡化
#equ = cv2.equalizeHist(gray)

#高斯平滑
Gaussian = cv2.GaussianBlur(GrayImage, (3, 3), 0, 0, cv2.BORDER_DEFAULT)
#Gaussian = cv2.GaussianBlur(GrayImage, (9, 9),0)

#中值濾波
Median = cv2.medianBlur(Gaussian, 5)

#顯示圖形
titles = ['Source Image','Gray Image', 'Gaussian Image', 'Median Image']  
images = [lenna_img, GrayImage, Gaussian, Median]  
for i in xrange(4):  
   plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')  
   plt.title(titles[i])  
   plt.xticks([]),plt.yticks([])  
plt.show()  

輸出結果如下圖所示,分別是原圖、灰度影象、高斯處理和中值濾波處理。

 


三、Sobel運算元提取輪廓和二值化處理

有時還需要加強影象中景物的邊緣和輪廓,邊緣和輪廓通常位於影象中灰度突出的地方,因而可以直觀的想到用灰度的差分對邊緣和輪廓進行提取,通常可以通過梯度運算元進行提取。影象銳化的目的是提高影象的對比度,從而使影象更清晰,通過提高鄰域內畫素的灰度差來提高影象的對比度。本文采用Sobel運算元提取邊緣輪廓。

閾值又稱為臨界值,它的目的是確定出一個範圍,然後這個範圍內的部分使用同一種方法處理,而閾值之外的部分則使用另一種處理方法或保持原樣。常用的包括產生二值圖:當x<T時y=0,當x>=T時y=255(其中T是閾值)。閾值變換在生物學上的應用比較廣泛,常用語細胞影象分割等。本文采用二值化處理將大於等於170畫素的轉換為255,而下於的轉換為0,使得影象更加清晰。

完整程式碼如下所示:

#encoding:utf-8
import cv2  
import numpy as np  
import matplotlib.pyplot as plt

#讀取圖片
imagePath = '10.jpg'
img = cv2.imread(imagePath)

#opencv預設的imread是以BGR的方式進行儲存的
lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

#灰度影象處理
GrayImage = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
print(u"讀入lenna圖的shape為", GrayImage.shape)

#直方圖均衡化
#equ = cv2.equalizeHist(gray)

# 高斯平滑
Gaussian = cv2.GaussianBlur(GrayImage, (3, 3), 0, 0, cv2.BORDER_DEFAULT)

# 中值濾波
Median = cv2.medianBlur(Gaussian, 5)

# Sobel運算元 XY方向求梯度
x = cv2.Sobel(Median, cv2.CV_8U, 1, 0, ksize = 3) #X方向
y = cv2.Sobel(Median, cv2.CV_8U, 0, 1, ksize = 3) #Y方向
absX = cv2.convertScaleAbs(x)   # 轉回uint8    
absY = cv2.convertScaleAbs(y)    
Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5,0)
cv2.imshow('dilation2', Sobel)
cv2.waitKey(0)

# 二值化處理 周圍畫素影響
ret, Binary = cv2.threshold(Sobel, 170, 255, cv2.THRESH_BINARY)
cv2.imshow('dilation2',Binary)
cv2.waitKey(0)

#顯示圖形
titles = ['Source Image','Gray Image', 'Gaussian Image', 'Median Image',
          'Sobel Image', 'Binary Image']  
images = [lenna_img, GrayImage, Gaussian, Median, Sobel, Binary]  
for i in xrange(6):  
   plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')  
   plt.title(titles[i])  
   plt.xticks([]),plt.yticks([])  
plt.show()  

輸出結果如下所示:
 

 

四、膨脹和腐蝕處理

接下來進行膨脹和腐蝕處理,其中膨脹讓輪廓突出,腐蝕去掉細節。

#encoding:utf-8
import cv2  
import numpy as np  
import matplotlib.pyplot as plt

#讀取圖片
imagePath = '10.jpg'
img = cv2.imread(imagePath)

#opencv預設的imread是以BGR的方式進行儲存的
lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

#灰度影象處理
GrayImage = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
print(u"讀入lenna圖的shape為", GrayImage.shape)

#直方圖均衡化
#equ = cv2.equalizeHist(gray)

#高斯平滑 去噪
Gaussian = cv2.GaussianBlur(GrayImage, (3, 3), 0, 0, cv2.BORDER_DEFAULT)
#Gaussian = cv2.GaussianBlur(GrayImage, (9, 9),0)

#中值濾波
Median = cv2.medianBlur(Gaussian, 5)

#Sobel運算元 XY方向求梯度 cv2.CV_8U
x = cv2.Sobel(Median, cv2.CV_32F, 1, 0, ksize = 3) #X方向
y = cv2.Sobel(Median, cv2.CV_32F, 0, 1, ksize = 3) #Y方向
#absX = cv2.convertScaleAbs(x)   # 轉回uint8    
#absY = cv2.convertScaleAbs(y)
#Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
gradient = cv2.subtract(x, y)
Sobel = cv2.convertScaleAbs(gradient)
cv2.imshow('dilation2', Sobel)
cv2.waitKey(0)

#二值化處理 周圍畫素影響
blurred = cv2.GaussianBlur(Sobel, (9, 9),0) #再進行高斯去噪
#注意170可以替換的
ret, Binary = cv2.threshold(blurred , 90, 255, cv2.THRESH_BINARY)
cv2.imshow('dilation2', Binary)
cv2.waitKey(0)

#膨脹和腐蝕操作的核函式
element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 1))
element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 7))

# 膨脹讓輪廓突出
Dilation = cv2.dilate(Binary, element2, iterations = 1)
# 腐蝕去掉細節
Erosion = cv2.erode(Dilation, element1, iterations = 1)
# 再次膨脹
Dilation2 = cv2.dilate(Erosion, element2,iterations = 3)
cv2.imshow('Dilation2 ', Dilation2)
cv2.waitKey(0)

#顯示圖形
titles = ['Source Image','Gray Image', 'Gaussian Image', 'Median Image',
          'Sobel Image', 'Binary Image', 'Dilation Image', 'Erosion Image', 'Dilation2 Image']  
images = [lenna_img, GrayImage, Gaussian,
          Median, Sobel, Binary,
          Dilation, Erosion, Dilation2]  
for i in xrange(9):  
   plt.subplot(3,3,i+1),plt.imshow(images[i],'gray')  
   plt.title(titles[i])  
   plt.xticks([]),plt.yticks([])  
plt.show()  

輸出結果如下圖所示,可以看到輪廓區域已經被提取出來,接下來開始有選擇的進行獲取。


 

五、指定演算法選擇車牌區域

該部分程式碼膨脹和腐蝕略有區別,採用closed變數實現。同時獲取最理想的區域,完整程式碼如下所示:

#encoding:utf-8
#BY:Eastmount CSDN 2018-08-06
import cv2  
import numpy as np  
import matplotlib.pyplot as plt

#讀取圖片
imagePath = '10.jpg'
img = cv2.imread(imagePath)

#opencv預設的imread是以BGR的方式進行儲存的
lenna_img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

#灰度影象處理
GrayImage = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
print(u"讀入lenna圖的shape為", GrayImage.shape)

#直方圖均衡化
#equ = cv2.equalizeHist(gray)

#高斯平滑 去噪
Gaussian = cv2.GaussianBlur(GrayImage, (3, 3), 0, 0, cv2.BORDER_DEFAULT)
#Gaussian = cv2.GaussianBlur(GrayImage, (9, 9),0)

#中值濾波
Median = cv2.medianBlur(Gaussian, 5)

#Sobel運算元 XY方向求梯度 cv2.CV_8U
x = cv2.Sobel(Median, cv2.CV_32F, 1, 0, ksize = 3) #X方向
y = cv2.Sobel(Median, cv2.CV_32F, 0, 1, ksize = 3) #Y方向
#absX = cv2.convertScaleAbs(x)   # 轉回uint8    
#absY = cv2.convertScaleAbs(y)
#Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
gradient = cv2.subtract(x, y)
Sobel = cv2.convertScaleAbs(gradient)
cv2.imshow('dilation2', Sobel)
cv2.waitKey(0)

#二值化處理 周圍畫素影響
blurred = cv2.GaussianBlur(Sobel, (9, 9),0) #再進行一次高斯去噪
#注意170可以替換的
ret, Binary = cv2.threshold(blurred , 170, 255, cv2.THRESH_BINARY)
cv2.imshow('dilation2', Binary)
cv2.waitKey(0)

# 膨脹和腐蝕操作的核函式
element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 1))
element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 7))
# 膨脹一次,讓輪廓突出
Dilation = cv2.dilate(Binary, element2, iterations = 1)
# 腐蝕一次,去掉細節
Erosion = cv2.erode(Dilation, element1, iterations = 1)
# 再次膨脹,讓輪廓明顯一些
Dilation2 = cv2.dilate(Erosion, element2,iterations = 3)
cv2.imshow('Dilation2 ', Dilation2)
cv2.waitKey(0)


##########################################

#建立一個橢圓核函式
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (25, 25))
#執行影象形態學, 細節直接查文件,很簡單
closed = cv2.morphologyEx(Binary, cv2.MORPH_CLOSE, kernel)
closed = cv2.erode(closed, None, iterations=4)
closed = cv2.dilate(closed, None, iterations=4)
cv2.imshow('erode dilate', closed)
cv2.waitKey(0)

##########################################


#顯示圖形
titles = ['Source Image','Gray Image', 'Gaussian Image', 'Median Image',
          'Sobel Image', 'Binary Image', 'Dilation Image', 'Erosion Image', 'Dilation2 Image']  
images = [lenna_img, GrayImage, Gaussian,
          Median, Sobel, Binary,
          Dilation, Erosion, closed]  
for i in xrange(9):  
   plt.subplot(3,3,i+1),plt.imshow(images[i],'gray')  
   plt.title(titles[i])  
   plt.xticks([]),plt.yticks([])  
plt.show()  

cv2.imshow('Gray', GrayImage)
cv2.waitKey(0)

"""
接下來使用Dilation2圖片確定車牌的輪廓
這裡opencv3返回的是三個引數
  引數一:二值化影象
  引數二:輪廓型別 檢測的輪廓不建立等級關係
  引數三:處理近似方法  例如一個矩形輪廓只需4個點來儲存輪廓資訊
"""
(_, cnts, _) = cv2.findContours(closed.copy(), 
                                cv2.RETR_LIST,               #RETR_TREE
                                cv2.CHAIN_APPROX_SIMPLE)

#畫出輪廓
c = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
print c

#compute the rotated bounding box of the largest contour
rect = cv2.minAreaRect(c)
print 'rectt', rect
Box = np.int0(cv2.boxPoints(rect))
print 'Box', Box

#draw a bounding box arounded the detected barcode and display the image
Final_img = cv2.drawContours(img.copy(), [Box], -1, (0, 0, 255), 3)

cv2.imshow('Final_img', Final_img)
cv2.waitKey(0)

輸出結果如下圖所示,可以看到車牌被選中了。

參考文獻推薦大家學習三位大神的作品:
https://www.jianshu.com/p/fcfbd3131b84
https://blog.csdn.net/sinat_36458870/article/details/78825571
https://blog.csdn.net/sumkee911/article/details/79435983

當然還有很多優化和提升的地方,作者自己也還在不斷學習中,希望大家對這篇文章感興趣,同時最近的Python新書也宣傳下,大家一起進步。基礎性文章,如果文章中有錯誤或不足之處還請海涵。

 

 

本書主要包括上下兩冊:

    《Python網路資料爬取及分析從入門到精通(爬取篇)》
    《Python網路資料爬取及分析從入門到精通(分析篇)》


(By:Eastmount 2018-08-06 深夜11點  http://blog.csdn.net/eastmount/ )

 

相關文章