车牌识别系统

读代码就像看电影~~细品哈

                                       just  do  it ! ! !

介绍(非常简单哈)

	输入:图片
	处理:用scikit-image 和Opencv-python识别图片中的车牌号
	输出:将车牌号保存到json文件中

功能(utils.py~其实就是一堆函数)

sort_comes(corns,img) 实现透视变换
perspective() 从指定的图像中裁剪车牌
div_func() 对裁剪的车牌实现颜色转换
divide_tab() 用数字和字母在线段上划分裁剪区域,返回包含字母和数字的列表
recognize() 识别裁剪区域的每个字母和数字,在识别每个字母时,会参考图像字符的概率,找到可能性最大的字母并将它附加到列表中
help_perform() 对指定图像实现颜色转换,检测每一个部分是否有四个角,然后计算角之间的距离,并处以这些值以确定是否为一个车牌号
perform_processing() 调用上面的功能函数实现读取图像裁剪识别等功能

主程序(调用而已)

main.py 功能是调用文件util.py中的功能函数,实现车牌识别功能

  1. 首先使用函数 add_argument()
    添加两个python命令参数mages_dir result_file
    然后根据用户设置的 images_dir逐一读取images_dir的目录中的图片
  2. 实现车牌识别(重点)
  3. 将结果保存到 resulits_file中

函数调用顺序(就是图片中的车牌子如何被识别成数字的流程)

perform_processing 被调用,开始车牌识别流程。
help_perform 被多次调用,尝试找到车牌区域。
如果help_perform成功,sort_corners被调用,对车牌进行排序。
perspective 根据排序结果裁剪车牌区域。
divide_tab 调用div_func,分割车牌中的字符。
recognize 识别每个字符,构建车牌号码。

开始读代码(细品哈)

perform_processing() 调用上面的功能函数实现读取图像裁剪识别等功能

# 主要功能->读取图像、处理等。
def perform_processing(image: np.ndarray, ref) -> str:
    th = 135
    win = 3
    wrapped_tab = None
    for i in range(5):
        wrapped_tab = help_perform(image, th=th, wind=win)
        th -= 5
        win += 6
        if wrapped_tab is not None:
            break
    # 如果没有找到任何内容,请返回问号
    if wrapped_tab is None:
        return '???????'
    try:
        single = divide_tab(wrapped_tab, th=115)
        resu = recognize(single, ref)
        if len(resu) == 7:
            return resu
        elif len(resu) > 7:
            x = len(resu) - 7
            return resu[x:]
        elif len(resu) == 0:
            return '???????'
        elif 7 > len(resu) > 0:
            x = 7 - len(resu)
            for i in range(x):
                resu = '?' + resu[0:]
            return resu
    except NameError:
        return '???????'

    return '???????'

help_perform() 对指定图像实现颜色转换,检测每一个部分是否有四个角,然后计算角之间的距离,并处以这些值以确定是否为一个车牌号

def help_perform(image, th=135, wind=3):
    wrapped_tab = None
    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    w = image.shape[1]
    h = image.shape[0]
    blurred = cv2.GaussianBlur(img_gray, (wind, wind), 3)
    _, thresh = cv2.threshold(blurred, th, 255, cv2.THRESH_BINARY_INV)

    cont, _ = cv2.findContours(thresh, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
    hull_list = []

    for i in range(len(cont)):
        h = cv2.convexHull(cont[i])
        hull_list.append(h)

    contours = sorted(hull_list, key=cv2.contourArea, reverse=True)[:8]

    for c in contours:
        clos = cv2.arcLength(c, True)
        apr = cv2.approxPolyDP(c, 0.05 * clos, True)
        cnt = 0
        nums = []
        points_arr = []

        # 检查是否有4个角
        if len(apr) == 4:
            for i in range(4):
                j = i + 1
                if j == 4:
                    j = 0
                k = i - 1
                if k == -1:
                    k = 3

                # 计算角点之间的距离,然后除以这些值,以确定这是否是一个车牌号
                w_len_1 = sqrt((apr[i][0][0] - apr[j][0][0]) ** 2 + (apr[i][0][1] - apr[j][0][1]) ** 2)
                w_len_2 = sqrt((apr[i][0][0] - apr[k][0][0]) ** 2 + (apr[i][0][1] - apr[k][0][1]) ** 2)

                if w_len_1 > w_len_2:
                    if w_len_1 / w_len_2 < 3 or w_len_1 / w_len_2 > 7.3 or w_len_1 >= w:
                        continue
                else:
                    if w_len_2 / w_len_1 < 3 or w_len_2 / w_len_1 > 7.3 or w_len_2 >= w:
                        continue

                # 下一个判断条件是确定这是否是车牌号
                if w_len_1 >= w / 3 or w_len_2 >= w / 3:
                    cnt += 1

                    points_arr.append(apr[i][0])
                if cnt == 2:
                    nums.append(apr)

        if len(nums) == 1:
            wrapped_tab = sort_cornes(points_arr, image)

    return wrapped_tab

sort_comes(corns,img) 实现透视变换

# 透视变换,实现排序[ul, ur, bl, br]
def sort_cornes(corns, img):
    sorted_arr = []
    image_corns = []
    upper_right = [img.shape[1], 0]
    upper_left = [0, 0]
    bottom_right = [img.shape[1], img.shape[0]]
    bottom_left = [0, img.shape[0]]
    image_corns.append(upper_left)
    image_corns.append(upper_right)
    image_corns.append(bottom_left)
    image_corns.append(bottom_right)
    order = []
    ord = 0

    #计算车牌角和整个图像角之间的距离,找出[ul,ur,bl,br] 中的字符
    for ind, val in enumerate(image_corns):
        lowest_dist = sqrt(img.shape[1]**2 + img.shape[0]**2)
        for i, v in enumerate(corns):
            dist = sqrt((val[0] - v[0])**2 + (val[1] - v[1])**2)
            if dist < lowest_dist:
                lowest_dist = dist
                ord = i
        order.append(ord)

    for o in order:
        sorted_arr.append(corns[o])

    # 一旦我们有了一个排序列表,我们就可以从图像中裁剪出车牌号
    return perspective(sorted_arr, img)

perspective() 从指定的图像中裁剪车牌

def perspective(arr, img, width=1000, height=250):
    p1 = np.float32([arr[0], arr[1], arr[2], arr[3]])
    p2 = np.float32([[0, 0], [width, 0], [0, height], [width, height]])

    get_tf = cv2.getPerspectiveTransform(p1, p2)
    persp = cv2.warpPerspective(img, get_tf, (width, height))
    return persp

div_func() 对裁剪的车牌实现颜色转换

# divide_tab的帮助函数
def div_func(image, th=115):
    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(img_gray, (5, 5), 0)
    _, thresh = cv2.threshold(blurred, th, 255, cv2.THRESH_BINARY_INV)

    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = sorted(contours, key=cv2.contourArea, reverse=True)
    boxes = []

    for c in contours:
        (x, y, w, h) = cv2.boundingRect(c)
        #检查高度和宽度是否合理
        if h > 150 and (w > 15 and w < 200):
            cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 0), 1)
            boxes.append([[x, y], [x + w, y], [x, y + h], [x + w, y + h]])
    return boxes

divide_tab() 用数字和字母在线段上划分裁剪区域,返回包含字母和数字的列表

def divide_tab(image, th=115):
    temp_img = image.copy()
    bboxes = div_func(image, th)

    if len(bboxes) < 7:
        th += 10
        if th >= 30:
            bboxes = div_func(temp_img, th)

    bboxes.sort()
    num_let_arr = []

    for b in bboxes:
        # 裁剪的单个值
        num_let = perspective(b, image, width=120, height=220)
        num_let_arr.append(num_let)

    # 返回包含字母和数字的列表
    return num_let_arr

recognize() 识别裁剪区域的每个字母和数字,在识别每个字母时,会参考图像字符的概率,找到可能性最大的字母并将它附加到列表中

# 识别每个字母和数字
def recognize(letters, im_paths):

    reference = []
    names = []
    # 读取参考图像并将其添加到数组(同时添加名称)
    for image_path in im_paths:
        image = cv2.imread(str(image_path), 0)
        reference.append(image)
        names.append(image_path.name[:1])

    text_arr = []

    #对于每个字母,计算参考图像的概率(所有字母和数字)
    for let in letters:
        probabilites = []
        letters_arr = []
        img_gray = cv2.cvtColor(let, cv2.COLOR_BGR2GRAY)
        blurred = cv2.GaussianBlur(img_gray, (3, 3), 0)
        ret, thresh = cv2.threshold(blurred, 135, 255, cv2.THRESH_BINARY)

        for ind, im in enumerate(reference):
            im = cv2.resize(im, (thresh.shape[1], thresh.shape[0]))
            result, _ = compare_ssim(thresh, im, full=True)
            letter = names[ind]
            probabilites.append(result)
            letters_arr.append(letter)

        # 找到概率最大的字母并将其附加到列表中
        max_val = max(probabilites)

        for ind, val in enumerate(probabilites):
            if max_val == val:
                if letters_arr[ind] != 'w' and letters_arr[ind] != 'c' and letters_arr[ind] != 'r':
                    text_arr.append(letters_arr[ind])
    return ''.join(text_arr)

main.py

import argparse
import json
from pathlib import Path
import cv2
import os
from processing.utils import perform_processing


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('images_dir', type=str)
    parser.add_argument('results_file', type=str)
    args = parser.parse_args()

    im_dir = Path('./numbers_letters/')
    im_paths = sorted([im_path for im_path in im_dir.iterdir() if im_path.name.endswith('.jpeg')])

    images_dir = Path(args.images_dir)
    results_file = Path(args.results_file)

    images_paths = sorted([image_path for image_path in images_dir.iterdir() if image_path.name.endswith('.jpg')])
    results = {}
    for image_path in images_paths:
        image = cv2.imread(str(image_path))
        if image is None:
            print(f'Error loading image {image_path}')
            continue

        results[image_path.name] = perform_processing(image, im_paths)
    print(os.path.abspath(images_dir))
    print(os.path.abspath(results_file))
    with results_file.open('w') as output_file:
        json.dump(results, output_file, indent=4)


if __name__ == '__main__':
    main()

To be continued…

	关注我,后续带来识别过程背后的数学原理,一起看透识别的本质  !!!
  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值