COCO格式数据集合并脚本

现附上GitHub链接:https://github.com/gintmr/Useful_tools/blob/main/merge_multi_datasets.py

博主在跑实验时突然发现一个问题,虽然COCO格式数据使用得非常广泛,但在需要使用多数据集一同训练/测试时,现有的框架(部分框架包含多数据集合并功能,但封装较为隐蔽,且对于初级用户不太友好,难以debug)通常难以下手。(此处的多数据集指的是不同类别集合下的数据集,而非COCO中的train与val合并)

因此下面贴上我自己写的一份COCO数据集合并脚本

整体设计思路:

给出图片文件夹路径,json文件路径

脚本能够将多个json合并(包含id、image_id、category_id等排序词的重排列),同时将对应图片复制到用户提供的输出图片文件夹下。

在合并json后,脚本会额外输出一个合并后数据集下category的字典列表,用于方便用户整合数据集信息。

解释一下上文提到的“方便用户整合数据集信息”

以我个人为例: 假设我有两份数据集A与B,数据集中category各不相同 我的实验需求:使用A+B数据集训练模型,但最终仅使用A的测试集测试效果 我的操作步骤:

  • 将A对应数据集各路径置于列表的第一位,B相关路径置于第二位。
  • 合并训练集后,将A对应test数据也放置于相同位置处,再运行一遍脚本(此时的核心目的实际上是将A中segmentation对应的category重新映射到新数据集的序号上去)
  • 最终,将第一次合并后输出的category的字典列表,替换到test数据集中的categories键中,即完成train与test数据的分别整合。
import os  
import json
import numpy as np
import cv2
from pycocotools import mask as maskUtils
from tqdm import tqdm
import shutil

image_folder_list = ['images/path/to/set/A',
                     'images/path/to/set/B'
                     ]

json_folder_list = ['json/path/to/set/A',
                    'json/path/to/set/B'
                  ]1

save_image_folder = 'out/path/of/images'
save_json_folder = 'out/path/of/json'
os.makedirs(save_image_folder,exist_ok=True)
os.makedirs(save_json_folder,exist_ok=True)

json_file_name = 'merge_datasets.json'

merged_json = {}
merged_json['images'] = []
merged_json['annotations'] = []
merged_json['categories'] = []

for i in tqdm(range(len(json_folder_list)), desc="Merging Datasets"):
    
    image_id_dict = {} ## 以image_id 为键值的dit,方便读取对应信息
    category_id_dict = {} ## 以category_id 为键值的dit,方便读取对应信息
    
    
    with open(json_folder_list[i],'r') as f:
        json_data = json.load(f)
        for image in tqdm(json_data['images'], desc=f"Merging Images in {json_folder_list[i]}"):
            image_id = image['id']
            
            merged_json['images'].append(image)
            image_new_id = len(merged_json['images']) - 1
            merged_json['images'][image_new_id]['id'] = image_new_id ## 此处确定的id为图片id,id序号是数字类型
            
            image_id_dict[str(image_id)] = {
                'file_name': image['file_name'],
                'new_id': image_new_id,
                'height': image['height'],
                'width': image['width']
            }
            
            
        for category in tqdm(json_data['categories'], desc=f"Merging Categories in {json_folder_list[i]}"):
            
            category_id = category['id']
            
            merged_json['categories'].append(category)
            category_new_id = len(merged_json['categories']) ## 此处确定的id为类别id,需要从 1 开始,序号0留给Foreground
            merged_json['categories'][category_new_id-1]['id'] = category_new_id
           
            if category_id is not None:
                category_id_dict[str(category_id)] = {
                    'name': category['name'],
                    'new_id': category_new_id
                }
            
            
            
        for annotation in tqdm(json_data['annotations'], desc=f"Merging Annotations in {json_folder_list[i]}"):
            merged_json['annotations'].append(annotation)
            now_id = len(merged_json['annotations']) - 1
            merged_json['annotations'][now_id]['id'] = now_id ## 当前标注自身的序号
            
            ori_image_id = annotation['image_id'] ## 得到合并前的对应图像id
            new_image_id = image_id_dict[str(ori_image_id)]['new_id'] ## 得到合并后的对应图像id
            image_file_name = image_id_dict[str(ori_image_id)]['file_name'] ## 得到合并后的对应图像文件名
            
            
            image_file_path = os.path.join(image_folder_list[i], image_file_name)
            shutil.copy(image_file_path, save_image_folder) ## 复制图片
            
            
            image_height = image_id_dict[str(ori_image_id)]['height'] ## 得到合并后的对应图像高度
            merged_json['annotations'][now_id]['image_height'] = image_height 

            image_width = image_id_dict[str(ori_image_id)]['width'] ## 得到合并后的对应图像宽度
            merged_json['annotations'][now_id]['image_width'] = image_width
             
            
            merged_json['annotations'][now_id]['image_id'] = new_image_id ## 更新图像id
            merged_json['annotations'][now_id]['image_name'] = image_file_name ## 更新图像文件名
            
            ori_category_id = annotation['category_id'] ## 得到合并前的对应类别id
            if ori_category_id is not None:
                new_category_id = category_id_dict[str(ori_category_id)]['new_id'] ## 得到合并后的对应类别id
                merged_json['annotations'][now_id]['category_id'] = new_category_id
            
            if annotation['segmentation'] is not None:
                ### 如果是rle格式的segmentation
                segmentation = annotation['segmentation']
                if "size" not in segmentation:
                    mask = np.zeros((image_height, image_width), dtype=np.uint8)
                    polygon = segmentation[0]
                   # 将一维数组转换为 (N, 2) 的形状
                    polygon_pairs = np.array(polygon).reshape(-1, 2)

                    # 转换为整数类型
                    polygon_pairs = polygon_pairs.astype(np.int32)

                    # 调整形状为 (N, 1, 2)
                    polygon_pairs = polygon_pairs.reshape(-1, 1, 2)
                    cv2.fillPoly(mask, polygon_pairs, 1)
                    
                    rle = maskUtils.encode(np.asfortranarray(mask))
                    
                    annotation['segmentation'] = {
                        "size": [image_height, image_width],
                        "counts":  rle["counts"].decode("utf-8")
                    }
            
            if "iscrowd" not in annotation or annotation['iscrowd'] is None:
                merged_json['annotations'][now_id]['iscrowd'] = 0
                
                
                
                
save_json_path = os.path.join(save_json_folder, json_file_name)

with open(save_json_path, 'w') as f:
    json.dump(merged_json, f, indent=4)
    print(f"Saved merged json to {save_json_path}")
    
save_category_path = save_json_path.replace('.json', '_categories.json')

with open(save_category_path, 'w') as f:
    json.dump(merged_json['categories'], f, indent=4)
    print(f"Saved category json to {save_category_path}")

### 下载符合COCO格式数据集 为了获取符合 COCO 格式数据集,可以按照以下方法操作: #### 使用 COCO128 数据集 如果只需要一个小规模的数据集来快速上手目标检测任务,则可以直接下载 **COCO128 数据集**。这是一个已经整理好的小型数据集,包含了来自 COCO 的前 128 张图片及其标注信息[^1]。 - 访问项目地址:[https://gitcode.com/open-source-toolkit/9ae60](https://gitcode.com/open-source-toolkit/9ae60),找到下载 `coco128.zip` 文件。 - 解压后会得到一个名为 `coco128` 的文件夹,其内部结构已满足 COCO 数据集的标准格式,具体如下: ``` coco128/ ├── images/ │ └── train2017/ # 存放训练集图像 └── labels/ └── train2017/ # 存放对应的标签文件 ``` 此数据集无需额外处理即可直接用于 YOLOv5 或其他支持 COCO 格式的目标检测框架。 --- #### 转换 VOC 数据集到 COCO 格式 如果需要更大规模或者自定义的数据集,可以通过将 Pascal VOC 格式数据集转换为 COCO 格式实现这一需求[^2]。 以下是具体的流程说明: 1. 准备原始的 Pascal VOC 数据集,将其按标准目录结构放置: ``` voc_dataset/ ├── JPEGImages/ # 存放所有的图像文件 ├── Annotations/ # 存放 XML 形式的标注文件 └── ImageSets/Main/ # 列表文件(指定哪些图像是训练、验证或测试) ``` 2. 安装必要的工具库以完成格式转换工作。例如安装 PyLabel 工具包: ```bash pip install pylabel ``` 3. 编写脚本执行转换逻辑。下面是一个简单的 Python 示例代码片段展示如何利用 PyLabel 实现该功能: ```python from pylabel import importer dataset = importer.ImportVoc( path="path/to/voc_annotations", path_to_images="path/to/images" ) dataset.export.ExportToCoco() ``` 4. 执行上述脚本之后,在指定路径下将会生成新的 JSON 文件作为 COCO 风格的标注文档。这些文件应被移动至类似于以下所示的位置以便后续使用: ``` custom_coco/ ├── annotations/ │ ├── instances_train.json │ ├── instances_val.json │ └── instances_test.json ├── train/ # 对应训练集中的图像 ├── val/ # 对应验证集中的图像 └── test/ # 如果存在则对应测试集中的图像 ``` 通过以上步骤就可以成功创建出适配于现代深度学习算法所需的标准化 COCO 类型资料集合了! ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值