cv2.intensity_transform.logTransform
One node, no settings, and a very strong opinion about shadows
- input
- result
Most tonal nodes in this pack ask you for a number. This one doesn't ask for anything. logTransform applies a logarithmic curve - dark values get pushed apart, highlights get squeezed together - and there is nothing to configure.
That makes it either perfect or useless, depending on your frame, and you'll know which within one run. It's a wrapper from bmad4ever/comfyui_cv, the pack that exposes OpenCV functions as ComfyUI nodes.
What it does
A log curve is the extreme version of "lift the shadows". Detail that was crushed into the bottom 20 levels of an underexposed frame gets a large share of the output range; everything in the highlights gets compressed into a narrow band. The author's tooltip calls it exactly that: it "strongly expands dark values and compresses highlights - it has NO parameters, so it is an all-or-nothing shadow lift."
There's no intensity dial, no midpoint, no amount. Compare that with cv2_intensity_transform.gammaCorrection, which is the same shape of operation with a control: gamma below 1 lifts midtones proportionally. Log is stronger and fixed. If one run of log is too much, you don't lower a number - you reach for gamma instead.
Inputs and outputs
input takes a ComfyUI IMAGE, a MASK or an NPARRAY, and the tooltip is specific that it wants 8-bit - like its siblings in this module, the transform is carried out through a lookup table. Float input is rejected; CV Cast Array in front of it is the fix.
result echoes the input format, so an IMAGE comes back as an IMAGE. That means it drops inline between a Load Image and a Save Image with no conversion node on either side, and it's batch-aware: an IMAGE batch gets looped frame by frame by the pack's low-level machinery.
One mechanism note that explains the shape of the node: the whole intensity_transform module returns results by writing into a buffer the caller pre-allocates, rather than returning an image. That's why there's no dst input here - the wrapper allocates it from your input and hands the result back.
When to reach for it
Underexposed captures, night footage, a render that came back murky, a scanned document with faded ink, or an X-ray-ish source where the interesting information lives at the dark end. Also worth trying on a mask or a score map before thresholding, since log compression makes a broad, low-contrast set of values separate better.
What it won't do: fix a well-exposed but flat-looking image (you want gamma or a sigmoid curve for that - brightness additives are the wrong family entirely), and it won't preserve your highlights, because compressing them is the whole point of the curve.
Installing the pack
Manager → search comfyui_cv, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Python ≥ 3.12 and a recent ComfyUI (V3 node API). Behaviour is curated against OpenCV 5.0.0.93. It's a contrib module, so the wheel matters: install the contrib build. Installing a plain opencv-python over a contrib one silently empties the contrib submodules from the shared site-packages/cv2, and this node - along with the rest of intensity_transform, ximgproc, img_hash and friends - just isn't there anymore. No error, no warning. tools/repair_opencv_contrib.py --check is the diagnostic; --apply repairs.
Traps
Float input. The LUT implementation means 8-bit only. It's the error you'll hit first if you're working in NPARRAY space.
Using it as a general brightener. It's all-or-nothing and it will happily turn a slightly dark image into grey milk. If the frame is close, use gamma; log is for when the shadows are genuinely destroyed.
Stacking it. Log after log after log crushes the highlights to a flat band very quickly. Compose with intent.
Expecting it to save clipped detail. If your capture blew the highlights to pure 255, there is no information left to compress. Log can only redistribute what's there - this is the same reason an overexposed RAW is unrecoverable.
And the standing caveat for this pack: the README says it was written with heavy LLM assistance, that some behaviour may be overfitted to its own tests, and that production use needs independent review. For a parameterless one-line cv2 call, the thing to sanity-check is simply that the output looks like a log curve and not a bug.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| input | COMFY_MATCHTYPE_V3 | The image output(s) echo this input's format. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| result | COMFY_MATCHTYPE_V3 | Echoes the 'input' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY. |