COCO数据集解析

news/2024/7/10 1:55:01 标签: COCO, 目标检测, 实例分割

介绍

官网:https://cocodataset.org/
下载地址:https://cocodataset.org/#download
COCO的全称是Common Objects in COntext,起源于微软于2014年出资标注党的Microsoft COCO数据,与ImageNet竞赛一样,是计算机视觉领域最受关注和权威的比赛之一。
COCO数据是一个大型、丰富的目标检测、分割和字幕数据集。其中一些样本如下所示。
image.png
COCO数据集主要包含有标注和无标注的数据:

  • 2014:训练集+验证集+测试集
  • 2015:测试集
  • 2017:训练集+验证集+测试集+未标注

image.png
如上图和下表所示,两个数据的图像数量分别如下,其中测试集都未提供标注文件:

COCO2014COCO2017
trainvaltesttrainvaltest
827834050440775118287500040670

COCO_20">COCO数据集格式

COCO数据集包含5种类型的标注,分别是:目标检测实例分割、关键点检测、全景分割和图像标注,都对应一个json文件。每个json文件主要包含如下五个部分:

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

info部分

info部分包含数据集的一些基本信息。

"info": 
{
  "description": "COCO 2017 Dataset",
  "url": "http://cocodataset.org",
  "version": "1.0","year": 2017,
  "contributor": "COCO Consortium",
  "date_created": "2017/09/01"
}

licenses部分

licenses部分包含数据集需要遵循的一些许可。

{
	"url":"http:\/\/creativecommons.org\/licenses\/by-nc-sa\/2.0\/",
	"id":1,
	"name":"Attribution-NonCommercial-ShareAlike License"
}

images部分

images中描述了数据集的图像信息,主要包括文件名、图像宽高信息。

{
    "coco_url": "", 
    "date_captured": "", 
    "file_name": "000001.jpg", 		# 只需要包含文件名即可, 在MMDetection训练中需要指定图像的路径
    "flickr_url": "", 
    "id": 1, 											# image_id
    "license": 0, 
    "width": 416, 								# 图像宽高
    "height": 416
}

categories描述了数据集中的类别信息,annotations中包含的是数据集的物体信息。不同的任务对应的json文件中annotation和categories的形式不同,分别如下:

目标检测实例分割

目标检测实例分割任务的标注文件为instances_train2017.json、instances_val2017.json这两个文件。

annotations部分

如下所示,每个对象实例都包含一系列字段,包括对象的类别ID、所属图像ID,annotation ID,分段掩码,目标框信息。

{
    "id": int,
    "image_id": int,
    "category_id": int,
    "segmentation": RLE or [polygon],
    "area": float,
    "bbox": [x,y,width,height],
    "iscrowd": 0 or 1
}

其中:

  • bbox为每个对象提供一个包围框,[x,y]表示框的左上角
  • segmentation格式取决于这个实例是单个对象(即iscrowd=0,将使用polygons格式,以多边形顶点表示。注意,单个对象可能需要多个多边形表示,例如遮挡时。),还是一组对象(即iscrowd=1,将使用RLE格式,mask编码)。
  • area表示标注区域的面积。如果是矩形框,那就是高乘以宽;如果是polygon或者RLE,则是mask区域围成的面积。

对于实例分割任务,annotations部分既要包含bbox元素,也要包含segmentation要素。而在segmentation部分由于实例的形式不同,表示的方法也不同。

polygon格式

polygon格式如下,比较简单,这些数按照相邻的顺序两两组成一个点的xy坐标,包含n个数必定为偶数,表示n/2个点坐标。

{
  "segmentation": [[68.59,227.1,...]],
  "area": 1441.7063,				# 目标区域面积
  "iscrowd": 0,
  "image_id": 210520,
  "bbox": [68.59,200.56,50.24,47.77],		# [x, y, width, height]边界框坐标, 其中x,y为图像左上角点
  "category_id": 50,		# 类别ID
  "id": 2231047					# 对象ID
}
RLE格式

如果iscrowd=1,那么segmentation就是RLE格式(segmentation字段会含有counts和size数组),如下所示。COCO数据的RLE都uncompressed RLE格式(与之相对的是compact RLE)。RLE所占字节的大小和边界上的像素数量是正相关的。
segmentation部分中的counts和size数组共同组成了这幅图片中的分割mask。其中size表示这张图像的宽高,然后对于一副图像的一个segmentation而言,每一个像素点要么在分割的目标区域中,要么是背景。显然,如果该像素在目标区域中为1,在背景中为1。那么对于一个张240×320的图像来说,共有76800个像素点,这样表示的大小为76800个bit,但是这样写很浪费空间,RLE则直接用0或1的个数表示。
RLE,Run-Length Encoding,变动长度编码算法,是一种对于二值图像的编码方式,以不同码字来表示连续的黑、白像素数。RLE格式是一种更加高效的图像语义分割数据表示格式,其数据以一串RLE编码的方式进行存储,而不是以像素点的形式存储,有效减少了数据的体积。RLE格式分割标注文件即是使用RLE格式对物体分割区域进行标注的文件。
很多分割数据集为了节省空间,标注文件采用了RLE格式,比如COCO等。RLE格式带来的好处就是基于RLE去计算目标区域的面积以及两个目标之间的union和intersection交并集时效果很高。

{
  "segmentation": {"counts": [272,2,4,4,4,4,...],"size": [240,320]},
  "area": 18419,
  "iscrowd": 1,
  "image_id": 448263,
  "bbox": [1,0,276,122],
  "category_id": 1,
  "id": 900100448263
}

基于Python实现RLE格式分割标注文件的格式转换 - 海_纳百川 - 博客园

polygon转RLE

假设输入的多边形数据,可以通过如下方式将其转换为RLE格式,以便后续转换为mask

if isinstance(mask_ann, list):
    # polygon -- a single object might consist of multiple parts
    # we merge all parts into one mask rle code
    rles = maskUtils.frPyObjects(mask_ann, img_h, img_w)
    rle = maskUtils.merge(rles)
RLE转mask二值图像

对于RLE格式的数据,直接调用decode方法来生成mask,其中0表示背景,1表示前景。

def _poly2mask(mask_ann, img_h, img_w):
    """Private function to convert masks represented with polygon to
    bitmaps.

    Args:
        mask_ann (list | dict): Polygon mask annotation input.
        img_h (int): The height of output mask.
        img_w (int): The width of output mask.

    Returns:
        numpy.ndarray: The decode bitmap mask of shape (img_h, img_w).
    """

    if isinstance(mask_ann, list):
        # polygon -- a single object might consist of multiple parts
        # we merge all parts into one mask rle code
        rles = maskUtils.frPyObjects(mask_ann, img_h, img_w)
        rle = maskUtils.merge(rles)
    elif isinstance(mask_ann['counts'], list):
        # uncompressed RLE
        rle = maskUtils.frPyObjects(mask_ann, img_h, img_w)
    else:
        # rle
        rle = mask_ann
    mask = maskUtils.decode(rle)
    return mask

def save_mask(mask, filename):
    mask = mask * 255		
    im = Image.fromarray(mask)
    im.save(filename)

if __name__ == "__main__":
    dataDir='datasets/COCO/'
    dataType='val2017'
    annFile='{}/annotations/instances_{}.json'.format(dataDir,dataType)
    # 初始化标注数据的 COCO api 
    coco=COCO(annFile)

    imgIds = coco.getImgIds(imgIds = [549220])
    # 获取到image结构体信息
    image = coco.loadImgs(imgIds[np.random.randint(0,len(imgIds))])[0]
    I = Image.open('%s/%s/%s'%(dataDir,dataType,image['file_name']))  
    I.save(image['file_name'])
    # 获取到annotation结构体信息
    annIds = coco.getAnnIds(imgIds=image['id'], iscrowd=0)
    annotation = coco.loadAnns(annIds[0])[0]
    # 将多边形或者RLE数据转换为mask
    mask = _poly2mask(annotation["segmentation"], img_h=image["height"], img_w=image['width'])    
    
    print(mask.shape, mask.dtype)
    # 保存mask
    save_mask(mask, "plot.png")
mask二值图像转RLE

基于上面的mask,转换为对应的RLE,步骤如下:

  1. 找到轮廓
  2. 将每个轮廓转换为RLE格式
def contours_to_rle(contours,  image_width, image_height):
    # 创建空白掩码图像
    mask_image = np.zeros((image_height, image_width), dtype=np.uint8)

    # 将每个轮廓绘制在掩码图像上
    cv2.drawContours(mask_image, contours, -1, 255, -1)

    # 将二进制掩码图像转换为 RLE 格式
    rle_encoding = maskUtils.encode(np.asfortranarray(mask_image))

    rle_encoding['counts'] = rle_encoding['counts'].decode('utf-8')
    # 返回 RLE 格式的分割信息
    return rle_encoding
# mask为上述转换RLE得到的
contours, hierarchy = cv2.findContours(mask * 255, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = [c for c in contours if cv2.contourArea(c)>100]

for contour in contours:
    # 将mask转换为RLEs
    rles = contours_to_rle(contour, mask.shape[1], mask.shape[0])
    # 以下代码是为了验证生成的RLEs是否有问题
    mask = _poly2mask(annotation["segmentation"], img_h=image["height"], img_w=image['width'])
    # 通过对比生成的mask可以知道不存在问题
    save_mask(mask, "plot.png")

categories部分

categories是一个包含多个category实例的数组,而category结构体描述如下:

{
    "id": int,
    "name": str,
    "supercategory": str,
}

从json标注文件中摘取两个category实例如下:

{
	"supercategory": "person",
	"id": 1,
	"name": "person"
},
{
	"supercategory": "vehicle",
	"id": 2,
	"name": "bicycle"
},

COCO2017数据集中,共有80个category。

object Keypoint类型的格式

person_keypoints_train2017.json和person_keypoints_val2017.json这两个文件是COCO2017数据中表示关键点的标注文件。在不同的JSON文件中info、licenses和images是共享的,不共享的是annotation和categories这两个字段。

annotations字段

这个annotation结构体中包含了目标检测实例分割任务annotation结构体的所有字段,再加上2个额外的字段。结构如下:

annotation{
    "keypoints": [x1,y1,v1,...],
    "num_keypoints": int,
    "id": int,
    "image_id": int,
    "category_id": int,
    "segmentation": RLE or [polygon],
    "area": float,
    "bbox": [x,y,width,height],
    "iscrowd": 0 or 1,
}

其中keypoints是一个长度为3×k的数组,其中k是category中keypoints的总数量,在COCO中,k=17。每个keypoint是一个长度为3的数组,第一个和第二个元素表示关键点的x和y坐标值,第三个元素是一个标注位v。当v=0时表示这个关键点没有标注,v=1时表示这个关键点标注了但是不可见(遮挡),v为2时表示这个关键点标注了且可见。
num_keypoints表示这个目标上被标注的关键点数量,其中比较小的目标上可能无法标注全部的关键点。示例如下:

{
	"segmentation": [[125.12,539.69,140.94,522.43...]],
	"num_keypoints": 10,
	"area": 47803.27955,
	"iscrowd": 0,
	"keypoints": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,142,309,1,177,320,2,191,398...],
	"image_id": 425226,"bbox": [73.35,206.02,300.58,372.5],"category_id": 1,
	"id": 183126
},
categories字段

COCO2017数据中categories字段中的元素数量为1,只有person一个。相比于检测和分割,keypoints的categories中新增了两个额外的元素。定义如下:

{
    "id": int,
    "name": str,
    "supercategory": str,
    "keypoints": [str],
    "skeleton": [edge]
}

其中keypoints元素是一个长度为k的数字,包含了每个关键点的名字;skeleton定义了哥哥关键点之间的的连接性(比如人的左手腕和左肘是连接的,但是左手腕和右手腕不是)。示例如下:

{
	"supercategory": "person",
	"id": 1,
	"name": "person",
	"keypoints": ["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"],
	"skeleton": [[16,14],[14,12],[17,15],[15,13],[12,13],[6,12],[7,13],[6,7],[6,8],[7,9],[8,10],[9,11],[2,3],[1,2],[1,3],[2,4],[3,5],[4,6],[5,7]]
}

COCO数据集的标注格式

COCOAPI_294">COCOAPI使用

coco安装

coco的安装方式非常简单,运行如下命令即可。

git clone https://github.com/pdollar/coco.git

cd coco/PythonAPI
# 如果使用的是 python2, 运行下面的命令:  
make -j8
# 如果使用的是 python3, 需要更改 Makefile:  
vi Makefile
# 将 Makefile 中的 python 改为 python3, 然后:
make -j8

coco使用

加载json文件,得到coco对象

  1. 导入相关的库
from pycocotools.coco import COCO
import numpy as np
import skimage.io as io
import matplotlib.pyplot as plt
import pylab
pylab.rcParams['figure.figsize'] = (8.0, 10.0)
  1. 构建coco对象

COCO是一个Microsoft COCO数据集的辅助类,用于读取和可视化标记文件。输入参数为JSON文件的路径

dataDir='/path/to/your/coco_data'
dataType='val2017'
annFile='{}/annotations/instances_{}.json'.format(dataDir,dataType)
# 初始化标注数据的 COCO api 
coco=COCO(annFile)

coco对象创建完成之后会输出如下信息:

loading annotations into memory...
Done (t=0.81s)
creating index...
index created!

COCO类包含如下的属性:

  • dataset:JSON文件加载之后读取的内容都包含在dataset属性中;
  • img_ann_map:表示从img ID到ann的映射关系,通过img ID得到图像中包含哪些ann;
  • cat_img_map:表示cat ID到img ID的映射关系,通过cat ID得到img ID,表示该图像中包含这些类别的数据;
  • anns,cats,imgs:通过ID能得到对应的annotation、category、image结构体内容;

提供如下的常见方法:

getCatIds(self, catNms=[], supNms=[], catIds=[])

基于给定的类别名,超类名或者类别ID来获取到类别ID列表。

getImgIds(self, imgIds=[], catIds=[])

基于给定的img ID或者cat ID来获取img ID列表。

def loadAnns(self, ids=[]):

基于给定的ann ID来获取对应的annotation结构体列表。

def loadCats(self, ids=[]):

基于给定的cat ID来获取category结构体列表。

def loadImgs(self, ids=[]):

基于给定的img ID来获取image结构体列表。

def showAnns(self, anns, draw_bbox=False):

显示标注信息。通过plt.gca()获取当前figure的axes(轴),并在该figure上渲染标注内容。

显示数据集中的类别名称和超类

# 获取COCO数据集所有的类别ID
class_ids = coco.getCatIds()
# 基于类别ID得到对应的category结构体内容
cats = coco.loadCats(class_ids)
# 分析category结构体中name属性
names = [cat['name'] for cat in cats]
print('COCO categories: \n{}\n'.format(' '.join(names)))

names = set([cat['supercategory'] for cat in cats])
print('COCO supercategories: \n{}'.format(' '.join(names)))

输出信息如下:

COCO categories: 
person bicycle car motorcycle airplane bus train truck boat traffic light fire hydrant stop sign parking meter bench bird cat dog horse sheep cow elephant bear zebra giraffe backpack umbrella handbag tie suitcase frisbee skis snowboard sports ball kite baseball bat baseball glove skateboard surfboard tennis racket bottle wine glass cup fork knife spoon bowl banana apple sandwich orange broccoli carrot hot dog pizza donut cake chair couch potted plant bed dining table toilet tv laptop mouse remote keyboard cell phone microwave oven toaster sink refrigerator book clock vase scissors teddy bear hair drier toothbrush

COCO supercategories: 
outdoor food indoor appliance sports person animal vehicle furniture accessory electronic kitchen

加载指定img-ID的图片并显示

下面是加载并显示指定image_id的例子。

# get all images containing given categories, select one at random
catIds = coco.getCatIds(catNms=['person','dog','skateboard'])
# 获取catIds对应的所有image_id
imgIds = coco.getImgIds(catIds=catIds )
# 输出[549220, 324158, 279278]
# 指定image_id
imgIds = coco.getImgIds(imgIds = [549220])
# loadImgs() 返回的是只有一个元素的列表, 使用[0]来访问这个元素
# 列表中的这个元素又是字典类型, 关键字有: ["license", "file_name", "coco_url", "height", "width", "date_captured", "id"]
img = coco.loadImgs(imgIds[np.random.randint(0,len(imgIds))])[0]

# 加载并显示图片,可以使用两种方式: 1) 加载本地图片, 2) 在线加载远程图片
# 1) 使用本地路径, 对应关键字 "file_name"
I = Image.open('%s/%s/%s'%(dataDir,dataType,img['file_name']))  

# 2) 使用 url, 对应关键字 "coco_url"
# I = io.imread(img['coco_url'])    
plt.axis('off')
plt.imshow(I)
plt.savefig("plot.png", bbox_inches='tight',pad_inches=0.0)

下面就是指定image_id对应的图像数据:
#pic_centerimage.png

加载segmentation标注信息并显示在图片上

下面这段代码的作用就是加载segmentation标注信息,并将其显示在图片上。

# 加载并显示标注信息
plt.imshow(I); plt.axis('off')
annIds = coco.getAnnIds(imgIds=img['id'], catIds=catIds, iscrowd=None)
anns = coco.loadAnns(annIds)
coco.showAnns(anns)
plt.savefig("plot.png", bbox_inches='tight',pad_inches=0.0)

输出效果如下:
image.png
在showAnns函数中会调用plt.gca()函数来获取当前“figure”对象,如果存在的话直接返回,不存在的话会新建一个”figure“对象,并将其轴返回。
(一) COCO Python API - 使用篇-CSDN博客


http://www.niftyadmin.cn/n/5103368.html

相关文章

年底将近,企业成本票空缺,如何筹划节税?

《梅梅谈税》专注于企业税务筹划!助力企业合理、合规、合法进行节税税收筹划! 随着年末的临近,许多企业开始面临一个严峻的问题:成本票空缺。这不仅会影响企业的财务状况,还可能引发税务风险。那么,企业应…

linux系统下,在vscode的命令行中调试python文件

首先参考vscode官网文档Command line debugging 步骤 1(只需一次):安装debugpy 步骤 2:在命令行中运行 python -m debugpy --listen 5678 --wait-for-client -m dir1.dir2.your_script 以上命令使用了端口5678,也可…

2023年中国婚恋交友服务行业发展趋势分析:数字化、智能化将成必然趋势[图]

婚恋交友服务提供商是指围绕适婚人群的婚恋或亲密关系需求,提供恋爱交友、相亲匹配及情感咨询等服务的企业。常见的模式主要分为两种:第一,由人脸识别、身份验证、属性分析公有云及大数据分析等信息技术为驱动的线上平台。第二,由…

增加3D模型表面粗糙度的方法

1、粗糙度在模型渲染中的作用 模型粗糙度是指在渲染过程中用于描述物体表面光滑程度的属性。它用来模拟物体表面的粗糙程度,从而影响光线在物体表面上的反射和散射效果。下面是对模型粗糙度的详细介绍: 表面光滑度和粗糙度:物体的表面可以是…

消息队列Beanstalkd介绍

摘要: Beanstalkd是一个高性能、轻量级的、分布式的、内存型的消息队列系统。最初设计的目的是想通过后台异步执行耗时的任务来降低高容量Web应用系统的页面访问延迟。其实Beanstalkd是典型的类Memcached设计,协议和使用方式都是同样的风格。其基本设计思…

【力扣每日一题】2023.10.19 同积元组

目录 题目: 示例: 分析: 代码: 题目: 示例: 分析: 题目比较简洁,给我们一个元素各不相同的数组,要我们找出该数组里能够组成 a*bc*d 的组合数目。 比较直观的做法是我们直接暴…

解决matlab报错“输入参数的数目不足”

报错语句:tanh((peakNums-parameter)/2) 报错提示:输入参数的数目不足 运行环境:matlab2021b 分析原因: 当执行peakNums - parameter时,如果peakNums和parameter都是向量,那么这并不一定意味着会得到对应…

小程序开发平台源码系统 +功能丰富 +有完整搭建教程

大家好啊,今天要给大家分享的这款系统可就厉害了。全新升级的小程序开发平台源码系统,其中包含了15项不同小程序功能,各行各业都有。一起来看看吧。以下是部分功能实现代码: 系统特色功能一览: 一、微同城本地生活服务…