Mercurial > hg > minc-tools
changeset 1548:a5e748f9c8e9
Initial revision
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/Makefile @@ -0,0 +1,38 @@ +# -------------------------------------------------------------------- +# +# MINC Makefile +# Written by leili Nov. 2002 +# + +ROOT = ../../minc +ACR_LIB_DIR = ../Acr_nema +include $(ACR_LIB_DIR)/Make_acrdefs +include $(ROOT)/Make_machine_specific +include $(ROOT)/Make_configuration + +# Executable names +# Couple of things have been added by Leili on April 30, 2003 +# dicomserver-nondebug-test , save_transferred_object_test.o +PROGS = dicomserver-nondebug dcm2mnc dicomserver-debug dicomserver-nondebug2 dicomserver-nondebug-old +EXTRA_OBJS = dicom_element_defs.o open_connection.o reply.o \ + save_transferred_object.o use_the_files.o \ + siemens_dicom_to_minc.o siemens_dicom_read.o minc_file.o \ + string_to_filename.o project_file.o \ + parse_dicom_groups.o siemens_to_dicom.o progress.o +HEADERS = dicomserver.h dicom_prototypes.h dicom_element_defs.h \ + spi_element_defs.h siemens_dicom_to_minc.h +CDEFINES = -DDEBUG# cpp defines +INCLUDES = -I/usr/include -I. -I$(ACR_LIB_DIR) -I$(PROG_LIB_DIR) \ + -I$(VOLIO_LIB_DIR)/Include -Isiemens_include \ + -I$(MINC_LIB_DIR) -I$(NETCDF_INCLUDE_DIR) +LDOPT = $(CC_ACR_LIB) $(PROG_LDOPT) +MANSECT = 1 +#MANPAGES = $(PROGS).$(MANSECT) + +include $(ROOT)/progs/Make_progs + +dicomserver-debug.o: dicomserver-nondebug.c +dicomserver-nondebug.o: dicomserver-nondebug.c +#Added by Leili +dicomserver-nondebug2.o: dicomserver-nondebug2.c +dicomserver-nondebug-old.o: dicomserver-nondebug-old.c
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/dcm2mnc.c @@ -0,0 +1,880 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dcm2mnc.c +@DESCRIPTION: Program to convert dicom files to minc +@GLOBALS : +@CREATED : June 2001 (Rick Hoge) +@MODIFIED : + * $Log: dcm2mnc.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.5 2002/04/26 12:02:50 rhoge + * updated usage statement for new forking defaults + * + * Revision 1.4 2002/04/26 11:32:48 rhoge + * made forking default + * + * Revision 1.3 2002/03/23 13:17:53 rhoge + * added support for Bourget network pushed dicom files, cleaned up + * file check and read_numa4_dicom vr check/assignment + * + * Revision 1.2 2002/03/22 19:19:36 rhoge + * Numerous fixes - + * - handle Numaris 4 Dicom patient name + * - option to cleanup input files + * - command option + * - list-only option + * - debug mode + * - user supplied name, idstr + * - anonymization + * + * Revision 1.1 2002/03/22 03:50:02 rhoge + * new name for standalone dicom to minc converter + * + * Revision 1.3 2002/03/22 00:38:08 rhoge + * Added progress bar, wait for children at end, updated feedback statements + * + * Revision 1.2 2002/03/19 13:13:56 rhoge + * initial working mosaic support - I think time is scrambled though. + * + * Revision 1.1 2001/12/31 17:26:21 rhoge + * adding file to repository- compiles without warning and converts non-mosaic + * Numa 4 files. + * Will probably not work for Numa 3 files yet. + * +---------------------------------------------------------------------------- */ + +#ifndef lint +static char rcsid[]="$Header: /private-cvsroot/minc/conversion/dicomserver_sonata/dcm2mnc.c,v 1.1 2003-08-15 19:52:55 leili Exp $"; +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#include "dicomserver.h" + +extern char *minc_history; // Global for minc history +char *pname; // program name +File_Type file_type = UNDEF ; // type of input files +char command_line[512]; +int N4_OFFSET; + +// function prototypes +private int ima_sort_function(const void *entry1, const void *entry2); +private int dcm_sort_function(const void *entry1, const void *entry2); +private int print_file_info( int ix, Data_Object_Info *info); +public int progress(long index, int end, char *message); + +#define EXTREME_LOGGING 10 + +int Do_logging = 0; +int Fork = 1; +int Debug = 0; +int Anon = 0; +int UserIdStr = 0; +char IdStr[512]; +int UserName = 0; +char Name[512]; + +// Do we keep files or are they temporary? +// (now this is overridden by Cleanup) +static int Keep_files = +#ifndef KEEP_FILES + FALSE; +#else + TRUE; +#endif + +// Globals for handling connection timeouts +// (obsolete?) +int Connection_timeout = FALSE; +Acr_File *Alarmed_afp = NULL; + +int main(int argc, char *argv[]) +{ + long ifile; + long max_group; + + Acr_Group group_list; + int exit_status; + char **file_list; + char **acq_file_list; + Data_Object_Info **file_info_list; + Data_Object_Info **acq_file_info_list; + int num_files, num_files_alloc; + int num_acq_files; + FILE *fptemp; + char last_file_name[256]; // delete? + char *project_name = NULL; + int process_files, have_extra_file; + pid_t parent_pid, child_pid; + int statptr; + + int ix; + int UseArgDir = 1; + char OutDir[128]; + int List = 0; + int Cleanup = 0; + // Added by leili + Acr_Element element; + char model_name[256]; + char patient_name[256]; + char patient_id[256]; + char reg_time[256]; + char reg_date[256]; + char out_dir[256]; + char temp_name[256]; + char *temp_dir; + static char file_prefix_string[L_tmpnam+1] = "dicomserver"; + char *file_prefix = file_prefix_string; + //double study_id; + //int acquisition_id, image_id, study_time, study_date; + + /* Get server process id */ + parent_pid = getpid(); + + /* Create minc history string */ + { + char *string; + string = "dicomserver"; + minc_history = time_stamp(1, &string); + } + + /* get program name */ + + pname = argv[0]; + + if (argc<2) { + usage(); + } + + /* Added by leili to test the temp_dir */ + // temp_dir = NULL; + //if (! Keep_files){ + // temp_dir = tempnam(NULL, NULL); + //if(mkdir(temp_dir, (mode_t) 0777)){ + // (void) fprintf(stderr, + // "%s: Unable to create directory for temporary files.\n", + // pname); + // perror(pname); + // exit(EXIT_FAILURE); + // } + // printf("Temp directory for the dcm files:%s",temp_dir); + // (void) strcpy(file_prefix, temp_dir); + // (void) strcat(file_prefix, "/dicom"); + //} + + /* read in all the input pars and file names */ + + for (ix = 1; ix<argc; ix++) { + + if (!strncmp(argv[ix],"-help",5)) { + usage(); + } + else if (!strncmp(argv[ix],"-log",4)) { + ix++; + Do_logging = atoi(argv[ix]); + } + else if (!strncmp(argv[ix],"-fork",5)) { + printf("Forking is now default; -fork option is no longer needed\n"); + Fork = 1; + } + else if (!strncmp(argv[ix],"-nofork",7)) { + Fork = 0; + } + else if (!strncmp(argv[ix],"-debug",4)) { + Debug = 1; + } + else if (!strncmp(argv[ix],"-anon",5)) { + Anon = 1; + } + else if (!strncmp(argv[ix],"-idstr",6)) { + ix++; + strcpy(IdStr,argv[ix]); + UserIdStr = 1; + } + else if (!strncmp(argv[ix],"-descr",6)) { + ix++; + strcpy(Name,argv[ix]); + UserName = 1; + } + else if (!strncmp(argv[ix],"-list",5)) { + List = 1; + } + else if (!strncmp(argv[ix],"-cleanup",6)) { + Cleanup = 1; + } + else if (!strncmp(argv[ix],"-cmd",4)) { + ix++; + strcpy(command_line,argv[ix]); + } + else if (!strncmp(argv[ix],"-",1)) { + (void) fprintf(stderr,"ERROR: input arg `%s' not recognized\n", + argv[ix]); + exit(EXIT_FAILURE); + } + else { /* assume rest of args are the file names and destination */ + + /* Get space for file lists */ + if (List) { + num_files = argc - ix; + } else { + num_files = argc - ix - 1; // last arg is output dir + } + file_list = MALLOC((size_t) num_files * sizeof(*file_list)); + file_info_list = MALLOC(num_files * sizeof(*file_info_list)); + + ifile = 0; + for ( ; ix < argc - 1; ix++) { + file_list[ifile] = strdup(argv[ix]); + ifile++; + } + + if (List) { + // if listing, last input arg will be an input file + file_list[ifile] = strdup(argv[argc-1]); + } else { + // otherwise, last input arg will be output directory + strcpy(OutDir,argv[argc-1]); + (void) strcat(OutDir, "/"); // make sure path ends with slash + } + } + } // end of loop over input args + + // if List, then no point in forking + + if (List) { + Fork = 0; + } + + /* figure out what kind of files we have - + * supported types are: + * + * IMA (Siemens .ima format - Numaris 3.5) + * N4DCM (Siemens DICOM - Numaris 4) + * + * if not all same type, return an error */ + + // note - checking that all files are the same type + // is quite slow so the hard-coded default is now + // to break out of this check once a file type is + // established. Generally users have only one file + // type in a directory. + + // we start by assuming N4DCM with no offset - we find that + // the file is IMA or has an offset (the 128 byte + DICM offset + // seen on Syngo CD's and exports) then the appropriate flag will + // be set. + + printf("Checking file types... "); + + file_type = N4DCM; // default + N4_OFFSET = 0; + + // printf(" The Manufacturer Model is %s:\n ", SPI_Manufacturer_model); + + for (ifile = 0; ifile < num_files; ifile++) { + + char dicm_test_string[5]; + + fptemp = fopen(file_list[ifile], "r"); + + if (fptemp == NULL) { + fprintf(stderr,"Error opening file %s!\n",file_list[ifile]); + exit(EXIT_FAILURE); + } + + /* Numaris 4 DICOM CD/Export file? if so, bytes 129-132 will + contain the string `DICM' */ + + fseek(fptemp,128,SEEK_SET); + fread(dicm_test_string,1,4,fptemp); + dicm_test_string[4] = (char) '\0'; + + // Added by leili to make the program works for data form the old vision system + // To do this, we should figure out the manufacturer model from the dicom header + // if the machine is the old vision, create the output directory + // then call the dicom_to_minc script to do the conversion + group_list = read_siemens_dicom(file_list[ifile],100); + element = acr_find_group_element(group_list, ACR_Manufacturer_model); + if (element != NULL) { + string_to_filename(acr_get_element_string(element), model_name, + sizeof(model_name));} + + if ((element == NULL) || (strlen(model_name) == 0)) + (void) strcpy(model_name, "unknown"); + printf(" The model_name is %s\n", model_name); + + printf("\nacq_file_info_list[ifile]->study_id: \t%.6f", + acq_file_info_list[ifile]->study_id); + printf("\nthe ifile is: %d", ifile); + + printf("\nacq_file_info_list[ifile]->study_date: \t%d", + acq_file_info_list[ifile]->study_date); + + printf("\nacq_file_info_list[ifile]->study_time: \t%d", + acq_file_info_list[ifile]->study_time); + + + if ((strcmp(model_name, "sonatavision") == 0) && (!strncmp(dicm_test_string,"DICM",4))){ + file_type = N4DCM; + N4_OFFSET = 1; + printf("assuming remaining files are Syngo DICOM (CD/Export).\n"); + (void) fclose(fptemp); + break; // break out of file checking loop + + }else if ((strcmp(model_name, "magnetom_vision") == 0)|| ((strcmp(model_name, "unknown")) == 0)) { + file_type = IMA; + printf("assuming remaining files are IMA!\n"); + (void) fclose(fptemp); + break; // break out of file checking loop + }else { + file_type = N4DCM; + N4_OFFSET = 0; + printf("assuming remaining files are Syngo DICOM.\n"); + (void) fclose(fptemp); + break; // break out of file checking loop + } // end of file type check + + (void) fclose(fptemp); + } // end of loop over files to check for mixed file types + + // now loop over all files, getting basic info on each + + for (ifile = 0; ifile < num_files; ifile++) { + + char message[20]; + sprintf(message,"Parsing %d files",num_files); + if (!Debug) { + progress(ifile, num_files, message); + } + + // allocate space for the current entry to file_info_list + file_info_list[ifile] = MALLOC(sizeof(*file_info_list[ifile])); + file_info_list[ifile]->file_index = ifile; + + if (file_type == N4DCM) { + + // read up to but not including pixel data + max_group = ACR_ACTUAL_IMAGE_GID - 1; + group_list = read_numa4_dicom(file_list[ifile], max_group); + + } else if (file_type == IMA) { + // Added by leili for the conversion of dicom data from the old vision machine + //strcat(out_dir, "/data/fmri/transfer/images/"); + group_list = read_siemens_dicom(file_list[ifile],100); + + + + element = acr_find_group_element(group_list, ACR_Patient_name); + if (element != NULL) { + string_to_filename(acr_get_element_string(element), patient_name, + sizeof(patient_name)); + } + if ((element == NULL) || (strlen(patient_name) == 0)) + (void) strcpy(patient_name, "unknown"); + + strcat(out_dir, patient_name); + strcat(out_dir, "_"); + + element = acr_find_group_element(group_list, ACR_Patient_identification); + if (element != NULL) { + string_to_filename(acr_get_element_string(element), patient_id, + sizeof(patient_id)); + } + if ((element == NULL) || (strlen(patient_id) == 0)) + (void) strcpy(patient_id, "unknown"); + + strcat(out_dir, patient_id); + strcat(out_dir, "_"); + + element = acr_find_group_element(group_list, ACR_Study_date); + if (element != NULL) { + string_to_filename(acr_get_element_string(element), reg_date, + sizeof(reg_date)); + } + if ((element == NULL) || (strlen(reg_date) == 0)) + (void) strcpy(patient_id, "unknown"); + + strcat(out_dir, reg_date); + strcat(out_dir, "_"); + + element = acr_find_group_element(group_list, ACR_Study_time); + if (element != NULL) { + string_to_filename(acr_get_element_string(element), reg_time, + sizeof(reg_time)); + } + if ((element == NULL) || (strlen(reg_time) == 0)) + (void) strcpy(patient_id, "unknown"); + + strcat(out_dir, reg_time); + + mkdir(out_dir,(mode_t) 0777); + // this part works fine but it's an ugly code!!! Perhaps a better solution than the system call? + strcat(temp_name,"/software/source/dicomserver_test/conversion/dicomserver/dicom_to_minc"); + strcat(temp_name," "); + //strcat(temp_name,out_dir); + strcat(temp_name, "/data/fmri/transfer/images/leili"); + strcat(temp_name,"/."); + strcat(temp_name," "); + strcat(temp_name,"-compress"); + strcat(temp_name," "); + strcat(temp_name,"-inputdir"); + strcat(temp_name, " "); + //strcat(temp_name,temp_dir); + strcat(temp_name, "/software/source/dicomserver_test/conversion/dicomserver/dicom_data/Numaris_3/IMAS"); + strcat(temp_name,"/."); + printf(" the temp_name is : %s\n", temp_name); + system (temp_name); + exit(0); + } + + // get some preliminary info from group_list + // (which should have been `corrected' in read_xxxx_dicom + parse_dicom_groups(group_list, file_info_list[ifile]); + + // put the file name into the info list + file_info_list[ifile]->file_name = strdup(file_list[ifile]); + + /* print out info about file */ + print_file_info(ifile,file_info_list[ifile]); + + // Delete the group list now that we're done with it + acr_delete_group_list(group_list); + + } // end of loop over files to get basic info + + printf("Sorting files... "); + + if (file_type == N4DCM) { + // sort the files based on acquisition number + qsort(file_info_list, num_files, sizeof(file_info_list[0]), + dcm_sort_function); + + } else if (file_type == IMA) { + // sort the files based on file name + // (could also use dcm_sort_function, but would have + // to use ACR_Image instead of ACR_Acquisition + qsort(file_list, num_files, sizeof(file_list[0]), + ima_sort_function); + } + + printf("Done sorting files.\n"); + + /* Get space for acquisition file lists */ + num_files_alloc = FILE_ALLOC_INCREMENT; + acq_file_list = MALLOC((size_t) num_files_alloc * sizeof(*acq_file_list)); + acq_file_info_list = MALLOC(num_files_alloc * sizeof(*acq_file_info_list)); + + /* Loop over files, processing by acquisition */ + + if (List) { + printf("Listing files by series...\n"); + } else { + printf("Processing files, one series at a time...\n"); + } + + num_acq_files = 1; + for (ifile = 0; ifile < num_files; ifile++) { + + // Wait for any children that have finished + while ((child_pid=wait3(&statptr, WNOHANG, NULL)) > 0) {} + + // If there are children, slow down the processing + if (child_pid == 0) { + (void) sleep((unsigned int) SERVER_SLEEP_TIME); + } + + /* Set flags indicating whether we should do anything with the files + and whether the file lists contain an extra file */ + process_files = FALSE; + have_extra_file = FALSE; + + /* Extend acquisition file list if necessary */ + if (num_acq_files >= num_files_alloc) { + num_files_alloc = num_acq_files + FILE_ALLOC_INCREMENT; + acq_file_list = REALLOC(acq_file_list, + num_files_alloc * sizeof(*acq_file_list)); + acq_file_info_list = + REALLOC(acq_file_info_list, + num_files_alloc * sizeof(*acq_file_info_list)); + } + acq_file_list[num_acq_files-1] = NULL; + acq_file_info_list[num_acq_files-1] = + MALLOC(sizeof(*acq_file_info_list[num_acq_files-1])); + + acq_file_list[num_acq_files-1] = strdup(file_info_list[ifile]->file_name); + + if (file_type == N4DCM) { + /* read up to but not including pixel data */ + max_group = ACR_ACTUAL_IMAGE_GID - 1; + group_list = read_numa4_dicom(acq_file_list[num_acq_files-1],max_group); + + } else if (file_type == IMA) { + group_list = siemens_to_dicom(acq_file_list[num_acq_files-1], TRUE); + if (group_list == NULL) { + fprintf(stderr,"Error reading groups from file %s!\n", + acq_file_list[num_acq_files-1]); + exit(EXIT_FAILURE); + } + } + parse_dicom_groups(group_list, acq_file_info_list[num_acq_files-1]); + + // put the file name into the info list + acq_file_info_list[num_acq_files-1]->file_name = + strdup(acq_file_list[num_acq_files-1]); + + // print some file info (junk) + print_file_info(num_acq_files,acq_file_info_list[num_acq_files-1]); + + // Check whether we have reached the end of a group of files + if (num_acq_files > 1) { + if ((acq_file_info_list[num_acq_files-1]->study_id != + acq_file_info_list[0]->study_id) || + (acq_file_info_list[num_acq_files-1]->acq_id != + acq_file_info_list[0]->acq_id)) { + + process_files = TRUE; + have_extra_file = TRUE; + } else if (ifile == num_files-1) { + // we're at the last file + process_files = TRUE; + } + } + + // Delete the group list now that we're done with it + acr_delete_group_list(group_list); + + // Use the files if we have a complete acquisition + if (process_files) { + + // Check for file from next acquisition + if (have_extra_file) num_acq_files--; + + if (List) { + + printf("Series %4d: %30s (%4d files)\n", + acq_file_info_list[0]->acq_id, + acq_file_info_list[0]->protocol_name, + num_acq_files); + + } else { + + printf("Converting data for series %d (%s: %d files)...\n", + acq_file_info_list[0]->acq_id, + acq_file_info_list[0]->protocol_name, + num_acq_files); + } + + if (Fork) { + // Fork child to process the files + child_pid = fork(); + } else { + child_pid = 0; + } + + if (child_pid > 0) { // Parent process + // printf("[Parent]: Forked process to create minc file.\n"); + } // Error forking + else if (child_pid < 0) { + fprintf(stderr, + "Error forking child to create minc file\n"); + return; + } + else { // Child process + + if (!List) { + + // process the files (same function as server) + use_the_files(project_name, num_acq_files, acq_file_list, + acq_file_info_list,UseArgDir,OutDir); + + // Print message about child finishing + printf("-File creation complete for Series %d.\n", + acq_file_info_list[0]->acq_id); + + // Clean up files, if needed + if (Cleanup) { + if (num_acq_files > 0) { + printf("-Removing input files... "); + cleanup_files(num_acq_files, acq_file_list); + printf("Done removing input files.\n"); + } + } + } + + if (Fork) { + // Exit from child, if forked + exit(EXIT_SUCCESS); + } + + } // End of child process + + // ---------------------------------------------------- + // if we get here we must be the parent, who goes + // along happily continuing to eat files + // ---------------------------------------------------- + + // Reset the lists for a new series + free_list(num_acq_files, acq_file_list, acq_file_info_list); + + // check to see if the last series ended by + // running into a new series: + if (have_extra_file) { + // move most recent file info to first entry in array, + // in preparation for next series + + // note that num_acq_files has already been decremented, + // so we do not need to subtract 1 to convert to array index + acq_file_list[0] = acq_file_list[num_acq_files]; + acq_file_info_list[0] = acq_file_info_list[num_acq_files]; + + acq_file_list[num_acq_files] = NULL; + acq_file_info_list[num_acq_files] = NULL; + } + // num_acq_files = (have_extra_file ? 1 : 0); + num_acq_files = (have_extra_file ? 2 : 1); + } else { + // we're not processing the files yet - just increment counter + num_acq_files++; + } + } // end of loop over files + + // Wait for child processes if we've been forking. + // We sleep in the checking loop to reduce parent CPU usage + if (Fork) { + printf("-(waiting for child processes to finish...)\n"); + while ((child_pid=wait3(&statptr, WNOHANG, NULL)) >= 0) {sleep(1);} + } + + if (List) { + printf("Done listing files.\n"); + } else { + printf("Done processing files.\n"); + } + + /* Save name of first file in last set */ + if ((num_acq_files > 0) && (acq_file_list[0] != NULL)) { + last_file_name[sizeof(last_file_name) - 1] = '\0'; + (void) strncpy(last_file_name,acq_file_list[0],sizeof(last_file_name)-1); + } + else { + last_file_name[0] = '\0'; + } + + FREE(acq_file_list); + FREE(acq_file_info_list); + + /* Print final message */ + + exit_status = EXIT_SUCCESS; + + exit(exit_status); + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : cleanup_files +@INPUT : num_files - number of files in list + file_list - array of file names +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Removes files. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void cleanup_files(int num_files, char *file_list[]) +{ + int i; + + // if (Keep_files) return; + + for (i=0; i < num_files; i++) { + if (file_list[i] != NULL) { + (void) remove(file_list[i]); + } + } + + return; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : free_list +@INPUT : num_files - number of files in list + file_list - array of file names +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free + the arrays themselves. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void free_list(int num_files, char **file_list, + Data_Object_Info **file_info_list) +{ + int i; + + for (i=0; i < num_files; i++) { + if (file_list[i] != NULL) { + FREE(file_list[i]); + } + if (file_info_list[i] != NULL) { + FREE(file_info_list[i]); + } + } + + return; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : ima_sort_function +@INPUT : entry1 + entry2 +@OUTPUT : (none) +@RETURNS : -1, 0, 1 for lt, eq, gt +@DESCRIPTION: Function to compare two ima file names +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : June 2001 (Rick Hoge) +@MODIFIED : +---------------------------------------------------------------------------- */ +private int ima_sort_function(const void *entry1, const void *entry2) +{ + + char * const *value1 = entry1; + char * const *value2 = entry2; + + int session1,series1,image1; + int session2,series2,image2; + + sscanf(*value1,"%d-%d-%d.ima",&session1,&series1,&image1); + sscanf(*value2,"%d-%d-%d.ima",&session2,&series2,&image2); + + if (session1 < session2) return -1; + else if (session1 > session2) return 1; + else if (series1 < series2) return -1; + else if (series1 > series2) return 1; + else if (image1 < image2) return -1; + else if (image1 > image2) return 1; + else return 0; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dcm_sort_function +@INPUT : entry1 + entry2 +@OUTPUT : (none) +@RETURNS : -1, 0, 1 for lt, eq, gt +@DESCRIPTION: Function to compare two dcm series numbers +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : June 2001 (Rick Hoge) +@MODIFIED : +---------------------------------------------------------------------------- */ +private int dcm_sort_function(const void *entry1, const void *entry2) +{ + + Data_Object_Info **file_info_list1 = (Data_Object_Info **) entry1; + Data_Object_Info **file_info_list2 = (Data_Object_Info **) entry2; + + // make a sort-able session ID number: date.time + double session1 = (*file_info_list1)->study_date + + (*file_info_list1)->study_time / 1e6; + double session2 = (*file_info_list2)->study_date + + (*file_info_list2)->study_time / 1e6; + + // series index + int series1 = (*file_info_list1)->acq_id; + int series2 = (*file_info_list2)->acq_id; + + // frame index + int frame1 = (*file_info_list1)->dyn_scan_number; + int frame2 = (*file_info_list2)->dyn_scan_number; + + // image index + int image1 = (*file_info_list1)->global_image_number; + int image2 = (*file_info_list2)->global_image_number; + + if (session1 < session2) return -1; + else if (session1 > session2) return 1; + else if (series1 < series2) return -1; + else if (series1 > series2) return 1; + else if (frame1 < frame2) return -1; + else if (frame1 > frame2) return 1; + else if (image1 < image2) return -1; + else if (image1 > image2) return 1; + else return 0; +} + +private int print_file_info( int ix, Data_Object_Info *info) { + + if (!Debug) { + return 0; + } + + // printf("SPI_Parameter_file_name = %s\n", + // acr_find_string(group_list, SPI_Parameter_file_name, "")); + // printf("SPI_Order_of_slices = %s\n", + // acr_find_string(group_list, SPI_Order_of_slices, "")); + + printf("%4s %18s\n %15s %8s %6s %8s %8s %3s %3s %3s %3s %3s %3s %4s %4s %4s %5s %16s\n", + "ix","file","study id","date","time","serialno","acq", + "nec","iec","ndy","idy","nsl","isl","acol","rcol","mrow","img#", + "seq"); + + /* ix file stu dat tim sn acq nec iec ndy idy nsl isl */ + printf("%4d: %18s:\n %.6f %8d %6d %8d %8d %3d %3d %3d %3d %3d %3d %4d %4d %4d %5d %16s\n\n", + ix, + info->file_name, + info->study_id, + info->study_date, + info->study_time, + info->scanner_serialno, + info->acq_id, + info->num_echoes, + info->echo_number, + info->num_dyn_scans, + info->dyn_scan_number, + info->num_slices_nominal, + info->slice_number, + info->acq_cols, + info->rec_cols, + info->num_mosaic_rows, + info->global_image_number, + info->sequence_name); + +} + +void usage (void) { + fprintf(stderr, + "\nUsage: dcm2mnc [options] file1 file2 file3 ... destdir\n"); + fprintf(stderr,"\noptions:\n"); + fprintf(stderr," -help : print this informative message\n"); + fprintf(stderr," -list : print list of series (don't create files)\n"); + fprintf(stderr," -anon : exclude subject name from file header\n"); + fprintf(stderr," -descr <str> : use str as session descriptor (default = patient initials)\n"); + fprintf(stderr," -idstr <str> : use str as subject id string (default = patient ID)\n"); + fprintf(stderr," -log <0|1|2|3> : set logging level (default=0)\n"); + fprintf(stderr," -cleanup : delete input files when done (careful!)\n"); + fprintf(stderr," -cmd <prog> : apply prog to output files (e.g. gzip)\n"); + fprintf(stderr," -fork : fork subprocesses to create minc files (now default)\n"); + fprintf(stderr," -nofork : don't fork subprocs, do show progress for individual files\n"); + fprintf(stderr," -debug : print debugging info\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"Files are named according to the following convention:\n\n"); + fprintf(stderr," Directory: descr-idstr-scanner-serialno-date-time/\n"); + fprintf(stderr," Files: descr-idstr-scanner-serialno-date-time-series-modality.mnc\n\n"); + + exit(EXIT_FAILURE); +}
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/dcm2mnc2.c @@ -0,0 +1,764 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dcm2mnc.c +@DESCRIPTION: Program to convert dicom files to minc +@GLOBALS : +@CREATED : June 2001 (Rick Hoge) +@MODIFIED : + * $Log: dcm2mnc2.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.5 2002/04/26 12:02:50 rhoge + * updated usage statement for new forking defaults + * + * Revision 1.4 2002/04/26 11:32:48 rhoge + * made forking default + * + * Revision 1.3 2002/03/23 13:17:53 rhoge + * added support for Bourget network pushed dicom files, cleaned up + * file check and read_numa4_dicom vr check/assignment + * + * Revision 1.2 2002/03/22 19:19:36 rhoge + * Numerous fixes - + * - handle Numaris 4 Dicom patient name + * - option to cleanup input files + * - command option + * - list-only option + * - debug mode + * - user supplied name, idstr + * - anonymization + * + * Revision 1.1 2002/03/22 03:50:02 rhoge + * new name for standalone dicom to minc converter + * + * Revision 1.3 2002/03/22 00:38:08 rhoge + * Added progress bar, wait for children at end, updated feedback statements + * + * Revision 1.2 2002/03/19 13:13:56 rhoge + * initial working mosaic support - I think time is scrambled though. + * + * Revision 1.1 2001/12/31 17:26:21 rhoge + * adding file to repository- compiles without warning and converts non-mosaic + * Numa 4 files. + * Will probably not work for Numa 3 files yet. + * +---------------------------------------------------------------------------- */ + +#ifndef lint +static char rcsid[]="$Header: /private-cvsroot/minc/conversion/dicomserver_sonata/dcm2mnc2.c,v 1.1 2003-08-15 19:52:55 leili Exp $"; +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> +#include "dicomserver.h" + +extern char *minc_history; // Global for minc history +char *pname; // program name +File_Type file_type = UNDEF ; // type of input files +char command_line[512]; +int N4_OFFSET; + +// function prototypes +private int ima_sort_function(const void *entry1, const void *entry2); +private int dcm_sort_function(const void *entry1, const void *entry2); +private int print_file_info( int ix, Data_Object_Info *info); +public int progress(long index, int end, char *message); + +#define EXTREME_LOGGING 10 + +int Do_logging = 0; +int Fork = 1; +int Debug = 0; +int Anon = 0; +int UserIdStr = 0; +char IdStr[512]; +int UserName = 0; +char Name[512]; + +// Do we keep files or are they temporary? +// (now this is overridden by Cleanup) +static int Keep_files = +#ifndef KEEP_FILES + FALSE; +#else + TRUE; +#endif + +// Globals for handling connection timeouts +// (obsolete?) +int Connection_timeout = FALSE; +Acr_File *Alarmed_afp = NULL; + +int main(int argc, char *argv[]) +{ + long ifile; + long max_group; + + Acr_Group group_list; + int exit_status; + char **file_list; + char **acq_file_list; + Data_Object_Info **file_info_list; + Data_Object_Info **acq_file_info_list; + int num_files, num_files_alloc; + int num_acq_files; + FILE *fptemp; + char last_file_name[256]; // delete? + char *project_name = NULL; + int process_files, have_extra_file; + pid_t parent_pid, child_pid; + int statptr; + + int ix; + int UseArgDir = 1; + char OutDir[128]; + int List = 0; + int Cleanup = 0; + + /* Get server process id */ + parent_pid = getpid(); + + /* Create minc history string */ + { + char *string; + string = "dicomserver"; + minc_history = time_stamp(1, &string); + } + + /* get program name */ + + pname = argv[0]; + + if (argc<2) { + usage(); + } + + /* read in all the input pars and file names */ + + for (ix = 1; ix<argc; ix++) { + + if (!strncmp(argv[ix],"-help",5)) { + usage(); + } + else if (!strncmp(argv[ix],"-log",4)) { + ix++; + Do_logging = atoi(argv[ix]); + } + else if (!strncmp(argv[ix],"-fork",5)) { + printf("Forking is now default; -fork option is no longer needed\n"); + Fork = 1; + } + else if (!strncmp(argv[ix],"-nofork",7)) { + Fork = 0; + } + else if (!strncmp(argv[ix],"-debug",4)) { + Debug = 1; + } + else if (!strncmp(argv[ix],"-anon",5)) { + Anon = 1; + } + else if (!strncmp(argv[ix],"-idstr",6)) { + ix++; + strcpy(IdStr,argv[ix]); + UserIdStr = 1; + } + else if (!strncmp(argv[ix],"-descr",6)) { + ix++; + strcpy(Name,argv[ix]); + UserName = 1; + } + else if (!strncmp(argv[ix],"-list",5)) { + List = 1; + } + else if (!strncmp(argv[ix],"-cleanup",6)) { + Cleanup = 1; + } + else if (!strncmp(argv[ix],"-cmd",4)) { + ix++; + strcpy(command_line,argv[ix]); + } + else if (!strncmp(argv[ix],"-",1)) { + (void) fprintf(stderr,"ERROR: input arg `%s' not recognized\n", + argv[ix]); + exit(EXIT_FAILURE); + } + else { /* assume rest of args are the file names and destination */ + + /* Get space for file lists */ + if (List) { + num_files = argc - ix; + } else { + num_files = argc - ix - 1; // last arg is output dir + } + file_list = MALLOC((size_t) num_files * sizeof(*file_list)); + file_info_list = MALLOC(num_files * sizeof(*file_info_list)); + + ifile = 0; + for ( ; ix < argc - 1; ix++) { + file_list[ifile] = strdup(argv[ix]); + ifile++; + } + + if (List) { + // if listing, last input arg will be an input file + file_list[ifile] = strdup(argv[argc-1]); + } else { + // otherwise, last input arg will be output directory + strcpy(OutDir,argv[argc-1]); + (void) strcat(OutDir, "/"); // make sure path ends with slash + } + } + } // end of loop over input args + + // if List, then no point in forking + + if (List) { + Fork = 0; + } + + /* figure out what kind of files we have - + * supported types are: + * + * IMA (Siemens .ima format - Numaris 3.5) + * N4DCM (Siemens DICOM - Numaris 4) + * + * if not all same type, return an error */ + + // note - checking that all files are the same type + // is quite slow so the hard-coded default is now + // to break out of this check once a file type is + // established. Generally users have only one file + // type in a directory. + + // we start by assuming N4DCM with no offset - we find that + // the file is IMA or has an offset (the 128 byte + DICM offset + // seen on Syngo CD's and exports) then the appropriate flag will + // be set. + + printf("Checking file types... "); + + file_type = N4DCM; // default + N4_OFFSET = 0; + + for (ifile = 0; ifile < num_files; ifile++) { + + char dicm_test_string[5]; + + fptemp = fopen(file_list[ifile], "r"); + if (fptemp == NULL) { + fprintf(stderr,"Error opening file %s!\n",file_list[ifile]); + exit(EXIT_FAILURE); + } + + /* Numaris 4 DICOM CD/Export file? if so, bytes 129-132 will + contain the string `DICM' */ + + fseek(fptemp,128,SEEK_SET); + fread(dicm_test_string,1,4,fptemp); + dicm_test_string[4] = (char) '\0'; + + if (!strncmp(dicm_test_string,"DICM",4)) { + + file_type = N4DCM; + N4_OFFSET = 1; + printf("assuming remaining files are Syngo DICOM (CD/Export).\n"); + (void) fclose(fptemp); + break; // break out of file checking loop + + } else if (strstr(file_list[ifile],".ima")!=NULL) { + + file_type = IMA; + printf("assuming remaining files are IMA!\n"); + (void) fclose(fptemp); + break; // break out of file checking loop + + } else { + // assume `regular' dicom (no offset) + file_type = N4DCM; + N4_OFFSET = 0; + printf("assuming remaining files are Syngo DICOM.\n"); + (void) fclose(fptemp); + break; // break out of file checking loop + } // end of file type check + + (void) fclose(fptemp); + } // end of loop over files to check for mixed file types + + // now loop over all files, getting basic info on each + + for (ifile = 0; ifile < num_files; ifile++) { + + char message[20]; + sprintf(message,"Parsing %d files",num_files); + if (!Debug) { + progress(ifile, num_files, message); + } + + // allocate space for the current entry to file_info_list + file_info_list[ifile] = MALLOC(sizeof(*file_info_list[ifile])); + file_info_list[ifile]->file_index = ifile; + + if (file_type == N4DCM) { + + // read up to but not including pixel data + max_group = ACR_ACTUAL_IMAGE_GID - 1; + group_list = read_numa4_dicom(file_list[ifile], max_group); + + } else if (file_type == IMA) { + group_list = siemens_to_dicom(file_list[ifile], TRUE); + if (group_list == NULL) { + fprintf(stderr,"Error reading groups from file %s!\n", + file_list[ifile]); + exit(EXIT_FAILURE); + } + } + + // get some preliminary info from group_list + // (which should have been `corrected' in read_xxxx_dicom + parse_dicom_groups(group_list, file_info_list[ifile]); + + // put the file name into the info list + file_info_list[ifile]->file_name = strdup(file_list[ifile]); + + /* print out info about file */ + print_file_info(ifile,file_info_list[ifile]); + + // Delete the group list now that we're done with it + acr_delete_group_list(group_list); + + } // end of loop over files to get basic info + + printf("Sorting files... "); + + if (file_type == N4DCM) { + // sort the files based on acquisition number + qsort(file_info_list, num_files, sizeof(file_info_list[0]), + dcm_sort_function); + + } else if (file_type == IMA) { + // sort the files based on file name + // (could also use dcm_sort_function, but would have + // to use ACR_Image instead of ACR_Acquisition + qsort(file_list, num_files, sizeof(file_list[0]), + ima_sort_function); + } + + printf("Done sorting files.\n"); + + /* Get space for acquisition file lists */ + num_files_alloc = FILE_ALLOC_INCREMENT; + acq_file_list = MALLOC((size_t) num_files_alloc * sizeof(*acq_file_list)); + acq_file_info_list = MALLOC(num_files_alloc * sizeof(*acq_file_info_list)); + + /* Loop over files, processing by acquisition */ + + if (List) { + printf("Listing files by series...\n"); + } else { + printf("Processing files, one series at a time...\n"); + } + + num_acq_files = 1; + for (ifile = 0; ifile < num_files; ifile++) { + + // Wait for any children that have finished + while ((child_pid=wait3(&statptr, WNOHANG, NULL)) > 0) {} + + // If there are children, slow down the processing + if (child_pid == 0) { + (void) sleep((unsigned int) SERVER_SLEEP_TIME); + } + + /* Set flags indicating whether we should do anything with the files + and whether the file lists contain an extra file */ + process_files = FALSE; + have_extra_file = FALSE; + + /* Extend acquisition file list if necessary */ + if (num_acq_files >= num_files_alloc) { + num_files_alloc = num_acq_files + FILE_ALLOC_INCREMENT; + acq_file_list = REALLOC(acq_file_list, + num_files_alloc * sizeof(*acq_file_list)); + acq_file_info_list = + REALLOC(acq_file_info_list, + num_files_alloc * sizeof(*acq_file_info_list)); + } + acq_file_list[num_acq_files-1] = NULL; + acq_file_info_list[num_acq_files-1] = + MALLOC(sizeof(*acq_file_info_list[num_acq_files-1])); + + acq_file_list[num_acq_files-1] = strdup(file_info_list[ifile]->file_name); + + if (file_type == N4DCM) { + /* read up to but not including pixel data */ + max_group = ACR_ACTUAL_IMAGE_GID - 1; + group_list = read_numa4_dicom(acq_file_list[num_acq_files-1],max_group); + + } else if (file_type == IMA) { + group_list = siemens_to_dicom(acq_file_list[num_acq_files-1], TRUE); + if (group_list == NULL) { + fprintf(stderr,"Error reading groups from file %s!\n", + acq_file_list[num_acq_files-1]); + exit(EXIT_FAILURE); + } + } + parse_dicom_groups(group_list, acq_file_info_list[num_acq_files-1]); + + // put the file name into the info list + acq_file_info_list[num_acq_files-1]->file_name = + strdup(acq_file_list[num_acq_files-1]); + + // print some file info (junk) + print_file_info(num_acq_files,acq_file_info_list[num_acq_files-1]); + + // Check whether we have reached the end of a group of files + if (num_acq_files > 1) { + if ((acq_file_info_list[num_acq_files-1]->study_id != + acq_file_info_list[0]->study_id) || + (acq_file_info_list[num_acq_files-1]->acq_id != + acq_file_info_list[0]->acq_id)) { + + process_files = TRUE; + have_extra_file = TRUE; + } else if (ifile == num_files-1) { + // we're at the last file + process_files = TRUE; + } + } + + // Delete the group list now that we're done with it + acr_delete_group_list(group_list); + + // Use the files if we have a complete acquisition + if (process_files) { + + // Check for file from next acquisition + if (have_extra_file) num_acq_files--; + + if (List) { + + printf("Series %4d: %30s (%4d files)\n", + acq_file_info_list[0]->acq_id, + acq_file_info_list[0]->protocol_name, + num_acq_files); + + } else { + + printf("Converting data for series %d (%s: %d files)...\n", + acq_file_info_list[0]->acq_id, + acq_file_info_list[0]->protocol_name, + num_acq_files); + } + + if (Fork) { + // Fork child to process the files + child_pid = fork(); + } else { + child_pid = 0; + } + + if (child_pid > 0) { // Parent process + // printf("[Parent]: Forked process to create minc file.\n"); + } // Error forking + else if (child_pid < 0) { + fprintf(stderr, + "Error forking child to create minc file\n"); + return; + } + else { // Child process + + if (!List) { + + // process the files (same function as server) + use_the_files(project_name, num_acq_files, acq_file_list, + acq_file_info_list,UseArgDir,OutDir); + + // Print message about child finishing + printf("-File creation complete for Series %d.\n", + acq_file_info_list[0]->acq_id); + + // Clean up files, if needed + if (Cleanup) { + if (num_acq_files > 0) { + printf("-Removing input files... "); + cleanup_files(num_acq_files, acq_file_list); + printf("Done removing input files.\n"); + } + } + } + + if (Fork) { + // Exit from child, if forked + exit(EXIT_SUCCESS); + } + + } // End of child process + + // ---------------------------------------------------- + // if we get here we must be the parent, who goes + // along happily continuing to eat files + // ---------------------------------------------------- + + // Reset the lists for a new series + free_list(num_acq_files, acq_file_list, acq_file_info_list); + + // check to see if the last series ended by + // running into a new series: + if (have_extra_file) { + // move most recent file info to first entry in array, + // in preparation for next series + + // note that num_acq_files has already been decremented, + // so we do not need to subtract 1 to convert to array index + acq_file_list[0] = acq_file_list[num_acq_files]; + acq_file_info_list[0] = acq_file_info_list[num_acq_files]; + + acq_file_list[num_acq_files] = NULL; + acq_file_info_list[num_acq_files] = NULL; + } + // num_acq_files = (have_extra_file ? 1 : 0); + num_acq_files = (have_extra_file ? 2 : 1); + } else { + // we're not processing the files yet - just increment counter + num_acq_files++; + } + } // end of loop over files + + // Wait for child processes if we've been forking. + // We sleep in the checking loop to reduce parent CPU usage + if (Fork) { + printf("-(waiting for child processes to finish...)\n"); + while ((child_pid=wait3(&statptr, WNOHANG, NULL)) >= 0) {sleep(1);} + } + + if (List) { + printf("Done listing files.\n"); + } else { + printf("Done processing files.\n"); + } + + /* Save name of first file in last set */ + if ((num_acq_files > 0) && (acq_file_list[0] != NULL)) { + last_file_name[sizeof(last_file_name) - 1] = '\0'; + (void) strncpy(last_file_name,acq_file_list[0],sizeof(last_file_name)-1); + } + else { + last_file_name[0] = '\0'; + } + + FREE(acq_file_list); + FREE(acq_file_info_list); + + /* Print final message */ + + exit_status = EXIT_SUCCESS; + + exit(exit_status); + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : cleanup_files +@INPUT : num_files - number of files in list + file_list - array of file names +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Removes files. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void cleanup_files(int num_files, char *file_list[]) +{ + int i; + + // if (Keep_files) return; + + for (i=0; i < num_files; i++) { + if (file_list[i] != NULL) { + (void) remove(file_list[i]); + } + } + + return; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : free_list +@INPUT : num_files - number of files in list + file_list - array of file names +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free + the arrays themselves. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void free_list(int num_files, char **file_list, + Data_Object_Info **file_info_list) +{ + int i; + + for (i=0; i < num_files; i++) { + if (file_list[i] != NULL) { + FREE(file_list[i]); + } + if (file_info_list[i] != NULL) { + FREE(file_info_list[i]); + } + } + + return; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : ima_sort_function +@INPUT : entry1 + entry2 +@OUTPUT : (none) +@RETURNS : -1, 0, 1 for lt, eq, gt +@DESCRIPTION: Function to compare two ima file names +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : June 2001 (Rick Hoge) +@MODIFIED : +---------------------------------------------------------------------------- */ +private int ima_sort_function(const void *entry1, const void *entry2) +{ + + char * const *value1 = entry1; + char * const *value2 = entry2; + + int session1,series1,image1; + int session2,series2,image2; + + sscanf(*value1,"%d-%d-%d.ima",&session1,&series1,&image1); + sscanf(*value2,"%d-%d-%d.ima",&session2,&series2,&image2); + + if (session1 < session2) return -1; + else if (session1 > session2) return 1; + else if (series1 < series2) return -1; + else if (series1 > series2) return 1; + else if (image1 < image2) return -1; + else if (image1 > image2) return 1; + else return 0; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dcm_sort_function +@INPUT : entry1 + entry2 +@OUTPUT : (none) +@RETURNS : -1, 0, 1 for lt, eq, gt +@DESCRIPTION: Function to compare two dcm series numbers +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : June 2001 (Rick Hoge) +@MODIFIED : +---------------------------------------------------------------------------- */ +private int dcm_sort_function(const void *entry1, const void *entry2) +{ + + Data_Object_Info **file_info_list1 = (Data_Object_Info **) entry1; + Data_Object_Info **file_info_list2 = (Data_Object_Info **) entry2; + + // make a sort-able session ID number: date.time + double session1 = (*file_info_list1)->study_date + + (*file_info_list1)->study_time / 1e6; + double session2 = (*file_info_list2)->study_date + + (*file_info_list2)->study_time / 1e6; + + // series index + int series1 = (*file_info_list1)->acq_id; + int series2 = (*file_info_list2)->acq_id; + + // frame index + int frame1 = (*file_info_list1)->dyn_scan_number; + int frame2 = (*file_info_list2)->dyn_scan_number; + + // image index + int image1 = (*file_info_list1)->global_image_number; + int image2 = (*file_info_list2)->global_image_number; + + if (session1 < session2) return -1; + else if (session1 > session2) return 1; + else if (series1 < series2) return -1; + else if (series1 > series2) return 1; + else if (frame1 < frame2) return -1; + else if (frame1 > frame2) return 1; + else if (image1 < image2) return -1; + else if (image1 > image2) return 1; + else return 0; +} + +private int print_file_info( int ix, Data_Object_Info *info) { + + if (!Debug) { + return 0; + } + + // printf("SPI_Parameter_file_name = %s\n", + // acr_find_string(group_list, SPI_Parameter_file_name, "")); + // printf("SPI_Order_of_slices = %s\n", + // acr_find_string(group_list, SPI_Order_of_slices, "")); + + printf("%4s %18s\n %15s %8s %6s %8s %8s %3s %3s %3s %3s %3s %3s %4s %4s %4s %5s %16s\n", + "ix","file","study id","date","time","serialno","acq", + "nec","iec","ndy","idy","nsl","isl","acol","rcol","mrow","img#", + "seq"); + + /* ix file stu dat tim sn acq nec iec ndy idy nsl isl */ + printf("%4d: %18s:\n %.6f %8d %6d %8d %8d %3d %3d %3d %3d %3d %3d %4d %4d %4d %5d %16s\n\n", + ix, + info->file_name, + info->study_id, + info->study_date, + info->study_time, + info->scanner_serialno, + info->acq_id, + info->num_echoes, + info->echo_number, + info->num_dyn_scans, + info->dyn_scan_number, + info->num_slices_nominal, + info->slice_number, + info->acq_cols, + info->rec_cols, + info->num_mosaic_rows, + info->global_image_number, + info->sequence_name); + +} + +void usage (void) { + fprintf(stderr, + "\nUsage: dcm2mnc [options] file1 file2 file3 ... destdir\n"); + fprintf(stderr,"\noptions:\n"); + fprintf(stderr," -help : print this informative message\n"); + fprintf(stderr," -list : print list of series (don't create files)\n"); + fprintf(stderr," -anon : exclude subject name from file header\n"); + fprintf(stderr," -descr <str> : use str as session descriptor (default = patient initials)\n"); + fprintf(stderr," -idstr <str> : use str as subject id string (default = patient ID)\n"); + fprintf(stderr," -log <0|1|2|3> : set logging level (default=0)\n"); + fprintf(stderr," -cleanup : delete input files when done (careful!)\n"); + fprintf(stderr," -cmd <prog> : apply prog to output files (e.g. gzip)\n"); + fprintf(stderr," -fork : fork subprocesses to create minc files (now default)\n"); + fprintf(stderr," -nofork : don't fork subprocs, do show progress for individual files\n"); + fprintf(stderr," -debug : print debugging info\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"Files are named according to the following convention:\n\n"); + fprintf(stderr," Directory: descr-idstr-scanner-serialno-date-time/\n"); + fprintf(stderr," Files: descr-idstr-scanner-serialno-date-time-series-modality.mnc\n\n"); + + exit(EXIT_FAILURE); +}
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/dicom_element_defs.c @@ -0,0 +1,23 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dicom_element_defs.c +@DESCRIPTION: Element definitions for DICOM +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : +@COPYRIGHT : + Copyright 1997 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. +---------------------------------------------------------------------------- */ + +#define GLOBAL_ELEMENT_DEFINITION + +#include <dicomserver.h>
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/dicom_element_defs.h @@ -0,0 +1,154 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dicom_element_defs.h +@DESCRIPTION: Element definitions for dicom +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : +@COPYRIGHT : + Copyright 1997 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. +---------------------------------------------------------------------------- */ + +/* Define standard UID's */ +#define FAVORITE_ABSTRACT_SYNTAX ACR_MR_IMAGE_STORAGE_UID +#define ACR_MR_IMAGE_STORAGE_UID "1.2.840.10008.5.1.4.1.1.4" +#define ACR_EXPLICIT_VR_BIG_END_UID "1.2.840.10008.1.2.2" +#define ACR_EXPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2.1" +#define ACR_IMPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2" +#define ACR_APPLICATION_CONTEXT_UID "1.2.840.10008.3.1.1.1" + +/* Define constants for accepting association */ +#define ACR_ASSOC_RJ_CALLED_AP_TITLE_UNREC 7 +#define ACR_ASSOC_RJ_NO_REASON 1 +#define ACR_ASSOC_RJ_PERM 1 +#define ACR_ASSOC_RJ_USER 1 +#define ACR_ASSOC_PR_CN_ACCEPT 0 +#define ACR_ASSOC_PR_CN_REJECT 1 +#define ACR_PDU_ITEM_USER_INFORMATION 0x50 + +/* Define group numbers */ +#define ACR_MESSAGE_GID 0 +#define ACR_ACTUAL_IMAGE_GID 0x7fe0 + +/* Define commands */ +#define ACR_C_STORE_RQ 0x0001 +#define ACR_C_STORE_RSP 0x8001 +#define ACR_C_ECHO_RQ 0x0030 +#define ACR_C_ECHO_RSP 0x8030 + +/* Define dataset type */ +#define ACR_NULL_DATASET 0x0101 + +/* Define status codes */ +#define ACR_SUCCESS 0x0000 + +/* Define data object types */ +#define ACR_IMAGE_OBJECT 0x0000 +#define ACR_OTHER_OBJECT 0x0100 + +/* Define acr-nema constants */ +#define ACR_MODALITY_MR "MR" + +/* Element id's for DICOM */ +GLOBAL_ELEMENT(ACR_Affected_SOP_class_UID , 0x0000, 0x0002, UI); +GLOBAL_ELEMENT(ACR_Command , 0x0000, 0x0100, US); +GLOBAL_ELEMENT(ACR_Message_id , 0x0000, 0x0110, US); +GLOBAL_ELEMENT(ACR_Message_id_brt , 0x0000, 0x0120, US); +GLOBAL_ELEMENT(ACR_Priority , 0x0000, 0x0700, US); +GLOBAL_ELEMENT(ACR_Dataset_type , 0x0000, 0x0800, US); +GLOBAL_ELEMENT(ACR_Status , 0x0000, 0x0900, US); +GLOBAL_ELEMENT(ACR_Affected_SOP_instance_UID , 0x0000, 0x1000, UI); +GLOBAL_ELEMENT(ACR_Move_originator_AE_title , 0x0000, 0x1031, AE); + +GLOBAL_ELEMENT(ACR_Image_type , 0x0008, 0x0008, CS); +GLOBAL_ELEMENT(ACR_Study_date , 0x0008, 0x0020, DA); +GLOBAL_ELEMENT(ACR_Series_date , 0x0008, 0x0021, DA); +GLOBAL_ELEMENT(ACR_Acquisition_date , 0x0008, 0x0022, DA); +GLOBAL_ELEMENT(ACR_Study_time , 0x0008, 0x0030, TM); +GLOBAL_ELEMENT(ACR_Series_time , 0x0008, 0x0031, TM); +GLOBAL_ELEMENT(ACR_Acquisition_time , 0x0008, 0x0032, TM); +GLOBAL_ELEMENT(ACR_Modality , 0x0008, 0x0060, CS); +GLOBAL_ELEMENT(ACR_Manufacturer , 0x0008, 0x0070, LO); +GLOBAL_ELEMENT(ACR_Institution_id , 0x0008, 0x0080, LO); +GLOBAL_ELEMENT(ACR_Referring_physician , 0x0008, 0x0090, PN); +GLOBAL_ELEMENT(ACR_Station_id , 0x0008, 0x1010, SH); +GLOBAL_ELEMENT(ACR_Procedure_description , 0x0008, 0x1030, LO); +GLOBAL_ELEMENT(ACR_Performing_physician , 0x0008, 0x1050, PN); +GLOBAL_ELEMENT(ACR_Operators_name , 0x0008, 0x1070, PN); +GLOBAL_ELEMENT(ACR_Manufacturer_model , 0x0008, 0x1090, LO); + +GLOBAL_ELEMENT(ACR_Patient_name , 0x0010, 0x0010, PN); +GLOBAL_ELEMENT(ACR_Patient_identification, 0x0010, 0x0020, LO); +GLOBAL_ELEMENT(ACR_Patient_birth_date , 0x0010, 0x0030, DA); +GLOBAL_ELEMENT(ACR_Patient_sex , 0x0010, 0x0040, CS); +GLOBAL_ELEMENT(ACR_Patient_age , 0x0010, 0x1010, AS); +GLOBAL_ELEMENT(ACR_Patient_weight , 0x0010, 0x1030, DS); + +GLOBAL_ELEMENT(ACR_Scanning_sequence , 0x0018, 0x0020, CS); +GLOBAL_ELEMENT(ACR_MR_acquisition_type , 0x0018, 0x0023, CS); +GLOBAL_ELEMENT(ACR_Sequence_name , 0x0018, 0x0024, CS); +GLOBAL_ELEMENT(ACR_Slice_thickness , 0x0018, 0x0050, DS); +GLOBAL_ELEMENT(ACR_Repetition_time , 0x0018, 0x0080, DS); +GLOBAL_ELEMENT(ACR_Echo_time , 0x0018, 0x0081, DS); +GLOBAL_ELEMENT(ACR_Inversion_time , 0x0018, 0x0082, DS); +GLOBAL_ELEMENT(ACR_Nr_of_averages , 0x0018, 0x0083, DS); +GLOBAL_ELEMENT(ACR_Imaging_frequency , 0x0018, 0x0084, DS); +GLOBAL_ELEMENT(ACR_Imaged_nucleus , 0x0018, 0x0085, SH); +GLOBAL_ELEMENT(ACR_Echo_number , 0x0018, 0x0086, IS); +GLOBAL_ELEMENT(ACR_Magnetic_field_strength,0x0018, 0x0087, DS); +GLOBAL_ELEMENT(ACR_Spacing_between_slices, 0x0018, 0x0088, DS); +GLOBAL_ELEMENT(ACR_Number_of_phase_encoding_steps, 0x0018, 0x0089, IS); +GLOBAL_ELEMENT(ACR_Echo_train_length , 0x0018, 0x0091, IS); +GLOBAL_ELEMENT(ACR_Percent_sampling , 0x0018, 0x0093, DS); +GLOBAL_ELEMENT(ACR_Percent_phase_field_of_view, 0x0018, 0x0094, DS); +GLOBAL_ELEMENT(ACR_Pixel_bandwidth , 0x0018, 0x0095, DS); +GLOBAL_ELEMENT(ACR_Device_serial_number , 0x0018, 0x1000, LO); +GLOBAL_ELEMENT(ACR_Software_versions , 0x0018, 0x1020, LO); +GLOBAL_ELEMENT(ACR_Protocol_name , 0x0018, 0x1030, LO); +GLOBAL_ELEMENT(ACR_Receiving_coil , 0x0018, 0x1250, SH); +GLOBAL_ELEMENT(ACR_Transmitting_coil , 0x0018, 0x1251, SH); +GLOBAL_ELEMENT(ACR_Acquisition_matrix , 0x0018, 0x1310, US); +GLOBAL_ELEMENT(ACR_Phase_encoding_direction, 0x0018, 0x1312, CS); +GLOBAL_ELEMENT(ACR_Flip_angle , 0x0018, 0x1314, DS); +GLOBAL_ELEMENT(ACR_SAR , 0x0018, 0x1316, DS); +GLOBAL_ELEMENT(ACR_Acq_comments , 0x0018, 0x4000, LT); +GLOBAL_ELEMENT(ACR_Patient_position , 0x0018, 0x5100, CS); + +GLOBAL_ELEMENT(ACR_Study , 0x0020, 0x0010, SH); +GLOBAL_ELEMENT(ACR_Series , 0x0020, 0x0011, IS); +GLOBAL_ELEMENT(ACR_Acquisition , 0x0020, 0x0012, IS); +GLOBAL_ELEMENT(ACR_Image , 0x0020, 0x0013, IS); +GLOBAL_ELEMENT(ACR_Image_position_patient, 0x0020, 0x0032, DS); +GLOBAL_ELEMENT(ACR_Image_orientation_patient, + 0x0020, 0x0037, DS); +GLOBAL_ELEMENT(ACR_Acquisitions_in_series, 0x0020, 0x1001, IS); + +GLOBAL_ELEMENT(ACR_Rows , 0x0028, 0x0010, US); +GLOBAL_ELEMENT(ACR_Columns , 0x0028, 0x0011, US); +GLOBAL_ELEMENT(ACR_Pixel_size , 0x0028, 0x0030, DS); +GLOBAL_ELEMENT(ACR_Bits_allocated , 0x0028, 0x0100, US); +GLOBAL_ELEMENT(ACR_Bits_stored , 0x0028, 0x0101, US); +GLOBAL_ELEMENT(ACR_Smallest_pixel_value , 0x0028, 0x0106, US); +GLOBAL_ELEMENT(ACR_Largest_pixel_value , 0x0028, 0x0107, US); +GLOBAL_ELEMENT(ACR_Image_location , 0x0028, 0x0200, US); +GLOBAL_ELEMENT(ACR_Window_centre , 0x0028, 0x1050, DS); +GLOBAL_ELEMENT(ACR_Window_width , 0x0028, 0x1051, DS); + +//GLOBAL_ELEMENT(ACR_Pixel_data, ACR_ACTUAL_IMAGE_GID, 0x0010, UNKNOWN); +GLOBAL_ELEMENT(ACR_Pixel_data , 0x7fe0, 0x0010, OW); + +#include <spi_element_defs.h> +#include <ext_element_defs.h> + + + +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/dicom_prototypes.h @@ -0,0 +1,82 @@ +public void timeout_handler(int sig); +public Acr_Group skip_command_groups(Acr_Group group_list); +public void cleanup_files(int num_files, char *file_list[]); +public void free_list(int num_files, char **file_list, + Data_Object_Info **file_info_list); +public int create_minc_file(char *minc_file, int clobber, + General_Info *general_info, + char *file_prefix, char **output_file_name, + Loop_Type loop_type); +public void setup_minc_variables(int mincid, General_Info *general_info, + Loop_Type loop_type); +public void save_minc_image(int icvid, General_Info *general_info, + File_Info *file_info, Image_Data *image); +public void close_minc_file(int icvid); +public void open_connection(int argc, char *argv[], + Acr_File **afpin, Acr_File **afpout); +public int read_project_file(char *project_name, + char *file_prefix, + int *output_uid, int *output_gid, + char *command_line, int maxlen_command); +public void get_project_option_string(char *project_option_string, + int maxlen_project_option); +public Acr_Message associate_reply(Acr_Message input_message, + char **project_name, + int *pres_context_id, + Acr_byte_order *byte_order, + Acr_VR_encoding_type *vr_encoding, + long *maximum_length); +public Acr_Message associate_reply_reject(Acr_Message input_message, + int reason); +public Acr_Message release_reply(Acr_Message input_message); +public Acr_Message abort_reply(Acr_Message input_message); +public Acr_Message data_reply(Acr_Message input_message); +public void save_transferred_object(Acr_Group group_list, char *file_prefix, + char **new_file_name, + Data_Object_Info *data_info); +public void get_file_info(Acr_Group group_list, File_Info *file_info, + General_Info *general_info); +public void get_identification_info(Acr_Group group_list, + double *study_id, int *acq_id, + int *rec_num, int *image_type); +public void get_intensity_info(Acr_Group group_list, File_Info *file_info); +public void get_coordinate_info(Acr_Group group_list, File_Info *file_info, + Orientation *orientation, + World_Index volume_to_world[VOL_NDIMS], + int sizes[VOL_NDIMS], + double dircos[VOL_NDIMS][WORLD_NDIMS], + double steps[VOL_NDIMS], + double starts[VOL_NDIMS], + double coordinate[WORLD_NDIMS]); +public void convert_numa3_coordinate(double coordinate[WORLD_NDIMS]); +public void convert_dicom_coordinate(double coordinate[WORLD_NDIMS]); +public void get_general_header_info(Acr_Group group_list, + General_Info *general_info); +public double convert_time_to_seconds(double dicom_time); +public void get_siemens_dicom_image(Acr_Group group_list, Image_Data *image); +public int siemens_dicom_to_minc(int num_files, char *file_list[], + char *minc_file, int clobber, + char *file_prefix, char **output_file_name); +public Acr_Group read_siemens_dicom(char *filename, int max_group); +public void free_info(General_Info *general_info, File_Info *file_info, + int num_files); +public int search_list(int value, int list[], int list_length, + int starting_point); +public void usage(void); +public void sort_dimensions(General_Info *general_info); +public int dimension_sort_function(const void *v1, const void *v2); +public void string_to_filename(char *string, char *filename, int maxlen); +public void use_the_files(char *project_name, + int num_files, char *file_list[], + Data_Object_Info *data_info[], + int UseArgDir,char *OutDir); +public Acr_Group siemens_to_dicom(char *filename, int read_image); + +// MGH specific stuff +public void string_to_initials(char *string, char *filename, int maxlen); + +// Numaris 4 specific stuff +public Acr_Group read_numa4_dicom(char *filename, int max_group); +public char *prot_find_string(Acr_Element Protocol, char *Field); +public char *dump_protocol_text(Acr_Element Protocol); +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/dicomreader.c @@ -0,0 +1,639 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dicomreader.c +@DESCRIPTION: Program to convert dicom files to minc +@GLOBALS : +@CREATED : June 2001 (Rick Hoge) +@MODIFIED : + * $Log: dicomreader.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.3 2002/03/22 00:38:08 rhoge + * Added progress bar, wait for children at end, updated feedback statements + * + * Revision 1.2 2002/03/19 13:13:56 rhoge + * initial working mosaic support - I think time is scrambled though. + * + * Revision 1.1 2001/12/31 17:26:21 rhoge + * adding file to repository- compiles without warning and converts non-mosaic + * Numa 4 files. + * Will probably not work for Numa 3 files yet. + * +---------------------------------------------------------------------------- */ + +#ifndef lint +static char rcsid[]="$Header: /private-cvsroot/minc/conversion/dicomserver_sonata/dicomreader.c,v 1.1 2003-08-15 19:52:55 leili Exp $"; +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> +#include "dicomserver.h" + +extern char *minc_history; /* Global for minc history */ +char *pname; /* program name */ +File_Type file_type = UNDEF ; /* type of input files */ + +/* function prototypes */ +private int ima_sort_function(const void *entry1, const void *entry2); +private int dcm_sort_function(const void *entry1, const void *entry2); +private int print_file_info( int ix, Data_Object_Info *info); +public int progress(long index, int end, char *message); + +#define EXTREME_LOGGING 10 /* rhoge */ + +/* Do we do logging? */ +int Do_logging = 0; +int NoFork = 0; + +/* Do we keep files or are they temporary? */ +static int Keep_files = +#ifndef KEEP_FILES + FALSE; +#else + TRUE; +#endif + +/* Globals for handling connection timeouts */ +int Connection_timeout = FALSE; +Acr_File *Alarmed_afp = NULL; + +int main(int argc, char *argv[]) +{ + long ifile; + long max_group; + + Acr_Group group_list; + int exit_status; + char **file_list; + char **acq_file_list; + Data_Object_Info **file_info_list; + Data_Object_Info **acq_file_info_list; + int num_files, num_files_alloc; + int num_acq_files; + FILE *fptemp; + char last_file_name[256]; // delete? + char *project_name = NULL; + int process_files, have_extra_file; + pid_t parent_pid, child_pid; + int statptr; + + /* added by rhoge */ + int ix; + int UseArgDir = 1; + char OutDir[128]; + + /* Get server process id */ + parent_pid = getpid(); + + /* Create minc history string */ + { + char *string; + string = "dicomserver"; + minc_history = time_stamp(1, &string); + } + + /* get program name */ + + pname = argv[0]; + + if (argc<2) { + usage(); + } + + /* read in all the input pars and file names */ + + for (ix = 1; ix<argc; ix++) { + + if (!strncmp(argv[ix],"-help",5)) { + usage(); + } + else if (!strncmp(argv[ix],"-log",4)) { + ix++; + Do_logging = atoi(argv[ix]); + } + else if (!strncmp(argv[ix],"-nofork",5)) { + NoFork = 1; + } + else if (!strncmp(argv[ix],"-",1)) { + (void) fprintf(stderr,"ERROR: input arg `%s' not recognized\n", + argv[ix]); + exit(EXIT_FAILURE); + } + else { /* assume rest of args are the file names and destination */ + + /* Get space for file lists */ + num_files = argc - ix - 1; + file_list = MALLOC((size_t) num_files * sizeof(*file_list)); + file_info_list = MALLOC(num_files * sizeof(*file_info_list)); + + ifile = 0; + for ( ; ix < argc - 1; ix++) { + file_list[ifile] = strdup(argv[ix]); + ifile++; + } + strcpy(OutDir,argv[argc-1]); + (void) strcat(OutDir, "/"); /* make sure path ends with slash */ + } + } // end of loop over input args + + /* figure out what kind of files we have - + * supported types are: + * + * SIEMENS_SPI (Siemens .ima format - Numaris 3.5) + * SIEMENS_DCM (Siemens DICOM - Numaris 4) + * + * if not all same type, return an error */ + + printf("Checking file types... "); + + for (ifile = 0; ifile < num_files; ifile++) { + + char dicm_test_string[5]; + + fptemp = fopen(file_list[ifile], "r"); + if (fptemp == NULL) { + fprintf(stderr,"Error opening file %s!\n",file_list[ifile]); + exit(EXIT_FAILURE); + } + + /* Numaris 4 DICOM file? if so, bytes 129-132 will + contain the string `DICM' */ + + fseek(fptemp,128,SEEK_SET); + fread(dicm_test_string,1,4,fptemp); + dicm_test_string[4] = (char) '\0'; + + if (!strncmp(dicm_test_string,"DICM",4)) { + if (file_type == IMA) { + fprintf(stderr,"ERROR: mixed file types\n"); + exit(EXIT_FAILURE); + } else { + file_type = N4DCM; + // should make check of all files optional + if (1) { + printf("assuming remaining files are N4DICOM!\n"); + break; // break out of file checking loop + } else { + progress(ifile, num_files, "Checking file types"); + } + } + } else if (strstr(file_list[ifile],".ima")!=NULL) { + if (file_type == N4DCM) { + fprintf(stderr,"ERROR: mixed file types\n"); + exit(EXIT_FAILURE); + } else { + file_type = IMA; + } + } else { + fprintf(stderr,"ERROR: File %s is unkown type\n",file_list[ifile]); + exit(EXIT_FAILURE); + } // end of file type check + + (void) fclose(fptemp); + } // end of loop over files to check for mixed file types + + // now loop over all files, getting basic info on each + + for (ifile = 0; ifile < num_files; ifile++) { + + char message[20]; + sprintf(message,"Parsing %d files",num_files); + progress(ifile, num_files, message); + + // allocate space for the current entry to file_info_list + file_info_list[ifile] = MALLOC(sizeof(*file_info_list[ifile])); + file_info_list[ifile]->file_index = ifile; + + if (file_type == N4DCM) { + + // read up to but not including pixel data + max_group = ACR_ACTUAL_IMAGE_GID - 1; + group_list = read_numa4_dicom(file_list[ifile], max_group); + + } else if (file_type == IMA) { + group_list = siemens_to_dicom(file_list[ifile], TRUE); + if (group_list == NULL) { + fprintf(stderr,"Error reading groups from file %s!\n", + file_list[ifile]); + exit(EXIT_FAILURE); + } + } + + // get some preliminary info from group_list + // (which should have been `corrected' in read_xxxx_dicom + parse_dicom_groups(group_list, file_info_list[ifile]); + + // put the file name into the info list + file_info_list[ifile]->file_name = strdup(file_list[ifile]); + + /* print out info about file */ + print_file_info(ifile,file_info_list[ifile]); + + // Delete the group list now that we're done with it + acr_delete_group_list(group_list); + + } // end of loop over files to get basic info + + printf("Sorting files... "); + + if (file_type == N4DCM) { + // sort the files based on acquisition number + qsort(file_info_list, num_files, sizeof(file_info_list[0]), + dcm_sort_function); + + } else if (file_type == IMA) { + // sort the files based on file name + // (could also use dcm_sort_function, but would have + // to use ACR_Image instead of ACR_Acquisition + qsort(file_list, num_files, sizeof(file_list[0]), + ima_sort_function); + } + + printf("Done sorting files.\n"); + + /* Get space for acquisition file lists */ + num_files_alloc = FILE_ALLOC_INCREMENT; + acq_file_list = MALLOC((size_t) num_files_alloc * sizeof(*acq_file_list)); + acq_file_info_list = MALLOC(num_files_alloc * sizeof(*acq_file_info_list)); + + /* Loop over files, processing by acquisition */ + + printf("Processing files, one series at a time...\n"); + + num_acq_files = 1; + for (ifile = 0; ifile < num_files; ifile++) { + + // Wait for any children that have finished + while ((child_pid=wait3(&statptr, WNOHANG, NULL)) > 0) {} + + // If there are children, slow down the processing + if (child_pid == 0) { + (void) sleep((unsigned int) SERVER_SLEEP_TIME); + } + + /* Set flags indicating whether we should do anything with the files + and whether the file lists contain an extra file */ + process_files = FALSE; + have_extra_file = FALSE; + + /* Extend acquisition file list if necessary */ + if (num_acq_files >= num_files_alloc) { + num_files_alloc = num_acq_files + FILE_ALLOC_INCREMENT; + acq_file_list = REALLOC(acq_file_list, + num_files_alloc * sizeof(*acq_file_list)); + acq_file_info_list = + REALLOC(acq_file_info_list, + num_files_alloc * sizeof(*acq_file_info_list)); + } + acq_file_list[num_acq_files-1] = NULL; + acq_file_info_list[num_acq_files-1] = + MALLOC(sizeof(*acq_file_info_list[num_acq_files-1])); + + acq_file_list[num_acq_files-1] = strdup(file_info_list[ifile]->file_name); + + if (file_type == N4DCM) { + /* read up to but not including pixel data */ + max_group = ACR_ACTUAL_IMAGE_GID - 1; + group_list = read_numa4_dicom(acq_file_list[num_acq_files-1],max_group); + + } else if (file_type == IMA) { + group_list = siemens_to_dicom(acq_file_list[num_acq_files-1], TRUE); + if (group_list == NULL) { + fprintf(stderr,"Error reading groups from file %s!\n", + acq_file_list[num_acq_files-1]); + exit(EXIT_FAILURE); + } + } + parse_dicom_groups(group_list, acq_file_info_list[num_acq_files-1]); + + // put the file name into the info list + acq_file_info_list[num_acq_files-1]->file_name = + strdup(acq_file_list[num_acq_files-1]); + + // print some file info (junk) + print_file_info(num_acq_files,acq_file_info_list[num_acq_files-1]); + + // Check whether we have reached the end of a group of files + if (num_acq_files > 1) { + if ((acq_file_info_list[num_acq_files-1]->study_id != + acq_file_info_list[0]->study_id) || + (acq_file_info_list[num_acq_files-1]->acq_id != + acq_file_info_list[0]->acq_id)) { + + process_files = TRUE; + have_extra_file = TRUE; + } else if (ifile == num_files-1) { + // we're at the last file + process_files = TRUE; + } + } + + // Delete the group list now that we're done with it + acr_delete_group_list(group_list); + + // Use the files if we have a complete acquisition + if (process_files) { + + // Check for file from next acquisition + if (have_extra_file) num_acq_files--; + + printf("Converting data for series %d (%s: %d files)...\n", + acq_file_info_list[0]->acq_id, + acq_file_info_list[0]->protocol_name, + num_acq_files); + + if (NoFork) { + child_pid = 0; + } else { + // Fork child to process the files + child_pid = fork(); + } + + if (child_pid > 0) { // Parent process + // printf("[Parent]: Forked process to create minc file.\n"); + } // Error forking + else if (child_pid < 0) { + fprintf(stderr, + "Error forking child to create minc file\n"); + return; + } + else { // Child process + + // process the files (same function as server) + use_the_files(project_name, num_acq_files, acq_file_list, + acq_file_info_list,UseArgDir,OutDir); + + // Print message about child finishing + if (NoFork) { + //printf("[Parent]: Minc creation process finished.\n"); + printf("File creation complete for Series %d.\n", + acq_file_info_list[0]->acq_id); + } else { + //printf("[Child]: Minc creation process finished.\n"); + printf(" File creation complete for Series %d.\n", + acq_file_info_list[0]->acq_id); + } + + if (!NoFork) { + // Exit from child, if forked + exit(EXIT_SUCCESS); + } + + } // End of child process + + // ---------------------------------------------------- + // if we get here we must be the parent, who goes + // along happily continuing to eat files + // ---------------------------------------------------- + + // Reset the lists for a new series + free_list(num_acq_files, acq_file_list, acq_file_info_list); + + // check to see if the last series ended by + // running into a new series: + if (have_extra_file) { + // move most recent file info to first entry in array, + // in preparation for next series + + // note that num_acq_files has already been decremented, + // so we do not need to subtract 1 to convert to array index + acq_file_list[0] = acq_file_list[num_acq_files]; + acq_file_info_list[0] = acq_file_info_list[num_acq_files]; + + acq_file_list[num_acq_files] = NULL; + acq_file_info_list[num_acq_files] = NULL; + } + // num_acq_files = (have_extra_file ? 1 : 0); + num_acq_files = (have_extra_file ? 2 : 1); + } else { + // we're not processing the files yet - just increment counter + num_acq_files++; + } + } // end of loop over files + + // Wait for child processes if we've been forking. + // We sleep in the checking loop to reduce parent CPU usage + if (!NoFork) { + printf(" (waiting for child processes to finish...)\n"); + while ((child_pid=wait3(&statptr, WNOHANG, NULL)) >= 0) {sleep(1);} + } + printf("Done processing files.\n"); + + /* Save name of first file in last set */ + if ((num_acq_files > 0) && (acq_file_list[0] != NULL)) { + last_file_name[sizeof(last_file_name) - 1] = '\0'; + (void) strncpy(last_file_name,acq_file_list[0],sizeof(last_file_name)-1); + } + else { + last_file_name[0] = '\0'; + } + + /* Clean up files, if needed */ + if (0) { + if (num_acq_files > 0) { + cleanup_files(num_acq_files, acq_file_list); + free_list(num_acq_files, acq_file_list, acq_file_info_list); + num_acq_files = 0; + } + } + FREE(acq_file_list); + FREE(acq_file_info_list); + + /* Print final message */ + + exit_status = EXIT_SUCCESS; + + exit(exit_status); + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : cleanup_files +@INPUT : num_files - number of files in list + file_list - array of file names +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Removes files. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void cleanup_files(int num_files, char *file_list[]) +{ + int i; + + if (Keep_files) return; + + for (i=0; i < num_files; i++) { + if (file_list[i] != NULL) { + (void) remove(file_list[i]); + } + } + + return; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : free_list +@INPUT : num_files - number of files in list + file_list - array of file names +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free + the arrays themselves. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void free_list(int num_files, char **file_list, + Data_Object_Info **file_info_list) +{ + int i; + + for (i=0; i < num_files; i++) { + if (file_list[i] != NULL) { + FREE(file_list[i]); + } + if (file_info_list[i] != NULL) { + FREE(file_info_list[i]); + } + } + + return; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : ima_sort_function +@INPUT : entry1 + entry2 +@OUTPUT : (none) +@RETURNS : -1, 0, 1 for lt, eq, gt +@DESCRIPTION: Function to compare two ima file names +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : June 2001 (Rick Hoge) +@MODIFIED : +---------------------------------------------------------------------------- */ +private int ima_sort_function(const void *entry1, const void *entry2) +{ + + char * const *value1 = entry1; + char * const *value2 = entry2; + + int session1,series1,image1; + int session2,series2,image2; + + sscanf(*value1,"%d-%d-%d.ima",&session1,&series1,&image1); + sscanf(*value2,"%d-%d-%d.ima",&session2,&series2,&image2); + + if (session1 < session2) return -1; + else if (session1 > session2) return 1; + else if (series1 < series2) return -1; + else if (series1 > series2) return 1; + else if (image1 < image2) return -1; + else if (image1 > image2) return 1; + else return 0; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dcm_sort_function +@INPUT : entry1 + entry2 +@OUTPUT : (none) +@RETURNS : -1, 0, 1 for lt, eq, gt +@DESCRIPTION: Function to compare two dcm series numbers +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : June 2001 (Rick Hoge) +@MODIFIED : +---------------------------------------------------------------------------- */ +private int dcm_sort_function(const void *entry1, const void *entry2) +{ + + Data_Object_Info **file_info_list1 = (Data_Object_Info **) entry1; + Data_Object_Info **file_info_list2 = (Data_Object_Info **) entry2; + + // make a sort-able session ID number: date.time + double session1 = (*file_info_list1)->study_date + + (*file_info_list1)->study_time / 1e6; + double session2 = (*file_info_list2)->study_date + + (*file_info_list2)->study_time / 1e6; + + // series index + int series1 = (*file_info_list1)->acq_id; + int series2 = (*file_info_list2)->acq_id; + + // frame index + int frame1 = (*file_info_list1)->dyn_scan_number; + int frame2 = (*file_info_list2)->dyn_scan_number; + + // image index + int image1 = (*file_info_list1)->global_image_number; + int image2 = (*file_info_list2)->global_image_number; + + if (session1 < session2) return -1; + else if (session1 > session2) return 1; + else if (series1 < series2) return -1; + else if (series1 > series2) return 1; + else if (frame1 < frame2) return -1; + else if (frame1 > frame2) return 1; + else if (image1 < image2) return -1; + else if (image1 > image2) return 1; + else return 0; +} + +private int print_file_info( int ix, Data_Object_Info *info) { + + return 0; + + // printf("SPI_Parameter_file_name = %s\n", + // acr_find_string(group_list, SPI_Parameter_file_name, "")); + // printf("SPI_Order_of_slices = %s\n", + // acr_find_string(group_list, SPI_Order_of_slices, "")); + + printf("%4s %18s %15s %8s %6s %8s %8s %3s %3s %3s %3s %3s %3s %4s %4s %4s %5s %16s\n", + "ix","file","study id","date","time","serialno","acq", + "nec","iec","ndy","idy","nsl","isl","acol","rcol","mrow","img#", + "seq"); + + /* ix file stu dat tim sn acq nec iec ndy idy nsl isl */ + printf("%4d: %18s %.6f %8d %6d %8d %8d %3d %3d %3d %3d %3d %3d %4d %4d %4d %5d %16s\n\n", + ix, + info->file_name, + info->study_id, + info->study_date, + info->study_time, + info->scanner_serialno, + info->acq_id, + info->num_echoes, + info->echo_number, + info->num_dyn_scans, + info->dyn_scan_number, + info->num_slices_nominal, + info->slice_number, + info->acq_cols, + info->rec_cols, + info->num_mosaic_rows, + info->global_image_number, + info->sequence_name); + +} + +void usage (void) { + fprintf(stderr, + "\nUsage: dicomreader [options] file1 file2 file3 ... destdir\n"); + fprintf(stderr,"\noptions:\n"); + fprintf(stderr," -help : print this informative message\n"); + fprintf(stderr," -log <0|1|2|3> : set logging level (default=0)\n"); + fprintf(stderr," -nofork : don't fork to create minc files\n"); + exit(EXIT_FAILURE); +}
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/dicomserver-debug.c @@ -0,0 +1,12 @@ +#if 0 +# define DO_INPUT_TRACING +#endif + +#if 1 +# define KEEP_FILES +#endif + +#if 0 +# define DO_HIGH_LOGGING +#endif +#include "dicomserver-nondebug.c"
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/dicomserver-nondebug.c @@ -0,0 +1,926 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dicomserver.c +@DESCRIPTION: Program to receive images from Siemens Vision. +@GLOBALS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + +Modified by R. Hoge Feb. 2000 to handle acquisition loop dynamic scans +on Siemens Sonata system + + * $Log: dicomserver-nondebug.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.9 2002/03/22 19:19:36 rhoge + * Numerous fixes - + * - handle Numaris 4 Dicom patient name + * - option to cleanup input files + * - command option + * - list-only option + * - debug mode + * - user supplied name, idstr + * - anonymization + * + * Revision 1.8 2001/12/31 18:27:21 rhoge + * modifications for dicomreader processing of Numaris 4 dicom files - at + * this point code compiles without warning, but does not deal with + * mosaiced files. Also will probably not work at this time for Numaris + * 3 .ima files. dicomserver may also not be functional... + * + * Revision 1.7 2001/08/29 20:55:53 rhoge + * added -help option + * + * Revision 1.6 2001/07/19 17:43:46 rhoge + * added -nofork command line option to prevent forking + * + * Revision 1.5 2001/02/26 13:37:59 rhoge + * redirect sderr to /dev/null if no logging - prevents hangups due to + * printfs dumping text into connection stream? + * + * Revision 1.4 2001/02/26 06:17:42 rhoge + * changed tmp dir assignment to be consistent, always uses stdio.h stuff. + * also added command-line flags for destination dir, logging level, and + * retention of dicom files (are put in dest dir - must be specified) + * + * Revision 1.3 2000/12/14 21:14:58 rhoge + * added macro (NO_FORK) to prevent forking if uncommented + * + * Revision 1.2 2000/12/13 13:22:18 rhoge + * experimental changes to forking code for minc file creation - should + * be no change + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * + * Revision 6.1 1999/10/29 17:51:55 neelin + * Fixed Log keyword + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.2 1997/07/10 17:35:35 neelin + * Changed error handling and fixed message deletion. + * + * Revision 4.1 1997/07/08 23:15:09 neelin + * Added support for C_ECHO command. + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.2 1997/03/11 13:10:48 neelin + * Working version of dicomserver. + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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. +---------------------------------------------------------------------------- */ + +#ifndef lint +static char rcsid[]="$Header: /private-cvsroot/minc/conversion/dicomserver_sonata/dicomserver-nondebug.c,v 1.1 2003-08-15 19:52:55 leili Exp $"; +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> +#include <dicomserver.h> + +/* Global for minc history */ +extern char *minc_history; + +/* State of server. Note that DISCONNECTING is used for a high-level + protocol error and TERMINATING is used for a low-level error or + end of input */ +typedef enum { + WAITING_FOR_ASSOCIATION, WAITING_FOR_DATA, DISCONNECTING, TERMINATING +} Server_state; + +/* stuff added by rhoge */ + +#define EXTREME_LOGGING 10 /* rhoge */ + +/* Do we do logging? */ +/********************/ +//int Do_logging = 0; /* Belong to rhoge, commented out by leili*/ +/*added by Leili:*/ +int Do_logging = +#ifndef DO_HIGH_LOGGING + LOW_LOGGING; +# else + HIGH_LOGGING; +# endif +/******************/ + +/* Changed by leili from False to True */ +int Fork = TRUE; +/*****************/ + +/* Do we keep files or are they temporary? */ +static int Keep_files = +#ifndef KEEP_FILES + FALSE; +#else + TRUE; +#endif +/******************/ +/* added by Leili */ +/* In what directory do we run? */ +static char *run_dir = "/data/fmri/transfer/tmp"; +/******************/ + +/* Globals for handling connection timeouts */ +int Connection_timeout = FALSE; +Acr_File *Alarmed_afp = NULL; +File_Type file_type = N4DCM ; /* type of input files */ +int N4_OFFSET; +char *pname; +int main(int argc, char *argv[]) +{ + char *pname; + Acr_File *afpin, *afpout; + Acr_Status status; + Server_state state; + int acr_command; + Acr_Group group_list; + Acr_Message input_message, output_message; + int exit_status; + char exit_string[256]; + char **file_list; + Data_Object_Info **file_info_list; + int num_files, num_files_alloc; + static char file_prefix_string[L_tmpnam+1] = "dicomserver"; + char *file_prefix = file_prefix_string; + //Added by Leili + char *file_prefix2 = "/usr/local/dicom_keep/"; + char *temp_dir; + int continue_looping; + FILE *fptemp; + char last_file_name[256]; + char *project_name = NULL; + char logfilename[256]; + int pdu_type; + int process_files, have_extra_file; + Acr_byte_order byte_order; + Acr_VR_encoding_type vr_encoding; + int pres_context_id; + long maximum_length; + pid_t server_pid, child_pid; + int statptr; + + /* added by rhoge */ + int ix; + int UseArgDir = 0; + char OutDir[128]; + + /* Get server process id */ + server_pid = getpid(); + + /*************************/ + + /* Change to tmp directory - + note that this will be default file destination if no aetitle + or command-line specification are received (rhoge) */ + /* commented out by leili */ + + //(void) chdir(P_tmpdir); + + /* added by leili */ + /* change to tmp directory */ + if (run_dir != NULL) { + (void) chdir(run_dir); + } + /*****************************/ + + /* Create minc history string */ + { + char *string; + string = "dicomserver"; + minc_history = time_stamp(1, &string); + } + + /* read in all the input pars and file names */ + + for (ix = 1; ix<argc; ix++) { + + if (!strncmp(argv[ix],"-destdir",8)) { + ix++; + UseArgDir = 1; + strcpy(OutDir,argv[ix]); + (void) strcat(OutDir, "/"); /* make sure path ends with slash */ + } + else if (!strncmp(argv[ix],"-log",4)) { + ix++; + Do_logging = atoi(argv[ix]); + } + else if (!strncmp(argv[ix],"-fork",5)) { + Fork = TRUE; + } + else if (!strncmp(argv[ix],"-keep_dcm",9)) { + Keep_files = TRUE; + } + else if (!strncmp(argv[ix],"-help",9)) { + (void) fprintf(stderr,"Usage: dicomserver [<options>]\nOptions:\n\t-log <n>\n\t-fork\n\t-keep_dcm\n\t-help\n"); + exit(EXIT_SUCCESS); + } + else { + (void) fprintf(stderr,"Usage: dicomserver [<options>]\nOptions:\n\t-log <n>\n\t-fork\n\t-keep_dcm\n\t-help\n"); + exit(EXIT_FAILURE); + } + } + + /* Re-open stderr if we are logging */ + if (Do_logging > NO_LOGGING) { + (void) sprintf(logfilename, "/data/fmri/transfer/logs/dicomserver-%d.log", + (int) getpid()); + (void) freopen(logfilename, "w", stderr); + setbuf(stderr, NULL); + } else { + (void) sprintf(logfilename, "/dev/null"); + (void) freopen(logfilename, "w", stderr); + setbuf(stderr, NULL); + } + + // set up type of connection (Syngo?) + file_type = N4DCM; // default + N4_OFFSET = 0; + + /* Print message at start */ + pname = argv[0]; + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "%s: Started dicom server.\n", pname); + } + + /* Make connection */ + open_connection(argc, argv, &afpin, &afpout); + + + /* Check that the connection was made */ + if ((afpin == NULL) || (afpout == NULL)) { + (void) fprintf(stderr, "%s: Error opening connection.\n", pname); + exit(EXIT_FAILURE); + } + + + + /* Print connection message */ + if (Do_logging >= HIGH_LOGGING) { + (void) fprintf(stderr, "%s: Connection accepted.\n", pname); + } + +#ifdef DO_INPUT_TRACING + /* Enable input tracing */ + acr_dicom_enable_trace(afpin); + acr_dicom_enable_trace(afpout); +#endif + + /* Create file prefix. Create the temporary file to avoid file name + clashes */ + /*****************************/ + /* Commented out by leili */ + //temp_dir = NULL; + //if ( !Keep_files) { + // (void) tmpnam(file_prefix); + // if (mkdir(file_prefix, (mode_t) 0777)) { + // (void) fprintf(stderr, + // "%s: Unable to create directory for temporary files.\n", + // pname); + // perror(pname); + // exit(EXIT_FAILURE); + // } + // temp_dir = strdup(file_prefix); + // (void) strcat(file_prefix, "/dicom"); + //} + + // else { + // file_prefix=strdup(OutDir); + + // if (mkdir(file_prefix, (mode_t) 0777)) { + // (void) fprintf(stderr, + // "Directory %s exists...\n",file_prefix); + // } + // temp_dir = strdup(file_prefix); + // (void) strcat(file_prefix, "/dicom"); + // } + /****************************/ + /* Added by Leili */ + temp_dir = NULL; + if (! Keep_files){ + //temp_dir = tempnam(run_dir, NULL); + temp_dir = tempnam(run_dir, NULL); + if(mkdir(temp_dir, (mode_t) 0777)){ + (void) fprintf(stderr, + "%s: Unable to create directory for temporary files.\n", + pname); + perror(pname); + exit(EXIT_FAILURE); + } + (void)strcpy(file_prefix, temp_dir); + (void) strcat(file_prefix, "/"); + //(void) strcat(file_prefix, "/dicom"); + } + /*****************************/ + (void) fprintf(stderr, "About to begin the space allocation \n"); /* Leili */ + + /* Get space for file lists */ + num_files_alloc = FILE_ALLOC_INCREMENT; + file_list = MALLOC((size_t) num_files_alloc * sizeof(*file_list)); + file_info_list = MALLOC(num_files_alloc * sizeof(*file_info_list)); + + + /* Loop while reading messages */ + state = WAITING_FOR_ASSOCIATION; + continue_looping = TRUE; + num_files = 0; + + (void) fprintf(stderr, "About to start reading a message \n"); /* Leili */ + while (continue_looping) { + + /* Wait for any children that have finished */ + while ((child_pid=wait3(&statptr, WNOHANG, NULL)) > 0) {} + + /* If there are children, slow down the processing */ + (void) fprintf(stderr, "If we have children, slow down \n"); /* Leili */ + if (child_pid == 0) { + (void) sleep((unsigned int) SERVER_SLEEP_TIME); + } + (void) fprintf(stderr, "Start reading the message\n"); /* Leili */ + /* Read in the message */ + Alarmed_afp = afpin; + (void) signal(SIGALRM, timeout_handler); + (void) alarm(CONNECTION_TIMEOUT); + + if (Do_logging > HIGH_LOGGING) + fprintf(stderr,"\nWaiting for dicom message...\n"); /* rhoge */ + + /* chokes here at end of asynchronous transfer */ + status=acr_input_dicom_message(afpin, &input_message); + (void) fprintf(stderr, "The status of the message is: %s \n", acr_status_string(status)); /* Leili */ + + if (Do_logging > HIGH_LOGGING) { /* rhoge */ + fprintf(stderr,"\nGot message.\n"); + fprintf(stderr,"\nCalling `alarm'...\n"); + } + + (void) alarm(0); + + if (Do_logging > HIGH_LOGGING) + fprintf(stderr,"\nAlarm called.\n"); /* rhoge */ + + (void) fprintf(stderr,"Checking for an error \n"); /* Leili */ + /* Check for error */ + if (status != ACR_OK) { + continue_looping = FALSE; + state = TERMINATING; + break; + } + + /* Set flags indicating whether we should do anything with the files + and whether the file lists contain an extra file */ + process_files = FALSE; + have_extra_file = FALSE; + /* Get group list */ + group_list = acr_get_message_group_list(input_message); + + /* Get PDU type. Default is data transfer */ + pdu_type = acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF); + (void) fprintf(stderr,"We got the pdu and it is: %d\n", pdu_type); /* Leili */ + + + /* Deal with PDU state */ + switch (pdu_type) { + + /* Associate request */ + case ACR_PDU_ASSOC_RQ: + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\n Associate request is ACR_PDU_ASSOC_RQ\n"); + + if (state != WAITING_FOR_ASSOCIATION) { + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + } + + /* Work out reply and get connection info */ + (void) fprintf(stderr,"About to go to reply program\n"); /* Leili */ + output_message = associate_reply(input_message, &project_name, + &pres_context_id, &byte_order, + &vr_encoding, &maximum_length); + (void) fprintf(stderr,"We finished with the reply program\n"); /* Leili */ + + /* Modify the input and output streams according to the + connection info */ + acr_set_byte_order(afpin, byte_order); + acr_set_vr_encoding(afpin, vr_encoding); + acr_set_byte_order(afpout, byte_order); + acr_set_vr_encoding(afpout, vr_encoding); + acr_set_dicom_pres_context_id(afpout, pres_context_id); + acr_set_dicom_maximum_length(afpout, maximum_length); + + /* Get ready for files */ + num_files = 0; + state = WAITING_FOR_DATA; + + break; + + /* Release */ + case ACR_PDU_REL_RQ: + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\nAssociate request is ACR_PDU_REL_RQ\n"); + + if (state != WAITING_FOR_DATA) { + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + } + output_message = release_reply(input_message); + state = TERMINATING; + process_files = TRUE; + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\nReceived release request; process flag set.\n"); + + break; + + /* Abort */ + case ACR_PDU_ABORT_RQ: + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\nAssociate request is ACR_PDU_ABORT_RQ\n"); + + output_message = abort_reply(input_message); + state = TERMINATING; + break; + + /* Data transfer */ + case ACR_PDU_DATA_TF: + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\nAssociate request is ACR_PDU_DATA_TF\n"); + + /* Check state */ + if (state != WAITING_FOR_DATA) { + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + } + + /* Check command and compose a reply */ + acr_command = acr_find_short(group_list, ACR_Command, -1); + switch (acr_command) { + case ACR_C_STORE_RQ: + case ACR_C_ECHO_RQ: + output_message = data_reply(input_message); + break; + default: + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + } + + /* Carry on only if we have a store command */ + if (acr_command != ACR_C_STORE_RQ) break; + + /* Get rid of the command groups */ + group_list = skip_command_groups(group_list); + + /* Was the data attached to the command? If not, read in the next + message - it should contain the data */ + if (group_list == NULL) { + + /* Delete the previous message */ + if (input_message != NULL) + acr_delete_message(input_message); + + /* Read the data and check the status */ + Alarmed_afp = afpin; + (void) signal(SIGALRM, timeout_handler); + (void) alarm(CONNECTION_TIMEOUT); + status=acr_input_dicom_message(afpin, &input_message); + (void) alarm(0); + if (status != ACR_OK) { + state = DISCONNECTING; + break; + } + + /* Check that we have a data PDU */ + group_list = acr_get_message_group_list(input_message); + if (acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF) + != ACR_PDU_DATA_TF) { + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + } + + /* Skip command groups and check for no data */ + group_list = skip_command_groups(group_list); + if (group_list == NULL) break; + + } + (void) fprintf(stderr,"End of while loop, we are out of the loop \n"); /* Leili */ + + /* Extend file list if necessary */ + if (num_files >= num_files_alloc) { + num_files_alloc = num_files + FILE_ALLOC_INCREMENT; + file_list = REALLOC(file_list, + num_files_alloc * sizeof(*file_list)); + file_info_list = + REALLOC(file_info_list, + num_files_alloc * sizeof(*file_info_list)); + } + file_list[num_files] = NULL; + file_info_list[num_files] = + MALLOC(sizeof(*file_info_list[num_files])); + + /* Save the object */ + //save_transferred_object(group_list, + // file_prefix, &file_list[num_files], + // file_info_list[num_files]); + /* Added by leili */ + save_transferred_object(group_list, + file_prefix2, &file_list[num_files], + file_info_list[num_files]); + + (void) fprintf(stderr,"Finished saving transfered object number %d \n", (num_files)+1); /* Leili */ + num_files++; + + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, " Copied %s\n", file_list[num_files-1]); + } + (void) fprintf(stderr,"Number of files are: %d \n", num_files); /* Leili */ + + if (Do_logging > HIGH_LOGGING) { /* rhoge */ + fprintf(stderr, + "About to check if we're done file group\n"); + + fprintf(stderr,"\nnum_files: \t%d",num_files); + fprintf(stderr,"\nfile_info_list[num_files-1]->study_id: \t%.6f", + file_info_list[num_files-1]->study_id); + fprintf(stderr,"\nfile_info_list[0]->study_id: \t%.6f", + file_info_list[0]->study_id); + fprintf(stderr,"\nfile_info_list[num_files-1]->acq_id: \t%d", + file_info_list[num_files-1]->acq_id); + fprintf(stderr,"\nfile_info_list[0]->acq_id: \t%d", + file_info_list[0]->acq_id); + + } + + /* Check whether we have reached the end of a group of files */ + if (num_files > 1) { + if ((file_info_list[num_files-1]->study_id != + file_info_list[0]->study_id) || + (file_info_list[num_files-1]->acq_id != + file_info_list[0]->acq_id)) { + process_files = TRUE; + have_extra_file = TRUE; + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\nend of file group detected, process flag set.\n"); + } + } + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\ndone checking for end of file group\n"); + + break; + + /* Unknown command */ + default: + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\nAssociate request is default (an error)\n"); + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + + } /* End of switch on pdu_type */ + + (void) fprintf(stderr,"We break and came out since we didn't have the store command\n"); /* Leili */ + + /* Delete input message */ + if (input_message != NULL) + acr_delete_message(input_message); + + /* Use the files if we have a complete acquisition */ + if (process_files) { + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\nEntered the `process files' block...\n"); + + /* Log the fact */ + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "\nCopied one acquisition.\n"); + } + + /* Check for file from next acquisition */ + if (have_extra_file) num_files--; + + /* uncomment to prevent forking */ + /* #define NO_FORK */ + + if (Fork) { + /* Fork child to process the files */ + child_pid = fork(); + } else { + child_pid = 0; + } + + if (child_pid > 0) { /* Parent process */ + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, + "Forked process to create minc files.\n"); + } + + } /* Error forking */ + else if (child_pid < 0) { + (void) fprintf(stderr, + "Error forking child to create minc file\n"); + return; + } + else { /* Child process */ + /*******************************************/ + /* Added by Leili */ + /* Close file descriptors to avoid buffering problems. + STDERR is sometimes left open, therefore this command + maybe needed */ + if(Fork){ + int fd; + for(fd=getdtablesize()-1; fd>= 0; fd--) { + if(fd != 2){ + (void) close(fd); + } + } + } + /******************************************/ + /* Do something with the files */ + (void) fprintf(stderr, "We are about to do something with the files.\n"); /* Leili */ + (void) fprintf(stderr, "The project name is:%s\n", project_name); /* Leili */ + (void) fprintf(stderr, "The pname is:%s\n", pname); /* Leili */ + use_the_files(project_name, num_files, file_list, + file_info_list,UseArgDir,OutDir); + + + /* Remove the temporary files */ + cleanup_files(num_files, file_list); + + /* Remove the temporary directory if the server has finished */ + /* (this does not seem to work all the time) */ + if ((temp_dir != NULL) && (kill(server_pid, 0) != 0)) { + cleanup_files(1, &temp_dir); + } + + /* Print message about child finishing */ + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "Minc creation process finished.\n"); + } + + if (Fork) { + /* Exit from child */ + exit(EXIT_SUCCESS); + } + + } /* End of child process */ + + /* Put blank line in log file */ + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "\n"); + } + + /* Reset the lists */ + free_list(num_files, file_list, file_info_list); + if (have_extra_file) { + file_list[0] = file_list[num_files]; + file_info_list[0] = file_info_list[num_files]; + file_list[num_files] = NULL; + file_info_list[num_files] = NULL; + } + num_files = (have_extra_file ? 1 : 0); + } // end of if(process files) + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\n just passed `process files' loop\n"); + + /* Check for disconnection */ + if (state == DISCONNECTING) { + continue_looping = FALSE; + break; + } + + /* Send reply */ + Alarmed_afp = afpout; + (void) signal(SIGALRM, timeout_handler); + (void) alarm(CONNECTION_TIMEOUT); + status = acr_output_dicom_message(afpout, output_message); + (void) alarm(0); + + /* Delete output message */ + if (output_message != NULL) + acr_delete_message(output_message); + + if (status != ACR_OK) { + state = TERMINATING; + break; + } + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\nbottom of loop over messages\n"); + + } /* End of loop over messages */ + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\njust out of loop over messages\n"); + + /* Free the input and output streams */ + acr_close_dicom_file(afpin); + acr_close_dicom_file(afpout); + + /* Save name of first file in last set transferred */ + if ((num_files > 0) && (file_list[0] != NULL)) { + last_file_name[sizeof(last_file_name) - 1] = '\0'; + (void) strncpy(last_file_name, file_list[0], sizeof(last_file_name)-1); + } + else { + last_file_name[0] = '\0'; + } + + /* Clean up files, if needed */ + if (num_files > 0) { + cleanup_files(num_files, file_list); + free_list(num_files, file_list, file_info_list); + num_files = 0; + } + FREE(file_list); + FREE(file_info_list); + + /* Remove the file prefix directory (this only happens if it is empty). */ + cleanup_files(1, &temp_dir); + FREE(temp_dir); + + /* Check for connection timeout */ + if (Connection_timeout) { + (void) fprintf(stderr, "Connection timed out.\n"); + } + + /* Print final message */ + if ((status == ACR_OK) || (status == ACR_END_OF_INPUT)) { + (void) sprintf(exit_string, "Finished transfer."); + exit_status = EXIT_SUCCESS; + } + else { + (void) sprintf(exit_string, "%s. Disconnecting.", + acr_status_string(status)); + exit_status = EXIT_FAILURE; + } + (void) fprintf(stderr, "The logging set to be %d\n", Do_logging); /* Leili */ + (void) fprintf(stderr, "The status of the message is: %s \n", acr_status_string(status)); /* Leili */ + + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "\n%s: %s\n", pname, exit_string); + } + + if ((status != ACR_OK) && (status != ACR_END_OF_INPUT)) { + if (SYSTEM_LOG != NULL) { + if ((fptemp = fopen(SYSTEM_LOG, "w")) != NULL) { + if ((int) strlen(last_file_name) > 0) { + (void) fprintf(fptemp, "%s: File \"%s\"\n", + pname, last_file_name); + } + (void) fprintf(fptemp, "%s: %s\n", pname, exit_string); + (void) fclose(fptemp); + } + } + } + + /* Free the project_name string */ + if (project_name != NULL) FREE(project_name); + + exit(exit_status); + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : timeout_handler +@INPUT : +@OUTPUT : (none) +@RETURNS : +@DESCRIPTION: Routine to handle connection timeouts. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : March 10, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +/* ARGSUSED */ +public void timeout_handler(int sig) +{ + Connection_timeout = TRUE; + if (Alarmed_afp != NULL) { + acr_dicom_set_eof(Alarmed_afp); + } + return; +} +/* ----------------------------- MNI Header ----------------------------------- +@NAME : skip_command_groups +@INPUT : group_list +@OUTPUT : (none) +@RETURNS : Pointer to head of group list +@DESCRIPTION: Skips over command groups in a group list, returning the rest + of the list. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : March 7, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public Acr_Group skip_command_groups(Acr_Group group_list) +{ + while ((group_list != NULL) && + ((acr_get_group_group(group_list) == DCM_PDU_GRPID) || + (acr_get_group_group(group_list) == ACR_MESSAGE_GID))) { + group_list = acr_get_group_next(group_list); + } + + return group_list; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : cleanup_files +@INPUT : num_files - number of files in list + file_list - array of file names +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Removes files. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void cleanup_files(int num_files, char *file_list[]) +{ + int i; + + if (Keep_files) return; + + for (i=0; i < num_files; i++) { + if (file_list[i] != NULL) { + (void) remove(file_list[i]); + } + } + + return; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : free_list +@INPUT : num_files - number of files in list + file_list - array of file names +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free + the arrays themselves. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void free_list(int num_files, char **file_list, + Data_Object_Info **file_info_list) +{ + int i; + + for (i=0; i < num_files; i++) { + if (file_list[i] != NULL) { + FREE(file_list[i]); + } + if (file_info_list[i] != NULL) { + FREE(file_info_list[i]); + } + } + + return; +} +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/dicomserver-nondebug2.c @@ -0,0 +1,1080 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dicomserver.c +@DESCRIPTION: Program to receive images from Siemens Vision. +@GLOBALS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + +Modified by R. Hoge Feb. 2000 to handle acquisition loop dynamic scans +on Siemens Sonata system + + * $Log: dicomserver-nondebug2.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.9 2002/03/22 19:19:36 rhoge + * Numerous fixes - + * - handle Numaris 4 Dicom patient name + * - option to cleanup input files + * - command option + * - list-only option + * - debug mode + * - user supplied name, idstr + * - anonymization + * + * Revision 1.8 2001/12/31 18:27:21 rhoge + * modifications for dicomreader processing of Numaris 4 dicom files - at + * this point code compiles without warning, but does not deal with + * mosaiced files. Also will probably not work at this time for Numaris + * 3 .ima files. dicomserver may also not be functional... + * + * Revision 1.7 2001/08/29 20:55:53 rhoge + * added -help option + * + * Revision 1.6 2001/07/19 17:43:46 rhoge + * added -nofork command line option to prevent forking + * + * Revision 1.5 2001/02/26 13:37:59 rhoge + * redirect sderr to /dev/null if no logging - prevents hangups due to + * printfs dumping text into connection stream? + * + * Revision 1.4 2001/02/26 06:17:42 rhoge + * changed tmp dir assignment to be consistent, always uses stdio.h stuff. + * also added command-line flags for destination dir, logging level, and + * retention of dicom files (are put in dest dir - must be specified) + * + * Revision 1.3 2000/12/14 21:14:58 rhoge + * added macro (NO_FORK) to prevent forking if uncommented + * + * Revision 1.2 2000/12/13 13:22:18 rhoge + * experimental changes to forking code for minc file creation - should + * be no change + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * + * Revision 6.1 1999/10/29 17:51:55 neelin + * Fixed Log keyword + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.2 1997/07/10 17:35:35 neelin + * Changed error handling and fixed message deletion. + * + * Revision 4.1 1997/07/08 23:15:09 neelin + * Added support for C_ECHO command. + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.2 1997/03/11 13:10:48 neelin + * Working version of dicomserver. + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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. +---------------------------------------------------------------------------- */ + +#ifndef lint +static char rcsid[]="$Header: /private-cvsroot/minc/conversion/dicomserver_sonata/dicomserver-nondebug2.c,v 1.1 2003-08-15 19:52:55 leili Exp $"; +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <dicomserver.h> + +/* Global for minc history */ +extern char *minc_history; + +/* State of server. Note that DISCONNECTING is used for a high-level + protocol error and TERMINATING is used for a low-level error or + end of input */ +typedef enum { + WAITING_FOR_ASSOCIATION, WAITING_FOR_DATA, DISCONNECTING, TERMINATING +} Server_state; + +/* stuff added by rhoge */ + +#define EXTREME_LOGGING 10 /* rhoge */ + + +/* Do we do logging? */ +/********************/ +//int Do_logging = 0; /* Belong to rhoge, commented out by leili*/ +/*added by Leili:*/ +int Do_logging = +#ifndef DO_HIGH_LOGGING + LOW_LOGGING; +# else + HIGH_LOGGING; +# endif +/******************/ + +/* Changed by leili from False to True */ +int Fork = TRUE; +/*****************/ + +/* Do we keep files or are they temporary? */ +static int Keep_files = +#ifndef KEEP_FILES + FALSE; +#else + TRUE; +#endif +/******************/ +/* added by Leili */ +/* In what directory do we run? */ +static char *run_dir = "/var/tmp"; +/******************/ + +/* Globals for handling connection timeouts */ +int Connection_timeout = FALSE; +Acr_File *Alarmed_afp = NULL; +File_Type file_type = N4DCM ; /* type of input files */ +int N4_OFFSET; +char *pname; +int main(int argc, char *argv[]) +{ + char *pname; + Acr_File *afpin, *afpout; + Acr_Status status; + Server_state state; + int acr_command; + Acr_Group group_list; + Acr_Message input_message, output_message; + int exit_status; + char exit_string[256]; + char **file_list; + Data_Object_Info **file_info_list; + int num_files, num_files_alloc; + static char file_prefix_string[L_tmpnam+1] = "dicomserver"; + char *file_prefix = file_prefix_string; + char *temp_dir; + int continue_looping; + FILE *fptemp; + char last_file_name[256]; + char *project_name = NULL; + char logfilename[256]; + int pdu_type; + int process_files, have_extra_file; + Acr_byte_order byte_order; + Acr_VR_encoding_type vr_encoding; + int pres_context_id; + long maximum_length; + pid_t server_pid, child_pid; + int statptr; + + /* added by rhoge */ + int ix; + int UseArgDir = 0; + char OutDir[128]; + /******************/ + /* Added by Leili */ + long ifile; + Acr_Element element; + char model_name[256]; + char patient_name[256]; + char patient_id[256]; + char reg_time[256]; + char reg_date[256]; + char out_dir[256]; + char temp_name[256]; + + /***********************/ + /* Get server process id */ + server_pid = getpid(); + + /*************************/ + + /* Change to tmp directory - + note that this will be default file destination if no aetitle + or command-line specification are received (rhoge) */ + /* commented out by leili */ + + //(void) chdir(P_tmpdir); + + /* added by leili */ + /* change to tmp directory */ + if (run_dir != NULL) { + (void) chdir(run_dir); + } + /*****************************/ + + /* Create minc history string */ + { + char *string; + string = "dicomserver"; + minc_history = time_stamp(1, &string); + } + + /* read in all the input pars and file names */ + + for (ix = 1; ix<argc; ix++) { + + if (!strncmp(argv[ix],"-destdir",8)) { + ix++; + UseArgDir = 1; + strcpy(OutDir,argv[ix]); + (void) strcat(OutDir, "/"); /* make sure path ends with slash */ + } + else if (!strncmp(argv[ix],"-log",4)) { + ix++; + Do_logging = atoi(argv[ix]); + } + else if (!strncmp(argv[ix],"-fork",5)) { + Fork = TRUE; + } + else if (!strncmp(argv[ix],"-keep_dcm",9)) { + Keep_files = TRUE; + } + else if (!strncmp(argv[ix],"-help",9)) { + (void) fprintf(stderr,"Usage: dicomserver [<options>]\nOptions:\n\t-log <n>\n\t-fork\n\t-keep_dcm\n\t-help\n"); + exit(EXIT_SUCCESS); + } + else { + (void) fprintf(stderr,"Usage: dicomserver [<options>]\nOptions:\n\t-log <n>\n\t-fork\n\t-keep_dcm\n\t-help\n"); + exit(EXIT_FAILURE); + } + } + + /* Re-open stderr if we are logging */ + if (Do_logging > NO_LOGGING) { + (void) sprintf(logfilename, "dicomserver-%d.log", + (int) getpid()); + (void) freopen(logfilename, "w", stderr); + setbuf(stderr, NULL); + } else { + (void) sprintf(logfilename, "/dev/null"); + (void) freopen(logfilename, "w", stderr); + setbuf(stderr, NULL); + } + + // set up type of connection (Syngo?) + file_type = N4DCM; // default + N4_OFFSET = 0; + + /* Print message at start */ + pname = argv[0]; + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "%s: Started dicom server.\n", pname); + } + + /* Make connection */ + open_connection(argc, argv, &afpin, &afpout); + + + /* Check that the connection was made */ + if ((afpin == NULL) || (afpout == NULL)) { + (void) fprintf(stderr, "%s: Error opening connection.\n", pname); + exit(EXIT_FAILURE); + } + + + + /* Print connection message */ + if (Do_logging >= HIGH_LOGGING) { + (void) fprintf(stderr, "%s: Connection accepted.\n", pname); + } + +#ifdef DO_INPUT_TRACING + /* Enable input tracing */ + acr_dicom_enable_trace(afpin); + acr_dicom_enable_trace(afpout); +#endif + + /* Create file prefix. Create the temporary file to avoid file name + clashes */ + /*****************************/ + /* Commented out by leili */ + //temp_dir = NULL; + //if ( !Keep_files) { + // (void) tmpnam(file_prefix); + // if (mkdir(file_prefix, (mode_t) 0777)) { + // (void) fprintf(stderr, + // "%s: Unable to create directory for temporary files.\n", + // pname); + // perror(pname); + // exit(EXIT_FAILURE); + // } + // temp_dir = strdup(file_prefix); + // (void) strcat(file_prefix, "/dicom"); + //} + + // else { + // file_prefix=strdup(OutDir); + + // if (mkdir(file_prefix, (mode_t) 0777)) { + // (void) fprintf(stderr, + // "Directory %s exists...\n",file_prefix); + // } + // temp_dir = strdup(file_prefix); + // (void) strcat(file_prefix, "/dicom"); + // } + /****************************/ + /* Added by Leili */ + temp_dir = NULL; + if (! Keep_files){ + temp_dir = tempnam(NULL, NULL); + if(mkdir(temp_dir, (mode_t) 0777)){ + (void) fprintf(stderr, + "%s: Unable to create directory for temporary files.\n", + pname); + perror(pname); + exit(EXIT_FAILURE); + } + (void) fprintf(stderr, "Temp directory for the dcm files:%s",temp_dir); + (void) strcpy(file_prefix, temp_dir); + (void) strcat(file_prefix, "/dicom"); + } + /*****************************/ + (void) fprintf(stderr, "About to begin the space allocation \n"); /* Leili */ + + /* Get space for file lists */ + num_files_alloc = FILE_ALLOC_INCREMENT; + file_list = MALLOC((size_t) num_files_alloc * sizeof(*file_list)); + file_info_list = MALLOC(num_files_alloc * sizeof(*file_info_list)); + + + /* Loop while reading messages */ + state = WAITING_FOR_ASSOCIATION; + continue_looping = TRUE; + num_files = 0; + + (void) fprintf(stderr, "About to start reading a message \n"); /* Leili */ + while (continue_looping) { + + /* Wait for any children that have finished */ + while ((child_pid=wait3(&statptr, WNOHANG, NULL)) > 0) {} + + /* If there are children, slow down the processing */ + (void) fprintf(stderr, "If we have children, slow down \n"); /* Leili */ + if (child_pid == 0) { + (void) sleep((unsigned int) SERVER_SLEEP_TIME); + } + (void) fprintf(stderr, "Start reading the message\n"); /* Leili */ + /* Read in the message */ + Alarmed_afp = afpin; + (void) signal(SIGALRM, timeout_handler); + (void) alarm(CONNECTION_TIMEOUT); + + if (Do_logging > HIGH_LOGGING) + fprintf(stderr,"\nWaiting for dicom message...\n"); /* rhoge */ + + /* chokes here at end of asynchronous transfer */ + status=acr_input_dicom_message(afpin, &input_message); + (void) fprintf(stderr, "The status of the message is: %s \n", acr_status_string(status)); /* Leili */ + + if (Do_logging > HIGH_LOGGING) { /* rhoge */ + fprintf(stderr,"\nGot message.\n"); + fprintf(stderr,"\nCalling `alarm'...\n"); + } + + (void) alarm(0); + + if (Do_logging > HIGH_LOGGING) + fprintf(stderr,"\nAlarm called.\n"); /* rhoge */ + + (void) fprintf(stderr,"Checking for an error \n"); /* Leili */ + /* Check for error */ + if (status != ACR_OK) { + continue_looping = FALSE; + state = TERMINATING; + break; + } + + /* Set flags indicating whether we should do anything with the files + and whether the file lists contain an extra file */ + process_files = FALSE; + have_extra_file = FALSE; + /* Get group list */ + group_list = acr_get_message_group_list(input_message); + + /* Get PDU type. Default is data transfer */ + pdu_type = acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF); + (void) fprintf(stderr,"We got the pdu and it is: %d\n", pdu_type); /* Leili */ + + + /* Deal with PDU state */ + switch (pdu_type) { + + /* Associate request */ + case ACR_PDU_ASSOC_RQ: + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\n Associate request is ACR_PDU_ASSOC_RQ\n"); + + if (state != WAITING_FOR_ASSOCIATION) { + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + } + + /* Work out reply and get connection info */ + (void) fprintf(stderr,"About to go to reply program\n"); /* Leili */ + output_message = associate_reply(input_message, &project_name, + &pres_context_id, &byte_order, + &vr_encoding, &maximum_length); + (void) fprintf(stderr,"We finished with the reply program\n"); /* Leili */ + + /* Modify the input and output streams according to the + connection info */ + acr_set_byte_order(afpin, byte_order); + acr_set_vr_encoding(afpin, vr_encoding); + acr_set_byte_order(afpout, byte_order); + acr_set_vr_encoding(afpout, vr_encoding); + acr_set_dicom_pres_context_id(afpout, pres_context_id); + acr_set_dicom_maximum_length(afpout, maximum_length); + + /* Get ready for files */ + num_files = 0; + state = WAITING_FOR_DATA; + + break; + + /* Release */ + case ACR_PDU_REL_RQ: + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\nAssociate request is ACR_PDU_REL_RQ\n"); + + if (state != WAITING_FOR_DATA) { + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + } + output_message = release_reply(input_message); + state = TERMINATING; + process_files = TRUE; + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\nReceived release request; process flag set.\n"); + + break; + + /* Abort */ + case ACR_PDU_ABORT_RQ: + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\nAssociate request is ACR_PDU_ABORT_RQ\n"); + + output_message = abort_reply(input_message); + state = TERMINATING; + break; + + /* Data transfer */ + case ACR_PDU_DATA_TF: + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\nAssociate request is ACR_PDU_DATA_TF\n"); + + /* Check state */ + if (state != WAITING_FOR_DATA) { + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + } + + /* Check command and compose a reply */ + acr_command = acr_find_short(group_list, ACR_Command, -1); + switch (acr_command) { + case ACR_C_STORE_RQ: + case ACR_C_ECHO_RQ: + output_message = data_reply(input_message); + break; + default: + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + } + + /* Carry on only if we have a store command */ + if (acr_command != ACR_C_STORE_RQ) break; + + /* Get rid of the command groups */ + group_list = skip_command_groups(group_list); + + /* Was the data attached to the command? If not, read in the next + message - it should contain the data */ + if (group_list == NULL) { + + /* Delete the previous message */ + if (input_message != NULL) + acr_delete_message(input_message); + + /* Read the data and check the status */ + Alarmed_afp = afpin; + (void) signal(SIGALRM, timeout_handler); + (void) alarm(CONNECTION_TIMEOUT); + status=acr_input_dicom_message(afpin, &input_message); + (void) alarm(0); + if (status != ACR_OK) { + state = DISCONNECTING; + break; + } + + /* Check that we have a data PDU */ + group_list = acr_get_message_group_list(input_message); + if (acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF) + != ACR_PDU_DATA_TF) { + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + } + + /* Skip command groups and check for no data */ + group_list = skip_command_groups(group_list); + if (group_list == NULL) break; + + } + (void) fprintf(stderr,"End of while loop, we are out of the loop \n"); /* Leili */ + + /* Extend file list if necessary */ + if (num_files >= num_files_alloc) { + num_files_alloc = num_files + FILE_ALLOC_INCREMENT; + file_list = REALLOC(file_list, + num_files_alloc * sizeof(*file_list)); + file_info_list = + REALLOC(file_info_list, + num_files_alloc * sizeof(*file_info_list)); + } + file_list[num_files] = NULL; + file_info_list[num_files] = + MALLOC(sizeof(*file_info_list[num_files])); + + /* Save the object */ + save_transferred_object(group_list, + file_prefix, &file_list[num_files], + file_info_list[num_files]); + (void) fprintf(stderr,"Finished saving transfered object number %d \n", (num_files)+1); /* Leili */ + num_files++; + + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, " Copied %s\n", file_list[num_files-1]); + } + (void) fprintf(stderr,"Number of files are: %d \n", num_files); /* Leili */ + + if (Do_logging > HIGH_LOGGING) { /* rhoge */ + fprintf(stderr, + "About to check if we're done file group\n"); + + fprintf(stderr,"\nnum_files: \t%d",num_files); + fprintf(stderr,"\nfile_info_list[num_files-1]->study_id: \t%.6f", + file_info_list[num_files-1]->study_id); + fprintf(stderr,"\nfile_info_list[0]->study_id: \t%.6f", + file_info_list[0]->study_id); + fprintf(stderr,"\nfile_info_list[num_files-1]->acq_id: \t%d", + file_info_list[num_files-1]->acq_id); + fprintf(stderr,"\nfile_info_list[0]->acq_id: \t%d", + file_info_list[0]->acq_id); + + } + + /* Check whether we have reached the end of a group of files */ + if (num_files > 1) { + if ((file_info_list[num_files-1]->study_id != + file_info_list[0]->study_id) || + (file_info_list[num_files-1]->acq_id != + file_info_list[0]->acq_id)) { + process_files = TRUE; + have_extra_file = TRUE; + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\nend of file group detected, process flag set.\n"); + } + } + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\ndone checking for end of file group\n"); + + break; + + /* Unknown command */ + default: + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\nAssociate request is default (an error)\n"); + status = ACR_HIGH_LEVEL_ERROR; + state = DISCONNECTING; + break; + + } /* End of switch on pdu_type */ + + (void) fprintf(stderr,"We break and came out since we didn't have the store command\n"); /* Leili */ + + /* Delete input message */ + if (input_message != NULL) + acr_delete_message(input_message); + + /* Use the files if we have a complete acquisition */ + if (process_files) { + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr,"\nEntered the `process files' block...\n"); + + /* Log the fact */ + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "\nCopied one acquisition.\n"); + } + + /* Check for file from next acquisition */ + if (have_extra_file) num_files--; + + /* uncomment to prevent forking */ + /* #define NO_FORK */ + + if (Fork) { + /* Fork child to process the files */ + child_pid = fork(); + } else { + child_pid = 0; + } + + if (child_pid > 0) { /* Parent process */ + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, + "Forked process to create minc files.\n"); + } + + } /* Error forking */ + else if (child_pid < 0) { + (void) fprintf(stderr, + "Error forking child to create minc file\n"); + return; + } + else { /* Child process */ + /*************************************************/ + /* Added by Leili */ + /* Close file descriptors to avoid buffering problems. + STDERR is sometimes left open, therefore this command + maybe needed */ + if(Fork){ + int fd; + for(fd=getdtablesize()-1; fd>= 0; fd--) { + if(fd != 2){ + (void) close(fd); + } + } + } + /*******************************************************/ + /* Added by leili to check the manfucaturer model names */ + printf("Checking file types... "); + + for (ifile = 0; ifile < num_files; ifile++) { + char dicm_test_string[5]; + fptemp = fopen(file_list[ifile], "r"); + + if (fptemp == NULL) { + fprintf(stderr,"Error opening file %s!\n",file_list[ifile]); + exit(EXIT_FAILURE); + } + + /* Numaris 4 DICOM CD/Export file? if so, bytes 129-132 will + contain the string `DICM' */ + + fseek(fptemp,128,SEEK_SET); + fread(dicm_test_string,1,4,fptemp); + dicm_test_string[4] = (char) '\0'; + + // Added by leili to make the program works for data form the old vision system + // To do this, we should figure out the manufacturer model from the dicom header + // if the machine is the old vision, create the output directory + // then call the dicom_to_minc script to do the conversion + group_list = read_siemens_dicom(file_list[ifile],100); + element = acr_find_group_element(group_list, ACR_Manufacturer_model); + if (element != NULL) { + string_to_filename(acr_get_element_string(element), model_name, + sizeof(model_name));} + + if ((element == NULL) || (strlen(model_name) == 0)) + (void) strcpy(model_name, "unknown"); + (void) fprintf(stderr," The model_name is %s\n", model_name); + //************************************************************* + //(void) fprintf(stderr,"\nfile_info_list[num_files-1]->study_id: \t%.6f", + // file_info_list[num_files-1]->study_id); + (void) fprintf(stderr,"\nfile_info_list[0]->study_id: \t%.6f", + file_info_list[0]->study_id); + + (void) fprintf(stderr,"\nfile_info_list[num_files-1]->study_date: \t%5d", + file_info_list[num_files-1]->study_date); + + (void) fprintf(stderr,"\nfile_info_list[num_files-1]->study_time: \t%5d", + file_info_list[num_files-1]->study_time); + + if ((strcmp(model_name, "sonatavision") == 0) && (!strncmp(dicm_test_string,"DICM",4)) || + ((file_info_list[0]->study_id > 20020601.0000) && (!strncmp(dicm_test_string,"DICM",4)))){ + file_type = N4DCM; + N4_OFFSET = 1; + (void) fprintf(stderr,"assuming remaining files are Syngo DICOM (CD/Export).\n"); + (void) fclose(fptemp); + break; // break out of file checking loop + + }else if ((strcmp(model_name, "magnetom_vision") == 0) || (file_info_list[0]->study_id <= 20020601.0000)) { + file_type = IMA; + (void) fprintf(stderr,"assuming remaining files are IMA!\n"); + (void) fclose(fptemp); + break; // break out of file checking loop + }else { + file_type = N4DCM; + N4_OFFSET = 0; + (void) fprintf(stderr,"assuming remaining files are Syngo DICOM.\n"); + (void) fclose(fptemp); + break; // break out of file checking loop + } // end of file type check + + (void) fclose(fptemp); + } // end of loop over files to check for mixed file types + + if (file_type == N4DCM) { + // read up to but not including pixel data + (void) fprintf(stderr, "We are about to do something with the files.\n"); /* Leili */ + (void) fprintf(stderr, "The project name is:%s\n", project_name); /* Leili */ + (void) fprintf(stderr, "The pname is:%s\n", pname); /* Leili */ + use_the_files(project_name, num_files, file_list, + file_info_list,UseArgDir,OutDir); + } + /******************************************************************************/ + /* Added by leili for the conversion of dicom data from the old vision machine */ + else if (file_type == IMA) { + strcat(out_dir, "/data/fmri/transfer/images/"); + (void) sprintf(out_dir,"/data/fmri/transfer/images/MagnetomVision_%f",file_info_list[0]->study_id); + group_list = read_siemens_dicom(file_list[ifile],100); + + element = acr_find_group_element(group_list, ACR_Patient_name); + if (element != NULL) { + string_to_filename(acr_get_element_string(element), patient_name, + sizeof(patient_name)); + strcat(out_dir, "_"); + strcat(out_dir, patient_name); + strcat(out_dir, "_"); + } + if ((element == NULL) || (strlen(patient_name) == 0)) + (void) strcpy(patient_name, "unknown"); + + + element = acr_find_group_element(group_list, ACR_Patient_identification); + if (element != NULL) { + string_to_filename(acr_get_element_string(element), patient_id, + sizeof(patient_id)); + strcat(out_dir, patient_id); + strcat(out_dir, "_"); + } + if ((element == NULL) || (strlen(patient_id) == 0)) + (void) strcpy(patient_id, "unknown"); + + + element = acr_find_group_element(group_list, ACR_Study_date); + if (element != NULL) { + string_to_filename(acr_get_element_string(element), reg_date, + sizeof(reg_date)); + strcat(out_dir, reg_date); + strcat(out_dir, "_"); + } + if ((element == NULL) || (strlen(reg_date) == 0)) + (void) strcpy(reg_date, "unknown"); + + + element = acr_find_group_element(group_list, ACR_Study_time); + if (element != NULL) { + string_to_filename(acr_get_element_string(element), reg_time, + sizeof(reg_time)); + strcat(out_dir, reg_time); + } + if ((element == NULL) || (strlen(reg_time) == 0)) + (void) strcpy(reg_time, "unknown"); + + + mkdir(out_dir,(mode_t) 0777); + + //execl("/sbin/cat", "/sbin/cat", "/usr/local/mni/bin", NULL); + //system ("set path=($path /usr/local/mni/bin)"); + + // this part works fine but it's pretty ugly!!! Perhaps a better solution to the system call? + strcat(temp_name,"/software/source/dicomserver_test/conversion/dicomserver/dicom_to_minc"); + //strcat(temp_name,"/usr/local/mni/bin/dicom_to_minc"); + strcat(temp_name," "); + strcat(temp_name,out_dir); + //strcat(temp_name,"/."); + strcat(temp_name," "); + strcat(temp_name,"-compress"); + strcat(temp_name, " "); + strcat(temp_name,"-inputdir"); + strcat(temp_name, " "); + strcat(temp_name,temp_dir); + //strcat(temp_name,"/."); + (void) fprintf(stderr," The temp_name is: %s\n", temp_name); + system (temp_name); + /* Remove the temporary files */ + cleanup_files(num_files, file_list); + + /* Remove the temporary directory if the server has finished */ + cleanup_files(1, &temp_dir); + exit(0); + } + + /* Remove the temporary files */ + cleanup_files(num_files, file_list); + + /* Remove the temporary directory if the server has finished */ + /* (this does not seem to work all the time) */ + if ((temp_dir != NULL) && (kill(server_pid, 0) != 0)) { + cleanup_files(1, &temp_dir); + } + + /* Print message about child finishing */ + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "Minc creation process finished.\n"); + } + + if (Fork) { + /* Exit from child */ + exit(EXIT_SUCCESS); + } + + } /* End of child process */ + + /* Put blank line in log file */ + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "\n"); + } + + /* Reset the lists */ + free_list(num_files, file_list, file_info_list); + if (have_extra_file) { + file_list[0] = file_list[num_files]; + file_info_list[0] = file_info_list[num_files]; + file_list[num_files] = NULL; + file_info_list[num_files] = NULL; + } + num_files = (have_extra_file ? 1 : 0); + } // end of if(process files) + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\n just passed `process files' loop\n"); + + /* Check for disconnection */ + if (state == DISCONNECTING) { + continue_looping = FALSE; + break; + } + + /* Send reply */ + Alarmed_afp = afpout; + (void) signal(SIGALRM, timeout_handler); + (void) alarm(CONNECTION_TIMEOUT); + status = acr_output_dicom_message(afpout, output_message); + (void) alarm(0); + + /* Delete output message */ + if (output_message != NULL) + acr_delete_message(output_message); + + if (status != ACR_OK) { + state = TERMINATING; + break; + } + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\nbottom of loop over messages\n"); + + } /* End of loop over messages */ + + if (Do_logging > HIGH_LOGGING) /* rhoge */ + fprintf(stderr, + "\njust out of loop over messages\n"); + + /* Free the input and output streams */ + acr_close_dicom_file(afpin); + acr_close_dicom_file(afpout); + + /* Save name of first file in last set transferred */ + if ((num_files > 0) && (file_list[0] != NULL)) { + last_file_name[sizeof(last_file_name) - 1] = '\0'; + (void) strncpy(last_file_name, file_list[0], sizeof(last_file_name)-1); + } + else { + last_file_name[0] = '\0'; + } + + /* Clean up files, if needed */ + if (num_files > 0) { + cleanup_files(num_files, file_list); + free_list(num_files, file_list, file_info_list); + num_files = 0; + } + FREE(file_list); + FREE(file_info_list); + + /* Remove the file prefix directory (this only happens if it is empty). */ + cleanup_files(1, &temp_dir); + FREE(temp_dir); + + /* Check for connection timeout */ + if (Connection_timeout) { + (void) fprintf(stderr, "Connection timed out.\n"); + } + + /* Print final message */ + if ((status == ACR_OK) || (status == ACR_END_OF_INPUT)) { + (void) sprintf(exit_string, "Finished transfer."); + exit_status = EXIT_SUCCESS; + } + else { + (void) sprintf(exit_string, "%s. Disconnecting.", + acr_status_string(status)); + exit_status = EXIT_FAILURE; + } + (void) fprintf(stderr, "The logging set to be %d\n", Do_logging); /* Leili */ + (void) fprintf(stderr, "The status of the message is: %s \n", acr_status_string(status)); /* Leili */ + + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "\n%s: %s\n", pname, exit_string); + } + + if ((status != ACR_OK) && (status != ACR_END_OF_INPUT)) { + if (SYSTEM_LOG != NULL) { + if ((fptemp = fopen(SYSTEM_LOG, "w")) != NULL) { + if ((int) strlen(last_file_name) > 0) { + (void) fprintf(fptemp, "%s: File \"%s\"\n", + pname, last_file_name); + } + (void) fprintf(fptemp, "%s: %s\n", pname, exit_string); + (void) fclose(fptemp); + } + } + } + + /* Free the project_name string */ + if (project_name != NULL) FREE(project_name); + + exit(exit_status); + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : timeout_handler +@INPUT : +@OUTPUT : (none) +@RETURNS : +@DESCRIPTION: Routine to handle connection timeouts. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : March 10, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +/* ARGSUSED */ +public void timeout_handler(int sig) +{ + Connection_timeout = TRUE; + if (Alarmed_afp != NULL) { + acr_dicom_set_eof(Alarmed_afp); + } + return; +} +/* ----------------------------- MNI Header ----------------------------------- +@NAME : skip_command_groups +@INPUT : group_list +@OUTPUT : (none) +@RETURNS : Pointer to head of group list +@DESCRIPTION: Skips over command groups in a group list, returning the rest + of the list. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : March 7, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public Acr_Group skip_command_groups(Acr_Group group_list) +{ + while ((group_list != NULL) && + ((acr_get_group_group(group_list) == DCM_PDU_GRPID) || + (acr_get_group_group(group_list) == ACR_MESSAGE_GID))) { + group_list = acr_get_group_next(group_list); + } + + return group_list; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : cleanup_files +@INPUT : num_files - number of files in list + file_list - array of file names +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Removes files. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void cleanup_files(int num_files, char *file_list[]) +{ + int i; + + if (Keep_files) return; + + for (i=0; i < num_files; i++) { + if (file_list[i] != NULL) { + (void) remove(file_list[i]); + } + } + + return; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : free_list +@INPUT : num_files - number of files in list + file_list - array of file names +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free + the arrays themselves. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void free_list(int num_files, char **file_list, + Data_Object_Info **file_info_list) +{ + int i; + + for (i=0; i < num_files; i++) { + if (file_list[i] != NULL) { + FREE(file_list[i]); + } + if (file_info_list[i] != NULL) { + FREE(file_info_list[i]); + } + } + + return; +} +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/dicomserver.h @@ -0,0 +1,158 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dicomserver.h +@DESCRIPTION: Header file that includes things needed for dicomserver. +@METHOD : +@GLOBALS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + + * $Log: dicomserver.h,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.5 2002/03/19 13:13:56 rhoge + * initial working mosaic support - I think time is scrambled though. + * + * Revision 1.4 2001/12/31 18:27:21 rhoge + * modifications for dicomreader processing of Numaris 4 dicom files - at + * this point code compiles without warning, but does not deal with + * mosaiced files. Also will probably not work at this time for Numaris + * 3 .ima files. dicomserver may also not be functional... + * + * Revision 1.3 2000/12/14 21:17:34 rhoge + * cleanup of log messages + * + * Revision 1.2 2000/12/14 21:15:58 rhoge + * added ACQ and MEAS constants as flags for type of (non-standard) + * dynamic scan looping + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * added num_slices_nominal to Data_Object_Info + * (for support of acquisition loop scans) + * + * Revision 6.1 1999/10/29 17:51:55 neelin + * Fixed Log keyword + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.2 1997/03/11 13:10:48 neelin + * Working version of dicomserver. + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <memory.h> +#include <limits.h> +#ifdef FLT_DIG +# undef FLT_DIG +#endif +#ifdef DBL_DIG +# undef DBL_DIG +#endif +#ifdef DBL_MIN +# undef DBL_MIN +#endif +#ifdef DBL_MAX +# undef DBL_MAX +#endif +#include <float.h> +#include <minc_def.h> +#include <time_stamp.h> +#include <acr_nema.h> +#include <dicom_element_defs.h> + +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif + +#define FILE_ALLOC_INCREMENT 10 + +/* Connection timeout length in seconds */ +/* Changed to 10 min instead of 5 by Leili */ +#define CONNECTION_TIMEOUT (60*10) + +/* Time to sleep between image reads when a child process is running. + This prevents the server from outrunning its children. */ + +#define SERVER_SLEEP_TIME 3 + +/* Define logging constants */ +#define NO_LOGGING 0 +#define LOW_LOGGING 1 +#define HIGH_LOGGING 2 + +/* added by rhoge for ACQ and MEAS loop handling */ +typedef enum { NONE = 0 , ACQ , MEAS } Loop_Type; + +/* supported file types */ +typedef enum { UNDEF, IMA, N3DCM, N4DCM } File_Type; + +/* File containing defaults for dicomserver */ +#define OUTPUT_DEFAULT_FILE_DIR "/usr/local/lib" +#define OUTPUT_DEFAULT_FILE_PREFIX "dicomserver." + +/* System log file (set to NULL for no logging of error) */ +#define SYSTEM_LOG "/dev/log" + +/* Type for carrying around object information */ +typedef struct { + int file_index; + char *file_name; + double study_id; // yyyymmdd.hhmmss + int study_date; + int study_time; + int scanner_serialno; + int acq_id; + int rec_num; + int image_type; + int num_echoes; + int echo_number; + int num_dyn_scans; + int dyn_scan_number; + int global_image_number; + int num_slices_nominal; + int slice_number; + int acq_rows; + int acq_cols; + int rec_rows; + int rec_cols; + int num_mosaic_rows; + int num_mosaic_cols; + int num_slices_in_file; + char sequence_name[256]; + char protocol_name[256]; + +} Data_Object_Info; + +/* Define macro for array size */ +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) + +#include <siemens_dicom_to_minc.h> +#include <dicom_prototypes.h>
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/ext_element_defs.h @@ -0,0 +1,30 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : ext_element_defs.h +@DESCRIPTION: Element definitions for extra elements needed for mosaics, etc. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : December 2001 (Rick Hoge) +@MODIFIED : +@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. +---------------------------------------------------------------------------- */ + +/* Element id's for EXT */ +GLOBAL_ELEMENT(EXT_Mosaic_rows , 0x0023, 0x0001, LO); +GLOBAL_ELEMENT(EXT_Mosaic_columns , 0x0023, 0x0002, LO); +GLOBAL_ELEMENT(EXT_Slices_in_file , 0x0023, 0x0003, LO); +GLOBAL_ELEMENT(EXT_Sub_image_rows , 0x0023, 0x0004, US); +GLOBAL_ELEMENT(EXT_Sub_image_columns , 0x0023, 0x0005, US); +GLOBAL_ELEMENT(EXT_MrProt_dump , 0x0023, 0x0006, LO); +GLOBAL_ELEMENT(EXT_Diffusion_b_value , 0x0023, 0x0007, LO); + +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/minc_file.c @@ -0,0 +1,1025 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : minc_file.c +@DESCRIPTION: Code to do minc file handling. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + * $Log: minc_file.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.12 2002/04/29 15:24:53 rhoge + * removed (mode_t) cast in minc_file - would not build on SGI's + * + * Revision 1.11 2002/04/08 17:26:34 rhoge + * added additional sequence info to minc header + * + * Revision 1.10 2002/03/27 18:57:50 rhoge + * added diffusion b value + * + * Revision 1.9 2002/03/22 19:19:36 rhoge + * Numerous fixes - + * - handle Numaris 4 Dicom patient name + * - option to cleanup input files + * - command option + * - list-only option + * - debug mode + * - user supplied name, idstr + * - anonymization + * + * Revision 1.8 2002/03/19 22:10:16 rhoge + * removed time sorting for N4DCM mosaics - time is random for mosaics + * + * Revision 1.7 2002/03/19 13:13:56 rhoge + * initial working mosaic support - I think time is scrambled though. + * + * Revision 1.6 2001/12/31 18:27:21 rhoge + * modifications for dicomreader processing of Numaris 4 dicom files - at + * this point code compiles without warning, but does not deal with + * mosaiced files. Also will probably not work at this time for Numaris + * 3 .ima files. dicomserver may also not be functional... + * + * Revision 1.5 2001/02/26 22:22:37 rhoge + * added scanner serial number to minc file naming + * + * Revision 1.4 2001/02/26 13:38:22 rhoge + * made `existing directory' warning conditional on logging + * + * Revision 1.3 2000/12/15 01:04:46 rhoge + * make sure acquisition_id (series no) is 6 digit hhmmss string for meas loop + * + * Revision 1.2 2000/12/14 21:19:22 rhoge + * added code to compute time spacing if measurement loop dynamic + * scanning has been detected + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * + * Revision 6.1 1999/10/29 17:51:55 neelin + * Fixed Log keyword + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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 <dicomserver.h> +#include <ctype.h> +extern int Do_logging; + +/* Global for minc history (sorry, but it was kludged in afterwards) */ +char *minc_history = NULL; +int Anon; +char IdStr[512]; +int UserIdStr; +char Name[512]; +int UserName; + +/* Define mri dimension names */ +static char *mri_dim_names[] = { + NULL, "echo_time", MItime, "phase_number", "chemical_shift", NULL}; + +/* Macros */ +#define STRLEN(s) ((int) strlen(s)) + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : create_minc_file +@INPUT : minc_file - name of file to create. If NULL, a name is + generated internally. + clobber - if TRUE, any existing file will be overwritten. + general_info - information for creating the file. + file_prefix - string providing any directory or prefix + for internally generated filename (if it is a directory, + then it must contain the last "/") +@OUTPUT : output_file_name - returns a pointer to an internal area + containing the file name of the created file if minc_file + is NULL, or simply a pointer to minc_file. If NULL, then + nothing is returned. +@RETURNS : id of image conversion variable (MI_ERROR in case of error). +@DESCRIPTION: Routine to create the minc file. +@METHOD : +@GLOBALS : +CALLS : +@CREATED : November 26, 1993 (Peter Neelin) +@MODIFIED : rhoge - modified to create directory for session +---------------------------------------------------------------------------- */ +public int create_minc_file(char *minc_file, int clobber, + General_Info *general_info, + char *file_prefix, char **output_file_name, + Loop_Type loop_type) +{ + static char temp_name[256]; + char patient_name[256]; + char patient_id[256]; + char scanner_model[256]; + char serial_no[256]; + char reg_time[256]; + char temp_str[256]; + int ix; + char *filename; + int minc_clobber; + int mincid, icvid; + Mri_Index imri; + char scan_label[MRI_NDIMS][20]; + + /* added by rhoge for study directory, new naming conventions: */ + + char full_path[256]; + + /* Prefixes for creating file name */ + static char *scan_prefix[MRI_NDIMS] = + {"sl", "e", "d", "p", "cs"}; + + /* Turn off fatal errors */ + ncopts = NCOPTS_DEFAULT; + + /* Create the file name if needed */ + if (minc_file != NULL) { + filename = minc_file; + } + else { + /* Get patient name */ + /******************************************/ + /* Changed by Leili from "string_to_initials" to "string_to_filename" */ + /* based on people's request at MNI */ + if (UserName) { + strcpy(patient_name,Name); + } else { + string_to_filename(general_info->patient.name, patient_name, + sizeof(patient_name)); + } + + if (STRLEN(patient_name) == 0) { + (void) strcpy(patient_name, "no_name"); + } + /*****************************************/ + /* Commented out by Leili,based on the request of people at MNI*/ + /* Get patient ID */ + //if (UserIdStr) { + // strcpy(patient_id,IdStr); + //} else { + // string_to_initials(general_info->patient.identification, patient_id, + // sizeof(patient_id)); + //} + //if (STRLEN(patient_id) == 0) { + // (void) strcpy(patient_id, "no_id"); + //} + /********************************************/ + /* Commented out by Leili,based on the request of people at MNI*/ + /* Get Scanner model */ + //string_to_filename(general_info->study.model, + // scanner_model, + // sizeof(scanner_model)); + //if (STRLEN(scanner_model) == 0) { + // (void) strcpy(scanner_model, "no_scanner"); + //} + + /* Get Scanner serial number */ + //string_to_filename(general_info->study.serial_no, + // serial_no, + // sizeof(serial_no)); + //if (STRLEN(serial_no) == 0) { + // (void) strcpy(serial_no, "no_serial_no"); + //} + + /* get rid of magnetom prefix, if present */ + + //if (!strncmp(scanner_model,"magnetom_",9)) { + // for (ix=0; ix<strlen(scanner_model)+1; ix++) { + // scanner_model[ix]=scanner_model[ix+9]; + // } + //} + /******************************************/ + /* Get Study Time */ + string_to_filename(general_info->patient.reg_time, temp_str, + sizeof(temp_str)); + /* truncate to first 6 chars (hhmmss) */ + strncpy(reg_time, temp_str, 6); + if (STRLEN(reg_time) == 0) { + (void) strcpy(reg_time, "no_time"); + } + reg_time[6]='\0'; /* terminate with null (strncpy does not) */ + + /* Get strings for echo number, etc. */ + for (imri=0; imri < MRI_NDIMS; imri++) { + if ((general_info->size[imri] < general_info->total_size[imri]) && + (general_info->size[imri] == 1)) { + (void) sprintf(scan_label[imri], "%s%d", scan_prefix[imri], + general_info->default_index[imri]); + } + else { + (void) strcpy(scan_label[imri], ""); + } + } + + /* rhoge: add session directory to prefix */ + + (void) strcpy(full_path,file_prefix); + + /* Changed by leili, omitted the scanner info, changed - to _ , and get rid off the patients ID */ + (void) sprintf(temp_name, "%s_%s_%s/", + patient_name, + //patient_id, + //scanner_model, + //serial_no, + general_info->patient.reg_date, + reg_time); + strcat(full_path,temp_name); + + + /* if measurement loop, make sure that acquisition_id is + a 6 digit (hhmmss) string with leading zero if needed */ + + if (loop_type == MEAS) { + + (void) sprintf(general_info->study.acquisition_id, "%06d", + general_info->acq_id); + + } + + /* Create file name */ + /* changed by leili, omitted the scanner info, changed - to _ */ + (void) sprintf(temp_name, "%s%s_%s_%s_%s%s%s%s%s%s_mri.mnc", + full_path, + patient_name, + //patient_id, + //scanner_model, + //serial_no, + general_info->patient.reg_date, + reg_time, + general_info->study.acquisition_id, + scan_label[SLICE], + scan_label[ECHO], + scan_label[TIME], + scan_label[PHASE], + scan_label[CHEM_SHIFT]); + filename = temp_name; + + if (Do_logging > HIGH_LOGGING) { /* rhoge */ + fprintf(stderr,"\nminc file name: %s\n",filename); + fprintf(stderr,"\nfile prefix: %s\n",full_path); + fprintf(stderr,"\npatient_name: %s\n",patient_name); + fprintf(stderr,"\nstudy_id: %s\n", + general_info->study.study_id); + fprintf(stderr,"\nacquisition_id: %s\n", + general_info->study.acquisition_id); + + fprintf(stderr,"\nRegistration date: %s\n", + general_info->patient.reg_date); + fprintf(stderr,"\nRegistration time: %s\n", + general_info->patient.reg_time); + } + } + + /* create the session directory if none exists */ + + if (mkdir(full_path, 0777) && + (Do_logging > HIGH_LOGGING)) { + (void) fprintf(stderr, "Directory %s exists...\n",full_path); + } + + /* Set output file name */ + if (output_file_name != NULL) + *output_file_name = filename; + + /* Set the clobber value */ + if (clobber) minc_clobber = NC_CLOBBER; + else minc_clobber = NC_NOCLOBBER; + + /* Create the file */ + mincid = micreate(filename, minc_clobber); + if (mincid == MI_ERROR) return MI_ERROR; + + /* Set up variables */ + setup_minc_variables(mincid, general_info,loop_type); + + /* Put the file in data mode */ + (void) ncsetfill(mincid, NC_NOFILL); + if (ncendef(mincid) == MI_ERROR) { + return MI_ERROR; + } + + /* Create the icv */ + icvid = miicv_create(); + + /* Set the type and range */ + (void) miicv_setint(icvid, MI_ICV_TYPE, NC_SHORT); + if (general_info->is_signed) + (void) miicv_setstr(icvid, MI_ICV_SIGN, MI_SIGNED); + else + (void) miicv_setstr(icvid, MI_ICV_SIGN, MI_UNSIGNED); + (void) miicv_setdbl(icvid, MI_ICV_VALID_MIN, general_info->pixel_min); + (void) miicv_setdbl(icvid, MI_ICV_VALID_MAX, general_info->pixel_max); + + /* Attach the icv */ + (void) miicv_attach(icvid, mincid, ncvarid(mincid, MIimage)); + + return icvid; + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : setup_minc_variables +@INPUT : mincid + general_info +@OUTPUT : general_info +@RETURNS : (nothing) +@DESCRIPTION: Routine to setup minc variables. +@METHOD : +@GLOBALS : +CALLS : +@CREATED : November 26, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void setup_minc_variables(int mincid, General_Info *general_info, + Loop_Type loop_type) +{ + Mri_Index imri; + Volume_Index ivol; + World_Index iworld; + int ndims; + int dim[MAX_VAR_DIMS]; + long dimsize; + char *dimname; + int varid, imgid, dicomvar; + double valid_range[2]; + char name[MAX_NC_NAME]; + int index; + int regular; + double separation, diff; + Acr_Group cur_group; + Acr_Element cur_element; + int length; + char *data; + nc_type datatype; + int is_char; + int ich; + + /* stuff added by rhoge */ + double sum; + double avg; + + /* Define the spatial dimension names */ + static char *spatial_dimnames[WORLD_NDIMS] = {MIxspace, MIyspace, MIzspace}; + + /* Create the dimensions from slowest to fastest */ + + ndims=0; + /* Create the non-spatial dimensions (from slowest to fastest) */ + for (imri=MRI_NDIMS-1; (int) imri > SLICE; imri--) { + + /* for the TIME dimension, check if we have acquisition-loop + dynamic scan OR a `corrected' dynamic scan */ + + if ( (imri==TIME) && + ((loop_type!=NONE) || (general_info->acq.num_dyn_scans>1)) ) { + + /* for Siemens scans using the signal averaging loop for + multiple time points we use the TR as the time step */ + + dimsize = general_info->size[TIME]; + if (general_info->size[TIME] > 1) { + dimname = mri_dim_names[TIME]; + dim[ndims] = ncdimdef(mincid, dimname, dimsize); + + varid = micreate_std_variable(mincid, dimname, NC_DOUBLE, 1, + &dim[ndims]); + (void) miattputstr(mincid, varid, MIspacing, MI_REGULAR); + (void) miattputstr(mincid, varid, MIunits, "s"); + if (loop_type == MEAS) { + /* if Meas loop, time step is not equal to TR, and + frames should have time values (rhoge) */ + sum = 0.0; + for (index=1; index < general_info->size[TIME]; index++) { + + sum += general_info->coordinates[TIME][1]- + general_info->coordinates[TIME][0]; + + } + /* compute mean */ + avg = sum/general_info->size[TIME]; + (void) miattputdbl(mincid, varid, MIstep,avg); + + /* check for uniformity of spacing */ + regular = TRUE; + for (index=1; index < general_info->size[TIME]; index++) { + diff = general_info->coordinates[TIME][1]- + general_info->coordinates[TIME][0] - avg; + + if (diff < 0.0) diff = -diff; + if (separation != 0.0) diff /= avg; + if (diff > COORDINATE_EPSILON) { + regular = FALSE; + break; + } + } + if (regular) + (void) miattputstr(mincid, varid, MIspacing, MI_REGULAR); + else + (void) miattputstr(mincid, varid, MIspacing, MI_IRREGULAR); + } else { + + /* assume ACQ loop and use TR for time step */ + (void) miattputdbl(mincid, varid, MIstep, + general_info->acq.rep_time); + } + (void) miattputdbl(mincid, varid, MIstart,0); + + general_info->image_index[TIME] = ndims; + ndims++; + } + + } else { /* NORMAL CODE */ + + dimsize = general_info->size[imri]; + if (general_info->size[imri] > 1) { + dimname = mri_dim_names[imri]; + dim[ndims] = ncdimdef(mincid, dimname, dimsize); + if (imri == TIME) { + varid = micreate_std_variable(mincid, dimname, NC_DOUBLE, 1, + &dim[ndims]); + (void) miattputstr(mincid, varid, MIunits, "s"); + } + else if (imri == ECHO) { + varid = ncvardef(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); + (void) miattputstr(mincid, varid, MIvartype, MI_DIMENSION); + (void) miattputstr(mincid, varid, MIspacing, MI_IRREGULAR); + (void) miattputstr(mincid, varid, MIunits, "s"); + } + general_info->image_index[imri] = ndims; + ndims++; + } + } + } + + /* Next the spatial dimensions */ + for (ivol=0; ivol < VOL_NDIMS; ivol++) { + switch (ivol) { + case VSLICE: + dimsize = general_info->size[SLICE]; + iworld = general_info->slice_world; + break; + case VROW: + dimsize = general_info->nrows; + iworld = general_info->row_world; + break; + case VCOLUMN: + dimsize = general_info->ncolumns; + iworld = general_info->column_world; + break; + } + dimname = spatial_dimnames[iworld]; + dim[ndims] = ncdimdef(mincid, dimname, dimsize); + if (ivol == VSLICE) { + varid = micreate_std_variable(mincid, dimname, NC_DOUBLE, + 1, &dim[ndims]); + /* Check for regular slices */ + regular = TRUE; + separation = general_info->step[general_info->slice_world]; + for (index=1; index < general_info->size[SLICE]; index++) { + diff = general_info->coordinates[SLICE][index] - + general_info->coordinates[SLICE][index-1] - separation; + if (diff < 0.0) diff = -diff; + if (separation != 0.0) diff /= separation; + if (diff > COORDINATE_EPSILON) { + regular = FALSE; + break; + } + } + if (regular) + (void) miattputstr(mincid, varid, MIspacing, MI_REGULAR); + } + else + varid = micreate_std_variable(mincid, dimname, NC_LONG, 0, NULL); + (void) miattputdbl(mincid, varid, MIstep, + general_info->step[iworld]); + (void) miattputdbl(mincid, varid, MIstart, + general_info->start[iworld]); + (void) miattputstr(mincid, varid, MIspacetype, MI_NATIVE); + (void) ncattput(mincid, varid, MIdirection_cosines, + NC_DOUBLE, WORLD_NDIMS, + general_info->dircos[iworld]); + if (ivol == VSLICE) { + general_info->image_index[SLICE] = ndims; + } + ndims++; + } + + /* Set up image variable */ + imgid = micreate_std_variable(mincid, MIimage, general_info->datatype, + ndims, dim); + if (general_info->is_signed) + (void) miattputstr(mincid, imgid, MIsigntype, MI_SIGNED); + else + (void) miattputstr(mincid, imgid, MIsigntype, MI_UNSIGNED); + valid_range[0] = general_info->pixel_min; + valid_range[1] = general_info->pixel_max; + (void) ncattput(mincid, imgid, MIvalid_range, NC_DOUBLE, 2, valid_range); + (void) miattputstr(mincid, imgid, MIcomplete, MI_FALSE); + + /* Create image max and min variables */ + varid = micreate_std_variable(mincid, MIimagemin, NC_DOUBLE, ndims-2, dim); + if (STRLEN(general_info->units) > 0) + (void) miattputstr(mincid, varid, MIunits, general_info->units); + varid = micreate_std_variable(mincid, MIimagemax, NC_DOUBLE, ndims-2, dim); + if (STRLEN(general_info->units) > 0) + (void) miattputstr(mincid, varid, MIunits, general_info->units); + + /* Create the patient variable */ + varid = micreate_group_variable(mincid, MIpatient); + if (STRLEN(general_info->patient.name) > 0) + if (Anon) { + (void) miattputstr(mincid, varid, MIfull_name, + "anonymous"); + } else { + (void) miattputstr(mincid, varid, MIfull_name, + general_info->patient.name); + } + if (STRLEN(general_info->patient.identification) > 0) + (void) miattputstr(mincid, varid, MIidentification, + general_info->patient.identification); + if (STRLEN(general_info->patient.birth_date) > 0) + (void) miattputstr(mincid, varid, MIbirthdate, + general_info->patient.birth_date); + if (STRLEN(general_info->patient.age) > 0) + (void) miattputstr(mincid, varid, "age", + general_info->patient.age); + if (STRLEN(general_info->patient.sex) > 0) + (void) miattputstr(mincid, varid, MIsex, + general_info->patient.sex); + if (general_info->patient.weight != -DBL_MAX) + (void) miattputdbl(mincid, varid, MIweight, + general_info->patient.weight); + + /* Create the study variable */ + varid = micreate_group_variable(mincid, MIstudy); + + /* rhoge: fixed date/time to reflect study */ + if (STRLEN(general_info->patient.reg_date) > 0) + (void) miattputstr(mincid, varid, "start_date", + general_info->patient.reg_date); + if (STRLEN(general_info->patient.reg_time) > 0) + (void) miattputstr(mincid, varid, "start_time", + general_info->patient.reg_time); + if (STRLEN(general_info->study.modality) > 0) + (void) miattputstr(mincid, varid, MImodality, + general_info->study.modality); + if (STRLEN(general_info->study.manufacturer) > 0) + (void) miattputstr(mincid, varid, "manufacturer", + general_info->study.manufacturer); + if (STRLEN(general_info->study.model) > 0) + (void) miattputstr(mincid, varid, "model", + general_info->study.model); + if (general_info->study.field_value != -DBL_MAX) + (void) miattputdbl(mincid, varid, "field_value", + general_info->study.field_value); + if (STRLEN(general_info->study.software_version) > 0) + (void) miattputstr(mincid, varid, "software_version", + general_info->study.software_version); + if (STRLEN(general_info->study.serial_no) > 0) + (void) miattputstr(mincid, varid, "serial_no", + general_info->study.serial_no); + if (STRLEN(general_info->study.calibration_date) > 0) + (void) miattputstr(mincid, varid, "calibration_date", + general_info->study.calibration_date); + if (STRLEN(general_info->study.institution) > 0) + (void) miattputstr(mincid, varid, MIinstitution, + general_info->study.institution); + if (STRLEN(general_info->study.station_id) > 0) + (void) miattputstr(mincid, varid, MIstation_id, + general_info->study.station_id); + if (STRLEN(general_info->study.referring_physician) > 0) + (void) miattputstr(mincid, varid, MIreferring_physician, + general_info->study.referring_physician); + + if (STRLEN(general_info->study.performing_physician) > 0) + (void) miattputstr(mincid, varid, "performing_physician", + general_info->study.referring_physician); + if (STRLEN(general_info->study.operator) > 0) + (void) miattputstr(mincid, varid, "operator", + general_info->study.operator); + + if (STRLEN(general_info->study.procedure) > 0) + (void) miattputstr(mincid, varid, MIprocedure, + general_info->study.procedure); + if (STRLEN(general_info->study.study_id) > 0) + (void) miattputstr(mincid, varid, MIstudy_id, + general_info->study.study_id); + + /* Create acquisition variable */ + varid = micreate_group_variable(mincid, MIacquisition); + if (STRLEN(general_info->study.acquisition_id) > 0) + (void) miattputstr(mincid, varid, "acquisition_id", + general_info->study.acquisition_id); + if (STRLEN(general_info->study.start_time) > 0) + (void) miattputstr(mincid, varid, MIstart_time, + general_info->study.start_time); + + if (STRLEN(general_info->acq.scan_seq) > 0) + (void) miattputstr(mincid, varid, MIscanning_sequence, + general_info->acq.scan_seq); + + if (STRLEN(general_info->acq.seq_owner) > 0) + (void) miattputstr(mincid, varid, "seq_owner", + general_info->acq.seq_owner); + + if (STRLEN(general_info->acq.seq_descr) > 0) + (void) miattputstr(mincid, varid, "seq_description", + general_info->acq.seq_descr); + + + if (STRLEN(general_info->acq.protocol_name) > 0) + (void) miattputstr(mincid, varid, "protocol_name", + general_info->acq.protocol_name); + if (STRLEN(general_info->acq.receive_coil) > 0) + (void) miattputstr(mincid, varid, "receive_coil", + general_info->acq.receive_coil); + if (STRLEN(general_info->acq.transmit_coil) > 0) + (void) miattputstr(mincid, varid, "transmit_coil", + general_info->acq.transmit_coil); + + if (general_info->acq.rep_time != -DBL_MAX) + (void) miattputdbl(mincid, varid, MIrepetition_time, + general_info->acq.rep_time); + if ((general_info->acq.echo_time != -DBL_MAX) && + (general_info->size[ECHO] <= 1)) + (void) miattputdbl(mincid, varid, MIecho_time, + general_info->acq.echo_time); + if (general_info->acq.echo_number != -DBL_MAX) + (void) miattputdbl(mincid, varid, "echo_number", + general_info->acq.echo_number); + if (general_info->acq.inv_time != -DBL_MAX) + (void) miattputdbl(mincid, varid, MIinversion_time, + general_info->acq.inv_time); + if (general_info->acq.flip_angle != -DBL_MAX) + (void) miattputdbl(mincid, varid, "flip_angle", + general_info->acq.flip_angle); + if (general_info->acq.slice_thickness != -DBL_MAX) + (void) miattputdbl(mincid, varid, "slice_thickness", + general_info->acq.slice_thickness); + if (general_info->acq.num_slices != -DBL_MAX) + (void) miattputdbl(mincid, varid, "num_slices", + general_info->acq.num_slices); + if (general_info->acq.b_value != -DBL_MAX) + (void) miattputdbl(mincid, varid, "b_value", + general_info->acq.b_value); + + /* add number of dynamic scans (rhoge) */ + /* this will be relevant if we are receiving siemens scans that + have been `cleaned up' (and hence have the correct number of + dynamic scans inserted) */ + + if (general_info->acq.num_dyn_scans != -DBL_MAX) + (void) miattputdbl(mincid, varid, "num_dyn_scans", + general_info->acq.num_dyn_scans); + + if (general_info->acq.num_avg != -DBL_MAX) + (void) miattputdbl(mincid, varid, MInum_averages, + general_info->acq.num_avg); + + if (general_info->acq.scan_dur != -DBL_MAX) + (void) miattputdbl(mincid, varid, "scan_duration", + general_info->acq.scan_dur); + + if (general_info->acq.ky_lines != -DBL_MAX) + (void) miattputdbl(mincid, varid, "ky_lines", + general_info->acq.ky_lines); + + if (general_info->acq.kymax_ix != -DBL_MAX) + (void) miattputdbl(mincid, varid, "kymax_ix", + general_info->acq.kymax_ix); + + if (general_info->acq.kymin_ix != -DBL_MAX) + (void) miattputdbl(mincid, varid, "kymin_ix", + general_info->acq.kymin_ix); + + if (general_info->acq.kz_lines != -DBL_MAX) + (void) miattputdbl(mincid, varid, "kz_lines", + general_info->acq.kz_lines); + + if (general_info->acq.dummy_scans != -DBL_MAX) + (void) miattputdbl(mincid, varid, "dummy_excitations", + general_info->acq.dummy_scans); + if (general_info->acq.imaging_freq != -DBL_MAX) + (void) miattputdbl(mincid, varid, MIimaging_frequency, + general_info->acq.imaging_freq); + if (STRLEN(general_info->acq.imaged_nucl) > 0) + (void) miattputstr(mincid, varid, MIimaged_nucleus, + general_info->acq.imaged_nucl); + + if (general_info->acq.adc_voltage != -DBL_MAX) + (void) miattputdbl(mincid, varid, "adc_voltage", + general_info->acq.adc_voltage); + + if (general_info->acq.adc_offset != -DBL_MAX) + (void) miattputdbl(mincid, varid, "adc_offset", + general_info->acq.adc_offset); + + if (general_info->acq.transmit_ampl != -DBL_MAX) + (void) miattputdbl(mincid, varid, "transmit_ampl", + general_info->acq.transmit_ampl); + + if (general_info->acq.rec_amp_gain != -DBL_MAX) + (void) miattputdbl(mincid, varid, "rec_amp_gain", + general_info->acq.rec_amp_gain); + + if (general_info->acq.rec_preamp_gain != -DBL_MAX) + (void) miattputdbl(mincid, varid, "rec_preamp_gain", + general_info->acq.rec_preamp_gain); + + if (general_info->acq.win_center != -DBL_MAX) + (void) miattputdbl(mincid, varid, "window_center", + general_info->acq.win_center); + + if (general_info->acq.win_width != -DBL_MAX) + (void) miattputdbl(mincid, varid, "window_width", + general_info->acq.win_width); + + if (general_info->acq.gy_ampl != -DBL_MAX) + (void) miattputdbl(mincid, varid, "gy_ampl", + general_info->acq.gy_ampl); + + if (general_info->acq.gx_ampl != -DBL_MAX) + (void) miattputdbl(mincid, varid, "gx_ampl", + general_info->acq.gx_ampl); + + if (general_info->acq.gz_ampl != -DBL_MAX) + (void) miattputdbl(mincid, varid, "gz_ampl", + general_info->acq.gz_ampl); + + if (general_info->acq.num_phase_enc_steps != -DBL_MAX) + (void) miattputdbl(mincid, varid, "num_phase_enc_steps", + general_info->acq.num_phase_enc_steps); + if (general_info->acq.percent_sampling != -DBL_MAX) + (void) miattputdbl(mincid, varid, "percent_sampling", + general_info->acq.percent_sampling); + if (general_info->acq.percent_phase_fov != -DBL_MAX) + (void) miattputdbl(mincid, varid, "percent_phase_fov", + general_info->acq.percent_phase_fov); + if (general_info->acq.pixel_bandwidth != -DBL_MAX) + (void) miattputdbl(mincid, varid, "pixel_bandwidth", + general_info->acq.pixel_bandwidth); + if (STRLEN(general_info->acq.phase_enc_dir) > 0) + (void) miattputstr(mincid, varid, "phase_enc_dir", + general_info->acq.phase_enc_dir); + if (general_info->acq.sar != -DBL_MAX) + (void) miattputdbl(mincid, varid, "SAR", + general_info->acq.sar); + if (STRLEN(general_info->acq.mr_acq_type) > 0) + (void) miattputstr(mincid, varid, "mr_acq_type", + general_info->acq.mr_acq_type); + if (STRLEN(general_info->acq.image_type) > 0) + (void) miattputstr(mincid, varid, "image_type", + general_info->acq.image_type); + + if (STRLEN(general_info->acq.comments) > 0) + (void) miattputstr(mincid, varid, MIcomments, + general_info->acq.comments); + + // this is Siemens Numaris 4 specific! + if (STRLEN(general_info->acq.MrProt) > 0) + (void) miattputstr(mincid, varid, "MrProt_dump", + general_info->acq.MrProt); + + /* Create the dicom info variable */ + varid = ncvardef(mincid, "dicominfo", NC_LONG, 0, NULL); + (void) miattputstr(mincid, varid, MIvartype, MI_GROUP); + (void) miattputstr(mincid, varid, MIvarid, + "MNI DICOM information variable"); + (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), varid); + if (STRLEN(general_info->image_type_string) > 0) + (void) miattputstr(mincid, varid, "image_type", + general_info->image_type_string); + (void) miattputdbl(mincid, varid, "window_min", general_info->window_min); + (void) miattputdbl(mincid, varid, "window_max", general_info->window_max); + + /* Put group info in header */ + cur_group = general_info->group_list; + dicomvar = ncvardef(mincid, DICOM_ROOT_VAR, NC_LONG, 0, NULL); + (void) miattputstr(mincid, dicomvar, MIvartype, MI_GROUP); + (void) miattputstr(mincid, dicomvar, MIvarid, "MNI DICOM variable"); + (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), dicomvar); + while (cur_group != NULL) { + + /* Create variable for group */ + (void) sprintf(name, "dicom_0x%04x", acr_get_group_group(cur_group)); + varid = ncvardef(mincid, name, NC_LONG, 0, NULL); + (void) miattputstr(mincid, varid, MIvartype, MI_GROUP); + (void) miattputstr(mincid, varid, MIvarid, "MNI DICOM variable"); + (void) miadd_child(mincid, dicomvar, varid); + + /* Loop through elements of group */ + cur_element = acr_get_group_element_list(cur_group); + while (cur_element != NULL) { + (void) sprintf(name, "el_0x%04x", + acr_get_element_element(cur_element)); + is_char = TRUE; + length = acr_get_element_length(cur_element); + data = acr_get_element_data(cur_element); + for (ich=0; ich < length; ich++) { + if (!isprint((int) data[ich])) { + is_char = FALSE; + break; + } + } + if (is_char) + datatype = NC_CHAR; + else + datatype = NC_BYTE; + ncattput(mincid, varid, name, datatype, length, data); + + cur_element = acr_get_element_next(cur_element); + } + cur_group = acr_get_group_next(cur_group); + } + + /* Create the history attribute */ + if (minc_history != NULL) { + (void) miattputstr(mincid, NC_GLOBAL, MIhistory, minc_history); + } + + return; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : save_minc_image +@INPUT : icvid + general_info + file_info + image +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Routine to save the image in the minc file +@METHOD : +@GLOBALS : +CALLS : +@CREATED : November 26, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void save_minc_image(int icvid, General_Info *general_info, + File_Info *file_info, Image_Data *image) +{ + int mincid, imgid; + long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS]; + int file_index, array_index; + int idim; + Mri_Index imri; + char *dimname; + unsigned short pvalue, pmax, pmin; + double dvalue, maximum, minimum, scale, offset; + long ipix, imagepix; + + /* Get the minc file id */ + (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); + (void) miicv_inqint(icvid, MI_ICV_VARID, &imgid); + + /* Create start and count variables */ + idim = 0; + for (imri=MRI_NDIMS-1; (int) imri >= 0; imri--) { + if (general_info->image_index[imri] >= 0) { + file_index = general_info->image_index[imri]; + if (general_info->size[imri] > 1) { + array_index = search_list(file_info->index[imri], + general_info->indices[imri], + general_info->size[imri], + general_info->search_start[imri]); + if (array_index < 0) array_index = 0; + general_info->search_start[imri] = array_index; + } + else { + array_index = 0; + } + start[file_index] = array_index; + count[file_index] = 1; + idim++; + } + } + start[idim] = 0; + start[idim+1] = 0; + count[idim] = general_info->nrows; + count[idim+1] = general_info->ncolumns; + + /* Write out slice position */ + switch (general_info->slice_world) { + case XCOORD: dimname = MIxspace; break; + case YCOORD: dimname = MIyspace; break; + case ZCOORD: dimname = MIzspace; break; + default: dimname = MIzspace; + } + (void) mivarput1(mincid, ncvarid(mincid, dimname), + &start[general_info->image_index[SLICE]], + NC_DOUBLE, NULL, &file_info->coordinate[SLICE]); + + /* Write out time of slice, if needed */ + if (general_info->size[TIME] > 1) { + (void) mivarput1(mincid, ncvarid(mincid, mri_dim_names[TIME]), + &start[general_info->image_index[TIME]], + NC_DOUBLE, NULL, &file_info->coordinate[TIME]); + } + + /* Write out echo time of slice, if needed */ + if (general_info->size[ECHO] > 1) { + (void) mivarput1(mincid, ncvarid(mincid, mri_dim_names[ECHO]), + &start[general_info->image_index[ECHO]], + NC_DOUBLE, NULL, &file_info->coordinate[ECHO]); + } + + /* Search image for max and min */ + imagepix = general_info->nrows * general_info->ncolumns; + pmax = 0; + pmin = USHRT_MAX; + for (ipix=0; ipix < imagepix; ipix++) { + pvalue = image->data[ipix]; + if (pvalue > pmax) pmax = pvalue; + if (pvalue < pmin) pmin = pvalue; + } + + /* Re-scale the images */ + if (pmax > pmin) + scale = (general_info->pixel_max - general_info->pixel_min) / + ((double) pmax - (double) pmin); + else + scale = 0.0; + + offset = general_info->pixel_min - scale * (double) pmin; + for (ipix=0; ipix < imagepix; ipix++) { + dvalue = image->data[ipix]; + image->data[ipix] = dvalue * scale + offset; + } + + /* Calculate new intensity max and min */ + if (general_info->pixel_max > general_info->pixel_min) + scale = (file_info->slice_max - file_info->slice_min) / + (general_info->pixel_max - general_info->pixel_min); + else + scale = 0.0; + + // debugging info for slice intensity scaling + // printf("general_info->pixel_max = %10.2f file_info->slice_max = %10.2f pmax = %u\n",general_info->pixel_max,file_info->slice_max,pmax); + + offset = file_info->slice_min - scale * general_info->pixel_min; + minimum = (double) pmin * scale + offset; + maximum = (double) pmax * scale + offset; + + /* Write out the max and min values */ + (void) mivarput1(mincid, ncvarid(mincid, MIimagemin), start, NC_DOUBLE, + NULL, &minimum); + (void) mivarput1(mincid, ncvarid(mincid, MIimagemax), start, NC_DOUBLE, + NULL, &maximum); + + /* Write out the image */ + (void) miicv_put(icvid, start, count, image->data); + + return; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : close_minc_file +@INPUT : icvid - value returned by create_minc_file +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Routine to close the minc file. +@METHOD : +@GLOBALS : +CALLS : +@CREATED : November 30, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void close_minc_file(int icvid) +{ + int mincid; + + /* Get the minc file id */ + (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); + + /* Write out the complete attribute */ + (void) miattputstr(mincid, ncvarid(mincid, MIimage), MIcomplete, MI_TRUE); + + /* Close the file */ + (void) miclose(mincid); + + (void) miicv_free(icvid); + + return; +}
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/modify_group_list.c @@ -0,0 +1,157 @@ +public void multi_image_modify_group_list(Acr_Group group_list, Acr_Element *big_image,Acr_Element *small_image,int iimage) +{ + int irow, ibyte, idim, nbyte; + int isub, jsub; + char *new, *old; + long old_offset, new_offset; + double position[3], distance; + double old_position[3], old_step[3], normal[3]; + char string[256]; + + int slices_in_file; + int num_mosaic_rows; + int num_mosaic_cols; + int pixel_size; + long new_image_size; + Acr_Element element; + int big_cols, big_rows; + int small_cols, small_rows; + void *data; + double RowColVec[6]; + double dircos[VOL_NDIMS][WORLD_NDIMS]; + + // get info about file: + + slices_in_file=acr_find_int(group_list, EXT_Slices_in_file, 999); + num_mosaic_rows=acr_find_int(group_list,EXT_Mosaic_rows, 999); + num_mosaic_cols = acr_find_int(group_list,EXT_Mosaic_columns,999); + + // Check the image number + if ((iimage < 0) || (iimage > slices_in_file)) { + (void) fprintf(stderr, "Invalid image number to send: %d of %d\n",iimage, slices_in_file); + exit(EXIT_FAILURE); + } + + // Figure out the sub-image indices + isub = iimage % num_mosaic_rows; + jsub = iimage / num_mosaic_cols; + + // Get pointers: + + old = acr_get_element_data(*big_image); + new = acr_get_element_data(*small_image); + + /* Copy the image */ + nbyte = small_cols * pixel_size; + for (irow=0; irow < small_rows; irow++) { + old_offset = isub * small_cols +(jsub * small_rows + irow) * big_rows; + old_offset *= pixel_size; + new_offset = (irow * small_cols) * pixel_size; + for (ibyte=0; ibyte < nbyte; ibyte++) { + new[new_offset + ibyte] = old[old_offset + ibyte]; + } + } + + /* Reset the byte order and VR encoding. This will be modified on each + send according to what the connection needs. */ + acr_set_element_byte_order(*small_image,acr_get_element_byte_order(*big_image)); + acr_set_element_vr_encoding(*small_image,acr_get_element_vr_encoding(*big_image)); + + // Update the slice index + acr_insert_numeric(&group_list, SPI_Current_slice_number,(double) (iimage + 1)); + + if (file_type == N3DCM || file_type == IMA) { + // get the image normals + element = acr_find_group_element(group_list, SPI_Image_normal); + acr_get_element_numeric_array(element, 3, normal); + + // get the old image position + element = acr_find_group_element(group_list, SPI_Image_position); + acr_get_element_numeric_array(element, 3, old_position); + + } else { + element = acr_find_group_element(group_list,ACR_Image_orientation_patient); + acr_get_element_numeric_array(element, 6, RowColVec); + + memcpy(dircos[VCOLUMN],RowColVec,sizeof(RowColVec[0])*3); + memcpy(dircos[VROW],&RowColVec[3],sizeof(RowColVec[0])*3); + + convert_dicom_coordinate(dircos[VROW]); + convert_dicom_coordinate(dircos[VCOLUMN]); + + // dircos[VSLICE][0] = + normal[0] = dircos[VCOLUMN][1] * dircos[VROW][2] - dircos[VCOLUMN][2] * dircos[VROW][1]; + + // dircos[VSLICE][1] = + normal[1] = dircos[VCOLUMN][2] * dircos[VROW][0] - dircos[VCOLUMN][0] * dircos[VROW][2]; + + // dircos[VSLICE][2] = + normal[2] = dircos[VCOLUMN][0] * dircos[VROW][1] - dircos[VCOLUMN][1] * dircos[VROW][0]; + + element = acr_find_group_element(group_list, ACR_Image_position_patient); + acr_get_element_numeric_array(element, WORLD_NDIMS, old_position); + convert_dicom_coordinate(old_position); + + } + + printf("here1\n"); + + /* Update the position */ + distance = 0.0; + for (idim=0; idim < 3; idim++) { + position[idim] = old_position[idim] + (double) iimage * old_step[idim]; + distance += position[idim] * normal[idim]; + } + + (void) sprintf(string, "%.15g\\%.15g\\%.15g",position[0], position[1], position[2]); + acr_insert_string(&group_list, SPI_Image_position, string); + + // call function to fix DICOM header to match Siemens header + update_coordinate_info(group_list); + +} + +public int multi_image_init(Acr_Group group_list, Acr_Element *big_image, Acr_Element *small_image) + +{ + int big_cols, big_rows; + int small_cols, small_rows; + int num_mosaic_rows; + int num_mosaic_cols; + int pixel_size; + long new_image_size; + void *data; + int group_id, element_id; + + // Steal the image element from the group list + *big_image = acr_find_group_element(group_list, ACR_Image); + + group_id = acr_get_element_group(*big_image); + element_id = acr_get_element_element(*big_image); + acr_group_steal_element(acr_find_group(group_list, group_id),*big_image); + + // Add a small image, if needed + + big_cols = acr_find_int(group_list, ACR_Columns, 1) * num_mosaic_cols; + big_rows = acr_find_int(group_list, ACR_Rows, 1) * num_mosaic_rows; + + small_cols = acr_find_int(group_list, ACR_Columns, 1); + small_rows = acr_find_int(group_list, ACR_Rows, 1); + + pixel_size = + (acr_find_int(group_list, ACR_Bits_allocated, 16)-1) / 8 + 1; + + new_image_size = acr_find_int(group_list, ACR_Columns, 1) * acr_find_int(group_list, ACR_Rows, 1) * pixel_size; + + data = malloc((size_t) new_image_size); + + *small_image = acr_create_element(group_id, element_id,acr_get_element_vr(*big_image),new_image_size, data); + + acr_set_element_vr(*small_image,acr_get_element_vr(*big_image)); + acr_set_element_byte_order(*small_image,acr_get_element_byte_order(*big_image)); + acr_set_element_vr_encoding(*small_image,acr_get_element_vr_encoding(*big_image)); + acr_insert_element_into_group_list(&group_list, *small_image); + + return 0; +} +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/open_connection.c @@ -0,0 +1,206 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : open_connection.c +@DESCRIPTION: File containing routines to open a decnet connection. +@GLOBALS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : + * $Log: open_connection.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * + * Revision 6.1 1999/10/29 17:51:56 neelin + * Fixed Log keyword + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * + * Revision 3.0 1995/05/15 19:31:44 neelin + * Release of minc version 0.3 + * + * Revision 2.5 1995/02/14 18:12:26 neelin + * Added project names and defaults files (using volume name). + * Added process id to log file name. + * Moved temporary files to subdirectory. + * + * Revision 2.4 1995/02/09 13:51:26 neelin + * Mods for irix 5 lint. + * + * Revision 2.3 1995/02/08 19:31:47 neelin + * Moved ARGSUSED statements for irix 5 lint. + * + * Revision 2.2 1994/12/07 09:45:59 neelin + * Fixed called to ioctl to get rid of type mismatch warning messages. + * + * Revision 2.1 94/12/07 08:20:10 neelin + * Added support for irix 5 decnet. + * + * Revision 2.0 94/09/28 10:35:32 neelin + * Release of minc version 0.2 + * + * Revision 1.5 94/09/28 10:34:50 neelin + * Pre-release + * + * Revision 1.4 94/01/18 14:23:41 neelin + * Changed bzero to memset. + * + * Revision 1.3 93/11/30 14:42:13 neelin + * Copies to minc format. + * + * Revision 1.2 93/11/25 13:26:55 neelin + * Working version. + * + * Revision 1.1 93/11/23 14:11:54 neelin + * Initial revision + * +@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 <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <signal.h> +#include <dicomserver.h> + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : connection_okay +@INPUT : sockfd - input file descriptor which might be a socket +@OUTPUT : (none) +@RETURNS : TRUE if connection is okay, FALSE otherwise +@DESCRIPTION: Checks whether the connection is allowed. Looks at sockfd + to find out if remote host is allowed to connect. If sockfd + is a file and not a socket, then the connection is allowed. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 20, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +private int connection_okay(int sockfd) +{ + struct sockaddr_in us, them; + int status; + int namelen; + extern int Do_logging; + + /* Get our own id. If sockfd is a file, then its okay. Check that we + have an internet connection. */ + namelen = sizeof(us); + if (getsockname(sockfd, &us, &namelen) != 0) { + if (errno == ENOTSOCK) + return TRUE; + else { + (void) fprintf(stderr, "Unable to get our own host address.\n"); + return FALSE; + } + } + else if (us.sin_family != AF_INET) { + (void) fprintf(stderr, "Connection is not from network.\n"); + return FALSE; + } + + /* Try to get id of host at other end of connection */ + namelen = sizeof(us); + status = getpeername(sockfd, &them, &namelen); + if (status != 0) { + (void) fprintf(stderr, "Unable to check connection source.\n"); + return FALSE; + } + + /* */ + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "Connection from %s ", inet_ntoa(them.sin_addr)); + } + + /* modified by rhoge to relax network restriction from class C to B */ + + /* Compare the addresses. Make sure that we have the same IP domain + assuming class B structure. */ + if ((us.sin_addr.s_addr & IN_CLASSB_NET) != + (them.sin_addr.s_addr & IN_CLASSB_NET)) { + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr,"Request not from same IP domain (class B)\n"); + (void) fprintf(stderr,"Our ip address: %d\n",us.sin_addr.s_addr); + (void) fprintf(stderr,"Their ip address: %d\n",them.sin_addr.s_addr); + (void) fprintf(stderr,"CLASSB mask: %d\n",IN_CLASSB_NET); + (void) fprintf(stderr, + "Connection from %s ", inet_ntoa(them.sin_addr)); + (void) fprintf(stderr, "refused.\n"); + } + /* return FALSE; */ + } + + /* Log a warning if hosts not from same class C network */ + if ((us.sin_addr.s_addr & IN_CLASSC_NET) != + (them.sin_addr.s_addr & IN_CLASSC_NET)) { + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr,"Request not from same IP domain (class C)\n"); + (void) fprintf(stderr, + "Connection from %s ", inet_ntoa(them.sin_addr)); + } + } + + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "accepted.\n"); + } + + return TRUE; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : open_connection +@INPUT : argc - number of command-line arguments + argv - array of command-line arguments +@OUTPUT : afpin - Acr file pointer for input + afpout - Acr file pointer for output +@RETURNS : (nothing) +@DESCRIPTION: Opens the connection for reading writing dicom messages. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +/* ARGSUSED */ +public void open_connection(int argc, char *argv[], + Acr_File **afpin, Acr_File **afpout) +{ + /* Set default file pointers */ + *afpin = *afpout = NULL; + + /* Check for a valid connection */ + if (!connection_okay(fileno(stdin))) return; + + /* Open the connection */ + *afpin=acr_initialize_dicom_input(stdin, 0, acr_stdio_read); + *afpout=acr_initialize_dicom_output(stdout, 0, acr_stdio_write); + + /* Ignore SIGPIPE errors in case connection gets closed when we are + doing output */ + (void) signal(SIGPIPE, SIG_IGN); +} +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/parse_dicom_groups.c @@ -0,0 +1,128 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : parse_dicom_groups.c +@DESCRIPTION: Routine to parse dicom file - replicates postconditions + of save_transferred_object.c +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : June 2001 (Rick Hoge) +@MODIFIED : + * $Log: parse_dicom_groups.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.2 2002/03/19 13:13:56 rhoge + * initial working mosaic support - I think time is scrambled though. + * + * Revision 1.1 2001/12/31 17:27:01 rhoge + * adding file to repository - works for numa4 non-mos files now + * +---------------------------------------------------------------------------- */ + +#include <dicomserver.h> + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : parse_dicom_groups +@INPUT : group_list - list of acr-nema groups that make up object +@OUTPUT : data_info - information about data object +@RETURNS : (nothing) +@DESCRIPTION: Routine to parse dicom object +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : June 2001 (Rick Hoge) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void parse_dicom_groups(Acr_Group group_list, Data_Object_Info *data_info) +{ + Acr_Group group; + Acr_Element element; + char patient_name[256]; + + unsigned short AcqMat[4]; + unsigned short freq_rows; + unsigned short freq_cols; + unsigned short phase_rows; + unsigned short phase_cols; + + int maxlen = sizeof(Cstring) - 1; + + // Get info to construct unique identifiers for study, series/acq + // for file processing + get_identification_info(group_list, + &(data_info->study_id), &(data_info->acq_id), + &(data_info->rec_num), &(data_info->image_type)); + + // Get number of echos, echo number, number of dynamic scans and + // dynamic_scan_number + data_info->num_echoes = + acr_find_int(group_list, SPI_Number_of_echoes, 999); + data_info->echo_number = + acr_find_int(group_list, ACR_Echo_number, 999); + data_info->num_dyn_scans = + acr_find_int(group_list, ACR_Acquisitions_in_series, 999); + data_info->dyn_scan_number = + acr_find_int(group_list, ACR_Acquisition, 999); + data_info->global_image_number = + acr_find_int(group_list, ACR_Image, 999); + + /* rhoge: + new info added to data_info by rhoge: nominal number of slices; + this is used in detection of a stream of files with the same + acquisition ID number in which there are more files than + slices. If the number of signal averages is greater than one, + we will assume that this means the acquisition loop was used for + dynamic scanning. + + WARNINGS: the same thing may need to be done with `number of + partitions' for it to work with 3D scans */ + + data_info->num_slices_nominal = + acr_find_int(group_list, SPI_Number_of_slices_nominal, 999); + data_info->slice_number = 999; + + // identification info needed to generate unique session id + // for file names + data_info->study_date = + acr_find_int(group_list, ACR_Study_date, 999); + data_info->study_time = + acr_find_int(group_list, ACR_Study_time, 999); + data_info->scanner_serialno = + acr_find_int(group_list, ACR_Device_serial_number, 999); + + // identification info needed to determine if mosaics used + + element = acr_find_group_element(group_list,ACR_Acquisition_matrix); + acr_get_element_short_array(element,4,AcqMat); + + freq_rows = AcqMat[0]; + freq_cols = AcqMat[1]; + + phase_rows = AcqMat[2]; + phase_cols = AcqMat[3]; + + // rows in acq matrix is larger of freq rows and freq columns: + data_info->acq_rows = ( freq_rows > freq_cols ? freq_rows : freq_cols ); + // all images are square, at this time + data_info->acq_cols = data_info->acq_rows; + + data_info->rec_rows = acr_find_int(group_list,ACR_Rows, 999); + data_info->rec_cols = acr_find_int(group_list,ACR_Columns, 999); + + data_info->num_mosaic_rows=acr_find_int(group_list,EXT_Mosaic_rows, 999); + data_info->num_mosaic_cols=acr_find_int(group_list,EXT_Mosaic_columns,999); + data_info->num_slices_in_file= + acr_find_int(group_list,EXT_Slices_in_file,999); + + // sequence, protocol names (useful for debugging): + + (void) strncpy(data_info->sequence_name, + acr_find_string(group_list,ACR_Sequence_name,""),maxlen); + (void) strncpy(data_info->protocol_name, + acr_find_string(group_list,ACR_Protocol_name,""),maxlen); + + return; + +} + +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/progress.c @@ -0,0 +1,54 @@ + +// This function prints a text progress bar within a term window +// Input arguments assume a for loop starting at zero: +// +// for (index = 0; index < end; index++) { ... + +#include <stdio.h> +#include <math.h> + +int progress(long index, int end, char *message) { + + int ix; + int width = 50; + int nchars; + + if (index == 0) { + + if (strlen(message) > 20) { + // truncate message if too long + message[20] = '\0'; + } + + printf("%-20s |<--",message); + for (ix = 0; ix < width; ix++) { + printf(" "); + } + printf("|"); + for (ix = 0; ix < width+1; ix++) { + printf("\b"); + } + } else if ((index > 0) && (index < end)) { + + nchars = (((float)index/(float)(end-1)) * width) - + floor(((float)(index-1)/(float)(end-1)) * width); + + for (ix = 0; ix < nchars; ix++) { + printf("\b->"); + (void) fflush(stdout); + } + + // print terminating newline at end if we're done + if (index == end-1) { + printf("\n"); + } + } else { + fprintf(stderr,"PROGRESS: bad input indices!\n"); + return 0; + } +} + + + + +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/project_file.c @@ -0,0 +1,214 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : project_file.c +@DESCRIPTION: Code to do manipulate the project files (files containing + info on what to do with files for each project). +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + * $Log: project_file.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * + * Revision 6.1 1999/10/29 17:51:56 neelin + * Fixed Log keyword + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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 <sys/types.h> +#include <unistd.h> +#include <dirent.h> +#include <ctype.h> +#include <dicomserver.h> + +/* Function prototypes */ +/************************************************/ +/* Commented out by rhoge, put back in by leili */ + int gethostname (char *name, size_t namelen); +/************************************************/ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : read_project_file +@INPUT : project_name - name to use for project file +@OUTPUT : file_prefix - string used as prefix for output files + (can be NULL) + output_uid - uid for created files (can be NULL). Set to + INT_MIN if file not found. + output_gid - gid for created files (can be NULL). Set to + INT_MIN if file not found. + command_line - command to execute on new file (can be NULL) + maxlen_command - maximum length for command_line +@RETURNS : TRUE if an error occurs, FALSE otherwise. +@DESCRIPTION: Routine to read in default information for a given project. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 14, 1995 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public int read_project_file(char *project_name, + char *file_prefix, + int *output_uid, int *output_gid, + char *command_line, int maxlen_command) +{ + char project_string[256]; + char output_default_file[256]; + char temp_file_prefix[256]; + int temp_uid, temp_gid; + char temp_command_line[4]; + int ichar, ochar; + int length, index; + FILE *fp; + char string[512]; + int project_name_given; + + /* Check that the user actually wants return values */ + if (file_prefix == NULL) file_prefix = temp_file_prefix; + if (output_uid == NULL) output_uid = &temp_uid; + if (output_gid == NULL) output_gid = &temp_gid; + if ((command_line == NULL) || (maxlen_command <= 0)) { + command_line = temp_command_line; + maxlen_command = sizeof(temp_command_line); + } + + /* Set some default values */ + file_prefix[0] = '\0'; + command_line[0] = '\0'; + *output_uid = *output_gid = INT_MIN; + + /* Copy the project name, removing spaces */ + if (project_name != NULL) + length = strlen(project_name); + else + length = 0; + for (ichar=0, ochar=0; + (ichar < length) && (ochar < sizeof(project_string)-1); + ichar++) { + if (isprint((int) project_name[ichar]) && + !isspace((int) project_name[ichar])) { + project_string[ochar] = (char) toupper((int) project_name[ichar]); + ochar++; + } + } + project_string[ochar] = '\0'; + + /* Get the host name if there is no project string */ + project_name_given = (strlen(project_string) > (size_t) 0); + if (!project_name_given) + (void) gethostname(project_string, sizeof(project_string) - 1); + (void) sprintf(output_default_file, "%s/%s%s", + OUTPUT_DEFAULT_FILE_DIR, OUTPUT_DEFAULT_FILE_PREFIX, + project_string); + + /* Open and read the defaults file - if it isn't there then return TRUE + if the caller gave a project name */ + if ((fp=fopen(output_default_file, "r")) == NULL) { + return project_name_given; + } + if (fgets(string, (int) sizeof(string), fp) == NULL) { + return TRUE; + } + if (sscanf(string, "%s %d %d", file_prefix, output_uid, output_gid) != 3) { + return TRUE; + } + (void) fgets(command_line, maxlen_command, fp); + index = strlen(command_line) - 1; + if ((index >= 0) && (command_line[index] == '\n')) + command_line[index] = '\0'; + (void) fclose(fp); + + return FALSE; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : get_project_option_string +@INPUT : (none) +@OUTPUT : project_option_string - string containing list of options + for project name + maxlen_project_option - maximum length for the string (including + '\0' at end) +@RETURNS : (nothing) +@DESCRIPTION: Routine to get a list of possibilities for the project name + (looking for appropriately named files). +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 14, 1995 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void get_project_option_string(char *project_option_string, + int maxlen_project_option) +{ + DIR *dirp; + struct dirent *dp; + int length; + char *name, *filler; + int compare_length; + + /* Set up the string */ + if (maxlen_project_option > 0) { + project_option_string[0] = '\0'; + length = 1; + } + + /* Open directory */ + if ((dirp = opendir(OUTPUT_DEFAULT_FILE_DIR)) == NULL) + return; + + /* Loop through directory entries */ + compare_length = strlen(OUTPUT_DEFAULT_FILE_PREFIX); + while ((dp = readdir(dirp)) != NULL) { + + /* Check for an entry with the right prefix */ + if (strncmp(OUTPUT_DEFAULT_FILE_PREFIX, + dp->d_name, compare_length) == 0) { + + /* Check for an uppercase letter */ + if ((strlen(dp->d_name) > (size_t) compare_length) && + (isupper(dp->d_name[compare_length]))) { + name = &dp->d_name[compare_length]; + + /* Check that we can read the project file */ + if (!read_project_file(name, NULL, NULL, NULL, NULL, 0)) { + if (length > 1) + filler = ", "; + else + filler = ""; + if ((strlen(name) + length + strlen(filler)) + < (size_t) maxlen_project_option) { + (void) strcat(strcat(project_option_string, filler), name); + length += strlen(filler) + strlen(name); + } + } /* We can read the project file */ + + } /* Found uppercase letter */ + + } /* Found file matching prefix */ + + } /* Loop over files */ +}
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/reply.c @@ -0,0 +1,617 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : reply.c +@DESCRIPTION: Routines for dealing with dicom messages. +@GLOBALS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + * $Log: reply.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.2 2001/02/26 06:14:39 rhoge + * modified to allow target directory to be passed as AE title (only 16 chars) + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * + * Revision 6.3 1999/10/29 17:51:57 neelin + * Fixed Log keyword + * + * Revision 6.2 1999/08/05 20:01:16 neelin + * Check for broken Siemens software using a list of implementation UIDs. + * + * Revision 6.1 1998/05/19 19:27:43 neelin + * Test for Siemens Vision machine by looking for implementation uid + * rather than AE title + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.1 1997/07/08 23:15:09 neelin + * Added support for C_ECHO command. + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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 <dicomserver.h> + +extern int Do_logging; + +/* List of implementation UIDs for Siemens Vision scanners with broken + handling of transfer syntax. These should be in ascending order of + software version, since the UID for version VB33A is used to identify + a change in element use. */ +static char *SPI_Vision_Implementation_UIDs[] = { + "2.16.840.1.113669.2.931128", + "1.3.12.2.1107.5.1995.1", /* Version VB33A */ + /* Added By Leili */ + "1.3.12.2.1107.5.2", /* Version MREASE_VA21A */ + NULL +}; +/* Index into above array for version VB33A */ +#define SPI_VISION_VB33A_INDEX 1 +/* Global to indicate whether we have a pre VB33A version */ +int SPI_Vision_version_pre33A = TRUE; + +/* Macros */ +#define SAVE_SHORT(group, elid, value) \ + acr_group_add_element(group, \ + acr_create_element_short(elid, (unsigned short) (value))) + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : uid_equal +@INPUT : uid1 + uid2 +@OUTPUT : (nothing) +@RETURNS : TRUE if uid's are equal, FALSE otherwise +@DESCRIPTION: Responds to READYq message +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 21, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +private int uid_equal(char *uid1, char *uid2) +{ + int len1, len2, i; + + len1 = strlen(uid1); + len2 = strlen(uid2); + + /* Skip leading blanks */ + while (isspace(*uid1)) {uid1++;} + while (isspace(*uid2)) {uid2++;} + + /* Skip trailing blanks */ + for (i=len1-1; (i >= 0) && isspace(uid1[i]); i++) {} + if (isspace(uid1[i+1])) uid1[i+1] = '\0'; + for (i=len2-1; (i >= 0) && isspace(uid1[i]); i++) {} + if (isspace(uid1[i+1])) uid1[i+1] = '\0'; + + /* Compare the strings */ + return (strcmp(uid1, uid2) == 0); +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : make_message +@INPUT : group_list +@OUTPUT : (nothing) +@RETURNS : output message. +@DESCRIPTION: Convert a group list into a message. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 24, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +private Acr_Message make_message(Acr_Group group_list) +{ + Acr_Group next_group, group; + Acr_Message output_message; + + /* Create the output message */ + output_message = acr_create_message(); + + /* Loop through groups, adding them to the message */ + group = group_list; + while (group != NULL) { + next_group = acr_get_group_next(group); + acr_set_group_next(group, NULL); + acr_message_add_group(output_message, group); + group = next_group; + } + + return output_message; + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : associate_reply +@INPUT : input_message +@OUTPUT : project_name - name to use for project file + pres_context_id - presentation context id to use for output. If + this is < 0, then an error occurred. + byte_order - byte order to use for messages + vr_encoding - VR encoding to use for messages + maximum_length - maximum length of output PDU's +@RETURNS : output_message +@DESCRIPTION: Responds to an associate request message +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public Acr_Message associate_reply(Acr_Message input_message, + char **project_name, + int *pres_context_id, + Acr_byte_order *byte_order, + Acr_VR_encoding_type *vr_encoding, + long *maximum_length) +{ + Acr_Group group, input_group; + Acr_Element element, item, subitem, sublist; + Acr_Element out_item, out_subitem, out_sublist, out_list; + /***********************************/ + /* Added by Leili */ + Acr_Element my_element_test; + /***********************************/ + int found_best; + int best_pres_context_id, cur_pres_context_id; + int best_transfer_syntax_priority; + int use_implicit_little_endian; + char *best_abstract_syntax, *best_transfer_syntax, *cur_syntax; + int impuid; + + /* Print log message */ + if (Do_logging >= HIGH_LOGGING) { + (void) fprintf(stderr, "\n\nReceived associate request message:\n"); + acr_dump_message(stderr, input_message); + } + + /* Set default presentation context id to flag error */ + *pres_context_id = -1; + + /* Free project_name string if needed */ + if (*project_name != NULL) FREE(*project_name); + + /* Get the group list */ + input_group = acr_get_message_group_list(input_message); + + /* Get the project name from the DICOM application name */ + *project_name = strdup(acr_find_string(input_group, + DCM_PDU_Called_Ap_title, "")); + + /* Check that the project file is okay. If it is not found, try the host + name */ + + /* changed by rhoge so as not to clobber the aetitle in the + unlikely event that it is being used to pass a file name (starts + with `/' or `~') - this functionality is of limited usefulness, since + the AE title can not be longer than 16 characters */ + + if (read_project_file(*project_name, NULL, NULL, NULL, NULL, 0) + && !(!strncmp(*project_name,"/",1)||!strncmp(*project_name,"~",1))) { + FREE(*project_name); + *project_name = NULL; + if (read_project_file(*project_name, NULL, NULL, NULL, NULL, 0)) { + return associate_reply_reject(input_message, + ACR_ASSOC_RJ_CALLED_AP_TITLE_UNREC); + } + } + + /* Check for Siemens Vision implementation that lies about its + transfer syntaxes */ + + /**********************************************/ + /* The if and else statement is commented out by leili */ + // if (0) { + + use_implicit_little_endian = FALSE; + for (impuid=0; SPI_Vision_Implementation_UIDs[impuid] != NULL; impuid++) { + if (uid_equal(acr_find_string(input_group, + DCM_PDU_Implementation_class_uid, ""), + SPI_Vision_Implementation_UIDs[impuid])) { + use_implicit_little_endian = TRUE; + (void) fprintf(stderr, "just made the implicit little endian true\n "); + SPI_Vision_version_pre33A = (impuid < SPI_VISION_VB33A_INDEX); + break; + } + } + // } else { + + //use_implicit_little_endian = FALSE; + + //} + /**********************************************/ + + + /* Get maximum length */ + *maximum_length = acr_find_long(input_group, DCM_PDU_Maximum_length, 0L); + + /* Get presentation context list */ + best_pres_context_id = -1; + best_abstract_syntax = NULL; + element = + acr_find_group_element(input_group, DCM_PDU_Presentation_context_list); + if ((element == NULL) || !acr_element_is_sequence(element)) { + (void) fprintf(stderr, "No presentation context list found\n"); + return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); + } + + /* Loop over presentation contexts */ + found_best = FALSE; + for (item = (Acr_Element) acr_get_element_data(element); + (item != NULL) && acr_element_is_sequence(item) && !found_best; + item = acr_get_element_next(item)) { + + /* Get presentation context info */ + sublist = (Acr_Element) acr_get_element_data(item); + + /* Get abstract syntax */ + subitem = acr_find_element_id(sublist, + DCM_PDU_Abstract_syntax); + if (subitem == NULL) continue; + cur_syntax = acr_get_element_string(subitem); + + /* Check whether this is either MR abstract syntax or we have + already found one (we take the first one if we cannot find + the one that we want) */ + if (uid_equal(cur_syntax, FAVORITE_ABSTRACT_SYNTAX)) + found_best = TRUE; + else if (best_abstract_syntax != NULL) + continue; + + /* Save the abstract syntax */ + best_abstract_syntax = cur_syntax; + /* Get presentation context id */ + subitem = acr_find_element_id(sublist, + DCM_PDU_Presentation_context_id); + if (subitem != NULL) + best_pres_context_id = acr_get_element_short(subitem); + else { + (void) fprintf(stderr, + "No presentation context - internal error\n"); + return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); + } + + /* Look for an appropriate transfer syntax */ + best_transfer_syntax = NULL; + best_transfer_syntax_priority = 0; + for (subitem = sublist; + subitem != NULL; + subitem=acr_find_element_id(acr_get_element_next(subitem), + DCM_PDU_Transfer_syntax)) { + + /* Check for syntaxes in descending order of preference */ + cur_syntax = acr_get_element_string(subitem); + if (uid_equal(cur_syntax, ACR_EXPLICIT_VR_BIG_END_UID)) { + if (best_transfer_syntax_priority < 3) { + best_transfer_syntax_priority = 3; + best_transfer_syntax = cur_syntax; + } + } + else if (uid_equal(cur_syntax, ACR_EXPLICIT_VR_LITTLE_END_UID)) { + if (best_transfer_syntax_priority < 2) { + best_transfer_syntax_priority = 2; + best_transfer_syntax = cur_syntax; + } + } + else if (uid_equal(cur_syntax, ACR_IMPLICIT_VR_LITTLE_END_UID)) { + if (best_transfer_syntax_priority < 1) { + best_transfer_syntax_priority = 1; + best_transfer_syntax = cur_syntax; + } + } + + } /* Loop over transfer syntaxes */ + + } /* Loop over presentation contexts */ + + + /* Check for machines that lie about their ability to do + different transfer syntaxes */ + if (use_implicit_little_endian) { + best_transfer_syntax = ACR_IMPLICIT_VR_LITTLE_END_UID; + } + + + /* Check that we found something useful */ + if ((best_pres_context_id < 0) || (best_abstract_syntax == NULL) || + (best_transfer_syntax == NULL)) { + (void) fprintf(stderr, + "Did not find understandable presentation context\n"); + return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); + } + + /****************************************************************************/ + /* Set the transfer syntax information */ + /* This part was commented out by rick, put it back in the program by leili */ + + if (uid_equal(best_transfer_syntax, ACR_EXPLICIT_VR_BIG_END_UID)) { + *byte_order = ACR_BIG_ENDIAN; + *vr_encoding = ACR_EXPLICIT_VR; + } + else if (uid_equal(best_transfer_syntax, ACR_EXPLICIT_VR_LITTLE_END_UID)) { + + /****************************************/ + /* this two lines were in rick's version */ + + *byte_order = ACR_LITTLE_ENDIAN; + *vr_encoding = ACR_EXPLICIT_VR; + /******************************************/ + } + else if (uid_equal(best_transfer_syntax, ACR_IMPLICIT_VR_LITTLE_END_UID)) { + *byte_order = ACR_LITTLE_ENDIAN; + *vr_encoding = ACR_IMPLICIT_VR; + } + else { + (void) fprintf(stderr, + "Did not understand transfer syntax.\n"); + return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); + } + + /*************************************************************************/ + /* Create the reply */ + group = acr_create_group(DCM_PDU_GRPID); + + /* Save the PDU type */ + SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_ASSOC_AC); + + /* Save the caller and calling AE titles */ + acr_group_add_element(group, + acr_create_element_string(DCM_PDU_Called_Ap_title, + acr_find_string(input_group, DCM_PDU_Called_Ap_title, ""))); + + /****************************************/ + /* Added by Leili */ + my_element_test =acr_create_element_string(DCM_PDU_Called_Ap_title,acr_find_string(input_group, DCM_PDU_Called_Ap_title, "")); + (void) fprintf(stderr, "This is the called Ap_title: %s \n", my_element_test->data_pointer); + /*****************************************/ + + acr_group_add_element(group, + acr_create_element_string(DCM_PDU_Calling_Ap_title, + acr_find_string(input_group, DCM_PDU_Calling_Ap_title, ""))); + + /****************************************/ + /* Added by Leili */ + my_element_test =acr_create_element_string(DCM_PDU_Calling_Ap_title,acr_find_string(input_group, DCM_PDU_Calling_Ap_title, "")); + (void) fprintf(stderr, "This is the calling Ap_title: %s \n", my_element_test->data_pointer); + /*****************************************/ + + /* Add the application context name */ + acr_group_add_element(group, + acr_create_element_string(DCM_PDU_Application_context, + acr_find_string(input_group, DCM_PDU_Application_context, + ACR_APPLICATION_CONTEXT_UID))); + + /* Loop over presentation contexts */ + item = (Acr_Element) + acr_get_element_data + (acr_find_group_element(input_group, + DCM_PDU_Presentation_context_list)); + out_list = NULL; + for (;(item != NULL); item = acr_get_element_next(item)) { + + if (!acr_element_is_sequence(item)) continue; + + /* Get presentation context info */ + sublist = (Acr_Element) acr_get_element_data(item); + + /* Save the id */ + subitem = acr_find_element_id(sublist, + DCM_PDU_Presentation_context_id); + if (subitem == NULL) continue; + cur_pres_context_id = acr_get_element_short(subitem); + + /* Create the presentation context */ + out_sublist = NULL; + out_subitem = acr_create_element_short(DCM_PDU_Presentation_context_id, + cur_pres_context_id); + out_sublist = acr_element_list_add(out_sublist, out_subitem); + + /* Accept or reject */ + out_subitem = acr_create_element_short(DCM_PDU_Result, + ((cur_pres_context_id == best_pres_context_id) ? + ACR_ASSOC_PR_CN_ACCEPT : ACR_ASSOC_PR_CN_REJECT)); + out_sublist = acr_element_list_add(out_sublist, out_subitem); + + /* Add the transfer syntax */ + out_subitem = acr_create_element_string(DCM_PDU_Transfer_syntax, + best_transfer_syntax); + out_sublist = acr_element_list_add(out_sublist, out_subitem); + + /* Add this context to the list */ + out_item = + acr_create_element_sequence(DCM_PDU_Presentation_context_reply, + out_sublist); + out_list = acr_element_list_add(out_list, out_item); + } + + /* Create the presentation context list element */ + element = + acr_create_element_sequence(DCM_PDU_Presentation_context_reply_list, + out_list); + acr_group_add_element(group, element); + + /* Add the user information */ + acr_group_add_element(group, + acr_create_element_long(DCM_PDU_Maximum_length, 0L)); + + /* Set the presentation context id to indicate success */ + *pres_context_id = best_pres_context_id; + + return make_message(group); + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : associate_reply_reject +@INPUT : input_message +@OUTPUT : reason +@RETURNS : output_message +@DESCRIPTION: Responds to an associate request message with a rejection +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 21, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +/* ARGSUSED */ +public Acr_Message associate_reply_reject(Acr_Message input_message, + int reason) +{ + Acr_Group group; + + /* Create the reply */ + group = acr_create_group(DCM_PDU_GRPID); + + /* Save the PDU type */ + SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_ASSOC_RJ); + + /* Give the result, source and reason */ + SAVE_SHORT(group, DCM_PDU_Result, ACR_ASSOC_RJ_PERM); + SAVE_SHORT(group, DCM_PDU_Source, ACR_ASSOC_RJ_USER); + SAVE_SHORT(group, DCM_PDU_Reason, reason); + + return make_message(group); +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : release_reply +@INPUT : input_message +@OUTPUT : (nothing) +@RETURNS : output_message +@DESCRIPTION: Responds to READYq message +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public Acr_Message release_reply(Acr_Message input_message) +{ + Acr_Group group; + + /* Print log message */ + if (Do_logging >= HIGH_LOGGING) { + (void) fprintf(stderr, "\n\nReceived release request message:\n"); + acr_dump_message(stderr, input_message); + } + + /* Create the reply */ + group = acr_create_group(DCM_PDU_GRPID); + + /* Save the PDU type */ + SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_REL_RP); + + return make_message(group); +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : abort_reply +@INPUT : input_message +@OUTPUT : (nothing) +@RETURNS : output_message +@DESCRIPTION: Responds to GCENDq message +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public Acr_Message abort_reply(Acr_Message input_message) +{ + Acr_Group group; + + /* Print log message */ + if (Do_logging >= HIGH_LOGGING) { + (void) fprintf(stderr, "\n\nReceived abort message:\n"); + acr_dump_message(stderr, input_message); + } + + /* Create the reply */ + group = acr_copy_group_list(acr_get_message_group_list(input_message)); + + return make_message(group); +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : data_reply +@INPUT : input_message + file_prefix +@OUTPUT : new_file_name (must be freed by caller) +@RETURNS : output_message +@DESCRIPTION: Responds to SENDq message +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 22, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public Acr_Message data_reply(Acr_Message input_message) +{ + Acr_Group group, input_group; + int reply_command; + + + /* Print log message */ + if (Do_logging >= HIGH_LOGGING) { + (void) fprintf(stderr, "\n\nReceived data message:\n"); + acr_dump_message(stderr, input_message); + } + + /* Get the input group list */ + input_group = acr_get_message_group_list(input_message); + + + /* Figure out the reply that we need */ + switch (acr_find_short(input_group, ACR_Command, -1)) { + case ACR_C_STORE_RQ: + reply_command = ACR_C_STORE_RSP; break; + case ACR_C_ECHO_RQ: + reply_command = ACR_C_ECHO_RSP; break; + default: + reply_command = ACR_C_ECHO_RSP; break; + } + + /* Create the reply */ + group = acr_create_group(ACR_MESSAGE_GID); + + /* Save appropriate stuff */ + acr_group_add_element(group, + acr_create_element_string(ACR_Affected_SOP_class_UID, + acr_find_string(input_group, ACR_Affected_SOP_class_UID, ""))); + SAVE_SHORT(group, ACR_Command, reply_command); + SAVE_SHORT(group, ACR_Message_id_brt, + acr_find_short(input_group, ACR_Message_id, 0)); + SAVE_SHORT(group, ACR_Dataset_type, ACR_NULL_DATASET); + SAVE_SHORT(group, ACR_Status, ACR_SUCCESS); + if (reply_command == ACR_C_STORE_RSP) { + acr_group_add_element(group, + acr_create_element_string(ACR_Affected_SOP_instance_UID, + acr_find_string(input_group, ACR_Affected_SOP_instance_UID, ""))); + } + + return make_message(group); + +} +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/save_transferred_object.c @@ -0,0 +1,192 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : save_transferred_object.c +@DESCRIPTION: Routine to save data object. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + * $Log: save_transferred_object.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.2 2001/12/31 18:27:21 rhoge + * modifications for dicomreader processing of Numaris 4 dicom files - at + * this point code compiles without warning, but does not deal with + * mosaiced files. Also will probably not work at this time for Numaris + * 3 .ima files. dicomserver may also not be functional... + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * + * Revision 6.1 1999/10/29 17:51:58 neelin + * Fixed Log keyword + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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 <dicomserver.h> +extern int Do_logging; + + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : save_transferred_object +@INPUT : group_list - list of acr-nema groups that make up object + file_prefix - prefix for file names +@OUTPUT : new_file_name - name for newly created file + data_info - information about data object +@RETURNS : (nothing) +@DESCRIPTION: Routine to save the object in a file. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 24, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void save_transferred_object(Acr_Group group_list, char *file_prefix2, + char **new_file_name, + Data_Object_Info *data_info) +{ + Acr_Group group; + Acr_Element element; + char temp_name[256]; + char patient_name[256]; + double study_id; + int acquisition_id, image_id; + Acr_File *afp; + FILE *fp; + Acr_Status status; + Acr_VR_encoding_type vr_encoding; + Acr_byte_order byte_order; + static int file_counter = 0; + /* Added by Leili */ + char full_path[256]; + + /* Get the VR encoding state and byte order */ + element = acr_get_group_element_list(group_list); + + + vr_encoding = acr_get_element_vr_encoding(element); + + byte_order = acr_get_element_byte_order(element); + + /* Get data info */ + get_identification_info(group_list, + &(data_info->study_id), &(data_info->acq_id), + &(data_info->rec_num), &(data_info->image_type)); + + /* Get number of echos, echo number, number of dynamic scans and + dynamic_scan_number */ + data_info->num_echoes = + acr_find_int(group_list, SPI_Number_of_echoes, 1); + data_info->echo_number = + acr_find_int(group_list, ACR_Echo_number, 1); + data_info->num_dyn_scans = + acr_find_int(group_list, ACR_Acquisitions_in_series, 1); + data_info->dyn_scan_number = + acr_find_int(group_list, ACR_Series, 1); + + /* rhoge: + new info added to data_info by rhoge: nominal number of slices; + this is used in detection of a stream of files with the same + acquisition ID number in which there are more files than + slices. If the number of signal averages is greater than one, + we will assume that this means the acquisition loop was used for + dynamic scanning. + + WARNINGS: the same thing may need to be done with `number of + partitions' for it to work with 3D scans */ + + data_info->num_slices_nominal = + acr_find_int(group_list, SPI_Number_of_slices_nominal, 1); + + /* Look for patient name */ + element = acr_find_group_element(group_list, ACR_Patient_name); + if (element != NULL) { + string_to_filename(acr_get_element_string(element), patient_name, + sizeof(patient_name)); + } + if ((element == NULL) || (strlen(patient_name) == 0)) + (void) strcpy(patient_name, "unknown"); + + /* Look for study and image numbers */ + study_id = data_info->study_id; + acquisition_id = data_info->acq_id; + image_id = acr_find_int(group_list, ACR_Image, 0); + + /* Added by Leili */ + //(void) strcpy(full_path, "/software/source/dicomserver_test/"); + (void) strcpy(full_path, file_prefix2); + //(void) strcat(full_path, file_prefix); + (void) sprintf(temp_name, "dicom-%s-%f/", patient_name, study_id); + strcat (full_path, temp_name); + /* Create the new file name */ + /* The %s ---> full_path is added by Leili to the begining of the temp_name */ + (void) sprintf(temp_name, "%s%s-%04d-%s_%f_%d_%d.dcm", + full_path,"dicom", file_counter++, patient_name, study_id, + acquisition_id, image_id); + + /* Added by Leili */ + /* create the session directory if none exists */ + if (mkdir(full_path, 0777) && (Do_logging> 2)) { + (void) fprintf(stderr, "Directory %s exists ...\n", full_path); + } + + /* Create the file and write out the data */ + fp = fopen(temp_name, "w"); + if (fp == NULL) { + (void) fprintf(stderr, "Error opening file for write: %s\n", + temp_name); + } + else { + /* Set up the output stream */ + afp = acr_file_initialize(fp, 0, acr_stdio_write); + acr_set_vr_encoding(afp, vr_encoding); + acr_set_byte_order(afp, byte_order); + + /* Loop over groups */ + group = group_list; + status = ACR_OK; + while ((group != NULL) && (status == ACR_OK)) { + + /* Write out the group */ + status = acr_output_group(afp, group); + if (status != ACR_OK) { + (void) fprintf(stderr, "Error writing file %s\n", + temp_name); + } + group = acr_get_group_next(group); + + } + + /* Close the file */ + acr_file_free(afp); + (void) fclose(fp); + } + + /* Copy the name */ + *new_file_name = strdup(temp_name); + return; + +}
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_dicom_read.c @@ -0,0 +1,1196 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : siemens_dicom_read.c +@DESCRIPTION: Code to read siemens dicom files and get info from them. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + * $Log: siemens_dicom_read.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.12 2002/05/01 21:29:34 rhoge + * removed MrProt from minc header - encountered files with large strings, + * causing seg faults + * + * Revision 1.11 2002/04/26 03:27:03 rhoge + * fixed MrProt problem - replaced fixed lenght char array with malloc + * + * Revision 1.10 2002/04/08 17:26:34 rhoge + * added additional sequence info to minc header + * + * Revision 1.9 2002/03/27 18:57:50 rhoge + * added diffusion b value + * + * Revision 1.8 2002/03/19 13:13:56 rhoge + * initial working mosaic support - I think time is scrambled though. + * + * Revision 1.7 2001/12/31 18:27:21 rhoge + * modifications for dicomreader processing of Numaris 4 dicom files - at + * this point code compiles without warning, but does not deal with + * mosaiced files. Also will probably not work at this time for Numaris + * 3 .ima files. dicomserver may also not be functional... + * + * Revision 1.6 2000/12/14 21:33:13 rhoge + * code modifications to restore measurement loop support that was broken + * by changes to support acqusition loop scanning + * + * Revision 1.5 2000/12/13 13:25:36 rhoge + * removed dummy printf from convert_time_to_seconds - turns out that + * buggy behaviour was an optimization problem + * + * Revision 1.4 2000/12/12 19:27:52 rhoge + * changed atof(acr_find_string) back to acr_find_double after realizing + * that these functions assume string representation + * + * Revision 1.3 2000/12/12 14:43:22 rhoge + * fixed syntax error (dangling comment symbol) from removal of debug + * printfs - compiles now + * + * Revision 1.2 2000/12/11 20:01:44 rhoge + * fixed code for frame time computation - ACR vals are strings. Also + * inserted dummy fprintf statement in convert_time_to_seconds as this + * seems to salvage Linux problems + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * -now always use ACR_Series for run number (seemed to be + * problems with test introduced in 6.1) + * -added code to detect use of acquisition loop and also to handle + * `corrected' siemens acq loop scans + * -changed code to use registration time instead of scan time for + * session id + * -got rid of extraneous acquisition id digit + * -added technical information about data acquisition + * + * Revision 6.2 1999/10/29 17:51:58 neelin + * Fixed Log keyword + * + * Revision 6.1 1999/08/05 20:00:34 neelin + * Get acquisition id from series or study element, depending on the + * version of the Siemens software. + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.1 1997/09/10 19:36:13 neelin + * Small fix to set default direction cosines when they are absent from the + * dicom data. + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.1 1997/06/13 12:51:21 neelin + * Changed definition of time index and acquisition id to match change + * in Siemens dicom software. + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.2 1997/03/11 13:10:48 neelin + * Working version of dicomserver. + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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 <dicomserver.h> +#include <math.h> + +extern int SPI_Vision_version_pre33A; +File_Type file_type; + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : get_file_info +@INPUT : group_list - input data +@OUTPUT : file_info - file-specific info + general_info - general information about files +@RETURNS : (nothing) +@DESCRIPTION: Routine to extract information from a group list +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 25, 1993 (Peter Neelin) +@MODIFIED : Modified Feb. 2000 by Rick Hoge to handle Acquisition loop mode + : if assume_acq_loop is FALSE the other added variables don't + : matter +---------------------------------------------------------------------------- */ +public void get_file_info(Acr_Group group_list, File_Info *file_info, + General_Info *general_info) +{ + Mri_Index imri; + World_Index iworld, jworld; + Volume_Index ivolume; + int nrows, ncolumns, spatial_sizes[VOL_NDIMS]; + double study_id; + int acq_id, rec_num; + int cur_index; + int index; + int number_of_3D_partitions; + Orientation orientation; + World_Index volume_to_world[VOL_NDIMS]; + double coordinate[WORLD_NDIMS], dircos[VOL_NDIMS][WORLD_NDIMS]; + double steps[VOL_NDIMS], starts[VOL_NDIMS], slice_index; + Acr_Element_Id mri_index_list[MRI_NDIMS]; + Acr_Element_Id mri_total_list[MRI_NDIMS]; + + // Array of elements for mri dimensions + mri_index_list[SLICE] = SPI_Current_slice_number; + mri_index_list[ECHO] = ACR_Echo_number; + // dicom time element may be different on old systems + mri_index_list[TIME] = ACR_Acquisition; + mri_index_list[PHASE] = NULL; + mri_index_list[CHEM_SHIFT] = NULL; + mri_total_list[SLICE] = SPI_Number_of_slices_nominal; + mri_total_list[ECHO] = SPI_Number_of_echoes; + mri_total_list[TIME] = ACR_Acquisitions_in_series; + mri_total_list[PHASE] = NULL; + mri_total_list[CHEM_SHIFT] = NULL; + + // Get image dimensions + nrows = acr_find_short(group_list, ACR_Rows, 0); + ncolumns = acr_find_short(group_list, ACR_Columns, 0); + spatial_sizes[VROW] = nrows; + spatial_sizes[VCOLUMN] = ncolumns; + spatial_sizes[VSLICE] = 1; + + // Get intensity information + get_intensity_info(group_list, file_info); + + // Check for necessary values not found + if ((nrows <= 0) || (ncolumns <= 0) || + (file_info->bits_stored <= 0) || + (file_info->bits_alloc <= 0)) { + file_info->valid = FALSE; + return; + } + + // Get study, acq, rec, image type id's + get_identification_info(group_list, &study_id, &acq_id, &rec_num, NULL); + + // Get number of 3D partitions for working out number of slices + number_of_3D_partitions = + acr_find_int(group_list, SPI_Number_of_3D_raw_partitions_nominal, 1); + if (number_of_3D_partitions < 1) + number_of_3D_partitions = 1; + + // Get indices for image in current file + for (imri=0; imri < MRI_NDIMS; imri++) { + + if (mri_index_list[imri] != NULL) { + file_info->index[imri] = + /* note that for TIME this will use ACR_Acqusition, + which does not work for measurement loop scans */ + acr_find_int(group_list, mri_index_list[imri], 1); + } + else { + file_info->index[imri] = 1; + } + } + + // Get coordinate information + get_coordinate_info(group_list, file_info, &orientation, volume_to_world, + spatial_sizes, dircos, steps, starts, coordinate); + + // Replace slice index with slice position in hundredths of millimetres if + // we have more than one partition + if (number_of_3D_partitions > 1 | 1) { // ALWAYS USE POSITION (rhoge) + slice_index = file_info->coordinate[SLICE] * 100.0; + if (slice_index >= 0.0) { + slice_index += 0.5; + } else { + slice_index -= 0.5; + } + slice_index = (int) slice_index; + file_info->index[SLICE] = slice_index; + } + + // Set up general info on first pass + if (!general_info->initialized) { + + // Get row and columns sizes + general_info->nrows = nrows; + general_info->ncolumns = ncolumns; + + // Save the study, acquisition, reconstruction and image type + // identifiers + general_info->study_id = study_id; + general_info->acq_id = acq_id; + general_info->rec_num = rec_num; + + // No image type is available + // (rhoge: no longer true?) + general_info->image_type_string[0] = '\0'; + + /* Get dimension information */ + for (imri=0; imri < MRI_NDIMS; imri++) { + + /* Gets sizes */ + general_info->size[imri] = 1; + if (mri_total_list[imri] != NULL) { + general_info->total_size[imri] = + acr_find_int(group_list, mri_total_list[imri], 1); + } else { + general_info->total_size[imri] = 1; + } + + if (general_info->total_size[imri] < 1) { + general_info->total_size[imri] = 1; + } + + /* Check for 3D partitions for slice dimensions */ + if (imri == SLICE) { + general_info->total_size[imri] *= number_of_3D_partitions; + } + + /* Set initial values */ + general_info->default_index[imri] = file_info->index[imri]; + general_info->image_index[imri] = -1; + + /* Allocate space for index and coordinate arrays if total_size > 1. + Set the first values. */ + if (general_info->total_size[imri] > 1) { + general_info->indices[imri] = + MALLOC(general_info->total_size[imri] * sizeof(int)); + general_info->coordinates[imri] = + MALLOC(general_info->total_size[imri] * sizeof(double)); + for (index=0; index < general_info->total_size[imri]; index++) { + general_info->indices[imri][index] = -1; + general_info->coordinates[imri][index] = 0; + } + general_info->search_start[imri] = 0; + general_info->indices[imri][0] = file_info->index[imri]; + general_info->coordinates[imri][0] = file_info->coordinate[imri]; + } + } /* Loop over dimensions */ + + /* Get spatial coordinate information */ + general_info->slice_world = volume_to_world[VSLICE]; + general_info->row_world = volume_to_world[VROW]; + general_info->column_world = volume_to_world[VCOLUMN]; + for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { + iworld = volume_to_world[ivolume]; + general_info->step[iworld] = steps[ivolume]; + general_info->start[iworld] = starts[ivolume]; + for (jworld=0; jworld < WORLD_NDIMS; jworld++) { + general_info->dircos[volume_to_world[ivolume]][jworld] + = dircos[ivolume][jworld]; + } + } + + /* Set data type and range */ + if (file_info->bits_alloc <= 8) + general_info->datatype = NC_BYTE; + else + general_info->datatype = NC_SHORT; + general_info->is_signed = ((general_info->datatype == NC_SHORT) && + (file_info->bits_stored < 16)); + general_info->pixel_min = file_info->pixel_min; + general_info->pixel_max = file_info->pixel_max; + + /* Save display window info */ + general_info->window_min = file_info->window_min; + general_info->window_max = file_info->window_max; + + /* Get the rest of the header information */ + get_general_header_info(group_list, general_info); + + /* Copy the group list */ + general_info->group_list = acr_copy_group_list(group_list); + + /* Set initialized flag */ + general_info->initialized = TRUE; + + } /* Set up file info */ + /* Update general info and validate file on later passes */ + else { //-- + + /* Check for consistent data type */ + if (((general_info->datatype == NC_BYTE) && + (file_info->bits_alloc > 8)) || + ((general_info->datatype == NC_SHORT) && + (file_info->bits_alloc <= 8))) { + file_info->valid = FALSE; + return; + } + + /* Check row and columns sizes */ + if ((nrows != general_info->nrows) && + (ncolumns != general_info->ncolumns)) { + file_info->valid = FALSE; + return; + } + + /* Check study and acquisition id's */ + if ((general_info->study_id != study_id) || + (general_info->acq_id != acq_id)) { + file_info->valid = FALSE; + return; + } + + /* Look to see if indices have changed */ + for (imri=0; imri < MRI_NDIMS; imri++) { + + /* Get current index */ + cur_index = file_info->index[imri]; + + /* Check whether this index is in the list */ + if (general_info->size[imri] == 1) { + index = + ((cur_index == general_info->default_index[imri]) ? 0 : -1); + } + else { + index = search_list(cur_index, general_info->indices[imri], + general_info->size[imri], + general_info->search_start[imri]); + } + + /* If it is not, then add it */ + if (index < 0) { + + /* Check whether we can add a new index */ + if (general_info->size[imri] >= general_info->total_size[imri]) { + file_info->valid = FALSE; + return; + } + + /* Add the index and coordinate to the lists */ + index = general_info->size[imri]; + general_info->search_start[imri] = index; + general_info->indices[imri][index] = cur_index; + general_info->coordinates[imri][index] = + file_info->coordinate[imri]; + general_info->size[imri]++; + + } + } /* Loop over Mri_Index */ + + // note that number of slices will be wrong here for mosaics + // we add some other mosaic-relevant fields... + + general_info->num_mosaic_rows = + acr_find_int(group_list,EXT_Mosaic_rows,1); + general_info->num_mosaic_cols = + acr_find_int(group_list,EXT_Mosaic_columns,1); + general_info->num_slices_in_file = + acr_find_int(group_list,EXT_Slices_in_file,1); + general_info->sub_image_rows = + acr_find_short(group_list,EXT_Sub_image_rows, + acr_find_short(group_list, ACR_Rows, 0)); + general_info->sub_image_columns = + acr_find_short(group_list,EXT_Sub_image_columns, + acr_find_short(group_list, ACR_Rows, 0)); + + // Update display window info + if (general_info->window_min > file_info->window_min) + general_info->window_min = file_info->window_min; + if (general_info->window_max < file_info->window_max) + general_info->window_max = file_info->window_max; + + } // Update general info for this file + + // If we get to here, then we have a valid file + file_info->valid = TRUE; + return; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : get_identification_info +@INPUT : group_list - input data +@OUTPUT : study_id + acq_id + rec_num + image_type +@RETURNS : (nothing) +@DESCRIPTION: Routine to get image identification information. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 28, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void get_identification_info(Acr_Group group_list, + double *study_id, int *acq_id, + int *rec_num, int *image_type) +{ + int number_of_frames; + int number_of_averages; + + if (study_id != NULL) { + // generate a study ID number from date & time: yyyymmdd.hhmmss + // (should be unique enough for our application) + *study_id = (((float)acr_find_int(group_list, ACR_Study_date, 0)) + + ((float)acr_find_int(group_list, ACR_Study_time, 0))/1e6); + } + if (acq_id != NULL) { + + *acq_id = acr_find_int(group_list, ACR_Series, 0); + + number_of_frames = + acr_find_int(group_list, ACR_Acquisitions_in_series, 1); + + number_of_averages = + acr_find_int(group_list, ACR_Nr_of_averages, 1); + + /* Determine if measurement loop was used (rhoge) - + + if so, we replace the different series numbers with + ACR_Study_time, which is the same for all frames in a run. + This will aid in grouping the files later on. + + Criteria used for identification of meast loop: + + 1) more than one dynamic scan + + 2) number of dynamic scans NOT equal to nominal number of signal + averages (if they're equal, we assume acquisition loop scan) + + WARNING: it is possible that someone might use the + measurement loop do serial scans which each have multiple signal + averages. If NSA = the number of measts. in this case, then + the scan will not be recognized as a Meast loop scan and the + different frames will be placed in different series. To fix + this, we'd really need to look at the series numbers of + future and past files. It's also unlikely to happen but I'm + sure it will... + + This also means we should NOT correct the number of signal + averages on the sending side */ + + + /* if ((number_of_frames > 1) || (*acq_id == 0)) { (orig test) */ + + if ( (file_type == N3DCM) && + (number_of_frames > 1) && + (number_of_frames != number_of_averages)) { + + *acq_id = acr_find_int(group_list, ACR_Study_time, 0); + + } + } + if (rec_num != NULL) + *rec_num = 0; + if (image_type != NULL) + *image_type = 0; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : get_intensity_info +@INPUT : group_list - input data +@OUTPUT : file_info - file-specific info +@RETURNS : (nothing) +@DESCRIPTION: Routine to get intensity information from a group list +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 28, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void get_intensity_info(Acr_Group group_list, File_Info *file_info) +{ + double window_centre, window_width; + + /* Get pixel storage information */ + file_info->bits_alloc = acr_find_short(group_list, ACR_Bits_allocated, 0); + file_info->bits_stored = acr_find_short(group_list, ACR_Bits_stored, 0); + +#ifdef USE_DICOM_PIXEL_MIN_MAX + + // Get pixel value information + // I think this might wrongly assume that the ACR values min/max + // apply to the whole volume - it actually appears that they apply + // to the current slice. + + file_info->pixel_min = + acr_find_short(group_list, ACR_Smallest_pixel_value, 0); + + file_info->pixel_max = acr_find_short(group_list,ACR_Largest_pixel_value, + (1 << file_info->bits_stored) - 1); + +#else + + // for now, use bits_stored to determine dynamic range + // DICOM info on largest pixel applies to first slice, + // not whole volume - this caused problems (roundoff?) + // in Siemens Numaris 4 scans + + file_info->pixel_min = 0; + file_info->pixel_max = (1 << file_info->bits_stored) - 1; + +#endif + + file_info->slice_min = file_info->pixel_min; + file_info->slice_max = file_info->pixel_max; + + /* Get window min and max */ + window_centre = (file_info->slice_max + file_info->slice_min) / 2.0; + window_width = file_info->slice_max - file_info->slice_min; + window_centre = + acr_find_double(group_list, ACR_Window_centre, window_centre); + window_width = + acr_find_double(group_list, ACR_Window_width, window_width); + file_info->window_min = window_centre - window_width / 2.0; + file_info->window_max = window_centre + window_width / 2.0; + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : get_coordinate_info +@INPUT : group_list - input data + sizes - size of each spatial dimension +@OUTPUT : file_info - file-specific info + volume_to_world - volume index to world coordinate index mapping + dircos - direction cosines for spatial dimensions + steps - step sizes for spatial dimensions + starts - start positions for spatial dimensions (for a slice) + coordinate - coordinate of centre of slice +@RETURNS : (nothing) +@DESCRIPTION: Routine to get coordinate information for a slice from + a group list +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 28, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void get_coordinate_info(Acr_Group group_list, File_Info *file_info, + Orientation *orientation, + World_Index volume_to_world[VOL_NDIMS], + int sizes[VOL_NDIMS], + double dircos[VOL_NDIMS][WORLD_NDIMS], + double steps[VOL_NDIMS], + double starts[VOL_NDIMS], + double coordinate[WORLD_NDIMS]) +{ + Volume_Index ivolume; + World_Index iworld; + Acr_Element_Id dircos_elid[VOL_NDIMS]; + Acr_Element element; + int found_dircos[VOL_NDIMS], found_coordinate; + double frame_time, start_time; + double magnitude, largest, centre; + double darray[2]; + static Orientation orientation_list[WORLD_NDIMS] = + {SAGITTAL, CORONAL, TRANSVERSE}; + + // row/column unit vectors in public dicom element + double RowColVec[6]; + + if (file_type == N3DCM || file_type == IMA) { + + /* Set direction cosine element ids. Note that the reversal of rows and + columns is intentional - their idea of the meaning of theses labels is + different from ours. (Their row vector points along the row and not + along the row dimension.) */ + dircos_elid[VSLICE] = SPI_Image_normal; + dircos_elid[VROW] = SPI_Image_column; + dircos_elid[VCOLUMN] = SPI_Image_row; + + // Get direction cosines + for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { + found_dircos[ivolume] = FALSE; + element = acr_find_group_element(group_list, dircos_elid[ivolume]); + if (element == NULL) continue; + if (acr_get_element_numeric_array(element, WORLD_NDIMS, dircos[ivolume]) + != WORLD_NDIMS) continue; + // negate the X and Z coordinates + convert_numa3_coordinate(dircos[ivolume]); + found_dircos[ivolume] = TRUE; + } + } else { + // in normal dicom file, you just have row and column unit vectors + + // read in row/col vectors: + element = acr_find_group_element(group_list, + ACR_Image_orientation_patient); + + acr_get_element_numeric_array(element, 6, RowColVec); + + memcpy(dircos[VCOLUMN],RowColVec,sizeof(RowColVec[0])*3); + memcpy(dircos[VROW],&RowColVec[3],sizeof(RowColVec[0])*3); + found_dircos[VCOLUMN] = TRUE; + found_dircos[VROW] = TRUE; + + convert_dicom_coordinate(dircos[VROW]); + convert_dicom_coordinate(dircos[VCOLUMN]); + + // slice direction unit vector is cross product of row, col vectors: + dircos[VSLICE][0] = + dircos[VCOLUMN][1] * dircos[VROW][2] - + dircos[VCOLUMN][2] * dircos[VROW][1]; + + dircos[VSLICE][1] = + dircos[VCOLUMN][2] * dircos[VROW][0] - + dircos[VCOLUMN][0] * dircos[VROW][2]; + + dircos[VSLICE][2] = + dircos[VCOLUMN][0] * dircos[VROW][1] - + dircos[VCOLUMN][1] * dircos[VROW][0]; + found_dircos[VSLICE] = TRUE; + + // slice does not need to be converted because + // it was computed as the cross product of two + // corrected vectors + // convert_dicom_coordinate(dircos[VSLICE]); + + } + + // Normalize the direction cosines + for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { + magnitude = 0.0; + for (iworld=0; iworld < WORLD_NDIMS; iworld++) { + magnitude += dircos[ivolume][iworld] * dircos[ivolume][iworld]; + } + if (magnitude <= 0) { + found_dircos[ivolume] = FALSE; + continue; + } + magnitude = sqrt(magnitude); + for (iworld=0; iworld < WORLD_NDIMS; iworld++) { + dircos[ivolume][iworld] /= magnitude; + } + } + + // If we don't find direction cosines, then assume transverse volume + if (!found_dircos[VSLICE] || !found_dircos[VROW] || + !found_dircos[VCOLUMN]) { + for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { + for (iworld=0; iworld < WORLD_NDIMS; iworld++) { + dircos[ivolume][iworld] = + ((ivolume == (WORLD_NDIMS-iworld-1)) ? -1.0 : 0.0); + } + } + } + + // Figure out volume index to world index mapping and sign of direction + // cosines - the code below figures out the primary direction in x,y,z + // of each volume coordinate (row,col,slice) + for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { + largest = -1.0; + for (iworld=0; iworld < WORLD_NDIMS; iworld++) { + magnitude = dircos[ivolume][iworld]; + if (magnitude < 0.0) magnitude = -magnitude; + if (magnitude > largest) { + largest = magnitude; + volume_to_world[ivolume] = iworld; + } + } + } + + // Get orientation (depends on primary direction of slice normal) + *orientation = orientation_list[volume_to_world[VSLICE]]; + + // Get step information + for (ivolume=0; ivolume < sizeof(darray)/sizeof(darray[0]); ivolume++) + darray[ivolume] = -DBL_MAX; + // note ACR_Pixel_size should now be called Pixel_spacing + element = acr_find_group_element(group_list, ACR_Pixel_size); + if (element != NULL) + (void) acr_get_element_numeric_array(element, + sizeof(darray)/sizeof(darray[0]), darray); + if (darray[0] == -DBL_MAX) darray[0] = 1.0; + if (darray[1] == -DBL_MAX) darray[1] = darray[0]; + steps[VCOLUMN] = darray[0]; + steps[VROW] = darray[1]; // anisotropic resolution? + + // steps[VSLICE] = acr_find_double(group_list, ACR_Slice_thickness, 1.0); + steps[VSLICE] = acr_find_double(group_list,ACR_Spacing_between_slices,1.0); + + /* Make sure that direction cosines point the right way (dot product + of direction cosine and axis is positive) and that step has proper + sign */ + for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { + iworld = volume_to_world[ivolume]; + if (dircos[ivolume][iworld] < 0.0) { + steps[ivolume] *= -1.0; + for (iworld = 0; iworld < WORLD_NDIMS; iworld++) { + dircos[ivolume][iworld] *= -1.0; + } + } + } + + if (file_type == N3DCM || file_type == IMA) { + + // Find 3D coordinate of slice - SPI_Image_position gives + // the *centre* of the slice! + element = acr_find_group_element(group_list, SPI_Image_position); + if ((element == NULL) || + (acr_get_element_numeric_array(element, WORLD_NDIMS, coordinate) + != WORLD_NDIMS)) { + found_coordinate = FALSE; + for (iworld=0; iworld < WORLD_NDIMS; iworld++) + coordinate[iworld] = 0.0; + } else { + found_coordinate = TRUE; + } + + convert_numa3_coordinate(coordinate); + + // Work out start positions in volume coordinates + for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { + + if (found_coordinate && found_dircos[VSLICE] && found_dircos[VROW] && + found_dircos[VCOLUMN]) { + centre = + coordinate[XCOORD] * dircos[ivolume][XCOORD] + + coordinate[YCOORD] * dircos[ivolume][YCOORD] + + coordinate[ZCOORD] * dircos[ivolume][ZCOORD]; + } + else { + centre = 0.0; + } + starts[ivolume] = centre - + (steps[ivolume] * (sizes[ivolume] - 1.0) / 2.0); + } + + } else { // normal dicom + + // Find 3D coordinate of slice - ACR_Image_position_patient gives + // the *corner* of the slice! + element = acr_find_group_element(group_list, ACR_Image_position_patient); + if ((element == NULL) || + (acr_get_element_numeric_array(element, WORLD_NDIMS, coordinate) + != WORLD_NDIMS)) { + found_coordinate = FALSE; + for (iworld=0; iworld < WORLD_NDIMS; iworld++) + coordinate[iworld] = 0.0; + } else { + found_coordinate = TRUE; + } + + convert_dicom_coordinate(coordinate); + + // Work out start positions in volume coordinates + for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { + + if (found_coordinate && found_dircos[VSLICE] && found_dircos[VROW] && + found_dircos[VCOLUMN]) { + starts[ivolume] = + coordinate[XCOORD] * dircos[ivolume][XCOORD] + + coordinate[YCOORD] * dircos[ivolume][YCOORD] + + coordinate[ZCOORD] * dircos[ivolume][ZCOORD]; + } + else { + starts[ivolume] = 0.0; + } + } + + } + + // Find position along each dimension + file_info->coordinate[SLICE] = starts[VSLICE]; + file_info->coordinate[ECHO] = + acr_find_double(group_list, ACR_Echo_time, 0.0) / (double) MS_PER_SECOND; + + // time section (rhoge) + // now assume that time has been fixed when file was read + + start_time = acr_find_double(group_list,ACR_Series_time, 0.0); + frame_time = + acr_find_double(group_list, ACR_Acquisition_time, 0.0); + start_time = convert_time_to_seconds(start_time); + frame_time = convert_time_to_seconds(frame_time) - start_time; + + // check for case where scan starts right before midnight, + // but frame is after midnight + if (frame_time < 0.0) frame_time += (double) SECONDS_PER_DAY; + file_info->coordinate[TIME] = frame_time; + + /* end of time section */ + + file_info->coordinate[PHASE] = 0.0; + file_info->coordinate[CHEM_SHIFT] = 0.0; + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : convert_numa3_coordinate +@INPUT : coordinate +@OUTPUT : coordinate +@RETURNS : (nothing) +@DESCRIPTION: Routine to convert a coordinate to the correct orientation +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 28, 1997 (Peter Neelin) +@MODIFIED : made version specific to Numaris 3 SPI data (rhoge) +---------------------------------------------------------------------------- */ +public void convert_numa3_coordinate(double coordinate[WORLD_NDIMS]) +{ + coordinate[XCOORD] = -coordinate[XCOORD]; + coordinate[ZCOORD] = -coordinate[ZCOORD]; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : convert_dicom_coordinate +@INPUT : coordinate +@OUTPUT : coordinate +@RETURNS : (nothing) +@DESCRIPTION: Routine to convert a coordinate to the correct orientation +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 28, 1997 (Peter Neelin) +@MODIFIED : made new dicom version (rhoge) +---------------------------------------------------------------------------- */ +public void convert_dicom_coordinate(double coordinate[WORLD_NDIMS]) +{ + coordinate[XCOORD] = -coordinate[XCOORD]; + coordinate[YCOORD] = -coordinate[YCOORD]; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : get_general_header_info +@INPUT : group_list - input data +@OUTPUT : general_info - general information about files +@RETURNS : (nothing) +@DESCRIPTION: Routine to extract general header information from a group list +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 28, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void get_general_header_info(Acr_Group group_list, + General_Info *general_info) +{ + int maxlen, length; + char *string, *ptr; + + /* Maximum length for strings */ + maxlen = sizeof(Cstring) - 1; + + /* Get intensity units */ + (void) strncpy(general_info->units, "", maxlen); + + /* Get patient info */ + (void) strncpy(general_info->patient.name, + acr_find_string(group_list, ACR_Patient_name, ""), maxlen); + (void) strncpy(general_info->patient.identification, + acr_find_string(group_list, ACR_Patient_identification, ""), maxlen); + (void) strncpy(general_info->patient.birth_date, + acr_find_string(group_list, ACR_Patient_birth_date, ""), maxlen); + (void) strncpy(general_info->patient.age, + acr_find_string(group_list, ACR_Patient_age, ""), maxlen); + string = acr_find_string(group_list, ACR_Patient_sex, ""); + if (*string == 'M') + (void) strncpy(general_info->patient.sex, MI_MALE, maxlen); + else if (*string == 'F') + (void) strncpy(general_info->patient.sex, MI_FEMALE, maxlen); + else if (*string == 'O') + (void) strncpy(general_info->patient.sex, MI_OTHER, maxlen); + else + (void) strncpy(general_info->patient.sex, "", maxlen); + general_info->patient.weight = + acr_find_double(group_list, ACR_Patient_weight, -DBL_MAX); + /* added by rhoge - registration timing info */ + (void) strncpy(general_info->patient.reg_date, + acr_find_string(group_list, ACR_Study_date, ""), maxlen); + (void) strncpy(general_info->patient.reg_time, + acr_find_string(group_list, ACR_Study_time, ""), maxlen); + + /* Get study info */ + (void) strncpy(general_info->study.start_time, + acr_find_string(group_list, ACR_Study_date, ""), maxlen - 1); + length = strlen(general_info->study.start_time); + general_info->study.start_time[length] = ' '; + length++; + (void) strncpy(&general_info->study.start_time[length], + acr_find_string(group_list, ACR_Study_time, ""), maxlen - length); + string = acr_find_string(group_list, ACR_Modality, ""); + if (strcmp(string, ACR_MODALITY_MR) == 0) + (void) strncpy(general_info->study.modality, MI_MRI, maxlen); + (void) strncpy(general_info->study.manufacturer, + acr_find_string(group_list, ACR_Manufacturer, ""), maxlen); + (void) strncpy(general_info->study.model, + acr_find_string(group_list, ACR_Manufacturer_model, ""), maxlen); + general_info->study.field_value = + acr_find_double(group_list, ACR_Magnetic_field_strength, -DBL_MAX); + (void) strncpy(general_info->study.software_version, + acr_find_string(group_list, ACR_Software_versions, ""), maxlen); + (void) strncpy(general_info->study.serial_no, + acr_find_string(group_list, ACR_Device_serial_number, ""), maxlen); + (void) strncpy(general_info->study.calibration_date, + acr_find_string(group_list, SPI_Calibration_date, ""), maxlen); + (void) strncpy(general_info->study.institution, + acr_find_string(group_list, ACR_Institution_id, ""), maxlen); + (void) strncpy(general_info->study.station_id, + acr_find_string(group_list, ACR_Station_id, ""), maxlen); + (void) strncpy(general_info->study.referring_physician, + acr_find_string(group_list, ACR_Referring_physician, ""), maxlen); + (void) strncpy(general_info->study.performing_physician, + acr_find_string(group_list, ACR_Performing_physician, ""), maxlen); + (void) strncpy(general_info->study.operator, + acr_find_string(group_list, ACR_Operators_name, ""), maxlen); + (void) strncpy(general_info->study.procedure, + acr_find_string(group_list, ACR_Procedure_description, ""), maxlen); + (void) sprintf(general_info->study.study_id, "%.6f",general_info->study_id); + /* acquisition id modified by rhoge to get rid of first digit that + is not required for identification of run */ + /* (void) sprintf(general_info->study.acquisition_id, "%d_%d", + acr_find_int(group_list, ACR_Series, 0), general_info->acq_id); */ + (void) sprintf(general_info->study.acquisition_id, "%d", + general_info->acq_id); + + /* Get acquisition information */ + /* string = acr_find_string(group_list, SPI_Sequence_file_name, ""); + ptr = string + strlen(string) - 1; + while ((ptr > string) && (*ptr != '/')) {ptr--;} + if ((ptr >= string) && (*ptr == '/')) string = ptr + 1; + (void) strncpy(general_info->acq.scan_seq, string, maxlen); + ptr = general_info->acq.scan_seq; + while (*ptr != '\0') { + if (*ptr == '.') *ptr = '\0'; + ptr++; + } */ + + (void) strncpy(general_info->acq.scan_seq, + acr_find_string(group_list, ACR_Sequence_name, ""), maxlen); + (void) strncpy(general_info->acq.seq_owner, + acr_find_string(group_list, SPI_Sequence_file_owner, ""), maxlen); + (void) strncpy(general_info->acq.seq_descr, + acr_find_string(group_list, SPI_Sequence_description, ""), maxlen); + (void) strncpy(general_info->acq.protocol_name, + acr_find_string(group_list, ACR_Protocol_name, ""), maxlen); + (void) strncpy(general_info->acq.receive_coil, + acr_find_string(group_list, ACR_Receiving_coil, ""), maxlen); + (void) strncpy(general_info->acq.transmit_coil, + acr_find_string(group_list, ACR_Transmitting_coil, ""), maxlen); + general_info->acq.rep_time = + acr_find_double(group_list, ACR_Repetition_time, -DBL_MAX); + if (general_info->acq.rep_time != -DBL_MAX) + general_info->acq.rep_time /= 1000.0; + general_info->acq.echo_time = + acr_find_double(group_list, ACR_Echo_time, -DBL_MAX); + if (general_info->acq.echo_time != -DBL_MAX) + general_info->acq.echo_time /= 1000.0; + general_info->acq.echo_number = + acr_find_double(group_list, ACR_Echo_number, -DBL_MAX); + general_info->acq.inv_time = + acr_find_double(group_list, ACR_Inversion_time, -DBL_MAX); + if (general_info->acq.inv_time != -DBL_MAX) + general_info->acq.inv_time /= 1000.0; + general_info->acq.b_value = + acr_find_double(group_list, EXT_Diffusion_b_value, -DBL_MAX); + general_info->acq.flip_angle = + acr_find_double(group_list, ACR_Flip_angle, -DBL_MAX); + general_info->acq.slice_thickness = + acr_find_double(group_list, ACR_Slice_thickness, -DBL_MAX); + general_info->acq.num_slices = + acr_find_double(group_list, SPI_Number_of_slices_nominal, -DBL_MAX); + general_info->acq.num_dyn_scans = + acr_find_double(group_list, ACR_Acquisitions_in_series, -DBL_MAX); + general_info->acq.num_avg = + acr_find_double(group_list, ACR_Nr_of_averages, -DBL_MAX); + general_info->acq.scan_dur = + acr_find_double(group_list, SPI_Total_measurement_time_cur, -DBL_MAX); + general_info->acq.ky_lines = + acr_find_double(group_list, SPI_Number_of_fourier_lines_current, + -DBL_MAX); + general_info->acq.kymax_ix = + acr_find_double(group_list, SPI_Number_of_fourier_lines_after_zero, + -DBL_MAX); + general_info->acq.kymin_ix = + acr_find_double(group_list, SPI_First_measured_fourier_line, + -DBL_MAX); + general_info->acq.kz_lines = + acr_find_double(group_list, SPI_Number_of_3d_raw_part_cur, + -DBL_MAX); + general_info->acq.dummy_scans = + acr_find_double(group_list, SPI_Number_of_prescans, + -DBL_MAX); + general_info->acq.imaging_freq = + acr_find_double(group_list, ACR_Imaging_frequency, -DBL_MAX); + if (general_info->acq.imaging_freq != -DBL_MAX) + general_info->acq.imaging_freq *= 1e6; + (void) strncpy(general_info->acq.imaged_nucl, + acr_find_string(group_list, ACR_Imaged_nucleus, ""), maxlen); + general_info->acq.adc_voltage = + acr_find_double(group_list, SPI_ADC_voltage, -DBL_MAX); + general_info->acq.adc_offset = + acr_find_double(group_list, SPI_ADC_offset, -DBL_MAX); + general_info->acq.transmit_ampl = + acr_find_double(group_list, SPI_Transmitter_amplitude, -DBL_MAX); + general_info->acq.rec_amp_gain = + acr_find_double(group_list, SPI_Receiver_amplifier_gain, -DBL_MAX); + general_info->acq.rec_preamp_gain = + acr_find_double(group_list, SPI_Receiver_preamplifier_gain, -DBL_MAX); + general_info->acq.win_center = + acr_find_double(group_list, SPI_Window_center, -DBL_MAX); + general_info->acq.win_width = + acr_find_double(group_list, SPI_Window_width, -DBL_MAX); + general_info->acq.gy_ampl = + acr_find_double(group_list, SPI_Phase_gradient_amplitude, -DBL_MAX); + general_info->acq.gx_ampl = + acr_find_double(group_list, SPI_Readout_gradient_amplitude, -DBL_MAX); + general_info->acq.gz_ampl = + acr_find_double(group_list, SPI_Selection_gradient_amplitude, -DBL_MAX); + + general_info->acq.num_phase_enc_steps = + acr_find_double(group_list, ACR_Number_of_phase_encoding_steps, -DBL_MAX); + general_info->acq.percent_sampling = 100 * + acr_find_double(group_list, ACR_Percent_sampling, -DBL_MAX); + general_info->acq.percent_phase_fov = 100 * + acr_find_double(group_list, ACR_Percent_phase_field_of_view, -DBL_MAX); + general_info->acq.pixel_bandwidth = + acr_find_double(group_list, ACR_Pixel_bandwidth, -DBL_MAX); + general_info->acq.sar = + acr_find_double(group_list, ACR_SAR, -DBL_MAX); + strncpy(general_info->acq.mr_acq_type, + acr_find_string(group_list,ACR_MR_acquisition_type,""),4); + strncpy(general_info->acq.image_type, + acr_find_string(group_list,ACR_Image_type,""),128); + + strncpy(general_info->acq.phase_enc_dir, + acr_find_string(group_list,ACR_Phase_encoding_direction,""),3); + (void) strncpy(general_info->acq.comments, "", maxlen); + // Siemens Numaris 4 specific! + + if (0) { // this causes problems with large MrProt fields + // printf("allocating %d bytes for MrProt...\n", + // strlen(acr_find_string(group_list, EXT_MrProt_dump, ""))); + general_info->acq.MrProt = + MALLOC(strlen(acr_find_string(group_list, EXT_MrProt_dump, "")) * + sizeof(char)); + (void) strcpy(general_info->acq.MrProt, + acr_find_string(group_list, EXT_MrProt_dump, "")); + } else { + general_info->acq.MrProt = + MALLOC(strlen("disabled")*sizeof(char)); + (void) strcpy(general_info->acq.MrProt, "disabled"); + } + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : convert_time_to_seconds +@INPUT : dicom_time +@OUTPUT : (none) +@RETURNS : real time in seconds from beginning of day +@DESCRIPTION: Routine to convert dicom seconds (decimal hhmmss.xxxxx) to + real seconds since the start of day. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 28, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public double convert_time_to_seconds(double dicom_time) +{ + /* Constants */ +#define DICOM_SECONDS_PER_HOUR 10000.0 +#define DICOM_SECONDS_PER_MINUTE 100.0 + + /* Variables */ + double real_time, Hours, Minutes, Seconds; + + /* Get the components of the time */ + + Hours = (int) (dicom_time / (double) DICOM_SECONDS_PER_HOUR); + dicom_time -= Hours * DICOM_SECONDS_PER_HOUR; + Minutes = (int) (dicom_time / (double) DICOM_SECONDS_PER_MINUTE); + dicom_time -= Minutes * (double) DICOM_SECONDS_PER_MINUTE; + Seconds = dicom_time; + + /* Work out the number of seconds */ + + real_time = (Hours * 3600.0) + (Minutes * 60.0) + Seconds; + + return real_time; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : get_siemens_dicom_image +@INPUT : group_list - input data +@OUTPUT : image - image data structure (user must free data) +@RETURNS : (nothing) +@DESCRIPTION: Routine to get an image from a group list +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 25, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void get_siemens_dicom_image(Acr_Group group_list, Image_Data *image) +{ + + /* Variables */ + Acr_Element element; + int nrows, ncolumns; + int bits_alloc; + int bits_stored; + int image_group; + void *data = NULL; + long imagepix, ipix; + struct Acr_Element_Id elid; + nc_type datatype; + + /* Get the image information */ + bits_alloc = acr_find_short(group_list, ACR_Bits_allocated, 0); + bits_stored = acr_find_short(group_list, ACR_Bits_stored, bits_alloc); + nrows = acr_find_short(group_list, ACR_Rows, 0); + ncolumns = acr_find_short(group_list, ACR_Columns, 0); + image_group = acr_find_short(group_list, ACR_Image_location, + ACR_ACTUAL_IMAGE_GID); + + /* Figure out type */ + if (bits_alloc > CHAR_BIT) + datatype = NC_SHORT; + else + datatype = NC_BYTE; + + /* Set image info */ + image->nrows = nrows; + image->ncolumns = ncolumns; + imagepix = nrows * ncolumns; + image->data = (unsigned short *) MALLOC(imagepix * sizeof(short)); + image->free = TRUE; + + /* Get image pointer */ + elid.group_id = image_group; + elid.element_id = SPI_IMAGE_ELEMENT; + element = acr_find_group_element(group_list, &elid); + if (element == NULL) { + (void) memset(image->data, 0, imagepix * sizeof(short)); + return; + } + data = acr_get_element_data(element); + + /* Convert the data according to type */ + + /* Look for byte data */ + if (datatype == NC_BYTE) { + for (ipix=0; ipix < imagepix; ipix++) { + image->data[ipix] = *((unsigned char *) data + ipix); + } + } + else { + + /* Look for unpacked short data */ + if (bits_alloc == nctypelen(datatype) * CHAR_BIT) { + acr_get_short(acr_get_element_byte_order(element), + nrows*ncolumns, data, image->data); + } + + /* Fill with zeros in any other case */ + else { + (void) memset(image->data, 0, imagepix * sizeof(short)); + } + } + + return; +} + +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_dicom_send.c @@ -0,0 +1,1404 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : siemens_dicom_send.c +@DESCRIPTION: Program to convert siemens vision internal format files to + dicom and send them to a remote server. +@METHOD : +@GLOBALS : +@CREATED : July 8, 1997 (Peter Neelin) re-write by Rick Hoge +@MODIFIED : $Log: siemens_dicom_send.c,v $ +@MODIFIED : Revision 1.1 2003-08-15 19:52:55 leili +@MODIFIED : Initial revision +@MODIFIED : +@MODIFIED : Revision 1.9 2001/10/19 13:08:39 rhoge +@MODIFIED : - fixed problem for single slice acq loop scans in siemens_dicom_send +@MODIFIED : - added diffusion pulse seq in ima2mnc, restored hard-coded path +@MODIFIED : +@MODIFIED : Revision 1.8 2001/04/19 19:33:10 rhoge +@MODIFIED : added multi-echo support, basic testing. Changed TEST to MYDEBUG +@MODIFIED : +@MODIFIED : Revision 1.7 2000/12/17 01:04:40 rhoge +@MODIFIED : clean up log format +@MODIFIED : +@MODIFIED : Revision 1.6 2000/12/17 01:02:58 rhoge +@MODIFIED : Made some debugging statements conditional on TEST macro, +@MODIFIED : in course of changes to restore Sun function after +@MODIFIED : byte-aligmnent fixes (padding) for Linux port +@MODIFIED : +@MODIFIED : Revision 1.5 2000/12/14 22:56:40 rhoge +@MODIFIED : removed DBL_MAX - not defined on sun (replaced with 1.0 in +@MODIFIED : acr_find_double, line 502) +@MODIFIED : +@MODIFIED : Revision 1.4 2000/12/11 20:04:35 rhoge +@MODIFIED : got rid of printf statements for time correction +@MODIFIED : +@MODIFIED : Revision 1.3 2000/12/11 19:27:25 rhoge +@MODIFIED : fix for leading zeros if hms less than two digits in time +@MODIFIED : +@MODIFIED : Revision 1.2 2000/12/11 17:43:01 rhoge +@MODIFIED : added frame time correction - code compiles and runs, but order +@MODIFIED : problem may not yet be fixed +@MODIFIED : +@MODIFIED : Revision 1.1.1.1 2000/11/30 02:05:54 rhoge +@MODIFIED : imported sources to CVS repository on amoeba +@MODIFIED : + * + * Revision 1.9 1999/08/03 17:42:55 neelin + * Added ability to handle multiple input directories. + * + * Revision 1.8 1998/11/17 16:13:29 neelin + * Changes to log messages to ensure that 100 percent is only displayed + * when transfer is complete. + * + * Revision 1.7 1998/11/17 14:49:22 neelin + * Modified help comment. + * + * Revision 1.6 1998/11/17 14:00:12 neelin + * Changed logging to print percentage with overstrike for terminals. + * + * Revision 1.5 1998/11/16 19:48:43 neelin + * Added support for breaking up mosaic images and sending the pieces one + * at a time. + * + * Revision 1.4 1998/11/13 16:02:09 neelin + * Modifications to support packed images and asynchronous transfer. + * + * Revision 1.3 1998/02/20 17:37:55 neelin + * Removed debugging statements. + * + * Revision 1.2 1997/11/04 14:31:30 neelin + * *** empty log message *** + * + * Revision 1.1 1997/08/11 12:50:53 neelin + * Initial revision + * +---------------------------------------------------------------------------- */ + +#ifndef lint +static char rcsid[]="$Header: /private-cvsroot/minc/conversion/dicomserver_sonata/siemens_dicom_send.c,v 1.1 2003-08-15 19:52:55 leili Exp $"; +#endif + +#include <stdio.h> +#include <math.h> /* for slice position hacks */ +#include <stdlib.h> +#include <limits.h> +#include <dirent.h> +#include <string.h> +#include <malloc.h> +#include <ParseArgv.h> + +#include <acr_nema.h> +#include <dicom_client_routines.h> + +/* Constants */ +#ifndef public +# define public +#endif +#ifndef private +# define private static +#endif +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +#endif +#ifndef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif +#define ALLOC_INCREMENT 100 +#define LOG_PERCENT (2.0) + +/* Dicom definitions */ +#define ACR_MR_IMAGE_STORAGE_UID "1.2.840.10008.5.1.4.1.1.4" +#define ACR_IMPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2" +#define ACR_EXPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2.1" +#define ACR_EXPLICIT_VR_BIG_END_UID "1.2.840.10008.1.2.2" +#define MY_AE_TITLE "MGH_CLIENT" + +/* rhoge: debugging definitions */ +/* #define MYDEBUG */ + +/* Define element id's */ + +DEFINE_ELEMENT(static, ACR_Slice_thickness , 0x0018, 0x0050, DS); +DEFINE_ELEMENT(static, ACR_Rows , 0x0028, 0x0010, US); +DEFINE_ELEMENT(static, ACR_Columns , 0x0028, 0x0011, US); +DEFINE_ELEMENT(static, ACR_Pixel_size , 0x0028, 0x0030, DS); +DEFINE_ELEMENT(static, ACR_Bits_allocated , 0x0028, 0x0100, US); +DEFINE_ELEMENT(static, ACR_Image , 0x7fe0, 0x0010, OW); + +DEFINE_ELEMENT(static, SPI_Sequence_File_Name , 0x0019, 0x1511, LT); +DEFINE_ELEMENT(static, SPI_Image_position , 0x0021, 0x1160, DS); +DEFINE_ELEMENT(static, SPI_Image_normal , 0x0021, 0x1161, DS); +DEFINE_ELEMENT(static, SPI_Image_distance , 0x0021, 0x1163, DS); +DEFINE_ELEMENT(static, SPI_Current_slice_number , 0x0021, 0x1342, IS); +DEFINE_ELEMENT(static, SPI_Slice_distance_factor , 0x0021, 0x1344, DS); + +/* added by rhoge for acquisition loop with mosaic overflow + * note: on the Sonata, {acquisition,run,series,scan} is called + * Study in the header! beware of confusion... */ + +DEFINE_ELEMENT(static, ACR_Study , 0x0020, 0x0010, IS); +DEFINE_ELEMENT(static, ACR_Series , 0x0020, 0x0011, IS); +DEFINE_ELEMENT(static, ACR_Acquisitions_in_series , 0x0020, 0x1001, IS); +DEFINE_ELEMENT(static, ACR_Echo_number , 0x0018, 0x0086, IS); + +DEFINE_ELEMENT(static, SPI_Number_of_averages , 0x0018, 0x0083, DS); +DEFINE_ELEMENT(static, SPI_Number_of_slices_nom , 0x0021, 0x1340, IS); +DEFINE_ELEMENT(static, SPI_Number_of_slices_cur , 0x0021, 0x1341, IS); +DEFINE_ELEMENT(static, SPI_Number_of_fourier_lines_nominal,0x0019, 0x1220, IS); +DEFINE_ELEMENT(static, SPI_Number_of_echoes , 0x0021, 0x1370, IS); + +DEFINE_ELEMENT(static, ACR_Study_time , 0x0008, 0x0030, TM); +DEFINE_ELEMENT(static, ACR_Repetition_time , 0x0018, 0x0080, DS); +DEFINE_ELEMENT(static, ACR_Acquisition_time , 0x0008, 0x0032, TM); + +/* Typedefs */ +typedef struct { + int key; + char *name; + int dirindex; +} Sort_entry; + +typedef struct { + int packed; + int mosaic_seq; + int size[2]; + int big[2]; + int grid[2]; + int pixel_size; + Acr_Element big_image; + Acr_Element small_image; + int sub_images; + int first_image; + double normal[3]; + double step[3]; + double position[3]; +} Multi_Image; + +typedef struct Mosaic_Info { + char *match_string; + int size[2]; + struct Mosaic_Info *next; +} *Mosaic_Info; + +/* Function prototypes */ +public Acr_Group siemens_to_dicom(char *filename, int read_image); +public void update_coordinate_info(Acr_Group group_list); +public int process_mosaic_args(char *dst, char *key, int argc, char **argv); +public Mosaic_Info get_mosaic_info(Mosaic_Info mosaic_info_list, + char *protocol); +public int get_directory(char *dst, char *key, char *nextarg); +public void get_file_names(int num_dirs, char **directory_list, + int patient_number, + int first_image, int last_image, + int *num_files, char ***file_list, + int **file_dirindex_list); +private int sort_function(const void *entry1, const void *entry2); +public void free_file_names(char *fullpath, char *file_list[], int num_files); +public int multi_image_init(Acr_Group group_list, + Multi_Image *multi_image); +public void multi_image_modify_group_list(Acr_Group group_list, + Multi_Image *multi_image, + int iimage); +public void multi_image_cleanup(Acr_Group group_list, + Multi_Image *multi_image); + +/* Variables for argument parsing */ +Mosaic_Info mosaic_info_list = NULL; +int max_outstanding = 10; +char **directory_list = NULL; +int num_dirs = 0; +int num_dirs_alloc = 0; + +/* Argument table */ +ArgvInfo argTable[] = { + {"-mosaic", ARGV_GENFUNC, (char *) process_mosaic_args, + (char *) &mosaic_info_list, + "Add a mosaic protocol and subimage size to the list.\n" +"\tUsage: -mosaic <protocol string> <m> <n>\n" +"\tIf the protocol name begins with a /, then an exact match is done.\n" +"\tIf it contains an extension, then a match on filename alone is done.\n" +"\tOtherwise, a prefix match is done.\n" +"\tParent directories can be specified for a prefix match.\n" +"\tExamples:\n" +"\t\t-mosaic /home/user/protcols/mosaic/myproto64_version1.ekc 64 64\n" +"\t\t-mosaic myproto64 64 64\n" +"\t\t-mosaic myproto64_version1.ekc 64 64\n" +"\t\t-mosaic mosaic/myproto64_ 64 64\n" +"\t\t-mosaic mosaic/myproto64_version1.ekc 64 64\n" +"\t\t-mosaic mosaic/ 64 64\n" + }, + {"-directory", ARGV_FUNC, (char *) get_directory, (char *) NULL, + "Specify a directory to search (instead of positional option)."}, + {"-max_outstanding", ARGV_INT, (char *) 1, (char *) &max_outstanding, + "Specify max outstanding replies for asynchronous transfer."}, + {(char *) NULL, ARGV_END, (char *) NULL, (char *) NULL, + (char *) NULL} +}; + +/* Main program */ + +int main(int argc, char *argv[]) +{ + Acr_Group group_list; + char *pname; + char *host; + char *port; + char *AEtitle; + int iarg; + int patient_number; + int first_image; + int last_image; + char **file_list; + int *file_dirindex_list; + int ifile, num_files, idir; + Acr_File *afpin, *afpout; + char *fullpath; + int maxlength, maxdirlength, len; + int iimage; + Multi_Image multi_image; + char *log_separator; + int needed_args; + + /* stuff added by rhoge for acquisition loop with mosaic overflow */ + + int idim; + int imosaic; + int iecho; + int num_mosaics; + int acqframe; + int num_acqframes; + int num_echos; + int echo; + int num_slices; + int num_sub_images; + int nimages; + int num_special_files; + int num_mosaic_elements; + double top_slice_position[3]; + int current_series; + int current_slice_index; + double scan_start_time; + double start_time_sec; + double start_hours; + double start_minutes; + double start_seconds; + double frame_hours; + double frame_minutes; + double frame_seconds; + double frame_hours_oflow; + double frame_minutes_oflow; + double frame_seconds_oflow; + double rel_frame_time_sec; + double abs_frame_time_sec; + char abs_frame_time_hms[100]; + + + /* Check arguments. */ + /* Ugly hack here to keep backwards compatibility, but also allow -directory + specification. needed_args stores the minimum number of args needed, + depending on whether the -directory option was specified. */ + pname = argv[0]; + if (ParseArgv(&argc, argv, argTable, 0) || + (argc < (needed_args = (directory_list == NULL ? 6:5))) || + (argc > needed_args+2)) { + (void) fprintf(stderr, + "Usage: %s host port AEtitle [directory] patient_num [first [last]]\n", + pname); + return EXIT_FAILURE; + } + iarg=1; + host = argv[iarg++]; + port = argv[iarg++]; + AEtitle = argv[iarg++]; + if (directory_list == NULL) { + num_dirs_alloc = 1; + directory_list = malloc(sizeof(*directory_list) * num_dirs_alloc); + directory_list[num_dirs] = argv[iarg++]; + num_dirs++; + } + patient_number = atoi(argv[iarg++]); + first_image = ((argc < needed_args+1) ? 0 : atoi(argv[iarg++])); + last_image = ((argc < needed_args+2) ? INT_MAX : atoi(argv[iarg++])); + + /* Check range */ + if ((last_image < first_image) || (first_image < 0)) { + (void) fprintf(stderr, + "Bad range of images: use +ve values with last >= first\n"); + return EXIT_FAILURE; + } + + /* Get file names */ + get_file_names(num_dirs, directory_list, + patient_number, first_image, last_image, + &num_files, &file_list, &file_dirindex_list); + if (num_files <= 0) { + (void) fprintf(stderr, "No files to send.\n"); + return EXIT_FAILURE; + } + + + /* Get space for file names */ + maxlength = 0; + for (ifile = 0; ifile < num_files; ifile++) { + len = strlen(file_list[ifile]); + if (len > maxlength) maxlength = len; + } + maxdirlength = 0; + for (idir = 0; idir < num_dirs; idir++) { + len = strlen(directory_list[idir]); + if (len > maxdirlength) maxdirlength = len; + } + fullpath = malloc(maxdirlength + maxlength + 2); + + /* Make dicom connection */ + if (!acr_open_dicom_connection(host, port, AEtitle, MY_AE_TITLE, + ACR_MR_IMAGE_STORAGE_UID, + ACR_IMPLICIT_VR_LITTLE_END_UID, + &afpin, &afpout)) { + free_file_names(fullpath, file_list, num_files); + return EXIT_FAILURE; + } + + /* Set maximum number of outstanding responses */ + acr_set_client_max_outstanding(afpin, max_outstanding); + + /* Loop over the input files, sending them one at a time */ + + ifile = 0; + + while (ifile < num_files) { + + /* get next dicom group */ + + /************** START of group read unit - function? ********/ + + /* Get file name */ + idir = file_dirindex_list[ifile]; + (void) sprintf(fullpath, "%s/%s", + directory_list[idir], file_list[ifile]); + + printf("processing file %s (%d of %d)\n", + file_list[ifile], + ifile+1,num_files); + (void) fflush(stdout); + + /* Read data */ + group_list = siemens_to_dicom(fullpath, TRUE); + if (group_list == NULL) continue; + + /* Look for multi-image files (many images in one) */ + num_mosaic_elements = multi_image_init(group_list, &multi_image); + +#ifdef MYDEBUG + printf("num_mosaic_elements = %d\n",num_mosaic_elements); +#endif + + /************** END of group read unit - function? ********/ + + /* determine number of `acqframes' */ + + if (multi_image.mosaic_seq) { + + num_acqframes = acr_find_int(group_list, + SPI_Number_of_averages, 1); + } else { + num_acqframes = 1; + } + +#ifdef MYDEBUG + printf("num_acqframes = %d\n",num_acqframes); +#endif + + /* determine real number of slices */ + + num_slices = acr_find_int(group_list, SPI_Number_of_slices_cur, 1); + + num_echos = acr_find_int(group_list, SPI_Number_of_echoes, 1); + +#ifdef MYDEBUG + printf("num_echos = %d\n",num_echos); +#endif + + /* determine number of mosaics per volume */ + + num_mosaics = (int)ceil((float)num_slices/(float)num_mosaic_elements); + + /* determine total number of `special' files expected for this + series */ + + num_special_files = num_acqframes * num_mosaics * num_echos; + +#ifdef MYDEBUG + printf("num_mosaics = %d\n",num_mosaics); + printf("num_special_files = %d\n",num_special_files); +#endif + + current_series = + acr_find_int(group_list, ACR_Study, 0); + +#ifdef MYDEBUG + printf("current_series = %d\n",current_series); +#endif + + /* add loop for echos, since diffusion scans may use echo loop + for multiple encodings. Loop order will usually be + acq, mosaic/slice, echo */ + + for (iecho = 0; iecho < num_echos; iecho++) { + + for (imosaic = 0; imosaic < num_mosaics; imosaic++) { + + current_slice_index = + acr_find_int(group_list, SPI_Current_slice_number, 1); + +#ifdef MYDEBUG + printf("current_slice_index = %d\n",current_slice_index); +#endif + + for (acqframe = 0; acqframe < num_acqframes; acqframe++) { + + /* check if we hit a new series or slice unexpectedly early - + this indicates an aborted run */ + + if ( acr_find_int(group_list,ACR_Study,0) != + current_series ) { + + /* if we hit a new SERIES too early, that's it. We break + out of all loops (acqframe and imosaic), clean up the + group, and start over again at the top of + while(ifile<num_files). Note that we *don't* decrement + ifile as the group will be re-read at the top of the + while loop with the correct file index as-is. */ + + printf("WARNING: new series too early; assuming aborted run for series %d.\n",current_series); + + acqframe = num_acqframes; + imosaic = num_mosaics; + multi_image_cleanup(group_list, &multi_image); + acr_delete_group_list(group_list); + break; + + } else if ( acr_find_int(group_list, SPI_Current_slice_number, 1) != + current_slice_index ) { + + /* if we hit a new SLICE too early, we've passed the last + frame in an overflow situation. We then bump up the + mosaic index, reset the frame counter (we're now at the + first frame of the next overflow mosaic), and update the + current slice index */ + + printf("WARNING: new mosaic too early; assuming aborted run for series %d.\n",current_series); + + imosaic++; + acqframe = 0; + current_slice_index = + acr_find_int(group_list, SPI_Current_slice_number, 1); + + } + + /* debug statements for diffusion/echo loop scans? */ +#ifdef MYDEBUG + printf("slice = %d\n", + acr_find_int(group_list, SPI_Current_slice_number, 1)); + + printf("echo = %d\n", + acr_find_int(group_list, ACR_Echo_number, 1)); +#endif + + /******* process .ima file ********/ + + /******** START of acquisition loop correction section *******/ + + /* note: we _could_ correct the number of signal averages, + but this may be useful on the receive side so we'll leave + it equal to the number of dynamic scans */ + + /* also, we only want to do this if looks like there really + are multiple acqframes (the acquisition loop has been used + for dynamic scanning). Otherwise, we could clobber valid + dynamic scan info for a measurement loop scan */ + + if (num_acqframes>1) { + + /* Number of dynamic scans */ + acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, + num_acqframes); + + /* now we have to fix the frame times, since these may be used + in sorting the frames at the receive end */ + + /* start of scanning run */ + scan_start_time = + atof(acr_find_string(group_list, ACR_Study_time, "")); + + /* extract hours, minutes, seconds */ + start_hours = (int) (scan_start_time/(double)10000); + scan_start_time -= start_hours * (10000.0); + start_minutes = (int) (scan_start_time/(double)100); + scan_start_time -= start_minutes * (100.0); + start_seconds = scan_start_time; + + /* convert start time to seconds from start of day */ + start_time_sec = + start_hours*3600.0 + + start_minutes*60.0 + + start_seconds; + + /* compute frame time (from start) based on TR */ + rel_frame_time_sec = acqframe * + (acr_find_double(group_list, ACR_Repetition_time,1.0)/1000); + + /* now add times in seconds to get absolute frame time */ + abs_frame_time_sec = rel_frame_time_sec + start_time_sec; + + /* convert absolute frame time in sec back to hms */ + frame_hours = floor(abs_frame_time_sec/3600.0); + abs_frame_time_sec -= frame_hours * 3600.0; + frame_minutes = floor(abs_frame_time_sec/60.0); + abs_frame_time_sec -= frame_minutes * 60.0; + frame_seconds = abs_frame_time_sec; + + /* check for day overflow (hours>23)*/ + frame_hours = ((int)frame_hours)%24; + + /* write frame time to string */ + sprintf(abs_frame_time_hms,"%02.0f%02.0f%06.3f", + frame_hours, + frame_minutes, + frame_seconds); + + /* now write the proper frame time into the group */ + + acr_insert_string(&group_list, ACR_Acquisition_time, + abs_frame_time_hms); + + /* current frame index */ + acr_insert_numeric(&group_list, ACR_Series,acqframe); + + } + + /******** END of acquisition loop correction section *******/ + + /* determine number of sub-images in mosaic */ + + if (multi_image.mosaic_seq) { + + if (imosaic == num_mosaics-1) { + + num_sub_images = num_slices-(num_mosaics-1)*num_mosaic_elements; + + } else { + + num_sub_images = num_mosaic_elements; + + } + +#ifdef MYDEBUG + printf("num_sub_images = %d\n",num_sub_images); +#endif + + } else { /* non-mosaic */ + + num_sub_images = 1; + + } + + /********* Start of slice position correction Section ********/ + + if (multi_image.mosaic_seq && num_sub_images > 1) { + + /* before 7:00pm Tuesday, March 28 2000: + + position of first mosaic was ok, but overflow was either + off by number of overflow slices (for interleaved + excitation order) or correct (for ascending) */ + + /* after 7:00pm Tuesday, March 28 2000: + + position of .ima file is that of LAST slice in mosaic + this must be corrected to give FIRST slice. + + This was originally done so that SPI_Current_slice_number + would give the number of slices in the mosaic. + Unfortunately, this only works for ascending order on the + new system. The SPL could therefore be modified to just + assign the position of the first sub-mosaic to the .ima + file. This would probably be more consistent with other + sites */ + + /* note that the code below depends *only* on the slice + position of the first mosaic being equal to that of the + last slice, so it should work for either case (it is + the slice position of subsequent `overflow' mosaics that + has been variable) */ + + if (imosaic==0) { + + for (idim=0; idim < 3; idim++) { + multi_image.position[idim] -= + (double) (num_sub_images-1) * multi_image.step[idim]; + + top_slice_position[idim]=multi_image.position[idim]; + } + + } else { + + for (idim=0; idim < 3; idim++) { + multi_image.position[idim] = + top_slice_position[idim] + + (double)imosaic*(double)num_mosaic_elements* + multi_image.step[idim]; + } + } + } /* end of if (multi_image.mosaic_seq) */ + + /********* End of slice position correction Section ********/ + + /* Now send all the sub-images */ + for (iimage=0; iimage < num_sub_images; iimage++) { + + + /* Modify the group list for this image */ + multi_image_modify_group_list(group_list, &multi_image, iimage); + + /* correct slice index if mosaic */ + if (multi_image.mosaic_seq) { + + acr_insert_numeric(&group_list, SPI_Current_slice_number, + imosaic*num_mosaic_elements+iimage+1); + + } + + /* Send the image */ + if (!acr_send_group_list(afpin, afpout, group_list, + ACR_MR_IMAGE_STORAGE_UID)) { + multi_image_cleanup(group_list, &multi_image); + acr_delete_group_list(group_list); + acr_close_dicom_connection(afpin, afpout); + free_file_names(fullpath, file_list, num_files); + return EXIT_FAILURE; + } + + } + + /* Clean up */ + + multi_image_cleanup(group_list, &multi_image); + + acr_delete_group_list(group_list); + + ifile++; + + if (ifile == num_files) { + + /* we've hit the last file - let's get out of here! (this + might happen if the last run in the file list is + aborted) */ + + acqframe = num_acqframes; + imosaic = num_mosaics; + break; + + + } else if (num_special_files > 1) { + + /* get next dicom group */ + + /************** START of group read unit - function? ********/ + + /* Get file name */ + idir = file_dirindex_list[ifile]; + (void) sprintf(fullpath, "%s/%s", + directory_list[idir], file_list[ifile]); + + printf("processing file %s (%d of %d)\n", + file_list[ifile], + ifile+1,num_files); + (void) fflush(stdout); + + /* Read data */ + group_list = siemens_to_dicom(fullpath, TRUE); + if (group_list == NULL) continue; + + /* Look for multi-image files (many images in one) */ + num_mosaic_elements = multi_image_init(group_list, &multi_image); + + /************** END of group read unit - function? ********/ + + } /* end of if (num_special_files > 1) */ + + } /* end of loop over acqframes */ + + } /* end of loop over mosaics */ + + } /* end of loop over echos */ + + } /* end of loop over files */ + + + (void) printf("Done sending files.\n"); + (void) fflush(stdout); + + /* Release the association and free stuff*/ + acr_close_dicom_connection(afpin, afpout); + + free_file_names(fullpath, file_list, num_files); + + return EXIT_SUCCESS; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : process_mosaic_args +@INPUT : dst - arguments from ParseArgv + key + argc + argv +@OUTPUT : argv +@RETURNS : Number of arguments left +@DESCRIPTION: Routine to process the arguments for the -mosaic option. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 13, 1998 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public int process_mosaic_args(char *dst, char *key, int argc, char **argv) +{ + Mosaic_Info *mosaic_info_list, tail, new; + char *match_string; + char *ptr; + int size[2]; + int iarg; + + /* Get pointer to mosaic info list */ + mosaic_info_list = (Mosaic_Info *) dst; + + /* Check number of arguments */ + if (argc < 3) { + (void) fprintf(stderr, "\"%s\" option requires 3 arguments\n", key); + return -1; + } + + /* Get arguments */ + match_string = argv[0]; + if (match_string == NULL) { + (void) fprintf(stderr, "Null string passed to \"%s\"\n", key); + return -1; + } + size[0] = strtol(argv[1], &ptr, 0); + if ((ptr == argv[1]) || (size[0] < 1)) { + (void) fprintf(stderr, "\"%s\" takes a positive integer (%s)\n", + key, argv[1]); + return -1; + } + size[1] = strtol(argv[2], &ptr, 0); + if ((ptr == argv[2]) || (size[1] < 1)) { + (void) fprintf(stderr, "\"%s\" takes a positive integer (%s)\n", + key, argv[2]); + return -1; + } + + /* Update the argv list. We copy one extra element, to get the NULL */ + argc -= 3; + for (iarg = 0; iarg <= argc; iarg++) { + argv[iarg] = argv[iarg+3]; + } + + /* Create a new entry and save the values */ + new = (Mosaic_Info) malloc(sizeof(*new)); + new->match_string = match_string; + new->size[0] = size[0]; + new->size[1] = size[1]; + new->next = NULL; + + /* Add the entry to the list */ + if (*mosaic_info_list == NULL) { + *mosaic_info_list = new; + } + else { + tail = *mosaic_info_list; + while (tail->next != NULL) { + tail = tail->next; + } + tail->next = new; + } + + return argc; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : get_mosaic_info +@INPUT : mosaic_info_list - pointer to beginning of list + protocol - protocol name +@OUTPUT : (none) +@RETURNS : Pointer to matching mosaic info structure or NULL +@DESCRIPTION: Routine to find a mosaic info structure that matches the + given protocol name. If none is found, then NULL is returned. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 13, 1998 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public Mosaic_Info get_mosaic_info(Mosaic_Info mosaic_info_list, + char *protocol) +{ +#define DIRECTORY_SEP '/' +#define EXTENSION_SEP '.' + + Mosaic_Info info; + char *ptr; + int have_extension; + int num_dirs; + int nchars_to_match; + int protolen; + + /* Loop over list */ + for (info=mosaic_info_list; info != NULL; info = info->next) { + + /* Check for an exact match */ + if (info->match_string[0] == DIRECTORY_SEP) { + if (strcmp(protocol, info->match_string) == 0) { + break; + } + } + + /* Otherwise look for a match with part of the protocol string */ + else { + + /* Figure out how many directories are given in the match string + and whether there is an extension */ + have_extension = FALSE; + num_dirs = 0; + for (ptr = &info->match_string[strlen(info->match_string)]; + ptr != info->match_string; + ptr--) { + + /* Extension can only occur on filename part */ + if ((num_dirs == 0) && (*ptr == EXTENSION_SEP)) { + have_extension = TRUE; + } + + /* Count directory separators, but ignore repeats */ + if ((*ptr == DIRECTORY_SEP) && (*(ptr+1) != DIRECTORY_SEP)) { + num_dirs++; + } + } + + /* Figure out where to start match on protocol string by counting + directories */ + for (ptr = &protocol[strlen(protocol)-1]; ptr != protocol; ptr--) { + if ((*ptr == DIRECTORY_SEP) && (*(ptr+1) != DIRECTORY_SEP)) { + num_dirs--; + } + if (num_dirs < 0) break; + } + if (*ptr == DIRECTORY_SEP) ptr++; + + /* Match whole string or just prefix, depending on extension */ + nchars_to_match = strlen(info->match_string); + protolen = strlen(ptr); + if (have_extension && (protolen > nchars_to_match)) { + nchars_to_match = protolen; + } + + /* Do the match */ + if (strncmp(ptr, info->match_string, nchars_to_match) == 0) { + break; + } + + } /* If exact match, else */ + + } /* End of loop */ + + /* Return the match */ + return info; + +} + + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : get_directory +@INPUT : dst - client data passed by ParseArgv + key - matching key in argv + nextarg - argument following key in argv +@OUTPUT : (none) +@RETURNS : TRUE since nextarg is used. +@DESCRIPTION: Gets directory name from command line and adds it to the list. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : August 3, 1999 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public int get_directory(char *dst, char *key, char *nextarg) + /* ARGSUSED */ +{ + /* Check for a following argument */ + if (nextarg == NULL) { + (void) fprintf(stderr, + "\"%s\" option requires an additional argument\n", + key); + exit(EXIT_FAILURE); + } + + /* Check whether we need more space for the directory name */ + if (num_dirs >= num_dirs_alloc) { + num_dirs_alloc += 10; + if (directory_list == NULL) + directory_list = malloc(sizeof(*directory_list) * num_dirs_alloc); + else + directory_list = realloc(directory_list, + sizeof(*directory_list) * num_dirs_alloc); + } + + /* Add the new directory */ + directory_list[num_dirs] = nextarg; + num_dirs++; + + return TRUE; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : get_file_names +@INPUT : num_dirs - number of directories in list + directory_list - list of directories in which to look + patient_number - number found at beginning of file name + first_image - first number in range + last_image - last number in range +@OUTPUT : num_files - number of files found + file_list - list of files to be sentn +@RETURNS : (nothing) +@DESCRIPTION: Routine to find all files in directory and of the + form "<patient number>-<scan number>-<image number>.ima" and + with <image number> in the range given by first_image and + last_image. The files are sorted by image number. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : July 11, 1997 (Peter Neelin) +@MODIFIED : August 3, 1999 (P.N.) +---------------------------------------------------------------------------- */ +public void get_file_names(int num_dirs, char **directory_list, + int patient_number, + int first_image, int last_image, + int *num_files, char ***file_list, + int **file_dirindex_list) +{ + DIR *dir; + struct dirent *dirent; + char *filename; + int patid, acqid, imgid; + char ch; + Sort_entry *sorting_files; + int num_entries, num_alloc, ifile, idir; + + /* Set initial values */ + *num_files = 0; + *file_list = NULL; + *file_dirindex_list = NULL; + + /* Set up array for file names */ + num_entries = 0; + num_alloc = ALLOC_INCREMENT; + sorting_files = malloc(sizeof(*sorting_files) * num_alloc); + + /* Loop over directories */ + for (idir=0; idir < num_dirs; idir++) { + + /* Open the directory and find appropriate files */ + if ((dir = opendir(directory_list[idir])) == NULL) { + (void) fprintf(stderr, "Error opening directory \"%s\": ", + directory_list[idir]); + perror(NULL); + continue; + } + + /* Loop over entries */ + while ((dirent = readdir(dir)) != NULL) { + + /* Get filename */ + filename = dirent->d_name; + + /* Parse the file name */ + if (sscanf(filename, "%d-%d-%d.ima%c", + &patid, &acqid, &imgid, &ch) != 3) + continue; + + /* Check that we want this file */ + if ((patid != patient_number) || (imgid < first_image) || + (imgid > last_image)) + continue; + + /* Add more space if needed */ + if (num_entries >= num_alloc) { + num_alloc += ALLOC_INCREMENT; + sorting_files = realloc(sorting_files, + sizeof(*sorting_files) * num_alloc); + } + + /* Add the entry and its number for sorting */ + sorting_files[num_entries].key = imgid; + sorting_files[num_entries].name = strdup(dirent->d_name); + sorting_files[num_entries].dirindex = idir; + + /* Increment the number of entries */ + num_entries++; + + } /* End of loop over files in directory */ + + /* Close the directory */ + (void) closedir(dir); + + } /* End of loop over directories */ + + /* Check the number of entries */ + if (num_entries <= 0) return; + + /* Sort the files */ + qsort(sorting_files, num_entries, sizeof(sorting_files[0]), + sort_function); + + /* Copy the file names into an array */ + *num_files = num_entries; + *file_list = malloc(sizeof(**file_list) * (num_entries + 1)); + *file_dirindex_list = malloc(sizeof(**file_dirindex_list) + * (num_entries + 1)); + (*file_list)[num_entries] = NULL; + (*file_dirindex_list)[num_entries] = -1; + for (ifile=0; ifile < num_entries; ifile++) { + (*file_list)[ifile] = sorting_files[ifile].name; + (*file_dirindex_list)[ifile] = sorting_files[ifile].dirindex; + } + + /* Free the sorting structure */ + free(sorting_files); + + return; +} + + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : sort_function +@INPUT : entry1 + entry2 +@OUTPUT : (none) +@RETURNS : -1, 0, 1 for lt, eq, gt +@DESCRIPTION: Function to compare two numbers for sorting files. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : July 11, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +private int sort_function(const void *entry1, const void *entry2) +{ + int value1, value2; + + value1 = ((Sort_entry *) entry1)->key; + value2 = ((Sort_entry *) entry2)->key; + + if (value1 < value2) return -1; + else if (value1 > value2) return 1; + else return 0; + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : free_file_names +@INPUT : fullpath + file_list + num_files +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Routine to free file name strings +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : September 19, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void free_file_names(char *fullpath, char *file_list[], int num_files) +{ + int ifile; + + free(fullpath); + for (ifile=0; ifile < num_files; ifile++) { + free(file_list[ifile]); + } + free(file_list); +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : multi_image_init +@INPUT : group_list - group list as read in from file +@OUTPUT : multi_image - structure containing information to be used + for modifying group list in loop over images +@RETURNS : number of images to be processed +@DESCRIPTION: Routine to set up loop over multiple images in an input file. +@METHOD : +@GLOBALS : mosaic_info_list +@CALLS : +@CREATED : November 6, 1998 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public int multi_image_init(Acr_Group group_list, + Multi_Image *multi_image) +{ + int group_id, element_id; + int last_image, grid_size; + long new_image_size; + void *data; + Acr_Element element; + char string[256]; + int idim; + double pixel_spacing[2], separation; + char *protocol; + Mosaic_Info mosaic_info; + + /* Find the protocol name and look for a mosaic info structure */ + protocol = acr_find_string(group_list, SPI_Sequence_File_Name, ""); + mosaic_info = get_mosaic_info(mosaic_info_list, protocol); + + /* Check whether we need to do anything */ + multi_image->mosaic_seq = (mosaic_info != NULL); + multi_image->packed = (mosaic_info != NULL); + + if (!multi_image->packed) return 1; + + /* Get some basic image information */ + multi_image->big[0] = acr_find_int(group_list, ACR_Columns, 1); + multi_image->big[1] = acr_find_int(group_list, ACR_Rows, 1); + multi_image->pixel_size = + (acr_find_int(group_list, ACR_Bits_allocated, 16)-1) / 8 + 1; + + /* Get the image size */ + multi_image->size[0] = mosaic_info->size[0]; + multi_image->size[1] = mosaic_info->size[1]; + + /* Get the grid shape, checking that it is not too big if specified */ + multi_image->grid[0] = multi_image->big[0] / multi_image->size[0]; + multi_image->grid[1] = multi_image->big[1] / multi_image->size[1]; + if ((multi_image->grid[0] < 1) || (multi_image->grid[0] < 1)) { + (void) fprintf(stderr, "Grid too small: %d x %d\n", + multi_image->grid[0], multi_image->grid[1]); + exit(EXIT_FAILURE); + } + + /* Check whether we need to do anything (1x1 grid may be the whole image) */ + grid_size = multi_image->grid[0] * multi_image->grid[1]; + if ((grid_size == 1) && + (multi_image->size[0] == multi_image->big[0]) && + (multi_image->size[1] == multi_image->big[1])) { + /* had to remove this as now ANY images acquired with + the mosaic sequence need special treatment */ + multi_image->packed = FALSE; + return 1; + } + + /* Steal the image element from the group list */ + multi_image->big_image = acr_find_group_element(group_list, ACR_Image); + + if (multi_image->big_image == NULL) { + (void) fprintf(stderr, "Couldn't find an image\n"); + exit(EXIT_FAILURE); + } + group_id = acr_get_element_group(multi_image->big_image); + element_id = acr_get_element_element(multi_image->big_image); + acr_group_steal_element(acr_find_group(group_list, group_id), + multi_image->big_image); + + /* Add a small image */ + new_image_size = + multi_image->size[0] * multi_image->size[1] * multi_image->pixel_size; + data = malloc((size_t) new_image_size); + multi_image->small_image = + acr_create_element(group_id, element_id, + acr_get_element_vr(multi_image->big_image), + new_image_size, data); + acr_set_element_vr(multi_image->small_image, + acr_get_element_vr(multi_image->big_image)); + acr_set_element_byte_order(multi_image->small_image, + acr_get_element_byte_order(multi_image->big_image)); + acr_set_element_vr_encoding(multi_image->small_image, + acr_get_element_vr_encoding(multi_image->big_image)); + acr_insert_element_into_group_list(&group_list, multi_image->small_image); + + /* Update the pixel size */ + acr_insert_short(&group_list, ACR_Rows, multi_image->size[1]); + acr_insert_short(&group_list, ACR_Columns, multi_image->size[0]); + + /* Get image image index info */ + last_image = acr_find_int(group_list, SPI_Current_slice_number, 1); + + /* sub_images is now just the number of mosaic elements, even if + they don't all contain slices */ + + multi_image->sub_images = multi_image->grid[0] * multi_image->grid[1]; + + /* NOTE: multi_image->first_image is unkown now, since last_image + is no longer necessarily correct. We update slice numbers in + main() now. + + multi_image->first_image = last_image - + multi_image->sub_images + 1; */ + + /* Fiddle the pixel size */ + element = acr_find_group_element(group_list, ACR_Pixel_size); + if ((element != NULL) && + (acr_get_element_numeric_array(element, 2, pixel_spacing) == 2)) { + pixel_spacing[0] *= + (double) multi_image->big[0] / (double) multi_image->size[0]; + pixel_spacing[1] *= + (double) multi_image->big[1] / (double) multi_image->size[1]; + (void) sprintf(string, "%.15g\\%.15g", + pixel_spacing[0], pixel_spacing[1]); + acr_insert_string(&group_list, ACR_Pixel_size, string); + } + + /* Get step between slices */ + separation = acr_find_double(group_list, ACR_Slice_thickness, 1.0) * + (1.0 + acr_find_double(group_list, SPI_Slice_distance_factor, + 0.0)); + + element = acr_find_group_element(group_list, SPI_Image_normal); + if ((element == NULL) || + (acr_get_element_numeric_array(element, 3, multi_image->normal) != 3)) { + multi_image->normal[0] = 0.0; + multi_image->normal[1] = 0.0; + multi_image->normal[2] = 1.0; + } + for (idim=0; idim < 3; idim++) { + multi_image->step[idim] = separation * multi_image->normal[idim]; + } + + /* Get position and correct to first slice */ + element = acr_find_group_element(group_list, SPI_Image_position); + if ((element == NULL) || + (acr_get_element_numeric_array(element, 3, + multi_image->position) != 3)) { + multi_image->position[0] = 0.0; + multi_image->position[1] = 0.0; + multi_image->position[2] = 0.0; + } + + /* sub_images may be wrong here if there is overflow, so I'm + commenting this out and moving this step to a point in main() + where multi_image->sub_images has been corrected */ + + /* for (idim=0; idim < 3; idim++) { + multi_image->position[idim] -= + (double) (multi_image->sub_images-1) * multi_image->step[idim]; + } */ + + /* Return number of sub-images in this image */ + return multi_image->sub_images; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : multi_image_modify_group_list +@INPUT : group_list - group list as read in from file + multi_image - structure containing information to be used + for modifying group list in loop over images + iimage - image number to process (counting from zero). +@OUTPUT : group_list +@RETURNS : (nothing) +@DESCRIPTION: Routine to modify the group list to use a new image. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 6, 1998 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void multi_image_modify_group_list(Acr_Group group_list, + Multi_Image *multi_image, + int iimage) +{ + int irow, ibyte, idim, nbyte; + int isub, jsub; + char *new, *old; + long old_offset, new_offset; + double position[3], distance; + char string[256]; + + /* Check whether we need to do anything */ + if (!multi_image->packed) return; + + /* Check the image number */ + if ((iimage < 0) || (iimage > multi_image->sub_images)) { + (void) fprintf(stderr, "Invalid image number to send: %d of %d\n", + iimage, multi_image->sub_images); + exit(EXIT_FAILURE); + } + + /* Figure out the sub-image indices */ + isub = iimage % multi_image->grid[0]; + jsub = iimage / multi_image->grid[0]; + + /* Get pointers */ + old = acr_get_element_data(multi_image->big_image); + new = acr_get_element_data(multi_image->small_image); + + /* Copy the image */ + nbyte = multi_image->size[0] * multi_image->pixel_size; + for (irow=0; irow < multi_image->size[1]; irow++) { + old_offset = isub * multi_image->size[0] + + (jsub * multi_image->size[1] + irow) * multi_image->big[0]; + old_offset *= multi_image->pixel_size; + new_offset = (irow * multi_image->size[0]) * multi_image->pixel_size; + for (ibyte=0; ibyte < nbyte; ibyte++) { + new[new_offset + ibyte] = old[old_offset + ibyte]; + } + } + + /* Reset the byte order and VR encoding. This will be modified on each + send according to what the connection needs. */ + acr_set_element_byte_order(multi_image->small_image, + acr_get_element_byte_order(multi_image->big_image)); + acr_set_element_vr_encoding(multi_image->small_image, + acr_get_element_vr_encoding(multi_image->big_image)); + + /* Update the index */ + acr_insert_numeric(&group_list, SPI_Current_slice_number, + (double) (iimage + multi_image->first_image)); + + /* Update the position */ + distance = 0.0; + for (idim=0; idim < 3; idim++) { + position[idim] = multi_image->position[idim] + + (double) iimage * multi_image->step[idim]; + distance += position[idim] * multi_image->normal[idim]; + } + + (void) sprintf(string, "%.15g\\%.15g\\%.15g", + position[0], position[1], position[2]); + acr_insert_string(&group_list, SPI_Image_position, string); + acr_insert_numeric(&group_list, SPI_Image_distance, distance); + update_coordinate_info(group_list); + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : multi_image_cleanup +@INPUT : group_list - group list as read in from file + multi_image - structure containing information to be used + for modifying group list in loop over images +@OUTPUT : multi_image +@RETURNS : (nothing) +@DESCRIPTION: Routine to clean up after doing multi-image stuff. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 6, 1998 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void multi_image_cleanup(Acr_Group group_list, + Multi_Image *multi_image) +/* ARGSUSED */ +{ + + if (!multi_image->packed) return; + + acr_delete_element(multi_image->big_image); +} + +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_dicom_to_minc.c @@ -0,0 +1,1287 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : siemens_dicom_to_minc.c +@DESCRIPTION: Code to convert a list of Siemens dicom files to minc + format. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + * $Log: siemens_dicom_to_minc.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.18 2002/09/26 15:24:33 rhoge + * Before was only skipping time sort for multi-slice N4 mosaics. Turns out + * this was also causing failure on single-slice scans. + * Changed slices>1 to slices>0 so that now N4 dicom scans never get sorted + * on their (apparently nonsensical) time value. The if statement should + * really be reworked, and should keep an eye on this. Seems like EPI + * time series sequences never sort properly on 'time'. + * + * Revision 1.17 2002/09/25 17:25:43 rhoge + * changed public void's to public int's + * + * Revision 1.16 2002/05/08 19:32:40 rhoge + * fixed handling of diffusion scans with separate series for each average + * + * Revision 1.15 2002/05/01 21:29:34 rhoge + * removed MrProt from minc header - encountered files with large strings, + * causing seg faults + * + * Revision 1.14 2002/04/30 12:36:35 rhoge + * fixes to handle current (and hopefully final) diffusion sequence + * + * Revision 1.13 2002/04/26 03:27:03 rhoge + * fixed MrProt problem - replaced fixed lenght char array with malloc + * + * Revision 1.12 2002/04/08 03:40:56 rhoge + * fixed mosaic extraction for non-square scans and 3D scans. + * added some new dicom elements + * + * Revision 1.11 2002/03/27 19:38:08 rhoge + * small comment change + * + * Revision 1.10 2002/03/27 18:57:50 rhoge + * added diffusion b value + * + * Revision 1.9 2002/03/23 13:17:53 rhoge + * added support for Bourget network pushed dicom files, cleaned up + * file check and read_numa4_dicom vr check/assignment + * + * Revision 1.8 2002/03/22 19:19:36 rhoge + * Numerous fixes - + * - handle Numaris 4 Dicom patient name + * - option to cleanup input files + * - command option + * - list-only option + * - debug mode + * - user supplied name, idstr + * - anonymization + * + * Revision 1.7 2002/03/21 13:31:56 rhoge + * updated comments + * + * Revision 1.6 2002/03/19 22:10:16 rhoge + * removed time sorting for N4DCM mosaics - time is random for mosaics + * + * Revision 1.5 2002/03/19 13:13:56 rhoge + * initial working mosaic support - I think time is scrambled though. + * + * Revision 1.4 2001/12/31 18:27:21 rhoge + * modifications for dicomreader processing of Numaris 4 dicom files - at + * this point code compiles without warning, but does not deal with + * mosaiced files. Also will probably not work at this time for Numaris + * 3 .ima files. dicomserver may also not be functional... + * + * Revision 1.3 2000/12/14 21:37:11 rhoge + * log message cleanup + * + * Revision 1.2 2000/12/14 21:36:22 rhoge + * changes to restore measurement loop support that was broken by changes + * to provide acquisition loop support + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * -now support Siemens acquisition loop scans with and without correction + * on sending side + * + * Revision 6.1 1999/10/29 17:51:59 neelin + * Fixed Log keyword + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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 <dicomserver.h> + +extern int Do_logging; +char *pname; +File_Type file_type; /* type of input files */ +int Fork; +int N4_OFFSET; + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : siemens_dicom_to_minc +@INPUT : num_files - number of image files + file_list - list of file names + minc_file - name of output minc file (NULL means make one + up) + clobber - if TRUE, then open the output with NC_CLOBBER + file_prefix - string providing any directory or prefix + for internally generated filename (if it is a directory, + then it must contain the last "/") +@OUTPUT : output_file_name - returns a pointer to an internal area + containing the file name of the created file if minc_file + is NULL, or simply a pointer to minc_file. If NULL, then + nothing is returned. +@RETURNS : EXIT_SUCCESS if no error, EXIT_FAILURE on error. +@DESCRIPTION: Routine to convert a list of Siemens dicom files to minc + format. +@METHOD : +@GLOBALS : Do_logging +@CALLS : +@CREATED : November 25, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public int siemens_dicom_to_minc(int num_files, char *file_list[], + char *minc_file, int clobber, + char *file_prefix, char **output_file_name) +{ + Acr_Group group_list; + int max_group; + File_Info *file_info; + General_Info general_info; + General_Info general_info_orig; + Image_Data image; + int icvid; + int ifile; + Mri_Index imri; + char *out_file_name; + int isep; + + // variables added by rhoge + + int num_files_expected; + int num_avg_nominal; + int num_frames_nominal; + int idim; + int frame; + int num_frames; + int index; + Loop_Type loop_type = NONE; + int subimage; + int iimage; + int num_images_allocated; + Acr_Element big_image; + Acr_Element small_image; + Multi_Image multi_image; + + /* Allocate space for the file information */ + file_info = MALLOC(num_files * sizeof(*file_info)); + num_images_allocated = num_files; + + // Last group needed for first pass + // max_group = ACR_ACTUAL_IMAGE_GID - 1; + // we now have to read up to and including the image, + // since image pointers are needed in multi_image_init + max_group = ACR_ACTUAL_IMAGE_GID; + + /* Add all control characters as numeric array separators to handle + odd behaviour with Siemens dicom files */ + for (isep=0; isep < 31; isep++) { + (void) acr_element_numeric_array_separator(isep); + } + + /* Initialize some values for general info */ + general_info.initialized = FALSE; + general_info.group_list = NULL; + for (imri=0; imri < MRI_NDIMS; imri++) { + general_info.indices[imri] = NULL; + general_info.coordinates[imri] = NULL; + } + + // Loop through file list getting information + // (note that we have to duplicate the handling + // of multiple images per file in this loop + // to accumulate dimension sizes correctly) + + // need separate counter for images, since some files may + // contain more than one image! + iimage = 0; + + for (ifile=0; ifile < num_files; ifile++) { + + if (!Fork) { + progress(ifile, num_files, "-Parsing series info"); + } + + // Read the file + if (file_type == N4DCM) { + group_list = read_numa4_dicom(file_list[ifile], max_group); + } else if (file_type == IMA) { + group_list = read_siemens_dicom(file_list[ifile], max_group); + } + + // initialize big and small images, if mosaic + if (acr_find_int(group_list, EXT_Slices_in_file,1)>1) { + + multi_image_init(group_list, &multi_image); + + // if multi images in file, extend file_info list + + num_images_allocated += + acr_find_int(group_list, EXT_Slices_in_file,1) - 1; + + file_info = REALLOC(file_info, + num_images_allocated * sizeof(*file_info)); + + } + + // loop over subimages in mosaic + for(subimage = 0; + subimage < acr_find_int(group_list, EXT_Slices_in_file,1); + subimage++) { + + // Modify the group list for this image if mosaic + if (acr_find_int(group_list, EXT_Slices_in_file,1)>1) { + multi_image_modify_group_list(group_list,&multi_image,subimage); + } + + // Get file-specific information + get_file_info(group_list, &file_info[iimage], &general_info); + + // increment iimage here + iimage++; + } + + // Delete the group list + acr_delete_group_list(group_list); + // cleanup multi_image struct if used + if (general_info.num_slices_in_file > 1) { + multi_image_cleanup(group_list, &multi_image); + } + + } + + // Sort the dimensions + sort_dimensions(&general_info); + + // Create the output file + if (general_info.initialized) { + icvid = create_minc_file(minc_file, clobber, &general_info, + file_prefix, &out_file_name, + loop_type); + } + if (output_file_name != NULL) + *output_file_name = out_file_name; + + // Check that we found the general info and that the minc file was + // created okay + if ((!general_info.initialized) || (icvid == MI_ERROR)) { + if (general_info.initialized) { + (void) fprintf(stderr, "Error creating minc file %s.\n", + out_file_name); + } + free_info(&general_info, file_info, num_files); + FREE(file_info); + return EXIT_FAILURE; + } + + if (Do_logging > HIGH_LOGGING) { /* rhoge */ + fprintf(stderr,"\nAbout to enter minc write loop...\n"); + } + + + // Loop through the files again and put images into the minc file + iimage = 0; + for (ifile=0; ifile < num_files; ifile++) { + + if (!Fork) { + progress(ifile, num_files, "-Creating minc file"); + } + + // Check that we have a valid file + if (!file_info[ifile].valid) { + continue; + } + + // Read the file + if (file_type == N4DCM) { + group_list = read_numa4_dicom(file_list[ifile], max_group); + } else if (file_type == IMA) { + group_list = read_siemens_dicom(file_list[ifile], max_group); + } + + // initialize big and small images, if mosaic + if (general_info.num_slices_in_file > 1) { + multi_image_init(group_list, &multi_image); + } + + // loop over subimages in mosaic + for(subimage = 0; + subimage < general_info.num_slices_in_file; + subimage++) { + + // Modify the group list for this image if mosaic + if (general_info.num_slices_in_file > 1) { + multi_image_modify_group_list(group_list,&multi_image,subimage); + } + + // Get image + get_siemens_dicom_image(group_list, &image); + + // Save the image and any other information + save_minc_image(icvid, &general_info, &file_info[iimage], &image); + + // increment image counter + iimage++; + + } + + // Delete the group list + acr_delete_group_list(group_list); + // cleanup multi_image struct if used + if (general_info.num_slices_in_file > 1) { + multi_image_cleanup(group_list, &multi_image); + } + + /* Free the image data */ + if ((image.data != NULL) && (image.free)) FREE(image.data); + + } + + /* Close the output file */ + close_minc_file(icvid); + + /* Free the general_info and file_info stuff */ + free_info(&general_info, file_info, num_files); + FREE(file_info); + + return EXIT_SUCCESS; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : read_siemens_dicom +@INPUT : filename - name of siemens dicom file to read + max_group - maximum group number to read +@OUTPUT : (none) +@RETURNS : group list read in from file +@DESCRIPTION: Routine to read in a group list from a file. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 25, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public Acr_Group read_siemens_dicom(char *filename, int max_group) +{ + FILE *fp; + Acr_File *afp; + Acr_Group group_list; + Acr_byte_order byte_order; + Acr_VR_encoding_type vr_encoding; + + /* Open the file */ + fp = fopen(filename, "r"); + if (fp == NULL) { + fprintf(stderr,"Error opening file %s!\n",filename); + return NULL; + } + + /* Connect to input stream */ + afp=acr_file_initialize(fp, 0, acr_stdio_read); + + /* should be: if file type is numa 4 dicom, and if read from file + with first 128 bytes blank, then do required skipping and setup */ + + if (1) { + + /* set byte ordering to explicit LE */ + + byte_order = ACR_LITTLE_ENDIAN; + vr_encoding = ACR_EXPLICIT_VR; + acr_set_byte_order(afp, byte_order); + acr_set_vr_encoding(afp, vr_encoding); + + /* skip 1st 128 bytes - are empty in storage class + + DICM (4 chars) 128+4 = 132 bytes to skip */ + + acr_skip_input_data(afp, 132); + + } + + /* Read in group list */ + (void) acr_input_group_list(afp, &group_list, max_group); + + /* Close the file */ + acr_file_free(afp); + (void) fclose(fp); + + return group_list; + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : read_numa4_dicom +@INPUT : filename - name of siemens Numaris 4 `dicom' file to read + max_group - maximum group number to read +@OUTPUT : (none) +@RETURNS : group list read in from file +@DESCRIPTION: Routine to read in a group list from a file. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : December 18, 2001 (Rick Hoge) +@MODIFIED : +---------------------------------------------------------------------------- */ +public Acr_Group read_numa4_dicom(char *filename, int max_group) +{ + FILE *fp; + Acr_File *afp; + Acr_Group group_list; + Acr_byte_order byte_order; + Acr_VR_encoding_type vr_encoding; + + // needed for group repair - some essential info + // only available in ascii dump of MrProt structure + Acr_Element Protocol; + Acr_Element element; + + int mosaic_rows, mosaic_cols; + short subimage_size[4]; + int subimage_rows, subimage_cols; + int num_slices, num_partitions; + char *field_ptr; + int num_encodings, enc_ix; + int average, files_per_average; + + // Open the file + fp = fopen(filename, "r"); + if (fp == NULL) { + fprintf(stderr,"Error opening file %s!\n",filename); + return NULL; + } + + // Connect to input stream + afp=acr_file_initialize(fp, 0, acr_stdio_read); + + /* should be: if file type is numa 4 dicom, and if read from file + with first 128 bytes blank, then do required skipping and setup */ + + // set byte ordering to explicit LE + + byte_order = ACR_LITTLE_ENDIAN; + // vr_encoding = ACR_EXPLICIT_VR; // CD format + // vr_encoding = ACR_IMPLICIT_VR; // Bourget format + + vr_encoding = acr_get_vr_encoding(afp); // doesn't seem to work on Export/CD + acr_set_byte_order(afp, byte_order); + acr_set_vr_encoding(afp, vr_encoding); + + if (N4_OFFSET) { + // skip 1st 128 bytes - are empty in Syngo CD/Local Export files + // DICM (4 chars) 128+4 = 132 bytes to skip + vr_encoding = ACR_EXPLICIT_VR; // CD format + acr_set_vr_encoding(afp, vr_encoding); + acr_skip_input_data(afp, 132); + } + // Read in group list + (void) acr_input_group_list(afp, &group_list, max_group); + + // Close the file + acr_file_free(afp); + (void) fclose(fp); + + // now fix the group list to provide essential info + // via standard dicom elements + // (this lets the rest of the code be more reusable) + + // Note that these parameters are mostly dimension lengths, + // and are not usually supplied in standard DICOM implementations. + // We could do without them, except that the use of mosaics for + // multi-slice data makes at least the number of slices necessary. + // For such elements, we will spoof our own `private' entries + // using Numaris 3.5 coordinates. These should not be used elsewhere + // in the code unless there's no other way! Basically things + // should be done as follows: + + // 1) use actual element in public dicom group, if possible + // 2) if not, then get info from MrProt and insert as + // correct public dicom element + // 3) if no public element exists, use an SPI element (careful!) + // 4) if no SPI element exists, use and EXT element (careful!) + + // WOULD RETURN HERE FOR NORMAL DICOM + + // read in Protocol group + Protocol = acr_find_group_element(group_list,SPI_Protocol); + + // add number of dynamic scans: + acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, + atoi((char*)prot_find_string(Protocol,"lRepetitions"))+1); + + // add number of echoes: + acr_insert_numeric(&group_list, SPI_Number_of_echoes, + atoi((char*)prot_find_string(Protocol,"lContrasts"))); + + // add receiving coil (for some reason this isn't in generic groups) + acr_insert_string(&group_list, ACR_Receiving_coil, + prot_find_string(Protocol, + "sCOIL_SELECT_MEAS.asList[0].sCoilElementID.tCoilID")); + // add MrProt dump + acr_insert_string(&group_list,EXT_MrProt_dump,dump_protocol_text(Protocol)); + + // add number of slices: + // (called `Partitions' for 3D) + num_slices = atoi((char*)prot_find_string(Protocol,"sSliceArray.lSize")); + num_partitions = + atoi((char*)prot_find_string(Protocol,"sKSpace.lPartitions")); + // NOTE: for some reason, lPartitions > 1 even for 2D scans + // (e.g. EPI, scouts) + + if (!strncmp(acr_find_string(group_list,ACR_MR_acquisition_type, + ""),"3D",2)) { + // use partitions if 3D + // (note that this gets more complicated if the 3D scan + // is mosaiced - see below) + + acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal, + 1); + acr_insert_numeric(&group_list, SPI_Number_of_3D_raw_partitions_nominal, + num_partitions); + } else { + // use slices for 2D + acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal,num_slices); + acr_insert_numeric(&group_list, + SPI_Number_of_3D_raw_partitions_nominal,1); + } + + // now figure out mosaic rows and columns, and put in EXT shadow group + + // check for interpolation - not supported for mosaics yet + if (strcmp(prot_find_string(Protocol,"sKSpace.uc2DInterpolation"),"0")){ + // if interpolated image, assume no mosaic + acr_insert_numeric(&group_list, EXT_Mosaic_rows, 1); + acr_insert_numeric(&group_list, EXT_Mosaic_columns, 1); + acr_insert_numeric(&group_list, EXT_Slices_in_file, 1); + + } else { + // compute mosaic rows and columns + + // here is a hack to handle non-square mosaiced images + // WARNING: as far as I can tell, the phase-encoding dir + // (row/col) is reversed for mosaic EPI scans (don't know + // if this is a mosaic thing, an EPI thing, or whatever) + + // get the array of sizes: + // freq row/freq col/phase row/phase col + element = acr_find_group_element(group_list, ACR_Acquisition_matrix); + acr_get_element_short_array(element, 4, subimage_size); + + // get subimage dimensions, assuming the OPPOSITE of the + // reported phase-encode direction!! + if (!strncmp(acr_find_string(group_list,ACR_Phase_encoding_direction,""), + "COL",3)) { + + subimage_rows = subimage_size[3]; + subimage_cols = subimage_size[0]; + + } else if (!strncmp(acr_find_string(group_list, + ACR_Phase_encoding_direction,""),"ROW",3)) { + + subimage_rows = subimage_size[2]; + subimage_cols = subimage_size[1]; + + } + + mosaic_rows = acr_find_int(group_list,ACR_Rows, 1)/subimage_rows; + mosaic_cols = acr_find_int(group_list,ACR_Columns, 1)/subimage_cols; + + acr_insert_numeric(&group_list, EXT_Mosaic_rows,mosaic_rows); + acr_insert_numeric(&group_list, EXT_Mosaic_columns,mosaic_cols); + + if (mosaic_rows * mosaic_cols > 1) { + + // if 3D mosaiced scan, write number of partitions to number of + // slices in dicom group THIS LOOKS REDUNDANT!!! + if (!strncmp(acr_find_string(group_list,ACR_MR_acquisition_type, + ""),"3D",2)) { + acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal, + num_partitions); + } + + // assume any mosaiced file contains all slices + // (we now support mosaics for 2D and 3D acquisitions, + // so we may need to use partitions instead of slices) + + if (!strncmp(acr_find_string(group_list,ACR_MR_acquisition_type,""), + "2D",2)) { + acr_insert_numeric(&group_list, EXT_Slices_in_file, num_slices); + acr_insert_numeric(&group_list, + SPI_Number_of_slices_nominal,num_slices); + } else if (!strncmp(acr_find_string(group_list,ACR_MR_acquisition_type, + ""),"3D",2)) { + acr_insert_numeric(&group_list, EXT_Slices_in_file, + num_partitions); + acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal, + num_partitions); + // also have to provide slice spacing - in case of 3D it's same + // as slice thickness (and not provided in dicom header!) + acr_insert_numeric(&group_list, ACR_Spacing_between_slices, + acr_find_double(group_list,ACR_Slice_thickness,1.0)); + } + } else { + acr_insert_numeric(&group_list, EXT_Slices_in_file, 1); + } + + // correct the rows and columns values - + // these will reflect those of the subimages in the mosaics + // NOT the total image dimensions + acr_insert_short(&group_list, EXT_Sub_image_columns,subimage_cols); + acr_insert_short(&group_list, EXT_Sub_image_rows,subimage_rows); + + // should also correct the image position here? + + } + + // correct dynamic scan info if diffusion scan: + // + // assumptions: + // + // - diffusion protocol indicated by sDiffusion.ucMode = 0x4 + // - there are 7 shots for DTI (b=0 + 6 encodings) + // - b=0 scan has sequence name "ep_b0" + // - encoded scans have seq names "ep_b700#1, ep_b700#2, ..." etc. + // + // actions: + // + // - change number of dynamic scans to 7 + // - modify dynamic scan index to encoding index + + if (!strcmp(prot_find_string(Protocol,"sDiffusion.ucMode"),"0x4")) { + + // try to get b value + acr_insert_numeric(&group_list, + EXT_Diffusion_b_value, + atoi((char*)prot_find_string(Protocol, + "sDiffusion.alBValue[1]"))); + + // if all averages in one series: + if (!strcmp(prot_find_string(Protocol,"ucOneSeriesForAllMeas"),"0x1")){ + + num_encodings = 7; // for now assume 7 shots in diffusion scan + + // number of 'time points' + acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, + num_encodings* + acr_find_double(group_list,ACR_Nr_of_averages,1)); + + // time index of current scan: + + // In the current scheme, the unencoded + // scan has a sequence name like "ep_b0" while the subsequent + // six diffusion encodings have names like "ep_b700#1" + // we could use this to come up with indices for an encoding dimension + field_ptr=strstr(acr_find_string(group_list,ACR_Sequence_name,""),"#"); + if (field_ptr == NULL) { + enc_ix = 0; + } else { + enc_ix = atoi(field_ptr+sizeof(char)); + } + + // however with the current sequence, we get usable + // time indices from floor(global_image_num/num_slices) + acr_insert_numeric(&group_list, ACR_Acquisition, + (acr_find_int(group_list, ACR_Image, 1)-1) / + num_slices); + + } else { // averages in different series - no special handling needed? + + num_encodings = 7; // for now assume 7 shots in diffusion scan + + // number of 'time points' + acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, + num_encodings); + + // For multi-series scans, we DO USE THIS BECAUSE global + // image number may be broken!! + field_ptr=strstr(acr_find_string(group_list,ACR_Sequence_name,""),"#"); + if (field_ptr == NULL) { + enc_ix = 0; + } else { + enc_ix = atoi(field_ptr+sizeof(char)); + } + + acr_insert_numeric(&group_list, ACR_Acquisition, enc_ix); + + + } + } // end of diffusion scan handling + + return group_list; + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : free_info +@INPUT : general_info + file_info + num_files +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Routine to free contents of general and file info structures. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 26, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +/* ARGSUSED */ +public void free_info(General_Info *general_info, File_Info *file_info, + int num_files) +{ + Mri_Index imri; + + /* Free the general info pointers */ + for (imri=0; imri < MRI_NDIMS; imri++) { + if (general_info->indices[imri] != NULL) { + FREE(general_info->indices[imri]); + } + if (general_info->coordinates[imri] != NULL) { + FREE(general_info->coordinates[imri]); + } + } + + /* Free the group list */ + if (general_info->group_list != NULL) { + acr_delete_group_list(general_info->group_list); + } + + return; + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : search_list +@INPUT : value + list + list_length + starting_point - point from which search should start +@OUTPUT : (none) +@RETURNS : Index in list where value is found, or -1 is value not found. +@DESCRIPTION: Routine to search a list for a value, returning the index + into the list. If the value is not found, then -1 is returned. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 28, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public int search_list(int value, int list[], int list_length, + int starting_point) +{ + int index; + + /* Check list length and starting point */ + if (list_length <= 0) return -1; + if ((starting_point >= list_length) || (starting_point < 0)) { + starting_point = 0; + } + + /* Loop over indices, wrapping at the end of the list */ + index = starting_point; + do { + if (list[index] == value) return index; + index++; + if (index >= list_length) index = 0; + } while (index != starting_point); + + /* If we get to here, we didn't find the value */ + return -1; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : sort_dimensions +@INPUT : general_info +@OUTPUT : general_info +@RETURNS : (nothing) +@DESCRIPTION: Routine to sort the MRI dimensions according to their + coordinates. It also fills in the step and start values for + the SLICE dimension. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 28, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void sort_dimensions(General_Info *general_info) +{ + Mri_Index imri; + Sort_Element *sort_array; + int nvalues, ival, jval; + int reverse_array; + + /* Sort the dimensions, if needed */ + for (imri = 0; imri < MRI_NDIMS; imri++) { + if (general_info->size[imri] > 1 && + !((file_type == N4DCM) && // don't sort on time for N4 mosaics! + (imri == TIME) && + (general_info->num_slices_in_file > 0))) { // also fails on 1 slice + + /* Set up the array for sorting */ + nvalues = general_info->size[imri]; + sort_array = MALLOC(nvalues * sizeof(*sort_array)); + for (ival=0; ival < nvalues; ival++) { + sort_array[ival].identifier = general_info->indices[imri][ival]; + sort_array[ival].original_index = ival; + sort_array[ival].value = general_info->coordinates[imri][ival]; + } + + /* Sort the array */ + qsort((void *) sort_array, (size_t) nvalues, sizeof(*sort_array), + dimension_sort_function); + + /* Figure out if we should reverse the array to keep something + similar to the original ordering */ + reverse_array = (sort_array[0].original_index > + sort_array[nvalues-1].original_index); + + /* Copy the information back into the appropriate arrays */ + for (ival=0; ival < nvalues; ival++) { + jval = (reverse_array ? nvalues - ival - 1 : ival); + general_info->indices[imri][ival] = sort_array[jval].identifier; + general_info->coordinates[imri][ival] = sort_array[jval].value; + } + + /* Free the array */ + FREE(sort_array); + + /* Update slice step and start */ + if (imri == SLICE) { + if (general_info->coordinates[imri][0] != + general_info->coordinates[imri][nvalues-1]) { + general_info->step[general_info->slice_world] = + (general_info->coordinates[imri][nvalues-1] - + general_info->coordinates[imri][0]) / + ((double) general_info->size[imri] - 1.0); + } + general_info->start[general_info->slice_world] = + general_info->coordinates[imri][0]; + } + + } /* If size > 1 */ + } /* Loop over dimensions */ + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dimension_sort_function +@INPUT : v1, v2 - values to compare +@OUTPUT : (none) +@RETURNS : -1, 0 or 1 if v1 < v2, v1 == v2 or v1 > v2 +@DESCRIPTION: Function to compare to array elements for sorting. Elements are + compared first on value, then on their original array index + (this tries to preserve the original sequence). +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : February 28, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public int dimension_sort_function(const void *v1, const void *v2) +{ + Sort_Element *value1, *value2; + + value1 = (Sort_Element *) v1; + value2 = (Sort_Element *) v2; + + if (value1->value < value2->value) + return -1; + else if (value1->value > value2->value) + return 1; + else if (value1->original_index < value2->original_index) + return -1; + else if (value1->original_index > value2->original_index) + return 1; + else + return 0; +} + +public char *prot_find_string(Acr_Element Protocol, char *Field) { + + char FieldName[512]; + char Separator[512]; + char FieldValue[512]; + char *Output = calloc(512,sizeof(char)); // return value + + char ProtHead[] = "### ASCCONV BEGIN ###"; + long prot_offset; + char *field_ptr; + char DefaultValue = '0'; + int ix1,ix2; + + // scan throught the group containing the protocol, to find the + // ascii dump of the MrProt structure + for (prot_offset = 0; + prot_offset < Protocol->data_length - sizeof(ProtHead); + prot_offset++) { + if (!memcmp(Protocol->data_pointer+prot_offset, + ProtHead,sizeof(ProtHead)-1)) { + break; + } + } + + // bail if we didn't find the protocol + if (prot_offset == Protocol->data_length-sizeof(ProtHead)) { + (void) fprintf(stderr, + "ERROR: could not find protocol dump in group\n"); + exit(EXIT_FAILURE); + } + + // try to find the Field name + // (should bracket with newline and white space) + field_ptr = strstr(Protocol->data_pointer+prot_offset,Field); + + if (field_ptr != NULL) { + sscanf(field_ptr,"%s %s %s",FieldName,Separator,FieldValue); + } else { + sprintf(FieldValue,"0"); + } + + // copy FieldValue to output, skipping quotation marks in strings + + ix1 = 0; + for (ix2 = 0; ix2 < strlen(FieldValue); ix2++) { + if (FieldValue[ix2] != '"') + Output[ix1++] = FieldValue[ix2]; + } + + // terminate string + Output[ix1] = '\0'; + + return (char*) Output; + +} + +public char *dump_protocol_text(Acr_Element Protocol) { + + char ProtHead[] = "### ASCCONV BEGIN ###"; + char ProtTail[] = "### ASCCONV END ###"; + char *Output = calloc(Protocol->data_length,sizeof(char)); // return value + int prot_found = FALSE; + long prot_offset; + int ix1; + + // scan throught the group containing the protocol, to find the + // ascii dump of the MrProt structure + ix1 = 0; + for (prot_offset = 0; + prot_offset < Protocol->data_length - sizeof(ProtHead); + prot_offset++) { + if (!memcmp(Protocol->data_pointer+prot_offset, + ProtHead,sizeof(ProtHead)-1)) { + prot_found = TRUE; + } + if (!memcmp(Protocol->data_pointer+prot_offset, + ProtTail,sizeof(ProtTail)-1)) { + break; + } + if (prot_found) { + if (*(Protocol->data_pointer+prot_offset) != '"') + Output[ix1++] = *(Protocol->data_pointer+prot_offset); + } + } + + // terminate string + Output[ix1] = '\0'; + + // bail if we didn't find the protocol + if (prot_offset == Protocol->data_length-sizeof(ProtHead)) { + (void) fprintf(stderr, + "ERROR: could not find protocol dump in group\n"); + exit(EXIT_FAILURE); + } + + return (char*) Output; + +} + +public int multi_image_init(Acr_Group group_list, + Multi_Image *multi_image) +{ + int group_id, element_id; + int last_image, grid_size; + long new_image_size; + void *data; + Acr_Element element; + char string[256]; + int idim; + double pixel_spacing[2], separation; + char *protocol; + double normal[3]; + double RowColVec[6]; + double dircos[VOL_NDIMS][WORLD_NDIMS]; + + // Get some basic image information + // (big[0/1] is number of columns/rows in whole mosaic) + multi_image->big[0] = acr_find_int(group_list, ACR_Columns, 1); + multi_image->big[1] = acr_find_int(group_list, ACR_Rows, 1); + multi_image->pixel_size = + (acr_find_int(group_list, ACR_Bits_allocated, 16)-1) / 8 + 1; + + // Get the image size + // (size[0/1] is number of columns/rows in a single slice) + multi_image->size[0] = acr_find_short(group_list,EXT_Sub_image_columns,1); + multi_image->size[1] = acr_find_short(group_list,EXT_Sub_image_rows,1); + + // Get the grid shape, checking that it is not too big if specified + multi_image->grid[0] = multi_image->big[0] / multi_image->size[0]; + multi_image->grid[1] = multi_image->big[1] / multi_image->size[1]; + if ((multi_image->grid[0] < 1) || (multi_image->grid[0] < 1)) { + (void) fprintf(stderr, "Grid too small: %d x %d\n", + multi_image->grid[0], multi_image->grid[1]); + exit(EXIT_FAILURE); + } + + // Check whether we need to do anything (1x1 grid may be the whole image) + grid_size = multi_image->grid[0] * multi_image->grid[1]; + if ((grid_size == 1) && + (multi_image->size[0] == multi_image->big[0]) && + (multi_image->size[1] == multi_image->big[1])) { + /* had to remove this as now ANY images acquired with + the mosaic sequence need special treatment */ + multi_image->packed = FALSE; + return 1; + } + + // Steal the image element from the group list + multi_image->big_image = acr_find_group_element(group_list, ACR_Pixel_data); + + if (multi_image->big_image == NULL) { + (void) fprintf(stderr, "Couldn't find an image\n"); + exit(EXIT_FAILURE); + } + group_id = acr_get_element_group(multi_image->big_image); + element_id = acr_get_element_element(multi_image->big_image); + acr_group_steal_element(acr_find_group(group_list, group_id), + multi_image->big_image); + + // Add a small image + new_image_size = + multi_image->size[0] * multi_image->size[1] * multi_image->pixel_size; + data = malloc((size_t) new_image_size); + multi_image->small_image = + acr_create_element(group_id, element_id, + acr_get_element_vr(multi_image->big_image), + new_image_size, data); + acr_set_element_vr(multi_image->small_image, + acr_get_element_vr(multi_image->big_image)); + acr_set_element_byte_order(multi_image->small_image, + acr_get_element_byte_order(multi_image->big_image)); + acr_set_element_vr_encoding(multi_image->small_image, + acr_get_element_vr_encoding(multi_image->big_image)); + acr_insert_element_into_group_list(&group_list, multi_image->small_image); + + // Update the number of image rows and columns + acr_insert_short(&group_list, ACR_Rows, multi_image->size[1]); + acr_insert_short(&group_list, ACR_Columns, multi_image->size[0]); + + // Get image image index info (number of slices in file) + last_image = acr_find_int(group_list, EXT_Slices_in_file,1); + + // sub_images is now just the number of mosaic elements, even if + // they don't all contain slices + multi_image->sub_images = multi_image->grid[0] * multi_image->grid[1]; + + // unlike Numaris 3.5, last_image should always be correct + multi_image->first_image = last_image - + multi_image->sub_images + 1; + + // get the pixel size + element = acr_find_group_element(group_list, ACR_Pixel_size); + if ((element != NULL) && + (acr_get_element_numeric_array(element, 2, pixel_spacing) == 2)) { + + // adjust pixel size for old Numaris 3.5 data + if (file_type == IMA || + file_type == N3DCM) { + + pixel_spacing[0] *= + (double) multi_image->big[0] / (double) multi_image->size[0]; + pixel_spacing[1] *= + (double) multi_image->big[1] / (double) multi_image->size[1]; + (void) sprintf(string, "%.15g\\%.15g", + pixel_spacing[0], pixel_spacing[1]); + acr_insert_string(&group_list, ACR_Pixel_size, string); + } + } + + // Get step between slices + separation = acr_find_double(group_list, ACR_Spacing_between_slices, 1.0); + + // get image normal vector + // (need to compute based on dicom field, which gives + // unit vectors for row and column direction) + element = acr_find_group_element(group_list, + ACR_Image_orientation_patient); + acr_get_element_numeric_array(element, 6, RowColVec); + + memcpy(dircos[VCOLUMN],RowColVec,sizeof(RowColVec[0])*3); + memcpy(dircos[VROW],&RowColVec[3],sizeof(RowColVec[0])*3); + + // used to convert x/y flips here... + // convert_dicom_coordinate(dircos[VROW]); + // convert_dicom_coordinate(dircos[VCOLUMN]); + + // compute slice normal as cross product of row/column unit vectors + // (should check for unit length?) + multi_image->normal[0] = + dircos[VCOLUMN][1] * dircos[VROW][2] - + dircos[VCOLUMN][2] * dircos[VROW][1]; + + multi_image->normal[1] = + dircos[VCOLUMN][2] * dircos[VROW][0] - + dircos[VCOLUMN][0] * dircos[VROW][2]; + + multi_image->normal[2] = + dircos[VCOLUMN][0] * dircos[VROW][1] - + dircos[VCOLUMN][1] * dircos[VROW][0]; + + // compute slice-to-slice step vector + for (idim=0; idim < 3; idim++) { + multi_image->step[idim] = separation * multi_image->normal[idim]; + } + + // Get position and correct to first slice + element = acr_find_group_element(group_list, ACR_Image_position_patient); + acr_get_element_numeric_array(element, WORLD_NDIMS,multi_image->position); + + if (file_type == IMA || + file_type == N3DCM) { + // Numaris 3.5 style correction: + // (position in file is for last slice, we want first) + for (idim=0; idim < 3; idim++) { + multi_image->position[idim] -= + (double) (multi_image->sub_images-1) * multi_image->step[idim]; + } + } else { + // Numaris 4 mosaic correction: + // - position given is edge of huge slice constructed as if + // real slice was at center of mosaic + // - multi_image->big[0,1] are number of columns and rows of mosaic + // - multi_image->size[0,1] are number of columns and rows of sub-image + + for (idim=0; idim < 3; idim++) { + + // correct offset from mosaic Center + multi_image->position[idim] += (double) + ((dircos[VCOLUMN][idim]*multi_image->big[0]*pixel_spacing[0]/2.0) + + (dircos[VROW][idim] * multi_image->big[1] * pixel_spacing[1]/2)); + + // move from center to corner of slice + multi_image->position[idim] -= + dircos[VCOLUMN][idim] * multi_image->size[0] * pixel_spacing[0]/2.0 + + dircos[VROW][idim] * multi_image->size[1] * pixel_spacing[1]/2.0; + + } + + } + + /* Return number of sub-images in this image */ + return multi_image->sub_images; +} + +public int multi_image_modify_group_list(Acr_Group group_list, + Multi_Image *multi_image, + int iimage) +{ + int irow, ibyte, idim, nbyte; + int isub, jsub; + char *new, *old; + long old_offset, new_offset; + double position[3], distance; + char string[256]; + + // slice order in mosaic is bottom->top under Numaris 4, + // unlike Numaris 3.5 in which slices were top->bottom + if (!(file_type == IMA || + file_type == N3DCM)) { + iimage = acr_find_int(group_list, EXT_Slices_in_file,1) - iimage - 1; + } + + // Check the image number + if ((iimage < 0) || (iimage > multi_image->sub_images)) { + (void) fprintf(stderr, "Invalid image number to send: %d of %d\n", + iimage, multi_image->sub_images); + exit(EXIT_FAILURE); + } + + // Figure out the sub-image indices + isub = iimage % multi_image->grid[0]; + jsub = iimage / multi_image->grid[0]; + + // Get pointers + old = acr_get_element_data(multi_image->big_image); + new = acr_get_element_data(multi_image->small_image); + + // Copy the image + nbyte = multi_image->size[0] * multi_image->pixel_size; + for (irow=0; irow < multi_image->size[1]; irow++) { + old_offset = isub * multi_image->size[0] + + (jsub * multi_image->size[1] + irow) * multi_image->big[0]; + old_offset *= multi_image->pixel_size; + new_offset = (irow * multi_image->size[0]) * multi_image->pixel_size; + for (ibyte=0; ibyte < nbyte; ibyte++) { + new[new_offset + ibyte] = old[old_offset + ibyte]; + } + } + + // Reset the byte order and VR encoding. This will be modified on each + // send according to what the connection needs. + acr_set_element_byte_order(multi_image->small_image, + acr_get_element_byte_order(multi_image->big_image)); + acr_set_element_vr_encoding(multi_image->small_image, + acr_get_element_vr_encoding(multi_image->big_image)); + + // Update the index + acr_insert_numeric(&group_list, SPI_Current_slice_number, + (double) iimage); + + // Update the position + + for (idim=0; idim < 3; idim++) { + position[idim] = multi_image->position[idim] + + (double) iimage * multi_image->step[idim]; + } + + (void) sprintf(string, "%.15g\\%.15g\\%.15g", + position[0], position[1], position[2]); + acr_insert_string(&group_list, SPI_Image_position, string); + acr_insert_string(&group_list, ACR_Image_position_patient, string); + + if (file_type == IMA || + file_type == N3DCM) { + // this will convert Siemens SPI info into dicom compliant info + // not needed for Numaris 4 + update_coordinate_info(group_list); + } + + return 1; + +} + +public int multi_image_cleanup(Acr_Group group_list, + Multi_Image *multi_image) +/* ARGSUSED */ +{ + + if (!multi_image->packed) return; + + acr_delete_element(multi_image->big_image); + + return 1; +} + + +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_dicom_to_minc.h @@ -0,0 +1,253 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : siemens_dicom_to_minc.h +@DESCRIPTION: Header file for siemens_dicom_to_minc.h +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + * $Log: siemens_dicom_to_minc.h,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.6 2002/04/26 03:27:03 rhoge + * fixed MrProt problem - replaced fixed lenght char array with malloc + * + * Revision 1.5 2002/04/08 17:26:34 rhoge + * added additional sequence info to minc header + * + * Revision 1.4 2002/03/27 18:57:50 rhoge + * added diffusion b value + * + * Revision 1.3 2002/03/19 13:13:57 rhoge + * initial working mosaic support - I think time is scrambled though. + * + * Revision 1.2 2001/12/31 18:27:21 rhoge + * modifications for dicomreader processing of Numaris 4 dicom files - at + * this point code compiles without warning, but does not deal with + * mosaiced files. Also will probably not work at this time for Numaris + * 3 .ima files. dicomserver may also not be functional... + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * + * Revision 6.1 1999/10/29 17:51:59 neelin + * Fixed Log keyword + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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 <minc.h> + +/* General constants */ +#define SECONDS_PER_MINUTE 60 +#define MINUTES_PER_HOUR 60 +#define SECONDS_PER_HOUR (MINUTES_PER_HOUR*SECONDS_PER_MINUTE) +#define HOURS_PER_DAY 24 +#define SECONDS_PER_DAY (HOURS_PER_DAY*SECONDS_PER_HOUR) +#define MS_PER_SECOND 1000 +#define COORDINATE_EPSILON (100.0*FLT_EPSILON) + +/* Default value for ncopts */ +#define NCOPTS_DEFAULT NC_VERBOSE + +/* MINC variable for dicom elements */ +#define DICOM_ROOT_VAR "dicom_groups" + +/* Possible MRI dimensions */ +typedef enum { SLICE = 0, ECHO, TIME, PHASE, CHEM_SHIFT, MRI_NDIMS } Mri_Index; + +/* World dimensions */ +typedef enum { XCOORD = 0, YCOORD, ZCOORD, WORLD_NDIMS } World_Index; + +/* Volume dimensions */ +typedef enum { VSLICE = 0, VROW, VCOLUMN, VOL_NDIMS } Volume_Index; + +/* Orientations */ +typedef enum {TRANSVERSE = 0, SAGITTAL, CORONAL, NUM_ORIENTATIONS} Orientation; + +/* String type */ +typedef char Cstring[256]; + +/* Structure for general info about files */ +typedef struct { + int initialized; + double study_id; + int acq_id; /* Time of scan */ + int rec_num; + int image_type; + Cstring image_type_string; + int nrows; + int ncolumns; + int default_index[MRI_NDIMS]; /* Index for dimensions with size == 1 */ + int size[MRI_NDIMS]; /* Size of dimension across these files */ + int total_size[MRI_NDIMS]; /* Size of dimension across acquisition */ + int *indices[MRI_NDIMS]; /* List of indices found for each dimension. + Only allocated when size > 1 */ + int search_start[MRI_NDIMS]; /* Indices into lists for starting searches */ + double *coordinates[MRI_NDIMS]; /* Array indicating coordinate of each + index in indices array */ + int image_index[MRI_NDIMS]; /* Mapping from MRI dim to output image dim */ + World_Index slice_world; + World_Index row_world; + World_Index column_world; + double step[WORLD_NDIMS]; + double start[WORLD_NDIMS]; + double dircos[WORLD_NDIMS][WORLD_NDIMS]; + nc_type datatype; + int is_signed; + double pixel_min; + double pixel_max; + Cstring units; + double window_min; + double window_max; + int num_mosaic_rows; + int num_mosaic_cols; + int num_slices_in_file; + int sub_image_rows; + int sub_image_columns; + struct { + Cstring name; + Cstring identification; + Cstring birth_date; + Cstring age; + Cstring sex; + Cstring reg_date; + Cstring reg_time; + double weight; + } patient; + struct { + Cstring start_time; + Cstring modality; + Cstring manufacturer; + Cstring model; + double field_value; + Cstring software_version; + Cstring serial_no; + Cstring calibration_date; + Cstring institution; + Cstring station_id; + Cstring referring_physician; + Cstring performing_physician; + Cstring operator; + Cstring procedure; + Cstring study_id; + Cstring acquisition_id; + } study; + struct { + Cstring scan_seq; + Cstring seq_owner; + Cstring seq_descr; + Cstring protocol_name; + Cstring receive_coil; + Cstring transmit_coil; + double rep_time; + double slice_thickness; + double num_slices; + double echo_time; + double echo_number; + double inv_time; + double b_value; + double flip_angle; + double num_avg; + double num_dyn_scans; + double scan_dur; + double ky_lines; + double kymax_ix; + double kymin_ix; + double kz_lines; + double dummy_scans; + double imaging_freq; + Cstring imaged_nucl; + double adc_voltage; + double adc_offset; + double transmit_ampl; + double rec_amp_gain; + double rec_preamp_gain; + double win_center; + double win_width; + double gy_ampl; + double gx_ampl; + double gz_ampl; + double num_phase_enc_steps; + double percent_sampling; + double percent_phase_fov; + double pixel_bandwidth; + char phase_enc_dir[16]; + char mr_acq_type[16]; + char image_type[128]; + double sar; + Cstring comments; + char *MrProt; // Siemens Numaris 4 specific + } acq; + Acr_Group group_list; +} General_Info; + +/* Structure for file-specific info */ +typedef struct { + int valid; + int bits_alloc; + int bits_stored; + int index[MRI_NDIMS]; + double pixel_max; + double pixel_min; + double slice_max; + double slice_min; + double window_max; + double window_min; + double coordinate[MRI_NDIMS]; +} File_Info; + +/* Structure for image data */ +typedef struct { + int free; + int nrows; + int ncolumns; + unsigned short *data; +} Image_Data; + +/* Structure for sorting dimensions */ +typedef struct { + int identifier; + int original_index; + double value; +} Sort_Element; + +// multi-image (mosaic) info +typedef struct { + int packed; + int mosaic_seq; + int size[2]; + int big[2]; + int grid[2]; + int pixel_size; + Acr_Element big_image; + Acr_Element small_image; + int sub_images; + int first_image; + double normal[3]; + double step[3]; + double position[3]; +} Multi_Image; +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_header_table.h @@ -0,0 +1,211 @@ +Siemens_header_entry Siemens_header_table[] = { +{0x0008, 0x0020, &Siemens_header.G08.Ide.StudyDate, create_ds_date_t_element, 1}, +{0x0008, 0x0022, &Siemens_header.G08.Ide.AcquisitionDate, create_ds_date_t_element, 1}, +{0x0008, 0x0023, &Siemens_header.G08.Ide.ImageDate, create_ds_date_t_element, 1}, +{0x0008, 0x0030, &Siemens_header.G08.Ide.StudyTime, create_ds_time_t_element, 1}, +{0x0008, 0x0032, &Siemens_header.G08.Ide.AcquisitionTime, create_ds_time_t_element, 1}, +{0x0008, 0x0033, &Siemens_header.G08.Ide.ImageTime, create_ds_time_t_element, 1}, +{0x0008, 0x0041, &Siemens_header.G08.Ide.DataSetSubtype, create_data_set_subtype_t_element, 1}, +{0x0008, 0x0060, &Siemens_header.G08.Ide.Modality, create_modality_t_element, 1}, +{0x0008, 0x0070, &Siemens_header.G08.Ide.Manufacturer, create_char_element, LENGTH_MANUFACTURER + 1}, +{0x0008, 0x0080, &Siemens_header.G08.Ide.InstitutionID, create_char_element, LENGTH_LABEL + 1}, +{0x0008, 0x0090, &Siemens_header.G08.Ide.ReferringPhysician, create_char_element, LENGTH_LABEL + 1}, +{0x0008, 0x1010, &Siemens_header.G08.Ide.StationID, create_char_element, LENGTH_LABEL + 1}, +{0x0008, 0x1080, &Siemens_header.G08.Ide.AdmittingDiagnosis, create_char_element, LENGTH_DIAGNOSIS + 1}, +{0x0008, 0x1090, &Siemens_header.G08.Ide.ManufacturerModel, create_char_element, LENGTH_LABEL + 1}, +{0x0009, 0x1041, &Siemens_header.G09.Ide.DataObjectSubtype, create_data_object_subtype_t_element, 1}, +{0x0009, 0x1210, &Siemens_header.G09.Ide.StorageMode, create_storage_mode_t_element, 1}, +{0x0009, 0x1226, &Siemens_header.G09.Ide.LastMoveDate, create_ds_date_t_element, 1}, +{0x0009, 0x1227, &Siemens_header.G09.Ide.LastMoveTime, create_ds_time_t_element, 1}, +{0x0009, 0x1316, &Siemens_header.G09.Ide.CPUIdentificationLabel, create_char_element, LENGTH_LABEL + 1}, +{0x0009, 0x1320, &Siemens_header.G09.Ide.HeaderVersion, create_char_element, LENGTH_HEADER_VERSION + 1}, +{0x0010, 0x0010, &Siemens_header.G10.Pat.PatientName, create_char_element, LENGTH_LABEL + 1}, +{0x0010, 0x0020, &Siemens_header.G10.Pat.PatientId, create_char_element, LENGTH_PATIENT_ID + 1}, +{0x0010, 0x0030, &Siemens_header.G10.Pat.PatientBirthdate, create_ds_date_t_element, 1}, +{0x0010, 0x0040, &Siemens_header.G10.Pat.PatientSex, create_sex_t_element, 1}, +{0x0010, 0x1010, &Siemens_header.G10.Pat.PatientAge, create_char_element, LENGTH_AGE + 1}, +{0x0010, 0x1030, &Siemens_header.G10.Pat.PatientWeight, create_long_element, 1}, +{0x0011, 0x1110, &Siemens_header.G11.Pat.RegistrationDate, create_ds_date_t_element, 1}, +{0x0011, 0x1111, &Siemens_header.G11.Pat.RegistrationTime, create_ds_time_t_element, 1}, +{0x0011, 0x1123, &Siemens_header.G11.Pat.UsedPatientWeight, create_long_element, 1}, +{0x0018, 0x0010, &Siemens_header.G18.Acq.Contrast, create_contrast_t_element, 1}, +{0x0018, 0x0050, &Siemens_header.G18.Acq.SliceThickness, create_double_element, 1}, +{0x0018, 0x0080, &Siemens_header.G18.Acq.RepetitionTime, create_double_element, 1}, +{0x0018, 0x0081, &Siemens_header.G18.Acq.EchoTime, create_double_element, 1}, +{0x0018, 0x0083, &Siemens_header.G18.Acq.NumberOfAverages, create_long_element, 1}, +{0x0018, 0x0084, &Siemens_header.G18.Acq.ImagingFrequency, create_double_element, 1}, +{0x0018, 0x0085, &Siemens_header.G18.Acq.ImagedNucleus, create_char_element, LENGTH_NUCLEUS + 1}, +{0x0018, 0x0086, &Siemens_header.G18.Acq.EchoNumber, create_long_element, 1}, +{0x0018, 0x0090, &Siemens_header.G18.Acq.DataCollectionDiameter, create_long_element, 1}, +{0x0018, 0x1000, &Siemens_header.G18.Acq.DeviceSerialNumber, create_char_element, LENGTH_LABEL + 1}, +{0x0018, 0x1020, &Siemens_header.G18.Acq.SoftwareVersion, create_char_element, LENGTH_SOFTWARE_VERSION + 1}, +{0x0018, 0x1200, &Siemens_header.G18.Acq.CalibrationDate, create_ds_date_t_element, 1}, +{0x0018, 0x1201, &Siemens_header.G18.Acq.CalibrationTime, create_ds_time_t_element, 1}, +{0x0018, 0x1250, &Siemens_header.G18.Acq.ReceivingCoil, create_char_element, LENGTH_LABEL + 1}, +{0x0018, 0x5100, &Siemens_header.G18.Acq.PatientPosition, create_patient_position_t_element, 1}, +{0x0019, 0x1010, &Siemens_header.G19.Acq1.CM.NetFrequency, create_long_element, 1}, +{0x0019, 0x1020, &Siemens_header.G19.Acq1.CM.MeasurementMode, create_measurement_mode_t_element, 1}, +{0x0019, 0x1030, &Siemens_header.G19.Acq1.CM.CalculationMode, create_calculation_mode_t_element, 1}, +{0x0019, 0x1050, &Siemens_header.G19.Acq1.CM.NoiseLevel, create_long_element, 1}, +{0x0019, 0x1060, &Siemens_header.G19.Acq1.CM.NumberOfDataBytes, create_long_element, 1}, +{0x0019, 0x1210, &Siemens_header.G19.Acq2.Mr.TotalMeasurementTime, create_double_element, 1}, +{0x0019, 0x1211, &Siemens_header.G19.Acq2.Mr.TotalMeasurementTimeCur, create_double_element, 1}, +{0x0019, 0x1212, &Siemens_header.G19.Acq2.Mr.StartDelayTime, create_double_element, 1}, +{0x0019, 0x1213, &Siemens_header.G19.Acq2.Mr.DwellTime, create_double_element, 1}, +{0x0019, 0x1214, &Siemens_header.G19.Acq2.Mr.NumberOfPhases, create_long_element, 1}, +{0x0019, 0x1220, &Siemens_header.G19.Acq2.Mr.NumberOfFourierLinesNominal, create_long_element, 1}, +{0x0019, 0x1221, &Siemens_header.G19.Acq2.Mr.NumberOfFourierLinesCurrent, create_long_element, 1}, +{0x0019, 0x1226, &Siemens_header.G19.Acq2.Mr.NumberOfFourierLinesAfterZero, create_long_element, 1}, +{0x0019, 0x1228, &Siemens_header.G19.Acq2.Mr.FirstMeasuredFourierLine, create_long_element, 1}, +{0x0019, 0x1230, &Siemens_header.G19.Acq2.Mr.AcquisitionColumns, create_long_element, 1}, +{0x0019, 0x1231, &Siemens_header.G19.Acq2.Mr.ReconstructionColumns, create_long_element, 1}, +{0x0019, 0x1250, &Siemens_header.G19.Acq2.Mr.NumberOfAverages, create_long_element, 1}, +{0x0019, 0x1260, &Siemens_header.G19.Acq2.Mr.FlipAngle, create_double_element, 1}, +{0x0019, 0x1270, &Siemens_header.G19.Acq2.Mr.NumberOfPrescans, create_long_element, 1}, +{0x0019, 0x1281, &Siemens_header.G19.Acq2.Mr.FilterTypeRawData, create_filter_type_t_element, 1}, +{0x0019, 0x1282, &Siemens_header.G19.Acq2.Mr.FilterParameterRawData, create_filter_parameter_t_element, 1}, +{0x0019, 0x1283, &Siemens_header.G19.Acq2.Mr.FilterTypeImageData, create_filter_type_image_t_element, 1}, +{0x0019, 0x1285, &Siemens_header.G19.Acq2.Mr.FilterTypePhaseCorrection, create_filter_type_t_element, 1}, +{0x0019, 0x1290, &Siemens_header.G19.Acq2.Mr.NumberOfSaturationRegions, create_long_element, 1}, +{0x0019, 0x1294, &Siemens_header.G19.Acq2.Mr.ImageRotationAngle, create_double_element, 1}, +{0x0019, 0x1298, &Siemens_header.G19.Acq2.Mr.CoilPosition, create_image_location_t_element, 1}, +{0x0019, 0x1412, &Siemens_header.G19.Acq3.Mr.MagneticFieldStrength, create_double_element, 1}, +{0x0019, 0x1414, &Siemens_header.G19.Acq3.Mr.ADCVoltage, create_double_element, 1}, +{0x0019, 0x1416, &Siemens_header.G19.Acq3.Mr.ADCOffset, create_double_element, 2}, +{0x0019, 0x1420, &Siemens_header.G19.Acq3.Mr.TransmitterAmplitude, create_double_element, 1}, +{0x0019, 0x1421, &Siemens_header.G19.Acq3.Mr.NumberOfTransmitterAmplitudes, create_long_element, 1}, +{0x0019, 0x1422, &Siemens_header.G19.Acq3.Mr.TransmitterAttenuator, create_double_element, 1}, +{0x0019, 0x1424, &Siemens_header.G19.Acq3.Mr.TransmitterCalibration, create_double_element, 1}, +{0x0019, 0x1426, &Siemens_header.G19.Acq3.Mr.TransmitterReference, create_double_element, 1}, +{0x0019, 0x1450, &Siemens_header.G19.Acq3.Mr.ReceiverTotalGain, create_double_element, 1}, +{0x0019, 0x1451, &Siemens_header.G19.Acq3.Mr.ReceiverAmplifierGain, create_double_element, 1}, +{0x0019, 0x1452, &Siemens_header.G19.Acq3.Mr.ReceiverPreamplifierGain, create_double_element, 1}, +{0x0019, 0x1454, &Siemens_header.G19.Acq3.Mr.ReceiverCableAttenuation, create_double_element, 1}, +{0x0019, 0x1455, &Siemens_header.G19.Acq3.Mr.ReceiverReferenceGain, create_double_element, 1}, +{0x0019, 0x1456, &Siemens_header.G19.Acq3.Mr.ReceiverFilterFrequency, create_long_element, 1}, +{0x0019, 0x1460, &Siemens_header.G19.Acq3.Mr.ReconstructionScaleFactor, create_double_element, 1}, +{0x0019, 0x1462, &Siemens_header.G19.Acq3.Mr.ReferenceScaleFactor, create_double_element, 1}, +{0x0019, 0x1470, &Siemens_header.G19.Acq3.Mr.PhaseGradientAmplitude, create_double_element, 1}, +{0x0019, 0x1471, &Siemens_header.G19.Acq3.Mr.ReadoutGradientAmplitude, create_double_element, 1}, +{0x0019, 0x1472, &Siemens_header.G19.Acq3.Mr.SelectionGradientAmplitude, create_double_element, 1}, +{0x0019, 0x1480, &Siemens_header.G19.Acq3.Mr.GradientDelayTime, create_gradient_delay_time_t_element, 1}, +{0x0019, 0x1482, &Siemens_header.G19.Acq3.Mr.TotalGradientDelayTime, create_double_element, 1}, +{0x0019, 0x1490, &Siemens_header.G19.Acq3.Mr.SensitivityCorrectionLabel, create_char_element, LENGTH_LABEL + 1}, +{0x0019, 0x14a0, &Siemens_header.G19.Acq3.Mr.RfWatchdogMask, create_long_element, 1}, +{0x0019, 0x14a2, &Siemens_header.G19.Acq3.Mr.RfPowerErrorIndicator, create_double_element, 1}, +{0x0019, 0x14a5, &Siemens_header.G19.Acq3.Mr.SarWholeBody, create_sar_sed_t_element, 1}, +{0x0019, 0x14a6, &Siemens_header.G19.Acq3.Mr.Sed, create_sar_sed_t_element, 1}, +{0x0019, 0x14b0, &Siemens_header.G19.Acq3.Mr.AdjustmentStatusMask, create_long_element, 1}, +{0x0019, 0x1510, &Siemens_header.G19.Acq4.CM.ParameterFileName, create_char_element, LENGTH_FILE_NAME + 1}, +{0x0019, 0x1511, &Siemens_header.G19.Acq4.CM.SequenceFileName, create_char_element, LENGTH_FILE_NAME + 1}, +{0x0019, 0x1512, &Siemens_header.G19.Acq4.CM.SequenceFileOwner, create_char_element, LENGTH_SEQUENCE_INFO + 1}, +{0x0019, 0x1513, &Siemens_header.G19.Acq4.CM.SequenceDescription, create_char_element, LENGTH_SEQUENCE_INFO + 1}, +{0x0020, 0x0010, &Siemens_header.G20.Rel.Study, create_long_element, 1}, +{0x0020, 0x0012, &Siemens_header.G20.Rel.Acquisition, create_long_element, 1}, +{0x0020, 0x0013, &Siemens_header.G20.Rel.Image, create_long_element, 1}, +{0x0020, 0x0050, &Siemens_header.G20.Rel.Location, create_long_element, 1}, +{0x0020, 0x0070, &Siemens_header.G20.Rel.ImageGeometryType, create_geometry_t_element, 1}, +{0x0020, 0x1001, &Siemens_header.G20.Rel.AcquisitionsInSeries, create_long_element, 1}, +{0x0020, 0x1020, &Siemens_header.G20.Rel.Reference, create_reference_t_element, 1}, +{0x0021, 0x1011, &Siemens_header.G21.Rel1.CM.Target, create_target_point_t_element, 1}, +{0x0021, 0x1020, &Siemens_header.G21.Rel1.CM.RoiMask, create_short_element, 1}, +{0x0021, 0x1120, &Siemens_header.G21.Rel1.CM.FoV, create_field_of_view_t_element, 1}, +{0x0021, 0x1122, &Siemens_header.G21.Rel1.CM.ImageMagnificationFactor, create_double_element, 1}, +{0x0021, 0x1130, &Siemens_header.G21.Rel1.CM.ViewDirection, create_view_direction_t_element, 1}, +{0x0021, 0x1132, &Siemens_header.G21.Rel1.CM.RestDirection, create_rest_direction_t_element, 1}, +{0x0021, 0x1160, &Siemens_header.G21.Rel1.CM.ImagePosition, create_image_location_t_element, 1}, +{0x0021, 0x1161, &Siemens_header.G21.Rel1.CM.ImageNormal, create_image_location_t_element, 1}, +{0x0021, 0x1163, &Siemens_header.G21.Rel1.CM.ImageDistance, create_double_element, 1}, +{0x0021, 0x1165, &Siemens_header.G21.Rel1.CM.ImagePositioningHistoryMask, create_short_element, 1}, +{0x0021, 0x116a, &Siemens_header.G21.Rel1.CM.ImageRow, create_image_location_t_element, 1}, +{0x0021, 0x116b, &Siemens_header.G21.Rel1.CM.ImageColumn, create_image_location_t_element, 1}, +{0x0021, 0x1170, &Siemens_header.G21.Rel1.CM.PatientOrientationSet1, create_patient_orientation_t_element, 1}, +{0x0021, 0x1171, &Siemens_header.G21.Rel1.CM.PatientOrientationSet2, create_patient_orientation_t_element, 1}, +{0x0021, 0x1180, &Siemens_header.G21.Rel1.CM.StudyName, create_char_element, LENGTH_LABEL + 1}, +{0x0021, 0x1182, &Siemens_header.G21.Rel1.CM.StudyType, create_study_type_t_element, 1}, +{0x0021, 0x1322, &Siemens_header.G21.Rel2.Mr.PhaseCorRowRec, create_long_element, 1}, +{0x0021, 0x1324, &Siemens_header.G21.Rel2.Mr.PhaseCorColRec, create_long_element, 1}, +{0x0021, 0x1330, &Siemens_header.G21.Rel2.Mr.NumberOf3DRawPartNom, create_long_element, 1}, +{0x0021, 0x1331, &Siemens_header.G21.Rel2.Mr.NumberOf3DRawPartCur, create_long_element, 1}, +{0x0021, 0x1334, &Siemens_header.G21.Rel2.Mr.NumberOf3DImaPart, create_long_element, 1}, +{0x0021, 0x1336, &Siemens_header.G21.Rel2.Mr.Actual3DImaPartNumber, create_long_element, 1}, +{0x0021, 0x1339, &Siemens_header.G21.Rel2.Mr.SlabThickness, create_double_element, 1}, +{0x0021, 0x1340, &Siemens_header.G21.Rel2.Mr.NumberOfSlicesNom, create_long_element, 1}, +{0x0021, 0x1341, &Siemens_header.G21.Rel2.Mr.NumberOfSlicesCur, create_long_element, 1}, +{0x0021, 0x1342, &Siemens_header.G21.Rel2.Mr.CurrentSliceNumber, create_long_element, 1}, +{0x0021, 0x1343, &Siemens_header.G21.Rel2.Mr.CurrentGroupNumber, create_long_element, 1}, +{0x0021, 0x1344, &Siemens_header.G21.Rel2.Mr.CurrentSliceDistanceFactor, create_double_element, 1}, +{0x0021, 0x134f, &Siemens_header.G21.Rel2.Mr.OrderOfSlices, create_order_of_slices_t_element, 1}, +{0x0021, 0x1356, &Siemens_header.G21.Rel2.Mr.RepetitionTime, create_double_element, 1}, +{0x0021, 0x1370, &Siemens_header.G21.Rel2.Mr.NumberOfEchoes, create_long_element, 1}, +{0x0028, 0x0005, &Siemens_header.G28.Pre.ImageDimension, create_short_element, 1}, +{0x0028, 0x0010, &Siemens_header.G28.Pre.Rows, create_short_element, 1}, +{0x0028, 0x0011, &Siemens_header.G28.Pre.Columns, create_short_element, 1}, +{0x0028, 0x0030, &Siemens_header.G28.Pre.PixelSize, create_pixel_size_t_element, 1}, +{0x0028, 0x0040, &Siemens_header.G28.Pre.ImageFormat, create_image_format_t_element, 1}, +{0x0028, 0x0060, &Siemens_header.G28.Pre.CompressionCode, create_compression_code_t_element, 1}, +{0x0028, 0x0100, &Siemens_header.G28.Pre.BitsAllocated, create_short_element, 1}, +{0x0028, 0x0101, &Siemens_header.G28.Pre.BitsStored, create_short_element, 1}, +{0x0028, 0x0102, &Siemens_header.G28.Pre.HighBit, create_short_element, 1}, +{0x0028, 0x0103, &Siemens_header.G28.Pre.PixelRepresentation, create_short_element, 1}, +{0x0028, 0x1050, &Siemens_header.G28.Pre.WindowCenter, create_windows_t_element, 1}, +{0x0028, 0x1051, &Siemens_header.G28.Pre.WindowWidth, create_windows_t_element, 1}, +{0x0028, 0x1052, &Siemens_header.G28.Pre.RescaleIntercept, create_long_element, 1}, +{0x0028, 0x1053, &Siemens_header.G28.Pre.RescaleSlope, create_long_element, 1}, +{0x0029, 0x1110, &Siemens_header.G29.Pre.WindowStyle, create_window_style_t_element, 1}, +{0x0029, 0x1120, &Siemens_header.G29.Pre.PixelQualityCode, create_pixel_quality_code_t_element, 1}, +{0x0029, 0x1152, &Siemens_header.G29.Pre.SortCode, create_long_element, 1}, +{0, 0, NULL, NULL, 0} +}; + + +/* Functions needed for this table: + + create_calculation_mode_t_element + create_char_element + create_compression_code_t_element + create_contrast_t_element + create_data_object_subtype_t_element + create_data_set_subtype_t_element + create_double_element + create_ds_date_t_element + create_ds_time_t_element + create_field_of_view_t_element + create_filter_parameter_t_element + create_filter_type_image_t_element + create_filter_type_t_element + create_gate_phase_t_element + create_geometry_t_element + create_gradient_delay_time_t_element + create_image_format_t_element + create_image_location_t_element + create_laterality_t_element + create_long_element + create_measurement_mode_t_element + create_modality_t_element + create_object_orientation_t_element + create_object_threshold_t_element + create_order_of_slices_t_element + create_patient_orientation_t_element + create_patient_phase_t_element + create_patient_position_t_element + create_patient_region_t_element + create_pixel_quality_code_t_element + create_pixel_quality_value_t_element + create_pixel_size_t_element + create_reference_t_element + create_rest_direction_t_element + create_rotation_direction_t_element + create_sar_sed_t_element + create_save_code_t_element + create_sex_t_element + create_short_element + create_storage_mode_t_element + create_study_type_t_element + create_target_point_t_element + create_view_direction_t_element + create_window_style_t_element + create_windows_t_element + + */ +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/STC_Common_Status.h @@ -0,0 +1,720 @@ +/*------------------------------------------------------------------------------* + * * + * File Name : STC_Common_Status.h * + * * + * Author : M. Rohbrecht CMS / SME 24 Tel. ... 9131 84 4790 * + * Author : C. Schaefer CMS / SME 24 Tel. ... 9131 84 4790 * + * Author : J.H. Siebert CMS / SME 24 Tel. ... 9131 84 4790 * + * * + * Description : Based on the definition of the structure of a status * + * file, the common (CMS) part can be found in the include * + * module `STC_Status.h`. * + * * + * The contents of this include file is in consistency * + * with the definition of the document 'Contents of * + * Status-, Configuration,- and Common Files' for the part * + * of 'Status File'. * + * * + * The informations for 'General Installation' and 'Fixed * + * Device Conditions' are found inhere. * + * * + *------------------------------------------------------------------------------* + * * + * Last Change : July 9th 1990 Creation * + * * + *------------------------------------------------------------------------------* + * * + * Last Change : August 6th 1990 Integration of exposure data * + * for company S.E.P.P. * + * * + *------------------------------------------------------------------------------* + * * + * Last Change : October 22nd 1990 Integration of device data and * + * additional camera data. * + * * + *------------------------------------------------------------------------------* + * * + * Last Change : December 11th 1990 Integration of modality * + * subtype (enum + variable). * + * * + *------------------------------------------------------------------------------* + * * + * Last Change : January 17th 1991 Integration of * + * - STC_Config_Hostname * + * - STC_Config_Consoles * + * - STC_Config_Host_Dbhost * + * - STC_Config_Host_Dbserver * + * - STC_Config_Host_Camera * + * * + *------------------------------------------------------------------------------* + * * + * Last Change : January 18th 1991 Integration of * + * - STC_Camera_1_Code * + * - STC_Camera_2_Code * + *------------------------------------------------------------------------------* + * * + * Last Change : February 15th 1991 by J. H. Siebert * + * Integration of * + * - Country Code for Japan * + * - __STC_Enum_UNDEFINED * + * * + *------------------------------------------------------------------------------* + * * + * Last Change : February 22nd 1991 by C. Schaefer * + * Integration of * + * - STC_Maxicam_Auto_Filmtransport* + * * + *------------------------------------------------------------------------------* + * * + * Last Change : March 12th 1991 by C. Schaefer * + * Integration of * + * - STC_Maxicam_Density * + * - STC_Maxicam_LUT * + * - STC_Num_Of_Cameras * + * * + *------------------------------------------------------------------------------* + * Last Change : (J.H. Siebert) * + * May 16th 1991 STC_PACS_NodeList introduced * + * June 25th 1991 STC_PACS_NodeFlag_t is now an int (before: enum), * + * __STC_PACS_NodeActiveSUN introduced * + * July, 9th 1991 Integration of STC_Customer, STC_City, STC_Destrict * + * Aug, 14th 1991 Integration of STC_dsvWinAutoCorr * + * * + *------------------------------------------------------------------------------* + * STC_Device_Net_Installed seems not to be used * + *------------------------------------------------------------------------------* + * * + * Last Change : Sep 9th 1991 by C. Schaefer * + * Integration of STC_VCR_Norm * + *------------------------------------------------------------------------------* + * Last Change : (J.H. Siebert) * + * November 4th 1991 New: * + * __STC_PACS_Country_Other, __STC_PACS_Country_UK * + * STC_DefaultHospitalInstall, * + * STC_CustomerHospitalInstall * + * STC_MIN_WINAUT_CORR, STC_MAX_WINAUT_CORR * + * STC_InstallCountry_t STC_Installation_Country * + * * + * 30Apr92 CHARM 205149 J.H.Siebert * + * Country and PACS code with additional countries * + *------------------------------------------------------------------------------*/ + +#ifndef BASIC_STC_DEFINE +#define BASIC_STC_DEFINE TRUE + +#ifndef __STC_Enum_UNDEFINED +#define __STC_Enum_UNDEFINED (-19222) +#endif + +#define CHAR char +#define UL unsigned long +#define US unsigned short +#define SS short +#define DO double +#define SL long int + + +/*------------------------------------------------------------------------------*/ + +#define __STC_BASIC_DATA_LEN 8192 + +/*------------------------------------------------------------------------------* + * For those variables of the status file where bitwise definitions are * + * needed, no enums are defined, but #define statements. * + *------------------------------------------------------------------------------*/ + +#define __STC_Type_None 0 +#define __STC_Type_MINICAM 1 +#define __STC_Type_MAXICAM 2 + +#define __STC_LISA_Present 1 +#define __STC_LISA_Not_Present 0 + +#define __STC_Plotter_Present 1 +#define __STC_Plotter_Not_Present 0 + +#define __STC_Input_Mouse 1 +#define __STC_Input_Trackball 2 + +#define __STC_HostnameLen 8 /*** Ex 16Bytes ?***/ + + +/******************\ +* Defines for PACS * +* * +\******************/ + +#define __STC_PACS_Country_None "000" +#define __STC_PACS_Country_Other "999" +#define __STC_PACS_Country_USA "001" +/* START CHANGE CHARM 205149 J.H.Siebert 30Apr92 */ +#define __STC_PACS_Country_France "033" +#define __STC_PACS_Country_Italy "039" +/* END CHANGE CHARM 205149 J.H.Siebert 30Apr92 */ +#define __STC_PACS_Country_Germany "049" +#define __STC_PACS_Country_Japan "081" +#define __STC_PACS_Country_UK __STC_PACS_Country_Other + +/*** NEW May, 16th 1991 J.H. Siebert***/ + +#define __STC_PACS_NUMOF_NODES 16 + +#define __STC_PACS_NODE_LOG_LEN __STC_HostnameLen +#define __STC_PACS_NODE_PHYS_LEN __STC_PACS_NODE_LOG_LEN + +/* defines of flags */ +typedef int STC_PACS_NodeFlag_t; +#define __STC_PACS_NodeNULL 0 +#define __STC_PACS_NodeActive 1 +#define __STC_PACS_NodeSUN 8 +#define __STC_PACS_NodeFlag_UNDEFINED __STC_Enum_UNDEFINED + +typedef enum +{ + __STC_PACS_NodeOwn = 0, + __STC_PACS_NodeRmt1, __STC_PACS_NodeRmt2, __STC_PACS_NodeRmt3, + __STC_PACS_NodeRmt4, __STC_PACS_NodeRmt5, __STC_PACS_NodeRmt6, + __STC_PACS_NodeRmt7, __STC_PACS_NodeRmt8, __STC_PACS_NodeRmt9, + __STC_PACS_NodeRmt10, __STC_PACS_NodeRmt11, __STC_PACS_NodeRmt12, + __STC_PACS_NodeRmt13, __STC_PACS_NodeRmt14, __STC_PACS_NodeRmt15, + __STC_PACS_Node_UNDEFINED = __STC_Enum_UNDEFINED +} STC_PACS_NodeNum_t; + + +typedef struct +{ + STC_PACS_NodeFlag_t flag; /* PACSNET configurationbits */ + char log[__STC_PACS_NODE_LOG_LEN+1]; /* logical name */ +/*** char log_ext[65-__STC_PACS_NODE_LOG_LEN];***/ /* logical name extension*/ + + char phys[__STC_PACS_NODE_PHYS_LEN+1]; /* physical name */ +/*** char phys_ext[65-__STC_PACS_NODE_PHYS_LEN];***/ /* physical name extension*/ + +} STC_PACS_node_t; + +typedef struct +{ + STC_PACS_node_t node[__STC_PACS_NUMOF_NODES]; +} STC_PACS_nodelist_t; + + +/*****************************************************************/ + + +#define __STC_Net_Present 1 +#define __STC_Net_Not_Present 0 + +/*------------------------------------------------------------------------------* + * * + * In case of a defined and limited value range enum definitions are used. * + * * + *------------------------------------------------------------------------------*/ + + typedef enum + { + __STC_Modality_CT = 1, + __STC_Modality_MR, + __STC_Modality_UNDEFINED = __STC_Enum_UNDEFINED + + } STC_Modality_t; + + + typedef enum + { + __STC_Modality_Sub_M2 = 0, + __STC_Modality_Sub_P8 = 1, + __STC_Modality_Sub_UNDEFINED = __STC_Enum_UNDEFINED + + } STC_Modality_Sub_t; + + +/*------------------------------------------------------------------------------*/ + + typedef enum + { + __STC_Weight_Kg = 1, + __STC_Weight_lbs, + __STC_Weight_UNDEFINED = __STC_Enum_UNDEFINED + + } STC_Weight_t; + +/*------------------------------------------------------------------------------*/ + + typedef enum + { + __STC_Main_Console = 1, + __STC_Satellite_Console, + __STC_Console_UNDEFINED = __STC_Enum_UNDEFINED + + } STC_Console_t; + +/*------------------------------------------------------------------------------*/ + + typedef enum + { + __STC_Language_English = 1, + __STC_Language_German, + __STC_Language_UNDEFINED = __STC_Enum_UNDEFINED + + } STC_Language_t; + +/*------------------------------------------------------------------------------*/ + + typedef enum + { + __STC_Device_Type_Unknown = 1, + __STC_Device_Type_Magnetic_Disk, + __STC_Device_Type_Magnetic_Tape, + __STC_Device_Type_Optical_Disk, + __STC_Device_Type_UNDEFINED = __STC_Enum_UNDEFINED + + } STC_Device_t; + +/*------------------------------------------------------------------------------*/ + + typedef enum + { + __STC_Net_Installed = 1, + __STC_Net_Not_Installed, + __STC_Net_UNDEFINED = __STC_Enum_UNDEFINED + + } STC_Net_t; + +/*------------------------------------------------------------------------------*/ + + typedef enum + { + __STC_Minicam_Matrix_512 = 1, + __STC_Minicam_Matrix_484, + __STC_Minicam_Matrix_UNDEFINED = __STC_Enum_UNDEFINED + + } STC_Minicam_Matrix_t; + +/*------------------------------------------------------------------------------*/ + + typedef enum + { + __STC_VCR_NORM_PAL = 1, + __STC_VCR_NORM_NTSC, + __STC_VCR_NORM_UNDEFINED = __STC_Enum_UNDEFINED + + } STC_VCR_Norm_t; + +/*------------------------------------------------------------------------------*/ + + typedef enum + { + __STC_List_Lokalizer_F = 1, + __STC_List_Lokalizer_A, + __STC_List_Lokalizer_N, + __STC_List_Lokalizer_UNDEFINED = __STC_Enum_UNDEFINED + + } STC_List_Lokalizer_Default_t; + + typedef enum + { + __STC_NULL_VERSION = 0, + __STC_91E5_VERSION, + __STC_91E7_VERSION, + __STC_91E11_VERSION, + __STC_Version_UNDEFINED = __STC_Enum_UNDEFINED + + } STC_Version_t; + +#define __STC_INFO_VERSION_LEN 32 + + typedef struct + { + int flag; + char version[__STC_INFO_VERSION_LEN+1]; + } STC_Info_t; + + + typedef struct + { + char space[__STC_BASIC_DATA_LEN - + (sizeof(STC_Version_t) + sizeof(STC_Info_t))]; + STC_Info_t info; + STC_Version_t Version; + } structInfo_t; + +#define __STC_CustomerLen 26 +#define __STC_CityLen 26 +#define __STC_DestrictLen 26 + +#define __STC_InstallationLen 26 +#define __STC_CustomerHospitalNUM 3 + +/*********************************\ +* * +* MR requirement (ui_window, ...) * +* * +\*********************************/ + +#define STC_MIN_WINAUT_CORR 0 +#define STC_MAX_WINAUT_CORR 5 + + typedef struct + { + int center; + int width; + int part; + } STC_dsvWinAutoCorr_t; + + +/***************************\ +* * +* MR requirement (M. Gress) * +* * +\***************************/ + +/* START CHANGE CHARM 205149 J.H.Siebert 30Apr92 */ + +typedef enum +{ + __STC_InstallCountry_USA = 1, + __STC_InstallCountry_GERMANY, + __STC_InstallCountry_UK, + __STC_InstallCountry_JAPAN, + __STC_InstallCountry_FRANCE, + __STC_InstallCountry_ITALY, + __STC_InstallCountry_OTHERS = 99, + __STC_InstallCountry_UNDEFINED = __STC_Enum_UNDEFINED +} STC_InstallCountry_t; + +/* END CHANGE CHARM 205149 J.H.Siebert 30Apr92 */ + + + +/*------------------------------------------------------------------------------* + * * + * Union and structure definition of the 8 KByte of common status file * + * data * + * * + *------------------------------------------------------------------------------*/ + +union __STC_BASIC_DATA +{ + char space [__STC_BASIC_DATA_LEN]; /* 8 blocks for map */ + + structInfo_t structInfo; + + struct blablabla /* Name needed only for STC tool for */ + /* naming within the debugger - Dummy */ + { + +/*------------------------------------------------------------------------------* + * * + * Manufacturer and installation * + * * + *------------------------------------------------------------------------------*/ + + + CHAR STC_Manufacturer [8+1]; /* Name of Manufacturer, e.g. */ + /* SIEMENS. */ + + CHAR STC_Manufacturer_Model [26+1]; /* Manufacturer's model, e.g. */ + /* SOMATOM, MAGNETOM */ + + CHAR STC_Installation_Name [__STC_InstallationLen+1]; + /* Name of hospital, instal. */ + + CHAR STC_Software_Version [8+1]; /* Software version for image */ + /* text */ + + UL STC_Int_Software_Version; /* Internal software version */ + + +/*------------------------------------------------------------------------------* + * PACS data * + *------------------------------------------------------------------------------*/ + + CHAR STC_PACS_Country_Code [3+1]; /* Country code for PACS */ + /* installation */ + + CHAR STC_PACS_Identification [3+1]; /* Manufacturer PACS identifi- */ + /* cation */ + + CHAR STC_PACS_Modality [4+1]; /* Modality PACS identification */ + +#ifndef VERSION_NO_PACS + STC_PACS_nodelist_t STC_PACS_NodeList; /* List of nodes in net */ +#endif + + UL STC_Serial_Number; /* Serial number of installation*/ + + UL STC_System_Code; /* System Code - to be defined */ + + STC_Modality_t STC_Modality; /* System Modality */ + + STC_Language_t STC_System_Language; /* User language */ + + STC_Weight_t STC_Weight_System; /* Weight system */ + + UL STC_Net_Frequency; /* Net frequency in Hz */ + + STC_Console_t STC_Console_Type; /* Either main or satellite */ + + +/*------------------------------------------------------------------------------* + * * + * Camera data * + * * + *------------------------------------------------------------------------------*/ + + + UL STC_Camera_Display_Birthday; /* Text superimposing of birth- */ + /* day */ + /* Yes = 1 */ + /* No = 0 */ + + UL STC_Camera_1_Type; /* Type of instal. camera no. 1 */ + /* <no camera> = 0 */ + /* MINICAM = 1 */ + /* MAXICAM = 2 */ + /* . . . */ + + UL STC_Camera_2_Type; /* Type of instal. camera no. 2 */ + /* <no camera> = 0 */ + /* MINICAM = 1 */ + /* MAXICAM = 2 */ + /* . . . */ + + UL STC_Camera_1_Code; /* Code for type of instal. */ + /* camera no. 1 */ + /* <no camera> = 0 */ + /* DIGICAM = 1 */ + /* . . . */ + + UL STC_Camera_2_Code; /* Code for type of instal. */ + /* camera no. 2 */ + /* <no camera> = 0 */ + /* DIGICAM = 1 */ + /* . . . */ + + CHAR STC_Camera_1_Name [40+1]; /* Name of camera no. 1 */ + + CHAR STC_Camera_2_Name [40+1]; /* Name of camera no. 2 */ + + UL STC_Camera_1_Copy_Range; /* Copy range for exposure */ + + UL STC_Camera_1_Low_Level_Timer; /* Low level timer */ + + UL STC_Camera_1_High_Level_Timer; /* High level timer */ + + UL STC_Camera_2_Copy_Range; /* Copy range for exposure */ + + UL STC_Camera_2_Low_Level_Timer; /* Low level timer */ + + UL STC_Camera_2_High_Level_Timer; /* High level timer */ + + +/*------------------------------------------------------------------------------* + * * + * Special entries for MINICAM only * + * * + * A maximum of 100 different film formats is possible. The actual valid * + * number of formats is inquired within the installation. Only those ele- * + * ments are supplied during instalation. * + * * + *------------------------------------------------------------------------------*/ + + + UL STC_Minicam_Num_of_Formats; /* Numver of valid formats */ + + + UL STC_Minicam_Filmsize [100]; + + UL STC_Minicam_Format [100]; + + UL STC_Minicam_Format_Code [100]; + + US STC_Minicam_Row [100]; + + US STC_Minicam_Col [100]; + + STC_Minicam_Matrix_t STC_Minicam_Matrixsize; + + +/*------------------------------------------------------------------------------* + * * + * Lokalizer components * + * * + *------------------------------------------------------------------------------*/ + + + STC_List_Lokalizer_Default_t STC_List_Lokalizer_Default; + + +/*------------------------------------------------------------------------------* + * * + * SMIV hardware components * + * * + *------------------------------------------------------------------------------*/ + + + UL STC_LISA_Present; /* Is component LISA installed */ + /* Yes = 1 */ + /* No = 0 */ + /* . . . */ + + UL STC_Plotter_Present; /* Is a plotter installed */ + /* Yes = 1 */ + /* No = 0 */ + /* . . . */ + + + UL STC_SIAM_Memsize; /* Number of MBytes for SIAM */ + + UL STC_Num_of_Mem_Board; /* Number of boards for additi- */ + /* onal imager memory */ + + UL STC_Add_Mem_Size; /* Number of MByte per board */ + /* for additional imager memory */ + + +/*------------------------------------------------------------------------------* + * * + * Graphical input medium * + * * + *------------------------------------------------------------------------------*/ + + + UL STC_Graphic_Input_Type; /* Kind of graphical input : */ + /* Mouse = 1 */ + /* Trackball = 2 */ + /* . . . */ + + UL STC_Graphic_Input_Character [3]; /* Factors for hysteresis */ + /* curve oF sensitivity of */ + /* graphical input */ + + +/*------------------------------------------------------------------------------* + * * + * Patient storage device data * + * * + *------------------------------------------------------------------------------*/ + + + STC_Device_t STC_Device_Type [10]; /* Kind of device : */ + /* Magnetic Disk */ + /* Magnetic Tape */ + /* Optical Disk */ + /* . . . */ + + CHAR STC_Device_Manufacturer_Name [10] [19+1]; + /* Name of Device manufacturer */ + + UL STC_Device_Net_Installed; /* Is a Net installed : */ + /* Yes = 1 */ + /* No = 0 */ + /* . . . */ + + +/*------------------------------------------------------------------------------* + * * + * Installation status encoding * + * * + *------------------------------------------------------------------------------*/ + + + UL STC_Installation_Status; /* Encoded status of already */ + /* done installation steps (e.g.*/ + /* first installation, partial */ + /* installation, etc.) */ + + +/*------------------------------------------------------------------------------* + * * + * Modality sub-type. This field contains informations about * + * variants of the modality described in STC_Modality (e.g. * + * STC_Modality == MR and STC_Modality_Sub == STC_Modality_Sub_P8). * + * This field can be used by both, CT and MR modalities. * + * * + * * + *------------------------------------------------------------------------------*/ + + STC_Modality_Sub_t STC_Modality_Sub; /* System Modality + Sub-type */ + + +/*------------------------------------------------------------------------------* + * * + * Host configuration data. * + * * + * * + *------------------------------------------------------------------------------*/ + + CHAR STC_Config_Hostname [__STC_HostnameLen+1]; /* Name of own host */ + + CHAR STC_Config_Consoles [8][__STC_HostnameLen+1]; /* Name of other hosts */ + + CHAR STC_Config_Host_Dbhost [__STC_HostnameLen+1]; /* Name of database host */ + + CHAR STC_Config_Host_Dbserver [__STC_HostnameLen+1]; /* Name of database server */ + + CHAR STC_Config_Host_Camera [__STC_HostnameLen+1]; /* Name of camera host */ + + +/*------------------------------------------------------------------------------* + * Special entries for MAXICAM only * + *------------------------------------------------------------------------------*/ + + UL STC_Maxicam_Auto_Filmtransport; /* */ + UL STC_Maxicam_Density; /* */ + UL STC_Maxicam_LUT; /* */ + + UL STC_Num_Of_Cameras; /* */ + +/*------------------------------------------------------------------------------* + * CT requirement * + *------------------------------------------------------------------------------*/ + + CHAR STC_Customer [__STC_CustomerLen+1]; /* Name of customer */ + CHAR STC_City [__STC_CityLen+1]; /* Name of city */ + CHAR STC_Destrict [__STC_DestrictLen+1]; /* Name of destrict */ + + +/*------------------------------------------------------------------------------* + * MR requirement * + *------------------------------------------------------------------------------*/ + + STC_dsvWinAutoCorr_t STC_dsvWinAutoCorr; + +/*------------------------------------------------------------------------------* + * MR requirement * + *------------------------------------------------------------------------------*/ + + STC_VCR_Norm_t STC_VCR_Norm; /* Videonorm of Camera */ + +/*------------------------------------------------------------------------------* + * New requirement * + *------------------------------------------------------------------------------*/ + + STC_InstallCountry_t STC_Installation_Country; + + + CHAR STC_DefaultHospitalInstall[__STC_InstallationLen+1]; + /* Name of default hospital */ + CHAR STC_CustomerHospitalInstall [__STC_CustomerHospitalNUM] [__STC_InstallationLen+1]; + /* Name of customer hospital, + via ui_text installed */ + + + } s; +}; + +/*------------------------------------------------------------------------------*/ + +struct STC_COMMON +{ + union __STC_BASIC_DATA Basic; +}; + +/*------------------------------------------------------------------------------*/ + +#endif + +/*------------------------------------------------------------------------------*/ +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_date.h @@ -0,0 +1,109 @@ +/*[- HEADER FILE -------------------------------------------------------------------------*/ +/* + Name: ds_date.h + + Description: The header file defines the basic types, constants and macros for the + data set library part "ds_date": date and time converting handling. + + + Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 +*/ +/*]-----------------------------------------------------------------------------------------*/ + +#ifndef DS_DATE +#define DS_DATE + +/* DECLARATION: types */ + +typedef struct ds_date_tag +{ + long Year; /* four digits e.g. 1989 */ + long Month; /* 1 - 12 */ + long Day; /* 1 - 31 */ +} ds_date_t; + + +typedef struct ds_time_tag +{ + long Hour; /* 0 - 23 */ + long Minute; /* 0 - 59 */ + long Second; /* 0 - 59 */ + long Fraction; /* 0 - 999 */ +} ds_time_t; + + +typedef struct date_position_table_tag +{ + long PreBegin; /* number of first character of + date string part pre-number; + always 0 */ + long PreDelimiter; /* number of pre-delimiter + character */ + long MonthBegin; /* number of first character of + date string part month */ + long PostDelimiter; /* number of post-delimiter + character */ + long PostBegin; /* number of first character of + date string part post-number */ +} date_position_table_t; + + +typedef struct time_position_table_tag +{ + long HourBegin; /* number of first character of + time string part hour; always 0 */ + long HourDelimiter; /* number of hour delimiter + character */ + long MinuteBegin; /* number of first character of + time string part minute */ + long MinuteDelimiter; /* number of minute delimiter + character */ + long SecondBegin; /* number of first character of + time string part second */ + long SecondDelimiter; /* number of second delimiter + character */ + long FractionBegin; /* number of first character of + time string part fraction */ + +} time_position_table_t; + + +typedef union ds_pdp_time_tag +{ + short ValueAsShort[2]; + long ValueAsLong; +} ds_pdp_time_t; + + + +/* PRECOMPILER: common constants */ +#define CENTURY 1900L +#define DEFAULT 0L +#define MAX_ALLOWED_DATE_STRING_LENGTH 20L +#define MAX_ALLOWED_TIME_STRING_LENGTH 15L +#define WITH_FRACTION 2L +#define WITH_SECOND 1L + + +/* PRECOMPILER: range constants */ +#define DS_A_DAY_IN_MS (24.0 * 60.0 * 60.0 * 1000.0) +#define DS_A_HOUR_IN_MS (60 * 60 * 1000) +#define DS_A_MINUTE_IN_MS (60 * 1000) +#define DS_A_SECOND_IN_MS (1000) + +#define DS_A_HOUR_IN_S (60 * 60) +#define DS_A_MINUTE_IN_S (60) + + +/* PRECOMPILER: delimiter constants */ +#define COMMON_DATE_DELIMITER_SET ".-/:" +#define DB_DATE_DELIMITER_SET "-" +#define NEMA_DATE_DELIMITER_SET "." +#define VMS_DATE_DELIMITER_SET "-" +#define COMMON_TIME_DELIMITER_SET ":.-/" +#define DB_TIME_DELIMITER_SET ":" +#define NEMA_TIME_DELIMITER_SET ":." +#define VMS_TIME_DELIMITER_SET ":." + + +#endif
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_functions.h @@ -0,0 +1,560 @@ +/*[- HEADER FILE -------------------------------------------------------------------------*/ +/* + Name: ds_functions.h + + Description: The header file defines all functions declared by Data Set Library. + + To generate an actual function list delete the current list and copy the + output of following command as statements in paragraph 'Sequence: + define functions'. + + % grep "Name:" ds_*.c | grep "()" | awk '{print " long " $3 ";"}' | sort + + + To generate an actual modul list use the command: + + % ls -l ds_*.c | awk '{print " " $8}' + + + To generate an actual header file list use the command: + + % ls -l ds_*.h | awk '{print " " $8}' + + + Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 +*/ +/*]-----------------------------------------------------------------------------------------*/ + +/* NOTE: list of data set library source code moduls */ +/* The first 15 characters must be unique. The unix "ar" utility works only with the first + 15 characters. */ +/* + ds_456789012345----0----5----0 + ds_adapt.c + ds_build_acr.c + ds_build_area.c + ds_build_s1.c + ds_build_s2.c + ds_build_up.c + ds_collect.c + ds_convert.c + ds_date.c + ds_db.c + ds_fill_acr.c + ds_fill_area.c + ds_fill_s1.c + ds_fill_s2.c + ds_fill_sha.c + ds_format.c + ds_get1.c + ds_get2.c + ds_head.c + ds_info.c + ds_interface.c + ds_mani.c + ds_mixed.c + ds_num1.c + ds_num1_xyz.c + ds_num2.c + ds_num2_xyz.c + ds_parser.c + ds_post.c + ds_pre.c + ds_separate.c + ds_set2.c + ds_set_s.c + ds_som0_b1.c + ds_som0_b2.c + ds_som0_b3.c + ds_som0_b4.c + ds_som0_b5.c + ds_som0_s.c + ds_som1.c + ds_split.c + ds_string.c + ds_text.c + ds_top.c + ds_transform.c + ds_vector.c + ds_xyz.c + ds_456789012345----0----5----0 +*/ + + +/* NOTE: list of data set library header files */ +/* + ds_date.h + ds_functions.h + ds_head_acr_groups_types.h + ds_head_basic_types.h + ds_head_constants.h + ds_head_image_text_type.h + ds_head_numaris1.h + ds_head_numaris2.h + ds_head_shadow_groups_types.h + ds_head_somaris0.h + ds_head_somaris1.h + ds_head_type.h + ds_include_files.h + ds_mani.h + ds_messages.h + ds_transformation.h + ds_transformation_control.h +*/ + + +#ifndef DS_FUNCTIONS +#define DS_FUNCTIONS + +/* DECLARATION: define data set library functions */ + + /* DECLARATION: define integer data set library functions */ + long ds_adapt_double(); + long ds_adapt_int(); + long ds_adapt_string(); + long ds_build_up_char_buf(); + long ds_build_up_cms_nema_data_set(); + long ds_build_up_data_groups(); + long ds_build_up_graphic_groups(); + long ds_build_up_group0008(); + long ds_build_up_group0009(); + long ds_build_up_group0010(); + long ds_build_up_group0011(); + long ds_build_up_group0013(); + long ds_build_up_group0018(); + long ds_build_up_group0019(); + long ds_build_up_group0020(); + long ds_build_up_group0021(); + long ds_build_up_group0028(); + long ds_build_up_group0029(); + long ds_build_up_group0051(); + long ds_build_up_group600x(); + long ds_build_up_group6021(); + long ds_build_up_group7FE0(); + long ds_build_up_group7FE1(); + long ds_build_up_header_groups(); + long ds_build_up_header_groups(); + long ds_build_up_image_text_item_value(); + long ds_build_up_key(); + long ds_build_up_long_buf(); + long ds_build_up_short_buf(); + long ds_build_up_som00008(); + long ds_build_up_som00009(); + long ds_build_up_som00010(); + long ds_build_up_som00011(); + long ds_build_up_som00018(); + long ds_build_up_som00020(); + long ds_build_up_som00021(); + long ds_build_up_som00028(); + long ds_build_up_som0600x(); + long ds_build_up_som07001(); + long ds_build_up_som07003(); + long ds_build_up_som07005(); + long ds_build_up_som0_blocked_data_set(); + long ds_build_up_som0_framed_data_set(); + long ds_collect_calculation_mode(); + long ds_collect_cardiac_code(); + long ds_collect_compression_code(); + long ds_collect_contrast(); + long ds_collect_data_object_subtype(); + long ds_collect_data_object_type(); + long ds_collect_data_set_subtype(); + long ds_collect_data_set_type(); + long ds_collect_date(); + long ds_collect_filter_type(); + long ds_collect_filter_type_image(); + long ds_collect_gate_phase(); + long ds_collect_ident(); + long ds_collect_image_format(); + long ds_collect_image_geometry_type(); + long ds_collect_image_location(); + long ds_collect_imaged_nucleus(); + long ds_collect_integer_number(); + long ds_collect_laterality(); + long ds_collect_measurement_mode(); + long ds_collect_modality(); + long ds_collect_order_of_slices(); + long ds_collect_patient_phase(); + long ds_collect_patient_position(); + long ds_collect_patient_region(); + long ds_collect_pixel_quality_mode(); + long ds_collect_procedure_description(); + long ds_collect_real_number(); + long ds_collect_rest_direction(); + long ds_collect_rotation_direction(); + long ds_collect_save_code(); + long ds_collect_sex(); + long ds_collect_storage_mode(); + long ds_collect_study_type(); + long ds_collect_time(); + long ds_collect_uid(); + long ds_collect_view_direction(); + long ds_collect_window_style(); + long ds_convert_double(); + long ds_convert_int(); + long ds_convert_string(); + long ds_date_check_cms(); + long ds_date_cms_to_display(); + long ds_date_cms_to_ingres(); + long ds_date_cms_to_nema(); + long ds_date_cms_to_rt11(); + long ds_date_cms_to_spi(); + long ds_date_days_of_month(); + long ds_date_days_of_year(); + long ds_date_display_to_cms(); + long ds_date_get(); + long ds_date_get_age(); + long ds_date_get_new(); + long ds_date_ingres_to_cms(); + long ds_date_nema_to_cms(); + long ds_date_pdp_to_cms(); + long ds_date_string_to_cms(); + long ds_date_vax_to_cms(); + long ds_date_vms_to_cms(); + long ds_db_set_comdiainfo(); + long ds_db_set_imainfo(); + long ds_db_set_patinfo(); + long ds_db_set_stuinfo(); + long ds_db_use_comdiainfo(); + long ds_db_use_patinfo(); + long ds_file_control(); + long ds_fill_basic_structs(); + long ds_fill_binary_data(); + long ds_fill_binary_g7FE0(); + long ds_fill_binary_g7FE1(); + long ds_fill_common_black_image_data(); + long ds_fill_data_structs(); + long ds_fill_default_data(); + long ds_fill_fix_data(); + long ds_fill_graphic_structs(); + long ds_fill_image_g7FE0(); + long ds_fill_image_graphic(); + long ds_fill_image_text(); + long ds_fill_patient_data(); + long ds_fill_struct_g08(); + long ds_fill_struct_g09(); + long ds_fill_struct_g09Cms(); + long ds_fill_struct_g09Lab(); + long ds_fill_struct_g09Spi(); + long ds_fill_struct_g10(); + long ds_fill_struct_g11(); + long ds_fill_struct_g11Cms(); + long ds_fill_struct_g11Spi(); + long ds_fill_struct_g13(); + long ds_fill_struct_g13Cms(); + long ds_fill_struct_g18(); + long ds_fill_struct_g19(); + long ds_fill_struct_g19Acqu(); + long ds_fill_struct_g19Cms(); + long ds_fill_struct_g19Ct(); + long ds_fill_struct_g19CtCoAd(); + long ds_fill_struct_g19Mr(); + long ds_fill_struct_g19MrCoAd(); + long ds_fill_struct_g20(); + long ds_fill_struct_g21(); + long ds_fill_struct_g21Cms(); + long ds_fill_struct_g21Ct(); + long ds_fill_struct_g21CtRaw(); + long ds_fill_struct_g21Med(); + long ds_fill_struct_g21Mr(); + long ds_fill_struct_g21MrRaw(); + long ds_fill_struct_g28(); + long ds_fill_struct_g29(); + long ds_fill_struct_g29Cms(); + long ds_fill_struct_g29Spi(); + long ds_fill_struct_g51(); + long ds_fill_struct_g6021(); + long ds_fill_test_data(); + long ds_finally_interface_status_update(); + long ds_generate_image_text(); + long ds_get_data_set_owner(); + long ds_get_data_set_type(); + long ds_get_g09_med_parameter(); + long ds_get_subgroup_owner_code(); + long ds_get_swap_mode(); + long ds_get_vax_char_buf(); + long ds_get_vax_integer2_buf(); + long ds_get_vax_integer4_buf(); + long ds_get_vax_real_buf(); + long ds_get_vax_skip_gap(); + long ds_hea_slice_1_2_single(); + long ds_info_get_xxx_version(); + long ds_info_show_versions(); + long ds_init_error_handling(); + long ds_init_header(); + long ds_initialize_nema_parser(); + long ds_mani_gen_rot_matrix(); + long ds_mani_image_magnify(); + long ds_mani_image_mirror(); + long ds_mani_image_rotate_90(); + long ds_mani_mult_matrix_matrix(); + long ds_mani_mult_matrix_scalar(); + long ds_mani_patient_modify(); + long ds_mani_set_id_matrix(); + long ds_mani_transform_vector(); + long ds_mes_calc_prs(); + long ds_modify_patient_data(); + long ds_nema_check_if_transformation_possible(); + long ds_num1_blocked_fill_basic_structs(); + long ds_num1_blocked_fill_data_structs(); + long ds_num1_framed_fill_basic_structs(); + long ds_num1_framed_fill_data_structs(); + long ds_num1_get_cms_sep_basic_data(); + long ds_num1_get_pixel_quality_code(); + long ds_num1_get_pixel_quality_mode(); + long ds_num1_num1_to_cms(); + long ds_num1_vax_to_sun(); + long ds_num2_fill_basic_structs(); + long ds_num2_fill_data_structs(); + long ds_num2_get_cms_sep_basic_data(); + long ds_num2_get_pixel_quality_code(); + long ds_num2_get_pixel_quality_mode(); + long ds_num2_num2_to_cms(); + long ds_num2_vax_to_sun(); + long ds_parser_error_handling(); + long ds_post_build_up_name(); + long ds_post_build_up_record(); + long ds_post_separate(); + long ds_pre_allocate_data_areas_b1(); + long ds_pre_allocate_data_areas_b2(); + long ds_pre_allocate_data_areas_s(); + long ds_pre_build_up_name(); + long ds_pre_build_up_record(); + long ds_pre_determine_control_status_b1(); + long ds_pre_determine_control_status_b2(); + long ds_pre_determine_control_status_s(); + long ds_pre_determine_data_area_lengths_b1(); + long ds_pre_determine_data_area_lengths_b2(); + long ds_pre_determine_data_area_lengths_s(); + long ds_pre_initialize(); + long ds_pre_separation(); + long ds_reset_nema_parser(); + long ds_return_control(); + long ds_separate_byte_buf(); + long ds_separate_char_buf(); + long ds_separate_cms_blocked_data_set(); + long ds_separate_common_data_set(); + long ds_separate_given_item(); + long ds_separate_group_600x(); + long ds_separate_key(); + long ds_separate_long_buf(); + long ds_separate_nema_data_set(); + long ds_separate_num1_blocked_data_set(); + long ds_separate_num1_framed_data_set(); + long ds_separate_num2_data_set(); + long ds_separate_short_buf(); + long ds_separate_som0_blocked_data_set(); + long ds_separate_som0_framed_data_set(); + long ds_separate_som_blocked_data_set(); + long ds_separate_som_framed_data_set(); + long ds_set_pdp_byte_buf(); + long ds_set_pdp_char_buf(); + long ds_set_pdp_fill_gap(); + long ds_set_pdp_integer2_buf(); + long ds_set_pdp_integer4_buf(); + long ds_set_struct_g09Cms(); + long ds_set_struct_g09Lab(); + long ds_set_struct_g09Spi(); + long ds_set_struct_g11Cms(); + long ds_set_struct_g11Spi(); + long ds_set_struct_g19Acqu(); + long ds_set_struct_g19Cms(); + long ds_set_struct_g19Ct(); + long ds_set_struct_g19CtCoAd(); + long ds_set_struct_g19Mr(); + long ds_set_struct_g19MrCoAd(); + long ds_set_struct_g21Cms(); + long ds_set_struct_g21Ct(); + long ds_set_struct_g21CtRaw(); + long ds_set_struct_g21Med(); + long ds_set_struct_g21Mr(); + long ds_set_struct_g21MrRaw(); + long ds_set_struct_g29Cms(); + long ds_set_struct_g29Spi(); + long ds_show_help_text(); + long ds_simulate_ct_mr_status_file(); + long ds_som0_blocked_build_up_basic_structs(); + long ds_som0_blocked_build_up_data_structs(); + long ds_som0_blocked_fill_basic_structs(); + long ds_som0_blocked_fill_data_structs(); + long ds_som0_check_if_transformation_possible(); + long ds_som0_cms_to_som0(); + long ds_som0_fill_bild_text(); + long ds_som0_fill_block_0(); + long ds_som0_framed_build_up_basic_groups(); + long ds_som0_framed_build_up_data_groups(); + long ds_som0_framed_fill_basic_structs(); + long ds_som0_framed_fill_data_structs(); + long ds_som0_get_cms_sep_basic_data(); + long ds_som0_get_patient_orientation_vector(); + long ds_som0_pdp_to_sun(); + long ds_som0_print_build_up_protocol(); + long ds_som0_som0_to_cms(); + long ds_som0_sun_to_pdp(); + long ds_som_blocked_fill_basic_structs(); + long ds_som_blocked_fill_data_structs(); + long ds_som_framed_fill_basic_structs(); + long ds_som_framed_fill_data_structs(); + long ds_som_get_cms_sep_basic_data(); + long ds_som_get_patient_position(); + long ds_som_get_rest_direction(); + long ds_som_som_to_cms(); + long ds_som_vax_to_sun(); + long ds_split_calculation_mode(); + long ds_split_cardiac_code(); + long ds_split_compression_code(); + long ds_split_contrast(); + long ds_split_data_object_subtype(); + long ds_split_data_set_subtype(); + long ds_split_filter_type(); + long ds_split_filter_type_image(); + long ds_split_gate_phase(); + long ds_split_ident(); + long ds_split_image_format(); + long ds_split_image_geometry_type(); + long ds_split_imaged_nucleus(); + long ds_split_integer_number(); + long ds_split_laterality(); + long ds_split_measurement_mode(); + long ds_split_modality(); + long ds_split_order_of_slices(); + long ds_split_patient_phase(); + long ds_split_patient_position(); + long ds_split_patient_region(); + long ds_split_pixel_quality_mode(); + long ds_split_procedure_description(); + long ds_split_real_number(); + long ds_split_rest_direction(); + long ds_split_rotation_direction(); + long ds_split_save_code(); + long ds_split_sex(); + long ds_split_storage_mode(); + long ds_split_study_type(); + long ds_split_view_direction(); + long ds_split_window_style(); + long ds_stop_dbx(); + long ds_string_delete_leading_characters(); + long ds_string_delete_multiple_characters(); + long ds_string_delete_tailing_characters(); + long ds_string_find_string_in_string(); + long ds_string_reformate(); + long ds_string_replace(); + long ds_text_comment_no_1(); + long ds_text_comment_no_2(); + long ds_text_contrast(); + long ds_text_data_set_id(); + long ds_text_date_of_measurement(); + long ds_text_echo_time(); + long ds_text_field_of_view(); + long ds_text_gantry_tilt(); + long ds_text_gating_and_trigger(); + long ds_text_get_black_image_text(); + long ds_text_get_none_image_text(); + long ds_text_get_normal_image_text(); + long ds_text_get_recon_3d_image_text(); + long ds_text_image_number(); + long ds_text_installation_name(); + long ds_text_label(); + long ds_text_magnification_factor(); + long ds_text_manufacturer_model(); + long ds_text_matrix(); + long ds_text_mip_column(); + long ds_text_mip_head_line(); + long ds_text_mip_line(); + long ds_text_mip_slice(); + long ds_text_number_of_acquisitions(); + long ds_text_patient_birthdate(); + long ds_text_patient_name(); + long ds_text_patient_number(); + long ds_text_patient_orientation_set_1(); + long ds_text_patient_orientation_set_2(); + long ds_text_patient_orientation_sets(); + long ds_text_patient_orientation_string(); + long ds_text_patient_position(); + long ds_text_patient_sex_and_age(); + long ds_text_repetition_time(); + long ds_text_saturation_regions(); + long ds_text_scan_number(); + long ds_text_sequence_information(); + long ds_text_slice_orientation_no(); + long ds_text_slice_position(); + long ds_text_slice_thickness(); + long ds_text_software_version(); + long ds_text_study_number(); + long ds_text_table_position(); + long ds_text_time_of_acquisition(); + long ds_text_time_of_measurement(); + long ds_text_time_of_scanning(); + long ds_text_tube_current(); + long ds_text_tube_voltage(); + long ds_text_type_of_measurement(); + long ds_text_zoom_center(); + long ds_time_check_cms(); + long ds_time_cms_to_display(); + long ds_time_cms_to_nema(); + long ds_time_cms_to_pdp(); + long ds_time_cms_to_spi(); + long ds_time_display_to_cms(); + long ds_time_nema_to_cms(); + long ds_time_pdp_to_cms(); + long ds_time_string_to_cms(); + long ds_top_up_header(); + long ds_trace_control(); + long ds_transform_cms_to_blocked_som0(); + long ds_transform_cms_to_cms_nema(); + long ds_transform_cms_to_framed_som0(); + long ds_transform_common_to_cms(); + long ds_update_nema_parser(); + long ds_vector_add(); + long ds_vector_assign(); + long ds_vector_check_if_equal(); + long ds_vector_check_if_nil(); + long ds_vector_check_if_normalized(); + long ds_vector_check_if_undefined(); + long ds_vector_cut(); + long ds_vector_get(); + long ds_vector_inner_product(); + long ds_vector_multiply(); + long ds_vector_orthogonal_to_polar(); + long ds_vector_polar_to_orthogonal(); + long ds_vector_print(); + long ds_vector_put(); + long ds_vector_set(); + long ds_vector_subtract(); + long ds_vector_transform_l(); + long ds_vector_transform_m(); + long ds_vector_vector_product(); + long ds_xyz_get_cms_black_basic_data(); + long ds_xyz_get_cms_derived_data(); + long ds_xyz_get_cms_field_of_view(); + long ds_xyz_get_cms_sep_basic_data(); + long ds_xyz_get_image_class(); + long ds_xyz_get_nema_image_place(); + long ds_xyz_get_slice_orientation_parameters(); + long ds_xyz_get_transformation_list1(); + long ds_xyz_get_transformation_list2(); + long ds_xyz_get_view_direction(); + long ds_xyz_shift_definition_point(); + + + /* DECLARATION: define special data set library functions */ + data_area_type_t ds_get_data_area_type(); + item_quality_t ds_get_item_quality(); + image_text_type_t ds_get_image_text_type(); + double ds_get_max_abs_vector_component(); + char *ds_info_get_header_version(); + char *ds_info_get_nema_version(); + char *ds_info_get_software_version(); + char *ds_info_get_spi_version(); + char *ds_strstr(); + long unsigned ds_cal_image_base_vectors(); + long unsigned ds_cal_vec_mode(); + long unsigned ds_mpr_mult_matmat3(); + +#define ds_string_find_string_in_string ds_strstr + + + /* DECLARATION: define special system functions */ + char *malloc(); + +#endif
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_head_acr_groups_types.h @@ -0,0 +1,237 @@ +/*[- HEADER FILE -------------------------------------------------------------------------*/ +/* + Name: ds_head_acr_groups_types.h + + Description: The header file defines the NEMA defined data set basic groups (even group + numbers) as structures for internal use (internal header). + + + Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 +*/ +/*]-----------------------------------------------------------------------------------------*/ + +#ifndef DS_HEAD_ACR_GROUPS_TYPES +#define DS_HEAD_ACR_GROUPS_TYPES + +/******************************************/ +/* Identifying Information (Group 0008'H) */ +/******************************************/ + +typedef struct acr_identifying_tag +{ + ds_date_t StudyDate; /* (0008,0020) 10 AT DF 2NS-NEM */ + ds_date_t AcquisitionDate; /* (0008,0022) 10 AT DF 2NS-CMS */ + ds_date_t ImageDate; /* (0008,0023) 10 AT DF 2NS-CMS */ + ds_time_t StudyTime; /* (0008,0030) 12 AT DF 2NS-NEM */ + ds_time_t AcquisitionTime; /* (0008,0032) 12 AT DF 2NS-CMS */ + ds_time_t ImageTime; /* (0008,0033) 12 AT DF 2NS-CMS */ + data_set_subtype_t DataSetSubtype; /* (0008,0041) 8 AT EV 2NS-CMS */ + modality_t Modality; /* (0008,0060) 2 AT EV 1NS-CMS */ + char Manufacturer[LENGTH_MANUFACTURER + 1]; /* (0008,0070) 8 AT FF 2NS-NEM */ + char InstitutionID[LENGTH_LABEL + 1]; /* (0008,0080) 26 AT FF 2NS-NEM */ + char ReferringPhysician[LENGTH_LABEL + 1]; /* (0008,0090) 26 AT FF 2NS-NEM */ + char StationID[LENGTH_LABEL + 1]; /* (0008,1010) 26 AT FF 2NS-NEM */ + char ProcedureDescription_1[LENGTH_COMMENT + 1]; /* (0008,1030) 52 AT FF 2DS-CMS */ + char ProcedureDescription_2[LENGTH_COMMENT + 1]; + char AdmittingDiagnosis[LENGTH_DIAGNOSIS + 1]; /* (0008,1080) 40 AT FF 2DS-CMS */ + char ManufacturerModel[LENGTH_LABEL + 1]; /* (0008,1090) 26 AT FF 2DS-CMS */ +} acr_identifying_t; + + +/**************************************/ +/* Patient Information (Group 0010'H) */ +/**************************************/ + +typedef struct acr_patient_tag +{ + char PatientName[LENGTH_LABEL + 1]; /* (0010,0010) 26 AT FF 2NS-NEM */ + char PatientId[LENGTH_PATIENT_ID + 1]; /* (0010,0020) 12 AT FF 2NS-NEM */ + ds_date_t PatientBirthdate; /* (0010,0030) 10 AT DF 2NS-NEM */ + sex_t PatientSex; /* (0010,0040) 2 AT EV 2NS-NEM */ + char PatientMaidenName[LENGTH_LABEL + 1]; /* (0010,1005) 26 AT FF 2DS-CMS */ + char PatientAge[LENGTH_AGE + 1]; /* (0010,1010) 4 AT DF 2NS-CMS */ + double PatientSize; /* (0010,1020) 6 AN FF 3NS-NEM */ + long PatientWeight; /* (0010,1030) 6 AN FF 2NS-CMS */ +} acr_patient_t; + + + +/******************************************/ +/* Acquisition Information (Group 0018'H) */ +/******************************************/ + +typedef struct acr_acquisition_tag +{ + contrast_t Contrast; /* (0018,0010) 8 AT EV 2DS-NEM */ + int Pad1; /* Dummy for byte alignment */ + double SliceThickness; /* (0018,0050) 14 AN FF 2NS-NEM */ + long GeneratorVoltage; /* (0018,0060) 6 AN FF 2NM-NEM */ + long GeneratorVoltageDual; /* (0018,0060) */ + double RepetitionTime; /* (0018,0080) 14 AN FF 2NS-NEM */ + double EchoTime; /* (0018,0081) 14 AN FF 2NS-NEM */ + double InversionTime; /* (0018,0082) 14 AN FF 2NS-NEM */ + long NumberOfAverages; /* (0018,0083) 6 AN FF 3NS-NEM */ + int Pad2; /* Dummy for byte alignment */ + double ImagingFrequency; /* (0018,0084) 14 AN FF 2NS-NEM */ + nucleus_t Gap0085; + long EchoNumber; /* (0018,0086) 6 AN FF 3NS-CMS */ + long DataCollectionDiameter; /* (0018,0090) 6 AN FF 3NS-NEM */ + char DeviceSerialNumber[LENGTH_LABEL + 1]; /* (0018,1000) 26 AT FF 2DS-CMS */ + char SoftwareVersion[LENGTH_SOFTWARE_VERSION + 1]; /* (0018,1020) 8 AT FF 2DS-CMS */ + long DistanceSourceToDetector; /* (0018,1110) 6 AN FF 3NS-NEM */ + long DistanceSourceToPatient; /* (0018,1111) 6 AN FF 3NS-NEM */ + long GantryTilt; /* (0018,1120) 6 AN FF 2NS-CMS */ + long TableHeight; /* (0018,1130) 6 AN FF 3NS-NEM */ + rotation_direction_t RotationDirection; /* (0018,1140) 2 AT EV 3NS-NEM */ + long ExposureTime; /* (0018,1150) 6 AN FF 2NS-CMS */ + long Exposure; /* (0018,1152) 6 AN FF 2NS-CMS */ + char FilterIdLabel[LENGTH_FILTER_ID + 1]; /* (0018,1160) 12 AT FF 3NS-NEM */ + int Pad3; /* Dummy for byte alignment */ + double GeneratorPower; /* (0018,1170) 14 AN FF 2NS-CMS */ + double FocalSpot; /* (0018,1190 14 AN FF 3NS-NEM) */ + ds_date_t CalibrationDate; /* (0018,1200) 10 AT DF 3NS-NEM */ + ds_time_t CalibrationTime; /* (0018,1201) 12 AT DF 3NS-NEM */ + char ConvolutionKernel[LENGTH_LABEL + 1]; /* (0018,1210) 12 AT DF 3NS-NEM */ + char ReceivingCoil[LENGTH_LABEL + 1]; /* (0018,1250) 26 AT FF 2DS-CMS */ + char Gap1251[LENGTH_LABEL + 1]; + patient_position_t PatientPosition; /* (0018,5100) 8 AT EV 2NS-CMS */ + char ImagedNucleus[LENGTH_NUCLEUS + 1]; /* (0018,0085) 8 AT FF 2NS-NEM */ +} acr_acquisition_t; + + + +/*******************************************/ +/* Relationship Information (Group 0020'H) */ +/*******************************************/ + +typedef struct acr_relationship_tag +{ + long Study; /* (0020,0010) 6 AN FF 2NS-NEM */ + long Gap0011; + long Acquisition; /* (0020,0012) 6 AN FF 2DS-NEM */ + long Image; /* (0020,0013) 6 AN FF 2DS-NEM */ + long Gap0030[3]; + int Pad1; /* Dummy for byte alignment */ + double Gap0035[6]; + long Location; /* (0020,0050) 6 AN FF 3NS-NEM */ + laterality_t Laterality; /* (0020,0060) 2 AT EV 2DS-NEM */ + geometry_t ImageGeometryType; /* (0020,0070) 8 AT EV 2DS-NEM */ + long AcquisitionsInSeries; /* (0020,1001) 6 AN FF 3NS-NEM */ + reference_t Reference; /* (0020,1020) n AT FF 3NM-NEM */ +} acr_relationship_t; + + + +/*************************************************/ +/* Image Presentation Information (Group 0028'H) */ +/*************************************************/ + +typedef struct acr_presentation_tag +{ + short ImageDimension; /* (0028,0005) 2 BI HX 1DS-NEM */ + short Rows; /* (0028,0010) 2 BI HX 1NS-NEM */ + short Columns; /* (0028,0011) 2 BI HX 1NS-NEM */ + pixel_size_t PixelSize; /* (0028,0030) 30 AN FF 2NM-NEM */ + image_format_t ImageFormat; /* (0028,0040) 4 AT EV 1DS-NEM */ + compression_code_t CompressionCode; /* (0028,0060) 4 AT EV 1DS-NEM */ + short BitsAllocated; /* (0028,0100) 2 BI HX 1DS-NEM */ + short BitsStored; /* (0028,0101) 2 BI HX 1DS-NEM */ + short HighBit; /* (0028,0102) 2 BI HX 1DS-NEM */ + short PixelRepresentation; /* (0028,0103) 2 BI HX 1DS-NEM */ + windows_t WindowCenter; /* (0028,1050) 12 AN DF 2NM-MED */ + windows_t WindowWidth; /* (0028,1051) 12 AN DF 2NM-MED */ + long RescaleIntercept; /* (0028,1052) 6 AN FF 2NS-MED */ + long RescaleSlope; /* (0028,1053) 6 AN FF 2NS-MED */ +} acr_presentation_t; + +#endif + + + + +/*== HELP TEXT ===========================================================================*/ + +#ifdef DS_STC_TOOL + +"G08.Ide.StudyDate.Year", "Study Date (0008,0020)", +"G08.Ide.AcquisitionDate.Year", "Acquisition Date (0008,0022)", +"G08.Ide.ImageDate.Year", "Image Date (0008,0023)", +"G08.Ide.StudyTime.Hour", "Study Time (0008,0030)", +"G08.Ide.AcquisitionTime.Hour", "Acquisition Time (0008,0032)", +"G08.Ide.ImageTime.Hour", "Image Time (0008,0033)", +"G08.Ide.DataSetSubtype.M", "Data Set Subtype (0008,0041)", +"G08.Ide.Modality", "Modality (0008,0060)", +"G08.Ide.Manufacturer", "Manufacturer (0008,0070)", +"G08.Ide.InstitutionID", "Institution ID (0008,0080)", +"G08.Ide.ReferringPhysician", "Referring Physician (0008,0090)", +"G08.Ide.StationID", "Station ID (0008,1010)", +"G08.Ide.ProcedureDescription_1", "Procedure Description (0008,1030) - first part", +"G08.Ide.ProcedureDescription_2", "Procedure Description (0008,1030) - second part", +"G08.Ide.AdmittingDiagnosis", "Admitting Diagnosis (0008,1080)", +"G08.Ide.ManufacturerModel", "Manufacturer Model (0008,1090)", + +"G10.Pat.PatientName", "Patient Name (0010,0010)", +"G10.Pat.PatientId", "Patient Id (0010,0020)", +"G10.Pat.PatientBirthdate.Year", "Patient Birthdate (0010,0030)", +"G10.Pat.PatientSex", "Patient Sex (0010,0040): Female | Male | Others", +"G10.Pat.PatientMaidenName", "Patient Maiden Name (0010,1005)", +"G10.Pat.PatientAge", "Patient Age (0010,1010) in Years | Months | Days", +"G10.Pat.PatientSize", "Patient Size (0010,1020) in meters", +"G10.Pat.PatientWeight", "Patient Weight (0010,1030) in kilograms", + +"G18.Acq.Contrast", "Contrast (0018,0010): APPLIED | NONE", +"G18.Acq.SliceThickness", "Slice Thickness (0018,0050) in mm", +"G18.Acq.GeneratorVoltage", "nominal Generator Voltage (0018,0060) in kV", +"G18.Acq.GeneratorVoltageDual", "second value of Generator Voltage (0018,0060) in kV", +"G18.Acq.RepetitionTime", "Repetition Time (0018,0080) in msec", +"G18.Acq.EchoTime", "Echo Time (0018,0081) in msec", +"G18.Acq.InversionTime", "Inversion Time (0018,0082) in msec", +"G18.Acq.NumberOfAverages", "nominal Number of Averages (0018,0083)", +"G18.Acq.ImagingFrequency", "Imaging Frequency (0018,0084) in MHz", +"G18.Acq.ImagedNucleus", "Imaged Nucleus (0018,0085)", +"G18.Acq.EchoNumber", "Echo Number (0018,0086)", +"G18.Acq.DataCollectionDiameter", "Data Collection Diameter (0018,0090) in mm", +"G18.Acq.DeviceSerialNumber", "Device Serial Number (0018,1000)", +"G18.Acq.SoftwareVersion", "Software Version (0018,1020)", +"G18.Acq.DistanceSourceToDetector", "Distance Source to Detector (0018,1110)in mm", +"G18.Acq.DistanceSourceToPatient", "Distance Source to Patient (0018,1111) in mm", +"G18.Acq.GantryTilt", "Gantry Tilt (0018,1120) in degrees", +"G18.Acq.TableHeight", "Table Height (0018,1130) in mm", +"G18.Acq.RotationDirection", "Rotation Direction (0018,1140): Counter Clock | Clock Wise | NO", +"G18.Acq.ExposureTime", "nominal Exposure Time (0018,1150) in msec", +"G18.Acq.Exposure", "nominal Exposure (0018,1152) in mAs", +"G18.Acq.FilterIdLabel", "Filter Id Label (0018,1160)", +"G18.Acq.GeneratorPower", "nominal Generator Power (0018,1170) in kW", +"G18.Acq.FocalSpot", "Focal Spot (0018,1190) in mm", +"G18.Acq.CalibrationDate.Year", "Calibration Date (0018,1200)", +"G18.Acq.CalibrationTime.Hour", "Calibration Time (0018,1201)", +"G18.Acq.ConvolutionKernel", "Convolution Kernel (0018,1210)", +"G18.Acq.ReceivingCoil", "Receiving Coil (0018,1250)", +"G18.Acq.PatientPosition", "Patient Position (0018,5100)", + +"G20.Rel.Study", "Study (0020,0010)", +"G20.Rel.Acquisition", "Acquisition (0020,0012)", +"G20.Rel.Image", "Image (0020,0013)", +"G20.Rel.Location", "Location (0020,0050) in mm", +"G20.Rel.Laterality", "Laterality (0020,0060)", +"G20.Rel.ImageGeometryType", +"Image Geometry Type (0020,0070): PLANAR | UNRAVEL | CURVED", +"G20.Rel.AcquisitionsInSeries", "Acquisitions in Series (0020,1001)", +"G20.Rel.Reference.One.Image", "Reference (0020,1020)", + +"G28.Pre.ImageDimension", "Image Dimension (0028,0005)", +"G28.Pre.Rows", "Rows (0028,0010)", +"G28.Pre.Columns", "Columns (0028,0011)", +"G28.Pre.PixelSize.Row", "Pixel Size (0028,0030) in mm", +"G28.Pre.ImageFormat", "Image Format (0028,0040)", +"G28.Pre.CompressionCode", "Compression Code (0028,0060)", +"G28.Pre.BitsAllocated", "Bits Allocated (0028,0100)", +"G28.Pre.BitsStored", "Bits Stored (0028,0101)", +"G28.Pre.HighBit", "High Bit (0028,0102)", +"G28.Pre.PixelRepresentation", "Pixel Representation (0028,0103)", +"G28.Pre.WindowCenter.X", "Window Center (0028,1050)", +"G28.Pre.WindowWidth.X", "Window Width (0028,1051)", +"G28.Pre.RescaleIntercept", "Rescale Intercept (0028,1052)", +"G28.Pre.RescaleSlope", "Rescale Slope (0028,1053)", + +#endif
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_head_basic_types.h @@ -0,0 +1,532 @@ +/*[- HEADER FILE -------------------------------------------------------------------------*/ +/* + Name: ds_head_basic_types.h + + Description: The header file defines the basic types for internal header. + Each enumerator identifier is build up as a type qualification and the + enumerator item value defined in document [DS Item Format] paragraphs "item + "item contents". + + As exampel: Rotation_CC + + Rotation --> identifier for item Rotation Direction (0018,1140 + CC --> counter clockwise rotation + + No enumerator value is set to zero to avoid problems e.g. if an automatic + initialication during allocation was done. Zore is not a unique + identification, because several item values equal zero are defined. + + The used bit masks are not defined with C-language type bit array, because + the internal representation is compiler dependent. To handle not CMS + created bit masks (e.g. form foreign manufacturer NEMA data set) the + data set library macros DS_BIT_xxx() are used. + + + Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 +*/ +/*]-----------------------------------------------------------------------------------------*/ + +#ifndef DS_HEAD_BASIC_TYPES +#define DS_HEAD_BASIC_TYPES + + +typedef enum cardiac_code_tag +{ + Cardiac_Code_COMMON = 1, + Cardiac_Code_CONFIRM = 2, + Cardiac_Code_DIASTOLE = 3, + Cardiac_Code_NONE = 4, + Cardiac_Code_SYSTOLE = 5, + Cardiac_Code_UNDEFINED = Enum_UNDEFINED +} cardiac_code_t; + +typedef enum +{ + Calculation_m_A = 20, + Calculation_m_NONE = 1, + Calculation_m_PC = 21, + Calculation_m_PU = 22, + Calculation_m_UNDEFINED = Enum_UNDEFINED +} calculation_mode_m_t; + + +typedef enum calculation_mode_s_tag +{ + Calculation_s_BSP = 1, + Calculation_s_IRS = 2, + Calculation_s_NONE = 3, + Calculation_s_SUN = 4, + Calculation_s_VAX = 5, + Calculation_s_UNDEFINED = Enum_UNDEFINED +} calculation_mode_s_t; + + +typedef struct calculation_mode_tag +{ + calculation_mode_m_t M; + calculation_mode_s_t S; +} calculation_mode_t; + + +typedef enum compression_code_tag +{ + Compression_DONE = 2, + Compression_NONE = 1, + Compression_UNDEFINED = Enum_UNDEFINED +} compression_code_t; + + +typedef enum contrast_tag +{ + Contrast_NONE = 1, + Contrast_APPLIED = 2, + Contrast_UNDEFINED = Enum_UNDEFINED +} contrast_t; + + +typedef enum data_object_subtype_m_tag +{ + Object_m_CT = 1, + Object_m_MRS = 2, + Object_m_MRU = 3, + Object_m_UNDEFINED = Enum_UNDEFINED +} data_object_subtype_m_t; + + +typedef enum data_object_subtype_d_tag +{ + Object_d_P = 1, + Object_d_U = 2, + Object_d_UNDEFINED = Enum_UNDEFINED +} data_object_subtype_d_t; + + +typedef enum data_object_subtype_s_tag +{ + Object_s_NONE = 1, + Object_s_UNDEFINED = Enum_UNDEFINED +} data_object_subtype_s_t; + + +typedef struct data_object_subtype_tag +{ + data_object_subtype_m_t M; + data_object_subtype_d_t D; + data_object_subtype_s_t S; +} data_object_subtype_t; + + +typedef enum data_set_subtype_m_tag +{ + Set_m_IMAGE = 1, + Set_m_PLOT = 2, + Set_m_RAW = 3, + Set_m_SPECT = 4, + Set_m_TEXT = 5, + Set_m_UNDEFINED = Enum_UNDEFINED +} data_set_subtype_m_t; + + +typedef enum data_set_subtype_s_tag +{ + /* NOTE: data_set_subtype_s_tag */ + /* If this basic data type "data_set_subtype_s_t" is changed please check also the basic + data type "measurement_mode_s_t" and the sequence "determine image type" in data set + library function "ds_get_image_text_type()". */ + + /* To find a free enum number you can use the following command "sort -t= +1 <Return> <set + list> <Control D>" */ + + Set_s_NONE = 1, + Set_s_DYNA = 14, + Set_s_HIS = 19, + Set_s_HISC = 60, + Set_s_PLOT = 18, + Set_s_QUAL = 17, + Set_s_ROT = 16, + Set_s_SCAN = 10, + Set_s_SINC = 11, + Set_s_SIN = 12, + Set_s_STAT = 15, + Set_s_R2D = 62, + Set_s_R3D = 63, + Set_s_TOPO = 13, + Set_s_CAR = 20, + Set_s_MC = 21, + Set_s_BLK = 41, + Set_s_FPA = 42, + Set_s_PROJ = 43, + Set_s_READ = 44, + Set_s_VFLO = 45, + Set_s_VFPA = 46, + Set_s_VSUM = 47, + Set_s_CFL = 51, + Set_s_CSH = 52, + Set_s_UNDEFINED = Enum_UNDEFINED +} data_set_subtype_s_t; + + +typedef struct data_set_subtype_tag +{ + data_set_subtype_m_t M; + data_set_subtype_s_t S; +} data_set_subtype_t; + + +typedef struct gradient_delay_time_tag +{ + double X; + double Y; + double Z; +} gradient_delay_time_t; + + +typedef struct field_of_view_tag +{ + double Height; + double Width; +} field_of_view_t; + +typedef struct filter_parameter_tag +{ + double Value1; + double Value2; + double Value3; + double Value4; +} filter_parameter_t; + + +typedef enum filter_type_tag +{ + Filter_EXTERNAL = 5, + Filter_FERMI = 1, + Filter_GAUSS = 2, + Filter_HANNING = 3, + Filter_NONE = 4, + Filter_UNDEFINED = Enum_UNDEFINED +} filter_type_t; + + +typedef enum filter_type_image_tag +{ + Filter_Image_NO1 = 1, + Filter_Image_NONE = 4, + Filter_Image_UNDEFINED = Enum_UNDEFINED +} filter_type_image_t; + + +typedef enum gate_phase_tag +{ + Gate_EXPIRATION = 1, + Gate_INSPIRATION = 2, + Gate_UNDEFINED = Enum_UNDEFINED +} gate_phase_t; + +typedef enum geometry_tag +{ + Geometry_CURVED = 3, + Geometry_PLANAR = 1, + Geometry_UNRAVEL = 2, + Geometry_UNDEFINED = Enum_UNDEFINED +} geometry_t; + + +typedef enum image_format_tag +{ + Format_RECT = 1, + Format_UNDEFINED = Enum_UNDEFINED +} image_format_t; + + +typedef struct image_id_tag +{ + char gap[LENGTH_LABEL + 1]; + char PatientName[LENGTH_LABEL + 1]; + char PatientId[LENGTH_LABEL + 1]; + long Image; +} image_id_t; + + +typedef struct image_location_tag +{ + double Sag; + double Cor; + double Tra; +} image_location_t; + + +typedef struct int_point_tag +{ + long X; + long Y; + long Z; +} int_point_t; + + +typedef enum laterality_tag +{ + Laterality_L = 1, + Laterality_NO = 2, + Laterality_R = 3, + Laterality_UNDEFINED = Enum_UNDEFINED +} laterality_t; + + +typedef enum measurement_mode_m_tag +{ + Measurement_m_ADJU = 1, + Measurement_m_EXAM = 2, + Measurement_m_TEST = 3, + Measurement_m_UNDEFINED = Enum_UNDEFINED +} measurement_mode_m_t; + + +typedef enum measurement_mode_s_tag +{ + Measurement_s_NONE = 1, + Measurement_s_DYNA = 14, + Measurement_s_HIST = 19, + Measurement_s_PLOT = 18, + Measurement_s_QUAL = 17, + Measurement_s_ROT = 16, + Measurement_s_SCAN = 10, + Measurement_s_SINC = 11, + Measurement_s_SINZ = 12, + Measurement_s_STAT = 15, + Measurement_s_R2D = 22, + Measurement_s_R3D = 23, + Measurement_s_TOPO = 13, + Measurement_s_UNDEFINED = Enum_UNDEFINED +} measurement_mode_s_t; /* check also data_set_subtype_s_t */ + + +typedef struct measurement_mode_tag +{ + measurement_mode_m_t M; + measurement_mode_s_t S; +} measurement_mode_t; + + +typedef enum modality_tag +{ + Modality_CT = 1, + Modality_MR = 2, + Modality_UNDEFINED = Enum_UNDEFINED +} modality_t; + + +typedef enum nucleus_tag +{ + Nucleus_C = 1, + Nucleus_F = 2, + Nucleus_H = 3, + Nucleus_N = 4, + Nucleus_NA = 5, + Nucleus_P = 6, + Nucleus_UNDEFINED = Enum_UNDEFINED +} nucleus_t; + + +typedef struct object_orientation_tag +{ + double Phi; + double Theta; + double Radius; +} object_orientation_t; + + +typedef struct object_threshold_tag +{ + long LowerBoundary; + long UpperBoundary; +} object_threshold_t; + + +typedef enum order_of_slices_tag +{ + Slice_Order_ASCENDING = 1, + Slice_Order_DECREASING = 2, + Slice_Order_FREE = 3, + Slice_Order_INTERLEAVED = 4, + Slice_Order_NONE = 5, + Slice_Order_UNDEFINED = Enum_UNDEFINED +} order_of_slices_t; + + +typedef struct patient_orientation_tag +{ + char Y[LENGTH_ORIENTATION + 1]; /* up - down */ + char X[LENGTH_ORIENTATION + 1]; /* left - right */ + char Z[LENGTH_ORIENTATION + 1]; /* back - front */ +} patient_orientation_t; + + +typedef enum patient_phase_tag +{ + Phase_ADULT = 1, + Phase_CHILD = 2, + Phase_UNDEFINED = Enum_UNDEFINED +} patient_phase_t; + + +typedef enum patient_position_tag +{ + Position_LEFT = 1, + Position_PRONE = 2, + Position_RIGHT = 3, + Position_SUPINE = 4, + Position_UNDEFINED = Enum_UNDEFINED +} patient_position_t; + + +typedef enum patient_region_tag +{ + Region_BODY = 1, + Region_HEAD = 2, + Region_UNDEFINED = Enum_UNDEFINED +} patient_region_t; + + +typedef struct pixel_size_tag +{ + double Row; + double Col; +} pixel_size_t; + + +typedef enum pixel_quality_mode_tag +{ + Pixel_Quality_ESTIMATED = 1, + Pixel_Quality_EXACT = 2, + Pixel_Quality_NONE = 3, + Pixel_Quality_UNDEFINED = Enum_UNDEFINED +} pixel_quality_mode_t; + + +typedef struct pixel_quality_code_tag +{ + pixel_quality_mode_t Min; + pixel_quality_mode_t Mean; + pixel_quality_mode_t Max; +} pixel_quality_code_t; + + +typedef struct pixel_quality_value_tag +{ + long Min; + long Mean; + long Max; +} pixel_quality_value_t; + + +typedef struct reference_tag +{ + image_id_t One; + image_id_t Two; + image_id_t Three; +} reference_t; + + +typedef enum rest_direction_tag +{ + Rest_FEET = 1, + Rest_HEAD = 2, + Rest_UNDEFINED = Enum_UNDEFINED +} rest_direction_t; + + +typedef enum rotation_direction_tag +{ + Rotation_NO = 1, + Rotation_CC = 2, + Rotation_CW = 3, + Rotation_UNDEFINED = Enum_UNDEFINED +} rotation_direction_t; + + +typedef struct sar_sed_tag +{ + double Lim; + double Cal; + double Det; +} sar_sed_t; + + +typedef enum save_code_tag +{ + Save_DONE = 1, + Save_MARKED = 2, + Save_NOT = 3, + Save_UNDEFINED = Enum_UNDEFINED +} save_code_t; + + +typedef enum sex_tag +{ + Sex_F = 1, + Sex_M = 2, + Sex_O = 3, + Sex_UNDEFINED = Enum_UNDEFINED +} sex_t; + + +typedef enum storage_mode_tag +{ + Storage_COMPRESS = 1, + Storage_EXPANDED = 2, + Storage_MIP_MPR = 5, + Storage_REDUCED = 3, + Storage_XDR = 4, + Storage_UNDEFINED = Enum_UNDEFINED +} storage_mode_t; + + +typedef enum study_type_tag +{ + Study_Type_CRE = 1, + Study_Type_MEA = 2, + Study_Type_MIP = 3, + Study_Type_MPR = 4, + Study_Type_RAW = 5, + Study_Type_UNDEFINED = Enum_UNDEFINED +} study_type_t; + + +typedef struct target_point_tag +{ + double X; + double Y; +} target_point_t; + + +typedef enum view_direction_tag +{ + View_FEET = 1, + View_HEAD = 2, + View_AtoP = 3, + View_LtoR = 4, + View_PtoA = 5, + View_RtoL = 6, + View_UNDEFINED = Enum_UNDEFINED +} view_direction_t; + + +typedef struct windows_tag +{ + long X; + long Y; +} windows_t; + + +typedef enum window_style_tag +{ + Style_DOUBLE = 1, + Style_HIGH = 2, + Style_NONE = 3, + Style_STD_1 = 4, + Style_STD_2 = 5, + Style_UNDEFINED = Enum_UNDEFINED +} window_style_t; + +#endif +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_head_constants.h @@ -0,0 +1,76 @@ +/*[- HEADER FILE -------------------------------------------------------------------------*/ +/* + Name: ds_head_constants.h + + Description: The header file defines the constants used by the internal header + definiton. + + + Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 +*/ +/*]----------------------------------------------------------------------------------------*/ + +#ifndef DS_HEAD_CONSTANTS +#define DS_HEAD_CONSTANTS + +#define DS_DUMMY_LENGTH 1L + +/* PRECOMPILER: define length of group overlaying buffers */ +#define LENGTH_GROUP_0008 384 +#define LENGTH_GROUP_0009 384 +#define LENGTH_GROUP_0010 256 +#define LENGTH_GROUP_0011 128 +#define LENGTH_GROUP_0013 384 +#define LENGTH_GROUP_0018 384 +#define LENGTH_GROUP_0019_PART1 128 +#define LENGTH_GROUP_0019_PART2 512 +#define LENGTH_GROUP_0019_PART3 384 +#define LENGTH_GROUP_0019_PART4 256 +#define LENGTH_GROUP_0020 512 +#define LENGTH_GROUP_0021_PART1 256 +#define LENGTH_GROUP_0021_PART2 256 +#define LENGTH_GROUP_0021_PART3 768 +#define LENGTH_GROUP_0028 256 +#define LENGTH_GROUP_0029 256 +#define LENGTH_GROUP_0051 640 +#define LENGTH_TO_FILL_K_BORDER DS_DUMMY_LENGTH + + +/* PRECOMPILER: define length of common header strings */ +#define LENGTH_LABEL 26L + + +/* PRECOMPILER: define length of special header stings */ +#define LENGTH_AGE 4L +#define LENGTH_COMMENT 26L +#define LENGTH_DIAGNOSIS 40L +#define LENGTH_DIRECTION 4L +#define LENGTH_FILE_NAME 64L +#define LENGTH_FILTER_ID 12L +#define LENGTH_HEADER_VERSION 8L +#define LENGTH_NUCLEUS 8L +#define LENGTH_MANUFACTURER 8L +#define LENGTH_ORIENTATION 3L +#define LENGTH_PATIENT_ID 12L +#define LENGTH_SEQUENCE_INFO 8L +#define LENGTH_SOFTWARE_VERSION 8L + + +/* PRECOMPILER: define length of origianl headers */ +#define LENGTH_NUM1_HEADER 4096L +#define LENGTH_NUM2_HEADER 8192L +#define LENGTH_SOM0_HEADER 2048L +#define LENGTH_SOM0_IMAGE_TEXT 960L +#define LENGTH_SOM1_HEADER 4096L + +#define LENGTH_SOM_HEADER LENGTH_SOM1_HEADER + + +/* PRECOMPILER: define Xxx_UNDEFINED constants */ +#define Enum_UNDEFINED (-19222) +#define Integer_UNDEFINED (-19222) +#define String_UNDEFINED '?' +#define Real_UNDEFINED (-19222.0) + + +#endif
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_head_image_text_type.h @@ -0,0 +1,195 @@ +/*[- HEADER FILE -------------------------------------------------------------------------*/ +/* + Name: ds_head_image_text_type.h + + Description: The header file defines the data set image text for internal + use (internal image text area). + + The image text parameter names are not NEMA item names. Parameters with + equal names can but must not contain equal information. + + The sign "->" means, this parameter is derivated from listed NEMA items. + + For more information please see document "Data Set Format" chapter "Group + 0051'H Image Text". + + + Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 +*/ +/*]-----------------------------------------------------------------------------------------*/ + +#ifndef DS_HEAD_IMAGE_TEXT_TYPE +#define DS_HEAD_IMAGE_TEXT_TYPE + +/* PRECOMPILER: define length constants */ +#define DS_PATIENT_NUMBER_SIZE 12 + +#define DS_PATIENT_DATE_SIZE 11 + +#define DS_PATIENT_POSITION_SIZE 11 + +#define DS_IMAGE_NUMBER_SIZE 11 +#define DS_IMAGE_NUMBER_TEXT "IMAGE" + +#define DS_LABEL_SIZE 5 + +#define DS_DATE_OF_MEASUREMENT_SIZE 11 + +#define DS_TIME_OF_MEASUREMENT_SIZE 5 + +#define DS_TIME_OF_ACQUISITION_SIZE 11 +#define DS_TIME_OF_ACQUISITION_TEXT_CT "TI" +#define DS_TIME_OF_ACQUISITION_TEXT_MR "TA " + +#define DS_NUMBER_OF_ACQUISITIONS_SIZE 11 +#define DS_NUMBER_OF_ACQUISITIONS_TEXT "AC" + +#define DS_COMMENT_NO1_SIZE 26 + +#define DS_COMMENT_NO2_SIZE 26 + +#define DS_INSTALLATION_NAME_SIZE 26 + +#define DS_SOFTWARE_VERSION_SIZE 11 + +#define DS_MATRIX_SIZE 11 + +#define DS_TYPE_OF_MEASUREMENT_SIZE 11 + +#define DS_SCAN_NUMBER_SIZE 11 +#define DS_SCAN_NUMBER_TEXT "SCAN" + +#define DS_REPETITION_TIME_SIZE 11 +#define DS_REPETITION_TIME_TEXT "TR" + +#define DS_ECHO_TIME_SIZE 11 +#define DS_ECHO_TIME_TEXT "TE" + +#define DS_GATING_AND_TRIGGER_SIZE 11 +#define DS_GATING_AND_TRIGGER_TEXT "TD" + +#define DS_TUBE_CURRENT_SIZE 11 +#define DS_TUBE_CURRENT_TEXT "mA" + +#define DS_TUBE_VOLTAGE_SIZE 11 +#define DS_TUBE_VOLTAGE_TEXT "kV" + +#define DS_SLICE_THICKNESS_SIZE 11 +#define DS_SLICE_THICKNESS_TEXT "SL" + +#define DS_SLICE_POSITION_SIZE 11 +#define DS_SLICE_POSITION_TEXT "SP" + +#define DS_SLICE_ORIENTATION_NO1_SIZE 11 +#define DS_SLICE_ORIENTATION_NO2_SIZE DS_SLICE_ORIENTATION_NO1_SIZE +#define DS_COR_TEXT "Cor" +#define DS_SAG_TEXT "Sag" +#define DS_TRA_TEXT "Tra" + +#define DS_FIELD_OF_VIEW_SIZE 11 +#define DS_FIELD_OF_VIEW_TEXT "FoV" + +#define DS_ZOOM_CENTER_SIZE 11 +#define DS_ZOOM_CENTER_TEXT "CE" +#define DS_ZOOM_CENTER_TEXT_MR "MF" + +#define DS_GANTRY_TILT_SIZE 11 +#define DS_GANTRY_TILT_TEXT "GT" + +#define DS_TABLE_POSITION_SIZE 11 +#define DS_TABLE_POSITION_TEXT "TP" + +#define DS_MIP_HEADLINE_SIZE 3 +#define DS_MIP_HEADLINE_TEXT "VOI" + +#define DS_MIP_LINE_SIZE 15 +#define DS_MIP_LINE_TEXT "Lin" + +#define DS_MIP_COLUMN_SIZE 15 +#define DS_MIP_COLUMN_TEXT "Col" + +#define DS_MIP_SLICE_SIZE 15 +#define DS_MIP_SLICE_TEXT "Sli" + +#define DS_STUDY_NUMBER_SIZE 11 +#define DS_STUDY_NUMBER_TEXT "STUDY" + +#define DS_CONTRAST_SIZE 5 +#define DS_CONTRAST_TEXT_CT "+C IV" +#define DS_CONTRAST_TEXT_MR "+C " +#define DS_CONTRAST_TEXT_NONE " " + +#define DS_PATIENT_BIRTHDATE_SIZE 11 + +#define DS_SEQUENCE_INFO_SIZE 11 + +#define DS_SATURATION_REGIONS_SIZE 11 +#define DS_SATURATION_REGIONS_TEXT "SAT" + +#define DS_DATA_SET_ID_SIZE 26 +#define DS_DATA_SET_ID_TEXT_STUDY "STU" +#define DS_DATA_SET_ID_TEXT_IMAGE "IMA" +#define DS_DATA_SET_ID_TEXT_DELIMITER "/" + +#define DS_MAGNIFICATION_FACTOR_SIZE 11 +#define DS_MAGNIFICATION_FACTOR_TEXT "MF" + +#define DS_MANUFACTURER_MODEL_SIZE 26 + +#define DS_PATIENT_NAME_SIZE 26 + +#define DS_TIME_OF_SCANNING_SIZE 8 + + + +/* DECLARATION: image text type */ + +typedef struct image_text_tag +{ + char PatientNumber[DS_PATIENT_NUMBER_SIZE + 1]; /* -> Patient Id */ + char PatientSexAndAge[DS_PATIENT_DATE_SIZE + 1]; /* -> Patient Sex, Patient Age */ + char PatientPosition[DS_PATIENT_POSITION_SIZE + 1]; /* -> Patient Rest Direction, ... */ + char ImageNumber[DS_IMAGE_NUMBER_SIZE + 1]; /* -> Image */ + char Label[DS_LABEL_SIZE + 1]; /* -> Archiving Mark Mask, ... */ + char DateOfMeasurement[DS_DATE_OF_MEASUREMENT_SIZE + 1]; /* -> Acquisition Date */ + char TimeOfMeasurement[DS_TIME_OF_MEASUREMENT_SIZE + 1]; /* -> Acquisition Time */ + char TimeOfAcquisition[DS_TIME_OF_ACQUISITION_SIZE + 1]; /* -> CT: Exposure Time -> MR: + Total Measurement Time */ + char NumberOfAcquisitions[DS_NUMBER_OF_ACQUISITIONS_SIZE + 1]; /* -> Number of Averages */ + char CommentNo1[DS_COMMENT_NO1_SIZE + 1]; /* -> Procedure Description */ + char CommentNo2[DS_COMMENT_NO2_SIZE + 1]; + char InstallationName[DS_INSTALLATION_NAME_SIZE + 1]; /* -> Institution ID */ + char SoftwareVersion[DS_SOFTWARE_VERSION_SIZE + 1]; /* -> Software Version */ + char Matrix[DS_MATRIX_SIZE + 1]; /* -> Rows, Columns */ + char TypeOfMeasurement[DS_TYPE_OF_MEASUREMENT_SIZE + 1]; /* -> Calculation Mode */ + char ScanNumber[DS_SCAN_NUMBER_SIZE + 1]; /* -> Acquisition */ + char RepetitionTime[DS_REPETITION_TIME_SIZE + 1]; /* -> Repetition Time */ + char EchoTime[DS_ECHO_TIME_SIZE + 1]; /* -> Echo Time */ + char GatingAndTrigger[DS_GATING_AND_TRIGGER_SIZE + 1]; /* -> Signal Mask */ + char TubeCurrent[DS_TUBE_CURRENT_SIZE + 1]; /* -> Exposure */ + char TubeVoltage[DS_TUBE_VOLTAGE_SIZE + 1]; /* -> Generator Power */ + char SliceThickness[DS_SLICE_THICKNESS_SIZE + 1]; /* -> Slice Thickness */ + char SlicePosition[DS_SLICE_POSITION_SIZE + 1]; /* -> Image Distance */ + char SliceOrientationNo1[DS_SLICE_ORIENTATION_NO1_SIZE + 1]; /* -> Image Position, ... */ + char SliceOrientationNo2[DS_SLICE_ORIENTATION_NO2_SIZE + 1]; + char FieldOfView[DS_FIELD_OF_VIEW_SIZE + 1]; /* -> Field of View */ + char ZoomCenter[DS_ZOOM_CENTER_SIZE + 1]; /* -> Target */ + char GantryTilt[DS_GANTRY_TILT_SIZE + 1]; /* -> Gantry Tilt */ + char TablePosition[DS_TABLE_POSITION_SIZE + 1]; /* -> Location */ + char MipHeadLine[DS_MIP_HEADLINE_SIZE + 1]; /* -> <string> */ + char MipLine[DS_MIP_LINE_SIZE + 1]; /* -> MIP x Row */ + char MipColumn[DS_MIP_COLUMN_SIZE + 1]; /* -> MIP x Column */ + char MipSlice[DS_MIP_SLICE_SIZE + 1]; /* -> MIP x Slice */ + char StudyNumber[DS_STUDY_NUMBER_SIZE + 1]; /* -> Study */ + char Contrast[DS_CONTRAST_SIZE + 1]; /* -> Contrast Agent */ + char PatientBirthdate[DS_PATIENT_BIRTHDATE_SIZE + 1]; /* -> Patient Birthday */ + char SequenceInformation[DS_SEQUENCE_INFO_SIZE + 1]; /* -> Sequence File Owner, ... */ + char SaturationRegions[DS_SATURATION_REGIONS_SIZE + 1]; /* -> Saturation Regions, ... */ + char DataSetId[DS_DATA_SET_ID_SIZE + 1]; /* -> Image, Study */ + char MagnificationFactor[DS_MAGNIFICATION_FACTOR_SIZE + 1]; /* -> Image Maginification Factor */ + char ManufacturerModel[DS_MANUFACTURER_MODEL_SIZE + 1]; /* -> Manufacturer Model */ + char PatientName[DS_PATIENT_NAME_SIZE + 1]; /* -> Patient Name */ + char TimeOfScanning[DS_TIME_OF_SCANNING_SIZE + 1]; /* -> Acquisition Time */ +} image_text_t; + +#endif
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_head_shadow_groups_types.h @@ -0,0 +1,671 @@ +/*[- HEADER FILE -------------------------------------------------------------------------*/ +/* + Name: ds_head_shadow_groups_types.h + + Description: The header file defines the SPI and CMS defined data set basic groups + (odd group numbers) as structures for internal use (internal header). + + + Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 +*/ +/*]-----------------------------------------------------------------------------------------*/ + +#ifndef DS_HEAD_SHADOW_GROUPS_TYPES +#define DS_HEAD_SHADOW_GROUPS_TYPES + +/******************************************/ +/* Identifying Information (Group 0009'H) */ +/******************************************/ + +typedef struct shadow_identifying_tag +{ + data_object_subtype_t DataObjectSubtype; /* (0009,1041) 8 AT EV 2NS-SPI */ + long NumberOfMeasurements; /* (0009,1200) 6 AN FF 3NS-CMS */ + storage_mode_t StorageMode; /* (0009,1210) 8 AT EV 2DS-CMS */ + long EvaluationMask; /* (0009,1212) 4 BD FF 2DS-CMS */ + long Gap1212; + ds_date_t LastMoveDate; /* (0009,1226) 10 AT DF 3NS-CMS */ + ds_time_t LastMoveTime; /* (0009,1227) 12 AT DF 3NS-CMS */ + char GeneratorIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1310) 26 AT DF 2DS-CMS */ + char GantryIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1311) 26 AT DF 2DS-CMS */ + char XRayTubeIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1312) 26 AT DF 2DS-CMS */ + char DetectorIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1313) 26 AT DF 2DS-CMS */ + char DASIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1314) 26 AT DF 2DS-CMS */ + char SMIIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1315) 26 AT DF 2DS-CMS */ + char CPUIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1316) 26 AT DF 2DS-CMS */ + char HeaderVersion[LENGTH_HEADER_VERSION + 1]; /* (0009,1320) 8 AT DF 2DS-CMS */ +} shadow_identifying_t; + + + +/**************************************/ +/* Patient Information (Group 0011'H) */ +/**************************************/ + +typedef struct shadow_patient_tag +{ + char Organ[LENGTH_LABEL + 1]; /* (0011,1010) 26 AT FF 3NS-SPI */ + ds_date_t RegistrationDate; /* (0011,1110) 10 AT DF 2NS-CMS */ + ds_time_t RegistrationTime; /* (0011,1111) 12 AT DF 2NS-CMS */ + long UsedPatientWeight; /* (0011,1123) 4 AN FF 2NS-CMS */ + long OrganCode; /* (0011,1012) 6 AN FF 3NS-CMS */ +} shadow_patient_t; + + + +/*********************************************/ +/* Patient Modify Information (Group 0013'H) */ +/*********************************************/ + +typedef struct shadow_patient_modify_tag +{ + char ModifyingPhysician[LENGTH_LABEL + 1]; /* (0013,1000) 26 AT FF 2NS-CMS */ + ds_date_t ModificationDate; /* (0013,1010) 10 AT DF 2NS-CMS */ + ds_time_t ModificationTime; /* (0013,1012) 12 AT DF 2NS-CMS */ + char PatientName[LENGTH_LABEL + 1]; /* (0013,1020) 26 AT FF 3NS-CMS */ + char PatientId[LENGTH_PATIENT_ID + 1]; /* (0013,1022) 12 AT FF 3NS-CMS */ + ds_date_t PatientBirthdate; /* (0013,1030) 10 AT DF 3NS-CMS */ + long PatientWeight; /* (0013,1031) 6 AN FF 3NS-CMS */ + char PatientMaidenName[LENGTH_LABEL + 1]; /* (0013,1032) 26 AT FF 3NS-CMS */ + char ReferringPhysician[LENGTH_LABEL + 1]; /* (0013,1033) 26 AT FF 3NS-CMS */ + char AdmittingDiagnosis[LENGTH_DIAGNOSIS + 1]; /* (0013,1034) 40 AT FF 3DS-CMS */ + sex_t PatientSex; /* (0013,1035) 2 AT EV 3NS-CMS */ + char ProcedureDescription_1[LENGTH_COMMENT + 1]; /* (0013,1040) 52 AT FF 3NS-CMS */ + char ProcedureDescription_2[LENGTH_COMMENT + 1]; + rest_direction_t RestDirection; /* (0013,1042) 4 AT EV 3NS-CMS */ + patient_position_t PatientPosition; /* (0013,1044) 8 AT EV 3NS-CMS */ + view_direction_t ViewDirection; /* (0013,1046) 4 AT EV 2NS-CMS */ +} shadow_patient_modify_t; + + + +/******************************************/ +/* Acquisition Information (Group 0019'H) */ +/******************************************/ + +typedef struct shadow_acquisition_cms_tag /* CMS shadowsubgroup */ +{ + long NetFrequency; /* (0019,1010) 6 AN FF 3NS-CMS */ + measurement_mode_t MeasurementMode; /* (0019,1020) 8 AT EV 2DS-CMS */ + calculation_mode_t CalculationMode; /* (0019,1030) 8 AT EV 2NS-CMS */ + long Gap1040; + long NoiseLevel; /* (0019,1050) 6 AN FF 3NS-CMS */ + long NumberOfDataBytes; /* (0019,1060) 6 AN FF 2NS-CMS */ +} shadow_acquisition_cms_t; + + +typedef struct shadow_acquisition_ct_tag /* CT shadowsubgroup */ +{ + double SourceSideCollimatorAperture; /* (0019,1110) 14 AN FF 3NS-CMS */ + double DetectorSideCollimatorAperture; /* (0019,1111) 14 AN FF 3NS-CMS */ + long ExposureTime; /* (0019,1120) 6 AN FF 3NS-CMS */ + long Exposure; /* (0019,1121) 6 AN FF 3NS-CMS */ + double GeneratorPower; /* (0019,1125) 14 AN FF 3NS-CMS */ + long GeneratorVoltage; /* (0019,1126) 6 AN FF 3NM-CMS */ + long GeneratorVoltageDual; + long MasterControlMask; /* (0019,1140) 4 BD HX 3NM-CMS */ + long Gap1140; + short ProcessingMask[5]; /* (0019,1142) 2 BI HX 3NM-CMS */ + short Gap1142[3]; + long NumberOfVirtuellChannels; /* (0019,1162) 6 AN FF 3NS-CMS */ + long NumberOfReadings; /* (0019,1170) 6 AN FF 3NS-CMS */ + long NumberOfProjections; /* (0019,1174) 6 AN FF 3NS-CMS */ + long NumberOfBytes; /* (0019,1175) 6 AN FF 3NS-CMS */ + long ReconstructionAlgorithmSet[3]; /* (0019,1180) 6 AN FF 3NS-CMS */ + long Gap1180[2]; + long ReconstructionAlgorithmIndex; /* (0019,1181) 6 AN FF 3NS-CMS */ + char RegenerationSoftwareVersion[LENGTH_SOFTWARE_VERSION + 1]; /* (0019,1182) 8 AT FF 3NS-CMS */ +} shadow_acquisition_ct_t; + + +typedef struct shadow_acquisition_mr_tag /* MR shadowsubgroup */ +{ + double TotalMeasurementTime; /* (0019,1210) 14 AN FF 3NS-CMS */ + double StartDelayTime; /* (0019,1212) 14 AN FF 3NS-CMS */ + long NumberOfPhases; /* (0019,1214) 6 AN FF 2DS-CMS */ + long SequenceControlMask[2]; /* (0019,1216) 4 BD HX 3NM-CMS */ + long Gap1216[2]; + long NumberOfFourierLinesNominal; /* (0019,1220) 6 AN FF 3NS-CMS */ + long NumberOfFourierLinesCurrent; /* (0019,1221) 6 AN FF 3NS-CMS */ + long NumberOfFourierLinesAfterZero; /* (0019,1226) 6 AN FF 3NS-CMS */ + long FirstMeasuredFourierLine; /* (0019,1228) 6 AN FF 3NS-CMS */ + long AcquisitionColumns; /* (0019,1230) 6 AN FF 3NS-CMS */ + long ReconstructionColumns; /* (0019,1231) 6 AN FF 3NS-CMS */ + long NumberOfAverages; /* (0019,1250) 6 AN FF 3NS-CMS */ + double FlipAngle; /* (0019,1260) 14 AN FF 3NS-CMS */ + long NumberOfPrescans; /* (0019,1270) 6 AN FF 3NS-CMS */ + filter_type_t FilterTypeRawData; /* (0019,1281) 8 AT EV 2DS-CMS */ + filter_parameter_t FilterParameterRawData; /* (0019,1282) 14 AN FF 3NM-CMS */ + filter_type_image_t FilterTypeImageData; /* (0019,1283) 8 AT EV 2DS-CMS */ + int Pad1; /* Dummy for byte alignament */ + filter_parameter_t FilterParameterImageData; /* (0019,1284) 14 AN FF 3NM-CMS */ + filter_type_t FilterTypePhaseCorrection; /* (0019,1285) 8 AT EV 2DS-CMS */ + int Pad2; /* Dummy for byte alignament */ + filter_parameter_t FilterParameterPhaseCorrection; /* (0019,1286) 14 AN FF 3NM-CMS */ + long NumberOfSaturationRegions; /* (0019,1290) 6 AN FF 2DS-CMS */ + /* short Pad4[5]; /* Dummy for byte alignment */ + int Pad3; /* Dummy for byte alignament */ + double ImageRotationAngle; /* (0019,1294) 14 AN FF 3NS-CMS */ + double DwellTime; /* (0019,1213) 14 AN FF 3NS-CMS */ + long CoilIdMask[3]; /* (0019,1296) 4 BD HX 3NM-CMS */ + long Gap1296[2]; + int Pad4; /* Dummy for byte alignament */ + image_location_t CoilPosition; /* (0019,1298) 14 AN FF 2NM-CMS */ + double TotalMeasurementTimeCur; /* (0019,1211) 14 AN FF 3NS-CMS*/ + long MeasurementStatusMask; /* (0019,1218) 4 BD FF 2DS-CMS */ +} shadow_acquisition_mr_t; + + +typedef struct shadow_acquisition_ct_conf_tag /* CT configuration and adjust + shadowsubgroup */ +{ + long DistanceSourceToSourceSideCollimator; /* (0019,1310) 6 AN FF 3NS-CMS */ + long DistanceSourceToDetectorSideCollimator; /* (0019,1311) 6 AN FF 3NS-CMS */ + long NumberOfPossibleChannels; /* (0019,1320) 6 AN FF 3NS-CMS */ + long MeanChannelNumber; /* (0019,1321) 6 AN FF 3NS-CMS */ + double DetectorSpacing; /* (0019,1322) 14 AN FF 3NS-CMS */ + double ReadingIntegrationTime; /* (0019,1324) 14 AN FF 3NS-CMS */ + double DetectorAlignment; /* (0019,1350) 14 AN FF 3NS-CMS */ + double FocusAlignment; /* (0019,1360) 14 AN FF 3NS-CMS */ + long FocalSpotDeflectionAmplitude; /* (0019,1365) 4 BD HX 3NS-CMS */ + long FocalSpotDeflectionPhase; /* (0019,1366) 4 BD HX 3NS-CMS */ + long FocalSpotDeflectionOffset; /* (0019,1367) 4 BD HX 3NS-CMS */ + double WaterScalingFactor; /* (0019,1370) 14 AN FF 3NS-CMS */ + double InterpolationFactor; /* (0019,1371) 14 AN FF 3NS-CMS */ + patient_region_t PatientRegion; /* (0019,1380) 4 AT EV 3NS-CMS */ + patient_phase_t PatientPhaseOfLife; /* (0019,1382) 8 AT EV 3NS-CMS */ + double DetectorCenter; /* (0019,1323) 14 AN FF 3NS-CMS */ +} shadow_acquisition_ct_conf_t; + + +typedef struct shadow_acquisition_mr_conf_tag /* MR configuration and adjust + shadowsubgroup */ +{ + double MagneticFieldStrength; /* (0019,1412) 14 AN FF 3NS-CMS */ + double ADCVoltage; /* (0019,1414) 14 AN FF 3NS-CMS */ + double TransmitterAmplitude; /* (0019,1420) 14 AN FF 3NS-CMS */ + long NumberOfTransmitterAmplitudes; /* (0019,1421) 6 AN FF 3NS-CMS */ + int Pad1; /* Dummy for byte alignment */ + double TransmitterCalibration; /* (0019,1424) 14 AN FF 3NS-CMS */ + double ReceiverTotalGain; /* (0019,1450) 14 AN FF 3NS-CMS */ + double ReceiverAmplifierGain; /* (0019,1451) 14 AN FF 3NS-CMS */ + double ReceiverPreamplifierGain; /* (0019,1452) 14 AN FF 3NS-CMS */ + double ReceiverCableAttenuation; /* (0019,1454) 14 AN FF 3NS-CMS */ + double ReconstructionScaleFactor; /* (0019,1460) 14 AN FF 3NS-CMS */ + double PhaseGradientAmplitude; /* (0019,1470) 14 AN FF 3NS-CMS */ + double ReadoutGradientAmplitude; /* (0019,1471) 14 AN FF 3NS-CMS */ + double SelectionGradientAmplitude; /* (0019,1472) 14 AN FF 3NS-CMS */ + gradient_delay_time_t GradientDelayTime; /* (0019,1480) 14 AN FF 3NS-CMS */ + char SensitivityCorrectionLabel[LENGTH_LABEL + 1]; /* (0019,1490) 26 AT FF 2DS-CMS */ + int Pad2; /* Dummy for byte alignment */ + double ADCOffset[2]; /* (0019,1416) 14 AN FF 3NM-CMS */ + double TransmitterAttenuator; /* (0019,1422) 14 AN FF 3NS-CMS */ + double TransmitterReference; /* (0019,1426) 14 AN FF 3NS-CMS */ + double ReceiverReferenceGain; /* (0019,1455) 14 AN FF 3NS-CMS */ + long ReceiverFilterFrequency; /* (0019,1456) 6 AN FF 3NS-CMS */ + int Pad3; /* Dummy for byte alignment */ + double ReferenceScaleFactor; /* (0019,1462) 14 AN FF 3NS-CMS */ + double TotalGradientDelayTime; /* (0019,1482) 14 AN FF 3NS-CMS */ + long RfWatchdogMask; /* (0019,14A0) 4 BD HX 3NM-CMS */ + int Pad4; /* Dummy for byte alignment */ + double RfPowerErrorIndicator; /* (0019,14A2) 14 AN FF 3NS-CMS */ + sar_sed_t SarWholeBody; /* (0019,14A5) 14 AN FF 3NS-CMS */ + sar_sed_t Sed; /* (0019,14A6) 14 AN FF 3NS-CMS */ + long AdjustmentStatusMask; /* (0019,14B0) 4 BD FF 2DS-CMS */ +} shadow_acquisition_mr_conf_t; + + +typedef struct shadow_acquisition_acq_tag /* acquisition shadowsubgroup */ +{ + char ParameterFileName[LENGTH_FILE_NAME + 1]; /* (0019,1510) 64 AT FF 3NS-CMS */ + char SequenceFileName[LENGTH_FILE_NAME + 1]; /* (0019,1511) 64 AT FF 3NS-CMS */ + char SequenceFileOwner[LENGTH_SEQUENCE_INFO + 1]; /* (0019,1512) 8 AT FF 2DS-CMS */ + char SequenceDescription[LENGTH_SEQUENCE_INFO + 1]; /* (0019,1513) 8 AT FF 2DS-CMS */ +} shadow_acquisition_acq_t; + + +/*******************************************/ +/* Relationship Information (Group 0021'H) */ +/*******************************************/ + +typedef struct shadow_relationship_med_cms_tag /* MED and CMS shadowsubgroups */ +{ + double Gap1010; + target_point_t Target; /* (0021,1011) 30 AT FF 2NM-MED */ + short RoiMask; /* (0021,1020) 2 BI HX 2NS-MED */ + int Pad1; /* Dummy for byte alignment */ + field_of_view_t FoV; /* (0021,1120) 30 AN FF 2NM-CMS */ + view_direction_t ViewDirection; /* (0021,1130) 4 AT EV 2NS-CMS */ + rest_direction_t RestDirection; /* (0021,1132) 4 AT EV 2NS-CMS */ + image_location_t ImagePosition; /* (0021,1160) 14 AN FF 2NM-CMS */ + image_location_t ImageNormal; /* (0021,1161) 14 AN FF 2NM-CMS */ + double ImageDistance; /* (0021,1163) 14 AN FF 2NM-CMS */ + short ImagePositioningHistoryMask; /* (0021,1165) 2 BI HX 2DS-CMS */ + int Pad2; /* Dummy for byte alignment */ + image_location_t ImageRow; /* (0021,116A) 14 AN FF 2NM-CMS */ + image_location_t ImageColumn; /* (0021,116B) 14 AN FF 2NM-CMS */ + patient_orientation_t PatientOrientationSet1; /* (0021,1170) 4 AT EV 2NM-CMS */ + patient_orientation_t PatientOrientationSet2; /* (0021,1171) 4 AT EV 2NM-CMS */ + char StudyName[LENGTH_LABEL + 1]; /* (0021,1180) 26 AT FF 3NS-CMS */ + study_type_t StudyType; /* (0021,1182) 4 AT EV 3NS-CMS */ + double ImageMagnificationFactor; /* (0021,1122) 14 AN FF 2DS-CMS */ +} shadow_relationship_med_cms_t; + + +typedef struct shadow_relationship_ct_tag /* CT common shadowsubgroups */ +{ + long RotationAngle; /* (0021,1210) 6 AN FF 3NS-CMS */ + long StartAngle; /* (0021,1211) 6 AN FF 3NS-CMS */ + long TubePosition; /* (0021,1230) 6 AN FF 3NS-CMS */ + long LengthOfTopogram; /* (0021,1232) 6 AN FF 3NS-CMS */ + double CorrectionFactor; /* (0021,1234) 14 AN FF 3NS-CMS */ + long MaximumTablePosition; /* (0021,1236) 6 AN FF 3NS-CMS */ + long TableMoveDirectionCode; /* (0021,1240) 6 AN FF 3NS-CMS */ + long VectorStartRow; /* (0021,1250) 6 AN FF 3NS-CMS */ + long VectorRowStep; /* (0021,1251) 6 AN FF 3NS-CMS */ + long VectorStartColumn; /* (0021,1252) 6 AN FF 3NS-CMS */ + long VectorColumnStep; /* (0021,1253) 6 AN FF 3NS-CMS */ + long VoiStartRow; /* (0021,1245) 6 AN FF 3NS-CMS */ + long VoiStopRow; /* (0021,1246) 6 AN FF 3NS-CMS */ + long VoiStartColumn; /* (0021,1247) 6 AN FF 3NS-CMS */ + long VoiStopColumn; /* (0021,1248) 6 AN FF 3NS-CMS */ + long VoiStartSlice; /* (0021,1249) 6 AN FF 3NS-CMS */ + long VoiStopSlice; /* (0021,124A) 6 AN FF 3NS-CMS */ + long RangeTypeCode; /* (0021,1260) 6 AN FF 3NS-CMS */ + long ReferenceTypeCode; /* (0021,1262) 6 AN FF 3NS-CMS */ + object_orientation_t ObjectOrientation; /* (0021,1270) 14 AN FF 3NS-CMS */ + object_orientation_t LightOrientation; /* (0021,1272) 14 AN FF 3NS-CMS */ + double LightBrightness; /* (0021,1275) 14 AN FF 3NS-CMS */ + double LightContrast; /* (0021,1276) 14 AN FF 3NS-CMS */ + object_threshold_t OverlayThreshold; /* (0021,127A) 12 AN FF 3NM-CMS */ + object_threshold_t SurfaceThreshold; /* (0021,127B) 12 AN FF 3NM-CMS */ + object_threshold_t GreyScaleThreshold; /* (0021,127C) 12 AN FF 3NM-CMS */ +} shadow_relationship_ct_t; + + +typedef struct shadow_relationship_mr_tag /* MR common shadowsubgroups */ +{ + long PhaseCorRowSeq; /* (0021,1320) 6 AN FF 3NS-CMS */ + long PhaseCorColSeq; /* (0021,1321) 6 AN FF 3NS-CMS */ + long PhaseCorRowRec; /* (0021,1322) 6 AN FF 3NS-CMS */ + long PhaseCorColRec; /* (0021,1324) 6 AN FF 3NS-CMS */ + long NumberOf3DRawPartNom; /* (0021,1330) 6 AN FF 3NS-CMS */ + long NumberOf3DRawPartCur; /* (0021,1331) 6 AN FF 3NS-CMS */ + long NumberOf3DImaPart; /* (0021,1334) 6 AN FF 3NS-CMS */ + long Actual3DImaPartNumber; /* (0021,1336) 6 AN FF 3NS-CMS */ + long Gap1338; + long NumberOfSlicesNom; /* (0021,1340) 6 AN FF 3NS-CMS */ + long NumberOfSlicesCur; /* (0021,1341) 6 AN FF 3NS-CMS */ + long CurrentSliceNumber; /* (0021,1342) 6 AN FF 3NS-CMS */ + long CurrentGroupNumber; /* (0021,1343) 6 AN FF 3NS-CMS */ + long MipStartRow; /* (0021,1345) 6 AN FF 3NS-CMS */ + long MipStopRow; /* (0021,1346) 6 AN FF 3NS-CMS */ + long MipStartColumn; /* (0021,1347) 6 AN FF 3NS-CMS */ + long MipStopColumn; /* (0021,1348) 6 AN FF 3NS-CMS */ + long MipStartSlice; /* (0021,1349) 6 AN FF 3NS-CMS */ + long MipStopSlice; /* (0021,134A) 6 AN FF 3NS-CMS */ + long SignalMask; /* (0021,1350) 4 BI HX 2DS-CMS */ + long Gap1350; + long DelayAfterTrigger; /* (0021,1352) 6 AN FF 3NS-CMS */ + long RRInterval; /* (0021,1353) 6 AN FF 3NS-CMS */ + int Pad1; /* Dummy for byte alignment */ + double NumberOfTriggerPulses; /* (0021,1354) 14 AN FF 3NS-CMS */ + double RepetitionTime; /* (0021,1356) 14 AN FF 3NS-CMS */ + gate_phase_t GatePhase; /* (0021,1357) 12 AT EV 3NS-CMS */ + int Pad2; /* Dummy for byte alignment */ + double GateThreshold; /* (0021,1358) 14 AN FF 3NS-CMS */ + double GateRatio; /* (0021,1359) 14 AN FF 3NS-CMS */ + long NumberOfInterpolatedImages; /* (0021,1360) 6 AN FF 3NS-CMS */ + long NumberOfEchoes; /* (0021,1370) 6 AN FF 3NS-CMS */ + double SecondEchoTime; /* (0021,1372) 14 AN FF 3NS-CMS */ + double SecondRepetitionTime; /* (0021,1373) 14 AN FF 3NS-CMS */ + long CardiacCode; /* (0021,1380) 6 AN FF 3NS-CMS */ + int Pad3; /* Dummy for byte alignment */ + double CurrentSliceDistanceFactor; /* (0021,1344) 14 AN FF 3NS-CMS */ + order_of_slices_t OrderOfSlices; /* (0021,134F) 12 AT EV 3NS-CMS */ + int Pad4; /* Dummy for byte alignment */ + double SlabThickness; /* (0021,1339) 6 AN FF 3NS-CMS */ +} shadow_relationship_mr_t; + + +typedef struct shadow_relationship_ct_spe_tag /* CT special shadowsubgroups */ +{ + long EvaluationMask[2]; /* (0021,2220) 4 BD FF 2DM-CMS */ + long Gap2220[2]; + short ExtendedProcessingMask[7]; /* (0021,2230) 4 BD FF 2DM-CMS */ + short Gap2230[3]; + long CreationMask[2]; /* (0021,2210) 2 BI HX 3NM-CMS + */ + long Gap2210[2]; +} shadow_relationship_ct_spe_t; + + +typedef struct shadow_relationship_mr_spe_tag /* MR special shadowsubgroups */ +{ + double EpiReconstructionPhase; /* (0019,12A0) 14 AN FF 3NS-CMS */ + double EpiReconstructionSlope; /* (0019,12A1) 14 AN FF 3NS-CMS */ + double EpiCapacity[6]; /* (0019,14C1) 14 AN FF 3NM-CMS */ + double EpiInductance[3]; /* (0019,14C2) 14 AN FF 3NM-CMS */ + long EpiSwitchConfigurationCode[3]; /* (0019,14C3) 6 AN FF 3NM-CMS */ + long EpiSwitchHardwareCode[3]; /* (0019,14C4) 6 AN FF 3NM-CMS */ + long EpiSwitchDelayTime[6]; /* (0019,14C5) 6 AN FF 3NM-CMS */ + char EpiFileName[LENGTH_FILE_NAME + 1]; /* (0019,1514) 64 AT FF 3NS-CMS */ + +} shadow_relationship_mr_spe_t; + + +/*************************************************/ +/* Image Presentation Information (Group 0029'H) */ +/*************************************************/ + +typedef struct shadow_presentation_tag +{ + window_style_t WindowStyle; /* (0029,1110) 8 AT EV 2DS-CMS */ + pixel_quality_code_t PixelQualityCode; /* (0029,1120) 12 AT FF 2DM-CMS */ + pixel_quality_value_t PixelQualityValue; /* (0029,1122) 6 AN FF 3NM-CMS */ + save_code_t ArchiveCode; /* (0029,1150) 8 AN EV 3NS-CMS */ + save_code_t ExposureCode; /* (0029,1151) 8 AN EV 3NS-CMS */ + long SortCode; /* (0029,1152) 6 AN FF 3NS-CMS */ + long Splash; /* (0029,1160) 6 AN FF 3NS-CMS */ +} shadow_presentation_t; + +#endif + + + + +/*== HELP TEXT ===========================================================================*/ + +#ifdef DS_STC_TOOL + +"G09.Ide.DataObjectSubtype.M", +"Data Object Subtype (0009,0041) - main: CT | MR Signed | MR Unsigned", +"G09.Ide.DataObjectSubtype.D", +"Data Object Subtype (0009,0041) - defined by: Predefined | defined by User", +"G09.Ide.DataObjectSubtype.S - sub: NONE", +"Data Object Subtype (0009,0041)", +"G09.Ide.NumberOfMeasurements", "Number of Measurements (0009,1200)", +"G09.Ide.StorageMode", +"Storage Mode (0009,1210): COMPRESS | EXPANDED | MIP_MPR | REDUCED | XDR", +"G09.Ide.EvaluationMask", "Evaluation Mask - Image (0009,1212)", +"G09.Ide.LastMoveDate.Year", "Last Move Date (0009,1226)", +"G09.Ide.LastMoveTime.Hour", "Last Move Time (0009,1227)", +"G09.Ide.GeneratorIdentificationLabel", "Generator Identification Label (0009,1310)", +"G09.Ide.GantryIdentificationLabel", "Gantry Identification Label (0009,1311)", +"G09.Ide.XRayTubeIdentificationLabel", "X-Ray Tube Identification Label (0009,1312)", +"G09.Ide.DetectorIdentificationLabel", "Detector Identification Label (0009,1313)", +"G09.Ide.DASIdentificationLabel", "DAS Identification Label (0009,1314)", +"G09.Ide.SMIIdentificationLabel", "SMI Identification Label (0009,1315)", +"G09.Ide.CPUIdentificationLabel", "CPU Identification Label (0009,1316)", +"G09.Ide.HeaderVersion", "Header Version (0009,1320)", + +"G11.Pat.Organ", "Organ (0011,1010)", +"G11.Pat.RegistrationDate.Year", "Registration Date (0011,1110)", +"G11.Pat.RegistrationTime.Hour", "Registration Time (0011,1111)", +"G11.Pat.UsedPatientWeight", "Used Patient Weight (0011,1123) in kg", +"G11.Pat.OrganCode", "Organ Code (0011,1140)", + +"G13.PatMod.ModifyingPhysician", "Modifying Physician (0013,1000)", +"G13.PatMod.ModificationDate.Year", "Modification Date (0013,1022)", +"G13.PatMod.ModificationTime.Hour", "Modification Time (0013,1010)", +"G13.PatMod.PatientName", "former Patient Name (0013,1020)", +"G13.PatMod.PatientId", "former Patient Id (0013,1022)", +"G13.PatMod.PatientBirthdate.Year", "former Patient Birthdate (0013,1030)", +"G13.PatMod.PatientWeight", "former Patient Weight (0013,1031)", +"G13.PatMod.PatientMaidenName", "former Patient Maiden Name (0013,1032)", +"G13.PatMod.ReferringPhysician", "former Referring Physician (0013,1033)", +"G13.PatMod.AdmittingDiagnosis", "former Admitting Diagnosis (0013,1034)", +"G13.PatMod.PatientSex", "former Patient Sex (0013,1035)", +"G13.PatMod.ProcedureDescription_1", "former Procedure Description_1 (0013,1040)", +"G13.PatMod.ProcedureDescription_2", "former Procedure Description_2", +"G13.PatMod.RestDirection", "former Rest Direction (0013,1042)", +"G13.PatMod.PatientPosition", "former Patient Position (0013,1044)", +"G13.PatMod.ViewDirection", "former View Direction (0013,1046)", + +"G19.Acq1.CM.NetFrequency", "Net Frequency (0019,1010) in Hz", +"G19.Acq1.CM.MeasurementMode.M", +"Measurement Mode (0019,1020) - main: ADJUstment | EXAMination | TEST", +"G19.Acq1.CM.MeasurementMode.S", +"Measurement Mode (0019,1020) - subreason", +"G19.Acq1.CM.CalculationMode.M", +"CalculationMode (0019,1030) - main: NONE | A| PC | PU", +"G19.Acq1.CM.CalculationMode.S", +"Calculation Mode (0019,1030) - sub: NONE |BSP | IRS | SUN | VAX", +"G19.Acq1.CM.NoiseLevel", "Noise Level (0019,1050)", +"G19.Acq1.CM.NumberOfDataBytes", "Number of Data Bytes (0019,1060)", + +"G19.Acq2.Ct.SourceSideCollimatorAperture", +"Source Side Collimator Aperture (0019,1110) in mm", +"G19.Acq2.Ct.DetectorSideCollimatorAperture", +"Detector Side Collimator Aperture (0019,1111) in mm", +"G19.Acq2.Ct.ExposureTime", "current Exposure Time (0019,1120) in msec", +"G19.Acq2.Ct.Exposure", "current Exposure (0019,1121) in mAs", +"G19.Acq2.Ct.GeneratorPower", "current Generator Power (0019,1125) in kW", +"G19.Acq2.Ct.GeneratorVoltage", "current Generator Voltage (0019,1126) in kV", +"G19.Acq2.Ct.GeneratorVoltageDual", "second value of Generator Voltage (0019,1126) in kV", +"G19.Acq2.Ct.MasterControlMask", "Master Control Mask (0019,1140)", +"G19.Acq2.Ct.ProcessingMask", "Processing Mask (0019,1142)", +"G19.Acq2.Ct.NumberOfVirtuellChannels", "Number of Virtuell Channels (0019,1162)", +"G19.Acq2.Ct.NumberOfReadings", "Number of Readings (0019,1170)", +"G19.Acq2.Ct.NumberOfProjections", "Number of Projections (0019,1174)", +"G19.Acq2.Ct.NumberOfBytes", "Number of Bytes (0019,1175)", +"G19.Acq2.Ct.ReconstructionAlgorithmSet", +"Reconstruction Algorithm Set (0019,1180): smooth, standard, sharp RPF number", +"G19.Acq2.Ct.ReconstructionAlgorithmIndex", "Reconstruction Algorithm Index (0019,1181)", +"G19.Acq2.Ct.RegenerationSoftwareVersion", "Regeneration Software Version (0019,1182)", + +"G19.Acq2.Mr.TotalMeasurementTime", "Total Measurement Time - nominal (0019,1210) in sec", +"G19.Acq2.Mr.TotalMeasurementTimeCur", "Total Measurement Time - current (0019,1211) in sec", +"G19.Acq2.Mr.StartDelayTime", "Start Delay Time (0019,1212) in sec", +"G19.Acq2.Mr.DwellTime", "Dwell Time (0019,1213) in usec", +"G19.Acq2.Mr.NumberOfPhases", "Number of Phases (0019,1214)", +"G19.Acq2.Mr.SequenceControlMask", "Sequence Control Mask (0019,1216)", +"G19.Acq2.Mr.MeasurementStatusMask", "Measurement Status Mask (0019,1218)", +"G19.Acq2.Mr.NumberOfFourierLinesNominal", "nominal Number of Fourier Lines (0019,1220)", +"G19.Acq2.Mr.NumberOfFourierLinesCurrent", "current Number of Fourier Lines (0019,1221)", +"G19.Acq2.Mr.NumberOfFourierLinesAfterZero", "Number of Fourier Lines after Zero (0019,1226)", +"G19.Acq2.Mr.FirstMeasuredFourierLine", "First Measured Fourier Line (0019,1228)", +"G19.Acq2.Mr.AcquisitionColumns", "Acquisition Columns (0019,1230)", +"G19.Acq2.Mr.ReconstructionColumns", "Reconstruction Columns (0019,1231)", +"G19.Acq2.Mr.NumberOfAverages", "current Number of Averages (0019,1250)", +"G19.Acq2.Mr.FlipAngle", "Flip Angle (0019,1260) in degree", +"G19.Acq2.Mr.NumberOfPrescans", "Number of Prescans (0019,1270) in main loop", +"G19.Acq2.Mr.FilterTypeRawData", +"Filter Type Raw Data (0019,1281): NONE | EXTERNAL | FERMI | GAUSS |HANNING", +"G19.Acq2.Mr.FilterParameterRawData.Value1", "Filter Parameter Raw Data (0019,1282)", +"G19.Acq2.Mr.FilterTypeImageData", +"Filter Type Image Data (0019,1283): NONE | NO1", +"G19.Acq2.Mr.FilterParameterImageData.Value1", "Filter Parameter Image Data (0019,1284)", +"G19.Acq2.Mr.FilterTypePhaseCorrection", +"Filter Type Phase Correction (0019,1285): NONE | EXTERNAL | FERMI | GAUSS |HANNING", +"G19.Acq2.Mr.FilterParameterPhaseCorrection", "Filter Parameter Phase Correction (0019,1286)", +"G19.Acq2.Mr.NumberOfSaturationRegions", "Number of Saturation Regions (0019,1290)", +"G19.Acq2.Mr.ImageRotationAngle", +"Image Rotation Angle (0019,1294) for readout direction in radiant", +"G19.Acq2.Mr.CoilIdMask", "Coil ID Mask (0019,1296)", +"G19.Acq2.Mr.CoilPosition.Sag", "Coil Position (0019,1298) vector (sag, cor, tra) in mm", +"G21.Rel3.Mr.EpiReconstructionPhase", "EPI Reconstruction Phase (0019,12A0)", +"G21.Rel3.Mr.EpiReconstructionSlope", "EPI Reconstruction Slope (0019,12A1)", + +"G19.Acq3.Ct.DistanceSourceToSourceSideCollimator", +"Distance Source to Source Side Collimator (0019,1310) in mm", +"G19.Acq3.Ct.DistanceSourceToDetectorSideCollimator", +"Distance Source to Detector Side Collimator (0019,1311) in mm", +"G19.Acq3.Ct.NumberOfPossibleChannels", "Number of Possible Channels (0019,1320)", +"G19.Acq3.Ct.MeanChannelNumber", "Mean Channel Number (0019,1321)", +"G19.Acq3.Ct.DetectorSpacing", "Detector Spacing (0019,1322) in minutes", +"G19.Acq3.Ct.DetectorCenter", "Detector Center (0019,1323) in channels as real", +"G19.Acq3.Ct.ReadingIntegrationTime", "Reading Integration Time (0019,1324) in ms", +"G19.Acq3.Ct.DetectorAlignment", "Detector Alignment (0019,1350) in mm", +"G19.Acq3.Ct.FocusAlignment", "Focus Alignment (0019,1360) in channel digits", +"G19.Acq3.Ct.FocalSpotDeflectionAmplitude", +"Focal Spot Deflection Amplitude (0019,1365) in DMS digits", +"G19.Acq3.Ct.FocalSpotDeflectionPhase", +"Focal Spot Deflection Phase (0019,1366) in DMS digits", +"G19.Acq3.Ct.FocalSpotDeflectionOffset", +"Focal Spot Deflection Offset (0019,1367) in DMS digits", +"G19.Acq3.Ct.WaterScalingFactor", "Water Scaling Factor (0019,1370)", +"G19.Acq3.Ct.InterpolationFactor", "Interpolation Factor (0019,1371)", +"G19.Acq3.Ct.PatientRegion", "Patient Region (0019,1380): BODY | HEAD", +"G19.Acq3.Ct.PatientPhaseOfLife", "Patient Phase of Life (0019,1382): ADULT | CHILD", + +"G19.Acq3.Mr.MagneticFieldStrength", "Magnetic Field Strength (0019,1412) in T", +"G19.Acq3.Mr.ADCVoltage", "ADC Voltage (0019,1414) in V", +"G19.Acq3.Mr.ADCOffset", "ADC Offset (0019,1416): real value, imaginary value", +"G19.Acq3.Mr.TransmitterAmplitude", "Transmitter Amplitude (0019,1420) in V", +"G19.Acq3.Mr.NumberOfTransmitterAmplitudes", "Number of Transmitter Amplitudes (0019,1421)", +"G19.Acq3.Mr.TransmitterAttenuator", "Transmitter Attenuator (0019,1422) in dB", +"G19.Acq3.Mr.TransmitterCalibration", "Transmitter Calibration (0019,1424) in V", +"G19.Acq3.Mr.TransmitterReference", "Transmitter Reference (0019,1426) in V", +"G19.Acq3.Mr.ReceiverTotalGain", "Receiver Total Gain (0019,1450) in dB", +"G19.Acq3.Mr.ReceiverAmplifierGain", "Receiver Amplifier Gain (0019,1451) in dB", +"G19.Acq3.Mr.ReceiverPreamplifierGain", "Receiver Preamplifier Gain (0019,1452) in dB", +"G19.Acq3.Mr.ReceiverCableAttenuation", "Receiver Cable Attenuation (0019,1454) in dB", +"G19.Acq3.Mr.ReceiverReferenceGain", "Receiver Reference Gain (0019,1455) in dB", +"G19.Acq3.Mr.ReceiverFilterFrequency", "Receiver Filter Frequency (0019,1456) in Hz", +"G19.Acq3.Mr.ReconstructionScaleFactor", "Reconstruction Scale Factor (0019,1460)", +"G19.Acq3.Mr.ReferenceScaleFactor", "Reference Scale Factor (0019,1462)", +"G19.Acq3.Mr.PhaseGradientAmplitude", "Phase Gradient Amplitude (0019,1470) in mT/m", +"G19.Acq3.Mr.ReadoutGradientAmplitude", "Readout Gradient Amplitude (0019,1471) in mT/m", +"G19.Acq3.Mr.SelectionGradientAmplitude", "Selection Gradient Amplitude (0019,1472) in mT/m", +"G19.Acq3.Mr.GradientDelayTime.X", "Gradient Delay Time (0019,1480) in usec", +"G19.Acq3.Mr.TotalGradientDelayTime", "Total Gradient Delay Time (0019,1482) in usec", +"G19.Acq3.Mr.SensitivityCorrectionLabel", "Sensitivity Correction Label (0019,1490)", +"G19.Acq3.Mr.RfWatchdogMask", "RF Watchdog Mask (0019,14A0)", +"G19.Acq3.Mr.RfPowerErrorIndicator", "RF Power Error Indicator (0019,14A2)", +"G19.Acq3.Mr.SarWholeBody.Lim", +"Specific Absorption Rate (SAR) - whole body (0019,14A5) - limit in W/kg", +"G19.Acq3.Mr.SarWholeBody.Cal", +"Specific Absorption Rate (SAR) - whole body (0019,14A5) - calculated value in W/kg", +"G19.Acq3.Mr.SarWholeBody.Det", +"Specific Absorption Rate (SAR) - whole body (0019,14A5) - detected value in W/kg", +"G19.Acq3.Mr.Sed.Lim", +"Specific Energy Dose (SED) (0019,14A6) - limit in Wmin/kg", +"G19.Acq3.Mr.Sed.Cal", +"Specific Energy Dose (SED) (0019,14A6) - calculated value in Wmin/kg", +"G19.Acq3.Mr.Sed.Det", +"Specific Energy Dose (SED) (0019,14A6) - detected value in Wmin/kg", +"G19.Acq3.Mr.AdjustmentStatusMask", "Adjustment Status Mask (0019,14B0)", +"G21.Rel3.Mr.EpiCapacity", "EPI Capacity (0019,14C1) in nF", +"G21.Rel3.Mr.EpiInductance", "EPI Inductance (0019,14C2) in uH", +"G21.Rel3.Mr.EpiSwitchConfigurationCode", +"EPI Switch Configuration Code (0019,14C3): 0 (without) | 1 (two) | 2 (four)", +"G21.Rel3.Mr.EpiSwitchHardwareCode", +"EPI Switch Hardware Code (0019,14C4): 0 (no) | 1 (thyristor) | 2 (transistor)", +"G21.Rel3.Mr.EpiSwitchDelayTime", "EPI Switch Delay Time (0019,14C5) in ns", + +"G19.Acq4.CM.ParameterFileName", "Parameter File Name (0019,1510)", +"G19.Acq4.CM.SequenceFileName", "Sequence File Name (0019,1511)", +"G19.Acq4.CM.SequenceFileOwner", "Sequence File Owner (0019,1512)", +"G19.Acq4.CM.SequenceDescription", "Sequence Description (0019,1513)", +"G21.Rel3.Mr.EpiFileName", "EPI File Name (0019,1514)", + +"G21.Rel1.CM.Target.X", "Target (0021,1011)", +"G21.Rel1.CM.RoiMask", "ROI Mask (0021,1020)", +"G21.Rel1.CM.FoV.Height", "Field of View (0021,1120) - height in mm", +"G21.Rel1.CM.FoV.Width", "Field of View (0021,1120) - width in mm", +"G21.Rel1.CM.ImageMagnificationFactor", "Image Magnification Factor (0021,1122)", +"G21.Rel1.CM.ViewDirection", +"View Direction (0021,1130): HEAD | FEET | AtoP | PtoA | RtoL | LtoR", +"G21.Rel1.CM.RestDirection", "Rest Direction (0021,1132): HEAD | FEET", +"G21.Rel1.CM.ImagePosition.Sag", "Image Position (0021,1160) vector (sag, cor, tra) in mm", +"G21.Rel1.CM.ImageNormal.Sag", "Image Normal (0021,1161) cosinus directions (sag, cor, tra)", +"G21.Rel1.CM.ImageDistance", "Image Distance (0021,1163) in mm", +"G21.Rel1.CM.ImagePositioningHistoryMask", "Image Positioning History Mask (0021,1165)", +"G21.Rel1.CM.ImageRow.Sag", "Image Row (0021,116A) cosinus directions (sag, cor, tra)", +"G21.Rel1.CM.ImageColumn.Sag", "Image Column (0021,116B) cosinus directions (sag, cor, tra)", +"G21.Rel1.CM.PatientOrientationSet1.Y", +"Patient Orientation Set 1 (0021,1170): Y = up, X = left, Z = back", +"G21.Rel1.CM.PatientOrientationSet2.Y", +"Patient Orientation Set 2 (0021,1171): Y = down, X = right, Z = front", +"G21.Rel1.CM.StudyName", "Study Name (0021,1180)", +"G21.Rel1.CM.StudyType", "Study Type (0021,1182): CRE | MEA | MIP | MPR | RAW", + +"G21.Rel2.Ct.RotationAngle", "Rotation Angle (0021,1210) in degree", +"G21.Rel2.Ct.StartAngle", "Start Angle (0021,1211) in degree (0.0 == 03:00)", +"G21.Rel2.Ct.TubePosition", "Tube Position (0021,1230) in degree (0.0 == 03:00)", +"G21.Rel2.Ct.LengthOfTopogram", "Length of Topogram (0021,1232) in mm", +"G21.Rel2.Ct.CorrectionFactor", "Correction Factor (0021,1234) for topogram in x-direction", +"G21.Rel2.Ct.MaximumTablePosition", "Maximum Table Position (0021,1236) in mm", +"G21.Rel2.Ct.TableMoveDirectionCode", "Tabel Move Direction Code (0019,1240): -1 (into) | 1 (out)", +"G21.Rel2.Ct.VoiStartRow", "VOI Start Row (0021,1245)", +"G21.Rel2.Ct.VoiStopRow", "VOI Stop Row (0021,1246)", +"G21.Rel2.Ct.VoiStartColumn", "VOI Start Column (0021,1247)", +"G21.Rel2.Ct.VoiStopColumn", "VOI Stop Column (0021,1248)", +"G21.Rel2.Ct.VoiStartSlice", "VOI Start Slice (0021,1249)", +"G21.Rel2.Ct.VoiStopSlice", "VOI Stop Slice (0021,124A)", +"G21.Rel2.Ct.VectorStartRow", "Vector Start Row(0021,1250)", +"G21.Rel2.Ct.VectorRowStep", "Vector Row Step(0021,1251)", +"G21.Rel2.Ct.VectorStartColumn", "Vector Start Column(0021,1252)", +"G21.Rel2.Ct.VectorColumnStep", "Vector Column Step(0021,1253)", +"G21.Rel2.Ct.RangeTypeCode", "Range Type Code (0021,1260): 0 (sector) | 1 (parallel)", +"G21.Rel2.Ct.ReferenceTypeCode", +"Reference Type Code (0021,1262): 0 (topo) | 1 (tra) | 2 (sag) | 3 (cor)", +"G21.Rel2.Ct.ObjectOrientation.Phi", +"Object Orientation (0021,1270) vector (phi, theta, 1.0) in degree", +"G21.Rel2.Ct.LightOrientation.Phi", +"Light Orientation (0021,1272) vector (phi, theta, 1.0) in degree", +"G21.Rel2.Ct.LightBrightness", "Light Brightness (0021,1275)", +"G21.Rel2.Ct.LightContrast", "Light Contrast (0021,1276)", +"G21.Rel2.Ct.OverlayThreshold.LowerBoundary", +"Overlay Threshold (0021,127A) boundaries (lower, upper)", +"G21.Rel2.Ct.SurfaceThreshold.LowerBoundary", +"Surface Threshold (0021,127B) boundaries (lower, upper)", +"G21.Rel2.Ct.GreyScaleThreshold.LowerBoundary", +"Grey Scale Threshold (0021,127C) boundaries (lower, upper)", + + +"G21.Rel2.Mr.PhaseCorRowSeq", "Phase Corrections Rows (0021,1320) of actual sequence", +"G21.Rel2.Mr.PhaseCorColSeq", "Phase Corrections Columns (0021,1321) of actual sequence", +"G21.Rel2.Mr.PhaseCorRowRec", "Phase Corrrections Rows (0021,1322) of actual reconstruction", +"G21.Rel2.Mr.PhaseCorColRec", "Phase Corrrections Columns (0021,1324) of actual reconstruction", +"G21.Rel2.Mr.NumberOf3DRawPartNom", "nominal Number of 3D Raw Partitions (0021,1330)", +"G21.Rel2.Mr.NumberOf3DRawPartCur", "current Number of 3D Raw Partitions (0021,1331)", +"G21.Rel2.Mr.NumberOf3DImaPart", "Number of 3D Image Partitions (0021,1334)", +"G21.Rel2.Mr.Actual3DImaPartNumber", "Actual 3D Image Partitions Number (0021,1336)", +"G21.Rel2.Mr.SlabThickness", "Slab Thickness (0021,1339) in mm", +"G21.Rel2.Mr.NumberOfSlicesNom", "nominal Number of Slices (0021,1340)", +"G21.Rel2.Mr.NumberOfSlicesCur", "current Number of Slices (0021,1341)", +"G21.Rel2.Mr.CurrentSliceNumber", "Current Slice Number (0021,1342)", +"G21.Rel2.Mr.CurrentGroupNumber", "Current Group Number (0021,1343)", +"G21.Rel2.Mr.CurrentSliceDistanceFactor", "Current Slice Distance Factor (0021,1344)", +"G21.Rel2.Mr.MipStartRow", "MIP Start Row (0021,1345)", +"G21.Rel2.Mr.MipStopRow", "MIP Stop Row (0021,1346)", +"G21.Rel2.Mr.MipStartColumn", "MIP Start Column (0021,1347)", +"G21.Rel2.Mr.MipStopColumn", "MIP Stop Column (0021,1348)", +"G21.Rel2.Mr.MipStartSlice", "MIP Start Slice (0021,1349)", +"G21.Rel2.Mr.MipStopSlice", "MIP StopSlice (0021,134A)", +"G21.Rel2.Mr.OrderOfSlices", +"Order of Slices (0021,134F): NONE | ASCENDING | DECREASING | FREE | INTERLEAVED", +"G21.Rel2.Mr.SignalMask", "Signal Mask (0021,1350)", +"G21.Rel2.Mr.DelayAfterTrigger", "Delay After Trigger (0021,1352) in msec", +"G21.Rel2.Mr.RRInterval", "RR Interval (0021,1353) in msec", +"G21.Rel2.Mr.NumberOfTriggerPulses", "Number of Trigger Pulses (0021,1354)", +"G21.Rel2.Mr.RepetitionTime", "effective Repetition Time (0021,1356) in msec", +"G21.Rel2.Mr.GatePhase", "Gate Phase (0021,1357): EXPIRATION | INSPIRATION", +"G21.Rel2.Mr.GateThreshold", "Gate Threshold (0021,1358) in %", +"G21.Rel2.Mr.GateRatio", "Gate Ratio (0021,1359) in %", +"G21.Rel2.Mr.NumberOfInterpolatedImages", "Number of Interpolated Images (0021,1360)", +"G21.Rel2.Mr.NumberOfEchoes", "total Number of Echoes (0021,1370)", +"G21.Rel2.Mr.SecondEchoTime", "second Echo Time (0021,1372) in msec", +"G21.Rel2.Mr.SecondRepetitionTime", "second Repetition Time (0021,1373) in msec", +"G21.Rel2.Mr.CardiacCode", "Cardiac Code (0021,1380)", + +"G21.Rel3.Ct.CreationMask", "Creation Mask - Service (0021,2210)", +"G21.Rel3.Ct.EvaluationMask", "Evaluation Mask - Service (0021,2220)", +"G21.Rel3.Ct.ExtendedProcessingMask", "Extended Processing Mask (0021,2230)", + +"G29.Pre.WindowStyle", +"Window Style (0029,1110): NONE | DOUBLE | HIGH | STD 1 | STD 2", +"G29.Pre.PixelQualityCode.Min", +"Pixel Quality Code (0029,1120): NONE | ESTIMATED | EXACT", +"G29.Pre.PixelQualityValue.Min", "Pixel Quality Value (0029,1122)", +"G29.Pre.ArchiveCode", "Archive Code (0029,1150): NOT | MARKED | DONE", +"G29.Pre.ExposureCode", "Exposure Code (0029,1151): NOT | MARKED | DONE", +"G29.Pre.SortCode", +"Sort Code (0029,1152): 0 (CH) | 1 (AN) | 2 (TE) | 3 (TR) | 4 (TD) | 5 (CA)", +"G29.Pre.Splash", "Splash (0029,1160)", + +"G51.Txt_buf", "Image Text (0051,1010)", +"Gap.Fill_buf", "reserved: gap to fill Internal Header to kByte border", +#endif
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_head_type.h @@ -0,0 +1,207 @@ +/*[- HEADER FILE -------------------------------------------------------------------------*/ +/* + Name: ds_head_type.h + + Description: The header file defines the data set basic groups as unions for + internal use (internal header). + Unions are used to overlay a buffer and the group structure for more flexi- + bility and compatibility. To expand a group the new item must only be + defined at the structure end in header file "ds_head_acr_groups_types.h" or + "ds_head_shadow_groups_types.h". + + + Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 +*/ +/*]-----------------------------------------------------------------------------------------*/ + +#ifndef DS_HEAD_TYPE +#define DS_HEAD_TYPE + +typedef union group_0008_tag +{ + char Ide_buf[LENGTH_GROUP_0008]; /* fill-in */ + acr_identifying_t Ide; /* work area */ +} group_0008_t; + + + +typedef union group_0009_tag +{ + char Ide_buf[LENGTH_GROUP_0009]; /* fill-in */ + shadow_identifying_t Ide; /* work area */ +} group_0009_t; + + + +typedef union group_0010_tag +{ + char Pat_buf[LENGTH_GROUP_0010]; /* fill-in */ + acr_patient_t Pat; /* work area */ +} group_0010_t; + + + +typedef union group_0011_tag +{ + char Pat_buf[LENGTH_GROUP_0011]; /* fill-in */ + shadow_patient_t Pat; /* work area */ +} group_0011_t; + + + +typedef union group_0013_tag +{ + char PatMod_buf[LENGTH_GROUP_0013]; /* fill-in */ + shadow_patient_modify_t PatMod; /* work area */ +} group_0013_t; + + + +typedef union group_0018_tag +{ + char Acq_buf[LENGTH_GROUP_0018]; /* fill-in */ + acr_acquisition_t Acq; /* work area */ +} group_0018_t; + + + +typedef union group_0019_part1_tag +{ + char Acq1_buf[LENGTH_GROUP_0019_PART1]; /* fill-in */ + shadow_acquisition_cms_t CM; /* work area */ +} group_0019_part1_t; + +typedef union group_0019_part2_tag +{ + char Acq2_buf[LENGTH_GROUP_0019_PART2]; /* fill-in */ + shadow_acquisition_ct_t Ct; /* CT common work area */ + shadow_acquisition_mr_t Mr; /* MR common work area */ +} group_0019_part2_t; + +typedef union group_0019_part3_tag +{ + char Acq3_buf[LENGTH_GROUP_0019_PART3]; /* fill-in */ + shadow_acquisition_ct_conf_t Ct; /* CT configuration work area */ + shadow_acquisition_mr_conf_t Mr; /* MR configuration work area */ +} group_0019_part3_t; + +typedef union group_0019_part4_tag +{ + char Acq4_buf[LENGTH_GROUP_0019_PART4]; /* fill-in */ + shadow_acquisition_acq_t CM; /* work area */ +} group_0019_part4_t; + +typedef struct group_0019_tag +{ + group_0019_part1_t Acq1; /* CMS subgroup */ + group_0019_part2_t Acq2; /* common subgroup */ + group_0019_part3_t Acq3; /* config. and adjust subgroup */ + group_0019_part4_t Acq4; /* acquisition subgroup */ +} group_0019_t; + + + +typedef union group_0020_tag +{ + char Rel_buf[LENGTH_GROUP_0020]; /* fill-in */ + acr_relationship_t Rel; /* work area */ +} group_0020_t; + + + +typedef union group_0021_part1_tag +{ + char Rel1_buf[LENGTH_GROUP_0021_PART1]; /* fill-in */ + shadow_relationship_med_cms_t CM; /* work area */ +} group_0021_part1_t; + +typedef union group_0021_part2_tag +{ + char Rel2_buf[LENGTH_GROUP_0021_PART2]; /* fill-in */ + shadow_relationship_ct_t Ct; /* CT common work area */ + shadow_relationship_mr_t Mr; /* MR common work area */ +} group_0021_part2_t; + +typedef union group_0021_part3_tag +{ + char Rel2_buf[LENGTH_GROUP_0021_PART3]; /* fill-in */ + shadow_relationship_ct_spe_t Ct; /* CT special work area */ + shadow_relationship_mr_spe_t Mr; /* MR special work area */ +} group_0021_part3_t; + +typedef struct group_0021_tag +{ + group_0021_part1_t Rel1; /* CMS subgroup */ + group_0021_part2_t Rel2; /* common subgroup */ + group_0021_part3_t Rel3; /* special subgroup */ +} group_0021_t; + + + +typedef union group_0028_tag +{ + char Pre_buf[LENGTH_GROUP_0028]; /* fill-in */ + acr_presentation_t Pre; /* work area */ +} group_0028_t; + + + +typedef union group_0029_tag +{ + char Pre_buf[LENGTH_GROUP_0029]; /* fill-in */ + shadow_presentation_t Pre; /* work area */ +} group_0029_t; + + + +typedef union group_0051_tag +{ + char Txt_buf[LENGTH_GROUP_0051]; /* fill-in */ + image_text_t Txt; /* work area */ +} group_0051_t; + + + +typedef union fill_to_border_tag +{ + char Fill_buf[LENGTH_TO_FILL_K_BORDER]; /* fill-in */ +} fill_to_border_t; + + + + +/*******************/ +/* internal header */ +/*******************/ + +typedef struct header_tag +{ + group_0008_t G08; /* Identifying Information */ + group_0009_t G09; + group_0010_t G10; /* Patient Information */ + group_0011_t G11; + group_0013_t G13; /* Patient Modify Information */ + group_0018_t G18; /* Acquisition Information */ + group_0019_t G19; + group_0020_t G20; /* Relationship Information */ + group_0021_t G21; + group_0028_t G28; /* Image Presentation Information */ + group_0029_t G29; + group_0051_t G51; /* Image Text */ +} header_t; + +#endif + + +/*==========================================================================================*/ + +#ifdef DS_STC_TOOL + +char *_STC_HELP[] = +{ +#include <ds_head_acr_groups_types.h> +#include <ds_head_shadow_groups_types.h> + 0 +}; + +#endif
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_include_files.h @@ -0,0 +1,42 @@ +/*[- HEADER FILE -------------------------------------------------------------------------*/ +/* + Name: ds_include_files.h + + Description: The header file include all necessary header files for data set library + functions. The "#ifndef XXX" - "#endif" construct in the different header + files avoid a multiple type, parameter, constant or macro definition. + Don't change the order because the header files are implemented as a + hierachy and so not indepent of calling order. + + + Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 +*/ +/*]-----------------------------------------------------------------------------------------*/ + +#ifndef DS_INCLUDE_FILES +#define DS_INCLUDE_FILES + + +/* PRECOMILER: sepecial include files */ +#include <STC_Common_Status.h> + + +/* PRECOMILER: data set library include files */ +#include <ds_head_constants.h> +#include <ds_transformation.h> + +#include <ds_messages.h> + +#include <ds_date.h> + +#include <ds_head_basic_types.h> +#include <ds_head_acr_groups_types.h> +#include <ds_head_shadow_groups_types.h> +#include <ds_head_image_text_type.h> +#include <ds_head_type.h> + +#include <ds_transformation_control.h> + +#include <ds_functions.h> + +#endif
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_messages.h @@ -0,0 +1,322 @@ +/*--------------------------------------------------- + * + * include ds_messages.h + * + *--------------------------------------------------- + */ +#define DS_RET_MORE_PARAMETER_AVAILABLE 6815752 /* (0x00680008) */ +#define DS_RET_MORE_PARAMETER_AVAILABLE_E 6815760 /* (0x00680010) */ +#define DS_RET_MORE_PARAMETER_AVAILABLE_U 6815768 /* (0x00680018) */ +#define DS_RET_MORE_PARAMETER_AVAILABLE_D 6815776 /* (0x00680020) */ +#define DS_RET_MORE_VALUES_AVAILABLE 6815784 /* (0x00680028) */ +#define DS_RET_MORE_VALUES_AVAILABLE_E 6815792 /* (0x00680030) */ +#define DS_RET_MORE_VALUES_AVAILABLE_U 6815800 /* (0x00680038) */ +#define DS_RET_MORE_VALUES_AVAILABLE_D 6815808 /* (0x00680040) */ +#define DS_RET_UNDEFINED 6815816 /* (0x00680048) */ +#define DS_RET_UNDEFINED_E 6815824 /* (0x00680050) */ +#define DS_RET_UNDEFINED_U 6815832 /* (0x00680058) */ +#define DS_RET_UNDEFINED_D 6815840 /* (0x00680060) */ +#define DS_RET_WITH_WARNING 6815848 /* (0x00680068) */ +#define DS_RET_WITH_WARNING_E 6815856 /* (0x00680070) */ +#define DS_RET_WITH_WARNING_U 6815864 /* (0x00680078) */ +#define DS_RET_WITH_WARNING_D 6815872 /* (0x00680080) */ +#define DS_RET_NORMAL 6817345 /* (0x00680641) */ +#define DS_RET_NORMAL_E 6817353 /* (0x00680649) */ +#define DS_RET_NORMAL_U 6817361 /* (0x00680651) */ +#define DS_RET_NORMAL_D 6817369 /* (0x00680659) */ +#define DS_RET_BAD_DATES 6818946 /* (0x00680c82) */ +#define DS_RET_BAD_DATES_E 6818954 /* (0x00680c8a) */ +#define DS_RET_BAD_DATES_U 6818962 /* (0x00680c92) */ +#define DS_RET_BAD_DATES_D 6818970 /* (0x00680c9a) */ +#define DS_RET_BAD_NUMBER 6818978 /* (0x00680ca2) */ +#define DS_RET_BAD_NUMBER_E 6818986 /* (0x00680caa) */ +#define DS_RET_BAD_NUMBER_U 6818994 /* (0x00680cb2) */ +#define DS_RET_BAD_NUMBER_D 6819002 /* (0x00680cba) */ +#define DS_RET_BUILD_UP_FAILED 6819010 /* (0x00680cc2) */ +#define DS_RET_BUILD_UP_FAILED_E 6819018 /* (0x00680cca) */ +#define DS_RET_BUILD_UP_FAILED_U 6819026 /* (0x00680cd2) */ +#define DS_RET_BUILD_UP_FAILED_D 6819034 /* (0x00680cda) */ +#define DS_RET_CMS_TO_NEMA_NOT_POSSIBLE 6819042 /* (0x00680ce2) */ +#define DS_RET_CMS_TO_NEMA_NOT_POSSIBLE_E 6819050 /* (0x00680cea) */ +#define DS_RET_CMS_TO_NEMA_NOT_POSSIBLE_U 6819058 /* (0x00680cf2) */ +#define DS_RET_CMS_TO_NEMA_NOT_POSSIBLE_D 6819066 /* (0x00680cfa) */ +#define DS_RET_CMS_TO_SOM0_NOT_POSSIBLE 6819074 /* (0x00680d02) */ +#define DS_RET_CMS_TO_SOM0_NOT_POSSIBLE_E 6819082 /* (0x00680d0a) */ +#define DS_RET_CMS_TO_SOM0_NOT_POSSIBLE_U 6819090 /* (0x00680d12) */ +#define DS_RET_CMS_TO_SOM0_NOT_POSSIBLE_D 6819098 /* (0x00680d1a) */ +#define DS_RET_DAY_OUT_OF_RANGE 6819106 /* (0x00680d22) */ +#define DS_RET_DAY_OUT_OF_RANGE_E 6819114 /* (0x00680d2a) */ +#define DS_RET_DAY_OUT_OF_RANGE_U 6819122 /* (0x00680d32) */ +#define DS_RET_DAY_OUT_OF_RANGE_D 6819130 /* (0x00680d3a) */ +#define DS_RET_FRACTION_OUT_OF_RANGE 6819138 /* (0x00680d42) */ +#define DS_RET_FRACTION_OUT_OF_RANGE_E 6819146 /* (0x00680d4a) */ +#define DS_RET_FRACTION_OUT_OF_RANGE_U 6819154 /* (0x00680d52) */ +#define DS_RET_FRACTION_OUT_OF_RANGE_D 6819162 /* (0x00680d5a) */ +#define DS_RET_HOUR_OUT_OF_RANGE 6819170 /* (0x00680d62) */ +#define DS_RET_HOUR_OUT_OF_RANGE_E 6819178 /* (0x00680d6a) */ +#define DS_RET_HOUR_OUT_OF_RANGE_U 6819186 /* (0x00680d72) */ +#define DS_RET_HOUR_OUT_OF_RANGE_D 6819194 /* (0x00680d7a) */ +#define DS_RET_INVALID_IMAGE_AXIS 6819202 /* (0x00680d82) */ +#define DS_RET_INVALID_IMAGE_AXIS_E 6819210 /* (0x00680d8a) */ +#define DS_RET_INVALID_IMAGE_AXIS_U 6819218 /* (0x00680d92) */ +#define DS_RET_INVALID_IMAGE_AXIS_D 6819226 /* (0x00680d9a) */ +#define DS_RET_INVALID_IMAGE_GRAPHICS 6819234 /* (0x00680da2) */ +#define DS_RET_INVALID_IMAGE_GRAPHICS_E 6819242 /* (0x00680daa) */ +#define DS_RET_INVALID_IMAGE_GRAPHICS_U 6819250 /* (0x00680db2) */ +#define DS_RET_INVALID_IMAGE_GRAPHICS_D 6819258 /* (0x00680dba) */ +#define DS_RET_INVALID_IMAGE_PLACE 6819266 /* (0x00680dc2) */ +#define DS_RET_INVALID_IMAGE_PLACE_E 6819274 /* (0x00680dca) */ +#define DS_RET_INVALID_IMAGE_PLACE_U 6819282 /* (0x00680dd2) */ +#define DS_RET_INVALID_IMAGE_PLACE_D 6819290 /* (0x00680dda) */ +#define DS_RET_INVALID_IMAGE_TEXT 6819298 /* (0x00680de2) */ +#define DS_RET_INVALID_IMAGE_TEXT_E 6819306 /* (0x00680dea) */ +#define DS_RET_INVALID_IMAGE_TEXT_U 6819314 /* (0x00680df2) */ +#define DS_RET_INVALID_IMAGE_TEXT_D 6819322 /* (0x00680dfa) */ +#define DS_RET_INVALID_INTERFACE 6819330 /* (0x00680e02) */ +#define DS_RET_INVALID_INTERFACE_E 6819338 /* (0x00680e0a) */ +#define DS_RET_INVALID_INTERFACE_U 6819346 /* (0x00680e12) */ +#define DS_RET_INVALID__INTERFACE_D 6819354 /* (0x00680e1a) */ +#define DS_RET_INVALID_MATRIX 6819362 /* (0x00680e22) */ +#define DS_RET_INVALID_MATRIX_E 6819370 /* (0x00680e2a) */ +#define DS_RET_INVALID_MATRIX_U 6819378 /* (0x00680e32) */ +#define DS_RET_INVALID_MATRIX_D 6819386 /* (0x00680e3a) */ +#define DS_RET_INVALID_VIEW_DIRECTION 6819394 /* (0x00680e42) */ +#define DS_RET_INVALID_VIEW_DIRECTION_E 6819402 /* (0x00680e4a) */ +#define DS_RET_INVALID_VIEW_DIRECTION_U 6819410 /* (0x00680e52) */ +#define DS_RET_INVALID_VIEW_DIRECTION_D 6819418 /* (0x00680e5a) */ +#define DS_RET_ITEM_NOT_FOUND 6819426 /* (0x00680e62) */ +#define DS_RET_ITEM_NOT_FOUND_E 6819434 /* (0x00680e6a) */ +#define DS_RET_ITEM_NOT_FOUND_U 6819442 /* (0x00680e72) */ +#define DS_RET_ITEM_NOT_FOUND_D 6819450 /* (0x00680e7a) */ +#define DS_RET_LENGTH_NOT_EVEN 6819458 /* (0x00680e82) */ +#define DS_RET_LENGTH_NOT_EVEN_E 6819466 /* (0x00680e8a) */ +#define DS_RET_LENGTH_NOT_EVEN_U 6819474 /* (0x00680e92) */ +#define DS_RET_LENGTH_NOT_EVEN_D 6819482 /* (0x00680e9a) */ +#define DS_RET_LENGTH_OUT_OF_RANGE 6819490 /* (0x00680ea2) */ +#define DS_RET_LENGTH_OUT_OF_RANGE_E 6819498 /* (0x00680eaa) */ +#define DS_RET_LENGTH_OUT_OF_RANGE_U 6819506 /* (0x00680eb2) */ +#define DS_RET_LENGTH_OUT_OF_RANGE_D 6819514 /* (0x00680eba) */ +#define DS_RET_LENGTH_TO_LESS 6819522 /* (0x00680ec2) */ +#define DS_RET_LENGTH_TO_LESS_E 6819530 /* (0x00680eca) */ +#define DS_RET_LENGTH_TO_LESS_U 6819538 /* (0x00680ed2) */ +#define DS_RET_LENGTH_TO_LESS_D 6819546 /* (0x00680eda) */ +#define DS_RET_MINUTE_OUT_OF_RANGE 6819554 /* (0x00680ee2) */ +#define DS_RET_MINUTE_OUT_OF_RANGE_E 6819562 /* (0x00680eea) */ +#define DS_RET_MINUTE_OUT_OF_RANGE_U 6819570 /* (0x00680ef2) */ +#define DS_RET_MINUTE_OUT_OF_RANGE_D 6819578 /* (0x00680efa) */ +#define DS_RET_MISSING_INFORMATION 6819586 /* (0x00680f02) */ +#define DS_RET_MISSING_INFORMATION_E 6819594 /* (0x00680f0a) */ +#define DS_RET_MISSING_INFORMATION_U 6819602 /* (0x00680f12) */ +#define DS_RET_MISSING_INFORMATION_D 6819610 /* (0x00680f1a) */ +#define DS_RET_MONTH_OUT_OF_RANGE 6819618 /* (0x00680f22) */ +#define DS_RET_MONTH_OUT_OF_RANGE_E 6819626 /* (0x00680f2a) */ +#define DS_RET_MONTH_OUT_OF_RANGE_U 6819634 /* (0x00680f32) */ +#define DS_RET_MONTH_OUT_OF_RANGE_D 6819642 /* (0x00680f3a) */ +#define DS_RET_NIL_VECTOR 6819650 /* (0x00680f42) */ +#define DS_RET_NIL_VECTOR_E 6819658 /* (0x00680f4a) */ +#define DS_RET_NIL_VECTOR_U 6819666 /* (0x00680f52) */ +#define DS_RET_NIL_VECTOR_D 6819674 /* (0x00680f5a) */ +#define DS_RET_NO_VALID_DATE 6819682 /* (0x00680f62) */ +#define DS_RET_NO_VALID_DATE_E 6819690 /* (0x00680f6a) */ +#define DS_RET_NO_VALID_DATE_U 6819698 /* (0x00680f72) */ +#define DS_RET_NO_VALID_DATE_D 6819706 /* (0x00680f7a) */ +#define DS_RET_NO_VALID_ITEM_VALUE 6819714 /* (0x00680f82) */ +#define DS_RET_NO_VALID_ITEM_VALUE_E 6819722 /* (0x00680f8a) */ +#define DS_RET_NO_VALID_ITEM_VALUE_U 6819730 /* (0x00680f92) */ +#define DS_RET_NO_VALID_ITEM_VALUE_D 6819738 /* (0x00680f9a) */ +#define DS_RET_NO_VALID_TIME 6819746 /* (0x00680fa2) */ +#define DS_RET_NO_VALID_TIME_E 6819754 /* (0x00680faa) */ +#define DS_RET_NO_VALID_TIME_U 6819762 /* (0x00680fb2) */ +#define DS_RET_NO_VALID_TIME_D 6819770 /* (0x00680fba) */ +#define DS_RET_NUMBER_NOT_EVEN 6819778 /* (0x00680fc2) */ +#define DS_RET_NUMBER_NOT_EVEN_E 6819786 /* (0x00680fca) */ +#define DS_RET_NUMBER_NOT_EVEN_U 6819794 /* (0x00680fd2) */ +#define DS_RET_NUMBER_NOT_EVEN_D 6819802 /* (0x00680fda) */ +#define DS_RET_NUMBER_OUT_OF_RANGE 6819810 /* (0x00680fe2) */ +#define DS_RET_NUMBER_OUT_OF_RANGE_E 6819818 /* (0x00680fea) */ +#define DS_RET_NUMBER_OUT_OF_RANGE_U 6819826 /* (0x00680ff2) */ +#define DS_RET_NUMBER_OUT_OF_RANGE_D 6819834 /* (0x00680ffa) */ +#define DS_RET_OUT_OF_FORMAT 6819842 /* (0x00681002) */ +#define DS_RET_OUT_OF_FORMAT_E 6819850 /* (0x0068100a) */ +#define DS_RET_OUT_OF_FORMAT_U 6819858 /* (0x00681012) */ +#define DS_RET_OUT_OF_FORMAT_D 6819866 /* (0x0068101a) */ +#define DS_RET_SECOND_OUT_OF_RANGE 6819874 /* (0x00681022) */ +#define DS_RET_SECOND_OUT_OF_RANGE_E 6819882 /* (0x0068102a) */ +#define DS_RET_SECOND_OUT_OF_RANGE_U 6819890 /* (0x00681032) */ +#define DS_RET_SECOND_OUT_OF_RANGE_D 6819898 /* (0x0068103a) */ +#define DS_RET_SEPARATE_FAILED 6819906 /* (0x00681042) */ +#define DS_RET_SEPARATE_FAILED_E 6819914 /* (0x0068104a) */ +#define DS_RET_SEPARATE_FAILED_U 6819922 /* (0x00681052) */ +#define DS_RET_SEPARATE_FAILED_D 6819930 /* (0x0068105a) */ +#define DS_RET_STRING_TO_LONG 6819938 /* (0x00681062) */ +#define DS_RET_STRING_TO_LONG_E 6819946 /* (0x0068106a) */ +#define DS_RET_STRING_TO_LONG_U 6819954 /* (0x00681072) */ +#define DS_RET_STRING_TO_LONG_D 6819962 /* (0x0068107a) */ +#define DS_RET_SYSTEM_ERROR 6819970 /* (0x00681082) */ +#define DS_RET_SYSTEM_ERROR_E 6819978 /* (0x0068108a) */ +#define DS_RET_SYSTEM_ERROR_U 6819986 /* (0x00681092) */ +#define DS_RET_SYSTEM_ERROR_D 6819994 /* (0x0068109a) */ +#define DS_RET_UNKNOWN_OVERLAY 6820002 /* (0x006810a2) */ +#define DS_RET_UNKNOWN_OVERLAY_E 6820010 /* (0x006810aa) */ +#define DS_RET_UNKNOWN_OVERLAY_U 6820018 /* (0x006810b2) */ +#define DS_RET_UNKNOWN_OVERLAY_D 6820026 /* (0x006810ba) */ +#define DS_RET_UNKNOWN_UPDATE_CODE 6820034 /* (0x006810c2) */ +#define DS_RET_UNKNOWN_UPDATE_CODE_E 6820042 /* (0x006810ca) */ +#define DS_RET_UNKNOWN_UPDATE_CODE_U 6820050 /* (0x006810d2) */ +#define DS_RET_UNKNOWN_UPDATE_CODE_D 6820058 /* (0x006810da) */ +#define DS_RET_UNKNOWN_HEADER 6820066 /* (0x006810e2) */ +#define DS_RET_UNKNOWN_HEADER_E 6820074 /* (0x006810ea) */ +#define DS_RET_UNKNOWN_HEADER_U 6820082 /* (0x006810f2) */ +#define DS_RET_UNKNOWN_HEADER_D 6820090 /* (0x006810fa) */ +#define DS_RET_UNKNOWN_UPDATE_MODE 6820098 /* (0x00681102) */ +#define DS_RET_UNKNOWN_UPDATE_MODE_E 6820106 /* (0x0068110a) */ +#define DS_RET_UNKNOWN_UPDATE_MODE_U 6820114 /* (0x00681112) */ +#define DS_RET_UNKNOWN_UPDATE_MODE_D 6820122 /* (0x0068111a) */ +#define DS_RET_UNKNOWN_VALUE_MODE 6820130 /* (0x00681122) */ +#define DS_RET_UNKNOWN_VALUE_MODE_E 6820138 /* (0x0068112a) */ +#define DS_RET_UNKNOWN_VALUE_MODE_U 6820146 /* (0x00681132) */ +#define DS_RET_UNKNOWN_VALUE_MODE_D 6820154 /* (0x0068113a) */ +#define DS_RET_VECTOR_NOT_DEFINED 6820162 /* (0x00681142) */ +#define DS_RET_VECTOR_NOT_DEFINED_E 6820170 /* (0x0068114a) */ +#define DS_RET_VECTOR_NOT_DEFINED_U 6820178 /* (0x00681152) */ +#define DS_RET_VECTOR_NOT_DEFINED_D 6820186 /* (0x0068115a) */ +#define DS_RET_VECTOR_NOT_NORMALIZED 6820194 /* (0x00681162) */ +#define DS_RET_VECTOR_NOT_NORMALIZED_E 6820202 /* (0x0068116a) */ +#define DS_RET_VECTOR_NOT_NORMALIZED_U 6820210 /* (0x00681172) */ +#define DS_RET_VECTOR_NOT_NORMALIZED_D 6820218 /* (0x0068117a) */ +#define DS_RET_YEAR_OUT_OF_RANGE 6820226 /* (0x00681182) */ +#define DS_RET_YEAR_OUT_OF_RANGE_E 6820234 /* (0x0068118a) */ +#define DS_RET_YEAR_OUT_OF_RANGE_U 6820242 /* (0x00681192) */ +#define DS_RET_YEAR_OUT_OF_RANGE_D 6820250 /* (0x0068119a) */ +#define DS_RET_FUNCTION_NOT_IMPLEMENTED 6820547 /* (0x006812c3) */ +#define DS_RET_FUNCTION_NOT_IMPLEMENTED_E 6820555 /* (0x006812cb) */ +#define DS_RET_FUNCTION_NOT_IMPLEMENTED_U 6820563 /* (0x006812d3) */ +#define DS_RET_FUNCTION_NOT_IMPLEMENTED_D 6820571 /* (0x006812db) */ +#define DS_RET_ITEM_FOUND 6820579 /* (0x006812e3) */ +#define DS_RET_ITEM_FOUND_E 6820587 /* (0x006812eb) */ +#define DS_RET_ITEM_FOUND_U 6820595 /* (0x006812f3) */ +#define DS_RET_ITEM_FOUND_D 6820603 /* (0x006812fb) */ +#define DS_RET_NO_GRAPHICS_AVAILABLE 6820611 /* (0x00681303) */ +#define DS_RET_NO_GRAPHICS_AVAILABLE_E 6820619 /* (0x0068130b) */ +#define DS_RET_NO_GRAPHICS_AVAILABLE_U 6820627 /* (0x00681313) */ +#define DS_RET_NO_GRAPHICS_AVAILABLE_D 6820635 /* (0x0068131b) */ +#define DS_RET_STRING_NOT_FOUND 6820643 /* (0x00681323) */ +#define DS_RET_STRING_NOT_FOUND_E 6820651 /* (0x0068132b) */ +#define DS_RET_STRING_NOT_FOUND_U 6820659 /* (0x00681333) */ +#define DS_RET_STRING_NOT_FOUND_D 6820667 /* (0x0068133b) */ +#define DS_RET_STRINGS_ARE_EQUAL 6820675 /* (0x00681343) */ +#define DS_RET_STRINGS_ARE_EQUAL_E 6820683 /* (0x0068134b) */ +#define DS_RET_STRINGS_ARE_EQUAL_U 6820691 /* (0x00681353) */ +#define DS_RET_STRINGS_ARE_EQUAL_D 6820699 /* (0x0068135b) */ +#define DS_RET_DATA_SET_OUT_OF_ORDER 6822148 /* (0x00681904) */ +#define DS_RET_DATA_SET_OUT_OF_ORDER_E 6822156 /* (0x0068190c) */ +#define DS_RET_DATA_SET_OUT_OF_ORDER_U 6822164 /* (0x00681914) */ +#define DS_RET_DATA_SET_OUT_OF_ORDER_D 6822172 /* (0x0068191c) */ +#define DS_RET_END_OF_DATA_SET_FOUND 6822180 /* (0x00681924) */ +#define DS_RET_END_OF_DATA_SET_FOUND_E 6822188 /* (0x0068192c) */ +#define DS_RET_END_OF_DATA_SET_FOUND_U 6822196 /* (0x00681934) */ +#define DS_RET_END_OF_DATA_SET_FOUND_D 6822204 /* (0x0068193c) */ +#define DS_RET_FIRST_ELEMENT_INVALID 6822212 /* (0x00681944) */ +#define DS_RET_FIRST_ELEMENT_INVALID_E 6822220 /* (0x0068194c) */ +#define DS_RET_FIRST_ELEMENT_INVALID_U 6822228 /* (0x00681954) */ +#define DS_RET_FIRST_ELEMENT_INVALID_D 6822236 /* (0x0068195c) */ +#define DS_RET_UNKNOWN_COMPRESSION_CODE 6822244 /* (0x00681964) */ +#define DS_RET_UNKNOWN_COMPRESSION_CODE_E 6822252 /* (0x0068196c) */ +#define DS_RET_UNKNOWN_COMPRESSION_CODE_U 6822260 /* (0x00681974) */ +#define DS_RET_UNKNOWN_COMPRESSION_CODE_D 6822268 /* (0x0068197c) */ +#define DS_RET_UNKNOWN_DATA_SET 6822276 /* (0x00681984) */ +#define DS_RET_UNKNOWN_DATA_SET_E 6822284 /* (0x0068198c) */ +#define DS_RET_UNKNOWN_DATA_SET_U 6822292 /* (0x00681994) */ +#define DS_RET_UNKNOWN_DATA_SET_D 6822300 /* (0x0068199c) */ +#define DS_RET_UNKNOWN_DATA_SET_OWNER 6822308 /* (0x006819a4) */ +#define DS_RET_UNKNOWN_DATA_SET_OWNER_E 6822316 /* (0x006819ac) */ +#define DS_RET_UNKNOWN_DATA_SET_OWNER_U 6822324 /* (0x006819b4) */ +#define DS_RET_UNKNOWN_DATA_SET_OWNER_D 6822332 /* (0x006819bc) */ +#define DS_RET_UNKNOWN_MODALITY 6822340 /* (0x006819c4) */ +#define DS_RET_UNKNOWN_MODALITY_E 6822348 /* (0x006819cc) */ +#define DS_RET_UNKNOWN_MODALITY_U 6822356 /* (0x006819d4) */ +#define DS_RET_UNKNOWN_MODALITY_D 6822364 /* (0x006819dc) */ +#define DS_RET_UNKNOWN_SWAP_MODE 6822372 /* (0x006819e4) */ +#define DS_RET_UNKNOWN_SWAP_MODE_E 6822380 /* (0x006819ec) */ +#define DS_RET_UNKNOWN_SWAP_MODE_U 6822388 /* (0x006819f4) */ +#define DS_RET_UNKNOWN_SWAP_MODE_D 6822396 /* (0x006819fc) */ +#define DS_RET_WITH_ERROR 6822404 /* (0x00681a04) */ +#define DS_RET_WITH_ERROR_E 6822412 /* (0x00681a0c) */ +#define DS_RET_WITH_ERROR_U 6822420 /* (0x00681a14) */ +#define DS_RET_WITH_ERROR_D 6822428 /* (0x00681a1c) */ +#define DS_CODE_SET_OUT_OF_ORDER 6825347 /* (0x00682583) */ +#define DS_CODE_SET_OUT_OF_ORDER_E 6825355 /* (0x0068258b) */ +#define DS_CODE_SET_OUT_OF_ORDER_U 6825363 /* (0x00682593) */ +#define DS_CODE_SET_OUT_OF_ORDER_D 6825371 /* (0x0068259b) */ +#define DS_CODE_END_OF_SET_FOUND 6825379 /* (0x006825a3) */ +#define DS_CODE_END_OF_SET_FOUND_E 6825387 /* (0x006825ab) */ +#define DS_CODE_END_OF_SET_FOUND_U 6825395 /* (0x006825b3) */ +#define DS_CODE_END_OF_SET_FOUND_D 6825403 /* (0x006825bb) */ +#define DS_CODE_FATAL_ERROR 6825411 /* (0x006825c3) */ +#define DS_CODE_FATAL_ERROR_E 6825419 /* (0x006825cb) */ +#define DS_CODE_FATAL_ERROR_U 6825427 /* (0x006825d3) */ +#define DS_CODE_FATAL_ERROR_D 6825435 /* (0x006825db) */ +#define DS_CODE_FIRST_ELEMENT_INVALID 6825443 /* (0x006825e3) */ +#define DS_CODE_FIRST_ELEMENT_INVALID_E 6825451 /* (0x006825eb) */ +#define DS_CODE_FIRST_ELEMENT_INVALID_U 6825459 /* (0x006825f3) */ +#define DS_CODE_FIRST_ELEMENT_INVALID_D 6825467 /* (0x006825fb) */ +#define DS_CODE_INVALID_MODALITY 6825475 /* (0x00682603) */ +#define DS_CODE_INVALID_MODALITY_E 6825483 /* (0x0068260b) */ +#define DS_CODE_INVALID_MODALITY_U 6825491 /* (0x00682613) */ +#define DS_CODE_INVALID_MODALITY_D 6825499 /* (0x0068261b) */ +#define DS_CODE_INVALID_SOM0_PARAMETERS 6825507 /* (0x00682623) */ +#define DS_CODE_INVALID_SOM0_PARAMETERS_E 6825515 /* (0x0068262b) */ +#define DS_CODE_INVALID_SOM0_PARAMETERS_U 6825523 /* (0x00682633) */ +#define DS_CODE_INVALID_PARAMETERS_D 6825531 /* (0x0068263b) */ +#define DS_CODE_INVALID_INTERFACE 6825539 /* (0x00682643) */ +#define DS_INVALID_INTERFACE_E 6825547 /* (0x0068264b) */ +#define DS_INVALID_INTERFACE_U 6825555 /* (0x00682653) */ +#define DS_INVALID_INTERFACE_D 6825563 /* (0x0068265b) */ +#define DS_CODE_MULTIPLE_FATAL_ERROR 6825571 /* (0x00682663) */ +#define DS_CODE_FMULTIPLE_ATAL_ERROR_E 6825579 /* (0x0068266b) */ +#define DS_CODE_MULTIPLE_FATAL_ERROR_U 6825587 /* (0x00682673) */ +#define DS_CODE_MULTIPLE_FATAL_ERROR_D 6825595 /* (0x0068267b) */ +#define DS_CODE_NO_OWNER 6825603 /* (0x00682683) */ +#define DS_CODE_NO_OWNER_E 6825611 /* (0x0068268b) */ +#define DS_CODE_NO_OWNER_U 6825619 /* (0x00682693) */ +#define DS_CODE_NO_OWNER_D 6825627 /* (0x0068269b) */ +#define DS_CODE_TRANSFORM_NOT_POSSIBLE 6825635 /* (0x006826a3) */ +#define DS_CODE_TRANSFORM_NOT_POSSIBLE_E 6825643 /* (0x006826ab) */ +#define DS_CODE_TRANSFORM_NOT_POSSIBLE_U 6825651 /* (0x006826b3) */ +#define DS_CODE_TRANSFORM_NOT_POSSIBLE_D 6825659 /* (0x006826bb) */ +#define DS_CODE_UNKNOWN_COMPRESSION 6825667 /* (0x006826c3) */ +#define DS_CODE_UNKNOWN_COMPRESSION_E 6825675 /* (0x006826cb) */ +#define DS_CODE_UNKNOWN_COMPRESSION_U 6825683 /* (0x006826d3) */ +#define DS_CODE_UNKNOWN_COMPRESSION_D 6825691 /* (0x006826db) */ +#define DS_CODE_UNKNOWN_DATA_SET 6825699 /* (0x006826e3) */ +#define DS_CODE_UNKNOWN_DATA_SET_E 6825707 /* (0x006826eb) */ +#define DS_CODE_UNKNOWN_DATA_SET_U 6825715 /* (0x006826f3) */ +#define DS_CODE_UNKNOWN_DATA_SET_D 6825723 /* (0x006826fb) */ +#define DS_CODE_UNKNOWN_HEADER 6825731 /* (0x00682703) */ +#define DS_CODE_UNKNOWN_HEADER_E 6825739 /* (0x0068270b) */ +#define DS_CODE_UNKNOWN_HEADER_U 6825747 /* (0x00682713) */ +#define DS_CODE_UNKNOWN_HEADER_D 6825755 /* (0x0068271b) */ +#define DS_CODE_UNKNOWN_OVERLAY 6825763 /* (0x00682723) */ +#define DS_CODE_UNKNOWN_OVERLAY_E 6825771 /* (0x0068272b) */ +#define DS_CODE_UNKNOWN_OVERLAY_U 6825779 /* (0x00682733) */ +#define DS_CODE_UNKNOWN_OVERLAY_D 6825787 /* (0x0068273b) */ +#define DS_CODE_MISSING_TYP_1 6825795 /* (0x00682743) */ +#define DS_CODE_MISSING_TYP_1_E 6825803 /* (0x0068274b) */ +#define DS_CODE_MISSING_TYP_1_U 6825811 /* (0x00682753) */ +#define DS_CODE_MISSING_TYP_1_D 6825819 /* (0x0068275b) */ +#define DS_CODE_MULTIPLE_ERROR 6825827 /* (0x00682763) */ +#define DS_CODE_MULTIPLE_ERROR_E 6825835 /* (0x0068276b) */ +#define DS_CODE_MULTIPLE_ERROR_U 6825843 /* (0x00682773) */ +#define DS_CODE_MULTIPLE_ERROR_D 6825851 /* (0x0068277b) */ +#define DS_CODE_NOT_ALL_FILLED 6825859 /* (0x00682783) */ +#define DS_CODE_NOT_ALL_FILLED_E 6825867 /* (0x0068278b) */ +#define DS_CODE_NOT_ALL_FILLED_U 6825875 /* (0x00682793) */ +#define DS_CODE_NOT_ALL_FILLED_D 6825883 /* (0x0068279b) */ +#define DS_CODE_NORMAL 6825891 /* (0x006827a3) */ +#define DS_CODE_NORMAL_E 6825899 /* (0x006827ab) */ +#define DS_CODE_NORMAL_U 6825907 /* (0x006827b3) */ +#define DS_CODE_NORMAL_D 6825915 /* (0x006827bb) */ +#define DS_CODE_UNDEFINED 6825923 /* (0x006827c3) */ +#define DS_CODE_UNDEFINED_E 6825931 /* (0x006827cb) */ +#define DS_CODE_UNDEFINED_U 6825939 /* (0x006827d3) */ +#define DS_CODE_UNDEFINED_D 6825947 /* (0x006827db) */
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_transformation.h @@ -0,0 +1,683 @@ +/*[- HEADER FILE -------------------------------------------------------------------------*/ +/* + Name: ds_transformation.h + + Description: The header file defines the common types, constants and macros for the + data set library. + + + Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 +*/ +/*]-----------------------------------------------------------------------------------------*/ + +#ifndef DS_TRANSFORMATION +#define DS_TRANSFORMATION + + + +/*== VERSION CONTROL ====================================================================*/ +/* PRECOMPILER: define version control contants */ +/* NOTE: versions */ +/* + The following list shows the used version codes in chronological order: + + Date | Doc DS IH ACR SPI notes + -------------+---------------------------------------------------------------- + 1989-JUL-03 | V0.2 - - 1.0 01.00 + 1990-MAR-10 | V1.1 - VA1 2.0 01.00 + 1990-MAR-12 | V1.1 V1.2 VA1 2.0 01.00 + 1990-APR-27 | V1.3 V1.3 VA3 2.0 01.00 internal version + 1990-AUG-01 | V1.4 V1.4 VA4 2.0 01.00 + 1990-OCT-01 | V1.5 V1.5 VA5 2.0 01.00 + 1990-OCT-11 | V1.5 V1.51 VA5 2.0 01.00 M 15 + 1990-OCT-15 | V1.5 V1.52 VA5 2.0 01.00 M 13, M19 + 1990-NOV-16 | V1.5 V1.53 VA53 2.0 01.00 parts of M16 + 1990-NOV-23 | V1.5 V1.54 VA53 2.0 01.00 M 28 + 1990-DEC-07 | V1.5 V1.55 VA55 2.0 01.00 M31, parts of M16, M30 + 1991-JAN-31 | V2.0 V2.0 VB 2.0 01.00 M*, som, num2, ... + 1991-FEB-19 | V2.0 V2.1 VB 2.0 01.00 M 40 + 1991-FEB-28 | V2.0 V2.2 VB2 2.0 01.00 M 38, M 39, M 40, M 45 + 1991-MAR-14 | V2.0 V2.3 VB2 2.0 01.00 M 41, M 48 + 1991-MAR-28 | V2.0 V2.4 VB2 2.0 01.00 M*, som, num2, ... + 1991-APR-17 | V2.0 V2.5 VB3 2.0 01.00 M* + 1991-MAY-23 | V2.0 V2.6 VB4 2.0 01.00 parts of M49 + 1991-JUL-31 | V2.0 V2.7 VB5 2.0 01.00 M*, db_set, num1, ... + 1991-AUG-08 | V2.0 V2.8 VB5 2.0 01.00 M 65 + 1991-SEP-12 | V2.0 V2.9 VB5 2.0 01.00 Image Text: M80, 73, 66, 64, 60, 59 + 1991-OCT-10 | V2.0 V2.10 VB5 2.0 01.00 M 86 + 1991-NOV | V3.0 V3.0 VB6 2.0 01.00 M*, som0 + + + labels: + ------- + Doc Documentation Version ("Phone Book" Version) + DS Data Set Library (Software) Version + IH Internal Header Version + ACR NEMA Standard Version + SPI SPI Standard Version +*/ + +/* version strings */ +/* NOTE: Software Version String */ +/* The constant "DS_SOFTWARE_VERSION_STRING" is not longer supported. The software + version is set in this and future software releases in data set library function + "ds_info_get_software_version" directly. In this way several modul compilations + after changing this common used data set library header file are avoided. */ + +/* constant definition */ +#define HEADER_VERSION_STRING "VB6 " +#define RECOGNITION_CODE_STRING "ACR-NEMA 2.0" +#define SPI_VERSION_STRING "SPI VERSION 01.00 " + + + +/*== CONSTANTS ==========================================================================*/ + +/* PRECOMPILER: define common non-numeric constants */ + +/* logical constants */ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + + +/* common status file constants */ +#define DS_STATUS_FILE_TYPE struct STC_COMMON + + +/* common nil pointer constants */ +#define DS_CHAR_NIL ((char *) 0) +#define DS_DOUBLE_NIL ((double *) 0) +#define DS_INT_NIL ((int *) 0) +#define DS_LONG_NIL ((long *) 0) +#define DS_SHORT_NIL ((short *) 0) +#define DS_STATUS_FILE_NIL ((DS_STATUS_FILE_TYPE *) 0) +#define DS_VOID_NIL ((void *) 0) + + +/* PRECOMPILER: define status area element codes */ +/* NOTE: list of status abbreviations */ +/* + PRE == PREparation of item value e.g. adapt, collect information + KEY == build up item KEY + BUF == transfere item value into nema data set BUFfer + SEP == SEParation of item from nema data set buffer + SET == SET back item value for internal use + POS == POSition item value into interanl header e.g. split, convert information + CH1 == Check a first parameter + CH2 == Check a second parameter +*/ + +/* error handling constants */ +#define PRE 0 +#define KEY 1 +#define BUF 2 +#define SEP 0 +#define SET 1 +#define POS 2 +#define CH1 0 +#define CH2 1 + + +/* sign constants */ +#define DELIMITER_DS_SIGN '\\' +#define INITALIZE_DS_SIGN ' ' +#define NON_PRINT_ABLE_DS_SIGN '~' +#define OVERLAY_DS_SIGN '/' +#define PADDING_DS_SIGN ' ' +#define UNDEFINED_TEXT_DS_SIGN ' ' + + +/* string constants */ +#define IMAGE_GRAPHIC_FORMAT_CMS "CMS " + + +/* end-of constants */ +#define EOAL (-19223) +#define END_OF_DATA_SET (-19224) + + +/* coordinate system constants */ +#define AAA 0 +#define BBB 1 +#define CCC 2 +#define XXX 0 +#define YYY 1 +#define ZZZ 2 +#define SAG 0 +#define COR 1 +#define TRA 2 +#define ROW 0 +#define COLUMN 1 +#define DOWN 0 +#define RIGHT 1 + + +/* common numeric contants */ +#define DS_DO_NOT_FILL (-1L) +#define DS_MAX_CHAR (256 + 1) +#define DS_SAVE_FACTOR (4096) +#define DS_UID_SIZE (26) +#define DS_WORK_CHAR (32 + 1) + + + +/*== RUNTIME CONTROL ====================================================================*/ + +/* PRECOMPILER: define debug constants */ + +/* NOTE: debug constants */ +/* + The constant 'DEBUGGING_BY_THUMSER' is used to compile or not compile C-source code + for debugging. If you change the '#undef'-command in a '#define'-command all parts + with debug code are compiled. This is usefull to activate calls of function printf() + etc. + + The macro 'DS_ERROR_CONDITION' controls the call of function ds_initiate_error_handling(). + During normal run time this macro is defined in a matter, that only if a error + or warning raised the function "ds_initiate_error_handling()" will be actived. If the + debug switch 'DEBUGGING_BY_THUMSER' is defined for every item a message is printed to + standard output device. Note that in this case the interface status is not updated + correctly. Please use the shown messages to anlyse the item transformation + results. + +*/ +#undef DEBUGGING_BY_THUMSER + +#ifndef DEBUGGING_BY_THUMSER +#define DS_ERROR_CONDITION (!(StatusList[0] & 0x0001) || \ + !(StatusList[1] & 0x0001) || \ + !(StatusList[2] & 0x0001)) +#else +#define DS_ERROR_CONDITION TRUE +#endif + + +#define ds_initiate_error_handling(Group, Element, Error, Status, Return) \ + ds_init_error_handling(__FILE__, __LINE__, Group, Element, Error, Status, Return) + + +/*-----------------------------------------------------------------------------------------*/ + + +/* PRECOMPILER: define interacive constants */ + +/* NOTE: interacive constants */ +/* The constant 'DS_INTERACTIVE' is used to compile or not compile C-source code for a + interactive use of separation functions. If you change the '#undef'-command in a + '#define'-command all parts with interactive code are compiled. */ +#undef DS_INTERACTIVE +/*-----------------------------------------------------------------------------------------*/ + + +/* PRECOMPILER: define Exception Handler */ + +/* NOTE: Exception Handler */ +/* Three makros are implemented to raise the exception handler. You can choise one for your + current work by precompiler statement #define and #undefine, respectively. + + + DS_RAISE_NORMAL + The raise is done with a goto statement. Only to raise an exception handler the goto + statement is used in data set library software. No other actions will be done. + + + DS_RAISE_TRACE + The raise is done with a goto statement also, but before flow control goes to the raised + exception handler a printf() call is done. The following text is printed: + + "<file>", line <line number>: raise ==> <status> + + The print out of this message can be controlled by file '/tmp/ds_trace_on'. If the file + exist the print out is done otherwise nothing is done. In this manner the switch can be + done during runtime without new compilation or process restart. + + If 'DS_RAISE_TRACE' defined an item trace is possible too. For more information about + this topic see description of function 'ds_finally_interface_status_update()' in modul + 'ds_mixed.c'. + + + DS_RAISE_DBX + The raise is done with a goto statement also, but before flow control goes to the raised + exception handler a ds_stop_dbx() is done. This function has no other function than to + stop point for debugger. Use during a dbx session the command 'stop in ds_stop_dbx'. + The debugger stops program execution everytime a exception is raised. A check of current + active parameters is now possible. +*/ +#undef DS_RAISE_NORMAL +#define DS_RAISE_TRACE +#undef DS_RAISE_DBX + +#ifdef DS_RAISE_NORMAL +#define DS_RAISE_EXCEPTION(x) goto x +#endif + + +#ifdef DS_RAISE_TRACE +#define DS_RAISE_EXCEPTION(x) { \ + ds_trace_control(__FILE__, __LINE__, Status); \ + goto x; \ + } +#endif + + +#ifdef DS_RAISE_DBX +#define DS_RAISE_EXCEPTION(x) { \ + ds_stop_dbx(); \ + goto x; \ + } +#endif + + +/* PRECOMPILER: define Return Code Handler */ + +/* NOTE: Return Code Handler */ +/* To set a special return code the makro 'DS_SET_RETURN_CODE()' is available. Three forms + of this makro are implemented to manage the handling during setting of a special return + code. You can choise one for your current work by precompiler statement #define and + #undefine, respectively. + + + DS_RETURN_NORMAL + The return code is set with a normal assign statement. No other actions will be done. + + + DS_RETURN_TRACE + The return code is set with an assign statement also, but in addition a printf() call + is done. The following text is printed: + + "<file>", line <line number>: return ==> <status> + + The print out of this message can be controlled by file '/tmp/ds_return_on'. If the file + exist the print out is done otherwise nothing is done. In this manner the switch can be + done during runtime without new compilation or process restart. + + + DS_RETURN_DBX + The return code is set with an assign statement also, but in additon a call of function + ds_stop_dbx() is done. This function has no other function than to stop point for debugger. + Use during a dbx session the command 'stop in ds_stop_dbx'. The debugger stops program + execution everytime a special return code is set. A check of current active parameters + is now possible. +*/ +#undef DS_RETURN_NORMAL +#define DS_RETURN_TRACE +#undef DS_RETURN_DBX + +#ifdef DS_RETURN_NORMAL +#define DS_SET_RETURN_CODE(x) ReturnCode = (x); +#endif + + +#ifdef DS_RETURN_TRACE +#define DS_SET_RETURN_CODE(x) { \ + ds_return_control(__FILE__, __LINE__, (x)); \ + ReturnCode = (x); \ + } +#endif + + +#ifdef DS_RETURN_DBX +#define DS_SET_RETURN_CODE(x) { \ + ds_stop_dbx(); \ + ReturnCode = (x); \ + } +#endif +/*-----------------------------------------------------------------------------------------*/ + + +/* PRECOMPILER: define length of NEMA item values */ + +/* NOTE: length of NEAM item values control */ +/* + If you want to use other lengths for NEMA item values as the CMS defined standard + lengths then change the following precompiler statement '#undef DS_USE_NOT_STAN- + DARD_LENGTH' to '#define DS_USE_NOT_STANDARD_LENGTH'. + + A reason to do this is do minimize the NEAM Data Set Length during data set build up. + If you want a minimal NEAM Data Set length define the following xxx_LENGTH + identifier with a replacement of 2. Then the Data Set Library functions get and + use the minimal necessary length of each item. + + If you use a replacement less than two then a fatal error occur. +*/ + +#undef DS_USE_NOT_STANDARD_LENGTH +#ifndef DS_USE_NOT_STANDARD_LENGTH + +#define AGE_LENGTH LENGTH_AGE +#define DATE_LENGTH 10L +#define DIAGNOSIS_LENGTH LENGTH_DIAGNOSIS +#define DIRECTION_LENGTH LENGTH_DIRECTION +#define COMMENT_LENGTH LENGTH_COMMENT +#define FILE_NAME_LENGTH LENGTH_FILE_NAME +#define FILTER_ID_LENGTH LENGTH_FILTER_ID +#define HEADER_VERSION_LENGTH LENGTH_HEADER_VERSION +#define INTEGER_NUMBER_LENGTH 6L +#define LABEL_LENGTH LENGTH_LABEL +#define LITTLE_IDENT_LENGTH 4L +#define LONG_IDENT_LENGTH 12L +#define NUCLEUS_LENGTH LENGTH_NUCLEUS +#define MANUFACTURER_LENGTH LENGTH_MANUFACTURER +#define MIDDLE_IDENT_LENGTH 8L +#define ORIENTATION_LENGTH LENGTH_ORIENTATION +#define PATIENT_ID_LENGTH LENGTH_PATIENT_ID +#define REAL_NUMBER_LENGTH 14L +#define SEQUENCE_INFO_LENGTH LENGTH_SEQUENCE_INFO +#define SHORT_IDENT_LENGTH 2L +#define SOFTWARE_VERSION_LENGTH LENGTH_SOFTWARE_VERSION +#define SPI_VERSION_LENGTH 18L +#define TIME_LENGTH 12L + +#else + +#define AGE_LENGTH 2L +#define DIAGNOSIS_LENGTH 2L +#define DIRECTION_LENGTH 2L +#define COMMENT_LENGTH 2L +#define FILE_NAME_LENGTH 2L +#define FILTER_ID_LENGTH 2L +#define HEADER_VERSION_LENGTH 2L +#define INTEGER_NUMBER_LENGTH 2L +#define LABEL_LENGTH 2L +#define LITTLE_IDENT_LENGTH 2L +#define LONG_IDENT_LENGTH 2L +#define NUCLEUS_LENGTH 2L +#define MANUFACTURER_LENGTH 2L +#define MIDDLE_IDENT_LENGTH 2L +#define ORIENTATION_LENGTH 2L +#define PATIENT_ID_LENGTH 2L +#define REAL_NUMBER_LENGTH 2L +#define SEQUENCE_INFO_LENGTH 2L +#define SHORT_IDENT_LENGTH 2L +#define SOFTWARE_VERSION_LENGTH 2L +#define SPI_VERSION_LENGTH 2L + +#define DATE_LENGTH 10L +#define TIME_LENGTH 12L + +#endif +/*-----------------------------------------------------------------------------------------*/ + + + +/*== TYPES ==============================================================================*/ + +/* DECLARATION: declare types */ +/* - undependent types */ + +typedef enum data_area_type_tag +{ + Area_Type_BYTE = 1, + Area_Type_LONG = 2, + Area_Type_SHORT = 3, + Area_Type_UNDEFINED = Enum_UNDEFINED +} data_area_type_t; + + +typedef enum data_set_format_tag +{ + Set_format_BLOCK = 1, + Set_format_NEMA = 2, + Set_format_UNDEFINED = Enum_UNDEFINED +} data_set_format_t; + + +typedef struct image_orientation_tag +{ + double RowX; + double RowY; + double RowZ; + double ColX; + double ColY; + double ColZ; +} image_orientation_t; + + +typedef enum image_class_tag +{ + Image_Class_CORONAL = 10, + Image_Class_SAGITTAL = 20, + Image_Class_TRANSVERSAL = 30, + Image_Class_UNDEFINED = Enum_UNDEFINED +} image_class_t; + + +typedef enum image_text_type_tag +{ + Image_Text_Type_BLACK = 1, + Image_Text_Type_MR_RAW = 2, + Image_Text_Type_NONE = 3, + Image_Text_Type_NORMAL = 4, + Image_Text_Type_CT_REBUILD = 5, + + Image_Text_Type_UNDEFINED = Enum_UNDEFINED +} image_text_type_t; + + +typedef enum item_quality_tag +{ + Item_Quality_AFTER_IN_FOLLOWING_GROUP = 10, + Item_Quality_AFTER_IN_THIS_GROUP = 11, + Item_Quality_BEFORE = 20, + Item_Quality_END_OF_DATA_SET = 50, + Item_Quality_EQUAL = 30, + Item_Quality_OUT_OF_RANGE = 99, + + Item_Quality_UNDEFINED = Enum_UNDEFINED +} item_quality_t; + + + +typedef char nema_patient_place_t[2][4]; + +typedef enum overlay_number_tag +{ + Overlay_6000 = 1, + Overlay_6002 = 2, + Overlay_6004 = 3, + Overlay_6006 = 4, + + Overlay_UNDEFINED = Enum_UNDEFINED +} overlay_number_t; + + +typedef union quart_as_tag /* four bytes as different bit + patterns */ +{ + char AsBytes[4]; /* as four bytes */ + float AsFloat; /* as float */ + long AsLong; /* as long */ + short AsShort[2]; /* as two short */ +} quart_as_t; + + +typedef enum swap_mode_tag +{ + Swap_NO = 0, + Swap_YES = 1, + Swap_UNDEFINED = Enum_UNDEFINED +} swap_mode_t; + + +typedef struct transformation_list_tag +{ + int Index; + double Factor; +} transformation_list_t[3]; + + +typedef double matrix_3x3_t[3][3]; + + +typedef enum update_mode_tag +{ + Update_ELEMENT = 1, + Update_FOUND = 2, + Update_GROUP = 3, + Update_UNDEFINED = Enum_UNDEFINED +} update_mode_t; + + +typedef enum value_mode_tag +{ + Value_IS_NORMAL = 1, + Value_IS_LAST = 2, + Value_UNDEFINED = Enum_UNDEFINED +} value_mode_t; + + +typedef enum value_representation_tag +{ + Representation_AN = 10, + Representation_AT = 11, + Representation_BI = 20, + Representation_BR = 21, + Representation_BY = 22, + Representation_BX = 23, + Representation_UNDEFINED = Enum_UNDEFINED +} value_representation_t; + + +typedef double vector_t[3]; + + +/* - dependent types */ + +typedef struct nema_image_place_tag +{ + nema_patient_place_t PatientOrientation; + vector_t ImagePosition; + vector_t ImageOrientation[2]; +} nema_image_place_t; + + + +/*== MACROS =============================================================================*/ + +/* PRECOMPILER: define macros */ + + /* NOTE: define macros */ + /* + If you want to use a "Mathematics" macro don't forget to insert the precompiler line + "#include <math.h>". + */ +/*-----------------------------------------------------------------------------------------*/ + + +/* Bit Manipulation */ +#define DS_BIT_CLEAR(Variable, BitNumber) ((Variable) = ((Variable) & ~(1<<(BitNumber)))) +#define DS_BIT_TEST(Variable, BitNumber) (((Variable) & (1<<(BitNumber))) != 0) +#define DS_BIT_SET(Variable, BitNumber) ((Variable) = ((Variable) | (1<<(BitNumber)))) +#define DS_BIT_TOGGLE(Variable, BitNumber) (for future use) +/*-----------------------------------------------------------------------------------------*/ + + +/* Mathematics */ +#define DS_MAX(a,b) ((a)>(b)?(a):(b)) +#define DS_MIN(a,b) ((a)>(b)?(b):(a)) + + +#define DS_I_ABS(x) ((x) < 0 ? -(x) : (x)) + +#define DS_R_ABS(x) fabs((x)) +#define DS_R_EQUAL(x, y) (fabs((double)(x) - (double)(y)) <= (double)0.00001) +#define DS_R_GREATER(x, y) ((x) > (y)) +#define DS_R_LESS(x, y) ((x) < (y)) + +#define DS_V_EQUAL(x, y) ds_vector_check_if_equal((x), (y)) + +#define DS_G_SIN(x) (sin(((M_PI/180.0)*(x)))) +#define DS_G_COS(x) (cos(((M_PI/180.0)*(x)))) +#define DS_G_TAN(x) (tan(((M_PI/180.0)*(x)))) +#define DS_G_TAN2(x,y) (tan2((M_PI/180.0)*((x),(y)))) + +#define DS_G_ASIN(x) ((180.0/M_PI)*asin((x))) +#define DS_G_ACOS(x) ((180.0/M_PI)*acos((x))) +#define DS_G_ATAN(x) ((180.0/M_PI)*atan((x))) +#define DS_G_ATAN2(x,y) ((180.0/M_PI)*atan2((x),(y))) + +#define DS_R_SIN(x) (sin((x)))) +#define DS_R_COS(x) (cos((x)))) +#define DS_R_TAN(x) (tan((x)))) +#define DS_R_TAN2(x,y) (tan2((x),(y)))) + +#define DS_R_ASIN(x) (asin((x))) +#define DS_R_ACOS(x) (acos((x))) +#define DS_R_ATAN(x) (atan((x))) +#define DS_R_ATAN2(x,y) (atan2((x),(y))) + +/*-----------------------------------------------------------------------------------------*/ + + +/* Transformation Matrix Handling */ +#define DS_SET_TRANSFORMATION_LIST(a_fac, a_ind, b_fac, b_ind, c_fac, c_ind) \ + { \ + TransformationList[AAA].Index = (a_ind); \ + TransformationList[AAA].Factor = (a_fac); \ + TransformationList[BBB].Index = (b_ind); \ + TransformationList[BBB].Factor = (b_fac); \ + TransformationList[CCC].Index = (c_ind); \ + TransformationList[CCC].Factor = (c_fac); \ + } +/*-----------------------------------------------------------------------------------------*/ + + +/* Image Class / History Label */ +#define DS_SET_HISTORY_LABEL(ClassLabel, No1Label, No2Label) \ + strncpy(Header->G51.Txt.SliceOrientationNo1, (ClassLabel), (int) 3); \ + if (!(DS_R_EQUAL(SliceAngleNo1, 0.0))) \ + { \ + Header->G51.Txt.SliceOrientationNo1[3] = '>'; \ + strncpy(&(Header->G51.Txt.SliceOrientationNo1[4]), (No1Label), (int) 3); \ + } \ + if (!(DS_R_EQUAL(SliceAngleNo2, 0.0))) \ + { \ + Header->G51.Txt.SliceOrientationNo2[3] = '>'; \ + strncpy(&(Header->G51.Txt.SliceOrientationNo2[4]), (No2Label), (int) 3); \ + } +/*-----------------------------------------------------------------------------------------*/ + + +/* String Initialization */ +#define DS_SET_STRING_DEFAULT(String, DefaultSign, StringLength) \ + memset((String), (DefaultSign), (int) (StringLength)); \ + String[(StringLength)] = '\0' + +#define DS_SET_STRING_UNDEFINED(String, FillSign, StringLength) \ + memset((String), (FillSign), (int) (StringLength)); \ + String[(StringLength)] = '\0'; \ + String[0] = String_UNDEFINED + +#define DS_SET_STRING_VALUE(String, FillString, StringLength) \ + bcopy((FillString), (String), (int) (StringLength)); \ + String[(StringLength)] = '\0' + +#define DS_SET_STRING_CHANGEABLE(String) \ + ds_string_delete_leading_characters(String, String, ' '); \ + ds_string_delete_tailing_characters(String, String, ' ') +/*-----------------------------------------------------------------------------------------*/ + + + +/*== FUNCTIONS ==========================================================================*/ + +/* NOTE: define system library functions */ +/* + The following system library functions are defined here to avoid an error message during + lint task. +*/ + +/* DECLARATION: define special parameter */ +/*char *sprintf();*/ + + + +/*========================================================================================*/ + + +#endif
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_include/ds_transformation_control.h @@ -0,0 +1,599 @@ +/*[- HEADER FILE -------------------------------------------------------------------------*/ +/* + Name: ds_transformation_control.h + + Description: The header file defines the types, constants and macros necessary to + control a data set transformation. + + NOTE + The discussion about graphic representation has not finished in the + moment (1989-DEC). A final declaration is possible after publishing the + graphic discussion results. + + + Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 +*/ +/*]-----------------------------------------------------------------------------------------*/ + +#ifndef DS_TRANSFORMATION_CONTROL +#define DS_TRANSFORMATION_CONTROL + + +/*== "Interpace Pointer List" ============================================================*/ + +/*[ + Description: Entity "Interpace Pointer List" + + The most important part to control the transformation is the Interface + Pointer List. To use the data set library build up function "ds_build_up_ + cms_nema_data_set() to export an Internal Data Set or the data set library + separation function "ds_separate_common_data_set() to import a NEMA Data + Set this structure must be filled with data to control the transformation. + This structure is the link between the different data areas in the host + memory. + + The following C-code fragment shows an example to fill an Interface Pointer + List before a NEMA Data Set is build up and separate respectively. + The example areas are named 'MyHead', 'MyGraphic', 'MyData' for Internal + Data Set and 'NemaSet' for the NEMA Data Set. The Interface Pointer List + 'MyInterface' is used. + Please note such areas must be available and filled with valid data before + a data set transformation can be done. It is the user's responsibility to + ensure that enough storage is available for the different areas. + + To avoid nested comments in the source file the C++ comment sign "//" is + used. + + // SEQUENCE: build up CMS defined NEMA Data Set + ... + // update interface pointer list + MyInterface.In.HeadAddress = &MyHead; // pointer to head area + MyInterface.In.HeadLength = sizeof(header_t); // length of head area + MyInterface.In.GraphicAddress = MyGraphic_p; // pointer to graphic area + // or DS_VOID_NIL + MyInterface.In.GraphicLength = MyGraphicLength; // length of graphic area + // or Integer_UNDEFINED + // or 0 + MyInterface.In.DataAddress = (void *)MyData_p; // pointer to data area + MyInterface.In.DataLength = MyDataLength; // length of data area 1) + + MyInterface.Ex.HeadAddress = MyNema_p; // pointer to NEMA area + MyInterface.Ex.HeadLength = Integer_UNDEFINED; // filled during build up + MyInterface.Ex.GraphicAddress = DS_VOID_NIL; // filled during build up + MyInterface.Ex.GraphicLength = Integer_UNDEFINED; // filled during build up + MyInterface.Ex.DataAddress = DS_VOID_NIL; // filled during build up + MyInterface.Ex.DataLength = Integer_UNDEFINED; // filled during build up + MyInterface.Ex.DataGroup = (short) Integer_UNDEFINED; // filled during build up + + MyInterface.CntStatus.Owner = DS_owner_UNDEFINED; // initialization + MyInterface.CntStatus.Modality = <required modality>; // p r e s e t t i n g !! + MyInterface.CntStatus.Code = DS_code_UNDEFINED; // initialization + MyInterface.CntStatus.StatusFileAddress = MyFileAddress; // pointer to mapped status file + MyInterface.CntStatus.StopItem.GroupNumber = 0; // initialization + MyInterface.CntStatus.StopItem.ElementNumber = 0; // initialization + ... + + 1) The pure data length in bytes is also stored into item Number of Data Bytes + (0019'H,1060'H). + + + // SEQUENCE: separate common Data Set + ... + // update interface pointer list + MyInterface.In.HeadAddress = &MyHead; // pointer to head area + MyInterface.In.HeadLength = sizeof(header_t); // length of head area + MyInterface.In.GraphicAddress = MyGraphic_p; // pointer to graphic area + MyInterface.In.GraphicLength = Integer_UNDEFINED; // filled during separation + MyInterface.In.DataAddress = (void *)MyData_p; // pointer to data area + MyInterface.In.DataLength = Integer_UNDEFINED; // filled during separation + + MyInterface.Ex.HeadAddress = MyNema_p; // pointer to NEMA area + MyInterface.Ex.HeadLength = Integer_UNDEFINED; // filled during separation + MyInterface.Ex.GraphicAddress = DS_VOID_NIL; // filled during separation + MyInterface.Ex.GraphicLength = Integer_UNDEFINED; // filled during separation + MyInterface.Ex.DataAddress = DS_VOID_NIL; // filled during separation + MyInterface.Ex.DataLength = Integer_UNDEFINED; // filled during separation + MyInterface.Ex.DataGroup = (short) Integer_UNDEFINED; // filled during separation + + MyInterface.CntStatus.Owner = DS_owner_UNDEFINED; // initialization or presetting + MyInterface.CntStatus.Modality = Modality_UNDEFINED; // initialization or presetting + MyInterface.CntStatus.Code = DS_code_UNDEFINED; // initialization + MyInterface.CntStatus.StatusFileAddress = DS_STATUS_FILE_NIL; // not used during separation + MyInterface.CntStatus.StopItem.GroupNumber = 0; // initialization + MyInterface.CntStatus.StopItem.ElementNumber = 0; // initialization + ... +*/ + + +/* DECLARATION: declare the interpace pointer list types */ + +typedef enum transformation_status_tag +{ + +/* NOTE: enum transformation_status_tag */ +/* The data set library defined transformation control status is supported by native + language support (nls). This means it is possible to use this code for a detailed status + messages after a transformation task. For example the following lines + + // SEQUENCE: transformate data set + MyStatus = ds_separate_common_data_set(&MyInterface); + + // SEQUENCE: show transformation task status + printf("\ninfo: separate data set: (%s)", nls_message((int) MyStatus)); + printf("\n (%s)", nls_message((int) MyInterface.CntStatus.Code)); + + print after transformatin task with error + + info: separate data set: ret: (DS_RET_WITH_ERROR an error occur during transformation task) + (DS_CODE_SET_OUT_OF_ORDER data set out of order) + + +*/ + + DS_code_DATA_SET_OUT_OF_ORDER = + DS_CODE_SET_OUT_OF_ORDER, /* Error - groups or elements not + in ascending order. Build up or + separation was aborted. */ + DS_code_END_OF_DATA_SET_FOUND = + DS_CODE_END_OF_SET_FOUND, /* Error - End Of Data set was + encountered. Build up or + separation was aborted. */ + DS_code_FATAL_ERROR = + DS_CODE_FATAL_ERROR, /* Error - fatal error occured + during data set transformation + Build up or separation was + aborted. */ + DS_code_FIRST_ELEMENT_IN_GROUP_INVALID = + DS_CODE_FIRST_ELEMENT_INVALID, /* Error - first element in group + is not element Group Length + (x'H,0000'H). Build up or + separation was aborted. */ + DS_code_INVALID_MODALITY = + DS_CODE_INVALID_MODALITY, /* Error - desired modality is not + supported. Build up or + separation was aborted. */ + DS_code_INVALID_SOM0_PARAMETERS = + DS_CODE_INVALID_SOM0_PARAMETERS, /* Error - given data set can not + transformed. Build up or + separation was aborted. */ + DS_code_INVALID_INTERFACE = + DS_CODE_INVALID_INTERFACE, /* Error - the interface pointer + list dosen't contain length + information. Separation of + internal data set was aborted. */ + DS_code_NO_OWNER = + DS_CODE_NO_OWNER, /* Error - no owner entries found + in shadow groups. Build up or + separation was aborted. */ + DS_code_TRANSFORMATION_NOT_POSSIBLE = + DS_CODE_TRANSFORM_NOT_POSSIBLE, /* Error - The transformation is + not possible, because required + dependencies are not fulfilled. + Please see data set library + function descriptions + "ds_*_check_if_transformation_pos + sible()" for more information. */ + DS_code_UNKNOWN_COMPRESSION_CODE = + DS_CODE_UNKNOWN_COMPRESSION, /* Error - compression code is + unknown or not supported. Build + up or separation was aborted. */ + DS_code_UNKNOWN_DATA_SET = + DS_CODE_UNKNOWN_DATA_SET, /* Error - data set format is + unknown. Build up or separation + was aborted. */ + DS_code_UNKNOWN_HEADER = + DS_CODE_UNKNOWN_HEADER, /* Error - header format is + unknown. Build up or separation + was aborted. */ + DS_code_UNKNOWN_OVERLAY = + DS_CODE_UNKNOWN_OVERLAY, /* Warning - a given overlay number + is invalid. The build up task + omitted the build up of this + overlay group. */ + DS_code_MISSING_TYP_1_VALUE = + DS_CODE_MISSING_TYP_1, /* Warning - not all type 1 NEMA + items could be build up, that + means an invalid NEMA Data Set + was build up or separated. */ + DS_code_MULTIPLE_ERROR = + DS_CODE_MULTIPLE_ERROR, /* Warning - several errors + detected. This means more than + one type 1 item values are + missing or a type 1 value is + missing and one or more other + item values are not filled. An + invalid NEMA Data Set was build + up or separated. */ + DS_code_MULTIPLE_FATAL_ERROR = + DS_CODE_MULTIPLE_FATAL_ERROR, /* Error - several fatal errors + detected. This means more than + one error occures to abort the + transformation. No external data + set is filled. */ + DS_code_NOT_ALL_FILLED = + DS_CODE_NOT_ALL_FILLED, /* Warning - not all Internal Data + Set parameters could be filled + or not all required NEMA items + could be built up. This + parameters are set 'undefined'. + That means an valid NEMA Data + Set was build up, but it is + possible that this data set + contains to less information for + special CMS internal use. */ + + DS_code_NORMAL = DS_CODE_NORMAL, /* successful transformation */ + DS_code_UNDEFINED = DS_CODE_UNDEFINED /* initialization value */ + +} transformation_status_t; + + + +typedef enum data_set_owner_tag +{ + DS_owner_CMS_NEMA = 1, /* The data set is NEMA defined + with CMS expansions. The format + is defined in [DS File Format] + and [DS Item Format] */ + DS_owner_NEMA = 2, /* The data set is NEMA defined. + Possible available expansions + are unknown and not analyzed. + The format is defined in [NEMA + Image], [NEMA Image88] and [NEMA + Image89]. */ + DS_owner_NUMARIS2_FRAMED = 3, /* The data set is Siemens defined + with a NEMA frame. The format is + defined in [MR Header]. */ + DS_owner_SOMARIS1_FRAMED = 4, /* The data set is Siemens defined + with a NEMA frame. The format is + defined in [CT Header] and [CT + Image]. */ + DS_owner_SOMARIS1_BLOCKED = 5, /* The data set is Siemens defined + as a binary data stream. The + format is defined in [CT + Header]. */ + DS_owner_SOMARIS0_FRAMED = 6, /* The data set is Siemens defined + with a NEMA frame. The format is + defined in [CT DRH Header] and + [CT Image]. */ + DS_owner_SOMARIS0_BLOCKED = 7, /* The data set is Siemens defined + as a binary data stream. The + format is defined in [CT DRH + Header]. */ + DS_owner_CMS_BLOCKED = 8, /* The data set is CMS defined + as a binary data stream. The + format is defined in [DS File + Format], [DS Item Format] and + [DS Reference Manual]. */ + DS_owner_NUMARIS1_FRAMED = 9, /* The data set is Siemens defined + with a NEMA frame. The format is + defined in [MR NUM1 Header]. */ + DS_owner_NUMARIS1_BLOCKED = 10, /* The data set is Siemens defined + with a binary data stream. The + format is defined in [MR NUM1 + Header]. */ + DS_owner_UNKNOWN = -99, /* The data set format is unknown. + No separation is possible */ + + DS_owner_UNDEFINED = Enum_UNDEFINED /* initialization */ +} data_set_owner_t; + + + +typedef struct keyword_tag +{ + short GroupNumber; /* item group number */ + short ElementNumber; /* item element number */ +} keyword_t; + + +typedef struct control_area_ex_tag /* - NEMA Data Set - */ +{ + char *HeadAddress; /* header area address (base + address of NEMA Data Set byte + stream, is also base address of + item key of item Group Length + (0008'H, 0000'H)) */ + long HeadLength; /* header area length in byte */ + char *GraphicAddress; /* graphic area address (base + address of item key of item + Group Length (6021'H, 0000'H)) */ + long GraphicLength; /* graphic area length in byte */ + char *DataAddress; /* data area address (base address + of item key of item Group Length + (<DataGroup>, 0000'H)) */ + long DataLength; /* data length in byte */ + short DataGroup; /* NEMA data group */ +} control_area_ex_t; + + +typedef struct control_area_in_tag /* - Internal Data Set - */ +{ + header_t *HeadAddress; /* header area address */ + long HeadLength; /* header area length in byte */ + char *GraphicAddress; /* graphic area address */ + long GraphicLength; /* graphic area length in byte */ + void *DataAddress; /* data area address */ + long DataLength; /* data length in byte */ +} control_area_in_t; + + +typedef struct control_status_tag +{ + data_set_owner_t Owner; /* Format of source data set. This + parameter must be initialized by + separation with the known owner + or with 'DS_owner_UNDEFINED'. If + 'DS_owner_UNDEFINED' is used, + the data set library will + determine the data set owner. If + a preset owner not equal the + real owner of data set the + separation is aborted. During + build up the owner is always set + by data set library functions to + 'DS_owner_CMS_NEMA'. */ + modality_t Modality; /* Modality of source data set. + This parameter must be + initialized by separation with + the required modality or with + 'Modality_UNDEFINED'. If + 'Modality_UNDEFINED' is used, + the data set library will + determine the data set modality + during separation. If a preset + owner not equal the real owner + of data set the separation is + aborted. During build up the + modality must be always preset + with a supported modality value + ('Modality_CT' or + 'Modality_MR'). */ + DS_STATUS_FILE_TYPE *StatusFileAddress; /* Common Status File address. This + parameter can be set if a status + file mapped outside of the data + set library. Otherwise this + parameter must be set to nil + (DS_STATUS_FILE_NIL). If acssess + to a status file possible the + Unique Identifier + (0009'H,1015'H) is build up with + common status file information + otherwise the non PACS + presettings are used. */ + transformation_status_t Code; /* Detailed status code. This + parameter is set by data set + library. */ + keyword_t StopItem; /* Last handled item. If an fatal + error occur during + transformation this parameter + contains the error produced item + group and element number. This + two parameters must be + initlaized with 0. */ +} control_status_t; + + + +typedef struct interface_pointer_list_tag +{ + control_area_ex_t Ex; /* Internal Data Set information */ + control_area_in_t In; /* NEMA Data Set information */ + control_status_t CntStatus; /* common status information */ +} interface_pointer_list_t; +/*]*/ + + +/*== "NEMA Parser Pointer List" ==========================================================*/ + +/*[ + Description: Entity "NEMA Parser Pointer List" + + The NEMA data set parser works as an anticipationed scanner. In this manner it is possible + to analyse the current work item in his NEMA data set context. Detection of end of + data set, group change, subgroup change, generation of subgroup owner list, position + control during parsing etc. can be done with the same data set library features. + + The NEMA parser pointer list contains several pointers (addresses) to special locations + in a NEMA data set, like begin of data set, end of data set, end of current worked + group, end of group containing next item. In addition the item keys of current + worked item and the following next item are stored. + + But the most importent information for a application programmer is the pointer to + the item value of the current worked item. This pointer is used to split, convert or + fill the item for internal use. + + For clearness, the 'current worked item' is the item currently analysed, transfered, + tested etc. by the running seperation. + The 'next item' is the item directly following the worked item in the Nema data set. + Durring item separation it is used to approach to the given (searched) item. But this + subject is transperent for a application which works in a level higher as level of + function ds_seperate_given_item(). + + + The following table shows a comparision of work and next/this item parameter: + + parameter parser base parser current location + ========================================================================= + item group number WorkKey.GroupNumber NextKey.GroupNumber + item element number WorkKey.ElementNumber NextKey.ElementNumber + item value length WorkKey.ValueLength NextKey.ValueLength + begin of item key - BeginOfNextKey + begin of item value BeginOfWorkValue - + end of group + containing item EndOfWorkGroup EndOfThisGroup + + The work item is the current item used by the separation application but the base of + scanning by parser. + The next/this item is the item current analyzed by the parser. The parameter 'EndOf- + ThisGroup' is not named 'EndOfNextGroup' to avoid confusion during reading source + code. The term 'next' discribe not the right situation for this pointer. +*/ + + +/* DECLARATION: declare the NEMA parser pointer list types*/ + +typedef struct item_key_tag +{ + short GroupNumber; /* item group number */ + short ElementNumber; /* item element number */ + long ValueLength; /* item value length */ +} item_key_t; + + +typedef struct nema_parser_pointer_list_tag +{ + item_key_t WorkKey; /* current worked item */ + item_key_t NextKey; /* next item in data set */ + char *BeginOfWorkValue; /* points to first byte of item + value */ + char *BeginOfNextKey; /* points to first byte of next + item key */ + char *EndOfWorkGroup; /* points to first byte of group + following the current worked + group. The last address in this + group is EndOfWorkGroup - 1. */ + char *EndOfThisGroup; /* points to first byte of group + following group containing next + item. The last address in this + group is EndOfThisGroup - 1. */ + char *BeginOfDataSet; /* points to first byte of data + set. */ + char *EndOfDataSet; /* points to first byte behint the + NEMA data set. The last address + in the data set is EndOfDataSet + - 1. The address EndOfDataSet is + not part of the data set. The + pointer is only used as stop + mark during data set parsing. + Don't dereference this pointer + to avoid segmentation fault. */ +} nema_parser_pointer_list_t; +/*]*/ + + +/*== "Shadow Subgroup Owner List" ========================================================*/ + +/*[ + Description: Entity "Shadow Subgroup Owner List" + + In each odd-numbered group expect the command information shadow group 1, a fixed block + of data element numbers is reserved to identify the "ownership" of sets of data elements. + The elements 0010'H to 007F'H are reserved to identify the manufacturer sets of elements. + Each identifier element (0010'H to 007F'H) is a type 1 free-formatted (FF) single (S) + ASCII string (AT) and contains e.g. the OWNER_STRING_SPI (SPI Recognition Code) for blocks + of data elements reserved by SPI or the OWNER_STRING_MED for blocks of data elements + reserved by Siemens UBMed. + + The shodow subgroup owner list is used to fit subgroup location order in the NEMA data + set byte stream to the CMS program separation order without a parser reset at change + from one shadow subgroup to the next. This is necessary for higher performance. + + The shadow subgroup owner list is a two-dimensional array. In the first column are stored + the subgroup numbers found by function ds_get_subgroup_owner_code() in ascent order as + item element base number. + In the second column are stored the CMS defined subgroup owner numbers. + The last element of the shadow subgroup owner list must be marked with the end of list + element END_OF_OWNER_LIST. + The length of the list must be equal the number of listed shadow owner strings. + + The following graphic shows the shodow subgroup owner list format and an example for a + CMS defined Identifiying Information (0009'H) shodow group: + + OWNER_NUMBERS SUBGROUP_NUMBERS 'ListCounter' + +------------------+------------------+ + |ItemElementBase |OwnerNumber | 0 + +------------------+------------------+ + |ItemElementBase |OwnerNumber | 1 + +------------------+------------------+ + |ItemElementBase |OwnerNumber | 2 + +------------------+------------------+ + |END_OF_OWNER_LIST |END_OF_OWNER_LIST | 3 + +------------------+------------------+ + ..... .. + +------------------+------------------+ + |END_OF_OWNER_LIST |END_OF_OWNER_LIST | MAX_OWNER + +------------------+------------------+ + + OWNER_NUMBERS SUBGROUP_NUMBERS 'ListCounter' + +------------------+------------------+ + | 0x1000 |OWNER_NUMBER_SPI_1| 0 + +------------------+------------------+ + | 0x1100 |OWNER_NUMBER_MED | 1 + +------------------+------------------+ + | 0x1200 |OWNER_NUMBER_CMS | 2 + +------------------+------------------+ + | 0x1300 |OWNER_NUMBER_LAB | 3 + +------------------+------------------+ + |END_OF_OWNER_LIST |END_OF_OWNER_LIST | 4 + +------------------+------------------+ + ..... .. + +------------------+------------------+ + |END_OF_OWNER_LIST |END_OF_OWNER_LIST | MAX_OWNER + +------------------+------------------+ + + + With this list it is possible to handle the shadow group flexibility defined in + [SPI Image] document 4. This means it is possible to parse a NEMA data set + without reset the parser at change from one shadow subgroup to the next. +*/ +/*]*/ + + +/* DECLARATION: declare the supported shodow subgroup owner */ + +#define OWNER_STRING_ACQU "SIEMENS CM VA0 ACQU" +#define OWNER_STRING_CMS "SIEMENS CM VA0 CMS " +#define OWNER_STRING_CT "SIEMENS CT VA0 GEN " +#define OWNER_STRING_CT_COAD "SIEMENS CT VA0 COAD" +#define OWNER_STRING_CT_IMA "SIEMENS CT VA0 IMA " +#define OWNER_STRING_CT_RAW "SIEMENS CT VA0 RAW " +#define OWNER_STRING_LAB "SIEMENS CM VA0 LAB " +#define OWNER_STRING_MED "SIEMENS MED " +#define OWNER_STRING_MR "SIEMENS MR VA0 GEN " +#define OWNER_STRING_MR_COAD "SIEMENS MR VA0 COAD" +#define OWNER_STRING_MR_IMA "SIEMENS MR VA0 IMA " +#define OWNER_STRING_MR_RAW "SIEMENS MR VA0 RAW " +#define OWNER_STRING_MR_SPEC "SIEMENS MR VA0 SPEC" +#define OWNER_STRING_SPI_1 "SPI RELEASE 1 " +#define OWNER_STRING_SPI_2 "SPI " +#define OWNER_STRING_SPI_3 "SPI Release 1 " +#define OWNER_STRING_SPI_4 "SPI-VERSION 01.00 " +#define OWNER_STRING_SPI_5 "SPI_RELEASE_1_" + + +/* NOTE: shodow subgroup owner list */ +/* Don't change the order of the following define-blocks! */ + +#define PRE 0 +#define OWNER_NUMBER_ACQU 0 +#define OWNER_NUMBER_CMS 1 +#define OWNER_NUMBER_CT 2 +#define OWNER_NUMBER_CT_COAD 3 +#define OWNER_NUMBER_CT_IMA 4 +#define OWNER_NUMBER_CT_RAW 5 +#define OWNER_NUMBER_LAB 6 +#define OWNER_NUMBER_MED 7 +#define OWNER_NUMBER_MR 8 +#define OWNER_NUMBER_MR_COAD 9 +#define OWNER_NUMBER_MR_IMA 10 +#define OWNER_NUMBER_MR_RAW 11 +#define OWNER_NUMBER_MR_SPEC 12 +#define OWNER_NUMBER_SPI_1 13 +#define OWNER_NUMBER_SPI_2 14 +#define OWNER_NUMBER_SPI_3 15 +#define OWNER_NUMBER_SPI_4 16 +#define OWNER_NUMBER_SPI_5 17 + + +#define MAX_OWNER 17 + 2 +#define OWNER_STRING_SPI OWNER_STRING_SPI_1 + +#define END_OF_OWNER_LIST (-1) +#define OWNER_NUMBERS 0 +#define SUBGROUP_NUMBERS 1 + +typedef long shadow_owner_list_t[MAX_OWNER][2]; + + +#endif
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/siemens_to_dicom.c @@ -0,0 +1,790 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : siemens_to_dicom.c +@DESCRIPTION: File containing routines to read in a Siemens vision internal + file and convert it to dicom. +@METHOD : +@GLOBALS : +@CREATED : July 8, 1997 (Peter Neelin) +@MODIFIED : $Log: siemens_to_dicom.c,v $ +@MODIFIED : Revision 1.1 2003-08-15 19:52:55 leili +@MODIFIED : Initial revision +@MODIFIED : +@MODIFIED : Revision 1.1 2001/12/31 17:28:34 rhoge +@MODIFIED : adding file to repos - now needed for reading .ima files in directly +@MODIFIED : +@MODIFIED : Revision 1.2 2000/12/17 01:05:24 rhoge +@MODIFIED : temporary activation of offset table printing macro +@MODIFIED : +@MODIFIED : Revision 1.1.1.1 2000/11/30 02:05:54 rhoge +@MODIFIED : imported sources to CVS repository on amoeba +@MODIFIED : + * Revision 1.4 1998/11/16 19:54:15 neelin + * Added definitions for SunOS. + * + * Revision 1.3 1998/11/13 16:02:09 neelin + * Modifications to support packed images and asynchronous transfer. + * + * Revision 1.2 1997/11/04 14:31:30 neelin + * *** empty log message *** + * + * Revision 1.1 1997/08/11 12:50:53 neelin + * Initial revision + * +---------------------------------------------------------------------------- */ + +#ifndef lint +static char rcsid[]="$Header: /private-cvsroot/minc/conversion/dicomserver_sonata/siemens_to_dicom.c,v 1.1 2003-08-15 19:52:55 leili Exp $"; +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define DS_FUNCTIONS +#include <ds_include_files.h> + +#include <acr_nema.h> + +/* Constants */ +#ifndef public +# define public +#endif +#ifndef private +# define private static +#endif +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +#endif +#ifndef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif + +#define ACR_IMAGE_GID 0x7fe0 +#define ACR_IMAGE_EID 0x10 +#define SIEMENS_IMAGE_OFFSET (6*1024) + +#define WORLD_NDIMS 3 +#define IMAGE_NDIMS 2 + +/* Macros */ +#define MALLOC(size) ((void *) malloc(size)) +#define FREE(ptr) free(ptr) +#define REALLOC(ptr, size) ((void *) realloc(ptr, size)) +#define CALLOC(nelem, elsize) ((void *) calloc(nelem, elsize)) + +/* Conversion functions that are not defined */ +#define create_calculation_mode_t_element NULL +#define create_compression_code_t_element NULL +#define create_contrast_t_element NULL +#define create_data_object_subtype_t_element NULL +#define create_data_set_subtype_t_element NULL +#define create_field_of_view_t_element NULL +#define create_filter_parameter_t_element NULL +#define create_filter_type_image_t_element NULL +#define create_filter_type_t_element NULL +#define create_gate_phase_t_element NULL +#define create_geometry_t_element NULL +#define create_gradient_delay_time_t_element NULL +#define create_image_format_t_element NULL +#define create_laterality_t_element NULL +#define create_measurement_mode_t_element NULL +#define create_object_orientation_t_element NULL +#define create_object_threshold_t_element NULL +/* #define create_order_of_slices_t_element NULL */ +#define create_patient_orientation_t_element NULL +#define create_patient_phase_t_element NULL +#define create_patient_position_t_element NULL +#define create_patient_region_t_element NULL +#define create_pixel_quality_code_t_element NULL +#define create_pixel_quality_value_t_element NULL +#define create_reference_t_element NULL +#define create_rest_direction_t_element NULL +#define create_rotation_direction_t_element NULL +#define create_sar_sed_t_element NULL +#define create_save_code_t_element NULL +#define create_storage_mode_t_element NULL +#define create_study_type_t_element NULL +#define create_target_point_t_element NULL +#define create_view_direction_t_element NULL +#define create_window_style_t_element NULL + +/* Define element id's */ +DEFINE_ELEMENT(static, ACR_Rows , 0x0028, 0x0010, US); +DEFINE_ELEMENT(static, ACR_Columns , 0x0028, 0x0011, US); +DEFINE_ELEMENT(static, ACR_Flip_angle , 0x0018, 0x1314, DS); +DEFINE_ELEMENT(static, SPI_Flip_angle , 0x0019, 0x1260, DS); +DEFINE_ELEMENT(static, ACR_Series , 0x0020, 0x0011, IS); +DEFINE_ELEMENT(static, ACR_Image_position , 0x0020, 0x0032, DS); +DEFINE_ELEMENT(static, ACR_Image_orientation , 0x0020, 0x0037, DS); +DEFINE_ELEMENT(static, ACR_Image_location , 0x0028, 0x0200, IS); +DEFINE_ELEMENT(static, SPI_Image_position , 0x0021, 0x1160, DS); +DEFINE_ELEMENT(static, SPI_Image_row , 0x0021, 0x116a, DS); +DEFINE_ELEMENT(static, SPI_Image_column , 0x0021, 0x116b, DS); +DEFINE_ELEMENT(static, ACR_Pixel_spacing , 0x0028, 0x0030, DS); + + +/* Types */ +typedef Acr_Element (*Create_Element_Function) + (int group_id, int element_id, void *data, int length); +typedef struct { + int group_id; + int element_id; + void *data; + Create_Element_Function function; + int length; +} Siemens_header_entry; + +/* Functions */ +public Acr_Group siemens_to_dicom(char *filename, int read_image); +public void update_coordinate_info(Acr_Group group_list); +private Acr_Element_Id get_elid(int group_id, int element_id, + Acr_VR_Type vr_code); +private Acr_Element create_char_element(int group_id, int element_id, + void *data, int length); +private Acr_Element create_long_element(int group_id, int element_id, + void *data, int length); +private Acr_Element create_short_element(int group_id, int element_id, + void *data, int length); +private Acr_Element create_double_element(int group_id, int element_id, + void *data, int length); +private Acr_Element create_ds_date_t_element(int group_id, int element_id, + void *data, int length); +private Acr_Element create_ds_time_t_element(int group_id, int element_id, + void *data, int length); +private Acr_Element create_modality_t_element(int group_id, int element_id, + void *data, int length); +private Acr_Element create_sex_t_element(int group_id, int element_id, + void *data, int length); +private Acr_Element create_order_of_slices_t_element(int group_id, + int element_id, + void *data, int length); +private Acr_Element create_pixel_size_t_element(int group_id, int element_id, + void *data, int length); +private Acr_Element create_windows_t_element(int group_id, int element_id, + void *data, int length); +private Acr_Element create_image_location_t_element(int group_id, + int element_id, + void *data, int length); + +/* Define the table of header values */ +header_t Siemens_header; +#include <siemens_header_table.h> + +/* flag to print offset table, useful in debugging byte pad issues + with Linux/sun porting */ +/* #define PRINT_OFFSET_TABLE */ + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : siemens_to_dicom +@INPUT : filename - name of Siemens internal file + read_image - if TRUE, then the image is added to the group list, + otherwise it is not read in. +@OUTPUT : (none) +@RETURNS : Acr-nema group list containing contents of Siemens file +@DESCRIPTION: Function to read in a siemens internal format file and + store it in an ACR-NEMA group list +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : July 8, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public Acr_Group siemens_to_dicom(char *filename, int read_image) +{ + FILE *fp; + Siemens_header_entry *entry; + Acr_Group group_list; + Acr_Element element; + long image_size, pixel_size; + void *image; + double flip_angle; + + short rows_in; + short cols_in; + + short rows; + short cols; + +#ifdef PRINT_OFFSET_TABLE + void *header_ptr; /* debug junk */ + void *data_ptr; /* debug junk */ + long offset; /* debug junk */ +#endif + + /* Open the file */ + if ((fp = fopen(filename, "r")) == NULL) { + (void) fprintf(stderr, "Error opening file %s\n", filename); + return NULL; + } + + /* Read in the header */ + if (fread(&Siemens_header, sizeof(Siemens_header), 1, fp) != 1) { + (void) fprintf(stderr, "Error reading header in %s\n", filename); + (void) fclose(fp); + return NULL; + } + + /* Get the image if it is needed */ + if (read_image) { + + /* Figure out how much space we need for the image */ + pixel_size = 2; + + rows_in = Siemens_header.G28.Pre.Rows; + acr_get_short(ACR_BIG_ENDIAN,1, (short *) &rows_in, &rows); + cols_in = Siemens_header.G28.Pre.Columns; + acr_get_short(ACR_BIG_ENDIAN,1, (short *) &cols_in, &cols); + + /* need to byte swap row/col values if needed */ + + image_size = rows*cols; + + image = MALLOC((size_t) pixel_size * image_size); + + /* Read in the image */ + if (fseek(fp, (long) SIEMENS_IMAGE_OFFSET, SEEK_SET)) { + (void) fprintf(stderr, "Error finding image in %s\n", filename); + (void) fclose(fp); + return NULL; + } + if (fread(image, pixel_size, image_size, fp) != image_size) { + (void) fprintf(stderr, "Error reading image in %s\n", filename); + (void) fclose(fp); + return NULL; + } + + } /* If read_image */ + + /* Close the file */ + (void) fclose(fp); + + /* Loop through the header table, creating a header */ + group_list = NULL; + for (entry = Siemens_header_table; entry->data != NULL; entry++) { + +#ifdef PRINT_OFFSET_TABLE + data_ptr = entry->data; + header_ptr = &Siemens_header; + offset = (long) data_ptr - (long) header_ptr; + printf("DEBUG: group = 0x%x, element = 0x%x, offset = 0x%x, length = 0x%x\n", + entry->group_id,entry->element_id,offset,entry->length); +#endif + + if (entry->function == NULL) { + continue; + } + element = entry->function(entry->group_id, entry->element_id, + entry->data, entry->length); + if (element == NULL) { + continue; + } + + acr_insert_element_into_group_list(&group_list, element); + } + + /* Insert flip angle element */ + element = acr_find_group_element(group_list, SPI_Flip_angle); + if (element != NULL) { + flip_angle = acr_get_element_numeric(element); + if (flip_angle >= 0.0) { + acr_insert_numeric(&group_list, ACR_Flip_angle, flip_angle); + } + } + + /* Insert a series number */ + acr_insert_numeric(&group_list, ACR_Series, 1.0); + + /* Insert appropriate image position and orientation information */ + update_coordinate_info(group_list); + + + /* Add the image if it is needed */ + if (read_image) { + + /* Insert the image location */ + acr_insert_short(&group_list, ACR_Image_location, ACR_IMAGE_GID); + + /* Add the image. We don't byte-swap here since it will be done + automatically when the data is written out. */ + element = acr_create_element(ACR_IMAGE_GID, ACR_IMAGE_EID, + ACR_VR_OW, + image_size * pixel_size, image); + acr_insert_element_into_group_list(&group_list, element); + + /* explicitly label image data as big-endian */ + acr_set_element_byte_order(element, ACR_BIG_ENDIAN); + + } /* If read_image */ + + /* Return the group list */ + return group_list; +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : update_coordinate_info +@INPUT : group_list +@OUTPUT : group_list +@RETURNS : (nothing) +@DESCRIPTION: Function to modify the DICOM coordinate information to match + the Siemens info. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 9, 1998 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void update_coordinate_info(Acr_Group group_list) +{ + Acr_Element element; + int nrows, ncolumns, idim; + double coord[WORLD_NDIMS], row[WORLD_NDIMS], column[WORLD_NDIMS]; + double pixel_spacing[IMAGE_NDIMS]; + char string[256]; + + /* Look for the row vector */ + element = acr_find_group_element(group_list, SPI_Image_row); + if ((element == NULL) || + (acr_get_element_numeric_array(element, WORLD_NDIMS, row) + != WORLD_NDIMS)) { + row[0] = 1.0; row[1] = 0.0; row[2] = 0.0; + } + + /* Look for the column vector */ + element = acr_find_group_element(group_list, SPI_Image_column); + if ((element == NULL) || + (acr_get_element_numeric_array(element, WORLD_NDIMS, column) + != WORLD_NDIMS)) { + column[0] = 0.0; column[1] = 1.0; column[2] = 0.0; + } + + /* Put in the dicom orientation (patient) field */ + (void) sprintf(string, "%.15g\\%.15g\\%.15g\\%.15g\\%.15g\\%.15g", + row[0], -row[1], -row[2], + column[0], -column[1], -column[2]); + acr_insert_string(&group_list, ACR_Image_orientation, string); + + /* Look for the position */ + element = acr_find_group_element(group_list, SPI_Image_position); + if ((element == NULL) || + (acr_get_element_numeric_array(element, WORLD_NDIMS, coord) + != WORLD_NDIMS)) { + coord[0] = 0.0; coord[1] = 0.0; coord[2] = 0.0; + } + + /* Get the number of rows and columns */ + nrows = acr_find_int(group_list, ACR_Rows, 0); + ncolumns = acr_find_int(group_list, ACR_Columns, 0); + if ((nrows <= 0) || (ncolumns <= 0)) { + (void) fprintf(stderr, "Illegal image size in Siemens file\n"); + exit(EXIT_FAILURE); + } + + /* Get the pixel size */ + element = acr_find_group_element(group_list, ACR_Pixel_spacing); + if ((element == NULL) || + (acr_get_element_numeric_array(element, IMAGE_NDIMS, pixel_spacing) + != IMAGE_NDIMS)) { + pixel_spacing[0] = pixel_spacing[1] = 1.0; + } + + /* Calculate the position of the first pixel. This coordinate is still in + the Siemens space, not dicom space and will need to be flipped. Note + that ncolumns is used with row, since they are the size and unit + vector of the same dimension. */ + for (idim = 0; idim < WORLD_NDIMS; idim++) { + coord[idim] -= + pixel_spacing[0] * ((double) ncolumns - 1.0) / 2.0 * row[idim] + + pixel_spacing[1] * ((double) nrows - 1.0) / 2.0 * column[idim]; + } + (void) sprintf(string, "%.15g\\%.15g\\%.15g", + coord[0], -coord[1], -coord[2]); + acr_insert_string(&group_list, ACR_Image_position, string); + +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : create_<type>_element +@INPUT : group_id + element_id + data - pointer to data in Siemens header + length - number of values in array (if appropriate) +@OUTPUT : (none) +@RETURNS : New element containing data +@DESCRIPTION: Series of functions to convert Siemens vision header types + to ACR-NEMA elements +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : July 8, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ + +private Acr_Element_Id get_elid(int group_id, int element_id, + Acr_VR_Type vr_code) +{ + static struct Acr_Element_Id elid_struct = {0, 0, ACR_VR_UNKNOWN}; + elid_struct.group_id = group_id; + elid_struct.element_id = element_id; + elid_struct.vr_code = vr_code; + return &elid_struct; +} + +private Acr_Element create_char_element(int group_id, int element_id, + void *data, int length) +/* ARGSUSED */ +{ + char *old, *new; + int oldsize, newsize, i; + + /* Get a pointer to the old string */ + old = (char *) data; + + /* Figure out the length of the new string up to the first NUL. Make + sure that there is a room for an additional NUL if necessary */ + for (i=0; (i < length-1) && (old[i] != '\0'); i++) {} + newsize = ((old[i] == '\0') ? i+1 : length + 1); + oldsize = newsize - 1; + if ((newsize % 2) != 1) newsize++; + + /* Copy the string, making sure that there is a NUL on the end */ + new = MALLOC(newsize); + for (i=0; i < newsize-1; i++) { + if (i < oldsize) + new[i] = old[i]; + else + new[i] = ' '; + } + new[newsize-1] = '\0'; + + /* Create the element */ + return acr_create_element(group_id, element_id, ACR_VR_ST, + (long) newsize-1, (void *) new); +} + +private Acr_Element create_long_element(int group_id, int element_id, + void *data, int length) + +/* modified by rhoge to byte swap if needed */ + +/* ARGSUSED */ +{ + long data_out; + + acr_get_long(ACR_BIG_ENDIAN,1, data, &data_out); + + return acr_create_element_numeric + (get_elid(group_id, element_id, ACR_VR_IS), + (long) data_out); +} + +private Acr_Element create_short_element(int group_id, int element_id, + void *data, int length) + +/* modified by rhoge to byte swap if needed */ + +/* ARGSUSED */ +{ + unsigned short data_out; + + acr_get_short(ACR_BIG_ENDIAN,1, data, &data_out); + + return acr_create_element_short + (get_elid(group_id, element_id, ACR_VR_US), + (unsigned short) data_out); +} + +private Acr_Element create_double_element(int group_id, int element_id, + void *data, int length) + +/* modified by rhoge to byte swap if needed */ + +/* ARGSUSED */ +{ + double data_out; + + acr_get_double(ACR_BIG_ENDIAN,1, data, &data_out); + + return acr_create_element_numeric + (get_elid(group_id, element_id, ACR_VR_DS), (double) data_out); +} + +private Acr_Element create_ds_date_t_element(int group_id, int element_id, + void *data, int length) + +/* modified by rhoge to byte swap if needed */ + +/* ARGSUSED */ +{ + char string[20]; + ds_date_t *ptr; + + long year; + long month; + long day; + + long year_in; + long month_in; + long day_in; + + ptr = (ds_date_t *) data; + + year_in = ptr->Year; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) &year_in, &year); + + month_in = ptr->Month; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) &month_in, &month); + + day_in = ptr->Day; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) &day_in, &day); + + if ((year < 0) || (year > 9999)) return NULL; + if ((month < 0) || (month > 12)) return NULL; + if ((day < 0) || (day > 40)) return NULL; + (void) sprintf(string, "%04d%02d%02d", + (int) year, (int) month, (int) day); + + return acr_create_element_string + (get_elid(group_id, element_id, ACR_VR_DA), string); +} + +private Acr_Element create_ds_time_t_element(int group_id, int element_id, + void *data, int length) +/* modified by rhoge to byte swap if needed */ + +/* ARGSUSED */ +{ + char string[20]; + ds_time_t *ptr; + + long hour; + long minute; + long second; + long fraction; + + long hour_in; + long minute_in; + long second_in; + long fraction_in; + + ptr = (ds_time_t *) data; + hour_in = ptr->Hour; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) &hour_in, &hour); + minute_in = ptr->Minute; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) &minute_in, &minute); + second_in = ptr->Second; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) &second_in, &second); + fraction_in = ptr->Fraction; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) &fraction_in, &fraction); + if ((hour < 0) || (hour > 24)) return NULL; + if ((minute < 0) || (minute > 60)) return NULL; + if ((second < 0) || (second > 60)) return NULL; + if ((fraction < 0) || (fraction > 999)) return NULL; + (void) sprintf(string, "%02d%02d%02d.%03d", hour, minute, second, fraction); + return acr_create_element_string + (get_elid(group_id, element_id, ACR_VR_TM), string); +} + +private Acr_Element create_modality_t_element(int group_id, int element_id, + void *data, int length) + +/* modified by rhoge to byte swap if needed */ + +/* ARGSUSED */ +{ + char *string; + modality_t *ptr_in; + modality_t modality; + /* modality_t *ptr; */ + + /* Get the appropriate string */ + + ptr_in = (modality_t *) data; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) ptr_in, (long *) &modality); + + switch (modality) { + case Modality_CT: + string = "CT"; + break; + case Modality_MR: + string = "MR"; + break; + default: + return NULL; + } + + /* Return a new element */ + return acr_create_element_string + (get_elid(group_id, element_id, ACR_VR_CS), string); +} + +private Acr_Element create_sex_t_element(int group_id, int element_id, + void *data, int length) + +/* modified by rhoge to byte swap if needed */ +/* ARGSUSED */ +{ + char *string; + sex_t *ptr_in; + sex_t sex; + + /* Get the appropriate string */ + ptr_in = (sex_t *) data; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) ptr_in, (long *) &sex); + switch (sex) { + case Sex_F: + string = "F "; + break; + case Sex_M: + string = "M "; + break; + case Sex_O: + string = "O "; + break; + default: + return NULL; + } + + /* Return a new element */ + return acr_create_element_string + (get_elid(group_id, element_id, ACR_VR_CS), string); +} + +private Acr_Element create_order_of_slices_t_element(int group_id, + int element_id, + void *data, int length) + +/* modified by rhoge to byte swap if needed */ +/* ARGSUSED */ +{ + char *string; + order_of_slices_t *ptr_in; + order_of_slices_t order_of_slices; + + /* Get the appropriate string */ + ptr_in = (order_of_slices_t *) data; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) ptr_in, (long *) &order_of_slices); + switch (order_of_slices) { + case Slice_Order_ASCENDING: + string = "ASCENDING "; + break; + case Slice_Order_DECREASING: + string = "DESCENDING "; + break; + case Slice_Order_INTERLEAVED: + string = "INTERLEAVED "; + break; + case Slice_Order_NONE: + string = "NONE "; + break; + case Slice_Order_UNDEFINED: + string = "UNDEFINED "; + break; + default: + return NULL; + } + + /* Return a new element */ + return acr_create_element_string + (get_elid(group_id, element_id, ACR_VR_CS), string); +} + +private Acr_Element create_pixel_size_t_element(int group_id, int element_id, + void *data, int length) + +/* modified by rhoge to byte swap if needed */ + +/* ARGSUSED */ +{ + pixel_size_t *ptr; + char string[64]; + + double row_in; + double col_in; + + double row; + double col; + + /* Get the pixel sizes */ + ptr = (pixel_size_t *) data; + + row_in = ptr->Row; + acr_get_double(ACR_BIG_ENDIAN,1, (double *) &row_in, &row); + col_in = ptr->Col; + acr_get_double(ACR_BIG_ENDIAN,1, (double *) &col_in, &col); + + (void) sprintf(string, "%.15g\\%.15g", row, col); + + return acr_create_element_string + (get_elid(group_id, element_id, ACR_VR_DS), string); +} + +private Acr_Element create_windows_t_element(int group_id, int element_id, + void *data, int length) + +/* modified by rhoge to byte swap if needed */ + +/* ARGSUSED */ +{ + windows_t *ptr; + char string[64]; + + long x_in; + long y_in; + + long x; + long y; + + /* Get the window info */ + ptr = (windows_t *) data; + + x_in = ptr->X; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) &x_in, &x); + y_in = ptr->Y; + acr_get_long(ACR_BIG_ENDIAN,1, (long *) &y_in, &y); + + (void) sprintf(string, "%ld\\%ld", x, y); + + return acr_create_element_string + (get_elid(group_id, element_id, ACR_VR_IS), string); +} + +private Acr_Element create_image_location_t_element(int group_id, + int element_id, + void *data, int length) +/* modified by rhoge to byte swap if needed */ +/* ARGSUSED */ +{ + image_location_t *ptr; + char string[64]; + + double sag; + double cor; + double tra; + + double sag_in; + double cor_in; + double tra_in; + + /* Get the coordinate */ + ptr = (image_location_t *) data; + + sag_in = ptr->Sag; + acr_get_double(ACR_BIG_ENDIAN,1, (double *) &sag_in, &sag); + cor_in = ptr->Cor; + acr_get_double(ACR_BIG_ENDIAN,1, (double *) &cor_in, &cor); + tra_in = ptr->Tra; + acr_get_double(ACR_BIG_ENDIAN,1, (double *) &tra_in, &tra); + + (void) sprintf(string, "%.15g\\%.15g\\%.15g", sag, cor, tra); + + return acr_create_element_string + (get_elid(group_id, element_id, ACR_VR_DS), string); +} + + + + +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/spi_element_defs.h @@ -0,0 +1,86 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : spi_element_defs.h +@DESCRIPTION: Element definitions for spi +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 23, 1993 (Peter Neelin) +@MODIFIED : +@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. +---------------------------------------------------------------------------- */ + +/* Define siemens-specific stuff for associations and messages */ +#define SPI_VISION_AE_TITLE "MRC16505" /* changed by Leili from OC1SA1MR010000 to MRC16505 */ +#define SPI_VISION_IMPLEMENTATION_UID "1.3.12.2.1107.5.2" /* Chaged by leili from 2.16.840.1.113669.2.931128 to 1.3.12.2.1107.5.2 */ + +/* Element number for images */ +#define SPI_IMAGE_ELEMENT 0x0010 + +/* Define spi constants */ +#define SPI_TRANSVERSE_ORIENTATION 1 +#define SPI_SAGITTAL_ORIENTATION 2 +#define SPI_CORONAL_ORIENTATION 3 + +/* Element id's for SPI */ +GLOBAL_ELEMENT(SPI_Acquisition_columns , 0x0019, 0x1230, LO); +GLOBAL_ELEMENT(SPI_Reconstruction_columns , 0x0019, 0x1231, LO); +GLOBAL_ELEMENT(SPI_Sequence_file_name , 0x0019, 0x1511, LO); +GLOBAL_ELEMENT(SPI_Image_position , 0x0021, 0x1160, DS); +GLOBAL_ELEMENT(SPI_Image_normal , 0x0021, 0x1161, DS); +GLOBAL_ELEMENT(SPI_Image_row , 0x0021, 0x116a, DS); +GLOBAL_ELEMENT(SPI_Image_column , 0x0021, 0x116b, DS); +GLOBAL_ELEMENT(SPI_Number_of_3D_raw_partitions_nominal, 0x0021, 0x1330, IS); +GLOBAL_ELEMENT(SPI_Number_of_3D_image_partitions , 0x0021, 0x1334, IS); +GLOBAL_ELEMENT(SPI_Actual_3D_partition_number , 0x0021, 0x1336, IS); +GLOBAL_ELEMENT(SPI_Number_of_slices_nominal , 0x0021, 0x1340, IS); +GLOBAL_ELEMENT(SPI_Current_slice_number , 0x0021, 0x1342, IS); +GLOBAL_ELEMENT(SPI_Number_of_echoes , 0x0021, 0x1370, IS); + +// added by rhoge: +GLOBAL_ELEMENT(SPI_Registration_date , 0x0011, 0x1110, DA); +GLOBAL_ELEMENT(SPI_Registration_time , 0x0011, 0x1111, TM); +GLOBAL_ELEMENT(SPI_Parameter_file_name , 0x0019, 0x1510, CS); +GLOBAL_ELEMENT(SPI_Protocol , 0x0029, 0x1020, CS); + +// (these appear to be public groups, and should be moved +// to dicom_element_defs.h ? ) +GLOBAL_ELEMENT(SPI_Manufacturer , 0x0008, 0x0070, CS); +GLOBAL_ELEMENT(SPI_Manufacturer_model , 0x0008, 0x1090, CS); +GLOBAL_ELEMENT(SPI_Device_serial_number , 0x0018, 0x1000, CS); +GLOBAL_ELEMENT(SPI_Software_version , 0x0018, 0x1020, CS); +GLOBAL_ELEMENT(SPI_Receiving_coil , 0x0018, 0x1250, CS); + +GLOBAL_ELEMENT(SPI_Calibration_date , 0x0018, 0x1200, DA); +GLOBAL_ELEMENT(SPI_Total_measurement_time_cur , 0x0019, 0x1211, DS); +GLOBAL_ELEMENT(SPI_Nominal_number_of_fourier_lines , 0x0019, 0x1220, IS); +GLOBAL_ELEMENT(SPI_Number_of_fourier_lines_current , 0x0019, 0x1221, IS); +GLOBAL_ELEMENT(SPI_Number_of_fourier_lines_after_zero , 0x0019, 0x1226, IS); +GLOBAL_ELEMENT(SPI_Number_of_3d_raw_part_cur , 0x0021, 0x1331, IS); +GLOBAL_ELEMENT(SPI_Order_of_slices , 0x0021, 0x134f, IS); +GLOBAL_ELEMENT(SPI_First_measured_fourier_line , 0x0019, 0x1228, IS); +GLOBAL_ELEMENT(SPI_Number_of_prescans , 0x0019, 0x1270, IS); +GLOBAL_ELEMENT(SPI_Magnetic_field_strength , 0x0019, 0x1412, DS); +GLOBAL_ELEMENT(SPI_ADC_voltage , 0x0019, 0x1414, DS); +GLOBAL_ELEMENT(SPI_ADC_offset , 0x0019, 0x1416, DS); +GLOBAL_ELEMENT(SPI_Transmitter_amplitude , 0x0019, 0x1420, DS); +GLOBAL_ELEMENT(SPI_Receiver_amplifier_gain , 0x0019, 0x1451, DS); +GLOBAL_ELEMENT(SPI_Receiver_preamplifier_gain , 0x0019, 0x1452, DS); +GLOBAL_ELEMENT(SPI_Phase_gradient_amplitude , 0x0019, 0x1470, DS); +GLOBAL_ELEMENT(SPI_Readout_gradient_amplitude , 0x0019, 0x1471, DS); +GLOBAL_ELEMENT(SPI_Selection_gradient_amplitude , 0x0019, 0x1472, DS); +GLOBAL_ELEMENT(SPI_Sequence_file_owner , 0x0019, 0x1512, CS); +GLOBAL_ELEMENT(SPI_Sequence_description , 0x0019, 0x1513, CS); +GLOBAL_ELEMENT(SPI_Number_of_slices_cur , 0x0021, 0x1341, IS); +GLOBAL_ELEMENT(SPI_Window_center , 0x0028, 0x1050, IS); +GLOBAL_ELEMENT(SPI_Window_width , 0x0028, 0x1051, IS); + +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/string_to_filename.c @@ -0,0 +1,312 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : string_to_filename.c +@DESCRIPTION: Code to convert a string to something that can be used in a + file name. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : January 10, 1997 (Peter Neelin) +@MODIFIED : + * $Log: string_to_filename.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.2 2002/03/22 19:19:36 rhoge + * Numerous fixes - + * - handle Numaris 4 Dicom patient name + * - option to cleanup input files + * - command option + * - list-only option + * - debug mode + * - user supplied name, idstr + * - anonymization + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * + * Revision 6.1 1999/10/29 17:52:00 neelin + * Fixed Log keyword + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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 <dicomserver.h> +#include <ctype.h> + +#define SEPARATOR '_' + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : string_to_filename +@INPUT : string - string to convert + maxlen - maximum length of output string (including terminating + '\0') +@OUTPUT : filename - output string +@RETURNS : (nothing) +@DESCRIPTION: Routine to convert a string to something that can be used in + a filename +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : December 10, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void string_to_filename(char *string, char *filename, int maxlen) +{ + int length, isrc, idst; + int ch; + int found_first, need_separator; + + /* Get string length */ + length = strlen(string); + if (length > maxlen-1) length = maxlen - 1; + + /* Loop through characters */ + idst = 0; + found_first = FALSE; + need_separator = FALSE; + for (isrc=0; isrc < length; isrc++) { + ch = string[isrc]; + if (isalnum(ch)) { + found_first = TRUE; + if (need_separator) { + filename[idst++] = SEPARATOR; + need_separator = FALSE; + } + filename[idst++] = tolower(ch); + } + else if (found_first) { + need_separator = TRUE; + } + } + + /* Add terminating '\0' */ + filename[idst++] = '\0'; + + return; +} + +public void string_to_initials(char *string, char *filename, int maxlen) +{ + /* function added by R. Hoge to convert name-like strings to + initials in environment where confidentiality policy prohibits + use of names in file-names */ + + int length, isrc, idst; + int ch; + int first_found, sep_found, multi_word, comma_found, comma_found2, in_word; + + /* Get string length */ + length = strlen(string); + if (length > maxlen-1) length = maxlen - 1; + + /* do first pass to look for multi-words, commas */ + + first_found = FALSE; + sep_found = FALSE; + multi_word = FALSE; + comma_found = FALSE; + + for (isrc=0; isrc < length; isrc++) { + ch = string[isrc]; + + /* if we hit a separator after finding a first alphanum, treat + as multi words */ + + /* alphanumeric expressions (including underscores, hyphens) are treated + as discrete words - note that we won't print hyphens */ + + /* examples of single words: + + test5 + test-5 + test_5 + + examples of multi words: + + test 5 + snr_test 5 + test,5 + snr-test 5 + + */ + + if (sep_found && isalnum(ch)) { + multi_word = TRUE; + } + + if (first_found && !(isalnum(ch)||ch=='-'||ch=='_')) { + sep_found = TRUE; + } + + if (isalnum(ch)) { + first_found = TRUE; + } + + // Numaris 4 used caret (^) to separate names (Last^first) + if (ch == ',' || ch == '^') { + comma_found = TRUE; + } + } + + /* if Patient name is only a single word, then just strip out + non-alphanumeric characters + + examples: + + snrtest1 -> snrtest1 + snrtest-1 -> snrtest1 + snr_test-2 -> snr_test2 + + note that hyphens are omitted, because these + are used as delimiters in filename */ + + if (!multi_word) { + + idst = 0; + for (isrc=0; isrc < length; isrc++) { + ch = string[isrc]; + + if (isalnum(ch) || ch=='_') { + filename[idst++] = tolower(ch); + } + } + + /* Add terminating '\0' */ + filename[idst++] = '\0'; + + } else { /* multiple words */ + + if (!comma_found) { + + /* examples of multi-word no comma: + + john doe -> jd + john edward doe -> jed + john doe-smith -> jds + my snr_test -> mst + john doe 12 -> jd12 + john12 smith -> j12s + 12john smith -> 12js + john doe test2b -> jst2b + + note that underscores are treated as separators here, + contiguous digits are all printed, and digits + are treated as printable separators */ + + /* Loop through characters */ + idst = 0; + in_word = FALSE; + for (isrc=0; isrc < length; isrc++) { + ch = string[isrc]; + if (isalpha(ch) && !in_word) { + in_word = TRUE; + filename[idst++] = tolower(ch); + } + else if (isdigit(ch)) { + in_word = FALSE; + filename[idst++] = ch; + } + else if (!isalnum(ch)) { + in_word = FALSE; + } + } + + /* Add terminating '\0' */ + filename[idst++] = '\0'; + + } else { /* multiple words with comma separation */ + + /* examples of multi-word with comma: + + doe, john -> jd + doe,john -> jd + doe-smith, john -> jds + + note that we treat stuff before the comma as the LAST name */ + + /* we do two passes: all the stuff after the comma THEN + all the stuff before the comma */ + + idst = 0; + + /* Loop through characters, writing those after comma*/ + + comma_found2 = FALSE; + in_word = FALSE; + for (isrc=0; isrc < length; isrc++) { + ch = string[isrc]; + + if (isalpha(ch) && !in_word) { + in_word = TRUE; + if (comma_found2) filename[idst++] = tolower(ch); + } + else if (isdigit(ch)) { + in_word = FALSE; + if (comma_found2) filename[idst++] = ch; + } + else if (!isalnum(ch)) { + in_word = FALSE; + } + + // numaris 4 uses Last^First + if (ch == ',' || ch == '^') { + comma_found2 = TRUE; + } + } + + /* now write characters/initials before the comma*/ + + comma_found2 = FALSE; + in_word = FALSE; + for (isrc=0; isrc < length; isrc++) { + ch = string[isrc]; + + if (isalpha(ch) && !in_word) { + in_word = TRUE; + if (!comma_found2) filename[idst++] = tolower(ch); + } + else if (isdigit(ch)) { + in_word = FALSE; + if (!comma_found2) filename[idst++] = ch; + } + else if (!isalnum(ch)) { + in_word = FALSE; + } + + // Numaris 4 uses Last^First + if (ch == ',' || ch == '^') { + comma_found2 = TRUE; + } + } + + /* Add terminating '\0' */ + filename[idst++] = '\0'; + + } + } + + return; +} + +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/use_the_files.c @@ -0,0 +1,297 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : use_the_files.c +@DESCRIPTION: Code to do something with the files copied through dicom. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + * $Log: use_the_files.c,v $ + * Revision 1.1 2003-08-15 19:52:55 leili + * Initial revision + * + * Revision 1.8 2002/04/26 03:27:03 rhoge + * fixed MrProt problem - replaced fixed lenght char array with malloc + * + * Revision 1.7 2002/03/22 19:19:36 rhoge + * Numerous fixes - + * - handle Numaris 4 Dicom patient name + * - option to cleanup input files + * - command option + * - list-only option + * - debug mode + * - user supplied name, idstr + * - anonymization + * + * Revision 1.6 2002/03/19 22:10:16 rhoge + * removed time sorting for N4DCM mosaics - time is random for mosaics + * + * Revision 1.5 2001/12/31 18:27:21 rhoge + * modifications for dicomreader processing of Numaris 4 dicom files - at + * this point code compiles without warning, but does not deal with + * mosaiced files. Also will probably not work at this time for Numaris + * 3 .ima files. dicomserver may also not be functional... + * + * Revision 1.4 2001/04/19 19:09:04 rhoge + * added macro to turn off echos/frames in one file + * + * Revision 1.3 2001/02/26 06:16:00 rhoge + * modified to allow specification of destination directory on command line + * + * Revision 1.2 2000/12/11 17:44:49 rhoge + * added 1 to indices in debugging statements for consistency with number + * of elements + * + * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge + * imported sources to CVS repository on amoeba + * + * Revision 6.1 1999/10/29 17:52:00 neelin + * Fixed Log keyword + * + * Revision 6.0 1997/09/12 13:24:27 neelin + * Release of minc version 0.6 + * + * Revision 5.0 1997/08/21 13:25:26 neelin + * Release of minc version 0.5 + * + * Revision 4.0 1997/05/07 20:06:20 neelin + * Release of minc version 0.4 + * + * Revision 1.2 1997/03/11 13:10:48 neelin + * Working version of dicomserver. + * + * Revision 1.1 1997/03/04 20:56:47 neelin + * Initial revision + * +@COPYRIGHT : + Copyright 1997 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 <sys/types.h> +#include <unistd.h> +#include <dicomserver.h> + +/* Function prototypes */ +/* Commented out by rhoge, put back in by Leili */ +//int gethostname (char *name, size_t namelen); + +File_Type file_type; +char command_line[512]; +char *pname; + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : use_the_files +@INPUT : project_name - name to use for project file + num_files - number of image files + file_list - list of file names + UseArgDir - flag to use command line OutDir destination + OutDir - destination directory +@OUTPUT : (none) +@RETURNS : (nothing) +@DESCRIPTION: Routine to do something with the files. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : November 23, 1993 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ +public void use_the_files(char *project_name, + int num_files, char *file_list[], + Data_Object_Info *data_info[], + int UseArgDir,char *OutDir) + /* last three args added by rhoge */ +{ + int ifile; + int num_acq_files; + extern int Do_logging; + char **acq_file_list; + int *used_file; + int found_first; + double cur_study; + int cur_acq; + int cur_rec; + int cur_imgtyp; + int cur_echo; + int cur_dyn_scan; + int echoes_in_one_file; + int dyn_scans_in_one_file; + int exit_status; + char *output_file_name; + char file_prefix[256]; + int output_uid, output_gid; + char string[512]; + FILE *fp; + (void) fprintf(stderr, "The pname is: %s\n", pname); + /* The if statment commented out by Leili forcing the old-style project_file to be used since it is invoked by the server*/ + //if (!strncmp(pname,"dicomserver",11)) { + // use old-style project_file if invoked by server + (void) read_project_file(project_name, file_prefix, + &output_uid, &output_gid, + command_line, (int) sizeof(command_line)); + //} + + if (UseArgDir) { // if an output directory name has been + // provided on the command line + //(void) fprintf(stderr, "The output directory name has been provided \n"); + strcpy(file_prefix,OutDir); + } else if (!strncmp(project_name,"/",1)) { // AEtitle is destination + //(void) fprintf(stderr, "The output directory is the one specified in the AEtitle \n"); + strcpy(file_prefix,project_name); + } + + if (Do_logging > HIGH_LOGGING) { // debugging + fprintf(stderr, + "project_name: [%s] file_prefix: [%s]\n", + project_name,file_prefix); + } + + // Allocate space for acquisition file list + acq_file_list = MALLOC(num_files * sizeof(*acq_file_list)); + used_file = MALLOC(num_files * sizeof(*used_file)); + for (ifile=0; ifile < num_files; ifile++) + used_file[ifile] = FALSE; + + do { + + // Loop through files, looking for an acquisition + + // file groups should already have been sorted into acquisitions + // in calling program + + // this code is in a `do while' loop because we may + + found_first = FALSE; + num_acq_files = 0; + for (ifile=0; ifile < num_files; ifile++) { + + if (used_file[ifile]) continue; + if (!found_first) { + + /* found first file: set all current attributes like + study id, acq id, rec num(?), image type, echo number, + dyn scan number, flag for multiple echoes, flag for + multiple time points the flag input file as `used' */ + + found_first = TRUE; + cur_study = data_info[ifile]->study_id; + cur_acq = data_info[ifile]->acq_id; + cur_rec = data_info[ifile]->rec_num; + cur_imgtyp = data_info[ifile]->image_type; + cur_echo = data_info[ifile]->echo_number; + cur_dyn_scan = data_info[ifile]->dyn_scan_number; + + /* note that if there are only two echos OR only two time + points, we turn off dyn_scans_in_one_file on the + presumption that the user is more likely to treat them + as separate acquisitions than a scan with a time + dimension (turned off!!) */ + +#define MIN_FRAMES_PER_FILE 1 + + echoes_in_one_file = + (data_info[ifile]->num_echoes > MIN_FRAMES_PER_FILE); + dyn_scans_in_one_file = + (data_info[ifile]->num_dyn_scans > MIN_FRAMES_PER_FILE); + + used_file[ifile] = TRUE; + } + + /* otherwise check if attributes of the new input file match those + of the current output context and flag input file as `used' */ + + else if ((data_info[ifile]->study_id == cur_study) && + (data_info[ifile]->acq_id == cur_acq) && + (data_info[ifile]->rec_num == cur_rec) && + (data_info[ifile]->image_type == cur_imgtyp) && + ((data_info[ifile]->echo_number == cur_echo) || + echoes_in_one_file) && + ((data_info[ifile]->dyn_scan_number == cur_dyn_scan) || + dyn_scans_in_one_file)) { + + used_file[ifile] = TRUE; + } + if (used_file[ifile]) { + + /* if input file is flagged as `used', then add its index + to the list of files for this acquisition (and increment + counter) */ + + acq_file_list[num_acq_files] = file_list[ifile]; + num_acq_files++; + } + } // rhoge: end of loop over files + + // Use the files for this acquisition + + if (found_first) { + + // Print out the file names + if (Do_logging >= HIGH_LOGGING) { + (void) fprintf(stderr, "\nFiles copied:\n"); + for (ifile=0; ifile < num_acq_files; ifile++) { + (void) fprintf(stderr, " %s\n", acq_file_list[ifile]); + } + } + + // Create minc file + exit_status = siemens_dicom_to_minc(num_acq_files, acq_file_list, + NULL, FALSE, file_prefix, + &output_file_name); + + if (exit_status != EXIT_SUCCESS) continue; + + /* Print log message */ + if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, "Created minc file %s.\n", + output_file_name); + } + + /* Invoke a command on the file (if requested) and get the + returned file name */ + if (strlen(command_line) > (size_t) 0) { + (void) sprintf(string, "%s %s", command_line, output_file_name); + printf("-Applying command '%s' to output file... ",command_line); + (void) fflush(stdout); + if ((fp=popen(string, "r")) != NULL) { + (void) fscanf(fp, "%s", output_file_name); + if (pclose(fp) != EXIT_SUCCESS) { + (void) fprintf(stderr, + "Error executing command\n \"%s\"\n", + string); + } + else if (Do_logging >= LOW_LOGGING) { + (void) fprintf(stderr, + "Executed command \"%s\",\nproducing file %s.\n", + string, output_file_name); + } + } + else { + (void) fprintf(stderr, "Error executing command \"%s\"\n", + string); + } + printf("Done.\n"); + } + + /* Change the ownership */ + if ((output_uid != INT_MIN) && (output_gid != INT_MIN)) { + (void) chown(output_file_name, (uid_t) output_uid, + (gid_t) output_gid); + } + } + } while (found_first); + + /* Free acquisition file list */ + FREE(acq_file_list); + FREE(used_file); + +} + +
new file mode 100644 --- /dev/null +++ b/conversion/dicomserver_sonata/valid_element_ids @@ -0,0 +1,280 @@ +0x0008 0x0000 +0x0008 0x0008 +0x0008 0x0016 +0x0008 0x0018 +0x0008 0x0020 +0x0008 0x0022 +0x0008 0x0023 +0x0008 0x0030 +0x0008 0x0032 +0x0008 0x0033 +0x0008 0x0040 +0x0008 0x0041 +0x0008 0x0050 +0x0008 0x0060 +0x0008 0x0070 +0x0008 0x0080 +0x0008 0x0090 +0x0008 0x1010 +0x0008 0x1080 +0x0008 0x1090 +0x0009 0x0000 +0x0009 0x0010 +0x0009 0x0012 +0x0009 0x0013 +0x0009 0x1010 +0x0009 0x1015 +0x0009 0x1040 +0x0009 0x1041 +0x0009 0x1210 +0x0009 0x1226 +0x0009 0x1227 +0x0009 0x1316 +0x0009 0x1320 +0x0010 0x0000 +0x0010 0x0010 +0x0010 0x0020 +0x0010 0x0030 +0x0010 0x0040 +0x0010 0x1010 +0x0010 0x1030 +0x0011 0x0000 +0x0011 0x0010 +0x0011 0x0011 +0x0011 0x110a +0x0011 0x1110 +0x0011 0x1111 +0x0011 0x1122 +0x0011 0x1123 +0x0018 0x0000 +0x0018 0x0010 +0x0018 0x0020 +0x0018 0x0021 +0x0018 0x0022 +0x0018 0x0023 +0x0018 0x0024 +0x0018 0x0025 +0x0018 0x0050 +0x0018 0x0080 +0x0018 0x0081 +0x0018 0x0083 +0x0018 0x0084 +0x0018 0x0085 +0x0018 0x0086 +0x0018 0x0087 +0x0018 0x0088 +0x0018 0x0090 +0x0018 0x0091 +0x0018 0x0093 +0x0018 0x0094 +0x0018 0x1000 +0x0018 0x1020 +0x0018 0x1200 +0x0018 0x1201 +0x0018 0x1250 +0x0018 0x1314 +0x0018 0x1316 +0x0018 0x5100 +0x0019 0x0000 +0x0019 0x0010 +0x0019 0x0012 +0x0019 0x0015 +0x0019 0x1010 +0x0019 0x1020 +0x0019 0x1030 +0x0019 0x1050 +0x0019 0x1060 +0x0019 0x1210 +0x0019 0x1211 +0x0019 0x1212 +0x0019 0x1213 +0x0019 0x1214 +0x0019 0x1220 +0x0019 0x1221 +0x0019 0x1226 +0x0019 0x1228 +0x0019 0x1230 +0x0019 0x1231 +0x0019 0x1240 +0x0019 0x1241 +0x0019 0x1242 +0x0019 0x1245 +0x0019 0x1246 +0x0019 0x1250 +0x0019 0x1260 +0x0019 0x1270 +0x0019 0x1281 +0x0019 0x1282 +0x0019 0x1283 +0x0019 0x1285 +0x0019 0x1287 +0x0019 0x1290 +0x0019 0x1293 +0x0019 0x1294 +0x0019 0x1297 +0x0019 0x1298 +0x0019 0x12a0 +0x0019 0x12a1 +0x0019 0x1412 +0x0019 0x1414 +0x0019 0x1416 +0x0019 0x1420 +0x0019 0x1421 +0x0019 0x1422 +0x0019 0x1424 +0x0019 0x1426 +0x0019 0x1450 +0x0019 0x1451 +0x0019 0x1452 +0x0019 0x1454 +0x0019 0x1455 +0x0019 0x1456 +0x0019 0x1460 +0x0019 0x1462 +0x0019 0x1470 +0x0019 0x1471 +0x0019 0x1472 +0x0019 0x1480 +0x0019 0x1482 +0x0019 0x1490 +0x0019 0x14a0 +0x0019 0x14a2 +0x0019 0x14a5 +0x0019 0x14a6 +0x0019 0x14b0 +0x0019 0x14c1 +0x0019 0x14c2 +0x0019 0x14c3 +0x0019 0x14c4 +0x0019 0x14c5 +0x0019 0x14d1 +0x0019 0x14d2 +0x0019 0x14d3 +0x0019 0x14d4 +0x0019 0x14d5 +0x0019 0x14d6 +0x0019 0x14d7 +0x0019 0x14d8 +0x0019 0x14d9 +0x0019 0x14da +0x0019 0x1510 +0x0019 0x1511 +0x0019 0x1512 +0x0019 0x1513 +0x0019 0x1514 +0x0020 0x0000 +0x0020 0x000d +0x0020 0x000e +0x0020 0x0010 +0x0020 0x0011 +0x0020 0x0012 +0x0020 0x0013 +0x0020 0x0030 +0x0020 0x0032 +0x0020 0x0035 +0x0020 0x0037 +0x0020 0x0050 +0x0020 0x0052 +0x0020 0x0070 +0x0020 0x0080 +0x0020 0x1001 +0x0020 0x1020 +0x0020 0x1040 +0x0020 0x1041 +0x0020 0x3100 +0x0020 0x3401 +0x0020 0x3402 +0x0020 0x3403 +0x0020 0x3404 +0x0020 0x3405 +0x0020 0x3406 +0x0020 0x5000 +0x0020 0x5002 +0x0021 0x0000 +0x0021 0x0010 +0x0021 0x0011 +0x0021 0x0013 +0x0021 0x0023 +0x0021 0x1010 +0x0021 0x1011 +0x0021 0x1020 +0x0021 0x1120 +0x0021 0x1122 +0x0021 0x1124 +0x0021 0x1126 +0x0021 0x1130 +0x0021 0x1132 +0x0021 0x1160 +0x0021 0x1161 +0x0021 0x1163 +0x0021 0x1165 +0x0021 0x116a +0x0021 0x116b +0x0021 0x1170 +0x0021 0x1171 +0x0021 0x1180 +0x0021 0x1182 +0x0021 0x1322 +0x0021 0x1324 +0x0021 0x1330 +0x0021 0x1331 +0x0021 0x1334 +0x0021 0x1336 +0x0021 0x1339 +0x0021 0x1340 +0x0021 0x1341 +0x0021 0x1342 +0x0021 0x1343 +0x0021 0x1344 +0x0021 0x134f +0x0021 0x1356 +0x0021 0x1370 +0x0021 0x2300 +0x0021 0x2301 +0x0021 0x2302 +0x0021 0x2303 +0x0021 0x2304 +0x0021 0x2305 +0x0021 0x2306 +0x0021 0x2307 +0x0021 0x2308 +0x0021 0x2309 +0x0021 0x2310 +0x0021 0x2311 +0x0021 0x2312 +0x0021 0x2313 +0x0021 0x2314 +0x0021 0x2315 +0x0021 0x2316 +0x0021 0x2317 +0x0021 0x2318 +0x0021 0x2331 +0x0028 0x0000 +0x0028 0x0002 +0x0028 0x0004 +0x0028 0x0005 +0x0028 0x0010 +0x0028 0x0011 +0x0028 0x0030 +0x0028 0x0040 +0x0028 0x0050 +0x0028 0x0060 +0x0028 0x0100 +0x0028 0x0101 +0x0028 0x0102 +0x0028 0x0103 +0x0028 0x0200 +0x0028 0x1050 +0x0028 0x1051 +0x0028 0x1052 +0x0028 0x1053 +0x0029 0x0000 +0x0029 0x0011 +0x0029 0x1110 +0x0029 0x1120 +0x0029 0x1152 +0x0051 0x0000 +0x0051 0x0010 +0x0051 0x1010 +0x7fe0 0x0000 +0x7fe0 0x0010