1. 程式人生 > >COCO資料集的標註格式

COCO資料集的標註格式

COCO的 全稱是Common Objects in COntext,是微軟團隊提供的一個可以用來進行影象識別的資料集。MS COCO資料集中的影象分為訓練、驗證和測試集。COCO通過在Flickr上搜索80個物件類別和各種場景型別來收集影象,其使用了亞馬遜的Mechanical Turk(AMT)。

比如標註image captions(看圖說話)這種型別的步驟如下:

(AMT上COCO標註步驟)

COCO通過大量使用Amazon Mechanical Turk來收集資料。COCO資料集現在有3種標註型別:object instances(目標例項), object keypoints(目標上的關鍵點), 和image captions(看圖說話)

,使用JSON檔案儲存。比如下面就是Gemfield下載的COCO 2017年訓練集中的標註檔案:

可以看到其中有上面所述的三種類型,每種型別又包含了訓練和驗證,所以共6個JSON檔案。

基本的JSON結構體型別

object instances(目標例項)、object keypoints(目標上的關鍵點)、image captions(看圖說話)這3種類型共享這些基本型別:info、image、license。

而annotation型別則呈現出了多型:

{
    "info": info,
    "licenses": [license
], "images": [image], "annotations": [annotation], } info{ "year": int, "version": str, "description": str, "contributor": str, "url": str, "date_created": datetime, } license{ "id": int, "name": str, "url": str, } image{ "id": int, "width": int,
"height": int, "file_name": str, "license": int, "flickr_url": str, "coco_url": str, "date_captured": datetime, }

1,info型別,比如一個info型別的例項:

"info":{
    "description":"This is stable 1.0 version of the 2014 MS COCO dataset.",
    "url":"http:\/\/mscoco.org",
    "version":"1.0","year":2014,
    "contributor":"Microsoft COCO group",
    "date_created":"2015-01-27 09:11:52.357475"
},

2,Images是包含多個image例項的陣列,對於一個image型別的例項:

{
    "license":3,
    "file_name":"COCO_val2014_000000391895.jpg",
    "coco_url":"http:\/\/mscoco.org\/images\/391895",
    "height":360,"width":640,"date_captured":"2013-11-14 11:18:45",
    "flickr_url":"http:\/\/farm9.staticflickr.com\/8186\/8119368305_4e622c8349_z.jpg",
    "id":391895
},

3,licenses是包含多個license例項的陣列,對於一個license型別的例項:

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

Object Instance 型別的標註格式

1,整體JSON檔案格式

比如上圖中的instances_train2017.json、instances_val2017.json這兩個檔案就是這種格式。

Object Instance這種格式的檔案從頭至尾按照順序分為以下段落:

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

是的,你開啟這兩個檔案,雖然內容很多,但從檔案開始到結尾按照順序就是這5段。其中,info、licenses、images這三個結構體/型別 在上一節中已經說了,在不同的JSON檔案中這三個型別是一樣的,定義是共享的。不共享的是annotation和category這兩種結構體,他們在不同型別的JSON檔案中是不一樣的。

images陣列元素的數量等同於劃入訓練集(或者測試集)的圖片的數量;

annotations陣列元素的數量等同於訓練集(或者測試集)中bounding box的數量;

categories陣列元素的數量為80(2017年);

>>> ann_train_file='annotations/instances_train2017.json'
>>> coco_train = COCO(ann_train_file)
loading annotations into memory...
Done (t=19.30s)
creating index...
index created!

>>> len(coco_train.dataset['categories'])
80
>>> len(coco_train.dataset['images'])
118287
>>> len(coco_train.dataset['annotations'])
860001
>>>

2,annotations欄位

annotations欄位是包含多個annotation例項的一個數組,annotation型別本身又包含了一系列的欄位,如這個目標的category id和segmentation mask。segmentation格式取決於這個例項是一個單個的物件(即iscrowd=0,將使用polygons格式)還是一組物件(即iscrowd=1,將使用RLE格式)。如下所示:

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

注意,單個的物件(iscrowd=0)可能需要多個polygon來表示,比如這個物件在影象中被擋住了。而iscrowd=1時(將標註一組物件,比如一群人)的segmentation使用的就是RLE格式。

注意啊,只要是iscrowd=0那麼segmentation就是polygon格式;只要iscrowd=1那麼segmentation就是RLE格式。另外,每個物件(不管是iscrowd=0還是iscrowd=1)都會有一個矩形框bbox ,矩形框左上角的座標和矩形框的長寬會以陣列的形式提供,陣列第一個元素就是左上角的橫座標值。

area是area of encoded masks,是標註區域的面積。如果是矩形框,那就是高乘寬;如果是polygon或者RLE,那就複雜點。

最後,annotation結構中的categories欄位儲存的是當前物件所屬的category的id,以及所屬的supercategory的name。

下面是從instances_val2017.json檔案中摘出的一個annotation的例項,這裡的segmentation就是polygon格式:

{
    "segmentation": [[510.66,423.01,511.72,420.03,510.45......]],
    "area": 702.1057499999998,
    "iscrowd": 0,
    "image_id": 289343,
    "bbox": [473.07,395.93,38.65,28.67],
    "category_id": 18,
    "id": 1768
},

polygon格式比較簡單,這些數按照相鄰的順序兩兩組成一個點的xy座標,如果有n個數(必定是偶數),那麼就是n/2個點座標。下面就是一段解析polygon格式的segmentation並且顯示多邊形的示例程式碼:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
fig, ax = plt.subplots()
polygons = []
num_sides = 100
gemfield_polygons = [[125.12, 539.69, 140.94, 522.43......]]
gemfield_polygon = gemfield_polygons[0]
max_value = max(gemfield_polygon) * 1.3
gemfield_polygon = [i * 1.0/max_value for i in gemfield_polygon]
poly = np.array(gemfield_polygon).reshape((int(len(gemfield_polygon)/2), 2))
polygons.append(Polygon(poly,True))
p = PatchCollection(polygons, cmap=matplotlib.cm.jet, alpha=0.4)
colors = 100*np.random.rand(1)
p.set_array(np.array(colors))

ax.add_collection(p)
plt.show()

如果iscrowd=1,那麼segmentation就是RLE格式(segmentation欄位會含有counts和size陣列),在json檔案中gemfield挑出一個這樣的例子,如下所示:

segmentation : 
{
    u'counts': [272, 2, 4, 4, 4, 4, 2, 9, 1, 2, 16, 43, 143, 24......], 
    u'size': [240, 320]
}

COCO資料集的RLE都是uncompressed RLE格式(與之相對的是compact RLE)。 RLE所佔位元組的大小和邊界上的畫素數量是正相關的。RLE格式帶來的好處就是當基於RLE去計算目標區域的面積以及兩個目標之間的unoin和intersection時會非常有效率。 上面的segmentation中的counts陣列和size陣列共同組成了這幅圖片中的分割 mask。其中size是這幅圖片的寬高,然後在這幅影象中,每一個畫素點要麼在被分割(標註)的目標區域中,要麼在背景中。很明顯這是一個bool量:如果該畫素在目標區域中為true那麼在背景中就是False;如果該畫素在目標區域中為1那麼在背景中就是0。對於一個240x320的圖片來說,一共有76800個畫素點,根據每一個畫素點在不在目標區域中,我們就有了76800個bit,比如像這樣(隨便寫的例子,和上文的陣列沒關係):00000111100111110…;但是這樣寫很明顯浪費空間,我們直接寫上0或者1的個數不就行了嘛(Run-length encoding),於是就成了54251…,這就是上文中的counts陣列。下面這個python程式碼片段直觀的顯示了這些bit:

rle = [272, 2, 4, 4, 4, 4, 2, 9, 1, 2, 16, 43, 143, 24, 5, 8......]
assert sum(rle) == 240*320

也可以使用下面的程式碼將這個rle陣列表示的分割區域畫出來:

import numpy as np
import matplotlib.pyplot as plt
rle = [272, 2, 4, 4, 4, 4, 2, 9, 1, 2, 16, 43, 143, 24, 5, 8......]
assert sum(rle) == 240*320
M = np.zeros(240*320)
N = len(rle)
n = 0
val = 1
for pos in range(N):
    val = not val
    for c in range(rle[pos]):
        M[n] = val
        n += 1

GEMFIELD = M.reshape(([240, 320]), order='F')
plt.imshow(GEMFIELD)
plt.show()

3,categories欄位

categories是一個包含多個category例項的陣列,而category結構體描述如下:

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

從instances_val2017.json檔案中摘出的2個category例項如下所示:

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

至2017年的時候,一共有80個category。

Object Keypoint 型別的標註格式

1,整體JSON檔案格式

比如上圖中的person_keypoints_train2017.json、person_keypoints_val2017.json這兩個檔案就是這種格式。

Object Keypoint這種格式的檔案從頭至尾按照順序分為以下段落,看起來和Object Instance一樣啊:

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

是的,你開啟這兩個檔案,雖然內容很多,但從檔案開始到結尾按照順序就是這5段。其中,info、licenses、images這三個結構體/型別 在第一節中已經說了,在不同的JSON檔案中這三個型別是一樣的,定義是共享的。不共享的是annotation和category這兩種結構體,他們在不同型別的JSON檔案中是不一樣的。

images陣列元素數量是劃入訓練集(測試集)的圖片的數量;

annotations是bounding box的數量,在這裡只有人這個類別的bounding box;

categories陣列元素的數量為1,只有一個:person(2017年);

2,annotations欄位

這個型別中的annotation結構體包含了Object Instance中annotation結構體的所有欄位,再加上2個額外的欄位。

新增的keypoints是一個長度為3*k的陣列,其中k是category中keypoints的總數量。每一個keypoint是一個長度為3的陣列,第一和第二個元素分別是x和y座標值,第三個元素是個標誌位v,v為0時表示這個關鍵點沒有標註(這種情況下x=y=v=0),v為1時表示這個關鍵點標註了但是不可見(被遮擋了),v為2時表示這個關鍵點標註了同時也可見。

num_keypoints表示這個目標上被標註的關鍵點的數量(v>0),比較小的目標上可能就無法標註關鍵點。

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,
}

從person_keypoints_val2017.json檔案中摘出一個annotation的例項如下:

{
    "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
},

3,categories欄位

最後,對於每一個category結構體,相比Object Instance中的category新增了2個額外的欄位,keypoints是一個長度為k的陣列,包含了每個關鍵點的名字;skeleton定義了各個關鍵點之間的連線性(比如人的左手腕和左肘就是連線的,但是左手腕和右手腕就不是)。目前,COCO的keypoints只標註了person category (分類為人)。

定義如下:

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

從person_keypoints_val2017.json檔案中摘出一個category的例項如下:

{
    "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]]
}

Image Caption的標註格式

1,整體JSON檔案格式

比如上圖中的captions_train2017.json、captions_val2017.json這兩個檔案就是這種格式。

Image Caption這種格式的檔案從頭至尾按照順序分為以下段落,看起來和Object Instance一樣,不過沒有最後的categories欄位:

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

是的,你開啟這兩個檔案,雖然內容很多,但從檔案開始到結尾按照順序就是這4段。其中,info、licenses、images這三個結構體/型別 在第一節中已經說了,在不同的JSON檔案中這三個型別是一樣的,定義是共享的。不共享的是annotations這種結構體,它在不同型別的JSON檔案中是不一樣的。

images陣列的元素數量等於劃入訓練集(或者測試集)的圖片的數量;

annotations的數量要多於圖片的數量,這是因為一個圖片可以有多個場景描述;

2,annotations欄位

這個型別中的annotation用來儲存描述圖片的語句。每個語句描述了對應圖片的內容,而每個圖片至少有5個描述語句(有的圖片更多)。annotation定義如下:

annotation{
    "id": int,
    "image_id": int,
    "caption": str
}

從captions_val2017.json中摘取的一個annotation例項如下:

{
    "image_id": 179765,
    "id": 38,"caption": "A black Honda motorcycle parked in front of a garage."
}