Skip to content

Commit db7f234

Browse files
committed
feat: apply affine to annotations
1 parent bcafa3c commit db7f234

File tree

1 file changed

+83
-27
lines changed

1 file changed

+83
-27
lines changed

python/examples/example_linear_registration.py

Lines changed: 83 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
NUM_DEMO_DIMS = 2 # Currently can be 2D or 3D
1414
AFFINE_NUM_DECIMALS = 4
1515

16-
# TODO may not be needed, depends on how we handle the two coord spaces
1716
MARKERS_SHADER = """
1817
#uicontrol vec3 fixedPointColor color(default="#00FF00")
1918
#uicontrol vec3 movingPointColor color(default="#0000FF")
@@ -102,7 +101,7 @@ def rigid_or_similarity_fit(
102101
# Homogeneous (D+1)x(D+1)
103102
T = np.zeros((D, D + 1))
104103
T[:D, :D] = s * R
105-
T[:, -1] = np.diagonal(t)
104+
T[:, -1] = -1 * np.diagonal(t)
106105

107106
affine = np.round(T, decimals=AFFINE_NUM_DECIMALS)
108107
return affine
@@ -246,6 +245,8 @@ def __init__(self, args):
246245
self.stored_points = [[], []]
247246
self.stored_moving_dims = {}
248247
self.moving_layer_names = []
248+
self.input_dim_names = []
249+
self.output_dim_names = []
249250
self.stored_group_number = -1
250251
self.affine = None
251252
self.co_ords_ready = False
@@ -314,9 +315,9 @@ def setup_two_panel_layout(self, s: neuroglancer.ViewerState):
314315
# In theory we could make keep unlinked and then on state change check
315316
# but that could be not worth compared to trying to improve rendering
316317
s.layout.children[1].crossSectionOrientation.link = "unlinked"
317-
s.layout.children[1].crossSectionScale.link = "unlinked"
318+
# s.layout.children[1].crossSectionScale.link = "unlinked"
318319
s.layout.children[1].projectionOrientation.link = "unlinked"
319-
s.layout.children[1].projectionScale.link = "unlinked"
320+
# s.layout.children[1].projectionScale.link = "unlinked"
320321

321322
def setup_second_coord_space(self):
322323
if not self.moving_layer_names:
@@ -327,33 +328,69 @@ def setup_second_coord_space(self):
327328
info_future = self.viewer.volume_info(layer_name)
328329
info_future.add_done_callback(lambda f: self.save_coord_space_info(f))
329330

331+
def combine_affine_across_dims(self, s: neuroglancer.ViewerState, affine):
332+
all_dims = s.dimensions.names
333+
moving_dims = self.output_dim_names
334+
# The affine matrix only applies to the moving dims
335+
# so we need to create a larger matrix that applies to all dims
336+
# by adding identity transforms for the real dims
337+
full_matrix = np.zeros((len(all_dims), len(all_dims) + 1))
338+
for i, dim in enumerate(all_dims):
339+
for j, dim2 in enumerate(all_dims):
340+
if dim in moving_dims and dim2 in moving_dims:
341+
moving_i = moving_dims.index(dim)
342+
moving_j = moving_dims.index(dim2)
343+
full_matrix[i, j] = affine[moving_i, moving_j]
344+
elif dim == dim2:
345+
full_matrix[i, j] = 1
346+
if dim in moving_dims:
347+
moving_i = moving_dims.index(dim)
348+
full_matrix[i, -1] = affine[moving_i, -1]
349+
return full_matrix
350+
330351
def setup_registration_layers(self, s: neuroglancer.ViewerState):
331352
dimensions = s.dimensions
332353
# It is possible that the dimensions are not ready yet, return if so
333354
if len(dimensions.names) != self.num_dims:
334355
return
356+
335357
# Make the annotation layer if needed
336-
# TODO probably don't need the properties, to be confirmed if
337-
# one co-ord space is fine or need two
338358
if s.layers.index(self.annotations_name) == -1:
339-
s.layers[self.annotations_name] = neuroglancer.LocalAnnotationLayer(
340-
dimensions=create_dimensions(s.dimensions),
341-
# annotation_properties=[
342-
# neuroglancer.AnnotationPropertySpec(
343-
# id="label",
344-
# type="uint32",
345-
# default=0,
346-
# ),
347-
# neuroglancer.AnnotationPropertySpec(
348-
# id="group",
349-
# type="uint8",
350-
# default=0,
351-
# enum_labels=["fixed", "moving"],
352-
# enum_values=[0, 1],
353-
# ),
354-
# ],
355-
# shader=MARKERS_SHADER,
356-
)
359+
if self.two_coord_spaces:
360+
s.layers[self.annotations_name] = neuroglancer.LocalAnnotationLayer(
361+
dimensions=create_dimensions(s.dimensions)
362+
)
363+
else:
364+
s.layers[self.annotations_name] = neuroglancer.LocalAnnotationLayer(
365+
dimensions=create_dimensions(s.dimensions),
366+
annotation_properties=[
367+
neuroglancer.AnnotationPropertySpec(
368+
id="label",
369+
type="uint32",
370+
default=0,
371+
),
372+
neuroglancer.AnnotationPropertySpec(
373+
id="group",
374+
type="uint8",
375+
default=0,
376+
enum_labels=["fixed", "moving"],
377+
enum_values=[0, 1],
378+
),
379+
],
380+
shader=MARKERS_SHADER,
381+
)
382+
383+
# Make a copy of all the moving layers but in original coord space
384+
# and as part of the left hand panel
385+
for layer_name in self.moving_layer_names:
386+
copy = deepcopy(s.layers[layer_name])
387+
copy.name = layer_name + "_registered"
388+
copy.visible = False
389+
for source in copy.source:
390+
# TODO might need mapping
391+
source.transform = None
392+
s.layers[copy.name] = copy
393+
s.layout.children[0].layers.append(copy.name)
357394
s.layers[self.annotations_name].tool = "annotatePoint"
358395
s.selected_layer.layer = self.annotations_name
359396
s.selected_layer.visible = True
@@ -394,9 +431,14 @@ def save_coord_space_info(self, info_future):
394431
source.transform = new_coord_space
395432

396433
def toggle_registered_visibility(self, _):
397-
self.setup_viewer()
398-
# is_registered_visible = s.layers["registered"].visible
399-
# s.layers["registered"].visible = not is_registered_visible
434+
if not self.ready:
435+
self.setup_viewer()
436+
return
437+
with self.viewer.txn() as s:
438+
for layer_name in self.moving_layer_names:
439+
registered_name = layer_name + "_registered"
440+
is_registered_visible = s.layers[registered_name].visible
441+
s.layers[registered_name].visible = not is_registered_visible
400442

401443
def setup_viewer_actions(self):
402444
viewer = self.viewer
@@ -489,6 +531,20 @@ def update_registered_layers(self, s: neuroglancer.ViewerState):
489531
output_dimensions=change_coord_names(v, "2"),
490532
matrix=transform,
491533
)
534+
for source in s.layers[k + "_registered"].source:
535+
source.transform = neuroglancer.CoordinateSpaceTransform(
536+
input_dimensions=v,
537+
output_dimensions=v,
538+
matrix=transform,
539+
)
540+
print(self.combine_affine_across_dims(s, self.affine).tolist())
541+
s.layers[self.annotations_name].source[
542+
0
543+
].transform = neuroglancer.CoordinateSpaceTransform(
544+
input_dimensions=create_dimensions(s.dimensions),
545+
output_dimensions=create_dimensions(s.dimensions),
546+
matrix=self.combine_affine_across_dims(s, self.affine).tolist(),
547+
)
492548

493549
# print(s.layers["registered"].source[0].transform.matrix)
494550
# TODO this is where that mapping needs to happen of affine dims

0 commit comments

Comments
 (0)