Exemplo n.º 1
0
    def jacobian(self, points):
        r"""
        Computes the Jacobian of the transform w.r.t the parameters.

        The Jacobian generated (for 2D) is of the form::

            x -y 1 0
            y  x 0 1

        This maintains a parameter order of::

          W(x;p) = [1 + a  -b   ] [x] + tx
                   [b      1 + a] [y] + ty

        Parameters
        ----------
        points : (N, D) ndarray
            The points to calculate the jacobian over

        Returns
        -------
        dW_dp : (N, P, D) ndarray
            A (``n_points``, ``n_params``, ``n_dims``) array representing
            the Jacobian of the transform.

        Raises
        ------
        DimensionalityError
            ``points.n_dims != self.n_dims`` or transform is not 2D
        """
        n_points, points_n_dim = points.shape
        if points_n_dim != self.n_dims:
            raise DimensionalityError('Trying to sample jacobian in incorrect '
                                      'dimensions (transform is {0}D, '
                                      'sampling at {1}D)'.format(
                                          self.n_dims, points_n_dim))
        elif self.n_dims != 2:
            # TODO: implement 3D Jacobian
            raise DimensionalityError("Only the Jacobian of a 2D similarity "
                                      "transform is currently supported.")

        # prealloc the jacobian
        jac = np.zeros((n_points, self.n_parameters, self.n_dims))
        ones = np.ones_like(points)

        # Build a mask and apply it to the points to build the jacobian
        # Do this for each parameter - [a, b, tx, ty] respectively
        self._apply_jacobian_mask(jac, np.array([1, 1]), 0, points)
        self._apply_jacobian_mask(jac, np.array([-1, 1]), 1, points[:, ::-1])
        self._apply_jacobian_mask(jac, np.array([1, 0]), 2, ones)
        self._apply_jacobian_mask(jac, np.array([0, 1]), 3, ones)

        return jac
Exemplo n.º 2
0
    def render(self, **kwargs):
        r"""
        Select the correct type of trimesh viewer for the given
        trimesh dimensionality.

        Parameters
        ----------
        kwargs : dict
            Passed through to trimesh viewer.

        Returns
        -------
        viewer : :class:`Renderer`
            The rendering object.

        Raises
        ------
        DimensionalityError
            Only 2D and 3D viewers are supported.
        """
        if self.points.shape[1] == 2:
            return TriMeshViewer2d(self.figure_id, self.new_figure,
                                   self.points, self.trilist).render(**kwargs)

        elif self.points.shape[1] == 3:
            return TriMeshViewer3d(self.figure_id, self.new_figure,
                                   self.points, self.trilist).render(**kwargs)
        else:
            raise DimensionalityError("Only 2D and 3D TriMeshes are "
                                      "currently supported")
Exemplo n.º 3
0
    def render(self, **kwargs):
        r"""
        Select the correct type of image viewer for the given
        image dimensionality.

        Parameters
        ----------
        kwargs : dict
            Passed through to image viewer.

        Returns
        -------
        viewer : :class:`Renderer`
            The rendering object.

        Raises
        ------
        DimensionalityError
            Only 2D images are supported.
        """
        if self.dimensions == 2:
            if self.use_subplots:
                return ImageSubplotsViewer2d(self.figure_id, self.new_figure,
                                             self.pixels).render(**kwargs)
            else:
                return ImageViewer2d(self.figure_id, self.new_figure,
                                     self.pixels).render(**kwargs)
        else:
            raise DimensionalityError("Only 2D images are currently supported")
Exemplo n.º 4
0
    def from_vector_inplace(self, p):
        r"""
        Returns an instance of the transform from the given parameters,
        expected to be in Fortran ordering.

        Supports rebuilding from 2D parameter sets.

        2D Similarity: 4 parameters::

            [a, b, tx, ty]

        Parameters
        ----------
        p : (P,) ndarray
            The array of parameters.

        Raises
        ------
        DimensionalityError, NotImplementedError
            Only 2D transforms are supported.
        """
        if p.shape[0] == 4:
            homog = np.eye(3)
            homog[0, 0] += p[0]
            homog[1, 1] += p[0]
            homog[0, 1] = -p[1]
            homog[1, 0] = p[1]
            homog[:2, 2] = p[2:]
            Affine.set_h_matrix(self, homog)  # use the Affine setter cheekily
        elif p.shape[0] == 7:
            raise NotImplementedError("3D similarity transforms cannot be "
                                      "vectorized yet.")
        else:
            raise DimensionalityError("Only 2D and 3D Similarity transforms "
                                      "are currently supported.")
Exemplo n.º 5
0
    def _view(self, figure_id=None, new_figure=False, textured=True, **kwargs):
        r"""
        Visualize the :class:`TexturedTriMesh`. Only 3D objects are currently
        supported.

        Parameters
        ----------
        textured : bool, optional
            If ``True``, render the texture.

            Default: ``True``

        Returns
        -------
        viewer : :class:`menpo.visualize.base.Renderer`
            The viewer object.

        Raises
        ------
        DimensionalityError
            If ``self.n_dims != 3``.
        """
        if textured:
            if self.n_dims == 3:
                return TexturedTriMeshViewer3d(
                    figure_id, new_figure, self.points, self.trilist,
                    self.texture, self.tcoords.points).render(**kwargs)
            else:
                raise DimensionalityError("Only viewing of 3D textured meshes"
                                          "is currently supported.")
        else:
            return super(TexturedTriMesh, self)._view(figure_id=figure_id,
                                                      new_figure=new_figure,
                                                      **kwargs)
Exemplo n.º 6
0
 def render(self, **kwargs):
     if self.dimensions == 2:
         if self.use_subplots:
             MultiImageSubplotsViewer2d(self.figure_id, self.new_figure,
                                        self.pixels_list).render(**kwargs)
         else:
             return MultiImageViewer2d(self.figure_id, self.new_figure,
                                       self.pixels_list).render(**kwargs)
     else:
         raise DimensionalityError("Only 2D images are currently supported")
Exemplo n.º 7
0
 def set_rotation_matrix(self, value):
     shape = value.shape
     if len(shape) != 2 and shape[0] != shape[1]:
         raise ValueError("You need to provide a square rotation matrix")
     # The update better be the same size
     elif self.n_dims != shape[0]:
         raise DimensionalityError("Trying to update the rotation "
                                   "matrix to a different dimension")
     # TODO actually check I am a valid rotation
     # TODO slightly dodgey here accessing _h_matrix
     self._h_matrix[:-1, :-1] = value
Exemplo n.º 8
0
    def jacobian(self, points):
        r"""
        Computes the Jacobian of the transform w.r.t the parameters. This is
        constant for affine transforms.

        The Jacobian generated (for 2D) is of the form::

            x 0 y 0 1 0
            0 x 0 y 0 1

        This maintains a parameter order of::

          W(x;p) = [1 + p1  p3      p5] [x]
                   [p2      1 + p4  p6] [y]
                                        [1]

        Parameters
        ----------
        points : (N, D) ndarray
            The set of points to calculate the jacobian for.

        Returns
        -------
        dW_dp : (N, P, D) ndarray
            A (``n_points``, ``n_params``, ``n_dims``) array representing
            the Jacobian of the transform.
        """
        n_points, points_n_dim = points.shape
        if points_n_dim != self.n_dims:
            raise DimensionalityError(
                "Trying to sample jacobian in incorrect dimensions "
                "(transform is {0}D, sampling at {1}D)".format(
                    self.n_dims, points_n_dim))
        # prealloc the jacobian
        jac = np.zeros((n_points, self.n_parameters, self.n_dims))
        # a mask that we can apply at each iteration
        dim_mask = np.eye(self.n_dims, dtype=np.bool)

        for i, s in enumerate(range(0, self.n_dims * self.n_dims,
                                    self.n_dims)):
            # i is current axis
            # s is slicing offset
            # make a mask for a single points jacobian
            full_mask = np.zeros((self.n_parameters, self.n_dims), dtype=bool)
            # fill the mask in for the ith axis
            full_mask[slice(s, s + self.n_dims)] = dim_mask
            # assign the ith axis points to this mask, broadcasting over all
            # points
            jac[:, full_mask] = points[:, i][..., None]
            # finally, just repeat the same but for the ones at the end
        full_mask = np.zeros((self.n_parameters, self.n_dims), dtype=bool)
        full_mask[slice(s + self.n_dims, s + 2 * self.n_dims)] = dim_mask
        jac[:, full_mask] = 1
        return jac
Exemplo n.º 9
0
    def set_h_matrix(self, value):
        r"""
        Updates the h_matrix, performing sanity checks.

        The Affine h_matrix is limited in what values are allowed. Account
        for them here.
        """
        shape = value.shape
        if len(shape) != 2 and shape[0] != shape[1]:
            raise ValueError("You need to provide a square homogeneous matrix")
        if self.h_matrix is not None:
            # already have a matrix set! The update better be the same size
            if self.n_dims != shape[0] - 1:
                raise DimensionalityError("Trying to update the homogeneous "
                                          "matrix to a different dimension")
        if shape[0] - 1 not in [2, 3]:
            raise DimensionalityError("Affine Transforms can only be 2D or 3D")
        if not (np.allclose(value[-1, :-1], 0)
                and np.allclose(value[-1, -1], 1)):
            raise ValueError("Bottom row must be [0 0 0 1] or [0, 0, 1]")
        self._h_matrix = value.copy()
Exemplo n.º 10
0
    def as_vector(self):
        r"""
        Return the parameters of the transform as a 1D array. These parameters
        are parametrised as deltas from the identity warp. The parameters
        are output in the order ``[a, b, tx, ty]``, given that
        ``a = k cos(theta) - 1`` and ``b = k sin(theta)`` where ``k`` is a
        uniform scale and ``theta`` is a clockwise rotation in radians.

        **2D**

        ========= ===========================================
        parameter definition
        ========= ===========================================
        a         ``a = k cos(theta) - 1``
        b         ``b = k sin(theta)``
        tx        Translation in ``x``
        ty        Translation in ``y``
        ========= ===========================================

        .. note::

            Only 2D transforms are currently supported.

        Returns
        -------
        params : (P,) ndarray
            The values that parameterise the transform.

        Raises
        ------
        DimensionalityError, NotImplementedError
            If the transform is not 2D
        """
        n_dims = self.n_dims
        if n_dims == 2:
            params = self.h_matrix - np.eye(n_dims + 1)
            # Pick off a, b, tx, ty
            params = params[:n_dims, :].flatten(order='F')
            # Pick out a, b, tx, ty
            return params[[0, 1, 4, 5]]
        elif n_dims == 3:
            raise NotImplementedError("3D similarity transforms cannot be "
                                      "vectorized yet.")
        else:
            raise DimensionalityError("Only 2D and 3D Similarity transforms "
                                      "are currently supported.")
Exemplo n.º 11
0
    def face_normals(self):
        r"""
        Normal at each face.

        :type: (``n_tris``, 3) ndarray

        Compute the face normals from the current set of points and
        triangle list. Only valid for 3D dimensional meshes.

        Raises
        ------
        DimensionalityError
            If mesh is not 3D
        """
        if self.n_dims != 3:
            raise DimensionalityError("Normals are only valid for 3D meshes")
        return compute_normals(self.points, self.trilist)[1]
Exemplo n.º 12
0
    def __setitem__(self, group_label, value):
        """
        Sets a new landmark group for the given label. This can be set using
        an existing landmark group, or using a PointCloud. Existing landmark
        groups will have their target reset. If a PointCloud is provided then
        all landmarks belong to a single label `all`.

        Parameters
        ----------
        group_label : String
            Label of new group.
        value : LandmarkGroup or PointCloud
            The new landmark group to set.

        Raises
        ------
        DimensionalityError
            If the landmarks and the shape are not of the same dimensionality.
        """
        from menpo.shape import PointCloud
        # firstly, make sure the dim is correct
        if value.n_dims != self._target.n_dims:
            from menpo.exception import DimensionalityError
            raise DimensionalityError("Trying to set {}D landmarks on a "
                                      "{}D shape".format(
                                          value.n_dims, self._target.n_dims))
        if isinstance(value, PointCloud):
            lmark_group = LandmarkGroup(
                None, None, value,
                {'all': np.ones(value.n_points, dtype=np.bool)})
        elif isinstance(value, LandmarkGroup):
            lmark_group = copy.deepcopy(value)
        else:
            raise ValueError('Valid types are PointCloud or LandmarkGroup')

        self._landmark_groups[group_label] = lmark_group
        self._landmark_groups[group_label]._group_label = group_label
        self._landmark_groups[group_label]._target = self._target
Exemplo n.º 13
0
    def render(self, **kwargs):
        r"""
        Select the correct type of landmark viewer for the given parent shape.

        Parameters
        ----------
        kwargs : dict
            Passed through to landmark viewer.

        Returns
        -------
        viewer : :class:`Renderer`
            The rendering object.

        Raises
        ------
        DimensionalityError
            Only 2D and 3D viewers are supported.
        """
        if self.pointcloud.n_dims == 2:
            from menpo.image.base import Image

            if isinstance(self.target, Image):
                return LandmarkViewer2dImage(
                    self.figure_id, self.new_figure, self.group_label,
                    self.pointcloud, self.labels_to_masks).render(**kwargs)
            else:
                return LandmarkViewer2d(self.figure_id, self.new_figure,
                                        self.group_label, self.pointcloud,
                                        self.labels_to_masks).render(**kwargs)
        elif self.pointcloud.n_dims == 3:
            return LandmarkViewer3d(self.figure_id, self.new_figure,
                                    self.group_label, self.pointcloud,
                                    self.labels_to_masks).render(**kwargs)
        else:
            raise DimensionalityError("Only 2D and 3D landmarks are "
                                      "currently supported")