Skip to content

Commit d9c66bb

Browse files
authored
Improve efficiency of plane position computation (#191)
1 parent 8fcc157 commit d9c66bb

File tree

1 file changed

+85
-18
lines changed

1 file changed

+85
-18
lines changed

src/highdicom/utils.py

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66

77
from highdicom.content import PlanePositionSequence
88
from highdicom.enum import CoordinateSystemNames
9-
from highdicom.spatial import map_pixel_into_coordinate_system
9+
from highdicom.spatial import (
10+
map_pixel_into_coordinate_system,
11+
PixelToReferenceTransformer,
12+
)
1013

1114

1215
def tile_pixel_matrix(
@@ -203,30 +206,94 @@ def compute_plane_position_slide_per_frame(
203206
float(pixel_measures.PixelSpacing[0]),
204207
float(pixel_measures.PixelSpacing[1]),
205208
)
206-
slice_thickness = getattr(
207-
pixel_measures,
208-
'SliceThickness',
209-
1.0
210-
)
211-
spacing_between_slices = getattr(
212-
pixel_measures,
213-
'SpacingBetweenSlices',
214-
1.0
209+
spacing_between_slices = float(
210+
getattr(
211+
pixel_measures,
212+
'SpacingBetweenSlices',
213+
1.0
214+
)
215215
)
216+
x_offset = image_origin.XOffsetInSlideCoordinateSystem
217+
y_offset = image_origin.YOffsetInSlideCoordinateSystem
218+
219+
transformer_lut = {}
220+
for slice_index in range(1, num_focal_planes + 1):
221+
# These checks are needed for mypy to determine the correct type
222+
z_offset = float(slice_index - 1) * spacing_between_slices
223+
transformer_lut[slice_index] = PixelToReferenceTransformer(
224+
image_position=(x_offset, y_offset, z_offset),
225+
image_orientation=image_orientation,
226+
pixel_spacing=pixel_spacing
227+
)
228+
229+
def _compute_plane_position_tiled_full_efficiently(
230+
row_index: int,
231+
column_index: int,
232+
rows: int,
233+
columns: int,
234+
transformer: PixelToReferenceTransformer
235+
) -> PlanePositionSequence:
236+
"""More efficient implementation of `compute_plane_position_tiled_full`.
237+
238+
Function re-uses an existing `transformer` instance instead of creating
239+
one for every function call. This can hurt performance if the number
240+
of frames in an image is large.
241+
242+
Parameters
243+
----------
244+
row_index: int
245+
One-based Row index value for a given frame (tile) along the column
246+
direction of the tiled Total Pixel Matrix, which is defined by
247+
the second triplet in `image_orientation` (values should be in the
248+
range [1, *n*], where *n* is the number of tiles per column)
249+
column_index: int
250+
One-based Column index value for a given frame (tile) along the row
251+
direction of the tiled Total Pixel Matrix, which is defined by
252+
the first triplet in `image_orientation` (values should be in the
253+
range [1, *n*], where *n* is the number of tiles per row)
254+
rows: int
255+
Number of rows per Frame (tile)
256+
columns: int
257+
Number of columns per Frame (tile)
258+
transformer: highdicom.spatial.PixelToReferenceTransformer
259+
Callable transformer instance to map pixel indices into reference
260+
slide coordinates
261+
262+
Returns
263+
-------
264+
highdicom.PlanePositionSequence
265+
Positon of the plane in the slide coordinate system
266+
267+
"""
268+
row_offset_frame = ((row_index - 1) * rows)
269+
column_offset_frame = ((column_index - 1) * columns)
270+
271+
# We should only be dealing with planar rotations.
272+
transformed_coordinates = transformer(
273+
np.array([(column_offset_frame, row_offset_frame)], dtype=int)
274+
)
275+
x = transformed_coordinates[0, 0]
276+
y = transformed_coordinates[0, 1]
277+
z = transformed_coordinates[0, 2]
278+
279+
return PlanePositionSequence(
280+
coordinate_system=CoordinateSystemNames.SLIDE,
281+
image_position=(x, y, z),
282+
# Position of plane (tile) in Total Pixel Matrix:
283+
# First tile has position (1, 1)
284+
pixel_matrix_position=(
285+
column_offset_frame + 1,
286+
row_offset_frame + 1,
287+
)
288+
)
216289

217290
return [
218-
compute_plane_position_tiled_full(
291+
_compute_plane_position_tiled_full_efficiently(
219292
row_index=r,
220293
column_index=c,
221-
x_offset=image_origin.XOffsetInSlideCoordinateSystem,
222-
y_offset=image_origin.YOffsetInSlideCoordinateSystem,
223294
rows=dataset.Rows,
224295
columns=dataset.Columns,
225-
image_orientation=image_orientation,
226-
pixel_spacing=pixel_spacing,
227-
slice_thickness=slice_thickness,
228-
spacing_between_slices=spacing_between_slices,
229-
slice_index=s,
296+
transformer=transformer_lut[s],
230297
)
231298
for _, s, r, c in itertools.product(
232299
range(num_optical_paths),

0 commit comments

Comments
 (0)