Pytorch_CCPD2019_to_CoCO_2_YOLO數據集


CCPD2019數據集 CCPD: Chinese City Parking Dataset

步驟:
 prepare_data 
    python reformat_CCPD.py 
	python dataset_provider.py

 # Base	200	正常車牌
 # FN		20	距離攝像頭相當的遠或者相當近
 # DB		20	光線暗或者比較亮
 # Rotate	10	水平傾斜20-25°,垂直傾斜-10-10°
 # Tilt	10	水平傾斜15-45°,垂直傾斜15-45°
 # Weather	10	在雨天,雪天,或者霧天
 # Blur	5	由於相機抖動造成的模糊
 # Challenge	10	其他的比較有挑戰性的車牌
 # NP		5	沒有車牌的新車
 	
 	
 # 0236-16_34-222&408_398&520-377&473_222&520_243&455_398&408-0_0_28_14_33_32_29-69-74.jpg	
 	
 每個名稱可以分為七個字段。這些字段解釋如下。
 
 面積:牌照面積與整個圖片區域的面積比。
 傾斜度:水平傾斜程度和垂直傾斜度。
 邊界框坐標:左上和右下頂點的坐標。
 四個頂點位置:整個圖像中LP的四個頂點的精確(x,y)坐標。這些坐標從右下角->左下角->左上角->右上角	
 	
 面積    0236
 傾斜度    16_34
 邊界框坐標   222&408_398&520
 四個頂點位置    377&473_222&520_243&455_398&408
 是車牌號每個字符的索引    0_0_28_14_33_32_29
 亮度    69
 模糊程度   74
 
 
 車牌號 號碼由一個漢字,一個字母和五個字母或數字組成
 provinces = ["皖", "滬", "津", "渝", "冀", "晉", "蒙", "遼", "吉", "黑", "蘇", "浙", "京", "閩", "贛", "魯", "豫", "鄂", "湘", "粵", "桂", "瓊", "川", "貴", "雲", "藏", "陝", "甘", "青", "寧", "新", "警", "學", "O"]
 alphabets = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
              'X', 'Y', 'Z', 'O']
 ads = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
        'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'O']	

格式變換

import os
import cv2
import random
import os.path


def annotation_from_name(file_name):
    # 圖片的名稱就是標簽,由"_"字符分隔
    # # 0236-16_34-222&408_398&520-377&473_222&520_243&455_398&408-0_0_28_14_33_32_29-69-74.jpg
    file_name = file_name[:-4]
    # 0236-16_34-222&408_398&520-377&473_222&520_243&455_398&408-0_0_28_14_33_32_29-69-74
    name_split = file_name.split('-')
    location = name_split[2]
    # 邊界框坐標 222&408_398&520
    location = location.split('_')
    # 222&408
    left_top = location[0].split('&')
    # 398&520
    right_bottom = location[1].split('&')
    x1 = int(left_top[0])
    y1 = int(left_top[1])
    x2 = int(right_bottom[0])
    y2 = int(right_bottom[1])
    # 四個頂點位置  377&473_222&520_243&455_398&408
    x1y1 = name_split[3].split('_')[0].split('&')
    x2y2 = name_split[3].split('_')[1].split('&')
    x3y3 = name_split[3].split('_')[2].split('&')
    x4y4 = name_split[3].split('_')[3].split('&')
    # 邊界框兩個頂點以及四個頂點位置 0-11
    return (x1, y1, x2, y2, int(x1y1[0]), int(x1y1[1]), int(x2y2[0]), int(x2y2[1]), int(x3y3[0]), int(x3y3[1]), int(x4y4[0]), int(x4y4[1]))


def generate_data_list(ccpd_path= r'D:\data\CCPD2019' ):
    # D:\data\CCPD2019\
    # Base	200	正常車牌
    # Blur	5	由於相機抖動造成的模糊
    # Challenge	10	其他的比較有挑戰性的車牌
    # DB		20	光線暗或者比較亮
    # FN		20	距離攝像頭相當的遠或者相當近
    # Rotate	10	水平傾斜20-25°,垂直傾斜-10-10°
    # Tilt	10	水平傾斜15-45°,垂直傾斜15-45°
    # Weather	10	在雨天,雪天,或者霧天
    # NP		5	沒有車牌的新車
    # 輸入的數據集
    image_roots = [os.path.join(ccpd_path , 'ccpd_base'),
                   os.path.join(ccpd_path ,'ccpd_blur'),
                   os.path.join(ccpd_path ,'ccpd_challenge'),
                   os.path.join(ccpd_path ,'ccpd_db'),
                   os.path.join(ccpd_path ,'ccpd_fn'),
                  # os.path.join(ccpd_path ,'ccpd_np'),
                   os.path.join(ccpd_path ,'ccpd_rotate'),
                   os.path.join(ccpd_path ,'ccpd_tilt'),
                   os.path.join( ccpd_path, 'ccpd_weather')]
    #輸出
    train_list_file_path = r'D:\data\data_folder\data_list_CCPD_train.txt'
    test_list_file_path = r'D:\data\data_folder\data_list_CCPD_test.txt'
    if not os.path.exists(os.path.dirname(train_list_file_path)):
        os.makedirs(os.path.dirname(train_list_file_path))
    fout_train = open(train_list_file_path, 'w')
    fout_test = open(test_list_file_path, 'w')
    # train_proportion:標注數據中用於train的比例 -取值 0到1之間
    train_proportion = 1
    # 訓練圖片的序數
    train_counter = 0
    test_counter = 0
    for root in image_roots:
        print(root)
        # 圖片的名稱列表
        file_name_list = [name for name in os.listdir(root) if name.endswith('.jpg')]
        # shuffle 重新隨機圖片列表
        random.shuffle(file_name_list)
        # 訓練集 和測試集 數據
        file_name_list_train = file_name_list[:int(len(file_name_list) * train_proportion)]
        file_name_list_test = file_name_list[int(len(file_name_list) * train_proportion):]


        for file_name in file_name_list_train:
            location_annotation = annotation_from_name(file_name)
            line = os.path.join(root, file_name) + ',1,1,' + str(location_annotation[0]) + ',' + str(location_annotation[1]) + ',' + str(location_annotation[2]) + ',' + str(location_annotation[3])\
             +',' + str(location_annotation[4]) + ',' + str(location_annotation[5]) + ',' + str(location_annotation[6]) + ',' + str(location_annotation[7]) + ',' + str(location_annotation[8])\
             +',' + str(location_annotation[9]) + ',' + str(location_annotation[10]) + ',' + str(location_annotation[11])
            
            fout_train.write(line + '\n')
            train_counter += 1
            print(train_counter)

        for file_name in file_name_list_test:
            location_annotation = annotation_from_name(file_name)
            print("test",file_name)
            line = os.path.join(root, file_name) + ',1,1,' + str(location_annotation[0]) + ',' + str(location_annotation[1]) + ',' + str(location_annotation[2]) + ',' + str(location_annotation[3])\
             +',' + str(location_annotation[4]) + ',' + str(location_annotation[5]) + ',' + str(location_annotation[6]) + ',' + str(location_annotation[7]) + ',' + str(location_annotation[8])\
             +',' + str(location_annotation[9]) + ',' + str(location_annotation[10]) + ',' + str(location_annotation[11])            
            
            fout_test.write(line + '\n')
            test_counter += 1
            print(test_counter)

    fout_train.close()
    fout_test.close()



if __name__ == '__main__':
    # 返回值 [image absolute path],[pos/neg flag],[num of bboxes],[x1],[y1],[width1],[height1],[x2],[y2],[width2],[height2]......
    generate_data_list(ccpd_path=r'D:\data\CCPD2019')

模型的路徑

 import sys
 sys.path.append("D:/Detection/Pytorch_Retina_License_Plate-master")  

ccpd_2_coco

# -*- coding: utf-8 -*-#
#     python ccpd_2_coco.py --data "./data"

import datetime
import json
import cv2
from random import randint
import numpy as np
from pathlib import Path
from PIL import Image
import os
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--data",
                    default=None,
                    type=str,
                    required=True,
                    help="The input data dir. Should contain all the images")
args = parser.parse_args()
IMAGE_DIR = Path(args.data)

INFO = {
    "description": "CCPD Dataset in COCO Format",
    "url": "",
    "date_created": datetime.datetime.utcnow().isoformat(' ')  # 顯示此刻時間,格式:'2019-04-30 02:17:49.040415'
}

LICENSES = [
    {
        "id": 1,
        "name": "ALL RIGHTS ",
        "url": ""
    }
]
CATEGORIES = [
    {
        'id': 1,
        'name': 'license plate',
        'supercategory': 'shape',
    },
    {
        'id': 2,
        'name': 'background',
        'supercategory': 'shape',
    }
]


def random_color(class_id):
    '''預定義12種顏色,基本涵蓋kjdz所有label類型
    顏色對照網址:https://tool.oschina.net/commons?type=3'''
    colorArr = [(255,0,0), # 紅色
                (255,255,0), # 黃色
                (0, 255, 0), # 綠色
                (0,0,255), # 藍色
                (160, 32, 240), # 紫色
                (165, 42, 42), # 棕色
                (238, 201, 0), # gold
                (255, 110, 180), # HotPink1
                (139, 0 ,0), #DarkRed
                (0 ,139 ,139),#DarkCyan
                (139, 0 ,139),#	DarkMagenta
                (0 ,0 ,139) # dark blue
                ]
    if class_id < 11:
        return colorArr[class_id]
    else: # 如有特殊情況,類別數超過12,則隨機返回一個顏色
        rm_col = (randint(0,255),randint(0,255),randint(0,255))
        return rm_col

# 獲取 bounding-box, segmentation 信息
# 輸入:image path
# 返回:
#   bounding box
#   four locations

def get_info(im_file):
    img_name = str(im_file)
    lbl = img_name.split('/')[-1].rsplit('.', 1)[0].split('-')[-3] # label: '16_2_32_30_25_29_6'
    iname = img_name.rsplit('/', 1)[-1].rsplit('.', 1)[0].split('-')
    [leftUp, rightDown] = [[float(eel) for eel in el.split('&')] for el in iname[2].split('_')] # bounding box
    height = rightDown[1]-leftUp[1]
    width = rightDown[0]-leftUp[0]
    left = leftUp[0]
    top = leftUp[1]
    segmentation = [[float(eel) for eel in el.split('&')] for el in iname[3].split('_')] # four vertices locations
    return [left, top, width, height], segmentation


# 計算任意多邊形的面積,頂點按照順時針或者逆時針方向排列
def compute_polygon_area(points):
    point_num = len(points)
    if(point_num < 3):
        return 0.0
    s = points[0][1] * (points[point_num-1][0] - points[1][0])
    #for i in range(point_num): # (int i = 1 i < point_num ++i):
    for i in range(1, point_num):
        s += points[i][1] * (points[i-1][0] - points[(i+1)%point_num][0])
    return abs(s/2.0)


def main():
    # coco lable文件(如training2017.json)需要存儲的信息
    coco_output = {
        "info": INFO,
        "licenses": LICENSES,
        "categories": CATEGORIES,
        "images": [],
        "annotations": []
    }

    # 初始化id(以后依次加一)
    image_id = 1
    annotation_id = 1


    # 加載圖片信息
    im_files = [f for f in IMAGE_DIR.iterdir()]
    im_files.sort(key=lambda f: f.stem,reverse=True)  # 排序,防止順序錯亂、數據和標簽不對應
    # print("im-length:",len(im_files),"\n im_files:",im_files)

    for im_file in im_files:
        # 寫入圖片信息(id、圖片名、圖片大小),其中id從1開始
        image = Image.open(im_file)
        #im_info = pycococreatortools.create_image_info( image_id, im_file.name, image.size) # 圖片信息
        im_info =  {
            "id" : image_id,   # 圖像id,可從0開始
            "width" :  image.size[0],   # 圖像的寬
            "height" : image.size[1],  # 圖像的高
            "file_name" : im_file.name,   # 文件名
            "license" : None,   # 遵循哪個協議
            "flickr_url" : None,   # flickr圖片鏈接url
            "coco_url" : None,   # COCO圖片鏈接url
            "date_captured" : "2019/05/20", # 獲取數據的日期
        }
        coco_output['images'].append(im_info) # 存儲圖片信息(id、圖片名、大小)


        annotation_info_list = []  # 存儲標注信息
        # 處理label信息, 包括左上角、右下角、四個角點(用於分割)
        bounding_box, segmentation = get_info(im_file)
        class_id = 1  # id 為數字形式,如 1,此時是list形式,后續需要轉換 # 指定為1,因為只有”是車牌“這一類
        area = compute_polygon_area(segmentation) # 當前segmentation的面積(比bounding box更精確)
        annot ={
            "id" : annotation_id,  # 注釋id編號
            "image_id" : image_id,  # 圖像id編號
            "category_id" : class_id,  # 類別id編號
            "segmentation" : segmentation,  # 分割具體數據
            "area" : area,  # 目標檢測的區域大小
            "bbox" : bounding_box,
            "iscrowd" : 0 ,  # 目標是否被遮蓋,默認為0
        }
        annotation_info_list.append(annot)

        # 上面得到單張圖片的所有bounding-box信息,接下來每單張圖片存儲一次
        for annotation_info in annotation_info_list:
            if annotation_info is not None:
                coco_output['annotations'].append(annotation_info)
        print(image_id)
        image_id += 1
       
    # 保存成json格式
    print("[INFO] Storing annotations json file...")
    output_json = Path(f'D:\data\CCPD2019\ccpd_annotations.json')
    with output_json.open('w', encoding='utf-8') as f:
        json.dump(coco_output, f)
    print("[INFO] Annotations JSON file saved in:", str(output_json))


if __name__ == "__main__":
    main()

COCO2YOLO

"""
COCO 格式的數據集轉化為 YOLO 格式的數據集
--json_path 輸入的json文件路徑
--save_path 保存的文件夾名字,默認為當前目錄下的labels。
"""

import os 
import json
from tqdm import tqdm
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--json_path', default='./instances_val2017.json',type=str, help="input: coco format(json)")
parser.add_argument('--save_path', default='./labels', type=str, help="specify where to save the output dir of labels")
arg = parser.parse_args()

def convert(size, box):
    dw = 1. / (size[0])
    dh = 1. / (size[1])
    x = box[0] + box[2] / 2.0
    y = box[1] + box[3] / 2.0
    w = box[2]
    h = box[3]

    x = x * dw
    w = w * dw
    y = y * dh
    h = h * dh
    return (x, y, w, h)

if __name__ == '__main__':
    json_file =   arg.json_path # COCO Object Instance 類型的標注
    ana_txt_save_path = arg.save_path  # 保存的路徑

    data = json.load(open(json_file, 'r'))
    if not os.path.exists(ana_txt_save_path):
        os.makedirs(ana_txt_save_path)
    
    id_map = {} # coco數據集的id不連續!重新映射一下再輸出!
    with open(os.path.join(ana_txt_save_path, 'classes.txt'), 'w') as f:
        # 寫入classes.txt
        for i, category in enumerate(data['categories']): 
            f.write(f"{category['name']}\n") 
            id_map[category['id']] = i
    # print(id_map)

    for img in tqdm(data['images']):
        filename = img["file_name"]
        img_width = img["width"]
        img_height = img["height"]
        img_id = img["id"]
        head, tail = os.path.splitext(filename)
        ana_txt_name = head + ".txt"  # 對應的txt名字,與jpg一致
        f_txt = open(os.path.join(ana_txt_save_path, ana_txt_name), 'w')
        for ann in data['annotations']:
            if ann['image_id'] == img_id:
                box = convert((img_width, img_height), ann["bbox"])
                f_txt.write("%s %s %s %s %s\n" % (id_map[ann["category_id"]], box[0], box[1], box[2], box[3]))
        f_txt.close()
1.修改數據集的格式
/data1/mydata/CCPD2019/ccpd_weather
python  predeal_data.py  --data /data1/mydata/CCPD2019/ccpd_weather

YOLO 數據目錄結構
 https://github.com/Weifeng-Chen/DL_tools/blob/main/coco2yolo.py
python  coco2yolo.py  --json_path /data1/mydata/CCPD2019/TestTrain/label/train/ccpd_annotations.json   --save_path /data1/mydata/CCPD2019/TestTrain/labels
parser.add_argument('--json_path', default='./instances_val2017.json',type=str, help="input: coco format(json)")
parser.add_argument('--save_path', default='./labels', type=str, help="specify where to save the output dir of labels")
yolo的數據格式為 (x_center, y_center, w, h); 而coco里面的bbox格式為(x_left, y_top, w, h) 。 
2.修改配置文件
 vim mydataset.yaml

 # Train/val/test sets as 1) dir: path/to/imgs, 2) file: /data1/mydata/CCPD2019/label_ccpd/ccpd_annotations.json, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
 train: /data1/mydata/CCPD2019/TestTrain/images/train  # train images (relative to 'path') 128 images
 val: /data1/mydata/CCPD2019/TestTrain/images/train  # val images (relative to 'path') 128 images
 test:  # test images (optional)
3.修改train中的下載部分
提前下載到相應的位置中
 01.Downloading https://ultralytics.com/assets/Arial.ttf to /root/.config/Ultralytics/Arial.ttf...
 02.Downloading  pt to  weight dir
 python train.py --img 640 --batch 16 --epochs 3 --data mydataset.yaml  --weights yolov5s.pt
   /opt/License-Plate-Detector-yolo# python train.py --img 640 --batch 16 --epochs 3 --data mydataset.yaml  --weights ./weights/yolov5s.pt

 # 需要滿足特定的目錄結構
 No labels found in /data1/mydata/CCPD2019/TestTrain/labels/train.cache. Can not train without labels.
4.檢測
  cd /opt/yolov5-master
  python detect.py --weights /opt/yolov5-master/runs/train/exp/weights/best.pt  --data  /opt/yolov5-master/data/mydataset.yaml --source /data1/mCamera012000.jpg
  
  /opt/yolov5-master/runs/train/exp/weights/last.pt

參考

 https://github.com/gm19900510/Pytorch_Retina_License_Plate/blob/master/prepare_data/reformat_CCPD.py
  License Plate Detection with Yolov5 https://github.com/zeusees/License-Plate-Detector 
 https://github.com/weidafeng/CCPD2COCO
 https://github.com/Weifeng-Chen/DL_tools/blob/main/coco2yolo.py


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM