zoukankan      html  css  js  c++  java
  • 【车牌识别】-车牌中字符分割代码详解

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

    思路:

      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 '''

    处理图片的过程展示:

  • 相关阅读:
    Android AsyncTask
    android 自定义 view 和 ViewGroup
    Android Acitivity 生命周期
    Android Service 与 IntentService
    Android LocalBroadcastManager 与 BroadcastReceiver
    如何提升 service 等级,不被kill(整合)
    Android 插件开发,做成动态加载
    新提交审核app保留检查更新入口将被拒绝(读取App Store 版本号的)
    Android: Service中创建窗口显示
    如何升级PowerShell
  • 原文地址:https://www.cnblogs.com/lyj0123/p/13669451.html
Copyright © 2011-2022 走看看