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