Saving YOLOv8 predictions to JSON is useful when detection results need to be stored, transferred, analyzed, indexed, or consumed by another application. Current Ultralytics Results objects provide a built-in to_json() method that exports prediction results as a JSON-formatted string. The method supports options such as normalized values and configurable decimal precision.
A simple YOLOv8 JSON workflow looks like:
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model("image.jpg")
json_data = results[0].to_json()
with open("predictions.json", "w") as f:
f.write(json_data)
For standard detection, the exported information can represent object coordinates, class information, confidence scores, and task-specific prediction data. For segmentation, pose, and OBB models, the structure changes to include relevant polygons, keypoints, or oriented bounding-box information.
Introduction to Saving YOLOv8 Predictions as JSON
YOLOv8 normally returns structured prediction objects rather than plain text. In Python, every processed image or frame receives its own Results object. That object can contain bounding boxes, masks, keypoints, oriented boxes, class mappings, paths, and other task-dependent information.
JSON provides a convenient way to transform those predictions into a portable text format.
A typical workflow is:
Input Image
↓
YOLOv8 Prediction
↓
Results Object
↓
Convert to JSON
↓
Save to File / API / Database
JSON is especially useful for applications such as:
REST APIs
web dashboards
analytics pipelines
event storage
database ingestion
dataset review tools
automation systems
Instead of trying to serialize raw PyTorch tensors manually, you can either use Ultralytics’ built-in to_json() method or construct your own Python dictionaries after converting tensor values to standard Python types.
What Does YOLOv8 Prediction Output Contain?
The exact prediction output depends on the model task.
For ordinary object detection, each prediction usually contains:
bounding-box coordinates
class ID
class name
confidence score
Current Ultralytics detection results store raw boxes with the structure:
x1, y1, x2, y2, confidence, class
inside the result’s box data.
The Results object also provides the class-name mapping and information about the source image.
Bounding Box Coordinates
Bounding boxes describe where detected objects appear.
For example:
x1 = 120
y1 = 80
x2 = 430
y2 = 610
In Python:
result = results[0]
print(result.boxes.xyxy)
You can also use:
result.boxes.xywh
for center-based coordinates.
Normalized variants include:
result.boxes.xyxyn
result.boxes.xywhn
This makes it possible to choose the coordinate representation that best fits your JSON schema.
Class IDs and Class Names
Class IDs are available through:
result.boxes.cls
For example:
for box in result.boxes:
class_id = int(box.cls[0])
print(class_id)
Human-readable class names are available through:
result.names
Example:
for box in result.boxes:
class_id = int(box.cls[0])
class_name = result.names[class_id]
print(class_name)
The Results object exposes names as a mapping between class indices and model class names.
Confidence Scores
Confidence scores are stored in:
result.boxes.conf
Example:
for box in result.boxes:
confidence = float(box.conf[0])
print(confidence)
A detection may conceptually look like:
{
"class_id": 2,
"class_name": "car",
"confidence": 0.91
}
Confidence can also be used to filter predictions before they are written to JSON.
How to Save YOLOv8 Predictions to JSON
Current Ultralytics makes JSON export straightforward through the Results.to_json() method.
The method returns a JSON-formatted string rather than automatically choosing your output filename.
This gives you control over where and how the JSON data is stored.
Run Inference with the Python API
Start by loading the model:
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
Run prediction:
results = model("image.jpg")
Then select the result:
result = results[0]
At this point, you can access:
result.boxes
result.names
result.orig_shape
result.path
depending on what information your JSON output should contain.
Convert Prediction Results to JSON
The simplest method is:
json_output = result.to_json()
Current Ultralytics documents:
to_json(normalize=False, decimals=5)
where:
normalize=False
→ keeps non-normalized numeric values
normalize=True
→ normalizes supported numeric values
decimals=5
→ controls decimal precision
The method returns a JSON string.
For example:
json_output = result.to_json(
normalize=False,
decimals=4
)
Save JSON Output to a File
Write the returned string directly:
with open(
"predictions.json",
"w"
) as f:
f.write(
result.to_json()
)
You can also specify UTF-8 explicitly:
with open(
"predictions.json",
"w",
encoding="utf-8"
) as f:
f.write(
result.to_json()
)
This is preferable to trying:
json.dump(result.boxes)
because the raw Boxes object contains tensor-backed data that standard Python JSON serialization does not understand directly.
Understanding YOLOv8 JSON Output Structure
The exact JSON structure produced by current Ultralytics is generated from the Results summary through its data-export utilities. The to_json() method converts the result summary into a structured tabular representation and serializes it as JSON.
The exact fields depend on task type.
For detection, expect object-level records representing the detected instances.
Image Information
If you are designing your own combined JSON structure, it is useful to include image-level metadata.
For example:
{
"image": "image.jpg",
"width": 1920,
"height": 1080,
"detections": []
}
The image path is available through:
result.path
and image dimensions through:
result.orig_shape
Current Ultralytics stores orig_shape as:
(height, width)
inside the Results object.
Detection Objects
A useful custom detection structure might look like:
{
"class_id": 0,
"class_name": "person",
"confidence": 0.9421,
"bbox": {
"x1": 120.5,
"y1": 82.1,
"x2": 410.9,
"y2": 702.0
}
}
If several objects are detected:
{
"image": "image.jpg",
"detections": [
{
"class_id": 0,
"class_name": "person"
},
{
"class_id": 2,
"class_name": "car"
}
]
}
This kind of schema is often easier for web applications than storing raw tensor arrays.
Bounding Boxes, Classes, and Confidence Values
A custom Python conversion can be written as:
detections = []
for box in result.boxes:
x1, y1, x2, y2 = (
box.xyxy[0]
.cpu()
.tolist()
)
class_id = int(
box.cls[0]
)
confidence = float(
box.conf[0]
)
detections.append({
"class_id": class_id,
"class_name": result.names[class_id],
"confidence": confidence,
"bbox": {
"x1": x1,
"y1": y1,
"x2": x2,
"y2": y2
}
})
Then:
import json
with open(
"predictions.json",
"w"
) as f:
json.dump(
detections,
f,
indent=2
)
Save Multiple Image Predictions to JSON
When processing several images, YOLOv8 returns one Results object per input image.
You therefore need to decide whether to create:
one JSON file per image
or:
one combined JSON file
for the complete collection.
Process a Folder of Images
Run:
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model.predict(
source="images/"
)
You can iterate through each image:
for result in results:
print(result.path)
print(result.boxes)
Current Ultralytics Predict mode returns one Results object for each image or video frame.
Store Results for Each Image
A simple custom structure is:
all_results = []
for result in results:
detections = []
for box in result.boxes:
class_id = int(
box.cls[0]
)
detections.append({
"class_id": class_id,
"class_name": result.names[class_id],
"confidence": float(box.conf[0]),
"xyxy": box.xyxy[0]
.cpu()
.tolist()
})
all_results.append({
"image": result.path,
"detections": detections
})
Now each image retains its own prediction group.
Create a Single Combined JSON File
Save:
import json
with open(
"all_predictions.json",
"w",
encoding="utf-8"
) as f:
json.dump(
all_results,
f,
indent=2
)
The resulting file could look like:
[
{
"image": "images/image1.jpg",
"detections": [
{
"class_id": 0,
"class_name": "person",
"confidence": 0.91,
"xyxy": [100, 80, 350, 640]
}
]
},
{
"image": "images/image2.jpg",
"detections": []
}
]
This format is useful when one JSON document must represent an entire inference job.
Save Video Predictions to JSON
Videos contain many frames, so prediction results should normally be processed incrementally.
Using:
stream=True
prevents every frame’s Results object from being retained in memory at once.
For long videos, this is usually more efficient.
Process Video Frames
Example:
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model.predict(
source="video.mp4",
stream=True
)
for result in results:
print(result.boxes)
Each yielded result corresponds to a processed frame.
Store Frame Numbers and Detections
Example:
import json
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
video_results = []
for frame_id, result in enumerate(
model.predict(
source="video.mp4",
stream=True
)
):
detections = []
if result.boxes is not None:
for box in result.boxes:
class_id = int(
box.cls[0]
)
detections.append({
"class_id": class_id,
"class_name": result.names[class_id],
"confidence": float(box.conf[0]),
"xyxy": box.xyxy[0]
.cpu()
.tolist()
})
video_results.append({
"frame": frame_id,
"detections": detections
})
with open(
"video_predictions.json",
"w"
) as f:
json.dump(
video_results,
f,
indent=2
)
For very long videos or continuous cameras, it is better to write results incrementally instead of accumulating the entire video_results list.
Save Tracking IDs with Predictions
If Track mode is used:
results = model.track(
source="video.mp4",
stream=True,
persist=True
)
tracking IDs can be available through:
result.boxes.id
Current Ultralytics tracked bounding boxes can include a track ID in addition to coordinates, confidence, and class information.
Example:
for box in result.boxes:
track_id = None
if box.id is not None:
track_id = int(
box.id[0]
)
record = {
"track_id": track_id,
"class_id": int(box.cls[0]),
"confidence": float(box.conf[0]),
"bbox": box.xyxy[0]
.cpu()
.tolist()
}
This is useful for trajectory and object-history analysis.
JSON Output for Different YOLOv8 Tasks
JSON export is not limited to object detection.
Current Ultralytics Results supports task-dependent structures for detection, segmentation, pose, and oriented bounding boxes. The built-in data export system is designed to export structured results for multiple supported prediction tasks.
Object Detection JSON
Detection JSON typically represents:
class
class name
confidence
bounding box
A simple custom example is:
{
"class_id": 2,
"class_name": "car",
"confidence": 0.91,
"bbox": [
110,
170,
620,
540
]
}
The standard detection Results object uses result.boxes.
Segmentation JSON
Segmentation models add:
result.masks
Current Ultralytics exposes:
result.masks.data
result.masks.xy
result.masks.xyn
where xy contains polygon points in pixel coordinates and xyn contains normalized polygons.
For JSON, polygons are usually more compact than storing every binary mask pixel.
Example:
segments = []
for polygon in result.masks.xy:
segments.append(
polygon.tolist()
)
Then:
{
"class_name": "person",
"polygon": [
[102.1, 80.2],
[110.5, 91.3],
[120.8, 108.5]
]
}
Pose Keypoint JSON
Pose results use:
result.keypoints
Current Ultralytics exposes:
result.keypoints.xy
result.keypoints.xyn
result.keypoints.data
for pose coordinates and optional visibility/confidence-related values.
Example:
keypoints = (
result.keypoints.xy
.cpu()
.tolist()
)
A custom JSON structure can be:
{
"person": 0,
"keypoints": [
[220.4, 115.2],
[214.1, 109.8],
[229.6, 110.1]
]
}
OBB JSON
OBB models use:
result.obb
Current Ultralytics provides:
result.obb.xywhr
result.obb.xyxyxyxy
result.obb.conf
result.obb.cls
The xywhr structure contains:
center x
center y
width
height
rotation
while xyxyxyxy contains four corner points.
Example:
corners = (
result.obb.xyxyxyxy
.cpu()
.tolist()
)
This can be stored directly as JSON-compatible polygon geometry.
Customize YOLOv8 JSON Predictions
The built-in to_json() method is convenient, but custom JSON generation gives you complete control over:
which classes are stored
which confidence levels are retained
which coordinate format is used
what metadata is included
This is useful when an external application expects a particular JSON schema.
Save Only Selected Classes
Suppose only class ID 0 is needed.
Use:
selected = []
for box in result.boxes:
class_id = int(
box.cls[0]
)
if class_id != 0:
continue
selected.append({
"class_id": class_id,
"confidence": float(box.conf[0]),
"bbox": box.xyxy[0]
.cpu()
.tolist()
})
You can also filter multiple classes:
allowed_classes = {
0,
2,
5
}
then:
if class_id not in allowed_classes:
continue
Filter Predictions by Confidence
Filter during prediction:
results = model.predict(
source="image.jpg",
conf=0.50
)
or after prediction:
for box in result.boxes:
confidence = float(
box.conf[0]
)
if confidence < 0.70:
continue
Using post-result filtering is useful when the JSON threshold differs from the threshold used for the original prediction workflow.
Change Bounding Box Coordinate Format
Pixel XYXY:
box.xyxy
Pixel XYWH:
box.xywh
Normalized XYXY:
box.xyxyn
Normalized XYWH:
box.xywhn
Example JSON using normalized center coordinates:
bbox = (
box.xywhn[0]
.cpu()
.tolist()
)
You could then store:
{
"bbox_format": "xywhn",
"bbox": [
0.51,
0.43,
0.22,
0.35
]
}
The built-in to_json() method also supports a normalize=True option.
YOLOv8 JSON vs TXT and CSV Output
JSON, TXT, and CSV are all useful, but they serve different purposes.
JSON is hierarchical and flexible. CSV is tabular. YOLO TXT labels are compact and useful for annotation-style workflows.
Current Ultralytics exports prediction and metrics data through structured export utilities including JSON and CSV.
Advantages of JSON Format
JSON is particularly useful when each detection contains nested information.
For example:
{
"image": "frame001.jpg",
"detections": [
{
"class": "person",
"confidence": 0.94,
"bbox": {
"x1": 100,
"y1": 50,
"x2": 300,
"y2": 600
}
}
]
}
JSON is a strong choice for:
APIs
web applications
NoSQL storage
nested segmentation data
pose keypoints
tracking events
multi-image metadata
It is also easy to read in JavaScript, Python, PHP, Java, and many other languages.
When to Use TXT or CSV Instead
TXT is useful when you need compact YOLO-style annotations.
A detection label may look like:
0 0.50 0.45 0.20 0.35
CSV is better when the data is flat and table-oriented:
image,class,confidence,x1,y1,x2,y2
For example:
img1.jpg,person,0.92,100,80,300,600
img1.jpg,car,0.85,400,200,800,550
CSV is convenient for:
Excel
spreadsheets
simple analytics
SQL imports
while JSON is more appropriate when each image contains a variable number of nested detections.
Common Problems When Saving Predictions to JSON
JSON serialization errors usually occur when raw PyTorch or NumPy values are passed directly to Python’s standard json module.
Built-in Ultralytics to_json() avoids much of this problem by converting prediction results through its export layer first.
Tensor Is Not JSON Serializable
This will fail:
import json
data = {
"box": result.boxes.xyxy[0]
}
json.dumps(data)
because:
torch.Tensor
is not a standard JSON type.
Convert it first:
data = {
"box": result.boxes.xyxy[0]
.cpu()
.tolist()
}
Likewise:
confidence = float(
box.conf[0]
)
and:
class_id = int(
box.cls[0]
)
produce standard Python types.
NumPy Values Cause Serialization Errors
A NumPy scalar such as:
np.float32
may also fail with standard json.dumps().
Convert:
value = float(
numpy_value
)
For arrays:
array.tolist()
is the normal conversion.
Alternatively, use Ultralytics:
result.to_json()
which handles result export for you.
Empty Detection Results
An image can produce a valid Results object with no detections.
For example:
if result.boxes is None or len(result.boxes) == 0:
detections = []
Your JSON can still represent the image:
{
"image": "empty_scene.jpg",
"detections": []
}
An empty detection list is not necessarily an error. It simply means no predictions survived filtering.
Incorrect Bounding Box Format
A common mistake is mixing:
xyxy
with:
xywh
For example:
[100, 80, 400, 600]
in XYXY means:
left = 100
top = 80
right = 400
bottom = 600
while in XYWH it means:
center x = 100
center y = 80
width = 400
height = 600
Always include the coordinate format in a custom API or JSON schema if another application will consume the output.
FAQs About Saving YOLOv8 Predictions to JSON
Can YOLOv8 save predictions directly to JSON?
Yes.
Current Ultralytics Results objects provide:
result.to_json()
which returns a JSON-formatted string representing the prediction result.
You can write it directly:
with open(
"results.json",
"w"
) as f:
f.write(
result.to_json()
)
How do I convert YOLOv8 results to JSON?
Use:
json_data = result.to_json()
Optional settings include:
result.to_json(
normalize=True,
decimals=4
)
Current Ultralytics documents both normalize and decimals as arguments for JSON export.
What information is included in YOLOv8 JSON output?
The information depends on the task.
Detection can contain:
class
class name
confidence
bounding box
Segmentation can include polygon information, pose can include keypoint data, and OBB output can include rotated-box geometry.
Can I save segmentation masks in JSON?
Yes, although storing complete binary masks as JSON is usually inefficient.
A better option is often to store segmentation polygons:
result.masks.xy
or normalized polygons:
result.masks.xyn
Current Ultralytics exposes both polygon forms as part of segmentation Results.
Convert polygons with:
polygon.tolist()
before using Python’s standard JSON serializer.
Can YOLOv8 tracking IDs be saved to JSON?
Yes.
When track IDs are present:
result.boxes.id
contains them.
Current Ultralytics tracked boxes include tracking IDs as an additional field in the bounding-box data structure.
Example:
track_id = None
if box.id is not None:
track_id = int(
box.id[0]
)
Then store it normally in JSON.
How do I save predictions from multiple images in one JSON file?
Process every Results object and append its predictions to one Python list:
combined = []
for result in results:
combined.append({
"image": result.path,
"detections": result.summary()
})
Then:
import json
with open(
"combined.json",
"w"
) as f:
json.dump(
combined,
f,
indent=2
)
Current Results provides summary() as a structured representation that can also be used by its JSON, CSV, and DataFrame export methods.
Why are YOLOv8 tensors not JSON serializable?
PyTorch tensors are Python objects containing numerical data and device information, but standard JSON supports only basic types such as:
string
number
boolean
null
list
object
Therefore:
box.xyxy
must be converted using something such as:
box.xyxy.cpu().tolist()
before json.dumps() can serialize it.
Using:
result.to_json()
is usually simpler when the built-in Ultralytics output format is acceptable.
Conclusion
Saving YOLOv8 predictions to JSON is straightforward with the current Ultralytics Results API.
The simplest workflow is:
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model("image.jpg")
result = results[0]
with open(
"predictions.json",
"w"
) as f:
f.write(
result.to_json()
)
Current Ultralytics exposes:
result.to_json(
normalize=False,
decimals=5
)
as a built-in JSON export method. It returns a JSON-formatted string generated from the structured prediction summary.
For custom schemas, a practical workflow is:
Run Prediction
↓
Read Results Object
↓
Extract Boxes / Masks / Keypoints / OBB
↓
Convert Tensor Values
↓
Add Image or Frame Metadata
↓
Filter Classes and Confidence
↓
Create Python Dictionaries
↓
Save with json.dump()
For normal detection:
for box in result.boxes:
class_id = int(
box.cls[0]
)
record = {
"class_id": class_id,
"class_name": result.names[class_id],
"confidence": float(box.conf[0]),
"xyxy": box.xyxy[0]
.cpu()
.tolist()
}
For segmentation, polygons are available through result.masks.xy and result.masks.xyn. For pose, keypoints are available through result.keypoints.xy, xyn, and data. For OBB models, rotated boxes are accessible through result.obb.xywhr and result.obb.xyxyxyxy.
For video and tracking workloads, include:
frame number
tracking ID
class
confidence
coordinates
so detections remain associated with both time and object identity.
JSON is usually the best choice when YOLOv8 predictions need to be consumed by APIs, dashboards, databases, tracking systems, or other applications because it can represent image-level metadata and multiple nested detections in a single structured format.
I’m Jane Austen, a skilled content writer with the ability to simplify any complex topic. I focus on delivering valuable tips and strategies throughout my articles.