Compare commits

...

10 commits

Author SHA1 Message Date
Ruben van de Ven
635eeb1e5e Add color to PCA and UMAP visualisations 2023-04-13 13:49:40 +02:00
Ruben van de Ven
913d67e019 PCA and UMAP visualisations on grid 2023-04-13 13:12:50 +02:00
Ruben van de Ven
291504263c ignore OUT 2023-04-13 13:12:07 +02:00
Ruben van de Ven
ecbe041041 tqdm.auto to support notebook 2023-04-13 13:11:58 +02:00
Ruben van de Ven
5deac894a5 change minor log annoyance 2023-04-05 17:17:35 +02:00
Ruben van de Ven
0f4b6044c4 Store embeddings 2023-04-05 17:17:15 +02:00
Ruben van de Ven
38e7d181f9 Fix docker issues and more deps 2023-04-05 17:16:28 +02:00
Ruben van de Ven
b9a31cfd29 Visualise embeddings using UMAP and PCA 2023-04-05 17:15:50 +02:00
Ruben van de Ven
9aa9e9c709 Update docker deps. 2023-03-31 15:58:46 +02:00
Ruben van de Ven
abf8085ab7 Allow image-dir as --input-video 2023-03-31 15:58:02 +02:00
8 changed files with 2239 additions and 18 deletions

2
.gitignore vendored
View file

@ -110,3 +110,5 @@ venv.bak/
# mypy # mypy
.mypy_cache/ .mypy_cache/
OUT/

View file

@ -1,5 +1,5 @@
{ {
"root":"/home/wangzd/datasets/MOT", "root":"/Towards-Realtime-MOT/datasets/MOT",
"train": "train":
{ {
"mot17":"./data/mot17.train", "mot17":"./data/mot17.train",

View file

@ -49,9 +49,14 @@ def track(opt):
n_frame = 0 n_frame = 0
logger.info('Starting tracking...') logger.info('Starting tracking...')
dataloader = datasets.LoadVideo(opt.input_video, opt.img_size) if os.path.isdir(opt.input_video):
print('Use image sequence')
dataloader = datasets.LoadImages(opt.input_video, opt.img_size)
frame_rate = 30 # hack for now; see https://motchallenge.net/data/MOT16/
else:
dataloader = datasets.LoadVideo(opt.input_video, opt.img_size)
frame_rate = dataloader.frame_rate
result_filename = os.path.join(result_root, 'results.txt') result_filename = os.path.join(result_root, 'results.txt')
frame_rate = dataloader.frame_rate
frame_dir = None if opt.output_format=='text' else osp.join(result_root, 'frame') frame_dir = None if opt.output_format=='text' else osp.join(result_root, 'frame')
try: try:

View file

@ -1,5 +1,20 @@
FROM pytorch/pytorch:1.3-cuda10.1-cudnn7-devel FROM pytorch/pytorch:1.13.1-cuda11.6-cudnn8-devel
RUN apt update && apt install -y ffmpeg libsm6 libxrender-dev RUN apt update && apt install -y ffmpeg libsm6 libxrender-dev
RUN pip install Cython RUN pip install Cython
RUN pip install opencv-python cython_bbox motmetrics numba matplotlib sklearn RUN pip install opencv-python cython_bbox motmetrics numba matplotlib sklearn
RUN pip install lap
RUN pip install umap-learn
ENV NUMBA_CACHE_DIR=/tmp/numba_cache
RUN pip install bokeh
RUN pip install ipykernel
RUN pip install ipython
# Vscode bug: https://github.com/microsoft/vscode-jupyter/issues/8552
RUN pip install ipywidgets==7.7.2
#RUN pip install panel jupyter_bokeh
# for bokeh
EXPOSE 5006
CMD python -m ipykernel_launcher -f $DOCKERNEL_CONNECTION_FILE

View file

@ -1,8 +1,11 @@
import os import os
import os.path as osp import os.path as osp
import pickle
import cv2 import cv2
import logging import logging
import argparse import argparse
from tqdm.auto import tqdm
import motmetrics as mm import motmetrics as mm
import torch import torch
@ -38,7 +41,7 @@ def write_results(filename, results, data_type):
logger.info('save results to {}'.format(filename)) logger.info('save results to {}'.format(filename))
def eval_seq(opt, dataloader, data_type, result_filename, save_dir=None, show_image=True, frame_rate=30): def eval_seq(opt, dataloader, data_type, result_filename, save_dir=None, save_img=False, save_figures=False, show_image=True, frame_rate=30):
''' '''
Processes the video sequence given and provides the output of tracking result (write the results in video file) Processes the video sequence given and provides the output of tracking result (write the results in video file)
@ -59,7 +62,9 @@ def eval_seq(opt, dataloader, data_type, result_filename, save_dir=None, show_im
The name(path) of the file for storing results. The name(path) of the file for storing results.
save_dir : String save_dir : String
Path to the folder for storing the frames containing bounding box information (Result frames). Path to the folder for storing the frames containing bounding box information (Result frames). If given, featuers will be save there as pickle
save_figures : bool
If set, individual crops of all embedded figures will be saved
show_image : bool show_image : bool
Option for shhowing individial frames during run-time. Option for shhowing individial frames during run-time.
@ -79,15 +84,20 @@ def eval_seq(opt, dataloader, data_type, result_filename, save_dir=None, show_im
tracker = JDETracker(opt, frame_rate=frame_rate) tracker = JDETracker(opt, frame_rate=frame_rate)
timer = Timer() timer = Timer()
results = [] results = []
frame_id = 0 frame_id = -1
for path, img, img0 in dataloader: for path, img, img0 in tqdm(dataloader):
if frame_id % 20 == 0: frame_id += 1
logger.info('Processing frame {} ({:.2f} fps)'.format(frame_id, 1./max(1e-5, timer.average_time))) # if frame_id % 20 == 0:
# logger.info('Processing frame {} ({:.2f} fps)'.format(frame_id, 1./max(1e-5, timer.average_time)))
frame_pickle_fn = os.path.join(save_dir, f'{frame_id:05d}.pcl')
if os.path.exists(frame_pickle_fn):
continue
# run tracking # run tracking
timer.tic() timer.tic()
blob = torch.from_numpy(img).cuda().unsqueeze(0) blob = torch.from_numpy(img).cuda().unsqueeze(0)
online_targets = tracker.update(blob, img0) # online targets: all tartgets that are not timed out
# frame_embeddings: the embeddings of objects visible only in the current frame
online_targets, frame_embeddings = tracker.update(blob, img0)
online_tlwhs = [] online_tlwhs = []
online_ids = [] online_ids = []
for t in online_targets: for t in online_targets:
@ -106,10 +116,27 @@ def eval_seq(opt, dataloader, data_type, result_filename, save_dir=None, show_im
if show_image: if show_image:
cv2.imshow('online_im', online_im) cv2.imshow('online_im', online_im)
if save_dir is not None: if save_dir is not None:
cv2.imwrite(os.path.join(save_dir, '{:05d}.jpg'.format(frame_id)), online_im) base_fn = os.path.join(save_dir, '{:05d}'.format(frame_id))
frame_id += 1 if save_img:
cv2.imwrite(base_fn+'.jpg', online_im)
if save_figures:
for i, fe in enumerate(frame_embeddings):
tlwh, curr_feat = fe
x,y,w,h = round(tlwh[0]), round(tlwh[1]), round(tlwh[2]), round(tlwh[3])
# print(x,y,w,h, tlwh)
crop_img = img0[y:y+h, x:x+w]
cv2.imwrite(f'{base_fn}-{i}.jpg', crop_img)
with open(os.path.join(save_dir, f'{frame_id:05d}-{i}.pcl'), 'wb') as fp:
pickle.dump(fe, fp)
with open(frame_pickle_fn, 'wb') as fp:
pickle.dump(frame_embeddings, fp)
# save results # save results
write_results(result_filename, results, data_type) if result_filename is not None:
write_results(result_filename, results, data_type)
return frame_id, timer.average_time, timer.calls return frame_id, timer.average_time, timer.calls
@ -131,7 +158,7 @@ def main(opt, data_root='/data/MOT16/train', det_root=None, seqs=('MOT16-05',),
for seq in seqs: for seq in seqs:
output_dir = os.path.join(data_root, '..','outputs', exp_name, seq) if save_images or save_videos else None output_dir = os.path.join(data_root, '..','outputs', exp_name, seq) if save_images or save_videos else None
logger.info('start seq: {}'.format(seq)) # logger.info('start seq: {}'.format(seq))
dataloader = datasets.LoadImages(osp.join(data_root, seq, 'img1'), opt.img_size) dataloader = datasets.LoadImages(osp.join(data_root, seq, 'img1'), opt.img_size)
result_filename = os.path.join(result_root, '{}.txt'.format(seq)) result_filename = os.path.join(result_root, '{}.txt'.format(seq))
meta_info = open(os.path.join(data_root, seq, 'seqinfo.ini')).read() meta_info = open(os.path.join(data_root, seq, 'seqinfo.ini')).read()

View file

@ -203,6 +203,8 @@ class JDETracker(object):
lost_stracks = [] # The tracks which are not obtained in the current frame but are not removed.(Lost for some time lesser than the threshold for removing) lost_stracks = [] # The tracks which are not obtained in the current frame but are not removed.(Lost for some time lesser than the threshold for removing)
removed_stracks = [] removed_stracks = []
frame_embeddings = []
t1 = time.time() t1 = time.time()
''' Step 1: Network forward, get detections & embeddings''' ''' Step 1: Network forward, get detections & embeddings'''
with torch.no_grad(): with torch.no_grad():
@ -220,8 +222,12 @@ class JDETracker(object):
detections = [STrack(STrack.tlbr_to_tlwh(tlbrs[:4]), tlbrs[4], f.numpy(), 30) for detections = [STrack(STrack.tlbr_to_tlwh(tlbrs[:4]), tlbrs[4], f.numpy(), 30) for
(tlbrs, f) in zip(dets[:, :5], dets[:, 6:])] (tlbrs, f) in zip(dets[:, :5], dets[:, 6:])]
# Surfacing Suspicion: extract features + frame id + bbox
frame_embeddings = [[track.tlwh, track.curr_feat] for track in detections]
else: else:
detections = [] detections = []
frame_embeddings = []
t2 = time.time() t2 = time.time()
# print('Forward: {} s'.format(t2-t1)) # print('Forward: {} s'.format(t2-t1))
@ -346,7 +352,7 @@ class JDETracker(object):
logger.debug('Lost: {}'.format([track.track_id for track in lost_stracks])) logger.debug('Lost: {}'.format([track.track_id for track in lost_stracks]))
logger.debug('Removed: {}'.format([track.track_id for track in removed_stracks])) logger.debug('Removed: {}'.format([track.track_id for track in removed_stracks]))
# print('Final {} s'.format(t5-t4)) # print('Final {} s'.format(t5-t4))
return output_stracks return output_stracks, frame_embeddings
def joint_stracks(tlista, tlistb): def joint_stracks(tlista, tlistb):
exists = {} exists = {}

View file

@ -10,7 +10,7 @@ def get_logger(name='root'):
handler.setFormatter(formatter) handler.setFormatter(formatter)
logger = logging.getLogger(name) logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG) logger.setLevel(logging.INFO)
logger.addHandler(handler) logger.addHandler(handler)
return logger return logger

2166
visualise_embeddings.ipynb Normal file

File diff suppressed because one or more lines are too long