ECHO Outdoor Match / 回响·户外追色(待复核)
For when your background average can't be trusted
- source
- reference
- source_background_mask
- reference_background_mask
- paired_surfaces
- protect_mask
- corrected
- review_report
The problem it was built for
Color-matching to a reference is boring, deterministic, and everywhere in real workflows - and it's the right layer to fix "the colors are off," not another diffusion pass with its seed lottery and face-rewriting. ECHO ships a studio-oriented ECHO Reference Match that does exactly that: background gets its own transform, the person (skin, hair, clothes) gets one smooth transform, protect masks keep logos and packaging byte-identical.
Then you shoot outdoors and the logic quietly breaks. The studio path reads the background's overall statistics. Swap a mostly-sky frame for a mostly-ground frame and those statistics move even if nothing about the lighting or the subject changed. The pack's own controlled counterexample is refreshingly concrete: hold the object colors and illumination fixed, change only how much of the frame is sky, and the old path drags a person from RGB 128,100,80 to 139,100,75. That's a demonstration of a class of error, not a failure rate - and it's why ECHO Outdoor Match exists as an opt-in route instead of a bigger strength knob.
Note the display name: 回响·户外追色(待复核). Review pending, and the report is honest about it - accepted is never true.
How it works
You feed it explicit evidence instead of a frame average (that's ECHO Paired Surface's job) plus two background masks. Each pair of samples becomes an anchor. ECHO converts to linear sRGB, takes luminance percentiles across each sample, and computes per-anchor exposure as a log2 ratio - display-relative stops, not camera EV or Kelvin - plus color as log R/G and B/G shifts. Anchors landing at nearly the same source brightness get merged, with equal weight per anchor rather than per pixel area, so a small dark sample isn't drowned by a huge bright one.
Then it fits a single monotone brightness curve per region, pinned at black and white, hard-limited to ±2 stops of exposure and ±1 stop of channel ratio, with slope limits so it can't crush or blow out detail. Color gain is re-normalized against luminance so white balance can't double-apply exposure. If two samples demand contradictory targets at the same brightness - the classic mixed-light case - the affected region is left exactly as the source and reported limited. No silent fallback to the studio algorithm. Region transforms are blended through soft mask edges, and out-of-gamut results are pulled toward neutral at constant luminance, which means yes, it can desaturate slightly. It's a bounded simplification, not CSS Color 4 perceptual mapping.
Inputs, outputs, wiring
Required: source, reference, source_background_mask and reference_background_mask (white = background), paired_surfaces (your chain from ECHO Paired Surface), and strength - default 0.85, where 0 is a byte-for-byte identity. Optional: protect_mask, white pixels stay pixel-identical, and protected pixels also carry no authority over the fitted transform.
The white background mask defines the background; the person is its complement, with no studio-style fallback. A sloppy person mask therefore isn't a small error, it's the wrong edit region.
ground sample pair ─→ Paired Surface ─┐
stone sample pair ──→ Paired Surface ─┤
garment sample pair → Paired Surface ─┴→ Outdoor Match → Save Image
source / reference / background masks ─┘ └→ review_report (STRING)
corrected is an IMAGE - goes to Save Image or a preview. review_report is a JSON STRING; wire it into a text display node (rgthree's Display Any is the usual) or read it from the console. It carries per-anchor usable pixel counts, sampled luminance ranges, source/reference hashes, gamut compression fractions and a review_reasons list.
One structural rule: don't chain a background match and then a separate person match. Hand every surface pair to a single Outdoor Match so it renders once, from the original.
Install
Same pack, no extra step. Install ECHO (ECHO · 回响, publisher sysyou) via ComfyUI Manager, or:
cd ComfyUI/custom_nodes
git clone https://github.com/x9c4gqtpv4-coder/ECHO.git
python -m pip install -r ECHO/requirements.txt # ComfyUI's own python
Restart, then look for the node under ECHO / 回响. Dependencies are numpy and Pillow; the outdoor engine is pure NumPy/Pillow and CPU-only, with no model downloads and no cloud call. Same caveat as the rest of the pack - clone it if Manager comes up empty.
Where people get burned
Connect ECHO Paired Surface evidence means your surface chain isn't actually wired into paired_surfaces, or something other than the ECHO node produced it. The pack refuses forged objects rather than coercing them.
If the report comes back applied: false / quality_status: limited, read review_reasons instead of re-queueing. CONFLICTING_SURFACES_OR_MIXED_LIGHT_AT_SAME_BRIGHTNESS means your samples disagree - usually one sample straddling two illuminants, like a wall half in tree shade, so split it into separate named pairs. EXPOSURE_EXCEEDS_TWO_STOP_MODEL_LIMIT means the reference is simply a couple of stops away and ECHO won't force it. MOST_EDIT_PIXELS_OUTSIDE_SAMPLED_TONE_RANGE is the one that catches people sampling a dark garment and expecting white lace and skin highlights to follow - dark clothing teaches you nothing about the top end. And SOURCE_HIGHLIGHT_DETAIL_MAY_ALREADY_BE_LOST means your source has pixels already clipped to white - no color match recovers texture that isn't in the file.
Other real ones: both background masks are required and must match their image's dimensions (no silent resizing, by design); the reference batch must be 1 or match the source batch; and there's a 32-megapixel admission limit with a working-memory estimate that will refuse rather than quietly downscale. Output is 8-bit sRGB, so treat it as a review candidate, not a zero-difference guarantee.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| source | IMAGE | — | |
| reference | IMAGE | — | |
| source_background_mask | MASK | — | |
| reference_background_mask | MASK | — | |
| paired_surfaces | ECHO_SURFACES | — | |
| strength | FLOAT | 0.850–1 | — |
| protect_maskopt | MASK | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| corrected | IMAGE | — |
| review_report | STRING | — |