<reponame>WanqingXia/yolov3-unseen<gh_stars>0
"""
This script has two functions:
1. Combine original train and val folder and split them into seen/unseen folder
2. Create all the files needed for training (.names data.data .shape .txt hyp cfg)
"""
import os
import random

from tqdm import tqdm
from lxml import etree
import json
import shutil

# root directory of YCB dataset
dataset_root = "../YCB_Video_Dataset"
data_root = os.path.join(dataset_root, "data")
names_root = os.path.join(dataset_root, "image_sets", "trainval.txt")
class_root = os.path.join(dataset_root, "image_sets", "classes.txt")

# Check whether all the original directories exist
assert os.path.exists(data_root), "YCB data path not exist..."
assert os.path.exists(names_root), "YCB names path not exist..."
assert os.path.exists(class_root), "YCB class path not exist..."

# save file directory (all train and val image, depth, labels )
save_file_root = "../my_ycb_video"
train_path = os.path.join(save_file_root, "train")
val_path = os.path.join(save_file_root, "val")
cfg_hyp_root = "./cfg"
if os.path.exists(cfg_hyp_root) is False:
    print("Please download hyp.yaml and yolov3-spp.cfg file first")
    os.makedirs(save_file_root)
# Concatenate the path for saving all the data (images, depth, labels)
train_images_path = os.path.join(train_path, "images")
train_label_path = os.path.join(train_path, "labels")
train_depth_path = os.path.join(train_path, "depth")
val_image_path = os.path.join(val_path, "images")
val_label_path = os.path.join(val_path, "labels")
val_depth_path = os.path.join(val_path, "depth")

# make all the directories
if os.path.exists(save_file_root) is False:
    # parent level
    os.makedirs(save_file_root)
    os.makedirs(train_path)
    os.makedirs(val_path)
    # child level
    os.makedirs(train_images_path)
    os.makedirs(train_label_path)
    os.makedirs(train_depth_path)
    os.makedirs(val_image_path)
    os.makedirs(val_label_path)
    os.makedirs(val_depth_path)

"""
For every file in the dictionary, put images that only have seen objects into train, only have unseen objects into val,
then randomly choose 40000 images to put in val (skip if the file already exists, the file number will be smaller than 
40000), all the others put in train
"""
def choose_files(classes_file):
    train_val_file = open(names_root, "r").readlines()
    # create train_data.txt val_data.txt to store all the names
    train_split = open(os.path.join(save_file_root, "train_split.txt"), 'w')
    val_split = open(os.path.join(save_file_root, "val_split.txt"), 'w')
    class_dict = open(classes_file, 'r').readlines()
    index_dict = []
    num_val = 0
    num_train = 0
    # For every file in the dictionary, put images that only have seen objects into train,
    # images that only have unseen objects into val
    for i, file in tqdm(enumerate(train_val_file), desc='Spliting files', total=len(train_val_file)):
        label_file = open(os.path.join(data_root, file.strip('\n') + "-box.txt"), "r")
        label = label_file.readlines()
        num = 0
        for index, line in enumerate(label):
            if class_dict[0].strip('\n') in line or class_dict[7].strip('\n') in line \
                    or class_dict[8].strip('\n') in line or class_dict[10].strip('\n') in line \
                    or class_dict[14].strip('\n') in line or class_dict[16].strip('\n') in line:
                num += 1
        if num == len(label):
            val_split.write(file)
            index_dict.append(i)
            num_val += 1
        elif num == 0:
            train_split.write(file)
            index_dict.append(i)
            num_train += 1
        else:
            pass
        label_file.close()

    # randomly choose another 40000 images  in the rest of images and put into val
    # using same random seed to keep result the same
    randomlist = []
    random.seed(0)
    # generate 40000 random index numbers
    num_random_values = 40000
    for i in range(0, num_random_values):
        n = random.randint(0, 133936)
        randomlist.append(n)
    # put the file correspond to the random index into val
    for i in tqdm(range(0, len(randomlist)), desc='Randomly choosing val images', total=len(randomlist)):
        if randomlist[i] in index_dict:
            pass
        else:
            val_split.write(train_val_file[randomlist[i]])
            index_dict.append(randomlist[i])
            num_val += 1
    # put the rest of unchosen images into train
    for i, file in tqdm(enumerate(train_val_file), desc='Allocating rest of images to train', total=len(train_val_file)):
        if i in index_dict:
            pass
        else:
            train_split.write(file)
            num_train += 1
    train_split.close()
    val_split.close()
    print("\nnumber of train file chosen: {}".format(num_train))
    print("\nnumber of val file chosen: {}".format(num_val))
    print("\nfile choosing finished")

"""
Copy files from the original folder to our division folder train/val
Record the new path for every image in a txt file
"""
def copy_files(train_val):
    if train_val == "train":
        dict_ini = open(os.path.join(save_file_root, "train_split.txt"), 'r').readlines()
        train_val_image_path = train_images_path
        train_val_depth_path = train_depth_path
        train_val_data_record = open(os.path.join(save_file_root, "train_data.txt"), 'w')
    elif train_val == "val":
        dict_ini = open(os.path.join(save_file_root, "val_split.txt"), 'r').readlines()
        train_val_image_path = val_image_path
        train_val_depth_path = val_depth_path
        train_val_data_record = open(os.path.join(save_file_root, "val_data.txt"), 'w')
    # loop through all file names in the train/val_split.txt
    for i, name in tqdm(enumerate(dict_ini), desc='Copying {} files over...'.format(train_val), total=len(dict_ini)):
        file = name.strip('\n')
        file_path = os.path.join(data_root, file)
        # find color and depth images in their original path
        img_file = file_path + '-color.png'
        dep_file = file_path + '-depth.png'
        assert os.path.exists(img_file), "\n warning: image file {} not exist...".format(img_file)
        assert os.path.exists(dep_file), "\n warning: depth file {} not exist...".format(dep_file)
        # create new path and copy image files over
        img_path_copy_to = os.path.join(train_val_image_path, file.split('/')[0] + "-" + file.split('/')[1] + "-color.png")
        train_val_data_record.write(img_path_copy_to + "\n")
        if os.path.exists(img_path_copy_to) is False:
            shutil.copyfile(img_file, img_path_copy_to)
        dep_path_copy_to = os.path.join(train_val_depth_path, file.split('/')[0] + "-" + file.split('/')[1] + "-depth.png")
        if os.path.exists(dep_path_copy_to) is False:
            shutil.copyfile(dep_file, dep_path_copy_to)
    print('\nCopy {} files finished'.format(train_val))
    train_val_data_record.close()

"""
translate from ycb style labelling to yolo style
1. change object name to its index number in my_class.txt
2. change box boundary from absolute value [xmin, ymin, xmax, ymax] to [x, y, w, h]
"""
def translate_info(classes_file, train_val):
    # All images in ycb are the same size
    img_height = 480
    img_width = 640
    # read class file
    class_dict = open(classes_file, 'r').readlines()
    # write to different files for train dataset and val dataset
    if train_val == "train":
        dict_ini = open(os.path.join(save_file_root, "train_split.txt"), 'r').readlines()
        txt_save_path = train_label_path
        txt_dict = open(os.path.join(save_file_root, "train_labels.txt"), 'w')
    elif train_val == "val":
        dict_ini = open(os.path.join(save_file_root, "val_split.txt"), 'r').readlines()
        txt_save_path = val_label_path
        txt_dict = open(os.path.join(save_file_root, "val_labels.txt"), 'w')
    # record how many files have bad data
    bad_data_count = 0
    # loop through all file names in train/val_split.txt
    for i, name in tqdm(enumerate(dict_ini), desc='Translating {} labels...'.format(train_val), total=len(dict_ini)):
        file = name.strip('\n')
        file_path = os.path.join(data_root, file)
        txt_file = file_path + '-box.txt'
        assert os.path.exists(txt_file), "\n warning: txt file {} not exist...".format(txt_file)
        # rename the txt file based on its original path and name
        txt_save_name = os.path.join(txt_save_path, file.split('/')[0] + "-" + file.split('/')[1] + "-box.txt")
        # read the information for every object in the label file
        with open(txt_save_name, "w") as f, open(txt_file, "r") as r:
            for index, obj in enumerate(r.readlines()):
                # get the bounding box for each object
                xmin = float(obj.split(" ")[1])
                ymin = float(obj.split(" ")[2])
                xmax = float(obj.split(" ")[3])
                ymax = float(obj.split(" ")[4])
                # find the corresponded index for the object name
                class_name = obj.split(" ")[0].strip()[4:]
                index_num = 0
                for class_index, classes in enumerate(class_dict):
                    if class_name == classes.strip('\n'):
                        index_num = class_index
                        break
                # check the data to make sure max is always bigger than min, which will affect the calculation of loss
                if xmax <= xmin or ymax <= ymin:
                    bad_data_count += 1
                    continue
                # translate bounding box to YOLO format (centre x, centre y, width, height)
                xcenter = xmin + (xmax - xmin) / 2
                ycenter = ymin + (ymax - ymin) / 2
                w = xmax - xmin
                h = ymax - ymin

                # real coordinate to relative coordinate (relative to the size of image), 6 decimal places
                xcenter = round(xcenter / img_width, 6)
                ycenter = round(ycenter / img_height, 6)
                w = round(w / img_width, 6)
                h = round(h / img_height, 6)
                info = [str(num) for num in [index_num, xcenter, ycenter, w, h]]

                f.write(" ".join(info) + '\n')
        # Check if there are empty txt file created
        if os.stat(txt_save_name).st_size is 0:
            print("\n file {} is empty".format(txt_save_name))
        txt_dict.write(txt_save_name + "\n")

    print("\n{} bad data point found and removed".format(bad_data_count))
    print("\nConverting {} label finished!".format(train_val))

"""
remove labels for unseen objects in the train dataset
unseen objects: master_chef_can(0), gelatin_box(7), potted_meat_can(8), pitcher_base(10), power_drill(14), scissors(16)
"""
def remove_unseen():
    label_names = open(os.path.join(save_file_root, "train_labels.txt"), 'r').readlines()
    # read the label file names
    for i, path in tqdm(enumerate(label_names), desc='deleting unseen labels...', total=len(label_names)):
        txt_file_path = path.strip('\n')
        # read content in label file
        with open(txt_file_path, 'r') as f:
            content = f.readlines()
        # write back with unseen classes filtered out
        with open(txt_file_path, 'w') as f:
            for index, line in enumerate(content):
                if int(line[0]) != 0 and int(line[0]) != 7 and int(line[0]) != 8 and\
                        int(line[:2]) != 10 and int(line[:2]) != 14 and int(line[:2]) != 16:
                    f.write(line)
    print("\nlabels for unseen objects are removed")

def create_data_data(classes_file):
    data_data = open(os.path.join(save_file_root, "ycb_data.data"), 'w')
    num_classes = len(open(classes_file, 'r').readlines())
    data_data.write("classes={}\n".format(num_classes))
    data_data.write("train=" + save_file_root + "/train_data.txt\n")
    data_data.write("val=" + save_file_root + "/val_data.txt\n")
    data_data.write("names=" + save_file_root + "/my_classes.names\n")

"""
Change the cdg file since our dataset has 21 classes
"""
def change_and_create_cfg_file(classes_file):
    # filters_lines = [636, 722, 809]
    # classes_lines = [643, 729, 816]

    cfg_path = cfg_hyp_root + "/yolov3-spp.cfg"
    save_cfg_path = cfg_hyp_root + "/my_yolov3.cfg"
    filters_lines = []
    classes_lines = []
    cfg_lines = open(cfg_path, "r").readlines()
    classes_info = open(classes_file, 'r').readlines()

    for line_no, line in enumerate(cfg_lines):
        if cfg_lines[line_no-1].split("=")[0] =='filters':
            if cfg_lines[line_no].split("=")[1] =='linear\n':
                filters_lines.append(line_no)
        if "classes" in cfg_lines[line_no-1]:
            classes_lines.append(line_no)

    for i in filters_lines:
        assert "filters" in cfg_lines[i-1], "filters param is not in line:{}".format(i-1)
        output_num = (5 + len(classes_info)) * 3
        cfg_lines[i-1] = "filters={}\n".format(output_num)

    for i in classes_lines:
        assert "classes" in cfg_lines[i-1], "classes param is not in line:{}".format(i-1)
        cfg_lines[i-1] = "classes={}\n".format(len(classes_info))

    with open(save_cfg_path, "w") as w:
        w.writelines(cfg_lines)

def main():
    # read classes from classes.txt and translate to my_classes
    classes = open(class_root, 'r').readlines()
    new_class_dict = os.path.join(save_file_root, "my_classes.names")
    new_class = open(new_class_dict, 'w')
    for index, line in enumerate(classes):
        if index + 1 == len(classes):
            new_class.write("{}".format(line.strip()[4:]))
        else:
            new_class.write("{}".format(line.strip()[4:]) + "\n")
    new_class.close()

    # Choose the file to put into train or val dataset
    choose_files(new_class_dict)

    # Translate YCB labelling to yolo labelling
    translate_info(new_class_dict, "train")
    translate_info(new_class_dict, "val")

    # Copy image and depth file to the new directory
    copy_files("train")
    copy_files("val")

    # Remove the labels for unseen objects in train dataset
    remove_unseen()

    # create data.data file
    create_data_data(new_class_dict)

    # Change the cfg file for our dataset
    change_and_create_cfg_file(new_class_dict)


if __name__ == '__main__':
    main()

"""
This script has two functions:
1. Combine original train and val folder and split them into seen/unseen folder
2. Create all the files needed for training (.names data.data .shape .txt hyp cfg)
"""
import os
import random

from tqdm import tqdm
from lxml import etree
import json
import shutil

# root directory of YCB dataset
dataset_root = "../YCB_Video_Dataset"
data_root = os.path.join(dataset_root, "data")
names_root = os.path.join(dataset_root, "image_sets", "trainval.txt")
class_root = os.path.join(dataset_root, "image_sets", "classes.txt")

# Check whether all the original directories exist
assert os.path.exists(data_root), "YCB data path not exist..."
assert os.path.exists(names_root), "YCB names path not exist..."
assert os.path.exists(class_root), "YCB class path not exist..."

# save file directory (all train and val image, depth, labels )
save_file_root = "../my_ycb_video"
train_path = os.path.join(save_file_root, "train")
val_path = os.path.join(save_file_root, "val")
cfg_hyp_root = "./cfg"
if os.path.exists(cfg_hyp_root) is False:
    print("Please download hyp.yaml and yolov3-spp.cfg file first")
    os.makedirs(save_file_root)
# Concatenate the path for saving all the data (images, depth, labels)
train_images_path = os.path.join(train_path, "images")
train_label_path = os.path.join(train_path, "labels")
train_depth_path = os.path.join(train_path, "depth")
val_image_path = os.path.join(val_path, "images")
val_label_path = os.path.join(val_path, "labels")
val_depth_path = os.path.join(val_path, "depth")

# make all the directories
if os.path.exists(save_file_root) is False:
    # parent level
    os.makedirs(save_file_root)
    os.makedirs(train_path)
    os.makedirs(val_path)
    # child level
    os.makedirs(train_images_path)
    os.makedirs(train_label_path)
    os.makedirs(train_depth_path)
    os.makedirs(val_image_path)
    os.makedirs(val_label_path)
    os.makedirs(val_depth_path)

"""
For every file in the dictionary, put images that only have seen objects into train, only have unseen objects into val,
then randomly choose 40000 images to put in val (skip if the file already exists, the file number will be smaller than 
40000), all the others put in train
"""
def choose_files(classes_file):
    train_val_file = open(names_root, "r").readlines()
    # create train_data.txt val_data.txt to store all the names
    train_split = open(os.path.join(save_file_root, "train_split.txt"), 'w')
    val_split = open(os.path.join(save_file_root, "val_split.txt"), 'w')
    class_dict = open(classes_file, 'r').readlines()
    index_dict = []
    num_val = 0
    num_train = 0
    # For every file in the dictionary, put images that only have seen objects into train,
    # images that only have unseen objects into val
    for i, file in tqdm(enumerate(train_val_file), desc='Spliting files', total=len(train_val_file)):
        label_file = open(os.path.join(data_root, file.strip('\n') + "-box.txt"), "r")
        label = label_file.readlines()
        num = 0
        for index, line in enumerate(label):
            if class_dict[0].strip('\n') in line or class_dict[7].strip('\n') in line \
                    or class_dict[8].strip('\n') in line or class_dict[10].strip('\n') in line \
                    or class_dict[14].strip('\n') in line or class_dict[16].strip('\n') in line:
                num += 1
        if num == len(label):
            val_split.write(file)
            index_dict.append(i)
            num_val += 1
        elif num == 0:
            train_split.write(file)
            index_dict.append(i)
            num_train += 1
        else:
            pass
        label_file.close()

    # randomly choose another 40000 images  in the rest of images and put into val
    # using same random seed to keep result the same
    randomlist = []
    random.seed(0)
    # generate 40000 random index numbers
    num_random_values = 40000
    for i in range(0, num_random_values):
        n = random.randint(0, 133936)
        randomlist.append(n)
    # put the file correspond to the random index into val
    for i in tqdm(range(0, len(randomlist)), desc='Randomly choosing val images', total=len(randomlist)):
        if randomlist[i] in index_dict:
            pass
        else:
            val_split.write(train_val_file[randomlist[i]])
            index_dict.append(randomlist[i])
            num_val += 1
    # put the rest of unchosen images into train
    for i, file in tqdm(enumerate(train_val_file), desc='Allocating rest of images to train', total=len(train_val_file)):
        if i in index_dict:
            pass
        else:
            train_split.write(file)
            num_train += 1
    train_split.close()
    val_split.close()
    print("\nnumber of train file chosen: {}".format(num_train))
    print("\nnumber of val file chosen: {}".format(num_val))
    print("\nfile choosing finished")

"""
Copy files from the original folder to our division folder train/val
Record the new path for every image in a txt file
"""
def copy_files(train_val):
    if train_val == "train":
        dict_ini = open(os.path.join(save_file_root, "train_split.txt"), 'r').readlines()
        train_val_image_path = train_images_path
        train_val_depth_path = train_depth_path
        train_val_data_record = open(os.path.join(save_file_root, "train_data.txt"), 'w')
    elif train_val == "val":
        dict_ini = open(os.path.join(save_file_root, "val_split.txt"), 'r').readlines()
        train_val_image_path = val_image_path
        train_val_depth_path = val_depth_path
        train_val_data_record = open(os.path.join(save_file_root, "val_data.txt"), 'w')
    # loop through all file names in the train/val_split.txt
    for i, name in tqdm(enumerate(dict_ini), desc='Copying {} files over...'.format(train_val), total=len(dict_ini)):
        file = name.strip('\n')
        file_path = os.path.join(data_root, file)
        # find color and depth images in their original path
        img_file = file_path + '-color.png'
        dep_file = file_path + '-depth.png'
        assert os.path.exists(img_file), "\n warning: image file {} not exist...".format(img_file)
        assert os.path.exists(dep_file), "\n warning: depth file {} not exist...".format(dep_file)
        # create new path and copy image files over
        img_path_copy_to = os.path.join(train_val_image_path, file.split('/')[0] + "-" + file.split('/')[1] + "-color.png")
        train_val_data_record.write(img_path_copy_to + "\n")
        if os.path.exists(img_path_copy_to) is False:
            shutil.copyfile(img_file, img_path_copy_to)
        dep_path_copy_to = os.path.join(train_val_depth_path, file.split('/')[0] + "-" + file.split('/')[1] + "-depth.png")
        if os.path.exists(dep_path_copy_to) is False:
            shutil.copyfile(dep_file, dep_path_copy_to)
    print('\nCopy {} files finished'.format(train_val))
    train_val_data_record.close()

"""
translate from ycb style labelling to yolo style
1. change object name to its index number in my_class.txt
2. change box boundary from absolute value [xmin, ymin, xmax, ymax] to [x, y, w, h]
"""
def translate_info(classes_file, train_val):
    # All images in ycb are the same size
    img_height = 480
    img_width = 640
    # read class file
    class_dict = open(classes_file, 'r').readlines()
    # write to different files for train dataset and val dataset
    if train_val == "train":
        dict_ini = open(os.path.join(save_file_root, "train_split.txt"), 'r').readlines()
        txt_save_path = train_label_path
        txt_dict = open(os.path.join(save_file_root, "train_labels.txt"), 'w')
    elif train_val == "val":
        dict_ini = open(os.path.join(save_file_root, "val_split.txt"), 'r').readlines()
        txt_save_path = val_label_path
        txt_dict = open(os.path.join(save_file_root, "val_labels.txt"), 'w')
    # record how many files have bad data
    bad_data_count = 0
    # loop through all file names in train/val_split.txt
    for i, name in tqdm(enumerate(dict_ini), desc='Translating {} labels...'.format(train_val), total=len(dict_ini)):
        file = name.strip('\n')
        file_path = os.path.join(data_root, file)
        txt_file = file_path + '-box.txt'
        assert os.path.exists(txt_file), "\n warning: txt file {} not exist...".format(txt_file)
        # rename the txt file based on its original path and name
        txt_save_name = os.path.join(txt_save_path, file.split('/')[0] + "-" + file.split('/')[1] + "-box.txt")
        # read the information for every object in the label file
        with open(txt_save_name, "w") as f, open(txt_file, "r") as r:
            for index, obj in enumerate(r.readlines()):
                # get the bounding box for each object
                xmin = float(obj.split(" ")[1])
                ymin = float(obj.split(" ")[2])
                xmax = float(obj.split(" ")[3])
                ymax = float(obj.split(" ")[4])
                # find the corresponded index for the object name
                class_name = obj.split(" ")[0].strip()[4:]
                index_num = 0
                for class_index, classes in enumerate(class_dict):
                    if class_name == classes.strip('\n'):
                        index_num = class_index
                        break
                # check the data to make sure max is always bigger than min, which will affect the calculation of loss
                if xmax <= xmin or ymax <= ymin:
                    bad_data_count += 1
                    continue
                # translate bounding box to YOLO format (centre x, centre y, width, height)
                xcenter = xmin + (xmax - xmin) / 2
                ycenter = ymin + (ymax - ymin) / 2
                w = xmax - xmin
                h = ymax - ymin

                # real coordinate to relative coordinate (relative to the size of image), 6 decimal places
                xcenter = round(xcenter / img_width, 6)
                ycenter = round(ycenter / img_height, 6)
                w = round(w / img_width, 6)
                h = round(h / img_height, 6)
                info = [str(num) for num in [index_num, xcenter, ycenter, w, h]]

                f.write(" ".join(info) + '\n')
        # Check if there are empty txt file created
        if os.stat(txt_save_name).st_size is 0:
            print("\n file {} is empty".format(txt_save_name))
        txt_dict.write(txt_save_name + "\n")

    print("\n{} bad data point found and removed".format(bad_data_count))
    print("\nConverting {} label finished!".format(train_val))

"""
remove labels for unseen objects in the train dataset
unseen objects: master_chef_can(0), gelatin_box(7), potted_meat_can(8), pitcher_base(10), power_drill(14), scissors(16)
"""
def remove_unseen():
    label_names = open(os.path.join(save_file_root, "train_labels.txt"), 'r').readlines()
    # read the label file names
    for i, path in tqdm(enumerate(label_names), desc='deleting unseen labels...', total=len(label_names)):
        txt_file_path = path.strip('\n')
        # read content in label file
        with open(txt_file_path, 'r') as f:
            content = f.readlines()
        # write back with unseen classes filtered out
        with open(txt_file_path, 'w') as f:
            for index, line in enumerate(content):
                if int(line[0]) != 0 and int(line[0]) != 7 and int(line[0]) != 8 and\
                        int(line[:2]) != 10 and int(line[:2]) != 14 and int(line[:2]) != 16:
                    f.write(line)
    print("\nlabels for unseen objects are removed")

def create_data_data(classes_file):
    data_data = open(os.path.join(save_file_root, "ycb_data.data"), 'w')
    num_classes = len(open(classes_file, 'r').readlines())
    data_data.write("classes={}\n".format(num_classes))
    data_data.write("train=" + save_file_root + "/train_data.txt\n")
    data_data.write("val=" + save_file_root + "/val_data.txt\n")
    data_data.write("names=" + save_file_root + "/my_classes.names\n")

"""
Change the cdg file since our dataset has 21 classes
"""
def change_and_create_cfg_file(classes_file):
    # filters_lines = [636, 722, 809]
    # classes_lines = [643, 729, 816]

    cfg_path = cfg_hyp_root + "/yolov3-spp.cfg"
    save_cfg_path = cfg_hyp_root + "/my_yolov3.cfg"
    filters_lines = []
    classes_lines = []
    cfg_lines = open(cfg_path, "r").readlines()
    classes_info = open(classes_file, 'r').readlines()

    for line_no, line in enumerate(cfg_lines):
        if cfg_lines[line_no-1].split("=")[0] =='filters':
            if cfg_lines[line_no].split("=")[1] =='linear\n':
                filters_lines.append(line_no)
        if "classes" in cfg_lines[line_no-1]:
            classes_lines.append(line_no)

    for i in filters_lines:
        assert "filters" in cfg_lines[i-1], "filters param is not in line:{}".format(i-1)
        output_num = (5 + len(classes_info)) * 3
        cfg_lines[i-1] = "filters={}\n".format(output_num)

    for i in classes_lines:
        assert "classes" in cfg_lines[i-1], "classes param is not in line:{}".format(i-1)
        cfg_lines[i-1] = "classes={}\n".format(len(classes_info))

    with open(save_cfg_path, "w") as w:
        w.writelines(cfg_lines)

def main():
    # read classes from classes.txt and translate to my_classes
    classes = open(class_root, 'r').readlines()
    new_class_dict = os.path.join(save_file_root, "my_classes.names")
    new_class = open(new_class_dict, 'w')
    for index, line in enumerate(classes):
        if index + 1 == len(classes):
            new_class.write("{}".format(line.strip()[4:]))
        else:
            new_class.write("{}".format(line.strip()[4:]) + "\n")
    new_class.close()

    # Choose the file to put into train or val dataset
    choose_files(new_class_dict)

    # Translate YCB labelling to yolo labelling
    translate_info(new_class_dict, "train")
    translate_info(new_class_dict, "val")

    # Copy image and depth file to the new directory
    copy_files("train")
    copy_files("val")

    # Remove the labels for unseen objects in train dataset
    remove_unseen()

    # create data.data file
    create_data_data(new_class_dict)

    # Change the cfg file for our dataset
    change_and_create_cfg_file(new_class_dict)


if __name__ == '__main__':
    main()
This script has two functions: 1. Combine original train and val folder and split them into seen/unseen folder 2. Create all the files needed for training (.names data.data .shape .txt hyp cfg) # root directory of YCB dataset # Check whether all the original directories exist # save file directory (all train and val image, depth, labels ) # Concatenate the path for saving all the data (images, depth, labels) # make all the directories # parent level # child level For every file in the dictionary, put images that only have seen objects into train, only have unseen objects into val, then randomly choose 40000 images to put in val (skip if the file already exists, the file number will be smaller than  40000), all the others put in train # create train_data.txt val_data.txt to store all the names # For every file in the dictionary, put images that only have seen objects into train, # images that only have unseen objects into val # randomly choose another 40000 images  in the rest of images and put into val # using same random seed to keep result the same # generate 40000 random index numbers # put the file correspond to the random index into val # put the rest of unchosen images into train Copy files from the original folder to our division folder train/val Record the new path for every image in a txt file # loop through all file names in the train/val_split.txt # find color and depth images in their original path # create new path and copy image files over translate from ycb style labelling to yolo style 1. change object name to its index number in my_class.txt 2. change box boundary from absolute value [xmin, ymin, xmax, ymax] to [x, y, w, h] # All images in ycb are the same size # read class file # write to different files for train dataset and val dataset # record how many files have bad data # loop through all file names in train/val_split.txt # rename the txt file based on its original path and name # read the information for every object in the label file # get the bounding box for each object # find the corresponded index for the object name # check the data to make sure max is always bigger than min, which will affect the calculation of loss # translate bounding box to YOLO format (centre x, centre y, width, height) # real coordinate to relative coordinate (relative to the size of image), 6 decimal places # Check if there are empty txt file created remove labels for unseen objects in the train dataset unseen objects: master_chef_can(0), gelatin_box(7), potted_meat_can(8), pitcher_base(10), power_drill(14), scissors(16) # read the label file names # read content in label file # write back with unseen classes filtered out Change the cdg file since our dataset has 21 classes # filters_lines = [636, 722, 809] # classes_lines = [643, 729, 816] # read classes from classes.txt and translate to my_classes # Choose the file to put into train or val dataset # Translate YCB labelling to yolo labelling # Copy image and depth file to the new directory # Remove the labels for unseen objects in train dataset # create data.data file # Change the cfg file for our dataset