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 |
|
|
#include "gage.h" |
25 |
|
|
#include "privateGage.h" |
26 |
|
|
|
27 |
|
|
/* |
28 |
|
|
** sets the filter sample location (fsl) array based on fractional |
29 |
|
|
** probe location ctx->point->frac |
30 |
|
|
** |
31 |
|
|
** One possible rare surpise: if a filter is not continuous with 0 |
32 |
|
|
** at the end of its support, and if the sample location is at the |
33 |
|
|
** highest possible point (xi == N-2, xf = 1.0), then the filter |
34 |
|
|
** weights may not be the desired ones. Forward differencing (via |
35 |
|
|
** nrrdKernelForwDiff) is a good example of this. |
36 |
|
|
*/ |
37 |
|
|
void |
38 |
|
|
_gageFslSet(gageContext *ctx) { |
39 |
|
|
int fr, i; |
40 |
|
|
double *fslx, *fsly, *fslz; |
41 |
|
|
double xf, yf, zf; |
42 |
|
|
|
43 |
|
517458 |
fr = ctx->radius; |
44 |
|
258729 |
fslx = ctx->fsl + 0*2*fr; |
45 |
|
258729 |
fsly = ctx->fsl + 1*2*fr; |
46 |
|
258729 |
fslz = ctx->fsl + 2*2*fr; |
47 |
|
258729 |
xf = ctx->point.frac[0]; |
48 |
|
258729 |
yf = ctx->point.frac[1]; |
49 |
|
258729 |
zf = ctx->point.frac[2]; |
50 |
✓✓✓ |
258729 |
switch (fr) { |
51 |
|
|
case 1: |
52 |
|
110862 |
fslx[0] = xf; fslx[1] = xf-1; |
53 |
|
110862 |
fsly[0] = yf; fsly[1] = yf-1; |
54 |
|
110862 |
fslz[0] = zf; fslz[1] = zf-1; |
55 |
|
110862 |
break; |
56 |
|
|
case 2: |
57 |
|
79662 |
fslx[0] = xf+1; fslx[1] = xf; fslx[2] = xf-1; fslx[3] = xf-2; |
58 |
|
79662 |
fsly[0] = yf+1; fsly[1] = yf; fsly[2] = yf-1; fsly[3] = yf-2; |
59 |
|
79662 |
fslz[0] = zf+1; fslz[1] = zf; fslz[2] = zf-1; fslz[3] = zf-2; |
60 |
|
79662 |
break; |
61 |
|
|
default: |
62 |
|
|
/* filter radius bigger than 2 */ |
63 |
✓✓ |
1824502 |
for (i=-fr+1; i<=fr; i++) { |
64 |
|
844046 |
fslx[i+fr-1] = xf-i; |
65 |
|
844046 |
fsly[i+fr-1] = yf-i; |
66 |
|
844046 |
fslz[i+fr-1] = zf-i; |
67 |
|
|
} |
68 |
|
|
break; |
69 |
|
|
} |
70 |
|
|
return; |
71 |
|
258729 |
} |
72 |
|
|
|
73 |
|
|
/* |
74 |
|
|
** renormalize weights of a reconstruction kernel with |
75 |
|
|
** constraint: the sum of the weights must equal the continuous |
76 |
|
|
** integral of the kernel |
77 |
|
|
*/ |
78 |
|
|
void |
79 |
|
|
_gageFwValueRenormalize(gageContext *ctx, int wch) { |
80 |
|
|
double integral, sumX, sumY, sumZ, *fwX, *fwY, *fwZ; |
81 |
|
|
int i, fd; |
82 |
|
|
|
83 |
|
17280 |
fd = 2*ctx->radius; |
84 |
|
8640 |
fwX = ctx->fw + 0 + fd*(0 + 3*wch); |
85 |
|
8640 |
fwY = ctx->fw + 0 + fd*(1 + 3*wch); |
86 |
|
8640 |
fwZ = ctx->fw + 0 + fd*(2 + 3*wch); |
87 |
|
8640 |
integral = ctx->ksp[wch]->kernel->integral(ctx->ksp[wch]->parm); |
88 |
|
|
sumX = sumY = sumZ = 0; |
89 |
✓✓ |
127872 |
for (i=0; i<fd; i++) { |
90 |
|
55296 |
sumX += fwX[i]; |
91 |
|
55296 |
sumY += fwY[i]; |
92 |
|
55296 |
sumZ += fwZ[i]; |
93 |
|
|
} |
94 |
✓✓ |
127872 |
for (i=0; i<fd; i++) { |
95 |
|
55296 |
fwX[i] *= integral/sumX; |
96 |
|
55296 |
fwY[i] *= integral/sumY; |
97 |
|
55296 |
fwZ[i] *= integral/sumZ; |
98 |
|
|
} |
99 |
|
|
return; |
100 |
|
8640 |
} |
101 |
|
|
|
102 |
|
|
/* |
103 |
|
|
** renormalize weights of a derivative kernel with |
104 |
|
|
** constraint: the sum of the weights must be zero, but |
105 |
|
|
** sign of individual weights must be preserved |
106 |
|
|
*/ |
107 |
|
|
void |
108 |
|
|
_gageFwDerivRenormalize(gageContext *ctx, int wch) { |
109 |
|
34560 |
char me[]="_gageFwDerivRenormalize"; |
110 |
|
|
double negX, negY, negZ, posX, posY, posZ, fixX, fixY, fixZ, |
111 |
|
|
*fwX, *fwY, *fwZ; |
112 |
|
|
int i, fd; |
113 |
|
|
|
114 |
|
17280 |
fd = 2*ctx->radius; |
115 |
|
17280 |
fwX = ctx->fw + 0 + fd*(0 + 3*wch); |
116 |
|
17280 |
fwY = ctx->fw + 0 + fd*(1 + 3*wch); |
117 |
|
17280 |
fwZ = ctx->fw + 0 + fd*(2 + 3*wch); |
118 |
|
|
negX = negY = negZ = 0; |
119 |
|
|
posX = posY = posZ = 0; |
120 |
✓✓ |
255744 |
for (i=0; i<fd; i++) { |
121 |
✓✓ |
221184 |
if (fwX[i] <= 0) { negX += -fwX[i]; } else { posX += fwX[i]; } |
122 |
✓✓ |
221184 |
if (fwY[i] <= 0) { negY += -fwY[i]; } else { posY += fwY[i]; } |
123 |
✓✓ |
221184 |
if (fwZ[i] <= 0) { negZ += -fwZ[i]; } else { posZ += fwZ[i]; } |
124 |
|
|
} |
125 |
|
|
/* fix is the sqrt() of factor by which the positive values |
126 |
|
|
are too big. negative values are scaled up by fix; |
127 |
|
|
positive values are scaled down by fix */ |
128 |
|
17280 |
fixX = sqrt(posX/negX); |
129 |
|
17280 |
fixY = sqrt(posY/negY); |
130 |
|
17280 |
fixZ = sqrt(posZ/negZ); |
131 |
✗✓ |
17280 |
if (ctx->verbose > 2) { |
132 |
|
|
fprintf(stderr, "%s: fixX = % 10.4f, fixY = % 10.4f, fixX = % 10.4f\n", |
133 |
|
|
me, (float)fixX, (float)fixY, (float)fixZ); |
134 |
|
|
} |
135 |
✓✓ |
255744 |
for (i=0; i<fd; i++) { |
136 |
✓✓ |
221184 |
if (fwX[i] <= 0) { fwX[i] *= fixX; } else { fwX[i] /= fixX; } |
137 |
✓✓ |
221184 |
if (fwY[i] <= 0) { fwY[i] *= fixY; } else { fwY[i] /= fixY; } |
138 |
✓✓ |
221184 |
if (fwZ[i] <= 0) { fwZ[i] *= fixZ; } else { fwZ[i] /= fixZ; } |
139 |
|
|
} |
140 |
|
|
return; |
141 |
|
17280 |
} |
142 |
|
|
|
143 |
|
|
void |
144 |
|
|
_gageFwSet(gageContext *ctx, unsigned int sidx, double sfrac) { |
145 |
|
517458 |
char me[]="_gageFwSet"; |
146 |
|
|
int kidx; |
147 |
|
|
unsigned int fd; |
148 |
|
|
|
149 |
|
258729 |
fd = 2*ctx->radius; |
150 |
✓✓ |
4139664 |
for (kidx=gageKernelUnknown+1; kidx<gageKernelLast; kidx++) { |
151 |
✓✓ |
1811103 |
if (!ctx->needK[kidx] || kidx==gageKernelStack) { |
152 |
|
|
continue; |
153 |
|
|
} |
154 |
|
|
/* we evaluate weights for all three axes with one call */ |
155 |
|
1410918 |
ctx->ksp[kidx]->kernel->evalN_d(ctx->fw + fd*3*kidx, ctx->fsl, |
156 |
|
705459 |
fd*3, ctx->ksp[kidx]->parm); |
157 |
|
705459 |
} |
158 |
|
|
|
159 |
✗✓ |
258729 |
if (ctx->verbose > 2) { |
160 |
|
|
fprintf(stderr, "%s: filter weights after kernel evaluation:\n", me); |
161 |
|
|
_gagePrint_fslw(stderr, ctx); |
162 |
|
|
} |
163 |
✓✓ |
258729 |
if (ctx->parm.renormalize) { |
164 |
✓✓ |
138240 |
for (kidx=gageKernelUnknown+1; kidx<gageKernelLast; kidx++) { |
165 |
✓✓ |
60480 |
if (!ctx->needK[kidx] || kidx==gageKernelStack) { |
166 |
|
|
continue; |
167 |
|
|
} |
168 |
✗✗✓✓
|
25920 |
switch (kidx) { |
169 |
|
|
case gageKernel00: |
170 |
|
|
case gageKernel10: |
171 |
|
|
case gageKernel20: |
172 |
|
8640 |
_gageFwValueRenormalize(ctx, kidx); |
173 |
|
8640 |
break; |
174 |
|
|
default: |
175 |
|
17280 |
_gageFwDerivRenormalize(ctx, kidx); |
176 |
|
17280 |
break; |
177 |
|
|
} |
178 |
|
|
} |
179 |
✗✓ |
8640 |
if (ctx->verbose > 2) { |
180 |
|
|
fprintf(stderr, "%s: filter weights after renormalization:\n", me); |
181 |
|
|
_gagePrint_fslw(stderr, ctx); |
182 |
|
|
} |
183 |
|
|
} |
184 |
|
|
|
185 |
✗✓✗✗
|
258729 |
if (ctx->parm.stackUse && ctx->parm.stackNormalizeDeriv) { |
186 |
|
|
unsigned int jj; |
187 |
|
|
double scl, norm, *fwX, *fwY, *fwZ; |
188 |
|
|
|
189 |
|
|
scl = AIR_AFFINE(0.0, sfrac, 1.0, |
190 |
|
|
ctx->stackPos[sidx], |
191 |
|
|
ctx->stackPos[sidx+1]); |
192 |
|
|
#if 0 |
193 |
|
|
double (*dgeval)(double x, const double *parm), |
194 |
|
|
dgparm[2] = {0, 3}; |
195 |
|
|
dgeval = nrrdKernelDiscreteGaussian->eval1_d; |
196 |
|
|
dgparm[0] = scl; |
197 |
|
|
/* from Eq. (120) in T. Lindeberg. "Feature Detection with Automatic |
198 |
|
|
Scale Selection." International Journal of Computer Vision, |
199 |
|
|
1998, 30, 77-116 */ |
200 |
|
|
/* 0.7978845608 ~= sqrt(2)/sqrt(pi) */ |
201 |
|
|
norm = 0.7978845608/(dgeval(0.0, dgparm) + dgeval(1.0, dgparm)); |
202 |
|
|
#endif |
203 |
|
|
/* really simple; no lindeberg normalization, possible bias */ |
204 |
|
|
norm = scl + ctx->parm.stackNormalizeDerivBias; |
205 |
|
|
|
206 |
|
|
fd = 2*ctx->radius; |
207 |
|
|
kidx = gageKernel11; |
208 |
|
|
fwX = ctx->fw + 0 + fd*(0 + 3*kidx); |
209 |
|
|
fwY = ctx->fw + 0 + fd*(1 + 3*kidx); |
210 |
|
|
fwZ = ctx->fw + 0 + fd*(2 + 3*kidx); |
211 |
|
|
for (jj=0; jj<fd; jj++) { |
212 |
|
|
fwX[jj] *= norm; |
213 |
|
|
fwY[jj] *= norm; |
214 |
|
|
fwZ[jj] *= norm; |
215 |
|
|
} |
216 |
|
|
kidx = gageKernel22; |
217 |
|
|
fwX = ctx->fw + 0 + fd*(0 + 3*kidx); |
218 |
|
|
fwY = ctx->fw + 0 + fd*(1 + 3*kidx); |
219 |
|
|
fwZ = ctx->fw + 0 + fd*(2 + 3*kidx); |
220 |
|
|
for (jj=0; jj<fd; jj++) { |
221 |
|
|
fwX[jj] *= norm*norm; |
222 |
|
|
fwY[jj] *= norm*norm; |
223 |
|
|
fwZ[jj] *= norm*norm; |
224 |
|
|
} |
225 |
|
|
} |
226 |
|
|
|
227 |
|
|
return; |
228 |
|
258729 |
} |
229 |
|
|
|
230 |
|
|
/* |
231 |
|
|
** _gageLocationSet |
232 |
|
|
** |
233 |
|
|
** updates probe location in general context, and things which |
234 |
|
|
** depend on it: |
235 |
|
|
** fsl, fw |
236 |
|
|
** |
237 |
|
|
** (_xi,_yi,_zi) is *index* space position in the volume |
238 |
|
|
** _si is the index-space position in the stack, the value is ignored |
239 |
|
|
** if there is no stack behavior |
240 |
|
|
** |
241 |
|
|
** does NOT use biff, but returns 1 on error and 0 if all okay |
242 |
|
|
** Currently only error is probing outside volume, which sets |
243 |
|
|
** ctx->errNum and sprints message into ctx->errStr. |
244 |
|
|
*/ |
245 |
|
|
int |
246 |
|
|
_gageLocationSet(gageContext *ctx, |
247 |
|
|
double xif, double yif, double zif, double sif) { |
248 |
|
682650 |
char me[]="_gageProbeLocationSet"; |
249 |
|
|
unsigned int top[3], /* "top" x, y, z: highest valid index in volume */ |
250 |
|
|
idx[4]; |
251 |
|
|
int sdiff; /* computed integral positions in volume */ |
252 |
|
|
double frac[4], min, max[3]; |
253 |
|
|
|
254 |
|
|
/* **** bounds checking **** */ |
255 |
|
341325 |
top[0] = ctx->shape->size[0] - 1; |
256 |
|
341325 |
top[1] = ctx->shape->size[1] - 1; |
257 |
|
341325 |
top[2] = ctx->shape->size[2] - 1; |
258 |
✗✓ |
341325 |
if (nrrdCenterNode == ctx->shape->center) { |
259 |
|
|
min = 0; |
260 |
|
|
max[0] = top[0]; |
261 |
|
|
max[1] = top[1]; |
262 |
|
|
max[2] = top[2]; |
263 |
|
|
} else { |
264 |
|
|
min = -0.5; |
265 |
|
341325 |
max[0] = AIR_CAST(double, top[0]) + 0.5; |
266 |
|
341325 |
max[1] = AIR_CAST(double, top[1]) + 0.5; |
267 |
|
341325 |
max[2] = AIR_CAST(double, top[2]) + 0.5; |
268 |
|
|
} |
269 |
✓✗✓✗ ✗✓ |
1023975 |
if (!( AIR_IN_CL(min, xif, max[0]) && |
270 |
✓✗✓✗
|
682650 |
AIR_IN_CL(min, yif, max[1]) && |
271 |
✓✗ |
682650 |
AIR_IN_CL(min, zif, max[2]) )) { |
272 |
|
|
if (ctx->parm.generateErrStr) { |
273 |
|
|
sprintf(ctx->errStr, "%s: position (%g,%g,%g) outside (%s-centered) " |
274 |
|
|
"bounds [%g,%g]x[%g,%g]x[%g,%g]", |
275 |
|
|
me, xif, yif, zif, |
276 |
|
|
airEnumStr(nrrdCenter, ctx->shape->center), |
277 |
|
|
min, max[0], min, max[1], min, max[2]); |
278 |
|
|
} else { |
279 |
|
|
strcpy(ctx->errStr, _GAGE_NON_ERR_STR); |
280 |
|
|
} |
281 |
|
|
ctx->errNum = gageErrBoundsSpace; |
282 |
|
|
return 1; |
283 |
|
|
} |
284 |
✗✓ |
341325 |
if (ctx->parm.stackUse) { |
285 |
|
|
if (!( AIR_IN_CL(0, sif, ctx->pvlNum-2) )) { |
286 |
|
|
if (ctx->parm.generateErrStr) { |
287 |
|
|
sprintf(ctx->errStr, "%s: stack position %g outside (%s-centered) " |
288 |
|
|
"bounds [0,%u]", me, sif, |
289 |
|
|
airEnumStr(nrrdCenter, nrrdCenterNode), ctx->pvlNum-2); |
290 |
|
|
} else { |
291 |
|
|
strcpy(ctx->errStr, _GAGE_NON_ERR_STR); |
292 |
|
|
} |
293 |
|
|
ctx->errNum = gageErrBoundsStack; |
294 |
|
|
return 1; |
295 |
|
|
} |
296 |
|
|
} |
297 |
|
|
|
298 |
|
|
/* **** computing integral and fractional sample locations **** */ |
299 |
|
|
/* Thu Jan 14 19:46:53 CST 2010: detected that along the low edge |
300 |
|
|
(next to sample 0) in cell centered, the rounding behavior of |
301 |
|
|
AIR_CAST(unsigned int, xif), namely [-0.5,0] --> 0, meant that |
302 |
|
|
the low edge was not treated symmetrically with the high edge. |
303 |
|
|
This motivated the change from using idx to store the lower |
304 |
|
|
corner of the containing voxel, to the upper corner. So, the new |
305 |
|
|
"idx" is always +1 of what the old idx was. Code here and in |
306 |
|
|
ctx.c (since idx is saved into ctx->point.idx) has been changed |
307 |
|
|
accordingly */ |
308 |
|
341325 |
ELL_3V_SET(idx, |
309 |
|
|
AIR_CAST(unsigned int, xif+1), /* +1: see above */ |
310 |
|
|
AIR_CAST(unsigned int, yif+1), |
311 |
|
|
AIR_CAST(unsigned int, zif+1)); |
312 |
✗✓ |
341325 |
if (ctx->verbose > 5) { |
313 |
|
|
fprintf(stderr, "%s: (%g,%g,%g,%g) -%s-> mm [%g, %g/%g/%g]\n" |
314 |
|
|
" --> idx %u %u %u\n", |
315 |
|
|
me, xif, yif, zif, sif, |
316 |
|
|
airEnumStr(nrrdCenter, ctx->shape->center), |
317 |
|
|
min, max[0], max[1], max[2], idx[0], idx[1], idx[2]); |
318 |
|
|
} |
319 |
|
|
/* these can only can kick in for node-centered, because that's when |
320 |
|
|
max[] has an integral value */ |
321 |
|
341325 |
idx[0] -= (idx[0]-1 == max[0]); |
322 |
|
341325 |
idx[1] -= (idx[1]-1 == max[1]); |
323 |
|
341325 |
idx[2] -= (idx[2]-1 == max[2]); |
324 |
✗✓ |
341325 |
if (ctx->verbose > 5) { |
325 |
|
|
fprintf(stderr, "%s: ----> idx %u %u %u\n", |
326 |
|
|
me, idx[0], idx[1], idx[2]); |
327 |
|
|
} |
328 |
|
341325 |
ELL_3V_SET(frac, |
329 |
|
|
xif - (AIR_CAST(float, idx[0])-1), |
330 |
|
|
yif - (AIR_CAST(float, idx[1])-1), |
331 |
|
|
zif - (AIR_CAST(float, idx[2])-1)); |
332 |
|
341325 |
ELL_3V_COPY(ctx->point.idx, idx); /* not idx[3], yet */ |
333 |
✗✓ |
341325 |
if (ctx->parm.stackUse) { |
334 |
|
|
idx[3] = AIR_CAST(unsigned int, sif); |
335 |
|
|
idx[3] -= (idx[3] == ctx->pvlNum-2); |
336 |
|
|
frac[3] = sif - idx[3]; |
337 |
|
|
sdiff = (ctx->point.idx[3] + ctx->point.frac[3] != sif); |
338 |
|
|
} else { |
339 |
|
|
idx[3] = 0; |
340 |
|
|
frac[3] = 0; |
341 |
|
|
sdiff = AIR_FALSE; |
342 |
|
|
} |
343 |
✗✓ |
341325 |
if (ctx->verbose > 2) { |
344 |
|
|
fprintf(stderr, "%s: \n" |
345 |
|
|
" pos (% 15.7f,% 15.7f,% 15.7f,% 15.7f) \n" |
346 |
|
|
" -> i(%5d,%5d,%5d,%5d) \n" |
347 |
|
|
" + f(% 15.7f,% 15.7f,% 15.7f,% 15.7f) \n", |
348 |
|
|
me, xif, yif, zif, sif, idx[0], idx[1], idx[2], idx[3], |
349 |
|
|
frac[0], frac[1], frac[2], frac[3]); |
350 |
|
|
} |
351 |
|
|
|
352 |
|
|
/* **** compute *spatial* fsl and fw **** |
353 |
|
|
these have to be reconsidered if anything changes about the |
354 |
|
|
fractional spatial position, or (if no fractional spatial change), |
355 |
|
|
movement along scale AND using normalization based on scale */ |
356 |
✗✗ |
341325 |
if ( ctx->point.frac[0] != frac[0] |
357 |
✓✓ |
580060 |
|| ctx->point.frac[1] != frac[1] |
358 |
✓✓ |
332130 |
|| ctx->point.frac[2] != frac[2] |
359 |
✓✓✗✓
|
175991 |
|| (ctx->parm.stackUse && sdiff && ctx->parm.stackNormalizeDeriv)) { |
360 |
|
|
/* We don't yet record the scale position in ctx->point because |
361 |
|
|
that's done below while setting stackFsl and stackFw. So, have |
362 |
|
|
to pass stack pos info to _gageFwSet() */ |
363 |
|
258729 |
ELL_3V_COPY(ctx->point.frac, frac); |
364 |
|
|
/* these may take some time (especially if using renormalization), |
365 |
|
|
hence the conditional above */ |
366 |
|
258729 |
_gageFslSet(ctx); |
367 |
|
258729 |
_gageFwSet(ctx, idx[3], frac[3]); |
368 |
|
258729 |
} |
369 |
|
|
|
370 |
|
|
/* **** compute *stack* fsl and fw **** */ |
371 |
✗✓✗✗
|
341325 |
if (ctx->verbose > 2 && ctx->parm.stackUse) { |
372 |
|
|
fprintf(stderr, "%s: point.frac[3] %f + idx[3] %u = %f %s sif %f\n", me, |
373 |
|
|
ctx->point.frac[3], ctx->point.idx[3], |
374 |
|
|
ctx->point.frac[3] + ctx->point.idx[3], |
375 |
|
|
(sdiff ? "*NOT ==*" : "=="), sif); |
376 |
|
|
} |
377 |
✓✗ |
341325 |
if (!ctx->parm.stackUse) { |
378 |
|
341325 |
ctx->point.idx[3] = idx[3]; |
379 |
|
341325 |
ctx->point.frac[3] = frac[3]; |
380 |
|
341325 |
ctx->point.stackFwNonZeroNum = 0; |
381 |
✗✗ |
341325 |
} else if (sdiff) { |
382 |
|
|
double sum; |
383 |
|
|
unsigned int ii, nnz; |
384 |
|
|
NrrdKernelSpec *sksp; |
385 |
|
|
|
386 |
|
|
/* node-centered sampling of stack indices from 0 to ctx->pvlNum-2 */ |
387 |
|
|
/* HEY: we really are quite far from implementing arbitrary |
388 |
|
|
nrrdBoundary behaviors here, and centering is stuck on node! */ |
389 |
|
|
/* HEY: honestly, the whole idea that it still makes sense to do |
390 |
|
|
low-level operations in index space, when the world-space locations |
391 |
|
|
of the samples can be non-uniform, is a little suspect. This is |
392 |
|
|
all legit for nrrdKernelTent and nrrdKernelHermiteScaleSpaceFlag, |
393 |
|
|
but is pretty fishy otherwise */ |
394 |
|
|
for (ii=0; ii<ctx->pvlNum-1; ii++) { |
395 |
|
|
ctx->stackFsl[ii] = sif - ii; |
396 |
|
|
if (ctx->verbose > 2) { |
397 |
|
|
fprintf(stderr, "%s: ctx->stackFsl[%u] = %g\n", |
398 |
|
|
me, ii, ctx->stackFsl[ii]); |
399 |
|
|
} |
400 |
|
|
} |
401 |
|
|
sksp = ctx->ksp[gageKernelStack]; |
402 |
|
|
sksp->kernel->evalN_d(ctx->stackFw, ctx->stackFsl, |
403 |
|
|
ctx->pvlNum-1, sksp->parm); |
404 |
|
|
if (ctx->verbose > 2) { |
405 |
|
|
for (ii=0; ii<ctx->pvlNum-1; ii++) { |
406 |
|
|
fprintf(stderr, "%s: ctx->stackFw[%u] = %g\n", |
407 |
|
|
me, ii, ctx->stackFw[ii]); |
408 |
|
|
} |
409 |
|
|
} |
410 |
|
|
/* compute stackFwNonZeroNum whether or not parm.stackNormalizeRecon! */ |
411 |
|
|
nnz = 0; |
412 |
|
|
if (ctx->parm.stackNormalizeRecon) { |
413 |
|
|
sum = 0; |
414 |
|
|
for (ii=0; ii<ctx->pvlNum-1; ii++) { |
415 |
|
|
nnz += !!ctx->stackFw[ii]; |
416 |
|
|
sum += ctx->stackFw[ii]; |
417 |
|
|
} |
418 |
|
|
if (!sum) { |
419 |
|
|
if (ctx->parm.generateErrStr) { |
420 |
|
|
sprintf(ctx->errStr, "%s: integral of stackFw[] is zero; " |
421 |
|
|
"can't do stack reconstruction", me); |
422 |
|
|
} else { |
423 |
|
|
strcpy(ctx->errStr, _GAGE_NON_ERR_STR); |
424 |
|
|
} |
425 |
|
|
ctx->errNum = gageErrStackIntegral; |
426 |
|
|
return 1; |
427 |
|
|
} |
428 |
|
|
for (ii=0; ii<ctx->pvlNum-1; ii++) { |
429 |
|
|
ctx->stackFw[ii] /= sum; |
430 |
|
|
} |
431 |
|
|
if (ctx->verbose > 2) { |
432 |
|
|
for (ii=0; ii<ctx->pvlNum-1; ii++) { |
433 |
|
|
fprintf(stderr, "%s: ctx->stackFw[%u] = %g\n", me, |
434 |
|
|
ii, ctx->stackFw[ii]); |
435 |
|
|
} |
436 |
|
|
} |
437 |
|
|
} else { |
438 |
|
|
for (ii=0; ii<ctx->pvlNum-1; ii++) { |
439 |
|
|
nnz += !!ctx->stackFw[ii]; |
440 |
|
|
} |
441 |
|
|
if (!nnz) { |
442 |
|
|
if (ctx->parm.generateErrStr) { |
443 |
|
|
sprintf(ctx->errStr, "%s: all stackFw[] weights are zero; " |
444 |
|
|
"can't do stack reconstruction", me); |
445 |
|
|
} else { |
446 |
|
|
strcpy(ctx->errStr, _GAGE_NON_ERR_STR); |
447 |
|
|
} |
448 |
|
|
ctx->errNum = gageErrStackIntegral; |
449 |
|
|
return 1; |
450 |
|
|
} |
451 |
|
|
} |
452 |
|
|
|
453 |
|
|
ctx->point.idx[3] = idx[3]; |
454 |
|
|
ctx->point.frac[3] = frac[3]; |
455 |
|
|
ctx->point.stackFwNonZeroNum = nnz; |
456 |
|
|
} |
457 |
|
|
|
458 |
|
341325 |
return 0; |
459 |
|
341325 |
} |