YOLOv8 Output Format: Predictions, Results, and Data Structure Explained

The YOLOv8 output format depends on the task being performed. Standard object detection returns bounding boxes, class IDs, confidence scores, and related image metadata. Segmentation adds masks, pose estimation adds keypoints, oriented bounding box models return rotated boxes, and tracking can add persistent object IDs. In the Ultralytics Python API, these outputs are organized inside a Results object returned for each image or video frame.

For normal detection, the most important output structure is:

result.boxes

which contains data such as:

bounding-box coordinates
confidence scores
class IDs
tracking IDs when available

Understanding the Results structure is essential when predictions need to be filtered, saved, converted to JSON or NumPy, sent to another application, or analyzed programmatically.

Introduction to YOLOv8 Output Format

YOLOv8 does not return only an annotated image after inference. The Ultralytics Python API produces structured prediction data that can be accessed directly.

A simple detection workflow is:

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

results = model("image.jpg")

For a normal image, results is a list containing one Results object for each processed image. When stream=True is used, Ultralytics instead yields Results objects through a generator.

Conceptually:

Input Image
     ↓
YOLOv8
     ↓
Results Object
     ↓
Task-Specific Output

For detection:

Results
└── boxes

For segmentation:

Results
├── boxes
└── masks

For pose:

Results
├── boxes
└── keypoints

For OBB:

Results
└── obb

The Results API provides a common interface regardless of which YOLO task is being used.

What Does YOLOv8 Output Contain?

The exact output depends on the model task, but standard YOLOv8 detection produces object-level information.

For every retained detection, you can usually access:

box coordinates
class ID
confidence score
class name through result.names

A standard detection row stored in result.boxes.data currently follows the structure:

[x1, y1, x2, y2, confidence, class_id]

and tracked detections can include an additional tracking ID.

Bounding Box Predictions

Bounding boxes identify where objects appear.

For example:

Detected object:
car

Bounding box:
x1 = 110
y1 = 140
x2 = 470
y2 = 520

In Python:

result = results[0]

print(result.boxes.xyxy)

The output may look conceptually like:

tensor([
    [110, 140, 470, 520],
    [520, 180, 740, 490]
])

Each row represents one detected object.

Class IDs and Class Names

The numeric class IDs are stored in:

result.boxes.cls

Example:

print(result.boxes.cls)

could produce:

tensor([0., 2., 7.])

These numbers correspond to the model’s class mapping.

The mapping is available through:

result.names

For 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 dictionary mapping class indices to human-readable class names.

Confidence Scores

Every detection also includes a confidence value:

result.boxes.conf

Example:

tensor([0.94, 0.87, 0.63])

Conceptually:

Detection 1 → confidence 0.94
Detection 2 → confidence 0.87
Detection 3 → confidence 0.63

Confidence scores can be used to rank or filter detections.

For example:

for box in result.boxes:
    confidence = float(box.conf[0])

    if confidence >= 0.70:
        print(box.xyxy)

YOLOv8 Detection Output Format

YOLOv8 exposes multiple coordinate representations through the Boxes object.

The most common are:

xyxy
xywh
xyxyn
xywhn

The first two use pixel coordinates, while the normalized forms scale coordinates relative to the original image dimensions.

XYXY Bounding Box Format

The XYXY format is:

[x1, y1, x2, y2]

where:

x1 = left edge
y1 = top edge
x2 = right edge
y2 = bottom edge

Access it using:

boxes = result.boxes.xyxy

Example:

[100, 80, 400, 600]

means the box extends from:

top-left:
(100, 80)

to:

bottom-right:
(400, 600)

Current Ultralytics detection Results store raw box coordinates in XYXY form.

XYWH Bounding Box Format

The XYWH format uses:

[x_center, y_center, width, height]

Access:

print(result.boxes.xywh)

For example:

[250, 340, 300, 520]

means:

center x = 250
center y = 340
width = 300
height = 520

This format is useful when calculations depend on object center position or dimensions.

Normalized Coordinates

Normalized coordinates scale values according to image width and height.

Use:

result.boxes.xyxyn

for normalized XYXY coordinates.

Use:

result.boxes.xywhn

for normalized XYWH.

A normalized output might be:

0.52 0.46 0.21 0.34

Normalized coordinates are especially useful when exporting YOLO-style label information because the values are resolution-independent.

Understanding the YOLOv8 Results Object

Ultralytics wraps prediction data inside a Results object.

The Results object currently includes common attributes such as:

orig_img
orig_shape
boxes
masks
probs
keypoints
obb
speed
names
path
save_dir

Only the task-relevant fields are populated.

For example:

result = results[0]

print(result.orig_shape)
print(result.names)
print(result.boxes)

Results Boxes

For object detection:

result.boxes

contains the Boxes object.

Important properties include:

result.boxes.data
result.boxes.xyxy
result.boxes.xywh
result.boxes.xyxyn
result.boxes.xywhn
result.boxes.conf
result.boxes.cls
result.boxes.id

Standard raw box data currently has shape:

(N, 6)

or:

(N, 7)

when a track ID is included.

Original Image and Image Shape

The original image is available through:

result.orig_img

Ultralytics stores it as a NumPy array.

The original dimensions are:

result.orig_shape

which returns:

(height, width)

For example:

(1080, 1920)

These values are important when converting normalized coordinates or drawing custom boxes.

Model Class Names

Class names are available through:

result.names

For example:

print(result.names)

might conceptually return:

{
    0: "person",
    1: "bicycle",
    2: "car"
}

You can then use:

class_id = int(box.cls[0])

class_name = result.names[class_id]

This avoids manually maintaining a separate class-name list.

YOLOv8 Output in Python

The Python API gives direct access to structured prediction output.

A basic workflow is:

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

results = model("image.jpg")

result = results[0]

From this point, the box coordinates, classes, and confidences can be extracted independently.

Access Bounding Box Coordinates

Get all boxes:

xyxy = result.boxes.xyxy

print(xyxy)

Loop through individual boxes:

for box in result.boxes:
    coordinates = box.xyxy[0]

    print(coordinates)

Convert them to a Python list:

coordinates = (
    box.xyxy[0]
    .cpu()
    .tolist()
)

Extract Confidence Scores

Get all confidences:

confidence_scores = result.boxes.conf

print(confidence_scores)

Individual confidence:

for box in result.boxes:
    confidence = float(box.conf[0])

    print(confidence)

This is useful for application-specific filtering or ranking.

Extract Class IDs and Labels

Get IDs:

class_ids = result.boxes.cls

Convert each class ID to a name:

for box in result.boxes:

    class_id = int(box.cls[0])

    class_name = result.names[class_id]

    print(
        class_id,
        class_name
    )

You can combine everything:

for box in result.boxes:

    x1, y1, x2, y2 = (
        box.xyxy[0]
        .cpu()
        .tolist()
    )

    confidence = float(
        box.conf[0]
    )

    class_id = int(
        box.cls[0]
    )

    class_name = result.names[
        class_id
    ]

    print(
        class_name,
        confidence,
        x1,
        y1,
        x2,
        y2
    )

Output Formats for Different YOLOv8 Tasks

The Results object changes depending on the model task.

Current Ultralytics uses separate task-specific fields including:

boxes
masks
keypoints
probs
obb

This keeps the API consistent while allowing each model family to return the information it needs.

Object Detection Output

A detection model primarily returns:

result.boxes

Important values are:

xyxy
confidence
class ID
optional track ID

The raw structure is:

[x1, y1, x2, y2, conf, cls]

with an optional tracking identifier in tracked results.

Segmentation Mask Output

Instance-segmentation models add:

result.masks

Current Ultralytics segmentation output includes:

result.masks.data
result.masks.xy
result.masks.xyn

masks.data contains binary instance masks with shape:

(N, H, W)

while xy provides mask polygons in pixel coordinates and xyn provides normalized polygon coordinates.

Segmentation also retains:

result.boxes

so each detected instance can have a mask, box, class, and confidence.

Pose Keypoint Output

Pose models populate:

result.keypoints

Useful properties include:

result.keypoints.data
result.keypoints.xy
result.keypoints.xyn
result.keypoints.conf

Current Ultralytics pose output stores keypoint data with shape:

(N, K, 2)

or:

(N, K, 3)

when a third confidence or visibility-related value is present.

Pose models also provide instance boxes through:

result.boxes

OBB Output

Oriented Bounding Box models use:

result.obb

instead of relying only on standard horizontal boxes.

Current Ultralytics OBB output provides:

result.obb.xywhr
result.obb.xyxyxyxy
result.obb.conf
result.obb.cls

xywhr has shape:

(N, 5)

and represents:

x_center
y_center
width
height
rotation

while xyxyxyxy returns four corner points with shape:

(N, 4, 2)

for each oriented box.

YOLOv8 Tracking Output Format

YOLOv8 Track mode extends the normal detection output with object identity information.

A tracked box can include:

coordinates
tracking ID
confidence
class ID

Current Ultralytics Boxes identifies whether tracking data is present with its is_track property, and boxes.id provides the tracking IDs when available.

Bounding Boxes with Track IDs

A normal detection row conceptually contains:

x1 y1 x2 y2 conf cls

Tracking adds an ID:

x1 y1 x2 y2 track_id conf cls

In Python:

results = model.track(
    source="video.mp4",
    stream=True,
    persist=True
)

for result in results:

    if result.boxes.id is not None:
        print(result.boxes.id)

This allows the same object to be associated across frames.

Object Classes and Confidence Scores

Tracking does not remove the normal box properties.

You can still access:

result.boxes.xyxy
result.boxes.cls
result.boxes.conf
result.boxes.id

For example:

for box in result.boxes:

    if box.id is None:
        continue

    track_id = int(box.id[0])
    class_id = int(box.cls[0])
    confidence = float(box.conf[0])

    print(
        track_id,
        class_id,
        confidence
    )

Frame-by-Frame Tracking Results

In stream mode, each frame yields its own Results object.

Conceptually:

Frame 1
ID 4 → person

Frame 2
ID 4 → person

Frame 3
ID 4 → person

This structure can be used for:

object counting
trajectory analysis
dwell time
zone entry
zone exit
vehicle analytics

Tracking IDs are generated by the tracker and are not permanent identities across unrelated videos or arbitrary restarts.

Saving and Exporting YOLOv8 Output

Ultralytics provides built-in methods for saving visual results and converting structured predictions into formats suitable for other software.

Current Results methods include:

save()
save_txt()
summary()
to_df()
to_csv()
to_json()
cpu()
numpy()

This means you often do not need to manually construct export code unless a custom schema is required.

Save Annotated Images and Videos

Python prediction can save outputs with:

results = model.predict(
    source="image.jpg",
    save=True
)

For an individual result:

result.save(
    filename="result.jpg"
)

The save() method creates an annotated result containing the detected boxes and other supported overlays.

CLI Predict mode also saves output by default in many normal CLI workflows, while the Python API defaults to not saving unless requested.

Save Results as Text Labels

You can request text output during prediction:

model.predict(
    source="images/",
    save_txt=True,
    save_conf=True
)

Current Predict documentation describes text detections in the general form:

class x_center y_center width height confidence

when confidence saving is enabled.

The exact output can differ for tasks such as segmentation, pose, OBB, or tracking because those tasks contain additional task-specific information.

Convert Results to JSON or NumPy

Current Ultralytics Results objects provide:

result.to_json()

which returns JSON-formatted prediction data.

Example:

json_output = result.to_json()

print(json_output)

For NumPy:

numpy_result = result.numpy()

This returns a Results copy whose tensor-backed prediction structures have been converted to NumPy arrays.

You can also convert a specific field:

boxes = (
    result.boxes.xyxy
    .cpu()
    .numpy()
)

Current Results also support:

result.to_csv()

and:

result.to_df()

for structured export workflows.

How to Filter YOLOv8 Output

Filtering can happen before inference output is finalized or after Results objects are returned.

Prediction-time filtering is useful when you want fewer detections from the model pipeline.

Post-processing filtering is useful when application rules are more specific.

Filter by Confidence Threshold

At prediction time:

results = model.predict(
    source="image.jpg",
    conf=0.50
)

After prediction:

for box in result.boxes:

    confidence = float(box.conf[0])

    if confidence >= 0.80:
        print(box.xyxy)

You can also filter the whole Boxes object:

boxes = result.boxes

selected = boxes[
    boxes.conf >= 0.80
]

Filter by Class

Suppose class 2 is the class you want.

You can use:

for box in result.boxes:

    class_id = int(box.cls[0])

    if class_id == 2:
        print(box.xyxy)

Or use vectorized filtering:

selected = result.boxes[
    result.boxes.cls == 2
]

This is helpful when one model detects many classes but your application only needs a subset.

Select Specific Detections

You can combine multiple conditions:

selected = []

for box in result.boxes:

    class_id = int(box.cls[0])
    confidence = float(box.conf[0])

    if (
        class_id == 2
        and confidence >= 0.75
    ):
        selected.append(
            box.xyxy[0]
            .cpu()
            .tolist()
        )

print(selected)

You can also filter by:

bounding-box size
image region
tracking ID
object center
class
confidence

depending on the application.

Common YOLOv8 Output Problems

Most YOLOv8 output problems come from misunderstanding task-specific fields, coordinate formats, tensor devices, or empty detections.

Inspecting the type, shape, and relevant Results field usually reveals the cause.

Empty Prediction Results

A valid Results object can contain zero detections.

For example:

results = model("image.jpg")

result = results[0]

print(len(result.boxes))

might return:

0

This means inference completed but no detection survived filtering.

Possible causes include:

no target objects
confidence threshold too high
poor model performance
very small objects
wrong model

Always distinguish:

empty result

from:

inference error

because they are different situations.

Incorrect Bounding Box Coordinates

A common mistake is confusing coordinate formats.

For example:

[100, 80, 400, 500]

means one thing in XYXY:

left = 100
top = 80
right = 400
bottom = 500

but something different in XYWH:

center x = 100
center y = 80
width = 400
height = 500

Always check whether you are reading:

boxes.xyxy

or:

boxes.xywh

Normalized variants should also not be confused with pixel values.

Unexpected Output Shape

The output shape depends on the task.

Detection:

boxes.data
→ (N, 6/7)

Segmentation:

masks.data
→ (N, H, W)

Pose:

keypoints.data
→ (N, K, 2/3)

OBB:

obb.xywhr
→ (N, 5)

and:

obb.xyxyxyxy
→ (N, 4, 2)

These task-specific shapes are documented by current Ultralytics Results interfaces.

Do not assume every YOLOv8 model returns the same tensor layout.

GPU Tensor Conversion Errors

If inference runs on CUDA, output tensors may remain on the GPU.

This can fail:

array = result.boxes.xyxy.numpy()

if the tensor is still CUDA-backed.

Use:

array = (
    result.boxes.xyxy
    .cpu()
    .numpy()
)

or:

result_cpu = result.cpu()

result_numpy = result_cpu.numpy()

Current Ultralytics Results exposes both cpu() and numpy() conversion methods.

FAQs About YOLOv8 Output Format

What is the output format of YOLOv8?

The output depends on the task.

For detection, the primary output is:

result.boxes

with raw rows containing:

[x1, y1, x2, y2, confidence, class_id]

plus an optional tracking ID for tracked detections.

Segmentation uses result.masks, pose uses result.keypoints, and OBB models use result.obb.

What does YOLOv8 return after inference?

The normal Python prediction call returns one Results object per input image or frame.

For example:

results = model("image.jpg")

result = results[0]

The Results object can contain:

boxes
masks
keypoints
probs
obb
orig_img
orig_shape
names
path
speed

depending on the model task.

How do I get bounding box coordinates from YOLOv8?

Use:

result.boxes.xyxy

for:

[x1, y1, x2, y2]

or:

result.boxes.xywh

for:

[x_center, y_center, width, height]

Normalized forms are available through xyxyn and xywhn.

Does YOLOv8 output confidence scores?

Yes.

For detection:

result.boxes.conf

returns one confidence score per box.

For OBB:

result.obb.conf

provides confidence values for rotated detections.

How do I get class names from YOLOv8 output?

First get the class ID:

class_id = int(box.cls[0])

Then:

class_name = result.names[class_id]

result.names contains the model’s class-ID-to-name mapping.

Can YOLOv8 output segmentation masks and keypoints?

Yes.

Instance segmentation uses:

result.masks

including:

result.masks.data
result.masks.xy
result.masks.xyn

Pose uses:

result.keypoints

including:

result.keypoints.xy
result.keypoints.xyn
result.keypoints.conf

when confidence data is available.

How can I save YOLOv8 results in JSON format?

Current Ultralytics Results provides:

json_output = result.to_json()

You can write it to disk:

with open(
    "results.json",
    "w"
) as f:
    f.write(
        result.to_json()
    )

The Results API also supports to_csv(), to_df(), summary(), and numpy() for other export and analysis workflows.

Conclusion

The YOLOv8 output format is built around the Ultralytics Results object, which provides a consistent interface for multiple computer vision tasks.

A standard detection workflow is:

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

results = model("image.jpg")

result = results[0]

The most important detection outputs are:

result.boxes.xyxy
result.boxes.xywh
result.boxes.xyxyn
result.boxes.xywhn
result.boxes.conf
result.boxes.cls

Standard detection box data follows:

x1
y1
x2
y2
confidence
class ID

and tracked outputs can additionally contain a track ID.

The overall Results structure can be summarized as:

YOLOv8 Inference
       ↓
Results Object
       ↓
┌───────────────┬──────────────────────┐
│ Detection     │ result.boxes         │
│ Segmentation  │ result.masks         │
│ Pose          │ result.keypoints     │
│ OBB           │ result.obb           │
│ Classification│ result.probs         │
└───────────────┴──────────────────────┘

For segmentation, current Ultralytics returns instance masks through result.masks.data, polygon coordinates through masks.xy, and normalized polygons through masks.xyn.

For pose, keypoints are available through result.keypoints.xy, xyn, and optional confidence information.

For OBB models, rotated detections can be read in xywhr format or as four corner points through xyxyxyxy.

YOLOv8 results can also be converted or exported using:

result.cpu()
result.numpy()
result.to_json()
result.to_csv()
result.to_df()
result.summary()
result.save()
result.save_txt()

which makes the output suitable for NumPy analysis, APIs, databases, JSON files, CSV files, and custom computer vision applications.

A practical programmatic workflow is:

Run Inference
      ↓
Get Results Object
      ↓
Select Task Output
      ↓
Extract Coordinates
      ↓
Read Confidence
      ↓
Read Class IDs
      ↓
Filter Predictions
      ↓
Convert to CPU / NumPy
      ↓
Export JSON / CSV / Labels

Understanding this data structure is essential for moving beyond simple annotated images and building real YOLOv8 applications where detections need to be filtered, stored, tracked, analyzed, or passed to other software systems.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top