Beispiel #1
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.getCoordSpaceTransform()

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

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

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

        if self.threedee:
            self.normals = np.array(normals, dtype=np.float32)
Beispiel #2
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 = transform.transform(vertices, xform)

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

        return vertices, texCoords, indices
Beispiel #3
0
    def __drawLight(self):

        opts = self.opts
        lightPos = np.array(opts.lightPos)
        lightPos *= (opts.zoom / 100.0)

        gl.glColor4f(1, 1, 1, 1)
        gl.glPointSize(10)
        gl.glBegin(gl.GL_POINTS)
        gl.glVertex3f(*lightPos)
        gl.glEnd()

        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)
        ])

        centre = transform.transform(centre, self.__viewMat)

        gl.glColor4f(1, 0, 1, 1)
        gl.glBegin(gl.GL_LINES)
        gl.glVertex3f(*lightPos)
        gl.glVertex3f(*centre)
        gl.glEnd()
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 = transform.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)
Beispiel #5
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')
        verts, voxs = glroutines.slice2D(shape, xax, yax, zpos, voxToDisplay,
                                         displayToVox)

        texs = transform.transform(voxs, voxToTex)
        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(
            (gl.GL_TEXTURE_3D, gl.GL_TEXTURE_COORD_ARRAY, gl.GL_VERTEX_ARRAY)):
            gl.glVertexPointer(3, gl.GL_FLOAT, 0, verts)
            gl.glTexCoordPointer(3, gl.GL_FLOAT, 0, texs)
            gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)

        texture.unbindTexture()
Beispiel #6
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, :] = transform.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)
Beispiel #7
0
    def canvasToWorld(self, xpos, ypos):
        """Transform the given x/y canvas coordinates into the display
        coordinate system.
        """

        b = self.__displayCtx.bounds
        width, height = self.GetSize()

        # The first step is to invert the mouse
        # coordinates w.r.t. the viewport.
        #
        # The canvas x axis corresponds to
        # (-xhalf, xhalf), and the canvas y
        # corresponds to (-yhalf, yhalf) -
        # see routines.show3D.
        xlen, ylen = glroutines.adjust(b.xlen, b.ylen, width, height)
        xhalf = 0.5 * xlen
        yhalf = 0.5 * ylen

        # Pixels to viewport coordinates
        xpos = xlen * (xpos / float(width)) - xhalf
        ypos = ylen * (ypos / float(height)) - yhalf

        # The second step is to transform from
        # viewport coords into model-view coords.
        # This is easy - transform by the inverse
        # MV matrix.
        #
        # z=-1 because the camera is offset by 1
        # on the depth axis (see __setViewport).
        pos = np.array([xpos, ypos, -1])
        xform = transform.invert(self.__viewMat)
        pos = transform.transform(pos, xform)

        return pos
Beispiel #8
0
    def draw2D(self, zpos, axes):
        """Draws this ``VoxelGrid`` annotation. """

        xax, yax, zax = axes
        dispLoc = [0] * 3
        dispLoc[zax] = zpos
        voxLoc = transform.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)
Beispiel #9
0
    def draw2D(self, zpos, axes):
        """Draws this ``VoxelSelection``."""

        xax, yax = axes[:2]
        shape = self.selection.getSelection().shape

        verts, voxs = glroutines.slice2D(shape, xax, yax, zpos,
                                         self.voxToDisplayMat,
                                         self.displayToVoxMat)

        texs = transform.transform(voxs, self.voxToTexMat)
        verts = np.array(verts, dtype=np.float32).ravel('C')
        texs = np.array(texs, dtype=np.float32).ravel('C')

        self.texture.bindTexture(gl.GL_TEXTURE0)

        gl.glTexEnvf(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_MODULATE)

        gl.glEnable(gl.GL_TEXTURE_3D)
        gl.glEnableClientState(gl.GL_TEXTURE_COORD_ARRAY)

        gl.glVertexPointer(3, gl.GL_FLOAT, 0, verts)
        gl.glTexCoordPointer(3, gl.GL_FLOAT, 0, texs)
        gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)

        gl.glDisableClientState(gl.GL_TEXTURE_COORD_ARRAY)
        gl.glDisable(gl.GL_TEXTURE_3D)

        self.texture.unbindTexture()
Beispiel #10
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 = transform.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]]
Beispiel #11
0
def updateShaderState(self):
    """Sets all variables required by the vertex and fragment programs. """

    if not self.ready():
        return

    opts   = self.opts
    shader = self.shader

    # enable the vertex and fragment programs
    shader.load()

    # The voxValXform transformation turns
    # an image texture value into a raw
    # voxel value. The colourMapXform
    # transformation turns a raw voxel value
    # into a value between 0 and 1, suitable
    # for looking up an appropriate colour
    # in the 1D colour map texture.
    voxValXform = transform.concat(self.colourTexture.getCoordinateTransform(),
                                   self.imageTexture.voxValXform)
    voxValXform = [voxValXform[0, 0], voxValXform[0, 3], 0, 0]

    # And the clipping range, normalised
    # to the image texture value range
    invClip     = 1 if opts.invertClipping    else -1
    useNegCmap  = 1 if opts.useNegativeCmap   else  0
    imageIsClip = 1 if opts.clipImage is None else -1

    imgXform = self.imageTexture.invVoxValXform
    if opts.clipImage is None: clipXform = imgXform
    else:                      clipXform = self.clipTexture.invVoxValXform

    clipLo  = opts.clippingRange[0] * clipXform[0, 0] + clipXform[0, 3]
    clipHi  = opts.clippingRange[1] * clipXform[0, 0] + clipXform[0, 3]
    texZero = 0.0                   * imgXform[ 0, 0] + imgXform[ 0, 3]

    clipping = [clipLo, clipHi, invClip, imageIsClip]
    negCmap  = [useNegCmap, texZero, 0, 0]

    changed  = False
    changed |= shader.setFragParam('voxValXform', voxValXform)
    changed |= shader.setFragParam('clipping',    clipping)
    changed |= shader.setFragParam('negCmap',     negCmap)

    if self.threedee:
        clipPlanes  = np.zeros((5, 4), dtype=np.float32)
        d2tmat      = opts.getTransform('display', 'texture')

        for i in range(opts.numClipPlanes):
            origin, normal   = self.get3DClipPlane(i)
            origin           = transform.transform(origin, d2tmat)
            normal           = transform.transformNormal(normal, d2tmat)
            clipPlanes[i, :] = glroutines.planeEquation2(origin, normal)

        changed |= shader.setFragParam('clipPlanes', clipPlanes)

    self.shader.unload()

    return changed
Beispiel #12
0
    def coordProportions(self, loc, voxel=False):
        """Looks up the region probabilities 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, which represent
                  the probability of each region for the specified
                  location. Returns an empty list if the given
                  location is out of bounds.
        """

        if not voxel:
            loc = transform.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 []

        props = 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 [props[l.index] for l in self.desc.labels]
Beispiel #13
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 = transform.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()
Beispiel #14
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
    tex = self.renderTexture1
    proj = self.canvas.projectionMatrix
    vertices, voxCoords, texCoords = self.generateVertices3D(bbox)
    rayStep, texform = opts.calculateRayCastSettings(xform, proj)

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

    self.shader.set('tex2ScreenXform', texform)
    self.shader.set('rayStep', rayStep)
    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()
Beispiel #15
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 = transform.transform(voxCoords, v2tMat)

        return vertices, voxCoords, texCoords
Beispiel #16
0
def _eval_coord_voxel_query(atlas, query, qtype, qin):

    voxel = qtype == 'voxel'

    if voxel: vx, vy, vz = query
    else: vx, vy, vz = transform.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.proportions(query, voxel=voxel) == expval
        assert atlas.coordProportions(query, voxel=voxel) == expval

    if isinstance(atlas, fslatlases.LabelAtlas): evalLabel()
    elif isinstance(atlas, fslatlases.ProbabilisticAtlas): evalProb()
    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 = transform.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 = transform.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
Beispiel #18
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 = transform.transform(voxels, d2vMat)
            voxels = opts.roundVoxels(voxels, daxes=[zax], roundOther=False)

        return voxels
Beispiel #19
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, :] = transform.transform(vertices, xform)

    return allVertCoords, allTexCoords, allIndices
Beispiel #20
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 = transform.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 = transform.transform(coords, a_img.voxToWorldMat)

    return tuple([dtype(c) for c in coords])
Beispiel #21
0
    def draw3D(self, *args, **kwargs):
        """Calls the version dependent ``draw3D`` function. """

        opts = self.opts
        w, h = self.canvas.GetSize()
        res = self.opts.resolution / 100.0
        w = int(np.ceil(w * res))
        h = int(np.ceil(h * res))

        # Initialise and resize
        # the offscreen textures
        for rt in [self.renderTexture1, self.renderTexture2]:
            if rt.getSize() != (w, h):
                rt.setSize(w, h)

            rt.bindAsRenderTarget()
            gl.glClearColor(0, 0, 0, 0)
            gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
            rt.unbindAsRenderTarget()

        if opts.resolution != 100:
            gl.glViewport(0, 0, w, h)

        # Do the render. Even though we're
        # drawing off-screen,  we need to
        # enable depth-testing, otherwise
        # depth values will not get written
        # to the depth buffer!
        with glroutines.enabled((gl.GL_DEPTH_TEST, gl.GL_CULL_FACE)):
            gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL)
            gl.glFrontFace(gl.GL_CCW)
            gl.glCullFace(gl.GL_BACK)
            fslgl.glvolume_funcs.draw3D(self, *args, **kwargs)

        # renderTexture1 should now
        # contain the final result -
        # draw it to the screen.
        verts = np.array([[-1, -1, 0], [-1, 1, 0], [1, -1, 0], [1, -1, 0],
                          [-1, 1, 0], [1, 1, 0]],
                         dtype=np.float32)

        invproj = transform.invert(self.canvas.getProjectionMatrix())
        verts = transform.transform(verts, invproj)

        if opts.resolution != 100:
            w, h = self.canvas.GetSize()
            gl.glViewport(0, 0, w, h)

        with glroutines.enabled((gl.GL_DEPTH_TEST)):
            self.renderTexture1.draw(verts, useDepth=True)
Beispiel #22
0
def pointGrid3D(shape, xform=None, origin='centre', bbox=None):
    """Generates a 3D grid of points into an image of the given ``shape``,
    with the given ``xform`` defining the index to display coordinate
    transform.

    note: Not implemented properly yet.
    """

    coords = np.indices(shape).transpose(1, 2, 3, 0).reshape(-1, 3)

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

    return coords
Beispiel #23
0
    def generateVertices2D(self, zpos, axes, bbox=None):
        """Generates vertex coordinates for a 2D slice of the :class:`.Image`,
        through the given ``zpos``, with the optional ``bbox`` applied to the
        coordinates.


        This is a convenience method for generating vertices which can be used
        to render a slice through a 3D texture. It is used by the
        :mod:`.gl14.glvolume_funcs` and :mod:`.gl21.glvolume_funcs` (and other)
        modules.


        A tuple of three values is returned, containing:

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

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

          - A ``6*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')
        xax, yax, zax = axes

        vertices, voxCoords = glroutines.slice2D(
            self.image.shape[:3],
            xax,
            yax,
            zpos,
            v2dMat,
            d2vMat,
            bbox=bbox)

        # If not interpolating, centre the
        # voxel coordinates on the Z/depth
        # axis. We do this to avoid rounding
        # bias when the display Z position is
        # on a voxel boundary.
        if not hasattr(opts, 'interpolation') or opts.interpolation == 'none':
            voxCoords = opts.roundVoxels(voxCoords, daxes=[zax])

        texCoords = transform.transform(voxCoords, v2tMat)

        return vertices, voxCoords, texCoords
Beispiel #24
0
    def drawClipPlanes(self, xform=None, bbox=None):
        """A convenience method for use with overlays being displayed
        in terms of a :class:`.Volume3DOpts` instance.

        Draws the active clipping planes, as specified by the
        :class:`.Volume3DOpts` clipping properties.

        :arg xform: A transformation matrix to apply to the clip plane
                    vertices before drawing them.

        :arg bbox:  A bounding box by which the clip planes can be limited
                    (not currently honoured).
        """

        if not self.opts.showClipPlanes:
            return

        for i in range(self.opts.numClipPlanes):

            verts, idxs = self.clipPlaneVertices(i, bbox)

            if len(idxs) == 0:
                continue

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

            verts = np.array(verts.ravel('C'), dtype=np.float32, copy=False)

            # A consistent colour for
            # each clipping plane
            rgb = self.__clipPlaneColours.get(i, None)
            if rgb is None:
                rgb = fslcmaps.randomBrightColour()[:3]
                self.__clipPlaneColours[i] = rgb

            r, g, b = rgb

            with glroutines.enabled(gl.GL_VERTEX_ARRAY):

                gl.glColor4f(r, g, b, 0.3)
                gl.glVertexPointer(3, gl.GL_FLOAT, 0, verts)
                gl.glDrawElements(gl.GL_TRIANGLES,
                                  len(idxs),
                                  gl.GL_UNSIGNED_INT,
                                  idxs)
Beispiel #25
0
    def calculateRayCastSettings(self, viewmat):
        """Calculates a camera direction and ray casting step vector, based
        on the given view matrix.
        """

        d2tmat = self.getTransform('display', 'texture')
        xform = transform.concat(d2tmat, viewmat)
        cdir = np.array([0, 0, 1])
        cdir = transform.transform(cdir, xform, vector=True)
        cdir = transform.normalise(cdir)

        # sqrt(3) so the maximum number
        # of samplews is taken along the
        # diagonal of a cube
        rayStep = np.sqrt(3) * cdir / self.numSteps

        return cdir, rayStep
Beispiel #26
0
    def draw(self, vertices, xform=None):
        """Draw the contents of this ``Texture2D`` to a region specified by
        the given vertices. The texture is bound to texture unit 0.

        :arg vertices: A ``numpy`` array of shape ``6 * 3`` specifying the
                       region, made up of two triangles, to which this
                       ``Texture2D`` should be drawn.

        :arg xform:    A transformation to be applied to the vertices.
        """

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

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

        vertices = np.array(vertices, dtype=np.float32)
        texCoords = np.zeros((6, 2), dtype=np.float32)
        indices = np.arange(6, dtype=np.uint32)

        texCoords[0, :] = [0, 0]
        texCoords[1, :] = [0, 1]
        texCoords[2, :] = [1, 0]
        texCoords[3, :] = [1, 0]
        texCoords[4, :] = [0, 1]
        texCoords[5, :] = [1, 1]

        vertices = vertices.ravel('C')
        texCoords = texCoords.ravel('C')

        self.bindTexture(gl.GL_TEXTURE0)

        gl.glClientActiveTexture(gl.GL_TEXTURE0)

        gl.glTexEnvf(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_REPLACE)

        with glroutines.enabled(
            (gl.GL_TEXTURE_2D, gl.GL_TEXTURE_COORD_ARRAY, gl.GL_VERTEX_ARRAY)):

            gl.glVertexPointer(3, gl.GL_FLOAT, 0, vertices)
            gl.glTexCoordPointer(2, gl.GL_FLOAT, 0, texCoords)
            gl.glDrawElements(gl.GL_TRIANGLES, 6, gl.GL_UNSIGNED_INT, indices)

        self.unbindTexture()
Beispiel #27
0
def draw2D(self, zpos, axes, xform=None, bbox=None):
    """Draws a 2D slice of the image at the given Z location. """

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

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

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

    # Voxel coordinates are calculated
    # in the vertex program
    self.shader.setAtt('texCoord', texCoords)

    with glroutines.enabled((gl.GL_VERTEX_ARRAY)):
        gl.glVertexPointer(3, gl.GL_FLOAT, 0, vertices)
        gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
Beispiel #28
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.
        """

        lo, hi = self.overlay.bounds
        xform = self.getTransform('mesh', 'display')

        lohi = transform.transform([lo, hi], xform)
        lohi.sort(axis=0)
        lo, hi = lohi[0, :], lohi[1, :]

        oldBounds = self.bounds
        self.bounds = [lo[0], hi[0], lo[1], hi[1], lo[2], hi[2]]

        if np.all(np.isclose(oldBounds, self.bounds)):
            self.propNotify('bounds')
Beispiel #29
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 = transform.transform(vertices, xform)

        return vertices
Beispiel #30
0
    def __drawBoundingBox(self):
        b = self.__displayCtx.bounds
        xlo, xhi = b.x
        ylo, yhi = b.y
        zlo, zhi = b.z
        xlo += 0.1
        xhi -= 0.1
        vertices = np.array([
            [xlo, ylo, zlo],
            [xlo, ylo, zhi],
            [xlo, yhi, zlo],
            [xlo, yhi, zhi],
            [xhi, ylo, zlo],
            [xhi, ylo, zhi],
            [xhi, yhi, zlo],
            [xhi, yhi, zhi],
            [xlo, ylo, zlo],
            [xlo, yhi, zlo],
            [xhi, ylo, zlo],
            [xhi, yhi, zlo],
            [xlo, ylo, zhi],
            [xlo, yhi, zhi],
            [xhi, ylo, zhi],
            [xhi, yhi, zhi],
            [xlo, ylo, zlo],
            [xhi, ylo, zlo],
            [xlo, ylo, zhi],
            [xhi, ylo, zhi],
            [xlo, yhi, zlo],
            [xhi, yhi, zlo],
            [xlo, yhi, zhi],
            [xhi, yhi, zhi],
        ])
        vertices = transform.transform(vertices, self.__viewMat)

        gl.glLineWidth(2)
        gl.glColor3f(0.5, 0, 0)
        gl.glBegin(gl.GL_LINES)
        for v in vertices:
            gl.glVertex3f(*v)
        gl.glEnd()