Skip to content

polygon_function

data.transform.polygon_function

PolygonType module-attribute

PolygonType = List[ndarray]

OffsetType module-attribute

OffsetType = Tuple[float, float]

calculate_transform_matrix

calculate_transform_matrix(width: int, height: int, offset_top_left: OffsetType, offset_top_right: OffsetType, offset_bottom_right: OffsetType, offset_bottom_left: OffsetType) -> ndarray

Calculates the perspective transformation matrix using the given width, height, and corner points of a rectangle.

Parameters:

  • width (int) –

    Width of the original image.

  • height (int) –

    Height of the original image.

  • offset_top_left (OffsetType) –

    The offset ratio of the top-left corner point. Represented by the coordinates (x, y) and has a range of [0, 49]. Calculate xand y according to the procedure below.

    x` = width * offset_top_left[0]
    y` = height * offset_top_left[1]
    
  • offset_bottom_right (OffsetType) –

    The offset ratio of the bottom-right corner point. Calculate xand y according to the procedure below.

    x` = width - width * offset_bottom_right[0]
    y` = hegiht - height * offset_bottom_right[1]
    
  • offset_top_right (OffsetType) –

    The offset ratio of the top-left corner point.

  • offset_bottom_left (OffsetType) –

    The offset ratio of the bottom-left corner point.

Returns:

  • ndarray

    np.ndarray: The 4x3 perspective transform matrix.

Source code in SaigeToolkit/data/transform/augmentation/augment_function.py
def calculate_transform_matrix(
    width: int,
    height: int,
    offset_top_left: OffsetType,
    offset_top_right: OffsetType,
    offset_bottom_right: OffsetType,
    offset_bottom_left: OffsetType,
) -> np.ndarray:
    """
    Calculates the perspective transformation matrix
    using the given width, height, and corner points of a rectangle.

    Parameters:
        width (int): Width of the original image.
        height (int): Height of the original image.
        offset_top_left (OffsetType):
            The offset ratio of the top-left corner point.
            Represented by the coordinates (x, y) and has a range of [0, 49].
            Calculate x` and y` according to the procedure below.

            ```
            x` = width * offset_top_left[0]
            y` = height * offset_top_left[1]
            ```

        offset_bottom_right (OffsetType):
            The offset ratio of the bottom-right corner point.
            Calculate x` and y` according to the procedure below.

            ```
            x` = width - width * offset_bottom_right[0]
            y` = hegiht - height * offset_bottom_right[1]
            ```

        offset_top_right (OffsetType): The offset ratio of the top-left corner point.
        offset_bottom_left (OffsetType): The offset ratio of the bottom-left corner point.

    Returns:
        np.ndarray: The 4x3 perspective transform matrix.

    """
    points_from = np.float32([[0, 0], [width, 0], [width, height], [0, height]])

    point_top_left: OffsetType = (
        (offset_top_left[0] / 100) * width,
        (offset_top_left[1] / 100) * height,
    )
    point_top_right: OffsetType = (
        width - (offset_top_right[0] / 100) * width,
        (offset_top_right[1] / 100) * height,
    )
    point_bottom_right: OffsetType = (
        width - (offset_bottom_right[0] / 100) * width,
        height - (offset_bottom_right[1] / 100) * height,
    )
    point_bottom_left: OffsetType = (
        (offset_bottom_left[0] / 100) * width,
        height - (offset_bottom_left[1] / 100) * height,
    )
    points_to = np.float32(
        [
            point_top_left,
            point_top_right,
            point_bottom_right,
            point_bottom_left,
        ]
    )
    transform_matrix = cv2.getPerspectiveTransform(points_from, points_to)

    return transform_matrix

check_value

check_value(data: Union[int, float], min_value: Union[int, float], max_value: Union[int, float])
Source code in SaigeToolkit/data/transform/function_util.py
def check_value(data: Union[int, float], min_value: Union[int, float], max_value: Union[int, float]):
    if not (isinstance(data, (int, float))):
        raise AugmentationParameterTypeError

    if not (min_value <= data <= max_value):
        raise AugmentationParameterRangeError

vertical_flip

vertical_flip(polygons: PolygonType, image_size: Tuple[int]) -> PolygonType
Source code in SaigeToolkit/data/transform/polygon_function.py
def vertical_flip(polygons: PolygonType, image_size: Tuple[int]) -> PolygonType:
    _, h = image_size
    polygons = deepcopy(polygons)
    for polygon in polygons:
        polygon[:, 1] = h - polygon[:, 1]
    return polygons

horizontal_flip

horizontal_flip(polygons: PolygonType, image_size: Tuple[int]) -> PolygonType
Source code in SaigeToolkit/data/transform/polygon_function.py
def horizontal_flip(polygons: PolygonType, image_size: Tuple[int]) -> PolygonType:
    w, _ = image_size
    polygons = deepcopy(polygons)
    for polygon in polygons:
        polygon[:, 0] = w - polygon[:, 0]
    return polygons

rotate

rotate(polygons: PolygonType, angle: float, image_size: Tuple[int], target_size: Optional[Tuple[int]] = None) -> PolygonType
Source code in SaigeToolkit/data/transform/polygon_function.py
def rotate(
    polygons: PolygonType,
    angle: float,
    image_size: Tuple[int],
    target_size: Optional[Tuple[int]] = None,
) -> PolygonType:
    if len(polygons) == 0:
        return polygons

    dtype = polygons[0].dtype
    w, h = image_size

    anchor_origin = np.array([w // 2, h // 2]).astype(dtype)

    if target_size is None:
        anchor_target = anchor_origin
    else:
        anchor_target = np.array([target_size[0] // 2, target_size[1] // 2]).astype(dtype)

    theta = np.deg2rad(angle)
    rotation_matrix = [[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]

    rotated_polygons = []
    for polygon in polygons:
        # Translate polygon so that the rotation point is at the origin
        rotated_polygon = polygon - anchor_origin

        # Rotate the polygon
        rotated_polygon = np.dot(rotated_polygon, rotation_matrix)

        # Translate the polygon back
        rotated_polygon += anchor_target

        rotated_polygons.append(rotated_polygon)

    return rotated_polygons

rotate90

rotate90(polygons: PolygonType, factor: int, image_size: Tuple[int]) -> PolygonType
Source code in SaigeToolkit/data/transform/polygon_function.py
def rotate90(
    polygons: PolygonType,
    factor: int,
    image_size: Tuple[int],
) -> PolygonType:
    angle = factor * 90.0
    if factor % 2 == 0:
        target_size = image_size
    else:
        target_size = (image_size[1], image_size[0])
    return rotate(polygons=polygons, angle=angle, image_size=image_size, target_size=target_size)

ratio_jitter

ratio_jitter(polygons: PolygonType, image_size: Tuple[int], proportion_left: float, proportion_right: float, proportion_top: float, proportion_bottom: float) -> PolygonType
Source code in SaigeToolkit/data/transform/polygon_function.py
def ratio_jitter(
    polygons: PolygonType,
    image_size: Tuple[int],
    proportion_left: float,
    proportion_right: float,
    proportion_top: float,
    proportion_bottom: float,
) -> PolygonType:
    w, h = image_size

    left = int(w * proportion_left)
    right = int(w * proportion_right)
    top = int(h * proportion_top)
    bottom = int(h * proportion_bottom)

    # Crop
    crop_left = left if left > 0 else 0
    crop_right = right if right > 0 else 0
    crop_top = top if top > 0 else 0
    crop_bottom = bottom if bottom > 0 else 0

    crop_w = max(w - crop_left - crop_right, 1)
    crop_h = max(h - crop_top - crop_bottom, 1)
    polygons = translate_polygon(polygons, [crop_left, crop_top])

    # Padding
    pad_left = 0 if left > 0 else -left
    pad_right = 0 if right > 0 else -right
    pad_top = 0 if top > 0 else -top
    pad_bottom = 0 if bottom > 0 else -bottom

    polygons = resize_polygon(
        polygons, (crop_w + pad_left + pad_right, crop_h + pad_top + pad_bottom), w, h
    )

    # Translate
    w_scale = (crop_w + pad_left + pad_right) / w
    h_scale = (crop_h + pad_top + pad_bottom) / h
    polygons = translate_polygon(polygons, [-int(pad_left / w_scale), -int(pad_top / h_scale)])

    return polygons

zoom

zoom(polygons: PolygonType, image_size: Tuple[int], ratio: float, h_start: float, w_start: float) -> PolygonType
Source code in SaigeToolkit/data/transform/polygon_function.py
def zoom(
    polygons: PolygonType,
    image_size: Tuple[int],
    ratio: float,
    h_start: float,
    w_start: float,
) -> PolygonType:
    w, h = image_size

    if ratio == 1.0:
        return polygons

    target_size = (int(w * ratio), int(h * ratio))
    polygons = resize_polygon(polygons, image_size, target_size[0], target_size[1])

    x1 = 0
    y1 = 0

    if ratio > 1.0:
        h_start = min(h_start, 1.0 - 1e-5)
        w_start = min(w_start, 1.0 - 1e-5)
        x1 = int((target_size[0] - w + 1) * w_start)
        y1 = int((target_size[1] - h + 1) * h_start)

    elif ratio < 1.0:
        x1 = -int((w - target_size[0] + 1) * w_start)
        y1 = -int((h - target_size[1] + 1) * h_start)

    polygons = translate_polygon(polygons, [x1, y1])

    return polygons

random_resized_crop_and_pad

random_resized_crop_and_pad(polygons: PolygonType, image_size: Tuple[int], scale: float, aspect_ratio: float, h_start: float, w_start: float, h_target: Optional[int] = None, w_target: Optional[int] = None, **params) -> PolygonType
Source code in SaigeToolkit/data/transform/polygon_function.py
def random_resized_crop_and_pad(
    polygons: PolygonType,
    image_size: Tuple[int],
    scale: float,
    aspect_ratio: float,
    h_start: float,
    w_start: float,
    h_target: Optional[int] = None,
    w_target: Optional[int] = None,
    **params
) -> PolygonType:
    w_original, h_original = image_size
    area = h_original * w_original
    target_area = scale * area

    crop_height = int(round(math.sqrt(target_area / aspect_ratio)))
    crop_width = int(round(math.sqrt(target_area * aspect_ratio)))

    # Padding
    pad_top = max(crop_height - h_original, 0)
    pad_bottom = max(crop_height - h_original, 0)
    pad_left = max(crop_width - w_original, 0)
    pad_right = max(crop_width - w_original, 0)

    padded_image_size = [w_original + pad_left + pad_right, h_original + pad_top + pad_bottom]
    polygons = translate_polygon(
        polygons,
        [-(pad_left), -(pad_top)],
    )

    # Crop
    x1 = int((padded_image_size[0] - crop_width + 1) * w_start)
    y1 = int((padded_image_size[1] - crop_height + 1) * h_start)
    polygons = translate_polygon(polygons, [x1, y1])

    # Resize
    polygons = resize_polygon(
        polygons,
        [crop_width, crop_height],
        w_target or w_original,
        h_target or h_original,
    )
    return polygons

perspective_transform

perspective_transform(polygons: PolygonType, image_size: Tuple[int], offset_top_left: OffsetType, offset_top_right: OffsetType, offset_bottom_right: OffsetType, offset_bottom_left: OffsetType) -> PolygonType
Source code in SaigeToolkit/data/transform/polygon_function.py
def perspective_transform(
    polygons: PolygonType,
    image_size: Tuple[int],
    offset_top_left: OffsetType,
    offset_top_right: OffsetType,
    offset_bottom_right: OffsetType,
    offset_bottom_left: OffsetType,
) -> PolygonType:
    width, height = image_size

    for point_offset in [
        offset_top_left,
        offset_top_right,
        offset_bottom_right,
        offset_bottom_left,
    ]:
        offset_x, offset_y = point_offset

        check_value(offset_x, 0, 49)
        check_value(offset_y, 0, 49)

    transform_matrix = calculate_transform_matrix(
        width=width,
        height=height,
        offset_top_left=offset_top_left,
        offset_top_right=offset_top_right,
        offset_bottom_right=offset_bottom_right,
        offset_bottom_left=offset_bottom_left,
    )
    new_polygons = deepcopy(polygons)
    for idx_poly, polygon in enumerate(polygons):
        for idx_pt, point in enumerate(polygon):
            x, y, scaling_factor = np.dot(transform_matrix, np.append(point, 1))

            new_polygons[idx_poly][idx_pt][0] = int(x / scaling_factor)
            new_polygons[idx_poly][idx_pt][1] = int(y / scaling_factor)

    return new_polygons

resize_polygon

resize_polygon(polygons: PolygonType, image_size: Tuple[int], tw: int, th: int) -> PolygonType

Resize polygon from (w, h) to (tw, th)

Source code in SaigeToolkit/data/transform/polygon_function.py
def resize_polygon(polygons: PolygonType, image_size: Tuple[int], tw: int, th: int) -> PolygonType:
    """Resize polygon from (w, h) to (tw, th)"""
    if len(polygons) == 0:
        return polygons

    w, h = image_size
    if w == tw and h == th:
        return polygons

    dtype = polygons[0].dtype

    w_scale = tw / w
    h_scale = th / h

    new_polygons = deepcopy(polygons)

    for polygon in new_polygons:
        polygon[:, 0] = polygon[:, 0] * w_scale
        polygon[:, 1] = polygon[:, 1] * h_scale
        polygon = polygon.astype(dtype)

    return new_polygons

translate_polygon

translate_polygon(polygons: PolygonType, offset: Tuple[int]) -> PolygonType

Translate polyfon from [(x1, y1), ... ] to [(x1 - x_offset), (y1 - y_offset), ...]

Source code in SaigeToolkit/data/transform/polygon_function.py
def translate_polygon(
    polygons: PolygonType,
    offset: Tuple[int],
) -> PolygonType:
    """Translate polyfon from [(x1, y1), ... ] to [(x1 - x_offset), (y1 - y_offset), ...]"""
    if len(polygons) == 0:
        return polygons

    if offset[0] == 0 and offset[1] == 0:
        return polygons

    dtype = polygons[0].dtype

    x_offset, y_offset = np.array(offset).astype(dtype)

    new_polygons = deepcopy(polygons)

    for polygon in new_polygons:
        polygon[:, 0] -= x_offset
        polygon[:, 1] -= y_offset

    return new_polygons