【数据集转换】VOC数据集转COCO数据集·代码实现+操作步骤

简介: 与VOC一个文件一个xml标注不同,COCO所有的目标框标注都是放在一个json文件中的。

在自己的数据集上实验时,往往需要将VOC数据集转化为coco数据集,因为这种需求所以才记录这篇文章,代码出处未知,感谢开源。


在远程服务器上测试目标检测算法需要用到测试集,最常用的是coco2014/2017和voc07/12数据集。


coco数据集的地址为http://cocodataset.org/#download


voc和coco的镜像为https://pjreddie.com/projects/pascal-voc-dataset-mirror/

一、数据集格式对比


1.1 VOC数据集


VOC_ROOT     #根目录
    ├── JPEGImages         # 存放源图,(当然图片并不一定要是**.jpg格式的,只是规定文件夹名字叫JPEGImages**);
    │     ├── aaaa.jpg     
    │     ├── bbbb.jpg  
    │     └── cccc.jpg
    ├── Annotations        # 存放xml文件,VOC的标注是xml格式,与JPEGImages中的图片一一对应
    │     ├── aaaa.xml 
    │     ├── bbbb.xml 
    │     └── cccc.xml 
    └── ImageSets          
        └── Main
          ├── train.txt    # txt文件中每一行包含一个图片的名称
          └── val.txt


1.2 COCO数据集


COCO_ROOT     #根目录
    ├── annotations        # 存放json格式的标注
    │     ├── instances_train2017.json   
    │     └── instances_val2017.json
    └── train2017         # 存放图片文件
    │     ├── 000000000001.jpg 
    │     ├── 000000000002.jpg 
    │     └── 000000000003.jpg 
    └── val2017        
          ├── 000000000004.jpg 
          └── 000000000005.jpg 


1.2.3 json标注格式


VOC一个文件一个xml标注不同,COCO所有的目标框标注都是放在一个json文件中的。


这个json文件解析出来是一个字典,格式如下:


{
  "info": info, 
  "images": [image], 
  "annotations": [annotation], 
  "categories": [categories],
  "licenses": [license],
}


二、转换步骤


2.1 程序总体目录


60948e641b0d40ff8bc485a505a6b2e9.png


33376b544a2a40e7bb70a2a3e871de36.png


2.2  标签文件转换代码实现(xml文件转json格式)VOC_To_CoCo_01.py


这里需要运行三次,因为train.txt val.txt test.txt是三个文件,具体看注释


# VOC_To_CoCo_01.py
import os
import argparse
import json
import xml.etree.ElementTree as ET
from typing import Dict, List
import re
def get_label2id(labels_path: str) -> Dict[str, int]:
    """id is 1 start"""
    with open(labels_path, 'r') as f:
        labels_str = f.read().split()
    labels_ids = list(range(1, len(labels_str) + 1))
    return dict(zip(labels_str, labels_ids))
def get_annpaths(ann_dir_path: str = None,
                 ann_ids_path: str = None,
                 ext: str = '',
                 annpaths_list_path: str = None) -> List[str]:
    # If use annotation paths list
    if annpaths_list_path is not None:
        with open(annpaths_list_path, 'r') as f:
            ann_paths = f.read().split()
        return ann_paths
    # If use annotaion ids list
    ext_with_dot = '.' + ext if ext != '' else ''
    with open(ann_ids_path, 'r') as f:
        ann_ids = f.read().split()
    ann_paths = [os.path.join(ann_dir_path, aid + ext_with_dot) for aid in ann_ids]
    return ann_paths
def get_image_info(annotation_root, extract_num_from_imgid=True):
    path = annotation_root.findtext('path')
    if path is None:
        filename = annotation_root.findtext('filename')
    else:
        filename = os.path.basename(path)
    img_name = os.path.basename(filename)
    img_id = os.path.splitext(img_name)[0]
    if extract_num_from_imgid and isinstance(img_id, str):
        img_id = int(re.findall(r'\d+', img_id)[0])
    size = annotation_root.find('size')
    width = int(size.findtext('width'))
    height = int(size.findtext('height'))
    image_info = {
        'file_name': filename,
        'height': height,
        'width': width,
        'id': img_id
    }
    return image_info
def get_coco_annotation_from_obj(obj, label2id):
    label = obj.findtext('name')
    assert label in label2id, f"Error: {label} is not in label2id !"
    category_id = label2id[label]
    bndbox = obj.find('bndbox')
    xmin = int(bndbox.findtext('xmin')) - 1
    ymin = int(bndbox.findtext('ymin')) - 1
    xmax = int(bndbox.findtext('xmax'))
    ymax = int(bndbox.findtext('ymax'))
    assert xmax > xmin and ymax > ymin, f"Box size error !: (xmin, ymin, xmax, ymax): {xmin, ymin, xmax, ymax}"
    o_width = xmax - xmin
    o_height = ymax - ymin
    ann = {
        'area': o_width * o_height,
        'iscrowd': 0,
        'bbox': [xmin, ymin, o_width, o_height],
        'category_id': category_id,
        'ignore': 0,
        'segmentation': []  # This script is not for segmentation
    }
    return ann
def convert_xmls_to_cocojson(annotation_paths: List[str],
                             label2id: Dict[str, int],
                             output_jsonpath: str,
                             extract_num_from_imgid: bool = True):
    output_json_dict = {
        "images": [],
        "type": "instances",
        "annotations": [],
        "categories": []
    }
    bnd_id = 1  # START_BOUNDING_BOX_ID, TODO input as args ?
    for a_path in annotation_paths:
        # Read annotation xml
        ann_tree = ET.parse(a_path)
        ann_root = ann_tree.getroot()
        img_info = get_image_info(annotation_root=ann_root,
                                  extract_num_from_imgid=extract_num_from_imgid)
        img_id = img_info['id']
        output_json_dict['images'].append(img_info)
        for obj in ann_root.findall('object'):
            ann = get_coco_annotation_from_obj(obj=obj, label2id=label2id)
            ann.update({'image_id': img_id, 'id': bnd_id})
            output_json_dict['annotations'].append(ann)
            bnd_id = bnd_id + 1
    for label, label_id in label2id.items():
        category_info = {'supercategory': 'none', 'id': label_id, 'name': label}
        output_json_dict['categories'].append(category_info)
    with open(output_jsonpath, 'w') as f:
        output_json = json.dumps(output_json_dict)
        f.write(output_json)
    print('Convert successfully !')
def main():
    parser = argparse.ArgumentParser(
        description='This script support converting voc format xmls to coco format json')
    parser.add_argument('--ann_dir', type=str, default='./VOCdevkit/Annotations')
    parser.add_argument('--ann_ids', type=str, default='./VOCdevkit/ImageSets/Main/val.txt') # 这里修改 train val test 一共修改三次
    #parser.add_argument('--ann_ids', type=str, default='./VOCdevkit/ImageSets/Main/train.txt')
    #parser.add_argument('--ann_ids', type=str, default='./VOCdevkit/ImageSets/Main/test.txt')
    parser.add_argument('--ann_paths_list', type=str, default=None)
    parser.add_argument('--labels', type=str, default='./VOCdevkit/labels.txt')
    parser.add_argument('--output', type=str, default='./output/annotations/val.json') # 这里修改 train val test 一共修改三次
    #parser.add_argument('--output', type=str, default='./output/annotations/train.json')
    #parser.add_argument('--output', type=str, default='./output/annotations/test.json')
    parser.add_argument('--ext', type=str, default='xml')
    args = parser.parse_args()
    label2id = get_label2id(labels_path=args.labels)
    ann_paths = get_annpaths(
        ann_dir_path=args.ann_dir,
        ann_ids_path=args.ann_ids,
        ext=args.ext,
        annpaths_list_path=args.ann_paths_list
    )
    convert_xmls_to_cocojson(
        annotation_paths=ann_paths,
        label2id=label2id,
        output_jsonpath=args.output,
        extract_num_from_imgid=True
    )
if __name__ == '__main__':
    if not os.path.exists('./output/annotations'):
        os.makedirs('./output/annotations')
    main()


2.3 数据集图像文件copy代码实现(复制图片数据集到coco中)VOC_To_CoCo_02.py


# VOC_To_CoCo_02.py
import os
import shutil
images_file_path = './VOCdevkit/JPEGImages/'
split_data_file_path = './VOCdevkit/ImageSets/Main/'
new_images_file_path = './output/'
if not os.path.exists(new_images_file_path + 'train'):
    os.makedirs(new_images_file_path + 'train')
if not os.path.exists(new_images_file_path + 'val'):
    os.makedirs(new_images_file_path + 'val')
if not os.path.exists(new_images_file_path + 'test'):
    os.makedirs(new_images_file_path + 'test')
dst_train_Image = new_images_file_path + 'train/'
dst_val_Image = new_images_file_path + 'val/'
dst_test_Image = new_images_file_path + 'test/'
total_txt = os.listdir(split_data_file_path)
for i in total_txt:
    name = i[:-4]
    if name == 'train':
        txt_file = open(split_data_file_path + i, 'r')
        for line in txt_file:
            line = line.strip('\n')
            line = line.strip('\r')
            srcImage = images_file_path + line + '.jpg'
            dstImage = dst_train_Image + line + '.jpg'
            shutil.copyfile(srcImage, dstImage)
        txt_file.close()
    elif name == 'val':
        txt_file = open(split_data_file_path + i, 'r')
        for line in txt_file:
            line = line.strip('\n')
            line = line.strip('\r')
            srcImage = images_file_path + line + '.jpg'
            dstImage = dst_val_Image + line + '.jpg'
            shutil.copyfile(srcImage, dstImage)
        txt_file.close()
    elif name == 'test':
        txt_file = open(split_data_file_path + i, 'r')
        for line in txt_file:
            line = line.strip('\n')
            line = line.strip('\r')
            srcImage = images_file_path + line + '.jpg'
            dstImage = dst_test_Image + line + '.jpg'
            shutil.copyfile(srcImage, dstImage)
        txt_file.close()
    else:
        print("Error, Please check the file name of folder")


三、效果展示


7da88fc7b4014973b8f4c0d87c4ad6b9.png


目录
相关文章
【yolo训练数据集】标注好的垃圾分类数据集共享
【yolo训练数据集】标注好的垃圾分类数据集共享
2307 138
【yolo训练数据集】标注好的垃圾分类数据集共享
【图像分类数据集】非常全面实用的垃圾分类图片数据集共享
【图像分类数据集】非常全面实用的垃圾分类图片数据集共享
964 24
【图像分类数据集】非常全面实用的垃圾分类图片数据集共享
|
算法 数据库 计算机视觉
Dataset之COCO数据集:COCO数据集的简介、下载、使用方法之详细攻略
Dataset之COCO数据集:COCO数据集的简介、下载、使用方法之详细攻略
|
2月前
|
PyTorch 算法框架/工具
数据集学习笔记(三):调用不同数据集获取trainloader和testloader
本文介绍了如何使用PyTorch框架调用CIFAR10数据集,并获取训练和测试的数据加载器(trainloader和testloader)。
45 4
数据集学习笔记(三):调用不同数据集获取trainloader和testloader
|
2月前
|
XML JSON 数据可视化
数据集学习笔记(六):目标检测和图像分割标注软件介绍和使用,并转换成YOLO系列可使用的数据集格式
本文介绍了labelImg和labelme两款图像标注工具的安装、使用、数据转换和验证方法,适用于目标检测和图像分割任务,支持YOLO等数据集格式。
516 2
数据集学习笔记(六):目标检测和图像分割标注软件介绍和使用,并转换成YOLO系列可使用的数据集格式
|
4月前
分割数据集,并对数据集进行预处理
【8月更文挑战第8天】分割数据集,并对数据集进行预处理。
39 1
|
5月前
|
机器学习/深度学习 存储 算法
MNIST数据集简介
【7月更文挑战第24天】MNIST数据集简介。
208 2
|
7月前
|
机器学习/深度学习 数据可视化 数据库
R语言对MNIST数据集分析:探索手写数字分类
R语言对MNIST数据集分析:探索手写数字分类
|
存储 大数据 Linux
基于 YOLOv8 的自定义数据集训练
基于 YOLOv8 的自定义数据集训练
|
存储 TensorFlow 算法框架/工具
mnist数据集预处理实战
mnist数据集预处理实战
294 0

热门文章

最新文章