UDeep

In your apps · plan, 28 September 2026

A figure standard is only useful if your tools speak it.

The profile is meant for the applications scientists already use: notebooks, image viewers, plotting libraries, and medical, microscopy and astronomy tools. It covers both halves of the name: the range a figure reaches above white, and the depth it holds, which the file states in its metadata. An app can adopt it at three levels, and each level stands on its own.

Three levels of support

Level 1 · files

Read and write conforming figures

Export the current view as a profile file: a gain-map JPEG, a 16-bit HDR PNG or an AVIF, with the declared mapping as metadata. Open one, and show its mapping and colour bar in data units.

Works in any app today, because the file is the ordinary figure wherever HDR is missing.

Level 2 · live view

Render HDR in the app's own window

Apply the declared mapping on the GPU and draw above white. Each platform has its own route:

  • Windows: a 16-bit float (scRGB) swap chain.
  • macOS and iOS: extended dynamic range (EDR) Metal layers.
  • The web: WebGPU with extended tone mapping.

Interface text stays at SDR white.

The most native feel, and the most work per app.

Level 3 · shared core

One library, many bindings

One small core library holds the mapping, the tone curve, the encoders and the conformance validator, with bindings for Python, JavaScript/WebAssembly, Julia, Java and MATLAB. Apps call it instead of reimplementing it.

Every implementation is held to the same golden test vectors.

Where to start

The first targets are apps that handle high-range data, are open source, and have a plugin system. None of those listed below has native HDR output for scientific data that we have found.

AppFieldWhy it fitsFirst step
JupytereverythingNotebooks render in a browser, and browsers already show HDR imagesShow udeep figures as HDR inline (level 1); a WebGPU widget for live views (level 2)
naparimicroscopyA Python viewer for 12–16-bit data, with pluginsAn export plugin (level 1), then an HDR canvas (level 2)
MakieJulia plottingScientific plotting with GPU back endsHDR export from CairoMakie: an extension of the Julia binding draws an HDR image and colour bar and saves the figure as a gain-map JPEG with its declaration (Makie 0.24). The Python validator accepts the files. Other Makie back ends are not covered, and the package is not yet installable on its own
Fiji / ImageJmicroscopyThe most widely used open image-analysis tool, with Java pluginsAn export plugin through the core's Java binding
3D Slicermedical imagingCT and MR, where window/level already means two rangesExtend-style export of a window plus above-window structure
ParaViewsimulationFloat data and GPU rendering; Python scriptingAn export filter (level 1)
Astronomy (astropy, SAOImage DS9)astronomyFloat images whose asinh stretches exist because 8-bit clipsAn astropy-style visualisation helper that writes profile files

What an app has to do

From the profile's viewer rules, in short:

  1. Apply the file's declared mapping exactly, fitted to the display's headroom.
  2. Never apply automatic tone mapping, "Auto HDR" or SDR-to-HDR expansion to scientific images.
  3. Keep interface text, axes and labels at SDR white.
  4. Show a colour bar in data units, with the value at SDR white marked.
  5. Never lift masked (saturated, clipped or invalid) pixels above white.
  6. When exporting, write the profile metadata, and write the ordinary SDR figure as the base.

What the conformance validator (python -m udeep.validate FILE.jpg) can check of this list, given only the file:

  • Item 6, the metadata half: every required property is present and the declaration is consistent, and the stated precision (MaxDataStep) is right. With --sdr FIGURE.png it also confirms that the named SDR figure is the one the file was made from, where the file declares its digest.
  • Item 1, the declaration half: the declared mapping rises everywhere and can be inverted, and nothing in the file is brighter than the declared headroom (5 % over is allowed for a lossy gain map).
  • Not from the file alone: whether the pixels are that mapping of the data, whether the base image is the ordinary SDR figure, and item 5. These need the figure that made the file (udeep.mpl.validate(fig, path)); the command-line tool reports them as skipped.
  • Not checked by any program: item 4. Items 2 and 3 are about how the app behaves on screen.

The shared core

The reference implementation today is Python (udeep). It reproduces all 90 golden vectors, which come in 14 kinds. A JavaScript port reproduces the 55 that it implements (11 kinds: it does not compute stored levels or write the metadata and caption). A small native core with a C interface exists in Rust: all 14 kinds are implemented; it reproduces all 90 cases. It has an optional gain-map JPEG writer, and no PNG writer or validator yet. The aim is that every language and app shares one implementation:

core (Rust, C ABI):  scale · tone curve · colour maps · PQ/HLG · gain map · metadata · validate
  ├─ Python     udeep            (matplotlib, Jupyter, napari, 3D Slicer, ParaView)
  ├─ JS / WASM  udeepcore            (web viewers, Jupyter widgets)
  ├─ Julia      UDeepCore        (through the core; figures with CairoMakie)
  ├─ Java       (planned)          (Fiji / ImageJ)
  └─ MATLAB     (planned)

A second implementation also matters for standardisation: standards bodies look for independent implementations that pass the same tests.