рд╕рд╛рдордЧреНрд░реА рдкрд░ рдЬрд╛рдПрдВ

рдХреЗ рд▓рд┐рдП рд╕рдВрджрд░реНрдн ultralytics/utils/ops.py

рдиреЛрдЯ

рдпрд╣ рдлрд╝рд╛рдЗрд▓ рдпрд╣рд╛рдБ рдЙрдкрд▓рдмреНрдз рд╣реИ https://github.com/ultralytics/ultralytics/рдмреВрдБрдж/рдореБрдЦреНрдп/ultralytics/utils/ops.py рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред рдпрджрд┐ рдЖрдк рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рджреЗрдЦрддреЗ рд╣реИрдВ рддреЛ рдХреГрдкрдпрд╛ рдкреБрд▓ рдЕрдиреБрд░реЛрдз рдХрд╛ рдпреЛрдЧрджрд╛рди рдХрд░рдХреЗ рдЗрд╕реЗ рдареАрдХ рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдХрд░реЗрдВ ЁЯЫая╕Пред ЁЯЩП рдзрдиреНрдпрд╡рд╛рдж !



ultralytics.utils.ops.Profile

рдХрд╛ рд░реВрдк: ContextDecorator

YOLOv8 рдкреНрд░реЛрдлрд╝рд╛рдЗрд▓ рд╡рд░реНрдЧред @Profile() рдХреЗ рд╕рд╛рде рдПрдХ рдбреЗрдХреЛрд░реЗрдЯрд░ рдХреЗ рд░реВрдк рдореЗрдВ рдпрд╛ 'рдкреНрд░реЛрдлрд╛рдЗрд▓ () рдХреЗ рд╕рд╛рде' рдХреЗ рд╕рд╛рде рдПрдХ рд╕рдВрджрд░реНрдн рдкреНрд░рдмрдВрдзрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред

рдЙрджрд╛рд╣рд░рдг
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"
рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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__()

рд╕рдордп рд╕реБрд░реБ рдЧрд░реНрдиреБрд╣реЛрд╕реНред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/utils/ops.py
def __enter__(self):
    """Start timing."""
    self.start = self.time()
    return self

__exit__(type, value, traceback)

рд╕рдордп рдмрдВрдж рдХрд░реЛред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдкреНрд░реЛрдлрд╝рд╛рдЗрд▓ рд╡рд░реНрдЧ рдкреНрд░рд╛рд░рдВрдн рдХрд░реЗрдВред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
t float

рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╕рдордпред 0.0 рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯред

0.0
device device

рдореЙрдбрд▓ рдЕрдиреБрдорд╛рди рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдЙрдкрдХрд░рдгред рдХреЛрдИ рдирд╣реАрдВ (рд╕реАрдкреАрдпреВ) рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯред

None
рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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__()

рдкреНрд░реЛрдлрд╛рдЗрд▓рд░ рдореЗрдВ рд╕рдВрдЪрд┐рдд рдмреАрддрд╛ рд╕рдордп рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рдиреЗ рд╡рд╛рд▓реА рдПрдХ рдорд╛рдирд╡-рдкрдардиреАрдп рд╕реНрдЯреНрд░рд┐рдВрдЧ рд▓реМрдЯрд╛рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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()

рд╡рд░реНрддрдорд╛рди рд╕рдордп рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

1 рд╕реЗрдЧрдореЗрдВрдЯ рд▓реЗрдмрд▓ рдХреЛ 1 рдмреЙрдХреНрд╕ рд▓реЗрдмрд▓ рдореЗрдВ рдмрджрд▓реЗрдВ, рдЕрдВрджрд░-рдЫрд╡рд┐ рдмрд╛рдзрд╛ рд▓рд╛рдЧреВ рдХрд░реЗрдВ, рдпрд╛рдиреА (xy1, xy2, ...) рд╕реЗ (xyxy)ред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
segment Tensor

рд╕реЗрдЧрдореЗрдВрдЯ рд▓реЗрдмрд▓

рдЖрд╡рд╢реНрдпрдХ
width int

рдЫрд╡рд┐ рдХреА рдЪреМрдбрд╝рд╛рдИ. 640 рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ

640
height int

рдЫрд╡рд┐ рдХреА рдКрдВрдЪрд╛рдИ. 640 рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ

640

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
ndarray

рд╕реЗрдЧрдореЗрдВрдЯ рдХрд╛ рдиреНрдпреВрдирддрдо рдФрд░ рдЕрдзрд┐рдХрддрдо x рдФрд░ y рдорд╛рдиред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ (рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ xyxy рдХреЗ рдкреНрд░рд╛рд░реВрдк рдореЗрдВ) рдХреЛ рдЙрд╕ рдЫрд╡рд┐ рдХреЗ рдЖрдХрд╛рд░ рд╕реЗ рд░реАрд╕реНрдХреЗрд▓ рдХрд░рддрд╛ рд╣реИ рдЬреЛ рд╡реЗ рдореВрд▓ рд░реВрдк рд╕реЗ рдереЗ рдПрдХ рдЕрд▓рдЧ рдЫрд╡рд┐ (img0_shape) рдХреЗ рдЖрдХрд╛рд░ рдХреЗ рд▓рд┐рдП (img1_shape) рдореЗрдВ рдирд┐рд░реНрджрд┐рд╖реНрдЯред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
img1_shape tuple

рдЫрд╡рд┐ рдХрд╛ рдЖрдХрд╛рд░ рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ (рдКрдВрдЪрд╛рдИ, рдЪреМрдбрд╝рд╛рдИ) рдХреЗ рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╣реИрдВред

рдЖрд╡рд╢реНрдпрдХ
boxes Tensor

рдЫрд╡рд┐ рдореЗрдВ рд╡рд╕реНрддреБрдУрдВ рдХреЗ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕, (x1, y1, x2, y2) рдХреЗ рдкреНрд░рд╛рд░реВрдк рдореЗрдВ

рдЖрд╡рд╢реНрдпрдХ
img0_shape tuple

рд▓рдХреНрд╖реНрдп рдЫрд╡рд┐ рдХрд╛ рдЖрдХрд╛рд░, (рдКрдВрдЪрд╛рдИ, рдЪреМрдбрд╝рд╛рдИ) рдХреЗ рдкреНрд░рд╛рд░реВрдк рдореЗрдВред

рдЖрд╡рд╢реНрдпрдХ
ratio_pad tuple

рдмрдХреНрд╕реЗ рдХреЛ рд╕реНрдХреЗрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП (рдЕрдиреБрдкрд╛рдд, рдкреИрдб) рдХрд╛ рдПрдХ рдЯреБрдкрд▓ред рдпрджрд┐ рдкреНрд░рджрд╛рди рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рддреЛ рдЕрдиреБрдкрд╛рдд рдФрд░ рдкреИрдб рдХреНрдпрд╛ рд╣реЛрдЧрд╛? рджреЛ рдЫрд╡рд┐рдпреЛрдВ рдХреЗ рдмреАрдЪ рдЖрдХрд╛рд░ рдЕрдВрддрд░ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЧрдгрдирд╛ рдХреА рдЬрд╛рддреА рд╣реИред

None
padding bool

рдпрджрд┐ рд╕рд╣реА рд╣реИ, рддреЛ рдпрд╣ рдорд╛рдирддреЗ рд╣реБрдП рдХрд┐ рдмреЙрдХреНрд╕ рджреНрд╡рд╛рд░рд╛ рд╕рдВрд╡рд░реНрдзрд┐рдд рдЫрд╡рд┐ рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИ yolo рд╡рд░реНрддрд┐рдХрд╛ред рдЕрдЧрд░ рдЧрд▓рдд рд╣реИ рддреЛ рдирд┐рдпрдорд┐рдд рдХрд░реЗрдВ рдкреБрдирдГ рд╕реНрдХреЗрд▓рд┐рдВрдЧред

True
xywh bool

рдмреЙрдХреНрд╕ рдкреНрд░рд╛рд░реВрдк xywh рд╣реИ рдпрд╛ рдирд╣реАрдВ, рдбрд┐рдлрд╝реЙрд▓реНрдЯ = рдЧрд▓рддред

False

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
boxes Tensor

рд╕реНрдХреЗрд▓ рдХрд┐рдП рдЧрдП рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕, (x1, y1, x2, y2) рдХреЗ рдкреНрд░рд╛рд░реВрдк рдореЗрдВ

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдирд┐рдХрдЯрддрдо рд╕рдВрдЦреНрдпрд╛ рджреЗрддрд╛ рд╣реИ рдЬреЛ рджрд┐рдП рдЧрдП рднрд╛рдЬрдХ рд╕реЗ рд╡рд┐рднрд╛рдЬреНрдп рд╣реИред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
x int

рд╡рд┐рднрд╛рдЬреНрдп рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдВрдЦреНрдпрд╛ред

рдЖрд╡рд╢реНрдпрдХ
divisor int | Tensor

рднрд╛рдЬрдХред

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
int

рднрд╛рдЬрдХ рджреНрд╡рд╛рд░рд╛ рд╡рд┐рднрд╛рдЬреНрдп рдирд┐рдХрдЯрддрдо рд╕рдВрдЦреНрдпрд╛ред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдУрдмреНрд╕ рдХреЗ рд▓рд┐рдП рдПрдирдПрдордПрд╕, рдкреНрд░реЛрдмрд┐рдпреВ рдФрд░ рдлрд╛рд╕реНрдЯ-рдПрдирдПрдордПрд╕ рджреНрд╡рд╛рд░рд╛ рд╕рдВрдЪрд╛рд▓рд┐рддред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
boxes Tensor

(рдПрди, 5), xywhrред

рдЖрд╡рд╢реНрдпрдХ
scores Tensor

(рдПрди,)ред

рдЖрд╡рд╢реНрдпрдХ
threshold float

IoU рджрд╣рд▓реАрдЬред

0.45

рджреЗрддрд╛:

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдмрдХреНрд╕реЗ рдХреЗ рдПрдХ рд╕реЗрдЯ рдкрд░ рдЧреИрд░-рдЕрдзрд┐рдХрддрдо рджрдорди (рдПрдирдПрдордПрд╕) рдХрд░реЗрдВ, рдорд╛рд╕реНрдХ рдФрд░ рдкреНрд░рддрд┐ рдмреЙрдХреНрд╕ рдХрдИ рд▓реЗрдмрд▓ рдХреЗ рд╕рдорд░реНрдерди рдХреЗ рд╕рд╛рдеред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
prediction Tensor

рдПрдХ tensor рдЖрдХрд╛рд░ рдХрд╛ (batch_size, num_classes + 4 + num_masks, num_boxes) рдЕрдиреБрдорд╛рдирд┐рдд рдмрдХреНрд╕реЗ, рдХрдХреНрд╖рд╛рдПрдВ рдФрд░ рдорд╛рд╕реНрдХ рдпреБрдХреНрддред рд╡рд╣реА tensor рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рдПрдХ рдореЙрдбрд▓ рджреНрд╡рд╛рд░рд╛ рдЖрдЙрдЯрдкреБрдЯ, рдЬреИрд╕реЗ YOLO.

рдЖрд╡рд╢реНрдпрдХ
conf_thres float

рдХреЙрдиреНрдлрд╝рд┐рдбреЗрдВрд╕ рдереНрд░реЗрд╢реЛрд▓реНрдб рдЬрд┐рд╕рдХреЗ рдиреАрдЪреЗ рдмреЙрдХреНрд╕ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд┐рдП рдЬрд╛рдПрдБрдЧреЗ. рдорд╛рдиреНрдп рдорд╛рди 0.0 рдФрд░ 1.0 рдХреЗ рдмреАрдЪ рд╣реИрдВ.

0.25
iou_thres float

IoU рдереНрд░реЗрд╢реЛрд▓реНрдб рдЬрд┐рд╕рдХреЗ рдиреАрдЪреЗ NMS рдХреЗ рджреМрд░рд╛рди рдмреЙрдХреНрд╕ рдХреЛ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдорд╛рдиреНрдп рдорд╛рди 0.0 рдФрд░ 1.0 рдХреЗ рдмреАрдЪ рд╣реИрдВ.

0.45
classes List[int]

рд╡рд┐рдЪрд╛рд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд░реНрдЧ рд╕реВрдЪрдХрд╛рдВрдХреЛрдВ рдХреА рдПрдХ рд╕реВрдЪреАред рдпрджрд┐ рдХреЛрдИ рдирд╣реАрдВ, рддреЛ рд╕рднреА рд╡рд░реНрдЧреЛрдВ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

None
agnostic bool

рдпрджрд┐ рд╕рдЪ рд╣реИ, рддреЛ рдореЙрдбрд▓ рд╡рд░реНрдЧреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рдХреЗ рд▓рд┐рдП рдЕрдЬреНрдЮреЗрдпрд╡рд╛рджреА рд╣реИ, рдФрд░ рд╕рднреА рдХрдХреНрд╖рд╛рдУрдВ рдХреЛ рдПрдХ рдорд╛рдирд╛ рдЬрд╛рдПрдЧрд╛ред

False
multi_label bool

рдпрджрд┐ рд╕рд╣реА рд╣реИ, рддреЛ рдкреНрд░рддреНрдпреЗрдХ рдмреЙрдХреНрд╕ рдореЗрдВ рдХрдИ рд▓реЗрдмрд▓ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред

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

рд╕реВрдЪрд┐рдпреЛрдВ рдХреА рдПрдХ рд╕реВрдЪреА, рдЬрд╣рд╛рдВ рдкреНрд░рддреНрдпреЗрдХ рдЖрдВрддрд░рд┐рдХ рд╕реВрдЪреА рдореЗрдВ рдХрд┐рд╕реА рджрд┐рдП рдЧрдП рдЫрд╡рд┐ рдХреЗ рд▓рд┐рдП Apriori рд▓реЗрдмрд▓ рд╣реЛрддреЗ рд╣реИрдВред рд╕реВрдЪреА рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдП рдбреЗрдЯрд╛рд▓реЛрдбрд░ рджреНрд╡рд╛рд░рд╛ рдЖрдЙрдЯрдкреБрдЯ, рдкреНрд░рддреНрдпреЗрдХ рд▓реЗрдмрд▓ (class_index, x1, y1, x2, y2) рдХрд╛ рдЯрдкрд▓ рд╣реЛрддрд╛ рд╣реИред

()
max_det int

рдПрдирдПрдордПрд╕ рдХреЗ рдмрд╛рдж рд░рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдмрдХреНрд╕реЗ рдХреА рдЕрдзрд┐рдХрддрдо рд╕рдВрдЦреНрдпрд╛ред

300
nc int

рдореЙрдбрд▓ рджреНрд╡рд╛рд░рд╛ рдЖрдЙрдЯрдкреБрдЯ рдХрдХреНрд╖рд╛рдУрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ред рдЗрд╕рдХреЗ рдмрд╛рдж рдХрд┐рд╕реА рднреА рд╕реВрдЪрдХрд╛рдВрдХ рдХреЛ рдорд╛рд╕реНрдХ рдорд╛рдирд╛ рдЬрд╛рдПрдЧрд╛ред

0
max_time_img float

рдПрдХ рдЫрд╡рд┐ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдзрд┐рдХрддрдо рд╕рдордп (рд╕реЗрдХрдВрдб)ред

0.05
max_nms int

torchvision.ops.nms() рдореЗрдВ рдмрдХреНрд╕реЗ рдХреА рдЕрдзрд┐рдХрддрдо рд╕рдВрдЦреНрдпрд╛ред

30000
max_wh int

рдкрд┐рдХреНрд╕реЗрд▓ рдореЗрдВ рдЕрдзрд┐рдХрддрдо рдмреЙрдХреНрд╕ рдЪреМрдбрд╝рд╛рдИ рдФрд░ рдКрдВрдЪрд╛рдИред

7680
in_place bool

рдпрджрд┐ рд╕рд╣реА рд╣реИ, рддреЛ рдЗрдирдкреБрдЯ рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгреА tensor рдЬрдЧрд╣ рдореЗрдВ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

True

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
List[Tensor]

рд▓рдВрдмрд╛рдИ batch_size рдХреА рдПрдХ рд╕реВрдЪреА, рдЬрд╣рд╛рдВ рдкреНрд░рддреНрдпреЗрдХ рддрддреНрд╡ рдПрдХ tensor рдХрд╛ рдЖрдХреГрддрд┐ (num_boxes, 6 + num_masks) рдЬрд┐рд╕рдореЗрдВ рд░рдЦреЗ рдЧрдП рдмрдХреНрд╕реЗ рд╣реИрдВ, рдХреЙрд▓рдо рдХреЗ рд╕рд╛рде (x1, y1, x2, y2, рдЖрддреНрдорд╡рд┐рд╢реНрд╡рд╛рд╕, рд╡рд░реНрдЧ, рдореБрдЦреМрдЯрд╛ 1, рдорд╛рд╕реНрдХ 2, ...)ред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдФрд░ рдПрдХ рдЖрдХреГрддрд┐ (рдКрдВрдЪрд╛рдИ, рдЪреМрдбрд╝рд╛рдИ) рдХреА рдПрдХ рд╕реВрдЪреА рд▓реЗрддрд╛ рд╣реИ рдФрд░ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдХреЛ рдЖрдХрд╛рд░ рдореЗрдВ рдХреНрд▓рд┐рдк рдХрд░рддрд╛ рд╣реИред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
boxes Tensor

рдХреНрд▓рд┐рдк рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕

рдЖрд╡рд╢реНрдпрдХ
shape tuple

рдЫрд╡рд┐ рдХрд╛ рдЖрдХрд╛рд░

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
Tensor | ndarray

рдХреНрд▓рд┐рдк рдХрд┐рдП рдЧрдП рдмреЙрдХреНрд╕

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдХреНрд▓рд┐рдк рд▓рд╛рдЗрди рдЫрд╡рд┐ рд╕реАрдорд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХрд░рддреА рд╣реИред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
coords Tensor | ndarray

рд░реЗрдЦрд╛ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреА рдПрдХ рд╕реВрдЪреА.

рдЖрд╡рд╢реНрдпрдХ
shape tuple

рдкреВрд░реНрдгрд╛рдВрдХреЛрдВ рдХрд╛ рдПрдХ рдЯрдкрд▓, рдЬреЛ рдкреНрд░рд╛рд░реВрдк (рдКрдВрдЪрд╛рдИ, рдЪреМрдбрд╝рд╛рдИ) рдореЗрдВ рдЫрд╡рд┐ рдХреЗ рдЖрдХрд╛рд░ рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рддрд╛ рд╣реИред

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
Tensor | ndarray

рдХреНрд▓рд┐рдк рдХрд┐рдП рдЧрдП рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдПрдХ рдореБрдЦреМрдЯрд╛ рд▓реЗрддрд╛ рд╣реИ, рдФрд░ рдЗрд╕реЗ рдореВрд▓ рдЫрд╡рд┐ рдЖрдХрд╛рд░ рдореЗрдВ рдмрджрд▓ рджреЗрддрд╛ рд╣реИред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
masks ndarray

рдЖрдХрд╛рд░ рдмрджрд▓рдиреЗ рдФрд░ рдЧрджреНрджреЗрджрд╛рд░ рдорд╛рд╕реНрдХ/рдЫрд╡рд┐рдпрд╛рдВ, [рдПрдЪ, рдбрдмреНрд▓реНрдпреВ, рдиреНрдпреВрдо]/[рдПрдЪ, рдбрдмреНрд▓реНрдпреВ, 3]ред

рдЖрд╡рд╢реНрдпрдХ
im0_shape tuple

рдореВрд▓ рдЫрд╡рд┐ рдЖрдХреГрддрд┐

рдЖрд╡рд╢реНрдпрдХ
ratio_pad tuple

рдореВрд▓ рдЫрд╡рд┐ рдХреЗ рд▓рд┐рдП рдкреИрдбрд┐рдВрдЧ рдХрд╛ рдЕрдиреБрдкрд╛рддред

None

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
masks Tensor

рдЬреЛ рдорд╛рд╕реНрдХ рд▓реМрдЯрд╛рдП рдЬрд╛ рд░рд╣реЗ рд╣реИрдВред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреЛ (x1, y1, x2, y2) рдкреНрд░рд╛рд░реВрдк рд╕реЗ (x, y, рдЪреМрдбрд╝рд╛рдИ, рдКрдВрдЪрд╛рдИ) рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░реЗрдВ рдЬрд╣рд╛рдВ (x1, y1) рдКрдкрд░реА-рдмрд╛рдПрдБ рдХреЛрдиреЗ рдФрд░ (x2, y2) рдирд┐рдЪрд▓реЗ-рджрд╛рдПрдБ рдХреЛрдиреЗ рд╣реИрдВред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
x ndarray | Tensor

рдЗрдирдкреБрдЯ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ (x1, y1, x2, y2) рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╕рдордиреНрд╡рдп рдХрд░рддрд╛ рд╣реИред

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
y ndarray | Tensor

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ (x, y, рдЪреМрдбрд╝рд╛рдИ, рдКрдВрдЪрд╛рдИ) рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╕рдордиреНрд╡рдп рдХрд░рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреЛ (x, y, рдЪреМрдбрд╝рд╛рдИ, рдКрдВрдЪрд╛рдИ) рдкреНрд░рд╛рд░реВрдк рд╕реЗ (x1, y1, x2, y2) рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░реЗрдВ рдЬрд╣рд╛рдВ (x1, y1) рдКрдкрд░реА-рдмрд╛рдПрдБ рдХреЛрдиреЗ рдФрд░ (x2, y2) рдирд┐рдЪрд▓реЗ-рджрд╛рдПрдБ рдХреЛрдиреЗ рд╣реИрдВред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
x ndarray | Tensor

рдЗрдирдкреБрдЯ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ (x, y, рдЪреМрдбрд╝рд╛рдИ, рдКрдВрдЪрд╛рдИ) рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╕рдордиреНрд╡рдп рдХрд░рддрд╛ рд╣реИред

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
y ndarray | Tensor

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ (x1, y1, x2, y2) рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХрд░рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рд╕рд╛рдорд╛рдиреНрдпреАрдХреГрдд рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреЛ рдкрд┐рдХреНрд╕реЗрд▓ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░реЗрдВред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
x ndarray | Tensor

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХред

рдЖрд╡рд╢реНрдпрдХ
w int

рдЫрд╡рд┐ рдХреА рдЪреМрдбрд╝рд╛рдИ. 640 рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ

640
h int

рдЫрд╡рд┐ рдХреА рдКрдВрдЪрд╛рдИред 640 рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ

640
padw int

рдкреИрдбрд┐рдВрдЧ рдЪреМрдбрд╝рд╛рдИред 0 рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ

0
padh int

рдкреИрдбрд┐рдВрдЧ рдКрдВрдЪрд╛рдИред 0 рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ

0

рджреЗрддрд╛: рд╡рд╛рдИ (рдПрдирдкреА.рдирджрд░рд░реЗ | torch.Tensor): рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдХреЗ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ [x1, y1, x2, y2] рдЬрд╣рд╛рдВ X1, Y1 рдКрдкрд░реА-рдмрд╛рдПрдБ рдХреЛрдиреЗ рд╣реИ, x2, y2 рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдХрд╛ рдирд┐рдЪрд▓рд╛-рджрд╛рдпрд╛рдБ рдХреЛрдирд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреЛ (x1, y1, x2, y2) рдкреНрд░рд╛рд░реВрдк рд╕реЗ (x, y, рдЪреМрдбрд╝рд╛рдИ, рдКрдВрдЪрд╛рдИ, рд╕рд╛рдорд╛рдиреНрдпреАрдХреГрдд) рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░реЗрдВред рдПрдХреНрд╕, рд╡рд╛рдИ, рдЪреМрдбрд╝рд╛рдИ рдФрд░ рдКрдВрдЪрд╛рдИ рдЫрд╡рд┐ рдЖрдпрд╛рдореЛрдВ рдХреЗ рд▓рд┐рдП рд╕рд╛рдорд╛рдиреНрдпреАрдХреГрдд рд╣реИрдВред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
x ndarray | Tensor

рдЗрдирдкреБрдЯ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ (x1, y1, x2, y2) рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╕рдордиреНрд╡рдп рдХрд░рддрд╛ рд╣реИред

рдЖрд╡рд╢реНрдпрдХ
w int

рдЫрд╡рд┐ рдХреА рдЪреМрдбрд╝рд╛рдИ. 640 рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ

640
h int

рдЫрд╡рд┐ рдХреА рдКрдВрдЪрд╛рдИ. 640 рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ

640
clip bool

рдпрджрд┐ рд╕рд╣реА рд╣реИ, рддреЛ рдмреЙрдХреНрд╕ рдЫрд╡рд┐ рд╕реАрдорд╛рдУрдВ рдкрд░ рдХреНрд▓рд┐рдк рдХрд┐рдП рдЬрд╛рдПрдВрдЧреЗред рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд╕реЗ рдЧрд▓рдд

False
eps float

рдмреЙрдХреНрд╕ рдХреА рдЪреМрдбрд╝рд╛рдИ рдФрд░ рдКрдВрдЪрд╛рдИ рдХрд╛ рдиреНрдпреВрдирддрдо рдорд╛рди. 0.0 рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ

0.0

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
y ndarray | Tensor

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ (x, y, рдЪреМрдбрд╝рд╛рдИ, рдКрдВрдЪрд╛рдИ, рд╕рд╛рдорд╛рдиреНрдпреАрдХреГрдд) рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╕рдордиреНрд╡рдп рдХрд░рддрд╛ рд╣реИ

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдкреНрд░рд╛рд░реВрдк рдХреЛ [x, y, w, h] рд╕реЗ [x1, y1, w, h] рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░реЗрдВ, рдЬрд╣рд╛рдБ x1, y1 рд╢реАрд░реНрд╖-рдмрд╛рдПрдБ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рд╣реИрдВред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
x ndarray | Tensor

рдЗрдирдкреБрдЯ tensor рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдХреЗ рд╕рд╛рде XYWH рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
y ndarray | Tensor

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ xyltwh рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╕рдордиреНрд╡рдп рдХрд░рддрд╛ рд╣реИ

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

nx4 рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдХреЛ [x1, y1, x2, y2] рд╕реЗ [x1, y1, w, h] рдореЗрдВ рдмрджрд▓реЗрдВ, рдЬрд╣рд╛рдБ xy1=рдКрдкрд░реА-рдмрд╛рдПрдБ, xy2=рдиреАрдЪреЗ-рджрд╛рдПрдБ.

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
x ndarray | Tensor

рдЗрдирдкреБрдЯ tensor рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдХреЗ рд╕рд╛рде xyxy рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
y ndarray | Tensor

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ xyltwh рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╕рдордиреНрд╡рдп рдХрд░рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

nx4 рдмрдХреНрд╕реЗ рдХреЛ [x1, y1, w, h] рд╕реЗ [x, y, w, h] рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░реЗрдВ рдЬрд╣рд╛рдБ xy1=рдКрдкрд░реА-рдмрд╛рдпрд╛рдБ, xy=рдХреЗрдВрджреНрд░.

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
x Tensor

рдЗрдирдкреБрдЯ tensor

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
y ndarray | Tensor

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ xywh рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХрд░рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдмреИрдЪреНрдб рдУрд░рд┐рдПрдВрдЯреЗрдб рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ (OBB) рдХреЛ [xy1, xy2, xy3, xy4] рд╕реЗ [xywh, рд░реЛрдЯреЗрд╢рди] рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░реЗрдВред рд░реЛрдЯреЗрд╢рди рдорд╛рди рд╣реИрдВ 0 рд╕реЗ 90 рддрдХ рдбрд┐рдЧреНрд░реА рдореЗрдВ рдЕрдкреЗрдХреНрд╖рд┐рддред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
corners ndarray | Tensor

рдЖрдХрд╛рд░ рдХреЗ рдЗрдирдкреБрдЯ рдХреЛрдиреЗ (рдПрди, 8)ред

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
ndarray | Tensor

рдЖрдХрд╛рд░ рдХреЗ [cx, cy, w, h, рд░реЛрдЯреЗрд╢рди] рдкреНрд░рд╛рд░реВрдк (n, 5) рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдбреЗрдЯрд╛ред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдмреИрдЪреНрдб рдУрд░рд┐рдПрдВрдЯреЗрдб рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ (OBB) рдХреЛ [xywh, рд░реЛрдЯреЗрд╢рди] рд╕реЗ [xy1, xy2, xy3, xy4] рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░реЗрдВред рд░реЛрдЯреЗрд╢рди рдорд╛рди рдЪрд╛рд╣рд┐рдП 0 рд╕реЗ 90 рддрдХ рдбрд┐рдЧреНрд░реА рдореЗрдВ рд╣реЛред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
rboxes ndarray | Tensor

рдЖрдХрд╛рд░ рдХреЗ [cx, cy, w, h, рд░реЛрдЯреЗрд╢рди] рдкреНрд░рд╛рд░реВрдк (n, 5) рдпрд╛ (b, n, 5) рдореЗрдВ рдмреЙрдХреНрд╕ред

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
ndarray | Tensor

рдЖрдХреГрддрд┐ рдХреЗ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХреЛрдиреЗ рдмрд┐рдВрджреБ (n, 4, 2) рдпрд╛ (b, n, 4, 2)ред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдпрд╣ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдХреЛ [x1, y1, w, h] рд╕реЗ [x1, y1, x2, y2] рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рддрд╛ рд╣реИ рдЬрд╣рд╛рдВ xy1=рдКрдкрд░-рдмрд╛рдПрдВ, xy2=рдиреАрдЪреЗ-рджрд╛рдПрдВред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
x ndarray | Tensor

рдЗрдирдкреБрдЯ рдЫрд╡рд┐

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
y ndarray | Tensor

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдХреЗ xyxy рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдпрд╣ рд╕реЗрдЧрдореЗрдВрдЯ рд▓реЗрдмрд▓ рдХреЛ рдмреЙрдХреНрд╕ рд▓реЗрдмрд▓ рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░рддрд╛ рд╣реИ, рдЕрд░реНрдерд╛рдд (cls, xy1, xy2, ...) рд╕реЗ (cls, xywh)

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
segments list

рдЦрдВрдбреЛрдВ рдХреА рд╕реВрдЪреА, рдкреНрд░рддреНрдпреЗрдХ рдЦрдВрдб рдмрд┐рдВрджреБрдУрдВ рдХреА рдПрдХ рд╕реВрдЪреА рд╣реИ, рдкреНрд░рддреНрдпреЗрдХ рдмрд┐рдВрджреБ рдПрдХреНрд╕, рд╡рд╛рдИ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреА рдПрдХ рд╕реВрдЪреА рд╣реИ

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
ndarray

XYWH рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдХрд╛ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХрд░рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдЦрдВрдбреЛрдВ (рдПрди, 2) рдХреА рдПрдХ рд╕реВрдЪреА рдЗрдирдкреБрдЯ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЦрдВрдбреЛрдВ рдХреА рдПрдХ рд╕реВрдЪреА (рдПрди, 2) рджреЗрддрд╛ рд╣реИ рдЬреЛ рдкреНрд░рддреНрдпреЗрдХ рдХреЛ рдПрди рдмрд┐рдВрджреБрдУрдВ рдкрд░ рдирдореВрдирд╛ рджреЗрддрд╛ рд╣реИред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
segments list

(рдПрди, 2) рд╕рд░рдгрд┐рдпреЛрдВ рдХреА рдПрдХ рд╕реВрдЪреА, рдЬрд╣рд╛рдВ рдПрди рдЦрдВрдб рдореЗрдВ рдмрд┐рдВрджреБрдУрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╣реИред

рдЖрд╡рд╢реНрдпрдХ
n int

рд╕реЗрдЧрдореЗрдВрдЯ рдХреЛ рдлрд┐рд░ рд╕реЗ рдирдореВрдирд╛ рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдВрдХреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ред 1000 рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ

1000

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
segments list

рдкреБрди: рдирдореВрдирд╛ рдЦрдВрдбред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдпрд╣ рдПрдХ рдореБрдЦреМрдЯрд╛ рдФрд░ рдПрдХ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рд▓реЗрддрд╛ рд╣реИ, рдФрд░ рдПрдХ рдореБрдЦреМрдЯрд╛ рд▓реМрдЯрд╛рддрд╛ рд╣реИ рдЬрд┐рд╕реЗ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдореЗрдВ рдХреНрд░реЙрдк рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
masks Tensor

[рдПрди, рдПрдЪ, рдбрдмреНрд▓реНрдпреВ] tensor рдорд╛рд╕реНрдХ рдХреА рд╕рдВрдЦреНрдпрд╛

рдЖрд╡рд╢реНрдпрдХ
boxes Tensor

[рдПрди, 4] tensor bbox рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХрд╛ рд╕рд╛рдкреЗрдХреНрд╖ рдмрд┐рдВрджреБ рд░реВрдк рдореЗрдВ

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
Tensor

рдорд╛рд╕реНрдХ рдХреЛ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдореЗрдВ рдХреНрд░реЙрдк рдХрд┐рдпрд╛ рдЬрд╛ рд░рд╣рд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдореБрдЦреМрдЯрд╛ рд╕рд┐рд░ рдХрд╛ рдЙрддреНрдкрд╛рджрди рд▓реЗрддрд╛ рд╣реИ, рдФрд░ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмрдХреНрд╕реЗ рдХреЗ рд▓рд┐рдП рдореБрдЦреМрдЯрд╛ рд▓рд╛рдЧреВ рд╣реЛрддрд╛ рд╣реИ. рдпрд╣ рдЙрдЪреНрдЪ рдЧреБрдгрд╡рддреНрддрд╛ рдХреЗ рдорд╛рд╕реНрдХ рдХрд╛ рдЙрддреНрдкрд╛рджрди рдХрд░рддрд╛ рд╣реИ рд▓реЗрдХрд┐рди рдзреАрдорд╛ рд╣реИред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
protos Tensor

[mask_dim, mask_h, mask_w]

рдЖрд╡рд╢реНрдпрдХ
masks_in Tensor

[рдПрди, mask_dim], рдПрди рдПрдирдПрдордПрд╕ рдХреЗ рдмрд╛рдж рдорд╛рд╕реНрдХ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╣реИ

рдЖрд╡рд╢реНрдпрдХ
bboxes Tensor

[рдПрди, 4], рдПрди рдПрдирдПрдордПрд╕ рдХреЗ рдмрд╛рдж рдорд╛рд╕реНрдХ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╣реИ

рдЖрд╡рд╢реНрдпрдХ
shape tuple

рдЗрдирдкреБрдЯ рдЫрд╡рд┐ рдХрд╛ рдЖрдХрд╛рд░ (H,W)

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
Tensor

рдКрдкрд░ рдЙрдареЗ рд╣реБрдП рдореБрдЦреМрдЯреЗред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдорд╛рд╕реНрдХ рд╕рд┐рд░ рдХреЗ рдЖрдЙрдЯрдкреБрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдкрд░ рдорд╛рд╕реНрдХ рд▓рдЧрд╛рдПрдВред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
protos Tensor

рдПрдХ tensor рдЖрдХрд╛рд░ рдХрд╛ [mask_dim, mask_h, mask_w]ред

рдЖрд╡рд╢реНрдпрдХ
masks_in Tensor

рдПрдХ tensor рдЖрдХрд╛рд░ рдХрд╛ [рдПрди, mask_dim], рдЬрд╣рд╛рдВ рдПрдирдПрдордПрд╕ рдХреЗ рдмрд╛рдж рдПрди рдорд╛рд╕реНрдХ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╣реИред

рдЖрд╡рд╢реНрдпрдХ
bboxes Tensor

рдПрдХ tensor рдЖрдХрд╛рд░ рдХрд╛ [рдПрди, 4], рдЬрд╣рд╛рдВ рдПрди рдПрдирдПрдордПрд╕ рдХреЗ рдмрд╛рдж рдорд╛рд╕реНрдХ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╣реИред

рдЖрд╡рд╢реНрдпрдХ
shape tuple

рдкреВрд░реНрдгрд╛рдВрдХреЛрдВ рдХрд╛ рдПрдХ рдЯрдкрд▓, рдЬреЛ рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдЗрдирдкреБрдЯ рдЫрд╡рд┐ рдХреЗ рдЖрдХрд╛рд░ рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рддрд╛ рд╣реИ (h, w).

рдЖрд╡рд╢реНрдпрдХ
upsample bool

рдпрд╣ рдЗрдВрдЧрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдзреНрд╡рдЬ рдХрд┐ рдХреНрдпрд╛ рдореБрдЦреМрдЯрд╛ рдХреЛ рдореВрд▓ рдЫрд╡рд┐ рдЖрдХрд╛рд░ рдореЗрдВ рдЕрдкрд╕реИрдореНрдкрд▓ рдХрд░рдирд╛ рд╣реИред рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдЧрд╝рд▓рдд рд╣реИ.

False

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
Tensor

рдПрдХ рдмрд╛рдЗрдирд░реА рдорд╛рд╕реНрдХ tensor рдЖрдХрд╛рд░ рдХрд╛ [рдПрди, рдПрдЪ, рдбрдмреНрд▓реНрдпреВ], рдЬрд╣рд╛рдВ рдПрдирдПрдордПрд╕ рдХреЗ рдмрд╛рдж рдПрди рдорд╛рд╕реНрдХ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╣реИ, рдФрд░ рдПрдЪ рдФрд░ рдбрдмреНрд▓реНрдпреВ рдЗрдирдкреБрдЯ рдЫрд╡рд┐ рдХреА рдКрдВрдЪрд╛рдИ рдФрд░ рдЪреМрдбрд╝рд╛рдИ рд╣реИрдВред рдорд╛рд╕реНрдХ рдХреЛ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдкрд░ рд▓рдЧрд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдпрд╣ рдореБрдЦреМрдЯрд╛ рд╕рд┐рд░ рдХрд╛ рдЙрддреНрдкрд╛рджрди рд▓реЗрддрд╛ рд╣реИ, рдФрд░ рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмрдХреНрд╕реЗ рдореЗрдВ рдЕрдкрд╕реИрдВрдкрд▓рд┐рдВрдЧ рдХреЗ рдмрд╛рдж рдЗрд╕реЗ рдлрд╕рд▓ рджреЗрддрд╛ рд╣реИред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
protos Tensor

[mask_dim, mask_h, mask_w]

рдЖрд╡рд╢реНрдпрдХ
masks_in Tensor

[рдПрди, mask_dim], рдПрди рдПрдирдПрдордПрд╕ рдХреЗ рдмрд╛рдж рдорд╛рд╕реНрдХ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╣реИ

рдЖрд╡рд╢реНрдпрдХ
bboxes Tensor

[рдПрди, 4], рдПрди рдПрдирдПрдордПрд╕ рдХреЗ рдмрд╛рдж рдорд╛рд╕реНрдХ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╣реИ

рдЖрд╡рд╢реНрдпрдХ
shape tuple

рдЗрдирдкреБрдЯ рдЫрд╡рд┐ рдХрд╛ рдЖрдХрд╛рд░ (H,W)

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
masks Tensor

рдЖрдпрд╛рдореЛрдВ рдХреЗ рд╕рд╛рде рд▓реМрдЯреЗ рд╣реБрдП рдорд╛рд╕реНрдХ [рдПрдЪ, рдбрдмреНрд▓реНрдпреВ, рдПрди]

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдЖрдХрд╛рд░ рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рд╕реЗрдЧрдореЗрдВрдЯ рдорд╛рд╕реНрдХ рдХреЛ рд░реАрд╕реНрдХреЗрд▓ рдХрд░реЗрдВред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
masks Tensor

(рдПрди, рд╕реА, рдПрдЪ, рдбрдмреНрд▓реНрдпреВ)ред

рдЖрд╡рд╢реНрдпрдХ
shape tuple

рдКрдВрдЪрд╛рдИ рдФрд░ рдЪреМрдбрд╝рд╛рдИред

рдЖрд╡рд╢реНрдпрдХ
padding bool

рдпрджрд┐ рд╕рд╣реА рд╣реИ, рддреЛ рдпрд╣ рдорд╛рдирддреЗ рд╣реБрдП рдХрд┐ рдмреЙрдХреНрд╕ рджреНрд╡рд╛рд░рд╛ рд╕рдВрд╡рд░реНрдзрд┐рдд рдЫрд╡рд┐ рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИ yolo рд╡рд░реНрддрд┐рдХрд╛ред рдЕрдЧрд░ рдЧрд▓рдд рд╣реИ рддреЛ рдирд┐рдпрдорд┐рдд рдХрд░реЗрдВ рдкреБрдирдГ рд╕реНрдХреЗрд▓рд┐рдВрдЧред

True
рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рд░реАрд╕реНрдХреЗрд▓ рд╕реЗрдЧрдореЗрдВрдЯ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ (xy) img1_shape рд╕реЗ img0_shape рддрдХред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
img1_shape tuple

рдЫрд╡рд┐ рдХрд╛ рдЖрдХрд╛рд░ рдЬреЛ рдХреЙрд░реНрдб рд╕реЗ рд╣реИрдВред

рдЖрд╡рд╢реНрдпрдХ
coords Tensor

рдХреЙрд░реНрдб рдХреЛ рдЖрдХрд╛рд░ рдПрди, 2 рдХреЗ рд╕реНрдХреЗрд▓ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рд╣реИред

рдЖрд╡рд╢реНрдпрдХ
img0_shape tuple

рдЫрд╡рд┐ рдХрд╛ рдЖрдХрд╛рд░ рдЬрд┐рд╕ рдкрд░ рд╡рд┐рднрд╛рдЬрди рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛ рд░рд╣рд╛ рд╣реИред

рдЖрд╡рд╢реНрдпрдХ
ratio_pad tuple

рдЫрд╡рд┐ рдЖрдХрд╛рд░ рдХрд╛ рдЧрджреНрджреЗрджрд╛рд░ рдЫрд╡рд┐ рдЖрдХрд╛рд░ рдХрд╛ рдЕрдиреБрдкрд╛рддред

None
normalize bool

рдпрджрд┐ рд╕рд╣реА рд╣реИ, рддреЛ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рд╕реАрдорд╛ [0, 1] рдХреЗ рд▓рд┐рдП рд╕рд╛рдорд╛рдиреНрдпреАрдХреГрдд рдХрд┐рдП рдЬрд╛рдПрдВрдЧреЗред рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдЧрд▓рдд рд╣реИ.

False
padding bool

рдпрджрд┐ рд╕рд╣реА рд╣реИ, рддреЛ рдпрд╣ рдорд╛рдирддреЗ рд╣реБрдП рдХрд┐ рдмреЙрдХреНрд╕ рджреНрд╡рд╛рд░рд╛ рд╕рдВрд╡рд░реНрдзрд┐рдд рдЫрд╡рд┐ рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИ yolo рд╡рд░реНрддрд┐рдХрд╛ред рдЕрдЧрд░ рдЧрд▓рдд рд╣реИ рддреЛ рдирд┐рдпрдорд┐рдд рдХрд░реЗрдВ рдкреБрдирдГ рд╕реНрдХреЗрд▓рд┐рдВрдЧред

True

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
coords Tensor

рд╕реНрдХреЗрд▓ рдХрд┐рдП рдЧрдП рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рдШреБрдорд╛рдП рдЧрдП рдмрдХреНрд╕реЛрдВ рдХреЛ рд╢реНрд░реЗрдгреА [0, pi/2] рдореЗрдВ рдирд┐рдпрдорд┐рдд рдХрд░реЗрдВред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
rboxes Tensor

(рдПрди, 5), xywhrред

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
Tensor

рдирд┐рдпрдорд┐рдд рдмрдХреНрд╕реЗред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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')

рдпрд╣ рдорд╛рд╕реНрдХ (рдПрди, рдПрдЪ, рдбрдмреНрд▓реНрдпреВ) рдХреА рдПрдХ рд╕реВрдЪреА рд▓реЗрддрд╛ рд╣реИ рдФрд░ рдЦрдВрдбреЛрдВ рдХреА рдПрдХ рд╕реВрдЪреА рджреЗрддрд╛ рд╣реИ (рдПрди, рдПрдХреНрд╕рд╡рд╛рдИ)

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
masks Tensor

рдореЙрдбрд▓ рдХрд╛ рдЖрдЙрдЯрдкреБрдЯ, рдЬреЛ рдПрдХ tensor рдЖрдХрд╛рд░ рдХреА (batch_size, 160, 160)

рдЖрд╡рд╢реНрдпрдХ
strategy str

'рдХреЙрдирдХреИрдЯ' рдпрд╛ 'рд╕рдмрд╕реЗ рдмрдбрд╝рд╛'ред рд╕рдмрд╕реЗ рдмрдбрд╝рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЪреВрдХ

'largest'

рджреЗрддрд╛:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
segments List

рд╕реЗрдЧрдореЗрдВрдЯ рдорд╛рд╕реНрдХ рдХреА рд╕реВрдЪреА

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

FP32 рдХреЗ рдПрдХ рдмреИрдЪ рдХреЛ рдХрдиреНрд╡рд░реНрдЯ рдХрд░реЗрдВ torch рдЯреЗрдВрд╕рд░ (0.0-1.0) рд╕реЗ NumPy uint8 рд╕рд░рдгреА (0-255), BCHW рд╕реЗ BHWC рд▓реЗрдЖрдЙрдЯ рдореЗрдВ рдмрджрд▓ рд░рд╣рд╛ рд╣реИред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
batch Tensor

рдирд┐рд╡реЗрд╢ tensor рдЖрдХрд╛рд░ рдХрд╛ рдмреИрдЪ (рдмреИрдЪ, рдЪреИрдирд▓, рдКрдВрдЪрд╛рдИ, рдЪреМрдбрд╝рд╛рдИ) рдФрд░ dtype torch.float32.

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
ndarray

рдЖрдЙрдЯрдкреБрдЯ NumPy рдЖрдХрд╛рд░ рдХрд╛ рд╕рд░рдгреА рдмреИрдЪ (рдмреИрдЪ, рдКрдВрдЪрд╛рдИ, рдЪреМрдбрд╝рд╛рдИ, рдЪреИрдирд▓) рдФрд░ dtype uint8.

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)

рд╡рд┐рд╢реЗрд╖ рд╡рд░реНрдгреЛрдВ рдХреЛ рдЕрдВрдбрд░рд╕реНрдХреЛрд░ _ рд╕реЗ рдмрджрд▓рдХрд░ рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╕рд╛рдлрд╝ рдХрд░рддрд╛ рд╣реИ

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
s str

рдПрдХ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдЬрд┐рд╕реЗ рд╡рд┐рд╢реЗрд╖ рд╡рд░реНрдгреЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
str

рд╡рд┐рд╢реЗрд╖ рд╡рд░реНрдгреЛрдВ рд╡рд╛рд▓реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдЬрд┐рд╕реЗ рдЕрдВрдбрд░рд╕реНрдХреЛрд░ _ рд╕реЗ рдмрджрд▓рд╛ рдЬрд╛рддрд╛ рд╣реИ

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб 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)





2023-11-12 рдмрдирд╛рдпрд╛ рдЧрдпрд╛, рдЕрдкрдбреЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ 2024-05-08
рд▓реЗрдЦрдХ: рдмреБрд░рд╣рд╛рди-рдХреНрдпреВ (1), рд▓рд╛рдлрд┐рдВрдЧ-рдХреНрдпреВ (1), рдЧреНрд▓реЗрди-рдЬреЛрдЪрд░ (4)