车牌模拟生成器:Python3.8+Opencv代码实现与商业应用前景(C#、python 开发包SDK)

发布于:2025-09-09 ⋅ 阅读:(23) ⋅ 点赞:(0)

车牌模拟生成器:Python代码实现与商业应用前景

引言

在智慧城市建设和汽车行业数字化浪潮中,车牌作为车辆的唯一标识,其相关技术应用正变得越来越重要。今天我们将介绍一个基于Python的车牌模拟生成器,探讨其技术实现、功能特点以及潜在的商业价值。

【注意】

在线生成随机或自定义的中国车牌,支持多种车牌类型和样式,仅用于模型数据测试、车牌识别系统的演示效果等,切勿用于商业用途和不合法用途,否则自己将承担相关责任,与本工具无关。

【需要的素材】

1、需要各个省市的简称:

由于用到了opencv,建议将图片和具体的车牌号做个映射关系:

【效果图】

映射关系文件 font_mappings.txt

云=yunnan
京=beijing
冀=hebei
晋=shanxi
蒙=neimenggu
辽=liaoning
吉=jilin
黑=heilongjiang
沪=shanghai
苏=jiangsu
浙=zhejiang
皖=anhui
闽=fujian
赣=jiangxi
鲁=shandong
豫=henan
鄂=hubei
湘=hunan
粤=guangdong
桂=guangxi
琼=hainan
渝=chongqing
川=sichuan
贵=guizhou
藏=xizang
陕=shanxi_s
甘=gansu
青=qinghai
宁=ningxia
新=xinjiang
津=tianjin
港=gang
澳=ao
使=shi
领=ling
学=xue
警=jing
挂=gua

需求分析

车牌模拟生成在多个领域有着广泛的应用需求:

  1. 软件开发与测试​:智能交通系统、停车场管理系统需要大量车牌数据进行测试
  2. 教育培训​:驾校、交通法规培训需要示例车牌进行教学演示
  3. 影视制作​:影视剧中需要符合规定的虚拟车牌避免侵权问

  4. 数据分析​:交通流量模拟、城市规划需要车牌数据支持

功能特点

我们的车牌模拟生成器具备以下核心功能:

1. 符合中国车牌标准

  •  

    支持普通蓝牌和新能源绿牌两种格式

  •  

    遵循中国车牌编号规则,排除易混淆字母(O/I)

  •  

    省份简称符合国家标准

2. 灵活生成模式

  •  

    可指定生成特定类型车牌

  •  

    支持完全随机生成模式

  •  

    生成数量可自定义扩展

3. 高度可定制化代码结构清晰,易于扩展其他类型车牌

  • 生成规则可调整,满足不同场景需求

  • 代码结构清晰,易于扩展其他类型车牌

由于服务端是python,这里给出的是部分核心逻辑代码,需要自己搭建。

【核心代码实现,流程参考】

import random
import string

class LicensePlateGenerator:
    """车牌生成器类"""
    
    def __init__(self):
        # 省份简称列表
        self.provinces = [
            '京', '津', '冀', '晋', '蒙', '辽', '吉', '黑', '沪', '苏',
            '浙', '皖', '闽', '赣', '鲁', '豫', '鄂', '湘', '粤', '桂',
            '琼', '渝', '川', '贵', '云', '藏', '陕', '甘', '青', '宁', '新'
        ]
        
        # 车牌字母列表(排除O和I)
        self.letters = [c for c in string.ascii_uppercase if c not in ['O', 'I']]
    
    def generate_plate(self, plate_type=None, province=None):
        """
        生成车牌号码
        :param plate_type: 车牌类型('normal'普通/'new_energy'新能源)
        :param province: 指定省份简称
        :return: 车牌号码字符串
        """
        # 确定省份
        if province and province in self.provinces:
            province_char = province
        else:
            province_char = random.choice(self.provinces)
        
        # 确定车牌类型
        if plate_type is None:
            plate_type = random.choice(['normal', 'new_energy'])
        
        # 生成普通车牌
        if plate_type == 'normal':
            return self._generate_normal_plate(province_char)
        
        # 生成新能源车牌
        elif plate_type == 'new_energy':
            return self._generate_new_energy_plate(province_char)
    
    def _generate_normal_plate(self, province):
        """生成普通蓝牌"""
        plate = province + random.choice(self.letters)
        
        # 生成5位序号
        for _ in range(5):
            if random.random() < 0.3:
                plate += random.choice(self.letters)
            else:
                plate += random.choice(string.digits)
        return plate
    
    def _generate_new_energy_plate(self, province):
        """生成新能源绿牌"""
        plate = province + random.choice(self.letters)
        plate += random.choice(['D', 'F'])  # D=纯电, F=混动
        plate += ''.join(random.choices(string.digits, k=5))
        return plate

# 使用示例
if __name__ == "__main__":
    generator = LicensePlateGenerator()
    
    # 生成10个随机车牌
    print("随机车牌示例:")
    for i in range(10):
        plate_type = random.choice(['normal', 'new_energy'])
        plate = generator.generate_plate(plate_type)
        print(f"{i+1}. {'普通车牌' if plate_type == 'normal' else '新能源车牌'}: {plate}")
    
    # 生成特定省份车牌
    print("\n北京车牌示例:")
    for i in range(3):
        print(f"{i+1}. {generator.generate_plate('normal', '京')}")

【生成车牌、计算数字边框等算法】

# -*- coding: utf-8 -*-
import numpy as np
import cv2, os, argparse
from glob import glob
from tqdm import tqdm

from plate_number import random_select, generate_plate_number_white, generate_plate_number_yellow_xue
from plate_number import generate_plate_number_black_gangao, generate_plate_number_black_shi, generate_plate_number_black_ling
from plate_number import generate_plate_number_blue, generate_plate_number_yellow_gua
from plate_number import letters, digits

# 加载中文字符到英文文件名的映射
def load_font_mappings():
    mappings = {}
    try:
        with open('font_mappings.txt', 'r', encoding='utf-8') as f:
            for line in f:
                if '=' in line:
                    cn, en = line.strip().split('=', 1)
                    mappings[cn] = en
    except Exception as e:
        print(f"警告: 无法加载字体映射文件,错误: {e}")
    return mappings

# 中文到英文的映射
CHINESE_TO_ENGLISH = load_font_mappings()
# 英文到中文的反向映射
ENGLISH_TO_CHINESE = {v: k for k, v in CHINESE_TO_ENGLISH.items()}


def get_location_data(length=7, split_id=1, height=140):
    """
    获取车牌号码在底牌中的位置
    length: 车牌字符数,7或者8,7为普通车牌、8为新能源车牌
    split_id: 分割空隙
    height: 车牌高度,对应单层和双层车牌
    """
    # 字符位置
    location_xy = np.zeros((length, 4), dtype=np.int32)

    # 单层车牌高度
    if height == 140:
        # 单层车牌,y轴坐标固定
        location_xy[:, 1] = 25
        location_xy[:, 3] = 115
        # 螺栓间隔
        step_split = 34 if length == 7 else 49
        # 字符间隔
        step_font = 12 if length == 7 else 9

        # 字符宽度
        width_font = 45
        for i in range(length):
            if i == 0:
                location_xy[i, 0] = 15
            elif i == split_id:
                location_xy[i, 0] = location_xy[i - 1, 2] + step_split
            else:
                location_xy[i, 0] = location_xy[i - 1, 2] + step_font
            # 新能源车牌
            if length == 8 and i > 0:
                width_font = 43
            location_xy[i, 2] = location_xy[i, 0] + width_font
    else:
        # 双层车牌第一层
        location_xy[0, :] = [110, 15, 190, 75]
        location_xy[1, :] = [250, 15, 330, 75]

        # 第二层
        width_font = 65
        step_font = 15
        for i in range(2, length):
            location_xy[i, 1] = 90
            location_xy[i, 3] = 200
            if i == 2:
                location_xy[i, 0] = 27
            else:
                location_xy[i, 0] = location_xy[i - 1, 2] + step_font
            location_xy[i, 2] = location_xy[i, 0] + width_font

    return location_xy


# 字符贴上底板
def copy_to_image_multi(img, font_img, bbox, bg_color, is_red):
    x1, y1, x2, y2 = bbox
    font_img = cv2.resize(font_img, (x2 - x1, y2 - y1))
    img_crop = img[y1: y2, x1: x2, :]

    if is_red:
        img_crop[font_img < 200, :] = [0, 0, 255]
    elif 'blue' in bg_color or 'black' in bg_color:
        img_crop[font_img < 200, :] = [255, 255, 255]
    else:
        img_crop[font_img < 200, :] = [0, 0, 0]
    return img


class MultiPlateGenerator:
    def __init__(self, adr_plate_model, adr_font):
        # 车牌底板路径
        self.adr_plate_model = adr_plate_model
        # 车牌字符路径
        # 如果存在英文目录,则使用英文目录
        self.adr_font = 'font_model_english' if os.path.exists('font_model_english') else adr_font

        # 车牌字符图片,预存处理
        self.font_imgs = {}
        # 获取所有jpg文件
        font_filenames = []
        for root, dirs, files in os.walk(self.adr_font):
            for file in files:
                if file.lower().endswith('.jpg'):
                    font_filenames.append(os.path.join(root, file))
                    
        for font_filename in font_filenames:
            # 尝试读取文件,如果失败则跳过
            try:
                font_img = cv2.imread(font_filename, cv2.IMREAD_GRAYSCALE)
                if font_img is None:
                    continue
                    
                if '140' in font_filename:
                    font_img = cv2.resize(font_img, (45, 90))
                elif '220' in font_filename:
                    font_img = cv2.resize(font_img, (65, 110))
                elif font_filename.split('_')[-1].split('.')[0] in letters + digits:
                    font_img = cv2.resize(font_img, (43, 90))
                    # 获取文件名作为key
                basename = os.path.basename(font_filename).split('.')[0]
                
                # 保存原始文件名映射
                self.font_imgs[basename] = font_img
                
                # 对于英文文件名,我们也建立到中文字符的映射
                for en, cn in ENGLISH_TO_CHINESE.items():
                    if en in basename:
                        # 构建中文文件名格式的key
                        parts = basename.split('_')
                        for i, part in enumerate(parts):
                            if part == en:
                                parts[i] = cn
                                chinese_key = '_'.join(parts)
                                self.font_imgs[chinese_key] = font_img
                                break
            except Exception as e:
                print(f"警告: 无法读取或处理文件 {font_filename}, 错误: {e}")
                continue

        # 字符位置
        self.location_xys = {}
        for i in [7, 8]:
            for j in [1, 2, 4]:
                for k in [140, 220]:
                    self.location_xys['{}_{}_{}'.format(i, j, k)] = \
                        get_location_data(length=i, split_id=j, height=k)

    # 获取字符位置
    def get_location_multi(self, plate_number, height=140):
        length = len(plate_number)
        if '警' in plate_number:
            split_id = 1
        elif '使' in plate_number:
            split_id = 4
        else:
            split_id = 2
        return self.location_xys['{}_{}_{}'.format(length, split_id, height)]

    # 随机生成车牌号码,获取底板颜色、单双层
    def generate_plate_number(self):
        rate = np.random.random(1)
        if rate > 0.4:
            # 蓝牌
            plate_number = generate_plate_number_blue(length=random_select([7, 8]))
        else:
            # 白牌、黄牌教练车、黄牌挂车、黑色港澳、黑色使、领馆
            generate_plate_number_funcs = [generate_plate_number_white,
                                           generate_plate_number_yellow_xue,
                                           generate_plate_number_yellow_gua,
                                           generate_plate_number_black_gangao,
                                           generate_plate_number_black_shi,
                                           generate_plate_number_black_ling]
            plate_number = random_select(generate_plate_number_funcs)()

        # 车牌底板颜色
        bg_color = random_select(['blue'] + ['yellow'])

        if len(plate_number) == 8:
            bg_color = random_select(['green_car'] * 10 + ['green_truck'])
        elif len(set(plate_number) & set(['使', '领', '港', '澳'])) > 0:
            bg_color = 'black'
        elif '警' in plate_number or plate_number[0] in letters:
            bg_color = 'white'
        elif len(set(plate_number) & set(['学', '挂'])) > 0:
            bg_color = 'yellow'

        is_double = random_select([False] + [True] * 3)

        if '使' in plate_number:
            bg_color = 'black_shi'

        if '挂' in plate_number:
            # 挂车双层
            is_double = True
        elif len(set(plate_number) & set(['使', '领', '港', '澳', '学', '警'])) > 0 \
                or len(plate_number) == 8 or bg_color == 'blue':
            # 使领港澳学警、新能源、蓝色都是单层
            is_double = False

        # special,首字符为字母、单层则是军车
        if plate_number[0] in letters and not is_double:
            bg_color = 'white_army'

        return plate_number, bg_color, is_double

    # 随机生成车牌图片
    def generate_plate(self, enhance=False):
        plate_number, bg_color, is_double = self.generate_plate_number()
        height = 220 if is_double else 140

        # 获取底板图片
        # print(plate_number, height, bg_color, is_double)
        number_xy = self.get_location_multi(plate_number, height)
        # 读取底板图片,确保中文文件名正确处理
        plate_model_path = os.path.join(self.adr_plate_model, '{}_{}.PNG'.format(bg_color, height))
        img_plate_model = cv2.imread(plate_model_path)
        if img_plate_model is None:
            print(f"警告: 无法读取底板图片 {plate_model_path}")
            # 使用默认蓝色底板图片作为备选
            default_path = os.path.join(self.adr_plate_model, 'blue_140.PNG')
            img_plate_model = cv2.imread(default_path)
            if img_plate_model is None:
                raise FileNotFoundError(f"无法读取默认底板图片 {default_path}")
        img_plate_model = cv2.resize(img_plate_model, (440 if len(plate_number) == 7 else 480, height))

        for i in range(len(plate_number)):
            if len(plate_number) == 8:
                # 新能源
                key = 'green_{}'.format(plate_number[i])
                # 如果找不到中文key,尝试使用英文key
                if key not in self.font_imgs:
                    # 检查字符是否是中文字符,如果是则转换为英文
                    char = plate_number[i]
                    if char in CHINESE_TO_ENGLISH:
                        en_char = CHINESE_TO_ENGLISH[char]
                        key = 'green_{}'.format(en_char)
                font_img = self.font_imgs[key]
            else:
                if '{}_{}'.format(height, plate_number[i]) in self.font_imgs:
                    key = '{}_{}'.format(height, plate_number[i])
                    # 如果找不到中文key,尝试使用英文key
                    if key not in self.font_imgs:
                        # 检查字符是否是中文字符,如果是则转换为英文
                        char = plate_number[i]
                        if char in CHINESE_TO_ENGLISH:
                            en_char = CHINESE_TO_ENGLISH[char]
                            key = '{}_{}'.format(height, en_char)
                    font_img = self.font_imgs[key]
                else:
                    # 双层车牌字体库
                    if i < 2:
                        key = '220_up_{}'.format(plate_number[i])
                        # 如果找不到中文key,尝试使用英文key
                        if key not in self.font_imgs:
                            # 检查字符是否是中文字符,如果是则转换为英文
                            char = plate_number[i]
                            if char in CHINESE_TO_ENGLISH:
                                en_char = CHINESE_TO_ENGLISH[char]
                                key = '220_up_{}'.format(en_char)
                        font_img = self.font_imgs[key]
                    else:
                        key = '220_down_{}'.format(plate_number[i])
                        # 如果找不到中文key,尝试使用英文key
                        if key not in self.font_imgs:
                            # 检查字符是否是中文字符,如果是则转换为英文
                            char = plate_number[i]
                            if char in CHINESE_TO_ENGLISH:
                                en_char = CHINESE_TO_ENGLISH[char]
                                key = '220_down_{}'.format(en_char)
                        font_img = self.font_imgs[key]

            # 字符是否红色
            if (i == 0 and plate_number[0] in letters) or plate_number[i] in ['警', '使', '领']:
                is_red = True
            elif i == 1 and plate_number[0] in letters and np.random.random(1) > 0.5:
                # second letter of army plate
                is_red = True
            else:
                is_red = False

            if enhance:
                k = np.random.randint(1, 6)
                kernel = np.ones((k, k), np.uint8)
                if np.random.random(1) > 0.5:
                    font_img = np.copy(cv2.erode(font_img, kernel, iterations=1))
                else:
                    font_img = np.copy(cv2.dilate(font_img, kernel, iterations=1))

            # 贴上底板
            img_plate_model = copy_to_image_multi(img_plate_model, font_img,
                                                  number_xy[i, :], bg_color, is_red)

        img_plate_model = cv2.blur(img_plate_model, (3, 3))

        return img_plate_model, number_xy, plate_number, bg_color, is_double

    def generate_plate_special(self, plate_number, bg_color, is_double, enhance=False):
        """
        生成特定号码、颜色车牌
        :param plate_number: 车牌号码
        :param bg_color: 背景颜色,black/black_shi(使领馆)/blue/green_car(新能源轿车)/green_truck(新能源卡车)/white/white_army(军队)/yellow
        :param is_double: 是否双层
        :param enhance: 图像增强
        :return: 车牌图
        """
        height = 220 if is_double else 140

        # print(plate_number, height, bg_color, is_double)
        number_xy = self.get_location_multi(plate_number, height)
        img_plate_model = cv2.imread(os.path.join(self.adr_plate_model, '{}_{}.PNG'.format(bg_color, height)))
        img_plate_model = cv2.resize(img_plate_model, (440 if len(plate_number) == 7 else 480, height))

        for i in range(len(plate_number)):
            if len(plate_number) == 8:
                # 新能源
                key = 'green_{}'.format(plate_number[i])
                # 如果找不到中文key,尝试使用英文key
                if key not in self.font_imgs:
                    # 检查字符是否是中文字符,如果是则转换为英文
                    char = plate_number[i]
                    if char in CHINESE_TO_ENGLISH:
                        en_char = CHINESE_TO_ENGLISH[char]
                        key = 'green_{}'.format(en_char)
                font_img = self.font_imgs[key]
            else:
                if '{}_{}'.format(height, plate_number[i]) in self.font_imgs:
                    key = '{}_{}'.format(height, plate_number[i])
                    # 如果找不到中文key,尝试使用英文key
                    if key not in self.font_imgs:
                        # 检查字符是否是中文字符,如果是则转换为英文
                        char = plate_number[i]
                        if char in CHINESE_TO_ENGLISH:
                            en_char = CHINESE_TO_ENGLISH[char]
                            key = '{}_{}'.format(height, en_char)
                    font_img = self.font_imgs[key]
                else:
                    if i < 2:
                        key = '220_up_{}'.format(plate_number[i])
                        # 如果找不到中文key,尝试使用英文key
                        if key not in self.font_imgs:
                            # 检查字符是否是中文字符,如果是则转换为英文
                            char = plate_number[i]
                            if char in CHINESE_TO_ENGLISH:
                                en_char = CHINESE_TO_ENGLISH[char]
                                key = '220_up_{}'.format(en_char)
                        font_img = self.font_imgs[key]
                    else:
                        key = '220_down_{}'.format(plate_number[i])
                        # 如果找不到中文key,尝试使用英文key
                        if key not in self.font_imgs:
                            # 检查字符是否是中文字符,如果是则转换为英文
                            char = plate_number[i]
                            if char in CHINESE_TO_ENGLISH:
                                en_char = CHINESE_TO_ENGLISH[char]
                                key = '220_down_{}'.format(en_char)
                        font_img = self.font_imgs[key]

            if (i == 0 and plate_number[0] in letters) or plate_number[i] in ['警', '使', '领']:
                is_red = True
            elif i == 1 and plate_number[0] in letters and np.random.random(1) > 0.5:
                # second letter of army plate
                is_red = True
            else:
                is_red = False

            if enhance:
                k = np.random.randint(1, 6)
                kernel = np.ones((k, k), np.uint8)
                if np.random.random(1) > 0.5:
                    font_img = np.copy(cv2.erode(font_img, kernel, iterations=1))
                else:
                    font_img = np.copy(cv2.dilate(font_img, kernel, iterations=1))

            img_plate_model = copy_to_image_multi(img_plate_model, font_img,
                                                  number_xy[i, :], bg_color, is_red)

        # is_double = 'double' if is_double else 'single'
        img_plate_model = cv2.blur(img_plate_model, (3, 3))

        return img_plate_model


def parse_args():
    parser = argparse.ArgumentParser(description='中国车牌生成器')
    parser.add_argument('--number', default=10, type=int, help='生成车牌数量')
    parser.add_argument('--save-adr', default='multi_val', help='车牌保存路径')
    args = parser.parse_args()
    return args


def mkdir(path):
    try:
        os.makedirs(path)
    except:
        pass


if __name__ == '__main__':
    args = parse_args()
    print(args)
    # 随机生成车牌
    print('save in {}'.format(args.save_adr))

    mkdir(args.save_adr)
    generator = MultiPlateGenerator('plate_model', 'font_model')

    for i in tqdm(range(args.number)):
        img, number_xy, gt_plate_number, bg_color, is_double = generator.generate_plate()
        # 使用cv2.imencode和open函数来正确处理中文文件名
        save_path = os.path.join(args.save_adr, '{}_{}_{}.jpg'.format(gt_plate_number, bg_color, is_double))
        try:
            # 将图像编码为JPEG格式
            success, encoded_img = cv2.imencode('.jpg', img)
            if success:
                # 使用open函数以二进制写入模式保存文件
                with open(save_path, 'wb') as f:
                    f.write(encoded_img.tobytes())
            else:
                print(f"警告: 无法编码图像 {save_path}")
        except Exception as e:
            print(f"警告: 无法保存图像 {save_path}, 错误: {e}")

商业应用前景

1. 软件开发服务

  •  

    为智能交通系统提供测试数据生成服务

  •  

    向停车场管理系统开发商提供车牌模拟解决方案

  •  

    为驾考系统提供虚拟车牌生成功能

2. 数据服务业务

  •  

    向研究机构提供交通模拟数据

  •  

    为城市规划部门提供车辆流量预测数据支持

  •  

    向广告公司提供区域车辆分布分析数据

3. 教育培训应用

  •  

    开发交通法规教学工具

  •  

    为驾校提供理论考试模拟系统

  •  

    制作交通安全教育材料

4. 增值服务扩展

  •  

    添加车牌识别验证功能

  •  

    开发车牌样式自定义功能

  •  

    增加多国车牌生成支持

技术拓展方向

1、增加图像生成功能​:将车牌文本转换为真实车牌图像

​2、添加验证算法​:验证生成的车牌是否符合编码规则

3、支持更多车牌类型​:扩展至武警车牌、领事馆车牌等特殊类型

4、​开发API接口​:提供Web服务供第三方调用

车牌模拟生成器虽是一个小型工具,但其应用场景广泛,商业价值可观。

【最后注意】:

在线生成随机或自定义的中国车牌,支持多种车牌类型和样式,仅用于模型数据测试、演示效果等,切勿用于商业用途和不合法用途,否则自己将承担相关责任,与本工具无关。

工具截图:可以自己开发一个,提供下载地址

通过网盘分享的文件(网盘中是编译好的可以运行的exe):车牌模拟生成demo
链接: https://pan.baidu.com/s/1WBzzd3qNpD8m837wqfgVBA?pwd=tgp4 提取码: tgp4 

生成的蓝牌、绿牌、黑、白、黄牌如下:
 

模拟生成效果:

感谢您的阅读和支持,欢迎点赞拍砖!


网站公告

今日签到

点亮在社区的每一天
去签到