首页 > 其他 > 详细

【车牌识别】-车牌中字符分割代码详解

时间:2020-09-14 22:19:23      阅读:99      评论:0      收藏:0      [点我收藏+]

车牌识别项目中,关于字符分割的实现:

思路:

  1. 使用 cv2 读取图片

  2. 使用 cv2.cvtColor( img,cv2.COLOR_RGB2GRAY) 函数将 BGR 图像转为灰度图

  3. 车牌原图 技术分享图片 较为简单,使用阈值处理灰度图,将像素值大于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 ‘‘‘

 

图片展示:

技术分享图片

 

 

### 对车牌图片进行处理,分割出车牌中的每一个字符并保存
# 在本地读取图片的时候,如果路径中包含中文,会导致读取失败。

import cv2
import paddle
import numpy as np
import matplotlib.pyplot as plt
#以下两行实现了在plt画图时,可以输出中文字符
plt.rcParams[‘font.sans-serif‘]=[‘SimHei‘]
plt.rcParams[‘axes.unicode_minus‘] = False


# cv2.imread() 读进来直接是BGR 格式数据,数值范围在 0~255 。在本地读取,路径中不要含有中文
license_plate = cv2.imread(‘../data/car.png‘) # license 拍照,plate 车牌
print(‘license_plate type ‘, type(license_plate), license_plate.shape) # <class ‘numpy.ndarray‘> (170, 722, 3)

plt.subplot(231)
plt.imshow(license_plate)
plt.title(原图 BGR ‘)

gray_plate2 = cv2.cvtColor(license_plate, cv2.COLOR_BGR2RGB) # RGB 转灰度图
plt.subplot(234)
plt.imshow(gray_plate2)
plt.title(‘RGB图像)
# cv2.cvtColor(p1,p2) 是颜色空间转换函数,p1是需要转换的图片,p2是转换成何种格式。
# cv2.COLOR_BGR2RGB BGR格式转换成RGB格式
# cv2.COLOR_BGR2GRAY BGR格式转换成灰度图片(灰度图片并不是指常规意义上的黑白图片,只用看是不是无符号八位整型(unit8,单通道即可判断)
gray_plate = cv2.cvtColor(license_plate, cv2.COLOR_RGB2GRAY) # RGB 转灰度图
print(‘gray_plate.shape ‘, gray_plate.shape) # (170, 722)

plt.subplot(232)
plt.imshow(gray_plate)
plt.title(‘GRAY 图像)

# Python: cv2.threshold(src, thresh, maxval, type[, dst]) → retval, dst
# src:表示的是图片源
# thresh:表示的是阈值(起始值)
# maxval:表示的是最大值,在高于阈值是赋予的新值
# type:表示的是这里划分的时候使用的是什么类型的算法**,常用值为0cv2.THRESH_BINARY
# cv2.THRESH_BINARY 大于阈值取最大值 maxval ,小于等于阈值取 0
# 两个返回值,第一个retVal(得到的阈值值(在后面一个方法中会用到)),第二个就是阈值化后的图像。
ret, binary_plate = cv2.threshold(gray_plate, 175, 255, cv2.THRESH_BINARY) # ret:阈值,binary_plate:根据阈值处理后的图像数据
print(‘ret ‘, ret) # 175.0
print(‘binary_plate ‘, binary_plate.shape ) # (170, 722)

plt.subplot(233)
plt.imshow(binary_plate)
plt.title(阈值处理之后的图像)

# 按列统计像素分布
result = []
for col in range(binary_plate.shape[1]):
result.append(0) # 每一列像素值初始化为 0
for row in range(binary_plate.shape[0]):
result[col] = result[col] + binary_plate[row][col] / 255
# print(result)
# 记录车牌中字符的位置
character_dict = {} # character_dict 是一个字典,里面一个元素中的value 部分存储一个车牌中的字符
num = 0 # 记录统计的车牌中的第几个字符,同时是 字典 character_dict 中的 key
i = 0 # 表示是第几列像素
while i < len(result):
# 这一列上没有像素值
if result[i] == 0:
i += 1
else:
index = i + 1
while result[index] != 0:
index += 1
# i 列 到 第 index-1 列,存储了一个字符,这里做了一个假设像这样左右结构的字,在列的方向上是没有像素断点的
# character_dict 是一个字典,num 是字典的 key[i, index - 1] 是一个存储了两个数的列表作为字典的value
character_dict[num] = [i, index - 1]
num += 1
i = index
print(‘character_dict ‘, character_dict)

# 将每个字符填充,并存储
characters = []
for i in range(8): # 车牌一共 8 个字符,其中第 3 个字符(下标为 2 )是一个 ·
if i == 2:
continue
# 将字符填充为 170*170 的灰度图,padding 为计算左右需要各自填充多少列元素
padding = (170 - (character_dict[i][1] - character_dict[i][0])) / 2

# np.pad() 函数原型:ndarray = numpy.pad(array, pad_width, mode, **kwargs)
# array为要填补的数组
# pad_width 是在各维度的各个方向上想要填补的长度,如((12),(22)),
# 表示在第一个维度上水平方向上padding=1,垂直方向上padding=2, 在第二个维度上水平方向上padding=2,垂直方向上padding=2
# 如果直接输入一个整数,则说明各个维度和各个方向所填补的长度都一样。
# mode为填补类型,即怎样去填补,有“constant”“edge”等模式,如果为constant模式,就得指定填补的值,如果不指定,则默认填充0
# 剩下的都是一些可选参数,具体可查看
# https://docs.scipy.org/doc/numpy/reference/generated/numpy.pad.html

# ndarray为填充好的返回值。
ndarray = np.pad(binary_plate[:, character_dict[i][0]:character_dict[i][1]], # array : 为要填补的数组
# pad_width:在各维度的各个方向上想要填补的长度。在第一个维度()前面填充 0 行,后面填充 0 行;
# 在第二个维度()前面填充 padding 列 后面填充 padding
((0, 0), (int(padding), int(padding))),
# mode为填补类型,即怎样去填补,有“constant”“edge”等模式,
# 如果为constant模式,就得指定填补的值,如果不指定,则默认填充0
‘constant‘, constant_values=(0, 0)
)
print( {} 个字符.format(i+1))
print(原数组尺寸 :, binary_plate[:, character_dict[i][0]:character_dict[i][1]].shape)
print(填充之后的尺寸 :, ndarray.shape)
ndarray = cv2.resize(ndarray, (20, 20))
print(‘resize 之后的尺寸 :, ndarray.shape)

cv2.imwrite(‘../data/‘ + str(i) + ‘.png‘, ndarray)
characters.append(ndarray)
ndarray2 = cv2.resize(binary_plate[:, character_dict[i][0]:character_dict[i][1]], (20, 20))
cv2.imwrite(‘../data/2‘ + str(i) + ‘.png‘, ndarray)

plt.show()


‘‘‘ 输出结果:
license_plate type <class ‘numpy.ndarray‘> (170, 722, 3)
gray_plate.shape (170, 722)
ret 175.0
binary_plate (170, 722)
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]}

1 个字符
原数组尺寸 : (170, 70)
填充之后的尺寸 : (170, 170)
resize 之后的尺寸 : (20, 20)
2 个字符
原数组尺寸 : (170, 70)
填充之后的尺寸 : (170, 170)
resize 之后的尺寸 : (20, 20)
4 个字符
原数组尺寸 : (170, 71)
填充之后的尺寸 : (170, 169)
resize 之后的尺寸 : (20, 20)
5 个字符
原数组尺寸 : (170, 72)
填充之后的尺寸 : (170, 170)
resize 之后的尺寸 : (20, 20)
6 个字符
原数组尺寸 : (170, 73)
填充之后的尺寸 : (170, 169)
resize 之后的尺寸 : (20, 20)
7 个字符
原数组尺寸 : (170, 75)
填充之后的尺寸 : (170, 169)
resize 之后的尺寸 : (20, 20)
8 个字符
原数组尺寸 : (170, 77)
填充之后的尺寸 : (170, 169)
resize 之后的尺寸 : (20, 20)
‘‘‘

【车牌识别】-车牌中字符分割代码详解

原文:https://www.cnblogs.com/lyj0123/p/13669451.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!