Nodes/ComfyUI-UtilsCollection/Tag Normalize and Combine (Legacy)
ComfyUI Node

Tag Normalize and Combine (Legacy)

Two taggers, one clean deduped list

By silveroxides·Created 2 months ago·Updated about 7 hours ago· 24
Tag Normalize and Combine (Legacy)
  • scores_1
  • scores_2
  • deduped_tags
  • normalized_scores
tags_1
tags_2

Run two WD-style taggers on the same image and you get two tag lists that disagree: different vocabularies, different scores, overlapping tags. That's not a bug in your workflow - it's just what happens, and most people resolve it by picking one tagger and ignoring the other's signal. This node is the merge: it normalizes both score sets to the same scale, dedupes the overlapping tags, keeps the higher score for each duplicate, and hands you one sorted, deduplicated list with a consistent score dictionary.

How it works

Two tag inputs (tags_1, tags_2 - multiline strings, comma-separated) plus two optional score inputs (scores_1, scores_2, which accept a dict or a JSON string). The core trick is normalization: scores from different taggers live on different scales (one tagger might never go below 0.4, another bottoms out at 0.1), so comparing them raw is meaningless. The node min-max scales each set into the range 0.000001–0.999999, so a "confident 0.9" from tagger A is actually comparable to a "confident 0.9" from tagger B.

If you feed tags without scores, it generates an even descending distribution in the order the tags appear - meaning order becomes priority. First tag listed = highest score, last = lowest. That's a deliberate design choice, and it's the most common source of confusion: with no scores, the node is effectively saying "trust the order you gave me."

The merge keeps the higher normalized score for any tag both lists share, sorts everything descending by score, and emits:

  • deduped_tags - the comma-joined string, ready for a prompt or a caption file.
  • normalized_scores - the score dict, if a downstream node wants the weights.

Why you'd reach for it

Two real use cases. First, in a tagging/captioning pipeline (LoRA dataset prep), combining two taggers with different strengths - say a general-purpose tagger plus a booru-specific one - gives richer captions than either alone, and this node makes the union clean rather than a mess of duplicates. Second, when you run the same tagger at different thresholds and want to merge the confident tags of both passes. Both come back with one consistent scale, which is what makes the merged weights usable for thresholding or ordering.

Where people get burned

  • No scores means order is everything. Feed tags_1/tags_2 with no scores and the output ranking reflects list order, not any real confidence. If you want true scores, wire them in.
  • Scores accept dict or JSON string, but a malformed JSON string silently becomes an empty dict (treated as "no scores"). Paste carefully.
  • The output scale is bounded at 0.999999/0.000001, so don't expect a raw 1.0 from a perfect match.
  • It's string plumbing, not a tagger. The tags come from somewhere else - this only merges.

Installing it

Ships in silveroxides/ComfyUI-UtilsCollection. ComfyUI Manager: search ComfyUI-UtilsCollection, install, restart. Or manually:

cd ComfyUI/custom_nodes
git clone https://github.com/silveroxides/ComfyUI-UtilsCollection
cd ComfyUI-UtilsCollection
pip install -r requirements.txt   # opencv-python, typing-extensions

Restart. No models, no downloads. This is the legacy alias of canonical UC_TagNormalizeCombine; identical behavior under the hood.

Categoryadvanced/text

Inputs (4)

NameTypeDefaultDescription
tags_1STRING
tags_2STRING
scores_1opt*Dictionary of scores for tags_1
scores_2opt*Dictionary of scores for tags_2

Outputs (2)

NameTypeDescription
deduped_tagsSTRING
normalized_scores*