• VOC和COCO数据集讲解


      相对其他计算机视觉任务,目标检测算法的数据格式更为复杂。为了对数据进行统一的处理,目标检测数据一般都会做成VOC或者COCO的格式。
      VOCCOCO都是既支持检测也支持分割的数据格式,本文主要分析PASCAL VOCCOCO数据集中物体识别相关的内容,并学习如何制作自己的数据集。

    一、VOC格式

    目录结构

      VOC格式数据集一般有着如下的目录结构:

    1. VOC_ROOT #根目录
    2. ├── JPEGImages # 存放源图片
    3. │ ├── aaaa.jpg
    4. │ ├── bbbb.jpg
    5. │ └── cccc.jpg
    6. ├── Annotations # 存放xml文件,与JPEGImages中的图片一一对应,解释图片的内容等等
    7. │ ├── aaaa.xml
    8. │ ├── bbbb.xml
    9. │ └── cccc.xml
    10. └── ImageSets
    11. └── Main
    12. ├── train.txt # txt文件中每一行包含一个图片的名称
    13. └── val.txt

      其中JPEGImages目录中存放的是源图片的数据,(当然图片并不一定要是.jpg格式的,只是规定文件夹名字叫JPEGImages);
      Annotations目录中存放的是标注数据,VOC的标注是xml格式的,文件名与JPEGImages中的图片一一对应;
      ImageSets/Main目录中存放的是训练和验证时的文件列表,每行一个文件名(不包含扩展名),例如train.txt是下面这种格式的:

    1. # train.txt
    2. aaaa
    3. bbbb
    4. cccc

    XML标注格式

      xml格式的标注格式如下:

    1. <annotation>
    2. <folder>VOC_ROOT</folder>
    3. <filename>aaaa.jpg</filename> # 文件名
    4. <size> # 图像尺寸(长宽以及通道数)
    5. <width>500</width>
    6. <height>332</height>
    7. <depth>3</depth>
    8. </size>
    9. <segmented>1</segmented> # 是否用于分割(在图像物体识别中无所谓)
    10. <object> # 检测到的物体
    11. <name>horse</name> # 物体类别
    12. <pose>Unspecified</pose> # 拍摄角度,如果是自己的数据集就Unspecified
    13. <truncated>0</truncated> # 是否被截断(0表示完整)
    14. <difficult>0</difficult> # 目标是否难以识别(0表示容易识别)
    15. <bndbox> # bounding-box(包含左下角和右上角xy坐标)
    16. <xmin>100</xmin>
    17. <ymin>96</ymin>
    18. <xmax>355</xmax>
    19. <ymax>324</ymax>
    20. </bndbox>
    21. </object>
    22. <object> # 检测到多个物体
    23. <name>person</name>
    24. <pose>Unspecified</pose>
    25. <truncated>0</truncated>
    26. <difficult>0</difficult>
    27. <bndbox>
    28. <xmin>198</xmin>
    29. <ymin>58</ymin>
    30. <xmax>286</xmax>
    31. <ymax>197</ymax>
    32. </bndbox>
    33. </object>
    34. </annotation>

    制作自己的VOC数据集

      制作自己数据集的步骤为:

      ① 新建一个JPEGImages的文件夹,把所有图片放到这个目录。(或者使用ln -s把图片文件夹软链接JPEGImages);

      ② 由原来的数据格式生成xml,其中posetruncateddifficult没有指定时使用默认的即可。bounding box的格式是[x1,y1,x2,y2],即[左上角的坐标, 右下角的坐标]x是宽方向上的,y是高方向上的。

      ③ 随机划分训练集和验证集,训练集的文件名列表存放在ImageSets/Main/train.txt,验证集的文件名列表存放在ImageSets/Main/val.txt

    参考代码

      附一个由csvvoc格式的脚本:

    1. # encoding=utf-8
    2. import os
    3. from collections import defaultdict
    4. import csv
    5. import cv2
    6. import ipdb
    7. import misc_utils as utils # pip3 install utils-misc==0.0.5 -i https://pypi.douban.com/simple/
    8. import json
    9. utils.color_print('建立JPEGImages目录', 3)
    10. os.makedirs('Annotations', exist_ok=True)
    11. utils.color_print('建立Annotations目录', 3)
    12. os.makedirs('ImageSets/Main', exist_ok=True)
    13. utils.color_print('建立ImageSets/Main目录', 3)
    14. files = os.listdir('train')
    15. files.sort()
    16. mem = defaultdict(list)
    17. confirm = input('即将生成annotations,大约需要3-5分钟,是否开始(y/n)? ')
    18. if confirm.lower() != 'y':
    19. utils.color_print(f'Aborted.', 3)
    20. exit()
    21. with open('train.csv', 'r') as f:
    22. csv_file = csv.reader(f)
    23. '''
    24. 读取的csv_file是一个iterator,每个元素代表一行
    25. '''
    26. for i, line in enumerate(csv_file):
    27. if i == 0:
    28. continue
    29. filename, width, height, bbox, _ = line
    30. x1, y1, x2, y2 = json.loads(bbox)
    31. x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
    32. x2 += x1
    33. y2 += y1
    34. mem[filename].append([x1, y1, x2, y2])
    35. for i, filename in enumerate(mem):
    36. utils.progress_bar(i, len(mem), 'handling...')
    37. img = cv2.imread(os.path.join('train', filename))
    38. # height, width, _ = img.shape
    39. with open(os.path.join('Annotations', filename.rstrip('.jpg')) + '.xml', 'w') as f:
    40. f.write(f"""
    41. train
    42. {filename}.jpg
    43. 1024
    44. 1024
    45. 3
    46. 0\n""")
    47. for x1, y1, x2, y2 in mem[filename]:
    48. f.write(f"""
    49. wheat
    50. Unspecified
    51. 0
    52. 0
    53. {x1}
    54. {y1}
    55. {x2}
    56. {y2}
    57. \n"""
      )
    58. f.write("")
    59. files = list(mem.keys())
    60. files.sort()
    61. f1 = open('ImageSets/Main/train.txt', 'w')
    62. f2 = open('ImageSets/Main/val.txt', 'w')
    63. train_count = 0
    64. val_count = 0
    65. with open('ImageSets/Main/all.txt', 'w') as f:
    66. for filename in files:
    67. # filename = filename.rstrip('.jpg')
    68. f.writelines(filename + '\n')
    69. if utils.gambling(0.1): # 10%的验证集
    70. f2.writelines(filename + '\n')
    71. val_count += 1
    72. else:
    73. f1.writelines(filename + '\n')
    74. train_count += 1
    75. f1.close()
    76. f2.close()
    77. utils.color_print(f'随机划分 训练集: {train_count}张图,测试集:{val_count}张图', 3)

    二、COCO格式

    目录结构

      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

      这里的train2017val2017称为set_nameannnotations文件夹中的json格式的标注文件名要与之对应并以instances_开头,也就是instances_{setname}.json

    json标注格式

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

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

    制作自己的数据集的时候infolicenses是不需要的。只需要中间的三个字段即可。

    其中images是一个字典的列表,每个图片的格式如下:

    1. # json['images'][0]
    2. {
    3. 'license': 4,
    4. 'file_name': '000000397133.jpg',
    5. 'coco_url': 'http://images.cocodataset.org/val2017/000000397133.jpg',
    6. 'height': 427,
    7. 'width': 640,
    8. 'date_captured': '2013-11-14 17:02:52',
    9. 'flickr_url': 'http://farm7.staticflickr.com/6116/6255196340_da26cf2c9e_z.jpg',
    10. 'id': 397133}

      自己的数据集只需要写file_name,height,widthid即可。id是图片的编号,在annotations中也要用到,每张图是唯一的。

      categories表示所有的类别,格式如下:

    1. [
    2. {'supercategory': 'person', 'id': 1, 'name': 'person'},
    3. {'supercategory': 'vehicle', 'id': 2, 'name': 'bicycle'},
    4. {'supercategory': 'vehicle', 'id': 3, 'name': 'car'},
    5. {'supercategory': 'vehicle', 'id': 4, 'name': 'motorcycle'},
    6. {'supercategory': 'vehicle', 'id': 5, 'name': 'airplane'},
    7. {'supercategory': 'vehicle', 'id': 6, 'name': 'bus'},
    8. {'supercategory': 'vehicle', 'id': 7, 'name': 'train'},
    9. {'supercategory': 'vehicle', 'id': 8, 'name': 'truck'},
    10. {'supercategory': 'vehicle', 'id': 9, 'name': 'boat'}
    11. # ....
    12. ]

      annotations是检测框的标注,一个bounding box的格式如下:

    1. {'segmentation': [[0, 0, 60, 0, 60, 40, 0, 40]],
    2. 'area': 240.000,
    3. 'iscrowd': 0,
    4. 'image_id': 289343,
    5. 'bbox': [0., 0., 60., 40.],
    6. 'category_id': 18,
    7. 'id': 1768}

      其中segmentation是分割的多边形,如果不知道直接填写[[x1, y1, x2, y1, x2, y2, x1, y2]]就可以了,area是分割的面积,bbox是检测框的[x, y, w, h]坐标,category_id是类别id,与categories中对应,image_id图像的id,idbboxid,每个检测框是唯一的。

    参考代码

      附一个VOC转COCO格式的参考代码

    1. voc_dataset = VOCTrainValDataset(voc_root,
    2. class_names,
    3. split=train_split,
    4. format=img_format,
    5. transforms=preview_transform)
    6. output_file = f'instances_{train_split[:-4]}.json'
    7. for i, sample in enumerate(voc_dataset):
    8. utils.progress_bar(i, len(voc_dataset), 'Drawing...')
    9. image = sample['image']
    10. bboxes = sample['bboxes'].cpu().numpy()
    11. labels = sample['labels'].cpu().numpy()
    12. image_path = sample['path']
    13. h, w, _ = image.shape
    14. global_image_id += 1
    15. coco_dataset['images'].append({
    16. 'file_name': os.path.basename(image_path),
    17. 'id': global_image_id,
    18. 'width': int(w),
    19. 'height': int(h)
    20. })
    21. for j in range(len(labels)):
    22. x1, y1, x2, y2 = bboxes[j]
    23. x1, y1, x2, y2 = float(x1), float(y1), float(x2), float(y2),
    24. category_id = int(labels[j].item()) + 1
    25. # label_name = class_names[label]
    26. width = max(0, x2 - x1)
    27. height = max(0, y2 - y1)
    28. area = width * height
    29. global_annotation_id += 1
    30. coco_dataset['annotations'].append({
    31. 'id': global_annotation_id,
    32. 'image_id': global_image_id,
    33. 'category_id': category_id,
    34. 'segmentation': [[x1, y1, x2, y1, x2, y2, x1, y2]],
    35. 'area': float(area),
    36. 'iscrowd': 0,
    37. 'bbox': [x1, y1, width, height],
    38. })
    39. with open(output_file, 'w', encoding='utf-8') as f:
    40. json.dump(coco_dataset, f, ensure_ascii=False)
    41. print(f'Done. coco json file has been saved to `{output_file}`')

  • 相关阅读:
    物联网开发笔记(38)- 使用Micropython开发ESP32开发板之控制温度传感器(DS18B20)
    媒体软文投放的流程与媒体平台的选择
    PHP JSON的解析和创建
    ASEMI整流桥KBU808参数,KBU808尺寸,KBU808大小
    boost之实用工具
    SpringBoot的学习
    互联网快讯:吉利正式收购魅族;胰岛素集采在31省全面落地
    java毕业设计琳琅天上超市管理系统mybatis+源码+调试部署+系统+数据库+lw
    由于flutter_app依赖于flutter_swiper>=0.0.2,不支持零安全,版本解决失败。
    答粉丝问)【问题记录&解决】如何重新训练已经经过p-tuning微调的模型;自然语言处理平台dialogflow 智能对话式问答应用程序 相关问题
  • 原文地址:https://blog.csdn.net/a15608445683/article/details/126648977