File size: 9,452 Bytes
29bfc1f
 
 
 
 
49be4be
a7e7d81
49be4be
 
 
 
 
 
 
29bfc1f
 
49be4be
 
 
 
 
 
 
 
 
 
 
 
 
29bfc1f
49be4be
29bfc1f
 
 
49be4be
29bfc1f
 
 
 
49be4be
29bfc1f
 
 
 
 
49be4be
29bfc1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
725ae84
29bfc1f
 
725ae84
29bfc1f
 
 
 
 
 
a7e7d81
29bfc1f
 
 
 
 
 
 
49be4be
29bfc1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a1ae64
 
 
29bfc1f
6a1ae64
 
 
29bfc1f
 
6a1ae64
 
 
29bfc1f
 
 
 
 
 
 
 
 
6a1ae64
 
29bfc1f
 
 
 
 
 
 
49be4be
29bfc1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dfc44c0
 
 
29bfc1f
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import os
from dotenv import load_dotenv

load_dotenv()

"""This Files have Imported and created Global Variables which are used 
    Throughout the Project , There are in Total of 56 Variables which"""


# ===============================================================
# Credentals / Secrets (set in HF Space secrets) / KEYS / URLS
# ===============================================================

#Pinecone + Cloudinary credentials (set in HF Space secrets)
DEFAULT_PINECONE_KEY = os.getenv("DEFAULT_PINECONE_KEY", "")
DEFAULT_CLOUDINARY_URL = os.getenv("DEFAULT_CLOUDINARY_URL", "")
#supbase Credentials
SUPABASE_URL = os.getenv("SUPABASE_URL", "")
SUPABASE_SERVICE_KEY = os.getenv("SUPABASE_SERVICE_KEY", "")
# HF Credentials
HF_TOKEN = os.getenv("HF_TOKEN", "")
# Upstash_Redis credentials
UPSTASH_REDIS_URL = os.getenv("UPSTASH_REDIS_URL", "")
UPSTASH_REDIS_TOKEN = os.getenv("UPSTASH_REDIS_TOKEN", "")

# ===============================================================
# Variables Set in HF Spaces Can be configured
# ===============================================================


# Index Names To be used to name variable for storing embeddings genereted from DInov2 and SIGLIP models
IDX_FACES = os.getenv("IDX_FACES", "enterprise-faces")
IDX_OBJECTS = os.getenv("IDX_OBJECTS", "enterprise-objects")

# Configure Index Names for storing Embedding Generated by ARCFACE and ADAFACE
IDX_FACES_ARCFACE = os.getenv("IDX_FACES_ARCFACE", "faces-arcface")
IDX_FACES_ADAFACE = os.getenv("IDX_FACES_ADAFACE", "faces-adaface")

# ──────────────────────────────────────────────────────────────
# Concurrency / limits / For Speeding Up Inference 
# ──────────────────────────────────────────────────────────────
MAX_CONCURRENT_INFERENCES = int(os.getenv("MAX_CONCURRENT_INFERENCES", "2"))
MAX_FILES_PER_UPLOAD = int(os.getenv("MAX_FILES_PER_UPLOAD", "50"))
INFERENCE_CACHE_SIZE = int(os.getenv("INFERENCE_CACHE_SIZE", "128"))



# ──────────────────────────────────────────────────────────────
# Image / detection
# ──────────────────────────────────────────────────────────────
MAX_IMAGE_SIZE = int(os.getenv("MAX_IMAGE_SIZE", "1024"))
MAX_CROPS = int(os.getenv("MAX_CROPS", "10"))
YOLO_PERSON_CLASS_ID = 0
YOLO_MIN_CROP_PX = int(os.getenv("YOLO_MIN_CROP_PX", "50"))
YOLO_CONF_THRESHOLD = float(os.getenv("YOLO_CONF_THRESHOLD", "0.25"))

DET_SIZE_PRIMARY = (640, 640)
DET_SCALES = [(1280, 1280), (960, 960), (640, 640)]
IOU_DEDUP_THRESHOLD = float(os.getenv("IOU_DEDUP_THRESHOLD", "0.4"))
MIN_FACE_SIZE = int(os.getenv("MIN_FACE_SIZE", "30"))
MAX_FACES_PER_IMAGE = int(os.getenv("MAX_FACES_PER_IMAGE", "20"))

# Phase 2: relaxed from 0.5 → 0.3 to index more faces (filter at query time)
FACE_QUALITY_GATE = float(os.getenv("FACE_QUALITY_GATE", "0.3"))

# Laplacian variance blur threshold for face crops.
# Faces below this score are excluded from search results AND clustering.
# Typical values: >100 = sharp, 50-100 = acceptable, <50 = blurry.
FACE_BLUR_THRESHOLD = float(os.getenv("FACE_BLUR_THRESHOLD", "50.0"))
CLUSTERING_BLUR_THRESHOLD = float(os.getenv("CLUSTERING_BLUR_THRESHOLD", "30.0"))  # Slightly more lenient for clustering

# ──────────────────────────────────────────────────────────────
# Embedding dimensions
# ──────────────────────────────────────────────────────────────
FACE_DIM = 512
ADAFACE_DIM = 512
FUSED_FACE_DIM = 1024

FACE_CROP_THUMB_SIZE = int(os.getenv("FACE_CROP_THUMB_SIZE", "112"))
FACE_CROP_QUALITY = int(os.getenv("FACE_CROP_QUALITY", "85"))
FACE_CROP_PADDING = float(os.getenv("FACE_CROP_PADDING", "0.2"))
ADAFACE_CROP_PADDING = float(os.getenv("ADAFACE_CROP_PADDING", "0.1"))

ENABLE_ADAFACE = int(os.getenv("ENABLE_ADAFACE", "1"))


# ──────────────────────────────────────────────────────────────
# Phase 1: Speed flags (unchanged, leaving on)
# ──────────────────────────────────────────────────────────────
USE_ONNX_VISION = int(os.getenv("USE_ONNX_VISION", "0"))
ONNX_MODELS_DIR = os.getenv("ONNX_MODELS_DIR", "onnx_models")
ONNX_USE_INT8 = int(os.getenv("ONNX_USE_INT8", "1"))
ENABLE_MULTI_SCALE_FALLBACK = int(os.getenv("ENABLE_MULTI_SCALE_FALLBACK", "1"))
ENABLE_HORIZONTAL_FLIP = int(os.getenv("ENABLE_HORIZONTAL_FLIP", "0"))
OMP_NUM_THREADS = int(os.getenv("OMP_NUM_THREADS", "2"))
MKL_NUM_THREADS = int(os.getenv("MKL_NUM_THREADS", "2"))

# ──────────────────────────────────────────────────────────────
# Phase 2: Recall flags — DEFAULT ON
# ──────────────────────────────────────────────────────────────

# Split-index mode: write ArcFace + AdaFace to separate indexes, score-fuse at query
USE_SPLIT_FACE_INDEXES = int(os.getenv("USE_SPLIT_FACE_INDEXES", "1"))

# Score fusion weights. ArcFace is more discriminative for generic faces;
# AdaFace helps with low-quality/extreme-angle cases. 0.6/0.4 is NIST-FRVT standard.
ARCFACE_WEIGHT = float(os.getenv("ARCFACE_WEIGHT", "0.6"))
ADAFACE_WEIGHT = float(os.getenv("ADAFACE_WEIGHT", "0.4"))

# ArcFace-R100 same-person mean ~0.55, std ~0.12.
# Lowered from 0.30 to 0.22 for better recall on same-person photos.
# Still well above impostor tail (different-person mean ~0.05, std ~0.08).
FACE_MATCH_THRESHOLD = float(os.getenv("FACE_MATCH_THRESHOLD", "0.22"))

# With both models agreeing, 0.26 fused ≈ arc 0.22 + ada 0.30 together.
# Lower threshold when both models agree = more complete galleries.
FUSED_MATCH_THRESHOLD = float(os.getenv("FUSED_MATCH_THRESHOLD", "0.26"))

# ArcFace-only floor (no AdaFace confirmation available).
# Lowered from 0.38 to 0.28 — still strict enough to reject imposters while
# capturing same-person photos across diverse angles/lighting.
ARCFACE_SOLO_THRESHOLD = float(os.getenv("ARCFACE_SOLO_THRESHOLD", "0.28"))

# Query-time augmentation: OFF by default, enabled via deep_search form flag
ENABLE_QUERY_TIME_AUG = int(os.getenv("ENABLE_QUERY_TIME_AUG", "0"))

# Larger top_k: was 50, now 500 so large galleries aren't truncated
FACE_SEARCH_TOP_K = int(os.getenv("FACE_SEARCH_TOP_K", "500"))
OBJECT_SEARCH_TOP_K = int(os.getenv("OBJECT_SEARCH_TOP_K", "100"))

# Final API returns at most this many per-face matches (after dedup)
# Increased from 200 to 500 to show complete photo galleries
FACE_RESULTS_PER_QUERY_CAP = int(os.getenv("FACE_RESULTS_PER_QUERY_CAP", "500"))

# ──────────────────────────────────────────────────────────────
# Phase 3: People View + Job Queue — DEFAULT OFF (opt-in via env)
# ──────────────────────────────────────────────────────────────

# Redis-backed inference cache + job queue (requires Upstash)
# Set UPSTASH_REDIS_URL + UPSTASH_REDIS_TOKEN in HF Space secrets.


# Master toggle: enable the persistent Redis cache (replaces in-memory dict).
# Falls back to in-memory if UPSTASH_REDIS_URL is not set, so this is safe to
# leave True even before Upstash is wired up.
USE_REDIS_CACHE = int(os.getenv("USE_REDIS_CACHE", "0"))

# Async upload mode: when True, POST /api/upload?async=true returns a job_id
# immediately and processes in the background worker.
# Synchronous uploads (no ?async param) always work regardless of this flag.
USE_ASYNC_UPLOADS = int(os.getenv("USE_ASYNC_UPLOADS", "1"))

# Cluster-aware search expansion: after the initial face search, expand results
# to include ALL images in the matched identity clusters.
# Near-100% recall for well-indexed people. Disable if Supabase is slow.
USE_CLUSTER_AWARE_SEARCH = int(os.getenv("USE_CLUSTER_AWARE_SEARCH", "1"))

# HDBSCAN parameters — tuned for typical 1k–10k image libraries
CLUSTER_MIN_SAMPLES = int(os.getenv("CLUSTER_MIN_SAMPLES", "5"))  # Increased to 5 for stricter clustering, fewer duplicates
CLUSTER_MIN_CLUSTER_SIZE = int(os.getenv("CLUSTER_MIN_CLUSTER_SIZE", "5"))  # Increased from 2 to 5, require 5+ faces per cluster
CLUSTER_EPSILON = float(os.getenv("CLUSTER_EPSILON", "0.20"))  # Tightened from 0.35 to 0.20 to reduce duplicate clusters

# Auto re-cluster after every N new face uploads (0 = disabled, manual only)
CLUSTER_AUTO_TRIGGER_EVERY = int(os.getenv("CLUSTER_AUTO_TRIGGER_EVERY", "0"))