#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)

目录
一、形态学车牌提取(简单:单情景)
1、读取图片,转灰度图
2、提取轮廓(Sobel算子提取y方向边缘)
3、自适应二值化
4、闭运算处理,把图像闭合、揉团,使图像区域化
5、腐蚀/膨胀去噪得到车牌区域
5-1、横向腐蚀、膨胀
5-2、纵向腐蚀、膨胀
6、获取外轮廓
6-1、得到轮廓
6-2、画出轮廓并显示
7、截取得到车牌
二、形态学车牌提取(优化:多情景)
1、转灰度图
2、顶帽运算
3、Sobel算子提取Y方向边缘
4、二值化图像
5、开运算分割(纵向分割)
6、闭运算合并
7、横/纵方向腐蚀/膨胀
7-1、纵方向: 腐蚀+膨胀
7-2、横方向:膨胀+腐蚀
8、腐蚀膨胀:去噪
9、获取外轮廓
10、 根据车牌特征找到车牌轮廓
代码
参考资料
一、形态学车牌提取(简单:单情景) (单图片还不错,但多图片不准确)
1、读取图片,转灰度图

# 1、读取图片,转灰度图 img = cv.imread('Resource/car.jpg') gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY) cv.imshow('gray', gray)

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


2、提取轮廓(Sobel算子提取y方向边缘) 为什么对Y方向取边缘:让图像变“瘦”,便于把车牌揉成一团。
# 2、提取轮廓(Sobel算子提取y方向) y = cv.Sobel(gray, cv.CV_16S, 1,0) # 注:对x/y微分和得到x/y方向图像相反要得到x/y方向边缘,就要求y/x方向的微分。 absY = cv.convertScaleAbs(y) cv.imshow('Y', absY)

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


3、自适应二值化 注:threshold函数返回两个参数,第二个参数才是二进制图像!!!
# 3、自适应二值化 ret, binary = cv.threshold(absY, 0, 255, cv.THRESH_OTSU) cv.imshow('binary', binary)

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


4、闭运算处理,把图像闭合、揉团,使图像区域化
闭运算处理,图像区域化,便于找到车牌区域,进而得到轮廓

测试多组图片,发现(17, 5)的卷积核比较好,能达到目的。
#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片

# 4、闭运算处理,把图像闭合、揉团,使图像区域化,便于找到车牌区域,进而得到轮廓 kernel = cv.getStructuringElement(cv.MORPH_RECT, (17,5)) print('kernel= \n', kernel) close = cv.morphologyEx(binary, cv.MORPH_CLOSE, kernel) cv.imshow('close', close)

(这里采用矩形卷积核处理。)

5、腐蚀/膨胀去噪得到车牌区域 上面虽然得到了二进制图像,且得到了大致区域,但是噪声(杂质)仍然太多,下面通过腐蚀、膨胀去噪。
5-1、横向腐蚀、膨胀
# 5-1、水平方向腐蚀/膨胀 erode = cv.morphologyEx(close, cv.MORPH_ERODE, kernel_x) cv.imshow('erode_x', erode) dilate = cv.morphologyEx(erode, cv.MORPH_DILATE, kernel_x) cv.imshow('dilate_x', dilate)

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片

横向处理完之后,继续对纵向进行处理。

5-2、纵向腐蚀、膨胀
# 5-2、竖直方向腐蚀/膨胀 erode = cv.morphologyEx(dilate, cv.MORPH_ERODE, kernel_y) cv.imshow('erode_y', erode) dilate = cv.morphologyEx(erode, cv.MORPH_DILATE, kernel_y) cv.imshow('dilate_y', dilate)

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片

如上图,得到了想要的结果,基本只剩下几个轮廓了,且车牌区域保留完好,不影响车牌的定位。

6、获取外轮廓 这里想先把包括车牌轮廓的外轮廓一起显示。
6-1、得到轮廓
# 6-1、得到轮廓 contours, hierarchy = cv.findContours(dilate_y, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)


6-2、画出轮廓并显示
# 6-2、画出轮廓 cv.drawContours(img_copy, contours, -1, (255,0,255), 2) cv.imshow('Contours', img_copy)

获取轮廓代码及效果:
# 6、获取外轮廓 img_copy = img.copy() # 6-1、得到轮廓 contours, hierarchy = cv.findContours(dilate_y, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) # 6-2、画出轮廓并显示 cv.drawContours(img_copy, contours, -1, (255,0,255), 2) cv.imshow('Contours', img_copy)

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


7、截取得到车牌 首先判断车牌的特征(比如宽:高一般在3~4),然后根据这个特征进行判断,只保留符合特征的图片(车牌)。
# 7、遍历所有轮廓,找到车牌轮廓 for contour in contours: # 7-1、得到矩形区域:左顶点坐标、宽和高 rect = cv.boundingRect(contour) # 7-2、判断宽高比例是否符合车牌标准,截取符合图片 if rect[2]>rect[3]*3 and rect[2]

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片



二、形态学车牌提取(优化:多情景) 1、转灰度图
gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY) cv.imshow('gray', gray)


2、顶帽运算 把较亮部分提取出来,使得车牌区域更加明显。
# 2、顶帽运算 # gray = cv.equalizeHist(gray) kernel = cv.getStructuringElement(cv.MORPH_RECT, (17,17)) tophat = cv.morphologyEx(gray, cv.MORPH_TOPHAT, kernel) cv.imshow('tophat', tophat)

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


3、Sobel算子提取Y方向边缘 纵向化车牌区域,使车牌更加明显。
# 3、Sobel算子提取y方向边缘(揉成一坨) y = cv.Sobel(tophat, cv.CV_16S, 1,0) absY = cv.convertScaleAbs(y) cv.imshow('absY', absY)

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


4、二值化图像 根据自己的实际情况选取阈值,阈值越大,变成白色(255)的门槛越多,则白色部分越少。 (这里阈值比较小,但阈值大也有不少利弊)
# 4、自适应二值化(阈值自己可调) ret, binary = cv.threshold(absY, 75, 255, cv.THRESH_BINARY) cv.imshow('binary', binary)


5、开运算分割(纵向分割) 为了防止后面的闭运算错误连接外部的杂质,需要先用开运算把它们分隔开。
# 5、开运算处理(纵向去噪,分隔) kernel = cv.getStructuringElement(cv.MORPH_RECT, (1, 15)) Open = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel) cv.imshow('Open', Open)

分割前:
可以看出来,这写杂质已经完全融入车牌中了,后续想要分离它们就十分困难了。
#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片

分割后:
虽然也有少部分的连接,但不是之前的那种大块连接,相对 比较好去除。
#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


6、闭运算合并
# 6、闭运算合并,把图像闭合、揉团,使图像区域化,便于找到车牌区域,进而得到轮廓 kernel = cv.getStructuringElement(cv.MORPH_RECT, (41, 15)) close = cv.morphologyEx(Open, cv.MORPH_CLOSE, kernel) cv.imshow('close', close)

效果上面有。

7、横/纵方向腐蚀/膨胀
# 7、膨胀/腐蚀(去噪得到车牌区域) # 中远距离车牌识别 kernel_x = cv.getStructuringElement(cv.MORPH_RECT, (25, 7)) kernel_y = cv.getStructuringElement(cv.MORPH_RECT, (1, 11)) # 近距离车牌识别 # kernel_x = cv.getStructuringElement(cv.MORPH_RECT, (91, 31)) # kernel_y = cv.getStructuringElement(cv.MORPH_RECT, (1, 31)) # 7-1、腐蚀、膨胀(去噪) erode_y = cv.morphologyEx(close, cv.MORPH_ERODE, kernel_y) cv.imshow('erode_y', erode_y) dilate_y = cv.morphologyEx(erode_y, cv.MORPH_DILATE, kernel_y) cv.imshow('dilate_y', dilate_y) # 7-1、膨胀、腐蚀(连接)(二次缝合) dilate_x = cv.morphologyEx(dilate_y, cv.MORPH_DILATE, kernel_x) cv.imshow('dilate_x', dilate_x) erode_x = cv.morphologyEx(dilate_x, cv.MORPH_ERODE, kernel_x) cv.imshow('erode_x', erode_x)

7-1、纵方向: 腐蚀+膨胀
分割上下距离比较近的物体。
#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片

7-2、横方向:膨胀+腐蚀
进一步连接横向的物体 (继上面的闭运算,再次连接车牌的内容)
#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


8、腐蚀膨胀:去噪
# 8、腐蚀、膨胀:去噪 kernel_e = cv.getStructuringElement(cv.MORPH_RECT, (25, 11)) erode = cv.morphologyEx(erode_x, cv.MORPH_ERODE, kernel_e) cv.imshow('erode', erode) kernel_d = cv.getStructuringElement(cv.MORPH_RECT, (25, 13)) dilate = cv.morphologyEx(erode, cv.MORPH_DILATE, kernel_d) cv.imshow('dilate', dilate)

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


9、获取外轮廓
# 9、获取外轮廓 img_copy = img.copy() # 9-1、得到轮廓 contours, hierarchy = cv.findContours(dilate, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) # 9-2、画出轮廓并显示 cv.drawContours(img_copy, contours, -1, (255, 0, 255), 2) cv.imshow('Contours', img_copy)

#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


10、 根据车牌特征找到车牌轮廓 车牌宽高比一般为3~4之间,但我这里首先是图比较多,有些图附带的杂质没有清除干净,所以就设置在3~6.5之间。
# 10、遍历所有轮廓,找到车牌轮廓 i = 0 for contour in contours: # 10-1、得到矩形区域:左顶点坐标、宽和高 rect = cv.boundingRect(contour) # 10-2、判断宽高比例是否符合车牌标准,截取符合图片 if rect[2]>rect[3]*3 and rect[2]

中远距离:
正常车牌成功率:100%(测试了11例,均成功)
倾斜车牌成功率:67%(测试2例,,3车牌,最小的车牌识别失败)(但是处理不是很好)
近距离车牌成功率:0%(测试2例,均失败)
#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


近距离:
(近距离成功率50%,因为只做了2个)
#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)
文章图片


代码
# 车牌识别 import cv2 as cv import numpy as np import os# 提取车牌(形态学) def Morph_Distinguish(img): # 1、转灰度图 gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY) cv.imshow('gray', gray)# 2、顶帽运算 # gray = cv.equalizeHist(gray) kernel = cv.getStructuringElement(cv.MORPH_RECT, (17,17)) tophat = cv.morphologyEx(gray, cv.MORPH_TOPHAT, kernel) cv.imshow('tophat', tophat)# 3、Sobel算子提取y方向边缘(揉成一坨) y = cv.Sobel(tophat, cv.CV_16S, 1,0) absY = cv.convertScaleAbs(y) cv.imshow('absY', absY)# 4、自适应二值化(阈值自己可调) ret, binary = cv.threshold(absY, 75, 255, cv.THRESH_BINARY) cv.imshow('binary', binary)# 5、开运算分割(纵向去噪,分隔) kernel = cv.getStructuringElement(cv.MORPH_RECT, (1, 15)) Open = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel) cv.imshow('Open', Open)# 6、闭运算合并,把图像闭合、揉团,使图像区域化,便于找到车牌区域,进而得到轮廓 kernel = cv.getStructuringElement(cv.MORPH_RECT, (41, 15)) close = cv.morphologyEx(Open, cv.MORPH_CLOSE, kernel) cv.imshow('close', close)# 7、膨胀/腐蚀(去噪得到车牌区域) # 中远距离车牌识别 kernel_x = cv.getStructuringElement(cv.MORPH_RECT, (25, 7)) kernel_y = cv.getStructuringElement(cv.MORPH_RECT, (1, 11)) # 近距离车牌识别 # kernel_x = cv.getStructuringElement(cv.MORPH_RECT, (79, 15)) # kernel_y = cv.getStructuringElement(cv.MORPH_RECT, (1, 31)) # 7-1、腐蚀、膨胀(去噪) erode_y = cv.morphologyEx(close, cv.MORPH_ERODE, kernel_y) cv.imshow('erode_y', erode_y) dilate_y = cv.morphologyEx(erode_y, cv.MORPH_DILATE, kernel_y) cv.imshow('dilate_y', dilate_y) # 7-1、膨胀、腐蚀(连接)(二次缝合) dilate_x = cv.morphologyEx(dilate_y, cv.MORPH_DILATE, kernel_x) cv.imshow('dilate_x', dilate_x) erode_x = cv.morphologyEx(dilate_x, cv.MORPH_ERODE, kernel_x) cv.imshow('erode_x', erode_x)# 8、腐蚀、膨胀:去噪 kernel_e = cv.getStructuringElement(cv.MORPH_RECT, (25, 9)) erode = cv.morphologyEx(erode_x, cv.MORPH_ERODE, kernel_e) cv.imshow('erode', erode) kernel_d = cv.getStructuringElement(cv.MORPH_RECT, (25, 11)) dilate = cv.morphologyEx(erode, cv.MORPH_DILATE, kernel_d) cv.imshow('dilate', dilate)# 9、获取外轮廓 img_copy = img.copy() # 9-1、得到轮廓 contours, hierarchy = cv.findContours(dilate, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) # 9-2、画出轮廓并显示 cv.drawContours(img_copy, contours, -1, (255, 0, 255), 2) cv.imshow('Contours', img_copy)# 10、遍历所有轮廓,找到车牌轮廓 i = 0 for contour in contours: # 10-1、得到矩形区域:左顶点坐标、宽和高 rect = cv.boundingRect(contour) # 10-2、判断宽高比例是否符合车牌标准,截取符合图片 if rect[2]>rect[3]*3 and rect[2]


代买仅供参考借鉴,目前只能提取一定距离的汽车车牌,太远或太近都太不行,也没有做倾斜处理,很难应用到实际生活中,实际生活中情景复杂的多,有什么建议或者批评可以尽管说出来。(后期可能会在此加入倾斜处理)



参考资料 【项目实战】基于OpenCV模板匹配的车牌识别项目_哔哩哔哩_bilibili
【#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)】基于python+OpenCV的车牌号码识别_PYH1009的博客-CSDN博客_基于python的车牌识别系统

    推荐阅读