python opencv识别蓝牌车牌号 之 取出车牌号 (1/3)
概述
车牌识别是计算机视频图像识别技术在车辆牌照识别中的一种应用,通常来讲如果结合opencv进行车牌识别主要分为四个大步骤,分别为:
- 图像采集
- 车牌定位
- 分割车牌字符
- 字符识别
当然,如果结合了机器学习可能步骤会变得更为精简,但是从opencv基础方法开始也不失为一种学习进步,此案例仅仅从蓝牌车牌入手,作为学习交流用,暂不打算花时间研究绿牌、黄牌车等车牌识别。
图像采集我们直接掠过,现在假设我们已经完成了图像采集,得到了包含车牌的图片。我们直接从车牌定位开始。
*** 文中的车辆、车牌均来自网络,与现实中任何事务无关。***
取出车牌
现在我们目的是从一张图片中识别出车牌位置,并将车牌位置的矩形单独取出来形成一张图片。为了达到这样的目的我们需要完成以下几步:
- 尺寸调整:将较大尺寸图片处理成较小尺寸图片,避免图像处理过慢
- 色域分析:取出蓝色色彩范围
- 色域叠加:将色域分析出的蓝色范围在图片中进行叠加,更加方便识别
- 调整为灰度图像:牢记我们最终目的是进行车牌识别,调整为灰度图像更方便后续使用
- 高斯过滤:主要过滤噪点等干扰
- 形态学处理:通常使用一次侵蚀和膨胀作为基本操作来执行形态转换,以方便后面的图像边缘检测
- 二值化:将灰度图像的某个值以下的全部设置为0,该值以上的全部设置为255,更加方便后续的图片边缘检测
- 边缘检测:检测图片中的边缘
- 特征找牌:根据车牌宽高比的特征取出车牌
当然,为什么要进行以上几步才找出车牌这个仁者见仁智者见智,就类似如果要吃饭首先要淘米,然后放入锅中煮一样,总而言之有更好更简洁高效的方式也是可以的。
尺寸调整
刚才说到,尺寸调整的目的是避免因图片尺寸过大而引起计算机处理速度过慢,在这里需要重点注意的是在调整尺寸的时候需要注意保持好图片的宽高比,假设我们需要做的是将图片的宽度变为500px,并同时假设我们手中的图片是19201080px,那么我们最终处理好的图片应该是889500,即500/1080*1920
以下代码来自网络,在python opencv中保持宽高比地处理图片:
点击查看代码
def resize_keep_aspectratio(image_src, dst_size):
src_h, src_w = image_src.shape[:2]
# print(src_h, src_w)
dst_h, dst_w = dst_size
# 判断应该按哪个边做等比缩放
h = dst_w * (float(src_h) / src_w) # 按照w做等比缩放
w = dst_h * (float(src_w) / src_h) # 按照h做等比缩放
h = int(h)
w = int(w)
if h <= dst_h:
image_dst = cv2.resize(image_src, (dst_w, int(h)))
else:
image_dst = cv2.resize(image_src, (int(w), dst_h))
h_, w_ = image_dst.shape[:2]
return image_dst
我们使用img = self.resize_keep_aspectratio(image, [500, 500])即可进行调用,其中虽然传递为[500,500],实际上改方法会根据宽高比自动调整。
色域分析&叠加
色域分析的关键函数是调用cv2.inRange方法,围绕该方法我们写出以下代码:
点击查看代码
# hsv提取蓝色部分
def hsv_color_find(img):
img_copy = img.copy()
"""
提取图中的蓝色部分 hsv范围可以自行优化
"""
hsv = cv2.cvtColor(img_copy, cv2.COLOR_BGR2HSV)
low_hsv = np.array([100, 80, 80])
high_hsv = np.array([124, 255, 255])
# 设置HSV的阈值
mask = cv2.inRange(hsv, lowerb=low_hsv, upperb=high_hsv)
cv2.imshow("hsv_color_find", mask)
# 将掩膜与图像层逐像素相加
res = cv2.bitwise_and(img_copy, img_copy, mask=mask)
cv2.imshow("hsv_color_find2", res)
return res
其中low_hsv以及high_hsv是根据经验设定的在hsv色域取出蓝色的值,最终效果如下:
原图:
进行色域分析后的图:
进行色域分析后进行叠加的图:
调整为灰度图像
点击查看代码
# RGB->灰度
gray_img = cv2.cvtColor(img_copy, cv2.COLOR_BGR2GRAY)
高斯过滤
点击查看代码
gray_img_ = cv2.GaussianBlur(gray_img, (5, 5), 0, 0, cv2.BORDER_DEFAULT)
其中(5, 5)也是经验值,可以自行调整
经过灰度处理和高斯过滤后结果如下:
形态学处理
点击查看代码
kernel = np.ones((23, 23), np.uint8)
# 使用侵蚀和膨胀作为基本操作来执行高级形态转换。任何操作都可以就地完成.在多通道图像的情况下,每个通道都是独立处理的.
img_opening = cv2.morphologyEx(gray_img, cv2.MORPH_OPEN, kernel)
# 计算两个数组的加权和
img_opening = cv2.addWeighted(gray_img, 1, img_opening, -1, 0)
进行形态学处理的主要方法是cv2.morphologyEx,里面包括了两个重要的参数,对于这两个重要的参数可以通过搜索引擎得知其用途。
处理过后效果如下:
二值化
ret2, img_thresh2 = cv2.threshold(img_opening, 0, 255, cv2.THRESH_BINARY)
效果如下:
边缘检测
点击查看代码
# # 使用开运算和闭运算让图像边缘成为一个整体
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
img_edge1 = cv2.morphologyEx(img_edge, cv2.MORPH_CLOSE, kernel)
img_edge2 = cv2.morphologyEx(img_edge1, cv2.MORPH_OPEN, kernel)
img_edge3 = cv2.morphologyEx(img_thresh2, cv2.MORPH_CLOSE, kernel)
img_edge4 = cv2.morphologyEx(img_edge3, cv2.MORPH_CLOSE, kernel)
contours, hierarchy = cv2.findContours(img_edge4, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
真正检测边缘的代码实际上就是
cv2.findContours
但是在真正进行检测的时候还做了几次开运算和闭运算,这样可以使碎片化的二值化图像形成一个整体,以便后续进行整体识别。
识别过后针对边缘画出的边缘图像如下:
根据车牌宽高比的特征取出车牌
从上一步得知,我们进行边缘检测得到的边缘多达9个,我们需要从这些边缘中找出车牌号的那块,OpenCV自带的cv2.contourArea()函数可以实现计算点集(轮廓)所围区域的面积,cv2.minAreaRect()函数可以计算出点集的最小外包旋转矩形,cv2.boxPoints()函数可以根据旋转矩形的中心的坐标、尺寸和旋转角度,计算出旋转矩形的四个顶点。
我们可以通过测量知道中国的蓝牌和黑牌是440×140,黄牌前牌尺寸同,后牌为440×220;摩托车及轻便摩托车前牌是220×95,后牌是220×140。我们可以通如下代码筛选:
点击查看代码
## 先筛选面积较大的地方
for contour in contours:
if cv2.contourArea(contour) > 2000:
temp_contours.append(contour)
for temp_contour in temp_contours:
rect_tupple = cv2.minAreaRect(temp_contour)
rect_width, rect_height = rect_tupple[1]
if rect_width < rect_height:
rect_width, rect_height = rect_height, rect_width
aspect_ratio = rect_width / rect_height
if aspect_ratio > 1.5 and aspect_ratio < 4.65: #此处可根据具体的情况针对比例进行具体的调整
car_plate1.append(temp_contour)
rect_vertices = cv2.boxPoints(rect_tupple)
#最后进行矩阵和投影变换,将斜着的矩形摆正
rect = cv2.minAreaRect(car_plate)
# 计算最小区域的坐标
box = cv2.boxPoints(rect)
wh = np.int0(rect[1])
# 变换矩阵
if 0<rect[2]<=45:
mat = cv2.getPerspectiveTransform(np.float32([[box[0][0], box[0][1]], [box[1][0], box[1][1]], [box[2][0], box[2][1]], [box[3][0], box[3][1]]]),np.float32([[0, wh[1]], [0, 0], [wh[0], 0], [wh[0], wh[1]]]))
# 投影变换
lic = cv2.warpPerspective(img, mat, (wh[0], wh[1]))
if 45 < rect[2] <= 90:
mat = cv2.getPerspectiveTransform(np.float32(
[[box[0][0], box[0][1]], [box[1][0], box[1][1]], [box[2][0], box[2][1]],
[box[3][0], box[3][1]]]), np.float32([[0, 0], [wh[1], 0], [wh[1], wh[0]], [0, wh[0]]]))
# 投影变换
lic = cv2.warpPerspective(img, mat, (wh[1], wh[0]))
cv2.imshow("card_img", lic)
得到的结果如下:
至此,我们完成了车牌的提取,得到这张图片后我们可以即可以进行后续的操作!
python opencv识别蓝牌车牌号 之 取出车牌号 (1/3)的更多相关文章
- Android OpenCV集成摄像头图片动态识别车牌号
最近两天开发一个使用OpenCV集成的一个识别车牌号的项目,困难重重,总结一下相关经验,以及开发注意事项: 一.开发环境: Android Studio 个人版本 3.1.4 NDK下载:14b CM ...
- 基于TensorFlow的车牌号识别系统
简介 过去几周我一直在涉足深度学习领域,尤其是卷积神经网络模型.最近,谷歌围绕街景多位数字识别技术发布了一篇不错的paper.该文章描述了一个用于提取街景门牌号的单个端到端神经网络系统.然后,作者阐述 ...
- Ubuntu下OpenCV不能被某个python版本识别
Ubuntu下OpenCV不能被某个python版本识别 Solution: 可以进入相应版本的python,查看该python的path: python import sys print(sys.p ...
- python的车牌号的检测
自己总结一下,从网上找到的关于车牌号的识别的一些博文.https://www.jianshu.com/p/fcfbd3131b84 https://www.cnblogs.com/do-hardwor ...
- 转载:使用 OpenCV 识别 QRCode
原文链接:http://coolshell.cn/articles/10590.html#jtss-tsina 识别二维码的项目数不胜数,每次都是开箱即用,方便得很. 这次想用 OpenCV 从零识别 ...
- 搭建基于python +opencv+Beautifulsoup+Neurolab机器学习平台
搭建基于python +opencv+Beautifulsoup+Neurolab机器学习平台 By 子敬叔叔 最近在学习麦好的<机器学习实践指南案例应用解析第二版>,在安装学习环境的时候 ...
- 【python+opencv】直线检测+圆检测
Python+OpenCV图像处理—— 直线检测 直线检测理论知识: 1.霍夫变换(Hough Transform) 霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也有很多改进 ...
- OpenCV识别技术
OpenCV识别技术# 老师:james 20181019 # 识别技术# Pycharm + Python3 + OpenCV """ 一.识别技术: 什么是OpenC ...
- python+opencv实现车牌定位
写在前面 HIT大三上学期视听觉信号处理课程中视觉部分的实验三,经过和学长们实验的对比发现每一级实验要求都不一样,因此这里标明了是2019年秋季学期的视觉实验三. 由于时间紧张,代码没有进行任何优化, ...
随机推荐
- 使用Xamarin开发移动应用示例——数独游戏(四)产生新游戏算法改进
项目代码可以从Github下载:https://github.com/zhenl/ZL.Shudu .代码随项目进度更新. 前面我们使用一个数组保存预制的游戏,然后随机从中抽取一个游戏作为新游戏,如果 ...
- 回顾 2021 中国 .NET 开发者峰会
.NET Conf China 2021 是面向开发人员的社区峰会,基于 .NET Conf 2021,庆祝 .NET 6 的发布和回顾过去一年来 .NET 在中国的发展.峰会由来自北京.上海.苏州. ...
- UCB DS100 讲义《数据科学的原理与技巧》校对活动正式启动 | ApacheCN
贡献指南:https://github.com/apachecn/ds100-textbook-zh/blob/master/CONTRIBUTING.md 整体进度:https://github.c ...
- AT3527 [ARC082D] Sandglass
解法一 直接考虑在初始为 \(a\) 的情况下时刻 \(t\) 时 \(A\) 中剩余的沙子是行不通的,不妨反过来考虑在时刻 \(t\) 每个初始值 \(a\) 的答案,令其为 \(f_t(a)\). ...
- Spring事物
简介 Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现. Spring事务管理器的接口是org ...
- Windows10关闭自动更新操作系统
1:按下:win+r 2:输入services.msc,打开服务本地 3:停止windows update服务,并禁用,同时在恢复里,改为无操作.
- JS generator(生成器)
笔记整理自:廖雪峰老师的JS教程 目录 简介 与函数的不同之处 函数写法 generator写法 generator调用 generator对象的`next()`方法调用 `for ... of`循环 ...
- YOLOv5模型训练及检测
一.为什么使用YOLOv5 二.软件工具 2.1 Anaconda https://www.anaconda.com/products/individual 2.2 PyCharm https://w ...
- Java == 和 equals 的区别(面试描述)
== == 是一个比较运算符 既可以判断基本类型,又可以判断引用类型 如果判断基本数据类型,判断的是值是否相等 如果判断的是引用类型,判断的是地址是否相等,判断是不是同一个对象 equals equa ...
- Solution -「CF 908D」New Year&Arbitrary Arrangement
\(\mathcal{Description}\) Link. 给定 \(n,p_a,p_b\),初始有一个空串,每次操作有 \(\frac{p_a}{p_a+p_b}\) 的概率在其后添加字 ...