Skip to content

Commit b0c0386

Browse files
committed
version 0.0.9: added iou threshold to detection metrics
1 parent 87c4ff2 commit b0c0386

6 files changed

Lines changed: 55 additions & 46 deletions

File tree

README.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ The following function are possible with the ``Detection`` class:
204204
| | corresponding groundtruth. Each |
205205
| | frame prediction/groundtruth can |
206206
| | be either as a ``list of list`` |
207+
| | or as a ``list of dict``. |
207208
| | (more details below). |
208209
+-----------------------------------+-----------------------------------+
209210
| video_end() | Call to make the end of one video |
@@ -236,7 +237,6 @@ The following function are possible with the ``Detection`` class:
236237
| | for all seen samples. |
237238
+-----------------------------------+-----------------------------------+
238239

239-
.. _args-1:
240240

241241
args:
242242
~~~~~
@@ -261,7 +261,7 @@ args:
261261
4. ``component`` can be any of the following (‘i’, ‘v’, ‘t’, ‘iv’,
262262
‘it’,‘ivt’) to compute performance for (instrument, verb, target,
263263
instrument-verb, instrument-target, instrument-verb-target)
264-
respectively, default is ‘ivt’ for triplets.<
264+
respectively, default is ‘ivt’ for triplets.
265265

266266
- the output is a ``dict`` with keys(“AP”, “mAP”, “Rec”, “mRec”, “Pre”,
267267
“mPre”) for per-class AP, mean AP, per-class Recall, mean Recall,
@@ -332,13 +332,14 @@ Although, the ``Detection()`` and ``Recognition()`` classes uses the ``Disentang
332332
Afterwards, any of the component's predictions/labels can be filtered from the main triplet's predictions/labels as follows:
333333

334334
.. code:: python
335+
335336
i_labels = filter.extract(inputs=ivt_labels, component="i")
336337
v_preds = filter.extract(inputs=ivt_preds, component="v")
337338
t_preds = filter.extract(inputs=ivt_preds, component="t")
338339
iv_labels = filter.extract(inputs=ivt_labels, component="iv")
339340
it_labels = filter.extract(inputs=ivt_labels, component="it")
340341
341-
342+
In this example, labels = (batch of) vector of groundtruth labels; preds = (batch of) vector of predicted probability scores.
342343

343344

344345

ivtmetrics/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# -*- coding: utf-8 -*-
33

44
__library__ = "ivtmetrics"
5-
__version__ = "0.0.8"
5+
__version__ = "0.0.9"
66
__author__ = 'Chinedu Nwoye'
77
__supervisor__ = 'Nicolas Padoy'
88
__credits__ = 'CAMMA, ICube Lab, University of Strasbourg, France'

ivtmetrics/detection.py

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#%%%%%%%% imports %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1010
import numpy as np
1111
import sys
12+
import warnings
1213
from ivtmetrics.recognition import Recognition
1314

1415
#%%%%%%%%%% RECOGNITION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -50,7 +51,7 @@ class Detection(Recognition):
5051
add compute_global_AP('i/ivt') return AP for all seen examples
5152
add reset_video()
5253
"""
53-
def __init__(self, num_class=100, num_tool=6):
54+
def __init__(self, num_class=100, num_tool=6, threshold=0.5):
5455
super(Recognition, self).__init__()
5556
self.num_class = num_class
5657
self.num_tool = num_tool
@@ -60,6 +61,7 @@ def __init__(self, num_class=100, num_tool=6):
6061
self.accumulator = {}
6162
self.video_count = 0
6263
self.end_call = False
64+
self.threshold = threshold
6365
self.reset()
6466

6567
def reset(self):
@@ -115,6 +117,7 @@ def is_match(self, det_gt, det_pd, threshold):
115117
return status
116118

117119
def list2stack(self, x):
120+
if x == []: x = [[-1,-1,-1,-1,-1,-1]] # empty
118121
#x format for a single frame: list(list): each list = [tripletID, toolID, toolProbs, x, y, w, h] bbox is scaled (0..1)
119122
assert isinstance(x[0], list), "Each frame must be a list of lists, each list a prediction of triplet and object locations"
120123
x = np.stack(x, axis=0)
@@ -136,7 +139,6 @@ def dict2stack(self, x):
136139
p = [d['triplet']]
137140
p.extend(d["instrument"])
138141
y.append(p)
139-
# y = np.stack(y, axis=0)
140142
return self.list2stack(y)
141143

142144
def update(self, targets, predictions, format="list"):
@@ -160,38 +162,38 @@ def update_frame(self, targets, predictions, format="list"):
160162
sys.exit("unkown input format for update function. Must be a list or dict")
161163
if len(detection_pd) + len(detection_gt) == 0:
162164
return
163-
detection_gt_i = detection_gt.copy()
164-
detection_pd_i = detection_pd.copy()
165+
detection_gt_ivt = detection_gt.copy()
166+
detection_pd_ivt = detection_pd.copy()
165167
# for triplet
166-
for gt in detection_gt:
168+
for gt in detection_gt_ivt:
167169
self.accumulator[self.video_count]["npos"][int(gt[0])] += 1
168-
for det_pd in detection_pd:
170+
for det_pd in detection_pd_ivt:
169171
self.accumulator[self.video_count]["ndet"][int(det_pd[0])] += 1
170172
matched = False
171-
for k, det_gt in enumerate(detection_gt):
173+
for k, det_gt in enumerate(detection_gt_ivt):
172174
y = det_gt[0:]
173175
f = det_pd[0:]
174-
if self.is_match(y, f, threshold=0.5):
175-
detection_gt = np.delete(detection_gt, obj=k, axis=0)
176+
if self.is_match(y, f, threshold=self.threshold):
177+
detection_gt_ivt = np.delete(detection_gt_ivt, obj=k, axis=0)
176178
matched = True
177179
break
178180
if matched:
179181
self.accumulator[self.video_count]["hits"][int(det_pd[0])].append(1.0)
180182
else:
181183
self.accumulator[self.video_count]["hits"][int(det_pd[0])].append(0.0)
182-
# for instrument
183-
detection_gt = detection_gt_i
184-
detection_pd = detection_pd_i
185-
for gt in detection_gt:
184+
# for instrument
185+
detection_gt_i = detection_gt.copy()
186+
detection_pd_i = detection_pd.copy()
187+
for gt in detection_gt_i:
186188
self.accumulator[self.video_count]["npos_i"][int(gt[1])] += 1
187-
for det_pd in detection_pd:
189+
for det_pd in detection_pd_i:
188190
self.accumulator[self.video_count]["ndet_i"][int(det_pd[1])] += 1
189191
matched = False
190-
for k, det_gt in enumerate(detection_gt):
191-
y = det_gt[1:6]
192-
f = det_pd[1:6]
193-
if self.is_match(y, f, threshold=0.5):
194-
detection_gt = np.delete(detection_gt, obj=k, axis=0)
192+
for k, det_gt in enumerate(detection_gt_i):
193+
y = det_gt[1:]
194+
f = det_pd[1:]
195+
if self.is_match(y, f, threshold=self.threshold):
196+
detection_gt_i = np.delete(detection_gt_i, obj=k, axis=0)
195197
matched = True
196198
break
197199
if matched:
@@ -240,7 +242,9 @@ def compute(self, component="ivt", video_id=None):
240242
classwise_ap.append(ap)
241243
classwise_rec.append(np.max(rec))
242244
classwise_prec.append(np.max(prec))
243-
return (classwise_ap, np.nanmean(classwise_ap)), (classwise_rec, np.nanmean(classwise_rec)), (classwise_prec, np.nanmean(classwise_prec))
245+
with warnings.catch_warnings():
246+
warnings.simplefilter("ignore", category=RuntimeWarning)
247+
return (classwise_ap, np.nanmean(classwise_ap)), (classwise_rec, np.nanmean(classwise_rec)), (classwise_prec, np.nanmean(classwise_prec))
244248

245249
def compute_video_AP(self, component="ivt"):
246250
classwise_ap = []
@@ -252,12 +256,14 @@ def compute_video_AP(self, component="ivt"):
252256
classwise_ap.append(ap)
253257
classwise_rec.append(rec)
254258
classwise_prec.append(prec)
255-
classwise_ap = np.nanmean(np.stack(classwise_ap, axis=0), axis=0)
256-
classwise_rec = np.nanmean(np.stack(classwise_rec, axis=0), axis=0)
257-
classwise_prec = np.nanmean(np.stack(classwise_prec, axis=0), axis=0)
258-
mAP = np.nanmean(classwise_ap)
259-
mRec = np.nanmean(classwise_rec)
260-
mPrec = np.nanmean(classwise_prec)
259+
with warnings.catch_warnings():
260+
warnings.simplefilter("ignore", category=RuntimeWarning)
261+
classwise_ap = np.nanmean(np.stack(classwise_ap, axis=0), axis=0)
262+
classwise_rec = np.nanmean(np.stack(classwise_rec, axis=0), axis=0)
263+
classwise_prec = np.nanmean(np.stack(classwise_prec, axis=0), axis=0)
264+
mAP = np.nanmean(classwise_ap)
265+
mRec = np.nanmean(classwise_rec)
266+
mPrec = np.nanmean(classwise_prec)
261267
return {"AP":classwise_ap, "mAP":mAP, "Rec":classwise_rec, "mRec":mRec, "Pre":classwise_prec, "mPre":mPrec}
262268

263269
def compute_AP(self, component="ivt"):

ivtmetrics/disentangle.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
An python implementation triplet component filtering .
55
Created on Thu Dec 30 12:37:56 2021
66
@author: nwoye chinedu i.
7-
icube, unistra
7+
(c) camma, icube, unistra
88
"""
99
#%%%%%%%% imports %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1010
import numpy as np
@@ -77,7 +77,8 @@ def extract(self, inputs, component="i"):
7777
return np.array(list(map(self.decompose, inputs, component)))
7878

7979
def map_file(self):
80-
return np.array([ [ 0, 0, 2, 1, 2, 1],
80+
return np.array([
81+
[ 0, 0, 2, 1, 2, 1],
8182
[ 1, 0, 2, 0, 2, 0],
8283
[ 2, 0, 2, 10, 2, 10],
8384
[ 3, 0, 0, 3, 0, 3],
@@ -176,4 +177,5 @@ def map_file(self):
176177
[96, 2, 9, 14, 29, 44],
177178
[97, 3, 9, 14, 39, 59],
178179
[98, 4, 9, 14, 49, 74],
179-
[99, 5, 9, 14, 59, 89]])
180+
[99, 5, 9, 14, 59, 89]
181+
])

ivtmetrics/recognition.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
An python implementation recognition AP for surgical action triplet evaluation.
55
Created on Thu Dec 30 12:37:56 2021
66
@author: nwoye chinedu i.
7-
icube, unistra
7+
(c) icube, unistra
88
"""
99
#%%%%%%%% imports %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1010
import numpy as np
@@ -110,9 +110,9 @@ def compute_AP(self, component="ivt", ignore_null=False):
110110
targets = self.extract(self.targets, component)
111111
predicts = self.extract(self.predictions, component)
112112
else:
113-
sys.exit(f"Function filtering {component} not supported yet!")
113+
sys.exit(f"Function filtering {component} not yet supported!")
114114
with warnings.catch_warnings():
115-
warnings.filterwarnings(action='ignore', message='Mean of empty slice')
115+
warnings.filterwarnings(action='ignore', message='[info] triplet classes not represented in this test sample will be reported as nan values.')
116116
classwise = average_precision_score(targets, predicts, average=None)
117117
if (ignore_null and component=="ivt"): classwise = classwise[:-6]
118118
mean = np.nanmean(classwise)
@@ -143,9 +143,9 @@ def compute_global_AP(self, component="ivt", ignore_null=False):
143143
targets = self.extract(targets, component)
144144
predicts = self.extract(predicts, component)
145145
else:
146-
sys.exit(f"Function filtering {component} not supported yet!")
146+
sys.exit(f"Function filtering {component} not yet supported!")
147147
with warnings.catch_warnings():
148-
warnings.filterwarnings(action='ignore', message='Mean of empty slice')
148+
warnings.filterwarnings(action='ignore', message='[info] triplet classes not represented in this test sample will be reported as nan values.')
149149
classwise = average_precision_score(targets, predicts, average=None)
150150
if (ignore_null and component=="ivt"): classwise = classwise[:-6]
151151
mean = np.nanmean(classwise)
@@ -172,19 +172,19 @@ def compute_video_AP(self, component="ivt", ignore_null=False):
172172
global_predictions.append(self.predictions)
173173
video_log = []
174174
with warnings.catch_warnings():
175-
warnings.filterwarnings(action='ignore', message='Mean of empty slice')
175+
warnings.simplefilter("ignore", category=RuntimeWarning)
176176
for targets, predicts in zip(global_targets, global_predictions):
177177
if component in ["ivt", "it", "iv", "t", "v", "i"]:
178178
targets = self.extract(targets, component)
179179
predicts = self.extract(predicts, component)
180180
else:
181-
sys.exit(f"Function filtering {component} not supported yet!")
181+
sys.exit(f"Function filtering {component} not yet supported!")
182182
classwise = average_precision_score(targets, predicts, average=None)
183183
video_log.append( classwise.reshape([1,-1]) )
184-
video_log = np.concatenate(video_log, axis=0)
185-
videowise = np.nanmean(video_log, axis=0)
186-
if (ignore_null and component=="ivt"): videowise = videowise[:-6]
187-
mean = np.nanmean(videowise)
184+
video_log = np.concatenate(video_log, axis=0)
185+
videowise = np.nanmean(video_log, axis=0)
186+
if (ignore_null and component=="ivt"): videowise = videowise[:-6]
187+
mean = np.nanmean(videowise)
188188
return {"AP":videowise, "mAP":mean}
189189

190190
##%%%%%%%%%%%%%%%%%%% TOP OP #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
setup(
88
name='ivtmetrics',
9-
version='0.0.8',
9+
version='0.0.9',
1010
packages=['ivtmetrics'],
1111
author='Chinedu Nwoye',
1212
author_email='nwoye.chinedu@gmail.com',
@@ -15,7 +15,7 @@
1515
long_description = long_description,
1616
long_description_content_type ='text/x-rst',
1717
url='https://github.com/CAMMA-public/ivtmetrics',
18-
download_url = 'https://github.com/CAMMA-public/ivtmetrics/archive/refs/tags/v0.0.8.tar.gz', # I explain this later on
18+
download_url = 'https://github.com/CAMMA-public/ivtmetrics/archive/refs/tags/v0.0.9.tar.gz', # I explain this later on
1919
include_package_data=True,
2020
license='BSD 2-clause', # Chose a license from here: https://help.github.com/articles/licensing-a-repository
2121
install_requires=['scikit-learn',

0 commit comments

Comments
 (0)