forked from JackWoo0831/Yolov7-tracker
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathplotting.py
More file actions
230 lines (184 loc) · 8.2 KB
/
plotting.py
File metadata and controls
230 lines (184 loc) · 8.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
import os
import numpy as np
from .utils import TrackEvalException
def plot_compare_trackers(tracker_folder, tracker_list, cls, output_folder, plots_list=None):
"""Create plots which compare metrics across different trackers."""
# Define what to plot
if plots_list is None:
plots_list = get_default_plots_list()
# Load data
data = load_multiple_tracker_summaries(tracker_folder, tracker_list, cls)
out_loc = os.path.join(output_folder, cls)
# Plot
for args in plots_list:
create_comparison_plot(data, out_loc, *args)
def get_default_plots_list():
# y_label, x_label, sort_label, bg_label, bg_function
plots_list = [
['AssA', 'DetA', 'HOTA', 'HOTA', 'geometric_mean'],
['AssPr', 'AssRe', 'HOTA', 'AssA', 'jaccard'],
['DetPr', 'DetRe', 'HOTA', 'DetA', 'jaccard'],
['HOTA(0)', 'LocA(0)', 'HOTA', 'HOTALocA(0)', 'multiplication'],
['HOTA', 'LocA', 'HOTA', None, None],
['HOTA', 'MOTA', 'HOTA', None, None],
['HOTA', 'IDF1', 'HOTA', None, None],
['IDF1', 'MOTA', 'HOTA', None, None],
]
return plots_list
def load_multiple_tracker_summaries(tracker_folder, tracker_list, cls):
"""Loads summary data for multiple trackers."""
data = {}
for tracker in tracker_list:
with open(os.path.join(tracker_folder, tracker, cls + '_summary.txt')) as f:
keys = next(f).split(' ')
done = False
while not done:
values = next(f).split(' ')
if len(values) == len(keys):
done = True
data[tracker] = dict(zip(keys, map(float, values)))
return data
def create_comparison_plot(data, out_loc, y_label, x_label, sort_label, bg_label=None, bg_function=None, settings=None):
""" Creates a scatter plot comparing multiple trackers between two metric fields, with one on the x-axis and the
other on the y axis. Adds pareto optical lines and (optionally) a background contour.
Inputs:
data: dict of dicts such that data[tracker_name][metric_field_name] = float
y_label: the metric_field_name to be plotted on the y-axis
x_label: the metric_field_name to be plotted on the x-axis
sort_label: the metric_field_name by which trackers are ordered and ranked
bg_label: the metric_field_name by which (optional) background contours are plotted
bg_function: the (optional) function bg_function(x,y) which converts the x_label / y_label values into bg_label.
settings: dict of plot settings with keys:
'gap_val': gap between axis ticks and bg curves.
'num_to_plot': maximum number of trackers to plot
"""
# Only loaded when run to reduce minimum requirements
from matplotlib import pyplot as plt
# Get plot settings
if settings is None:
gap_val = 2
num_to_plot = 20
else:
gap_val = settings['gap_val']
num_to_plot = settings['num_to_plot']
if (bg_label is None) != (bg_function is None):
raise TrackEvalException('bg_function and bg_label must either be both given or neither given.')
# Extract data
tracker_names = np.array(list(data.keys()))
sort_index = np.array([data[t][sort_label] for t in tracker_names]).argsort()[::-1]
x_values = np.array([data[t][x_label] for t in tracker_names])[sort_index][:num_to_plot]
y_values = np.array([data[t][y_label] for t in tracker_names])[sort_index][:num_to_plot]
# Print info on what is being plotted
tracker_names = tracker_names[sort_index][:num_to_plot]
print('\nPlotting %s vs %s, for the following (ordered) trackers:' % (y_label, x_label))
for i, name in enumerate(tracker_names):
print('%i: %s' % (i+1, name))
# Find best fitting boundaries for data
boundaries = _get_boundaries(x_values, y_values, round_val=gap_val/2)
fig = plt.figure()
# Plot background contour
if bg_function is not None:
_plot_bg_contour(bg_function, boundaries, gap_val)
# Plot pareto optimal lines
_plot_pareto_optimal_lines(x_values, y_values)
# Plot data points with number labels
labels = np.arange(len(y_values)) + 1
plt.plot(x_values, y_values, 'b.', markersize=15)
for xx, yy, l in zip(x_values, y_values, labels):
plt.text(xx, yy, str(l), color="red", fontsize=15)
# Add extra explanatory text to plots
plt.text(0, -0.11, 'label order:\nHOTA', horizontalalignment='left', verticalalignment='center',
transform=fig.axes[0].transAxes, color="red", fontsize=12)
if bg_label is not None:
plt.text(1, -0.11, 'curve values:\n' + bg_label, horizontalalignment='right', verticalalignment='center',
transform=fig.axes[0].transAxes, color="grey", fontsize=12)
plt.xlabel(x_label, fontsize=15)
plt.ylabel(y_label, fontsize=15)
title = y_label + ' vs ' + x_label
if bg_label is not None:
title += ' (' + bg_label + ')'
plt.title(title, fontsize=17)
plt.xticks(np.arange(0, 100, gap_val))
plt.yticks(np.arange(0, 100, gap_val))
min_x, max_x, min_y, max_y = boundaries
plt.xlim(min_x, max_x)
plt.ylim(min_y, max_y)
plt.gca().set_aspect('equal', adjustable='box')
plt.tight_layout()
os.makedirs(out_loc, exist_ok=True)
filename = os.path.join(out_loc, title.replace(' ', '_'))
plt.savefig(filename + '.pdf', bbox_inches='tight', pad_inches=0.05)
plt.savefig(filename + '.png', bbox_inches='tight', pad_inches=0.05)
def _get_boundaries(x_values, y_values, round_val):
x1 = np.min(np.floor((x_values - 0.5) / round_val) * round_val)
x2 = np.max(np.ceil((x_values + 0.5) / round_val) * round_val)
y1 = np.min(np.floor((y_values - 0.5) / round_val) * round_val)
y2 = np.max(np.ceil((y_values + 0.5) / round_val) * round_val)
x_range = x2 - x1
y_range = y2 - y1
max_range = max(x_range, y_range)
x_center = (x1 + x2) / 2
y_center = (y1 + y2) / 2
min_x = max(x_center - max_range / 2, 0)
max_x = min(x_center + max_range / 2, 100)
min_y = max(y_center - max_range / 2, 0)
max_y = min(y_center + max_range / 2, 100)
return min_x, max_x, min_y, max_y
def geometric_mean(x, y):
return np.sqrt(x * y)
def jaccard(x, y):
x = x / 100
y = y / 100
return 100 * (x * y) / (x + y - x * y)
def multiplication(x, y):
return x * y / 100
bg_function_dict = {
"geometric_mean": geometric_mean,
"jaccard": jaccard,
"multiplication": multiplication,
}
def _plot_bg_contour(bg_function, plot_boundaries, gap_val):
""" Plot background contour. """
# Only loaded when run to reduce minimum requirements
from matplotlib import pyplot as plt
# Plot background contour
min_x, max_x, min_y, max_y = plot_boundaries
x = np.arange(min_x, max_x, 0.1)
y = np.arange(min_y, max_y, 0.1)
x_grid, y_grid = np.meshgrid(x, y)
if bg_function in bg_function_dict.keys():
z_grid = bg_function_dict[bg_function](x_grid, y_grid)
else:
raise TrackEvalException("background plotting function '%s' is not defined." % bg_function)
levels = np.arange(0, 100, gap_val)
con = plt.contour(x_grid, y_grid, z_grid, levels, colors='grey')
def bg_format(val):
s = '{:1f}'.format(val)
return '{:.0f}'.format(val) if s[-1] == '0' else s
con.levels = [bg_format(val) for val in con.levels]
plt.clabel(con, con.levels, inline=True, fmt='%r', fontsize=8)
def _plot_pareto_optimal_lines(x_values, y_values):
""" Plot pareto optimal lines """
# Only loaded when run to reduce minimum requirements
from matplotlib import pyplot as plt
# Plot pareto optimal lines
cxs = x_values
cys = y_values
best_y = np.argmax(cys)
x_pareto = [0, cxs[best_y]]
y_pareto = [cys[best_y], cys[best_y]]
t = 2
remaining = cxs > x_pareto[t - 1]
cys = cys[remaining]
cxs = cxs[remaining]
while len(cxs) > 0 and len(cys) > 0:
best_y = np.argmax(cys)
x_pareto += [x_pareto[t - 1], cxs[best_y]]
y_pareto += [cys[best_y], cys[best_y]]
t += 2
remaining = cxs > x_pareto[t - 1]
cys = cys[remaining]
cxs = cxs[remaining]
x_pareto.append(x_pareto[t - 1])
y_pareto.append(0)
plt.plot(np.array(x_pareto), np.array(y_pareto), '--r')