小白填坑系列(4) cocoapi

0. 前言

  • 一直都在用 COCO 数据集,但对这个API了解不够深入。
  • 现在搞完了,感觉使用已经差不多了。
  • 如果说还有什么要深入的话,那就是Mask的具体使用,以及COCOEval的基本原理了。

1. 安装

  • Linux
    • pip install cython; pip install 'git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI'
  • Windows
    • Gihub: cocoapi windows version
    • 在PythonAPI文件夹中运行 python setup.py build_ext install 即可。
    • 可能会报错 error: Unable to find vcvarsall.bat
      • 解决:在这里下载 Visual C++ Build Tools 2015 ,在win10下安装。
      • image_1dsk6pian1osjtic1mq31ru2150r9.png-31.7kB

2. COCO 类

  • 位于 coco.py 中。
  • 作用:整合一系列图像以及对应的标注结果。

2.1. 初始化

  • 初始化该类需要导入配置文件。COCO中配置文件主要分为三类:
    • 用于实例分割与物体检测的instances_{}.json
    • 用于关键点识别的person_keypoints_{}.json
    • 提供简单介绍图像的文字captions_{}.json
  • 代码基本流程:
    • 第一步:读取json文件,作为self.dataset实例。
    • 第二步:根据self.dataset对象,获取五个成员变量,下面分别介绍
      • imgs:key为数字id,value为字典,2.2. 基本方法中会介绍该字典。
      • cats:key为数字id,value为字典,2.2. 基本方法中会介绍该字典。
      • anns:key为数字id,value为字典,2.2. 基本方法中会介绍该字典。
      • imgToAnns:key为数字id,value为list,list中每个元素是个字典,该字典内容与anns一致。
      • catToImgs:key为数字id,value为list,list中每个元素是个字典,该字典内容与imgs一致。
      • 这一步主要就是通过 createIndex 方法实现。

2.2. 基本方法

  • get系列:获取各种id。

    • def getCatIds(self, catNms=[], supNms=[], catIds=[])
      • 通过分类名称、父类名称、分类编号获取分类编号。
      • 三个条件要同时成立。
    • def getImgIds(self, imgIds=[], catIds=[])
      • 通过图片编号与分类编号获取图片编号。
      • 注意,如果有多个类型编号,则要求一张图片中包含所有指定的类型。
    • def getAnnIds(self, imgIds=[], catIds=[], areaRng=[], iscrowd=None)
      • 通过图片编号、分类编号、面积范围、crowd记号来获取对应的标签。
      • 所有条件必须同时满足。
  • load系列:根据id获取各种实际数据。

    • 共有 loadCats, loadAnns, loadImgs 三个方法,结果举例如下。
    • 注意,anno中对应的 segmentation 有两种形式
      • 当iscrowd为0时,是polygon形式。
      • 当iscrowd为1时,是rle形式。
    • 注意,anno中对应的 keypoints 表示17个关键点信息,用以为数组表示。
      • 该数组可以转换为 [17, 3],其中三个数字分别是横坐标、纵坐标、关键点可见性(0或1)。
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        # instances: loadCats
        {'supercategory': 'person', 'id': 1, 'name': 'person'}

        # keypoints: loadCats
        {
        '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': [...], # 关键点连接性,编号从1开始
        }


        # loadAnns
        {
        'segmentation': ...,
        'area': 702.1057499999998,
        'iscrowd': 0,
        'image_id': 289343,
        'bbox': [473.07, 395.93, 38.65, 28.67],
        'keypoints': [...],
        'category_id': 18,
        'id': 1768
        }

        # loadImgs
        {
        'license': 4,
        'file_name': '000000397133.jpg',
        'coco_url': 'http://images.cocodataset.org/val2017/000000397133.jpg',
        'height': 427,
        'width': 640,
        'date_captured': '2013-11-14 17:02:52',
        'flickr_url': 'http://farm7.staticflickr.com/6116/6255196340_da26cf2c9e_z.jpg',
        'id': 397133
        }
  • mask转换方法

    • annToMask:将ann中的segmentation转换为 binary mask形式。
    • annToRLE:将ann中的segmentation转换为RLE形式。

2.3. 其他方法

  • info:输出数据集基本信息,参考结果如下。

    1
    2
    3
    4
    5
    6
    description: COCO 2017 Dataset
    url: http://cocodataset.org
    version: 1.0
    year: 2017
    contributor: COCO Consortium
    date_created: 2017/09/01
  • download:下载数据集,我也用不上。

  • def showAnns(self, anns)

    • 在plt上展示注释结果,可以用来展示mask和关键点,好像不能用来展示bbox。
    • 这个需要之前先用 plt.imshow() 展示原始图片。
    • 调用了 plt.gca() 方法,好像意思是会自动寻找plt.figure
  • loadRes

    • 将JSON形式的结果转换为COCO对象。
    • 好像可以用在后续的评估方法中。
  • loadNumpyAnnotations

    • 将一个 [Nx7]ndarray 对象转换为 list[dict] 形式。
    • 其中每个字典包括 image_id, bbox, score, category_id 四个属性。
    • [Nx7] 中每一行的形式为 {imageID,x1,y1,w,h,score,class}

2.4. 注意事项

  • 在源码中会设置 import matplotlib; matplotlib.use('Agg'),就算直接只Python源码也没用,可能需要修改源码后重新重新安装才行。

3. COCOeval

  • 位于 cocoeval.py 中。
  • 作用:用于性能指标评估。

3.1. 初始化

  • 函数形式:def __init__(self, cocoGt=None, cocoDt=None, iouType='segm')
  • 构造主要通过两个COCO对象以及比较结果来定义
    • 两个COCO对象分别表示Ground Truth以及预测结果。
    • iouType 有三个选择,分别是'segm', 'bbox', 'keypoints'

3.2. 基本使用

  • 基本流程

    1
    2
    3
    4
    5
    6
    cocoGt=..., cocoDt=...       # load dataset and results
    E = CocoEval(cocoGt,cocoDt); # initialize CocoEval object
    E.params.recThrs = ...; # set parameters as desired
    E.evaluate(); # run per image evaluation
    E.accumulate(); # accumulate per image results
    E.summarize(); # display summary metrics of results
  • E.params 中可以设置的参数包括

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # The evaluation parameters are as follows (defaults in brackets):
    # imgIds - [all] N img ids to use for evaluation
    # catIds - [all] K cat ids to use for evaluation
    # iouThrs - [.5:.05:.95] T=10 IoU thresholds for evaluation
    # recThrs - [0:.01:1] R=101 recall thresholds for evaluation
    # areaRng - [...] A=4 object area ranges for evaluation
    # maxDets - [1 10 100] M=3 thresholds on max detections per image
    # iouType - ['segm'] set iouType to 'segm', 'bbox' or 'keypoints'
    # iouType replaced the now DEPRECATED useSegm parameter.
    # useCats - [1] if true use category labels for evaluation
    # Note: if useCats=0 category labels are ignored as in proposal scoring.
    # Note: multiple areaRngs [Ax2] and maxDets [Mx1] can be specified.

4. 举例

  • 两个官方实例放出来,也没注释,看看就看懂了。

4.1. pycocoDemo

  • 好像也没啥要说的。
    • 一般流程是根据catIds获取imgIds,最后获取annIds,然后分别通过load方法导入信息。
    • 其实可以活用 imgToAnns catToImgs 这两个成员变量。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      %matplotlib inline
      # from __future__ import print_function # 为了在3.6中正常运行,好像需要注释
      from pycocotools.coco import COCO
      import os, sys, zipfile
      import urllib.request
      import shutil
      import numpy as np
      import skimage.io as io
      import matplotlib.pyplot as plt
      import pylab
      pylab.rcParams['figure.figsize'] = (8.0, 10.0)


      # Record package versions for reproducibility
      print("os: %s" % os.name)
      print("sys: %s" % sys.version)
      print("numpy: %s, %s" % (np.__version__, np.__file__))


      # Setup data paths
      dataDir = '../..'
      dataType = 'val2017'
      annDir = '{}/annotations'.format(dataDir)
      annZipFile = '{}/annotations_train{}.zip'.format(dataDir, dataType)
      annFile = '{}/instances_{}.json'.format(annDir, dataType)
      annURL = 'http://images.cocodataset.org/annotations/annotations_train{}.zip'.format(dataType)
      print (annDir)
      print (annFile)
      print (annZipFile)
      print (annURL)


      # Download data if not available locally
      if not os.path.exists(annDir):
      os.makedirs(annDir)
      if not os.path.exists(annFile):
      if not os.path.exists(annZipFile):
      print ("Downloading zipped annotations to " + annZipFile + " ...")
      with urllib.request.urlopen(annURL) as resp, open(annZipFile, 'wb') as out:
      shutil.copyfileobj(resp, out)
      print ("... done downloading.")
      print ("Unzipping " + annZipFile)
      with zipfile.ZipFile(annZipFile,"r") as zip_ref:
      zip_ref.extractall(dataDir)
      print ("... done unzipping")
      print ("Will use annotations in " + annFile)


      # initialize COCO api for instance annotations
      coco=COCO(annFile)

      # display COCO categories and supercategories
      cats = coco.loadCats(coco.getCatIds())
      nms=[cat['name'] for cat in cats]
      print('COCO categories: \n{}\n'.format(' '.join(nms)))

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

      # get all images containing given categories, select one at random
      catIds = coco.getCatIds(catNms=['person','dog','skateboard']);
      imgIds = coco.getImgIds(catIds=catIds );
      imgIds = coco.getImgIds(imgIds = [324158])
      img = coco.loadImgs(imgIds[np.random.randint(0,len(imgIds))])[0]

      # load and display image
      I = io.imread('%s/images/%s/%s'%(dataDir,dataType,img['file_name']))
      # use url to load image
      # I = io.imread(img['coco_url'])
      plt.axis('off')
      plt.imshow(I)
      plt.show()

      # load and display instance annotations
      plt.imshow(I); plt.axis('off')
      annIds = coco.getAnnIds(imgIds=img['id'], catIds=catIds, iscrowd=None)
      anns = coco.loadAnns(annIds)
      coco.showAnns(anns)

      # initialize COCO api for person keypoints annotations
      annFile = '{}/annotations/person_keypoints_{}.json'.format(dataDir,dataType)
      coco_kps=COCO(annFile)

      # load and display keypoints annotations
      plt.imshow(I); plt.axis('off')
      ax = plt.gca()
      annIds = coco_kps.getAnnIds(imgIds=img['id'], catIds=catIds, iscrowd=None)
      anns = coco_kps.loadAnns(annIds)
      coco_kps.showAnns(anns)

      # initialize COCO api for caption annotations
      annFile = '{}/annotations/captions_{}.json'.format(dataDir,dataType)
      coco_caps=COCO(annFile)

      # load and display caption annotations
      annIds = coco_caps.getAnnIds(imgIds=img['id']);
      anns = coco_caps.loadAnns(annIds)
      coco_caps.showAnns(anns)
      plt.imshow(I); plt.axis('off'); plt.show()

4.2. pycocoEvalDemo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
%matplotlib inline
import matplotlib.pyplot as plt
from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval
import os, sys, zipfile
import urllib.request
import shutil
import numpy as np
import skimage.io as io
import pylab
pylab.rcParams['figure.figsize'] = (10.0, 8.0)

# Record package versions for reproducibility
print("os: %s" % os.name)
print("sys: %s" % sys.version)
print("numpy: %s, %s" % (np.__version__, np.__file__))

annType = ['segm','bbox','keypoints']
annType = annType[1] #specify type here
prefix = 'person_keypoints' if annType=='keypoints' else 'instances'
print('Running demo for *%s* results.'%(annType))

# Setup data paths
dataDir = '../..'
dataType = 'val2014'
annDir = '{}/annotations'.format(dataDir)
annZipFile = '{}/annotations_train{}.zip'.format(dataDir, dataType)
annFile = '{}/instances_{}.json'.format(annDir, dataType)
annURL = 'http://images.cocodataset.org/annotations/annotations_train{}.zip'.format(dataType)
print (annDir)
print (annFile)
print (annZipFile)
print (annURL)

# Download data if not available locally
if not os.path.exists(annDir):
os.makedirs(annDir)
if not os.path.exists(annFile):
if not os.path.exists(annZipFile):
print ("Downloading zipped annotations to " + annZipFile + " ...")
with urllib.request.urlopen(annURL) as resp, open(annZipFile, 'wb') as out:
shutil.copyfileobj(resp, out)
print ("... done downloading.")
print ("Unzipping " + annZipFile)
with zipfile.ZipFile(annZipFile,"r") as zip_ref:
zip_ref.extractall(dataDir)
print ("... done unzipping")
print ("Will use annotations in " + annFile)

#initialize COCO ground truth api
cocoGt=COCO(annFile)

#initialize COCO detections api
resFile='%s/results/%s_%s_fake%s100_results.json'
resFile = resFile%(dataDir, prefix, dataType, annType)
cocoDt=cocoGt.loadRes(resFile)

imgIds=sorted(cocoGt.getImgIds())
imgIds=imgIds[0:100]
imgId = imgIds[np.random.randint(100)]

# running evaluation
cocoEval = COCOeval(cocoGt,cocoDt,annType)
cocoEval.params.imgIds = imgIds
cocoEval.evaluate()
cocoEval.accumulate()
cocoEval.summarize()