<commit_before>from copy import deepcopy
import itertools
import numpy as np
from scipy.ndimage import binary_erosion
from pybug.image.base import AbstractNDImage
from pybug.image.boolean import BooleanNDImage
from pybug.visualize.base import ImageViewer


class MaskedNDImage(AbstractNDImage):
    r"""
    Represents an n-dimensional k-channel image, which has a mask.
    Images can be masked in order to identify a region of interest. All
    images implicitly have a mask that is defined as the the entire image.
    The mask is an instance of
    :class:`BooleanNDImage`.

    Parameters
    ----------
    image_data :  ndarray
        The pixel data for the image, where the last axis represents the
        number of channels.
    mask : (M, N) ``np.bool`` ndarray or :class:`BooleanNDImage`, optional
        A binary array representing the mask. Must be the same
        shape as the image. Only one mask is supported for an image (so the
        mask is applied to every channel equally).

        Default: :class:`BooleanNDImage` covering the whole image

    Raises
    ------
    ValueError
        Mask is not the same shape as the image
    """
    def __init__(self, image_data, mask=None):
        super(MaskedNDImage, self).__init__(image_data)
        if mask is not None:
            if not isinstance(mask, BooleanNDImage):
                mask_image = BooleanNDImage(mask)
            else:
                mask_image = mask
            # have a BooleanNDImage object that we definitely own
            if mask_image.shape == self.shape:
                self.mask = mask_image
            else:
                raise ValueError("Trying to construct a Masked Image of "
                                 "shape {} with a Mask of differing "
                                 "shape {}".format(self.shape,
                                                   mask.shape))
        else:
            # no mask provided - make the default.
            self.mask = BooleanNDImage.blank(self.shape, fill=True)

    # noinspection PyMethodOverriding
    @classmethod
    def _init_with_channel(cls, image_data_with_channel, mask):
        r"""
        Constructor that always requires the image has a
        channel on the last axis. Only used by from_vector. By default,
        just calls the constructor. Subclasses with constructors that don't
        require channel axes need to overwrite this.
        """
        return cls(image_data_with_channel, mask)

    @classmethod
    def blank(cls, shape, n_channels=1, fill=0, dtype=np.float, mask=None):
        r"""
        Returns a blank image

        Parameters
        ----------
        shape : tuple or list
            The shape of the image
        n_channels: int, optional
            The number of channels to create the image with

            Default: 1
        fill : int, optional
            The value to fill all pixels with

            Default: 0
        dtype: numpy datatype, optional
            The datatype of the image.

            Default: np.float
        mask: (M, N) boolean ndarray or :class:`BooleanNDImage`
            An optional mask that can be applied to the image. Has to have a
             shape equal to that of the image.

             Default: all True :class:`BooleanNDImage`

        Returns
        -------
        blank_image : :class:`MaskedNDImage`
            A new masked image of the requested size.
        """
        if fill == 0:
            pixels = np.zeros(shape + (n_channels,), dtype=dtype)
        else:
            pixels = np.ones(shape + (n_channels,), dtype=dtype) * fill
        return cls(pixels, mask=mask)

    @property
    def masked_pixels(self):<commit_msg>masked images extra properties<commit_after>from copy import deepcopy
import itertools
import numpy as np
from scipy.ndimage import binary_erosion
from pybug.image.base import AbstractNDImage
from pybug.image.boolean import BooleanNDImage
from pybug.visualize.base import ImageViewer


class MaskedNDImage(AbstractNDImage):
    r"""
    Represents an n-dimensional k-channel image, which has a mask.
    Images can be masked in order to identify a region of interest. All
    images implicitly have a mask that is defined as the the entire image.
    The mask is an instance of
    :class:`BooleanNDImage`.

    Parameters
    ----------
    image_data :  ndarray
        The pixel data for the image, where the last axis represents the
        number of channels.
    mask : (M, N) ``np.bool`` ndarray or :class:`BooleanNDImage`, optional
        A binary array representing the mask. Must be the same
        shape as the image. Only one mask is supported for an image (so the
        mask is applied to every channel equally).

        Default: :class:`BooleanNDImage` covering the whole image

    Raises
    ------
    ValueError
        Mask is not the same shape as the image
    """
    def __init__(self, image_data, mask=None):
        super(MaskedNDImage, self).__init__(image_data)
        if mask is not None:
            if not isinstance(mask, BooleanNDImage):
                mask_image = BooleanNDImage(mask)
            else:
                mask_image = mask
            # have a BooleanNDImage object that we definitely own
            if mask_image.shape == self.shape:
                self.mask = mask_image
            else:
                raise ValueError("Trying to construct a Masked Image of "
                                 "shape {} with a Mask of differing "
                                 "shape {}".format(self.shape,
                                                   mask.shape))
        else:
            # no mask provided - make the default.
            self.mask = BooleanNDImage.blank(self.shape, fill=True)

    # noinspection PyMethodOverriding
    @classmethod
    def _init_with_channel(cls, image_data_with_channel, mask):
        r"""
        Constructor that always requires the image has a
        channel on the last axis. Only used by from_vector. By default,
        just calls the constructor. Subclasses with constructors that don't
        require channel axes need to overwrite this.
        """
        return cls(image_data_with_channel, mask)

    @classmethod
    def blank(cls, shape, n_channels=1, fill=0, dtype=np.float, mask=None):
        r"""
        Returns a blank image

        Parameters
        ----------
        shape : tuple or list
            The shape of the image
        n_channels: int, optional
            The number of channels to create the image with

            Default: 1
        fill : int, optional
            The value to fill all pixels with

            Default: 0
        dtype: numpy datatype, optional
            The datatype of the image.

            Default: np.float
        mask: (M, N) boolean ndarray or :class:`BooleanNDImage`
            An optional mask that can be applied to the image. Has to have a
             shape equal to that of the image.

             Default: all True :class:`BooleanNDImage`

        Returns
        -------
        blank_image : :class:`MaskedNDImage`
            A new masked image of the requested size.
        """
        if fill == 0:
            pixels = np.zeros(shape + (n_channels,), dtype=dtype)
        else:
            pixels = np.ones(shape + (n_channels,), dtype=dtype) * fill
        return cls(pixels, mask=mask)

    @property
    def n_true_pixels(self):
        return self.mask.n_true

    @property
    def n_false_pixels(self):
        return self.mask.n_false

    @property
    def n_true_elements(self):
        return self.n_true_pixels * self.n_channels

    @property
    def n_false_elements(self):
        return self.n_false_pixels * self.n_channels

    @property
    def masked_pixels(self):
{'fraction_non_alphanumeric': 0.07003991245010943, 'fraction_numerical': 0.0012874983906270116, 'mean_word_length': 1.9424242424242424, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5178172', 'n_tokens_mistral': 2013, 'n_tokens_neox': 1968, 'n_words': 772}