Nodes/ComfyUI-DiffHDR/DiffHDR Save EXR
ComfyUI Node

DiffHDR Save EXR

Writing float HDR that a compositor won't throw back at you

By claussteinmassl·Created 2 days ago·Updated a day ago· 5
DiffHDR Save EXR
  • images
    filename_prefixDiffHDR/hdr
    formatexr
    bit_depthhalf
    compressionzip
    dwa_compression_level45
    colorspacelinear_rec709
    start_frame1
    preview_exposure0.00

    This is the delivery node, and it's the reason the rest of the pack exists. The hdr output of DiffHDR is float linear Rec.709 with values in the tens. ComfyUI's SaveImage will quietly quantise that to 8 bits and clip everything above 1.0 - you'd be paying for an A100-minute of radiance reconstruction and throwing it away in the last node of the graph. DiffHDR Save EXR writes OpenEXR (16- or 32-bit float, with real compression choices) or Radiance .hdr, converts primaries if you need to, and stamps the correct chromaticities into the header so the next tool reads it correctly.

    It's an output node: no outputs, just files on disk, plus a tonemapped preview in the UI so you can see what you're writing.

    The settings that matter

    • filename_prefix - default DiffHDR/hdr, relative to ComfyUI's output folder. A batch doesn't go into one file: it becomes <prefix>_<counter>/frame_####.exr. A single image is written directly as <prefix>_<counter>.exr. That asymmetry is worth knowing before you point a comp at a folder.
    • format - exr or hdr (Radiance RGBE). The Radiance path is deliberately dumb: always linear Rec.709, and bit_depth, compression and colorspace are ignored. Fine for a quick handoff to a phone wallpaper generator; use EXR for anything serious.
    • bit_depth - half (16-bit) or float (32-bit). half is what most compositing work actually uses and it's half the size; values are clamped to ±65504, which is far beyond anything DiffHDR produces. Pick float when you're feeding something that insists on it.
    • compression - zip (default) and piz are lossless; dwaa/dwab are lossy and very effective on this kind of content, with dwa_compression_level (default 45, the OpenEXR default) trading size against quality. b44/b44a are the half-float-only compressors, so pairing them with bit_depth = float will get you an error from OpenEXR itself - that's a format rule, not a bug in the node. none on a 99-frame 720p sequence is a good way to fill a disk.
    • colorspace - linear_rec709 (default), acescg or aces2065_1. The node converts the pixels with a Bradford-adapted matrix and writes the matching chromaticities into the header, so downstream tools interpret the file correctly. acescg is the one a Nuke comp generally wants. Note the aces2065_1 path also sets acesImageContainerFlag, which is exactly the kind of detail that separates "it opens" from "it's right".
    • start_frame - the first frame number of a sequence. Padding is at least four digits and widens for the whole sequence when the last frame needs it, so frame_0998.exr doesn't sort after frame_1000.exr.
    • preview_exposure - stops, for the UI thumbnail only. It does not affect the file. Set it to −4 if you want the thumbnail to show you what the pack's README shows you.

    Frames, not just pictures

    A batch of frames gets a counter bump if the target folder already exists, so re-running a graph doesn't silently overwrite yesterday's render - you get a fresh hdr_00002/. Small thing, saves a lot of swearing. And saving is cheap next to sampling: 33 frames as float32 ZIP ran about 7.7 s including the preview, so don't let file size scare you off float.

    Install

    cd ComfyUI/custom_nodes
    git clone https://github.com/claussteinmassl/ComfyUI-DiffHDR.git
    pip install -r ComfyUI-DiffHDR/requirements.txt   # OpenEXR>=3.3 + huggingface_hub
    

    This is the node that actually needs the OpenEXR dependency, so don't skip the pip step - or use ComfyUI Manager, which installs requirements for you. Restart afterwards; the node appears under DiffHDR.

    Gotchas

    • OpenEXR too old. The pack pins OpenEXR>=3.3 because the writer uses the 3.x File API. An older binding installed in your ComfyUI environment produces an import or attribute error at save time, not at load time. pip install -U "OpenEXR>=3.3" in the ComfyUI venv.
    • A dark thumbnail. The preview is tonemapped with Reinhard at preview_exposure, and 0 EV on a scene-linear frame looks dim. Push it to −4 and it'll look like the demo. The file on disk is unaffected.
    • EXR that "looks wrong" in an 8-bit viewer. Most consumer image viewers apply a gamma curve they have no business applying to linear data. Check in something that knows what chromaticities are - Nuke, Blender's compositor, Affinity, oiio tooling - before you diagnose a colour problem.
    • Reading it back into ComfyUI as an 8-bit image. There are EXR-reading nodes out there (the OIIO-style packs, and the ones people reach for when a depth map needs more than 8 bits). If a loader hands you an 8-bit tensor, you've lost the HDR before a single node has run. The classic complaint in that corner is exactly this: someone's depth values come back quantised to 8 bits and they can't tell whether it was the model or the pipeline. Don't be that thread.
    • Not a DiffHDR-specific problem, but the pack's problem too: writing a 99-frame 720p float32 EXR sequence is real disk I/O. zip or dwaa is your friend, and dwaa at level 45 is visually free on this content.
    CategoryDiffHDR

    Inputs (9)

    NameTypeDefaultDescription
    imagesIMAGELinear Rec.709 HDR image or frame batch.
    filename_prefixSTRINGDiffHDR/hdrPrefix inside the ComfyUI output folder. Batches are written to <prefix>_<counter>/frame_####.exr.
    formatCOMBOexrexr: OpenEXR. hdr: Radiance RGBE (always linear Rec.709; bit depth, compression and colorspace are ignored).
    bit_depthCOMBOhalfhalf (16-bit float) or float (32-bit).
    compressionCOMBOzipEXR compression. dwaa/dwab are lossy; zip/piz are lossless.
    dwa_compression_levelFLOAT450–500DWAA/DWAB compression level (dwaCompressionLevel). Higher = smaller and lossier. 45 is the OpenEXR default.
    colorspaceCOMBOlinear_rec709Output primaries. Pixels are converted and chromaticities are written to the header.
    start_frameINT10–9999999Frame number of the first file in a sequence. Numbers are padded to at least four digits, widened for the whole sequence when the last frame needs more.
    preview_exposureFLOAT0.00-16–16Exposure in stops for the tonemapped UI preview.

    Outputs (0)

    No outputs