@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.
@@ -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
+ }