YOLOv8 Results Boxes: Bounding Box Output Explained

YOLOv8 Results Boxes are the structured bounding-box outputs returned after object detection. In the Ultralytics Python API, detection results are stored inside a Results object, and the bounding-box information is available through result.boxes. The current Boxes class provides direct access to coordinates, confidence scores, class IDs, normalized coordinates, and optional tracking IDs.

For standard object detection, the most commonly used properties are:

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

If the results come from tracking, result.boxes.id can also contain persistent tracking IDs. Understanding these properties makes it much easier to build custom detection pipelines, export predictions, filter objects, or connect YOLOv8 outputs to another application.

Introduction to YOLOv8 Results Boxes

When YOLOv8 processes an image, it does not only return an annotated picture. The Python API returns structured result objects containing the information generated by the model.

For a detection model, the workflow is:

Input Image
    ↓
YOLOv8 Inference
    ↓
Results Object
    ↓
Boxes Object
    ↓
Coordinates
Confidence
Class IDs
Tracking IDs if available

A simple prediction example is:

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

results = model("image.jpg")

result = results[0]

print(result.boxes)

The Boxes object is designed specifically for managing detection boxes and supports several coordinate representations as well as conversion between CPU, GPU, tensor, and NumPy forms.

This structured output is one of the main reasons the Python API is useful. Instead of manually parsing raw neural-network output, developers can work with properties such as xyxy, conf, and cls directly.

What Are Results Boxes in YOLOv8?

Results boxes are the detected object bounding boxes stored inside each YOLOv8 Results object.

For example:

results = model("image.jpg")

boxes = results[0].boxes

The current Ultralytics Boxes class can contain data in either six-column or seven-column form. Standard detections contain coordinates, confidence, and class ID, while tracked detections can additionally include a track ID.

Conceptually:

Standard detection:

x1 y1 x2 y2 confidence class_id

Tracking result:

x1 y1 x2 y2 track_id confidence class_id

This information is wrapped inside the Boxes class so you normally do not need to manually index raw columns.

Role of Bounding Boxes in Detection Results

Bounding boxes identify where detected objects appear inside the image.

For example:

Detected class:
person

Bounding box:
x1 = 120
y1 = 80
x2 = 360
y2 = 640

The coordinates define the rectangular region containing the detected object.

Bounding boxes are useful for:

drawing detections
cropping detected objects
tracking objects
counting objects
measuring position
zone monitoring
saving annotations

Without the box coordinates, you would know which classes were detected but not where they appeared.

How YOLOv8 Stores Detection Output

The current Boxes object stores raw detection information internally in:

boxes.data

and exposes easier properties on top of that data.

Important attributes include:

data
orig_shape
is_track
xyxy
xywh
xyxyn
xywhn
conf
cls
id

The orig_shape value stores the original image dimensions as:

(height, width)

and is used when normalized coordinate properties are calculated.

Understanding the YOLOv8 Boxes Object

The Boxes class acts as a convenient interface between the raw model output and your application.

A basic example is:

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

results = model("image.jpg")

boxes = results[0].boxes

print(boxes.xyxy)
print(boxes.conf)
print(boxes.cls)

The number of rows corresponds to the number of detections.

If five objects were detected:

boxes.xyxy shape
→ [5, 4]

boxes.conf shape
→ [5]

boxes.cls shape
→ [5]

The corresponding row positions refer to the same detection.

Bounding Box Coordinates

The most common box property is:

boxes.xyxy

It returns coordinates in:

[x1, y1, x2, y2]

format.

For example:

print(result.boxes.xyxy)

might produce values conceptually like:

[
 [120, 80, 360, 640],
 [500, 200, 720, 480]
]

Current Ultralytics documents xyxy as an (N, 4) tensor or NumPy array where N is the number of detected boxes.

Confidence Scores

Confidence values are available using:

boxes.conf

For example:

print(result.boxes.conf)

may return:

tensor([0.92, 0.81, 0.67])

Each confidence value corresponds to the detection at the same index.

For example:

Box 0 → confidence 0.92
Box 1 → confidence 0.81
Box 2 → confidence 0.67

The current Boxes.conf property returns one confidence score per detection.

Class IDs

Predicted class IDs are stored in:

boxes.cls

Example:

print(result.boxes.cls)

may return:

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

These values represent class indices.

You can convert them to names using:

names = result.names

for box in result.boxes:
    class_id = int(box.cls[0])
    class_name = names[class_id]

    print(class_name)

The current cls property returns one class identifier for each detected box.

YOLOv8 Bounding Box Coordinate Formats

YOLOv8 exposes several box coordinate formats so developers do not need to manually convert them.

The main forms are:

xyxy
xywh
xyxyn
xywhn

The difference is whether coordinates represent corners or center dimensions and whether they are absolute pixels or normalized values.

XYXY Format

Access:

boxes.xyxy

Format:

[x1, y1, x2, y2]

where:

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

Example:

[100, 60, 400, 500]

means the box begins at pixel coordinate:

(100, 60)

and ends at:

(400, 500)

Ultralytics stores standard box coordinates internally in this form.

XYWH Format

Access:

boxes.xywh

Format:

[x_center, y_center, width, height]

For example:

[250, 280, 300, 440]

means:

center x = 250
center y = 280
width = 300
height = 440

Current Ultralytics converts xyxy boxes into center-based XYWH form automatically through the xywh property.

XYWH is useful for:

custom visualization
geometric calculations
YOLO-style annotation conversion
object-center analysis

Normalized Coordinates

Normalized coordinates scale values relative to the original image dimensions.

YOLOv8 provides:

boxes.xyxyn

and:

boxes.xywhn

xyxyn returns:

[x1, y1, x2, y2]

normalized approximately to:

0.0–1.0

relative to the original image dimensions.

xywhn returns normalized:

[x_center, y_center, width, height]

For example:

print(result.boxes.xywhn)

could produce:

0.50 0.42 0.20 0.31

Normalized coordinates are especially useful when saving YOLO-format labels because they do not depend directly on pixel resolution.

How to Access YOLOv8 Results Boxes in Python

Accessing box results requires only a few lines of Python.

Basic workflow:

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

results = model("image.jpg")

result = results[0]

boxes = result.boxes

Once the Boxes object is available, coordinates, classes, and confidence values can be extracted directly.

Access results.boxes

Example:

results = model("image.jpg")

result = results[0]

print(result.boxes)

If several images are processed:

results = model([
    "image1.jpg",
    "image2.jpg"
])

for result in results:
    print(result.boxes)

Each Results object contains the boxes for its own image.

This means:

results[0].boxes
→ detections from image 1

results[1].boxes
→ detections from image 2

Extract Box Coordinates

To get all boxes:

boxes = result.boxes.xyxy

print(boxes)

To process each detected object separately:

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

    print(xyxy)

You can convert individual values:

for box in result.boxes:
    x1, y1, x2, y2 = box.xyxy[0].tolist()

    print(
        x1,
        y1,
        x2,
        y2
    )

Extract Confidence and Class Values

Example:

for box in result.boxes:

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

    print(
        "Class:",
        class_id,
        "Confidence:",
        confidence
    )

You can also retrieve the class name:

for box in result.boxes:

    class_id = int(box.cls[0])

    class_name = result.names[class_id]

    confidence = float(box.conf[0])

    print(
        class_name,
        confidence
    )

This gives a human-readable output such as:

person 0.93
car 0.87
dog 0.76

Working with Multiple Detection Boxes

Most real-world images contain more than one detection.

The Boxes object makes it easy to iterate through all predictions and maintain the relationship between coordinates, confidence, and class ID.

The important rule is that matching indices refer to the same detection.

For example:

boxes.xyxy[3]
boxes.conf[3]
boxes.cls[3]

all represent detection number 3.

Loop Through Detected Objects

A common loop is:

for box in result.boxes:

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

    confidence = float(box.conf[0])

    class_id = int(box.cls[0])

    print(
        xyxy,
        confidence,
        class_id
    )

This is useful when every object needs individual processing.

For example:

Detection 1
→ check class
→ store result

Detection 2
→ check class
→ store result

Match Boxes with Classes

Example:

for box in result.boxes:

    class_id = int(box.cls[0])

    class_name = result.names[class_id]

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

    print(
        class_name,
        coordinates
    )

You might obtain:

person [100, 50, 280, 600]
car [350, 300, 700, 610]

This makes it easy to build class-specific logic.

Filter Specific Predictions

Suppose you only want persons.

If the person class ID is:

0

you can use:

for box in result.boxes:

    class_id = int(box.cls[0])

    if class_id != 0:
        continue

    print(box.xyxy)

You can also combine class and confidence filtering:

for box in result.boxes:

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

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

YOLOv8 Boxes for Object Tracking

When YOLOv8 Track mode is used, bounding boxes can include tracking IDs in addition to the normal coordinates, confidence, and class values.

Current Ultralytics stores standard detection boxes with six columns, while tracked boxes contain seven values because a tracking ID is included. The Boxes.is_track attribute indicates whether IDs are present.

Access Tracking IDs

Tracking example:

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

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

for result in results:

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

Current Boxes.id returns tracking IDs only when tracking information is available. Otherwise, it returns None.

You can convert them to integers:

track_ids = (
    result.boxes.id
    .int()
    .cpu()
    .tolist()
)

Match Boxes Across Video Frames

Tracking IDs make it possible to associate the same object across frames.

Example:

Frame 1
Person box → ID 4

Frame 2
Person box → ID 4

Frame 3
Person box → ID 4

A typical loop is:

for result in results:

    if (
        result.boxes is None
        or result.boxes.id is None
    ):
        continue

    for box in result.boxes:

        track_id = int(box.id[0])

        class_id = int(box.cls[0])

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

        print(
            track_id,
            class_id,
            coordinates
        )

This can be used for counting, trajectories, dwell time, entry and exit detection, or object history.

Convert and Export YOLOv8 Box Results

YOLOv8 detection results often begin as PyTorch tensors, particularly when inference runs on a GPU.

For logging, JSON, CSV, NumPy processing, or database storage, you may need to move the data to CPU memory and convert it.

The Boxes class inherits convenience methods such as cpu(), numpy(), cuda(), and to().

Convert Boxes to NumPy

The cleanest approach is:

boxes_numpy = result.boxes.numpy()

The returned Boxes object contains NumPy-backed data.

You can then access:

print(boxes_numpy.xyxy)
print(boxes_numpy.conf)
print(boxes_numpy.cls)

If you only need one property:

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

when the property is a PyTorch tensor.

Move Box Data from GPU to CPU

Use:

boxes_cpu = result.boxes.cpu()

Then:

print(boxes_cpu.xyxy)

This is important when inference runs on CUDA.

A common pattern is:

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

The cpu() method returns a copy of the Boxes object with tensor data moved to CPU memory.

Save Bounding Box Results

For custom export:

import csv

with open(
    "detections.csv",
    "w",
    newline=""
) as f:

    writer = csv.writer(f)

    writer.writerow([
        "class_id",
        "confidence",
        "x1",
        "y1",
        "x2",
        "y2"
    ])

    for box in result.boxes:

        class_id = int(box.cls[0])

        confidence = float(box.conf[0])

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

        writer.writerow([
            class_id,
            confidence,
            x1,
            y1,
            x2,
            y2
        ])

The same structure can be adapted for:

JSON
CSV
database rows
REST API payloads
analytics events

Current Ultralytics Results also contains summary functionality that produces structured dictionaries containing class name, class ID, confidence, box coordinates, and track ID when tracking is enabled.

Filtering YOLOv8 Results Boxes

Filtering can happen either during prediction or after the results are returned.

Prediction-level filtering reduces detections before you receive them.

Result-level filtering lets your application apply custom rules to the returned boxes.

Both approaches are useful depending on the workflow.

Filter by Confidence Score

During prediction:

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

After prediction:

for box in result.boxes:

    confidence = float(box.conf[0])

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

Vector-style filtering is also possible:

boxes = result.boxes

mask = boxes.conf >= 0.70

high_conf_boxes = boxes[mask]

Because Boxes supports indexing, this can produce a filtered Boxes object.

Filter by Class ID

Suppose class ID 2 represents car.

You can filter:

for box in result.boxes:

    if int(box.cls[0]) == 2:
        print(box.xyxy)

Or:

boxes = result.boxes

car_boxes = boxes[
    boxes.cls == 2
]

This is useful when one model detects many classes but the application only cares about specific categories.

Select Specific Bounding Boxes

You can combine conditions.

Example:

selected = []

for box in result.boxes:

    cls_id = int(box.cls[0])

    conf = float(box.conf[0])

    if cls_id == 2 and conf >= 0.80:
        selected.append(
            box.xyxy[0].cpu().tolist()
        )

print(selected)

This could represent:

only cars
with confidence >= 0.80

Additional filters can also use:

box width
box height
box center
screen region
tracking ID

depending on the application.

Common YOLOv8 Results Boxes Problems

Problems with results.boxes usually come from misunderstanding output shape, tensor devices, empty predictions, or coordinate formats.

Checking the object type and shape is often the fastest way to diagnose the issue.

No Boxes Returned

If:

print(result.boxes)

shows no detections, possible reasons include:

target object absent
confidence threshold too high
incorrect model
poor model performance
very small objects
unsupported input

Try inspecting:

print(len(result.boxes))

If the length is:

0

the prediction completed but no boxes survived detection filtering.

This is different from the inference itself failing.

Incorrect Coordinate Values

A common issue is confusing:

xyxy

with:

xywh

For example:

[100, 50, 300, 400]

in XYXY means:

left = 100
top = 50
right = 300
bottom = 400

but in XYWH it means:

center x = 100
center y = 50
width = 300
height = 400

The meanings are completely different.

Always check whether you are using:

boxes.xyxy

or:

boxes.xywh

before interpreting the numbers.

Empty Detection Results

A result object may exist even if no objects were detected.

For example:

results = model("empty_scene.jpg")

result = results[0]

may still be valid while:

len(result.boxes)

is zero.

Use:

if len(result.boxes) == 0:
    print("No detections")

before attempting to loop through detections.

An empty Boxes collection is not necessarily an error.

CUDA Tensor Conversion Errors

A common PyTorch error occurs when trying to convert a GPU tensor directly to NumPy.

Incorrect:

array = result.boxes.xyxy.numpy()

when the tensor is still on CUDA.

Use:

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

or convert the whole result structure:

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

Current Ultralytics exposes both cpu() and numpy() methods on the Boxes object specifically for these conversions.

FAQs About YOLOv8 Results Boxes

What is results.boxes in YOLOv8?

results.boxes is the bounding-box container attached to an Ultralytics detection Results object.

It stores:

coordinates
confidence scores
class IDs
optional tracking IDs

and provides multiple coordinate formats such as xyxy, xywh, xyxyn, and xywhn.

How do I get bounding box coordinates from YOLOv8?

Example:

results = model("image.jpg")

boxes = results[0].boxes.xyxy

print(boxes)

To process individual detections:

for box in results[0].boxes:
    print(box.xyxy[0])

The returned format is:

[x1, y1, x2, y2]

for xyxy.

What does boxes.xyxy mean in YOLOv8?

boxes.xyxy contains coordinates in:

[x1, y1, x2, y2]

format.

Where:

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

Current Ultralytics returns this as an (N, 4) tensor or array where N is the number of detections.

What is the difference between xyxy and xywh?

xyxy represents two opposite box corners:

x1 y1 x2 y2

xywh represents:

x_center
y_center
width
height

Ultralytics provides both directly through the Boxes object.

How do I get confidence scores from YOLOv8 boxes?

Use:

confidence = result.boxes.conf

or:

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

The conf property returns one confidence value for every detection.

How do I get class IDs from YOLOv8 results?

Use:

class_ids = result.boxes.cls

For individual boxes:

for box in result.boxes:
    class_id = int(box.cls[0])
    print(class_id)

To obtain the name:

class_name = result.names[class_id]

The current cls property provides the category ID associated with every detection box.

Can YOLOv8 results boxes include tracking IDs?

Yes.

When Track mode returns tracked boxes, the Boxes structure can contain:

result.boxes.id

The current implementation identifies tracked box data using is_track=True, and the tracking ID is included as an additional value in the underlying box data.

For example:

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

Conclusion

YOLOv8 Results Boxes provide structured access to the detections returned by an Ultralytics object detection model.

A standard Python workflow is:

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

results = model("image.jpg")

result = results[0]

boxes = result.boxes

The most important properties are:

boxes.xyxy
→ [x1, y1, x2, y2]

boxes.xywh
→ [x_center, y_center, width, height]

boxes.xyxyn
→ normalized XYXY

boxes.xywhn
→ normalized XYWH

boxes.conf
→ confidence scores

boxes.cls
→ class IDs

boxes.id
→ tracking IDs when available

All of these properties are part of the current Ultralytics Boxes API.

A complete extraction example is:

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

results = model("image.jpg")

for result in results:

    if result.boxes is None:
        continue

    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
        )

The internal relationship can be summarized as:

YOLOv8 Prediction
       ↓
Results Object
       ↓
Boxes Object
       ↓
Coordinates
Confidence
Class
Tracking ID

For normal detection, the internal box data contains coordinates, confidence, and class information. Tracking adds an optional track ID. Current Ultralytics supports six-value standard detections and seven-value tracked detection rows internally.

Understanding the Boxes object is especially important when building custom applications because it allows YOLOv8 detections to be filtered, converted, exported, tracked, counted, stored, and connected directly to application logic without manually decoding raw neural-network outputs.

Leave a Comment

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

Scroll to Top