Lazy Image Select (only runs the chosen branch)
The switch that doesn't pay for the branch it skips
- on_true
- on_false
- IMAGE
The whole point of this pack, in one 30-line node
Most "if/then" nodes in ComfyUI are switches over values that have already been computed. You build a graph where a 40-second SUPIR pass and a 6-second SeedVR2 pass both run, and then you pick one of the results. On a batch of 500 images that's hours of work thrown away, and it's why curated pipelines end up as several separate workflows.
Lazy Image Select is different, and the difference is the entire premise of this pack. It takes a boolean and two images, returns one of them, and only executes the branch it picks. The other branch's upstream chain - the model, the sampler, the VAE decode - never runs.
How it works
Two lazy inputs (on_true and on_false) plus a check_lazy_status method that returns the socket name the condition asks for. ComfyUI's executor sees that the node asks for exactly one input and resolves only that one. That's the mechanism - no node bypassing, no dynamic graph rewriting, just the lazy-input feature the engine already has.
Which brings the one real trap, straight from the author's README: don't run this with --cache-none. The lazy-input machinery needs a real output cache to know what has already been executed. With the cache disabled, the executor can't tell, and it re-runs the whole upstream chain of your chosen branch every single time it's requested. You don't get a wrong image; you get a pipeline that's dramatically slower than the graph looks.
The output is a single unnamed IMAGE output. Required inputs are condition (a boolean, forced to be an input so it must be wired), on_true and on_false. No thresholds, no settings, nothing to tune.
What you wire into it
Anything whose result is expensive and conditional. In this pack it turns every measurement into a cheap branch:
PortraitQualityScore.needs_refine→ select the super-res result or the originalImageChromaCheck.is_monochrome→ select the colourised crop or the crop as it wasSkinTextureCheck.is_low→ select the SUPIR restoration or the untouched cropFaceIdentityGuard.passed→ arguably you don't need this one, since the guard already does the substitution internally
The pack's example workflow (PORTRAIT_DATASET_GATE_v1.json) uses it exactly this way, three times: after SR pass 1, after SR pass 2, after colourisation, and after SUPIR. That's the pattern worth copying even if you never touch the rest of the pack: measure, then branch lazily.
Install
It comes with the pack. ComfyUI Manager: search Portrait Dataset Tools. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/BillyBGit/ComfyUI-PortraitDatasetTools
pip install -r ComfyUI-PortraitDatasetTools/requirements.txt
The pack's dependencies (insightface, opencv-python, numpy, Pillow, onnxruntime) are for the measuring nodes. This one is pure plumbing and works even if the rest of the pack's model isn't downloaded yet.
Where people get burned
--cache-none is the big one, covered above. If lazy selection seems to do nothing for your speed, check your launch args first.
Second: laziness skips execution, not loading. If a checkpoint loader or a 4x upscale-model loader also feeds something else in the graph - or you wired the chooser's model loader into both branches - it still runs and still holds VRAM. Lazy selection cuts the sampler pass, the decode, the upscale. It doesn't magically free a model that something else needed anyway.
Third: you can't feed the boolean from a node that's downstream of the select node, obviously, but that mistake is easy to make when the condition comes from a measurement performed on the result of a previous stage. Keep the measure → choose → measure → choose chain running strictly left to right.
Last, a small honesty note: the engine facts under this node - lazy inputs, IS_CHANGED and the output-node-backward cache - are the most stable corner of ComfyUI. The node itself is obscure enough that Reddit has essentially never discussed this pack. Nothing exotic here means it should break, but if it does, you're reading the source rather than finding a thread.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| condition | BOOLEAN | — | |
| on_true | IMAGE | — | |
| on_false | IMAGE | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |