Digital Image Processing -- License Plate Recognition

Digital Image Processing – License Plate Recognition

main content

Realize license plate recognition

Algorithm process

In this paper, the specific process design and algorithm use of license plate recognition are mainly divided into the following steps.
1. Read the source license plate image.
2. Preprocessing the original license plate image: grayscale, using a filter based on geometric operations (opening operation) to eliminate glitch noise.
3. Binarization operation.
4. Use the canny edge detection algorithm to eliminate small areas and retain large areas.
5. Determine and locate the license plate position through color recognition.
6. Use mask processing to segment the image after the license plate is located, and directly segment the license plate.
7. Binarize the license plate again.
8. Split characters.
9. Carry out neural network model matching on the segmented characters, and output the license plate string.
According to the above experimental design steps, the experimental flow chart is made, as shown in the figure below.
insert image description here

source code

upper code

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import font_manager
import ddddocr


#读取字体文件
font = font_manager.FontProperties(fname=r".\OPPOSans-Heavy.ttf")


class Get_license():

    #图像拉伸函数
    def stretch(self, 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(self, 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(self, 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(self, img, afterimg):

        contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        #找出最大的三个区域
        block = []
        for c in contours:
            #找出轮廓的左上点和右下点
            #由此计算它的面积和长度比
            r = self.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(self, 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 = self.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 = self.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 = self.locate_license(openingimg, img)

        return rect, img

    #图像分割函数
    def cut_license(self, afterimg, rect):

        #转换为宽度和高度
        rect[2] = rect[2] - rect[0]
        rect[3] = rect[3] - rect[1]
        rect_copy = tuple(rect.copy())
        #创建掩膜
        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]
        #cv2.imshow('111',img_show)
        return img_show


class Segmentation():
    def __init__(self, cutimg):

        #1、读取图像,并把图像转换为灰度图像并显示
        # cutimg = cv2.imread("3.jpg")  #读取图片
        img_gray = cv2.cvtColor(cutimg, cv2.COLOR_BGR2GRAY)  #转换了灰度化
        #cv2.imshow('gray', img_gray)   #显示图片
        #cv2.waitKey(0)

        #2、将灰度图像二值化,设定阈值是100
        self.img_thre = img_gray
        cv2.threshold(img_gray, 100, 255, cv2.THRESH_BINARY_INV, self.img_thre)

        #cv2.waitKey(0)

        #3、保存黑白图片
        cv2.imwrite('thre_res.png', self.img_thre)

        #4、分割字符
        self.white = []               #记录每一列的白色像素总和
        self.black = []               #黑色
        self.height = self.img_thre.shape[0]
        self.width = self.img_thre.shape[1]
        self.white_max = 0
        self.black_max = 0
        #计算每一列的黑白色像素总和
        for i in range(self.width):
            s = 0                     #这一列白色总数
            t = 0                     #这一列黑色总数
            for j in range(self.height):
                if self.img_thre[j][i] == 255:
                    s += 1
                if self.img_thre[j][i] == 0:
                    t += 1
            self.white_max = max(self.white_max, s)
            self.black_max = max(self.black_max, t)
            self.white.append(s)
            self.black.append(t)

        self.arg = False                #False表示白底黑字;True表示黑底白字
        if self.black_max > self.white_max:
            self.arg = True

    def heibai(self):
        return self.img_thre


    def find_end(self, start_):
        end_ = start_ + 1
        for m in range(start_ + 1, self.width - 1):
            if (self.black[m] if self.arg else self.white[m]) > (
            0.85 * self.black_max if self.arg else 0.85 * self.white_max):
                end_ = m
                break
        return end_

    def display(self):
        #img_list = []
        n = 1
        plt.figure()
        img_num = 0
        while n < self.width - 2:
            n += 1
            if (self.white[n] if self.arg else self.black[n]) > (
            0.15 * self.white_max if self.arg else 0.15 * self.black_max):
                #上面这些判断用来辨别是白底黑字还是黑底白字

                start = n
                end = self.find_end(start)
                n = end

                if end - start > 5:
                    cj = self.img_thre[1:self.height, start:end]
                    img_num += 1
                    cj = cv2.cvtColor(cj, cv2.COLOR_RGB2BGR)

                    plt.figure(2)
                    plt.subplot(2, 4, img_num)
                    plt.title('{}'.format(img_num))
                    plt.imshow(cj)
        plt.show()
        return self.img_thre


if __name__ == '__main__':

    #读取源图像
    img = cv2.imread('8.png')
    img1 = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)

    #绘图
    plt.figure(1)
    plt.suptitle('车牌识别',fontproperties=font)
    plt.subplot(2, 3, 1)
    plt.title('原始图像', fontproperties=font)
    plt.imshow(img1)

    #预处理图像
    license = Get_license()
    rect, afterimg = license.find_license(img)
    afterimg = cv2.cvtColor(afterimg, cv2.COLOR_RGB2BGR)

    plt.subplot(2, 3, 2)
    plt.title('预处理后图像', fontproperties=font)
    plt.imshow(afterimg)

    #车牌号打框
    cv2.rectangle(afterimg, (rect[0], rect[1]), (rect[2], rect[3]), (0, 255, 0), 1)
    x1, y1, x2, y2 = int(rect[0]), int(rect[1]), int(rect[2]), int(rect[3])
    print('车牌框出:',x1, x2, y1, y2)
    plt.subplot(2, 3, 3)
    plt.title('车牌框出', fontproperties=font)
    plt.imshow(afterimg)

    #背景去除
    cutimg = license.cut_license(afterimg, rect)
    plt.subplot(2, 3, 4)
    plt.title('车牌背景去除', fontproperties=font)
    plt.imshow(cutimg)
    # print(int(_rect[0]), int(_rect[3]), int(_rect[2]), int(_rect[1]))
    print('车牌背景去除',x1, y1, x2, y2)

    #开始分割车牌
    # cutimg = cutimg[140:165, 151:240]
    cutimg = cutimg[y1 + 3:y2 - 3, x1 - 1:x2 - 3]
    # cutimg = cutimg[int(_rect[0]):int(_rect[3]),int(_rect[2]):int(_rect[1])]
    print('cutimg:',cutimg)
    height, width = cutimg.shape[:2]
    cutimg1 = cv2.resize(cutimg, (2 * width, 2 * height), interpolation=cv2.INTER_CUBIC)
    plt.subplot(2, 3, 5)
    plt.title('分割车牌与背景', fontproperties=font)
    plt.imshow(cutimg)

    #字符切割
    seg = Segmentation(cutimg)
    plt.subplot(2, 3, 6)
    img_hei = seg.heibai()
    img_hei = cv2.cvtColor(img_hei, cv2.COLOR_RGB2BGR)
    plt.title('车牌二值化处理', fontproperties=font)
    plt.imshow(img_hei)
    seg.display()
    plt.show()

    #打印车牌
    ocr = ddddocr.DdddOcr()
    with open('thre_res.png', 'rb') as f:
        image = f.read()
    res = ocr.classification(image)
    print(res)



achieve results

Select the license plate photo and successfully complete the recognition of the license plate number. As shown in the figure below, it is the image display after the original image to the final segmentation of the license plate and the background.

insert image description here
Next, character segmentation is performed on the binarized image, and the result is shown in the figure below.
insert image description here
Finally, each character is matched by utilizing the ddddocr image recognition library. Thus, the conversion of character pictures to text is realized, and the license plate number is successfully printed out.
insert image description here
Note: After selecting other photos, you may need to fine-tune the parameter values ​​​​in the code (the license plate number is boxed). It's just that the pictures are a bit limited. I hope that some small partners will come up with more generalized code implementations in the future! ! !

Guess you like

Origin blog.csdn.net/MZYYZT/article/details/128212029