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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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:
# 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
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
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
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"