Annotations
Annotation resource
Access annotations through client.annotations.
Prerequisites
- Vi SDK installed with authentication configured
- A secret key for API authentication
- Understanding of Vi SDK basics
- Familiarity with annotation formats — phrase grounding, VQA, or freeform (coming soon)
Methods
list()
List annotations for an asset.
# Basic listing
annotations = client.annotations.list(
dataset_id="dataset_abc123",
asset_id="asset_xyz789"
)
for annotation in annotations.items:
print(f"ID: {annotation.annotation_id}")
print(f"Type: {annotation.spec.bound_type}")# With custom pagination
from vi.api.types import PaginationParams
annotations = client.annotations.list(
dataset_id="dataset_abc123",
asset_id="asset_xyz789",
pagination=PaginationParams(page_size=100)
)
for annotation in annotations.items:
print(f"{annotation.annotation_id}: {annotation.spec.bound_type}")# Using dict for pagination
annotations = client.annotations.list(
dataset_id="dataset_abc123",
asset_id="asset_xyz789",
pagination={"page_size": 100}
)# Iterate through all pages
for page in client.annotations.list(
dataset_id="dataset_abc123",
asset_id="asset_xyz789"
):
for annotation in page.items:
print(f"{annotation.annotation_id}: {annotation.spec.bound_type}")# Collect all annotations for an asset
all_annotations = list(
client.annotations.list(
dataset_id="dataset_abc123",
asset_id="asset_xyz789"
).all_items()
)
print(f"Total annotations: {len(all_annotations)}")Parameters:
| Parameter | Type | Description | Default |
|---|---|---|---|
dataset_id | str | Dataset identifier | Required |
asset_id | str | Asset identifier | Required |
pagination | PaginationParams | dict | Pagination settings | PaginationParams() |
Returns: PaginatedResponse[Annotation]
get()
Get a specific annotation.
# Basic usage
annotation = client.annotations.get(
dataset_id="dataset_abc123",
asset_id="asset_xyz789",
annotation_id="annotation_xyz789"
)
print(f"Type: {annotation.spec.bound_type}")
print(f"Content: {annotation.spec.contents}")# Access caption content
annotation = client.annotations.get(
dataset_id="dataset_abc123",
asset_id="asset_xyz789",
annotation_id="annotation_xyz789"
)
if hasattr(annotation.spec.contents, 'contents'):
# It's a Caption
print(f"Caption: {annotation.spec.contents.contents}")# Access bounding box content
annotation = client.annotations.get(
dataset_id="dataset_abc123",
asset_id="asset_xyz789",
annotation_id="annotation_xyz789"
)
if hasattr(annotation.spec.contents, 'bound'):
# It's a PhraseGroundingBoundingBox
print(f"Bounds: {annotation.spec.contents.bound}")
print(f"Target span: {annotation.spec.contents.target}")# Display detailed information
annotation = client.annotations.get(
dataset_id="dataset_abc123",
asset_id="asset_xyz789",
annotation_id="annotation_xyz789"
)
annotation.info() # Prints formatted annotation summaryParameters:
| Parameter | Type | Description |
|---|---|---|
dataset_id | str | Dataset identifier |
asset_id | str | Asset identifier |
annotation_id | str | Annotation identifier |
Returns: Annotation
upload()
Upload annotations to a dataset.
# Upload single JSONL file
result = client.annotations.upload(
dataset_id="dataset_abc123",
paths="annotations.jsonl",
wait_until_done=True
)
print(f"✓ Imported: {result.total_annotations}")# Upload folder of annotation files
result = client.annotations.upload(
dataset_id="dataset_abc123",
paths="./annotations/",
wait_until_done=True
)
print(result.summary())# Upload multiple specific files
result = client.annotations.upload(
dataset_id="dataset_abc123",
paths=["batch1.jsonl", "batch2.jsonl"],
wait_until_done=True
)# Upload and verify count
def upload_and_verify(dataset_id: str, annotations_path: str) -> bool:
"""Upload annotations and verify success."""
# Count annotations in file
with open(annotations_path) as f:
local_count = sum(1 for _ in f)
# Upload
result = client.annotations.upload(
dataset_id=dataset_id,
paths=annotations_path,
wait_until_done=True
)
# Verify
print(f"Local: {local_count}")
print(f"Imported: {result.total_annotations}")
return result.total_annotations == local_count
success = upload_and_verify("dataset_abc123", "annotations.jsonl")# Create annotations programmatically and upload
import json
from pathlib import Path
def create_and_upload_annotations(dataset_id: str, images_dir: str):
"""Create annotations for images and upload."""
annotations = []
for image_path in Path(images_dir).glob("*.jpg"):
annotation = {
"asset_id": image_path.stem,
"caption": f"Image of {image_path.stem}",
"grounded_phrases": []
}
annotations.append(annotation)
# Save to JSONL
output_file = "generated_annotations.jsonl"
with open(output_file, "w") as f:
for annotation in annotations:
json.dump(annotation, f)
f.write("\n")
# Upload
result = client.annotations.upload(
dataset_id=dataset_id,
paths=output_file,
wait_until_done=True
)
return result
result = create_and_upload_annotations("dataset_abc123", "./images")Parameters:
| Parameter | Type | Description | Default |
|---|---|---|---|
dataset_id | str | Dataset identifier | Required |
paths | str | list[str] | File/folder paths | Required |
wait_until_done | bool | Wait for completion | True |
Returns: AnnotationUploadResult
Dataset type support
Annotation upload is currently supported for:
- ✅ Phrase Grounding datasets — Upload captions with grounded phrases and bounding boxes
- ✅ VQA datasets — Upload question-answer pairs
- 🚧 Freeform datasets — Coming soon
Upload format is Vi JSONL for all dataset types.
delete()
Delete an annotation.
# Delete single annotation
deleted = client.annotations.delete(
dataset_id="dataset_abc123",
asset_id="asset_xyz789",
annotation_id="annotation_xyz789"
)# Delete all annotations for an asset
annotations = client.annotations.list(
dataset_id="dataset_abc123",
asset_id="asset_xyz789"
)
for annotation in annotations.items:
client.annotations.delete(
dataset_id="dataset_abc123",
asset_id="asset_xyz789",
annotation_id=annotation.annotation_id
)
print(f"Deleted: {annotation.annotation_id}")Parameters:
| Parameter | Type | Description |
|---|---|---|
dataset_id | str | Dataset identifier |
asset_id | str | Asset identifier |
annotation_id | str | Annotation identifier |
Returns: DeletedAnnotation
wait_until_done()
Wait for an import session to complete.
# Basic usage
status = client.annotations.wait_until_done(
dataset_id="dataset_abc123",
annotation_import_session_id="session_id"
)
print(f"Total: {status.status.annotations.status_count}")# After async upload
result = client.annotations.upload(
dataset_id="dataset_abc123",
paths="annotations.jsonl",
wait_until_done=False
)
# Do other work...
# Then wait for completion
status = client.annotations.wait_until_done(
dataset_id="dataset_abc123",
annotation_import_session_id=result.session_id
)
print(f"Import complete: {status.status.annotations.status_count}")Parameters:
| Parameter | Type | Description |
|---|---|---|
dataset_id | str | Dataset identifier |
annotation_import_session_id | str | Session identifier |
Returns: AnnotationImportSession
download()
Download annotations without assets.
# Download with default settings (ViJsonl format)
result = client.annotations.download(
dataset_id="dataset_abc123"
)
print(result.summary())# Download in COCO format
from vi.api.resources.datasets.types import (
DatasetExportFormat,
DatasetExportSettings
)
result = client.annotations.download(
dataset_id="dataset_abc123",
export_settings=DatasetExportSettings(
format=DatasetExportFormat.COCO
),
save_dir="./annotations"
)
print(f"Downloaded to: {result.save_dir}")# Download in YOLO format
result = client.annotations.download(
dataset_id="dataset_abc123",
export_settings={"format": DatasetExportFormat.YOLO_DARKNET},
save_dir="./yolo_annotations"
)# Download using existing export
result = client.annotations.download(
dataset_id="dataset_abc123",
dataset_export_id="export_xyz789"
)# Download without progress bars (CI/CD environments)
result = client.annotations.download(
dataset_id="dataset_abc123",
show_progress=False,
overwrite=True
)# Download and process annotations
from pathlib import Path
import json
result = client.annotations.download(
dataset_id="dataset_abc123",
export_settings={"format": DatasetExportFormat.VI_JSONL},
save_dir="./annotations"
)
# Process downloaded annotations
annotation_files = Path(result.save_dir).glob("*.jsonl")
for file in annotation_files:
with open(file) as f:
for line in f:
annotation = json.loads(line)
print(f"Asset: {annotation['asset_id']}")Parameters:
| Parameter | Type | Description | Default |
|---|---|---|---|
dataset_id | str | Dataset identifier | Required |
dataset_export_id | str | None | Existing export ID | None |
export_settings | DatasetExportSettings | dict | None | Export format configuration | None (ViJsonl) |
save_dir | Path | str | Directory to save annotations | ~/.datature/vi/annotations/ |
overwrite | bool | Overwrite existing files | False |
show_progress | bool | Show progress bars | True |
Returns: DatasetDownloadResult
Supported export formats
Available formats depend on your dataset type:
Phrase Grounding datasets:
CsvFourCorner— CSV with 4-corner coordinatesCsvWidthHeight— CSV with width/height formatCoco— COCO JSON formatPascalVoc— Pascal VOC XML formatYoloDarknet— YOLO Darknet TXT formatYoloKerasPytorch— YOLO Keras/PyTorch formatViJsonl— Vi JSONL format (default)ViTfrecord— Vi TFRecord formatVQA datasets:
ViJsonl— Vi JSONL format (default)ViTfrecord— Vi TFRecord formatFreeform datasets: 🚧 Coming soon
ViJsonl— Vi JSONL format (default)The
ViFullformat is not supported for annotation-only downloads.
Performance tip
Downloading annotations without assets is much faster than downloading the full dataset. Use this method when you only need to update or review annotations, or when working with large datasets where asset files are not needed.
Format compatibility
Using incompatible export formats will raise a
ViValidationError. For example, COCO format is not supported for VQA or freeform datasets. Make sure to use formats compatible with your dataset type.
Response types
Annotation
Main annotation response object.
from vi.api.resources.datasets.annotations.responses import Annotation| Property | Type | Description |
|---|---|---|
annotation_id | str | Unique identifier |
asset_id | str | Parent asset ID |
organization_id | str | Organization ID |
dataset_id | str | Dataset ID |
spec | AnnotationSpec | Annotation specification |
metadata | ResourceMetadata | Metadata |
self_link | str | API link |
etag | str | Entity tag |
Methods:
| Method | Returns | Description |
|---|---|---|
info() | None | Display formatted annotation information |
AnnotationSpec
from vi.api.resources.datasets.annotations.responses import AnnotationSpec| Property | Type | Description |
|---|---|---|
bound_type | str | Boundary type (box, polygon, etc.) |
tag | int | Tag identifier |
aggregation | AnnotationAggregation | Aggregation info |
contents | AnnotationContent | Annotation content |
AnnotationAggregation
from vi.api.resources.datasets.annotations.responses import AnnotationAggregationWhole
from vi.api.resources.datasets.annotations.responses import Whole| Property | Type | Description |
|---|---|---|
id | str | Parent annotation ID |
ThisPart
from vi.api.resources.datasets.annotations.responses import ThisPart| Property | Type | Description |
|---|---|---|
role | Roles | Part role type |
AnnotationContent
Union type for annotation content variants.
from vi.api.resources.datasets.annotations.responses import AnnotationContent
AnnotationContent = Caption | PhraseGroundingBoundingBoxCaption
Caption content for annotations.
from vi.api.resources.datasets.annotations.responses import Caption| Property | Type | Description |
|---|---|---|
contents | str | Caption text |
PhraseGroundingBoundingBox
Bounding box for phrase grounding.
from vi.api.resources.datasets.annotations.responses import PhraseGroundingBoundingBox| Property | Type | Description |
|---|---|---|
bound | list[list[float]] | 4 coordinate pairs [[x1,y1], [x2,y1], [x2,y2], [x1,y2]] |
target | tuple[int, int] | Phrase span (start_index, end_index) |
AnnotationUploadResult
Result from uploading annotations.
| Property | Type | Description |
|---|---|---|
session_id | str | Import session ID |
total_annotations | int | Total annotations imported |
session | AnnotationImportSession | Session details |
Methods:
| Method | Returns | Description |
|---|---|---|
summary() | str | Get summary string |
AnnotationImportSession
from vi.api.resources.datasets.annotations.responses import AnnotationImportSession| Property | Type | Description |
|---|---|---|
user | str | Initiating user ID |
organization_id | str | Organization ID |
dataset_id | str | Dataset ID |
annotation_import_session_id | str | Session identifier |
self_link | str | API link |
etag | str | Entity tag |
metadata | ResourceMetadata | Metadata |
spec | AnnotationImportSpec | Import specification |
status | AnnotationImportSessionStatus | Session status |
AnnotationImportSpec
from vi.api.resources.datasets.annotations.responses import AnnotationImportSpec| Property | Type | Description |
|---|---|---|
upload_before | int | Upload deadline timestamp |
failure_policies | AnnotationImportFailurePolicy | Failure handling |
source | AnnotationImportSource | Import source type |
AnnotationImportFailurePolicy
from vi.api.resources.datasets.annotations.responses import AnnotationImportFailurePolicy| Property | Type | Description |
|---|---|---|
on_bad_annotation | FailurePolicy | Bad annotation handling |
on_bad_file | FailurePolicy | Bad file handling |
on_overwritten | FailurePolicy | Overwrite handling |
AnnotationImportSessionStatus
from vi.api.resources.datasets.annotations.responses import AnnotationImportSessionStatus| Property | Type | Description |
|---|---|---|
conditions | list[AnnotationImportSessionCondition] | Session conditions |
files | AnnotationFileStatus | File status |
annotations | AnnotationStatus | Annotation status |
reason | str | None | Status reason |
AnnotationImportSessionCondition
from vi.api.resources.datasets.annotations.responses import AnnotationImportSessionCondition| Property | Type | Description |
|---|---|---|
condition | str | Condition name |
status | ConditionStatus | Condition status |
last_transition_time | int | float | Transition timestamp |
reason | ConditionReason | None | Condition reason |
AnnotationFileStatus
from vi.api.resources.datasets.annotations.responses import AnnotationFileStatus| Property | Type | Description |
|---|---|---|
page_count | int | Pages processed |
total_size_bytes | int | Total size in bytes |
status_count | dict[str, int] | Status distribution |
AnnotationStatus
from vi.api.resources.datasets.annotations.responses import AnnotationStatus| Property | Type | Description |
|---|---|---|
status_count | dict[str, int] | Status distribution |
ExportedAnnotations
from vi.api.resources.datasets.annotations.responses import ExportedAnnotations| Property | Type | Description |
|---|---|---|
kind | str | Resource kind |
organization_id | str | Organization ID |
dataset_id | str | Dataset ID |
dataset_export_id | str | Export identifier |
spec | dict[str, Any] | Export specification |
status | ExportedAnnotationsStatus | Export status |
metadata | ResourceMetadata | Metadata |
ExportedAnnotationsStatus
from vi.api.resources.datasets.annotations.responses import ExportedAnnotationsStatus| Property | Type | Description |
|---|---|---|
conditions | list[dict] | Status conditions |
download_url | ExportedAnnotationsDownloadUrl | Download URL |
ExportedAnnotationsDownloadUrl
from vi.api.resources.datasets.annotations.responses import ExportedAnnotationsDownloadUrl| Property | Type | Description |
|---|---|---|
url | str | Download URL |
expires_at | int | Expiration timestamp |
Enums
Roles
from vi.api.resources.datasets.annotations.responses import Roles| Value | Description |
|---|---|
CAPTION | Caption annotation |
PHRASE_GROUNDING_BOUNDING_BOX | Phrase grounding bounding box |
FailurePolicy
from vi.api.resources.datasets.annotations.responses import FailurePolicy| Value | Description |
|---|---|
WARN | Log warning and continue |
REJECT_FILE | Reject the file |
REJECT_SESSION | Reject entire session |
AnnotationImportSource
from vi.api.resources.datasets.annotations.responses import AnnotationImportSource| Value | Description |
|---|---|
UPLOADED_INDIVIDUAL_FILES | Uploaded individual files |
ConditionReason
from vi.api.resources.datasets.annotations.responses import ConditionReason| Value | Description |
|---|---|
CANCELLED_BY_USER | Cancelled by user |
USER_UPLOAD_ERRORED | Upload error |
Annotation formats
Phrase Grounding format
{
"asset_id": "asset_123",
"caption": "A cat sitting on a couch",
"grounded_phrases": [
{
"phrase": "cat",
"bbox": [0.1, 0.2, 0.5, 0.7]
},
{
"phrase": "couch",
"bbox": [0.6, 0.2, 0.9, 0.7]
}
]
}Fields:
| Field | Type | Description |
|---|---|---|
asset_id | str | Asset identifier (required) |
caption | str | Caption text |
grounded_phrases | list[GroundedPhrase] | List of grounded phrases |
GroundedPhrase Schema:
| Field | Type | Description |
|---|---|---|
phrase | str | Phrase text |
bbox | list[float] | Normalized bbox [x_min, y_min, x_max, y_max] |
VQA Format
{
"asset_id": "asset_123",
"contents": {
"interactions": [
{
"question": "What is the cat doing?",
"answer": "Sitting on a couch"
}
]
}
}Fields:
| Field | Type | Description |
|---|---|---|
asset_id | str | Asset identifier (required) |
contents.interactions | list[Interaction] | Q&A pairs |
Interaction Schema:
| Field | Type | Description |
|---|---|---|
question | str | Question text |
answer | str | Answer text |
Freeform Format
Coming soon
Freeform annotation format support is currently in development. Check back for updates on the format specification and upload capabilities.
Coordinate system
Bounding box coordinates are normalized to [0, 1] range:
x_min: Left edge (0 = left, 1 = right)y_min: Top edge (0 = top, 1 = bottom)x_max: Right edgey_max: Bottom edge
Convert to Pixels:
def to_pixels(bbox: list[float], width: int, height: int) -> list[int]:
"""Convert normalized bbox to pixel coordinates."""
x_min, y_min, x_max, y_max = bbox
return [
int(x_min * width),
int(y_min * height),
int(x_max * width),
int(y_max * height)
]
# Example
bbox = [0.1, 0.2, 0.5, 0.7]
pixel_bbox = to_pixels(bbox, 1920, 1080)
# Result: [192, 216, 960, 756]Convert from Pixels:
def from_pixels(bbox: list[int], width: int, height: int) -> list[float]:
"""Convert pixel coordinates to normalized bbox."""
x_min, y_min, x_max, y_max = bbox
return [
x_min / width,
y_min / height,
x_max / width,
y_max / height
]Related resources
- Upload annotations — Manual annotation upload guide with format specs
- Download data — Export annotations and assets from datasets
- Annotate data — Use the visual annotator to create annotations
- Vi SDK getting started — Quick start guide for the SDK
- Assets API — Manage assets programmatically
- Phrase grounding format — Learn about phrase-box annotations
- VQA format — Learn about question-answer annotations
- Datasets API — Manage datasets and exports
- Vi SDK installation — Install the Vi SDK
- API resources — Complete SDK reference
- AI-assisted tools — Use annotation automation
- Secret keys — Manage API authentication
Need help?
We're here to support your VLMOps journey. Reach out through any of these channels:
Updated about 1 month ago
