Annotation Format

The Pictograph JSON annotation format specification for computer vision labels.

Pictograph uses a JSON-based annotation format designed for flexibility and compatibility with popular ML frameworks. This page documents the format in detail.

Export File Structure

When you download a dataset or export, each image has a corresponding JSON file:

JSON
{
  "image": "filename.png",
  "image_url": "https://api.pictograph.io/api/v1/developer/images/{uuid}",
  "image_uuid": "7241531b-3b1b-4de6-a754-5750ef288754",
  "image_path": "/folder/path",
  "annotation_count": 3,
  "annotation_url": "https://app.pictograph.io/project/{id}/annotate/{filename}",
  "dataset": {
    "name": "My Dataset",
    "slug": "my-dataset"
  },
  "org": {
    "name": "My Organization",
    "slug": "my-org"
  },
  "annotations": [
    // Array of annotation objects (see below)
  ]
}

Annotation Types

Pictograph supports four annotation types:

Bounding Box (bbox)

Rectangular boxes defined by top-left corner and dimensions:

JSON
{
  "id": "ann-uuid-1234",
  "name": "person",
  "type": "bbox",
  "bounding_box": {
    "x": 100,
    "y": 200,
    "w": 150,
    "h": 300
  }
}

Coordinates are in absolute pixels. x, y is the top-left corner, w, h are width and height.

Polygon (polygon)

Closed shapes for instance segmentation:

JSON
{
  "id": "ann-uuid-5678",
  "name": "car",
  "type": "polygon",
  "polygon": {
    "path": [
      {"x": 100, "y": 200},
      {"x": 150, "y": 200},
      {"x": 175, "y": 250},
      {"x": 125, "y": 275},
      {"x": 100, "y": 250}
    ]
  }
}

Polygons are automatically closed - the last point connects back to the first. Points should be in clockwise order.

Polyline (polyline)

Open paths for lines, edges, and boundaries:

JSON
{
  "id": "ann-uuid-9012",
  "name": "road_edge",
  "type": "polyline",
  "polyline": {
    "path": [
      {"x": 0, "y": 500},
      {"x": 200, "y": 480},
      {"x": 400, "y": 450},
      {"x": 600, "y": 420}
    ]
  }
}

Unlike polygons, polylines don't close automatically. Useful for lanes, boundaries, and trajectories.

Keypoint (keypoint)

Single points for landmarks and pose estimation:

JSON
{
  "id": "ann-uuid-3456",
  "name": "nose",
  "type": "keypoint",
  "keypoint": {
    "x": 250,
    "y": 150
  }
}

Field Reference

Field Type Required Description
id string Yes Unique identifier (UUID)
name string Yes Class name (e.g., "person", "car")
type string Yes One of: bbox, polygon, polyline, keypoint
metadata object No Custom key-value metadata
created_by string No User ID who created the annotation

SDK Annotation Format

When saving annotations via the SDK, you can use a simplified format:

Python
# Bounding box
{
    "id": "ann-1",
    "name": "person",
    "type": "bbox",
    "bbox": [100, 200, 150, 300]  # [x, y, width, height]
}

# Polygon (flat array format)
{
    "id": "ann-2",
    "name": "car",
    "type": "polygon",
    "polygon": [[100, 200], [150, 200], [175, 250], [100, 250]]
}

# Keypoint
{
    "id": "ann-3",
    "name": "nose",
    "type": "keypoint",
    "keypoint": [250, 150]  # [x, y]
}

Converting to Other Formats

To COCO Format

Python
def pictograph_bbox_to_coco(bbox):
    """Convert Pictograph bbox to COCO format [x, y, width, height]."""
    return [bbox['x'], bbox['y'], bbox['w'], bbox['h']]

def pictograph_polygon_to_coco(polygon):
    """Convert Pictograph polygon to COCO segmentation (flat list)."""
    return [coord for point in polygon['path']
            for coord in (point['x'], point['y'])]

To YOLO Format

Python
def pictograph_bbox_to_yolo(bbox, image_width, image_height):
    """Convert to YOLO format (normalized center + size)."""
    x_center = (bbox['x'] + bbox['w'] / 2) / image_width
    y_center = (bbox['y'] + bbox['h'] / 2) / image_height
    width = bbox['w'] / image_width
    height = bbox['h'] / image_height
    return x_center, y_center, width, height

Validation Rules

The SDK validates annotations before saving:

  • Required fields: id, name, type
  • Type field: Must match the annotation's type-specific field
  • Coordinates: Must be valid numbers (integers or floats)
  • Polygons: Must have at least 3 points to form a valid shape
Python
from pictograph.exceptions import ValidationError

try:
    client.annotations.save("image-id", [
        {"id": "1", "type": "bbox"}  # Missing "name" field
    ])
except ValidationError as e:
    print(e)  # "Annotation 0 missing 'name' field"
Copied to clipboard