Exemple #1
0
def mask_parcellation(mask_images, nb_parcel, threshold=0, output_image=None):
    """ Performs the parcellation of a certain mask

    Parameters
    ----------
    mask_images: string or Nifti1Image or list of strings/Nifti1Images,
                 paths of mask image(s) that define(s) the common space.
    nb_parcel: int,
               number of desired parcels
    threshold: float, optional,
               level of intersection of the masks
    output_image: string, optional
                  path of the output image

    Returns
    -------
    wim: Nifti1Imagine instance,  representing the resulting parcellation
    """
    if isinstance(mask_images, basestring):
        mask = mask_images
    elif isinstance(mask_images, Nifti1Image):
        mask = mask_images
    else:
        # mask_images should be a list
        mask_data = intersect_masks(mask_images, threshold=0) > 0
        mask = Nifti1Image(mask_data.astype('u8'),
                           load(mask_images[0]).get_affine())

    domain = grid_domain_from_image(mask)
    cent, labels, J = kmeans(domain.coord, nb_parcel)
    sub_dom = SubDomains(domain, labels)
    # get id (or labels) image
    wim = sub_dom.to_image(fid='id', roi=True)
    return wim
Exemple #2
0
def mask_parcellation(mask_images, nb_parcel, threshold=0, output_image=None):
    """ Performs the parcellation of a certain mask

    Parameters
    ----------
    mask_images: string or Nifti1Image or list of strings/Nifti1Images,
                 paths of mask image(s) that define(s) the common space.
    nb_parcel: int,
               number of desired parcels
    threshold: float, optional,
               level of intersection of the masks
    output_image: string, optional
                  path of the output image

    Returns
    -------
    wim: Nifti1Imagine instance,  representing the resulting parcellation
    """
    if isinstance(mask_images, basestring):
        mask = mask_images
    elif isinstance(mask_images, Nifti1Image):
        mask = mask_images
    else:
        # mask_images should be a list
        mask_data = intersect_masks(mask_images, threshold=0) > 0
        mask = Nifti1Image(mask_data.astype('u8'),
                           get_affine(load(mask_images[0])))

    domain = grid_domain_from_image(mask)
    cent, labels, J = kmeans(domain.coord, nb_parcel)
    sub_dom = SubDomains(domain, labels)
    # get id (or labels) image
    wim = sub_dom.to_image(fid='id', roi=True)
    return wim
Exemple #3
0
def fixed_parcellation(mask_image, betas, nbparcel, nn=6, method='ward',
                          write_dir=None, mu=10., verbose=0, fullpath=None):
    """ Fixed parcellation of a given dataset

    Parameters
    ----------
    domain/mask_image
    betas: list of paths to activation images from the subject
    nbparcel, int : number fo desired parcels
    nn=6: number of nearest neighbors  to define the image topology
          (6, 18 or 26)
    method='ward': clustering method used, to be chosen among
                   'ward', 'gkm', 'ward_and-gkm'
                   'ward': Ward's clustering algorithm
                   'gkm': Geodesic k-means algorithm, random initialization
                   'gkm_and_ward': idem, initialized by Ward's clustering
    write_di: string, topional, write directory.
                    If fullpath is None too, then no file output.
    mu = 10., float: the relative weight of anatomical information
    verbose=0: verbosity mode
    fullpath=None, string,
                   path of the output image
                   If write_dir and fullpath are None then no file output.
                   If only fullpath is None then it is the write dir + a name
                   depending on the method.

    Notes
    -----
    Ward's method takes time (about 6 minutes for a 60K voxels dataset)

    Geodesic k-means is 'quick and dirty'

    Ward's + GKM is expensive but quite good

    To reduce CPU time, rather use nn=6 (especially with Ward)
    """
    from nipy.algorithms.graph.field import field_from_coo_matrix_and_data

    if method not in ['ward', 'gkm', 'ward_and_gkm', 'kmeans']:
        raise ValueError('unknown method')
    if nn not in [6, 18, 26]:
        raise ValueError('nn should be 6,18 or 26')

    # step 1: load the data ----------------------------
    # 1.1 the domain
    domain = grid_domain_from_image(mask_image, nn)

    if method is not 'kmeans':
        # 1.2 get the main cc of the graph
        # to remove the small connected components
        pass

    coord = domain.coord

    # 1.3 read the functional data
    beta = np.array([domain.make_feature_from_image(b) for b in betas])

    if len(beta.shape) > 2:
        beta = np.squeeze(beta)

    if beta.shape[0] != domain.size:
        beta = beta.T

    feature = np.hstack((beta, mu * coord / np.std(coord)))

    #step 2: parcellate the data ---------------------------

    if method is not 'kmeans':
        g = field_from_coo_matrix_and_data(domain.topology, feature)

    if method == 'kmeans':
        _, u, _ = kmeans(feature, nbparcel)

    if method == 'ward':
        u, _ = g.ward(nbparcel)

    if method == 'gkm':
        seeds = np.argsort(np.random.rand(g.V))[:nbparcel]
        _, u, _ = g.geodesic_kmeans(seeds)

    if method == 'ward_and_gkm':
        w, _ = g.ward(nbparcel)
        _, u, _ = g.geodesic_kmeans(label=w)

    lpa = SubDomains(domain, u)

    if verbose:
        var_beta = np.array(
            [np.var(beta[lpa.label == k], 0).sum() for k in range(lpa.k)])
        var_coord = np.array(
            [np.var(coord[lpa.label == k], 0).sum() for k in range(lpa.k)])
        size = lpa.get_size()
        vf = np.dot(var_beta, size) / size.sum()
        va = np.dot(var_coord, size) / size.sum()
        print nbparcel, "functional variance", vf, "anatomical variance", va

    # step3:  write the resulting label image
    if fullpath is not None:
        label_image = fullpath
    elif write_dir is not None:
        label_image = os.path.join(write_dir, "parcel_%s.nii" % method)
    else:
        label_image = None

    if label_image is not None:
        lpa_img = lpa.to_image(
            fid='id', roi=True, descrip='Intra-subject parcellation image')
        save(lpa_img, label_image)
        if verbose:
            print "Wrote the parcellation images as %s" % label_image

    return lpa
def _optim_hparcel(feature, domain, graphs, nb_parcel, lamb=1., dmax=10.,
                   niter=5, initial_mask=None, chunksize=1.e5, verbose=0):
    """ Core function of the heirrachical parcellation procedure.

    Parameters
    ----------
    feature: list of subject-related feature arrays
    Pa : parcellation instance that is updated
    graphs: graph that represents the topology of the parcellation
    anat_coord: array of shape (nvox,3) space defining set of coordinates
    nb_parcel: int
               the number of desrired parcels
    lamb=1.0: parameter to weight position
              and feature impact on the algorithm
    dmax = 10: locality parameter (in the space of anat_coord)
              to limit surch volume (CPU save)
    chunksize = int, optional
    niter = 5: number of iterations in the algorithm
    verbose=0: verbosity level

    Returns
    -------
    U: list of arrays of length nsubj
       subject-dependent parcellations
    Proto_anat: array of shape (nvox) labelling of the common space
                (template parcellation)
    """
    nb_subj = len(feature)

    # a1. perform a rough clustering of the data to make prototype
    indiv_coord = np.array([domain.coord[initial_mask[:, s] > - 1]
                             for s in range(nb_subj)])
    reduced_anat, reduced_feature = _reduce_and_concatenate(
        indiv_coord, feature, chunksize)

    _, labs, _ = kmeans(reduced_feature, nb_parcel, Labels=None, maxiter=10)
    proto_anat = [np.mean(reduced_anat[labs == k], 0)
                  for k in range(nb_parcel)]
    proto_anat = np.array(proto_anat)
    proto = [np.mean(reduced_feature[labs == k], 0) for k in range(nb_parcel)]
    proto = np.array(proto)

    # a2. topological model of the parcellation
    # group-level part
    spatial_proto = Field(nb_parcel)
    spatial_proto.set_field(proto_anat)
    spatial_proto.voronoi_diagram(proto_anat, domain.coord)
    spatial_proto.set_gaussian(proto_anat)
    spatial_proto.normalize()

    for git in range(niter):
        LP = []
        LPA = []
        U = []
        Energy = 0
        for s in range(nb_subj):
            # b.subject-specific instances of the model
            # b.0 subject-specific information
            Fs = feature[s]
            lac = indiv_coord[s]
            target = proto_anat.copy()
            lseeds = np.zeros(nb_parcel, np.int)
            aux = np.argsort(rand(nb_parcel))
            toto = np.zeros(lac.shape[0])
            for j in range(nb_parcel):
                # b.1 speed-up :only take a small ball
                i = aux[j]
                dx = lac - target[i]
                iz = np.nonzero(np.sum(dx ** 2, 1) < dmax ** 2)
                iz = np.reshape(iz, np.size(iz))
                if np.size(iz) == 0:
                    iz = np.array([np.argmin(np.sum(dx ** 2, 1))])

                # b.2: anatomical constraints
                lanat = np.reshape(lac[iz], (np.size(iz),
                                             domain.coord.shape[1]))
                pot = np.zeros(np.size(iz))
                JM, rmin = _exclusion_map(i, spatial_proto, target, lanat)
                pot[JM < 0] = np.inf
                pot[JM >= 0] = - JM[JM >= 0]

                # b.3: add feature discrepancy
                df = Fs[iz] - proto[i]
                df = np.reshape(df, (np.size(iz), proto.shape[1]))
                pot += lamb * np.sum(df ** 2, 1)

                # b.4: solution
                if np.sum(np.isinf(pot)) == np.size(pot):
                    pot = np.sum(dx[iz] ** 2, 1)

                sol = iz[np.argmin(pot)]
                target[i] = lac[sol]
                lseeds[i] = sol
                toto[sol] = 1

            if verbose > 1:
                jm = _field_gradient_jac(spatial_proto, target)
                print jm.min(), jm.max(), np.sum(toto > 0)

            # c.subject-specific parcellation
            g = graphs[s]
            f = Field(g.V, g.edges, g.weights, Fs)
            U.append(f.constrained_voronoi(lseeds))

            Energy += np.sum((Fs - proto[U[-1]]) ** 2) / \
                np.sum(initial_mask[:, s] > - 1)
            # recompute the prototypes
            # (average in subject s)
            lproto = [np.mean(Fs[U[-1] == k], 0) for k in range(nb_parcel)]
            lproto = np.array(lproto)
            lproto_anat = np.array([np.mean(lac[U[-1] == k], 0)
                                    for k in range(nb_parcel)])

            LP.append(lproto)
            LPA.append(lproto_anat)

        # recompute the prototypes across subjects
        proto_mem = proto.copy()
        proto = np.mean(np.array(LP), 0)
        proto_anat = np.mean(np.array(LPA), 0)
        displ = np.sqrt(np.sum((proto_mem - proto) ** 2, 1).max())
        if verbose:
            print 'energy', Energy, 'displacement', displ

        # recompute the topological model
        spatial_proto.set_field(proto_anat)
        spatial_proto.voronoi_diagram(proto_anat, domain.coord)
        spatial_proto.set_gaussian(proto_anat)
        spatial_proto.normalize()

        if displ < 1.e-4 * dmax:
            break
    return U, proto_anat
Exemple #5
0
def fixed_parcellation(mask_image, betas, nbparcel, nn=6, method='ward',
                          write_dir=None, mu=10., verbose=0, fullpath=None):
    """ Fixed parcellation of a given dataset

    Parameters
    ----------
    domain/mask_image
    betas: list of paths to activation images from the subject
    nbparcel, int : number fo desired parcels
    nn=6: number of nearest neighbors  to define the image topology
          (6, 18 or 26)
    method='ward': clustering method used, to be chosen among
                   'ward', 'gkm', 'ward_and-gkm'
                   'ward': Ward's clustering algorithm
                   'gkm': Geodesic k-means algorithm, random initialization
                   'gkm_and_ward': idem, initialized by Ward's clustering
    write_di: string, topional, write directory.
                    If fullpath is None too, then no file output.
    mu = 10., float: the relative weight of anatomical information
    verbose=0: verbosity mode
    fullpath=None, string,
                   path of the output image
                   If write_dir and fullpath are None then no file output.
                   If only fullpath is None then it is the write dir + a name
                   depending on the method.

    Notes
    -----
    Ward's method takes time (about 6 minutes for a 60K voxels dataset)

    Geodesic k-means is 'quick and dirty'

    Ward's + GKM is expensive but quite good

    To reduce CPU time, rather use nn=6 (especially with Ward)
    """
    from nipy.algorithms.graph.field import field_from_coo_matrix_and_data

    if method not in ['ward', 'gkm', 'ward_and_gkm', 'kmeans']:
        raise ValueError('unknown method')
    if nn not in [6, 18, 26]:
        raise ValueError('nn should be 6,18 or 26')

    # step 1: load the data ----------------------------
    # 1.1 the domain
    domain = grid_domain_from_image(mask_image, nn)

    if method is not 'kmeans':
        # 1.2 get the main cc of the graph
        # to remove the small connected components
        pass

    coord = domain.coord

    # 1.3 read the functional data
    beta = np.array([domain.make_feature_from_image(b) for b in betas])

    if len(beta.shape) > 2:
        beta = np.squeeze(beta)

    if beta.shape[0] != domain.size:
        beta = beta.T

    feature = np.hstack((beta, mu * coord / np.std(coord)))

    #step 2: parcellate the data ---------------------------

    if method is not 'kmeans':
        g = field_from_coo_matrix_and_data(domain.topology, feature)

    if method == 'kmeans':
        _, u, _ = kmeans(feature, nbparcel)

    if method == 'ward':
        u, _ = g.ward(nbparcel)

    if method == 'gkm':
        seeds = np.argsort(np.random.rand(g.V))[:nbparcel]
        _, u, _ = g.geodesic_kmeans(seeds)

    if method == 'ward_and_gkm':
        w, _ = g.ward(nbparcel)
        _, u, _ = g.geodesic_kmeans(label=w)

    lpa = SubDomains(domain, u)

    if verbose:
        var_beta = np.array(
            [np.var(beta[lpa.label == k], 0).sum() for k in range(lpa.k)])
        var_coord = np.array(
            [np.var(coord[lpa.label == k], 0).sum() for k in range(lpa.k)])
        size = lpa.get_size()
        vf = np.dot(var_beta, size) / size.sum()
        va = np.dot(var_coord, size) / size.sum()
        print nbparcel, "functional variance", vf, "anatomical variance", va

    # step3:  write the resulting label image
    if fullpath is not None:
        label_image = fullpath
    elif write_dir is not None:
        label_image = os.path.join(write_dir, "parcel_%s.nii" % method)
    else:
        label_image = None

    if label_image is not None:
        lpa_img = lpa.to_image(
            fid='id', roi=True, descrip='Intra-subject parcellation image')
        save(lpa_img, label_image)
        if verbose:
            print "Wrote the parcellation images as %s" % label_image

    return lpa
def _optim_hparcel(feature,
                   domain,
                   graphs,
                   nb_parcel,
                   lamb=1.,
                   dmax=10.,
                   niter=5,
                   initial_mask=None,
                   chunksize=1.e5,
                   verbose=0):
    """ Core function of the heirrachical parcellation procedure.

    Parameters
    ----------
    feature: list of subject-related feature arrays
    Pa : parcellation instance that is updated
    graphs: graph that represents the topology of the parcellation
    anat_coord: array of shape (nvox,3) space defining set of coordinates
    nb_parcel: int
               the number of desrired parcels
    lamb=1.0: parameter to weight position
              and feature impact on the algorithm
    dmax = 10: locality parameter (in the space of anat_coord)
              to limit surch volume (CPU save)
    chunksize = int, optional
    niter = 5: number of iterations in the algorithm
    verbose=0: verbosity level

    Returns
    -------
    U: list of arrays of length nsubj
       subject-dependent parcellations
    Proto_anat: array of shape (nvox) labelling of the common space
                (template parcellation)
    """
    nb_subj = len(feature)

    # a1. perform a rough clustering of the data to make prototype
    indiv_coord = np.array(
        [domain.coord[initial_mask[:, s] > -1] for s in range(nb_subj)])
    reduced_anat, reduced_feature = _reduce_and_concatenate(
        indiv_coord, feature, chunksize)

    _, labs, _ = kmeans(reduced_feature, nb_parcel, Labels=None, maxiter=10)
    proto_anat = [
        np.mean(reduced_anat[labs == k], 0) for k in range(nb_parcel)
    ]
    proto_anat = np.array(proto_anat)
    proto = [np.mean(reduced_feature[labs == k], 0) for k in range(nb_parcel)]
    proto = np.array(proto)

    # a2. topological model of the parcellation
    # group-level part
    spatial_proto = Field(nb_parcel)
    spatial_proto.set_field(proto_anat)
    spatial_proto.voronoi_diagram(proto_anat, domain.coord)
    spatial_proto.set_gaussian(proto_anat)
    spatial_proto.normalize()

    for git in range(niter):
        LP = []
        LPA = []
        U = []
        Energy = 0
        for s in range(nb_subj):
            # b.subject-specific instances of the model
            # b.0 subject-specific information
            Fs = feature[s]
            lac = indiv_coord[s]
            target = proto_anat.copy()
            lseeds = np.zeros(nb_parcel, np.int)
            aux = np.argsort(rand(nb_parcel))
            toto = np.zeros(lac.shape[0])
            for j in range(nb_parcel):
                # b.1 speed-up :only take a small ball
                i = aux[j]
                dx = lac - target[i]
                iz = np.nonzero(np.sum(dx**2, 1) < dmax**2)
                iz = np.reshape(iz, np.size(iz))
                if np.size(iz) == 0:
                    iz = np.array([np.argmin(np.sum(dx**2, 1))])

                # b.2: anatomical constraints
                lanat = np.reshape(lac[iz],
                                   (np.size(iz), domain.coord.shape[1]))
                pot = np.zeros(np.size(iz))
                JM, rmin = _exclusion_map(i, spatial_proto, target, lanat)
                pot[JM < 0] = np.inf
                pot[JM >= 0] = -JM[JM >= 0]

                # b.3: add feature discrepancy
                df = Fs[iz] - proto[i]
                df = np.reshape(df, (np.size(iz), proto.shape[1]))
                pot += lamb * np.sum(df**2, 1)

                # b.4: solution
                if np.sum(np.isinf(pot)) == np.size(pot):
                    pot = np.sum(dx[iz]**2, 1)

                sol = iz[np.argmin(pot)]
                target[i] = lac[sol]
                lseeds[i] = sol
                toto[sol] = 1

            if verbose > 1:
                jm = _field_gradient_jac(spatial_proto, target)
                print jm.min(), jm.max(), np.sum(toto > 0)

            # c.subject-specific parcellation
            g = graphs[s]
            f = Field(g.V, g.edges, g.weights, Fs)
            U.append(f.constrained_voronoi(lseeds))

            Energy += np.sum((Fs - proto[U[-1]]) ** 2) / \
                np.sum(initial_mask[:, s] > - 1)
            # recompute the prototypes
            # (average in subject s)
            lproto = [np.mean(Fs[U[-1] == k], 0) for k in range(nb_parcel)]
            lproto = np.array(lproto)
            lproto_anat = np.array(
                [np.mean(lac[U[-1] == k], 0) for k in range(nb_parcel)])

            LP.append(lproto)
            LPA.append(lproto_anat)

        # recompute the prototypes across subjects
        proto_mem = proto.copy()
        proto = np.mean(np.array(LP), 0)
        proto_anat = np.mean(np.array(LPA), 0)
        displ = np.sqrt(np.sum((proto_mem - proto)**2, 1).max())
        if verbose:
            print 'energy', Energy, 'displacement', displ

        # recompute the topological model
        spatial_proto.set_field(proto_anat)
        spatial_proto.voronoi_diagram(proto_anat, domain.coord)
        spatial_proto.set_gaussian(proto_anat)
        spatial_proto.normalize()

        if displ < 1.e-4 * dmax:
            break
    return U, proto_anat