MathOnco / valis

Virtual Alignment of pathoLogy Image Series
https://valis.readthedocs.io/en/latest/
MIT License
119 stars 29 forks source link

after registration, parameters for rigid registration #73

Closed WXY-Belief closed 1 year ago

WXY-Belief commented 1 year ago

Hello, I would like to ask about how to obtain parameters for rigid registration, such as rotation angle and translation distance, after registration.

cdgatenbee commented 1 year ago

Hi @WXY-Belief, So sorry it took me this long to get back to you, have just been a bit swamped lately. Anyway, you should be able to use the following function to extract the registration parameters (rigid and non-rigid) for the full resolution WSI :


from skimage import transform
import numpy as np
import pyvips
from valis import registration, warp_tools

def summarize_slide_transforms(slide_obj, crop=True):

    # Transforms found on lower resolution images, so scale to use on slide
    src_slide_shape_rc = slide_obj.slide_dimensions_wh[0][::-1]
    uncropped_reg_slide_shape_rc = slide_obj.val_obj.aligned_slide_shape_rc

    img_corners_xy = warp_tools.get_corners_of_image(src_slide_shape_rc)[:, ::-1]
    warped_corners = warp_tools.warp_xy(img_corners_xy, M=slide_obj.M,
                                transformation_src_shape_rc=slide_obj.processed_img_shape_rc,
                                transformation_dst_shape_rc=slide_obj.reg_img_shape_rc,
                                src_shape_rc=src_slide_shape_rc,
                                dst_shape_rc=uncropped_reg_slide_shape_rc)

    M_tform = transform.ProjectiveTransform()
    M_tform.estimate(warped_corners, img_corners_xy)
    slide_M = M_tform.params

    if crop:
        crop_method = slide_obj.get_crop_method(crop)
        if crop_method is not False:
            if crop_method == registration.CROP_REF:
                ref_slide = slide_obj.val_obj.get_ref_slide()
                slide_aligned_shape_rc = ref_slide.slide_dimensions_wh[0][::-1]
            elif crop_method == registration.CROP_OVERLAP:
                slide_aligned_shape_rc = slide_obj.val_obj.aligned_slide_shape_rc

            slide_bbox_xywh, _ = slide_obj.get_crop_xywh(crop=crop_method, out_shape_rc=slide_aligned_shape_rc)
            crop_T = np.eye(3)
            crop_T[0:2, 2] = slide_bbox_xywh[0:2]
            slide_M = slide_M @ crop_T

    # Extract rigid transforms
    slide_transform_summarizer = transform.SimilarityTransform(slide_M)
    slide_rotation_rad = slide_transform_summarizer.rotation
    slide_rotation_translation_xy = slide_transform_summarizer.translation
    slide_rotation_scale = slide_transform_summarizer.scale

    # Summarize non-rigid displacements inside registration mask
    if slide_obj.bk_dxdy is not None:

        if isinstance(slide_obj.non_rigid_reg_mask, pyvips.Image):
            nr_mask = warp_tools.vips2numpy(slide_obj.non_rigid_reg_mask)
        else:
            nr_mask = slide_obj.non_rigid_reg_mask

        reg_img_shape = np.array(slide_obj.reg_img_shape_rc)
        downscaled_dxdy = warp_tools.scale_dxdy(slide_obj.bk_dxdy, out_shape_rc=reg_img_shape)
        downscaled_dxdy = warp_tools.vips2numpy(downscaled_dxdy)

        displacement_sxy = (uncropped_reg_slide_shape_rc/reg_img_shape)[::-1]
        scaled_dx = downscaled_dxdy[..., 0]*displacement_sxy[0]
        scaled_dy = downscaled_dxdy[..., 1]*displacement_sxy[1]

        scaled_mask = warp_tools.resize_img(nr_mask, reg_img_shape)

        mean_dx = np.mean(scaled_dx[scaled_mask>0])*slide_obj.resolution
        mean_dy = np.mean(scaled_dy[scaled_mask>0])*slide_obj.resolution
        mean_dxdy = (mean_dx, mean_dy, slide_obj.units)

    else:
        mean_dxdy = None

    return slide_rotation_rad, slide_rotation_translation_xy, slide_rotation_scale, mean_dxdy

You can then loop through each slide and extract/summarize the transforms. This example loads a Valis object that was saved the the /data directory (ends in "_registrar.pickle") and then loops through the slides, but you could do this right after the registration, too:


registrar = registration.load_registrar(registrar_file)

for slide_name, slide_obj in registrar.slide_dict.items():
    slide_rotation_rad, slide_rotation_translation_xy, slide_rotation_scale, slide_mean_dxdy = summarize_slide_transforms(slide_obj)
    print(f"{slide_name} transforms:\n"
          f"rotation (radians)={slide_rotation_rad}\n"
          f"translation={slide_rotation_translation_xy}\n"
          f"scaling={slide_rotation_scale}\n"
          f"mean dxdy={slide_mean_dxdy}\n\n"
          )

Hopefully that helps, but if not, please let me know.

Best, -Chandler

WXY-Belief commented 1 year ago

Thank you for your response. It's a great help to me.

cdgatenbee commented 1 year ago

Great! I'll go ahead and close the issue then.

Best, -Chandler