@niivue/niivue 0.12.2 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/assets/worker.9df7e894.js +1 -0
- package/dist/niivue.es.js +113 -5
- package/dist/niivue.umd.js +22 -22
- package/package.json +11 -11
- package/src/heap-verifier.js +51 -0
- package/src/index.html +3 -3
- package/src/linear-memory.js +33 -0
- package/src/niivue.js +107 -5
- package/src/nvimage.js +59 -0
- package/src/process-image.wasm +0 -0
- package/src/wasm/core.c +959 -0
- package/src/wasm/core.h +187 -0
- package/src/wasm/core32.c +3 -0
- package/src/wasm/core32.h +17 -0
- package/src/wasm/core64.c +3 -0
- package/src/wasm/core64.h +18 -0
- package/src/wasm/coreFLT.c +6285 -0
- package/src/wasm/nifti2_wasm.c +27 -0
- package/src/wasm/nifti2_wasm.h +76 -0
- package/src/wasm/walloc.c +466 -0
- package/src/worker.js +72 -0
package/src/wasm/core.c
ADDED
|
@@ -0,0 +1,959 @@
|
|
|
1
|
+
#include "core.h"
|
|
2
|
+
#define _USE_MATH_DEFINES //microsoft compiler
|
|
3
|
+
#include <float.h> //FLT_EPSILON
|
|
4
|
+
#include <limits.h>
|
|
5
|
+
#include <math.h>
|
|
6
|
+
#include <stdio.h>
|
|
7
|
+
#include <stdlib.h>
|
|
8
|
+
#ifdef __aarch64__
|
|
9
|
+
#include "arm_malloc.h"
|
|
10
|
+
#else
|
|
11
|
+
#include <immintrin.h>
|
|
12
|
+
#endif
|
|
13
|
+
|
|
14
|
+
#ifdef USING_WASM
|
|
15
|
+
#define WASM_EXPORT(name) \
|
|
16
|
+
__attribute__((export_name(#name))) \
|
|
17
|
+
name
|
|
18
|
+
|
|
19
|
+
// Pull these in from walloc.c.
|
|
20
|
+
|
|
21
|
+
void* WASM_EXPORT(walloc)(size_t size) {
|
|
22
|
+
return xmalloc(size);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
void WASM_EXPORT(wfree)(void* ptr) {
|
|
26
|
+
xfree(ptr);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
void xmemcpy ( void * destination, const void * source, size_t num ) {
|
|
30
|
+
uint8_t * s = (uint8_t*) source;
|
|
31
|
+
uint8_t * d = (uint8_t*) destination;
|
|
32
|
+
for (size_t i = 0; i < num; i++)
|
|
33
|
+
d[i] = s[i];
|
|
34
|
+
}
|
|
35
|
+
#define staticx
|
|
36
|
+
#include <nifti2_wasm.h>
|
|
37
|
+
#else
|
|
38
|
+
#define xmemcpy memcpy
|
|
39
|
+
#include <nifti2_io.h>
|
|
40
|
+
#endif
|
|
41
|
+
|
|
42
|
+
#ifndef M_PI
|
|
43
|
+
#define M_PI 3.14159265358979323846264338327
|
|
44
|
+
#endif
|
|
45
|
+
|
|
46
|
+
int nii_otsu(int* H, int nBin, int mode) {
|
|
47
|
+
//H: Histogram H[0..nBin-1] with each bin storing nuumber of pixels of this brightness
|
|
48
|
+
//nBin: number of bins in histogram, e.g. 256 for H[0..255]
|
|
49
|
+
//mode: threshold
|
|
50
|
+
double Sum = 0.0;
|
|
51
|
+
for (int v = 0; v < nBin; v++)
|
|
52
|
+
Sum = Sum + H[v];
|
|
53
|
+
if (Sum <= 0)
|
|
54
|
+
return 0;
|
|
55
|
+
double P[nBin][nBin], S[nBin][nBin];
|
|
56
|
+
P[0][0] = H[0];
|
|
57
|
+
S[0][0] = H[0];
|
|
58
|
+
for (int v = 1; v < nBin; v++) {
|
|
59
|
+
double Prob = H[v]/Sum;
|
|
60
|
+
P[0][v] = P[0][v-1]+Prob;
|
|
61
|
+
S[0][v] = S[0][v-1]+(v+1)*Prob;
|
|
62
|
+
}
|
|
63
|
+
for (int u = 1; u < nBin; u++) {
|
|
64
|
+
for (int v = u; v < nBin; v++) {
|
|
65
|
+
P[u][v] = P[0][v]-P[0][u-1];
|
|
66
|
+
S[u][v] = S[0][v]-S[0][u-1];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//result is eq 29 from Liao
|
|
70
|
+
for (int u = 0; u < nBin; u++) {
|
|
71
|
+
for (int v = u; v < nBin; v++) {
|
|
72
|
+
if (P[u][v] != 0) //avoid divide by zero errors...
|
|
73
|
+
P[u][v] = sqr(S[u][v]) / P[u][v];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
int thresh = 0;
|
|
77
|
+
if ((mode == 1) || (mode == 5)) {
|
|
78
|
+
int lo = (int)(0.25*nBin);
|
|
79
|
+
int mi = (int)(0.50*nBin);
|
|
80
|
+
int hi = (int)(0.75*nBin);
|
|
81
|
+
//double max = P[0][lo] + P[lo+1][hi] + P[hi+1][nBin-1];
|
|
82
|
+
double max = P[0][lo] + P[lo+1][mi] + P[mi+1][hi] + P[hi+1][255];
|
|
83
|
+
for (int l = 0; l < (nBin-3); l++) {
|
|
84
|
+
for (int m = l + 1; m < (nBin-2); m++) {
|
|
85
|
+
for (int h = m + 1; h < (nBin-1); h++) {
|
|
86
|
+
//double v = P[0][l]+P[l+1][h]+P[h+1][nBin-1];
|
|
87
|
+
double v = P[0][l] + P[l+1][m] + P[m+1][h] + P[h+1][255];
|
|
88
|
+
if (v > max) {
|
|
89
|
+
lo = l;
|
|
90
|
+
mi = m;
|
|
91
|
+
hi = h;
|
|
92
|
+
max = v;
|
|
93
|
+
} //new max
|
|
94
|
+
}//for h -> hi
|
|
95
|
+
} //for m -> mi
|
|
96
|
+
} //for l -> low
|
|
97
|
+
//printf(">>>>%d %d %d\n", lo, mi, hi);
|
|
98
|
+
if (mode == 1)
|
|
99
|
+
thresh = hi;
|
|
100
|
+
else
|
|
101
|
+
thresh = lo;
|
|
102
|
+
} else if ((mode == 2) || (mode == 4)) {
|
|
103
|
+
int lo = (int)(0.33*nBin);
|
|
104
|
+
int hi = (int)(0.67*nBin);
|
|
105
|
+
double max = P[0][lo] + P[lo+1][hi] + P[hi+1][nBin-1];
|
|
106
|
+
for (int l = 0; l < (nBin-2); l++) {
|
|
107
|
+
for (int h = l + 1; h < (nBin-1); h++) {
|
|
108
|
+
double v = P[0][l]+P[l+1][h]+P[h+1][nBin-1];
|
|
109
|
+
if (v > max) {
|
|
110
|
+
lo = l;
|
|
111
|
+
hi = h;
|
|
112
|
+
max = v;
|
|
113
|
+
} //new max
|
|
114
|
+
}//for h -> hi
|
|
115
|
+
} //for l -> low
|
|
116
|
+
//printf(">>>>%d %d\n", lo, hi);
|
|
117
|
+
if (mode == 1)
|
|
118
|
+
thresh = hi;
|
|
119
|
+
else
|
|
120
|
+
thresh = lo;
|
|
121
|
+
} else {
|
|
122
|
+
thresh = (int)(0.25*nBin); //nBin / 2;
|
|
123
|
+
double max = P[0][thresh]+P[thresh+1][nBin-1];
|
|
124
|
+
//exhaustively search
|
|
125
|
+
for (int i = 0; i < (nBin-1); i++) {
|
|
126
|
+
double v = P[0][i]+P[i+1][nBin-1];
|
|
127
|
+
if (v > max) {
|
|
128
|
+
thresh = i;
|
|
129
|
+
max = v;
|
|
130
|
+
}//new max
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//printf(">>>>%d\n", thresh);
|
|
134
|
+
return thresh;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#ifndef USING_WASM
|
|
138
|
+
int nifti_save(nifti_image *nim, const char *postfix) {
|
|
139
|
+
char extnii[5] = ".nii"; /* modifiable, for possible uppercase */
|
|
140
|
+
char exthdr[5] = ".hdr";
|
|
141
|
+
char extimg[5] = ".img";
|
|
142
|
+
char extgz[5] = ".gz";
|
|
143
|
+
//e.g if current filename is img.nii and postfix is "_FA", save file "img_FA.nii"
|
|
144
|
+
char *fname_in = nim->fname;
|
|
145
|
+
char *iname_in = nim->iname;
|
|
146
|
+
int nifti_type_in = nim->nifti_type;
|
|
147
|
+
char *hname = (char *)calloc(sizeof(char), strlen(nim->fname) + strlen(postfix) + 8);
|
|
148
|
+
char *iname = (char *)calloc(sizeof(char), strlen(nim->fname) + strlen(postfix) + 8);
|
|
149
|
+
//char * hext = (char *)calloc(sizeof(char),8);
|
|
150
|
+
//char * iext = (char *)calloc(sizeof(char),8);
|
|
151
|
+
const char *ext; //input extension
|
|
152
|
+
ext = nifti_find_file_extension(nim->fname);
|
|
153
|
+
strcpy(hname, nim->fname);
|
|
154
|
+
hname[strlen(hname) - strlen(ext)] = 0;
|
|
155
|
+
strcat(hname, postfix);
|
|
156
|
+
strcpy(iname, hname);
|
|
157
|
+
//default extension: .nii
|
|
158
|
+
//strcpy(hext, extnii);
|
|
159
|
+
//strcpy(iext, extnii);
|
|
160
|
+
//read environment
|
|
161
|
+
//export FSLOUTPUTTYPE=NIFTI
|
|
162
|
+
const char *key = "FSLOUTPUTTYPE";
|
|
163
|
+
char nii2Key[2] = "2";
|
|
164
|
+
char gzKey[3] = "GZ";
|
|
165
|
+
char pairKey[5] = "PAIR";
|
|
166
|
+
char *value;
|
|
167
|
+
value = getenv(key);
|
|
168
|
+
//n* has precedence, resolve conflicts between ->dim[*] and ->n*
|
|
169
|
+
nim->dim[1] = nim->nx; //e.g. crop, subsamp2offc
|
|
170
|
+
nim->dim[2] = nim->ny; //e.g. subsamp2offc
|
|
171
|
+
nim->dim[3] = nim->nz; //e.g. subsamp2offc
|
|
172
|
+
nim->dim[4] = nim->nt; //e.g. 4D -> 3D operations like mean
|
|
173
|
+
nim->dim[5] = nim->nu;
|
|
174
|
+
nim->dim[6] = nim->nv;
|
|
175
|
+
nim->dim[7] = nim->nw;
|
|
176
|
+
//d* has precedence, resolve conflicts between ->pixdim[*] and d*
|
|
177
|
+
nim->pixdim[1] = nim->dx;
|
|
178
|
+
nim->pixdim[2] = nim->dy;
|
|
179
|
+
nim->pixdim[3] = nim->dz;
|
|
180
|
+
nim->pixdim[4] = nim->dt;
|
|
181
|
+
//set dime[0]
|
|
182
|
+
int maxDim = 1;
|
|
183
|
+
for (int i = 2; i < 8; i++)
|
|
184
|
+
if (nim->dim[i] > 1)
|
|
185
|
+
maxDim = i;
|
|
186
|
+
nim->dim[0] = maxDim;
|
|
187
|
+
nim->ndim = maxDim;
|
|
188
|
+
//nim->dim[0] = 3;
|
|
189
|
+
//nim->dim[4] = 1;
|
|
190
|
+
int isGz = 0;
|
|
191
|
+
int isNifti2 = 0;
|
|
192
|
+
if ((value != NULL) && strstr(value, nii2Key))
|
|
193
|
+
isNifti2 = 1; //NIFTI2_GZ, NIFTI2_PAIR_GZ, NIFTI_GZ, NIFTI_PAIR_GZ
|
|
194
|
+
#ifdef HAVE_ZLIB // if compression is requested, make sure of suffix
|
|
195
|
+
if ((value == NULL) || strstr(value, gzKey))
|
|
196
|
+
isGz = 1; //NIFTI2_GZ, NIFTI2_PAIR_GZ, NIFTI_GZ, NIFTI_PAIR_GZ
|
|
197
|
+
#endif
|
|
198
|
+
if ((value != NULL) && strstr(value, pairKey)) {
|
|
199
|
+
strcat(hname, exthdr);
|
|
200
|
+
strcat(iname, extimg);
|
|
201
|
+
if (isNifti2)
|
|
202
|
+
nim->nifti_type = NIFTI_FTYPE_NIFTI2_2;
|
|
203
|
+
else
|
|
204
|
+
nim->nifti_type = NIFTI_FTYPE_NIFTI1_2;
|
|
205
|
+
|
|
206
|
+
if (isGz)
|
|
207
|
+
strcat(iname, extgz);
|
|
208
|
+
} else {
|
|
209
|
+
strcat(hname, extnii);
|
|
210
|
+
strcat(iname, extnii);
|
|
211
|
+
if (isNifti2)
|
|
212
|
+
nim->nifti_type = NIFTI_FTYPE_NIFTI2_1;
|
|
213
|
+
else
|
|
214
|
+
nim->nifti_type = NIFTI_FTYPE_NIFTI1_1;
|
|
215
|
+
if (isGz) {
|
|
216
|
+
strcat(hname, extgz);
|
|
217
|
+
strcat(iname, extgz);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//append extensions...
|
|
221
|
+
nim->fname = hname;
|
|
222
|
+
nim->iname = iname;
|
|
223
|
+
nifti_image_write(nim);
|
|
224
|
+
free(hname);
|
|
225
|
+
if (nim->iname != NULL)
|
|
226
|
+
free(iname);
|
|
227
|
+
//return to input names
|
|
228
|
+
nim->fname = fname_in;
|
|
229
|
+
nim->iname = iname_in;
|
|
230
|
+
nim->nifti_type = nifti_type_in;
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
mat44 xform(nifti_image *nim) {
|
|
235
|
+
if ((nim->sform_code == NIFTI_XFORM_UNKNOWN) && (nim->qform_code == NIFTI_XFORM_UNKNOWN)) {
|
|
236
|
+
mat44 m; //4x4 matrix includes translations
|
|
237
|
+
LOAD_MAT44(m, nim->dx, 0.0, 0.0, 0.0, 0.0, nim->dy, 0.0, 0.0, 0.0, 0.0, nim->dz, 0.0);
|
|
238
|
+
return m;
|
|
239
|
+
}
|
|
240
|
+
nifti_dmat44 AA = nim->sto_xyz;
|
|
241
|
+
if (nim->sform_code < nim->qform_code) //give precedence to SForm, like SPM but unlike VTK tools like ANTs
|
|
242
|
+
AA = nim->qto_xyz; //note qform more constrained than sform: quaternions can not store shears, matrices can
|
|
243
|
+
mat44 m; //4x4 matrix includes translations
|
|
244
|
+
LOAD_MAT44(m, AA.m[0][0], AA.m[0][1], AA.m[0][2], AA.m[0][3],
|
|
245
|
+
AA.m[1][0], AA.m[1][1], AA.m[1][2], AA.m[1][3],
|
|
246
|
+
AA.m[2][0], AA.m[2][1], AA.m[2][2], AA.m[2][3]);
|
|
247
|
+
return m;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
int neg_determ(nifti_image *nim) {
|
|
251
|
+
//returns -1 for negative determinant, +1 for positive
|
|
252
|
+
mat44 AA = xform(nim);
|
|
253
|
+
mat33 m;
|
|
254
|
+
LOAD_MAT33(m, AA.m[0][0], AA.m[0][1], AA.m[0][2], AA.m[1][0], AA.m[1][1], AA.m[1][2], AA.m[2][0], AA.m[2][1], AA.m[2][2]);
|
|
255
|
+
//printf("determ = %g\n", nifti_mat33_determ(m));
|
|
256
|
+
if (nifti_mat33_determ(m) < 0)
|
|
257
|
+
return 1;
|
|
258
|
+
return 0;
|
|
259
|
+
} //report if negative determinant, e.g. we don't want negative volume, eg. "brain volume of -1400cc"
|
|
260
|
+
|
|
261
|
+
nifti_image *nifti_image_read2(const char *hname, int read_data) {
|
|
262
|
+
//in fslmaths 6.0.1 the commands are different, the first preserves cal_min, cal_max
|
|
263
|
+
// fslmaths in out
|
|
264
|
+
// fslmaths in -add 0 out -odt input
|
|
265
|
+
nifti_image *nim = nifti_image_read(hname, read_data);
|
|
266
|
+
if (nim == NULL)
|
|
267
|
+
exit(134);
|
|
268
|
+
nim->cal_min = 0.0;
|
|
269
|
+
nim->cal_max = 0.0;
|
|
270
|
+
//nim->descrip = '';
|
|
271
|
+
char blank_string[128];
|
|
272
|
+
memset(&blank_string[0], 0, sizeof(blank_string));
|
|
273
|
+
memcpy(nim->descrip, blank_string, 79);
|
|
274
|
+
nim->descrip[79] = '\0';
|
|
275
|
+
strcat(nim->descrip, "6.0.5"); //target fslmaths version
|
|
276
|
+
memcpy(nim->aux_file, blank_string, 23);
|
|
277
|
+
nim->aux_file[23] = '\0';
|
|
278
|
+
memcpy(nim->intent_name, blank_string, 15);
|
|
279
|
+
nim->intent_name[15] = '\0';
|
|
280
|
+
return nim;
|
|
281
|
+
}
|
|
282
|
+
#endif //not USING_WASM
|
|
283
|
+
vec4 setVec4(float x, float y, float z) {
|
|
284
|
+
vec4 v = {{x, y, z, 1}};
|
|
285
|
+
return v;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
vec4 nifti_vect44mat44_mul(vec4 v, mat44 m) { //multiply vector * 4x4matrix
|
|
289
|
+
vec4 vO;
|
|
290
|
+
for (int i = 0; i < 4; i++) { //multiply Pcrs * m
|
|
291
|
+
vO.v[i] = 0;
|
|
292
|
+
for (int j = 0; j < 4; j++)
|
|
293
|
+
vO.v[i] += m.m[i][j] * v.v[j];
|
|
294
|
+
}
|
|
295
|
+
return vO;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
float vertexDisplacement(float x, float y, float z, mat44 m, mat44 m2) {
|
|
299
|
+
//distance between position of voxel [x,y,z] in space m versus space m2
|
|
300
|
+
vec4 vx = setVec4(x, y, z);
|
|
301
|
+
vec4 pos = nifti_vect44mat44_mul(vx, m);
|
|
302
|
+
vec4 pos2 = nifti_vect44mat44_mul(vx, m2);
|
|
303
|
+
return sqrt(sqr(pos.v[0] - pos2.v[0]));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
#ifndef USING_WASM
|
|
307
|
+
float max_displacement_mm(nifti_image *nim, nifti_image *nim2) {
|
|
308
|
+
//examines each corner of two NIfTI images and returns the max difference in vertex location
|
|
309
|
+
// used to detect if two volumes are aligned
|
|
310
|
+
mat44 m = xform(nim); //4x4 matrix includes translations
|
|
311
|
+
mat44 m2 = xform(nim2); //4x4 matrix includes translations
|
|
312
|
+
float mx = vertexDisplacement(0, 0, 0, m, m2);
|
|
313
|
+
mx = MAX(mx, vertexDisplacement(nim->nx - 1, 0, 0, m, m2));
|
|
314
|
+
mx = MAX(mx, vertexDisplacement(nim->nx - 1, nim->ny - 1, 0, m, m2));
|
|
315
|
+
mx = MAX(mx, vertexDisplacement(nim->nx - 1, nim->ny - 1, nim->nz - 1, m, m2));
|
|
316
|
+
mx = MAX(mx, vertexDisplacement(nim->nx - 1, 0, nim->nz - 1, m, m2));
|
|
317
|
+
mx = MAX(mx, vertexDisplacement(0, nim->ny - 1, 0, m, m2));
|
|
318
|
+
mx = MAX(mx, vertexDisplacement(0, nim->ny - 1, nim->nz - 1, m, m2));
|
|
319
|
+
mx = MAX(mx, vertexDisplacement(0, 0, nim->nz - 1, m, m2));
|
|
320
|
+
return mx;
|
|
321
|
+
}
|
|
322
|
+
#endif
|
|
323
|
+
|
|
324
|
+
in_hdr set_input_hdr(nifti_image *nim) {
|
|
325
|
+
//remember input datatype, slope and intercept in case user saves back to this
|
|
326
|
+
in_hdr ihdr;
|
|
327
|
+
ihdr.datatype = nim->datatype;
|
|
328
|
+
ihdr.scl_slope = nim->scl_slope;
|
|
329
|
+
ihdr.scl_inter = nim->scl_inter;
|
|
330
|
+
return ihdr;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
#ifndef USING_WASM
|
|
334
|
+
int nifti_image_change_datatype(nifti_image *nim, int dt, in_hdr *ihdr) {
|
|
335
|
+
//returns -1 on failure, 0 if okay
|
|
336
|
+
if (nim->datatype == dt)
|
|
337
|
+
return 0; //no change!
|
|
338
|
+
if (nim->nvox < 1)
|
|
339
|
+
return -1;
|
|
340
|
+
if (nim->scl_slope == 0.0f)
|
|
341
|
+
nim->scl_slope = 1.0;
|
|
342
|
+
float scl = nim->scl_slope;
|
|
343
|
+
float inter = nim->scl_inter;
|
|
344
|
+
if (ihdr->datatype == dt) { //saving BACK to original format, e.g. int16 converted to float32 for calculations and saved back to int16
|
|
345
|
+
nim->scl_slope = ihdr->scl_slope;
|
|
346
|
+
nim->scl_inter = ihdr->scl_inter;
|
|
347
|
+
} else {
|
|
348
|
+
nim->scl_slope = 1.0f;
|
|
349
|
+
nim->scl_inter = 0.0f;
|
|
350
|
+
}
|
|
351
|
+
int idt = nim->datatype; //input datatype
|
|
352
|
+
double *f64 = (double *)nim->data;
|
|
353
|
+
float *f32 = (float *)nim->data;
|
|
354
|
+
uint32_t *u32 = (uint32_t *)nim->data;
|
|
355
|
+
int32_t *i32 = (int32_t *)nim->data;
|
|
356
|
+
uint16_t *u16 = (uint16_t *)nim->data;
|
|
357
|
+
int16_t *i16 = (int16_t *)nim->data;
|
|
358
|
+
uint8_t *u8 = (uint8_t *)nim->data;
|
|
359
|
+
int8_t *i8 = (int8_t *)nim->data;
|
|
360
|
+
int ok = -1;
|
|
361
|
+
if (dt == DT_FLOAT64) {
|
|
362
|
+
nim->datatype = DT_FLOAT64;
|
|
363
|
+
nim->nbyper = 8;
|
|
364
|
+
void *dat = (void *)calloc(1, nim->nvox * sizeof(double));
|
|
365
|
+
double *o64 = (double *)dat;
|
|
366
|
+
if (idt == DT_FLOAT32) {
|
|
367
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
368
|
+
o64[i] = (u32[i] * scl) + inter;
|
|
369
|
+
ok = 0;
|
|
370
|
+
}
|
|
371
|
+
if (idt == DT_UINT32) {
|
|
372
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
373
|
+
o64[i] = (u32[i] * scl) + inter;
|
|
374
|
+
ok = 0;
|
|
375
|
+
}
|
|
376
|
+
if (idt == DT_INT32) {
|
|
377
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
378
|
+
o64[i] = (i32[i] * scl) + inter;
|
|
379
|
+
ok = 0;
|
|
380
|
+
}
|
|
381
|
+
if (idt == DT_UINT16) {
|
|
382
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
383
|
+
o64[i] = (u16[i] * scl) + inter;
|
|
384
|
+
ok = 0;
|
|
385
|
+
}
|
|
386
|
+
if (idt == DT_INT16) {
|
|
387
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
388
|
+
o64[i] = (i16[i] * scl) + inter;
|
|
389
|
+
ok = 0;
|
|
390
|
+
}
|
|
391
|
+
if (idt == DT_UINT8) {
|
|
392
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
393
|
+
o64[i] = (u8[i] * scl) + inter;
|
|
394
|
+
ok = 0;
|
|
395
|
+
}
|
|
396
|
+
if (idt == DT_INT8) {
|
|
397
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
398
|
+
o64[i] = (i8[i] * scl) + inter;
|
|
399
|
+
ok = 0;
|
|
400
|
+
}
|
|
401
|
+
if (ok == 0) {
|
|
402
|
+
free(nim->data);
|
|
403
|
+
nim->data = dat;
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
free(dat);
|
|
407
|
+
} //if (dt == DT_FLOAT64
|
|
408
|
+
if (dt == DT_FLOAT32) {
|
|
409
|
+
float *o32 = (float *)nim->data;
|
|
410
|
+
nim->datatype = DT_FLOAT32;
|
|
411
|
+
nim->nbyper = 4;
|
|
412
|
+
if (idt == DT_UINT32) {
|
|
413
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
414
|
+
o32[i] = (u32[i] * scl) + inter;
|
|
415
|
+
return 0;
|
|
416
|
+
}
|
|
417
|
+
if (idt == DT_INT32) {
|
|
418
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
419
|
+
o32[i] = (i32[i] * scl) + inter;
|
|
420
|
+
return 0;
|
|
421
|
+
}
|
|
422
|
+
//following change nbyper
|
|
423
|
+
void *dat = (void *)calloc(1, nim->nvox * sizeof(float));
|
|
424
|
+
o32 = (float *)dat;
|
|
425
|
+
if (idt == DT_FLOAT64) {
|
|
426
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
427
|
+
o32[i] = (f64[i] * scl) + inter;
|
|
428
|
+
ok = 0;
|
|
429
|
+
}
|
|
430
|
+
if (idt == DT_UINT16) {
|
|
431
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
432
|
+
o32[i] = (u16[i] * scl) + inter;
|
|
433
|
+
ok = 0;
|
|
434
|
+
}
|
|
435
|
+
if (idt == DT_INT16) {
|
|
436
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
437
|
+
o32[i] = (i16[i] * scl) + inter;
|
|
438
|
+
ok = 0;
|
|
439
|
+
}
|
|
440
|
+
if (idt == DT_UINT8) {
|
|
441
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
442
|
+
o32[i] = (u8[i] * scl) + inter;
|
|
443
|
+
ok = 0;
|
|
444
|
+
}
|
|
445
|
+
if (idt == DT_INT8) {
|
|
446
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
447
|
+
o32[i] = (i8[i] * scl) + inter;
|
|
448
|
+
ok = 0;
|
|
449
|
+
}
|
|
450
|
+
if (ok == 0) {
|
|
451
|
+
free(nim->data);
|
|
452
|
+
nim->data = dat;
|
|
453
|
+
return 0;
|
|
454
|
+
}
|
|
455
|
+
free(dat);
|
|
456
|
+
} //if (dt == DT_FLOAT32)
|
|
457
|
+
if (dt == DT_INT32) {
|
|
458
|
+
int32_t *o32 = (int32_t *)nim->data;
|
|
459
|
+
nim->datatype = DT_INT32;
|
|
460
|
+
nim->nbyper = 4;
|
|
461
|
+
if (idt == DT_UINT32) {
|
|
462
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
463
|
+
o32[i] = round((u32[i] * scl) + inter);
|
|
464
|
+
return 0;
|
|
465
|
+
}
|
|
466
|
+
if (idt == DT_FLOAT32) {
|
|
467
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
468
|
+
o32[i] = round((f32[i] * scl) + inter);
|
|
469
|
+
return 0;
|
|
470
|
+
}
|
|
471
|
+
//following change nbyper
|
|
472
|
+
void *dat = (void *)calloc(1, nim->nvox * sizeof(int32_t));
|
|
473
|
+
o32 = (int32_t *)dat;
|
|
474
|
+
if (idt == DT_FLOAT64) {
|
|
475
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
476
|
+
o32[i] = round(f64[i] * scl) + inter;
|
|
477
|
+
ok = 0;
|
|
478
|
+
}
|
|
479
|
+
if (idt == DT_UINT16) {
|
|
480
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
481
|
+
o32[i] = round((u16[i] * scl) + inter);
|
|
482
|
+
ok = 0;
|
|
483
|
+
}
|
|
484
|
+
if (idt == DT_INT16) {
|
|
485
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
486
|
+
o32[i] = round((i16[i] * scl) + inter);
|
|
487
|
+
free(nim->data);
|
|
488
|
+
nim->data = dat;
|
|
489
|
+
ok = 0;
|
|
490
|
+
}
|
|
491
|
+
if (idt == DT_UINT8) {
|
|
492
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
493
|
+
o32[i] = round((u8[i] * scl) + inter);
|
|
494
|
+
free(nim->data);
|
|
495
|
+
nim->data = dat;
|
|
496
|
+
ok = 0;
|
|
497
|
+
}
|
|
498
|
+
if (idt == DT_INT8) {
|
|
499
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
500
|
+
o32[i] = round((i8[i] * scl) + inter);
|
|
501
|
+
free(nim->data);
|
|
502
|
+
nim->data = dat;
|
|
503
|
+
ok = 0;
|
|
504
|
+
}
|
|
505
|
+
if (ok == 0) {
|
|
506
|
+
free(nim->data);
|
|
507
|
+
nim->data = dat;
|
|
508
|
+
return 0;
|
|
509
|
+
}
|
|
510
|
+
free(dat);
|
|
511
|
+
} //if (dt == DT_INT32)
|
|
512
|
+
if (dt == DT_INT16) {
|
|
513
|
+
int16_t *o16 = (int16_t *)nim->data;
|
|
514
|
+
nim->datatype = DT_INT16;
|
|
515
|
+
nim->nbyper = 2;
|
|
516
|
+
if (idt == DT_UINT16) {
|
|
517
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
518
|
+
o16[i] = round((u16[i] * scl) + inter);
|
|
519
|
+
return 0;
|
|
520
|
+
}
|
|
521
|
+
//following change nbyper
|
|
522
|
+
void *dat = (void *)calloc(1, nim->nvox * sizeof(int16_t));
|
|
523
|
+
o16 = (int16_t *)dat;
|
|
524
|
+
if (idt == DT_FLOAT64) {
|
|
525
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
526
|
+
o16[i] = round(f64[i] * scl) + inter;
|
|
527
|
+
ok = 0;
|
|
528
|
+
}
|
|
529
|
+
if (idt == DT_UINT32) {
|
|
530
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
531
|
+
o16[i] = round((u32[i] * scl) + inter);
|
|
532
|
+
ok = 0;
|
|
533
|
+
}
|
|
534
|
+
if (idt == DT_FLOAT32) {
|
|
535
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
536
|
+
o16[i] = round((f32[i] * scl) + inter);
|
|
537
|
+
ok = 0;
|
|
538
|
+
}
|
|
539
|
+
if (idt == DT_UINT8) {
|
|
540
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
541
|
+
o16[i] = round((u8[i] * scl) + inter);
|
|
542
|
+
ok = 0;
|
|
543
|
+
}
|
|
544
|
+
if (idt == DT_INT8) {
|
|
545
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
546
|
+
o16[i] = round((i8[i] * scl) + inter);
|
|
547
|
+
ok = 0;
|
|
548
|
+
}
|
|
549
|
+
if (ok == 0) {
|
|
550
|
+
free(nim->data);
|
|
551
|
+
nim->data = dat;
|
|
552
|
+
return 0;
|
|
553
|
+
}
|
|
554
|
+
free(dat);
|
|
555
|
+
} //if (dt == DT_INT16)
|
|
556
|
+
if (dt == DT_UINT16) {
|
|
557
|
+
uint16_t *o16 = (uint16_t *)nim->data;
|
|
558
|
+
nim->datatype = DT_UINT16;
|
|
559
|
+
nim->nbyper = 2;
|
|
560
|
+
if (idt == DT_INT16) {
|
|
561
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
562
|
+
o16[i] = round((i16[i] * scl) + inter);
|
|
563
|
+
return 0;
|
|
564
|
+
}
|
|
565
|
+
//following change nbyper
|
|
566
|
+
void *dat = (void *)calloc(1, nim->nvox * sizeof(int16_t));
|
|
567
|
+
o16 = (uint16_t *)dat;
|
|
568
|
+
if (idt == DT_FLOAT64) {
|
|
569
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
570
|
+
o16[i] = round(f64[i] * scl) + inter;
|
|
571
|
+
ok = 0;
|
|
572
|
+
}
|
|
573
|
+
if (idt == DT_UINT32) {
|
|
574
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
575
|
+
o16[i] = round((u32[i] * scl) + inter);
|
|
576
|
+
ok = 0;
|
|
577
|
+
}
|
|
578
|
+
if (idt == DT_FLOAT32) {
|
|
579
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
580
|
+
o16[i] = round((f32[i] * scl) + inter);
|
|
581
|
+
ok = 0;
|
|
582
|
+
}
|
|
583
|
+
if (idt == DT_UINT8) {
|
|
584
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
585
|
+
o16[i] = round((u8[i] * scl) + inter);
|
|
586
|
+
ok = 0;
|
|
587
|
+
}
|
|
588
|
+
if (idt == DT_INT8) {
|
|
589
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
590
|
+
o16[i] = round((i8[i] * scl) + inter);
|
|
591
|
+
ok = 0;
|
|
592
|
+
}
|
|
593
|
+
if (ok == 0) {
|
|
594
|
+
free(nim->data);
|
|
595
|
+
nim->data = dat;
|
|
596
|
+
return 0;
|
|
597
|
+
}
|
|
598
|
+
free(dat);
|
|
599
|
+
} //if (dt == DT_UINT16)
|
|
600
|
+
if (dt == DT_UINT8) {
|
|
601
|
+
uint8_t *o8 = (uint8_t *)nim->data;
|
|
602
|
+
nim->datatype = DT_UINT8;
|
|
603
|
+
nim->nbyper = 1;
|
|
604
|
+
if (idt == DT_INT8) {
|
|
605
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
606
|
+
o8[i] = round((i8[i] * scl) + inter);
|
|
607
|
+
return 0;
|
|
608
|
+
}
|
|
609
|
+
//following change nbyper
|
|
610
|
+
void *dat = (void *)calloc(1, nim->nvox * sizeof(uint8_t));
|
|
611
|
+
o8 = (uint8_t *)dat;
|
|
612
|
+
if (idt == DT_FLOAT64) {
|
|
613
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
614
|
+
o8[i] = round(f64[i] * scl) + inter;
|
|
615
|
+
ok = 0;
|
|
616
|
+
}
|
|
617
|
+
if (idt == DT_UINT16) {
|
|
618
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
619
|
+
o8[i] = round((u16[i] * scl) + inter);
|
|
620
|
+
ok = 0;
|
|
621
|
+
}
|
|
622
|
+
if (idt == DT_INT16) {
|
|
623
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
624
|
+
o8[i] = round((i16[i] * scl) + inter);
|
|
625
|
+
ok = 0;
|
|
626
|
+
}
|
|
627
|
+
if (idt == DT_UINT32) {
|
|
628
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
629
|
+
o8[i] = round((u32[i] * scl) + inter);
|
|
630
|
+
ok = 0;
|
|
631
|
+
}
|
|
632
|
+
if (idt == DT_FLOAT32) {
|
|
633
|
+
for (size_t i = 0; i < nim->nvox; i++)
|
|
634
|
+
o8[i] = round((f32[i] * scl) + inter);
|
|
635
|
+
ok = 0;
|
|
636
|
+
}
|
|
637
|
+
if (ok == 0) {
|
|
638
|
+
free(nim->data);
|
|
639
|
+
nim->data = dat;
|
|
640
|
+
return 0;
|
|
641
|
+
}
|
|
642
|
+
free(dat);
|
|
643
|
+
} //if (dt == DT_UINT16)
|
|
644
|
+
printfx("nifti_image_change_datatype: Unsupported datatype %d -> %d\n", idt, dt);
|
|
645
|
+
return ok;
|
|
646
|
+
} //nifti_image_change_datatype()
|
|
647
|
+
|
|
648
|
+
int *make_kernel_file(nifti_image *nim, int *nkernel, char *fin) {
|
|
649
|
+
nifti_image *nim2 = nifti_image_read(fin, 1);
|
|
650
|
+
if (!nim2) {
|
|
651
|
+
printfx("make_kernel_file: failed to read NIfTI image '%s'\n", fin);
|
|
652
|
+
return NULL;
|
|
653
|
+
}
|
|
654
|
+
int x = nim2->nx;
|
|
655
|
+
int y = nim2->ny;
|
|
656
|
+
int z = nim2->nz;
|
|
657
|
+
int xlo = (int)(-x / 2);
|
|
658
|
+
int ylo = (int)(-y / 2);
|
|
659
|
+
int zlo = (int)(-z / 2);
|
|
660
|
+
in_hdr ihdr = set_input_hdr(nim2);
|
|
661
|
+
if (nifti_image_change_datatype(nim2, DT_FLOAT32, &ihdr) != 0) {
|
|
662
|
+
nifti_image_free(nim2);
|
|
663
|
+
return NULL;
|
|
664
|
+
}
|
|
665
|
+
int n = 0;
|
|
666
|
+
float *f32 = (float *)nim2->data;
|
|
667
|
+
double sum = 0.0;
|
|
668
|
+
for (int i = 0; i < nim2->nvox; i++) {
|
|
669
|
+
if (f32[i] == 0)
|
|
670
|
+
continue;
|
|
671
|
+
sum += fabs(f32[i]);
|
|
672
|
+
n++;
|
|
673
|
+
}
|
|
674
|
+
if ((sum == 0.0) || (n == 0))
|
|
675
|
+
return NULL;
|
|
676
|
+
*nkernel = n;
|
|
677
|
+
int *kernel = (int *)_mm_malloc((n * 4) * sizeof(int), 64); //4 values: offset, xpos, ypos, weight
|
|
678
|
+
//for evenly weighted voxels:
|
|
679
|
+
//int kernelWeight = (int)((double)INT_MAX/(double)n); //requires <limits.h>
|
|
680
|
+
double kernelWeight = (double)INT_MAX / sum;
|
|
681
|
+
int vx = -1;
|
|
682
|
+
int i = 0;
|
|
683
|
+
for (int zi = zlo; zi < (zlo + z); zi++)
|
|
684
|
+
for (int yi = ylo; yi < (ylo + y); yi++)
|
|
685
|
+
for (int xi = xlo; xi < (xlo + x); xi++) {
|
|
686
|
+
vx++;
|
|
687
|
+
if (f32[vx] == 0)
|
|
688
|
+
continue;
|
|
689
|
+
kernel[i] = xi + (yi * nim->nx) + (zi * nim->nx * nim->ny);
|
|
690
|
+
kernel[i + n] = xi; //left-right wrap detection
|
|
691
|
+
kernel[i + n + n] = yi; //anterior-posterior wrap detection
|
|
692
|
+
kernel[i + n + n + n] = (int)(kernelWeight * f32[vx]); //kernel height (weight)
|
|
693
|
+
i++;
|
|
694
|
+
}
|
|
695
|
+
nifti_image_free(nim2);
|
|
696
|
+
return kernel;
|
|
697
|
+
} //make_kernel_file()
|
|
698
|
+
#endif //not USING_WASM
|
|
699
|
+
|
|
700
|
+
int *make_kernel_sphere(nifti_image *nim, int *nkernel, double mm) {
|
|
701
|
+
// sphere of radius <size> mm centered on target voxel
|
|
702
|
+
mm = fabs(mm);
|
|
703
|
+
if (mm == 0.0)
|
|
704
|
+
return NULL;
|
|
705
|
+
int x = (2 * floor(mm / nim->dx)) + 1;
|
|
706
|
+
int y = (2 * floor(mm / nim->dy)) + 1;
|
|
707
|
+
int z = (2 * floor(mm / nim->dz)) + 1;
|
|
708
|
+
int xlo = (int)(-x / 2);
|
|
709
|
+
int ylo = (int)(-y / 2);
|
|
710
|
+
int zlo = (int)(-z / 2);
|
|
711
|
+
//first pass: determine number of surviving voxels (n)
|
|
712
|
+
int n = 0;
|
|
713
|
+
for (int zi = zlo; zi < (zlo + z); zi++)
|
|
714
|
+
for (int yi = ylo; yi < (ylo + y); yi++)
|
|
715
|
+
for (int xi = xlo; xi < (xlo + x); xi++) {
|
|
716
|
+
float dx = (xi * nim->dx);
|
|
717
|
+
float dy = (yi * nim->dy);
|
|
718
|
+
float dz = (zi * nim->dz);
|
|
719
|
+
float dist = sqrt(dx * dx + dy * dy + dz * dz);
|
|
720
|
+
if (dist > mm)
|
|
721
|
+
continue;
|
|
722
|
+
n++;
|
|
723
|
+
}
|
|
724
|
+
*nkernel = n;
|
|
725
|
+
int *kernel = (int *)_mm_malloc((n * 4) * sizeof(int), 64); //4 values: offset, xpos, ypos, weight
|
|
726
|
+
int kernelWeight = (int)((double)INT_MAX / (double)n); //requires <limits.h>
|
|
727
|
+
//second pass: fill surviving voxels
|
|
728
|
+
int i = 0;
|
|
729
|
+
for (int zi = zlo; zi < (zlo + z); zi++)
|
|
730
|
+
for (int yi = ylo; yi < (ylo + y); yi++)
|
|
731
|
+
for (int xi = xlo; xi < (xlo + x); xi++) {
|
|
732
|
+
float dx = (xi * nim->dx);
|
|
733
|
+
float dy = (yi * nim->dy);
|
|
734
|
+
float dz = (zi * nim->dz);
|
|
735
|
+
float dist = sqrt(dx * dx + dy * dy + dz * dz);
|
|
736
|
+
if (dist > mm)
|
|
737
|
+
continue;
|
|
738
|
+
kernel[i] = xi + (yi * nim->nx) + (zi * nim->nx * nim->ny);
|
|
739
|
+
kernel[i + n] = xi; //left-right wrap detection
|
|
740
|
+
kernel[i + n + n] = yi; //anterior-posterior wrap detection
|
|
741
|
+
kernel[i + n + n + n] = kernelWeight; //kernel height
|
|
742
|
+
i++;
|
|
743
|
+
}
|
|
744
|
+
return kernel;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
int *make_kernel(nifti_image *nim, int *nkernel, int x, int y, int z) {
|
|
748
|
+
//returns voxels in kernel
|
|
749
|
+
x = MAX(1, x);
|
|
750
|
+
y = MAX(1, y);
|
|
751
|
+
z = MAX(1, z);
|
|
752
|
+
if (((x % 2) == 0) || ((y % 2) == 0) || ((z % 2) == 0))
|
|
753
|
+
printfx("Off-center kernel due to even dimensions.\n");
|
|
754
|
+
int n = x * y * z;
|
|
755
|
+
*nkernel = n;
|
|
756
|
+
int *kernel = (int *)_mm_malloc((n * 4) * sizeof(int), 64); //4 values: offset, xpos, ypos, weight
|
|
757
|
+
int xlo = (int)(-x / 2);
|
|
758
|
+
int ylo = (int)(-y / 2);
|
|
759
|
+
int zlo = (int)(-z / 2);
|
|
760
|
+
int i = 0;
|
|
761
|
+
int kernelWeight = (int)((double)INT_MAX / (double)n); //requires <limits.h>
|
|
762
|
+
for (int zi = zlo; zi < (zlo + z); zi++)
|
|
763
|
+
for (int yi = ylo; yi < (ylo + y); yi++)
|
|
764
|
+
for (int xi = xlo; xi < (xlo + x); xi++) {
|
|
765
|
+
//printf("%d %d %d\n", xi,yi,zi);
|
|
766
|
+
kernel[i] = xi + (yi * nim->nx) + (zi * nim->nx * nim->ny);
|
|
767
|
+
kernel[i + n] = xi; //left-right wrap detection
|
|
768
|
+
kernel[i + n + n] = yi; //anterior-posterior wrap detection
|
|
769
|
+
kernel[i + n + n + n] = kernelWeight; //kernel height
|
|
770
|
+
i++;
|
|
771
|
+
}
|
|
772
|
+
return kernel;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
//box filter, aka nearest neighbor
|
|
776
|
+
#define box_support (0.5)
|
|
777
|
+
static double box_filter(double t) {
|
|
778
|
+
if ((t > -0.5) && (t <= 0.5))
|
|
779
|
+
return (1.0);
|
|
780
|
+
return (0.0);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
//triangle filter, aka linear
|
|
784
|
+
#define triangle_support (1.0)
|
|
785
|
+
static double triangle_filter(double t) {
|
|
786
|
+
if (t < 0.0)
|
|
787
|
+
t = -t;
|
|
788
|
+
if (t < 1.0)
|
|
789
|
+
return (1.0 - t);
|
|
790
|
+
return (0.0);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
#define B_spline_support (2.0)
|
|
794
|
+
static double B_spline_filter(double t) {
|
|
795
|
+
double tt;
|
|
796
|
+
if (t < 0)
|
|
797
|
+
t = -t;
|
|
798
|
+
if (t < 1) {
|
|
799
|
+
tt = t * t;
|
|
800
|
+
return ((.5 * tt * t) - tt + (2.0 / 3.0));
|
|
801
|
+
} else if (t < 2) {
|
|
802
|
+
t = 2 - t;
|
|
803
|
+
return ((1.0 / 6.0) * (t * t * t));
|
|
804
|
+
}
|
|
805
|
+
return (0.0);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
static double sinc(double x) {
|
|
809
|
+
x *= M_PI;
|
|
810
|
+
if (x != 0)
|
|
811
|
+
return (sin(x) / x);
|
|
812
|
+
return (1.0);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
#define Lanczos3_support (3.0)
|
|
816
|
+
static double Lanczos3_filter(double t) {
|
|
817
|
+
if (t < 0)
|
|
818
|
+
t = -t;
|
|
819
|
+
if (t < 3.0)
|
|
820
|
+
return (sinc(t) * sinc(t / 3.0));
|
|
821
|
+
return (0.0);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
#define Mitchell_support (2.0)
|
|
825
|
+
#define B (1.0 / 3.0)
|
|
826
|
+
#define C (1.0 / 3.0)
|
|
827
|
+
static double Mitchell_filter(double t) {
|
|
828
|
+
double tt;
|
|
829
|
+
tt = t * t;
|
|
830
|
+
if (t < 0)
|
|
831
|
+
t = -t;
|
|
832
|
+
if (t < 1.0) {
|
|
833
|
+
t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt)) + ((-18.0 + 12.0 * B + 6.0 * C) * tt) + (6.0 - 2 * B));
|
|
834
|
+
return (t / 6.0);
|
|
835
|
+
} else if (t < 2.0) {
|
|
836
|
+
t = (((-1.0 * B - 6.0 * C) * (t * tt)) + ((6.0 * B + 30.0 * C) * tt) + ((-12.0 * B - 48.0 * C) * t) + (8.0 * B + 24 * C));
|
|
837
|
+
return (t / 6.0);
|
|
838
|
+
}
|
|
839
|
+
return (0.0);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
static double filter(double t) {
|
|
843
|
+
/* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */
|
|
844
|
+
if (t < 0.0)
|
|
845
|
+
t = -t;
|
|
846
|
+
if (t < 1.0)
|
|
847
|
+
return ((2.0 * t - 3.0) * t * t + 1.0);
|
|
848
|
+
return (0.0);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
CLIST *createFilter(int srcXsize, int dstXsize, int filterMethod) {
|
|
852
|
+
//CLIST * createFilter(int srcXsize, int dstXsize, double (*filterf)(), double fwidth) {
|
|
853
|
+
double (*filterf)() = filter;
|
|
854
|
+
double fwidth;
|
|
855
|
+
if (filterMethod == 0) {
|
|
856
|
+
filterf = box_filter;
|
|
857
|
+
fwidth = box_support;
|
|
858
|
+
} else if (filterMethod == 2) {
|
|
859
|
+
filterf = B_spline_filter;
|
|
860
|
+
fwidth = B_spline_support;
|
|
861
|
+
} else if (filterMethod == 3) {
|
|
862
|
+
filterf = Lanczos3_filter;
|
|
863
|
+
fwidth = Lanczos3_support;
|
|
864
|
+
} else if (filterMethod == 4) {
|
|
865
|
+
filterf = Mitchell_filter;
|
|
866
|
+
fwidth = Mitchell_support;
|
|
867
|
+
} else { //method 1 is the default: linear
|
|
868
|
+
filterf = triangle_filter;
|
|
869
|
+
fwidth = triangle_support;
|
|
870
|
+
}
|
|
871
|
+
CLIST *contrib = (CLIST *)calloc(dstXsize, sizeof(CLIST));
|
|
872
|
+
double xscale = (double)dstXsize / (double)srcXsize;
|
|
873
|
+
double width, fscale, weight;
|
|
874
|
+
double center, left, right;
|
|
875
|
+
int i, j, k, n;
|
|
876
|
+
if (xscale < 1.0) { //image reduction requires anti-aliasing
|
|
877
|
+
width = fwidth / xscale;
|
|
878
|
+
fscale = 1.0 / xscale;
|
|
879
|
+
for (i = 0; i < dstXsize; ++i) {
|
|
880
|
+
contrib[i].n = 0;
|
|
881
|
+
contrib[i].p = (CONTRIB *)calloc((int)(width * 2 + 1), sizeof(CONTRIB));
|
|
882
|
+
center = (double)i / xscale;
|
|
883
|
+
left = ceil(center - width);
|
|
884
|
+
right = floor(center + width);
|
|
885
|
+
for (j = (int)left; j <= (int)right; ++j) {
|
|
886
|
+
weight = center - (double)j;
|
|
887
|
+
weight = (*filterf)(weight / fscale) / fscale;
|
|
888
|
+
if (j < 0) {
|
|
889
|
+
n = -j;
|
|
890
|
+
} else if (j >= srcXsize) {
|
|
891
|
+
n = (srcXsize - j) + srcXsize - 1;
|
|
892
|
+
} else {
|
|
893
|
+
n = j;
|
|
894
|
+
}
|
|
895
|
+
k = contrib[i].n++;
|
|
896
|
+
contrib[i].p[k].pixel = n;
|
|
897
|
+
contrib[i].p[k].weight = weight;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
} else { //if shrink else zoom
|
|
901
|
+
for (i = 0; i < dstXsize; ++i) {
|
|
902
|
+
contrib[i].n = 0;
|
|
903
|
+
contrib[i].p = (CONTRIB *)calloc((int)(fwidth * 2 + 1),
|
|
904
|
+
sizeof(CONTRIB));
|
|
905
|
+
center = (double)i / xscale;
|
|
906
|
+
left = ceil(center - fwidth);
|
|
907
|
+
right = floor(center + fwidth);
|
|
908
|
+
for (j = (int)left; j <= (int)right; ++j) {
|
|
909
|
+
weight = center - (double)j;
|
|
910
|
+
weight = (*filterf)(weight);
|
|
911
|
+
if (j < 0)
|
|
912
|
+
n = -j;
|
|
913
|
+
else if (j >= srcXsize)
|
|
914
|
+
n = (srcXsize - j) + srcXsize - 1;
|
|
915
|
+
else
|
|
916
|
+
n = j;
|
|
917
|
+
k = contrib[i].n++;
|
|
918
|
+
contrib[i].p[k].pixel = n;
|
|
919
|
+
contrib[i].p[k].weight = weight;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
} //if shrink else zoom
|
|
923
|
+
return contrib;
|
|
924
|
+
} //createFilter()
|
|
925
|
+
|
|
926
|
+
//https://raw.githubusercontent.com/afni/afni/b6a9f7a21c1f3231ff09efbd861f8975ad48e525/src/mri_stats.c
|
|
927
|
+
/*******************************************************************/
|
|
928
|
+
/**** Given p, return x such that Q(x)=p, for 0 < p < 1. ****/
|
|
929
|
+
/**** Q(x) = 1-P(x) = reversed cdf of N(0,1) variable. ****/
|
|
930
|
+
/*******************************************************************/
|
|
931
|
+
//qg and qginv are from AFNI, early AFNI code is GPL later is public domain
|
|
932
|
+
// these are likely early routines, but generic formula
|
|
933
|
+
// if one wants to remove GPL, this should be investigated more carefully
|
|
934
|
+
|
|
935
|
+
double qg(double x) { return 0.5 * erfc(x / 1.414213562373095); }
|
|
936
|
+
|
|
937
|
+
double qginv(double p) {
|
|
938
|
+
double dp, dx, dt, ddq, dq;
|
|
939
|
+
int newt; /* not Gingrich, but Isaac */
|
|
940
|
+
|
|
941
|
+
dp = (p <= 0.5) ? (p) : (1.0 - p); /* make between 0 and 0.5 */
|
|
942
|
+
|
|
943
|
+
if (dp <= 6.1172e-39) { /* cut off at 13 sigma */
|
|
944
|
+
dx = 13.0;
|
|
945
|
+
return ((p <= 0.5) ? (dx) : (-dx));
|
|
946
|
+
}
|
|
947
|
+
/** Step 1: use 26.2.23 from Abramowitz and Stegun **/
|
|
948
|
+
dt = sqrt(-2.0 * log(dp));
|
|
949
|
+
dx = dt - ((.010328 * dt + .802853) * dt + 2.515517) / (((.001308 * dt + .189269) * dt + 1.432788) * dt + 1.);
|
|
950
|
+
/** Step 2: do 3 Newton steps to improve this (uses the math library erfc function) **/
|
|
951
|
+
for (newt = 0; newt < 3; newt++) {
|
|
952
|
+
dq = 0.5 * erfc(dx / 1.414213562373095) - dp;
|
|
953
|
+
ddq = exp(-0.5 * dx * dx) / 2.506628274631000;
|
|
954
|
+
dx = dx + dq / ddq;
|
|
955
|
+
}
|
|
956
|
+
if (dx > 13.0)
|
|
957
|
+
dx = 13.0;
|
|
958
|
+
return ((p <= 0.5) ? (dx) : (-dx)); /* return with correct sign */
|
|
959
|
+
}
|