Nodes/ComfyUI-Majoor-ImageOps/〽️ Image Ops Chromatic Aberration
ComfyUI Node

〽️ Image Ops Chromatic Aberration

The two-pixel lie that makes a render look shot

By MajoorWaldi·Created 9 months ago·Updated 24 days ago· 14
〽️ Image Ops Chromatic Aberration
  • image
  • mask
  • image
  • mask
◄bypassfalse►
◄amount2.0►
◄center_x0.50►
◄center_y0.50►
◄invert_maskfalse►

Cheap lenses don't bend every wavelength to the same place, so red and blue land a hair apart - worst at the corners, invisible in the middle. It's a defect. It's also one of the four moves in the community's standing recipe for de-AI'ing a frame: grain, camera shake, red-channel fringing, vignette. post-processing.md collects that recipe and notes the mechanic: photorealism.md points out that models "rarely generate" this class of lens behaviour, so you add it.

ImageOps Chromatic Aberration does exactly that, and almost nothing else.

How it works

It's a radial channel shift, not a blur and not a per-channel scale. From nodes/chromatic_aberration.py, the red channel is resampled with a positive radial offset +k and blue with -k, while green is left completely untouched:

r  = _radial_sample(rgb[..., 0], xs_px, ys_px, cx_t, cy_t,  k, w, h)
g  = rgb[..., 1]
bl = _radial_sample(rgb[..., 2], xs_px, ys_px, cx_t, cy_t, -k, w, h)

That _radial_sample is the important detail: the shift scales with distance from the centre, so the middle of the frame stays perfectly clean and the fringing grows as you move outward. That's how the real optical defect behaves, and it's why this reads as a lens rather than as a glitch filter. Green staying put is also correct-ish for a simple achromat - you get magenta/cyan fringes rather than the rainbow stripes of a broken CRT.

The knobs that matter

  • amount (default 2.0, −30 → 30): the red/blue shift in pixels at the frame corner. This is the only setting most people touch.
  • center_x / center_y (default 0.5, 0.5, −1 → 2): where the aberration originates. 0.5/0.5 is frame centre, which is right for a full-frame shot. If you're applying this to a crop, put the centre where the optical centre of the original framing would be, or the fringing will be lopsided.
  • invert_mask and bypass - bypass returns the input unchanged, handy for a before/after toggle.

Inputs: image and an optional mask; outputs: image and mask. Feed the image output to Preview, Save Image, or the next node in your grade.

Opinion, because you'll ask: 2 pixels is already at the edge of what looks like a real lens. Somewhere past 5 it stops being a defect and becomes an effect - which is a legitimate thing to want, but know that you're crossing that line. Going negative flips which channel moves outward. And at amount 0 the node is a passthrough, so it's harmless to leave in the graph.

Installing it

Same pack, same steps:

cd ComfyUI/custom_nodes
git clone https://github.com/MajoorWaldi/ComfyUI-Majoor-ImageOps

Restart ComfyUI, then hard-refresh the browser (Ctrl+F5 / Cmd+Shift+R) - the pack registers frontend JS for its live preview and a cached bundle will make you think the install failed. ComfyUI Manager users can search ComfyUI-Majoor-ImageOps instead. No model files, no extra dependencies; the hard requirement is ComfyUI 0.19.0+, since the pack uses the V3 node schema and won't load on an older build.

Where people get burned

  • It looks terrible and you can't say why. Usually it's amount too high plus a subject near the corner of the frame. Real CA is subtle and mostly invisible on a busy background. Try 1–3 on a landscape before you judge it.
  • "It's not doing anything." Check that your image has colour in it - CA needs a red or blue edge to displace. Fringing on a grey wall is genuinely invisible.
  • It's a post-process, not a fix. This adds aberration. If your render already has smeared channels from a bad VAE pass or a naive upscale, stacking fake CA on top just makes it worse; run it last, after sharpening and upscaling.
  • On a long video batch it's worth knowing that this node (like Bloom, Vignette, Clamp, Transform and Corner Pin) can be pushed to CPU with IMAGEOPS_COMPUTE_DEVICE=cpu, which trades speed for not fighting the diffusion model for VRAM.
Categoryimage/imageops

Inputs (7)

NameTypeDefaultDescription
bypassBOOLEANfalse—
amountFLOAT2.0-30–30Radial channel shift in pixels at the frame corner. Positive shifts red outward and blue inward.
center_xFLOAT0.50-1–2—
center_yFLOAT0.50-1–2—
invert_maskBOOLEANfalse—
imageoptIMAGE,VIDEOImages/Video input.
maskoptMASK—

Outputs (2)

NameTypeDescription
imageIMAGE—
maskMASK—