1 //#define MY_DEBUG
2 #if defined(_WIN64) || defined(_WIN32)
3 #include <windows.h> //write to registry
4 #endif
5 #ifdef _MSC_VER
6 #include <direct.h>
7 #define getcwd _getcwd
8 #define chdir _chrdir
9 #include "io.h"
10 #include <math.h>
11 //#define snprintf _snprintf
12 //#define vsnprintf _vsnprintf
13 #define strcasecmp _stricmp
14 #define strncasecmp _strnicmp
15 #ifdef _WIN32
16 #pragma comment(lib, "advapi32")
17 #endif
18 #else
19 #include <unistd.h>
20 #endif
21 //#include <time.h> //clock()
22 #ifndef USING_R
23 #include "nifti1.h"
24 #endif
25 #include "jpg_0XC3.h"
26 #include "nifti1_io_core.h"
27 #include "nii_dicom.h"
28 #include "print.h"
29 #include <ctype.h> //toupper
30 #include <float.h>
31 #include <math.h>
32 #include <stddef.h>
33 #include <stdint.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h> // discriminate files from folders
38 #include <sys/types.h>
39 
40 #ifdef USING_R
41 #undef isnan
42 #define isnan ISNAN
43 #endif
44 
45 #ifndef myDisableClassicJPEG
46 #ifdef myTurboJPEG
47 #include <turbojpeg.h>
48 #else
49 #include "ujpeg.h"
50 #endif
51 #endif
52 #ifdef myEnableJasper
53 #include <jasper/jasper.h>
54 #endif
55 #ifndef myDisableOpenJPEG
56 #include "openjpeg.h"
57 
58 #ifdef myEnableJasper
59 ERROR : YOU CAN NOT COMPILE WITH myEnableJasper AND NOT myDisableOpenJPEG OPTIONS SET SIMULTANEOUSLY
60 #endif
61 
62 unsigned char * imagetoimg(opj_image_t *image) {
63 	int numcmpts = image->numcomps;
64 	int sgnd = image->comps[0].sgnd;
65 	int width = image->comps[0].w;
66 	int height = image->comps[0].h;
67 	int bpp = (image->comps[0].prec + 7) >> 3; //e.g. 12 bits requires 2 bytes
68 	int imgbytes = bpp * width * height * numcmpts;
69 	bool isOK = true;
70 	if (numcmpts > 1) {
71 		for (int comp = 1; comp < numcmpts; comp++) { //check RGB data
72 			if (image->comps[0].w != image->comps[comp].w)
73 				isOK = false;
74 			if (image->comps[0].h != image->comps[comp].h)
75 				isOK = false;
76 			if (image->comps[0].dx != image->comps[comp].dx)
77 				isOK = false;
78 			if (image->comps[0].dy != image->comps[comp].dy)
79 				isOK = false;
80 			if (image->comps[0].prec != image->comps[comp].prec)
81 				isOK = false;
82 			if (image->comps[0].sgnd != image->comps[comp].sgnd)
83 				isOK = false;
84 		}
85 		if (numcmpts != 3)
86 			isOK = false; //we only handle Gray and RedGreenBlue, not GrayAlpha or RedGreenBlueAlpha
87 		if (image->comps[0].prec != 8)
88 			isOK = false; //only 8-bit for RGB data
89 	}
90 	if ((image->comps[0].prec < 1) || (image->comps[0].prec > 16))
91 		isOK = false; //currently we only handle 1 and 2 byte data
92 	if (!isOK) {
93 		printMessage("jpeg decode failure w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts, opj_version());
94 		return NULL;
95 	}
96 #ifdef MY_DEBUG
97 	printMessage("w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts, opj_version());
98 #endif
99 	//extract the data
100 	if ((bpp < 1) || (bpp > 2) || (width < 1) || (height < 1) || (imgbytes < 1)) {
101 		printError("Catastrophic decompression error\n");
102 		return NULL;
103 	}
104 	unsigned char *img = (unsigned char *)malloc(imgbytes);
105 	uint16_t *img16ui = (uint16_t *)img; //unsigned 16-bit
106 	int16_t *img16i = (int16_t *)img; //signed 16-bit
107 	if (sgnd)
108 		bpp = -bpp;
109 	if (bpp == -1) {
110 		free(img);
111 		printError("Signed 8-bit DICOM?\n");
112 		return NULL;
113 	}
114 	//n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB
115 	int pix = 0; //output pixel
116 	for (int cmptno = 0; cmptno < numcmpts; ++cmptno) {
117 		int cpix = 0; //component pixel
118 		int *v = image->comps[cmptno].data;
119 		for (int y = 0; y < height; ++y) {
120 			for (int x = 0; x < width; ++x) {
121 				switch (bpp) {
122 				case 1:
123 					img[pix] = (unsigned char)v[cpix];
124 					break;
125 				case 2:
126 					img16ui[pix] = (uint16_t)v[cpix];
127 					break;
128 				case -2:
129 					img16i[pix] = (int16_t)v[cpix];
130 					break;
131 				}
132 				pix++;
133 				cpix++;
134 			} //for x
135 		} //for y
136 	} //for each component
137 	return img;
138 } // imagetoimg()
139 
140 typedef struct bufinfo {
141 	unsigned char *buf;
142 	unsigned char *cur;
143 	size_t len;
144 } BufInfo;
145 
my_stream_free(void * p_user_data)146 static void my_stream_free(void *p_user_data) { //do nothing
147 	//BufInfo d = (BufInfo) p_user_data;
148 	//free(d.buf);
149 } // my_stream_free()
150 
opj_read_from_buffer(void * p_buffer,OPJ_UINT32 p_nb_bytes,BufInfo * p_file)151 static OPJ_UINT32 opj_read_from_buffer(void *p_buffer, OPJ_UINT32 p_nb_bytes, BufInfo *p_file) {
152 	OPJ_UINT32 l_nb_read;
153 	if (p_file->cur + p_nb_bytes < p_file->buf + p_file->len) {
154 		l_nb_read = p_nb_bytes;
155 	} else {
156 		l_nb_read = (OPJ_UINT32)(p_file->buf + p_file->len - p_file->cur);
157 	}
158 	memcpy(p_buffer, p_file->cur, l_nb_read);
159 	p_file->cur += l_nb_read;
160 
161 	return l_nb_read ? l_nb_read : ((OPJ_UINT32)-1);
162 } //opj_read_from_buffer()
163 
opj_write_from_buffer(void * p_buffer,OPJ_UINT32 p_nb_bytes,BufInfo * p_file)164 static OPJ_UINT32 opj_write_from_buffer(void *p_buffer, OPJ_UINT32 p_nb_bytes, BufInfo *p_file) {
165 	memcpy(p_file->cur, p_buffer, p_nb_bytes);
166 	p_file->cur += p_nb_bytes;
167 	p_file->len += p_nb_bytes;
168 	return p_nb_bytes;
169 } // opj_write_from_buffer()
170 
opj_skip_from_buffer(OPJ_SIZE_T p_nb_bytes,BufInfo * p_file)171 static OPJ_SIZE_T opj_skip_from_buffer(OPJ_SIZE_T p_nb_bytes, BufInfo *p_file) {
172 	if (p_file->cur + p_nb_bytes < p_file->buf + p_file->len) {
173 		p_file->cur += p_nb_bytes;
174 		return p_nb_bytes;
175 	}
176 	p_file->cur = p_file->buf + p_file->len;
177 	return (OPJ_SIZE_T)-1;
178 } //opj_skip_from_buffer()
179 
180 //fix for https://github.com/neurolabusc/dcm_qa/issues/5
opj_seek_from_buffer(OPJ_SIZE_T p_nb_bytes,BufInfo * p_file)181 static OPJ_BOOL opj_seek_from_buffer(OPJ_SIZE_T p_nb_bytes, BufInfo *p_file) {
182 //printf("opj_seek_from_buffer %d + %d -> %d + %d\n", p_file->cur , p_nb_bytes, p_file->buf, p_file->len);
183 	if (p_nb_bytes < p_file->len) {
184 		p_file->cur = p_file->buf + p_nb_bytes;
185 		return OPJ_TRUE;
186 	}
187 	p_file->cur = p_file->buf + p_file->len;
188 	return OPJ_FALSE;
189 } //opj_seek_from_buffer()
190 
opj_stream_create_buffer_stream(BufInfo * p_file,OPJ_UINT32 p_size,OPJ_BOOL p_is_read_stream)191 opj_stream_t *opj_stream_create_buffer_stream(BufInfo *p_file, OPJ_UINT32 p_size, OPJ_BOOL p_is_read_stream) {
192 	opj_stream_t *l_stream;
193 	if (!p_file)
194 		return NULL;
195 	l_stream = opj_stream_create(p_size, p_is_read_stream);
196 	if (!l_stream)
197 		return NULL;
198 	opj_stream_set_user_data(l_stream, p_file, my_stream_free);
199 	opj_stream_set_user_data_length(l_stream, p_file->len);
200 	opj_stream_set_read_function(l_stream, (opj_stream_read_fn)opj_read_from_buffer);
201 	opj_stream_set_write_function(l_stream, (opj_stream_write_fn)opj_write_from_buffer);
202 	opj_stream_set_skip_function(l_stream, (opj_stream_skip_fn)opj_skip_from_buffer);
203 	opj_stream_set_seek_function(l_stream, (opj_stream_seek_fn)opj_seek_from_buffer);
204 	return l_stream;
205 } //opj_stream_create_buffer_stream()
206 
nii_loadImgCoreOpenJPEG(char * imgname,struct nifti_1_header hdr,struct TDICOMdata dcm,int compressFlag)207 unsigned char *nii_loadImgCoreOpenJPEG(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, int compressFlag) {
208 	//OpenJPEG library is not well documented and has changed between versions
209 	//Since the JPEG is embedded in a DICOM we need to skip bytes at the start of the file
210 	// In theory we might also want to strip data that exists AFTER the image, see gdcmJPEG2000Codec.c
211 	unsigned char *ret = NULL;
212 	opj_dparameters_t params;
213 	opj_codec_t *codec;
214 	opj_image_t *jpx;
215 	opj_stream_t *stream;
216 	FILE *reader = fopen(imgname, "rb");
217 	fseek(reader, 0, SEEK_END);
218 	long size = ftell(reader) - dcm.imageStart;
219 	if (size <= 8)
220 		return NULL;
221 	fseek(reader, dcm.imageStart, SEEK_SET);
222 	unsigned char *data = (unsigned char *)malloc(size);
223 	size_t sz = fread(data, 1, size, reader);
224 	fclose(reader);
225 	if (sz < size)
226 		return NULL;
227 	OPJ_CODEC_FORMAT format = OPJ_CODEC_JP2;
228 	//DICOM JPEG2k is SUPPOSED to start with codestream, but some vendors include a header
229 	if (data[0] == 0xFF && data[1] == 0x4F && data[2] == 0xFF && data[3] == 0x51)
230 		format = OPJ_CODEC_J2K;
231 	opj_set_default_decoder_parameters(&params);
232 	BufInfo dx;
233 	dx.buf = data;
234 	dx.cur = data;
235 	dx.len = size;
236 	stream = opj_stream_create_buffer_stream(&dx, (OPJ_UINT32)size, true);
237 	if (stream == NULL)
238 		return NULL;
239 	codec = opj_create_decompress(format);
240 	// setup the decoder decoding parameters using user parameters
241 	if (!opj_setup_decoder(codec, &params))
242 		goto cleanup2;
243 	// Read the main header of the codestream and if necessary the JP2 boxes
244 	if (!opj_read_header(stream, codec, &jpx)) {
245 		printError("OpenJPEG failed to read the header %s (offset %d)\n", imgname, dcm.imageStart);
246 //comment these next lines to abort: include these to create zero-padded slice
247 #ifdef MY_ZEROFILLBROKENJPGS
248 		//fix broken slices https://github.com/scitran-apps/dcm2niix/issues/4
249 		printError("Zero-filled slice created\n");
250 		int imgbytes = (hdr.bitpix / 8) * hdr.dim[1] * hdr.dim[2];
251 		ret = (unsigned char *)calloc(imgbytes, 1);
252 #endif
253 		goto cleanup2;
254 	}
255 	// Get the decoded image
256 	if (!(opj_decode(codec, stream, jpx) && opj_end_decompress(codec, stream))) {
257 		printError("OpenJPEG j2k_to_image failed to decode %s\n", imgname);
258 		goto cleanup1;
259 	}
260 	ret = imagetoimg(jpx);
261 cleanup1:
262 	opj_image_destroy(jpx);
263 cleanup2:
264 	free(dx.buf);
265 	opj_stream_destroy(stream);
266 	opj_destroy_codec(codec);
267 	return ret;
268 }
269 #endif //myDisableOpenJPEG
270 
271 #ifndef M_PI
272 #define M_PI 3.14159265358979323846
273 #endif
274 
deFuzz(float v)275 float deFuzz(float v) {
276 	if (fabs(v) < 0.00001)
277 		return 0;
278 	else
279 		return v;
280 }
281 
282 #ifdef MY_DEBUG
reportMat33(char * str,mat33 A)283 void reportMat33(char *str, mat33 A) {
284 	printMessage("%s = [%g %g %g ; %g %g %g; %g %g %g ]\n", str,
285 		deFuzz(A.m[0][0]), deFuzz(A.m[0][1]), deFuzz(A.m[0][2]),
286 		deFuzz(A.m[1][0]), deFuzz(A.m[1][1]), deFuzz(A.m[1][2]),
287 		deFuzz(A.m[2][0]), deFuzz(A.m[2][1]), deFuzz(A.m[2][2]));
288 }
289 
reportMat44(char * str,mat44 A)290 void reportMat44(char *str, mat44 A) {
291 //example: reportMat44((char*)"out",*R);
292 	printMessage("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n", str,
293 		deFuzz(A.m[0][0]), deFuzz(A.m[0][1]), deFuzz(A.m[0][2]), deFuzz(A.m[0][3]),
294 		deFuzz(A.m[1][0]), deFuzz(A.m[1][1]), deFuzz(A.m[1][2]), deFuzz(A.m[1][3]),
295 		deFuzz(A.m[2][0]), deFuzz(A.m[2][1]), deFuzz(A.m[2][2]), deFuzz(A.m[2][3]));
296 }
297 #endif
298 
verify_slice_dir(struct TDICOMdata d,struct TDICOMdata d2,struct nifti_1_header * h,mat44 * R,int isVerbose)299 int verify_slice_dir(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, mat44 *R, int isVerbose) {
300 //returns slice direction: 1=sag,2=coronal,3=axial, -= flipped
301 	if (h->dim[3] < 2)
302 		return 0; //don't care direction for single slice
303 	int iSL = 1; //find Z-slice direction: row with highest magnitude of 3rd column
304 	if ((fabs(R->m[1][2]) >= fabs(R->m[0][2])) && (fabs(R->m[1][2]) >= fabs(R->m[2][2])))
305 		iSL = 2; //
306 	if ((fabs(R->m[2][2]) >= fabs(R->m[0][2])) && (fabs(R->m[2][2]) >= fabs(R->m[1][2])))
307 		iSL = 3; //axial acquisition
308 	float pos = NAN;
309 	if (!isnan(d2.patientPosition[iSL])) { //patient position fields exist
310 		pos = d2.patientPosition[iSL];
311 		if (isSameFloat(pos, d.patientPosition[iSL]))
312 			pos = NAN;
313 #ifdef MY_DEBUG
314 		if (!isnan(pos))
315 			printMessage("position determined using lastFile %f\n", pos);
316 #endif
317 	}
318 	if (isnan(pos) && (!isnan(d.patientPositionLast[iSL]))) { //patient position fields exist
319 		pos = d.patientPositionLast[iSL];
320 		if (isSameFloat(pos, d.patientPosition[iSL]))
321 			pos = NAN;
322 #ifdef MY_DEBUG
323 		if (!isnan(pos))
324 			printMessage("position determined using last (4d) %f\n", pos);
325 #endif
326 	}
327 	if (isnan(pos) && (!isnan(d.stackOffcentre[iSL])))
328 		pos = d.stackOffcentre[iSL];
329 	if (isnan(pos) && (!isnan(d.lastScanLoc)))
330 		pos = d.lastScanLoc;
331 	vec4 x;
332 	x.v[0] = 0.0;
333 	x.v[1] = 0.0;
334 	x.v[2] = (float)(h->dim[3] - 1.0);
335 	x.v[3] = 1.0;
336 	vec4 pos1v = nifti_vect44mat44_mul(x, *R);
337 	float pos1 = pos1v.v[iSL - 1]; //-1 as C indexed from 0
338 	bool flip = false;
339 	if (!isnan(pos)) // we have real SliceLocation for last slice or volume center
340 		flip = (pos > R->m[iSL - 1][3]) != (pos1 > R->m[iSL - 1][3]); // same direction?, note C indices from 0
341 	else { // we do some guess work and warn user
342 		vec3 readV = setVec3(d.orient[1], d.orient[2], d.orient[3]);
343 		vec3 phaseV = setVec3(d.orient[4], d.orient[5], d.orient[6]);
344 		//printMessage("rd %g %g %g\n",readV.v[0],readV.v[1],readV.v[2]);
345 		//printMessage("ph %g %g %g\n",phaseV.v[0],phaseV.v[1],phaseV.v[2]);
346 		vec3 sliceV = crossProduct(readV, phaseV); //order important: this is our hail mary
347 		flip = ((sliceV.v[0] + sliceV.v[1] + sliceV.v[2]) < 0);
348 		//printMessage("verify slice dir %g %g %g\n",sliceV.v[0],sliceV.v[1],sliceV.v[2]);
349 		if (isVerbose) { //1st pass only
350 			if (!d.isDerived) { //do not warn user if image is derived
351 				printWarning("Unable to determine slice direction: please check whether slices are flipped\n");
352 			} else {
353 				printWarning("Unable to determine slice direction: please check whether slices are flipped (derived image)\n");
354 			}
355 		}
356 	}
357 	if (flip) {
358 		for (int i = 0; i < 4; i++)
359 			R->m[i][2] = -R->m[i][2];
360 	}
361 	if (flip)
362 		iSL = -iSL;
363 #ifdef MY_DEBUG
364 	printMessage("verify slice dir %d %d %d\n", h->dim[1], h->dim[2], h->dim[3]);
365 	//reportMat44((char*)"Rout",*R);
366 	printMessage("flip = %d\n", flip);
367 	printMessage("sliceDir = %d\n", iSL);
368 	printMessage(" pos1 = %f\n", pos1);
369 #endif
370 	return iSL;
371 } //verify_slice_dir()
372 
noNaN(mat44 Q44,bool isVerbose,bool * isBogus)373 mat44 noNaN(mat44 Q44, bool isVerbose, bool *isBogus) //simplify any headers that have NaN values
374 {
375 	mat44 ret = Q44;
376 	bool isNaN44 = false;
377 	for (int i = 0; i < 4; i++)
378 		for (int j = 0; j < 4; j++)
379 			if (isnan(ret.m[i][j]))
380 				isNaN44 = true;
381 	if (isNaN44) {
382 		*isBogus = true;
383 		if (isVerbose)
384 			printWarning("Bogus spatial matrix (perhaps non-spatial image): inspect spatial orientation\n");
385 		for (int i = 0; i < 4; i++)
386 			for (int j = 0; j < 4; j++)
387 				if (i == j)
388 					ret.m[i][j] = 1;
389 				else
390 					ret.m[i][j] = 0;
391 		ret.m[1][1] = -1;
392 	} //if isNaN detected
393 	return ret;
394 }
395 
396 #define kSessionOK 0
397 #define kSessionBadMatrix 1
398 
setQSForm(struct nifti_1_header * h,mat44 Q44i,bool isVerbose)399 void setQSForm(struct nifti_1_header *h, mat44 Q44i, bool isVerbose) {
400 	bool isBogus = false;
401 	mat44 Q44 = noNaN(Q44i, isVerbose, &isBogus);
402 	if ((h->session_error == kSessionBadMatrix) || (isBogus)) {
403 		h->session_error = kSessionBadMatrix;
404 		h->sform_code = NIFTI_XFORM_UNKNOWN;
405 	} else
406 		h->sform_code = NIFTI_XFORM_SCANNER_ANAT;
407 	h->srow_x[0] = Q44.m[0][0];
408 	h->srow_x[1] = Q44.m[0][1];
409 	h->srow_x[2] = Q44.m[0][2];
410 	h->srow_x[3] = Q44.m[0][3];
411 	h->srow_y[0] = Q44.m[1][0];
412 	h->srow_y[1] = Q44.m[1][1];
413 	h->srow_y[2] = Q44.m[1][2];
414 	h->srow_y[3] = Q44.m[1][3];
415 	h->srow_z[0] = Q44.m[2][0];
416 	h->srow_z[1] = Q44.m[2][1];
417 	h->srow_z[2] = Q44.m[2][2];
418 	h->srow_z[3] = Q44.m[2][3];
419 	float dumdx, dumdy, dumdz;
420 	nifti_mat44_to_quatern(Q44, &h->quatern_b, &h->quatern_c, &h->quatern_d, &h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz, &h->pixdim[0]);
421 	h->qform_code = h->sform_code;
422 } //setQSForm()
423 
424 #ifdef my_unused
425 
maxCol(mat33 R)426 ivec3 maxCol(mat33 R) {
427 //return index of maximum column in 3x3 matrix, e.g. [1 0 0; 0 1 0; 0 0 1] -> 1,2,3
428 	ivec3 ixyz;
429 	mat33 foo;
430 	for (int i = 0; i < 3; i++)
431 		for (int j = 0; j < 3; j++)
432 			foo.m[i][j] = fabs(R.m[i][j]);
433 	//ixyz.v[0] : row with largest value in column 1
434 	ixyz.v[0] = 1;
435 	if ((foo.m[1][0] > foo.m[0][0]) && (foo.m[1][0] >= foo.m[2][0]))
436 		ixyz.v[0] = 2; //2nd column largest column
437 	else if ((foo.m[2][0] > foo.m[0][0]) && (foo.m[2][0] > foo.m[1][0]))
438 		ixyz.v[0] = 3; //3rd column largest column
439 	//ixyz.v[1] : row with largest value in column 2, but not the same row as ixyz.v[1]
440 	if (ixyz.v[0] == 1) {
441 		ixyz.v[1] = 2;
442 		if (foo.m[2][1] > foo.m[1][1])
443 			ixyz.v[1] = 3;
444 	} else if (ixyz.v[0] == 2) {
445 		ixyz.v[1] = 1;
446 		if (foo.m[2][1] > foo.m[0][1])
447 			ixyz.v[1] = 3;
448 	} else { //ixyz.v[0] == 3
449 		ixyz.v[1] = 1;
450 		if (foo.m[1][1] > foo.m[0][1])
451 			ixyz.v[1] = 2;
452 	}
453 	//ixyz.v[2] : 3rd row, constrained by previous rows
454 	ixyz.v[2] = 6 - ixyz.v[1] - ixyz.v[0]; //sum of 1+2+3
455 	return ixyz;
456 }
457 
sign(float x)458 int sign(float x) {
459 //returns -1,0,1 depending on if X is less than, equal to or greater than zero
460 	if (x < 0)
461 		return -1;
462 	else if (x > 0)
463 		return 1;
464 	return 0;
465 }
466 
467 // Subfunction: get dicom xform matrix and related info
468 // This is a direct port of Xiangrui Li's dicm2nii function
xform_mat(struct TDICOMdata d)469 mat44 xform_mat(struct TDICOMdata d) {
470 	vec3 readV = setVec3(d.orient[1], d.orient[2], d.orient[3]);
471 	vec3 phaseV = setVec3(d.orient[4], d.orient[5], d.orient[6]);
472 	vec3 sliceV = crossProduct(readV, phaseV);
473 	mat33 R;
474 	LOAD_MAT33(R, readV.v[0], readV.v[1], readV.v[2],
475 		phaseV.v[0], phaseV.v[1], phaseV.v[2],
476 		sliceV.v[0], sliceV.v[1], sliceV.v[2]);
477 	R = nifti_mat33_transpose(R);
478 	//reportMat33((char*)"R",R);
479 	ivec3 ixyz = maxCol(R);
480 	//printMessage("%d %d %d\n", ixyz.v[0], ixyz.v[1], ixyz.v[2]);
481 	int iSL = ixyz.v[2]; // 1/2/3 for Sag/Cor/Tra slice
482 	float cosSL = R.m[iSL - 1][2];
483 	//printMessage("cosSL\t%g\n", cosSL);
484 	//vec3 pixdim = setVec3(d.xyzMM[1], d.xyzMM[2], d.xyzMM[3]);
485 	//printMessage("%g %g %g\n", pixdim.v[0], pixdim.v[1], pixdim.v[2]);
486 	mat33 pixdim;
487 	LOAD_MAT33(pixdim, d.xyzMM[1], 0.0, 0.0,
488 		0.0, d.xyzMM[2], 0.0,
489 		0.0, 0.0, d.xyzMM[3]);
490 	R = nifti_mat33_mul(R, pixdim);
491 	//reportMat33((char*)"R",R);
492 	mat44 R44;
493 	LOAD_MAT44(R44, R.m[0][0], R.m[0][1], R.m[0][2], d.patientPosition[1],
494 		R.m[1][0], R.m[1][1], R.m[1][2], d.patientPosition[2],
495 		R.m[2][0], R.m[2][1], R.m[2][2], d.patientPosition[3]);
496 	//reportMat44((char*)"R",R44);
497 	//rest are former: R = verify_slice_dir(R, s, dim, iSL)
498 	if ((d.xyzDim[3] < 2) && (d.CSA.mosaicSlices < 2))
499 		return R44; //don't care direction for single slice
500 	vec3 dim = setVec3(d.xyzDim[1], d.xyzDim[2], d.xyzDim[3]);
501 	if (d.CSA.mosaicSlices > 1) { //Siemens mosaic: use dim(1) since no transpose to img
502 		float nRowCol = ceil(sqrt((double)d.CSA.mosaicSlices));
503 		dim.v[0] = dim.v[0] / nRowCol;
504 		dim.v[1] = dim.v[1] / nRowCol;
505 		dim.v[2] = d.CSA.mosaicSlices;
506 		vec4 dim4 = setVec4((nRowCol - 1) * dim.v[0] / 2.0f, (nRowCol - 1) * dim.v[1] / 2.0f, 0);
507 		vec4 offset = nifti_vect44mat44_mul(dim4, R44);
508 		//printMessage("%g %g %g\n", dim.v[0], dim.v[1], dim.v[2]);
509 		//printMessage("%g %g %g\n", dim4.v[0], dim4.v[1], dim4.v[2]);
510 		//printMessage("%g %g %g %g\n", offset.v[0], offset.v[1], offset.v[2], offset.v[3]);
511 		//printMessage("nRowCol\t%g\n", nRowCol);
512 		R44.m[0][3] = offset.v[0];
513 		R44.m[1][3] = offset.v[1];
514 		R44.m[2][3] = offset.v[2];
515 		//R44.m[3][3] = offset.v[3];
516 		if (sign(d.CSA.sliceNormV[iSL]) != sign(cosSL)) {
517 			R44.m[0][2] = -R44.m[0][2];
518 			R44.m[1][2] = -R44.m[1][2];
519 			R44.m[2][2] = -R44.m[2][2];
520 			R44.m[3][2] = -R44.m[3][2];
521 		}
522 		//reportMat44((char*)"iR44",R44);
523 		return R44;
524 	} else if (true) {
525 		//SliceNormalVector TO DO
526 		printMessage("Not completed");
527 #ifndef USING_R
528 		exit(2);
529 #endif
530 		return R44;
531 	}
532 	printMessage("Unable to determine spatial transform\n");
533 #ifndef USING_R
534 	exit(1);
535 #else
536 	return R44;
537 #endif
538 }
539 
set_nii_header(struct TDICOMdata d)540 mat44 set_nii_header(struct TDICOMdata d) {
541 	mat44 R = xform_mat(d);
542 	//R(1:2,:) = -R(1:2,:); % dicom LPS to nifti RAS, xform matrix before reorient
543 	for (int i = 0; i < 2; i++)
544 		for (int j = 0; j < 4; j++)
545 			R.m[i][j] = -R.m[i][j];
546 #ifdef MY_DEBUG
547 	reportMat44((char *)"R44", R);
548 #endif
549 }
550 #endif
551 
552 // This code predates Xiangrui Li's set_nii_header function
set_nii_header_x(struct TDICOMdata d,struct TDICOMdata d2,struct nifti_1_header * h,int * sliceDir,int isVerbose)553 mat44 set_nii_header_x(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int *sliceDir, int isVerbose) {
554 	*sliceDir = 0;
555 	mat44 Q44 = nifti_dicom2mat(d.orient, d.patientPosition, d.xyzMM);
556 	//Q44 = doQuadruped(Q44);
557 	if (d.isSegamiOasis == true) {
558 		//Segami reconstructions appear to disregard DICOM spatial parameters: assume center of volume is isocenter and no table tilt
559 		// Consider sample image with d.orient (0020,0037) = -1 0 0; 0 1 0: this suggests image RAI (L->R, P->A, S->I) but the vendors viewing software suggests LPS
560 		//Perhaps we should ignore 0020,0037 and 0020,0032 as they are hidden in sequence 0054,0022, but in this case no positioning is provided
561 		// http://www.cs.ucl.ac.uk/fileadmin/cmic/Documents/DavidAtkinson/DICOM.pdf
562 		// https://www.slicer.org/wiki/Coordinate_systems
563 		LOAD_MAT44(Q44, -h->pixdim[1], 0, 0, 0, 0, -h->pixdim[2], 0, 0, 0, 0, h->pixdim[3], 0); //X and Y dimensions flipped in NIfTI (RAS) vs DICOM (LPS)
564 		vec4 originVx = setVec4((h->dim[1] + 1.0f) / 2.0f, (h->dim[2] + 1.0f) / 2.0f, (h->dim[3] + 1.0f) / 2.0f);
565 		vec4 originMm = nifti_vect44mat44_mul(originVx, Q44);
566 		for (int i = 0; i < 3; i++)
567 			Q44.m[i][3] = -originMm.v[i]; //set origin to center voxel
568 		if (isVerbose) {
569 			//printMessage("origin (vx) %g %g %g\n",originVx.v[0],originVx.v[1],originVx.v[2]);
570 			//printMessage("origin (mm) %g %g %g\n",originMm.v[0],originMm.v[1],originMm.v[2]);
571 			printWarning("Segami coordinates defy DICOM convention, please check orientation\n");
572 		}
573 		return Q44;
574 	}
575 	//next line only for Siemens mosaic: ignore for UIH grid
576 	//	https://github.com/xiangruili/dicm2nii/commit/47ad9e6d9bc8a999344cbd487d602d420fb1509f
577 	if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (d.CSA.mosaicSlices > 1)) {
578 		double nRowCol = ceil(sqrt((double)d.CSA.mosaicSlices));
579 		double lFactorX = (d.xyzDim[1] - (d.xyzDim[1] / nRowCol)) / 2.0;
580 		double lFactorY = (d.xyzDim[2] - (d.xyzDim[2] / nRowCol)) / 2.0;
581 		Q44.m[0][3] = (float)((Q44.m[0][0] * lFactorX) + (Q44.m[0][1] * lFactorY) + Q44.m[0][3]);
582 		Q44.m[1][3] = (float)((Q44.m[1][0] * lFactorX) + (Q44.m[1][1] * lFactorY) + Q44.m[1][3]);
583 		Q44.m[2][3] = (float)((Q44.m[2][0] * lFactorX) + (Q44.m[2][1] * lFactorY) + Q44.m[2][3]);
584 		for (int c = 0; c < 2; c++)
585 			for (int r = 0; r < 4; r++)
586 				Q44.m[c][r] = -Q44.m[c][r];
587 		mat33 Q;
588 		LOAD_MAT33(Q, d.orient[1], d.orient[4], d.CSA.sliceNormV[1],
589 			d.orient[2], d.orient[5], d.CSA.sliceNormV[2],
590 			d.orient[3], d.orient[6], d.CSA.sliceNormV[3]);
591 		if (nifti_mat33_determ(Q) < 0) { //Siemens sagittal are R>>L, whereas NIfTI is L>>R, we retain Siemens order on disk so ascending is still ascending, but we need to have the spatial transform reflect this.
592 			mat44 det;
593 			*sliceDir = kSliceOrientMosaicNegativeDeterminant; //we need to handle DTI vectors accordingly
594 			LOAD_MAT44(det, 1.0l, 0.0l, 0.0l, 0.0l, 0.0l, 1.0l, 0.0l, 0.0l, 0.0l, 0.0l, -1.0l, 0.0l);
595 			//patient_to_tal.m[2][3] = 1-d.CSA.MosaicSlices;
596 			Q44 = nifti_mat44_mul(Q44, det);
597 		}
598 	} else { //not a mosaic
599 		*sliceDir = verify_slice_dir(d, d2, h, &Q44, isVerbose);
600 		for (int c = 0; c < 4; c++) // LPS to nifti RAS, xform matrix before reorient
601 			for (int r = 0; r < 2; r++) //swap rows 1 & 2
602 				Q44.m[r][c] = -Q44.m[r][c];
603 	}
604 #ifdef MY_DEBUG
605 	reportMat44((char *)"Q44", Q44);
606 #endif
607 	return Q44;
608 }
609 
headerDcm2NiiSForm(struct TDICOMdata d,struct TDICOMdata d2,struct nifti_1_header * h,int isVerbose)610 int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int isVerbose) { //fill header s and q form
611 	//see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c
612 	//returns sliceDir: 0=unknown,1=sag,2=coro,3=axial,-=reversed slices
613 	int sliceDir = 0;
614 	if (h->dim[3] < 2) {
615 		mat44 Q44 = set_nii_header_x(d, d2, h, &sliceDir, isVerbose);
616 		setQSForm(h, Q44, isVerbose);
617 		return sliceDir; //don't care direction for single slice
618 	}
619 	h->sform_code = NIFTI_XFORM_UNKNOWN;
620 	h->qform_code = NIFTI_XFORM_UNKNOWN;
621 	bool isOK = false;
622 	for (int i = 1; i <= 6; i++)
623 		if (d.orient[i] != 0.0)
624 			isOK = true;
625 	if (!isOK) {
626 		//we will have to guess, assume axial acquisition saved in standard Siemens style?
627 		d.orient[1] = 1.0f;
628 		d.orient[2] = 0.0f;
629 		d.orient[3] = 0.0f;
630 		d.orient[1] = 0.0f;
631 		d.orient[2] = 1.0f;
632 		d.orient[3] = 0.0f;
633 		if ((d.isDerived) || ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3) && (d.manufacturer == kMANUFACTURER_SIEMENS))) {
634 			printMessage("Unable to determine spatial orientation: 0020,0037 missing (probably not a problem: derived image)\n");
635 		} else {
636 			printMessage("Unable to determine spatial orientation: 0020,0037 missing (Type 1 attribute: not a valid DICOM) Series %ld\n", d.seriesNum);
637 		}
638 	}
639 	mat44 Q44 = set_nii_header_x(d, d2, h, &sliceDir, isVerbose);
640 	setQSForm(h, Q44, isVerbose);
641 	return sliceDir;
642 } //headerDcm2NiiSForm()
643 
headerDcm2Nii2(struct TDICOMdata d,struct TDICOMdata d2,struct nifti_1_header * h,int isVerbose)644 int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int isVerbose) { //final pass after de-mosaic
645 	char txt[1024] = {""};
646 	if (h->slice_code == NIFTI_SLICE_UNKNOWN)
647 		h->slice_code = d.CSA.sliceOrder;
648 	if (h->slice_code == NIFTI_SLICE_UNKNOWN)
649 		h->slice_code = d2.CSA.sliceOrder; //sometimes the first slice order is screwed up https://github.com/eauerbach/CMRR-MB/issues/29
650 	if (d.modality == kMODALITY_MR)
651 		sprintf(txt, "TE=%.2g;Time=%.3f", d.TE, d.acquisitionTime);
652 	else
653 		sprintf(txt, "Time=%.3f", d.acquisitionTime);
654 	if (d.CSA.phaseEncodingDirectionPositive >= 0) {
655 		char dtxt[1024] = {""};
656 		sprintf(dtxt, ";phase=%d", d.CSA.phaseEncodingDirectionPositive);
657 		strcat(txt, dtxt);
658 	}
659 	//from dicm2nii 20151117 InPlanePhaseEncodingDirection
660 	if (d.phaseEncodingRC == 'R')
661 		h->dim_info = (3 << 4) + (1 << 2) + 2;
662 	if (d.phaseEncodingRC == 'C')
663 		h->dim_info = (3 << 4) + (2 << 2) + 1;
664 	if (d.CSA.multiBandFactor > 1) {
665 		char dtxt[1024] = {""};
666 		sprintf(dtxt, ";mb=%d", d.CSA.multiBandFactor);
667 		strcat(txt, dtxt);
668 	}
669 	// GCC 8 warns about truncation using snprintf
670 	// snprintf(h->descrip,80, "%s",txt);
671 	memcpy(h->descrip, txt, 79);
672 	h->descrip[79] = '\0';
673 	if (strlen(d.imageComments) > 0)
674 		snprintf(h->aux_file, 24, "%.23s", d.imageComments);
675 	return headerDcm2NiiSForm(d, d2, h, isVerbose);
676 } //headerDcm2Nii2()
677 
dcmStrLen(int len,int kMaxLen)678 int dcmStrLen(int len, int kMaxLen) {
679 	if (len < kMaxLen)
680 		return len + 1;
681 	else
682 		return kMaxLen;
683 } //dcmStrLen()
684 
clear_dicom_data()685 struct TDICOMdata clear_dicom_data() {
686 	struct TDICOMdata d;
687 	//d.dti4D = NULL;
688 	d.locationsInAcquisition = 0;
689 	d.locationsInAcquisitionConflict = 0; //for GE discrepancy between tags 0020,1002; 0021,104F; 0054,0081
690 	d.modality = kMODALITY_UNKNOWN;
691 	d.effectiveEchoSpacingGE = 0;
692 	for (int i = 0; i < 4; i++) {
693 		d.CSA.dtiV[i] = 0;
694 		d.patientPosition[i] = NAN;
695 		//d.patientPosition2nd[i] = NAN; //used to distinguish XYZT vs XYTZ for Philips 4D
696 		d.patientPositionLast[i] = NAN; //used to compute slice direction for Philips 4D
697 		d.stackOffcentre[i] = NAN;
698 		d.angulation[i] = 0.0f;
699 		d.xyzMM[i] = 1;
700 	}
701 	for (int i = 0; i < MAX_NUMBER_OF_DIMENSIONS; ++i)
702 		d.dimensionIndexValues[i] = 0;
703 	//d.CSA.sliceTiming[0] = -1.0f; //impossible value denotes not known
704 	for (int z = 0; z < kMaxEPI3D; z++)
705 		d.CSA.sliceTiming[z] = -1.0;
706 	d.CSA.numDti = 0;
707 	for (int i = 0; i < 5; i++)
708 		d.xyzDim[i] = 1;
709 	for (int i = 0; i < 7; i++)
710 		d.orient[i] = 0.0f;
711 	strcpy(d.patientName, "");
712 	strcpy(d.patientID, "");
713 	strcpy(d.accessionNumber, "");
714 	strcpy(d.imageType, "");
715 	strcpy(d.imageComments, "");
716 	strcpy(d.imageBaseName, "");
717 	strcpy(d.phaseEncodingDirectionDisplayedUIH, "");
718 	strcpy(d.studyDate, "");
719 	strcpy(d.studyTime, "");
720 	strcpy(d.protocolName, "");
721 	strcpy(d.seriesDescription, "");
722 	strcpy(d.sequenceName, "");
723 	strcpy(d.scanningSequence, "");
724 	strcpy(d.sequenceVariant, "");
725 	strcpy(d.manufacturersModelName, "");
726 	strcpy(d.institutionalDepartmentName, "");
727 	strcpy(d.procedureStepDescription, "");
728 	strcpy(d.institutionName, "");
729 	strcpy(d.referringPhysicianName, "");
730 	strcpy(d.institutionAddress, "");
731 	strcpy(d.deviceSerialNumber, "");
732 	strcpy(d.softwareVersions, "");
733 	strcpy(d.stationName, "");
734 	strcpy(d.scanOptions, "");
735 	//strcpy(d.mrAcquisitionType, "");
736 	strcpy(d.seriesInstanceUID, "");
737 	strcpy(d.instanceUID, "");
738 	strcpy(d.studyID, "");
739 	strcpy(d.studyInstanceUID, "");
740 	strcpy(d.bodyPartExamined, "");
741 	strcpy(d.coilName, "");
742 	strcpy(d.coilElements, "");
743 	strcpy(d.radiopharmaceutical, "");
744 	strcpy(d.convolutionKernel, "");
745 	strcpy(d.parallelAcquisitionTechnique, "");
746 	strcpy(d.imageOrientationText, "");
747 	strcpy(d.unitsPT, "");
748 	strcpy(d.decayCorrection, "");
749 	strcpy(d.attenuationCorrectionMethod, "");
750 	strcpy(d.reconstructionMethod, "");
751 	d.phaseEncodingLines = 0;
752 	//~ d.patientPositionSequentialRepeats = 0;
753 	//~ d.patientPositionRepeats = 0;
754 	d.isHasPhase = false;
755 	d.isHasReal = false;
756 	d.isHasImaginary = false;
757 	d.isHasMagnitude = false;
758 	//d.maxGradDynVol = -1; //PAR/REC only
759 	d.sliceOrient = kSliceOrientUnknown;
760 	d.dateTime = (double)19770703150928.0;
761 	d.acquisitionTime = 0.0f;
762 	d.acquisitionDate = 0.0f;
763 	d.manufacturer = kMANUFACTURER_UNKNOWN;
764 	d.isPlanarRGB = false;
765 	d.lastScanLoc = NAN;
766 	d.TR = 0.0;
767 	d.TE = 0.0;
768 	d.TI = 0.0;
769 	d.flipAngle = 0.0;
770 	d.bandwidthPerPixelPhaseEncode = 0.0;
771 	d.acquisitionDuration = 0.0;
772 	d.imagingFrequency = 0.0;
773 	d.numberOfAverages = 0.0;
774 	d.fieldStrength = 0.0;
775 	d.SAR = 0.0;
776 	d.pixelBandwidth = 0.0;
777 	d.zSpacing = 0.0;
778 	d.zThick = 0.0;
779 	//~ d.numberOfDynamicScans = 0;
780 	d.echoNum = 1;
781 	d.echoTrainLength = 0;
782 	d.waterFatShift = 0.0;
783 	d.groupDelay = 0.0;
784 	d.decayFactor = 0.0;
785 	d.percentSampling = 0.0;
786 	d.phaseFieldofView = 0.0;
787 	d.dwellTime = 0;
788 	d.protocolBlockStartGE = 0;
789 	d.protocolBlockLengthGE = 0;
790 	d.phaseEncodingSteps = 0;
791 	d.coilCrc = 0;
792 	d.seriesUidCrc = 0;
793 	d.instanceUidCrc = 0;
794 	d.accelFactPE = 0.0;
795 	d.accelFactOOP = 0.0;
796 	//d.patientPositionNumPhilips = 0;
797 	d.imageBytes = 0;
798 	d.intenScale = 1;
799 	d.intenScalePhilips = 0;
800 	d.intenIntercept = 0;
801 	d.gantryTilt = 0.0;
802 	d.exposureTimeMs = 0.0;
803 	d.xRayTubeCurrent = 0.0;
804 	d.radionuclidePositronFraction = 0.0;
805 	d.radionuclideHalfLife = 0.0;
806 	d.doseCalibrationFactor = 0.0;
807 	d.ecat_isotope_halflife = 0.0;
808 	d.frameDuration = -1.0;
809 	d.ecat_dosage = 0.0;
810 	d.radionuclideTotalDose = 0.0;
811 	d.seriesNum = 1;
812 	d.acquNum = 0;
813 	d.imageNum = 1;
814 	d.imageStart = 0;
815 	d.is3DAcq = false; //e.g. MP-RAGE, SPACE, TFE
816 	d.is2DAcq = false; //
817 	d.isDerived = false; //0008,0008 = DERIVED,CSAPARALLEL,POSDISP
818 	d.isSegamiOasis = false; //these images do not store spatial coordinates
819 	d.isBVecWorldCoordinates = false; //bvecs can be in image space (GE) or world coordinates (Siemens)
820 	d.isGrayscaleSoftcopyPresentationState = false;
821 	d.isRawDataStorage = false;
822 	d.isPartialFourier = false;
823 	d.isIR = false;
824 	d.isEPI = false;
825 	d.isDiffusion = false;
826 	d.isVectorFromBMatrix = false;
827 	d.isStackableSeries = false; //combine DCE series https://github.com/rordenlab/dcm2niix/issues/252
828 	d.isXA10A = false; //https://github.com/rordenlab/dcm2niix/issues/236
829 	d.triggerDelayTime = 0.0;
830 	d.RWVScale = 0.0;
831 	d.RWVIntercept = 0.0;
832 	d.isScaleOrTEVaries = false;
833 	d.isScaleVariesEnh = false; //issue363
834 	d.bitsAllocated = 16; //bits
835 	d.bitsStored = 0;
836 	d.samplesPerPixel = 1;
837 	d.pixelPaddingValue = NAN;
838 	d.isValid = false;
839 	d.isXRay = false;
840 	d.isMultiEcho = false;
841 	d.isSigned = false; //default is unsigned!
842 	d.isFloat = false; //default is for integers, not single or double precision
843 	d.isResampled = false; //assume data not resliced to remove gantry tilt problems
844 	d.isLocalizer = false;
845 	d.isNonParallelSlices = false;
846 	d.isCoilVaries = false;
847 	d.compressionScheme = 0; //none
848 	d.isExplicitVR = true;
849 	d.isLittleEndian = true; //DICOM initially always little endian
850 	d.converted2NII = 0;
851 	d.numberOfDiffusionDirectionGE = -1;
852 	d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNKNOWN;
853 	d.rtia_timerGE = -1.0;
854 	d.rawDataRunNumber = -1;
855 	d.maxEchoNumGE = -1;
856 	d.epiVersionGE = -1;
857 	d.internalepiVersionGE = -1;
858 	d.durationLabelPulseGE = -1;
859 	d.aslFlags = kASL_FLAG_NONE;
860 	d.partialFourierDirection = kPARTIAL_FOURIER_DIRECTION_UNKNOWN;
861 	d.mtState = -1;
862 	d.numberOfExcitations = -1;
863 	d.numberOfArms = -1;
864 	d.numberOfPointsPerArm = -1;
865 	d.phaseNumber = - 1; //Philips Multi-Phase ASL
866 	d.spoiling = kSPOILING_UNKOWN;
867 	d.interp3D = -1;
868 	for (int i = 0; i < kMaxOverlay; i++)
869 		d.overlayStart[i] = 0;
870 	d.isHasOverlay = false;
871 	d.isPrivateCreatorRemap = false;
872 	d.isRealIsPhaseMapHz = false;
873 	d.numberOfImagesInGridUIH = 0;
874 	d.phaseEncodingRC = '?';
875 	d.patientSex = '?';
876 	d.patientWeight = 0.0;
877 	strcpy(d.patientBirthDate, "");
878 	strcpy(d.patientAge, "");
879 	d.CSA.bandwidthPerPixelPhaseEncode = 0.0;
880 	d.CSA.mosaicSlices = 0;
881 	d.CSA.sliceNormV[1] = 0.0;
882 	d.CSA.sliceNormV[2] = 0.0;
883 	d.CSA.sliceNormV[3] = 1.0; //default Siemens Image Numbering is F>>H https://www.mccauslandcenter.sc.edu/crnl/tools/stc
884 	d.CSA.sliceOrder = NIFTI_SLICE_UNKNOWN;
885 	d.CSA.slice_start = 0;
886 	d.CSA.slice_end = 0;
887 	d.CSA.protocolSliceNumber1 = 0;
888 	d.CSA.phaseEncodingDirectionPositive = -1; //unknown
889 	d.CSA.isPhaseMap = false;
890 	d.CSA.multiBandFactor = 1;
891 	d.CSA.SeriesHeader_offset = 0;
892 	d.CSA.SeriesHeader_length = 0;
893 	return d;
894 } //clear_dicom_data()
895 
isdigitdot(int c)896 int isdigitdot(int c) { //returns true if digit or '.'
897 	if (c == '.')
898 		return 1;
899 	return isdigit(c);
900 }
901 
dcmStrDigitsDotOnlyKey(char key,char * lStr)902 void dcmStrDigitsDotOnlyKey(char key, char *lStr) {
903 //e.g. string "F:2.50" returns 2.50 if key==":"
904 	size_t len = strlen(lStr);
905 	if (len < 1)
906 		return;
907 	bool isKey = false;
908 	for (int i = 0; i < (int)len; i++) {
909 		if (!isdigitdot(lStr[i])) {
910 			isKey = (lStr[i] == key);
911 			lStr[i] = ' ';
912 		} else if (!isKey)
913 			lStr[i] = ' ';
914 	}
915 } //dcmStrDigitsOnlyKey()
916 
dcmStrDigitsOnlyKey(char key,char * lStr)917 void dcmStrDigitsOnlyKey(char key, char *lStr) {
918 //e.g. string "p2s3" returns 2 if key=="p" and 3 if key=="s"
919 	size_t len = strlen(lStr);
920 	if (len < 1)
921 		return;
922 	bool isKey = false;
923 	for (int i = 0; i < (int)len; i++) {
924 		if (!isdigit(lStr[i])) {
925 			isKey = (lStr[i] == key);
926 			lStr[i] = ' ';
927 		} else if (!isKey)
928 			lStr[i] = ' ';
929 	}
930 } //dcmStrDigitsOnlyKey()
931 
dcmStrDigitsOnly(char * lStr)932 void dcmStrDigitsOnly(char *lStr) {
933 //e.g. change "H11" to " 11"
934 	size_t len = strlen(lStr);
935 	if (len < 1)
936 		return;
937 	for (int i = 0; i < (int)len; i++)
938 		if (!isdigit(lStr[i]))
939 			lStr[i] = ' ';
940 }
941 
942 // Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/
mz_crc32X(unsigned char * ptr,size_t buf_len)943 uint32_t mz_crc32X(unsigned char *ptr, size_t buf_len) {
944 	static const uint32_t s_crc32[16] = {0, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
945 		0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,0xedb88320, 0xf00f9344,
946 		0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c};
947 	uint32_t crcu32 = 0;
948 	if (!ptr)
949 		return crcu32;
950 	crcu32 = ~crcu32;
951 	while (buf_len--) {
952 		uint8_t b = *ptr++;
953 		crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)];
954 		crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)];
955 	}
956 	return ~crcu32;
957 }
958 
dcmStr(int lLength,unsigned char lBuffer[],char * lOut,bool isStrLarge=false)959 void dcmStr(int lLength, unsigned char lBuffer[], char *lOut, bool isStrLarge = false) {
960 	if (lLength < 1)
961 		return;
962 	char *cString = (char *)malloc(sizeof(char) * (lLength + 1));
963 	cString[lLength] = 0;
964 	memcpy(cString, (char *)&lBuffer[0], lLength);
965 //memcpy(cString, test, lLength);
966 //printMessage("X%dX\n", (unsigned char)d.patientName[1]);
967 #ifdef ISO8859
968 	for (int i = 0; i < lLength; i++)
969 		//assume specificCharacterSet (0008,0005) is ISO_IR 100 http://en.wikipedia.org/wiki/ISO/IEC_8859-1
970 		if (cString[i] < 1) {
971 			unsigned char c = (unsigned char)cString[i];
972 			if ((c >= 192) && (c <= 198))
973 				cString[i] = 'A';
974 			if (c == 199)
975 				cString[i] = 'C';
976 			if ((c >= 200) && (c <= 203))
977 				cString[i] = 'E';
978 			if ((c >= 204) && (c <= 207))
979 				cString[i] = 'I';
980 			if (c == 208)
981 				cString[i] = 'D';
982 			if (c == 209)
983 				cString[i] = 'N';
984 			if ((c >= 210) && (c <= 214))
985 				cString[i] = 'O';
986 			if (c == 215)
987 				cString[i] = 'x';
988 			if (c == 216)
989 				cString[i] = 'O';
990 			if ((c >= 217) && (c <= 220))
991 				cString[i] = 'O';
992 			if (c == 221)
993 				cString[i] = 'Y';
994 			if ((c >= 224) && (c <= 230))
995 				cString[i] = 'a';
996 			if (c == 231)
997 				cString[i] = 'c';
998 			if ((c >= 232) && (c <= 235))
999 				cString[i] = 'e';
1000 			if ((c >= 236) && (c <= 239))
1001 				cString[i] = 'i';
1002 			if (c == 240)
1003 				cString[i] = 'o';
1004 			if (c == 241)
1005 				cString[i] = 'n';
1006 			if ((c >= 242) && (c <= 246))
1007 				cString[i] = 'o';
1008 			if (c == 248)
1009 				cString[i] = 'o';
1010 			if ((c >= 249) && (c <= 252))
1011 				cString[i] = 'u';
1012 			if (c == 253)
1013 				cString[i] = 'y';
1014 			if (c == 255)
1015 				cString[i] = 'y';
1016 		}
1017 #endif
1018 	//we no longer sanitize strings, see issue 425
1019 	int len = lLength;
1020 	if (cString[len - 1] == ' ')
1021 		len--;
1022 	//while ((len > 0) && (cString[len]=='_')) len--; //remove trailing '_'
1023 	cString[len] = 0; //null-terminate, strlcpy does this anyway
1024 	int maxLen = kDICOMStr;
1025 	if (isStrLarge)
1026 		maxLen = kDICOMStrLarge;
1027 	len = dcmStrLen(len, maxLen);
1028 	if (len == maxLen) { //we need space for null-termination
1029 		if (cString[len - 2] == '_')
1030 			len = len - 2;
1031 	}
1032 	memcpy(lOut, cString, len - 1);
1033 	lOut[len - 1] = 0;
1034 	free(cString);
1035 } //dcmStr()
1036 
1037 #ifdef MY_OLD
1038 //this code works on Intel but not some older systems https://github.com/rordenlab/dcm2niix/issues/327
dcmFloat(int lByteLength,unsigned char lBuffer[],bool littleEndian)1039 float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 32-bit float
1040 //http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian
1041 	bool swap = (littleEndian != littleEndianPlatform());
1042 	float retVal = 0;
1043 	if (lByteLength < 4)
1044 		return retVal;
1045 	memcpy(&retVal, (char *)&lBuffer[0], 4);
1046 	if (!swap)
1047 		return retVal;
1048 	float swapVal;
1049 	char *inFloat = (char *)&retVal;
1050 	char *outFloat = (char *)&swapVal;
1051 	outFloat[0] = inFloat[3];
1052 	outFloat[1] = inFloat[2];
1053 	outFloat[2] = inFloat[1];
1054 	outFloat[3] = inFloat[0];
1055 	//printMessage("swapped val = %f\n",swapVal);
1056 	return swapVal;
1057 } //dcmFloat()
1058 
dcmFloatDouble(const size_t lByteLength,const unsigned char lBuffer[],const bool littleEndian)1059 double dcmFloatDouble(const size_t lByteLength, const unsigned char lBuffer[], const bool littleEndian) { //read binary 64-bit float
1060 //http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian
1061 	bool swap = (littleEndian != littleEndianPlatform());
1062 	double retVal = 0.0f;
1063 	if (lByteLength < 8)
1064 		return retVal;
1065 	memcpy(&retVal, (char *)&lBuffer[0], 8);
1066 	if (!swap)
1067 		return retVal;
1068 	char *floatToConvert = (char *)&lBuffer;
1069 	char *returnFloat = (char *)&retVal;
1070 	//swap the bytes into a temporary buffer
1071 	returnFloat[0] = floatToConvert[7];
1072 	returnFloat[1] = floatToConvert[6];
1073 	returnFloat[2] = floatToConvert[5];
1074 	returnFloat[3] = floatToConvert[4];
1075 	returnFloat[4] = floatToConvert[3];
1076 	returnFloat[5] = floatToConvert[2];
1077 	returnFloat[6] = floatToConvert[1];
1078 	returnFloat[7] = floatToConvert[0];
1079 	//printMessage("swapped val = %f\n",retVal);
1080 	return retVal;
1081 } //dcmFloatDouble()
1082 #else
1083 
dcmFloat(int lByteLength,unsigned char lBuffer[],bool littleEndian)1084 float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 32-bit float
1085 	//http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian
1086 	if (lByteLength < 4)
1087 		return 0.0;
1088 	bool swap = (littleEndian != littleEndianPlatform());
1089 	union {
1090 		uint32_t i;
1091 		float f;
1092 		uint8_t c[4];
1093 	} i, o;
1094 	memcpy(&i.i, (char *)&lBuffer[0], 4);
1095 	//printf("%02x%02x%02x%02x\n",i.c[0], i.c[1], i.c[2], i.c[3]);
1096 	if (!swap)
1097 		return i.f;
1098 	o.c[0] = i.c[3];
1099 	o.c[1] = i.c[2];
1100 	o.c[2] = i.c[1];
1101 	o.c[3] = i.c[0];
1102 	//printf("swp %02x%02x%02x%02x\n",o.c[0], o.c[1], o.c[2], o.c[3]);
1103 	return o.f;
1104 } //dcmFloat()
1105 
dcmFloatDouble(const size_t lByteLength,const unsigned char lBuffer[],const bool littleEndian)1106 double dcmFloatDouble(const size_t lByteLength, const unsigned char lBuffer[], const bool littleEndian) { //read binary 64-bit float
1107 	//http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian
1108 	if (lByteLength < 8)
1109 		return 0.0;
1110 	bool swap = (littleEndian != littleEndianPlatform());
1111 	union {
1112 		uint32_t i;
1113 		double d;
1114 		uint8_t c[8];
1115 	} i, o;
1116 	memcpy(&i.i, (char *)&lBuffer[0], 8);
1117 	if (!swap)
1118 		return i.d;
1119 	o.c[0] = i.c[7];
1120 	o.c[1] = i.c[6];
1121 	o.c[2] = i.c[5];
1122 	o.c[3] = i.c[4];
1123 	o.c[4] = i.c[3];
1124 	o.c[5] = i.c[2];
1125 	o.c[6] = i.c[1];
1126 	o.c[7] = i.c[0];
1127 	return o.d;
1128 } //dcmFloatDouble()
1129 #endif
1130 
dcmInt(int lByteLength,unsigned char lBuffer[],bool littleEndian)1131 int dcmInt(int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 16 or 32 bit integer
1132 	if (littleEndian) {
1133 		if (lByteLength <= 3)
1134 			return lBuffer[0] | (lBuffer[1] << 8); //shortint vs word?
1135 		return lBuffer[0] + (lBuffer[1] << 8) + (lBuffer[2] << 16) + (lBuffer[3] << 24); //shortint vs word?
1136 	}
1137 	if (lByteLength <= 3)
1138 		return lBuffer[1] | (lBuffer[0] << 8); //shortint vs word?
1139 	return lBuffer[3] + (lBuffer[2] << 8) + (lBuffer[1] << 16) + (lBuffer[0] << 24); //shortint vs word?
1140 } //dcmInt()
1141 
dcmAttributeTag(unsigned char lBuffer[],bool littleEndian)1142 uint32_t dcmAttributeTag(unsigned char lBuffer[], bool littleEndian) {
1143 	// read Attribute Tag (AT) value
1144 	// return in Group + (Element << 16) format
1145 	if (littleEndian)
1146 		return lBuffer[0] + (lBuffer[1] << 8) + (lBuffer[2] << 16) + (lBuffer[3] << 24);
1147 	return lBuffer[1] + (lBuffer[0] << 8) + (lBuffer[3] << 16) + (lBuffer[2] << 24);
1148 } //dcmInt()
1149 
dcmStrInt(const int lByteLength,const unsigned char lBuffer[])1150 int dcmStrInt(const int lByteLength, const unsigned char lBuffer[]) { //read int stored as a string
1151 	char *cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
1152 	cString[lByteLength] = 0;
1153 	memcpy(cString, (const unsigned char *)(&lBuffer[0]), lByteLength);
1154 	int ret = atoi(cString);
1155 	free(cString);
1156 	return ret;
1157 } //dcmStrInt()
1158 
dcmStrManufacturer(const int lByteLength,unsigned char lBuffer[])1159 int dcmStrManufacturer(const int lByteLength, unsigned char lBuffer[]) { //read float stored as a string
1160 	if (lByteLength < 2)
1161 		return kMANUFACTURER_UNKNOWN;
1162 	//#ifdef _MSC_VER
1163 	char *cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
1164 	//#else
1165 	//	char cString[lByteLength + 1];
1166 	//#endif
1167 	int ret = kMANUFACTURER_UNKNOWN;
1168 	cString[lByteLength] = 0;
1169 	memcpy(cString, (char *)&lBuffer[0], lByteLength);
1170 	if ((toupper(cString[0]) == 'S') && (toupper(cString[1]) == 'I'))
1171 		ret = kMANUFACTURER_SIEMENS;
1172 	if ((toupper(cString[0]) == 'G') && (toupper(cString[1]) == 'E'))
1173 		ret = kMANUFACTURER_GE;
1174 	if ((toupper(cString[0]) == 'H') && (toupper(cString[1]) == 'I'))
1175 		ret = kMANUFACTURER_HITACHI;
1176 	if ((toupper(cString[0]) == 'M') && (toupper(cString[1]) == 'E'))
1177 		ret = kMANUFACTURER_MEDISO;
1178 	if ((toupper(cString[0]) == 'P') && (toupper(cString[1]) == 'H'))
1179 		ret = kMANUFACTURER_PHILIPS;
1180 	if ((toupper(cString[0]) == 'T') && (toupper(cString[1]) == 'O'))
1181 		ret = kMANUFACTURER_TOSHIBA;
1182 	//CANON_MEC
1183 	if ((toupper(cString[0]) == 'C') && (toupper(cString[1]) == 'A'))
1184 		ret = kMANUFACTURER_CANON;
1185 	if ((toupper(cString[0]) == 'U') && (toupper(cString[1]) == 'I'))
1186 		ret = kMANUFACTURER_UIH;
1187 	if ((toupper(cString[0]) == 'B') && (toupper(cString[1]) == 'R'))
1188 		ret = kMANUFACTURER_BRUKER;
1189 	if (ret == kMANUFACTURER_UNKNOWN)
1190 		printWarning("Unknown manufacturer %s\n", cString);
1191 	//#ifdef _MSC_VER
1192 	free(cString);
1193 	//#endif
1194 	return ret;
1195 } //dcmStrManufacturer
1196 
csaMultiFloat(unsigned char buff[],int nItems,float Floats[],int * ItemsOK)1197 float csaMultiFloat(unsigned char buff[], int nItems, float Floats[], int *ItemsOK) {
1198 	//warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats]
1199 	//if lnItems == 1, returns first item, if lnItems > 1 returns index of final successful conversion
1200 	TCSAitem itemCSA;
1201 	*ItemsOK = 0;
1202 	if (nItems < 1)
1203 		return 0.0f;
1204 	Floats[1] = 0;
1205 	int lPos = 0;
1206 	for (int lI = 1; lI <= nItems; lI++) {
1207 		memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
1208 		lPos += sizeof(itemCSA);
1209 		// Storage order is always little-endian, so byte-swap required values if necessary
1210 		if (!littleEndianPlatform())
1211 			nifti_swap_4bytes(1, &itemCSA.xx2_Len);
1212 		if (itemCSA.xx2_Len > 0) {
1213 			char *cString = (char *)malloc(sizeof(char) * (itemCSA.xx2_Len));
1214 			memcpy(cString, &buff[lPos], itemCSA.xx2_Len); //TPX memcpy(&cString, &buff[lPos], sizeof(cString));
1215 			lPos += ((itemCSA.xx2_Len + 3) / 4) * 4;
1216 			//printMessage(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString);
1217 			Floats[lI] = (float)atof(cString);
1218 			*ItemsOK = lI; //some sequences have store empty items
1219 			free(cString);
1220 		}
1221 	} //for each item
1222 	return Floats[1];
1223 } //csaMultiFloat()
1224 
csaIsPhaseMap(unsigned char buff[],int nItems)1225 bool csaIsPhaseMap(unsigned char buff[], int nItems) {
1226 	//returns true if the tag "ImageHistory" has an item named "CC:ComplexAdd"
1227 	TCSAitem itemCSA;
1228 	if (nItems < 1)
1229 		return false;
1230 	int lPos = 0;
1231 	for (int lI = 1; lI <= nItems; lI++) {
1232 		memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
1233 		lPos += sizeof(itemCSA);
1234 		// Storage order is always little-endian, so byte-swap required values if necessary
1235 		if (!littleEndianPlatform())
1236 			nifti_swap_4bytes(1, &itemCSA.xx2_Len);
1237 		if (itemCSA.xx2_Len > 0) {
1238 			char *cString = (char *)malloc(sizeof(char) * (itemCSA.xx2_Len + 1));
1239 			memcpy(cString, &buff[lPos], sizeof(itemCSA.xx2_Len)); //TPX memcpy(&cString, &buff[lPos], sizeof(cString));
1240 			lPos += ((itemCSA.xx2_Len + 3) / 4) * 4;
1241 			//printMessage(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString);
1242 			if (strcmp(cString, "CC:ComplexAdd") == 0)
1243 				return true;
1244 			free(cString);
1245 		}
1246 	} //for each item
1247 	return false;
1248 } //csaIsPhaseMap()
1249 
checkSliceTimes(struct TCSAdata * CSA,int itemsOK,int isVerbose,bool is3DAcq)1250 void checkSliceTimes(struct TCSAdata *CSA, int itemsOK, int isVerbose, bool is3DAcq) {
1251 	if ((is3DAcq) || (itemsOK < 1)) //we expect 3D sequences to be simultaneous
1252 		return;
1253 	if (itemsOK > kMaxEPI3D) {
1254 		printError("Please increase kMaxEPI3D and recompile\n");
1255 		return;
1256 	}
1257 	float maxTimeValue, minTimeValue, timeValue1;
1258 	minTimeValue = CSA->sliceTiming[0];
1259 	for (int z = 0; z < itemsOK; z++)
1260 		if (CSA->sliceTiming[z] < minTimeValue)
1261 			minTimeValue = CSA->sliceTiming[z];
1262 	//CSA can report negative slice times
1263 	// https://neurostars.org/t/slice-timing-illegal-values-in-fmriprep/1516/8
1264 	// Nov 1, 2018 <siemens-healthineers.com> wrote:
1265 	// If you have an interleaved dataset we can more definitively validate this formula (aka sliceTime(i) - min(sliceTimes())).
1266 	if (minTimeValue < 0) {
1267 		//printWarning("Adjusting for negative MosaicRefAcqTimes (issue 271).\n"); //if uncommented, overwhelming number of warnings (one per DICOM input), better once per series
1268 		CSA->sliceTiming[kMaxEPI3D - 1] = -2.0; //issue 271: flag for unified warning
1269 		for (int z = 0; z < itemsOK; z++)
1270 			CSA->sliceTiming[z] = CSA->sliceTiming[z] - minTimeValue;
1271 	}
1272 	CSA->multiBandFactor = 1;
1273 	timeValue1 = CSA->sliceTiming[0];
1274 	int nTimeZero = 0;
1275 	if (CSA->sliceTiming[0] == 0)
1276 		nTimeZero++;
1277 	int minTimeIndex = 0;
1278 	int maxTimeIndex = minTimeIndex;
1279 	minTimeValue = CSA->sliceTiming[0];
1280 	maxTimeValue = minTimeValue;
1281 	if (isVerbose > 1)
1282 		printMessage("   sliceTimes %g\t", CSA->sliceTiming[0]);
1283 	for (int z = 1; z < itemsOK; z++) { //find index and value of fastest time
1284 		if (isVerbose > 1)
1285 			printMessage("%g\t", CSA->sliceTiming[z]);
1286 		if (CSA->sliceTiming[z] == 0)
1287 			nTimeZero++;
1288 		if (CSA->sliceTiming[z] < minTimeValue) {
1289 			minTimeValue = CSA->sliceTiming[z];
1290 			minTimeIndex = (float)z;
1291 		}
1292 		if (CSA->sliceTiming[z] > maxTimeValue) {
1293 			maxTimeValue = CSA->sliceTiming[z];
1294 			maxTimeIndex = (float)z;
1295 		}
1296 		if (CSA->sliceTiming[z] == timeValue1)
1297 			CSA->multiBandFactor++;
1298 	}
1299 	if (isVerbose > 1)
1300 		printMessage("\n");
1301 	CSA->slice_start = minTimeIndex;
1302 	CSA->slice_end = maxTimeIndex;
1303 	if (minTimeIndex == maxTimeIndex) {
1304 		if (isVerbose)
1305 			printMessage("No variability in slice times (3D EPI?)\n");
1306 	}
1307 	if (nTimeZero < 2) { //not for multi-band, not 3D
1308 		if (minTimeIndex == 1)
1309 			CSA->sliceOrder = NIFTI_SLICE_ALT_INC2; // e.g. 3,1,4,2
1310 		else if (minTimeIndex == (itemsOK - 2))
1311 			CSA->sliceOrder = NIFTI_SLICE_ALT_DEC2; // e.g. 2,4,1,3 or 5,2,4,1,3
1312 		else if ((minTimeIndex == 0) && (CSA->sliceTiming[1] < CSA->sliceTiming[2]))
1313 			CSA->sliceOrder = NIFTI_SLICE_SEQ_INC; // e.g. 1,2,3,4
1314 		else if ((minTimeIndex == 0) && (CSA->sliceTiming[1] > CSA->sliceTiming[2]))
1315 			CSA->sliceOrder = NIFTI_SLICE_ALT_INC; //e.g. 1,3,2,4
1316 		else if ((minTimeIndex == (itemsOK - 1)) && (CSA->sliceTiming[itemsOK - 3] > CSA->sliceTiming[itemsOK - 2]))
1317 			CSA->sliceOrder = NIFTI_SLICE_SEQ_DEC; //e.g. 4,3,2,1 or 5,4,3,2,1
1318 		else if ((minTimeIndex == (itemsOK - 1)) && (CSA->sliceTiming[itemsOK - 3] < CSA->sliceTiming[itemsOK - 2]))
1319 			CSA->sliceOrder = NIFTI_SLICE_ALT_DEC; //e.g. 4,2,3,1 or 3,5,2,4,1
1320 		else {
1321 			if (!is3DAcq) //we expect 3D sequences to be simultaneous
1322 				printWarning("Unable to determine slice order from CSA tag MosaicRefAcqTimes\n");
1323 		}
1324 	}
1325 	if ((CSA->sliceOrder != NIFTI_SLICE_UNKNOWN) && (nTimeZero > 1) && (nTimeZero < itemsOK)) {
1326 		if (isVerbose)
1327 			printMessage(" Multiband x%d sequence: setting slice order as UNKNOWN (instead of %d)\n", nTimeZero, CSA->sliceOrder);
1328 		CSA->sliceOrder = NIFTI_SLICE_UNKNOWN;
1329 	}
1330 } //checkSliceTimes()
1331 
readCSAImageHeader(unsigned char * buff,int lLength,struct TCSAdata * CSA,int isVerbose,bool is3DAcq)1332 int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, int isVerbose, bool is3DAcq) {
1333 //see also http://afni.nimh.nih.gov/pub/dist/src/siemens_dicom_csa.c
1334 //printMessage("%c%c%c%c\n",buff[0],buff[1],buff[2],buff[3]);
1335 	if (lLength < 36)
1336 		return EXIT_FAILURE;
1337 	if ((buff[0] != 'S') || (buff[1] != 'V') || (buff[2] != '1') || (buff[3] != '0'))
1338 		return EXIT_FAILURE;
1339 	int lPos = 8; //skip 8 bytes of data, 'SV10' plus 2 32-bit values unused1 and unused2
1340 	int lnTag = buff[lPos] + (buff[lPos + 1] << 8) + (buff[lPos + 2] << 16) + (buff[lPos + 3] << 24);
1341 	if (buff[lPos + 4] != 77)
1342 		return EXIT_FAILURE;
1343 	lPos += 8; //skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0
1344 	TCSAtag tagCSA;
1345 	TCSAitem itemCSA;
1346 	int itemsOK;
1347 	float lFloats[7];
1348 	for (int lT = 1; lT <= lnTag; lT++) {
1349 		memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag
1350 		lPos += sizeof(tagCSA);
1351 		// Storage order is always little-endian, so byte-swap required values if necessary
1352 		if (!littleEndianPlatform())
1353 			nifti_swap_4bytes(1, &tagCSA.nitems);
1354 		if (isVerbose > 1) //extreme verbosity: show every CSA tag
1355 			printMessage("   %d CSA of %s %d\n", lPos, tagCSA.name, tagCSA.nitems);
1356 		if (tagCSA.nitems > 0) {
1357 			if (strcmp(tagCSA.name, "ImageHistory") == 0)
1358 				CSA->isPhaseMap = csaIsPhaseMap(&buff[lPos], tagCSA.nitems);
1359 			else if (strcmp(tagCSA.name, "NumberOfImagesInMosaic") == 0)
1360 				CSA->mosaicSlices = (int)round(csaMultiFloat(&buff[lPos], 1, lFloats, &itemsOK));
1361 			else if (strcmp(tagCSA.name, "B_value") == 0) {
1362 				CSA->dtiV[0] = csaMultiFloat(&buff[lPos], 1, lFloats, &itemsOK);
1363 				if (CSA->dtiV[0] < 0.0) {
1364 					printWarning("(Corrupt) CSA reports negative b-value! %g\n", CSA->dtiV[0]);
1365 					CSA->dtiV[0] = 0.0;
1366 				}
1367 				CSA->numDti = 1; //triggered by b-value, as B0 images do not have DiffusionGradientDirection tag
1368 			} else if ((strcmp(tagCSA.name, "DiffusionGradientDirection") == 0) && (tagCSA.nitems > 2)) {
1369 				CSA->dtiV[1] = csaMultiFloat(&buff[lPos], 3, lFloats, &itemsOK);
1370 				CSA->dtiV[2] = lFloats[2];
1371 				CSA->dtiV[3] = lFloats[3];
1372 				if (isVerbose)
1373 					printMessage("DiffusionGradientDirection %f %f %f\n", lFloats[1], lFloats[2], lFloats[3]);
1374 			} else if ((strcmp(tagCSA.name, "SliceNormalVector") == 0) && (tagCSA.nitems > 2)) {
1375 				CSA->sliceNormV[1] = csaMultiFloat(&buff[lPos], 3, lFloats, &itemsOK);
1376 				CSA->sliceNormV[2] = lFloats[2];
1377 				CSA->sliceNormV[3] = lFloats[3];
1378 				if (isVerbose > 1)
1379 					printMessage("   SliceNormalVector %f %f %f\n", CSA->sliceNormV[1], CSA->sliceNormV[2], CSA->sliceNormV[3]);
1380 			} else if (strcmp(tagCSA.name, "SliceMeasurementDuration") == 0)
1381 				CSA->sliceMeasurementDuration = csaMultiFloat(&buff[lPos], 3, lFloats, &itemsOK);
1382 			else if (strcmp(tagCSA.name, "BandwidthPerPixelPhaseEncode") == 0)
1383 				CSA->bandwidthPerPixelPhaseEncode = csaMultiFloat(&buff[lPos], 3, lFloats, &itemsOK);
1384 			else if ((strcmp(tagCSA.name, "MosaicRefAcqTimes") == 0) && (tagCSA.nitems > 3)) {
1385 				if (itemsOK > kMaxEPI3D) {
1386 					printError("Please increase kMaxEPI3D and recompile\n");
1387 				} else {
1388 					float *sliceTimes = (float *)malloc(sizeof(float) * (tagCSA.nitems + 1));
1389 					csaMultiFloat(&buff[lPos], tagCSA.nitems, sliceTimes, &itemsOK);
1390 					for (int z = 0; z < kMaxEPI3D; z++)
1391 						CSA->sliceTiming[z] = -1.0;
1392 					for (int z = 0; z < itemsOK; z++)
1393 						CSA->sliceTiming[z] = sliceTimes[z + 1];
1394 					free(sliceTimes);
1395 					checkSliceTimes(CSA, itemsOK, isVerbose, is3DAcq);
1396 				}
1397 			} else if (strcmp(tagCSA.name, "ProtocolSliceNumber") == 0)
1398 				CSA->protocolSliceNumber1 = (int)round(csaMultiFloat(&buff[lPos], 1, lFloats, &itemsOK));
1399 			else if (strcmp(tagCSA.name, "PhaseEncodingDirectionPositive") == 0)
1400 				CSA->phaseEncodingDirectionPositive = (int)round(csaMultiFloat(&buff[lPos], 1, lFloats, &itemsOK));
1401 			for (int lI = 1; lI <= tagCSA.nitems; lI++) {
1402 				memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
1403 				lPos += sizeof(itemCSA);
1404 				// Storage order is always little-endian, so byte-swap required values if necessary
1405 				if (!littleEndianPlatform())
1406 					nifti_swap_4bytes(1, &itemCSA.xx2_Len);
1407 				lPos += ((itemCSA.xx2_Len + 3) / 4) * 4;
1408 			}
1409 		} //if at least 1 item
1410 	} // for lT 1..lnTag
1411 	if (CSA->protocolSliceNumber1 > 1)
1412 		CSA->sliceOrder = NIFTI_SLICE_UNKNOWN;
1413 	return EXIT_SUCCESS;
1414 } // readCSAImageHeader()
1415 
dcmMultiShorts(int lByteLength,unsigned char lBuffer[],int lnShorts,uint16_t * lShorts,bool littleEndian)1416 void dcmMultiShorts(int lByteLength, unsigned char lBuffer[], int lnShorts, uint16_t *lShorts, bool littleEndian) {
1417 //read array of unsigned shorts US http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html
1418 	if ((lnShorts < 1) || (lByteLength != (lnShorts * 2)))
1419 		return;
1420 	memcpy(&lShorts[0], (uint16_t *)&lBuffer[0], lByteLength);
1421 	bool swap = (littleEndian != littleEndianPlatform());
1422 	if (swap)
1423 		nifti_swap_2bytes(lnShorts, &lShorts[0]);
1424 } //dcmMultiShorts()
1425 
dcmMultiLongs(int lByteLength,unsigned char lBuffer[],int lnLongs,uint32_t * lLongs,bool littleEndian)1426 void dcmMultiLongs(int lByteLength, unsigned char lBuffer[], int lnLongs, uint32_t *lLongs, bool littleEndian) {
1427 //read array of unsigned longs UL http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html
1428 	if ((lnLongs < 1) || (lByteLength != (lnLongs * 4)))
1429 		return;
1430 	memcpy(&lLongs[0], (uint32_t *)&lBuffer[0], lByteLength);
1431 	bool swap = (littleEndian != littleEndianPlatform());
1432 	if (swap)
1433 		nifti_swap_4bytes(lnLongs, &lLongs[0]);
1434 } //dcmMultiLongs()
1435 
dcmMultiFloat(int lByteLength,char lBuffer[],int lnFloats,float * lFloats)1436 void dcmMultiFloat(int lByteLength, char lBuffer[], int lnFloats, float *lFloats) {
1437 //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats]
1438 	if ((lnFloats < 1) || (lByteLength < 1))
1439 		return;
1440 	char *cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
1441 	memcpy(cString, (char *)&lBuffer[0], lByteLength);
1442 	cString[lByteLength] = 0; //null terminate
1443 	char *temp = (char *)malloc(lByteLength + 1);
1444 	int f = 0, lStart = 0;
1445 	bool isOK = false;
1446 	for (int i = 0; i <= lByteLength; i++) {
1447 		if ((lBuffer[i] >= '0') && (lBuffer[i] <= '9'))
1448 			isOK = true;
1449 		if ((isOK) && ((i == (lByteLength)) || (lBuffer[i] == '/') || (lBuffer[i] == ' ') || (lBuffer[i] == '\\'))) {
1450 			snprintf(temp, i - lStart + 1, "%s", &cString[lStart]);
1451 			//printMessage("dcmMultiFloat %s\n",temp);
1452 			if (f < lnFloats) {
1453 				f++;
1454 				lFloats[f] = (float)atof(temp);
1455 				isOK = false;
1456 				//printMessage("%d == %f\n", f, atof(temp));
1457 			} //if f <= nFloats
1458 			lStart = i + 1;
1459 		} //if isOK
1460 	} //for i to length
1461 	free(temp);
1462 	free(cString);
1463 } //dcmMultiFloat()
1464 
dcmStrFloat(const int lByteLength,const unsigned char lBuffer[])1465 float dcmStrFloat(const int lByteLength, const unsigned char lBuffer[]) { //read float stored as a string
1466 	char *cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
1467 	memcpy(cString, (char *)&lBuffer[0], lByteLength);
1468 	cString[lByteLength] = 0; //null terminate
1469 	float ret = (float)atof(cString);
1470 	free(cString);
1471 	return ret;
1472 } //dcmStrFloat()
1473 
headerDcm2Nii(struct TDICOMdata d,struct nifti_1_header * h,bool isComputeSForm)1474 int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeSForm) {
1475 	memset(h, 0, sizeof(nifti_1_header)); //zero-fill structure so unused items are consistent
1476 	for (int i = 0; i < 80; i++)
1477 		h->descrip[i] = 0;
1478 	for (int i = 0; i < 24; i++)
1479 		h->aux_file[i] = 0;
1480 	for (int i = 0; i < 18; i++)
1481 		h->db_name[i] = 0;
1482 	for (int i = 0; i < 10; i++)
1483 		h->data_type[i] = 0;
1484 	for (int i = 0; i < 16; i++)
1485 		h->intent_name[i] = 0;
1486 	if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3)) {
1487 		h->intent_code = NIFTI_INTENT_ESTIMATE; //make sure we treat this as RGBRGB...RGB
1488 		h->datatype = DT_RGB24;
1489 	} else if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 1))
1490 		h->datatype = DT_UINT8;
1491 	else if ((d.bitsAllocated == 12) && (d.samplesPerPixel == 1))
1492 		h->datatype = DT_INT16;
1493 	else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (d.isSigned))
1494 		h->datatype = DT_INT16;
1495 	else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (!d.isSigned))
1496 		h->datatype = DT_UINT16;
1497 	else if ((d.bitsAllocated == 32) && (d.isFloat))
1498 		h->datatype = DT_FLOAT32;
1499 	else if (d.bitsAllocated == 32)
1500 		h->datatype = DT_INT32;
1501 	else if ((d.bitsAllocated == 64) && (d.isFloat))
1502 		h->datatype = DT_FLOAT64;
1503 	else {
1504 		printMessage("Unsupported DICOM bit-depth %d with %d samples per pixel\n", d.bitsAllocated, d.samplesPerPixel);
1505 		return EXIT_FAILURE;
1506 	}
1507 	if ((h->datatype == DT_UINT16) && (d.bitsStored > 0) && (d.bitsStored < 16))
1508 		h->datatype = DT_INT16; // DT_INT16 is more widely supported, same representation for values 0..32767
1509 	for (int i = 0; i < 8; i++) {
1510 		h->pixdim[i] = 0.0f;
1511 		h->dim[i] = 0;
1512 	}
1513 	//next items listed as unused in NIfTI format, but zeroed for consistency across runs
1514 	h->extents = 0;
1515 	h->session_error = kSessionOK;
1516 	h->glmin = 0; //unused, but make consistent
1517 	h->glmax = 0; //unused, but make consistent
1518 	h->regular = 114; //in legacy Analyze this was always 114
1519 	//these are important
1520 	h->scl_inter = d.intenIntercept;
1521 	h->scl_slope = d.intenScale;
1522 	h->cal_max = 0;
1523 	h->cal_min = 0;
1524 	h->magic[0] = 'n';
1525 	h->magic[1] = '+';
1526 	h->magic[2] = '1';
1527 	h->magic[3] = '\0';
1528 	h->vox_offset = (float)d.imageStart;
1529 	if (d.bitsAllocated == 12)
1530 		h->bitpix = 16 * d.samplesPerPixel;
1531 	else
1532 		h->bitpix = d.bitsAllocated * d.samplesPerPixel;
1533 	h->pixdim[1] = d.xyzMM[1];
1534 	h->pixdim[2] = d.xyzMM[2];
1535 	h->pixdim[3] = d.xyzMM[3];
1536 	h->pixdim[4] = d.TR / 1000.0; //TR reported in msec, time is in sec
1537 	h->dim[1] = d.xyzDim[1];
1538 	h->dim[2] = d.xyzDim[2];
1539 	h->dim[3] = d.xyzDim[3];
1540 	h->dim[4] = d.xyzDim[4];
1541 	h->dim[5] = 1;
1542 	h->dim[6] = 1;
1543 	h->dim[7] = 1;
1544 	if (h->dim[4] < 2)
1545 		h->dim[0] = 3;
1546 	else
1547 		h->dim[0] = 4;
1548 	for (int i = 0; i <= 3; i++) {
1549 		h->srow_x[i] = 0.0f;
1550 		h->srow_y[i] = 0.0f;
1551 		h->srow_z[i] = 0.0f;
1552 	}
1553 	h->slice_start = 0;
1554 	h->slice_end = 0;
1555 	h->srow_x[0] = -1;
1556 	h->srow_y[2] = 1;
1557 	h->srow_z[1] = -1;
1558 	h->srow_x[3] = ((float)h->dim[1] / 2);
1559 	h->srow_y[3] = -((float)h->dim[3] / 2);
1560 	h->srow_z[3] = ((float)h->dim[2] / 2);
1561 	h->qform_code = NIFTI_XFORM_UNKNOWN;
1562 	h->sform_code = NIFTI_XFORM_UNKNOWN;
1563 	h->toffset = 0;
1564 	h->intent_code = NIFTI_INTENT_NONE;
1565 	h->dim_info = 0; //Freq, Phase and Slice all unknown
1566 	h->xyzt_units = NIFTI_UNITS_MM + NIFTI_UNITS_SEC;
1567 	h->slice_duration = 0; //avoid +inf/-inf, NaN
1568 	h->intent_p1 = 0; //avoid +inf/-inf, NaN
1569 	h->intent_p2 = 0; //avoid +inf/-inf, NaN
1570 	h->intent_p3 = 0; //avoid +inf/-inf, NaN
1571 	h->pixdim[0] = 1; //QFactor should be 1 or -1
1572 	h->sizeof_hdr = 348; //used to signify header does not need to be byte-swapped
1573 	h->slice_code = d.CSA.sliceOrder;
1574 	if (isComputeSForm)
1575 		headerDcm2Nii2(d, d, h, false);
1576 	return EXIT_SUCCESS;
1577 } // headerDcm2Nii()
1578 
isFloatDiff(float a,float b)1579 bool isFloatDiff(float a, float b) {
1580 	return (fabs(a - b) > FLT_EPSILON);
1581 } //isFloatDiff()
1582 
nifti_mat33_reorder_cols(mat33 m,ivec3 v)1583 mat33 nifti_mat33_reorder_cols(mat33 m, ivec3 v) {
1584 // matlab equivalent ret = m(:, v); where v is 1,2,3 [INDEXED FROM ONE!!!!]
1585 	mat33 ret;
1586 	for (int r = 0; r < 3; r++) {
1587 		for (int c = 0; c < 3; c++)
1588 			ret.m[r][c] = m.m[r][v.v[c] - 1];
1589 	}
1590 	return ret;
1591 } //nifti_mat33_reorder_cols()
1592 
changeExt(char * file_name,const char * ext)1593 void changeExt(char *file_name, const char *ext) {
1594 	char *p_extension;
1595 	p_extension = strrchr(file_name, '.');
1596 	if (p_extension)
1597 		strcpy(++p_extension, ext);
1598 } //changeExt()
1599 
cleanStr(char * lOut)1600 void cleanStr(char *lOut) {
1601 //e.g. strings such as image comments with special characters (e.g. "G/6/2009") can disrupt file saves
1602 	size_t lLength = strlen(lOut);
1603 	if (lLength < 1)
1604 		return;
1605 	char *cString = (char *)malloc(sizeof(char) * (lLength + 1));
1606 	cString[lLength] = 0;
1607 	memcpy(cString, (char *)&lOut[0], lLength);
1608 	for (int i = 0; i < lLength; i++)
1609 		//assume specificCharacterSet (0008,0005) is ISO_IR 100 http://en.wikipedia.org/wiki/ISO/IEC_8859-1
1610 		if (cString[i] < 1) {
1611 			unsigned char c = (unsigned char)cString[i];
1612 			if ((c >= 192) && (c <= 198))
1613 				cString[i] = 'A';
1614 			if (c == 199)
1615 				cString[i] = 'C';
1616 			if ((c >= 200) && (c <= 203))
1617 				cString[i] = 'E';
1618 			if ((c >= 204) && (c <= 207))
1619 				cString[i] = 'I';
1620 			if (c == 208)
1621 				cString[i] = 'D';
1622 			if (c == 209)
1623 				cString[i] = 'N';
1624 			if ((c >= 210) && (c <= 214))
1625 				cString[i] = 'O';
1626 			if (c == 215)
1627 				cString[i] = 'x';
1628 			if (c == 216)
1629 				cString[i] = 'O';
1630 			if ((c >= 217) && (c <= 220))
1631 				cString[i] = 'O';
1632 			if (c == 221)
1633 				cString[i] = 'Y';
1634 			if ((c >= 224) && (c <= 230))
1635 				cString[i] = 'a';
1636 			if (c == 231)
1637 				cString[i] = 'c';
1638 			if ((c >= 232) && (c <= 235))
1639 				cString[i] = 'e';
1640 			if ((c >= 236) && (c <= 239))
1641 				cString[i] = 'i';
1642 			if (c == 240)
1643 				cString[i] = 'o';
1644 			if (c == 241)
1645 				cString[i] = 'n';
1646 			if ((c >= 242) && (c <= 246))
1647 				cString[i] = 'o';
1648 			if (c == 248)
1649 				cString[i] = 'o';
1650 			if ((c >= 249) && (c <= 252))
1651 				cString[i] = 'u';
1652 			if (c == 253)
1653 				cString[i] = 'y';
1654 			if (c == 255)
1655 				cString[i] = 'y';
1656 		}
1657 	for (int i = 0; i < lLength; i++)
1658 		if ((cString[i] < 1) || (cString[i] == ' ') || (cString[i] == ',') || (cString[i] == '/') || (cString[i] == '\\') || (cString[i] == '%') || (cString[i] == '*') || (cString[i] == 9) || (cString[i] == 10) || (cString[i] == 11) || (cString[i] == 13))
1659 			cString[i] = '_'; //issue398
1660 	//if ((cString[i]<1) || (cString[i]==' ') || (cString[i]==',') || (cString[i]=='^') || (cString[i]=='/') || (cString[i]=='\\') || (cString[i]=='%') || (cString[i]=='*') || (cString[i] == 9) || (cString[i] == 10) || (cString[i] == 11) || (cString[i] == 13)) cString[i] = '_';
1661 	int len = 1;
1662 	for (int i = 1; i < lLength; i++) { //remove repeated "_"
1663 		if ((cString[i - 1] != '_') || (cString[i] != '_')) {
1664 			cString[len] = cString[i];
1665 			len++;
1666 		}
1667 	} //for each item
1668 	if (cString[len - 1] == '_')
1669 		len--;
1670 	cString[len] = 0; //null-terminate, strlcpy does this anyway
1671 	int maxLen = kDICOMStr;
1672 	len = dcmStrLen(len, maxLen);
1673 	if (len == maxLen) { //we need space for null-termination
1674 		if (cString[len - 2] == '_')
1675 			len = len - 2;
1676 	}
1677 	memcpy(lOut, cString, len - 1);
1678 	lOut[len - 1] = 0;
1679 	free(cString);
1680 } //cleanStr()
1681 
isSameFloatGE(float a,float b)1682 int isSameFloatGE(float a, float b) {
1683 	//Kludge for bug in 0002,0016="DIGITAL_JACKET", 0008,0070="GE MEDICAL SYSTEMS" DICOM data: Orient field (0020:0037) can vary 0.00604261 == 0.00604273 !!!
1684 	//return (a == b); //niave approach does not have any tolerance for rounding errors
1685 	return (fabs(a - b) <= 0.0001);
1686 }
1687 
nii_readParRec(char * parname,int isVerbose,struct TDTI4D * dti4D,bool isReadPhase)1688 struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dti4D, bool isReadPhase) {
1689 	struct TDICOMdata d = clear_dicom_data();
1690 	dti4D->sliceOrder[0] = -1;
1691 	dti4D->volumeOnsetTime[0] = -1;
1692 	dti4D->decayFactor[0] = -1;
1693 	dti4D->frameDuration[0] = -1;
1694 	//dti4D->fragmentOffset[0] = -1;
1695 	dti4D->intenScale[0] = 0.0;
1696 	strcpy(d.protocolName, ""); //erase dummy with empty
1697 	strcpy(d.seriesDescription, ""); //erase dummy with empty
1698 	strcpy(d.sequenceName, ""); //erase dummy with empty
1699 	strcpy(d.scanningSequence, "");
1700 	FILE *fp = fopen(parname, "r");
1701 	if (fp == NULL)
1702 		return d;
1703 #define LINESZ 2048
1704 #define kSlice 0
1705 #define kEcho 1
1706 #define kDyn 2
1707 #define kCardiac 3
1708 #define kImageType 4
1709 #define kSequence 5
1710 #define kIndex 6
1711 //V3 only identical for columns 1..6
1712 #define kBitsPerVoxel 7 //V3: not per slice: "Image pixel size [8 or 16 bits]"
1713 #define kXdim 9 //V3: not per slice: "Recon resolution (x, y)"
1714 #define kYdim 10 //V3: not per slice: "Recon resolution (x, y)"
1715 	int kRI = 11; //V3: 7
1716 	int kRS = 12; //V3: 8
1717 	int kSS = 13; //V3: 9
1718 	int kAngulationAPs = 16; //V3: 12
1719 	int kAngulationFHs = 17; //V3: 13
1720 	int kAngulationRLs = 18; //V3: 14
1721 	int kPositionAP = 19; //V3: 15
1722 	int kPositionFH = 20; //V3: 16
1723 	int kPositionRL = 21; //V3: 17
1724 #define kThickmm 22 //V3: not per slice: "Slice thickness [mm]"
1725 #define kGapmm 23 //V3: not per slice: "Slice gap [mm]"
1726 	int kSliceOrients = 25; //V3: 19
1727 	int kXmm = 28; //V3: 22
1728 	int kYmm = 29; //V3: 23
1729 	int kTEcho = 30; //V3: 24
1730 	int kDynTime = 31; //V3: 25
1731 	int kTriggerTime = 32; //V3: 26
1732 	int kbval = 33;//V3: 27
1733 //the following do not exist in V3
1734 #define kInversionDelayMs 40
1735 #define kbvalNumber 41
1736 #define kGradientNumber 42
1737 //the following do not exist in V40 or earlier
1738 #define kv1 47
1739 #define kv2 45
1740 #define kv3 46
1741 //the following do not exist in V41 or earlier
1742 #define kASL 48
1743 #define kMaxImageType 4 //4 observed image types: real, imag, mag, phase (in theory also subsequent calculation such as B1)
1744 	printWarning("dcm2niix PAR is not actively supported (hint: use dicm2nii)\n");
1745 	if (isReadPhase)
1746 		printWarning(" Reading phase images from PAR/REC\n");
1747 	char buff[LINESZ];
1748 	//next values: PAR V3 only
1749 	int v3BitsPerVoxel = 16; //V3: not per slice: "Image pixel size [8 or 16 bits]"
1750 	int v3Xdim = 128; //not per slice: "Recon resolution (x, y)"
1751 	int v3Ydim = 128; //V3: not per slice: "Recon resolution (x, y)"
1752 	float v3Thickmm = 2.0; //V3: not per slice: "Slice thickness [mm]"
1753 	float v3Gapmm = 0.0; //V3: not per slice: "Slice gap [mm]"
1754 	//from top of header
1755 	int maxNumberOfDiffusionValues = 1;
1756 	int maxNumberOfGradientOrients = 1;
1757 	int maxNumberOfCardiacPhases = 1;
1758 	int maxNumberOfEchoes = 1;
1759 	int maxNumberOfDynamics = 1;
1760 	int maxNumberOfMixes = 1;
1761 	int maxNumberOfLabels = 1; //Number of label types <0=no ASL>
1762 	float maxBValue = 0.0f;
1763 	float maxDynTime = 0.0f;
1764 	float minDynTime = 999999.0f;
1765 	float TE = 0.0;
1766 	int minDyn = 32767;
1767 	int maxDyn = 0;
1768 	int minSlice = 32767;
1769 	int maxSlice = 0;
1770 	bool ADCwarning = false;
1771 	bool isTypeWarning = false;
1772 	bool isType4Warning = false;
1773 	bool isSequenceWarning = false;
1774 	int numSlice2D = 0;
1775 	int prevDyn = -1;
1776 	bool dynNotAscending = false;
1777 	int parVers = 0;
1778 	int maxSeq = -1; //maximum value of Seq column
1779 	int seq1 = -1; //value of Seq volume for first slice
1780 	int maxEcho = 1;
1781 	int maxCardiac = 1;
1782 	int nCols = 26;
1783 	//int diskSlice = 0;
1784 	int num3DExpected = 0; //number of 3D volumes in the top part of the header
1785 	int num2DExpected = 0; //number of 2D slices described in the top part of the header
1786 	int maxVol = -1;
1787 	int patientPositionNumPhilips = 0;
1788 	d.isValid = false;
1789 	const int kMaxCols = 49;
1790 	float *cols = (float *)malloc(sizeof(float) * (kMaxCols + 1));
1791 	for (int i = 0; i < kMaxCols; i++)
1792 		cols[i] = 0.0; //old versions of PAR do not fill all columns - beware of buffer overflow
1793 	char *p = fgets(buff, LINESZ, fp);
1794 	bool isIntenScaleVaries = false;
1795 	for (int i = 0; i < kMaxDTI4D; i++) {
1796 		dti4D->S[i].V[0] = -1.0;
1797 		dti4D->TE[i] = -1.0;
1798 	}
1799 	for (int i = 0; i < kMaxSlice2D; i++)
1800 		dti4D->sliceOrder[i] = -1;
1801 	while (p) {
1802 		if (strlen(buff) < 1)
1803 			continue;
1804 		if (buff[0] == '#') { //comment
1805 			char Comment[7][50];
1806 			sscanf(buff, "# %s %s %s %s %s %s V%s\n", Comment[0], Comment[1], Comment[2], Comment[3], Comment[4], Comment[5], Comment[6]);
1807 			if ((strcmp(Comment[0], "sl") == 0) && (strcmp(Comment[1], "ec") == 0)) {
1808 				num3DExpected = maxNumberOfGradientOrients * maxNumberOfDiffusionValues * maxNumberOfLabels * maxNumberOfCardiacPhases * maxNumberOfEchoes * maxNumberOfDynamics * maxNumberOfMixes;
1809 				num2DExpected = d.xyzDim[3] * num3DExpected;
1810 				if ((num2DExpected) >= kMaxSlice2D) {
1811 					printError("Use dicm2nii or increase kMaxSlice2D to be more than %d\n", num2DExpected);
1812 					printMessage("  slices*grad*bval*cardiac*echo*dynamic*mix*label = %d*%d*%d*%d*%d*%d*%d*%d\n",
1813 						d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues,
1814 						maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels);
1815 					free(cols);
1816 					return d;
1817 				}
1818 			}
1819 			if (strcmp(Comment[1], "TRYOUT") == 0) {
1820 				//sscanf(buff, "# %s %s %s %s %s %s V%s\n", Comment[0], Comment[1], Comment[2], Comment[3],Comment[4], Comment[5],Comment[6]);
1821 				parVers = (int)round(atof(Comment[6]) * 10); //4.2 = 42 etc
1822 				if (parVers <= 29) {
1823 					printMessage("Unsupported old PAR version %0.2f (use dicm2nii)\n", parVers / 10.0);
1824 					return d;
1825 					//nCols = 26; //e.g. PAR 3.0 has 26 relevant columns
1826 				}
1827 				if (parVers < 40) {
1828 					nCols = 29; // PAR 3.0?
1829 					kRI = 7;
1830 					kRS = 8;
1831 					kSS = 9;
1832 					kAngulationAPs = 12;
1833 					kAngulationFHs = 13;
1834 					kAngulationRLs = 14;
1835 					kPositionAP = 15;
1836 					kPositionFH = 16;
1837 					kPositionRL = 17;
1838 					kSliceOrients = 19;
1839 					kXmm = 22;
1840 					kYmm = 23;
1841 					kTEcho = 24;
1842 					kDynTime = 25;
1843 					kTriggerTime = 26;
1844 					kbval = 27;
1845 				} else if (parVers < 41)
1846 					nCols = kv1; //e.g PAR 4.0
1847 				else if (parVers < 42)
1848 					nCols = kASL; //e.g. PAR 4.1 - last column is final diffusion b-value
1849 				else
1850 					nCols = kMaxCols; //e.g. PAR 4.2
1851 			}
1852 			//the following do not exist in V3
1853 			p = fgets(buff, LINESZ, fp); //get next line
1854 			continue;
1855 		} //process '#' comment
1856 		if (buff[0] == '.') { //tag
1857 			char Comment[9][50];
1858 			for (int i = 0; i < 9; i++)
1859 				strcpy(Comment[i], "");
1860 			sscanf(buff, ". %s %s %s %s %s %s %s %s %s\n", Comment[0], Comment[1], Comment[2], Comment[3], Comment[4], Comment[5], Comment[6], Comment[7], Comment[8]);
1861 			if ((strcmp(Comment[0], "Acquisition") == 0) && (strcmp(Comment[1], "nr") == 0)) {
1862 				d.acquNum = atoi(Comment[3]);
1863 				d.seriesNum = d.acquNum;
1864 			}
1865 			if ((strcmp(Comment[0], "Recon") == 0) && (strcmp(Comment[1], "resolution") == 0)) {
1866 				v3Xdim = (int)atoi(Comment[5]);
1867 				v3Ydim = (int)atoi(Comment[6]);
1868 				//printMessage("recon %d,%d\n", v3Xdim,v3Ydim);
1869 			}
1870 			if ((strcmp(Comment[1], "pixel") == 0) && (strcmp(Comment[2], "size") == 0)) {
1871 				v3BitsPerVoxel = (int)atoi(Comment[8]);
1872 				//printMessage("bits %d\n", v3BitsPerVoxel);
1873 			}
1874 			if ((strcmp(Comment[0], "Slice") == 0) && (strcmp(Comment[1], "gap") == 0)) {
1875 				v3Gapmm = (float)atof(Comment[4]);
1876 				//printMessage("gap %g\n", v3Gapmm);
1877 			}
1878 			if ((strcmp(Comment[0], "Slice") == 0) && (strcmp(Comment[1], "thickness") == 0)) {
1879 				v3Thickmm = (float)atof(Comment[4]);
1880 				//printMessage("thick %g\n", v3Thickmm);
1881 			}
1882 			if ((strcmp(Comment[0], "Repetition") == 0) && (strcmp(Comment[1], "time") == 0))
1883 				d.TR = (float)atof(Comment[4]);
1884 			if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "name") == 0)) {
1885 				strcpy(d.patientName, Comment[3]);
1886 				strcat(d.patientName, Comment[4]);
1887 				strcat(d.patientName, Comment[5]);
1888 				strcat(d.patientName, Comment[6]);
1889 				strcat(d.patientName, Comment[7]);
1890 				cleanStr(d.patientName);
1891 				//printMessage("%s\n",d.patientName);
1892 			}
1893 			if ((strcmp(Comment[0], "Technique") == 0) && (strcmp(Comment[1], ":") == 0)) {
1894 				strcpy(d.patientID, Comment[2]);
1895 				strcat(d.patientID, Comment[3]);
1896 				strcat(d.patientID, Comment[4]);
1897 				strcat(d.patientID, Comment[5]);
1898 				strcat(d.patientID, Comment[6]);
1899 				strcat(d.patientID, Comment[7]);
1900 				cleanStr(d.patientID);
1901 			}
1902 			if ((strcmp(Comment[0], "Protocol") == 0) && (strcmp(Comment[1], "name") == 0)) {
1903 				strcpy(d.protocolName, Comment[3]);
1904 				strcat(d.protocolName, Comment[4]);
1905 				strcat(d.protocolName, Comment[5]);
1906 				strcat(d.protocolName, Comment[6]);
1907 				strcat(d.protocolName, Comment[7]);
1908 				cleanStr(d.protocolName);
1909 			}
1910 			if ((strcmp(Comment[0], "Examination") == 0) && (strcmp(Comment[1], "name") == 0)) {
1911 				strcpy(d.imageComments, Comment[3]);
1912 				strcat(d.imageComments, Comment[4]);
1913 				strcat(d.imageComments, Comment[5]);
1914 				strcat(d.imageComments, Comment[6]);
1915 				strcat(d.imageComments, Comment[7]);
1916 				cleanStr(d.imageComments);
1917 			}
1918 			if ((strcmp(Comment[0], "Series") == 0) && (strcmp(Comment[1], "Type") == 0)) {
1919 				strcpy(d.seriesDescription, Comment[3]);
1920 				strcat(d.seriesDescription, Comment[4]);
1921 				strcat(d.seriesDescription, Comment[5]);
1922 				strcat(d.seriesDescription, Comment[6]);
1923 				strcat(d.seriesDescription, Comment[7]);
1924 				cleanStr(d.seriesDescription);
1925 			}
1926 			if ((strcmp(Comment[0], "Examination") == 0) && (strcmp(Comment[1], "date/time") == 0)) {
1927 				if ((strlen(Comment[3]) >= 10) && (strlen(Comment[5]) >= 8)) {
1928 					//DICOM date format is YYYYMMDD, but PAR stores YYYY.MM.DD 2016.03.25
1929 					d.studyDate[0] = Comment[3][0];
1930 					d.studyDate[1] = Comment[3][1];
1931 					d.studyDate[2] = Comment[3][2];
1932 					d.studyDate[3] = Comment[3][3];
1933 					d.studyDate[4] = Comment[3][5];
1934 					d.studyDate[5] = Comment[3][6];
1935 					d.studyDate[6] = Comment[3][8];
1936 					d.studyDate[7] = Comment[3][9];
1937 					d.studyDate[8] = '\0';
1938 					//DICOM time format is HHMMSS.FFFFFF, but PAR stores HH:MM:SS, e.g. 18:00:42 or 09:34:16
1939 					d.studyTime[0] = Comment[5][0];
1940 					d.studyTime[1] = Comment[5][1];
1941 					d.studyTime[2] = Comment[5][3];
1942 					d.studyTime[3] = Comment[5][4];
1943 					d.studyTime[4] = Comment[5][6];
1944 					d.studyTime[5] = Comment[5][7];
1945 					d.studyTime[6] = '\0';
1946 					d.dateTime = (atof(d.studyDate) * 1000000) + atof(d.studyTime);
1947 				}
1948 			}
1949 			if ((strcmp(Comment[0], "Off") == 0) && (strcmp(Comment[1], "Centre") == 0)) {
1950 				//Off Centre midslice(ap,fh,rl) [mm]
1951 				d.stackOffcentre[2] = (float)atof(Comment[5]);
1952 				d.stackOffcentre[3] = (float)atof(Comment[6]);
1953 				d.stackOffcentre[1] = (float)atof(Comment[7]);
1954 			}
1955 			if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "position") == 0)) {
1956 				//Off Centre midslice(ap,fh,rl) [mm]
1957 				d.patientOrient[0] = toupper(Comment[3][0]);
1958 				d.patientOrient[1] = toupper(Comment[4][0]);
1959 				d.patientOrient[2] = toupper(Comment[5][0]);
1960 				d.patientOrient[3] = 0;
1961 			}
1962 			if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "slices/locations") == 0)) {
1963 				d.xyzDim[3] = atoi(Comment[5]);
1964 			}
1965 			if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "diffusion") == 0)) {
1966 				maxNumberOfDiffusionValues = atoi(Comment[6]);
1967 				//if (maxNumberOfDiffusionValues > 1) maxNumberOfDiffusionValues -= 1; //if two listed, one is B=0
1968 			}
1969 			if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "gradient") == 0)) {
1970 				maxNumberOfGradientOrients = atoi(Comment[6]);
1971 				//Warning ISOTROPIC scans may be stored that are not reported here! 32 directions plus isotropic = 33 volumes
1972 			}
1973 			if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "cardiac") == 0)) {
1974 				maxNumberOfCardiacPhases = atoi(Comment[6]);
1975 			}
1976 			if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "echoes") == 0)) {
1977 				maxNumberOfEchoes = atoi(Comment[5]);
1978 				if (maxNumberOfEchoes > 1)
1979 					d.isMultiEcho = true;
1980 			}
1981 			if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "dynamics") == 0)) {
1982 				maxNumberOfDynamics = atoi(Comment[5]);
1983 			}
1984 			if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "mixes") == 0)) {
1985 				maxNumberOfMixes = atoi(Comment[5]);
1986 				if (maxNumberOfMixes > 1)
1987 					printError("maxNumberOfMixes > 1. Please update this software to support these images\n");
1988 			}
1989 			if ((strcmp(Comment[0], "Number") == 0) && (strcmp(Comment[2], "label") == 0)) {
1990 				maxNumberOfLabels = atoi(Comment[7]);
1991 				if (maxNumberOfLabels < 1)
1992 					maxNumberOfLabels = 1;
1993 			}
1994 			p = fgets(buff, LINESZ, fp); //get next line
1995 			continue;
1996 		} //process '.' tag
1997 		if (strlen(buff) < 24) { //empty line
1998 			p = fgets(buff, LINESZ, fp); //get next line
1999 			continue;
2000 		}
2001 		if (parVers < 20) {
2002 			printError("PAR files should have 'CLINICAL TRYOUT' line with a version from 2.0-4.2: %s\n", parname);
2003 			free(cols);
2004 			return d;
2005 		}
2006 		for (int i = 0; i <= nCols; i++)
2007 			cols[i] = strtof(p, &p); // p+1 skip comma, read a float
2008 		//printMessage("xDim %dv%d yDim %dv%d bits %dv%d\n", d.xyzDim[1],(int)cols[kXdim], d.xyzDim[2], (int)cols[kYdim], d.bitsAllocated, (int)cols[kBitsPerVoxel]);
2009 		if ((int)cols[kSlice] == 0) { //line does not contain attributes
2010 			p = fgets(buff, LINESZ, fp); //get next line
2011 			continue;
2012 		}
2013 		//diskSlice ++;
2014 		bool isADC = false;
2015 		if ((maxNumberOfGradientOrients >= 2) && (cols[kbval] > 50) && isSameFloat(0.0, cols[kv1]) && isSameFloat(0.0, cols[kv2]) && isSameFloat(0.0, cols[kv3])) {
2016 			isADC = true;
2017 			ADCwarning = true;
2018 		}
2019 		if (numSlice2D < 1) {
2020 			d.xyzMM[1] = cols[kXmm];
2021 			d.xyzMM[2] = cols[kYmm];
2022 			if (parVers < 40) { //v3 does things differently
2023 				//cccc
2024 				d.xyzDim[1] = v3Xdim;
2025 				d.xyzDim[2] = v3Ydim;
2026 				d.xyzMM[3] = v3Thickmm + v3Gapmm;
2027 				d.bitsAllocated = v3BitsPerVoxel;
2028 				d.bitsStored = v3BitsPerVoxel;
2029 			} else {
2030 				d.xyzDim[1] = (int)cols[kXdim];
2031 				d.xyzDim[2] = (int)cols[kYdim];
2032 				d.xyzMM[3] = cols[kThickmm] + cols[kGapmm];
2033 				d.bitsAllocated = (int)cols[kBitsPerVoxel];
2034 				d.bitsStored = (int)cols[kBitsPerVoxel];
2035 			}
2036 			d.patientPosition[1] = cols[kPositionRL];
2037 			d.patientPosition[2] = cols[kPositionAP];
2038 			d.patientPosition[3] = cols[kPositionFH];
2039 			d.angulation[1] = cols[kAngulationRLs];
2040 			d.angulation[2] = cols[kAngulationAPs];
2041 			d.angulation[3] = cols[kAngulationFHs];
2042 			d.sliceOrient = (int)cols[kSliceOrients];
2043 			d.TE = cols[kTEcho];
2044 			d.echoNum = cols[kEcho];
2045 			d.TI = cols[kInversionDelayMs];
2046 			d.intenIntercept = cols[kRI];
2047 			d.intenScale = cols[kRS];
2048 			d.intenScalePhilips = cols[kSS];
2049 		} else {
2050 			if (parVers >= 40) {
2051 				if ((d.xyzDim[1] != cols[kXdim]) || (d.xyzDim[2] != cols[kYdim]) || (d.bitsAllocated != cols[kBitsPerVoxel])) {
2052 					printError("Slice dimensions or bit depth varies %s\n", parname);
2053 					printError("xDim %dv%d yDim %dv%d bits  %dv%d\n", d.xyzDim[1], (int)cols[kXdim], d.xyzDim[2], (int)cols[kYdim], d.bitsAllocated, (int)cols[kBitsPerVoxel]);
2054 					return d;
2055 				}
2056 			}
2057 			if ((d.intenScale != cols[kRS]) || (d.intenIntercept != cols[kRI]))
2058 				isIntenScaleVaries = true;
2059 		}
2060 		if (cols[kImageType] == 0)
2061 			d.isHasMagnitude = true;
2062 		if (cols[kImageType] != 0)
2063 			d.isHasPhase = true;
2064 		if (isSameFloat(cols[kImageType], 18)) {
2065 			//printWarning("Field map in Hz will be saved as the 'real' image.\n");
2066 			//isTypeWarning = true;
2067 			d.isRealIsPhaseMapHz = true;
2068 		} else if (((cols[kImageType] < 0.0) || (cols[kImageType] > 4.0)) && (!isTypeWarning)) {
2069 			printError("Unknown type %g: not magnitude[0], real[1], imaginary[2] or phase[3].\n", cols[kImageType]);
2070 			isTypeWarning = true;
2071 		}
2072 		if (cols[kDyn] > maxDyn)
2073 			maxDyn = (int)cols[kDyn];
2074 		if (cols[kDyn] < minDyn)
2075 			minDyn = (int)cols[kDyn];
2076 		if (cols[kDyn] < prevDyn)
2077 			dynNotAscending = true;
2078 		prevDyn = cols[kDyn];
2079 		if (cols[kDynTime] > maxDynTime)
2080 			maxDynTime = cols[kDynTime];
2081 		if (cols[kDynTime] < minDynTime)
2082 			minDynTime = cols[kDynTime];
2083 		if (cols[kEcho] > maxEcho)
2084 			maxEcho = cols[kEcho];
2085 		if (cols[kCardiac] > maxCardiac)
2086 			maxCardiac = cols[kCardiac];
2087 		if ((cols[kEcho] == 1) && (cols[kDyn] == 1) && (cols[kCardiac] == 1) && (cols[kGradientNumber] == 1)) {
2088 			if (cols[kSlice] == 1) {
2089 				d.patientPosition[1] = cols[kPositionRL];
2090 				d.patientPosition[2] = cols[kPositionAP];
2091 				d.patientPosition[3] = cols[kPositionFH];
2092 			}
2093 			patientPositionNumPhilips++;
2094 		}
2095 		if (true) { //for every slice
2096 			int slice = (int)cols[kSlice];
2097 			if (slice < minSlice)
2098 				minSlice = slice;
2099 			if (slice > maxSlice) {
2100 				maxSlice = slice;
2101 				d.patientPositionLast[1] = cols[kPositionRL];
2102 				d.patientPositionLast[2] = cols[kPositionAP];
2103 				d.patientPositionLast[3] = cols[kPositionFH];
2104 			}
2105 			int volStep = maxNumberOfDynamics;
2106 			int vol = ((int)cols[kDyn] - 1);
2107 #ifdef old
2108 			int gradDynVol = (int)cols[kGradientNumber] - 1;
2109 			if (gradDynVol < 0)
2110 				gradDynVol = 0; //old PAREC without cols[kGradientNumber]
2111 			vol = vol + (volStep * (gradDynVol));
2112 			if (vol < 0)
2113 				vol = 0;
2114 			volStep = volStep * maxNumberOfGradientOrients;
2115 			int bval = (int)cols[kbvalNumber];
2116 			if (bval > 2) //b=0 is 0, b=1000 is 1, b=2000 is 2 - b=0 does not have multiple directions
2117 				bval = bval - 1;
2118 			else
2119 				bval = 1;
2120 			//if (slice == 1) printMessage("bVal %d bVec %d isADC %d nbVal %d nGrad %d\n",(int) cols[kbvalNumber], (int)cols[kGradientNumber], isADC, maxNumberOfDiffusionValues, maxNumberOfGradientOrients);
2121 			vol = vol + (volStep * (bval - 1));
2122 			volStep = volStep * (maxNumberOfDiffusionValues - 1);
2123 			if (isADC)
2124 				vol = volStep + (bval - 1);
2125 #else
2126 			if (maxNumberOfDiffusionValues > 1) {
2127 				int grad = (int)cols[kGradientNumber] - 1;
2128 				if (grad < 0)
2129 					grad = 0; //old v4 does not have this tag
2130 				int bval = (int)cols[kbvalNumber] - 1;
2131 				if (bval < 0)
2132 					bval = 0; //old v4 does not have this tag
2133 				if (isADC)
2134 					vol = vol + (volStep * maxNumberOfDiffusionValues * maxNumberOfGradientOrients) + bval;
2135 				else
2136 					vol = vol + (volStep * grad) + (bval * maxNumberOfGradientOrients);
2137 
2138 				volStep = volStep * (maxNumberOfDiffusionValues + 1) * maxNumberOfGradientOrients;
2139 				//if (slice == 1) printMessage("vol %d step %d bVal %d bVec %d isADC %d nbVal %d nGrad %d\n", vol, volStep, (int) cols[kbvalNumber], (int)cols[kGradientNumber], isADC, maxNumberOfDiffusionValues, maxNumberOfGradientOrients);
2140 			}
2141 #endif
2142 			vol = vol + (volStep * ((int)cols[kEcho] - 1));
2143 			volStep = volStep * maxNumberOfEchoes;
2144 			vol = vol + (volStep * ((int)cols[kCardiac] - 1));
2145 			volStep = volStep * maxNumberOfCardiacPhases;
2146 			int ASL = (int)cols[kASL];
2147 			if (ASL < 1)
2148 				ASL = 1;
2149 			vol = vol + (volStep * (ASL - 1));
2150 			volStep = volStep * maxNumberOfLabels;
2151 			//if ((int)cols[kSequence] > 0)
2152 			int seq = (int)cols[kSequence];
2153 			if (seq1 < 0)
2154 				seq1 = seq;
2155 			if (seq > maxSeq)
2156 				maxSeq = seq;
2157 			if (seq != seq1) { //sequence varies within this PAR file
2158 				if (!isSequenceWarning) {
2159 					isSequenceWarning = true;
2160 					printWarning("'scanning sequence' column varies within a single file. This behavior is not described at the top of the header.\n");
2161 				}
2162 				vol = vol + (volStep * 1);
2163 				volStep = volStep * 2;
2164 			}
2165 			//if (slice == 1)  printMessage("%d\t%d\t%d\t%d\t%d\n", isADC,(int)cols[kbvalNumber], (int)cols[kGradientNumber], bval, vol);
2166 			if (vol > maxVol)
2167 				maxVol = vol;
2168 			bool isReal = (cols[kImageType] == 1);
2169 			bool isImaginary = (cols[kImageType] == 2);
2170 			bool isPhase = (cols[kImageType] == 3);
2171 			if (cols[kImageType] == 18) {
2172 				isReal = true;
2173 				d.isRealIsPhaseMapHz = true;
2174 			}
2175 			if (cols[kImageType] == 4) {
2176 				if (!isType4Warning) {
2177 					printWarning("Unknown image type (4). Be aware the 'phase' image is of an unknown type.\n");
2178 					isType4Warning = true;
2179 				}
2180 				isPhase = true; //2019
2181 			}
2182 			if ((cols[kImageType] != 18) && ((cols[kImageType] < 0.0) || (cols[kImageType] > 3.0))) {
2183 				if (!isType4Warning) {
2184 					printWarning("Unknown image type (%g). Be aware the 'phase' image is of an unknown type.\n", round(cols[kImageType]));
2185 					isType4Warning = true;
2186 				}
2187 				isReal = true; //<- this is not correct, kludge for bug in ROGERS_20180526_WIP_B0_NS_8_1.PAR
2188 			}
2189 			if (isReal)
2190 				vol += num3DExpected;
2191 			if (isImaginary)
2192 				vol += (2 * num3DExpected);
2193 			if (isPhase)
2194 				vol += (3 * num3DExpected);
2195 			if (vol >= kMaxDTI4D) {
2196 				printError("Use dicm2nii or increase kMaxDTI4D (currently %d)to be more than %d\n", kMaxDTI4D, kMaxImageType * num2DExpected);
2197 				printMessage("  slices*grad*bval*cardiac*echo*dynamic*mix*label = %d*%d*%d*%d*%d*%d*%d*%d\n",
2198 					d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues,
2199 					maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels);
2200 				free(cols);
2201 				return d;
2202 			}
2203 			// dti4D->S[vol].V[0] = cols[kbval];
2204 			//dti4D->gradDynVol[vol] = gradDynVol;
2205 			dti4D->TE[vol] = cols[kTEcho];
2206 			if (isSameFloatGE(cols[kTEcho], 0))
2207 				dti4D->TE[vol] = TE; //kludge for cols[kImageType]==18 where TE set as 0
2208 			else
2209 				TE = cols[kTEcho];
2210 			dti4D->triggerDelayTime[vol] = cols[kTriggerTime];
2211 			if (dti4D->TE[vol] < 0)
2212 				dti4D->TE[vol] = 0; //used to detect sparse volumes
2213 			//dti4D->intenIntercept[vol] = cols[kRI];
2214 			//dti4D->intenScale[vol] = cols[kRS];
2215 			//dti4D->intenScalePhilips[vol] = cols[kSS];
2216 			dti4D->isReal[vol] = isReal;
2217 			dti4D->isImaginary[vol] = isImaginary;
2218 			dti4D->isPhase[vol] = isPhase;
2219 			if ((maxNumberOfGradientOrients > 1) && (parVers > 40)) {
2220 				dti4D->S[vol].V[0] = cols[kbval];
2221 				dti4D->S[vol].V[1] = cols[kv1];
2222 				dti4D->S[vol].V[2] = cols[kv2];
2223 				dti4D->S[vol].V[3] = cols[kv3];
2224 				if ((vol + 1) > d.CSA.numDti)
2225 					d.CSA.numDti = vol + 1;
2226 			}
2227 			if (numSlice2D < kMaxDTI4D) { //issue 363: intensity can vary with each 2D slice of 4D volume
2228 				dti4D->intenIntercept[numSlice2D] = cols[kRI];
2229 				dti4D->intenScale[numSlice2D] = cols[kRS];
2230 				dti4D->intenScalePhilips[numSlice2D] = cols[kSS];
2231 			}
2232 			//if (slice == 1) printWarning("%d\n", (int)cols[kEcho]);
2233 			slice = slice + (vol * d.xyzDim[3]);
2234 			//offset images by type: mag+0,real+1, imag+2,phase+3
2235 			//if (cols[kImageType] != 0) //yikes - phase maps!
2236 			//	slice = slice + numExpected;
2237 			//printWarning("%d\t%d\n", slice -1, numSlice2D);
2238 			if ((slice >= 0) && (slice < kMaxSlice2D) && (numSlice2D < kMaxSlice2D) && (numSlice2D >= 0)) {
2239 				dti4D->sliceOrder[slice - 1] = numSlice2D;
2240 				//printMessage("%d\t%d\t%d\n", numSlice2D, slice, (int)cols[kSlice],(int)vol);
2241 			}
2242 			numSlice2D++;
2243 		}
2244 		//printMessage("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff))
2245 		p = fgets(buff, LINESZ, fp); //get next line
2246 	}
2247 	free(cols);
2248 	fclose(fp);
2249 	if ((parVers <= 0) || (numSlice2D < 1)) {
2250 		printError("Invalid PAR format header (unable to detect version or slices) %s\n", parname);
2251 		return d;
2252 	}
2253 	if (numSlice2D > kMaxSlice2D) { //check again after reading, as top portion of header does not report image types or isotropics
2254 		printError("Increase kMaxSlice2D from %d to at least %d (or use dicm2nii).\n", kMaxSlice2D, numSlice2D);
2255 		return d;
2256 	}
2257 	if (numSlice2D > kMaxDTI4D) { //since issue460, kMaxSlice2D == kMaxSlice4D, so we should never get here
2258 		printError("Increase kMaxDTI4D from %d to at least %d (or use dicm2nii).\n", kMaxDTI4D, numSlice2D);
2259 		return d;
2260 	}
2261 	d.manufacturer = kMANUFACTURER_PHILIPS;
2262 	d.isValid = true;
2263 	d.isSigned = true;
2264 	//remove unused volumes - this will happen if unless we have all 4 image types: real, imag, mag, phase
2265 	maxVol = 0;
2266 	for (int i = 0; i < kMaxDTI4D; i++) {
2267 		if (dti4D->TE[i] > -1.0) {
2268 			dti4D->TE[maxVol] = dti4D->TE[i];
2269 			dti4D->triggerDelayTime[maxVol] = dti4D->triggerDelayTime[i];
2270 			//dti4D->intenIntercept[maxVol] = dti4D->intenIntercept[i];
2271 			//dti4D->intenScale[maxVol] = dti4D->intenScale[i];
2272 			//dti4D->intenScalePhilips[maxVol] = dti4D->intenScalePhilips[i];
2273 			dti4D->isReal[maxVol] = dti4D->isReal[i];
2274 			dti4D->isImaginary[maxVol] = dti4D->isImaginary[i];
2275 			dti4D->isPhase[maxVol] = dti4D->isPhase[i];
2276 			dti4D->S[maxVol].V[0] = dti4D->S[i].V[0];
2277 			dti4D->S[maxVol].V[1] = dti4D->S[i].V[1];
2278 			dti4D->S[maxVol].V[2] = dti4D->S[i].V[2];
2279 			dti4D->S[maxVol].V[3] = dti4D->S[i].V[3];
2280 			maxVol = maxVol + 1;
2281 		}
2282 	}
2283 	if (d.CSA.numDti > 0)
2284 		d.CSA.numDti = maxVol; //e.g. gradient 2 can skip B=0 but include isotropic
2285 	//remove unused slices - this will happen if unless we have all 4 image types: real, imag, mag, phase
2286 	int slice = 0;
2287 	for (int i = 0; i < kMaxSlice2D; i++) {
2288 		if (dti4D->sliceOrder[i] > -1) { //this slice was populated
2289 			dti4D->sliceOrder[slice] = dti4D->sliceOrder[i];
2290 			slice = slice + 1;
2291 		}
2292 	}
2293 	if (slice != numSlice2D) {
2294 		printError("Catastrophic error: found %d but expected %d slices. %s\n", slice, numSlice2D, parname);
2295 		printMessage("  slices*grad*bval*cardiac*echo*dynamic*mix*labels = %d*%d*%d*%d*%d*%d*%d*%d\n",
2296 				d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues,
2297 				maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels);
2298 		d.isValid = false;
2299 	}
2300 	for (int i = 0; i < numSlice2D; i++) { //issue363
2301 		if (dti4D->intenIntercept[i] != dti4D->intenIntercept[0])
2302 			d.isScaleVariesEnh = true;
2303 		if (dti4D->intenScale[i] != dti4D->intenScale[0])
2304 			d.isScaleVariesEnh = true;
2305 		if (dti4D->intenScalePhilips[i] != dti4D->intenScalePhilips[0])
2306 			d.isScaleVariesEnh = true;
2307 		//printf("%g --> %g\n", dti4D->intenIntercept[i], dti4D->intenScale[i]);
2308 	}
2309 	if (d.isScaleVariesEnh) { //juggle to sorted order, required for subsequent rescaling
2310 		printWarning("PAR/REC intensity scaling varies between slices (please validate output).\n");
2311 		TDTI4D tmp;
2312 		for (int i = 0; i < numSlice2D; i++) { //issue363
2313 			tmp.intenIntercept[i] = dti4D->intenIntercept[i];
2314 			tmp.intenScale[i] = dti4D->intenScale[i];
2315 			tmp.intenScalePhilips[i] = dti4D->intenScalePhilips[i];
2316 		}
2317 		for (int i = 0; i < numSlice2D; i++) {
2318 			int j = dti4D->sliceOrder[i];
2319 			dti4D->intenIntercept[i] = tmp.intenIntercept[j];
2320 			dti4D->intenScale[i] = tmp.intenScale[j];
2321 			dti4D->intenScalePhilips[i] = tmp.intenScalePhilips[j];
2322 		}
2323 	}
2324 	d.isScaleOrTEVaries = true;
2325 	if (numSlice2D > kMaxSlice2D) {
2326 		printError("Overloaded slice re-ordering. Number of slices (%d) exceeds kMaxSlice2D (%d)\n", numSlice2D, kMaxSlice2D);
2327 		dti4D->sliceOrder[0] = -1;
2328 		dti4D->intenScale[0] = 0.0;
2329 	}
2330 	if ((maxSlice - minSlice + 1) != d.xyzDim[3]) {
2331 		int numSlice = (maxSlice - minSlice) + 1;
2332 		printWarning("Expected %d slices, but found %d (%d..%d). %s\n", d.xyzDim[3], numSlice, minSlice, maxSlice, parname);
2333 		if (numSlice <= 0)
2334 			d.isValid = false;
2335 		d.xyzDim[3] = numSlice;
2336 		num2DExpected = d.xyzDim[3] * num3DExpected;
2337 	}
2338 	if ((maxBValue <= 0.0f) && (maxDyn > minDyn) && (maxDynTime > minDynTime)) { //use max vs min Dyn instead of && (d.CSA.numDti > 1)
2339 		int numDyn = (maxDyn - minDyn) + 1;
2340 		if (numDyn != maxNumberOfDynamics) {
2341 			printWarning("Expected %d dynamics, but found %d (%d..%d).\n", maxNumberOfDynamics, numDyn, minDyn, maxDyn);
2342 			maxNumberOfDynamics = numDyn;
2343 			num3DExpected = maxNumberOfGradientOrients * maxNumberOfDiffusionValues * maxNumberOfLabels * maxNumberOfCardiacPhases * maxNumberOfEchoes * maxNumberOfDynamics * maxNumberOfMixes;
2344 			num2DExpected = d.xyzDim[3] * num3DExpected;
2345 		}
2346 		float TRms = 1000.0f * (maxDynTime - minDynTime) / (float)(numDyn - 1); //-1 for fence post
2347 		if (fabs(TRms - d.TR) > 0.005f)
2348 			printWarning("Reported TR=%gms, measured TR=%gms (prospect. motion corr.?)\n", d.TR, TRms);
2349 		d.TR = TRms;
2350 	}
2351 	if ((isTypeWarning) && ((numSlice2D % num2DExpected) != 0) && ((numSlice2D % d.xyzDim[3]) == 0)) {
2352 		num2DExpected = numSlice2D;
2353 	}
2354 	if (((numSlice2D % num2DExpected) != 0) && ((numSlice2D % d.xyzDim[3]) == 0)) {
2355 		num2DExpected = d.xyzDim[3] * (int)(numSlice2D / d.xyzDim[3]);
2356 		if (!ADCwarning)
2357 			printWarning("More volumes than described in header (ADC or isotropic?)\n");
2358 	}
2359 	if ((numSlice2D % num2DExpected) != 0) {
2360 		printMessage("Found %d slices, but expected divisible by %d: slices*grad*bval*cardiac*echo*dynamic*mix*labels = %d*%d*%d*%d*%d*%d*%d*%d %s\n", numSlice2D, num2DExpected,
2361 			d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues,
2362 			maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels, parname);
2363 		d.isValid = false;
2364 	}
2365 	if (dynNotAscending) {
2366 		printWarning("PAR file volumes not saved in ascending temporal order (please check re-ordering)\n");
2367 	}
2368 	if ((slice % d.xyzDim[3]) != 0) {
2369 		printError("Total number of slices (%d) not divisible by slices per 3D volume (%d) [acquisition aborted]. Try dicm2nii or R2AGUI: %s\n", slice, d.xyzDim[3], parname);
2370 		d.isValid = false;
2371 		return d;
2372 	}
2373 	d.xyzDim[4] = slice / d.xyzDim[3];
2374 	d.locationsInAcquisition = d.xyzDim[3];
2375 	if (ADCwarning)
2376 		printWarning("PAR/REC dataset includes derived (isotropic, ADC, etc) map(s) that could disrupt analysis. Please remove volume and ensure vectors are reported correctly\n");
2377 	if (isIntenScaleVaries)
2378 		printWarning("Intensity slope/intercept varies between slices! [check resulting images]\n");
2379 	if ((isVerbose) && (d.isValid)) {
2380 		printMessage("  slices*grad*bval*cardiac*echo*dynamic*mix*labels = %d*%d*%d*%d*%d*%d*%d*%d\n",
2381 			d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues,
2382 			maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels);
2383 	}
2384 	if ((d.xyzDim[3] > 1) && (minSlice == 1) && (maxSlice > minSlice)) { //issue 273
2385 		float dx[4];
2386 		dx[1] = (d.patientPosition[1] - d.patientPositionLast[1]);
2387 		dx[2] = (d.patientPosition[2] - d.patientPositionLast[2]);
2388 		dx[3] = (d.patientPosition[3] - d.patientPositionLast[3]);
2389 		//compute error using 3D pythagorean theorm
2390 		float sliceMM = sqrt(pow(dx[1], 2) + pow(dx[2], 2) + pow(dx[3], 2));
2391 		sliceMM = sliceMM / (maxSlice - minSlice);
2392 		if (!(isSameFloatGE(sliceMM, d.xyzMM[3]))) {
2393 			//if (d.xyzMM[3] > 0.0)
2394 			printWarning("Distance between slices reported by slice gap+thick does not match estimate from slice positions (issue 273).\n");
2395 			d.xyzMM[3] = sliceMM;
2396 		}
2397 	} //issue 273
2398 	printMessage("Done reading PAR header version %.1f, with %d slices\n", (float)parVers / 10, numSlice2D);
2399 	//see Xiangrui Li 's dicm2nii (also BSD license)
2400 	// http://www.mathworks.com/matlabcentral/fileexchange/42997-dicom-to-nifti-converter
2401 	// Rotation order and signs are figured out by trial and error, not 100% sure
2402 	float d2r = (float)(M_PI / 180.0);
2403 	vec3 ca = setVec3(cos(d.angulation[1] * d2r), cos(d.angulation[2] * d2r), cos(d.angulation[3] * d2r));
2404 	vec3 sa = setVec3(sin(d.angulation[1] * d2r), sin(d.angulation[2] * d2r), sin(d.angulation[3] * d2r));
2405 	mat33 rx, ry, rz;
2406 	LOAD_MAT33(rx, 1.0f, 0.0f, 0.0f, 0.0f, ca.v[0], -sa.v[0], 0.0f, sa.v[0], ca.v[0]);
2407 	LOAD_MAT33(ry, ca.v[1], 0.0f, sa.v[1], 0.0f, 1.0f, 0.0f, -sa.v[1], 0.0f, ca.v[1]);
2408 	LOAD_MAT33(rz, ca.v[2], -sa.v[2], 0.0f, sa.v[2], ca.v[2], 0.0f, 0.0f, 0.0f, 1.0f);
2409 	mat33 R = nifti_mat33_mul(rx, ry);
2410 	R = nifti_mat33_mul(R, rz);
2411 	ivec3 ixyz = setiVec3(1, 2, 3);
2412 	if (d.sliceOrient == kSliceOrientSag) {
2413 		ixyz = setiVec3(2, 3, 1);
2414 		for (int r = 0; r < 3; r++)
2415 			for (int c = 0; c < 3; c++)
2416 				if (c != 1)
2417 					R.m[r][c] = -R.m[r][c]; //invert first and final columns
2418 	} else if (d.sliceOrient == kSliceOrientCor) {
2419 		ixyz = setiVec3(1, 3, 2);
2420 		for (int r = 0; r < 3; r++)
2421 			R.m[r][2] = -R.m[r][2]; //invert rows of final column
2422 	}
2423 	R = nifti_mat33_reorder_cols(R, ixyz); //dicom rotation matrix
2424 	d.orient[1] = R.m[0][0];
2425 	d.orient[2] = R.m[1][0];
2426 	d.orient[3] = R.m[2][0];
2427 	d.orient[4] = R.m[0][1];
2428 	d.orient[5] = R.m[1][1];
2429 	d.orient[6] = R.m[2][1];
2430 	mat33 diag;
2431 	LOAD_MAT33(diag, d.xyzMM[1], 0.0f, 0.0f, 0.0f, d.xyzMM[2], 0.0f, 0.0f, 0.0f, d.xyzMM[3]);
2432 	R = nifti_mat33_mul(R, diag);
2433 	mat44 R44;
2434 	LOAD_MAT44(R44, R.m[0][0], R.m[0][1], R.m[0][2], d.stackOffcentre[1],
2435 		R.m[1][0], R.m[1][1], R.m[1][2], d.stackOffcentre[2],
2436 		R.m[2][0], R.m[2][1], R.m[2][2], d.stackOffcentre[3]);
2437 	vec3 x;
2438 	if (parVers > 40) //guess
2439 		x = setVec3(((float)d.xyzDim[1] - 1) / 2, ((float)d.xyzDim[2] - 1) / 2, ((float)d.xyzDim[3] - 1) / 2);
2440 	else
2441 		x = setVec3((float)d.xyzDim[1] / 2, (float)d.xyzDim[2] / 2, ((float)d.xyzDim[3] - 1) / 2);
2442 	mat44 eye;
2443 	LOAD_MAT44(eye, 1.0f, 0.0f, 0.0f, x.v[0],
2444 		0.0f, 1.0f, 0.0f, x.v[1],
2445 		0.0f, 0.0f, 1.0f, x.v[2]);
2446 	eye = nifti_mat44_inverse(eye); //we wish to compute R/eye, so compute invEye and calculate R*invEye
2447 	R44 = nifti_mat44_mul(R44, eye);
2448 	vec4 y;
2449 	y.v[0] = 0.0f;
2450 	y.v[1] = 0.0f;
2451 	y.v[2] = (float)d.xyzDim[3] - 1.0f;
2452 	y.v[3] = 1.0f;
2453 	y = nifti_vect44mat44_mul(y, R44);
2454 	int iOri = 2; //for axial, slices are 3rd dimenson (indexed from 0) (k)
2455 	if (d.sliceOrient == kSliceOrientSag)
2456 		iOri = 0; //for sagittal, slices are 1st dimension (i)
2457 	if (d.sliceOrient == kSliceOrientCor)
2458 		iOri = 1; //for coronal, slices are 2nd dimension (j)
2459 	if (d.xyzDim[3] > 1) { //detect and fix Philips Bug
2460 		//Est: assuming "image offcentre (ap,fh,rl in mm )" is correct
2461 		float stackOffcentreEst[4];
2462 		stackOffcentreEst[1] = (d.patientPosition[1] + d.patientPositionLast[1]) * 0.5;
2463 		stackOffcentreEst[2] = (d.patientPosition[2] + d.patientPositionLast[2]) * 0.5;
2464 		stackOffcentreEst[3] = (d.patientPosition[3] + d.patientPositionLast[3]) * 0.5;
2465 		//compute error using 3D pythagorean theorm
2466 		stackOffcentreEst[0] = sqrt(pow(stackOffcentreEst[1] - d.stackOffcentre[1], 2) + pow(stackOffcentreEst[2] - d.stackOffcentre[2], 2) + pow(stackOffcentreEst[3] - d.stackOffcentre[3], 2));
2467 		//Est: assuming "image offcentre (ap,fh,rl in mm )" is stored in order rl,ap,fh
2468 		float stackOffcentreRev[4];
2469 		stackOffcentreRev[1] = (d.patientPosition[2] + d.patientPositionLast[2]) * 0.5;
2470 		stackOffcentreRev[2] = (d.patientPosition[3] + d.patientPositionLast[3]) * 0.5;
2471 		stackOffcentreRev[3] = (d.patientPosition[1] + d.patientPositionLast[1]) * 0.5;
2472 		//compute error using 3D pythagorean theorm
2473 		stackOffcentreRev[0] = sqrt(pow(stackOffcentreRev[1] - d.stackOffcentre[1], 2) + pow(stackOffcentreRev[2] - d.stackOffcentre[2], 2) + pow(stackOffcentreRev[3] - d.stackOffcentre[3], 2));
2474 		//detect, report and fix error
2475 		if ((stackOffcentreEst[0] > 1.0) && (stackOffcentreRev[0] < stackOffcentreEst[0])) {
2476 			//error detected: the ">1.0" handles the low precision of the "Off Centre" values
2477 			printMessage("Order of 'image offcentre (ap,fh,rl in mm )' appears incorrect (assuming rl,ap,fh)\n");
2478 			printMessage(" err[ap,fh,rl]= %g (%g %g %g) \n", stackOffcentreEst[0], stackOffcentreEst[1], stackOffcentreEst[2], stackOffcentreEst[3]);
2479 			printMessage(" err[rl,ap,fh]= %g (%g %g %g) \n", stackOffcentreRev[0], stackOffcentreRev[1], stackOffcentreRev[2], stackOffcentreRev[3]);
2480 			printMessage(" orient\t%d\tOffCentre 1st->mid->nth\t%g\t%g\t%g\t->\t%g\t%g\t%g\t->\t%g\t%g\t%g\t=\t%g\t%s\n", iOri,
2481 				d.patientPosition[1], d.patientPosition[2], d.patientPosition[3],
2482 				d.stackOffcentre[1], d.stackOffcentre[2], d.stackOffcentre[3],
2483 				d.patientPositionLast[1], d.patientPositionLast[2], d.patientPositionLast[3], (d.patientPosition[iOri + 1] - d.patientPositionLast[iOri + 1]), parname);
2484 			//correct patientPosition
2485 			for (int i = 1; i < 4; i++)
2486 				stackOffcentreRev[i] = d.patientPosition[i];
2487 			d.patientPosition[1] = stackOffcentreRev[2];
2488 			d.patientPosition[2] = stackOffcentreRev[3];
2489 			d.patientPosition[3] = stackOffcentreRev[1];
2490 			//correct patientPositionLast
2491 			for (int i = 1; i < 4; i++)
2492 				stackOffcentreRev[i] = d.patientPositionLast[i];
2493 			d.patientPositionLast[1] = stackOffcentreRev[2];
2494 			d.patientPositionLast[2] = stackOffcentreRev[3];
2495 			d.patientPositionLast[3] = stackOffcentreRev[1];
2496 		} //if bug: report and fix
2497 	} //if 3D data
2498 	bool flip = false;
2499 	//assume head first supine
2500 	if ((iOri == 0) && (((d.patientPosition[iOri + 1] - d.patientPositionLast[iOri + 1]) > 0)))
2501 		flip = true; //6/2018 : TODO, not sure if this is >= or >
2502 	if ((iOri == 1) && (((d.patientPosition[iOri + 1] - d.patientPositionLast[iOri + 1]) <= 0)))
2503 		flip = true; //<= not <, leslie_dti_6_1.PAR
2504 	if ((iOri == 2) && (((d.patientPosition[iOri + 1] - d.patientPositionLast[iOri + 1]) <= 0)))
2505 		flip = true; //<= not <, see leslie_dti_3_1.PAR
2506 	if (flip) {
2507 		//if ((d.patientPosition[iOri+1] - d.patientPositionLast[iOri+1]) < 0) {
2508 		//if (( (y.v[iOri]-R44.m[iOri][3])>0 ) == ( (y.v[iOri]-d.stackOffcentre[iOri+1])>0 ) ) {
2509 		d.patientPosition[1] = R44.m[0][3];
2510 		d.patientPosition[2] = R44.m[1][3];
2511 		d.patientPosition[3] = R44.m[2][3];
2512 		d.patientPositionLast[1] = y.v[0];
2513 		d.patientPositionLast[2] = y.v[1];
2514 		d.patientPositionLast[3] = y.v[2];
2515 		//printWarning(" Flipping slice order: please verify %s\n", parname);
2516 	} else {
2517 		//printWarning(" NOT Flipping slice order: please verify %s\n", parname);
2518 		d.patientPosition[1] = y.v[0];
2519 		d.patientPosition[2] = y.v[1];
2520 		d.patientPosition[3] = y.v[2];
2521 		d.patientPositionLast[1] = R44.m[0][3];
2522 		d.patientPositionLast[2] = R44.m[1][3];
2523 		d.patientPositionLast[3] = R44.m[2][3];
2524 	}
2525 	//finish up
2526 	changeExt(parname, "REC");
2527 #ifndef _MSC_VER //Linux is case sensitive, #include <unistd.h>
2528 	if (access(parname, F_OK) != 0)
2529 		changeExt(parname, "rec");
2530 #endif
2531 	d.locationsInAcquisition = d.xyzDim[3];
2532 	d.imageStart = 0;
2533 	if (d.CSA.numDti >= kMaxDTI4D) {
2534 		printError("Unable to convert DTI [increase kMaxDTI4D] found %d directions\n", d.CSA.numDti);
2535 		printMessage("  slices*grad*bval*cardiac*echo*dynamic*mix*label = %d*%d*%d*%d*%d*%d*%d*%d\n", d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels);
2536 		d.CSA.numDti = 0;
2537 	};
2538 	//check if dimensions vary
2539 	if (maxVol > 0) { //maxVol indexed from 0
2540 		for (int i = 1; i <= maxVol; i++) {
2541 			//if (dti4D->gradDynVol[i] > d.maxGradDynVol) d.maxGradDynVol = dti4D->gradDynVol[i];
2542 			//issue363 slope/intercept can vary for each 2D slice, not only between 3D volumes in a 4D time series
2543 			//if (dti4D->intenIntercept[i] != dti4D->intenIntercept[0]) d.isScaleOrTEVaries = true;
2544 			//if (dti4D->intenScale[i] != dti4D->intenScale[0]) d.isScaleOrTEVaries = true;
2545 			//if (dti4D->intenScalePhilips[i] != dti4D->intenScalePhilips[0]) d.isScaleOrTEVaries = true;
2546 			if (dti4D->isPhase[i] != dti4D->isPhase[0])
2547 				d.isScaleOrTEVaries = true;
2548 			if (dti4D->isReal[i] != dti4D->isReal[0])
2549 				d.isScaleOrTEVaries = true;
2550 			if (dti4D->isImaginary[i] != dti4D->isImaginary[0])
2551 				d.isScaleOrTEVaries = true;
2552 			if (dti4D->triggerDelayTime[i] != dti4D->triggerDelayTime[0])
2553 				d.isScaleOrTEVaries = true;
2554 		}
2555 		//if (d.isScaleOrTEVaries)
2556 		//	printWarning("Varying dimensions (echoes, phase maps, intensity scaling) will require volumes to be saved separately (hint: you may prefer dicm2nii output)\n");
2557 	}
2558 	//if (d.CSA.numDti > 1)
2559 	//	for (int i = 0; i < d.CSA.numDti; i++)
2560 	//		printMessage("%d\tb=\t%g\tv=\t%g\t%g\t%g\n",i,dti4D->S[i].V[0],dti4D->S[i].V[1],dti4D->S[i].V[2],dti4D->S[i].V[3]);
2561 	//check DTI makes sense
2562 	if (d.CSA.numDti > 1) {
2563 		bool v1varies = false;
2564 		bool v2varies = false;
2565 		bool v3varies = false;
2566 		for (int i = 1; i < d.CSA.numDti; i++) {
2567 			if (dti4D->S[0].V[1] != dti4D->S[i].V[1])
2568 				v1varies = true;
2569 			if (dti4D->S[0].V[2] != dti4D->S[i].V[2])
2570 				v2varies = true;
2571 			if (dti4D->S[0].V[3] != dti4D->S[i].V[3])
2572 				v3varies = true;
2573 		}
2574 		if ((!v1varies) || (!v2varies) || (!v3varies))
2575 			printError("Bizarre b-vectors %s\n", parname);
2576 	}
2577 	if ((maxEcho > 1) || (maxCardiac > 1))
2578 		printWarning("Multiple Echo (%d) or Cardiac (%d). Carefully inspect output\n", maxEcho, maxCardiac);
2579 	if ((maxEcho > 1) || (maxCardiac > 1))
2580 		d.isScaleOrTEVaries = true;
2581 	return d;
2582 } //nii_readParRec()
2583 
nii_SliceBytes(struct nifti_1_header hdr)2584 size_t nii_SliceBytes(struct nifti_1_header hdr) {
2585 	//size of 2D slice
2586 	size_t imgsz = hdr.bitpix / 8;
2587 	for (int i = 1; i < 3; i++)
2588 		if (hdr.dim[i] > 1)
2589 			imgsz = imgsz * hdr.dim[i];
2590 	return imgsz;
2591 } //nii_SliceBytes()
2592 
nii_ImgBytes(struct nifti_1_header hdr)2593 size_t nii_ImgBytes(struct nifti_1_header hdr) {
2594 	size_t imgsz = hdr.bitpix / 8;
2595 	for (int i = 1; i < 8; i++)
2596 		if (hdr.dim[i] > 1)
2597 			imgsz = imgsz * hdr.dim[i];
2598 	return imgsz;
2599 } //nii_ImgBytes()
2600 
2601 //unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, int nMosaicSlices, int ProtocolSliceNumber1) {
nii_demosaic(unsigned char * inImg,struct nifti_1_header * hdr,int nMosaicSlices,bool isUIH)2602 unsigned char *nii_demosaic(unsigned char *inImg, struct nifti_1_header *hdr, int nMosaicSlices, bool isUIH) {
2603 	//demosaic http://nipy.org/nibabel/dicom/dicom_mosaic.html
2604 	if (nMosaicSlices < 2)
2605 		return inImg;
2606 	//Byte inImg[ [img length] ];
2607 	//[img getBytes:&inImg length:[img length]];
2608 	int nCol = (int)ceil(sqrt((double)nMosaicSlices));
2609 	int nRow = nCol;
2610 	//n.b. Siemens store 20 images as 5x5 grid, UIH as 5rows, 4 Col https://github.com/rordenlab/dcm2niix/issues/225
2611 	if (isUIH)
2612 		nRow = ceil((float)nMosaicSlices / (float)nCol);
2613 	//printf("%d = %dx%d\n", nMosaicSlices, nCol, nRow);
2614 	int colBytes = hdr->dim[1] / nCol * hdr->bitpix / 8;
2615 	int lineBytes = hdr->dim[1] * hdr->bitpix / 8;
2616 	int rowBytes = hdr->dim[1] * hdr->dim[2] / nRow * hdr->bitpix / 8;
2617 	int col = 0;
2618 	int row = 0;
2619 	int lOutPos = 0;
2620 	hdr->dim[1] = hdr->dim[1] / nCol;
2621 	hdr->dim[2] = hdr->dim[2] / nRow;
2622 	hdr->dim[3] = nMosaicSlices;
2623 	size_t imgsz = nii_ImgBytes(*hdr);
2624 	unsigned char *outImg = (unsigned char *)malloc(imgsz);
2625 	for (int m = 1; m <= nMosaicSlices; m++) {
2626 		int lPos = (row * rowBytes) + (col * colBytes);
2627 		for (int y = 0; y < hdr->dim[2]; y++) {
2628 			memcpy(&outImg[lOutPos], &inImg[lPos], colBytes); // dest, src, bytes
2629 			lPos += lineBytes;
2630 			lOutPos += colBytes;
2631 		}
2632 		col++;
2633 		if (col >= nCol) {
2634 			row++;
2635 			col = 0;
2636 		} //start new column
2637 	} //for m = each mosaic slice
2638 	free(inImg);
2639 	return outImg;
2640 } // nii_demosaic()
2641 
nii_flipImgY(unsigned char * bImg,struct nifti_1_header * hdr)2642 unsigned char *nii_flipImgY(unsigned char *bImg, struct nifti_1_header *hdr) {
2643 	//DICOM row order opposite from NIfTI
2644 	int dim3to7 = 1;
2645 	for (int i = 3; i < 8; i++)
2646 		if (hdr->dim[i] > 1)
2647 			dim3to7 = dim3to7 * hdr->dim[i];
2648 	size_t lineBytes = hdr->dim[1] * hdr->bitpix / 8;
2649 	if ((hdr->datatype == DT_RGB24) && (hdr->bitpix == 24) && (hdr->intent_code == NIFTI_INTENT_NONE)) {
2650 		//we use the intent code to indicate planar vs triplet...
2651 		lineBytes = hdr->dim[1];
2652 		dim3to7 = dim3to7 * 3;
2653 	} //rgb data saved planar (RRR..RGGGG..GBBB..B
2654 	unsigned char *line = (unsigned char *)malloc(sizeof(unsigned char) * (lineBytes));
2655 	size_t sliceBytes = hdr->dim[2] * lineBytes;
2656 	int halfY = hdr->dim[2] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns
2657 	for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice
2658 		size_t slBottom = (size_t)sl * sliceBytes;
2659 		size_t slTop = (((size_t)sl + 1) * sliceBytes) - lineBytes;
2660 		for (int y = 0; y < halfY; y++) {
2661 			//swap order of lines
2662 			memcpy(line, &bImg[slBottom], lineBytes); //memcpy(&line, &bImg[slBottom], lineBytes);
2663 			memcpy(&bImg[slBottom], &bImg[slTop], lineBytes);
2664 			memcpy(&bImg[slTop], line, lineBytes); //tpx memcpy(&bImg[slTop], &line, lineBytes);
2665 			slTop -= lineBytes;
2666 			slBottom += lineBytes;
2667 		} //for y
2668 	} //for each slice
2669 	free(line);
2670 	return bImg;
2671 } // nii_flipImgY()
2672 
nii_flipImgZ(unsigned char * bImg,struct nifti_1_header * hdr)2673 unsigned char *nii_flipImgZ(unsigned char *bImg, struct nifti_1_header *hdr) {
2674 	//DICOM row order opposite from NIfTI
2675 	int halfZ = hdr->dim[3] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns
2676 	if (halfZ < 1)
2677 		return bImg;
2678 	int dim4to7 = 1;
2679 	for (int i = 4; i < 8; i++)
2680 		if (hdr->dim[i] > 1)
2681 			dim4to7 = dim4to7 * hdr->dim[i];
2682 	size_t sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix / 8;
2683 	size_t volBytes = sliceBytes * hdr->dim[3];
2684 	unsigned char *slice = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes));
2685 	for (int vol = 0; vol < dim4to7; vol++) { //for each 2D slice
2686 		size_t slBottom = vol * volBytes;
2687 		size_t slTop = ((vol + 1) * volBytes) - sliceBytes;
2688 		for (int z = 0; z < halfZ; z++) {
2689 			//swap order of lines
2690 			memcpy(slice, &bImg[slBottom], sliceBytes); //TPX memcpy(&slice, &bImg[slBottom], sliceBytes);
2691 			memcpy(&bImg[slBottom], &bImg[slTop], sliceBytes);
2692 			memcpy(&bImg[slTop], slice, sliceBytes); //TPX
2693 			slTop -= sliceBytes;
2694 			slBottom += sliceBytes;
2695 		} //for Z
2696 	} //for each volume
2697 	free(slice);
2698 	return bImg;
2699 } // nii_flipImgZ()
2700 
nii_flipZ(unsigned char * bImg,struct nifti_1_header * h)2701 unsigned char *nii_flipZ(unsigned char *bImg, struct nifti_1_header *h) {
2702 	//flip slice order
2703 	if (h->dim[3] < 2)
2704 		return bImg;
2705 	mat33 s;
2706 	mat44 Q44;
2707 	LOAD_MAT33(s, h->srow_x[0], h->srow_x[1], h->srow_x[2], h->srow_y[0], h->srow_y[1], h->srow_y[2],
2708 		h->srow_z[0], h->srow_z[1], h->srow_z[2]);
2709 	LOAD_MAT44(Q44, h->srow_x[0], h->srow_x[1], h->srow_x[2], h->srow_x[3],
2710 		h->srow_y[0], h->srow_y[1], h->srow_y[2], h->srow_y[3],
2711 		h->srow_z[0], h->srow_z[1], h->srow_z[2], h->srow_z[3]);
2712 	vec4 v = setVec4(0.0f, 0.0f, (float)h->dim[3] - 1.0f);
2713 	v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin
2714 	mat33 mFlipZ;
2715 	LOAD_MAT33(mFlipZ, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f);
2716 	s = nifti_mat33_mul(s, mFlipZ);
2717 	LOAD_MAT44(Q44, s.m[0][0], s.m[0][1], s.m[0][2], v.v[0],
2718 		s.m[1][0], s.m[1][1], s.m[1][2], v.v[1],
2719 		s.m[2][0], s.m[2][1], s.m[2][2], v.v[2]);
2720 	//printMessage(" ----------> %f %f %f\n",v.v[0],v.v[1],v.v[2]);
2721 	setQSForm(h, Q44, true);
2722 	//printMessage("nii_flipImgY dims %dx%dx%d %d \n",h->dim[1],h->dim[2], dim3to7,h->bitpix/8);
2723 	return nii_flipImgZ(bImg, h);
2724 } // nii_flipZ()
2725 
nii_flipY(unsigned char * bImg,struct nifti_1_header * h)2726 unsigned char *nii_flipY(unsigned char *bImg, struct nifti_1_header *h) {
2727 	mat33 s;
2728 	mat44 Q44;
2729 	LOAD_MAT33(s, h->srow_x[0], h->srow_x[1], h->srow_x[2], h->srow_y[0], h->srow_y[1], h->srow_y[2],
2730 		h->srow_z[0], h->srow_z[1], h->srow_z[2]);
2731 	LOAD_MAT44(Q44, h->srow_x[0], h->srow_x[1], h->srow_x[2], h->srow_x[3],
2732 		h->srow_y[0], h->srow_y[1], h->srow_y[2], h->srow_y[3],
2733 		h->srow_z[0], h->srow_z[1], h->srow_z[2], h->srow_z[3]);
2734 	vec4 v = setVec4(0, (float)h->dim[2] - 1, 0);
2735 	v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin
2736 	mat33 mFlipY;
2737 	LOAD_MAT33(mFlipY, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
2738 	s = nifti_mat33_mul(s, mFlipY);
2739 	LOAD_MAT44(Q44, s.m[0][0], s.m[0][1], s.m[0][2], v.v[0],
2740 		s.m[1][0], s.m[1][1], s.m[1][2], v.v[1],
2741 		s.m[2][0], s.m[2][1], s.m[2][2], v.v[2]);
2742 	setQSForm(h, Q44, true);
2743 	//printMessage("nii_flipImgY dims %dx%d %d \n",h->dim[1],h->dim[2], h->bitpix/8);
2744 	return nii_flipImgY(bImg, h);
2745 } // nii_flipY()
2746 
conv12bit16bit(unsigned char * img,struct nifti_1_header hdr)2747 void conv12bit16bit(unsigned char *img, struct nifti_1_header hdr) {
2748 	//convert 12-bit allocated data to 16-bit
2749 	// works for MR-MONO2-12-angio-an1 from http://www.barre.nom.fr/medical/samples/
2750 	// looks wrong: this sample toggles between big and little endian stores
2751 	printWarning("Support for images that allocate 12 bits is experimental\n");
2752 	int nVox = (int)nii_ImgBytes(hdr) / (hdr.bitpix / 8);
2753 	for (int i = (nVox - 1); i >= 0; i--) {
2754 		int i16 = i * 2;
2755 		int i12 = floor(i * 1.5);
2756 		uint16_t val;
2757 		if ((i % 2) != 1) {
2758 			val = img[i12 + 1] + (img[i12 + 0] << 8);
2759 			val = val >> 4;
2760 		} else {
2761 			val = img[i12 + 0] + (img[i12 + 1] << 8);
2762 		}
2763 		img[i16 + 0] = val & 0xFF;
2764 		img[i16 + 1] = (val >> 8) & 0xFF;
2765 	}
2766 } //conv12bit16bit()
2767 
nii_loadImgCore(char * imgname,struct nifti_1_header hdr,int bitsAllocated,int imageStart32)2768 unsigned char *nii_loadImgCore(char *imgname, struct nifti_1_header hdr, int bitsAllocated, int imageStart32) {
2769 	size_t imgsz = nii_ImgBytes(hdr);
2770 	size_t imgszRead = imgsz;
2771 	size_t imageStart = imageStart32;
2772 	if (bitsAllocated == 12)
2773 		imgszRead = round(imgsz * 0.75);
2774 	FILE *file = fopen(imgname, "rb");
2775 	if (!file) {
2776 		printError("Unable to open '%s'\n", imgname);
2777 		return NULL;
2778 	}
2779 	fseek(file, 0, SEEK_END);
2780 	long fileLen = ftell(file);
2781 	if (fileLen < (imgszRead + imageStart)) {
2782 		//note hdr.vox_offset is a float: issue507
2783 		//https://www.nitrc.org/forum/message.php?msg_id=27155
2784 		printMessage("FileSize < (ImageSize+HeaderSize): %ld < (%zu+%zu) \n", fileLen, imgszRead, imageStart);
2785 		printWarning("File not large enough to store image data: %s\n", imgname);
2786 		return NULL;
2787 	}
2788 	fseek(file, (long)imageStart, SEEK_SET);
2789 	unsigned char *bImg = (unsigned char *)malloc(imgsz);
2790 	//int i = 0;
2791 	//while (bImg[i] == 0) i++;
2792 	//printMessage("%d %d<\n",i,bImg[i]);
2793 	size_t sz = fread(bImg, 1, imgszRead, file);
2794 	fclose(file);
2795 	if (sz < imgszRead) {
2796 		printError("Only loaded %zu of %zu bytes for %s\n", sz, imgszRead, imgname);
2797 		return NULL;
2798 	}
2799 	if (bitsAllocated == 12)
2800 		conv12bit16bit(bImg, hdr);
2801 	return bImg;
2802 } //nii_loadImgCore()
2803 
nii_planar2rgb(unsigned char * bImg,struct nifti_1_header * hdr,int isPlanar)2804 unsigned char *nii_planar2rgb(unsigned char *bImg, struct nifti_1_header *hdr, int isPlanar) {
2805 	//DICOM data saved in triples RGBRGBRGB, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B
2806 	if (bImg == NULL)
2807 		return NULL;
2808 	if (hdr->datatype != DT_RGB24)
2809 		return bImg;
2810 	if (isPlanar == 0)
2811 		return bImg;
2812 	int dim3to7 = 1;
2813 	for (int i = 3; i < 8; i++)
2814 		if (hdr->dim[i] > 1)
2815 			dim3to7 = dim3to7 * hdr->dim[i];
2816 	int sliceBytes8 = hdr->dim[1] * hdr->dim[2];
2817 	int sliceBytes24 = sliceBytes8 * 3;
2818 	unsigned char *slice24 = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes24));
2819 	int sliceOffsetRGB = 0;
2820 	int sliceOffsetR = 0;
2821 	int sliceOffsetG = sliceOffsetR + sliceBytes8;
2822 	int sliceOffsetB = sliceOffsetR + 2 * sliceBytes8;
2823 	//printMessage("planar->rgb %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7);
2824 	int i = 0;
2825 	for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice
2826 		memcpy(slice24, &bImg[sliceOffsetRGB], sliceBytes24);
2827 		for (int rgb = 0; rgb < sliceBytes8; rgb++) {
2828 			bImg[i++] = slice24[sliceOffsetR + rgb];
2829 			bImg[i++] = slice24[sliceOffsetG + rgb];
2830 			bImg[i++] = slice24[sliceOffsetB + rgb];
2831 		}
2832 		sliceOffsetRGB += sliceBytes24;
2833 	} //for each slice
2834 	free(slice24);
2835 	return bImg;
2836 } //nii_planar2rgb()
2837 
nii_rgb2planar(unsigned char * bImg,struct nifti_1_header * hdr,int isPlanar)2838 unsigned char *nii_rgb2planar(unsigned char *bImg, struct nifti_1_header *hdr, int isPlanar) {
2839 	//DICOM data saved in triples RGBRGBRGB, Analyze RGB saved in planes RRR..RGGG..GBBBB..B
2840 	if (bImg == NULL)
2841 		return NULL;
2842 	if (hdr->datatype != DT_RGB24)
2843 		return bImg;
2844 	if (isPlanar == 1)
2845 		return bImg; //return nii_bgr2rgb(bImg,hdr);
2846 	int dim3to7 = 1;
2847 	for (int i = 3; i < 8; i++)
2848 		if (hdr->dim[i] > 1)
2849 			dim3to7 = dim3to7 * hdr->dim[i];
2850 	int sliceBytes8 = hdr->dim[1] * hdr->dim[2];
2851 	int sliceBytes24 = sliceBytes8 * 3;
2852 	unsigned char *slice24 = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes24));
2853 	//printMessage("rgb->planar %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7);
2854 	int sliceOffsetR = 0;
2855 	for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice
2856 		memcpy(slice24, &bImg[sliceOffsetR], sliceBytes24); //TPX memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24);
2857 		int sliceOffsetG = sliceOffsetR + sliceBytes8;
2858 		int sliceOffsetB = sliceOffsetR + 2 * sliceBytes8;
2859 		int i = 0;
2860 		int j = 0;
2861 		for (int rgb = 0; rgb < sliceBytes8; rgb++) {
2862 			bImg[sliceOffsetR + j] = slice24[i++];
2863 			bImg[sliceOffsetG + j] = slice24[i++];
2864 			bImg[sliceOffsetB + j] = slice24[i++];
2865 			j++;
2866 		}
2867 		sliceOffsetR += sliceBytes24;
2868 	} //for each slice
2869 	free(slice24);
2870 	return bImg;
2871 } //nii_rgb2Planar()
2872 
nii_iVaries(unsigned char * img,struct nifti_1_header * hdr,struct TDTI4D * dti4D)2873 unsigned char *nii_iVaries(unsigned char *img, struct nifti_1_header *hdr, struct TDTI4D *dti4D) {
2874 	//each DICOM image can have its own intensity scaling, whereas NIfTI requires the same scaling for all images in a file
2875 	//WARNING: do this BEFORE nii_check16bitUnsigned!!!!
2876 	//if (hdr->datatype != DT_INT16) return img;
2877 	int dim3to7 = 1;
2878 	for (int i = 3; i < 8; i++)
2879 		if (hdr->dim[i] > 1)
2880 			dim3to7 = dim3to7 * hdr->dim[i];
2881 	int nVox = hdr->dim[1] * hdr->dim[2] * dim3to7;
2882 	if (nVox < 1)
2883 		return img;
2884 	float *img32 = (float *)malloc(nVox * sizeof(float));
2885 	if (hdr->datatype == DT_UINT8) {
2886 		uint8_t *img8i = (uint8_t *)img;
2887 		for (int i = 0; i < nVox; i++)
2888 			img32[i] = img8i[i];
2889 	} else if (hdr->datatype == DT_UINT16) {
2890 		uint16_t *img16ui = (uint16_t *)img;
2891 		for (int i = 0; i < nVox; i++)
2892 			img32[i] = img16ui[i];
2893 	} else if (hdr->datatype == DT_INT16) {
2894 		int16_t *img16i = (int16_t *)img;
2895 		for (int i = 0; i < nVox; i++)
2896 			img32[i] = img16i[i];
2897 	} else if (hdr->datatype == DT_INT32) {
2898 		int32_t *img32i = (int32_t *)img;
2899 		for (int i = 0; i < nVox; i++)
2900 			img32[i] = (float)img32i[i];
2901 	}
2902 	free(img); //release previous image
2903 	if ((dti4D != NULL) && (dti4D->intenScale[0] != 0.0)) { //enhanced dataset, intensity varies across slices of a single file
2904 		if (dti4D->RWVScale[0] != 0.0)
2905 			printWarning("Intensity scale/slope using 0028,1053 and 0028,1052"); //to do: real-world values and precise values
2906 		int dim1to2 = hdr->dim[1] * hdr->dim[2];
2907 		int slice = -1;
2908 		//(0028,1052) SS = scale slope (2005,100E) RealWorldIntercept = (0040,9224) Real World Slope = (0040,9225)
2909 		//printf("vol\tRS(0028,1053)\tRI(0028,1052)\tSS(2005,100E)\trwS(0040,9225)\trwI(0040,9224)\n");
2910 		for (int i = 0; i < nVox; i++) { //issue 363
2911 			if ((i % dim1to2) == 0) {
2912 				slice++;
2913 				//printf("%d\t%g\t%g\t%g\t%g\t%g\n", slice, dti4D->intenScale[slice], dti4D->intenIntercept[slice],dti4D->intenScalePhilips[slice], dti4D->RWVScale[slice], dti4D->RWVIntercept[slice]);
2914 			}
2915 			img32[i] = (img32[i] * dti4D->intenScale[slice]) + dti4D->intenIntercept[slice];
2916 		}
2917 	} else { //
2918 		for (int i = 0; i < nVox; i++)
2919 			img32[i] = (img32[i] * hdr->scl_slope) + hdr->scl_inter;
2920 	}
2921 	hdr->scl_slope = 1;
2922 	hdr->scl_inter = 0;
2923 	hdr->datatype = DT_FLOAT;
2924 	hdr->bitpix = 32;
2925 	return (unsigned char *)img32;
2926 } //nii_iVaries()
2927 
nii_reorderSlicesX(unsigned char * bImg,struct nifti_1_header * hdr,struct TDTI4D * dti4D)2928 unsigned char *nii_reorderSlicesX(unsigned char *bImg, struct nifti_1_header *hdr, struct TDTI4D *dti4D) {
2929 	//Philips can save slices in any random order... rearrange all of them
2930 	int dim3to7 = 1;
2931 	for (int i = 3; i < 8; i++)
2932 		if (hdr->dim[i] > 1)
2933 			dim3to7 = dim3to7 * hdr->dim[i];
2934 	if (dim3to7 < 2)
2935 		return bImg;
2936 	if (dim3to7 > kMaxSlice2D)
2937 		return bImg;
2938 	uint64_t imgSz = nii_ImgBytes(*hdr);
2939 	uint64_t sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix / 8;
2940 	unsigned char *outImg = (unsigned char *)malloc(imgSz);
2941 	memcpy(&outImg[0], &bImg[0], imgSz);
2942 	for (int i = 0; i < dim3to7; i++) { //for each volume
2943 		int fromSlice = dti4D->sliceOrder[i];
2944 		//if (i < 10) printMessage(" ===> Moving slice from/to positions\t%d\t%d\n", i, toSlice);
2945 		//printMessage(" ===> Moving slice from/to positions\t%d\t%d\n", fromSlice, i);
2946 		if ((i < 0) || (fromSlice >= dim3to7)) {
2947 			printError("Re-ordered slice out-of-volume %d\n", fromSlice);
2948 		} else if (i != fromSlice) {
2949 			uint64_t inPos = fromSlice * sliceBytes;
2950 			uint64_t outPos = i * sliceBytes;
2951 			memcpy(&bImg[outPos], &outImg[inPos], sliceBytes);
2952 		}
2953 	}
2954 	free(outImg);
2955 	return bImg;
2956 }
2957 
nii_byteswap(unsigned char * img,struct nifti_1_header * hdr)2958 unsigned char *nii_byteswap(unsigned char *img, struct nifti_1_header *hdr) {
2959 	if (hdr->bitpix < 9)
2960 		return img;
2961 	uint64_t nvox = nii_ImgBytes(*hdr) / (hdr->bitpix / 8);
2962 	void *ar = (void *)img;
2963 	if (hdr->bitpix == 16)
2964 		nifti_swap_2bytes(nvox, ar);
2965 	if (hdr->bitpix == 32)
2966 		nifti_swap_4bytes(nvox, ar);
2967 	if (hdr->bitpix == 64)
2968 		nifti_swap_8bytes(nvox, ar);
2969 	return img;
2970 } //nii_byteswap()
2971 
2972 #ifdef myEnableJasper
nii_loadImgCoreJasper(char * imgname,struct nifti_1_header hdr,struct TDICOMdata dcm,int compressFlag)2973 unsigned char *nii_loadImgCoreJasper(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, int compressFlag) {
2974 	if (jas_init()) {
2975 		return NULL;
2976 	}
2977 	jas_stream_t *in;
2978 	jas_image_t *image;
2979 	jas_setdbglevel(0);
2980 	if (!(in = jas_stream_fopen(imgname, "rb"))) {
2981 		printError("Cannot open input image file %s\n", imgname);
2982 		return NULL;
2983 	}
2984 	//int isSeekable = jas_stream_isseekable(in);
2985 	jas_stream_seek(in, dcm.imageStart, 0);
2986 	int infmt = jas_image_getfmt(in);
2987 	if (infmt < 0) {
2988 		printError("Input image has unknown format %s offset %d bytes %d\n", imgname, dcm.imageStart, dcm.imageBytes);
2989 		return NULL;
2990 	}
2991 	char opt[] = "\0";
2992 	char *inopts = opt;
2993 	if (!(image = jas_image_decode(in, infmt, inopts))) {
2994 		printError("Cannot decode image data %s offset %d bytes %d\n", imgname, dcm.imageStart, dcm.imageBytes);
2995 		return NULL;
2996 	}
2997 	int numcmpts;
2998 	int cmpts[4];
2999 	switch (jas_clrspc_fam(jas_image_clrspc(image))) {
3000 	case JAS_CLRSPC_FAM_RGB:
3001 		if (jas_image_clrspc(image) != JAS_CLRSPC_SRGB)
3002 			printWarning("Inaccurate color\n");
3003 		numcmpts = 3;
3004 		if ((cmpts[0] = jas_image_getcmptbytype(image,
3005 												JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R))) < 0 ||
3006 			(cmpts[1] = jas_image_getcmptbytype(image,
3007 												JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G))) < 0 ||
3008 			(cmpts[2] = jas_image_getcmptbytype(image,
3009 												JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B))) < 0) {
3010 			printError("Missing color component\n");
3011 			return NULL;
3012 		}
3013 		break;
3014 	case JAS_CLRSPC_FAM_GRAY:
3015 		if (jas_image_clrspc(image) != JAS_CLRSPC_SGRAY)
3016 			printWarning("Inaccurate color\n");
3017 		numcmpts = 1;
3018 		if ((cmpts[0] = jas_image_getcmptbytype(image,
3019 												JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y))) < 0) {
3020 			printError("Missing color component\n");
3021 			return NULL;
3022 		}
3023 		break;
3024 	default:
3025 		printError("Unsupported color space\n");
3026 		return NULL;
3027 		break;
3028 	}
3029 	int width = jas_image_cmptwidth(image, cmpts[0]);
3030 	int height = jas_image_cmptheight(image, cmpts[0]);
3031 	int prec = jas_image_cmptprec(image, cmpts[0]);
3032 	int sgnd = jas_image_cmptsgnd(image, cmpts[0]);
3033 #ifdef MY_DEBUG
3034 	printMessage("offset %d w*h %d*%d bpp %d sgnd %d components %d '%s' Jasper=%s\n", dcm.imageStart, width, height, prec, sgnd, numcmpts, imgname, jas_getversion());
3035 #endif
3036 	for (int cmptno = 0; cmptno < numcmpts; ++cmptno) {
3037 		if (jas_image_cmptwidth(image, cmpts[cmptno]) != width ||
3038 			jas_image_cmptheight(image, cmpts[cmptno]) != height ||
3039 			jas_image_cmptprec(image, cmpts[cmptno]) != prec ||
3040 			jas_image_cmptsgnd(image, cmpts[cmptno]) != sgnd ||
3041 			jas_image_cmpthstep(image, cmpts[cmptno]) != jas_image_cmpthstep(image, 0) ||
3042 			jas_image_cmptvstep(image, cmpts[cmptno]) != jas_image_cmptvstep(image, 0) ||
3043 			jas_image_cmpttlx(image, cmpts[cmptno]) != jas_image_cmpttlx(image, 0) ||
3044 			jas_image_cmpttly(image, cmpts[cmptno]) != jas_image_cmpttly(image, 0)) {
3045 			printMessage("The NIfTI format cannot be used to represent an image with this geometry.\n");
3046 			return NULL;
3047 		}
3048 	}
3049 	//extract the data
3050 	int bpp = (prec + 7) >> 3; //e.g. 12 bits requires 2 bytes
3051 	int imgbytes = bpp * width * height * numcmpts;
3052 	if ((bpp < 1) || (bpp > 2) || (width < 1) || (height < 1) || (imgbytes < 1)) {
3053 		printError("Catastrophic decompression error\n");
3054 		return NULL;
3055 	}
3056 	jas_seqent_t v;
3057 	unsigned char *img = (unsigned char *)malloc(imgbytes);
3058 	uint16_t *img16ui = (uint16_t *)img; //unsigned 16-bit
3059 	int16_t *img16i = (int16_t *)img; //signed 16-bit
3060 	if (sgnd)
3061 		bpp = -bpp;
3062 	if (bpp == -1) {
3063 		printError("Signed 8-bit DICOM?\n");
3064 		return NULL;
3065 	}
3066 	jas_matrix_t *data;
3067 	jas_seqent_t *d;
3068 	data = 0;
3069 	int cmptno, y, x;
3070 	int pix = 0;
3071 	for (cmptno = 0; cmptno < numcmpts; ++cmptno) {
3072 		if (!(data = jas_matrix_create(1, width))) {
3073 			free(img);
3074 			return NULL;
3075 		}
3076 	}
3077 	//n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB
3078 	for (cmptno = 0; cmptno < numcmpts; ++cmptno) {
3079 		for (y = 0; y < height; ++y) {
3080 			if (jas_image_readcmpt(image, cmpts[cmptno], 0, y, width, 1, data)) {
3081 				free(img);
3082 				return NULL;
3083 			}
3084 			d = jas_matrix_getref(data, 0, 0);
3085 			for (x = 0; x < width; ++x) {
3086 				v = *d;
3087 				switch (bpp) {
3088 				case 1:
3089 					img[pix] = v;
3090 					break;
3091 				case 2:
3092 					img16ui[pix] = v;
3093 					break;
3094 				case -2:
3095 					img16i[pix] = v;
3096 					break;
3097 				}
3098 				pix++;
3099 				++d;
3100 			} //for x
3101 		} //for y
3102 	} //for each component
3103 	jas_matrix_destroy(data);
3104 	jas_image_destroy(image);
3105 	jas_image_clearfmts();
3106 	return img;
3107 } //nii_loadImgCoreJasper()
3108 #endif
3109 
3110 struct TJPEG {
3111 	long offset;
3112 	long size;
3113 };
3114 
decode_JPEG_SOF_0XC3_stack(const char * fn,int skipBytes,int isVerbose,int frames,bool isLittleEndian)3115 TJPEG *decode_JPEG_SOF_0XC3_stack(const char *fn, int skipBytes, int isVerbose, int frames, bool isLittleEndian) {
3116 #define abortGoto() free(lOffsetRA); return NULL;
3117 	TJPEG *lOffsetRA = (TJPEG *)malloc(frames * sizeof(TJPEG));
3118 	FILE *reader = fopen(fn, "rb");
3119 	fseek(reader, 0, SEEK_END);
3120 	long lRawSz = ftell(reader) - skipBytes;
3121 	if (lRawSz <= 8) {
3122 		printError("Unable to open %s\n", fn);
3123 		abortGoto(); //read failure
3124 	}
3125 	fseek(reader, skipBytes, SEEK_SET);
3126 	unsigned char *lRawRA = (unsigned char *)malloc(lRawSz);
3127 	size_t lSz = fread(lRawRA, 1, lRawSz, reader);
3128 	fclose(reader);
3129 	if (lSz < (size_t)lRawSz) {
3130 		printError("Unable to read %s\n", fn);
3131 		abortGoto(); //read failure
3132 	}
3133 	long lRawPos = 0; //starting position
3134 	int frame = 0;
3135 	while ((frame < frames) && ((lRawPos + 10) < lRawSz)) {
3136 		int tag = dcmInt(4, &lRawRA[lRawPos], isLittleEndian);
3137 		lRawPos += 4; //read tag
3138 		int tagLength = dcmInt(4, &lRawRA[lRawPos], isLittleEndian);
3139 		long tagEnd = lRawPos + tagLength + 4;
3140 		if (isVerbose)
3141 			printMessage("Frame %d Tag %#x length %d end at %ld\n", frame + 1, tag, tagLength, tagEnd + skipBytes);
3142 		lRawPos += 4; //read tag length
3143 		if ((lRawRA[lRawPos] != 0xFF) || (lRawRA[lRawPos + 1] != 0xD8) || (lRawRA[lRawPos + 2] != 0xFF)) {
3144 			if (isVerbose)
3145 				printWarning("JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn);
3146 		} else {
3147 			lOffsetRA[frame].offset = lRawPos + skipBytes;
3148 			lOffsetRA[frame].size = tagLength;
3149 			frame++;
3150 		}
3151 		lRawPos = tagEnd;
3152 	}
3153 	free(lRawRA);
3154 	if (frame < frames) {
3155 		printMessage("Only found %d of %d JPEG fragments. Please use dcmdjpeg or gdcmconv to uncompress data.\n", frame, frames);
3156 		abortGoto();
3157 	}
3158 	return lOffsetRA;
3159 }
3160 
nii_loadImgJPEGC3(char * imgname,struct nifti_1_header hdr,struct TDICOMdata dcm,int isVerbose)3161 unsigned char *nii_loadImgJPEGC3(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, int isVerbose) {
3162 	//arcane and inefficient lossless compression method popularized by dcmcjpeg, examples at http://www.osirix-viewer.com/resources/dicom-image-library/
3163 	int dimX, dimY, bits, frames;
3164 	//clock_t start = clock();
3165 	// https://github.com/rii-mango/JPEGLosslessDecoderJS/blob/master/tests/data/jpeg_lossless_sel1-8bit.dcm
3166 	//N.B. this current code can not extract a 2D image that is saved as multiple fragments, for example see the JPLL files at
3167 	// ftp://medical.nema.org/MEDICAL/Dicom/DataSets/WG04/
3168 	//Live javascript code that can handle these is at
3169 	// https://github.com/chafey/cornerstoneWADOImageLoader
3170 	//I have never seen these segmented images in the wild, so we will simply warn the user if we encounter such a file
3171 	//int Sz = JPEG_SOF_0XC3_sz (imgname, (dcm.imageStart - 4), dcm.isLittleEndian);
3172 	//printMessage("Sz %d %d\n", Sz, dcm.imageBytes );
3173 	//This behavior is legal but appears extremely rare
3174 	//ftp://medical.nema.org/medical/dicom/final/cp900_ft.pdf
3175 	if (65536 == dcm.imageBytes)
3176 		printError("One frame may span multiple fragments. SOFxC3 lossless JPEG. Please extract with dcmdjpeg or gdcmconv.\n");
3177 	unsigned char *ret = decode_JPEG_SOF_0XC3(imgname, dcm.imageStart, isVerbose, &dimX, &dimY, &bits, &frames, 0);
3178 	if (ret == NULL) {
3179 		printMessage("Unable to decode JPEG. Please use dcmdjpeg to uncompress data.\n");
3180 		return NULL;
3181 	}
3182 	if (hdr.dim[3] != frames) { //multi-slice image saved as multiple image fragments rather than a single image
3183 		//printMessage("Unable to decode all slices (%d/%d). Please use dcmdjpeg to uncompress data.\n", frames, hdr.dim[3]);
3184 		if (ret != NULL)
3185 			free(ret);
3186 		TJPEG *offsetRA = decode_JPEG_SOF_0XC3_stack(imgname, dcm.imageStart - 8, isVerbose, hdr.dim[3], dcm.isLittleEndian);
3187 		if (offsetRA == NULL)
3188 			return NULL;
3189 		size_t slicesz = nii_SliceBytes(hdr);
3190 		size_t imgsz = slicesz * hdr.dim[3];
3191 		size_t pos = 0;
3192 		unsigned char *bImg = (unsigned char *)malloc(imgsz);
3193 		for (int frame = 0; frame < hdr.dim[3]; frame++) {
3194 			if (isVerbose)
3195 				printMessage("JPEG frame %d has %ld bytes @ %ld\n", frame, offsetRA[frame].size, offsetRA[frame].offset);
3196 			unsigned char *ret = decode_JPEG_SOF_0XC3(imgname, (int)offsetRA[frame].offset, false, &dimX, &dimY, &bits, &frames, (int)offsetRA[frame].size);
3197 			if (ret == NULL) {
3198 				printMessage("Unable to decode JPEG. Please use dcmdjpeg to uncompress data.\n");
3199 				free(bImg);
3200 				return NULL;
3201 			}
3202 			memcpy(&bImg[pos], ret, slicesz); //dest, src, size
3203 			free(ret);
3204 			pos += slicesz;
3205 		}
3206 		free(offsetRA);
3207 		return bImg;
3208 	}
3209 	return ret;
3210 }
3211 
3212 #ifndef F_OK
3213 #define F_OK 0 /* existence check */
3214 #endif
3215 
3216 #ifndef myDisableClassicJPEG
3217 
3218 #ifdef myTurboJPEG //if turboJPEG instead of nanoJPEG for classic JPEG decompression
3219 
3220 //unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) {
nii_loadImgJPEG50(char * imgname,struct TDICOMdata dcm)3221 unsigned char *nii_loadImgJPEG50(char *imgname, struct TDICOMdata dcm) {
3222 	//decode classic JPEG using nanoJPEG
3223 	//printMessage("50 offset %d\n", dcm.imageStart);
3224 	if ((dcm.samplesPerPixel != 1) && (dcm.samplesPerPixel != 3)) {
3225 		printError("%d components (expected 1 or 3) in a JPEG image '%s'\n", dcm.samplesPerPixel, imgname);
3226 		return NULL;
3227 	}
3228 	if (access(imgname, F_OK) == -1) {
3229 		printError("Unable to find '%s'\n", imgname);
3230 		return NULL;
3231 	}
3232 	//load compressed data
3233 	FILE *f = fopen(imgname, "rb");
3234 	fseek(f, 0, SEEK_END);
3235 	long unsigned int _jpegSize = (long unsigned int)ftell(f);
3236 	_jpegSize = _jpegSize - dcm.imageStart;
3237 	if (_jpegSize < 8) {
3238 		printError("File too small\n");
3239 		fclose(f);
3240 		return NULL;
3241 	}
3242 	unsigned char *_compressedImage = (unsigned char *)malloc(_jpegSize);
3243 	fseek(f, dcm.imageStart, SEEK_SET);
3244 	_jpegSize = (long unsigned int)fread(_compressedImage, 1, _jpegSize, f);
3245 	fclose(f);
3246 	int jpegSubsamp, width, height;
3247 	//printMessage("Decoding with turboJPEG\n");
3248 	tjhandle _jpegDecompressor = tjInitDecompress();
3249 	tjDecompressHeader2(_jpegDecompressor, _compressedImage, _jpegSize, &width, &height, &jpegSubsamp);
3250 	int COLOR_COMPONENTS = dcm.samplesPerPixel;
3251 	//printMessage("turboJPEG h*w %d*%d sampling %d components %d\n", width, height, jpegSubsamp, COLOR_COMPONENTS);
3252 	if ((jpegSubsamp == TJSAMP_GRAY) && (COLOR_COMPONENTS != 1)) {
3253 		printError("Grayscale jpegs should not have %d components '%s'\n", COLOR_COMPONENTS, imgname);
3254 	}
3255 	if ((jpegSubsamp != TJSAMP_GRAY) && (COLOR_COMPONENTS != 3)) {
3256 		printError("Color jpegs should not have %d components '%s'\n", COLOR_COMPONENTS, imgname);
3257 	}
3258 	//unsigned char bImg[width*height*COLOR_COMPONENTS]; //!< will contain the decompressed image
3259 	unsigned char *bImg = (unsigned char *)malloc(width * height * COLOR_COMPONENTS);
3260 	if (COLOR_COMPONENTS == 1) //TJPF_GRAY
3261 		tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, bImg, width, 0 /*pitch*/, height, TJPF_GRAY, TJFLAG_FASTDCT);
3262 	else
3263 		tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, bImg, width, 0 /*pitch*/, height, TJPF_RGB, TJFLAG_FASTDCT);
3264 	//printMessage("turboJPEG h*w %d*%d (sampling %d)\n", width, height, jpegSubsamp);
3265 	tjDestroy(_jpegDecompressor);
3266 	return bImg;
3267 }
3268 
3269 #else //if turboJPEG else use nanojpeg...
3270 
3271 //unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) {
nii_loadImgJPEG50(char * imgname,struct TDICOMdata dcm)3272 unsigned char *nii_loadImgJPEG50(char *imgname, struct TDICOMdata dcm) {
3273 	//decode classic JPEG using nanoJPEG
3274 	//printMessage("50 offset %d\n", dcm.imageStart);
3275 	if (access(imgname, F_OK) == -1) {
3276 		printError("Unable to find '%s'\n", imgname);
3277 		return NULL;
3278 	}
3279 	//load compressed data
3280 	FILE *f = fopen(imgname, "rb");
3281 	fseek(f, 0, SEEK_END);
3282 	int size = (int)ftell(f);
3283 	size = size - dcm.imageStart;
3284 	if (size < 8) {
3285 		printError("File too small '%s'\n", imgname);
3286 		fclose(f);
3287 		return NULL;
3288 	}
3289 	char *buf = (char *)malloc(size);
3290 	fseek(f, dcm.imageStart, SEEK_SET);
3291 	size = (int)fread(buf, 1, size, f);
3292 	fclose(f);
3293 	//decode
3294 	njInit();
3295 	if (njDecode(buf, size)) {
3296 		printError("Unable to decode JPEG image.\n");
3297 		return NULL;
3298 	}
3299 	free(buf);
3300 	unsigned char *bImg = (unsigned char *)malloc(njGetImageSize());
3301 	memcpy(bImg, njGetImage(), njGetImageSize()); //dest, src, size
3302 	njDone();
3303 	return bImg;
3304 }
3305 #endif
3306 #endif
3307 
rleInt(int lIndex,unsigned char lBuffer[],bool swap)3308 uint32_t rleInt(int lIndex, unsigned char lBuffer[], bool swap) { //read binary 32-bit integer
3309 	uint32_t retVal = 0;
3310 	memcpy(&retVal, (char *)&lBuffer[lIndex * 4], 4);
3311 	if (!swap)
3312 		return retVal;
3313 	uint32_t swapVal;
3314 	char *inInt = (char *)&retVal;
3315 	char *outInt = (char *)&swapVal;
3316 	outInt[0] = inInt[3];
3317 	outInt[1] = inInt[2];
3318 	outInt[2] = inInt[1];
3319 	outInt[3] = inInt[0];
3320 	return swapVal;
3321 } //rleInt()
3322 
nii_loadImgPMSCT_RLE1(char * imgname,struct nifti_1_header hdr,struct TDICOMdata dcm)3323 unsigned char *nii_loadImgPMSCT_RLE1(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) {
3324 	//Transfer Syntax 1.3.46.670589.33.1.4.1 also handled by TomoVision and GDCM's rle2img
3325 	//https://github.com/malaterre/GDCM/blob/a923f206060e85e8d81add565ae1b9dd7b210481/Examples/Cxx/rle2img.cxx
3326 	//see rle2img: Philips/ELSCINT1 run-length compression 07a1,1011= PMSCT_RLE1
3327 	if (dcm.imageBytes < 66) { //64 for header+ 2 byte minimum image
3328 		printError("%d is not enough bytes for PMSCT_RLE1 compression '%s'\n", dcm.imageBytes, imgname);
3329 		return NULL;
3330 	}
3331 	int bytesPerSample = dcm.samplesPerPixel * (dcm.bitsAllocated / 8);
3332 	if (bytesPerSample != 2) { //there is an RGB variation of this format, but we have not seen it in the wild
3333 		printError("PMSCT_RLE1 should be 16-bits per sample (please report on Github and use pmsct_rgb1).\n");
3334 		return NULL;
3335 	}
3336 	FILE *file = fopen(imgname, "rb");
3337 	if (!file) {
3338 		printError("Unable to open %s\n", imgname);
3339 		return NULL;
3340 	}
3341 	fseek(file, 0, SEEK_END);
3342 	long fileLen = ftell(file);
3343 	if ((fileLen < 1) || (dcm.imageBytes < 1) || (fileLen < (dcm.imageBytes + dcm.imageStart))) {
3344 		printMessage("File not large enough to store image data: %s\n", imgname);
3345 		fclose(file);
3346 		return NULL;
3347 	}
3348 	fseek(file, (long)dcm.imageStart, SEEK_SET);
3349 	size_t imgsz = nii_ImgBytes(hdr);
3350 	char *cImg = (char *)malloc(dcm.imageBytes); //compressed input
3351 	size_t sz = fread(cImg, 1, dcm.imageBytes, file);
3352 	fclose(file);
3353 	if (sz < (size_t)dcm.imageBytes) {
3354 		printError("Only loaded %zu of %d bytes for %s\n", sz, dcm.imageBytes, imgname);
3355 		free(cImg);
3356 		return NULL;
3357 	}
3358 	if (imgsz == dcm.imageBytes) { // Handle special case that data is not compressed:
3359 		return (unsigned char *)cImg;
3360 	}
3361 	unsigned char *bImg = (unsigned char *)malloc(imgsz); //binary output
3362 	// RLE pass: compressed -> temp (bImg -> tImg)
3363 	char *tImg = (char *)malloc(imgsz); //temp output
3364 	int o = 0;
3365 	for (size_t i = 0; i < dcm.imageBytes; ++i) {
3366 		if (cImg[i] == (char)0xa5) {
3367 			int repeat = (unsigned char)cImg[i + 1] + 1;
3368 			char value = cImg[i + 2];
3369 			while (repeat) {
3370 				tImg[o] = value;
3371 				o++;
3372 				--repeat;
3373 			}
3374 			i += 2;
3375 		} else {
3376 			tImg[o] = cImg[i];
3377 			o++;
3378 		}
3379 	} //for i
3380 	free(cImg);
3381 	int tempsize = o;
3382 	//Looks like this RLE is pretty ineffective...
3383 	// printMessage("RLE %d -> %d\n", dcm.imageBytes, o);
3384 	//Delta encoding pass: temp -> output (tImg -> bImg)
3385 	unsigned short delta = 0;
3386 	o = 0;
3387 	int n16 = (int)imgsz >> 1;
3388 	unsigned short *bImg16 = (unsigned short *)bImg;
3389 	for (size_t i = 0; i < tempsize; ++i) {
3390 		if (tImg[i] == (unsigned char)0x5a) {
3391 			unsigned char v1 = (unsigned char)tImg[i + 1];
3392 			unsigned char v2 = (unsigned char)tImg[i + 2];
3393 			unsigned short value = (unsigned short)(v2 * 256 + v1);
3394 			if (o < n16)
3395 				bImg16[o] = value;
3396 			o++;
3397 			delta = value;
3398 			i += 2;
3399 		} else {
3400 			unsigned short value = (unsigned short)(tImg[i] + delta);
3401 			if (o < n16)
3402 				bImg16[o] = value;
3403 			o++;
3404 			delta = value;
3405 		}
3406 	} //for i
3407 	//printMessage("Delta %d -> %d (of %d)\n", tempsize, 2*(o-1), imgsz);
3408 	free(tImg);
3409 	return bImg;
3410 } // nii_loadImgPMSCT_RLE1()
3411 
nii_loadImgRLE(char * imgname,struct nifti_1_header hdr,struct TDICOMdata dcm)3412 unsigned char *nii_loadImgRLE(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) {
3413 	//decompress PackBits run-length encoding https://en.wikipedia.org/wiki/PackBits
3414 	if (dcm.imageBytes < 66) { //64 for header+ 2 byte minimum image
3415 		printError("%d is not enough bytes for RLE compression '%s'\n", dcm.imageBytes, imgname);
3416 		return NULL;
3417 	}
3418 	FILE *file = fopen(imgname, "rb");
3419 	if (!file) {
3420 		printError("Unable to open %s\n", imgname);
3421 		return NULL;
3422 	}
3423 	fseek(file, 0, SEEK_END);
3424 	long fileLen = ftell(file);
3425 	if ((fileLen < 1) || (dcm.imageBytes < 1) || (fileLen < (dcm.imageBytes + dcm.imageStart))) {
3426 		printMessage("File not large enough to store image data: %s\n", imgname);
3427 		fclose(file);
3428 		return NULL;
3429 	}
3430 	fseek(file, (long)dcm.imageStart, SEEK_SET);
3431 	size_t imgsz = nii_ImgBytes(hdr);
3432 	unsigned char *cImg = (unsigned char *)malloc(dcm.imageBytes); //compressed input
3433 	size_t sz = fread(cImg, 1, dcm.imageBytes, file);
3434 	fclose(file);
3435 	if (sz < (size_t)dcm.imageBytes) {
3436 		printError("Only loaded %zu of %d bytes for %s\n", sz, dcm.imageBytes, imgname);
3437 		free(cImg);
3438 		return NULL;
3439 	}
3440 	//read header http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_G.3.html
3441 	bool swap = (dcm.isLittleEndian != littleEndianPlatform());
3442 	int bytesPerSample = dcm.samplesPerPixel * (dcm.bitsAllocated / 8);
3443 	uint32_t bytesPerSampleRLE = rleInt(0, cImg, swap);
3444 	if ((bytesPerSample < 0) || (bytesPerSampleRLE != (uint32_t)bytesPerSample)) {
3445 		printError("RLE header corrupted %d != %d\n", bytesPerSampleRLE, bytesPerSample);
3446 		free(cImg);
3447 		return NULL;
3448 	}
3449 	unsigned char *bImg = (unsigned char *)malloc(imgsz); //binary output
3450 	for (size_t i = 0; i < imgsz; i++)
3451 		bImg[i] = 0;
3452 	for (int i = 0; i < bytesPerSample; i++) {
3453 		uint32_t offset = rleInt(i + 1, cImg, swap);
3454 		if ((dcm.imageBytes < 0) || (offset > (uint32_t)dcm.imageBytes)) {
3455 			printError("RLE header error\n");
3456 			free(cImg);
3457 			free(bImg);
3458 			return NULL;
3459 		}
3460 		//save in platform's endian:
3461 		// The first Segment is generated by stripping off the most significant byte of each Padded Composite Pixel Code...
3462 		size_t vx = i;
3463 		if ((dcm.samplesPerPixel == 1) && (littleEndianPlatform())) //endian, except for RGB
3464 			vx = (bytesPerSample - 1) - i;
3465 		while (vx < imgsz) {
3466 			int8_t n = (int8_t)cImg[offset];
3467 			offset++;
3468 			//http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_G.3.html
3469 			//if ((n >= 0) && (n <= 127)) { //not needed: int8_t always <=127
3470 			if (n >= 0) { //literal bytes
3471 				int reps = 1 + (int)n;
3472 				for (int r = 0; r < reps; r++) {
3473 					int8_t v = cImg[offset];
3474 					offset++;
3475 					if (vx >= imgsz)
3476 						; //printMessage("literal overflow %d %d\n", r, reps);
3477 					else
3478 						bImg[vx] = v;
3479 					vx = vx + bytesPerSample;
3480 				}
3481 			} else if ((n <= -1) && (n >= -127)) { //repeated run
3482 				int8_t v = cImg[offset];
3483 				offset++;
3484 				int reps = -(int)n + 1;
3485 				for (int r = 0; r < reps; r++) {
3486 					if (vx >= imgsz)
3487 						; //printMessage("repeat overflow %d\n", reps);
3488 					else
3489 						bImg[vx] = v;
3490 					vx = vx + bytesPerSample;
3491 				}
3492 			}; //n.b. we ignore -128!
3493 		} //while vx < imgsz
3494 	} //for i < bytesPerSample
3495 	free(cImg);
3496 	return bImg;
3497 } // nii_loadImgRLE()
3498 
3499 #ifdef myDisableOpenJPEG
3500 #ifndef myEnableJasper
3501 //avoid compiler warning, see https://stackoverflow.com/questions/3599160/unused-parameter-warnings-in-c
3502 #define UNUSED(x) (void)(x)
3503 #endif
3504 #endif
3505 
3506 #if defined(myEnableJPEGLS) || defined(myEnableJPEGLS1) //Support for JPEG-LS
3507 //JPEG-LS: Transfer Syntaxes 1.2.840.10008.1.2.4.80 1.2.840.10008.1.2.4.81
3508 
3509 #ifdef myEnableJPEGLS1 //use CharLS v1.* requires c++03
3510 //-std=c++03 -DmyEnableJPEGLS1 charls1/header.cpp charls1/jpegls.cpp charls1/jpegmarkersegment.cpp charls1/interface.cpp charls1/jpegstreamwriter.cpp
3511 #include "charls1/interface.h"
3512 #else //use latest release of CharLS: CharLS 2.x requires c++14
3513 //-std=c++14 -DmyEnableJPEGLS charls/jpegls.cpp charls/jpegmarkersegment.cpp charls/interface.cpp charls/jpegstreamwriter.cpp charls/jpegstreamreader.cpp
3514 #include "charls/charls.h"
3515 #endif
3516 #include "charls/publictypes.h"
3517 
nii_loadImgJPEGLS(char * imgname,struct nifti_1_header hdr,struct TDICOMdata dcm)3518 unsigned char *nii_loadImgJPEGLS(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) {
3519 	//load compressed data
3520 	FILE *file = fopen(imgname, "rb");
3521 	if (!file) {
3522 		printError("Unable to open %s\n", imgname);
3523 		return NULL;
3524 	}
3525 	fseek(file, 0, SEEK_END);
3526 	long fileLen = ftell(file);
3527 	if ((fileLen < 1) || (dcm.imageBytes < 1) || (fileLen < (dcm.imageBytes + dcm.imageStart))) {
3528 		printMessage("File not large enough to store JPEG-LS data: %s\n", imgname);
3529 		fclose(file);
3530 		return NULL;
3531 	}
3532 	fseek(file, (long)dcm.imageStart, SEEK_SET);
3533 	unsigned char *cImg = (unsigned char *)malloc(dcm.imageBytes); //compressed input
3534 	size_t sz = fread(cImg, 1, dcm.imageBytes, file);
3535 	fclose(file);
3536 	if (sz < (size_t)dcm.imageBytes) {
3537 		printError("Only loaded %zu of %d bytes for %s\n", sz, dcm.imageBytes, imgname);
3538 		free(cImg);
3539 		return NULL;
3540 	}
3541 	//create buffer for uncompressed data
3542 	size_t imgsz = nii_ImgBytes(hdr);
3543 	unsigned char *bImg = (unsigned char *)malloc(imgsz); //binary output
3544 	JlsParameters params = {};
3545 #ifdef myEnableJPEGLS1
3546 	if (JpegLsReadHeader(cImg, dcm.imageBytes, &params) != OK) {
3547 #else
3548 	using namespace charls;
3549 	if (JpegLsReadHeader(cImg, dcm.imageBytes, &params, nullptr) != ApiResult::OK) {
3550 #endif
3551 		printMessage("CharLS failed to read header.\n");
3552 		return NULL;
3553 	}
3554 #ifdef myEnableJPEGLS1
3555 	if (JpegLsDecode(&bImg[0], imgsz, &cImg[0], dcm.imageBytes, &params) != OK) {
3556 #else
3557 	if (JpegLsDecode(&bImg[0], imgsz, &cImg[0], dcm.imageBytes, &params, nullptr) != ApiResult::OK) {
3558 #endif
3559 		free(bImg);
3560 		printMessage("CharLS failed to read image.\n");
3561 		return NULL;
3562 	}
3563 	return (bImg);
3564 }
3565 #endif
3566 
3567 unsigned char *nii_loadImgXL(char *imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries, int compressFlag, int isVerbose, struct TDTI4D *dti4D) {
3568 	//provided with a filename (imgname) and DICOM header (dcm), creates NIfTI header (hdr) and img
3569 	if (headerDcm2Nii(dcm, hdr, true) == EXIT_FAILURE)
3570 		return NULL; //TOFU
3571 	unsigned char *img;
3572 	if (dcm.compressionScheme == kCompress50) {
3573 #ifdef myDisableClassicJPEG
3574 		printMessage("Software not compiled to decompress classic JPEG DICOM images\n");
3575 		return NULL;
3576 #else
3577 		//img = nii_loadImgJPEG50(imgname, *hdr, dcm);
3578 		img = nii_loadImgJPEG50(imgname, dcm);
3579 		if (hdr->datatype == DT_RGB24) //convert to planar
3580 			img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); //do this BEFORE Y-Flip, or RGB order can be flipped
3581 #endif
3582 	} else if (dcm.compressionScheme == kCompressJPEGLS) {
3583 #if defined(myEnableJPEGLS) || defined(myEnableJPEGLS1)
3584 		img = nii_loadImgJPEGLS(imgname, *hdr, dcm);
3585 		if (hdr->datatype == DT_RGB24) //convert to planar
3586 			img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); //do this BEFORE Y-Flip, or RGB order can be flipped
3587 #else
3588 		printMessage("Software not compiled to decompress JPEG-LS DICOM images\n");
3589 		return NULL;
3590 #endif
3591 	} else if (dcm.compressionScheme == kCompressPMSCT_RLE1) {
3592 		img = nii_loadImgPMSCT_RLE1(imgname, *hdr, dcm);
3593 	} else if (dcm.compressionScheme == kCompressRLE) {
3594 		img = nii_loadImgRLE(imgname, *hdr, dcm);
3595 		if (hdr->datatype == DT_RGB24) //convert to planar
3596 			img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); //do this BEFORE Y-Flip, or RGB order can be flipped
3597 	} else if (dcm.compressionScheme == kCompressC3)
3598 		img = nii_loadImgJPEGC3(imgname, *hdr, dcm, isVerbose);
3599 	else
3600 #ifndef myDisableOpenJPEG
3601 		if (((dcm.compressionScheme == kCompress50) || (dcm.compressionScheme == kCompressYes)) && (compressFlag != kCompressNone))
3602 		img = nii_loadImgCoreOpenJPEG(imgname, *hdr, dcm, compressFlag);
3603 	else
3604 #else
3605 #ifdef myEnableJasper
3606 		if ((dcm.compressionScheme == kCompressYes) && (compressFlag != kCompressNone))
3607 		img = nii_loadImgCoreJasper(imgname, *hdr, dcm, compressFlag);
3608 	else
3609 #endif
3610 #endif
3611 		if (dcm.compressionScheme == kCompressYes) {
3612 		printMessage("Software not set up to decompress DICOM\n");
3613 		return NULL;
3614 	} else
3615 		img = nii_loadImgCore(imgname, *hdr, dcm.bitsAllocated, dcm.imageStart);
3616 	if (img == NULL)
3617 		return img;
3618 	if ((dcm.compressionScheme == kCompressNone) && (dcm.isLittleEndian != littleEndianPlatform()) && (hdr->bitpix > 8))
3619 		img = nii_byteswap(img, hdr);
3620 	if ((dcm.compressionScheme == kCompressNone) && (hdr->datatype == DT_RGB24))
3621 		img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); //do this BEFORE Y-Flip, or RGB order can be flipped
3622 	dcm.isPlanarRGB = true;
3623 	if (dcm.CSA.mosaicSlices > 1) {
3624 		img = nii_demosaic(img, hdr, dcm.CSA.mosaicSlices, (dcm.manufacturer == kMANUFACTURER_UIH)); //, dcm.CSA.protocolSliceNumber1);
3625 	}
3626 	if ((dti4D == NULL) && (!dcm.isFloat) && (iVaries)) //must do afte
3627 		img = nii_iVaries(img, hdr, NULL);
3628 	int nAcq = dcm.locationsInAcquisition;
3629 	if ((nAcq > 1) && (hdr->dim[0] < 4) && ((hdr->dim[3] % nAcq) == 0) && (hdr->dim[3] > nAcq)) {
3630 		hdr->dim[4] = hdr->dim[3] / nAcq;
3631 		hdr->dim[3] = nAcq;
3632 		hdr->dim[0] = 4;
3633 	}
3634 	if ((dti4D != NULL) && (dti4D->sliceOrder[0] >= 0))
3635 		img = nii_reorderSlicesX(img, hdr, dti4D);
3636 	if ((dti4D != NULL) && (!dcm.isFloat) && (iVaries))
3637 		img = nii_iVaries(img, hdr, dti4D);
3638 	headerDcm2NiiSForm(dcm, dcm, hdr, false);
3639 	return img;
3640 } //nii_loadImgXL()
3641 
3642 int isSQ(uint32_t groupElement) { //Detect sequence VR ("SQ") for implicit tags
3643 	static const int array_size = 35;
3644 	uint32_t array[array_size] = {0x2005 + (uint32_t(0x140F) << 16), 0x0008 + (uint32_t(0x1111) << 16), 0x0008 + (uint32_t(0x1115) << 16), 0x0008 + (uint32_t(0x1140) << 16), 0x0008 + (uint32_t(0x1199) << 16), 0x0008 + (uint32_t(0x2218) << 16), 0x0008 + (uint32_t(0x9092) << 16), 0x0018 + (uint32_t(0x9006) << 16), 0x0018 + (uint32_t(0x9042) << 16), 0x0018 + (uint32_t(0x9045) << 16), 0x0018 + (uint32_t(0x9049) << 16), 0x0018 + (uint32_t(0x9112) << 16), 0x0018 + (uint32_t(0x9114) << 16), 0x0018 + (uint32_t(0x9115) << 16), 0x0018 + (uint32_t(0x9117) << 16), 0x0018 + (uint32_t(0x9119) << 16), 0x0018 + (uint32_t(0x9125) << 16), 0x0018 + (uint32_t(0x9152) << 16), 0x0018 + (uint32_t(0x9176) << 16), 0x0018 + (uint32_t(0x9226) << 16), 0x0018 + (uint32_t(0x9239) << 16), 0x0020 + (uint32_t(0x9071) << 16), 0x0020 + (uint32_t(0x9111) << 16), 0x0020 + (uint32_t(0x9113) << 16), 0x0020 + (uint32_t(0x9116) << 16), 0x0020 + (uint32_t(0x9221) << 16), 0x0020 + (uint32_t(0x9222) << 16), 0x0028 + (uint32_t(0x9110) << 16), 0x0028 + (uint32_t(0x9132) << 16), 0x0028 + (uint32_t(0x9145) << 16), 0x0040 + (uint32_t(0x0260) << 16), 0x0040 + (uint32_t(0x0555) << 16), 0x0040 + (uint32_t(0xa170) << 16), 0x5200 + (uint32_t(0x9229) << 16), 0x5200 + (uint32_t(0x9230) << 16)};
3645 	for (int i = 0; i < array_size; i++) {
3646 		//if (array[i] == groupElement) printMessage(" implicitSQ %04x,%04x\n",  groupElement & 65535,groupElement>>16);
3647 		if (array[i] == groupElement)
3648 			return 1;
3649 	}
3650 	return 0;
3651 } //isSQ()
3652 
3653 int isDICOMfile(const char *fname) { //0=NotDICOM, 1=DICOM, 2=Maybe(not Part 10 compliant)
3654 	//Someday: it might be worthwhile to detect "IMGF" at offset 3228 to warn user if they attempt to convert Signa data
3655 	FILE *fp = fopen(fname, "rb");
3656 	if (!fp)
3657 		return 0;
3658 	fseek(fp, 0, SEEK_END);
3659 	long fileLen = ftell(fp);
3660 	if (fileLen < 256) {
3661 		fclose(fp);
3662 		return 0;
3663 	}
3664 	fseek(fp, 0, SEEK_SET);
3665 	unsigned char buffer[256];
3666 	size_t sz = fread(buffer, 1, 256, fp);
3667 	fclose(fp);
3668 	if (sz < 256)
3669 		return 0;
3670 	if ((buffer[128] == 'D') && (buffer[129] == 'I') && (buffer[130] == 'C') && (buffer[131] == 'M'))
3671 		return 1; //valid DICOM
3672 	if ((buffer[0] == 8) && (buffer[1] == 0) && (buffer[3] == 0))
3673 		return 2; //not valid Part 10 file, perhaps DICOM object
3674 	return 0;
3675 } //isDICOMfile()
3676 
3677 //START RIR 12/2017 Robert I. Reid
3678 
3679 // Gathering spot for all the info needed to get the b value and direction
3680 // for a volume.
3681 struct TVolumeDiffusion {
3682 	struct TDICOMdata *pdd; // The multivolume
3683 	struct TDTI4D *pdti4D;	// permanent records.
3684 	uint8_t manufacturer; // kMANUFACTURER_UNKNOWN, kMANUFACTURER_SIEMENS, etc.
3685 
3686 	//void set_manufacturer(const uint8_t m) {manufacturer = m; update();} // unnecessary
3687 
3688 	// Everything after this in the structure would be private if it were a C++
3689 	// class, but it has been rewritten as a struct for C compatibility. I am
3690 	// using _ as a hint of that, although _ for privacy is not really a
3691 	// universal convention in C. Privacy is desired because immediately
3692 	// any of these are updated _update_tvd() should be called.
3693 
3694 	bool _isAtFirstPatientPosition; // Limit b vals and vecs to 1 per volume.
3695 
3696 	//float bVal0018_9087; // kDiffusion_b_value, always present in Philips/Siemens.
3697 	//float bVal2001_1003; // kDiffusionBFactor
3698 	// float dirRL2005_10b0; // kDiffusionDirectionRL
3699 	// float dirAP2005_10b1; // kDiffusionDirectionAP
3700 	// float dirFH2005_10b2; // kDiffusionDirectionFH
3701 	// Philips diffusion scans tend to have a "trace" (average of the diffusion
3702 	// weighted volumes) volume tacked on, usually but not always at the end,
3703 	// so b is > 0, but the direction is meaningless.  Most software versions
3704 	// explicitly set the direction to 0, but version 3 does not, making (0x18,
3705 	// 0x9075) necessary.
3706 	bool _isPhilipsNonDirectional;
3707 	//char _directionality0018_9075[16]; // DiffusionDirectionality, not in Philips 2.6.
3708 	// float _orientation0018_9089[3]; // kDiffusionOrientation, always present in Philips/Siemens for volumes with a direction.
3709 	//char _seq0018_9117[64]; // MRDiffusionSequence, not in Philips 2.6.
3710 	float _dtiV[4];
3711 	double _symBMatrix[6];
3712 	//uint16_t numDti;
3713 };
3714 struct TVolumeDiffusion initTVolumeDiffusion(struct TDICOMdata *ptdd, struct TDTI4D *dti4D);
3715 void clear_volume(struct TVolumeDiffusion *ptvd); // Blank the volume-specific members or set them to impossible values.
3716 void set_directionality0018_9075(struct TVolumeDiffusion *ptvd, unsigned char *inbuf);
3717 void set_orientation0018_9089(struct TVolumeDiffusion *ptvd, int lLength, unsigned char *inbuf, bool isLittleEndian);
3718 void set_isAtFirstPatientPosition_tvd(struct TVolumeDiffusion *ptvd, bool iafpp);
3719 int set_bValGE(struct TVolumeDiffusion *ptvd, int lLength, unsigned char *inbuf);
3720 void set_diffusion_directionPhilips(struct TVolumeDiffusion *ptvd, float vec, const int axis);
3721 void set_diffusion_directionGE(struct TVolumeDiffusion *ptvd, int lLength, unsigned char *inbuf, int axis);
3722 void set_bVal(struct TVolumeDiffusion *ptvd, float b);
3723 void set_bMatrix(struct TVolumeDiffusion *ptvd, float b, int component);
3724 void _update_tvd(struct TVolumeDiffusion *ptvd);
3725 
3726 struct TVolumeDiffusion initTVolumeDiffusion(struct TDICOMdata *ptdd, struct TDTI4D *dti4D) {
3727 	struct TVolumeDiffusion tvd;
3728 	tvd.pdd = ptdd;
3729 	tvd.pdti4D = dti4D;
3730 	clear_volume(&tvd);
3731 	return tvd;
3732 } //initTVolumeDiffusion()
3733 
3734 void clear_volume(struct TVolumeDiffusion *ptvd) {
3735 	ptvd->_isAtFirstPatientPosition = false;
3736 	ptvd->manufacturer = kMANUFACTURER_UNKNOWN;
3737 	//bVal0018_9087 = -1;
3738 	//ptvd->_directionality0018_9075[0] = 0;
3739 	//ptvd->seq0018_9117[0] = 0;
3740 	//bVal2001_1003 = -1;
3741 	// dirRL2005_10b0 = 2;
3742 	// dirAP2005_10b1 = 2;
3743 	// dirFH2005_10b2 = 2;
3744 	ptvd->_isPhilipsNonDirectional = false;
3745 	ptvd->_dtiV[0] = -1;
3746 	for (int i = 1; i < 4; ++i)
3747 		ptvd->_dtiV[i] = 2;
3748 	for (int i = 1; i < 6; ++i)
3749 		ptvd->_symBMatrix[i] = NAN;
3750 	//numDti = 0;
3751 } //clear_volume()
3752 
3753 void set_directionality0018_9075(struct TVolumeDiffusion *ptvd, unsigned char *inbuf) {
3754 	//if(!strncmp(( char*)(inbuf), "BMATRIX", 4)) printf("FOUND BMATRIX----%s\n",inbuf );
3755 	//n.b. strncmp returns 0 if the contents of both strings are equal, for boolean 0 = false!
3756 	// elsewhere we use strstr() which returns 0/null if match is not present
3757 	if (strncmp((char *)(inbuf), "DIRECTIONAL", 11) && // strncmp = 0 for ==.
3758 		//strncmp(( char*)(inbuf), "NONE", 4) && //issue 256
3759 		strncmp((char *)(inbuf), "BMATRIX", 7)) { // Siemens XA10
3760 		ptvd->_isPhilipsNonDirectional = true;
3761 		// Explicitly set the direction to 0 now, because there may
3762 		// not be a 0018,9089 for this frame.
3763 		for (int i = 1; i < 4; ++i) // 1-3 is intentional.
3764 			ptvd->_dtiV[i] = 0.0;
3765 	} else {
3766 		ptvd->_isPhilipsNonDirectional = false;
3767 		// Wait for 0018,9089 to get the direction.
3768 	}
3769 	_update_tvd(ptvd);
3770 } //set_directionality0018_9075()
3771 
3772 int set_bValGE(struct TVolumeDiffusion *ptvd, int lLength, unsigned char *inbuf) {
3773 	//see Series 16 https://github.com/nikadon/cc-dcm2bids-wrapper/tree/master/dicom-qa-examples/ge-mr750-dwi-b-vals#table b750 = 1000000750\8\0\0 b1500 = 1000001500\8\0\0
3774 	int bVal = dcmStrInt(lLength, inbuf);
3775 	bVal = (bVal % 10000);
3776 	ptvd->_dtiV[0] = bVal;
3777 	//printf("(0043,1039) '%s' Slop_int_6 -->%d \n", inbuf, bVal);
3778 	//dd.CSA.numDti = 1; // Always true for GE.
3779 	_update_tvd(ptvd);
3780 	return bVal;
3781 } //set_bValGE()
3782 
3783 // axis: 0 -> x, 1 -> y , 2 -> z
3784 void set_diffusion_directionPhilips(struct TVolumeDiffusion *ptvd, float vec, const int axis) {
3785 	ptvd->_dtiV[axis + 1] = vec;
3786 	//printf("(2005,10b0..2) v[%d]=%g\n", axis, ptvd->_dtiV[axis + 1]);
3787 	_update_tvd(ptvd);
3788 } //set_diffusion_directionPhilips()
3789 
3790 // axis: 0 -> x, 1 -> y , 2 -> z
3791 void set_diffusion_directionGE(struct TVolumeDiffusion *ptvd, int lLength, unsigned char *inbuf, const int axis) {
3792 	ptvd->_dtiV[axis + 1] = dcmStrFloat(lLength, inbuf);
3793 	//printf("(0019,10bb..d) v[%d]=%g\n", axis, ptvd->_dtiV[axis + 1]);
3794 	_update_tvd(ptvd);
3795 } //set_diffusion_directionGE()
3796 
3797 void dcmMultiFloatDouble(size_t lByteLength, unsigned char lBuffer[], size_t lnFloats, float *lFloats, bool isLittleEndian) {
3798 	size_t floatlen = lByteLength / lnFloats;
3799 	for (size_t i = 0; i < lnFloats; ++i)
3800 		lFloats[i] = dcmFloatDouble((int)floatlen, lBuffer + i * floatlen, isLittleEndian);
3801 } //dcmMultiFloatDouble()
3802 
3803 void set_orientation0018_9089(struct TVolumeDiffusion *ptvd, int lLength, unsigned char *inbuf, bool isLittleEndian) {
3804 	if (ptvd->_isPhilipsNonDirectional) {
3805 		for (int i = 1; i < 4; ++i) // Deliberately ignore inbuf; it might be nonsense.
3806 			ptvd->_dtiV[i] = 0.0;
3807 	} else
3808 		dcmMultiFloatDouble(lLength, inbuf, 3, ptvd->_dtiV + 1, isLittleEndian);
3809 	_update_tvd(ptvd);
3810 } //set_orientation0018_9089()
3811 
3812 void set_bVal(struct TVolumeDiffusion *ptvd, const float b) {
3813 	ptvd->_dtiV[0] = b;
3814 	_update_tvd(ptvd);
3815 } //set_bVal()
3816 
3817 void set_bMatrix(struct TVolumeDiffusion *ptvd, double b, int idx) {
3818 	if ((idx < 0) || (idx > 5))
3819 		return;
3820 	ptvd->_symBMatrix[idx] = b;
3821 	_update_tvd(ptvd);
3822 }
3823 
3824 void set_isAtFirstPatientPosition_tvd(struct TVolumeDiffusion *ptvd, const bool iafpp) {
3825 	ptvd->_isAtFirstPatientPosition = iafpp;
3826 	_update_tvd(ptvd);
3827 } //set_isAtFirstPatientPosition_tvd()
3828 
3829 // Update the diffusion info in dd and *pdti4D for a volume once all the
3830 // diffusion info for that volume has been read into pvd.
3831 //
3832 // Note that depending on the scanner software the diffusion info can arrive in
3833 // different tags, in different orders (because of enclosing sequence tags),
3834 // and the values in some tags may be invalid, or may be essential, depending
3835 // on the presence of other tags. Thus it is best to gather all the diffusion
3836 // info for a volume (frame) before taking action on it.
3837 //
3838 // On the other hand, dd and *pdti4D need to be updated as soon as the
3839 // diffusion info is ready, before diffusion info for the next volume is read
3840 // in.
3841 void _update_tvd(struct TVolumeDiffusion *ptvd) {
3842 	// Figure out if we have both the b value and direction (if any) for this
3843 	// volume, and if isFirstPosition.
3844 	// // GE (software version 27) is liable to NOT include kDiffusion_b_value for the
3845 	// // slice if it is 0, but should still have kDiffusionBFactor, which comes
3846 	// // after PatientPosition.
3847 	// if(isAtFirstPatientPosition && manufacturer == kMANUFACTURER_GE && dtiV[0] < 0)
3848 	// dtiV[0] = 0; // Implied 0.
3849 	bool isReady = (ptvd->_isAtFirstPatientPosition && (ptvd->_dtiV[0] >= 0));
3850 	if (!isReady)
3851 		return; //no B=0
3852 	if (isReady) {
3853 		for (int i = 1; i < 4; ++i) {
3854 			if (ptvd->_dtiV[i] > 1) {
3855 				isReady = false;
3856 				break;
3857 			}
3858 		}
3859 	}
3860 	if (!isReady) { //bvecs NOT filled: see if symBMatrix filled
3861 		isReady = true;
3862 		for (int i = 1; i < 6; ++i)
3863 			if (isnan(ptvd->_symBMatrix[i]))
3864 				isReady = false;
3865 		if (!isReady)
3866 			return; //	symBMatrix not filled
3867 		//START BRUKER KLUDGE
3868 		//see issue 265: Bruker stores xx,xy,xz,yx,yy,yz instead of xx,xy,xz,yy,yz,zz
3869 		// we can recover since xx+yy+zz = bval
3870 		// since any value squared is positive, a negative diagonal reveals fault
3871 		double xx = ptvd->_symBMatrix[0]; //x*x
3872 		double xy = ptvd->_symBMatrix[1]; //x*y
3873 		double xz = ptvd->_symBMatrix[2]; //x*z
3874 		double yy = ptvd->_symBMatrix[3]; //y*y
3875 		double yz = ptvd->_symBMatrix[4]; //y*z
3876 		double zz = ptvd->_symBMatrix[5]; //z*z
3877 		bool isBrukerBug = false;
3878 		if ((xx < 0.0) || (yy < 0.0) || (zz < 0.0))
3879 			isBrukerBug = true;
3880 		double sumDiag = ptvd->_symBMatrix[0] + ptvd->_symBMatrix[3] + ptvd->_symBMatrix[5]; //if correct xx+yy+zz = bval
3881 		double bVecError = fabs(sumDiag - ptvd->pdd->CSA.dtiV[0]);
3882 		if (bVecError > 0.5)
3883 			isBrukerBug = true;
3884 		//next: check diagonals
3885 		double x = sqrt(xx);
3886 		double y = sqrt(yy);
3887 		double z = sqrt(zz);
3888 		if ((fabs((x * y) - xy)) > 0.5)
3889 			isBrukerBug = true;
3890 		if ((fabs((x * z) - xz)) > 0.5)
3891 			isBrukerBug = true;
3892 		if ((fabs((y * z) - yz)) > 0.5)
3893 			isBrukerBug = true;
3894 		if (isBrukerBug)
3895 			printWarning("Fixing corrupt bmat (issue 265). [%g %g %g %g %g %g]\n", xx, xy, xz, yy, yz, zz);
3896 		if (isBrukerBug) {
3897 			ptvd->_symBMatrix[3] = ptvd->_symBMatrix[4];
3898 			ptvd->_symBMatrix[4] = ptvd->_symBMatrix[5];
3899 			//next: solve for zz given bvalue, xx, and yy
3900 			ptvd->_symBMatrix[5] = ptvd->_dtiV[0] - ptvd->_symBMatrix[0] - ptvd->_symBMatrix[3];
3901 			if ((ptvd->_symBMatrix[0] < 0.0) || (ptvd->_symBMatrix[5] < 0.0))
3902 				printError("DICOM BMatrix corrupt.\n");
3903 		}
3904 		//END BRUKER_KLUDGE
3905 		vec3 bVec = nifti_mat33_eig3(ptvd->_symBMatrix[0], ptvd->_symBMatrix[1], ptvd->_symBMatrix[2], ptvd->_symBMatrix[3], ptvd->_symBMatrix[4], ptvd->_symBMatrix[5]);
3906 		ptvd->_dtiV[1] = bVec.v[0];
3907 		ptvd->_dtiV[2] = bVec.v[1];
3908 		ptvd->_dtiV[3] = bVec.v[2];
3909 		//printf("bmat=[%g %g %g %g %g %g %g %g %g]\n", ptvd->_symBMatrix[0],ptvd->_symBMatrix[1],ptvd->_symBMatrix[2], ptvd->_symBMatrix[1],ptvd->_symBMatrix[3],ptvd->_symBMatrix[4], ptvd->_symBMatrix[2],ptvd->_symBMatrix[4],ptvd->_symBMatrix[5]);
3910 		//printf("bmats=[%g %g %g %g %g %g];\n", ptvd->_symBMatrix[0],ptvd->_symBMatrix[1],ptvd->_symBMatrix[2],ptvd->_symBMatrix[3],ptvd->_symBMatrix[4],ptvd->_symBMatrix[5]);
3911 		//printf("bvec=[%g %g %g];\n", ptvd->_dtiV[1], ptvd->_dtiV[2], ptvd->_dtiV[3]);
3912 		//printf("bval=%g;\n\n", ptvd->_dtiV[0]);
3913 	}
3914 	if (!isReady)
3915 		return;
3916 	// If still here, update dd and *pdti4D.
3917 	ptvd->pdd->CSA.numDti++;
3918 	if (ptvd->pdd->CSA.numDti == 2) { // First time we know that this is a 4D DTI dataset;
3919 		for (int i = 0; i < 4; ++i) // Start *pdti4D before ptvd->pdd->CSA.dtiV
3920 			ptvd->pdti4D->S[0].V[i] = ptvd->pdd->CSA.dtiV[i]; // is updated.
3921 	}
3922 	for (int i = 0; i < 4; ++i) // Update pdd
3923 		ptvd->pdd->CSA.dtiV[i] = ptvd->_dtiV[i];
3924 	if ((ptvd->pdd->CSA.numDti > 1) && (ptvd->pdd->CSA.numDti < kMaxDTI4D)) { // Update *pdti4D
3925 		//d.dti4D = (TDTI *)malloc(kMaxDTI4D * sizeof(TDTI));
3926 		for (int i = 0; i < 4; ++i)
3927 			ptvd->pdti4D->S[ptvd->pdd->CSA.numDti - 1].V[i] = ptvd->_dtiV[i];
3928 	}
3929 	clear_volume(ptvd); // clear the slate for the next volume.
3930 } //_update_tvd()
3931 //END RIR
3932 
3933 struct TDCMdim { //DimensionIndexValues
3934 	uint32_t dimIdx[MAX_NUMBER_OF_DIMENSIONS];
3935 	uint32_t diskPos;
3936 	float triggerDelayTime, TE, intenScale, intenIntercept, intenScalePhilips, RWVScale, RWVIntercept;
3937 	float V[4];
3938 	bool isPhase;
3939 	bool isReal;
3940 	bool isImaginary;
3941 };
3942 
3943 void getFileNameX(char *pathParent, const char *path, int maxLen) { //if path is c:\d1\d2 then filename is 'd2'
3944 	const char *filename = strrchr(path, '/'); //UNIX
3945 	const char *filenamew = strrchr(path, '\\'); //Windows
3946 	if (filename == NULL)
3947 		filename = filenamew;
3948 	//if ((filename != NULL) && (filenamew != NULL)) filename = std::max(filename, filenamew);
3949 	if ((filename != NULL) && (filenamew != NULL) && (filenamew > filename))
3950 		filename = filenamew; //for mixed file separators, e.g. "C:/dir\filenane.tmp"
3951 	if (filename == NULL) { //no path separator
3952 		strcpy(pathParent, path);
3953 		return;
3954 	}
3955 	filename++;
3956 	strncpy(pathParent, filename, maxLen - 1);
3957 }
3958 
3959 void getFileName(char *pathParent, const char *path) { //if path is c:\d1\d2 then filename is 'd2'
3960 	getFileNameX(pathParent, path, kDICOMStr);
3961 }
3962 
3963 struct fidx {
3964 	float value;
3965 	int index;
3966 };
3967 
3968 int fcmp(const void *a, const void *b) {
3969 	struct fidx *a1 = (struct fidx *)a;
3970 	struct fidx *a2 = (struct fidx *)b;
3971 	if ((*a1).value > (*a2).value)
3972 		return 1;
3973 	else if ((*a1).value < (*a2).value)
3974 		return -1;
3975 	else
3976 		return 0;
3977 }
3978 
3979 #ifdef USING_R
3980 
3981 // True iff dcm1 sorts *before* dcm2
3982 bool compareTDCMdim(const TDCMdim &dcm1, const TDCMdim &dcm2) {
3983 	for (int i = MAX_NUMBER_OF_DIMENSIONS - 1; i >= 0; i--) {
3984 		if (dcm1.dimIdx[i] < dcm2.dimIdx[i])
3985 			return true;
3986 		else if (dcm1.dimIdx[i] > dcm2.dimIdx[i])
3987 			return false;
3988 	}
3989 	return false;
3990 } //compareTDCMdim()
3991 
3992 bool compareTDCMdimRev(const TDCMdim &dcm1, const TDCMdim &dcm2) {
3993 	for (int i = 0; i < MAX_NUMBER_OF_DIMENSIONS; i++) {
3994 		if (dcm1.dimIdx[i] < dcm2.dimIdx[i])
3995 			return true;
3996 		else if (dcm1.dimIdx[i] > dcm2.dimIdx[i])
3997 			return false;
3998 	}
3999 	return false;
4000 } //compareTDCMdimRev()
4001 
4002 #else
4003 
4004 int compareTDCMdim(void const *item1, void const *item2) {
4005 	struct TDCMdim const *dcm1 = (const struct TDCMdim *)item1;
4006 	struct TDCMdim const *dcm2 = (const struct TDCMdim *)item2;
4007 	//for (int i = 0; i < MAX_NUMBER_OF_DIMENSIONS; i++) {
4008 	for (int i = MAX_NUMBER_OF_DIMENSIONS - 1; i >= 0; i--) {
4009 		if (dcm1->dimIdx[i] < dcm2->dimIdx[i])
4010 			return -1;
4011 		else if (dcm1->dimIdx[i] > dcm2->dimIdx[i])
4012 			return 1;
4013 	}
4014 	return 0;
4015 } //compareTDCMdim()
4016 
4017 int compareTDCMdimRev(void const *item1, void const *item2) {
4018 	struct TDCMdim const *dcm1 = (const struct TDCMdim *)item1;
4019 	struct TDCMdim const *dcm2 = (const struct TDCMdim *)item2;
4020 	for (int i = 0; i < MAX_NUMBER_OF_DIMENSIONS; i++) {
4021 		if (dcm1->dimIdx[i] < dcm2->dimIdx[i])
4022 			return -1;
4023 		else if (dcm1->dimIdx[i] > dcm2->dimIdx[i])
4024 			return 1;
4025 	}
4026 	return 0;
4027 } //compareTDCMdimRev()
4028 
4029 #endif // USING_R
4030 
4031 struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D *dti4D) {
4032 	//struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, struct TDTI4D *dti4D) {
4033 	int isVerbose = prefs->isVerbose;
4034 	int compressFlag = prefs->compressFlag;
4035 	struct TDICOMdata d = clear_dicom_data();
4036 	d.imageNum = 0; //not set
4037 	strcpy(d.protocolName, ""); //erase dummy with empty
4038 	strcpy(d.protocolName, ""); //erase dummy with empty
4039 	strcpy(d.seriesDescription, ""); //erase dummy with empty
4040 	strcpy(d.sequenceName, ""); //erase dummy with empty
4041 	//do not read folders - code specific to GCC (LLVM/Clang seems to recognize a small file size)
4042 	dti4D->sliceOrder[0] = -1;
4043 	dti4D->volumeOnsetTime[0] = -1;
4044 	dti4D->decayFactor[0] = -1;
4045 	dti4D->frameDuration[0] = -1;
4046 	//dti4D->fragmentOffset[0] = -1;
4047 	dti4D->intenScale[0] = 0.0;
4048 	struct TVolumeDiffusion volDiffusion = initTVolumeDiffusion(&d, dti4D);
4049 	struct stat s;
4050 	if (stat(fname, &s) == 0) {
4051 		if (!(s.st_mode & S_IFREG)) {
4052 			printMessage("DICOM read fail: not a valid file (perhaps a directory) %s\n", fname);
4053 			return d;
4054 		}
4055 	}
4056 	bool isPart10prefix = true;
4057 	int isOK = isDICOMfile(fname);
4058 	if (isOK == 0)
4059 		return d;
4060 	if (isOK == 2) {
4061 		d.isExplicitVR = false;
4062 		isPart10prefix = false;
4063 	}
4064 	FILE *file = fopen(fname, "rb");
4065 	if (!file) {
4066 		printMessage("Unable to open file %s\n", fname);
4067 		return d;
4068 	}
4069 	fseek(file, 0, SEEK_END);
4070 	long fileLen = ftell(file); //Get file length
4071 	if (fileLen < 256) {
4072 		printMessage("File too small to be a DICOM image %s\n", fname);
4073 		return d;
4074 	}
4075 //Since size of DICOM header is unknown, we will load it in 1mb segments
4076 //This uses less RAM and makes is faster for computers with slow disk access
4077 //Benefit is largest for 4D images.
4078 //To disable caching and load entire file to RAM, compile with "-dmyLoadWholeFileToReadHeader"
4079 //To implement the segments, we define these variables:
4080 // fileLen = size of file in bytes
4081 // MaxBufferSz = maximum size of buffer in bytes
4082 // Buffer = array with n elements, where n is smaller of fileLen or MaxBufferSz
4083 // lPos = position in Buffer (indexed from 0), 0..(n-1)
4084 // lFileOffset = offset of Buffer in file: true file position is lOffset+lPos (initially 0)
4085 #ifdef myLoadWholeFileToReadHeader
4086 	size_t MaxBufferSz = fileLen;
4087 #else
4088 	size_t MaxBufferSz = 1000000; //ideally size of DICOM header, but this varies from 2D to 4D files
4089 #endif
4090 	if (MaxBufferSz > (size_t)fileLen)
4091 		MaxBufferSz = fileLen;
4092 	//printf("%d -> %d\n", MaxBufferSz, fileLen);
4093 	long lFileOffset = 0;
4094 	fseek(file, 0, SEEK_SET);
4095 	//Allocate memory
4096 	unsigned char *buffer = (unsigned char *)malloc(MaxBufferSz + 1);
4097 	if (!buffer) {
4098 		printError("Memory exhausted!");
4099 		fclose(file);
4100 		return d;
4101 	}
4102 	//Read file contents into buffer
4103 	size_t sz = fread(buffer, 1, MaxBufferSz, file);
4104 	if (sz < MaxBufferSz) {
4105 		printError("Only loaded %zu of %zu bytes for %s\n", sz, MaxBufferSz, fname);
4106 		fclose(file);
4107 		return d;
4108 	}
4109 #ifdef myLoadWholeFileToReadHeader
4110 	fclose(file);
4111 #endif
4112 	//DEFINE DICOM TAGS
4113 #define kUnused 0x0001 + (0x0001 << 16)
4114 #define kStart 0x0002 + (0x0000 << 16)
4115 #define kMediaStorageSOPClassUID 0x0002 + (0x0002 << 16)
4116 #define kMediaStorageSOPInstanceUID 0x0002 + (0x0003 << 16)
4117 #define kTransferSyntax 0x0002 + (0x0010 << 16)
4118 #define kImplementationVersionName 0x0002 + (0x0013 << 16)
4119 #define kSourceApplicationEntityTitle 0x0002 + (0x0016 << 16)
4120 #define kDirectoryRecordSequence 0x0004 + (0x1220 << 16)
4121 //#define kSpecificCharacterSet 0x0008+(0x0005 << 16 ) //someday we should handle foreign characters...
4122 #define kImageTypeTag 0x0008 + (0x0008 << 16)
4123 //#define kSOPInstanceUID 0x0008+(0x0018 << 16 ) //Philips inserts time as last item, e.g. ?.?.?.YYYYMMDDHHmmSS.SSSS
4124 // not reliable https://neurostars.org/t/heudiconv-no-extraction-of-slice-timing-data-based-on-philips-dicoms/2201/21
4125 #define kStudyDate 0x0008 + (0x0020 << 16)
4126 #define kAcquisitionDate 0x0008 + (0x0022 << 16)
4127 #define kAcquisitionDateTime 0x0008 + (0x002A << 16)
4128 #define kStudyTime 0x0008 + (0x0030 << 16)
4129 #define kSeriesTime 0x0008 + (0x0031 << 16)
4130 #define kAcquisitionTime 0x0008 + (0x0032 << 16) //TM
4131 //#define kContentTime 0x0008+(0x0033 << 16 ) //TM
4132 #define kModality 0x0008 + (0x0060 << 16) //CS
4133 #define kManufacturer 0x0008 + (0x0070 << 16)
4134 #define kInstitutionName 0x0008 + (0x0080 << 16)
4135 #define kInstitutionAddress 0x0008 + (0x0081 << 16)
4136 #define kReferringPhysicianName 0x0008 + (0x0090 << 16)
4137 #define kStationName 0x0008 + (0x1010 << 16)
4138 #define kSeriesDescription 0x0008 + (0x103E << 16) // '0008' '103E' 'LO' 'SeriesDescription'
4139 #define kInstitutionalDepartmentName 0x0008 + (0x1040 << 16)
4140 #define kManufacturersModelName 0x0008 + (0x1090 << 16)
4141 #define kDerivationDescription 0x0008 + (0x2111 << 16)
4142 #define kComplexImageComponent (uint32_t)0x0008 + (0x9208 << 16) //'0008' '9208' 'CS' 'ComplexImageComponent'
4143 #define kAcquisitionContrast (uint32_t)0x0008 + (0x9209 << 16) //'0008' '9209' 'CS' 'AcquisitionContrast'
4144 #define kPatientName 0x0010 + (0x0010 << 16)
4145 #define kPatientID 0x0010 + (0x0020 << 16)
4146 #define kAccessionNumber 0x0008 + (0x0050 << 16)
4147 #define kPatientBirthDate 0x0010 + (0x0030 << 16)
4148 #define kPatientSex 0x0010 + (0x0040 << 16)
4149 #define kPatientAge 0x0010 + (0x1010 << 16)
4150 #define kPatientWeight 0x0010 + (0x1030 << 16)
4151 #define kAnatomicalOrientationType 0x0010 + (0x2210 << 16)
4152 #define kDeidentificationMethod 0x0012 + (0x0063 << 16) //[DICOMANON, issue 383
4153 #define kBodyPartExamined 0x0018 + (0x0015 << 16)
4154 #define kBodyPartExamined 0x0018 + (0x0015 << 16)
4155 #define kScanningSequence 0x0018 + (0x0020 << 16)
4156 #define kSequenceVariant 0x0018 + (0x0021 << 16)
4157 #define kScanOptions 0x0018 + (0x0022 << 16)
4158 #define kMRAcquisitionType 0x0018 + (0x0023 << 16)
4159 #define kSequenceName 0x0018 + (0x0024 << 16)
4160 #define kRadiopharmaceutical 0x0018 + (0x0031 << 16) //LO
4161 #define kZThick 0x0018 + (0x0050 << 16)
4162 #define kTR 0x0018 + (0x0080 << 16)
4163 #define kTE 0x0018 + (0x0081 << 16)
4164 #define kTI 0x0018 + (0x0082 << 16) // Inversion time
4165 #define kNumberOfAverages 0x0018 + (0x0083 << 16) //DS
4166 #define kImagingFrequency 0x0018 + (0x0084 << 16) //DS
4167 //#define kEffectiveTE 0x0018+(0x9082 << 16 )
4168 #define kEchoNum 0x0018 + (0x0086 << 16) //IS
4169 #define kMagneticFieldStrength 0x0018 + (0x0087 << 16) //DS
4170 #define kZSpacing 0x0018 + (0x0088 << 16) //'DS' 'SpacingBetweenSlices'
4171 #define kPhaseEncodingSteps 0x0018 + (0x0089 << 16) //'IS'
4172 #define kEchoTrainLength 0x0018 + (0x0091 << 16) //IS
4173 #define kPercentSampling 0x0018 + (0x0093 << 16) //'DS'
4174 #define kPhaseFieldofView 0x0018 + (0x0094 << 16) //'DS'
4175 #define kPixelBandwidth 0x0018 + (0x0095 << 16) //'DS' 'PixelBandwidth'
4176 #define kDeviceSerialNumber 0x0018 + (0x1000 << 16) //LO
4177 #define kSoftwareVersions 0x0018 + (0x1020 << 16) //LO
4178 #define kProtocolName 0x0018 + (0x1030 << 16)
4179 #define kTriggerTime 0x0018 + (0x1060 << 16) //DS
4180 #define kRadionuclideTotalDose 0x0018 + (0x1074 << 16)
4181 #define kRadionuclideHalfLife 0x0018 + (0x1075 << 16)
4182 #define kRadionuclidePositronFraction 0x0018 + (0x1076 << 16)
4183 #define kGantryTilt 0x0018 + (0x1120 << 16)
4184 #define kXRayTimeMS 0x0018 + (0x1150 << 16) //IS
4185 #define kXRayTubeCurrent 0x0018 + (0x1151 << 16) //IS
4186 #define kXRayExposure 0x0018 + (0x1152 << 16)
4187 #define kConvolutionKernel 0x0018 + (0x1210 << 16) //SH
4188 #define kFrameDuration 0x0018 + (0x1242 << 16) //IS
4189 #define kReceiveCoilName 0x0018 + (0x1250 << 16) // SH
4190 //#define kTransmitCoilName 0x0018 + (0x1251 << 16) // SH issue527
4191 #define kAcquisitionMatrix 0x0018 + (0x1310 << 16) //US
4192 #define kFlipAngle 0x0018 + (0x1314 << 16)
4193 #define kInPlanePhaseEncodingDirection 0x0018 + (0x1312 << 16) //CS
4194 #define kSAR 0x0018 + (0x1316 << 16) //'DS' 'SAR'
4195 #define kPatientOrient 0x0018 + (0x5100 << 16) //0018,5100. patient orientation - 'HFS'
4196 #define kInversionRecovery 0x0018 + uint32_t(0x9009 << 16) //'CS' 'YES'/'NO'
4197 #define kSpoiling 0x0018 + uint32_t(0x9016 << 16) //'CS'
4198 #define kEchoPlanarPulseSequence 0x0018 + uint32_t(0x9018 << 16) //'CS' 'YES'/'NO'
4199 #define kMagnetizationTransferAttribute 0x0018 + uint32_t(0x9020 << 16) //'CS' 'ON_RESONANCE','OFF_RESONANCE','NONE'
4200 #define kRectilinearPhaseEncodeReordering 0x0018 + uint32_t(0x9034 << 16) //'CS' 'REVERSE_LINEAR'/'LINEAR'
4201 #define kPartialFourierDirection 0x0018 + uint32_t(0x9036 << 16) //'CS'
4202 #define kCardiacSynchronizationTechnique 0x0018 + uint32_t(0x9037 << 16) //'CS'
4203 #define kParallelReductionFactorInPlane 0x0018 + uint32_t(0x9069 << 16) //FD
4204 #define kAcquisitionDuration 0x0018 + uint32_t(0x9073 << 16) //FD
4205 //#define kFrameAcquisitionDateTime 0x0018+uint32_t(0x9074<< 16 ) //DT "20181019212528.232500"
4206 #define kDiffusionDirectionality 0x0018 + uint32_t(0x9075 << 16) // NONE, ISOTROPIC, or DIRECTIONAL
4207 #define kParallelAcquisitionTechnique 0x0018 + uint32_t(0x9078 << 16) //CS: SENSE, SMASH
4208 #define kInversionTimes 0x0018 + uint32_t(0x9079 << 16) //FD
4209 #define kPartialFourier 0x0018 + uint32_t(0x9081 << 16) //CS
4210 const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16);
4211 //#define kDiffusionBFactorSiemens 0x0019+(0x100C<< 16 ) // 0019;000C;SIEMENS MR HEADER;B_value
4212 #define kDiffusion_bValue 0x0018 + uint32_t(0x9087 << 16) // FD
4213 #define kDiffusionOrientation 0x0018 + uint32_t(0x9089 << 16) // FD, seen in enhanced DICOM from Philips 5.* and Siemens XA10.
4214 #define kImagingFrequency2 0x0018 + uint32_t(0x9098 << 16) //FD
4215 #define kParallelReductionFactorOutOfPlane 0x0018 + uint32_t(0x9155 << 16) //FD
4216 //#define kFrameAcquisitionDuration 0x0018+uint32_t(0x9220 << 16 ) //FD
4217 #define kDiffusionBValueXX 0x0018 + uint32_t(0x9602 << 16) //FD
4218 #define kDiffusionBValueXY 0x0018 + uint32_t(0x9603 << 16) //FD
4219 #define kDiffusionBValueXZ 0x0018 + uint32_t(0x9604 << 16) //FD
4220 #define kDiffusionBValueYY 0x0018 + uint32_t(0x9605 << 16) //FD
4221 #define kDiffusionBValueYZ 0x0018 + uint32_t(0x9606 << 16) //FD
4222 #define kDiffusionBValueZZ 0x0018 + uint32_t(0x9607 << 16) //FD
4223 #define kMREchoSequence 0x0018 + uint32_t(0x9114 << 16) //SQ
4224 #define kMRAcquisitionPhaseEncodingStepsInPlane 0x0018 + uint32_t(0x9231 << 16) //US
4225 #define kNumberOfImagesInMosaic 0x0019 + (0x100A << 16) //US NumberOfImagesInMosaic
4226 //https://nmrimaging.wordpress.com/2011/12/20/when-we-process/
4227 // https://nciphub.org/groups/qindicom/wiki/DiffusionrelatedDICOMtags:experienceacrosssites?action=pdf
4228 #define kDiffusion_bValueSiemens 0x0019 + (0x100C << 16) //IS
4229 #define kDiffusionGradientDirectionSiemens 0x0019 + (0x100E << 16) //FD
4230 #define kSeriesPlaneGE 0x0019 + (0x1017 << 16) //SS
4231 #define kDwellTime 0x0019 + (0x1018 << 16) //IS in NSec, see https://github.com/rordenlab/dcm2niix/issues/127
4232 #define kLastScanLoc 0x0019 + (0x101B << 16)
4233 #define kBandwidthPerPixelPhaseEncode 0x0019 + (0x1028 << 16) //FD
4234 #define kSliceTimeSiemens 0x0019 + (0x1029 << 16) ///FD
4235 #define kPulseSequenceNameGE 0x0019 + (0x109C << 16) //LO 'epiRT' or 'epi'
4236 #define kInternalPulseSequenceNameGE 0x0019 + (0x109E << 16) //LO 'EPI' or 'EPI2'
4237 #define kRawDataRunNumberGE 0x0019 + (0x10A2 << 16)//SL
4238 #define kMaxEchoNumGE 0x0019 + (0x10A9 << 16) //DS
4239 #define kUserData12GE 0x0019 + (0x10B3 << 16) //DS phase diffusion direction
4240 #define kDiffusionDirectionGEX 0x0019 + (0x10BB << 16) //DS phase diffusion direction
4241 #define kDiffusionDirectionGEY 0x0019 + (0x10BC << 16) //DS frequency diffusion direction
4242 #define kDiffusionDirectionGEZ 0x0019 + (0x10BD << 16) //DS slice diffusion direction
4243 #define kNumberOfDiffusionDirectionGE 0x0019 + (0x10E0 << 16) ///DS NumberOfDiffusionDirection:UserData24
4244 #define kStudyID 0x0020 + (0x0010 << 16)
4245 #define kSeriesNum 0x0020 + (0x0011 << 16)
4246 #define kAcquNum 0x0020 + (0x0012 << 16)
4247 #define kImageNum 0x0020 + (0x0013 << 16)
4248 #define kStudyInstanceUID 0x0020 + (0x000D << 16)
4249 #define kSeriesInstanceUID 0x0020 + (0x000E << 16)
4250 #define kImagePositionPatient 0x0020 + (0x0032 << 16) // Actually !
4251 #define kOrientationACR 0x0020 + (0x0035 << 16)
4252 #define kOrientation 0x0020 + (0x0037 << 16)
4253 #define kTemporalPosition 0x0020+(0x0100 << 16 ) //IS
4254 //#define kNumberOfTemporalPositions 0x0020+(0x0105 << 16 ) //IS public tag for NumberOfDynamicScans
4255 #define kTemporalResolution 0x0020 + (0x0110 << 16) //DS
4256 #define kImagesInAcquisition 0x0020 + (0x1002 << 16) //IS
4257 //#define kSliceLocation 0x0020+(0x1041 << 16 ) //DS would be useful if not optional type 3
4258 #define kImageComments 0x0020 + (0x4000 << 16) // '0020' '4000' 'LT' 'ImageComments'
4259 #define kFrameContentSequence 0x0020 + uint32_t(0x9111 << 16) //SQ
4260 #define kTriggerDelayTime 0x0020 + uint32_t(0x9153 << 16) //FD
4261 #define kDimensionIndexValues 0x0020 + uint32_t(0x9157 << 16) // UL n-dimensional index of frame.
4262 #define kInStackPositionNumber 0x0020 + uint32_t(0x9057 << 16) // UL can help determine slices in volume
4263 #define kTemporalPositionIndex 0x0020 + uint32_t(0x9128 << 16) // UL
4264 #define kDimensionIndexPointer 0x0020 + uint32_t(0x9165 << 16)
4265 //Private Group 21 as Used by Siemens:
4266 #define kSequenceVariant21 0x0021 + (0x105B << 16) //CS
4267 #define kPATModeText 0x0021 + (0x1009 << 16) //LO, see kImaPATModeText
4268 #define kTimeAfterStart 0x0021 + (0x1104 << 16) //DS
4269 #define kPhaseEncodingDirectionPositiveSiemens 0x0021 + (0x111C << 16) //IS
4270 //#define kRealDwellTime 0x0021+(0x1142<< 16 )//IS
4271 #define kBandwidthPerPixelPhaseEncode21 0x0021 + (0x1153 << 16) //FD
4272 #define kCoilElements 0x0021 + (0x114F << 16) //LO
4273 #define kAcquisitionMatrixText21 0x0021 + (0x1158 << 16) //SH
4274 //Private Group 21 as used by GE:
4275 #define kLocationsInAcquisitionGE 0x0021 + (0x104F << 16) //SS 'LocationsInAcquisitionGE'
4276 #define kRTIA_timer 0x0021 + (0x105E << 16) //DS
4277 #define kProtocolDataBlockGE 0x0025 + (0x101B << 16) //OB
4278 #define kNumberOfPointsPerArm 0x0027 + (0x1060 << 16) //FL
4279 #define kNumberOfArms 0x0027 + (0x1061 << 16) //FL
4280 #define kNumberOfExcitations 0x0027 + (0x1062 << 16) //FL
4281 #define kSamplesPerPixel 0x0028 + (0x0002 << 16)
4282 #define kPhotometricInterpretation 0x0028 + (0x0004 << 16)
4283 #define kPlanarRGB 0x0028 + (0x0006 << 16)
4284 #define kDim3 0x0028 + (0x0008 << 16) //number of frames - for Philips this is Dim3*Dim4
4285 #define kDim2 0x0028 + (0x0010 << 16)
4286 #define kDim1 0x0028 + (0x0011 << 16)
4287 #define kXYSpacing 0x0028 + (0x0030 << 16) //DS 'PixelSpacing'
4288 #define kBitsAllocated 0x0028 + (0x0100 << 16)
4289 #define kBitsStored 0x0028 + (0x0101 << 16) //US 'BitsStored'
4290 #define kIsSigned 0x0028 + (0x0103 << 16) //PixelRepresentation
4291 #define kPixelPaddingValue 0x0028 + (0x0120 << 16) // https://github.com/rordenlab/dcm2niix/issues/262
4292 #define kFloatPixelPaddingValue 0x0028 + (0x0122 << 16) // https://github.com/rordenlab/dcm2niix/issues/262
4293 #define kIntercept 0x0028 + (0x1052 << 16)
4294 #define kSlope 0x0028 + (0x1053 << 16)
4295 //#define kRescaleType 0x0028+(0x1053 << 16 ) //LO e.g. for Philips Fieldmap: [Hz]
4296 //#define kSpectroscopyDataPointColumns 0x0028+(0x9002 << 16 ) //IS
4297 #define kGeiisFlag 0x0029 + (0x0010 << 16) //warn user if dreaded GEIIS was used to process image
4298 #define kCSAImageHeaderInfo 0x0029 + (0x1010 << 16)
4299 #define kCSASeriesHeaderInfo 0x0029 + (0x1020 << 16)
4300 #define kStudyComments 0x0032 + (0x4000 << 16) //LT StudyComments
4301 //#define kObjectGraphics 0x0029+(0x1210 << 16 ) //0029,1210 syngoPlatformOOGInfo Object Oriented Graphics
4302 #define kProcedureStepDescription 0x0040 + (0x0254 << 16)
4303 #define kRealWorldIntercept 0x0040 + uint32_t(0x9224 << 16) //IS dicm2nii's SlopInt_6_9
4304 #define kRealWorldSlope 0x0040 + uint32_t(0x9225 << 16) //IS dicm2nii's SlopInt_6_9
4305 #define kUserDefineDataGE 0x0043 + (0x102A << 16) //OB
4306 #define kEffectiveEchoSpacingGE 0x0043 + (0x102C << 16) //SS
4307 #define kImageTypeGE 0x0043 + (0x102F << 16) //SS 0/1/2/3 for magnitude/phase/real/imaginary
4308 #define kDiffusion_bValueGE 0x0043 + (0x1039 << 16) //IS dicm2nii's SlopInt_6_9
4309 #define kEpiRTGroupDelayGE 0x0043 + (0x107C << 16) //FL
4310 #define kAssetRFactorsGE 0x0043 + (0x1083 << 16) //DS
4311 #define kASLContrastTechniqueGE 0x0043 + (0x10A3 << 16) //CS
4312 #define kASLLabelingTechniqueGE 0x0043 + (0x10A4 << 16) //LO
4313 #define kDurationLabelPulseGE 0x0043 + (0x10A5 << 16) //IS
4314 #define kMultiBandGE 0x0043 + (0x10B6 << 16) //LO
4315 #define kAcquisitionMatrixText 0x0051 + (0x100B << 16) //LO
4316 #define kImageOrientationText 0x0051 + (0x100E << 16) //
4317 #define kCoilSiemens 0x0051 + (0x100F << 16)
4318 #define kImaPATModeText 0x0051 + (0x1011 << 16)
4319 #define kLocationsInAcquisition 0x0054 + (0x0081 << 16)
4320 #define kUnitsPT 0x0054 + (0x1001 << 16) //CS
4321 #define kAttenuationCorrectionMethod 0x0054 + (0x1101 << 16) //LO
4322 #define kDecayCorrection 0x0054 + (0x1102 << 16) //CS
4323 #define kReconstructionMethod 0x0054 + (0x1103 << 16) //LO
4324 #define kDecayFactor 0x0054 + (0x1321 << 16) //LO
4325 //ftp://dicom.nema.org/MEDICAL/dicom/2014c/output/chtml/part03/sect_C.8.9.4.html
4326 //If ImageType is REPROJECTION we slice direction is reversed - need example to test
4327 // #define kSeriesType 0x0054+(0x1000 << 16 )
4328 #define kDoseCalibrationFactor 0x0054 + (0x1322 << 16)
4329 #define kPETImageIndex 0x0054 + (0x1330 << 16)
4330 #define kPEDirectionDisplayedUIH 0x0065 + (0x1005 << 16) //SH
4331 #define kDiffusion_bValueUIH 0x0065 + (0x1009 << 16) //FD
4332 #define kParallelInformationUIH 0x0065 + (0x100D << 16) //SH
4333 #define kNumberOfImagesInGridUIH 0x0065 + (0x1050 << 16) //DS
4334 #define kDiffusionGradientDirectionUIH 0x0065 + (0x1037 << 16) //FD
4335 //#define kMRVFrameSequenceUIH 0x0065+(0x1050<< 16 ) //SQ
4336 #define kPhaseEncodingDirectionPositiveUIH 0x0065 + (0x1058 << 16) //IS issue410
4337 #define kIconImageSequence 0x0088 + (0x0200 << 16)
4338 #define kElscintIcon 0x07a3 + (0x10ce << 16) //see kGeiisFlag and https://github.com/rordenlab/dcm2niix/issues/239
4339 #define kPMSCT_RLE1 0x07a1 + (0x100a << 16) //Elscint/Philips compression
4340 #define kPrivateCreator 0x2001 + (0x0010 << 16) // LO (Private creator is any tag where group is odd and element is x0010-x00FF
4341 #define kDiffusion_bValuePhilips 0x2001 + (0x1003 << 16) // FL
4342 #define kPhaseNumber 0x2001 + (0x1008 << 16) //IS
4343 #define kCardiacSync 0x2001 + (0x1010 << 16) //CS
4344 //#define kDiffusionDirectionPhilips 0x2001+(0x1004 << 16 )//CS Diffusion Direction
4345 #define kSliceNumberMrPhilips 0x2001 + (0x100A << 16) //IS Slice_Number_MR
4346 #define kSliceOrient 0x2001 + (0x100B << 16) //2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL)
4347 #define kEPIFactorPhilips 0x2001 + (0x1013 << 16) //SL
4348 #define kPrepulseDelay 0x2001 + (0x101B << 16) //FL
4349 #define kPrepulseType 0x2001 + (0x101C << 16) //CS
4350 #define kRespirationSync 0x2001 + (0x101F << 16) //CS
4351 #define kNumberOfSlicesMrPhilips 0x2001 + (0x1018 << 16) //SL 0x2001, 0x1018 ), "Number_of_Slices_MR"
4352 #define kPartialMatrixScannedPhilips 0x2001 + (0x1019 << 16) // CS
4353 #define kWaterFatShiftPhilips 0x2001 + (0x1022 << 16) //FL
4354 //#define kMRSeriesAcquisitionNumber 0x2001+(0x107B << 16 ) //IS
4355 //#define kNumberOfLocationsPhilips 0x2001+(0x1015 << 16 ) //SS
4356 //#define kStackSliceNumber 0x2001+(0x1035 << 16 )//? Potential way to determine slice order for Philips?
4357 #define kNumberOfDynamicScans 0x2001 + (0x1081 << 16) //'2001' '1081' 'IS' 'NumberOfDynamicScans'
4358 #define kMRfMRIStatusIndicationPhilips 0x2005 + (0x1063 << 16)
4359 #define kMRAcquisitionTypePhilips 0x2005 + (0x106F << 16) //SS
4360 #define kAngulationAP 0x2005 + (0x1071 << 16) //'2005' '1071' 'FL' 'MRStackAngulationAP'
4361 #define kAngulationFH 0x2005 + (0x1072 << 16) //'2005' '1072' 'FL' 'MRStackAngulationFH'
4362 #define kAngulationRL 0x2005 + (0x1073 << 16) //'2005' '1073' 'FL' 'MRStackAngulationRL'
4363 #define kMRStackOffcentreAP 0x2005 + (0x1078 << 16)
4364 #define kMRStackOffcentreFH 0x2005 + (0x1079 << 16)
4365 #define kMRStackOffcentreRL 0x2005 + (0x107A << 16)
4366 #define kPhilipsSlope 0x2005 + (0x100E << 16)
4367 #define kMRImageDynamicScanBeginTime 0x2005 + (0x10a0 << 16) //FL
4368 #define kDiffusionDirectionRL 0x2005 + (0x10B0 << 16)
4369 #define kDiffusionDirectionAP 0x2005 + (0x10B1 << 16)
4370 #define kDiffusionDirectionFH 0x2005 + (0x10B2 << 16)
4371 #define kPrivatePerFrameSq 0x2005 + (0x140F << 16)
4372 #define kMRImageDiffBValueNumber 0x2005 + (0x1412 << 16) //IS
4373 #define kMRImageGradientOrientationNumber 0x2005+(0x1413 << 16) //IS
4374 #define kMRImageLabelType 0x2005 + (0x1429 << 16) //CS ASL LBL_CTL https://github.com/physimals/dcm_convert_phillips/
4375 #define kMRImageDiffVolumeNumber 0x2005+(0x1596 << 16) //IS
4376 #define kSharedFunctionalGroupsSequence 0x5200 + uint32_t(0x9229 << 16) // SQ
4377 #define kPerFrameFunctionalGroupsSequence 0x5200 + uint32_t(0x9230 << 16) // SQ
4378 #define kWaveformSq 0x5400 + (0x0100 << 16)
4379 #define kSpectroscopyData 0x5600 + (0x0020 << 16) //OF
4380 #define kImageStart 0x7FE0 + (0x0010 << 16)
4381 #define kImageStartFloat 0x7FE0 + (0x0008 << 16)
4382 #define kImageStartDouble 0x7FE0 + (0x0009 << 16)
4383 	uint32_t kItemTag = 0xFFFE + (0xE000 << 16);
4384 	uint32_t kItemDelimitationTag = 0xFFFE + (0xE00D << 16);
4385 	uint32_t kSequenceDelimitationItemTag = 0xFFFE + (0xE0DD << 16);
4386 #define salvageAgfa
4387 #ifdef salvageAgfa //issue435
4388 // handle PrivateCreator renaming e.g. 0021,10xx -> 0021,11xx
4389 // https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/siemens.tpl
4390 // https://github.com/neurolabusc/dcm_qa_agfa
4391 // http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_7.8.html
4392 #define kMaxRemaps 16 //no vendor uses more than 5 private creator groups
4393 	//we need to keep track of multiple remappings, e.g. issue 437 2005,0014->2005,0012; 2005,0015->2005,0011
4394 	int nRemaps = 0;
4395 	uint32_t privateCreatorMasks[kMaxRemaps]; //0 -> none
4396 	uint32_t privateCreatorRemaps[kMaxRemaps]; //0 -> none
4397 #endif
4398 	double TE = 0.0; //most recent echo time recorded
4399 	float temporalResolutionMS = 0.0;
4400 	float MRImageDynamicScanBeginTime = 0.0;
4401 	bool is2005140FSQ = false;
4402 	bool overlayOK = true;
4403 	int userData12GE = 0;
4404 	int overlayRows = 0;
4405 	int overlayCols = 0;
4406 	bool isNeologica = false;
4407 	bool isTriggerSynced = false;
4408 	bool isProspectiveSynced = false;
4409 	bool isDICOMANON = false; //issue383
4410 	bool isMATLAB = false; //issue383
4411 	bool isASL = false;
4412 	//double contentTime = 0.0;
4413 	int echoTrainLengthPhil = 0;
4414 	int philMRImageDiffBValueNumber = 0;
4415 	int philMRImageDiffVolumeNumber = -1;
4416 	int sqDepth = 0;
4417 	int acquisitionTimesGE_UIH = 0;
4418 	int sqDepth00189114 = -1;
4419 	bool hasDwiDirectionality = false;
4420 	//float sliceLocation = INFINITY; //useless since this tag is optional
4421 	//int numFirstPatientPosition = 0;
4422 	int nDimIndxVal = -1; //tracks Philips kDimensionIndexValues
4423 	int locationsInAcquisitionGE = 0;
4424 	int PETImageIndex = 0;
4425 	int inStackPositionNumber = 0;
4426 	bool isKludgeIssue533 = false;
4427 	uint32_t dimensionIndexPointer[MAX_NUMBER_OF_DIMENSIONS];
4428 	size_t dimensionIndexPointerCounter = 0;
4429 	int maxInStackPositionNumber = 0;
4430 	int temporalPositionIndex = 0;
4431 	int maxTemporalPositionIndex = 0;
4432 	//int temporalPositionIdentifier = 0;
4433 	int locationsInAcquisitionPhilips = 0;
4434 	int imagesInAcquisition = 0;
4435 	//int sumSliceNumberMrPhilips = 0;
4436 	int sliceNumberMrPhilips = 0;
4437 	int volumeNumber = -1;
4438 	int gradientOrientationNumberPhilips = -1;
4439 	int numberOfFrames = 0;
4440 	//int MRImageGradientOrientationNumber = 0;
4441 	//int minGradNum = kMaxDTI4D + 1;
4442 	//int maxGradNum = -1;
4443 	int numberOfDynamicScans = 0;
4444 	//int mRSeriesAcquisitionNumber = 0;
4445 	uint32_t lLength;
4446 	uint32_t groupElement;
4447 	long lPos = 0;
4448 	bool isPhilipsDerived = false;
4449 	//bool isPhilipsDiffusion = false;
4450 	if (isPart10prefix) { //for part 10 files, skip preamble and prefix
4451 		lPos = 128 + 4; //4-byte signature starts at 128
4452 		groupElement = buffer[lPos] | (buffer[lPos + 1] << 8) | (buffer[lPos + 2] << 16) | (buffer[lPos + 3] << 24);
4453 		if (groupElement != kStart)
4454 			printMessage("DICOM appears corrupt: first group:element should be 0x0002:0x0000 '%s'\n", fname);
4455 	} else { //no isPart10prefix - need to work out if this is explicit VR!
4456 		if (isVerbose > 1)
4457 			printMessage("DICOM preamble and prefix missing: this is not a valid DICOM image.\n");
4458 		//See Toshiba Aquilion images from https://www.aliza-dicom-viewer.com/download/datasets
4459 		lLength = buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24);
4460 		if (lLength > fileLen) {
4461 			if (isVerbose > 1)
4462 				printMessage("Guessing this is an explicit VR image.\n");
4463 			d.isExplicitVR = true;
4464 		}
4465 	}
4466 	char vr[2];
4467 	//float intenScalePhilips = 0.0;
4468 	char seriesTimeTxt[kDICOMStr] = "";
4469 	char acquisitionDateTimeTxt[kDICOMStr] = "";
4470 	char imageType1st[kDICOMStr] = "";
4471 	bool isEncapsulatedData = false;
4472 	int multiBandFactor = 0;
4473 	int frequencyRows = 0;
4474 	int numberOfImagesInMosaic = 0;
4475 	int encapsulatedDataFragments = 0;
4476 	int encapsulatedDataFragmentStart = 0; //position of first FFFE,E000 for compressed images
4477 	int encapsulatedDataImageStart = 0; //position of 7FE0,0010 for compressed images (where actual image start should be start of first fragment)
4478 	bool isOrient = false;
4479 	//bool isDcm4Che = false;
4480 	bool isMoCo = false;
4481 	bool isPaletteColor = false;
4482 	bool isInterpolated = false;
4483 	bool isIconImageSequence = false;
4484 	int sqDepthIcon = -1;
4485 	bool isSwitchToImplicitVR = false;
4486 	bool isSwitchToBigEndian = false;
4487 	bool isAtFirstPatientPosition = false; //for 3d and 4d files: flag is true for slices at same position as first slice
4488 	bool isMosaic = false;
4489 	bool isGEfieldMap = false; //issue501
4490 	int patientPositionNum = 0;
4491 	float B0Philips = -1.0;
4492 	float vRLPhilips = 0.0;
4493 	float vAPPhilips = 0.0;
4494 	float vFHPhilips = 0.0;
4495 	bool isPhase = false;
4496 	bool isReal = false;
4497 	bool isImaginary = false;
4498 	bool isMagnitude = false;
4499 	d.seriesNum = -1;
4500 	//start issue 372:
4501 	vec3 sliceV; //cross-product of kOrientation 0020,0037
4502 	sliceV.v[0] = NAN;
4503 	float sliceMM[kMaxSlice2D];
4504 	int nSliceMM = 0;
4505 	float minSliceMM = INFINITY;
4506 	float maxSliceMM = -INFINITY;
4507 	float minDynamicScanBeginTime = INFINITY;
4508 	float maxDynamicScanBeginTime = -INFINITY;
4509 	float minPatientPosition[4] = {NAN, NAN, NAN, NAN};
4510 	float maxPatientPosition[4] = {NAN, NAN, NAN, NAN};
4511 	//end issue 372
4512 	//float frameAcquisitionDuration = 0.0; //issue369
4513 	float patientPositionPrivate[4] = {NAN, NAN, NAN, NAN};
4514 	float patientPosition[4] = {NAN, NAN, NAN, NAN}; //used to compute slice direction for Philips 4D
4515 	//float patientPositionPublic[4] = {NAN, NAN, NAN, NAN}; //used to compute slice direction for Philips 4D
4516 	float patientPositionEndPhilips[4] = {NAN, NAN, NAN, NAN};
4517 	float patientPositionStartPhilips[4] = {NAN, NAN, NAN, NAN};
4518 	//struct TDTI philDTI[kMaxDTI4D];
4519 	//for (int i = 0; i < kMaxDTI4D; i++)
4520 	//	philDTI[i].V[0] = -1;
4521 	//array for storing DimensionIndexValues
4522 	int numDimensionIndexValues = 0;
4523 #ifdef USING_R
4524 	// Allocating a large array on the stack, as below, vexes valgrind and may cause overflow
4525 	std::vector<TDCMdim> dcmDim(kMaxSlice2D);
4526 #else
4527 	TDCMdim dcmDim[kMaxSlice2D];
4528 #endif
4529 	for (int i = 0; i < kMaxSlice2D; i++) {
4530 		dcmDim[i].diskPos = i;
4531 		for (int j = 0; j < MAX_NUMBER_OF_DIMENSIONS; j++)
4532 			dcmDim[i].dimIdx[j] = 0;
4533 	}
4534 //http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_7.5.html
4535 //The array nestPos tracks explicit lengths for Data Element Tag of Value (FFFE,E000)
4536 //a delimiter (fffe,e000) can have an explicit length, in which case there is no delimiter (fffe,e00d)
4537 // fffe,e000 can provide explicit lengths, to demonstrate ./dcmconv +ti ex.DCM im.DCM
4538 #define kMaxNestPost 128
4539 	int nNestPos = 0;
4540 	size_t nestPos[kMaxNestPost];
4541 	while ((d.imageStart == 0) && ((lPos + 8 + lFileOffset) < fileLen)) {
4542 #ifndef myLoadWholeFileToReadHeader //read one segment at a time
4543 		if ((size_t)(lPos + 128) > MaxBufferSz) { //avoid overreading the file
4544 			lFileOffset = lFileOffset + lPos;
4545 			if ((lFileOffset + MaxBufferSz) > (size_t)fileLen)
4546 				MaxBufferSz = fileLen - lFileOffset;
4547 			fseek(file, lFileOffset, SEEK_SET);
4548 			size_t sz = fread(buffer, 1, MaxBufferSz, file);
4549 			if (sz < MaxBufferSz) {
4550 				printError("Only loaded %zu of %zu bytes for %s\n", sz, MaxBufferSz, fname);
4551 				fclose(file);
4552 				return d;
4553 			}
4554 			lPos = 0;
4555 		}
4556 #endif
4557 		if (d.isLittleEndian)
4558 			groupElement = buffer[lPos] | (buffer[lPos + 1] << 8) | (buffer[lPos + 2] << 16) | (buffer[lPos + 3] << 24);
4559 		else
4560 			groupElement = buffer[lPos + 1] | (buffer[lPos] << 8) | (buffer[lPos + 3] << 16) | (buffer[lPos + 2] << 24);
4561 		if ((isSwitchToBigEndian) && ((groupElement & 0xFFFF) != 2)) {
4562 			isSwitchToBigEndian = false;
4563 			d.isLittleEndian = false;
4564 			groupElement = buffer[lPos + 1] | (buffer[lPos] << 8) | (buffer[lPos + 3] << 16) | (buffer[lPos + 2] << 24);
4565 		} //transfer syntax requests switching endian after group 0002
4566 		if ((isSwitchToImplicitVR) && ((groupElement & 0xFFFF) != 2)) {
4567 			isSwitchToImplicitVR = false;
4568 			d.isExplicitVR = false;
4569 		} //transfer syntax requests switching VR after group 0001
4570 		//uint32_t group = (groupElement & 0xFFFF);
4571 		lPos += 4;
4572 		//issue409 - icons can have their own sub-sections... keep reading until we get to the icon image?
4573 		//if ((groupElement == kItemDelimitationTag) || (groupElement == kSequenceDelimitationItemTag)) isIconImageSequence = false;
4574 		//if (groupElement == kItemTag) sqDepth++;
4575 		bool unNest = false;
4576 		while ((nNestPos > 0) && (nestPos[nNestPos] <= (lFileOffset + lPos))) {
4577 			nNestPos--;
4578 			sqDepth--;
4579 			unNest = true;
4580 			if ((sqDepthIcon >= 0) && (sqDepth <= sqDepthIcon)) { //issue415
4581 				sqDepthIcon = -1;
4582 				isIconImageSequence = false;
4583 			}
4584 		}
4585 		if (groupElement == kItemDelimitationTag) { //end of item with undefined length
4586 			sqDepth--;
4587 			unNest = true;
4588 			if ((sqDepthIcon >= 0) && (sqDepth <= sqDepthIcon)) { //issue415
4589 				sqDepthIcon = -1;
4590 				isIconImageSequence = false;
4591 			}
4592 		}
4593 		if (unNest) {
4594 			is2005140FSQ = false;
4595 			if (sqDepth < 0)
4596 				sqDepth = 0; //should not happen, but protect for faulty anonymization
4597 			//if we leave the folder MREchoSequence 0018,9114
4598 			if ((nDimIndxVal > 0) && ((d.manufacturer == kMANUFACTURER_CANON) || (d.manufacturer == kMANUFACTURER_BRUKER) || (d.manufacturer == kMANUFACTURER_PHILIPS)) && (sqDepth00189114 >= sqDepth)) {
4599 				sqDepth00189114 = -1; //triggered
4600 				//printf("slice %d---> 0020,9157 = %d %d %d\n", inStackPositionNumber, d.dimensionIndexValues[0], d.dimensionIndexValues[1], d.dimensionIndexValues[2]);
4601 				// d.aslFlags = kASL_FLAG_PHILIPS_LABEL; kASL_FLAG_PHILIPS_LABEL
4602 				if ((nDimIndxVal > 1) && (volumeNumber > 0) && (inStackPositionNumber > 0) && (d.aslFlags == kASL_FLAG_PHILIPS_LABEL) || (d.aslFlags == kASL_FLAG_PHILIPS_CONTROL)) {
4603 					isKludgeIssue533 = true;
4604 					for (int i = 0; i < nDimIndxVal; i++)
4605 						d.dimensionIndexValues[i] = 0;
4606 					int phase = d.phaseNumber;
4607 					if (d.phaseNumber < 0) phase = 0; //if not set: we are saving as UINT
4608 					d.dimensionIndexValues[0] = inStackPositionNumber; //dim[3] slice changes fastest
4609 					d.dimensionIndexValues[1] = phase; //dim[4] successive volumes are phase
4610 					d.dimensionIndexValues[2] = d.aslFlags == kASL_FLAG_PHILIPS_LABEL; //dim[5] Control/Label
4611 					d.dimensionIndexValues[3] = volumeNumber; //dim[6] Repeat changes slowest
4612 					nDimIndxVal = 4; //slice < phase < control/label < volume
4613 					//printf("slice %d phase %d control/label %d repeat %d\n", inStackPositionNumber, d.phaseNumber, d.aslFlags == kASL_FLAG_PHILIPS_LABEL, volumeNumber);
4614 				}
4615 				int ndim = nDimIndxVal;
4616 				if (inStackPositionNumber > 0) {
4617 					//for images without SliceNumberMrPhilips (2001,100A)
4618 					int sliceNumber = inStackPositionNumber;
4619 					//printf("slice %d \n", sliceNumber);
4620 					if ((sliceNumber == 1) && (!isnan(patientPosition[1]))) {
4621 						for (int k = 0; k < 4; k++)
4622 							patientPositionStartPhilips[k] = patientPosition[k];
4623 					} else if ((sliceNumber == 1) && (!isnan(patientPositionPrivate[1]))) {
4624 						for (int k = 0; k < 4; k++)
4625 							patientPositionStartPhilips[k] = patientPositionPrivate[k];
4626 					}
4627 					if ((sliceNumber == maxInStackPositionNumber) && (!isnan(patientPosition[1]))) {
4628 						for (int k = 0; k < 4; k++)
4629 							patientPositionEndPhilips[k] = patientPosition[k];
4630 					} else if ((sliceNumber == maxInStackPositionNumber) && (!isnan(patientPositionPrivate[1]))) {
4631 						for (int k = 0; k < 4; k++)
4632 							patientPositionEndPhilips[k] = patientPositionPrivate[k];
4633 					}
4634 					patientPosition[1] = NAN;
4635 					patientPositionPrivate[1] = NAN;
4636 				}
4637 				inStackPositionNumber = 0;
4638 				if (numDimensionIndexValues >= kMaxSlice2D) {
4639 					printError("Too many slices to track dimensions. Only up to %d are supported\n", kMaxSlice2D);
4640 					break;
4641 				}
4642 				uint32_t dimensionIndexOrder[MAX_NUMBER_OF_DIMENSIONS];
4643 				for (size_t i = 0; i < nDimIndxVal; i++)
4644 					dimensionIndexOrder[i] = i;
4645 				// Bruker Enhanced MR IOD: reorder dimensions to ensure InStackPositionNumber corresponds to the first one
4646 				// This will ensure correct ordering of slices in 4D datasets
4647 				//Canon and Bruker reverse dimensionIndexItem order relative to Philips: new versions introduce compareTDCMdimRev
4648 				//printf("%d: %d %d %d %d\n", ndim, d.dimensionIndexValues[0], d.dimensionIndexValues[1], d.dimensionIndexValues[2], d.dimensionIndexValues[3]);
4649 				if ((philMRImageDiffVolumeNumber > 0) && (sliceNumberMrPhilips > 0)) { //issue546: 2005,1596 provides temporal order
4650 					dcmDim[numDimensionIndexValues].dimIdx[0] = 1;
4651 					dcmDim[numDimensionIndexValues].dimIdx[1] = sliceNumberMrPhilips;
4652 					dcmDim[numDimensionIndexValues].dimIdx[2] = philMRImageDiffVolumeNumber;
4653 				} else {
4654 					for (int i = 0; i < ndim; i++)
4655 						dcmDim[numDimensionIndexValues].dimIdx[i] = d.dimensionIndexValues[dimensionIndexOrder[i]];
4656 				}
4657 				dcmDim[numDimensionIndexValues].TE = TE;
4658 				dcmDim[numDimensionIndexValues].intenScale = d.intenScale;
4659 				dcmDim[numDimensionIndexValues].intenIntercept = d.intenIntercept;
4660 				dcmDim[numDimensionIndexValues].isPhase = isPhase;
4661 				dcmDim[numDimensionIndexValues].isReal = isReal;
4662 				dcmDim[numDimensionIndexValues].isImaginary = isImaginary;
4663 				dcmDim[numDimensionIndexValues].intenScalePhilips = d.intenScalePhilips;
4664 				dcmDim[numDimensionIndexValues].RWVScale = d.RWVScale;
4665 				dcmDim[numDimensionIndexValues].RWVIntercept = d.RWVIntercept;
4666 				//printf("%d %d %g????\n", isTriggerSynced, isProspectiveSynced, d.triggerDelayTime);
4667 				//TODO533: isKludgeIssue533 alias Philips ASL as FrameDuration?
4668 				//if ((d.triggerDelayTime > 0.0) && (d.manufacturer == kMANUFACTURER_PHILIPS) && (d.aslFlags != kASL_FLAG_NONE))
4669 				//printf(">>>%g\n", d.triggerDelayTime);
4670 				//if ((isASL) || (d.aslFlags != kASL_FLAG_NONE)) d.triggerDelayTime = 0.0; //see dcm_qa_philips_asl
4671 				//if ((d.manufacturer == kMANUFACTURER_PHILIPS) && ((!isTriggerSynced) || (!isProspectiveSynced)) ) //issue408
4672 				//	d.triggerDelayTime = 0.0;
4673 				if (isSameFloat(MRImageDynamicScanBeginTime * 1000.0, d.triggerDelayTime) )
4674 					dcmDim[numDimensionIndexValues].triggerDelayTime = 0.0; //issue395
4675 				else
4676 					dcmDim[numDimensionIndexValues].triggerDelayTime = d.triggerDelayTime;
4677 				dcmDim[numDimensionIndexValues].V[0] = -1.0;
4678 #ifdef MY_DEBUG
4679 				if (numDimensionIndexValues < 19) {
4680 					printMessage("dimensionIndexValues0020x9157[%d] = [", numDimensionIndexValues);
4681 					for (int i = 0; i < ndim; i++)
4682 						printMessage("%d ", d.dimensionIndexValues[i]);
4683 					printMessage("]\n");
4684 					//printMessage("B0= %g num=%d\n", B0Philips, gradNum);
4685 				} else
4686 					return d;
4687 #endif
4688 				//next: add diffusion if reported
4689 				if (B0Philips >= 0.0) { //diffusion parameters
4690 					// Philips does not always provide 2005,1413 (MRImageGradientOrientationNumber) and sometimes after dimensionIndexValues
4691 					/*int gradNum = 0;
4692 				for (int i = 0; i < ndim; i++)
4693 					if (d.dimensionIndexValues[i] > 0) gradNum = d.dimensionIndexValues[i];
4694 				if (gradNum <= 0) break;
4695 				With Philips 51.0 both ADC and B=0 are saved as same direction, though they vary in another dimension
4696 				(0018,9075) CS [ISOTROPIC]
4697 				(0020,9157) UL 1\2\1\33 << ADC MAP
4698 				(0018,9075) CS [NONE]
4699 				(0020,9157) UL 1\1\2\33
4700 				next two lines attempt to skip ADC maps
4701 				we could also increment gradNum for ADC if we wanted...
4702 				*/
4703 					if (isPhilipsDerived) {
4704 						//gradNum ++;
4705 						B0Philips = 2000.0;
4706 						vRLPhilips = 0.0;
4707 						vAPPhilips = 0.0;
4708 						vFHPhilips = 0.0;
4709 					}
4710 					if (B0Philips == 0.0) {
4711 						//printMessage(" DimensionIndexValues grad %d b=%g vec=%gx%gx%g\n", gradNum, B0Philips, vRLPhilips, vAPPhilips, vFHPhilips);
4712 						vRLPhilips = 0.0;
4713 						vAPPhilips = 0.0;
4714 						vFHPhilips = 0.0;
4715 					}
4716 					//if ((MRImageGradientOrientationNumber > 0) && ((gradNum != MRImageGradientOrientationNumber)) break;
4717 					/*if (gradNum < minGradNum) minGradNum = gradNum;
4718 				if (gradNum >= maxGradNum) maxGradNum = gradNum;
4719 				if (gradNum >= kMaxDTI4D) {
4720 						printError("Number of DTI gradients exceeds 'kMaxDTI4D (%d).\n", kMaxDTI4D);
4721 				} else {
4722 					gradNum = gradNum - 1; //index from 0
4723 					philDTI[gradNum].V[0] = B0Philips;
4724 					philDTI[gradNum].V[1] = vRLPhilips;
4725 					philDTI[gradNum].V[2] = vAPPhilips;
4726 					philDTI[gradNum].V[3] = vFHPhilips;
4727 				}*/
4728 					dcmDim[numDimensionIndexValues].V[0] = B0Philips;
4729 					dcmDim[numDimensionIndexValues].V[1] = vRLPhilips;
4730 					dcmDim[numDimensionIndexValues].V[2] = vAPPhilips;
4731 					dcmDim[numDimensionIndexValues].V[3] = vFHPhilips;
4732 					isPhilipsDerived = false;
4733 					//printMessage(" DimensionIndexValues grad %d b=%g vec=%gx%gx%g\n", gradNum, B0Philips, vRLPhilips, vAPPhilips, vFHPhilips);
4734 					//!!! 16032018 : next line as well as definition of B0Philips may need to be set to zero if Philips omits DiffusionBValue tag for B=0
4735 					B0Philips = -1.0; //Philips may skip reporting B-values for B=0 volumes, so zero these
4736 					vRLPhilips = 0.0;
4737 					vAPPhilips = 0.0;
4738 					vFHPhilips = 0.0;
4739 					//MRImageGradientOrientationNumber = 0;
4740 				} //diffusion parameters
4741 				numDimensionIndexValues++;
4742 				nDimIndxVal = -1; //we need DimensionIndexValues
4743 			} //record dimensionIndexValues slice information
4744 		} //groupElement == kItemDelimitationTag : delimit item exits folder
4745 		if (groupElement == kItemTag) {
4746 			uint32_t slen = dcmInt(4, &buffer[lPos], d.isLittleEndian);
4747 			uint32_t kUndefinedLen = 0xFFFFFFFF;
4748 			if (slen != kUndefinedLen) {
4749 				nNestPos++;
4750 				if (nNestPos >= kMaxNestPost)
4751 					nNestPos = kMaxNestPost - 1;
4752 				nestPos[nNestPos] = slen + lFileOffset + lPos;
4753 			}
4754 			lLength = 4;
4755 			sqDepth++;
4756 			//return d;
4757 		} else if (((groupElement == kItemDelimitationTag) || (groupElement == kSequenceDelimitationItemTag)) && (!isEncapsulatedData)) {
4758 			vr[0] = 'N';
4759 			vr[1] = 'A';
4760 			lLength = 4;
4761 		} else if (d.isExplicitVR) {
4762 			vr[0] = buffer[lPos];
4763 			vr[1] = buffer[lPos + 1];
4764 			if (buffer[lPos + 1] < 'A') { //implicit vr with 32-bit length
4765 				if (d.isLittleEndian)
4766 					lLength = buffer[lPos] | (buffer[lPos + 1] << 8) | (buffer[lPos + 2] << 16) | (buffer[lPos + 3] << 24);
4767 				else
4768 					lLength = buffer[lPos + 3] | (buffer[lPos + 2] << 8) | (buffer[lPos + 1] << 16) | (buffer[lPos] << 24);
4769 				lPos += 4;
4770 			} else if (((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'N')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'C')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'R')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'T')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'V')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'B')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'D')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'F')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'L')) | ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'V')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'W')) || ((buffer[lPos] == 'S') && (buffer[lPos + 1] == 'V'))) { //VR= UN, OB, OW, SQ || ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q'))
4771 				//for example of UC/UR/UV/OD/OF/OL/OV/SV see VR conformance test https://www.aliza-dicom-viewer.com/download/datasets
4772 				lPos = lPos + 4; //skip 2 byte VR string and 2 reserved bytes = 4 bytes
4773 				if (d.isLittleEndian)
4774 					lLength = buffer[lPos] | (buffer[lPos + 1] << 8) | (buffer[lPos + 2] << 16) | (buffer[lPos + 3] << 24);
4775 				else
4776 					lLength = buffer[lPos + 3] | (buffer[lPos + 2] << 8) | (buffer[lPos + 1] << 16) | (buffer[lPos] << 24);
4777 				lPos = lPos + 4; //skip 4 byte length
4778 			} else if ((buffer[lPos] == 'S') && (buffer[lPos + 1] == 'Q')) {
4779 				lLength = 8; //Sequence Tag
4780 				//printMessage(" !!!SQ\t%04x,%04x\n", groupElement & 65535,groupElement>>16);
4781 			} else { //explicit VR with 16-bit length
4782 				if ((d.isLittleEndian))
4783 					lLength = buffer[lPos + 2] | (buffer[lPos + 3] << 8);
4784 				else
4785 					lLength = buffer[lPos + 3] | (buffer[lPos + 2] << 8);
4786 				lPos += 4; //skip 2 byte VR string and 2 length bytes = 4 bytes
4787 			}
4788 		} else { //implicit VR
4789 			vr[0] = 'U';
4790 			vr[1] = 'N';
4791 			if (d.isLittleEndian)
4792 				lLength = buffer[lPos] | (buffer[lPos + 1] << 8) | (buffer[lPos + 2] << 16) | (buffer[lPos + 3] << 24);
4793 			else
4794 				lLength = buffer[lPos + 3] | (buffer[lPos + 2] << 8) | (buffer[lPos + 1] << 16) | (buffer[lPos] << 24);
4795 			lPos += 4; //we have loaded the 32-bit length
4796 			if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isSQ(groupElement))) { //https://github.com/rordenlab/dcm2niix/issues/144
4797 				vr[0] = 'S';
4798 				vr[1] = 'Q';
4799 				lLength = 0; //Do not skip kItemTag - required to determine nesting of Philips Enhanced
4800 			}
4801 			if ((d.manufacturer != kMANUFACTURER_PHILIPS) && (isSQ(groupElement))) { //https://github.com/rordenlab/dcm2niix/issues/144
4802 				vr[0] = 'S';
4803 				vr[1] = 'Q';
4804 				lLength = 0; //Do not skip kItemTag - required to determine nesting of Philips Enhanced
4805 			}
4806 		} //if explicit else implicit VR
4807 		if (lLength == 0xFFFFFFFF) {
4808 			lLength = 8; //SQ (Sequences) use 0xFFFFFFFF [4294967295] to denote unknown length
4809 				//09032018 - do not count these as SQs: Horos does not count even groups
4810 				//uint32_t special = dcmInt(4,&buffer[lPos],d.isLittleEndian);
4811 			//http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_7.5.html
4812 			//if (special != ksqDelim) {
4813 			vr[0] = 'S';
4814 			vr[1] = 'Q';
4815 			//}
4816 		}
4817 		if ((groupElement == kItemTag) && (isEncapsulatedData)) { //use this to find image fragment for compressed datasets, e.g. JPEG transfer syntax
4818 			d.imageBytes = dcmInt(4, &buffer[lPos], d.isLittleEndian);
4819 			lPos = lPos + 4;
4820 			lLength = d.imageBytes;
4821 			if (d.imageBytes > 128) {
4822 				/*if (encapsulatedDataFragments < kMaxDTI4D) {
4823 					dti4D->fragmentOffset[encapsulatedDataFragments] = (int)lPos + (int)lFileOffset;
4824 					dti4D->fragmentLength[encapsulatedDataFragments] = lLength;
4825 				}*/
4826 				encapsulatedDataFragments++;
4827 				if (encapsulatedDataFragmentStart == 0)
4828 					encapsulatedDataFragmentStart = (int)lPos + (int)lFileOffset;
4829 			}
4830 		}
4831 		if ((isIconImageSequence) && ((groupElement & 0x0028) == 0x0028))
4832 			groupElement = kUnused; //ignore icon dimensions
4833 #ifdef salvageAgfa					//issue435
4834 		//Handle remapping using integers, and slower but simpler approach is with strings:
4835 		// https://github.com/pydicom/pydicom/blob/master/pydicom/_private_dict.py
4836 		if (((groupElement & 65535) % 2) == 0)
4837 			goto skipRemap; //remap odd (private) groups
4838 		//printf("tag %04x,%04x\n", groupElement & 65535, groupElement >> 16);
4839 		if (((groupElement >> 16) >= 0x10) && ((groupElement >> 16) <= 0xFF)) { //tags (gggg,0010-00FF) may define new remapping
4840 			//if remapping tag
4841 			//first: see if this remapping overwrites existing tag
4842 			uint32_t privateCreatorMask = 0; //0 -> none
4843 			uint32_t privateCreatorRemap = 0; //0 -> none
4844 			privateCreatorMask = (groupElement & 65535) + ((groupElement & 0xFFFF0000) << 8);
4845 			if (nRemaps > 0) {
4846 				int j = 0;
4847 				for (int i = 0; i < nRemaps; i++) //remove duplicate remapping
4848 					//copy all remaps except exact match
4849 					if (privateCreatorMasks[i] != privateCreatorMask) {
4850 						privateCreatorMasks[j] = privateCreatorMasks[i];
4851 						privateCreatorRemaps[j] = privateCreatorRemaps[i];
4852 						j++;
4853 					}
4854 				nRemaps = j;
4855 			}
4856 			//see if this is known private vendor tag
4857 			privateCreatorRemap = 0;
4858 			char privateCreator[kDICOMStr];
4859 			dcmStr(lLength, &buffer[lPos], privateCreator);
4860 			//next lines determine remapping, append as needed
4861 			//Siemens https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/siemens.tpl
4862 			if (strstr(privateCreator, "SIEMENS MR HEADER") != NULL)
4863 				privateCreatorRemap = 0x0019 + (0x1000 << 16);
4864 			if (strstr(privateCreator, "SIEMENS MR SDS 01") != NULL)
4865 				privateCreatorRemap = 0x0021 + (0x1000 << 16);
4866 			if (strstr(privateCreator, "SIEMENS MR SDI 02") != NULL)
4867 				privateCreatorRemap = 0x0021 + (0x1100 << 16);
4868 			if (strstr(privateCreator, "SIEMENS CSA HEADER") != NULL)
4869 				privateCreatorRemap = 0x0029 + (0x1000 << 16);
4870 			if (strstr(privateCreator, "SIEMENS MR HEADER") != NULL)
4871 				privateCreatorRemap = 0x0051 + (0x1000 << 16);
4872 			//GE https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/gems.tpl
4873 			if (strstr(privateCreator, "GEMS_ACQU_01") != NULL)
4874 				privateCreatorRemap = 0x0019 + (0x1000 << 16);
4875 			if (strstr(privateCreator, "GEMS_RELA_01") != NULL)
4876 				privateCreatorRemap = 0x0021 + (0x1000 << 16);
4877 			if (strstr(privateCreator, "GEMS_SERS_01") != NULL)
4878 				privateCreatorRemap = 0x0025 + (0x1000 << 16);
4879 			if (strstr(privateCreator, "GEMS_PARM_01") != NULL)
4880 				privateCreatorRemap = 0x0043 + (0x1000 << 16);
4881 			//ELSCINT https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/elscint.tpl
4882 			int grp = (groupElement & 65535);
4883 			if ((grp == 0x07a1) && (strstr(privateCreator, "ELSCINT1") != NULL))
4884 				privateCreatorRemap = 0x07a1 + (0x1000 << 16);
4885 			if ((grp == 0x07a3) && (strstr(privateCreator, "ELSCINT1") != NULL))
4886 				privateCreatorRemap = 0x07a3 + (0x1000 << 16);
4887 			//Philips https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/philips.tpl
4888 			if (strstr(privateCreator, "PHILIPS IMAGING DD 001") != NULL)
4889 				privateCreatorRemap = 0x2001 + (0x1000 << 16);
4890 			if (strstr(privateCreator, "Philips Imaging DD 001") != NULL)
4891 				privateCreatorRemap = 0x2001 + (0x1000 << 16);
4892 			if (strstr(privateCreator, "PHILIPS MR IMAGING DD 001") != NULL)
4893 				privateCreatorRemap = 0x2005 + (0x1000 << 16);
4894 			if (strstr(privateCreator, "Philips MR Imaging DD 001") != NULL)
4895 				privateCreatorRemap = 0x2005 + (0x1000 << 16);
4896 			if (strstr(privateCreator, "PHILIPS MR IMAGING DD 005") != NULL)
4897 				privateCreatorRemap = 0x2005 + (0x1400 << 16);
4898 			if (strstr(privateCreator, "Philips MR Imaging DD 005") != NULL)
4899 				privateCreatorRemap = 0x2005 + (0x1400 << 16);
4900 			//UIH https://github.com/neurolabusc/dcm_qa_uih
4901 			if (strstr(privateCreator, "Image Private Header") != NULL)
4902 				privateCreatorRemap = 0x0065 + (0x1000 << 16);
4903 			//sanity check: group should match
4904 			if (grp != (privateCreatorRemap & 65535))
4905 				privateCreatorRemap = 0;
4906 			if (privateCreatorRemap == 0)
4907 				goto skipRemap; //this is not a known private group
4908 			if (privateCreatorRemap == privateCreatorMask)
4909 				goto skipRemap; //the remapping and mask are identical 2005,1000 -> 2005,1000
4910 			if ((nRemaps + 1) >= kMaxRemaps)
4911 				goto skipRemap; //all slots full (should never happen)
4912 			//add new remapping
4913 			privateCreatorMasks[nRemaps] = privateCreatorMask;
4914 			privateCreatorRemaps[nRemaps] = privateCreatorRemap;
4915 			//printf("new remapping %04x,%04x -> %04x,%04x\n", privateCreatorMask & 65535, privateCreatorMask >> 16, privateCreatorRemap & 65535, privateCreatorRemap >> 16);
4916 			if (isVerbose > 1)
4917 				printMessage("new remapping (%d) %04x,%02xxy -> %04x,%02xxy\n", nRemaps, privateCreatorMask & 65535, privateCreatorMask >> 24, privateCreatorRemap & 65535, privateCreatorRemap >> 24);
4918 			nRemaps += 1;
4919 			//for (int i = 0; i < nRemaps; i++)
4920 			//	printf(" %d = %04x,%02xxy -> %04x,%02xxy\n", i, privateCreatorMasks[i] & 65535, privateCreatorMasks[i] >> 24, privateCreatorRemaps[i] & 65535, privateCreatorRemaps[i] >> 24);
4921 			goto skipRemap;
4922 		}
4923 		if (nRemaps < 1)
4924 			goto skipRemap;
4925 		{
4926 			uint32_t remappedGroupElement = 0;
4927 			for (int i = 0; i < nRemaps; i++)
4928 				if ((groupElement & 0xFF00FFFF) == (privateCreatorMasks[i] & 0xFF00FFFF))
4929 					remappedGroupElement = privateCreatorRemaps[i] + (groupElement & 0x00FF0000);
4930 			if (remappedGroupElement == 0)
4931 				goto skipRemap;
4932 			if (isVerbose > 1)
4933 				printMessage("remapping %04x,%04x -> %04x,%04x\n", groupElement & 65535, groupElement >> 16, remappedGroupElement & 65535, remappedGroupElement >> 16);
4934 			groupElement = remappedGroupElement;
4935 		}
4936 	skipRemap:
4937 #endif // salvageAgfa
4938 		if ((lLength % 2) != 0) { //https://www.nitrc.org/forum/forum.php?thread_id=11827&forum_id=4703
4939 			printMessage("Illegal DICOM tag %04x,%04x (odd element length %d): %s\n", groupElement & 65535, groupElement >> 16, lLength, fname);
4940 			//proper to return here, but we can carry on as a hail mary
4941 			// d.isValid = false;
4942 			//return d;
4943 		}
4944 		switch (groupElement) {
4945 		case kMediaStorageSOPClassUID: {
4946 			char mediaUID[kDICOMStr];
4947 			dcmStr(lLength, &buffer[lPos], mediaUID);
4948 			//Philips "XX_" files
4949 			//see https://github.com/rordenlab/dcm2niix/issues/328
4950 			if (strstr(mediaUID, "1.2.840.10008.5.1.4.1.1.66") != NULL)
4951 				d.isRawDataStorage = true;
4952 			if (strstr(mediaUID, "1.3.46.670589.11.0.0.12.1") != NULL)
4953 				d.isRawDataStorage = true; //Private MR Spectrum Storage
4954 			if (strstr(mediaUID, "1.3.46.670589.11.0.0.12.2") != NULL)
4955 				d.isRawDataStorage = true; //Private MR Series Data Storage
4956 			if (strstr(mediaUID, "1.3.46.670589.11.0.0.12.4") != NULL)
4957 				d.isRawDataStorage = true; //Private MR Examcard Storage
4958 			if (d.isRawDataStorage)
4959 				d.isDerived = true;
4960 			if (d.isRawDataStorage)
4961 				printMessage("Skipping non-image DICOM: %s\n", fname);
4962 			//Philips "PS_" files
4963 			if (strstr(mediaUID, "1.2.840.10008.5.1.4.1.1.11.1") != NULL)
4964 				d.isGrayscaleSoftcopyPresentationState = true;
4965 			if (d.isGrayscaleSoftcopyPresentationState)
4966 				d.isDerived = true;
4967 			break;
4968 		}
4969 		case kMediaStorageSOPInstanceUID: { // 0002, 0003
4970 			//char SOPInstanceUID[kDICOMStr];
4971 			dcmStr(lLength, &buffer[lPos], d.instanceUID);
4972 			//printMessage(">>%s\n", d.seriesInstanceUID);
4973 			d.instanceUidCrc = mz_crc32X((unsigned char *)&d.instanceUID, strlen(d.instanceUID));
4974 			break;
4975 		}
4976 		case kTransferSyntax: {
4977 			char transferSyntax[kDICOMStr];
4978 			strcpy(transferSyntax, "");
4979 			dcmStr(lLength, &buffer[lPos], transferSyntax);
4980 			if (strcmp(transferSyntax, "1.2.840.10008.1.2.1") == 0)
4981 				; //default isExplicitVR=true; //d.isLittleEndian=true
4982 			else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.50") == 0) {
4983 				d.compressionScheme = kCompress50;
4984 				//printMessage("Lossy JPEG: please decompress with Osirix or dcmdjpg. %s\n", transferSyntax);
4985 				//d.imageStart = 1; //abort as invalid (imageStart MUST be >128)
4986 			} else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.51") == 0) {
4987 				d.compressionScheme = kCompress50;
4988 			} else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.57") == 0) {
4989 				//d.isCompressed = true;
4990 				//https://www.medicalconnections.co.uk/kb/Transfer_Syntax should be SOF = 0xC3
4991 				d.compressionScheme = kCompressC3;
4992 				//printMessage("Ancient JPEG-lossless (SOF type 0xc3): please check conversion\n");
4993 			} else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.70") == 0) {
4994 				d.compressionScheme = kCompressC3;
4995 			} else if ((strcmp(transferSyntax, "1.2.840.10008.1.2.4.80") == 0) || (strcmp(transferSyntax, "1.2.840.10008.1.2.4.81") == 0)) {
4996 #if defined(myEnableJPEGLS) || defined(myEnableJPEGLS1)
4997 				d.compressionScheme = kCompressJPEGLS;
4998 #else
4999 				printWarning("Unsupported transfer syntax '%s' (decode with 'dcmdjpls jpg.dcm raw.dcm' or 'gdcmconv -w jpg.dcm raw.dcm', or recompile dcm2niix with JPEGLS support)\n", transferSyntax);
5000 				d.imageStart = 1; //abort as invalid (imageStart MUST be >128)
5001 #endif
5002 			} else if (strcmp(transferSyntax, "1.3.46.670589.33.1.4.1") == 0) {
5003 				d.compressionScheme = kCompressPMSCT_RLE1;
5004 				//printMessage("Unsupported transfer syntax '%s' (decode with rle2img)\n",transferSyntax);
5005 				//d.imageStart = 1; //abort as invalid (imageStart MUST be >128)
5006 			} else if ((compressFlag != kCompressNone) && (strcmp(transferSyntax, "1.2.840.10008.1.2.4.90") == 0)) {
5007 				d.compressionScheme = kCompressYes;
5008 				//printMessage("JPEG2000 Lossless support is new: please validate conversion\n");
5009 			} else if ((strcmp(transferSyntax, "1.2.840.10008.1.2.1.99") == 0)) {
5010 				//n.b. Deflate compression applied applies to the encoding of the **entire** DICOM Data Set, not just image data
5011 				// see https://www.medicalconnections.co.uk/kb/Transfer-Syntax/
5012 				//#ifndef myDisableZLib
5013 				//d.compressionScheme = kCompressDeflate;
5014 				//#else
5015 				printWarning("Unsupported transfer syntax '%s' (inflate files with 'dcmconv +te gz.dcm raw.dcm' or 'gdcmconv -w gz.dcm raw.dcm)'\n", transferSyntax);
5016 				d.imageStart = 1; //abort as invalid (imageStart MUST be >128)
5017 				//#endif
5018 			} else if ((compressFlag != kCompressNone) && (strcmp(transferSyntax, "1.2.840.10008.1.2.4.91") == 0)) {
5019 				d.compressionScheme = kCompressYes;
5020 				//printMessage("JPEG2000 support is new: please validate conversion\n");
5021 			} else if (strcmp(transferSyntax, "1.2.840.10008.1.2.5") == 0)
5022 				d.compressionScheme = kCompressRLE; //run length
5023 			else if (strcmp(transferSyntax, "1.2.840.10008.1.2.2") == 0)
5024 				isSwitchToBigEndian = true; //isExplicitVR=true;
5025 			else if (strcmp(transferSyntax, "1.2.840.10008.1.2") == 0)
5026 				isSwitchToImplicitVR = true; //d.isLittleEndian=true
5027 			else {
5028 				if (lLength < 1) //"1.2.840.10008.1.2"
5029 					printWarning("Missing transfer syntax: assuming default (1.2.840.10008.1.2)\n");
5030 				else {
5031 					printWarning("Unsupported transfer syntax '%s' (see www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage)\n", transferSyntax);
5032 					d.imageStart = 1; //abort as invalid (imageStart MUST be >128)
5033 				}
5034 			}
5035 			break;
5036 		} //{} provide scope for variable 'transferSyntax
5037 		case kImplementationVersionName: {
5038 			char impTxt[kDICOMStr];
5039 			dcmStr(lLength, &buffer[lPos], impTxt);
5040 			int slen = (int)strlen(impTxt);
5041 			if ((slen > 5) && (strstr(impTxt, "MATLAB") != NULL))
5042 				isMATLAB = true;
5043 			if ((slen < 5) || (strstr(impTxt, "XA10A") == NULL))
5044 				break;
5045 			d.isXA10A = true;
5046 			break;
5047 		}
5048 		case kSourceApplicationEntityTitle: {
5049 			char saeTxt[kDICOMStr];
5050 			dcmStr(lLength, &buffer[lPos], saeTxt);
5051 			int slen = (int)strlen(saeTxt);
5052 			if ((slen < 5) || (strstr(saeTxt, "oasis") == NULL))
5053 				break;
5054 			d.isSegamiOasis = true;
5055 			break;
5056 		}
5057 		case kDirectoryRecordSequence: {
5058 			d.isRawDataStorage = true;
5059 			break;
5060 		}
5061 		case kImageTypeTag: {
5062 			bool is1st = strlen(d.imageType) == 0;
5063 			dcmStr(lLength, &buffer[lPos], d.imageType, false); //<-distinguish spaces from pathdelim: [ORIGINAL\PHASE MAP\FFE] should return "PHASE MAP" not "PHASE_MAP"
5064 			int slen;
5065 			slen = (int)strlen(d.imageType);
5066 			if (slen > 1) {
5067 				for (int i = 0; i < slen; i++)
5068 					if (d.imageType[i] == '\\')
5069 						d.imageType[i] = '_';
5070 			}
5071 			if (is1st)
5072 				strcpy(imageType1st, d.imageType);
5073 			if ((slen > 5) && strstr(d.imageType, "_MOCO_")) {
5074 				//d.isDerived = true; //this would have 'i- y' skip MoCo images
5075 				isMoCo = true;
5076 			}
5077 			if ((slen > 5) && strstr(d.imageType, "B0") && strstr(d.imageType, "MAP"))
5078 				d.isRealIsPhaseMapHz = true;
5079 			if ((slen > 5) && strstr(d.imageType, "_ADC_"))
5080 				d.isDerived = true;
5081 			if ((slen > 5) && strstr(d.imageType, "_TRACEW_"))
5082 				d.isDerived = true;
5083 			if ((slen > 5) && strstr(d.imageType, "_TRACE_"))
5084 				d.isDerived = true;
5085 			if ((slen > 5) && strstr(d.imageType, "_FA_"))
5086 				d.isDerived = true;
5087 			if ((slen > 12) && strstr(d.imageType, "_DIFFUSION_"))
5088 				d.isDiffusion = true;
5089 			//if (strcmp(transferSyntax, "ORIGINAL_PRIMARY_M_ND_MOSAIC") == 0)
5090 			if ((slen > 5) && !strcmp(d.imageType + slen - 6, "MOSAIC"))
5091 				isMosaic = true;
5092 			//const char* prefix = "MOSAIC";
5093 			const char *pos = strstr(d.imageType, "MOSAIC");
5094 			//const char p = (const char *) d.imageType;
5095 			//p = (const char) strstr(d.imageType, "MOSAIC");
5096 			//const char* p = strstr(d.imageType, "MOSAIC");
5097 			if (pos != NULL)
5098 				isMosaic = true;
5099 			//isNonImage 0008,0008 = DERIVED,CSAPARALLEL,POSDISP
5100 			// sometime ComplexImageComponent 0008,9208 is missing - see ADNI data
5101 			// attempt to detect non-images, see https://github.com/scitran/data/blob/a516fdc39d75a6e4ac75d0e179e18f3a5fc3c0af/scitran/data/medimg/dcm/mr/siemens.py
5102 			//For Philips combinations see Table 3-28 Table 3-28: Valid combinations of Image Type applied values
5103 			// http://incenter.medical.philips.com/doclib/enc/fetch/2000/4504/577242/577256/588723/5144873/5144488/5144982/DICOM_Conformance_Statement_Intera_R7%2c_R8_and_R9.pdf%3fnodeid%3d5147977%26vernum%3d-2
5104 			if ((slen > 3) && (strstr(d.imageType, "_R_") != NULL)) {
5105 				d.isHasReal = true;
5106 				isReal = true;
5107 			}
5108 			if ((slen > 3) && (strstr(d.imageType, "_M_") != NULL)) {
5109 				d.isHasMagnitude = true;
5110 				isMagnitude = true;
5111 			}
5112 			if ((slen > 3) && (strstr(d.imageType, "_I_") != NULL)) {
5113 				d.isHasImaginary = true;
5114 				isImaginary = true;
5115 			}
5116 			if ((slen > 3) && (strstr(d.imageType, "_P_") != NULL)) {
5117 				d.isHasPhase = true;
5118 				isPhase = true;
5119 			}
5120 			if ((slen > 6) && (strstr(d.imageType, "_REAL_") != NULL)) {
5121 				d.isHasReal = true;
5122 				isReal = true;
5123 			}
5124 			if ((slen > 11) && (strstr(d.imageType, "_MAGNITUDE_") != NULL)) {
5125 				d.isHasMagnitude = true;
5126 				isMagnitude = true;
5127 			}
5128 			if ((slen > 11) && (strstr(d.imageType, "_IMAGINARY_") != NULL)) {
5129 				d.isHasImaginary = true;
5130 				isImaginary = true;
5131 			}
5132 			if ((slen > 6) && (strstr(d.imageType, "PHASE") != NULL)) {
5133 				d.isHasPhase = true;
5134 				isPhase = true;
5135 			}
5136 			if ((slen > 6) && (strstr(d.imageType, "DERIVED") != NULL))
5137 				d.isDerived = true;
5138 			//if((slen > 4) && (strstr(typestr, "DIS2D") != NULL) )
5139 			//	d.isNonImage = true;
5140 			//not mutually exclusive: possible for Philips enhanced DICOM to store BOTH magnitude and phase in the same image
5141 			break;
5142 		}
5143 		case kAcquisitionDate:
5144 			char acquisitionDateTxt[kDICOMStr];
5145 			dcmStr(lLength, &buffer[lPos], acquisitionDateTxt);
5146 			d.acquisitionDate = atof(acquisitionDateTxt);
5147 			break;
5148 		case kAcquisitionDateTime:
5149 			//char acquisitionDateTimeTxt[kDICOMStr];
5150 			dcmStr(lLength, &buffer[lPos], acquisitionDateTimeTxt);
5151 			//printMessage("%s\n",acquisitionDateTimeTxt);
5152 			break;
5153 		case kStudyDate:
5154 			dcmStr(lLength, &buffer[lPos], d.studyDate);
5155 			if (((int)strlen(d.studyDate) > 7) && (strstr(d.studyDate, "19000101") != NULL))
5156 				isNeologica = true;
5157 			break;
5158 		case kModality:
5159 			if (lLength < 2)
5160 				break;
5161 			if ((buffer[lPos] == 'C') && (toupper(buffer[lPos + 1]) == 'R'))
5162 				d.modality = kMODALITY_CR;
5163 			else if ((buffer[lPos] == 'C') && (toupper(buffer[lPos + 1]) == 'T'))
5164 				d.modality = kMODALITY_CT;
5165 			if ((buffer[lPos] == 'M') && (toupper(buffer[lPos + 1]) == 'R'))
5166 				d.modality = kMODALITY_MR;
5167 			if ((buffer[lPos] == 'P') && (toupper(buffer[lPos + 1]) == 'T'))
5168 				d.modality = kMODALITY_PT;
5169 			if ((buffer[lPos] == 'U') && (toupper(buffer[lPos + 1]) == 'S'))
5170 				d.modality = kMODALITY_US;
5171 			break;
5172 		case kManufacturer:
5173 			if (d.manufacturer == kMANUFACTURER_UNKNOWN)
5174 				d.manufacturer = dcmStrManufacturer(lLength, &buffer[lPos]);
5175 			volDiffusion.manufacturer = d.manufacturer;
5176 			break;
5177 		case kInstitutionName:
5178 			dcmStr(lLength, &buffer[lPos], d.institutionName);
5179 			break;
5180 		case kInstitutionAddress: //VR is "ST": 1024 chars maximum
5181 			dcmStr(lLength, &buffer[lPos], d.institutionAddress);
5182 			break;
5183 		case kReferringPhysicianName:
5184 			dcmStr(lLength, &buffer[lPos], d.referringPhysicianName);
5185 			break;
5186 		case kComplexImageComponent:
5187 			if (is2005140FSQ)
5188 				break; //see Maastricht DICOM data for magnitude data with this field set as REAL! https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging
5189 			if (lLength < 2)
5190 				break;
5191 			//issue 256: Philips files report real ComplexImageComponent but Magnitude ImageType https://github.com/rordenlab/dcm2niix/issues/256
5192 			isPhase = false;
5193 			isReal = false;
5194 			isImaginary = false;
5195 			isMagnitude = false;
5196 			//see Table C.8-85 http://dicom.nema.org/medical/Dicom/2017c/output/chtml/part03/sect_C.8.13.3.html
5197 			if ((buffer[lPos] == 'R') && (toupper(buffer[lPos + 1]) == 'E'))
5198 				isReal = true;
5199 			if ((buffer[lPos] == 'I') && (toupper(buffer[lPos + 1]) == 'M'))
5200 				isImaginary = true;
5201 			if ((buffer[lPos] == 'P') && (toupper(buffer[lPos + 1]) == 'H'))
5202 				isPhase = true;
5203 			if ((buffer[lPos] == 'M') && (toupper(buffer[lPos + 1]) == 'A'))
5204 				isMagnitude = true;
5205 			//not mutually exclusive: possible for Philips enhanced DICOM to store BOTH magnitude and phase in the same image
5206 			if (isPhase)
5207 				d.isHasPhase = true;
5208 			if (isReal)
5209 				d.isHasReal = true;
5210 			if (isImaginary)
5211 				d.isHasImaginary = true;
5212 			if (isMagnitude)
5213 				d.isHasMagnitude = true;
5214 			break;
5215 		case kAcquisitionContrast:
5216 			char acqContrast[kDICOMStr];
5217 			dcmStr(lLength, &buffer[lPos], acqContrast);
5218 			if (((int)strlen(acqContrast) > 8) && (strstr(acqContrast, "DIFFUSION") != NULL))
5219 				d.isDiffusion = true;
5220 			if (((int)strlen(acqContrast) > 8) && (strstr(acqContrast, "PERFUSION") != NULL))
5221 				isASL = true; //see series 301 of dcm_qa_philips_asl
5222 			break;
5223 		case kAcquisitionTime: {
5224 			char acquisitionTimeTxt[kDICOMStr];
5225 			dcmStr(lLength, &buffer[lPos], acquisitionTimeTxt);
5226 			d.acquisitionTime = atof(acquisitionTimeTxt);
5227 			if (d.manufacturer != kMANUFACTURER_UIH)
5228 				break;
5229 			//UIH slice timing- do not use for Siemens as Siemens de-identification can corrupt this field https://github.com/rordenlab/dcm2niix/issues/236
5230 			d.CSA.sliceTiming[acquisitionTimesGE_UIH] = d.acquisitionTime;
5231 			acquisitionTimesGE_UIH++;
5232 			break;
5233 		}
5234 		case kSeriesTime:
5235 			dcmStr(lLength, &buffer[lPos], seriesTimeTxt);
5236 			break;
5237 		case kStudyTime:
5238 			if (strlen(d.studyTime) < 2)
5239 				dcmStr(lLength, &buffer[lPos], d.studyTime);
5240 			break;
5241 		case kPatientName:
5242 			dcmStr(lLength, &buffer[lPos], d.patientName);
5243 			break;
5244 		case kAnatomicalOrientationType: {
5245 			char aotTxt[kDICOMStr]; //ftp://dicom.nema.org/MEDICAL/dicom/2015b/output/chtml/part03/sect_C.7.6.2.html#sect_C.7.6.2.1.1
5246 			dcmStr(lLength, &buffer[lPos], aotTxt);
5247 			int slen = (int)strlen(aotTxt);
5248 			if ((slen < 9) || (strstr(aotTxt, "QUADRUPED") == NULL))
5249 				break;
5250 			printError("Anatomical Orientation Type (0010,2210) is QUADRUPED: rotate coordinates accordingly\n");
5251 			break;
5252 		}
5253 		case kDeidentificationMethod: { //issue 383
5254 			char anonTxt[kDICOMStr];
5255 			dcmStr(lLength, &buffer[lPos], anonTxt);
5256 			int slen = (int)strlen(anonTxt);
5257 			if ((slen < 10) || (strstr(anonTxt, "DICOMANON") == NULL))
5258 				break;
5259 			isDICOMANON = true;
5260 			printWarning("Matlab DICOMANON can scramble SeriesInstanceUID (0020,000e) and remove crucial data (see issue 383). \n");
5261 			break;
5262 		}
5263 		case kPatientID:
5264 			if (strlen(d.patientID) > 1)
5265 				break;
5266 			dcmStr(lLength, &buffer[lPos], d.patientID);
5267 			break;
5268 		case kAccessionNumber:
5269 			dcmStr(lLength, &buffer[lPos], d.accessionNumber);
5270 			break;
5271 		case kPatientBirthDate:
5272 			dcmStr(lLength, &buffer[lPos], d.patientBirthDate);
5273 			break;
5274 		case kPatientSex: {
5275 			//must be M,F,O: http://dicom.nema.org/dicom/2013/output/chtml/part03/sect_C.2.html
5276 			char patientSex = toupper(buffer[lPos]);
5277 			if ((patientSex == 'M') || (patientSex == 'F') || (patientSex == 'O'))
5278 				d.patientSex = patientSex;
5279 			break;
5280 		}
5281 		case kPatientAge:
5282 			dcmStr(lLength, &buffer[lPos], d.patientAge);
5283 			break;
5284 		case kPatientWeight:
5285 			d.patientWeight = dcmStrFloat(lLength, &buffer[lPos]);
5286 			break;
5287 		case kStationName:
5288 			dcmStr(lLength, &buffer[lPos], d.stationName);
5289 			break;
5290 		case kSeriesDescription:
5291 			dcmStr(lLength, &buffer[lPos], d.seriesDescription);
5292 			break;
5293 		case kInstitutionalDepartmentName:
5294 			dcmStr(lLength, &buffer[lPos], d.institutionalDepartmentName);
5295 			break;
5296 		case kManufacturersModelName:
5297 			dcmStr(lLength, &buffer[lPos], d.manufacturersModelName);
5298 			break;
5299 		case kDerivationDescription: {
5300 			//strcmp(transferSyntax, "1.2.840.10008.1.2")
5301 			char derivationDescription[kDICOMStr];
5302 			dcmStr(lLength, &buffer[lPos], derivationDescription); //strcasecmp, strcmp
5303 			if (strcasecmp(derivationDescription, "MEDCOM_RESAMPLED") == 0)
5304 				d.isResampled = true;
5305 			break;
5306 		}
5307 		case kDeviceSerialNumber: {
5308 			dcmStr(lLength, &buffer[lPos], d.deviceSerialNumber);
5309 			break;
5310 		}
5311 		case kSoftwareVersions: {
5312 			dcmStr(lLength, &buffer[lPos], d.softwareVersions);
5313 			int slen = (int)strlen(d.softwareVersions);
5314 			if ((slen > 4) && (strstr(d.softwareVersions, "XA11") != NULL))
5315 				d.isXA10A = true;
5316 			if ((slen > 4) && (strstr(d.softwareVersions, "XA20") != NULL))
5317 				d.isXA10A = true;
5318 			if ((slen > 4) && (strstr(d.softwareVersions, "XA30") != NULL))
5319 				d.isXA10A = true;
5320 			if ((slen < 5) || (strstr(d.softwareVersions, "XA10") == NULL))
5321 				break;
5322 			d.isXA10A = true;
5323 			break;
5324 		}
5325 		case kProtocolName: {
5326 			dcmStr(lLength, &buffer[lPos], d.protocolName);
5327 			break;
5328 		}
5329 		case kPatientOrient:
5330 			dcmStr(lLength, &buffer[lPos], d.patientOrient);
5331 			break;
5332 		case kInversionRecovery: // CS [YES],[NO]
5333 			if (lLength < 2)
5334 				break;
5335 			if (toupper(buffer[lPos]) == 'Y')
5336 				d.isIR = true;
5337 			break;
5338 		case kSpoiling: // CS 0018,9016
5339 			if (lLength < 2)
5340 				break;
5341 			char sTxt[kDICOMStr];
5342 			dcmStr(lLength, &buffer[lPos], sTxt);
5343 			if ((strstr(sTxt, "RF") != NULL) && (strstr(sTxt, "RF") != NULL))
5344 				d.spoiling = kSPOILING_RF_AND_GRADIENT;
5345 			else if (strstr(sTxt, "RF") != NULL)
5346 				d.spoiling = kSPOILING_RF;
5347 			else if (strstr(sTxt, "GRADIENT") != NULL)
5348 				d.spoiling = kSPOILING_GRADIENT;
5349 			else if (strstr(sTxt, "NONE") != NULL)
5350 				d.spoiling = kSPOILING_NONE;
5351 			break;
5352 		case kEchoPlanarPulseSequence: // CS [YES],[NO]
5353 			if (lLength < 2)
5354 				break;
5355 			if (toupper(buffer[lPos]) == 'Y')
5356 				d.isEPI = true;
5357 			break;
5358 		case kMagnetizationTransferAttribute: //'CS' 'ON_RESONANCE','OFF_RESONANCE','NONE'
5359 			if (lLength < 2)
5360 				break; //https://github.com/bids-standard/bids-specification/pull/681
5361 			if ((toupper(buffer[lPos]) == 'O') && (toupper(buffer[lPos+1]) == 'F')) // OFF_RESONANCE
5362 				d.mtState = 1; //TRUE
5363 			if ((toupper(buffer[lPos]) == 'O') && (toupper(buffer[lPos+1]) == 'N')) // ON_RESONANCE and NONE
5364 				d.mtState = 0; //FALSE
5365 			if ((toupper(buffer[lPos]) == 'N') && (toupper(buffer[lPos+1]) == 'O')) // ON_RESONANCE and NONE
5366 				d.mtState = 0; //FALSE
5367 			break;
5368 		case kRectilinearPhaseEncodeReordering: { //'CS' [REVERSE_LINEAR],[LINEAR],[CENTRIC],[REVERSE_CENTRIC]
5369 			if (d.manufacturer != kMANUFACTURER_GE)
5370 				break; //only found in GE software beginning with RX27
5371 			if (lLength < 2)
5372 				break;
5373 			if (toupper(buffer[lPos]) == 'L')
5374 				d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED;
5375 			if (toupper(buffer[lPos]) == 'R')
5376 				d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED;
5377 			break;
5378 		}
5379 		case kPartialFourierDirection: { //'CS' PHASE FREQUENCY SLICE_SELECT COMBINATION
5380 			if (lLength < 2)
5381 				break;
5382 			if (toupper(buffer[lPos]) == 'P')
5383 				d.partialFourierDirection = kPARTIAL_FOURIER_DIRECTION_PHASE;
5384 			if (toupper(buffer[lPos]) == 'F')
5385 				d.partialFourierDirection = kPARTIAL_FOURIER_DIRECTION_FREQUENCY;
5386 			if (toupper(buffer[lPos]) == 'S')
5387 				d.partialFourierDirection = kPARTIAL_FOURIER_DIRECTION_SLICE_SELECT;
5388 			if (toupper(buffer[lPos]) == 'C')
5389 				d.partialFourierDirection = kPARTIAL_FOURIER_DIRECTION_COMBINATION;
5390 			break;
5391 		}
5392 		case kCardiacSynchronizationTechnique:
5393 			if (toupper(buffer[lPos]) == 'P')
5394 				isProspectiveSynced = true;
5395 			break;
5396 		case kParallelReductionFactorInPlane:
5397 			if (d.manufacturer == kMANUFACTURER_SIEMENS)
5398 				break;
5399 			d.accelFactPE = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
5400 			break;
5401 		case kAcquisitionDuration:
5402 			//n.b. used differently by different vendors https://github.com/rordenlab/dcm2niix/issues/225
5403 			d.acquisitionDuration = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
5404 			break;
5405 		//in theory, 0018,9074 could provide XA10 slice time information, but scrambled by XA10 de-identification: better to use 0021,1104
5406 		//case kFrameAcquisitionDateTime: {
5407 		// //(0018,9074) DT [20190621095516.140000] YYYYMMDDHHMMSS
5408 		// //see https://github.com/rordenlab/dcm2niix/issues/303
5409 		//	char dateTime[kDICOMStr];
5410 		//	dcmStr(lLength, &buffer[lPos], dateTime);
5411 		//	printf("%s\tkFrameAcquisitionDateTime\n", dateTime);
5412 		//}
5413 		case kDiffusionDirectionality: { // 0018, 9075
5414 			set_directionality0018_9075(&volDiffusion, (&buffer[lPos]));
5415 			if ((d.manufacturer != kMANUFACTURER_PHILIPS) || (lLength < 10))
5416 				break;
5417 			char dir[kDICOMStr];
5418 			dcmStr(lLength, &buffer[lPos], dir);
5419 			if (strcmp(dir, "ISOTROPIC") == 0)
5420 				isPhilipsDerived = true;
5421 			break;
5422 		}
5423 		case kParallelAcquisitionTechnique: //CS
5424 			dcmStr(lLength, &buffer[lPos], d.parallelAcquisitionTechnique);
5425 			break;
5426 		case kInversionTimes: { //issue 380
5427 			if ((lLength < 8) || ((lLength % 8) != 0))
5428 				break;
5429 			d.TI = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
5430 			break;
5431 		}
5432 		case kPartialFourier: //(0018,9081) CS [YES],[NO]
5433 			if (lLength < 2)
5434 				break;
5435 			if (toupper(buffer[lPos]) == 'Y')
5436 				d.isPartialFourier = true;
5437 			break;
5438 		case kMREchoSequence:
5439 			if (d.manufacturer != kMANUFACTURER_BRUKER)
5440 				break;
5441 			if (sqDepth == 0)
5442 				sqDepth = 1; //should not happen, in case faulty anonymization
5443 			sqDepth00189114 = sqDepth - 1;
5444 			break;
5445 		case kMRAcquisitionPhaseEncodingStepsInPlane:
5446 			d.phaseEncodingLines = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5447 			break;
5448 		case kNumberOfImagesInMosaic:
5449 			if (d.manufacturer == kMANUFACTURER_SIEMENS)
5450 				numberOfImagesInMosaic = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5451 			break;
5452 		case kSeriesPlaneGE: //SS 2=Axi, 4=Sag, 8=Cor, 16=Obl, 256=3plane
5453 			if (d.manufacturer != kMANUFACTURER_GE)
5454 				break;
5455 			if (dcmInt(lLength, &buffer[lPos], d.isLittleEndian) == 256)
5456 				d.isLocalizer = true;
5457 			break;
5458 		case kDwellTime:
5459 			d.dwellTime = dcmStrInt(lLength, &buffer[lPos]);
5460 			break;
5461 		case kDiffusion_bValueSiemens:
5462 			if (d.manufacturer != kMANUFACTURER_SIEMENS)
5463 				break;
5464 			//issue409
5465 			B0Philips = dcmStrInt(lLength, &buffer[lPos]);
5466 			set_bVal(&volDiffusion, B0Philips);
5467 			break;
5468 		case kSliceTimeSiemens: { //Array of FD (64-bit double)
5469 			if (d.manufacturer != kMANUFACTURER_SIEMENS)
5470 				break;
5471 			if ((lLength < 8) || ((lLength % 8) != 0))
5472 				break;
5473 			int nSlicesTimes = lLength / 8;
5474 			if (nSlicesTimes > kMaxEPI3D)
5475 				break;
5476 			d.CSA.mosaicSlices = nSlicesTimes;
5477 			//printf(">>>> %d\n", nSlicesTimes);
5478 			//issue 296: for images de-identified to remove readCSAImageHeader
5479 			for (int z = 0; z < nSlicesTimes; z++)
5480 				d.CSA.sliceTiming[z] = dcmFloatDouble(8, &buffer[lPos + (z * 8)], d.isLittleEndian);
5481 			//for (int z = 0; z < nSlicesTimes; z++)
5482 			//	printf("%d>>>%g\n", z+1, d.CSA.sliceTiming[z]);
5483 			checkSliceTimes(&d.CSA, nSlicesTimes, isVerbose, d.is3DAcq);
5484 			//d.CSA.dtiV[0] = dcmStrInt(lLength, &buffer[lPos]);
5485 			//d.CSA.numDti = 1;
5486 			break;
5487 		}
5488 		case kDiffusionGradientDirectionSiemens: {
5489 			if (d.manufacturer != kMANUFACTURER_SIEMENS)
5490 				break;
5491 			float v[4];
5492 			dcmMultiFloatDouble(lLength, &buffer[lPos], 3, v, d.isLittleEndian);
5493 			//dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, v);
5494 			//printf(">>>%g %g %g\n", v[0], v[1], v[2]);
5495 			d.CSA.dtiV[1] = v[0];
5496 			d.CSA.dtiV[2] = v[1];
5497 			d.CSA.dtiV[3] = v[2];
5498 			break;
5499 		}
5500 		case kNumberOfDiffusionDirectionGE: {
5501 			if (d.manufacturer != kMANUFACTURER_GE)
5502 				break;
5503 			float f = dcmStrFloat(lLength, &buffer[lPos]);
5504 			d.numberOfDiffusionDirectionGE = round(f);
5505 			break;
5506 		}
5507 		case kLastScanLoc:
5508 			d.lastScanLoc = dcmStrFloat(lLength, &buffer[lPos]);
5509 			break;
5510 		//GE bug: multiple echos can create identical instance numbers
5511 		// in theory, one could detect as kRawDataRunNumberGE varies
5512 		// sliceN of echoE will have the same value for all timepoints
5513 		// this value does not appear indexed
5514 		// different echoes record same echo time.
5515 		// use multiEchoSortGEDICOM.py to salvage
5516 		case kRawDataRunNumberGE:
5517 			if (d.manufacturer != kMANUFACTURER_GE)
5518 				break;
5519 			d.rawDataRunNumber = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5520 			break;
5521 		case kMaxEchoNumGE:
5522 			if (d.manufacturer != kMANUFACTURER_GE)
5523 				break;
5524 			d.maxEchoNumGE = round(dcmStrFloat(lLength, &buffer[lPos]));
5525 			break;
5526 		case kUserData12GE: {
5527 			if (d.manufacturer != kMANUFACTURER_GE)
5528 				break;
5529 			userData12GE = round(dcmStrFloat(lLength, &buffer[lPos]));
5530 			//printf("%d<<<<\n", userData12GE);
5531 			break; }
5532 		case kDiffusionDirectionGEX:
5533 			if (d.manufacturer == kMANUFACTURER_GE)
5534 				set_diffusion_directionGE(&volDiffusion, lLength, (&buffer[lPos]), 0);
5535 			break;
5536 		case kDiffusionDirectionGEY:
5537 			if (d.manufacturer == kMANUFACTURER_GE)
5538 				set_diffusion_directionGE(&volDiffusion, lLength, (&buffer[lPos]), 1);
5539 			break;
5540 		case kDiffusionDirectionGEZ:
5541 			if (d.manufacturer == kMANUFACTURER_GE)
5542 				set_diffusion_directionGE(&volDiffusion, lLength, (&buffer[lPos]), 2);
5543 			break;
5544 		case kPulseSequenceNameGE: { //LO 'epi'/'epiRT'
5545 			if (d.manufacturer != kMANUFACTURER_GE)
5546 				break;
5547 			char epiStr[kDICOMStr];
5548 			dcmStr(lLength, &buffer[lPos], epiStr);
5549 			if (strstr(epiStr, "epi_pepolar") != NULL) {
5550 				d.epiVersionGE = kGE_EPI_PEPOLAR_FWD; //n.b. combine with 0019,10B3
5551 			} else if (strstr(epiStr, "epi2") != NULL) {
5552 				d.epiVersionGE = kGE_EPI_EPI2; //-1 = not epi, 0 = epi, 1 = epiRT, 2 = epi2
5553 			} else if (strstr(epiStr, "epiRT") != NULL) {
5554 				d.epiVersionGE = kGE_EPI_EPIRT; //-1 = not epi, 0 = epi, 1 = epiRT
5555 			} else if (strstr(epiStr, "epi") != NULL) {
5556 				d.epiVersionGE = kGE_EPI_EPI; //-1 = not epi, 0 = epi, 1 = epiRT
5557 			}
5558 			if (strcmp(epiStr, "3db0map") == 0) {
5559 				isGEfieldMap = true; //issue501
5560 			}
5561 			break;
5562 		}
5563 		case kInternalPulseSequenceNameGE: { //LO 'EPI'(gradient echo)/'EPI2'(spin echo):
5564 			if (d.manufacturer != kMANUFACTURER_GE)
5565 				break;
5566 			char epiStr[kDICOMStr];
5567 			dcmStr(lLength, &buffer[lPos], epiStr);
5568 			if ((d.epiVersionGE < kGE_EPI_PEPOLAR_FWD) && (strcmp(epiStr, "EPI") == 0)) {
5569 				d.internalepiVersionGE = 1; //-1 = not EPI, 1 = EPI, 2 = EPI2
5570 				if (d.epiVersionGE != 1) {	// 1 = epiRT by kEpiRTGroupDelayGE or kPulseSequenceNameGE
5571 					d.epiVersionGE = 0;		// 0 = epi (multi-phase epi)
5572 				}
5573 			}
5574 			if (strcmp(epiStr, "EPI2") == 0) {
5575 				d.internalepiVersionGE = 2; //-1 = not epi, 1 = EPI, 2 = EPI2
5576 			}
5577 			if (strcmp(epiStr, "B0map") == 0) {
5578 				isGEfieldMap = true; //issue501
5579 			}
5580 			break;
5581 		}
5582 		case kBandwidthPerPixelPhaseEncode:
5583 			d.bandwidthPerPixelPhaseEncode = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
5584 			break;
5585 		case kStudyInstanceUID: // 0020,000D
5586 			dcmStr(lLength, &buffer[lPos], d.studyInstanceUID);
5587 			break;
5588 		case kSeriesInstanceUID: // 0020,000E
5589 			dcmStr(lLength, &buffer[lPos], d.seriesInstanceUID);
5590 			//printMessage(">>%s\n", d.seriesInstanceUID);
5591 			d.seriesUidCrc = mz_crc32X((unsigned char *)&d.seriesInstanceUID, strlen(d.seriesInstanceUID));
5592 			break;
5593 		case kImagePositionPatient: {
5594 			if (is2005140FSQ) {
5595 				dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &patientPositionPrivate[0]);
5596 				break;
5597 			}
5598 			patientPositionNum++;
5599 			isAtFirstPatientPosition = true;
5600 			//char dx[kDICOMStr];
5601 			//dcmStr(lLength, &buffer[lPos], dx);
5602 			//printMessage("*%s*", dx);
5603 			dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &patientPosition[0]); //slice position
5604 			if (isnan(d.patientPosition[1])) {
5605 				//dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPosition[0]); //slice position
5606 				for (int k = 0; k < 4; k++)
5607 					d.patientPosition[k] = patientPosition[k];
5608 			} else {
5609 				//dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPositionLast[0]); //slice direction for 4D
5610 				for (int k = 0; k < 4; k++)
5611 					d.patientPositionLast[k] = patientPosition[k];
5612 				if ((isFloatDiff(d.patientPositionLast[1], d.patientPosition[1])) ||
5613 					(isFloatDiff(d.patientPositionLast[2], d.patientPosition[2])) ||
5614 					(isFloatDiff(d.patientPositionLast[3], d.patientPosition[3]))) {
5615 					isAtFirstPatientPosition = false; //this slice is not at position of 1st slice
5616 						//if (d.patientPositionSequentialRepeats == 0) //this is the first slice with different position
5617 						//	d.patientPositionSequentialRepeats = patientPositionNum-1;
5618 				}		//if different position from 1st slice in file
5619 			}			//if not first slice in file
5620 			set_isAtFirstPatientPosition_tvd(&volDiffusion, isAtFirstPatientPosition);
5621 			//if (isAtFirstPatientPosition) numFirstPatientPosition++;
5622 			if (isVerbose > 0) //verbose > 1 will report full DICOM tag
5623 				printMessage("   Patient Position 0020,0032 (#,@,X,Y,Z)\t%d\t%ld\t%g\t%g\t%g\n", patientPositionNum, lPos, patientPosition[1], patientPosition[2], patientPosition[3]);
5624 			if ((isOrient) && (nSliceMM < kMaxSlice2D)) {
5625 				vec3 pos = setVec3(patientPosition[1], patientPosition[2], patientPosition[3]);
5626 				sliceMM[nSliceMM] = dotProduct(pos, sliceV);
5627 				if (sliceMM[nSliceMM] < minSliceMM) {
5628 					minSliceMM = sliceMM[nSliceMM];
5629 					for (int k = 0; k < 4; k++)
5630 						minPatientPosition[k] = patientPosition[k];
5631 				}
5632 				if (sliceMM[nSliceMM] > maxSliceMM) {
5633 					maxSliceMM = sliceMM[nSliceMM];
5634 					for (int k = 0; k < 4; k++)
5635 						maxPatientPosition[k] = patientPosition[k];
5636 				}
5637 				nSliceMM++;
5638 			}
5639 			break;
5640 		}
5641 		case kInPlanePhaseEncodingDirection:
5642 			d.phaseEncodingRC = toupper(buffer[lPos]); //first character is either 'R'ow or 'C'ol
5643 			break;
5644 		case kSAR:
5645 			d.SAR = dcmStrFloat(lLength, &buffer[lPos]);
5646 			break;
5647 		case kStudyID:
5648 			dcmStr(lLength, &buffer[lPos], d.studyID);
5649 			break;
5650 		case kSeriesNum:
5651 			d.seriesNum = dcmStrInt(lLength, &buffer[lPos]);
5652 			break;
5653 		case kAcquNum:
5654 			d.acquNum = dcmStrInt(lLength, &buffer[lPos]);
5655 			break;
5656 		case kImageNum:
5657 			//Enhanced Philips also uses this in once per file SQ 0008,1111
5658 			//Enhanced Philips also uses this once per slice in SQ 2005,140f
5659 			if (d.imageNum < 1)
5660 				d.imageNum = dcmStrInt(lLength, &buffer[lPos]); //Philips renames each image again in 2001,9000, which can lead to duplicates
5661 			break;
5662 		case kInStackPositionNumber:
5663 			if ((d.manufacturer != kMANUFACTURER_CANON) && (d.manufacturer != kMANUFACTURER_HITACHI) && (d.manufacturer != kMANUFACTURER_UNKNOWN) && (d.manufacturer != kMANUFACTURER_PHILIPS) && (d.manufacturer != kMANUFACTURER_BRUKER))
5664 				break;
5665 			inStackPositionNumber = dcmInt(4, &buffer[lPos], d.isLittleEndian);
5666 			//if (inStackPositionNumber == 1) numInStackPositionNumber1 ++;
5667 			//printf("<%d>\n",inStackPositionNumber);
5668 			if (inStackPositionNumber > maxInStackPositionNumber)
5669 				maxInStackPositionNumber = inStackPositionNumber;
5670 			break;
5671 		case kTemporalPositionIndex:
5672 			temporalPositionIndex = dcmInt(4, &buffer[lPos], d.isLittleEndian);
5673 			if (temporalPositionIndex > maxTemporalPositionIndex)
5674 				maxTemporalPositionIndex = temporalPositionIndex;
5675 			break;
5676 		case kDimensionIndexPointer:
5677 			dimensionIndexPointer[dimensionIndexPointerCounter++] = dcmAttributeTag(&buffer[lPos], d.isLittleEndian);
5678 			break;
5679 		case kFrameContentSequence:
5680 			//if (!(d.manufacturer == kMANUFACTURER_BRUKER)) break; //see https://github.com/rordenlab/dcm2niix/issues/241
5681 			if (sqDepth == 0)
5682 				sqDepth = 1; //should not happen, in case faulty anonymization
5683 			sqDepth00189114 = sqDepth - 1;
5684 			break;
5685 		case kTriggerDelayTime: { //0x0020+uint32_t(0x9153<< 16 ) //FD
5686 			if (prefs->isIgnoreTriggerTimes)
5687 				break; //issue499
5688 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
5689 				break;
5690 			//if (isVerbose < 2) break;
5691 			double trigger = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
5692 			d.triggerDelayTime = trigger;
5693 			if (isSameFloatGE(d.triggerDelayTime, 0.0))
5694 				d.triggerDelayTime = 0.0; //double to single
5695 			break;
5696 		}
5697 		case kDimensionIndexValues: { // kImageNum is not enough for 4D series from Philips 5.*.
5698 			if (lLength < 4)
5699 				break;
5700 			nDimIndxVal = lLength / 4;
5701 			if (nDimIndxVal > MAX_NUMBER_OF_DIMENSIONS) {
5702 				printError("%d is too many dimensions.  Only up to %d are supported\n", nDimIndxVal, MAX_NUMBER_OF_DIMENSIONS);
5703 				nDimIndxVal = MAX_NUMBER_OF_DIMENSIONS; // Truncate
5704 			}
5705 			dcmMultiLongs(4 * nDimIndxVal, &buffer[lPos], nDimIndxVal, d.dimensionIndexValues, d.isLittleEndian);
5706 			break;
5707 		}
5708 		case kPhotometricInterpretation: {
5709 			char interp[kDICOMStr];
5710 			dcmStr(lLength, &buffer[lPos], interp);
5711 			if (strcmp(interp, "PALETTE_COLOR") == 0)
5712 				isPaletteColor = true;
5713 			//printError("Photometric Interpretation 'PALETTE COLOR' not supported\n");
5714 			break;
5715 		}
5716 		case kPlanarRGB:
5717 			d.isPlanarRGB = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5718 			break;
5719 		case kDim3:
5720 			d.xyzDim[3] = dcmStrInt(lLength, &buffer[lPos]);
5721 			numberOfFrames = d.xyzDim[3];
5722 			break;
5723 		case kSamplesPerPixel:
5724 			d.samplesPerPixel = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5725 			break;
5726 		case kDim2:
5727 			d.xyzDim[2] = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5728 			break;
5729 		case kDim1:
5730 			d.xyzDim[1] = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5731 			break;
5732 		//order is Row,Column e.g. YX
5733 		case kXYSpacing: {
5734 			float yx[3];
5735 			dcmMultiFloat(lLength, (char *)&buffer[lPos], 2, yx);
5736 			d.xyzMM[1] = yx[2];
5737 			d.xyzMM[2] = yx[1];
5738 			break;
5739 		}
5740 		case kImageComments:
5741 			dcmStr(lLength, &buffer[lPos], d.imageComments, true);
5742 			break;
5743 		//group 21: siemens
5744 		//g21
5745 		case kPATModeText: { //e.g. Siemens iPAT x2 listed as "p2"
5746 			char accelStr[kDICOMStr];
5747 			dcmStr(lLength, &buffer[lPos], accelStr);
5748 			char *ptr;
5749 			dcmStrDigitsOnlyKey('p', accelStr); //e.g. if "p2s4" return "2", if "s4" return ""
5750 			d.accelFactPE = (float)strtof(accelStr, &ptr);
5751 			if (*ptr != '\0')
5752 				d.accelFactPE = 0.0;
5753 			//between slice accel
5754 			dcmStr(lLength, &buffer[lPos], accelStr);
5755 			dcmStrDigitsOnlyKey('s', accelStr); //e.g. if "p2s4" return "4", if "p2" return ""
5756 			multiBandFactor = (int)strtol(accelStr, &ptr, 10);
5757 			if (*ptr != '\0')
5758 				multiBandFactor = 0.0;
5759 			//printMessage("p%gs%d\n", d.accelFactPE, multiBandFactor);
5760 			break;
5761 		}
5762 		case kTimeAfterStart:
5763 			//0021,1104 see https://github.com/rordenlab/dcm2niix/issues/303
5764 			// 0021,1104 6@159630 DS 4.635
5765 			// 0021,1104 2@161164 DS 0
5766 			if (d.manufacturer != kMANUFACTURER_SIEMENS)
5767 				break;
5768 			if (acquisitionTimesGE_UIH >= kMaxEPI3D)
5769 				break;
5770 			d.CSA.sliceTiming[acquisitionTimesGE_UIH] = dcmStrFloat(lLength, &buffer[lPos]);
5771 			d.CSA.sliceTiming[acquisitionTimesGE_UIH] *= 1000.0; //convert sec to msec
5772 			//printf("x\t%d\t%g\tkTimeAfterStart\n", acquisitionTimesGE_UIH, d.CSA.sliceTiming[acquisitionTimesGE_UIH]);
5773 			acquisitionTimesGE_UIH++;
5774 			break;
5775 		case kPhaseEncodingDirectionPositiveSiemens: {
5776 			if (d.manufacturer != kMANUFACTURER_SIEMENS)
5777 				break;
5778 			int ph = dcmStrInt(lLength, &buffer[lPos]);
5779 			if (ph == 0)
5780 				d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED;
5781 			if (ph == 1)
5782 				d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED;
5783 			break;
5784 		}
5785 		//case kRealDwellTime : //https://github.com/rordenlab/dcm2niix/issues/240
5786 		//	if (d.manufacturer != kMANUFACTURER_SIEMENS) break;
5787 		//	d.dwellTime = dcmStrInt(lLength, &buffer[lPos]);
5788 		//	break;
5789 		case kBandwidthPerPixelPhaseEncode21:
5790 			if (d.manufacturer != kMANUFACTURER_SIEMENS)
5791 				break;
5792 			d.bandwidthPerPixelPhaseEncode = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
5793 			break;
5794 		case kCoilElements:
5795 			if (d.manufacturer != kMANUFACTURER_SIEMENS)
5796 				break;
5797 			dcmStr(lLength, &buffer[lPos], d.coilElements);
5798 			break;
5799 		//group 21: GE
5800 		case kLocationsInAcquisitionGE:
5801 			locationsInAcquisitionGE = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5802 			break;
5803 		case kRTIA_timer:
5804 			if (d.manufacturer != kMANUFACTURER_GE)
5805 				break;
5806 			//see dicm2nii slice timing from 0021,105E DS RTIA_timer
5807 			d.rtia_timerGE = dcmStrFloat(lLength, &buffer[lPos]); //RefAcqTimes = t/10; end % in ms
5808 			//printf("%s\t%g\n", fname, d.rtia_timerGE);
5809 			break;
5810 		case kProtocolDataBlockGE:
5811 			if (d.manufacturer != kMANUFACTURER_GE)
5812 				break;
5813 			d.protocolBlockLengthGE = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5814 			d.protocolBlockStartGE = (int)lPos + (int)lFileOffset + 4;
5815 			//printError("ProtocolDataBlockGE %d @ %d\n", d.protocolBlockLengthGE, d.protocolBlockStartGE);
5816 			break;
5817 		case kNumberOfExcitations: //FL
5818 			if (d.manufacturer != kMANUFACTURER_GE)
5819 				break;
5820 			d.numberOfExcitations = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
5821 			break;
5822 		case kNumberOfArms: //FL
5823 			if (d.manufacturer != kMANUFACTURER_GE)
5824 				break;
5825 			d.numberOfArms = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
5826 			break;
5827 		case kNumberOfPointsPerArm: //FL
5828 			if (d.manufacturer != kMANUFACTURER_GE)
5829 				break;
5830 			d.numberOfPointsPerArm = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
5831 			break;
5832 		case kDoseCalibrationFactor:
5833 			d.doseCalibrationFactor = dcmStrFloat(lLength, &buffer[lPos]);
5834 			break;
5835 		case kPETImageIndex:
5836 			PETImageIndex = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5837 			break;
5838 		case kPEDirectionDisplayedUIH:
5839 			if (d.manufacturer != kMANUFACTURER_UIH)
5840 				break;
5841 			dcmStr(lLength, &buffer[lPos], d.phaseEncodingDirectionDisplayedUIH);
5842 			break;
5843 		case kDiffusion_bValueUIH: {
5844 			if (d.manufacturer != kMANUFACTURER_UIH)
5845 				break;
5846 			float v[4];
5847 			dcmMultiFloatDouble(lLength, &buffer[lPos], 1, v, d.isLittleEndian);
5848 			B0Philips = v[0];
5849 			set_bVal(&volDiffusion, v[0]);
5850 			break;
5851 		}
5852 		case kParallelInformationUIH: { //SENSE factor (0065,100d) SH [F:2S]
5853 			if (d.manufacturer != kMANUFACTURER_UIH)
5854 				break;
5855 			char accelStr[kDICOMStr];
5856 			dcmStr(lLength, &buffer[lPos], accelStr);
5857 			//char *ptr;
5858 			dcmStrDigitsDotOnlyKey(':', accelStr); //e.g. if "p2s4" return "2", if "s4" return ""
5859 			d.accelFactPE = atof(accelStr);
5860 			break;
5861 		}
5862 		case kNumberOfImagesInGridUIH:
5863 			if (d.manufacturer != kMANUFACTURER_UIH)
5864 				break;
5865 			d.numberOfImagesInGridUIH = dcmStrFloat(lLength, &buffer[lPos]);
5866 			d.CSA.mosaicSlices = d.numberOfImagesInGridUIH;
5867 			break;
5868 		case kPhaseEncodingDirectionPositiveUIH: {
5869 			if (d.manufacturer != kMANUFACTURER_UIH)
5870 				break;
5871 			int ph = dcmStrInt(lLength, &buffer[lPos]);
5872 			if (ph == 1)
5873 				d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED;
5874 			if (ph == 0)
5875 				d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED;
5876 			break;
5877 		}
5878 		case kDiffusionGradientDirectionUIH: {
5879 			if (d.manufacturer != kMANUFACTURER_UIH)
5880 				break;
5881 			float v[4];
5882 			dcmMultiFloatDouble(lLength, &buffer[lPos], 3, v, d.isLittleEndian);
5883 			//dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, v);
5884 			//printf(">>>%g %g %g\n", v[0], v[1], v[2]);
5885 			d.CSA.dtiV[1] = v[0];
5886 			d.CSA.dtiV[2] = v[1];
5887 			d.CSA.dtiV[3] = v[2];
5888 			//vRLPhilips = v[0];
5889 			//vAPPhilips = v[1];
5890 			//vFHPhilips = v[2];
5891 			break;
5892 		}
5893 		case kBitsAllocated:
5894 			d.bitsAllocated = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5895 			break;
5896 		case kBitsStored:
5897 			d.bitsStored = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5898 			break;
5899 		case kIsSigned: //http://dicomiseasy.blogspot.com/2012/08/chapter-12-pixel-data.html
5900 			d.isSigned = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5901 			break;
5902 		case kPixelPaddingValue:
5903 			// According to the DICOM standard, this can be either unsigned (US) or signed (SS). Currently this
5904 			// is used only in nii_saveNII3Dtilt() which only allows DT_INT16, so treat it as signed.
5905 			d.pixelPaddingValue = (float)(short)dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
5906 			break;
5907 		case kFloatPixelPaddingValue:
5908 			d.pixelPaddingValue = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
5909 			break;
5910 		case kTR:
5911 			d.TR = dcmStrFloat(lLength, &buffer[lPos]);
5912 			break;
5913 		case kTE:
5914 			TE = dcmStrFloat(lLength, &buffer[lPos]);
5915 			if (d.TE <= 0.0)
5916 				d.TE = TE;
5917 			break;
5918 		case kNumberOfAverages:
5919 			d.numberOfAverages = dcmStrFloat(lLength, &buffer[lPos]);
5920 			break;
5921 		case kImagingFrequency:
5922 			d.imagingFrequency = dcmStrFloat(lLength, &buffer[lPos]);
5923 			break;
5924 		case kTriggerTime: {
5925 			if (prefs->isIgnoreTriggerTimes)
5926 				break; //issue499
5927 				//untested method to detect slice timing for GE PSD “epi” with multiphase option
5928 				// will not work for current PSD “epiRT” (BrainWave RT, fMRI/DTI package provided by Medical Numerics)
5929 			if ((d.manufacturer != kMANUFACTURER_GE) && (d.manufacturer != kMANUFACTURER_PHILIPS))
5930 				break; //issue384
5931 			d.triggerDelayTime = dcmStrFloat(lLength, &buffer[lPos]); //???? issue 336
5932 			if (d.manufacturer != kMANUFACTURER_GE)
5933 				break;
5934 			d.CSA.sliceTiming[acquisitionTimesGE_UIH] = d.triggerDelayTime;
5935 			//printf("%g\n", d.CSA.sliceTiming[acquisitionTimesGE_UIH]);
5936 			acquisitionTimesGE_UIH++;
5937 			break;
5938 		}
5939 		case kRadionuclideTotalDose:
5940 			d.radionuclideTotalDose = dcmStrFloat(lLength, &buffer[lPos]);
5941 			break;
5942 		case kEffectiveTE: {
5943 			TE = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
5944 			if (d.TE <= 0.0)
5945 				d.TE = TE;
5946 			break;
5947 		}
5948 		case kTI:
5949 			d.TI = dcmStrFloat(lLength, &buffer[lPos]);
5950 			break;
5951 		case kEchoNum:
5952 			d.echoNum = dcmStrInt(lLength, &buffer[lPos]);
5953 			break;
5954 		case kMagneticFieldStrength:
5955 			d.fieldStrength = dcmStrFloat(lLength, &buffer[lPos]);
5956 			break;
5957 		case kZSpacing:
5958 			d.zSpacing = dcmStrFloat(lLength, &buffer[lPos]);
5959 			break;
5960 		case kPhaseEncodingSteps:
5961 			d.phaseEncodingSteps = dcmStrInt(lLength, &buffer[lPos]);
5962 			break;
5963 		case kEchoTrainLength:
5964 			d.echoTrainLength = dcmStrInt(lLength, &buffer[lPos]);
5965 			break;
5966 		case kPercentSampling:
5967 			d.percentSampling = dcmStrFloat(lLength, &buffer[lPos]);
5968 		case kPhaseFieldofView:
5969 			d.phaseFieldofView = dcmStrFloat(lLength, &buffer[lPos]);
5970 			break;
5971 		case kPixelBandwidth:
5972 			d.pixelBandwidth = dcmStrFloat(lLength, &buffer[lPos]);
5973 			//printWarning(" PixelBandwidth (0018,0095)====> %g @%d\n", d.pixelBandwidth, lPos);
5974 			break;
5975 		case kAcquisitionMatrix:
5976 			if (lLength == 8) {
5977 				uint16_t acquisitionMatrix[4];
5978 				dcmMultiShorts(lLength, &buffer[lPos], 4, &acquisitionMatrix[0], d.isLittleEndian); //slice position
5979 				//phaseEncodingLines stored in either image columns or rows
5980 				if (acquisitionMatrix[3] > 0)
5981 					d.phaseEncodingLines = acquisitionMatrix[3];
5982 				if (acquisitionMatrix[2] > 0)
5983 					d.phaseEncodingLines = acquisitionMatrix[2];
5984 				if (acquisitionMatrix[1] > 0)
5985 					frequencyRows = acquisitionMatrix[1];
5986 				if (acquisitionMatrix[0] > 0)
5987 					frequencyRows = acquisitionMatrix[0];
5988 			}
5989 			break;
5990 		case kFlipAngle:
5991 			d.flipAngle = dcmStrFloat(lLength, &buffer[lPos]);
5992 			break;
5993 		case kRadionuclideHalfLife:
5994 			d.radionuclideHalfLife = dcmStrFloat(lLength, &buffer[lPos]);
5995 			break;
5996 		case kRadionuclidePositronFraction:
5997 			d.radionuclidePositronFraction = dcmStrFloat(lLength, &buffer[lPos]);
5998 			break;
5999 		case kGantryTilt:
6000 			d.gantryTilt = dcmStrFloat(lLength, &buffer[lPos]);
6001 			break;
6002 		case kXRayTimeMS: //IS
6003 			d.exposureTimeMs = dcmStrInt(lLength, &buffer[lPos]);;
6004 			break;
6005 		case kXRayTubeCurrent: //IS
6006 			d.xRayTubeCurrent = dcmStrInt(lLength, &buffer[lPos]);;
6007 			break;
6008 		case kXRayExposure: //CTs do not have echo times, we use this field to detect different exposures: https://github.com/neurolabusc/dcm2niix/pull/48
6009 			if (d.TE == 0) { // for CT we will use exposure (0018,1152) whereas for MR we use echo time (0018,0081)
6010 				d.isXRay = true;
6011 				d.TE = dcmStrFloat(lLength, &buffer[lPos]);
6012 			}
6013 			break;
6014 		case kConvolutionKernel: //CS
6015 			dcmStr(lLength, &buffer[lPos], d.convolutionKernel);
6016 			break;
6017 		case kFrameDuration:
6018 			d.frameDuration = dcmStrInt(lLength, &buffer[lPos]);
6019 			break;
6020 		case kReceiveCoilName:
6021 			dcmStr(lLength, &buffer[lPos], d.coilName);
6022 			if (strlen(d.coilName) < 1)
6023 				break;
6024 			d.coilCrc = mz_crc32X((unsigned char *)&d.coilName, strlen(d.coilName));
6025 			break;
6026 		case kSlope:
6027 			d.intenScale = dcmStrFloat(lLength, &buffer[lPos]);
6028 			break;
6029 		//case kSpectroscopyDataPointColumns :
6030 		//	d.xyzDim[4] = dcmInt(4,&buffer[lPos],d.isLittleEndian);
6031 		//	break;
6032 		case kPhilipsSlope:
6033 			if ((lLength == 4) && (d.manufacturer == kMANUFACTURER_PHILIPS))
6034 				d.intenScalePhilips = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6035 			break;
6036 		case kMRImageDynamicScanBeginTime: { //FL
6037 			if (lLength != 4)
6038 				break;
6039 			MRImageDynamicScanBeginTime = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6040 			if (MRImageDynamicScanBeginTime < minDynamicScanBeginTime)
6041 				minDynamicScanBeginTime = MRImageDynamicScanBeginTime;
6042 			if (MRImageDynamicScanBeginTime > maxDynamicScanBeginTime)
6043 				maxDynamicScanBeginTime = MRImageDynamicScanBeginTime;
6044 			break;
6045 		}
6046 		case kIntercept:
6047 			d.intenIntercept = dcmStrFloat(lLength, &buffer[lPos]);
6048 			break;
6049 		case kRadiopharmaceutical:
6050 			dcmStr(lLength, &buffer[lPos], d.radiopharmaceutical);
6051 			break;
6052 		case kZThick:
6053 			d.xyzMM[3] = dcmStrFloat(lLength, &buffer[lPos]);
6054 			d.zThick = d.xyzMM[3];
6055 			break;
6056 		case kAcquisitionMatrixText21:
6057 			//fall through to kAcquisitionMatrixText
6058 		case kAcquisitionMatrixText: {
6059 			if (d.manufacturer == kMANUFACTURER_SIEMENS) {
6060 				char matStr[kDICOMStr];
6061 				dcmStr(lLength, &buffer[lPos], matStr);
6062 				char *pPosition = strchr(matStr, 'I');
6063 				if (pPosition != NULL)
6064 					isInterpolated = true;
6065 			}
6066 			break;
6067 		}
6068 		case kImageOrientationText: //issue522
6069 			if (d.manufacturer != kMANUFACTURER_SIEMENS)
6070 				break;
6071 			dcmStr(lLength, &buffer[lPos], d.imageOrientationText);
6072 			break;
6073 		case kCoilSiemens: {
6074 			if (d.manufacturer == kMANUFACTURER_SIEMENS) {
6075 				//see if image from single coil "H12" or an array "HEA;HEP"
6076 				//char coilStr[kDICOMStr];
6077 				//int coilNum;
6078 				dcmStr(lLength, &buffer[lPos], d.coilName);
6079 				if (strlen(d.coilName) < 1)
6080 					break;
6081 				//printf("-->%s\n", coilStr);
6082 				//d.coilName = coilStr;
6083 				//if (coilStr[0] == 'C') break; //kludge as Nova 32-channel defaults to "C:A32" https://github.com/rordenlab/dcm2niix/issues/187
6084 				//char *ptr;
6085 				//dcmStrDigitsOnly(coilStr);
6086 				//coilNum = (int)strtol(coilStr, &ptr, 10);
6087 				d.coilCrc = mz_crc32X((unsigned char *)&d.coilName, strlen(d.coilName));
6088 				//printf("%d:%s\n", d.coilNum, coilStr);
6089 				//if (*ptr != '\0')
6090 				// d.coilNum = 0;
6091 			}
6092 			break;
6093 		}
6094 		case kImaPATModeText: { //e.g. Siemens iPAT x2 listed as "p2"
6095 			char accelStr[kDICOMStr];
6096 			dcmStr(lLength, &buffer[lPos], accelStr);
6097 			char *ptr;
6098 			dcmStrDigitsOnlyKey('p', accelStr); //e.g. if "p2s4" return "2", if "s4" return ""
6099 			d.accelFactPE = (float)strtof(accelStr, &ptr);
6100 			if (*ptr != '\0')
6101 				d.accelFactPE = 0.0;
6102 			//between slice accel
6103 			dcmStr(lLength, &buffer[lPos], accelStr);
6104 			dcmStrDigitsOnlyKey('s', accelStr); //e.g. if "p2s4" return "4", if "p2" return ""
6105 			multiBandFactor = (int)strtol(accelStr, &ptr, 10);
6106 			if (*ptr != '\0')
6107 				multiBandFactor = 0.0;
6108 			break;
6109 		}
6110 		case kLocationsInAcquisition:
6111 			d.locationsInAcquisition = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
6112 			break;
6113 		case kUnitsPT: //CS
6114 			dcmStr(lLength, &buffer[lPos], d.unitsPT);
6115 			break;
6116 		case kAttenuationCorrectionMethod: //LO
6117 			dcmStr(lLength, &buffer[lPos], d.attenuationCorrectionMethod);
6118 			break;
6119 		case kDecayCorrection: //CS
6120 			dcmStr(lLength, &buffer[lPos], d.decayCorrection);
6121 			break;
6122 		case kReconstructionMethod: //LO
6123 			dcmStr(lLength, &buffer[lPos], d.reconstructionMethod);
6124 			break;
6125 		case kDecayFactor:
6126 			d.decayFactor = dcmStrFloat(lLength, &buffer[lPos]);
6127 			break;
6128 		case kIconImageSequence:
6129 			isIconImageSequence = true;
6130 			if (sqDepthIcon < 0)
6131 				sqDepthIcon = sqDepth;
6132 			break;
6133 		//case kMRSeriesAcquisitionNumber: // 0x2001+(0x107B << 16 ) //IS
6134 		//	mRSeriesAcquisitionNumber = dcmStrInt(lLength, &buffer[lPos]);
6135 		//	break;
6136 		case kNumberOfDynamicScans:
6137 			//~d.numberOfDynamicScans = dcmStrInt(lLength, &buffer[lPos]);
6138 			numberOfDynamicScans = dcmStrInt(lLength, &buffer[lPos]);
6139 			break;
6140 		case kMRAcquisitionType: //detect 3D acquisition: we can reorient these without worrying about slice time correct or BVEC/BVAL orientation
6141 			if (lLength > 1)
6142 				d.is2DAcq = (buffer[lPos] == '2') && (toupper(buffer[lPos + 1]) == 'D');
6143 			if (lLength > 1)
6144 				d.is3DAcq = (buffer[lPos] == '3') && (toupper(buffer[lPos + 1]) == 'D');
6145 			//dcmStr(lLength, &buffer[lPos], d.mrAcquisitionType);
6146 			break;
6147 		case kBodyPartExamined: {
6148 			dcmStr(lLength, &buffer[lPos], d.bodyPartExamined);
6149 			break;
6150 		}
6151 		case kScanningSequence: {
6152 			dcmStr(lLength, &buffer[lPos], d.scanningSequence);
6153 			//According to the DICOM standard 0018,9018 is REQUIRED for EPI raw data
6154 			// http://dicom.nema.org/MEDICAL/Dicom/2015c/output/chtml/part03/sect_C.8.13.4.html
6155 			//In practice, this is not the case for all vendors
6156 			//Fortunately, the combination of 0018,0020 and 0018,9018 appears to reliably detect EPI data
6157 			//Siemens (pre-XA) omits 0018,9018, but reports [EP] for 0018,0020 (regardless of SE/GR)
6158 			//Siemens (XA) reports 0018,9018 but omits 0018,0020
6159 			//Canon/Toshiba omits 0018,9018, but reports [SE\EP];[GR\EP] for 0018,0020
6160 			//GE omits 0018,9018, but reports [EP\GR];[EP\SE] for 0018,0020
6161 			//Philips reports 0018,9018, but reports [SE];[GR] for 0018,0020
6162 			if ((lLength > 1) && (strstr(d.scanningSequence, "IR") != NULL))
6163 				d.isIR = true;
6164 			if ((lLength > 1) && (strstr(d.scanningSequence, "EP") != NULL))
6165 				d.isEPI = true;
6166 			break; //warp
6167 		}
6168 		case kSequenceVariant21:
6169 			if (d.manufacturer != kMANUFACTURER_SIEMENS)
6170 				break; //see GE dataset in dcm_qa_nih
6171 			//fall through...
6172 		case kSequenceVariant: {
6173 			dcmStr(lLength, &buffer[lPos], d.sequenceVariant);
6174 			break;
6175 		}
6176 		case kScanOptions:
6177 			dcmStr(lLength, &buffer[lPos], d.scanOptions);
6178 			if ((lLength > 1) && (strstr(d.scanOptions, "PFF") != NULL))
6179 				d.isPartialFourier = true; //e.g. GE does not populate (0018,9081)
6180 			break;
6181 		case kSequenceName: {
6182 			dcmStr(lLength, &buffer[lPos], d.sequenceName);
6183 			break;
6184 		}
6185 		case kMRfMRIStatusIndicationPhilips: {//fmri volume number
6186 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6187 				break;
6188 			int i = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
6189 			if (i > 0) //only if positive value, see Magdeburg_2014 sample data from dcm_qa_philips Philips MR 51.0
6190 				volumeNumber = i;
6191 			break;
6192 		}
6193 		case kMRAcquisitionTypePhilips: //kMRAcquisitionType
6194 			if (lLength > 1)
6195 				d.is3DAcq = (buffer[lPos] == '3') && (toupper(buffer[lPos + 1]) == 'D');
6196 			break;
6197 		case kAngulationRL:
6198 			d.angulation[1] = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6199 			break;
6200 		case kAngulationAP:
6201 			d.angulation[2] = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6202 			break;
6203 		case kAngulationFH:
6204 			d.angulation[3] = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6205 			break;
6206 		case kMRStackOffcentreRL:
6207 			d.stackOffcentre[1] = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6208 			break;
6209 		case kMRStackOffcentreAP:
6210 			d.stackOffcentre[2] = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6211 			break;
6212 		case kMRStackOffcentreFH:
6213 			d.stackOffcentre[3] = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6214 			break;
6215 		case kSliceOrient: {
6216 			char orientStr[kDICOMStr];
6217 			orientStr[0] = 'X'; //avoid compiler warning: orientStr filled by dcmStr
6218 			dcmStr(lLength, &buffer[lPos], orientStr);
6219 			if (toupper(orientStr[0]) == 'S')
6220 				d.sliceOrient = kSliceOrientSag; //sagittal
6221 			else if (toupper(orientStr[0]) == 'C')
6222 				d.sliceOrient = kSliceOrientCor; //coronal
6223 			else
6224 				d.sliceOrient = kSliceOrientTra; //transverse (axial)
6225 			break;
6226 		}
6227 		case kElscintIcon:
6228 			printWarning("Assuming icon SQ 07a3,10ce.\n");
6229 			isIconImageSequence = true;
6230 			if (sqDepthIcon < 0)
6231 				sqDepthIcon = sqDepth;
6232 			break;
6233 		case kPMSCT_RLE1:
6234 			//https://groups.google.com/forum/#!topic/comp.protocols.dicom/8HuP_aNy9Pc
6235 			//https://discourse.slicer.org/t/fail-to-load-pet-ct-gemini/8158/3
6236 			// d.compressionScheme = kCompressPMSCT_RLE1; //force RLE
6237 			if (d.compressionScheme != kCompressPMSCT_RLE1)
6238 				break;
6239 			d.imageStart = (int)lPos + (int)lFileOffset;
6240 			d.imageBytes = lLength;
6241 			break;
6242 		case kPrivateCreator: {
6243 			if (d.manufacturer != kMANUFACTURER_UNKNOWN)
6244 				break;
6245 			d.manufacturer = dcmStrManufacturer(lLength, &buffer[lPos]);
6246 			volDiffusion.manufacturer = d.manufacturer;
6247 			break;
6248 		}
6249 		case kDiffusion_bValuePhilips:
6250 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6251 				break;
6252 			B0Philips = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6253 			set_bVal(&volDiffusion, B0Philips);
6254 			break;
6255 		case kPhaseNumber: //IS issue529
6256 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6257 				break;
6258 			d.phaseNumber = dcmStrInt(lLength, &buffer[lPos]); //see dcm_qa_philips_asl
6259 			break;
6260 		case kCardiacSync: //CS [TRIGGERED],[NO]
6261 			if (lLength < 2)
6262 				break;
6263 			if (toupper(buffer[lPos]) != 'N')
6264 				isTriggerSynced = true;
6265 			break;
6266 		case kDiffusion_bValue: // 0018,9087
6267 			if (d.manufacturer == kMANUFACTURER_UNKNOWN) {
6268 				d.manufacturer = kMANUFACTURER_PHILIPS;
6269 				printWarning("Found 0018,9087 but manufacturer (0008,0070) unknown: assuming Philips.\n");
6270 			}
6271 			// Note that this is ahead of kImagePositionPatient (0020,0032), so
6272 			// isAtFirstPatientPosition is not necessarily set yet.
6273 			// Philips uses this tag too, at least as of 5.1, but they also
6274 			// use kDiffusionBFactor (see above), and we do not want to
6275 			// double count. More importantly, with Philips this tag
6276 			// (sometimes?) gets repeated in a nested sequence with the
6277 			// value *unset*!
6278 			// GE started using this tag in 27, and annoyingly, NOT including
6279 			// the b value if it is 0 for the slice.
6280 			//if((d.manufacturer != kMANUFACTURER_PHILIPS) || !is2005140FSQ){
6281 			// d.CSA.numDti++;
6282 			// if (d.CSA.numDti == 2) { //First time we know that this is a 4D DTI dataset
6283 			// dti4D->S[0].V[0] = d.CSA.dtiV[0];
6284 			// dti4D->S[0].V[1] = d.CSA.dtiV[1];
6285 			// dti4D->S[0].V[2] = d.CSA.dtiV[2];
6286 			// dti4D->S[0].V[3] = d.CSA.dtiV[3];
6287 			//}
6288 			B0Philips = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6289 			//d.CSA.dtiV[0] = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6290 			set_bVal(&volDiffusion, dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian));
6291 			// if ((d.CSA.numDti > 1) && (d.CSA.numDti < kMaxDTI4D))
6292 			//  dti4D->S[d.CSA.numDti-1].V[0] = d.CSA.dtiV[0];
6293 			//}
6294 			break;
6295 		case kDiffusionOrientation: // 0018, 9089
6296 			// Note that this is ahead of kImagePositionPatient (0020,0032), so
6297 			// isAtFirstPatientPosition is not necessarily set yet.
6298 			// Philips uses this tag too, at least as of 5.1, but they also
6299 			// use kDiffusionDirectionRL, etc., and we do not want to double
6300 			// count. More importantly, with Philips this tag (sometimes?)
6301 			// gets repeated in a nested sequence with the value *unset*!
6302 			// if (((d.manufacturer == kMANUFACTURER_SIEMENS) ||
6303 			//  ((d.manufacturer == kMANUFACTURER_PHILIPS) && !is2005140FSQ)) &&
6304 			//  (isAtFirstPatientPosition || isnan(d.patientPosition[1])))
6305 			//if((d.manufacturer == kMANUFACTURER_SIEMENS) || ((d.manufacturer == kMANUFACTURER_PHILIPS) && !is2005140FSQ))
6306 			if ((d.manufacturer == kMANUFACTURER_TOSHIBA) || (d.manufacturer == kMANUFACTURER_CANON) || (d.manufacturer == kMANUFACTURER_HITACHI) || (d.manufacturer == kMANUFACTURER_SIEMENS) || (d.manufacturer == kMANUFACTURER_PHILIPS)) {
6307 				//for kMANUFACTURER_HITACHI see https://nciphub.org/groups/qindicom/wiki/StandardcompliantenhancedmultiframeDWI
6308 				float v[4];
6309 				//dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, v);
6310 				//dcmMultiFloatDouble(lLength, &buffer[lPos], 3, v, d.isLittleEndian);
6311 				dcmMultiFloatDouble(lLength, &buffer[lPos], 3, v, d.isLittleEndian);
6312 				vRLPhilips = v[0];
6313 				vAPPhilips = v[1];
6314 				vFHPhilips = v[2];
6315 				//printMessage("><>< 0018,9089:\t%g\t%g\t%g\n", v[0], v[1], v[2]);
6316 				//https://github.com/rordenlab/dcm2niix/issues/256
6317 				//d.CSA.dtiV[1] = v[0];
6318 				//d.CSA.dtiV[2] = v[1];
6319 				//d.CSA.dtiV[3] = v[2];
6320 				//printMessage("><>< 0018,9089: DWI bxyz %g %g %g %g\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]);
6321 				hasDwiDirectionality = true;
6322 				d.isBVecWorldCoordinates = true; //e.g. Canon saved image space coordinates in Comments, world space in 0018, 9089
6323 				set_orientation0018_9089(&volDiffusion, lLength, &buffer[lPos], d.isLittleEndian);
6324 			}
6325 			break;
6326 		case kImagingFrequency2:
6327 			d.imagingFrequency = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6328 			break;
6329 		case kParallelReductionFactorOutOfPlane:
6330 			if (d.manufacturer == kMANUFACTURER_SIEMENS)
6331 				break;
6332 			d.accelFactOOP = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6333 			break;
6334 		//case kFrameAcquisitionDuration :
6335 		//	frameAcquisitionDuration = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); //issue369
6336 		//	break;
6337 		case kDiffusionBValueXX: {
6338 			if (!(d.manufacturer == kMANUFACTURER_BRUKER))
6339 				break; //other manufacturers provide bvec directly, rather than bmatrix
6340 			double bMat = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6341 			set_bMatrix(&volDiffusion, bMat, 0);
6342 			break;
6343 		}
6344 		case kDiffusionBValueXY: {
6345 			if (!(d.manufacturer == kMANUFACTURER_BRUKER))
6346 				break; //other manufacturers provide bvec directly, rather than bmatrix
6347 			double bMat = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6348 			set_bMatrix(&volDiffusion, bMat, 1);
6349 			break;
6350 		}
6351 		case kDiffusionBValueXZ: {
6352 			if (!(d.manufacturer == kMANUFACTURER_BRUKER))
6353 				break; //other manufacturers provide bvec directly, rather than bmatrix
6354 			double bMat = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6355 			set_bMatrix(&volDiffusion, bMat, 2);
6356 			break;
6357 		}
6358 		case kDiffusionBValueYY: {
6359 			if (!(d.manufacturer == kMANUFACTURER_BRUKER))
6360 				break; //other manufacturers provide bvec directly, rather than bmatrix
6361 			double bMat = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6362 			set_bMatrix(&volDiffusion, bMat, 3);
6363 			break;
6364 		}
6365 		case kDiffusionBValueYZ: {
6366 			if (!(d.manufacturer == kMANUFACTURER_BRUKER))
6367 				break; //other manufacturers provide bvec directly, rather than bmatrix
6368 			double bMat = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6369 			set_bMatrix(&volDiffusion, bMat, 4);
6370 			break;
6371 		}
6372 		case kDiffusionBValueZZ: {
6373 			if (!(d.manufacturer == kMANUFACTURER_BRUKER))
6374 				break; //other manufacturers provide bvec directly, rather than bmatrix
6375 			double bMat = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6376 			set_bMatrix(&volDiffusion, bMat, 5);
6377 			d.isVectorFromBMatrix = true;
6378 			break;
6379 		}
6380 		case kSliceNumberMrPhilips: {
6381 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6382 				break;
6383 			sliceNumberMrPhilips = dcmStrInt(lLength, &buffer[lPos]);
6384 			int sliceNumber = sliceNumberMrPhilips;
6385 			//use public patientPosition if it exists - fall back to private patient position
6386 			if ((sliceNumber == 1) && (!isnan(patientPosition[1]))) {
6387 				for (int k = 0; k < 4; k++)
6388 					patientPositionStartPhilips[k] = patientPosition[k];
6389 			} else if ((sliceNumber == 1) && (!isnan(patientPositionPrivate[1]))) {
6390 				for (int k = 0; k < 4; k++)
6391 					patientPositionStartPhilips[k] = patientPositionPrivate[k];
6392 			}
6393 			if ((sliceNumber == locationsInAcquisitionPhilips) && (!isnan(patientPosition[1]))) {
6394 				for (int k = 0; k < 4; k++)
6395 					patientPositionEndPhilips[k] = patientPosition[k];
6396 			} else if ((sliceNumber == locationsInAcquisitionPhilips) && (!isnan(patientPositionPrivate[1]))) {
6397 				for (int k = 0; k < 4; k++)
6398 					patientPositionEndPhilips[k] = patientPositionPrivate[k];
6399 			}
6400 			break;
6401 		}
6402 		case kEPIFactorPhilips:
6403 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6404 				break;
6405 			echoTrainLengthPhil = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
6406 			break;
6407 		case kPrepulseDelay: //FL
6408 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6409 				break;
6410 			d.TI = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6411 			break;
6412 		case kPrepulseType: //CS [INV]
6413 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6414 				break;
6415 			if (lLength < 3)
6416 				break;
6417 			if ((toupper(buffer[lPos]) != 'I') && (toupper(buffer[lPos + 1]) != 'N') && (toupper(buffer[lPos + 2]) != 'V'))
6418 				d.isIR = true;
6419 			break;
6420 		case kRespirationSync: //CS [TRIGGERED],[NO]
6421 			if (lLength < 2)
6422 				break;
6423 			if (toupper(buffer[lPos]) != 'N')
6424 				isTriggerSynced = true;
6425 			break;
6426 		case kNumberOfSlicesMrPhilips:
6427 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6428 				break;
6429 			locationsInAcquisitionPhilips = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
6430 			//printMessage("====> locationsInAcquisitionPhilips\t%d\n", locationsInAcquisitionPhilips);
6431 			break;
6432 		case kPartialMatrixScannedPhilips:
6433 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6434 				break;
6435 			if (lLength < 2)
6436 				break;
6437 			if (toupper(buffer[lPos]) == 'Y')
6438 				d.isPartialFourier = true;
6439 			break;
6440 		case kWaterFatShiftPhilips:
6441 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6442 				break;
6443 			d.waterFatShift = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6444 			break;
6445 		case kDiffusionDirectionRL:
6446 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6447 				break;
6448 			vRLPhilips = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6449 			set_diffusion_directionPhilips(&volDiffusion, vRLPhilips, 0);
6450 			break;
6451 		case kDiffusionDirectionAP:
6452 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6453 				break;
6454 			vAPPhilips = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6455 			set_diffusion_directionPhilips(&volDiffusion, vAPPhilips, 1);
6456 			break;
6457 		case kDiffusionDirectionFH:
6458 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6459 				break;
6460 			vFHPhilips = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6461 			set_diffusion_directionPhilips(&volDiffusion, vFHPhilips, 2);
6462 			break;
6463 		case kPrivatePerFrameSq:
6464 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6465 				break;
6466 			//if ((vr[0] == 'S') && (vr[1] == 'Q')) break;
6467 			//if (!is2005140FSQwarned)
6468 			//	printWarning("expected VR of 2005,140F to be 'SQ' (prior DICOM->DICOM conversion error?)\n");
6469 			is2005140FSQ = true;
6470 			//is2005140FSQwarned = true;
6471 			break;
6472 		case kMRImageGradientOrientationNumber :
6473 			if (d.manufacturer == kMANUFACTURER_PHILIPS)
6474 				gradientOrientationNumberPhilips = dcmStrInt(lLength, &buffer[lPos]);
6475 			break;
6476 		case kMRImageLabelType : //CS ??? LBL CTL
6477 			if ((d.manufacturer != kMANUFACTURER_PHILIPS) || (lLength < 2)) break;
6478 			//TODO529: issue529 for ASL LBL/CTL  "LABEL"
6479 			//if (toupper(buffer[lPos]) == 'L') isLabel = true;
6480 			if (toupper(buffer[lPos]) == 'L') d.aslFlags = kASL_FLAG_PHILIPS_LABEL;
6481 			if (toupper(buffer[lPos]) == 'C') d.aslFlags = kASL_FLAG_PHILIPS_CONTROL;
6482 			break;
6483 		case kMRImageDiffBValueNumber:
6484 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6485 				break;
6486 			philMRImageDiffBValueNumber = dcmStrInt(lLength, &buffer[lPos]);
6487 			break;
6488 		case kMRImageDiffVolumeNumber:
6489 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6490 				break;
6491 			philMRImageDiffVolumeNumber = dcmStrInt(lLength, &buffer[lPos]);
6492 			break;
6493 		case kWaveformSq:
6494 			d.imageStart = 1; //abort!!!
6495 			printMessage("Skipping DICOM (audio not image) '%s'\n", fname);
6496 			break;
6497 		case kSpectroscopyData: //kSpectroscopyDataPointColumns
6498 			printMessage("Skipping Spectroscopy DICOM '%s'\n", fname);
6499 			d.imageStart = (int)lPos + (int)lFileOffset;
6500 			break;
6501 		case kCSAImageHeaderInfo:
6502 			if ((lPos + lLength) > fileLen)
6503 				break;
6504 			readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose, d.is3DAcq); //, dti4D);
6505 			if (!d.isHasPhase)
6506 				d.isHasPhase = d.CSA.isPhaseMap;
6507 			break;
6508 		case kCSASeriesHeaderInfo:
6509 			if ((lPos + lLength) > fileLen)
6510 				break;
6511 			d.CSA.SeriesHeader_offset = (int)lPos;
6512 			d.CSA.SeriesHeader_length = lLength;
6513 			break;
6514 		case kRealWorldIntercept:
6515 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6516 				break;
6517 			d.RWVIntercept = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6518 			if (isSameFloat(0.0, d.intenIntercept)) //give precedence to standard value
6519 				d.intenIntercept = d.RWVIntercept;
6520 			break;
6521 		case kRealWorldSlope:
6522 			if (d.manufacturer != kMANUFACTURER_PHILIPS)
6523 				break;
6524 			d.RWVScale = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
6525 			if (d.RWVScale > 1.0E38)
6526 				d.RWVScale = 0.0;
6527 			else if (isSameFloat(1.0, d.intenScale)) //give precedence to standard value
6528 				d.intenScale = d.RWVScale;
6529 			break;
6530 		case kUserDefineDataGE: { //0043,102A
6531 			if ((d.manufacturer != kMANUFACTURER_GE) || (lLength < 128))
6532 				break;
6533 #define MY_DEBUG_GE // <- uncomment this to use following code to infer GE phase encoding direction
6534 #ifdef MY_DEBUG_GE
6535 			int isVerboseX = isVerbose; //for debugging only - in standard release we will enable user defined "isVerbose"
6536 			//int isVerboseX = 2;
6537 			if (isVerboseX > 1)
6538 				printMessage(" UserDefineDataGE file offset/length %ld %u\n", lFileOffset + lPos, lLength);
6539 			if (lLength < 916) { //minimum size is hdr_offset=0, read 0x0394
6540 				printMessage(" GE header too small to be valid  (A)\n");
6541 				break;
6542 			}
6543 			//debug code to export binary data
6544 			/*
6545 				char str[kDICOMStr];
6546 				sprintf(str, "%s_ge.bin",fname);
6547 				FILE *pFile = fopen(str, "wb");
6548 				fwrite(&buffer[lPos], 1, lLength, pFile);
6549 				fclose (pFile);
6550 			*/
6551 			if ((size_t)(lPos + lLength) > MaxBufferSz) {
6552 				//we could re-read the buffer in this case, however in practice GE headers are concise so we never see this issue
6553 				printMessage(" GE header overflows buffer\n");
6554 				break;
6555 			}
6556 			uint16_t hdr_offset = dcmInt(2, &buffer[lPos + 24], true);
6557 			if (isVerboseX > 1)
6558 				printMessage(" header offset: %d\n", hdr_offset);
6559 			if (lLength < (hdr_offset + 916)) { //minimum size is hdr_offset=0, read 0x0394
6560 				printMessage(" GE header too small to be valid  (B)\n");
6561 				break;
6562 			}
6563 			//size_t hdr = lPos+hdr_offset;
6564 			float version = dcmFloat(4, &buffer[lPos + hdr_offset], true);
6565 			if (isVerboseX > 1)
6566 				printMessage(" version %g\n", version);
6567 			if (version < 5.0 || version > 40.0) {
6568 				//printMessage(" GE header file format incorrect %g\n", version);
6569 				break;
6570 			}
6571 			//char const *hdr = &buffer[lPos + hdr_offset];
6572 			char *hdr = (char *)&buffer[lPos + hdr_offset];
6573 			int epi_chk_off = 0x003a;
6574 			int pepolar_off = 0x0030;
6575 			int kydir_off = 0x0394;
6576 			if (version >= 25.002) {
6577 				hdr += 0x004c;
6578 				kydir_off -= 0x008c;
6579 			}
6580 			//int seqOrInter =dcmInt(2,(unsigned char*)(hdr + pepolar_off-638),true);
6581 			//int seqOrInter2 =dcmInt(2,(unsigned char*)(hdr + kydir_off-638),true);
6582 			//printf("%d %d<<<\n", seqOrInter,seqOrInter2);
6583 			//check if EPI
6584 			if (true) {
6585 				//int check = *(short const *)(hdr + epi_chk_off) & 0x800;
6586 				int check = dcmInt(2, (unsigned char *)hdr + epi_chk_off, true) & 0x800;
6587 				if (check == 0) {
6588 					if (isVerboseX > 1)
6589 						printMessage("%s: Warning: Data is not EPI\n", fname);
6590 					break;
6591 				}
6592 			}
6593 			//Check for PE polarity
6594 			// int flag1 = *(short const *)(hdr + pepolar_off) & 0x0004;
6595 			//Check for ky direction (view order)
6596 			// int flag2 = *(int const *)(hdr + kydir_off);
6597 			int phasePolarityFlag = dcmInt(2, (unsigned char *)hdr + pepolar_off, true) & 0x0004;
6598 			//Check for ky direction (view order)
6599 			int sliceOrderFlag = dcmInt(2, (unsigned char *)hdr + kydir_off, true);
6600 			if (isVerboseX > 1)
6601 				printMessage(" GE phasePolarity/sliceOrder flags %d %d\n", phasePolarityFlag, sliceOrderFlag);
6602 			if (phasePolarityFlag == kGE_PHASE_ENCODING_POLARITY_FLIPPED)
6603 				d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED;
6604 			if (phasePolarityFlag == kGE_PHASE_ENCODING_POLARITY_UNFLIPPED)
6605 				d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED;
6606 			if (sliceOrderFlag == kGE_SLICE_ORDER_BOTTOM_UP) {
6607 				//https://cfmriweb.ucsd.edu/Howto/3T/operatingtips.html
6608 				if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_UNFLIPPED)
6609 					d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED;
6610 				else
6611 					d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED;
6612 			}
6613 //if (sliceOrderFlag == kGE_SLICE_ORDER_TOP_DOWN)
6614 //	d.sliceOrderGE = kGE_SLICE_ORDER_TOP_DOWN;
6615 //if (sliceOrderFlag == kGE_SLICE_ORDER_BOTTOM_UP)
6616 //	d.sliceOrderGE = kGE_SLICE_ORDER_BOTTOM_UP;
6617 #endif
6618 			break;
6619 		}
6620 		case kEffectiveEchoSpacingGE:
6621 			if (d.manufacturer == kMANUFACTURER_GE)
6622 				d.effectiveEchoSpacingGE = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
6623 			break;
6624 		case kImageTypeGE: { //0/1/2/3 for magnitude/phase/real/imaginary
6625 			if (d.manufacturer != kMANUFACTURER_GE)
6626 				break;
6627 			int dt = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
6628 			if (dt == 0)
6629 				d.isHasMagnitude = true;
6630 			if (dt == 1)
6631 				d.isHasPhase = true;
6632 			if (dt == 2)
6633 				d.isHasReal = true;
6634 			if (dt == 3)
6635 				d.isHasImaginary = true;
6636 			break;
6637 		}
6638 		case kDiffusion_bValueGE:
6639 			if (d.manufacturer == kMANUFACTURER_GE) {
6640 				d.CSA.dtiV[0] = (float)set_bValGE(&volDiffusion, lLength, &buffer[lPos]);
6641 				d.CSA.numDti = 1;
6642 			}
6643 			break;
6644 		case kEpiRTGroupDelayGE: //FL
6645 			if (d.manufacturer != kMANUFACTURER_GE)
6646 				break;
6647 			d.groupDelay = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian);
6648 			d.groupDelay *= 1000.0; //sec -> ms
6649 			// If kEpiRTGroupDelayGE (0043,107C) exists, epiRT
6650 			d.epiVersionGE = 1; //-1 = not epi, 0 = epi, 1 = epiRT
6651 			break;
6652 		case kAssetRFactorsGE: { //DS issue427GE
6653 			if (d.manufacturer != kMANUFACTURER_GE)
6654 				break;
6655 			float PhaseSlice[3];
6656 			dcmMultiFloat(lLength, (char *)&buffer[lPos], 2, PhaseSlice);
6657 			if (PhaseSlice[1] > 0.0)
6658 				d.accelFactPE = 1.0f / PhaseSlice[1];
6659 			if (PhaseSlice[2] > 0.0)
6660 				d.accelFactOOP = 1.0f / PhaseSlice[2];
6661 			break;
6662 		}
6663 		case kASLContrastTechniqueGE: { //CS
6664 			if (d.manufacturer != kMANUFACTURER_GE)
6665 				break;
6666 			char st[kDICOMStr];
6667 			//aslFlags
6668 			dcmStr(lLength, &buffer[lPos], st);
6669 			if (strstr(st, "PSEUDOCONTINUOUS") != NULL)
6670 				d.aslFlags = (d.aslFlags | kASL_FLAG_GE_PSEUDOCONTINUOUS);
6671 			else if (strstr(st, "CONTINUOUS") != NULL)
6672 				d.aslFlags = (d.aslFlags | kASL_FLAG_GE_CONTINUOUS);
6673 			break;
6674 		}
6675 		case kASLLabelingTechniqueGE: { //LO issue427GE
6676 			if (d.manufacturer != kMANUFACTURER_GE)
6677 				break;
6678 			char st[kDICOMStr];
6679 			dcmStr(lLength, &buffer[lPos], st);
6680 			if (strstr(st, "3D continuous") != NULL)
6681 				d.aslFlags = (d.aslFlags | kASL_FLAG_GE_3DCASL);
6682 			if (strstr(st, "3D pulsed continuous") != NULL)
6683 				d.aslFlags = (d.aslFlags | kASL_FLAG_GE_3DPCASL);
6684 			break;
6685 		}
6686 		case kDurationLabelPulseGE: { //IS
6687 			if (d.manufacturer != kMANUFACTURER_GE)
6688 				break;
6689 			d.durationLabelPulseGE = dcmStrInt(lLength, &buffer[lPos]);
6690 			break;
6691 		}
6692 		case kMultiBandGE: { //LO issue427GE
6693 			if (d.manufacturer != kMANUFACTURER_GE)
6694 				break;
6695 			//LO array: Value 1 = Multiband factor, Value 2 = Slice FOV Shift Factor, Value 3 = Calibration method
6696 			int mb = dcmStrInt(lLength, &buffer[lPos]);
6697 			if (mb > 1)
6698 				d.CSA.multiBandFactor = mb;
6699 			break;
6700 		}
6701 		case kGeiisFlag:
6702 			if ((lLength > 4) && (buffer[lPos] == 'G') && (buffer[lPos + 1] == 'E') && (buffer[lPos + 2] == 'I') && (buffer[lPos + 3] == 'I')) {
6703 				//read a few digits, as bug is specific to GEIIS, while GEMS are fine
6704 				printWarning("GEIIS violates the DICOM standard. Inspect results and admonish your vendor.\n");
6705 				isIconImageSequence = true;
6706 				if (sqDepthIcon < 0)
6707 					sqDepthIcon = sqDepth;
6708 				//geiisBug = true; //compressed thumbnails do not follow transfer syntax! GE should not re-use pulbic tags for these proprietary images http://sonca.kasshin.net/gdcm/Doc/GE_ImageThumbnails
6709 			}
6710 			break;
6711 		case kStudyComments: {
6712 			//char commentStr[kDICOMStr];
6713 			//dcmStr(lLength, &buffer[lPos], commentStr);
6714 			//printf(">> %s\n", commentStr);
6715 			break;
6716 		}
6717 		case kProcedureStepDescription:
6718 			dcmStr(lLength, &buffer[lPos], d.procedureStepDescription);
6719 			break;
6720 		case kOrientationACR: //use in emergency if kOrientation is not present!
6721 			if (!isOrient)
6722 				dcmMultiFloat(lLength, (char *)&buffer[lPos], 6, d.orient);
6723 			break;
6724 		case kOrientation: {
6725 			if (isOrient) { //already read orient - read for this slice to see if it varies (localizer)
6726 				float orient[7];
6727 				dcmMultiFloat(lLength, (char *)&buffer[lPos], 6, orient);
6728 				if ((!isSameFloatGE(d.orient[1], orient[1]) || !isSameFloatGE(d.orient[2], orient[2]) || !isSameFloatGE(d.orient[3], orient[3]) ||
6729 					!isSameFloatGE(d.orient[4], orient[4]) || !isSameFloatGE(d.orient[5], orient[5]) || !isSameFloatGE(d.orient[6], orient[6]))) {
6730 					if (!d.isLocalizer)
6731 						printMessage("slice orientation varies (localizer?) [%g %g %g %g %g %g] != [%g %g %g %g %g %g]\n",
6732 							d.orient[1], d.orient[2], d.orient[3], d.orient[4], d.orient[5], d.orient[6],
6733 							orient[1], orient[2], orient[3], orient[4], orient[5], orient[6]);
6734 					d.isLocalizer = true;
6735 				}
6736 			}
6737 			dcmMultiFloat(lLength, (char *)&buffer[lPos], 6, d.orient);
6738 			vec3 readV = setVec3(d.orient[1], d.orient[2], d.orient[3]);
6739 			vec3 phaseV = setVec3(d.orient[4], d.orient[5], d.orient[6]);
6740 			sliceV = crossProduct(readV, phaseV);
6741 			//printf("sliceV %g %g %g\n", sliceV.v[0], sliceV.v[1], sliceV.v[2]);
6742 			isOrient = true;
6743 			break;
6744 		}
6745 		case kTemporalPosition: //fall through, both kSliceNumberMrPhilips (2001,100A) and kTemporalPosition are is
6746 			volumeNumber = dcmStrInt(lLength, &buffer[lPos]);
6747 			//temporalPositionIdentifier = volumeNumber;
6748 			break;
6749 		case kTemporalResolution:
6750 			temporalResolutionMS = dcmStrFloat(lLength, &buffer[lPos]);
6751 			break;
6752 		case kImagesInAcquisition:
6753 			imagesInAcquisition = dcmStrInt(lLength, &buffer[lPos]);
6754 			break;
6755 		//case kSliceLocation : //optional so useless, infer from image position patient (0020,0032) and image orientation (0020,0037)
6756 		//	sliceLocation = dcmStrFloat(lLength, &buffer[lPos]);
6757 		//	break;
6758 		case kImageStart:
6759 			//if ((!geiisBug) && (!isIconImageSequence)) //do not exit for proprietary thumbnails
6760 			if (isIconImageSequence) {
6761 				//20200116 see example from Tashrif Bilah that saves GEIIS thumbnails uncompressed
6762 				// therefore, the next couple lines are not a perfect detection for GEIIS thumbnail icons
6763 				//int imgBytes = (d.xyzDim[1] * d.xyzDim[2] * int(d.bitsAllocated / 8));
6764 				//if (imgBytes == lLength)
6765 				//	isIconImageSequence = false;
6766 				if ((isIconImageSequence) && (sqDepth < 1))
6767 					printWarning("Assuming 7FE0,0010 refers to an icon not the main image\n");
6768 			}
6769 			if ((d.compressionScheme == kCompressNone) && (!isIconImageSequence)) //do not exit for proprietary thumbnails
6770 				d.imageStart = (int)lPos + (int)lFileOffset;
6771 			//geiisBug = false;
6772 			//http://www.dclunie.com/medical-image-faq/html/part6.html
6773 			//unlike raw data, Encapsulated data is stored as Fragments contained in Items that are the Value field of Pixel Data
6774 			if ((d.compressionScheme != kCompressNone) && (!isIconImageSequence)) {
6775 				isEncapsulatedData = true;
6776 				encapsulatedDataImageStart = (int)lPos + (int)lFileOffset;
6777 				lLength = 0;
6778 			}
6779 			isIconImageSequence = false;
6780 			break;
6781 		case kImageStartFloat:
6782 			d.isFloat = true;
6783 			if (!isIconImageSequence) //do not exit for proprietary thumbnails
6784 				d.imageStart = (int)lPos + (int)lFileOffset;
6785 			isIconImageSequence = false;
6786 			break;
6787 		case kImageStartDouble:
6788 			printWarning("Double-precision DICOM conversion untested: please provide samples to developer\n");
6789 			d.isFloat = true;
6790 			if (!isIconImageSequence) //do not exit for proprietary thumbnails
6791 				d.imageStart = (int)lPos + (int)lFileOffset;
6792 			isIconImageSequence = false;
6793 			break;
6794 		} //switch/case for groupElement
6795 		if ((((groupElement >> 8) & 0xFF) == 0x60) && (groupElement % 2 == 0) && ((groupElement & 0xFF) < 0x1E)) { //Group 60xx: OverlayGroup http://dicom.nema.org/dicom/2013/output/chtml/part03/sect_C.9.html
6796 			//even group numbers 0x6000..0x601E
6797 			int overlayN = ((groupElement & 0xFF) >> 1);
6798 			//printf("%08x %d %d\n", groupElement, (groupElement & 0xFF), overlayN);
6799 			int element = groupElement >> 16;
6800 			switch (element) {
6801 			case 0x0010: //US OverlayRows
6802 				overlayRows = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
6803 				break;
6804 			case 0x0011: //US OverlayColumns
6805 				overlayCols = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
6806 				break;
6807 			case 0x0050: { //SSx2! OverlayOrigin
6808 				if (lLength != 4)
6809 					break;
6810 				int row = dcmInt(2, &buffer[lPos], d.isLittleEndian);
6811 				int col = dcmInt(2, &buffer[lPos + 2], d.isLittleEndian);
6812 				if ((row == 1) && (col == 1))
6813 					break;
6814 				printMessage("Unsupported overlay origin %d/%d\n", row, col);
6815 				overlayOK = false;
6816 				break;
6817 			}
6818 			case 0x0100: { //US OverlayBitsAllocated
6819 				int bits = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
6820 				if (bits == 1)
6821 					break;
6822 				//old style Burned-In
6823 				printMessage("Illegal/Obsolete DICOM: Overlay Bits Allocated must be 1, not %d\n", bits);
6824 				overlayOK = false;
6825 				break;
6826 			}
6827 			case 0x0102: { //US OverlayBitPosition
6828 				int pos = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
6829 				if (pos == 0)
6830 					break;
6831 				//old style Burned-In
6832 				printMessage("Illegal/Obsolete DICOM: Overlay Bit Position shall be 0, not %d\n", pos);
6833 				overlayOK = false;
6834 				break;
6835 			}
6836 			case 0x3000: {
6837 				d.overlayStart[overlayN] = (int)lPos + (int)lFileOffset;
6838 				d.isHasOverlay = true;
6839 				break;
6840 			}
6841 			}
6842 		} //Group 60xx even values 0x6000..0x601E https://www.medicalconnections.co.uk/kb/Number-Of-Overlays-In-Image/
6843 #ifndef USING_R
6844 		if (isVerbose > 1) {
6845 			//dcm2niix i fast because it does not use a dictionary.
6846 			// this is a very incomplete DICOM header report, and not a substitute for tools like dcmdump
6847 			// the purpose is to see how dcm2niix has parsed the image for diagnostics
6848 			// this section will report very little for implicit data
6849 			//if (d.isHasReal) printf("r");else printf("m");
6850 			char str[kDICOMStr];
6851 			sprintf(str, "%*c%04x,%04x %u@%ld ", sqDepth + 1, ' ', groupElement & 65535, groupElement >> 16, lLength, lFileOffset + lPos);
6852 			bool isStr = false;
6853 			if (d.isExplicitVR) {
6854 				sprintf(str, "%s%c%c ", str, vr[0], vr[1]);
6855 				if ((vr[0] == 'F') && (vr[1] == 'D'))
6856 					sprintf(str, "%s%g ", str, dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian));
6857 				if ((vr[0] == 'F') && (vr[1] == 'L'))
6858 					sprintf(str, "%s%g ", str, dcmFloat(lLength, &buffer[lPos], d.isLittleEndian));
6859 				if ((vr[0] == 'S') && (vr[1] == 'S'))
6860 					sprintf(str, "%s%d ", str, dcmInt(lLength, &buffer[lPos], d.isLittleEndian));
6861 				if ((vr[0] == 'S') && (vr[1] == 'L'))
6862 					sprintf(str, "%s%d ", str, dcmInt(lLength, &buffer[lPos], d.isLittleEndian));
6863 				if ((vr[0] == 'U') && (vr[1] == 'S'))
6864 					sprintf(str, "%s%d ", str, dcmInt(lLength, &buffer[lPos], d.isLittleEndian));
6865 				if ((vr[0] == 'U') && (vr[1] == 'L'))
6866 					sprintf(str, "%s%d ", str, dcmInt(lLength, &buffer[lPos], d.isLittleEndian));
6867 				if ((vr[0] == 'A') && (vr[1] == 'E'))
6868 					isStr = true;
6869 				if ((vr[0] == 'A') && (vr[1] == 'S'))
6870 					isStr = true;
6871 				if ((vr[0] == 'C') && (vr[1] == 'S'))
6872 					isStr = true;
6873 				if ((vr[0] == 'D') && (vr[1] == 'A'))
6874 					isStr = true;
6875 				if ((vr[0] == 'D') && (vr[1] == 'S'))
6876 					isStr = true;
6877 				if ((vr[0] == 'D') && (vr[1] == 'T'))
6878 					isStr = true;
6879 				if ((vr[0] == 'I') && (vr[1] == 'S'))
6880 					isStr = true;
6881 				if ((vr[0] == 'L') && (vr[1] == 'O'))
6882 					isStr = true;
6883 				if ((vr[0] == 'L') && (vr[1] == 'T'))
6884 					isStr = true;
6885 				//if ((vr[0]=='O') && (vr[1]=='B')) isStr = xxx;
6886 				//if ((vr[0]=='O') && (vr[1]=='D')) isStr = xxx;
6887 				//if ((vr[0]=='O') && (vr[1]=='F')) isStr = xxx;
6888 				//if ((vr[0]=='O') && (vr[1]=='W')) isStr = xxx;
6889 				if ((vr[0] == 'P') && (vr[1] == 'N'))
6890 					isStr = true;
6891 				if ((vr[0] == 'S') && (vr[1] == 'H'))
6892 					isStr = true;
6893 				if ((vr[0] == 'S') && (vr[1] == 'T'))
6894 					isStr = true;
6895 				if ((vr[0] == 'T') && (vr[1] == 'M'))
6896 					isStr = true;
6897 				if ((vr[0] == 'U') && (vr[1] == 'I'))
6898 					isStr = true;
6899 				if ((vr[0] == 'U') && (vr[1] == 'T'))
6900 					isStr = true;
6901 			} else
6902 				isStr = (lLength > 12); //implicit encoding: not always true as binary vectors may exceed 12 bytes, but often true
6903 			if (lLength > 128) {
6904 				sprintf(str, "%s<%d bytes> ", str, lLength);
6905 				printMessage("%s\n", str);
6906 			} else if (isStr) { //if length is greater than 8 bytes (+4 hdr) the MIGHT be a string
6907 				char tagStr[kDICOMStr];
6908 				//tagStr[0] = 'X'; //avoid compiler warning: orientStr filled by dcmStr
6909 				strcpy(tagStr, "");
6910 				if (lLength > 0)
6911 					dcmStr(lLength, &buffer[lPos], tagStr);
6912 				if (strlen(tagStr) > 1) {
6913 					for (size_t pos = 0; pos < strlen(tagStr); pos++)
6914 						if ((tagStr[pos] == '<') || (tagStr[pos] == '>') || (tagStr[pos] == ':') || (tagStr[pos] == '"') || (tagStr[pos] == '\\') || (tagStr[pos] == '/') || (tagStr[pos] < 32) //issue398
6915 							//|| (tagStr[pos] == '^') || (tagStr[pos] < 33)
6916 							|| (tagStr[pos] == '*') || (tagStr[pos] == '|') || (tagStr[pos] == '?'))
6917 							tagStr[pos] = '_';
6918 				}
6919 				printMessage("%s %s\n", str, tagStr);
6920 			} else
6921 				printMessage("%s\n", str);
6922 			//if (d.isExplicitVR) printMessage(" VR=%c%c\n", vr[0], vr[1]);
6923 		} //printMessage(" tag=%04x,%04x length=%u pos=%ld %c%c nest=%d\n", groupElement & 65535,groupElement>>16, lLength, lPos,vr[0], vr[1], nest);
6924 #endif
6925 		lPos = lPos + (lLength);
6926 	} //while d.imageStart == 0
6927 	free(buffer);
6928 	if (d.bitsStored < 0)
6929 		d.isValid = false;
6930 	if (d.bitsStored == 1)
6931 		printWarning("1-bit binary DICOMs not supported\n"); //maybe not valid - no examples to test
6932 	//printf("%d bval=%g bvec=%g %g %g<<<\n", d.CSA.numDti, d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]);
6933 	//printMessage("><>< DWI bxyz %g %g %g %g\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]);
6934 	if (encapsulatedDataFragmentStart > 0) {
6935 		if ((encapsulatedDataFragments > 1) && (encapsulatedDataFragments == numberOfFrames) && (encapsulatedDataFragments < kMaxDTI4D)) {
6936 			printWarning("Compressed image stored as %d fragments: if conversion fails decompress with gdcmconv, Osirix, dcmdjpeg or dcmjp2k %s\n", encapsulatedDataFragments, fname);
6937 			d.imageStart = encapsulatedDataFragmentStart;
6938 		} else if (encapsulatedDataFragments > 1) {
6939 			printError("Compressed image stored as %d fragments: decompress with gdcmconv, Osirix, dcmdjpeg or dcmjp2k %s\n", encapsulatedDataFragments, fname);
6940 		} else {
6941 			d.imageStart = encapsulatedDataFragmentStart;
6942 			//dti4D->fragmentOffset[0] = -1;
6943 		}
6944 	} else if ((isEncapsulatedData) && (d.imageStart < 128)) {
6945 		//http://www.dclunie.com/medical-image-faq/html/part6.html
6946 		//Uncompressed data (unencapsulated) is sent in DICOM as a series of raw bytes or words (little or big endian) in the Value field of the Pixel Data element (7FE0,0010). Encapsulated data on the other hand is sent not as raw bytes or words but as Fragments contained in Items that are the Value field of Pixel Data
6947 		printWarning("DICOM violation (contact vendor): compressed image without image fragments, assuming image offset defined by 0x7FE0,x0010: %s\n", fname);
6948 		d.imageStart = encapsulatedDataImageStart;
6949 	}
6950 	if ((d.manufacturer == kMANUFACTURER_GE) && (d.groupDelay > 0.0))
6951 		d.TR += d.groupDelay; //Strangely, for GE the sample rate is (0018,0080) + ((0043,107c) * 1000.0)
6952 	if ((d.modality == kMODALITY_PT) && (PETImageIndex > 0)) {
6953 		d.imageNum = PETImageIndex; //https://github.com/rordenlab/dcm2niix/issues/184
6954 									//printWarning("PET scan using 0054,1330 for image number %d\n", PETImageIndex);
6955 	}
6956 	if (d.isHasOverlay) {
6957 		if ((overlayCols > 0) && (d.xyzDim[1] != overlayCols))
6958 			overlayOK = false;
6959 		if ((overlayRows > 0) && (d.xyzDim[2] != overlayRows))
6960 			overlayOK = false;
6961 		if (!overlayOK)
6962 			d.isHasOverlay = false;
6963 	}
6964 //Recent Philips images include DateTime (0008,002A) but not separate date and time (0008,0022 and 0008,0032)
6965 #define kYYYYMMDDlen 8 //how many characters to encode year,month,day in "YYYYDDMM" format
6966 	if ((strlen(acquisitionDateTimeTxt) > (kYYYYMMDDlen + 5)) && (!isFloatDiff(d.acquisitionTime, 0.0f)) && (!isFloatDiff(d.acquisitionDate, 0.0f))) {
6967 		// 20161117131643.80000 -> date 20161117 time 131643.80000
6968 		//printMessage("acquisitionDateTime %s\n",acquisitionDateTimeTxt);
6969 		char acquisitionDateTxt[kDICOMStr];
6970 		memcpy(acquisitionDateTxt, acquisitionDateTimeTxt, kYYYYMMDDlen);
6971 		acquisitionDateTxt[kYYYYMMDDlen] = '\0'; // IMPORTANT!
6972 		d.acquisitionDate = atof(acquisitionDateTxt);
6973 		char acquisitionTimeTxt[kDICOMStr];
6974 		int timeLen = (int)strlen(acquisitionDateTimeTxt) - kYYYYMMDDlen;
6975 		strncpy(acquisitionTimeTxt, &acquisitionDateTimeTxt[kYYYYMMDDlen], timeLen);
6976 		acquisitionTimeTxt[timeLen] = '\0'; // IMPORTANT!
6977 		d.acquisitionTime = atof(acquisitionTimeTxt);
6978 	}
6979 	d.dateTime = (atof(d.studyDate) * 1000000) + atof(d.studyTime);
6980 	//printMessage("slices in Acq %d %d %g %g\n",locationsInAcquisitionGE, d.locationsInAcquisition, d.xyzMM[3], d.zSpacing);
6981 	if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.locationsInAcquisition == 0))
6982 		d.locationsInAcquisition = locationsInAcquisitionPhilips;
6983 	if ((d.manufacturer == kMANUFACTURER_GE) && (imagesInAcquisition > 0))
6984 		d.locationsInAcquisition = imagesInAcquisition; //e.g. if 72 slices acquired but interpolated as 144
6985 	if ((d.manufacturer == kMANUFACTURER_GE) && (d.locationsInAcquisition > 0) && (locationsInAcquisitionGE > 0) && (d.locationsInAcquisition != locationsInAcquisitionGE)) {
6986 		if (isVerbose)
6987 			printMessage("Check number of slices, discrepancy between tags (0020,1002; 0021,104F; 0054,0081) (%d vs %d) %s\n", locationsInAcquisitionGE, d.locationsInAcquisition, fname);
6988 		/* SAH.start: Fix for ZIP2 */
6989 		int zipFactor = (int)roundf(d.xyzMM[3] / d.zSpacing);
6990 		if (zipFactor > 1) {
6991 			d.interp3D = zipFactor;
6992 			//printMessage("Issue 373: Check for ZIP2 Factor: %d SliceThickness+SliceGap: %f, SpacingBetweenSlices: %f \n", zipFactor, d.xyzMM[3], d.zSpacing);
6993 			locationsInAcquisitionGE *= zipFactor; // Multiply number of slices by ZIP factor. Do this prior to checking for conflict below (?).
6994 		}
6995 		if (isGEfieldMap) { //issue501 : to do check zip factor
6996 			//Volume 1) derived phase field map [Hz] and 2) magnitude volume.
6997 			d.isDerived = (d.imageNum <= locationsInAcquisitionGE); //first volume
6998 			d.isRealIsPhaseMapHz = d.isDerived;
6999 			d.isHasReal = d.isDerived;
7000 		}
7001 		/* SAH.end */
7002 		if (locationsInAcquisitionGE < d.locationsInAcquisition) {
7003 			d.locationsInAcquisitionConflict = d.locationsInAcquisition;
7004 			d.locationsInAcquisition = locationsInAcquisitionGE;
7005 		} else
7006 			d.locationsInAcquisitionConflict = locationsInAcquisitionGE;
7007 	}
7008 	if ((d.manufacturer == kMANUFACTURER_GE) && (d.locationsInAcquisition == 0))
7009 		d.locationsInAcquisition = locationsInAcquisitionGE;
7010 	if (d.zSpacing > 0.0)
7011 		d.xyzMM[3] = d.zSpacing; //use zSpacing if provided: depending on vendor, kZThick may or may not include a slice gap
7012 	//printMessage("patientPositions = %d XYZT = %d slicePerVol = %d numberOfDynamicScans %d\n",patientPositionNum,d.xyzDim[3], d.locationsInAcquisition, d.numberOfDynamicScans);
7013 	if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (patientPositionNum > d.xyzDim[3])) {
7014 		d.CSA.numDti = d.xyzDim[3];																																			//issue506
7015 		printMessage("Please check slice thicknesses: Philips R3.2.2 bug can disrupt estimation (%d positions reported for %d slices)\n", patientPositionNum, d.xyzDim[3]); //Philips reported different positions for each slice!
7016 	}
7017 	if ((d.imageStart > 144) && (d.xyzDim[1] > 1) && (d.xyzDim[2] > 1))
7018 		d.isValid = true;
7019 	//if ((d.imageStart > 144) && (d.xyzDim[1] >= 1) && (d.xyzDim[2] >= 1) && (d.xyzDim[4] > 1)) //Spectroscopy
7020 	//	d.isValid = true;
7021 	if ((d.xyzMM[1] > FLT_EPSILON) && (d.xyzMM[2] < FLT_EPSILON)) {
7022 		printMessage("Please check voxel size\n");
7023 		d.xyzMM[2] = d.xyzMM[1];
7024 	}
7025 	if ((d.xyzMM[2] > FLT_EPSILON) && (d.xyzMM[1] < FLT_EPSILON)) {
7026 		printMessage("Please check voxel size\n");
7027 		d.xyzMM[1] = d.xyzMM[2];
7028 	}
7029 	if ((d.xyzMM[3] < FLT_EPSILON)) {
7030 		printMessage("Unable to determine slice thickness: please check voxel size\n");
7031 		d.xyzMM[3] = 1.0;
7032 	}
7033 	//printMessage("Patient Position\t%g\t%g\t%g\tThick\t%g\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3]);
7034 	//printMessage("Patient Position\t%g\t%g\t%g\tThick\t%g\tStart\t%d\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3], d.imageStart);
7035 	// printMessage("ser %ld\n", d.seriesNum);
7036 	//int kEchoMult = 100; //For Siemens/GE Series 1,2,3... save 2nd echo as 201, 3rd as 301, etc
7037 	//if (d.seriesNum > 100)
7038 	// kEchoMult = 10; //For Philips data Saved as Series 101,201,301... save 2nd echo as 111, 3rd as 121, etc
7039 	//if (coilNum > 0) //segment images with multiple coils
7040 	// d.seriesNum = d.seriesNum + (100*coilNum);
7041 	//if (d.echoNum > 1) //segment images with multiple echoes
7042 	// d.seriesNum = d.seriesNum + (kEchoMult*d.echoNum);
7043 	if (isPaletteColor) {
7044 		d.isValid = false;
7045 		d.isDerived = true; //to my knowledge, palette images always derived
7046 		printWarning("Photometric Interpretation 'PALETTE COLOR' not supported\n");
7047 	}
7048 	if ((d.compressionScheme == kCompress50) && (d.bitsAllocated > 8)) {
7049 		//dcmcjpg with +ee can create .51 syntax images that are 8,12,16,24-bit: we can only decode 8/24-bit
7050 		printError("Unable to decode %d-bit images with Transfer Syntax 1.2.840.10008.1.2.4.51, decompress with dcmdjpg or gdcmconv\n", d.bitsAllocated);
7051 		d.isValid = false;
7052 	}
7053 	if ((isMosaic) && (d.CSA.mosaicSlices < 1) && (numberOfImagesInMosaic < 1) && (!isInterpolated) && (d.phaseEncodingLines > 0) && (frequencyRows > 0) && ((d.xyzDim[1] % frequencyRows) == 0) && ((d.xyzDim[1] / frequencyRows) > 2) && ((d.xyzDim[2] % d.phaseEncodingLines) == 0) && ((d.xyzDim[2] / d.phaseEncodingLines) > 2)) {
7054 		//n.b. in future check if frequency is in row or column direction (and same with phase)
7055 		// >2 avoids detecting interpolated as mosaic, in future perhaps check "isInterpolated"
7056 		numberOfImagesInMosaic = (d.xyzDim[1] / frequencyRows) * (d.xyzDim[2] / d.phaseEncodingLines);
7057 		printWarning("Guessing this is a mosaic up to %d slices (issue 337).\n", numberOfImagesInMosaic);
7058 	}
7059 	if ((numberOfImagesInMosaic > 1) && (d.CSA.mosaicSlices < 1))
7060 		d.CSA.mosaicSlices = numberOfImagesInMosaic;
7061 	if (d.isXA10A)
7062 		d.manufacturer = kMANUFACTURER_SIEMENS; //XA10A mosaics omit Manufacturer 0008,0070!
7063 	if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (isMosaic) && (d.CSA.mosaicSlices < 1) && (d.phaseEncodingSteps > 0) && ((d.xyzDim[1] % d.phaseEncodingSteps) == 0) && ((d.xyzDim[2] % d.phaseEncodingSteps) == 0)) {
7064 		d.CSA.mosaicSlices = (d.xyzDim[1] / d.phaseEncodingSteps) * (d.xyzDim[2] / d.phaseEncodingSteps);
7065 		printWarning("Mosaic inferred without CSA header (check number of slices and spatial orientation)\n");
7066 	}
7067 	if ((d.isXA10A) && (isMosaic) && (d.CSA.mosaicSlices < 1))
7068 		d.CSA.mosaicSlices = -1;								//mark as bogus DICOM
7069 	if ((!d.isXA10A) && (isMosaic) && (d.CSA.mosaicSlices < 1)) //See Erlangen Vida dataset - never reports "XA10" but mosaics have no attributes
7070 		printWarning("0008,0008=MOSAIC but number of slices not specified: %s\n", fname);
7071 	if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (d.CSA.dtiV[1] < -1.0) && (d.CSA.dtiV[2] < -1.0) && (d.CSA.dtiV[3] < -1.0))
7072 		d.CSA.dtiV[0] = 0; //SiemensTrio-Syngo2004A reports B=0 images as having impossible b-vectors.
7073 	if ((strlen(d.protocolName) < 1) && (strlen(d.seriesDescription) > 1))
7074 		strcpy(d.protocolName, d.seriesDescription);
7075 	if ((strlen(d.protocolName) > 1) && (isMoCo))
7076 		strcat(d.protocolName, "_MoCo"); //disambiguate MoCo https://github.com/neurolabusc/MRIcroGL/issues/31
7077 	if ((strlen(d.protocolName) < 1) && (strlen(d.sequenceName) > 1) && (d.manufacturer != kMANUFACTURER_SIEMENS))
7078 		strcpy(d.protocolName, d.sequenceName); //protocolName (0018,1030) optional, sequence name (0018,0024) is not a good substitute for Siemens as it can vary per volume: *ep_b0 *ep_b1000#1, *ep_b1000#2, etc https://www.nitrc.org/forum/forum.php?thread_id=8771&forum_id=4703
7079 	if (numberOfFrames == 0)
7080 		numberOfFrames = d.xyzDim[3];
7081 	if ((locationsInAcquisitionPhilips > 0) && ((d.xyzDim[3] % locationsInAcquisitionPhilips) == 0)) {
7082 		d.xyzDim[4] = d.xyzDim[3] / locationsInAcquisitionPhilips;
7083 		d.xyzDim[3] = locationsInAcquisitionPhilips;
7084 	} else if ((numberOfDynamicScans > 1) && (d.xyzDim[4] < 2) && (d.xyzDim[3] > 1) && ((d.xyzDim[3] % numberOfDynamicScans) == 0)) {
7085 		d.xyzDim[3] = d.xyzDim[3] / numberOfDynamicScans;
7086 		d.xyzDim[4] = numberOfDynamicScans;
7087 	}
7088 	if ((maxInStackPositionNumber > 1) && (d.xyzDim[4] < 2) && (d.xyzDim[3] > 1) && ((d.xyzDim[3] % maxInStackPositionNumber) == 0)) {
7089 		d.xyzDim[4] = d.xyzDim[3] / maxInStackPositionNumber;
7090 		d.xyzDim[3] = maxInStackPositionNumber;
7091 	}
7092 	if ((!isnan(patientPositionStartPhilips[1])) && (!isnan(patientPositionEndPhilips[1]))) {
7093 		for (int k = 0; k < 4; k++) {
7094 			d.patientPosition[k] = patientPositionStartPhilips[k];
7095 			d.patientPositionLast[k] = patientPositionEndPhilips[k];
7096 		}
7097 	}
7098 	if ((numberOfFrames > 1) && (locationsInAcquisitionPhilips > 0) && ((numberOfFrames % locationsInAcquisitionPhilips) != 0)) { //issue515
7099 		printWarning("Number of frames (%d) not divisible by locations in acquisition (2001,1018) %d (issue 515)\n", numberOfFrames, locationsInAcquisitionPhilips);
7100 		d.xyzDim[4] = d.xyzDim[3] / locationsInAcquisitionPhilips;
7101 		d.xyzDim[3] = locationsInAcquisitionPhilips;
7102 		d.xyzDim[0] = numberOfFrames;
7103 	}
7104 	if ((B0Philips >= 0) && (d.CSA.numDti == 0)) {
7105 		d.CSA.dtiV[0] = B0Philips;
7106 		d.CSA.numDti = 1;
7107 	}											//issue409 Siemens XA saved as classic 2D not enhanced
7108 	if (!isnan(patientPositionStartPhilips[1])) //for Philips data without
7109 		for (int k = 0; k < 4; k++)
7110 			d.patientPosition[k] = patientPositionStartPhilips[k];
7111 	if (isVerbose) {
7112 		printMessage("DICOM file: %s\n", fname);
7113 		printMessage(" patient position (0020,0032)\t%g\t%g\t%g\n", d.patientPosition[1], d.patientPosition[2], d.patientPosition[3]);
7114 		if (!isnan(patientPositionEndPhilips[1]))
7115 			printMessage(" patient position end (0020,0032)\t%g\t%g\t%g\n", patientPositionEndPhilips[1], patientPositionEndPhilips[2], patientPositionEndPhilips[3]);
7116 		printMessage(" orient (0020,0037)\t%g\t%g\t%g\t%g\t%g\t%g\n", d.orient[1], d.orient[2], d.orient[3], d.orient[4], d.orient[5], d.orient[6]);
7117 		printMessage(" acq %d img %d ser %ld dim %dx%dx%dx%d mm %gx%gx%g offset %d loc %d valid %d ph %d mag %d nDTI %d 3d %d bits %d littleEndian %d echo %d coilCRC %d TE %g TR %g\n", d.acquNum, d.imageNum, d.seriesNum, d.xyzDim[1], d.xyzDim[2], d.xyzDim[3], d.xyzDim[4], d.xyzMM[1], d.xyzMM[2], d.xyzMM[3], d.imageStart, d.locationsInAcquisition, d.isValid, d.isHasPhase, d.isHasMagnitude, d.CSA.numDti, d.is3DAcq, d.bitsAllocated, d.isLittleEndian, d.echoNum, d.coilCrc, d.TE, d.TR);
7118 		if (d.CSA.dtiV[0] > 0)
7119 			printMessage(" DWI bxyz %g %g %g %g\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]);
7120 	}
7121 	if ((d.isValid) && (d.xyzDim[1] > 1) && (d.xyzDim[2] > 1) && (d.imageStart < 132) && (!d.isRawDataStorage)) {
7122 		//20190524: Philips MR 55.1 creates non-image files that report kDim1/kDim2 - we can detect them since 0008,0016 reports "RawDataStorage"
7123 		//see https://neurostars.org/t/dcm2niix-error-from-philips-dicom-qsm-data-can-this-be-skipped/4883
7124 		printError("Conversion aborted due to corrupt file: %s %dx%d %d\n", fname, d.xyzDim[1], d.xyzDim[2], d.imageStart);
7125 #ifdef USING_R
7126 		Rf_error("Irrecoverable error during conversion");
7127 #else
7128 		exit(kEXIT_CORRUPT_FILE_FOUND);
7129 #endif
7130 	}
7131 	if ((numberOfFrames > 1) && (numDimensionIndexValues == 0) && (numberOfFrames == nSliceMM)) { //issue 372
7132 		fidx *objects = (fidx *)malloc(sizeof(struct fidx) * numberOfFrames);
7133 		for (int i = 0; i < numberOfFrames; i++) {
7134 			objects[i].value = sliceMM[i];
7135 			objects[i].index = i;
7136 		}
7137 		qsort(objects, numberOfFrames, sizeof(struct fidx), fcmp);
7138 		numDimensionIndexValues = numberOfFrames;
7139 		for (int i = 0; i < numberOfFrames; i++) {
7140 			//	printf("%d > %g\n", objects[i].index, objects[i].value);
7141 			dcmDim[objects[i].index].dimIdx[0] = i;
7142 		}
7143 		for (int i = 0; i < 4; i++) {
7144 			d.patientPosition[i] = minPatientPosition[i];
7145 			d.patientPositionLast[i] = maxPatientPosition[i];
7146 		}
7147 		//printf("%g -> %g\n", objects[0].value, objects[numberOfFrames-1].value);
7148 		//printf("%g %g %g -> %g %g %g\n", d.patientPosition[1], d.patientPosition[2], d.patientPosition[3], 	d.patientPositionLast[1], d.patientPositionLast[2], d.patientPositionLast[3]);
7149 		free(objects);
7150 	} //issue 372
7151 	if ((d.echoTrainLength == 0) && (echoTrainLengthPhil))
7152 		d.echoTrainLength = echoTrainLengthPhil; //+1 ?? to convert "EPI factor" to echo train length, see issue 377
7153 	if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.xyzDim[4] > 1) && (d.is3DAcq) && (d.echoTrainLength > 1) && (minDynamicScanBeginTime < maxDynamicScanBeginTime)) { //issue369
7154 		float TR = 1000.0 * ((maxDynamicScanBeginTime - minDynamicScanBeginTime) / (d.xyzDim[4] - 1)); //-1 : fence post problem
7155 		if (fabs(TR - d.TR) > 0.001) {
7156 			printWarning("Assuming TR = %gms, not 0018,0080 = %gms (see issue 369)\n", TR, d.TR);
7157 			d.TR = TR;
7158 		}
7159 	}
7160 	//	printWarning("3D EPI with FrameAcquisitionDuration = %gs volumes = %d (see issue 369)\n", frameAcquisitionDuration/1000.0, d.xyzDim[4]);
7161 	//printf("%g %g\n", minDynamicScanBeginTime, maxDynamicScanBeginTime);
7162 	//if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.xyzDim[4] > 1) && (d.is3DAcq) && (d.echoTrainLength > 1) && (frameAcquisitionDuration > 0.0)) //issue369
7163 	//	printWarning("3D EPI with FrameAcquisitionDuration = %gs volumes = %d (see issue 369)\n", frameAcquisitionDuration/1000.0, d.xyzDim[4]);
7164 	if (numDimensionIndexValues > 1)
7165 		strcpy(d.imageType, imageType1st); //for multi-frame datasets, return name of book, not name of last chapter
7166 	if ((numDimensionIndexValues > 1) && (numDimensionIndexValues == numberOfFrames)) {
7167 		//Philips enhanced datasets can have custom slice orders and pack images with different TE, Phase/Magnitude/Etc.
7168 		int maxVariableItem = 0;
7169 		int nVariableItems = 0;
7170 		if (true) { //
7171 			int mn[MAX_NUMBER_OF_DIMENSIONS];
7172 			int mx[MAX_NUMBER_OF_DIMENSIONS];
7173 			for (int j = 0; j < MAX_NUMBER_OF_DIMENSIONS; j++) {
7174 				mx[j] = dcmDim[0].dimIdx[j];
7175 				mn[j] = mx[j];
7176 				for (int i = 0; i < numDimensionIndexValues; i++) {
7177 					if (mx[j] < dcmDim[i].dimIdx[j])
7178 						mx[j] = dcmDim[i].dimIdx[j];
7179 					if (mn[j] > dcmDim[i].dimIdx[j])
7180 						mn[j] = dcmDim[i].dimIdx[j];
7181 				}
7182 				if (mx[j] != mn[j]) {
7183 					maxVariableItem = j;
7184 					nVariableItems++;
7185 				}
7186 			}
7187 			if (isVerbose > 1) {
7188 				printMessage(" DimensionIndexValues (0020,9157), dimensions with variability:\n");
7189 				for (int i = 0; i < MAX_NUMBER_OF_DIMENSIONS; i++)
7190 					if (mn[i] != mx[i])
7191 						printMessage(" Dimension %d Range: %d..%d\n", i, mn[i], mx[i]);
7192 			}
7193 		} //verbose > 1
7194 		//see http://dicom.nema.org/medical/Dicom/2018d/output/chtml/part03/sect_C.8.24.3.3.html
7195 		//Philips puts spatial position as lower item than temporal position, the reverse is true for Bruker and Canon
7196 		int stackPositionItem = 0;
7197 		if (dimensionIndexPointerCounter > 0)
7198 			for (size_t i = 0; i < dimensionIndexPointerCounter; i++)
7199 				if (dimensionIndexPointer[i] == kInStackPositionNumber)
7200 					stackPositionItem = i;
7201 		if ((d.manufacturer == kMANUFACTURER_CANON) && (nVariableItems == 1) && (d.xyzDim[4] > 1)) {
7202 			//WARNING: Canon CANON V6.0SP2001* (0018,9005) = "AX fMRI" strangely sets TemporalPositionIndex(0020,9128) as 1 for all volumes: (0020,9157) and (0020,9128) are INCORRECT!
7203 			printf("Invalid enhanced DICOM created by Canon: Only single dimension in DimensionIndexValues (0020,9157) varies, for 4D file (e.g. BOTH space and time should vary)\n");
7204 			printf("%d %d\n", stackPositionItem, maxVariableItem);
7205 			int stackTimeItem = 0;
7206 			if (stackPositionItem == 0) {
7207 				maxVariableItem++;
7208 				stackTimeItem++; //e.g. slot 0 = space, slot 1 = time
7209 			}
7210 			int vol = 0;
7211 			for (int i = 0; i < numDimensionIndexValues; i++) {
7212 				if (1 == dcmDim[i].dimIdx[stackPositionItem])
7213 					vol++;
7214 				dcmDim[i].dimIdx[stackTimeItem] = vol;
7215 				//printf("vol %d slice %d\n", dcmDim[i].dimIdx[stackTimeItem], dcmDim[i].dimIdx[stackPositionItem]);
7216 			}
7217 		} //Kuldge for corrupted CANON 0020,9157
7218 		/* //issue533: this code fragment will replicate dicm2nii (reverse order of volume hierarchy) https://github.com/xiangruili/dicm2nii/blob/f918366731162e6895be6e5ee431444642e0e7f9/dicm2nii.m#L2205
7219 		if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (stackPositionItem == 1) && ( maxVariableItem > 2)) {
7220 			//replicate dicm2nii: s2.DimensionIndexValues([3:end 1])
7221 				uint32_t tmp[MAX_NUMBER_OF_DIMENSIONS];
7222 				for (int i = 0; i < numDimensionIndexValues; i++) {
7223 					for (int j = 2; j <= maxVariableItem; j++)
7224 						tmp[j] = dcmDim[i].dimIdx[j];
7225 					for (int j = 2; j <= maxVariableItem; j++)
7226 						dcmDim[i].dimIdx[j] = tmp[maxVariableItem - j + 2];
7227 				}
7228 		}*/
7229 		if ((isKludgeIssue533) && (numDimensionIndexValues > 1))
7230 			printWarning("Guessing temporal order for Philips enhanced DICOM ASL (issue 532).\n");
7231 		//sort dimensions
7232 #ifdef USING_R
7233 		if (stackPositionItem < maxVariableItem)
7234 			std::sort(dcmDim.begin(), dcmDim.begin() + numberOfFrames, compareTDCMdim);
7235 		else
7236 			std::sort(dcmDim.begin(), dcmDim.begin() + numberOfFrames, compareTDCMdimRev);
7237 #else
7238 		if (stackPositionItem < maxVariableItem)
7239 			qsort(dcmDim, numberOfFrames, sizeof(struct TDCMdim), compareTDCMdim);
7240 		else
7241 			qsort(dcmDim, numberOfFrames, sizeof(struct TDCMdim), compareTDCMdimRev);
7242 #endif
7243 		//for (int i = 0; i < numberOfFrames; i++)
7244 		//	printf("i %d diskPos= %d dimIdx= %d %d %d %d TE= %g\n", i, dcmDim[i].diskPos, dcmDim[i].dimIdx[0], dcmDim[i].dimIdx[1], dcmDim[i].dimIdx[2], dcmDim[i].dimIdx[3], dti4D->TE[i]);
7245 		for (int i = 0; i < numberOfFrames; i++) {
7246 			dti4D->sliceOrder[i] = dcmDim[i].diskPos;
7247 			dti4D->intenScale[i] = dcmDim[i].intenScale;
7248 			dti4D->intenIntercept[i] = dcmDim[i].intenIntercept;
7249 			dti4D->intenScalePhilips[i] = dcmDim[i].intenScalePhilips;
7250 			dti4D->RWVIntercept[i] = dcmDim[i].RWVIntercept;
7251 			dti4D->RWVScale[i] = dcmDim[i].RWVScale;
7252 			if (dti4D->intenIntercept[i] != dti4D->intenIntercept[0])
7253 				d.isScaleVariesEnh = true;
7254 			if (dti4D->intenScale[i] != dti4D->intenScale[0])
7255 				d.isScaleVariesEnh = true;
7256 			if (dti4D->intenScalePhilips[i] != dti4D->intenScalePhilips[0])
7257 				d.isScaleVariesEnh = true;
7258 		}
7259 		if (!(d.manufacturer == kMANUFACTURER_BRUKER && d.isDiffusion) && (d.xyzDim[4] > 1) && (d.xyzDim[4] < kMaxDTI4D)) { //record variations in TE
7260 			d.isScaleOrTEVaries = false;
7261 			bool isTEvaries = false;
7262 			bool isScaleVaries = false;
7263 			//setting j = 1 in next few lines is a hack, just in case TE/scale/intercept listed AFTER dimensionIndexValues
7264 			int j = 0;
7265 			for (int i = 0; i < d.xyzDim[4]; i++) {
7266 				int slice = j + (i * d.xyzDim[3]);
7267 				//dti4D->gradDynVol[i] = 0; //only PAR/REC
7268 				dti4D->TE[i] = dcmDim[slice].TE;
7269 				dti4D->isPhase[i] = dcmDim[slice].isPhase;
7270 				dti4D->isReal[i] = dcmDim[slice].isReal;
7271 				dti4D->isImaginary[i] = dcmDim[slice].isImaginary;
7272 				dti4D->triggerDelayTime[i] = dcmDim[slice].triggerDelayTime;
7273 				dti4D->S[i].V[0] = dcmDim[slice].V[0];
7274 				dti4D->S[i].V[1] = dcmDim[slice].V[1];
7275 				dti4D->S[i].V[2] = dcmDim[slice].V[2];
7276 				dti4D->S[i].V[3] = dcmDim[slice].V[3];
7277 				//printf("te=\t%g\tscl=\t%g\tintercept=\t%g\n",dti4D->TE[i], dti4D->intenScale[i],dti4D->intenIntercept[i]);
7278 				if ((!isSameFloatGE(dti4D->TE[i], 0.0)) && (dti4D->TE[i] != d.TE))
7279 					isTEvaries = true;
7280 				if (dti4D->isPhase[i] != isPhase)
7281 					d.isScaleOrTEVaries = true;
7282 				if (dti4D->triggerDelayTime[i] != d.triggerDelayTime)
7283 					d.isScaleOrTEVaries = true;
7284 				if (dti4D->isReal[i] != isReal)
7285 					d.isScaleOrTEVaries = true;
7286 				if (dti4D->isImaginary[i] != isImaginary)
7287 					d.isScaleOrTEVaries = true;
7288 				/*Philips can vary intensity scalings for separate slices within a volume!
7289 				dti4D->intenScale[i] = dcmDim[slice].intenScale;
7290 				dti4D->intenIntercept[i] = dcmDim[slice].intenIntercept;
7291 				dti4D->intenScalePhilips[i] = dcmDim[slice].intenScalePhilips;
7292 				dti4D->RWVIntercept[i] = dcmDim[slice].RWVIntercept;
7293 				dti4D->RWVScale[i] = dcmDim[slice].RWVScale;
7294 				if (dti4D->intenScale[i] != d.intenScale) isScaleVaries = true;
7295 				if (dti4D->intenIntercept[i] != d.intenIntercept) isScaleVaries = true;*/
7296 			}
7297 			if ((isScaleVaries) || (isTEvaries))
7298 				d.isScaleOrTEVaries = true;
7299 			if (isTEvaries)
7300 				d.isMultiEcho = true;
7301 			//if echoVaries,count number of echoes
7302 			/*int echoNum = 1;
7303 			for (int i = 1; i < d.xyzDim[4]; i++) {
7304 				if (dti4D->TE[i-1] != dti4D->TE[i])
7305 			}*/
7306 			if ((isVerbose) && (d.isScaleOrTEVaries)) {
7307 				printMessage("Parameters vary across 3D volumes packed in single DICOM file:\n");
7308 				for (int i = 0; i < d.xyzDim[4]; i++) {
7309 					int slice = (i * d.xyzDim[3]);
7310 					printMessage(" %d TE=%g Slope=%g Inter=%g PhilipsScale=%g Phase=%d\n", i, dti4D->TE[i], dti4D->intenScale[slice], dti4D->intenIntercept[slice], dti4D->intenScalePhilips[slice], dti4D->isPhase[i]);
7311 				}
7312 			}
7313 		}
7314 		if ((d.xyzDim[3] == maxInStackPositionNumber) && (maxInStackPositionNumber > 1) && (d.zSpacing <= 0.0)) {
7315 			float dx = sqrt(pow(d.patientPosition[1] - d.patientPositionLast[1], 2) +
7316 							pow(d.patientPosition[2] - d.patientPositionLast[2], 2) +
7317 							pow(d.patientPosition[3] - d.patientPositionLast[3], 2));
7318 			dx = dx / (maxInStackPositionNumber - 1);
7319 			if ((dx > 0.0) && (!isSameFloatGE(dx, d.xyzMM[3]))) //patientPosition has some rounding error
7320 				d.xyzMM[3] = dx;
7321 		} //d.zSpacing <= 0.0: Bruker does not populate 0018,0088 https://github.com/rordenlab/dcm2niix/issues/241
7322 	} //if numDimensionIndexValues > 1 : enhanced DICOM
7323 	if (d.CSA.numDti >= kMaxDTI4D) {
7324 		printError("Unable to convert DTI [recompile with increased kMaxDTI4D] detected=%d, max = %d\n", d.CSA.numDti, kMaxDTI4D);
7325 		d.CSA.numDti = 0;
7326 	}
7327 	if ((hasDwiDirectionality) && (d.CSA.numDti < 1))
7328 		d.CSA.numDti = 1;
7329 	if ((d.isValid) && (d.imageNum == 0)) { //Philips non-image DICOMs (PS_*, XX_*) are not valid images and do not include instance numbers
7330 		//TYPE 1 for MR image http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0020,0013)
7331 		// Only type 2 for some other DICOMs! Therefore, generate warning not error
7332 		printWarning("Instance number (0020,0013) not found: %s\n", fname);
7333 		d.imageNum = abs((int)d.instanceUidCrc) % 2147483647; //INT_MAX;
7334 		if (d.imageNum == 0)
7335 			d.imageNum = 1; //https://github.com/rordenlab/dcm2niix/issues/341
7336 							//d.imageNum = 1; //not set
7337 	}
7338 	if ((numDimensionIndexValues < 1) && (d.manufacturer == kMANUFACTURER_PHILIPS) && (d.seriesNum > 99999) && (philMRImageDiffBValueNumber > 0)) {
7339 		//Ugly kludge to distinguish Philips classic DICOM dti
7340 		// images from a single sequence can have identical series number, instance number, gradient number
7341 		// the ONLY way to distinguish slices is using the private tag MRImageDiffBValueNumber
7342 		// confusingly, the same sequence can also generate MULTIPLE series numbers!
7343 		// for examples see https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging
7344 		d.seriesNum += (philMRImageDiffBValueNumber * 1000);
7345 	}
7346 	//if (contentTime != 0.0) && (numDimensionIndexValues < (MAX_NUMBER_OF_DIMENSIONS - 1)){
7347 	//	uint_32t timeCRC = mz_crc32X((unsigned char*) &contentTime, sizeof(double));
7348 	//}
7349 	//if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (strcmp(d.sequenceName, "fldyn3d1")== 0)) {
7350 	if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (strstr(d.sequenceName, "fldyn3d1") != NULL)) {
7351 		//combine DCE series https://github.com/rordenlab/dcm2niix/issues/252
7352 		d.isStackableSeries = true;
7353 		d.imageNum += (d.seriesNum * 1000);
7354 		strcpy(d.seriesInstanceUID, d.studyInstanceUID);
7355 		d.seriesUidCrc = mz_crc32X((unsigned char *)&d.protocolName, strlen(d.protocolName));
7356 	}
7357 	//TODO533: alias Philips ASL PLD as frameDuration? isKludgeIssue533
7358 	//if ((d.manufacturer == kMANUFACTURER_PHILIPS) && ((!isTriggerSynced) || (!isProspectiveSynced)) ) //issue408
7359 	//	d.triggerDelayTime = 0.0; 		 //Philips ASL use "(0018,9037) CS [NONE]" but "(2001,1010) CS [TRIGGERED]", a situation not described in issue408
7360 	if (isSameFloat(MRImageDynamicScanBeginTime * 1000.0, d.triggerDelayTime)) //issue395
7361 		d.triggerDelayTime = 0.0;
7362 	if (d.phaseNumber > 0) //Philips TurboQUASAR set this uniquely for each slice
7363 		d.triggerDelayTime = 0.0;
7364 	//printf("%d\t%g\t%g\t%g\n", d.imageNum, d.acquisitionTime, d.triggerDelayTime, MRImageDynamicScanBeginTime);
7365 	if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (strlen(seriesTimeTxt) > 1) && (d.isXA10A) && (d.xyzDim[3] == 1) && (d.xyzDim[4] < 2)) {
7366 		//printWarning(">>Ignoring series number of XA data saved as classic DICOM (issue 394)\n");
7367 		d.isStackableSeries = true;
7368 		d.imageNum += (d.seriesNum * 1000);
7369 		strcpy(d.seriesInstanceUID, seriesTimeTxt); // dest <- src
7370 		d.seriesUidCrc = mz_crc32X((unsigned char *)&seriesTimeTxt, strlen(seriesTimeTxt));
7371 	}
7372 	if (((d.manufacturer == kMANUFACTURER_TOSHIBA) || (d.manufacturer == kMANUFACTURER_CANON)) && (B0Philips > 0.0)) { //issue 388
7373 		char txt[1024] = {""};
7374 		sprintf(txt, "b=%d(", (int)round(B0Philips));
7375 		if (strstr(d.imageComments, txt) != NULL) {
7376 			//printf("%s>>>%s %g\n", txt, d.imageComments, B0Philips);
7377 			int len = strlen(txt);
7378 			strcpy(txt, (char *)&d.imageComments[len]);
7379 			len = strlen(txt);
7380 			for (int i = 0; i <= len; i++) {
7381 				if ((txt[i] >= '0') && (txt[i] <= '9'))
7382 					continue;
7383 				if ((txt[i] == '.') || (txt[i] == '-'))
7384 					continue;
7385 				txt[i] = ' ';
7386 			}
7387 			float v[4];
7388 			dcmMultiFloat(len, (char *)&txt[0], 3, &v[0]);
7389 			d.CSA.dtiV[0] = B0Philips;
7390 #ifdef swizzleCanon //see issue422 and dcm_qa_canon
7391 			d.CSA.dtiV[1] = v[2];
7392 			d.CSA.dtiV[2] = v[1];
7393 			d.CSA.dtiV[3] = -v[3];
7394 #else
7395 			d.CSA.dtiV[1] = v[2];
7396 			d.CSA.dtiV[2] = v[1];
7397 			d.CSA.dtiV[3] = v[3];
7398 			d.manufacturer = kMANUFACTURER_CANON;
7399 #endif
7400 			//d.CSA.dtiV[1] = v[1];
7401 			//d.CSA.dtiV[2] = v[2];
7402 			//d.CSA.dtiV[3] = v[3];
7403 			d.CSA.numDti = 1;
7404 		}
7405 	}
7406 	if ((isDICOMANON) && (isMATLAB)) {
7407 		//issue 383
7408 		strcpy(d.seriesInstanceUID, d.studyDate);
7409 		// This check is unlikely to be important in practice, but it silences a warning from GCC with -Wrestrict
7410 		if (strlen(d.studyDate) < kDICOMStr) {
7411 			strncat(d.seriesInstanceUID, d.studyTime, kDICOMStr - strlen(d.studyDate));
7412 		}
7413 		d.seriesUidCrc = mz_crc32X((unsigned char *)&d.seriesInstanceUID, strlen(d.seriesInstanceUID));
7414 	}
7415 	if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (strstr(d.sequenceName, "fl2d1") != NULL)) {
7416 		d.isLocalizer = true;
7417 	}
7418 	//detect pepolar https://github.com/nipy/heudiconv/issues/479
7419 	if ((d.epiVersionGE == kGE_EPI_PEPOLAR_FWD) && (userData12GE == 1))
7420 		d.epiVersionGE = kGE_EPI_PEPOLAR_REV;
7421 	if ((d.epiVersionGE == kGE_EPI_PEPOLAR_FWD) && (userData12GE == 2))
7422 		d.epiVersionGE = kGE_EPI_PEPOLAR_REV_FWD;
7423 	if ((d.epiVersionGE == kGE_EPI_PEPOLAR_FWD) && (userData12GE == 3))
7424 		d.epiVersionGE = kGE_EPI_PEPOLAR_FWD_REV;
7425 	if ((d.epiVersionGE == kGE_EPI_PEPOLAR_REV_FWD) && (volumeNumber > 0) && ((volumeNumber % 2) == 1))
7426 		d.epiVersionGE = kGE_EPI_PEPOLAR_REV_FWD_FLIP;
7427 	if ((d.epiVersionGE == kGE_EPI_PEPOLAR_FWD_REV) && (volumeNumber > 0) && ((volumeNumber % 2) == 0))
7428 		d.epiVersionGE = kGE_EPI_PEPOLAR_FWD_REV_FLIP;
7429 	#ifndef myDisableGEPEPolarFlip //e.g. to disable patch for issue 532 "make CFLAGS=-DmyDisableGEPEPolarFlip"
7430 	if ((d.epiVersionGE == kGE_EPI_PEPOLAR_REV) || (d.epiVersionGE == kGE_EPI_PEPOLAR_FWD_REV_FLIP)  || (d.epiVersionGE == kGE_EPI_PEPOLAR_REV_FWD_FLIP)) {
7431 		if (d.epiVersionGE != kGE_EPI_PEPOLAR_REV) d.seriesNum += 1000;
7432 		if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_UNFLIPPED)
7433 				d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED;
7434 		else if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_FLIPPED)
7435 				d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED;
7436 	}
7437 	#endif
7438 	//UIH 3D T1 scans report echo train length, which is interpreted as 3D EPI
7439 	if ((d.manufacturer == kMANUFACTURER_UIH) && (strstr(d.sequenceName, "gre_fsp") != NULL))
7440 		d.echoTrainLength = 0;
7441 	//printf(">>%s\n", d.sequenceName); d.isValid = false;
7442 	// Andrey Fedorov has requested keeping GE bvalues, see issue 264
7443 	//if ((d.CSA.numDti > 0) && (d.manufacturer == kMANUFACTURER_GE) && (d.numberOfDiffusionDirectionGE < 1))
7444 	//	d.CSA.numDti = 0; //https://github.com/rordenlab/dcm2niix/issues/264
7445 	if ((!d.isLocalizer) && (isInterpolated) && (d.imageNum <= 1))
7446 		printWarning("interpolated protocol '%s' may be unsuitable for dwidenoise/mrdegibbs. %s\n", d.protocolName, fname);
7447 	if (((numDimensionIndexValues + 3) < MAX_NUMBER_OF_DIMENSIONS) && (d.rawDataRunNumber > 0))
7448 		d.dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS - 4] = d.rawDataRunNumber;
7449 	if ((numDimensionIndexValues + 2) < MAX_NUMBER_OF_DIMENSIONS)
7450 		d.dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS - 3] = d.instanceUidCrc;
7451 	if ((numDimensionIndexValues + 1) < MAX_NUMBER_OF_DIMENSIONS)
7452 		d.dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS - 2] = d.echoNum;
7453 	if (numDimensionIndexValues < MAX_NUMBER_OF_DIMENSIONS) //https://github.com/rordenlab/dcm2niix/issues/221
7454 		d.dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS - 1] = mz_crc32X((unsigned char *)&d.seriesInstanceUID, strlen(d.seriesInstanceUID));
7455 	if ((d.isValid) && (d.seriesUidCrc == 0)) {
7456 		if (d.seriesNum < 1)
7457 			d.seriesUidCrc = 1; //no series information
7458 		else
7459 			d.seriesUidCrc = d.seriesNum; //file does not have Series UID, use series number instead
7460 	}
7461 	if (d.seriesNum < 1) //https://github.com/rordenlab/dcm2niix/issues/218
7462 		d.seriesNum = mz_crc32X((unsigned char *)&d.seriesInstanceUID, strlen(d.seriesInstanceUID));
7463 	getFileName(d.imageBaseName, fname);
7464 	if (multiBandFactor > d.CSA.multiBandFactor)
7465 		d.CSA.multiBandFactor = multiBandFactor; //SMS reported in 0051,1011 but not CSA header
7466 #ifndef myLoadWholeFileToReadHeader
7467 	fclose(file);
7468 #endif
7469 	if ((temporalResolutionMS > 0.0) && (isSameFloatGE(d.TR, temporalResolutionMS))) {
7470 		//do something profound
7471 		//in practice 0020,0110 not used
7472 		//https://github.com/bids-standard/bep001/blob/repetitiontime/Proposal_RepetitionTime.md
7473 	}
7474 	//issue 542
7475 	if ((d.manufacturer == kMANUFACTURER_GE) && (isNeologica) && (!isSameFloat(d.CSA.dtiV[0], 0.0f)) && ((isSameFloat(d.CSA.dtiV[1], 0.0f)) && (isSameFloat(d.CSA.dtiV[2], 0.0f)) && (isSameFloat(d.CSA.dtiV[3], 0.0f)) ) )
7476 		printWarning("GE DWI vectors may have been removed by Neologica DICOM Anonymizer Pro (Issue 542)\n");
7477 	//start: issue529 TODO JJJJ
7478 	if ((!isSameFloat(d.CSA.dtiV[0], 0.0f)) && ((isSameFloat(d.CSA.dtiV[1], 0.0f)) && (isSameFloat(d.CSA.dtiV[2], 0.0f)) && (isSameFloat(d.CSA.dtiV[3], 0.0f)) ) )
7479 		gradientOrientationNumberPhilips = kMaxDTI4D + 1; //Philips includes derived Trace/ADC images into raw DTI, these should be removed...
7480 	//if (sliceNumberMrPhilips == 1) printf("instance\t %d\ttime\t%g\tvolume\t%d\tgradient\t%d\tphase\t%d\tisLabel\t%d\n", d.imageNum,  MRImageDynamicScanBeginTime, volumeNumber, gradientOrientationNumberPhilips, d.phaseNumber, d.aslFlags == kASL_FLAG_PHILIPS_LABEL);
7481 	//if (sliceNumberMrPhilips == 1) printf("ACQtime\t%g\tinstance\t %d\ttime\t%g\tvolume\t%d\tphase\t%d\tisLabel\t%d\n", d.acquisitionTime, d.imageNum,  MRImageDynamicScanBeginTime, volumeNumber, d.phaseNumber, d.aslFlags == kASL_FLAG_PHILIPS_LABEL);
7482 	//if (sliceNumberMrPhilips == 1) printf("%d\t%d\t%g\t%d\n", philMRImageDiffBValueNumber, gradientOrientationNumberPhilips, d.CSA.dtiV[0], philMRImageDiffVolumeNumber); //issue546
7483 	d.phaseNumber  =  (d.phaseNumber > philMRImageDiffBValueNumber) ? d.phaseNumber : philMRImageDiffBValueNumber; //we need both BValueNumber(2005,1412) and GradientOrientationNumber(2005,1413) to resolve volumes: issue546
7484 	d.rawDataRunNumber =  (d.rawDataRunNumber > volumeNumber) ? d.rawDataRunNumber : volumeNumber;
7485 	d.rawDataRunNumber =  (d.rawDataRunNumber > gradientOrientationNumberPhilips) ? d.rawDataRunNumber : gradientOrientationNumberPhilips;
7486 	if ((d.rawDataRunNumber < 0) && (d.manufacturer == kMANUFACTURER_PHILIPS) && (nDimIndxVal > 1) && (d.dimensionIndexValues[nDimIndxVal - 1] > 0))
7487 		d.rawDataRunNumber =  d.dimensionIndexValues[nDimIndxVal - 1]; //Philips enhanced scans converted to classic with dcuncat
7488 	if (philMRImageDiffVolumeNumber > 0) { //use 2005,1596 for Philips DWI >= R5.6
7489 		d.rawDataRunNumber = philMRImageDiffVolumeNumber;
7490 		d.phaseNumber  = 0;
7491 	}
7492 	// d.rawDataRunNumber =  (d.rawDataRunNumber > d.phaseNumber) ? d.rawDataRunNumber : d.phaseNumber; //will not work: conflict for MultiPhase ASL with multiple averages
7493 	//end: issue529
7494 	if (hasDwiDirectionality)
7495 		d.isVectorFromBMatrix = false; //issue 265: Philips/Siemens have both directionality and bmatrix, Bruker only has bmatrix
7496 	//printf("%s\t%s\t%s\t%s\t%s_%s\n",d.patientBirthDate, d.procedureStepDescription,d.patientName, fname, d.studyDate, d.studyTime);
7497 	//d.isValid = false;
7498 	//printMessage(" patient position (0020,0032)\t%g\t%g\t%g\n", d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]);
7499 	//printf("%d\t%g\t%g\t%g\t%g\n", d.imageNum, d.rtia_timerGE, d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]);
7500 	//printf("%g\t\t%g\t%g\t%g\t%s\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3], fname);
7501 	//printMessage("buffer usage %d %d %d\n",d.imageStart, lPos+lFileOffset, MaxBufferSz);
7502 	return d;
7503 } // readDICOM()
7504 
7505 void setDefaultPrefs(struct TDCMprefs *prefs) {
7506 	prefs->isVerbose = false;
7507 	prefs->compressFlag = kCompressSupport;
7508 	prefs->isIgnoreTriggerTimes = false;
7509 }
7510 
7511 struct TDICOMdata readDICOMv(char *fname, int isVerbose, int compressFlag, struct TDTI4D *dti4D) {
7512 	struct TDCMprefs prefs;
7513 	setDefaultPrefs(&prefs);
7514 	prefs.isVerbose = isVerbose;
7515 	prefs.compressFlag = compressFlag;
7516 	TDICOMdata ret = readDICOMx(fname, &prefs, dti4D);
7517 	return ret;
7518 }
7519 
7520 struct TDICOMdata readDICOM(char *fname) {
7521 	struct TDTI4D *dti4D = (struct TDTI4D *)malloc(sizeof(struct TDTI4D)); //unused
7522 	TDICOMdata ret = readDICOMv(fname, false, kCompressSupport, dti4D);
7523 	free(dti4D);
7524 	return ret;
7525 } // readDICOM()
7526