Esempio n. 1
0
def applySmoothing(data, matrix, newShape):
    """Called by the :func:`resample` function.

    If interpolating and smoothing, we apply a gaussian filter along axes with
    a resampling ratio greater than 1.1. We do this so that interpolation has
    an effect when down-sampling to a resolution where the voxel centres are
    aligned (as otherwise any interpolation regime will be equivalent to
    nearest neighbour). This more-or-less mimics the behaviour of FLIRT.

    See the ``scipy.ndimage.gaussian_filter`` function for more details.

    :arg data:     Data to be smoothed.
    :arg matrix:   Affine matrix to be used during resampling. The voxel
                   scaling factors are extracted from this.
    :arg newShape: Shape the data is to be resampled into.
    :returns:      A smoothed copy of ``data``.
    """

    ratio = affine.decompose(matrix[:3, :3])[0]

    if len(newShape) > 3:
        ratio = np.concatenate((
            ratio,
            [float(o) / float(s)
             for o, s in zip(data.shape[3:], newShape[3:])]))

    sigma                = np.array(ratio)
    sigma[ratio <  1.1]  = 0
    sigma[ratio >= 1.1] *= 0.425

    return ndimage.gaussian_filter(data, sigma)
Esempio n. 2
0
    def __onAffine(self, ev):
        """Called when the affine changes. If linking is enabled, updates
        the pixdim values from the new affine.
        """
        if not self.link:
            return

        scales = fslaffine.decompose(self.affine)[0]
        self.__pixdimx.SetValue(scales[0])
        self.__pixdimy.SetValue(scales[1])
        self.__pixdimz.SetValue(scales[2])
Esempio n. 3
0
    def __onPixdim(self, ev):
        """Called when any pixdim widget changes. If linking is enabled,
        reconstructs the affine with the new pixdim values.
        """
        if not self.link:
            return

        # If we construct an affine with
        # scales == 0, we get explosions
        if any([p == 0 for p in self.pixdim]):
            return

        offsets, rotations = fslaffine.decompose(self.affine)[1:]
        self.affine = fslaffine.compose(self.pixdim, offsets, rotations)
Esempio n. 4
0
    def __newImage(self):
        """Displays a :class:`NewImageDialog`, then creates a new
        :class:`.Image`, and adds it to the :class:`.OverlayList`.

        If the currently selected overlay is a :class:`.Nifti`, the
        :class:`NewImageDialog` is initialised to the properties of
        the selected overlay.
        """

        ovl = self.displayCtx.getSelectedOverlay()

        if ovl is not None and isinstance(ovl, fslimage.Nifti):
            shape = ovl.shape[:3]
            pixdim = ovl.pixdim[:3]
            dtype = ovl.dtype
            affine = ovl.voxToWorldMat
            xyzUnits = ovl.xyzUnits
            timeUnits = ovl.timeUnits

            # adjust pixdims in case there
            # are inversions (e.g. l/r flip)
            pixdim = pixdim * np.sign(fslaffine.decompose(affine)[0])
        else:
            shape = None
            pixdim = None
            dtype = None
            affine = np.eye(4)
            xyzUnits = None
            timeUnits = None

        dlg = NewImageDialog(self.__frame,
                             shape=shape,
                             pixdim=pixdim,
                             affine=affine,
                             dtype=dtype)

        if dlg.ShowModal() != wx.ID_OK:
            return

        img = newImage(dlg.shape,
                       dlg.pixdim,
                       dlg.dtype,
                       dlg.affine,
                       xyzUnits=xyzUnits,
                       timeUnits=timeUnits,
                       name='new')
        self.overlayList.append(img)
        self.displayCtx.selectOverlay(img)
Esempio n. 5
0
    def __drawLegend(self):
        """Draws a legend in the bottom left corner of the screen, showing
        anatomical orientation.
        """

        copts = self.opts
        b = self.__displayCtx.bounds
        w, h = self.GetSize()
        xlen, ylen = glroutines.adjust(b.xlen, b.ylen, w, h)

        # A line for each axis
        vertices = np.zeros((6, 3), dtype=np.float32)
        vertices[0, :] = [-1, 0, 0]
        vertices[1, :] = [1, 0, 0]
        vertices[2, :] = [0, -1, 0]
        vertices[3, :] = [0, 1, 0]
        vertices[4, :] = [0, 0, -1]
        vertices[5, :] = [0, 0, 1]

        # Each axis line is scaled to
        # 60 pixels, and the legend is
        # offset from the bottom-left
        # corner by twice this amount.
        scale = [xlen * 30.0 / w] * 3
        offset = [
            -0.5 * xlen + 2.0 * scale[0], -0.5 * ylen + 2.0 * scale[1], 0
        ]

        # Apply the current camera
        # angle and rotation settings
        # to the legend vertices. Offset
        # anatomical labels off each
        # axis line by a small amount.
        rotation = affine.decompose(self.__viewMat)[2]
        xform = affine.compose(scale, offset, rotation)
        labelPoses = affine.transform(vertices * 1.2, xform)
        vertices = affine.transform(vertices, xform)

        # Draw the legend lines
        gl.glDisable(gl.GL_DEPTH_TEST)
        gl.glColor3f(*copts.cursorColour[:3])
        gl.glLineWidth(2)
        gl.glBegin(gl.GL_LINES)
        gl.glVertex3f(*vertices[0])
        gl.glVertex3f(*vertices[1])
        gl.glVertex3f(*vertices[2])
        gl.glVertex3f(*vertices[3])
        gl.glVertex3f(*vertices[4])
        gl.glVertex3f(*vertices[5])
        gl.glEnd()

        canvas = np.array([w, h])
        view = np.array([xlen, ylen])

        # Draw each label
        for i, label in enumerate(self.__legendLabels):

            # Calculate pixel x/y
            # location for this label
            xx, xy = canvas * (labelPoses[i, :2] + 0.5 * view) / view

            label.xpix = xx
            label.ypix = xy

            label.draw(w, h)
Esempio n. 6
0
def boundingBox(dataShape,
                voxToDisplayMat,
                displayToVoxMat,
                geometry='triangles',
                origin='centre',
                bbox=None):
    """Generates a bounding box to represent a 3D image of the given shape,
    in the coordinate system defined by the ``voxToDisplayMat`` affine.

    See the :func:`slice2D` function for details on the arguments.

    Returns a tuple containing:

      - A ``N*3`` ``numpy.float32`` array containing the vertex locations
        of a bounding box ``N=36`` if ``geometry=triangles``,
        or ``N=24`` if ``geometry=square``,

      - A ``N*3`` ``numpy.float32`` array containing the voxel coordinates
        that correspond to the vertex locations.
    """

    xlo, ylo, zlo = (0, 0, 0)
    xhi, yhi, zhi = dataShape

    if origin == 'centre':
        xlo, ylo, zlo = xlo - 0.5, ylo - 0.5, zlo - 0.5
        xhi, yhi, zhi = xhi - 0.5, yhi - 0.5, zhi - 0.5

    # If the voxel -> display transformation
    # matrix contains negative scales, we need
    # to invert the voxel coordinate ranges to
    # ensure a correct triangle winding order
    # (so that back faces can be culled).
    scales = affine.decompose(voxToDisplayMat)[0]

    if scales[0] < 0: xlo, xhi = xhi, xlo
    if scales[1] < 0: ylo, yhi = yhi, ylo
    if scales[2] < 0: zlo, zhi = zhi, zlo

    if geometry == 'triangles':
        voxCoords = np.zeros((36, 3), dtype=np.float32)

        voxCoords[0, :] = (xlo, yhi, zlo)
        voxCoords[1, :] = (xhi, ylo, zlo)
        voxCoords[2, :] = (xlo, ylo, zlo)
        voxCoords[3, :] = (xlo, yhi, zlo)
        voxCoords[4, :] = (xhi, yhi, zlo)
        voxCoords[5, :] = (xhi, ylo, zlo)

        voxCoords[6, :] = (xlo, ylo, zhi)
        voxCoords[7, :] = (xhi, ylo, zhi)
        voxCoords[8, :] = (xlo, yhi, zhi)
        voxCoords[9, :] = (xlo, yhi, zhi)
        voxCoords[10, :] = (xhi, ylo, zhi)
        voxCoords[11, :] = (xhi, yhi, zhi)

        voxCoords[12, :] = (xlo, ylo, zlo)
        voxCoords[13, :] = (xhi, ylo, zlo)
        voxCoords[14, :] = (xlo, ylo, zhi)
        voxCoords[15, :] = (xlo, ylo, zhi)
        voxCoords[16, :] = (xhi, ylo, zlo)
        voxCoords[17, :] = (xhi, ylo, zhi)

        voxCoords[18, :] = (xlo, yhi, zlo)
        voxCoords[19, :] = (xhi, yhi, zlo)
        voxCoords[20, :] = (xlo, yhi, zhi)
        voxCoords[21, :] = (xlo, yhi, zhi)
        voxCoords[22, :] = (xhi, yhi, zlo)
        voxCoords[23, :] = (xhi, yhi, zhi)

        voxCoords[18, :] = (xlo, yhi, zhi)
        voxCoords[19, :] = (xhi, yhi, zlo)
        voxCoords[20, :] = (xlo, yhi, zlo)
        voxCoords[21, :] = (xlo, yhi, zhi)
        voxCoords[22, :] = (xhi, yhi, zhi)
        voxCoords[23, :] = (xhi, yhi, zlo)

        voxCoords[24, :] = (xlo, ylo, zhi)
        voxCoords[25, :] = (xlo, yhi, zlo)
        voxCoords[26, :] = (xlo, ylo, zlo)
        voxCoords[27, :] = (xlo, ylo, zhi)
        voxCoords[28, :] = (xlo, yhi, zhi)
        voxCoords[29, :] = (xlo, yhi, zlo)

        voxCoords[30, :] = (xhi, ylo, zlo)
        voxCoords[31, :] = (xhi, yhi, zlo)
        voxCoords[32, :] = (xhi, ylo, zhi)
        voxCoords[33, :] = (xhi, ylo, zhi)
        voxCoords[34, :] = (xhi, yhi, zlo)
        voxCoords[35, :] = (xhi, yhi, zhi)

    elif geometry == 'square':
        voxCoords = np.zeros((24, 3), dtype=np.float32)

        voxCoords[0, :] = (xlo, ylo, zlo)
        voxCoords[1, :] = (xhi, ylo, zlo)
        voxCoords[2, :] = (xhi, yhi, zlo)
        voxCoords[3, :] = (xlo, yhi, zlo)

        voxCoords[4, :] = (xlo, ylo, zhi)
        voxCoords[5, :] = (xhi, ylo, zhi)
        voxCoords[6, :] = (xhi, yhi, zhi)
        voxCoords[7, :] = (xlo, yhi, zhi)

        voxCoords[8, :] = (xlo, ylo, zlo)
        voxCoords[9, :] = (xhi, ylo, zlo)
        voxCoords[10, :] = (xhi, ylo, zhi)
        voxCoords[11, :] = (xlo, ylo, zhi)

        voxCoords[12, :] = (xlo, yhi, zlo)
        voxCoords[13, :] = (xhi, yhi, zlo)
        voxCoords[14, :] = (xhi, yhi, zhi)
        voxCoords[15, :] = (xlo, yhi, zhi)

        voxCoords[16, :] = (xlo, ylo, zlo)
        voxCoords[17, :] = (xlo, yhi, zlo)
        voxCoords[18, :] = (xlo, yhi, zhi)
        voxCoords[19, :] = (xlo, ylo, zhi)

        voxCoords[20, :] = (xhi, ylo, zlo)
        voxCoords[21, :] = (xhi, yhi, zlo)
        voxCoords[22, :] = (xhi, yhi, zhi)
        voxCoords[23, :] = (xhi, ylo, zhi)

    else:
        raise ValueError('Unrecognised geometry type: {}'.format(geometry))

    vertices = affine.transform(voxCoords, voxToDisplayMat)

    return vertices, voxCoords
Esempio n. 7
0
def test_compose_and_decompose():

    testfile = op.join(datadir, 'test_transform_test_compose.txt')
    lines = readlines(testfile)
    ntests = len(lines) // 4

    for i in range(ntests):

        xform = lines[i * 4:i * 4 + 4]
        xform = np.genfromtxt(xform)

        scales, offsets, rotations, shears = affine.decompose(xform,
                                                              shears=True)

        result = affine.compose(scales, offsets, rotations, shears=shears)

        assert np.all(np.isclose(xform, result, atol=1e-5))

        # The decompose function does not support a
        # different rotation origin, but we test
        # explicitly passing the origin for
        # completeness
        scales, offsets, rotations, shears = affine.decompose(xform,
                                                              shears=True)
        result = affine.compose(scales,
                                offsets,
                                rotations,
                                origin=[0, 0, 0],
                                shears=shears)

        assert np.all(np.isclose(xform, result, atol=1e-5))

    # compose should also accept a rotation matrix
    rots = [np.pi / 5, np.pi / 4, np.pi / 3]
    rmat = affine.axisAnglesToRotMat(*rots)
    xform = affine.compose([1, 1, 1], [0, 0, 0], rmat)

    # And the angles flag should cause decompose
    # to return the rotation matrix, instead of
    # the axis angls
    sc, of, rot = affine.decompose(xform)
    scat, ofat, rotat = affine.decompose(xform, angles=True)
    scaf, ofaf, rotaf = affine.decompose(xform, angles=False)

    sc, of, rot = np.array(sc), np.array(of), np.array(rot)
    scat, ofat, rotat = np.array(scat), np.array(ofat), np.array(rotat)
    scaf, ofaf, rotaf = np.array(scaf), np.array(ofaf), np.array(rotaf)

    assert np.all(np.isclose(sc, [1, 1, 1]))
    assert np.all(np.isclose(of, [0, 0, 0]))
    assert np.all(np.isclose(scat, [1, 1, 1]))
    assert np.all(np.isclose(ofat, [0, 0, 0]))
    assert np.all(np.isclose(scaf, [1, 1, 1]))
    assert np.all(np.isclose(ofaf, [0, 0, 0]))

    assert np.all(np.isclose(rot, rots))
    assert np.all(np.isclose(rotat, rots))
    assert np.all(np.isclose(rotaf, rmat))

    # decompose should accept a 3x3
    # affine, and return translations of 0
    affine.decompose(xform[:3, :3])
    sc, of, rot = affine.decompose(xform[:3, :3])
    sc, of, rot = np.array(sc), np.array(of), np.array(rot)
    assert np.all(np.isclose(sc, [1, 1, 1]))
    assert np.all(np.isclose(of, [0, 0, 0]))
    assert np.all(np.isclose(rot, rots))
Esempio n. 8
0
    def __drawLegend(self):
        """Draws a legend in the bottom left corner of the screen, showing
        anatomical orientation.
        """

        copts      = self.opts
        b          = self.__displayCtx.bounds
        w, h       = self.GetSize()
        xlen, ylen = glroutines.adjust(b.xlen, b.ylen, w, h)

        # A line for each axis
        vertices       = np.zeros((6, 3), dtype=np.float32)
        vertices[0, :] = [-1,  0,  0]
        vertices[1, :] = [ 1,  0,  0]
        vertices[2, :] = [ 0, -1,  0]
        vertices[3, :] = [ 0,  1,  0]
        vertices[4, :] = [ 0,  0, -1]
        vertices[5, :] = [ 0,  0,  1]

        # Each axis line is scaled to
        # 60 pixels, and the legend is
        # offset from the bottom-left
        # corner by twice this amount.
        scale      = [xlen * 30.0 / w] * 3
        offset     = [-0.5 * xlen + 2.0 * scale[0],
                      -0.5 * ylen + 2.0 * scale[1],
                       0]

        # Apply the current camera
        # angle and rotation settings
        # to the legend vertices. Offset
        # anatomical labels off each
        # axis line by a small amount.
        rotation   = affine.decompose(self.__viewMat)[2]
        xform      = affine.compose(scale, offset, rotation)
        labelPoses = affine.transform(vertices * 1.2, xform)
        vertices   = affine.transform(vertices,       xform)

        # Draw the legend lines
        gl.glDisable(gl.GL_DEPTH_TEST)
        gl.glColor3f(*copts.cursorColour[:3])
        gl.glLineWidth(2)
        gl.glBegin(gl.GL_LINES)
        gl.glVertex3f(*vertices[0])
        gl.glVertex3f(*vertices[1])
        gl.glVertex3f(*vertices[2])
        gl.glVertex3f(*vertices[3])
        gl.glVertex3f(*vertices[4])
        gl.glVertex3f(*vertices[5])
        gl.glEnd()


        # Figure out the anatomical
        # labels for each axis.
        overlay = self.__displayCtx.getSelectedOverlay()
        dopts   = self.__displayCtx.getOpts(overlay)
        labels  = dopts.getLabels()[0]

        # getLabels returns (xlo, ylo, zlo, xhi, yhi, zhi) -
        # - rearrange them to (xlo, xhi, ylo, yhi, zlo, zhi)
        labels = [labels[0],
                  labels[3],
                  labels[1],
                  labels[4],
                  labels[2],
                  labels[5]]

        canvas = np.array([w, h])
        view   = np.array([xlen, ylen])

        # Draw each label
        for i in range(6):

            # Calculate pixel x/y
            # location for this label
            xx, xy = canvas * (labelPoses[i, :2] + 0.5 * view) / view

            # Calculate the size of the label
            # in pixels, so we can centre the
            # label
            tw, th = glroutines.text2D(labels[i], (xx, xy), 10, (w, h),
                                       calcSize=True)

            # Draw the text
            xx -= 0.5 * tw
            xy -= 0.5 * th
            gl.glColor3f(*copts.legendColour[:3])
            glroutines.text2D(labels[i], (xx, xy), 10, (w, h))