Selector
The Selector that makes 'best of N' real
- model
- inputs
- images
- latents
- masks
- SCORES
- IMAGES
- LATENTS
- MASKS
Everything else in this pack just loads and tokenizes. The Selector is where the actual scoring happens, and it's the node you'll actually reach for: generate a batch of images, feed them in with a prompt, and get back only the best ones - plus their matching latents and masks, which is the part that makes this genuinely useful in a real workflow. It's the "I'll make eight, keep the one that works" pattern you'd otherwise do by eyeballing a grid.
The inputs that matter
model(PS_MODEL) andinputs(PS_INPUTS) - wire these from the Loader and Processor. They're the brain and the tokenized prompt+batch.threshold- aFLOATfrom 0 to 1, default 0. A floor: images scoring below it are dropped before selection.limit- anINTfrom 1 to 1000, default 1. How many winners to keep.images/latents/masks(all optional) - pass-throughs. Feed in whatever you want filtered to match the winners.
What it outputs
Four outputs: SCORES (a STRING, not a tensor - comma-separated scores rounded to three decimals, for display and sanity checks), plus IMAGES, LATENTS, and MASKS. The last three mirror whatever pass-throughs you supplied, re-indexed so only the selected entries come through. Wire up the latents you used to generate the batch and you get back only the winners' latents - which means you can run a second, higher-step pass on just the top few instead of wasting compute on the duds.
How the scoring works
PickScore embeds the text and every image, normalizes the embeddings, and takes the cosine similarity between them. Then comes the subtle bit: when there's more than one image, the scores get multiplied by the model's learned temperature and passed through a softmax over the batch. So your scores are relative to the batch - with eight images they sum to about 1, and a 0.6 here is not a 0.6 in a different batch. The threshold only behaves like an absolute floor in that relative world. With a single image there's no softmax at all and you get a raw cosine similarity, which can sit anywhere in [-1, 1]. The practical lesson: don't compare scores across runs, and if a score seems meaningless, remember it was never meant to stand alone.
The trap that stops your whole workflow
If you don't wire anything into the images, latents, or masks inputs, the Selector has nothing to return - so it raises an interrupt and kills the entire run. That "why is my workflow not running" moment is usually this: the chain is built correctly but no pass-through is connected. You don't need all three, but you need at least one, or the Selector refuses to finish. This trips up real people - the community example is someone feeding 100 extracted video frames in, expecting the top 5 out, and getting a dead run because the Selector had nothing to pass along.
Where people actually use it
Two classic setups. First: batch generation - vary seeds or prompts to produce a grid, feed all of them with a shared prompt, keep the top-limit winners, and re-encode their latents with more steps. Second: picking frames from video or a big folder - pull out a hundred thumbnails, score them against "sharp, well-composed, in focus", and keep the five that match. This is a niche pack and it stays niche, but for "generate N, keep the good one" it's far less fiddly than wiring CLIP score comparisons by hand.
Installing
Find "ComfyUI PickScore Nodes" in ComfyUI Manager, or:
cd ComfyUI/custom_nodes
git clone https://github.com/Zuellni/ComfyUI-PickScore-Nodes
Restart ComfyUI, and remember the first run downloads the ~3.9 GB PickScore model.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| model | PS_MODEL | — | |
| inputs | PS_INPUTS | — | |
| threshold | FLOAT | 0.0000–1 | — |
| limit | INT | 11–1000 | — |
| imagesopt | IMAGE | — | |
| latentsopt | LATENT | — | |
| masksopt | MASK | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| SCORES | STRING | — |
| IMAGES | IMAGE | — |
| LATENTS | LATENT | — |
| MASKS | MASK | — |