Image Assembly#
Converting raw scanimage-tiff files into fused z-planes.
Quickstart#
All you need is a path to your .tiffs and a place to save them.
import mbo_utilities as mbo
scan = mbo.imread(r"path/to/tiffs*") # glob or list of filepaths
# Save options:
scan.roi = 1 # save just mROI 1
scan.roi = [1, 2] # save mROI 1 and 2
scan.roi = 0 # save all mROIs separately
scan.roi = None # stitch/fuse all mROIs
mbo.imwrite(scan, "/path/to/save", planes=[1, 7, 14])
# Creates: plane_01.tiff, plane_07.tiff, plane_14.tiff
from pathlib import Path
import numpy as np
import fastplotlib as fpl
import mbo_utilities as mbo
Input data: Path to your raw .tiff file(s)#
Make sure your data_path contains only .tiff files for this imaging session. If there are other .tiff files, such as from another session or a processed file for this session, those files will be included in the scan and lead to errors.
Initialize a scanreader object#
Pass a list of files, or a wildcard string (e.g. “/path/to/files/*” matches all files in that directory) to mbo.imread().
Tip: mbo.get_files() is useful to easily get all files of the same filetype.
files = mbo.get_files("/path/to/data/raw", 'tif')
len(files)
scan = mbo.imread(files)
print(f'Planes: {scan.num_channels}')
print(f'Frames: {scan.num_frames}')
print(f'ROIs: {scan.num_rois}')
print(f'Shape (T, Z, Y, X): {scan.shape}')
Accessing data in the scan#
Numpy-like indexing:
frame = scan[0, 0, :, :] # first frame, plane 1
zplane7 = scan[:, 6, :, :] # all frames from z-plane 7
# Visualize the data
iw = scan.imshow()
iw.show()
iw.close()
Save assembled files#
The currently supported file extensions are .tiff, .bin, .zarr, and .h5.
Basic Usage: Stitch ROIs and Save All Planes#
save_path = Path("/path/to/save")
save_path.mkdir(exist_ok=True)
# Stitch all ROIs together (default behavior)
scan.roi = None
mbo.imwrite(scan, save_path, ext='.tiff')
# Creates: plane01_stitched.tiff, plane02_stitched.tiff, ..., plane14_stitched.tiff
Save Specific Planes#
# Save only first, middle, and last planes (for 14-plane volume)
mbo.imwrite(
scan,
save_path,
planes=[1, 7, 14],
overwrite=False,
ext='.tiff'
)
# Creates: plane01_stitched.tiff, plane07_stitched.tiff, plane14_stitched.tiff
ROI Handling Options#
# Option 1: Stitch all ROIs together (default)
scan.roi = None
mbo.imwrite(scan, save_path / "stitched")
# Creates: plane01_stitched.tiff, plane02_stitched.tiff, ...
# Option 2: Save all ROIs as separate files
scan.roi = 0
mbo.imwrite(scan, save_path / "split_rois")
# Creates: plane01_roi1.tiff, plane01_roi2.tiff, ..., plane14_roi1.tiff, plane14_roi2.tiff
# Option 3: Save specific ROI only
scan.roi = 1
mbo.imwrite(scan, save_path / "roi1_only")
# Creates: plane01_roi1.tiff, plane02_roi1.tiff, ..., plane14_roi1.tiff
# Option 4: Save multiple specific ROIs
scan.roi = [1, 3]
mbo.imwrite(scan, save_path / "roi1_and_roi3")
# Creates: plane01_roi1.tiff, plane01_roi3.tiff, ..., plane14_roi1.tiff, plane14_roi3.tiff
Enable Phase Correction#
Fix bidirectional scan phase artifacts before saving:
scan.fix_phase = True
scan.phasecorr_method = "mean" # Options: "mean", "median", "max"
scan.use_fft = True # Use FFT-based correction (faster)
mbo.imwrite(scan, save_path / "phase_corrected")
Z-Plane Registration#
Align z-planes spatially using Suite3D registration:
# Automatic registration with Suite3D (requires Suite3D + CuPy installed)
mbo.imwrite(
scan,
save_path / "registered",
register_z=True,
roi=None
)
# Computes rigid shifts and applies them during write
# Validates registration results before proceeding
Export Subset of Frames#
Useful for testing or creating demos:
# Export only first 1000 frames
mbo.imwrite(scan, save_path / "test_data", num_frames=1000, planes=[1, 7, 14])
Convert to Suite2p Binary Format#
# Save as Suite2p-compatible binary files
mbo.imwrite(scan, save_path / "suite2p", ext='.bin', roi=0)
# Creates: plane01_roi1/data_raw.bin + ops.npy, plane01_roi2/data_raw.bin + ops.npy, ...
Save to Zarr Format#
# Save as Zarr v3 stores (efficient for large datasets)
mbo.imwrite(scan, save_path / "zarr_data", ext='.zarr', roi=0)
# Creates: plane01_roi1.zarr, plane01_roi2.zarr, ...
Use Pre-Computed Registration Shifts#
# Load previously computed shifts
import numpy as np
summary = np.load("previous_job/summary/summary.npy", allow_pickle=True).item()
shift_vectors = summary['plane_shifts'] # shape: (n_planes, 2)
# Apply shifts without re-running registration
mbo.imwrite(scan, save_path / "registered", shift_vectors=shift_vectors)
Example Output#
Saving plane01_stitched.tiff: 100%|██████████| 108/108 [02:43<00:00, 1.51s/it]
Saving plane07_stitched.tiff: 100%|██████████| 108/108 [02:42<00:00, 1.51s/it]
Saving plane14_stitched.tiff: 100%|██████████| 108/108 [02:41<00:00, 1.50s/it]
Vizualize data with fastplotlib#
To get a rough idea of the quality of your extracted timeseries, we can create a fastplotlib visualization to preview traces of individual pixels.
Here, we simply click on any pixel in the movie, and we get a 2D trace (or “temporal component” as used in this field) of the pixel through the course of the movie:
More advanced visualizations can be easily created, i.e. adding a baseline subtracted element to the trace, or passing the trace through a frequency filter.
import tifffile
from ipywidgets import VBox
img = mbo.imread("path/to/assembled/plane07_stitched.tiff")
iw_movie = fpl.ImageWidget(img, cmap="viridis")
tfig = fpl.Figure()
raw_trace = tfig[0, 0].add_line(np.zeros(img.shape[0]))
@iw_movie.managed_graphics[0].add_event_handler("click")
def pixel_clicked(ev):
col, row = ev.pick_info["index"]
raw_trace.data[:, 1] = iw_movie.data[0][:, row, col]
tfig[0, 0].auto_scale(maintain_aspect=False)
VBox([iw_movie.show(), tfig.show()])