opencv 十三 基于模板匹配实现复杂环境下的车牌区域提取

一、算法需求

基于模板匹配实现复杂环境下提取车辆照片中的车牌区域

在这里插入图片描述

二、问题分析

目前分析发现,在车辆照片中,车牌区域及其附近区域的特点如下:
1、车牌只存在于车头和车尾部分,车牌的下方是一个阴影区域(这是自然光照射所形成的
2、车牌在车尾时周围纹理比较单一,但车牌在车头时其上部分纹理较为复杂(不同车型的发动机出气口不同
3、车牌中的字符具备水平方向的边缘特征(车牌号都是水平排列的
要达成目前,首先裁剪出一个面积较大的模板(包含车牌周围的环境信息【这里所提到的特定1】),对图像进行模板粗粒度匹配,得到车牌大概区域(通过该操作可以将图像的size缩减到原来的1/3,可以滤除掉车辆外的干扰因素【例如,地板上存在车牌,但其没有固定在车辆上】)。然后对大模板裁剪出的小图进行分析,遴选出最具代表性的精准模板,然后进行多尺度模板匹配。

关键知识
模板匹配函数:matchTemplate

result = cv2.matchTemplate(image, template, cv2.TM_CCOEFF)

参数含义:
image:原图像,待匹配图像,8bit整数型、32bit浮点型,可以是单通道或多通道;
template:模板图像,类型同原图像,尺寸必须小于源图像;
method:匹配方法;
mask:掩码;
result:返回结果,32bit浮点型,原图像为W×H,模板图像为w×h,生成的图像对象为(W−w+1)×(H−h+1);

匹配方法TemplateMatchModes有6种,可以用相应的cv2.TM_xxx传入Python接口:
1、cv::TM_SQDIFF:该方法使用平方差进行匹配,因此最佳的匹配结果在结果为0处,值越大匹配结果越差。

2、cv::TM_SQDIFF_NORMED:该方法使用归一化的平方差进行匹配,最佳匹配也在结果为0处。

3、cv::TM_CCORR:相关性匹配方法,该方法使用源图像与模板图像的卷积结果进行匹配,因此,最佳匹配位置在值最大处,值越小匹配结果越差。

4、cv::TM_CCORR_NORMED:归一化的相关性匹配方法,与相关性匹配方法类似,最佳匹配位置也是在值最大处。

5、cv::TM_CCOEFF:相关性系数匹配方法,该方法使用源图像与其均值的差、模板与其均值的差二者之间的相关性进行匹配,最佳匹配结果在值等于1处,最差匹配结果在值等于-1处,值等于0直接表示二者不相关。

6、cv::TM_CCOEFF_NORMED:归一化的相关性系数匹配方法,正值表示匹配的结果较好,负值则表示匹配的效果较差,也是值越大,匹配效果也好。

匹配方法的选取根据实际情况而定。
以上内容引用自 :https://blog.csdn.net/guduruyu/article/details/69231259

三、核心思路

1、读取图片为灰度图
2、根据相对尺寸,截取粗粒度模板(包含车牌周围的环境信息
3、进行简单模板匹配(利用高斯滤波降噪,再利用Cnny提取边缘信息进行匹配),得到车牌粗区域
4、根据相对尺寸,截取精细模板(包含车牌邻域的空白区域信息);该操作可能需要尝试很多遍
5、进行多尺度模板匹配(利用sobel算子提取车牌字符的横向梯度让车牌字符变得明显,同时滤除掉车头车牌上面的各种y方向的梯度线

四、具体实现

4.1 读取图片为灰度图

灰度图更利于我们分析车辆的特征,用于模板裁剪的车牌应轮廓完整,字迹清晰,并且车牌周围少有其他明显的干扰信息。

代码和执行效果如下所示:

import cv2,os

def all_gray(path_list):
    for p in path_list:
        img=cv2.imread(path+'/'+p,1)
        print(path+"/"+p)
        img=cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
        cv2.imwrite(path+'/'+p,img)

path="images"
path_list=os.listdir(path)
#当jpg在x中才返回x,生成的path_list2只包含jpg文件
path_list2=[x for x in path_list if ".jpg" in x]
print(path_list)
print(path_list2)
all_gray(path_list2)

灰度图1:
在这里插入图片描述

灰度图2:
在这里插入图片描述

灰度图1和灰度图2相比,灰度图2更适合用于裁剪模板,灰度图1车牌区域显得很亮,而车牌周围区域显得很黑(这与大部分的车牌区域特征不合),因此基于灰度图1裁剪出来的模板在其他车辆上适配度不高。

4.2 根据相对尺寸,截取粗粒度模板

粗粒度模板应包含车牌周围的环境信息(直接使用精细的车牌模块很难一次性截取到车牌区域,其针对大部分图像都存在错误匹配),我们在选择模板时尽可能的扩大车牌区域信息(这里将车辆下方的阴影区域进行了保留),基于更多的空间信息可以避免错误匹配。
截取代码和粗粒度模板如下所示:

import cv2 
 
 
#保存面积较大的模板,更容易匹配到目标
def save_temp1():
    path=r"Car/2.jpg"
    img=cv2.imread(path,1)
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    #cv2.imshow('grsy',gray)
    gau_img=cv2.GaussianBlur(gray,(3,3),0)#高斯滤波
    #根据相对尺寸,采集模板
    hi,wi,c=img.shape
    yr,xr,hr,wr=0.3,0.33,0.45,0.45
    y,x,h,w=yr*hi,xr*wi,hr*hi,wr*wi
    y,x,h,w=int(y),int(x),int(h),int(w)
    print(y/hi,x/wi,h/hi,w/wi)
    im = gau_img[y:y+h,x:x+w]
    save_path = r'template1.jpg'
    cv2.imwrite(save_path,im)
    cv2.waitKey()
 save_temp1()

粗粒度模板如下所示:
在这里插入图片描述

4.3 简单模板匹配

通过简单模板匹配可以将图像的size缩减到原来的1/3,可以滤除掉车辆外的干扰因素,确定车牌初步范围。

执行代码:

 
import glob
import cv2

def template_crop(templateo,imageo):
    rate=5
    image=cv2.resize(imageo,None,fx=1/rate,fy=1/rate)
    template=cv2.resize(templateo,None,fx=1/rate,fy=1/rate)

    tH,tW,c=template.shape

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)

    gray = cv2.Canny(image, 50, 200)
    template = cv2.Canny(template, 50, 200)
    gray=cv2.GaussianBlur(gray,(3,3),0)#高斯滤波
    #cv2.imshow("template", template)
    #cv2.imshow("gray", gray)
    print(gray.shape, template.shape)
    result = cv2.matchTemplate(gray, template, cv2.TM_CCOEFF)
    (_, maxVal, _, maxLoc) = cv2.minMaxLoc(result)
    print(imagePath,maxVal)
    # 计算测试图片中模板所在的具体位置,即左上角和右下角的坐标值,并乘上对应的裁剪因子
    (startX, startY) = (int(maxLoc[0] -30), int(maxLoc[1] ))
    if startX<0:
        startX=0
    (endX, endY) = (int((maxLoc[0] + tW)+30 ), int((maxLoc[1] + tH) ))
        
    crop=imageo[startY*rate:endY*rate,startX*rate:endX*rate]
    crop=crop[50:-50]
    return(crop)

    # 绘制并显示结果
    #cv2.rectangle(image, (startX, startY), (endX, endY), (0, 0, 255), 2)
    #cv2.imshow("Image", image)
    cv2.waitKey(0)

def get_template():
    # 读取模板图片
    template = cv2.imread('template1.jpg')
    return template
template=get_template()
# 遍历所有的图片寻找模板
for imagePath in glob.glob("Car/*.jpg"):
    image = cv2.imread(imagePath)
    print(imagePath,image.shape)
    print(template.shape)
    img=template_crop(template,image)
    print(img.shape)
    sname=imagePath.replace('.jpg','.png')
    cv2.imwrite(sname,img)
    #cv2.imshow("imagimagee", image)
    cv2.imshow("image", img)
    cv2.waitKey(0)

代码执行效果:

在这里插入图片描述

4.4 根据相对尺寸,截取精细模板

通过4.3 我们已经得到了车牌的范围,排除了车牌周围大部分的干扰,但要想进一步确定车牌范围,还需在4.3的基础上裁剪出精细模板,尽可能的去除干扰。精细模板需根据实际匹配情况进行多次裁剪。
截取代码:

import cv2
#保存车牌模板,用于精准匹配到目标
def save_temp2():
    path=r"Car/23.jpg"
    img=cv2.imread(path,1)
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    gau_img=cv2.GaussianBlur(gray,(3,3),0)#高斯滤波
    #根据相对尺寸,采集模板
    hi,wi,c=img.shape
    yr,xr,hr,wr=0.35,0.4,0.15,0.45 
    y,x,h,w=yr*hi,xr*wi,hr*hi,wr*wi 
    y,x,h,w=int(y),int(x),int(h),int(w)
    print(y/hi,x/wi,h/hi,w/wi)
    im = gau_img[y:y+h,x:x+w]
    save_path = r'template2.jpg'
    cv2.imwrite(save_path,im)
 
save_temp2()

精细模板:
在这里插入图片描述

4.5 多尺度模板匹配

使用matchTemplate()只能检测和模板图像大小一样的目标,对于不同大小的目标,或者稍有差异的目标检测不行。我们所拍摄的车牌姿态信息较为丰富,距离远近各有不同。在单一尺度下进行精细的模板匹配,无法在所有的图片中都准确找到车牌。
针对这样的情况,对图像进行多尺度模板匹配(通过改变待检测图像的大小来实现多尺度的模板匹配算法)。在该匹配过程中,任然发现有部分匹配错误(主要是将车牌上方的出气口或者logo文字识别为匹配区域)。所以,对图像求水平方向的梯度,过滤掉大部分不符合车牌字符特征的纹理,基于梯度进行模板匹配。
运行代码:


import numpy as np
import argparse
import imutils
import glob
import cv2

args = {
    
    'template': 'template2.jpg', 'images': "images",'visualize':False}
 
# 读取模板图片
template = cv2.imread(args["template"])
# 转换为灰度图片
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
# 执行边缘检测
#template = cv2.Canny(template, 50, 200)
template = cv2.Sobel(template, cv2.CV_64F, dx=1, dy=0, ksize=3)
template = cv2.convertScaleAbs(template)
kernel=cv2.getStructuringElement(shape=cv2.MORPH_RECT,ksize=(7,7))
template=cv2.morphologyEx(src=template,op=cv2.MORPH_CLOSE,kernel=kernel,iterations=1)
(tH, tW) = template.shape[:2]
# 显示模板
cv2.imshow("Template", template)
print('args.get("visualize", False):====',args.get("visualize", True))
# 遍历所有的图片寻找模板
for imagePath in glob.glob(args["images"] + "/*.png"):
    print(imagePath)
    # 读取测试图片并将其转化为灰度图片
    image = cv2.imread(imagePath)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    found = None

    # 循环遍历不同的尺度
    for scale in np.linspace(0.2, 1.0, 10)[::-1]:
        # 根据尺度大小对输入图片进行裁剪
        resized = imutils.resize(gray, width = int(gray.shape[1] * scale))
        r = gray.shape[1] / float(resized.shape[1])

        # 如果裁剪之后的图片小于模板的大小直接退出
        if resized.shape[0] < tH or resized.shape[1] < tW:
            break

        # 首先进行高斯滤波,然后用Sobel算子计算水平方向的梯度,接着进行闭运算
        resized=cv2.GaussianBlur(resized,(5,5),0)#高斯滤波
        edged = cv2.Sobel(resized, cv2.CV_64F, dx=1, dy=0, ksize=3)
        edged = cv2.convertScaleAbs(edged)
        edged=cv2.morphologyEx(src=edged,op=cv2.MORPH_CLOSE,kernel=kernel,iterations=1)
        #cv2.imshow("edged", edged)
        #cv2.waitKey()
        #模板匹配
        result = cv2.matchTemplate(edged, template, cv2.TM_CCOEFF)
        (_, maxVal, _, maxLoc) = cv2.minMaxLoc(result)

        # 结果可视化
        if args.get("visualize", False):
            # 绘制矩形框并显示结果
            clone = np.dstack([edged, edged, edged])
            cv2.rectangle(clone, (maxLoc[0], maxLoc[1]), (maxLoc[0] + tW, maxLoc[1] + tH), (0, 0, 255), 2)
            cv2.imshow("Visualize", clone)
            cv2.waitKey(0)

        # 如果发现一个新的关联值则进行更新
        if found is None or maxVal > found[0]:
            found = (maxVal, maxLoc, r)

    # 计算测试图片中模板所在的具体位置,即左上角和右下角的坐标值,并乘上对应的裁剪因子
    (_, maxLoc, r) = found
    (startX, startY) = (int(maxLoc[0] * r), int(maxLoc[1] * r))
    (endX, endY) = (int((maxLoc[0] + tW) * r), int((maxLoc[1] + tH) * r))
    startX-=30
    endX+=30
    # 绘制并显示结果
    cv2.rectangle(image, (startX, startY), (endX, endY), (0, 0, 255), 2)
    cv2.imshow("Image", image)
    save_name=imagePath.replace(".png",".bmp")
    cv2.imwrite(save_name,image)
    cv2.waitKey(0)

代码运行效果:

猜你喜欢

转载自blog.csdn.net/m0_74259636/article/details/133101850