Skip to content

Commit

Permalink
PythonAPI supports Python3 and update minor mask api update
Browse files Browse the repository at this point in the history
  • Loading branch information
tylin committed Dec 17, 2016
1 parent 0059559 commit 6fe62ae
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 121 deletions.
35 changes: 26 additions & 9 deletions PythonAPI/pycocotools/_mask.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

__author__ = 'tsungyi'

import sys
PYTHON_VERSION = sys.version_info[0]

# import both Python-level and C-level symbols of Numpy
# the API uses Numpy to interface C and Python
import numpy as np
Expand Down Expand Up @@ -38,7 +41,7 @@ cdef extern from "maskApi.h":
void rlesInit( RLE **R, siz n )
void rleEncode( RLE *R, const byte *M, siz h, siz w, siz n )
void rleDecode( const RLE *R, byte *mask, siz n )
void rleMerge( const RLE *R, RLE *M, siz n, bint intersect )
void rleMerge( const RLE *R, RLE *M, siz n, int intersect )
void rleArea( const RLE *R, siz n, uint *a )
void rleIou( RLE *dt, RLE *gt, siz m, siz n, byte *iscrowd, double *o )
void bbIou( BB dt, BB gt, siz m, siz n, byte *iscrowd, double *o )
Expand Down Expand Up @@ -119,7 +122,12 @@ def _frString(rleObjs):
cdef bytes py_string
cdef char* c_string
for i, obj in enumerate(rleObjs):
py_string = str(obj['counts']).encode('utf8')
if PYTHON_VERSION == 2:
py_string = str(obj['counts']).encode('utf8')
elif PYTHON_VERSION == 3:
py_string = str.encode(obj['counts']) if type(obj['counts']) == str else obj['counts']
else:
raise Exception('Python version must be 2 or 3')
c_string = py_string
rleFrString( <RLE*> &Rs._R[i], <char*> c_string, obj['size'][0], obj['size'][1] )
return Rs
Expand All @@ -138,10 +146,10 @@ def decode(rleObjs):
cdef RLEs Rs = _frString(rleObjs)
h, w, n = Rs._R[0].h, Rs._R[0].w, Rs._n
masks = Masks(h, w, n)
rleDecode( <RLE*>Rs._R, masks._mask, n );
rleDecode(<RLE*>Rs._R, masks._mask, n);
return np.array(masks)

def merge(rleObjs, bint intersect=0):
def merge(rleObjs, intersect=0):
cdef RLEs Rs = _frString(rleObjs)
cdef RLEs R = RLEs(1)
rleMerge(<RLE*>Rs._R, <RLE*> R._R, <siz> Rs._n, intersect)
Expand Down Expand Up @@ -277,15 +285,24 @@ def frUncompressedRLE(ucRles, siz h, siz w):
objs.append(_toString(Rs)[0])
return objs

def frPyObjects(pyobj, siz h, w):
def frPyObjects(pyobj, h, w):
# encode rle from a list of python objects
if type(pyobj) == np.ndarray:
objs = frBbox(pyobj, h, w )
objs = frBbox(pyobj, h, w)
elif type(pyobj) == list and len(pyobj[0]) == 4:
objs = frBbox(pyobj, h, w )
objs = frBbox(pyobj, h, w)
elif type(pyobj) == list and len(pyobj[0]) > 4:
objs = frPoly(pyobj, h, w )
elif type(pyobj) == list and type(pyobj[0]) == dict:
objs = frPoly(pyobj, h, w)
elif type(pyobj) == list and type(pyobj[0]) == dict \
and 'counts' in pyobj[0] and 'size' in pyobj[0]:
objs = frUncompressedRLE(pyobj, h, w)
# encode rle from single python object
elif type(pyobj) == list and len(pyobj) == 4:
objs = frBbox([pyobj], h, w)[0]
elif type(pyobj) == list and len(pyobj) > 4:
objs = frPoly([pyobj], h, w)[0]
elif type(pyobj) == dict and 'counts' in pyobj and 'size' in pyobj:
objs = frUncompressedRLE([pyobj], h, w)[0]
else:
raise Exception('input type is not supported.')
return objs
91 changes: 64 additions & 27 deletions PythonAPI/pycocotools/coco.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
# loadAnns - Load anns with the specified ids.
# loadCats - Load cats with the specified ids.
# loadImgs - Load imgs with the specified ids.
# segToMask - Convert polygon segmentation to binary mask.
# annToMask - Convert segmentation in an annotation to binary mask.
# showAnns - Display the specified annotations.
# loadRes - Load algorithm results and create API for accessing them.
# download - Download COCO images from mscoco.org server.
Expand All @@ -37,7 +37,7 @@
# See also COCO>decodeMask,
# COCO>encodeMask, COCO>getAnnIds, COCO>getCatIds,
# COCO>getImgIds, COCO>loadAnns, COCO>loadCats,
# COCO>loadImgs, COCO>segToMask, COCO>showAnns
# COCO>loadImgs, COCO>annToMask, COCO>showAnns

# Microsoft COCO Toolbox. version 2.0
# Data, paper, and tutorials available at: http://mscoco.org/
Expand All @@ -50,12 +50,17 @@
from matplotlib.collections import PatchCollection
from matplotlib.patches import Polygon
import numpy as np
import urllib
import copy
import itertools
import mask
import pycocotools.mask as maskUtils
import os
from collections import defaultdict
import sys
PYTHON_VERSION = sys.version_info[0]
if PYTHON_VERSION == 2:
from urllib import urlretrieve
elif PYTHON_VERSION == 3:
from urllib.request import urlretrieve

class COCO:
def __init__(self, annotation_file=None):
Expand All @@ -69,18 +74,18 @@ def __init__(self, annotation_file=None):
self.dataset,self.anns,self.cats,self.imgs = dict(),dict(),dict(),dict()
self.imgToAnns, self.catToImgs = defaultdict(list), defaultdict(list)
if not annotation_file == None:
print 'loading annotations into memory...'
print('loading annotations into memory...')
tic = time.time()
dataset = json.load(open(annotation_file, 'r'))
assert type(dataset)==dict, "annotation file format %s not supported"%(type(dataset))
print 'Done (t=%0.2fs)'%(time.time()- tic)
assert type(dataset)==dict, 'annotation file format {} not supported'.format(type(dataset))
print('Done (t={:0.2f}s)'.format(time.time()- tic))
self.dataset = dataset
self.createIndex()

def createIndex(self):
# create index
print 'creating index...'
anns,cats,imgs = dict(),dict(),dict()
print('creating index...')
anns, cats, imgs = {}, {}, {}
imgToAnns,catToImgs = defaultdict(list),defaultdict(list)
if 'annotations' in self.dataset:
for ann in self.dataset['annotations']:
Expand All @@ -94,10 +99,12 @@ def createIndex(self):
if 'categories' in self.dataset:
for cat in self.dataset['categories']:
cats[cat['id']] = cat

if 'annotations' in self.dataset and 'categories' in self.dataset:
for ann in self.dataset['annotations']:
catToImgs[ann['category_id']].append(ann['image_id'])

print 'index created!'
print('index created!')

# create class members
self.anns = anns
Expand All @@ -112,7 +119,7 @@ def info(self):
:return:
"""
for key, value in self.dataset['info'].items():
print '%s: %s'%(key, value)
print('{}: {}'.format(key, value))

def getAnnIds(self, imgIds=[], catIds=[], areaRng=[], iscrowd=None):
"""
Expand Down Expand Up @@ -231,7 +238,7 @@ def showAnns(self, anns):
elif 'caption' in anns[0]:
datasetType = 'captions'
else:
raise Exception("datasetType not supported")
raise Exception('datasetType not supported')
if datasetType == 'instances':
ax = plt.gca()
ax.set_autoscale_on(False)
Expand All @@ -243,17 +250,17 @@ def showAnns(self, anns):
if type(ann['segmentation']) == list:
# polygon
for seg in ann['segmentation']:
poly = np.array(seg).reshape((len(seg)/2, 2))
poly = np.array(seg).reshape((int(len(seg)/2), 2))
polygons.append(Polygon(poly))
color.append(c)
else:
# mask
t = self.imgs[ann['image_id']]
if type(ann['segmentation']['counts']) == list:
rle = mask.frPyObjects([ann['segmentation']], t['height'], t['width'])
rle = maskUtils.frPyObjects([ann['segmentation']], t['height'], t['width'])
else:
rle = [ann['segmentation']]
m = mask.decode(rle)
m = maskUtils.decode(rle)
img = np.ones( (m.shape[0], m.shape[1], 3) )
if ann['iscrowd'] == 1:
color_mask = np.array([2.0,166.0,101.0])/255
Expand All @@ -276,11 +283,11 @@ def showAnns(self, anns):
plt.plot(x[v>1], y[v>1],'o',markersize=8, markerfacecolor=c, markeredgecolor=c, markeredgewidth=2)
p = PatchCollection(polygons, facecolor=color, linewidths=0, alpha=0.4)
ax.add_collection(p)
p = PatchCollection(polygons, facecolor="none", edgecolors=color, linewidths=2)
p = PatchCollection(polygons, facecolor='none', edgecolors=color, linewidths=2)
ax.add_collection(p)
elif datasetType == 'captions':
for ann in anns:
print ann['caption']
print(ann['caption'])

def loadRes(self, resFile):
"""
Expand All @@ -291,7 +298,7 @@ def loadRes(self, resFile):
res = COCO()
res.dataset['images'] = [img for img in self.dataset['images']]

print 'Loading and preparing results... '
print('Loading and preparing results...')
tic = time.time()
if type(resFile) == str or type(resFile) == unicode:
anns = json.load(open(resFile))
Expand Down Expand Up @@ -322,9 +329,9 @@ def loadRes(self, resFile):
res.dataset['categories'] = copy.deepcopy(self.dataset['categories'])
for id, ann in enumerate(anns):
# now only support compressed RLE format as segmentation results
ann['area'] = mask.area([ann['segmentation']])[0]
ann['area'] = maskUtils.area(ann['segmentation'])
if not 'bbox' in ann:
ann['bbox'] = mask.toBbox([ann['segmentation']])[0]
ann['bbox'] = maskUtils.toBbox(ann['segmentation'])
ann['id'] = id+1
ann['iscrowd'] = 0
elif 'keypoints' in anns[0]:
Expand All @@ -337,21 +344,21 @@ def loadRes(self, resFile):
ann['area'] = (x1-x0)*(y1-y0)
ann['id'] = id + 1
ann['bbox'] = [x0,y0,x1-x0,y1-y0]
print 'DONE (t=%0.2fs)'%(time.time()- tic)
print('DONE (t={:0.2f}s)'.format(time.time()- tic))

res.dataset['annotations'] = anns
res.createIndex()
return res

def download( self, tarDir = None, imgIds = [] ):
def download(self, tarDir = None, imgIds = [] ):
'''
Download COCO images from mscoco.org server.
:param tarDir (str): COCO results directory name
imgIds (list): images to be downloaded
:return:
'''
if tarDir is None:
print 'Please specify target directory'
print('Please specify target directory')
return -1
if len(imgIds) == 0:
imgs = self.imgs.values()
Expand All @@ -364,28 +371,58 @@ def download( self, tarDir = None, imgIds = [] ):
tic = time.time()
fname = os.path.join(tarDir, img['file_name'])
if not os.path.exists(fname):
urllib.urlretrieve(img['coco_url'], fname)
print 'downloaded %d/%d images (t=%.1fs)'%(i, N, time.time()- tic)
urlretrieve(img['coco_url'], fname)
print('downloaded {}/{} images (t={:0.1f}s)'.format(i, N, time.time()- tic))

def loadNumpyAnnotations(self, data):
"""
Convert result data from a numpy array [Nx7] where each row contains {imageID,x1,y1,w,h,score,class}
:param data (numpy.ndarray)
:return: annotations (python nested list)
"""
print("Converting ndarray to lists...")
print('Converting ndarray to lists...')
assert(type(data) == np.ndarray)
print(data.shape)
assert(data.shape[1] == 7)
N = data.shape[0]
ann = []
for i in range(N):
if i % 1000000 == 0:
print("%d/%d" % (i,N))
print('{}/{}'.format(i,N))
ann += [{
'image_id' : int(data[i, 0]),
'bbox' : [ data[i, 1], data[i, 2], data[i, 3], data[i, 4] ],
'score' : data[i, 5],
'category_id': int(data[i, 6]),
}]
return ann

def annToRLE(self, ann):
"""
Convert annotation which can be polygons, uncompressed RLE to RLE.
:return: binary mask (numpy 2D array)
"""
t = self.imgs[ann['image_id']]
h, w = t['height'], t['width']
segm = ann['segmentation']
if type(segm) == list:
# polygon -- a single object might consist of multiple parts
# we merge all parts into one mask rle code
rles = maskUtils.frPyObjects(segm, h, w)
rle = maskUtils.merge(rles)
elif type(segm['counts']) == list:
# uncompressed RLE
rle = maskUtils.frPyObjects(segm, h, w)
else:
# rle
rle = ann['segmentation']
return rle

def annToMask(self, ann):
"""
Convert annotation which can be polygons, uncompressed RLE, or RLE to binary mask.
:return: binary mask (numpy 2D array)
"""
rle = self.annToRLE(ann)
m = maskUtils.decode(rle)
return m
Loading

0 comments on commit 6fe62ae

Please sign in to comment.