franzi2505 commited on
Commit
9cf5bb9
·
1 Parent(s): 802ca0e

update det-metrics for class-specific computation

Browse files
Files changed (2) hide show
  1. README.md +7 -2
  2. det-metrics.py +60 -4
README.md CHANGED
@@ -61,10 +61,11 @@ results = module.compute()
61
  print(results)
62
  ```
63
 
64
- This will output the following dictionary containing metrics for the detection model. The key of the dictionary will be the model name or "custom" if no model names are available like in this case.
65
 
66
  ```json
67
  {
 
68
  "custom": {
69
  "metrics": ...,
70
  "eval": ...,
@@ -137,6 +138,7 @@ Customize your evaluation by specifying various parameters when loading SEA-AI/d
137
  - **bbox_format**: Set the bounding box format (e.g., `"xywh"`).
138
  - **iou_threshold**: Choose the IOU threshold for determining correct detections.
139
  - **class_agnostic**: Specify whether to calculate metrics disregarding class labels.
 
140
 
141
  ```python
142
  area_ranges_tuples = [
@@ -191,6 +193,8 @@ SEA-AI/det-metrics metrics dictionary provides a detailed breakdown of performan
191
  - **fpi**: Number of images with predictions but no ground truths.
192
  - **nImgs**: Total number of images evaluated.
193
 
 
 
194
  ### Eval
195
 
196
  The SEA-AI/det-metrics evaluation dictionary provides details about evaluation metrics and results. Below is a description of each field:
@@ -244,7 +248,8 @@ The params return value of the COCO evaluation parameters in PyCOCO represents a
244
  - **areaRng**: Object area ranges for evaluation. This parameter defines the sizes of objects to evaluate. It is specified as a list of tuples, where each tuple represents a range of area in square pixels.
245
  - **maxDets**: List of thresholds on maximum detections per image for evaluation. By default, it evaluates with thresholds of 1, 10, and 100 detections per image.
246
  - **iouType**: Type of IoU calculation used for evaluation. It can be ‘segm’ (segmentation), ‘bbox’ (bounding box), or ‘keypoints’.
247
- - **useCats**: Boolean flag indicating whether to use category labels for evaluation (default is 1, meaning true).
 
248
 
249
  > Note:
250
  > If useCats=0 category labels are ignored as in proposal scoring.
 
61
  print(results)
62
  ```
63
 
64
+ This will output the following dictionary containing metrics for the detection model. The key of the dictionary will be the model name or "custom" if no model names are available like in this case. Additionally, there is a single key "classes" which maps the labels to the respective indices of the results. If the results are class agnostic, the value of "classes" is None.
65
 
66
  ```json
67
  {
68
+ "classes": ...
69
  "custom": {
70
  "metrics": ...,
71
  "eval": ...,
 
138
  - **bbox_format**: Set the bounding box format (e.g., `"xywh"`).
139
  - **iou_threshold**: Choose the IOU threshold for determining correct detections.
140
  - **class_agnostic**: Specify whether to calculate metrics disregarding class labels.
141
+ - **label_mapping**: Provide an optional mapping of string labels to numeric labels in the form of a dictionary (e.g., `{"SHIP": 0, "BOAT": 1}`). Defaults to a label mapping defined by the SEA.AI label merging map.
142
 
143
  ```python
144
  area_ranges_tuples = [
 
193
  - **fpi**: Number of images with predictions but no ground truths.
194
  - **nImgs**: Total number of images evaluated.
195
 
196
+ If the det-metrics is computed with `class_agnostic=False`, all counts (`tp/fp/fn/duplicates/support/fpi`) and scores (`precision/recall/f1`) are arrays instead of single numbers. For a label mapping of `{"SHIP": 0, "BOAT": 1}`, a exemplary array could be `tp=np.array([10, 4])`, which means there are 10 true positive ships and 4 true positive boats.
197
+
198
  ### Eval
199
 
200
  The SEA-AI/det-metrics evaluation dictionary provides details about evaluation metrics and results. Below is a description of each field:
 
248
  - **areaRng**: Object area ranges for evaluation. This parameter defines the sizes of objects to evaluate. It is specified as a list of tuples, where each tuple represents a range of area in square pixels.
249
  - **maxDets**: List of thresholds on maximum detections per image for evaluation. By default, it evaluates with thresholds of 1, 10, and 100 detections per image.
250
  - **iouType**: Type of IoU calculation used for evaluation. It can be ‘segm’ (segmentation), ‘bbox’ (bounding box), or ‘keypoints’.
251
+ - **class_agnostic**: Boolean flag indicating whether to use category labels for evaluation (default is 1, meaning true).
252
+ - **label_mapping**: Dict of str: int pairs, mapping string labels to numeric labels, so that the payload labels can be mapped to numeric labels (default is a label mapping defined by the class merging structure). Should be provided only if `class_agnostic=False`.
253
 
254
  > Note:
255
  > If useCats=0 category labels are ignored as in proposal scoring.
det-metrics.py CHANGED
@@ -13,7 +13,7 @@
13
  # limitations under the License.
14
  """TODO: Add a description here."""
15
 
16
- from typing import List, Literal, Tuple
17
 
18
  import datasets
19
  import evaluate
@@ -23,6 +23,43 @@ from seametrics.detection import PrecisionRecallF1Support
23
  from seametrics.detection.utils import payload_to_det_metric
24
  from seametrics.payload import Payload
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  _CITATION = """\
27
  @InProceedings{coco:2020,
28
  title = {Microsoft {COCO:} Common Objects in Context},
@@ -124,6 +161,7 @@ class DetectionMetric(evaluate.Metric):
124
  bbox_format: str = "xywh",
125
  iou_type: Literal["bbox", "segm"] = "bbox",
126
  payload: Payload = None,
 
127
  **kwargs,
128
  ):
129
  super().__init__(**kwargs)
@@ -136,6 +174,12 @@ class DetectionMetric(evaluate.Metric):
136
  self.class_agnostic = class_agnostic
137
  self.iou_type = iou_type
138
  self.bbox_format = bbox_format
 
 
 
 
 
 
139
 
140
  # postprocess parameters
141
  self.iou_thresholds = (
@@ -143,7 +187,7 @@ class DetectionMetric(evaluate.Metric):
143
  )
144
  self.area_ranges = [v for _, v in area_ranges_tuples]
145
  self.area_ranges_labels = [k for k, _ in area_ranges_tuples]
146
-
147
  # initialize coco_metrics
148
  self.coco_metric = PrecisionRecallF1Support(
149
  iou_thresholds=self.iou_thresholds,
@@ -152,6 +196,7 @@ class DetectionMetric(evaluate.Metric):
152
  class_agnostic=self.class_agnostic,
153
  iou_type=self.iou_type,
154
  box_format=self.bbox_format,
 
155
  )
156
 
157
  # initialize evaluation metric
@@ -237,6 +282,7 @@ class DetectionMetric(evaluate.Metric):
237
  """Called within the evaluate.Metric.compute() method"""
238
 
239
  results = {}
 
240
  for model_name in self.model_names:
241
  print(f"\n##### {model_name} #####")
242
  # add payload if available (otherwise predictions and references must be added with add function)
@@ -260,7 +306,7 @@ class DetectionMetric(evaluate.Metric):
260
  """Converts the payload to the format expected by the metric"""
261
  # import only if needed since fiftyone is not a direct dependency
262
 
263
- predictions, references = payload_to_det_metric(payload, model_name)
264
  self.add(prediction=predictions, reference=references)
265
 
266
  return self
@@ -312,6 +358,11 @@ class DetectionMetric(evaluate.Metric):
312
  import plotly.graph_objects as go
313
  from seametrics.detection.utils import get_confidence_metric_vals
314
 
 
 
 
 
 
315
  # Create traces
316
  fig = go.Figure()
317
  metrics = ["precision", "recall", "f1"]
@@ -377,6 +428,11 @@ class DetectionMetric(evaluate.Metric):
377
  wandb: To interact with the Weights and Biases platform.
378
  datetime: To generate a timestamp for run names.
379
  """
 
 
 
 
 
380
  import os
381
  import wandb
382
  import datetime
@@ -448,7 +504,7 @@ class DetectionMetric(evaluate.Metric):
448
  Note:
449
  - If the metric does not support area ranges, the metric should store the results under the `all` key.
450
  - If a range area is provided it will be displayed in the output. if area_ranges_tuples is None, then all the area ranges will be displayed
451
- """
452
  results = {}
453
 
454
  for model_name in payload.models:
 
13
  # limitations under the License.
14
  """TODO: Add a description here."""
15
 
16
+ from typing import List, Literal, Tuple, Dict
17
 
18
  import datasets
19
  import evaluate
 
23
  from seametrics.detection.utils import payload_to_det_metric
24
  from seametrics.payload import Payload
25
 
26
+ LABEL_MAPPING = {
27
+ 'SHIP': 0,
28
+ 'BATTLE_SHIP': 0,
29
+ 'FISHING_SHIP': 0,
30
+ 'CONTAINER_SHIP': 0,
31
+ 'CRUISE_SHIP': 0,
32
+ 'BOAT_WITHOUT_SAILS': 1,
33
+ 'MOTORBOAT': 1,
34
+ 'MARITIME_VEHICLE': 1,
35
+ 'BOAT': 1,
36
+ 'SAILING_BOAT': 2,
37
+ 'SAILING_BOAT_WITH_CLOSED_SAILS': 2,
38
+ 'SAILING_BOAT_WITH_OPEN_SAILS': 2,
39
+ 'LEISURE_VEHICLE': 3,
40
+ 'WATER_SKI': 3,
41
+ 'BUOY': 4,
42
+ 'CONSTRUCTION': 4,
43
+ 'FISHING_BUOY': 4,
44
+ 'HARBOUR_BUOY': 4,
45
+ 'FLOTSAM': 5,
46
+ 'CONTAINER': 5,
47
+ 'SEA_MINE': 5,
48
+ 'WOODEN_LOG': 5,
49
+ 'UNKNOWN': 5,
50
+ 'HUMAN_IN_WATER': 5,
51
+ 'FAR_AWAY_OBJECT': 6,
52
+ 'MARITIME_ANIMAL': 7,
53
+ 'ANIMAL': 7,
54
+ 'FISH': 7,
55
+ 'DOLPHIN': 7,
56
+ 'MAMMAL': 7,
57
+ 'WHALE': 7,
58
+ 'AERIAL_ANIMAL': 8,
59
+ 'SEAGULL': 8,
60
+ 'BIRD': 8,
61
+ }
62
+
63
  _CITATION = """\
64
  @InProceedings{coco:2020,
65
  title = {Microsoft {COCO:} Common Objects in Context},
 
161
  bbox_format: str = "xywh",
162
  iou_type: Literal["bbox", "segm"] = "bbox",
163
  payload: Payload = None,
164
+ label_mapping: Dict[str, int] = None,
165
  **kwargs,
166
  ):
167
  super().__init__(**kwargs)
 
174
  self.class_agnostic = class_agnostic
175
  self.iou_type = iou_type
176
  self.bbox_format = bbox_format
177
+ self.label_mapping = LABEL_MAPPING if not self.class_agnostic else None
178
+ if not class_agnostic:
179
+ if label_mapping:
180
+ print("WARNING: overwritting the default label mapping with the \
181
+ custom label mapping provided via `label_mapping`.")
182
+ self.label_mapping = label_mapping
183
 
184
  # postprocess parameters
185
  self.iou_thresholds = (
 
187
  )
188
  self.area_ranges = [v for _, v in area_ranges_tuples]
189
  self.area_ranges_labels = [k for k, _ in area_ranges_tuples]
190
+
191
  # initialize coco_metrics
192
  self.coco_metric = PrecisionRecallF1Support(
193
  iou_thresholds=self.iou_thresholds,
 
196
  class_agnostic=self.class_agnostic,
197
  iou_type=self.iou_type,
198
  box_format=self.bbox_format,
199
+ labels=sorted(list(set(list(self.label_mapping.values())))) if self.label_mapping else None,
200
  )
201
 
202
  # initialize evaluation metric
 
282
  """Called within the evaluate.Metric.compute() method"""
283
 
284
  results = {}
285
+ results["classes"] = self.label_mapping
286
  for model_name in self.model_names:
287
  print(f"\n##### {model_name} #####")
288
  # add payload if available (otherwise predictions and references must be added with add function)
 
306
  """Converts the payload to the format expected by the metric"""
307
  # import only if needed since fiftyone is not a direct dependency
308
 
309
+ predictions, references = payload_to_det_metric(payload, model_name, class_agnostic=self.class_agnostic, label_mapping=self.label_mapping)
310
  self.add(prediction=predictions, reference=references)
311
 
312
  return self
 
358
  import plotly.graph_objects as go
359
  from seametrics.detection.utils import get_confidence_metric_vals
360
 
361
+ if not self.class_agnostic:
362
+ raise ValueError(
363
+ "This method is not yet implemented for `self.class_agnostic=False`."
364
+ )
365
+
366
  # Create traces
367
  fig = go.Figure()
368
  metrics = ["precision", "recall", "f1"]
 
428
  wandb: To interact with the Weights and Biases platform.
429
  datetime: To generate a timestamp for run names.
430
  """
431
+ if not self.class_agnostic:
432
+ raise ValueError(
433
+ "This method is not yet implemented for `self.class_agnostic=False`."
434
+ )
435
+
436
  import os
437
  import wandb
438
  import datetime
 
504
  Note:
505
  - If the metric does not support area ranges, the metric should store the results under the `all` key.
506
  - If a range area is provided it will be displayed in the output. if area_ranges_tuples is None, then all the area ranges will be displayed
507
+ """
508
  results = {}
509
 
510
  for model_name in payload.models: