YOLO convert COCO format to YOLO format

tags: YOLO

Step 1: Download dataset

如果檔案太大,可以不要下載test2017.zip 以及 unlabeled2017.zip (我也僅用 train(64K images) 以及 val (26K images)做,大概就有40GB左右了)

mkdir coco cd coco mkdir images cd images wget -c http://images.cocodataset.org/zips/train2017.zip wget -c http://images.cocodataset.org/zips/val2017.zip wget -c http://images.cocodataset.org/zips/test2017.zip wget -c http://images.cocodataset.org/zips/unlabeled2017.zip unzip train2017.zip unzip val2017.zip unzip test2017.zip unzip unlabeled2017.zip rm train2017.zip rm val2017.zip rm test2017.zip rm unlabeled2017.zip cd ../ wget -c http://images.cocodataset.org/annotations/annotations_trainval2017.zip wget -c http://images.cocodataset.org/annotations/stuff_annotations_trainval2017.zip wget -c http://images.cocodataset.org/annotations/image_info_test2017.zip wget -c http://images.cocodataset.org/annotations/image_info_unlabeled2017.zip unzip annotations_trainval2017.zip unzip stuff_annotations_trainval2017.zip unzip image_info_test2017.zip unzip image_info_unlabeled2017.zip rm annotations_trainval2017.zip rm stuff_annotations_trainval2017.zip rm image_info_test2017.zip rm image_info_unlabeled2017.zip

Step 2: Convert to xml

cd coco vim convert2xml.py

加入內容如下:

import os from shutil import copyfile from pycocotools.coco import COCO from xml.dom.minidom import parseString from lxml.etree import Element, SubElement, tostring from IPython.display import clear_output def update_progress(progress): bar_length = 20 if isinstance(progress, int): progress = float(progress) if not isinstance(progress, float): progress = 0 if progress < 0: progress = 0 if progress >= 1: progress = 1 block = int(round(bar_length * progress)) clear_output(wait = True) text = "Progress: [{0}] {1:.1f}%".format( "#" * block + "-" * (bar_length - block), progress * 100) print(text) current_path = os.path.abspath(os.getcwd()) COCO_ANNOTATIONS_PATH = current_path + "/annotations/instances_val2017.json" COCO_IMAGES_DIRECTORY = current_path + "/images/val2017/" EXTRACTED_SAVING_PATH = current_path + "/test_coco_format/" SAVE_FOLDER = EXTRACTED_SAVING_PATH.split('/')[len(EXTRACTED_SAVING_PATH.split('/'))-2] if not os.path.exists(EXTRACTED_SAVING_PATH): os.mkdir(EXTRACTED_SAVING_PATH) coco = COCO(COCO_ANNOTATIONS_PATH) cats = coco.loadCats(coco.getCatIds()) nms=[cat['name'] for cat in cats] target_classes = [] with open(current_path + '/classes.txt', 'r') as f: for line in f.readlines(): target_classes.append(line.strip()) img_dict = {} for classes in target_classes: catIds = coco.getCatIds(catNms=[classes]) imgIds = coco.getImgIds(catIds=catIds) for imgID in imgIds: try: content = '' content = img_dict[imgID] + ',' except: pass img_dict[imgID] = content + classes total_progress = len(img_dict) progress = 0 for img_id in img_dict: progress +=1 annotation_ids = coco.getAnnIds(img_id) annotations = coco.loadAnns(annotation_ids) image_meta = coco.loadImgs(annotations[0]["image_id"])[0] node_root = Element('annotation') node_folder = SubElement(node_root, 'folder') node_folder.text = SAVE_FOLDER node_filename = SubElement(node_root, 'filename') node_filename.text = image_meta['file_name'] node_size = SubElement(node_root, 'size') node_width = SubElement(node_size, 'width') node_width.text = str(image_meta['width']) node_height = SubElement(node_size, 'height') node_height.text = str(image_meta['height']) node_depth = SubElement(node_size, 'depth') node_depth.text = '3' for i in range(len(annotations)): entity_id = annotations[i]["category_id"] entity = coco.loadCats(entity_id)[0]["name"] if entity in target_classes: node_object = SubElement(node_root, 'object') node_name = SubElement(node_object, 'name') node_name.text = entity node_difficult = SubElement(node_object, 'difficult') node_difficult.text = '0' node_bndbox = SubElement(node_object, 'bndbox') node_xmin = SubElement(node_bndbox, 'xmin') node_xmin.text = str(round(annotations[i]['bbox'][0])) node_ymin = SubElement(node_bndbox, 'ymin') node_ymin.text = str(round(annotations[i]['bbox'][1])) node_xmax = SubElement(node_bndbox, 'xmax') node_xmax.text = str(round(annotations[i]['bbox'][0]+annotations[i]['bbox'][2])) node_ymax = SubElement(node_bndbox, 'ymax') node_ymax.text = str(round(annotations[i]['bbox'][1]+annotations[i]['bbox'][3])) xml = tostring(node_root, pretty_print=True) dom = parseString(xml) with open( EXTRACTED_SAVING_PATH + image_meta['file_name'].split('.')[0] + '.xml','w') as xml_file: xml_file.write(dom.toxml()) copyfile(COCO_IMAGES_DIRECTORY + image_meta['file_name'], EXTRACTED_SAVING_PATH + image_meta['file_name']) update_progress(progress/total_progress)

修改 convert.py 內的三個變數

COCO_ANNOTATIONS_PATH // COCO ANNOTATIONS
COCO_IMAGES_DIRECTORY // COCO IMAGES
EXTRACTED_SAVING_PAT // 轉換後的路徑

建立classes.txt,放入你想抓出來的class名稱,並以Enter換行作為區隔

vim classes.txt

convert xml to yolo format

from bs4 import BeautifulSoup import os import shutil from IPython.display import clear_output status_dic = {'person':0} #用dictionary 記錄label的名稱 def getYoloFormat(filename, label_path, img_path, yolo_path, newname): with open(label_path+filename, 'r') as f: soup = BeautifulSoup(f.read(), 'xml') imgname = soup.select_one('filename').text #讀取xml image_w = soup.select_one('width').text image_h = soup.select_one('height').text ary = [] for obj in soup.select('object'): #取出xmin, xmax, ymin, ymax及name xmin = int(obj.select_one('xmin').text) #並且用status_dictionary 來轉換name,good =>2 xmax = int(obj.select_one('xmax').text) ymin = int(obj.select_one('ymin').text) ymax = int(obj.select_one('ymax').text) objclass = status_dic.get(obj.select_one('name').text) x = (xmin + (xmax-xmin)/2) * 1.0 / float(image_w) #YOLO吃的參數檔有固定的格式 y = (ymin + (ymax-ymin)/2) * 1.0 / float(image_h) #先照YOLO的格式訂好x,y,w,h w = (xmax-xmin) * 1.0 / float(image_w) h = (ymax-ymin) * 1.0 / float(image_h) ary.append(' '.join([str(objclass), str(x), str(y), str(w), str(h)])) if os.path.exists(img_path+imgname+'.jpg'): # 圖片本來在image裡面,把圖片移到yolo資料夾下 shutil.copyfile(img_path+imgname+'.jpg', yolo_path+newname+'.jpg') #同時把yolo參數檔寫到yolo之下 with open(yolo_path+newname+'.txt', 'w') as f: f.write('\n'.join(ary)) elif os.path.exists(img_path+imgname): #有的labelImg名稱有自動加上.jpg shutil.copyfile(img_path+imgname, yolo_path+newname+'.jpg') with open(yolo_path+newname+'.txt', 'w') as f: f.write('\n'.join(ary)) def update_progress(progress): bar_length = 20 if isinstance(progress, int): progress = float(progress) if not isinstance(progress, float): progress = 0 if progress < 0: progress = 0 if progress >= 1: progress = 1 block = int(round(bar_length * progress)) clear_output(wait = True) text = "Progress: [{0}] {1:.1f}%".format( "#" * block + "-" * (bar_length - block), progress * 100) print(text) labelpath = '/home/jim93073/coco/test_coco_format/' #設定路徑 imgpath = '/home/jim93073/coco/test_coco_format/' yolopath = '/home/jim93073/coco/test_yolo_person/' ary = [] total_progress = len(os.listdir(labelpath)) progress = 0 for idx, f in enumerate(os.listdir(labelpath)): #透過getYoloFormat將圖像和參數檔全部寫到YOLO下 progress += 1 try: if f.split('.')[1] == 'xml': getYoloFormat(f, labelpath, imgpath, yolopath, str(idx)) except Exception as e: print(e) update_progress(progress/total_progress)

修改 labelpath 、imgpath 、yolopath

labelpath // 放置 xml 檔案的地方
imgpath // 放置 image 的地方
yolopath // 輸出成 yolo 格式的地方

建立訓練的 txt 檔案

import os import random save_path = '/home/jim93073/coco/test_yolo_person/' create_txt_path = '/home/jim93073/coco/cfg/test_person.txt' datasets = [save_path + f for f in os.listdir(save_path) if f.endswith('.jpg')] random.shuffle(datasets) len_dataset = int(len(datasets) * 1.0) random.shuffle(datasets) print('All data length',len(datasets)) print(len_dataset) # print(len(datasets)-len_dataset) with open(create_txt_path, 'w') as f: f.write('\n'.join(datasets[:len_dataset]))

修改 save_path 及 create_txt_path

save_path // 要訓練的 image 路徑
create_txt_path //轉乘 txt 的檔案名稱