Quickstart: Import a Dataset with Pre-annotations¶
This guide walks you through importing an annotated dataset into Pixano in three steps: init your data directory, import your dataset, and serve the application. By the end you will have a running Pixano instance displaying images with bounding-box pre-annotations. See Installing Pixano for setup instructions.
Prerequisites¶
- Python 3.10+ installed
- Pixano installed:
Running from source with uv
Clone the repository and install with uv:
When running from source, prefix every command with uv run (e.g. uv run pixano init ./my_data).
Step 1 — Initialize Your Data Directory¶
Create the directory structure Pixano needs to store datasets, media files, and models:
This creates the following tree:
my_data/
library/ # LanceDB dataset storage
media/ # Imported media files (images, videos)
models/ # Model files for inference
Step 2 — Prepare and Import Your Dataset¶
Pixano imports datasets from a source directory organized by splits (e.g. train/, val/). Each split folder contains the media files and an optional metadata.jsonl file with pre-annotations.
Object Detection¶
You have images with bounding-box annotations and want to import them into Pixano.
Organize your source folder
street_objects/
train/
img_001.jpg
img_002.jpg
img_003.jpg
metadata.jsonl
val/
img_004.jpg
img_005.jpg
Two rules:
- Each subfolder is a split.
- A
metadata.jsonlfile inside a split folder provides pre-annotations for that split. Splits without one (likeval/above) import images with no pre-annotations.
Define your schema
The base Entity class has no domain-specific attributes, so you need a custom schema to attach fields like category to your objects. Save this file as schema.py:
# schema.py
from pixano.features import Entity
from pixano.datasets.workspaces import DefaultImageDatasetItem
class DetectedObject(Entity):
category: str = ""
class StreetObjectsDatasetItem(DefaultImageDatasetItem):
objects: list[DetectedObject] # overrides default Entity table
image_source: str = "" # item-level metadata
Extending Entity lets you add custom attributes to each detected object. Extending DefaultImageDatasetItem gives you the standard image tables out of the box — you only need to override what you want to customize.
What's in the default schema?
DefaultImageDatasetItem provides the following attributes. Fields you don't override are included automatically.
| Attribute | Type | Stored in |
|---|---|---|
image |
Image |
image table (view) |
objects |
list[Entity] |
objects table (entities) |
bboxes |
list[BBox] |
bboxes table (annotations) |
masks |
list[CompressedRLE] |
masks table (annotations) |
keypoints |
list[KeyPoints] |
keypoints table (annotations) |
In the example above, objects is overridden with list[DetectedObject] to add the category field. The image_source field does not correspond to any schema group, so it is stored as item-level metadata in the item table.
Write the metadata.jsonl
Create a metadata.jsonl file in each split folder that has pre-annotations. Each line is a JSON object describing one image:
street_objects/train/metadata.jsonl
{"image": "img_001.jpg", "image_source": "dashcam", "objects": {"bboxes": [[0.12, 0.25, 0.15, 0.30], [0.55, 0.10, 0.20, 0.45]], "category": ["car", "pedestrian"]}}
{"image": "img_002.jpg", "image_source": "dashcam", "objects": {"bboxes": [[0.30, 0.40, 0.08, 0.12]], "category": ["bicycle"]}}
{"image": "img_003.jpg", "image_source": "drone"}
Format rules:
imagematches the view field name in the schema and points to the image file in the same folder.image_sourceis stored as item-level metadata (it does not match any schema table name).objectsmatches the entity field name in the schema. Its value is a dict containing entity attributes and annotation data.bboxesinsideobjectsmatches the annotation table name. Each bounding box is[x, y, w, h].- Entity attributes (
category) are parallel arrays — one value per bounding box. - Items without
objects(likeimg_003.jpg) have no pre-annotations. - Coordinates are auto-detected as normalized when all values fall within [0, 1].
Format details
The metadata.jsonl format mirrors the schema structure:
- Top-level keys that match a view field (e.g.
image) are treated as view references — the value is the filename relative to the split folder. - Top-level keys that match an entity field (e.g.
objects) contain a nested dict. Keys inside this dict that match annotation table names (e.g.bboxes) are parsed as annotation data. All other keys are treated as entity attributes. - Top-level keys that don't match any view or entity field (e.g.
image_source) are stored as item-level metadata. - Entity attributes and annotation arrays must have the same length — each index corresponds to one object.
Run the import
pixano data import ./my_data ./street_objects \
--name "Street Objects" \
--schema ./schema.py:StreetObjectsDatasetItem
Common options:
| Option | Description |
|---|---|
--name |
Dataset name. Defaults to the source directory name. |
--type |
Dataset type: image (default), video, or vqa. |
--mode |
create (default, fails if exists), overwrite, or add (append). |
--schema |
Custom schema as path/to/file.py:ClassName. Uses the default schema if omitted. |
Alternative: build the dataset with Python
You can also build the dataset programmatically:
from pathlib import Path
from pixano.datasets import DatasetInfo
from pixano.datasets.builders import ImageFolderBuilder
builder = ImageFolderBuilder(
media_dir=Path("./my_data/media"),
library_dir=Path("./my_data/library"),
dataset_item=StreetObjectsDatasetItem,
info=DatasetInfo(
name="Street Objects",
description="Street scene images with object detection pre-annotations",
),
dataset_path="street_objects",
)
dataset = builder.build(mode="create")
print(f"Dataset built: {dataset.num_rows} items")
Try it: Pascal VOC 2007¶
The repository includes a ready-to-run example that downloads a sample of the Pascal VOC 2007 dataset and produces a Pixano-compatible folder. Use it to test the full import workflow with real data.
1. Generate the sample folder
pip install pillow # required by the script
python examples/voc/generate_sample.py ./voc_sample --num-samples 50
This downloads VOC 2007, samples 50 images per split, converts the XML annotations to metadata.jsonl files with normalized bounding boxes, and writes everything to ./voc_sample/.
The resulting folder looks like:
voc_sample/
train/
000032.jpg
000045.jpg
...
metadata.jsonl
validation/
000007.jpg
000019.jpg
...
metadata.jsonl
Each line in metadata.jsonl follows the format described above:
{"image": "000032.jpg", "objects": {"bboxes": [[0.078, 0.090, 0.756, 0.792], ...], "category": ["aeroplane", ...], "is_difficult": [false, ...]}}
2. Review the schema
The example schema is at examples/voc/schema.py:
from pixano.datasets.workspaces import DefaultImageDatasetItem
from pixano.features import Entity
class VOCObject(Entity):
category: str = ""
is_difficult: bool = False
class VOCDatasetItem(DefaultImageDatasetItem):
objects: list[VOCObject]
Same pattern as the street-objects example — a custom Entity subclass with domain-specific attributes, plugged into DefaultImageDatasetItem.
3. Import and serve
pixano init ./my_data
pixano data import ./my_data ./voc_sample \
--name "VOC 2007 Sample" \
--schema examples/voc/schema.py:VOCDatasetItem
pixano server run ./my_data
Open http://127.0.0.1:7492 to browse the imported VOC images and bounding boxes.
Step 3 — Launch the Server¶
Open http://127.0.0.1:7492 in your browser to explore and annotate your dataset.
--host— bind address (e.g.0.0.0.0for network access). Default:127.0.0.1.--port— port number. Default:7492.
Explore Your Data¶
The web UI lets you browse items, view annotations, and start annotating. You can also interact with your data programmatically.
Explore the REST API
While the server is running, interactive API documentation is available at:
- Swagger UI:
http://127.0.0.1:7492/docs - ReDoc:
http://127.0.0.1:7492/redoc
List all datasets and find the dataset ID:
The response includes an id field for each dataset. Use this ID in subsequent requests (referred to as <DATASET_ID> below).
Get items (first 3):
Get image views:
Get entities (objects with category):
Get bounding boxes for a specific item:
curl -s 'http://localhost:7492/annotations/<DATASET_ID>/bboxes?item_ids=<ITEM_ID>' | python -m json.tool
Browse dataset (same endpoint the UI uses):
Verify with the Python API
Load the dataset and inspect items to confirm the import worked:
Next Steps¶
- Build and query a dataset — deeper dive into dataset creation and querying
- Pre-annotation — more pre-annotation formats and workflows
- Semantic search — search your dataset using embeddings