GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/gage/st.c Lines: 0 153 0.0 %
Date: 2017-05-26 Branches: 0 94 0.0 %

Line Branch Exec Source
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
#define GAGE_CACHE_LEN 1013
28
29
unsigned int
30
_gageHash(int x, int y, int z) {
31
  unsigned int h, g;
32
  unsigned char s[6];
33
  int i;
34
35
  s[0] = x & 0xff;
36
  s[1] = (x >> 8) & 0xff;
37
  s[2] = y & 0xff;
38
  s[3] = (y >> 8) & 0xff;
39
  s[4] = z & 0xff;
40
  s[5] = (z >> 8) & 0xff;
41
42
  h = 0;
43
  for (i=0; i<=5; i++) {
44
    h = (h << 4) + s[i];
45
    if ((g = h & 0xf0000000)) {
46
      h = h ^ (g >> 24);
47
      h = h ^ g;
48
    }
49
  }
50
  return h % GAGE_CACHE_LEN;
51
}
52
53
void
54
_gageCacheProbe(gageContext *ctx, double *grad,
55
                int *cc, double *gc,
56
                int x, int y, int z) {
57
  int hi;
58
59
  hi = _gageHash(x, y, z);
60
  if ( (cc[3*hi + 0] == x) &&
61
       (cc[3*hi + 1] == y) &&
62
       (cc[3*hi + 2] == z) ) {
63
    /* cache hit */
64
    ELL_3V_COPY(grad, gc + 3*hi);
65
  } else {
66
    /* cache miss */
67
    cc[3*hi + 0] = x;
68
    cc[3*hi + 1] = y;
69
    cc[3*hi + 2] = z;
70
    gageProbe(ctx, x, y, z);
71
    ELL_3V_COPY(gc + 3*hi, grad);
72
  }
73
  return ;
74
}
75
76
/*
77
******** gageStructureTensor()
78
**
79
** Computes volume of structure tensors.  Currently, only works on a scalar
80
** fields (for multi-scalar fields, just add structure tensors from each
81
** component scalar), and only uses the B-spline kernel for differentiation
82
** and derivative blurring.
83
**
84
** Note, if you want to use dsmp > 1, its your responsibility to give
85
** an appropriate iScale > 1, so that you don't undersample.
86
*/
87
int
88
gageStructureTensor(Nrrd *nout, const Nrrd *nin,
89
                    int dScale, int iScale, int dsmp) {
90
  static const char me[]="gageStructureTensor";
91
  NrrdKernelSpec *gk0, *gk1, *ik0;
92
  int E, rad, diam, osx, osy, osz, oxi, oyi, ozi,
93
    _ixi, _iyi, _izi, ixi, iyi, izi, wi, *coordCache;
94
  gageContext *ctx;
95
  gageQuery query;
96
  gagePerVolume *pvl;
97
  airArray *mop;
98
  double *grad, *ixw, *iyw, *izw, wght, sten[6], *gradCache, *out;
99
  double xs, ys, zs, ms;
100
101
  if (!(nout && nin)) {
102
    biffAddf(GAGE, "%s: got NULL pointer", me);
103
    return 1;
104
  }
105
  if (!( 3 == nin->dim && nrrdTypeBlock != nin->type )) {
106
    biffAddf(GAGE, "%s: nin isn't a 3D non-block type nrrd", me);
107
    return 1;
108
  }
109
  /*
110
  if (!( AIR_EXISTS(nin->axis[0].spacing)
111
         && AIR_EXISTS(nin->axis[1].spacing)
112
         && AIR_EXISTS(nin->axis[2].spacing) )) {
113
    biffAddf(GAGE, "%s: nin's axis 0,1,2 spacings don't all exist", me);
114
    return 1;
115
  }
116
  */
117
  if (!( dScale >= 1 && iScale >= 1 && dsmp >= 1 )) {
118
    biffAddf(GAGE, "%s: dScale (%d), iScale (%d), dsmp (%d) not all >= 1",
119
             me, dScale, iScale, dsmp);
120
    return 1;
121
  }
122
123
  mop = airMopNew();
124
  gk0 = nrrdKernelSpecNew();
125
  gk1 = nrrdKernelSpecNew();
126
  ik0 = nrrdKernelSpecNew();
127
  airMopAdd(mop, gk0, (airMopper)nrrdKernelSpecNix, airMopAlways);
128
  airMopAdd(mop, gk1, (airMopper)nrrdKernelSpecNix, airMopAlways);
129
  airMopAdd(mop, ik0, (airMopper)nrrdKernelSpecNix, airMopAlways);
130
  if ( nrrdKernelSpecParse(gk0, "cubic:1,0")
131
       || nrrdKernelSpecParse(gk1, "cubicd:1,0")
132
       || nrrdKernelSpecParse(ik0, "cubic:1,0")) {
133
    biffMovef(GAGE, NRRD, "%s: couldn't set up kernels", me);
134
    airMopError(mop); return 1;
135
  }
136
  /* manually set scale parameters */
137
  gk0->parm[0] = dScale;
138
  gk1->parm[0] = dScale;
139
  ik0->parm[0] = 1.0;       /* this is more complicated . . . */
140
  ctx = gageContextNew();
141
  airMopAdd(mop, ctx, (airMopper)gageContextNix, airMopAlways);
142
  gageParmSet(ctx, gageParmRenormalize, AIR_TRUE);
143
  E = 0;
144
  if (!E) E |= !(pvl = gagePerVolumeNew(ctx, nin, gageKindScl));
145
  if (!E) E |= gagePerVolumeAttach(ctx, pvl);
146
  if (!E) E |= gageKernelSet(ctx, gageKernel00, gk0->kernel, gk0->parm);
147
  if (!E) E |= gageKernelSet(ctx, gageKernel11, gk1->kernel, gk1->parm);
148
  if (!E) GAGE_QUERY_RESET(query);
149
  if (!E) GAGE_QUERY_ITEM_ON(query, gageSclGradVec);
150
  if (!E) E |= gageQuerySet(ctx, pvl, query);
151
  if (!E) E |= gageUpdate(ctx);
152
  if (E) {
153
    biffAddf(GAGE, "%s: ", me);
154
    airMopError(mop); return 1;
155
  }
156
  grad = _gageAnswerPointer(ctx, pvl, gageSclGradVec);
157
158
  xs = nin->axis[0].spacing;
159
  ys = nin->axis[1].spacing;
160
  zs = nin->axis[2].spacing;
161
  xs = AIR_EXISTS(xs) ? AIR_ABS(xs) : 1.0;
162
  ys = AIR_EXISTS(ys) ? AIR_ABS(ys) : 1.0;
163
  zs = AIR_EXISTS(zs) ? AIR_ABS(zs) : 1.0;
164
  ms = airCbrt(xs*ys*zs);
165
  /* ms is geometric mean of {xs,ys,zs} */
166
  xs /= ms;
167
  ys /= ms;
168
  zs /= ms;
169
  fprintf(stderr, "iScale = %d, xs, ys, zs = %g, %g, %g\n",
170
          iScale, xs, ys, xs);
171
172
  rad = 0;
173
  ik0->parm[0] = iScale/xs;
174
  rad = AIR_MAX(rad, AIR_ROUNDUP(ik0->kernel->support(ik0->parm)));
175
  ik0->parm[0] = iScale/ys;
176
  rad = AIR_MAX(rad, AIR_ROUNDUP(ik0->kernel->support(ik0->parm)));
177
  ik0->parm[0] = iScale/zs;
178
  rad = AIR_MAX(rad, AIR_ROUNDUP(ik0->kernel->support(ik0->parm)));
179
  diam = 2*rad + 1;
180
  ixw = AIR_CALLOC(diam, double);
181
  iyw = AIR_CALLOC(diam, double);
182
  izw = AIR_CALLOC(diam, double);
183
  airMopAdd(mop, ixw, airFree, airMopAlways);
184
  airMopAdd(mop, iyw, airFree, airMopAlways);
185
  airMopAdd(mop, izw, airFree, airMopAlways);
186
  if (!(ixw && iyw && izw)) {
187
    biffAddf(GAGE, "%s: couldn't allocate grad vector or weight buffers", me);
188
    airMopError(mop); return 1;
189
  }
190
191
  /* the only reason that it is thread-safe to cache gageProbe results,
192
     without having the cache hang directly off the gageContext, is that
193
     we're doing all the probing for one context in one shot- producing
194
     an entirely volume of structure tensors with one function call */
195
  gradCache = AIR_CALLOC(3*GAGE_CACHE_LEN, double);
196
  coordCache = AIR_CALLOC(3*GAGE_CACHE_LEN, int);
197
  airMopAdd(mop, gradCache, airFree, airMopAlways);
198
  airMopAdd(mop, coordCache, airFree, airMopAlways);
199
  if (!(gradCache && coordCache)) {
200
    biffAddf(GAGE, "%s: couldn't allocate caches", me);
201
    airMopError(mop); return 1;
202
  }
203
  for (ixi=0; ixi<GAGE_CACHE_LEN; ixi++) {
204
    coordCache[3*ixi + 0] = -1;
205
    coordCache[3*ixi + 1] = -1;
206
    coordCache[3*ixi + 2] = -1;
207
  }
208
209
  for (wi=-rad; wi<=rad; wi++) {
210
    ik0->parm[0] = iScale/xs;
211
    ixw[wi+rad] = ik0->kernel->eval1_d(wi, ik0->parm);
212
    ik0->parm[0] = iScale/ys;
213
    iyw[wi+rad] = ik0->kernel->eval1_d(wi, ik0->parm);
214
    ik0->parm[0] = iScale/zs;
215
    izw[wi+rad] = ik0->kernel->eval1_d(wi, ik0->parm);
216
    fprintf(stderr, "%d --> (%g,%g,%g) -> (%g,%g,%g)\n",
217
            wi, wi/xs, wi/ys, wi/zs, ixw[wi+rad], iyw[wi+rad], izw[wi+rad]);
218
  }
219
220
  osx = (nin->axis[0].size)/dsmp;
221
  osy = (nin->axis[1].size)/dsmp;
222
  osz = (nin->axis[2].size)/dsmp;
223
  if (nrrdMaybeAlloc_va(nout, nrrdTypeDouble, 4,
224
                        AIR_CAST(size_t, 7),
225
                        AIR_CAST(size_t, osx),
226
                        AIR_CAST(size_t, osy),
227
                        AIR_CAST(size_t, osz))) {
228
    biffAddf(GAGE, NRRD, "%s: couldn't allocate output", me);
229
    airMopError(mop); return 1;
230
  }
231
  airMopAdd(mop, nout, (airMopper)nrrdEmpty, airMopOnError);
232
233
  out = AIR_CAST(double *, nout->data);
234
  for (ozi=0; ozi<osz; ozi++) {
235
    fprintf(stderr, "%s: z = %d/%d\n", me, ozi+1, osz);
236
    for (oyi=0; oyi<osy; oyi++) {
237
      for (oxi=0; oxi<osx; oxi++) {
238
239
        sten[0] = sten[1] = sten[2] = sten[3] = sten[4] = sten[5] = 0;
240
        for (_izi=0; _izi<diam; _izi++) {
241
          izi = AIR_CLAMP(0, _izi - rad + ozi*dsmp,
242
                          (int)nin->axis[2].size-1);
243
          if (!izw[_izi]) continue;
244
          for (_iyi=0; _iyi<diam; _iyi++) {
245
            iyi = AIR_CLAMP(0, _iyi - rad + oyi*dsmp,
246
                            (int)nin->axis[1].size-1);
247
            if (!iyw[_iyi]) continue;
248
            for (_ixi=0; _ixi<diam; _ixi++) {
249
              ixi = AIR_CLAMP(0, _ixi - rad + oxi*dsmp,
250
                              (int)nin->axis[0].size-1);
251
              if (!ixw[_ixi]) continue;
252
              wght = ixw[_ixi]*iyw[_iyi]*izw[_izi];
253
              _gageCacheProbe(ctx, grad, coordCache, gradCache, ixi, iyi, izi);
254
              sten[0] += wght*grad[0]*grad[0];
255
              sten[1] += wght*grad[0]*grad[1];
256
              sten[2] += wght*grad[0]*grad[2];
257
              sten[3] += wght*grad[1]*grad[1];
258
              sten[4] += wght*grad[1]*grad[2];
259
              sten[5] += wght*grad[2]*grad[2];
260
            }
261
          }
262
        }
263
        out[0] = 1.0;
264
        out[1] = sten[0];
265
        out[2] = sten[1];
266
        out[3] = sten[2];
267
        out[4] = sten[3];
268
        out[5] = sten[4];
269
        out[6] = sten[5];
270
        out += 7;
271
272
      }
273
    }
274
  }
275
276
  airMopOkay(mop);
277
  return 0;
278
}