-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathsupervertex_clustering.m
152 lines (132 loc) · 5.65 KB
/
supervertex_clustering.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
function [ superLabels, superSeries, superVertices, superIdx ] = supervertex_clustering( ...
timeseries, verticalCoords, corticalMask, superVertices, superIdx, adj, R, geodesics )
%SUPERVERTEX_CLUSTERING Cluster cortical vertices into supervertices
% SUPERVERTEX_CLUSTERING groups the cortical vertices into a predefined
% numbers of supervertices.
%
% --------------------------------INPUT----------------------------------
% TIMESERIES: Timeseries data that will be clustered
%
% VERTICALCOORDS: x, y, z, coordinates of the cortical vertices
%
% CORTICALMASK: A binary mask that separates the cortical vertices from
% the medial wall.
%
% SUPERVERTICES: x, y, z coordinates of the initial seeds.
%
% SUPERIDX: The cortical ids of the intial seeds
%
% ADJ: A binary matrix of the adjacent vertices in the cortical mesh
%
% GEODESICS = A cell structure of size 2 x 1, which stores the geodesic
% distance of their cortical vertices and their neighbours on the
% cortical mesh.
%
% R: Range of the local search space in mm
%
% --------------------------------OUTPUT--------------------------------
% SUPERLABELS: [29K x 1] label vector, showing the cluster membership of
% the cortical vertices. Each label is within [1, nSupervs].
%
% SUPERSERIES: [nSupervs x d] timeseries matrix, in which each timeseries
% is associated with a supervertex.
%
% SUPERVERTICES: [nSupervs x 3] matrix. x, y, z coordinates of the
% supervertices.
%
% SUPERIDX: [nSupervs x 1] vector. Cortical IDs of the supervertices.
%
% ------------------------------ALGORITHM-------------------------------
% A k-means alike algorithm is proposed that parcellates the rs-fMRI
% data into sub-regions with respect to their functional correlations
% and spatial proximity. It is different from the classical k-means in
% two aspects: 1) The search space is limited to reduce the number of
% distance calculations and 2) the distance function is capable of
% matching highly correlated vertices with each other while enforcing
% spatial contiguity within a cluster.
% Internal parameters
endThr = 10; % If the number of vertices that have been assigned
% to another cluster in the previous iteration is less
% than ENDTHR, then STOP. In the paper, it is set to 1
% for the finest possible clusters, but its impact on
% the final parcellations is tiny.
changed = 1; % Has anything changed between iterations or not?
verbose = 1; % Write output on console?
% Mappers
[ map32to29, map29to32 ] = cortical_mappers(corticalMask);
% The number of supervertices
nSupervs = length(superVertices);
superSeries = timeseries(map32to29(superIdx),:);
superDists = Inf(length(timeseries),1);
superLabels = zeros(length(timeseries),1);
% Limit search space to reduce computational cost
geodesicsLocal = get_vertices_within_search_space(superVertices, superIdx, geodesics, R);
idsLocal = geodesicsLocal{1};
distsLocal = geodesicsLocal{2};
newSuperVertices = zeros(size(superVertices));
newSuperSeries = zeros(size(superSeries));
oldSuperLabels = superLabels;
fprintf('\nSupervertices are being computed');
while (changed)
% Print progress on the console
if verbose == 1
fprintf('.');
end
% Real action starts from here
for i = 1 : nSupervs
indx = map32to29(idsLocal{i}');
neighTimeseries = timeseries(indx,:);
distances = dist_func(superSeries(i,:), neighTimeseries, distsLocal{i}, R);
di = superDists(indx);
Li = superLabels(indx);
compared = distances < di;
di(compared == 1) = distances(compared == 1);
Li(compared == 1) = i;
superDists(indx) = di;
superLabels(indx) = Li;
end
% Update cluster centers and bunch time series
for i = 1 : nSupervs
idx = find(superLabels == i);
if isempty(idx)
idx = map32to29(superIdx(i));
superLabels(idx) = i;
end
bunch = timeseries(idx,:);
newSuperSeries(i,:) = mean(bunch,1);
mapped = map29to32(idx);
newSuperVertices(i, :) = mean(verticalCoords(mapped,:), 1);
end
% If the program steps into this, it is because one or more
% supervertices have been undertaken by the others. This happens
% when R is too small compared to nSupervs. My solution is to locate
% these supervertices and keep them as singleton clusters.
if sum(superLabels == 0) > 0
zs = map29to32(superLabels == 0);
for i = 1 : length(zs)
id = zs(i);
ids = nonzeros(map32to29(adj(id,:)>0));
[ uniqs, ~ ] = count_unique_elements(nonzeros(superLabels(ids)));
superLabels(map32to29(id)) = uniqs(1);
end
end
% The recently computed supervertex centroids are being matched with the
% closest vertices on the cortical surface.
temp = verticalCoords;
temp(corticalMask == 0,:) = Inf; % Wall vertices are assigned to Inf.
superIdx = knnsearch(temp, newSuperVertices);
newSuperVertices = verticalCoords(superIdx, :);
% Getting ready for the next iteration
geodesicsLocal = get_vertices_within_search_space(newSuperVertices, superIdx, geodesics, R);
idsLocal = geodesicsLocal{1};
distsLocal = geodesicsLocal{2};
superSeries = newSuperSeries;
superVertices = newSuperVertices;
% Calculate if any vertex has changed its clsuter
if sum(oldSuperLabels ~= superLabels) < endThr
changed = 0;
else
oldSuperLabels = superLabels;
end
end
fprintf('\nDone.\n');