Mpi Grade Match
The pale ghost at the edge of your re-rendered crop, and how to kill it
- destination
- source
- mask
- image
You re-rendered a box of a clip with a video model, the content lines up, and you paste it back - and there it is: a faint, lighter rectangle sitting inside the original frame. A model re-rendering a crop almost never lands on the exact same grade as the source. The crop comes back a shade brighter, or a touch warmer, and a hard-edged paste turns that into a visible patch.
MpiGradeMatch is the node that fixes it: composite the re-render through your mask, but fit a grade to the original first, on the pixels outside the mask where both images show the same thing.
Why the usual color-match suspects don't work here
The KB's standing answer for "this region doesn't match its surroundings" is a color match - Color Match Image, or KJNodes' ColorMatch - and for a pasted subject that's correct advice. Neither one knows about your mask. They move the whole frame's palette toward a reference's statistics, so if you feed them an inpaint composite they'll drag your new content toward the plate too.
The same pack's MpiInpaintHeal has the opposite problem: it shifts a fill's mean toward its surroundings, which is right for a repaired region and wrong for an added object. The author's own example is red horns composited into a scene - heal would tint them toward the wall behind.
MpiGradeMatch only works when destination and source really are the same shot, and it uses that: outside the mask, the difference between them is grading drift and nothing else.
How it works
Per frame, it fits a per-channel gain and offset (o ≈ a·g + b, three gains and three offsets) by least squares over the paired pixels outside the mask, then applies that to the source and composites destination·(1−w) + source·w.
The band widget is what makes it usable on real footage. Instead of fitting on everything outside the mask, it fits on a ring that many pixels wide hugging the mask - the pixels nearest the seam, which is where the mismatch actually shows. Then it refits three times, each pass dropping pixels whose residual is past 2.5 standard deviations in any channel. That matters because content moves through the ring: a hand crossing the box edge is not grading drift, and one pass left a 6% patch of motion skewing the line. Gains are clamped to 0.2–5, so a degenerate fit can't output a hallucinated curve.
band 0 means "fit on all of outside", which is the more robust choice when the shot's exterior is static or your mask is large.
Inputs and outputs
- destination - the original footage. Kept outside the mask, and the grade reference.
- source - the re-render of the same shot. Kept inside the mask, after grading.
- mask - where the source is kept. A batch of 1 holds for every frame, which is the common case for a box that stays put.
- band - default 48. The width of the fit ring.
One output: image, the composite. Wire it to a save/preview, or onward if another pass follows.
Installing it
Same as the rest of the pack - ComfyUI Manager, search the pack title, or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/MadPonyInteractive/ComfyUi-MpiNodes
# restart ComfyUI
No models, no pip steps: this node is pure torch with no ComfyUI import at all, which is also why the pack can unit-test it without a running engine. The pack itself ships no requirements.txt and declares no pip dependencies - it leans on what ComfyUI already has.
Where people get burned
Frame counts have to agree. destination, source and mask can each be a batch of 1 or a batch of N; 4 / 8 / 1 raises with all three counts named, rather than quietly grading and pasting the wrong frames. If your re-render came back a different length, fix that upstream.
A tighter band can silently do nothing. If the ring holds fewer than 16 usable pixels, the frame is passed through ungraded - no warning. On a huge mask with a band of 8, you can end up with a no-op and assume the node is broken.
Different sizes get resampled, bilinearly. A source or mask at another resolution is interpolated to the destination's size with bilinear filtering, so a hard-edged mask comes back slightly soft. Resize it yourself if the edge has to be exact.
And the obvious one: if the model invented something outside the mask rather than re-rendering the same shot, there's no paired content to fit on and the grade it invents will be nonsense. This node assumes a re-render, not a new image.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| destination | IMAGE | The original footage: kept outside the mask, and the grade reference. | |
| source | IMAGE | The re-render of the SAME shot: kept inside the mask, after grading. | |
| mask | MASK | Where the source is kept. A batch of 1 holds for every frame. | |
| band | INT | 480–1024 | Width of the ring round the mask the grade is fitted on. 0 fits on all of outside. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |