AI-enriched object insights
AXIS Scene Metadata provides representative snapshots of tracked objects. Send them to an image-capable AI model to identify details beyond standard classifications, such as a vehicle's model, body style, and visible text, or a person's clothing, accessories, and carried items.
- Person
- Car
- Bicycle
- Person on a scooter
Analyze the person in the image. Output hair type, accessories, clothing, and estimated age in JSON format.
{
"gender": "Female",
"hair_type": "Short, light brown/blonde",
"accessories": ["Eyeglasses", "Shoulder bag (black)"],
"clothing": ["White short-sleeved t-shirt", "Light blue denim jeans", "White sneakers"],
"estimated_age": "20-30"
}
View original scene metadata
{
"channel_id": 1,
"id": "6a385c75-2e36-47e7-8758-7b0d0c7ae503",
"start_time": "2026-06-23T14:34:48.565889Z",
"end_time": "2026-06-23T14:34:56.665929Z",
"duration": 8.1,
"classes": [
{
"type": "Human",
"score": 0.8823,
"carries_bag": true,
"upper_clothing_colors": [
{ "name": "white", "score": 0.613 },
{ "name": "gray", "score": 0.2945 }
],
"lower_clothing_colors": [
{ "name": "blue", "score": 0.6605 },
{ "name": "gray", "score": 0.2985 }
]
}
],
"image": {
"id": "ed428166-7bc7-4b19-b6c3-1c3b5166a15b",
"timestamp": "2026-06-23T14:34:49.765895Z",
"crop_box": { "top": 0.1433, "right": 0.2654, "bottom": 0.4575, "left": 0.1568 },
"data": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBD…"
},
"path": [
{
"timestamp": "2026-06-23T14:34:48.565889Z",
"bounding_box": { "top": 0.2662, "right": 0.0753, "bottom": 0.5256, "left": 0.0092 }
}
]
}
Analyze the car in the image. Output its model, type, color, and estimated year in JSON format.
{
"model": "Kia Ceed (JD) Sportswagon",
"type": "Station Wagon",
"color": "Brown/Bronze",
"estimated_year": "2012-2018"
}
View original scene metadata
{
"channel_id": 1,
"id": "2bce57ea-e05c-4d4f-9e49-4bc7addbc08c",
"start_time": "2026-06-23T14:01:40.178698Z",
"end_time": "2026-06-23T14:01:44.578715Z",
"duration": 4.4,
"classes": [
{
"type": "Car",
"score": 0.8084,
"colors": [
{ "name": "gray", "score": 0.5272 },
{ "name": "black", "score": 0.3624 }
]
}
],
"image": {
"id": "46dc1484-8710-4d24-9678-6ea739d3c913",
"timestamp": "2026-06-23T14:01:44.578715Z",
"crop_box": { "top": 0.1494, "right": 0.8339, "bottom": 0.9193, "left": 0.1268 },
"data": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBD…"
},
"path": [
{
"timestamp": "2026-06-23T14:01:40.178698Z",
"bounding_box": { "top": 0.1672, "right": 0.9973, "bottom": 0.2328, "left": 0.9634 }
}
]
}
Analyze the bike in the image. Output type, color, and accessories in JSON format.
{
"type": "City Bike",
"color": "Red",
"accessories": ["Front Basket", "Rear Rack", "Fenders"]
}
View original scene metadata
{
"channel_id": 1,
"id": "076d27e9-5c43-4a97-977d-91af982a6f90",
"start_time": "2026-06-23T14:28:49.164246Z",
"end_time": "2026-06-23T14:28:53.064264Z",
"duration": 3.9,
"classes": [
{
"type": "Bike",
"score": 0.7011
}
],
"image": {
"id": "f11eca76-969a-4668-9506-7b193dfff17e",
"timestamp": "2026-06-23T14:28:51.464260Z",
"crop_box": { "top": 0.2147, "right": 0.7444, "bottom": 0.4264, "left": 0.6245 },
"data": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBD…"
},
"path": [
{
"timestamp": "2026-06-23T14:28:49.164246Z",
"bounding_box": { "top": 0.1754, "right": 0.9452, "bottom": 0.2519, "left": 0.9184 }
}
]
}
Analyze the person in the image. Output hair type, accessories, clothing, and estimated age in JSON format.
{
"gender": "male",
"hair_type": "short, light brown/blonde",
"accessories": ["baseball cap", "electric scooter"],
"clothing": ["crewneck shirt (brown)", "cargo shorts (khaki)"],
"estimated_age": "20-30"
}
View original scene metadata
{
"channel_id": 1,
"id": "3d510d0f-60d1-4f73-863e-33f850729aba",
"start_time": "2026-06-23T14:35:36.766120Z",
"end_time": "2026-06-23T14:35:40.566139Z",
"duration": 3.8,
"classes": [
{
"type": "Human",
"score": 0.7591,
"upper_clothing_colors": [
{ "name": "black", "score": 0.6287 },
{ "name": "blue", "score": 0.1913 }
],
"lower_clothing_colors": [
{ "name": "beige", "score": 0.3961 },
{ "name": "black", "score": 0.32 }
]
}
],
"image": {
"id": "cb0c175a-61a1-47cb-adf2-61f9ee270a21",
"timestamp": "2026-06-23T14:35:39.666133Z",
"crop_box": { "top": 0.1966, "right": 0.5498, "bottom": 0.9103, "left": 0.2993 },
"data": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBD…"
},
"path": [
{
"timestamp": "2026-06-23T14:35:36.766120Z",
"bounding_box": { "top": 0.3833, "right": 0.999, "bottom": 0.4385, "left": 0.9071 }
}
]
}
AI-generated details depend on the image, prompt, and model. Validate important results before using them for decisions, alerts, or automated actions.
How it works
When you enable object snapshots, AXIS Scene Metadata captures representative images of tracked objects. Your application can send these images to an image-capable AI model to extract more details. Because scene metadata already identifies the broad object class, your application can use that class to select a focused prompt for the model.
You can receive the images from two data sources:
| Data source | Snapshot output |
|---|---|
| Object Track | Contains the latest representative snapshot available when the object track ends. |
| Object Snapshot | Sends each generated snapshot update during tracking, which can mean more MQTT messages and AI requests. |
This example uses Object Track. When tracking ends, the application receives the consolidated object information and latest representative image. It then selects a prompt from the most likely object class, sends the image and prompt to the AI model, and prints the returned details as JSON.
The full flow is:
- Enable the Object Snapshot feature on the device.
- Configure an Analytics MQTT publisher for the Object Track data source.
- Read the most likely object class and representative snapshot from each payload.
- Choose a prompt based on the object class.
- Send the image and prompt to an image-capable AI model.
Try it yourself
Prerequisites
- A device that supports AXIS Scene Metadata
- Object snapshots enabled on the device, as described in Enable and retrieve object snapshots
- An MQTT broker connected to the device
- An Analytics MQTT publisher configured with the
com.axis.scene.object_track.v1#1data source key and a broker topic, as described in Configure scene metadata over MQTT - Python with the
paho-mqttpackage - Access to an image-capable AI model
Update the MQTT settings and implement analyze_image() for your chosen model provider. Then run the script and move an object through the camera view. The Object Track message is emitted when tracking ends.
View full script
import json
import paho.mqtt.client as mqtt
MQTT_BROKER_HOST = "<MQTT broker IP>"
MQTT_BROKER_PORT = 1883
MQTT_TOPIC = "<Analytics MQTT publisher topic>"
DEFAULT_PROMPT = "Describe the object in the image. Return the result as JSON."
CLASS_PROMPTS = {
"car": "Analyze the car in the image. Output its model, type, color, and estimated year in JSON format.",
"human": "Analyze the person in the image. Output hair type, accessories, clothing, and estimated age in JSON format.",
"bike": "Analyze the bike in the image. Output type, color, and accessories in JSON format.",
}
def analyze_image(base64_image: str, prompt: str) -> str:
"""Send a base64-encoded image to an image-capable AI model."""
raise NotImplementedError("Add your model call here")
def get_most_likely_object_type(object_track: dict) -> str | None:
classes = object_track.get("classes", [])
if not classes:
return None
return classes[0].get("type", "").lower()
def analyze_object_track(_client, _userdata, message):
object_track = json.loads(message.payload)
object_snapshot = object_track.get("image")
if not object_snapshot:
return
object_type = get_most_likely_object_type(object_track)
prompt = CLASS_PROMPTS.get(object_type, DEFAULT_PROMPT)
analysis = json.loads(analyze_image(object_snapshot["data"], prompt))
result = {
"object_track_id": object_track["id"],
"object_snapshot_id": object_snapshot["id"],
"analysis": analysis,
}
print(json.dumps(result))
client = mqtt.Client()
client.on_message = analyze_object_track
client.connect(MQTT_BROKER_HOST, MQTT_BROKER_PORT)
client.subscribe(MQTT_TOPIC)
client.loop_forever()
Understand the code
1. Connect to the consolidated object-track stream
Configure an Analytics MQTT publisher with com.axis.scene.object_track.v1#1 as its data_source_key. The #1 suffix selects channel 1; use the channel that matches your device configuration. The publisher's mqtt_topic is the broker topic that the script subscribes to through MQTT_TOPIC.
import paho.mqtt.client as mqtt
client = mqtt.Client()
client.on_message = analyze_object_track
client.connect(MQTT_BROKER_HOST, MQTT_BROKER_PORT)
client.subscribe(MQTT_TOPIC)
client.loop_forever()
For publisher setup and connection details, see Configure scene metadata over MQTT.
2. Read the snapshot and object class
Each Object Track payload represents one completed track. Its image.data field contains the latest representative snapshot, and the first item in classes is the most likely object classification. A track might not contain image, for example, if it was too short to produce a snapshot.
def get_most_likely_object_type(object_track: dict) -> str | None:
classes = object_track.get("classes", [])
if not classes:
return None
return classes[0].get("type", "").lower()
def analyze_object_track(_client, _userdata, message):
object_track = json.loads(message.payload)
object_snapshot = object_track.get("image")
if not object_snapshot:
return
object_type = get_most_likely_object_type(object_track)
3. Select a focused prompt
Scene metadata already identifies the broad object class. Use it to ask the model a focused question instead of requesting a generic image description. A default prompt handles classes that aren't in the mapping.
prompt = CLASS_PROMPTS.get(object_type, DEFAULT_PROMPT)
Example prompt mapping:
CLASS_PROMPTS = {
"car": "Analyze the car in the image. Output its model, type, color, and estimated year in JSON format.",
"human": "Analyze the person in the image. Output hair type, accessories, clothing, and estimated age in JSON format.",
"bike": "Analyze the bike in the image. Output type, color, and accessories in JSON format.",
}
4. Call an AI model
The example keeps the provider-specific code behind one function. It expects the model to return a JSON object encoded as text:
def analyze_image(base64_image: str, prompt: str) -> str:
"""Send a base64-encoded image to an image-capable AI model."""
raise NotImplementedError("Add your model call here")
Implement this function with a local model or a hosted service. It must accept a base64-encoded image and a prompt, then return a JSON object as text. Provider SDKs and request formats change independently of AXIS Scene Metadata, so this page leaves that boundary explicit rather than prescribing one provider.
Keep credentials outside your source code. When the provider supports structured output, use a JSON schema and validate the response before storing or acting on it.