"""
Source url: https://github.com/OPHoperHPO/image-background-remove-tool
Author: Nikita Selin (OPHoperHPO)[https://github.com/OPHoperHPO].
License: Apache License 2.0
"""
import PIL.Image
import cv2
import numpy as np


class CV2TrimapGenerator:
    def __init__(self, kernel_size: int = 30, erosion_iters: int = 1):
        """
        Initialize a new CV2TrimapGenerator instance

        Args:
            kernel_size: The size of the offset from the object mask
            in pixels when an unknown area is detected in the trimap
            erosion_iters: The number of iterations of erosion that
            the object's mask will be subjected to before forming an unknown area
        """
        self.kernel_size = kernel_size
        self.erosion_iters = erosion_iters

    def __call__(self, original_image: PIL.Image.Image, mask: PIL.Image.Image) -> PIL.Image.Image:
        """
        Generates trimap based on predicted object mask to refine object mask borders.
        Based on cv2 erosion algorithm.

        Args:
            original_image: Original image
            mask: Predicted object mask

        Returns:
            Generated trimap for image.
        """
        if mask.mode != "L":
            raise ValueError("Input mask has wrong color mode.")
        if mask.size != original_image.size:
            raise ValueError("Sizes of input image and predicted mask doesn't equal")
        # noinspection PyTypeChecker
        mask_array = np.array(mask)
        pixels = 2 * self.kernel_size + 1
        kernel = np.ones((pixels, pixels), np.uint8)

        if self.erosion_iters > 0:
            erosion_kernel = np.ones((3, 3), np.uint8)
            erode = cv2.erode(mask_array, erosion_kernel, iterations=self.erosion_iters)
            erode = np.where(erode > 0, 255, mask_array)
        else:
            erode = mask_array.copy()

        dilation = cv2.dilate(erode, kernel, iterations=1)

        dilation = np.where(dilation == 255, 127, dilation)  # WHITE to GRAY
        trimap = np.where(erode > 127, 200, dilation)  # mark the tumor inside GRAY

        trimap = np.where(trimap < 127, 0, trimap)  # Embelishment
        trimap = np.where(trimap > 200, 0, trimap)  # Embelishment
        trimap = np.where(trimap == 200, 255, trimap)  # GRAY to WHITE

        return PIL.Image.fromarray(trimap).convert("L")
