/* ----------------------------- MNI Header ----------------------------------- @NAME : scxtominc @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Converts scanditronix format files to a minc format file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 11, 1993 (Peter Neelin) @MODIFIED : * $Log: scxtominc.c,v $ * Revision 6.4 2008-01-17 02:33:02 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.3 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.2 1999/11/09 13:34:48 neelin * Year 2000 fix for date stored in minc file. * * Revision 6.1 1999/10/29 17:52:07 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:31 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:32 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:13 neelin * Release of minc version 0.4 * * Revision 3.1 1996/01/04 13:41:48 neelin * Added missing exit when user specifies a bad slice range. * * Revision 3.0 1995/05/15 19:31:05 neelin * Release of minc version 0.3 * * Revision 2.5 1995/02/09 14:11:43 neelin * Mods to make irix 5 lint happy. * * Revision 2.4 1995/02/08 19:31:47 neelin * Moved ARGSUSED statements for irix 5 lint. * * Revision 2.3 1995/01/23 09:21:13 neelin * Changed ncclose to miclose * * Revision 2.2 95/01/23 08:57:37 neelin * Changed nccreate to micreate. * * Revision 2.1 95/01/09 15:17:37 neelin * Added code to check for generic reconstructions. * * Revision 2.0 94/09/28 10:33:50 neelin * Release of minc version 0.2 * * Revision 1.12 94/09/28 10:33:30 neelin * Pre-release * * Revision 1.11 94/09/28 08:23:58 neelin * Find max and min pixel values for both bytes and shorts. * (Shorts are rescaled to full range). * * Revision 1.10 94/05/31 07:56:42 neelin * Added insertblood.c to optionally insert blood data into minc file. * * Revision 1.9 93/11/17 12:21:55 neelin * Changed default to -noclobber. * * Revision 1.8 93/08/11 15:27:34 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include "isotope_list.h" /* Macro definitions */ #undef MALLOC #undef REALLOC #undef FREE #define MALLOC( n_items, type ) \ ( (void *) malloc( (size_t) (n_items) * sizeof(type) ) ) #define REALLOC( ptr, n_items, type ) \ ( (void *) realloc( (void *) ptr, (size_t) (n_items) * sizeof(type) ) ) #define FREE( ptr ) \ free( (void *) ptr ) /* Type declarations */ typedef struct { char name[8]; int mult; nc_type type; void *att_vector; } scx_mnem_list_type; typedef struct { int nslices; int low_slice; int high_slice; double scan_time; double time_width; double half_life; double zstart; double zstep; char isotope[16]; int image_size; int ordered_file; int *ordered_slices; char image_type[16]; } scx_file_info_type; typedef struct { int num_scx_slices; int max_nslices; int max_size; float zwidth; float xystep; float xywidth; long vmax; char img_units[16]; char patient_name[32]; char patient_sex[8]; long patient_age; char study_id[40]; char start_time[40]; long start_year; long start_month; long start_day; long start_hour; long start_minute; float start_seconds; char tracer[10]; char injection_time[40]; long injection_hour; long injection_minute; float injection_seconds; float injection_dose; scx_mnem_list_type *mnem_list; int num_mnems; int used_MNI_generic_reconstruction; } scx_general_info_type; typedef struct { double sort_key; void *sort_value; } scx_sort_type; /* Function declarations */ void usage_error(char *progname); int get_scx_file_info(int num_scx_files, char **scx_files, int slice_range[2], scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info); void sort_scx_slices(int sort_over_time, int num_scx_files, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info); int sortcmp(const void *val1, const void *val2); int setup_minc_file(int mincid, int write_byte_data, int copy_all_header, int ndims, long count[], int num_scx_files, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info, char *blood_file); int write_minc_slice(double scale, int write_byte_data, int mincid, int icvid, int ndims,long start[], long count[], short *image, int image_size, long pixel_max, float image_max, double scan_time, double time_width, double zpos); int get_scx_slice(scx_file *scx_fp, int slice_num, long *pixel_max, float *image_max, short *image, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info); double decay_correction(double scan_time, double measure_time, double start_time, double half_life); void CreateBloodStructures (int mincHandle, int bloodHandle); void FillBloodStructures (int mincHandle, int bloodHandle); /* Constants */ #define TRUE 1 #define FALSE 0 #define MAX_DIMS 4 #define HOURS_PER_DAY 24 #define MIN_PER_HOUR 60 #define SEC_PER_MIN 60 /* Scanditronix mnemonics used */ #define SCX_NCS "NCS" #define SCX_IMFM "IMFM" #define SCX_DATY "DATY" #define SCX_DATM "DATM" #define SCX_DATD "DATD" #define SCX_TIM "TIM" #define SCX_TIMH "TIMH" #define SCX_TIMM "TIMM" #define SCX_TIMS "TIMS" #define SCX_TIHU "TIHU" #define SCX_ITM "ITM" #define SCX_ITMH "ITMH" #define SCX_ITMM "ITMM" #define SCX_ITMS "ITMS" #define SCX_MTM "MTM" #define SCX_ISO "ISO" #define SCX_CLV "CLV" #define SCX_CDI "CDI" #define SCX_CSZ "CSZ" #define SCX_PXS "PXS" #define SCX_MAX "MAX" #define SCX_IMUN "IMUN" #define SCX_MAG "MAG" #define SCX_FWD "FWD" #define SCX_PNM "PNM" #define SCX_SEX "SEX" #define SCX_AGE "AGE" #define SCX_RIN "RIN" #define SCX_CAR "CAR" #define SCX_ACT "ACT" #define SCX_IMTP "IMTP" #define SCX_USC "USC" /* Scanditronix constants */ #define SCX_ACTIVITY "" #define SCX_MNI_GENERIC_RECONSTRUCTION_CODE "Generic 8" /* Main program */ int main(int argc, char *argv[]) { /* Variables for arguments */ static int write_byte_data=TRUE; static int sort_over_time=TRUE; static int clobber=FALSE; static int verbose=TRUE; static int decay_correct=TRUE; static int slice_range[2]={0, 9999}; static int copy_all_header=FALSE; static char *blood_file = NULL; /* Argument option table */ static ArgvInfo argTable[] = { {"-byte", ARGV_CONSTANT, (char *) TRUE, (char *) &write_byte_data, "Write out data as bytes (default)."}, {"-short", ARGV_CONSTANT, (char *) FALSE, (char *) &write_byte_data, "Write out data as short integers."}, {"-time", ARGV_CONSTANT, (char *) TRUE, (char *) &sort_over_time, "Keep time ordering of data (default)."}, {"-zposition", ARGV_CONSTANT, (char *) FALSE, (char *) &sort_over_time, "Sort data according to z position."}, {"-decay_correct", ARGV_CONSTANT, (char *) TRUE, (char *) &decay_correct, "Do decay correction on images (default)."}, {"-nodecay_correct", ARGV_CONSTANT, (char *) FALSE, (char *) &decay_correct, "Don't do decay correction."}, {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "List files as they are converted (default)"}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not list files as they are converted."}, {"-small_header", ARGV_CONSTANT, (char *) FALSE, (char *) ©_all_header, "Copy only basic header information (default)."}, {"-all_header", ARGV_CONSTANT, (char *) TRUE, (char *) ©_all_header, "Copy all scanditronix header information."}, {"-slices", ARGV_INT, (char *) 2, (char *) slice_range, "Range of slices to copy."}, {"-bloodfile", ARGV_STRING, (char *) 1, (char *) &blood_file, "Insert blood data from this file."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Other variables */ char *pname; char *mincfile; char **scx_files; int num_scx_files; scx_file_info_type *scx_file_info; scx_general_info_type *scx_general_info; long count[MAX_DIMS], start[MAX_DIMS]; int ndims; int mincid, icvid, varid; int islice, ifile, i, slice_num; long pixel_max; float image_max; double scale; short *image; scx_file *scx_fp; int status; char *tm_stamp; double first_z, last_z, zstep; /* Get time stamp */ tm_stamp = time_stamp(argc, argv); /* Check arguments */ pname = argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || (argc < 3)) { usage_error(pname); } /* Check the slice range */ if ((slice_range[0] < 0) || (slice_range[1] < 0) || (slice_range[1] < slice_range[0])) { (void) fprintf(stderr, "%s: Error in slice range: %d to %d.\n", pname, slice_range[0], slice_range[1]); exit(EXIT_FAILURE); } /* Get file names */ mincfile = argv[argc-1]; argv[argc-1] = NULL; /* Null-terminate scx file list */ num_scx_files = argc - 2; scx_files = &argv[1]; /* Print log message */ if (verbose) { (void) fprintf(stderr, "Reading headers.\n"); } /* Read the files to get basic information */ scx_general_info=MALLOC(1, *scx_general_info); scx_file_info = MALLOC(num_scx_files, *scx_file_info); status=get_scx_file_info(num_scx_files, scx_files, slice_range, scx_file_info, scx_general_info); if (status >= 0) { (void) fprintf(stderr, "%s: Error reading scanditronix file %s.\n", pname, scx_files[status]); exit(EXIT_FAILURE); } /* Allocate space for ordered slice list if sorting over z position */ if (!sort_over_time) { for (ifile=0; ifile1)) { ndims = 4; count[0]=num_scx_files; count[1]=scx_general_info->max_nslices; } else { ndims=3; count[0]=scx_general_info->num_scx_slices; } count[ndims-1] = count[ndims-2] = scx_general_info->max_size; mincid = micreate(mincfile, (clobber ? NC_CLOBBER : NC_NOCLOBBER)); (void) miattputstr(mincid, NC_GLOBAL, MIhistory, tm_stamp); icvid=setup_minc_file(mincid, write_byte_data, copy_all_header, ndims, count, num_scx_files, scx_file_info, scx_general_info, blood_file); if (icvid==MI_ERROR) { (void) fprintf(stderr, "%s: Error setting up minc file %s from scx file %s.\n", pname, mincfile, scx_files[0]); exit(EXIT_FAILURE); } /* Initialize minc start and count vectors */ for (i=0; imax_size * scx_general_info->max_size, short); /* Print log message */ if (verbose) { (void) fprintf(stderr, "Copying files:\n"); } /* Loop through files */ for (ifile=0; ifile1)) { start[0]=scx_file_info[ifile].ordered_file; start[1]=islice; } else if (sort_over_time) start[0]=islice; else start[0]=scx_file_info[ifile].ordered_slices[islice]; count[ndims-1] = count[ndims-2] = scx_file_info[ifile].image_size; /* Copy the slice */ slice_num = islice + scx_file_info[ifile].low_slice; if (get_scx_slice(scx_fp, slice_num, &pixel_max, &image_max, image, &scx_file_info[ifile], scx_general_info) || write_minc_slice(scale, write_byte_data, mincid, icvid, ndims, start, count, image, scx_file_info[ifile].image_size, pixel_max, image_max, scx_file_info[ifile].scan_time, scx_file_info[ifile].time_width, scx_file_info[ifile].zstep * (double) slice_num + scx_file_info[ifile].zstart)) { (void) fprintf(stderr, "%s: Error copying slice from file %s.\n", pname, scx_files[ifile]); exit(EXIT_FAILURE); } } /* End slice loop */ /* Close the scanditronix file */ scx_close(scx_fp); } /* End file loop */ /* Write out average z step and start for irregularly spaced slices */ if ((ndims!=MAX_DIMS) && (num_scx_files>1)) { start[0] = 0; varid = ncvarid(mincid, MIzspace); (void) mivarget1(mincid, varid, start, NC_DOUBLE, NULL, &first_z); start[0] = scx_general_info->num_scx_slices - 1; (void) mivarget1(mincid, varid, start, NC_DOUBLE, NULL, &last_z); if (start[0] > 0) zstep = (last_z - first_z) / ((double) start[0]); else zstep = 1.0; (void) miattputdbl(mincid, varid, MIstep, zstep); (void) miattputdbl(mincid, varid, MIstart, first_z); } /* Close minc file */ (void) miattputstr(mincid, ncvarid(mincid, MIimage), MIcomplete, MI_TRUE); (void) miclose(mincid); FREE(image); if (!sort_over_time) { for (ifile=0; ifile] [...] \n", progname); (void) fprintf(stderr, " %s [-help]\n\n", progname); exit(EXIT_FAILURE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_scx_file_info @INPUT : num_scx_files - number of scanditronix files. scx_files - array of scanditronix file names. slice_range - 2-component array giving range of slices @OUTPUT : scx_file_info - array of structures containing information about each file. scx_general_info - general information about the scx files. @RETURNS : (-1) if no error occurs, otherwise, the index (in scx_file) of the first file that could not be read. @DESCRIPTION: Reads information from each scanditronix file given by scx_files. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int get_scx_file_info(int num_scx_files, char **scx_files, int slice_range[2], scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info) { static char *the_months[]= {NULL, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; int *num_scx_slices, *max_nslices, *max_size; scx_file *fp; scx_file_info_type *sfip; long lvalue, timh, timm, tims, tihu; float fvalue; char svalue[40]; double inj_time; int ifile, i, imnem, imult, length; scx_mnem_types mtype; scx_mnem_list_type *mnem_list; void *att_vector; /* Initialize number of scanditronix slices */ num_scx_slices = &(scx_general_info->num_scx_slices); max_nslices = &(scx_general_info->max_nslices); max_size = &(scx_general_info->max_size); *num_scx_slices = 0; *max_nslices = 0; *max_size = 0; /* Loop through files, reading information */ for (ifile=0; ifilelow_slice = 0; sfip->high_slice = lvalue - 1; if (slice_range[0] > sfip->low_slice) sfip->low_slice = slice_range[0]; if (slice_range[1] < sfip->high_slice) sfip->high_slice = slice_range[1]; if (sfip->low_slice > sfip->high_slice) sfip->low_slice = sfip->high_slice; sfip->nslices = sfip->high_slice - sfip->low_slice + 1; *num_scx_slices += sfip->nslices; if (sfip->nslices > *max_nslices) *max_nslices = sfip->nslices; /* Get image width */ if (scx_get_mnem(fp, SCX_IMFM, 0, &lvalue, NULL, NULL)) return ifile; sfip->image_size = lvalue; if (lvalue > *max_size) *max_size = lvalue; /* Get time (in seconds) */ if (scx_get_mnem(fp, SCX_TIMH, 0, &timh, NULL, NULL) || scx_get_mnem(fp, SCX_TIMM, 0, &timm, NULL, NULL) || scx_get_mnem(fp, SCX_TIMS, 0, &tims, NULL, NULL) || scx_get_mnem(fp, SCX_TIHU, 0, &tihu, NULL, NULL)) { return ifile; } sfip->scan_time = (timh * MIN_PER_HOUR + timm) * SEC_PER_MIN + tims + ((double) tihu)/100.0; /* Get injection time (in seconds) (use date from before and check for time before inj_time) */ if (scx_get_mnem(fp, SCX_ITMH, 0, &timh, NULL, NULL) || scx_get_mnem(fp, SCX_ITMM, 0, &timm, NULL, NULL) || scx_get_mnem(fp, SCX_ITMS, 0, &tims, NULL, NULL)) { return ifile; } inj_time = (timh * MIN_PER_HOUR + timm) * SEC_PER_MIN + tims; /* Set scan time to be from injection time. Check for scans over midnight (cannot deal with scans longer than 1 day */ sfip->scan_time -= inj_time; if (sfip->scan_time < 0.0) sfip->scan_time += HOURS_PER_DAY * MIN_PER_HOUR * SEC_PER_MIN; /* Get length of frame (in seconds) */ if (scx_get_mnem(fp, SCX_MTM, 0, NULL, &fvalue, NULL)) return ifile; sfip->time_width = fvalue; /* Get scan type */ if (scx_get_mnem(fp, SCX_IMTP, 0, NULL, NULL, sfip->image_type)) return ifile; /* Get isotope and half-life */ if (scx_get_mnem(fp, SCX_ISO, 0, NULL, NULL, sfip->isotope)) return ifile; for (i=0; isotope_list[i].name !=NULL; i++) { if (strncmp(isotope_list[i].name, sfip->isotope, strlen(isotope_list[i].name))==0) break; } sfip->half_life = isotope_list[i].half_life; /* Get z start and step (correct start for non-zero first slice */ if (scx_get_mnem(fp, SCX_CDI, 0, NULL, &fvalue, NULL)) return ifile; sfip->zstep = fvalue; if (scx_get_mnem(fp, SCX_CLV, 0, NULL, &fvalue, NULL)) return ifile; sfip->zstart = fvalue + sfip->low_slice * sfip->zstep; /* Get general information from first scx file */ if (ifile==0) { if (scx_get_mnem(fp, SCX_CSZ, 0, NULL, &scx_general_info->zwidth, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_PXS, 0, NULL, &scx_general_info->xystep, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_FWD, 0, NULL, &scx_general_info->xywidth, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_MAX, 0, &scx_general_info->vmax, NULL, NULL)) return MI_ERROR; if ((scx_general_info->vmax<=0) || (scx_general_info->vmax>32767)) scx_general_info->vmax=32000; if (scx_get_mnem(fp, SCX_IMUN, 0, NULL, NULL, scx_general_info->img_units)) return MI_ERROR; if (scx_get_mnem(fp, SCX_PNM, 0, NULL, NULL, scx_general_info->patient_name)) return MI_ERROR; if (scx_get_mnem(fp, SCX_SEX, 0, NULL, NULL, scx_general_info->patient_sex)) return MI_ERROR; if (scx_general_info->patient_sex[0]=='M') (void) strcpy(scx_general_info->patient_sex, MI_MALE); else if (scx_general_info->patient_sex[0]=='F') (void) strcpy(scx_general_info->patient_sex, MI_FEMALE); else (void) strcpy(scx_general_info->patient_sex, MI_OTHER); if (scx_get_mnem(fp, SCX_AGE, 0, &scx_general_info->patient_age, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_RIN, 0, NULL, NULL, scx_general_info->study_id)) return MI_ERROR; if (scx_get_mnem(fp, SCX_DATY, 0, &scx_general_info->start_year, NULL, NULL)) return MI_ERROR; scx_general_info->start_year += 1900; if (scx_general_info->start_year < 1950) scx_general_info->start_year += 100; if (scx_get_mnem(fp, SCX_DATM, 0, &scx_general_info->start_month, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_DATD, 0, &scx_general_info->start_day, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_TIMH, 0, &scx_general_info->start_hour, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_TIMM, 0, &scx_general_info->start_minute, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_TIMS, 0, NULL, &scx_general_info->start_seconds, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_TIHU, 0, NULL, &fvalue, NULL)) return MI_ERROR; scx_general_info->start_seconds += fvalue/100.0; if (scx_get_mnem(fp, SCX_TIM, 0, NULL, NULL, svalue)) return MI_ERROR; (void) sprintf(scx_general_info->start_time, "%d-%s-%d %s", (int) scx_general_info->start_day, the_months[scx_general_info->start_month], (int) scx_general_info->start_year, svalue); if (scx_get_mnem(fp, SCX_CAR, 0, NULL, NULL, scx_general_info->tracer)) return MI_ERROR; if (scx_get_mnem(fp, SCX_ITM, 0, NULL, NULL, scx_general_info->injection_time)) return MI_ERROR; if (scx_get_mnem(fp, SCX_ITMH, 0, &scx_general_info->injection_hour, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_ITMM, 0, &scx_general_info->injection_minute, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_ITMS, 0, NULL, &scx_general_info->injection_seconds, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_ACT, 0, NULL, &scx_general_info->injection_dose, NULL)) return MI_ERROR; /* Find out if MNI generic reconstruction was used */ if (scx_get_mnem(fp, SCX_USC, 0, NULL, NULL, svalue)) return MI_ERROR; scx_general_info->used_MNI_generic_reconstruction = (strncmp(svalue, SCX_MNI_GENERIC_RECONSTRUCTION_CODE, strlen(SCX_MNI_GENERIC_RECONSTRUCTION_CODE)) == 0); /* Get list of header values */ /* Get space for first mnemonic */ mnem_list = MALLOC(1, *mnem_list); /* Loop through mnemonics */ for (imnem=0; scx_list_mnems(fp, imnem, mnem_list[imnem].name, &mnem_list[imnem].mult, &mtype)!=NULL; imnem++){ /* Get space for attributes (handle strings differently) */ switch (mtype) { case scx_string: mnem_list[imnem].type = NC_CHAR; length = 2; att_vector = MALLOC(length, char); *((char *) att_vector) = '\0'; break; case scx_long: mnem_list[imnem].type = NC_LONG; att_vector = MALLOC(mnem_list[imnem].mult, long); break; case scx_float: mnem_list[imnem].type = NC_FLOAT; att_vector = MALLOC(mnem_list[imnem].mult, float); break; } /* Loop through multiplicity */ for (imult=0; imult < mnem_list[imnem].mult; imult++) { if (scx_get_mnem(fp, mnem_list[imnem].name, imult, &lvalue, &fvalue, svalue)) return MI_ERROR; switch (mtype) { case scx_string: if (imult>0) { *((char *) att_vector + length - 2) = '\n'; *((char *) att_vector + length - 1) = '\0'; length += 1; } length += strlen(svalue); att_vector = REALLOC(att_vector, length, char); att_vector = strcat((char *) att_vector, svalue); break; case scx_long: *(((long *) att_vector) + imult) = lvalue; break; case scx_float: *(((float *) att_vector) + imult) = fvalue; break; } } /* Loop over multiplicity */ /* Save length of string */ if (mtype==scx_string) { mnem_list[imnem].mult = length - 1; } /* Save pointer to attributes */ mnem_list[imnem].att_vector = att_vector; /* Get space for next mnemonic */ mnem_list = REALLOC(mnem_list, imnem+2, *mnem_list); } scx_general_info->mnem_list=mnem_list; scx_general_info->num_mnems=imnem; } /* If first file */ /* Close file */ scx_close(fp); } /* Loop over files */ return -1; } /* ----------------------------- MNI Header ----------------------------------- @NAME : sort_scx_slices @INPUT : sort_over_time - boolean indicating whether sort should be over time or z position. num_scx_files - number of scanditronix files. scx_file_info - array of scanditronix file information. scx_general_info - general information about scx files. @OUTPUT : scx_file_info - modified to give slice ordering information. @RETURNS : (nothing) @DESCRIPTION: Sorts the scanditronix slices for the output file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void sort_scx_slices(int sort_over_time, int num_scx_files, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info) { int ifile, islice, isort, num_sort, slice_num; /* Variables for sorting */ scx_sort_type *scx_sort; struct { int file; int slice; } *slice_ptr; scx_file_info_type *file_ptr; /* Allocate array for sorting */ num_sort = (sort_over_time) ? num_scx_files : scx_general_info->num_scx_slices; scx_sort = MALLOC (num_sort, *scx_sort); /* Are we sorting over time or z position */ if (sort_over_time) { /* Go through the files */ for (ifile=0; ifilefile = ifile; slice_ptr->slice = islice; scx_sort[isort].sort_value = slice_ptr; isort++; } } } /* Sort the slices */ qsort(scx_sort, num_sort, sizeof(*scx_sort), sortcmp); /* Loop through sorted list */ for (isort=0; isortordered_file = isort; } else { slice_ptr = scx_sort[isort].sort_value; ifile = slice_ptr->file; islice = slice_ptr->slice; scx_file_info[ifile].ordered_slices[islice] = isort; FREE(slice_ptr); } } /* Free the sorting array */ FREE(scx_sort); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : sortcmp @INPUT : val1 - first value val2 - second value @OUTPUT : (none) @RETURNS : 0 if values are the same, -1 if val1->sort_key < val2->sort_key and +1 if val1->sort_key > val2->sort_key. @DESCRIPTION: Compares two double precision values. If they are the same, then return 0. If val1 < val2, return -1. If val1 > val2, return +1. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int sortcmp(const void *val1, const void *val2) { if (((scx_sort_type *)val1)->sort_key < ((scx_sort_type *)val2)->sort_key) return -1; else if (((scx_sort_type *)val1)->sort_key > ((scx_sort_type *)val2)->sort_key) return 1; else return 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_minc_file @INPUT : mincid - id of minc file write_byte_data - boolean indicating whether data should be written as bytes (TRUE) or shorts (FALSE). copy_all_header - boolean indicating whether all of the scanditronix header information should be copied or not. ndims - number of dimensions for minc file count - lengths of dimensions minc file scx_filename - name of scanditronix file for header values num_scx_files - number of scanditronix files. scx_file_info - array of information on scanditronix files. scx_general_info - general information about scx files blood_file - name of blood file containing data to include @OUTPUT : (nothing) @RETURNS : Image conversion variable id or MI_ERROR if an error occurs. @DESCRIPTION: Initializes the header of the minc file using information from the scanditronix file and the other structures. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int setup_minc_file(int mincid, int write_byte_data, int copy_all_header, int ndims, long count[], int num_scx_files, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info, char *blood_file) { static char *dim_names_array[]={MItime, MIzspace, MIyspace, MIxspace}; char **dim_names; static char *dimwidth_names_array[]={ MItime_width, MIzspace_width, MIyspace_width, MIxspace_width}; char **dimwidth_names; int dim[MAX_DIMS]; int img, imgmax, imgmin, dimvarid, widvarid, icv, varid, scx_var; int idim, imnem; int bloodid; double vrange[2]; /* Create the dimensions */ dim_names = dim_names_array + MAX_DIMS - ndims; dimwidth_names = dimwidth_names_array + MAX_DIMS - ndims; for (idim=0; idim1)) ? 1 : 0, &dim[idim]); widvarid=micreate_std_variable(mincid, dimwidth_names[idim], NC_DOUBLE, (strcmp(dim_names[idim], MItime)==0) ? 1 : 0, &dim[idim]); /* Add attributes to the dimension variables */ if (strcmp(dim_names[idim], MIzspace)==0) { /* Write out step and start. We will rewrite this for irregularly spaced files */ (void) miattputdbl(mincid, dimvarid, MIstep, scx_file_info[0].zstep); (void) miattputdbl(mincid, dimvarid, MIstart, scx_file_info[0].zstart); (void) miattputstr(mincid, dimvarid, MIunits, "mm"); (void) miattputstr(mincid, dimvarid, MIspacetype, MI_NATIVE); (void) miattputdbl(mincid, widvarid, MIwidth, (double) scx_general_info->zwidth); (void) miattputstr(mincid, widvarid, MIunits, "mm"); (void) miattputstr(mincid, widvarid, MIfiltertype, MI_GAUSSIAN); } else if ((strcmp(dim_names[idim], MIyspace)==0) || (strcmp(dim_names[idim], MIxspace)==0)) { (void) miattputstr(mincid, dimvarid, MIunits, "mm"); (void) miattputdbl(mincid, dimvarid, MIstep, (double) scx_general_info->xystep); (void) miattputstr(mincid, dimvarid, MIspacetype, MI_NATIVE); (void) miattputdbl(mincid, widvarid, MIwidth, (double) scx_general_info->xywidth); (void) miattputstr(mincid, widvarid, MIfiltertype, MI_GAUSSIAN); } else if (strcmp(dim_names[idim], MItime)==0) { (void) miattputstr(mincid, dimvarid, MIunits, "seconds"); (void) miattputstr(mincid, widvarid, MIunits, "seconds"); } } /* Create the image variable */ if (write_byte_data) { img=micreate_std_variable(mincid, MIimage, NC_BYTE, ndims, dim); (void) miattputstr(mincid, img, MIsigntype, MI_UNSIGNED); vrange[0]=0; vrange[1]=255; } else { img=micreate_std_variable(mincid, MIimage, NC_SHORT, ndims, dim); (void) miattputstr(mincid, img, MIsigntype, MI_SIGNED); vrange[0] = -scx_general_info->vmax; vrange[1] = scx_general_info->vmax; } (void) ncattput(mincid, img, MIvalid_range, NC_DOUBLE, 2, vrange); (void) miattputstr(mincid, img, MIcomplete, MI_FALSE); /* Create the image max and min variables */ imgmax=micreate_std_variable(mincid, MIimagemax, NC_DOUBLE, ndims-2, dim); imgmin=micreate_std_variable(mincid, MIimagemin, NC_DOUBLE, ndims-2, dim); (void) miattputstr(mincid, imgmax, MIunits, scx_general_info->img_units); (void) miattputstr(mincid, imgmin, MIunits, scx_general_info->img_units); /* Create the image conversion variable */ icv=miicv_create(); (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT); (void) miicv_setdbl(icv, MI_ICV_VALID_MAX, (double) scx_general_info->vmax); (void) miicv_setdbl(icv, MI_ICV_VALID_MIN, (double) -scx_general_info->vmax); /* Save patient info */ varid = micreate_group_variable(mincid, MIpatient); (void) miattputstr(mincid, varid, MIfull_name, scx_general_info->patient_name); (void) miattputstr(mincid, varid, MIsex, scx_general_info->patient_sex); (void) ncattput(mincid, varid, MIage, NC_LONG, 1, &scx_general_info->patient_age); /* Save study info */ varid = micreate_group_variable(mincid, MIstudy); (void) miattputstr(mincid, varid, MImodality, MI_PET); (void) miattputstr(mincid, varid, MImanufacturer, "Scanditronix"); (void) miattputstr(mincid, varid, MIstudy_id, scx_general_info->study_id); (void) miattputstr(mincid, varid, MIstart_time, scx_general_info->start_time); (void) ncattput(mincid, varid, MIstart_year, NC_LONG, 1, &scx_general_info->start_year); (void) ncattput(mincid, varid, MIstart_month, NC_LONG, 1, &scx_general_info->start_month); (void) ncattput(mincid, varid, MIstart_day, NC_LONG, 1, &scx_general_info->start_day); (void) ncattput(mincid, varid, MIstart_hour, NC_LONG, 1, &scx_general_info->start_hour); (void) ncattput(mincid, varid, MIstart_minute, NC_LONG, 1, &scx_general_info->start_minute); (void) ncattput(mincid, varid, MIstart_seconds, NC_FLOAT, 1, &scx_general_info->start_seconds); /* Save acquisition info */ varid = micreate_group_variable(mincid, MIacquisition); (void) miattputstr(mincid, varid, MIradionuclide, scx_file_info[0].isotope); if (scx_file_info[0].half_life > 0.0) { (void) miattputdbl(mincid, varid, MIradionuclide_halflife, (double) scx_file_info[0].half_life); } (void) miattputstr(mincid, varid, MItracer, scx_general_info->tracer); (void) miattputstr(mincid, varid, MIinjection_time, scx_general_info->injection_time); (void) ncattput(mincid, varid, MIinjection_hour, NC_LONG, 1, &scx_general_info->injection_hour); (void) ncattput(mincid, varid, MIinjection_minute, NC_LONG, 1, &scx_general_info->injection_minute); (void) ncattput(mincid, varid, MIinjection_seconds, NC_FLOAT, 1, &scx_general_info->injection_seconds); (void) ncattput(mincid, varid, MIinjection_dose, NC_FLOAT, 1, &scx_general_info->injection_dose); (void) miattputstr(mincid, varid, MIdose_units, "mCurie"); /* If the MNI generic reconstruction was used, indicate the fact */ if (scx_general_info->used_MNI_generic_reconstruction) { varid = ncvardef(mincid, "MNI_PET_rec", NC_LONG, 0, NULL); (void) miattputstr(mincid, varid, MIvartype, MI_GROUP); (void) miattputstr(mincid, varid, MIvarid, "MNI PET reconstruction info"); (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), varid); (void) miattputstr(mincid, varid, "generic_reconstruction", MI_TRUE); } /* If we want all of the values from the scanditronix header, get them */ if (copy_all_header) { /* Create a variable for scx mnemonics */ scx_var = ncvardef(mincid, "scanditronix", NC_LONG, 0, NULL); (void) miattputstr(mincid, scx_var, MIvartype, MI_GROUP); (void) miattputstr(mincid, scx_var, MIvarid, "MNI SCX variable"); (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), scx_var); /* Loop through mnemonics */ for (imnem=0; imnemnum_mnems; imnem++){ ncattput(mincid, scx_var, scx_general_info->mnem_list[imnem].name, scx_general_info->mnem_list[imnem].type, scx_general_info->mnem_list[imnem].mult, scx_general_info->mnem_list[imnem].att_vector); } /* Loop through mnemonics */ } /* If copy_all_header */ /* Open the blood file and create the variables if needed */ if (blood_file != NULL) { bloodid = ncopen(blood_file, NC_NOWRITE); CreateBloodStructures(mincid, bloodid); } /* Attach the icv */ (void) ncendef(mincid); (void) miicv_attach(icv, mincid, img); /* Copy the blood data */ if (blood_file != NULL) { FillBloodStructures(mincid, bloodid); ncclose(bloodid); } return icv; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_scx_slice @INPUT : scx_fp - file pointer for scanditronix file slice_num - slice to copy scx_file_info - information on scanditronix file. scx_general_info - general scx file information @OUTPUT : pixel_max - maximum pixel value image_max - real value to which pixel_max corresponds image - scanditronix image @RETURNS : Returns TRUE if an error occurs. @DESCRIPTION: Gets a scanditronix image. @METHOD : @GLOBALS : @CALLS : @CREATED : January 20, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int get_scx_slice(scx_file *scx_fp, int slice_num, long *pixel_max, float *image_max, short *image, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info) /* ARGSUSED */ { long npix, ix, iy, y_offset, off1, off2; short temp; /* Get the image from the scanditronix file */ if (scx_get_image(scx_fp, slice_num, image)) return TRUE; /* Flip the scanditronix image to give positive x & y axes */ npix = scx_file_info->image_size * scx_file_info->image_size; for (iy=0; iyimage_size/2; iy++) { y_offset = iy * scx_file_info->image_size; for (ix=0; iximage_size; ix++) { off1 = y_offset + ix; off2 = npix - off1 - 1; temp = image[off1]; image[off1] = image[off2]; image[off2] = temp; } } /* Get image and pixel max */ if (scx_get_mnem(scx_fp, SCX_MAG, slice_num, NULL, image_max, NULL)) return TRUE; if (scx_get_mnem(scx_fp, SCX_MAX, 0, pixel_max, NULL, NULL)) return TRUE; if ((*pixel_max<=0) || (*pixel_max>32767)) *pixel_max = 32000; return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : write_minc_slice @INPUT : scale - scale for decay correcting image write_byte_data - boolean indicating whether data should be written as bytes (TRUE) or shorts (FALSE). mincid - id of minc file icvid - id of image conversion variable start - coordinate of slice in minc file count - edge lengths of image to write in minc file image - pointer to image buffer pixel_max - maximum pixel value image_max - real value to which pixel_max corresponds scan_time - time of slice time_width - time width of slice zpos - z position of slice scx_file_info - information on scanditronix file. scx_general_info - general scx file information @OUTPUT : (nothing) @RETURNS : Returns TRUE if an error occurs. @DESCRIPTION: Writes out the image to the minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int write_minc_slice(double scale, int write_byte_data, int mincid, int icvid, int ndims,long start[], long count[], short *image, int image_size, long pixel_max, float image_max, double scan_time, double time_width, double zpos) /*ARGSUSED*/ { double pixmin, pixmax; long ipix, npix; double maximum, minimum; /* Search for pixel max and min */ npix = image_size * image_size; pixmin = pixmax = image[0]; for (ipix=1; ipixpixmax) pixmax = image[ipix]; if (image[ipix]