python+opencv生成较真实的车牌号码图片

news2024/10/2 1:51:07

本文参考github代码:https://github.com/loveandhope/license-plate-generator

 效果:

 

一、代码目录结构:

font目录下存放车牌中文、字符的ttf字体

images目录下存放蓝色底牌、新能源绿色底牌、污渍(噪声)的图片

完整代码可参考:https://download.csdn.net/download/benben044/87546578?spm=1001.2014.3001.5503

二、生成流程

本代码可以根据车牌list生成对应的车牌图片list。

(1)生成白底黑字的车牌号码图片

首先,生成一个白底的空白车牌号图片

img = np.array(Image.new("RGB", (880, 280), (255, 255, 255)))

然后,逐字(字符)生成图片

ImageDraw.Draw(img).text((0, self.height_offset), char, self.fg_color, font=self.font_en)

最后,将上一步生成的图片逐个复制到上上一步步生成的图片中

img[:, char_width_start:char_width_end] = self.generate_char_image(plate_num[i])

因为生成的图片和img的第一维(height)大小相同,所以在img中直接使用符号":"。

(2)生成车牌底牌

直接读取底牌的图片即可。

plate_image = cv2.imread(LicensePlateImageGenerator.single_blue_plate_bg)

(3)生成最后的车牌图片(以蓝牌为例)

首先,将文字图片转为黑底白字

img = cv2.bitwise_not(char_img)

此时,背景部分值为0,字部分值为255。

然后,将黑底背景变为蓝色底牌背景

img = cv2.bitwise_or(img, template_image)

此时,黑色背景值0 与 蓝色背景值x进行二进制的or操作,只保留了蓝色背景值,实现了背景的替换。

(4)数据增强(增加噪声)

高斯模糊:

通过cv2.blur() 方法实现

高斯噪声:

    def add_single_channel_noise(self, single):
        """ 添加高斯噪声
        :param single: 单一通道的图像数据
        """
        diff = 255 - single.max()
        noise = np.random.normal(0, 1 + self.rand_reduce(6), single.shape)
        noise = (noise - noise.min()) / (noise.max() - noise.min())
        noise = diff * noise
        noise = noise.astype(np.uint8)
        dst = single + noise
        return dst

添加污渍:

通过cv2.bitwise_not() 和cv2.bitwise_and()操作完成

三、python代码:

import os
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw
import numpy as np
import cv2
import random

class CharsImageGenerator(object):
    """生成字符图像,背景为白色,字体为黑色"""
    # 数字和英文字母列表
    numerals = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
                'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
                'U', 'V', 'W', 'X', 'Y', 'Z']

    def __init__(self, plate_type):
        self.plate_type = plate_type
        # 字符图片参数
        self.font_ch = ImageFont.truetype("./font/platech.ttf", 180, 0)  # 中文字体格式
        self.font_en = ImageFont.truetype('./font/platechar.ttf', 240, 0)  # 英文字体格式
        self.bg_color = (255, 255, 255)  # 车牌背景颜色
        self.fg_color = (0, 0, 0)  # 车牌号的字体颜色

        self.plate_height = 280  # 车牌高度
        self.left_offset = 32  # 车牌号左边第一个字符的偏移量
        self.height_offset = 10  # 高度方向的偏移量
        self.char_height = 180  # 字符高度
        self.chinese_original_width = 180  # 中文字符原始宽度
        self.english_original_width = 90  # 非中文字符原始宽度
        if plate_type in ['single_blue', 'single_yellow']:
            self.char_num = 7
            self.char_width = 90  # 字符校正后的宽度
            self.plate_width = 880  # 车牌的宽度
            self.char_interval = 24  # 字符间的间隔
            self.point_size = 20  # 第2个字符与第三个字符间有一个点,该点的尺寸
        elif plate_type == 'small_new_energy':
            self.char_num = 8
            self.first_char_width = 90  # 第一个字符校正后的宽度
            self.char_width = 86  # 其余字符校正后宽度
            self.plate_width = 960  # 车牌的宽度
            self.char_interval = 18  # 字符间的间隔
            self.point_size = 62  # 第2个字符与第三个字符间有一个点,该点的尺寸
        else:
            raise ValueError('目前不支持该类型车牌!')

    def generate_images(self, plate_num_str_list):
        if self.plate_type in ['single_blue', 'single_yellow', ]:
            plate_images = self.generate_440_140_plate(plate_num_str_list)
        elif self.plate_type == 'small_new_energy':
            plate_images = self.generate_480_140_plate(plate_num_str_list)
        else:
            raise ValueError('该类型车牌目前功能尚未完成!')

        return plate_images

    def generate_440_140_plate(self, plate_num_str_list):
        """ 生成440 * 140尺寸的7位车牌字符图片
        :param plate_nums:
        :return:
        """
        plate_images = list()
        for plate_num in plate_num_str_list:
            # 创建空白车牌号图片
            img = np.array(Image.new("RGB", (self.plate_width, self.plate_height), self.bg_color))
            # 每个字符的x轴起始、终止位置
            char_width_start = self.left_offset
            char_width_end = char_width_start + self.char_width
            img[:, char_width_start:char_width_end] = self.generate_char_image(
                plate_num[0])  # 生成的图片和img的第一维大小相同,所以在img中直接使用符号":"

            char_width_start = char_width_end + self.char_interval
            char_width_end = char_width_start + self.char_width
            img[:, char_width_start:char_width_end] = self.generate_char_image(plate_num[1])
            # 隔开特殊间隙,继续添加车牌的后续车牌号
            char_width_end = char_width_end + self.point_size + self.char_interval
            for i in range(2, len(plate_num)):
                char_width_start = char_width_end + self.char_interval
                char_width_end = char_width_start + self.char_width
                img[:, char_width_start:char_width_end] = self.generate_char_image(plate_num[i])

            plate_images.append(img)
            # chars_image debug
            # cv2.imshow("chars_image debug", img)
            # cv2.waitKey()
        return plate_images

    def generate_char_image(self, char):
        """ 生成字符图片
        :param char: 字符
        :return:
        """
        # 根据是否中文字符,选择生成模式
        if char in CharsImageGenerator.numerals or char in CharsImageGenerator.alphabet:
            img = self.generate_en_char_image(char)
        else:
            img = self.generate_ch_char_image(char)
        return img

    def generate_ch_char_image(self, char):
        """ 生成中文字符图片
        :param char: 待生成的中文字符
        """
        img = Image.new("RGB", (self.chinese_original_width, self.plate_height), self.bg_color)
        ImageDraw.Draw(img).text((0, self.height_offset), char, self.fg_color, font=self.font_ch)
        img = img.resize((self.char_width, self.plate_height))
        return np.array(img)

    def generate_en_char_image(self, char):
        """" 生成英文字符图片
        :param char: 待生成的英文字符
        """
        img = Image.new("RGB", (self.english_original_width, self.plate_height), self.bg_color)
        ImageDraw.Draw(img).text((0, self.height_offset), char, self.fg_color, font=self.font_en)
        img = img.resize((self.char_width, self.plate_height))
        return np.array(img)


class LicensePlateImageGenerator(object):
    """根据车牌类型生成底牌图片"""
    single_blue_plate_bg = './images/single_blue.bmp'
    small_new_energy_plate_bg = './images/small_new_energy.jpg'

    def __init__(self, plate_type):
        self.plate_type = plate_type

        if plate_type == 'single_blue':
            plate_image = cv2.imread(LicensePlateImageGenerator.single_blue_plate_bg)
        elif plate_type == 'small_new_energy':
            plate_image = cv2.imread(LicensePlateImageGenerator.small_new_energy_plate_bg)
        else:
            raise ValueError('该类型车牌目前功能尚未完成!')

        # template_image debug
        # cv2.imshow("template_image debug", plate_image)
        # cv2.waitKey()

        self.bg = plate_image

    def generate_template_image(self, width, height):
        return cv2.resize(self.bg, (width, height))


class ImageAugmentation(object):
    """图像增强操作:HSV变化,添加背景,高斯噪声,污渍"""

    def __init__(self, plate_type, template_image):
        self.plate_type = plate_type
        # 确定字符颜色是否应该为黑色
        if plate_type == 'single_blue':
            # 字符为白色
            self.is_black_char = False
        elif plate_type in ['single_yellow', 'small_new_energy']:
            # 字符为黑字
            self.is_black_char = True
        else:
            raise ValueError('暂时不支持该类型车牌')
        self.template_image = template_image
        # 色调,饱和度,亮度
        self.hue_keep = 0.8
        self.saturation_keep = 0.3
        self.value_keep = 0.2
        # 自然环境照片的路径列表
        self.env_data_paths = ImageAugmentation.search_file("background")
        # 高斯噪声level
        self.level = 1 + ImageAugmentation.rand_reduce(4)
        # 污渍
        self.smu = cv2.imread("images/smu.jpg")

    @staticmethod
    def search_file(search_path, file_format='.jpg'):
        """在指定目录search_path下,递归目录搜索指定尾缀的文件"""
        file_path_list = []
        for root_path, dir_names, file_names in os.walk(search_path):
            for filename in file_names:
                if filename.endswith(file_format):
                    file_path_list.append(os.path.join(root_path, filename))
        return file_path_list

    @staticmethod
    def rand_reduce(val):
        return int(np.random.random() * val)

    def add_gauss(self, img, level=None):
        """ 添加高斯模糊
        :param img: 待加噪图片
        :param level: 加噪水平
        """
        if level is None:
            level = self.level
        return cv2.blur(img, (level * 2 + 1, level * 2 + 1))

    def add_single_channel_noise(self, single):
        """ 添加高斯噪声
        :param single: 单一通道的图像数据
        """
        diff = 255 - single.max()
        noise = np.random.normal(0, 1 + self.rand_reduce(6), single.shape)
        noise = (noise - noise.min()) / (noise.max() - noise.min())
        noise = diff * noise
        noise = noise.astype(np.uint8)
        dst = single + noise
        return dst

    def add_noise(self, img):
        """添加噪声"""
        img[:, :, 0] = self.add_single_channel_noise(img[:, :, 0])
        img[:, :, 1] = self.add_single_channel_noise(img[:, :, 1])
        img[:, :, 2] = self.add_single_channel_noise(img[:, :, 2])
        return img

    def add_smudge(self, img, smu=None):
        """添加污渍"""
        if smu is None:
            smu = self.smu
        # 截取某一部分
        rows = self.rand_reduce(smu.shape[0] - img.shape[0])
        cols = self.rand_reduce(smu.shape[1] - img.shape[1])
        add_smu = smu[rows:rows + img.shape[0], cols:cols + img.shape[1]]
        img = cv2.bitwise_not(img)
        img = cv2.bitwise_and(add_smu, img)
        img = cv2.bitwise_not(img)
        return img

    def augment(self, img):
        """
        综合上面的加载操作,进行全流程加噪
        :param img:
        :return:
        """
        if not self.is_black_char:
            # 转为黑底白字
            img = cv2.bitwise_not(img)
            img = cv2.bitwise_or(img, self.template_image)
        else:
            # 底牌加车牌文字
            img = cv2.bitwise_and(img, self.template_image)

        img = self.add_gauss(img)
        img = self.add_noise(img)
        img = self.add_smudge(img)
        return img

class LicensePlateGenerator(object):

    @staticmethod
    def generate_license_plate_images(plate_type, plate_num_str_list, save_path):
        """
        生成特定类型的的车牌图片,并保存到指定目录下
        :param plate_type: 车牌类型
        :param plate_num_str_list: 车牌号码列表
        :param save_path: 文件根目录
        :return:
        """

        save_path = os.path.join(save_path, plate_type)
        if not os.path.exists(save_path):
            os.makedirs(save_path)

        print('\r>> 生成车牌号图片...')

        # 生成车牌号码,白底黑字
        chars_image_generator = CharsImageGenerator(plate_type)
        chars_images = chars_image_generator.generate_images(plate_num_str_list)

        # 生成车牌底牌
        license_template_generator = LicensePlateImageGenerator(plate_type)
        template_image = license_template_generator.generate_template_image(chars_image_generator.plate_width, chars_image_generator.plate_height)

        print('\r>> 生成车牌图片...')

        # 数据增强及车牌字符颜色修正,并保存
        augmentation = ImageAugmentation(plate_type, template_image)
        plate_height = 72
        plate_width = int(chars_image_generator.plate_width * plate_height / chars_image_generator.plate_height)
        i = 1
        for index, char_image in enumerate(chars_images):
            image_name = str(i) + "_" + plate_num_str_list[index] + ".jpg"
            image_path = os.path.join(save_path, image_name)
            image = augmentation.augment(char_image)
            image = cv2.resize(image, (plate_width, plate_height))
            cv2.imencode('.jpg', image)[1].tofile(image_path)
            print("\r>> {} done...".format(image_name))

            i += 1


if __name__ == '__main__':
    # 保存文件夹名称
    file_path = os.path.join(os.getcwd(), 'plate_images')
    # 车牌号码列表
    plate_num_str_list = ["苏A5B5T3"]

    LicensePlateGenerator.generate_license_plate_images('single_blue', plate_num_str_list, file_path)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/397742.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式物联网毕业设计选题智能图像识别项目-stm32mp157 linux开发板

stm32mp157开发板FS-MP1A是华清远见自主研发的一款高品质、高性价比的Linux单片机二合一的嵌入式教学级开发板。开发板搭载ST的STM32MP157高性能微处理器,集成2个Cortex-A7核和1个Cortex-M4 核,A7核上可以跑Linux操作系统,M4核上可以跑FreeRT…

ControlNet-有条件图文生成论文阅读

文章目录摘要算法:ControlNetControlNet in Image Diffusion ModelTrainingImproved Training实验Canny edgesHough linesHuman scribblesHED boundary mapOpenpifpaf poseOpenposeADE20K segmentation mapCOCO-Stuff segmentation mapDIODE normal mapDepth-to-Ima…

蓝桥冲刺31天之第六天

今天是摆子的一天,明天我要肝一整天的第四题!!! PS:一个普通的排序罢了 import java.io.*; import java.util.Arrays; import java.util.Scanner;/*** ClassName 考勤刷卡* Description TODO* Author 小怂很怂* Date 2…

DataX与DB2导入导出案例

DataX与DB2导入导出案例 文章目录DataX与DB2导入导出案例0. 写在前面1. DB2介绍2. DB2数据库对象关系3. 安装前的准备3.1 安装依赖3.2 修改配置文件 sysctl.conf3.3 修改配置文件 limits.conf4. 安装4.1 预检查4.2 添加组和用户4.3 创建实例4.4 创建实例库、开启服务4.5 连接5.…

在CentOS7上静默安装Oracle19c

1.下载Oracle 官方安装包下载路径(需要登录Oracle账号): https://www.oracle.com/database/technologies/oracle-database-software-downloads.html#19c 可选择windows/Linux平台对应的安装包,我选择Linux x86-64、ZIP包下载&…

分析linux内核移植中vmlinux可执行文件是如何生成的?以及 uImage/zImage/Image/vmlinx之间关系

一:分析linux内核移植中vmlinux可执行文件是如何生成的? 1:进入内核源码顶层目录下打开Makefile文件,搜索vmlinux 这里构建vmlinux的命令使用了makefile的内置函数call。这是一个比较特殊的内置函数,make使用它来引用…

Go语言学习编程实践:五种模式解决go中的并发问题

五种模式解决go中的并发问题For-Select-Done扇入模式从流中获取前 n 个值订阅模式地图模式过滤模式For-Select-Done 我们应该防止程序中发生任何泄露。所以我们应该对于留在程序中的go例程发送信号,让它知道它可以退出。 最常见的就是将for-select循环与通道结合起…

UEFI启动流程

以上是UEFI系统运行的7个阶段,下边是详细描述: SEC阶段:(安全验证) 1、接收和处理系统的启动,重启,异常信号; 2、SEC阶段特色功能“Cache As RAM(CAR)”&am…

英伦四地到底是什么关系?

英格兰、苏格兰、威尔士和北爱尔兰四地到底是什么关系,为何苏格兰非要独立?故事还要从中世纪说起。大不列颠岛位于欧洲西部,和欧洲大陆隔海相望。在古代,大不列颠岛和爱尔兰属于凯尔特人的领地。凯尔特人是欧洲西部一个庞大的族群…

Caddy2学习笔记——Caddy2反向代理Frp内网穿透和反向代理PVE

一、环境概述 本人拥有一个国内云服务商的云主机和一个备案好的域名,通过caddy2来作为web服务器。 我的云主机是公网ip,地址为:43.126.100.78;我备案好的域名是:hotgirl.com 。后面的文章都以上述的ip和域名来进行讲解…

Actipro WinForms Studio Crack

Actipro WinForms Studio Crack 已验证Microsoft.NET 7兼容性。 添加了MetroDark配色方案。 添加了支持MetroLight和MetroDark颜色方案的MetroScrollBarRenderer。 添加了IWindowsColorScheme接口,该接口将替换对WindowsColorScheme的大多数引用。 添加了IWindowsCo…

Zookeeper客户端ZkClient、Curator的使用,史上最详细的教程来啦~

1 前言 本文主要介绍了操作Zookeeper的几种客户端的基础使用,希望对老铁们会有所帮助。 可以去操作zookeeper创建、删除、查询、修改znode节点 2 Zookeeper服务器客户端分类 目前,Zookeeper服务器有三种Java客户端: Zookeeper、Zkclient和…

inquirerjs

inquirerjs inquirerjs是一个用来实现命令行交互界面的工具集合。它帮助我们实现与用户的交互交流,比如给用户一个提醒,用户给我们一个答案,我们根据用户的答案来做一些事情,典型应用如plop等生成器工具。 npm install inquirer…

软测面试了一个00后,绝对能称为是内卷届的天花板

前言 公司前段缺人,也面了不少测试,结果竟然没有一个合适的。一开始瞄准的就是中级的水准,也没指望来大牛,提供的薪资也不低,面试的人很多,但平均水平很让人失望。令我印象最深的是一个00后测试员&#xf…

certum验证域名所有权

Certum证书支持 Email、文件上传、DNS解析 验证域名所有权。1 .Email 方式请确认自己域名已开通,域名邮箱。仅支持以下邮箱:adminyourdomain.comadministratoryourdomain.comwebmasteryourdomain.compostmasteryourdomain.comhostmasteryourdomain.com收…

深度学习笔记:卷积神经网络(1)

1 卷积神经网络整体结构 卷积神经网络(CNN)相比全连接网络多了卷积层和池化层。对于全连接网络,所有相邻层的神经元都用Affine层进行连接,如图中即为Affine-ReLU的连接组合。 卷积神经网络则包含卷积层和池化层与激活函数相连&a…

后端Java随机比大小游戏实战讲解

## - 利用print打印输出提示用户 ## - 利用Scanner函数抓取数据 ## - 利用Math方法实现随机数 #### 1.首先用到的是print函数,对用户进行提醒进一步的操作 通过System.out.print();提示用户进行选择买大买小。 #### 2.然后利用Scanner函数,对用户输出…

Spring Bean生命周期七大阶段-Java八股面试(七)

系列文章目录 第一章 ArrayList-Java八股面试(一) 第二章 HashMap-Java八股面试(二) 第三章 单例模式-Java八股面试(三) 第四章 线程池和Volatile关键字-Java八股面试(四) 第五章 ConcurrentHashMap-Java八股面试(五) 第六章 spring之refresh流程-Java八股面试(六) 提示&…

HTTPS是怎么加密数据的?

HTTPS是怎么加密数据的?对安全或密码学基础有了解的同学,应该知道常见的加密手段。一般来说,加密分为对称加密、非对称加密(也叫公开密钥加密)对称加密对称加密的意思就是,加密数据用的密钥,跟解…

儿童反复感染,是体质差还是免疫缺陷?

原发性免疫缺陷病(PIDs)它是一组由遗传因素或先天性免疫系统发育不良引起的免疫系统功能障碍综合征,可涉及固有免疫或适应性免疫。在中国,PID的中位发病率为6个月,男孩的发病率通常高于女孩。▼分类目前,国…