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

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

 效果:

一、代码目录结构:

background目录下存放各种背景图片 

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()操作完成

添加饱和度光照的噪声

通过调整HSV颜色空间实现,

  • Hue:色调
  • Saturation:饱和度
  • Value:明亮度
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 色调,饱和度,亮度
hsv[:, :, 0] = hsv[:, :, 0] * (self.hue_keep + np.random.random() * (1 - self.hue_keep))
hsv[:, :, 1] = hsv[:, :, 1] * (self.saturation_keep + np.random.random() * (1 - self.saturation_keep))
hsv[:, :, 2] = hsv[:, :, 2] * (self.value_keep + np.random.random() * (1 - self.value_keep))
img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

添加透视变换

透视变换api整体和放射变化类似,先通过点与点的映射关系获取变换矩阵,然后再将图形进行转换。

以左向投影为例:

    ->    

 其计算过程如下:

寻找源图像中4个点和目标图像中的4个点,在左向倾斜时右边两个顶点的height值会做修改。

shape = img.shape
size_src = (shape[1], shape[0])  # width, height
# 源图像四个顶点坐标
pts1 = np.float32([[0, 0], [0, size_src[1]], [size_src[0], 0], [size_src[0], size_src[1]]])
# 计算图片进行投影倾斜后的位置
interval = abs(int(math.sin((float(angle) / 180) * math.pi) * shape[0]))
# 目标图像上四个顶点的坐标
if is_left:
	pts2 = np.float32([[0, 0], [0, size_src[1]],
					   [size_src[0], interval], [size_src[0], size_src[1] - interval]])
else:
	pts2 = np.float32([[0, interval], [0, size_src[1] - interval],
					   [size_src[0], 0], [size_src[0], size_src[1]]])
# 获取 3x3的投影映射/透视变换 矩阵
matrix = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(img, matrix, size_src)

 

三、python代码:

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

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变化,添加背景,高斯噪声,污渍"""
    horizontal_sight_directions = ('left', 'mid', 'right')
    vertical_sight_directions = ('up', 'mid', 'down')

    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.angle_horizontal = 15
        self.angle_vertical = 15
        self.angle_up_down = 10
        self.angle_left_right = 5
        self.factor = 10
        # 色调,饱和度,亮度
        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")

    def left_right_transfer(self, img, is_left=True, angle=None):
        """
        左右视角,默认左视角
        :param img:
        :param is_left:
        :param angle: 角度
        :return:
        """
        if angle is None:
            angle = self.angle_left_right

        shape = img.shape
        size_src = (shape[1], shape[0])  # width, height
        # 源图像四个顶点坐标
        pts1 = np.float32([[0, 0], [0, size_src[1]], [size_src[0], 0], [size_src[0], size_src[1]]])
        # 计算图片进行投影倾斜后的位置
        interval = abs(int(math.sin((float(angle) / 180) * math.pi) * shape[0]))
        # 目标图像上四个顶点的坐标
        if is_left:
            pts2 = np.float32([[0, 0], [0, size_src[1]],
                               [size_src[0], interval], [size_src[0], size_src[1] - interval]])
        else:
            pts2 = np.float32([[0, interval], [0, size_src[1] - interval],
                               [size_src[0], 0], [size_src[0], size_src[1]]])
        # 获取 3x3的投影映射/透视变换 矩阵
        matrix = cv2.getPerspectiveTransform(pts1, pts2)
        dst = cv2.warpPerspective(img, matrix, size_src)
        return dst, matrix, size_src

    def up_down_transfer(self, img, is_down=True, angle=None):
        """ 上下视角,默认下视角
        :param img: 正面视角原始图片
        :param is_down: 是否下视角
        :param angle: 角度
        :return:
        """
        if angle is None:
            angle = self.rand_reduce(self.angle_up_down)

        shape = img.shape
        size_src = (shape[1], shape[0])
        # 源图像四个顶点坐标
        pts1 = np.float32([[0, 0], [0, size_src[1]], [size_src[0], 0], [size_src[0], size_src[1]]])
        # 计算图片进行投影倾斜后的位置
        interval = abs(int(math.sin((float(angle) / 180) * math.pi) * shape[0]))
        # 目标图像上四个顶点的坐标
        if is_down:
            pts2 = np.float32([[interval, 0], [0, size_src[1]],
                               [size_src[0] - interval, 0], [size_src[0], size_src[1]]])
        else:
            pts2 = np.float32([[0, 0], [interval, size_src[1]],
                               [size_src[0], 0], [size_src[0] - interval, size_src[1]]])
        # 获取 3x3的投影映射/透视变换 矩阵
        matrix = cv2.getPerspectiveTransform(pts1, pts2)
        dst = cv2.warpPerspective(img, matrix, size_src)
        return dst, matrix, size_src

    def vertical_tilt_transfer(self, img, is_left_high=True):
        """ 添加按照指定角度进行垂直倾斜(上倾斜或下倾斜,最大倾斜角度self.angle_vertical一半)
        :param img: 输入图像的numpy
        :param is_left_high: 图片投影的倾斜角度,左边是否相对右边高
        """
        angle = self.rand_reduce(self.angle_vertical)

        shape = img.shape
        size_src = [shape[1], shape[0]]
        # 源图像四个顶点坐标
        pts1 = np.float32([[0, 0], [0, size_src[1]], [size_src[0], 0], [size_src[0], size_src[1]]])

        # 计算图片进行上下倾斜后的距离,及形状
        interval = abs(int(math.sin((float(angle) / 180) * math.pi) * shape[1]))
        size_target = (int(math.cos((float(angle) / 180) * math.pi) * shape[1]), shape[0] + interval)
        # 目标图像上四个顶点的坐标
        if is_left_high:
            pts2 = np.float32([[0, 0], [0, size_target[1] - interval],
                               [size_target[0], interval], [size_target[0], size_target[1]]])
        else:
            pts2 = np.float32([[0, interval], [0, size_target[1]],
                               [size_target[0], 0], [size_target[0], size_target[1] - interval]])

        # 获取 3x3的投影映射/透视变换 矩阵
        matrix = cv2.getPerspectiveTransform(pts1, pts2)
        dst = cv2.warpPerspective(img, matrix, size_target)
        return dst, matrix, size_target

    def horizontal_tilt_transfer(self, img, is_right_tilt=True):
        """ 添加按照指定角度进行水平倾斜(右倾斜或左倾斜,最大倾斜角度self.angle_horizontal一半)
        :param img: 输入图像的numpy
        :param is_right_tilt: 图片投影的倾斜方向(右倾,左倾)
        """
        angle = self.rand_reduce(self.angle_horizontal)

        shape = img.shape
        size_src = [shape[1], shape[0]]
        # 源图像四个顶点坐标
        pts1 = np.float32([[0, 0], [0, size_src[1]], [size_src[0], 0], [size_src[0], size_src[1]]])

        # 计算图片进行左右倾斜后的距离,及形状
        interval = abs(int(math.sin((float(angle) / 180) * math.pi) * shape[0]))
        size_target = (shape[1] + interval, int(math.cos((float(angle) / 180) * math.pi) * shape[0]))
        # 目标图像上四个顶点的坐标
        if is_right_tilt:
            pts2 = np.float32([[interval, 0], [0, size_target[1]],
                               [size_target[0], 0], [size_target[0] - interval, size_target[1]]])
        else:
            pts2 = np.float32([[0, 0], [interval, size_target[1]],
                               [size_target[0] - interval, 0], [size_target[0], size_target[1]]])

        # 获取 3x3的投影映射/透视变换 矩阵
        matrix = cv2.getPerspectiveTransform(pts1, pts2)
        dst = cv2.warpPerspective(img, matrix, size_target)
        return dst, matrix, size_target

    def sight_transfer(self, images, horizontal_sight_direction, vertical_sight_direction):
        """
        对图片进行视角变换
        :param images:
        :param horizontal_sight_direction: 水平视角变换方向
        :param vertical_sight_direction: 垂直视角变换方向
        :return:
        """
        flag = 0
        img_num = len(images)
        # 左右视角
        if horizontal_sight_direction == 'left':
            flag += 1
            images[0], matrix, size = self.left_right_transfer(images[0], is_left=True)
            for i in range(1, img_num):
                images[i] = cv2.warpPerspective(images[i], matrix, size)
        elif horizontal_sight_direction == 'right':
            flag -= 1
            images[0], matrix, size = self.left_right_transfer(images[0], is_left=False)
            for i in range(1, img_num):
                images[i] = cv2.warpPerspective(images[i], matrix, size)
        else:
            pass

        # 上下视角
        if vertical_sight_direction == 'down':
            flag += 1
            images[0], matrix, size = self.up_down_transfer(images[0], is_down=True)
            for i in range(1, img_num):
                images[i] = cv2.warpPerspective(images[i], matrix, size)
        elif vertical_sight_direction == 'up':
            flag -= 1
            images[0], matrix, size = self.up_down_transfer(images[0], is_down=False)
            for i in range(1, img_num):
                images[i] = cv2.warpPerspective(images[i], matrix, size)
        else:
            pass

        # 左下视角 或 右上视角
        if abs(flag) == 2:
            images[0], matrix, size = self.vertical_tilt_transfer(images[0], is_left_high=True)
            for i in range(1, img_num):
                images[i] = cv2.warpPerspective(images[i], matrix, size)

            images[0], matrix, size = self.horizontal_tilt_transfer(images[0], is_right_tilt=True)
            for i in range(1, img_num):
                images[i] = cv2.warpPerspective(images[i], matrix, size)
        # 左上视角 或 右下视角
        elif abs(flag) == 1:
            images[0], matrix, size = self.vertical_tilt_transfer(images[0], is_left_high=False)
            for i in range(1, img_num):
                images[i] = cv2.warpPerspective(images[i], matrix, size)

            images[0], matrix, size = self.horizontal_tilt_transfer(images[0], is_right_tilt=False)
            for i in range(1, img_num):
                images[i] = cv2.warpPerspective(images[i], matrix, size)
        else:
            pass

        return images

    @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 rand_environment(self, img, env_data_paths=None):
        """ 添加自然环境的噪声
        :param img: 待加噪图片
        :param env_data_paths: 自然环境图片路径列表
        """
        if env_data_paths is None:
            env_data_paths = self.env_data_paths
        # 随机选取环境照片
        index = self.rand_reduce(len(env_data_paths))
        env = cv2.imread(env_data_paths[index])
        env = cv2.resize(env, (img.shape[1], img.shape[0]))
        # 找到黑背景,反转为白
        bak = (img == 0)
        for i in range(bak.shape[2]):
            bak[:, :, 0] &= bak[:, :, i]
        for i in range(bak.shape[2]):
            bak[:, :, i] = bak[:, :, 0]
        bak = bak.astype(np.uint8) * 255
        # 环境照片用白掩码裁剪,然后与原图非黑部分合并
        inv = cv2.bitwise_and(bak, env)
        img = cv2.bitwise_or(inv, img)
        return img

    def rand_hsv(self, img):
        """ 添加饱和度光照的噪声
        :param img: BGR格式的图片
        :return 加了饱和度、光照噪声的BGR图片
        """
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        # 色调,饱和度,亮度
        hsv[:, :, 0] = hsv[:, :, 0] * (self.hue_keep + np.random.random() * (1 - self.hue_keep))
        hsv[:, :, 1] = hsv[:, :, 1] * (self.saturation_keep + np.random.random() * (1 - self.saturation_keep))
        hsv[:, :, 2] = hsv[:, :, 2] * (self.value_keep + np.random.random() * (1 - self.value_keep))
        img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
        return img

    def augment(self, img, horizontal_sight_direction=None, vertical_sight_direction=None):
        """
        综合上面的加载操作,进行全流程加噪
        :param img:
        :param horizontal_sight_direction: 水平视角方向
        :param vertical_sight_direction: 垂直视角方向
        :return:
        """
        if horizontal_sight_direction is None:
            horizontal_sight_direction = ImageAugmentation.horizontal_sight_directions[random.randint(0, 2)]
        if vertical_sight_direction is None:
            vertical_sight_direction = ImageAugmentation.vertical_sight_directions[random.randint(0, 2)]


        if not self.is_black_char:
            # 转为黑底白字
            img = cv2.bitwise_not(img)
            img = cv2.bitwise_or(img, self.template_image)
            # 基于视角的变换
            img = self.sight_transfer([img], horizontal_sight_direction, vertical_sight_direction)
            img = img[0]
            img = self.rand_environment(img)
            img = self.rand_hsv(img)
        else:
            # 底牌加车牌文字
            img = cv2.bitwise_and(img, self.template_image)
            # 基于视角的变换
            img = self.sight_transfer([img], horizontal_sight_direction, vertical_sight_direction)
            img = img[0]
            img = self.rand_environment(img)
            img = self.rand_hsv(img)

        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)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vector - CAPL - RS232串口处理

摸鱼聊天、答疑解惑首选之地 --- 车载网络哪些事儿你是否还在为VT板卡系统昂贵而发愁?是否为MCU log没办法而烦恼?当前车载网络协议测试这块,vector可以说是一家独大,因此各种骚操作一年比一年多,然而对于我们测试工程…

【小猫爪】AUTOSAR学习笔记06-Communication Stack之ComM模块

【小猫爪】AUTOSAR学习笔记06-Communication Stack之ComM模块前言1 ComM简介2 ComM功能介绍2.1 PNC 状态管理2.2 Channel状态管理2.3 通信禁止功能2.4 不同类型的NM2.5 User、PNC 与 Channel 的映射2.6 状态保存END前言 因为一个偶然的机会让我接触到了AUTOSAR,所以…

数据库--进阶版-11--SQL优化

1.插入数据 insert优化: 例如要插入下面这些 insert into tb_test values(1,tom); insert into tb_test values(2,cat); insert into tb_test values(3,jerry); 我们可以通过以下几个方面进行操作: >批量插入(如果一次性要插入多条…

排好队,一个一个来:宫本武藏教你学队列(附各种队列源码)

文章目录前言:理解“队列”的正确姿势一个关于队列的小思考——请求处理队列的两大“护法”————顺序队列和链式队列数组实现的队列链表实现的队列循环队列关于开篇,你明白了吗?最后说一句前言: 哈喽!欢迎来到黑洞晓…

货物摆放 (蓝桥杯) JAVA

题目描述 小蓝有一个超大的仓库,可以摆放很多货物。 现在,小蓝有n 箱货物要摆放在仓库,每箱货物都是规则的正方体。 小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。 小蓝希望所有的货物最终摆…

可换皮肤的Qt登录界面

⭐️我叫忆_恒心,一名喜欢书写博客的在读研究生👨‍🎓。 如果觉得本文能帮到您,麻烦点个赞👍呗! 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧,喜欢的小伙伴给个三连支持一下呗。👍⭐️❤️ 可换皮肤的Qt登录界面 QSS的学习笔记 快…

真是一篇神奇的文章 动图DIY - 动图剪辑 - 录屏 - 画画动图 (无需VIP的软件推荐)

很多人想自己去搞一些 动图 ,去了好多网址或者app,不是收费 💴 就是 VIP 🔑 ,博主也苦恼了好久,今天遇到一款超级、无敌、好用的软件 ScreeTogif ,下载连接 😗😙&#x1…

高通开发系列 - Sensors Bring Up

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 返回高通开发系列 - 总目录 目录 问题背景高通android sensor信息Sensors Execution Environment (SEE)qxdm抓sensor log的方法android 调试…

Hadoop之Mapreduce序列化

目录 什么是序列化: 什么是反序列化: 为什么要序列化: Java的序列化: Hadoop序列化: 自定义序列化接口: 实现序列化的步骤: 先看源码进行简单分析: 序列化案例实操: 案例需…

蓝桥杯真题——自动售水机

2012年第四届全国电子专业人才设计与技能大赛“自动售水机”设计任务书1. 系统框图接下来我们将任务分块: 1. 按键控制单元 设定按键 S7 为出水控制按键,当 S7 按下后,售水机持续出水(继电器接通,指示 灯 L10 点亮&…

Java中的JSON序列化和反序列化

文章目录Java 和 JSON 序列化JSON 简介JSON 是什么JSON 标准JSON 优缺点JSON 工具Java JSON 库JSON 编码指南Fastjson 应用添加 maven 依赖Fastjson API定义 Bean序列化反序列化Fastjson 注解JSONFieldJSONTypeJackson 应用添加 maven 依赖Jackson API序列化反序列化容器的序列…

开箱即用的密码框组件

写了一个小玩具,分享一下 - 组件功能: 初次进入页面时,密码隐藏显示,且无法查看真实密码 当修改密码时,触发键盘,输入框则会直接清空 此时输入密码,可以设置密码的隐藏或显示: …

Spark了解

目录 1 概述 2 发展 3 Spark和Hadoop 4 Spark核心模块 1 概述 Apache Spark是一个快速、通用、可扩展的分布式计算系统,最初由加州大学伯克利分校的AMPLab开发。 Spark可以处理大规模数据处理任务,包括批处理、迭代式算法、交互式查询和流处理等。Spa…

算法强化每日一题--组队竞赛

大家好 先看看题目 链接:组队竞赛__牛客网 [编程题]组队竞赛 牛牛举办了一次编程比赛,参加比赛的有3*n个选手,每个选手都有一个水平值a_i.现在要将这些选手进行组队,一共组成n个队伍,即每个队伍3人.牛牛发现队伍的水平值等于该队伍队员中第二高水平值。 例如: 一个队…

pytest学习和使用21-测试报告插件allure-pytest如何使用?

21-测试报告插件allure-pytest如何使用?1 Allure简介2 环境配置2.1 allure-pytest插件安装2.2 pytest安装2.3 allure文件下载2.4 allure环境变量配置2.5 配置java环境3 查看allure版本4 运行allure4.1 测试用例4.1 执行方法4.3 报告查看方法4.4 指定报告生成的端口4…

使用TensorFlow Serving进行模型的部署和客户端推理

目的:在一个server端使用TensorFlow框架对模型进行训练和保存模型文件后用TensorFlow Serving进行部署,使得能在客户端上传输入数据后得到server端返回的结果,实现远程调用的效果。环境:操作系统: ubuntu 20.04.1当然可…

函数(上)——“Python”

各位CSDN的uu们你们好呀,今天小雅兰的内容是Python的函数呀,下面,就让我们进入函数的世界吧 首先可以选择性地看一下小雅兰很久之前写的C语言函数章节的知识: 函数——“C”_认真学习的小雅兰.的博客-CSDN博客 函数递归&#xf…

【进阶数据结构】二叉搜索树经典习题讲解

🌈感谢阅读East-sunrise学习分享——[进阶数据结构]二叉搜索树 博主水平有限,如有差错,欢迎斧正🙏感谢有你 码字不易,若有收获,期待你的点赞关注💙我们一起进步 🌈我们在之前已经学习…

鸟哥的Linux私房菜 Shell脚本

第十二章、学习 Shell Scripts https://linux.vbird.org/linux_basic/centos7/0340bashshell-scripts.php 12.2 简单的 shell script 练习 #!/bin/bash# Program: # User inputs his first name and last name. Program shows his full name.read -p "Please in…

【SpringMVC】SpringMVC方式,向作用域对象共享数据(ModelAndView、Model、map、ModelMap)

个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ 向域对象共享数据一、使用 原生ServletAPI二、…