第二十章 解读PASCAL VOC2012与MS COCO数据集(工具)
PASCAL VOC2012数据集
Pascal Visual Object Classes 2012 官方发布网站地址为:http://host.robots.ox.ac.uk/pascal/VOC/voc2012/。官方发布的关于介绍该数据集的一篇文章名为《The PASCALVisual Object Classes Challenge: A Retrospective》的链接为:http://host.robots.ox.ac.uk/pascal/VOC/pubs/everingham15.pdf。

1 简介
Pascal VOC挑战赛是一项世界知名的世界级计算机视觉竞赛项目(简称PASCAL),其全称为Pattern Analysis, Statistical Modeling and Computational Learning(简称PASCAL),是一项由欧盟资助支持下的开放性研究平台。该竞赛主要涵盖的主要领域包括图像识别技术、物体检测技术和物体分割技术等基础研究方向以及行为分析技术等前沿探索课题。
- 图像分类与目标检测任务

- 分割任务需要注意以下几点:首先,在图像分割领域中通常会涉及三种类型:语义分割、实例分割以及全景分割;其中实例分割的主要目的是通过不同颜色标记每个独立的目标对象(如示意图中间所示),而语义分割则是将同类目标统一用相同颜色标注(如示意图右侧展示)。值得注意的是,在实际应用中应根据具体需求选择合适的分割方法。

- 行为识别任务

- 人体布局检测任务

2 Pascal VOC数据集目标类别
该数据库涵盖Pascal VOC集合中的20个目标类别数目。该图表呈现了各类别名称及其所属的上位类别。

3 数据集下载与目录结构
下载地址: http://host.robots.ox.ac.uk/pascal/VOC/voc2012/index.html#devkit
打开链接后如下图所示,只用下载training/validation data (2GB tar file)文件即可。

下载后将文件进行解压,解压后的文件目录结构如下所示:
VOCdevkit
└── VOC2012
├── Annotations 所有的图像标注信息(XML文件)
├── ImageSets
│ ├── Action 人的行为动作图像信息
│ ├── Layout 人的各个部位图像信息
│ │
│ ├── Main 目标检测分类图像信息
│ │ ├── train.txt 训练集(5717)
│ │ ├── val.txt 验证集(5823)
│ │ └── trainval.txt 训练集+验证集(11540)
│ │
│ └── Segmentation 目标分割图像信息
│ ├── train.txt 训练集(1464)
│ ├── val.txt 验证集(1449)
│ └── trainval.txt 训练集+验证集(2913)
│
├── JPEGImages 所有图像文件
├── SegmentationClass 语义分割png图(基于类别)
└── SegmentationObject 实例分割png图(基于目标)
AI写代码

请注意,在.train, .val, 和.trainval这三个文件中存储的是对应标注文件的索引信息。如上所示:

4 目标检测任务
接下来简单介绍下如何使用该数据集中目标检测的数据。
首先,在Main目录中获取指定的文本数据。需要注意的是,在Main目录下除了train.txt、val.txt和trainval.txt文件之外,还存在针对各个类别的专门数据文件(例如bus_train.txt、bus_val.txt以及bus_trainval.txt)。例如,在进行训练时通常会采用train.txt中的数据集。这些数据通常会被用来生成相应的标签索引信息。
├── Main 目标检测分类图像信息
│ ├── train.txt 训练集(5717)
│ ├── val.txt 验证集(5823)
│ └── trainval.txt 训练集+验证集(11540)
随后,在Annotations/目录中使用索引查找对应的XML标注文件。例如索引为`2007_000323时能够在该目录中找到对应名称为2007_000323.xml的XML文件(如图所示)。在标注文件中包含了所需的所有信息例如filename字段能够定位到对应的图片位于JPEGImages目录下size字段则记录了图像的宽度、高度以及通道信息(通道通常指RGB颜色通道)。每个object代表一个目标物体其中name字段记录了目标名称 pose字段表示目标物体的姿态(朝向) truncated字段指示目标是否被部分截断 difficult字段标识检测难度(值为1表示较难 为0表示简单) bndbox字段则完整地记录了目标边界框的位置和尺寸

接着按照如下方法,在标注文件的'filename'字段所在的'JPEGImages'目录中查找相应的图片。例如,在Annotations/Annotations下的一个.xml文档中找到对应的图像路径时,在Annotations/JPEGImages目录下会存在对应的JPG图像。

5 语义分割任务
接下来简单介绍下如何使用该数据集中语义分割的数据。
首先,在Segmentation文件中获取对应的train.txt文件内容。例如,在训练过程中使用train.txt中的数据进行处理时,则需要解析该train.txt文件的内容,并根据每行数据提取相应的图像索引信息。
└── Segmentation 目标分割图像信息
├── train.txt 训练集(1464)
├── val.txt 验证集(1449)
└── trainval.txt 训练集+验证集(2913)
通过索引定位至JPEGImages目录中查找相应的图像;例如,在索引位置为2994时(对应图像编号为)可以看到该图像位于path/.../2994/该编号下的.jpg文件

通过索引定位到SegmentationClass/文件夹中对应的标注图像(.png格式)。比如使用2014_11968作为索引名称,则即可获取到对应位置的`.png$文件。

在语义分割任务中,在标注图像文件(.png格式)使用PIL库中的Image.open()函数进行读取操作时,默认得到一张单通道图片。具体而言,在目标边缘位置设置像素值255(通常在训练过程中会忽略这些像素值),而背景区域则设为0像素值。此外,在目标区域内根据目标类别对应的索引号信息对相应区域进行填充颜色处理。例如,在分类任务中人被标记的目标索引号通常是15号类标签,则对该人区域填充15号类标签对应的颜色代码信息。

6 实例分割任务
同样地,在Segmentation文件中进行第一步操作:读取对应的train.txt文件中的数据集,并对其中的每一行进行逐行解析处理。每条记录对应一个图像的索引信息。
└── Segmentation 目标分割图像信息
├── train.txt 训练集(1464)
├── val.txt 验证集(1449)
└── trainval.txt 训练集+验证集(2913)
根据索引,在 JPEGImages 文件夹中定位对应的图像。例如取编号为 2XXX_XXXXXX.jpg 的图像文件作为目标对象进行处理。

通过索引,在指定的SegmentationObject文件夹中查找相应的标注图像(.png)即可获取所需信息。例如,在已知实例中使用2014_119568这一编号时,则可直接访问2014_119568.png这一文件。

注意,在实例分割过程中对应的是标注图像(.png),该图像通过PIL Image.open()函数获取时默认采用单通道模式。在背景区域中的像素值被设定为0;而位于目标边缘或需特别处理的部分则会被赋予255(通常情况下这些区域会被系统自动忽略)。接下来,在Annotations目录内查找与之对应的XML文件,并解析其中记录的信息;这些解析结果会在PNG格式下生成相应的视觉标记层分布情况,并且这些视觉标记层的位置设置将完全吻合原始的目标标识符位置信息;具体来说,在这种情况下生成的目标标记层数量和位置将与原始的目标标识符数目及分布情况完全一致;如上图所示,在XML文档中的每一个目标标识符与PNG标注图片中的相应像素层之间存在明确的一一对应关系

7 类别索引与名称对应关系
下面给出在Pascal VOC数据集中各目标类别名称与类别索引对应关系:
{
"background": 0,
"aeroplane": 1,
"bicycle": 2,
"bird": 3,
"boat": 4,
"bottle": 5,
"bus": 6,
"car": 7,
"cat": 8,
"chair": 9,
"cow": 10,
"diningtable": 11,
"dog": 12,
"horse": 13,
"motorbike": 14,
"person": 15,
"pottedplant": 16,
"sheep": 17,
"sofa": 18,
"train": 19,
"tvmonitor": 20
}
AI写代码json

MS COCO数据集
1. MS COCO数据集简介
简介
MS COCO是一个规模宏大的常用数据集;它涵盖了目标检测、分割以及图像描述等多个领域;该数据集的主要特性如下:
- 目标级分割
- 图像情景识别
- 超像素分割
- 超过33万张图像(其中超过20万张被标注)
- 150万个对象实例
- 80个目标类别
- 91个材料类别
- 每张图像有5段情景描述
- 对25万个人进行了关键点标注

-
注意点
-
需要特别关注的一个要点是"什么是'stuff'类别"这一问题,在官方论文中对此有明确说明:
where 'stuff'类别被正式定义为那些具有模糊界限的材料和物体(如天空、街道、草地)
直观来说,“stuff"类别的范围包括那些具有模糊界限的材料与物体。- object的80类与stuff中的91类的区别在哪?在官方的介绍论文中有如下说明:
Note that we have limited the 2014 release to a subset of 80 categories. We did not collect segmentations for the following 11 categories: hat, shoe, eyeglasses (too many instances), mirror, window, door, street sign (ambiguous and difficult to label), plate, desk (due to confusion with bowl and dining table, respectively) and blender, hair brush (too few instances).
简单的理解就是object80类是stuff91类的子集 。对于我们自己使用,如果仅仅是做目标检测,基本只用object80类即可。
- object的80类与stuff中的91类的区别在哪?在官方的介绍论文中有如下说明:
*基于PASCAL VOC数据集的简单性比较 该文中展示了通过详细对比分析得出的结论图。通过对比可以看出,不仅分类器性能得到明显提升,而且各分类器在同类任务中的表现差异也更加显著。

为了深入了解该数据集,请访问官方介绍文档:Microsoft COCO: Common Objects in Context https://arxiv.org/pdf/1405.0312.pdf

2. MS COCO数据集下载
这里以下载coco2017数据集为例,主要下载三个文件:
2017 Train images [118K/18GB]:涵盖2017年训练阶段使用的全部图像数据集2017 Val images [5K/1GB]:涵盖2017年验证阶段使用的全部验证数据集2017 Train/Val annotations [241MB]:其中包含在内的是训练集与验证集对应的标注信息存储在[241MB]的数据集中
下载后都解压到coco2017目录下,可以得到如下目录结构:
├── coco2017: 数据集根目录
├── train2017: 所有训练图像文件夹(118287张)
├── val2017: 所有验证图像文件夹(5000张)
└── annotations: 对应标注文件夹
├── instances_train2017.json: 对应目标检测、分割任务的训练集标注文件
├── instances_val2017.json: 对应目标检测、分割任务的验证集标注文件
├── captions_train2017.json: 对应图像描述的训练集标注文件
├── captions_val2017.json: 对应图像描述的验证集标注文件
├── person_keypoints_train2017.json: 对应人体关键点检测的训练集标注文件
└── person_keypoints_val2017.json: 对应人体关键点检测的验证集标注文件夹
AI写代码

3. MS COCO标注文件格式
官网提供了关于标注文件的规范说明,请访问以下链接获取详细规定:
3.1 使用Python的json库查看
参考官方提供的文档说明后
import json
json_path = "/data/coco2017/annotations/instances_val2017.json"
json_labels = json.load(open(json_path, "r"))
print(json_labels["info"])
AI写代码python
运行
通过单步调试可以看出,加载后该数据呈现为字典形式,并且包含了详细的info、licenses、images、annotations以及categories信息。

其中:
images 是一个列表对象(其中各个元素的数量与所处理的图像数量一致),在列表中的每一个条目都对应一个字典结构。具体来说,则包含了以下各项关键信息:图片名称、图片宽度与高度数据等基础参数。

annotations是一个列表(元素个数对应数据集中所有标注的目标个数,注意不是图像的张数),列表中每个元素都是一个dict对应一个目标的标注信息。包括目标的分割信息 (polygons多边形)、目标边界框信息[x,y,width,height](左上角x,y坐标,以及宽高) 、目标面积 、对应图像id 以及类别id 等。iscrowd参数只有0或1两种情况,一般0代表单个对象,1代表对象集合。

categories是一个容器(其数量与检测的目标类别一一对应)。在该容器中,每一个条目都是一些元数据字段的集合。这些元数据字段包括以下三个字段:
- 类别id
- 类别名称
- 所属超类

3.2 使用官方cocoAPI查看
官方提供了获取MS COCO数据集相关信息的一个API(同时此APIMeaningfully supports additional core functionalities),具体链接如下:其中包含了关于此APIMeaningfully supports additional core functionalities的一个演示资源。
- Linux系统安装pycocotools:
pip install pycocotools
- Windows系统安装pycocotools:
pip install pycocotools-windows
读取每张图片的bbox信息
下面是使用pycocotools读取图像以及对应bbox信息的简单示例:
import os
from pycocotools.coco import COCO
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
json_path = "/data/coco2017/annotations/instances_val2017.json"
img_path = "/data/coco2017/val2017"
# load coco data
coco = COCO(annotation_file=json_path)
# get all image index info
ids = list(sorted(coco.imgs.keys()))
print("number of images: {}".format(len(ids)))
# get all coco class labels
coco_classes = dict([(v["id"], v["name"]) for k, v in coco.cats.items()])
# 遍历前三张图像
for img_id in ids[:3]:
# 获取对应图像id的所有annotations idx信息
ann_ids = coco.getAnnIds(imgIds=img_id)
# 根据annotations idx信息获取所有标注信息
targets = coco.loadAnns(ann_ids)
# get image file name
path = coco.loadImgs(img_id)[0]['file_name']
# read image
img = Image.open(os.path.join(img_path, path)).convert('RGB')
draw = ImageDraw.Draw(img)
# draw box to image
for target in targets:
x, y, w, h = target["bbox"]
x1, y1, x2, y2 = x, y, int(x + w), int(y + h)
draw.rectangle((x1, y1, x2, y2))
draw.text((x1, y1), coco_classes[target["category_id"]])
# show image
plt.imshow(img)
plt.show()
AI写代码python
运行

从pycocotools获取的图像数据及其相应的target信息可以通过与matplotlib库协作生成标注示例图。

读取每张图像的segmentation信息
下面是使用pycocotools读取图像segmentation信息的简单示例:
import os
import random
import numpy as np
from pycocotools.coco import COCO
from pycocotools import mask as coco_mask
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
random.seed(0)
json_path = "/data/coco2017/annotations/instances_val2017.json"
img_path = "/data/coco2017/val2017"
# random pallette
pallette = [0, 0, 0] + [random.randint(0, 255) for _ in range(255*3)]
# load coco data
coco = COCO(annotation_file=json_path)
# get all image index info
ids = list(sorted(coco.imgs.keys()))
print("number of images: {}".format(len(ids)))
# get all coco class labels
coco_classes = dict([(v["id"], v["name"]) for k, v in coco.cats.items()])
# 遍历前三张图像
for img_id in ids[:3]:
# 获取对应图像id的所有annotations idx信息
ann_ids = coco.getAnnIds(imgIds=img_id)
# 根据annotations idx信息获取所有标注信息
targets = coco.loadAnns(ann_ids)
# get image file name
path = coco.loadImgs(img_id)[0]['file_name']
# read image
img = Image.open(os.path.join(img_path, path)).convert('RGB')
img_w, img_h = img.size
masks = []
cats = []
for target in targets:
cats.append(target["category_id"]) # get object class id
polygons = target["segmentation"] # get object polygons
rles = coco_mask.frPyObjects(polygons, img_h, img_w)
mask = coco_mask.decode(rles)
if len(mask.shape) < 3:
mask = mask[..., None]
mask = mask.any(axis=2)
masks.append(mask)
cats = np.array(cats, dtype=np.int32)
if masks:
masks = np.stack(masks, axis=0)
else:
masks = np.zeros((0, height, width), dtype=np.uint8)
# merge all instance masks into a single segmentation map
# with its corresponding categories
target = (masks * cats[:, None, None]).max(axis=0)
# discard overlapping instances
target[masks.sum(0) > 1] = 255
target = Image.fromarray(target.astype(np.uint8))
target.putpalette(pallette)
plt.imshow(target)
plt.show()
AI写代码python
运行

通过pycocotools获取的图像segmentation数据,并结合matplotlib库生成标注图。

读取人体关键点信息
在MS COCO任务中对人体进行了关键点标定工作,在该任务中共标定了17个关键标志点其具体分布情况如下:
["nose","left_eye","right_eye","left_ear","right_ear","left_shoulder","right_shoulder","left_elbow","right_elbow","left_wrist","right_wrist","left_hip","right_hip","left_knee","right_knee","left_ankle","right_ankle"]
在COCO提供的标注文件中,默认情况下每个人体的标注格式如下所示。由于共有17个关键点因此总共有51个数值。将它们按照每组3个划分前两个值分别表示关键点的x和y坐标第三个值表示该关键点的可见度它只会取三个整数值:0 1 2其中0表示该标记位于图像外无法标注1表示虽然不可见但大致能推测位置而2则表明标记可见。若第三个值为0则对应的x和y都设为0:
[427, 170, 1, 429, 169, 2, 0, 0, 0, 434, 168, 2, 0, 0, 0, 441, 177, 2, 446, 177, 2, 437, 200, 2, 430, 206, 2, 430, 220, 2, 420, 215, 2, 445, 226, 2, 452, 223, 2, 447, 260, 2, 454, 257, 2, 455, 290, 2, 459, 286, 2]
下面是使用pycocotools读取图像keypoints信息的简单示例:
import numpy as np
from pycocotools.coco import COCO
json_path = "/data/coco2017/annotations/person_keypoints_val2017.json"
coco = COCO(json_path)
img_ids = list(sorted(coco.imgs.keys()))
# 遍历前5张图片中的人体关键点信息(注意,并不是每张图片里都有人体信息)
for img_id in img_ids[:5]:
idx = 0
img_info = coco.loadImgs(img_id)[0]
ann_ids = coco.getAnnIds(imgIds=img_id)
anns = coco.loadAnns(ann_ids)
for ann in anns:
xmin, ymin, w, h = ann['bbox']
# 打印人体bbox信息
print(f"[image id: {img_id}] person {idx} bbox: [{xmin:.2f}, {ymin:.2f}, {xmin + w:.2f}, {ymin + h:.2f}]")
keypoints_info = np.array(ann["keypoints"]).reshape([-1, 3])
visible = keypoints_info[:, 2]
keypoints = keypoints_info[:, :2]
# 打印关键点信息以及可见度信息
print(f"[image id: {img_id}] person {idx} keypoints: {keypoints.tolist()}")
print(f"[image id: {img_id}] person {idx} keypoints visible: {visible.tolist()}")
idx += 1
AI写代码python
运行

终端显示的结果表明,在这五幅图中仅有一幅图像标注了人体关键部位的数据
[image id: 139] person 0 bbox: [412.80, 157.61, 465.85, 295.62]
[image id: 139] person 0 keypoints: [[427, 170], [429, 169], [0, 0], [434, 168], [0, 0], [441, 177], [446, 177], [437, 200], [430, 206], [430, 220], [420, 215], [445, 226], [452, 223], [447, 260], [454, 257], [455, 290], [459, 286]]
[image id: 139] person 0 keypoints visible: [1, 2, 0, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
[image id: 139] person 1 bbox: [384.43, 172.21, 399.55, 207.95]
[image id: 139] person 1 keypoints: [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[image id: 139] person 1 keypoints visible: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
