【車牌識別】-車牌中字符分割代碼詳解


車牌識別項目中,關於字符分割的實現:

思路:

  1. 讀取圖片,使用 cv2 。

  2. 將 BGR 圖像轉為灰度圖,使用 cv2.cvtColor( img,cv2.COLOR_RGB2GRAY) 函數。

  3. 車牌原圖尺寸 (170, 722) ,使用閾值處理灰度圖,將像素值大於175的像素點的像素設置為 255 ,不大於175的像素點的像素設置為 0 。

  4.觀察車牌中字符,可以看到每個字符塊中的 每列像素值的和 都不為 0 ,這里做了假設,將左右結構的省份簡寫的字也看作是由連續相鄰的列組成的,如 “ 桂 ” 。

 

  5. 對於經過閾值處理的車牌中的字符進行按列求像素值的和,

  • 如果一列像素值的和為 0,則表明該列不含有字符為空白區域。
  • 反之,則該列屬於字符中的一列。判斷直到又出現一列像素點的值的和為0,則這這兩列中間的列構成一個字符,保存到字典 character_dict 中,
  • 字典的 key 值為第幾個字符 ( 下標從0開始 ),字典的value值為起始列的下標和終止列的下標 。
  • character_dict  是字典,每一個元素中的value 是一個列表記錄了夾住一個字符的起始列下標和終止列下標 。

  6. 之后再對字符進行填充,填充為170*170大小的灰度圖(第三個字符為一個點,不需要處理,跳過即可。有可能列數不足170,這影響不大)。

  7. 對填充之后的字符進行resize,處理成20*20的灰度圖,然后對字符分別進行存儲。

 

代碼實現:

  1 ### 對車牌圖片進行處理,分割出車牌中的每一個字符並保存
  2 # 在本地讀取圖片的時候,如果路徑中包含中文,會導致讀取失敗。
  3 
  4 import cv2
  5 import paddle
  6 import numpy as np
  7 import matplotlib.pyplot as plt
  8 #以下兩行實現了在plt畫圖時,可以輸出中文字符
  9 plt.rcParams['font.sans-serif']=['SimHei']
 10 plt.rcParams['axes.unicode_minus'] = False
 11 
 12 
 13 # cv2.imread() 讀進來直接是BGR 格式數據,數值范圍在 0~255 。在本地讀取,路徑中不要含有中文
 14 license_plate = cv2.imread('../data/car.png')  # license 拍照,plate 車牌
 15 print('license_plate  的 type  ', type(license_plate), license_plate.shape)  # <class 'numpy.ndarray'> (170, 722, 3)
 16 
 17 plt.subplot(231)
 18 plt.imshow(license_plate)
 19 plt.title('原圖 BGR ')
 20 
 21 gray_plate2 = cv2.cvtColor(license_plate, cv2.COLOR_BGR2RGB)  # RGB 轉灰度圖
 22 plt.subplot(234)
 23 plt.imshow(gray_plate2)
 24 plt.title('RGB圖像')
 25 # cv2.cvtColor(p1,p2) 是顏色空間轉換函數,p1是需要轉換的圖片,p2是轉換成何種格式。
 26 # cv2.COLOR_BGR2RGB 將BGR格式轉換成RGB格式
 27 # cv2.COLOR_BGR2GRAY 將BGR格式轉換成灰度圖片(灰度圖片並不是指常規意義上的黑白圖片,只用看是不是無符號八位整型(unit8),單通道即可判斷)
 28 gray_plate = cv2.cvtColor(license_plate, cv2.COLOR_RGB2GRAY)  # RGB 轉灰度圖
 29 print('gray_plate.shape    ', gray_plate.shape)               # (170, 722)
 30 
 31 plt.subplot(232)
 32 plt.imshow(gray_plate)
 33 plt.title('GRAY 圖像')
 34 
 35 # Python: cv2.threshold(src, thresh, maxval, type[, dst]) → retval, dst
 36 # src:表示的是圖片源
 37 # thresh:表示的是閾值(起始值)
 38 # maxval:表示的是最大值,在高於閾值是賦予的新值
 39 # type:表示的是這里划分的時候使用的是什么類型的算法**,常用值為0(cv2.THRESH_BINARY)
 40 #       cv2.THRESH_BINARY 大於閾值取最大值 maxval ,小於等於閾值取 0
 41 # 兩個返回值,第一個retVal(得到的閾值值(在后面一個方法中會用到)),第二個就是閾值化后的圖像。
 42 ret, binary_plate = cv2.threshold(gray_plate, 175, 255, cv2.THRESH_BINARY)  # ret:閾值,binary_plate:根據閾值處理后的圖像數據
 43 print('ret  ', ret)                                # 175.0
 44 print('binary_plate  ', binary_plate.shape )       # (170, 722)
 45 
 46 plt.subplot(233)
 47 plt.imshow(binary_plate)
 48 plt.title('閾值處理之后的圖像 ')
 49 
 50 # 按列統計像素分布
 51 result = []
 52 for col in range(binary_plate.shape[1]):
 53     result.append(0)    # 每一列像素值初始化為 0
 54     for row in range(binary_plate.shape[0]):
 55         result[col] = result[col] + binary_plate[row][col] / 255
 56 # print(result)
 57 # 記錄車牌中字符的位置
 58 character_dict = {}   # character_dict 是一個字典,里面一個元素中的value 部分存儲一個車牌中的字符
 59 num = 0    # 記錄統計的車牌中的第幾個字符,同時是 字典 character_dict 中的 key 值
 60 i = 0      # 表示是第幾列像素
 61 while i < len(result):
 62     # 這一列上沒有像素值
 63     if result[i] == 0:
 64         i += 1
 65     else:
 66         index = i + 1
 67         while result[index] != 0:
 68             index += 1
 69         # 第 i 列 到 第 index-1 列,存儲了一個字符,這里做了一個假設像 “ 桂 ” 這樣左右結構的字,在列的方向上是沒有像素斷點的
 70         # character_dict 是一個字典,num 是字典的 key,[i, index - 1] 是一個存儲了兩個數的列表作為字典的value
 71         character_dict[num] = [i, index - 1]
 72         num += 1
 73         i = index
 74 print('character_dict  ', character_dict)
 75 
 76 # 將每個字符填充,並存儲
 77 characters = []
 78 for i in range(8):  # 車牌一共 8 個字符,其中第 3 個字符(下標為 2 )是一個 ·
 79     if i == 2:
 80         continue
 81     # 將字符填充為 170*170 的灰度圖,padding 為計算左右需要各自填充多少列元素
 82     padding = (170 - (character_dict[i][1] - character_dict[i][0])) / 2
 83 
 84     # np.pad() 函數原型:ndarray = numpy.pad(array, pad_width, mode, **kwargs)
 85     # array為要填補的數組
 86     # pad_width 是在各維度的各個方向上想要填補的長度,如((1,2),(2,2)),
 87     #     表示在第一個維度上水平方向上padding=1,垂直方向上padding=2,      在第二個維度上水平方向上padding=2,垂直方向上padding=2。
 88     #     如果直接輸入一個整數,則說明各個維度和各個方向所填補的長度都一樣。
 89     # mode為填補類型,即怎樣去填補,有“constant”,“edge”等模式,如果為constant模式,就得指定填補的值,如果不指定,則默認填充0。
 90     # 剩下的都是一些可選參數,具體可查看
 91     # https://docs.scipy.org/doc/numpy/reference/generated/numpy.pad.html
 92 
 93     # ndarray為填充好的返回值。
 94     ndarray = np.pad(binary_plate[:, character_dict[i][0]:character_dict[i][1]], # array : 為要填補的數組
 95                      # pad_width:在各維度的各個方向上想要填補的長度。在第一個維度(行)前面填充 0 行,后面填充 0 行;
 96                      # 在第二個維度(列)前面填充 padding 列 后面填充 padding 列
 97                      ((0, 0), (int(padding), int(padding))),
 98                      # mode為填補類型,即怎樣去填補,有“constant”,“edge”等模式,
 99                      # 如果為constant模式,就得指定填補的值,如果不指定,則默認填充0。
100                      'constant', constant_values=(0, 0)
101                      )
102     print('第 {} 個字符'.format(i+1))
103     print('原數組尺寸 : ', binary_plate[:, character_dict[i][0]:character_dict[i][1]].shape)
104     print('填充之后的尺寸 :', ndarray.shape)
105     ndarray = cv2.resize(ndarray, (20, 20))
106     print('resize 之后的尺寸 :', ndarray.shape)
107 
108     cv2.imwrite('../data/' + str(i) + '.png', ndarray)
109     characters.append(ndarray)
110     ndarray2 = cv2.resize(binary_plate[:, character_dict[i][0]:character_dict[i][1]], (20, 20))
111     cv2.imwrite('../data/2' + str(i) + '.png', ndarray)
112 
113 plt.show()
114 
115 
116 ''' 輸出結果:
117 license_plate  的 type   <class 'numpy.ndarray'> (170, 722, 3)
118 gray_plate.shape     (170, 722)
119 ret   175.0
120 binary_plate   (170, 722)
121 character_dict   {0: [17, 87], 1: [109, 179], 2: [203, 216], 3: [240, 311], 4: [334, 406], 5: [430, 503], 6: [528, 603], 7: [629, 706]}
122 
123 第 1 個字符
124 原數組尺寸 :  (170, 70)
125 填充之后的尺寸 : (170, 170)
126 resize 之后的尺寸 : (20, 20)
127 第 2 個字符
128 原數組尺寸 :  (170, 70)
129 填充之后的尺寸 : (170, 170)
130 resize 之后的尺寸 : (20, 20)
131 第 4 個字符
132 原數組尺寸 :  (170, 71)
133 填充之后的尺寸 : (170, 169)
134 resize 之后的尺寸 : (20, 20)
135 第 5 個字符
136 原數組尺寸 :  (170, 72)
137 填充之后的尺寸 : (170, 170)
138 resize 之后的尺寸 : (20, 20)
139 第 6 個字符
140 原數組尺寸 :  (170, 73)
141 填充之后的尺寸 : (170, 169)
142 resize 之后的尺寸 : (20, 20)
143 第 7 個字符
144 原數組尺寸 :  (170, 75)
145 填充之后的尺寸 : (170, 169)
146 resize 之后的尺寸 : (20, 20)
147 第 8 個字符
148 原數組尺寸 :  (170, 77)
149 填充之后的尺寸 : (170, 169)
150 resize 之后的尺寸 : (20, 20)
151 '''

 

處理圖片的過程展示:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM