Saltar al contenido

Referencia para ultralytics/utils/ops.py

Nota

Este archivo est√° disponible en https://github.com/ultralytics/ ultralytics/blob/main/ ultralytics/utils/ops .py. Si detectas alg√ļn problema, por favor, ayuda a solucionarlo contribuyendo con una Pull Request ūüõ†ÔłŹ. ¬°Gracias ūüôŹ!



ultralytics.utils.ops.Profile

Bases: ContextDecorator

YOLOv8 Clase Perfil. Utilízala como decorador con @Perfil() o como gestor de contexto con 'con Perfil():'.

Ejemplo
from ultralytics.utils.ops import Profile

with Profile(device=device) as dt:
    pass  # slow operation here

print(dt)  # prints "Elapsed time is 9.5367431640625e-07 s"
Código fuente en ultralytics/utils/ops.py
class Profile(contextlib.ContextDecorator):
    """
    YOLOv8 Profile class. Use as a decorator with @Profile() or as a context manager with 'with Profile():'.

    Example:
        ```python
        from ultralytics.utils.ops import Profile

        with Profile(device=device) as dt:
            pass  # slow operation here

        print(dt)  # prints "Elapsed time is 9.5367431640625e-07 s"
        ```
    """

    def __init__(self, t=0.0, device: torch.device = None):
        """
        Initialize the Profile class.

        Args:
            t (float): Initial time. Defaults to 0.0.
            device (torch.device): Devices used for model inference. Defaults to None (cpu).
        """
        self.t = t
        self.device = device
        self.cuda = bool(device and str(device).startswith("cuda"))

    def __enter__(self):
        """Start timing."""
        self.start = self.time()
        return self

    def __exit__(self, type, value, traceback):  # noqa
        """Stop timing."""
        self.dt = self.time() - self.start  # delta-time
        self.t += self.dt  # accumulate dt

    def __str__(self):
        """Returns a human-readable string representing the accumulated elapsed time in the profiler."""
        return f"Elapsed time is {self.t} s"

    def time(self):
        """Get current time."""
        if self.cuda:
            torch.cuda.synchronize(self.device)
        return time.time()

__enter__()

Hora de inicio.

Código fuente en ultralytics/utils/ops.py
def __enter__(self):
    """Start timing."""
    self.start = self.time()
    return self

__exit__(type, value, traceback)

Detener el tiempo.

Código fuente en ultralytics/utils/ops.py
def __exit__(self, type, value, traceback):  # noqa
    """Stop timing."""
    self.dt = self.time() - self.start  # delta-time
    self.t += self.dt  # accumulate dt

__init__(t=0.0, device=None)

Inicializa la clase Perfil.

Par√°metros:

Nombre Tipo Descripción Por defecto
t float

Tiempo inicial. Por defecto 0.0.

0.0
device device

Dispositivos utilizados para la inferencia del modelo. Por defecto Ninguno (cpu).

None
Código fuente en ultralytics/utils/ops.py
def __init__(self, t=0.0, device: torch.device = None):
    """
    Initialize the Profile class.

    Args:
        t (float): Initial time. Defaults to 0.0.
        device (torch.device): Devices used for model inference. Defaults to None (cpu).
    """
    self.t = t
    self.device = device
    self.cuda = bool(device and str(device).startswith("cuda"))

__str__()

Devuelve una cadena legible por humanos que representa el tiempo transcurrido acumulado en el perfilador.

Código fuente en ultralytics/utils/ops.py
def __str__(self):
    """Returns a human-readable string representing the accumulated elapsed time in the profiler."""
    return f"Elapsed time is {self.t} s"

time()

Obtener la hora actual.

Código fuente en ultralytics/utils/ops.py
def time(self):
    """Get current time."""
    if self.cuda:
        torch.cuda.synchronize(self.device)
    return time.time()



ultralytics.utils.ops.segment2box(segment, width=640, height=640)

Convierte 1 etiqueta de segmento en 1 etiqueta de caja, aplicando la restricción de imagen interior, es decir, (xy1, xy2, ...) en (xyxy).

Par√°metros:

Nombre Tipo Descripción Por defecto
segment Tensor

la etiqueta del segmento

necesario
width int

La anchura de la imagen. Por defecto es 640

640
height int

La altura de la imagen. Por defecto 640

640

Devuelve:

Tipo Descripción
ndarray

los valores x e y mínimos y máximos del segmento.

Código fuente en ultralytics/utils/ops.py
def segment2box(segment, width=640, height=640):
    """
    Convert 1 segment label to 1 box label, applying inside-image constraint, i.e. (xy1, xy2, ...) to (xyxy).

    Args:
        segment (torch.Tensor): the segment label
        width (int): the width of the image. Defaults to 640
        height (int): The height of the image. Defaults to 640

    Returns:
        (np.ndarray): the minimum and maximum x and y values of the segment.
    """
    x, y = segment.T  # segment xy
    inside = (x >= 0) & (y >= 0) & (x <= width) & (y <= height)
    x = x[inside]
    y = y[inside]
    return (
        np.array([x.min(), y.min(), x.max(), y.max()], dtype=segment.dtype)
        if any(x)
        else np.zeros(4, dtype=segment.dtype)
    )  # xyxy



ultralytics.utils.ops.scale_boxes(img1_shape, boxes, img0_shape, ratio_pad=None, padding=True, xywh=False)

Reescala los cuadros delimitadores (en el formato xyxy por defecto) de la forma de la imagen en la que se especificaron originalmente (forma_img1) a la forma de una imagen diferente (forma_img0). originalmente (forma_img1) a la forma de otra imagen (forma_img0).

Par√°metros:

Nombre Tipo Descripción Por defecto
img1_shape tuple

La forma de la imagen para la que son las cajas delimitadoras, en el formato (alto, ancho).

necesario
boxes Tensor

las cajas delimitadoras de los objetos de la imagen, en el formato (x1, y1, x2, y2)

necesario
img0_shape tuple

La forma de la imagen de destino, en el formato (alto, ancho).

necesario
ratio_pad tuple

una tupla de (ratio, pad) para escalar las cajas. Si no se proporcionan, el ratio y el pad se calcular√°n calcular√°n en funci√≥n de la diferencia de tama√Īo entre las dos im√°genes.

None
padding bool

Si es True, asume que las cajas se basan en la imagen aumentada por el estilo yolo . Si es Falso, entonces haz un cambio de escala.

True
xywh bool

El formato de la caja es xywh o no, por defecto=False.

False

Devuelve:

Nombre Tipo Descripción
boxes Tensor

Las cajas delimitadoras escaladas, en formato (x1, y1, x2, y2)

Código fuente en ultralytics/utils/ops.py
def scale_boxes(img1_shape, boxes, img0_shape, ratio_pad=None, padding=True, xywh=False):
    """
    Rescales bounding boxes (in the format of xyxy by default) from the shape of the image they were originally
    specified in (img1_shape) to the shape of a different image (img0_shape).

    Args:
        img1_shape (tuple): The shape of the image that the bounding boxes are for, in the format of (height, width).
        boxes (torch.Tensor): the bounding boxes of the objects in the image, in the format of (x1, y1, x2, y2)
        img0_shape (tuple): the shape of the target image, in the format of (height, width).
        ratio_pad (tuple): a tuple of (ratio, pad) for scaling the boxes. If not provided, the ratio and pad will be
            calculated based on the size difference between the two images.
        padding (bool): If True, assuming the boxes is based on image augmented by yolo style. If False then do regular
            rescaling.
        xywh (bool): The box format is xywh or not, default=False.

    Returns:
        boxes (torch.Tensor): The scaled bounding boxes, in the format of (x1, y1, x2, y2)
    """
    if ratio_pad is None:  # calculate from img0_shape
        gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1])  # gain  = old / new
        pad = (
            round((img1_shape[1] - img0_shape[1] * gain) / 2 - 0.1),
            round((img1_shape[0] - img0_shape[0] * gain) / 2 - 0.1),
        )  # wh padding
    else:
        gain = ratio_pad[0][0]
        pad = ratio_pad[1]

    if padding:
        boxes[..., 0] -= pad[0]  # x padding
        boxes[..., 1] -= pad[1]  # y padding
        if not xywh:
            boxes[..., 2] -= pad[0]  # x padding
            boxes[..., 3] -= pad[1]  # y padding
    boxes[..., :4] /= gain
    return clip_boxes(boxes, img0_shape)



ultralytics.utils.ops.make_divisible(x, divisor)

Devuelve el n√ļmero m√°s pr√≥ximo divisible por el divisor dado.

Par√°metros:

Nombre Tipo Descripción Por defecto
x int

El n√ļmero que hay que hacer divisible.

necesario
divisor int | Tensor

El divisor.

necesario

Devuelve:

Tipo Descripción
int

El n√ļmero m√°s pr√≥ximo divisible por el divisor.

Código fuente en ultralytics/utils/ops.py
def make_divisible(x, divisor):
    """
    Returns the nearest number that is divisible by the given divisor.

    Args:
        x (int): The number to make divisible.
        divisor (int | torch.Tensor): The divisor.

    Returns:
        (int): The nearest number divisible by the divisor.
    """
    if isinstance(divisor, torch.Tensor):
        divisor = int(divisor.max())  # to int
    return math.ceil(x / divisor) * divisor



ultralytics.utils.ops.nms_rotated(boxes, scores, threshold=0.45)

NMS para obbs, impulsado por probiou y fast-nms.

Par√°metros:

Nombre Tipo Descripción Por defecto
boxes Tensor

(N, 5), xywhr.

necesario
scores Tensor

(N, ).

necesario
threshold float

Umbral IoU.

0.45

Devuelve:

Código fuente en ultralytics/utils/ops.py
def nms_rotated(boxes, scores, threshold=0.45):
    """
    NMS for obbs, powered by probiou and fast-nms.

    Args:
        boxes (torch.Tensor): (N, 5), xywhr.
        scores (torch.Tensor): (N, ).
        threshold (float): IoU threshold.

    Returns:
    """
    if len(boxes) == 0:
        return np.empty((0,), dtype=np.int8)
    sorted_idx = torch.argsort(scores, descending=True)
    boxes = boxes[sorted_idx]
    ious = batch_probiou(boxes, boxes).triu_(diagonal=1)
    pick = torch.nonzero(ious.max(dim=0)[0] < threshold).squeeze_(-1)
    return sorted_idx[pick]



ultralytics.utils.ops.non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False, labels=(), max_det=300, nc=0, max_time_img=0.05, max_nms=30000, max_wh=7680, in_place=True, rotated=False)

Realiza una supresi√≥n no m√°xima (NMS) en un conjunto de cajas, con soporte para m√°scaras y m√ļltiples etiquetas por caja.

Par√°metros:

Nombre Tipo Descripción Por defecto
prediction Tensor

Un tensor de forma (tama√Īo_lote, n¬ļ_clases + 4 + n¬ļ_m√°scaras, n¬ļ_cajas) que contenga las cajas, clases y m√°scaras predichas. El tensor debe tener el formato producido por un modelo, como YOLO.

necesario
conf_thres float

El umbral de confianza por debajo del cual se filtrar√°n las casillas. Los valores v√°lidos est√°n entre 0,0 y 1,0.

0.25
iou_thres float

El umbral de IoU por debajo del cual se filtrar√°n las cajas durante el NMS. Los valores v√°lidos est√°n entre 0,0 y 1,0.

0.45
classes List[int]

Una lista de índices de clases a considerar. Si es Ninguno, se considerarán todas las clases.

None
agnostic bool

Si es Verdadero, el modelo es agn√≥stico al n√ļmero de clases, y todas las clases se considerar√°n como una sola.

False
multi_label bool

Si es Verdadero, cada casilla puede tener varias etiquetas.

False
labels List[List[Union[int, float, Tensor]]]

Una lista de listas, donde cada lista contiene las etiquetas apriori de una imagen determinada. La lista debe tener el formato producido por un cargador de datos, siendo cada etiqueta una tupla de (índice_clase, x1, y1, x2, y2).

()
max_det int

El n√ļmero m√°ximo de cajas a conservar despu√©s de NMS.

300
nc int

El n√ļmero de clases producidas por el modelo. Cualquier √≠ndice posterior a √©ste se considerar√° m√°scara.

0
max_time_img float

El tiempo m√°ximo (segundos) para procesar una imagen.

0.05
max_nms int

El n√ļmero m√°ximo de cajas en torchvision.ops.nms().

30000
max_wh int

La anchura y altura máximas de la caja en píxeles.

7680
in_place bool

Si es Verdadero, la predicción de entrada tensor se modificará en su lugar.

True

Devuelve:

Tipo Descripción
List[Tensor]

Una lista de longitud tama√Īo_lote, donde cada elemento es un tensor de forma (num_cajas, 6 + num_m√°scaras) que contiene las cajas conservadas, con columnas (x1, y1, x2, y2, confianza, clase, m√°scara1, m√°scara2, ...).

Código fuente en ultralytics/utils/ops.py
def non_max_suppression(
    prediction,
    conf_thres=0.25,
    iou_thres=0.45,
    classes=None,
    agnostic=False,
    multi_label=False,
    labels=(),
    max_det=300,
    nc=0,  # number of classes (optional)
    max_time_img=0.05,
    max_nms=30000,
    max_wh=7680,
    in_place=True,
    rotated=False,
):
    """
    Perform non-maximum suppression (NMS) on a set of boxes, with support for masks and multiple labels per box.

    Args:
        prediction (torch.Tensor): A tensor of shape (batch_size, num_classes + 4 + num_masks, num_boxes)
            containing the predicted boxes, classes, and masks. The tensor should be in the format
            output by a model, such as YOLO.
        conf_thres (float): The confidence threshold below which boxes will be filtered out.
            Valid values are between 0.0 and 1.0.
        iou_thres (float): The IoU threshold below which boxes will be filtered out during NMS.
            Valid values are between 0.0 and 1.0.
        classes (List[int]): A list of class indices to consider. If None, all classes will be considered.
        agnostic (bool): If True, the model is agnostic to the number of classes, and all
            classes will be considered as one.
        multi_label (bool): If True, each box may have multiple labels.
        labels (List[List[Union[int, float, torch.Tensor]]]): A list of lists, where each inner
            list contains the apriori labels for a given image. The list should be in the format
            output by a dataloader, with each label being a tuple of (class_index, x1, y1, x2, y2).
        max_det (int): The maximum number of boxes to keep after NMS.
        nc (int, optional): The number of classes output by the model. Any indices after this will be considered masks.
        max_time_img (float): The maximum time (seconds) for processing one image.
        max_nms (int): The maximum number of boxes into torchvision.ops.nms().
        max_wh (int): The maximum box width and height in pixels.
        in_place (bool): If True, the input prediction tensor will be modified in place.

    Returns:
        (List[torch.Tensor]): A list of length batch_size, where each element is a tensor of
            shape (num_boxes, 6 + num_masks) containing the kept boxes, with columns
            (x1, y1, x2, y2, confidence, class, mask1, mask2, ...).
    """
    import torchvision  # scope for faster 'import ultralytics'

    # Checks
    assert 0 <= conf_thres <= 1, f"Invalid Confidence threshold {conf_thres}, valid values are between 0.0 and 1.0"
    assert 0 <= iou_thres <= 1, f"Invalid IoU {iou_thres}, valid values are between 0.0 and 1.0"
    if isinstance(prediction, (list, tuple)):  # YOLOv8 model in validation model, output = (inference_out, loss_out)
        prediction = prediction[0]  # select only inference output

    bs = prediction.shape[0]  # batch size
    nc = nc or (prediction.shape[1] - 4)  # number of classes
    nm = prediction.shape[1] - nc - 4
    mi = 4 + nc  # mask start index
    xc = prediction[:, 4:mi].amax(1) > conf_thres  # candidates

    # Settings
    # min_wh = 2  # (pixels) minimum box width and height
    time_limit = 2.0 + max_time_img * bs  # seconds to quit after
    multi_label &= nc > 1  # multiple labels per box (adds 0.5ms/img)

    prediction = prediction.transpose(-1, -2)  # shape(1,84,6300) to shape(1,6300,84)
    if not rotated:
        if in_place:
            prediction[..., :4] = xywh2xyxy(prediction[..., :4])  # xywh to xyxy
        else:
            prediction = torch.cat((xywh2xyxy(prediction[..., :4]), prediction[..., 4:]), dim=-1)  # xywh to xyxy

    t = time.time()
    output = [torch.zeros((0, 6 + nm), device=prediction.device)] * bs
    for xi, x in enumerate(prediction):  # image index, image inference
        # Apply constraints
        # x[((x[:, 2:4] < min_wh) | (x[:, 2:4] > max_wh)).any(1), 4] = 0  # width-height
        x = x[xc[xi]]  # confidence

        # Cat apriori labels if autolabelling
        if labels and len(labels[xi]) and not rotated:
            lb = labels[xi]
            v = torch.zeros((len(lb), nc + nm + 4), device=x.device)
            v[:, :4] = xywh2xyxy(lb[:, 1:5])  # box
            v[range(len(lb)), lb[:, 0].long() + 4] = 1.0  # cls
            x = torch.cat((x, v), 0)

        # If none remain process next image
        if not x.shape[0]:
            continue

        # Detections matrix nx6 (xyxy, conf, cls)
        box, cls, mask = x.split((4, nc, nm), 1)

        if multi_label:
            i, j = torch.where(cls > conf_thres)
            x = torch.cat((box[i], x[i, 4 + j, None], j[:, None].float(), mask[i]), 1)
        else:  # best class only
            conf, j = cls.max(1, keepdim=True)
            x = torch.cat((box, conf, j.float(), mask), 1)[conf.view(-1) > conf_thres]

        # Filter by class
        if classes is not None:
            x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)]

        # Check shape
        n = x.shape[0]  # number of boxes
        if not n:  # no boxes
            continue
        if n > max_nms:  # excess boxes
            x = x[x[:, 4].argsort(descending=True)[:max_nms]]  # sort by confidence and remove excess boxes

        # Batched NMS
        c = x[:, 5:6] * (0 if agnostic else max_wh)  # classes
        scores = x[:, 4]  # scores
        if rotated:
            boxes = torch.cat((x[:, :2] + c, x[:, 2:4], x[:, -1:]), dim=-1)  # xywhr
            i = nms_rotated(boxes, scores, iou_thres)
        else:
            boxes = x[:, :4] + c  # boxes (offset by class)
            i = torchvision.ops.nms(boxes, scores, iou_thres)  # NMS
        i = i[:max_det]  # limit detections

        # # Experimental
        # merge = False  # use merge-NMS
        # if merge and (1 < n < 3E3):  # Merge NMS (boxes merged using weighted mean)
        #     # Update boxes as boxes(i,4) = weights(i,n) * boxes(n,4)
        #     from .metrics import box_iou
        #     iou = box_iou(boxes[i], boxes) > iou_thres  # IoU matrix
        #     weights = iou * scores[None]  # box weights
        #     x[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum(1, keepdim=True)  # merged boxes
        #     redundant = True  # require redundant detections
        #     if redundant:
        #         i = i[iou.sum(1) > 1]  # require redundancy

        output[xi] = x[i]
        if (time.time() - t) > time_limit:
            LOGGER.warning(f"WARNING ‚ö†ÔłŹ NMS time limit {time_limit:.3f}s exceeded")
            break  # time limit exceeded

    return output



ultralytics.utils.ops.clip_boxes(boxes, shape)

Toma una lista de cuadros delimitadores y una forma (alto, ancho) y recorta los cuadros delimitadores a la forma.

Par√°metros:

Nombre Tipo Descripción Por defecto
boxes Tensor

las cajas delimitadoras a recortar

necesario
shape tuple

la forma de la imagen

necesario

Devuelve:

Tipo Descripción
Tensor | ndarray

Cajas recortadas

Código fuente en ultralytics/utils/ops.py
def clip_boxes(boxes, shape):
    """
    Takes a list of bounding boxes and a shape (height, width) and clips the bounding boxes to the shape.

    Args:
        boxes (torch.Tensor): the bounding boxes to clip
        shape (tuple): the shape of the image

    Returns:
        (torch.Tensor | numpy.ndarray): Clipped boxes
    """
    if isinstance(boxes, torch.Tensor):  # faster individually (WARNING: inplace .clamp_() Apple MPS bug)
        boxes[..., 0] = boxes[..., 0].clamp(0, shape[1])  # x1
        boxes[..., 1] = boxes[..., 1].clamp(0, shape[0])  # y1
        boxes[..., 2] = boxes[..., 2].clamp(0, shape[1])  # x2
        boxes[..., 3] = boxes[..., 3].clamp(0, shape[0])  # y2
    else:  # np.array (faster grouped)
        boxes[..., [0, 2]] = boxes[..., [0, 2]].clip(0, shape[1])  # x1, x2
        boxes[..., [1, 3]] = boxes[..., [1, 3]].clip(0, shape[0])  # y1, y2
    return boxes



ultralytics.utils.ops.clip_coords(coords, shape)

Recorta las coordenadas de las líneas a los límites de la imagen.

Par√°metros:

Nombre Tipo Descripción Por defecto
coords Tensor | ndarray

Una lista de coordenadas de línea.

necesario
shape tuple

Una tupla de enteros que representa el tama√Īo de la imagen en el formato (alto, ancho).

necesario

Devuelve:

Tipo Descripción
Tensor | ndarray

Coordenadas recortadas

Código fuente en ultralytics/utils/ops.py
def clip_coords(coords, shape):
    """
    Clip line coordinates to the image boundaries.

    Args:
        coords (torch.Tensor | numpy.ndarray): A list of line coordinates.
        shape (tuple): A tuple of integers representing the size of the image in the format (height, width).

    Returns:
        (torch.Tensor | numpy.ndarray): Clipped coordinates
    """
    if isinstance(coords, torch.Tensor):  # faster individually (WARNING: inplace .clamp_() Apple MPS bug)
        coords[..., 0] = coords[..., 0].clamp(0, shape[1])  # x
        coords[..., 1] = coords[..., 1].clamp(0, shape[0])  # y
    else:  # np.array (faster grouped)
        coords[..., 0] = coords[..., 0].clip(0, shape[1])  # x
        coords[..., 1] = coords[..., 1].clip(0, shape[0])  # y
    return coords



ultralytics.utils.ops.scale_image(masks, im0_shape, ratio_pad=None)

Toma una m√°scara y redimensi√≥nala al tama√Īo de la imagen original.

Par√°metros:

Nombre Tipo Descripción Por defecto
masks ndarray

m√°scaras/im√°genes redimensionadas y acolchadas, [h, w, num]/[h, w, 3].

necesario
im0_shape tuple

la forma original de la imagen

necesario
ratio_pad tuple

la relación entre el relleno y la imagen original.

None

Devuelve:

Nombre Tipo Descripción
masks Tensor

Las m√°scaras que se devuelven.

Código fuente en ultralytics/utils/ops.py
def scale_image(masks, im0_shape, ratio_pad=None):
    """
    Takes a mask, and resizes it to the original image size.

    Args:
        masks (np.ndarray): resized and padded masks/images, [h, w, num]/[h, w, 3].
        im0_shape (tuple): the original image shape
        ratio_pad (tuple): the ratio of the padding to the original image.

    Returns:
        masks (torch.Tensor): The masks that are being returned.
    """
    # Rescale coordinates (xyxy) from im1_shape to im0_shape
    im1_shape = masks.shape
    if im1_shape[:2] == im0_shape[:2]:
        return masks
    if ratio_pad is None:  # calculate from im0_shape
        gain = min(im1_shape[0] / im0_shape[0], im1_shape[1] / im0_shape[1])  # gain  = old / new
        pad = (im1_shape[1] - im0_shape[1] * gain) / 2, (im1_shape[0] - im0_shape[0] * gain) / 2  # wh padding
    else:
        # gain = ratio_pad[0][0]
        pad = ratio_pad[1]
    top, left = int(pad[1]), int(pad[0])  # y, x
    bottom, right = int(im1_shape[0] - pad[1]), int(im1_shape[1] - pad[0])

    if len(masks.shape) < 2:
        raise ValueError(f'"len of masks shape" should be 2 or 3, but got {len(masks.shape)}')
    masks = masks[top:bottom, left:right]
    masks = cv2.resize(masks, (im0_shape[1], im0_shape[0]))
    if len(masks.shape) == 2:
        masks = masks[:, :, None]

    return masks



ultralytics.utils.ops.xyxy2xywh(x)

Convierte las coordenadas de la caja delimitadora del formato (x1, y1, x2, y2) al formato (x, y, anchura, altura), donde (x1, y1) es la esquina superior izquierda y (x2, y2) es la esquina inferior derecha. esquina superior izquierda y (x2, y2) es la esquina inferior derecha.

Par√°metros:

Nombre Tipo Descripción Por defecto
x ndarray | Tensor

Las coordenadas del cuadro delimitador de entrada en formato (x1, y1, x2, y2).

necesario

Devuelve:

Nombre Tipo Descripción
y ndarray | Tensor

Las coordenadas de la caja delimitadora en formato (x, y, anchura, altura).

Código fuente en ultralytics/utils/ops.py
def xyxy2xywh(x):
    """
    Convert bounding box coordinates from (x1, y1, x2, y2) format to (x, y, width, height) format where (x1, y1) is the
    top-left corner and (x2, y2) is the bottom-right corner.

    Args:
        x (np.ndarray | torch.Tensor): The input bounding box coordinates in (x1, y1, x2, y2) format.

    Returns:
        y (np.ndarray | torch.Tensor): The bounding box coordinates in (x, y, width, height) format.
    """
    assert x.shape[-1] == 4, f"input shape last dimension expected 4 but input shape is {x.shape}"
    y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x)  # faster than clone/copy
    y[..., 0] = (x[..., 0] + x[..., 2]) / 2  # x center
    y[..., 1] = (x[..., 1] + x[..., 3]) / 2  # y center
    y[..., 2] = x[..., 2] - x[..., 0]  # width
    y[..., 3] = x[..., 3] - x[..., 1]  # height
    return y



ultralytics.utils.ops.xywh2xyxy(x)

Convierte las coordenadas del cuadro delimitador del formato (x, y, anchura, altura) al formato (x1, y1, x2, y2), donde (x1, y1) es la esquina superior izquierda y (x2, y2) es la esquina inferior derecha. esquina superior izquierda y (x2, y2) es la esquina inferior derecha.

Par√°metros:

Nombre Tipo Descripción Por defecto
x ndarray | Tensor

Las coordenadas del cuadro delimitador de entrada en formato (x, y, anchura, altura).

necesario

Devuelve:

Nombre Tipo Descripción
y ndarray | Tensor

Las coordenadas de la caja delimitadora en formato (x1, y1, x2, y2).

Código fuente en ultralytics/utils/ops.py
def xywh2xyxy(x):
    """
    Convert bounding box coordinates from (x, y, width, height) format to (x1, y1, x2, y2) format where (x1, y1) is the
    top-left corner and (x2, y2) is the bottom-right corner.

    Args:
        x (np.ndarray | torch.Tensor): The input bounding box coordinates in (x, y, width, height) format.

    Returns:
        y (np.ndarray | torch.Tensor): The bounding box coordinates in (x1, y1, x2, y2) format.
    """
    assert x.shape[-1] == 4, f"input shape last dimension expected 4 but input shape is {x.shape}"
    y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x)  # faster than clone/copy
    dw = x[..., 2] / 2  # half-width
    dh = x[..., 3] / 2  # half-height
    y[..., 0] = x[..., 0] - dw  # top left x
    y[..., 1] = x[..., 1] - dh  # top left y
    y[..., 2] = x[..., 0] + dw  # bottom right x
    y[..., 3] = x[..., 1] + dh  # bottom right y
    return y



ultralytics.utils.ops.xywhn2xyxy(x, w=640, h=640, padw=0, padh=0)

Convierte las coordenadas normalizadas del cuadro delimitador en coordenadas de píxel.

Par√°metros:

Nombre Tipo Descripción Por defecto
x ndarray | Tensor

Las coordenadas del cuadro delimitador.

necesario
w int

Anchura de la imagen. Por defecto 640

640
h int

Altura de la imagen. Por defecto 640

640
padw int

Ancho del relleno. Por defecto 0

0
padh int

Altura del relleno. Por defecto a 0

0

Devuelve: y (np.ndarray | torch.Tensor): Las coordenadas de la caja delimitadora en el formato [x1, y1, x2, y2] donde x1,y1 es la esquina superior izquierda, x2,y2 es la esquina inferior derecha del cuadro delimitador.

Código fuente en ultralytics/utils/ops.py
def xywhn2xyxy(x, w=640, h=640, padw=0, padh=0):
    """
    Convert normalized bounding box coordinates to pixel coordinates.

    Args:
        x (np.ndarray | torch.Tensor): The bounding box coordinates.
        w (int): Width of the image. Defaults to 640
        h (int): Height of the image. Defaults to 640
        padw (int): Padding width. Defaults to 0
        padh (int): Padding height. Defaults to 0
    Returns:
        y (np.ndarray | torch.Tensor): The coordinates of the bounding box in the format [x1, y1, x2, y2] where
            x1,y1 is the top-left corner, x2,y2 is the bottom-right corner of the bounding box.
    """
    assert x.shape[-1] == 4, f"input shape last dimension expected 4 but input shape is {x.shape}"
    y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x)  # faster than clone/copy
    y[..., 0] = w * (x[..., 0] - x[..., 2] / 2) + padw  # top left x
    y[..., 1] = h * (x[..., 1] - x[..., 3] / 2) + padh  # top left y
    y[..., 2] = w * (x[..., 0] + x[..., 2] / 2) + padw  # bottom right x
    y[..., 3] = h * (x[..., 1] + x[..., 3] / 2) + padh  # bottom right y
    return y



ultralytics.utils.ops.xyxy2xywhn(x, w=640, h=640, clip=False, eps=0.0)

Convierte las coordenadas de la caja delimitadora del formato (x1, y1, x2, y2) al formato (x, y, anchura, altura, normalizado). x, y, anchura y altura se normalizan a las dimensiones de la imagen.

Par√°metros:

Nombre Tipo Descripción Por defecto
x ndarray | Tensor

Las coordenadas del cuadro delimitador de entrada en formato (x1, y1, x2, y2).

necesario
w int

La anchura de la imagen. Por defecto 640

640
h int

La altura de la imagen. Por defecto 640

640
clip bool

Si es Verdadero, las cajas se recortarán a los límites de la imagen. Por defecto es Falso

False
eps float

El valor mínimo de la anchura y la altura de la caja. Por defecto es 0.0

0.0

Devuelve:

Nombre Tipo Descripción
y ndarray | Tensor

Las coordenadas de la caja delimitadora en formato (x, y, anchura, altura, normalizado)

Código fuente en ultralytics/utils/ops.py
def xyxy2xywhn(x, w=640, h=640, clip=False, eps=0.0):
    """
    Convert bounding box coordinates from (x1, y1, x2, y2) format to (x, y, width, height, normalized) format. x, y,
    width and height are normalized to image dimensions.

    Args:
        x (np.ndarray | torch.Tensor): The input bounding box coordinates in (x1, y1, x2, y2) format.
        w (int): The width of the image. Defaults to 640
        h (int): The height of the image. Defaults to 640
        clip (bool): If True, the boxes will be clipped to the image boundaries. Defaults to False
        eps (float): The minimum value of the box's width and height. Defaults to 0.0

    Returns:
        y (np.ndarray | torch.Tensor): The bounding box coordinates in (x, y, width, height, normalized) format
    """
    if clip:
        x = clip_boxes(x, (h - eps, w - eps))
    assert x.shape[-1] == 4, f"input shape last dimension expected 4 but input shape is {x.shape}"
    y = torch.empty_like(x) if isinstance(x, torch.Tensor) else np.empty_like(x)  # faster than clone/copy
    y[..., 0] = ((x[..., 0] + x[..., 2]) / 2) / w  # x center
    y[..., 1] = ((x[..., 1] + x[..., 3]) / 2) / h  # y center
    y[..., 2] = (x[..., 2] - x[..., 0]) / w  # width
    y[..., 3] = (x[..., 3] - x[..., 1]) / h  # height
    return y



ultralytics.utils.ops.xywh2ltwh(x)

Convierte el formato del cuadro delimitador de [x, y, w, h] a [x1, y1, w, h], donde x1, y1 son las coordenadas superiores izquierdas.

Par√°metros:

Nombre Tipo Descripción Por defecto
x ndarray | Tensor

La entrada tensor con las coordenadas del cuadro delimitador en formato xywh

necesario

Devuelve:

Nombre Tipo Descripción
y ndarray | Tensor

Las coordenadas del cuadro delimitador en formato xyltwh

Código fuente en ultralytics/utils/ops.py
def xywh2ltwh(x):
    """
    Convert the bounding box format from [x, y, w, h] to [x1, y1, w, h], where x1, y1 are the top-left coordinates.

    Args:
        x (np.ndarray | torch.Tensor): The input tensor with the bounding box coordinates in the xywh format

    Returns:
        y (np.ndarray | torch.Tensor): The bounding box coordinates in the xyltwh format
    """
    y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
    y[..., 0] = x[..., 0] - x[..., 2] / 2  # top left x
    y[..., 1] = x[..., 1] - x[..., 3] / 2  # top left y
    return y



ultralytics.utils.ops.xyxy2ltwh(x)

Convierte nx4 cajas delimitadoras de [x1, y1, x2, y2] a [x1, y1, w, h], donde xy1=arriba-izquierda, xy2=abajo-derecha.

Par√°metros:

Nombre Tipo Descripción Por defecto
x ndarray | Tensor

La entrada tensor con las coordenadas de las cajas delimitadoras en formato xyxy

necesario

Devuelve:

Nombre Tipo Descripción
y ndarray | Tensor

Las coordenadas del cuadro delimitador en formato xyltwh.

Código fuente en ultralytics/utils/ops.py
def xyxy2ltwh(x):
    """
    Convert nx4 bounding boxes from [x1, y1, x2, y2] to [x1, y1, w, h], where xy1=top-left, xy2=bottom-right.

    Args:
        x (np.ndarray | torch.Tensor): The input tensor with the bounding boxes coordinates in the xyxy format

    Returns:
        y (np.ndarray | torch.Tensor): The bounding box coordinates in the xyltwh format.
    """
    y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
    y[..., 2] = x[..., 2] - x[..., 0]  # width
    y[..., 3] = x[..., 3] - x[..., 1]  # height
    return y



ultralytics.utils.ops.ltwh2xywh(x)

Convierte cajas nx4 de [x1, y1, w, h] a [x, y, w, h] donde xy1=arriba a la izquierda, xy=centro.

Par√°metros:

Nombre Tipo Descripción Por defecto
x Tensor

la entrada tensor

necesario

Devuelve:

Nombre Tipo Descripción
y ndarray | Tensor

Las coordenadas del cuadro delimitador en formato xywh.

Código fuente en ultralytics/utils/ops.py
def ltwh2xywh(x):
    """
    Convert nx4 boxes from [x1, y1, w, h] to [x, y, w, h] where xy1=top-left, xy=center.

    Args:
        x (torch.Tensor): the input tensor

    Returns:
        y (np.ndarray | torch.Tensor): The bounding box coordinates in the xywh format.
    """
    y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
    y[..., 0] = x[..., 0] + x[..., 2] / 2  # center x
    y[..., 1] = x[..., 1] + x[..., 3] / 2  # center y
    return y



ultralytics.utils.ops.xyxyxyxy2xywhr(corners)

Convierte Cajas delimitadoras orientadas (OBB) por lotes de [xy1, xy2, xy3, xy4] a [xywh, rotación]. Los valores de rotación se previstos en grados de 0 a 90.

Par√°metros:

Nombre Tipo Descripción Por defecto
corners ndarray | Tensor

Esquinas de entrada de forma (n, 8).

necesario

Devuelve:

Tipo Descripción
ndarray | Tensor

Datos convertidos en formato [cx, cy, w, h, rotación] de forma (n, 5).

Código fuente en ultralytics/utils/ops.py
def xyxyxyxy2xywhr(corners):
    """
    Convert batched Oriented Bounding Boxes (OBB) from [xy1, xy2, xy3, xy4] to [xywh, rotation]. Rotation values are
    expected in degrees from 0 to 90.

    Args:
        corners (numpy.ndarray | torch.Tensor): Input corners of shape (n, 8).

    Returns:
        (numpy.ndarray | torch.Tensor): Converted data in [cx, cy, w, h, rotation] format of shape (n, 5).
    """
    is_torch = isinstance(corners, torch.Tensor)
    points = corners.cpu().numpy() if is_torch else corners
    points = points.reshape(len(corners), -1, 2)
    rboxes = []
    for pts in points:
        # NOTE: Use cv2.minAreaRect to get accurate xywhr,
        # especially some objects are cut off by augmentations in dataloader.
        (x, y), (w, h), angle = cv2.minAreaRect(pts)
        rboxes.append([x, y, w, h, angle / 180 * np.pi])
    return (
        torch.tensor(rboxes, device=corners.device, dtype=corners.dtype)
        if is_torch
        else np.asarray(rboxes, dtype=points.dtype)
    )  # rboxes



ultralytics.utils.ops.xywhr2xyxyxyxy(rboxes)

Convierte Cajas delimitadoras orientadas (OBB) por lotes de [xywh, rotación] a [xy1, xy2, xy3, xy4]. Los valores de rotación deben estar en grados de 0 a 90.

Par√°metros:

Nombre Tipo Descripción Por defecto
rboxes ndarray | Tensor

Cajas en formato [cx, cy, w, h, rotación] de forma (n, 5) o (b, n, 5).

necesario

Devuelve:

Tipo Descripción
ndarray | Tensor

Puntos de esquina convertidos de forma (n, 4, 2) o (b, n, 4, 2).

Código fuente en ultralytics/utils/ops.py
def xywhr2xyxyxyxy(rboxes):
    """
    Convert batched Oriented Bounding Boxes (OBB) from [xywh, rotation] to [xy1, xy2, xy3, xy4]. Rotation values should
    be in degrees from 0 to 90.

    Args:
        rboxes (numpy.ndarray | torch.Tensor): Boxes in [cx, cy, w, h, rotation] format of shape (n, 5) or (b, n, 5).

    Returns:
        (numpy.ndarray | torch.Tensor): Converted corner points of shape (n, 4, 2) or (b, n, 4, 2).
    """
    is_numpy = isinstance(rboxes, np.ndarray)
    cos, sin = (np.cos, np.sin) if is_numpy else (torch.cos, torch.sin)

    ctr = rboxes[..., :2]
    w, h, angle = (rboxes[..., i : i + 1] for i in range(2, 5))
    cos_value, sin_value = cos(angle), sin(angle)
    vec1 = [w / 2 * cos_value, w / 2 * sin_value]
    vec2 = [-h / 2 * sin_value, h / 2 * cos_value]
    vec1 = np.concatenate(vec1, axis=-1) if is_numpy else torch.cat(vec1, dim=-1)
    vec2 = np.concatenate(vec2, axis=-1) if is_numpy else torch.cat(vec2, dim=-1)
    pt1 = ctr + vec1 + vec2
    pt2 = ctr + vec1 - vec2
    pt3 = ctr - vec1 - vec2
    pt4 = ctr - vec1 + vec2
    return np.stack([pt1, pt2, pt3, pt4], axis=-2) if is_numpy else torch.stack([pt1, pt2, pt3, pt4], dim=-2)



ultralytics.utils.ops.ltwh2xyxy(x)

Convierte la caja delimitadora de [x1, y1, w, h] a [x1, y1, x2, y2] donde xy1=arriba-izquierda, xy2=abajo-derecha.

Par√°metros:

Nombre Tipo Descripción Por defecto
x ndarray | Tensor

la imagen de entrada

necesario

Devuelve:

Nombre Tipo Descripción
y ndarray | Tensor

las coordenadas xyxy de las cajas delimitadoras.

Código fuente en ultralytics/utils/ops.py
def ltwh2xyxy(x):
    """
    It converts the bounding box from [x1, y1, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right.

    Args:
        x (np.ndarray | torch.Tensor): the input image

    Returns:
        y (np.ndarray | torch.Tensor): the xyxy coordinates of the bounding boxes.
    """
    y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
    y[..., 2] = x[..., 2] + x[..., 0]  # width
    y[..., 3] = x[..., 3] + x[..., 1]  # height
    return y



ultralytics.utils.ops.segments2boxes(segments)

Convierte las etiquetas de segmento en etiquetas de casilla, es decir, (cls, xy1, xy2, ...) en (cls, xywh)

Par√°metros:

Nombre Tipo Descripción Por defecto
segments list

lista de segmentos, cada segmento es una lista de puntos, cada punto es una lista de coordenadas x, y

necesario

Devuelve:

Tipo Descripción
ndarray

las coordenadas xywh de las cajas delimitadoras.

Código fuente en ultralytics/utils/ops.py
def segments2boxes(segments):
    """
    It converts segment labels to box labels, i.e. (cls, xy1, xy2, ...) to (cls, xywh)

    Args:
        segments (list): list of segments, each segment is a list of points, each point is a list of x, y coordinates

    Returns:
        (np.ndarray): the xywh coordinates of the bounding boxes.
    """
    boxes = []
    for s in segments:
        x, y = s.T  # segment xy
        boxes.append([x.min(), y.min(), x.max(), y.max()])  # cls, xyxy
    return xyxy2xywh(np.array(boxes))  # cls, xywh



ultralytics.utils.ops.resample_segments(segments, n=1000)

Introduce una lista de segmentos (n,2) y devuelve una lista de segmentos (n,2) muestreados a n puntos cada uno.

Par√°metros:

Nombre Tipo Descripción Por defecto
segments list

una lista de matrices (n,2), donde n es el n√ļmero de puntos del segmento.

necesario
n int

N√ļmero de puntos a los que remuestrear el segmento. Por defecto es 1000

1000

Devuelve:

Nombre Tipo Descripción
segments list

los segmentos remuestreados.

Código fuente en ultralytics/utils/ops.py
def resample_segments(segments, n=1000):
    """
    Inputs a list of segments (n,2) and returns a list of segments (n,2) up-sampled to n points each.

    Args:
        segments (list): a list of (n,2) arrays, where n is the number of points in the segment.
        n (int): number of points to resample the segment to. Defaults to 1000

    Returns:
        segments (list): the resampled segments.
    """
    for i, s in enumerate(segments):
        s = np.concatenate((s, s[0:1, :]), axis=0)
        x = np.linspace(0, len(s) - 1, n)
        xp = np.arange(len(s))
        segments[i] = (
            np.concatenate([np.interp(x, xp, s[:, i]) for i in range(2)], dtype=np.float32).reshape(2, -1).T
        )  # segment xy
    return segments



ultralytics.utils.ops.crop_mask(masks, boxes)

Toma una m√°scara y un cuadro delimitador, y devuelve una m√°scara recortada al cuadro delimitador.

Par√°metros:

Nombre Tipo Descripción Por defecto
masks Tensor

[n, h, w] tensor de m√°scaras

necesario
boxes Tensor

[n, 4] tensor de coordenadas bbox en forma de punto relativo

necesario

Devuelve:

Tipo Descripción
Tensor

Las m√°scaras se recortan al cuadro delimitador.

Código fuente en ultralytics/utils/ops.py
def crop_mask(masks, boxes):
    """
    It takes a mask and a bounding box, and returns a mask that is cropped to the bounding box.

    Args:
        masks (torch.Tensor): [n, h, w] tensor of masks
        boxes (torch.Tensor): [n, 4] tensor of bbox coordinates in relative point form

    Returns:
        (torch.Tensor): The masks are being cropped to the bounding box.
    """
    _, h, w = masks.shape
    x1, y1, x2, y2 = torch.chunk(boxes[:, :, None], 4, 1)  # x1 shape(n,1,1)
    r = torch.arange(w, device=masks.device, dtype=x1.dtype)[None, None, :]  # rows shape(1,1,w)
    c = torch.arange(h, device=masks.device, dtype=x1.dtype)[None, :, None]  # cols shape(1,h,1)

    return masks * ((r >= x1) * (r < x2) * (c >= y1) * (c < y2))



ultralytics.utils.ops.process_mask_upsample(protos, masks_in, bboxes, shape)

Toma la salida del cabezal de m√°scara y aplica la m√°scara a los cuadros delimitadores. Esto produce m√°scaras de mayor calidad pero es m√°s lento.

Par√°metros:

Nombre Tipo Descripción Por defecto
protos Tensor

[m√°scara_dim, m√°scara_h, m√°scara_w]

necesario
masks_in Tensor

[n, mask_dim], n es el n√ļmero de m√°scaras despu√©s de nms

necesario
bboxes Tensor

[n, 4], n es el n√ļmero de m√°scaras despu√©s de nms

necesario
shape tuple

el tama√Īo de la imagen de entrada (h,w)

necesario

Devuelve:

Tipo Descripción
Tensor

Las m√°scaras sobremuestreadas.

Código fuente en ultralytics/utils/ops.py
def process_mask_upsample(protos, masks_in, bboxes, shape):
    """
    Takes the output of the mask head, and applies the mask to the bounding boxes. This produces masks of higher quality
    but is slower.

    Args:
        protos (torch.Tensor): [mask_dim, mask_h, mask_w]
        masks_in (torch.Tensor): [n, mask_dim], n is number of masks after nms
        bboxes (torch.Tensor): [n, 4], n is number of masks after nms
        shape (tuple): the size of the input image (h,w)

    Returns:
        (torch.Tensor): The upsampled masks.
    """
    c, mh, mw = protos.shape  # CHW
    masks = (masks_in @ protos.float().view(c, -1)).sigmoid().view(-1, mh, mw)
    masks = F.interpolate(masks[None], shape, mode="bilinear", align_corners=False)[0]  # CHW
    masks = crop_mask(masks, bboxes)  # CHW
    return masks.gt_(0.5)



ultralytics.utils.ops.process_mask(protos, masks_in, bboxes, shape, upsample=False)

Aplica m√°scaras a cuadros delimitadores utilizando la salida del cabezal de m√°scaras.

Par√°metros:

Nombre Tipo Descripción Por defecto
protos Tensor

Un tensor de forma [m√°scara_dim, m√°scara_h, m√°scara_w].

necesario
masks_in Tensor

Un tensor de forma [n, mask_dim], donde n es el n√ļmero de m√°scaras despu√©s de NMS.

necesario
bboxes Tensor

Un tensor de forma [n, 4], donde n es el n√ļmero de m√°scaras despu√©s de NMS.

necesario
shape tuple

Una tupla de enteros que representa el tama√Īo de la imagen de entrada en el formato (h, w).

necesario
upsample bool

Bandera que indica si se debe sobremuestrear la m√°scara al tama√Īo de la imagen original. Por defecto es Falso.

False

Devuelve:

Tipo Descripción
Tensor

Una m√°scara binaria tensor de forma [n, h, w], donde n es el n√ļmero de m√°scaras despu√©s de NMS, y h y w son la altura y la anchura de la imagen de entrada. La m√°scara se aplica a los cuadros delimitadores.

Código fuente en ultralytics/utils/ops.py
def process_mask(protos, masks_in, bboxes, shape, upsample=False):
    """
    Apply masks to bounding boxes using the output of the mask head.

    Args:
        protos (torch.Tensor): A tensor of shape [mask_dim, mask_h, mask_w].
        masks_in (torch.Tensor): A tensor of shape [n, mask_dim], where n is the number of masks after NMS.
        bboxes (torch.Tensor): A tensor of shape [n, 4], where n is the number of masks after NMS.
        shape (tuple): A tuple of integers representing the size of the input image in the format (h, w).
        upsample (bool): A flag to indicate whether to upsample the mask to the original image size. Default is False.

    Returns:
        (torch.Tensor): A binary mask tensor of shape [n, h, w], where n is the number of masks after NMS, and h and w
            are the height and width of the input image. The mask is applied to the bounding boxes.
    """

    c, mh, mw = protos.shape  # CHW
    ih, iw = shape
    masks = (masks_in @ protos.float().view(c, -1)).sigmoid().view(-1, mh, mw)  # CHW
    width_ratio = mw / iw
    height_ratio = mh / ih

    downsampled_bboxes = bboxes.clone()
    downsampled_bboxes[:, 0] *= width_ratio
    downsampled_bboxes[:, 2] *= width_ratio
    downsampled_bboxes[:, 3] *= height_ratio
    downsampled_bboxes[:, 1] *= height_ratio

    masks = crop_mask(masks, downsampled_bboxes)  # CHW
    if upsample:
        masks = F.interpolate(masks[None], shape, mode="bilinear", align_corners=False)[0]  # CHW
    return masks.gt_(0.5)



ultralytics.utils.ops.process_mask_native(protos, masks_in, bboxes, shape)

Toma la salida del cabezal de la m√°scara, y la recorta tras el remuestreo a los cuadros delimitadores.

Par√°metros:

Nombre Tipo Descripción Por defecto
protos Tensor

[m√°scara_dim, m√°scara_h, m√°scara_w]

necesario
masks_in Tensor

[n, mask_dim], n es el n√ļmero de m√°scaras despu√©s de nms

necesario
bboxes Tensor

[n, 4], n es el n√ļmero de m√°scaras despu√©s de nms

necesario
shape tuple

el tama√Īo de la imagen de entrada (h,w)

necesario

Devuelve:

Nombre Tipo Descripción
masks Tensor

Las m√°scaras devueltas con dimensiones [h, w, n]

Código fuente en ultralytics/utils/ops.py
def process_mask_native(protos, masks_in, bboxes, shape):
    """
    It takes the output of the mask head, and crops it after upsampling to the bounding boxes.

    Args:
        protos (torch.Tensor): [mask_dim, mask_h, mask_w]
        masks_in (torch.Tensor): [n, mask_dim], n is number of masks after nms
        bboxes (torch.Tensor): [n, 4], n is number of masks after nms
        shape (tuple): the size of the input image (h,w)

    Returns:
        masks (torch.Tensor): The returned masks with dimensions [h, w, n]
    """
    c, mh, mw = protos.shape  # CHW
    masks = (masks_in @ protos.float().view(c, -1)).sigmoid().view(-1, mh, mw)
    masks = scale_masks(masks[None], shape)[0]  # CHW
    masks = crop_mask(masks, bboxes)  # CHW
    return masks.gt_(0.5)



ultralytics.utils.ops.scale_masks(masks, shape, padding=True)

Reescala las m√°scaras de segmento a la forma.

Par√°metros:

Nombre Tipo Descripción Por defecto
masks Tensor

(N, C, H, W).

necesario
shape tuple

Altura y anchura.

necesario
padding bool

Si es True, asume que las cajas se basan en la imagen aumentada por el estilo yolo . Si es Falso, entonces haz un cambio de escala.

True
Código fuente en ultralytics/utils/ops.py
def scale_masks(masks, shape, padding=True):
    """
    Rescale segment masks to shape.

    Args:
        masks (torch.Tensor): (N, C, H, W).
        shape (tuple): Height and width.
        padding (bool): If True, assuming the boxes is based on image augmented by yolo style. If False then do regular
            rescaling.
    """
    mh, mw = masks.shape[2:]
    gain = min(mh / shape[0], mw / shape[1])  # gain  = old / new
    pad = [mw - shape[1] * gain, mh - shape[0] * gain]  # wh padding
    if padding:
        pad[0] /= 2
        pad[1] /= 2
    top, left = (int(pad[1]), int(pad[0])) if padding else (0, 0)  # y, x
    bottom, right = (int(mh - pad[1]), int(mw - pad[0]))
    masks = masks[..., top:bottom, left:right]

    masks = F.interpolate(masks, shape, mode="bilinear", align_corners=False)  # NCHW
    return masks



ultralytics.utils.ops.scale_coords(img1_shape, coords, img0_shape, ratio_pad=None, normalize=False, padding=True)

Reescala las coordenadas del segmento (xy) de img1_forma a img0_forma.

Par√°metros:

Nombre Tipo Descripción Por defecto
img1_shape tuple

La forma de la imagen de la que proceden las coordenadas.

necesario
coords Tensor

las coordenadas a escalar de la forma n,2.

necesario
img0_shape tuple

la forma de la imagen a la que se aplica la segmentación.

necesario
ratio_pad tuple

la relaci√≥n entre el tama√Īo de la imagen y el tama√Īo de la imagen acolchada.

None
normalize bool

Si es Verdadero, las coordenadas se normalizar√°n al rango [0, 1]. Por defecto es Falso.

False
padding bool

Si es True, asume que las cajas se basan en la imagen aumentada por el estilo yolo . Si es Falso, entonces haz un cambio de escala.

True

Devuelve:

Nombre Tipo Descripción
coords Tensor

Las coordenadas a escala.

Código fuente en ultralytics/utils/ops.py
def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None, normalize=False, padding=True):
    """
    Rescale segment coordinates (xy) from img1_shape to img0_shape.

    Args:
        img1_shape (tuple): The shape of the image that the coords are from.
        coords (torch.Tensor): the coords to be scaled of shape n,2.
        img0_shape (tuple): the shape of the image that the segmentation is being applied to.
        ratio_pad (tuple): the ratio of the image size to the padded image size.
        normalize (bool): If True, the coordinates will be normalized to the range [0, 1]. Defaults to False.
        padding (bool): If True, assuming the boxes is based on image augmented by yolo style. If False then do regular
            rescaling.

    Returns:
        coords (torch.Tensor): The scaled coordinates.
    """
    if ratio_pad is None:  # calculate from img0_shape
        gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1])  # gain  = old / new
        pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2  # wh padding
    else:
        gain = ratio_pad[0][0]
        pad = ratio_pad[1]

    if padding:
        coords[..., 0] -= pad[0]  # x padding
        coords[..., 1] -= pad[1]  # y padding
    coords[..., 0] /= gain
    coords[..., 1] /= gain
    coords = clip_coords(coords, img0_shape)
    if normalize:
        coords[..., 0] /= img0_shape[1]  # width
        coords[..., 1] /= img0_shape[0]  # height
    return coords



ultralytics.utils.ops.regularize_rboxes(rboxes)

Regulariza las cajas rotadas en el intervalo [0, pi/2].

Par√°metros:

Nombre Tipo Descripción Por defecto
rboxes Tensor

(N, 5), xywhr.

necesario

Devuelve:

Tipo Descripción
Tensor

Las cajas regularizadas.

Código fuente en ultralytics/utils/ops.py
def regularize_rboxes(rboxes):
    """
    Regularize rotated boxes in range [0, pi/2].

    Args:
        rboxes (torch.Tensor): (N, 5), xywhr.

    Returns:
        (torch.Tensor): The regularized boxes.
    """
    x, y, w, h, t = rboxes.unbind(dim=-1)
    # Swap edge and angle if h >= w
    w_ = torch.where(w > h, w, h)
    h_ = torch.where(w > h, h, w)
    t = torch.where(w > h, t, t + math.pi / 2) % math.pi
    return torch.stack([x, y, w_, h_, t], dim=-1)  # regularized boxes



ultralytics.utils.ops.masks2segments(masks, strategy='largest')

Toma una lista de m√°scaras(n,h,w) y devuelve una lista de segmentos(n,xy)

Par√°metros:

Nombre Tipo Descripción Por defecto
masks Tensor

la salida del modelo, que es un tensor de forma (tama√Īo_lote, 160, 160)

necesario
strategy str

concat" o "mayor". Por defecto, mayor

'largest'

Devuelve:

Nombre Tipo Descripción
segments List

lista de m√°scaras de segmento

Código fuente en ultralytics/utils/ops.py
def masks2segments(masks, strategy="largest"):
    """
    It takes a list of masks(n,h,w) and returns a list of segments(n,xy)

    Args:
        masks (torch.Tensor): the output of the model, which is a tensor of shape (batch_size, 160, 160)
        strategy (str): 'concat' or 'largest'. Defaults to largest

    Returns:
        segments (List): list of segment masks
    """
    segments = []
    for x in masks.int().cpu().numpy().astype("uint8"):
        c = cv2.findContours(x, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
        if c:
            if strategy == "concat":  # concatenate all segments
                c = np.concatenate([x.reshape(-1, 2) for x in c])
            elif strategy == "largest":  # select largest segment
                c = np.array(c[np.array([len(x) for x in c]).argmax()]).reshape(-1, 2)
        else:
            c = np.zeros((0, 2))  # no segments found
        segments.append(c.astype("float32"))
    return segments



ultralytics.utils.ops.convert_torch2numpy_batch(batch)

Convierte un lote de tensores FP32 torch (0,0-1,0) en una matriz NumPy uint8 (0-255), cambiando la disposición de BCHW a BHWC.

Par√°metros:

Nombre Tipo Descripción Por defecto
batch Tensor

Entrada tensor lote de forma (Lote, Canales, Altura, Anchura) y dtype torch.float32.

necesario

Devuelve:

Tipo Descripción
ndarray

Salida NumPy array batch de forma (Lote, Altura, Anchura, Canales) y dtype uint8.

Código fuente en ultralytics/utils/ops.py
def convert_torch2numpy_batch(batch: torch.Tensor) -> np.ndarray:
    """
    Convert a batch of FP32 torch tensors (0.0-1.0) to a NumPy uint8 array (0-255), changing from BCHW to BHWC layout.

    Args:
        batch (torch.Tensor): Input tensor batch of shape (Batch, Channels, Height, Width) and dtype torch.float32.

    Returns:
        (np.ndarray): Output NumPy array batch of shape (Batch, Height, Width, Channels) and dtype uint8.
    """
    return (batch.permute(0, 2, 3, 1).contiguous() * 255).clamp(0, 255).to(torch.uint8).cpu().numpy()



ultralytics.utils.ops.clean_str(s)

Limpia una cadena sustituyendo los caracteres especiales por guiones bajos _

Par√°metros:

Nombre Tipo Descripción Por defecto
s str

una cadena que necesita que se sustituyan los caracteres especiales

necesario

Devuelve:

Tipo Descripción
str

una cadena con caracteres especiales sustituidos por un guión bajo _

Código fuente en ultralytics/utils/ops.py
def clean_str(s):
    """
    Cleans a string by replacing special characters with underscore _

    Args:
        s (str): a string needing special characters replaced

    Returns:
        (str): a string with special characters replaced by an underscore _
    """
    return re.sub(pattern="[|@#!¬°¬∑$‚ā¨%&()=?¬Ņ^*;:,¬®¬ī><+]", repl="_", string=s)





Creado 2023-11-12, Actualizado 2024-05-18
Autores: glenn-jocher (5), Burhan-Q (1), Laughing-q (1)