车牌模拟生成器: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. 符合中国车牌标准
支持普通蓝牌和新能源绿牌两种格式
遵循中国车牌编号规则,排除易混淆字母(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
生成的蓝牌、绿牌、黑、白、黄牌如下:
模拟生成效果:
感谢您的阅读和支持,欢迎点赞拍砖!