[项目实践] 【Python实战】——Python+Opencv是实现车牌自动识别

976 0
Honkers 2025-3-5 17:54:32 | 显示全部楼层 |阅读模式

该篇文章将以实战形式演示利用Python结合Opencv实现车牌识别,全程涉及图像预处理、车牌定位、车牌分割、通过模板匹配识别结果输出。该项目对于智能交通、车辆管理等领域具有实际应用价值。通过自动识别车牌号码,可以实现车辆追踪、违章查询、停车场管理等功能,提高交通管理的效率和准确性。可用于车牌识别技术学习。

技术要点:

  • OpenCV:用于图像处理和计算机视觉任务。
  • Python:作为编程语言,具有简单易学、资源丰富等优点。
  • 图像处理技术:如灰度化、噪声去除、边缘检测、形态学操作、透视变换等。

1 导入相关模块

  1. import cv2
  2. from matplotlib import pyplot as plt
  3. import os
  4. import numpy as np
  5. from PIL import ImageFont, ImageDraw, Image
复制代码

2 相关功能函数定义

2.1 彩色图片显示函数(plt_show0)

  1. def plt_show0(img):
  2. b,g,r = cv2.split(img)
  3. img = cv2.merge([r, g, b])
  4. plt.imshow(img)
  5. plt.show()
复制代码

cv2与plt的图像通道不同:cv2为[b,g,r];plt为[r, g, b]

2.2 灰度图片显示函数(plt_show)

  1. def plt_show(img):
  2. plt.imshow(img,cmap='gray')
  3. plt.show()
复制代码

2.3 图像去噪函数(gray_guss)

  1. def gray_guss(image):
  2. image = cv2.GaussianBlur(image, (3, 3), 0)
  3. gray_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
  4. return gray_image
复制代码

此处演示使用高斯模糊去噪。

cv2.GaussianBlur参数说明:

  • src:输入图像,可以是任意数量的通道,这些通道可以独立处理,但深度应为 CV_8U、CV_16U、CV_16S、CV_32F 或 CV_64F。
  • ksize:高斯核的大小,必须是正奇数,例如 (3, 3)、(5, 5) 等。如果 ksize 的值为零,那么它会根据 sigmaX 和 sigmaY 的值来计算。
  • sigmaX:X 方向上的高斯核标准偏差。
  • dst:输出图像,大小和类型与 src 相同。
  • sigmaY:Y 方向上的高斯核标准偏差,如果 sigmaY 是零,那么它会与 sigmaX 的值相同。如果 sigmaY 是负数,那么它会从 ksize.width 和 ksize.height 计算得出。
  • borderType:像素外插法,有默认值。

2 图像预处理

2.1 图片读取

  1. origin_image = cv2.imread('D:/image/car3.jpg')
复制代码

  此处演示识别车牌原图:

2.2 高斯去噪

  1. origin_image = cv2.imread('D:/image/car3.jpg')
  2. # 复制一张图片,在复制图上进行图像操作,保留原图
  3. image = origin_image.copy()
  4. gray_image = gray_guss(image)
复制代码

2.3 边缘检测

  1. Sobel_x = cv2.Sobel(gray_image, cv2.CV_16S, 1, 0)
  2. absX = cv2.convertScaleAbs(Sobel_x)
  3. image = absX
复制代码

x方向上的边缘检测(增强边缘信息)

2.4 阈值化

  1. # 图像阈值化操作——获得二值化图
  2. ret, image = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)
  3. # 显示灰度图像
  4. plt_show(image)
复制代码

  运行结果:

3 车牌定位

3.1 区域选择

  1. kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 10))
  2. image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernelX,iterations = 1)
  3. # 显示灰度图像
  4. plt_show(image)
复制代码

从图像中提取对表达和描绘区域形状有意义的图像分量。

  运行结果:

3.2 形态学操作

  1. # 腐蚀(erode)和膨胀(dilate)
  2. kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 1))
  3. kernelY = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 20))
  4. #x方向进行闭操作(抑制暗细节)
  5. image = cv2.dilate(image, kernelX)
  6. image = cv2.erode(image, kernelX)
  7. #y方向的开操作
  8. image = cv2.erode(image, kernelY)
  9. image = cv2.dilate(image, kernelY)
  10. # 中值滤波(去噪)
  11. image = cv2.medianBlur(image, 21)
  12. # 显示灰度图像
  13. plt_show(image)
复制代码

使用膨胀和腐蚀操作来突出车牌区域。

   运行结果:

3.3 轮廓检测

  1. contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  2. for item in contours:
  3. rect = cv2.boundingRect(item)
  4. x = rect[0]
  5. y = rect[1]
  6. weight = rect[2]
  7. height = rect[3]
  8. # 根据轮廓的形状特点,确定车牌的轮廓位置并截取图像
  9. if (weight > (height * 3)) and (weight < (height * 4.5)):
  10. image = origin_image[y:y + height, x:x + weight]
  11. plt_show(image)
复制代码

4 车牌字符分割

4.1 高斯去噪

  1. # 图像去噪灰度处理
  2. gray_image = gray_guss(image)
复制代码

4.2 阈值化

  1. ret, image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_OTSU)
  2. plt_show(image)
复制代码

  运行结果:

4.3 膨胀操作

  1. #膨胀操作
  2. kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (4, 4))
  3. image = cv2.dilate(image, kernel)
  4. plt_show(image)
复制代码

  运行结果:

4.4 车牌号排序

  1. words = sorted(words,key=lambda s:s[0],reverse=False)
  2. i = 0
  3. #word中存放轮廓的起始点和宽高
  4. for word in words:
  5. # 筛选字符的轮廓
  6. if (word[3] > (word[2] * 1.5)) and (word[3] < (word[2] * 5.5)) and (word[2] > 10):
  7. i = i+1
  8. if word[2] < 15:
  9. splite_image = image[word[1]:word[1] + word[3], word[0]-word[2]:word[0] + word[2]*2]
  10. else:
  11. splite_image = image[word[1]:word[1] + word[3], word[0]:word[0] + word[2]]
  12. word_images.append(splite_image)
  13. print(i)
  14. print(words)
复制代码

  运行结果:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. [[2, 0, 7, 70], [12, 6, 30, 55], [15, 7, 7, 9], [46, 6, 32, 55], [83, 30, 9, 9], [96, 7, 32, 55], [132, 8, 32, 55], [167, 8, 30, 54], [202, 62, 7, 6], [203, 7, 30, 55], [245, 7, 12, 54], [266, 0, 12, 70]]
复制代码

4.5 分割效果

  1. for i,j in enumerate(word_images):
  2. plt.subplot(1,7,i+1)
  3. plt.imshow(word_images[i],cmap='gray')
  4. plt.show()
复制代码

  运行结果:

5 模板匹配

5.1 准备模板

  1. # 准备模板(template[0-9]为数字模板;)
  2. template = ['0','1','2','3','4','5','6','7','8','9',
  3. 'A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z',
  4. '藏','川','鄂','甘','赣','贵','桂','黑','沪','吉','冀','津','晋','京','辽','鲁','蒙','闽','宁',
  5. '青','琼','陕','苏','皖','湘','新','渝','豫','粤','云','浙']
  6. # 读取一个文件夹下的所有图片,输入参数是文件名,返回模板文件地址列表
  7. def read_directory(directory_name):
  8. referImg_list = []
  9. for filename in os.listdir(directory_name):
  10. referImg_list.append(directory_name + "/" + filename)
  11. return referImg_list
  12. # 获得中文模板列表(只匹配车牌的第一个字符)
  13. def get_chinese_words_list():
  14. chinese_words_list = []
  15. for i in range(34,64):
  16. #将模板存放在字典中
  17. c_word = read_directory('D:/refer1/'+ template[i])
  18. chinese_words_list.append(c_word)
  19. return chinese_words_list
  20. chinese_words_list = get_chinese_words_list()
  21. # 获得英文模板列表(只匹配车牌的第二个字符)
  22. def get_eng_words_list():
  23. eng_words_list = []
  24. for i in range(10,34):
  25. e_word = read_directory('D:/refer1/'+ template[i])
  26. eng_words_list.append(e_word)
  27. return eng_words_list
  28. eng_words_list = get_eng_words_list()
  29. # 获得英文和数字模板列表(匹配车牌后面的字符)
  30. def get_eng_num_words_list():
  31. eng_num_words_list = []
  32. for i in range(0,34):
  33. word = read_directory('D:/refer1/'+ template[i])
  34. eng_num_words_list.append(word)
  35. return eng_num_words_list
  36. eng_num_words_list = get_eng_num_words_list()
复制代码

此处需提前准备各类字符模板。

5.2 匹配结果

  1. # 获得英文和数字模板列表(匹配车牌后面的字符)
  2. def get_eng_num_words_list():
  3. eng_num_words_list = []
  4. for i in range(0,34):
  5. word = read_directory('D:/refer1/'+ template[i])
  6. eng_num_words_list.append(word)
  7. return eng_num_words_list
  8. eng_num_words_list = get_eng_num_words_list()
  9. # 读取一个模板地址与图片进行匹配,返回得分
  10. def template_score(template,image):
  11. #将模板进行格式转换
  12. template_img=cv2.imdecode(np.fromfile(template,dtype=np.uint8),1)
  13. template_img = cv2.cvtColor(template_img, cv2.COLOR_RGB2GRAY)
  14. #模板图像阈值化处理——获得黑白图
  15. ret, template_img = cv2.threshold(template_img, 0, 255, cv2.THRESH_OTSU)
  16. # height, width = template_img.shape
  17. # image_ = image.copy()
  18. # image_ = cv2.resize(image_, (width, height))
  19. image_ = image.copy()
  20. #获得待检测图片的尺寸
  21. height, width = image_.shape
  22. # 将模板resize至与图像一样大小
  23. template_img = cv2.resize(template_img, (width, height))
  24. # 模板匹配,返回匹配得分
  25. result = cv2.matchTemplate(image_, template_img, cv2.TM_CCOEFF)
  26. return result[0][0]
  27. # 对分割得到的字符逐一匹配
  28. def template_matching(word_images):
  29. results = []
  30. for index,word_image in enumerate(word_images):
  31. if index==0:
  32. best_score = []
  33. for chinese_words in chinese_words_list:
  34. score = []
  35. for chinese_word in chinese_words:
  36. result = template_score(chinese_word,word_image)
  37. score.append(result)
  38. best_score.append(max(score))
  39. i = best_score.index(max(best_score))
  40. # print(template[34+i])
  41. r = template[34+i]
  42. results.append(r)
  43. continue
  44. if index==1:
  45. best_score = []
  46. for eng_word_list in eng_words_list:
  47. score = []
  48. for eng_word in eng_word_list:
  49. result = template_score(eng_word,word_image)
  50. score.append(result)
  51. best_score.append(max(score))
  52. i = best_score.index(max(best_score))
  53. # print(template[10+i])
  54. r = template[10+i]
  55. results.append(r)
  56. continue
  57. else:
  58. best_score = []
  59. for eng_num_word_list in eng_num_words_list:
  60. score = []
  61. for eng_num_word in eng_num_word_list:
  62. result = template_score(eng_num_word,word_image)
  63. score.append(result)
  64. best_score.append(max(score))
  65. i = best_score.index(max(best_score))
  66. # print(template[i])
  67. r = template[i]
  68. results.append(r)
  69. continue
  70. return results
  71. word_images_ = word_images.copy()
  72. # 调用函数获得结果
  73. result = template_matching(word_images_)
  74. print(result)
  75. print( "".join(result))
复制代码

  运行结果:

  1. ['渝', 'B', 'F', 'U', '8', '7', '1']
  2. 渝BFU871
复制代码

“”.join(result)函数将列表转换为拼接好的字符串,方便结果显示

5.3 匹配效果展示

  1. height,weight = origin_image.shape[0:2]
  2. print(height)
  3. print(weight)
  4. image_1 = origin_image.copy()
  5. cv2.rectangle(image_1, (int(0.2*weight), int(0.75*height)), (int(weight*0.9), int(height*0.95)), (0, 255, 0), 5)
  6. #设置需要显示的字体
  7. fontpath = "font/simsun.ttc"
  8. font = ImageFont.truetype(fontpath,64)
  9. img_pil = Image.fromarray(image_1)
  10. draw = ImageDraw.Draw(img_pil)
  11. #绘制文字信息
  12. draw.text((int(0.2*weight)+25, int(0.75*height)), "".join(result), font = font, fill = (255, 255, 0))
  13. bk_img = np.array(img_pil)
  14. print(result)
  15. print( "".join(result))
  16. plt_show0(bk_img)
复制代码

  运行结果:

6完整代码

  1. # 导入所需模块
  2. import cv2
  3. from matplotlib import pyplot as plt
  4. import os
  5. import numpy as np
  6. from PIL import ImageFont, ImageDraw, Image
  7. # plt显示彩色图片
  8. def plt_show0(img):
  9. b,g,r = cv2.split(img)
  10. img = cv2.merge([r, g, b])
  11. plt.imshow(img)
  12. plt.show()
  13. # plt显示灰度图片
  14. def plt_show(img):
  15. plt.imshow(img,cmap='gray')
  16. plt.show()
  17. # 图像去噪灰度处理
  18. def gray_guss(image):
  19. image = cv2.GaussianBlur(image, (3, 3), 0)
  20. gray_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
  21. return gray_image
  22. # 读取待检测图片
  23. origin_image = cv2.imread('D:/image/car3.jpg')
  24. # 复制一张图片,在复制图上进行图像操作,保留原图
  25. image = origin_image.copy()
  26. # 图像去噪灰度处理
  27. gray_image = gray_guss(image)
  28. # x方向上的边缘检测(增强边缘信息)
  29. Sobel_x = cv2.Sobel(gray_image, cv2.CV_16S, 1, 0)
  30. absX = cv2.convertScaleAbs(Sobel_x)
  31. image = absX
  32. # 图像阈值化操作——获得二值化图
  33. ret, image = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)
  34. # 显示灰度图像
  35. plt_show(image)
  36. # 形态学(从图像中提取对表达和描绘区域形状有意义的图像分量)——闭操作
  37. kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 10))
  38. image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernelX,iterations = 1)
  39. # 显示灰度图像
  40. plt_show(image)
  41. # 腐蚀(erode)和膨胀(dilate)
  42. kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 1))
  43. kernelY = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 20))
  44. #x方向进行闭操作(抑制暗细节)
  45. image = cv2.dilate(image, kernelX)
  46. image = cv2.erode(image, kernelX)
  47. #y方向的开操作
  48. image = cv2.erode(image, kernelY)
  49. image = cv2.dilate(image, kernelY)
  50. # 中值滤波(去噪)
  51. image = cv2.medianBlur(image, 21)
  52. # 显示灰度图像
  53. plt_show(image)
  54. # 获得轮廓
  55. contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  56. for item in contours:
  57. rect = cv2.boundingRect(item)
  58. x = rect[0]
  59. y = rect[1]
  60. weight = rect[2]
  61. height = rect[3]
  62. # 根据轮廓的形状特点,确定车牌的轮廓位置并截取图像
  63. if (weight > (height * 3)) and (weight < (height * 4.5)):
  64. image = origin_image[y:y + height, x:x + weight]
  65. plt_show(image)
  66. #车牌字符分割
  67. # 图像去噪灰度处理
  68. gray_image = gray_guss(image)
  69. # 图像阈值化操作——获得二值化图
  70. ret, image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_OTSU)
  71. plt_show(image)
  72. #膨胀操作
  73. kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (4, 4))
  74. image = cv2.dilate(image, kernel)
  75. plt_show(image)
  76. # 查找轮廓
  77. contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  78. words = []
  79. word_images = []
  80. #对所有轮廓逐一操作
  81. for item in contours:
  82. word = []
  83. rect = cv2.boundingRect(item)
  84. x = rect[0]
  85. y = rect[1]
  86. weight = rect[2]
  87. height = rect[3]
  88. word.append(x)
  89. word.append(y)
  90. word.append(weight)
  91. word.append(height)
  92. words.append(word)
  93. # 排序,车牌号有顺序。words是一个嵌套列表
  94. words = sorted(words,key=lambda s:s[0],reverse=False)
  95. i = 0
  96. #word中存放轮廓的起始点和宽高
  97. for word in words:
  98. # 筛选字符的轮廓
  99. if (word[3] > (word[2] * 1.5)) and (word[3] < (word[2] * 5.5)) and (word[2] > 10):
  100. i = i+1
  101. if word[2] < 15:
  102. splite_image = image[word[1]:word[1] + word[3], word[0]-word[2]:word[0] + word[2]*2]
  103. else:
  104. splite_image = image[word[1]:word[1] + word[3], word[0]:word[0] + word[2]]
  105. word_images.append(splite_image)
  106. print(i)
  107. print(words)
  108. for i,j in enumerate(word_images):
  109. plt.subplot(1,7,i+1)
  110. plt.imshow(word_images[i],cmap='gray')
  111. plt.show()
  112. #模版匹配
  113. # 准备模板(template[0-9]为数字模板;)
  114. template = ['0','1','2','3','4','5','6','7','8','9',
  115. 'A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z',
  116. '藏','川','鄂','甘','赣','贵','桂','黑','沪','吉','冀','津','晋','京','辽','鲁','蒙','闽','宁',
  117. '青','琼','陕','苏','皖','湘','新','渝','豫','粤','云','浙']
  118. # 读取一个文件夹下的所有图片,输入参数是文件名,返回模板文件地址列表
  119. def read_directory(directory_name):
  120. referImg_list = []
  121. for filename in os.listdir(directory_name):
  122. referImg_list.append(directory_name + "/" + filename)
  123. return referImg_list
  124. # 获得中文模板列表(只匹配车牌的第一个字符)
  125. def get_chinese_words_list():
  126. chinese_words_list = []
  127. for i in range(34,64):
  128. #将模板存放在字典中
  129. c_word = read_directory('D:/refer1/'+ template[i])
  130. chinese_words_list.append(c_word)
  131. return chinese_words_list
  132. chinese_words_list = get_chinese_words_list()
  133. # 获得英文模板列表(只匹配车牌的第二个字符)
  134. def get_eng_words_list():
  135. eng_words_list = []
  136. for i in range(10,34):
  137. e_word = read_directory('D:/refer1/'+ template[i])
  138. eng_words_list.append(e_word)
  139. return eng_words_list
  140. eng_words_list = get_eng_words_list()
  141. # 获得英文和数字模板列表(匹配车牌后面的字符)
  142. def get_eng_num_words_list():
  143. eng_num_words_list = []
  144. for i in range(0,34):
  145. word = read_directory('D:/refer1/'+ template[i])
  146. eng_num_words_list.append(word)
  147. return eng_num_words_list
  148. eng_num_words_list = get_eng_num_words_list()
  149. # 读取一个模板地址与图片进行匹配,返回得分
  150. def template_score(template,image):
  151. #将模板进行格式转换
  152. template_img=cv2.imdecode(np.fromfile(template,dtype=np.uint8),1)
  153. template_img = cv2.cvtColor(template_img, cv2.COLOR_RGB2GRAY)
  154. #模板图像阈值化处理——获得黑白图
  155. ret, template_img = cv2.threshold(template_img, 0, 255, cv2.THRESH_OTSU)
  156. # height, width = template_img.shape
  157. # image_ = image.copy()
  158. # image_ = cv2.resize(image_, (width, height))
  159. image_ = image.copy()
  160. #获得待检测图片的尺寸
  161. height, width = image_.shape
  162. # 将模板resize至与图像一样大小
  163. template_img = cv2.resize(template_img, (width, height))
  164. # 模板匹配,返回匹配得分
  165. result = cv2.matchTemplate(image_, template_img, cv2.TM_CCOEFF)
  166. return result[0][0]
  167. # 对分割得到的字符逐一匹配
  168. def template_matching(word_images):
  169. results = []
  170. for index,word_image in enumerate(word_images):
  171. if index==0:
  172. best_score = []
  173. for chinese_words in chinese_words_list:
  174. score = []
  175. for chinese_word in chinese_words:
  176. result = template_score(chinese_word,word_image)
  177. score.append(result)
  178. best_score.append(max(score))
  179. i = best_score.index(max(best_score))
  180. # print(template[34+i])
  181. r = template[34+i]
  182. results.append(r)
  183. continue
  184. if index==1:
  185. best_score = []
  186. for eng_word_list in eng_words_list:
  187. score = []
  188. for eng_word in eng_word_list:
  189. result = template_score(eng_word,word_image)
  190. score.append(result)
  191. best_score.append(max(score))
  192. i = best_score.index(max(best_score))
  193. # print(template[10+i])
  194. r = template[10+i]
  195. results.append(r)
  196. continue
  197. else:
  198. best_score = []
  199. for eng_num_word_list in eng_num_words_list:
  200. score = []
  201. for eng_num_word in eng_num_word_list:
  202. result = template_score(eng_num_word,word_image)
  203. score.append(result)
  204. best_score.append(max(score))
  205. i = best_score.index(max(best_score))
  206. # print(template[i])
  207. r = template[i]
  208. results.append(r)
  209. continue
  210. return results
  211. word_images_ = word_images.copy()
  212. # 调用函数获得结果
  213. result = template_matching(word_images_)
  214. print(result)
  215. # "".join(result)函数将列表转换为拼接好的字符串,方便结果显示
  216. print( "".join(result))
  217. height,weight = origin_image.shape[0:2]
  218. print(height)
  219. print(weight)
  220. image_1 = origin_image.copy()
  221. cv2.rectangle(image_1, (int(0.2*weight), int(0.75*height)), (int(weight*0.9), int(height*0.95)), (0, 255, 0), 5)
  222. #设置需要显示的字体
  223. fontpath = "font/simsun.ttc"
  224. font = ImageFont.truetype(fontpath,64)
  225. img_pil = Image.fromarray(image_1)
  226. draw = ImageDraw.Draw(img_pil)
  227. #绘制文字信息
  228. draw.text((int(0.2*weight)+25, int(0.75*height)), "".join(result), font = font, fill = (255, 255, 0))
  229. bk_img = np.array(img_pil)
  230. print(result)
  231. print( "".join(result))
  232. plt_show0(bk_img)
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4008
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行