Harmony Score Evaluation
No-reference harmony scoring with BargainNet
- composite_image
- mask
- harmonized_image
- harmony_score
- score_text
You harmonized the composite, and it looks better. But "looks better" is a vibe, not a number, and vibes don't survive an A/B test between five model variants. Harmony Score Evaluation gives you the number. It runs BargainNet - a no-reference harmony evaluator from the same libcom toolkit that backs this pack's harmonization models - and predicts how harmonious the foreground and background are on a 0–1 scale. Higher is better. No ground-truth image required; it judges your composite directly.
How it works
The mechanism is about as simple as it gets. Feed it composite_image and a mask (white = the foreground region), and it scores how well that foreground's appearance matches the background. Under the hood it lazy-loads a HarmonyScorer on first run and converts your tensors to the numpy format BargainNet expects. The interesting output path: if you also wire in the optional harmonized_image, it scores both versions and hands you the delta.
That's the workflow this node was built for. Run the pack's ImageHarmonization node, split your pipeline, feed the original and the result in here, and you get an actual improvement value instead of a hunch. It's the only honest way to tune pctnet_strength or pick between CDTNet and PCT-Net variants - run a couple of configurations, keep the one with the bigger improvement, move on.
The outputs
harmony_score(FLOAT) - 0–1, higher = more harmonious. With aharmonized_imagewired in, this returns the harmonized score (not the composite's).score_text(STRING) - the one you'll actually read. With a comparison wired up it's three lines: composite score, harmonized score, and theImprovement: +0.1234delta. Without one, it's the single composite score, pretty-printed.
So: wire the FLOAT into anything that needs a number, or just glance at score_text in the node preview. For iteration, the text output is the whole product.
First-run download (the real gotcha)
Unlike the harmonization nodes, whose weights ship inside the repo, BargainNet's weights are downloaded at first run from HuggingFace (BCMIZB/Libcom_pretrained_models), with a ModelScope fallback. The download path lives in the vendored libcom code, and it needs huggingface_hub (and tqdm) - neither is in the pack's requirements.txt. If your first run dies on an import, that's why:
cd ComfyUI/custom_nodes/comfyui-libcom-image-composition
# portable: .\python_embeded\python.exe -m pip install huggingface_hub tqdm
pip install huggingface_hub tqdm
The download only happens once; after that the .pth sits in model/libcom/harmony_score/pretrained_models/.
Two honest caveats
First, BargainNet was trained on synthetic composites in the iHarmony4 style, so treat the score as relative guidance for A/B testing, not an absolute "is this photoreal" judge. It'll happily rank two bad composites against each other. Use it to pick the better output, not to certify one. Second, same pack health warning as the others: this is a one-commit, zero-impression project. Install the same way (ComfyUI Manager, search "comfyui-libcom-image-composition", or git clone https://github.com/fuselayer/comfyui-libcom-image-composition into custom_nodes/, restart). If a number instead of a gut feeling is worth one extra node in the graph, this is it.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| composite_image | IMAGE | — | |
| mask | MASK | — | |
| harmonized_imageopt | IMAGE | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| harmony_score | FLOAT | — |
| score_text | STRING | — |