<NME> boolean.py
<BEF> from functools import partial
from warnings import warn

import numpy as np

from menpo.transform import Translation
from .base import Image
    r"""
    A mask image made from binary pixels. The region of the image that is
    left exposed by the mask is referred to as the 'masked region'. The
    set of 'masked' pixels is those pixels corresponding to a True value in
    the mask.

    Parameters
    points that were NOT outside the triangulation. Otherwise, all points
    were inside and we just return those as ``True``. In general, points
    on the boundary are counted as inside the polygon.
        Image is built from just a 2D numpy array of mask_data.
        Automatically coerced in to boolean values.
    copy: `bool`, optional
        If False, the image_data will not be copied on assignment. Note that
        if the array you provide is not boolean, there **will still be copy**.
        In general this should only be used if you know what you are doing.
    """
    batch_size : `int` or ``None``, optional
        See constrain_to_pointcloud for more information about the batch_size
        parameter.

    Returns
    -------
    mask : (d,) `bool ndarray`
        Whether each pixel index was in inside the convex hull of the
        pointcloud or not.
    """
    from menpo.transform.piecewiseaffine import PiecewiseAffine
    from menpo.transform.piecewiseaffine import TriangleContainmentError

    try:
        pwa = PiecewiseAffine(pcloud, pcloud)
        pwa.apply(indices, batch_size=batch_size)
        return np.ones(indices.shape[0], dtype=bool)
    except TriangleContainmentError as e:
        return ~e.points_outside_source_domain


def convex_hull_point_in_pointcloud(pcloud, indices):
    """
    Uses the matplotlib ``contains_points`` method, which in turn uses:

        "Crossings Multiply algorithm of InsideTest"
        By Eric Haines, 3D/Eye Inc, erich@eye.com
        http://erich.realtimerendering.com/ptinpoly/

        ----------
        shape : `tuple` or `list`
            The shape of the image. Any floating point values are rounded
            according to the `round` kwarg.
        fill : `bool`, optional
            The mask value to be set everywhere.
        round: {``ceil``, ``floor``, ``round``}, optional
            Rounding function to be applied to floating point shapes.

        Returns
        The pointcloud to use for the containment test.
    indices : (d, n_dims) `ndarray`
        The list of pixel indices to test.

    Returns
    -------
    mask : (d,) `bool ndarray`
        Whether each pixel index was in inside the convex hull of the
        pointcloud or not.
    """
    from scipy.spatial import ConvexHull
    from matplotlib.path import Path

    c_hull = ConvexHull(pcloud.points)
    polygon = pcloud.points[c_hull.vertices, :]

        Returns the pixels of the mask with no channel axis. This is what
        should be used to mask any k-dimensional image.

        :type: ``(M, N, ..., L)``, `bool` `ndarray`
        """
        return self.pixels[..., 0]

    set of 'masked' pixels is those pixels corresponding to a ``True`` value in
    the mask.

    Parameters
    ----------
    mask_data : ``(M, N, ..., L)`` `ndarray`
        The binary mask data. Note that there is no channel axis - a 2D Mask
        Image is built from just a 2D numpy array of mask_data.
        Automatically coerced in to boolean values.
    copy: `bool`, optional
        If ``False``, the image_data will not be copied on assignment. Note that
        if the array you provide is not boolean, there **will still be copy**.
        In general this should only be used if you know what you are doing.
    """

    def __init__(self, mask_data, copy=True):
        # Add a channel dimension. We do this little reshape trick to add
        # the axis because this maintains C-contiguous'ness
        mask_data = mask_data.reshape((1,) + mask_data.shape)
        # If we are trying not to copy, but the data we have isn't boolean,
        # then unfortunately, we forced to copy anyway!
        if mask_data.dtype != bool:
            mask_data = np.array(mask_data, dtype=bool, copy=True, order="C")
            if not copy:
                warn(
                    "The copy flag was NOT honoured. A copy HAS been made. "
                    "Please ensure the data you pass is C-contiguous."
                )
        super(BooleanImage, self).__init__(mask_data, copy=copy)

    @classmethod
    def init_blank(cls, shape, fill=True, round="ceil", **kwargs):
        r"""
        r"""
        The proportion of the mask which is ``False``

        :type: float
        """
        return (self.n_false() * 1.0) / self.n_pixels

        fill : `bool`, optional
            The mask value to be set everywhere.
        round: ``{ceil, floor, round}``, optional
            Rounding function to be applied to floating point shapes.

        Returns
        -------
        blank_image : :map:`BooleanImage`
            A blank mask of the requested size

        """
        from .base import round_image_shape

        shape = round_image_shape(shape, round)
        if fill:
            mask = np.ones(shape, dtype=bool)
        else:
            mask = np.zeros(shape, dtype=bool)
        return cls(mask, copy=False)

    @classmethod
    def init_from_channels_at_back(cls, pixels):
        r"""
        This method is not required for ``BooleanImage`` types as boolean images
        do not expect a channel axis for construction.

        Parameters
        ----------
        pixels : ``(M, N ..., Q)`` `ndarray`
        by reshaping the vector to the correct dimensions. Note that this is
        rebuilding a boolean image **itself** from boolean values. The mask
        is in no way interpreted in performing the operation, in contrast to
        MaskedImage, where only the masked region is used in from_vector()
        and as_vector(). Any image landmarks are transferred in the process.

        Parameters
        ----------
        vector : ``(n_pixels,)`` `bool` `ndarray`
            A flattened vector of all the pixels of a :map:`BooleanImage`.
        copy : `bool`, optional
            If ``False``, no copy of the vector will be taken.
        r"""
        Create an Image that is big enough to contain the given pointcloud.
        The pointcloud will be translated to the origin and then translated
        according to its bounds in order to fit inside the new image.
        An optional boundary can be provided in order to increase the space
        around the boundary of the pointcloud. The boundary will be added
        Raises
        ------
        Warning
            If copy=False cannot be honored.
        """
        mask = BooleanImage(vector.reshape(self.shape), copy=copy)
        mask.landmarks = self.landmarks
        ----------
        pointcloud : :map:`PointCloud`
            Pointcloud to place inside the newly created image.
        group : `str`, optional
            If ``None``, the pointcloud will only be used to create the image.
            If a `str` then the pointcloud will be attached as a landmark
            group to the image, with the given string as key.

    def invert(self):
        r"""
        Returns a copy of this Boolean image, which is inverted.

        Returns
        -------
        inverted : :map:`BooleanImage`
            A copy of this boolean mask, where all True values are False and
            all False values are True.
        """
        inverse = self.copy()
        inverse.invert_inplace()
        image : :map:`MaskedImage`
            A new image with the same size as the given pointcloud, optionally
    def bounds_true(self, boundary=0, constrain_to_bounds=True):
        r"""
        Returns the minimum to maximum indices along all dimensions that the
        mask includes which fully surround the True mask values. In the case
        of a 2D Image for instance, the min and max define two corners of a
        rectangle bounding the True pixel values.

        new_image = cls.init_blank(image_shape, fill=fill)
        if constrain:
            new_image = new_image.constrain_to_pointcloud(origin_pc)
        if group is not None:
            new_image.landmarks[group] = origin_pc
        return new_image

    def as_masked(self, mask=None, copy=True):
        r"""
        Impossible for a :map:`BooleanImage` to be transformed to a
        --------
        min_b : ``(D,)`` `ndarray`
            The minimum extent of the ``True`` mask region with the boundary
            along each dimension. If `constrain_to_bounds` was ``True``,
            is clipped to legal image bounds.
        max_b : ``(D,)`` `ndarray`
            The maximum extent of the ``True`` mask region with the boundary
            along each dimension. If `constrain_to_bounds` was ``True``,
            is clipped to legal image bounds.
        """
        mpi = self.true_indices()
        """
        return self.pixels[0, ...]

    def n_true(self):
        r"""
        The number of ``True`` values in the mask.

        :type: `int`
        """
        return np.sum(self.pixels)

    def n_false(self):
        r"""
        The number of ``False`` values in the mask.

        :type: `int`
        """
        return self.n_pixels - self.n_true()

    def all_true(self):
        r"""
        ``True`` iff every element of the mask is ``True``.

        :type: `bool`
        -------
        min_b : ``(D,)`` `ndarray`
            The minimum extent of the ``True`` mask region with the boundary
            along each dimension. If `constrain_to_bounds` was ``True``,
            is clipped to legal image bounds.
        max_b : ``(D,)`` `ndarray`
            The maximum extent of the ``True`` mask region with the boundary
            along each dimension. If `constrain_to_bounds` was ``True``,
            is clipped to legal image bounds.
        """
        return self.invert().bounds_true(
    def proportion_false(self):
        r"""
        The proportion of the mask which is ``False``

        :type: `float`
        """
        return (self.n_false() * 1.0) / self.n_pixels

    def true_indices(self):
        r"""
        The indices of pixels that are ``True``.

        :type: ``(n_dims, n_true)`` `ndarray`
        """
        if self.all_true():
            return self.indices()
        else:
            # Ignore the channel axis
            return np.vstack(np.nonzero(self.pixels[0])).T
            Defines, for each pixel location on the template, which pixel
            location should be sampled from on this image.
        warp_landmarks : `bool`, optional
            If ``True``, warped_image will have the same landmark dictionary
            as self, but with each landmark updated to the warped position.
        mode : {``constant, ``nearest``, ``reflect`` or ``wrap``}, optional
            Points outside the boundaries of the input are filled according
            to the given mode.
        cval : `float`, optional
            Used in conjunction with mode ``'constant'``, the value outside
            the image boundaries.

        Returns
        )

    def from_vector(self, vector, copy=True):
        r"""
        Takes a flattened vector and returns a new :map:`BooleanImage` formed
        by reshaping the vector to the correct dimensions. Note that this is
        rebuilding a boolean image **itself** from boolean values. The mask
        is in no way interpreted in performing the operation, in contrast to
        :map:`MaskedImage`, where only the masked region is used in
        :meth:`from_vector` and :meth`as_vector`. Any image landmarks are
        transferred in the process.

        Parameters
        ----------
        vector : ``(n_pixels,)`` `bool ndarray`

        Note that the order keyword argument is in fact ignored, as any order
        other than 0 makes no sense on a binary image. The keyword argument is
        present only for compatibility with the Image warp_to_shape API.

        Parameters
        ----------
            New BooleanImage of same shape as this image

        Raises
        ------
        Warning
            Defines, for each index on template_shape, which pixel location
            should be sampled from on this image.
        warp_landmarks : `bool`, optional
            If `True`, ``warped_image`` will have the same landmark dictionary
            as self, but with each landmark updated to the warped position.
        mode : {``constant, ``nearest``, ``reflect`` or ``wrap``}, optional
            Points outside the boundaries of the input are filled according
            to the given mode.
        cval : `float`, optional
            Used in conjunction with mode ``'constant'``, the value outside
            the image boundaries.

        Returns
        Returns
        -------
        inverted : :map:`BooleanImage`
            A copy of this boolean mask, where all ``True`` values are ``False``
            and all ``False`` values are ``True``.
        """
        inverse = self.copy()
        inverse.pixels = ~self.pixels
        return inverse

    def bounds_true(self, boundary=0, constrain_to_bounds=True):
        r"""
        Returns the minimum to maximum indices along all dimensions that the
        mask includes which fully surround the ``True`` mask values. In the case
        of a 2D Image for instance, the min and max define two corners of a
        rectangle bounding the True pixel values.

        Parameters
        ----------
        boundary : `int`, optional
            A number of pixels that should be added to the extent. A
            negative value can be used to shrink the bounds in.
        constrain_to_bounds: `bool`, optional
            If ``True``, the bounding extent is snapped to not go beyond
            the edge of the image. If ``False``, the bounds are left unchanged.

        Returns
        --------
        min_b : ``(D,)`` `ndarray`
            The minimum extent of the ``True`` mask region with the boundary
            along each dimension. If ``constrain_to_bounds=True``,
            is clipped to legal image bounds.
        max_b : ``(D,)`` `ndarray`
            The maximum extent of the ``True`` mask region with the boundary
    def constrain_to_landmarks(self, group=None, label=None, trilist=None):
        r"""
        Restricts this mask to be equal to the convex hull around the
        landmarks chosen.

        Parameters
        ----------
        group : `str`, optional
            The key of the landmark set that should be used. If `None`,
            and if there is only one set of landmarks, this set will be used.
        label: `str`, optional
            The label of of the landmark manager that you wish to use. If no
            label is passed, the convex hull of all landmarks is used.
        trilist: ``(t, 3)`` `ndarray`, optional
            Triangle list to be used on the landmarked points in selecting
            the mask region. If None defaults to performing Delaunay
            triangulation on the points.
        """
        self.constrain_to_pointcloud(self.landmarks[group][label],
        boundary : `int` >= 0, optional

    def constrain_to_pointcloud(self, pointcloud, trilist=None):
        r"""
        Restricts this mask to be equal to the convex hull around a point cloud

        Parameters
        ----------
            The minimum extent of the ``True`` mask region with the boundary
            along each dimension. If ``constrain_to_bounds=True``,
            is clipped to legal image bounds.
        max_b : ``(D,)`` `ndarray`
            The maximum extent of the ``True`` mask region with the boundary
            along each dimension. If ``constrain_to_bounds=True``,
            is clipped to legal image bounds.
        """
        return self.invert().bounds_true(
            boundary=boundary, constrain_to_bounds=constrain_to_bounds
        )

    # noinspection PyMethodOverriding
    def sample(self, points_to_sample, mode="constant", cval=False, **kwargs):
        r"""
        Sample this image at the given sub-pixel accurate points. The input
        PointCloud should have the same number of dimensions as the image e.g.
        a 2D PointCloud for a 2D multi-channel image. A numpy array will be
        returned the has the values for every given point across each channel
        of the image.

        Parameters
        ----------
        points_to_sample : :map:`PointCloud`
            Array of points to sample from the image. Should be
            `(n_points, n_dims)`
        mode : ``{constant, nearest, reflect, wrap}``, optional
            Points outside the boundaries of the input are filled according
            to the given mode.
        cval : `float`, optional
            Used in conjunction with mode ``constant``, the value outside
            the image boundaries.

        Returns
        -------
        sampled_pixels : (`n_points`, `n_channels`) `bool ndarray`
            The interpolated values taken across every channel of the image.
        """
        # enforce the order as 0, as this is boolean data, then call super
        return Image.sample(self, points_to_sample, order=0, mode=mode, cval=cval)

    # noinspection PyMethodOverriding
    def warp_to_mask(
        self,
        template_mask,
        transform,
        warp_landmarks=True,
        mode="constant",
        cval=False,
        batch_size=None,
        return_transform=False,
    ):
        r"""
        Return a copy of this :map:`BooleanImage` warped into a different
        reference space.

        Note that warping into a mask is slower than warping into a full image.
        If you don't need a non-linear mask, consider warp_to_shape instead.

        Parameters
        ----------
        template_mask : :map:`BooleanImage`
            Defines the shape of the result, and what pixels should be
            sampled.
        transform : :map:`Transform`
            Transform **from the template space back to this image**.
            Defines, for each pixel location on the template, which pixel
            location should be sampled from on this image.
        warp_landmarks : `bool`, optional
            If ``True``, result will have the same landmark dictionary
            as self, but with each landmark updated to the warped position.
        mode : ``{constant, nearest, reflect or wrap}``, optional
            Points outside the boundaries of the input are filled according
            to the given mode.
        cval : `float`, optional
            Used in conjunction with mode ``constant``, the value outside
            the image boundaries.
        batch_size : `int` or ``None``, optional
            This should only be considered for large images. Setting this
            value can cause warping to become much slower, particular for
            cached warps such as Piecewise Affine. This size indicates
            how many points in the image should be warped at a time, which
            keeps memory usage low. If ``None``, no batching is used and all
            points are warped at once.
        return_transform : `bool`, optional
            This argument is for internal use only. If ``True``, then the
            :map:`Transform` object is also returned.

        Returns
        -------
        warped_image : :map:`BooleanImage`
            A copy of this image, warped.
        transform : :map:`Transform`
            The transform that was used. It only applies if
            `return_transform` is ``True``.
        """
        # enforce the order as 0, as this is boolean data, then call super
        return Image.warp_to_mask(
            self,
            template_mask,
            transform,
            warp_landmarks=warp_landmarks,
            order=0,
            mode=mode,
            cval=cval,
            batch_size=batch_size,
            return_transform=return_transform,
        )

    # noinspection PyMethodOverriding
    def warp_to_shape(
        self,
        template_shape,
        transform,
        warp_landmarks=True,
        mode="constant",
        cval=False,
        order=None,
        batch_size=None,
        return_transform=False,
    ):
        """
        Return a copy of this :map:`BooleanImage` warped into a different
        reference space.

        Note that the order keyword argument is in fact ignored, as any order
        other than 0 makes no sense on a binary image. The keyword argument is
        present only for compatibility with the :map:`Image` warp_to_shape API.

        Parameters
        ----------
        template_shape : ``(n_dims, )`` `tuple` or `ndarray`
            Defines the shape of the result, and what pixel indices should be
            sampled (all of them).
        transform : :map:`Transform`
            Transform **from the template_shape space back to this image**.
            Defines, for each index on template_shape, which pixel location
            should be sampled from on this image.
        warp_landmarks : `bool`, optional
            If ``True``, result will have the same landmark dictionary
            as self, but with each landmark updated to the warped position.
        mode : ``{constant, nearest, reflect or wrap}``, optional
            Points outside the boundaries of the input are filled according
            to the given mode.
        cval : `float`, optional
            Used in conjunction with mode ``constant``, the value outside
            the image boundaries.
        batch_size : `int` or ``None``, optional
            This should only be considered for large images. Setting this
            value can cause warping to become much slower, particular for
            cached warps such as Piecewise Affine. This size indicates
            how many points in the image should be warped at a time, which
            keeps memory usage low. If ``None``, no batching is used and all
            points are warped at once.
        return_transform : `bool`, optional
            This argument is for internal use only. If ``True``, then the
            :map:`Transform` object is also returned.

        Returns
        -------
        warped_image : :map:`BooleanImage`
            A copy of this image, warped.
        transform : :map:`Transform`
            The transform that was used. It only applies if
            `return_transform` is ``True``.
        """
        # call the super variant and get ourselves an Image back
        # note that we force the use of order=0 for BooleanImages.
        warped = Image.warp_to_shape(
            self,
            template_shape,
            transform,
            warp_landmarks=warp_landmarks,
            order=0,
            mode=mode,
            cval=cval,
            batch_size=batch_size,
        )
        # unfortunately we can't escape copying here, let BooleanImage
        # convert us to bool
        boolean_image = BooleanImage(warped.pixels.reshape(template_shape))
        if warped.has_landmarks:
            boolean_image.landmarks = warped.landmarks
        if hasattr(warped, "path"):
            boolean_image.path = warped.path
        # optionally return the transform
        if return_transform:
            return boolean_image, transform
        else:
            return boolean_image

    def _build_warp_to_mask(self, template_mask, sampled_pixel_values, **kwargs):
        r"""
        Builds the warped image from the template mask and sampled pixel values.
        """
        # start from a copy of the template_mask
        warped_img = template_mask.copy()
        if warped_img.all_true():
            # great, just reshape the sampled_pixel_values
            warped_img.pixels = sampled_pixel_values.reshape((1,) + warped_img.shape)
        else:
            # we have to fill out mask with the sampled mask..
            warped_img.pixels[:, warped_img.mask] = sampled_pixel_values
        return warped_img

    def constrain_to_landmarks(self, group=None, batch_size=None):
        r"""
        Returns a copy of this image whereby the ``True`` values in the image
        are restricted to be equal to the convex hull around the landmarks
        chosen. This is not a per-pixel convex hull, but instead relies on a
        triangulated approximation. If the landmarks in question are an instance
        of :map:`TriMesh`, the triangulation of the landmarks will be used in
        the convex hull calculation. If the landmarks are an instance of
        :map:`PointCloud`, Delaunay triangulation will be used to create a
        triangulation.

        Parameters
        ----------
        group : `str`, optional
            The key of the landmark set that should be used. If ``None``,
            and if there is only one set of landmarks, this set will be used.
        batch_size : `int` or ``None``, optional
            This should only be considered for large images. Setting this value
            will cause constraining to become much slower. This size indicates
            how many points in the image should be checked at a time, which
            keeps memory usage low. If ``None``, no batching is used and all
            points are checked at once.

        Returns
        -------
        constrained : :map:`BooleanImage`
            The new boolean image, constrained by the given landmark group.
        """
        return self.constrain_to_pointcloud(
            self.landmarks[group], batch_size=batch_size
        )

    def constrain_to_pointcloud(
        self, pointcloud, batch_size=None, point_in_pointcloud="pwa"
    ):
        r"""
        Returns a copy of this image whereby the ``True`` values in the image
        are restricted to be equal to the convex hull around a pointcloud. The
        choice of whether a pixel is inside or outside of the pointcloud is
        determined by the ``point_in_pointcloud`` parameter. By default a
        Piecewise Affine transform is used to test for containment, which is
        useful when aligning images by their landmarks. Triangluation will be
        decided by Delauny - if you wish to customise it, a :map:`TriMesh`
        instance can be passed for the ``pointcloud`` argument. In this case,
        the triangulation of the Trimesh will be used to define the retained
        region.

        For large images, a faster and pixel-accurate method can be used (
        'convex_hull'). Here, there is no specialization for
        :map:`TriMesh` instances. Alternatively, a callable can be provided to
        override the test. By default, the provided implementations are only
        valid for 2D images.


        Parameters
        ----------
        pointcloud : :map:`PointCloud` or :map:`TriMesh`
            The pointcloud of points that should be constrained to. See
            `point_in_pointcloud` for how in some cases a :map:`TriMesh` may be
            used to control triangulation.
        batch_size : `int` or ``None``, optional
            This should only be considered for large images. Setting this value
            will cause constraining to become much slower. This size indicates
            how many points in the image should be checked at a time, which
            keeps memory usage low. If ``None``, no batching is used and all
            points are checked at once. By default, this is only used for
            the 'pwa' point_in_pointcloud choice.
        point_in_pointcloud : {'pwa', 'convex_hull'} or `callable`
            The method used to check if pixels in the image fall inside the
            ``pointcloud`` or not. If 'pwa', Menpo's :map:`PiecewiseAffine`
            transform will be used to test for containment. In this case
            ``pointcloud`` should be a :map:`TriMesh`. If it isn't, Delauny
            triangulation will be used to first triangulate ``pointcloud`` into
            a  :map:`TriMesh` before testing for containment.
            If a callable is passed, it should take two parameters,
            the :map:`PointCloud` to constrain with and the pixel locations
            ((d, n_dims) ndarray) to test and should return a (d, 1) boolean
            ndarray of whether the pixels were inside (True) or outside (False)
            of the :map:`PointCloud`.

        Returns
        -------
        constrained : :map:`BooleanImage`
            The new boolean image, constrained by the given pointcloud.

        Raises
        ------
        ValueError
            If the image is not 2D and a default implementation is chosen.
        ValueError
            If the chosen ``point_in_pointcloud`` is unknown.
        """
        copy = self.copy()
        if point_in_pointcloud in {"pwa", "convex_hull"} and self.n_dims != 2:
            raise ValueError(
                "Can only constrain mask on 2D images with the "
                "default point_in_pointcloud implementations."
                "Please provide a custom callable for calculating "
                "the new mask in this "
                "{}D image".format(self.n_dims)
            )

        if point_in_pointcloud == "pwa":
            point_in_pointcloud = partial(
                pwa_point_in_pointcloud, batch_size=batch_size
            )
        elif point_in_pointcloud == "convex_hull":
            point_in_pointcloud = convex_hull_point_in_pointcloud
        elif not callable(point_in_pointcloud):
            # Not a function, or a string, so we have an error!
            raise ValueError(
                "point_in_pointcloud must be a callable that "
                "take two arguments: the Menpo PointCloud as a "
                "boundary and the ndarray of pixel indices "
                "to test. {} is an unknown option.".format(point_in_pointcloud)
            )

        # Only consider indices inside the bounding box of the PointCloud
        bounds = pointcloud.bounds()
        # Convert to integer to try and reduce boundary fp rounding errors.
        bounds = [b.astype(int) for b in bounds]
        indices = copy.indices()

        # This loop is to ensure the code is multi-dimensional
        for k in range(self.n_dims):
            indices = indices[indices[:, k] >= bounds[0][k], :]
            indices = indices[indices[:, k] <= bounds[1][k], :]
        # Due to only testing bounding box indices, make sure the mask starts
        # off as all False
        copy.pixels[:] = False

        # slice(0, 1) because we know we only have 1 channel
        # Slice all the channels, only inside the bounding box (for setting
        # the new mask values).
        all_channels = [slice(0, 1)]
        slices = tuple(
            all_channels
            + [slice(bounds[0][k], bounds[1][k] + 1) for k in range(self.n_dims)]
        )
        copy.pixels[slices].flat = point_in_pointcloud(pointcloud, indices)
        return copy

<MSG> Fix BooleanImage docs

<DFF> @@ -8,7 +8,7 @@ class BooleanImage(Image):
     r"""
     A mask image made from binary pixels. The region of the image that is
     left exposed by the mask is referred to as the 'masked region'. The
-    set of 'masked' pixels is those pixels corresponding to a True value in
+    set of 'masked' pixels is those pixels corresponding to a ``True`` value in
     the mask.
 
     Parameters
@@ -18,7 +18,7 @@ class BooleanImage(Image):
         Image is built from just a 2D numpy array of mask_data.
         Automatically coerced in to boolean values.
     copy: `bool`, optional
-        If False, the image_data will not be copied on assignment. Note that
+        If ``False``, the image_data will not be copied on assignment. Note that
         if the array you provide is not boolean, there **will still be copy**.
         In general this should only be used if you know what you are doing.
     """
@@ -54,10 +54,10 @@ class BooleanImage(Image):
         ----------
         shape : `tuple` or `list`
             The shape of the image. Any floating point values are rounded
-            according to the `round` kwarg.
+            according to the ``round`` kwarg.
         fill : `bool`, optional
             The mask value to be set everywhere.
-        round: {``ceil``, ``floor``, ``round``}, optional
+        round: ``{ceil, floor, round}``, optional
             Rounding function to be applied to floating point shapes.
 
         Returns
@@ -80,7 +80,7 @@ class BooleanImage(Image):
         Returns the pixels of the mask with no channel axis. This is what
         should be used to mask any k-dimensional image.
 
-        :type: ``(M, N, ..., L)``, `bool` `ndarray`
+        :type: ``(M, N, ..., L)``, `bool ndarray`
         """
         return self.pixels[..., 0]
 
@@ -120,7 +120,7 @@ class BooleanImage(Image):
         r"""
         The proportion of the mask which is ``False``
 
-        :type: float
+        :type: `float`
         """
         return (self.n_false() * 1.0) / self.n_pixels
 
@@ -156,12 +156,13 @@ class BooleanImage(Image):
         by reshaping the vector to the correct dimensions. Note that this is
         rebuilding a boolean image **itself** from boolean values. The mask
         is in no way interpreted in performing the operation, in contrast to
-        MaskedImage, where only the masked region is used in from_vector()
-        and as_vector(). Any image landmarks are transferred in the process.
+        :map:`MaskedImage`, where only the masked region is used in
+        :meth:`from_vector` and :meth`as_vector`. Any image landmarks are
+        transferred in the process.
 
         Parameters
         ----------
-        vector : ``(n_pixels,)`` `bool` `ndarray`
+        vector : ``(n_pixels,)`` `bool ndarray`
             A flattened vector of all the pixels of a :map:`BooleanImage`.
         copy : `bool`, optional
             If ``False``, no copy of the vector will be taken.
@@ -174,7 +175,7 @@ class BooleanImage(Image):
         Raises
         ------
         Warning
-            If copy=False cannot be honored.
+            If ``copy=False`` cannot be honored.
         """
         mask = BooleanImage(vector.reshape(self.shape), copy=copy)
         mask.landmarks = self.landmarks
@@ -188,13 +189,13 @@ class BooleanImage(Image):
 
     def invert(self):
         r"""
-        Returns a copy of this Boolean image, which is inverted.
+        Returns a copy of this boolean image, which is inverted.
 
         Returns
         -------
         inverted : :map:`BooleanImage`
-            A copy of this boolean mask, where all True values are False and
-            all False values are True.
+            A copy of this boolean mask, where all ``True`` values are ``False``
+            and all ``False`` values are ``True``.
         """
         inverse = self.copy()
         inverse.invert_inplace()
@@ -203,7 +204,7 @@ class BooleanImage(Image):
     def bounds_true(self, boundary=0, constrain_to_bounds=True):
         r"""
         Returns the minimum to maximum indices along all dimensions that the
-        mask includes which fully surround the True mask values. In the case
+        mask includes which fully surround the ``True`` mask values. In the case
         of a 2D Image for instance, the min and max define two corners of a
         rectangle bounding the True pixel values.
 
@@ -220,11 +221,11 @@ class BooleanImage(Image):
         --------
         min_b : ``(D,)`` `ndarray`
             The minimum extent of the ``True`` mask region with the boundary
-            along each dimension. If `constrain_to_bounds` was ``True``,
+            along each dimension. If ``constrain_to_bounds=True``,
             is clipped to legal image bounds.
         max_b : ``(D,)`` `ndarray`
             The maximum extent of the ``True`` mask region with the boundary
-            along each dimension. If `constrain_to_bounds` was ``True``,
+            along each dimension. If ``constrain_to_bounds=True``,
             is clipped to legal image bounds.
         """
         mpi = self.true_indices()
@@ -255,11 +256,11 @@ class BooleanImage(Image):
         -------
         min_b : ``(D,)`` `ndarray`
             The minimum extent of the ``True`` mask region with the boundary
-            along each dimension. If `constrain_to_bounds` was ``True``,
+            along each dimension. If ``constrain_to_bounds=True``,
             is clipped to legal image bounds.
         max_b : ``(D,)`` `ndarray`
             The maximum extent of the ``True`` mask region with the boundary
-            along each dimension. If `constrain_to_bounds` was ``True``,
+            along each dimension. If ``constrain_to_bounds=True``,
             is clipped to legal image bounds.
         """
         return self.invert().bounds_true(
@@ -285,13 +286,13 @@ class BooleanImage(Image):
             Defines, for each pixel location on the template, which pixel
             location should be sampled from on this image.
         warp_landmarks : `bool`, optional
-            If ``True``, warped_image will have the same landmark dictionary
+            If ``True``, result will have the same landmark dictionary
             as self, but with each landmark updated to the warped position.
-        mode : {``constant, ``nearest``, ``reflect`` or ``wrap``}, optional
+        mode : ``{constant, nearest, reflect or wrap}``, optional
             Points outside the boundaries of the input are filled according
             to the given mode.
         cval : `float`, optional
-            Used in conjunction with mode ``'constant'``, the value outside
+            Used in conjunction with mode ``constant``, the value outside
             the image boundaries.
 
         Returns
@@ -313,7 +314,7 @@ class BooleanImage(Image):
 
         Note that the order keyword argument is in fact ignored, as any order
         other than 0 makes no sense on a binary image. The keyword argument is
-        present only for compatibility with the Image warp_to_shape API.
+        present only for compatibility with the :map:`Image` warp_to_shape API.
 
         Parameters
         ----------
@@ -325,13 +326,13 @@ class BooleanImage(Image):
             Defines, for each index on template_shape, which pixel location
             should be sampled from on this image.
         warp_landmarks : `bool`, optional
-            If `True`, ``warped_image`` will have the same landmark dictionary
+            If ``True``, result will have the same landmark dictionary
             as self, but with each landmark updated to the warped position.
-        mode : {``constant, ``nearest``, ``reflect`` or ``wrap``}, optional
+        mode : ``{constant, nearest, reflect or wrap}``, optional
             Points outside the boundaries of the input are filled according
             to the given mode.
         cval : `float`, optional
-            Used in conjunction with mode ``'constant'``, the value outside
+            Used in conjunction with mode ``constant``, the value outside
             the image boundaries.
 
         Returns
@@ -372,19 +373,20 @@ class BooleanImage(Image):
     def constrain_to_landmarks(self, group=None, label=None, trilist=None):
         r"""
         Restricts this mask to be equal to the convex hull around the
-        landmarks chosen.
+        landmarks chosen. This is not a per-pixel convex hull, but instead
+        relies on a triangulated approximation.
 
         Parameters
         ----------
         group : `str`, optional
-            The key of the landmark set that should be used. If `None`,
+            The key of the landmark set that should be used. If ``None``,
             and if there is only one set of landmarks, this set will be used.
         label: `str`, optional
             The label of of the landmark manager that you wish to use. If no
             label is passed, the convex hull of all landmarks is used.
         trilist: ``(t, 3)`` `ndarray`, optional
             Triangle list to be used on the landmarked points in selecting
-            the mask region. If None defaults to performing Delaunay
+            the mask region. If ``None``, defaults to performing Delaunay
             triangulation on the points.
         """
         self.constrain_to_pointcloud(self.landmarks[group][label],
@@ -392,7 +394,9 @@ class BooleanImage(Image):
 
     def constrain_to_pointcloud(self, pointcloud, trilist=None):
         r"""
-        Restricts this mask to be equal to the convex hull around a point cloud
+        Restricts this mask to be equal to the convex hull around a point cloud.
+        This is not a per-pixel convex hull, but instead
+        relies on a triangulated approximation.
 
         Parameters
         ----------

{'commit': 'diff --git a/menpo/image/boolean.py b/menpo/image/boolean.py\nindex bf2e2d6e..551bfa48 100644\n--- a/menpo/image/boolean.py\n+++ b/menpo/image/boolean.py\n@@ -8,7 +8,7 @@ class BooleanImage(Image):\n     r"""\n     A mask image made from binary pixels. The region of the image that is\n     left exposed by the mask is referred to as the \'masked region\'. The\n-    set of \'masked\' pixels is those pixels corresponding to a True value in\n+    set of \'masked\' pixels is those pixels corresponding to a ``True`` value in\n     the mask.\n \n     Parameters\n@@ -18,7 +18,7 @@ class BooleanImage(Image):\n         Image is built from just a 2D numpy array of mask_data.\n         Automatically coerced in to boolean values.\n     copy: `bool`, optional\n-        If False, the image_data will not be copied on assignment. Note that\n+        If ``False``, the image_data will not be copied on assignment. Note that\n         if the array you provide is not boolean, there **will still be copy**.\n         In general this should only be used if you know what you are doing.\n     """\n@@ -54,10 +54,10 @@ class BooleanImage(Image):\n         ----------\n         shape : `tuple` or `list`\n             The shape of the image. Any floating point values are rounded\n-            according to the `round` kwarg.\n+            according to the ``round`` kwarg.\n         fill : `bool`, optional\n             The mask value to be set everywhere.\n-        round: {``ceil``, ``floor``, ``round``}, optional\n+        round: ``{ceil, floor, round}``, optional\n             Rounding function to be applied to floating point shapes.\n \n         Returns\n@@ -80,7 +80,7 @@ class BooleanImage(Image):\n         Returns the pixels of the mask with no channel axis. This is what\n         should be used to mask any k-dimensional image.\n \n-        :type: ``(M, N, ..., L)``, `bool` `ndarray`\n+        :type: ``(M, N, ..., L)``, `bool ndarray`\n         """\n         return self.pixels[..., 0]\n \n@@ -120,7 +120,7 @@ class BooleanImage(Image):\n         r"""\n         The proportion of the mask which is ``False``\n \n-        :type: float\n+        :type: `float`\n         """\n         return (self.n_false() * 1.0) / self.n_pixels\n \n@@ -156,12 +156,13 @@ class BooleanImage(Image):\n         by reshaping the vector to the correct dimensions. Note that this is\n         rebuilding a boolean image **itself** from boolean values. The mask\n         is in no way interpreted in performing the operation, in contrast to\n-        MaskedImage, where only the masked region is used in from_vector()\n-        and as_vector(). Any image landmarks are transferred in the process.\n+        :map:`MaskedImage`, where only the masked region is used in\n+        :meth:`from_vector` and :meth`as_vector`. Any image landmarks are\n+        transferred in the process.\n \n         Parameters\n         ----------\n-        vector : ``(n_pixels,)`` `bool` `ndarray`\n+        vector : ``(n_pixels,)`` `bool ndarray`\n             A flattened vector of all the pixels of a :map:`BooleanImage`.\n         copy : `bool`, optional\n             If ``False``, no copy of the vector will be taken.\n@@ -174,7 +175,7 @@ class BooleanImage(Image):\n         Raises\n         ------\n         Warning\n-            If copy=False cannot be honored.\n+            If ``copy=False`` cannot be honored.\n         """\n         mask = BooleanImage(vector.reshape(self.shape), copy=copy)\n         mask.landmarks = self.landmarks\n@@ -188,13 +189,13 @@ class BooleanImage(Image):\n \n     def invert(self):\n         r"""\n-        Returns a copy of this Boolean image, which is inverted.\n+        Returns a copy of this boolean image, which is inverted.\n \n         Returns\n         -------\n         inverted : :map:`BooleanImage`\n-            A copy of this boolean mask, where all True values are False and\n-            all False values are True.\n+            A copy of this boolean mask, where all ``True`` values are ``False``\n+            and all ``False`` values are ``True``.\n         """\n         inverse = self.copy()\n         inverse.invert_inplace()\n@@ -203,7 +204,7 @@ class BooleanImage(Image):\n     def bounds_true(self, boundary=0, constrain_to_bounds=True):\n         r"""\n         Returns the minimum to maximum indices along all dimensions that the\n-        mask includes which fully surround the True mask values. In the case\n+        mask includes which fully surround the ``True`` mask values. In the case\n         of a 2D Image for instance, the min and max define two corners of a\n         rectangle bounding the True pixel values.\n \n@@ -220,11 +221,11 @@ class BooleanImage(Image):\n         --------\n         min_b : ``(D,)`` `ndarray`\n             The minimum extent of the ``True`` mask region with the boundary\n-            along each dimension. If `constrain_to_bounds` was ``True``,\n+            along each dimension. If ``constrain_to_bounds=True``,\n             is clipped to legal image bounds.\n         max_b : ``(D,)`` `ndarray`\n             The maximum extent of the ``True`` mask region with the boundary\n-            along each dimension. If `constrain_to_bounds` was ``True``,\n+            along each dimension. If ``constrain_to_bounds=True``,\n             is clipped to legal image bounds.\n         """\n         mpi = self.true_indices()\n@@ -255,11 +256,11 @@ class BooleanImage(Image):\n         -------\n         min_b : ``(D,)`` `ndarray`\n             The minimum extent of the ``True`` mask region with the boundary\n-            along each dimension. If `constrain_to_bounds` was ``True``,\n+            along each dimension. If ``constrain_to_bounds=True``,\n             is clipped to legal image bounds.\n         max_b : ``(D,)`` `ndarray`\n             The maximum extent of the ``True`` mask region with the boundary\n-            along each dimension. If `constrain_to_bounds` was ``True``,\n+            along each dimension. If ``constrain_to_bounds=True``,\n             is clipped to legal image bounds.\n         """\n         return self.invert().bounds_true(\n@@ -285,13 +286,13 @@ class BooleanImage(Image):\n             Defines, for each pixel location on the template, which pixel\n             location should be sampled from on this image.\n         warp_landmarks : `bool`, optional\n-            If ``True``, warped_image will have the same landmark dictionary\n+            If ``True``, result will have the same landmark dictionary\n             as self, but with each landmark updated to the warped position.\n-        mode : {``constant, ``nearest``, ``reflect`` or ``wrap``}, optional\n+        mode : ``{constant, nearest, reflect or wrap}``, optional\n             Points outside the boundaries of the input are filled according\n             to the given mode.\n         cval : `float`, optional\n-            Used in conjunction with mode ``\'constant\'``, the value outside\n+            Used in conjunction with mode ``constant``, the value outside\n             the image boundaries.\n \n         Returns\n@@ -313,7 +314,7 @@ class BooleanImage(Image):\n \n         Note that the order keyword argument is in fact ignored, as any order\n         other than 0 makes no sense on a binary image. The keyword argument is\n-        present only for compatibility with the Image warp_to_shape API.\n+        present only for compatibility with the :map:`Image` warp_to_shape API.\n \n         Parameters\n         ----------\n@@ -325,13 +326,13 @@ class BooleanImage(Image):\n             Defines, for each index on template_shape, which pixel location\n             should be sampled from on this image.\n         warp_landmarks : `bool`, optional\n-            If `True`, ``warped_image`` will have the same landmark dictionary\n+            If ``True``, result will have the same landmark dictionary\n             as self, but with each landmark updated to the warped position.\n-        mode : {``constant, ``nearest``, ``reflect`` or ``wrap``}, optional\n+        mode : ``{constant, nearest, reflect or wrap}``, optional\n             Points outside the boundaries of the input are filled according\n             to the given mode.\n         cval : `float`, optional\n-            Used in conjunction with mode ``\'constant\'``, the value outside\n+            Used in conjunction with mode ``constant``, the value outside\n             the image boundaries.\n \n         Returns\n@@ -372,19 +373,20 @@ class BooleanImage(Image):\n     def constrain_to_landmarks(self, group=None, label=None, trilist=None):\n         r"""\n         Restricts this mask to be equal to the convex hull around the\n-        landmarks chosen.\n+        landmarks chosen. This is not a per-pixel convex hull, but instead\n+        relies on a triangulated approximation.\n \n         Parameters\n         ----------\n         group : `str`, optional\n-            The key of the landmark set that should be used. If `None`,\n+            The key of the landmark set that should be used. If ``None``,\n             and if there is only one set of landmarks, this set will be used.\n         label: `str`, optional\n             The label of of the landmark manager that you wish to use. If no\n             label is passed, the convex hull of all landmarks is used.\n         trilist: ``(t, 3)`` `ndarray`, optional\n             Triangle list to be used on the landmarked points in selecting\n-            the mask region. If None defaults to performing Delaunay\n+            the mask region. If ``None``, defaults to performing Delaunay\n             triangulation on the points.\n         """\n         self.constrain_to_pointcloud(self.landmarks[group][label],\n@@ -392,7 +394,9 @@ class BooleanImage(Image):\n \n     def constrain_to_pointcloud(self, pointcloud, trilist=None):\n         r"""\n-        Restricts this mask to be equal to the convex hull around a point cloud\n+        Restricts this mask to be equal to the convex hull around a point cloud.\n+        This is not a per-pixel convex hull, but instead\n+        relies on a triangulated approximation.\n \n         Parameters\n         ----------\n', 'commit_subject': 'Fix BooleanImage docs', 'repo_name': 'menpo/menpo', 'committer': {'name': 'Patrick Snape', 'email': '7632b035b02eb34afc9678cf89604f75fcb3f21f@gmail.com', 'time_sec': '1422635039', 'tz_offset': '0', 'date': {'seconds': '1422635039'}}, 'commit_tree': '7533beefcbc52795238712d802f14adca11bcac2', 'commit_message': 'Fix BooleanImage docs\n', 'language_name': 'Python', 'license': 'bsd-3-clause', 'hunks': '@@ -8,7 +8,7 @@ class BooleanImage(Image):\n     r"""\n     A mask image made from binary pixels. The region of the image that is\n     left exposed by the mask is referred to as the \'masked region\'. The\n-    set of \'masked\' pixels is those pixels corresponding to a True value in\n+    set of \'masked\' pixels is those pixels corresponding to a ``True`` value in\n     the mask.\n \n     Parameters\n@@ -18,7 +18,7 @@ class BooleanImage(Image):\n         Image is built from just a 2D numpy array of mask_data.\n         Automatically coerced in to boolean values.\n     copy: `bool`, optional\n-        If False, the image_data will not be copied on assignment. Note that\n+        If ``False``, the image_data will not be copied on assignment. Note that\n         if the array you provide is not boolean, there **will still be copy**.\n         In general this should only be used if you know what you are doing.\n     """\n@@ -54,10 +54,10 @@ class BooleanImage(Image):\n         ----------\n         shape : `tuple` or `list`\n             The shape of the image. Any floating point values are rounded\n-            according to the `round` kwarg.\n+            according to the ``round`` kwarg.\n         fill : `bool`, optional\n             The mask value to be set everywhere.\n-        round: {``ceil``, ``floor``, ``round``}, optional\n+        round: ``{ceil, floor, round}``, optional\n             Rounding function to be applied to floating point shapes.\n \n         Returns\n@@ -80,7 +80,7 @@ class BooleanImage(Image):\n         Returns the pixels of the mask with no channel axis. This is what\n         should be used to mask any k-dimensional image.\n \n-        :type: ``(M, N, ..., L)``, `bool` `ndarray`\n+        :type: ``(M, N, ..., L)``, `bool ndarray`\n         """\n         return self.pixels[..., 0]\n \n@@ -120,7 +120,7 @@ class BooleanImage(Image):\n         r"""\n         The proportion of the mask which is ``False``\n \n-        :type: float\n+        :type: `float`\n         """\n         return (self.n_false() * 1.0) / self.n_pixels\n \n@@ -156,12 +156,13 @@ class BooleanImage(Image):\n         by reshaping the vector to the correct dimensions. Note that this is\n         rebuilding a boolean image **itself** from boolean values. The mask\n         is in no way interpreted in performing the operation, in contrast to\n-        MaskedImage, where only the masked region is used in from_vector()\n-        and as_vector(). Any image landmarks are transferred in the process.\n+        :map:`MaskedImage`, where only the masked region is used in\n+        :meth:`from_vector` and :meth`as_vector`. Any image landmarks are\n+        transferred in the process.\n \n         Parameters\n         ----------\n-        vector : ``(n_pixels,)`` `bool` `ndarray`\n+        vector : ``(n_pixels,)`` `bool ndarray`\n             A flattened vector of all the pixels of a :map:`BooleanImage`.\n         copy : `bool`, optional\n             If ``False``, no copy of the vector will be taken.\n@@ -174,7 +175,7 @@ class BooleanImage(Image):\n         Raises\n         ------\n         Warning\n-            If copy=False cannot be honored.\n+            If ``copy=False`` cannot be honored.\n         """\n         mask = BooleanImage(vector.reshape(self.shape), copy=copy)\n         mask.landmarks = self.landmarks\n@@ -188,13 +189,13 @@ class BooleanImage(Image):\n \n     def invert(self):\n         r"""\n-        Returns a copy of this Boolean image, which is inverted.\n+        Returns a copy of this boolean image, which is inverted.\n \n         Returns\n         -------\n         inverted : :map:`BooleanImage`\n-            A copy of this boolean mask, where all True values are False and\n-            all False values are True.\n+            A copy of this boolean mask, where all ``True`` values are ``False``\n+            and all ``False`` values are ``True``.\n         """\n         inverse = self.copy()\n         inverse.invert_inplace()\n@@ -203,7 +204,7 @@ class BooleanImage(Image):\n     def bounds_true(self, boundary=0, constrain_to_bounds=True):\n         r"""\n         Returns the minimum to maximum indices along all dimensions that the\n-        mask includes which fully surround the True mask values. In the case\n+        mask includes which fully surround the ``True`` mask values. In the case\n         of a 2D Image for instance, the min and max define two corners of a\n         rectangle bounding the True pixel values.\n \n@@ -220,11 +221,11 @@ class BooleanImage(Image):\n         --------\n         min_b : ``(D,)`` `ndarray`\n             The minimum extent of the ``True`` mask region with the boundary\n-            along each dimension. If `constrain_to_bounds` was ``True``,\n+            along each dimension. If ``constrain_to_bounds=True``,\n             is clipped to legal image bounds.\n         max_b : ``(D,)`` `ndarray`\n             The maximum extent of the ``True`` mask region with the boundary\n-            along each dimension. If `constrain_to_bounds` was ``True``,\n+            along each dimension. If ``constrain_to_bounds=True``,\n             is clipped to legal image bounds.\n         """\n         mpi = self.true_indices()\n@@ -255,11 +256,11 @@ class BooleanImage(Image):\n         -------\n         min_b : ``(D,)`` `ndarray`\n             The minimum extent of the ``True`` mask region with the boundary\n-            along each dimension. If `constrain_to_bounds` was ``True``,\n+            along each dimension. If ``constrain_to_bounds=True``,\n             is clipped to legal image bounds.\n         max_b : ``(D,)`` `ndarray`\n             The maximum extent of the ``True`` mask region with the boundary\n-            along each dimension. If `constrain_to_bounds` was ``True``,\n+            along each dimension. If ``constrain_to_bounds=True``,\n             is clipped to legal image bounds.\n         """\n         return self.invert().bounds_true(\n@@ -285,13 +286,13 @@ class BooleanImage(Image):\n             Defines, for each pixel location on the template, which pixel\n             location should be sampled from on this image.\n         warp_landmarks : `bool`, optional\n-            If ``True``, warped_image will have the same landmark dictionary\n+            If ``True``, result will have the same landmark dictionary\n             as self, but with each landmark updated to the warped position.\n-        mode : {``constant, ``nearest``, ``reflect`` or ``wrap``}, optional\n+        mode : ``{constant, nearest, reflect or wrap}``, optional\n             Points outside the boundaries of the input are filled according\n             to the given mode.\n         cval : `float`, optional\n-            Used in conjunction with mode ``\'constant\'``, the value outside\n+            Used in conjunction with mode ``constant``, the value outside\n             the image boundaries.\n \n         Returns\n@@ -313,7 +314,7 @@ class BooleanImage(Image):\n \n         Note that the order keyword argument is in fact ignored, as any order\n         other than 0 makes no sense on a binary image. The keyword argument is\n-        present only for compatibility with the Image warp_to_shape API.\n+        present only for compatibility with the :map:`Image` warp_to_shape API.\n \n         Parameters\n         ----------\n@@ -325,13 +326,13 @@ class BooleanImage(Image):\n             Defines, for each index on template_shape, which pixel location\n             should be sampled from on this image.\n         warp_landmarks : `bool`, optional\n-            If `True`, ``warped_image`` will have the same landmark dictionary\n+            If ``True``, result will have the same landmark dictionary\n             as self, but with each landmark updated to the warped position.\n-        mode : {``constant, ``nearest``, ``reflect`` or ``wrap``}, optional\n+        mode : ``{constant, nearest, reflect or wrap}``, optional\n             Points outside the boundaries of the input are filled according\n             to the given mode.\n         cval : `float`, optional\n-            Used in conjunction with mode ``\'constant\'``, the value outside\n+            Used in conjunction with mode ``constant``, the value outside\n             the image boundaries.\n \n         Returns\n@@ -372,19 +373,20 @@ class BooleanImage(Image):\n     def constrain_to_landmarks(self, group=None, label=None, trilist=None):\n         r"""\n         Restricts this mask to be equal to the convex hull around the\n-        landmarks chosen.\n+        landmarks chosen. This is not a per-pixel convex hull, but instead\n+        relies on a triangulated approximation.\n \n         Parameters\n         ----------\n         group : `str`, optional\n-            The key of the landmark set that should be used. If `None`,\n+            The key of the landmark set that should be used. If ``None``,\n             and if there is only one set of landmarks, this set will be used.\n         label: `str`, optional\n             The label of of the landmark manager that you wish to use. If no\n             label is passed, the convex hull of all landmarks is used.\n         trilist: ``(t, 3)`` `ndarray`, optional\n             Triangle list to be used on the landmarked points in selecting\n-            the mask region. If None defaults to performing Delaunay\n+            the mask region. If ``None``, defaults to performing Delaunay\n             triangulation on the points.\n         """\n         self.constrain_to_pointcloud(self.landmarks[group][label],\n@@ -392,7 +394,9 @@ class BooleanImage(Image):\n \n     def constrain_to_pointcloud(self, pointcloud, trilist=None):\n         r"""\n-        Restricts this mask to be equal to the convex hull around a point cloud\n+        Restricts this mask to be equal to the convex hull around a point cloud.\n+        This is not a per-pixel convex hull, but instead\n+        relies on a triangulated approximation.\n \n         Parameters\n         ----------\n', 'addition_count': 33, 'deletion_count': 29, 'src_file': 'a/menpo/image/boolean.py', 'tgt_file': 'b/menpo/image/boolean.py', 'file_extension': '.py', 'before_file': 'from functools import partial\nfrom warnings import warn\n\nimport numpy as np\n\nfrom menpo.transform import Translation\nfrom .base import Image\n    r"""\n    A mask image made from binary pixels. The region of the image that is\n    left exposed by the mask is referred to as the \'masked region\'. The\n    set of \'masked\' pixels is those pixels corresponding to a True value in\n    the mask.\n\n    Parameters\n    points that were NOT outside the triangulation. Otherwise, all points\n    were inside and we just return those as ``True``. In general, points\n    on the boundary are counted as inside the polygon.\n        Image is built from just a 2D numpy array of mask_data.\n        Automatically coerced in to boolean values.\n    copy: `bool`, optional\n        If False, the image_data will not be copied on assignment. Note that\n        if the array you provide is not boolean, there **will still be copy**.\n        In general this should only be used if you know what you are doing.\n    """\n    batch_size : `int` or ``None``, optional\n        See constrain_to_pointcloud for more information about the batch_size\n        parameter.\n\n    Returns\n    -------\n    mask : (d,) `bool ndarray`\n        Whether each pixel index was in inside the convex hull of the\n        pointcloud or not.\n    """\n    from menpo.transform.piecewiseaffine import PiecewiseAffine\n    from menpo.transform.piecewiseaffine import TriangleContainmentError\n\n    try:\n        pwa = PiecewiseAffine(pcloud, pcloud)\n        pwa.apply(indices, batch_size=batch_size)\n        return np.ones(indices.shape[0], dtype=bool)\n    except TriangleContainmentError as e:\n        return ~e.points_outside_source_domain\n\n\ndef convex_hull_point_in_pointcloud(pcloud, indices):\n    """\n    Uses the matplotlib ``contains_points`` method, which in turn uses:\n\n        "Crossings Multiply algorithm of InsideTest"\n        By Eric Haines, 3D/Eye Inc, erich@eye.com\n        http://erich.realtimerendering.com/ptinpoly/\n\n        ----------\n        shape : `tuple` or `list`\n            The shape of the image. Any floating point values are rounded\n            according to the `round` kwarg.\n        fill : `bool`, optional\n            The mask value to be set everywhere.\n        round: {``ceil``, ``floor``, ``round``}, optional\n            Rounding function to be applied to floating point shapes.\n\n        Returns\n        The pointcloud to use for the containment test.\n    indices : (d, n_dims) `ndarray`\n        The list of pixel indices to test.\n\n    Returns\n    -------\n    mask : (d,) `bool ndarray`\n        Whether each pixel index was in inside the convex hull of the\n        pointcloud or not.\n    """\n    from scipy.spatial import ConvexHull\n    from matplotlib.path import Path\n\n    c_hull = ConvexHull(pcloud.points)\n    polygon = pcloud.points[c_hull.vertices, :]\n\n        Returns the pixels of the mask with no channel axis. This is what\n        should be used to mask any k-dimensional image.\n\n        :type: ``(M, N, ..., L)``, `bool` `ndarray`\n        """\n        return self.pixels[..., 0]\n\n    set of \'masked\' pixels is those pixels corresponding to a ``True`` value in\n    the mask.\n\n    Parameters\n    ----------\n    mask_data : ``(M, N, ..., L)`` `ndarray`\n        The binary mask data. Note that there is no channel axis - a 2D Mask\n        Image is built from just a 2D numpy array of mask_data.\n        Automatically coerced in to boolean values.\n    copy: `bool`, optional\n        If ``False``, the image_data will not be copied on assignment. Note that\n        if the array you provide is not boolean, there **will still be copy**.\n        In general this should only be used if you know what you are doing.\n    """\n\n    def __init__(self, mask_data, copy=True):\n        # Add a channel dimension. We do this little reshape trick to add\n        # the axis because this maintains C-contiguous\'ness\n        mask_data = mask_data.reshape((1,) + mask_data.shape)\n        # If we are trying not to copy, but the data we have isn\'t boolean,\n        # then unfortunately, we forced to copy anyway!\n        if mask_data.dtype != bool:\n            mask_data = np.array(mask_data, dtype=bool, copy=True, order="C")\n            if not copy:\n                warn(\n                    "The copy flag was NOT honoured. A copy HAS been made. "\n                    "Please ensure the data you pass is C-contiguous."\n                )\n        super(BooleanImage, self).__init__(mask_data, copy=copy)\n\n    @classmethod\n    def init_blank(cls, shape, fill=True, round="ceil", **kwargs):\n        r"""\n        r"""\n        The proportion of the mask which is ``False``\n\n        :type: float\n        """\n        return (self.n_false() * 1.0) / self.n_pixels\n\n        fill : `bool`, optional\n            The mask value to be set everywhere.\n        round: ``{ceil, floor, round}``, optional\n            Rounding function to be applied to floating point shapes.\n\n        Returns\n        -------\n        blank_image : :map:`BooleanImage`\n            A blank mask of the requested size\n\n        """\n        from .base import round_image_shape\n\n        shape = round_image_shape(shape, round)\n        if fill:\n            mask = np.ones(shape, dtype=bool)\n        else:\n            mask = np.zeros(shape, dtype=bool)\n        return cls(mask, copy=False)\n\n    @classmethod\n    def init_from_channels_at_back(cls, pixels):\n        r"""\n        This method is not required for ``BooleanImage`` types as boolean images\n        do not expect a channel axis for construction.\n\n        Parameters\n        ----------\n        pixels : ``(M, N ..., Q)`` `ndarray`\n        by reshaping the vector to the correct dimensions. Note that this is\n        rebuilding a boolean image **itself** from boolean values. The mask\n        is in no way interpreted in performing the operation, in contrast to\n        MaskedImage, where only the masked region is used in from_vector()\n        and as_vector(). Any image landmarks are transferred in the process.\n\n        Parameters\n        ----------\n        vector : ``(n_pixels,)`` `bool` `ndarray`\n            A flattened vector of all the pixels of a :map:`BooleanImage`.\n        copy : `bool`, optional\n            If ``False``, no copy of the vector will be taken.\n        r"""\n        Create an Image that is big enough to contain the given pointcloud.\n        The pointcloud will be translated to the origin and then translated\n        according to its bounds in order to fit inside the new image.\n        An optional boundary can be provided in order to increase the space\n        around the boundary of the pointcloud. The boundary will be added\n        Raises\n        ------\n        Warning\n            If copy=False cannot be honored.\n        """\n        mask = BooleanImage(vector.reshape(self.shape), copy=copy)\n        mask.landmarks = self.landmarks\n        ----------\n        pointcloud : :map:`PointCloud`\n            Pointcloud to place inside the newly created image.\n        group : `str`, optional\n            If ``None``, the pointcloud will only be used to create the image.\n            If a `str` then the pointcloud will be attached as a landmark\n            group to the image, with the given string as key.\n\n    def invert(self):\n        r"""\n        Returns a copy of this Boolean image, which is inverted.\n\n        Returns\n        -------\n        inverted : :map:`BooleanImage`\n            A copy of this boolean mask, where all True values are False and\n            all False values are True.\n        """\n        inverse = self.copy()\n        inverse.invert_inplace()\n        image : :map:`MaskedImage`\n            A new image with the same size as the given pointcloud, optionally\n    def bounds_true(self, boundary=0, constrain_to_bounds=True):\n        r"""\n        Returns the minimum to maximum indices along all dimensions that the\n        mask includes which fully surround the True mask values. In the case\n        of a 2D Image for instance, the min and max define two corners of a\n        rectangle bounding the True pixel values.\n\n        new_image = cls.init_blank(image_shape, fill=fill)\n        if constrain:\n            new_image = new_image.constrain_to_pointcloud(origin_pc)\n        if group is not None:\n            new_image.landmarks[group] = origin_pc\n        return new_image\n\n    def as_masked(self, mask=None, copy=True):\n        r"""\n        Impossible for a :map:`BooleanImage` to be transformed to a\n        --------\n        min_b : ``(D,)`` `ndarray`\n            The minimum extent of the ``True`` mask region with the boundary\n            along each dimension. If `constrain_to_bounds` was ``True``,\n            is clipped to legal image bounds.\n        max_b : ``(D,)`` `ndarray`\n            The maximum extent of the ``True`` mask region with the boundary\n            along each dimension. If `constrain_to_bounds` was ``True``,\n            is clipped to legal image bounds.\n        """\n        mpi = self.true_indices()\n        """\n        return self.pixels[0, ...]\n\n    def n_true(self):\n        r"""\n        The number of ``True`` values in the mask.\n\n        :type: `int`\n        """\n        return np.sum(self.pixels)\n\n    def n_false(self):\n        r"""\n        The number of ``False`` values in the mask.\n\n        :type: `int`\n        """\n        return self.n_pixels - self.n_true()\n\n    def all_true(self):\n        r"""\n        ``True`` iff every element of the mask is ``True``.\n\n        :type: `bool`\n        -------\n        min_b : ``(D,)`` `ndarray`\n            The minimum extent of the ``True`` mask region with the boundary\n            along each dimension. If `constrain_to_bounds` was ``True``,\n            is clipped to legal image bounds.\n        max_b : ``(D,)`` `ndarray`\n            The maximum extent of the ``True`` mask region with the boundary\n            along each dimension. If `constrain_to_bounds` was ``True``,\n            is clipped to legal image bounds.\n        """\n        return self.invert().bounds_true(\n    def proportion_false(self):\n        r"""\n        The proportion of the mask which is ``False``\n\n        :type: `float`\n        """\n        return (self.n_false() * 1.0) / self.n_pixels\n\n    def true_indices(self):\n        r"""\n        The indices of pixels that are ``True``.\n\n        :type: ``(n_dims, n_true)`` `ndarray`\n        """\n        if self.all_true():\n            return self.indices()\n        else:\n            # Ignore the channel axis\n            return np.vstack(np.nonzero(self.pixels[0])).T\n            Defines, for each pixel location on the template, which pixel\n            location should be sampled from on this image.\n        warp_landmarks : `bool`, optional\n            If ``True``, warped_image will have the same landmark dictionary\n            as self, but with each landmark updated to the warped position.\n        mode : {``constant, ``nearest``, ``reflect`` or ``wrap``}, optional\n            Points outside the boundaries of the input are filled according\n            to the given mode.\n        cval : `float`, optional\n            Used in conjunction with mode ``\'constant\'``, the value outside\n            the image boundaries.\n\n        Returns\n        )\n\n    def from_vector(self, vector, copy=True):\n        r"""\n        Takes a flattened vector and returns a new :map:`BooleanImage` formed\n        by reshaping the vector to the correct dimensions. Note that this is\n        rebuilding a boolean image **itself** from boolean values. The mask\n        is in no way interpreted in performing the operation, in contrast to\n        :map:`MaskedImage`, where only the masked region is used in\n        :meth:`from_vector` and :meth`as_vector`. Any image landmarks are\n        transferred in the process.\n\n        Parameters\n        ----------\n        vector : ``(n_pixels,)`` `bool ndarray`\n\n        Note that the order keyword argument is in fact ignored, as any order\n        other than 0 makes no sense on a binary image. The keyword argument is\n        present only for compatibility with the Image warp_to_shape API.\n\n        Parameters\n        ----------\n            New BooleanImage of same shape as this image\n\n        Raises\n        ------\n        Warning\n            Defines, for each index on template_shape, which pixel location\n            should be sampled from on this image.\n        warp_landmarks : `bool`, optional\n            If `True`, ``warped_image`` will have the same landmark dictionary\n            as self, but with each landmark updated to the warped position.\n        mode : {``constant, ``nearest``, ``reflect`` or ``wrap``}, optional\n            Points outside the boundaries of the input are filled according\n            to the given mode.\n        cval : `float`, optional\n            Used in conjunction with mode ``\'constant\'``, the value outside\n            the image boundaries.\n\n        Returns\n        Returns\n        -------\n        inverted : :map:`BooleanImage`\n            A copy of this boolean mask, where all ``True`` values are ``False``\n            and all ``False`` values are ``True``.\n        """\n        inverse = self.copy()\n        inverse.pixels = ~self.pixels\n        return inverse\n\n    def bounds_true(self, boundary=0, constrain_to_bounds=True):\n        r"""\n        Returns the minimum to maximum indices along all dimensions that the\n        mask includes which fully surround the ``True`` mask values. In the case\n        of a 2D Image for instance, the min and max define two corners of a\n        rectangle bounding the True pixel values.\n\n        Parameters\n        ----------\n        boundary : `int`, optional\n            A number of pixels that should be added to the extent. A\n            negative value can be used to shrink the bounds in.\n        constrain_to_bounds: `bool`, optional\n            If ``True``, the bounding extent is snapped to not go beyond\n            the edge of the image. If ``False``, the bounds are left unchanged.\n\n        Returns\n        --------\n        min_b : ``(D,)`` `ndarray`\n            The minimum extent of the ``True`` mask region with the boundary\n            along each dimension. If ``constrain_to_bounds=True``,\n            is clipped to legal image bounds.\n        max_b : ``(D,)`` `ndarray`\n            The maximum extent of the ``True`` mask region with the boundary\n    def constrain_to_landmarks(self, group=None, label=None, trilist=None):\n        r"""\n        Restricts this mask to be equal to the convex hull around the\n        landmarks chosen.\n\n        Parameters\n        ----------\n        group : `str`, optional\n            The key of the landmark set that should be used. If `None`,\n            and if there is only one set of landmarks, this set will be used.\n        label: `str`, optional\n            The label of of the landmark manager that you wish to use. If no\n            label is passed, the convex hull of all landmarks is used.\n        trilist: ``(t, 3)`` `ndarray`, optional\n            Triangle list to be used on the landmarked points in selecting\n            the mask region. If None defaults to performing Delaunay\n            triangulation on the points.\n        """\n        self.constrain_to_pointcloud(self.landmarks[group][label],\n        boundary : `int` >= 0, optional\n\n    def constrain_to_pointcloud(self, pointcloud, trilist=None):\n        r"""\n        Restricts this mask to be equal to the convex hull around a point cloud\n\n        Parameters\n        ----------\n            The minimum extent of the ``True`` mask region with the boundary\n            along each dimension. If ``constrain_to_bounds=True``,\n            is clipped to legal image bounds.\n        max_b : ``(D,)`` `ndarray`\n            The maximum extent of the ``True`` mask region with the boundary\n            along each dimension. If ``constrain_to_bounds=True``,\n            is clipped to legal image bounds.\n        """\n        return self.invert().bounds_true(\n            boundary=boundary, constrain_to_bounds=constrain_to_bounds\n        )\n\n    # noinspection PyMethodOverriding\n    def sample(self, points_to_sample, mode="constant", cval=False, **kwargs):\n        r"""\n        Sample this image at the given sub-pixel accurate points. The input\n        PointCloud should have the same number of dimensions as the image e.g.\n        a 2D PointCloud for a 2D multi-channel image. A numpy array will be\n        returned the has the values for every given point across each channel\n        of the image.\n\n        Parameters\n        ----------\n        points_to_sample : :map:`PointCloud`\n            Array of points to sample from the image. Should be\n            `(n_points, n_dims)`\n        mode : ``{constant, nearest, reflect, wrap}``, optional\n            Points outside the boundaries of the input are filled according\n            to the given mode.\n        cval : `float`, optional\n            Used in conjunction with mode ``constant``, the value outside\n            the image boundaries.\n\n        Returns\n        -------\n        sampled_pixels : (`n_points`, `n_channels`) `bool ndarray`\n            The interpolated values taken across every channel of the image.\n        """\n        # enforce the order as 0, as this is boolean data, then call super\n        return Image.sample(self, points_to_sample, order=0, mode=mode, cval=cval)\n\n    # noinspection PyMethodOverriding\n    def warp_to_mask(\n        self,\n        template_mask,\n        transform,\n        warp_landmarks=True,\n        mode="constant",\n        cval=False,\n        batch_size=None,\n        return_transform=False,\n    ):\n        r"""\n        Return a copy of this :map:`BooleanImage` warped into a different\n        reference space.\n\n        Note that warping into a mask is slower than warping into a full image.\n        If you don\'t need a non-linear mask, consider warp_to_shape instead.\n\n        Parameters\n        ----------\n        template_mask : :map:`BooleanImage`\n            Defines the shape of the result, and what pixels should be\n            sampled.\n        transform : :map:`Transform`\n            Transform **from the template space back to this image**.\n            Defines, for each pixel location on the template, which pixel\n            location should be sampled from on this image.\n        warp_landmarks : `bool`, optional\n            If ``True``, result will have the same landmark dictionary\n            as self, but with each landmark updated to the warped position.\n        mode : ``{constant, nearest, reflect or wrap}``, optional\n            Points outside the boundaries of the input are filled according\n            to the given mode.\n        cval : `float`, optional\n            Used in conjunction with mode ``constant``, the value outside\n            the image boundaries.\n        batch_size : `int` or ``None``, optional\n            This should only be considered for large images. Setting this\n            value can cause warping to become much slower, particular for\n            cached warps such as Piecewise Affine. This size indicates\n            how many points in the image should be warped at a time, which\n            keeps memory usage low. If ``None``, no batching is used and all\n            points are warped at once.\n        return_transform : `bool`, optional\n            This argument is for internal use only. If ``True``, then the\n            :map:`Transform` object is also returned.\n\n        Returns\n        -------\n        warped_image : :map:`BooleanImage`\n            A copy of this image, warped.\n        transform : :map:`Transform`\n            The transform that was used. It only applies if\n            `return_transform` is ``True``.\n        """\n        # enforce the order as 0, as this is boolean data, then call super\n        return Image.warp_to_mask(\n            self,\n            template_mask,\n            transform,\n            warp_landmarks=warp_landmarks,\n            order=0,\n            mode=mode,\n            cval=cval,\n            batch_size=batch_size,\n            return_transform=return_transform,\n        )\n\n    # noinspection PyMethodOverriding\n    def warp_to_shape(\n        self,\n        template_shape,\n        transform,\n        warp_landmarks=True,\n        mode="constant",\n        cval=False,\n        order=None,\n        batch_size=None,\n        return_transform=False,\n    ):\n        """\n        Return a copy of this :map:`BooleanImage` warped into a different\n        reference space.\n\n        Note that the order keyword argument is in fact ignored, as any order\n        other than 0 makes no sense on a binary image. The keyword argument is\n        present only for compatibility with the :map:`Image` warp_to_shape API.\n\n        Parameters\n        ----------\n        template_shape : ``(n_dims, )`` `tuple` or `ndarray`\n            Defines the shape of the result, and what pixel indices should be\n            sampled (all of them).\n        transform : :map:`Transform`\n            Transform **from the template_shape space back to this image**.\n            Defines, for each index on template_shape, which pixel location\n            should be sampled from on this image.\n        warp_landmarks : `bool`, optional\n            If ``True``, result will have the same landmark dictionary\n            as self, but with each landmark updated to the warped position.\n        mode : ``{constant, nearest, reflect or wrap}``, optional\n            Points outside the boundaries of the input are filled according\n            to the given mode.\n        cval : `float`, optional\n            Used in conjunction with mode ``constant``, the value outside\n            the image boundaries.\n        batch_size : `int` or ``None``, optional\n            This should only be considered for large images. Setting this\n            value can cause warping to become much slower, particular for\n            cached warps such as Piecewise Affine. This size indicates\n            how many points in the image should be warped at a time, which\n            keeps memory usage low. If ``None``, no batching is used and all\n            points are warped at once.\n        return_transform : `bool`, optional\n            This argument is for internal use only. If ``True``, then the\n            :map:`Transform` object is also returned.\n\n        Returns\n        -------\n        warped_image : :map:`BooleanImage`\n            A copy of this image, warped.\n        transform : :map:`Transform`\n            The transform that was used. It only applies if\n            `return_transform` is ``True``.\n        """\n        # call the super variant and get ourselves an Image back\n        # note that we force the use of order=0 for BooleanImages.\n        warped = Image.warp_to_shape(\n            self,\n            template_shape,\n            transform,\n            warp_landmarks=warp_landmarks,\n            order=0,\n            mode=mode,\n            cval=cval,\n            batch_size=batch_size,\n        )\n        # unfortunately we can\'t escape copying here, let BooleanImage\n        # convert us to bool\n        boolean_image = BooleanImage(warped.pixels.reshape(template_shape))\n        if warped.has_landmarks:\n            boolean_image.landmarks = warped.landmarks\n        if hasattr(warped, "path"):\n            boolean_image.path = warped.path\n        # optionally return the transform\n        if return_transform:\n            return boolean_image, transform\n        else:\n            return boolean_image\n\n    def _build_warp_to_mask(self, template_mask, sampled_pixel_values, **kwargs):\n        r"""\n        Builds the warped image from the template mask and sampled pixel values.\n        """\n        # start from a copy of the template_mask\n        warped_img = template_mask.copy()\n        if warped_img.all_true():\n            # great, just reshape the sampled_pixel_values\n            warped_img.pixels = sampled_pixel_values.reshape((1,) + warped_img.shape)\n        else:\n            # we have to fill out mask with the sampled mask..\n            warped_img.pixels[:, warped_img.mask] = sampled_pixel_values\n        return warped_img\n\n    def constrain_to_landmarks(self, group=None, batch_size=None):\n        r"""\n        Returns a copy of this image whereby the ``True`` values in the image\n        are restricted to be equal to the convex hull around the landmarks\n        chosen. This is not a per-pixel convex hull, but instead relies on a\n        triangulated approximation. If the landmarks in question are an instance\n        of :map:`TriMesh`, the triangulation of the landmarks will be used in\n        the convex hull calculation. If the landmarks are an instance of\n        :map:`PointCloud`, Delaunay triangulation will be used to create a\n        triangulation.\n\n        Parameters\n        ----------\n        group : `str`, optional\n            The key of the landmark set that should be used. If ``None``,\n            and if there is only one set of landmarks, this set will be used.\n        batch_size : `int` or ``None``, optional\n            This should only be considered for large images. Setting this value\n            will cause constraining to become much slower. This size indicates\n            how many points in the image should be checked at a time, which\n            keeps memory usage low. If ``None``, no batching is used and all\n            points are checked at once.\n\n        Returns\n        -------\n        constrained : :map:`BooleanImage`\n            The new boolean image, constrained by the given landmark group.\n        """\n        return self.constrain_to_pointcloud(\n            self.landmarks[group], batch_size=batch_size\n        )\n\n    def constrain_to_pointcloud(\n        self, pointcloud, batch_size=None, point_in_pointcloud="pwa"\n    ):\n        r"""\n        Returns a copy of this image whereby the ``True`` values in the image\n        are restricted to be equal to the convex hull around a pointcloud. The\n        choice of whether a pixel is inside or outside of the pointcloud is\n        determined by the ``point_in_pointcloud`` parameter. By default a\n        Piecewise Affine transform is used to test for containment, which is\n        useful when aligning images by their landmarks. Triangluation will be\n        decided by Delauny - if you wish to customise it, a :map:`TriMesh`\n        instance can be passed for the ``pointcloud`` argument. In this case,\n        the triangulation of the Trimesh will be used to define the retained\n        region.\n\n        For large images, a faster and pixel-accurate method can be used (\n        \'convex_hull\'). Here, there is no specialization for\n        :map:`TriMesh` instances. Alternatively, a callable can be provided to\n        override the test. By default, the provided implementations are only\n        valid for 2D images.\n\n\n        Parameters\n        ----------\n        pointcloud : :map:`PointCloud` or :map:`TriMesh`\n            The pointcloud of points that should be constrained to. See\n            `point_in_pointcloud` for how in some cases a :map:`TriMesh` may be\n            used to control triangulation.\n        batch_size : `int` or ``None``, optional\n            This should only be considered for large images. Setting this value\n            will cause constraining to become much slower. This size indicates\n            how many points in the image should be checked at a time, which\n            keeps memory usage low. If ``None``, no batching is used and all\n            points are checked at once. By default, this is only used for\n            the \'pwa\' point_in_pointcloud choice.\n        point_in_pointcloud : {\'pwa\', \'convex_hull\'} or `callable`\n            The method used to check if pixels in the image fall inside the\n            ``pointcloud`` or not. If \'pwa\', Menpo\'s :map:`PiecewiseAffine`\n            transform will be used to test for containment. In this case\n            ``pointcloud`` should be a :map:`TriMesh`. If it isn\'t, Delauny\n            triangulation will be used to first triangulate ``pointcloud`` into\n            a  :map:`TriMesh` before testing for containment.\n            If a callable is passed, it should take two parameters,\n            the :map:`PointCloud` to constrain with and the pixel locations\n            ((d, n_dims) ndarray) to test and should return a (d, 1) boolean\n            ndarray of whether the pixels were inside (True) or outside (False)\n            of the :map:`PointCloud`.\n\n        Returns\n        -------\n        constrained : :map:`BooleanImage`\n            The new boolean image, constrained by the given pointcloud.\n\n        Raises\n        ------\n        ValueError\n            If the image is not 2D and a default implementation is chosen.\n        ValueError\n            If the chosen ``point_in_pointcloud`` is unknown.\n        """\n        copy = self.copy()\n        if point_in_pointcloud in {"pwa", "convex_hull"} and self.n_dims != 2:\n            raise ValueError(\n                "Can only constrain mask on 2D images with the "\n                "default point_in_pointcloud implementations."\n                "Please provide a custom callable for calculating "\n                "the new mask in this "\n                "{}D image".format(self.n_dims)\n            )\n\n        if point_in_pointcloud == "pwa":\n            point_in_pointcloud = partial(\n                pwa_point_in_pointcloud, batch_size=batch_size\n            )\n        elif point_in_pointcloud == "convex_hull":\n            point_in_pointcloud = convex_hull_point_in_pointcloud\n        elif not callable(point_in_pointcloud):\n            # Not a function, or a string, so we have an error!\n            raise ValueError(\n                "point_in_pointcloud must be a callable that "\n                "take two arguments: the Menpo PointCloud as a "\n                "boundary and the ndarray of pixel indices "\n                "to test. {} is an unknown option.".format(point_in_pointcloud)\n            )\n\n        # Only consider indices inside the bounding box of the PointCloud\n        bounds = pointcloud.bounds()\n        # Convert to integer to try and reduce boundary fp rounding errors.\n        bounds = [b.astype(int) for b in bounds]\n        indices = copy.indices()\n\n        # This loop is to ensure the code is multi-dimensional\n        for k in range(self.n_dims):\n            indices = indices[indices[:, k] >= bounds[0][k], :]\n            indices = indices[indices[:, k] <= bounds[1][k], :]\n        # Due to only testing bounding box indices, make sure the mask starts\n        # off as all False\n        copy.pixels[:] = False\n\n        # slice(0, 1) because we know we only have 1 channel\n        # Slice all the channels, only inside the bounding box (for setting\n        # the new mask values).\n        all_channels = [slice(0, 1)]\n        slices = tuple(\n            all_channels\n            + [slice(bounds[0][k], bounds[1][k] + 1) for k in range(self.n_dims)]\n        )\n        copy.pixels[slices].flat = point_in_pointcloud(pointcloud, indices)\n        return copy\n', 'message': 'Fix BooleanImage docs\n', 'author_name': 'Patrick Snape', 'lang': 'py'}