EmbeddingGemma 2 Rank Images
Sort a batch by your prompt, keep the best three
- images
- image_embeddings
- query
- images
- ranking
This is the node that makes "generate twelve, keep the good ones" a graph instead of an evening of clicking. Give it an image batch, the matching embeddings, and a query vector; it reorders the batch best-first and hands you the top k as a normal IMAGE you can save, upscale, or feed straight into a video model.
Unlike Similarity, which reports scores and leaves the decision to you, this gives you pixels back. That's the difference between measuring and doing.
How the sort works
It takes only the first vector of query - this is a single-query ranker by design, not a multi-criteria one. If your query bundle has five vectors, four of them are ignored without complaint. Score every image against that one vector, argsort descending (stable, so ties keep their original batch order), optionally truncate to top_k, then index the image tensor with the resulting order.
top_k is an INT between 0 and 4096, default 0, and 0 means "keep everything, just sorted". So the same node does two jobs: full ranking when you want to look at the whole order, top-k when you want to throw away the tail. Setting it to 3 gives you a 3-image batch.
Outputs: images, an IMAGE batch in the new order, and ranking, a JSON string with the query label, the index order, and the score at each position. That JSON is how you find out why something came first - dump it to a Show Text node while you're tuning, and drop it once you trust it.
The thing that will bite you
image_embeddings must come from Encode Images run on this exact batch. The node checks that the count matches the number of images and raises a message telling you to encode the batch if it doesn't. It cannot check that the embeddings correspond to these images - same count, different batch, and you get a confidently wrong ordering. The usual way to do this by accident: sort a batch somewhere upstream after encoding it. Encode immediately before you rank.
The second one: dimension mismatch. The query and the image embeddings have to be the same Matryoshka length - 768 vs 256 raises immediately, which is the good outcome. Text at 256 and images at 256 is fine.
Third, quieter: the query text wants to be SearchQuery. Media encoders apply no prefix; the text side does, and SearchQuery is the prompt the model was trained to compare against images. Use Document here and your ranking will be subtly worse with no error to tell you.
The workflow, concretely
Empty Latent → KSampler → VAEDecode → [IMAGE batch]
├→ Encode Images ─┐
You, typing a prompt → Encode Text (SearchQuery) ────────────┤
↓
Rank Images (top_k=3) → Save Image
The shipped rank_frames workflow in the repo does the frame version of this - a video's frames ranked against a description, best first - which is a genuinely nice trick: pick the frame that best matches "wide shot of the valley" out of 300 without scrubbing a timeline.
Two notes on that. Frame ranking is where this node is strongest, since the frames are already decoded and you're just choosing among them. And with generated batches, rank rather than filter: sorting 12 images still leaves you 12, and top_k becomes a decision you can change without re-running the sampler.
What it won't do
It ranks by semantic content, not quality. "A red fox in the snow" will surface the images that actually read as a fox in snow - and among four good fox images, the one it likes best is the one whose content is clearest to the encoder, not the one with better lighting, cleaner anatomy, or fewer fingers. For prompt adherence it's a real filter. For "which is prettier", no.
If you want the reasoning rather than the result - every candidate scored against every query, with labels - use EmbeddingGemma 2 Similarity and read its JSON. Rank Images is the decision; Similarity is the report.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/hiroki-abe-58/ComfyUI-EmbeddingGemma2
python -m pip install -r ComfyUI-EmbeddingGemma2/requirements.txt
python ComfyUI-EmbeddingGemma2/tools/download_model.py --models-dir ../models/embeddinggemma
Or search EmbeddingGemma 2 for ComfyUI in ComfyUI Manager, install, restart. About 1.5 GB of model at a pinned revision, SHA-256 verified, into models/embeddinggemma/embeddinggemma-2/. The empty model dropdown usually means the folder is missing modules.json - the loader only lists complete sentence-transformers folders, so use the download script rather than hand-copying a HuggingFace snapshot.
The pack is written against ComfyUI's V3 node API, so on an older build you get no nodes at all rather than an error saying so. And it pulls sentence-transformers 6.1.0, which pins huggingface-hub<2.0 and downgrades a fresh environment's hub from 2.2.0 to 1.33.0 - harmless in testing, worth knowing if an unrelated pack breaks its imports.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| image_embeddings | EG2_EMBEDDINGS | — | |
| query | EG2_EMBEDDINGS | — | |
| top_k | INT | 00–4096 | 0 = keep all, sorted |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | — |
| ranking | STRING | — |