Skip to content

Results & Boxes

DFINE.predict returns a list of Results (one per image); each holds the detected Boxes (original-scale xyxy), with .plot()/.save() and interop converters (to_pandas/to_coco/to_supervision).

dfine.results.Results

Results(orig_img: Image, boxes: Boxes, names: dict[int, str], masks: Masks | None = None)

Detections for one image + helpers to visualize them.

Source code in dfine/results.py
def __init__(
    self,
    orig_img: Image.Image,
    boxes: Boxes,
    names: dict[int, str],
    masks: Masks | None = None,
):
    self.orig_img = orig_img
    self.boxes = boxes
    self.masks = masks
    self.names = names
    self.orig_shape = (orig_img.height, orig_img.width)

plot

plot(line_width: int | None = None) -> np.ndarray

Draw boxes+labels on a copy of the image; return an RGB HWC uint8 array.

When the boxes carry track ids (boxes.id), each label is prefixed with #<id> and boxes are colored by track id so an object keeps its color. Instance masks (self.masks), when present, are overlaid semi-transparently in each detection's color before boxes/labels are drawn on top.

Source code in dfine/results.py
def plot(self, line_width: int | None = None) -> np.ndarray:
    """Draw boxes+labels on a copy of the image; return an RGB HWC uint8 array.

    When the boxes carry track ids (``boxes.id``), each label is prefixed with
    ``#<id>`` and boxes are colored by track id so an object keeps its color.
    Instance masks (``self.masks``), when present, are overlaid semi-transparently
    in each detection's color before boxes/labels are drawn on top.
    """
    img = self.orig_img.convert("RGB").copy()
    ids = self.boxes.id

    def _color(i: int, cls_id: int) -> tuple[int, int, int]:
        track_id = int(ids[i]) if ids is not None else None
        return _PALETTE[(track_id if track_id is not None else cls_id) % len(_PALETTE)]

    # Masks first (under the boxes), alpha-blended into each detection's color.
    if self.masks is not None and len(self.masks):
        arr = np.asarray(img).astype(np.float32)
        alpha = 0.5
        for i, m in enumerate(self.masks):
            mask = np.asarray(m).astype(bool)
            color = np.array(_color(i, int(self.boxes.cls[i])), dtype=np.float32)
            arr[mask] = arr[mask] * (1 - alpha) + color * alpha
        img = Image.fromarray(arr.clip(0, 255).astype(np.uint8))

    draw = ImageDraw.Draw(img)
    lw = line_width or max(2, round(sum(self.orig_shape) / 600))

    for i, (xyxy, conf, cls) in enumerate(self.boxes):
        cls_id = int(cls)
        track_id = int(ids[i]) if ids is not None else None
        color = _color(i, cls_id)
        box = [float(v) for v in xyxy]
        draw.rectangle(box, outline=color, width=lw)

        text = self._label(cls_id, float(conf), track_id)
        tl = draw.textbbox((box[0], box[1]), text)
        draw.rectangle([tl[0], tl[1], tl[2], tl[3]], fill=color)
        draw.text((box[0], box[1]), text, fill=(255, 255, 255))

    return np.asarray(img)

save

save(filename: str | Path) -> Path

Render via :meth:plot and write to filename; return the path.

Source code in dfine/results.py
def save(self, filename: str | Path) -> Path:
    """Render via :meth:`plot` and write to ``filename``; return the path."""
    path = Path(filename)
    Image.fromarray(self.plot()).save(path)
    return path

to_pandas

to_pandas()

Return detections as a pandas.DataFrame (one row per box).

Columns xmin, ymin, xmax, ymax, confidence, class, name — the ultralytics .pandas().xyxy[0] layout. An empty Results yields an empty frame that still carries those columns. Requires pandas.

Source code in dfine/results.py
def to_pandas(self):
    """Return detections as a ``pandas.DataFrame`` (one row per box).

    Columns ``xmin, ymin, xmax, ymax, confidence, class, name`` — the
    ultralytics ``.pandas().xyxy[0]`` layout. An empty ``Results`` yields an
    empty frame that still carries those columns. Requires ``pandas``.
    """
    try:
        import pandas as pd
    except ImportError as e:  # pragma: no cover - trivial guard
        raise ImportError(
            "Results.to_pandas() needs pandas — install it with `pip install pandas` "
            "or `pip install pydfine[interop]`."
        ) from e

    columns = ["xmin", "ymin", "xmax", "ymax", "confidence", "class", "name"]
    rows = []
    for xyxy, conf, cls in self.boxes:
        cls_id = int(cls)
        x1, y1, x2, y2 = (float(v) for v in xyxy)
        rows.append(
            {
                "xmin": x1,
                "ymin": y1,
                "xmax": x2,
                "ymax": y2,
                "confidence": float(conf),
                "class": cls_id,
                "name": self.names.get(cls_id, str(cls_id)) if self.names else str(cls_id),
            }
        )
    return pd.DataFrame(rows, columns=columns)

to_coco

to_coco(image_id: int = 0) -> list[dict]

Detections as COCO-format result dicts (the loadRes layout).

Each box becomes {"image_id", "category_id", "bbox": [x, y, w, h], "score"} with the bbox in COCO xywh (top-left + size, original-image pixels). category_id is the contiguous class id this library predicts; pass image_id to tag the detections with a dataset image id. Pure Python — no extra dependency.

Source code in dfine/results.py
def to_coco(self, image_id: int = 0) -> list[dict]:
    """Detections as COCO-format result dicts (the ``loadRes`` layout).

    Each box becomes ``{"image_id", "category_id", "bbox": [x, y, w, h],
    "score"}`` with the bbox in COCO ``xywh`` (top-left + size, original-image
    pixels). ``category_id`` is the contiguous class id this library predicts;
    pass ``image_id`` to tag the detections with a dataset image id. Pure
    Python — no extra dependency.
    """
    out = []
    for xyxy, conf, cls in self.boxes:
        x1, y1, x2, y2 = (float(v) for v in xyxy)
        out.append(
            {
                "image_id": image_id,
                "category_id": int(cls),
                "bbox": [x1, y1, x2 - x1, y2 - y1],
                "score": float(conf),
            }
        )
    return out

to_supervision

to_supervision()

Convert to a supervision.Detections (xyxy/confidence/class_id).

Boxes are the original-scale xyxy corners (float32); class ids are the contiguous labels. Instance masks (when present) are attached as a bool [N, H, W] mask array. Requires the supervision package.

Source code in dfine/results.py
def to_supervision(self):
    """Convert to a ``supervision.Detections`` (``xyxy``/``confidence``/``class_id``).

    Boxes are the original-scale ``xyxy`` corners (float32); class ids are the
    contiguous labels. Instance masks (when present) are attached as a bool
    ``[N, H, W]`` ``mask`` array. Requires the ``supervision`` package.
    """
    try:
        import supervision as sv
    except ImportError as e:  # pragma: no cover - trivial guard
        raise ImportError(
            "Results.to_supervision() needs supervision — install it with "
            "`pip install supervision` or `pip install pydfine[interop]`."
        ) from e

    xyxy = self.boxes.xyxy.cpu().numpy().reshape(-1, 4).astype(np.float32)
    conf = self.boxes.conf.cpu().numpy().reshape(-1).astype(np.float32)
    cls = self.boxes.cls.cpu().numpy().reshape(-1).astype(int)
    mask = None
    if self.masks is not None and len(self.masks):
        mask = self.masks.data.cpu().numpy().astype(bool)
    return sv.Detections(xyxy=xyxy, confidence=conf, class_id=cls, mask=mask)

dfine.results.Boxes

Boxes(xyxy: Tensor, conf: Tensor, cls: Tensor, id: Tensor | None = None)

Detected boxes for one image: xyxy (pixels), conf, cls.

id holds per-box track ids when the boxes came from a tracker (e.g. :meth:DFINE.predict_video with track=True); it is None otherwise.

Source code in dfine/results.py
def __init__(
    self,
    xyxy: torch.Tensor,
    conf: torch.Tensor,
    cls: torch.Tensor,
    id: torch.Tensor | None = None,
):
    self.xyxy = xyxy
    self.conf = conf
    self.cls = cls
    self.id = id