Repository navigation
smartspim: prefer SmartSPIM's stitched All_Channels volume - #142
Merged
Merged
Conversation
ferret-lightsheet (formats/SmartSPIM.md, spim2zarr --source all_channels)
established that All_Channels/ holds SmartSPIM AutoStitch's TeraStitcher
merge of the destriped tiles, with flat-field / left-right illumination
correction and blended overlaps -- the better-stitched, harmonized data
Imaris is built from. The reader now prefers it.
Format layer (new):
* readAllChannelsInfo - index All_Channels/<Y>_<X>_<z*10>_ch<k>.tiff,
map each Ex_* channel to its ch<k> (from <channel>_MIP_stitched,
else filter-channel rank; not acquisition order), slices sorted by
numeric z, canvas size / class from the first header, voxel size
from metadata.json.
* hasAllChannels - non-throwing probe (names only): usable only when
every channel has slices.
* readAllChannelsVolume - one channel as (z, y, x), optional ZRange.
Reader epochs:
{root} stitched volume, all channels on C (ch<k> order)
{root, channel} stitched volume, one channel
{root, channel, tile} destriped Ex_* tile, unchanged
Without a usable All_Channels (AutoStitch not run, partial restore),
{root} and {root, channel} keep the ChannelNotPinned / TileNotPinned
errors: the Ex_* tiles are not obsolete, so the per-tile layout stays.
resolveepoch gains source / channelNames / numSlices / height / width /
dtype / allChannels fields; info.tile is kept for tile epochs.
The test fixture takes 'AllChannels', true to add an LZW All_Channels
folder; new TestSmartSPIMAllChannels and TestSmartSPIMReaderAllChannels.
Not run: no MATLAB in the authoring environment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U33UPxYJWPpcQimNu5t5Cb
Contributor
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #142 +/- ##
==========================================
+ Coverage 53.26% 54.26% +1.00%
==========================================
Files 160 163 +3
Lines 9190 9420 +230
==========================================
+ Hits 4895 5112 +217
- Misses 4295 4308 +13 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
tiffDtype treated any SampleFormat containing "signed integer" as signed, and imfinfo's 'Unsigned integer' contains that substring. A TIFF that stores SampleFormat = 1 explicitly (libtiff-based writers may) therefore came back 'int16', and readframes then preallocated int16, clipping every pixel above 32767. Files from tifffile and imwrite omit the tag, which is why the fixtures and real tiles never hit it. Rule out 'unsigned' before testing for 'signed'. New tests write TIFFs with an explicit SampleFormat tag (UInt and Int) for readTileInfo and for readAllChannelsInfo, which already had the corrected logic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U33UPxYJWPpcQimNu5t5Cb
tiffclass matched SampleFormat by exact string, and its float case only accepted 'ieeefloat' / 'float'. imfinfo reports float TIFFs as 'IEEE floating point', which fell through to the unsigned branch: a float32 stack came back as 'uint32' and readframes cast every value to uint32, dropping fractions and clamping negatives to 0. Match by substring instead, ruling out 'unsigned' before testing for 'signed' (the same fix as readTileInfo in 3e300e5). Affects ndr.reader.prairieview too, which reuses tiffclass. New tests round-trip a float32 and an int16 stack written with an explicit SampleFormat; writeMultipageTiff takes an optional format and derives BitsPerSample from the data class (default unchanged: uint16). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U33UPxYJWPpcQimNu5t5Cb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ferret-lightsheet (
formats/SmartSPIM.md,spim2zarr --source all_channels) established thatAll_Channels/contains SmartSPIM AutoStitch's TeraStitcher merge of the destriped tiles, with flat-field and left/right illumination correction and blended overlaps. This is the better-stitched, harmonized data that Imaris is built from. With this PR,ndr.reader.smartspimreads it first. The PR also fixes two TIFF data-type bugs found along the way (see below).All_Channels layout:
<Y>_<X>_<z*10>_ch<k>.tiff, plus anImageList.txt.ch<k>follows the filter-channel number, not the acquisition order. It comes from<channel>_MIP_stitched/, or from the filter-channel rank when that folder is missing.New format functions (
+ndr/+format/+smartspim)readAllChannelsInfo: indexes the slices (sorted by numeric z) and maps each channel to itsch<k>. Also returns the canvas size and class from the first TIFF header, and the voxel size frommetadata.json.hasAllChannels: a non-throwing probe that checks file names only. It returns true only when every channel has slices.readAllChannelsVolume: reads one channel as (z, y, x), with an optionalZRange.Reader epochs
{root}ch<k>order){root, channel}{root, channel, tile}Ex_*tile (unchanged)Fallback: the
Ex_*tiles are not obsolete; ferret-lightsheet treats them as the source of truth. WhenAll_Channelsis missing or incomplete,{root}and{root, channel}raise the sameChannelNotPinned/TileNotPinnederrors as before.New
resolveepochfields:source,channelNames,numSlices,height,width,dtypeandallChannels.info.tileis unchanged for tile epochs.Fix 1:
readTileInforeported int16 for some uint16 TIFFs (commit3e300e5)tiffDtypetreated anySampleFormatcontaining "signed integer" as signed. imfinfo's'Unsigned integer'contains that text.int16.readframesthen allocated anint16array, so every pixel above 32767 was clipped.imwritedon't store the tag. libtiff-based writers may.'unsigned'before testing for'signed'.readTileInfoandreadAllChannelsInfo.readAllChannelsInfoalready used the corrected logic.Fix 2:
tiffstack.tiffclassread float TIFFs as uint32 (commit8f870c1)tiffclassmatched SampleFormat by exact string, and its float case accepted only'ieeefloat'/'float'. imfinfo reports float TIFFs as'IEEE floating point', so they fell through to the unsigned branch.uint32.readframesthen cast every value touint32, dropping fractions and clamping negative values to 0.ndr.reader.prairieview, which reusestiffclass.TestTiffstacknow round-trips a float32 stack and an int16 stack.writeMultipageTifftakes an optional SampleFormat; existing callers still get uint16.Tests
makeExampleFixture(..., 'AllChannels', true)adds an LZWAll_Channelsfolder to the test fixture.TestSmartSPIMAllChannelsandTestSmartSPIMReaderAllChannels.All_Channels) still cover the fallback path.3e300e5: 396 tests, 0 failures. The tiffstack tests were added afterwards in8f870c1.The Python port is VH-Lab/NDR-python#31. Its bridge YAML records
b8f0e4b(All_Channels),3e300e5(readTileInfo) and8f870c1(tiffstack). The Python readers get the data type directly from tifffile and never had either bug.🤖 Generated with Claude Code
https://claude.ai/code/session_01U33UPxYJWPpcQimNu5t5Cb