使用 ultralytics 官方工具 json2yolo 完成标注格式转换(应用于南京旷视科技的 RPC:retail_product_checkout 数据集)

准备工作

用自己的数据集训练 YOLO 模型可能会出现数据集标签格式不一样的问题,比如你的数据集如果使用 .json (可以是所有图片写在单个 json )存储那么就是 coco 格式;又比如VOC 数据集使用 .txt (每张 image 对应一个)存储标签,那它就是 yolo 格式。

以下是 coco 格式 RPC 数据集的例子:

以下是 yolo 格式 VOC 数据集的例子: 

以下是 COCO JSON 和 YOLO 的目录结构,实际可能会有一点区别,比如缺少 annotations 这一级目录:

COCO_JSON/
├── 2017train/
│   └── *.jpg # 训练集图片
├── 2017val/
│   └── *.jpg # 验证集图片
├── 2017test/
│   └── *.jpg # 测试集图片
└── annotations/
    ├── instances_train.json # 训练集标注
    ├── instances_val.json # 验证集标注
    └── instances_test.json # 测试集标注

YOLO/
├── images/
│   ├── train/
│   │   └── *.jpg # 训练集图片
│   ├── val/
│   │   └── *.jpg # 验证集图片
│   └── test/
│       └── *.jpg # 测试集图片
└── labels/
    ├── train/
    │   └── *.txt # 训练集标签
    ├── val/
    │   └── *.txt # 验证集标签
    └── test/
        └── *.txt # 测试集标签

copy from:xiaohangguo/JSON_YOLO_intertwined: COCO JSON 格式 和YOLO 相互转化的代码,包含YOLO2JSON 和JSON2YOLO,在很多数据集上测试有效。

有很多博主分享了自己的 json2yolo 的代码(比如以上这个github项目),但其实官方自己有 json2yolo 的工具,而且使用起来十分方便,我将使用 RPC 数据集为例演示一下如何使用。

RPC数据集:

Project Page:RPC-Dataset Project Page

kaggle:Retail Product Checkout Dataset

官方工具:ultralytics/JSON2YOLO: Convert JSON annotations into YOLO format.

(使用 git clone 或download zip)

使用

首先,RPC 数据集是缺少 annotations 目录的,可以自己补上:

然后我们查看一下已经下载好的官方工具代码

打开 general_json2yolo.py

官方为我们提供了多种格式的 json2yolo 如 infolks_json、vott_json等,这里我们使用 coco_json :

def convert_coco_json(json_dir="../coco/annotations/", use_segments=False, cls91to80=False):
    """Converts COCO JSON format to YOLO label format, with options for segments and class mapping."""
    save_dir = make_dirs()  # output directory
    coco80 = coco91_to_coco80_class()

    # Import json
    for json_file in sorted(Path(json_dir).resolve().glob("*.json")):
        fn = Path(save_dir) / "labels" / json_file.stem.replace("instances_", "")  # folder name
        fn.mkdir()
        with open(json_file) as f:
            data = json.load(f)

        # Create image dict
        images = {"{:g}".format(x["id"]): x for x in data["images"]}
        # Create image-annotations dict
        imgToAnns = defaultdict(list)
        for ann in data["annotations"]:
            imgToAnns[ann["image_id"]].append(ann)

        # Write labels file
        for img_id, anns in tqdm(imgToAnns.items(), desc=f"Annotations {json_file}"):
            img = images[f"{img_id:g}"]
            h, w, f = img["height"], img["width"], img["file_name"]

            bboxes = []
            segments = []
            for ann in anns:
                if ann["iscrowd"]:
                    continue
                # The COCO box format is [top left x, top left y, width, height]
                box = np.array(ann["bbox"], dtype=np.float64)
                box[:2] += box[2:] / 2  # xy top-left corner to center
                box[[0, 2]] /= w  # normalize x
                box[[1, 3]] /= h  # normalize y
                if box[2] <= 0 or box[3] <= 0:  # if w <= 0 and h <= 0
                    continue

                cls = coco80[ann["category_id"] - 1] if cls91to80 else ann["category_id"] - 1  # class
                box = [cls] + box.tolist()
                if box not in bboxes:
                    bboxes.append(box)
                # Segments
                if use_segments:
                    if len(ann["segmentation"]) > 1:
                        s = merge_multi_segment(ann["segmentation"])
                        s = (np.concatenate(s, axis=0) / np.array([w, h])).reshape(-1).tolist()
                    else:
                        s = [j for i in ann["segmentation"] for j in i]  # all segments concatenated
                        s = (np.array(s).reshape(-1, 2) / np.array([w, h])).reshape(-1).tolist()
                    s = [cls] + s
                    if s not in segments:
                        segments.append(s)

            # Write
            with open((fn / f).with_suffix(".txt"), "a") as file:
                for i in range(len(bboxes)):
                    line = (*(segments[i] if use_segments else bboxes[i]),)  # cls, box or segments
                    file.write(("%g " * len(line)).rstrip() % line + "\n")

函数定义中的3个参数分别为:

json_dir :.json 文件所在的目录

use_segments:是否使用分割信息,如果只做目标检测则没有分割信息

cls91to80:是否使用 COCO91 to COCO80,将91种类别映射到常见的80种类别

 主函数:

if __name__ == "__main__":

    source = "COCO"

    if source == "COCO":
        convert_coco_json(
            "/root/dataset/retail_product_checkout/annotations",  # directory with *.json
            use_segments=False,
            cls91to80=False,
        )

    elif source == "infolks":  # Infolks https://infolks.info/
        convert_infolks_json(name="out", files="../data/sm4/json/*.json", img_path="../data/sm4/images/")

    elif source == "vott":  # VoTT https://github.com/microsoft/VoTT
        convert_vott_json(
            name="data",
            files="../../Downloads/athena_day/20190715/*.json",
            img_path="../../Downloads/athena_day/20190715/",
        )  # images folder

    elif source == "ath":  # ath format
        convert_ath_json(json_dir="../../Downloads/athena/")  # images folder

我们只关注 source=coco 的情况,我们需要修改 .json 文件目录,第二个参数如果不用分割信息就 false,而最后一个参数是false,因为我使用的RPC数据集一共有200个类别,并且也不是COCO91,所以不需要将它映射到80个类别。

修改完后,接下来就可以直接执行这个文件,它将在你数据集的目录下生成一个 new_dir 的文件夹,转换后的 .txt 标签已经放在 labels 文件夹下:

到此使用官方工具进行 josn2yolo 的转换已经完成了。

最后,如需正常使用数据集,记得按 yolo 格式目录结构整理一下自己的目录。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值