Nodes/noEmbryo nodes/H3 Clip Refiner /noEmbryo
ComfyUI Node

H3 Clip Refiner /noEmbryo

Your H3 clips get grainier every join — here's the fix that stays in latent space

By noembryo·Created 3 years ago·Updated 4 days ago· 46
H3 Clip Refiner /noEmbryo
  • latent
  • reference_latent
  • model
  • positive
  • negative
  • latent
  • report
◄strength1.00►
◄context_length22►
◄refine_denoise0.40►
◄refine_steps3►
◄refine_cfg1.0►
◄sampler_nameeuler►
◄schedulernormal►
◄seed0►
◄detail_strength1.00►

If you've chained more than three H3 Motion Context clips together, you've seen the texture ratchet. Clip one is clean. Clip four looks like it was shot on pushed film stock, and by clip seven the whole thing is alive with high-frequency grain that nobody asked for. It isn't your sampler, and it isn't random - it's cumulative, and it gets a little worse at every join.

This node is a one-scalar correction for exactly that.

What it's doing

The degradation has a statistic, and the node names it: band_ratio, the standard deviation of the high-frequency band divided by the total standard deviation. Split a latent's spatial content into a blurred low band and everything left over, and that leftover's share of the total sigma creeps up monotonically down a chain - the author's own measurements go 0.3643 → 0.3673 → 0.3702 across three joins. Roughly +4–5% of mid-band energy per clip. Small enough to ignore once, obvious by the sixth clip.

So the fix is: measure that ratio, and rescale the high band until it matches the clip you liked.

The band split is a difference of Gaussians - a separable blur over the latent's H/W gives you lo, with hi = t - lo. The gain is not naively target / current, because scaling the high band changes the total sigma it is a fraction of, so the target moves as you apply it; that undershoots by a few percent, the sort of correction that silently does most of its job and leaves you wondering. Instead the node solves the fixed point, then refines against the measured statistic over a few passes, with the gain clamped between 0.5 and 2.0. Two consequences worth knowing: the low band is untouched, bit-identical, so nothing gets blurred or smeared, and the correction can't invent detail because it's one scalar on one band. The worst it can do is get the gain wrong, which the report tells you.

strength then blends: exactly 0 is a no-op (measure only, even with a target set), below 1.0 gives partial correction, 1.0 is the full move, above 1.0 extrapolates past it. The audio stream is passed through untouched; only the video latent is rescaled.

The two-run workflow people fumble

  1. Measure. Leave target_ratio at 0.0 on your first clip. The latent passes straight through unchanged - measure mode is completely safe to leave in the graph - and the report output prints something like band_ratio=0.3643.
  2. Correct. For every later clip, type that number into target_ratio.

If you'd rather not copy numbers around, wire clip one's latent into reference_latent and the node measures the target for you. Cleanest setup there is, with one trap: when reference_latent is connected, target_ratio is ignored entirely. People leave both set, the widget looks authoritative, and the node quietly does something else.

To make a chain less grainy than the reference, aim lower than measured - the report's own example is measuring 0.3917 and targeting 0.3800. And read the report after a correction, not just before: it prints the gain it actually applied, and since that gain is clamped, an ambitious target can land short of where you pointed it. Also, the number in the report is averaged over three band scales while the correction targets the mid scale, so don't expect the next clip to read back precisely the value you typed. It's a guide, not an identity.

Inputs and outputs

latent takes the sampler's output - the AV latent from SamplerCustomAdvanced. If you're saving this clip into the archive, grab the sampler's denoised output rather than the raw one. target_ratio and strength are the two widgets above. reference_latent is optional.

Out comes latent (refined) and report (a string - wire it to a text display node, or just read the console, because the node also logs the measurement). Place the whole thing between the sampler and H3 Motion Context Save Latent. That's the only position that helps: fix the clip before it's written into the archive, because once clip four is saved with its grain baked in, later clips inherit it as context.

Install

ComfyUI Manager → search noEmbryo, or:

cd ComfyUI/custom_nodes
git clone https://github.com/noembryo/ComfyUI-noEmbryo

Restart afterwards. No pip dependencies - the pack declares none - and the nodes appear under Add Node → noEmbryo → MiniMax H3. ComfyUI's own H3 support and the H3 VAEs are assumed either way.

What goes wrong

  • H3ClipRefiner: latent has no audio stream. This node needs a full AV latent - video and audio, as a pair. A bare video latent from a plain VAE Encode won't do. In a normal H3 Motion Context graph you already have the right thing; it only bites when you slot the refiner into a different pipeline.
  • could not measure band_ratio (latent too small?). The Gaussian split needs at least 8 pixels of spatial extent in the latent. Tiny latents have nothing to measure.
  • Over-correction looks like plastic. Push target_ratio well below the measured value, or strength above ~1.5, and you're scrubbing the high band that carried skin texture and fine detail along with the grain. Turn it down until the joins stop jumping; the goal is continuity, not cleanliness.
CategorynoEmbryo/MiniMax H3

Inputs (14)

NameTypeDefaultDescription
latentLATENTThe generated AV latent from your H3 sampler (SamplerCustomAdvanced output).
strengthFLOAT1.000–3Correction strength. 0.0 = measure only (pass through). 1.0 = full match to reference statistics. >1.0 = overshoot (pushes past the reference).
context_lengthCOMBO22Number of frames at head/tail that are the Motion Context overlap regions. Must match the context_length in H3 Motion Context and Clip Stitcher.
reference_latentoptLATENTReference latent (e.g., first clip). When connected, all target statistics are measured from this latent and the input latent is corrected to match it.
modeloptMODELOptional. Connect your H3 model to enable the refine resample: a short low-denoise second sample that regenerates the micro-detail the chain loses. Leave disconnected for the pure statistical corrector.
positiveoptCONDITIONINGPositive conditioning for the refine resample (same prompt you sampled the clip with).
negativeoptCONDITIONINGNegative conditioning for the refine resample.
refine_denoiseoptFLOAT0.400–1Denoise for the refine resample. ~0.3-0.5 restores detail while keeping the structure (only the high band is transferred, so the background cannot shift). Higher changes content more.
refine_stepsoptINT31–50Sampler steps for the refine resample. 2-4 is plenty.
refine_cfgoptFLOAT1.00–100CFG for the refine resample. Match your main sampler.
sampler_nameoptCOMBOeulerSampler for the refine resample.
scheduleroptCOMBOnormalScheduler for the refine resample.
seedoptINT00–18446744073709550000Seed for the refine resample noise.
detail_strengthoptFLOAT1.000–1How much of the refined high band (detail) to transfer over the original. 0 = statistical only, 1 = full refined detail. The low band (structure) is always kept from the original, so the background never shifts.

Outputs (2)

NameTypeDescription
latentLATENT—
reportSTRING—