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