_mask.pyx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. # distutils: language = c
  2. # distutils: sources = ../MatlabAPI/private/maskApi.c
  3. #**************************************************************************
  4. # Microsoft COCO Toolbox. version 2.0
  5. # Data, paper, and tutorials available at: http://mscoco.org/
  6. # Code written by Piotr Dollar and Tsung-Yi Lin, 2015.
  7. # Licensed under the Simplified BSD License [see coco/license.txt]
  8. #**************************************************************************
  9. __author__ = 'tsungyi'
  10. # import both Python-level and C-level symbols of Numpy
  11. # the API uses Numpy to interface C and Python
  12. import numpy as np
  13. cimport numpy as np
  14. from libc.stdlib cimport malloc, free
  15. # intialized Numpy. must do.
  16. np.import_array()
  17. # import numpy C function
  18. # we use PyArray_ENABLEFLAGS to make Numpy ndarray responsible to memoery management
  19. cdef extern from "numpy/arrayobject.h":
  20. void PyArray_ENABLEFLAGS(np.ndarray arr, int flags)
  21. # Declare the prototype of the C functions in MaskApi.h
  22. cdef extern from "maskApi.h":
  23. ctypedef unsigned int uint
  24. ctypedef unsigned long siz
  25. ctypedef unsigned char byte
  26. ctypedef double* BB
  27. ctypedef struct RLE:
  28. siz h,
  29. siz w,
  30. siz m,
  31. uint* cnts,
  32. void rlesInit( RLE **R, siz n )
  33. void rleEncode( RLE *R, const byte *M, siz h, siz w, siz n )
  34. void rleDecode( const RLE *R, byte *mask, siz n )
  35. void rleMerge( const RLE *R, RLE *M, siz n, bint intersect )
  36. void rleArea( const RLE *R, siz n, uint *a )
  37. void rleIou( RLE *dt, RLE *gt, siz m, siz n, byte *iscrowd, double *o )
  38. void bbIou( BB dt, BB gt, siz m, siz n, byte *iscrowd, double *o )
  39. void rleToBbox( const RLE *R, BB bb, siz n )
  40. void rleFrBbox( RLE *R, const BB bb, siz h, siz w, siz n )
  41. void rleFrPoly( RLE *R, const double *xy, siz k, siz h, siz w )
  42. char* rleToString( const RLE *R )
  43. void rleFrString( RLE *R, char *s, siz h, siz w )
  44. # python class to wrap RLE array in C
  45. # the class handles the memory allocation and deallocation
  46. cdef class RLEs:
  47. cdef RLE *_R
  48. cdef siz _n
  49. def __cinit__(self, siz n =0):
  50. rlesInit(&self._R, n)
  51. self._n = n
  52. # free the RLE array here
  53. def __dealloc__(self):
  54. if self._R is not NULL:
  55. for i in range(self._n):
  56. free(self._R[i].cnts)
  57. free(self._R)
  58. def __getattr__(self, key):
  59. if key == 'n':
  60. return self._n
  61. raise AttributeError(key)
  62. # python class to wrap Mask array in C
  63. # the class handles the memory allocation and deallocation
  64. cdef class Masks:
  65. cdef byte *_mask
  66. cdef siz _h
  67. cdef siz _w
  68. cdef siz _n
  69. def __cinit__(self, h, w, n):
  70. self._mask = <byte*> malloc(h*w*n* sizeof(byte))
  71. self._h = h
  72. self._w = w
  73. self._n = n
  74. # def __dealloc__(self):
  75. # the memory management of _mask has been passed to np.ndarray
  76. # it doesn't need to be freed here
  77. # called when passing into np.array() and return an np.ndarray in column-major order
  78. def __array__(self):
  79. cdef np.npy_intp shape[1]
  80. shape[0] = <np.npy_intp> self._h*self._w*self._n
  81. # Create a 1D array, and reshape it to fortran/Matlab column-major array
  82. ndarray = np.PyArray_SimpleNewFromData(1, shape, np.NPY_UINT8, self._mask).reshape((self._h, self._w, self._n), order='F')
  83. # The _mask allocated by Masks is now handled by ndarray
  84. PyArray_ENABLEFLAGS(ndarray, np.NPY_OWNDATA)
  85. return ndarray
  86. # internal conversion from Python RLEs object to compressed RLE format
  87. def _toString(RLEs Rs):
  88. cdef siz n = Rs.n
  89. cdef bytes py_string
  90. cdef char* c_string
  91. objs = []
  92. for i in range(n):
  93. c_string = rleToString( <RLE*> &Rs._R[i] )
  94. py_string = c_string
  95. objs.append({
  96. 'size': [Rs._R[i].h, Rs._R[i].w],
  97. 'counts': py_string
  98. })
  99. free(c_string)
  100. return objs
  101. # internal conversion from compressed RLE format to Python RLEs object
  102. def _frString(rleObjs):
  103. cdef siz n = len(rleObjs)
  104. Rs = RLEs(n)
  105. cdef bytes py_string
  106. cdef char* c_string
  107. for i, obj in enumerate(rleObjs):
  108. py_string = str(obj['counts'])
  109. c_string = py_string
  110. rleFrString( <RLE*> &Rs._R[i], <char*> c_string, obj['size'][0], obj['size'][1] )
  111. return Rs
  112. # encode mask to RLEs objects
  113. # list of RLE string can be generated by RLEs member function
  114. def encode(np.ndarray[np.uint8_t, ndim=3, mode='fortran'] mask):
  115. h, w, n = mask.shape[0], mask.shape[1], mask.shape[2]
  116. cdef RLEs Rs = RLEs(n)
  117. rleEncode(Rs._R,<byte*>mask.data,h,w,n)
  118. objs = _toString(Rs)
  119. return objs
  120. # decode mask from compressed list of RLE string or RLEs object
  121. def decode(rleObjs):
  122. cdef RLEs Rs = _frString(rleObjs)
  123. h, w, n = Rs._R[0].h, Rs._R[0].w, Rs._n
  124. masks = Masks(h, w, n)
  125. rleDecode( <RLE*>Rs._R, masks._mask, n );
  126. return np.array(masks)
  127. def merge(rleObjs, bint intersect=0):
  128. cdef RLEs Rs = _frString(rleObjs)
  129. cdef RLEs R = RLEs(1)
  130. rleMerge(<RLE*>Rs._R, <RLE*> R._R, <siz> Rs._n, intersect)
  131. obj = _toString(R)[0]
  132. return obj
  133. def area(rleObjs):
  134. cdef RLEs Rs = _frString(rleObjs)
  135. cdef uint* _a = <uint*> malloc(Rs._n* sizeof(uint))
  136. rleArea(Rs._R, Rs._n, _a)
  137. cdef np.npy_intp shape[1]
  138. shape[0] = <np.npy_intp> Rs._n
  139. a = np.array((Rs._n, ), dtype=np.uint8)
  140. a = np.PyArray_SimpleNewFromData(1, shape, np.NPY_UINT32, _a)
  141. PyArray_ENABLEFLAGS(a, np.NPY_OWNDATA)
  142. return a
  143. # iou computation. support function overload (RLEs-RLEs and bbox-bbox).
  144. def iou( dt, gt, pyiscrowd ):
  145. def _preproc(objs):
  146. if len(objs) == 0:
  147. return objs
  148. if type(objs) == np.ndarray:
  149. if len(objs.shape) == 1:
  150. objs = objs.reshape((objs[0], 1))
  151. # check if it's Nx4 bbox
  152. if not len(objs.shape) == 2 or not objs.shape[1] == 4:
  153. raise Exception('numpy ndarray input is only for *bounding boxes* and should have Nx4 dimension')
  154. objs = objs.astype(np.double)
  155. elif type(objs) == list:
  156. # check if list is in box format and convert it to np.ndarray
  157. isbox = np.all(np.array([(len(obj)==4) and ((type(obj)==list) or (type(obj)==np.ndarray)) for obj in objs]))
  158. isrle = np.all(np.array([type(obj) == dict for obj in objs]))
  159. if isbox:
  160. objs = np.array(objs, dtype=np.double)
  161. if len(objs.shape) == 1:
  162. objs = objs.reshape((1,objs.shape[0]))
  163. elif isrle:
  164. objs = _frString(objs)
  165. else:
  166. raise Exception('list input can be bounding box (Nx4) or RLEs ([RLE])')
  167. else:
  168. raise Exception('unrecognized type. The following type: RLEs (rle), np.ndarray (box), and list (box) are supported.')
  169. return objs
  170. def _rleIou(RLEs dt, RLEs gt, np.ndarray[np.uint8_t, ndim=1] iscrowd, siz m, siz n, np.ndarray[np.double_t, ndim=1] _iou):
  171. rleIou( <RLE*> dt._R, <RLE*> gt._R, m, n, <byte*> iscrowd.data, <double*> _iou.data )
  172. def _bbIou(np.ndarray[np.double_t, ndim=2] dt, np.ndarray[np.double_t, ndim=2] gt, np.ndarray[np.uint8_t, ndim=1] iscrowd, siz m, siz n, np.ndarray[np.double_t, ndim=1] _iou):
  173. bbIou( <BB> dt.data, <BB> gt.data, m, n, <byte*> iscrowd.data, <double*>_iou.data )
  174. def _len(obj):
  175. cdef siz N = 0
  176. if type(obj) == RLEs:
  177. N = obj.n
  178. elif len(obj)==0:
  179. pass
  180. elif type(obj) == np.ndarray:
  181. N = obj.shape[0]
  182. return N
  183. # convert iscrowd to numpy array
  184. cdef np.ndarray[np.uint8_t, ndim=1] iscrowd = np.array(pyiscrowd, dtype=np.uint8)
  185. # simple type checking
  186. cdef siz m, n
  187. dt = _preproc(dt)
  188. gt = _preproc(gt)
  189. m = _len(dt)
  190. n = _len(gt)
  191. if m == 0 or n == 0:
  192. return []
  193. if not type(dt) == type(gt):
  194. raise Exception('The dt and gt should have the same data type, either RLEs, list or np.ndarray')
  195. # define local variables
  196. cdef double* _iou = <double*> 0
  197. cdef np.npy_intp shape[1]
  198. # check type and assign iou function
  199. if type(dt) == RLEs:
  200. _iouFun = _rleIou
  201. elif type(dt) == np.ndarray:
  202. _iouFun = _bbIou
  203. else:
  204. raise Exception('input data type not allowed.')
  205. _iou = <double*> malloc(m*n* sizeof(double))
  206. iou = np.zeros((m*n, ), dtype=np.double)
  207. shape[0] = <np.npy_intp> m*n
  208. iou = np.PyArray_SimpleNewFromData(1, shape, np.NPY_DOUBLE, _iou)
  209. PyArray_ENABLEFLAGS(iou, np.NPY_OWNDATA)
  210. _iouFun(dt, gt, iscrowd, m, n, iou)
  211. return iou.reshape((m,n), order='F')
  212. def toBbox( rleObjs ):
  213. cdef RLEs Rs = _frString(rleObjs)
  214. cdef siz n = Rs.n
  215. cdef BB _bb = <BB> malloc(4*n* sizeof(double))
  216. rleToBbox( <const RLE*> Rs._R, _bb, n )
  217. cdef np.npy_intp shape[1]
  218. shape[0] = <np.npy_intp> 4*n
  219. bb = np.array((1,4*n), dtype=np.double)
  220. bb = np.PyArray_SimpleNewFromData(1, shape, np.NPY_DOUBLE, _bb).reshape((n, 4))
  221. PyArray_ENABLEFLAGS(bb, np.NPY_OWNDATA)
  222. return bb
  223. def frBbox(np.ndarray[np.double_t, ndim=2] bb, siz h, siz w ):
  224. cdef siz n = bb.shape[0]
  225. Rs = RLEs(n)
  226. rleFrBbox( <RLE*> Rs._R, <const BB> bb.data, h, w, n )
  227. objs = _toString(Rs)
  228. return objs
  229. def frPoly( poly, siz h, siz w ):
  230. cdef np.ndarray[np.double_t, ndim=1] np_poly
  231. n = len(poly)
  232. Rs = RLEs(n)
  233. for i, p in enumerate(poly):
  234. np_poly = np.array(p, dtype=np.double, order='F')
  235. rleFrPoly( <RLE*>&Rs._R[i], <const double*> np_poly.data, len(np_poly)/2, h, w )
  236. objs = _toString(Rs)
  237. return objs
  238. def frUncompressedRLE(ucRles, siz h, siz w):
  239. cdef np.ndarray[np.uint32_t, ndim=1] cnts
  240. cdef RLE R
  241. cdef uint *data
  242. n = len(ucRles)
  243. objs = []
  244. for i in range(n):
  245. Rs = RLEs(1)
  246. cnts = np.array(ucRles[i]['counts'], dtype=np.uint32)
  247. # time for malloc can be saved here but it's fine
  248. data = <uint*> malloc(len(cnts)* sizeof(uint))
  249. for j in range(len(cnts)):
  250. data[j] = <uint> cnts[j]
  251. R = RLE(ucRles[i]['size'][0], ucRles[i]['size'][1], len(cnts), <uint*> data)
  252. Rs._R[0] = R
  253. objs.append(_toString(Rs)[0])
  254. return objs
  255. def frPyObjects(pyobj, siz h, w):
  256. if type(pyobj) == np.ndarray:
  257. objs = frBbox(pyobj, h, w )
  258. elif type(pyobj) == list and len(pyobj[0]) == 4:
  259. objs = frBbox(pyobj, h, w )
  260. elif type(pyobj) == list and len(pyobj[0]) > 4:
  261. objs = frPoly(pyobj, h, w )
  262. elif type(pyobj) == list and type(pyobj[0]) == dict:
  263. objs = frUncompressedRLE(pyobj, h, w)
  264. else:
  265. raise Exception('input type is not supported.')
  266. return objs