Skip to content

Format-aware image export (normalization, dtype, and format options) #256

@PierreRaybaut

Description

@PierreRaybaut

Summary

Add a format‑aware image export pipeline that lets users configure how image data are normalized, cast, and written, with options that adapt to the chosen file format (PNG, TIFF, JPEG/JP2, NPY, …).

Scope includes:

  • A new Export Image dialog in DataLab (format‑specific UI).
  • Backend support in Sigima (sigima.io) to prepare data (normalize/clip/cast) and to pass format constraints.
  • An optional graphical preview showing the image as it would be exported (write to a temp file, reload, display).

Motivation

Currently, ClassicsImageFormat (see sigima/io/image/formats.py) hard‑casts to uint8 for BMP/JPEG/PNG and to uint8/uint16 for JPEG2000 before calling skimage.io.imsave, without normalization. When the source array is float or spans a wide dynamic range, this causes unexpected clipping or loss. Scientific formats (NPY, some TIFF) can keep raw data but there is no unified way to choose behavior from DataLab.

We want:

  1. Predictable exports (explicit normalization and dtype choice).
  2. Format‑aware UI (only relevant options per selected format).
  3. A clean backend API so DataLab delegates to Sigima.

DataLab – Export dialog (format‑aware)

The dialog dynamically shows options relevant to the chosen format:

Data → Normalization

  • Mode: None / Min–Max / Percentile (p_low–p_high) / Manual (range_min–range_max)
  • Behavior: Clip vs Rescale
  • NaN/Inf policy: error / clip / replace (with replacement value)
  • Optional: Gamma (apply before/after scaling), Invert

Data → Target dtype

  • If supported by the format: uint8, uint16, float32, float64, …

Format‑specific options (progressive rollout)

  • PNG: compression level
  • JPEG: quality
  • TIFF: compression (none, LZW, Deflate, JPEG), bit depth (8/16/32F), multi‑page
  • JP2: bit depth (8/16)
  • NPY: raw export (no normalization) or explicit normalization if requested

Metadata

  • Include DataLab/Sigima metadata (embed if supported, or sidecar JSON).

Batch / stack export

  • Single image, stack to multi‑page TIFF, or sequence with naming pattern.

Preview (optional but recommended)

  • Button to render a graphical preview by exporting to a temporary file using current settings, reopening it, and displaying the exact result. This removes ambiguity vs. “report‑only” approaches.

Sigima – Backend API (non‑breaking)

To keep the public API stable, extend the existing function:

# sigima/io/__init__.py
def write_image(filename: str, image: ImageObj, param: ImageExportParam | None = None) -> None:
    ...
  • If param is None, behavior is unchanged (backward compatible).
  • If param is provided, data are prepared (normalize/clip/cast) before delegating to the format writer.

New parameter class

Introduce a format-aware export preparation layer so the UI can pass one object describing export choices:

  • New parameter object (DataSet) ImageExportParam:
    • normalization_mode: Enum('none', 'minmax', 'percentile', 'manual')
    • range_min: float | None, range_max: float | None (manual)
    • percentile_low: float, percentile_high: float
    • scale_behavior: Enum('clip', 'rescale')
    • target_dtype: np.dtype | str | None
    • nan_policy: Enum('error', 'clip', 'replace')
    • nan_replacement: float | None
    • gamma: float | None
    • invert: bool
    • format_options: dict[str, Any] (passed through to the writer when supported; e.g. quality, compress_level, compression)

Export preparation

# sigima/io/image/export.py
def prepare_image_for_export(data: np.ndarray, ext: str, param: ImageExportParam) -> np.ndarray:
    """
    Applies normalization/range policy, gamma/invert, dtype casting, and
    enforces per‑format constraints (e.g. PNG/JPEG require uint8, JP2 allows uint8/uint16, TIFF allows 8/16/32F, NPY any).
    Returns the final array ready for writing.
    """

Write path (no API break)

Implementation sketch in sigima/io/__init__.py:

  1. Resolve format via ImageIORegistry.get_format(filename, IOAction.SAVE).
  2. If param is not None:
    • data2 = prepare_image_for_export(image.data, ext, param)
    • Create a shallow copy of image with data2 (to preserve metadata).
    • Delegate to the resolved format’s write(...) (unchanged signature).
  3. Else: current behavior (call registry write directly).

Note: For phase 1, we focus on normalization/dtype (which can be handled entirely in prepare_image_for_export).
Phase 2 will add optional propagation of param.format_options (e.g., TIFF compression, JPEG quality, PNG compress level). That may require small refactors in concrete write_data(...) implementations to accept and forward writer kwargs. This can be done incrementally per format without breaking the public API.

Per‑format rules (enforced in preparation)

  • PNG/JPEG: require uint8. If param.normalization_mode == 'none' and input isn’t uint8, either error or auto min–max (configurable project default).
  • JP2: allow uint8 or uint16; normalize/cast accordingly.
  • TIFF: support uint8 / uint16 / float32 (and float64 where supported).
  • NPY: raw by default; if param requests normalization or dtype change, apply then save.

Preview design (DataLab)

  • Preview” button in the dialog:
    • Calls write_image(tempfile, image, param) → reopens → displays.
    • Shows the actual exported result for maximum clarity.
    • Cleans up temp files automatically.

Testing

Sigima (unit tests):

  • Synthetic ramps/patterns with float → PNG/JPEG (uint8 min–max and percentile).
  • Wide dynamic range float → TIFF as float32 (raw) vs uint16 (scaled).
  • JP2: verify uint8 vs uint16.
  • Edge cases: NaN/Inf handling, manual range, gamma, invert, clip vs rescale.

DataLab (integration/UI tests):

  • Option enable/disable by format.
  • Preview renders exactly what the export produces (temp file round‑trip).

Open questions

  • Default policy when exporting non‑uint8 to 8‑bit formats:
    • strict (error) vs permissive (auto min–max). Propose a global config default and a clear notice in the dialog.
  • Rollout strategy for format_options (TIFF compression, JPEG quality, PNG compress level): per‑format incremental support in phase 2.

Benefits

  • Predictable, format‑appropriate results; less accidental data loss.
  • A single extensible dialog in DataLab rather than ad‑hoc prompts.
  • Clear separation of concerns: UI declares intent; Sigima prepares data and enforces constraints; writers remain stable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions