Nodes/comfyui-x2hdr/X2HDR Dynamic Range QA
ComfyUI Node

X2HDR Dynamic Range QA

Is your 'HDR' actually HDR? This node answers instead of guessing

By facok·Created 2 months ago·Updated about a month ago· 3
X2HDR Dynamic Range QA
  • hdr_image
  • qa_json
  • exposure_strip
sdr_reference1.00
headroom_threshold_stops3.0
dynamic_range_threshold_stops10.0
preview_methodaces
white_percentile99.5
gamma2.20
save_previewtrue
filename_prefixx2hdr_dr_qa

The most common failure in an X2HDR workflow isn't the decode - it's that the decode produced an SDR-range image wearing an HDR costume. A float tensor can hold values above 1.0 while all the actual energy sits in the SDR band, and then you save an EXR that's technically HDR and practically a flat PNG. X2HDR Dynamic Range QA catches that before you commit.

How it works

Per frame it computes luminance and RGB statistics - percentiles, max RGB, black fraction, how much sits above the SDR reference - and runs three checks:

  • Exceeds SDR: max_rgb or lum_p995 above sdr_reference (default 1.0).
  • Highlight headroom: log2(lum_p995 / lum_p50)headroom_threshold_stops (default 3).
  • Useful dynamic range: log2(lum_p995 / positive_lum_p01)dynamic_range_threshold_stops (default 10).

verdict = pass only if every frame passes all three. Otherwise it's review, with per-frame review_reasons naming the failed check. Pure-black pixels are reported separately as black_fraction instead of polluting the dynamic-range number - a nice touch, since a single dead pixel would otherwise tank the whole measurement.

The second half is visual: it builds a -4/-2/0/+2/+4 EV exposure preview strip so you can see recoverability. Lower exposure should reveal highlight detail; higher exposure should reveal shadow detail. The README's own framing says it best: bright numbers alone are not enough.

Inputs

  • hdr_image - decoded linear HDR; wire the hdr_image output of any X2HDR decode here.
  • sdr_reference (1.0), headroom_threshold_stops (3), dynamic_range_threshold_stops (10) - the pass/fail bar. Tune them if your pipeline's target differs, but the defaults are sensible for a ~10-stop HDR check.
  • preview_method (aces / reinhard / log), white_percentile, gamma - control how the strip looks, not the verdict.
  • save_preview (true) and filename_prefix - whether the strip gets saved as a temporary preview image in the UI.

Outputs

  • qa_json (STRING) - the full report; wire to a text-display node.
  • exposure_strip (IMAGE) - the five-EV strip; wire to a Preview Image.

Installing it

Part of facok/comfyui-x2hdr. ComfyUI Manager → search comfyui-x2hdr → install, restart, nodes under image/HDR/X2HDR. Or:

cd ComfyUI/custom_nodes
git clone https://github.com/facok/comfyui-x2hdr

then restart. The one extra dependency is PyAV (av), imported at load time; pip install av in ComfyUI's Python environment if the pack errors.

Where people get burned

  • It's the fastest wrong-decode detector in the pack. If QA says review because nothing exceeds the SDR reference, your model wasn't trained with the encoding you decoded - you've wired a PU21 decode to a LogC-trained model, or vice versa.
  • Pass means range, not beauty. A technically-HDR image can still look bad tone-mapped. QA answers "is the range there?", not "will this win a colorist award."
Categoryimage/HDR/X2HDR

Inputs (9)

NameTypeDefaultDescription
hdr_imageIMAGE
sdr_referenceFLOAT1.000.001–100000Reference SDR ceiling. A frame must have max_rgb or lum_p995 above this value to prove it exceeds SDR/VAE range.
headroom_threshold_stopsFLOAT3.00–32Minimum highlight headroom in stops: log2(lum_p995 / lum_p50). Higher means highlights carry more recoverable range above midtones.
dynamic_range_threshold_stopsFLOAT10.00–32Minimum useful dynamic range in stops: log2(lum_p995 / positive_lum_p01). Pure black pixels are reported separately as black_fraction.
preview_methodCOMBOacesTone mapper used for the exposure preview strip.
white_percentileFLOAT99.50–100Luminance percentile used as the preview white point when white_point is automatic.
gammaFLOAT2.200.1–8Display gamma for the LDR exposure preview strip.
save_previewBOOLEANtrueSave the -4/-2/0/+2/+4 EV strip as a temporary preview image in the node UI.
filename_prefixSTRINGx2hdr_dr_qaFilename prefix for saved temporary QA preview strips.

Outputs (2)

NameTypeDescription
qa_jsonSTRING
exposure_stripIMAGE