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 "ten.h"
25 #include "privateTen.h"
26 
27 double
tenBVecNonLinearFit_error(double * bb,double * ss,double * ww,int len,double amp,double dec)28 tenBVecNonLinearFit_error(double *bb, double *ss, double *ww, int len,
29                           double amp, double dec) {
30   int ii;
31   double err, tmp;
32 
33   err = 0;
34   for (ii=0; ii<len; ii++) {
35     tmp = ww[ii]*(amp*exp(-dec*bb[ii]) - ss[ii]);
36     err += tmp*tmp;
37   }
38   return err;
39 }
40 
41 void
tenBVecNonLinearFit_linear(double * amp,double * dec,double * bb,double * ss,double * ww,int len)42 tenBVecNonLinearFit_linear(double *amp, double *dec,
43                            double *bb, double *ss, double *ww, int len) {
44   double x, y, wi=0, xi=0, yi=0, xiyi=0, xisq=0, det;
45   int ii;
46 
47   for (ii=0; ii<len; ii++) {
48     x = bb[ii];
49     y = log(AIR_MAX(ss[ii], 0.01));
50     xi += ww[ii]*x;
51     yi += ww[ii]*y;
52     xiyi += ww[ii]*x*y;
53     xisq += ww[ii]*x*x;
54     wi += ww[ii];
55   }
56   det = xisq*wi - xi*xi;
57   *dec = -(wi*xiyi - xi*yi)/det;   /* negative sign assumed in model */
58   *amp = exp((-xi*xiyi + xisq*yi)/det);
59   return;
60 }
61 
62 void
tenBVecNonLinearFit_GNstep(double * d_amp,double * d_dec,double * bb,double * ss,double * ww,int len,double amp,double dec)63 tenBVecNonLinearFit_GNstep(double *d_amp, double *d_dec,
64                            double *bb, double *ss, double *ww, int len,
65                            double amp, double dec) {
66   double tmp, ff, dfdx1, dfdx2, AA=0, BB=0, CC=0, JTf[2], det;
67   int ii;
68 
69   JTf[0] = JTf[1] = 0;
70   for (ii=0; ii<len; ii++) {
71     tmp = exp(-dec*bb[ii]);
72     ff = ww[ii]*(amp*tmp - ss[ii]);
73     dfdx1 = ww[ii]*tmp;
74     dfdx2 = -ww[ii]*amp*bb[ii]*tmp;
75     AA += dfdx1*dfdx1;
76     BB += dfdx1*dfdx2;
77     CC += dfdx2*dfdx2;
78     JTf[0] += dfdx1*ff;
79     JTf[1] += dfdx2*ff;
80   }
81   det = AA*CC - BB*BB;
82   *d_amp = -(CC*JTf[0] - BB*JTf[1])/det;
83   *d_dec = -(-BB*JTf[0] + AA*JTf[1])/det;
84   return;
85 }
86 
87 
88 /*
89 ******** tenBVecNonLinearFit
90 **
91 ** Assuming that axis 0 represents a sequence of DWI measurements at a
92 ** range of b values (as described by bb[i]), do non-linear least-squares
93 ** fitting of those measurements, governed by weights ww[i] (with at
94 ** most iterMax interations, or terminated when L2 norm change < eps).
95 **
96 ** Based on model fit amp*exp(-b*dec), output nrrd's axis 0 has three values:
97 ** 0: amp
98 ** 1: dec
99 ** 2: error of fit
100 ** and all other axes are unchanged from input.  Output type is always double.
101 */
102 int
tenBVecNonLinearFit(Nrrd * nout,const Nrrd * nin,double * bb,double * ww,int iterMax,double eps)103 tenBVecNonLinearFit(Nrrd *nout, const Nrrd *nin,
104                     double *bb, double *ww, int iterMax, double eps) {
105   static const char me[]="tenBVecNonLinearFit";
106   int map[NRRD_DIM_MAX], vecSize, iter;
107   size_t ii, size[NRRD_DIM_MAX], vecI, vecNum;
108   char *vec;
109   double *out, ss[AIR_STRLEN_SMALL], amp, dec, d_amp, d_dec, error, diff,
110     (*vecLup)(const void *v, size_t I);
111 
112   if (!( nout && nin && bb && ww )) {
113     biffAddf(TEN, "%s: got NULL pointer", me);
114     return 1;
115   }
116 
117   if (!( nin->dim >= 2 )) {
118     biffAddf(TEN, "%s: nin->dim (%d) not >= 2", me, nin->dim);
119     return 1;
120   }
121   if (!( nin->axis[0].size < AIR_STRLEN_SMALL )) {
122     char stmp[AIR_STRLEN_SMALL];
123     biffAddf(TEN, "%s: sorry need nin->axis[0].size (%s) < %d", me,
124              airSprintSize_t(stmp, nin->axis[0].size), AIR_STRLEN_SMALL);
125     return 1;
126   }
127 
128   /* allocate/set-up output */
129   nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size);
130   size[0] = 3;
131   if (nrrdMaybeAlloc_nva(nout, nrrdTypeDouble, nin->dim, size)) {
132     biffMovef(TEN, NRRD, "%s: couldn't allocate output", me);
133     return 1;
134   }
135   for (ii=1; ii<nin->dim; ii++) {
136     map[ii] = ii;
137   }
138   map[0] = -1;
139   if (nrrdAxisInfoCopy(nout, nin, map, NRRD_AXIS_INFO_NONE)) {
140     biffMovef(TEN, NRRD, "%s: couldn't copy axis info", me);
141     return 1;
142   }
143 
144   /* process all b vectors */
145   vecSize = nin->axis[0].size*nrrdTypeSize[nin->type];
146   vecNum = nrrdElementNumber(nin)/nin->axis[0].size;
147   vecLup = nrrdDLookup[nin->type];
148   vec = (char*)nin->data;
149   out = (double*)nout->data;
150   for (vecI=0; vecI<vecNum; vecI++) {
151     /* copy DWI signal values */
152     for (ii=0; ii<nin->axis[0].size; ii++) {
153       ss[ii] = vecLup(vec, ii);
154     }
155     /* start with linear fit */
156     tenBVecNonLinearFit_linear(&amp, &dec, bb, ss, ww, nin->axis[0].size);
157     error = tenBVecNonLinearFit_error(bb, ss, ww, nin->axis[0].size, amp, dec);
158     /* possibly refine with gauss-newton */
159     if (iterMax > 0) {
160       iter = 0;
161       do {
162         iter++;
163         tenBVecNonLinearFit_GNstep(&d_amp, &d_dec,
164                                    bb, ss, ww, nin->axis[0].size, amp, dec);
165         amp += 0.3*d_amp;
166         dec += 0.3*d_dec;
167         diff = d_amp*d_amp + d_dec*d_dec;
168       } while (iter < iterMax && diff > eps);
169     }
170     error = tenBVecNonLinearFit_error(bb, ss, ww, nin->axis[0].size, amp, dec);
171     out[0] = amp;
172     out[1] = dec;
173     out[2] = error;
174     vec += vecSize;
175     out += 3;
176   }
177 
178   return 0;
179 }
180 
181