Skip to content

Commit 3588ea0

Browse files
Merge pull request #1136 from TheDeanLab/update-shear-parameters
Update shear parameters
2 parents 7590ae5 + 196b258 commit 3588ea0

File tree

5 files changed

+73
-25
lines changed

5 files changed

+73
-25
lines changed

src/navigate/controller/sub_controllers/acquire_bar.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,9 @@ def progress_bar(
161161
number_of_positions = 1
162162
else:
163163
# The first row is a header that describes stage axis designation.
164-
number_of_positions = len(
165-
self.parent_controller.configuration["multi_positions"]
166-
) - 1
164+
number_of_positions = (
165+
len(self.parent_controller.configuration["multi_positions"]) - 1
166+
)
167167

168168
if mode == "single":
169169
number_of_slices = 1
@@ -540,9 +540,14 @@ def toggle_bdv_widgets(self, main_widget: str, dependent_widgets: list) -> None:
540540
"""
541541
state = self.acquire_pop.tab_frame.inputs[main_widget].get_variable().get()
542542
for widget in dependent_widgets:
543-
self.acquire_pop.tab_frame.inputs[widget].widget.config(
544-
state="readonly" if state else "disabled"
545-
)
543+
if "dimension" in widget:
544+
self.acquire_pop.tab_frame.inputs[widget].widget.config(
545+
state="readonly" if state else "disabled"
546+
)
547+
else:
548+
self.acquire_pop.tab_frame.inputs[widget].widget.config(
549+
state="normal" if state else "disabled"
550+
)
546551

547552
def update_microscope_mode(self, *args: Iterable) -> None:
548553
"""Gets the state of the pull-down menu and tells the central controller

src/navigate/model/metadata_sources/bdv_metadata.py

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,9 @@ class BigDataViewerMetadata(XMLMetadata):
5858
"""
5959

6060
def __init__(self) -> None:
61-
"""Initialize the BigDataViewer metadata object.
62-
63-
Parameters
64-
----------
65-
configuration : Optional[Dict[str, Any]]
66-
Configuration dictionary.
67-
"""
61+
"""Initialize the BigDataViewer metadata object."""
6862
super().__init__()
6963

70-
# Affine Transform Parameters
7164
#: bool: Shear the data.
7265
self.shear_data = False
7366

@@ -80,7 +73,6 @@ def __init__(self) -> None:
8073
#: npt.NDArray: Shear transform matrix.
8174
self.shear_transform = np.eye(3, 4)
8275

83-
# Rotation Transform Parameters
8476
#: bool: Rotate the data.
8577
self.rotate_data = False
8678

@@ -194,9 +186,23 @@ def bdv_xml_dict(
194186
}
195187

196188
# Calculate shear and rotation transforms
189+
pixel_size = [self.dx, self.dy, self.dz]
197190
self.bdv_shear_transform()
198191
self.bdv_rotate_transform()
199192

193+
if self.shear_data:
194+
shear_angle = np.deg2rad(self.shear_angle)
195+
# Must scale the voxel size to account for shear
196+
if self.shear_dimension == "YZ":
197+
scaled_z = abs(self.dz * np.sin(shear_angle))
198+
pixel_size = [self.dx, self.dy, scaled_z]
199+
elif self.shear_dimension == "XZ":
200+
# TODO: Implement
201+
pass
202+
else:
203+
# TODO: Implement
204+
pass
205+
200206
# Populate ViewSetups
201207
bdv_dict["SequenceDescription"]["ViewSetups"] = {}
202208
bdv_dict["SequenceDescription"]["ViewSetups"]["ViewSetup"] = []
@@ -210,6 +216,7 @@ def bdv_xml_dict(
210216
{"name": "tile", "Tile": []},
211217
{"name": "angle", "Angle": {"id": {"text": 0}, "name": {"text": 0}}},
212218
]
219+
213220
# The actual loop that populates ViewSetup
214221
view_id = 0
215222
for c in range(self.shape_c):
@@ -226,7 +233,9 @@ def bdv_xml_dict(
226233
"size": {"text": f"{self.shape_x} {self.shape_y} {self.shape_z}"},
227234
"voxelSize": {
228235
"unit": {"text": "um"},
229-
"size": {"text": f"{self.dx} {self.dy} {self.dz}"},
236+
"size": {
237+
"text": f"{pixel_size[0]} {pixel_size[1]} {pixel_size[2]}"
238+
},
230239
},
231240
"attributes": {
232241
"illumination": {"text": "0"},
@@ -238,7 +247,8 @@ def bdv_xml_dict(
238247

239248
bdv_dict["SequenceDescription"]["ViewSetups"]["ViewSetup"].append(d)
240249
view_id += 1
241-
# Finish up the Tile Attributes outside of the channels loop so we have
250+
251+
# Finish up the Tile Attributes outside the channels loop so we have
242252
# one per tile
243253
for pos in range(self.positions):
244254
tile = {"id": {"text": str(pos)}, "name": {"text": str(pos)}}
@@ -254,6 +264,7 @@ def bdv_xml_dict(
254264
}
255265

256266
# View registrations
267+
reference_position = np.eye(3, 4, dtype=float)
257268
bdv_dict["ViewRegistrations"] = {"ViewRegistration": []}
258269
for t in range(self.shape_t):
259270
for p in range(self.positions):
@@ -282,6 +293,13 @@ def bdv_xml_dict(
282293
# an acquisition.
283294
pass
284295

296+
if t == 0 and p == 0 and c == 0:
297+
# Save reference position to allow translation offsets for
298+
# shear to be applied relative to this position.
299+
reference_position = mat.ravel()
300+
301+
current_position = mat.ravel()
302+
285303
view_transforms = [
286304
{
287305
"type": "affine",
@@ -308,6 +326,33 @@ def bdv_xml_dict(
308326
}
309327
)
310328

329+
delta_z_position = current_position[-1] - reference_position[-1]
330+
shear_offset = np.eye(3, 4, dtype=float).ravel()
331+
if self.shear_dimension == "YZ":
332+
shear_offset[7] = (
333+
delta_z_position
334+
* self.dz
335+
* np.tan(np.deg2rad(self.shear_angle))
336+
/ self.dy
337+
)
338+
339+
elif self.shear_dimension == "XZ":
340+
# TODO: Implement
341+
pass
342+
else:
343+
# TODO: Implement
344+
pass
345+
346+
view_transforms.append(
347+
{
348+
"type": "affine",
349+
"Name": "Shear Offset Transform",
350+
"affine": {
351+
"text": " ".join([f"{x:.6f}" for x in shear_offset])
352+
},
353+
}
354+
)
355+
311356
if self.rotate_data:
312357
view_transforms.append(
313358
{
@@ -328,9 +373,7 @@ def bdv_xml_dict(
328373

329374
bdv_dict["ViewRegistrations"]["ViewRegistration"].append(d)
330375

331-
bdv_dict["Misc"] = {
332-
"Entry": {"Key": "Note", "text": self.misc}
333-
}
376+
bdv_dict["Misc"] = {"Entry": {"Key": "Note", "text": self.misc}}
334377

335378
return bdv_dict
336379

src/navigate/tools/linear_algebra.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def affine_shear(dz, dy, dx, dimension="YZ", angle=0):
157157
return shear_transform
158158

159159
scaled_angle = np.multiply(
160-
np.cos(np.deg2rad(angle)),
160+
np.tan(np.deg2rad(angle)),
161161
[dy / dx, dz / dx, dz / dy],
162162
)
163163

test/model/metadata_sources/test_bdv_metadata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def test_bdv_metadata(ext):
5555
md.shear_angle = 15
5656
md.dx, md.dy, md.dz = 1, 1, 1
5757
md.bdv_shear_transform()
58-
assert md.shear_transform[0, 2] == np.cos(np.deg2rad(15))
58+
assert md.shear_transform[0, 2] == np.tan(np.deg2rad(15))
5959

6060
md.rotate_data = True
6161
md.rotate_angle_x = 15

test/tools/test_linear_algebra.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,18 @@ def test_no_shear(self):
8282
def test_shear_xy(self):
8383
result = affine_shear(1, 1, 1, dimension="XY", angle=45)
8484
expected = np.eye(4, 4)
85-
expected[0, 1] = 0.70710678
85+
expected[0, 1] = 1.0
8686
np.testing.assert_array_almost_equal(result, expected)
8787

8888
def test_different_pixe_sizes(self):
8989
result = affine_shear(167, 167, 333, dimension="XY", angle=45)
9090
expected = np.eye(4, 4)
91-
expected[0, 1] = 0.35461511
91+
expected[0, 1] = 0.501502
9292
np.testing.assert_array_almost_equal(result, expected)
9393

9494
def test_shear_xz(self):
9595
# Test shear in XZ dimension
9696
result = affine_shear(167, 167, 333, dimension="XZ", angle=45)
9797
expected = np.eye(4, 4)
98-
expected[0, 2] = 0.35461511
98+
expected[0, 2] = 0.501502
9999
np.testing.assert_array_almost_equal(result, expected)

0 commit comments

Comments
 (0)