Scaffold locally · batch
Image dataset
Curate image datasets, run CLIP search and DETR detection, export to PyTorch or COCO.
Scaffold locally
uvx pixeltable-new --template image-dataset my-image-datasetSame starter-kit files Cloud uses. Local UI (static HTML in some templates) is for uvx, not for Cloud. Cloud deploys schema + insert routes via pxt serve.
schema.py
"""Data Lab -- ML dataset engineering pipeline with auto-annotation and embedding search."""
import os
import pixeltable as pxt
from pixeltable.functions.huggingface import clip, detr_for_object_detection
from pixeltable.functions.uuid import uuid7
# ---------------------------------------------------------------------------
# Namespace
# ---------------------------------------------------------------------------
pxt.create_dir("datalab", if_exists="ignore")
# ---------------------------------------------------------------------------
# Dataset table
# ---------------------------------------------------------------------------
dataset = pxt.create_table(
"datalab.dataset",
{
"uuid": uuid7(),
"image": pxt.Image,
"label": pxt.String,
"split": pxt.String,
"source": pxt.String,
"timestamp": pxt.Timestamp,
},
primary_key=["uuid"],
if_exists="ignore",
)
dataset = pxt.get_table("datalab.dataset")
# ---------------------------------------------------------------------------
# Auto-annotation: DETR object detection
# ---------------------------------------------------------------------------
try:
dataset.add_computed_column(
detections=detr_for_object_detection(dataset.image, model_id="facebook/detr-resnet-50", threshold=0.8),
if_exists="ignore",
)
dataset.add_computed_column(
detection_labels=dataset.detections.label_text,
if_exists="ignore",
)
except Exception:
pass
# ---------------------------------------------------------------------------
# Optional: Vision LLM annotation (requires OPENAI_API_KEY)
# ---------------------------------------------------------------------------
if os.environ.get("OPENAI_API_KEY"):
try:
from pixeltable.functions.openai import chat_completions
dataset.add_computed_column(
vision_annotation=chat_completions(
model="gpt-4o-mini",
messages=[
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {"url": dataset.image},
},
{
"type": "text",
"text": (
"Classify this image into exactly one category. "
"Return ONLY the category name, no explanation."
),
},
],
}
],
)
.choices[0]
.message.content,
if_exists="ignore",
)
except Exception:
pass
# ---------------------------------------------------------------------------
# CLIP embeddings for visual similarity search
# ---------------------------------------------------------------------------
clip_embed = clip.using(model_id="openai/clip-vit-base-patch32")
dataset.add_computed_column(
clip_embedding=clip_embed(dataset.image),
if_exists="ignore",
)
dataset.add_embedding_index(
"image",
idx_name="image_clip_idx",
embedding=clip_embed,
if_exists="ignore",
)
# ---------------------------------------------------------------------------
# Query functions
# ---------------------------------------------------------------------------
@pxt.query
def search_similar(query_text: str, limit: int = 10):
"""Find images matching a text description via CLIP similarity."""
sim = dataset.image.similarity(string=query_text)
return (
dataset.order_by(sim, asc=False)
.limit(limit)
.select(dataset.uuid, dataset.image, dataset.label, dataset.split, score=sim)
)
def find_similar_images(image_uuid: str, limit: int = 10):
"""Find visually similar images for deduplication and curation.
Not a @pxt.query because it needs to .collect() an intermediate result
to fetch the reference image before running similarity search.
"""
ref = dataset.where(dataset.uuid == image_uuid).select(dataset.image).collect()
if len(ref) == 0:
return []
ref_img = ref["image"][0]
sim = dataset.image.similarity(image=ref_img)
return (
dataset.order_by(sim, asc=False)
.limit(limit)
.select(dataset.uuid, dataset.image, dataset.label, dataset.split, sim)
.collect()
.to_pandas()
.to_dict("records")
)
@pxt.query
def list_by_label(label: str):
"""List all images with a given label."""
return dataset.where(dataset.label == label).select(
dataset.uuid, dataset.image, dataset.label, dataset.split, dataset.source
)
@pxt.query
def dataset_stats():
"""Count per label and split."""
return dataset.group_by(dataset.label, dataset.split).select(
dataset.label, dataset.split, count=dataset.uuid.count()
)
@pxt.query
def get_annotations(limit: int = 50):
"""Get images with their auto-generated annotations."""
cols = [dataset.uuid, dataset.image, dataset.label, dataset.split]
if hasattr(dataset, "detections"):
cols.append(dataset.detections)
if hasattr(dataset, "detection_labels"):
cols.append(dataset.detection_labels)
return dataset.limit(limit).select(*cols)
if __name__ == "__main__":
print("Schema initialized. Run: pxt serve datalab")
README
Data Lab -- ML Dataset Engineering
Import, auto-annotate, curate with embedding search, version, and export to PyTorch/Parquet. Your own Roboflow, self-hosted.
What It Replaces
| SaaS | Typical Cost | What Data Lab Covers |
|---|---|---|
| Labelbox | $10K+/yr | Auto-annotation, label management, versioning |
| Scale AI | $25K+/yr | Object detection annotation, structured labeling |
| Roboflow | $5K-50K/yr | Dataset curation, embedding search, export |
Workflow
Import → Auto-Annotate → Curate → Version → Export
│ │ │ │ │
│ DETR object CLIP sim Built-in PyTorch
│ detection + search + version Parquet
│ Vision LLM dedup control COCO
│ Label Studio
▼
datalab.dataset table
Quickstart
1. Install
uv sync --extra export
# Optional: uv sync --extra openai for vision LLM annotations
2. Initialize & Ingest
import schema # creates tables, computed columns, and indexes
import pixeltable as pxt
dataset = pxt.get_table('datalab.dataset')
dataset.insert([
{'image': 'path/to/image1.jpg', 'label': 'cat', 'split': 'train', 'source': 'coco'},
{'image': 'path/to/image2.jpg', 'label': 'dog', 'split': 'val', 'source': 'coco'},
])
3. Search & Curate
from schema import search_similar, find_similar_images, dataset_stats
# Find images matching a text description (@pxt.query → call .collect())
results = search_similar('a dog running on grass', limit=20).collect()
# Find duplicates / near-duplicates (plain helper — returns records directly)
similar = find_similar_images(image_uuid='...', limit=10)
# Dataset overview (@pxt.query → call .collect())
stats = dataset_stats().collect()
Export Formats
from export import export_to_pytorch, export_to_parquet, export_to_coco
# PyTorch DataLoader
train_ds = export_to_pytorch(split='train')
loader = torch.utils.data.DataLoader(train_ds, batch_size=32)
# Parquet (for Spark, DuckDB, pandas)
export_to_parquet('exports/dataset.parquet')
# COCO format (requires DETR detections)
export_to_coco()
API Server
uv run python schema.py # initialize tables
uv run pxt serve datalab # http://localhost:8000/docs
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/search |
CLIP similarity search by text |
| POST | /api/ingest |
Upload + insert an image |
| GET | /api/annotations |
Retrieve auto-generated annotations |
| GET | /api/stats |
Label/split distribution |
Auto-Annotation Pipeline
Always on (computed columns):
- DETR Object Detection --
facebook/detr-resnet-50detects objects and produces bounding boxes, labels, and scores. - CLIP Embeddings --
openai/clip-vit-base-patch32enables text-to-image and image-to-image similarity search.
Optional (requires OPENAI_API_KEY):
- Vision LLM Classification -- GPT-4o-mini classifies each image into a single category.
All annotations run automatically on insert -- no manual labeling step.
Project Structure
image-dataset/
├── schema.py # Table definitions, computed columns, query functions
├── export.py # PyTorch, Parquet, COCO export helpers
├── pyproject.toml # Dependencies and service routes
└── README.md