基於OpenCV和Python的車牌提取和字元分割

榮仔!最靚的仔!發表於2020-09-25

這是一篇介紹基於 OpenCV 和 Python 實現車牌提取專案思路和原始碼的文章,本文涉及一些人工智慧和影像識別技術,具體而言,涉及到關於車牌號碼識別的研究(車牌提取和字元分割),網上查詢到的方案有 tensorflow 和opencv,opencv 也是比較成熟的方案,先從簡單的開始,以下是關於使用opencv實現車牌號碼提取的部分。

1 車牌提取

1.1 實現思路

  1. 讀取彩色的圖片
  2. 轉換為灰度圖
  3. 高斯模糊
  4. Sobel運算元進行邊緣檢測
  5. 影像二值化
  6. 閉操作(腐蝕和擴張)
  7. 迴圈找到所有的輪廓
  8. 判斷車牌區域

1.2 原圖

在這裡插入圖片描述

1.3 程式碼詳解

1.3.1 匯入包庫

import cv2 as cv
import matplotlib.pyplot as plt

1.3.2 讀取彩色的圖片

cv.imread("E:/car.png")

在這裡插入圖片描述

1.3.3 轉換為灰度圖

cv.cvtColor(img, cv.COLOR_BGR2GRAY)

在這裡插入圖片描述

1.3.4 高斯模糊

通過高斯模糊,可以去除部分的干擾,讓識別更加準確。

cv.GaussianBlur(img1,(5,5),10)

在這裡插入圖片描述

1.3.5 用Sobel運算元進行邊緣檢測

便於接下來提取輪廓

cv.Sobel(img2,cv.CV_8U,1,0,ksize=1)
cv.Canny(img3,250,100)

在這裡插入圖片描述

1.3.6 進行二值化處理

將影像上的畫素點的灰度值設定為0或255,影像呈現出明顯的只有黑和白。

cv.threshold(img4,0,255,cv.THRESH_BINARY)

在這裡插入圖片描述

1.3.7 閉操作

依次進行腐蝕和擴張,這一步操作可以將目標區域連成一個整體,便於後續輪廓的提取。

cv.getStructuringElement(cv.MORPH_RECT,(43,33))
cv.dilate(img5,kernel)

在這裡插入圖片描述

1.3.8 查詢輪廓

i,j = cv.findContours(img6,cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE)

1.3.9 判斷車牌區域

result = None
for i1 in i:
    x,y,w,h = cv.boundingRect(i1)
    if w>2*h:
        print(1)
        plt.imshow(img[y:y+h,x:x+w])
        plt.show()
        result = img[y:y+h,x:x+w]

在這裡插入圖片描述

1.4 總觀程式碼

import cv2 as cv
import matplotlib.pyplot as plt

# 讀取彩色的圖片
img = cv.imread("E:/car.png")
plt.imshow(img)
plt.show()
# 轉換為灰度圖
img1 = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
plt.imshow(img1)
plt.show()
# 用Sobel進行邊緣檢測
# # 高斯模糊
img2 = cv.GaussianBlur(img1,(5,5),10)
plt.imshow(img2)
plt.show()
# Laplacian進行邊緣檢測
img3 = cv.Sobel(img2,cv.CV_8U,1,0,ksize=1)
plt.imshow(img3)
plt.show()
img4 = cv.Canny(img3,250,100)
plt.imshow(img4)
plt.show()
# 進行二值化處理
i,img5 = cv.threshold(img4,0,255,cv.THRESH_BINARY)
plt.imshow(img5)
plt.show()
# 可以侵蝕和擴張
kernel = cv.getStructuringElement(cv.MORPH_RECT,(43,33))
img6 = cv.dilate(img5,kernel)
plt.imshow(img6)
plt.show()
# # 迴圈找到所有的輪廓
i,j = cv.findContours(img6,cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE)
result = None
for i1 in i:
    x,y,w,h = cv.boundingRect(i1)
    if w>2*h:
        print(1)
        plt.imshow(img[y:y+h,x:x+w])
        plt.show()
        result = img[y:y+h,x:x+w]

1.5 最終實現效果

在這裡插入圖片描述

2 車牌識別之字元分割

前面對這牌提取做個詳細描述,與此相類似,車牌的字元分割也是很重要的一部分,字元分割的思想在其他專案中同樣有很重要的作用。因此有必要針對字元分割的思路和實現過程做一個記錄。

2.1 實現思路

總的來說,是基於畫素直方圖的字元分割實現的:首先對圖片進行二值化處理,統計水平方向和豎直方向上各行各列的黑色畫素的個數,根據畫素的特點確定分割位置,進而完成字元分割。

2.2 原圖

在這裡插入圖片描述

2.3 程式碼詳解

2.3.1 匯入包庫

import cv2
from matplotlib import pyplot as plt

2.3.2 讀取影像,並把影像轉換為灰度影像並顯示

cv2.imread('E:/3.png')  # 讀取圖片
cv2.cvtColor(img_, cv2.COLOR_BGR2GRAY)  # 轉換了灰度化

在這裡插入圖片描述

2.3.3 將灰度影像二值化,設定閾值是100

cv2.threshold(img_gray, 100, 255, cv2.THRESH_BINARY_INV)

在這裡插入圖片描述

2.3.4 分割字元

水平方向:統計每一行黑色畫素數量n,並記錄。可以根據每一行黑色畫素的數目來確定分割的起始和終止;由圖可知,當 n減小到一定閾值時,為字元的邊緣;
豎直方向:同理,統計每一列的黑色畫素數量v,並記錄。可以可以根據每一列黑色畫素的數目的變化來確定分割的起始和終止。

white = []  # 記錄每一列的白色畫素總和
black = []  # ..........黑色.......
height = img_thre.shape[0]
width = img_thre.shape[1]
white_max = 0
black_max = 0
# 計算每一列的黑白色畫素總和
for i in range(width):
    s = 0  # 這一列白色總數
    t = 0  # 這一列黑色總數
    for j in range(height):
        if img_thre[j][i] == 255:
            s += 1
        if img_thre[j][i] == 0:
            t += 1
    white_max = max(white_max, s)
    black_max = max(black_max, t)
    white.append(s)
    black.append(t)

2.3.5 分割影像

def find_end(start_):
    end_ = start_ + 1
    for m in range(start_ + 1, width - 1):
        if (black[m] if arg else white[m]) > (0.95 * black_max if arg else 0.95 * white_max):  # 0.95這個引數請多調整,對應下面的0.05(針對畫素分佈調節)
            end_ = m
            break
    return end_

2.4 總觀程式碼

import cv2
from matplotlib import pyplot as plt
## 根據每行和每列的黑色和白色畫素數進行圖片分割。

# 1、讀取影像,並把影像轉換為灰度影像並顯示
img_ = cv2.imread('E:/3.png')  # 讀取圖片
img_gray = cv2.cvtColor(img_, cv2.COLOR_BGR2GRAY)  # 轉換了灰度化
# cv2.imshow('gray', img_gray)  # 顯示圖片
# cv2.waitKey(0)

# 2、將灰度影像二值化,設定閾值是100
ret, img_thre = cv2.threshold(img_gray, 100, 255, cv2.THRESH_BINARY_INV)
# cv2.imshow('white_black image', img_thre)  # 顯示圖片
# cv2.waitKey(0)

# 4、分割字元
white = []  # 記錄每一列的白色畫素總和
black = []  # ..........黑色.......
height = img_thre.shape[0]
width = img_thre.shape[1]
white_max = 0
black_max = 0
# 計算每一列的黑白色畫素總和
for i in range(width):
    s = 0  # 這一列白色總數
    t = 0  # 這一列黑色總數
    for j in range(height):
        if img_thre[j][i] == 255:
            s += 1
        if img_thre[j][i] == 0:
            t += 1
    white_max = max(white_max, s)
    black_max = max(black_max, t)
    white.append(s)
    black.append(t)
    # print(s)
    # print(t)

arg = False  # False表示白底黑字;True表示黑底白字
if black_max > white_max:
    arg = True

# 分割影像
def find_end(start_):
    end_ = start_ + 1
    for m in range(start_ + 1, width - 1):
        if (black[m] if arg else white[m]) > (0.95 * black_max if arg else 0.95 * white_max):  # 0.95這個引數請多調整,對應下面的0.05(針對畫素分佈調節)
            end_ = m
            break
    return end_

n = 1
start = 1
end = 2
word = []
while n < width - 2:
    n += 1
    if (white[n] if arg else black[n]) > (0.05 * white_max if arg else 0.05 * black_max):
        # 上面這些判斷用來辨別是白底黑字還是黑底白字
        # 0.05這個引數請多調整,對應上面的0.95
        start = n
        end = find_end(start)
        n = end
        if end - start > 5:
            cj = img_[1:height, start:end]
            cj = cv2.resize(cj, (15, 30))
            word.append(cj)

print(len(word))
for i,j in enumerate(word):
    plt.subplot(1,8,i+1)
    plt.imshow(word[i],cmap='gray')
plt.show()

2.5 最終實現效果

在這裡插入圖片描述

相關文章