1 /**
2 * \file Reader for MGH/MGZ (FreeSurfer) format files.
3 */
4
5 #ifdef HAVE_CONFIG_H
6 #include "config.h"
7 #endif /*HAVE_CONFIG_H*/
8
9 #include "input_mgh.h"
10
11 #ifdef _WIN32
12 # define WIN32_LEAN_AND_MEAN
13 # include <Winsock2.h>
14 #else
15 # include <arpa/inet.h> /* for ntohl and ntohs */
16 #endif
17 #include "znzlib.h"
18 #include "errno.h"
19
20 #define NUM_BYTE_VALUES (UCHAR_MAX + 1)
21
22 #define MGH_MAX_DIMS 4 /* Maximum number of dimensions */
23 #define MGH_N_SPATIAL VIO_N_DIMENSIONS /* Number of spatial dimensions */
24 #define MGH_N_COMPONENTS 4 /* Number of transform components. */
25 #define MGH_N_XFORM (MGH_N_COMPONENTS * MGH_N_SPATIAL)
26
27 #define MGH_HEADER_SIZE 284 /* Total number of bytes in the header. */
28
29 #define MGH_EXTRA_SIZE 194 /* Number of "unused" bytes in the header. */
30
31 #define MGH_TYPE_UCHAR 0 /**< Voxels are 1-byte unsigned integers. */
32 #define MGH_TYPE_INT 1 /**< Voxels are 4-byte signed integers. */
33 #define MGH_TYPE_LONG 2 /**< Unsupported here. */
34 #define MGH_TYPE_FLOAT 3 /**< Voxels are 4-byte floating point. */
35 #define MGH_TYPE_SHORT 4 /**< Voxels are 2-byte signed integers. */
36 #define MGH_TYPE_BITMAP 5 /**< Unsupported here. */
37 #define MGH_TYPE_TENSOR 6 /**< Unsupported here. */
38
39 /* MGH tag types, at least the ones that are minimally documented.
40 */
41 #define TAG_OLD_COLORTABLE 1
42 #define TAG_OLD_USEREALRAS 2
43 #define TAG_CMDLINE 3
44 #define TAG_USEREALRAS 4
45 #define TAG_COLORTABLE 5
46
47 #define TAG_GCAMORPH_GEOM 10
48 #define TAG_GCAMORPH_TYPE 11
49 #define TAG_GCAMORPH_LABELS 12
50
51 #define TAG_OLD_SURF_GEOM 20
52 #define TAG_SURF_GEOM 21
53
54 #define TAG_OLD_MGH_XFORM 30
55 #define TAG_MGH_XFORM 31
56 #define TAG_GROUP_AVG_SURFACE_AREA 32
57
58 /**
59 * Information in the MGH/MGZ file header.
60 */
61 struct mgh_header {
62 int version; /**< Must be 0x00000001. */
63 int sizes[MGH_MAX_DIMS]; /**< Dimension sizes, fastest-varying FIRST. */
64 int type; /**< One of the MGH_TYPE_xxx values. */
65 int dof; /**< Degrees of freedom, if used. */
66 short goodRASflag; /**< True if spacing and dircos valid. */
67 float spacing[MGH_N_SPATIAL]; /**< Dimension spacing. */
68 float dircos[MGH_N_COMPONENTS][MGH_N_SPATIAL]; /**< Dimension transform. */
69 };
70
71 /**
72 * Trailer information found immediately AFTER the hyperslab of data.
73 */
74 struct mgh_trailer {
75 float TR;
76 float FlipAngle;
77 float TE;
78 float TI;
79 float FoV;
80 };
81
82 /**
83 * Trivial function to swap floats if necessary.
84 * \param f The big-endian 4-byte float value.
85 * \return The float value in host byte order.
86 */
87 static float
swapFloat(float f)88 swapFloat(float f)
89 {
90 union {
91 float f;
92 int i;
93 } sl;
94
95 sl.f = f;
96
97 sl.i = ntohl(sl.i);
98
99 return sl.f;
100 }
101
102 /**
103 * Reads the next slice from the MGH volume. As a side effect, it advances
104 * the slice_index value in the volume input structure if successful.
105 *
106 * \param in_ptr The volume input information.
107 * \return VIO_OK if success
108 */
109 static VIO_Status
input_next_slice(volume_input_struct * in_ptr)110 input_next_slice(
111 volume_input_struct *in_ptr
112 )
113 {
114 size_t n_voxels_in_slice;
115 size_t n_bytes_per_voxel;
116 znzFile fp = (znzFile) in_ptr->volume_file;
117
118 n_bytes_per_voxel = get_type_size(in_ptr->file_data_type);
119 n_voxels_in_slice = (in_ptr->sizes_in_file[0] *
120 in_ptr->sizes_in_file[1]);
121 if (znzread(in_ptr->generic_slice_buffer,
122 n_bytes_per_voxel,
123 n_voxels_in_slice,
124 fp) != n_voxels_in_slice)
125 {
126 fprintf(stderr, "read error %d\n", errno);
127 return VIO_ERROR;
128 }
129
130 return VIO_OK;
131 }
132
133 /**
134 * Converts a MGH file header into a general linear transform for the
135 * Volume IO library. There are two different ways of defining the
136 " "centre" of the volume in the MGH world. One uses the values in
137 * c_r, c_a, and c_s (the last row of the dircos field) to offset
138 * the origin. The other, more common case ignores these fields and
139 * just uses the voxel size and spacing to determine a value for
140 * the centre.
141 * Note that the geometric structures produced by MGH tools use
142 * the latter case.
143 *
144 * \param hdr_ptr A pointer to the MGH header structure.
145 * \param in_ptr A pointer to the input information for this volume.
146 * \param ignore_offsets TRUE if we should use grid centres.
147 * \param linear_xform_ptr A pointer to the output transform
148 */
149 static void
mgh_header_to_linear_transform(const struct mgh_header * hdr_ptr,const volume_input_struct * in_ptr,VIO_BOOL ignore_offsets,struct VIO_General_transform * linear_xform_ptr)150 mgh_header_to_linear_transform(const struct mgh_header *hdr_ptr,
151 const volume_input_struct *in_ptr,
152 VIO_BOOL ignore_offsets,
153 struct VIO_General_transform *linear_xform_ptr)
154 {
155 int i, j;
156 VIO_Transform mnc_xform;
157 VIO_Real mgh_xform[MGH_N_SPATIAL][MGH_N_COMPONENTS];
158
159 make_identity_transform(&mnc_xform);
160
161 #if DEBUG
162 /* Print out the raw MGH transform information.
163 */
164 for (i = 0; i < MGH_N_SPATIAL; i++)
165 {
166 for (j = 0; j < MGH_N_COMPONENTS; j++)
167 {
168 printf("%c_%c %8.4f ", "xyzc"[j], "ras"[i], hdr_ptr->dircos[j][i]);
169 }
170 printf("\n");
171 }
172 #endif // DEBUG
173
174 /* Multiply the direction cosines by the spacings.
175 */
176 for (i = 0; i < MGH_N_SPATIAL; i++)
177 {
178 for (j = 0; j < MGH_N_SPATIAL; j++)
179 {
180 mgh_xform[i][j] = hdr_ptr->dircos[j][i] * hdr_ptr->spacing[i];
181 }
182 }
183
184 /* Work out the final MGH transform. This requires that we figure out
185 * the origin values to fill in the final column of the transform.
186 */
187 for (i = 0; i < MGH_N_SPATIAL; i++)
188 {
189 double temp = 0.0;
190 for (j = 0; j < MGH_N_SPATIAL; j++)
191 {
192 temp += mgh_xform[i][j] * (hdr_ptr->sizes[j] / 2.0);
193 }
194
195 /* Set the origin for the voxel-to-world transform .
196 */
197 if (ignore_offsets)
198 {
199 mgh_xform[i][MGH_N_COMPONENTS - 1] = -temp;
200 }
201 else
202 {
203 mgh_xform[i][MGH_N_COMPONENTS - 1] = hdr_ptr->dircos[MGH_N_COMPONENTS - 1][i] - temp;
204 }
205 }
206
207 #if DEBUG
208 printf("mgh_xform:\n"); /* DEBUG */
209 for (i = 0; i < MGH_N_SPATIAL; i++)
210 {
211 for (j = 0; j < MGH_N_COMPONENTS; j++)
212 {
213 printf("%.4f ", mgh_xform[i][j]);
214 }
215 printf("\n");
216 }
217 #endif // DEBUG
218
219 /* Convert MGH transform to the MINC format. The only difference is
220 * that our transform is always written in XYZ (RAS) order, so we
221 * have to swap the _columns_ as needed.
222 */
223 for (i = 0; i < MGH_N_SPATIAL; i++)
224 {
225 for (j = 0; j < MGH_N_COMPONENTS; j++)
226 {
227 int volume_axis = j;
228 if (j < VIO_N_DIMENSIONS)
229 {
230 volume_axis = in_ptr->axis_index_from_file[j];
231 }
232 Transform_elem(mnc_xform, i, volume_axis) = mgh_xform[i][j];
233 }
234 }
235 create_linear_transform(linear_xform_ptr, &mnc_xform);
236 #if DEBUG
237 output_transform(stdout, "debug", NULL, NULL, linear_xform_ptr);
238 #endif // DEBUG
239 }
240
241 /**
242 * Read an MGH header from an open file stream.
243 * \param fp The open file (may be a pipe).
244 * \param hdr_ptr A pointer to the header structure to fill in.
245 * \returns TRUE if successful.
246 */
247 static VIO_BOOL
mgh_header_from_file(znzFile fp,struct mgh_header * hdr_ptr)248 mgh_header_from_file(znzFile fp, struct mgh_header *hdr_ptr)
249 {
250 int i, j;
251 char dummy[MGH_HEADER_SIZE];
252
253 /* Read in the header. We do this piecemeal because the mgh_header
254 * struct will not be properly aligned on most systems, so a single
255 * fread() WILL NOT WORK.
256 */
257 if (znzread(&hdr_ptr->version, sizeof(int), 1, fp) != 1 ||
258 znzread(hdr_ptr->sizes, sizeof(int), MGH_MAX_DIMS, fp) != MGH_MAX_DIMS ||
259 znzread(&hdr_ptr->type, sizeof(int), 1, fp) != 1 ||
260 znzread(&hdr_ptr->dof, sizeof(int), 1, fp) != 1 ||
261 znzread(&hdr_ptr->goodRASflag, sizeof(short), 1, fp) != 1 ||
262 /* The rest of the fields are optional, but we can safely read them
263 * now and check goodRASflag later to see if we should really trust
264 * them.
265 */
266 znzread(hdr_ptr->spacing, sizeof(float), MGH_N_SPATIAL, fp) != MGH_N_SPATIAL ||
267 znzread(hdr_ptr->dircos, sizeof(float), MGH_N_XFORM, fp) != MGH_N_XFORM ||
268 znzread(dummy, 1, MGH_EXTRA_SIZE, fp) != MGH_EXTRA_SIZE)
269 {
270 print_error("Problem reading MGH file header.");
271 return FALSE;
272 }
273
274 /* Successfully read all of the data. Now we have to convert it to the
275 * machine's byte ordering.
276 */
277 hdr_ptr->version = ntohl(hdr_ptr->version);
278 for (i = 0; i < MGH_MAX_DIMS; i++)
279 {
280 hdr_ptr->sizes[i] = ntohl(hdr_ptr->sizes[i]);
281 }
282 hdr_ptr->type = ntohl(hdr_ptr->type);
283 hdr_ptr->dof = ntohl(hdr_ptr->dof);
284 hdr_ptr->goodRASflag = ntohs(hdr_ptr->goodRASflag);
285
286 if (hdr_ptr->version != 1)
287 {
288 print_error("Must be MGH version 1.\n");
289 return FALSE;
290 }
291
292 if (hdr_ptr->goodRASflag)
293 {
294 for (i = 0; i < MGH_N_SPATIAL; i++)
295 {
296 hdr_ptr->spacing[i] = swapFloat(hdr_ptr->spacing[i]);
297 for (j = 0; j < MGH_N_COMPONENTS; j++)
298 {
299 hdr_ptr->dircos[j][i] = swapFloat(hdr_ptr->dircos[j][i]);
300 }
301 }
302 }
303 else
304 {
305 /* Flag is zero, so just use the defaults.
306 */
307 for (i = 0; i < MGH_N_SPATIAL; i++)
308 {
309 /* Default spacing is 1.0.
310 */
311 hdr_ptr->spacing[i] = 1.0;
312
313 /* Assume coronal orientation.
314 */
315 for (j = 0; j < MGH_N_COMPONENTS; j++)
316 {
317 hdr_ptr->dircos[j][i] = 0.0;
318 }
319 hdr_ptr->dircos[0][0] = -1.0;
320 hdr_ptr->dircos[1][2] = -1.0;
321 hdr_ptr->dircos[2][1] = 1.0;
322 }
323 }
324 return TRUE;
325 }
326
327 static VIO_BOOL
mgh_scan_for_voxel_range(volume_input_struct * in_ptr,long n_voxels_in_slice,float * min_value_ptr,float * max_value_ptr)328 mgh_scan_for_voxel_range(volume_input_struct *in_ptr,
329 long n_voxels_in_slice,
330 float *min_value_ptr,
331 float *max_value_ptr)
332 {
333 znzFile fp = (znzFile) in_ptr->volume_file;
334 float min_value = FLT_MAX;
335 float max_value = -FLT_MAX;
336 int slice;
337 float value = 0;
338 int i;
339 void *data_ptr;
340 long int data_offset = znztell((znzFile) fp);
341 int total_slices = in_ptr->sizes_in_file[2];
342
343 if (in_ptr->sizes_in_file[3] > 1)
344 {
345 /* If there is a time dimension, incorporate that into our slice
346 * count.
347 */
348 total_slices *= in_ptr->sizes_in_file[3];
349 }
350
351 if (data_offset < 0)
352 return FALSE;
353
354 for (slice = 0; slice < total_slices; slice++)
355 {
356 input_next_slice( in_ptr );
357 data_ptr = in_ptr->generic_slice_buffer;
358 for (i = 0; i < n_voxels_in_slice; i++)
359 {
360 switch (in_ptr->file_data_type)
361 {
362 case VIO_UNSIGNED_BYTE:
363 value = ((unsigned char *)data_ptr)[i];
364 break;
365
366 case VIO_SIGNED_SHORT:
367 value = ntohs(((short *)data_ptr)[i]);
368 break;
369
370 case VIO_SIGNED_INT:
371 value = ntohl(((int *)data_ptr)[i]);
372 break;
373
374 case VIO_FLOAT:
375 value = swapFloat(((float *)data_ptr)[i]);
376 break;
377
378 case VIO_NO_DATA_TYPE:
379 case VIO_SIGNED_BYTE:
380 case VIO_UNSIGNED_SHORT:
381 case VIO_UNSIGNED_INT:
382 case VIO_DOUBLE:
383 case VIO_MAX_DATA_TYPE:
384 break;
385 }
386
387 if (value < min_value )
388 min_value = value;
389 if (value > max_value )
390 max_value = value;
391 }
392 }
393
394 *min_value_ptr = min_value;
395 *max_value_ptr = max_value;
396 in_ptr->slice_index = 0;
397 znzseek((znzFile) fp, data_offset, SEEK_SET);
398 return TRUE;
399 }
400
401 VIOAPI VIO_Status
initialize_mgh_format_input(VIO_STR filename,VIO_Volume volume,volume_input_struct * in_ptr)402 initialize_mgh_format_input(VIO_STR filename,
403 VIO_Volume volume,
404 volume_input_struct *in_ptr)
405 {
406 int sizes[VIO_MAX_DIMENSIONS];
407 long n_voxels_in_slice;
408 int n_bytes_per_voxel;
409 nc_type desired_nc_type;
410 znzFile fp;
411 int axis;
412 struct mgh_header hdr;
413 VIO_General_transform mnc_native_xform;
414
415 VIO_Real mnc_dircos[VIO_N_DIMENSIONS][VIO_N_DIMENSIONS];
416 VIO_Real mnc_steps[VIO_MAX_DIMENSIONS];
417 VIO_Real mnc_starts[VIO_MAX_DIMENSIONS];
418 int n_dimensions;
419 nc_type file_nc_type;
420 VIO_BOOL signed_flag;
421
422 if ((fp = znzopen(filename, "rb", 1)) == NULL)
423 {
424 print_error("Unable to open file %s, errno %d.\n", filename, errno);
425 return VIO_ERROR;
426 }
427
428 if (!mgh_header_from_file(fp, &hdr))
429 {
430 znzclose(fp);
431 return VIO_ERROR;
432 }
433
434 /* Translate from MGH to VIO types.
435 */
436 switch (hdr.type)
437 {
438 case MGH_TYPE_UCHAR:
439 in_ptr->file_data_type = VIO_UNSIGNED_BYTE;
440 file_nc_type = NC_BYTE;
441 signed_flag = FALSE;
442 break;
443 case MGH_TYPE_INT:
444 in_ptr->file_data_type = VIO_SIGNED_INT;
445 file_nc_type = NC_INT;
446 signed_flag = TRUE;
447 break;
448 case MGH_TYPE_FLOAT:
449 in_ptr->file_data_type = VIO_FLOAT;
450 file_nc_type = NC_FLOAT;
451 signed_flag = TRUE;
452 break;
453 case MGH_TYPE_SHORT:
454 in_ptr->file_data_type = VIO_SIGNED_SHORT;
455 file_nc_type = NC_SHORT;
456 signed_flag = TRUE;
457 break;
458 default:
459 print_error("Unknown MGH data type.\n");
460 znzclose(fp);
461 return VIO_ERROR;
462 }
463
464 /* Decide how to store data in memory. */
465
466 if ( get_volume_data_type(volume) == VIO_NO_DATA_TYPE )
467 desired_nc_type = file_nc_type;
468 else
469 desired_nc_type = get_volume_nc_data_type(volume, &signed_flag);
470
471 if( volume->spatial_axes[VIO_X] < 0 ||
472 volume->spatial_axes[VIO_Y] < 0 ||
473 volume->spatial_axes[VIO_Z] < 0 )
474 {
475 print_error("warning: setting MGH spatial axes to XYZ.\n");
476 volume->spatial_axes[VIO_X] = 0;
477 volume->spatial_axes[VIO_Y] = 1;
478 volume->spatial_axes[VIO_Z] = 2;
479 }
480
481 /* Calculate the number of non-trivial dimensions in the file.
482 */
483 n_dimensions = 0;
484 for_less( axis, 0, MGH_MAX_DIMS )
485 {
486 in_ptr->sizes_in_file[axis] = hdr.sizes[axis];
487 if (hdr.sizes[axis] > 1)
488 {
489 n_dimensions++;
490 }
491 }
492
493 if (!set_volume_n_dimensions(volume, n_dimensions))
494 {
495 printf("Problem setting number of dimensions to %d\n", n_dimensions);
496 }
497
498 /* Set up the correspondence between the file axes and the MINC
499 * spatial axes. Each row contains the 'x', 'y', and 'z' components
500 * along the right/left, anterior/posterior, or superior/inferior
501 * axes (RAS). The "xspace" axis will be the one that has the largest
502 * component in the RL direction, "yspace" refers to AP, and "zspace"
503 * to SI. This tells us both how to convert the transform and how the
504 * file data is arranged.
505 */
506 for_less( axis, 0, MGH_N_SPATIAL )
507 {
508 int spatial_axis = VIO_X;
509 float c_x = fabsf(hdr.dircos[axis][VIO_X]);
510 float c_y = fabsf(hdr.dircos[axis][VIO_Y]);
511 float c_z = fabsf(hdr.dircos[axis][VIO_Z]);
512
513 if (c_y > c_x && c_y > c_z)
514 {
515 spatial_axis = VIO_Y;
516 }
517 if (c_z > c_x && c_z > c_y)
518 {
519 spatial_axis = VIO_Z;
520 }
521 in_ptr->axis_index_from_file[axis] = spatial_axis;
522 }
523
524 /* Record the time axis, if present.
525 */
526 in_ptr->axis_index_from_file[3] = 3;
527
528 mgh_header_to_linear_transform(&hdr, in_ptr, TRUE, &mnc_native_xform);
529
530 convert_transform_to_starts_and_steps(&mnc_native_xform,
531 VIO_N_DIMENSIONS,
532 NULL,
533 volume->spatial_axes,
534 mnc_starts,
535 mnc_steps,
536 mnc_dircos);
537
538 delete_general_transform(&mnc_native_xform);
539
540 for_less( axis, 0, VIO_N_DIMENSIONS)
541 {
542 int volume_axis = volume->spatial_axes[axis];
543 int file_axis = in_ptr->axis_index_from_file[volume_axis];
544 sizes[file_axis] = in_ptr->sizes_in_file[volume_axis];
545 set_volume_direction_cosine(volume, volume_axis, mnc_dircos[volume_axis]);
546 }
547 #if DEBUG
548 for_less( axis, 0, VIO_N_DIMENSIONS)
549 {
550 int volume_axis = volume->spatial_axes[axis];
551
552 printf("%d %d size:%4d step:%6.3f start:%9.4f dc:[%7.4f %7.4f %7.4f]\n",
553 axis,
554 in_ptr->axis_index_from_file[volume_axis],
555 sizes[volume_axis],
556 mnc_steps[volume_axis],
557 mnc_starts[volume_axis],
558 mnc_dircos[volume_axis][0],
559 mnc_dircos[volume_axis][1],
560 mnc_dircos[volume_axis][2]);
561 }
562 #endif // DEBUG
563 /* If there is a time axis, just assign a default step size of one.
564 */
565 mnc_steps[3] = 1;
566 set_volume_separations( volume, mnc_steps );
567
568 /* If there is a time axis, just assign a default start time of zero.
569 */
570 mnc_starts[3] = 0;
571 set_volume_starts( volume, mnc_starts );
572
573 set_volume_type( volume, desired_nc_type, signed_flag, 0.0, 0.0 );
574
575 /* If we are a 4D image, we need to copy the size here.
576 */
577 sizes[3] = in_ptr->sizes_in_file[3];
578 set_volume_sizes( volume, sizes );
579
580 n_bytes_per_voxel = get_type_size( in_ptr->file_data_type );
581
582 n_voxels_in_slice = (in_ptr->sizes_in_file[0] *
583 in_ptr->sizes_in_file[1]);
584
585 in_ptr->min_value = DBL_MAX;
586 in_ptr->max_value = -DBL_MAX;
587
588 /* Allocate the slice buffer. */
589
590 in_ptr->generic_slice_buffer = malloc(n_voxels_in_slice * n_bytes_per_voxel);
591 if (in_ptr->generic_slice_buffer == NULL)
592 {
593 return VIO_ERROR;
594 }
595
596 in_ptr->volume_file = (FILE *) fp;
597
598 in_ptr->slice_index = 0;
599
600 /* If the data must be converted to byte, read the entire image file simply
601 * to find the max and min values. This allows us to set the value_scale and
602 * value_translation properly when we read the file.
603 */
604 if (get_volume_data_type(volume) != in_ptr->file_data_type )
605 {
606 float min_value, max_value;
607
608 mgh_scan_for_voxel_range(in_ptr, n_voxels_in_slice, &min_value, &max_value);
609 set_volume_voxel_range(volume, min_value, max_value);
610 }
611 return VIO_OK;
612 }
613
614 VIOAPI void
delete_mgh_format_input(volume_input_struct * in_ptr)615 delete_mgh_format_input(
616 volume_input_struct *in_ptr
617 )
618 {
619 znzFile fp = (znzFile) in_ptr->volume_file;
620
621 free( in_ptr->generic_slice_buffer );
622
623 znzclose( fp );
624 }
625
626 VIOAPI VIO_BOOL
input_more_mgh_format_file(VIO_Volume volume,volume_input_struct * in_ptr,VIO_Real * fraction_done)627 input_more_mgh_format_file(
628 VIO_Volume volume,
629 volume_input_struct *in_ptr,
630 VIO_Real *fraction_done
631 )
632 {
633 int i;
634 VIO_Real value = 0;
635 VIO_Status status;
636 VIO_Real value_translation, value_scale;
637 VIO_Real original_min_voxel, original_max_voxel;
638 int *inner_index, indices[VIO_MAX_DIMENSIONS];
639 void *data_ptr;
640 int data_ind;
641 int total_slices = in_ptr->sizes_in_file[2];
642
643 if (in_ptr->sizes_in_file[3] > 1)
644 {
645 /* If there is a time dimension, incorporate that into our slice
646 * count.
647 */
648 total_slices *= in_ptr->sizes_in_file[3];
649 }
650
651 if ( in_ptr->slice_index < total_slices )
652 {
653 /* If the memory for the volume has not been allocated yet,
654 * initialize that memory now.
655 */
656 if (!volume_is_alloced(volume))
657 {
658 alloc_volume_data(volume);
659 if (!volume_is_alloced(volume))
660 {
661 print_error("Failed to allocate volume.\n");
662 return FALSE;
663 }
664 }
665
666 status = input_next_slice( in_ptr );
667 if ( status != VIO_OK )
668 {
669 return FALSE;
670 }
671
672 /* See if we need to apply scaling to this slice. This is only
673 * needed if the volume voxel type is not the same as the file
674 * voxel type. THIS IS ONLY REALLY LEGAL FOR BYTE VOLUME TYPES.
675 */
676 if (get_volume_data_type(volume) != in_ptr->file_data_type)
677 {
678 get_volume_voxel_range(volume, &original_min_voxel, &original_max_voxel);
679 value_translation = original_min_voxel;
680 value_scale = (original_max_voxel - original_min_voxel) /
681 (VIO_Real) (NUM_BYTE_VALUES - 1);
682 }
683 else
684 {
685 /* Just do the trivial scaling. */
686 value_translation = 0.0;
687 value_scale = 1.0;
688 }
689
690 /* Set up the indices.
691 */
692 inner_index = &indices[in_ptr->axis_index_from_file[0]];
693
694 if (in_ptr->sizes_in_file[3] > 1)
695 {
696 /* If a time dimension is present, convert the slice index into
697 * both a time and slice coordinate using the number of slices.
698 */
699 indices[in_ptr->axis_index_from_file[3]] = in_ptr->slice_index / in_ptr->sizes_in_file[2];
700 indices[in_ptr->axis_index_from_file[2]] = in_ptr->slice_index % in_ptr->sizes_in_file[2];
701 }
702 else
703 {
704 indices[in_ptr->axis_index_from_file[2]] = in_ptr->slice_index;
705 }
706
707 if ( status == VIO_OK )
708 {
709 data_ptr = in_ptr->generic_slice_buffer;
710 data_ind = 0;
711
712 for_less( i, 0, in_ptr->sizes_in_file[1] )
713 {
714 indices[in_ptr->axis_index_from_file[1]] = i;
715 for_less( *inner_index, 0, in_ptr->sizes_in_file[0] )
716 {
717 switch ( in_ptr->file_data_type )
718 {
719 case VIO_UNSIGNED_BYTE:
720 value = ((unsigned char *)data_ptr)[data_ind++];
721 break;
722 case VIO_SIGNED_SHORT:
723 value = ntohs(((short *)data_ptr)[data_ind++]);
724 break;
725 case VIO_SIGNED_INT:
726 value = ntohl(((int *)data_ptr)[data_ind++]);
727 break;
728 case VIO_FLOAT:
729 value = swapFloat(((float *)data_ptr)[data_ind++]);
730 break;
731 default:
732 handle_internal_error( "input_more_mgh_format_file" );
733 break;
734 }
735 value = (value - value_translation) / value_scale;
736 if (value > in_ptr->max_value)
737 {
738 in_ptr->max_value = value;
739 }
740 if (value < in_ptr->min_value)
741 {
742 in_ptr->min_value = value;
743 }
744 set_volume_voxel_value( volume,
745 indices[VIO_X],
746 indices[VIO_Y],
747 indices[VIO_Z],
748 indices[3],
749 0,
750 value);
751 }
752 }
753 }
754 in_ptr->slice_index++; /* Advance to the next slice. */
755 }
756
757 *fraction_done = (VIO_Real) in_ptr->slice_index / total_slices;
758
759 /* See if we are all done. If so, we need to perform a final check
760 * of the volume to set the ranges appropriately.
761 */
762 if (in_ptr->slice_index == total_slices)
763 {
764 set_volume_voxel_range( volume, in_ptr->min_value, in_ptr->max_value );
765
766 /* Make sure we scale the data up to the original real range,
767 * if appropriate.
768 */
769 if (get_volume_data_type(volume) != in_ptr->file_data_type)
770 {
771 set_volume_real_range(volume, original_min_voxel, original_max_voxel);
772 }
773
774 return FALSE;
775 }
776 else
777 {
778 return TRUE; /* More work to do. */
779 }
780 }
781