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