[email protected] commited on
Commit
930daa4
·
1 Parent(s): 6b2433c

metric works, not fully integrated

Browse files
__pycache__/box_metrics.cpython-39.pyc ADDED
Binary file (5.9 kB). View file
 
__pycache__/utils.cpython-39.pyc ADDED
Binary file (4.85 kB). View file
 
box_metrics.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import evaluate
2
+ import datasets
3
+ import motmetrics as mm
4
+ import numpy as np
5
+ from seametrics.payload import Payload
6
+ import torch
7
+ from utils import bbox_iou, bbox_bep
8
+ import datasets
9
+
10
+ # _DESCRIPTION = """\
11
+ # The box-metrics package provides a set of metrics to evaluate
12
+ # the performance of object detection algorithms in ther of sizing and positioning
13
+ # of the bounding boxes."""
14
+
15
+ # _KWARGS_DESCRIPTION = """
16
+ # Calculates how good are predictions given some references, using certain scores
17
+ # Args:
18
+ # predictions: list of predictions to score. Each predictions
19
+ # should be a string with tokens separated by spaces.
20
+ # references: list of reference for each prediction. Each
21
+ # reference should be a string with tokens separated by spaces.
22
+ # max_iou (`float`, *optional*):
23
+ # If specified, this is the minimum Intersection over Union (IoU) threshold to consider a detection as a true positive.
24
+ # Default is 0.5.
25
+ # """
26
+
27
+ # _CITATION = """\
28
+ # @InProceedings{huggingface:module,
29
+ # title = {A great new module},
30
+ # authors={huggingface, Inc.},
31
+ # year={2020}
32
+ # }\
33
+ # @article{milan2016mot16,
34
+ # title={Are object detection assessment criteria ready for maritime computer vision?},
35
+ # author={Dilip K. Prasad1, Deepu Rajan and Chai Quek},
36
+ # journal={arXiv:1809.04659v1},
37
+ # year={2018}
38
+ # }
39
+ # """
40
+
41
+ _CITATION = """\
42
+ @InProceedings{huggingface:module,
43
+ title = {A great new module},
44
+ authors={huggingface, Inc.},
45
+ year={2020}
46
+ }\
47
+ @article{milan2016mot16,
48
+ title={MOT16: A benchmark for multi-object tracking},
49
+ author={Milan, Anton and Leal-Taix{\'e}, Laura and Reid, Ian and Roth, Stefan and Schindler, Konrad},
50
+ journal={arXiv preprint arXiv:1603.00831},
51
+ year={2016}
52
+ }
53
+ """
54
+
55
+ _DESCRIPTION = """\
56
+ The MOT Metrics module is designed to evaluate multi-object tracking (MOT)
57
+ algorithms by computing various metrics based on predicted and ground truth bounding
58
+ boxes. It serves as a crucial tool in assessing the performance of MOT systems,
59
+ aiding in the iterative improvement of tracking algorithms."""
60
+
61
+
62
+ _KWARGS_DESCRIPTION = """
63
+ Calculates how good are predictions given some references, using certain scores
64
+ Args:
65
+ predictions: list of predictions to score. Each predictions
66
+ should be a string with tokens separated by spaces.
67
+ references: list of reference for each prediction. Each
68
+ reference should be a string with tokens separated by spaces.
69
+ max_iou (`float`, *optional*):
70
+ If specified, this is the minimum Intersection over Union (IoU) threshold to consider a detection as a true positive.
71
+ Default is 0.5.
72
+ """
73
+
74
+ # @evaluate.utils.file_utils.add_start_docstrings(_DESCRIPTION, _KWARGS_DESCRIPTION)
75
+ class BoxMetrics(evaluate.Metric):
76
+
77
+ def __init__(self, max_iou: float = 0.01, **kwargs):
78
+ # super().__init__(**kwargs)
79
+ self.max_iou = max_iou
80
+ self.boxes = {}
81
+ self.gt_field = "ground_truth_det"
82
+
83
+
84
+ def _info(self):
85
+ # TODO: Specifies the evaluate.EvaluationModuleInfo object
86
+ return evaluate.MetricInfo(
87
+ # This is the description that will appear on the modules page.
88
+ module_type="metric",
89
+ description=_DESCRIPTION,
90
+ citation=_CITATION,
91
+ inputs_description=_KWARGS_DESCRIPTION,
92
+ # This defines the format of each prediction and reference
93
+ features=datasets.Features({
94
+ "predictions": datasets.Sequence(
95
+ datasets.Sequence(datasets.Value("float"))
96
+ ),
97
+ "references": datasets.Sequence(
98
+ datasets.Sequence(datasets.Value("float"))
99
+ )
100
+ }),
101
+ # Additional links to the codebase or references
102
+ codebase_urls=["http://github.com/path/to/codebase/of/new_module"],
103
+ reference_urls=["http://path.to.reference.url/new_module"]
104
+ )
105
+
106
+
107
+ def add_payload(self, payload: Payload):
108
+ """Convert a payload to the format of the tracking metrics library"""
109
+ self.add(payload)
110
+
111
+ def add(self, payload: Payload):
112
+ self.gt_field = payload.gt_field_name
113
+ for sequence in payload.sequences:
114
+ self.boxes[sequence] = {}
115
+ target = payload.sequences[sequence][self.gt_field]
116
+ resolution = payload.sequences[sequence]["resolution"]
117
+ target_tm = self.payload_labels_to_tm(target, resolution)
118
+ self.boxes[sequence][self.gt_field] = target_tm
119
+
120
+ for model in payload.models:
121
+ preds = payload.sequences[sequence][model]
122
+ preds_tm = self.payload_preds_to_rm(preds, resolution)
123
+ self.boxes[sequence][model] = preds_tm
124
+
125
+ def compute(self):
126
+ """Compute the metric value"""
127
+
128
+ output = {}
129
+
130
+ for sequence in self.boxes:
131
+ ious = []
132
+ beps = []
133
+ bottom_x = []
134
+ bottom_y = []
135
+ widths = []
136
+ heights = []
137
+ output[sequence] = {}
138
+
139
+ target = self.boxes[sequence][self.gt_field]
140
+ for model in self.boxes[sequence]:
141
+ preds = self.boxes[sequence][model]
142
+
143
+ for i in range(len(preds)):
144
+
145
+ target_tm_bbs = target[i][:, 1:]
146
+ pred_tm_bbs = preds[i][:, :4]
147
+
148
+ if target_tm_bbs.shape[0] == 0 or pred_tm_bbs.shape[0] == 0:
149
+ continue
150
+
151
+ for t_box in target_tm_bbs:
152
+ iou = bbox_iou(t_box.unsqueeze(0), pred_tm_bbs, xywh=False)
153
+ bep = bbox_bep(t_box.unsqueeze(0), pred_tm_bbs, xywh=False)
154
+ matches = pred_tm_bbs[iou.squeeze(1) > self.max_iou]
155
+
156
+ bep = bep[iou>self.max_iou]
157
+ iou = iou[iou>self.max_iou]
158
+
159
+ if torch.any(iou <= 0):
160
+ raise ValueError("IoU should be greater than 0, pls contact code maintainer")
161
+ if torch.any(bep <= 0):
162
+ raise ValueError("BEP should be greater than 0, pls contact code maintainer")
163
+
164
+ ious.extend(iou.tolist())
165
+ beps.extend(bep.tolist())
166
+
167
+ for match in matches:
168
+ t_xc = (match[0].item()+match[2].item())/2
169
+ p_xc = (t_box[0].item()+t_box[2].item())/2
170
+ t_w = t_box[2].item()-t_box[0].item()
171
+ p_w = match[2].item()-match[0].item()
172
+ t_h = t_box[3].item()-t_box[1].item()
173
+ p_h = match[3].item()-match[1].item()
174
+
175
+
176
+ bottom_x.append(abs(t_xc-p_xc))
177
+ widths.append(abs(t_w-p_w))
178
+ bottom_y.append(abs(t_box[1].item()-match[1].item()))
179
+ heights.append(abs(t_h-p_h))
180
+
181
+ output[sequence][model] = {
182
+ "iou_mean": np.mean(ious),
183
+ "bep_mean": np.mean(beps),
184
+ "bottom_x_mean": np.mean(bottom_x),
185
+ "bottom_y_mean": np.mean(bottom_y),
186
+ "width_mean": np.mean(widths),
187
+ "height_mean": np.mean(heights),
188
+ "bottom_x_std": np.std(bottom_x),
189
+ "bottom_y_std": np.std(bottom_y),
190
+ "width_std": np.std(widths),
191
+ "height_std": np.std(heights)
192
+ }
193
+ return output
194
+
195
+ @staticmethod
196
+ def payload_labels_to_tm(labels, resolution):
197
+ """Convert the labels of a payload sequence to the format of torch metrics"""
198
+ target_tm = []
199
+ for frame in labels:
200
+ target_tm_frame = []
201
+ for det in frame:
202
+ label = 0
203
+ box = det["bounding_box"]
204
+ x1, y1, x2, y2 = box[0], box[1], box[0]+box[2], box[1]+box[3]
205
+ x1, y1, x2, y2 = x1*resolution.width, y1*resolution.height, x2*resolution.width, y2*resolution.height
206
+ target_tm_frame.append([label, x1, y1, x2, y2])
207
+ target_tm.append(torch.tensor(target_tm_frame) if len(target_tm_frame) > 0 else torch.empty((0, 5)))
208
+
209
+ return target_tm
210
+
211
+ @staticmethod
212
+ def payload_preds_to_rm(preds, resolution):
213
+ """Convert the predictions of a payload sequence to the format of torch metrics"""
214
+ preds_tm = []
215
+ for frame in preds:
216
+ pred_tm_frame = []
217
+ for det in frame:
218
+ label = 0
219
+ box = det["bounding_box"]
220
+ x1, y1, x2, y2 = box[0], box[1], box[0]+box[2], box[1]+box[3]
221
+ x1, y1, x2, y2 = x1*resolution.width, y1*resolution.height, x2*resolution.width, y2*resolution.height
222
+ conf = 1
223
+ pred_tm_frame.append([x1, y1, x2, y2, conf, label])
224
+ preds_tm.append(torch.tensor(pred_tm_frame) if len(pred_tm_frame) > 0 else torch.empty((0, 6)))
225
+
226
+ return preds_tm
227
+
228
+
229
+
compute.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ from utils import BoxMetrics, concat_labels, concat_preds
4
+ import fiftyone as fo
5
+ from seametrics.fo_utils.utils import fo_to_payload
6
+ from const import INDEX_MAPPING, CLASS_MAPPING, INDEX_MAPPING_INV
7
+ from tqdm import tqdm
8
+
9
+ tags = ["WHALES"]
10
+ cameras = ["thermal_narrow"]
11
+
12
+ dataset_name = "SENTRY_VIDEOS_DATASET_QA"
13
+ #dataset_name = "SENTRY_VIDEOS_DATASET_QA"
14
+ model = "cerulean-level-17_11_2023_RL_SPLIT_ep147_CNN"
15
+ det_gt_field = "ground_truth_det"
16
+
17
+ cm = BoxMetrics(nc=10, conf=0, iou_thres=0)
18
+
19
+ if dataset_name == "SAILING_DATASET_QA":
20
+ cameras = ["thermal_left"]
21
+ dataset_view = fo.load_dataset(dataset_name).match_tags(tags).select_group_slices(cameras).filter_labels(f"{model}", True, only_matches=False)
22
+ sequences = dataset_view.distinct("sequence")
23
+ if dataset_name == "SENTRY_VIDEOS_DATASET_QA":
24
+ cameras = ["thermal_wide"]
25
+ dataset_view = fo.load_dataset(dataset_name).match_tags(tags).select_group_slices(cameras).filter_labels(f"frames.{model}", True, only_matches=False)
26
+ sequences = dataset_view.distinct("sequence")
27
+
28
+ for sequence in tqdm(sequences):
29
+ payload = fo_to_payload(dataset = dataset_name,
30
+ gt_field = det_gt_field,
31
+ models = [model],
32
+ tracking_mode = True,
33
+ sequence_list = [sequence],
34
+ excluded_classes = ["BIRD"],)
35
+
36
+ target = payload["sequences"][sequence][det_gt_field]
37
+ preds = payload["sequences"][sequence][model]
38
+ resolution = payload["sequences"][sequence]["resolution"]
39
+ target_tm = []
40
+ preds_tm = []
41
+ for frame in target:
42
+ target_tm_batch = []
43
+ for det in frame:
44
+ if CLASS_MAPPING[det["label"]] is not None:
45
+ label = INDEX_MAPPING[CLASS_MAPPING[det["label"]]]-1
46
+ else:
47
+ continue
48
+ box = det["bounding_box"]
49
+ x1, y1, x2, y2 = box[0], box[1], box[0]+box[2], box[1]+box[3]
50
+ x1, y1, x2, y2 = x1*resolution[1], y1*resolution[0], x2*resolution[1], y2*resolution[0]
51
+ target_tm_batch.append([label, x1, y1, x2, y2])
52
+ target_tm.append(torch.tensor(target_tm_batch) if len(target_tm_batch) > 0 else torch.empty((0, 5)))
53
+
54
+ for frame in preds:
55
+ pred_tm_batch = []
56
+ for det in frame:
57
+ label = INDEX_MAPPING[det["label"]]-1
58
+ box = det["bounding_box"]
59
+ x1, y1, x2, y2 = box[0], box[1], box[0]+box[2], box[1]+box[3]
60
+ x1, y1, x2, y2 = x1*resolution[1], y1*resolution[0], x2*resolution[1], y2*resolution[0]
61
+ conf = 1
62
+ pred_tm_batch.append([x1, y1, x2, y2, conf, label])
63
+
64
+ preds_tm.append(torch.tensor(pred_tm_batch) if len(pred_tm_batch) > 0 else torch.empty((0, 6)))
65
+
66
+ for i in range(len(target_tm)):
67
+ target_batch = target_tm[i]
68
+ pred_batch = preds_tm[i]
69
+ cm.process_batch(pred_batch, target_batch)
70
+
71
+ print("SUMMARY: ")
72
+ print("\nmodel: ", model)
73
+ print("\nconfusion matrix: ")
74
+ print(cm.matrix.astype(int))
75
+
76
+ tp = cm.matrix[:-1, :-1].sum()
77
+ fp = cm.matrix[:-1, -1].sum()
78
+ fn = cm.matrix[-1, :-1].sum()
79
+ print("\nTP: ", tp, "FP: ", fp, "FN: ", fn, "support: ", tp + fn)
80
+ #Detection Rates:
81
+ print("\nDetection Rates:")
82
+ for i in range(10):
83
+ tp = cm.matrix[:-1, i].sum()
84
+ fn = cm.matrix[-1, i].sum()
85
+ if tp + fn == 0:
86
+ print(f"{INDEX_MAPPING_INV[i+1]}: NaN")
87
+ else:
88
+ print(f"{INDEX_MAPPING_INV[i+1]}: {tp/(tp+fn)}")
test.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ import fiftyone as fo
4
+ from box_metrics import BoxMetrics
5
+ from seametrics.fo_utils.utils import fo_to_payload
6
+ from tqdm import tqdm
7
+
8
+ tags = ["WHALES"]
9
+ dataset_name = "SENTRY_VIDEOS_DATASET_QA"
10
+ model = "cerulean-level-17_11_2023_RL_SPLIT_ep147_CNN"
11
+ det_gt_field = "ground_truth_det"
12
+
13
+ dataset = fo.load_dataset(dataset_name)
14
+ dataset_view = fo.load_dataset(dataset_name).match_tags(tags) if tags else fo.load_dataset(dataset_name)
15
+ sequences = dataset_view.distinct("sequence")
16
+
17
+ bbox_metric = BoxMetrics(max_iou=0.01)
18
+ payload = fo_to_payload(dataset = dataset_name,
19
+ gt_field = det_gt_field,
20
+ models = [model],
21
+ tracking_mode = True,
22
+ sequence_list = sequences)
23
+ print(payload)
24
+ bbox_metric.add_payload(payload)
25
+ result = bbox_metric.compute()
26
+ print(result)
utils.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ import math
4
+
5
+ def bbox_bep(box1, box2, xywh=True, eps=1e-7, bep1 = True):
6
+ """
7
+ Calculates bottom edge proximity between two boxes
8
+
9
+ Input shapes are box1(1,4) to box2(n,4)
10
+
11
+ Implementation of bep2 from
12
+ Are object detection assessment criteria ready for maritime computer vision?
13
+ """
14
+
15
+ # Get the coordinates of bounding boxes
16
+ if xywh: # transform from xywh to xyxy
17
+ (x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)
18
+ w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2
19
+ b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1_, x1 + w1_, y1 - h1_, y1 + h1_
20
+ b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2_, x2 + w2_, y2 - h2_, y2 + h2_
21
+ else: # x1, y1, x2, y2 = box1
22
+ b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1)
23
+ b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1)
24
+ w1, h1 = b1_x2 - b1_x1, (b1_y2 - b1_y1).clamp(eps)
25
+ w2, h2 = b2_x2 - b2_x1, (b2_y2 - b2_y1).clamp(eps)
26
+
27
+ # Bottom edge distance (absolute value)
28
+ # xb = torch.abs(b2_x2 - b1_x1)
29
+ xb = torch.min(b2_x2-b1_x1, b1_x2-b2_x1)
30
+ xa = w2 - xb
31
+ xc = w1 - xb
32
+ ybe = torch.abs(b2_y2 - b1_y2)
33
+
34
+ X2 = xb/(xb+xa)
35
+ Y2 = 1-ybe/h2
36
+
37
+ X1 = xb/(xb+xa+xc+eps)
38
+ Y1 = 1-ybe/(torch.max(h2,h1)+eps)
39
+
40
+ bep = X1*Y1 if bep1 else X2*Y2
41
+
42
+ return bep
43
+
44
+ def bbox_iou(box1, box2, xywh=True, GIoU=False, DIoU=False, CIoU=False, eps=1e-7):
45
+ """
46
+ Calculates IoU, GIoU, DIoU, or CIoU between two boxes, supporting xywh/xyxy formats.
47
+
48
+ Input shapes are box1(1,4) to box2(n,4).
49
+ """
50
+
51
+ # Get the coordinates of bounding boxes
52
+ if xywh: # transform from xywh to xyxy
53
+ (x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)
54
+ w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2
55
+ b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1_, x1 + w1_, y1 - h1_, y1 + h1_
56
+ b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2_, x2 + w2_, y2 - h2_, y2 + h2_
57
+ else: # x1, y1, x2, y2 = box1
58
+ b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1)
59
+ b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1)
60
+ w1, h1 = b1_x2 - b1_x1, (b1_y2 - b1_y1).clamp(eps)
61
+ w2, h2 = b2_x2 - b2_x1, (b2_y2 - b2_y1).clamp(eps)
62
+
63
+ # Intersection area
64
+ inter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp(0) * (
65
+ b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)
66
+ ).clamp(0)
67
+
68
+ # Union Area
69
+ union = w1 * h1 + w2 * h2 - inter + eps
70
+
71
+ # IoU
72
+ iou = inter / union
73
+ if CIoU or DIoU or GIoU:
74
+ cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1) # convex (smallest enclosing box) width
75
+ ch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1) # convex height
76
+ if CIoU or DIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
77
+ c2 = cw**2 + ch**2 + eps # convex diagonal squared
78
+ rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4 # center dist ** 2
79
+ if CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47
80
+ v = (4 / math.pi**2) * (torch.atan(w2 / h2) - torch.atan(w1 / h1)).pow(2)
81
+ with torch.no_grad():
82
+ alpha = v / (v - iou + (1 + eps))
83
+ return iou - (rho2 / c2 + v * alpha) # CIoU
84
+ return iou - rho2 / c2 # DIoU
85
+ c_area = cw * ch + eps # convex area
86
+ return iou - (c_area - union) / c_area # GIoU https://arxiv.org/pdf/1902.09630.pdf
87
+ return iou # IoU
88
+
89
+ class BoxMetrics:
90
+ # Updated version of https://github.com/kaanakan/object_detection_confusion_matrix
91
+ def __init__(self):
92
+ self.preds_tm = []
93
+ self.target_tm = []
94
+ self.bottom_x = []
95
+ self.bottom_y = []
96
+ self.widths = []
97
+ self.heights = []
98
+ self.ious = []
99
+ self.beps = []
100
+
101
+ def add_batch(self, preds, target):
102
+ """
103
+ Return intersection-over-union (Jaccard index) of boxes.
104
+ Both sets of boxes are expected to be in (x1, y1, x2, y2) format.
105
+ Arguments:
106
+ detections torch(Array[N, 6]), x1, y1, x2, y2, conf, class
107
+ labels torch(Array[M, 5]), class, x1, y1, x2, y2
108
+ Returns:
109
+ None, updates confusion matrix accordingly
110
+ """
111
+ self.preds_tm.extend(preds)
112
+ self.target_tm.extend(target)
113
+
114
+ def compute(self):
115
+ """
116
+ Computes bbox iou, bep and location/size statistics
117
+ """
118
+
119
+ for i in range(len(self.target_tm)):
120
+ target_batch_boxes = self.target_tm[i][:, 1:]
121
+ pred_batch_boxes = self.preds_tm[i][:, :4]
122
+
123
+ if pred_batch_boxes.shape[0] == 0:
124
+ continue
125
+
126
+ if target_batch_boxes.shape[0] == 0:
127
+ continue
128
+
129
+ for t_box in target_batch_boxes:
130
+ iou = bbox_iou(t_box.unsqueeze(0), pred_batch_boxes, xywh=False)
131
+ bep = bbox_bep(t_box.unsqueeze(0), pred_batch_boxes, xywh=False)
132
+
133
+ matches = pred_batch_boxes[iou.squeeze(1) > 0.1]
134
+
135
+ bep = bep[iou > 0]
136
+ iou = iou[iou > 0]
137
+ # if any iou value is 0 or less, raise error
138
+ if torch.any(iou <= 0):
139
+ raise ValueError("IoU values must be greater than 0.")
140
+ #same for bep
141
+ if torch.any(bep <= 0):
142
+ print(t_box.unsqueeze(0))
143
+ print(pred_batch_boxes)
144
+ print(bep)
145
+ print(iou)
146
+ raise ValueError("BEP values must be greater than 0.")
147
+
148
+ self.ious.extend(iou.tolist())
149
+ self.beps.extend(bep.tolist())
150
+
151
+ for match in matches:
152
+ t_xc = (match[0].item()+match[2].item())/2
153
+ p_xc = (t_box[0].item()+t_box[2].item())/2
154
+ t_w = t_box[2].item()-t_box[0].item()
155
+ p_w = match[2].item()-match[0].item()
156
+ t_h = t_box[3].item()-t_box[1].item()
157
+ p_h = match[3].item()-match[1].item()
158
+
159
+ self.bottom_x.append(p_xc - t_xc)
160
+ self.bottom_y.append(match[3].item()-t_box[3].item())
161
+ self.widths.append(p_w-t_w)
162
+ self.heights.append(p_h-t_h)
163
+
164
+ return {"iou_mean": np.mean(self.ious),
165
+ "bep_mean": np.mean(self.beps),
166
+ "bottom_x_std": np.std(self.bottom_x),
167
+ "bottom_y_std": np.std(self.bottom_y),
168
+ "widths_std": np.std(self.widths),
169
+ "heights_std": np.std(self.heights),
170
+ "bottom_x_mean": np.mean(self.bottom_x),
171
+ "bottom_y_mean": np.mean(self.bottom_y),
172
+ "widths_mean": np.mean(self.widths),
173
+ "heights_mean": np.mean(self.heights)}