目录
一、形态学车牌提取(简单:单情景)
?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)
?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)
?
3、自适应二值化
注:threshold函数返回两个参数,第二个参数才是二进制图像!!!?
# 3、自适应二值化
ret, binary = cv.threshold(absY, 0, 255, cv.THRESH_OTSU)
cv.imshow('binary', binary)
4、闭运算处理,把图像闭合、揉团,使图像区域化
闭运算处理,图像区域化,便于找到车牌区域,进而得到轮廓
测试多组图片,发现(17, 5)的卷积核比较好,能达到目的。?
# 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)
?
横向处理完之后,继续对纵向进行处理。?
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)
?
?如上图,得到了想要的结果,基本只剩下几个轮廓了,且车牌区域保留完好,不影响车牌的定位。
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)
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]<rect[3]*5:
# 截取车牌并显示
img = img[rect[1]:(rect[1]+rect[3]), rect[0]:(rect[0]+rect[2])]
cv.imshow('license plate', img)
?
二、形态学车牌提取(优化:多情景)
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)
?
3、Sobel算子提取Y方向边缘
纵向化车牌区域,使车牌更加明显。?
# 3、Sobel算子提取y方向边缘(揉成一坨)
y = cv.Sobel(tophat, cv.CV_16S, 1, 0)
absY = cv.convertScaleAbs(y)
cv.imshow('absY', absY)
?
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)
分割前:?
可以看出来,这写杂质已经完全融入车牌中了,后续想要分离它们就十分困难了。
?分割后:
虽然也有少部分的连接,但不是之前的那种大块连接,相对 比较好去除。
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、纵方向: 腐蚀+膨胀
分割上下距离比较近的物体。
?7-2、横方向:膨胀+腐蚀
进一步连接横向的物体 (继上面的闭运算,再次连接车牌的内容)
?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)
?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)
?
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]<rect[3]*6.5:
# 截取车牌并显示
print(rect)
img = img[(rect[1]-5):(rect[1]+rect[3]+5), (rect[0]-5):(rect[0]+rect[2]+5)] #高,宽
try:
cv.imshow('license plate%d-%d' % (count, i), img)
# cv.imwrite('img%d-%d.jpg'%(count, i), img)
i += 1
except:
pass
cv.waitKey(0)
中远距离:
正常车牌成功率:100%(测试了11例,均成功)
倾斜车牌成功率:67%(测试2例,,3车牌,最小的车牌识别失败)(但是处理不是很好)
近距离车牌成功率:0%(测试2例,均失败)
?
近距离:
(近距离成功率50%,因为只做了2个)
代码
# 车牌识别
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]<rect[3]*7:
# 截取车牌并显示
print(rect)
img = img[(rect[1]-5):(rect[1]+rect[3]+5), (rect[0]-5):(rect[0]+rect[2]+5)] #高,宽
try:
cv.imshow('license plate%d-%d' % (count, i), img)
cv.imwrite('car_licenses/img%d-%d.jpg'%(count, i), img)
i += 1
except:
pass
cv.waitKey(0)
if __name__ == '__main__':
global count
count=0
# 遍历文件夹中的每张图片(车)
for car in os.listdir('cars'):
# 1、获取路径
path = 'cars/'+'car'+str(count)+'.jpg'
# 2、获取图片
img = cv.imread(path)
# 3、定位车牌
Morph_Distinguish(img) #形态学提取车牌
count += 1
cv.waitKey(0)
?参考资料
https://www.bilibili.com/video/BV1yg4y187kU?from=search&seid=14407715114627193868?
https://blog.csdn.net/PYH1009/article/details/102951768?spm=1001.2014.3001.5501
|