44
55import betterosi
66import numpy as np
7- import pandas as pd
87import pyproj
98import shapely
109from matplotlib import pyplot as plt
1110from matplotlib .patches import Polygon as PltPolygon
11+ import pandas as pd
12+ import polars as pl
1213
1314import pandera as pa
1415import pandera .extensions as extensions
@@ -165,7 +166,7 @@ def __init__(self, recording, idx):
165166 self .idx = int (idx )
166167 self ._recording = recording
167168
168- self ._df = self ._recording ._df .loc [ self . _recording . _df [ "idx" ] == self .idx ]
169+ self ._df = self ._recording ._df .filter ( pl . col ( "idx" ) == self .idx )
169170
170171 for k in [
171172 "x" ,
@@ -182,23 +183,23 @@ def __init__(self, recording, idx):
182183 "pitch" ,
183184 "polygon" ,
184185 ]:
185- setattr (self , k , self ._df . loc [:, k ]. values )
186- self .vel = np .linalg .norm ([ self .vel_x , self . vel_y ], axis = 0 )
187- self .timestamps = self ._df . loc [:, "total_nanos" ]. values / 1e9
186+ setattr (self , k , self ._df [:, k ])
187+ self .vel = np .linalg .norm (self ._df [ " vel_x" , " vel_y" ], axis = 0 )
188+ self .timestamps = self ._df [ "total_nanos" ] / 1e9
188189 for k in ["length" , "width" , "height" ]:
189- setattr (self , f"{ k } s" , self ._df . loc [:, k ]. values )
190- setattr (self , k , np . mean ( self ._df . loc [:, k ].values ))
190+ setattr (self , f"{ k } s" , self ._df [ k ] )
191+ setattr (self , k , self ._df [ k ].mean ( ))
191192
192- self .type = betterosi .MovingObjectType (self ._df . loc [:, "type" ]. iloc [0 ])
193- subtype_int = self ._df . loc [:, "subtype" ]. iloc [0 ]
193+ self .type = betterosi .MovingObjectType (self ._df [ "type" ][0 ])
194+ subtype_int = self ._df [ "subtype" ][0 ]
194195 self .subtype = betterosi .MovingObjectVehicleClassificationType (subtype_int ) if subtype_int != - 1 else None
195- role_int = self ._df . loc [:, "role" ]. iloc [0 ]
196+ role_int = self ._df [ "role" ][0 ]
196197 self .role = betterosi .MovingObjectVehicleClassificationRole (role_int ) if role_int != - 1 else None
197- self .birth = int (self ._df . loc [:, "frame" ]. iloc [0 ])
198- self .end = int (self ._df . loc [:, "frame" ]. iloc [- 1 ])
198+ self .birth = int (self ._df [ "frame" ][0 ])
199+ self .end = int (self ._df [ "frame" ][- 1 ])
199200
200- def set (self , k , val ):
201- self ._recording ._df .loc [self ._recording ._df ["idx" ] == self .idx , k ] = val
201+ # def set(self, k, val):
202+ # self._recording._df.loc[self._recording._df["idx"] == self.idx, k] = val
202203
203204 @property
204205 def nanos (self ):
@@ -209,7 +210,7 @@ def plot(self, ax: plt.Axes):
209210 pass
210211
211212 def plot_mv_frame (self , ax : plt .Axes , frame : int ):
212- polys = self ._df [ self . _df [ "frame" ] == frame ] ["polygon" ]. values
213+ polys = self ._df . filter ( pl . col ( "frame" ) == frame ) ["polygon" ]
213214 for p in polys :
214215 ax .add_patch (PltPolygon (p .exterior .coords , fc = "red" , alpha = 0.2 ))
215216
@@ -241,10 +242,10 @@ def _get_polygons(df):
241242
242243 @staticmethod
243244 def get_moving_object_ground_truth (
244- nanos : int , df : pd .DataFrame , host_vehicle = None , validate = True
245+ nanos : int , df : pl .DataFrame , host_vehicle = None , validate = False
245246 ) -> betterosi .GroundTruth :
246247 if validate :
247- recording_moving_object_schema .validate (df , lazy = True )
248+ recording_moving_object_schema .validate (df . to_pandas () , lazy = True )
248249
249250 def get_object (row ):
250251 return betterosi .MovingObject (
@@ -262,7 +263,7 @@ def get_object(row):
262263 ),
263264 )
264265
265- mvs = list ( df .apply ( get_object , axis = 1 ). values )
266+ mvs = [ get_object ( r ) for r in df .iter_rows ( named = True )]
266267 gt = betterosi .GroundTruth (
267268 version = betterosi .InterfaceVersion (version_major = 3 , version_minor = 7 , version_patch = 9 ),
268269 timestamp = betterosi .Timestamp (seconds = int (nanos // 1_000_000_000 ), nanos = int (nanos % 1_000_000_000 )),
@@ -273,14 +274,17 @@ def get_object(row):
273274 )
274275 return gt
275276
276- def __init__ (self , df , map = None , projections = None , host_vehicle = None , validate = True ):
277+ def __init__ (self , df , map = None , projections = None , host_vehicle = None , validate = False ):
277278 if validate :
278- recording_moving_object_schema .validate (df , lazy = True )
279+ recording_moving_object_schema .validate (df . to_pandas () , lazy = True )
279280 super ().__init__ ()
280- self .nanos2frame = {n : i for i , n in enumerate (df .total_nanos .unique ())}
281- df ["frame" ] = df .total_nanos .map (self .nanos2frame )
282- if "polygon" not in df .columns :
283- df ["polygon" ] = self ._get_polygons (df )
281+ self .nanos2frame = {n : i for i , n in enumerate (df ["total_nanos" ].unique ())}
282+ mapping = pl .DataFrame ({"total_nanos" : list (self .nanos2frame .keys ()), "frame" : list (self .nanos2frame .values ())})
283+ if 'frame' in df .columns :
284+ df = df .drop ('frame' )
285+ df = df .join (mapping , on = "total_nanos" , how = "left" )
286+ if 'polygon' not in df .columns :
287+ df = df .with_columns ([pl .Series (name = "polygon" , values = self ._get_polygons (df ))])
284288 self .projections = projections
285289 self ._df = df
286290 self .map = map
@@ -290,7 +294,7 @@ def __init__(self, df, map=None, projections=None, host_vehicle=None, validate=T
290294 def to_osi_gts (self ) -> list [betterosi .GroundTruth ]:
291295 gts = [
292296 self .get_moving_object_ground_truth (nanos , group_df , host_vehicle = self .host_vehicle , validate = False )
293- for nanos , group_df in self ._df .groupby ("total_nanos" )
297+ for [ nanos ] , group_df in self ._df .group_by ("total_nanos" )
294298 ]
295299
296300 if self .map is not None and isinstance (self .map , MapOsi | MapOsiCenterline ):
@@ -351,11 +355,11 @@ def from_osi_gts(cls, gts: list[betterosi.GroundTruth], validate: bool = True):
351355 )
352356 for mv in gt .moving_object
353357 ]
354- df_mv = pd .DataFrame (mvs ).sort_values ( by = ["total_nanos" , "idx" ]). reset_index ( drop = "index" )
358+ df_mv = pl .DataFrame (mvs ).sort ( ["total_nanos" , "idx" ])
355359 return cls (df_mv , projections = projs , validate = validate )
356360
357361 @classmethod
358- def from_file (cls , filepath , xodr_path : str | None = None , validate : bool = True , skip_odr_parse : bool = False ):
362+ def from_file (cls , filepath , xodr_path : str | None = None , validate : bool = False , skip_odr_parse : bool = False ):
359363 gts = betterosi .read (
360364 filepath ,
361365 return_ground_truth = True ,
@@ -396,43 +400,47 @@ def to_mcap(self, filepath):
396400
397401 def to_hdf (self , filename , key = "moving_object" ):
398402 #!pip install tables
399- self ._df .drop (columns = ["polygon" , "frame" ]).to_hdf (filename , key = key )
403+ self ._df .drop (columns = ["polygon" , "frame" ]).to_pandas (). to_hdf (filename , key = key )
400404
401405 @classmethod
402406 def from_hdf (cls , filename , key = "moving_object" ):
403- df = pd .read_hdf (filename , key = key )
407+ df = pl . DataFrame ( pd .read_hdf (filename , key = key ) )
404408 return cls (df , map = None , host_vehicle = None )
405409
406410 def interpolate (self , new_nanos : list [int ] | None = None , hz : float | None = None ):
407411 df = self ._df
408412 if new_nanos is None and hz is None :
409413 new_nanos = np .linspace (
410- df . total_nanos .min (), df . total_nanos .max (), df . frame .max () - df . frame .min (), dtype = int
414+ df [ " total_nanos" ] .min (), df [ " total_nanos" ] .max (), df [ " frame" ] .max () - df [ " frame" ] .min (), dtype = int
411415 )
412416 elif hz is not None :
413- step = 1_000_000_000 * hz
414- new_nanos = np .arange (start = df . total_nanos .min (), stop = df . total_nanos .max () + 1 , step = step , dtype = int )
417+ step = 1_000_000_000 / hz
418+ new_nanos = np .arange (start = df [ " total_nanos" ] .min (), stop = df [ " total_nanos" ] .max () + 1 , step = step , dtype = int )
415419 else :
416420 new_nanos = np .array (new_nanos )
417421 new_dfs = []
418- for idx , track_df in df .groupby ("idx" ):
422+ for [ idx ] , track_df in df .group_by ("idx" ):
419423 track_data = {}
420424 track_new_nanos = new_nanos [
421- track_df . frame .min () - df . frame .min () : track_df . frame .max () - df . frame .min () + 1
425+ track_df [ " frame" ] .min () - df [ " frame" ] .min () : track_df [ " frame" ] .max () - df [ " frame" ] .min () + 1
422426 ]
423427 for c in ["x" , "y" , "z" , "vel_x" , "vel_y" , "vel_z" , "acc_x" , "acc_y" , "acc_z" , "length" , "width" , "height" ]:
424428 track_data [c ] = np .interp (track_new_nanos , track_df ["total_nanos" ], track_df [c ])
425429 for c in ["type" , "subtype" , "role" ]:
426- track_data [c ] = nearest_interp (track_new_nanos , track_df ["total_nanos" ].values , track_df [c ].values )
430+ track_data [c ] = nearest_interp (
431+ track_new_nanos , track_df ["total_nanos" ].to_numpy (), track_df [c ].to_numpy ()
432+ )
427433 for c in ["roll" , "pitch" , "yaw" ]:
428434 track_data [c ] = np .mod (
429435 np .interp (track_new_nanos , track_df ["total_nanos" ], np .unwrap (track_df [c ], period = np .pi )), np .pi
430436 )
431- new_track_df = pd .DataFrame (track_data )
432- new_track_df ["idx" ] = idx
433- new_track_df ["total_nanos" ] = track_new_nanos
437+ new_track_df = pl .DataFrame (track_data )
438+ new_track_df = new_track_df .with_columns (
439+ pl .Series (name = "idx" , values = np .ones_like (track_new_nanos ) * idx ),
440+ pl .Series (name = "total_nanos" , values = track_new_nanos ),
441+ )
434442 new_dfs .append (new_track_df )
435- new_df = pd .concat (new_dfs )
443+ new_df = pl .concat (new_dfs )
436444 return self .__init__ (new_df , self .map , self .host_vehicle )
437445
438446 def plot (self , ax = None , legend = False ) -> plt .Axes :
@@ -452,6 +460,6 @@ def plot_frame(self, frame: int, ax=None):
452460 return ax
453461
454462 def plot_mv_frame (self , ax : plt .Axes , frame : int ):
455- polys = self ._df [ self . _df [ "frame" ] == frame ] ["polygon" ]. values
463+ polys = self ._df . filter ( pl . col ( "frame" ) == frame ) ["polygon" ]
456464 for p in polys :
457465 ax .add_patch (PltPolygon (p .exterior .coords , fc = "red" ))
0 commit comments