Library guide¶
This guide covers the public Python API. For every signature and docstring, see the API reference.
Embedder¶
Embedder turns one local model directory into text and image embeddings.
It loads the model lazily on the first call, is safe to share across threads,
and is not safe to share across processes.
from siglip2_toolkit import Embedder, models
resolved = models.resolve_model("google/siglip2-base-patch16-naflex")
embedder = Embedder(
model=resolved.path,
model_id=resolved.identifier, # recorded in the manifest
device="auto", # cuda → mps → cpu
)
texts = ["a cat on a sofa", "a dog on the beach"]
text_vectors = embedder.embed_texts(texts, batch_size=32, max_length=64)
print(text_vectors.shape, text_vectors.dtype) # (2, dim), float32
image_vectors = embedder.embed_images(
["examples/images/cat.jpg", "examples/images/coffee.jpg"],
batch_size=16,
)
- Embeddings are L2-normalised float32 numpy arrays (one row per input).
embed_imagesaccepts paths, bytes, file-like objects or PIL images.on_error="skip"skips unreadable inputs; withreturn_indices=Trueyou also get the positions that were kept.embedding_space()returns anEmbeddingSpacemanifest (model,dimensions,normalization, optionalrevision/preprocess).dimensionsanddeviceareNoneuntil the model is loaded.load()is idempotent; you normally do not need to call it.
Zero-shot classification¶
labels = ["cat", "dog", "astronaut", "coffee", "rocket"]
results = embedder.classify(
["examples/images/cat.jpg", "examples/images/coffee.jpg"],
labels,
template="a photo of a {}", # "{}" is replaced by each label
top_k=3,
)
for row in results:
print(row) # [("cat", 0.93), ...] sorted by probability
Use zero_shot_logits when you need raw scores instead of softmax
probabilities. The scores follow the SigLIP recipe: cosine similarity scaled
by the model's learned logit_scale plus logit_bias.
Fine-tuned classification¶
ImageClassifier loads a fine-tuned image-classification checkpoint. Labels
come from config.id2label:
from siglip2_toolkit import ImageClassifier
classifier = ImageClassifier("/path/to/finetuned-model", device="auto")
for image, labels in zip(
["examples/images/cat.jpg"],
classifier.classify(["examples/images/cat.jpg"], top_k=3),
):
print(image, labels)
num_labels overrides the head size when a checkpoint requires it.
Exporter¶
Exporter scans image directories, embeds the images and writes a portable
export. The configuration is a dataclass:
from siglip2_toolkit import ExportConfig, Exporter
config = ExportConfig(
input_dirs=["./photos"],
output_file="embeddings.jsonl",
model="google/siglip2-base-patch16-naflex", # cache id or local path
device="auto",
batch_size=32,
incremental=True,
store_metadata=True,
create_thumbnails=False,
thumbnail_dir="thumbnails",
thumbnail_size=(128, 128),
)
result = Exporter(config).run()
print(result.processed, result.skipped, result.removed, result.total)
print(result.embedding_space)
| Field | Meaning |
|---|---|
input_dirs |
One or more directories to scan (recursive). |
output_file |
.jsonl or .pkl; the format is inferred. |
output_format |
Override jsonl/pkl. |
model |
Toolkit cache identifier or local model directory; None = current. |
home |
Non-default toolkit home. |
device |
auto, cpu, cuda, mps, cuda:1… |
batch_size |
Images per embedding batch. |
incremental |
Only new/modified files; drop deleted files. |
source_file |
Base export for an incremental run (defaults to output_file). |
store_metadata |
Store EXIF/dimensions/timestamps/filename. |
create_thumbnails |
Also write WEBP thumbnails. |
thumbnail_dir, thumbnail_size |
Thumbnail destination and size. |
Progress callbacks:
def progress(done, total, path):
print(f"{done}/{total} {path}")
def stage(name):
print("stage:", name) # scanning / embedding / done
Exporter(config, on_progress=progress, on_stage=stage).run()
With incremental=True the exporter keeps the ids and metadata of unchanged
files, embeds new/modified ones and removes deleted files. Modification
detection needs metadata (store_metadata=True); with metadata disabled only
new/deleted files are detected.
Failed runs write to <output>.tmp and are only moved into place when the
export succeeds, so interrupted runs do not leave a half-written output.
Filters¶
Filters are composable, serialisable predicates over metadata dictionaries:
from siglip2_toolkit.filters import field, matches, to_dict, from_dict
predicate = (field("width") >= 2000) & (field("filename").contains("cat"))
metadata = {"width": 2400, "filename": "cat_01.jpg"}
assert matches(metadata, predicate)
payload = to_dict(predicate) # JSON-serialisable
same = from_dict(payload)
Operators: ==, !=, <, <=, >, >=, between(low, high), in_,
contains(value, case_sensitive=False), startswith(...), exists().
Combine with &, |, ~. See Filters.
Retrieval¶
Storage-agnostic helpers that operate on numpy arrays and (payload, score)
lists:
import numpy as np
from siglip2_toolkit.retrieval import (
rank_by_similarity, results_matrix, fuse_rrf, apply_similarity_drop,
)
from siglip2_toolkit import formats
records = formats.load_records("embeddings.jsonl")
matrix = results_matrix(records) # (n, dimensions)
query = embedder.embed_texts(["a cat"])[0]
ranked = rank_by_similarity(query, matrix, top_k=50, metric="cosine")
ranked = apply_similarity_drop(ranked, max_drop_percent=15)
# Fuse several ranked lists (for example text and image queries)
fused = fuse_rrf([ranked_text, ranked_image], weights=[1.0, 0.7], k=60)
- Metrics:
cosine(1 − cosine similarity),euclid,manhattan. Scores are distances (lower is better). fuse_rrfaccepts lists of(payload, score); payloads can be ids, paths or dicts with anid/pathkey. Ties prefer the better original score.apply_similarity_dropkeeps results within a percentage of the best score. Uselower_is_better=Falsefor RRF/higher-is-better scores.
Formats¶
from siglip2_toolkit import EmbeddingSpace, formats
header = formats.read_header("embeddings.jsonl")
space = formats.header_embedding_space(header)
if space:
print(space.model, space.dimensions, space.id)
for record in formats.iter_records("embeddings.jsonl"):
print(record["id"], len(record["vector"]))
records = formats.load_records("embeddings.jsonl")
by_id = formats.load_by_id("embeddings.jsonl")
formats.write_export(
"copy.jsonl",
records,
embedding_space=space,
)
make_record(vector, metadata, id=...) builds a record. See
Export format.
Utilities¶
utils.find_image_files(dirs)— recursive image discovery (JPEG/PNG/WebP/TIFF/HEIF, depending on Pillow support).utils.extract_metadata(path)— EXIF/dimensions/timestamps/size.utils.create_thumbnail(...)— WEBP thumbnails.utils.register_heif_support()— enable HEIF/HEIC ifpillow-heifis installed (it is a dependency, but registration is explicit).utils.resolve_torch_device(...)— the device policy used internally.
Logging¶
The library uses the standard logging module and never configures handlers
at import time. The CLI calls setup_logging(level); libraries can configure
their own logger hierarchy.