image vector dimensionality reduction

This commit is contained in:
sriram
2026-09-17 14:21:48 +05:30
parent deae694a1f
commit afa0bfa743
11 changed files with 907 additions and 183 deletions

View File

@@ -381,27 +381,41 @@ USE_PLAYWRIGHT_FALLBACK = _bool("USE_PLAYWRIGHT_FALLBACK", "true")
MIN_IMAGE_BYTES = int(os.getenv("MIN_IMAGE_BYTES", "3000"))
# ---------------------------------------------------------------------------
# img_vector - a 32x32 RGB pixel thumbnail of each product's primary image,
# stored on its brand table (app/services/image_vector.py)
# img_vector - a MobileNetV3-Small image embedding (1024 floats, L2-normalised)
# of each product's primary image, stored on its brand table
# (app/services/image_vector.py owns the column, image_embedder.py the model)
# ---------------------------------------------------------------------------
# Computed off the request path by one bounded worker thread after every
# catalog write, and by scripts/backfill_image_vectors.py for existing rows.
# Default on: the work is one small download per row just written, never on
# a request. tests/conftest.py pins it OFF so the suite never dials the
# database named in a developer's .env.
# Default on: the work is one small download and one ~30ms inference per row
# just written, never on a request. tests/conftest.py pins it OFF so the
# suite never dials the database named in a developer's .env.
ENABLE_IMAGE_VECTORS = _bool("ENABLE_IMAGE_VECTORS", "true")
# A download past this many bytes is abandoned - a wrong URL to a video must
# not fill the container.
IMAGE_VECTOR_MAX_BYTES = int(os.getenv("IMAGE_VECTOR_MAX_BYTES", str(8 * 1024 * 1024)))
# Refused before decoding when the header claims more pixels than this
# (decompression-bomb guard; 40MP is well past any product photo).
IMAGE_VECTOR_MAX_PIXELS = int(os.getenv("IMAGE_VECTOR_MAX_PIXELS", "40000000"))
# (decompression-bomb guard; 25MP is well past any product photo, and the
# embedder decodes at full resolution - ~75MB of RGB at this cap).
IMAGE_VECTOR_MAX_PIXELS = int(os.getenv("IMAGE_VECTOR_MAX_PIXELS", "25000000"))
IMAGE_VECTOR_TIMEOUT_SECONDS = float(os.getenv("IMAGE_VECTOR_TIMEOUT_SECONDS", "15"))
# Minimum gap between two requests to the same image host.
IMAGE_VECTOR_HOST_PAUSE_SECONDS = float(os.getenv("IMAGE_VECTOR_HOST_PAUSE_SECONDS", "0.5"))
# Writes waiting for the worker; beyond this a write's rows are left for the
# backfill script rather than queued.
IMAGE_VECTOR_QUEUE_MAX = int(os.getenv("IMAGE_VECTOR_QUEUE_MAX", "64"))
# The TFLite embedder (mobilenet_v3_small_embedder.tflite, input [1,224,224,3]
# float32, output [1,1024]). Lives under app/, NOT data/: /app/data is a named
# volume on every deployment (see BUNDLED_ASSETS_DIR), and a file added to
# the image under a mounted path is invisible on any volume that already
# exists. app/ is copied into the image and never mounted.
IMAGE_EMBED_MODEL_PATH = _dir(
"IMAGE_EMBED_MODEL_PATH",
_BACKEND_ROOT / "app" / "services" / "models" / "mobilenet" / "mobilenet_v3_small_embedder.tflite",
)
# Intra-op threads for one inference. 2 on the prod host; inference itself is
# serialised by a lock (a TFLite interpreter is not thread-safe).
IMAGE_EMBED_NUM_THREADS = int(os.getenv("IMAGE_EMBED_NUM_THREADS", "2"))
# ---------------------------------------------------------------------------
# USDA FoodData Central - nutrition for loose, unbranded commodities