Using the Python SDK
If you're integrating with our APIs using Python, the Dragoneye SDK streamlines the process with minimal setup. Here's how you can get started and explore the types and endpoints in detail.
Installation
Install the package using pip.
pip install dragoneye-python
Quick Start
Don't have an API key yet? See Creating an Access Token.
To call the classifier, follow these steps:
import asyncio
from dragoneye import Dragoneye, Image, Video
async def main():
# The api_key can also be set via the DRAGONEYE_API_KEY environment variable.
client = Dragoneye(api_key="<YOUR_ACCESS_TOKEN>")
# Example: predict from an image
image = Image.from_path("photo.jpg")
image_result = await client.classification.predict_image(
media=image,
model_name="recognize_anything/your_model_name", # change to your desired model
)
# Example: predict from a video
# NOTE! When loading a file, you can optionally pass a file name or identifier
# that you use to identify your own files.
video = Video.from_path(
path="example.mp4",
name="any-file-name",
)
video_result = await client.classification.predict_video(
media=video,
model_name="recognize_anything/your_model_name",
)
# Accessing image results
for obj in image_result.objects:
bbox = obj.bbox_observation.normalized_bbox
top_category = max(obj.categories, key=lambda c: c.score)
print(f"Category: {top_category.name} ({top_category.score:.2f})")
for attr in top_category.attributes:
print(f" {attr.attribute_name}: {attr.option_name} ({attr.score:.2f})")
asyncio.run(main())
Model names follow the format recognize_anything/model_name. Use the name you specified when creating the model.
How predictions are structured
Both endpoints return a list of objects the model detected. Each object has:
- A bounding box — where the object is, in normalized
(x1, y1, x2, y2)coordinates. - One or more categories — what the object is, each with a confidence
score. - A list of attributes on each category — additional properties the model predicted (for example, a building's exterior color), each as the chosen option plus a score.
Images and videos return slightly different object shapes. An image is a single moment, so each object has one bounding box and one score per attribute. A video adds a time dimension: the same object is tracked across frames, so it carries the timestamps where it appeared, a bounding box per sampled frame, and attribute scores that can change over time.
Example Image Response
Below is an example of what a ClassificationPredictImageResponse looks like for a Building Detection model. The response is a flat list of objects, where each ImageDetectedObject is a single detected object with one bounding box and a score per attribute:
ClassificationPredictImageResponse(
prediction_task_uuid="a1b2c3d4-e5f6-7890-abcd-ef1234567890",
original_file_name="my-photo",
objects=[
ImageDetectedObject(
object_id=1,
bbox_observation=BboxObservation(normalized_bbox=(0.12, 0.25, 0.55, 0.78), bbox_score=0.97),
categories=[
ImageCategoryPrediction(
category_id=2084323334,
name="House (detached)",
score=0.92,
attributes=[
ImageAttributePrediction(
attribute_id=1371766615,
attribute_name="Building Exterior Color",
option_id=3498033303,
option_name="White / Off-white",
score=0.85,
),
ImageAttributePrediction(
attribute_id=448392115,
attribute_name="Building Exterior Material",
option_id=3887467550,
option_name="Wood (incl. timber siding)",
score=0.78,
),
# ... more attributes omitted for brevity
],
),
],
),
ImageDetectedObject(
object_id=2,
bbox_observation=BboxObservation(normalized_bbox=(0.60, 0.30, 0.88, 0.75), bbox_score=0.90),
categories=[
ImageCategoryPrediction(
category_id=3212613421,
name="Garage (detached)",
score=0.87,
attributes=[
# ... attributes omitted for brevity
],
),
],
),
],
)
Each ImageDetectedObject has an object_id, a single bbox_observation, and its categories. The bbox_observation is a BboxObservation — the same bounding-box type videos use. Every attribute is one chosen option with a single score.
Example Video Response
Below is an example of what a ClassificationPredictVideoResponse looks like for the same model. The response is a flat list of objects, where each VideoDetectedObject is a single object tracked across the whole video:
ClassificationPredictVideoResponse(
prediction_task_uuid="a1b2c3d4-e5f6-7890-abcd-ef1234567890",
original_file_name="any-file-name",
frames_per_second=1,
# Every processed frame's timestamp (microseconds), sorted — including
# frames where nothing was detected.
frame_timestamps_microseconds=[0, 1000000, 2000000, 3000000],
objects=[
VideoDetectedObject(
object_id=1,
# When this object was on screen (in microseconds).
timestamp_ranges=[TimestampRange(timestamp_start_us_inclusive=0, timestamp_end_us_inclusive=3000000)],
# One observation per sampled frame in the object's lifespan.
bbox_observations=[
VideoBboxObservation(timestamp_microseconds=0, observation=BboxObservation(normalized_bbox=(0.12, 0.25, 0.55, 0.78), bbox_score=0.97)),
VideoBboxObservation(timestamp_microseconds=1000000, observation=BboxObservation(normalized_bbox=(0.13, 0.26, 0.56, 0.79), bbox_score=0.96)),
# Gap frame: the object is still on screen, but the model
# didn't predict a box for it this frame, so the whole
# observation is None. Skip these when drawing or denormalizing.
VideoBboxObservation(timestamp_microseconds=2000000, observation=None),
VideoBboxObservation(timestamp_microseconds=3000000, observation=BboxObservation(normalized_bbox=(0.14, 0.27, 0.57, 0.80), bbox_score=0.95)),
],
categories=[
VideoCategoryPrediction(
category_id=2084323334,
name="House (detached)",
score=0.92,
# Each attribute is the option the model predicted
# as well as the start and end times that the model
# predicted it.
attributes=[
VideoAttributePrediction(
attribute_id=1371766615,
attribute_name="Building Exterior Color",
option_id=3498033303,
option_name="White / Off-white",
timestamp_ranges=[
ScoredTimestampRange(timestamp_start_us_inclusive=0, timestamp_end_us_inclusive=3000000, score=0.85),
],
),
VideoAttributePrediction(
attribute_id=448392115,
attribute_name="Building Exterior Material",
option_id=3887467550,
option_name="Wood (incl. timber siding)",
timestamp_ranges=[
ScoredTimestampRange(timestamp_start_us_inclusive=0, timestamp_end_us_inclusive=3000000, score=0.78),
],
),
# ... more attributes omitted for brevity
],
),
],
),
VideoDetectedObject(
object_id=2,
timestamp_ranges=[TimestampRange(timestamp_start_us_inclusive=0, timestamp_end_us_inclusive=1000000)],
bbox_observations=[
VideoBboxObservation(timestamp_microseconds=0, observation=BboxObservation(normalized_bbox=(0.60, 0.30, 0.88, 0.75), bbox_score=0.90)),
VideoBboxObservation(timestamp_microseconds=1000000, observation=BboxObservation(normalized_bbox=(0.61, 0.31, 0.89, 0.76), bbox_score=0.89)),
],
categories=[
VideoCategoryPrediction(
category_id=3212613421,
name="Garage (detached)",
score=0.87,
attributes=[
# ... attributes omitted for brevity
],
),
],
),
],
)
Read a video response one object at a time. Each VideoDetectedObject is one object the model tracked through the video, and it tells you:
object_id— a stable id, so you can follow the same object from frame to frame.timestamp_ranges— when the object was on screen, in microseconds.bbox_observations— where the object was, with one observation per sampled frame in its lifespan.categories— what the object is, plus its attribute predictions.
The response also carries frame_timestamps_microseconds: the sorted list of every frame the model processed, in microseconds — including frames where nothing was detected. This is the full timeline of the video, broader than any single object's timestamp_ranges or bbox_observations (which only cover the frames where that object appeared). Use it to line a playback position up with a real frame: snap an arbitrary scrub time to the nearest value in this list, then look up detections at that timestamp. It's video-only — image responses don't have it.
A tracked object can stay on screen across frames where the model didn't predict a box for it. These are gap frames: the object is still within its timestamp_ranges, but for that frame the model produced no detection. The SDK keeps one VideoBboxObservation per sampled frame in the lifespan and sets its observation to None on gap frames, so a track's observations stay aligned to the frames it spans. Any code that draws or denormalizes coordinates must skip observations where observation is None:
for obs in obj.bbox_observations:
if obs.observation is None:
continue # gap frame — object on screen but no predicted box
x1, y1, x2, y2 = obs.observation.normalized_bbox
# ... draw / denormalize
Gap frames only occur on the video path. Image objects always carry a real bounding box.
Attributes work a little differently in video because the model's answer can change over time. Each VideoAttributePrediction is one chosen option together with the time spans where that option applied. If the answer changes partway through (say a traffic light goes from green to red), the same attribute appears again with the new option. Images don't have a time dimension, so they use the simpler ImageDetectedObject shape shown above.
Client
Dragoneye
The main client used to interact with the API.
client = Dragoneye(
api_key="<YOUR_ACCESS_TOKEN>",
max_retries=10,
max_backoff_time=120,
)
Arguments:
api_key(Optional[str]): Your API key. If omitted, the SDK reads from theDRAGONEYE_API_KEYenvironment variable.max_retries(int): Maximum retry attempts on rate-limit (429) responses. Default:10.max_backoff_time(int): Maximum backoff time in seconds for exponential backoff. Default:120.
Media Classes
Image and Video are used to wrap media before passing it to a prediction endpoint. Each class restricts the MIME type to its respective media type (image/* or video/*).