Nodes/ComfyUI CV/CV HDR Merge to Radiance
ComfyUI Node

CV HDR Merge to Radiance

Real linear light, not a displayable image

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
CV HDR Merge to Radiance
  • image
  • radiance
◄exposure_times0.033, 0.25, 2.5, 15.0►
◄methodDebevec►
◄samples70►

This is the other half of HDR, the part exposure fusion skips. It recovers the camera's response curve from your bracketed frames and their shutter times, then fuses a single linear-light radiance map - float values well past 1.0, a real measurement of how much light was there rather than a screen-ready picture. Then you decide how to show it.

It's Debevec and Robertson (the CalibrateDebevec/MergeDebevec and Robertson pair) wrapped as a node in bmad4ever's ComfyUI CV (bmad4ever/comfyui_cv, forked from Gerold Meisinger's opencv-comfyui), sitting between CV HDR Exposure Fusion (Mertens) and CV HDR Tonemap in the hdr family.

Inputs

image - the batch of registered exposures, and frame order matters from here on. exposure_times - a comma-separated string of shutter times in seconds, one per frame, in the same order you stacked them. The default 0.033, 0.25, 2.5, 15.0 is a plausible four-stop bracket. Only the ratios matter, so 1, 8, 64, 512 or 2^EV-style values work just as well as real seconds.

Get the count wrong and it raises, with a message that tells you how many values it got against how many frames - one of those small courtesies that saves ten minutes. Feed a zero or negative time and it objects too.

method - Debevec (default) or Robertson. Debevec is robust and the usual choice; Robertson can wring out a little more range but is noisier and less stable. Start with Debevec.

samples (70, advanced, Debevec only) - how many pixel locations are sampled to fit the response curve. More is a steadier fit and slower.

Output

radiance - an HxWx3 float32 BGR array of linear radiance. It is not an image. It has no valid 0–1 range and no gamma; dropping it into an IMAGE socket reference or a standard preview gets you either an error or a black frame. Two ways to look at it:

  • Preview CV Array in normalize or heatmap mode, to check it's sane.
  • CV HDR Tonemap, to actually turn it into a picture.

One implementation detail worth trusting: Robertson in particular can emit NaN or Inf on a degenerate bracket, and the node runs the map through nan_to_num, setting non-finite values to 0. Without that, a single NaN poisons every downstream tonemap and preview, and you'd be staring at a blank frame wondering which node died.

Why bother, versus Mertens

Mertens' fusion blends your input frames; it can't create light information that no frame captured. A radiance map is an estimate of the underlying linear scene, so:

  • You can expose it after the fact - tone it brighter or darker, in linear space, without re-shooting.
  • You get one consistent map to apply multiple tone curves to, and compare them fairly. That's what the pack's playground does: one Debevec merge, then Reinhard, Drago and Mantiuk tonemaps side by side from the same radiance.
  • You can grade it and hand it to a compositing pipeline in the same spirit as the scene-linear/EXR path the KB describes for VFX handoff. ComfyUI's normal tensors are 8-bit-ish sRGB; a radiance map is the closest thing in this pack to the linear half of that world.

The cost is that it's fussier: exposure times must be right, frames must be registered, and if your bracket is two stops wide you can't invent the range.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Or search ComfyUI CV in ComfyUI Manager. Python ≥ 3.12, V3-API ComfyUI. workflows/15_hdr_playground.json is the reference: four exposures, MTB alignment, a Debevec merge, and three tonemaps off it.

Common issues

Trying to preview it as an image. It'll be black or clipped. Radiance first, tonemap second.

"exposure_times has N values but the batch holds M". The message is literal: re-count your stack and your list. Also the most common cause of a silently bad merge is a correct count in the wrong order.

Huge, blown-out radiance values and a milky tonemap. The bracket is too sparse for the range in the scene, or a frame is over-exposed everywhere (a 15-second exposure of a bright room is just white). Check the individual frames first - the merge can only work with what's in them.

Unregistered frames. Nothing in this node compensates for camera movement. Put CV Align MTB or CV Align MTB To Reference in front of it; align before merging, always.

Categoryimage/CV/hdr

Inputs (4)

NameTypeDefaultDescription
imageIMAGEBatch of registered exposures of the same scene. Frame order MUST match exposure_times.
exposure_timesSTRING0.033, 0.25, 2.5, 15.0Comma-separated shutter time (seconds) of each frame, in the SAME order they were stacked. Only the ratios matter, so relative values (e.g. 2^EV) work too. Must be one value per exposure.
methodCOMBODebevecResponse-calibration + merge algorithm. Debevec is robust and the usual choice; Robertson can wring out a little more range but is noisier and less stable.
samplesoptINT7010–2000Debevec only: number of pixel locations sampled to fit the response curve. More = steadier fit, slower.

Outputs (1)

NameTypeDescription
radianceNPARRAYHxWx3 float32 linear-radiance map (BGR). Feed it to 'HDR Tonemap'; preview it with 'Preview CV Array'.