1 /*
2 Teem: Tools to process and visualize scientific data and images .
3 Copyright (C) 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 "teem/gage.h"
25
26 #define PROBE "probePolynomial"
27
28 /*
29 ** Tests:
30 **
31 */
32
33 /* the hope is that without too much work it would be possible to
34 increase this to 4th-order polynomials and 4th-order derivatives */
35 #define POWER_MAX 3
36
37 /*
38 ** polyeval takes a polynomial defined by coef --
39 ** coef[i][j][k] is the coefficient for (x^i)*(y^j)*(z^k) --
40 ** and evaluates at position pos[] the dx-th derivative along X,
41 ** the dy-th derivative along Y, and the dz-th derivative along Z,
42 ** dx = dy = dz = 0 to evaluate the polynomial itself
43 */
44 static double
polyeval(double coef[POWER_MAX+1][POWER_MAX+1][POWER_MAX+1],unsigned int dx,unsigned int dy,unsigned int dz,const double pos[3])45 polyeval(double coef[POWER_MAX+1][POWER_MAX+1][POWER_MAX+1],
46 unsigned int dx, unsigned int dy, unsigned int dz,
47 const double pos[3]) {
48 unsigned int xi, yi, zi;
49 double tmp, ret,
50 xp[POWER_MAX+1], yp[POWER_MAX+1], zp[POWER_MAX+1],
51 dc[POWER_MAX+1][POWER_MAX+1] = {
52 {1.0, 1.0, 1.0, 1.0}, /* how coeffs are scaled for 0th derivative */
53 {0.0, 1.0, 2.0, 3.0}, /* how coeffs are scaled for 1st derivative */
54 {0.0, 0.0, 2.0, 6.0}, /* how coeffs are scaled for 2nd derivative */
55 {0.0, 0.0, 0.0, 6.0}}; /* how coeffs are scaled for 3rd derivative */
56
57 tmp = 1.0;
58 for (xi=0; xi<=POWER_MAX; xi++) {
59 xp[xi] = tmp;
60 tmp *= pos[0];
61 }
62 tmp = 1.0;
63 for (yi=0; yi<=POWER_MAX; yi++) {
64 yp[yi] = tmp;
65 tmp *= pos[1];
66 }
67 tmp = 1.0;
68 for (zi=0; zi<=POWER_MAX; zi++) {
69 zp[zi] = tmp;
70 tmp *= pos[2];
71 }
72
73 ret = 0.0;
74 for (xi=dx; xi<=POWER_MAX; xi++) {
75 for (yi=dy; yi<=POWER_MAX; yi++) {
76 for (zi=dz; zi<=POWER_MAX; zi++) {
77 ret += (coef[xi][yi][zi]*xp[xi-dx]*yp[yi-dy]*zp[zi-dz]
78 *dc[dx][xi]*dc[dy][yi]*dc[dz][zi]);
79 }
80 }
81 }
82
83 return ret;
84 }
85
86 static void
randvec(double vec[NRRD_SPACE_DIM_MAX],double lenexp,double lensig,airRandMTState * rng)87 randvec(double vec[NRRD_SPACE_DIM_MAX], double lenexp, double lensig,
88 airRandMTState *rng) {
89 double tmp, clen[2], len;
90
91 airNormalRand_r(vec + 0, vec + 1, rng);
92 airNormalRand_r(vec + 2, NULL, rng);
93 ELL_3V_NORM(vec, vec, tmp);
94 airNormalRand_r(clen + 0, clen + 1, rng);
95 /* rician noise, actually */
96 clen[0] = lenexp + lensig*clen[0];
97 clen[1] = lensig*clen[1];
98 len = ELL_2V_LEN(clen);
99 ELL_3V_SCALE(vec, len, vec);
100 }
101
102 /*
103 ** makeVolume allocates inside nin a new randomly oriented volume of
104 ** size sx-by-sy-sz; the spaceDirection vectors are random but enforced
105 ** to not be too parallel
106 */
107 static gageContext *
makeVolume(Nrrd * nin,unsigned int sx,unsigned int sy,unsigned int sz,double coef[POWER_MAX+1][POWER_MAX+1][POWER_MAX+1],airRandMTState * rng)108 makeVolume(Nrrd *nin, unsigned int sx, unsigned int sy, unsigned int sz,
109 double coef[POWER_MAX+1][POWER_MAX+1][POWER_MAX+1],
110 airRandMTState *rng) {
111 static const char me[]="makeVolume";
112 double spcOrig[NRRD_SPACE_DIM_MAX], spcVec[3][NRRD_SPACE_DIM_MAX],
113 angle10, angle20, angle21, ooff[3], aperm=0.6, lexp=0.1, lstdv=0.04,
114 kparm[NRRD_KERNEL_PARMS_NUM];
115 gageContext *gctx;
116 gagePerVolume *gpvl;
117 unsigned int xi, yi, zi;
118 airArray *submop;
119 int EE;
120
121 submop = airMopNew();
122 randvec(spcVec[0], lexp, lstdv, rng);
123 do {
124 randvec(spcVec[1], lexp, lstdv, rng);
125 angle10 = ell_3v_angle_d(spcVec[1], spcVec[0]);
126 } while (!( AIR_IN_OP(AIR_PI*(1-aperm)/2, angle10, AIR_PI*(1+aperm)/2) ));
127 do {
128 randvec(spcVec[2], lexp, lstdv, rng);
129 angle20 = ell_3v_angle_d(spcVec[2], spcVec[0]);
130 angle21 = ell_3v_angle_d(spcVec[2], spcVec[1]);
131 } while (!( AIR_IN_OP(AIR_PI*(1-aperm)/2, angle20, AIR_PI*(1+aperm)/2) &&
132 AIR_IN_OP(AIR_PI*(1-aperm)/2, angle21, AIR_PI*(1+aperm)/2) ));
133
134 /* center volume near origin */
135 airNormalRand_r(ooff + 0, ooff + 1, rng);
136 airNormalRand_r(ooff + 2, NULL, rng);
137 ELL_3V_SET(spcOrig, 0.0, 0.0, 0.0);
138 ELL_3V_SCALE_INCR(spcOrig, -AIR_CAST(double, sx)/2.0 + 2*ooff[0], spcVec[0]);
139 ELL_3V_SCALE_INCR(spcOrig, -AIR_CAST(double, sy)/2.0 + 2*ooff[1], spcVec[1]);
140 ELL_3V_SCALE_INCR(spcOrig, -AIR_CAST(double, sz)/2.0 + 2*ooff[1], spcVec[2]);
141
142 /* allocate data */
143 if (nrrdMaybeAlloc_va(nin, nrrdTypeDouble, 3,
144 AIR_CAST(size_t, sx),
145 AIR_CAST(size_t, sy),
146 AIR_CAST(size_t, sz))
147 || nrrdSpaceSet(nin, nrrdSpaceRightAnteriorSuperior)
148 || nrrdSpaceOriginSet(nin, spcOrig)) {
149 biffMovef(PROBE, NRRD, "%s: trouble setting volume", me);
150 airMopError(submop); return NULL;
151 }
152 nrrdAxisInfoSet_va(nin, nrrdAxisInfoSpaceDirection,
153 spcVec[0],
154 spcVec[1],
155 spcVec[2]);
156 nrrdAxisInfoSet_va(nin, nrrdAxisInfoCenter,
157 nrrdCenterCell,
158 nrrdCenterCell,
159 nrrdCenterCell);
160
161 /* set data */
162 {
163 double *ddata = AIR_CAST(double *, nin->data);
164 for (zi=0; zi<sz; zi++) {
165 double pos[3];
166 for (yi=0; yi<sy; yi++) {
167 for (xi=0; xi<sx; xi++) {
168 ELL_3V_SCALE_ADD2(pos, 1.0, spcOrig, xi, spcVec[0]);
169 ELL_3V_SCALE_ADD2(pos, 1.0, pos, yi, spcVec[1]);
170 ELL_3V_SCALE_ADD2(pos, 1.0, pos, zi, spcVec[2]);
171 ddata[xi + sx*(yi + sy*zi)] = polyeval(coef, 0, 0, 0, pos);
172 }
173 }
174 }
175 }
176
177 /* create context, using the c4hexic kernel and its derivatives. c4hexic
178 is can reconstruct cubics without pre-filtering, and its analytical
179 derivatives are readily available. tmf:n,3,4 can also reconstruct
180 cubics but its not clear which other kernels are its derivatives */
181 gctx = gageContextNew();
182 airMopAdd(submop, gctx, (airMopper)gageContextNix, airMopOnError);
183 gageParmSet(gctx, gageParmRenormalize, AIR_FALSE);
184 gageParmSet(gctx, gageParmCheckIntegrals, AIR_TRUE);
185 EE = 0;
186 if (!EE) EE |= gageKernelSet(gctx, gageKernel00, nrrdKernelC4Hexic, kparm);
187 if (!EE) EE |= gageKernelSet(gctx, gageKernel11, nrrdKernelC4HexicD, kparm);
188 if (!EE) EE |= gageKernelSet(gctx, gageKernel22, nrrdKernelC4HexicDD, kparm);
189 if (!EE) EE |= !(gpvl = gagePerVolumeNew(gctx, nin, gageKindScl));
190 if (!EE) EE |= gagePerVolumeAttach(gctx, gpvl);
191 if (!EE) EE |= gageQueryItemOn(gctx, gpvl, gageSclValue);
192 if (!EE) EE |= gageQueryItemOn(gctx, gpvl, gageSclGradVec);
193 if (!EE) EE |= gageQueryItemOn(gctx, gpvl, gageSclHessian);
194 if (!EE) EE |= gageUpdate(gctx);
195 if (EE) {
196 biffMovef(PROBE, GAGE, "%s: trouble setting up gage", me);
197 airMopError(submop); return NULL;
198 }
199
200 airMopOkay(submop);
201 return gctx;
202 }
203
204 int
main(int argc,const char ** argv)205 main(int argc, const char **argv) {
206 airArray *mop;
207 char *err;
208
209 airRandMTState *rng;
210 Nrrd *nin;
211 unsigned int xi, yi, zi, runNum, runIdx;
212 double coef[POWER_MAX+1][POWER_MAX+1][POWER_MAX+1], pos[3], epsilon;
213 const double *vmeas, *gmeas, *hmeas;
214 gageContext *gctx;
215
216 AIR_UNUSED(argc);
217 mop = airMopNew();
218
219 rng = airRandMTStateNew(429);
220 airMopAdd(mop, rng, (airMopper)airRandMTStateNix, airMopAlways);
221
222 nin = nrrdNew();
223 airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways);
224 epsilon = 1e-8;
225 runNum = 30;
226
227 for (runIdx=0; runIdx<runNum; runIdx++) {
228 unsigned int sx, sy, sz, probeNum;
229 double avgDiff;
230 airArray *submop;
231
232 submop = airMopNew();
233 /* set random coefficients in polynomial */
234 for (xi=0; xi<=POWER_MAX; xi++) {
235 for (yi=0; yi<=POWER_MAX; yi++) {
236 for (zi=0; zi<=POWER_MAX; zi++) {
237 if (xi + yi + zi > POWER_MAX) {
238 coef[xi][yi][zi] = 0.0;
239 } else {
240 airNormalRand_r(&(coef[xi][yi][zi]), NULL, rng);
241 }
242 }
243 }
244 }
245
246 sx = 20 + airRandInt_r(rng, 120);
247 sy = 20 + airRandInt_r(rng, 120);
248 sz = 20 + airRandInt_r(rng, 120);
249 fprintf(stderr, "%u: %u %u %u: ", runIdx, sx, sy, sz); fflush(stderr);
250 if (!(gctx = makeVolume(nin, sx, sy, sz, coef, rng))) {
251 airMopAdd(mop, err = biffGetDone(PROBE), airFree, airMopAlways);
252 fprintf(stderr, "trouble creating volume:\n%s", err);
253 airMopError(submop); airMopError(mop); return 1;
254 }
255 airMopAdd(submop, gctx, (airMopper)gageContextNix, airMopAlways);
256 vmeas = gageAnswerPointer(gctx, gctx->pvl[0], gageSclValue);
257 gmeas = gageAnswerPointer(gctx, gctx->pvl[0], gageSclGradVec);
258 hmeas = gageAnswerPointer(gctx, gctx->pvl[0], gageSclHessian);
259 ELL_3V_SET(pos, 0, 0, 0);
260 gageProbeSpace(gctx, pos[0], pos[1], pos[2],
261 AIR_FALSE /* indexSpace */,
262 AIR_TRUE /* clamp */);
263 probeNum = 0;
264 avgDiff = 0.0;
265 /* take a random walk starting at (0,0,0), making sure that at the
266 visited positions the gage-measured values, gradients, and
267 hessians are the same as the analytical ones */
268 do {
269 double vanal, ganal[3], hanal[9], vdiff, gdiff[3], hdiff[9],
270 step[3], stepSize = 0.2;
271 probeNum++;
272 vanal = polyeval(coef, 0, 0, 0, pos);
273 ganal[0] = polyeval(coef, 1, 0, 0, pos);
274 ganal[1] = polyeval(coef, 0, 1, 0, pos);
275 ganal[2] = polyeval(coef, 0, 0, 1, pos);
276 hanal[0] = polyeval(coef, 2, 0, 0, pos);
277 hanal[1] = polyeval(coef, 1, 1, 0, pos);
278 hanal[2] = polyeval(coef, 1, 0, 1, pos);
279 hanal[3] = hanal[1];
280 hanal[4] = polyeval(coef, 0, 2, 0, pos);
281 hanal[5] = polyeval(coef, 0, 1, 1, pos);
282 hanal[6] = hanal[2];
283 hanal[7] = hanal[5];
284 hanal[8] = polyeval(coef, 0, 0, 2, pos);
285 vdiff = fabs(vmeas[0] - vanal);
286 ELL_3V_SUB(gdiff, gmeas, ganal);
287 ELL_3M_SUB(hdiff, hmeas, hanal);
288 if (vdiff > epsilon ||
289 ELL_3V_LEN(gdiff) > epsilon ||
290 ELL_3M_FROB(hdiff) > epsilon) {
291 fprintf(stderr, "at (%g,%g,%g) one diff %.17g %.17g %.17g "
292 "> eps %.17g\n",
293 pos[0], pos[1], pos[2], vdiff,
294 ELL_3V_LEN(gdiff), ELL_3M_FROB(hdiff), epsilon);
295 airMopError(submop); airMopError(mop); return 1;
296 }
297 avgDiff += vdiff + ELL_3V_LEN(gdiff) + ELL_3M_FROB(hdiff);
298 airNormalRand_r(step + 0, step + 1, rng);
299 airNormalRand_r(step + 2, NULL, rng);
300 ELL_3V_SCALE_INCR(pos, stepSize, step);
301 gageProbeSpace(gctx, pos[0], pos[1], pos[2],
302 AIR_FALSE /* indexSpace */,
303 AIR_TRUE /* clamp */);
304 } while (!gctx->edgeFrac);
305 avgDiff /= probeNum;
306 fprintf(stderr, "%u (%.17g)\n", probeNum, avgDiff);
307 airMopOkay(submop);
308 }
309
310 airMopOkay(mop);
311 return 0;
312 }
313