Skip to content

feat(stack): add nansen.stack.crop to crop and export image stacks - #139

Merged
ehennestad merged 2 commits into
devfrom
add-imagestack-crop-function
Sep 17, 2026
Merged

ehennestad merged 2 commits into
devfrom
add-imagestack-crop-function

Conversation

@ehennestad

Copy link
Copy Markdown
Collaborator

Adds nansen.stack.crop, the image stack counterpart of imcrop. It crops every frame of an ImageStack to a rectangle and returns a new ImageStack. With FilePath, the cropped data is written to a new file and a virtual ImageStack for that file is returned. Adds a unit test class for the function.

imageStack = nansen.stack.ImageStack('/path/to/recording.tsm');

% Crop to a 120-by-120 pixel region with its upper left pixel at column 56, row 25,
% and save the result as a binary stack.
croppedStack = nansen.stack.crop(imageStack, [56, 25, 120, 120], ...
    FilePath='/path/to/recording_cropped.raw');
  • rect is [xmin, ymin, width, height] in pixel indices, and the output has exactly height rows and width columns. A rectangle that extends outside the image is an error.
  • All channels, planes and timepoints are kept, along with the dimension arrangement and the metadata that stays valid for a cropped stack.
  • Data is read and written in chunks, and only the cropped region is requested from the source, so a stack larger than memory can be cropped to a binary file. ChunkLength overrides the default chunk size, which is chosen from the available memory.
  • Without FilePath, the cropped stack is returned in memory. With FilePath, the extension selects the format: binary (.raw) or TIFF (.tif, .tiff), which are the formats nansen.stack.open can create.
  • Existing files are never written to. The function errors if the target file exists. It also errors if TiffMultiPart resolves the new path to a numbered series of TIFF files that already exists in the folder, because writes would otherwise go into those files.

Also fixes cropped reads in the TSM adapter. TSM.readData subtracted the full dark frame from the requested data. A read cropped along x or y failed with an incompatible sizes error, and a read of a single row or column was implicitly expanded to the full frame size. The dark frame is now indexed with the same x and y subscripts as the image data. nansen.stack.crop depends on this, because it requests only the cropped region.

Metadata is copied property by property, because StackMetadata.updateFromSource clears StartTime on the source metadata object. That bug is left as is here.

The tests that create .raw files are expected to fail on the Linux runner. From reading the code, Binary.initializeFile assigns status only on Windows and macOS, so creating a binary stack errors on Linux. The fix is a one-line change in Binary.m and is not part of this PR.

🤖 Generated with Claude Code

ehennestad and others added 2 commits September 17, 2026 17:16
nansen.stack.crop(imageStack, rect) crops every frame of an ImageStack
to the rectangle [xmin, ymin, width, height] and returns a new
ImageStack. The output has exactly height rows and width columns. All
channels, planes and timepoints are kept, along with the dimension
arrangement and the metadata that stays valid for a cropped stack.

With FilePath, the cropped data is written to a binary (.raw) or TIFF
file, and a virtual ImageStack for that file is returned. Data is read
and written in chunks along the time or plane dimension, and only the
cropped region is requested from the source, so a stack larger than
memory can be cropped to a binary file.

Existing files are never written to. The function errors if the target
file exists. It also errors if the TIFF adapter resolves the new file
path to a numbered series of TIFF files that already exists in the
folder, because writes would otherwise go into those files.

Metadata is copied property by property, because
StackMetadata.updateFromSource clears StartTime on the source metadata
object.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
TSM.readData subtracted the full dark frame from the requested data.
A read cropped along x or y failed with an incompatible sizes error,
because the sizes differ. A read of a single row or column did not
fail, but was implicitly expanded to the full frame size.

The dark frame is now indexed with the same x and y subscripts as the
image data. nansen.stack.crop depends on this, because it requests
only the cropped region from the source.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ehennestad
ehennestad merged commit 4567aaa into dev Sep 17, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant