1 /* ----------------------------- MNI Header -----------------------------------
2 @NAME : minclookup
3 @INPUT : argc, argv - command line arguments
4 @OUTPUT : (none)
5 @RETURNS : status
6 @DESCRIPTION: Program to perform lookup table operations on a scalar minc
7 file.
8 @METHOD :
9 @GLOBALS :
10 @CALLS :
11 @CREATED : December 6, 1994 (Peter Neelin)
12 @MODIFIED :
13 * $Log: minclookup.c,v $
14 * Revision 6.10 2008-01-17 02:33:02 rotor
15 * * removed all rcsids
16 * * removed a bunch of ^L's that somehow crept in
17 * * removed old (and outdated) BUGS file
18 *
19 * Revision 6.9 2008/01/12 19:08:15 stever
20 * Add __attribute__ ((unused)) to all rcsid variables.
21 *
22 * Revision 6.8 2007/12/11 12:43:01 rotor
23 * * added static to all global variables in main programs to avoid linking
24 * problems with libraries (compress in mincconvert and libz for example)
25 *
26 * Revision 6.7 2005/08/26 21:07:17 bert
27 * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used
28 *
29 * Revision 6.6 2004/12/03 21:57:08 bert
30 * Include config.h
31 *
32 * Revision 6.5 2004/11/01 22:38:38 bert
33 * Eliminate all references to minc_def.h
34 *
35 * Revision 6.4 2004/05/20 21:52:08 bert
36 * Revised man pages
37 *
38 * Revision 6.3 2001/04/24 13:38:43 neelin
39 * Replaced NC_NAT with MI_ORIGINAL_TYPE.
40 *
41 * Revision 6.2 2001/04/17 18:40:20 neelin
42 * Modifications to work with NetCDF 3.x
43 * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints).
44 * Changed NC_UNSPECIFIED to NC_NAT.
45 * A few fixes to the configure script.
46 *
47 * Revision 6.1 1999/10/19 14:45:24 neelin
48 * Fixed Log subsitutions for CVS
49 *
50 * Revision 6.0 1997/09/12 13:24:13 neelin
51 * Release of minc version 0.6
52 *
53 * Revision 5.0 1997/08/21 13:25:12 neelin
54 * Release of minc version 0.5
55 *
56 * Revision 4.0 1997/05/07 20:01:47 neelin
57 * Release of minc version 0.4
58 *
59 * Revision 3.2 1996/07/10 16:58:37 neelin
60 * Added -lut_string option and added special handling of duplicated first
61 * or last entries.
62 *
63 * Revision 3.1 1996/07/10 14:38:03 neelin
64 * Added options to set output file type, sign and range.
65 *
66 * Revision 3.0 1995/05/15 19:32:39 neelin
67 * Release of minc version 0.3
68 *
69 * Revision 1.6 1995/03/21 14:26:42 neelin
70 * changed usage message.
71 *
72 * Revision 1.5 1995/03/21 14:00:10 neelin
73 * Changed calls to voxel_loop routines.
74 *
75 * Revision 1.4 1995/02/08 19:31:47 neelin
76 * Moved ARGSUSED statements for irix 5 lint.
77 *
78 * Revision 1.3 1994/12/14 09:22:30 neelin
79 * Removed debugging code.
80 *
81 * Revision 1.2 94/12/13 16:34:02 neelin
82 * Added sorting of lookup file.
83 *
84 * Revision 1.1 94/12/09 15:31:00 neelin
85 * Initial revision
86 *
87 ---------------------------------------------------------------------------- */
88
89 #if HAVE_CONFIG_H
90 #include "config.h"
91 #endif
92 #include <stdlib.h>
93 #include <stdio.h>
94 #include <string.h>
95 #include <ctype.h>
96 #include <math.h>
97 #include <float.h>
98 #include <minc.h>
99 #include <ParseArgv.h>
100 #include <time_stamp.h>
101 #include <voxel_loop.h>
102
103 #ifndef TRUE
104 # define TRUE 1
105 # define FALSE 0
106 #endif
107
108 #define DEFAULT_RANGE DBL_MAX
109 #define NCOPTS_DEFAULT NC_VERBOSE | NC_FATAL
110
111 /* Types */
112 typedef enum {LU_TABLE, LU_GRAY, LU_HOTMETAL, LU_SPECTRAL} Lookup_Type;
113
114 /* Lookup table structure */
115 typedef struct {
116 int nentries;
117 int vector_length;
118 double *table;
119 int free_data;
120 } Lookup_Table;
121
122 /* Structure for lookup information */
123 typedef struct {
124 Lookup_Table *lookup_table;
125 double *null_value;
126 int invert;
127 int discrete;
128 double range[2];
129 } Lookup_Data;
130
131 /* Structure for sorting the lookup table */
132 typedef struct {
133 double key;
134 int index;
135 } Sort_Key;
136
137 /* Function prototypes */
138 static Lookup_Table *read_lookup_table(char *lookup_file, char *lookup_string);
139 static double *get_values_from_string(char *string, int array_size,
140 double *array, int *nread);
141 static double *get_null_value(int vector_length, char *null_value_string);
142 static void get_full_range(int mincid, double lookup_range[2]);
143 static void do_lookup(void *caller_data, long num_voxels,
144 int input_num_buffers, int input_vector_length,
145 double *input_data[],
146 int output_num_buffers, int output_vector_length,
147 double *output_data[], Loop_Info *loop_info);
148 static void lookup_in_table(double index, Lookup_Table *lookup_table,
149 int discrete_values, double null_value[],
150 double output_value[]);
151 static char *get_next_line(char *line, int linelen, FILE *fp, char **string);
152 static int sorting_function(const void *value1, const void *value2);
153
154 /* Lookup tables */
155 static double gray_lookup_values[] = {
156 0.0, 0.0, 0.0, 0.0,
157 1.0, 1.0, 1.0, 1.0
158 };
159 static Lookup_Table gray_lookup = {
160 sizeof(gray_lookup_values)/sizeof(gray_lookup_values[0])/4,
161 3,
162 gray_lookup_values,
163 FALSE
164 };
165
166 static double spectral_lookup_values[] = {
167 0.00, 0.0000,0.0000,0.0000,
168 0.05, 0.4667,0.0000,0.5333,
169 0.10, 0.5333,0.0000,0.6000,
170 0.15, 0.0000,0.0000,0.6667,
171 0.20, 0.0000,0.0000,0.8667,
172 0.25, 0.0000,0.4667,0.8667,
173 0.30, 0.0000,0.6000,0.8667,
174 0.35, 0.0000,0.6667,0.6667,
175 0.40, 0.0000,0.6667,0.5333,
176 0.45, 0.0000,0.6000,0.0000,
177 0.50, 0.0000,0.7333,0.0000,
178 0.55, 0.0000,0.8667,0.0000,
179 0.60, 0.0000,1.0000,0.0000,
180 0.65, 0.7333,1.0000,0.0000,
181 0.70, 0.9333,0.9333,0.0000,
182 0.75, 1.0000,0.8000,0.0000,
183 0.80, 1.0000,0.6000,0.0000,
184 0.85, 1.0000,0.0000,0.0000,
185 0.90, 0.8667,0.0000,0.0000,
186 0.95, 0.8000,0.0000,0.0000,
187 1.00, 0.8000,0.8000,0.8000
188 };
189 static Lookup_Table spectral_lookup = {
190 sizeof(spectral_lookup_values)/sizeof(spectral_lookup_values[0])/4,
191 3,
192 spectral_lookup_values,
193 FALSE
194 };
195
196 static double hotmetal_lookup_values[] = {
197 0.00, 0.0, 0.0, 0.0,
198 0.25, 0.5, 0.0, 0.0,
199 0.50, 1.0, 0.5, 0.0,
200 0.75, 1.0, 1.0, 0.5,
201 1.00, 1.0, 1.0, 1.0
202 };
203 static Lookup_Table hotmetal_lookup = {
204 sizeof(hotmetal_lookup_values)/sizeof(hotmetal_lookup_values[0])/4,
205 3,
206 hotmetal_lookup_values,
207 FALSE
208 };
209
210 /* Argument variables */
211 static int clobber = FALSE;
212 static int verbose = TRUE;
213 static nc_type datatype = MI_ORIGINAL_TYPE;
214 static int is_signed = FALSE;
215 static double valid_range[2] = {0.0, 0.0};
216 static int buffer_size = 10 * 1024;
217 static char *lookup_file = NULL;
218 static char *lookup_string = NULL;
219 static Lookup_Type lookup_type = LU_GRAY;
220 static int invert_table = FALSE;
221 static double lookup_range[2] = {DEFAULT_RANGE, DEFAULT_RANGE};
222 static double lookup_min = DEFAULT_RANGE;
223 static double lookup_max = DEFAULT_RANGE;
224 static int discrete_lookup = FALSE;
225 static char *null_value_string = NULL;
226 #if MINC2
227 static int minc2_format = FALSE;
228 #endif /* MINC2 */
229
230 /* Argument table */
231 ArgvInfo argTable[] = {
232 #if MINC2
233 {"-2", ARGV_CONSTANT, (char *) TRUE, (char *) &minc2_format,
234 "Produce a MINC 2.0 format output file"},
235 #endif /* MINC2 */
236 {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber,
237 "Overwrite existing file."},
238 {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber,
239 "Don't overwrite existing file (default)."},
240 {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose,
241 "Print out log messages (default)."},
242 {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose,
243 "Do not print out log messages."},
244 {"-buffer_size", ARGV_INT, (char *) 1, (char *) &buffer_size,
245 "Set the internal buffer size (in kb)."},
246 {"-filetype", ARGV_CONSTANT, (char *) MI_ORIGINAL_TYPE, (char *) &datatype,
247 "Use data type of first file (default)."},
248 {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &datatype,
249 "Write out byte data."},
250 {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &datatype,
251 "Write out short integer data."},
252 {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype,
253 "Write out 32-bit integer data."},
254 {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype,
255 "Superseded by -int."},
256 {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &datatype,
257 "Write out single-precision floating-point data."},
258 {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &datatype,
259 "Write out double-precision floating-point data."},
260 {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &is_signed,
261 "Write signed integer data."},
262 {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &is_signed,
263 "Write unsigned integer data (default if type specified)."},
264 {"-valid_range", ARGV_FLOAT, (char *) 2, (char *) valid_range,
265 "Valid range for output data."},
266 {"-gray", ARGV_CONSTANT, (char *) LU_GRAY, (char *) &lookup_type,
267 "Use a grayscale lookup table (default)."},
268 {"-grey", ARGV_CONSTANT, (char *) LU_GRAY, (char *) &lookup_type,
269 "Use a grayscale lookup table."},
270 {"-hotmetal", ARGV_CONSTANT, (char *) LU_HOTMETAL, (char *) &lookup_type,
271 "Use a hot metal lookup table."},
272 {"-spectral", ARGV_CONSTANT, (char *) LU_SPECTRAL, (char *) &lookup_type,
273 "Use a spectral lookup table."},
274 {"-invert", ARGV_CONSTANT, (char *) TRUE, (char *) &invert_table,
275 "Invert the lookup table (only applies to -continuous)."},
276 {"-noinvert", ARGV_CONSTANT, (char *) FALSE, (char *) &invert_table,
277 "Don't invert the lookup table."},
278 {"-range", ARGV_FLOAT, (char *) 2, (char *) lookup_range,
279 "Min and max for lookup table (default from file)."},
280 {"-minimum", ARGV_FLOAT, (char *) 1, (char *) &lookup_min,
281 "Minimum for continuous lookup table."},
282 {"-maximum", ARGV_FLOAT, (char *) 1, (char *) &lookup_max,
283 "Maximum for continuous lookup table."},
284 {"-lookup_table", ARGV_STRING, (char *) 1, (char *) &lookup_file,
285 "File containing the lookup table (use - for stdin)."},
286 {"-lut_string", ARGV_STRING, (char *) 1, (char *) &lookup_string,
287 "String containing the lookup table, with \";\" to separate lines."},
288 {"-discrete", ARGV_CONSTANT, (char *) TRUE, (char *) &discrete_lookup,
289 "Lookup table has discrete (integer) entries - range is ignored."},
290 {"-continuous", ARGV_CONSTANT, (char *) FALSE, (char *) &discrete_lookup,
291 "Lookup table is continuous from 0 to 1 (default)."},
292 {"-null_value", ARGV_STRING, (char *) 1, (char *) &null_value_string,
293 "Specify a vector value for entries missing from a discrete lookup."},
294 {NULL, ARGV_END, NULL, NULL, NULL}
295 };
296 /* Main program */
297
main(int argc,char * argv[])298 int main(int argc, char *argv[])
299 {
300 char *infile, *outfile;
301 char *arg_string;
302 Loop_Options *loop_options;
303 int inmincid;
304 Lookup_Data lookup_data;
305
306 /* Save time stamp and args */
307 arg_string = time_stamp(argc, argv);
308
309 /* Get arguments */
310 if (ParseArgv(&argc, argv, argTable, 0) || (argc != 3)) {
311 (void) fprintf(stderr, "\nUsage: %s [options] <in.mnc> <out.mnc>\n",
312 argv[0]);
313 (void) fprintf(stderr, " %s -help\n\n",
314 argv[0]);
315 exit(EXIT_FAILURE);
316 }
317 infile = argv[1];
318 outfile = argv[2];
319
320 /* Get the appropriate lookup table */
321 if ((lookup_file != NULL) || (lookup_string != NULL)) {
322 lookup_data.lookup_table = read_lookup_table(lookup_file, lookup_string);
323 }
324 else {
325 switch (lookup_type) {
326 case LU_GRAY:
327 lookup_data.lookup_table = &gray_lookup;
328 break;
329 case LU_HOTMETAL:
330 lookup_data.lookup_table = &hotmetal_lookup;
331 break;
332 case LU_SPECTRAL:
333 lookup_data.lookup_table = &spectral_lookup;
334 break;
335 }
336 }
337
338 /* Get the null value */
339 lookup_data.null_value =
340 get_null_value(lookup_data.lookup_table->vector_length,
341 null_value_string);
342
343 /* Open the input file and get the range */
344 inmincid = miopen(infile, NC_NOWRITE);
345 if (!discrete_lookup && (lookup_range[0] == DEFAULT_RANGE)) {
346 get_full_range(inmincid, lookup_range);
347 }
348 if (lookup_min != DEFAULT_RANGE)
349 lookup_range[0] = lookup_min;
350 if (lookup_max != DEFAULT_RANGE)
351 lookup_range[1] = lookup_max;
352
353 /* Set up lookup information */
354 lookup_data.invert = invert_table;
355 lookup_data.range[0] = lookup_range[0];
356 lookup_data.range[1] = lookup_range[1];
357 lookup_data.discrete = discrete_lookup;
358
359 /* Set up looping options */
360 loop_options = create_loop_options();
361 set_loop_clobber(loop_options, clobber);
362 set_loop_verbose(loop_options, verbose);
363 #if MINC2
364 set_loop_v2format(loop_options, minc2_format);
365 #endif /* MINC2 */
366 set_loop_datatype(loop_options, datatype, is_signed,
367 valid_range[0], valid_range[1]);
368 set_loop_convert_input_to_scalar(loop_options, TRUE);
369 set_loop_output_vector_size(loop_options,
370 lookup_data.lookup_table->vector_length);
371 set_loop_buffer_size(loop_options, (long) buffer_size * 1024);
372 set_loop_first_input_mincid(loop_options, inmincid);
373
374 /* Do loop */
375 voxel_loop(1, &infile, 1, &outfile, arg_string, loop_options,
376 do_lookup, (void *) &lookup_data);
377
378 /* Free stuff */
379 if (lookup_data.null_value != NULL) free(lookup_data.null_value);
380 if (lookup_data.lookup_table->free_data) {
381 free(lookup_data.lookup_table->table);
382 free(lookup_data.lookup_table);
383 }
384
385 exit(EXIT_SUCCESS);
386 }
387
388 /* ----------------------------- MNI Header -----------------------------------
389 @NAME : read_lookup_table
390 @INPUT : lookup_filename - name of file from which to read lookup table
391 ("-" means use stdin)
392 lookup_string - string from which to read lookup table if
393 lookup_filename is NULL
394 @OUTPUT : (nothing)
395 @RETURNS : Pointer to lookup table
396 @DESCRIPTION: Routine to read in a lookup table from a file or stdin. If
397 the filename is NULL, then data is read from the lookup_string.
398 @METHOD :
399 @GLOBALS :
400 @CALLS :
401 @CREATED : December 8, 1994 (Peter Neelin)
402 @MODIFIED :
403 ---------------------------------------------------------------------------- */
read_lookup_table(char * lookup_filename,char * lookup_string)404 static Lookup_Table *read_lookup_table(char *lookup_filename,
405 char *lookup_string)
406 {
407 Lookup_Table *lookup_table;
408 FILE *fp;
409 double *table, *row, *new_table;
410 int nentries, table_nvalues, nvalues, ivalue, ientry;
411 char line[1000];
412 Sort_Key *sort_table;
413 int need_sort;
414 int old_offset, new_offset;
415 char *lut_string;
416
417 /* Check for null file name */
418 if ((lookup_filename == NULL) && (lookup_string == NULL)) return NULL;
419
420 /* Open the file */
421 if (lookup_filename == NULL) {
422 fp = NULL;
423 }
424 else if (strcmp(lookup_filename, "-") == 0) {
425 fp = stdin;
426 }
427 else {
428 fp = fopen(lookup_filename, "r");
429 if (fp == NULL) {
430 (void) fprintf(stderr, "Unable to open lookup file \"%s\".\n",
431 lookup_filename);
432 exit(EXIT_FAILURE);
433 }
434 }
435
436 /* Read in the table */
437
438 /* Read the first line and get the vector length from that*/
439 nentries = 0;
440 lut_string = lookup_string;
441 if (get_next_line(line, sizeof(line), fp, &lut_string) == NULL) {
442 if (fp != NULL)
443 (void) fprintf(stderr, "Lookup file %s is empty.\n", lookup_filename);
444 else
445 (void) fprintf(stderr, "Lookup string is empty.\n");
446 exit(EXIT_FAILURE);
447 }
448 row = get_values_from_string(line, 0, NULL, &table_nvalues);
449 if (table_nvalues < 2) {
450 (void) fprintf(stderr, "First line has fewer than 2 values.\n");
451 if (row != NULL) free(row);
452 exit(EXIT_FAILURE);
453 }
454 table = malloc(sizeof(*table) * table_nvalues);
455 for (ivalue=0; ivalue < table_nvalues; ivalue++)
456 table[ivalue] = row[ivalue];
457 nentries++;
458 need_sort = FALSE;
459 while (get_next_line(line, sizeof(line), fp, &lut_string) != NULL) {
460 (void) get_values_from_string(line, table_nvalues, row, &nvalues);
461 if (nvalues != table_nvalues) {
462 (void) fprintf(stderr,
463 "Wrong number of values on line %d.\n",
464 nentries+1);
465 free(row);
466 free(table);
467 exit(EXIT_FAILURE);
468 }
469 table = realloc(table, sizeof(*table) * table_nvalues * (nentries+1));
470 for (ivalue=0; ivalue < table_nvalues; ivalue++) {
471 table[ivalue + nentries*table_nvalues] = row[ivalue];
472 }
473 nentries++;
474
475 /* Check for need to sort */
476 if (table[(nentries-2)*table_nvalues] >
477 table[(nentries-1)*table_nvalues]) {
478 need_sort = TRUE;
479 }
480 }
481
482 /* Close the input file */
483 if (fp != NULL) {
484 (void) fclose(fp);
485 }
486
487 /* Do sorting if needed */
488 if (need_sort) {
489
490 /* Set up sorting table */
491 sort_table = malloc(sizeof(*sort_table) * nentries);
492 for (ientry=0; ientry < nentries; ientry++) {
493 sort_table[ientry].key = table[ientry*table_nvalues];
494 sort_table[ientry].index = ientry;
495 }
496
497 /* Sort the sorting table */
498 qsort((void *) sort_table, nentries, sizeof(*sort_table),
499 sorting_function);
500
501 /* Copy the table */
502 new_table = malloc(sizeof(*table) * table_nvalues * nentries);
503 for (ientry=0; ientry < nentries; ientry++) {
504 new_offset = ientry * table_nvalues;
505 old_offset = sort_table[ientry].index * table_nvalues;
506 for (ivalue=0; ivalue < table_nvalues; ivalue++) {
507 new_table[new_offset + ivalue] = table[old_offset + ivalue];
508 }
509 }
510 free(table);
511 table = new_table;
512 free(sort_table);
513 }
514
515 /* Allocate space for the lookup table and set initial values */
516 lookup_table = malloc(sizeof(*lookup_table));
517 lookup_table->free_data = TRUE;
518 lookup_table->nentries = nentries;
519 lookup_table->vector_length = table_nvalues - 1;
520 lookup_table->table = table;
521
522 /* Return the lookup table */
523 return lookup_table;
524 }
525
526 /* ----------------------------- MNI Header -----------------------------------
527 @NAME : get_next_line
528 @INPUT : linelen - length of line to read in
529 fp - file pointer or NULL if data should be read from a string
530 string - pointer string containing data
531 @OUTPUT : line - line that has been read in
532 string - pointer is advanced to character following ";"
533 @RETURNS : pointer to line or NULL if end of data occurs
534 @DESCRIPTION: Routine to get the next line from a file or from a string
535 that uses ";" as a line separator.
536 @METHOD :
537 @GLOBALS :
538 @CALLS :
539 @CREATED : July 10, 1996 (Peter Neelin)
540 @MODIFIED :
541 ---------------------------------------------------------------------------- */
542 #define LOOKUP_LINE_SEPARATOR ';'
get_next_line(char * line,int linelen,FILE * fp,char ** string)543 static char *get_next_line(char *line, int linelen, FILE *fp, char **string)
544 {
545 int count;
546
547 /* Read from the file if appropriate */
548 if (fp != NULL) {
549 return fgets(line, linelen, fp);
550 }
551
552 /* Otherwise search the string for the ";", copying characters */
553 else {
554
555 /* Check for an empty string */
556 if (**string == '\0') return NULL;
557
558 /* Loop over characters */
559 count = 0;
560 while ((**string != '\0') && (**string != LOOKUP_LINE_SEPARATOR)) {
561 if (count < linelen-1) {
562 line[count] = **string;
563 count++;
564 }
565 (*string)++;
566 }
567
568 /* Terminate the line and move past the ";" */
569 line[count] = '\0';
570 if (**string != '\0')
571 (*string)++;
572
573 /* Return the line */
574 return line;
575
576 }
577
578 }
579
580 /* ----------------------------- MNI Header -----------------------------------
581 @NAME : sorting_function
582 @INPUT : value1 - pointer to first value
583 value2 - pointer to second value
584 @OUTPUT : (nothing)
585 @RETURNS : -1, 0 or 1 for value1 <, ==, > value2
586 @DESCRIPTION: Routine to compare two values for sorting. The value is a
587 pointer to a structure that has a double precision primary
588 key. If that is equal then an integer secondary key is used.
589 @METHOD :
590 @GLOBALS :
591 @CALLS :
592 @CREATED : December 8, 1994 (Peter Neelin)
593 @MODIFIED :
594 ---------------------------------------------------------------------------- */
sorting_function(const void * value1,const void * value2)595 static int sorting_function(const void *value1, const void *value2)
596 {
597 Sort_Key *key1, *key2;
598
599 key1 = (Sort_Key *) value1;
600 key2 = (Sort_Key *) value2;
601
602 if (key1->key == key2->key) {
603 if (key1->index == key2->index) return 0;
604 else if (key1->index < key2->index) return -1;
605 else return 1;
606 }
607 else if (key1->key < key2->key) return -1;
608 else return 1;
609
610 }
611
612 /* ----------------------------- MNI Header -----------------------------------
613 @NAME : get_values_from_string
614 @INPUT : string - string containing values
615 array_size - maximum number of values (<=0 means allocate a new
616 array )
617 @OUTPUT : array - array into which values are written (can be NULL if
618 array_size <= 0)
619 nread - number of values read
620 @RETURNS : Pointer to array of values
621 @DESCRIPTION: Routine to convert a string to an array of values
622 (floating point). If an existing array is passed in
623 (array_size > 0), then up to array_size values are copied into
624 it. Otherwise, a new array is created. Normally, the number of
625 values read is returned - if an error occurs (non-numeric string
626 for example), then zero is returned.
627 @METHOD :
628 @GLOBALS :
629 @CALLS :
630 @CREATED : December 8, 1994 (Peter Neelin)
631 @MODIFIED :
632 ---------------------------------------------------------------------------- */
get_values_from_string(char * string,int array_size,double * array,int * nread)633 static double *get_values_from_string(char *string, int array_size,
634 double *array, int *nread)
635 {
636 #define VECTOR_SEPARATOR ','
637
638 char *cur, *prev, *end;
639 int num_read, num_alloc;
640 double dvalue;
641
642 /* Set up variables */
643 num_read = 0;
644 if (array_size <= 0) {
645 num_alloc = 0;
646 array = NULL;
647 }
648 else {
649 num_alloc = array_size;
650 }
651
652 /* Skip leading white space */
653 end = string + strlen(string);
654 cur = string;
655 while (isspace(*cur)) cur++;
656
657 /* Loop through string looking for doubles */
658 while (cur!=end) {
659
660 /* Get double */
661 prev = cur;
662 dvalue = strtod(prev, &cur);
663 if (cur == prev) {
664 *nread = 0;
665 if (array_size <= 0 && array != NULL) {
666 free(array);
667 }
668 return NULL;
669 }
670
671 /* Add the value to the list */
672 num_read++;
673 if (num_read > num_alloc) {
674 if (array_size <= 0) {
675 num_alloc += 1;
676 if (array == NULL) {
677 array = malloc(num_alloc * sizeof(*array));
678 }
679 else {
680 array = realloc(array, num_alloc * sizeof(*array));
681 }
682 }
683 else {
684 *nread = num_read;
685 return array;
686 }
687 }
688 array[num_read-1] = dvalue;
689
690 /* Skip any spaces */
691 while (isspace(*cur)) cur++;
692
693 /* Skip an optional comma */
694 if (*cur == VECTOR_SEPARATOR) cur++;
695
696 }
697
698 *nread = num_read;
699 return array;
700 }
701
702 /* ----------------------------- MNI Header -----------------------------------
703 @NAME : get_null_value
704 @INPUT : vector_length - desired vector length
705 string - string from which we should get null values
706 @OUTPUT : (nothing)
707 @RETURNS : Pointer to array of values
708 @DESCRIPTION: Routine to convert a string to an array of values
709 to be used as a null value for the lookup table. Returns
710 NULL if string is NULL
711 @METHOD :
712 @GLOBALS :
713 @CALLS :
714 @CREATED : December 8, 1994 (Peter Neelin)
715 @MODIFIED :
716 ---------------------------------------------------------------------------- */
get_null_value(int vector_length,char * string)717 static double *get_null_value(int vector_length, char *string)
718 {
719 int num_read;
720 double *values;
721
722 /* Check for NULL string */
723 if (string == NULL) return NULL;
724
725 /* Read values */
726 values = get_values_from_string(string, 0, NULL, &num_read);
727
728 /* Check the number of values read */
729 if (num_read != vector_length) {
730 if (values != NULL) free(values);
731 (void) fprintf(stderr,
732 "Null value does not match lookup table (%d values).\n",
733 num_read);
734 exit(EXIT_FAILURE);
735 }
736
737 return values;
738 }
739
740 /* ----------------------------- MNI Header -----------------------------------
741 @NAME : get_full_range
742 @INPUT : mincid - id of the input minc file
743 @OUTPUT : range - 2-value array giving real range of input values
744 @RETURNS : (nothing)
745 @DESCRIPTION: Routine to get the full real range of an input file.
746 @METHOD :
747 @GLOBALS :
748 @CALLS :
749 @CREATED : December 8, 1994 (Peter Neelin)
750 @MODIFIED :
751 ---------------------------------------------------------------------------- */
get_full_range(int mincid,double range[2])752 static void get_full_range(int mincid, double range[2])
753 {
754 char *string;
755 int varid;
756 int ivar, idim, ndims;
757 int dim[MAX_VAR_DIMS];
758 double sign, *extreme, *values;
759 long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS], num_values;
760 long ivalue, length;
761
762 /* Loop over image-min and image-max variables */
763 range[0] = 0.0;
764 range[1] = 1.0;
765 for (ivar=0; ivar < 2; ivar++) {
766 if (ivar==0) {
767 string = MIimagemin;
768 sign = -1.0;
769 extreme = &range[0];
770 }
771 else {
772 string = MIimagemax;
773 sign = +1.0;
774 extreme = &range[1];
775 }
776 ncopts = 0;
777 varid = ncvarid(mincid, string);
778 ncopts = NCOPTS_DEFAULT;
779 if (varid != MI_ERROR) {
780 (void) ncvarinq(mincid, varid, NULL, NULL, &ndims, dim, NULL);
781 num_values = 1;
782 for (idim=0; idim < ndims; idim++) {
783 (void) ncdiminq(mincid, dim[idim], NULL, &length);
784 start[idim] = 0;
785 count[idim] = length;
786 num_values *= length;
787 }
788 if (num_values > 0) {
789 values = malloc(num_values * sizeof(*values));
790 (void) mivarget(mincid, varid, start, count,
791 NC_DOUBLE, NULL, values);
792 *extreme = values[0];
793 for (ivalue=0; ivalue < num_values; ivalue++) {
794 if ((values[ivalue] * sign) > (*extreme * sign))
795 *extreme = values[ivalue];
796 }
797 free(values);
798 }
799
800 } /* If variable is found */
801
802 } /* Loop over image-min/max */
803
804
805 return;
806 }
807
808 /* ----------------------------- MNI Header -----------------------------------
809 @NAME : do_lookup
810 @INPUT : caller_data - pointer to structure containing lookup info
811 num_voxels - number of voxels to work on
812 input_num_buffers - number of input buffers
813 input_vector_length - length of input vector dimension
814 input_data - vector of pointers to input buffer data
815 output_num_buffers - number of output buffers
816 output_vector_length - length of output vector dimension
817 start - vector specifying start of hyperslab (not used)
818 count - vector specifying count of hyperslab (not used)
819 @OUTPUT : output_data - vector of pointers to output buffer data
820 @RETURNS : (nothing)
821 @DESCRIPTION: Routine to loop through an array of voxels and do a lookup
822 table conversion on them.
823 @METHOD :
824 @GLOBALS :
825 @CALLS :
826 @CREATED : December 8, 1994 (Peter Neelin)
827 @MODIFIED :
828 ---------------------------------------------------------------------------- */
do_lookup(void * caller_data,long num_voxels,int input_num_buffers,int input_vector_length,double * input_data[],int output_num_buffers,int output_vector_length,double * output_data[],Loop_Info * loop_info)829 static void do_lookup(void *caller_data, long num_voxels,
830 int input_num_buffers, int input_vector_length,
831 double *input_data[],
832 int output_num_buffers, int output_vector_length,
833 double *output_data[], Loop_Info *loop_info)
834 /* ARGSUSED */
835 {
836 Lookup_Data *lookup_data;
837 long ivoxel;
838 double lookup_value, scale, offset, denom;
839
840 /* Get pointer to lookup info */
841 lookup_data = (Lookup_Data *) caller_data;
842
843 /* Check that values correspond */
844 if ((input_num_buffers != 1) || (output_num_buffers != 1) ||
845 (input_vector_length != 1) ||
846 (output_vector_length != lookup_data->lookup_table->vector_length)) {
847 (void) fprintf(stderr, "Bad internal values.\n");
848 exit(EXIT_FAILURE);
849 }
850
851 /* Calculate a scale and offset for input values */
852 if (lookup_data->discrete) {
853 scale = 1.0;
854 offset = 0.0;
855 }
856 else {
857 denom = (lookup_data->range[1] - lookup_data->range[0]);
858 if (denom == 0.0)
859 scale = 0.0;
860 else
861 scale = 1.0 / denom;
862 if (!lookup_data->invert) {
863 offset = -lookup_data->range[0] * scale;
864 }
865 else {
866 scale = -scale;
867 offset = -lookup_data->range[1] * scale;
868 }
869 }
870
871 /* Loop through the voxels */
872 for (ivoxel=0; ivoxel < num_voxels; ivoxel++) {
873
874 /* Convert input to a lookup value */
875 lookup_value = input_data[0][ivoxel] * scale + offset;
876
877 /* Look it up */
878 lookup_in_table(lookup_value, lookup_data->lookup_table,
879 lookup_data->discrete, lookup_data->null_value,
880 &output_data[0][ivoxel*output_vector_length]);
881 }
882
883 return;
884 }
885
886
887 /* ----------------------------- MNI Header -----------------------------------
888 @NAME : lookup_in_table
889 @INPUT : index - value to look up in table
890 lookup_table - the lookup table (big surprise!)
891 discrete_values - flag indicating whether the table should
892 be considered continuous in the range 0 to 1 (FALSE) or
893 discrete, with integer values that should be rounded (TRUE).
894 If the table is continuous, then interpolation between
895 table entries is done (the first and last values propagate
896 beyond the bounds). If the table is discrete, then the
897 null_value is used for indices that are not found.
898 null_value - array specifying the null values to use if
899 the index is not found in the table. Only applies to
900 discrete valued tables (may be NULL otherwise).
901 @OUTPUT : output_value - vector of output values.
902 @RETURNS : (nothing)
903 @DESCRIPTION: Routine to look up a value in the table.
904 @METHOD :
905 @GLOBALS :
906 @CALLS :
907 @CREATED : December 8, 1994 (Peter Neelin)
908 @MODIFIED :
909 ---------------------------------------------------------------------------- */
lookup_in_table(double index,Lookup_Table * lookup_table,int discrete_values,double null_value[],double output_value[])910 static void lookup_in_table(double index, Lookup_Table *lookup_table,
911 int discrete_values, double null_value[],
912 double output_value[])
913 {
914 int vector_length, nentries;
915 int start, length, mid;
916 int offset, offset1, offset2, ivalue;
917 double value1, value2, *result, frac, rfrac, denom;
918
919 /* Check for bad lookup table */
920 nentries = lookup_table->nentries;
921 vector_length = lookup_table->vector_length;
922 if ((nentries < 1) || (vector_length < 1)) {
923 (void) fprintf(stderr, "Bad table size %d x %d\n",
924 nentries, vector_length);
925 exit(EXIT_FAILURE);
926 }
927
928 /* Round values if needed */
929 if (discrete_values) index = rint(index);
930
931 /* Search the table for the value */
932 start = 0;
933 length = nentries;
934 while (length > 1) {
935 mid = start + length / 2;
936 offset = mid*(vector_length+1);
937 if (index < lookup_table->table[offset]) {
938 length = mid - start;
939 }
940 else {
941 length = start + length - mid;
942 start = mid;
943 }
944 }
945
946 /* Add a special check for the end of the table */
947 if (nentries > 1) {
948 offset1 = vector_length+1;
949 offset2 = (nentries-2) * (vector_length+1);
950 if ((start == 0) && (index == lookup_table->table[offset1]))
951 start = 1;
952 else if ((start == nentries-1) &&
953 (index == lookup_table->table[offset2]))
954 start = nentries-2;
955 }
956
957 /* Save the value */
958 offset = start*(vector_length+1);
959 if (discrete_values) {
960 if (index == lookup_table->table[offset])
961 result = &lookup_table->table[offset+1];
962 else
963 result = null_value;
964 for (ivalue=0; ivalue < vector_length; ivalue++) {
965 if (result != NULL)
966 output_value[ivalue] = result[ivalue];
967 else
968 output_value[ivalue] = 0.0;
969 }
970 }
971 else {
972 offset1 = offset;
973 if (start < nentries - 1)
974 offset2 = offset + vector_length + 1;
975 else
976 offset2 = offset;
977 value1 = lookup_table->table[offset1];
978 value2 = lookup_table->table[offset2];
979 denom = value2 - value1;
980 if (denom != 0.0)
981 frac = (index - value1) / denom;
982 else
983 frac = 0.0;
984 if (frac < 0.0) frac = 0.0;
985 if (frac > 1.0) frac = 1.0;
986 rfrac = 1.0 - frac;
987 for (ivalue=0; ivalue < vector_length; ivalue++) {
988 output_value[ivalue] =
989 rfrac * lookup_table->table[offset1 + 1 + ivalue] +
990 frac * lookup_table->table[offset2 + 1 + ivalue];
991 }
992 }
993 }
994
995