车牌识别项目全过程——opencv知识自学(1)

目录

  • 什么是opencv?
    • 图像数字化基础知识
    • 图像获取
    • 图像变换
      • 改变大小
  • 车牌识别内容分析
    • 定位车牌
      • 图像降噪
      • 形态学处理
      • 阈值分割+边缘检测

什么是opencv?

  1. OpenCV(Open Source Computer Vision Library)是开源的计算机视觉和机器学习库,提供了C++、C以及python等接口,并支持Windows、Linux、Android、MacOS平台。
  2. 在2016年以后,深度学习的应用越来越广泛,OpenCV里也添加了CNN之类的模块,可以与Tensorflow、Caffe2这些框架训练出来的模型对接。

图像数字化基础知识

一张图片,是由无数个小方格组成的。
彩色图片——是由彩色小方格组成,每一个彩色的方格都是由三个数值组成的向量量化的。(0,0,0)代表黑色,(255,255,255)代表白色,(255,0,0)代表红色。
灰色图片——是由灰色小方格组成,每一个灰色小方格都是由一个数值组成的向量量化的。

508行(高度)672列(宽度)的二维数字矩阵。

计算机会将每一个像素数字化为一个数值,灰度图的”位深度“是8bit(彩色是24位),代表将每一个方格数字化为[0,255]之间的uchar类型数字,即用256个数字来衡量灰度的深浅,值越大,代表越亮,值越小,代表越灰,255代表白色,0代表黑色。

图像获取

import cv2
gray_img = cv2.imread(r'C:\Users\fujie\Pictures\shunli.jpg',1) # 读入图像
cv2.imshow('Image', gray_img) # 显示图像
cv2.imwrite('images/output.jpg', gray_img)     # 保存图像
cv2.waitKey()  #等待时间,毫秒级,0表示任意中止

图像变换

对图像进行一些操作,可以看作是一种图像扩充的手段。

  • 图像增强的目的是要改善图像的视觉效果,针对给定图像的应用场合,有目的的增强图像的整体或局部特性,将原来不清晰的图像变得清晰或增强某些感兴趣的特征,扩大图像中不同物体的特征之间的差别,抑制不感兴趣的特征,使之改善图像质量、丰富信息量,将强图像判读和识别效果,满足某些特征分析的需求。

改变大小

(x,y,3)
行x——高
列y——宽

车牌识别内容分析

  1. 汽车牌照自动识别技术是把处理图像的方法与计算机的软件技术相连接在一起,以准确识别出车牌牌照的字符为目的,将识别出的数据传送至交通实时管理系统,以最终实现交通监管的功能。
  2. 我国的汽车车牌一般由七个字符和一个点组成。
    车牌识别大致分为四部分:图像获取(从视觉传感器中获取车辆的图像信息)——定位车牌(从原始图像中找到车牌的位置)——字符分割(将车牌进行分割,分割出七个字符用于后续的字符识别)——字符识别(对分离出的单个字符进行识别,从而识别出车牌的值)

定位车牌

从获取的图像中找到车牌所在位置(定位所在区域),并把车牌从该区域中分割出来,以便后续的字符分割使用。
车牌定位的方法主要分为四类:

  1. 基于颜色的分割:利用颜色空间的信息,包括彩色边缘算法、颜色距离算法、相似度算法。
  2. 基于纹理的分割:如利用车牌区域水平方向的纹理特征进行分割,包括小波纹理、水平梯度差纹理。
  3. 基于边缘检测的分割
  4. 基于数学形态法的分割

图像降噪

获取图像,原始图像为彩色,变为灰度图像便于后续处理。

原始图像包含噪声信息,噪声可以理解为由一种或多种原因造成的灰度值的随机变化,在大多数情况下,需要平滑技术(滤波)进行去除。常用的平滑处理算法包括基于二维离散卷积的高斯平滑、均值平滑。。。本次操作使用高斯平滑方法。

高斯滤波:如果有一个像素点的值远高于周围的点则可能是噪声或高频的边缘,高斯滤波对用该点周围多个点与其做加权平均等于用周边的值拉低了这个高值,也就是所谓平滑。高斯滤波在数学上的体现就是对整个图像像素值通过加权平均重赋值的操作。(加权平均可以理解为不同部分按照不同的比重进行计算再相加的结果。)
细节链接

——得到的图像与原始图像相比有些轻微的模糊。这样,单独的一个像素点在经过高斯平滑的图像上变得几乎没有影响。

  • OpenCV之高斯平滑:
    高斯平滑是通过做两个矩阵之间的二维离散卷积运算完成的,高斯卷积核和原始图像进行卷积。

  • 卷积:
  • 在OpenCV中给出了构建一维垂直方向上的高斯卷积核的函数:Mat getGaussianKernel(int ksize, double sigma, in ktype = CV/_64F)

形态学处理

形态学处理主要用于从图像中提取对表达和描绘区域形状有意义的图像分量,使后续的识别工作能够抓住目标对象最为本质〈最具区分能力-most discriminative)的形状特征,如边界和连通区域等。同时像细化、像素化和修剪毛刺等技术也常应用于图像的预处理和后处理中,成为图像增强技术的有力补充。

形态学处理的基本运算:腐蚀、膨胀、开运算、闭运算,击中与击不中、骨架抽取等。如腐蚀是让暗的区域变大,膨胀是让亮的区域变大。

进行开运算(开运算可以使图像的轮廓变得光滑, 还能使狭窄的连接断开和消除细毛刺。该操作为后面更加准确的提取车牌的轮廓做铺垫):

开运算详情链接
开运算主要是黑吃白,黑色区域变大。去除没用的信息点。
闭运算主要是白吃黑,白色区域变大。加大白色高亮区域。

将原始灰度图片和形态学图片结合成为一张图片进行后续的阈值分割操作:

阈值分割+边缘检测

阈值分割是常见的直接对图像进行分割的算法,根据图像像素的灰度值的不同而定。对应单一目标图像,只需选取一个阈值,即可将图像分为目标和背景两大类,这个称为单阈值分割;如果目标图像复杂,选取多个阈值,才能将图像中的目标区域和背景被分割成多个,这个称为多阈值分割,此时还需要区分检测结果中的图像目标,对各个图像目标区域进行唯一的标识进行区分。

通过阈值处理,就是希望能够从背景中分离出我们的研究对象。把图像分成若干个特定的、具有独特性质的区域,每一个区域代表一个像素的集合,每一个集合又代表一个物体。

采用Ostu阈值处理:在使用函数cv.threshold()进行阈值处理时,需要自定义一个阈值,并以此阈值作为图像阈值处理的依据。OTSU可以帮我们遍历所有可能的阈值,从而找到最佳的阈值。

进行闭运行平滑(阈值分割后未进行边缘检测)

阈值分割后加入边缘检测:(在这里使用canny边缘检测)

之后进行闭运算增加白色高亮区域:

进行开运算去除无用的白色部分 填充缝隙:

最后进行膨胀再次填充缝隙并增大白色高亮部分:

每个轮廓我们可以看作是一系列的点(像素)构成的一个有序的点集,而现在我们要提取这些白色区域的轮廓。(画出带有轮廓的原始图片)

现在我们已经有了轮廓,我们需要筛选出车牌所在的那个轮廓(可以看到车牌所在的区域为矩形),由于车牌宽和高的比例是固定的(车牌字符的高度和宽度是固定的,分别为90mm和45mm),依据这个几何特征,我们进行筛选,然后用绿色的线条将得到的车牌框选出来,同时截取出车牌用来做下一步的字符分割。

定位车牌的全部代码:

import cv2
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
#########该函数能够读取磁盘中的图片文件,默认以彩色图像的方式进行读取
def imread_photo(filename, flags=cv2.IMREAD_COLOR):
    return cv2.imread(filename, flags)


##############这个函数的作用就是来调整图像的尺寸大小,当输入图像尺寸的宽度大于阈值(默认1000),我们会将图像按比例缩小#######
def resize_photo(imgArr,MAX_WIDTH = 1000):
    img = imgArr
    rows, cols= img.shape[:2]     #获取输入图像的高和宽即第0列和第1列
    if cols >  MAX_WIDTH:
        change_rate = MAX_WIDTH / cols
        img = cv2.resize(img ,( MAX_WIDTH ,int(rows * change_rate) ), interpolation = cv2.INTER_AREA)
    return img

# ################高斯平滑###############
#我们首先会对图像水平方向进行卷积,然后再对垂直方向进行卷积,其中sigma代表高斯卷积核的标准差
def gaussBlur(image,sigma,H,W,_boundary = 'fill', _fillvalue = 0):
    #水平方向上的高斯卷积核
    gaussKenrnel_x = cv2.getGaussianKernel(sigma,W,cv2.CV_64F)
    #进行转置
    gaussKenrnel_x = np.transpose(gaussKenrnel_x)
    #图像矩阵与水平高斯核卷积
    gaussBlur_x = signal.convolve2d(image,gaussKenrnel_x,mode='same',boundary=_boundary,fillvalue=_fillvalue)
    #构建垂直方向上的卷积核
    gaussKenrnel_y = cv2.getGaussianKernel(sigma,H,cv2.CV_64F)
    #图像与垂直方向上的高斯核卷积核
    gaussBlur_xy = signal.convolve2d(gaussBlur_x,gaussKenrnel_y,mode='same',boundary= _boundary,fillvalue=_fillvalue)
    return gaussBlur_xy


def chose_licence_plate(contours, Min_Area=2000):
    temp_contours = []
    for contour in contours:
        if cv2.contourArea(contour) > Min_Area:
            temp_contours.append(contour)
    car_plate = []
    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
        # 车牌正常情况下宽高比在2 - 5.5之间
        if aspect_ratio > 2 and aspect_ratio < 5.5:
            car_plate.append(temp_contour)
            rect_vertices = cv2.boxPoints(rect_tupple)
            rect_vertices = np.int0(rect_vertices)
    return car_plate

def license_segment( car_plates ):
    if len(car_plates)==1:
        for car_plate in car_plates:
            row_min,col_min = np.min(car_plate[:,0,:],axis=0)
            row_max, col_max = np.max(car_plate[:, 0, :], axis=0)
            cv2.rectangle(img, (row_min,col_min), (row_max, col_max), (0,255,0), 2)
            card_img = img[col_min:col_max,row_min:row_max,:]
            cv2.imshow("img", img)
        cv2.imwrite( "card_img.png", card_img)
        cv2.imshow("card_img.png", card_img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    return  "card_img.png"


if __name__ == "__main__":

    img = imread_photo("car_test.png")  # 默认读取彩色图片
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 将彩色图片转换为灰色图片,灰色图像更便于后续处理。
    rgray_img = resize_photo(gray_img)

    # 高斯平滑
    blurImage = gaussBlur(gray_img, 5, 400, 400, 'symm')
    #对bIurImage进行灰度级显示
    blurImage = np.round(blurImage)
    blurImage = blurImage.astype(np.uint8)

    kernel = np.ones((10, 10), np.uint8)
    #开运算
    img_opening = cv2.morphologyEx(blurImage, cv2.MORPH_OPEN, kernel)
    # cv2.imshow("GaussBlur_gray_img", blurImage)
    # cv2.imshow("xingtai_gray_img", img_opening)


    #将两幅图像合成为一幅图像
    img_opening = cv2.addWeighted(rgray_img, 1, img_opening, -1, 0)
    # cv2.imshow("hecheng_gray_img", img_opening)



    #阈值分割
    t, result_img = cv2.threshold(img_opening, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    # cv2.imshow("yuzhi_gray_img",result_img)
    #canny边缘检测
    img_edge = cv2.Canny(result_img, 100, 200)
    # cv2.imshow("bianyuan_gray_img", img_edge)
    #闭运算来填充白色物体内细小黑色空洞的区域并平滑其边界
    kernel1 = np.ones((18, 18), np.uint8)
    img_edge1 = cv2.morphologyEx(img_edge, cv2.MORPH_CLOSE, kernel1)
    # cv2.imshow("biyunsuan", img_edge1)

    kernel2 = np.ones((10, 10), np.uint8)
    img_edge2 = cv2.morphologyEx(img_edge1, cv2.MORPH_OPEN, kernel2)
    # cv2.imshow("kaiyunsuan", img_edge2)

    kernel = np.ones((20, 20), np.uint8)
    img_dilate = cv2.dilate(img_edge2, kernel)  # 膨胀
    cv2.imshow("dilate", img_dilate)  # 显示图片

    # #查找图像边缘整体形成的矩形区域, contours是找到的多个轮廓
    image, contours, hierarchy = cv2.findContours(img_dilate,cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    # cv2.imshow("xunzhao", hierarchy)

    draw_img = img.copy()
    result = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
    # 画出带有轮廓的原始图片
    cv2.imshow('ret',result)
    # cv2.waitKey(0)
    # cv2.destroyAllWindows()

    car_plates = chose_licence_plate(contours)
    card_img = license_segment(car_plates)

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2023年6月7日
下一篇 2023年6月7日

相关推荐