44from copy import deepcopy
55from enum import Enum
66from time import ctime , time
7+ from typing import Union
78
89import neuroglancer
910import neuroglancer .cli
@@ -47,6 +48,7 @@ def debounced(*args, **kwargs):
4748 return decorator
4849
4950
51+ # TODO further test these fits, 2 point fit not right at the moment
5052def fit_model (fixed_points : np .ndarray , moving_points : np .ndarray ):
5153 """
5254 Choose the appropriate model based on number of points and dimensions.
@@ -164,7 +166,7 @@ def transform_points(affine: np.ndarray, points: np.ndarray):
164166
165167
166168# Only used if no data provided
167- def _create_demo_data (size : int | tuple = 60 , radius : float = 20 ):
169+ def _create_demo_data (size : Union [ int , tuple ] = 60 , radius : float = 20 ):
168170 data_size = (size ,) * NUM_DEMO_DIMS if isinstance (size , int ) else size
169171 data = np .zeros (data_size , dtype = np .uint8 )
170172 if NUM_DEMO_DIMS == 2 :
@@ -255,23 +257,22 @@ class LinearRegistrationWorkflow:
255257 def __init__ (self , args ):
256258 starting_state = args .state
257259 self .annotations_name = args .annotations_name
258- self . status_timers = {}
259- self .stored_points = [[ ], []]
260+
261+ self .stored_points = ([ ], [])
260262 self .stored_map_moving_name_to_data_coords = {}
261263 self .stored_map_moving_name_to_viewer_coords = {}
262- self .moving_layer_names = []
263- self .input_dim_names = []
264- self .output_dim_names = []
265264 self .affine = None
266- self .co_ords_ready = False
267265 self .ready_state = ReadyState .NOT_READY
268- self .last_updated_print_time = - 1
269- self .num_dims = 0
270266 self .viewer = neuroglancer .Viewer ()
271267 self .viewer .shared_state .add_changed_callback (
272268 lambda : self .viewer .defer_callback (self .on_state_changed )
273269 )
274270
271+ self ._last_updated_print_time = - 1
272+ self ._status_timers = {}
273+ self ._current_moving_layer_idx = 0
274+ self ._cached_moving_layer_names = []
275+
275276 if starting_state is None :
276277 self ._add_demo_data_to_viewer ()
277278 else :
@@ -287,9 +288,9 @@ def __init__(self, args):
287288 def update (self ):
288289 """Primary update loop, called whenever the viewer state changes."""
289290 current_time = time ()
290- if current_time - self .last_updated_print_time > 5 :
291+ if current_time - self ._last_updated_print_time > 5 :
291292 print (f"Viewer states are successfully syncing at { ctime ()} " )
292- self .last_updated_print_time = current_time
293+ self ._last_updated_print_time = current_time
293294 if self .ready_state == ReadyState .COORDS_READY :
294295 self .setup_registration_layers ()
295296 elif self .ready_state == ReadyState .ERROR :
@@ -301,10 +302,17 @@ def update(self):
301302 self .update_affine ()
302303 self ._clear_status_messages ()
303304
305+ def get_moving_layer_names (self , s : neuroglancer .ViewerState ):
306+ right_panel_layers = [
307+ n for n in s .layout .children [1 ].layers if n != self .annotations_name
308+ ]
309+ return right_panel_layers
310+
304311 def copy_moving_layers_to_left_panel (self ):
305312 """Make copies of the moving layers to show the registered result."""
306313 with self .viewer .txn () as s :
307- for layer_name in self .moving_layer_names :
314+ self ._cached_moving_layer_names = self .get_moving_layer_names (s )
315+ for layer_name in self ._cached_moving_layer_names :
308316 copy = deepcopy (s .layers [layer_name ])
309317 copy .name = layer_name + "_registered"
310318 copy .visible = False
@@ -313,10 +321,6 @@ def copy_moving_layers_to_left_panel(self):
313321
314322 def setup_viewer_after_user_ready (self ):
315323 """Called when the user indicates they have placed layers in the two panels."""
316- if not self .moving_layer_names :
317- moving_layers = self .get_state ().layout .children [1 ].layers
318- self .moving_layer_names = moving_layers
319- self ._moving_idx = 0
320324 self .copy_moving_layers_to_left_panel ()
321325 self .setup_second_coord_space ()
322326 self ._set_status_message (
@@ -350,7 +354,7 @@ def setup_initial_two_panel_layout(self):
350354
351355 def setup_second_coord_space (self ):
352356 """Set up the second coordinate space for the moving layers."""
353- layer_name = self .moving_layer_names [self ._moving_idx ]
357+ layer_name = self ._cached_moving_layer_names [self ._current_moving_layer_idx ]
354358 info_future = self .viewer .volume_info (layer_name )
355359 info_future .add_done_callback (lambda f : self .save_coord_space_info (f ))
356360
@@ -361,7 +365,7 @@ def combine_affine_across_dims(self, s: neuroglancer.ViewerState, affine):
361365 applies to all dims so we need to create a larger matrix
362366 """
363367 all_dims = s .dimensions .names
364- moving_dims = self .output_dim_names
368+ moving_dims = self .get_fixed_and_moving_dims ( None , all_dims )
365369 full_matrix = np .zeros ((len (all_dims ), len (all_dims ) + 1 ))
366370
367371 for i , dim in enumerate (all_dims ):
@@ -377,12 +381,13 @@ def combine_affine_across_dims(self, s: neuroglancer.ViewerState, affine):
377381 full_matrix [i , - 1 ] = affine [moving_i , - 1 ]
378382 return full_matrix
379383
384+ def has_two_coord_spaces (self , s : neuroglancer .ViewerState ):
385+ fixed_dims , moving_dims = self .get_fixed_and_moving_dims (s )
386+ return len (fixed_dims ) == len (moving_dims )
387+
380388 def setup_registration_layers (self ):
381389 with self .viewer .txn () as s :
382- dimensions = s .dimensions
383- if not self .ready_state == ReadyState .ERROR or (
384- len (dimensions .names ) != self .num_dims
385- ):
390+ if self .ready_state == ReadyState .ERROR or not self .has_two_coord_spaces (s ):
386391 return
387392
388393 # Make the annotation layer if needed
@@ -400,13 +405,16 @@ def setup_registration_layers(self):
400405 self .ready_state = ReadyState .READY
401406
402407 def setup_panel_coordinates (self , s : neuroglancer .ViewerState ):
408+ fixed_dims , moving_dims = self .get_fixed_and_moving_dims (s )
403409 s .layout .children [1 ].displayDimensions .link = "unlinked"
404- s .layout .children [1 ].displayDimensions .value = self . output_dim_names [:3 ]
410+ s .layout .children [1 ].displayDimensions .value = moving_dims [:3 ]
405411 s .layout .children [0 ].displayDimensions .link = "unlinked"
406- s .layout .children [0 ].displayDimensions .value = self . input_dim_names [:3 ]
412+ s .layout .children [0 ].displayDimensions .value = fixed_dims [:3 ]
407413
408414 def save_coord_space_info (self , info_future ):
409- self .moving_name = self .moving_layer_names [self ._moving_idx ]
415+ self .moving_name = self ._cached_moving_layer_names [
416+ self ._current_moving_layer_idx
417+ ]
410418 try :
411419 result = info_future .result ()
412420 except Exception as e :
@@ -421,48 +429,32 @@ def save_coord_space_info(self, info_future):
421429 result .dimensions
422430 )
423431
424- self ._moving_idx += 1
425- if self ._moving_idx < len (self .moving_layer_names ):
432+ self ._current_moving_layer_idx += 1
433+ if self ._current_moving_layer_idx < len (self ._cached_moving_layer_names ):
426434 self .setup_second_coord_space ()
427435 return
428436
429437 # If we get here we have all the coord spaces ready and can update viewer
430438 self .ready_state = ReadyState .COORDS_READY
431439 with self .viewer .txn () as s :
432- for layer_name in self .moving_layer_names :
433- input_dims = self .stored_map_moving_name_to_data_coords .get (
440+ for layer_name in self ._cached_moving_layer_names :
441+ output_dims = self .stored_map_moving_name_to_data_coords .get (
434442 layer_name , None
435443 )
436- if input_dims is None :
444+ if output_dims is None :
437445 self .ready_state = ReadyState .ERROR
438446 continue
439- self .input_dim_names = input_dims .names
440447 self .stored_map_moving_name_to_viewer_coords [layer_name ] = []
441- # TODO this logic I think needs to be improved because volume info
442- # is actually the mapped dims not the input dims
443448 for source in s .layers [layer_name ].source :
444449 if source .transform is None :
445- output_dims = new_coord_space_names (input_dims , "2" )
450+ output_dims = new_coord_space_names (output_dims , "2" )
446451 else :
447452 output_dims = new_coord_space_names (
448453 source .transform .output_dimensions , "2"
449454 )
450- self .output_dim_names = output_dims .names
451455 new_coord_space = neuroglancer .CoordinateSpaceTransform (
452- input_dimensions = input_dims ,
453456 output_dimensions = output_dims ,
454457 )
455- self .num_dims = max (
456- self .num_dims ,
457- 2
458- * len (
459- [
460- n
461- for n in new_coord_space .output_dimensions .names
462- if not n .endswith (("'" , "^" , "#" ))
463- ]
464- ),
465- )
466458 self .stored_map_moving_name_to_viewer_coords [layer_name ].append (
467459 new_coord_space
468460 )
@@ -478,7 +470,7 @@ def continue_workflow(self, _):
478470 elif self .ready_state == ReadyState .ERROR :
479471 self .setup_registration_layers ()
480472 with self .viewer .txn () as s :
481- for layer_name in self .moving_layer_names :
473+ for layer_name in self .get_moving_layer_names ( s ) :
482474 registered_name = layer_name + "_registered"
483475 is_registered_visible = s .layers [registered_name ].visible
484476 s .layers [registered_name ].visible = not is_registered_visible
@@ -499,8 +491,8 @@ def update_affine(self):
499491 with self .viewer .txn () as s :
500492 self .estimate_affine (s )
501493
502- def get_moving_and_fixed_dims (
503- self , s : neuroglancer .ViewerState | None , dim_names = ()
494+ def get_fixed_and_moving_dims (
495+ self , s : Union [ neuroglancer .ViewerState , None ] , dim_names : list | tuple = ()
504496 ):
505497 if s is None :
506498 dimensions = dim_names
@@ -522,7 +514,7 @@ def split_points_into_pairs(self, annotations, dim_names):
522514 if len (annotations ) == 0 :
523515 return np .zeros ((0 , 0 )), np .zeros ((0 , 0 ))
524516 first_name = dim_names [0 ]
525- fixed_dims , _ = self .get_moving_and_fixed_dims ( dim_names )
517+ fixed_dims , _ = self .get_fixed_and_moving_dims ( None , dim_names )
526518 real_dims_last = first_name not in fixed_dims
527519 num_points = len (annotations )
528520 num_dims = len (annotations [0 ].point ) // 2
@@ -596,6 +588,18 @@ def update_registered_layers(self, s: neuroglancer.ViewerState):
596588 def estimate_affine (self , s : neuroglancer .ViewerState ):
597589 annotations = s .layers [self .annotations_name ].annotations
598590 if len (annotations ) == 0 :
591+ if len (self .stored_points [0 ]) > 0 :
592+ # Again not sure if need channels
593+ _ , moving_dims = self .get_fixed_and_moving_dims (s )
594+ n_dims = len (moving_dims )
595+ affine = np .zeros (shape = (n_dims , n_dims + 1 ))
596+ for i in range (n_dims ):
597+ affine [i ][i ] = 1
598+ print (affine )
599+ self .affine = affine
600+ self .update_registered_layers (s )
601+ self .stored_points = ([], [])
602+ return True
599603 return False
600604
601605 dim_names = s .dimensions .names
@@ -627,6 +631,7 @@ def get_registration_info(self):
627631 fixed_points , moving_points = self .split_points_into_pairs (
628632 annotations , dim_names
629633 )
634+ info ["annotations" ] = annotations .tolist ()
630635 info ["fixedPoints" ] = fixed_points .tolist ()
631636 info ["movingPoints" ] = moving_points .tolist ()
632637 if self .affine is not None :
@@ -651,18 +656,18 @@ def __str__(self):
651656
652657 def _clear_status_messages (self ):
653658 to_pop = []
654- for k , v in self .status_timers .items ():
659+ for k , v in self ._status_timers .items ():
655660 if time () - v > MESSAGE_DURATION :
656661 to_pop .append (k )
657662 for k in to_pop :
658663 with self .viewer .config_state .txn () as s :
659664 s .status_messages .pop (k , None )
660- self .status_timers .pop (k )
665+ self ._status_timers .pop (k )
661666
662667 def _set_status_message (self , key : str , message : str ):
663668 with self .viewer .config_state .txn () as s :
664669 s .status_messages [key ] = message
665- self .status_timers [key ] = time ()
670+ self ._status_timers [key ] = time ()
666671
667672 def _transform_points_with_affine (self , points : np .ndarray ):
668673 if self .affine is not None :
0 commit comments