MAGICMATCH
ComfyUI custom nodes for neural color match: two images in, graded source out.
MAGICMATCH
ComfyUI custom nodes for neural color match: two images in, graded source out. Tune strength on a preview node before you wire the result to save/export.
Install
-
Clone into ComfyUI
custom_nodes:cd ComfyUI/custom_nodes git clone https://github.com/lookuters22/MAGICMATCH.git -
Install Python deps (ComfyUI’s environment):
pip install -r MAGICMATCH/requirements.txt -
Model files are included in the repo (pull gets everything):
MAGICMATCH/models/color_match.onnx MAGICMATCH/models/face/face_detect_landscape.onnx MAGICMATCH/models/face/face_detect_portrait.onnx MAGICMATCH/models/face/face_parse.onnxTo regenerate locally (optional), use Python 3.12 +
scripts/convert_face_models_to_onnx.pyand the color-match convert script underpolarrnext/color_match_extract/. -
Restart ComfyUI. Nodes appear under MAGICMATCH.
Workflow (strength before export)
Use Build + Preview so changing the slider only re-applies the LUT (fast):
[Source] ──┬──► MagicMatch Build LUT ◄── [Reference]
│ │
│ ▼ lut
└──────► MagicMatch Preview (strength) ──► Preview Image
│
└──► Save / export when ready
| Step | Node | |------|------| | 1 | MagicMatch Build LUT — run once per source/reference pair | | 2 | MagicMatch Preview (strength) — run once, then use the live preview inside the node and drag strength (no re-queue) | | 3 | When happy with the slider, Queue again and connect output to Save / export |
In-node live preview
- Run the workflow once (Build + Preview execute; LUT is cached).
- Open the MagicMatch Preview node — you’ll see a live image under the widgets.
- Drag strength — preview updates instantly in the node (WebGL, no workflow re-run).
- When the look is right, Queue Prompt once more so the image output matches the slider, then save/export.
Live preview uses WebGL (merged 25³ LUT + strength mix). Mid-strength may differ slightly from the queued image output, which uses the full CPU path — queue once more before export.
MagicMatch (one-shot) combines build + apply in one node (simpler, slower when you change strength).
Requirements
- ComfyUI with standard
IMAGEtensors (batch size 1) onnxruntime,numpy(seerequirements.txt)- Face auto-WB/light uses ONNX face models under
models/face/(see install step 3) - CPU inference by default (~5–15s for Build on first run per pair)
CUDA inference (experimental)
A separate GPU path lives alongside the CPU parity stack — default nodes and
scripts/parity_pair.py golden checks are unchanged.
Install (H100 / Linux)
# In ComfyUI's venv — onnxruntime and onnxruntime-gpu are mutually exclusive on many builds
pip uninstall -y onnxruntime
pip install -r MAGICMATCH/requirements-cuda.txt
Requires NVIDIA driver + CUDA libs compatible with your onnxruntime-gpu wheel.
ComfyUI already provides PyTorch with CUDA — the GPU pipeline uses Torch for develop,
detection buffers, and LUT apply (ONNX uses CUDA EP).
On machines without CUDA, CUDA nodes fall back to CPU EP + CPU Torch automatically.
ComfyUI nodes
After restart, look under MAGICMATCH/CUDA (experimental):
| Class | Display name |
|-------|----------------|
| MagicMatchBuildCUDA | MagicMatch Build LUT (CUDA) |
| MagicMatchPreviewCUDA | MagicMatch Preview (CUDA LUT) |
| MagicMatchCUDA | MagicMatch one-shot (CUDA) |
GPU pipeline (CUDA nodes): CUDA ONNX + GPU detection buffers + GPU develop@1600 + GPU full-res apply.
GPU Full pipeline (experimental)
A complete hot-path port keeps detection buffers, luminance stats, face ONNX preprocess,
develop@1600, full-res LUT apply, and net reference resize on GPU/Torch. Minimal CPU sync:
JPEG q98 worker normalize, detection-sized downloads for face/color/WB parity helpers,
256×256 reference JPEG/WebP round-trips, and single .cpu().numpy() feeds for ONNX.
ComfyUI category: MAGICMATCH/GPU Full (experimental)
| Class | Display name |
|-------|----------------|
| MagicMatchBuildGPUFull | MagicMatch Build LUT (GPU Full) |
| MagicMatchPreviewGPUFull | MagicMatch Preview (GPU Full LUT) |
| MagicMatchGPUFull | MagicMatch one-shot (GPU Full) |
Set MAGICMATCH_GPU_FULL_NODES=0 to hide GPU Full nodes.
CPU default nodes under MAGICMATCH are unchanged.
Set MAGICMATCH_CUDA_NODES=0 before starting ComfyUI to hide CUDA nodes.
Use python3.12 on RunPod if python is not on PATH.
Benchmark / parity
# CPU golden (must stay parity_ok)
python scripts/parity_pair.py \
../polarrnext/standalone_probe/public/pair/source.png \
../polarrnext/standalone_probe/public/pair/reference.jpg
# CPU vs CUDA timings + lut_hash compare
python scripts/bench_cuda_vs_cpu.py \
../polarrnext/standalone_probe/public/pair/source.png \
../polarrnext/standalone_probe/public/pair/reference.jpg
# Full GPU pipeline phase breakdown + parity report
python scripts/bench_gpu_full.py \
../polarrnext/standalone_probe/public/pair/source.png \
../polarrnext/standalone_probe/public/pair/reference.jpg
# Per-phase GPU timings only (via bench_cuda_vs_cpu)
python scripts/bench_cuda_vs_cpu.py --profile-gpu \
../polarrnext/standalone_probe/public/pair/source.png \
../polarrnext/standalone_probe/public/pair/reference.jpg
Expected golden lut_hash on the polarrnext/pair test set: a48758ca22a2e389.
CUDA may differ slightly if GPU face-detect scores diverge from CPU f16 quirks; compare
lut_max_abs_delta in the bench report.
Nodes (CPU default)
| Class | Display name |
|-------|----------------|
| MagicMatchBuild | MagicMatch Build LUT |
| MagicMatchPreview | MagicMatch Preview (strength) |
| MagicMatch | MagicMatch (one-shot) |
License
See LICENSE. The bundled color_match.onnx is part of this package; use responsibly and in compliance with applicable terms for any upstream model you obtained.