Python-OpenCV车牌识别简易版(1)

  本程序主要在于学习OpenCV函数和建立机器视觉识别的简单思路,熟悉Python语法和numpy包的使用,熟悉Jupyter的使用,适合作为入门OpenCV的第一个小项目。

环境搭建

  项目需要电脑具有Python环境,此处不作赘述。

  Jupyter Notebook是一个运行在浏览器的交互式计算、编码环境,支持多种语言,以Python和R居多。它可以把代码、文本注释、数学公式、运行结果等元素组合在一起显示,动态的步进和执行。非常适合入门学习或者教学使用,我们可以很方便的看到代码每一步的执行结果。

  Jupyter Notebook可以通过Anaconda或者pip安装,本文推荐用简易的pip安装的方法。

  打开CMD,直接输入pip安装即可。

pip install jupyter notebook

  我们还需要安装OpenCV环境,而OpenCV有很多操作都是基于Numpy包。Numpy 是一个通用的数组处理包,提供了处理 n 维数组的工具,它是 Python 中科学计算和数据分析的基本包。继续在CMD中输入以下命令来安装numpy。

pip install numpy

  之后我们安装OpenCV-Python包,继续在CMD中输入以下命令,一路回车就行。

pip install opencv-python

  tips:如果以上安装太慢的话,可以使用国内源来安装,例如安装OpenCV可以输入pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple

  两个都安装完成后,我们在CMD中输入python回车,打开编译器输入以下命令来查看是否安装成功。

>>> import cv2
>>> print(cv2.__version__)
>>> from numpy import *
>>> eye(5)
b431870035bd6f807715b7542e32608

  其中from numpy import *是numpy特殊的导包语句,eye(5)是输出一个5阶对角阵。

  输入exit()退出python编译器,之后我们新建一个python虚拟环境来开始我们的学习。

  所谓虚拟环境就是在本地电脑实体上借助虚拟机docker开辟出来的相对独立的Python环境,我们把独立出来的部分称为“容器“,在容器中我们可以安装本项目需要的包,避免了和其他项目相互的影响。

  在Python3.3之后,自带了venv工具(之前版本需要pip安装virtualenv工具)来建立虚拟环境,我们在D盘中(或者自己想放置的文件目录下)打开CMD,输入以下代码来新建一个OpenCV_CarLicence的虚拟环境。

python -m venv OpenCV_CarLicence

 CMD不会有任何提示,但在D盘中会有一个新的文件夹,其内部结构如下所示。

3ce932d5db4f7a6600645df5ccfac64

  进入项目目录中的Scripts文件夹中,输入activate启动这个虚拟环境,CMD会变成以(OpenCV_CarLicence)开头的虚拟环境界面。

cd OpenCV_CarLicence
cd Scripts
activate
8b5d143bd407f8329cf86d264a1324e

  若要退出当前虚拟环境,输入exit()即可。

  之后,我们解决如何在Jupyter Notebook中打开这个虚拟环境的问题。我们首先要在这个环境下安装Jupyter的内核kernel。

ipython kernel install --user --name=OpenCV_CarLicence
5fe33bc9f7a0d0c727a937613417751

 之后我们cd ..退回到OpenCV_CarLicence主文件中,在这里我们输入jupyter notebook来打开jupyter的主界面。

8ece6ca0816b1266e14126e8ad36d11

 注意,这个CMD后台不能退出,一旦退出浏览器就访问不到jupyter了。

  在jupyter的由上角我们new一个Notebook,选择刚刚建立的虚拟环境。

d567de89854a74342e6f57f8bf9b17e

 这里就进入到了我们的代码书写界面,先回车一下,再选择第一个In[ ]:框,再把光标移到【代码】处,选择【Markdown】,第一个输入框就变成了我们输入.md笔记的地方了。

 在此我们按照.md的格式,输入#Python_OpenCV_车牌识别简易版然后按Shirt+Enter,就设置了本文件的大标题。

e04816c9b1d8ac3a0f44ec24aac5b12

车牌定位

  之后我们开始正式的编程环节,整体车牌识别要经过三个阶段:从图片中定位到车牌-把每个字符分割提取出来-对每个字符拼接。本章节我们把车牌定位出来。

  我们需要两个基础的函数库,一个是cv2,是opencv的python库,一个是matplotlib,用于在Python中创建静态、动态的交互式图像。

  我们先定义三个用来现实图片的方法,运行的快捷键是Shift+Enter

import cv2
# 【1】
from matplotlib import pyplot as plt

#显示图片
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()

#使用 matplotlib 库的 pyplot 模块来显示一个图像
#OpenCV 默认使用 BGR 格式存储图像,而 matplotlib 使用 RGB 格式
def plt_show0(img):
# 【2】
b,g,r = cv2.split(img)
img = cv2.merge([r,g,b])
plt.imshow(img) #这里用的是plt的imshow,将img显示在画布上
plt.show() #plt要多一个show()方法,显示所有打开的图形。
#show 函数会根据后端的设置,将图形嵌入到 GUI 中

#plt显示灰色图片
def plt_show(img):
plt.imshow(img,cmap = 'gray')
plt.show()

  【1】from matplotlib import pyplot as plt

  Pyplot 是 Matplotlib 的子库,提供了和 MATLAB 类似的绘图 API,使用的时候,我们可以使用 import 导入 pyplot 库,并设置一个别名 plt。

  【2】b,g,r = cv2.split(img)

  分离出图片的B,R,G颜色通道,之后要合并成BGR再imshow,cv2.imshow("合并后的图像",cv2.merge([B,G,R])) 合并(B,G,R)三通道。

  之后我们加载图片,利用cv2.imread()

#加载图片
rawImage = cv2.imread('./img/car1.png')
plt_show0(rawImage)
car1

  读取到了原图就要先高斯去噪,可以在保留主要特征的情况下去除噪音、平滑图像,高斯滤波是用一个卷积核来对整个图像的每一个像素进行加权平均。

#高斯去噪
# 【3】
image = cv2.GaussianBlur(rawImage,(3,3),0)
plt_show0(image)

  48e832454695e8f372152ad1716a261

  【3】高斯去噪 cv2.GaussianBlur(img, ksize, sigmaX, sigmaY, borderType)

  卷积核的大小决定了模糊的范围;sigmaX 和 sigmaY 是高斯核在 X 和 Y 方向上的标准差,表示模糊的强度,如果为0,则根据高斯核的大小自动计算。

  borderType 是边界处理的方式,可以是 cv2.BORDER_CONSTANT常量, cv2.BORDER_REPLICATE重复, cv2.BORDER_REFLECT反射, cv2.BORDER_WRAP包裹等,默认为 cv2.BORDER_DEFAULT。

  正常的图片三通道信息量太大,不适合处理,所以我们下一步把图片转为灰度图。

#灰度处理
gray_image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
plt_show(gray_image)

  e1195099e21549a27964eeaf3618090

  之后我们对图片提取轮廓信息,特别是纵向的轮廓信息,因为车牌地区纵向的线条会比较多。Sobel边缘检测可以把图像的边缘信息提取出来,适用于灰度图和噪声较多的图片。图片的轮廓信息是灰度值变化最大的地方,Sobel利用一阶导最大值来检测边界,可以分为X轴方向和Y轴方向。

# Sobel边缘检测
# 【4】
Sobel_y = cv2.Sobel(gray_image,cv2.CV_16S,1,0)
# 【5】
absY = cv2.convertScaleAbs(Sobel_y) #cv2.convertScaleAbs()是用于进行线性变换和绝对值转换的函数,可以用于图像增强和显示。
image = absY
plt_show(image)

  0df6a3e121636bc0d75a90e5ea1617c

  【4】Sobel边缘检测 Sobel_y = cv2.Sobel(gray_image,cv2.CV_16S,1,0)

  Sobel算子用于边缘检测的离散微分算子,在追求效率且不那么重视信息量的情况下可以使用Sobel,但是Canny才是永远的神。1和0分别是x和y方向上的求导阶数,表示只在x方向上求一阶导数,最后得到纵向边缘,Gx 用于检测纵向边缘, Gy 用于检测横向边缘,需要x方向图像就对y求微分,需要y方向图像就对x求微分,两者颠倒。

  【5】像素取绝对值 dst = cv2.convertScaleAbs(src, alpha, beta)

  alpha表示像素值缩放的比例因子,beta表示像素值偏移量,如果我们想要将一个16位的深度图像转换为8位的彩色图像,并增加对比度和亮度,我们可以使用以下代码:depth_image = cv2.convertScaleAbs(depth_image, alpha=0.03, beta=100)

  现在图片上还有很多浅浅的灰色线条,我们设定一个阈值,让大于这个值的灰度色就变成白色,小于这个颜色的变成黑色。这样可以使整张图片看起来有效信息更突出。

#自适应阈值处理,处理完成之后图片非黑即白
#【6】
ret,image = cv2.threshold(image,0,255,cv2.THRESH_OTSU)
plt_show(image)

  e79d403c663849eed8d2c5c78a25669

  【6】阈值处理ret,image = cv2.threshold(image,0,255,cv2.THRESH_OTSU)

  threshold()阈值处理,输入必须是单通道的灰度图,0是用于计算阈值的参数,当使用Otsu’s二值化时,该参数无效,可以设置为任意值。

  255是用于赋值给大于或等于阈值的像素的最大值,通常为255,表示白色。

  cv2.THRESH_OTSU是阈值类型,表示使用Otsu’s算法自动计算阈值,并将小于阈值的像素赋值为0,表示黑色。ret是计算出的最佳阈值,可以用于后续的分析或处理。

  image是阈值处理后的图像,是一个二值图像,只包含0和255两种像素值。  

  THRESH_BINARY:最基础的二进制阈值化,大于阈值的像素设为最大值,小于阈值的像素设为0。

  THRESH_BINARY_INV:反二进制阈值化,小于阈值的像素设为最大值,大于阈值的像素设为0。

  THRESH_TRUNC:截断阈值化,大于阈值的像素设为阈值,小于阈值的像素不变。

  THRESH_TOZERO:阈值化为0,小于阈值的像素设为0,大于阈值的像素不变。

  THRESH_TOZERO_INV:反阈值化为0,大于阈值的像素设为0,小于阈值的像素不变。

  THRESH_MASK:掩码阈值化,使用掩码图像来决定哪些像素需要阈值化。THRESH_OTSU:

  Otsu’s阈值化,使用Otsu’s算法自动寻找最佳阈值,只支持8位单通道图像。

  THRESH_TRIANGLE:Triangle阈值化,使用Triangle算法自动寻找最佳阈值,只支持8位单通道图像。

  之后对图像进行闭运算,闭运算就是先腐蚀再膨胀,可以填充图片中的小孔或使得边缘平滑。

# 闭运算,先膨胀再腐蚀,可以填充图像中的小孔或者平滑边缘
# 【7】
kernelX = cv2.getStructuringElement(cv2.MORPH_RECT,(17,5))#卷积核,这里是一个17*5的矩形
print(kernelX)
image = cv2.morphologyEx(image,cv2.MORPH_CLOSE,kernelX,iterations = 1)#iterations表示迭代次数
plt_show(image)
175b0606ef168277befb63978305d99

  【7】闭运算 getStructuringElement() morphologyEx()

  cv2.getStructuringElement (shape, ksize, anchor = new cv.Point (-1, -1)) 创建一个指定形状和大小的卷积核,用于后续的形态学操作。

  shape 表示结构元素的形状,可以是以下值之一:

    cv2.MORPH_RECT 表示矩形形状,即所有的元素都是1;

    cv2.MORPH_CROSS 表示十字形状,即中心行和列的元素都是1,其余为0;

    cv2.MORPH_ELLIPSE 表示椭圆形状,即根据指定的大小生成一个椭圆;

  ksize 表示结构元素的大小,是一个二元组,表示宽度和高度。

  anchor 表示结构元素的锚点位置,是一个二元组,表示结构元素中心的坐标。默认值是 (-1, -1)。

  cv2.morphologyEx (src, op, kernel, dst, anchor, iterations, borderType, borderValue) 这个函数是对图像进行形态学变换的通用函数,可以实现腐蚀、膨胀、开、闭、梯度、顶帽、黑帽等操作。

  src 表示输入图像,可以是任意通道数,但深度必须是 cv2.CV_8U, cv2.CV_16U, cv2.CV_16S, cv2.CV_32F 或 cv2.CV_64F 之一。

  op 表示形态学操作的类型,可以是以下值之一:

    cv2.MORPH_ERODE 表示腐蚀操作,即将结构元素滑动到图像的每个位置,如果结构元素覆盖的像素都是1,那么输出图像的对应位置也是1,否则是0。这样可以减少图像中的白色区域,去除小白点或分离相连的物体;

    cv2.MORPH_DILATE 表示膨胀操作,即将结构元素滑动到图像的每个位置,如果结构元素覆盖的像素中有一个是1,那么输出图像的对应位置也是1。这样可以增加图像中的白色区域,填补小孔或连接断裂的物体;

    cv2.MORPH_OPEN 表示开操作,即先进行腐蚀操作,再进行膨胀操作。这样可以去除小的白色噪声,或者分离相连的物体;

    cv2.MORPH_CLOSE 表示闭操作,即先进行膨胀操作,再进行腐蚀操作。这样可以填补物体内部的小孔,或者去除物体上的小黑点;

    cv2.MORPH_GRADIENT 表示梯度操作,即膨胀图像和腐蚀图像的差。这样可以得到物体的轮廓;

    cv2.MORPH_TOPHAT 表示顶帽操作,即原始图像和开操作之后的图像的差。这样可以得到比周围亮的区域;

    cv2.MORPH_BLACKHAT 表示黑帽操作,即闭操作之后的图像和原始图像的差。这样可以得到比周围暗的区域。

  kernel 表示结构元素,可以是 cv2.getStructuringElement 函数创建的,也可以是自定义的二维数组。

  dst 表示输出图像,和输入图像有相同的大小和类型。

  anchor 表示结构元素的锚点位置,和 cv2.getStructuringElement 函数的参数含义相同。默认值是 (-1, -1),表示结构元素的中心是其几何中心。

  iterations 表示迭代次数,默认值是1。

  borderType 表示边界填充的方式,和 cv2.copyMakeBorder 函数的参数含义相同。可以是以下值之一:

    cv2.BORDER_CONSTANT 表示用常数填充边界,需要指定 borderValue 参数;

    cv2.BORDER_REPLICATE 表示用边界像素的值填充边界;

    cv2.BORDER_REFLECT 表示用边界像素的镜像填充边界,不包括边界像素本身;

    cv2.BORDER_REFLECT_101 表示用边界像素的镜像填充边界,包括边界像素本身;

    cv2.BORDER_WRAP 表示用另一边的像素填充边界;

    cv2.BORDER_DEFAULT 表示用 cv2.BORDER_REFLECT_101 的方式填充边界;

  borderValue 表示边界填充的常数值,只有当 borderType 为 cv2.BORDER_CONSTANT 时有效。默认值是 cv2.morphologyDefaultBorderValue (),表示自动选择一个合适的值。

  现在的图片上小白点还是很多,这些都不会是车牌出现的位置,我们去除这些小白点。

#去除一些小白点
kernelX = cv2.getStructuringElement(cv2.MORPH_RECT,(17,1))
kernelY = cv2.getStructuringElement(cv2.MORPH_RECT,(1,13))
#膨胀、腐蚀
image = cv2.dilate(image,kernelX)
image = cv2.erode(image,kernelX)
#腐蚀、膨胀
image = cv2.erode(image,kernelY)
image = cv2.dilate(image,kernelY)

plt_show(image)
#这一步效果对于部分图片无法通过,需要精心调制才具有一般性
b949bb2ba78e92f804fa85c8afef58c

  可见图片上还有一些细小的白条,没有和大片的区域链接起来,我们采用中值滤波让他们图片变的平滑,让这些本该在一起的连接起来。

# 中值滤波 除去噪点
# 【8】
image = cv2.medianBlur(image,15)
plt_show(image)
42cad395fcf2b3c8908cf18e1190132

  【8】中值滤波 image = cv2.medianBlur(image,15)

  dst = cv2.medianBlur(src, ksize)是OpenCV中用于对图像进行中值滤波的函数。

  中值滤波是一种非线性滤波方法,它的原理是用邻域内所有像素值的中位数来替换当前像素值,从而消除图像中的椒盐噪声等。ksize表示滤波核的大小,必须是大于1的奇数。