From 58223d8ab5d16c0ff09125ec22ef167007bee4fe Mon Sep 17 00:00:00 2001 From: Michael Hoss Date: Wed, 24 Jan 2024 17:54:26 +0100 Subject: [PATCH 1/9] WIP upgrade to pymotmetrics 1.4.0 --- python-sdk/nuscenes/eval/tracking/mot.py | 13 ++++++++++++- setup/requirements/requirements_tracking.txt | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/python-sdk/nuscenes/eval/tracking/mot.py b/python-sdk/nuscenes/eval/tracking/mot.py index aa18421b0..f8015f377 100644 --- a/python-sdk/nuscenes/eval/tracking/mot.py +++ b/python-sdk/nuscenes/eval/tracking/mot.py @@ -6,6 +6,16 @@ py-motmetrics at: https://github.com/cheind/py-motmetrics + +Notes by Michael Hoss: +This code is mainly copy-pasted from the original motmetrics repo, likely from version 1.1.3. +TODO: upgrade this code to version 1.4.0, or even better, see if I can just use the origial code. +-> looks like I can pretty much use the original code, as the changes here were mostly done for speed. +But: the motmetrics 1.4.0 code seems to use floats as object ids, but nuscenes-devkit has strings. +-> TODO overwrite the motmetrics code to use strings (or `object`) as object ids again, but otherwise, +use the newer code. + +It looks """ from collections import OrderedDict from itertools import count @@ -15,7 +25,7 @@ import pandas as pd -class MOTAccumulatorCustom(motmetrics.mot.MOTAccumulator): +class MOTAccumulatorCustom(motmetrics.MOTAccumulator): def __init__(self): super().__init__() @@ -57,6 +67,7 @@ def new_event_dataframe(): @property def events(self): + """This is needed to call the custom new_event_dataframe_with_data with the speedup.""" if self.dirty_events: self.cached_events_df = MOTAccumulatorCustom.new_event_dataframe_with_data(self._indices, self._events) self.dirty_events = False diff --git a/setup/requirements/requirements_tracking.txt b/setup/requirements/requirements_tracking.txt index abcc4d75d..50ff8142c 100644 --- a/setup/requirements/requirements_tracking.txt +++ b/setup/requirements/requirements_tracking.txt @@ -1,2 +1,2 @@ -motmetrics<=1.1.3 +motmetrics==1.4.0 pandas>=0.24 From 8359453ca06dcbd4dd8c607f41c569a21121b822 Mon Sep 17 00:00:00 2001 From: Michael Hoss Date: Fri, 26 Jan 2024 15:22:25 +0100 Subject: [PATCH 2/9] replace MOTAccumulatorCustom by 1.4.0 implementation --- python-sdk/nuscenes/eval/tracking/mot.py | 82 ++++++++++++++---------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/python-sdk/nuscenes/eval/tracking/mot.py b/python-sdk/nuscenes/eval/tracking/mot.py index f8015f377..639919831 100644 --- a/python-sdk/nuscenes/eval/tracking/mot.py +++ b/python-sdk/nuscenes/eval/tracking/mot.py @@ -8,58 +8,72 @@ https://github.com/cheind/py-motmetrics Notes by Michael Hoss: -This code is mainly copy-pasted from the original motmetrics repo, likely from version 1.1.3. -TODO: upgrade this code to version 1.4.0, or even better, see if I can just use the origial code. --> looks like I can pretty much use the original code, as the changes here were mostly done for speed. -But: the motmetrics 1.4.0 code seems to use floats as object ids, but nuscenes-devkit has strings. --> TODO overwrite the motmetrics code to use strings (or `object`) as object ids again, but otherwise, -use the newer code. - -It looks +For Python 3.10, we need to update the version of py-motmetrics to 1.4.0. +Then, to keep this code working, we need to change back the types of OId HId to object because they are +strings in nuscenes-devkit, whereas motmetrics changed these types to float from 1.1.3 to 1.4.0. """ from collections import OrderedDict from itertools import count -import motmetrics import numpy as np import pandas as pd +from motmetrics import MOTAccumulator +_INDEX_FIELDS = ['FrameId', 'Event'] -class MOTAccumulatorCustom(motmetrics.MOTAccumulator): +class MOTAccumulatorCustom(MOTAccumulator): + """This custom class was created by nuscenes-devkit to use a faster implementation of + `new_event_dataframe_with_data` under compatibility with motmetrics<=1.1.3. + Now that we use motmetrics==1.4.0, we need to use this custom implementation to use + objects instead of strings for OId and HId. + """ def __init__(self): super().__init__() @staticmethod def new_event_dataframe_with_data(indices, events): - """ - Create a new DataFrame filled with data. - This version overwrites the original in MOTAccumulator achieves about 2x speedups. + """Create a new DataFrame filled with data. Params ------ - indices: list - list of tuples (frameid, eventid) - events: list - list of events where each event is a list containing - 'Type', 'OId', HId', 'D' + indices: dict + dict of lists with fields 'FrameId' and 'Event' + events: dict + dict of lists with fields 'Type', 'OId', 'HId', 'D' """ - idx = pd.MultiIndex.from_tuples(indices, names=['FrameId', 'Event']) - df = pd.DataFrame(events, index=idx, columns=['Type', 'OId', 'HId', 'D']) + + if len(events) == 0: + return MOTAccumulatorCustom.new_event_dataframe() + + raw_type = pd.Categorical( + events['Type'], + categories=['RAW', 'FP', 'MISS', 'SWITCH', 'MATCH', 'TRANSFER', 'ASCEND', 'MIGRATE'], + ordered=False) + series = [ + pd.Series(raw_type, name='Type'), + pd.Series(events['OId'], dtype=object, name='OId'), # OId is string in nuscenes-devkit + pd.Series(events['HId'], dtype=object, name='HId'), # HId is string in nuscenes-devkit + pd.Series(events['D'], dtype=float, name='D') + ] + + idx = pd.MultiIndex.from_arrays( + [indices[field] for field in _INDEX_FIELDS], + names=_INDEX_FIELDS) + df = pd.concat(series, axis=1) + df.index = idx return df @staticmethod def new_event_dataframe(): - """ Create a new DataFrame for event tracking. """ + """Create a new DataFrame for event tracking.""" idx = pd.MultiIndex(levels=[[], []], codes=[[], []], names=['FrameId', 'Event']) - cats = pd.Categorical([], categories=['RAW', 'FP', 'MISS', 'SWITCH', 'MATCH']) + cats = pd.Categorical([], categories=['RAW', 'FP', 'MISS', 'SWITCH', 'MATCH', 'TRANSFER', 'ASCEND', 'MIGRATE']) df = pd.DataFrame( OrderedDict([ - ('Type', pd.Series(cats)), # Type of event. One of FP (false positive), MISS, SWITCH, MATCH - ('OId', pd.Series(dtype=object)), - # Object ID or -1 if FP. Using float as missing values will be converted to NaN anyways. - ('HId', pd.Series(dtype=object)), - # Hypothesis ID or NaN if MISS. Using float as missing values will be converted to NaN anyways. - ('D', pd.Series(dtype=float)), # Distance or NaN when FP or MISS + ('Type', pd.Series(cats)), # Type of event. One of FP (false positive), MISS, SWITCH, MATCH + ('OId', pd.Series(dtype=object)), # Object ID or -1 if FP. Using float as missing values will be converted to NaN anyways. + ('HId', pd.Series(dtype=object)), # Hypothesis ID or NaN if MISS. Using float as missing values will be converted to NaN anyways. + ('D', pd.Series(dtype=float)), # Distance or NaN when FP or MISS ]), index=idx ) @@ -67,15 +81,13 @@ def new_event_dataframe(): @property def events(self): - """This is needed to call the custom new_event_dataframe_with_data with the speedup.""" if self.dirty_events: self.cached_events_df = MOTAccumulatorCustom.new_event_dataframe_with_data(self._indices, self._events) self.dirty_events = False return self.cached_events_df @staticmethod - def merge_event_dataframes(dfs, update_frame_indices=True, update_oids=True, update_hids=True, - return_mappings=False): + def merge_event_dataframes(dfs, update_frame_indices=True, update_oids=True, update_hids=True, return_mappings=False): """Merge dataframes. Params @@ -115,8 +127,8 @@ def merge_event_dataframes(dfs, update_frame_indices=True, update_oids=True, upd # Update index if update_frame_indices: - next_frame_id = max(r.index.get_level_values(0).max() + 1, - r.index.get_level_values(0).unique().shape[0]) + # pylint: disable=cell-var-from-loop + next_frame_id = max(r.index.get_level_values(0).max() + 1, r.index.get_level_values(0).unique().shape[0]) if np.isnan(next_frame_id): next_frame_id = 0 copy.index = copy.index.map(lambda x: (x[0] + next_frame_id, x[1])) @@ -124,15 +136,19 @@ def merge_event_dataframes(dfs, update_frame_indices=True, update_oids=True, upd # Update object / hypothesis ids if update_oids: + # pylint: disable=cell-var-from-loop oid_map = dict([oid, str(next(new_oid))] for oid in copy['OId'].dropna().unique()) copy['OId'] = copy['OId'].map(lambda x: oid_map[x], na_action='ignore') infos['oid_map'] = oid_map if update_hids: + # pylint: disable=cell-var-from-loop hid_map = dict([hid, str(next(new_hid))] for hid in copy['HId'].dropna().unique()) copy['HId'] = copy['HId'].map(lambda x: hid_map[x], na_action='ignore') infos['hid_map'] = hid_map + # Avoid pandas warning. But is this legit/do we need such a column later on again? + # copy = copy.dropna(axis=1, how='all') r = pd.concat((r, copy)) mapping_infos.append(infos) From b22705f563eac3f7497e1e0150d0a8c00537aeb7 Mon Sep 17 00:00:00 2001 From: Michael Hoss Date: Fri, 26 Jan 2024 15:25:06 +0100 Subject: [PATCH 3/9] add pred_frequencies, as this is now required --- .../nuscenes/eval/tracking/constants.py | 1 + python-sdk/nuscenes/eval/tracking/evaluate.py | 16 +++++++++------ python-sdk/nuscenes/eval/tracking/utils.py | 20 +++++++++++++------ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/python-sdk/nuscenes/eval/tracking/constants.py b/python-sdk/nuscenes/eval/tracking/constants.py index c90fc63d1..c3c75bb32 100644 --- a/python-sdk/nuscenes/eval/tracking/constants.py +++ b/python-sdk/nuscenes/eval/tracking/constants.py @@ -17,6 +17,7 @@ MOT_METRIC_MAP = { # Mapping from motmetrics names to metric names used here. 'num_frames': '', # Used in FAF. 'num_objects': 'gt', # Used in MOTAR computation. + 'pred_frequencies': '', # Only printed out. 'num_predictions': '', # Only printed out. 'num_matches': 'tp', # Used in MOTAR computation and printed out. 'motar': 'motar', # Only used in AMOTA. diff --git a/python-sdk/nuscenes/eval/tracking/evaluate.py b/python-sdk/nuscenes/eval/tracking/evaluate.py index 8cc1d456b..941847b99 100644 --- a/python-sdk/nuscenes/eval/tracking/evaluate.py +++ b/python-sdk/nuscenes/eval/tracking/evaluate.py @@ -5,17 +5,21 @@ import json import os import time -from typing import Tuple, List, Dict, Any +from typing import Any, Dict, List, Tuple import numpy as np - from nuscenes import NuScenes from nuscenes.eval.common.config import config_factory -from nuscenes.eval.common.loaders import load_prediction, load_gt, add_center_dist, filter_eval_boxes +from nuscenes.eval.common.loaders import add_center_dist, filter_eval_boxes, load_gt, load_prediction from nuscenes.eval.tracking.algo import TrackingEvaluation -from nuscenes.eval.tracking.constants import AVG_METRIC_MAP, MOT_METRIC_MAP, LEGACY_METRICS -from nuscenes.eval.tracking.data_classes import TrackingMetrics, TrackingMetricDataList, TrackingConfig, TrackingBox, \ - TrackingMetricData +from nuscenes.eval.tracking.constants import AVG_METRIC_MAP, LEGACY_METRICS, MOT_METRIC_MAP +from nuscenes.eval.tracking.data_classes import ( + TrackingBox, + TrackingConfig, + TrackingMetricData, + TrackingMetricDataList, + TrackingMetrics, +) from nuscenes.eval.tracking.loaders import create_tracks from nuscenes.eval.tracking.render import recall_metric_curve, summary_plot from nuscenes.eval.tracking.utils import print_final_metrics diff --git a/python-sdk/nuscenes/eval/tracking/utils.py b/python-sdk/nuscenes/eval/tracking/utils.py index da078f290..4f2a335e0 100644 --- a/python-sdk/nuscenes/eval/tracking/utils.py +++ b/python-sdk/nuscenes/eval/tracking/utils.py @@ -3,7 +3,7 @@ import unittest import warnings -from typing import Optional, Dict +from typing import Dict, Optional import numpy as np @@ -14,8 +14,15 @@ raise unittest.SkipTest('Skipping test as motmetrics was not found!') from nuscenes.eval.tracking.data_classes import TrackingMetrics -from nuscenes.eval.tracking.metrics import motar, mota_custom, motp_custom, faf, track_initialization_duration, \ - longest_gap_duration, num_fragmentations_custom +from nuscenes.eval.tracking.metrics import ( + faf, + longest_gap_duration, + mota_custom, + motar, + motp_custom, + num_fragmentations_custom, + track_initialization_duration, +) def category_to_tracking_name(category_name: str) -> Optional[str]: @@ -111,6 +118,7 @@ def print_threshold_metrics(metrics: Dict[str, Dict[str, float]]) -> None: recall = metrics['recall'][threshold_str] num_frames = metrics['num_frames'][threshold_str] num_objects = metrics['num_objects'][threshold_str] + pred_frequencies = metrics['pred_frequencies'][threshold_str] num_predictions = metrics['num_predictions'][threshold_str] num_false_positives = metrics['num_false_positives'][threshold_str] num_misses = metrics['num_misses'][threshold_str] @@ -124,7 +132,7 @@ def print_threshold_metrics(metrics: Dict[str, Dict[str, float]]) -> None: 'Pred', 'Pred-TP', 'Pred-FP', 'Pred-IDS',)) print('%s\t%.3f\t%.3f\t%.3f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d' % (threshold_str, motar_val, motp, recall, num_frames, - num_objects, num_matches, num_misses, num_switches, + num_objects, num_matches, num_misses, num_switches, pred_frequencies, num_predictions, num_matches, num_false_positives, num_switches)) print() @@ -148,8 +156,8 @@ def create_motmetrics() -> MetricsHost: # Register standard metrics. fields = [ 'num_frames', 'obj_frequencies', 'num_matches', 'num_switches', 'num_false_positives', 'num_misses', - 'num_detections', 'num_objects', 'num_predictions', 'mostly_tracked', 'mostly_lost', 'num_fragmentations', - 'motp', 'mota', 'precision', 'recall', 'track_ratios' + 'num_detections', 'num_objects', 'pred_frequencies', 'num_predictions', 'mostly_tracked', 'mostly_lost', + 'num_fragmentations', 'motp', 'mota', 'precision', 'recall', 'track_ratios' ] for field in fields: mh.register(getattr(motmetrics.metrics, field), formatter='{:d}'.format) From a6c6371099306e5adc4a999921940c182f261992 Mon Sep 17 00:00:00 2001 From: Michael Hoss Date: Fri, 26 Jan 2024 16:29:51 +0100 Subject: [PATCH 4/9] fix printing of pred_frequencies --- python-sdk/nuscenes/eval/tracking/constants.py | 2 +- python-sdk/nuscenes/eval/tracking/utils.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/python-sdk/nuscenes/eval/tracking/constants.py b/python-sdk/nuscenes/eval/tracking/constants.py index c3c75bb32..5fde8db41 100644 --- a/python-sdk/nuscenes/eval/tracking/constants.py +++ b/python-sdk/nuscenes/eval/tracking/constants.py @@ -17,7 +17,7 @@ MOT_METRIC_MAP = { # Mapping from motmetrics names to metric names used here. 'num_frames': '', # Used in FAF. 'num_objects': 'gt', # Used in MOTAR computation. - 'pred_frequencies': '', # Only printed out. + 'pred_frequencies': '', # Only needed in background. 'num_predictions': '', # Only printed out. 'num_matches': 'tp', # Used in MOTAR computation and printed out. 'motar': 'motar', # Only used in AMOTA. diff --git a/python-sdk/nuscenes/eval/tracking/utils.py b/python-sdk/nuscenes/eval/tracking/utils.py index 4f2a335e0..3bbd59e96 100644 --- a/python-sdk/nuscenes/eval/tracking/utils.py +++ b/python-sdk/nuscenes/eval/tracking/utils.py @@ -118,7 +118,6 @@ def print_threshold_metrics(metrics: Dict[str, Dict[str, float]]) -> None: recall = metrics['recall'][threshold_str] num_frames = metrics['num_frames'][threshold_str] num_objects = metrics['num_objects'][threshold_str] - pred_frequencies = metrics['pred_frequencies'][threshold_str] num_predictions = metrics['num_predictions'][threshold_str] num_false_positives = metrics['num_false_positives'][threshold_str] num_misses = metrics['num_misses'][threshold_str] @@ -132,7 +131,7 @@ def print_threshold_metrics(metrics: Dict[str, Dict[str, float]]) -> None: 'Pred', 'Pred-TP', 'Pred-FP', 'Pred-IDS',)) print('%s\t%.3f\t%.3f\t%.3f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d' % (threshold_str, motar_val, motp, recall, num_frames, - num_objects, num_matches, num_misses, num_switches, pred_frequencies, + num_objects, num_matches, num_misses, num_switches, num_predictions, num_matches, num_false_positives, num_switches)) print() From 1f96078665c4f88a0f633f4a4bdeb5a8eac596f7 Mon Sep 17 00:00:00 2001 From: Michael Hoss Date: Fri, 26 Jan 2024 16:23:02 +0100 Subject: [PATCH 5/9] fix ana in signature --- python-sdk/nuscenes/eval/tracking/metrics.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python-sdk/nuscenes/eval/tracking/metrics.py b/python-sdk/nuscenes/eval/tracking/metrics.py index fad933056..b4215b187 100644 --- a/python-sdk/nuscenes/eval/tracking/metrics.py +++ b/python-sdk/nuscenes/eval/tracking/metrics.py @@ -7,7 +7,7 @@ py-motmetrics at: https://github.com/cheind/py-motmetrics """ -from typing import Any +from typing import Any, Optional import numpy as np @@ -109,7 +109,7 @@ def longest_gap_duration(df: DataFrame, obj_frequencies: DataFrame) -> float: def motar(df: DataFrame, num_matches: int, num_misses: int, num_switches: int, num_false_positives: int, - num_objects: int, alpha: float = 1.0) -> float: + num_objects: int, alpha: float = 1.0, ana: Optional[dict] = None) -> float: """ Initializes a MOTAR class which refers to the modified MOTA metric at https://www.nuscenes.org/tracking. Note that we use the measured recall, which is not identical to the hypothetical recall of the @@ -121,6 +121,7 @@ def motar(df: DataFrame, num_matches: int, num_misses: int, num_switches: int, n :param num_false_positives: The number of false positives. :param num_objects: The total number of objects of this class in the GT. :param alpha: MOTAR weighting factor (previously 0.2). + :param ana: something for caching, introduced by motmetrics 1.4.0 :return: The MOTAR or nan if there are no GT objects. """ recall = num_matches / num_objects From f68c6d4d21f2f366db0a6ae5a76e16bf60e7db5a Mon Sep 17 00:00:00 2001 From: Michael Hoss Date: Fri, 26 Jan 2024 17:53:15 +0100 Subject: [PATCH 6/9] leave notes for weird pandas issue. to be debugged further --- python-sdk/nuscenes/eval/tracking/mot.py | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/python-sdk/nuscenes/eval/tracking/mot.py b/python-sdk/nuscenes/eval/tracking/mot.py index 639919831..b2d39ebda 100644 --- a/python-sdk/nuscenes/eval/tracking/mot.py +++ b/python-sdk/nuscenes/eval/tracking/mot.py @@ -57,6 +57,7 @@ def new_event_dataframe_with_data(indices, events): ] idx = pd.MultiIndex.from_arrays( + # TODO What types are the indices FrameId and Event? string or int? [indices[field] for field in _INDEX_FIELDS], names=_INDEX_FIELDS) df = pd.concat(series, axis=1) @@ -128,6 +129,42 @@ def merge_event_dataframes(dfs, update_frame_indices=True, update_oids=True, upd # Update index if update_frame_indices: # pylint: disable=cell-var-from-loop + # TODO TypeError: can only concatenate tuple (not "int") to tuple + # This is likely because we have a multi-index dataframe r here (see new_event_dataframe()) + # See also https://stackoverflow.com/questions/39080555/pandas-get-level-values-for-multiple-columns + # Playground code: https://onecompiler.com/python/422kn8tev + """ + + import pandas as pd + + a={"gk":[15,12,13,22,32,12],"mk":[12,21,23,22,56,12], "sf": [1,2,3,4,5,5]} + df=pd.DataFrame(a) + + # B=df[df["mk"]>=21] + + # print(df) + # print(B) + + df = df.set_index(["gk", "sf"]) + + print(df) + + print("Experiment") + print(df.index.get_level_values(1)) + print("First argument of max") + print(df.index.get_level_values(0).max()) + print(df.index.get_level_values(0).max() +1) # the maximum value of the 0th index column incremented by 1 + print(df.index.get_level_values(1).max()) + print(df.index.get_level_values(1).max() +1) + print("Second argument of max") + print(df.index.get_level_values(0)) + print(df.index.get_level_values(0).unique()) + print(df.index.get_level_values(0).unique().shape) + print(df.index.get_level_values(0).unique().shape[0]) # number of unique values in the 0th index column + print("Final max evaluation") + print(max(df.index.get_level_values(0).max() +1,df.index.get_level_values(0).unique().shape[0])) + """ + next_frame_id = max(r.index.get_level_values(0).max() + 1, r.index.get_level_values(0).unique().shape[0]) if np.isnan(next_frame_id): next_frame_id = 0 From 02a689b72f540324e13a1c78adf1b5f9261f3483 Mon Sep 17 00:00:00 2001 From: Michael Hoss Date: Tue, 30 Jan 2024 18:10:02 +0100 Subject: [PATCH 7/9] tidy up and fix bug of messing up an empty MultiIndex --- python-sdk/nuscenes/eval/tracking/mot.py | 40 ++---------------------- 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/python-sdk/nuscenes/eval/tracking/mot.py b/python-sdk/nuscenes/eval/tracking/mot.py index b2d39ebda..53b813f3c 100644 --- a/python-sdk/nuscenes/eval/tracking/mot.py +++ b/python-sdk/nuscenes/eval/tracking/mot.py @@ -57,7 +57,6 @@ def new_event_dataframe_with_data(indices, events): ] idx = pd.MultiIndex.from_arrays( - # TODO What types are the indices FrameId and Event? string or int? [indices[field] for field in _INDEX_FIELDS], names=_INDEX_FIELDS) df = pd.concat(series, axis=1) @@ -129,46 +128,11 @@ def merge_event_dataframes(dfs, update_frame_indices=True, update_oids=True, upd # Update index if update_frame_indices: # pylint: disable=cell-var-from-loop - # TODO TypeError: can only concatenate tuple (not "int") to tuple - # This is likely because we have a multi-index dataframe r here (see new_event_dataframe()) - # See also https://stackoverflow.com/questions/39080555/pandas-get-level-values-for-multiple-columns - # Playground code: https://onecompiler.com/python/422kn8tev - """ - - import pandas as pd - - a={"gk":[15,12,13,22,32,12],"mk":[12,21,23,22,56,12], "sf": [1,2,3,4,5,5]} - df=pd.DataFrame(a) - - # B=df[df["mk"]>=21] - - # print(df) - # print(B) - - df = df.set_index(["gk", "sf"]) - - print(df) - - print("Experiment") - print(df.index.get_level_values(1)) - print("First argument of max") - print(df.index.get_level_values(0).max()) - print(df.index.get_level_values(0).max() +1) # the maximum value of the 0th index column incremented by 1 - print(df.index.get_level_values(1).max()) - print(df.index.get_level_values(1).max() +1) - print("Second argument of max") - print(df.index.get_level_values(0)) - print(df.index.get_level_values(0).unique()) - print(df.index.get_level_values(0).unique().shape) - print(df.index.get_level_values(0).unique().shape[0]) # number of unique values in the 0th index column - print("Final max evaluation") - print(max(df.index.get_level_values(0).max() +1,df.index.get_level_values(0).unique().shape[0])) - """ - next_frame_id = max(r.index.get_level_values(0).max() + 1, r.index.get_level_values(0).unique().shape[0]) if np.isnan(next_frame_id): next_frame_id = 0 - copy.index = copy.index.map(lambda x: (x[0] + next_frame_id, x[1])) + if not copy.index.empty: + copy.index = copy.index.map(lambda x: (x[0] + next_frame_id, x[1])) infos['frame_offset'] = next_frame_id # Update object / hypothesis ids From 8e06003efcd4266a078590edbc65bf06811ea1bc Mon Sep 17 00:00:00 2001 From: Dominik Dienlin Date: Fri, 22 Mar 2024 12:39:25 +0100 Subject: [PATCH 8/9] fix requirements specification for compatibility with python3.12 --- setup/requirements/requirements_base.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup/requirements/requirements_base.txt b/setup/requirements/requirements_base.txt index aa8eff2f6..85b6c6a68 100644 --- a/setup/requirements/requirements_base.txt +++ b/setup/requirements/requirements_base.txt @@ -1,12 +1,12 @@ cachetools descartes fire -matplotlib<3.6.0 -numpy>=1.22.0 +matplotlib +numpy>=1.22.0,<2.0 opencv-python>=4.5.4.58 Pillow>6.2.1 pyquaternion>=0.9.5 scikit-learn scipy -Shapely<2.0.0 +Shapely~=2.0.3 tqdm From d6a1964af9be9a56bae33fdea1a70eb582dc7593 Mon Sep 17 00:00:00 2001 From: Dominik Dienlin Date: Fri, 22 Mar 2024 12:39:52 +0100 Subject: [PATCH 9/9] fix issue with setting title via matplotlib when using newer versions of matplotlib --- python-sdk/nuscenes/nuscenes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python-sdk/nuscenes/nuscenes.py b/python-sdk/nuscenes/nuscenes.py index c36c023ce..9bd61d1bc 100644 --- a/python-sdk/nuscenes/nuscenes.py +++ b/python-sdk/nuscenes/nuscenes.py @@ -1023,9 +1023,9 @@ def render_pointcloud_in_image(self, if ax is None: fig, ax = plt.subplots(1, 1, figsize=(9, 16)) if lidarseg_preds_bin_path: - fig.canvas.set_window_title(sample_token + '(predictions)') + fig.canvas.manager.set_window_title(sample_token + '(predictions)') else: - fig.canvas.set_window_title(sample_token) + fig.canvas.manager.set_window_title(sample_token) else: # Set title on if rendering as part of render_sample. ax.set_title(camera_channel) ax.imshow(im)