Skip to content

Commit 363509b

Browse files
committed
fix: address various assumptions to make pipeline more stable
1 parent b105a58 commit 363509b

File tree

1 file changed

+59
-54
lines changed

1 file changed

+59
-54
lines changed

python/examples/example_linear_registration.py

Lines changed: 59 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from copy import deepcopy
55
from enum import Enum
66
from time import ctime, time
7+
from typing import Union
78

89
import neuroglancer
910
import 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
5052
def 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

Comments
 (0)