Sign in Get started

Local & Framework

Run Pictograph datasets, annotations, and trained models locally — export to any of 8 formats, convert COCO/YOLO/Pascal VOC offline, feed a PyTorch training loop, run local ONNX inference, evaluate a model's precision/recall, and draw annotations, all with Pictograph's own tooling.

View as Markdown

The Pictograph SDK is a complete, self-contained computer-vision toolkit — you don’t need to bolt on a third-party library to get your data into a training loop, convert it between COCO/YOLO, export it, or visualize it. Everything below runs locally with Pictograph’s own tooling. Only PyTorch and the local ONNX runtime are optional extras, and only because they’re heavy.

pip install 'pictograph[torch]'         # client.datasets.as_pytorch(...)
pip install 'pictograph[inference]'     # local ONNX inference: get_model(...).predict(...)
pip install 'pictograph[all]'           # everything

Export to any format

Export a dataset to a downloadable ZIP in any of the 12 Pictograph formats — pictograph, coco, yolo, yolo_obb, yolo_pose, dota, pascal_voc, darwin, cvat, datumaro, labelme, csv — built by Pictograph’s own server-side converters, no extra dependency. yolo_obb and dota carry oriented (rotated) boxes; coco and yolo_pose carry keypoint skeletons (pose).

from pictograph import Client

client = Client()
export = client.exports.create("road-signs", "road-signs-coco", format="coco", include_images=True)
client.exports.download("road-signs", export.name, "road-signs-coco.zip")

Or one command from the CLI:

pictograph datasets export road-signs --format yolo --include-images -o ./out

COCO, YOLO & Pascal VOC converters (offline)

pictograph.formats converts between external COCO / YOLO / Pascal VOC annotations and Pictograph’s typed annotation models entirely on your machine — no API call, no third-party library. Use it to bring an existing dataset into Pictograph’s models (then save it with client.annotations.save), or to emit those formats from annotations you already hold. Unlike the ZIP export above (a whole dataset, server-side), these are pure in-memory functions over the same annotation objects the rest of the SDK uses.

from pictograph.formats import from_coco, to_yolo

# Parse a local COCO file into Pictograph's typed models.
imp = from_coco("instances_val.json")   # -> CocoImport(annotations, class_names)

# Or emit YOLO label text for one image (normalized to its pixel size).
yolo_txt = to_yolo(imp.annotations["a.jpg"], imp.class_names, image_width=640, image_height=480)

from_coco / to_coco handle bounding boxes (exact round-trip), polygon segmentation, and keypoints; from_yolo / to_yolo handle detection and segmentation labels; from_pascal_voc / to_pascal_voc handle the Pascal VOC per-image XML. For hole-accurate COCO (RLE) or a downloadable ZIP, use the server-side export above.

To go from a local file to annotations saved on a dataset in one call — create missing classes, match images by filename, chunked bulk-save, and a per-image report — use the import pipelines (the dataset must already hold the images the file references):

from pictograph import Client
from pictograph.pipelines import import_coco_annotations

client = Client()
report = import_coco_annotations(client, "road-signs", "instances_val.json")
print(report.images_saved, "images annotated;", len(report.unmatched_files), "unmatched")

Or one command from the CLI:

pictograph datasets import-coco road-signs instances_val.json

PyTorch

client.datasets.as_pytorch(name) returns a map-style torch.utils.data.Dataset that plugs straight into a DataLoader. Each item is an (image, target) pair, where target follows the torchvision detection convention (boxes in xyxy, integer labels, area, iscrowd, image_id, and the raw annotations). Images download lazily on first access and are cached.

from torch.utils.data import DataLoader
from pictograph import Client

client = Client()
dataset = client.datasets.as_pytorch("road-signs")
loader = DataLoader(dataset, batch_size=8, collate_fn=lambda batch: tuple(zip(*batch)))

for images, targets in loader:
    ...  # your training step

Visualization

draw_annotations renders any Pictograph annotations onto an image using only Pillow, which is a base dependency, so it works out of the box — no third-party renderer. All four annotation types render, and each class gets a stable, distinct color.

from pictograph import Client, draw_annotations

client = Client()
annotations = client.annotations.get("image-uuid")
annotated = draw_annotations("photo.jpg", annotations)
annotated.save("photo.annotated.png")

Local inference

Trained Pictograph models run on your own machine with the inference extra. get_model(...).predict(...) returns an InferenceResult whose .predictions are ordinary Pictograph annotations — so you can draw them, save them back, or export them with everything above. See Deployments for the hosted inference endpoint option.

from pictograph import get_model, draw_annotations

model = get_model("my-detector")          # downloads + caches the ONNX weights
result = model.predict("photo.jpg")
draw_annotations("photo.jpg", result.predictions).save("photo.pred.png")

Local model evaluation

pictograph.metrics scores a model against a labeled set entirely on your machine — no server round-trip, no third-party library. evaluate_detections matches predicted annotations to ground truth by IoU and returns per-class and overall precision, recall, F1, and average precision — including mAP (result.mean_average_precision, the standard detection metric) and per-class average_precision. Predictions can come from client.auto_annotate, a deployed model’s /predict, or local inference above.

from pictograph import Client
from pictograph.metrics import evaluate_detections

client = Client()
ground_truth = {img_id: client.annotations.get(img_id) for img_id in image_ids}
predictions = {img_id: run_my_model(img_id) for img_id in image_ids}

result = evaluate_detections(predictions, ground_truth, iou_threshold=0.5)
print(result.precision, result.recall, result.f1)
for name, m in result.per_class.items():
    print(name, m.precision, m.recall, m.support)

confusion_matrix() (same module) returns a class-agnostic confusion matrix that surfaces cross-class confusion. Want Pictograph to run the inference for you and store the result? Use the server-side client.model_evaluations — the metric math is identical, so a server run and a local run on the same data agree.

Active learning — rank by uncertainty

pictograph.metrics.rank_by_uncertainty turns a model’s predictions into a review queue ordered by how unsure the model is, so a human labels the most-informative images first — the highest label-efficiency order, Pictograph’s native answer to the active-learning loop. Every score is in [0, 1] where higher means more uncertain. It reads each prediction’s confidence, so it works on output from client.auto_annotate, a deployment /predict, or local inference.

from pictograph.metrics import rank_by_uncertainty

# predictions for your UNLABELED / lightly-labeled images
predictions = {img_id: run_my_model(img_id) for img_id in unlabeled_image_ids}

for item in rank_by_uncertainty(predictions, method="least_confidence")[:20]:
    print(item.image_key, round(item.score, 3), item.num_predictions)

Four strategies aggregate an image’s per-detection confidences: least_confidence (default, 1 − mean(confidence)), min_confidence (1 − min), margin (closeness to the 0.5 decision boundary), and entropy. An image with no predictions scores 1.0 by default — the model found nothing, which is itself worth a look (pass empty_score=0.0 to sink them instead).

For the whole loop in one call — enumerate a dataset’s images, run your inference, and rank — use the pictograph.pipelines.rank_dataset_by_uncertainty pipeline. It returns an ActiveLearningReport (.queue ranked most-uncertain first, plus a per-image .failures list so one bad inference never shrinks the queue silently):

from pictograph import Client
from pictograph.pipelines import rank_dataset_by_uncertainty

client = Client()

def predict(image):                      # bring your own inference
    return run_my_model(image.filename)   # -> list[Annotation]

report = rank_dataset_by_uncertainty(
    client, "my-dataset", predict, unlabeled_only=True, max_images=500,
)
for item in report.queue[:20]:
    print(item.image_key, round(item.score, 3), item.num_predictions)
Copied to clipboard