-
Notifications
You must be signed in to change notification settings - Fork 402
Expand file tree
/
Copy pathMakefile
More file actions
286 lines (257 loc) · 12.9 KB
/
Copy pathMakefile
File metadata and controls
286 lines (257 loc) · 12.9 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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# MOT benchmark workflow — Makefile orchestrates `trackers tune`, `trackers eval`,
# small helper scripts under scripts/, and `codabench_submit.py`.
#
# cd benchmark
# make help
# make data-check
# make benchmark-default TRACKER=sort CODABENCH_TOKEN=...
# make benchmark-tuned TRACKER=sort CODABENCH_TOKEN=...
# make benchmark BENCHMARK_CONFIG=all TRACKER=sort CODABENCH_TOKEN=...
#
# See README.md for data setup.
SHELL := /bin/bash
ROOT := $(CURDIR)
REPO_ROOT := $(abspath $(ROOT)/..)
PYTHON ?= python
UV ?= uv
DATA_ROOT ?= $(ROOT)/data
PREP_DIR ?= $(ROOT)/benchmark_prep
OUTPUT_DIR ?= $(ROOT)/benchmark_outputs
TRACKER ?= sort
DATASET ?= mot17
CONFIG ?= default
BENCHMARK_CONFIG ?= default
# Multi-tracker comparison (docs/trackers/comparison.md layout). Set to `all` or list ids.
# Tracker list is defined once in scripts/datasets.py (COMPARISON_TRACKERS).
TRACKERS ?=
DATASETS_PY := $(PYTHON) scripts/datasets.py
COMPARISON_TRACKERS ?= $(shell $(DATASETS_PY) --field comparison_trackers)
COMPARISON_TRACKERS_CSV ?= $(shell $(DATASETS_PY) --field comparison_trackers_csv)
comma := ,
empty :=
space := $(empty) $(empty)
ifeq ($(TRACKERS),all)
BENCHMARK_TRACKERS := $(COMPARISON_TRACKERS)
else ifneq ($(strip $(TRACKERS)),)
BENCHMARK_TRACKERS := $(TRACKERS)
else ifeq ($(words $(DATASETS)),1)
# One dataset → all trackers + comparison table (docs/trackers/comparison.md layout).
BENCHMARK_TRACKERS := $(COMPARISON_TRACKERS)
else
BENCHMARK_TRACKERS := $(TRACKER)
endif
N_TRIALS ?= 10
OBJECTIVE ?= HOTA
THRESHOLD ?= 0.5
SEED ?=
FIXED_PARAMS ?=
# Optional BoT-SORT appearance: set REID_ENCODER=fastreid_mot17_sbs50 to enable.
REID_ENCODER ?=
APPEARANCE_THRESHOLD ?= 0.2
CODABENCH_URL ?= https://www.codabench.org
CODABENCH_TOKEN ?=
CODABENCH_WAIT_TIMEOUT ?= 3600
CODABENCH_POLL_INTERVAL ?= 10
# BoT-SORT requires CMC on by default for the published numbers.
ifeq ($(TRACKER),botsort)
ifeq ($(strip $(FIXED_PARAMS)),)
# No spaces — Make word-splits $(FIXED_PARAMS) when expanding recipe lines.
FIXED_PARAMS := {"enable_cmc":true}
endif
endif
CODABENCH_DATASETS := mot17 sportsmot dancetrack
LOCAL_DATASETS := soccernet
ALL_DATASETS := $(CODABENCH_DATASETS) $(LOCAL_DATASETS)
DATASETS ?= $(ALL_DATASETS)
# Per-dataset tune splits. Score split is "test" for everyone (Codabench test for
# codabench datasets, public test GT for soccernet). Path lookups for image dirs
# delegate to scripts/datasets.py to stay DRY.
mot17_TUNE_SPLIT := val
sportsmot_TUNE_SPLIT := val
dancetrack_TUNE_SPLIT := val
soccernet_TUNE_SPLIT := train
SCORE_SPLIT := test
# Codabench (competition_id, phase_id) per dataset.
mot17_CB := 10049 16382
sportsmot_CB := 13077 21402
dancetrack_CB := 14885 24635
LAYOUT := $(DATASETS_PY) --data-root "$(DATA_ROOT)"
.PHONY: help setup data-check prep prep-all tune track-default track-tuned _track-and-score \
benchmark benchmark-default benchmark-tuned benchmark-comparison-default benchmark-comparison-tuned \
upload collect collect-comparison poll clean
help:
@echo "MOT benchmark workflow — run from \`cd benchmark\`"
@echo ""
@echo "Targets:"
@echo " setup Install \`trackers[tune,reid]\` from $(REPO_ROOT)"
@echo " data-check Print present/missing assets under $(DATA_ROOT)"
@echo " prep Prep one dataset (DATASET=...) into $(PREP_DIR)"
@echo " prep-all Prep every dataset"
@echo " tune Tune one (TRACKER=, DATASET=)"
@echo " track-default Track on default params, then score (TRACKER=, DATASET=)"
@echo " track-tuned Track on best_params.json, then score (TRACKER=, DATASET=)"
@echo " upload Upload an existing submission.zip (TRACKER=, DATASET=, CONFIG=default|tuned)"
@echo " benchmark Full pipeline (TRACKER=, BENCHMARK_CONFIG=default|tuned|all, DATASETS=...)"
@echo " benchmark-default Same as \`make benchmark BENCHMARK_CONFIG=default\`"
@echo " benchmark-tuned Same as \`make benchmark BENCHMARK_CONFIG=tuned\`"
@echo " benchmark-comparison-default All $(COMPARISON_TRACKERS) on DATASET= (default params)"
@echo " benchmark-comparison-tuned All $(COMPARISON_TRACKERS) on DATASET= (tune + tuned)"
@echo " collect Rebuild tables.md for one tracker (TRACKER=)"
@echo " collect-comparison Tracker comparison table (DATASET=dancetrack)"
@echo " poll Poll an existing Codabench submission (SUBMISSION_ID=, DATASET=, CONFIG=)"
@echo " clean Remove $(PREP_DIR) and $(OUTPUT_DIR)"
@echo ""
@echo "BoT-SORT ReID: pass REID_ENCODER=fastreid_mot17_sbs50 APPEARANCE_THRESHOLD=0.2"
@echo "Codabench upload requires CODABENCH_TOKEN. See README for data setup."
setup:
$(UV) pip install -e "$(REPO_ROOT)[tune,reid]"
data-check:
@$(PYTHON) scripts/data_check.py --data-root "$(DATA_ROOT)"
prep:
$(PYTHON) scripts/prep_data.py --dataset $(DATASET) --split all \
--data-root "$(DATA_ROOT)" --prep-dir "$(PREP_DIR)"
prep-all:
$(PYTHON) scripts/prep_data.py --dataset all --split all \
--data-root "$(DATA_ROOT)" --prep-dir "$(PREP_DIR)"
# Tune via the `trackers tune` CLI. Pass --images-dir only for BoT-SORT CMC (ByteTrack/SORT/OC-SORT are det-only).
tune:
@if [ ! -d "$(PREP_DIR)/$(DATASET)/$($(DATASET)_TUNE_SPLIT)" ]; then \
echo "Run: make prep DATASET=$(DATASET)"; exit 1; \
fi
@mkdir -p "$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)"
$(eval TUNE_SPLIT := $($(DATASET)_TUNE_SPLIT))
$(eval TUNE_IMAGES := $(shell $(LAYOUT) --dataset $(DATASET) --split $(TUNE_SPLIT) --field images_dir))
trackers tune \
--tracker $(TRACKER) \
--gt-dir "$(PREP_DIR)/$(DATASET)/$(TUNE_SPLIT)/gt" \
--detections-dir "$(PREP_DIR)/$(DATASET)/$(TUNE_SPLIT)/dets" \
--metrics CLEAR HOTA Identity --objective $(OBJECTIVE) --threshold $(THRESHOLD) \
--n-trials $(N_TRIALS) \
$(if $(SEED),--seed $(SEED),) \
$(if $(FIXED_PARAMS),--fixed-params '$(FIXED_PARAMS)',) \
$(if $(and $(filter botsort,$(TRACKER)),$(TUNE_IMAGES)),--images-dir "$(TUNE_IMAGES)",) \
--output "$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/best_params.json"
track-default:
@$(MAKE) _track-and-score TRACKER=$(TRACKER) DATASET=$(DATASET) CONFIG=default
track-tuned:
@test -f "$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/best_params.json" || \
{ echo "missing best_params.json — run \`make tune TRACKER=$(TRACKER) DATASET=$(DATASET)\`"; exit 1; }
@$(MAKE) _track-and-score TRACKER=$(TRACKER) DATASET=$(DATASET) CONFIG=tuned
# 1) track on the score split (test) → predictions
# 2) score: `trackers eval` for soccernet, `mot_format.py` + `codabench_submit.py` for the rest.
_track-and-score:
@if [ ! -d "$(PREP_DIR)/$(DATASET)/$(SCORE_SPLIT)" ]; then \
echo "Run: make prep DATASET=$(DATASET)"; exit 1; \
fi
$(PYTHON) scripts/track_split.py \
--tracker $(TRACKER) --dataset $(DATASET) --split $(SCORE_SPLIT) \
--data-root "$(DATA_ROOT)" --prep-dir "$(PREP_DIR)" \
--output-dir "$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/$(CONFIG)" \
$(if $(filter tuned,$(CONFIG)),--params "$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/best_params.json",) \
$(if $(REID_ENCODER),--reid "$(REID_ENCODER)",) \
$(if $(and $(REID_ENCODER),$(APPEARANCE_THRESHOLD)),--appearance-threshold $(APPEARANCE_THRESHOLD),)
@if [ "$(DATASET)" = "soccernet" ]; then \
trackers eval \
--gt-dir "$(PREP_DIR)/soccernet/test/gt" \
--tracker-dir "$(OUTPUT_DIR)/$(TRACKER)/soccernet/$(CONFIG)/pred" \
--metrics CLEAR HOTA Identity --threshold $(THRESHOLD) \
--output "$(OUTPUT_DIR)/$(TRACKER)/soccernet/$(CONFIG)/eval.json"; \
else \
[ -n "$(CODABENCH_TOKEN)" ] || { echo "ERROR: CODABENCH_TOKEN not set — required for $(DATASET)."; exit 1; }; \
$(PYTHON) scripts/mot_format.py --dataset $(DATASET) \
--pred-dir "$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/$(CONFIG)/pred" \
--out-zip "$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/$(CONFIG)/submission.zip"; \
$(PYTHON) scripts/codabench_submit.py \
"$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/$(CONFIG)/submission.zip" \
--competition-id $(word 1,$($(DATASET)_CB)) \
--phase $(word 2,$($(DATASET)_CB)) \
--base-url "$(CODABENCH_URL)" --token "$(CODABENCH_TOKEN)" \
--description "$(TRACKER) $(CONFIG) — benchmark Makefile" \
--wait-timeout $(CODABENCH_WAIT_TIMEOUT) --poll-interval $(CODABENCH_POLL_INTERVAL) \
--output "$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/$(CONFIG)/codabench.json"; \
fi
# Re-upload a zip without re-tracking (e.g. after Codabench daily limit resets).
upload:
@test -f "$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/$(CONFIG)/submission.zip" || \
{ echo "missing $(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/$(CONFIG)/submission.zip"; exit 1; }
@[ -n "$(CODABENCH_TOKEN)" ] || { echo "ERROR: CODABENCH_TOKEN not set."; exit 1; }
@test "$(DATASET)" != "soccernet" || { echo "soccernet is scored locally — use track-default/track-tuned"; exit 1; }
$(PYTHON) scripts/codabench_submit.py \
"$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/$(CONFIG)/submission.zip" \
--competition-id $(word 1,$($(DATASET)_CB)) \
--phase $(word 2,$($(DATASET)_CB)) \
--base-url "$(CODABENCH_URL)" --token "$(CODABENCH_TOKEN)" \
--description "$(TRACKER) $(CONFIG) — benchmark Makefile" \
--wait-timeout $(CODABENCH_WAIT_TIMEOUT) --poll-interval $(CODABENCH_POLL_INTERVAL) \
--output "$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/$(CONFIG)/codabench.json"
# Poll Codabench for scores without re-uploading (e.g. after a transient 502 during benchmark).
poll:
@test -n "$(SUBMISSION_ID)" || { echo "Set SUBMISSION_ID= (from Codabench upload log)"; exit 1; }
@test "$(DATASET)" != "soccernet" || { echo "soccernet is scored locally"; exit 1; }
@[ -n "$(CODABENCH_TOKEN)" ] || { echo "ERROR: CODABENCH_TOKEN not set."; exit 1; }
$(PYTHON) scripts/codabench_submit.py --submission-id $(SUBMISSION_ID) \
--competition-id $(word 1,$($(DATASET)_CB)) \
--phase $(word 2,$($(DATASET)_CB)) \
--base-url "$(CODABENCH_URL)" --token "$(CODABENCH_TOKEN)" \
--wait-timeout $(CODABENCH_WAIT_TIMEOUT) --poll-interval $(CODABENCH_POLL_INTERVAL) \
--output "$(OUTPUT_DIR)/$(TRACKER)/$(DATASET)/$(CONFIG)/codabench.json"
collect:
$(PYTHON) scripts/collect.py --tracker $(TRACKER) --output-dir "$(OUTPUT_DIR)" \
--datasets "$(subst $(space),$(comma),$(DATASETS))"
collect-comparison:
@if [ "$(origin DATASET)" != "command line" ]; then \
echo "Set DATASET=dancetrack"; exit 1; \
fi
$(PYTHON) scripts/collect.py --compare-dataset $(DATASET) --output-dir "$(OUTPUT_DIR)" \
--trackers "$(if $(filter all,$(TRACKERS)),$(COMPARISON_TRACKERS_CSV),$(if $(strip $(TRACKERS)),$(subst $(space),$(comma),$(TRACKERS)),$(COMPARISON_TRACKERS_CSV)))"
# Full pipeline. BENCHMARK_CONFIG=default|tuned|all (default: default only — one Codabench pass).
# Set TRACKERS=all (or a space-separated list) to benchmark every tracker and write comparison tables.
benchmark:
@case "$(BENCHMARK_CONFIG)" in default|tuned|all) ;; \
*) echo "Set BENCHMARK_CONFIG=default, tuned, or all (or use benchmark-default / benchmark-tuned)"; exit 1;; \
esac
@if [ -n "$(strip $(filter $(CODABENCH_DATASETS),$(DATASETS)))" ] && [ -z "$(CODABENCH_TOKEN)" ]; then \
echo "ERROR: CODABENCH_TOKEN must be set (required for: $(filter $(CODABENCH_DATASETS),$(DATASETS)))."; exit 1; \
fi
@for d in $(DATASETS); do \
echo "===== prep [$$d] ====="; \
$(MAKE) prep DATASET=$$d || exit 1; \
done
@for t in $(BENCHMARK_TRACKERS); do \
for d in $(DATASETS); do \
if [ "$(BENCHMARK_CONFIG)" = "default" ] || [ "$(BENCHMARK_CONFIG)" = "all" ]; then \
echo ""; echo "===== [$$t/$$d] default ====="; \
$(MAKE) track-default TRACKER=$$t DATASET=$$d || exit 1; \
fi; \
if [ "$(BENCHMARK_CONFIG)" = "tuned" ] || [ "$(BENCHMARK_CONFIG)" = "all" ]; then \
echo ""; echo "===== [$$t/$$d] tune ====="; \
extras=""; \
if [ "$$t" = "botsort" ]; then extras='FIXED_PARAMS={"enable_cmc":true}'; fi; \
$(MAKE) tune TRACKER=$$t DATASET=$$d N_TRIALS=$(N_TRIALS) $$extras || exit 1; \
echo "===== [$$t/$$d] tuned ====="; \
$(MAKE) track-tuned TRACKER=$$t DATASET=$$d || exit 1; \
fi; \
done; \
done
@if [ "$(words $(BENCHMARK_TRACKERS))" -gt 1 ]; then \
for d in $(DATASETS); do \
echo ""; echo "===== collect comparison [$$d] ====="; \
$(PYTHON) scripts/collect.py --compare-dataset $$d --output-dir "$(OUTPUT_DIR)" \
--trackers "$(subst $(space),$(comma),$(BENCHMARK_TRACKERS))" || exit 1; \
done; \
else \
echo ""; echo "===== collect [$(TRACKER)] ====="; \
$(MAKE) collect TRACKER=$(TRACKER) DATASETS="$(DATASETS)"; \
fi
benchmark-comparison-default:
@test -n "$(DATASET)" || { echo "Set DATASET= (e.g. dancetrack)"; exit 1; }
@$(MAKE) benchmark BENCHMARK_CONFIG=default DATASETS="$(DATASET)" TRACKERS=all
benchmark-comparison-tuned:
@test -n "$(DATASET)" || { echo "Set DATASET= (e.g. dancetrack)"; exit 1; }
@$(MAKE) benchmark BENCHMARK_CONFIG=tuned DATASETS="$(DATASET)" TRACKERS=all
benchmark-default:
@$(MAKE) benchmark BENCHMARK_CONFIG=default
benchmark-tuned:
@$(MAKE) benchmark BENCHMARK_CONFIG=tuned
clean:
rm -rf "$(PREP_DIR)" "$(OUTPUT_DIR)"