#|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)
文章图片
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]
文章图片
二、形态学车牌提取(优化:多情景) 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]
中远距离:
正常车牌成功率: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]
代买仅供参考借鉴,目前只能提取一定距离的汽车车牌,太远或太近都太不行,也没有做倾斜处理,很难应用到实际生活中,实际生活中情景复杂的多,有什么建议或者批评可以尽管说出来。(后期可能会在此加入倾斜处理)
参考资料 【项目实战】基于OpenCV模板匹配的车牌识别项目_哔哩哔哩_bilibili
【#|OpenCV(项目)车牌识别1 -- 车牌提取(形态学)】基于python+OpenCV的车牌号码识别_PYH1009的博客-CSDN博客_基于python的车牌识别系统
推荐阅读
- opencv|opencv C++模板匹配的简单实现
- Java|Java OpenCV图像处理之SIFT角点检测详解
- 17|17 关山松 第二课作业#公众号项目# D20
- RxJava|RxJava 在Android项目中的使用(一)
- Hacking|Hacking with iOS: SwiftUI Edition - SnowSeeker 项目(一)
- 靠QQ月入上万灰色暴利偏门的项目
- spring|spring boot项目启动websocket
- vuex|vuex 基础结构
- 区块链开发平台(以太坊)
- 如何在手机上查看测试vue-cli构建的项目