@@ -296,13 +296,35 @@ def plot_manual_landmark_transforms(landmarks_before,
296296 Display inline. Set ``False`` when called from a worker thread.
297297 """
298298 # ── Load landmarked TIFF ──────────────────────────────────────────────
299+ # Handle both channel-first (C, H, W) and channel-last (H, W, C) TIFFs.
300+ # A small first dimension (≤8) reliably indicates (C, H, W); a large one
301+ # means the first axis is height, i.e. (H, W) or (H, W, C).
299302 with tifffile .TiffFile (landmarked_image_path ) as tif :
300303 lm_stack = tif .asarray ()
301- lm_img = lm_stack if lm_stack .ndim == 2 else lm_stack [1 ]
302-
303- # ── Load sdata morphology at full resolution ──────────────────────────
304- morph = sdata ['morphology_focus' ]
305- sdata_img = np .asarray (sd .get_pyramid_levels (morph , n = 0 )[1 ])
304+ if lm_stack .ndim == 2 :
305+ lm_img = lm_stack
306+ elif lm_stack .ndim == 3 and lm_stack .shape [0 ] <= 8 : # (C, H, W)
307+ lm_img = lm_stack [min (1 , lm_stack .shape [0 ] - 1 )]
308+ elif lm_stack .ndim == 3 : # (H, W, C)
309+ lm_img = lm_stack [:, :, min (1 , lm_stack .shape [2 ] - 1 )]
310+ else : # unexpected shape — take first plane
311+ lm_img = lm_stack .reshape (- 1 , lm_stack .shape [- 2 ], lm_stack .shape [- 1 ])[0 ]
312+
313+ # ── Load sdata morphology at a downsampled level for speed ────────────
314+ # Full-res (n=0) can be 4k–8k px and is slow to materialise; a lower
315+ # level is sufficient for a diagnostic overlay. Landmarks are in full-res
316+ # pixel space, so we compute scale factors to map them to display space.
317+ morph = sdata ['morphology_focus' ]
318+ n_scales = len (list (morph .keys ()))
319+ disp_lvl = min (2 , n_scales - 1 )
320+ disp_da = sd .get_pyramid_levels (morph , n = disp_lvl )
321+ n_ch = disp_da .shape [0 ]
322+ sdata_img = np .asarray (disp_da [min (1 , n_ch - 1 )])
323+
324+ # Scale factors: full-res → display level (dask shape is cheap to read)
325+ full_res_shape = sd .get_pyramid_levels (morph , n = 0 ).shape [- 2 :] # (H, W), lazy
326+ scale_y = sdata_img .shape [0 ] / full_res_shape [0 ]
327+ scale_x = sdata_img .shape [1 ] / full_res_shape [1 ]
306328
307329 fig , axes = plt .subplots (1 , 2 , figsize = (14 , 6 ))
308330
@@ -314,8 +336,10 @@ def plot_manual_landmark_transforms(landmarks_before,
314336 axes [0 ].set_title ('Landmarked image + original landmarks (image pixel space)' )
315337
316338 # ── Right: sdata image + transformed landmarks ────────────────────────
339+ # Scale landmark coordinates from full-res pixel space to display level.
317340 axes [1 ].imshow (sdata_img , cmap = 'gray' )
318- axes [1 ].scatter (landmarks_after ['xenium_x' ], landmarks_after ['xenium_y' ],
341+ axes [1 ].scatter (landmarks_after ['xenium_x' ] * scale_x ,
342+ landmarks_after ['xenium_y' ] * scale_y ,
319343 c = 'red' , s = 15 , zorder = 5 )
320344 subtitle = 'sdata morphology_focus (scale0) + transformed landmarks'
321345 if landmarks_tf_info is not None :
@@ -338,7 +362,6 @@ def plot_manual_landmark_transforms(landmarks_before,
338362 else :
339363 plt .close (fig )
340364
341-
342365def find_landmarked_img_transforms (landmarked_image_path ,
343366 sdata_path ,
344367 landmarks ,
0 commit comments