COCO to YOLOv5 script

Script to convert from COCO-Format to YOLOv5 Format.

This file makes a COCO til YoloV5 format very fast. However the penalty is that we are dependent on the pycocotools library.

COCO Format

import os
import json
from tqdm import tqdm
import shutil
from pycocotools.coco import COCO

def convert_bbox_coco2yolo(img_width, img_height, bbox):
    """
    Convert bounding box from COCO  format to YOLO format

    Parameters
    ----------
    img_width : int
        width of image
    img_height : int
        height of image
    bbox : list[int]
        bounding box annotation in COCO format: 
        [top left x position, top left y position, width, height]

    Returns
    -------
    list[float]
        bounding box annotation in YOLO format: 
        [x_center_rel, y_center_rel, width_rel, height_rel]
    """

    # YOLO bounding box format: [x_center, y_center, width, height]
    # (float values relative to width and height of image)
    x_tl, y_tl, w, h = bbox

    dw = 1.0 / img_width
    dh = 1.0 / img_height

    x_center = x_tl + w / 2.0
    y_center = y_tl + h / 2.0

    x = x_center * dw
    y = y_center * dh
    w = w * dw
    h = h * dh

    return [x, y, w, h]


def make_folders(path="output"):
    if os.path.exists(path):
        shutil.rmtree(path)
    os.makedirs(path)
    return path


def convert_coco_json_to_yolo_txt(output_path, json_file):

    path = make_folders(output_path)

    coco = COCO(json_file)

    with open(json_file) as f:
        json_data = json.load(f)

    # write _darknet.labels, which holds names of all classes (one class per line)
    label_file = os.path.join(output_path, "_darknet.labels")
    with open(label_file, "w") as f:
        for category in tqdm(json_data["categories"], desc="Categories"):
            category_name = category["name"]
            f.write(f"{category_name}\n")

    # get img ids
    img_ids = coco.getImgIds()

    # process a file for each image
    for id in tqdm(img_ids, desc="Annotation txt for each image"):
        image = coco.loadImgs(ids=[id])[0]
        # print(image)
        img_id = image["id"]
        img_name = image["file_name"]
        img_width = image["width"]
        img_height = image["height"]

        # anno_in_image = [anno for anno in json_data["annotations"] if anno["image_id"] == img_id]
        ann_ids = coco.getAnnIds(imgIds=[id])
        anno_in_image = coco.loadAnns(ids=ann_ids)
        anno_txt = os.path.join(output_path, img_name.split(".")[0] + ".txt")
        with open(anno_txt, "w") as f:
            for anno in anno_in_image:
                category = anno["category_id"] - 1  # MAP ONE LOWER CLASS
                bbox_COCO = anno["bbox"]
                x, y, w, h = convert_bbox_coco2yolo(img_width, img_height, bbox_COCO)
                f.write(f"{category} {x:.6f} {y:.6f} {w:.6f} {h:.6f}\n")

    print("Converting COCO Json to YOLO txt finished!")


if __name__ == "__main__":
    output_path = '../supervised_annotations_yolov5/yolo_ground/train/labels'
    json_file = '../supervised_annotations/8k_aerial_8k_ground_train_aligned_ids_w_indicator.json'
    convert_coco_json_to_yolo_txt(output_path, json_file)