diff --git a/examples/register_config_test_elastic.yaml b/examples/register_config_test_elastic.yaml index a212bd1..e98a82a 100644 --- a/examples/register_config_test_elastic.yaml +++ b/examples/register_config_test_elastic.yaml @@ -25,7 +25,7 @@ coherent_point_drift: w: 0.00001 lmd: 0.1 beta: 100 - maxiter: 150 # Low for debugging purpose, should be 100-150 + maxiter: 100 # Low for debugging purpose, should be 100-150 ILP: min_neighbours: 10 diff --git a/examples/register_config_test_rigid.yaml b/examples/register_config_test_rigid.yaml index dbf329a..c5039f3 100644 --- a/examples/register_config_test_rigid.yaml +++ b/examples/register_config_test_rigid.yaml @@ -31,6 +31,6 @@ ILP: min_neighbours: 10 max_dist: 30 -mobie_export: True +mobie_export: False semantic_seg: True mobie_dataset_name: "platy1_muscles_stardist" diff --git a/matchmaker/cpd_nonrigid_registration.py b/matchmaker/cpd_nonrigid_registration.py index ec06506..01fddfa 100644 --- a/matchmaker/cpd_nonrigid_registration.py +++ b/matchmaker/cpd_nonrigid_registration.py @@ -1,3 +1,4 @@ +import os import sys import click import logging @@ -22,17 +23,17 @@ def cpd_from_images(fixed_img, fixed_resolution, moving_img, moving_resolution, fixed_pcd = create_pcd(fixed_center_coords, fixed_labels) moving_pcd = create_pcd(moving_center_coord, moving_labels) - overlay_pcds(fixed_pcd, moving_pcd, projection="xz", save_path = output_dir / "pcds_before_registration_xz.png") - overlay_pcds(fixed_pcd, moving_pcd, projection="yz", save_path = output_dir / "pcds_before_registration_yz.png") + overlay_pcds(fixed_pcd, moving_pcd, projection="xz", save_path = output_dir / "plots/pcds_before_registration_xz.png") + overlay_pcds(fixed_pcd, moving_pcd, projection="yz", save_path = output_dir / "plots/pcds_before_registration_yz.png") logging.info(f"Point cloud registration with parameters w={w}, beta={beta}, lmd={lmd}, maxiter={maxiter}") registered_pcd = run_cpd(fixed_pcd, moving_pcd, w, beta, lmd, maxiter) - overlay_pcds(fixed_pcd, registered_pcd, projection="xz", save_path = output_dir / "pcds_after_registration_xz.png") - overlay_pcds(fixed_pcd, registered_pcd, projection="yz", save_path = output_dir / "pcds_after_registration_yz.png") + overlay_pcds(fixed_pcd, registered_pcd, projection="xz", save_path = output_dir / "plots/pcds_after_registration_xz.png") + overlay_pcds(fixed_pcd, registered_pcd, projection="yz", save_path = output_dir / "plots/pcds_after_registration_yz.png") - visualize_displacement_field(moving_pcd, registered_pcd, projection="xz", save_path = output_dir / "displacement_field.png") + visualize_displacement_field(moving_pcd, registered_pcd, projection="xz", save_path = output_dir / "plots/displacement_field.pdf") o3d.t.io.write_point_cloud(str(output_dir / "fixed_pcd.pcd"), fixed_pcd, write_ascii=True) o3d.t.io.write_point_cloud(str(output_dir / "moving_pcd.pcd"), moving_pcd, write_ascii=True) @@ -56,13 +57,15 @@ def main(fixed_path, fixed_key, moving_path, moving_key, output_dir, w, beta, lm level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", handlers=[ - logging.FileHandler(f"{output_dir}/rigid_alignment.log", mode="w"), + logging.FileHandler(f"{output_dir}/cpd_nonrigid_registration.log", mode="w"), logging.StreamHandler(sys.stdout), ], datefmt="%Y-%m-%d %H:%M:%S", ) output_dir = Path(output_dir) + os.makedirs(output_dir / "plots", exist_ok=True) + logging.info("Reading fixed image") fixed_img = read_volume(fixed_path, fixed_key) fixed_resolution = get_attrs(fixed_path, fixed_key)["resolution"] diff --git a/matchmaker/elastix_deformable_pointset_registration.py b/matchmaker/elastix_deformable_pointset_registration.py index d43ca19..8c98c0e 100644 --- a/matchmaker/elastix_deformable_pointset_registration.py +++ b/matchmaker/elastix_deformable_pointset_registration.py @@ -19,7 +19,6 @@ read_volume, write_volume, get_attrs, - plot_overlay, itk_scalar_img, itk_to_np_order, apply_transform_chanwise, @@ -114,7 +113,7 @@ def elastix_deformable_pointset_alignment( plot_overlay( itk_to_np_order(itk.GetArrayFromImage(fixed_img)), itk_to_np_order(itk.GetArrayFromImage(moving_img)), - output_dir / f"deformable_pointset_alignment_before.png", + f"{output_dir}/plots/deformable_pointset_alignment_before.pdf", ) SCRIPT_DIR = Path(__file__).resolve().parent @@ -140,7 +139,7 @@ def elastix_deformable_pointset_alignment( plot_overlay( itk_to_np_order(itk.GetArrayFromImage(fixed_img)), result_img_np, - output_dir / f"deformable_pointset_alignment_semantic.png", + f"{output_dir}/plots/deformable_pointset_alignment_semantic.pdf", ) logging.info(f"Apply transform to all channels") @@ -151,7 +150,7 @@ def elastix_deformable_pointset_alignment( plot_overlay( fixed_img_np, result_img_np, - output_dir / f"deformable_pointset_alignment_final.png", + f"{output_dir}/plots/deformable_pointset_alignment_final.pdf", ) logging.info(f"Result image shape {result_img_np.shape}") @@ -163,8 +162,8 @@ def elastix_deformable_pointset_alignment( transformed_grid_np = apply_transform_chanwise( result_transform_parameters, grid_img_np.astype(np.float32), moving_resolution ) - plot_overlay(fixed_img_np, grid_img_np, output_dir / f"grid_before.png") - plot_overlay(fixed_img_np, transformed_grid_np, output_dir / f"grid_after.png") + plot_overlay(fixed_img_np, grid_img_np, f"{output_dir}/plots/grid_before.png") + plot_overlay(fixed_img_np, transformed_grid_np, f"{output_dir}/plots/grid_after.png") return result_img_np @@ -236,6 +235,7 @@ def main( ) output_dir = Path(output_dir) + os.makedirs(output_dir / "plots", exist_ok=True) logging.info("Reading fixed image") fixed_img = read_volume(fixed_path, fixed_key) @@ -282,7 +282,7 @@ def main( plot_overlay( prealigned_fixed, prealigned_moving_aligned, - output_dir / f"deformable_pointset_alignment_prealigned.png", + f"{output_dir}/plots/deformable_pointset_alignment_prealigned.pdf", ) diff --git a/matchmaker/match_pointclouds.py b/matchmaker/match_pointclouds.py index f9fcd76..a855672 100644 --- a/matchmaker/match_pointclouds.py +++ b/matchmaker/match_pointclouds.py @@ -1,3 +1,4 @@ +import os import sys import click import logging @@ -43,12 +44,12 @@ def match_points(fixed_pcd, registered_pcd, output_dir): if swap_order: plot_matching_qc( - pos_2, pos_1, output_dir / "point_matching.png", pairs=matched_idx_pairs + pos_2, pos_1, f"{output_dir}/plots/point_matching.pdf", pairs=matched_idx_pairs ) else: plot_matching_qc( - pos_1, pos_2, output_dir / "point_matching.png", pairs=matched_idx_pairs + pos_1, pos_2, f"{output_dir}/plots/point_matching.pdf", pairs=matched_idx_pairs ) return matched_idx_pairs, matched_label_pairs @@ -87,6 +88,8 @@ def main(fixed_pcd, moving_pcd, output_dir, min_neighbours, max_dist): ) output_dir = Path(output_dir) + os.makedirs(output_dir / "plots", exist_ok=True) + logging.info("Reading fixed point cloud") fixed_pcd = o3d.t.io.read_point_cloud(fixed_pcd) diff --git a/matchmaker/prealignment.py b/matchmaker/prealignment.py index 0d5f5c4..265634e 100755 --- a/matchmaker/prealignment.py +++ b/matchmaker/prealignment.py @@ -10,6 +10,8 @@ write_transform_dict, plot_three_slices, plot_overlay, setup_logging, get_axis_orient_matrix, resample_volume, transform_axes_vis) +from matchmaker.utils.vis import CYAN_HEX, PINK, CYAN, PINK_HEX + def get_SVD_transform(img, spacing, save_path=None): """Convert image to point cloud by thresholding, then run SVD on resulting point cloud. @@ -76,24 +78,24 @@ def orient_axis(fixed_prealigned, moving_prealigned, output_dir): int_prof_x_fixed = np.sum(fixed_prealigned > 0, axis=(1, 0)) / np.sum(fixed_prealigned > 0) plt.figure() - plt.plot(int_prof_z, label="moving") - plt.plot(int_prof_z_fixed, label="fixed") + plt.plot(int_prof_z_fixed, label="fixed", color=PINK_HEX) + plt.plot(int_prof_z, label="moving", color=CYAN_HEX) plt.xlabel(f"Axis Z Coordinate") plt.ylabel(f"Sum intensity along axis = Z") plt.legend() plt.savefig(f"{output_dir}/plots/axis_int_profile_Z.png", dpi=300) plt.figure() - plt.plot(int_prof_y, label="moving") - plt.plot(int_prof_y_fixed, label="fixed") + plt.plot(int_prof_y_fixed, label="fixed", color=PINK_HEX) + plt.plot(int_prof_y, label="moving", color=CYAN_HEX) plt.xlabel(f"Axis Y Coordinate") plt.ylabel(f"Sum intensity along axis = Y") plt.legend() plt.savefig(f"{output_dir}/plots/axis_int_profile_Y.png", dpi=300) plt.figure() - plt.plot(int_prof_x, label="moving") - plt.plot(int_prof_x_fixed, label="fixed") + plt.plot(int_prof_x_fixed, label="fixed", color=PINK_HEX) + plt.plot(int_prof_x, label="moving", color=CYAN_HEX) plt.xlabel(f"Axis X Coordinate") plt.ylabel(f"Sum intensity along axis = X") plt.legend() @@ -313,22 +315,42 @@ def run_prealignment( plot_three_slices( fixed_img, - save_path=f"{output_dir}/plots/fixed_input.png", + save_path=f"{output_dir}/plots/fixed_input.pdf", gc=gc_fixed, Vt=Vt_fixed, + cmap="gnuplot2_r" ) plot_three_slices( moving_img, - save_path=f"{output_dir}/plots/moving_input.png", + save_path=f"{output_dir}/plots/moving_input.pdf", gc=gc_moving, Vt=Vt_moving, + cmap="gnuplot2_r" + ) + + plot_three_slices( + fixed_img, + save_path=f"{output_dir}/plots/fixed_input_semantic.pdf", + gc=gc_fixed, + Vt=Vt_fixed, + cmap=PINK, + + ) + + plot_three_slices( + moving_img, + save_path=f"{output_dir}/plots/moving_input_semantic.pdf", + gc=gc_moving, + Vt=Vt_moving, + cmap=CYAN, + ) plot_overlay( fixed_img, moving_img, - save_path=f"{output_dir}/plots/overlay_input.png", + save_path=f"{output_dir}/plots/overlay_input.pdf", gc1=gc_fixed, Vt1=Vt_fixed, gc2=gc_moving, @@ -404,13 +426,17 @@ def run_prealignment( save_path=f"{output_dir}/plots/fixed_prealigned.png", gc = (np.linalg.inv(T_fixed) @ np.append(gc_fixed, 1))[:3], Vt = transform_axes_vis(Vt_fixed, T_fixed), + cmap=PINK, + ) plot_three_slices( moving_prealigned, - save_path=f"{output_dir}/plots/moving_prealigned.png", + save_path=f"{output_dir}/plots/moving_prealigned.pdf", gc = (np.linalg.inv(T_moving) @ np.append(gc_moving, 1))[:3], Vt = transform_axes_vis(Vt_moving, T_moving), + cmap=CYAN, + ) plot_overlay( diff --git a/matchmaker/raw_to_n5.py b/matchmaker/raw_to_n5.py index 4f97261..2e737e7 100755 --- a/matchmaker/raw_to_n5.py +++ b/matchmaker/raw_to_n5.py @@ -5,7 +5,7 @@ import tifffile as tif from pathlib import Path -from matchmaker.utils import (read_volume, write_volume, get_attrs, set_attrs, plot_three_slices, convert_to_int) +from matchmaker.utils import (read_volume, write_volume, plot_three_slices, convert_to_int) def preprocess_tif_input(input_path, output_path, output_key, log_dir, x_res, y_res, z_res): @@ -17,7 +17,7 @@ def preprocess_tif_input(input_path, output_path, output_key, log_dir, x_res, y_ assert (image.ndim == 3) or ( image.ndim == 4 - ), "Currently pipeline only works with ZYX or CZYX images, input has {image.ndim} dimensions" + ), f"Currently pipeline only works with ZYX or CZYX images, input has {image.ndim} dimensions" image = convert_to_int(image) logging.info(f"Writing output image to {output_path}") @@ -28,11 +28,19 @@ def preprocess_tif_input(input_path, output_path, output_key, log_dir, x_res, y_ if image.ndim == 4: chunks = (1, 128, 512, 512) for chan in range(image.shape[0]): - plot_three_slices(image[chan], log_dir / f"input_image_{Path(input_path).stem}_{chan}.png") + plot_three_slices( + image[chan], + log_dir / f"input_image_{Path(input_path).stem}_{chan}.png", + cmap="gnuplot2_r", + ) else: chunks = (128, 512, 512) - plot_three_slices(image, log_dir / f"input_image_{Path(input_path).stem}.png") + plot_three_slices( + image, + log_dir / f"input_image_{Path(input_path).stem}.png", + cmap="gnuplot2_r", + ) write_volume(output_path, image, output_key, chunks=chunks, attrs=attrs) @@ -58,16 +66,23 @@ def preprocess_n5_input(input_path, input_key, output_path, output_key, log_dir, if image.ndim == 4: chunks = (1, 128, 512, 512) for chan in range(image.shape[0]): - plot_three_slices(image[chan], log_dir / f"input_image_{Path(input_path).stem}_{chan}.png") + plot_three_slices( + image[chan], + log_dir / f"input_image_{Path(input_path).stem}_{chan}.png", + cmap="gnuplot2_r", + ) else: chunks = (128, 512, 512) - plot_three_slices(image, log_dir / f"input_image_{Path(input_path).stem}.png") + plot_three_slices( + image, + save_path=log_dir / f"input_image_{Path(input_path).stem}.png", + cmap="gnuplot2_r", + ) write_volume(output_path, image, output_key, chunks=chunks, attrs=attrs) - @click.command() @click.option("-in", "--input_path", required=True, help="Path of the input image in .tif format") @click.option("-ink", "--input_key", required=False, default=None, help="Key of the input image in .n5 format") diff --git a/matchmaker/rigid_alignment_elastix.py b/matchmaker/rigid_alignment_elastix.py index 96805d1..b94a603 100644 --- a/matchmaker/rigid_alignment_elastix.py +++ b/matchmaker/rigid_alignment_elastix.py @@ -52,7 +52,7 @@ def elastix_segm_rigid_alignment( plot_overlay( itk_to_np_order(itk.GetArrayFromImage(fixed_img)), result_img_np, - f"{output_dir}/plots/overlay_after_rigid_alignment.png", + f"{output_dir}/plots/overlay_after_rigid_alignment.pdf", ) logging.info("Apply transform to all channels") diff --git a/matchmaker/utils/vis.py b/matchmaker/utils/vis.py index 68a17b3..b3d8914 100644 --- a/matchmaker/utils/vis.py +++ b/matchmaker/utils/vis.py @@ -2,16 +2,60 @@ import matplotlib.pyplot as plt import open3d as o3d import seaborn as sns -import logging +import matplotlib.colors as mcolors from matchmaker.preprocessing import percentile_norm +PINK_HEX = '#FF3E96' +CYAN_HEX = '#00CED1' + + +def get_pink_cmap(): + pink_colors = [ + '#FFFFFF', # White (for 0) + '#FF9CCA', # Mid Pink + '#FF69B4', # Hot Pink + '#FF3E96', # Strong Pink + '#FF1493', # Deep Pink + ] + + # Create the colormap + custom_pink_cmap = mcolors.LinearSegmentedColormap.from_list( + "custom_pink", pink_colors, N=256 + ) + + return custom_pink_cmap + + +def get_cyan_cmap(): + cyan_colors = [ + '#FFFFFF', # White (for 0) + '#9FFBFF', # Soft Cyan + '#66F2FF', # Mid Cyan + '#33EAF7', # Bright Cyan + '#00CED1', # Dark Turquoise / Cyan + ] + + # Create the colormap + custom_cyan_cmap = mcolors.LinearSegmentedColormap.from_list( + "custom_cyan", cyan_colors, N=256 + ) + return custom_cyan_cmap + + +PINK = get_pink_cmap() +CYAN = get_cyan_cmap() + + def _slice_gc_coords(gc, axis): cz, cy, cx = gc - if axis==0: return cx, cy - if axis==1: return cx, cz - if axis==2: return cy, cz + if axis == 0: + return cx, cy + if axis == 1: + return cx, cz + if axis == 2: + return cy, cz def _draw_axes(px, py, Vt, shape, axis): @@ -37,8 +81,18 @@ def _draw_axes(px, py, Vt, shape, axis): plt.plot([x0, x1], [y0, y1], color=colors[i], linewidth=2) -def plot_three_slices(img, save_path=None, x_pos=None, y_pos=None, z_pos=None, - cmap="Greys_r", max_pos=False, alpha=False, gc=None, Vt=None): +def plot_three_slices( + img, + save_path=None, + x_pos=None, + y_pos=None, + z_pos=None, + cmap="Greys_r", + max_pos=False, + alpha=False, + gc=None, + Vt=None, +): """ Plot slices of a 3D image along each axis. @@ -63,32 +117,34 @@ def plot_three_slices(img, save_path=None, x_pos=None, y_pos=None, z_pos=None, y_pos = int(img.shape[1] // 2) if z_pos is None: z_pos = int(img.shape[0] // 2) - assert img.ndim==3 + assert img.ndim == 3 if max_pos: z_pos, y_pos, x_pos = np.unravel_index(np.argmax(img), img.shape) else: if x_pos is None: - x_pos=img.shape[2]//2 + x_pos = img.shape[2] // 2 if y_pos is None: - y_pos=img.shape[1]//2 + y_pos = img.shape[1] // 2 if z_pos is None: - z_pos=img.shape[0]//2 + z_pos = img.shape[0] // 2 - alpha=(img>0).astype(np.float32) if alpha else np.ones_like(img) + alpha = (img > 0).astype(np.float32) if alpha else np.ones_like(img) - plt.figure(figsize=(15,5)) + plt.figure(figsize=(15, 5)) - slices=[ - (0, z_pos, img[z_pos,:,:], alpha[z_pos,:,:]), - (1, y_pos, img[:,y_pos,:], alpha[:,y_pos,:]), - (2, x_pos, img[:,:,x_pos], alpha[:,:,x_pos]) + slices = [ + (0, z_pos, img[z_pos, :, :], alpha[z_pos, :, :]), + (1, y_pos, img[:, y_pos, :], alpha[:, y_pos, :]), + (2, x_pos, img[:, :, x_pos], alpha[:, :, x_pos]) ] for i, (axis, pos, s, a) in enumerate(slices, 1): - plt.subplot(1,3,i) + plt.subplot(1, 3, i) plt.title(f"{'zyx'[axis]} slice at {pos}") - plt.imshow(s, cmap=cmap, alpha=a) + semantic = cmap is PINK or cmap is CYAN + display = (s > 0).astype(np.float32) if semantic else s + plt.imshow(display, cmap=cmap, alpha=a, vmin=0 if semantic else None, vmax=1 if semantic else None) if gc is not None: px, py = _slice_gc_coords(gc, axis) @@ -108,29 +164,32 @@ def plot_overlay(img1, img2, save_path=None, x_pos=None, y_pos=None, z_pos=None, """ Plot slices of two 3D images along each axis. """ - assert img1.ndim==3 and img2.ndim==3 + assert img1.ndim == 3 and img2.ndim == 3 - if x_pos is None: x_pos=min(img1.shape[2]//2, img2.shape[2]//2) - if y_pos is None: y_pos=min(img1.shape[1]//2, img2.shape[1]//2) - if z_pos is None: z_pos=min(img1.shape[0]//2, img2.shape[0]//2) + if x_pos is None: + x_pos = min(img1.shape[2] // 2, img2.shape[2] // 2) + if y_pos is None: + y_pos = min(img1.shape[1] // 2, img2.shape[1] // 2) + if z_pos is None: + z_pos = min(img1.shape[0] // 2, img2.shape[0] // 2) - plt.figure(figsize=(30,10), dpi=300) + plt.figure(figsize=(30, 10), dpi=300) - img1_alpha = (percentile_norm(img1,0,100) > 0) * 0.5 - img2_alpha = (percentile_norm(img2,0,100) > 0) * 0.5 + img1_alpha = (percentile_norm(img1, 0, 100) > 0) * 0.5 + img2_alpha = (percentile_norm(img2, 0, 100) > 0) * 0.5 slices = [ - (0,z_pos,img1[z_pos,:,:],img2[z_pos,:,:],img1_alpha[z_pos,:,:],img2_alpha[z_pos,:,:]), - (1,y_pos,img1[:,y_pos,:],img2[:,y_pos,:],img1_alpha[:,y_pos,:],img2_alpha[:,y_pos,:]), - (2,x_pos,img1[:,:,x_pos],img2[:,:,x_pos],img1_alpha[:,:,x_pos],img2_alpha[:,:,x_pos]) + (0, z_pos, img1[z_pos, :, :], img2[z_pos, :, :], img1_alpha[z_pos, :, :], img2_alpha[z_pos, :, :]), + (1, y_pos, img1[:, y_pos, :], img2[:, y_pos, :], img1_alpha[:, y_pos, :], img2_alpha[:, y_pos, :]), + (2, x_pos, img1[:, :, x_pos], img2[:, :, x_pos], img1_alpha[:, :, x_pos], img2_alpha[:, :, x_pos]) ] for i, (axis, pos, s1, s2, a1, a2) in enumerate(slices, 1): - plt.subplot(1,3,i) + plt.subplot(1, 3, i) plt.title(f"{'zyx'[axis]} slice at {pos}") - plt.imshow(s1, cmap="Reds", alpha=a1) - plt.imshow(s2, cmap="Blues", alpha=a2) + plt.imshow((s1 > 0).astype(np.float32), cmap=PINK, alpha=a1, vmin=0, vmax=1) + plt.imshow((s2 > 0).astype(np.float32), cmap=CYAN, alpha=a2, vmin=0, vmax=1) if gc1 is not None: px, py = _slice_gc_coords(gc1, axis) @@ -188,8 +247,8 @@ def plot_projection(fixed_np, moving_np, projection, center_slice, max_points): def overlay_pcds( fixed_pcd: o3d.t.geometry.PointCloud, moving_pcd: o3d.t.geometry.PointCloud, - fixed_col="cornflowerblue", - moving_col="orangered", + fixed_col=PINK_HEX, + moving_col=CYAN_HEX, projection="xy", save_path=None, title="", @@ -203,11 +262,12 @@ def overlay_pcds( len(projection) == 2 ), f"Projection should be xy, yz or something like that of length 2, not {projection}" - fixed_np = fixed_pcd.point.positions.numpy() moving_np = moving_pcd.point.positions.numpy() - roi_x, roi_y, fixed_mask, moving_mask, min_range, max_range = plot_projection(fixed_np, moving_np, projection, center_slice, max_points) + roi_x, roi_y, fixed_mask, moving_mask, min_range, max_range = plot_projection( + fixed_np, moving_np, projection, center_slice, max_points + ) plt.figure(figsize=(10, 10)) plt.cla() @@ -217,7 +277,7 @@ def overlay_pcds( plt.scatter( fixed_np[roi_x][fixed_mask], fixed_np[roi_y][fixed_mask], - s=0.6, + s=2, c=fixed_col, alpha=0.5, label="Fixed point cloud", @@ -225,7 +285,7 @@ def overlay_pcds( plt.scatter( moving_np[roi_x][moving_mask], moving_np[roi_y][moving_mask], - s=0.6, + s=2, c=moving_col, alpha=0.5, label="Moving point cloud", @@ -233,6 +293,7 @@ def overlay_pcds( plt.legend() plt.xlabel(projection[0]) plt.ylabel(projection[1]) + plt.gca().invert_yaxis() if save_path is None: plt.show() else: @@ -255,16 +316,19 @@ def visualize_displacement_field( moving_np = moving_pcd.point.positions.numpy() registered_np = registered_pcd.point.positions.numpy() - roi_x, roi_y, moving_mask, registered_mask, min_range, max_range = plot_projection(moving_np, registered_np, projection, center_slice, max_points) + roi_x, roi_y, moving_mask, registered_mask, min_range, max_range = plot_projection( + moving_np, registered_np, projection, center_slice, max_points + ) for idx in np.nonzero(moving_mask): - plt.plot( [moving_np[roi_x][idx], registered_np[roi_x][idx]], [moving_np[roi_y][idx], registered_np[roi_y][idx]], + linewidth=0.5, ) plt.axis("equal") + plt.gca().invert_yaxis() if save_path is None: plt.show() else: @@ -281,21 +345,23 @@ def plot_matching_qc( d2 = axis_order[projection[1]] plt.figure() - roi_x, roi_y, fixed_mask, moving_mask, min_range, max_range = plot_projection(fixed_np, moving_np, projection, center_slice, max_points) + roi_x, roi_y, fixed_mask, moving_mask, min_range, max_range = plot_projection( + fixed_np, moving_np, projection, center_slice, max_points + ) sns.scatterplot( x=fixed_np[fixed_mask, d1], y=fixed_np[fixed_mask, d2], alpha=0.8, label="fixed", - c="mediumpurple", + color=PINK_HEX, ) sns.scatterplot( x=moving_np[moving_mask, d1], y=moving_np[moving_mask, d2], alpha=0.8, label="moving", - c="lightseagreen", + color=CYAN_HEX, ) if "z" not in projection: @@ -315,9 +381,11 @@ def plot_matching_qc( & (p2[orth_axis] > min_range) & (p2[orth_axis] < max_range) ): - plt.plot([p1[d1], p2[d1]], [p1[d2], p2[d2]], c="green") + plt.plot([p1[d1], p2[d1]], [p1[d2], p2[d2]], c="lightseagreen", linewidth=0.5) plt.legend() + plt.axis("equal") + plt.gca().invert_yaxis() plt.savefig(fig_name, dpi=300) plt.close() diff --git a/matchmaker_vis.pdf b/matchmaker_vis.pdf new file mode 100644 index 0000000..2149ecd Binary files /dev/null and b/matchmaker_vis.pdf differ diff --git a/matchmaker_vis.svg b/matchmaker_vis.svg new file mode 100644 index 0000000..9b48a81 --- /dev/null +++ b/matchmaker_vis.svg @@ -0,0 +1,136521 @@ + + + +TransformationID FixedID Moving12345...13475...roughly ovCompute final transformation based on matched instances as landmarksmovingmovingfinal overlayfixedfixedInputRigid alignmentPoint cloud registrationMatchingID FixedID Moving12345...13475...Prealignment + +- put together centers of mass +- rotate samples to have the + same orientationregister as close as possiblewithout deformations- create point clouds from center of instances- deform moving point cloud to match fixed point cloudmatch instances, multiple instances from bigger point cloud can match to one instance in smaller point cloudinstance segmentations diff --git a/visualization.svg b/visualization.svg new file mode 100644 index 0000000..18441d6 --- /dev/null +++ b/visualization.svg @@ -0,0 +1,70955 @@ + + + +movingfixedinputinstance segmentationsrigid alignmentregister as close as possible without deformationPoint Cloud Registration- create point clouds from center of instances- deform moving point cloud to match fixed onematchingmatch instances, multiple instances from bigger point cloudcan match to one instance in the smaller point cloudID FixedID Moving12345...13475...roughly ovTRANSFORMATIONprealignment + +- put together centers of mass +- rotate samples to have the same orientation