Skip to content

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

Note

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


ultralytics.models.yolo.segment.val.SegmentationValidator

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

Bases: DetectionValidator

A class extending the DetectionValidator class for validation based on a segmentation model.

Example
from ultralytics.models.yolo.segment import SegmentationValidator

args = dict(model="yolo11n-seg.pt", data="coco8-seg.yaml")
validator = SegmentationValidator(args=args)
validator()
Source code in ultralytics/models/yolo/segment/val.py
def __init__(self, dataloader=None, save_dir=None, pbar=None, args=None, _callbacks=None):
    """Initialize SegmentationValidator and set task to 'segment', metrics to SegmentMetrics."""
    super().__init__(dataloader, save_dir, pbar, args, _callbacks)
    self.plot_masks = None
    self.process = None
    self.args.task = "segment"
    self.metrics = SegmentMetrics(save_dir=self.save_dir, on_plot=self.on_plot)

_prepare_batch

_prepare_batch(si, batch)

Prepares a batch for training or inference by processing images and targets.

Source code in ultralytics/models/yolo/segment/val.py
def _prepare_batch(self, si, batch):
    """Prepares a batch for training or inference by processing images and targets."""
    prepared_batch = super()._prepare_batch(si, batch)
    midx = [si] if self.args.overlap_mask else batch["batch_idx"] == si
    prepared_batch["masks"] = batch["masks"][midx]
    return prepared_batch

_prepare_pred

_prepare_pred(pred, pbatch, proto)

Prepares a batch for training or inference by processing images and targets.

Source code in ultralytics/models/yolo/segment/val.py
def _prepare_pred(self, pred, pbatch, proto):
    """Prepares a batch for training or inference by processing images and targets."""
    predn = super()._prepare_pred(pred, pbatch)
    pred_masks = self.process(proto, pred[:, 6:], pred[:, :4], shape=pbatch["imgsz"])
    return predn, pred_masks

_process_batch

_process_batch(
    detections,
    gt_bboxes,
    gt_cls,
    pred_masks=None,
    gt_masks=None,
    overlap=False,
    masks=False,
)

Compute correct prediction matrix for a batch based on bounding boxes and optional masks.

Parameters:

Name Type Description Default
detections Tensor

Tensor of shape (N, 6) representing detected bounding boxes and associated confidence scores and class indices. Each row is of the format [x1, y1, x2, y2, conf, class].

required
gt_bboxes Tensor

Tensor of shape (M, 4) representing ground truth bounding box coordinates. Each row is of the format [x1, y1, x2, y2].

required
gt_cls Tensor

Tensor of shape (M,) representing ground truth class indices.

required
pred_masks Tensor | None

Tensor representing predicted masks, if available. The shape should match the ground truth masks.

None
gt_masks Tensor | None

Tensor of shape (M, H, W) representing ground truth masks, if available.

None
overlap bool

Flag indicating if overlapping masks should be considered.

False
masks bool

Flag indicating if the batch contains mask data.

False

Returns:

Type Description
Tensor

A correct prediction matrix of shape (N, 10), where 10 represents different IoU levels.

Note
  • If masks is True, the function computes IoU between predicted and ground truth masks.
  • If overlap is True and masks is True, overlapping masks are taken into account when computing IoU.
Example
detections = torch.tensor([[25, 30, 200, 300, 0.8, 1], [50, 60, 180, 290, 0.75, 0]])
gt_bboxes = torch.tensor([[24, 29, 199, 299], [55, 65, 185, 295]])
gt_cls = torch.tensor([1, 0])
correct_preds = validator._process_batch(detections, gt_bboxes, gt_cls)
Source code in ultralytics/models/yolo/segment/val.py
def _process_batch(self, detections, gt_bboxes, gt_cls, pred_masks=None, gt_masks=None, overlap=False, masks=False):
    """
    Compute correct prediction matrix for a batch based on bounding boxes and optional masks.

    Args:
        detections (torch.Tensor): Tensor of shape (N, 6) representing detected bounding boxes and
            associated confidence scores and class indices. Each row is of the format [x1, y1, x2, y2, conf, class].
        gt_bboxes (torch.Tensor): Tensor of shape (M, 4) representing ground truth bounding box coordinates.
            Each row is of the format [x1, y1, x2, y2].
        gt_cls (torch.Tensor): Tensor of shape (M,) representing ground truth class indices.
        pred_masks (torch.Tensor | None): Tensor representing predicted masks, if available. The shape should
            match the ground truth masks.
        gt_masks (torch.Tensor | None): Tensor of shape (M, H, W) representing ground truth masks, if available.
        overlap (bool): Flag indicating if overlapping masks should be considered.
        masks (bool): Flag indicating if the batch contains mask data.

    Returns:
        (torch.Tensor): A correct prediction matrix of shape (N, 10), where 10 represents different IoU levels.

    Note:
        - If `masks` is True, the function computes IoU between predicted and ground truth masks.
        - If `overlap` is True and `masks` is True, overlapping masks are taken into account when computing IoU.

    Example:
        ```python
        detections = torch.tensor([[25, 30, 200, 300, 0.8, 1], [50, 60, 180, 290, 0.75, 0]])
        gt_bboxes = torch.tensor([[24, 29, 199, 299], [55, 65, 185, 295]])
        gt_cls = torch.tensor([1, 0])
        correct_preds = validator._process_batch(detections, gt_bboxes, gt_cls)
        ```
    """
    if masks:
        if overlap:
            nl = len(gt_cls)
            index = torch.arange(nl, device=gt_masks.device).view(nl, 1, 1) + 1
            gt_masks = gt_masks.repeat(nl, 1, 1)  # shape(1,640,640) -> (n,640,640)
            gt_masks = torch.where(gt_masks == index, 1.0, 0.0)
        if gt_masks.shape[1:] != pred_masks.shape[1:]:
            gt_masks = F.interpolate(gt_masks[None], pred_masks.shape[1:], mode="bilinear", align_corners=False)[0]
            gt_masks = gt_masks.gt_(0.5)
        iou = mask_iou(gt_masks.view(gt_masks.shape[0], -1), pred_masks.view(pred_masks.shape[0], -1))
    else:  # boxes
        iou = box_iou(gt_bboxes, detections[:, :4])

    return self.match_predictions(detections[:, 5], gt_cls, iou)

eval_json

eval_json(stats)

Return COCO-style object detection evaluation metrics.

Source code in ultralytics/models/yolo/segment/val.py
def eval_json(self, stats):
    """Return COCO-style object detection evaluation metrics."""
    if self.args.save_json and self.is_coco and len(self.jdict):
        anno_json = self.data["path"] / "annotations/instances_val2017.json"  # annotations
        pred_json = self.save_dir / "predictions.json"  # predictions
        LOGGER.info(f"\nEvaluating pycocotools mAP using {pred_json} and {anno_json}...")
        try:  # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb
            check_requirements("pycocotools>=2.0.6")
            from pycocotools.coco import COCO  # noqa
            from pycocotools.cocoeval import COCOeval  # noqa

            for x in anno_json, pred_json:
                assert x.is_file(), f"{x} file not found"
            anno = COCO(str(anno_json))  # init annotations api
            pred = anno.loadRes(str(pred_json))  # init predictions api (must pass string, not Path)
            for i, eval in enumerate([COCOeval(anno, pred, "bbox"), COCOeval(anno, pred, "segm")]):
                if self.is_coco:
                    eval.params.imgIds = [int(Path(x).stem) for x in self.dataloader.dataset.im_files]  # im to eval
                eval.evaluate()
                eval.accumulate()
                eval.summarize()
                idx = i * 4 + 2
                stats[self.metrics.keys[idx + 1]], stats[self.metrics.keys[idx]] = eval.stats[
                    :2
                ]  # update mAP50-95 and mAP50
        except Exception as e:
            LOGGER.warning(f"pycocotools unable to run: {e}")
    return stats

finalize_metrics

finalize_metrics(*args, **kwargs)

Sets speed and confusion matrix for evaluation metrics.

Source code in ultralytics/models/yolo/segment/val.py
def finalize_metrics(self, *args, **kwargs):
    """Sets speed and confusion matrix for evaluation metrics."""
    self.metrics.speed = self.speed
    self.metrics.confusion_matrix = self.confusion_matrix

get_desc

get_desc()

Return a formatted description of evaluation metrics.

Source code in ultralytics/models/yolo/segment/val.py
def get_desc(self):
    """Return a formatted description of evaluation metrics."""
    return ("%22s" + "%11s" * 10) % (
        "Class",
        "Images",
        "Instances",
        "Box(P",
        "R",
        "mAP50",
        "mAP50-95)",
        "Mask(P",
        "R",
        "mAP50",
        "mAP50-95)",
    )

init_metrics

init_metrics(model)

Initialize metrics and select mask processing function based on save_json flag.

Source code in ultralytics/models/yolo/segment/val.py
def init_metrics(self, model):
    """Initialize metrics and select mask processing function based on save_json flag."""
    super().init_metrics(model)
    self.plot_masks = []
    if self.args.save_json:
        check_requirements("pycocotools>=2.0.6")
    # more accurate vs faster
    self.process = ops.process_mask_native if self.args.save_json or self.args.save_txt else ops.process_mask
    self.stats = dict(tp_m=[], tp=[], conf=[], pred_cls=[], target_cls=[], target_img=[])

plot_predictions

plot_predictions(batch, preds, ni)

Plots batch predictions with masks and bounding boxes.

Source code in ultralytics/models/yolo/segment/val.py
def plot_predictions(self, batch, preds, ni):
    """Plots batch predictions with masks and bounding boxes."""
    plot_images(
        batch["img"],
        *output_to_target(preds[0], max_det=15),  # not set to self.args.max_det due to slow plotting speed
        torch.cat(self.plot_masks, dim=0) if len(self.plot_masks) else self.plot_masks,
        paths=batch["im_file"],
        fname=self.save_dir / f"val_batch{ni}_pred.jpg",
        names=self.names,
        on_plot=self.on_plot,
    )  # pred
    self.plot_masks.clear()

plot_val_samples

plot_val_samples(batch, ni)

Plots validation samples with bounding box labels.

Source code in ultralytics/models/yolo/segment/val.py
def plot_val_samples(self, batch, ni):
    """Plots validation samples with bounding box labels."""
    plot_images(
        batch["img"],
        batch["batch_idx"],
        batch["cls"].squeeze(-1),
        batch["bboxes"],
        masks=batch["masks"],
        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)

Post-processes YOLO predictions and returns output detections with proto.

Source code in ultralytics/models/yolo/segment/val.py
def postprocess(self, preds):
    """Post-processes YOLO predictions and returns output detections with proto."""
    p = super().postprocess(preds[0])
    proto = preds[1][-1] if len(preds[1]) == 3 else preds[1]  # second output is len 3 if pt, but only 1 if exported
    return p, proto

pred_to_json

pred_to_json(predn, filename, pred_masks)

Save one JSON result.

Examples:

>>> result = {"image_id": 42, "category_id": 18, "bbox": [258.15, 41.29, 348.26, 243.78], "score": 0.236}
Source code in ultralytics/models/yolo/segment/val.py
def pred_to_json(self, predn, filename, pred_masks):
    """
    Save one JSON result.

    Examples:
         >>> result = {"image_id": 42, "category_id": 18, "bbox": [258.15, 41.29, 348.26, 243.78], "score": 0.236}
    """
    from pycocotools.mask import encode  # noqa

    def single_encode(x):
        """Encode predicted masks as RLE and append results to jdict."""
        rle = encode(np.asarray(x[:, :, None], order="F", dtype="uint8"))[0]
        rle["counts"] = rle["counts"].decode("utf-8")
        return rle

    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
    pred_masks = np.transpose(pred_masks, (2, 0, 1))
    with ThreadPool(NUM_THREADS) as pool:
        rles = pool.map(single_encode, pred_masks)
    for i, (p, b) in enumerate(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),
                "segmentation": rles[i],
            }
        )

preprocess

preprocess(batch)

Preprocesses batch by converting masks to float and sending to device.

Source code in ultralytics/models/yolo/segment/val.py
def preprocess(self, batch):
    """Preprocesses batch by converting masks to float and sending to device."""
    batch = super().preprocess(batch)
    batch["masks"] = batch["masks"].to(self.device).float()
    return batch

save_one_txt

save_one_txt(predn, pred_masks, save_conf, shape, file)

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

Source code in ultralytics/models/yolo/segment/val.py
def save_one_txt(self, predn, pred_masks, save_conf, shape, file):
    """Save YOLO detections to a txt file in normalized coordinates in a specific format."""
    from ultralytics.engine.results import Results

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

update_metrics

update_metrics(preds, batch)

Metrics.

Source code in ultralytics/models/yolo/segment/val.py
def update_metrics(self, preds, batch):
    """Metrics."""
    for si, (pred, proto) in enumerate(zip(preds[0], preds[1])):
        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),
            tp_m=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

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

        # Evaluate
        if nl:
            stat["tp"] = self._process_batch(predn, bbox, cls)
            stat["tp_m"] = self._process_batch(
                predn, bbox, cls, pred_masks, gt_masks, self.args.overlap_mask, masks=True
            )
        if self.args.plots:
            self.confusion_matrix.process_batch(predn, bbox, cls)

        for k in self.stats.keys():
            self.stats[k].append(stat[k])

        pred_masks = torch.as_tensor(pred_masks, dtype=torch.uint8)
        if self.args.plots and self.batch_i < 3:
            self.plot_masks.append(pred_masks[:15].cpu())  # filter top 15 to plot

        # Save
        if self.args.save_json:
            self.pred_to_json(
                predn,
                batch["im_file"][si],
                ops.scale_image(
                    pred_masks.permute(1, 2, 0).contiguous().cpu().numpy(),
                    pbatch["ori_shape"],
                    ratio_pad=batch["ratio_pad"][si],
                ),
            )
        if self.args.save_txt:
            self.save_one_txt(
                predn,
                pred_masks,
                self.args.save_conf,
                pbatch["ori_shape"],
                self.save_dir / "labels" / f"{Path(batch['im_file'][si]).stem}.txt",
            )



📅 Created 1 year ago ✏️ Updated 5 months ago