OpenCV实现车牌定位和字符分割

前言:本案例的车牌图像来源于互联网,如有侵权请尽快联系我,立删。

一、概述

在智能交通系统中,汽车牌照识别发挥了巨大的作用。其实现是将图像处理技术与计算机软件技术相连接在一起,以准确识别出车牌牌照的字符为目的,将识别出的数据传送至交通实时管理系统,以最终实现交通监管的功能。在车牌自动识别系统中,从汽车图像的获取到车牌字符处理是一个复杂的过程,主要分为四个阶段:图像获取、车牌定位、字符分割以及字符识别。本文主要通过OpenCV的各种图像处理方法实现车牌定位以及字符分割。(字符识别可参考我的另一篇博客车牌识别

二、车牌图像分析

我国汽车牌照一般由七个字符和一个点组成(参考下图),车牌字符的高度和宽度是固定的,分别为90mm和45mm,七个字符之间的距离也是固定的12mm,中间分割符圆点的直径是10mm,但是真实车牌图像会因为透视原因造成字符间的距离变化。在民用车牌中,字符排列位置遵循以下规律:第一个字符通常是我国各省区的简称,共31个,用汉字表示;第二个字符通常是发证机关的代码号,最后五个字符由英文字母和数字组合而成,字母是24个大写字母(除去 I 和 O)的组合,数字用"0-9"之间的数字表示。

标准车牌
从图像处理角度看,汽车牌照具有以下几个特征:

  1. 车牌的几何特征,即车牌形状统一为高宽比固定的矩形;
  2. 车牌的灰度分布呈现出连续的波谷-波峰-波谷分布,这是因为我国车牌颜色单一,字符直线排列;
  3. 车牌直方图呈现出双峰状的特点,即车牌直方图中可以看到双个波峰;
  4. 车牌具有强边缘信息,这是因为车牌的字符相对集中在车牌的中心,而车牌边缘无字符,因此车牌的边缘信息感较强;
  5. 车牌的字符颜色和车牌背景颜色对比鲜明。目前,我国国内的车牌大致可分为蓝底白字和黄底黑字,特殊用车采用白底黑字或黑底白字,有时辅以红色字体等。为简化处理,本文只考虑蓝底白字的车牌。

三、车牌定位

1. 基本处理

调整尺寸和转灰度图
为了确保输入的车牌图像不能太大或太小,需要对图像进行尺寸调整,一般的照片高宽比是3:4,此次我们限制图像最大宽度为400像素,函数如下:

def resize_img(img):
    """ resize图像 """
    h, w = img.shape[:-1]
    scale = 400 / max(h, w)
    img_resized = cv.resize(img, None, fx=scale, fy=scale, 
                            interpolation=cv.INTER_CUBIC)
    # print(img_resized.shape)
    return img_resized

因图像后续处理输入要求,在此需将图像转为灰度图

img_gray = cv.cvtColor(img_resized, cv.COLOR_BGR2GRAY)
cv.imshow('Gray', img_gray)

效果展示:
在这里插入图片描述

2. 图像降噪

噪声是由一种或者多种原因造成的灰度值的随机变化,通常需要平滑技术(也常称为滤波或者降噪技术)进行抑制或者去除。图像降噪即通过滤波器增强图像中某个波段或频率并阻塞(或降低)其他频率波段。常见的图像滤波方式有均值滤波、高斯滤波、中值滤波、双边滤波等。一般采用高斯滤波来对图像进行降噪。(本案例车牌图像质量较好,此次没有进行此操作)

  img_gaussian = cv.GaussianBlur(img_gray, (3, 3), 0) 
  cv.imshow("Gaussian_Blur2",  img_gaussian)

在这里插入图片描述

3. 灰度拉伸

图像拉伸主要用来改善图像显示的对比度,道路提取流程中往往首先要对图像进行拉伸的预处理。图像拉伸主要有三种方式:灰度拉伸、直方图均衡化和直方图规定化,此次使用灰度拉伸,将灰度值拉伸到整个0-255的区间,那么其对比度显然是大幅增强的。可以用如下的公式来将某个像素的灰度值映射到更大的灰度空间:
在这里插入图片描述
其中Imin,Imax是原始图像的最小灰度值和最大灰度值,MIN和MAX是要拉伸到的灰度空间的灰度最小值和最大值。

def stretching(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)
    img_stretched = img
    return img_stretched

效果展示:
在这里插入图片描述

4. 图像差分

图像差分,就是把两幅图像的对应像素值相减,以削弱图像的相似部分,突出显示图像的变化部分。在进行差分前,需要对图像进行开运算,即先腐蚀后膨胀。

# 进行开运算,去除噪声
r = 14
h = w = r * 2 + 1
kernel = np.zeros((h, w), np.uint8)
cv.circle(kernel, (r, r), r, 1, -1)
# 开运算
img_opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)

效果展示:
在这里插入图片描述
再对开运算前后图像进行差分,使用cv.absdiff函数
效果展示:
在这里插入图片描述

5. 二值化

图像二值化处理就是将图像上点的灰度置为0或255,即整个图像呈现出明显的黑白效果。将256个亮度等级的灰度图像通过适当的阀值选取而获得仍然可以反映图像整体和局部特征的二值化图像。

def binarization(img):
    """ 二值化处理函数 """
    maxi = float(img.max())
    mini = float(img.min())
    x = maxi - ((maxi - mini) / 2)
    # 二值化, 返回阈值ret和二值化操作后的图像img_binary
    ret, img_binary = cv.threshold(img, x, 255, cv.THRESH_BINARY)
    # img_binary = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 5, 2)
    return img_binary

二值化后可以明显看到车牌区域,效果展示:
在这里插入图片描述

6. 边缘检测

边缘检测的目的是找到图像中亮度变化剧烈的像素点构成的集合,表现出来往往是轮廓。边缘检测有很多检测器,其中最常用的是canny边检测器,不容易受到噪声的影响。

def canny(img):
    """ canny边缘检测 """
    img_canny = cv.Canny(img, img.shape[0], img.shape[1])
    return img_canny

效果展示:
在这里插入图片描述

7. 形态学处理

开运算和闭运算是形态学常用的图像处理方式,开运算可以消除亮度较高的细小区域,在纤细点处分离物体,对于较大物体,可以在不明显改变其面积的情况下平滑其边界。闭运算具有填充白色物体内细小黑色空洞的区域、连接临近物体、平滑边界等作用。

def opening_closing(img):
    """ 开闭运算,保留车牌区域,消除其他区域,从而定位车牌 """
    # 进行闭运算
    kernel = np.ones((5, 23), np.uint8)
    img_closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)
    cv.imshow("Closing", img_closing)

    # 进行开运算
    img_opening1 = cv.morphologyEx(img_closing, cv.MORPH_OPEN, kernel)
    cv.imshow("Opening_1", img_opening1)

    # 再次进行开运算
    kernel = np.ones((11, 6), np.uint8)
    img_opening2 = cv.morphologyEx(img_opening1, cv.MORPH_OPEN, kernel)
    return img_opening2

三次运算效果展示:
在这里插入图片描述在这里插入图片描述在这里插入图片描述

8. 定位车牌

先对上一步的图像 ‘img_opening2’ 检测轮廓,使用的是cv.findContours,该函数会返回图像的轮廓信息,然后对轮廓信息进行大小,高宽比,颜色筛选出最符合车牌的矩形轮廓,从而定位车牌区域。

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(original, img):
    """ 定位车牌号 """
    _, contours, hierarchy = cv.findContours(img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    img_cont = original.copy()
    img_cont = cv.drawContours(img_cont, contours, -1, (255, 0, 0), 6)
    cv.imshow("Contours", img_cont)
    # 计算轮廓面积及高宽比
    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])
    # 选出面积最大的五个区域
    block = sorted(block, key=lambda b: b[1])[-5:]

    # 使用颜色识别判断找出最像车牌的区域
    maxweight, maxindex=0, -1
    for i in range(len(block)):
        # print('block', block[i])
        if 2 <= block[i][2] <=4 and 1000 <= block[i][1] <= 20000:    # 对矩形区域高宽比及面积进行限制 
            b = original[block[i][0][1]: block[i][0][3], block[i][0][0]: block[i][0][2]]
            # BGR转HSV
            hsv = cv.cvtColor(b, cv.COLOR_BGR2HSV)
            lower = np.array([100, 50, 50])
            upper = np.array([140, 255, 255])
            # 根据阈值构建掩膜
            mask = cv.inRange(hsv, lower, upper)
            # 统计权值
            w1 = 0
            for m in mask:
                w1 += m / 255
                print(w1)

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

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

    rect = block[maxindex][0]
    return rect

在原图中框出车牌,效果展示:
在这里插入图片描述在这里插入图片描述

四、字符分割

1. 去除上下边缘

将车牌区域从图像中裁剪出来,如下图:

在这里插入图片描述
车牌的上下边界通常都是不规范的,其中拉铆螺母的位置也会干扰字符分割,我们需要去除边缘没用的部分。

def find_waves(threshold, histogram):
    """ 根据设定的阈值和图片直方图,找出波峰,用于分隔字符 """
    up_point = -1    # 上升点
    is_peak = False
    if histogram[0] > threshold:
        up_point = 0
        is_peak = True
    wave_peaks = []
    for i, x in enumerate(histogram):
        if is_peak and x < threshold:
            if i - up_point > 2:
                is_peak = False
                wave_peaks.append((up_point, i))
        elif not is_peak and x >= threshold:
            is_peak = True
            up_point = i
    if is_peak and up_point != -1 and i - up_point > 4:
        wave_peaks.append((up_point, i))
    return wave_peaks
 
 
def remove_upanddown_border(img):
    """ 去除车牌上下无用的边缘部分,确定上下边界 """
    plate_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    ret, plate_binary_img = cv.threshold(plate_gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
    row_histogram = np.sum(plate_binary_img, axis=1)    # 数组的每一行求和
    row_min = np.min(row_histogram)
    row_average = np.sum(row_histogram) / plate_binary_img.shape[0]
    row_threshold = (row_min + row_average) / 2
    wave_peaks = find_waves(row_threshold, row_histogram)
    # 挑选跨度最大的波峰
    wave_span = 0.0
    for wave_peak in wave_peaks:
        span = wave_peak[1] - wave_peak[0]
        if span > wave_span:
            wave_span = span
            selected_wave = wave_peak
    plate_binary_img = plate_binary_img[selected_wave[0]:selected_wave[1], :]
    #cv.imshow("plate_binary_img", plate_binary_img)
    return  plate_binary_img

效果展示:
在这里插入图片描述

2. 分割并保存字符

从左往右开始检测匹配字符,若宽度(end - start)大于5则认为是字符,将其裁剪并保存下来。

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.95*black_max if arg else 0.95*white_max):
            end = m
            break
    return end


def char_segmentation(thresh):
    """ 分割字符 """
    white, black = [], []    # list记录每一列的黑/白色像素总和
    height, width = thresh.shape
    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_max', white_max)
        # print('black_max', black_max)
    # arg为true表示黑底白字,False为白底黑字
    arg = True
    if black_max < white_max:
        arg = False

    # 分割车牌字符
    n = 1
    while n < width - 2:
        n += 1
        # 判断是白底黑字还是黑底白字  0.05参数对应上面的0.95 可作调整
        if (white[n] if arg else black[n]) > (0.05 * white_max if arg else 0.05 * black_max):  # 这点没有理解透彻
            start = n
            end = find_end(start, arg, black, white, width, black_max, white_max)
            n = end
            if end - start > 5 or end > (width * 3 / 7):
                cropImg = thresh[0:height, start-1:end+1]
                # 对分割出的数字、字母进行resize并保存
                cropImg = cv.resize(cropImg, (34, 56))
                cv.imwrite(save_path + '\\{}.bmp'.format(n), cropImg)
                cv.imshow('Char_{}'.format(n), cropImg)

最终分割的字符保存至文件夹中,效果展示:
在这里插入图片描述

五、测试其它图片

测试另外两张汽车正视图,字符分割效果较好,展示如下:

在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述

六、总结

案例思路总结起来是先对图像做预处理,包含调整图像尺寸、转换灰度图、图像降噪、灰度拉伸、图像差分、图像二值化、canny边缘检测、形态学开闭运算,从而定位车牌区域,然后裁剪车牌,去除上下无用边缘部分,最后进行字符分割并将其保存至特定文件夹。

本案例对汽车正视图的车牌定位以及字符分割的效果较为成功,如果图像中车牌有一定的倾斜度以及透视变形,则其中还需对车牌进行倾斜矫正以及透视变换,图像处理也将更为复杂。后续的字符识别只要拥有足够的数据集训练,其过程也与手写数字识别案例一样简单。

七、附上完整代码

import cv2 as cv
import numpy as np

img_path = 'data\\img\\test_005.jpg'
save_path = 'Chars\\test'


def resize_img(img, max_size):
    """ resize图像 """
    h, w = img.shape[0:2]
    scale = max_size / max(h, w)
    img_resized = cv.resize(img, None, fx=scale, fy=scale, 
                            interpolation=cv.INTER_CUBIC)
    # print(img_resized.shape)
    return img_resized


def stretching(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)
    img_stretched = img
    return img_stretched


def absdiff(img):
    """ 对开运算前后图像做差分 """
    # 进行开运算,用来去除噪声
    r = 15
    h = w = r * 2 + 1
    kernel = np.zeros((h, w), np.uint8)
    cv.circle(kernel, (r, r), r, 1, -1)
    # 开运算
    img_opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)
    # 获取差分图
    img_absdiff = cv.absdiff(img, img_opening)
    cv.imshow("Opening", img_opening)
    return img_absdiff  


def binarization(img):
    """ 二值化处理函数 """
    maxi = float(img.max())
    mini = float(img.min())
    x = maxi - ((maxi - mini) / 2)
    # 二值化, 返回阈值ret和二值化操作后的图像img_binary
    ret, img_binary = cv.threshold(img, x, 255, cv.THRESH_BINARY)
    # img_binary = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 5, 2)
    # 返回二值化后的黑白图像
    return img_binary


def canny(img):
    """ canny边缘检测 """
    img_canny = cv.Canny(img, img.shape[0], img.shape[1])
    return img_canny


def opening_closing(img):
    """ 开闭运算,保留车牌区域,消除其他区域,从而定位车牌 """
    # 进行闭运算
    kernel = np.ones((5, 23), np.uint8)
    img_closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)
    cv.imshow("Closing", img_closing)

    # 进行开运算
    img_opening1 = cv.morphologyEx(img_closing, cv.MORPH_OPEN, kernel)
    cv.imshow("Opening_1", img_opening1)

    # 再次进行开运算
    kernel = np.ones((11, 6), np.uint8)
    img_opening2 = cv.morphologyEx(img_opening1, cv.MORPH_OPEN, kernel)
    return img_opening2


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(original, img):
    """ 定位车牌号 """
    _, contours, hierarchy = cv.findContours(img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    img_cont = original.copy()
    img_cont = cv.drawContours(img_cont, contours, -1, (255, 0, 0), 6)
    cv.imshow("Contours", img_cont)
    # 计算轮廓面积及高宽比
    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])
    # 选出面积最大的五个区域
    block = sorted(block, key=lambda bl: bl[1])[-5:]

    # 使用颜色识别判断找出最像车牌的区域
    maxweight, maxindex=0, -1
    for i in range(len(block)):
        # print('block', block[i])
        if 2 <= block[i][2] <=4 and 1000 <= block[i][1] <= 20000:    # 对矩形区域高宽比及面积进行限制 
            b = original[block[i][0][1]: block[i][0][3], block[i][0][0]: block[i][0][2]]
            # BGR转HSV
            hsv = cv.cvtColor(b, cv.COLOR_BGR2HSV)
            lower = np.array([100, 50, 50])
            upper = np.array([140, 255, 255])
            # 根据阈值构建掩膜
            mask = cv.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

    rect = block[maxindex][0]
    return rect


def preprocessing(img):
    # resize图像至300 * 400
    img_resized = resize_img(img, 400)
    cv.imshow('Original', img_resized)
    # 转灰度图
    img_gray = cv.cvtColor(img_resized, cv.COLOR_BGR2GRAY)
    cv.imshow('Gray', img_gray)
    # 高斯滤波
    # img_gaussian = cv.GaussianBlur(img_gray, (3,3), 0)
    # cv.imshow("Gaussian_Blur", img_gaussian)
    # 灰度拉伸,提升图像对比度
    img_stretched = stretching(img_gray)
    cv.imshow('Stretching', img_stretched)
    # 差分开运算前后图像
    img_absdiff = absdiff(img_stretched)
    cv.imshow("Absdiff", img_absdiff)
    # 图像二值化
    img_binary = binarization(img_absdiff)
    cv.imshow('Binarization', img_binary)
    # 边缘检测
    img_canny = canny(img_binary)
    cv.imshow("Canny", img_canny)
    # 开闭运算,保留车牌区域,消除其他区域
    img_opening2 = opening_closing(img_canny)
    cv.imshow("Opening_2", img_opening2)
    # 定位车牌号所在矩形区域
    rect = locate_license(img_resized, img_opening2)
    print("rect:", rect)  
    # 框出并显示车牌
    img_copy = img_resized.copy()
    cv.rectangle(img_copy, (rect[0], rect[1]), (rect[2], rect[3]), (0, 255, 0), 2)
    cv.imshow('License', img_copy)
    return rect, img_resized


def cut_license(original, rect):
    """ 裁剪车牌 """
    license_img = original[rect[1]:rect[3], rect[0]:rect[2]]
    return license_img


def find_waves(threshold, histogram):
    """ 根据设定的阈值和图片直方图,找出波峰,用于分隔字符 """
    up_point = -1    # 上升点
    is_peak = False
    if histogram[0] > threshold:
        up_point = 0
        is_peak = True
    wave_peaks = []
    for i, x in enumerate(histogram):
        if is_peak and x < threshold:
            if i - up_point > 2:
                is_peak = False
                wave_peaks.append((up_point, i))
        elif not is_peak and x >= threshold:
            is_peak = True
            up_point = i
    if is_peak and up_point != -1 and i - up_point > 4:
        wave_peaks.append((up_point, i))
    return wave_peaks
 
 
def remove_upanddown_border(img):
    """ 去除车牌上下无用的边缘部分,确定上下边界 """
    plate_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    ret, plate_binary_img = cv.threshold(plate_gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
    row_histogram = np.sum(plate_binary_img, axis=1)    # 数组的每一行求和
    row_min = np.min(row_histogram)
    row_average = np.sum(row_histogram) / plate_binary_img.shape[0]
    row_threshold = (row_min + row_average) / 2
    wave_peaks = find_waves(row_threshold, row_histogram)
    # 挑选跨度最大的波峰
    wave_span = 0.0
    selected_wave = []
    for wave_peak in wave_peaks:
        span = wave_peak[1] - wave_peak[0]
        if span > wave_span:
            wave_span = span
            selected_wave = wave_peak
    plate_binary_img = plate_binary_img[selected_wave[0]:selected_wave[1], :]
    return  plate_binary_img


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.95*black_max if arg else 0.95*white_max):
            end = m
            break
    return end


def char_segmentation(thresh):
    """ 分割字符 """
    white, black = [], []    # list记录每一列的黑/白色像素总和
    height, width = thresh.shape
    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_max', white_max)
        # print('black_max', black_max)
    # arg为true表示黑底白字,False为白底黑字
    arg = True
    if black_max < white_max:
        arg = False

    # 分割车牌字符
    n = 1
    while n < width - 2:
        n += 1
        # 判断是白底黑字还是黑底白字  0.05参数对应上面的0.95 可作调整
        if (white[n] if arg else black[n]) > (0.05 * white_max if arg else 0.05 * black_max):  # 这点没有理解透彻
            start = n
            end = find_end(start, arg, black, white, width, black_max, white_max)
            n = end
            if end - start > 5 or end > (width * 3 / 7):
                cropImg = thresh[0:height, start-1:end+1]
                # 对分割出的数字、字母进行resize并保存
                cropImg = cv.resize(cropImg, (34, 56))
                cv.imwrite(save_path + '\\{}.bmp'.format(n), cropImg)
                cv.imshow('Char_{}'.format(n), cropImg)


def main():
    # 读取图像
    image = cv.imread(img_path)
    # 图像预处理,返回img_resized和定位的车牌矩形区域
    rect, img_resized = preprocessing(image)
    # 裁剪出车牌
    license_img = cut_license(img_resized, rect)
    cv.imshow('License', license_img)
    # 去除车牌上下无用边缘
    plate_b_img = remove_upanddown_border(license_img)
    cv.imshow('plate_binary', plate_b_img)
    # 字符分割,保存至文件夹
    char_segmentation(plate_b_img)
    cv.waitKey(0)
    cv.destroyAllWindows()


if __name__ == '__main__':
    main()
  • 38
    点赞
  • 279
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
车牌字符分割是图像识别领域的一个重要问题,使用OpenCV可以实现车牌字符分割。下面我简单介绍一下车牌字符分割的步骤: 1. 车牌定位:采用Haar特征分类器或者Adaboost分类器进行车牌定位,将车牌从图像中分离出来。 2. 预处理:对车牌图像进行预处理,去除噪声和干扰,增强车牌字符的对比度。 3. 字符分割:采用图像处理算法对车牌字符进行分割,得到单个字符的图像。 4. 字符识别:对每个字符进行识别,得到车牌号码。 下面是代码示例: ``` import cv2 import numpy as np # 车牌字符分割 def plate_segmentation(img): # 灰度化 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 二值化 ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 膨胀操作 kernel = np.ones((3, 3), np.uint8) dilation = cv2.dilate(thresh, kernel, iterations=1) # 查找轮廓 contours, hierarchy = cv2.findContours(dilation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 获取字符区域 rect_list = [] for contour in contours: rect = cv2.boundingRect(contour) x, y, w, h = rect if w > h: rect_list.append(rect) # 对字符区域进行排序 rect_list.sort() # 存储字符图像 char_img_list = [] for rect in rect_list: x, y, w, h = rect char_img = img[y:y + h, x:x + w] char_img_list.append(char_img) return char_img_list ``` 这是一个简单的车牌字符分割实现,首先将车牌图像进行灰度化和二值化处理,然后进行膨胀操作,接着查找轮廓,获取字符区域,对字符区域进行排序,最后存储字符图像。需要注意的是,在实际应用中,需要根据具体情况对车牌字符分割算法进行优化和改进,以提高分割准确率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阁不鸽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值