Skip to main content

Pictograph SDK — agent-native computer vision annotation platform

Project description

Pictograph Python SDK

Official Python SDK for Pictograph Context Engine - a powerful computer vision annotation platform for creating high-quality training datasets.

Features

  • Simple, intuitive API - Get started with just a few lines of code
  • Dataset management - List, download, and manage annotation datasets
  • Image operations - Upload images, retrieve metadata, and manage assets
  • Annotation tools - Get, save, and delete annotations in Pictograph JSON format
  • Batch operations - Download entire datasets with parallel processing
  • Ecosystem interop - Bridge annotations (and local model predictions) to/from supervision Detections for trackers, annotators, COCO/YOLO loaders, and metrics
  • Visualization - Draw annotations onto images with draw_annotations (Pillow-only)
  • PyTorch adapter - client.datasets.as_pytorch(...) yields a ready-to-train torch.Dataset
  • Automatic retries - Built-in retry logic for transient failures
  • Rate limiting - Automatic handling of API rate limits
  • Type hints - Full type annotations for better IDE support

Installation

pip install pictograph

Optional extras pull in heavier dependencies only when you need them:

pip install 'pictograph[supervision]'   # supervision.Detections interop bridge
pip install 'pictograph[torch]'         # client.datasets.as_pytorch(...)
pip install 'pictograph[cli]'           # the `pictograph` command-line tool
pip install 'pictograph[all]'           # everything

Quick Start

from pictograph import Client

# Initialize the client with your API key
client = Client(api_key="pk_live_your_key_here")

# List all datasets
datasets = client.datasets.list()
for dataset in datasets:
    print(f"{dataset['name']}: {dataset['image_count']} images")

# Download a complete dataset
client.datasets.download(
    "dataset-uuid",
    output_dir="./my_dataset",
    mode="full"  # Download images + annotations
)

# Upload an image
result = client.images.upload(
    "dataset-uuid",
    "/path/to/image.jpg",
    folder_path="/train/images"
)
print(f"Uploaded: {result['image_id']}")

# Get annotations for an image
annotations = client.annotations.get("image-uuid")
for ann in annotations:
    print(f"{ann['name']}: {ann['type']}")

# Save annotations
client.annotations.save("image-uuid", [
    {
        "id": "ann-1",
        "name": "person",
        "type": "bbox",
        "bbox": [100, 200, 50, 80],  # [x, y, width, height]
        "confidence": 1.0
    }
])

Authentication

Get your API key from the Pictograph dashboard.

from pictograph import Client

client = Client(api_key="pk_live_your_key_here")

You can also configure the base URL and timeout:

client = Client(
    api_key="pk_live_your_key_here",
    base_url="https://your-instance.pictograph.io",
    timeout=60,
    max_retries=5
)

Usage

Working with Datasets

List Datasets

# List all datasets
datasets = client.datasets.list()

# With pagination
datasets = client.datasets.list(limit=50, offset=0)

Get Dataset Details

# Get basic info
dataset = client.datasets.get("dataset-uuid")
print(dataset['name'], dataset['image_count'])

# Get with images included
dataset = client.datasets.get(
    "dataset-uuid",
    include_images=True,
    images_limit=1000
)
for img in dataset['images']:
    print(img['filename'], img['image_url'])

List Images in Dataset

# List all images
images = client.datasets.list_images("dataset-uuid")

# Filter by annotation status
completed_images = client.datasets.list_images(
    "dataset-uuid",
    status="complete",
    limit=500
)

Download Dataset

# Download everything (images + annotations)
result = client.datasets.download(
    "dataset-uuid",
    output_dir="./dataset",
    mode="full",
    max_workers=20,  # Parallel downloads
    show_progress=True
)
print(f"Downloaded {result['images_downloaded']} images")

# Download only annotations
result = client.datasets.download(
    "dataset-uuid",
    output_dir="./annotations",
    mode="annotations_only"
)

# Download only completed images
result = client.datasets.download(
    "dataset-uuid",
    output_dir="./completed",
    mode="full",
    status_filter="complete"
)

Working with Images

Get Image Metadata

image = client.images.get("image-uuid")
print(image['filename'])
print(image['image_url'])  # CDN URL for viewing
print(image['annotation_count'])

Upload Image

# Simple upload
result = client.images.upload(
    "dataset-uuid",
    "/path/to/image.jpg"
)

# Upload to specific folder
result = client.images.upload(
    "dataset-uuid",
    "/path/to/image.jpg",
    folder_path="/train/images",
    filename="custom_name.jpg"
)

print(result['image_id'])

Delete Image

# Archive (soft delete)
client.images.delete("image-uuid")

# Permanent delete
client.images.delete("image-uuid", permanent=True)

Working with Annotations

Pictograph uses a JSON format that supports multiple annotation types:

  • bbox - Bounding boxes [x, y, width, height]
  • polygon - Polygons [[x1, y1], [x2, y2], ...]
  • polyline - Polylines [[x1, y1], [x2, y2], ...]
  • keypoint - Single points [x, y]

Get Annotations

annotations = client.annotations.get("image-uuid")
for ann in annotations:
    print(ann['id'], ann['name'], ann['type'])
    if ann['type'] == 'bbox':
        x, y, width, height = ann['bbox']
        print(f"  Bbox: ({x}, {y}) - {width}x{height}")

Save Annotations

# Bounding box
annotations = [
    {
        "id": "ann-1",
        "name": "person",
        "type": "bbox",
        "bbox": [100, 200, 50, 80],
        "confidence": 1.0
    }
]
result = client.annotations.save("image-uuid", annotations)
print(result['new_count'])

# Polygon
annotations = [
    {
        "id": "ann-2",
        "name": "car",
        "type": "polygon",
        "polygon": [[10, 20], [30, 40], [50, 60], [10, 20]],
        "confidence": 1.0
    }
]
client.annotations.save("image-uuid", annotations)

# Multiple annotations
annotations = [
    {"id": "ann-1", "name": "person", "type": "bbox", "bbox": [100, 200, 50, 80]},
    {"id": "ann-2", "name": "car", "type": "polygon", "polygon": [[10,20], [30,40], [50,60], [10,20]]},
    {"id": "ann-3", "name": "road", "type": "polyline", "polyline": [[0,100], [50,100], [100,100]]},
    {"id": "ann-4", "name": "landmark", "type": "keypoint", "keypoint": [150, 200]}
]
client.annotations.save("image-uuid", annotations)

Helper Methods

# Create properly formatted annotations
bbox = client.annotations.create_bbox(
    "ann-1",
    "person",
    [100, 200, 50, 80],
    confidence=0.95
)

polygon = client.annotations.create_polygon(
    "ann-2",
    "car",
    [[10, 20], [30, 40], [50, 60], [10, 20]]
)

polyline = client.annotations.create_polyline(
    "ann-3",
    "road",
    [[0, 100], [50, 100], [100, 100]]
)

keypoint = client.annotations.create_keypoint(
    "ann-4",
    "landmark",
    [150, 200]
)

# Save them all
client.annotations.save("image-uuid", [bbox, polygon, polyline, keypoint])

Delete Annotations

# Delete all annotations for an image
result = client.annotations.delete("image-uuid")
print(result['deleted_count'])

Context Manager

Use the client as a context manager to ensure proper cleanup:

with Client(api_key="pk_live_your_key_here") as client:
    datasets = client.datasets.list()
    # Client session automatically closed when done

Error Handling

The SDK raises specific exceptions for different error types:

from pictograph import Client, AuthenticationError, RateLimitError, NotFoundError

client = Client(api_key="pk_live_your_key_here")

try:
    dataset = client.datasets.get("invalid-uuid")
except AuthenticationError:
    print("Invalid API key")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")
except NotFoundError:
    print("Dataset not found")
except Exception as e:
    print(f"Unexpected error: {e}")

Advanced Usage

Batch Processing

from concurrent.futures import ThreadPoolExecutor

# Upload multiple images in parallel
def upload_image(image_path):
    return client.images.upload("dataset-uuid", image_path)

image_paths = ["img1.jpg", "img2.jpg", "img3.jpg"]
with ThreadPoolExecutor(max_workers=10) as executor:
    results = list(executor.map(upload_image, image_paths))

print(f"Uploaded {len(results)} images")

Custom Metadata

# Add custom metadata to annotations
annotation = client.annotations.create_bbox(
    "ann-1",
    "person",
    [100, 200, 50, 80],
    metadata={
        "annotator": "john@example.com",
        "difficulty": "easy",
        "verified": True
    }
)
client.annotations.save("image-uuid", [annotation])

Ecosystem interop (supervision)

supervision is the hub of the open computer-vision tooling ecosystem — trackers (ByteTrack, BoT-SORT), box/mask/label annotators, dataset loaders (COCO, YOLO, Pascal VOC), and metrics all speak its Detections container. pictograph.interop bridges Pictograph annotations — and your local model's predictions — straight into it and back (pip install 'pictograph[supervision]'):

import supervision as sv
from pictograph import Client, get_model, to_supervision, from_supervision

client = Client()

# Pictograph annotations -> supervision.Detections (masks when a resolution is given)
annotations = client.annotations.get("image-uuid")
detections = to_supervision(annotations, resolution_wh=(1920, 1080))

# ...now use the whole supervision toolbox: annotate, track, measure...
labeled = sv.LabelAnnotator().annotate(sv.cv2.imread("photo.jpg"), detections)

# A local model's predictions are ordinary annotations, so they bridge too:
result = get_model("my-detector").predict("photo.jpg")
detections = to_supervision(result.predictions)

# supervision.Detections -> Pictograph annotations (save them back, or draw them)
new_annotations = from_supervision(detections)
client.annotations.save("image-uuid", [a.model_dump() for a in new_annotations])

bbox and polygon annotations map to detections (polygons contribute their enclosing box, plus a rasterized mask with holes when resolution_wh is supplied); polyline and keypoint annotations — which have no box/mask representation — are skipped.

Whole datasets

client.datasets.as_supervision(name) materializes an entire dataset (images + annotations) into a supervision.DetectionDataset, unlocking local format export, splitting, and evaluation with no further server round-trips:

ds = client.datasets.as_supervision("road-signs")   # downloads images to a cache dir

# Export to any format the ecosystem understands — locally, no server round-trip:
ds.as_yolo("out/images", "out/labels", "out/data.yaml")
ds.as_coco("out/images", "out/annotations.json")
ds.as_pascal_voc("out/images", "out/annotations")

# Split for training, or evaluate a model:
train, val = ds.split(split_ratio=0.8, random_state=42)

The dataset's classes is the union of the project's configured classes and every class seen on an annotation. Pass with_masks=True for segmentation-format export.

Rate Limits

The SDK automatically handles rate limits:

  • Free tier: 1,000 requests/hour
  • Core tier: 5,000 requests/hour
  • Pro tier: 20,000 requests/hour
  • Enterprise tier: 100,000 requests/hour

If you hit a rate limit, the SDK will automatically wait and retry (if retry time < 2 minutes).

Requirements

  • Python 3.8+
  • requests >= 2.31.0
  • Pillow >= 10.0.0
  • tqdm >= 4.65.0

Support

License

MIT License - see LICENSE file for details.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pictograph-1.11.0.tar.gz (326.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pictograph-1.11.0-py3-none-any.whl (271.4 kB view details)

Uploaded Python 3

File details

Details for the file pictograph-1.11.0.tar.gz.

File metadata

  • Download URL: pictograph-1.11.0.tar.gz
  • Upload date:
  • Size: 326.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for pictograph-1.11.0.tar.gz
Algorithm Hash digest
SHA256 8396cac383a5c5a4773009b10a4bd7af3d7cbc4562d5ffa5598eaa7634e50604
MD5 aa4378929baf1fdba81b5e05cceaa5a7
BLAKE2b-256 20a5553e1ae464abcb378d164c73e1eca1512cb8950eb59685b0889d9d57f479

See more details on using hashes here.

File details

Details for the file pictograph-1.11.0-py3-none-any.whl.

File metadata

  • Download URL: pictograph-1.11.0-py3-none-any.whl
  • Upload date:
  • Size: 271.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for pictograph-1.11.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3639e5f3372a512daa675f9facac9695b57dcc904387a3901f08208ebf6f1215
MD5 2cde06a75f12d74e6c6b1f5580398a29
BLAKE2b-256 da9a1c30d822c7c34d17c81e567d5f4e388560c4756990dd16db87724120bfaa

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page