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, or the image UUID |
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 | Filename, or the image UUID |
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 | Filename, or the image UUID |
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 | Filename, or the image UUID |
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 a fork 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 | Filename, or the image UUID |
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 | Filename, or the image UUID |
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 |