COCO 和 CityScapes 数据集的标注格式和使用
COCO 和 CityScapes 是 Detection 常用的两个数据集,当然由于 Instance Segmentation 和 Detection 任务是相似的,且它们都提供 mask 级的 label,所以也可以使用。目前它们也都提供了 stuff 的 label,所以也可以用于 Semantic Segmentation,以及结合两种 label 之后用于 Panoptic Segmentation。此外它们还可以用于一些其它任务,下面会提到。
本文主要关注 Detection/Instance Segmentation,对于其它 Segmentation 也会简略概括,其余一些 labels 则直接复制网上现有资料或略过。
2023 年了重新看了下,原来我 2019 年 8 月还在学习这个,这几年 AI 的发展真是快哇,有些后来很著名的东西当时还是刚刚听说,说的就是你 Transformer(其实对那会我的记忆里是 BERT 听说很屌,当时还搞不清它和 Transformer 的关系)。最近准备再认真看几篇这些年重要的论文和全部 Code,写一点 Blog。。
这几年边上班边玩耍,开始在 YouTube 更新一点旅游视频(4K HDR/3DVR),欢迎订阅:https://youtube.com/@snow_miku
Table of Contents
COCO Dataset
COCO 的全称是 Common Objects in COntext,是微软团队提供的一个可以用来进行目标检测、图像分割、关键点检测、图像描述的数据集。COCO 通过在 Flickr 上搜索 80 个对象类别和各种场景类型来收集图像,其使用了亚马逊的 Mechanical Turk (AMT)。
COCO 目前有 2014 和 2017 两个版本,图片都是一样的,只是 train/val 的划分不同。COCO 2014 经过实践后,开始将一部分 val 的图片划入 train 中。后来的 2017 中采用了这一划分。如果是 Detection/Instance Segmentation 这两个版本没有太多区别,2014 有现成的新 JSON。2017 后来出了 Semantic Segmentation 和 Panoptic Segmentation 的 label,除了 JSON 还提供一种 PNG 图片格式的,这两个 task 直接用 2017 会方便一些。
数据集下载:http://cocodataset.org/#download
COCO 2014 trainval35k JSON 下载:https://github.com/rbgirshick/py-faster-rcnn/blob/master/data/README.md
对于 Detection/Instance Segmentation,COCO_2017_train = COCO_2014_train + valminusminival (:val - minival)
, COCO_2017_val = minival
。
在同一个文件夹中解压 zip 文件,得到的文件结构如下:
COCO 2014
COCO 2017
有些解压工具会一次性读取/扫描 zip 里面的所有文件,导致 train 这种超大的 zip 打开就卡住,可以使用以下 Python 脚本解压,只需修改 coco_dataset_path
后运行。且多数解压工具会自动在外面再套上一个文件夹,需要手动调整一下目录结构或者用下面的脚本。
1 | import zipfile |
COCO 有 Object Detection (Instance Segmentation)、Keypoint Detection、Stuff Segmentation (Semantic Segmentation)、Panoptic Segmentation、Image Captioning 五种标注。
COCO 标注格式
COCO 官网有 Data Format 的简介,下面逐项详细介绍。
Instance (Object Detection) 的标注格式
以下为 COCO-2017 Val 的 JSON (instance_val2017.json
) 部分内容和整体格式
1 | { |
info
是整个数据集的描述信息,之后不会用到。licenses
是协议的 id、网址和名称,之后每张图片的信息中都会有一个 licenses id。images
中一般需要用到的有四个。file_name
:图片在对应 train/val/test 文件夹中的文件名;height/width
:图片的宽高;id
:图片的唯一 id,在 COCO API 中会用到。annotations
所有图片上所有物体的标注统一储存在同一个 “annotations” 中,在这里面的每一个标注都含有它对应哪张图片的信息(img_id
),和它自己的序号(id
)。"iscrowd": 0
的 annotation 是 Polygons 形式的,表示单个的对象,它的 “segmentation” 是二维 list,这是因为一个物体可能被遮挡部分而分隔开(少数情况),需要多个封闭的曲线才能标注。每个一维 list 里面就是一个封闭图形,值为 xyxyxy… 形式的坐标,即[510.66,423.01,511.72,420.03,...]
代表图上(510.66,423.01), (511.72,420.03), ...
n 个点。"iscrowd": 1
的是 RLE(Run-Length Encoding,游程编码) 形式的,标注一组对象(密集,无法很好地分隔成单个的标注)。由0
开始,交替进行,比如[20736,2,453, ...]
表示有 20736 个0
,接下来是 2 个1
,接下来是 453 个0
…,所以这种形式还需要一个size
信息,最后把这一行 reshape 成这个形状就得到 label。"area"
为 mask 的面积。"bbox"
框的坐标,xywh 格式。都是 0-index 的,横轴为 x/w,像素的中心点为原点,即bbox = [0, 0, 1, 1]
包围了(左上角)第一个像素。categories
一个小类一个 id,被annotations
中指向。一个大类下包含若干个小类。注意 COCO 总共有 80+1 类 things,而 id 是 1~90,并不是连续的。
其中,info
、licenses
、images
这三个字段的内容在不同的 task 的 JSON 文件中是一样的。不同的是 annotation
和 category
。下面的只会介绍这两个字段。
COCO Keypoint Detection / Stuff Segmentation / Image Caption 的标注格式
Keypoint Detection
annotations
完全包含上面 Detection 中的内容,并且每个 anno 多出两个字段:"keypoints": [x1,y1,v1,...], ...
和 "num_keypoints": <int>, ...
。keypoints
是一个长度为 3×k 的数组,其中 k 是这个类中 keypoints 的总数量。每一个 keypoint 是一个长度为 3 的数组。第一和第二个元素分别是 x 和 y 坐标值,第三个元素是个标志位 v,v=0 时表示这个关键点没有标注(这种情况下 x=y=v=0),v=1 时表示这个关键点标注了但是不可见(被遮挡了),v=2 时表示这个关键点标注了同时也可见。num_keypoints
表示这个目标上被标注的关键点的数量(v>0),比较小的目标上无法标注关键点。
categories
则只有一类 "supercategory": "person"
,同样包含了 Instance Detection 中的所有内容,并且多出了两个字段:"keypoints": [str]
和 "skeleton": [edge]
。keypoints
是一个长度为k的数组,包含了每个关键点的名字。skeleton
定义了各个关键点之间的连接性(比如人的左手腕和左肘就是连接的)。
1 | "annotations":[ |
Stuff Segmentation (Semantic Segmentation)
格式字段与上面 Instance Detection 的完全相同,只是 annotations
中的 iscrowd
没有意义,默认为 0
,且每个 segmentation
的值都是 encoded RLE,而不是之前的 polygons/uncompressed RLE(后面有 encoded/uncompressed 的介绍)。categories
的 id 为 92~183,共有 92 类。
1 | { |
Image Caption
没有 categories
字段。 annotations
多于 images 的个数,一张图片可以有多个描述。且只含三个信息,很简单,如下:
1 | annotation{ |
COCO Panoptic Segmentaion 的标注格式
全景分割的 annotations
与之前的格式不一样,它将一张图片的所有 annos 储存在一个 dict 中,而不像之前一样所有图片所有 annos 平级共同储存。以下是 COCO 2017 的 panoptic_val2017.json
。
1 | { |
因为 Panoptic 是 Non-Overlap 的,所以 segmentation 是用一张 PNG 图片储存:["annotations"][i]["file_name"]
。它是 class-agnostic 的,每个 segment 的 semantic information (class) 储存在 JSON:["annotations"][i]["segments_info"][j] ([id])
。以 RGB24 打开 PNG,如果像素值为 (0,0,0),则表示 Unlabeled pixels (void),其余的通过 ids = R + G×256 + B×256^2
即可得到 JSON 中对应的 id 值。i
表示总图片数量,j
表示每张图中标签的数量
此外,iscrowd 只与 things 有关。thing categories 与 Detection 中的完全相同,stuff categories 与 Semantic Seg 中的有所不同。categories
里面多出的 isthing
用来区分 things 和 stuff。在 panopticapi/panoptic_coco_categories.json 中还会多出一个 color: [int, int, int]
表示这一类的在生成 PNG 时的颜色,对于 things,不同 instance 之间会在这个 base color 上抖动一点,然后通过最终的 RGB 生成 JSON 里的 id。
COCO Python API
安装 COCO Python API:
1 | # Offical |
所有的 API 可以参考 GitHub/cocoapi-coco.py 和 GitHub/cocoapi-mask.py。这里列出部分 API 使用方式。
获取 Annotation 信息相关 API
首先读取 annotation json 文件:
1 | from pycocotools.coco import COCO |
img_ids = coco.getImgIds(imgIds=[], catIds=[]):
-> list[int]
空参数:返回所有图片的 id:[397133, 37777, 252219, 87038, 174482, 403385, 6818, 480985, 458054, 331352, ...]
,在 json 文件的"images" -> "id"
字段。
一个或两个参数传入 int 或 list[int]:返回同时满足所有参数中条件的所有图片(一张图片中可能有多个 Cats)imgs = coco.loadImgs(img_ids)
-> list[dict]
传入一个图片 id 组成的 list(或单个 int id),返回一个长度相同的 list,每个元素为一个 dict,就是 json 文件的"images"
字段中一个/多个的 dict 组成的 list。
一般使用imgs = {k: v for k, v in imgs.items() if k in ['id', 'width', 'height', 'file_name']}
去除无用的信息。ann_ids = coco.getAnnIds(imgIds=[], catIds=[], areaRng=[], iscrowd=None)
与getImgIds()
类似,返回所有 annotation 的 id,在"annotations" -> "id"
字段。一般传入imgIds=img_ids, iscrowd=False
。areaRng
传入一个 float array,代表 area 字段中的大小筛选。cat_ids = coco.getCatIds(catNms=[], supNms=[], catIds=[])
返回所有 categories 的 id:list[int],在这里就是[1, 2, 3, ..., 90]
。前两个参数是 list[string],最后一个是 list[int] 类型。anns = coco.loadAnns(ann_ids)
和cats = coco.loadCats(cat_ids)
与loadImgs()
类似,返回的就是annotations
字段中一个/多个的 dict 组成的 list。注意这里都是 Python list 而不是 Numpy Array,所以不能bbox[2:] = bbox[:2] + bbox[2:]
转换 xywh 为 xyxy。
BBox/Mask 相关 API
1 | import pycocotools.mask as mask |
各种函数和它的参数及格式在代码 GitHub/cocoapi-mask.py 开头的注释已经写得比较清楚了。说明两点:
里面的 Mask IoU 等计算都需要将 binary masks 转换为 encoded RLE 格式之后进行。之前 JSON 里面的 RLE 是 uncompressed RLE,由 binary masks 到它的转换之前已经介绍过了,而 encoded RLE 是
bytes
类型的,类似于1
。[hxwxn] Binary mask 可以使用mask.encode()
转换为 encoded RLE,而其他的包括 uncompressed RLE 可以用mask.frPyObjects()
转换。例如 binary masks 等等,coco 里面的数组都是 column-major order (Fortran memory order),调用一些 API 也要使用这种。一般的 numpy array 都是 row-major order (C memory order),也就是数组里面的每一行内的数在内存中是相邻的,同时在 reshape 和 broadcast 等操作中 last dim 是变化最快的。使用
np.ascontiguousarray()
和np.asfortranarray()
构造/转换数组。
标注可视化
在 GitHub/cocoapi-pycocoDemo.ipynb 中已经有了部分 anno 的图片和可视化代码。里面直接使用了 coco.showAnns(anns)
,下面 Gist 中的代码用 Numpy/OpenCV 画出一个被分隔成两个 Polygon 的物体的 Mask,和一个 RLE 格式的 Mask。
Multi-Polygon Annos
RLE Annos
全部的可以在 COCO 官网上使用 COCO Explorer 搜索查看。
Common Practice
COCO 除了 crowd 通常不参与训练,需要排除,此外有一些无效的标注也需要排除,它的类别标号不是连续的,需要创建 dict 映射一下,不然 Loss 函数会不接受大于 len(logit) 的标注。这部分的代码可参考 maskrcnn-benchmark/coco.py。
Panoptic API
https://github.com/cocodataset/panopticapi
CityScapes Dataset
在下载界面有很多文件,在这里只需要 leftImg8bit_trainvaltest.zip (11GB)
和 gtFine_trainvaltest.zip (241MB)
即可。图像是在车上左右放了两台相机拍摄的,因此这个数据集还可以有 depth maps 和 Right Image。除了 8bit 以外,还提供 16bit HDR 图片。此外还有车的 GPS 坐标,等等很多信息,供自动驾驶等综合任务使用。extra 中是额外 19998 张粗略标注的图片(对应 gtCoarse.zip (1.3GB)
)。对于纯粹的 Detection/Segmentation,只需要前面两个文件就足够了(3475 trainval,1525 test)。
对于文件目录结构和文件名含义,可以参考 GitHub/cityscapesScripts,这个 repo 还提供一些有用的脚本,git clone 到同一目录之后(去掉 Git 自动加上的文件夹),文件如下图。
CityScapes 的标注格式
与 COCO 不同,CityScapes 的 label 储存在对应的文件夹中而不是集中在一个 JSON,每一张图片对应了 4 个 label 文件,前半部分文件名相同:
{city}_{seq:0>6}_{frame:0>6}_gtFine_color.png
用于观看的彩色 label{city}_{seq:0>6}_{frame:0>6}_gtFine_instanceIds.png
为 16bit png label。如果是 stuff,则像素值为 0~24 的 class ID;如果是 things,则像素值为 class ID × 1000 + X,X 为连续的自然数,例如26000, 26001, 26003
可以表示图中的三辆不同的车。class ID 对应的类别名称见下一节。{city}_{seq:0>6}_{frame:0>6}_gtFine_labelIds.png
{city}_{seq:0>6}_{frame:0>6}_gtFine_polygons.json
1 | { |
cityscapesScripts
在上面 GitHub Repo 里 Scripts 介绍内的 Most important files 中,包含了 label 的定义和转换工具,可以阅读代码开头的注释。
helpers & preparation
helpers/labels.py
内定义了所有类别的 class ID,对于 hasInstances = True and ignoreInEval = False
的,即可以用于 Detection 的,共有 8+1 类。
cityscapesViewer
这是一个使用 PyQt4 写的 GUI 预览程序,在 Windows + Anaconda 下,只需要先 conda install sip
,然后在 https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyqt4 下载编译好的 Windows PyQt4 Wheel,然后 pip install
安装。
1 | cd CityScapse_ROOT/cityscapesscripts |
即可打开 GUI,按说明使用即可,如果报错 ModuleNotFoundError: No module named 'cityscapesscripts'
,那么可以在 cityscapesViewer.py
中加上 sys.path.append("../")
。如果 GUI 打开之后报错路径错误,那么检查下文件目录是否是上节图中那样,或是把 CityScapes 的根目录加到 PATH 中。
这个 GUI Viewer 非常好用,鼠标移到某个位置显示 label 区域和名称,载入方便迅速,工具齐全。
Convert Cityscapes Annotations to COCO Format
可以使用 maskrcnn-benchmark 附带的脚本:GitHub/maskrcnn-benchmark/tools/cityscapes/,使用方法:GitHub/Steps to convert Cityscapes Annotations to COCO Format。注意这里面的 gtFine 外面还有一个文件夹,跟上面不一样,或者可以修改 convert_cityscapes_to_coco.py 的 line127~line130。
此外,由于 CityScapes 数据集较小,可以使用 COCO 训练的模型作为其预训练模型,需要将 80+1 类的预测改为 10+1 类的预测,因此需要对模型进行部分改动。此外还有其他一些技巧,可参考Mask R-CNN: CityScapes