Mi-Ripple Local Repair
Mi-Ripple Local Repair hands your image back untouched — and that's the design
- image
- image
- candidate
- change_mask
- restoration_applied
- outcome
- report
Queue this node and get a pixel-identical image back? Nothing has gone wrong - that's the most common correct outcome. It runs a deterministic, CPU-only diagnose → treat → verify pipeline on one image, and only lets a changed image out of the image output when the official verifier signed off. Everything else comes back as your input, with a report saying why. The opposite of a black box.
What it's for, in a real workflow
Two artifact classes specifically: an isolated periodic lattice (that faint regular grid over otherwise-clean areas) and granular noise in flat regions. It's not a denoiser, not a general cleanup pass, and not a fix for the tiled-upscaler grid you get running an FP8 model - that grid comes from the precision, so the fix is different precision, not a filter.
In the graph it goes before your upscale, because upscaling bakes defects in - except the verified scope is 512×512, so the honest pipeline is downscale → repair → upscale, which only works if the artifact survives the downscale. On a 4K image with a half-pixel lattice, it won't.
How it works under the hood
The wrapper writes your image to a temp 8-bit PNG and calls MIYANG's file-based pipeline: a small rules engine that diagnoses, treats, verifies, and rolls back when verification fails. Two treatments show up. iso_clean reduces the 3–8 px band only where a structure mask permits, so edges and coherent detail stay protected. notch FFTs the lightness channel, finds small isolated peaks above a local median baseline outside a 24 px DC radius, and attenuates just those.
When the diagnosis says the texture is content-entangled, the rules want to regenerate from a cleaned reference - and this build refuses. Regeneration is disabled by an authorizer that always says no, and MIYANG_* variables are ignored, so you get needs_human_decision and your input back. No key, no network, ever.
The inputs that matter
image is one RGB image, batch 1. Batches and alpha are refused with a message telling you to split first, which beats silently dropping frames.
size_policy defaults to 512x512 only and refuses a 640×480 input with the size named; nothing is resized or cropped. any size (unverified) runs the official code unchanged elsewhere - a path explicitly not compared against the reference.
accept_verify_failure is the interesting switch. Off, a failed-verify candidate is discarded and you get your input. On, you get the candidate anyway. An inspection hatch, not a shipping setting: in the author's tests the notch candidate visibly chewed the mortar lines on a brick-wall photo, the verifier rejected it, and the input came back - flip this and you can see the damage.
keep_records dumps the official JSON sidecars, boards and XML trace to output/mi-ripple/<run id>/.
Outputs, and what to wire where
image is the safe one - candidate when restoration was applied, your input otherwise. Wire it to Save Image and stop thinking.
candidate is the official final image whenever one was written, including after a failed verification. Preview it beside the input to judge whether the verifier's taste matches yours. change_mask is max over RGB of |candidate − input| divided by eight 8-bit levels, clipped 0..1 - so eight levels of change in any channel pins it at 1.0. A "did it touch this" map, not a delicate difference image. restoration_applied is a BOOLEAN you can gate a branch on, outcome is the official string verbatim, and report is the JSON: steps, verify result, diagnosis summary, face-detection status, versions, upstream commit.
Installing
ComfyUI Manager, search Mi-Ripple Local (registry name mi-ripple), or:
cd ComfyUI/custom_nodes
git clone https://github.com/hiroki-abe-58/ComfyUI-MiRipple
pip install -r ComfyUI-MiRipple/requirements.txt
scikit-image is the dependency actually exercised; httpx is there for upstream's regeneration client. No weights, no downloads, CPU only - 0.7–1.7 s for a 512×512 image on the author's machine. Needs Python 3.11+ and a current ComfyUI.
When it looks broken
Check outcome, not the picture. delivered with final_is_source_copy means it ran and found nothing it would change. delivered_with_verify_failure means it treated something and the verifier threw it out. needs_human_decision means the rules wanted regeneration, which this build never does. In the README's 19-case table exactly one input came back changed - upstream's own 384×512 test image. That base rate is the point, not a defect.
The node isn't loaded at all. The startup console shows [Mi-Ripple] not loaded: when scikit-image is missing or your ComfyUI predates the V3 API; the pack logs and registers nothing rather than crashing.
OpenCV is optional and only affects face protection. A cv2 that imports but won't work used to fail the whole upstream run; this pack makes it best-effort and says so in the report (cv2_unavailable, cv2_error:<type>).
The verdict
Reach for it when you can actually see a lattice or flat-region grain, at or near 512, and you'd rather not damage the image than get a visibly different one out. Skip it if you want a cleaner that fixes whatever's wrong - it'll hand the picture back and tell you why.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | One RGB image (batch 1). Quantised to 8 bits like the official file input. | |
| size_policy | COMBO | 512x512 only | '512x512 only' (verified scope) refuses other sizes. 'any size (unverified)' runs the official code on any size without resizing or cropping; results there were not compared with the official code. |
| accept_verify_failure | BOOLEAN | false | Return the candidate even when the official verification failed (outcome delivered_with_verify_failure). Off: the input is returned unchanged. |
| keep_records | BOOLEAN | false | Copy the official records (JSON sidecars, diagnosis / verify boards, XML trace) to output/mi-ripple/<run id>/ for inspection |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | The candidate when restoration_applied, otherwise the input unchanged |
| candidate | IMAGE | The official final image when one was written (also after a failed verification), otherwise the input unchanged. For inspection. |
| change_mask | MASK | max over RGB of |candidate - input| / 8 levels, clipped to 0..1 |
| restoration_applied | BOOLEAN | — |
| outcome | STRING | Official outcome: delivered, delivered_with_verify_failure, needs_human_decision, failed or step_limit (cancelled raises an interrupt) |
| report | STRING | — |