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


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

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


    coco数据集的地址为http://cocodataset.org/#download
    voc和coco的镜像为https://pjreddie.com/projects/pascal-voc-dataset-mirror/

    一、数据集格式对比

    1.1 VOC数据集

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

    1.2 COCO数据集

    1. COCO_ROOT #根目录
    2. ├── annotations # 存放json格式的标注
    3. │ ├── instances_train2017.json
    4. │ └── instances_val2017.json
    5. └── train2017 # 存放图片文件
    6. │ ├── 000000000001.jpg
    7. │ ├── 000000000002.jpg
    8. │ └── 000000000003.jpg
    9. └── val2017
    10. ├── 000000000004.jpg
    11. └── 000000000005.jpg

    1.2.3 json标注格式

    VOC一个文件一个xml标注不同,COCO所有的目标框标注都是放在一个json文件中的。
    这个json文件解析出来是一个字典,格式如下:

    1. {
    2. "info": info,
    3. "images": [image],
    4. "annotations": [annotation],
    5. "categories": [categories],
    6. "licenses": [license],
    7. }

    二、转换步骤

    2.1 程序总体目录

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

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

    1. # VOC_To_CoCo_01.py
    2. import os
    3. import argparse
    4. import json
    5. import xml.etree.ElementTree as ET
    6. from typing import Dict, List
    7. import re
    8. def get_label2id(labels_path: str) -> Dict[str, int]:
    9. """id is 1 start"""
    10. with open(labels_path, 'r') as f:
    11. labels_str = f.read().split()
    12. labels_ids = list(range(1, len(labels_str) + 1))
    13. return dict(zip(labels_str, labels_ids))
    14. def get_annpaths(ann_dir_path: str = None,
    15. ann_ids_path: str = None,
    16. ext: str = '',
    17. annpaths_list_path: str = None) -> List[str]:
    18. # If use annotation paths list
    19. if annpaths_list_path is not None:
    20. with open(annpaths_list_path, 'r') as f:
    21. ann_paths = f.read().split()
    22. return ann_paths
    23. # If use annotaion ids list
    24. ext_with_dot = '.' + ext if ext != '' else ''
    25. with open(ann_ids_path, 'r') as f:
    26. ann_ids = f.read().split()
    27. ann_paths = [os.path.join(ann_dir_path, aid + ext_with_dot) for aid in ann_ids]
    28. return ann_paths
    29. def get_image_info(annotation_root, extract_num_from_imgid=True):
    30. path = annotation_root.findtext('path')
    31. if path is None:
    32. filename = annotation_root.findtext('filename')
    33. else:
    34. filename = os.path.basename(path)
    35. img_name = os.path.basename(filename)
    36. img_id = os.path.splitext(img_name)[0]
    37. if extract_num_from_imgid and isinstance(img_id, str):
    38. img_id = int(re.findall(r'\d+', img_id)[0])
    39. size = annotation_root.find('size')
    40. width = int(size.findtext('width'))
    41. height = int(size.findtext('height'))
    42. image_info = {
    43. 'file_name': filename,
    44. 'height': height,
    45. 'width': width,
    46. 'id': img_id
    47. }
    48. return image_info
    49. def get_coco_annotation_from_obj(obj, label2id):
    50. label = obj.findtext('name')
    51. assert label in label2id, f"Error: {label} is not in label2id !"
    52. category_id = label2id[label]
    53. bndbox = obj.find('bndbox')
    54. xmin = int(bndbox.findtext('xmin')) - 1
    55. ymin = int(bndbox.findtext('ymin')) - 1
    56. xmax = int(bndbox.findtext('xmax'))
    57. ymax = int(bndbox.findtext('ymax'))
    58. assert xmax > xmin and ymax > ymin, f"Box size error !: (xmin, ymin, xmax, ymax): {xmin, ymin, xmax, ymax}"
    59. o_width = xmax - xmin
    60. o_height = ymax - ymin
    61. ann = {
    62. 'area': o_width * o_height,
    63. 'iscrowd': 0,
    64. 'bbox': [xmin, ymin, o_width, o_height],
    65. 'category_id': category_id,
    66. 'ignore': 0,
    67. 'segmentation': [] # This script is not for segmentation
    68. }
    69. return ann
    70. def convert_xmls_to_cocojson(annotation_paths: List[str],
    71. label2id: Dict[str, int],
    72. output_jsonpath: str,
    73. extract_num_from_imgid: bool = True):
    74. output_json_dict = {
    75. "images": [],
    76. "type": "instances",
    77. "annotations": [],
    78. "categories": []
    79. }
    80. bnd_id = 1 # START_BOUNDING_BOX_ID, TODO input as args ?
    81. for a_path in annotation_paths:
    82. # Read annotation xml
    83. ann_tree = ET.parse(a_path)
    84. ann_root = ann_tree.getroot()
    85. img_info = get_image_info(annotation_root=ann_root,
    86. extract_num_from_imgid=extract_num_from_imgid)
    87. img_id = img_info['id']
    88. output_json_dict['images'].append(img_info)
    89. for obj in ann_root.findall('object'):
    90. ann = get_coco_annotation_from_obj(obj=obj, label2id=label2id)
    91. ann.update({'image_id': img_id, 'id': bnd_id})
    92. output_json_dict['annotations'].append(ann)
    93. bnd_id = bnd_id + 1
    94. for label, label_id in label2id.items():
    95. category_info = {'supercategory': 'none', 'id': label_id, 'name': label}
    96. output_json_dict['categories'].append(category_info)
    97. with open(output_jsonpath, 'w') as f:
    98. output_json = json.dumps(output_json_dict)
    99. f.write(output_json)
    100. print('Convert successfully !')
    101. def main():
    102. parser = argparse.ArgumentParser(
    103. description='This script support converting voc format xmls to coco format json')
    104. parser.add_argument('--ann_dir', type=str, default='./VOCdevkit/Annotations')
    105. parser.add_argument('--ann_ids', type=str, default='./VOCdevkit/ImageSets/Main/val.txt') # 这里修改 train val test 一共修改三次
    106. #parser.add_argument('--ann_ids', type=str, default='./VOCdevkit/ImageSets/Main/train.txt')
    107. #parser.add_argument('--ann_ids', type=str, default='./VOCdevkit/ImageSets/Main/test.txt')
    108. parser.add_argument('--ann_paths_list', type=str, default=None)
    109. parser.add_argument('--labels', type=str, default='./VOCdevkit/labels.txt')
    110. parser.add_argument('--output', type=str, default='./output/annotations/val.json') # 这里修改 train val test 一共修改三次
    111. #parser.add_argument('--output', type=str, default='./output/annotations/train.json')
    112. #parser.add_argument('--output', type=str, default='./output/annotations/test.json')
    113. parser.add_argument('--ext', type=str, default='xml')
    114. args = parser.parse_args()
    115. label2id = get_label2id(labels_path=args.labels)
    116. ann_paths = get_annpaths(
    117. ann_dir_path=args.ann_dir,
    118. ann_ids_path=args.ann_ids,
    119. ext=args.ext,
    120. annpaths_list_path=args.ann_paths_list
    121. )
    122. convert_xmls_to_cocojson(
    123. annotation_paths=ann_paths,
    124. label2id=label2id,
    125. output_jsonpath=args.output,
    126. extract_num_from_imgid=True
    127. )
    128. if __name__ == '__main__':
    129. if not os.path.exists('./output/annotations'):
    130. os.makedirs('./output/annotations')
    131. main()

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

    1. # VOC_To_CoCo_02.py
    2. import os
    3. import shutil
    4. images_file_path = './VOCdevkit/JPEGImages/'
    5. split_data_file_path = './VOCdevkit/ImageSets/Main/'
    6. new_images_file_path = './output/'
    7. if not os.path.exists(new_images_file_path + 'train'):
    8. os.makedirs(new_images_file_path + 'train')
    9. if not os.path.exists(new_images_file_path + 'val'):
    10. os.makedirs(new_images_file_path + 'val')
    11. if not os.path.exists(new_images_file_path + 'test'):
    12. os.makedirs(new_images_file_path + 'test')
    13. dst_train_Image = new_images_file_path + 'train/'
    14. dst_val_Image = new_images_file_path + 'val/'
    15. dst_test_Image = new_images_file_path + 'test/'
    16. total_txt = os.listdir(split_data_file_path)
    17. for i in total_txt:
    18. name = i[:-4]
    19. if name == 'train':
    20. txt_file = open(split_data_file_path + i, 'r')
    21. for line in txt_file:
    22. line = line.strip('\n')
    23. line = line.strip('\r')
    24. srcImage = images_file_path + line + '.jpg'
    25. dstImage = dst_train_Image + line + '.jpg'
    26. shutil.copyfile(srcImage, dstImage)
    27. txt_file.close()
    28. elif name == 'val':
    29. txt_file = open(split_data_file_path + i, 'r')
    30. for line in txt_file:
    31. line = line.strip('\n')
    32. line = line.strip('\r')
    33. srcImage = images_file_path + line + '.jpg'
    34. dstImage = dst_val_Image + line + '.jpg'
    35. shutil.copyfile(srcImage, dstImage)
    36. txt_file.close()
    37. elif name == 'test':
    38. txt_file = open(split_data_file_path + i, 'r')
    39. for line in txt_file:
    40. line = line.strip('\n')
    41. line = line.strip('\r')
    42. srcImage = images_file_path + line + '.jpg'
    43. dstImage = dst_test_Image + line + '.jpg'
    44. shutil.copyfile(srcImage, dstImage)
    45. txt_file.close()
    46. else:
    47. print("Error, Please check the file name of folder")

    三、效果展示

  • 相关阅读:
    4.1.3 vivado中AXI写DDR说明
    bug:Junit5报错,@SpringBootTest没有运行
    goland的Markdown拖动插入链接编码有问题
    跨境电商如何减少客户流失率:成功的5种保留策略
    做产品经理需要很高的学历吗?真相来咯!
    2023人工智能全景报告《State of AI Report》出炉!AI未来一年的10大预测:GPT-4仍是全球最强,GenAI 大爆发,...
    IDEA2022用maven创建的Servlet项目
    无人机镜头稳定的原理和相关算法
    Ubuntu镜像设置及docker安装
    TscanCode的安装与基本使用
  • 原文地址:https://blog.csdn.net/qq_39237205/article/details/126120347