fullfield stitching - #52
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR adds utilities for reading ScanImage full-field TIFF metadata and stitching full-field images or z-stacks.
- Implements
read_si_fullfield_metadatato parse slice, volume, and column counts. - Adds
stitch_fullfield(and helper_stitch) for assembling full-field images, supporting multi-channel inputs. - Introduces
fullfield_zstackfor creating volume-averaged z-stacks.
| num_slices, num_volumes, num_columns = read_si_fullfield_metadata(fullfield_fn) | ||
| fullfield_all = imread(fullfield_fn) | ||
| assert fullfield_all.shape[0] == num_slices * num_volumes | ||
| num_rows = int((fullfield_all.shape[1]+1) / num_columns) |
There was a problem hiding this comment.
When handling multi-channel TIFFs, fullfield_all.shape[1] refers to the channel count rather than the image height. Compute num_rows using the image height axis (e.g. fullfield_all.shape[-2] or derive it from fullfield after channel selection).
| num_rows = int((fullfield_all.shape[1]+1) / num_columns) | |
| num_rows = int((fullfield_all.shape[-2]+1) / num_columns) |
| return num_slices, num_volumes, num_columns | ||
|
|
||
|
|
||
| def stitch_fullfield(fullfield_fn, channels=[0]): |
There was a problem hiding this comment.
Using a mutable default argument (channels=[0]) can lead to unexpected behavior if the list is modified. Consider using channels=None and initializing inside the function (e.g. if channels is None: channels = [0]).
| def stitch_fullfield(fullfield_fn, channels=[0]): | |
| def stitch_fullfield(fullfield_fn, channels=None): |
|
|
||
| num_rows = int((fullfield_all.shape[1]+1) / num_columns) | ||
| ind = np.hstack([np.arange(i, fullfield_all.shape[0], num_slices) for i in range(num_slices)]) | ||
| fullfield_ = np.concatenate([fullfield_all[ind,:,:],np.zeros((fullfield_all.shape[0],1,fullfield_all.shape[2]))], axis=1) |
There was a problem hiding this comment.
This assignment to fullfield_ is immediately overwritten on the next line, making it dead code. Remove or consolidate these lines to avoid confusion.
| fullfield_ = np.concatenate([fullfield_all[ind,:,:],np.zeros((fullfield_all.shape[0],1,fullfield_all.shape[2]))], axis=1) |
| ind = np.hstack([np.arange(i, fullfield_all.shape[0], num_slices) for i in range(num_slices)]) | ||
| fullfield_ = np.concatenate([fullfield_all[ind,:,:],np.zeros((fullfield_all.shape[0],1,fullfield_all.shape[2]))], axis=1) | ||
| fullfield_ = fullfield_all[ind,:,:] | ||
| fullfield_stitched = np.concatenate([np.roll(fullfield_[:, i*num_rows : (i+1)*num_rows, :], -i*12, axis=1) for i in range(num_columns)],axis=2) |
There was a problem hiding this comment.
[nitpick] The roll offset multiplier 12 is a magic number with no explanation. Consider defining a named constant or adding a comment describing why 12 was chosen.
| fullfield_stitched = np.concatenate([np.roll(fullfield_[:, i*num_rows : (i+1)*num_rows, :], -i*12, axis=1) for i in range(num_columns)],axis=2) | |
| fullfield_stitched = np.concatenate([np.roll(fullfield_[:, i*num_rows : (i+1)*num_rows, :], -i*ROLL_OFFSET_MULTIPLIER, axis=1) for i in range(num_columns)],axis=2) |
No description provided.