Note
Go to the end to download the full example code. or to run this example in your browser via Binder
Visualizing derotation line-by-line with custom hooks#
This example demonstrates how to use a custom plotting hook to visualize how each image frame is reconstructed during derotation, line by line.
The derotation process works by rotating each horizontal line of an image by a given angle and placing it into a new derotated frame. Understanding this process is useful both for debugging and for developing intuition about how motion correction works.
In this example, we define a custom hook called after each line is processed during derotation. The hook visualizes the following:
The original image, with the current line highlighted
The partially built derotated image
The currently rotated line, overlaid on the right-hand image (if visible)
The hook is triggered only for frame 135 and every 20 lines, to keep the output readable.
- This can be particularly helpful when:
You want to verify that the rotation mapping is working as expected
You want to debug center of rotation or interpolation artifacts
You are developing your own hooks or modifying the pipeline internals
Imports#
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from derotation.analysis.full_derotation_pipeline import FullPipeline
from derotation.config.load_config import load_config, update_config_paths
from derotation.sample_data import fetch_data
Load and configure paths#
We’ll use a small example dataset and write output to the current folder.
output_folder = Path.cwd()
config = load_config()
config = update_config_paths(
config=config,
tif_path=str(fetch_data("rotation_sample.tif")),
aux_path=str(fetch_data("analog_signals.npy")),
stim_randperm_path=str(fetch_data("stimulus_randperm.csv")),
output_folder=str(output_folder),
)
Define a custom hook function#
This hook is called after every line is derotated and placed into the new
frame. We’ll use it to inspect how frame 135 is constructed over time.
This is a simplified version of the hook
derotation.plotting_hooks.for_derotation.line_addition()
.
def inspect_frame_135_and_line_180(
derotated_filled_image: np.ndarray,
rotated_line: np.ndarray,
image_counter: int,
line_counter: int,
angle: float,
original_image: np.ndarray,
):
if image_counter == 135 and line_counter == 180:
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
# background fig color: black
fig.patch.set_facecolor("black")
ax[0].imshow(original_image, cmap="turbo")
# highlight the line in the original image
ax[0].plot(
[0, original_image.shape[1] - 1],
[line_counter, line_counter],
color="red",
linewidth=2,
)
ax[0].set_title(
f"Take line {line_counter}\nfrom original image,\n then rotate it"
f"of {angle:.2f} degrees"
)
ax[0].title.set_color("white")
ax[0].axis("off")
ax[1].imshow(derotated_filled_image, cmap="turbo")
ax[1].set_title(
f"Place the line in a new image\nto build frame {image_counter}"
)
ax[1].title.set_color("white")
ax[1].axis("off")
# plot on top axis 1 the rotated_line with a red colormap
ax[1].imshow(rotated_line, cmap="hsv", alpha=0.5)
plt.show()
Register the hook#
hooks = {
"plotting_hook_line_addition": inspect_frame_135_and_line_180,
}
Run the derotation pipeline with the custom hook#
Our hook will be called during processing of each line.
pipeline = FullPipeline(config)
pipeline.hooks = hooks
pipeline()

2025-07-24 15:36:18 PM INFO 2025-07-24 15:36:18 PM - INFO - fancylog.py:451
MainProcess fancylog.py:451 -
Starting logging
INFO 2025-07-24 15:36:18 PM - INFO - fancylog.py:452
MainProcess fancylog.py:452 -
Not logging multiple processes
INFO 2025-07-24 full_derotation_pipeline.py:164
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:164
- Loading
data...
INFO 2025-07-24 full_derotation_pipeline.py:165
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:165
- Loading
/home/runner/.de
rotation/data/ro
tation_sample.ti
f
INFO 2025-07-24 full_derotation_pipeline.py:186
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:186
- Number of
rotations: 2
INFO 2025-07-24 full_derotation_pipeline.py:194
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:194
- Rotation
direction:
speed 200
direction
-1 1
1 1
INFO 2025-07-24 full_derotation_pipeline.py:253
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:253
- Deleting
previous debug
plots...
INFO 2025-07-24 full_derotation_pipeline.py:263
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:263
- Dataset
rotation_sample
loaded
INFO 2025-07-24 full_derotation_pipeline.py:264
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:264
- Filename:
derotated
INFO 2025-07-24 full_derotation_pipeline.py:361
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:361
- Finding
rotation ticks
peaks...
INFO 2025-07-24 full_derotation_pipeline.py:436
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:436
- Cleaning start
and end rotation
signal...
INFO 2025-07-24 full_derotation_pipeline.py:461
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:461
- Creating
signed rotation
array...
INFO 2025-07-24 full_derotation_pipeline.py:483
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:483
- Dropping ticks
outside of the
rotation
period...
INFO 2025-07-24 full_derotation_pipeline.py:519
15:36:18 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:519
- Ticks dropped:
1.
Ticks remaining:
3451
2025-07-24 15:36:19 PM INFO 2025-07-24 full_derotation_pipeline.py:550
15:36:19 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:550
- Number of
rotations is as
expected
WARNING 2025-07-24 full_derotation_pipeline.py:597
15:36:19 PM -
WARNING -
MainProcess
full_derotation_
pipeline.py:597
- Number of
ticks is not as
expected: 3451.
Expected ticks:
3600.0
Delta: -149.0
INFO 2025-07-24 full_derotation_pipeline.py:649
15:36:19 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:649
- New increment
example: 0.209
INFO 2025-07-24 full_derotation_pipeline.py:662
15:36:19 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:662
- Interpolating
angles...
INFO 2025-07-24 full_derotation_pipeline.py:702
15:36:19 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:702
- Cleaning
interpolated
angles...
INFO 2025-07-24 full_derotation_pipeline.py:761
15:36:19 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:761
- Calculating
angles by line
and frame...
INFO 2025-07-24 full_derotation_pipeline.py:856
15:36:19 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:856
- Plotting
rotation ticks
and rotation on
signal...
INFO 2025-07-24 full_derotation_pipeline.py:898
15:36:19 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:898
- Plotting
rotation
angles...
INFO 2025-07-24 full_derotation_pipeline.py:901
15:36:19 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:901
- Speeds:
{np.int64(200)}
INFO 2025-07-24 full_derotation_pipeline.py:902
15:36:19 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:902
- len (speeds):
1
2025-07-24 15:36:20 PM INFO 2025-07-24 full_derotation_pipeline.py:348
15:36:20 PM -
INFO -
MainProcess
full_derotation_
pipeline.py:348
- ✨ Analog
signals
processed ✨
INFO 2025-07-24 full_derotation_pipeline.py:1121
15:36:20 PM -
INFO -
MainProcess
full_derotation
_pipeline.py:11
21 - Optimal
center of
rotation read
from file.
INFO 2025-07-24 full_derotation_pipeline.py:1167
15:36:20 PM -
INFO -
MainProcess
full_derotation
_pipeline.py:11
67 - Starting
derotation by
line...
INFO 2025-07-24 full_derotation_pipeline.py:1135
15:36:20 PM -
INFO -
MainProcess
full_derotation
_pipeline.py:11
35 - Plotting
max projection
with center...
0%| | 0/69120 [00:00<?, ?it/s]
48%|████▊ | 33046/69120 [00:00<00:00, 329590.43it/s]
95%|█████████▌| 66006/69120 [00:01<00:00, 55471.39it/s]
100%|██████████| 69120/69120 [00:01<00:00, 65768.32it/s]
2025-07-24 15:36:21 PM INFO 2025-07-24 full_derotation_pipeline.py:1135
15:36:21 PM -
INFO -
MainProcess
full_derotation
_pipeline.py:11
35 - Plotting
max projection
with center...
INFO 2025-07-24 full_derotation_pipeline.py:1198
15:36:21 PM -
INFO -
MainProcess
full_derotation
_pipeline.py:11
98 - ✨ Image
stack derotated
✨
/opt/hostedtoolcache/Python/3.12.11/x64/lib/python3.12/site-packages/numpy/_core/fromnumeric.py:3860: RuntimeWarning: Mean of empty slice.
return _methods._mean(a, axis=axis, dtype=dtype,
/opt/hostedtoolcache/Python/3.12.11/x64/lib/python3.12/site-packages/numpy/_core/_methods.py:136: RuntimeWarning: invalid value encountered in divide
ret = um.true_divide(
2025-07-24 15:36:25 PM INFO 2025-07-24 full_derotation_pipeline.py:1338
15:36:25 PM -
INFO -
MainProcess
full_derotation
_pipeline.py:13
38 - Saving
/home/runner/wo
rk/derotation/d
erotation/examp
les/derotated.t
if
WARNING 2025-07-24 full_derotation_pipeline.py:1357
15:36:25 PM -
WARNING -
MainProcess
full_derotation
_pipeline.py:13
57 - Number of
rotation angles
by frame is
higher than the
number of
frames
Total running time of the script: (0 minutes 6.564 seconds)