python自动生成黄蓝绿车牌,并自动标注yolo模型labelImg文件

参考作者:

作者:Charlotte77

出处:http://www.cnblogs.com/charlotte77/

本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途!

介绍:本文灵感来自于作者:Charlotte77,在其自动生成车牌的基础上稍作修改,可随机生成三色车牌并标注xml文件。生成保存路径和xml保存路径以及单词随机生成个数,可在代码中设置。因为我是做车牌字符的识别,所以生成时规定了省份简称,如需更换可修改代码中相应部分,亦可随机。

修改部分:使用pypinyin模块得到省份简称的拼音,同音字已做区分,藏字得音为cang,需要注意!后续模型分类可另作修改。

注意:注释不一定是原作者真是用意,请加以甄别!

 

RandomGenerationVLP.py
#coding=utf-8
"""
   genPlate.py:生成随机车牌
"""

__author__ = "Huxiaoman"
__copyright__ = "Copyright (c) 2017 "

import PIL
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw
import cv2
import numpy as np
import os
from math import *
import pypinyin
import sys
import os
import xml.dom.minidom

import pypinyin


def genXML(plate, labels, xmlPath):  # 生成的字符列表和 标签位置
    # pinyin = pypinyin.slug(imageName[0])
    # if imageName[0] == '贵':
    #     pinyin = 'gui'
    # elif imageName[0] == '桂':
    #     pinyin = 'guilin'
    # elif imageName[0] == '甘':
    #     pinyin = 'gan'
    # elif imageName[0] == '赣':
    #     pinyin = 'jiangxi'
    # elif imageName[0] == '豫':
    #     pinyin = 'yu'
    # elif imageName[0] == '渝':
    #     pinyin = 'chongqing'
    # elif imageName[0] == '晋':
    #     pinyin = 'jin'
    # elif imageName[0] == '津':
    #     pinyin = 'tianjin'

    new_txtname = ''.join(plate)  #

    # 创建空的Dom文档对象
    doc = xml.dom.minidom.Document()
    # 创建根结点,根节点名为 annotation
    annotation = doc.createElement('annotation')  # 根节点
    # 将根节点添加到Dom文档对象中
    doc.appendChild(annotation)

    # folder节点
    folder = doc.createElement('folder')  # 创建一个名叫folder的节点
    # 内容写入
    folder_text = doc.createTextNode('JPEGImages')  # folder节点里面要写的内容
    folder.appendChild(folder_text)  # 添加到folder节点下,如果是内容,节点内容createTextNode类型,就作为内容写入;如果是createElement类型,就作为子节点添加进去
    annotation.appendChild(folder)  # 之后将添加好内容的folder节点,作为子节点添加到annotation节点中

    # filename节点
    filename = doc.createElement('filename')
    filename_text = doc.createTextNode(str(new_txtname) + '.jpg')  # 这个地方是随机生成的图片文件名
    filename.appendChild(filename_text)
    #
    annotation.appendChild(filename)

    # path节点
    path = doc.createElement('path')
    path_text = doc.createTextNode('E:\\darknet-master\\build\\darknet\\myVLPCharData\\JPEGImages\\%s.jpg' % new_txtname)
    path.appendChild(path_text)
    #
    annotation.appendChild(path)

    # sourch节点
    source = doc.createElement('source')
    #
    database = doc.createElement('database')
    database_text = doc.createTextNode('Unknown')
    database.appendChild(database_text)
    #
    source.appendChild(database)
    #
    annotation.appendChild(source)

    # size节点
    size = doc.createElement('size')

    width = doc.createElement('width')
    width_text = doc.createTextNode('272')
    width.appendChild(width_text)
    size.appendChild(width)

    height = doc.createElement('height')
    height_text = doc.createTextNode('72')
    height.appendChild(height_text)
    size.appendChild(height)

    depth = doc.createElement('depth')
    depth_text = doc.createTextNode('3')
    depth.appendChild(depth_text)
    size.appendChild(depth)
    #
    annotation.appendChild(size)

    # segmented节点
    segmented = doc.createElement('segmented')
    segmented_text = doc.createTextNode('0')
    segmented.appendChild(segmented_text)
    #
    annotation.appendChild(segmented)

    # object节点
    for [y1, y2, x1, x2], pChar in zip(labels, plate):
        object = doc.createElement('object')

        name = doc.createElement('name')
        name_text = doc.createTextNode(pChar)  # 这个地方是标签的name,也就是分类名称
        name.appendChild(name_text)
        object.appendChild(name)

        pose = doc.createElement('pose')
        pose_text = doc.createTextNode("Unspecified")
        pose.appendChild(pose_text)
        object.appendChild(pose)

        truncated = doc.createElement('truncated')
        truncated_text = doc.createTextNode("0")
        truncated.appendChild(truncated_text)
        object.appendChild(truncated)

        difficult = doc.createElement('difficult')
        difficult_text = doc.createTextNode("0")
        difficult.appendChild(difficult_text)
        object.appendChild(difficult)

        bndbox = doc.createElement('bndbox')
        #
        xmin = doc.createElement('xmin')
        xmin_text = doc.createTextNode(str(x1))
        xmin.appendChild(xmin_text)
        bndbox.appendChild(xmin)
        #
        ymin = doc.createElement('ymin')
        ymin_text = doc.createTextNode(str(y1))
        ymin.appendChild(ymin_text)
        bndbox.appendChild(ymin)
        #
        xmax = doc.createElement('xmax')
        xmax_text = doc.createTextNode(str(x2))
        xmax.appendChild(xmax_text)
        bndbox.appendChild(xmax)
        #
        ymax = doc.createElement('ymax')
        ymax_text = doc.createTextNode(str(y2))
        ymax.appendChild(ymax_text)
        bndbox.appendChild(ymax)
        #
        object.appendChild(bndbox)
        #
        annotation.appendChild(object)

    # 写入xml文本文件中
    if not os.path.exists(xmlPath):
        os.mkdir(xmlPath)
    fp = open(xmlPath + '/%s.xml' % new_txtname, 'w+')
    doc.writexml(fp, indent='\n', addindent='\t', newl='', encoding='utf-8')
    fp.close()


index = {"": 0, "": 1, "": 2, "": 3, "": 4, "": 5, "": 6, "": 7, "": 8, "": 9, "": 10, "": 11, "": 12,
         "": 13, "": 14, "": 15, "": 16, "": 17, "": 18, "": 19, "": 20, "": 21, "": 22, "": 23, "": 24,
         "": 25, "": 26, "": 27, "": 28, "": 29, "": 30, "0": 31, "1": 32, "2": 33, "3": 34, "4": 35, "5": 36,
         "6": 37, "7": 38, "8": 39, "9": 40, "A": 41, "B": 42, "C": 43, "D": 44, "E": 45, "F": 46, "G": 47, "H": 48,
         "J": 49, "K": 50, "L": 51, "M": 52, "N": 53, "P": 54, "Q": 55, "R": 56, "S": 57, "T": 58, "U": 59, "V": 60,
         "W": 61, "X": 62, "Y": 63, "Z": 64}

chars = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
             "", "", "", "", "", "", "", "", "", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
             "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
             "Y", "Z"
             ]

def AddSmudginess(img, Smu):
    rows = r(Smu.shape[0] - 50)
    cols = r(Smu.shape[1] - 50)
    adder = Smu[rows:rows + 50, cols:cols + 50]
    adder = cv2.resize(adder, (50, 50))
    #adder = cv2.bitwise_not(adder)
    img = cv2.resize(img,(50,50))
    img = cv2.bitwise_not(img)
    img = cv2.bitwise_and(adder, img)
    img = cv2.bitwise_not(img)
    return img

def rot(img,angel,shape,max_angel):  # com, [-30, 30), (70, 226, 3), 30
    """
        添加放射畸变
        img 输入图像
        factor 畸变的参数
        size 为图片的目标尺寸
    """
    size_o = [shape[1], shape[0]]  # [226, 70]
    size = (shape[1] + int(shape[0] * cos((float(max_angel)/180) * 3.14)), shape[0])  # (226+60=286, 70)  # 宽度也就是shape[1]的长度加上高度乘以高度的倾斜宽30/180,等于最后放射的宽度
    interval = abs(int(sin((float(angel) / 180) * 3.14) * shape[0]))  # [0, 34]  # 0到34之间整数
    pts1 = np.float32([[0, 0], [0, size_o[1]], [size_o[0], 0], [size_o[0], size_o[1]]])  # [[0, 0], [0, 70], [226, 0], [226, 70]]
    if angel > 0:
        pts2 = np.float32([[interval, 0], [0, size[1]], [size[0], 0], [size[0]-interval, size_o[1]]])  # [[0到34, 0], [0, 70], [226, 0], [226-0到34, 70]]
    else:
        pts2 = np.float32([[0, 0], [interval, size[1]], [size[0]-interval, 0], [size[0], size_o[1]]])  # [[0, 0], [0到34, 70], [226-0到34, 0], [226, 70]]
    M = cv2.getPerspectiveTransform(pts1, pts2)
    dst = cv2.warpPerspective(img, M, size)
    return dst

def rotRandrom(img, factor, size):  # com, 10, (286, 70)  # com.shape = (70, 286, 3)
    """
    添加透视畸变
    """
    shape = size
    pts1 = np.float32([[0, 0], [0, shape[0]], [shape[1], 0], [shape[1], shape[0]]])
    pts2 = np.float32([[r(factor), r(factor)],
                       [r(factor), shape[0] - r(factor)],
                       [shape[1] - r(factor), r(factor)],
                       [shape[1] - r(factor), shape[0] - r(factor)]])
    M = cv2.getPerspectiveTransform(pts1, pts2)
    dst = cv2.warpPerspective(img, M, size)
    return dst

def tfactor(img):
    """
    添加饱和度光照的噪声
    """
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    hsv[:, :, 0] = hsv[:, :, 0]*(0.8 + np.random.random()*0.2)
    hsv[:, :, 1] = hsv[:, :, 1]*(0.3 + np.random.random()*0.7)
    hsv[:, :, 2] = hsv[:, :, 2]*(0.2 + np.random.random()*0.8)

    img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
    return img

def random_envirment(img, data_set):
    """
    添加自然环境的噪声
    """
    index=r(len(data_set))
    env = cv2.imread(data_set[index])
    env = cv2.resize(env,(img.shape[1],img.shape[0]))
    bak = (img==0)
    bak = bak.astype(np.uint8)*255
    inv = cv2.bitwise_and(bak,env)
    img = cv2.bitwise_or(inv,img)
    return img

def GenCh(f, val):
    """
    生成中文字符
    """
    img = Image.new("RGB", (45, 70), (255, 255, 255))
    draw = ImageDraw.Draw(img)
    draw.text((0, 3), val, (0, 0, 0), font=f)
    img = img.resize((23, 70))  # 文字图片转换成了23列,70行
    A = np.array(img)
    return A

def GenCh1(f,val):
    """
    生成英文字符
    """
    img=Image.new("RGB", (23, 70), (255, 255, 255))
    draw = ImageDraw.Draw(img)
    # draw.text((0, 2), val.decode('utf-8'), (0, 0, 0), font=f)
    draw.text((0, 2), val, (0, 0, 0), font=f)
    A = np.array(img)
    return A

def AddGauss(img, level):  # level [1, 5)
    """
    添加高斯模糊
    """
    # return cv2.blur(img, (level*2 + 1, level*2 + 1))
    return cv2.blur(img, (5, 5))  # 可接受的模糊程度
    # return cv2.blur(img, (level, level))

def r(val):  # 返回随机数
    return int(np.random.random() * val)

def AddNoiseSingleChannel(single):
    """
    添加高斯噪声
    """
    diff = 255-single.max()
    noise = np.random.normal(0, 1+r(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 addNoise(img, sdev = 0.5, avg=10):
    img[:, :, 0] = AddNoiseSingleChannel(img[:, :, 0])
    img[:, :, 1] = AddNoiseSingleChannel(img[:, :, 1])
    img[:, :, 2] = AddNoiseSingleChannel(img[:, :, 2])
    return img


class GenPlate:

    def __init__(self, fontCh, fontEng, NoPlates, xmlPath):  # "./font/platech.ttf", './font/platechar.ttf', "./NoPlates"
        self.fontC = ImageFont.truetype(fontCh, 43, 0)
        self.fontE = ImageFont.truetype(fontEng, 60, 0)
        self.img = np.array(Image.new("RGB", (226, 70), (255, 255, 255)))
        # 蓝色车牌背景图
        self.bg = cv2.resize(cv2.imread("./images/template.bmp"), (226, 70))
        # 黄色车牌背景图
        # self.bg = cv2.resize(cv2.imread("./images/y1.bmp"),(226,70))
        # # self.smu = cv2.imread("./images/smu2.jpg")
        # 绿色车牌背景图
        # self.bg = cv2.resize(cv2.imread("./images/g1.jpg"),(226,70))
        self.noplates_path = []
        self.xmlPath = xmlPath  # xml文件保存位置
        for parent, parent_folder, filenames in os.walk(NoPlates):
            # print(parent, parent_folder, filenames)
            # ./NoPlates
            # []
            # ['A01_N84E28_1.jpg', 'A01_N84E28_2.jpg', 'A01_NMV802_1.jpg', 'A01_NMV802_2.jpg', 'A02_NBD719_1.jpg', 'A02_NBD719_2.jpg', 'A03_A05F26_1.jpg', 'A03_A137U8_1.jpg', 'A03_A137U8_2.jpg', 'A03_A137U8_3.jpg', 'A03_A19Z80_0.jpg', 'A03_A19Z80_1.jpg', 'A03_A19Z80_2.jpg', 'A03_A19Z80_3.jpg', 'A03_A19Z80_4.jpg', 'A03_A19Z80_5.jpg', 'A03_A19Z80_7.jpg', 'A03_A19Z80_8.jpg', 'A03_A1L828_0.jpg', 'A03_A1L828_1.jpg', 'A03_A1L828_2.jpg', 'A03_A1L828_4.jpg', 'A03_A1Z726_1.jpg', 'A03_A203J1_1.jpg', 'A03_A203J1_2.jpg', 'A03_A2H337_0.jpg', 'A03_A2H337_2.jpg', 'A03_A2M801_1.jpg', 'A03_A2M801_2.jpg', 'A03_A4F288_1.jpg', 'A03_A5X098_1.jpg', 'A03_A60L48_0.jpg', 'A03_A60L48_1.jpg', 'A03_A60L48_2.jpg', 'A03_A60L48_3.jpg', 'A03_A60L48_4.jpg', 'A03_A63X72_1.jpg', 'A03_A6U922_1.jpg', 'A03_A6U922_2.jpg', 'A03_A722S6_0.jpg', 'A03_A79A95_1.jpg', 'A03_A7N292_1.jpg', 'A03_A82E65_1.jpg', 'A03_A82E65_2.jpg', 'A03_A85V02_1.jpg', 'A03_A8B389_1.jpg', 'A03_A8B389_2.jpg', 'A03_A8B389_3.jpg', 'A03_A8B389_4.jpg', 'A03_A8E322_1.jpg', 'A03_A9F208_1.jpg', 'A03_A9F208_2.jpg', 'A03_AAC595_0.jpg', 'A03_AAC595_2.jpg', 'A03_AAC595_3.jpg', 'A03_AAC595_4.jpg', 'A03_AAC595_5.jpg', 'A03_AAQ839_0.jpg', 'A03_AAQ839_1.jpg', 'A03_AAQ839_2.jpg', 'A03_AAQ839_4.jpg', 'A03_AAQ839_5.jpg', 'A03_ABF318_1.jpg', 'A03_ABF318_2.jpg']
            for filename in filenames:
                path = parent + "/" + filename
                # path = os.path.join(parent, filename)
                # print(path)
                self.noplates_path.append(path)


    def draw(self,val):  # val =  # 鄂ARQ2CR
        offset = 2
        self.img[0:70, offset+8:offset+8+23] = GenCh(self.fontC, val[0])  # 第一个中文的位置 x, y, w, h 在0- 70行的 第10- 第10+23=33列
        # print(offset+8, ':', offset+8+23)
        self.img[0:70, offset+8+23+6:offset+8+23+6+23] = GenCh1(self.fontE, val[1])  # 第二个城市简称的位置在 第10+23 + 6 = 39- 第39+23= 62列
        # print(offset+8+23+6, ':', offset+8+23+6+23)
        for i in range(5):
            base = offset + 8 + 23 + 6 + 23 + 17 + i*23 + i*6  # 后五位编号的位置在第二个城市代号末尾列,也就是第10+23+6+23= 62列的后面加上17= 79列,17是城市代号到后五位编号中间的防伪标记点的占距
            self.img[0:70, base: base+23] = GenCh1(self.fontE, val[i+2])
            # print(base, ':', base+23)
        return self.img
        # 总结一下:(226, 70)   10-63行
        # 省份简称 [:, 10:33],
        # 城市代号[:, 39:62],
        # 第一位编号[:, 79:102],
        # 第二位编号[:, 108:131],
        # 第三位编号[:, 137:160],
        # 第四位编号[:, 166:189],
        # 第五位编号[:, 195:218]

        # 放大后
        # [11: 61, 12: 39]
        # [11:61, 46: 74]
        # [11:61, 95: 122]
        # [11:61, 129: 157]
        # [11:61, 164: 192]
        # [11:61, 199: 227]
        # [11:61, 234: 262]


    def generate(self, text):  # 鄂ARQ2CR
        # if len(text) == 9:
        if len(text) == 7:
            # fg = self.draw(text.decode(encoding="utf-8"))
            # text.encode(encoding="utf-8").decode(encoding="utf-8")
            # fg = self.draw(text.encode(encoding="utf-8").decode(encoding="utf-8"))
            fg = self.draw(text)  # 鄂ARQ2CR

            # 白色字体  # 使用时,注释掉黑色字体部分
            fg = cv2.bitwise_not(fg)
            com = cv2.bitwise_or(fg, self.bg)
            # com = rot(com, r(60)-30, com.shape, 30)  # 放射畸变  # 整体效果为 车牌行数不变,列数拉伸成∠60°的平行四边形状
            # print(com.shape)  # (70, 286, 3)  #
            # com = rotRandrom(com, 10, (com.shape[1], com.shape[0]))  # 透视畸变  # 整体效果为 梯形拉伸,
            com = tfactor(com)  # 饱和光照的噪声
            com = random_envirment(com, self.noplates_path)  # 自然环境噪声
            com = AddGauss(com, 1+r(4))  # 添加高斯模糊
            com = addNoise(com)
            print(com.shape)

            # # 黑色字体  黄车牌适用  # 使用时,注释掉白色字体部分
            # com = cv2.bitwise_and(fg,self.bg)
            #
            # com = addNoise(com)  # 先添加噪音,后进行其他操作对字体颜色的影响很小
            # # com = rot(com, r(60) - 30, com.shape, 30)
            # # com = rotRandrom(com, 10, (com.shape[1], com.shape[0]))
            # com = tfactor(com)
            # com = random_envirment(com, self.noplates_path)  # 绿色车牌背景图时,要注释掉自然环境噪声,减小对字体颜色影响
            # com = AddGauss(com, 3 + r(2))

            return com

        else:
            return None

    def genPlateString(self, pos, val):  # -1, -1
        '''
        生成车牌String,存为图片
        生成车牌list,存为label
        '''
        plateStr = ""
        plateList = []
        box = [0, 0, 0, 0, 0, 0, 0]
        if pos != -1:
            box[pos] = 1
        for unit, cpos in zip(box, range(len(box))):
            if unit == 1:
                plateStr += val
                #print plateStr
                plateList.append(val)
            else:
                if cpos == 0:
                    # plateStr += chars[r(31)]  # 控制省份随机范围,可指定某一省份
                    plateStr += chars[11]  # 控制省份随机范围,可指定某一省份  京 0  渝 3  30封顶
                    plateList.append(plateStr)
                elif cpos == 1:
                    plateStr += chars[41+r(24)]
                    plateList.append(plateStr)
                else:
                    plateStr += chars[31 + r(34)]
                    plateList.append(plateStr)
        plate = [plateList[0]]
        b = [plateList[i][-1] for i in range(len(plateList))]
        plate.extend(b[1:7])  # extend() 在一个list后面,添加另一个list的多个值
        return plateStr, plate

    # 将生成的车牌图片写入文件夹,对应的label写入label.txt
    def genBatch(self, batchSize, pos, charRange, outputPath, size):
        if not os.path.exists(outputPath):
            os.mkdir(outputPath)
        outfile = open('label.txt', 'w')
        for i in range(batchSize):
            plateStr, plate = G.genPlateString(-1, -1)

            pinyin = pypinyin.slug(plateStr[0])
            if plateStr[0] == '':
                pinyin = 'gui'
            elif plateStr[0] == '':
                pinyin = 'guilin'
            elif plateStr[0] == '':
                pinyin = 'gan'
            elif plateStr[0] == '':
                pinyin = 'jiangxi'
            elif plateStr[0] == '':
                pinyin = 'yu'
            elif plateStr[0] == '':
                pinyin = 'chongqing'
            elif plateStr[0] == '':
                pinyin = 'jin'
            elif plateStr[0] == '':
                pinyin = 'tianjin'
            elif plateStr[0] == '':
                pinyin = 'hebei'
            elif plateStr[0] == '':
                pinyin = 'ji'

            labels = [
                [11, 61, 12, 39],
                [11, 61, 46, 74],
                [11, 61, 95, 122],
                [11, 61, 129, 157],
                [11, 61, 164, 192],
                [11, 61, 199, 227],
                [11, 61, 234, 262]
            ]
            #  修改后的位置
            labels = [
                [12, 61, 11, 40],
                [12, 61, 45, 75],
                [12, 61, 94, 123],
                [12, 61, 128, 158],
                [12, 61, 163, 193],
                [12, 61, 198, 228],
                [12, 61, 233, 263]
            ]

            imageName = pinyin + plateStr[1:]  # 省份简称替换成拼音
            plate[0] = pinyin  # 标签换成转换后的拼音

            genXML(plate, labels, self.xmlPath)  # 生成对应的标签

            print(imageName)
            print(plateStr, plate)  # 鄂ARQ2CR ['鄂', 'A', 'R', 'Q', '2', 'C', 'R']
            img = G.generate(plateStr)
            # print(img.shape)
            img = cv2.resize(img, size)  # (272, 72)  # 注释掉这一步是因为要get到没放大的字符位置
            # print(img.shape)
            # cv2.imwrite(outputPath + "/" + str(i).zfill(2) + ".jpg", img)
            if not os.path.exists(outputPath):
                os.mkdir(outputPath)
            cv2.imwrite(outputPath + "/" + imageName + ".jpg", img)
            outfile.write(str(plate)+"\n")
        outfile.close()
# G = GenPlate("./font/platech.ttf", './font/platechar.ttf', "./NoPlates", 'E:\\darknet-master\\build\\darknet\\myVLPCharData\\Annotations')
G = GenPlate("./font/platech.ttf", './font/platechar.ttf', "./NoPlates", './xml')  # 测试修改时使用
#G.genBatch(100, 2, range(31, 65), "./plate_100", (272, 72))

if __name__ == '__main__':
    # G.genBatch(int(sys.argv[1]), 2, range(31, 65), sys.argv[2], (272, 72))
    # # # python genPlate.py 100 ./plate_100
    # G.genBatch(int(300), 2, range(31, 65), "E:\\darknet-master\\build\\darknet\\myVLPCharData\\JPEGImages", (272, 72))
    G.genBatch(int(13), 2, range(31, 65), "./plate_100", (272, 72))  # 测试修改时使用

    classDict = {'jing': "", 'hu': "", 'tianjin': "", 'chongqing': "", 'hebei': "", 'jin': "", 'meng': "",
                 'liao': "", 'ji': "", 'hei': "", 'su': "", 'zhe': "", 'wan': "", 'min': "", 'jiangxi': "",
                 'lu': "", 'yu': "", 'e': "", 'xiang': "", 'yue': "", 'guangxi': "", 'qiong': "", 'chuan': "",
                 'gui': "", 'yun': "", 'cang': "", 'shan': "", 'gan': "", 'qing': "", 'ning': "", 'xin': ""}

 

 

 

网盘文件链接(基础代码皆来自-作者:Charlotte77):

链接:https://pan.baidu.com/s/1qPnEvZh8fzmqmOuuZfe8Ag
提取码:m1sw 

posted @ 2020-11-12 10:46  海阔天高不知处  阅读(1598)  评论(0编辑  收藏  举报