python-wml 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of python-wml might be problematic. Click here for more details.

Files changed (164) hide show
  1. python_wml-3.0.0.dist-info/LICENSE +23 -0
  2. python_wml-3.0.0.dist-info/METADATA +51 -0
  3. python_wml-3.0.0.dist-info/RECORD +164 -0
  4. python_wml-3.0.0.dist-info/WHEEL +5 -0
  5. python_wml-3.0.0.dist-info/top_level.txt +1 -0
  6. wml/__init__.py +0 -0
  7. wml/basic_data_def/__init__.py +2 -0
  8. wml/basic_data_def/detection_data_def.py +279 -0
  9. wml/basic_data_def/io_data_def.py +2 -0
  10. wml/basic_img_utils.py +816 -0
  11. wml/img_patch.py +92 -0
  12. wml/img_utils.py +571 -0
  13. wml/iotoolkit/__init__.py +17 -0
  14. wml/iotoolkit/aic_keypoint.py +115 -0
  15. wml/iotoolkit/baidu_mask_toolkit.py +244 -0
  16. wml/iotoolkit/base_dataset.py +210 -0
  17. wml/iotoolkit/bboxes_statistics.py +515 -0
  18. wml/iotoolkit/build.py +0 -0
  19. wml/iotoolkit/cityscapes_toolkit.py +183 -0
  20. wml/iotoolkit/classification_data_statistics.py +25 -0
  21. wml/iotoolkit/coco_data_fwd.py +225 -0
  22. wml/iotoolkit/coco_keypoints.py +118 -0
  23. wml/iotoolkit/coco_keypoints_fmt2.py +103 -0
  24. wml/iotoolkit/coco_toolkit.py +397 -0
  25. wml/iotoolkit/coco_wholebody.py +269 -0
  26. wml/iotoolkit/common.py +108 -0
  27. wml/iotoolkit/crowd_pose.py +146 -0
  28. wml/iotoolkit/fast_labelme.py +110 -0
  29. wml/iotoolkit/image_folder.py +95 -0
  30. wml/iotoolkit/imgs_cache.py +58 -0
  31. wml/iotoolkit/imgs_reader_mt.py +73 -0
  32. wml/iotoolkit/labelme_base.py +102 -0
  33. wml/iotoolkit/labelme_json_to_img.py +49 -0
  34. wml/iotoolkit/labelme_toolkit.py +117 -0
  35. wml/iotoolkit/labelme_toolkit_fwd.py +733 -0
  36. wml/iotoolkit/labelmemckeypoints_dataset.py +169 -0
  37. wml/iotoolkit/lspet.py +48 -0
  38. wml/iotoolkit/mapillary_vistas_toolkit.py +269 -0
  39. wml/iotoolkit/mat_data.py +90 -0
  40. wml/iotoolkit/mckeypoints_statistics.py +28 -0
  41. wml/iotoolkit/mot_datasets.py +62 -0
  42. wml/iotoolkit/mpii.py +108 -0
  43. wml/iotoolkit/npmckeypoints_dataset.py +164 -0
  44. wml/iotoolkit/o365_to_coco.py +136 -0
  45. wml/iotoolkit/object365_toolkit.py +156 -0
  46. wml/iotoolkit/object365v2_toolkit.py +71 -0
  47. wml/iotoolkit/pascal_voc_data.py +51 -0
  48. wml/iotoolkit/pascal_voc_toolkit.py +194 -0
  49. wml/iotoolkit/pascal_voc_toolkit_fwd.py +473 -0
  50. wml/iotoolkit/penn_action.py +57 -0
  51. wml/iotoolkit/rawframe_dataset.py +129 -0
  52. wml/iotoolkit/rewrite_pascal_voc.py +28 -0
  53. wml/iotoolkit/semantic_data.py +49 -0
  54. wml/iotoolkit/split_file_by_type.py +29 -0
  55. wml/iotoolkit/sports_mot_datasets.py +78 -0
  56. wml/iotoolkit/vis_objectdetection_dataset.py +70 -0
  57. wml/iotoolkit/vis_torch_data.py +39 -0
  58. wml/iotoolkit/yolo_toolkit.py +38 -0
  59. wml/object_detection2/__init__.py +4 -0
  60. wml/object_detection2/basic_visualization.py +37 -0
  61. wml/object_detection2/bboxes.py +812 -0
  62. wml/object_detection2/data_process_toolkit.py +146 -0
  63. wml/object_detection2/keypoints.py +292 -0
  64. wml/object_detection2/mask.py +120 -0
  65. wml/object_detection2/metrics/__init__.py +3 -0
  66. wml/object_detection2/metrics/build.py +15 -0
  67. wml/object_detection2/metrics/classifier_toolkit.py +440 -0
  68. wml/object_detection2/metrics/common.py +71 -0
  69. wml/object_detection2/metrics/mckps_toolkit.py +338 -0
  70. wml/object_detection2/metrics/toolkit.py +1953 -0
  71. wml/object_detection2/npod_toolkit.py +361 -0
  72. wml/object_detection2/odtools.py +243 -0
  73. wml/object_detection2/standard_names.py +75 -0
  74. wml/object_detection2/visualization.py +956 -0
  75. wml/object_detection2/wmath.py +34 -0
  76. wml/semantic/__init__.py +0 -0
  77. wml/semantic/basic_toolkit.py +65 -0
  78. wml/semantic/mask_utils.py +156 -0
  79. wml/semantic/semantic_test.py +21 -0
  80. wml/semantic/structures.py +1 -0
  81. wml/semantic/toolkit.py +105 -0
  82. wml/semantic/visualization_utils.py +658 -0
  83. wml/threadtoolkit.py +50 -0
  84. wml/walgorithm.py +228 -0
  85. wml/wcollections.py +212 -0
  86. wml/wfilesystem.py +487 -0
  87. wml/wml_utils.py +657 -0
  88. wml/wstructures/__init__.py +4 -0
  89. wml/wstructures/common.py +9 -0
  90. wml/wstructures/keypoints_train_toolkit.py +149 -0
  91. wml/wstructures/kps_structures.py +579 -0
  92. wml/wstructures/mask_structures.py +1161 -0
  93. wml/wtorch/__init__.py +8 -0
  94. wml/wtorch/bboxes.py +104 -0
  95. wml/wtorch/classes_suppression.py +24 -0
  96. wml/wtorch/conv_module.py +181 -0
  97. wml/wtorch/conv_ws.py +144 -0
  98. wml/wtorch/data/__init__.py +16 -0
  99. wml/wtorch/data/_utils/__init__.py +45 -0
  100. wml/wtorch/data/_utils/collate.py +183 -0
  101. wml/wtorch/data/_utils/fetch.py +47 -0
  102. wml/wtorch/data/_utils/pin_memory.py +121 -0
  103. wml/wtorch/data/_utils/signal_handling.py +72 -0
  104. wml/wtorch/data/_utils/worker.py +227 -0
  105. wml/wtorch/data/base_data_loader_iter.py +93 -0
  106. wml/wtorch/data/dataloader.py +501 -0
  107. wml/wtorch/data/datapipes/__init__.py +1 -0
  108. wml/wtorch/data/datapipes/iter/__init__.py +12 -0
  109. wml/wtorch/data/datapipes/iter/batch.py +126 -0
  110. wml/wtorch/data/datapipes/iter/callable.py +92 -0
  111. wml/wtorch/data/datapipes/iter/listdirfiles.py +37 -0
  112. wml/wtorch/data/datapipes/iter/loadfilesfromdisk.py +30 -0
  113. wml/wtorch/data/datapipes/iter/readfilesfromtar.py +60 -0
  114. wml/wtorch/data/datapipes/iter/readfilesfromzip.py +63 -0
  115. wml/wtorch/data/datapipes/iter/sampler.py +94 -0
  116. wml/wtorch/data/datapipes/utils/__init__.py +0 -0
  117. wml/wtorch/data/datapipes/utils/common.py +65 -0
  118. wml/wtorch/data/dataset.py +354 -0
  119. wml/wtorch/data/datasets/__init__.py +4 -0
  120. wml/wtorch/data/datasets/common.py +53 -0
  121. wml/wtorch/data/datasets/listdirfilesdataset.py +36 -0
  122. wml/wtorch/data/datasets/loadfilesfromdiskdataset.py +30 -0
  123. wml/wtorch/data/distributed.py +135 -0
  124. wml/wtorch/data/multi_processing_data_loader_iter.py +866 -0
  125. wml/wtorch/data/sampler.py +267 -0
  126. wml/wtorch/data/single_process_data_loader_iter.py +24 -0
  127. wml/wtorch/data/test_data_loader.py +26 -0
  128. wml/wtorch/dataset_toolkit.py +67 -0
  129. wml/wtorch/depthwise_separable_conv_module.py +98 -0
  130. wml/wtorch/dist.py +591 -0
  131. wml/wtorch/dropblock/__init__.py +6 -0
  132. wml/wtorch/dropblock/dropblock.py +228 -0
  133. wml/wtorch/dropblock/dropout.py +40 -0
  134. wml/wtorch/dropblock/scheduler.py +48 -0
  135. wml/wtorch/ema.py +61 -0
  136. wml/wtorch/fc_module.py +73 -0
  137. wml/wtorch/functional.py +34 -0
  138. wml/wtorch/iter_dataset.py +26 -0
  139. wml/wtorch/loss.py +69 -0
  140. wml/wtorch/nets/__init__.py +0 -0
  141. wml/wtorch/nets/ckpt_toolkit.py +219 -0
  142. wml/wtorch/nets/fpn.py +276 -0
  143. wml/wtorch/nets/hrnet/__init__.py +0 -0
  144. wml/wtorch/nets/hrnet/config.py +2 -0
  145. wml/wtorch/nets/hrnet/hrnet.py +494 -0
  146. wml/wtorch/nets/misc.py +249 -0
  147. wml/wtorch/nets/resnet/__init__.py +0 -0
  148. wml/wtorch/nets/resnet/layers/__init__.py +17 -0
  149. wml/wtorch/nets/resnet/layers/aspp.py +144 -0
  150. wml/wtorch/nets/resnet/layers/batch_norm.py +231 -0
  151. wml/wtorch/nets/resnet/layers/blocks.py +111 -0
  152. wml/wtorch/nets/resnet/layers/wrappers.py +110 -0
  153. wml/wtorch/nets/resnet/r50_config.py +38 -0
  154. wml/wtorch/nets/resnet/resnet.py +691 -0
  155. wml/wtorch/nets/shape_spec.py +20 -0
  156. wml/wtorch/nets/simple_fpn.py +101 -0
  157. wml/wtorch/nms.py +109 -0
  158. wml/wtorch/nn.py +896 -0
  159. wml/wtorch/ocr_block.py +193 -0
  160. wml/wtorch/summary.py +331 -0
  161. wml/wtorch/train_toolkit.py +603 -0
  162. wml/wtorch/transformer_blocks.py +266 -0
  163. wml/wtorch/utils.py +719 -0
  164. wml/wtorch/wlr_scheduler.py +100 -0
@@ -0,0 +1,1953 @@
1
+ #coding=utf-8
2
+ import numpy as np
3
+ import os
4
+ import wml.object_detection2.npod_toolkit as npod
5
+ import wml.object_detection2.bboxes as odb
6
+ import math
7
+ import logging
8
+ from wml.thirdparty.odmetrics import coco_evaluation
9
+ from wml.thirdparty.odmetrics import standard_fields
10
+ import copy
11
+ import sys
12
+ from pycocotools.coco import COCO
13
+ from pycocotools.cocoeval import COCOeval
14
+ import wml.wml_utils as wmlu
15
+ from .build import METRICS_REGISTRY
16
+ from .classifier_toolkit import ConfusionMatrix
17
+ from .common import *
18
+
19
+
20
+ def getF1(gtboxes,gtlabels,boxes,labels,threshold=0.5):
21
+ gt_shape = gtboxes.shape
22
+ #indict if there have some box match with this ground-truth box
23
+ gt_mask = np.zeros([gt_shape[0]],dtype=np.int32)
24
+ boxes_shape = boxes.shape
25
+ #indict if there have some ground-truth box match with this box
26
+ boxes_mask = np.zeros(boxes_shape[0],dtype=np.int32)
27
+ gt_size = gtlabels.shape[0]
28
+ boxes_size = labels.shape[0]
29
+ for i in range(gt_size):
30
+ max_index = -1
31
+ max_jaccard = 0.0
32
+ #iterator on all boxes to find one which have the most maximum jacard value with current ground-truth box
33
+ for j in range(boxes_size):
34
+ if gtlabels[i] != labels[j] or boxes_mask[j] != 0:
35
+ continue
36
+ jaccard = npod.box_jaccard(gtboxes[i],boxes[j])
37
+ if jaccard>threshold and jaccard > max_jaccard:
38
+ max_jaccard = jaccard
39
+ max_index = j
40
+
41
+ if max_index < 0:
42
+ continue
43
+
44
+ gt_mask[i] = 1
45
+ boxes_mask[max_index] = 1
46
+
47
+ correct_num = np.sum(gt_mask)
48
+ f1 = safe_persent(2*correct_num,correct_num+gt_shape[0])
49
+
50
+ return f1
51
+
52
+ '''
53
+ gtboxes:[X,4](ymin,xmin,ymax,xmax) relative coordinates, ground truth boxes
54
+ gtlabels:[X] the labels for ground truth boxes
55
+ boxes:[Y,4](ymin,xmin,ymax,xmax) relative coordinates,predicted boxes
56
+ labels:[Y], the labels for predicted boxes
57
+ probability:[Y], the probability for boxes, if probability is none, assum the boxes's probability is ascending order
58
+ return:
59
+ mAP:[0,100]
60
+ '''
61
+ def getmAP(gtboxes,gtlabels,boxes,labels,probability=None,threshold=0.5,is_crowd=None):
62
+
63
+ if not isinstance(gtboxes,np.ndarray):
64
+ gtboxes = np.array(gtboxes)
65
+ if len(gtboxes)==0:
66
+ gtboxes = np.zeros([0,4],dtype=np.float32)
67
+ if not isinstance(gtlabels,np.ndarray):
68
+ gtlabels = np.array(gtlabels)
69
+ if len(gtlabels)==0:
70
+ gtlabels = np.zeros([0],dtype=np.int32)
71
+ if not isinstance(boxes,np.ndarray):
72
+ boxes = np.array(boxes)
73
+ if len(boxes)==0:
74
+ boxes = np.zeros([0,4],dtype=np.float32)
75
+ if not isinstance(labels,np.ndarray):
76
+ labels = np.array(labels)
77
+ if len(labels)==0:
78
+ labels = np.zeros([0],dtype=np.int32)
79
+ if is_crowd is None:
80
+ is_crowd = np.zeros([gtlabels.shape[0]],dtype=np.bool)
81
+ if len(is_crowd)==0:
82
+ is_crowd = np.zeros([0],dtype=np.bool)
83
+ if not isinstance(is_crowd,np.ndarray):
84
+ is_crowd = np.array(is_crowd)
85
+
86
+ gtboxes = copy.deepcopy(np.array(gtboxes))
87
+ gtlabels = copy.deepcopy(np.array(gtlabels))
88
+ boxes = copy.deepcopy(boxes)
89
+ labels = copy.deepcopy(labels)
90
+ if probability is not None:
91
+ probability = copy.deepcopy(probability)
92
+ index = np.argsort(probability)
93
+ boxes = boxes[index]
94
+ labels = labels[index]
95
+
96
+ max_nr = 20
97
+ data_nr = boxes.shape[0]
98
+
99
+ if data_nr==0:
100
+ if gtboxes.size == 0:
101
+ return 100.0
102
+ return 0.0
103
+
104
+ if data_nr>max_nr:
105
+ beg_index = range(0,data_nr,data_nr//max_nr)
106
+ else:
107
+ beg_index = range(0,data_nr)
108
+
109
+ t_res = []
110
+
111
+ for v in beg_index:
112
+ p,r = getPrecision(gtboxes,gtlabels,boxes[v:],labels[v:],threshold,is_crowd=is_crowd)
113
+ t_res.append([p,r])
114
+
115
+
116
+ t_res1 = []
117
+ old_v = None
118
+ for v in t_res:
119
+ if old_v is not None and v[0]<old_v[0]:
120
+ v[0] = old_v[0]
121
+ t_res1.append(v)
122
+ old_v = v
123
+
124
+ res = []
125
+ old_v = None
126
+ for v in reversed(t_res1):
127
+ if old_v is not None:
128
+ if v[1]<old_v[1]:
129
+ v[1] = old_v[1]
130
+ if math.fabs(v[1]-old_v[1])<1e-3 and v[0]<old_v[0]:
131
+ v[0] = old_v[0]
132
+ res.append(v)
133
+ old_v = v
134
+
135
+ min_r = res[0][1]
136
+ max_r = res[-1][1]
137
+ logging.debug("mAP: max r {}, min r {}".format(max_r,min_r))
138
+
139
+ if min_r > 1e-2:
140
+ res = np.concatenate([np.array([[res[0][0],0.]]),res],axis=0)
141
+
142
+ l_precisions = res[-1][0]
143
+ l_recall = res[-1][1]
144
+ append_res = []
145
+ cur_reall = l_recall
146
+ while cur_reall+10<100.0:
147
+ cur_reall = cur_reall+10
148
+ append_res.append([min(l_precisions,l_precisions*l_recall/cur_reall,l_recall),cur_reall])
149
+
150
+ if len(append_res)>0:
151
+ res = np.concatenate([res,np.array(append_res)],axis=0)
152
+
153
+ if max_r <100.0-1e-2:
154
+ l_precisions = res[-1][0]
155
+ l_recall = res[-1][1]
156
+ t_precision = min(l_precisions*l_recall/100.0,l_precisions,l_recall)
157
+ res = np.concatenate([res,np.array([[t_precision,100.0]])])
158
+
159
+ res = np.array(res)
160
+ res = res.transpose()
161
+ precisions = res[0]
162
+ recall = res[1]
163
+ new_r = np.arange(0.,100.01,10.).tolist()
164
+ new_p = []
165
+ for r in new_r:
166
+ new_p.append(np.interp(r,recall,precisions))
167
+ precisions = np.array(new_p)
168
+ return np.mean(precisions)
169
+
170
+
171
+ def getRecall(gtboxes,gtlabels,boxes,labels,threshold=0.5):
172
+ gt_shape = gtboxes.shape
173
+ #indict if there have some box match with this ground-truth box
174
+ gt_mask = np.zeros([gt_shape[0]],dtype=np.int32)
175
+ boxes_shape = boxes.shape
176
+ #indict if there have some ground-truth box match with this box
177
+ boxes_mask = np.zeros(boxes_shape[0],dtype=np.int32)
178
+ gt_size = gtlabels.shape[0]
179
+ boxes_size = labels.shape[0]
180
+ for i in range(gt_size):
181
+ max_index = -1
182
+ max_jaccard = 0.0
183
+ #iterator on all boxes to find one have the most maximum jacard value with current ground-truth box
184
+ for j in range(boxes_size):
185
+ if gtlabels[i] != labels[j] or boxes_mask[j] != 0:
186
+ continue
187
+ jaccard = npod.box_jaccard(gtboxes[i],boxes[j])
188
+ if jaccard>threshold and jaccard > max_jaccard:
189
+ max_jaccard = jaccard
190
+ max_index = j
191
+
192
+ if max_index < 0:
193
+ continue
194
+
195
+ gt_mask[i] = 1
196
+ boxes_mask[max_index] = 1
197
+
198
+ correct_num = np.sum(gt_mask)
199
+ total_num = gt_size
200
+
201
+ if 0 == total_num:
202
+ return 100.
203
+
204
+ return 100.*correct_num/total_num
205
+
206
+ def getAccuracy(gtboxes,gtlabels,boxes,labels,threshold=0.5,ext_info=False,is_crowd=None):
207
+ '''
208
+ :param gtboxes: [N,4]
209
+ :param gtlabels: [N]
210
+ :param boxes: [M,4]
211
+ :param labels: [M]
212
+ :param threshold: nms_threshold,float
213
+ :return: precision,recall float
214
+ '''
215
+ if not isinstance(gtboxes,np.ndarray):
216
+ gtboxes = np.array(gtboxes)
217
+ if not isinstance(gtlabels,np.ndarray):
218
+ gtlabels = np.array(gtlabels)
219
+ if is_crowd is None:
220
+ is_crowd = np.zeros([gtlabels.shape[0]],dtype=np.bool)
221
+ if not isinstance(is_crowd,np.ndarray):
222
+ is_crowd = np.array(is_crowd)
223
+ gt_shape = gtboxes.shape
224
+ #indict if there have some box match with this ground-truth box
225
+ gt_mask = np.zeros([gt_shape[0]],dtype=np.int32)
226
+ boxes_shape = boxes.shape
227
+ #indict if there have some ground-truth box match with this box
228
+ boxes_mask = np.zeros(boxes_shape[0],dtype=np.int32)
229
+ gt_size = gtlabels.shape[0]
230
+ boxes_size = labels.shape[0]
231
+ iou_matrix = odb.iou_matrix(gtboxes,boxes)
232
+ if iou_matrix.size>0:
233
+ target_idxs = np.argmax(iou_matrix,axis=-1)
234
+ else:
235
+ target_idxs = np.ones([gt_size])*-1
236
+ for i in range(gt_size):
237
+ max_index = -1
238
+ max_jaccard = 0.0
239
+
240
+ max_index = target_idxs[i]
241
+ if max_index<0:
242
+ continue
243
+
244
+ max_jaccard = iou_matrix[i,max_index]
245
+ if max_jaccard<threshold:
246
+ continue
247
+
248
+ if gtlabels[i] == labels[max_index] and boxes_mask[max_index] == 0:
249
+ pass
250
+ else:
251
+ max_index = -1
252
+ for j in range(boxes_size):
253
+ if gtlabels[i] != labels[j] or boxes_mask[j] != 0:
254
+ continue
255
+
256
+ jaccard = iou_matrix[i,j]
257
+ if jaccard>threshold and jaccard > max_jaccard:
258
+ max_jaccard = jaccard
259
+ max_index = j
260
+
261
+ if max_index < 0:
262
+ continue
263
+
264
+ gt_mask[i] = 1
265
+ boxes_mask[max_index] = 1
266
+
267
+ r_gt_mask = np.logical_or(gt_mask,is_crowd)
268
+ correct_gt_num = np.sum(r_gt_mask)
269
+ correct_bbox_num = np.sum(boxes_mask)
270
+ correct_num = np.sum(gt_mask)
271
+ r_gt_size = gt_size-correct_gt_num+correct_bbox_num
272
+
273
+ P_v = gt_size
274
+ TP_v = correct_bbox_num
275
+ FP_v = boxes_size-correct_num
276
+
277
+ return safe_persent(TP_v,r_gt_size+boxes_size-correct_bbox_num)
278
+
279
+ def getPrecision(gtboxes,gtlabels,boxes,labels,threshold=0.5,ext_info=False,is_crowd=None):
280
+ '''
281
+ :param gtboxes: [N,4]
282
+ :param gtlabels: [N]
283
+ :param boxes: [M,4]
284
+ :param labels: [M]
285
+ :param threshold: nms_threshold,float
286
+ :return: precision,recall float
287
+ '''
288
+ if not isinstance(gtboxes,np.ndarray):
289
+ gtboxes = np.array(gtboxes)
290
+ if not isinstance(gtlabels,np.ndarray):
291
+ gtlabels = np.array(gtlabels)
292
+ if not isinstance(labels,np.ndarray):
293
+ labels = np.array(labels)
294
+ if is_crowd is None:
295
+ is_crowd = np.zeros([gtlabels.shape[0]],dtype=np.bool)
296
+ if not isinstance(is_crowd,np.ndarray):
297
+ is_crowd = np.array(is_crowd)
298
+ gt_shape = gtboxes.shape
299
+ #indict if there have some box match with this ground-truth box
300
+ gt_mask = np.zeros([gt_shape[0]],dtype=np.int32)
301
+ boxes_shape = boxes.shape
302
+ #indict if there have some ground-truth box match with this box
303
+ boxes_mask = np.zeros(boxes_shape[0],dtype=np.int32)
304
+ gt_size = gtlabels.shape[0]
305
+ boxes_size = labels.shape[0]
306
+ iou_matrix = odb.iou_matrix(gtboxes,boxes)
307
+ if iou_matrix.size>0:
308
+ target_idxs = np.argmax(iou_matrix,axis=-1)
309
+ else:
310
+ target_idxs = np.ones([gt_size])*-1
311
+ #print(">>>>",gtboxes,gtlabels)
312
+ for i in range(gt_size):
313
+ max_index = -1
314
+ max_jaccard = 0.0
315
+
316
+ max_index = target_idxs[i]
317
+ if max_index<0:
318
+ continue
319
+ max_jaccard = iou_matrix[i,max_index]
320
+ if max_jaccard<threshold:
321
+ continue
322
+
323
+ if gtlabels[i] == labels[max_index] and boxes_mask[max_index] == 0:
324
+ pass
325
+ else:
326
+ max_index = -1
327
+ for j in range(boxes_size):
328
+ if gtlabels[i] != labels[j] or boxes_mask[j] != 0:
329
+ continue
330
+
331
+ jaccard = iou_matrix[i,j]
332
+ if jaccard>threshold and jaccard > max_jaccard:
333
+ max_jaccard = jaccard
334
+ max_index = j
335
+
336
+ if max_index < 0:
337
+ continue
338
+
339
+ gt_mask[i] = 1
340
+ boxes_mask[max_index] = 1
341
+
342
+
343
+ r_gt_mask = np.logical_or(gt_mask,is_crowd)
344
+ correct_gt_num = np.sum(r_gt_mask)
345
+ correct_bbox_num = np.sum(boxes_mask)
346
+
347
+ recall = safe_persent(correct_gt_num,gt_size)
348
+ precision = safe_persent(correct_bbox_num,boxes_size)
349
+ P_v = gt_size
350
+ TP_v = correct_bbox_num
351
+ FP_v = boxes_size-correct_bbox_num
352
+
353
+
354
+ if ext_info:
355
+ gt_label_list = []
356
+ for i in range(gt_mask.shape[0]):
357
+ if gt_mask[i] != 1:
358
+ gt_label_list.append(gtlabels[i])
359
+ pred_label_list = []
360
+ for i in range(boxes_size):
361
+ if boxes_mask[i] != 1:
362
+ pred_label_list.append(labels[i])
363
+ return precision,recall,gt_label_list,pred_label_list,TP_v,FP_v,P_v
364
+ else:
365
+ return precision,recall
366
+
367
+ def getEasyPrecision(gtboxes,gtlabels,boxes,labels,threshold=0.05,auto_scale_threshold=True,ext_info=False):
368
+ '''
369
+ :param gtboxes: [N,4]
370
+ :param gtlabels: [N]
371
+ :param boxes: [M,4]
372
+ :param labels: [M]
373
+ :param threshold: nms_threshold,float
374
+ :return: precision,recall float
375
+ '''
376
+ if not isinstance(gtboxes,np.ndarray):
377
+ gtboxes = np.array(gtboxes)
378
+ if not isinstance(gtlabels,np.ndarray):
379
+ gtlabels = np.array(gtlabels)
380
+ gt_shape = gtboxes.shape
381
+ #indict if there have some box match with this ground-truth box
382
+ gt_mask = np.zeros([gt_shape[0]],dtype=np.int32)
383
+ boxes_shape = boxes.shape
384
+ #indict if there have some ground-truth box match with this box
385
+ boxes_mask = np.zeros(boxes_shape[0],dtype=np.int32)
386
+ gt_size = gtlabels.shape[0]
387
+ boxes_size = labels.shape[0]
388
+ MIN_VOL = 0.005
389
+ #print(">>>>",gtboxes,gtlabels)
390
+ for i in range(gt_size):
391
+ max_index = -1
392
+ max_jaccard = 0.0
393
+
394
+ t_threshold = threshold
395
+ if auto_scale_threshold:
396
+ #print(i,gtboxes,gtlabels)
397
+ vol = npod.box_vol(gtboxes[i])
398
+ if vol < MIN_VOL:
399
+ t_threshold = vol*threshold/MIN_VOL
400
+ #iterator on all boxes to find one have the most maximum jacard value with current ground-truth box
401
+ for j in range(boxes_size):
402
+ if gtlabels[i] != labels[j] or boxes_mask[j] != 0:
403
+ continue
404
+
405
+ jaccard = npod.box_jaccard(gtboxes[i],boxes[j])
406
+ if jaccard>t_threshold and jaccard > max_jaccard:
407
+ max_jaccard = jaccard
408
+ max_index = j
409
+
410
+ if max_index < 0:
411
+ continue
412
+
413
+ gt_mask[i] = 1
414
+ boxes_mask[max_index] = 1
415
+
416
+ pred_labels = set(labels[boxes_mask.astype(np.bool)].tolist())
417
+ for j in range(boxes_size):
418
+ if boxes_mask[j] != 0:
419
+ continue
420
+ if labels[j] in pred_labels:
421
+ boxes_mask[j] = 1
422
+
423
+
424
+ correct_num = np.sum(gt_mask)
425
+ correct_num1 = np.sum(boxes_mask)
426
+
427
+ recall = safe_persent(correct_num,gt_size)
428
+ precision = safe_persent(correct_num1,boxes_size)
429
+ P_v = gt_size
430
+ TP_v = correct_num
431
+ FP_v = boxes_size-correct_num1
432
+
433
+
434
+ if ext_info:
435
+ gt_label_list = []
436
+ for i in range(gt_mask.shape[0]):
437
+ if gt_mask[i] != 1:
438
+ gt_label_list.append(gtlabels[i])
439
+ pred_label_list = []
440
+ for i in range(boxes_size):
441
+ if boxes_mask[i] != 1:
442
+ pred_label_list.append(labels[i])
443
+ return precision,recall,gt_label_list,pred_label_list,TP_v,FP_v,P_v
444
+ else:
445
+ return precision,recall
446
+
447
+ def getPrecisionV2(gt_data,pred_data,pred_func,threshold,return_f1=False):
448
+ '''
449
+ :param gt_data: N objects
450
+ :param pred_data: M object
451
+ :param pred_func: float (*)(obj0,obj1) get the distance of two objects, distance greater or equal zero
452
+ :return: precision,recall float
453
+ '''
454
+ NR_GT = len(gt_data)
455
+ NR_PRED = len(pred_data)
456
+ #indict if there have some box match with this ground-truth box
457
+ gt_mask = np.zeros([NR_GT],dtype=np.int32)
458
+ #indict if there have some ground-truth box match with this box
459
+ pred_mask = np.zeros(NR_PRED,dtype=np.int32)
460
+ for i in range(NR_GT):
461
+ min_index = -1
462
+ min_dis = 1e10
463
+
464
+ #iterator on all boxes to find one have the most maximum jacard value with current ground-truth box
465
+ for j in range(NR_PRED):
466
+ if pred_mask[j] != 0:
467
+ continue
468
+ dis = pred_func(gt_data[i],pred_data[j])
469
+ if dis<threshold and dis< min_dis:
470
+ min_dis = dis
471
+ min_index = j
472
+
473
+ if min_index < 0:
474
+ continue
475
+
476
+ gt_mask[i] = 1
477
+ pred_mask[min_index] = 1
478
+
479
+ correct_num = np.sum(gt_mask)
480
+
481
+ recall = safe_persent(correct_num,NR_GT)
482
+ precision = safe_persent(correct_num,NR_PRED)
483
+
484
+ if return_f1:
485
+ f1 = safe_persent(2*correct_num,NR_PRED+NR_GT)
486
+ return precision,recall,f1
487
+
488
+ return precision,recall
489
+
490
+ @METRICS_REGISTRY.register()
491
+ class Accuracy(BaseMetrics):
492
+ def __init__(self,threshold=0.1,num_classes=90,label_trans=None,classes_begin_value=1,*args,**kwargs):
493
+ super().__init__()
494
+ self.threshold = threshold
495
+ self.gtboxes = []
496
+ self.gtlabels = []
497
+ self.is_crowd = []
498
+ self.boxes = []
499
+ self.labels = []
500
+ self.precision = None
501
+ self.recall = None
502
+ self.total_test_nr = 0
503
+ self.num_classes = num_classes
504
+ self.label_trans = label_trans
505
+ self.bboxes_offset = np.zeros([1,4],dtype=np.float32)
506
+ del classes_begin_value
507
+
508
+ def __call__(self, gtboxes,gtlabels,boxes,labels,probability=None,img_size=[512,512],
509
+ gtmasks=None,
510
+ masks=None,is_crowd=None,use_relative_coord=True):
511
+ if self.label_trans is not None:
512
+ gtlabels = self.label_trans(gtlabels)
513
+ labels = self.label_trans(labels)
514
+ if gtboxes.shape[0]>0:
515
+ self.gtboxes.append(np.array(gtboxes)+self.bboxes_offset)
516
+ self.gtlabels.append(np.array(gtlabels))
517
+ if is_crowd is None:
518
+ is_crowd = np.zeros([gtlabels.shape[0]],dtype=np.bool)
519
+ self.is_crowd.append(np.array(is_crowd))
520
+ if boxes.shape[0]>0:
521
+ self.boxes.append(np.array(boxes)+self.bboxes_offset)
522
+ self.labels.append(np.array(labels))
523
+
524
+ self.total_test_nr += 1
525
+
526
+ t_bboxes = np.concatenate([gtboxes,boxes],axis=0)
527
+ if t_bboxes.size>0:
528
+ t_max = np.max(t_bboxes,axis=0)
529
+ else:
530
+ t_max = np.zeros([4])
531
+ max_0 = t_max[2]
532
+ max_1 = t_max[3]
533
+ offset = np.array([[max_0,max_1,max_0,max_1]],dtype=np.float32)
534
+ self.bboxes_offset = self.bboxes_offset+offset
535
+
536
+ def evaluate(self):
537
+ if self.total_test_nr==0 or len(self.boxes)==0 or len(self.labels)==0:
538
+ self.precision,self.recall = 0,0
539
+ return
540
+ gtboxes = np.concatenate(self.gtboxes,axis=0)
541
+ gtlabels = np.concatenate(self.gtlabels,axis=0)
542
+ boxes = np.concatenate(self.boxes,axis=0)
543
+ labels = np.concatenate(self.labels,axis=0)
544
+ if self.is_crowd is not None and len(self.is_crowd)>0:
545
+ is_crowd = np.concatenate(self.is_crowd,axis=0)
546
+ else:
547
+ is_crowd = None
548
+ self.acc = getAccuracy(gtboxes, gtlabels, boxes, labels, threshold=self.threshold,
549
+ ext_info=False,
550
+ is_crowd=is_crowd)
551
+ def value(self):
552
+ return self.acc
553
+
554
+ def show(self,name=""):
555
+ self.evaluate()
556
+ res = f"{name}: total test nr {self.total_test_nr}, acc {self.acc:.3f}"
557
+ print(res)
558
+
559
+ def to_string(self):
560
+ try:
561
+ return f"{self.acc:.3f}({self.total_test_nr})"
562
+ except:
563
+ return "N.A."
564
+
565
+ @METRICS_REGISTRY.register()
566
+ class PrecisionAndRecall(BaseMetrics):
567
+ def __init__(self,threshold=0.5,num_classes=90,label_trans=None,classes_begin_value=1,*args,**kwargs):
568
+ self.threshold = threshold
569
+ self.gtboxes = []
570
+ self.gtlabels = []
571
+ self.boxes = []
572
+ self.labels = []
573
+ self.is_crowd = []
574
+ self.precision = None
575
+ self.recall = None
576
+ self.total_test_nr = 0
577
+ self.num_classes = num_classes
578
+ self.label_trans = label_trans
579
+ self.bboxes_offset = np.zeros([1,4],dtype=np.float32)
580
+ del classes_begin_value
581
+
582
+ def __call__(self, gtboxes,gtlabels,boxes,labels,probability=None,img_size=[512,512],
583
+ gtmasks=None,
584
+ masks=None,is_crowd=None,use_relative_coord=True):
585
+
586
+ if self.label_trans is not None:
587
+ gtlabels = self.label_trans(gtlabels)
588
+ labels = self.label_trans(labels)
589
+
590
+ if gtboxes.shape[0]>0:
591
+ self.gtboxes.append(gtboxes+self.bboxes_offset)
592
+ self.gtlabels.append(np.array(gtlabels))
593
+ self.is_crowd.append(np.array(is_crowd))
594
+
595
+ if boxes.shape[0]>0:
596
+ self.boxes.append(boxes+self.bboxes_offset)
597
+ self.labels.append(np.array(labels))
598
+ if len(gtboxes)==0:
599
+ gtboxes = np.zeros([0,4],dtype=gtboxes.dtype)
600
+
601
+ if boxes.size == 0:
602
+ boxes = np.zeros([0,4],dtype=boxes.dtype)
603
+
604
+ t_bboxes = np.concatenate([gtboxes,boxes],axis=0)
605
+ if len(t_bboxes)>0:
606
+ t_max = np.max(t_bboxes,axis=0)
607
+ else:
608
+ t_max = [0,0,0,0]
609
+ max_0 = t_max[2]
610
+ max_1 = t_max[3]
611
+ offset = np.array([[max_0,max_1,max_0,max_1]],dtype=np.float32)
612
+ self.bboxes_offset = self.bboxes_offset+offset
613
+
614
+ self.total_test_nr += 1
615
+
616
+ cur_precision,cur_recall = getPrecision(gtboxes=gtboxes,
617
+ gtlabels=gtlabels,
618
+ boxes=boxes,labels=labels,
619
+ threshold=self.threshold,
620
+ ext_info=False,
621
+ is_crowd=is_crowd)
622
+ self._current_info = f"precision={cur_precision:.3f}, recall={cur_recall:.3f}"
623
+
624
+ def evaluate(self):
625
+ if self.total_test_nr==0 or len(self.boxes)==0 or len(self.labels)==0:
626
+ self.precision,self.recall = 0,0
627
+ return
628
+ if len(self.gtboxes) == 0:
629
+ gtboxes = np.zeros([0,4],dtype=np.float32)
630
+ gtlabels = np.zeros([0],dtype=np.int32)
631
+ is_crowd = np.zeros([0],dtype=np.bool)
632
+ else:
633
+ gtboxes = np.concatenate(self.gtboxes,axis=0)
634
+ gtlabels = np.concatenate(self.gtlabels,axis=0)
635
+ is_crowd = np.concatenate(self.is_crowd,axis=0).astype(np.bool)
636
+ boxes = np.concatenate(self.boxes,axis=0)
637
+ labels = np.concatenate(self.labels,axis=0)
638
+ self.precision,self.recall = getPrecision(gtboxes, gtlabels, boxes, labels, threshold=self.threshold,
639
+ ext_info=False,
640
+ is_crowd=is_crowd)
641
+ @property
642
+ def f1(self):
643
+ return 2*self.precision*self.recall/max(self.precision+self.recall,1e-8)
644
+
645
+ def show(self,name=""):
646
+ self.evaluate()
647
+ res = f"{name}: {self}"
648
+ print(res)
649
+
650
+ def value(self):
651
+ return self.f1
652
+
653
+ def detail_valus(self):
654
+ return self.precision,self.recall
655
+
656
+ def to_string(self):
657
+ try:
658
+ return f"{self.precision:.3f}/{self.recall:.3f}/{self.f1:.3f}/({self.total_test_nr})"
659
+ except:
660
+ return "N.A."
661
+
662
+ def __repr__(self):
663
+ res = f"total test nr {self.total_test_nr}, precision {self.precision:.3f}, recall {self.recall:.3f}, f1 {self.f1:.3f}"
664
+ return res
665
+
666
+ @METRICS_REGISTRY.register()
667
+ class EasyPrecisionAndRecall(BaseMetrics):
668
+ def __init__(self,threshold=0.05,num_classes=90,label_trans=None,classes_begin_value=1,*args,**kwargs):
669
+ self.threshold = threshold
670
+ self.gtboxes = []
671
+ self.gtlabels = []
672
+ self.boxes = []
673
+ self.labels = []
674
+ self.precision = None
675
+ self.recall = None
676
+ self.total_test_nr = 0
677
+ self.num_classes = num_classes
678
+ self.label_trans = label_trans
679
+ del classes_begin_value
680
+
681
+ def __call__(self, gtboxes,gtlabels,boxes,labels,probability=None,img_size=[512,512],
682
+ gtmasks=None,
683
+ masks=None,is_crowd=None,use_relative_coord=True):
684
+ if self.label_trans is not None:
685
+ gtlabels = self.label_trans(gtlabels)
686
+ labels = self.label_trans(labels)
687
+ if gtboxes.shape[0]>0:
688
+ self.gtboxes.append(gtboxes)
689
+ self.gtlabels.append(np.array(gtlabels)+self.total_test_nr*self.num_classes)
690
+ if boxes.shape[0]>0:
691
+ self.boxes.append(boxes)
692
+ self.labels.append(np.array(labels)+self.total_test_nr*self.num_classes)
693
+ self.total_test_nr += 1
694
+
695
+ def evaluate(self):
696
+ if self.total_test_nr==0 or len(self.boxes)==0 or len(self.labels)==0:
697
+ self.precision,self.recall = 0,0
698
+ return
699
+ gtboxes = np.concatenate(self.gtboxes,axis=0)
700
+ gtlabels = np.concatenate(self.gtlabels,axis=0)
701
+ boxes = np.concatenate(self.boxes,axis=0)
702
+ labels = np.concatenate(self.labels,axis=0)
703
+ self.precision,self.recall = getEasyPrecision(gtboxes, gtlabels, boxes, labels, threshold=self.threshold,
704
+ auto_scale_threshold=False, ext_info=False)
705
+ @property
706
+ def f1(self):
707
+ return 2*self.precision*self.recall/max(self.precision+self.recall,1e-8)
708
+
709
+ def show(self,name=""):
710
+ self.evaluate()
711
+ res = f"{name}: {self}"
712
+ print(res)
713
+
714
+ def value(self):
715
+ return self.f1
716
+
717
+ def detail_valus(self):
718
+ return self.precision,self.recall
719
+
720
+ def to_string(self):
721
+ try:
722
+ return f"{self.precision:.3f}/{self.recall:.3f}/{self.f1}/({self.total_test_nr})"
723
+ except:
724
+ return "N.A."
725
+
726
+ def __repr__(self):
727
+ res = f"total test nr {self.total_test_nr}, precision {self.precision:.3f}, recall {self.recall:.3f}, f1 {self.f1}"
728
+ return res
729
+
730
+ @METRICS_REGISTRY.register()
731
+ class ClsPrecisionAndRecall(BaseMetrics):
732
+ def __init__(self,threshold=0.5,num_classes=90,label_trans=None,classes_begin_value=1,*args,**kwargs):
733
+ self.threshold = threshold
734
+ self.precision = None
735
+ self.recall = None
736
+ self.total_test_nr = 0
737
+ self.num_classes = num_classes
738
+ self.label_trans = label_trans
739
+ del classes_begin_value
740
+ self.tp = 0
741
+ self.fn = 0
742
+ self.fp = 0
743
+
744
+ def __call__(self, gtboxes,gtlabels,boxes,labels,probability=None,img_size=[512,512],
745
+ gtmasks=None,
746
+ masks=None,is_crowd=None,use_relative_coord=True):
747
+ if self.label_trans is not None:
748
+ gtlabels = self.label_trans(gtlabels)
749
+ labels = self.label_trans(labels)
750
+ if not isinstance(gtlabels,np.ndarray):
751
+ gtlabels = np.array(gtlabels)
752
+ if is_crowd is not None and not isinstance(is_crowd,np.ndarray):
753
+ is_crowd = np.array(is_crowd)
754
+ ori_gtlabels = gtlabels.copy()
755
+ gtlabels = set(gtlabels)
756
+ labels = set(labels)
757
+ union_labels = gtlabels&labels
758
+ tp = len(union_labels)
759
+ false_negative = gtlabels-union_labels
760
+ fn = len(false_negative)
761
+
762
+ if is_crowd is not None:
763
+ for l in false_negative:
764
+ mask = ori_gtlabels==l
765
+ t_is_crowd = is_crowd[mask]
766
+ if np.all(t_is_crowd):
767
+ fn = fn-1
768
+
769
+ fp = len(labels)-tp
770
+ self.tp += tp
771
+ self.fn += fn
772
+ self.fp += fp
773
+ self.total_test_nr += 1
774
+ if fp+tp>0:
775
+ precision = tp/(fp+tp)
776
+ else:
777
+ precision = 1.0
778
+ if fn+tp>0:
779
+ recall = tp/(fn+tp)
780
+ else:
781
+ recall = 1.0
782
+ self._current_info = f"precision={precision:.3f}, recall={recall:.3f}"
783
+
784
+
785
+
786
+ def evaluate(self):
787
+ if self.total_test_nr==0:
788
+ self.precision,self.recall = 0,0
789
+ return
790
+ self.precision = self.tp/max(self.fp+self.tp,1)
791
+ self.recall = self.tp/max(self.fn+self.tp,1)
792
+
793
+ @property
794
+ def f1(self):
795
+ return 2*self.precision*self.recall/max(self.precision+self.recall,1e-8)
796
+
797
+ def show(self,name=""):
798
+ self.evaluate()
799
+ res = f"{name}: {self}"
800
+ print(res)
801
+
802
+ def value(self):
803
+ return self.f1
804
+
805
+ def detail_valus(self):
806
+ return self.precision,self.recall
807
+
808
+ def to_string(self):
809
+ try:
810
+ return f"{self.precision:.3f}/{self.recall:.3f}/{self.f1}/({self.total_test_nr})"
811
+ except:
812
+ return "N.A."
813
+
814
+ def __repr__(self):
815
+ res = f"total test nr {self.total_test_nr}, precision {self.precision:.3f}, recall {self.recall:.3f}, f1 {self.f1}"
816
+ return res
817
+
818
+ @METRICS_REGISTRY.register()
819
+ class ROC:
820
+ def __init__(self,threshold=0.5,num_classes=90,label_trans=None,classes_begin_value=1,*args,**kwargs):
821
+ self.threshold = threshold
822
+ self.gtboxes = []
823
+ self.gtlabels = []
824
+ self.boxes = []
825
+ self.labels = []
826
+ self.probs = []
827
+ self.precision = None
828
+ self.recall = None
829
+ self.total_test_nr = 0
830
+ self.num_classes = num_classes
831
+ self.label_trans = label_trans
832
+ self.results = None
833
+ del classes_begin_value
834
+
835
+ def __call__(self, gtboxes,gtlabels,boxes,labels,probability=None,img_size=[512,512],
836
+ gtmasks=None,
837
+ masks=None,is_crowd=None):
838
+ if self.label_trans is not None:
839
+ gtlabels = self.label_trans(gtlabels)
840
+ labels = self.label_trans(labels)
841
+ if gtboxes.shape[0]>0:
842
+ self.gtboxes.append(gtboxes)
843
+ self.gtlabels.append(np.array(gtlabels)+self.total_test_nr*self.num_classes)
844
+ if boxes.shape[0]>0:
845
+ self.boxes.append(boxes)
846
+ self.labels.append(np.array(labels)+self.total_test_nr*self.num_classes)
847
+ self.probs.append(np.array(probability))
848
+ self.total_test_nr += 1
849
+
850
+ def evaluate(self):
851
+ if self.total_test_nr==0 or len(self.boxes)==0 or len(self.labels)==0:
852
+ self.precision,self.recall = 0,0
853
+ return
854
+ gtboxes = np.concatenate(self.gtboxes,axis=0)
855
+ gtlabels = np.concatenate(self.gtlabels,axis=0)
856
+ boxes = np.concatenate(self.boxes,axis=0)
857
+ labels = np.concatenate(self.labels,axis=0)
858
+ probs = np.concatenate(self.probs,axis=0)
859
+ self.results = []
860
+
861
+ for p in np.arange(0,1,0.05):
862
+ mask = np.greater(probs,p)
863
+ t_boxes = boxes[mask]
864
+ t_labels = labels[mask]
865
+ precision, recall, gt_label_list, pred_label_list, TP_v, FP_v, P_v = \
866
+ getPrecision(gtboxes, gtlabels, t_boxes, t_labels, threshold=self.threshold,
867
+ ext_info=True)
868
+ self.results.append([p,precision,recall])
869
+
870
+ def show(self,name=""):
871
+ print(self.to_string())
872
+
873
+ def to_string(self):
874
+ self.evaluate()
875
+ res = ""
876
+ if self.results is None or len(self.results) == 0:
877
+ return res
878
+ for p, precision, recall in self.results:
879
+ res += f"{p:.3f},{precision:.3f},{recall:.3f};\n"
880
+
881
+ return res
882
+
883
+ class ModelPerformance:
884
+ def __init__(self,threshold,no_mAP=False,no_F1=False):
885
+ self.total_map = 0.
886
+ self.total_recall = 0.
887
+ self.total_precision = 0.
888
+ self.total_F1 = 0.
889
+ self.threshold = threshold
890
+ self.test_nr = 0
891
+ self.no_mAP=no_mAP
892
+ self.no_F1 = no_F1
893
+
894
+ def __call__(self, gtboxes,gtlabels,boxes,labels,probability=None):
895
+ gtboxes = copy.deepcopy(np.array(gtboxes))
896
+ gtlabels = copy.deepcopy(np.array(gtlabels))
897
+ boxes = copy.deepcopy(boxes)
898
+ labels = copy.deepcopy(labels)
899
+ if probability is not None:
900
+ probability = copy.deepcopy(probability)
901
+
902
+ if self.no_mAP:
903
+ ap = 0.
904
+ else:
905
+ ap = getmAP(gtboxes, gtlabels, boxes, labels, probability=probability,threshold=self.threshold)
906
+
907
+ rc = getRecall(gtboxes, gtlabels, boxes, labels, self.threshold)
908
+
909
+ if self.no_F1:
910
+ f1 = 0.
911
+ else:
912
+ f1 = getF1(gtboxes, gtlabels, boxes, labels, self.threshold)
913
+
914
+ pc,_ = getPrecision(gtboxes, gtlabels, boxes, labels, self.threshold)
915
+
916
+ self.total_map += ap
917
+ self.total_recall += rc
918
+ self.total_precision += pc
919
+ self.total_F1 += f1
920
+ self.test_nr += 1
921
+ return ap,rc,pc,f1
922
+
923
+ @staticmethod
924
+ def safe_div(v0,v1):
925
+ if math.fabs(v1)<1e-8:
926
+ return 0.
927
+ return v0/v1
928
+
929
+ def __getattr__(self, item):
930
+ if item=="mAP":
931
+ return self.safe_div(self.total_map,self.test_nr)
932
+ elif item =="recall":
933
+ return self.safe_div(self.total_recall,self.test_nr)
934
+ elif item=="precision":
935
+ return self.safe_div(self.total_precision,self.test_nr)
936
+
937
+ class GeneralCOCOEvaluation(BaseMetrics):
938
+ def __init__(self,categories_list=None,
939
+ num_classes=None,mask_on=False,label_trans=None,
940
+ classes_begin_value=1,
941
+ min_bbox_size=0,
942
+ one_classes=False,
943
+ **kwargs):
944
+ super().__init__(**kwargs)
945
+ if categories_list is None:
946
+ print(f"WARNING: Use default categories list, start classes is {classes_begin_value}")
947
+ self.categories_list = [{"id":x+classes_begin_value,"name":str(x+classes_begin_value)} for x in range(num_classes)]
948
+ else:
949
+ self.categories_list = categories_list
950
+ if not mask_on:
951
+ self.coco_evaluator = coco_evaluation.CocoDetectionEvaluator(
952
+ self.categories_list,include_metrics_per_category=False)
953
+ else:
954
+ self.coco_evaluator = coco_evaluation.CocoMaskEvaluator(
955
+ self.categories_list,include_metrics_per_category=False)
956
+ self.min_bbox_size = min_bbox_size
957
+ if self.min_bbox_size > 0:
958
+ print(f"{type(self).__name__}: set min_bbox_size to {self.min_bbox_size}")
959
+ self.label_trans = label_trans
960
+ self.image_id = 0
961
+ self.cached_values = {}
962
+ self.one_classes = one_classes
963
+ '''
964
+ gtboxes:[N,4]
965
+ gtlabels:[N]
966
+ img_size:[H,W]
967
+ gtmasks:[N,H,W]
968
+ is_crowd: [N]
969
+ '''
970
+ def __call__(self, gtboxes,gtlabels,boxes,labels,probability=None,img_size=[512,512],
971
+ gtmasks=None,
972
+ masks=None,is_crowd=None,use_relative_coord=False):
973
+ if self.min_bbox_size > 0:
974
+ gtboxes = odb.clamp_bboxes(gtboxes,self.min_bbox_size)
975
+ boxes = odb.clamp_bboxes(boxes,self.min_bbox_size)
976
+
977
+ cur_ap = getmAP(gtboxes=gtboxes,
978
+ gtlabels=gtlabels,
979
+ boxes=boxes,
980
+ labels=labels,
981
+ probability=probability,
982
+ is_crowd=is_crowd)
983
+ self._current_info = f"ap={cur_ap:.3f}"
984
+
985
+ if probability is None:
986
+ probability = np.ones_like(labels,dtype=np.float32)
987
+ if not isinstance(gtboxes,np.ndarray):
988
+ gtboxes = np.array(gtboxes)
989
+ if not isinstance(gtlabels,np.ndarray):
990
+ gtlabels = np.array(gtlabels)
991
+ if not isinstance(boxes,np.ndarray):
992
+ boxes = np.array(boxes)
993
+ if not isinstance(labels,np.ndarray):
994
+ labels = np.array(labels)
995
+ if self.label_trans is not None:
996
+ gtlabels = self.label_trans(gtlabels)
997
+ labels = self.label_trans(labels)
998
+
999
+ if self.one_classes:
1000
+ gtlabels = np.zeros_like(gtlabels)
1001
+ labels = np.zeros_like(labels)
1002
+
1003
+ if probability is not None and not isinstance(probability,np.ndarray):
1004
+ probability = np.array(probability)
1005
+ if gtlabels.shape[0]>0:
1006
+ if use_relative_coord:
1007
+ gtboxes = gtboxes*[[img_size[0],img_size[1],img_size[0],img_size[1]]]
1008
+ groundtruth_dict={
1009
+ standard_fields.InputDataFields.groundtruth_boxes:
1010
+ gtboxes,
1011
+ standard_fields.InputDataFields.groundtruth_classes:gtlabels,
1012
+ }
1013
+ if is_crowd is not None:
1014
+ if not isinstance(is_crowd,np.ndarray):
1015
+ is_crowd = np.array(is_crowd)
1016
+ groundtruth_dict[standard_fields.InputDataFields.groundtruth_is_crowd] = is_crowd
1017
+ if gtmasks is not None:
1018
+ groundtruth_dict[standard_fields.InputDataFields.groundtruth_instance_masks] = gtmasks
1019
+ self.coco_evaluator.add_single_ground_truth_image_info(
1020
+ image_id=str(self.image_id),
1021
+ groundtruth_dict=groundtruth_dict)
1022
+ if labels.shape[0]>0 and gtlabels.shape[0]>0:
1023
+ if use_relative_coord:
1024
+ boxes = boxes*[[img_size[0],img_size[1],img_size[0],img_size[1]]]
1025
+ detections_dict={
1026
+ standard_fields.DetectionResultFields.detection_boxes:
1027
+ boxes,
1028
+ standard_fields.DetectionResultFields.detection_scores:
1029
+ probability,
1030
+ standard_fields.DetectionResultFields.detection_classes:
1031
+ labels
1032
+ }
1033
+ if masks is not None:
1034
+ detections_dict[standard_fields.DetectionResultFields.detection_masks] = masks
1035
+ self.coco_evaluator.add_single_detected_image_info(
1036
+ image_id=str(self.image_id),
1037
+ detections_dict=detections_dict)
1038
+ self.image_id += 1
1039
+
1040
+ def num_examples(self):
1041
+ if '_image_ids_with_detections' in self.coco_evaluator.__dict__:
1042
+ return len(self.coco_evaluator._image_ids_with_detections)
1043
+ elif '_image_ids' in self.coco_evaluator.__dict__:
1044
+ return len(self.coco_evaluator._image_ids)
1045
+ else:
1046
+ raise RuntimeError("Error evaluator type.")
1047
+
1048
+ def evaluate(self):
1049
+ print(f"Test size {self.num_examples()}")
1050
+ res = self.coco_evaluator.evaluate()
1051
+ for k,v in res.items():
1052
+ index = k.find("/")
1053
+ if index>0:
1054
+ k = k[index+1:]
1055
+ self.cached_values[k] = v
1056
+
1057
+ return res
1058
+
1059
+ def show(self,name=""):
1060
+ sys.stdout.flush()
1061
+
1062
+ if name is None or len(name)==0:
1063
+ name = self.cfg_name
1064
+
1065
+ print(f"Test size {self.num_examples()}")
1066
+ res = self.coco_evaluator.evaluate()
1067
+ str0 = "|配置|"
1068
+ str1 = "|---|"
1069
+ str2 = f"|{name}|"
1070
+ for k,v in res.items():
1071
+ index = k.find("/")
1072
+ if index>0:
1073
+ k = k[index+1:]
1074
+ self.cached_values[k] = v
1075
+ str0 += f"{k}|"
1076
+ str1 += "---|"
1077
+ str2 += f"{v:.3f}|"
1078
+ print(str0)
1079
+ print(str1)
1080
+ print(str2)
1081
+ sys.stdout.flush()
1082
+ return res
1083
+
1084
+ def to_string(self):
1085
+ if 'mAP' in self.cached_values and 'mAP@.50IOU' in self.cached_values:
1086
+ #return f"{self.cached_values['mAP@.50IOU']:.3f}/{self.cached_values['mAP']:.3f}"
1087
+ return f"{self.cached_values['mAP@.50IOU']:.3f}"
1088
+ else:
1089
+ return f"N.A."
1090
+
1091
+ def __repr__(self):
1092
+ return self.to_string()
1093
+
1094
+ def value(self):
1095
+ if 'mAP' in self.cached_values:
1096
+ return self.cached_values['mAP']
1097
+ elif 'mAP@.50IOU' in self.cached_values:
1098
+ return self.cached_values['mAP@.50IOU']
1099
+ else:
1100
+ return 0.0
1101
+
1102
+ @METRICS_REGISTRY.register()
1103
+ class COCOBoxEvaluation(GeneralCOCOEvaluation):
1104
+ def __init__(self,categories_list=None,num_classes=None,label_trans=None,classes_begin_value=1,**kwargs):
1105
+ super().__init__(categories_list=categories_list,
1106
+ num_classes=num_classes,
1107
+ mask_on=False,
1108
+ label_trans=label_trans,
1109
+ classes_begin_value=classes_begin_value,
1110
+ **kwargs)
1111
+ @METRICS_REGISTRY.register()
1112
+ class COCOMaskEvaluation(GeneralCOCOEvaluation):
1113
+ def __init__(self,categories_list=None,num_classes=None,label_trans=None,classes_begin_value=1,**kwargs):
1114
+ super().__init__(categories_list=categories_list,
1115
+ num_classes=num_classes,
1116
+ mask_on=True,
1117
+ label_trans=label_trans,
1118
+ classes_begin_value=classes_begin_value,
1119
+ **kwargs)
1120
+
1121
+ @METRICS_REGISTRY.register()
1122
+ class COCOEvaluation(BaseMetrics):
1123
+ '''
1124
+ num_classes: 不包含背景
1125
+ '''
1126
+ def __init__(self,categories_list=None,num_classes=None,mask_on=False,label_trans=None,classes_begin_value=1,**kwargs):
1127
+ super().__init__(**kwargs)
1128
+ self.box_evaluator = COCOBoxEvaluation(categories_list=categories_list,
1129
+ num_classes=num_classes,
1130
+ label_trans=label_trans,
1131
+ classes_begin_value=classes_begin_value,
1132
+ **kwargs)
1133
+ self.mask_evaluator = None
1134
+ if mask_on:
1135
+ self.mask_evaluator = COCOMaskEvaluation(categories_list=categories_list,
1136
+ num_classes=num_classes,
1137
+ label_trans=label_trans,
1138
+ classes_begin_value=classes_begin_value,
1139
+ **kwargs)
1140
+ def __call__(self, *args, **kwargs):
1141
+ self.box_evaluator(*args,**kwargs)
1142
+ self._current_info = self.box_evaluator.current_info()
1143
+ if self.mask_evaluator is not None:
1144
+ self.mask_evaluator(*args,**kwargs)
1145
+ self._current_info += ", mask" + self.mask_evaluator.current_info()
1146
+
1147
+ def num_examples(self):
1148
+ return self.box_evaluator.num_examples()
1149
+
1150
+ def evaluate(self):
1151
+ res = self.box_evaluator.evaluate()
1152
+ if self.mask_evaluator is not None:
1153
+ res1 = self.mask_evaluator.evaluate()
1154
+ return res,res1
1155
+ return res
1156
+
1157
+ def show(self,name=""):
1158
+ self.box_evaluator.show(name=name)
1159
+ if self.mask_evaluator is not None:
1160
+ self.mask_evaluator.show(name=name)
1161
+
1162
+ def to_string(self):
1163
+ if self.mask_evaluator is not None:
1164
+ return self.box_evaluator.to_string()+";"+self.mask_evaluator.to_string()
1165
+ else:
1166
+ return self.box_evaluator.to_string()
1167
+
1168
+
1169
+ @METRICS_REGISTRY.register()
1170
+ class OneClassesCOCOEvaluation(BaseMetrics):
1171
+ '''
1172
+ num_classes: 不包含背景
1173
+ '''
1174
+ def __init__(self,categories_list=None,num_classes=None,mask_on=False,label_trans=None,classes_begin_value=1,**kwargs):
1175
+ print(f"Init OneClassesCOCOEvaluation")
1176
+ super().__init__(**kwargs)
1177
+ self.box_evaluator = COCOBoxEvaluation(categories_list=categories_list,
1178
+ num_classes=num_classes,
1179
+ label_trans=label_trans,
1180
+ classes_begin_value=classes_begin_value,
1181
+ one_classes=True,
1182
+ **kwargs)
1183
+ self.mask_evaluator = None
1184
+ if mask_on:
1185
+ self.mask_evaluator = COCOMaskEvaluation(categories_list=categories_list,
1186
+ num_classes=num_classes,
1187
+ label_trans=label_trans,
1188
+ classes_begin_value=classes_begin_value,
1189
+ one_classes=True,
1190
+ **kwargs)
1191
+ def __call__(self, *args, **kwargs):
1192
+ self.box_evaluator(*args,**kwargs)
1193
+ self._current_info = self.box_evaluator.current_info()
1194
+ if self.mask_evaluator is not None:
1195
+ self.mask_evaluator(*args,**kwargs)
1196
+ self._current_info += ", mask" + self.mask_evaluator.current_info()
1197
+
1198
+ def num_examples(self):
1199
+ return self.box_evaluator.num_examples()
1200
+
1201
+ def evaluate(self):
1202
+ res = self.box_evaluator.evaluate()
1203
+ if self.mask_evaluator is not None:
1204
+ res1 = self.mask_evaluator.evaluate()
1205
+ return res,res1
1206
+ return res
1207
+
1208
+ def show(self,name=""):
1209
+ self.box_evaluator.show(name=name)
1210
+ if self.mask_evaluator is not None:
1211
+ self.mask_evaluator.show(name=name)
1212
+
1213
+ def to_string(self):
1214
+ if self.mask_evaluator is not None:
1215
+ return self.box_evaluator.to_string()+";"+self.mask_evaluator.to_string()
1216
+ else:
1217
+ return self.box_evaluator.to_string()
1218
+
1219
+ @METRICS_REGISTRY.register()
1220
+ class COCOKeypointsEvaluation(BaseMetrics):
1221
+ def __init__(self,num_joints,categories="person",oks_sigmas=None):
1222
+ categories_keypoints = []
1223
+ for i in range(num_joints):
1224
+ categories_keypoints.append({"id":i,"name":f"KP{i}"})
1225
+ self.coco_evaluator = coco_evaluation.CocoKeypointEvaluator(
1226
+ category_id=1,
1227
+ category_keypoints=categories_keypoints,
1228
+ class_text=categories,
1229
+ oks_sigmas=oks_sigmas)
1230
+ self.image_id = 0
1231
+ self.cached_values = {}
1232
+ '''
1233
+ gtboxes:[N,4]
1234
+ img_size:[H,W]
1235
+ gtkeypoitns:[N,num_joints,3]
1236
+ boxes: [M,4]
1237
+ kps: [M,num_joints,3]
1238
+ '''
1239
+ def __call__(self, gtboxes,gtkeypoints,kps,scores,area=None,iscrowd=None):
1240
+ self.image_id += 1
1241
+ self.add_groundtruth(self.image_id,gtboxes,gtkeypoints,area=area,iscrowd=iscrowd)
1242
+ self.add_detection(self.image_id,kps,scores)
1243
+
1244
+ def add_groundtruth(self, image_id,gtboxes,gtkeypoints,area=None,iscrowd=None):
1245
+ if not isinstance(gtboxes,np.ndarray):
1246
+ gtboxes = np.array(gtboxes)
1247
+ if not isinstance(gtkeypoints,np.ndarray):
1248
+ gtkeypoints = np.array(gtkeypoints)
1249
+
1250
+ if gtboxes.shape[0]>0:
1251
+ groundtruth_dict={
1252
+ standard_fields.InputDataFields.groundtruth_boxes:
1253
+ gtboxes,
1254
+ standard_fields.InputDataFields.groundtruth_classes:np.ones([gtboxes.shape[0]],dtype=np.int32),
1255
+ }
1256
+ if iscrowd is not None:
1257
+ if not isinstance(iscrowd,np.ndarray):
1258
+ iscrowd = np.array(iscrowd)
1259
+ groundtruth_dict[standard_fields.InputDataFields.groundtruth_is_crowd] = iscrowd
1260
+ if area is not None:
1261
+ if not isinstance(area,np.ndarray):
1262
+ area = np.array(area)
1263
+ groundtruth_dict[standard_fields.InputDataFields.groundtruth_area] = area
1264
+ groundtruth_dict[standard_fields.InputDataFields.groundtruth_keypoints] = gtkeypoints[...,:2]
1265
+ groundtruth_dict[standard_fields.InputDataFields.groundtruth_keypoint_visibilities] = gtkeypoints[...,2]
1266
+ self.coco_evaluator.add_single_ground_truth_image_info(
1267
+ image_id=str(image_id),
1268
+ groundtruth_dict=groundtruth_dict)
1269
+
1270
+ def add_detection(self, image_id,kps,scores):
1271
+ if not isinstance(kps,np.ndarray):
1272
+ kps = np.array(kps)
1273
+ if not isinstance(scores,np.ndarray):
1274
+ scores = np.array(scores)
1275
+
1276
+ if kps.shape[0]>0:
1277
+ detections_dict={
1278
+ standard_fields.DetectionResultFields.detection_boxes:np.zeros([kps.shape[0],4],dtype=np.float32),
1279
+ standard_fields.DetectionResultFields.detection_scores: scores,
1280
+ standard_fields.DetectionResultFields.detection_keypoints: kps[...,:2],
1281
+ standard_fields.DetectionResultFields.detection_classes:np.ones([kps.shape[0]],dtype=np.int32),
1282
+ }
1283
+ self.coco_evaluator.add_single_detected_image_info(
1284
+ image_id=str(image_id),
1285
+ detections_dict=detections_dict)
1286
+
1287
+ def num_examples(self):
1288
+ if '_image_ids_with_detections' in self.coco_evaluator.__dict__:
1289
+ return len(self.coco_evaluator._image_ids_with_detections)
1290
+ elif '_image_ids' in self.coco_evaluator.__dict__:
1291
+ return len(self.coco_evaluator._image_ids)
1292
+ else:
1293
+ raise RuntimeError("Error evaluator type.")
1294
+
1295
+ def evaluate(self):
1296
+ print(f"Test size {self.num_examples()}")
1297
+ res = self.coco_evaluator.evaluate()
1298
+ for k,v in res.items():
1299
+ index = k.find("/")
1300
+ if index>0:
1301
+ k = k[index+1:]
1302
+ self.cached_values[k] = v
1303
+ return res
1304
+
1305
+ def show(self,name=""):
1306
+ sys.stdout.flush()
1307
+ print(f"Test size {self.num_examples()}")
1308
+ res = self.coco_evaluator.evaluate()
1309
+ str0 = "|配置|"
1310
+ str1 = "|---|"
1311
+ str2 = f"|{name}|"
1312
+ for k,v in res.items():
1313
+ index = k.find("/")
1314
+ if index>0:
1315
+ k = k[index+1:]
1316
+ self.cached_values[k] = v
1317
+ str0 += f"{k}|"
1318
+ str1 += "---|"
1319
+ str2 += f"{v:.3f}|"
1320
+ print(str0)
1321
+ print(str1)
1322
+ print(str2)
1323
+ sys.stdout.flush()
1324
+ return res
1325
+
1326
+ def to_string(self):
1327
+ if 'mAP' in self.cached_values and 'mAP@.50IOU' in self.cached_values:
1328
+ return f"{self.cached_values['mAP']:.3f}/{self.cached_values['mAP@.50IOU']:.3f}"
1329
+ else:
1330
+ return f"N.A."
1331
+
1332
+ @METRICS_REGISTRY.register()
1333
+ class ClassesWiseModelPerformace(BaseMetrics):
1334
+ def __init__(self,num_classes,threshold=0.5,classes_begin_value=1,model_type=COCOEvaluation,
1335
+ model_args={},
1336
+ label_trans=None,
1337
+ classes=None,
1338
+ **kwargs):
1339
+ super().__init__(**kwargs)
1340
+ self.num_classes = num_classes
1341
+ self.clases_begin_value = classes_begin_value
1342
+ model_args['classes_begin_value'] = classes_begin_value
1343
+
1344
+ if isinstance(model_type,(str,bytes)):
1345
+ print(model_type)
1346
+ model_type = METRICS_REGISTRY.get(model_type)
1347
+ print(model_type)
1348
+ print(model_type)
1349
+
1350
+ if classes is None:
1351
+ classes = [f"C{i+1}" for i in range(num_classes)]
1352
+
1353
+ self.classes = classes
1354
+
1355
+ self.data = []
1356
+ for i in range(self.num_classes):
1357
+ self.data.append(model_type(num_classes=num_classes,**model_args))
1358
+ self.mp = model_type(num_classes=num_classes,**model_args)
1359
+ self.label_trans = label_trans
1360
+ self.have_data = np.zeros([num_classes],dtype=np.bool)
1361
+
1362
+ @staticmethod
1363
+ def select_bboxes_and_labels(bboxes,labels,classes):
1364
+ if len(labels) == 0:
1365
+ return np.array([],dtype=np.float32),np.array([],dtype=np.int32),np.array([],dtype=np.bool)
1366
+ if not isinstance(labels,np.ndarray):
1367
+ labels = np.array(labels)
1368
+ mask = np.equal(labels,classes)
1369
+ rbboxes = bboxes[mask,:]
1370
+ rlabels = labels[mask]
1371
+ return rbboxes,rlabels,mask
1372
+
1373
+ def __call__(self, gtboxes,gtlabels,boxes,labels,probability=None,img_size=None,use_relative_coord=False,is_crowd=None):
1374
+ if not isinstance(gtboxes,np.ndarray):
1375
+ gtboxes = np.array(gtboxes)
1376
+ if not isinstance(gtlabels,np.ndarray):
1377
+ gtlabels = np.array(gtlabels)
1378
+ if not isinstance(labels,np.ndarray):
1379
+ labels = np.array(labels)
1380
+ if self.label_trans is not None:
1381
+ gtlabels = self.label_trans(gtlabels)
1382
+ labels = self.label_trans(labels)
1383
+
1384
+ if is_crowd is not None and not isinstance(is_crowd,np.ndarray):
1385
+ is_crowd = np.array(is_crowd)
1386
+
1387
+ for i in range(self.num_classes):
1388
+ classes = i+self.clases_begin_value
1389
+ lgtboxes,lgtlabels,lgtmask = self.select_bboxes_and_labels(gtboxes,gtlabels,classes)
1390
+ lboxes,llabels,lmask = self.select_bboxes_and_labels(boxes,labels,classes)
1391
+ if is_crowd is not None:
1392
+ lis_crowd = is_crowd[lgtmask]
1393
+ else:
1394
+ lis_crowd = None
1395
+ if probability is not None:
1396
+ lprobs = probability[lmask]
1397
+ else:
1398
+ lprobs = None
1399
+ if (lgtlabels.shape[0]==0) and (llabels.shape[0] ==0):
1400
+ continue
1401
+ self.have_data[i] = True
1402
+ self.data[i](lgtboxes,lgtlabels,lboxes,llabels,lprobs,img_size=img_size,use_relative_coord=use_relative_coord,is_crowd=lis_crowd)
1403
+
1404
+ self._current_info = ""
1405
+
1406
+ return self.mp(gtboxes,gtlabels,boxes,labels,probability,is_crowd=is_crowd,img_size=img_size,
1407
+ use_relative_coord=use_relative_coord)
1408
+
1409
+ def show(self):
1410
+ sys.stdout.flush()
1411
+ for i in range(self.num_classes):
1412
+ if not self.have_data[i]:
1413
+ continue
1414
+ classes = i+self.clases_begin_value
1415
+ print(f"Classes:{classes}")
1416
+ try:
1417
+ self.data[i].show()
1418
+ except:
1419
+ print("N.A.")
1420
+ pass
1421
+ self.classes_wise_results = {}
1422
+ sys.stdout.flush()
1423
+ print(f"---------------------------------------------------------------")
1424
+ print(f"All classes")
1425
+ sys.stdout.flush()
1426
+ self.mp.show()
1427
+ sys.stdout.flush()
1428
+ print(f"Per classes")
1429
+ str0 = "|配置|"
1430
+ str1 = "|---|"
1431
+ str2 = f"|{self.cfg_name}|"
1432
+
1433
+
1434
+ #总体
1435
+ str0 += f"ALL|"
1436
+ str1 += "---|"
1437
+ str2 += f"{str(self.mp.to_string())}|"
1438
+
1439
+ for i in range(self.num_classes):
1440
+ classes_id = i+1
1441
+ str0 += f"{self.classes[i]}|"
1442
+ str1 += "---|"
1443
+ str2 += f"{str(self.data[i].to_string())}|"
1444
+ try:
1445
+ keys = ['mAP', 'mAP (small)', 'mAP (medium)', 'mAP (large)']
1446
+ if hasattr(self.data[i], "box_evaluator"):
1447
+ d = self.data[i].box_evaluator.cached_values
1448
+ else:
1449
+ d = self.data[i].cached_values
1450
+ values = [d[k] for k in keys]
1451
+ self.classes_wise_results[classes_id] = values
1452
+ except:
1453
+ self.classes_wise_results[classes_id] = [-1.0]*len(keys)
1454
+ print(str0)
1455
+ print(str1)
1456
+ print(str2)
1457
+ #wmlu.show_dict(self.classes_wise_results,format="{:.3f}")
1458
+ print("Summary")
1459
+ wmlu.show_dict(self.classes_wise_results)
1460
+ sys.stdout.flush()
1461
+ return str2
1462
+
1463
+ def __getattr__(self, item):
1464
+ if item=="mAP":
1465
+ return self.mp.mAP
1466
+ elif item =="recall":
1467
+ return self.mp.recall
1468
+ elif item=="precision":
1469
+ return self.mp.precision
1470
+
1471
+ def evaluate(self):
1472
+ [d.evaluate() for d in self.data]
1473
+ self.mp.evaluate()
1474
+
1475
+ def detail_valus(self):
1476
+ return [d.detail_valus() for d in self.data]
1477
+
1478
+ @METRICS_REGISTRY.register()
1479
+ class SizeWiseModelPerformace(BaseMetrics):
1480
+ def __init__(self,num_classes,sizes=[100],threshold=0.5,classes_begin_value=1,model_type=COCOEvaluation,
1481
+ model_args={},
1482
+ label_trans=None,
1483
+ **kwargs):
1484
+ super().__init__(**kwargs)
1485
+ self.num_classes = num_classes
1486
+ self.clases_begin_value = classes_begin_value
1487
+ model_args['classes_begin_value'] = classes_begin_value
1488
+
1489
+ if isinstance(model_type,(str,bytes)):
1490
+ model_type = METRICS_REGISTRY.get(model_type)
1491
+
1492
+ self.sizes = sizes
1493
+ self.data = []
1494
+ for i in range(len(sizes)+1):
1495
+ self.data.append(model_type(num_classes=num_classes,**model_args))
1496
+ self.mp = model_type(num_classes=num_classes,**model_args)
1497
+ self.label_trans = label_trans
1498
+ self.have_data = np.zeros([len(self.data)],dtype=np.bool)
1499
+
1500
+ def get_idx(self,gtbboxes):
1501
+ '''
1502
+ 根据最大bboxes的大小确定
1503
+ '''
1504
+ wh = gtbboxes[...,2:]-gtbboxes[...,:2]
1505
+ size = np.max(wh)
1506
+ for i,s in enumerate(self.sizes):
1507
+ if size<s:
1508
+ return i
1509
+ return len(self.sizes)
1510
+
1511
+ def __call__(self, gtboxes,gtlabels,boxes,labels,probability=None,img_size=None,use_relative_coord=False,is_crowd=None):
1512
+ if not isinstance(gtboxes,np.ndarray):
1513
+ gtboxes = np.array(gtboxes)
1514
+ if not isinstance(gtlabels,np.ndarray):
1515
+ gtlabels = np.array(gtlabels)
1516
+ if not isinstance(labels,np.ndarray):
1517
+ labels = np.array(labels)
1518
+ if self.label_trans is not None:
1519
+ gtlabels = self.label_trans(gtlabels)
1520
+ labels = self.label_trans(labels)
1521
+
1522
+ if is_crowd is not None and not isinstance(is_crowd,np.ndarray):
1523
+ is_crowd = np.array(is_crowd)
1524
+
1525
+
1526
+ idx = self.get_idx(gtboxes)
1527
+ self.data[idx](gtboxes,gtlabels,boxes,labels,probability,is_crowd=is_crowd,img_size=img_size,
1528
+ use_relative_coord=use_relative_coord)
1529
+ self.have_data[idx] = True
1530
+
1531
+ self._current_info = ""
1532
+
1533
+ return self.mp(gtboxes,gtlabels,boxes,labels,probability,is_crowd=is_crowd,img_size=img_size,
1534
+ use_relative_coord=use_relative_coord)
1535
+
1536
+ def show(self):
1537
+ sys.stdout.flush()
1538
+ names = []
1539
+ for i,s in enumerate(self.sizes):
1540
+ names.append(f"<={s}")
1541
+ names.append(f">{self.sizes[-1]}")
1542
+ for i in range(len(self.data)):
1543
+ if not self.have_data[i]:
1544
+ continue
1545
+ print(f"{names[i]}")
1546
+ try:
1547
+ self.data[i].show()
1548
+ except:
1549
+ print("N.A.")
1550
+ pass
1551
+ sys.stdout.flush()
1552
+ print(f"---------------------------------------------------------------")
1553
+ print(f"All sizes")
1554
+ sys.stdout.flush()
1555
+ self.mp.show()
1556
+ sys.stdout.flush()
1557
+ print(f"Per sizes")
1558
+ str0 = "|配置|"
1559
+ str1 = "|---|"
1560
+ str2 = f"|{self.cfg_name}|"
1561
+
1562
+
1563
+ #总体
1564
+ str0 += f"ALL|"
1565
+ str1 += "---|"
1566
+ str2 += f"{str(self.mp.to_string())}|"
1567
+
1568
+ for i in range(len(self.data)):
1569
+ str0 += f"{names[i]}|"
1570
+ str1 += "---|"
1571
+ str2 += f"{str(self.data[i].to_string())}|"
1572
+ print(str0)
1573
+ print(str1)
1574
+ print(str2)
1575
+ #wmlu.show_dict(self.classes_wise_results,format="{:.3f}")
1576
+ sys.stdout.flush()
1577
+ return str2
1578
+
1579
+ def __getattr__(self, item):
1580
+ if item=="mAP":
1581
+ return self.mp.mAP
1582
+ elif item =="recall":
1583
+ return self.mp.recall
1584
+ elif item=="precision":
1585
+ return self.mp.precision
1586
+
1587
+ @METRICS_REGISTRY.register()
1588
+ class SubsetsModelPerformace(BaseMetrics):
1589
+ def __init__(self, num_classes, sub_sets,threshold=0.5, model_type=COCOEvaluation, classes_begin_value=1,model_args={},
1590
+ label_trans=None,
1591
+ **kwargs):
1592
+ '''
1593
+
1594
+ :param num_classes: 不包含背景
1595
+ :param sub_sets: list(list):如[[1,2],[3,4,5]]表示label 1,2一组进行评估,label 3 ,4,5一组进行评估
1596
+ :param threshold:
1597
+ :param classes_begin_value:
1598
+ :param model_type:
1599
+ :param model_args:
1600
+ :param label_trans:
1601
+ '''
1602
+ model_args['classes_begin_value'] = classes_begin_value
1603
+ self.num_classes = num_classes
1604
+ self.data = []
1605
+ self.sub_sets = sub_sets
1606
+ for i in range(len(sub_sets)):
1607
+ self.data.append(model_type(num_classes=num_classes, **model_args))
1608
+ self.mp = model_type(num_classes=num_classes, **model_args)
1609
+ self.label_trans = label_trans
1610
+
1611
+ @staticmethod
1612
+ def select_bboxes_and_labels(bboxes, labels, classes):
1613
+ if len(labels) == 0:
1614
+ return np.array([], dtype=np.float32), np.array([], dtype=np.int32), np.array([], dtype=np.bool)
1615
+
1616
+ if not isinstance(labels, np.ndarray):
1617
+ labels = np.array(labels)
1618
+ mask = np.zeros_like(labels, dtype=np.bool)
1619
+ for i,l in enumerate(labels):
1620
+ if l in classes:
1621
+ mask[i] = True
1622
+ rbboxes = bboxes[mask, :]
1623
+ rlabels = labels[mask]
1624
+ return rbboxes, rlabels,mask
1625
+
1626
+ def __call__(self, gtboxes, gtlabels, boxes, labels, probability=None, img_size=None):
1627
+ if not isinstance(gtboxes, np.ndarray):
1628
+ gtboxes = np.array(gtboxes)
1629
+ if not isinstance(gtlabels, np.ndarray):
1630
+ gtlabels = np.array(gtlabels)
1631
+ if not isinstance(labels, np.ndarray):
1632
+ labels = np.array(labels)
1633
+ if self.label_trans is not None:
1634
+ gtlabels = self.label_trans(gtlabels)
1635
+ labels = self.label_trans(labels)
1636
+
1637
+ for i,sub_set_labels in enumerate(self.sub_sets):
1638
+ lgtboxes, lgtlabels,_ = self.select_bboxes_and_labels(gtboxes, gtlabels, sub_set_labels)
1639
+ lboxes, llabels,lmask = self.select_bboxes_and_labels(boxes, labels, sub_set_labels)
1640
+ if probability is not None:
1641
+ lprobs = probability[lmask]
1642
+ else:
1643
+ lprobs = None
1644
+ if lgtlabels.shape[0] == 0:
1645
+ continue
1646
+ self.data[i](lgtboxes, lgtlabels, lboxes, llabels, lprobs,img_size=img_size)
1647
+ return self.mp(gtboxes, gtlabels, boxes, labels)
1648
+
1649
+ def current_info(self):
1650
+ return self.mp.current_info()
1651
+
1652
+ def show(self):
1653
+ for i,sub_set_labels in enumerate(self.sub_sets):
1654
+ print(f"Classes:{sub_set_labels}")
1655
+ self.data[i].show()
1656
+ str0 = "|配置|"
1657
+ str1 = "|---|"
1658
+ str2 = "||"
1659
+ for i,sub_set_labels in enumerate(self.sub_sets):
1660
+ str0 += f"S{sub_set_labels}|"
1661
+ str1 += "---|"
1662
+ str2 += f"{str(self.data[i].to_string())}|"
1663
+ print(str0)
1664
+ print(str1)
1665
+ print(str2)
1666
+
1667
+ def __getattr__(self, item):
1668
+ if item == "mAP":
1669
+ return self.mp.mAP
1670
+ elif item == "recall":
1671
+ return self.mp.recall
1672
+ elif item == "precision":
1673
+ return self.mp.precision
1674
+
1675
+ @METRICS_REGISTRY.register()
1676
+ class MeanIOU(BaseMetrics):
1677
+ def __init__(self,num_classes,*args,**kwargs):
1678
+ self.intersection = np.zeros(shape=[num_classes],dtype=np.int64)
1679
+ self.union = np.zeros(shape=[num_classes],dtype=np.int64)
1680
+ self.num_classes = num_classes
1681
+
1682
+ def get_per_classes_iou(self):
1683
+ return self.intersection/np.maximum(self.union,1e-8)
1684
+
1685
+ def get_mean_iou(self):
1686
+ return np.mean(self.get_per_classes_iou())
1687
+
1688
+
1689
+ def __call__(self, gtlabels,predictions):
1690
+ all_equal = np.equal(gtlabels,predictions)
1691
+ for i in range(1,self.num_classes+1):
1692
+ mask = np.equal(gtlabels,i)
1693
+ t_int = np.sum(all_equal[mask].astype(np.int64))
1694
+ t_data0 = np.sum(np.equal(gtlabels,i).astype(np.int64))
1695
+ t_data1 = np.sum(np.equal(predictions,i).astype(np.int64))
1696
+ t_union = t_data0+t_data1-t_int
1697
+ self.intersection[i-1] += t_int
1698
+ self.union[i-1] += t_union
1699
+
1700
+
1701
+ def show(self,name):
1702
+ str0 = "|配置|mIOU|"
1703
+ str1 = "|---|---|"
1704
+ str2 = f"|{name}|{self.get_mean_iou():.4f}"
1705
+ data = self.get_per_classes_iou()
1706
+ for i in range(self.num_classes):
1707
+ str0 += f"C{i+1}|"
1708
+ str1 += "---|"
1709
+ str2 += f"{data[i]:.3f}|"
1710
+ print(str0)
1711
+ print(str1)
1712
+ print(str2)
1713
+
1714
+ def coco_keypoint_eval_file(gt_file, res_file):
1715
+ coco = COCO(gt_file)
1716
+ coco_dt = coco.loadRes(res_file)
1717
+ coco_eval = COCOeval(coco, coco_dt, 'keypoints')
1718
+ coco_eval.params.useSegm = None
1719
+ coco_eval.evaluate()
1720
+ coco_eval.accumulate()
1721
+ coco_eval.summarize()
1722
+
1723
+ stats_names = ['AP', 'Ap .5', 'AP .75', 'AP (M)', 'AP (L)', 'AR', 'AR .5', 'AR .75', 'AR (M)', 'AR (L)']
1724
+
1725
+ info_str = []
1726
+ for ind, name in enumerate(stats_names):
1727
+ info_str.append((name, coco_eval.stats[ind]))
1728
+
1729
+ return info_str
1730
+
1731
+ def coco_bbox_eval_file(gt_file, res_file):
1732
+ coco = COCO(gt_file)
1733
+ coco_dt = coco.loadRes(res_file)
1734
+ coco_eval = COCOeval(coco, coco_dt, 'bbox')
1735
+ coco_eval.params.useSegm = None
1736
+ coco_eval.evaluate()
1737
+ coco_eval.accumulate()
1738
+ coco_eval.summarize()
1739
+
1740
+ stats_names = ['AP', 'Ap .5', 'AP .75', 'AP (M)', 'AP (L)', 'AR', 'AR .5', 'AR .75', 'AR (M)', 'AR (L)']
1741
+
1742
+ info_str = []
1743
+ for ind, name in enumerate(stats_names):
1744
+ info_str.append((name, coco_eval.stats[ind]))
1745
+
1746
+ return info_str
1747
+
1748
+ @METRICS_REGISTRY.register()
1749
+ class WMAP(BaseMetrics):
1750
+ def __init__(self,categories_list=None,num_classes=None,mask_on=False,label_trans=None,classes_begin_value=1,threshold=0.5):
1751
+ if categories_list is None:
1752
+ print(f"WARNING: Use default categories list, start classes is {classes_begin_value}")
1753
+ self.categories_list = [{"id":x+classes_begin_value,"name":str(x+classes_begin_value)} for x in range(num_classes)]
1754
+ else:
1755
+ self.categories_list = categories_list
1756
+ self.label_trans = label_trans
1757
+ self.image_id = 0
1758
+ self.num_classes = num_classes
1759
+ self.cached_values = {}
1760
+ self.a_gtbboxes = []
1761
+ self.a_gtlabels = []
1762
+ self.a_bboxes = []
1763
+ self.a_labels = []
1764
+ self.a_scores = []
1765
+ self.threshold = 0.5
1766
+ self.map = None
1767
+ '''
1768
+ gtboxes:[N,4]
1769
+ gtlabels:[N]
1770
+ img_size:[H,W]
1771
+ gtmasks:[N,H,W]
1772
+ '''
1773
+ def __call__(self, gtboxes,gtlabels,boxes,labels,probability=None,img_size=[512,512],
1774
+ gtmasks=None,
1775
+ masks=None,is_crowd=None,use_relative_coord=False):
1776
+ if probability is None:
1777
+ probability = np.ones_like(labels,dtype=np.float32)
1778
+ if not isinstance(gtboxes,np.ndarray):
1779
+ gtboxes = np.array(gtboxes)
1780
+ if not isinstance(gtlabels,np.ndarray):
1781
+ gtlabels = np.array(gtlabels)
1782
+ if not isinstance(boxes,np.ndarray):
1783
+ boxes = np.array(boxes)
1784
+ if not isinstance(labels,np.ndarray):
1785
+ labels = np.array(labels)
1786
+ if self.label_trans is not None:
1787
+ gtlabels = self.label_trans(gtlabels)
1788
+ labels = self.label_trans(labels)
1789
+ if probability is not None and not isinstance(probability,np.ndarray):
1790
+ probability = np.array(probability)
1791
+
1792
+ cur_ap = getmAP(gtboxes=gtboxes,
1793
+ gtlabels=gtlabels,
1794
+ boxes=boxes,
1795
+ labels=labels,
1796
+ probability=probability,
1797
+ is_crowd=is_crowd)
1798
+ self._current_info = f"ap={cur_ap}"
1799
+
1800
+ if gtlabels.shape[0]>0:
1801
+ if use_relative_coord:
1802
+ gtboxes = gtboxes*[[img_size[0],img_size[1],img_size[0],img_size[1]]]
1803
+ gtlabels = gtlabels+self.image_id*self.num_classes
1804
+ self.a_gtbboxes.append(gtboxes)
1805
+ self.a_gtlabels.append(gtlabels)
1806
+ if labels.shape[0]>0 and gtlabels.shape[0]>0:
1807
+ if use_relative_coord:
1808
+ boxes = boxes*[[img_size[0],img_size[1],img_size[0],img_size[1]]]
1809
+ labels = gtlabels+self.image_id*self.num_classes
1810
+ self.a_bboxes.append(boxes)
1811
+ self.a_labels.append(labels)
1812
+ self.a_scores.append(probability)
1813
+ self.image_id += 1
1814
+
1815
+ def num_examples(self):
1816
+ return self.image_id
1817
+
1818
+ def evaluate(self):
1819
+ print(f"Test size {self.num_examples()}")
1820
+ gtlabels = np.stack(self.a_gtbboxes,axis=0)
1821
+ gtbboxes = np.stack(self.a_gtbboxes,axis=0)
1822
+ labels = np.stack(self.a_labels,axis=0)
1823
+ bboxes = np.stack(self.a_bboxes,axis=0)
1824
+ scores = np.stack(self.a_scores,axis=0)
1825
+ self.map = getmAP(gtbboxes,gtlabels,bboxes,labels,scores,threshold=self.threshold)
1826
+
1827
+ def show(self,name=""):
1828
+ sys.stdout.flush()
1829
+ print(f"Test size {self.num_examples()}")
1830
+ print(f"mAP={self.map}")
1831
+ return self.map
1832
+
1833
+ def to_string(self):
1834
+ return self.map
1835
+
1836
+ def __repr__(self):
1837
+ return self.to_string()
1838
+
1839
+ def value(self):
1840
+ return self.map
1841
+
1842
+ @METRICS_REGISTRY.register()
1843
+ class DetConfusionMatrix(BaseMetrics):
1844
+ def __init__(self,categories_list=None,num_classes=None,mask_on=False,label_trans=None,classes_begin_value=1,score_thr=0.5,threshold=0.3):
1845
+ super().__init__()
1846
+ if categories_list is None:
1847
+ print(f"WARNING: Use default categories list, start classes is {classes_begin_value}")
1848
+ self.categories_list = [{"id":x+classes_begin_value,"name":str(x+classes_begin_value)} for x in range(num_classes)]
1849
+ else:
1850
+ self.categories_list = categories_list
1851
+ self.num_classes = num_classes
1852
+ self.label_trans = label_trans
1853
+ self.iou_thr = threshold
1854
+ self.score_thr = score_thr
1855
+ self.pred_labels = []
1856
+ self.gt_labels = []
1857
+ self.kernel = ConfusionMatrix(num_classes=self.num_classes+1)
1858
+ self.image_id = 0
1859
+
1860
+ def __call__(self, gtboxes,gtlabels,boxes,labels,probability=None,img_size=[512,512],
1861
+ gtmasks=None,
1862
+ masks=None,is_crowd=None,use_relative_coord=False):
1863
+ if probability is None:
1864
+ probability = np.ones_like(labels,dtype=np.float32)
1865
+ if not isinstance(gtboxes,np.ndarray):
1866
+ gtboxes = np.array(gtboxes)
1867
+ if not isinstance(gtlabels,np.ndarray):
1868
+ gtlabels = np.array(gtlabels)
1869
+ if not isinstance(boxes,np.ndarray):
1870
+ boxes = np.array(boxes)
1871
+ if not isinstance(labels,np.ndarray):
1872
+ labels = np.array(labels)
1873
+ if self.label_trans is not None:
1874
+ gtlabels = self.label_trans(gtlabels)
1875
+ labels = self.label_trans(labels)
1876
+ if probability is not None and not isinstance(probability,np.ndarray):
1877
+ probability = np.array(probability)
1878
+
1879
+ if self.score_thr is not None:
1880
+ keep = probability>self.score_thr
1881
+ labels = labels[keep]
1882
+ boxes = boxes[keep]
1883
+ probability = probability[keep]
1884
+
1885
+ tmp_pred_labels = []
1886
+ tmp_gt_labels = []
1887
+ gt_set = set(list(range(gtlabels.shape[0])))
1888
+ pred_set = set(list(range(labels.shape[0])))
1889
+
1890
+ if len(gt_set)>0 and len(pred_set)>0:
1891
+ iou_matrix = odb.iou_matrix(boxes,gtboxes) #boxes_nr x gtboxes_nr
1892
+ max_idx = np.argmax(iou_matrix,axis=0)
1893
+ ious = np.max(iou_matrix,axis=0)
1894
+
1895
+ sorted_idx = np.argsort(ious)[::-1]
1896
+
1897
+ for gt_idx in sorted_idx:
1898
+ iou = ious[gt_idx]
1899
+ if iou<self.iou_thr:
1900
+ break
1901
+ pred_idx = max_idx[gt_idx]
1902
+ if pred_idx not in pred_set:
1903
+ continue
1904
+ tmp_gt_labels.append(gtlabels[gt_idx])
1905
+ tmp_pred_labels.append(labels[pred_idx])
1906
+ gt_set.remove(gt_idx)
1907
+ pred_set.remove(pred_idx)
1908
+
1909
+ for idx in gt_set:
1910
+ v = gtlabels[idx]
1911
+ tmp_gt_labels.append(v)
1912
+ tmp_pred_labels.append(self.num_classes) #使用self.num_classes+1作为背景
1913
+
1914
+ for idx in pred_set:
1915
+ v = labels[idx]
1916
+ tmp_gt_labels.append(self.num_classes)
1917
+ tmp_pred_labels.append(v)
1918
+
1919
+ self.pred_labels.extend(tmp_pred_labels)
1920
+ self.gt_labels.extend(tmp_gt_labels)
1921
+ self.image_id += 1
1922
+ #self.show()
1923
+
1924
+ def evaluate(self):
1925
+ print(f"Test size {self.num_examples()}")
1926
+ self.kernel(output=np.array(self.pred_labels,dtype=np.int32),
1927
+ target=np.array(self.gt_labels,dtype=np.int32))
1928
+ self.kernel.evaluate()
1929
+
1930
+ def show(self,name=""):
1931
+ #显示的最后一行,列为背景
1932
+ '''
1933
+ i行,j列: 表示gt类别i被分为类别j的数量
1934
+ '''
1935
+ sys.stdout.flush()
1936
+ print(f"Test size {self.num_examples()}")
1937
+ self.evaluate()
1938
+ return self.kernel.show()
1939
+
1940
+ def to_string(self):
1941
+ return self.kernel.to_string()
1942
+
1943
+ def __repr__(self):
1944
+ return self.to_string()
1945
+
1946
+ def value(self):
1947
+ return self.kernel.value()
1948
+
1949
+ def num_examples(self):
1950
+ return self.image_id
1951
+
1952
+
1953
+