1 |
|
|
/* |
2 |
|
|
Teem: Tools to process and visualize scientific data and images . |
3 |
|
|
Copyright (C) 2013, 2012, 2011, 2010, 2009 University of Chicago |
4 |
|
|
Copyright (C) 2008, 2007, 2006, 2005 Gordon Kindlmann |
5 |
|
|
Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah |
6 |
|
|
|
7 |
|
|
This library is free software; you can redistribute it and/or |
8 |
|
|
modify it under the terms of the GNU Lesser General Public License |
9 |
|
|
(LGPL) as published by the Free Software Foundation; either |
10 |
|
|
version 2.1 of the License, or (at your option) any later version. |
11 |
|
|
The terms of redistributing and/or modifying this software also |
12 |
|
|
include exceptions to the LGPL that facilitate static linking. |
13 |
|
|
|
14 |
|
|
This library is distributed in the hope that it will be useful, |
15 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 |
|
|
Lesser General Public License for more details. |
18 |
|
|
|
19 |
|
|
You should have received a copy of the GNU Lesser General Public License |
20 |
|
|
along with this library; if not, write to Free Software Foundation, Inc., |
21 |
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 |
|
|
*/ |
23 |
|
|
|
24 |
|
|
|
25 |
|
|
#include "ten.h" |
26 |
|
|
#include "privateTen.h" |
27 |
|
|
|
28 |
|
|
|
29 |
|
|
#define MAX_KMEANS_ITERATIONS 50 |
30 |
|
|
|
31 |
|
|
|
32 |
|
|
|
33 |
|
|
|
34 |
|
|
/* Calculate the Q-ball profile from DWIs */ |
35 |
|
|
void |
36 |
|
|
_tenQball(const double b, const int gradcount, const double svals[], |
37 |
|
|
const double grads[], double qvals[] ) { |
38 |
|
|
/* Not an optimal Q-ball implementation! (Taken from submission to |
39 |
|
|
MICCAI 2006) Should be solved analytically in the future, |
40 |
|
|
implemented from recent papers. */ |
41 |
|
|
int i,j; |
42 |
|
|
double d, dist, weight, min, max; |
43 |
|
|
|
44 |
|
|
AIR_UNUSED(b); |
45 |
|
|
min = max = svals[1] / svals[0]; |
46 |
|
|
for( i = 0; i < gradcount; i++ ) { |
47 |
|
|
d = svals[i+1] / svals[0]; |
48 |
|
|
if( d > max ) |
49 |
|
|
max = d; |
50 |
|
|
else if( d < min ) |
51 |
|
|
min = d; |
52 |
|
|
} |
53 |
|
|
|
54 |
|
|
for( i = 0; i < gradcount; i++ ) { |
55 |
|
|
qvals[i] = 0; |
56 |
|
|
for( j = 0; j < gradcount; j++ ) { |
57 |
|
|
d = AIR_AFFINE( min, svals[j+1] / svals[0], max, 0,1 ); |
58 |
|
|
dist = ELL_3V_DOT(grads + 3*i, grads + 3*j); |
59 |
|
|
dist = AIR_ABS(dist); |
60 |
|
|
weight = cos( 0.5 * AIR_PI * dist ); |
61 |
|
|
qvals[i] += d * weight*weight*weight*weight; |
62 |
|
|
} |
63 |
|
|
} |
64 |
|
|
return; |
65 |
|
|
} |
66 |
|
|
|
67 |
|
|
/* Segments DWIs into 2 segments based on Q-ball profiles */ |
68 |
|
|
void |
69 |
|
|
_tenSegsamp2(const int gradcount, const double qvals[], |
70 |
|
|
const double grads[], const double qpoints[], |
71 |
|
|
unsigned int seg[], double dists[]) { |
72 |
|
|
const int segcount = 2; |
73 |
|
|
int i, changed=AIR_TRUE; |
74 |
|
|
double centroids[ 3*2 ]; /* 3*segcount */ |
75 |
|
|
|
76 |
|
|
AIR_UNUSED(grads); |
77 |
|
|
_tenInitcent2(gradcount, qvals, qpoints, centroids); |
78 |
|
|
|
79 |
|
|
for( i = 0; i < MAX_KMEANS_ITERATIONS && changed; i++ ) { |
80 |
|
|
_tenCalcdists(segcount, centroids, gradcount, qpoints, dists); |
81 |
|
|
changed = _tenCalccent2(gradcount, qpoints, dists, centroids, seg); |
82 |
|
|
|
83 |
|
|
/* |
84 |
|
|
printf( "Seg[%d]\t= { ",i ); |
85 |
|
|
for( j = 0; j < gradcount; j++ ) |
86 |
|
|
printf( "%d ", seg[j] ); |
87 |
|
|
printf( changed ? "}\n" : "} Convergence!\n" ); |
88 |
|
|
*/ |
89 |
|
|
|
90 |
|
|
} |
91 |
|
|
} |
92 |
|
|
|
93 |
|
|
/* Gives an inital choice of 2 centroids */ |
94 |
|
|
void |
95 |
|
|
_tenInitcent2(const int gradcount, const double qvals[], |
96 |
|
|
const double qpoints[], double centroids[6]) { |
97 |
|
|
int i, maxidx; |
98 |
|
|
double max, dist; |
99 |
|
|
|
100 |
|
|
/* Find largest peak in Q-ball */ |
101 |
|
|
maxidx = 0; |
102 |
|
|
for( i = 0; i < gradcount; i++ ) |
103 |
|
|
if( qvals[maxidx] < qvals[i] ) |
104 |
|
|
maxidx = i; |
105 |
|
|
|
106 |
|
|
ELL_3V_COPY( centroids, qpoints +3*maxidx ); |
107 |
|
|
/* |
108 |
|
|
printf("init: max=%d cent0=[%f %f %f]\n", maxidx, |
109 |
|
|
centroids[0], centroids[1], centroids[2]); |
110 |
|
|
*/ |
111 |
|
|
|
112 |
|
|
/* Find peak/axis from Q-ball furthest away from first peak */ |
113 |
|
|
max = 0; |
114 |
|
|
for( i = 0; i < gradcount; i++ ) { |
115 |
|
|
dist = _tenPldist(qpoints +3*i, centroids); |
116 |
|
|
if (dist > max) { |
117 |
|
|
maxidx = i; |
118 |
|
|
max = dist; |
119 |
|
|
} |
120 |
|
|
} |
121 |
|
|
|
122 |
|
|
ELL_3V_COPY( centroids+3, qpoints +3*maxidx ); |
123 |
|
|
/* |
124 |
|
|
printf( "\ninit: max=%d cent1=[%f %f %f]\n", maxidx, |
125 |
|
|
centroids[3], centroids[4], centroids[5]); |
126 |
|
|
*/ |
127 |
|
|
} |
128 |
|
|
|
129 |
|
|
/* Calculates 2 new centroids (and a new segmentation) from distances |
130 |
|
|
between Q-balls and centroids, returns true if segmentation changed |
131 |
|
|
*/ |
132 |
|
|
int |
133 |
|
|
_tenCalccent2(const int gradcount, const double qpoints[], |
134 |
|
|
const double dists[], double centroid[6], unsigned int seg[]) { |
135 |
|
|
#if 0 |
136 |
|
|
/* HEY: Attempt to implement better line-adding by adding |
137 |
|
|
outerproducts of points and estimating major eigenvector |
138 |
|
|
afterwards */ |
139 |
|
|
int i,changed=AIR_FALSE; |
140 |
|
|
double sum0[9],sum1[9],mat[9], eval[3],evec[9]; |
141 |
|
|
|
142 |
|
|
ELL_3M_ZERO_SET( sum0 ); |
143 |
|
|
ELL_3M_ZERO_SET( sum1 ); |
144 |
|
|
for( i = 0; i < gradcount; i++ ) { |
145 |
|
|
if( dists[i] < dists[gradcount+i] ) { |
146 |
|
|
ELL_3MV_OUTER( mat, qpoints +3*i, qpoints +3*i ); |
147 |
|
|
ELL_3M_ADD2( sum0, sum0, mat ); |
148 |
|
|
changed = changed || (seg[i] != 0); |
149 |
|
|
seg[i] = 0; |
150 |
|
|
} else { |
151 |
|
|
ELL_3MV_OUTER( mat, qpoints +3*i +gradcount, qpoints +3*i +gradcount ); |
152 |
|
|
ELL_3M_ADD2( sum1, sum1, mat ); |
153 |
|
|
changed = changed || (seg[i] != 1); |
154 |
|
|
seg[i] = 1; |
155 |
|
|
} |
156 |
|
|
} |
157 |
|
|
|
158 |
|
|
ell_3m_eigensolve_d( eval, evec, sum0, 0 ); |
159 |
|
|
ELL_3V_COPY( centroid, evec + 3*ELL_MAX3_IDX( eval[0], eval[1], eval[2] ) ); |
160 |
|
|
/* ELL_3V_SCALE( centroid, ELL_3V_LEN( centroid ), centroid ); */ |
161 |
|
|
|
162 |
|
|
|
163 |
|
|
ell_3m_eigensolve_d( eval, evec, sum1, 0 ); |
164 |
|
|
ELL_3V_COPY( centroid +3, evec + 3*ELL_MAX3_IDX( eval[0], eval[1], eval[2] ) ); |
165 |
|
|
/* ELL_3V_SCALE( centroid +3, ELL_3V_LEN( centroid ), centroid +3); Normalize */ |
166 |
|
|
|
167 |
|
|
return changed; |
168 |
|
|
#endif |
169 |
|
|
|
170 |
|
|
int i, sign, seg0count=0, seg1count=0, changed=AIR_FALSE; |
171 |
|
|
double oldcentroid[6], diff[3], sum[3]; |
172 |
|
|
|
173 |
|
|
memcpy( oldcentroid, centroid, 6 * sizeof( double )); |
174 |
|
|
|
175 |
|
|
for( i = 0; i < gradcount; i++ ) { |
176 |
|
|
if( dists[ 0*gradcount +i] < dists[1*gradcount +i] ) { |
177 |
|
|
/* Try to resolve sign so that centroid do not end up as all 0 */ |
178 |
|
|
/* Choose signs so that the point lies "on the same side" as */ |
179 |
|
|
/* the previous centroid. */ |
180 |
|
|
diff[0] = oldcentroid[0] - qpoints[3*i +0]; |
181 |
|
|
diff[1] = oldcentroid[1] - qpoints[3*i +1]; |
182 |
|
|
diff[2] = oldcentroid[2] - qpoints[3*i +2]; |
183 |
|
|
sum[0] = oldcentroid[0] + qpoints[3*i +0]; |
184 |
|
|
sum[1] = oldcentroid[1] + qpoints[3*i +1]; |
185 |
|
|
sum[2] = oldcentroid[2] + qpoints[3*i +2]; |
186 |
|
|
sign = (diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]) < |
187 |
|
|
(sum[0]*sum[0] + sum[1]*sum[1] + sum[2]*sum[2]) ? -1 : +1; |
188 |
|
|
|
189 |
|
|
changed = changed || (seg[i] != 0); |
190 |
|
|
|
191 |
|
|
seg[i] = 0; |
192 |
|
|
centroid[0] += sign * qpoints[3*i +0]; |
193 |
|
|
centroid[1] += sign * qpoints[3*i +1]; |
194 |
|
|
centroid[2] += sign * qpoints[3*i +2]; |
195 |
|
|
seg0count++; |
196 |
|
|
} else { |
197 |
|
|
diff[0] = oldcentroid[3+0] - qpoints[3*i +0]; |
198 |
|
|
diff[1] = oldcentroid[3+1] - qpoints[3*i +1]; |
199 |
|
|
diff[2] = oldcentroid[3+2] - qpoints[3*i +2]; |
200 |
|
|
sum[0] = oldcentroid[3+0] + qpoints[3*i +0]; |
201 |
|
|
sum[1] = oldcentroid[3+1] + qpoints[3*i +1]; |
202 |
|
|
sum[2] = oldcentroid[3+2] + qpoints[3*i +2]; |
203 |
|
|
sign = (diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]) < |
204 |
|
|
(sum[0]*sum[0] + sum[1]*sum[1] + sum[2]*sum[2]) ? -1 : +1; |
205 |
|
|
|
206 |
|
|
changed = changed || (seg[i] != 1); |
207 |
|
|
|
208 |
|
|
seg[i] = 1; |
209 |
|
|
centroid[3+0] += sign * qpoints[3*i +0]; |
210 |
|
|
centroid[3+1] += sign * qpoints[3*i +1]; |
211 |
|
|
centroid[3+2] += sign * qpoints[3*i +2]; |
212 |
|
|
seg1count++; |
213 |
|
|
} |
214 |
|
|
} |
215 |
|
|
centroid[0] /= seg0count; |
216 |
|
|
centroid[1] /= seg0count; |
217 |
|
|
centroid[2] /= seg0count; |
218 |
|
|
centroid[3+0] /= seg1count; |
219 |
|
|
centroid[3+1] /= seg1count; |
220 |
|
|
centroid[3+2] /= seg1count; |
221 |
|
|
|
222 |
|
|
/* printf("cent = %f %f %f %f %f %f\n", centroid[0],centroid[1],centroid[2],centroid[3],centroid[4],centroid[5] ); */ |
223 |
|
|
|
224 |
|
|
/* |
225 |
|
|
Should give error if any segment contains less than 6 elements, |
226 |
|
|
i.e. if( seg0count < 6 || seg1count < 6 ), since that would |
227 |
|
|
imply that a tensor cannot be computed for that segment. |
228 |
|
|
*/ |
229 |
|
|
|
230 |
|
|
return changed; |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
/* Converts Q-values and gradients to points on the Q-ball surface */ |
234 |
|
|
void |
235 |
|
|
_tenQvals2points(const int gradcount, const double qvals[], |
236 |
|
|
const double grads[], double qpoints[] ) { |
237 |
|
|
int i; |
238 |
|
|
memcpy( qpoints, grads, 3 * gradcount * sizeof( double ) ); |
239 |
|
|
for( i = 0; i < gradcount; i++ ) { |
240 |
|
|
qpoints[3*i +0] *= qvals[i]; |
241 |
|
|
qpoints[3*i +1] *= qvals[i]; |
242 |
|
|
qpoints[3*i +2] *= qvals[i]; |
243 |
|
|
} |
244 |
|
|
} |
245 |
|
|
|
246 |
|
|
/* Calculates the shortest distances from each centroid/axis to each |
247 |
|
|
Q-ball point */ |
248 |
|
|
void |
249 |
|
|
_tenCalcdists(const int centcount, const double centroid[], |
250 |
|
|
const int gradcount, const double qpoints[], double dists[] ) { |
251 |
|
|
int i,j; |
252 |
|
|
for( j = 0; j < centcount; j++ ) |
253 |
|
|
for( i = 0; i < gradcount; i++ ) |
254 |
|
|
dists[j*gradcount +i] = _tenPldist(&qpoints[3*i], ¢roid[3*j]); |
255 |
|
|
|
256 |
|
|
/* |
257 |
|
|
printf("dists = "); |
258 |
|
|
for( i = 0; i < 2*gradcount; i++ ) |
259 |
|
|
printf( "%f ", dists[i] ); |
260 |
|
|
printf("\n"); |
261 |
|
|
*/ |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
/* Estimates the shortest distance from a point to a line going |
265 |
|
|
through the origin */ |
266 |
|
|
double |
267 |
|
|
_tenPldist( const double point[], const double line[] ) { |
268 |
|
|
|
269 |
|
|
double cross[3]; |
270 |
|
|
double negpoint[3]; |
271 |
|
|
|
272 |
|
|
negpoint[0] = -point[0]; |
273 |
|
|
negpoint[1] = -point[1]; |
274 |
|
|
negpoint[2] = -point[2]; |
275 |
|
|
|
276 |
|
|
ELL_3V_CROSS( cross, line, negpoint ); |
277 |
|
|
|
278 |
|
|
return ELL_3V_LEN( cross ) / ELL_3V_LEN( line ); |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
/* Converts a segmentation into a set of 0-1 weights */ |
282 |
|
|
void |
283 |
|
|
_tenSeg2weights(const int gradcount, const int seg[], |
284 |
|
|
const int segcount, double weights[] ) { |
285 |
|
|
int i,j; |
286 |
|
|
for( j = 0; j < segcount; j++ ) { |
287 |
|
|
for( i = 0; i < gradcount; i++ ) { |
288 |
|
|
weights[j*gradcount +i] = (seg[i] == j) ? 1 : 0; |
289 |
|
|
} |
290 |
|
|
} |
291 |
|
|
return; |
292 |
|
|
} |
293 |
|
|
|