Shortcuts

Source code for mmeval.metrics.gradient_error

# Copyright (c) OpenMMLab. All rights reserved.Dict
import cv2
import numpy as np
from typing import Dict, List, Sequence

from mmeval.core import BaseMetric


def gaussian(x: np.ndarray, sigma: float):
    """Gaussian function.

    Args:
        x (np.ndarray): The independent variable.
        sigma (float): Standard deviation of the gaussian function.

    Returns:
        np.ndarray or scalar: Gaussian value of `x`.
    """

    return np.exp(-x**2 / (2 * sigma**2)) / (sigma * np.sqrt(2 * np.pi))


def dgaussian(x: np.ndarray, sigma: float):
    """Derivative of Gaussian function.

    Args:
        x (np.ndarray): The independent variable.
        sigma (float): Standard deviation of the gaussian function.

    Returns:
        np.ndarray or scalar: Gradient of gaussian of `x`.
    """

    return -x * gaussian(x, sigma) / sigma**2


def gauss_filter(sigma: float, epsilon: float = 1e-2):
    """Gaussian Filter.

    Args:
        sigma (float): Standard deviation of the gaussian kernel.
        epsilon (float): Small value used when calculating kernel size.
            Default to 1e-2.

    Returns:
        tuple(np.ndarray, np.ndarray): Gaussian filter along x and y axis.
    """

    half_size = np.ceil(
        sigma * np.sqrt(-2 * np.log(np.sqrt(2 * np.pi) * sigma * epsilon)))
    size = int(2 * half_size + 1)

    # create filter in x axis
    filter_x = np.zeros((size, size))
    for i in range(size):
        for j in range(size):
            filter_x[i, j] = gaussian(i - half_size, sigma) * dgaussian(
                j - half_size, sigma)

    # normalize filter
    norm = np.sqrt((filter_x**2).sum())
    filter_x = filter_x / norm
    filter_y = np.transpose(filter_x)

    return filter_x, filter_y


def gauss_gradient(img: np.ndarray, sigma: float):
    """Gaussian gradient.

    Reference: https://www.mathworks.com/matlabcentral/mlc-downloads/downloads/
    submissions/8060/versions/2/previews/gaussgradient/gaussgradient.m/
    index.html

    Args:
        img (np.ndarray): Input image.
        sigma (float): Standard deviation of the gaussian kernel.

    Returns:
        np.ndarray: Gaussian gradient of input `img`.
    """

    filter_x, filter_y = gauss_filter(sigma)
    img_filtered_x = cv2.filter2D(
        img, -1, filter_x, borderType=cv2.BORDER_REPLICATE)
    img_filtered_y = cv2.filter2D(
        img, -1, filter_y, borderType=cv2.BORDER_REPLICATE)
    return np.sqrt(img_filtered_x**2 + img_filtered_y**2)


[docs]class GradientError(BaseMetric): """Gradient error for evaluating alpha matte prediction. Args: sigma (float): Standard deviation of the gaussian kernel. Defaults to 1.4 . norm_const (int): Divide the result to reduce its magnitude. Defaults to 1000. **kwargs: Keyword parameters passed to :class:`BaseMetric`. Note: The current implementation assumes the image / alpha / trimap a numpy array with pixel values ranging from 0 to 255. The pred_alpha should be masked by trimap before passing into this metric. The trimap is the most commonly used prior knowledge. As the name implies, trimap is a ternary graph and each pixel takes one of {0, 128, 255}, representing the foreground, the unknown and the background respectively. Examples: >>> from mmeval import GradientError >>> import numpy as np >>> >>> gradient_error = GradientError() >>> np.random.seed(0) >>> pred_alpha = np.random.randn(32, 32).astype('uint8') >>> gt_alpha = np.ones((32, 32), dtype=np.uint8) * 255 >>> trimap = np.zeros((32, 32), dtype=np.uint8) >>> trimap[:16, :16] = 128 >>> trimap[16:, 16:] = 255 >>> gradient_error(pred_alpha, gt_alpha, trimap) # doctest: +ELLIPSIS {'gradient_error': ...} """ def __init__(self, sigma: float = 1.4, norm_const: int = 1000, **kwargs) -> None: super().__init__(**kwargs) self.sigma = sigma self.norm_const = norm_const
[docs] def add(self, pred_alphas: Sequence[np.ndarray], gt_alphas: Sequence[np.ndarray], trimaps: Sequence[np.ndarray]) -> None: # type: ignore # yapf: disable # noqa: E501 """Add GradientError score of batch to ``self._results`` Args: pred_alphas (Sequence[np.ndarray]): Predict the probability that pixels belong to the foreground. gt_alphas (Sequence[np.ndarray]): Probability that the actual pixel belongs to the foreground. trimaps (Sequence[np.ndarray]): Broadly speaking, the trimap consists of foreground and unknown region. """ for pred_alpha, gt_alpha, trimap in zip(pred_alphas, gt_alphas, trimaps): assert pred_alpha.shape == gt_alpha.shape, 'The shape of ' \ '`pred_alpha` and `gt_alpha` should be the same, but got: ' \ f'{pred_alpha.shape} and {gt_alpha.shape}' gt_alpha_normed = np.zeros_like(gt_alpha) pred_alpha_normed = np.zeros_like(pred_alpha) cv2.normalize(gt_alpha, gt_alpha_normed, 1.0, 0.0, cv2.NORM_MINMAX) cv2.normalize(pred_alpha, pred_alpha_normed, 1.0, 0.0, cv2.NORM_MINMAX) gt_alpha_grad = gauss_gradient(gt_alpha_normed, self.sigma) pred_alpha_grad = gauss_gradient(pred_alpha_normed, self.sigma) # this is the sum over n samples grad_loss = ((gt_alpha_grad - pred_alpha_grad)**2 * (trimap == 128)).sum() # divide by self.norm_const to reduce the magnitude of the result grad_loss /= self.norm_const self._results.append(grad_loss)
[docs] def compute_metric(self, results: List) -> Dict[str, float]: """Compute the GradientError metric. Args: results (List): A list that consisting the GradientError score. This list has already been synced across all ranks. Returns: Dict[str, float]: The computed GradientError metric. The keys are the names of the metrics, and the values are corresponding results. """ return {'gradient_error': float(np.array(results).mean())}
Read the Docs v: latest
Versions
latest
stable
Downloads
pdf
html
epub
On Read the Docs
Project Home
Builds

Free document hosting provided by Read the Docs.