Skip to content

points

sleap_io.transform.points

Point coordinate transformation functions.

This module provides functions for transforming landmark coordinates to match transformed video frames. Each function corresponds to a geometric transformation (crop, scale, rotate, pad) and adjusts coordinates accordingly.

Functions:

Name Description
count_out_of_bounds

Count how many points fall outside the given bounds.

crop_points

Adjust point coordinates for a crop transformation.

get_out_of_bounds_mask

Get a boolean mask indicating which points are outside bounds.

pad_points

Adjust point coordinates for a pad transformation.

rotate_points

Adjust point coordinates for a rotation transformation.

scale_points

Adjust point coordinates for a scale transformation.

transform_points

Transform point coordinates using an affine matrix.

uncrop_points

Map crop-local point coordinates back to source coordinates.

Attributes:

Name Type Description
__cached__

str(object='') -> str

__doc__

str(object='') -> str

__file__

str(object='') -> str

__name__

str(object='') -> str

__package__

str(object='') -> str

__cached__ = '/home/runner/work/sleap-io/sleap-io/sleap_io/transform/__pycache__/points.cpython-313.pyc' module-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__doc__ = 'Point coordinate transformation functions.\n\nThis module provides functions for transforming landmark coordinates to match\ntransformed video frames. Each function corresponds to a geometric transformation\n(crop, scale, rotate, pad) and adjusts coordinates accordingly.\n' module-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__file__ = '/home/runner/work/sleap-io/sleap-io/sleap_io/transform/points.py' module-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__name__ = 'sleap_io.transform.points' module-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

__package__ = 'sleap_io.transform' module-attribute

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

count_out_of_bounds(points, bounds)

Count how many points fall outside the given bounds.

Parameters:

Name Type Description Default
points ndarray

Coordinate array of shape (..., 2) where the last dimension contains (x, y) coordinates.

required
bounds tuple[int, int, int, int]

Bounds as (x_min, y_min, x_max, y_max).

required

Returns:

Type Description
int

Number of points outside bounds (excluding NaN points).

Source code in sleap_io/transform/points.py
def count_out_of_bounds(
    points: np.ndarray,
    bounds: tuple[int, int, int, int],
) -> int:
    """Count how many points fall outside the given bounds.

    Args:
        points: Coordinate array of shape (..., 2) where the last dimension
            contains (x, y) coordinates.
        bounds: Bounds as (x_min, y_min, x_max, y_max).

    Returns:
        Number of points outside bounds (excluding NaN points).
    """
    x_min, y_min, x_max, y_max = bounds

    # Flatten to (n, 2)
    flat_points = points.reshape(-1, 2)

    # Mask for valid (non-NaN) points
    valid_mask = ~np.isnan(flat_points).any(axis=-1)

    if not valid_mask.any():
        return 0

    valid_points = flat_points[valid_mask]

    out_of_bounds = (
        (valid_points[:, 0] < x_min)
        | (valid_points[:, 0] >= x_max)
        | (valid_points[:, 1] < y_min)
        | (valid_points[:, 1] >= y_max)
    )

    return int(out_of_bounds.sum())

crop_points(points, crop)

Adjust point coordinates for a crop transformation.

Parameters:

Name Type Description Default
points ndarray

Coordinate array of shape (..., 2) where the last dimension contains (x, y) coordinates. NaN values are preserved.

required
crop tuple[int, int, int, int]

Crop region as (x1, y1, x2, y2) pixel coordinates.

required

Returns:

Type Description
ndarray

Adjusted coordinates with same shape as input.

Source code in sleap_io/transform/points.py
def crop_points(
    points: np.ndarray,
    crop: tuple[int, int, int, int],
) -> np.ndarray:
    """Adjust point coordinates for a crop transformation.

    Args:
        points: Coordinate array of shape (..., 2) where the last dimension
            contains (x, y) coordinates. NaN values are preserved.
        crop: Crop region as (x1, y1, x2, y2) pixel coordinates.

    Returns:
        Adjusted coordinates with same shape as input.
    """
    x1, y1, x2, y2 = crop
    result = points.copy()
    result[..., 0] = points[..., 0] - x1
    result[..., 1] = points[..., 1] - y1
    return result

get_out_of_bounds_mask(points, bounds)

Get a boolean mask indicating which points are outside bounds.

Parameters:

Name Type Description Default
points ndarray

Coordinate array of shape (n_points, 2) where each row is (x, y).

required
bounds tuple[int, int, int, int]

Bounds as (x_min, y_min, x_max, y_max).

required

Returns:

Type Description
ndarray

Boolean array of shape (n_points,) where True indicates the point is out of bounds. NaN points are considered in bounds (not marked as OOB).

Source code in sleap_io/transform/points.py
def get_out_of_bounds_mask(
    points: np.ndarray,
    bounds: tuple[int, int, int, int],
) -> np.ndarray:
    """Get a boolean mask indicating which points are outside bounds.

    Args:
        points: Coordinate array of shape (n_points, 2) where each row is (x, y).
        bounds: Bounds as (x_min, y_min, x_max, y_max).

    Returns:
        Boolean array of shape (n_points,) where True indicates the point is
        out of bounds. NaN points are considered in bounds (not marked as OOB).
    """
    x_min, y_min, x_max, y_max = bounds

    # Mask for valid (non-NaN) points
    valid_mask = ~np.isnan(points).any(axis=-1)

    # Initialize result - NaN points are not considered OOB
    oob_mask = np.zeros(len(points), dtype=bool)

    if valid_mask.any():
        valid_points = points[valid_mask]
        oob = (
            (valid_points[:, 0] < x_min)
            | (valid_points[:, 0] >= x_max)
            | (valid_points[:, 1] < y_min)
            | (valid_points[:, 1] >= y_max)
        )
        oob_mask[valid_mask] = oob

    return oob_mask

pad_points(points, padding)

Adjust point coordinates for a pad transformation.

Parameters:

Name Type Description Default
points ndarray

Coordinate array of shape (..., 2) where the last dimension contains (x, y) coordinates. NaN values are preserved.

required
padding tuple[int, int, int, int]

Padding as (top, right, bottom, left) in pixels.

required

Returns:

Type Description
ndarray

Adjusted coordinates with same shape as input.

Source code in sleap_io/transform/points.py
def pad_points(
    points: np.ndarray,
    padding: tuple[int, int, int, int],
) -> np.ndarray:
    """Adjust point coordinates for a pad transformation.

    Args:
        points: Coordinate array of shape (..., 2) where the last dimension
            contains (x, y) coordinates. NaN values are preserved.
        padding: Padding as (top, right, bottom, left) in pixels.

    Returns:
        Adjusted coordinates with same shape as input.
    """
    top, right, bottom, left = padding
    result = points.copy()
    result[..., 0] = points[..., 0] + left
    result[..., 1] = points[..., 1] + top
    return result

rotate_points(points, angle, center)

Adjust point coordinates for a rotation transformation.

Points are rotated clockwise about the specified center.

Parameters:

Name Type Description Default
points ndarray

Coordinate array of shape (..., 2) where the last dimension contains (x, y) coordinates. NaN values are preserved.

required
angle float

Rotation angle in degrees. Positive is clockwise.

required
center tuple[float, float]

Center of rotation as (cx, cy).

required

Returns:

Type Description
ndarray

Adjusted coordinates with same shape as input.

Source code in sleap_io/transform/points.py
def rotate_points(
    points: np.ndarray,
    angle: float,
    center: tuple[float, float],
) -> np.ndarray:
    """Adjust point coordinates for a rotation transformation.

    Points are rotated clockwise about the specified center.

    Args:
        points: Coordinate array of shape (..., 2) where the last dimension
            contains (x, y) coordinates. NaN values are preserved.
        angle: Rotation angle in degrees. Positive is clockwise.
        center: Center of rotation as (cx, cy).

    Returns:
        Adjusted coordinates with same shape as input.
    """
    if angle == 0:
        return points.copy()

    cx, cy = center
    angle_rad = np.radians(angle)
    cos_a = np.cos(angle_rad)
    sin_a = np.sin(angle_rad)

    # Translate to origin
    dx = points[..., 0] - cx
    dy = points[..., 1] - cy

    # Rotate (clockwise: positive angle)
    # x' = x*cos(a) + y*sin(a)
    # y' = -x*sin(a) + y*cos(a)
    result = points.copy()
    result[..., 0] = dx * cos_a + dy * sin_a + cx
    result[..., 1] = -dx * sin_a + dy * cos_a + cy

    return result

scale_points(points, scale)

Adjust point coordinates for a scale transformation.

Parameters:

Name Type Description Default
points ndarray

Coordinate array of shape (..., 2) where the last dimension contains (x, y) coordinates. NaN values are preserved.

required
scale tuple[float, float]

Scale factors as (scale_x, scale_y).

required

Returns:

Type Description
ndarray

Adjusted coordinates with same shape as input.

Source code in sleap_io/transform/points.py
def scale_points(
    points: np.ndarray,
    scale: tuple[float, float],
) -> np.ndarray:
    """Adjust point coordinates for a scale transformation.

    Args:
        points: Coordinate array of shape (..., 2) where the last dimension
            contains (x, y) coordinates. NaN values are preserved.
        scale: Scale factors as (scale_x, scale_y).

    Returns:
        Adjusted coordinates with same shape as input.
    """
    scale_x, scale_y = scale
    result = points.copy()
    result[..., 0] = points[..., 0] * scale_x
    result[..., 1] = points[..., 1] * scale_y
    return result

transform_points(points, matrix)

Transform point coordinates using an affine matrix.

Parameters:

Name Type Description Default
points ndarray

Coordinate array of shape (n_points, 2) where each row is (x, y). NaN values are preserved.

required
matrix ndarray

3x3 affine transformation matrix.

required

Returns:

Type Description
ndarray

Transformed coordinates with same shape as input.

Source code in sleap_io/transform/points.py
def transform_points(
    points: np.ndarray,
    matrix: np.ndarray,
) -> np.ndarray:
    """Transform point coordinates using an affine matrix.

    Args:
        points: Coordinate array of shape (n_points, 2) where each row is (x, y).
            NaN values are preserved.
        matrix: 3x3 affine transformation matrix.

    Returns:
        Transformed coordinates with same shape as input.
    """
    if points.size == 0:
        return points.copy()

    result = points.copy()

    # Create mask for valid (non-NaN) points
    valid_mask = ~np.isnan(points).any(axis=-1)

    if valid_mask.any():
        # Convert to homogeneous coordinates
        valid_points = points[valid_mask]
        ones = np.ones((valid_points.shape[0], 1), dtype=np.float64)
        homogeneous = np.hstack([valid_points, ones])

        # Apply transformation
        transformed = (matrix @ homogeneous.T).T

        # Extract x, y from homogeneous coordinates
        result[valid_mask, 0] = transformed[:, 0]
        result[valid_mask, 1] = transformed[:, 1]

    return result

uncrop_points(points, crop)

Map crop-local point coordinates back to source coordinates.

Inverse of :func:crop_points: maps crop-local (x, y) coordinates back to source coordinates by adding the crop origin (x1, y1).

Parameters:

Name Type Description Default
points ndarray

Coordinate array of shape (..., 2) where the last dimension contains (x, y) coordinates. NaN values are preserved.

required
crop tuple[int, int, int, int]

Crop region as (x1, y1, x2, y2) pixel coordinates.

required

Returns:

Type Description
ndarray

Adjusted coordinates with same shape as input.

Source code in sleap_io/transform/points.py
def uncrop_points(
    points: np.ndarray,
    crop: tuple[int, int, int, int],
) -> np.ndarray:
    """Map crop-local point coordinates back to source coordinates.

    Inverse of :func:`crop_points`: maps crop-local (x, y) coordinates back to
    source coordinates by adding the crop origin (x1, y1).

    Args:
        points: Coordinate array of shape (..., 2) where the last dimension
            contains (x, y) coordinates. NaN values are preserved.
        crop: Crop region as (x1, y1, x2, y2) pixel coordinates.

    Returns:
        Adjusted coordinates with same shape as input.
    """
    x1, y1, x2, y2 = crop
    result = points.copy()
    result[..., 0] = points[..., 0] + x1
    result[..., 1] = points[..., 1] + y1
    return result