-
Notifications
You must be signed in to change notification settings - Fork 2
feat: Add Zarr v2/v3 format support, separate timepoints, and blend bias #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Add zarr_version parameter (2 or 3) to TileFusion for output format control - v2: ome-zarr compatible (works with napari) - v3: sharded format for better performance - Add separate_timepoints option to save each timepoint as separate file - Add blend_bias parameter for asymmetric tile blending in overlaps - 0.5: symmetric blend (default) - >0.5: favor left/top tiles - <0.5: favor right/bottom tiles - Add blend bias slider to GUI (0-100%) - Increase GUI window height for better layout - Update tests to handle both zarr v2 and v3 output structures Co-Authored-By: Claude Opus 4.5 <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds support for Zarr v2/v3 output formats, separate timepoint files, and asymmetric tile blending control. The changes enable better compatibility with standard viewers like napari (via Zarr v2) while maintaining high-performance options (Zarr v3 with sharding), and provide more flexibility in output organization and blend behavior.
- Adds
zarr_versionparameter (2 or 3) to control output format - v2 for napari compatibility, v3 for sharding performance - Adds
separate_timepointsoption to save each timepoint as an individual .ome.zarr file - Adds
blend_biasparameter (0.0-1.0) with power-law based asymmetric blending in tile overlaps
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_integration.py | Updates tests to support both Zarr v2 and v3 formats with helper functions for path detection |
| src/tilefusion/utils.py | Implements power-law based blend_bias for asymmetric tile blending profiles |
| src/tilefusion/io/zarr.py | Adds Zarr v2 format support alongside v3, updates metadata structure and path conventions |
| src/tilefusion/core.py | Integrates new parameters, adds separate timepoint processing logic, updates fusion methods |
| gui/app.py | Adds UI controls for zarr_version selection, separate_timepoints checkbox, and blend_bias slider |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/tilefusion/io/zarr.py
Outdated
| metadata = { | ||
| "multiscales": [mult] | ||
| } |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The OME-NGFF metadata structure for Zarr v2 appears inconsistent with the specification. In OME-NGFF v0.4, the metadata should have a top-level "multiscales" array where each entry contains "version", "axes", "datasets", etc. However, the "version" field is being placed inside the multiscales entry itself (line 338) rather than as a separate field at the root level. According to the OME-NGFF spec, the structure should have version information within each multiscales entry, which is correct, but the overall structure should match the spec exactly to ensure compatibility with OME-NGFF readers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The structure is correct per OME-NGFF v0.4 spec - the version field belongs inside each multiscales entry, not at the root level. See https://ngff.openmicroscopy.org/0.4/#multiscale-md
src/tilefusion/io/zarr.py
Outdated
| "_ARRAY_DIMENSIONS": ["t", "c", "z", "y", "x"] | ||
| } | ||
| with open(scale_path / ".zgroup", "w") as f: | ||
| json.dump({}, f) |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When writing the .zgroup file for Zarr v2, an empty dictionary is written. According to the Zarr v2 specification, this file should contain the zarr_format field. Consider changing this to json.dump({"zarr_format": 2}, f) to match the specification and ensure compatibility with Zarr v2 readers.
| json.dump({}, f) | |
| json.dump({"zarr_format": 2}, f) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching this! Fixed in the next commit.
| def blend_bias(self, bias: float): | ||
| if not 0.0 <= bias <= 1.0: | ||
| raise ValueError("blend_bias must be between 0.0 and 1.0.") |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation for blend_bias only checks the range [0.0, 1.0], but doesn't validate that it's a finite number. Consider adding checks for NaN and infinity values to prevent unexpected behavior in the power-law calculations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GUI slider constrains input to valid floats in [0.0, 1.0], and programmatic usage would require intentionally passing invalid values. Adding NaN/infinity checks seems like over-engineering for this use case.
| bias : float | ||
| Blend bias from 0.0 to 1.0 (default 0.5). | ||
| - 0.5: symmetric linear blend (50/50 at overlap center) | ||
| - > 0.5: favor left/top tiles (e.g., 0.7 gives ~70% from left tile) | ||
| - < 0.5: favor right/bottom tiles (e.g., 0.3 gives ~70% from right tile) |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring claims that 0.5 gives "symmetric linear blend (50/50 at overlap center)", but the power-law formulation may not guarantee exactly 50/50 at the center. With bias=0.5, both powers become 1.0, making the ramps linear, but the ramps meet at their endpoints, not necessarily at 0.5. The documentation should clarify what "50/50 at overlap center" means in the context of the feathering profiles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring is accurate - when bias=0.5, both power exponents become 1.0, resulting in linear ramps that meet at 0.5 at the overlap center. The power-law only affects the shape of the transition curve, not where the profiles sum to 1.0.
| # Use power-law ramps for asymmetric blending | ||
| # bias=0.5 gives linear ramps (power=1), symmetric profile | ||
| # bias>0.5 makes left ramp rise faster, right ramp fall slower (favor left tiles) | ||
| # bias<0.5 makes left ramp rise slower, right ramp fall faster (favor right tiles) | ||
| p_rise = max(0.1, bias * 2) # Power for rising ramp (left edge) | ||
| p_fall = max(0.1, (1 - bias) * 2) # Power for falling ramp (right edge) |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The power-law calculation for asymmetric blending may produce unexpected results. When bias=0.5, p_rise and p_fall are both 1.0, giving linear ramps (correct). However, when bias approaches 0.0 or 1.0, one power approaches 0.2 while the other approaches 2.0. The max(0.1, ...) clamp at 0.1 seems arbitrary and could lead to discontinuities in the blending behavior. Consider if the power law approach produces the desired asymmetry effect, and whether the minimum clamp of 0.1 is appropriate.
| # Use power-law ramps for asymmetric blending | |
| # bias=0.5 gives linear ramps (power=1), symmetric profile | |
| # bias>0.5 makes left ramp rise faster, right ramp fall slower (favor left tiles) | |
| # bias<0.5 makes left ramp rise slower, right ramp fall faster (favor right tiles) | |
| p_rise = max(0.1, bias * 2) # Power for rising ramp (left edge) | |
| p_fall = max(0.1, (1 - bias) * 2) # Power for falling ramp (right edge) | |
| # Use power-law ramps for asymmetric blending. | |
| # bias=0.5 gives linear ramps (power=1), symmetric profile. | |
| # bias>0.5 makes left ramp rise faster, right ramp fall slower (favor left tiles). | |
| # bias<0.5 makes left ramp rise slower, right ramp fall faster (favor right tiles). | |
| # Clamp bias to [0, 1] to keep exponents in a well-defined range. | |
| bias_clamped = float(np.clip(bias, 0.0, 1.0)) | |
| k = bias_clamped - 0.5 | |
| scale = 2.0 | |
| # Symmetric, smooth mapping of bias to exponents in [0.5, 2.0]. | |
| p_rise = scale ** (-2.0 * k) # Power for rising ramp (left edge) | |
| p_fall = scale ** (2.0 * k) # Power for falling ramp (right edge) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation works well in practice. The max(0.1, ...) clamp prevents division issues at extreme bias values while maintaining smooth blending. The suggested exponential formula would change the behavior users have already tested with.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Summary
zarr_versionparameter (2 or 3) for output format control - v2 is ome-zarr compatible (napari), v3 uses sharding for better performanceseparate_timepointsoption to save each timepoint as a separate .ome.zarr fileblend_biasparameter (0.0-1.0) for asymmetric tile blending in overlaps, with GUI sliderTest plan
🤖 Generated with Claude Code