Face detection/recognition: - Replace YuNet + SFace with InsightFace buffalo_l (RetinaFace + ArcFace) - 512-d ArcFace embeddings (was 128-d SFace), migration 0006 resizes column - Remove YOLO person-bbox workaround — RetinaFace is accurate enough - Detection threshold 0.65 cleanly separates real faces (0.72+) from false positives on dogs/paintings (0.56-0.61) Content-type classification: - CLIP zero-shot classifier using native PyTorch text encoder + ONNX image encoder for high-quality text-image similarity - Categories: photograph, screenshot, document, receipt, meme, artwork - Writes Tag(kind=content_type) per photo via photo_tags - Margin-based confidence: top-1 vs top-2 score difference - New ClassifierSettings in config (enabled, min_confidence) - Wired into vision_fanout pipeline Tested: 6 real faces from 4 photos (zero false positives), 11/13 photos classified (8 photograph, 2 artwork, 1 meme). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
40 lines
1.3 KiB
Python
40 lines
1.3 KiB
Python
"""face_embeddings vector 128 -> 512
|
|
|
|
Revision ID: 0006_face_512d
|
|
Revises: 0005_face_embeddings
|
|
Create Date: 2026-04-10
|
|
|
|
Resize face_embeddings.vector from Vector(128) to Vector(512) for
|
|
ArcFace embeddings (InsightFace). Drops existing data and HNSW index,
|
|
recreates both.
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
|
|
revision: str = "0006_face_512d"
|
|
down_revision: Union[str, None] = "0005_face_embeddings"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
# Drop index, truncate (old 128-d vectors are incompatible), resize
|
|
op.execute("DROP INDEX IF EXISTS ix_face_embeddings_vector_hnsw")
|
|
op.execute("DELETE FROM face_embeddings")
|
|
op.execute("ALTER TABLE face_embeddings ALTER COLUMN vector TYPE vector(512)")
|
|
op.execute("""
|
|
CREATE INDEX IF NOT EXISTS ix_face_embeddings_vector_hnsw
|
|
ON face_embeddings USING hnsw (vector vector_cosine_ops)
|
|
""")
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.execute("DROP INDEX IF EXISTS ix_face_embeddings_vector_hnsw")
|
|
op.execute("DELETE FROM face_embeddings")
|
|
op.execute("ALTER TABLE face_embeddings ALTER COLUMN vector TYPE vector(128)")
|
|
op.execute("""
|
|
CREATE INDEX IF NOT EXISTS ix_face_embeddings_vector_hnsw
|
|
ON face_embeddings USING hnsw (vector vector_cosine_ops)
|
|
""")
|