def load_predictions_csv(fp): '''Loads output of tracker from CSV file. Args: fp: File-like object with read() and seek(). Returns: List of (time, prediction-dict) pairs. ''' # has_header = csv.Sniffer().has_header(fp.read(4<<10)) # 4 kB # fp.seek(0) reader = _dict_reader_optional_fieldnames(fp, PREDICTION_FIELD_NAMES) predictions = util.SparseTimeSeries() for row in reader: present = util.str2bool(row['present']) t = int(row['frame_num']) predictions[t] = make_prediction( present=present, score=float(row['score']), xmin=float(row['xmin']) if present else None, xmax=float(row['xmax']) if present else None, ymin=float(row['ymin']) if present else None, ymax=float(row['ymax']) if present else None) return predictions
def load_dataset_annotations_csv(fp): '''Loads the annotations for an entire dataset from one CSV file.''' reader = csv.DictReader(fp, fieldnames=TRACK_FIELDS) rows = [row for row in reader] # Group rows by object. rows_by_track = {} for row in rows: vid_id = row['video_id'] obj_id = row['object_id'] rows_by_track.setdefault((vid_id, obj_id), []).append(row) tracks = VideoObjectDict() for vid_obj in rows_by_track.keys(): # vid_id, obj_id = vid_obj frames = util.SparseTimeSeries() for row in rows_by_track[vid_obj]: present = _parse_is_present(row['object_presence']) # TODO: Support 'exemplar' field in CSV format? t = int(row['frame_num']) frames[t] = make_frame_label( present=present, xmin=float(row['xmin']) if present else None, xmax=float(row['xmax']) if present else None, ymin=float(row['ymin']) if present else None, ymax=float(row['ymax']) if present else None) assert len(frames) >= 2 first_row = rows_by_track[vid_obj][0] tracks[vid_obj] = make_track_label( category=first_row['class_name'], frames=frames, contains_cuts=first_row['contains_cuts'], always_visible=first_row['always_visible']) return tracks
def make_task_from_track(track): '''Creates a tracking task from a track annotation dict (oxuva.annot.make_track_label). The first frame is adopted as initialization. The remaining frames become the ground-truth rectangles. ''' frames = list(track['frames'].sorted_items()) init_time, init_annot = frames[0] labels = util.SparseTimeSeries(frames[1:]) # TODO: Check that init_annot['exemplar'] is True. init_rect = {k: init_annot[k] for k in ['xmin', 'xmax', 'ymin', 'ymax']} attributes = {k: v for k, v in track.items() if k not in {'frames'}} return Task(init_time, init_rect, labels=labels, attributes=attributes)
def assess_sequence(gt, pred, iou_threshold): '''Evaluate predicted track against ground-truth annotations. Args: gt: SparseTimeSeries of annotation dicts. pred: SparseTimeSeries of prediction dicts. iou_threshold: Threshold for determining true positive. Returns: An assessment of each frame with ground-truth. This is a TimeSeries of frame assessment dicts. ''' times = gt.sorted_keys() # if pred.sorted_keys() != times: pred = subset_using_previous_if_missing(pred, times) return util.SparseTimeSeries({t: assess_frame(gt[t], pred[t], iou_threshold) for t in times})
def subset_using_previous_if_missing(series, times): '''Extracts a subset of values at the given times. If there is no data for a particular time, then the last value is used. Args: series: SparseTimeSeries of data. times: List of times. Returns: Time series sampled at specified times. Examples: >> subset_using_previous_if_missing([(2, 'hi'), (4, 'bye')], [2, 3, 4, 5]) ['hi', 'hi', 'bye', 'bye'] Raises an exception if asked for a time before the first element in series. ''' assert isinstance(series, util.SparseTimeSeries) series = list(series.sorted_items()) subset = [None for _ in times] t_curr, x_curr = None, None for i, t in enumerate(times): # Read from series until we have read all elements <= t. read_all = False while not read_all: if len(series) == 0: read_all = True else: # Peek at next element. t_next, x_next = series[0] if t_next > t: # We have gone past t. read_all = True else: # Keep going. t_curr, x_curr = t_next, x_next series = series[1:] if t_curr is None: raise ValueError('no value for time: {}'.format(t)) if t_curr != t: logger.warning( 'no prediction for time %d: use prediction for time %s', t, t_curr) subset[i] = x_curr return util.SparseTimeSeries(zip(times, subset))
def test_idempotent(self): source = util.SparseTimeSeries({1: 'one', 3: 'three'}) times = [1, 2, 3, 4] once = subset_using_previous_if_missing(source, times) twice = subset_using_previous_if_missing(once, times) self.assertEqual(list(once.sorted_items()), list(twice.sorted_items()))
def test_before_start(self): source = util.SparseTimeSeries({3: 'three'}) times = [1, 2, 3] self.assertRaises( Exception, lambda: subset_using_previous_if_missing(source, times))
def test_beyond_end(self): source = util.SparseTimeSeries({1: 'one', 3: 'three'}) times = [2, 4, 6] got = subset_using_previous_if_missing(source, times) want = [(2, 'one'), (4, 'three'), (6, 'three')] self.assertEqual(list(got.sorted_items()), want)
def test_missing(self): source = util.SparseTimeSeries({1: 'one', 3: 'three'}) times = [1, 2, 3] got = subset_using_previous_if_missing(source, times) want = [(1, 'one'), (2, 'one'), (3, 'three')] self.assertEqual(list(got.sorted_items()), want)