Swap the model, keep the workflow.
Bring your own detector, embedding extractor, InsightFace provider settings, or grouping algorithm while keeping Sort Moments' local folder workflow.
These docs describe the intended package surface. Do not treat the PyPI install path as live until the package, wheel metadata, and import surface are verified. For now, use the CLI from source.
Swap the face detector
A detector can be an object with get(image_rgb) or a callable. Return objects compatible with FaceModel: bbox, det_score, and optionally embedding.
from sortmoments import DetectedFace, SortMomentsOrganizer
class MyDetector:
def get(self, image_rgb):
boxes = run_my_local_detector(image_rgb)
return [DetectedFace(bbox=box, det_score=0.98) for box in boxes]
organizer = SortMomentsOrganizer(face_model=MyDetector())Swap the embedding model
If your detector does not produce embeddings, supply a separate embedder. It can expose embed(image_rgb, face) or be a callable.
class MyEmbedder:
def embed(self, image_rgb, face):
crop = crop_face(image_rgb, face.bbox)
return my_embedding_model.encode(crop)Control the default InsightFace provider
organizer = SortMomentsOrganizer(SortMomentsConfig(
model_name="buffalo_l",
det_size=(512, 512),
prefer_gpu=False,
))Reference: performance and model fields.
Swap the grouping strategy
A grouping strategy receives FaceRecord objects. Use this to enforce stricter clustering, known identities, album-specific rules, or a completely different algorithm.
class MyGrouping:
def group(self, face_records, similarity_threshold=0.5):
groups = {}
for record in face_records:
person_id = assign_with_my_clusterer(record.embedding)
groups.setdefault(person_id, []).append(record)
return groupsKnown-person matching
If you already have labeled reference embeddings, use a grouping model that compares every face to your known index before falling back to rename_N.
class KnownPeopleGrouping:
def __init__(self, reference_embeddings):
self.reference_embeddings = reference_embeddings
def group(self, face_records, similarity_threshold=0.62):
groups = {}
for record in face_records:
name, score = nearest_known_person(record.embedding, self.reference_embeddings)
person_id = name if score >= similarity_threshold else "review_unknown"
groups.setdefault(person_id, []).append(record)
return groupsDebugging custom models
- Set keep_intermediate=True so crops and embeddings stay on disk.
- Start with a tiny folder of 5–10 images before batch processing thousands.
- Use two-pass workflows to avoid rerunning detection while tuning grouping.
- Use the CLI dry-run for path/config validation before launching expensive jobs.
Contract and privacy assumptions
- Detectors return face boxes in image pixel coordinates.
- Embedders return numeric vectors or set face.embedding.
- Grouping strategies return stable folder-safe person IDs.
- Source images should not be mutated.
Reference protocols: Provider protocols.