示例#1
0
def _affine_field(src, ref, xform, srcSpace, refSpace, shape=None, fv2w=None):

    if shape is None: shape = ref.shape[:3]
    if fv2w is None: fv2w = ref.getAffine('voxel', 'world')

    rx, ry, rz = np.meshgrid(np.arange(shape[0]),
                             np.arange(shape[1]),
                             np.arange(shape[2]),
                             indexing='ij')

    rvoxels = np.vstack((rx.flatten(), ry.flatten(), rz.flatten())).T
    f2r = affine.concat(ref.getAffine('world', refSpace), fv2w)
    rcoords = affine.transform(rvoxels, f2r)
    scoords = affine.transform(rcoords, xform)

    field = np.zeros(list(shape[:3]) + [3])
    field[:] = (scoords - rcoords).reshape(*it.chain(shape, [3]))
    field = nonlinear.DeformationField(field,
                                       src,
                                       ref,
                                       srcSpace=srcSpace,
                                       refSpace=refSpace,
                                       xform=fv2w,
                                       header=ref.header,
                                       defType='relative')
    return field
示例#2
0
    def __drawLight(self):
        """Draws a representation of the light source. """

        lightPos = self.lightPos
        bounds = self.__displayCtx.bounds
        centre = np.array([
            bounds.xlo + 0.5 * (bounds.xhi - bounds.xlo),
            bounds.ylo + 0.5 * (bounds.yhi - bounds.ylo),
            bounds.zlo + 0.5 * (bounds.zhi - bounds.zlo)
        ])
        lightPos = affine.transform(lightPos, self.__viewMat)
        centre = affine.transform(centre, self.__viewMat)

        # draw the light as a point
        gl.glColor4f(1, 1, 0, 1)
        gl.glPointSize(10)
        gl.glBegin(gl.GL_POINTS)
        gl.glVertex3f(*lightPos)
        gl.glEnd()

        # draw a line  from the light to the
        # centre of the display bounding box
        gl.glBegin(gl.GL_LINES)
        gl.glVertex3f(*lightPos)
        gl.glVertex3f(*centre)
        gl.glEnd()
示例#3
0
    def updateVertices(self, *a):
        """Called by :meth:`__init__`, and when certain display properties
        change. (Re-)generates the mesh vertices, indices and normals (if
        being displayed in 3D). They are stored as attributes called
        ``vertices``, ``indices``, and ``normals`` respectively.
        """

        overlay  = self.overlay
        vertices = overlay.vertices
        indices  = overlay.indices
        normals  = self.overlay.vnormals
        xform    = self.opts.getTransform('mesh', 'display')

        if not np.all(np.isclose(xform, np.eye(4))):
            vertices = affine.transform(vertices, xform)

            if self.threedee:
                nmat    = affine.invert(xform).T
                normals = affine.transform(normals, nmat, vector=True)

        self.vertices = np.asarray(vertices,          dtype=np.float32)
        self.indices  = np.asarray(indices.flatten(), dtype=np.uint32)

        self.vertices = dutils.makeWriteable(self.vertices)
        self.indices  = dutils.makeWriteable(self.indices)

        if self.threedee:
            self.normals = np.array(normals, dtype=np.float32)
示例#4
0
def _random_affine_field():

    src = _random_image()
    ref = _random_image()

    # our test field just encodes an affine
    xform = affine.compose(np.random.randint(2, 5, 3),
                           np.random.randint(1, 10, 3), np.random.random(3))

    rx, ry, rz = np.meshgrid(np.arange(ref.shape[0]),
                             np.arange(ref.shape[1]),
                             np.arange(ref.shape[2]),
                             indexing='ij')

    rvoxels = np.vstack((rx.flatten(), ry.flatten(), rz.flatten())).T
    rcoords = affine.transform(rvoxels, ref.voxToScaledVoxMat)
    scoords = affine.transform(rcoords, xform)

    field = np.zeros(list(ref.shape[:3]) + [3])
    field[:] = (scoords - rcoords).reshape(*it.chain(ref.shape, [3]))
    field = nonlinear.DeformationField(field,
                                       src,
                                       ref,
                                       header=ref.header,
                                       defType='relative')
    return field, xform
示例#5
0
def draw3D(self, xform=None, bbox=None):
    """Draws the image in 3D on the canvas.

    :arg self:    The :class:`.GLVolume` object which is managing the image
                  to be drawn.

    :arg xform:   A 4*4 transformation matrix to be applied to the vertex
                  data.

    :arg bbox:    An optional bounding box.
    """

    opts = self.opts
    canvas = self.canvas
    copts = canvas.opts
    tex = self.renderTexture1
    proj = self.canvas.projectionMatrix
    vertices, voxCoords, texCoords = self.generateVertices3D(bbox)
    rayStep, texform = opts.calculateRayCastSettings(xform, proj)

    rayStep = affine.transformNormal(
        rayStep, self.imageTexture.texCoordXform(self.overlay.shape))
    texform = affine.concat(
        texform, self.imageTexture.invTexCoordXform(self.overlay.shape))

    # If lighting is enabled, we specify the light
    # position in image texture coordinates, to make
    # life easier for the shader
    if copts.light:
        lxform = opts.getTransform('display', 'texture')
        lightPos = affine.transform(canvas.lightPos, lxform)
    else:
        lightPos = [0, 0, 0]

    if xform is not None:
        vertices = affine.transform(vertices, xform)

    self.shader.set('lighting', copts.light)
    self.shader.set('tex2ScreenXform', texform)
    self.shader.set('rayStep', rayStep)
    self.shader.set('lightPos', lightPos)
    self.shader.setAtt('vertex', vertices)
    self.shader.setAtt('texCoord', texCoords)

    self.shader.loadAtts()

    tex.bindAsRenderTarget()
    gl.glDrawArrays(gl.GL_TRIANGLES, 0, 36)
    tex.unbindAsRenderTarget()

    self.shader.unloadAtts()
    self.shader.unload()
示例#6
0
    def __drawCursor(self):
        """Draws three lines at the current :attr:`.DisplayContext.location`.
        """

        opts = self.opts
        b = self.__displayCtx.bounds
        pos = opts.pos

        points = np.array([
            [pos.x, pos.y, b.zlo],
            [pos.x, pos.y, b.zhi],
            [pos.x, b.ylo, pos.z],
            [pos.x, b.yhi, pos.z],
            [b.xlo, pos.y, pos.z],
            [b.xhi, pos.y, pos.z],
        ],
                          dtype=np.float32)
        points = affine.transform(points, self.__viewMat)
        gl.glLineWidth(1)

        r, g, b = opts.cursorColour[:3]

        gl.glColor4f(r, g, b, 1)
        gl.glBegin(gl.GL_LINES)
        for p in points:
            gl.glVertex3f(*p)
        gl.glEnd()
示例#7
0
    def lightPos(self):
        """Takes the values of :attr:`.Scene3DOpts.lightPos` and
        :attr:`.Scene3DOpts.lightDistance`, and converts it to a position in
        the display coordinate system. The ``Scene3DOpts.lightPos`` property
        contains rotations about the centre of the display bounding box,
        and the :attr:`.Scene3DOpts.lightDistance` property specifies the
        distance of the light from the bounding box centre.
        """
        b = self.__displayCtx.bounds
        centre = np.array([
            b.xlo + 0.5 * (b.xhi - b.xlo), b.ylo + 0.5 * (b.yhi - b.ylo),
            b.zlo + 0.5 * (b.zhi - b.zlo)
        ])

        yaw, pitch, roll = self.opts.lightPos
        distance = self.opts.lightDistance
        yaw = yaw * np.pi / 180
        pitch = pitch * np.pi / 180
        roll = roll * np.pi / 180

        rotmat = affine.axisAnglesToRotMat(pitch, roll, yaw)
        xform = affine.compose([1, 1, 1], [0, 0, 0], rotmat, origin=centre)

        lightPos = centre + [0, 0, distance * b.zlen]
        lightPos = affine.transform(lightPos, xform)

        return lightPos
示例#8
0
    def __updateBounds(self):
        """Called whenever any of the :attr:`refImage`, :attr:`coordSpace`,
        or :attr:`transform` properties change.

        Updates the :attr:`.DisplayOpts.bounds` property accordingly.
        """

        # create a bounding box for the
        # overlay vertices in their
        # native coordinate system
        lo, hi = self.overlay.bounds
        xlo, ylo, zlo = lo
        xhi, yhi, zhi = hi

        # Transform the bounding box
        # into display coordinates
        xform = self.getTransform('mesh', 'display')
        bbox = list(it.product(*zip(lo, hi)))
        bbox = affine.transform(bbox, xform)

        # re-calculate the min/max bounds
        x = np.sort(bbox[:, 0])
        y = np.sort(bbox[:, 1])
        z = np.sort(bbox[:, 2])
        xlo, xhi = x.min(), x.max()
        ylo, yhi = y.min(), y.max()
        zlo, zhi = z.min(), z.max()

        oldBounds = self.bounds
        self.bounds = [xlo, xhi, ylo, yhi, zlo, zhi]

        if np.all(np.isclose(oldBounds, self.bounds)):
            self.propNotify('bounds')
示例#9
0
def _eval_coord_voxel_query(atlas, query, qtype, qin):

    voxel = qtype == 'voxel'

    if voxel: vx, vy, vz = query
    else: vx, vy, vz = affine.transform(query, atlas.worldToVoxMat)

    vx, vy, vz = [int(round(v)) for v in [vx, vy, vz]]

    def evalLabel():
        if qin in ('in', 'zero'): expval = atlas[vx, vy, vz]
        else: expval = None

        assert atlas.label(query, voxel=voxel) == expval
        assert atlas.coordLabel(query, voxel=voxel) == expval

    def evalProb():
        if qin in ('in', 'zero'):
            expval = atlas[vx, vy, vz, :]
            expval = [expval[l.index] for l in atlas.desc.labels]
        elif qin == 'out':
            expval = []

        assert atlas.values(query, voxel=voxel) == expval
        assert atlas.coordValues(query, voxel=voxel) == expval

    if isinstance(atlas, fslatlases.LabelAtlas): evalLabel()
    elif isinstance(atlas, fslatlases.ProbabilisticAtlas): evalProb()
示例#10
0
    def generateVertices(
            cls, zpos, xmin, xmax, ymin, ymax, xax, yax, xform=None):
        """Generates a set of vertices suitable for passing to the
        :meth:`.Texture2D.draw` method, for drawing a ``Texture2D`` to a 2D
        canvas.

        :arg zpos:  Position along the Z axis, in the display coordinate
                    system.
        :arg xmin:  Minimum X axis coordinate.
        :arg xmax:  Maximum X axis coordinate.
        :arg ymin:  Minimum Y axis coordinate.
        :arg ymax:  Maximum Y axis coordinate.
        :arg xax:   Display space axis which maps to the horizontal screen
                    axis.
        :arg yax:   Display space axis which maps to the vertical screen
                    axis.
        :arg xform: Transformation matrix to appply to vertices.
        """

        zax              = 3 - xax - yax
        vertices         = np.zeros((6, 3), dtype=np.float32)
        vertices[:, zax] = zpos

        vertices[ 0, [xax, yax]] = [xmin, ymin]
        vertices[ 1, [xax, yax]] = [xmin, ymax]
        vertices[ 2, [xax, yax]] = [xmax, ymin]
        vertices[ 3, [xax, yax]] = [xmax, ymin]
        vertices[ 4, [xax, yax]] = [xmin, ymax]
        vertices[ 5, [xax, yax]] = [xmax, ymax]

        if xform is not None:
            vertices = affine.transform(vertices, xform)

        return vertices
示例#11
0
    def draw2D(self, zpos, axes):
        """Draws this ``VoxelGrid`` annotation. """

        xax, yax, zax = axes
        dispLoc       = [0] * 3
        dispLoc[zax]  = zpos
        voxLoc        = affine.transform([dispLoc], self.displayToVoxMat)[0]

        vox = int(round(voxLoc[zax]))

        restrictions = [slice(None)] * 3
        restrictions[zax] = slice(vox - self.offsets[zax],
                                  vox - self.offsets[zax] + 1)

        xs, ys, zs = np.where(self.selectMask[restrictions])
        voxels     = np.vstack((xs, ys, zs)).T

        for ax in range(3):
            off = restrictions[ax].start
            if off is None:
                off = 0
            voxels[:, ax] += off + self.offsets[ax]

        verts, idxs = glroutines.voxelGrid(voxels, xax, yax, 1, 1)
        verts = verts.ravel('C')

        gl.glVertexPointer(3, gl.GL_FLOAT, 0, verts)
        gl.glDrawElements(gl.GL_LINES, len(idxs), gl.GL_UNSIGNED_INT, idxs)
示例#12
0
    def coordLabel(self, loc, voxel=False):
        """Looks up and returns the label at the given location.

        :arg loc:   A sequence of three values, interpreted as atlas
                    coordinates. In this case, :meth:`coordLabel` is called.

        :arg voxel: Defaults to ``False``. If ``True``, the ``location``
                    is interpreted as voxel coordinates.

        :returns:   The label at the given coordinates, or ``None`` if the
                    coordinates are out of bounds.

        .. note:: Use the :meth:`find` method to retrieve the ``AtlasLabel``
                  associated with each returned value.
        """

        if not voxel:
            loc = affine.transform([loc], self.worldToVoxMat)[0]
            loc = [int(v) for v in loc.round()]

        if loc[0] <  0             or \
           loc[1] <  0             or \
           loc[2] <  0             or \
           loc[0] >= self.shape[0] or \
           loc[1] >= self.shape[1] or \
           loc[2] >= self.shape[2]:
            return None

        return self[loc[0], loc[1], loc[2]]
示例#13
0
    def __prepareCoords(self, vertices, xform=None):
        """Called by :meth:`draw`. Prepares vertices, texture coordinates and
        indices for drawing the texture.

        If ``vertices is None``, it is assumed that the caller has already
        assigned vertices and texture coordinates, either via a shader, or
        via vertex/texture coordinate pointers. In this case,

        :returns: A tuple containing the vertices, texture coordinates, and
                  indices, or ``(None, None, indices)`` if
                  ``vertices is None``
        """

        indices = np.arange(6, dtype=np.uint32)

        if vertices is None:
            return None, None, indices

        if vertices.shape != (6, 3):
            raise ValueError('Six vertices must be provided')

        if xform is not None:
            vertices = affine.transform(vertices, xform)

        vertices  = np.array(vertices, dtype=np.float32).ravel('C')
        texCoords = self.generateTextureCoords()        .ravel('C')

        return vertices, texCoords, indices
示例#14
0
    def coordValues(self, loc, voxel=False):
        """Looks up the region values for the given location.

        :arg loc:   A sequence of three values, interpreted as atlas
                    world or voxel coordinates.

        :arg voxel: Defaults to ``False``. If ``True``, the ``loc``
                    argument is interpreted as voxel coordinates.

        :returns: a list of values, one per region.  Returns an empty
                  list if the given location is out of bounds.
        """

        if not voxel:
            loc = affine.transform([loc], self.worldToVoxMat)[0]
            loc = [int(v) for v in loc.round()]

        if loc[0] <  0             or \
           loc[1] <  0             or \
           loc[2] <  0             or \
           loc[0] >= self.shape[0] or \
           loc[1] >= self.shape[1] or \
           loc[2] >= self.shape[2]:
            return []

        vals = self[loc[0], loc[1], loc[2], :]

        # We only return labels for this atlas -
        # the underlying image may have more
        # volumes than this atlas has labels.
        return [vals[l.index] for l in self.desc.labels]
示例#15
0
def draw2D(self, zpos, axes, xform=None, bbox=None):
    """Draws the specified 2D slice from the specified image on the canvas.

    :arg self:    The :class:`.GLVolume` object which is managing the image
                  to be drawn.

    :arg zpos:    World Z position of slice to be drawn.

    :arg axes:    x, y, z axis indices.

    :arg xform:   A 4*4 transformation matrix to be applied to the vertex
                  data.

    :arg bbox:    An optional bounding box.
    """

    vertices, voxCoords, texCoords = self.generateVertices2D(zpos,
                                                             axes,
                                                             bbox=bbox)

    if xform is not None:
        vertices = affine.transform(vertices, xform)

    self.shader.setAtt('vertex', vertices)
    self.shader.setAtt('voxCoord', voxCoords)
    self.shader.setAtt('texCoord', texCoords)

    self.shader.loadAtts()

    gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
示例#16
0
    def draw2D(self, zpos, axes):
        """Draws this ``VoxelSelection``."""

        xax, yax     = axes[:2]
        opts         = self.__opts
        texture      = self.__texture
        shape        = self.__selection.getSelection().shape
        displayToVox = opts.getTransform('display', 'voxel')
        voxToDisplay = opts.getTransform('voxel',   'display')
        voxToTex     = opts.getTransform('voxel',   'texture')
        voxToTex     = affine.concat(texture.texCoordXform(shape), voxToTex)
        verts, voxs  = glroutines.slice2D(shape,
                                          xax,
                                          yax,
                                          zpos,
                                          voxToDisplay,
                                          displayToVox)

        texs  = affine.transform(voxs, voxToTex)[:, :texture.ndim]
        verts = np.array(verts, dtype=np.float32).ravel('C')
        texs  = np.array(texs,  dtype=np.float32).ravel('C')

        texture.bindTexture(gl.GL_TEXTURE0)
        gl.glClientActiveTexture(gl.GL_TEXTURE0)
        gl.glTexEnvf(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_MODULATE)

        with glroutines.enabled((texture.target,
                                 gl.GL_TEXTURE_COORD_ARRAY,
                                 gl.GL_VERTEX_ARRAY)):
            gl.glVertexPointer(  3,            gl.GL_FLOAT, 0, verts)
            gl.glTexCoordPointer(texture.ndim, gl.GL_FLOAT, 0, texs)
            gl.glDrawArrays(     gl.GL_TRIANGLES, 0, 6)

        texture.unbindTexture()
示例#17
0
def drawAll(self, axes, zposes, xforms):
    """Draws mutltiple slices of the given image at the given Z position,
    applying the corresponding transformation to each of the slices.
    """

    nslices = len(zposes)
    vertices = np.zeros((nslices * 6, 3), dtype=np.float32)
    texCoords = np.zeros((nslices * 6, 3), dtype=np.float32)
    indices = np.arange(nslices * 6, dtype=np.uint32)

    for i, (zpos, xform) in enumerate(zip(zposes, xforms)):

        v, vc, tc = self.generateVertices2D(zpos, axes)

        vertices[i * 6:i * 6 + 6, :] = affine.transform(v, xform)
        texCoords[i * 6:i * 6 + 6, :] = tc

    vertices = vertices.ravel('C')

    self.shader.setAtt('texCoord', texCoords)

    with glroutines.enabled((gl.GL_VERTEX_ARRAY)):
        gl.glVertexPointer(3, gl.GL_FLOAT, 0, vertices)
        gl.glDrawElements(gl.GL_TRIANGLES, nslices * 6, gl.GL_UNSIGNED_INT,
                          indices)
示例#18
0
    def generateVertices3D(self, bbox=None):
        """Generates vertex coordinates defining the 3D bounding box of the
        :class:`.Image`, with the optional ``bbox`` applied to the
        coordinates. See the :func:`.routines.boundingBox` function.

        A tuple of three values is returned, containing:

          - A ``36*3 numpy.float32`` array containing the vertex coordinates

          - A ``36*3 numpy.float32`` array containing the voxel coordinates
            corresponding to each vertex

          - A ``36*3 numpy.float32`` array containing the texture coordinates
            corresponding to each vertex
        """
        opts = self.opts
        v2dMat = opts.getTransform('voxel', 'display')
        d2vMat = opts.getTransform('display', 'voxel')
        v2tMat = opts.getTransform('voxel', 'texture')

        vertices, voxCoords = glroutines.boundingBox(self.image.shape[:3],
                                                     v2dMat,
                                                     d2vMat,
                                                     bbox=bbox)

        texCoords = affine.transform(voxCoords, v2tMat)

        return vertices, voxCoords, texCoords
示例#19
0
def test_fromFnirt():

    basefield, basexform = _random_affine_field()
    src = basefield.src
    ref = basefield.ref
    spaces = list(it.permutations(('voxel', 'fsl', 'world'), 2))

    for from_, to in spaces:

        got = fnirt.fromFnirt(basefield, from_, to)

        assert got.srcSpace == to
        assert got.refSpace == from_

        coords = [
            np.random.randint(0, basefield.shape[0], 5),
            np.random.randint(0, basefield.shape[1], 5),
            np.random.randint(0, basefield.shape[2], 5)
        ]
        coords = np.array(coords).T

        coords = affine.transform(coords, ref.getAffine('voxel', from_))

        aff = affine.concat(src.getAffine('fsl', to), basexform,
                            ref.getAffine(from_, 'fsl'))

        got = got.transform(coords)
        exp = affine.transform(coords, aff)

        enan = np.isnan(exp)
        gnan = np.isnan(got)

        assert np.all(np.isclose(enan, gnan))
        assert np.all(np.isclose(exp[~enan], got[~gnan]))

    # Converting from a FNIRT coefficient field
    src = fslimage.Image(op.join(datadir, 'src'))
    ref = fslimage.Image(op.join(datadir, 'ref'))
    coef = fnirt.readFnirt(op.join(datadir, 'coefficientfield'), src, ref)
    disp = fnirt.readFnirt(op.join(datadir, 'displacementfield'), src, ref)

    for from_, to in spaces:

        cnv = fnirt.fromFnirt(coef, from_, to)
        exp = nonlinear.convertDeformationSpace(disp, from_, to)
        tol = dict(atol=1e-5, rtol=1e-5)
        assert np.all(np.isclose(cnv.data, exp.data, **tol))
示例#20
0
    def __getMeshInfo(self, overlay, display):
        """Creates and returns an :class:`OverlayInfo` object containing
        information about the given :class:`.Mesh` overlay.

        :arg overlay: A :class:`.Mesh` instance.
        :arg display: The :class:`.Display` instance assocated with the
                      ``Mesh``.
        """

        opts = display.opts
        refImg = opts.refImage

        modelInfo = [
            ('numVertices', overlay.vertices.shape[0]),
            ('numTriangles', overlay.indices.shape[0]),
        ]

        if refImg is None:
            modelInfo.append(
                ('displaySpace', strings.labels[self, overlay, 'coordSpace',
                                                'display']))

            mesh2worldXform = np.eye(4)

        else:

            refOpts = self.displayCtx.getOpts(refImg)
            dsImg = self.displayCtx.displaySpace
            displaySpace = strings.labels[self, refImg, 'displaySpace',
                                          refOpts.transform]
            coordSpace = strings.labels[self, overlay, 'coordSpace',
                                        opts.coordSpace].format(refImg.name)
            mesh2worldXform = affine.concat(
                refOpts.getTransform('display', 'world'),
                opts.getTransform('mesh', 'display'))

            if refOpts.transform == 'reference':
                dsDisplay = self.displayCtx.getDisplay(dsImg)
                displaySpace = displaySpace.format(dsDisplay.name)

            modelInfo.append(('refImage', refImg.dataSource))
            modelInfo.append(('coordSpace', coordSpace))
            modelInfo.append(('displaySpace', displaySpace))

        bounds = affine.transform(overlay.bounds, mesh2worldXform)
        lens = bounds[1] - bounds[0]
        lens = 'X={:0.0f} mm Y={:0.0f} mm Z={:0.0f} mm'.format(*lens)

        modelInfo.append(('size', lens))

        info = OverlayInfo('{} - {}'.format(display.name,
                                            strings.labels[self, overlay]))

        info.addInfo(strings.labels[self, 'dataSource'], overlay.dataSource)

        for name, value in modelInfo:
            info.addInfo(strings.labels[self, overlay, name], value)

        return info
示例#21
0
    def updateVertices(self, *a):
        """Called by :meth:`__init__`, and when certain display properties
        change. (Re-)generates the mesh vertices, indices and normals (if
        being displayed in 3D). They are stored as attributes called
        ``vertices``, ``indices``, and ``normals`` respectively.
        """

        overlay = self.overlay
        opts = self.opts
        threedee = self.threedee
        vertices = overlay.vertices
        indices = overlay.indices
        normals = self.overlay.vnormals
        vdata = opts.getVertexData('vertex')
        xform = opts.getTransform('mesh', 'display')

        if not np.all(np.isclose(xform, np.eye(4))):
            vertices = affine.transform(vertices, xform)

            if self.threedee:
                nmat = affine.invert(xform).T
                normals = affine.transform(normals, nmat, vector=True)

        self.origIndices = indices
        indices = np.asarray(indices.flatten(), dtype=np.uint32)

        # If flatShading is active, we cannot share
        # vertices between triangles, so we generate
        # a set of unique vertices for each triangle,
        # and then re-generate the triangle indices.
        # The original indices are saved above, as
        # they will be used by the getVertexData
        # method to duplicate the vertex data.
        if threedee and (vdata is not None) and opts.flatShading:
            self.vertices = vertices[indices].astype(np.float32)
            self.indices = np.arange(0, len(self.vertices), dtype=np.uint32)
            normals = normals[indices, :]
        else:
            self.vertices = np.asarray(vertices, dtype=np.float32)
            self.indices = indices

        self.vertices = dutils.makeWriteable(self.vertices)
        self.indices = dutils.makeWriteable(self.indices)

        if self.threedee:
            self.normals = np.array(normals, dtype=np.float32)
示例#22
0
def _field_coords(field):
    vx, vy, vz = field.shape[ :3]
    coords     = np.meshgrid(np.arange(vx),
                             np.arange(vy),
                             np.arange(vz), indexing='ij')
    coords = np.array(coords).transpose((1, 2, 3, 0))
    return affine.transform(
        coords.reshape(-1, 3),
        field.getAffine('voxel', 'fsl')).reshape(field.shape)
示例#23
0
    def generateVoxelCoordinates2D(self, zpos, axes, bbox=None, space='voxel'):
        """Generates a 2D grid of voxel coordinates along the
        XY display coordinate system plane, at the given ``zpos``.

        :arg zpos:  Position along the display coordinate system Z axis.

        :arg axes:  Axis indices.

        :arg bbox:  Limiting bounding box.

        :arg space: Either ``'voxel'`` (the default) or ``'display'``.
                    If the latter, the returned coordinates are in terms
                    of the display coordinate system. Otherwise, the
                    returned coordinates are integer voxel coordinates.

        :returns: A ``numpy.float32`` array of shape ``(N, 3)``, containing
                  the coordinates for ``N`` voxels.

        See the :func:`.pointGrid` function.
        """

        if space not in ('voxel', 'display'):
            raise ValueError('Unknown value for space ("{}")'.format(space))

        image = self.image
        opts = self.opts
        v2dMat = opts.getTransform('voxel', 'display')
        d2vMat = opts.getTransform('display', 'voxel')
        xax, yax, zax = axes

        # TODO If space == voxel, you should call
        #      pointGrid to generate voxels, and
        #      avoid the subsequent transform back
        #      from display to voxel space.

        if opts.transform == 'id':
            resolution = [1, 1, 1]
        elif opts.transform in ('pixdim', 'pixdim-flip'):
            resolution = image.pixdim[:3]
        else:
            resolution = [min(image.pixdim[:3])] * 3

        voxels = glroutines.pointGrid(image.shape,
                                      resolution,
                                      v2dMat,
                                      xax,
                                      yax,
                                      bbox=bbox)[0]

        voxels[:, zax] = zpos

        if space == 'voxel':
            voxels = affine.transform(voxels, d2vMat)
            voxels = opts.roundVoxels(voxels, daxes=[zax], roundOther=False)

        return voxels
示例#24
0
def draw(self,
         glType,
         vertices,
         indices=None,
         normals=None,
         vdata=None,
         mdata=None):
    """Called for 3D meshes, and when :attr:`.MeshOpts.vertexData` is not
    ``None``. Loads and runs the shader program.

    :arg glType:   The OpenGL primitive type.

    :arg vertices: ``(n, 3)`` array containing the line vertices to draw.

    :arg indices:  Indices into the ``vertices`` array. If not provided,
                   ``glDrawArrays`` is used.

    :arg normals:  Vertex normals.

    :arg vdata:    ``(n, )`` array containing data for each vertex.

    :arg mdata:    ``(n, )`` array containing alpha modulation data for
                   each vertex.
    """

    canvas = self.canvas
    shader = self.activeShader

    # for 3D, shader attributes are
    # configured in updateShaderState
    if self.threedee:
        vertices = None
        normals = None
        vdata = None
        mdata = None

    if vertices is not None: shader.setAtt('vertex', vertices)
    if normals is not None: shader.setAtt('normal', normals)
    if vdata is not None: shader.setAtt('vertexData', vdata)
    if mdata is not None: shader.setAtt('modulateData', mdata)

    if self.threedee:
        lightPos = affine.transform(canvas.lightPos, canvas.viewMatrix)
        shader.set('lighting', canvas.opts.light)
        shader.set('lightPos', lightPos)

    shader.loadAtts()

    if indices is None:
        gl.glDrawArrays(glType, 0, vertices.shape[0])
    else:
        nverts = indices.shape[0]
        if self.threedee:
            indices = None
        gl.glDrawElements(glType, nverts, gl.GL_UNSIGNED_INT, indices)
示例#25
0
def test_transform_vector(seed):

    # Some transform with a
    # translation component
    xform = affine.compose([1, 2, 3], [5, 10, 15], [np.pi / 2, np.pi / 2, 0])

    vecs = np.random.random((20, 3))

    for v in vecs:

        vecExpected = np.dot(xform, list(v) + [0])[:3]
        ptExpected = np.dot(xform, list(v) + [1])[:3]

        vecResult = affine.transform(v, xform, vector=True)
        vec33Result = affine.transform(v, xform[:3, :3], vector=True)
        ptResult = affine.transform(v, xform, vector=False)

        assert np.all(np.isclose(vecExpected, vecResult))
        assert np.all(np.isclose(vecExpected, vec33Result))
        assert np.all(np.isclose(ptExpected, ptResult))
示例#26
0
    def __imageDataChanged(self, image, topic, sliceobj):
        """Called when the :class:`.Image` notifies about a data changes.
        Triggers an image texture refresh via a call to :meth:`set`.

        :arg image:    The ``Image`` instance

        :arg topic:    The string ``'data'``

        :arg sliceobj: Slice object specifying the portion of the image
                       that was changed.
        """

        # TODO If the change has caused the image
        #      data range to change, and texture
        #      data normalisation is on, you have
        #      to refresh the full texture.
        #
        #      The Image instance does follow up
        #      data change notifications with a
        #      data range notification; perhaps
        #      you can use this somehow.

        # If the data change was performed using
        # normal array indexing, we can just replace
        # that part of the image texture.
        if isinstance(sliceobj, tuple):

            # Get the new data, and calculate an
            # offset into the full image from the
            # slice object.
            data = np.array(image[sliceobj])
            offset = imagewrapper.sliceObjToSliceTuple(sliceobj, image.shape)
            offset = [o[0] for o in offset]

            # Make sure the data/offset are
            # compatible with 2D textures
            data = self.shapeData(data, oldShape=image.shape)
            offset[:3] = affine.transform(offset[:3],
                                          self.texCoordXform(image.shape))

            log.debug('{} data changed - refreshing part of '
                      'texture (offset: {}, size: {})'.format(
                          image.name, offset, data.shape))

            self.patchData(data, offset)

        # Otherwise (boolean array indexing) we have
        # to replace the whole image texture.
        else:

            log.debug('{} data changed - refreshing full '
                      'texture'.format(image.name))

            self.set()
示例#27
0
def broadcast(vertices, indices, zposes, xforms, zax):
    """Given a set of vertices and indices (assumed to be 2D representations
    of some geometry in a 3D space, with the depth axis specified by ``zax``),
    replicates them across all of the specified Z positions, applying the
    corresponding transformation to each set of vertices.

    :arg vertices: Vertex array (a ``N*3`` numpy array).

    :arg indices:  Index array.

    :arg zposes:   Positions along the depth axis at which the vertices
                   are to be replicated.

    :arg xforms:   Sequence of transformation matrices, one for each
                   Z position.

    :arg zax:      Index of the 'depth' axis

    Returns three values:

      - A numpy array containing all of the generated vertices

      - A numpy array containing the original vertices for each of the
        generated vertices, which may be used as texture coordinates

      - A new numpy array containing all of the generated indices.
    """

    vertices = np.array(vertices)
    indices = np.array(indices)

    nverts = vertices.shape[0]
    nidxs = indices.shape[0]

    allTexCoords = np.zeros((nverts * len(zposes), 3), dtype=np.float32)
    allVertCoords = np.zeros((nverts * len(zposes), 3), dtype=np.float32)
    allIndices = np.zeros(nidxs * len(zposes), dtype=np.uint32)

    for i, (zpos, xform) in enumerate(zip(zposes, xforms)):

        vertices[:, zax] = zpos

        vStart = i * nverts
        vEnd = vStart + nverts

        iStart = i * nidxs
        iEnd = iStart + nidxs

        allIndices[iStart:iEnd] = indices + i * nverts
        allTexCoords[vStart:vEnd, :] = vertices
        allVertCoords[vStart:vEnd, :] = affine.transform(vertices, xform)

    return allVertCoords, allTexCoords, allIndices
示例#28
0
def test_convertDeformationSpace():

    basefield, xform = _random_affine_field()
    src = basefield.src
    ref = basefield.ref

    # generate reference fsl->fsl coordinate mappings

    # For each combination of srcspace->tospace
    # Generate random coordinates, check that
    # displacements are correct
    spaces = ['fsl', 'voxel', 'world']
    spaces = list(it.combinations_with_replacement(spaces, 2))
    spaces = spaces + [(r, s) for s, r in spaces]
    spaces = list(set(spaces))

    for from_, to in spaces:

        refcoords = [
            np.random.randint(0, basefield.shape[0], 5),
            np.random.randint(0, basefield.shape[1], 5),
            np.random.randint(0, basefield.shape[2], 5)
        ]
        refcoords = np.array(refcoords, dtype=np.int).T
        refcoords = affine.transform(refcoords, ref.voxToScaledVoxMat)
        srccoords = basefield.transform(refcoords)

        field = nonlinear.convertDeformationSpace(basefield, from_, to)
        premat = ref.getAffine('fsl', from_)
        postmat = src.getAffine('fsl', to)

        input = affine.transform(refcoords, premat)
        expect = affine.transform(srccoords, postmat)

        got = field.transform(input)
        enan = np.isnan(expect)
        gnan = np.isnan(got)

        assert np.all(np.isclose(enan, gnan))
        assert np.all(np.isclose(expect[~enan], got[~gnan]))
示例#29
0
def _gen_coord_voxel_query(atlas, use_label, q_type, q_in, res):

    a_img = _get_atlas(atlas, use_label, res)
    voxel = q_type == 'voxel'

    if voxel: dtype = int
    else: dtype = float

    if q_in == 'out':

        if voxel:
            dlo = (0, 0, 0)
            dhi = a_img.shape
        else:
            dlo, dhi = affine.axisBounds(a_img.shape, a_img.voxToWorldMat)

        dlen = [hi - lo for lo, hi in zip(dlo, dhi)]

        coords = []
        for d in range(3):

            # over
            if np.random.random() > 0.5:
                coords.append(dlo[d] + dlen[d] + dlen[d] * np.random.random())
            # or under
            else:
                coords.append(dlo[d] - dlen[d] * np.random.random())

        coords = np.array(coords, dtype=dtype)

    else:

        # Make a mask which tells us which
        # voxels in the atlas are all zeros
        zmask = _get_zero_mask(a_img, atlas, use_label, res)

        # get indices to voxels which are
        # either all zero, or which are
        # not all all zero, depending on
        # the value of q_in
        if q_in == 'in': zidxs = np.where(zmask == 0)
        else: zidxs = np.where(zmask)

        # Randomly choose a voxel
        cidx = np.random.randint(0, len(zidxs[0]))
        coords = [zidxs[0][cidx], zidxs[1][cidx], zidxs[2][cidx]]
        coords = np.array(coords, dtype=dtype)

        if not voxel:
            coords = affine.transform(coords, a_img.voxToWorldMat)

    return tuple([dtype(c) for c in coords])
示例#30
0
    def generateVertices3D(self, bbox=None):
        """Overrides :meth:`.GLImageObject.generateVertices3D`.

        Appliies the :meth:`.ImageTextureBase.texCoordXform` to the texture
        coordinates - this is performed to support 2D images/textures.
        """

        vertices, voxCoords, texCoords = \
            glimageobject.GLImageObject.generateVertices3D(self, bbox)

        texCoords = affine.transform(
            texCoords, self.imageTexture.texCoordXform(self.overlay.shape))

        return vertices, voxCoords, texCoords