quantumjot / btrack

Bayesian multi-object tracking
https://btrack.readthedocs.io
MIT License
311 stars 50 forks source link

track properties requirements #210

Closed will-moore closed 1 year ago

will-moore commented 1 year ago

Thanks for creating this plugin!

If I create tracks in napari without any properties or if the properties are missing a "t" value, I get an error when I click on a track.

I don't see that requirement documented anywhere. Is this really essential?

Key Error stack trace ``` KeyError Traceback (most recent call last) File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/pandas/core/indexes/base.py:3802, in Index.get_loc(self=Index(['id', 'entity', 'name', 'sphericity', 'volume', 'track_id'], dtype='object'), key='t', method=None, tolerance=None) 3801 try: -> 3802 return self._engine.get_loc(casted_key) casted_key = 't' self = Index(['id', 'entity', 'name', 'sphericity', 'volume', 'track_id'], dtype='object') 3803 except KeyError as err: File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/pandas/_libs/index.pyx:138, in pandas._libs.index.IndexEngine.get_loc() File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/pandas/_libs/index.pyx:165, in pandas._libs.index.IndexEngine.get_loc() File pandas/_libs/hashtable_class_helper.pxi:5745, in pandas._libs.hashtable.PyObjectHashTable.get_item() File pandas/_libs/hashtable_class_helper.pxi:5753, in pandas._libs.hashtable.PyObjectHashTable.get_item() KeyError: 't' The above exception was the direct cause of the following exception: KeyError Traceback (most recent call last) File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/vispy/app/backends/_qt.py:522, in QtBaseCanvasBackend.mouseDoubleClickEvent(self=, ev=) 520 if self._vispy_canvas is None: 521 return --> 522 self._vispy_mouse_double_click( self = ev = BUTTONMAP = {0: 0, 1: 1, 2: 2, 4: 3, 8: 4, 16: 5} 523 native=ev, 524 pos=_get_event_xy(ev), 525 button=BUTTONMAP.get(ev.button(), 0), 526 modifiers=self._modifiers(ev), 527 ) File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/vispy/app/base.py:239, in BaseCanvasBackend._vispy_mouse_double_click(self=, **kwargs={'button': 1, 'buttons': [], 'last_event': , 'last_mouse_press': None, 'modifiers': (), 'native': , 'pos': (197, 533), 'press_event': None}) 235 def _vispy_mouse_double_click(self, **kwargs): 236 # default method for delivering double-click events to the canvas 237 kwargs.update(self._vispy_mouse_data) --> 239 ev = self._vispy_canvas.events.mouse_double_click(**kwargs) self._vispy_canvas.events.mouse_double_click = kwargs = {'native': , 'pos': (197, 533), 'button': 1, 'modifiers': (), 'buttons': [], 'press_event': None, 'last_event': pos=[197 533] press_event=MouseEvent source=None sources=[] time=1678285810.7207735 type=mouse_release>, 'last_mouse_press': None} self = self._vispy_canvas.events = self._vispy_canvas = 240 self._vispy_mouse_data['last_event'] = ev 241 return ev File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/vispy/util/event.py:453, in EventEmitter.__call__(self=, *args=(), **kwargs={'button': 1, 'buttons': [], 'last_event': , 'last_mouse_press': None, 'modifiers': (), 'native': , 'pos': (197, 533), 'press_event': None}) 450 if self._emitting > 1: 451 raise RuntimeError('EventEmitter loop detected!') --> 453 self._invoke_callback(cb, event) event = pos=[197 533] press_event=None source=None sources=[] time=1678285810.8208368 type=mouse_double_click> self = cb = > 454 if event.blocked: 455 break File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/vispy/util/event.py:471, in EventEmitter._invoke_callback(self=, cb=>, event=) 469 cb(event) 470 except Exception: --> 471 _handle_exception(self.ignore_callback_errors, self = cb = > event = pos=[197 533] press_event=None source=None sources=[] time=1678285810.8208368 type=mouse_double_click> (cb, event) = (>, pos=[197 533] press_event=None source=None sources=[] time=1678285810.8208368 type=mouse_double_click>) 472 self.print_callback_errors, 473 self, cb_event=(cb, event)) File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/vispy/util/event.py:469, in EventEmitter._invoke_callback(self=, cb=>, event=) 467 def _invoke_callback(self, cb, event): 468 try: --> 469 cb(event) cb = > event = pos=[197 533] press_event=None source=None sources=[] time=1678285810.8208368 type=mouse_double_click> 470 except Exception: 471 _handle_exception(self.ignore_callback_errors, 472 self.print_callback_errors, 473 self, cb_event=(cb, event)) File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/napari/_qt/qt_viewer.py:1057, in QtViewer.on_mouse_double_click(self=, event=) 1038 def on_mouse_double_click(self, event): 1039 """Called whenever a mouse double-click happen on the canvas 1040 1041 Parameters (...) 1055 - mouse_release 1056 """ -> 1057 self._process_mouse_event(mouse_double_click_callbacks, event) event = pos=[197 533] press_event=None source=None sources=[] time=1678285810.8208368 type=mouse_double_click> self = File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/napari/_qt/qt_viewer.py:1026, in QtViewer._process_mouse_event(self=, mouse_callbacks=, event=) 1024 layer = self.viewer.layers.selection.active 1025 if layer is not None: -> 1026 mouse_callbacks(layer, event) event = layer = mouse_callbacks = File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/napari/utils/interactions.py:86, in mouse_double_click_callbacks(obj=, event=) 80 if inspect.isgeneratorfunction(mouse_click_func): 81 raise ValueError( 82 trans._( 83 "Double-click actions can't be generators.", deferred=True 84 ) 85 ) ---> 86 mouse_click_func(obj, event) mouse_click_func = .show_tree at 0x17a669820> obj = event = File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/napari_arboretum/plugin.py:105, in Arboretum.append_mouse_callback..show_tree(tracks=, event=) 101 track_id = tracks.get_value(cursor_position, world=True) 102 if track_id is not None: 103 # Setting this property automatically triggers re-drawing of the 104 # tree and property graph --> 105 self.track_id = track_id track_id = 1 self = 106 self.draw_current_time_line() File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/napari_arboretum/util.py:35, in TrackPropertyMixin.track_id(self=, track_id=1) 32 @track_id.setter 33 def track_id(self, track_id: int) -> None: 34 self._track_id = track_id ---> 35 self.on_track_id_change() self = File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/napari_arboretum/plugin.py:68, in Arboretum.on_track_id_change(self=) 66 def on_track_id_change(self): 67 self.plotter.track_id = self.track_id ---> 68 self.property_plotter.track_id = self.track_id self = self.property_plotter = 69 root_id = get_root_id(self.tracks, self.track_id) 70 self.title.setText(f"Lineage Tree #{root_id}") File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/napari_arboretum/util.py:35, in TrackPropertyMixin.track_id(self=, track_id=1) 32 @track_id.setter 33 def track_id(self, track_id: int) -> None: 34 self._track_id = track_id ---> 35 self.on_track_id_change() self = File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/napari_arboretum/visualisation/base_plotter.py:153, in PropertyPlotterBase.on_track_id_change(self=) 152 def on_track_id_change(self) -> None: --> 153 self.plot_property() self = File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/napari_arboretum/visualisation/base_plotter.py:161, in PropertyPlotterBase.plot_property(self=) 155 def plot_property(self) -> None: 156 """ 157 Plot a property. The property is taken from the currently selected 158 layer, currently selected track_id, and the property used to 'color_by' 159 in the napari viewer. 160 """ --> 161 t, prop = self.get_track_properties() self = 163 self.clear() 164 self.plot(t, prop) File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/napari_arboretum/visualisation/base_plotter.py:184, in PropertyPlotterBase.get_track_properties(self=) 182 all_props = pd.DataFrame(self.tracks.properties) 183 all_props = all_props.loc[all_props["track_id"] == self.track_id] --> 184 return all_props["t"].values, all_props[self.tracks.color_by].values all_props = id entity name sphericity volume track_id 34 35000 line P0 0.283824 412587.0 1 35 36000 line AB 0.534055 265596.0 1 36 37000 line AB 0.530781 257160.0 1 37 38000 line AB 0.563564 292312.0 1 38 39000 line AB 0.534701 284133.0 1 39 40000 line AB 0.660281 240985.0 1 40 41000 line AB 0.667198 236730.0 1 41 42000 line AB 0.650135 220862.0 1 42 43000 line AB 0.726241 243857.0 1 43 44000 line AB 0.736025 227168.0 1 44 45000 line AB 0.733346 254991.0 1 45 46000 line AB 0.780814 273370.0 1 46 47000 line AB 0.769157 233345.0 1 47 48000 line AB 0.752062 257244.0 1 48 49000 line AB 0.791786 291136.0 1 self = File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/pandas/core/frame.py:3807, in DataFrame.__getitem__(self= id entity name sphericity volume tra...49000 line AB 0.791786 291136.0 1, key='t') 3805 if self.columns.nlevels > 1: 3806 return self._getitem_multilevel(key) -> 3807 indexer = self.columns.get_loc(key) key = 't' indexer = None self = id entity name sphericity volume track_id 34 35000 line P0 0.283824 412587.0 1 35 36000 line AB 0.534055 265596.0 1 36 37000 line AB 0.530781 257160.0 1 37 38000 line AB 0.563564 292312.0 1 38 39000 line AB 0.534701 284133.0 1 39 40000 line AB 0.660281 240985.0 1 40 41000 line AB 0.667198 236730.0 1 41 42000 line AB 0.650135 220862.0 1 42 43000 line AB 0.726241 243857.0 1 43 44000 line AB 0.736025 227168.0 1 44 45000 line AB 0.733346 254991.0 1 45 46000 line AB 0.780814 273370.0 1 46 47000 line AB 0.769157 233345.0 1 47 48000 line AB 0.752062 257244.0 1 48 49000 line AB 0.791786 291136.0 1 3808 if is_integer(indexer): 3809 indexer = [indexer] File ~/opt/anaconda3/envs/ssbd/lib/python3.9/site-packages/pandas/core/indexes/base.py:3804, in Index.get_loc(self=Index(['id', 'entity', 'name', 'sphericity', 'volume', 'track_id'], dtype='object'), key='t', method=None, tolerance=None) 3802 return self._engine.get_loc(casted_key) 3803 except KeyError as err: -> 3804 raise KeyError(key) from err key = 't' 3805 except TypeError: 3806 # If we have a listlike key, _check_indexing_error will raise 3807 # InvalidIndexError. Otherwise we fall through and re-raise 3808 # the TypeError. 3809 self._check_indexing_error(key) KeyError: 't' ```
quantumjot commented 1 year ago

Hi @will-moore ! You shouldn't need a "t" key in the properties table. Are you able to a code snippet to help debug?

will-moore commented 1 year ago

This seems to do the trick:

viewer = napari.Viewer()
tracks = [
    [0, 1, 39.92264938, 109.31625366, 307.4147644],
    [0, 2, 39.49820709, 113.88571167, 309.51132202],
    [0, 3, 39.99920273, 111.54975128, 315.68569946],
    [0, 4, 40.12161255, 112.83349609, 321.04629517],
    [0, 5, 42.20673752, 115.65319824, 332.90838623],
    [0, 6, 42.50518417, 117.12906647, 328.36157227],
]

viewer._add_layer_from_data(tracks, {}, "tracks")
napari.run()

Then launch the arboretum plugin and double-click on the track:

Screenshot 2023-03-08 at 20 39 08
quantumjot commented 1 year ago

Yes - I can reproduce this.

It seems to stem from this line: https://github.com/lowe-lab-ucl/arboretum/blob/ae378b9687df550aef9b1c6e6328869fe12cf478/napari_arboretum/visualisation/base_plotter.py#L180

I'll take a look and try to fix this

quantumjot commented 1 year ago

@will-moore - this should be fixed on main now (over at https://github.com/lowe-lab-ucl/arboretum). Let us know if it doesn't work!

will-moore commented 1 year ago

Looks good, thanks!