Images
List a dataset's images, and upload, fetch, download, tag, review, split, or delete them.
This page covers listing a dataset’s images and the per-image operations.
For bulk annotation work across many images, see the
batch resource.
An image is its path. In REST, a single image is addressed as
{dataset}/{directory}/{filename}:
GET /api/v1/developer/images/road-signs/train/stop-sign-0421.jpg
└dataset─┘ └dir┘ └── filename ──┘
Directories nest to any depth; an image at the root is road-signs/stop-sign-0421.jpg.
In the SDK and CLI you pass the dataset name and the filename - the directory
is resolved for you. A filename living in two directories of the same dataset is
ambiguous and raises; narrow it with list(directory_path=...).
list
List a dataset’s images, newest first, filtered by directory, stage, split, or
model confidence. Returns a single page; use iter to page over all
of them.
Source: Images.list
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
directory_path |
str | None |
None |
Restrict to one virtual directory, e.g. /train. None lists all |
filename |
str | None |
None |
Exact-filename lookup across directories |
status |
str | None |
None |
Restrict to a stage: new / annotate / review / complete |
split |
ImageSplit | None |
None |
Restrict to train / val / test |
include_archived |
bool |
False |
Include soft-deleted (archived) images |
min_confidence_lt |
float | None |
None |
Keep only images whose model confidence is below this (0-1) |
limit |
int |
100 |
Page size, capped at 1000 |
offset |
int |
0 |
Page offset |
images = client.images.list(
dataset_name="road-signs",
directory_path="/train",
limit=50,
)
for img in images:
print(img.filename, img.status, img.annotation_count, img.min_confidence)
pictograph images list road-signs --directory /train --limit 50
pictograph images list road-signs --status complete --min-confidence-lt 0.9
curl -s "https://api.pictograph.io/api/v1/developer/images/?dataset=road-signs&directory_path=/train&status=complete&limit=50&offset=0" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns list[Image]
Image · 18 fields
class Image(BaseModel):
"""An image within a Pictograph dataset."""
id: str
dataset_id: str | None = None
filename: str
status: Literal['new', 'annotate', 'review', 'complete'] = 'new'
split: Optional[Literal['train', 'val', 'test']] = None
annotation_count: int = 0
min_confidence: float | None = None
file_size: int = 0
width: int | None = None
height: int | None = None
content_type: str | None = None
directory_path: str | None = None
tags: list[str] = []
is_archived: bool = False
image_url: str | None = None
thumbnail_url: str | None = None
annotation_url: str | None = None
created_at: datetime
The response shape is {"data": [...], "pagination": {"limit": 50, "offset": 0, "total": 1234, "has_more": true}} - total is the dataset-wide count for the active filters, so has_more is authoritative.
iter
Auto-page over every image in a dataset, with no offset bookkeeping.
Materialize with .all(), or peek the first match with .first(). Filters
mirror list.
Source: Images.iter
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
directory_path |
str | None |
None |
Restrict to one virtual directory; None iterates all |
filename |
str | None |
None |
Exact-filename lookup across directories |
status |
str | None |
None |
Restrict to a stage; None iterates all |
split |
ImageSplit | None |
None |
Restrict to train / val / test |
include_archived |
bool |
False |
Include archived images |
min_confidence_lt |
float | None |
None |
Iterate only images with model confidence below this (0-1) |
page_size |
int |
100 |
Items per round-trip, capped at 1000 |
max_total |
int | None |
None |
Stop after this many items; None yields all |
# Walk an entire directory, paging transparently
for img in client.images.iter(dataset_name="road-signs", directory_path="/train"):
print(img.filename)
# Cap the total and materialize to a list
complete = client.images.iter(
dataset_name="road-signs",
status="complete",
max_total=500,
).all()
# Active learning: page the images a model was least confident about.
# Every listed image carries `min_confidence` (1.0 = certain).
for img in client.images.iter(dataset_name="road-signs", min_confidence_lt=0.9):
print(img.filename, img.min_confidence)
# The CLI list command pages for you - --limit is a total, not a page size.
pictograph images list road-signs --directory /train --limit 5000
# iter hits the same endpoint as list, walking offset until
# pagination.has_more is false.
curl -s "https://api.pictograph.io/api/v1/developer/images/?dataset=road-signs&directory_path=/train&limit=100&offset=100" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns OffsetPager[Image]
Image · 18 fields
class Image(BaseModel):
"""An image within a Pictograph dataset."""
id: str
dataset_id: str | None = None
filename: str
status: Literal['new', 'annotate', 'review', 'complete'] = 'new'
split: Optional[Literal['train', 'val', 'test']] = None
annotation_count: int = 0
min_confidence: float | None = None
file_size: int = 0
width: int | None = None
height: int | None = None
content_type: str | None = None
directory_path: str | None = None
tags: list[str] = []
is_archived: bool = False
image_url: str | None = None
thumbnail_url: str | None = None
annotation_url: str | None = None
created_at: datetime
get
Fetch metadata for a single image.
Source: Images.get
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
image |
str |
required | Filename (unique in the dataset, any folder), or the image UUID (it must be in dataset_name) |
image = client.images.get(
dataset_name="road-signs",
image="stop-sign-0421.jpg",
)
print(image.filename, image.status, image.annotation_count)
pictograph images get road-signs stop-sign-0421.jpg
curl -s "https://api.pictograph.io/api/v1/developer/images/road-signs/train/stop-sign-0421.jpg/metadata" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
# Embed the annotations in the same response:
curl -s "https://api.pictograph.io/api/v1/developer/images/road-signs/train/stop-sign-0421.jpg/metadata?include_annotations=true" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns Image
Image · 18 fields
class Image(BaseModel):
"""An image within a Pictograph dataset."""
id: str
dataset_id: str | None = None
filename: str
status: Literal['new', 'annotate', 'review', 'complete'] = 'new'
split: Optional[Literal['train', 'val', 'test']] = None
annotation_count: int = 0
min_confidence: float | None = None
file_size: int = 0
width: int | None = None
height: int | None = None
content_type: str | None = None
directory_path: str | None = None
tags: list[str] = []
is_archived: bool = False
image_url: str | None = None
thumbnail_url: str | None = None
annotation_url: str | None = None
created_at: datetime
tags are the user image tags that bulk_tag writes;
min_confidence is the lowest per-annotation model confidence on the image
(1.0 = certain, or human-drawn). Annotations live on the
annotations resource: call
client.annotations.get(dataset_name="road-signs", image="stop-sign-0421.jpg")
to fetch them.
upload
Upload a local file to a dataset. The SDK uses the three-step signed-URL flow:
request a signed upload URL, PUT the bytes straight to storage, then register
the image. Bytes never relay through the API, which is faster and avoids the
request-body size limit.
Source: Images.upload
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
file_path |
str | Path |
required | Local file. Dimensions are read client-side |
directory_path |
str |
"/" |
Virtual directory, e.g. /cars |
filename |
str | None |
basename | Override the destination filename |
content_type |
str | None |
inferred | Override the MIME type |
progress |
callable | None |
None |
Called with (bytes_sent, total_bytes) |
from pathlib import Path
image = client.images.upload(
dataset_name="road-signs",
file_path=Path("./photo.jpg"),
directory_path="/cars",
)
print(image.id, image.filename, image.directory_path)
pictograph images upload road-signs ./photo.jpg --directory /cars
# Step 1 - request a signed upload URL.
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/upload-url" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dataset": "road-signs",
"filename": "photo.jpg",
"directory_path": "/cars",
"content_type": "image/jpeg"
}'
# Step 2 - PUT the raw bytes to the returned upload_url (no API key on that URL).
curl -s -X PUT "<upload_url>" \
-H "Content-Type: image/jpeg" \
--upload-file ./photo.jpg
# Step 3 - register the uploaded blob. Same field names as step 1; the server
# derives the storage path itself, so nothing storage-internal round-trips.
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/register" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dataset": "road-signs",
"filename": "photo.jpg",
"directory_path": "/cars",
"file_size": 123456,
"content_type": "image/jpeg",
"width": 1920,
"height": 1080
}'
Register returns the full canonical Image ({"data": {...}}), so no
follow-up metadata fetch is needed. Raises ConflictError (409) if a file with
the same name already exists in the same directory.
If you would rather hand the bytes to the API in one request, POST /upload
relays the file and does all three steps server-side:
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/upload" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-F "dataset=road-signs" \
-F "directory_path=/cars" \
-F "file=@./photo.jpg"
Returns Image
Image · 18 fields
class Image(BaseModel):
"""An image within a Pictograph dataset."""
id: str
dataset_id: str | None = None
filename: str
status: Literal['new', 'annotate', 'review', 'complete'] = 'new'
split: Optional[Literal['train', 'val', 'test']] = None
annotation_count: int = 0
min_confidence: float | None = None
file_size: int = 0
width: int | None = None
height: int | None = None
content_type: str | None = None
directory_path: str | None = None
tags: list[str] = []
is_archived: bool = False
image_url: str | None = None
thumbnail_url: str | None = None
annotation_url: str | None = None
created_at: datetime
Supported extensions: .jpg, .jpeg, .png, .webp, .bmp, .tif,
.tiff, .gif, .heic. HEIC is auto-converted to PNG server-side.
bulk_upload
Upload many local files to one directory in a single efficient pass, up to 500
files per call. Rather than the per-file three-step flow, this makes one
bulk-upload-url call, PUTs each file straight to storage, then makes one
bulk-register call: two round-trips plus N PUTs, not 3N. A filename collision
lands in failed rather than failing the whole batch.
Source: Images.bulk_upload
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
file_paths |
Sequence[str | Path] |
required | Local files, up to 500 |
directory_path |
str |
"/" |
Virtual directory for every file in the batch |
max_workers |
int |
8 |
Parallel upload threads |
progress |
callable | None |
None |
Called with (done, total) |
result = client.images.bulk_upload(
dataset_name="road-signs",
file_paths=["a.jpg", "b.jpg"],
directory_path="/",
)
print(result.count, "registered,", len(result.failed), "failed")
for img in result.succeeded:
print(img.filename, img.id, img.status)
pictograph images bulk-upload road-signs ./a.jpg ./b.jpg --directory /
# Step 1 - request signed upload URLs for the whole batch.
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/bulk-upload-url" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dataset": "road-signs",
"images": [
{"filename": "a.jpg", "directory_path": "/", "content_type": "image/jpeg"},
{"filename": "b.jpg", "directory_path": "/", "content_type": "image/jpeg"}
]
}'
# Step 2 - PUT each file's bytes to its returned upload_url.
curl -s -X PUT "<upload_url_for_a>" -H "Content-Type: image/jpeg" --upload-file ./a.jpg
# Step 3 - register the uploaded blobs in one call.
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/bulk-register" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dataset": "road-signs",
"images": [
{"filename": "a.jpg", "directory_path": "/", "file_size": 12345,
"content_type": "image/jpeg", "width": 640, "height": 480},
{"filename": "b.jpg", "directory_path": "/", "file_size": 23456,
"content_type": "image/jpeg", "width": 800, "height": 600}
]
}'
Returns BulkUploadResult
BulkUploadResult · 2 fields
class BulkUploadResult(BaseModel):
"""Outcome of Images.bulk_upload."""
succeeded: list[Image]
failed: list[BulkUploadFailure]
The REST response is {"data": {"succeeded": [...], "failed": [...], "count": N}}
succeededcarries the full canonical image per registered row, andfailedcarries{filename, directory_path, error}per declined item. The SDK returnsBulkUploadResultwithsucceeded(list[Image]),failed(list[BulkUploadFailure]), andcount.
upload_from_directory
Upload a whole local directory tree, recreating its structure on the dataset.
Batches the signed-URL and register calls the same way bulk_upload does, and
creates the dataset if it does not exist.
Source: Images.upload_from_directory
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
directory |
str | Path |
required | Local directory, walked recursively |
organize_by_class |
bool |
True |
ImageFolder mode: the first subdirectory level becomes the virtual directory |
preserve_structure |
bool |
False |
Recreate the full nested tree instead |
parallel |
bool |
True |
Upload concurrently |
max_workers |
int |
8 |
Parallel upload threads |
skip_existing |
bool |
True |
Skip filenames already present |
create_if_missing |
bool |
True |
Create the dataset if it does not exist |
progress |
callable | None |
None |
Called with (done, total, filename) |
report = client.images.upload_from_directory(
dataset_name="road-signs",
directory="./photos",
organize_by_class=True,
parallel=True,
max_workers=8,
)
print(report.images_uploaded, len(report.failures))
pictograph images upload-directory road-signs ./photos --by-class --workers 8
# The helper drives these two endpoints, up to 500 images per call.
# POST /api/v1/developer/images/bulk-upload-url (request many signed URLs)
# POST /api/v1/developer/images/bulk-register (register many uploaded blobs)
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/bulk-upload-url" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dataset": "road-signs",
"images": [
{"filename": "a.jpg", "directory_path": "/cars", "content_type": "image/jpeg"},
{"filename": "b.jpg", "directory_path": "/cars", "content_type": "image/jpeg"}
]
}'
Returns UploadReport
UploadReport · 5 fields
class UploadReport(BaseModel):
"""Outcome of an Images.upload_from_directory call."""
dataset_name: str
images_attempted: int = 0
images_uploaded: int = 0
images_skipped: int = 0
failures: list[UploadFailure] = []
download
Stream the original image bytes to a local file, chunked so large images are
safe. Bytes land in a sibling .part file and are renamed onto output_path
only once the transfer completes, so a failed download never leaves a partial
file behind.
Source: Images.download
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
image |
str |
required | The image’s path - directory/filename, or just the filename for an image at the dataset root - or its UUID (it must be in dataset_name) |
output_path |
str | Path |
required | Local destination; parents are created |
chunk_size |
int |
8388608 |
Bytes per chunk (8 MiB) |
client.images.download(
dataset_name="road-signs",
image="stop-sign-0421.jpg",
output_path="./photo.jpg",
)
pictograph images download road-signs stop-sign-0421.jpg --output ./photo.jpg
curl -s "https://api.pictograph.io/api/v1/developer/images/road-signs/train/stop-sign-0421.jpg" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-o ./photo.jpg
Returns Path
The bytes are served through a CDN with 30-day edge caching, so repeat downloads are fast.
download_bundle
Stream the image’s data bundle to a local zip: the original bytes, its depth
map when one has been generated, its annotations as Pictograph JSON, and a
manifest.json naming exactly what is and is not inside. This is the same
archive the annotation editor’s “Image data” button produces - one server-side
builder assembles both, so the two cannot drift.
A missing depth map is not an error: the zip still arrives and the manifest
records the omission and why. Same atomic .part rename as download, so a
failed transfer leaves no partial zip.
Source: Images.download_bundle
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
image |
str |
required | The image’s path - directory/filename, or just the filename for an image at the dataset root - or its UUID (it must be in dataset_name) |
output_path |
str | Path |
required | Local destination; parents are created |
chunk_size |
int |
8388608 |
Bytes per chunk (8 MiB) |
client.images.download_bundle(
dataset_name="road-signs",
image="stop-sign-0421.jpg",
output_path="./stop-sign-0421.zip",
)
pictograph images download-bundle "road-signs" stop-sign-0421.jpg
curl -s "https://api.pictograph.io/api/v1/developer/images/road-signs/train/stop-sign-0421.jpg/data-bundle" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-o ./stop-sign-0421.zip
Returns Path
The CLI defaults --output to ./<image-stem>.zip in the current directory, so
the bare command lands the same file the editor’s button does.
delete
Soft-delete (archive) by default, which is recoverable. Set permanent=True to
free the stored bytes; that is irreversible and needs an admin+ API key.
Source: Images.delete
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
image |
str |
required | The image’s path - directory/filename, or just the filename for an image at the dataset root - or its UUID (it must be in dataset_name) |
permanent |
bool |
False |
True deletes the bytes irreversibly |
client.images.delete(
dataset_name="road-signs",
image="stop-sign-0421.jpg",
)
client.images.delete(
dataset_name="road-signs",
image="stop-sign-0421.jpg",
permanent=True,
)
# The CLI defaults the OTHER way - it deletes permanently unless you pass --archive.
pictograph images delete road-signs stop-sign-0421.jpg --archive
pictograph images delete road-signs stop-sign-0421.jpg --yes
# Archive (recoverable)
curl -s -X DELETE "https://api.pictograph.io/api/v1/developer/images/road-signs/train/stop-sign-0421.jpg" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
# Permanent (irreversible, admin+ role)
curl -s -X DELETE "https://api.pictograph.io/api/v1/developer/images/road-signs/train/stop-sign-0421.jpg?permanent=true" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns None
Permanent delete is reference-counted: a blob still referenced by an imported copy of the image is retained.
review
Approve or request changes on an image in the annotation review workflow.
approve marks the image complete and accepts its annotations;
request_changes sends it back to annotate with an optional note the
annotator sees in the editor. Returns the image’s new status. Requires a
member+ API key.
This is the programmatic entry point for QA automation, for example auto-approving high-confidence predictions and bouncing low-confidence ones for human review.
Source: Images.review
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
image |
str |
required | The image’s path - directory/filename, or just the filename for an image at the dataset root - or its UUID (it must be in dataset_name) |
action |
str |
required | "approve" or "request_changes" |
note |
str | None |
None |
Message for the annotator on request_changes |
# Approve - accept the annotations, mark complete
client.images.review(
dataset_name="road-signs",
image="stop-sign-0421.jpg",
action="approve",
)
# Request changes - send back to the annotator with a note
client.images.review(
dataset_name="road-signs",
image="stop-sign-0422.jpg",
action="request_changes",
note="tighten the left car bbox",
)
# QA loop: bounce every low-confidence image for a human to fix
for img in client.images.iter(dataset_name="road-signs", min_confidence_lt=0.6):
client.images.review(
dataset_name="road-signs",
image=img.filename,
action="request_changes",
note="model unsure - please verify",
)
pictograph images review road-signs stop-sign-0421.jpg
pictograph images review road-signs stop-sign-0422.jpg \
--request-changes --note "tighten the left car bbox"
# Approve
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/road-signs/train/stop-sign-0421.jpg/review" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
-d '{"action": "approve"}'
# Request changes with a note
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/road-signs/train/stop-sign-0422.jpg/review" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
-d '{"action": "request_changes", "note": "tighten the left car bbox"}'
Returns ImageStatus
ImageStatus
ImageStatus = Literal['new', 'annotate', 'review', 'complete']
set_split
Assign one image to a train / val / test split, or clear its assignment
with split=None. To partition a whole dataset at once, use
assign_splits; to read a partition back, filter
list or iter with split=.
Source: Images.set_split
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
image |
str |
required | The image’s path - directory/filename, or just the filename for an image at the dataset root - or its UUID (it must be in dataset_name) |
split |
ImageSplit | None |
required | "train" / "val" / "test", or None to clear |
client.images.set_split(
dataset_name="road-signs",
image="stop-sign-0421.jpg",
split="test",
)
client.images.set_split(
dataset_name="road-signs",
image="stop-sign-0421.jpg",
split=None,
)
# Read a partition back
train = client.images.iter(dataset_name="road-signs", split="train").all()
pictograph images split road-signs stop-sign-0421.jpg test
pictograph images split road-signs stop-sign-0421.jpg none
pictograph images list road-signs --split train
# Assign
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/road-signs/train/stop-sign-0421.jpg/split" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
-d '{"split": "test"}'
# Clear
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/road-signs/train/stop-sign-0421.jpg/split" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
-d '{"split": null}'
# Filter a dataset by split
curl -s "https://api.pictograph.io/api/v1/developer/images/?dataset=road-signs&split=train" \
-H "X-API-Key: $PICTOGRAPH_API_KEY"
Returns ImageSplit | None
ImageSplit
ImageSplit = Literal['train', 'val', 'test']
assign_splits
One-call Rebalance: partition the whole non-archived dataset by ratio in a
single atomic call - the fast path compared with per-image set_split, and the
same operation the grid’s Rebalance button performs. val and test take
their floor and train takes the remainder, so the counts sum to the total
exactly (a 0 weight yields 0 images, for example 80/20/0), and the
shuffle is deterministic under seed. Requires a member+ API key. Pairs with
a split-organized, directly-trainable export.
Source: Images.assign_splits
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
train |
int |
70 |
Integer percentage weight |
val |
int |
20 |
Integer percentage weight |
test |
int |
10 |
Integer percentage weight |
seed |
int |
42 |
Deterministic shuffle seed |
mode |
"random" | "embedding" |
"random" |
"embedding" clusters visually similar images first, so near-duplicates land in the same split instead of leaking across train and val. Adds clusters / unclustered to the result |
counts = client.images.assign_splits(
dataset_name="road-signs",
train=70,
val=20,
test=10,
)
print(counts)
# The CLI takes the dataset UUID here, not the name.
pictograph images rebalance b7e4c1a0-83f2-4d55-9a6e-1f0c2d38b915 \
--train 80 --val 10 --test 10
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/assign-splits" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" -H "Content-Type: application/json" \
-d '{"dataset": "road-signs", "train": 70, "val": 20, "test": 10, "seed": 42}'
Returns dict[str, int]
On the REST body the embedding mode is "split_mode": "embedding".
bulk_tag
Add or remove user tags across many images in one call. Returns the number of images updated.
Source: Images.bulk_tag
| Arg | Type | Default | Notes |
|---|---|---|---|
dataset_name |
str |
required | Dataset name. |
image_ids |
Sequence[str] |
required | Image UUIDs to tag |
tags |
Sequence[str] |
required | Tags to apply |
add |
bool |
True |
False removes the tags instead |
reviewed = client.images.iter(dataset_name="road-signs", status="complete").all()
n = client.images.bulk_tag(
dataset_name="road-signs",
image_ids=[i.id for i in reviewed],
tags=["reviewed"],
)
print(n, "images tagged")
client.images.bulk_tag(
dataset_name="road-signs",
image_ids=[i.id for i in reviewed],
tags=["blurry"],
add=False,
)
# Image ids are POSITIONAL and are UUIDs, not filenames. --tag is repeatable.
pictograph images tag road-signs \
3f1c8e42-6b90-4a71-9d0e-2b5c7a11e004 \
9a2d5b17-4c83-4f60-8e11-6d7f0c93a221 \
--tag reviewed
# --remove strips the same tags instead
pictograph images tag road-signs \
3f1c8e42-6b90-4a71-9d0e-2b5c7a11e004 \
--tag blurry --remove
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/bulk-tag" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dataset": "road-signs",
"image_ids": ["3f1c8e42-6b90-4a71-9d0e-2b5c7a11e004"],
"tags": ["reviewed"],
"add": true
}'
Returns int
augment
Generate multiplier augmented variants of every image in source.
Source: Images.augment
| Arg | Type | Default | Notes |
|---|---|---|---|
source |
str |
required | Source dataset name. |
ops |
Sequence[Augmentation] |
required | Augmentation ops (from pictograph.augment) applied in order. |
multiplier |
int |
3 |
Variants generated per source image (>= 1). |
into |
str | None |
None |
Target dataset name. None (or equal to source) appends the variants into the source dataset itself; any other name is created if missing, copying the source’s class config. |
include_original |
bool |
True |
When writing to a new dataset, also copy each original image + annotations (so the new dataset is a superset). Ignored when appending to the source (the originals are already there). |
directory_path |
str |
'/augmented' |
Virtual directory the generated images land in. |
seed |
int | None |
None |
RNG seed for reproducible variants. |
max_source_images |
int | None |
None |
Cap the number of source images processed (handy for a quick trial). None processes all. |
jpeg_quality |
int |
95 |
Quality for the generated JPEG images (1-100). |
drop_classes |
Iterable[str] | None |
None |
Preprocessing - annotation class names to remove before augmenting. Dropped classes are also removed from a newly-created target’s class config. |
skip_empty |
bool |
False |
Preprocessing - when True, a source image left with no annotations (originally, or after drop_classes) is skipped entirely and counted in report.skipped_empty. |
on_progress |
Callable[[int, int], None] | None |
None |
Optional (done, total) callback fired per source image. |
from pictograph.augment import Brightness, HorizontalFlip
report = client.images.augment(
source="road-signs",
ops=[HorizontalFlip(), Brightness(0.2)],
multiplier=3,
into="road-signs-augmented",
)
print(report.variants_created, "variants from", report.source_images, "images")
pictograph augment dataset road-signs --into road-signs-aug
# `augment` builds the new images on your machine (Pillow), so it has no endpoint of
# its own - it drives the two public ones, once per generated image:
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/upload" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-F "dataset=road-signs" -F "directory_path=/augmented" -F "file=@tile.jpg"
curl -s -X POST "https://api.pictograph.io/api/v1/developer/annotations/bulk" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"saves": [{"image_id": "$IMAGE_ID", "annotations": [ ... ]}]}'
Returns AugmentReport
AugmentReport · 8 fields
class AugmentReport(BaseModel):
"""Outcome of an Images.augment run."""
source: str
target: str
source_images: int = 0
originals_copied: int = 0
variants_created: int = 0
annotations_written: int = 0
skipped_empty: int = 0
failures: list[AugmentFailure] = []
tile
Slice every image in source into a rows x cols grid of tiles.
Source: Images.tile
| Arg | Type | Default | Notes |
|---|---|---|---|
source |
str |
required | Source dataset name. |
rows |
int |
2 |
Grid rows per image (>= 1). |
cols |
int |
2 |
Grid columns per image (>= 1). |
overlap |
float |
0.0 |
Fractional overlap added to each tile edge, [0.0, 0.9). |
min_visibility |
float |
0.1 |
Drop an annotation from a tile when less than this fraction of its area survives the clip. |
include_empty |
bool |
True |
When False, tiles with no surviving annotations are not uploaded. |
into |
str | None |
None |
Target dataset name. None (or equal to source) appends the tiles into the source dataset itself; any other name is created if missing, copying the source’s class config. |
directory_path |
str |
'/tiles' |
Virtual directory the generated tiles land in. |
max_source_images |
int | None |
None |
Cap the number of source images processed. None processes all. |
jpeg_quality |
int |
95 |
Quality for the generated JPEG tiles (1-100). |
on_progress |
Callable[[int, int], None] | None |
None |
Optional (done, total) callback fired per source image. |
report = client.images.tile(
source="aerial",
rows=2,
cols=2,
overlap=0.1,
into="aerial-tiled",
)
print(report.tiles_created, "tiles;", report.annotations_written, "annotations")
pictograph tile dataset road-signs --into road-signs-tiled --rows 2
# `tile` builds the new images on your machine (Pillow), so it has no endpoint of
# its own - it drives the two public ones, once per generated image:
curl -s -X POST "https://api.pictograph.io/api/v1/developer/images/upload" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-F "dataset=road-signs" -F "directory_path=/tiles" -F "file=@tile.jpg"
curl -s -X POST "https://api.pictograph.io/api/v1/developer/annotations/bulk" \
-H "X-API-Key: $PICTOGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"saves": [{"image_id": "$IMAGE_ID", "annotations": [ ... ]}]}'
Returns TileReport
TileReport · 7 fields
class TileReport(BaseModel):
"""Outcome of an Images.tile run."""
source: str
target: str
source_images: int = 0
tiles_created: int = 0
empty_tiles: int = 0
annotations_written: int = 0
failures: list[TileFailure] = []
Common errors
| Status | Exception | Cause |
|---|---|---|
| 404 | NotFoundError |
The image or dataset does not exist, or belongs to another organization |
| 409 | ConflictError |
Filename collision in the same virtual directory |
| 403 | ForbiddenError |
Upload requires member+; permanent delete requires admin+ |
| 400 | ApiError |
Invalid filename or directory path, or the file is not an image |