OCIO Exposure
The node that won't clamp your HDR headroom
- image
- video
- image/sequence/video
- ComfyUI Video
A one-line node with a very specific reason to exist
OCIO Exposure brightens or darkens an image in stops. That's the whole job, and on the surface it's the most boring node in SlavaSexton's ComfyUI-OCIO pack. The reason it exists at all is the trap that makes boring worth it: almost every "brightness" or "exposure" node elsewhere in ComfyUI clamps to 0..1 by default, and a clamp quietly destroys exactly the values this pack exists to carry.
ComfyUI has no color management. It holds images as gamma-encoded sRGB in a 0..1 range, and a stock VAE decode even clamps to that range before anything else sees the tensor. If you're a solo generator making finished images, that's correct and this node is overkill. But the moment AI output has to survive as an element in a color-managed shot - an HDR pass, a float32 EXR, an ACES pipeline - you're working with values above 1.0 and below 0.0, and the moment you reach for a normal brighten node, they're gone. This one doesn't touch them.
How it works (and why "just a multiply" is the point)
out = in * 2**exposure. That's the whole mechanism: it computes gain = 2.0 ** exposure and multiplies the RGB channels by it, in float32, leaving alpha alone. A multiply preserves sign, so a negative stays negative and scales along with everything else - the pack's own test holds the node to it: 12.0 at +1 stop must come out 24.0, and −0.25 must come out −0.5. A clamped node fails both.
That also makes it the pack's rare dependency-free color node. No OCIO config is read and OpenColorIO isn't even needed - a multiply is a multiply. It's still linear gain, though, so placement matters: put it before a display transform, never after. Applied to display-referred codes it's a gamma-space stretch masquerading as an exposure, and the result looks burnt or crushed in a way no number fixes.
The inputs and outputs that matter
Only two widgets, and honestly only one you'll set:
exposure- stops, from −20 to +20 in 0.01 steps, default 0.0. That default is a pass-through; at 0.0 the node does nothing.mix- 0 to 1, default 1.0, blends the result with the original. 0.0 is a bit-exact bypass.
Inputs are image and video, mutually exclusive - wire one and the other disconnects, and a VIDEO in gives you a VIDEO out. Outputs are the same pair: image/sequence/video (IMAGE) and ComfyUI Video (VIDEO). Everything flows on plain ComfyUI types, so it drops into any graph.
The case it was added for
An SDR-to-HDR reconstruction pass re-renders into its own tonal distribution, so it comes back darker than the plate it came from. Measured across six shots, the lift ran 0.55 to 1.58 stops - one number per shot, and a number from one shot is wrong on the next. Find it as log2(plate median / pass median) and set it here. And leave it at 0.0 in a master: exposure belongs in the grade, not baked into a scene-linear deliverable other people will match to.
Installing it
Via ComfyUI Manager, search ComfyUI-OCIO. In the version dialog, take the highest numbered version or Nightly - one entry labeled Latest points at deprecated 1.2.6, and 1.2.3 / 1.2.4 / 1.3.0 are outright banned for a path-traversal fix. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/SlavaSexton/ComfyUI-OCIO
pip install -r ComfyUI-OCIO/requirements.txt
Restart and the nodes appear under the OCIO category. The requirements pull in OpenColorIO, OpenCV, OpenEXR, tifffile, Pillow and numpy, and the pack needs ffmpeg on your PATH only if you use the Read/Write video containers. OCIO Exposure itself needs none of it - it'll run even if OpenColorIO never imports. If a render "succeeds" into an empty folder, it's ComfyUI's partial_execution_targets dropping output nodes, not this node - queue from the graph. And when the exposure "does nothing," check that exposure isn't at 0.0 and mix isn't at 0.0 before you suspect the node.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| exposure | FLOAT | 0.00-20–20 | Stops. out = in * 2**exposure. 0.0 is a pass-through. Nothing is clamped: highlights above 1.0 and negatives both survive. |
| mix | FLOAT | 1.000–1 | Blend with the original: 1.0 = full effect, 0.0 = bypass. |
| imageopt | IMAGE | — | |
| videoopt | VIDEO | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image/sequence/video | IMAGE | Image scaled by 2**exposure. Nothing is clamped at either end. |
| ComfyUI Video | VIDEO | — |