MaSC: A Masked Similarity Metric for Evaluating Concept-Driven Generation
Project page · GitHub · PyPI
MaSC is a non-LLM evaluation metric for single-concept text-to-image
personalization. One forward pass of google/siglip2-so400m-patch16-naflex
per image plus pre-computed segmentation masks yields two scores from
the same patch-token tensor:
- Concept Preservation (CP) — masked-maxcos over the foreground concept region.
- Prompt Following (PF) — cosine between a subject-stripped prompt embedding and a background-pooled image embedding.
Install
pip install masc-metric
You will also need a SigLIP2 backbone, which the package auto-downloads
from HuggingFace on first use. The default checkpoint
(google/siglip2-so400m-patch16-naflex) is gated — accept the model's
license on HF and huggingface-cli login once.
Usage
import numpy as np
from PIL import Image
from masc import MaSC
masc = MaSC() # default: siglip2-so400m-patch16-naflex; auto-picks cuda > mps > cpu
ref_image = Image.open("ref.png")
out_image = Image.open("out.png")
ref_mask = np.array(Image.open("ref_mask.png").convert("L")) # H, W uint8
out_mask = np.array(Image.open("out_mask.png").convert("L"))
result = masc.score(
ref_image=ref_image,
ref_mask=ref_mask,
out_image=out_image,
out_mask=out_mask,
prompt="a photo of a cat on the beach",
object_name="cat", # subject stripped from prompt for the PF head
)
print(result.cp, result.pf)
print(result.extras)
A runnable end-to-end version of the snippet above is in
examples/score_sample.py, which scores a
real (reference, output, prompt) sample shipped under
examples/sample_hat/.
MaSC.score(...) returns a MaSCResult with:
cp: float— concept preservation score in[-1, 1](higher = better).pf: float— prompt following score in[-1, 1](higher = better).extras: dict— patch counts, fg fractions, the (possibly stripped) prompt actually fed to the text encoder.
Masks
MaSC expects pre-computed binary masks as np.ndarray (H, W). It does
not bundle a segmenter — produce masks however you like (SAM3, Grounded
SAM, hand-labeled). Foreground = non-zero. Values can be uint8 (0..255)
or float (0..1).
The masks must mark the concept region; in the reference image the concept is the subject being preserved, in the output image the concept is the same subject in its new scene.
Subject stripping (PF prompt)
Passing object_name removes the subject from the prompt before the PF text
encoding. Two strippers are available:
masc = MaSC() # stripper="regex": default, behind every MaSC number in the paper
masc = MaSC(stripper="spacy") # syntax-aware, optional extra
The default regex stripper removes (a|an|the) <object_name>, or the bare
name when no article precedes it. It needs no extra dependency, but it misses
inflected forms ("kittens" for "kitten") and leaves fragments such as the
possessive 's or a stranded adjective ("a vintage motorcycle" becomes
"a vintage").
The spacy stripper parses the prompt and removes every mention of the
subject together with its noun-phrase pre-modifiers (determiner, possessive,
numeral, adjective, compound), matching by lemma so plurals and capitalized
forms are found. It keeps everything attached to the right of the subject
(prepositional phrases, participial clauses, relations to other objects):
from masc import strip_subject_from_prompt
strip_subject_from_prompt("Two vintage motorcycles parked on a lonely desert road",
"motorcycle", stripper="spacy")
# -> 'parked on a lonely desert road'
Install it with:
pip install "masc-metric[spacy]"
python -m spacy download en_core_web_sm
On DreamBench++ the two strippers produce different text for 162 of the 1,179 non-style prompts, but PF agreement with the human ratings does not change: Krippendorff's α differs by −0.001 (95% CI [−0.004, +0.001], paired bootstrap over 6,605 rated images). The regex stripper therefore stays the default. The spaCy stripper gives cleaner text when prompts mention the subject in the plural, with a possessive or with adjectives; whether that changes agreement on such prompt sets is untested.
Known limits of the spacy stripper:
- Pronouns that refer to the subject ("its", "her") stay; the pipeline has no coreference component.
- A subject mentioned only by a synonym or a different noun ("a corgi" for
object_name="corgi dog") is not found, and the prompt is kept. - Coordination keeps the conjunction: "a kitten and a puppy" becomes "and a puppy".
- Style prompts keep their "in the style of" scaffold.
- The small English model often parses a subject followed by a participle as one compound noun ("a dolphin swimming"). The stripper then removes the determiner and adjectives with the subject, unless a noun not ending in "-ing" follows directly ("a motorcycle rider" becomes "a rider"). A participle without "-ing" can therefore leave them behind: "a ripe avocado split open" becomes "a ripe split open".
Outputs depend on the spaCy model. The DreamBench++ comparison above and the
expected strings in tests/test_text.py use en_core_web_sm 3.8.0 (spaCy
3.8); loading another version of it warns. result.extras records the
stripper, the spaCy pipeline (stripper_model, e.g.
"en_core_web_sm-3.8.0") and whether the subject was found and removed
(prompt_stripped).
Choosing a backbone
Any SigLIP2 checkpoint works (fixed-size or NaFlex):
masc = MaSC(model_id="google/siglip2-base-patch16-512")
Smaller backbones are faster but lose accuracy. The paper's headline
numbers are on siglip2-so400m-patch16-naflex at 1024 patches.
Device
By default MaSC auto-picks the best available torch device: CUDA →
Apple MPS → CPU. Override with device="cuda", device="mps", or
device="cpu" if you want to pin it.
Citation
If you use MaSC, please cite:
@article{bartkowiak2026masc,
title={MaSC: A Masked Similarity Metric for Evaluating Concept-Driven Generation},
author={Bartkowiak, Patryk and Petersen, Lennart and Kotrys, Bartosz and Michels, Dominik and Pirk, Soren and Palubicki, Wojtek},
journal={arXiv preprint arXiv:2605.22469},
year={2026}
}
License
Apache-2.0. See LICENSE.
Metadata
Release files for masc-metric 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| masc_metric-0.2.0.tar.gz | 21.8 kB | Without provenance | Details for masc_metric-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| masc_metric-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for masc_metric-0.2.0-py3-none-any.whl |
Total release size: 40.8 kB
Release files / masc_metric-0.2.0.tar.gz
| Download URL | masc_metric-0.2.0.tar.gz |
|---|---|
| Size | 21.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b0745ea7ece6c43b2e11816db4811d907a6ed6bcb5176adff112d8fa5b6364c5
|
|
BLAKE2b-256 checksum How to use checksums |
2dd25fd1a0712c1e448c211ca92698f2fced0084d58dac3aea028ab870e09481
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Release files / masc_metric-0.2.0-py3-none-any.whl
| Download URL | masc_metric-0.2.0-py3-none-any.whl |
|---|---|
| Size | 19.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b2ed1c8520a3ec7ff91e713f85229705632a544aaf9b779dd29393a54e8eb462
|
|
BLAKE2b-256 checksum How to use checksums |
6827b9b053e09fcf65763137e32774364afca874eeeb31059b219cc20f3a78b0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Security for masc-metric 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.