Spaces:
Build error
Build error
from __future__ import division, print_function, absolute_import | |
import argparse | |
import os | |
import cv2 | |
import numpy as np | |
from application_util import preprocessing | |
from application_util import visualization | |
from deep_sort import nn_matching | |
from deep_sort.detection import Detection | |
from deep_sort.tracker import Tracker | |
def gather_sequence_info(sequence_dir, detection_file): | |
"""Gather sequence information, such as image filenames, detections, | |
groundtruth (if available). | |
Parameters | |
---------- | |
sequence_dir : str | |
Path to the MOTChallenge sequence directory. | |
detection_file : str | |
Path to the detection file. | |
Returns | |
------- | |
Dict | |
A dictionary of the following sequence information: | |
* sequence_name: Name of the sequence | |
* image_filenames: A dictionary that maps frame indices to image | |
filenames. | |
* detections: A numpy array of detections in MOTChallenge format. | |
* groundtruth: A numpy array of ground truth in MOTChallenge format. | |
* image_size: Image size (height, width). | |
* min_frame_idx: Index of the first frame. | |
* max_frame_idx: Index of the last frame. | |
""" | |
image_dir = os.path.join(sequence_dir, "img1") | |
image_filenames = { | |
int(os.path.splitext(f)[0]): os.path.join(image_dir, f) | |
for f in os.listdir(image_dir)} | |
groundtruth_file = os.path.join(sequence_dir, "gt/gt.txt") | |
detections = None | |
if detection_file is not None: | |
detections = np.load(detection_file) | |
groundtruth = None | |
if os.path.exists(groundtruth_file): | |
groundtruth = np.loadtxt(groundtruth_file, delimiter=',') | |
if len(image_filenames) > 0: | |
image = cv2.imread(next(iter(image_filenames.values())), | |
cv2.IMREAD_GRAYSCALE) | |
image_size = image.shape | |
else: | |
image_size = None | |
if len(image_filenames) > 0: | |
min_frame_idx = min(image_filenames.keys()) | |
max_frame_idx = max(image_filenames.keys()) | |
else: | |
min_frame_idx = int(detections[:, 0].min()) | |
max_frame_idx = int(detections[:, 0].max()) | |
info_filename = os.path.join(sequence_dir, "seqinfo.ini") | |
if os.path.exists(info_filename): | |
with open(info_filename, "r") as f: | |
line_splits = [l.split('=') for l in f.read().splitlines()[1:]] | |
info_dict = dict( | |
s for s in line_splits if isinstance(s, list) and len(s) == 2) | |
update_ms = 1000 / int(info_dict["frameRate"]) | |
else: | |
update_ms = None | |
feature_dim = detections.shape[1] - 10 if detections is not None else 0 | |
seq_info = { | |
"sequence_name": os.path.basename(sequence_dir), | |
"image_filenames": image_filenames, | |
"detections": detections, | |
"groundtruth": groundtruth, | |
"image_size": image_size, | |
"min_frame_idx": min_frame_idx, | |
"max_frame_idx": max_frame_idx, | |
"feature_dim": feature_dim, | |
"update_ms": update_ms | |
} | |
return seq_info | |
def create_detections(detection_mat, frame_idx, min_height=0): | |
"""Create detections for given frame index from the raw detection matrix. | |
Parameters | |
---------- | |
detection_mat : ndarray | |
Matrix of detections. The first 10 columns of the detection matrix are | |
in the standard MOTChallenge detection format. In the remaining columns | |
store the feature vector associated with each detection. | |
frame_idx : int | |
The frame index. | |
min_height : Optional[int] | |
A minimum detection bounding box height. Detections that are smaller | |
than this value are disregarded. | |
Returns | |
------- | |
List[tracker.Detection] | |
Returns detection responses at given frame index. | |
""" | |
frame_indices = detection_mat[:, 0].astype(np.int) | |
mask = frame_indices == frame_idx | |
detection_list = [] | |
for row in detection_mat[mask]: | |
bbox, confidence, feature = row[2:6], row[6], row[10:] | |
if bbox[3] < min_height: | |
continue | |
detection_list.append(Detection(bbox, confidence, feature)) | |
return detection_list | |
def run(sequence_dir, detection_file, output_file, min_confidence, | |
nms_max_overlap, min_detection_height, max_cosine_distance, | |
nn_budget, display): | |
"""Run multi-target tracker on a particular sequence. | |
Parameters | |
---------- | |
sequence_dir : str | |
Path to the MOTChallenge sequence directory. | |
detection_file : str | |
Path to the detections file. | |
output_file : str | |
Path to the tracking output file. This file will contain the tracking | |
results on completion. | |
min_confidence : float | |
Detection confidence threshold. Disregard all detections that have | |
a confidence lower than this value. | |
nms_max_overlap: float | |
Maximum detection overlap (non-maxima suppression threshold). | |
min_detection_height : int | |
Detection height threshold. Disregard all detections that have | |
a height lower than this value. | |
max_cosine_distance : float | |
Gating threshold for cosine distance metric (object appearance). | |
nn_budget : Optional[int] | |
Maximum size of the appearance descriptor gallery. If None, no budget | |
is enforced. | |
display : bool | |
If True, show visualization of intermediate tracking results. | |
""" | |
seq_info = gather_sequence_info(sequence_dir, detection_file) | |
metric = nn_matching.NearestNeighborDistanceMetric( | |
"cosine", max_cosine_distance, nn_budget) | |
tracker = Tracker(metric) | |
results = [] | |
def frame_callback(vis, frame_idx): | |
print("Processing frame %05d" % frame_idx) | |
# Load image and generate detections. | |
detections = create_detections( | |
seq_info["detections"], frame_idx, min_detection_height) | |
detections = [d for d in detections if d.confidence >= min_confidence] | |
# Run non-maxima suppression. | |
boxes = np.array([d.tlwh for d in detections]) | |
scores = np.array([d.confidence for d in detections]) | |
indices = preprocessing.non_max_suppression( | |
boxes, nms_max_overlap, scores) | |
detections = [detections[i] for i in indices] | |
# Update tracker. | |
tracker.predict() | |
tracker.update(detections) | |
# Update visualization. | |
if display: | |
image = cv2.imread( | |
seq_info["image_filenames"][frame_idx], cv2.IMREAD_COLOR) | |
vis.set_image(image.copy()) | |
vis.draw_detections(detections) | |
vis.draw_trackers(tracker.tracks) | |
# Store results. | |
for track in tracker.tracks: | |
if not track.is_confirmed() or track.time_since_update > 1: | |
continue | |
bbox = track.to_tlwh() | |
results.append([ | |
frame_idx, track.track_id, bbox[0], bbox[1], bbox[2], bbox[3]]) | |
# Run tracker. | |
if display: | |
visualizer = visualization.Visualization(seq_info, update_ms=5) | |
else: | |
visualizer = visualization.NoVisualization(seq_info) | |
visualizer.run(frame_callback) | |
# Store results. | |
f = open(output_file, 'w') | |
for row in results: | |
print('%d,%d,%.2f,%.2f,%.2f,%.2f,1,-1,-1,-1' % ( | |
row[0], row[1], row[2], row[3], row[4], row[5]),file=f) | |
def bool_string(input_string): | |
if input_string not in {"True","False"}: | |
raise ValueError("Please Enter a valid Ture/False choice") | |
else: | |
return (input_string == "True") | |
def parse_args(): | |
""" Parse command line arguments. | |
""" | |
parser = argparse.ArgumentParser(description="Deep SORT") | |
parser.add_argument( | |
"--sequence_dir", help="Path to MOTChallenge sequence directory", | |
default=None, required=True) | |
parser.add_argument( | |
"--detection_file", help="Path to custom detections.", default=None, | |
required=True) | |
parser.add_argument( | |
"--output_file", help="Path to the tracking output file. This file will" | |
" contain the tracking results on completion.", | |
default="/tmp/hypotheses.txt") | |
parser.add_argument( | |
"--min_confidence", help="Detection confidence threshold. Disregard " | |
"all detections that have a confidence lower than this value.", | |
default=0.8, type=float) | |
parser.add_argument( | |
"--min_detection_height", help="Threshold on the detection bounding " | |
"box height. Detections with height smaller than this value are " | |
"disregarded", default=0, type=int) | |
parser.add_argument( | |
"--nms_max_overlap", help="Non-maxima suppression threshold: Maximum " | |
"detection overlap.", default=1.0, type=float) | |
parser.add_argument( | |
"--max_cosine_distance", help="Gating threshold for cosine distance " | |
"metric (object appearance).", type=float, default=0.2) | |
parser.add_argument( | |
"--nn_budget", help="Maximum size of the appearance descriptors " | |
"gallery. If None, no budget is enforced.", type=int, default=None) | |
parser.add_argument( | |
"--display", help="Show intermediate tracking results", | |
default=True, type=bool_string) | |
return parser.parse_args() | |
if __name__ == "__main__": | |
args = parse_args() | |
run( | |
args.sequence_dir, args.detection_file, args.output_file, | |
args.min_confidence, args.nms_max_overlap, args.min_detection_height, | |
args.max_cosine_distance, args.nn_budget, args.display) |