Skip to content

Reference for ultralytics/models/yolo/detect/val.py

Note

This file is available at https://github.com/ultralytics/ultralytics/blob/main/ultralytics/models/yolo/detect/val.py. If you spot a problem please help fix it by contributing a Pull Request 🛠️. Thank you 🙏!


ultralytics.models.yolo.detect.val.DetectionValidator

DetectionValidator(
    dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None
)

Bases: BaseValidator

A class extending the BaseValidator class for validation based on a detection model.

This class implements validation functionality specific to object detection tasks, including metrics calculation, prediction processing, and visualization of results.

Attributes:

Name Type Description
nt_per_class ndarray

Number of targets per class.

nt_per_image ndarray

Number of targets per image.

is_coco bool

Whether the dataset is COCO.

is_lvis bool

Whether the dataset is LVIS.

class_map list

Mapping from model class indices to dataset class indices.

metrics DetMetrics

Object detection metrics calculator.

iouv Tensor

IoU thresholds for mAP calculation.

niou int

Number of IoU thresholds.

lb list

List for storing ground truth labels for hybrid saving.

jdict list

List for storing JSON detection results.

stats dict

Dictionary for storing statistics during validation.

Examples:

>>> from ultralytics.models.yolo.detect import DetectionValidator
>>> args = dict(model="yolo11n.pt", data="coco8.yaml")
>>> validator = DetectionValidator(args=args)
>>> validator()

Parameters:

Name Type Description Default
dataloader DataLoader

Dataloader to use for validation.

None
save_dir Path

Directory to save results.

None
pbar Any

Progress bar for displaying progress.

None
args dict

Arguments for the validator.

None
_callbacks list

List of callback functions.

None
Source code in ultralytics/models/yolo/detect/val.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None):
    """
    Initialize detection validator with necessary variables and settings.

    Args:
        dataloader (torch.utils.data.DataLoader, optional): Dataloader to use for validation.
        save_dir (Path, optional): Directory to save results.
        pbar (Any, optional): Progress bar for displaying progress.
        args (dict, optional): Arguments for the validator.
        _callbacks (list, optional): List of callback functions.
    """
    super().__init__(dataloader, save_dir, pbar, args, _callbacks)
    self.nt_per_class = None
    self.nt_per_image = None
    self.is_coco = False
    self.is_lvis = False
    self.class_map = None
    self.args.task = "detect"
    self.metrics = DetMetrics(save_dir=self.save_dir)
    self.iouv = torch.linspace(0.5, 0.95, 10)  # IoU vector for mAP@0.5:0.95
    self.niou = self.iouv.numel()

build_dataset

build_dataset(img_path, mode='val', batch=None)

Build YOLO Dataset.

Parameters:

Name Type Description Default
img_path str

Path to the folder containing images.

required
mode str

train mode or val mode, users are able to customize different augmentations for each mode.

'val'
batch int

Size of batches, this is for rect.

None

Returns:

Type Description
Dataset

YOLO dataset.

Source code in ultralytics/models/yolo/detect/val.py
292
293
294
295
296
297
298
299
300
301
302
303
304
def build_dataset(self, img_path, mode="val", batch=None):
    """
    Build YOLO Dataset.

    Args:
        img_path (str): Path to the folder containing images.
        mode (str): `train` mode or `val` mode, users are able to customize different augmentations for each mode.
        batch (int, optional): Size of batches, this is for `rect`.

    Returns:
        (Dataset): YOLO dataset.
    """
    return build_yolo_dataset(self.args, img_path, batch, self.data, mode=mode, stride=self.stride)

eval_json

eval_json(stats)

Evaluate YOLO output in JSON format and return performance statistics.

Parameters:

Name Type Description Default
stats dict

Current statistics dictionary.

required

Returns:

Type Description
dict

Updated statistics dictionary with COCO/LVIS evaluation results.

Source code in ultralytics/models/yolo/detect/val.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
def eval_json(self, stats):
    """
    Evaluate YOLO output in JSON format and return performance statistics.

    Args:
        stats (dict): Current statistics dictionary.

    Returns:
        (dict): Updated statistics dictionary with COCO/LVIS evaluation results.
    """
    if self.args.save_json and (self.is_coco or self.is_lvis) and len(self.jdict):
        pred_json = self.save_dir / "predictions.json"  # predictions
        anno_json = (
            self.data["path"]
            / "annotations"
            / ("instances_val2017.json" if self.is_coco else f"lvis_v1_{self.args.split}.json")
        )  # annotations
        pkg = "pycocotools" if self.is_coco else "lvis"
        LOGGER.info(f"\nEvaluating {pkg} mAP using {pred_json} and {anno_json}...")
        try:  # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb
            for x in pred_json, anno_json:
                assert x.is_file(), f"{x} file not found"
            check_requirements("pycocotools>=2.0.6" if self.is_coco else "lvis>=0.5.3")
            if self.is_coco:
                from pycocotools.coco import COCO  # noqa
                from pycocotools.cocoeval import COCOeval  # noqa

                anno = COCO(str(anno_json))  # init annotations api
                pred = anno.loadRes(str(pred_json))  # init predictions api (must pass string, not Path)
                val = COCOeval(anno, pred, "bbox")
            else:
                from lvis import LVIS, LVISEval

                anno = LVIS(str(anno_json))  # init annotations api
                pred = anno._load_json(str(pred_json))  # init predictions api (must pass string, not Path)
                val = LVISEval(anno, pred, "bbox")
            val.params.imgIds = [int(Path(x).stem) for x in self.dataloader.dataset.im_files]  # images to eval
            val.evaluate()
            val.accumulate()
            val.summarize()
            if self.is_lvis:
                val.print_results()  # explicitly call print_results
            # update mAP50-95 and mAP50
            stats[self.metrics.keys[-1]], stats[self.metrics.keys[-2]] = (
                val.stats[:2] if self.is_coco else [val.results["AP"], val.results["AP50"]]
            )
            if self.is_lvis:
                stats["metrics/APr(B)"] = val.results["APr"]
                stats["metrics/APc(B)"] = val.results["APc"]
                stats["metrics/APf(B)"] = val.results["APf"]
                stats["fitness"] = val.results["AP"]
        except Exception as e:
            LOGGER.warning(f"{pkg} unable to run: {e}")
    return stats

finalize_metrics

finalize_metrics(*args, **kwargs)

Set final values for metrics speed and confusion matrix.

Parameters:

Name Type Description Default
*args Any

Variable length argument list.

()
**kwargs Any

Arbitrary keyword arguments.

{}
Source code in ultralytics/models/yolo/detect/val.py
229
230
231
232
233
234
235
236
237
238
def finalize_metrics(self, *args, **kwargs):
    """
    Set final values for metrics speed and confusion matrix.

    Args:
        *args (Any): Variable length argument list.
        **kwargs (Any): Arbitrary keyword arguments.
    """
    self.metrics.speed = self.speed
    self.metrics.confusion_matrix = self.confusion_matrix

get_dataloader

get_dataloader(dataset_path, batch_size)

Construct and return dataloader.

Parameters:

Name Type Description Default
dataset_path str

Path to the dataset.

required
batch_size int

Size of each batch.

required

Returns:

Type Description
DataLoader

Dataloader for validation.

Source code in ultralytics/models/yolo/detect/val.py
306
307
308
309
310
311
312
313
314
315
316
317
318
def get_dataloader(self, dataset_path, batch_size):
    """
    Construct and return dataloader.

    Args:
        dataset_path (str): Path to the dataset.
        batch_size (int): Size of each batch.

    Returns:
        (torch.utils.data.DataLoader): Dataloader for validation.
    """
    dataset = self.build_dataset(dataset_path, batch=batch_size, mode="val")
    return build_dataloader(dataset, batch_size, self.args.workers, shuffle=False, rank=-1)  # return dataloader

get_desc

get_desc()

Return a formatted string summarizing class metrics of YOLO model.

Source code in ultralytics/models/yolo/detect/val.py
109
110
111
def get_desc(self):
    """Return a formatted string summarizing class metrics of YOLO model."""
    return ("%22s" + "%11s" * 6) % ("Class", "Images", "Instances", "Box(P", "R", "mAP50", "mAP50-95)")

get_stats

get_stats()

Calculate and return metrics statistics.

Returns:

Type Description
dict

Dictionary containing metrics results.

Source code in ultralytics/models/yolo/detect/val.py
240
241
242
243
244
245
246
247
248
249
250
251
252
253
def get_stats(self):
    """
    Calculate and return metrics statistics.

    Returns:
        (dict): Dictionary containing metrics results.
    """
    stats = {k: torch.cat(v, 0).cpu().numpy() for k, v in self.stats.items()}  # to numpy
    self.nt_per_class = np.bincount(stats["target_cls"].astype(int), minlength=self.nc)
    self.nt_per_image = np.bincount(stats["target_img"].astype(int), minlength=self.nc)
    stats.pop("target_img", None)
    if len(stats):
        self.metrics.process(**stats, on_plot=self.on_plot)
    return self.metrics.results_dict

init_metrics

init_metrics(model)

Initialize evaluation metrics for YOLO detection validation.

Parameters:

Name Type Description Default
model Module

Model to validate.

required
Source code in ultralytics/models/yolo/detect/val.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def init_metrics(self, model):
    """
    Initialize evaluation metrics for YOLO detection validation.

    Args:
        model (torch.nn.Module): Model to validate.
    """
    val = self.data.get(self.args.split, "")  # validation path
    self.is_coco = (
        isinstance(val, str)
        and "coco" in val
        and (val.endswith(f"{os.sep}val2017.txt") or val.endswith(f"{os.sep}test-dev2017.txt"))
    )  # is COCO
    self.is_lvis = isinstance(val, str) and "lvis" in val and not self.is_coco  # is LVIS
    self.class_map = converter.coco80_to_coco91_class() if self.is_coco else list(range(1, len(model.names) + 1))
    self.args.save_json |= self.args.val and (self.is_coco or self.is_lvis) and not self.training  # run final val
    self.names = model.names
    self.nc = len(model.names)
    self.end2end = getattr(model, "end2end", False)
    self.metrics.names = self.names
    self.metrics.plot = self.args.plots
    self.confusion_matrix = ConfusionMatrix(nc=self.nc, conf=self.args.conf)
    self.seen = 0
    self.jdict = []
    self.stats = dict(tp=[], conf=[], pred_cls=[], target_cls=[], target_img=[])

plot_predictions

plot_predictions(batch, preds, ni)

Plot predicted bounding boxes on input images and save the result.

Parameters:

Name Type Description Default
batch dict

Batch containing images and annotations.

required
preds List[Tensor]

List of predictions from the model.

required
ni int

Batch index.

required
Source code in ultralytics/models/yolo/detect/val.py
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
def plot_predictions(self, batch, preds, ni):
    """
    Plot predicted bounding boxes on input images and save the result.

    Args:
        batch (dict): Batch containing images and annotations.
        preds (List[torch.Tensor]): List of predictions from the model.
        ni (int): Batch index.
    """
    plot_images(
        batch["img"],
        *output_to_target(preds, max_det=self.args.max_det),
        paths=batch["im_file"],
        fname=self.save_dir / f"val_batch{ni}_pred.jpg",
        names=self.names,
        on_plot=self.on_plot,
    )  # pred

plot_val_samples

plot_val_samples(batch, ni)

Plot validation image samples.

Parameters:

Name Type Description Default
batch dict

Batch containing images and annotations.

required
ni int

Batch index.

required
Source code in ultralytics/models/yolo/detect/val.py
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
def plot_val_samples(self, batch, ni):
    """
    Plot validation image samples.

    Args:
        batch (dict): Batch containing images and annotations.
        ni (int): Batch index.
    """
    plot_images(
        batch["img"],
        batch["batch_idx"],
        batch["cls"].squeeze(-1),
        batch["bboxes"],
        paths=batch["im_file"],
        fname=self.save_dir / f"val_batch{ni}_labels.jpg",
        names=self.names,
        on_plot=self.on_plot,
    )

postprocess

postprocess(preds)

Apply Non-maximum suppression to prediction outputs.

Parameters:

Name Type Description Default
preds Tensor

Raw predictions from the model.

required

Returns:

Type Description
List[Tensor]

Processed predictions after NMS.

Source code in ultralytics/models/yolo/detect/val.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def postprocess(self, preds):
    """
    Apply Non-maximum suppression to prediction outputs.

    Args:
        preds (torch.Tensor): Raw predictions from the model.

    Returns:
        (List[torch.Tensor]): Processed predictions after NMS.
    """
    return ops.non_max_suppression(
        preds,
        self.args.conf,
        self.args.iou,
        nc=0 if self.args.task == "detect" else self.nc,
        multi_label=True,
        agnostic=self.args.single_cls or self.args.agnostic_nms,
        max_det=self.args.max_det,
        end2end=self.end2end,
        rotated=self.args.task == "obb",
    )

pred_to_json

pred_to_json(predn, filename)

Serialize YOLO predictions to COCO json format.

Parameters:

Name Type Description Default
predn Tensor

Predictions in the format (x1, y1, x2, y2, conf, class).

required
filename str

Image filename.

required
Source code in ultralytics/models/yolo/detect/val.py
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
def pred_to_json(self, predn, filename):
    """
    Serialize YOLO predictions to COCO json format.

    Args:
        predn (torch.Tensor): Predictions in the format (x1, y1, x2, y2, conf, class).
        filename (str): Image filename.
    """
    stem = Path(filename).stem
    image_id = int(stem) if stem.isnumeric() else stem
    box = ops.xyxy2xywh(predn[:, :4])  # xywh
    box[:, :2] -= box[:, 2:] / 2  # xy center to top-left corner
    for p, b in zip(predn.tolist(), box.tolist()):
        self.jdict.append(
            {
                "image_id": image_id,
                "category_id": self.class_map[int(p[5])],
                "bbox": [round(x, 3) for x in b],
                "score": round(p[4], 5),
            }
        )

preprocess

preprocess(batch)

Preprocess batch of images for YOLO validation.

Parameters:

Name Type Description Default
batch dict

Batch containing images and annotations.

required

Returns:

Type Description
dict

Preprocessed batch.

Source code in ultralytics/models/yolo/detect/val.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def preprocess(self, batch):
    """
    Preprocess batch of images for YOLO validation.

    Args:
        batch (dict): Batch containing images and annotations.

    Returns:
        (dict): Preprocessed batch.
    """
    batch["img"] = batch["img"].to(self.device, non_blocking=True)
    batch["img"] = (batch["img"].half() if self.args.half else batch["img"].float()) / 255
    for k in ["batch_idx", "cls", "bboxes"]:
        batch[k] = batch[k].to(self.device)

    return batch

print_results

print_results()

Print training/validation set metrics per class.

Source code in ultralytics/models/yolo/detect/val.py
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
def print_results(self):
    """Print training/validation set metrics per class."""
    pf = "%22s" + "%11i" * 2 + "%11.3g" * len(self.metrics.keys)  # print format
    LOGGER.info(pf % ("all", self.seen, self.nt_per_class.sum(), *self.metrics.mean_results()))
    if self.nt_per_class.sum() == 0:
        LOGGER.warning(f"no labels found in {self.args.task} set, can not compute metrics without labels")

    # Print results per class
    if self.args.verbose and not self.training and self.nc > 1 and len(self.stats):
        for i, c in enumerate(self.metrics.ap_class_index):
            LOGGER.info(
                pf % (self.names[c], self.nt_per_image[c], self.nt_per_class[c], *self.metrics.class_result(i))
            )

    if self.args.plots:
        for normalize in True, False:
            self.confusion_matrix.plot(
                save_dir=self.save_dir, names=self.names.values(), normalize=normalize, on_plot=self.on_plot
            )

save_one_txt

save_one_txt(predn, save_conf, shape, file)

Save YOLO detections to a txt file in normalized coordinates in a specific format.

Parameters:

Name Type Description Default
predn Tensor

Predictions in the format (x1, y1, x2, y2, conf, class).

required
save_conf bool

Whether to save confidence scores.

required
shape tuple

Shape of the original image.

required
file Path

File path to save the detections.

required
Source code in ultralytics/models/yolo/detect/val.py
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
def save_one_txt(self, predn, save_conf, shape, file):
    """
    Save YOLO detections to a txt file in normalized coordinates in a specific format.

    Args:
        predn (torch.Tensor): Predictions in the format (x1, y1, x2, y2, conf, class).
        save_conf (bool): Whether to save confidence scores.
        shape (tuple): Shape of the original image.
        file (Path): File path to save the detections.
    """
    from ultralytics.engine.results import Results

    Results(
        np.zeros((shape[0], shape[1]), dtype=np.uint8),
        path=None,
        names=self.names,
        boxes=predn[:, :6],
    ).save_txt(file, save_conf=save_conf)

update_metrics

update_metrics(preds, batch)

Update metrics with new predictions and ground truth.

Parameters:

Name Type Description Default
preds List[Tensor]

List of predictions from the model.

required
batch dict

Batch data containing ground truth.

required
Source code in ultralytics/models/yolo/detect/val.py
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
def update_metrics(self, preds, batch):
    """
    Update metrics with new predictions and ground truth.

    Args:
        preds (List[torch.Tensor]): List of predictions from the model.
        batch (dict): Batch data containing ground truth.
    """
    for si, pred in enumerate(preds):
        self.seen += 1
        npr = len(pred)
        stat = dict(
            conf=torch.zeros(0, device=self.device),
            pred_cls=torch.zeros(0, device=self.device),
            tp=torch.zeros(npr, self.niou, dtype=torch.bool, device=self.device),
        )
        pbatch = self._prepare_batch(si, batch)
        cls, bbox = pbatch.pop("cls"), pbatch.pop("bbox")
        nl = len(cls)
        stat["target_cls"] = cls
        stat["target_img"] = cls.unique()
        if npr == 0:
            if nl:
                for k in self.stats.keys():
                    self.stats[k].append(stat[k])
                if self.args.plots:
                    self.confusion_matrix.process_batch(detections=None, gt_bboxes=bbox, gt_cls=cls)
            continue

        # Predictions
        if self.args.single_cls:
            pred[:, 5] = 0
        predn = self._prepare_pred(pred, pbatch)
        stat["conf"] = predn[:, 4]
        stat["pred_cls"] = predn[:, 5]

        # Evaluate
        if nl:
            stat["tp"] = self._process_batch(predn, bbox, cls)
        if self.args.plots:
            self.confusion_matrix.process_batch(predn, bbox, cls)
        for k in self.stats.keys():
            self.stats[k].append(stat[k])

        # Save
        if self.args.save_json:
            self.pred_to_json(predn, batch["im_file"][si])
        if self.args.save_txt:
            self.save_one_txt(
                predn,
                self.args.save_conf,
                pbatch["ori_shape"],
                self.save_dir / "labels" / f"{Path(batch['im_file'][si]).stem}.txt",
            )





📅 Created 1 year ago ✏️ Updated 8 months ago