def align_and_overlay_images(
    img1: np.ndarray,
    img2: np.ndarray,
    mask1: np.ndarray,
    mask2: np.ndarray,
    angle: float = 0.0,
    ratio: float = 1.0,
    dx: float = 0.0,
    dy: float = 0.0,
    box2: Optional[Tuple[int, int, int, int]] = None,
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, float]:
    """
    Paste img1 to img2 with angle, ratio and mask, while considering optional transformations.

    Args:
        img1 (np.ndarray): The first input image.
        img2 (np.ndarray): The second input image.
        mask1 (np.ndarray): The mask corresponding to img1.
        mask2 (np.ndarray): The mask corresponding to img2.
        angle (float, optional): The rotation angle for img1 (default is 0.0).
        ratio (float, optional): The scaling ratio for img1 (default is 1.0).
        dx (float, otional): the pixel of horizontal move for img1 (default is 0.0).
        dy (float, otional): the pixel of vertical move for img1 (default is 0.0).
        box2 (Optional[Tuple[int, int, int, int]]): The bounding box for ROI in img2 (left, upper, right, lower) (default is None).

    Returns:
        Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, float]:
            - The resulting overlay image.
            - The processed img1.
            - The processed mask1.
            - The mask2.
            - The mask2 used in the final overlay.
            - The Intersection over Union (IoU) value.
    """

    def calculate_box_center(box):
        return np.array((int((box[0] + box[2]) / 2), int((box[1] + box[3]) / 2)))

    def crop_and_paste(
        img1: np.ndarray,
        mask1: np.ndarray,
        img2: np.ndarray,
        mask2: np.ndarray,
        angle: float,
        x: float,
        y: float,
        dx: float,
        dy: float,
        ratio: float,
    ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, float]:
        """
        Crop and paste one image onto another image while considering rotation, resizing, and color fill.

        Args:
            img1 (np.ndarray): The first input image.
            mask1 (np.ndarray): The mask corresponding to img1.
            img2 (np.ndarray): The second input image.
            mask2 (np.ndarray): The mask corresponding to img2.
            angle (float): The rotation angle for img1.
            x (float): The x-coordinate of the center point for transformation.
            y (float): The y-coordinate of the center point for transformation.
            dx (float, otional): the pixel of horizontal move for img1 (default is 0.0).
            dy (float, otional): the pixel of vertical move for img1 (default is 0.0).
            ratio (float): The scaling ratio for img1.

        Returns:
            Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, float]:
                - The resulting merged image.
                - The processed img1.
                - The processed img2.
                - The processed mask1.
                - The processed mask2.
                - The Intersection over Union (IoU) value.
        """
        # Rotate and resize img1 and mask1
        img1 = rotate_resize_image(img1, angle, ratio, dx, dy)
        mask1 = rotate_resize_image(mask1, angle, ratio, dx, dy)

        # Paste img1 onto img2, considering masks
        result_img, img1, img2, mask1, mask2, iou = merge_images(img1, img2, mask1, mask2, x, y)

        return result_img, img1, img2, mask1, mask2, iou

    def rotate_resize_image(
        array: np.ndarray,
        angle: float = 0.0,
        scale_ratio: float = 1.0,
        dx: float = 0.0,
        dy: float = 0.0,
        use_white_bg: Optional[bool] = False,
    ) -> np.ndarray:
        """
        Rotate and resize an image while optionally setting excess border to white.

        Args:
            array (np.ndarray): The input image.
            angle (float, optional): The rotation angle in degrees (default is 0.0).
            scale_ratio (float, optional): The scaling ratio (default is 1.0).
            dx (float, optional): The translation along the x-axis (default is 0.0).
            dy (float, optional): The translation along the y-axis (default is 0.0).
            use_white_bg (bool, optional): Whether to set the excess border to white (default is False).

        Returns:
            np.ndarray: The rotated and resized image.
        """
        height, width = array.shape[:2]
        image_center = (width / 2, height / 2)

        rotation_mat = cv2.getRotationMatrix2D(image_center, angle, scale_ratio)

        # Calculate the size of the new image to accommodate the rotated image
        radians = math.radians(angle)
        sin = math.sin(radians)
        cos = math.cos(radians)
        bound_w = int((height * abs(sin)) + (width * abs(cos)))
        bound_h = int((height * abs(cos)) + (width * abs(sin)))

        # Adjust the rotation matrix to translate to the center of the new image
        rotation_mat[0, 2] += (bound_w - width) / 2
        rotation_mat[1, 2] += (bound_h - height) / 2

        rotated_mat = cv2.warpAffine(array, rotation_mat, (bound_w, bound_h))

        # Apply the final padding (dx and dy)
        pad_left = max(0, int(dx))
        pad_right = max(0, -int(dx))
        pad_top = max(0, int(dy))
        pad_down = max(0, -int(dy))
        rotated_mat = cv2.copyMakeBorder(rotated_mat, pad_top, pad_down, pad_left, pad_right, cv2.BORDER_CONSTANT, value=0)

        if use_white_bg:
            # Create a white background with the same size
            white_bg = np.zeros_like(rotated_mat)
            white_bg.fill(255)  # Set the background to white

            # Combine the rotated image and the white background using a mask
            mask = rotated_mat == 0
            rotated_mat = np.where(mask, white_bg, rotated_mat)

        return rotated_mat

    def merge_images(
        img1: np.ndarray,
        img2: np.ndarray,
        mask1: np.ndarray,
        mask2: np.ndarray,
        x: float,
        y: float,
    ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, float]:
        """
        Paste img1 onto img2 with a specified center point (x, y), filter the result by mask1 and mask2, and calculate IoU.

        Args:
            img1 (np.ndarray): The first input image to be pasted.
            img2 (np.ndarray): The second input image.
            mask1 (np.ndarray): The mask corresponding to img1.
            mask2 (np.ndarray): The mask corresponding to img2.
            x (float): The x-coordinate of the center point for pasting.
            y (float): The y-coordinate of the center point for pasting.

        Returns:
            Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, float]:
                - The resulting merged image.
                - The processed img1.
                - The processed img2.
                - The processed mask1.
                - The processed mask2.
                - The Intersection over Union (IoU) value.
        """
        h1, w1 = img1.shape[:2]
        h2, w2 = img2.shape[:2]

        h_max, w_max = max(h1, h2), max(w1, w2)

        # Create large arrays for the final result and intermediate steps
        expand_img1 = np.zeros((h_max, w_max, 3))
        expand_img2 = np.zeros((h_max, w_max, 3))
        result_img = np.zeros((h_max, w_max, 3))
        expand_mask1 = np.zeros((h_max, w_max))
        expand_mask2 = np.zeros((h_max, w_max))

        # Merge masks to determine where to use img1 and where to use img2
        merge_mask = np.zeros((h_max, w_max))

        # Paste images and masks to the center of the larger arrays
        expand_img1 = paste_image_center(img1, expand_img1)
        expand_img2 = paste_image_center(img2, expand_img2)
        expand_mask1 = paste_image_center(mask1, expand_mask1)
        expand_mask2 = paste_image_center(mask2, expand_mask2)

        # Calculate IoU between the expanded masks
        iou = calculate_mask_iou(expand_mask1, expand_mask2)

        # Create a merge mask to determine which pixels to use from each image
        merge_mask = np.where(np.logical_and(expand_mask1 > 128, expand_mask2 > 128), 255, 0)

        # Combine images based on the merge mask
        result_img[merge_mask == 255] = expand_img1[merge_mask == 255]
        result_img[merge_mask == 0] = expand_img2[merge_mask == 0]

        return result_img, expand_img1, expand_img2, expand_mask1, expand_mask2, iou

    def paste_image_center(img1: np.ndarray, img2: np.ndarray) -> np.ndarray:
        """
        Paste img1 at the center of img2.

        Args:
            img1 (np.ndarray): The image to paste.
            img2 (np.ndarray): The target image.

        Returns:
            np.ndarray: The resulting image.
        """
        h1, w1 = img1.shape[:2]
        h2, w2 = img2.shape[:2]

        # Calculate the coordinates to place img1 at the center of img2
        paste_x = (w2 - w1) // 2
        paste_y = (h2 - h1) // 2

        # Ensure img1 is within the bounds of img2
        paste_x = max(0, paste_x)
        paste_y = max(0, paste_y)
        paste_x = min(paste_x, w2 - w1)
        paste_y = min(paste_y, h2 - h1)

        # Create an empty result image
        result_img = img2.copy()

        # Paste img1 onto result_img using the calculated coordinates
        result_img[paste_y : paste_y + h1, paste_x : paste_x + w1] = img1

        return result_img

    def calculate_mask_iou(mask1: np.ndarray, mask2: np.ndarray) -> float:
        """
        Calculate the Intersection over Union (IoU) of two binary masks.

        Args:
            mask1 (np.ndarray): The first binary mask as a NumPy array.
            mask2 (np.ndarray): The second binary mask as a NumPy array.

        Returns:
            float: The IoU (Intersection over Union) between the two masks.
        """
        # Calculate the intersection
        intersection = np.logical_and(mask1, mask2)

        # Calculate the union
        union = np.logical_or(mask1, mask2)

        # Calculate IoU
        iou = np.sum(intersection) / np.sum(union)

        return iou

    # Crop img1 to the same size as img2 based on the provided bounding box or default center.
    if box2:
        x, y = calculate_box_center(box2)
        resized_img1 = resize_and_stretch(img1, target_size=(box2[2] - box2[0], box2[3] - box2[1]), white_back=True)
        resized_mask1 = resize_and_stretch(mask1, target_size=(box2[2] - box2[0], box2[3] - box2[1]))
        resized_mask1 = resized_mask1[:, :, 0]
    else:
        x, y = img2.shape[1] / 2, img2.shape[0] / 2
        resized_img1 = resize_and_stretch(img1, target_size=(img2.shape[1], img2.shape[0]), white_back=True)
        resized_mask1 = resize_and_stretch(mask1, target_size=(mask2.shape[1], mask2.shape[0]))
        resized_mask1 = resized_mask1[:, :, 0]

    # rotate & expand img1, and paste to the center of img2
    final_res, final_img1, final_img2, final_mask1, final_mask2, iou = crop_and_paste(
        resized_img1, resized_mask1, img2, mask2, angle, x, y, dx, dy, ratio
    )

    print(f"Merge img1, img2! Mask IoU: {iou}")

    return final_res, final_img1, final_mask1, final_mask2, iou

    Paste img1 to img2 with angle, ratio and mask, while considering optional transformations.

    Args:
        img1 (np.ndarray): The first input image.
        img2 (np.ndarray): The second input image.
        mask1 (np.ndarray): The mask corresponding to img1.
        mask2 (np.ndarray): The mask corresponding to img2.
        angle (float, optional): The rotation angle for img1 (default is 0.0).
        ratio (float, optional): The scaling ratio for img1 (default is 1.0).
        dx (float, otional): the pixel of horizontal move for img1 (default is 0.0).
        dy (float, otional): the pixel of vertical move for img1 (default is 0.0).
        box2 (Optional[Tuple[int, int, int, int]]): The bounding box for ROI in img2 (left, upper, right, lower) (default is None).

    Returns:
        Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, float]:
            - The resulting overlay image.
            - The processed img1.
            - The processed mask1.
            - The mask2.
            - The mask2 used in the final overlay.
            - The Intersection over Union (IoU) value.
    
scripts/easyphoto_utils/tryon_utils.py