Skip to content

Commit 00b71dc

Browse files
Merge pull request #1912 from Fleyderer/master
Added FPS downsampling feature
2 parents f773731 + 1d44efe commit 00b71dc

File tree

2 files changed

+79
-11
lines changed

2 files changed

+79
-11
lines changed

tracking/utils.py

+24-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from ultralytics.engine.results import Results
99
from typing import Union
1010
from pathlib import Path
11+
import json
12+
import shutil
1113
import os
1214
import sys
1315
import git
@@ -253,6 +255,25 @@ def unzip_mot_dataset(zip_path, val_tools_path, benchmark):
253255
else:
254256
LOGGER.info(f'{benchmark} folder already exists.')
255257
return extract_path
258+
259+
260+
def set_gt_fps(opt, seq_paths):
261+
fps_json_filepath = opt.exp_folder_path / 'seqs_frame_nums.json'
262+
with open(fps_json_filepath, 'r') as f:
263+
seqs_frame_nums = json.load(f)
264+
265+
for seq_path in seq_paths:
266+
seq_name = seq_path.parent.name
267+
frame_nums = seqs_frame_nums[seq_name]
268+
269+
gt_dir = seq_path.parent / 'gt'
270+
gt_orig_path = gt_dir / 'gt.txt'
271+
gt_temp_path = gt_dir / 'gt_temp.txt'
272+
shutil.copy(gt_orig_path, gt_temp_path)
273+
274+
seq = np.loadtxt(gt_temp_path, delimiter=',')
275+
seq_filtered = seq[np.isin(seq[:, 0], frame_nums)]
276+
np.savetxt(gt_temp_path, seq_filtered, delimiter=',')
256277

257278

258279
def eval_setup(opt, val_tools_path):
@@ -300,10 +321,12 @@ def eval_setup(opt, val_tools_path):
300321
# Default handling for other datasets
301322
seq_paths = [p / 'img1' for p in mot_seqs_path.iterdir() if p.is_dir()]
302323

324+
# Set FPS for GT files
325+
set_gt_fps(opt, seq_paths)
326+
303327
# Determine save directory
304328
save_dir = Path(opt.project) / opt.name
305329

306-
307330
# Setup MOT results folder
308331
MOT_results_folder = val_tools_path / 'data' / 'trackers' / 'mot_challenge' / opt.benchmark / save_dir.name / 'data'
309332
MOT_results_folder.mkdir(parents=True, exist_ok=True) # Ensure directory exists
@@ -382,4 +405,3 @@ def write_mot_results(txt_path: Path, mot_results: np.ndarray) -> None:
382405
# Open the file in append mode and save the MOT results
383406
with open(str(txt_path), 'a') as file:
384407
np.savetxt(file, mot_results, fmt='%d,%d,%d,%d,%d,%d,%d,%d,%.6f')
385-

tracking/val.py

+55-9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pathlib import Path
66
import numpy as np
77
from tqdm import tqdm
8+
import configparser
89
import shutil
910
import json
1011
import queue
@@ -235,13 +236,16 @@ def generate_dets_embs(args: argparse.Namespace, y: Path, source: Path) -> None:
235236
np.savetxt(f, embs, fmt='%f')
236237

237238

238-
def generate_mot_results(args: argparse.Namespace, config_dict: dict = None) -> None:
239+
def generate_mot_results(args: argparse.Namespace, config_dict: dict = None) -> dict[str, np.ndarray]:
239240
"""
240241
Generates MOT results for the specified arguments and configuration.
241242
242243
Args:
243244
args (Namespace): Parsed command line arguments.
244245
config_dict (dict, optional): Additional configuration dictionary.
246+
247+
Returns:
248+
dict[str, np.ndarray]: {seq_name: array} with frame ids used for MOT
245249
"""
246250
args.device = select_device(args.device)
247251
tracker = create_tracker(
@@ -267,19 +271,51 @@ def generate_mot_results(args: argparse.Namespace, config_dict: dict = None) ->
267271
txt_path = args.exp_folder_path / (source.parent.name + '.txt')
268272
all_mot_results = []
269273

270-
for frame_idx, d in enumerate(tqdm(dataset, desc=source.parent.name, leave=False)):
271-
if frame_idx == len(dataset):
272-
break
274+
# Change FPS
275+
if args.fps:
276+
277+
# Extract original FPS
278+
conf_path = source.parent / 'seqinfo.ini'
279+
conf = configparser.ConfigParser()
280+
conf.read(conf_path)
281+
282+
orig_fps = int(conf.get("Sequence", "frameRate"))
283+
284+
if orig_fps < args.fps:
285+
LOGGER.warning(f"Original FPS ({orig_fps}) is lower than "
286+
f"requested FPS ({args.fps}) for sequence "
287+
f"{source.parent.name}. Using original FPS.")
288+
target_fps = orig_fps
289+
else:
290+
target_fps = args.fps
291+
292+
293+
step = orig_fps/target_fps
294+
else:
295+
step = 1
296+
297+
# Create list with frame numbers according to needed step
298+
frame_nums = np.arange(1, len(dataset) + 1, step).astype(int).tolist()
299+
300+
seq_frame_nums = {source.parent.name: frame_nums.copy()}
301+
302+
for frame_num, d in enumerate(tqdm(dataset, desc=source.parent.name), 1):
303+
# Filter using list with needed numbers
304+
if len(frame_nums) > 0:
305+
if frame_num < frame_nums[0]:
306+
continue
307+
else:
308+
frame_nums.pop(0)
273309

274310
im = d[1][0]
275-
frame_dets_n_embs = dets_n_embs[dets_n_embs[:, 0] == frame_idx + 1]
311+
frame_dets_n_embs = dets_n_embs[dets_n_embs[:, 0] == frame_num]
276312

277313
dets = frame_dets_n_embs[:, 1:7]
278314
embs = frame_dets_n_embs[:, 7:]
279315
tracks = tracker.update(dets, im, embs)
280316

281317
if tracks.size > 0:
282-
mot_results = convert_to_mot_format(tracks, frame_idx + 1)
318+
mot_results = convert_to_mot_format(tracks, frame_num)
283319
all_mot_results.append(mot_results)
284320

285321
if all_mot_results:
@@ -289,6 +325,8 @@ def generate_mot_results(args: argparse.Namespace, config_dict: dict = None) ->
289325

290326
write_mot_results(txt_path, all_mot_results)
291327

328+
return seq_frame_nums
329+
292330

293331
def parse_mot_results(results: str) -> dict:
294332
"""
@@ -340,6 +378,7 @@ def trackeval(args: argparse.Namespace, seq_paths: list, save_dir: Path, MOT_res
340378
"--TRACKER_SUB_FOLDER", "",
341379
"--NUM_PARALLEL_CORES", str(4),
342380
"--SKIP_SPLIT_FOL", "True",
381+
"--GT_LOC_FORMAT", "{gt_folder}/{seq}/gt/gt_temp.txt",
343382
"--SEQ_INFO", *d
344383
]
345384

@@ -384,7 +423,8 @@ def process_single_mot(opt: argparse.Namespace, d: Path, e: Path, evolve_config:
384423
new_opt = copy.deepcopy(opt)
385424
new_opt.dets_file_path = d
386425
new_opt.embs_file_path = e
387-
generate_mot_results(new_opt, evolve_config)
426+
frames_dict = generate_mot_results(new_opt, evolve_config)
427+
return frames_dict
388428

389429
def run_generate_mot_results(opt: argparse.Namespace, evolve_config: dict = None) -> None:
390430
"""
@@ -427,16 +467,21 @@ def run_generate_mot_results(opt: argparse.Namespace, evolve_config: dict = None
427467
# Submit the task to process this file pair in parallel
428468
tasks.append(executor.submit(process_single_mot, opt, d, e, evolve_config))
429469

470+
# Dict with {seq_name: [frame_nums]}
471+
seqs_frame_nums = {}
430472
# Wait for all tasks to complete and log any exceptions
431473
for future in concurrent.futures.as_completed(tasks):
432474
try:
433-
future.result()
475+
seqs_frame_nums.update(future.result())
434476
except Exception as exc:
435477
LOGGER.error(f'Error processing file pair: {exc}')
436478

437479
# Postprocess data with gsi if requested
438480
if opt.gsi:
439-
gsi(mot_results_folder=opt.exp_folder_path)
481+
gsi(mot_results_folder=opt.exp_folder_path)
482+
483+
with open(opt.exp_folder_path / 'seqs_frame_nums.json', 'w') as f:
484+
json.dump(seqs_frame_nums, f)
440485

441486

442487
def run_trackeval(opt: argparse.Namespace) -> dict:
@@ -477,6 +522,7 @@ def parse_opt() -> argparse.Namespace:
477522
parser.add_argument('--reid-model', nargs='+', type=Path, default=[WEIGHTS / 'osnet_x0_25_msmt17.pt'], help='reid model path')
478523
parser.add_argument('--source', type=str, help='file/dir/URL/glob, 0 for webcam')
479524
parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=None, help='inference size h,w')
525+
parser.add_argument('--fps', type=int, default=None, help='video frame-rate')
480526
parser.add_argument('--conf', type=float, default=0.01, help='min confidence threshold')
481527
parser.add_argument('--iou', type=float, default=0.7, help='intersection over union (IoU) threshold for NMS')
482528
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')

0 commit comments

Comments
 (0)