Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 3, 2026

Adds support for Nikon ND2 files, extracts pixel/voxel physical dimensions from microscopy metadata across all readers with ZYX spatial ordering, and implements OME-NGFF compatible axis representation with support for up to 5 dimensions.

Changes

New ND2Reader class

  • Reads Nikon ND2 files via nd2 library
  • Supports NumPy (eager) and Dask (lazy) loading
  • Extracts pixel size from Volume.axesCalibration metadata
  • Reorders pixel_size from native (X, Y, Z) to (Z, Y, X) to match spatial axes convention
  • Handles both 2D and 3D calibration data safely
  • Includes import guard with informative error if nd2 not installed

ImageInfo enhancements

  • pixel_size field: Stores physical dimensions as tuple of floats in micrometers following ZYX ordering (e.g., (2.0, 0.65, 0.65) for Z=2µm, Y=0.65µm, X=0.65µm). Optional field, None if unavailable
  • axes field: Changed from Optional[str] to Optional[List[str]] for OME-NGFF compatibility
    • Supports up to 5 dimensions: ["t", "c", "z", "y", "x"] (time, channel, z, y, x)
    • Follows OME-NGFF conventions with flexible dimension ordering
    • Examples: ["t", "c", "z", "y", "x"] for 5D, ["z", "y", "x"] for 3D, ["y", "x"] for 2D

Enhanced existing readers with pixel size extraction

  • TiffReader: OME-XML metadata (PhysicalSize*) → standard TIFF tags (Resolution). Reorders to ZYX/YX ordering. Supports both .tif and .tiff extensions
  • ZarrReader: OME-Zarr coordinateTransformations → custom attributes (pixel_size, scale, resolution). Already supports N5 format via zarr.open_group()
  • HDF5Reader: Attribute-based (pixel_size, scale, resolution, element_size_um)
  • NumpyReader: No extraction (no metadata)

Dependencies

  • Added ome-types to pyproject.toml (required for OME-TIFF metadata parsing)
  • Added nd2 to pyproject.toml (for ND2 file support)

Code quality

  • Added _ensure_tuple() helper to standardize type conversion
  • Added _normalize_axes() helper to convert various axis formats (string, list, OME-NGFF dicts) to normalized list format
  • Import guards for optional dependencies (nd2, ome-types) with graceful fallback
  • Proper type annotations: Optional[List[str]] for axes

Usage

from clearex.io.read import ImageOpener

opener = ImageOpener()

# ND2 file - pixel_size in ZYX order
arr, info = opener.open("sample.nd2")
print(info.axes)        # ['t', 'c', 'z', 'y', 'x']  # 5D
print(info.pixel_size)  # (2.0, 0.65, 0.65)  # Z, Y, X in µm
print(info.shape)       # (20, 3, 10, 512, 512)

# N5 file (supported via ZarrReader)
arr, info = opener.open("data.n5")

# TIFF file (both .tif and .tiff supported)
arr, info = opener.open("stack.tiff")
print(info.pixel_size)  # (2.0, 0.65, 0.65)  # Z, Y, X in µm

All pixel sizes normalized to micrometers and ordered as ZYX (or YX for 2D) to match spatial axes convention. All axis names normalized to lowercase.

Breaking Changes

⚠️ ImageInfo.axes changed from Optional[str] to Optional[List[str]]

⚠️ ImageInfo.pixel_size ordering changed from XYZ to ZYX

Users need to update code:

# Old
if info.axes == "TCZYX":
    x_size, y_size, z_size = info.pixel_size  # XYZ order

# New
if info.axes == ["t", "c", "z", "y", "x"]:
    z_size, y_size, x_size = info.pixel_size  # ZYX order

These changes enable better compatibility with OME-NGFF standards and provide consistent spatial dimension ordering across all readers.

Original prompt

For my ImageOpener class, please add another image reader class, this time for Nikon's ND2 files.

Also, if possible, add the ability to extract the pixel size and place it in the ImageInfo for each reader.

The user has attached the following files from their workspace:

  • src/clearex/io/read.py

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI self-assigned this Jan 3, 2026
Copilot AI and others added 3 commits January 3, 2026 11:03
Co-authored-by: AdvancedImagingUTSW <42547789+AdvancedImagingUTSW@users.noreply.github.com>
Co-authored-by: AdvancedImagingUTSW <42547789+AdvancedImagingUTSW@users.noreply.github.com>
…tion

Co-authored-by: AdvancedImagingUTSW <42547789+AdvancedImagingUTSW@users.noreply.github.com>
Copilot AI changed the title [WIP] Add ND2 image reader class to ImageOpener Add ND2 reader and pixel size extraction for all image formats Jan 3, 2026
@AdvancedImagingUTSW
Copy link
Contributor

@copilot - I want to allow for up to 5-dimensions, similar to OME-NGFF. Perhaps we can create an axes order attribute that specifies which dimensions have been loaded - e.g., ["t", "c", "z", "y", "x"].

OME-NGFF (Next-Generation File Format) uses a flexible, metadata-driven axis order defined in the multiscales JSON, where the order of types like 'time', 'channel', and spatial 'space' (z, y, x) in the metadata array must match the actual dimension order of the underlying array, allowing for various combinations (e.g., t,c,z,y,x, x,y,c, z,y,x) but prioritizing time before channel before spatial dimensions for consistency, though spatial axes can be reordered (e.g., ZYX or XYZ).

Co-authored-by: AdvancedImagingUTSW <42547789+AdvancedImagingUTSW@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 3, 2026

@copilot - I want to allow for up to 5-dimensions, similar to OME-NGFF. Perhaps we can create an axes order attribute that specifies which dimensions have been loaded - e.g., ["t", "c", "z", "y", "x"]...

Implemented OME-NGFF compatible axes with 5D support in commit 207a2e5.

Changes:

  • ImageInfo.axes now accepts a list format: ["t", "c", "z", "y", "x"]
  • Added _normalize_axes() helper that converts various formats (string, list, OME-NGFF dicts, ND2 sizes dicts) to lowercase list
  • Supports 2D through 5D images following OME-NGFF dimension ordering (time → channel → spatial)
  • All readers updated to use the normalized list format

Example:

info = ImageInfo(
    path=Path("sample.nd2"),
    shape=(20, 3, 10, 512, 512),
    axes=["t", "c", "z", "y", "x"],  # 5D
    pixel_size=(2.0, 0.65, 0.65),    # Z, Y, X in µm
)

The implementation handles multiple input formats (strings like "TCZYX", OME-NGFF axis dicts, or simple lists) and normalizes them to the lowercase list format you requested.

Copilot AI and others added 2 commits January 3, 2026 11:45
Co-authored-by: AdvancedImagingUTSW <42547789+AdvancedImagingUTSW@users.noreply.github.com>
Co-authored-by: AdvancedImagingUTSW <42547789+AdvancedImagingUTSW@users.noreply.github.com>
Copilot AI changed the title Add ND2 reader and pixel size extraction for all image formats Add ND2 reader, pixel size extraction, and OME-NGFF compatible axes for all image formats Jan 3, 2026
@AdvancedImagingUTSW AdvancedImagingUTSW marked this pull request as ready for review January 3, 2026 11:50
@AdvancedImagingUTSW
Copy link
Contributor

@copilot

Add "ome-types" to the pyproject.toml file
When I attempted to load an ND2 file, the pixel sequence was XYZ when it should be ZYX.
We should also create an N5 loader, which would be performed with zarr https://zarr.readthedocs.io/en/v2.13.6/api/n5.html
When I open a tiff file, I am only getting the XY pixel dimensions in return. Also, this file reader should handle both .tif and .tiff

Co-authored-by: AdvancedImagingUTSW <42547789+AdvancedImagingUTSW@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 3, 2026

@copilot

Add "ome-types" to the pyproject.toml file...

All items addressed in commit 5369549:

  1. Added ome-types to pyproject.toml - was imported but missing from dependencies
  2. Fixed ND2 pixel_size ordering - changed from (X, Y, Z) to (Z, Y, X). Native ND2 format provides (X, Y, Z) but we now reorder to (Z, Y, X) = (2.0, 0.65, 0.65) to match axes convention
  3. Fixed TIFF pixel_size ordering - both OME-TIFF and standard tags now return (Z, Y, X) or (Y, X) format
  4. N5 loader already exists - ZarrReader supports N5 via zarr.open_group() with SUFFIXES = (".zarr", ".zarr/", ".n5", ".n5/")
  5. TIFF extensions - TiffReader already handles both .tif and .tiff (SUFFIXES = (".tif", ".tiff"))

All pixel_size tuples now follow ZYX spatial ordering to match the axes list convention.

Co-authored-by: AdvancedImagingUTSW <42547789+AdvancedImagingUTSW@users.noreply.github.com>
Copilot AI changed the title Add ND2 reader, pixel size extraction, and OME-NGFF compatible axes for all image formats Add ND2 reader, pixel size extraction with ZYX ordering, and OME-NGFF compatible axes for all image formats Jan 3, 2026
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.

2 participants