Find center of rotation with simulated data#

This tutorial use the synthetic data class to show how it can be possible to use an incremental rotation movie to find the center of rotation, rotation plane angle and rotation plane orientation and then derotate a movie with any angle pattern.

What happens behind the scenes:
  • Generate a synthetic image with two cells and two angle patterns, one for incremental pipeline (in steps) and one for testing (a sinusoidal pattern).

  • Use the Rotator class to derotate the movie with the incremental angle pattern and the sinusoidal angle pattern.

  • Find the center of rotation by fitting an ellipse to the largest blob for different angle positions.

  • Derotate the movie with the sinusoidal angle pattern using the center of rotation and ellipse parameters found in the previous step.

We are going to inspect the plots generated during the pipeline to see how the algorithm works and how the center of rotation is found.

This serves only as an illustrative example. To understand how to find the center of rotation with real data, please refer to the Key concepts page in the User guide.

Imports#

from pathlib import Path

import matplotlib.pyplot as plt

from derotation.simulate.synthetic_data import SyntheticData

Define parameters for the synthetic simulation#

rotation_plane_angle = 10  # degrees
rotation_plane_orientation = 30  # degrees
center_of_rotation_offset = (7, -7)  # pixels

Run full pipeline#

This will generate simulated rotated data and run an incremental search to find the optimal center of rotation. All intermediate plots are saved in the “debug” folder.

s_data = SyntheticData(
    radius=2,
    center_of_rotation_offset=center_of_rotation_offset,
    rotation_plane_angle=rotation_plane_angle,
    rotation_plane_orientation=rotation_plane_orientation,
    num_frames=50,
    pad=20,
    background_value=80,
    plots=True,
)

s_data.integration_pipeline()
  0%|          | 0/50 [00:00<?, ?it/s]
  4%|▍         | 2/50 [00:00<00:03, 13.45it/s]
  8%|▊         | 4/50 [00:00<00:07,  5.81it/s]
 10%|█         | 5/50 [00:00<00:08,  5.21it/s]
 12%|█▏        | 6/50 [00:01<00:09,  4.86it/s]
 14%|█▍        | 7/50 [00:01<00:09,  4.64it/s]
 16%|█▌        | 8/50 [00:01<00:09,  4.49it/s]
 18%|█▊        | 9/50 [00:01<00:09,  4.40it/s]
 20%|██        | 10/50 [00:02<00:09,  4.33it/s]
 22%|██▏       | 11/50 [00:02<00:09,  4.28it/s]
 24%|██▍       | 12/50 [00:02<00:08,  4.25it/s]
 26%|██▌       | 13/50 [00:02<00:08,  4.22it/s]
 28%|██▊       | 14/50 [00:03<00:08,  4.20it/s]
 30%|███       | 15/50 [00:03<00:08,  4.19it/s]
 32%|███▏      | 16/50 [00:03<00:08,  4.19it/s]
 34%|███▍      | 17/50 [00:03<00:07,  4.19it/s]
 36%|███▌      | 18/50 [00:03<00:07,  4.19it/s]
 38%|███▊      | 19/50 [00:04<00:07,  4.20it/s]
 40%|████      | 20/50 [00:04<00:07,  4.20it/s]
 42%|████▏     | 21/50 [00:04<00:06,  4.20it/s]
 44%|████▍     | 22/50 [00:04<00:06,  4.20it/s]
 46%|████▌     | 23/50 [00:05<00:06,  4.20it/s]
 48%|████▊     | 24/50 [00:05<00:06,  4.20it/s]
 50%|█████     | 25/50 [00:05<00:05,  4.20it/s]
 52%|█████▏    | 26/50 [00:05<00:05,  4.20it/s]
 54%|█████▍    | 27/50 [00:06<00:05,  4.21it/s]
 56%|█████▌    | 28/50 [00:06<00:05,  4.21it/s]
 58%|█████▊    | 29/50 [00:06<00:04,  4.21it/s]
 60%|██████    | 30/50 [00:06<00:04,  4.21it/s]
 62%|██████▏   | 31/50 [00:07<00:04,  4.21it/s]
 64%|██████▍   | 32/50 [00:07<00:04,  4.21it/s]
 66%|██████▌   | 33/50 [00:07<00:04,  4.21it/s]
 68%|██████▊   | 34/50 [00:07<00:03,  4.21it/s]
 70%|███████   | 35/50 [00:08<00:03,  4.21it/s]
 72%|███████▏  | 36/50 [00:08<00:03,  4.21it/s]
 74%|███████▍  | 37/50 [00:08<00:03,  4.20it/s]
 76%|███████▌  | 38/50 [00:08<00:02,  4.19it/s]
 78%|███████▊  | 39/50 [00:08<00:02,  4.19it/s]
 80%|████████  | 40/50 [00:09<00:02,  4.18it/s]
 82%|████████▏ | 41/50 [00:09<00:02,  4.18it/s]
 84%|████████▍ | 42/50 [00:09<00:01,  4.18it/s]
 86%|████████▌ | 43/50 [00:09<00:01,  4.19it/s]
 88%|████████▊ | 44/50 [00:10<00:01,  4.19it/s]
 90%|█████████ | 45/50 [00:10<00:01,  4.19it/s]
 92%|█████████▏| 46/50 [00:10<00:00,  4.19it/s]
 94%|█████████▍| 47/50 [00:10<00:00,  4.19it/s]
 96%|█████████▌| 48/50 [00:11<00:00,  4.19it/s]
 98%|█████████▊| 49/50 [00:11<00:00,  4.18it/s]
100%|██████████| 50/50 [00:11<00:00,  4.17it/s]
100%|██████████| 50/50 [00:11<00:00,  4.31it/s]

  0%|          | 0/50 [00:00<?, ?it/s]
  2%|▏         | 1/50 [00:00<00:11,  4.21it/s]
  4%|▍         | 2/50 [00:00<00:11,  4.19it/s]
  6%|▌         | 3/50 [00:00<00:11,  4.19it/s]
  8%|▊         | 4/50 [00:00<00:10,  4.19it/s]
 10%|█         | 5/50 [00:01<00:10,  4.13it/s]
 12%|█▏        | 6/50 [00:01<00:10,  4.15it/s]
 14%|█▍        | 7/50 [00:01<00:10,  4.16it/s]
 16%|█▌        | 8/50 [00:01<00:10,  4.17it/s]
 18%|█▊        | 9/50 [00:02<00:09,  4.16it/s]
 20%|██        | 10/50 [00:02<00:09,  4.16it/s]
 22%|██▏       | 11/50 [00:02<00:09,  4.16it/s]
 24%|██▍       | 12/50 [00:02<00:09,  4.15it/s]
 26%|██▌       | 13/50 [00:03<00:08,  4.13it/s]
 28%|██▊       | 14/50 [00:03<00:08,  4.13it/s]
 30%|███       | 15/50 [00:03<00:08,  4.14it/s]
 32%|███▏      | 16/50 [00:03<00:08,  4.15it/s]
 34%|███▍      | 17/50 [00:04<00:07,  4.16it/s]
 36%|███▌      | 18/50 [00:04<00:07,  4.17it/s]
 38%|███▊      | 19/50 [00:04<00:07,  4.17it/s]
 40%|████      | 20/50 [00:04<00:07,  4.18it/s]
 42%|████▏     | 21/50 [00:05<00:06,  4.19it/s]
 44%|████▍     | 22/50 [00:05<00:06,  4.19it/s]
 46%|████▌     | 23/50 [00:05<00:06,  4.19it/s]
 48%|████▊     | 24/50 [00:05<00:06,  4.19it/s]
 50%|█████     | 25/50 [00:05<00:05,  4.18it/s]
 52%|█████▏    | 26/50 [00:06<00:05,  4.18it/s]
 54%|█████▍    | 27/50 [00:06<00:05,  4.18it/s]
 56%|█████▌    | 28/50 [00:06<00:05,  4.18it/s]
 58%|█████▊    | 29/50 [00:06<00:05,  4.18it/s]
 60%|██████    | 30/50 [00:07<00:04,  4.18it/s]
 62%|██████▏   | 31/50 [00:07<00:04,  4.19it/s]
 64%|██████▍   | 32/50 [00:07<00:04,  4.18it/s]
 66%|██████▌   | 33/50 [00:07<00:04,  4.18it/s]
 68%|██████▊   | 34/50 [00:08<00:03,  4.18it/s]
 70%|███████   | 35/50 [00:08<00:03,  4.18it/s]
 72%|███████▏  | 36/50 [00:08<00:03,  4.17it/s]
 74%|███████▍  | 37/50 [00:08<00:03,  4.16it/s]
 76%|███████▌  | 38/50 [00:09<00:02,  4.13it/s]
 78%|███████▊  | 39/50 [00:09<00:02,  4.12it/s]
 80%|████████  | 40/50 [00:09<00:02,  4.13it/s]
 82%|████████▏ | 41/50 [00:09<00:02,  4.14it/s]
 84%|████████▍ | 42/50 [00:10<00:01,  4.15it/s]
 86%|████████▌ | 43/50 [00:10<00:01,  4.15it/s]
 88%|████████▊ | 44/50 [00:10<00:01,  4.16it/s]
 90%|█████████ | 45/50 [00:10<00:01,  4.17it/s]
 92%|█████████▏| 46/50 [00:11<00:00,  4.18it/s]
 94%|█████████▍| 47/50 [00:11<00:00,  4.19it/s]
 96%|█████████▌| 48/50 [00:11<00:00,  4.19it/s]
 98%|█████████▊| 49/50 [00:11<00:00,  4.19it/s]
100%|██████████| 50/50 [00:11<00:00,  4.18it/s]
100%|██████████| 50/50 [00:11<00:00,  4.17it/s]

  0%|          | 0/35 [00:00<?, ?it/s]
 11%|█▏        | 4/35 [00:00<00:00, 35.70it/s]
 23%|██▎       | 8/35 [00:00<00:00, 35.83it/s]
 34%|███▍      | 12/35 [00:00<00:00, 35.97it/s]
 46%|████▌     | 16/35 [00:00<00:00, 36.04it/s]
 57%|█████▋    | 20/35 [00:00<00:00, 36.02it/s]
 69%|██████▊   | 24/35 [00:00<00:00, 36.12it/s]
 80%|████████  | 28/35 [00:00<00:00, 36.18it/s]
 91%|█████████▏| 32/35 [00:00<00:00, 36.16it/s]
100%|██████████| 35/35 [00:00<00:00, 36.10it/s]

0it [00:00, ?it/s]
12it [00:00, 181.31it/s]

  0%|          | 0/7000 [00:00<?, ?it/s]
 42%|████▏     | 2940/7000 [00:00<00:00, 29386.44it/s]
 84%|████████▍ | 5880/7000 [00:00<00:00, 29391.71it/s]
100%|██████████| 7000/7000 [00:00<00:00, 29406.50it/s]

Display relevant plots#

Let’s display some of the plots generated during the pipeline.

debug_folder = Path("debug")
debug_images = sorted(debug_folder.glob("*.png"))


def get_image_path(name):
    return [img_path for img_path in debug_images if name in img_path.name][0]


def show_image(path):
    img = plt.imread(path)
    plt.imshow(img)
    plt.axis("off")
    plt.show()

The synthetic image with two cells that was generated

show_image(get_image_path("image_"))
find center of rotation

Rotation angles for incremental pipeline and sinusoidal pattern

show_image(get_image_path("rotation_angles"))
find center of rotation

Fit an ellipse to the largest blob for different angle positions

show_image(get_image_path("ellipse_fit"))

print(f"Estimated center of rotation: {s_data.fitted_center}")
print(f"Estimated rotation plane angle: {s_data.rotation_plane_angle}")
print(
    "Estimated rotation plane orientation: "
    f"{s_data.rotation_plane_orientation}"
)
find center of rotation
Estimated center of rotation: (63, 77)
Estimated rotation plane angle: 12.2
Estimated rotation plane orientation: -33.3

Derotated movie with the sinusoidal angle pattern using the information found in the previous step

show_image(get_image_path("derotated_sinusoidal"))
find center of rotation

Plot mean projection of the derotated movie as a check of the derotation quality

show_image(get_image_path("mean_projection"))
find center of rotation

Total running time of the script: (0 minutes 35.611 seconds)

Gallery generated by Sphinx-Gallery