Skip to content

Conversation

@Alpaca233
Copy link

Summary

  • Add zarr_version parameter (2 or 3) for output format control - v2 is ome-zarr compatible (napari), v3 uses sharding for better performance
  • Add separate_timepoints option to save each timepoint as a separate .ome.zarr file
  • Add blend_bias parameter (0.0-1.0) for asymmetric tile blending in overlaps, with GUI slider

Test plan

  • All 68 unit tests pass
  • Code formatted with Black
  • Manual testing with GUI - stitching completes successfully with new blend bias slider

🤖 Generated with Claude Code

- 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]>
Copy link
Contributor

Copilot AI left a 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_version parameter (2 or 3) to control output format - v2 for napari compatibility, v3 for sharding performance
  • Adds separate_timepoints option to save each timepoint as an individual .ome.zarr file
  • Adds blend_bias parameter (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.

Comment on lines 356 to 358
metadata = {
"multiscales": [mult]
}
Copy link

Copilot AI Jan 9, 2026

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.

Copilot uses AI. Check for mistakes.
Copy link
Author

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

"_ARRAY_DIMENSIONS": ["t", "c", "z", "y", "x"]
}
with open(scale_path / ".zgroup", "w") as f:
json.dump({}, f)
Copy link

Copilot AI Jan 9, 2026

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.

Suggested change
json.dump({}, f)
json.dump({"zarr_format": 2}, f)

Copilot uses AI. Check for mistakes.
Copy link
Author

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.

Comment on lines +442 to +444
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.")
Copy link

Copilot AI Jan 9, 2026

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.

Copilot uses AI. Check for mistakes.
Copy link
Author

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.

Comment on lines +63 to +67
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)
Copy link

Copilot AI Jan 9, 2026

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.

Copilot uses AI. Check for mistakes.
Copy link
Author

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.

Comment on lines +80 to +85
# 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)
Copy link

Copilot AI Jan 9, 2026

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.

Suggested change
# 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)

Copilot uses AI. Check for mistakes.
Copy link
Author

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.

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