Industry
Pixeltable for ecommerce catalogs
An ecommerce catalog is SKUs, titles, and photos that get replaced. Exact duplicates share a normalized PNG hash. Near-duplicates and "find this bag in navy" are CLIP on the image column. S3 plus Qdrant plus a worker is the glue stack. One TableModel file can cover the same slots.
Updated · Matching docs cookbook
A catalog drop is thousands of studio photos, marketplace crops, and vendor dumps. Merchandising asks for near-duplicates of a hero shot. Shoppers search with a sentence or a phone picture. Ops wants to refuse a re-upload of the same pixels. The files already sit on S3. The missing piece is a row per SKU photo that hashes, embeds, and serves search when a new file lands.
This is the user-uploaded media architecture plus a SKU column. FiftyOne is the App for visual QA of a dump. Pixeltable is the production table the site and the PIM talk to. POST /images does not run the duplicate check; call keep_if_new yourself.
SKU photo pipeline
- Insert sku, title, and the image (upload or S3 URL).
- Hash a normalized RGBA PNG for exact duplicates.
- Index the image with CLIP for text-to-image and image-to-image search.
- Before insert, look up the digest, then CLIP only if that lookup is empty.
- Serve /products insert and /search. Image-to-image is POST multipart.
Worked example: SKU photos with exact-hash and CLIP search
This sketch follows the image-search and image-dedup how-tos. Tune the near-duplicate threshold on labelled pairs from your catalog. 0.95 is a starting point, not a measured result. Keep Qdrant when that engine is already the serving index.
import hashlibfrom io import BytesIOimport pixeltable as pxtfrom pixeltable.functions.huggingface import clipfrom pixeltable.serving import FastAPIRouterTableModel = pxt.model_base()clip_embed = clip.using(model_id='openai/clip-vit-base-patch32')def rgba_png_sha256(img) -> str:buf = BytesIO()img.convert('RGBA').save(buf, format='PNG')return hashlib.sha256(buf.getvalue()).hexdigest()@pxt.udfdef image_sha256(img: pxt.Image) -> str:return rgba_png_sha256(img)class Products(TableModel, name='products'):sku: pxt.Stringtitle: pxt.Stringimage: pxt.Imagefile_sha256 = image_sha256(image)__indexes__ = [pxt.EmbeddingIndex(image, embedding=clip_embed),]@pxt.querydef search_catalog(query: str, limit: int = 8):sim = Products.image.similarity(string=query)return (Products.order_by(sim, asc=False).limit(limit).select(Products.sku, Products.title, Products.image, sim))def keep_if_new(candidate, sku: str, title: str, threshold: float = 0.95) -> None:products = pxt.get_table('catalog.products')digest = rgba_png_sha256(candidate)exact = products.where(products.file_sha256 == digest).select(products.sku).collect()if len(exact) > 0:returnsim = products.image.similarity(image=candidate)hits = (products.where(sim > threshold).order_by(sim, asc=False).limit(10).select(products.sku, sim).collect())if len(hits) == 0:products.insert([{'image': candidate, 'sku': sku, 'title': title}])api = FastAPIRouter(name='catalog')api.add_insert_route(Products,path='/products',inputs=[Products.sku, Products.title],uploadfile_inputs=[Products.image],)api.add_query_route(path='/search', query=search_catalog)# POST /products does not call keep_if_new.# pxt init# pxt schema update app.py catalog -f# pxt service update app.py catalog -f
To run a sketch, install pixeltable[serve] and the packages its model functions import (such as sentence-transformers, openai, or openai-whisper), export the provider key a model call reads (such as OPENAI_API_KEY), save the file as app.py, and run the commands in its last lines. The tables start empty: insert a row, or POST to an insert route, then select the computed columns. Get started covers the CLI.
Honest alternatives per slot
| Slot | Role | Pixeltable | Named alternatives |
|---|---|---|---|
| Object store | Studio and marketplace photos | Image column; insert an S3 URL if the bucket is the origin |
|
| Vector index | Visual search and near-duplicates | CLIP EmbeddingIndex on the image column |
|
| Visual QA | Review a dump of vendor photos | Query results; not a CV visualizer |
|
Where Pixeltable fits
Pixeltable is the catalog photo table: SKU, title, image, a hash UDF, CLIP, and HTTP. Keep S3 as the CDN origin. Keep Qdrant or pgvector when that engine is already the index. Keep FiftyOne for a one-off dump. POST /products does not refuse duplicates; call keep_if_new. The how-tos are image similarity search and image dedup. The architecture is /reference-architecture/user-uploaded-media-pipeline.