OpenCV+Python识别车牌和字符分割的实现


Posted in Python onJanuary 31, 2019

本篇文章主要基于python语言和OpenCV库(cv2)进行车牌区域识别和字符分割,开篇之前针对在python中安装opencv的环境这里不做介绍,可以自行安装配置!

车牌号检测需要大致分为四个部分:

1.车辆图像获取

2.车牌定位、

3.车牌字符分割

4.车牌字符识别

具体介绍

车牌定位需要用到的是图片二值化为黑白后进canny边缘检测后多次进行开运算与闭运算用于消除小块的区域,保留大块的区域,后用cv2.rectangle选取矩形框,从而定位车牌位置

车牌字符的分割前需要准备的是只保留车牌部分,将其他部分均变为黑色背景。这里我采用cv2.grabCut方法,可将图像分割成前景与背景。分割完成后,再经过二值化为黑白图后即可进行字符分割。由于图像中只有黑色和白色像素,因此我们需要通过图像的白色像素和黑色像素来分割开字符。即分别通过判断每一行每一列的黑色白色像素值的位置,来定位出字符。

具体步骤如下:

1.灰度转换:将彩色图片转换为灰度图像,常见的R=G=B=像素平均值。

2.高斯平滑和中值滤波:去除噪声。

3.Sobel算子:提取图像边缘轮廓,X方向和Y方向平方和开跟。

4.二值化处理:图像转换为黑白两色,通常像素大于127设置为255,小于设置为0。

5.膨胀和细化:放大图像轮廓,转换为一个个区域,这些区域内包含车牌。

6.通过算法选择合适的车牌位置,通常将较小的区域过滤掉或寻找蓝色底的区域。

7.标注车牌位置

8.图像切割和识别

通过代码实现:

# -*- coding: utf-8 -*-
"""
@email:cuiran2001@163.com
@author: cuiran
"""
import cv2
import numpy as np
from PIL import Image
import os.path
from skimage import io,data
def stretch(img):
 '''
 图像拉伸函数
 '''
 maxi=float(img.max())
 mini=float(img.min())

 for i in range(img.shape[0]):
  for j in range(img.shape[1]):
   img[i,j]=(255/(maxi-mini)*img[i,j]-(255*mini)/(maxi-mini))

 return img

def dobinaryzation(img):
 '''
 二值化处理函数
 '''
 maxi=float(img.max())
 mini=float(img.min())

 x=maxi-((maxi-mini)/2)
 #二值化,返回阈值ret 和 二值化操作后的图像thresh
 ret,thresh=cv2.threshold(img,x,255,cv2.THRESH_BINARY)
 #返回二值化后的黑白图像
 return thresh

def find_rectangle(contour):
 '''
 寻找矩形轮廓
 '''
 y,x=[],[]

 for p in contour:
  y.append(p[0][0])
  x.append(p[0][1])

 return [min(y),min(x),max(y),max(x)]

def locate_license(img,afterimg):
 '''
 定位车牌号
 '''
 img,contours,hierarchy=cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

 #找出最大的三个区域
 block=[]
 for c in contours:
  #找出轮廓的左上点和右下点,由此计算它的面积和长度比
  r=find_rectangle(c)
  a=(r[2]-r[0])*(r[3]-r[1]) #面积
  s=(r[2]-r[0])*(r[3]-r[1]) #长度比

  block.append([r,a,s])
 #选出面积最大的3个区域
 block=sorted(block,key=lambda b: b[1])[-3:]

 #使用颜色识别判断找出最像车牌的区域
 maxweight,maxindex=0,-1
 for i in range(len(block)):
  b=afterimg[block[i][0][1]:block[i][0][3],block[i][0][0]:block[i][0][2]]
  #BGR转HSV
  hsv=cv2.cvtColor(b,cv2.COLOR_BGR2HSV)
  #蓝色车牌的范围
  lower=np.array([100,50,50])
  upper=np.array([140,255,255])
  #根据阈值构建掩膜
  mask=cv2.inRange(hsv,lower,upper)
  #统计权值
  w1=0
  for m in mask:
   w1+=m/255

  w2=0
  for n in w1:
   w2+=n

  #选出最大权值的区域
  if w2>maxweight:
   maxindex=i
   maxweight=w2

 return block[maxindex][0]

def find_license(img):
 '''
 预处理函数
 '''
 m=400*img.shape[0]/img.shape[1]

 #压缩图像
 img=cv2.resize(img,(400,int(m)),interpolation=cv2.INTER_CUBIC)

 #BGR转换为灰度图像
 gray_img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

 #灰度拉伸
 stretchedimg=stretch(gray_img)

 '''进行开运算,用来去除噪声'''
 r=16
 h=w=r*2+1
 kernel=np.zeros((h,w),np.uint8)
 cv2.circle(kernel,(r,r),r,1,-1)
 #开运算
 openingimg=cv2.morphologyEx(stretchedimg,cv2.MORPH_OPEN,kernel)
 #获取差分图,两幅图像做差 cv2.absdiff('图像1','图像2')
 strtimg=cv2.absdiff(stretchedimg,openingimg)

 #图像二值化
 binaryimg=dobinaryzation(strtimg)

 #canny边缘检测
 canny=cv2.Canny(binaryimg,binaryimg.shape[0],binaryimg.shape[1])

 '''消除小的区域,保留大块的区域,从而定位车牌'''
 #进行闭运算
 kernel=np.ones((5,19),np.uint8)
 closingimg=cv2.morphologyEx(canny,cv2.MORPH_CLOSE,kernel)

 #进行开运算
 openingimg=cv2.morphologyEx(closingimg,cv2.MORPH_OPEN,kernel)

 #再次进行开运算
 kernel=np.ones((11,5),np.uint8)
 openingimg=cv2.morphologyEx(openingimg,cv2.MORPH_OPEN,kernel)

 #消除小区域,定位车牌位置
 rect=locate_license(openingimg,img)

 return rect,img

def cut_license(afterimg,rect):
 '''
 图像分割函数
 '''
 #转换为宽度和高度
 rect[2]=rect[2]-rect[0]
 rect[3]=rect[3]-rect[1]
 rect_copy=tuple(rect.copy())
 rect=[0,0,0,0]
 #创建掩膜
 mask=np.zeros(afterimg.shape[:2],np.uint8)
 #创建背景模型 大小只能为13*5,行数只能为1,单通道浮点型
 bgdModel=np.zeros((1,65),np.float64)
 #创建前景模型
 fgdModel=np.zeros((1,65),np.float64)
 #分割图像
 cv2.grabCut(afterimg,mask,rect_copy,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
 mask2=np.where((mask==2)|(mask==0),0,1).astype('uint8')
 img_show=afterimg*mask2[:,:,np.newaxis]

 return img_show

def deal_license(licenseimg):
 '''
 车牌图片二值化
 '''
 #车牌变为灰度图像
 gray_img=cv2.cvtColor(licenseimg,cv2.COLOR_BGR2GRAY)

 #均值滤波 去除噪声
 kernel=np.ones((3,3),np.float32)/9
 gray_img=cv2.filter2D(gray_img,-1,kernel)

 #二值化处理
 ret,thresh=cv2.threshold(gray_img,120,255,cv2.THRESH_BINARY)

 return thresh


def find_end(start,arg,black,white,width,black_max,white_max):
 end=start+1
 for m in range(start+1,width-1):
  if (black[m] if arg else white[m])>(0.98*black_max if arg else 0.98*white_max):
   end=m
   break
 return end


if __name__=='__main__':
 img=cv2.imread('test_images/car001.jpg',cv2.IMREAD_COLOR)
 #预处理图像
 rect,afterimg=find_license(img)

 #框出车牌号
 cv2.rectangle(afterimg,(rect[0],rect[1]),(rect[2],rect[3]),(0,255,0),2)
 cv2.imshow('afterimg',afterimg)

 #分割车牌与背景
 cutimg=cut_license(afterimg,rect)
 cv2.imshow('cutimg',cutimg)

 #二值化生成黑白图
 thresh=deal_license(cutimg)
 cv2.imshow('thresh',thresh)
 cv2.waitKey(0)

 #分割字符
 '''
 判断底色和字色
 '''
 #记录黑白像素总和
 white=[]
 black=[]
 height=thresh.shape[0] #263
 width=thresh.shape[1] #400
 #print('height',height)
 #print('width',width)
 white_max=0
 black_max=0
 #计算每一列的黑白像素总和
 for i in range(width):
  line_white=0
  line_black=0
  for j in range(height):
   if thresh[j][i]==255:
    line_white+=1
   if thresh[j][i]==0:
    line_black+=1
  white_max=max(white_max,line_white)
  black_max=max(black_max,line_black)
  white.append(line_white)
  black.append(line_black)
  print('white',white)
  print('black',black)
 #arg为true表示黑底白字,False为白底黑字
 arg=True
 if black_max<white_max:
  arg=False

 n=1
 start=1
 end=2
 s_width=28
 s_height=28
 while n<width-2:
  n+=1
  #判断是白底黑字还是黑底白字 0.05参数对应上面的0.95 可作调整
  if(white[n] if arg else black[n])>(0.02*white_max if arg else 0.02*black_max):
   start=n
   end=find_end(start,arg,black,white,width,black_max,white_max)
   n=end
   if end-start>5:
    cj=thresh[1:height,start:end]

    # new_image = cj.resize((s_width,s_height),Image.BILINEAR)
    # cj=cj.reshape(28, 28)
    print("result/%s.jpg" % (n))
    #保存分割的图片 by cayden
    # cj.save("result/%s.jpg" % (n))
    infile="result/%s.jpg" % (n)
    io.imsave(infile,cj)

    # im = Image.open(infile)
    # out=im.resize((s_width,s_height),Image.BILINEAR)
    # out.save(infile)

    cv2.imshow('cutlicense',cj)
    cv2.waitKey(0)


 cv2.waitKey(0)
 cv2.destroyAllWindows()

运行效果如图所示

车牌定位并进行处理

OpenCV+Python识别车牌和字符分割的实现

车牌分割如图所示

OpenCV+Python识别车牌和字符分割的实现

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python 运算符 供重载参考
Jun 11 Python
python判断windows隐藏文件的方法
Mar 21 Python
利用Python自动监控网站并发送邮件告警的方法
Aug 24 Python
Python 通配符删除文件的实例
Apr 24 Python
Python中的CSV文件使用&quot;with&quot;语句的方式详解
Oct 16 Python
Django处理Ajax发送的Get请求代码详解
Jul 29 Python
如何在django中运行scrapy框架
Apr 22 Python
离线状态下在jupyter notebook中使用plotly实例
Apr 24 Python
python 如何快速复制序列
Sep 07 Python
Python实现简单的猜单词小游戏
Oct 28 Python
matplotlib绘制多子图共享鼠标光标的方法示例
Jan 08 Python
Django基础CBV装饰器和中间件
Mar 22 Python
Python3删除排序数组中重复项的方法分析
Jan 31 #Python
对python判断ip是否可达的实例详解
Jan 31 #Python
对python:threading.Thread类的使用方法详解
Jan 31 #Python
python实现一个简单的ping工具方法
Jan 31 #Python
Python获取网段内ping通IP的方法
Jan 31 #Python
Python实现删除排序数组中重复项的两种方法示例
Jan 31 #Python
python重试装饰器的简单实现方法
Jan 31 #Python
You might like
PHP 字符串操作入门教程
2006/12/06 PHP
Discuz! 6.1_jQuery兼容问题
2008/09/23 Javascript
关于可运行代码无法正常执行的使用说明
2010/05/13 Javascript
javaScript事件学习小结(四)event的公共成员(属性和方法)
2016/06/09 Javascript
jquery validate表单验证插件
2016/09/06 Javascript
Javascript之图片的延迟加载的实例详解
2017/07/24 Javascript
详解vue.js之props传递参数
2017/12/12 Javascript
JS Object.preventExtensions(),Object.seal()与Object.freeze()用法实例分析
2018/08/25 Javascript
JSONP原理及应用实例详解
2018/09/13 Javascript
Vue实现导航栏的显示开关控制
2019/11/01 Javascript
Vue组件模板及组件互相引用代码实例
2020/03/11 Javascript
原生js canvas实现鼠标跟随效果
2020/08/02 Javascript
vue3中轻松实现switch功能组件的全过程
2021/01/07 Vue.js
在Python程序中进行文件读取和写入操作的教程
2015/04/28 Python
Python实现把数字转换成中文
2015/06/29 Python
Python模拟登陆淘宝并统计淘宝消费情况的代码实例分享
2016/07/04 Python
Python使用Scrapy爬虫框架全站爬取图片并保存本地的实现代码
2018/03/04 Python
Pandas 合并多个Dataframe(merge,concat)的方法
2018/06/08 Python
python3+PyQt5 数据库编程--增删改实例
2019/06/17 Python
python动态视频下载器的实现方法
2019/09/16 Python
python 实现将小图片放到另一个较大的白色或黑色背景图片中
2019/12/12 Python
python logging.info在终端没输出的解决
2020/05/12 Python
django 模型中的计算字段实例
2020/05/19 Python
Python3爬虫发送请求的知识点实例
2020/07/30 Python
python 爬虫如何正确的使用cookie
2020/10/27 Python
英国汽车和货车租赁网站:Hertz英国
2016/09/02 全球购物
CK澳大利亚官网:Calvin Klein澳大利亚
2020/12/12 全球购物
什么是托管函数?托管函数有什么用?
2014/06/15 面试题
物业招聘计划书
2014/01/10 职场文书
客服部班长工作责任制
2014/02/25 职场文书
小学母亲节活动方案
2014/03/14 职场文书
法律顾问服务方案
2014/05/15 职场文书
八一建军节慰问信
2015/02/14 职场文书
超搞笑婚前保证书
2015/05/08 职场文书
党员发展大会主持词
2015/07/03 职场文书
我的中国梦心得体会范文
2016/01/05 职场文书