Mercurial > hg > minc-tools
changeset 2099:be6c75043ba4
Initial checkin
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/conversion/Acr_nema/Makefile.am @@ -0,0 +1,60 @@ +AUTOMAKE_OPTIONS = check-news + +# This is the only header installed in the "includedir" +# +include_HEADERS = acr_nema.h + +# All other headers are installed in this subdirectory. +# +include_acr_nemadir = $(includedir)/acr_nema + +include_acr_nema_HEADERS = \ + acr_nema/acr_io.h \ + acr_nema/dicom_client_routines.h \ + acr_nema/dicom_network.h \ + acr_nema/element.h \ + acr_nema/file_io.h \ + acr_nema/group.h \ + acr_nema/message.h \ + acr_nema/value_repr.h + +# Libraries which must be built and installed. +# +lib_LTLIBRARIES = libacr_nema.la + +bin_PROGRAMS = \ + acr_test \ + dump_acr_nema \ + extract_acr_nema \ + read_acr_nema \ + dicom_test \ + copy_acr_nema + +noinst_PROGRAMS = \ + sample_dicom_client + +LDADD = libacr_nema.la + +acr_test_SOURCES = acr_test.c + +dump_acr_nema_SOURCES = dump_acr_nema.c + +extract_acr_nema_SOURCES = extract_acr_nema.c + +read_acr_nema_SOURCES = read_acr_nema.c + +dicom_test_SOURCES = dicom_test.c + +copy_acr_nema_SOURCES = copy_acr_nema.c + +libacr_nema_la_LDFLAGS = -version-info 1:0:1 +libacr_nema_la_SOURCES = \ + acr_io.c \ + dicom_client_routines.c \ + dicom_network.c \ + element.c \ + file_io.c \ + globals.c \ + group.c \ + message.c \ + value_repr.c
--- a/conversion/Acr_nema/acr_io.c +++ b/conversion/Acr_nema/acr_io.c @@ -7,7 +7,19 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : * $Log: acr_io.c,v $ - * Revision 6.5 2000-08-16 15:53:46 neelin + * Revision 6.7.2.1 2005-05-12 21:15:29 bert + * Initial checkin + * + * Revision 6.7 2005/03/04 00:15:14 bert + * Lose public and private keywords; change acr_read_one_element to assume implicit VR format for special sequence delimiters (group 0xfffe) + * + * Revision 6.6 2004/10/29 13:08:41 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.5 2000/08/16 15:53:46 neelin * Added VR type UN (unknown) which has a length field similar to OB. * * Revision 6.4 2000/05/01 17:54:02 neelin @@ -96,7 +108,6 @@ #include <stdlib.h> #include <stdio.h> #include <limits.h> -#include <minc_def.h> #include <acr_nema.h> /* Define constants */ @@ -104,9 +115,6 @@ # define TRUE 1 # define FALSE 0 #endif -#ifndef private -# define private static -#endif #define ACR_BYTE_ORDER_DEFAULT ACR_LITTLE_ENDIAN @@ -120,13 +128,13 @@ } *Data_Info; /* Private functions */ -private int test_vr(char vr_to_test[2], char *vr_list[]); -private int is_sequence_vr(char vr_to_test[2]); -private int is_special_vr(char vr_to_test[2]); -private Data_Info get_data_info(Acr_File *afp); -private void invert_values(Acr_byte_order byte_order, - long nvals, size_t value_size, - void *input_value, void *mach_value); +static int test_vr(char vr_to_test[2], char *vr_list[]); +static int is_sequence_vr(char vr_to_test[2]); +static int is_special_vr(char vr_to_test[2]); +static Data_Info get_data_info(Acr_File *afp); +static void invert_values(Acr_byte_order byte_order, + long nvals, size_t value_size, + void *input_value, void *mach_value); /* Macros */ #define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(a[0])) @@ -146,7 +154,7 @@ @CREATED : January 29, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private int test_vr(char vr_to_test[2], char *vr_list[]) +static int test_vr(char vr_to_test[2], char *vr_list[]) { int found_special, i; @@ -162,13 +170,13 @@ return found_special; } -private int is_sequence_vr(char vr_to_test[2]) +static int is_sequence_vr(char vr_to_test[2]) { static char *sequence_vrs[] = {"SQ", NULL}; return test_vr(vr_to_test, sequence_vrs); } -private int is_special_vr(char vr_to_test[2]) +static int is_special_vr(char vr_to_test[2]) { static char *special_vrs[] = {"OB", "OW", "SQ", "UN", NULL}; return test_vr(vr_to_test, special_vrs); @@ -187,7 +195,7 @@ @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private Data_Info get_data_info(Acr_File *afp) +static Data_Info get_data_info(Acr_File *afp) { Data_Info data_info; @@ -215,8 +223,7 @@ @CREATED : January 29, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_set_byte_order(Acr_File *afp, - Acr_byte_order byte_order) +void acr_set_byte_order(Acr_File *afp, Acr_byte_order byte_order) { Data_Info data_info; @@ -240,7 +247,7 @@ @CREATED : January 29, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_byte_order acr_get_byte_order(Acr_File *afp) +Acr_byte_order acr_get_byte_order(Acr_File *afp) { Data_Info data_info; @@ -264,7 +271,7 @@ @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_get_machine_byte_order(void) +int acr_get_machine_byte_order(void) { int dummy = 1; char *ptr = (char *) &dummy; @@ -296,7 +303,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : January 29, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public int acr_need_invert(Acr_byte_order byte_order) +int acr_need_invert(Acr_byte_order byte_order) { return (acr_get_machine_byte_order() != byte_order); } @@ -314,8 +321,7 @@ @CREATED : January 29, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_set_vr_encoding(Acr_File *afp, - Acr_VR_encoding_type vr_encoding) +void acr_set_vr_encoding(Acr_File *afp, Acr_VR_encoding_type vr_encoding) { Data_Info data_info; @@ -339,7 +345,7 @@ @CREATED : January 29, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_VR_encoding_type acr_get_vr_encoding(Acr_File *afp) +Acr_VR_encoding_type acr_get_vr_encoding(Acr_File *afp) { Data_Info data_info; @@ -366,8 +372,7 @@ @CREATED : April 28, 2000 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_set_ignore_errors(Acr_File *afp, - int ignore_nonfatal_protocol_errors) +void acr_set_ignore_errors(Acr_File *afp, int ignore_nonfatal_protocol_errors) { Data_Info data_info; @@ -392,7 +397,7 @@ @CREATED : April 28, 2000 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_ignore_protocol_errors(Acr_File *afp) +int acr_ignore_protocol_errors(Acr_File *afp) { Data_Info data_info; @@ -420,8 +425,8 @@ @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_reverse_byte_order(long nvals, size_t value_size, - void *input_values, void *output_values) +void acr_reverse_byte_order(long nvals, size_t value_size, + void *input_values, void *output_values) { long i, jlow, jhigh; char *ptr1, *ptr2, v0, v1; @@ -464,9 +469,9 @@ @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private void invert_values(Acr_byte_order byte_order, - long nvals, size_t value_size, - void *input_value, void *mach_value) +static void invert_values(Acr_byte_order byte_order, + long nvals, size_t value_size, + void *input_value, void *mach_value) { long i; char *ptr1, *ptr2; @@ -499,9 +504,9 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_get_short(Acr_byte_order byte_order, - long nvals, void *input_value, - unsigned short *mach_value) +void acr_get_short(Acr_byte_order byte_order, + long nvals, void *input_value, + unsigned short *mach_value) { invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_SHORT, input_value, mach_value); @@ -522,8 +527,8 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_get_long(Acr_byte_order byte_order, - long nvals, void *input_value, long *mach_value) +void acr_get_long(Acr_byte_order byte_order, + long nvals, void *input_value, long *mach_value) { invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_LONG, input_value, mach_value); @@ -545,8 +550,8 @@ @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_get_float(Acr_byte_order byte_order, - long nvals, void *input_value, float *mach_value) +void acr_get_float(Acr_byte_order byte_order, + long nvals, void *input_value, float *mach_value) { invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_FLOAT, input_value, mach_value); @@ -568,8 +573,8 @@ @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_get_double(Acr_byte_order byte_order, - long nvals, void *input_value, double *mach_value) +void acr_get_double(Acr_byte_order byte_order, + long nvals, void *input_value, double *mach_value) { invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_DOUBLE, input_value, mach_value); @@ -590,9 +595,9 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_put_short(Acr_byte_order byte_order, - long nvals, unsigned short *mach_value, - void *output_value) +void acr_put_short(Acr_byte_order byte_order, + long nvals, unsigned short *mach_value, + void *output_value) { invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_SHORT, mach_value, output_value); @@ -613,8 +618,8 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_put_long(Acr_byte_order byte_order, - long nvals, long *mach_value, void *output_value) +void acr_put_long(Acr_byte_order byte_order, + long nvals, long *mach_value, void *output_value) { invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_LONG, mach_value, output_value); @@ -635,8 +640,8 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_put_float(Acr_byte_order byte_order, - long nvals, float *mach_value, void *output_value) +void acr_put_float(Acr_byte_order byte_order, + long nvals, float *mach_value, void *output_value) { invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_FLOAT, mach_value, output_value); @@ -657,8 +662,8 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_put_double(Acr_byte_order byte_order, - long nvals, double *mach_value, void *output_value) +void acr_put_double(Acr_byte_order byte_order, + long nvals, double *mach_value, void *output_value) { invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_DOUBLE, mach_value, output_value); @@ -681,7 +686,7 @@ @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Status acr_skip_input_data(Acr_File *afp, long nbytes_to_skip) +Acr_Status acr_skip_input_data(Acr_File *afp, long nbytes_to_skip) { long i; int ch; @@ -727,8 +732,8 @@ @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Status acr_read_buffer(Acr_File *afp, unsigned char buffer[], - long nbytes_to_read, long *nbytes_read) +Acr_Status acr_read_buffer(Acr_File *afp, unsigned char buffer[], + long nbytes_to_read, long *nbytes_read) { long i; int ch; @@ -775,8 +780,8 @@ @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Status acr_unget_buffer(Acr_File *afp, unsigned char buffer[], - long nbytes_to_unget) +Acr_Status acr_unget_buffer(Acr_File *afp, unsigned char buffer[], + long nbytes_to_unget) { long i; @@ -810,8 +815,8 @@ @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Status acr_write_buffer(Acr_File *afp, unsigned char buffer[], - long nbytes_to_write, long *nbytes_written) +Acr_Status acr_write_buffer(Acr_File *afp, unsigned char buffer[], + long nbytes_to_write, long *nbytes_written) { long i; @@ -856,7 +861,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : January 29, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public Acr_Status acr_test_byte_order(Acr_File *afp) +Acr_Status acr_test_byte_order(Acr_File *afp) { long buflen; unsigned char buffer[2*ACR_SIZEOF_SHORT+ACR_SIZEOF_LONG]; @@ -917,6 +922,9 @@ &data_length2); } if (data_length2 >= ACR_TEST_MAX2) { + /* If we get here, we have completely failed to make sense of + * the byte ordering. + */ acr_set_byte_order(afp, old_byte_order); } } @@ -938,7 +946,7 @@ @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_copy_file_encoding(Acr_File *afp1, Acr_File *afp2) +void acr_copy_file_encoding(Acr_File *afp1, Acr_File *afp2) { acr_set_byte_order(afp2, acr_get_byte_order(afp1)); acr_set_vr_encoding(afp2, acr_get_vr_encoding(afp1)); @@ -957,8 +965,8 @@ @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_get_element_header_size(char vr_name[2], - Acr_VR_encoding_type vr_encoding) +int acr_get_element_header_size(char vr_name[2], + Acr_VR_encoding_type vr_encoding) { int length; @@ -985,8 +993,8 @@ @CREATED : February 5, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Status acr_peek_at_next_element_id(Acr_File *afp, - int *group_id, int *element_id) +Acr_Status acr_peek_at_next_element_id(Acr_File *afp, + int *group_id, int *element_id) { long buflen; unsigned char buffer[2*ACR_SIZEOF_SHORT]; @@ -1042,10 +1050,10 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : January 29, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public Acr_Status acr_read_one_element(Acr_File *afp, - int *group_id, int *element_id, - char vr_name[], - long *data_length, char **data_pointer) +Acr_Status acr_read_one_element(Acr_File *afp, + int *group_id, int *element_id, + char vr_name[], + long *data_length, char **data_pointer) { long buflen; unsigned char buffer[2*ACR_SIZEOF_SHORT+ACR_SIZEOF_LONG]; @@ -1071,7 +1079,7 @@ *element_id = elid; /* Look for VR and length of data */ - if (acr_get_vr_encoding(afp) == ACR_IMPLICIT_VR) { + if (grpid == ACR_ITEM_GROUP || acr_get_vr_encoding(afp) == ACR_IMPLICIT_VR) { vr_name[0] = '\0'; vr_name[1] = '\0'; acr_get_long(byte_order, 1, &buffer[offset], (long *) &datalen); @@ -1148,10 +1156,10 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : January 29, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public Acr_Status acr_write_one_element(Acr_File *afp, - int group_id, int element_id, - char vr_name[], - long data_length, char *data_pointer) +Acr_Status acr_write_one_element(Acr_File *afp, + int group_id, int element_id, + char vr_name[], + long data_length, char *data_pointer) { long buflen; unsigned char buffer[2*ACR_SIZEOF_SHORT+2*ACR_SIZEOF_LONG]; @@ -1233,7 +1241,7 @@ @CREATED : July 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public char *acr_status_string(Acr_Status status) +char *acr_status_string(Acr_Status status) { char *status_string;
--- a/conversion/Acr_nema/acr_nema.h +++ b/conversion/Acr_nema/acr_nema.h @@ -6,7 +6,19 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : * $Log: acr_nema.h,v $ - * Revision 6.1 1999-10-29 17:51:50 neelin + * Revision 6.3.2.1 2005-05-12 21:15:48 bert + * Initial checkin + * + * Revision 6.3 2005/02/16 19:22:32 bert + * Autoconfiscation + * + * Revision 6.2 2004/10/29 13:08:41 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.1 1999/10/29 17:51:50 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin @@ -75,11 +87,17 @@ #endif /* Include files */ -#include <file_io.h> -#include <acr_io.h> -#include <value_repr.h> -#include <element.h> -#include <group.h> -#include <message.h> -#include <dicom_network.h> -#include <dicom_client_routines.h> +#include <acr_nema/file_io.h> +#include <acr_nema/acr_io.h> +#include <acr_nema/value_repr.h> +#include <acr_nema/element.h> +#include <acr_nema/group.h> +#include <acr_nema/message.h> +#include <acr_nema/dicom_network.h> +#include <acr_nema/dicom_client_routines.h> + +/* these are pinched from minc_def.h */ +#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))
--- a/conversion/Acr_nema/acr_test.c +++ b/conversion/Acr_nema/acr_test.c @@ -5,7 +5,6 @@ #define GLOBAL_ELEMENT_DEFINITION #include <acr_nema.h> -#include <minc_def.h> GLOBAL_ELEMENT(ACR_Recognition_code, 0x0, 0x10, LO); GLOBAL_ELEMENT(Shadow_Recognition_code, 0x1, 0x10, LO);
new file mode 100755 --- /dev/null +++ b/conversion/Acr_nema/autogen.sh @@ -0,0 +1,20 @@ +#! /bin/sh + +cat <<EOF +Messages of the following type may be safely ignored. +Any other diagnostics may be a sign of trouble. +Let us know if something goes wrong. + + automake: configure.in: installing [...] + warning: AC_TRY_RUN called without default to allow cross compiling + + + +EOF + +aclocal +autoheader +libtoolize --automake +automake --add-missing +autoconf +
new file mode 100644 --- /dev/null +++ b/conversion/Acr_nema/configure.ac @@ -0,0 +1,24 @@ +AC_INIT +AC_CONFIG_SRCDIR([./acr_nema.h]) +AC_CONFIG_AUX_DIR(ac_config_aux) +AM_INIT_AUTOMAKE(acr_nema, 1.0) +AC_CONFIG_HEADERS([config.h]) + +AC_REVISION($Revision: 6.1.2.1 $) + +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET + +AM_PROG_CC_C_O +AC_PROG_RANLIB + +AC_DISABLE_SHARED +AC_PROG_LIBTOOL + +AC_CONFIG_FILES([ +Makefile +]) +AC_OUTPUT + +
--- a/conversion/Acr_nema/copy_acr_nema.c +++ b/conversion/Acr_nema/copy_acr_nema.c @@ -7,7 +7,16 @@ @CREATED : November 9, 2000 (Peter Neelin) @MODIFIED : * $Log: copy_acr_nema.c,v $ - * Revision 6.4 2001-11-08 14:17:05 neelin + * Revision 6.5.2.1 2005-05-12 21:15:30 bert + * Initial checkin + * + * Revision 6.5 2004/10/29 13:08:41 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.4 2001/11/08 14:17:05 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. @@ -39,7 +48,6 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <minc_def.h> #include <acr_nema.h> #define UNKNOWN_VR_ENCODING ((Acr_VR_encoding_type) -1)
new file mode 100644 --- /dev/null +++ b/conversion/Acr_nema/dicom.txt @@ -0,0 +1,490 @@ +// Group 8 +Specific_Character_Set 0x0008 0x0005 CS +Image_Type 0x0008 0x0008 CS +Instance_Creation_Date 0x0008 0x0012 DA +Instance_Creation_Time 0x0008 0x0013 TM +Instance_Creator_UID 0x0008 0x0014 UI +SOP_Class_UID 0x0008 0x0016 UI +SOP_Instance_UID 0x0008 0x0018 UI +Related_General_SOP_Class_UID 0x0008 0x001A UI +Original_Specialized_SOP_Class_UID 0x0008 0x001B UI +Study_Date 0x0008 0x0020 DA +Series_Date 0x0008 0x0021 DA +Acquisition_Date 0x0008 0x0022 DA +Content_Date 0x0008 0x0023 DA +Overlay_Date 0x0008 0x0024 DA +Curve_Date 0x0008 0x0025 DA +Acquisition_Datetime 0x0008 0x002A DT +Study_Time 0x0008 0x0030 TM +Series_Time 0x0008 0x0031 TM +Acquisition_Time 0x0008 0x0032 TM +Content_Time 0x0008 0x0033 TM +Overlay_Time 0x0008 0x0034 TM +Curve_Time 0x0008 0x0035 TM +Data_Set_Type 0x0008 0x0040 RET +Data_Set_Subtype 0x0008 0x0041 RET +Nuclear_Medicine_Series_Type 0x0008 0x0042 CS +Accession_Number 0x0008 0x0050 SH +Query/Retrieve_Level 0x0008 0x0052 CS +Retrieve_AE_Title 0x0008 0x0054 AE +Instance_Availability 0x0008 0x0056 CS +Failed_SOP_Instance_UID_List 0x0008 0x0058 UI +Modality 0x0008 0x0060 CS +Modalities_in_Study 0x0008 0x0061 CS +SOP_Classes_in_Study 0x0008 0x0062 UI +Conversion_Type 0x0008 0x0064 CS +Presentation_Intent_Type 0x0008 0x0068 CS +Manufacturer 0x0008 0x0070 LO +Institution_Name 0x0008 0x0080 LO +Institution_Address 0x0008 0x0081 ST +Institution_Code_Sequence 0x0008 0x0082 SQ +Referring_Physician's_Name 0x0008 0x0090 PN +Referring_Physician's_Address 0x0008 0x0092 ST +Referring_Physician's_Telephone_Numbers 0x0008 0x0094 SH +Referring_Physician_Identificiation_Sequence 0x0008 0x0096 SQ +Code_Value 0x0008 0x0100 SH +Coding_Scheme_Designator 0x0008 0x0102 SH +Coding_Scheme_Version 0x0008 0x0103 SH +Code_Meaning 0x0008 0x0104 LO +Mapping_Resource 0x0008 0x0105 CS +Context_Group_Version 0x0008 0x0106 DT +Context_Group_Local_Version 0x0008 0x0107 DT +// Some omitted fields +Timezone_Offset_From_UTC 0x0008 0x0201 SH +// Some fields omitted here... +Network_ID 0x0008 0x1000 RET +Station_Name 0x0008 0x1010 SH +Study_Description 0x0008 0x1030 LO +Procedure_Code_Sequence 0x0008 0x1032 SQ +Series_Description 0x0008 0x103E LO +Institutional_Department_Name 0x0008 0x1040 LO +Physician(s)_of_Record 0x0008 0x1048 PN +Physician(s)_of_Record_Identification_Sequence 0x0008 0x1049 SQ +Performing_Physician's_Name 0x0008 0x1050 PN +Performing_Physician_Idntification_Sequence 0x0008 0x1052 SQ +Name_of_Physician(s)_Reading_Study 0x0008 0x1060 PN +Physician(s)_Reading_Study_Identification_Sequence 0x0008 0x1062 SQ +Operator's_Name 0x0008 0x1070 PN +Operator_Identification_Sequence 0x0008 0x1072 SQ +Admitting_Diagnoses_Description 0x0008 0x1080 LO +Admitting_Diagnoses_Code_Sequence 0x0008 0x1084 SQ +Manufacturer's_Model_Name 0x0008 0x1090 LO +Referenced_Results_Sequence 0x0008 0x1100 SQ +Referenced_Study_Sequence 0x0008 0x1110 SQ +Referenced_Performed_Procedure_Step_Sequence 0x0008 0x1111 SQ +Referenced_Series_Sequence 0x0008 0x1115 SQ +Referenced_Patient_Sequence 0x0008 0x1120 SQ +Referenced_Visit_Sequence 0x0008 0x1125 SQ +Referenced_Overlay_Sequence 0x0008 0x1130 SQ +Referenced_Waveform_Sequence 0x0008 0x113A SQ +Referenced_Image_Sequence 0x0008 0x1140 SQ +Referenced_Curve_Sequence 0x0008 0x1145 SQ +Referenced_Instance_Sequence 0x0008 0x114A SQ +Referenced_SOP_Class_UID 0x0008 0x1150 UI +Referenced_SOP_Instance_UID 0x0008 0x1155 UI +Referenced_SOP_Sequence 0x0008 0x1199 SQ +Anatomic_Region_Sequence 0x0008 0x2218 SQ +Frame_Type 0x0008 0x9007 CS +Referenced_Raw_Data_Sequence 0x0008 0x9121 SQ +Pixel_Presentation 0x0008 0x9205 CS +Volumetric_Properties 0x0008 0x9206 CS +Volume_Based_Calculation_Technique 0x0008 0x9207 CS +Complex_Image_Component 0x0008 0x9208 CS +Acquisition_Contrast 0x0008 0x9209 CS +// More stuff omitted.. +// Group 10 +Patient's_Name 0x0010 0x0010 PN +Patient_ID 0x0010 0x0020 LO +Issuer_of_Patient_ID 0x0010 0x0021 LO +Patient's_Birth_Date 0x0010 0x0030 DA +Patient's_Birth_Time 0x0010 0x0032 TM +Patient's_Sex 0x0010 0x0040 CS +Patient's_Insurance_Plan_Code_Sequence 0x0010 0x0050 SQ +Patient's_Primary_Language_Code_Sequence 0x0010 0x0101 SQ +Patient's_Primary_Language_Code_Modifier_Sequence 0x0010 0x0102 SQ +Other_Patient_IDs 0x0010 0x1000 LO +Other_Patient_Names 0x0010 0x1001 PN +Patient's_Birth_Name 0x0010 0x1005 PN +Patient's_Age 0x0010 0x1010 AS +Patient's_Size 0x0010 0x1020 DS +Patient's_Weight 0x0010 0x1030 DS +Patient's_Address 0x0010 0x1040 LO +Insurance_Plan_Identification 0x0010 0x1050 RET +Patient's_Mother's_Birth_Name 0x0010 0x1060 PN +Military_Rank 0x0010 0x1080 LO +Branch_of_Service 0x0010 0x1081 LO +Medical_Record_Locator 0x0010 0x1090 LO +Medical_Alerts 0x0010 0x2000 LO +Contrast_Allergies 0x0010 0x2110 LO +Country_of_Residence 0x0010 0x2150 LO +Region_of_Residence 0x0010 0x2152 LO +Patient's_Telephone_Numbers 0x0010 0x2154 SH +Ethnic_Group 0x0010 0x2160 SH +Occupation 0x0010 0x2180 SH +Smoking_Status 0x0010 0x21A0 CS +Additional_Patient_History 0x0010 0x21B0 LT +Pregnancy_Status 0x0010 0x21C0 US +Last_Menstrual_Date 0x0010 0x21D0 DA +Patient's_Religious_Preference 0x0010 0x21F0 LO +Patient_Comments 0x0010 0x4000 LT +// Group 12 (clinical trial) +Clinical_Trial_Sponsor_Name 0x0012 0x0010 LO +Clinical_Trial_Protocol_ID 0x0012 0x0020 LO +Clinical_Trial_Protocol_Name 0x0012 0x0021 LO +Clinical_Trial_Site_ID 0x0012 0x0030 LO +Clinical_Trial_Site_Name 0x0012 0x0031 LO +Clinical_Trial_Subject_ID 0x0012 0x0040 LO +Clinical_Trial_Subject_Reading_ID 0x0012 0x0042 LO +Clinical_Trial_Time_Point_ID 0x0012 0x0050 LO +Clinical_Trial_Time_Point_Description 0x0012 0x0051 ST +Clinical_Trial_Coordinating_Center_Name 0x0012 0x0060 LO +// Group 18 +Contrast/Bolus_Agent 0x0018 0x0010 LO +Contrast/Bolus_Agent_Sequence 0x0018 0x0012 SQ +Contrast/Bolus_Agent_Administration_Route_Sequence 0x0018 0x0014 SQ +Body_Part_Examined 0x0018 0x0015 CS +Scanning_Sequence 0x0018 0x0020 CS +Sequence_Variant 0x0018 0x0021 CS +Scan_Options 0x0018 0x0022 CS +MR_Acquisition_Type 0x0018 0x0023 CS +Sequence_Name 0x0018 0x0024 SH +Angio_Flag 0x0018 0x0025 CS +Intervention_Drug_Information_Sequence 0x0018 0x0026 SQ +Intervention_Drug_Stop_Time 0x0018 0x0027 TM +Intervention_Drug_Dose 0x0018 0x0028 DS +Intervention_Drug_Sequence 0x0018 0x0029 SQ +Additional_Drug_Sequence 0x0018 0x002A SQ +Radionuclide 0x0018 0x0030 LO +Radiopharmaceutical 0x0018 0x0031 LO +Energy_Window_Centerline 0x0018 0x0032 DS +Energy_Window_Total_Width 0x0018 0x0033 DS +Intervention_Drug_Name 0x0018 0x0034 LO +Intervention_Drug_Start_Time 0x0018 0x0035 TM +Intervention_Sequence 0x0018 0x0036 SQ +Therapy_Type 0x0018 0x0037 CS +Intervention_Status 0x0018 0x0038 CS +Therapy_Description 0x0018 0x0039 CS +Intervention_Description 0x0018 0x003A ST +Cine_Rate 0x0018 0x0040 IS +Slice_Thickness 0x0018 0x0050 DS +KVP 0x0018 0x0060 DS +Counts_Accumulated 0x0018 0x0070 IS +Acquisition_Termination_Condition 0x0018 0x0071 CS +Effective_Duration 0x0018 0x0072 DS +Acquisition_Start_Condition 0x0018 0x0073 CS +Acquisition_Start_Condition_Data 0x0018 0x0074 IS +Acquisition_Termination_Condition_Data 0x0018 0x0075 IS +Repetition_Time 0x0018 0x0080 DS +Echo_Time 0x0018 0x0081 DS +Inversion_Time 0x0018 0x0082 DS +Number_of_Averages 0x0018 0x0083 DS +Imaging_Frequency 0x0018 0x0084 DS +Imaged_Nucleus 0x0018 0x0085 SH +Echo_Number(s) 0x0018 0x0086 IS +Magnetic_Field_Strength 0x0018 0x0087 DS +Spacing_Between_Slices 0x0018 0x0088 DS +Number_of_Phase_Encoding_Steps 0x0018 0x0089 IS +Data_Collection_Diameter 0x0018 0x0090 DS +Echo_Train_Length 0x0018 0x0091 IS +Percent_Sampling 0x0018 0x0093 DS +Percent_Phase_Field_of_View 0x0018 0x0094 DS +Pixel_Bandwidth 0x0018 0x0095 DS +Device_Serial_Number 0x0018 0x1000 LO +Plate_ID 0x0018 0x1004 LO +Secondary_Capture_Device_ID 0x0018 0x1010 LO +Hardcopy_Creation_Device_ID 0x0018 0x1011 LO +Date_of_Secondary_Capture 0x0018 0x1012 DA +Time_of_Secondary_Capture 0x0018 0x1014 TM +Secondary_Capture_Device_Manufacturer 0x0018 0x1016 LO +Hardcopy_Device_Manufacturer 0x0018 0x1017 LO +Secondary_Capture_Device_Manufacturer's_Model_name 0x0018 0x1018 LO +Secondary_Capture_Device_Software_Version(s) 0x0018 0x1019 LO +Hardcopy_Device_Software_Version 0x0018 0x101A LO +Hardcopy_Device_Manufacturer's_Model_Name 0x0018 0x101B LO +Software_Version 0x0018 0x1020 LO +Video_Image_Format_Acquired 0x0018 0x1022 SH +Digital_Image_Format_Acquired 0x0018 0x1022 LO +Protocol_Name 0x0018 0x1030 LO +Contrast/Bolus_Route 0x0018 0x1040 LO +Contrast/Bolus_Volume 0x0018 0x1041 DS +Contrast/Bolus_Start_Time 0x0018 0x1042 TM +Contrast/Bolus_Stop_Time 0x0018 0x1043 TM +Contrast/Bolus_Total_Dose 0x0018 0x1044 DS +Syringe_Counts 0x0018 0x1045 IS +Contrast_Flow_Rate 0x0018 0x1046 DS +Contrast_Flow_Duration 0x0018 0x1047 DS +Contrast/Bolus_Ingredient 0x0018 0x1048 CS +Contrast/Bolus_Ingredient_Concentration 0x0018 0x1049 DS +Spatial_Resolution 0x0018 0x1050 DS +Trigger_Time 0x0018 0x1060 DS +Trigger_Source_or_Type 0x0018 0x1061 LO +Nominal_Interval 0x0018 0x1062 IS +Frame_Time 0x0018 0x1063 DS +Framing_Type 0x0018 0x1064 LO +Frame_Time_Vector 0x0018 0x1065 DS +Frame_Delay 0x0018 0x1066 DS +Image_Trigger_Delay 0x0018 0x1067 DS +Multiplex_Group_Time_Offset 0x0018 0x1068 DS +Trigger_Time_Offset 0x0018 0x1069 DS +Synchronization_Trigger 0x0018 0x106A CS +// ... +Cardiac_Number_of_Images 0x0018 0x1090 IS +// ... +Date_of_Last_Calibration 0x0018 0x1200 DA +Time_of_Last_Calibration 0x0018 0x1201 TM +Convolution_Kernel 0x0018 0x1210 SH +Upper/Lower_Pixel_Values 0x0018 0x1240 RET +Actual_Frame_Duration 0x0018 0x1242 IS +Count_Rate 0x0018 0x1243 IS +Preferred_Playback_Sequencing 0x0018 0x1244 US +Receive_Coil_Name 0x0018 0x1250 SH +Transmit_Coil_Name 0x0018 0x1251 SH +Plate_Type 0x0018 0x1260 SH +Phosphor_Type 0x0018 0x1261 LO +Scan_Velocity 0x0018 0x1300 DS +Whole_Body_Technique 0x0018 0x1301 CS +Scan_Length 0x0018 0x1302 IS +Acquisition_Matrix 0x0018 0x1310 US +In-plane_Phase_Encoding_Direction 0x0018 0x1312 CS +Flip_Angle 0x0018 0x1314 DS +Variable_Flip_Angle_Flag 0x0018 0x1315 CS +SAR 0x0018 0x1316 DS +dB/dt 0x0018 0x1318 DS +Acquisition_Device_Processing_Description 0x0018 0x1400 LO +Acquisition_Device_Processing_Code 0x0018 0x1401 LO +Cassette_Orientation 0x0018 0x1402 CS +Cassette_Size 0x0018 0x1403 CS +Exposures_on_Plate 0x0018 0x1404 US +//... +Patient_Position 0x0018 0x5100 CS +//... +Content_Qualification 0x0018 0x9004 CS +//... +Applicable_Safety_Standard_Agency 0x0018 0x9174 CS +MR_Image_Frame_Type_Sequence 0x0018 0x9226 SQ + +Contrast/Bolus_Agent_Number 0x0018 0x9337 US +Contrast/Bolus_Ingredient_Code_Sequence 0x0018 0x9338 SQ +Contrast/Bolus_Usage_Sequence 0x0018 0x9341 SQ +Contrast/Bolus_Agent_Administered 0x0018 0x9342 CS +Contrast/Bolus_Agent_Detected 0x0018 0x9343 CS +Contrast/Bolus_Agent_Phase 0x0018 0x9344 CS +//... +// Group 20 (study) +Study_Instance_UID 0x0020 0x000D UI +Series_Instance_UID 0x0020 0x000E UI +Study_ID 0x0020 0x0010 SH +Series_Number 0x0020 0x0011 IS +Acquisition_Number 0x0020 0x0012 IS +Instance_Number 0x0020 0x0013 IS +Isotope_Number 0x0020 0x0014 IS +Phase_Number 0x0020 0x0015 IS +Interval_Number 0x0020 0x0016 IS +Time_Slot_Number 0x0020 0x0017 IS +Angle_Number 0x0020 0x0018 IS +Item_Number 0x0020 0x0019 IS +Patient_Orientation 0x0020 0x0020 CS +Overlay_Number 0x0020 0x0022 IS +Curve_Number 0x0020 0x0024 IS +Lookup_Table_Number 0x0020 0x0026 IS +Image_Position 0x0020 0x0030 RET +Image_Position_(Patient) 0x0020 0x0032 DS +Image_Orientation 0x0020 0x0035 RET +Image_Orientation_(Patient) 0x0020 0x0037 DS +Location 0x0020 0x0050 RET +Frame_of_Reference_UID 0x0020 0x0052 UI +Laterality 0x0020 0x0060 CS +Image_Laterality 0x0020 0x0062 CS +Image_Geometry_Type 0x0020 0x0070 RET +Masking_Image 0x0020 0x0080 RET +Temporal_Position_Identifier 0x0020 0x0100 IS +Number_of_Temporal_Positions 0x0020 0x0105 IS +Temporal_Resolution 0x0020 0x0110 DS +Synchronization_Frame_of_Refernence_UID 0x0020 0x0200 UI +Series_in_Study 0x0020 0x1000 IS +Acquisitions_in_Series 0x0020 0x1001 RET +Images_in_Acquisition 0x0020 0x1002 IS +Acquisitions_in_Study 0x0020 0x1004 IS +Reference 0x0020 0x1020 RET +Position_Reference_Indicator 0x0020 0x1040 LO +Slice_Location 0x0020 0x1041 DS +Other_Study_Numbers 0x0020 0x1070 IS +Number_of_Patient_Related_Studies 0x0020 0x1200 IS +Number_of_Patient_Related_Series 0x0020 0x1202 IS +Number_of_Patient_Related_Instances 0x0020 0x1204 IS +Number_of_Study_Related_Series 0x0020 0x1206 IS +Number_of_Study_Related_Instances 0x0020 0x1208 IS +//... +Image_Comments 0x0020 0x4000 LT +//... +Frame_Anatomy_Sequence 0x0020 0x9071 SQ +Frame_Laterality 0x0020 0x9072 CS +Frame_Content_Sequence 0x0020 0x9111 SQ +Plane_Position_Sequence 0x0020 0x9113 SQ +Plane_Orientation_Sequence 0x0020 0x9116 SQ +Frame_Acquisition_Number 0x0020 0x9156 US +//... +Dimension_Organization_Sequence 0x0020 0x9221 SQ +Dimension_Index_Sequence 0x0020 0x9222 SQ +//... +//Group 28 +Samples_per_Pixel 0x0028 0x0002 US +Samples_per_Pixel_Used 0x0028 0x0003 US +Photometric_Interpretation 0x0028 0x0004 CS +Image_Dimensions 0x0028 0x0005 RET +Planar_Configuration 0x0028 0x0006 US +Number_of_Frames 0x0028 0x0008 IS +Frame_Increment_Pointer 0x0028 0x0009 AT +Frame_Dimension_Pointer 0x0028 0x000A AT +Rows 0x0028 0x0010 US +Columns 0x0028 0x0011 US +Planes 0x0028 0x0012 US +Ultrasound_Color_Data_Present 0x0028 0x0014 US +Pixel_Spacing 0x0028 0x0030 DS +Zoom_Factor 0x0028 0x0031 DS +Zoom_Center 0x0028 0x0032 DS +Pixel_Aspect_Ratio 0x0028 0x0034 IS +Image_Format 0x0028 0x0040 RET +Manipulated_Image 0x0028 0x0050 RET +Corrected_Image 0x0028 0x0051 CS +Compression_Code 0x0028 0x0060 RET +Bits_Allocated 0x0028 0x0100 US +Bits_Stored 0x0028 0x0101 US +High_Bit 0x0028 0x0102 US +Pixel_Representation 0x0028 0x0103 US +Smallest_Valid_Pixel_Value 0x0028 0x0104 RET +Largest_Valid_Pixel_Value 0x0028 0x0105 RET +Smallest_Image_Pixel_Value 0x0028 0x0106 US +Largest_Image_Pixel_Value 0x0028 0x0107 US +Smallest_Pixel_Value_in_Series 0x0028 0x0108 US +Largest_Pixel_Value_in_Series 0x0028 0x0109 US +Smallest_Pixel_Value_in_Plane 0x0028 0x0110 US +Largest_Pixel_Value_in_Plane 0x0028 0x0111 US +Pixel_Padding_Value 0x0028 0x0120 US +Image_Location 0x0028 0x0200 RET +Quality_Control_Image 0x0028 0x0300 CS +Burned_In_Annotation 0x0028 0x0301 CS +Pixel_Intensity_Relationship 0x0028 0x1040 CS +Pixel_Intensity_Relationship_Sign 0x0028 0x1041 SS +Window_Center 0x0028 0x1050 DS +Window_Width 0x0028 0x1051 DS +Rescale_Intercept 0x0028 0x1052 DS +Rescale_Slope 0x0028 0x1053 DS +Rescale_Type 0x0028 0x1054 LO +Window_Center&Width_Explanation 0x0028 0x1055 LO +Gray_Scale 0x0028 0x1080 RET +Lossy_Image_Compression 0x0028 0x2110 CS +Lossy_Image_Compression_Ratio 0x0028 0x2112 DS +Lossy_Image_Compression_Method 0x0028 0x2114 CS +LUT_Explanation 0x0028 0x3003 LO +Pixel_Measures_Sequence 0x0028 0x9110 SQ +Frame_VOI_LUT_Sequence 0x0028 0x9132 SQ +Pixel_Value_Transformation_Sequence 0x0028 0x9145 SQ +//... +//Group 32 +Requested_Procedure_Description 0x0032 0x1060 LO +Study_Comments 0x0032 0x4000 LT +//Group 40 +Performed_Procedure_Step_Start_Date 0x0040 0x0244 DA +Performed_Procedure_Step_Start_Time 0x0040 0x0245 TM +Performed_Procedure_Step_ID 0x0040 0x0253 SH +Acquisition_Context_Sequence 0x0040 0x0555 SQ +Measurement_Units_Code_Sequence 0x0040 0x08EA SQ +Real_World_Value_Mapping_Sequence 0x0040 0x9096 SQ +LUT_Label 0x0040 0x9210 SH +Real_World_Value_Last_Value_Mapped 0x0040 0x9211 US +Real_World_Value_LUT_Data 0x0040 0x9212 FD +Real_World_Value_First_Value_Mapped 0x0040 0x9216 US +Real_World_Value_Intercept 0x0040 0x9224 FD +Real_World_Value_Slope 0x0040 0x9225 FD +//Group 54 +Energy_Window_Vector 0x0054 0x0010 US +Number_of_Energy_Windows 0x0054 0x0011 US +Energy_Window_Information_Sequence 0x0054 0x0012 SQ +Energy_Window_Range_Sequence 0x0054 0x0013 SQ +Energy_Window_Lower_Limit 0x0054 0x0014 DS +Energy_Window_Upper_Limit 0x0054 0x0015 DS +Radiopharmaceutical_Information_Sequence 0x0054 0x0016 SQ +Residual_Syringe_Counts 0x0054 0x0017 IS +Energy_Window_Name 0x0054 0x0018 SH +Detector_Vector 0x0054 0x0020 US +Number_of_Detectors 0x0054 0x0021 US +Detector_Information_Sequence 0x0054 0x0022 SQ +Phase_Vector 0x0054 0x0030 US +Number_of_Phases 0x0054 0x0031 US +Phase_Information_Sequence 0x0054 0x0032 SQ +Number_of_Frames_in_Phase 0x0054 0x0033 US +Phase_Delay 0x0054 0x0036 IS +Pause_Between_Frames 0x0054 0x0038 IS +Phase_Description 0x0054 0x0039 CS +Rotation_Vector 0x0054 0x0050 US +Number_of_Rotations 0x0054 0x0051 US +Rotation_Information_Sequence 0x0054 0x0052 SQ +Number_of_Frames_in_Rotation 0x0054 0x0053 US +R-R_Interval_Vector 0x0054 0x0060 US +Number_of_R-R_Intervals 0x0054 0x0061 US +Gated_Information_Sequence 0x0054 0x0062 SQ +Data_Information_Sequence 0x0054 0x0063 SQ +Time_Slot_Vector 0x0054 0x0070 US +Number_of_Time_Slots 0x0054 0x0071 US +Time_Slot_Information_Sequence 0x0054 0x0072 SQ +Time_Slot_Time 0x0054 0x0073 DS +Slice_Vector 0x0054 0x0080 US +Number_of_Slices 0x0054 0x0081 US +Angular_View_Vector 0x0054 0x0090 US +Time_Slice_Vector 0x0054 0x0100 US +Number_of_Time_Slices 0x0054 0x0101 US +Start_Angle 0x0054 0x0200 DS +Type_of_Detector_Motion 0x0054 0x0202 CS +Trigger_Vector 0x0054 0x0210 IS +Number_of_Triggers_in_Phase 0x0054 0x0211 US +View_Code_Sequence 0x0054 0x0220 SQ +View_Modifier_Code_Sequence 0x0054 0x0222 SQ +Radionuclide_Code_Sequence 0x0054 0x0300 SQ +Administration_Route_Code_Sequence 0x0054 0x0302 SQ +Radiopharmaceutical_Code_Sequence 0x0054 0x0304 SQ +Calibration_Data_Sequence 0x0054 0x0306 SQ +Energy_Window_Number 0x0054 0x0308 US +Image_ID 0x0054 0x0400 SH +Patient_Orientation_Code_Sequence 0x0054 0x0410 SQ +Patient_Orientation_Modifier_Code_Sequence 0x0054 0x0412 SQ +Patient_Gantry_Relationship_Code_Sequence 0x0054 0x0414 SQ +Slice_Progression_Direction 0x0054 0x0500 CS +Series_Type 0x0054 0x1000 CS +Units 0x0054 0x1001 CS +Counts_Source 0x0054 0x1002 CS +Reprojection_Method 0x0054 0x1004 CS +Randoms_Correction_Method 0x0054 0x1100 CS +Attenuation_Correction_Method 0x0054 0x1101 LO +Decay_Correction 0x0054 0x1102 CS +Reconstruction_Method 0x0054 0x1103 LO +Detector_Lines_of_Response_Used 0x0054 0x1104 LO +Scatter_Correction_Method 0x0054 0x1105 LO +Axial_Acceptance 0x0054 0x1200 DS +Axial_Mash 0x0054 0x1201 IS +Transverse_Mash 0x0054 0x1202 IS +Detector_Element_Size 0x0054 0x1203 DS +Concidence_Window_Width 0x0054 0x1210 DS +Secondary_Counts_Type 0x0054 0x1220 CS +Frame_Reference_Time 0x0054 0x1300 DS +Primary_(Prompts)_Counts_Accumulated 0x0054 0x1310 IS +Secondary_Counts_Accumulated 0x0054 0x1311 IS +Slice_Sensitivity_Factor 0x0054 0x1320 DS +Decay_Factor 0x0054 0x1321 DS +Dose_Calibration_Factor 0x0054 0x1322 DS +Scatter_Fraction_Factor 0x0054 0x1323 DS +Dead_Time_Factor 0x0054 0x1324 DS +Image_Index 0x0054 0x1330 US +Counts_Included 0x0054 0x1400 CS +Dead_Time_Correction_Flag 0x0054 0x1401 CS +//Group 0x2050 +Presentation_LUT_Sequence 0x2050 0x0010 SQ +Presentation_LUT_Shape 0x2050 0x0020 CS +Referenced_Presentation_LUT_Sequence 0x2050 0x0500 SQ +//Group 0x5200 +Shared_Functional_Groups_Sequence 0x5200 0x9229 SQ +Per_Frame_Functional_Groups_Sequence 0x5200 0x9230 SQ + +//Group 0x7fe0 +Pixel_Data 0x7fe0 0x0010 OW +
--- a/conversion/Acr_nema/dicom_client_routines.c +++ b/conversion/Acr_nema/dicom_client_routines.c @@ -6,7 +6,19 @@ @CREATED : May 6, 1997 (Peter Neelin) @MODIFIED : * $Log: dicom_client_routines.c,v $ - * Revision 6.19 2001-03-19 18:31:55 neelin + * Revision 6.21.2.1 2005-05-12 21:15:30 bert + * Initial checkin + * + * Revision 6.21 2005/02/16 19:22:32 bert + * Autoconfiscation + * + * Revision 6.20 2004/10/29 13:08:41 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.19 2001/03/19 18:31:55 neelin * Modifications to allow opening a stream to stdout (with no input) so * that a dicom stream can be captured. * @@ -110,7 +122,7 @@ ---------------------------------------------------------------------------- */ #ifndef lint -static char rcsid[]="$Header: /private-cvsroot/minc/conversion/Acr_nema/dicom_client_routines.c,v 6.19 2001-03-19 18:31:55 neelin Exp $"; +static char rcsid[]="$Header: /private-cvsroot/minc/conversion/Acr_nema/dicom_client_routines.c,v 6.21.2.1 2005-05-12 21:15:30 bert Exp $"; #endif #include <stdio.h> @@ -131,7 +143,6 @@ #include <string.h> #include <ctype.h> #include <time.h> -#include <minc_def.h> #include <acr_nema.h> /* Constants */
--- a/conversion/Acr_nema/dicom_network.c +++ b/conversion/Acr_nema/dicom_network.c @@ -6,7 +6,16 @@ @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : * $Log: dicom_network.c,v $ - * Revision 6.10 2001-03-19 18:30:32 neelin + * Revision 6.11.2.1 2005-05-12 21:15:30 bert + * Initial checkin + * + * Revision 6.11 2004/10/29 13:08:41 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.10 2001/03/19 18:30:32 neelin * Added function to set implementation uid and changed name of function * that gets it. * @@ -91,7 +100,6 @@ #include <ctype.h> #include <string.h> #include <time.h> -#include <minc_def.h> #include <acr_nema.h> /* Constants */
--- a/conversion/Acr_nema/dicom_test.c +++ b/conversion/Acr_nema/dicom_test.c @@ -3,7 +3,6 @@ #include <limits.h> #include <acr_nema.h> -#include <minc_def.h> #if 0 #define WRITING
--- a/conversion/Acr_nema/dump_acr_nema.c +++ b/conversion/Acr_nema/dump_acr_nema.c @@ -6,7 +6,19 @@ @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : * $Log: dump_acr_nema.c,v $ - * Revision 6.5 2001-11-08 14:17:05 neelin + * Revision 6.7.2.1 2005-05-12 21:15:30 bert + * Initial checkin + * + * Revision 6.7 2005/03/11 22:19:59 bert + * add '-t' option to parse files which contain lists of field names with corresponding group and element id's. + * + * Revision 6.6 2004/10/29 13:08:41 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.5 2001/11/08 14:17:05 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. @@ -84,10 +96,90 @@ #include <stdlib.h> #include <stdio.h> +#include <ctype.h> #include <string.h> -#include <minc_def.h> #include <acr_nema.h> +#define N_NAME 31 + +struct table_element { + struct table_element *next; + unsigned short el_id; + char name[N_NAME+1]; +}; + +struct table_group { + struct table_group *next; + struct table_element *list; + unsigned short grp_id; +}; + +struct table_group *_head; + +char *get_name(unsigned short grp_id, unsigned short el_id) +{ + struct table_group *tg_ptr; + struct table_element *te_ptr; + + for (tg_ptr = _head; tg_ptr; tg_ptr = tg_ptr->next) { + if (tg_ptr->grp_id == grp_id) { + for (te_ptr = tg_ptr->list; te_ptr; te_ptr = te_ptr->next) { + if (te_ptr->el_id == el_id) { + return (te_ptr->name); + } + } + } + } + return (NULL); +} + +void parse_table(char *filename) +{ + FILE *fp; + char line[1024]; + char name[1024]; + unsigned int el_id; /* Must be int for sscanf */ + unsigned int grp_id; /* Must be int for sscanf */ + char vr[1024]; + struct table_element *te_ptr; + struct table_group *tg_ptr; + + fp = fopen(filename, "r"); + if (fp == NULL) { + return; + } + + while (fgets(line, sizeof(line), fp)) { + if (!isalnum(line[0])) { + continue; /* Ignore */ + } + if (sscanf(line, "%s %x %x %s\n", name, &grp_id, &el_id, vr) != 4) { + continue; + } + + for (tg_ptr = _head; tg_ptr; tg_ptr = tg_ptr->next) { + if (tg_ptr->grp_id == grp_id) { + break; + } + } + if (tg_ptr == NULL) { + tg_ptr = malloc(sizeof(struct table_group)); + tg_ptr->next = _head; + _head = tg_ptr; + tg_ptr->list = NULL; + tg_ptr->grp_id = (unsigned short) grp_id; + } + + te_ptr = malloc(sizeof(struct table_element)); + te_ptr->el_id = (unsigned short) el_id; + te_ptr->next = tg_ptr->list; + tg_ptr->list = te_ptr; + strncpy(te_ptr->name, name, N_NAME); + } + fclose(fp); +} + + #define UNKNOWN_VR_ENCODING ((Acr_VR_encoding_type) -1) int main(int argc, char *argv[]) @@ -107,7 +199,7 @@ char *ptr; int iarg, argcounter; char *arg; - char *usage = "Usage: %s [-h] [-i] [-b] [-l] [-e] [<file> [<max group>]]\n"; + char *usage = "Usage: %s [-h] [-i] [-b] [-l] [-e] [-t <table>] [<file> [<max group>]]\n"; /* Check arguments */ pname = argv[0]; @@ -120,9 +212,17 @@ exit(EXIT_FAILURE); } switch (arg[1]) { + case 't': + if (iarg < argc - 1) { + parse_table(argv[++iarg]); + _acr_name_proc = get_name; + break; + } + /* Fall through */ case 'h': (void) fprintf(stderr, "Options:\n"); (void) fprintf(stderr, " -h:\tPrint this message\n"); + (void) fprintf(stderr, " -t <table>:\tUse table to decode element names\n"); (void) fprintf(stderr, " -i:\tIgnore protocol errors\n"); (void) fprintf(stderr, " -b:\tAssume big-endian data\n"); (void) fprintf(stderr, " -l:\tAssume little-endian data\n");
--- a/conversion/Acr_nema/element.c +++ b/conversion/Acr_nema/element.c @@ -6,7 +6,25 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : * $Log: element.c,v $ - * Revision 6.3 2002-12-07 01:37:24 neelin + * Revision 6.7.2.1 2005-05-12 21:15:30 bert + * Initial checkin + * + * Revision 6.7 2005/04/18 23:22:29 bert + * Initialize newlist in acr_copy_element() to avoid problems with empty lists + * + * Revision 6.6 2005/03/11 22:05:29 bert + * Implement _acr_name_proc to allow printing of field names in dump_acr_nema + * + * Revision 6.5 2005/03/04 00:25:54 bert + * Avoid memory leak by freeing unused elements in a sequence. Fix order of initialization in acr_create_element() to set variable length property correctly. Don't change VR encoding when parsing a sequence, rely on the new handling of 0xfffe group items by the acr_io functions + * + * Revision 6.4 2004/10/29 13:08:41 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.3 2002/12/07 01:37:24 neelin * Added missing type * * Revision 6.2 2001/12/12 19:00:54 neelin @@ -93,14 +111,13 @@ #include <string.h> #include <ctype.h> #include <math.h> -#include <minc_def.h> #include <acr_nema.h> /* Private functions */ -private void delete_element_data(Acr_Element element); -private Acr_Element create_element_mem(Acr_Element_Id elid, - Acr_VR_Type vr_code, - size_t value_size, void *value); +static void delete_element_data(Acr_Element element); +static Acr_Element create_element_mem(Acr_Element_Id elid, + Acr_VR_Type vr_code, + size_t value_size, void *value); /* Macros */ #define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(a[0])) @@ -128,9 +145,9 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : February 4, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public Acr_Element acr_create_element(int group_id, int element_id, - Acr_VR_Type vr_code, - long data_length, char *data_pointer) +Acr_Element acr_create_element(int group_id, int element_id, + Acr_VR_Type vr_code, + long data_length, char *data_pointer) { Acr_Element element; @@ -144,9 +161,9 @@ acr_set_element_vr(element, vr_code); acr_set_element_vr_encoding(element, ACR_EXPLICIT_VR); acr_set_element_byte_order(element, acr_get_machine_byte_order()); - acr_set_element_variable_length(element, (data_length < 0)); acr_set_element_next(element, NULL); acr_set_element_data(element, data_length, data_pointer); + acr_set_element_variable_length(element, (data_length < 0)); return element; } @@ -164,7 +181,7 @@ @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private void delete_element_data(Acr_Element element) +static void delete_element_data(Acr_Element element) { char *data_pointer; @@ -193,7 +210,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : February 4, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public void acr_delete_element(Acr_Element element) +void acr_delete_element(Acr_Element element) { if (element == NULL) return; @@ -215,7 +232,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_delete_element_list(Acr_Element element_list) +void acr_delete_element_list(Acr_Element element_list) { Acr_Element next, cur; @@ -246,8 +263,8 @@ @CREATED : February 11, 1997 (P.N.) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Element acr_element_list_add(Acr_Element element_list, - Acr_Element element) +Acr_Element acr_element_list_add(Acr_Element element_list, + Acr_Element element) { Acr_Element current; @@ -281,8 +298,8 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_set_element_id(Acr_Element element, - int group_id, int element_id) +void acr_set_element_id(Acr_Element element, + int group_id, int element_id) { element->group_id = group_id; element->element_id = element_id; @@ -303,8 +320,8 @@ @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_set_element_vr(Acr_Element element, - Acr_VR_Type vr_code) +void acr_set_element_vr(Acr_Element element, + Acr_VR_Type vr_code) { element->vr_code = (short) vr_code; return; @@ -324,8 +341,8 @@ @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_set_element_vr_encoding(Acr_Element element, - Acr_VR_encoding_type vr_encoding) +void acr_set_element_vr_encoding(Acr_Element element, + Acr_VR_encoding_type vr_encoding) { element->uses_explicit_vr = (vr_encoding == ACR_EXPLICIT_VR); return; @@ -345,8 +362,8 @@ @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_set_element_byte_order(Acr_Element element, - Acr_byte_order byte_order) +void acr_set_element_byte_order(Acr_Element element, + Acr_byte_order byte_order) { element->has_little_endian_order = (byte_order == ACR_LITTLE_ENDIAN); return; @@ -367,8 +384,8 @@ @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_set_element_variable_length(Acr_Element element, - int has_variable_length) +void acr_set_element_variable_length(Acr_Element element, + int has_variable_length) { if (acr_element_is_sequence(element)) { element->has_variable_length = (has_variable_length != FALSE); @@ -396,8 +413,8 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : February 4, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public void acr_set_element_data(Acr_Element element, - long data_length, char *data_pointer) +void acr_set_element_data(Acr_Element element, + long data_length, char *data_pointer) { Acr_Element item, last, last2; long last_length; @@ -465,7 +482,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_set_element_next(Acr_Element element, Acr_Element next) +void acr_set_element_next(Acr_Element element, Acr_Element next) { element->next = next; return; @@ -483,7 +500,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_get_element_group(Acr_Element element) +int acr_get_element_group(Acr_Element element) { return element->group_id; } @@ -500,7 +517,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_get_element_element(Acr_Element element) +int acr_get_element_element(Acr_Element element) { return element->element_id; } @@ -517,7 +534,7 @@ @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_VR_Type acr_get_element_vr(Acr_Element element) +Acr_VR_Type acr_get_element_vr(Acr_Element element) { return (Acr_VR_Type) element->vr_code; } @@ -534,7 +551,7 @@ @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_VR_encoding_type acr_get_element_vr_encoding(Acr_Element element) +Acr_VR_encoding_type acr_get_element_vr_encoding(Acr_Element element) { return (element->uses_explicit_vr ? ACR_EXPLICIT_VR : ACR_IMPLICIT_VR); } @@ -551,7 +568,7 @@ @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_element_is_sequence(Acr_Element element) +int acr_element_is_sequence(Acr_Element element) { return element->is_sequence; } @@ -568,7 +585,7 @@ @CREATED : February 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_byte_order acr_get_element_byte_order(Acr_Element element) +Acr_byte_order acr_get_element_byte_order(Acr_Element element) { return (element->has_little_endian_order ? ACR_LITTLE_ENDIAN : ACR_BIG_ENDIAN); @@ -586,7 +603,7 @@ @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_element_has_variable_length(Acr_Element element) +int acr_element_has_variable_length(Acr_Element element) { return element->has_variable_length; } @@ -604,7 +621,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public long acr_get_element_length(Acr_Element element) +long acr_get_element_length(Acr_Element element) { long data_length; @@ -629,7 +646,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public char *acr_get_element_data(Acr_Element element) +char *acr_get_element_data(Acr_Element element) { return element->data_pointer; } @@ -639,7 +656,7 @@ @INPUT : element vr_encoding - ACR_IMPLICIT_VR or ACR_EXPLICIT_VR @OUTPUT : (none) -@RETURNS : total length for element +@RETURNS : total length for element, or zero if error. @DESCRIPTION: Get total length for element in ACR-NEMA representation depending on VR @METHOD : @@ -648,12 +665,19 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public long acr_get_element_total_length(Acr_Element element, - Acr_VR_encoding_type vr_encoding) +long acr_get_element_total_length(Acr_Element element, + Acr_VR_encoding_type vr_encoding) { + /* bert- verify that the VR name is non-null. This protects against + * core dumps when reading improperly-formatted files. + */ + char *vr_name = acr_get_vr_name(acr_get_element_vr(element)); + if (vr_name == NULL) { + return (0); + } + return acr_get_element_length(element) + - acr_get_element_header_size(acr_get_vr_name(acr_get_element_vr(element)), - vr_encoding); + acr_get_element_header_size(vr_name, vr_encoding); } /* ----------------------------- MNI Header ----------------------------------- @@ -668,7 +692,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Element acr_get_element_next(Acr_Element element) +Acr_Element acr_get_element_next(Acr_Element element) { return element->next; } @@ -685,7 +709,7 @@ @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : February 4, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public Acr_Element acr_copy_element(Acr_Element element) +Acr_Element acr_copy_element(Acr_Element element) { Acr_Element copy; long length; @@ -700,6 +724,7 @@ length = -1; olditem = (Acr_Element) acr_get_element_data(element); newitem = NULL; + newlist = NULL; /* bert- initialize in case list is empty */ while (olditem != NULL) { if (newitem == NULL) { newlist = acr_copy_element(olditem); @@ -754,7 +779,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Status acr_input_element(Acr_File *afp, Acr_Element *element) +Acr_Status acr_input_element(Acr_File *afp, Acr_Element *element) { int group_id, element_id, item_gid, item_elid; long data_length; @@ -764,11 +789,13 @@ int is_sequence, more_to_read, found_delimiter, has_variable_length; Acr_Element item, itemlist, previtem; Acr_VR_Type vr_code; - Acr_VR_encoding_type old_vr_encoding; + Acr_VR_encoding_type vr_encoding; /* Set element in case of error */ *element = NULL; + vr_encoding = acr_get_vr_encoding(afp); + /* Read in the value */ status = acr_read_one_element(afp, &group_id, &element_id, vr_name, &data_length, &data_pointer); @@ -785,16 +812,16 @@ if ((data_length > 0) && (data_pointer == NULL)) { is_sequence = TRUE; } + has_variable_length = (data_length < 0); /* If we have a sequence, read in all the items and store them as a - list of elements. We must turn off explicit VR encoding for items. */ + list of elements. */ if (is_sequence) { more_to_read = TRUE; itemlist = NULL; - old_vr_encoding = acr_get_vr_encoding(afp); - acr_set_vr_encoding(afp, ACR_IMPLICIT_VR); + while (more_to_read) { /* Read in an item */ @@ -803,7 +830,7 @@ /* If we know the length of the whole sequence, check it */ if ((status == ACR_OK) && (data_length > 0)) { - data_length -= acr_get_element_total_length(item, ACR_IMPLICIT_VR); + data_length -= acr_get_element_total_length(item, vr_encoding); if (data_length < 0) status = ACR_PROTOCOL_ERROR; } @@ -825,6 +852,9 @@ } previtem = item; } + else { + free(item); /* Avoid leaking memory */ + } /* Check for end of items */ if ((data_length == 0) || found_delimiter || (status != ACR_OK)) { @@ -832,9 +862,6 @@ } } /* End of loop over items */ - /* Restore the VR encoding */ - acr_set_vr_encoding(afp, old_vr_encoding); - /* Save the item list as the data */ data_pointer = (char *) itemlist; } @@ -866,7 +893,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Status acr_output_element(Acr_File *afp, Acr_Element element) +Acr_Status acr_output_element(Acr_File *afp, Acr_Element element) { char *vr_name; Acr_VR_Type vr_code; @@ -957,8 +984,8 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_convert_element_byte_order(Acr_Element element, - Acr_byte_order byte_order) +void acr_convert_element_byte_order(Acr_Element element, + Acr_byte_order byte_order) { Acr_byte_order element_byte_order; long nvalues; @@ -1015,8 +1042,8 @@ @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_match_element_id(Acr_Element_Id elid, - Acr_Element element) +int acr_match_element_id(Acr_Element_Id elid, + Acr_Element element) { return ((elid->group_id == acr_get_element_group(element)) && (elid->element_id == acr_get_element_element(element))); @@ -1037,8 +1064,8 @@ @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Element acr_find_element_id(Acr_Element element_list, - Acr_Element_Id elid) +Acr_Element acr_find_element_id(Acr_Element element_list, + Acr_Element_Id elid) { Acr_Element element; @@ -1071,7 +1098,7 @@ @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void *acr_memdup(size_t value_size, void *value) +void *acr_memdup(size_t value_size, void *value) { char *copy, *original; size_t i; @@ -1100,9 +1127,9 @@ @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private Acr_Element create_element_mem(Acr_Element_Id elid, - Acr_VR_Type vr_code, - size_t value_size, void *value) +static Acr_Element create_element_mem(Acr_Element_Id elid, + Acr_VR_Type vr_code, + size_t value_size, void *value) { return acr_create_element(elid->group_id, elid->element_id, vr_code, value_size, acr_memdup(value_size, value)); @@ -1121,8 +1148,8 @@ @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Element acr_create_element_short(Acr_Element_Id elid, - unsigned short value) +Acr_Element acr_create_element_short(Acr_Element_Id elid, + unsigned short value) { return create_element_mem(elid, ACR_VR_US, sizeof(value), (void *) &value); } @@ -1140,8 +1167,8 @@ @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Element acr_create_element_long(Acr_Element_Id elid, - long value) +Acr_Element acr_create_element_long(Acr_Element_Id elid, + long value) { return create_element_mem(elid, ACR_VR_UL, sizeof(value), (void *) &value); } @@ -1162,8 +1189,8 @@ @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Element acr_create_element_numeric(Acr_Element_Id elid, - double value) +Acr_Element acr_create_element_numeric(Acr_Element_Id elid, + double value) { char string[256]; Acr_Element element; @@ -1192,8 +1219,8 @@ @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Element acr_create_element_string(Acr_Element_Id elid, - char *value) +Acr_Element acr_create_element_string(Acr_Element_Id elid, + char *value) { long data_length; long alloc_length; @@ -1243,8 +1270,8 @@ @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Element acr_create_element_sequence(Acr_Element_Id elid, - Acr_Element itemlist) +Acr_Element acr_create_element_sequence(Acr_Element_Id elid, + Acr_Element itemlist) { return acr_create_element(elid->group_id, elid->element_id, ACR_VR_SQ, -1L, (char *) itemlist); @@ -1263,7 +1290,7 @@ @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public unsigned short acr_get_element_short(Acr_Element element) +unsigned short acr_get_element_short(Acr_Element element) { unsigned short value; @@ -1288,7 +1315,7 @@ @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public long acr_get_element_long(Acr_Element element) +long acr_get_element_long(Acr_Element element) { long value; @@ -1313,7 +1340,7 @@ @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public double acr_get_element_numeric(Acr_Element element) +double acr_get_element_numeric(Acr_Element element) { double value; @@ -1337,7 +1364,7 @@ @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public char *acr_get_element_string(Acr_Element element) +char *acr_get_element_string(Acr_Element element) { return acr_get_string_vr(acr_get_element_vr(element), @@ -1363,8 +1390,8 @@ @CREATED : February 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public long acr_get_element_short_array(Acr_Element element, long max_values, - unsigned short values[]) +long acr_get_element_short_array(Acr_Element element, long max_values, + unsigned short values[]) { long nvalues; @@ -1407,7 +1434,7 @@ @CREATED : February 27, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int *acr_element_numeric_array_separator(int character) +int *acr_element_numeric_array_separator(int character) { static int *separator_list = NULL; static int nseparators = 0; @@ -1457,8 +1484,8 @@ @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_get_element_numeric_array(Acr_Element element, - int max_values, double values[]) +int acr_get_element_numeric_array(Acr_Element element, + int max_values, double values[]) { char *start, *end, *cur, *prev; int *separator_list; @@ -1519,8 +1546,8 @@ @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_dump_element_list(FILE *file_pointer, - Acr_Element element_list) +void acr_dump_element_list(FILE *file_pointer, + Acr_Element element_list) { #define INDENT_AMOUNT 3 Acr_Element cur_element; @@ -1548,11 +1575,23 @@ /* Print the element id */ (void) fprintf(file_pointer, - "0x%04x 0x%04x length = %d :", + "0x%04x 0x%04x length = %d ", acr_get_element_group(cur_element), acr_get_element_element(cur_element), (int) acr_get_element_length(cur_element)); + if (_acr_name_proc != NULL) { + char *name_ptr; + + name_ptr = (*_acr_name_proc)(acr_get_element_group(cur_element), + acr_get_element_element(cur_element)); + + if (name_ptr != NULL) { + fprintf(file_pointer, "(%s)", name_ptr); + } + } + fprintf(file_pointer, ":"); + /* Print value if needed */ vr_code = acr_get_element_vr(cur_element); if (acr_element_is_sequence(cur_element)) {
--- a/conversion/Acr_nema/extract_acr_nema.c +++ b/conversion/Acr_nema/extract_acr_nema.c @@ -6,7 +6,16 @@ @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : * $Log: extract_acr_nema.c,v $ - * Revision 6.5 2001-11-08 14:17:05 neelin + * Revision 6.6.2.1 2005-05-12 21:15:30 bert + * Initial checkin + * + * Revision 6.6 2004/10/29 13:08:41 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.5 2001/11/08 14:17:05 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. @@ -71,7 +80,6 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <minc_def.h> #include <acr_nema.h> #define UNKNOWN_VR_ENCODING ((Acr_VR_encoding_type) -1)
--- a/conversion/Acr_nema/file_io.c +++ b/conversion/Acr_nema/file_io.c @@ -6,7 +6,22 @@ @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : * $Log: file_io.c,v $ - * Revision 6.4 2001-11-08 14:17:05 neelin + * Revision 6.7.2.1 2005-05-12 21:15:30 bert + * Initial checkin + * + * Revision 6.7 2005/03/04 00:09:00 bert + * Lose private and public + * + * Revision 6.6 2005/02/16 19:22:32 bert + * Autoconfiscation + * + * Revision 6.5 2004/10/29 13:08:41 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.4 2001/11/08 14:17:05 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. @@ -88,8 +103,7 @@ #include <unistd.h> #include <errno.h> #include <limits.h> -#include <minc_def.h> -#include <file_io.h> +#include <acr_nema/file_io.h> /* Define some constants */ #define ACR_MAX_BUFFER_LENGTH (64*1024) @@ -122,7 +136,7 @@ @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_file_enable_trace(Acr_File *afp) +void acr_file_enable_trace(Acr_File *afp) { afp->do_trace = TRUE; } @@ -139,7 +153,7 @@ @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_file_disable_trace(Acr_File *afp) +void acr_file_disable_trace(Acr_File *afp) { afp->do_trace = FALSE; } @@ -161,9 +175,9 @@ @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : February 5, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public Acr_File *acr_file_initialize(void *io_data, - int maxlength, - Acr_Io_Routine io_routine) +Acr_File *acr_file_initialize(void *io_data, + int maxlength, + Acr_Io_Routine io_routine) { Acr_File *afp; @@ -173,7 +187,7 @@ } /* Allocate the strcture */ - afp = MALLOC(sizeof(*afp)); + afp = malloc(sizeof(*afp)); /* Initialize fields */ afp->io_data = io_data; @@ -191,7 +205,7 @@ afp->client_data = NULL; /* Allocate the buffer */ - afp->start = MALLOC((size_t) afp->buffer_length); + afp->start = malloc((size_t) afp->buffer_length); /* Set ptr and end to start so that we can detect beginning of i/o */ afp->length = 0; @@ -217,22 +231,22 @@ @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_file_free(Acr_File *afp) +void acr_file_free(Acr_File *afp) { if (afp != NULL) { if (afp->stream_type == ACR_WRITE_STREAM) { (void) acr_file_flush(afp); } if (afp->start != NULL) { - FREE(afp->start); + free(afp->start); } if (afp->tracefp != NULL) { (void) fclose(afp->tracefp); } if (afp->client_data != NULL) { - FREE(afp->client_data); + free(afp->client_data); } - FREE(afp); + free(afp); } return; } @@ -250,7 +264,7 @@ @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_file_reset(Acr_File *afp) +void acr_file_reset(Acr_File *afp) { afp->watchpoint_set = FALSE; afp->bytes_to_watchpoint = 0; @@ -277,8 +291,8 @@ @CREATED : May 17, 2000 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_file_set_ismore_function(Acr_File *afp, - Acr_Ismore_Function ismore_function) +void acr_file_set_ismore_function(Acr_File *afp, + Acr_Ismore_Function ismore_function) { afp->ismore_function = ismore_function; } @@ -298,7 +312,7 @@ @CREATED : March 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_file_set_eof(Acr_File *afp) +void acr_file_set_eof(Acr_File *afp) { afp->reached_eof = TRUE; } @@ -320,10 +334,10 @@ @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_file_set_client_data(Acr_File *afp, void *client_data) +void acr_file_set_client_data(Acr_File *afp, void *client_data) { if (afp->client_data != NULL) { - FREE(afp->client_data); + free(afp->client_data); } afp->client_data = client_data; } @@ -340,7 +354,7 @@ @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void *acr_file_get_client_data(Acr_File *afp) +void *acr_file_get_client_data(Acr_File *afp) { return afp->client_data; } @@ -358,7 +372,7 @@ @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : February 5, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public int acr_file_read_more(Acr_File *afp) +int acr_file_read_more(Acr_File *afp) { int nread, ichar; char trace_file[128]; @@ -434,8 +448,7 @@ if (afp->do_trace) { if (afp->tracefp == NULL) { (void) strcpy(trace_file, Input_trace_file); - (void) mktemp(trace_file); - afp->tracefp = fopen(trace_file, "w"); + afp->tracefp = fdopen(mkstemp(trace_file), "w"); if (afp->tracefp != NULL) { (void) fprintf(stderr, "Opened input trace file %s.\n", trace_file); @@ -482,7 +495,7 @@ @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : February 5, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public int acr_file_write_more(Acr_File *afp, int character) +int acr_file_write_more(Acr_File *afp, int character) { /* Check the pointer */ @@ -517,7 +530,7 @@ @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : February 5, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public int acr_file_flush(Acr_File *afp) +int acr_file_flush(Acr_File *afp) { int length, nwritten; char trace_file[128]; @@ -549,8 +562,7 @@ if (afp->do_trace) { if (afp->tracefp == NULL) { (void) strcpy(trace_file, Output_trace_file); - (void) mktemp(trace_file); - afp->tracefp = fopen(trace_file, "w"); + afp->tracefp = fdopen(mkstemp(trace_file), "w"); if (afp->tracefp != NULL) { (void) fprintf(stderr, "Opened output trace file %s.\n", trace_file); @@ -607,7 +619,7 @@ @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : February 5, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public int acr_ungetc(int c, Acr_File *afp) +int acr_ungetc(int c, Acr_File *afp) { /* Check the pointer */ @@ -645,7 +657,7 @@ @CREATED : February 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void *acr_file_get_io_data(Acr_File *afp) +void *acr_file_get_io_data(Acr_File *afp) { return afp->io_data; } @@ -666,7 +678,7 @@ @CREATED : February 5, 1997 (P.N.) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_set_io_watchpoint(Acr_File *afp, long bytes_to_watchpoint) +void acr_set_io_watchpoint(Acr_File *afp, long bytes_to_watchpoint) { if (afp == NULL) return; @@ -710,7 +722,7 @@ @CREATED : February 5, 1997 (P.N.) @MODIFIED : ---------------------------------------------------------------------------- */ -public long acr_get_io_watchpoint(Acr_File *afp) +long acr_get_io_watchpoint(Acr_File *afp) { if ((afp == NULL) || !afp->watchpoint_set) return ACR_NO_WATCHPOINT; @@ -738,7 +750,7 @@ @CREATED : May 17, 2000 (P.N.) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_file_ismore(Acr_File *afp) +int acr_file_ismore(Acr_File *afp) { int retval; @@ -791,7 +803,7 @@ @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_stdio_read(void *io_data, void *buffer, int nbytes) +int acr_stdio_read(void *io_data, void *buffer, int nbytes) { FILE *fp; int nread; @@ -821,7 +833,7 @@ @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_stdio_write(void *io_data, void *buffer, int nbytes) +int acr_stdio_write(void *io_data, void *buffer, int nbytes) { FILE *fp; int nwritten; @@ -855,7 +867,7 @@ @CREATED : May 17, 2000 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_stdio_ismore(void *io_data) +int acr_stdio_ismore(void *io_data) { FILE *fp; int val;
--- a/conversion/Acr_nema/globals.c +++ b/conversion/Acr_nema/globals.c @@ -24,5 +24,4 @@ #include <stdio.h> #include <limits.h> #include <ctype.h> -#include <minc_def.h> #include <acr_nema.h>
--- a/conversion/Acr_nema/group.c +++ b/conversion/Acr_nema/group.c @@ -6,7 +6,25 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : * $Log: group.c,v $ - * Revision 6.6 2002-12-08 22:31:34 neelin + * Revision 6.10.2.1 2005-05-12 21:15:30 bert + * Initial checkin + * + * Revision 6.10 2005/05/09 15:34:46 bert + * For acr_find_{short,int,long,double}, treat a zero-length element as if it were absent, and return the default value. + * + * Revision 6.9 2005/03/11 22:05:29 bert + * Implement _acr_name_proc to allow printing of field names in dump_acr_nema + * + * Revision 6.8 2005/03/04 17:09:11 bert + * Change several functions to return Acr_Status instead of void; lose public and private; Make insert_element() check the return value of acr_get_element_total_length() + * + * Revision 6.7 2004/10/29 13:08:42 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.6 2002/12/08 22:31:34 neelin * When a last fragment is received, the dicom watchpoint is only updated when the next read happens, so the peek ahead will fail after the watchpoint test in acr_input_group_with_max. * * Revision 6.5 2001/11/08 14:17:05 neelin @@ -113,20 +131,21 @@ #include <stdio.h> #include <ctype.h> #include <string.h> -#include <minc_def.h> #include <acr_nema.h> /* Private functions */ -private void steal_element(Acr_Group group, Acr_Element element, +static void steal_element(Acr_Group group, Acr_Element element, + Acr_Element previous); +static void remove_element(Acr_Group group, Acr_Element element, Acr_Element previous); -private void remove_element(Acr_Group group, Acr_Element element, - Acr_Element previous); -private void insert_element(Acr_Group group, Acr_Element element, - Acr_Element previous); -private void update_group_length_element(Acr_Group group, - Acr_VR_encoding_type vr_encoding); -private Acr_Status acr_input_group_with_max(Acr_File *afp, Acr_Group *group, - int max_group_id); +static Acr_Status insert_element(Acr_Group group, Acr_Element element, + Acr_Element previous); +static void update_group_length_element(Acr_Group group, + Acr_VR_encoding_type vr_encoding); +static Acr_Status acr_input_group_with_max(Acr_File *afp, Acr_Group *group, + int max_group_id); + +acr_name_proc_t _acr_name_proc = NULL; /* ----------------------------- MNI Header ----------------------------------- @@ -141,7 +160,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : February 4, 1997 (P.N.) ---------------------------------------------------------------------------- */ -public Acr_Group acr_create_group(int group_id) +Acr_Group acr_create_group(int group_id) { Acr_Group group; Acr_Element length_element; @@ -183,7 +202,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_delete_group(Acr_Group group) +void acr_delete_group(Acr_Group group) { acr_delete_element_list(group->list_head); @@ -204,7 +223,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_delete_group_list(Acr_Group group_list) +void acr_delete_group_list(Acr_Group group_list) { Acr_Group next, cur; @@ -234,7 +253,7 @@ @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Group acr_copy_group(Acr_Group group) +Acr_Group acr_copy_group(Acr_Group group) { Acr_Group copy; Acr_Element cur; @@ -264,7 +283,7 @@ @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Group acr_copy_group_list(Acr_Group group_list) +Acr_Group acr_copy_group_list(Acr_Group group_list) { Acr_Group copy_list; Acr_Group copy_group; @@ -301,8 +320,8 @@ @CREATED : November 6, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private void steal_element(Acr_Group group, Acr_Element element, - Acr_Element previous) +static void steal_element(Acr_Group group, Acr_Element element, + Acr_Element previous) { Acr_Element next; @@ -347,8 +366,8 @@ @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private void remove_element(Acr_Group group, Acr_Element element, - Acr_Element previous) +static void remove_element(Acr_Group group, Acr_Element element, + Acr_Element previous) { /* Get rid of the old element from the group */ @@ -366,7 +385,7 @@ previous - pointer to previous element or NULL if beginning of group element list @OUTPUT : (none) -@RETURNS : (nothing) +@RETURNS : Acr_Status @DESCRIPTION: Insert an element into a group. @METHOD : @GLOBALS : @@ -374,10 +393,11 @@ @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private void insert_element(Acr_Group group, Acr_Element element, - Acr_Element previous) +static Acr_Status insert_element(Acr_Group group, Acr_Element element, + Acr_Element previous) { Acr_Element next; + long length; /* Update the pointers */ if (previous != NULL) { /* Middle or tail of list */ @@ -397,15 +417,23 @@ /* Update the group fields */ group->nelements++; - group->implicit_total_length += - acr_get_element_total_length(element, ACR_IMPLICIT_VR); - group->explicit_total_length += - acr_get_element_total_length(element, ACR_EXPLICIT_VR); + length = acr_get_element_total_length(element, ACR_IMPLICIT_VR); + if (length <= 0) { + return (ACR_OTHER_ERROR); + } + group->implicit_total_length += length; + + length = acr_get_element_total_length(element, ACR_EXPLICIT_VR); + if (length <= 0) { + return (ACR_OTHER_ERROR); + } + group->explicit_total_length += length; + /* Update the length element */ update_group_length_element(group, acr_get_element_vr_encoding(group->list_head)); - + return (ACR_OK); } /* ----------------------------- MNI Header ----------------------------------- @@ -413,7 +441,7 @@ @INPUT : group element @OUTPUT : (none) -@RETURNS : (nothing) +@RETURNS : Acr_Status @DESCRIPTION: Insert an element into a group. If an element of the same id already exists in the list, it is removed and deleted. @METHOD : @@ -422,8 +450,8 @@ @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_group_insert_element(Acr_Group group, - Acr_Element element) +Acr_Status acr_group_insert_element(Acr_Group group, + Acr_Element element) { Acr_Element next_element, prev_element, cur_element; int element_id; @@ -478,7 +506,7 @@ } /* Insert the new element */ - insert_element(group, element, prev_element); + return insert_element(group, element, prev_element); } @@ -487,7 +515,7 @@ @INPUT : group element @OUTPUT : (none) -@RETURNS : (nothing) +@RETURNS : Acr_Status @DESCRIPTION: Add an element to an acr-nema group @METHOD : @GLOBALS : @@ -495,7 +523,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_group_add_element(Acr_Group group, Acr_Element element) +Acr_Status acr_group_add_element(Acr_Group group, Acr_Element element) { /* Check that the element belongs in this group */ if (group->group_id != acr_get_element_group(element)) { @@ -508,9 +536,7 @@ } /* Insert the element at the tail */ - insert_element(group, element, group->list_tail); - - return; + return insert_element(group, element, group->list_tail); } /* ----------------------------- MNI Header ----------------------------------- @@ -526,7 +552,7 @@ @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_group_remove_element(Acr_Group group, int element_id) +void acr_group_remove_element(Acr_Group group, int element_id) { Acr_Element next_element, prev_element; @@ -564,7 +590,7 @@ @CREATED : November 6, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_group_steal_element(Acr_Group group, Acr_Element element) +void acr_group_steal_element(Acr_Group group, Acr_Element element) { int element_id; Acr_Element next_element, prev_element; @@ -605,8 +631,8 @@ @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private void update_group_length_element(Acr_Group group, - Acr_VR_encoding_type vr_encoding) +static void update_group_length_element(Acr_Group group, + Acr_VR_encoding_type vr_encoding) { long group_length; Acr_Element length_element; @@ -647,7 +673,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_set_group_next(Acr_Group group, Acr_Group next) +void acr_set_group_next(Acr_Group group, Acr_Group next) { group->next = next; return; @@ -665,7 +691,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_get_group_group(Acr_Group group) +int acr_get_group_group(Acr_Group group) { return group->group_id; } @@ -682,7 +708,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Element acr_get_group_element_list(Acr_Group group) +Acr_Element acr_get_group_element_list(Acr_Group group) { return group->list_head; } @@ -700,8 +726,8 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public long acr_get_group_total_length(Acr_Group group, - Acr_VR_encoding_type vr_encoding) +long acr_get_group_total_length(Acr_Group group, + Acr_VR_encoding_type vr_encoding) { if (vr_encoding == ACR_IMPLICIT_VR) return group->implicit_total_length; @@ -721,7 +747,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_get_group_nelements(Acr_Group group) +int acr_get_group_nelements(Acr_Group group) { return group->nelements; } @@ -738,7 +764,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Group acr_get_group_next(Acr_Group group) +Acr_Group acr_get_group_next(Acr_Group group) { return group->next; } @@ -760,8 +786,8 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private Acr_Status acr_input_group_with_max(Acr_File *afp, Acr_Group *group, - int max_group_id) +static Acr_Status acr_input_group_with_max(Acr_File *afp, Acr_Group *group, + int max_group_id) { int group_id, element_id, next_group_id; long group_length; @@ -894,7 +920,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Status acr_input_group(Acr_File *afp, Acr_Group *group) +Acr_Status acr_input_group(Acr_File *afp, Acr_Group *group) { return acr_input_group_with_max(afp, group, 0); @@ -914,7 +940,7 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Status acr_output_group(Acr_File *afp, Acr_Group group) +Acr_Status acr_output_group(Acr_File *afp, Acr_Group group) { long ielement, nelements; Acr_Element cur, next; @@ -965,8 +991,8 @@ @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Status acr_input_group_list(Acr_File *afp, Acr_Group *group_list, - int max_group_id) +Acr_Status acr_input_group_list(Acr_File *afp, Acr_Group *group_list, + int max_group_id) { Acr_Group cur_group, next_group; Acr_Status status; @@ -1016,7 +1042,7 @@ @CREATED : November 6, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Group acr_find_group(Acr_Group group_list, int group_id) +Acr_Group acr_find_group(Acr_Group group_list, int group_id) { Acr_Group group; int next_id; @@ -1050,8 +1076,8 @@ @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Element acr_find_group_element(Acr_Group group_list, - Acr_Element_Id elid) +Acr_Element acr_find_group_element(Acr_Group group_list, + Acr_Element_Id elid) { Acr_Group group; @@ -1079,7 +1105,7 @@ @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_dump_group_list(FILE *file_pointer, Acr_Group group_list) +void acr_dump_group_list(FILE *file_pointer, Acr_Group group_list) { Acr_Group cur_group; @@ -1130,13 +1156,13 @@ @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_find_short(Acr_Group group_list, Acr_Element_Id elid, - int default_value) +int acr_find_short(Acr_Group group_list, Acr_Element_Id elid, + int default_value) { Acr_Element element; element = acr_find_group_element(group_list, elid); - if (element != NULL) + if (element != NULL && acr_get_element_length(element) > 0) return (int) acr_get_element_short(element); else return default_value; @@ -1157,13 +1183,13 @@ @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public long acr_find_long(Acr_Group group_list, Acr_Element_Id elid, - long default_value) +long acr_find_long(Acr_Group group_list, Acr_Element_Id elid, + long default_value) { Acr_Element element; element = acr_find_group_element(group_list, elid); - if (element != NULL) + if (element != NULL && acr_get_element_length(element) > 0) return acr_get_element_long(element); else return default_value; @@ -1184,13 +1210,13 @@ @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_find_int(Acr_Group group_list, Acr_Element_Id elid, - int default_value) +int acr_find_int(Acr_Group group_list, Acr_Element_Id elid, + int default_value) { Acr_Element element; element = acr_find_group_element(group_list, elid); - if (element != NULL) + if (element != NULL && acr_get_element_length(element) > 0) return (int) acr_get_element_numeric(element); else return default_value; @@ -1211,13 +1237,13 @@ @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public double acr_find_double(Acr_Group group_list, Acr_Element_Id elid, - double default_value) +double acr_find_double(Acr_Group group_list, Acr_Element_Id elid, + double default_value) { Acr_Element element; element = acr_find_group_element(group_list, elid); - if (element != NULL) + if (element != NULL && acr_get_element_length(element) > 0) return acr_get_element_numeric(element); else return default_value; @@ -1238,13 +1264,13 @@ @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public char *acr_find_string(Acr_Group group_list, Acr_Element_Id elid, - char *default_value) +char *acr_find_string(Acr_Group group_list, Acr_Element_Id elid, + char *default_value) { Acr_Element element; element = acr_find_group_element(group_list, elid); - if (element != NULL) + if (element != NULL) /* Allow zero-length strings */ return acr_get_element_string(element); else return default_value; @@ -1256,7 +1282,7 @@ (can be NULL) element - element to insert @OUTPUT : group_list - modified group list -@RETURNS : (nothing) +@RETURNS : Acr_Status @DESCRIPTION: Insert an element into a group list. If the group_list is NULL, then it is created. Note that the element is not copied, it is just inserted into the list, so it should not be modified after @@ -1268,8 +1294,8 @@ @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_insert_element_into_group_list(Acr_Group *group_list, - Acr_Element element) +Acr_Status acr_insert_element_into_group_list(Acr_Group *group_list, + Acr_Element element) { Acr_Group group, next_group, prev_group; int group_id; @@ -1310,7 +1336,7 @@ } /* Insert the element into the appropriate group */ - acr_group_insert_element(group, element); + return acr_group_insert_element(group, element); } @@ -1320,7 +1346,7 @@ elid value @OUTPUT : group_list - modified group list -@RETURNS : (nothing) +@RETURNS : Acr_Status @DESCRIPTION: Creates and inserts an element into a group list. @METHOD : @GLOBALS : @@ -1328,13 +1354,13 @@ @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_insert_short(Acr_Group *group_list, Acr_Element_Id elid, - int value) +Acr_Status acr_insert_short(Acr_Group *group_list, Acr_Element_Id elid, + int value) { Acr_Element element; element = acr_create_element_short(elid, value); - acr_insert_element_into_group_list(group_list, element); + return acr_insert_element_into_group_list(group_list, element); } @@ -1344,7 +1370,7 @@ elid value @OUTPUT : group_list - modified group list -@RETURNS : (nothing) +@RETURNS : Acr_Status @DESCRIPTION: Creates and inserts an element into a group list. @METHOD : @GLOBALS : @@ -1352,13 +1378,13 @@ @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_insert_long(Acr_Group *group_list, Acr_Element_Id elid, - long value) +Acr_Status acr_insert_long(Acr_Group *group_list, Acr_Element_Id elid, + long value) { Acr_Element element; element = acr_create_element_long(elid, value); - acr_insert_element_into_group_list(group_list, element); + return acr_insert_element_into_group_list(group_list, element); } @@ -1368,7 +1394,7 @@ elid value @OUTPUT : group_list - modified group list -@RETURNS : (nothing) +@RETURNS : Acr_Status @DESCRIPTION: Creates and inserts an element into a group list. @METHOD : @GLOBALS : @@ -1376,13 +1402,14 @@ @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_insert_numeric(Acr_Group *group_list, Acr_Element_Id elid, - double value) +Acr_Status acr_insert_numeric(Acr_Group *group_list, + Acr_Element_Id elid, + double value) { Acr_Element element; element = acr_create_element_numeric(elid, value); - acr_insert_element_into_group_list(group_list, element); + return acr_insert_element_into_group_list(group_list, element); } @@ -1392,7 +1419,7 @@ elid value @OUTPUT : group_list - modified group list -@RETURNS : (nothing) +@RETURNS : Acr_Status @DESCRIPTION: Creates and inserts an element into a group list. @METHOD : @GLOBALS : @@ -1400,13 +1427,14 @@ @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_insert_string(Acr_Group *group_list, Acr_Element_Id elid, - char *value) +Acr_Status acr_insert_string(Acr_Group *group_list, + Acr_Element_Id elid, + char *value) { Acr_Element element; element = acr_create_element_string(elid, value); - acr_insert_element_into_group_list(group_list, element); + return acr_insert_element_into_group_list(group_list, element); } @@ -1416,7 +1444,7 @@ elid itemlist @OUTPUT : group_list - modified group list -@RETURNS : (nothing) +@RETURNS : Acr_Status @DESCRIPTION: Creates and inserts an element into a group list. @METHOD : @GLOBALS : @@ -1424,13 +1452,14 @@ @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public void acr_insert_sequence(Acr_Group *group_list, Acr_Element_Id elid, - Acr_Element itemlist) +Acr_Status acr_insert_sequence(Acr_Group *group_list, + Acr_Element_Id elid, + Acr_Element itemlist) { Acr_Element element; element = acr_create_element_sequence(elid, itemlist); - acr_insert_element_into_group_list(group_list, element); + return acr_insert_element_into_group_list(group_list, element); } @@ -1451,7 +1480,7 @@ @CREATED : November 8, 2001 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_Status acr_test_dicom_file(Acr_File *afp) +Acr_Status acr_test_dicom_file(Acr_File *afp) { #define DICOM_FILE_MAGIC_OFFSET 128 #define DICOM_MAGIC_STRING "DICM"
--- a/conversion/Acr_nema/message.c +++ b/conversion/Acr_nema/message.c @@ -6,7 +6,16 @@ @CREATED : November 16, 1993 (Peter Neelin) @MODIFIED : * $Log: message.c,v $ - * Revision 6.6 2002-12-08 21:43:08 neelin + * Revision 6.7.2.1 2005-05-12 21:15:30 bert + * Initial checkin + * + * Revision 6.7 2004/10/29 13:08:42 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.6 2002/12/08 21:43:08 neelin * Fixed excessive memory freeing on error when reading message (seen in linux) * * Revision 6.5 2002/11/13 03:00:27 neelin @@ -86,7 +95,6 @@ #include <stdlib.h> #include <stdio.h> #include <limits.h> -#include <minc_def.h> #include <acr_nema.h> /* Message length group and element id */
--- a/conversion/Acr_nema/read_acr_nema.c +++ b/conversion/Acr_nema/read_acr_nema.c @@ -7,7 +7,16 @@ @CREATED : March 14, 1994 (Peter Neelin) @MODIFIED : * $Log: read_acr_nema.c,v $ - * Revision 6.2 2001-11-08 14:17:06 neelin + * Revision 6.3.2.1 2005-05-12 21:15:30 bert + * Initial checkin + * + * Revision 6.3 2004/10/29 13:08:42 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.2 2001/11/08 14:17:06 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. @@ -60,7 +69,6 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <minc_def.h> #include <acr_nema.h> /* Define some constants */
--- a/conversion/Acr_nema/sample_dicom_client.c +++ b/conversion/Acr_nema/sample_dicom_client.c @@ -5,7 +5,16 @@ @CREATED : May 6, 1997 (Peter Neelin) @MODIFIED : * $Log: sample_dicom_client.c,v $ - * Revision 6.2 2001-11-08 14:17:06 neelin + * Revision 6.3.2.1 2005-05-12 21:15:30 bert + * Initial checkin + * + * Revision 6.3 2004/10/29 13:08:42 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.2 2001/11/08 14:17:06 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. @@ -54,13 +63,12 @@ ---------------------------------------------------------------------------- */ #ifndef lint -static char rcsid[]="$Header: /private-cvsroot/minc/conversion/Acr_nema/sample_dicom_client.c,v 6.2 2001-11-08 14:17:06 neelin Exp $"; +static char rcsid[]="$Header: /private-cvsroot/minc/conversion/Acr_nema/sample_dicom_client.c,v 6.3.2.1 2005-05-12 21:15:30 bert Exp $"; #endif #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <minc_def.h> #include <acr_nema.h> #ifndef public
--- a/conversion/Acr_nema/value_repr.c +++ b/conversion/Acr_nema/value_repr.c @@ -6,7 +6,19 @@ @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : * $Log: value_repr.c,v $ - * Revision 6.2 2000-08-16 15:53:46 neelin + * Revision 6.4.2.1 2005-05-12 21:15:31 bert + * Initial checkin + * + * Revision 6.4 2005/03/04 00:08:37 bert + * Lose private and public + * + * Revision 6.3 2004/10/29 13:08:42 rotor + * * rewrote Makefile with no dependency on a minc distribution + * * removed all references to the abominable minc_def.h + * * I should autoconf this really, but this is old code that + * is now replaced by Jon Harlaps PERL version.. + * + * Revision 6.2 2000/08/16 15:53:46 neelin * Added VR type UN (unknown) which has a length field similar to OB. * * Revision 6.1 1999/10/29 17:51:54 neelin @@ -47,7 +59,6 @@ #include <string.h> #include <ctype.h> #include <math.h> -#include <minc_def.h> #include <acr_nema.h> /* Types for VR table entries and conversion functions */ @@ -67,40 +78,40 @@ }; /* Private functions */ -private void check_table_integrity(); -private Acr_VR_Entry *get_vr_entry(Acr_VR_Type vr_code); -private Acr_VR_Type find_vr_name(char *vr_name); -private double return_zero(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length); -private double string_to_numeric(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length); -private double get_short(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length); -private double get_long(Acr_VR_Entry *vr_entry, +static void check_table_integrity(); +static Acr_VR_Entry *get_vr_entry(Acr_VR_Type vr_code); +static Acr_VR_Type find_vr_name(char *vr_name); +static double return_zero(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length); +static double string_to_numeric(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length); +static double get_short(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); -private double get_float(Acr_VR_Entry *vr_entry, +static double get_long(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length); +static double get_float(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length); +static double get_double(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); -private double get_double(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length); -private double guess_numeric_type(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length); -private void extend_internal_buffer(int length); -private char *return_empty_string(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length); -private char *return_the_string(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length); -private char *numeric_to_string(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length); +static double guess_numeric_type(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length); +static void extend_internal_buffer(int length); +static char *return_empty_string(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length); +static char *return_the_string(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length); +static char *numeric_to_string(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length); /* Table of VRs and conversion routines */ static Acr_VR_Entry VR_table[] = { @@ -155,7 +166,7 @@ @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private void check_table_integrity() +static void check_table_integrity() { int ientry; @@ -194,7 +205,7 @@ @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private Acr_VR_Entry *get_vr_entry(Acr_VR_Type vr_code) +static Acr_VR_Entry *get_vr_entry(Acr_VR_Type vr_code) { CHECK_TABLE_INTEGRITY; @@ -226,7 +237,7 @@ @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -private Acr_VR_Type find_vr_name(char *vr_name) +static Acr_VR_Type find_vr_name(char *vr_name) { int ientry; @@ -265,7 +276,7 @@ @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public char *acr_get_vr_name(Acr_VR_Type vr_code) +char *acr_get_vr_name(Acr_VR_Type vr_code) { Acr_VR_Entry *entry; @@ -287,7 +298,7 @@ @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public int acr_test_vr_name(char *vr_name) +int acr_test_vr_name(char *vr_name) { Acr_VR_Type vr_code; @@ -308,7 +319,7 @@ @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public Acr_VR_Type acr_lookup_vr_name(char *vr_name) +Acr_VR_Type acr_lookup_vr_name(char *vr_name) { Acr_VR_Type vr_code; int ientry; @@ -364,9 +375,9 @@ @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public double acr_get_numeric_vr(Acr_VR_Type vr_code, - Acr_byte_order byte_order, - char *data, long data_length) +double acr_get_numeric_vr(Acr_VR_Type vr_code, + Acr_byte_order byte_order, + char *data, long data_length) { Acr_VR_Entry *entry; @@ -392,9 +403,9 @@ @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ -public char *acr_get_string_vr(Acr_VR_Type vr_code, - Acr_byte_order byte_order, - char *data, long data_length) +char *acr_get_string_vr(Acr_VR_Type vr_code, + Acr_byte_order byte_order, + char *data, long data_length) { Acr_VR_Entry *entry; @@ -426,25 +437,25 @@ ---------------------------------------------------------------------------- */ /* ARGSUSED */ -private double return_zero(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length) +static double return_zero(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length) { return 0.0; } /* ARGSUSED */ -private double string_to_numeric(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length) +static double string_to_numeric(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length) { return atof((char *) data); } /* ARGSUSED */ -private double get_short(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length) +static double get_short(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length) { unsigned short value; @@ -458,9 +469,9 @@ } /* ARGSUSED */ -private double get_long(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length) +static double get_long(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length) { long value; @@ -474,9 +485,9 @@ } /* ARGSUSED */ -private double get_float(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length) +static double get_float(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length) { float value; @@ -490,9 +501,9 @@ } /* ARGSUSED */ -private double get_double(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length) +static double get_double(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length) { double value; @@ -506,9 +517,9 @@ } /* ARGSUSED */ -private double guess_numeric_type(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length) +static double guess_numeric_type(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length) { switch (data_length) { case ACR_SIZEOF_SHORT: @@ -547,7 +558,7 @@ static char *internal_string_buffer = NULL; static int length_of_internal_string = 0; -private void extend_internal_buffer(int length) +static void extend_internal_buffer(int length) { if (length+1 > length_of_internal_string) { @@ -561,9 +572,9 @@ } /* ARGSUSED */ -private char *return_empty_string(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length) +static char *return_empty_string(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length) { extend_internal_buffer(LINE_LENGTH); @@ -573,17 +584,17 @@ } /* ARGSUSED */ -private char *return_the_string(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length) +static char *return_the_string(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length) { return (char *) data; } /* ARGSUSED */ -private char *numeric_to_string(Acr_VR_Entry *vr_entry, - Acr_byte_order byte_order, - char *data, long data_length) +static char *numeric_to_string(Acr_VR_Entry *vr_entry, + Acr_byte_order byte_order, + char *data, long data_length) { extend_internal_buffer(LINE_LENGTH); (void) sprintf(internal_string_buffer, "%.6g",
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/acr_element_defs.h @@ -0,0 +1,148 @@ +/* ----------------------------- 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 ACR_IMAGE_GID 0x7fe0 +#define ACR_IMAGE_EID 0x0010 + +/* Define acr-nema constants */ +#define ACR_MODALITY_MR "MR" +#define ACR_MODALITY_PT "PT" + +/* Defined values for pixel representation (0028,0103) */ +#define ACR_PIXEL_REP_UNSIGNED 0 /* Unsigned magnitude */ +#define ACR_PIXEL_REP_SIGNED 1 /* 2's complement signed */ + +/* 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_SOP_Class_UID , 0x0008, 0x0016, UI); +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_Image_time , 0x0008, 0x0033, 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_Calibration_date , 0x0018, 0x1200, DA); +GLOBAL_ELEMENT(ACR_Actual_frame_duration , 0x0018, 0x1242, IS); +GLOBAL_ELEMENT(ACR_Receive_coil_name , 0x0018, 0x1250, SH); +GLOBAL_ELEMENT(ACR_Transmit_coil_name , 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_old, 0x0020, 0x0030, DS); +GLOBAL_ELEMENT(ACR_Image_position_patient, 0x0020, 0x0032, DS); +GLOBAL_ELEMENT(ACR_Image_orientation_patient_old, + 0x0020, 0x0035, DS); +GLOBAL_ELEMENT(ACR_Image_orientation_patient, + 0x0020, 0x0037, DS); +GLOBAL_ELEMENT(ACR_Acquisitions_in_series, 0x0020, 0x1001, IS); +GLOBAL_ELEMENT(ACR_Images_in_acquisition, 0x0020, 0x1002, IS); +GLOBAL_ELEMENT(ACR_Slice_location, 0x0020, 0x1041, DS); + +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_High_bit , 0x0028, 0x0102, US); +GLOBAL_ELEMENT(ACR_Pixel_representation , 0x0028, 0x0103, 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_Rescale_intercept , 0x0028, 0x1052, DS); +GLOBAL_ELEMENT(ACR_Rescale_slope , 0x0028, 0x1053, DS); + +GLOBAL_ELEMENT(ACR_Number_of_slices , 0x0054, 0x0081, US); +GLOBAL_ELEMENT(ACR_Number_of_time_slices , 0x0054, 0x0101, US); +GLOBAL_ELEMENT(ACR_Units , 0x0054, 0x1001, CS); +GLOBAL_ELEMENT(ACR_Frame_reference_time , 0x0054, 0x1300, DS); + +GLOBAL_ELEMENT(ACR_Pixel_data , ACR_IMAGE_GID, ACR_IMAGE_EID, OW); + + + + +
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/dcm2mnc.c @@ -0,0 +1,931 @@ +/* ----------------------------- 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.14.2.1 2005-05-12 21:16:47 bert + * Initial checkin + * + * Revision 1.14 2005/05/09 15:32:02 bert + * Change version to 2.0.05 + * + * Revision 1.13 2005/04/29 23:09:36 bert + * Add support for -stdin option to read file list from standard input + * + * Revision 1.12 2005/04/26 23:49:24 bert + * Update version + * + * Revision 1.11 2005/04/18 16:38:42 bert + * Fix up file type detection code + * + * Revision 1.10 2005/04/06 13:26:41 bert + * Fix listing option + * + * Revision 1.9 2005/04/05 21:52:24 bert + * Add -minmax option to enable use of explicit DICOM pixel min/max information, and updated version number + * + * Revision 1.8 2005/03/18 19:10:31 bert + * Scan coordinate and location information for validity before relying on it + * + * Revision 1.7 2005/03/15 17:03:34 bert + * Yet another directory expansion fix (sigh) + * + * Revision 1.6 2005/03/14 22:51:33 bert + * Actually get the directory expansion working properly... + * + * Revision 1.5 2005/03/14 22:25:41 bert + * If a directory is specified on the file list, expand it internally. This gets around shell limitations. + * + * Revision 1.4 2005/03/03 20:10:14 bert + * Consider patient_id and patient_name when sorting into series + * + * Revision 1.3 2005/03/03 18:59:15 bert + * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) + * + * Revision 1.2 2005/03/02 18:23:32 bert + * Added mosaic sequence and bitwise options + * + * Revision 1.1 2005/02/17 16:38:09 bert + * Initial checkin, revised DICOM to MINC converter + * + * Revision 1.1.1.1 2003/08/15 19:52:55 leili + * Leili's dicom server for sonata + * + * 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. + * +---------------------------------------------------------------------------- */ + +static const char rcsid[]="$Header: /private-cvsroot/minc/conversion/dcm2mnc/dcm2mnc.c,v 1.14.2.1 2005-05-12 21:16:47 bert Exp $"; + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <math.h> +#include <dirent.h> +#include <ParseArgv.h> + +#define GLOBAL_ELEMENT_DEFINITION /* To define elements */ +#include "dcm2mnc.h" + +/* Function Prototypes */ +static int dcm_sort_function(const void *entry1, const void *entry2); +static void use_the_files(int num_files, + Data_Object_Info *data_info[], + const char *out_dir); +static void usage(void); +static void free_list(int num_files, + const char **file_list, + Data_Object_Info **file_info_list); +static int check_file_type_consistency(int num_files, const char *file_list[]); + + +struct globals G; + +#define VERSION_STRING "2.0.05 built " __DATE__ " " __TIME__ + +ArgvInfo argTable[] = { + {NULL, ARGV_VERINFO, VERSION_STRING, NULL, NULL }, + {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &G.clobber, + "Overwrite output files"}, + {"-list", ARGV_CONSTANT, (char *) TRUE, (char *) &G.List, + "Print list of series (don't create files)"}, + {"-anon", ARGV_CONSTANT, (char *) TRUE, (char *) &G.Anon, + "Exclude subject name from file header"}, + {"-descr", ARGV_STRING, (char *) 1, G.Name, + "Use <str> as session descriptor (default = patient initials)"}, + {"-cmd", ARGV_STRING, (char *) 1, G.command_line, + "Apply <command> to output files (e.g. gzip)"}, + {"-verbose", ARGV_CONSTANT, (char *) LO_LOGGING, (char *) &G.Debug, + "Print debugging information"}, + {"-debug", ARGV_CONSTANT, (char *) HI_LOGGING, (char *) &G.Debug, + "Print lots of debugging information"}, + {"-nosplitecho", ARGV_CONSTANT, (char *) FALSE, (char *) &G.splitEcho, + "Combine all echoes into a single file."}, + {"-splitdynamic", ARGV_CONSTANT, (char *) TRUE, (char *)&G.splitDynScan, + "Split dynamic scans into a separate files."}, + {"-opts", ARGV_INT, (char *) 1, (char *) &G.opts, + "Set debugging options"}, + + {"-descending", + ARGV_CONSTANT, + (char *) MOSAIC_SEQ_DESCENDING, + (char *) &G.mosaic_seq, + "Mosaic sequence is in descending slice order."}, + + {"-interleaved", + ARGV_CONSTANT, + (char *) MOSAIC_SEQ_INTERLEAVED, + (char *) &G.mosaic_seq, + "Mosaic sequence is in interleaved slice order."}, + + {"-minmax", + ARGV_CONSTANT, + (char *)TRUE, + (char *) &G.useMinMax, + "Honor DICOM pixel minimum and pixel maximum values."}, + + {"-stdin", + ARGV_CONSTANT, + (char *)TRUE, + (char *)&G.use_stdin, + "Read file list from standard input."}, + + {NULL, ARGV_END, NULL, NULL, NULL} + +}; + +int +main(int argc, char *argv[]) +{ + int ifile; + Acr_Group group_list; + const char **file_list; /* List of file names */ + Data_Object_Info **file_info_list; + int num_file_args; /* Number of files on command line */ + int num_files; /* Total number of files */ + string_t out_dir; /* Output directory */ + string_t message; /* Generic message */ + int num_files_ok; /* Actual number of DICOM/IMA files */ + struct stat st; + int length; + + G.mosaic_seq = MOSAIC_SEQ_ASCENDING; /* Assume ascending by default. */ + G.splitDynScan = FALSE; /* Don't split dynamic scans by default */ + G.splitEcho = TRUE; /* Do split by echo by default */ + G.use_stdin = FALSE; /* Do not read file list from stdin */ + + G.minc_history = time_stamp(argc, argv); /* Create minc history string */ + + G.pname = argv[0]; /* get program name */ + + /* Get the input parameters and file names. + */ + if (ParseArgv(&argc, argv, argTable, 0)) { + usage(); + } + + if (argc < 2) { + usage(); + } + + if (G.List) { + num_file_args = argc - 1; /* Assume no directory given. */ + } + else { + num_file_args = argc - 2; /* Assume last arg is directory. */ + + strcpy(out_dir, argv[argc - 1]); + + /* make sure path ends with slash + */ + length = strlen(out_dir); + if (out_dir[length - 1] != '/') { + out_dir[length++] = '/'; + out_dir[length++] = '\0'; + } + + if (stat(out_dir, &st) != 0 || !S_ISDIR(st.st_mode)) { + fprintf(stderr, "The final argument, '%s', is not a directory\n", + out_dir); + exit(EXIT_FAILURE); + } + } + + /* Get space for file lists */ + /* Allocate the array of pointers used to implement the + * list of filenames. + */ + file_list = malloc(num_file_args * sizeof(char *)); + CHKMEM(file_list); + + /* Go through the list of files, expanding directories where they + * are encountered... + */ + num_files = 0; + for (ifile = 0 ; ifile < num_file_args; ifile++) { + if (stat(argv[ifile + 1], &st) == 0 && S_ISDIR(st.st_mode)) { + DIR *dp; + struct dirent *np; + char *tmp_str; + + if (G.Debug) { + printf("Expanding directory '%s'\n", argv[ifile + 1]); + } + + length = strlen(argv[ifile + 1]); + + dp = opendir(argv[ifile + 1]); + if (dp != NULL) { + while ((np = readdir(dp)) != NULL) { + /* Generate the full path to the file. + */ + tmp_str = malloc(length + strlen(np->d_name) + 2); + strcpy(tmp_str, argv[ifile + 1]); + if (tmp_str[length-1] != '/') { + tmp_str[length] = '/'; + tmp_str[length+1] = '\0'; + } + strcat(&tmp_str[length], np->d_name); + if (stat(tmp_str, &st) == 0 && S_ISREG(st.st_mode)) { + file_list = realloc(file_list, + (num_files + 1) * sizeof(char *)); + file_list[num_files++] = tmp_str; + } + else { + free(tmp_str); + } + } + closedir(dp); + } + else { + fprintf(stderr, "Error opening directory '%s'\n", + argv[ifile + 1]); + } + } + else { + file_list[num_files++] = strdup(argv[ifile + 1]); + } + } + + if (G.use_stdin) { + char linebuf[1024]; + char *p; + + while (fgets(linebuf, sizeof(linebuf), stdin) != NULL) { + /* Strip off newline at end of string. + */ + for (p = linebuf; *p != '\0'; p++) { + if (*p == '\n') { + *p = '\0'; + } + } + if (strlen(linebuf) != 0) { + file_list = realloc(file_list, + (num_files + 1) * sizeof(char *)); + file_list[num_files++] = strdup(linebuf); + } + } + } + + file_info_list = malloc(num_files * sizeof(*file_info_list)); + CHKMEM(file_info_list); + + /* 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 + * + * 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...\n"); + + if (check_file_type_consistency(num_files, file_list) < 0) { + exit(EXIT_FAILURE); + } + + /* Now loop over all files, getting basic info on each + */ + + num_files_ok = 0; + for (ifile = 0; ifile < num_files; ifile++) { + const char *cur_fname_ptr = file_list[ifile]; + + if (!G.Debug) { + sprintf(message, "Parsing %d files", num_files); + progress(ifile, num_files, message); + } + + if (G.file_type == IMA) { + group_list = siemens_to_dicom(cur_fname_ptr, ACR_IMAGE_GID - 1); + } + else { + /* read up to but not including pixel data + */ + group_list = read_numa4_dicom(cur_fname_ptr, ACR_IMAGE_GID - 1); + } + + if (group_list == NULL) { + /* This file appears to be invalid - it is probably a dicomdir + * file or some other stray junk in the directory. + */ + printf("Skipping file %s, which is not in the expected format.\n", + cur_fname_ptr); + free((void *) cur_fname_ptr); + } + else { + /* Copy it back to the (possibly earlier) position in the real + * file list. + */ + file_list[num_files_ok] = cur_fname_ptr; + + /* allocate space for the current entry to file_info_list + */ + file_info_list[num_files_ok] = malloc(sizeof(*file_info_list[0])); + CHKMEM(file_info_list[num_files_ok]); + file_info_list[num_files_ok]->file_index = num_files_ok; + + parse_dicom_groups(group_list, file_info_list[num_files_ok]); + + /* put the file name into the info list + */ + file_info_list[num_files_ok]->file_name = strdup(file_list[num_files_ok]); + + /* Delete the group list now that we're done with it + */ + acr_delete_group_list(group_list); + num_files_ok++; + } + } /* end of loop over files to get basic info */ + + if (G.Debug) { + printf("Using %d files\n", num_files_ok); + } + + num_files = num_files_ok; + + printf("Sorting %d files... ", num_files); + + /* sort the files into series based on acquisition number + */ + qsort(file_info_list, num_files, sizeof(file_info_list[0]), + dcm_sort_function); + + /* If DEBUG, print a list of all files. + */ + if (G.Debug) { + printf("\n"); + for (ifile = 0; ifile < num_files; ifile++) { + Data_Object_Info *info = file_info_list[ifile]; + char *fname; + + if ((ifile % 16) == 0) { + printf("%-4s %-32.32s %-14s %-8s %-8s %-4s %-4s %-4s %-4s %-4s %-4s %-4s %-4s %-4s %-5s %-16s\n", + "num", + "filename", + "studyid", + "serialno", + "acq", + "nec", + "iec", + "ndy", + "idy", + "nsl", + "isl", + "acol", + "rcol", + "mrow", + "img#", + "seq"); + } + /* Print out info about file. Truncate the name if necessary. + */ + fname = info->file_name; + if (strlen(fname) > 32) { + fname += strlen(fname) - 32; + } + + printf("%4d %-32.32s %14.6f %8d %8d %4d %4d %4d %4d %4d %4d %4d %4d %4d %5d %-16s\n", + ifile, + fname, + info->study_id, + 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); + } + } + + printf("Done sorting files.\n"); + + /* Loop over files, processing by acquisition */ + + if (G.List) { + printf("Listing files by series...\n"); + } + else { + printf("Processing files, one series at a time...\n"); + } + + use_the_files(num_files, file_info_list, out_dir); + + if (G.List) { + printf("Done listing files.\n"); + } + else { + printf("Done processing files.\n"); + } + + free_list(num_files, file_list, file_info_list); + + free(file_list); + free(file_info_list); + + + exit(EXIT_SUCCESS); +} + +/* ----------------------------- 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 : +---------------------------------------------------------------------------- */ +static void +free_list(int num_files, + const char **file_list, + Data_Object_Info **file_info_list) +{ + int i; + + for (i = 0; i < num_files; i++) { + if (file_list[i] != NULL) { + free((void *) file_list[i]); + } + if (file_info_list[i] != NULL) { + free(file_info_list[i]); + } + } +} + +/* ----------------------------- 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 : +---------------------------------------------------------------------------- */ +static 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; + + int slice1 = (*file_info_list1)->slice_number; + int slice2 = (*file_info_list2)->slice_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 if (slice1 < slice2) return -1; + else if (slice1 > slice2) return 1; + else return 0; +} + +static void +usage(void) +{ + fprintf(stderr, "\nUsage: %s [options] file1 file2 file3 ... destdir\n", + G.pname); + fprintf(stderr, "\n"); + fprintf(stderr,"Files are named according to the following convention:\n\n"); + fprintf(stderr," Directory: lastname_firstname_yyyymmdd_hhmmss/\n"); + fprintf(stderr," Files: lastname_firstname_yyyymmdd_hhmmss_series_modality.mnc\n\n"); + + exit(EXIT_FAILURE); +} + +static void +use_the_files(int num_files, + Data_Object_Info *di_ptr[], + const char *out_dir) +{ + int ifile; + int acq_num_files; + const char **acq_file_list; + int *used_file; + int *acq_file_index; + double cur_study_id; + int cur_acq_id; + int cur_rec_num; + int cur_image_type; + int cur_echo_number; + int cur_dyn_scan_number; + string_t cur_patient_name; + string_t cur_patient_id; + int exit_status; + char *output_file_name; + string_t file_prefix; + int output_uid; + int output_gid; + string_t string; + FILE *fp; + int trust_location; + int trust_coord; + int user_opts; /* Options as set by user. We may override.. */ + + if (out_dir != NULL) { /* if an output directory name has been + * provided on the command line + */ + if (G.Debug) { + printf("Using directory '%s'\n", out_dir); + } + strcpy(file_prefix, out_dir); + } + else { + file_prefix[0] = '\0'; + } + + if (G.Debug) { /* debugging */ + printf("file_prefix: [%s]\n", file_prefix); + } + + /* Allocate space for acquisition file list. + */ + acq_file_list = malloc(num_files * sizeof(*acq_file_list)); + CHKMEM(acq_file_list); + + acq_file_index = malloc(num_files * sizeof(*acq_file_index)); + CHKMEM(acq_file_index); + + used_file = malloc(num_files * sizeof(*used_file)); + CHKMEM(used_file); + + for (ifile = 0; ifile < num_files; ifile++) { + used_file[ifile] = FALSE; + } + + for (;;) { + + /* Loop through files, looking for an acquisition + * + * file groups should already have been sorted into acquisitions + * in calling program + * + * this code is in a `forever' loop because we loop over multiple + * acquisitions until all of the files are used up. + */ + + acq_num_files = 0; + + for (ifile = 0; ifile < num_files; ifile++) { + + /* If already marked used (can this happen???), we've already + * written the file to an output somewhere. + */ + if (used_file[ifile]) { + continue; + } + + if (acq_num_files == 0) { + + /* 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' + */ + + cur_study_id = di_ptr[ifile]->study_id; + cur_acq_id = di_ptr[ifile]->acq_id; + cur_rec_num = di_ptr[ifile]->rec_num; + cur_image_type = di_ptr[ifile]->image_type; + cur_echo_number = di_ptr[ifile]->echo_number; + cur_dyn_scan_number = di_ptr[ifile]->dyn_scan_number; + + strcpy(cur_patient_name, di_ptr[ifile]->patient_name); + strcpy(cur_patient_id, di_ptr[ifile]->patient_id); + + 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 ((di_ptr[ifile]->study_id == cur_study_id) && + (di_ptr[ifile]->acq_id == cur_acq_id) && + (di_ptr[ifile]->rec_num == cur_rec_num) && + (di_ptr[ifile]->image_type == cur_image_type) && + (di_ptr[ifile]->echo_number == cur_echo_number || + !G.splitEcho) && + (di_ptr[ifile]->dyn_scan_number == cur_dyn_scan_number || + !G.splitDynScan) && + !strcmp(cur_patient_name, di_ptr[ifile]->patient_name) && + !strcmp(cur_patient_id, di_ptr[ifile]->patient_id)) { + + 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[acq_num_files] = di_ptr[ifile]->file_name; + acq_file_index[acq_num_files] = ifile; + acq_num_files++; + } + } + + /* If no files were added to this acquisition, it implies that + * all files have been processed. + */ + if (acq_num_files == 0) { + break; /* All done!!! */ + } + + /* Use the files for this acquisition + */ + + /* Print out the file names if we are debugging. + */ + if (G.Debug || G.List) { + printf("\nSeries %4d %20s %20s (%4d files):\n", + cur_acq_id, + cur_patient_name, + di_ptr[acq_file_index[0]]->protocol_name, + acq_num_files); + for (ifile = 0; ifile < acq_num_files; ifile++) { + printf(" %s\n", di_ptr[acq_file_index[ifile]]->file_name); + } + if (G.List) { + continue; + } + } + + /* Do some sanity checks on the acquisition. In particular, we + * verify that the coordinate and/or slice location information + * looks reliable. + */ + trust_location = 1; + trust_coord = 1; + + for (ifile = 0; ifile < acq_num_files; ifile++) { + int jfile; + int ix = acq_file_index[ifile]; + + if (!di_ptr[ix]->coord_found) { + trust_coord = 0; + } + + for (jfile = ifile + 1; jfile < acq_num_files; jfile++) { + int jx = acq_file_index[jfile]; + + if (NEARLY_EQUAL(di_ptr[ix]->slice_location, + di_ptr[jx]->slice_location)) { + trust_location = 0; + } + } + } + + user_opts = G.opts; + + if (!trust_coord) { + printf("WARNING: Image coordinates absent or incomplete.\n"); + if (!trust_location) { + printf("WARNING: Slice location is untrustworthy.\n"); + G.opts |= OPTS_NO_LOCATION; + } + } + + /* Create minc file + */ + exit_status = dicom_to_minc(acq_num_files, + acq_file_list, + NULL, + G.clobber, + file_prefix, + &output_file_name); + + G.opts = user_opts; + + if (exit_status != EXIT_SUCCESS) + continue; + + /* Print log message */ + if (G.Debug) { + printf("Created minc file %s.\n", output_file_name); + } + + /* Invoke a command on the file (if requested) and get the + * returned file name + */ + if (G.command_line[0] != '\0') { + sprintf(string, "%s %s", G.command_line, output_file_name); + printf("-Applying command '%s' to output file... ", + G.command_line); + fflush(stdout); + if ((fp = popen(string, "r")) != NULL) { + fscanf(fp, "%s", output_file_name); + if (pclose(fp) != EXIT_SUCCESS) { + fprintf(stderr, + "Error executing command\n \"%s\"\n", + string); + } + else if (G.Debug) { + printf("Executed command \"%s\",\nproducing file %s.\n", + string, output_file_name); + } + } + else { + fprintf(stderr, "Error executing command \"%s\"\n", string); + } + printf("Done.\n"); + } + + /* Change the ownership */ + if ((output_uid != INT_MIN) && (output_gid != INT_MIN)) { + chown(output_file_name, (uid_t) output_uid, (gid_t) output_gid); + } + } + + /* Free acquisition file list */ + free(acq_file_list); + free(used_file); + +} + +static int +is_cdexport_file(const char *fullname) +{ + FILE *fp; + char tst_str[DICM_MAGIC_SIZE+1]; + int result = 0; + + if ((fp = fopen(fullname, "rb")) == NULL) { + fprintf(stderr, "Error opening file %s!\n", fullname); + } + else { + fseek(fp, DICM_MAGIC_OFFS, SEEK_SET); + fread(tst_str, 1, DICM_MAGIC_SIZE, fp); + tst_str[DICM_MAGIC_SIZE] = '\0'; + + if (!strcmp(tst_str, DICM_MAGIC_STR)) { + result = 1; + } + fclose(fp); + } + return (result); +} + +static int +is_ima_file(const char *fullname) +{ + FILE *fp; + char mfg_str[IMA_MAGIC_SIZE]; + int result = 0; + + if ((fp = fopen(fullname, "rb")) == NULL) { + fprintf(stderr, "Error opening file %s!\n", fullname); + } + else { + fseek(fp, IMA_MAGIC_OFFS, SEEK_SET); + fread(mfg_str, 1, IMA_MAGIC_SIZE, fp); + + /* We only deal with Siemens IMA files - not sure any other kinds + * exist, frankly. + */ + if (!strcmp(mfg_str, IMA_MAGIC_STR)) { + result = 1; + } + fclose(fp); + } + return (result); +} + +static int +check_file_type_consistency(int num_files, const char *file_list[]) +{ + int i; + const char *fn_ptr; + int n4_offset = 0; + + for (i = 0; i < num_files; i++) { + + fn_ptr = file_list[i]; + + /* Numaris 4 DICOM CD/Export file? if so, bytes 128-131 will + * contain the string `DICM' with no null termination. + */ + + if (is_cdexport_file(fn_ptr)) { + if (G.file_type == UNDEF) { + G.file_type = N4DCM; + n4_offset = 1; + printf("File %s appears to be DICOM (CD/Export).\n", + fn_ptr); + } + else if (G.file_type != N4DCM || n4_offset != 1) { + printf("MISMATCH: File %s appears to be DICOM (CD/Export).\n", + fn_ptr); + return (-1); + } + } + else if (is_ima_file(fn_ptr)) { + if (G.file_type == UNDEF) { + G.file_type = IMA; + printf("File %s appears to be Siemens IMA.\n", fn_ptr); + } + else if (G.file_type != IMA) { + printf("MISMATCH: File %s appears to be Siemens IMA.\n", + fn_ptr); + return (-1); + } + } + else { + if (G.file_type == UNDEF) { + G.file_type = N4DCM; + n4_offset = 0; + printf("File %s appears to be standard DICOM.\n", fn_ptr); + } + else if (G.file_type != N4DCM || n4_offset != 0) { + printf("MISMATCH: File %s appears to be standard DICOM.\n", + fn_ptr); + return (-1); + } + } + } + return (0); +} + +/* compare two floating-point numbers */ +int fcmp(double x, double y, double delta) +{ + return ((fabs(x - y) / ((x == 0.0) ? 1.0 : fabs(x))) < delta); +} +
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/dcm2mnc.h @@ -0,0 +1,234 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dicomserver.h +@DESCRIPTION: Header file that includes things needed for dicomserver. +@METHOD : +@GLOBALS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + + * $Log: dcm2mnc.h,v $ + * Revision 1.10.2.1 2005-05-12 21:16:47 bert + * Initial checkin + * + * Revision 1.10 2005/04/29 23:09:36 bert + * Add support for -stdin option to read file list from standard input + * + * Revision 1.9 2005/04/18 16:38:09 bert + * Add comments and defines for both DICOM and IMA file detection + * + * Revision 1.8 2005/04/05 21:50:11 bert + * Add comment + * + * Revision 1.7 2005/03/18 19:10:39 bert + * Scan coordinate and location information for validity before relying on it + * + * Revision 1.6 2005/03/13 19:34:41 bert + * Add pms_element_defs.h to the header + * + * Revision 1.5 2005/03/03 20:10:14 bert + * Consider patient_id and patient_name when sorting into series + * + * Revision 1.4 2005/03/03 18:59:15 bert + * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) + * + * Revision 1.3 2005/03/02 18:23:33 bert + * Added mosaic sequence and bitwise options + * + * Revision 1.2 2005/02/23 18:28:11 bert + * Minor updates + * + * Revision 1.1 2005/02/17 16:38:09 bert + * Initial checkin, revised DICOM to MINC converter + * + * Revision 1.1.1.1 2003/08/15 19:52:55 leili + * Leili's dicom server for sonata + * + * 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> +#include <float.h> +#include <time_stamp.h> +#include <acr_nema.h> + +#include "acr_element_defs.h" +#include "spi_element_defs.h" +#include "ext_element_defs.h" +#include "pms_element_defs.h" /* Philips Medical Systems */ +#include "gems_element_defs.h" /* GE Medical Systems */ + +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif + +/* Constants for "standard" DICOM files (herein referred to as + * CD/Export files). For more details of this, see section 7.1 "DICOM + * FILE META INFORMATION" of the DICOM specification section 3.10. + */ +#define DICM_MAGIC_SIZE 4 +#define DICM_MAGIC_OFFS 0x0080 +#define DICM_MAGIC_STR "DICM" + +/* Constants for Siemens IMA file format detection. These are somewhat + * arbitrary - I test for IMA format by looking for the manufacturer string + * SIEMENS at offset 0x0060 + */ +#define IMA_MAGIC_SIZE (8+1) +#define IMA_MAGIC_OFFS 0x0060 +#define IMA_MAGIC_STR "SIEMENS" + +/* Test to see if two floating-point numbers are very close in value. + */ + +extern int fcmp(double x, double y, double delta); + +#define NEARLY_EQUAL(x, y) (fcmp(x, y, 1e-6)) + +typedef char string_t[511+1]; +#define STRING_T_LEN (sizeof(string_t) - 1) + +/* Define logging constants */ +#define NO_LOGGING 0 +#define LO_LOGGING 1 +#define HI_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; + +/* Type for carrying around object information + */ +typedef struct { + int file_index; /* input file index */ + char *file_name; /* input 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; + string_t sequence_name; + string_t protocol_name; + string_t patient_name; + string_t patient_id; + double slice_location; + int coord_found; +} Data_Object_Info; + +#include "dicom_to_minc.h" +#include "dicom_read.h" +#include "minc_file.h" +#include "progress.h" +#include "siemens_to_dicom.h" +#include "string_to_filename.h" + +typedef enum { + MOSAIC_SEQ_UNKNOWN, + MOSAIC_SEQ_INTERLEAVED, + MOSAIC_SEQ_ASCENDING, + MOSAIC_SEQ_DESCENDING +} mosaic_seq_t; + +/* Globals */ +struct globals { + char *minc_history; /* Global for minc history */ + char *pname; /* program name */ + File_Type file_type; /* type of input files */ + short Debug; /* Debug on/off */ + short Anon; /* "Anonymize" the output */ + short List; + short useMinMax; /* TRUE if need to use pixel min/max values */ + short splitEcho; /* TRUE if echos in separate files */ + short splitDynScan; /* TRUE if dynamic scans in separate files */ + short clobber; + string_t Name; + string_t command_line; + unsigned long opts; + mosaic_seq_t mosaic_seq; + short use_stdin; +}; + +/* Values for options flags */ +#define OPTS_NO_MOSAIC 0x00000001 /* Don't parse mosaic information. */ +#define OPTS_KEEP_COORD 0x00000002 /* Don't flip DICOM coordinates */ +#define OPTS_NO_LOCATION 0x00000004 /* Never rely on slice location */ + +extern struct globals G; + +#define CHKMEM(x) \ + if ((x) == NULL) \ + (fprintf(stderr, "Out of memory at %s:%d\n", __FILE__, __LINE__), \ + exit(-1))
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/dicom_read.c @@ -0,0 +1,1811 @@ +/* ----------------------------- MNI Header ----------------------------------- + @NAME : 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: dicom_read.c,v $ + * Revision 1.16.2.1 2005-05-12 21:16:47 bert + * Initial checkin + * + * Revision 1.16 2005/05/09 15:30:32 bert + * Don't allow a rescale slope value of zero + * + * Revision 1.15 2005/04/28 17:17:57 bert + * Set and update new width information fields in a manner analogous to the coordinate fields in the General_Info and File_Info structures + * + * Revision 1.14 2005/04/20 23:14:04 bert + * Remove most SPI_ references + * + * Revision 1.13 2005/04/20 17:47:38 bert + * Fairly major restructuring, added init_general_info() function + * + * Revision 1.12 2005/04/18 21:43:04 bert + * Properly set default minimum and maximum values based on the pixel representation + * + * Revision 1.11 2005/04/18 21:01:51 bert + * Set signed/unsigned flag correctly + * + * Revision 1.10 2005/04/18 20:43:25 bert + * Added some additional debugging information for image position and orientation + * + * Revision 1.9 2005/04/18 16:22:13 bert + * Don't allow non-slice MRI dimensions to grow arbitrarily + * + * Revision 1.8 2005/04/05 21:49:52 bert + * Use rescale slope and intercept to determine the proper slice minimum and maximum + * + * Revision 1.7 2005/03/29 20:21:44 bert + * Fix use of slice spacing; fully check for position information if possible, otherwise create a reasonable position from the slice index + * + * Revision 1.6 2005/03/14 23:29:35 bert + * Support basic dynamic PET fields. Also allocate indices and coordinates arrays for all dimensions, even those we won't use. + * + * Revision 1.5 2005/03/13 19:37:42 bert + * Try to use slice location for coordinate when all else fails, also added one debugging message and a check for PET modality + * + * Revision 1.4 2005/03/03 20:11:00 bert + * Consider patient_id and patient_name when sorting into series. Fix handling of missing direction cosines + * + * Revision 1.3 2005/03/03 18:59:15 bert + * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) + * + * Revision 1.2 2005/03/02 20:18:09 bert + * Latest fixes and tweaks + * + * Revision 1.1 2005/02/17 16:38:10 bert + * Initial checkin, revised DICOM to MINC converter + * + * Revision 1.1.1.1 2003/08/15 19:52:55 leili + * Leili's dicom server for sonata + * + * 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 "dcm2mnc.h" + +#include <math.h> + +static double convert_time_to_seconds(double dicom_time); +static void get_intensity_info(Acr_Group group_list, File_Info *file_info); +static void get_coordinate_info(Acr_Group group_list, File_Info *file_info, + Orientation *orientation, + World_Index volume_to_world[VOL_NDIMS], + const int sizes[VOL_NDIMS], + double dircos[VOL_NDIMS][WORLD_NDIMS], + double steps[VOL_NDIMS], + double starts[VOL_NDIMS], + double coordinate[WORLD_NDIMS]); +static void get_general_header_info(Acr_Group group_list, + General_Info *general_info); +static void convert_numa3_coordinate(double coordinate[WORLD_NDIMS]); +static void convert_dicom_coordinate(double coordinate[WORLD_NDIMS]); +static void get_identification_info(Acr_Group group_list, + double *study_id, int *acq_id, + int *rec_num, int *image_type); + +static int irnd(double x) +{ + if (x > 0.0) { + x += 0.5; + } + else { + x -= 0.5; + } + return (int) floor(x); +} + + +/* ----------------------------- MNI Header ----------------------------------- + @NAME : init_general_info + @INPUT : fi_ptr - file-specific info + group_list - input data + volume_to_world - correspondence of volume to world dimensions + spatial_sizes - 3D voxel counts + dircos - direction cosines + steps - width of each voxel for each spatial dimension + starts - starting position for each spatial dimension + coordinate - + @OUTPUT : gi_ptr - general information about files in this series + + + @RETURNS : (nothing) + @DESCRIPTION: Initializes a "General_Info" structure based upon several + bits of information, including a previously initialized + File_Info structure (fi_ptr) and the DICOM group_list. + Broken out from the get_file_info() function to help + simplify that function. + @METHOD : + @GLOBALS : + @CALLS : + @CREATED : April 19, 2005 (Bert Vincent) + @MODIFIED : + ---------------------------------------------------------------------------- */ +static void +init_general_info(General_Info *gi_ptr, /* OUT */ + const File_Info *fi_ptr, /* IN */ + const Acr_Group group_list, /* IN */ + const World_Index volume_to_world[VOL_NDIMS], /* IN */ + const int spatial_sizes[VOL_NDIMS], /* IN */ + double dircos[VOL_NDIMS][WORLD_NDIMS], /* IN */ + const double steps[VOL_NDIMS], /* IN */ + const double starts[VOL_NDIMS], /* IN */ + double study_id, /* IN */ + int acq_id, /* IN */ + int rec_num) /* IN */ +{ + Acr_Element_Id mri_total_list[MRI_NDIMS]; + int ivalue; /* For pixel representation value. */ + World_Index iworld; /* World coordinate index (XCOORD, YCOORD...) */ + World_Index jworld; /* World coordinate index */ + Volume_Index ivolume; /* Voxel coordinate index (VROW, VCOLUMN...) */ + Mri_Index imri; + int index; + + // Initialize array for MRI dimension lengths + // + mri_total_list[SLICE] = ACR_Images_in_acquisition; + mri_total_list[ECHO] = ACR_Echo_train_length; + mri_total_list[TIME] = ACR_Acquisitions_in_series; + mri_total_list[PHASE] = NULL; + mri_total_list[CHEM_SHIFT] = NULL; + + // Get row and columns sizes + gi_ptr->nrows = spatial_sizes[VROW]; + gi_ptr->ncolumns = spatial_sizes[VCOLUMN]; + + // Save the study, acquisition, reconstruction and image type + // identifiers + gi_ptr->study_id = study_id; + gi_ptr->acq_id = acq_id; + gi_ptr->rec_num = rec_num; + + strcpy(gi_ptr->image_type_string, acr_find_string(group_list, + ACR_Image_type, + "")); + + /* Get dimension information + */ + for (imri = 0; imri < MRI_NDIMS; imri++) { + + /* Get sizes along "MRI" dimensions... + */ + gi_ptr->cur_size[imri] = 1; + + if (mri_total_list[imri] != NULL) { + int def_val = 1; + + /* Special case for slice index - need to look at the + * new standard element 0x0020:0x1002 "Images in + * Acquisition". We use this as a default, but override + * it with the Siemens-specific value if present. + */ + if (imri == SLICE) { + /* Look for the standard slice count fields first. We + * start with the (0054, 0081) first, and if that fails + * we retry with (0020, 1002). + */ + def_val = acr_find_int(group_list, ACR_Number_of_slices, 0); + if (def_val == 0) { + def_val = acr_find_int(group_list, + ACR_Images_in_acquisition, + 1); + } + } + + if (imri == TIME) { + /* Look for the official time slice count field first. + */ + def_val = acr_find_int(group_list, + ACR_Number_of_time_slices, + 0); + } + gi_ptr->max_size[imri] = acr_find_int(group_list, + mri_total_list[imri], + def_val); + } + else { + gi_ptr->max_size[imri] = 1; + } + + if (gi_ptr->max_size[imri] < 1) { + gi_ptr->max_size[imri] = 1; + } + + /* Check for 3D partitions for slice dimensions */ + if (imri == SLICE) { + /* Get number of 3D partitions for working out number of + * slices + */ + int 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; + } + + gi_ptr->max_size[imri] *= number_of_3D_partitions; + } + + gi_ptr->default_index[imri] = fi_ptr->index[imri]; + gi_ptr->image_index[imri] = -1; + + /* Allocate space for index and coordinate arrays. + * Set the first values. + */ + + gi_ptr->indices[imri] = malloc(gi_ptr->max_size[imri] * sizeof(int)); + + gi_ptr->coordinates[imri] = + malloc(gi_ptr->max_size[imri] * sizeof(double)); + + gi_ptr->widths[imri] = + malloc(gi_ptr->max_size[imri] * sizeof(double)); + + for (index = 0; index < gi_ptr->max_size[imri]; index++) { + gi_ptr->indices[imri][index] = -1; + gi_ptr->coordinates[imri][index] = 0; + gi_ptr->widths[imri][index] = 0; /* default */ + } + gi_ptr->search_start[imri] = 0; + gi_ptr->indices[imri][0] = fi_ptr->index[imri]; + gi_ptr->coordinates[imri][0] = fi_ptr->coordinate[imri]; + gi_ptr->widths[imri][0] = fi_ptr->width[imri]; + + if (G.Debug) { + printf("%2d. %s axis length %d\n", + imri, Mri_Names[imri], gi_ptr->max_size[imri]); + } + } /* Loop over dimensions */ + + /* Get spatial coordinate information */ + gi_ptr->slice_world = volume_to_world[VSLICE]; + gi_ptr->row_world = volume_to_world[VROW]; + gi_ptr->column_world = volume_to_world[VCOLUMN]; + for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { + iworld = volume_to_world[ivolume]; + gi_ptr->step[iworld] = steps[ivolume]; + gi_ptr->start[iworld] = starts[ivolume]; + for (jworld = 0; jworld < WORLD_NDIMS; jworld++) { + gi_ptr->dircos[iworld][jworld] = dircos[ivolume][jworld]; + } + + if (G.Debug) { + printf("%2d. %s axis length %d step %.3f, start %.3f, cosines %.3f,%.3f,%.3f\n", + ivolume, + World_Names[iworld], + spatial_sizes[ivolume], + gi_ptr->step[iworld], + gi_ptr->start[iworld], + gi_ptr->dircos[iworld][XCOORD], + gi_ptr->dircos[iworld][YCOORD], + gi_ptr->dircos[iworld][ZCOORD] + ); + } + } + + /* Set data type and range */ + if (fi_ptr->bits_alloc <= 8) { + gi_ptr->datatype = NC_BYTE; + } + else { + gi_ptr->datatype = NC_SHORT; + } + + /* bert- modify code to correctly read the pixel + * representation if available and use that to set the + * signed/unsigned flag. + */ + ivalue = acr_find_short(group_list, ACR_Pixel_representation, -1); + if (ivalue == ACR_PIXEL_REP_UNSIGNED) { + gi_ptr->is_signed = 0; + } + else if (ivalue == ACR_PIXEL_REP_SIGNED) { + gi_ptr->is_signed = 1; + } + else { + if (ivalue != -1) { + printf("WARNING: Unknown pixel representation value %d\n", + ivalue); + } + gi_ptr->is_signed = ((gi_ptr->datatype == NC_SHORT) && + (fi_ptr->bits_stored < 16)); + } + + gi_ptr->pixel_min = fi_ptr->pixel_min; + gi_ptr->pixel_max = fi_ptr->pixel_max; + + /* Save display window info */ + gi_ptr->window_min = fi_ptr->window_min; + gi_ptr->window_max = fi_ptr->window_max; + + /* Get the rest of the header information */ + get_general_header_info(group_list, gi_ptr); + + /* Copy the group list */ + gi_ptr->group_list = acr_copy_group_list(group_list); + + // note that number of slices will be wrong here for mosaics + // we add some other mosaic-relevant fields... + + gi_ptr->num_mosaic_rows = + acr_find_int(group_list, EXT_Mosaic_rows, 1); + gi_ptr->num_mosaic_cols = + acr_find_int(group_list, EXT_Mosaic_columns, 1); + gi_ptr->num_slices_in_file = + acr_find_int(group_list, EXT_Slices_in_file, 1); + gi_ptr->sub_image_rows = + acr_find_short(group_list, EXT_Sub_image_rows, + acr_find_short(group_list, ACR_Rows, 0)); + gi_ptr->sub_image_columns = + acr_find_short(group_list, EXT_Sub_image_columns, + acr_find_short(group_list, ACR_Rows, 0)); + + /* Set initialized flag */ + gi_ptr->initialized = TRUE; + + if (G.Debug) { + printf("Pixel minimum %.10f maximum %.10f\n", + gi_ptr->pixel_min, gi_ptr->pixel_max); + printf("Window minimum %.10f maximum %.10f\n", + gi_ptr->window_min, gi_ptr->window_max); + } +} + +/* ----------------------------- MNI Header ----------------------------------- + @NAME : get_file_info + @INPUT : group_list - input data + @OUTPUT : file_inf_p - 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 + ---------------------------------------------------------------------------- */ +void +get_file_info(Acr_Group group_list, File_Info *fi_ptr, General_Info *gi_ptr) +{ + Mri_Index imri; /* MRI index (SLICE, ECHO, TIME, PHASE...) */ + int nrows; /* Row count in this file */ + int ncolumns; /* Column count in this file */ + int spatial_sizes[VOL_NDIMS]; /* Voxel coordinate extents */ + double study_id; /* Study identifier */ + int acq_id; /* Acquisition identifier */ + int rec_num; /* ? Seems to be a dummy */ + int cur_index; /* Index of slice(s) in current file */ + int index; /* General index value */ + Orientation orientation; /* TRANSVERSE, SAGITTAL, or CORONAL */ + World_Index volume_to_world[VOL_NDIMS]; /* Maps voxel to world indices */ + double coordinate[WORLD_NDIMS]; /* Slice coordinates */ + double dircos[VOL_NDIMS][WORLD_NDIMS]; /* Direction cosines */ + double steps[VOL_NDIMS]; /* Step (spacing) coordinates */ + double starts[VOL_NDIMS]; /* Start (origin) coordinates */ + Acr_Element_Id mri_index_list[MRI_NDIMS]; + + // Initialize array of elements for MRI positions (indices) + // + mri_index_list[SLICE] = SPI_Current_slice_number; + mri_index_list[ECHO] = ACR_Echo_number; + mri_index_list[TIME] = ACR_Acquisition; + mri_index_list[PHASE] = NULL; + mri_index_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] = acr_find_int(group_list, ACR_Images_in_acquisition, + 1); + + /* Get intensity information + */ + get_intensity_info(group_list, fi_ptr); + + /* Check for necessary values not found + */ + if ((nrows <= 0) || (ncolumns <= 0) || + (fi_ptr->bits_stored <= 0) || + (fi_ptr->bits_alloc <= 0)) { + if (G.Debug) { + printf("ERROR: Needed values missing, marking invalid\n"); + } + fi_ptr->valid = FALSE; + return; + } + + /* Get study, acq, rec, image type id's + */ + get_identification_info(group_list, &study_id, &acq_id, &rec_num, NULL); + + /* Get indices for image in current file + */ + for (imri = 0; imri < MRI_NDIMS; imri++) { + if (mri_index_list[imri] != NULL) { + fi_ptr->index[imri] = acr_find_int(group_list, + mri_index_list[imri], 1); + } + else { + fi_ptr->index[imri] = 1; + } + } + + /* Get coordinate information + */ + get_coordinate_info(group_list, fi_ptr, &orientation, volume_to_world, + spatial_sizes, dircos, steps, starts, coordinate); + + /* + * Use the coordinate information rather than the slice or time + * position derived above. This seems to be much more reliable. + */ + + fi_ptr->index[SLICE] = irnd(fi_ptr->coordinate[SLICE] * 100.0); + + /* For non-IMA files, use the time coordinate to order the time. + * index. IMA files do not seem to have a reliable slice time + * indicator. + */ + if (G.file_type != IMA) { + fi_ptr->index[TIME] = irnd(fi_ptr->coordinate[TIME] * 100.0); + } + + /* Set up general info on first pass + */ + if (!gi_ptr->initialized) { + init_general_info(gi_ptr, + fi_ptr, + group_list, + volume_to_world, + spatial_sizes, + dircos, + steps, + starts, + study_id, + acq_id, + rec_num); + } + + /* Set up file info */ + + /* Update general info and validate file on later passes + */ + else { + + /* Check for consistent pixel minimum and maximum. */ + if ((gi_ptr->pixel_max != fi_ptr->pixel_max) || + (gi_ptr->pixel_min != fi_ptr->pixel_min)) { + printf("WARNING: Inconsistent pixel minimum and maximum\n"); + } + + /* Check for consistent data type */ + if (((gi_ptr->datatype == NC_BYTE) && (fi_ptr->bits_alloc > 8)) || + ((gi_ptr->datatype == NC_SHORT) && (fi_ptr->bits_alloc <= 8))) { + printf("Inconsistent datatype, marking invalid\n"); + fi_ptr->valid = FALSE; + return; + } + + /* Check row and columns sizes */ + if ((nrows != gi_ptr->nrows) && (ncolumns != gi_ptr->ncolumns)) { + printf("Mismatched rows/columns, marking invalid\n"); + fi_ptr->valid = FALSE; + return; + } + + /* Check study and acquisition id's */ + if ((gi_ptr->study_id != study_id) || (gi_ptr->acq_id != acq_id)) { + printf("Mismatched acquisition/study, marking invalid\n"); + fi_ptr->valid = FALSE; + return; + } + + /* Look to see if indices have changed */ + for (imri = 0; imri < MRI_NDIMS; imri++) { + /* If a dimension is known to have a maximum size of one + * or less, we do NOT allow it to grow in any way. An + * exception is made for the slice dimension, however, + * since it appears that it is common for it to be + * unspecified and can be guessed only by the number of + * distinct locations discovered. + */ + if (imri != SLICE && gi_ptr->max_size[imri] <= 1) { + continue; + } + + /* Get current index */ + cur_index = fi_ptr->index[imri]; + + /* Check whether this index is in the list. + */ + if (gi_ptr->cur_size[imri] == 1) { + index = ((cur_index == gi_ptr->default_index[imri]) ? 0 : -1); + } + else { + /* Search list of indices for 'cur_index'. Search is + started at search_start[] and has maximum length of + size[imri]. + */ + index = search_list(cur_index, + gi_ptr->indices[imri], + gi_ptr->cur_size[imri], + gi_ptr->search_start[imri]); + } + + /* If it is not, then add it */ + if (index < 0) { + if (G.Debug >= HI_LOGGING) { + printf("Need to add index %d to %s list, %d/%d\n", + cur_index, Mri_Names[imri], + gi_ptr->cur_size[imri], + gi_ptr->max_size[imri]); + } + + /* Check whether we can add a new index */ + if (gi_ptr->cur_size[imri] >= gi_ptr->max_size[imri]) { + gi_ptr->max_size[imri]++; + gi_ptr->indices[imri] = + realloc(gi_ptr->indices[imri], + gi_ptr->max_size[imri] * sizeof(int)); + + gi_ptr->coordinates[imri] = + realloc(gi_ptr->coordinates[imri], + gi_ptr->max_size[imri] * sizeof(double)); + + gi_ptr->widths[imri] = + realloc(gi_ptr->widths[imri], + gi_ptr->max_size[imri] * sizeof(double)); + } + + + /* Add the index and coordinate to the lists */ + index = gi_ptr->cur_size[imri]; + gi_ptr->search_start[imri] = index; + gi_ptr->indices[imri][index] = cur_index; + gi_ptr->coordinates[imri][index] = fi_ptr->coordinate[imri]; + gi_ptr->widths[imri][index] = fi_ptr->width[imri]; + gi_ptr->cur_size[imri]++; + + } + } /* Loop over Mri_Index */ + + // Update display window info + if (gi_ptr->window_min > fi_ptr->window_min) + gi_ptr->window_min = fi_ptr->window_min; + if (gi_ptr->window_max < fi_ptr->window_max) + gi_ptr->window_max = fi_ptr->window_max; + + } // Update general info for this file + + // If we get to here, then we have a valid file + fi_ptr->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 : + ---------------------------------------------------------------------------- */ +static 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 ( (G.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 : fi_ptr - 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 : + ---------------------------------------------------------------------------- */ +static void +get_intensity_info(Acr_Group group_list, File_Info *fi_ptr) +{ + double window_centre, window_width; + double rescale_intercept, rescale_slope; + int ivalue; /* 0000 for unsigned, 0001 for signed */ + int imin, imax; /* Default minimum and maximum values */ + + /* Get pixel storage information */ + fi_ptr->bits_alloc = acr_find_short(group_list, ACR_Bits_allocated, 0); + fi_ptr->bits_stored = acr_find_short(group_list, ACR_Bits_stored, 0); + + /* bert- properly set the minimum and maximum pixel values depending + * on whether or not this file specifies signed pixel values. + */ + ivalue = acr_find_short(group_list, ACR_Pixel_representation, -1); + + if (ivalue == ACR_PIXEL_REP_SIGNED) { + imin = -(1 << (fi_ptr->bits_stored - 1)); + imax = (1 << (fi_ptr->bits_stored - 1)) - 1; + } + else { + imin = 0; + imax = (1 << fi_ptr->bits_stored) - 1; + } + + if (G.useMinMax) { + // 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. + + fi_ptr->pixel_min = acr_find_short(group_list, + ACR_Smallest_pixel_value, imin); + + fi_ptr->pixel_max = acr_find_short(group_list, + ACR_Largest_pixel_value, imax); + + } + 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 + */ + fi_ptr->pixel_min = imin; + fi_ptr->pixel_max = imax; + } + + /* Get the rescale intercept and slope. If they are not present, + * we use the default values of 0.0 for the intercept and 1.0 for + * the slope. + */ + rescale_intercept = acr_find_double(group_list, ACR_Rescale_intercept, 0); + rescale_slope = acr_find_double(group_list, ACR_Rescale_slope, 1); + + /* If the rescale slope is set to zero, force the default value of + * one and issue a warning. + */ + if (rescale_slope == 0.0) { + printf("WARNING: File contains a rescale slope value of zero.\n"); + rescale_slope = 1.0; + } + + fi_ptr->slice_min = fi_ptr->pixel_min * rescale_slope + rescale_intercept; + fi_ptr->slice_max = fi_ptr->pixel_max * rescale_slope + rescale_intercept; + + /* Get window min and max */ + window_centre = (fi_ptr->slice_max + fi_ptr->slice_min) / 2.0; + window_width = fi_ptr->slice_max - fi_ptr->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); + fi_ptr->window_min = window_centre - window_width / 2.0; + fi_ptr->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 : fi_ptr - 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 : + ---------------------------------------------------------------------------- */ + +#define SPECIAL_CASE_IMA 1 + +#define DARRAY_SIZE 2 + +static void +get_coordinate_info(Acr_Group group_list, + File_Info *fi_ptr, + Orientation *orientation, + World_Index volume_to_world[VOL_NDIMS], + const 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 element; + int found_dircos[VOL_NDIMS]; + int found_coordinate; + double frame_time; + double start_time; + double magnitude; + double largest; + double darray[DARRAY_SIZE]; + double dbl_tmp1, dbl_tmp2; + int result; + + double RowColVec[6]; /* row/column unit vectors in public dicom element */ + + const Orientation orientation_list[WORLD_NDIMS] = { + SAGITTAL, CORONAL, TRANSVERSE + }; + + if (G.Debug >= HI_LOGGING) { + printf("get_coordinate_info(%lx, ...)\n", (unsigned long) group_list); + } + + /* Initialize a few things... */ + for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { + found_dircos[ivolume] = FALSE; + } + found_coordinate = FALSE; + +#ifdef SPECIAL_CASE_IMA + /* TODO: For now this appears to be necessary. In cases I don't fully + * understand, the IMA file's simulated image orientation does not + * give the correct direction cosines. This appears to be an issue + * that may be related to the handedness of the coordinate system or + * some similar issue. + */ + if (G.file_type == N3DCM || G.file_type == IMA) { + Acr_Element_Id dircos_elid[VOL_NDIMS]; + + /* 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++) { + 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 { +#endif + /* read in row/col vectors: + */ + element = acr_find_group_element(group_list, + ACR_Image_orientation_patient); + if (element == NULL) { + /* If we failed to find the newer, better patient orientation + * information, try to use the obsolete information if present. + */ + element = acr_find_group_element(group_list, + ACR_Image_orientation_patient_old); + } + if (element == NULL) { + printf("WARNING: Failed to find image orientation!\n"); + } + else if ((result = acr_get_element_numeric_array(element, 6, + RowColVec)) != 6) { + printf("WARNING: Failed to read image orientation! (%d, '%s')\n", + result, acr_get_element_string(element)); + } + else { + dircos[VCOLUMN][XCOORD] = RowColVec[0]; + dircos[VCOLUMN][YCOORD] = RowColVec[1]; + dircos[VCOLUMN][ZCOORD] = RowColVec[2]; + + dircos[VROW][XCOORD] = RowColVec[3]; + dircos[VROW][YCOORD] = RowColVec[4]; + dircos[VROW][ZCOORD] = RowColVec[5]; + + 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][XCOORD] = + dircos[VCOLUMN][YCOORD] * dircos[VROW][ZCOORD] - + dircos[VCOLUMN][ZCOORD] * dircos[VROW][YCOORD]; + + dircos[VSLICE][YCOORD] = + dircos[VCOLUMN][ZCOORD] * dircos[VROW][XCOORD] - + dircos[VCOLUMN][XCOORD] * dircos[VROW][ZCOORD]; + + dircos[VSLICE][ZCOORD] = + dircos[VCOLUMN][XCOORD] * dircos[VROW][YCOORD] - + dircos[VCOLUMN][YCOORD] * dircos[VROW][XCOORD]; + found_dircos[VSLICE] = TRUE; + } +#ifdef SPECIAL_CASE_IMA + } +#endif + + if (G.Debug >= HI_LOGGING) { + printf("dircos %f %f %f %f %f %f %f %f %f\n", + dircos[VSLICE][XCOORD], + dircos[VSLICE][YCOORD], + dircos[VSLICE][ZCOORD], + dircos[VROW][XCOORD], + dircos[VROW][YCOORD], + dircos[VROW][ZCOORD], + dircos[VCOLUMN][XCOORD], + dircos[VCOLUMN][YCOORD], + dircos[VCOLUMN][ZCOORD]); + } + + /* 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]) { + + if (G.Debug) { + printf("Using default direction cosines\n"); + } + + 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; + } + } + } + + if (G.Debug >= HI_LOGGING) { + printf(" Volume_to_world slice=%s row=%s column=%s\n", + World_Names[volume_to_world[VSLICE]], + World_Names[volume_to_world[VROW]], + World_Names[volume_to_world[VCOLUMN]]); + } + + /* Get orientation (depends on primary direction of slice normal) + */ + *orientation = orientation_list[volume_to_world[VSLICE]]; + if (G.Debug >= HI_LOGGING) { + printf(" Orientation is %s\n", + (*orientation == SAGITTAL) ? "SAGITTAL" : + (*orientation == CORONAL) ? "CORONAL" : "TRANSVERSE"); + } + + /* Get step information + */ + for (ivolume=0; ivolume < DARRAY_SIZE; 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) { + acr_get_element_numeric_array(element, DARRAY_SIZE, 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? */ + + /* Figure out the slice thickness. It could be from either one of + * two possible places in the file. + */ + dbl_tmp1 = acr_find_double(group_list, ACR_Slice_thickness, 0.0); + dbl_tmp2 = acr_find_double(group_list, ACR_Spacing_between_slices, 0.0); + + if (dbl_tmp1 == 0.0) { + if (dbl_tmp2 == 0.0) { + if (G.Debug >= HI_LOGGING) { + printf("Using default slice thickness of 1.0\n"); + } + steps[VSLICE] = 1.0; + } + else { + if (G.Debug >= HI_LOGGING) { + printf("Using (0018,0088) for slice thickness\n"); + } + steps[VSLICE] = dbl_tmp2; + } + } + else if (dbl_tmp2 == 0.0) { + if (G.Debug >= HI_LOGGING) { + printf("Using (0018,0050) for slice thickness\n"); + } + steps[VSLICE] = dbl_tmp1; + } + else { + if (G.Debug && !NEARLY_EQUAL(dbl_tmp1, dbl_tmp2)) { + printf("WARNING: slice thickness conflict: "); + printf("old = %.10f, new = %.10f\n", dbl_tmp1, dbl_tmp2); + } + steps[VSLICE] = dbl_tmp2; + } + + /* 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) { + if (G.Debug >= HI_LOGGING) { + printf("Swapping direction of %s %s\n", + Volume_Names[ivolume], + World_Names[iworld]); + } + steps[ivolume] *= -1.0; + for (iworld = 0; iworld < WORLD_NDIMS; iworld++) { + dircos[ivolume][iworld] *= -1.0; + } + } + } + + /* Find 3D coordinate of slice - ACR_Image_position_patient gives + * the *corner* of the slice! + * + * Start by assuming that we didn't find it. + */ + found_coordinate = FALSE; + for (iworld = 0; iworld < WORLD_NDIMS; iworld++) { + coordinate[iworld] = 0.0; + } + + if (G.opts & OPTS_NO_LOCATION) { + /* If the coordinates are untrustworthy, just generate something + * reasonable for the slice coordinate. Ignore the rest. + */ + coordinate[volume_to_world[VSLICE]] = + (steps[VSLICE] * fi_ptr->index[SLICE]); + found_coordinate = TRUE; + } + else { + element = acr_find_group_element(group_list, + ACR_Image_position_patient); + if (element == NULL) { + element = acr_find_group_element(group_list, + ACR_Image_position_patient_old); + } + if (element == NULL) { + printf("WARNING: Failed to find image position\n"); + } + else if ((result = acr_get_element_numeric_array(element, + WORLD_NDIMS, + coordinate)) != WORLD_NDIMS) { + printf("WARNING: Failed to read image position (%d, '%s')\n", + result, acr_get_element_string(element)); + } + else { + found_coordinate = TRUE; + } + if (!found_coordinate) { + /* Last gasp - try to interpret the slice location as our slice + * position. It might work. + */ + if (!found_coordinate) { + coordinate[volume_to_world[VSLICE]] = + acr_find_double(group_list, ACR_Slice_location, 1.0); + } + + 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; + } + } + + if (G.Debug >= HI_LOGGING) { + printf(" coordinate %f %f %f, start %f %f %f\n", + coordinate[XCOORD], coordinate[YCOORD], coordinate[ZCOORD], + starts[VROW], starts[VCOLUMN], starts[VSLICE]); + } + + /* Find position along each dimension + */ + fi_ptr->coordinate[SLICE] = starts[VSLICE]; + fi_ptr->coordinate[ECHO] = + acr_find_double(group_list, ACR_Echo_time, 0.0) / MS_PER_SECOND; + + + /* Get the dimension width for time, if available. The units are in + * milliseconds in DICOM, whereas we use seconds in MINC. + */ + fi_ptr->width[TIME] = acr_find_double(group_list, + ACR_Actual_frame_duration, + 0.0) / MS_PER_SECOND; + + /* PET scan times (bert) + */ + start_time = acr_find_double(group_list, ACR_Frame_reference_time, -1.0); + frame_time = acr_find_double(group_list, ACR_Actual_frame_duration, -1.0); + if (start_time > 0.0 && frame_time > 0.0) { + frame_time = start_time / 1000.0; /* Convert msec to seconds. */ + } + else { + /* 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 += SECONDS_PER_DAY; + } + } + fi_ptr->coordinate[TIME] = frame_time; + + /* end of time section */ + + fi_ptr->coordinate[PHASE] = 0.0; + fi_ptr->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) + ---------------------------------------------------------------------------- */ +static 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) + ---------------------------------------------------------------------------- */ +static void +convert_dicom_coordinate(double coordinate[WORLD_NDIMS]) +{ + /* Allow the user to override this, if only for debugging purposes... + */ + if (G.opts & OPTS_KEEP_COORD) { + return; + } + + coordinate[XCOORD] = -coordinate[XCOORD]; + coordinate[YCOORD] = -coordinate[YCOORD]; +} + +/* ----------------------------- MNI Header ----------------------------------- + @NAME : get_general_header_info + @INPUT : group_list - input data + @OUTPUT : gi_ptr - 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 : + ---------------------------------------------------------------------------- */ + +static void +get_string_field(char *out_str, Acr_Group group_list, + Acr_Element_Id element_id) +{ + strncpy(out_str, acr_find_string(group_list, element_id, ""), + STRING_T_LEN); +} + +void +get_general_header_info(Acr_Group group_list, General_Info *gi_ptr) +{ + int length; + char *string; + + if (G.Debug) { + printf("SOP Class UID: %s\n", + acr_find_string(group_list, ACR_SOP_Class_UID, "")); + printf("Images in acquisition: %d\n", + acr_find_int(group_list, ACR_Images_in_acquisition, -1)); + printf("Acquisitions in series: %d\n", + acr_find_int(group_list, ACR_Acquisitions_in_series, -1)); + printf("3D raw partitions: %d\n", + acr_find_int(group_list, SPI_Number_of_3D_raw_partitions_nominal, -1)); + } + /* Get intensity units */ + strncpy(gi_ptr->units, "", STRING_T_LEN); + + /* Get patient info */ + get_string_field(gi_ptr->patient.name, group_list, ACR_Patient_name); + + get_string_field(gi_ptr->patient.identification, + group_list, ACR_Patient_identification); + get_string_field(gi_ptr->patient.birth_date, + group_list, ACR_Patient_birth_date); + + get_string_field(gi_ptr->patient.age, + group_list, ACR_Patient_age); + + string = acr_find_string(group_list, ACR_Patient_sex, ""); + if (*string == 'M') + strncpy(gi_ptr->patient.sex, MI_MALE, STRING_T_LEN); + else if (*string == 'F') + strncpy(gi_ptr->patient.sex, MI_FEMALE, STRING_T_LEN); + else if (*string == 'O') + strncpy(gi_ptr->patient.sex, MI_OTHER, STRING_T_LEN); + else + strncpy(gi_ptr->patient.sex, "", STRING_T_LEN); + + gi_ptr->patient.weight = + acr_find_double(group_list, ACR_Patient_weight, -DBL_MAX); + + /* added by rhoge - registration timing info */ + get_string_field(gi_ptr->patient.reg_date, + group_list, ACR_Study_date); + + get_string_field(gi_ptr->patient.reg_time, + group_list, ACR_Study_time); + + /* Get study info */ + get_string_field(gi_ptr->study.start_time, + group_list, ACR_Study_date); + + length = strlen(gi_ptr->study.start_time); + gi_ptr->study.start_time[length] = ' '; + length++; + strncpy(&gi_ptr->study.start_time[length], + acr_find_string(group_list, ACR_Study_time, ""), STRING_T_LEN - length); + string = acr_find_string(group_list, ACR_Modality, ""); + if (strcmp(string, ACR_MODALITY_MR) == 0) + strncpy(gi_ptr->study.modality, MI_MRI, STRING_T_LEN); + else if (strcmp(string, ACR_MODALITY_PT) == 0) + strncpy(gi_ptr->study.modality, MI_PET, STRING_T_LEN); + get_string_field(gi_ptr->study.manufacturer, + group_list, ACR_Manufacturer); + get_string_field(gi_ptr->study.model, + group_list, ACR_Manufacturer_model); + gi_ptr->study.field_value = + acr_find_double(group_list, ACR_Magnetic_field_strength, -DBL_MAX); + get_string_field(gi_ptr->study.software_version, + group_list, ACR_Software_versions); + get_string_field(gi_ptr->study.serial_no, + group_list, ACR_Device_serial_number); + get_string_field(gi_ptr->study.calibration_date, + group_list, ACR_Calibration_date); + get_string_field(gi_ptr->study.institution, + group_list, ACR_Institution_id); + get_string_field(gi_ptr->study.station_id, + group_list, ACR_Station_id); + get_string_field(gi_ptr->study.referring_physician, + group_list, ACR_Referring_physician); + get_string_field(gi_ptr->study.performing_physician, + group_list, ACR_Performing_physician); + get_string_field(gi_ptr->study.operator, + group_list, ACR_Operators_name); + get_string_field(gi_ptr->study.procedure, + group_list, ACR_Procedure_description); + sprintf(gi_ptr->study.study_id, "%.6f",gi_ptr->study_id); + + /* Acquisition id modified by rhoge to get rid of first digit that + is not required for identification of run */ + /* sprintf(gi_ptr->study.acquisition_id, "%d_%d", + acr_find_int(group_list, ACR_Series, 0), gi_ptr->acq_id); */ + sprintf(gi_ptr->study.acquisition_id, "%d", gi_ptr->acq_id); + + /* Get acquisition information */ + + get_string_field(gi_ptr->acq.scan_seq, group_list, ACR_Sequence_name); + get_string_field(gi_ptr->acq.protocol_name, group_list, ACR_Protocol_name); + get_string_field(gi_ptr->acq.receive_coil, group_list, + ACR_Receive_coil_name); + get_string_field(gi_ptr->acq.transmit_coil, group_list, + ACR_Transmit_coil_name); + + gi_ptr->acq.rep_time = + acr_find_double(group_list, ACR_Repetition_time, -DBL_MAX); + if (gi_ptr->acq.rep_time != -DBL_MAX) + gi_ptr->acq.rep_time /= 1000.0; + + gi_ptr->acq.echo_time = + acr_find_double(group_list, ACR_Echo_time, -DBL_MAX); + if (gi_ptr->acq.echo_time != -DBL_MAX) + gi_ptr->acq.echo_time /= 1000.0; + + gi_ptr->acq.echo_number = + acr_find_double(group_list, ACR_Echo_number, -DBL_MAX); + + gi_ptr->acq.inv_time = + acr_find_double(group_list, ACR_Inversion_time, -DBL_MAX); + if (gi_ptr->acq.inv_time != -DBL_MAX) + gi_ptr->acq.inv_time /= 1000.0; + gi_ptr->acq.b_value = + acr_find_double(group_list, EXT_Diffusion_b_value, -DBL_MAX); + gi_ptr->acq.flip_angle = + acr_find_double(group_list, ACR_Flip_angle, -DBL_MAX); + gi_ptr->acq.slice_thickness = + acr_find_double(group_list, ACR_Slice_thickness, -DBL_MAX); + gi_ptr->acq.num_slices = + acr_find_double(group_list, ACR_Images_in_acquisition, -DBL_MAX); + gi_ptr->acq.num_dyn_scans = + acr_find_double(group_list, ACR_Acquisitions_in_series, -DBL_MAX); + gi_ptr->acq.num_avg = + acr_find_double(group_list, ACR_Nr_of_averages, -DBL_MAX); + gi_ptr->acq.imaging_freq = + acr_find_double(group_list, ACR_Imaging_frequency, -DBL_MAX); + if (gi_ptr->acq.imaging_freq != -DBL_MAX) + gi_ptr->acq.imaging_freq *= 1e6; + get_string_field(gi_ptr->acq.imaged_nucl, + group_list, ACR_Imaged_nucleus); + gi_ptr->acq.win_center = + acr_find_double(group_list, ACR_Window_centre, -DBL_MAX); + gi_ptr->acq.win_width = + acr_find_double(group_list, ACR_Window_width, -DBL_MAX); + + gi_ptr->acq.num_phase_enc_steps = + acr_find_double(group_list, ACR_Number_of_phase_encoding_steps, -DBL_MAX); + gi_ptr->acq.percent_sampling = 100 * + acr_find_double(group_list, ACR_Percent_sampling, -DBL_MAX); + + gi_ptr->acq.percent_phase_fov = 100 * + acr_find_double(group_list, ACR_Percent_phase_field_of_view, -DBL_MAX); + + gi_ptr->acq.pixel_bandwidth = + acr_find_double(group_list, ACR_Pixel_bandwidth, -DBL_MAX); + + gi_ptr->acq.sar = acr_find_double(group_list, ACR_SAR, -DBL_MAX); + + get_string_field(gi_ptr->acq.mr_acq_type, + group_list, ACR_MR_acquisition_type); + + get_string_field(gi_ptr->acq.image_type, group_list, ACR_Image_type); + if (G.Debug) { + if (strstr(gi_ptr->acq.image_type, "MOSAIC") != NULL) { + printf("This appears to be a Mosaic image\n"); + } + } + + get_string_field(gi_ptr->acq.phase_enc_dir, + group_list, ACR_Phase_encoding_direction); + + strncpy(gi_ptr->acq.comments, "", STRING_T_LEN); + + /* Siemens Numaris 4 specific! + */ + +#if 0 + gi_ptr->acq.MrProt = strdup(acr_find_string(group_list, EXT_MrProt_dump, + "")); +#else + gi_ptr->acq.MrProt = strdup(""); +#endif +} + +/* ----------------------------- 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 : + ---------------------------------------------------------------------------- */ +static 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 hh, mm, ss; + + /* Get the components of the time */ + + hh = (int) (dicom_time / DICOM_SECONDS_PER_HOUR); + dicom_time -= hh * DICOM_SECONDS_PER_HOUR; + mm = (int) (dicom_time / DICOM_SECONDS_PER_MINUTE); + dicom_time -= mm * DICOM_SECONDS_PER_MINUTE; + ss = dicom_time; + + /* Work out the number of seconds */ + + return (hh * 3600.0) + (mm * 60.0) + ss; +} + +/* ----------------------------- 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 : + ---------------------------------------------------------------------------- */ +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_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 = ACR_IMAGE_EID; + element = acr_find_group_element(group_list, &elid); + if (element == NULL) { + 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 { + memset(image->data, 0, imagepix * sizeof(short)); + } + } + + return; +} + + +/* ----------------------------- 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 : + ---------------------------------------------------------------------------- */ + +#define IDEFAULT (-1) + +void +parse_dicom_groups(Acr_Group group_list, Data_Object_Info *di_ptr) +{ + Acr_Element element; + unsigned short AcqMat[4]; + unsigned short freq_rows; + unsigned short freq_cols; + unsigned short phase_rows; + unsigned short phase_cols; + double slice_coord[WORLD_NDIMS]; + + /* Get info to construct unique identifiers for study, series/acq + * for file processing + */ + get_identification_info(group_list, + &(di_ptr->study_id), &(di_ptr->acq_id), + &(di_ptr->rec_num), &(di_ptr->image_type)); + + /* Get number of echos, echo number, number of dynamic scans and + * dynamic_scan_number + */ + + di_ptr->num_echoes = acr_find_int(group_list, + ACR_Echo_train_length, + IDEFAULT); + + di_ptr->echo_number = acr_find_int(group_list, + ACR_Echo_number, + IDEFAULT); + + di_ptr->num_dyn_scans = acr_find_int(group_list, + ACR_Acquisitions_in_series, + IDEFAULT); + + di_ptr->dyn_scan_number = acr_find_int(group_list, + ACR_Acquisition, + IDEFAULT); + + di_ptr->global_image_number = acr_find_int(group_list, + ACR_Image, + IDEFAULT); + + /* rhoge: + new info added to di_ptr 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 */ + + di_ptr->num_slices_nominal = acr_find_int(group_list, + ACR_Images_in_acquisition, + IDEFAULT); + + di_ptr->slice_number = acr_find_int(group_list, + SPI_Current_slice_number, + IDEFAULT); + + di_ptr->slice_location = acr_find_double(group_list, + ACR_Slice_location, + 0.0); + + di_ptr->coord_found = 0; + element = acr_find_group_element(group_list, ACR_Image_position_patient); + if (element == NULL) { + element = acr_find_group_element(group_list, + ACR_Image_position_patient_old); + } + if (element != NULL) { + if (acr_get_element_numeric_array(element, WORLD_NDIMS, + slice_coord) == WORLD_NDIMS) { + di_ptr->coord_found = 1; + } + } + + /* identification info needed to generate unique session id + * for file names + */ + di_ptr->study_date = acr_find_int(group_list, ACR_Study_date, + IDEFAULT); + + di_ptr->study_time = acr_find_int(group_list, ACR_Study_time, + IDEFAULT); + + di_ptr->scanner_serialno = acr_find_int(group_list, + ACR_Device_serial_number, + IDEFAULT); + + /* identification info needed to determine if mosaics used + */ + + element = acr_find_group_element(group_list, ACR_Acquisition_matrix); + + if (element != NULL) { + 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: + */ + di_ptr->acq_rows = ( freq_rows > freq_cols ? freq_rows : freq_cols ); + + /* all images are square, at this time + */ + di_ptr->acq_cols = di_ptr->acq_rows; + } + else { + di_ptr->acq_rows = IDEFAULT; + di_ptr->acq_cols = IDEFAULT; + } + + di_ptr->rec_rows = acr_find_int(group_list, ACR_Rows, IDEFAULT); + di_ptr->rec_cols = acr_find_int(group_list, ACR_Columns, IDEFAULT); + + di_ptr->num_mosaic_rows = acr_find_int(group_list, EXT_Mosaic_rows, + IDEFAULT); + di_ptr->num_mosaic_cols = acr_find_int(group_list, EXT_Mosaic_columns, + IDEFAULT); + di_ptr->num_slices_in_file = acr_find_int(group_list, EXT_Slices_in_file, + IDEFAULT); + + /* sequence, protocol names (useful for debugging): + */ + + get_string_field(di_ptr->sequence_name, group_list, ACR_Sequence_name); + get_string_field(di_ptr->protocol_name, group_list, ACR_Protocol_name); + get_string_field(di_ptr->patient_name, group_list, ACR_Patient_name); + get_string_field(di_ptr->patient_id, group_list, ACR_Patient_identification); +} + +
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/dicom_read.h @@ -0,0 +1,4 @@ +extern void get_siemens_dicom_image(Acr_Group group_list, Image_Data *image); +extern void parse_dicom_groups(Acr_Group group_list, Data_Object_Info *di_ptr); +extern void get_file_info(Acr_Group group_list, File_Info *file_info, + General_Info *general_info);
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/dicom_to_minc.c @@ -0,0 +1,1823 @@ +/* ----------------------------- MNI Header ----------------------------------- + @NAME : dicom_to_minc.c + @DESCRIPTION: Code to convert a list of DICOM files to minc + format. + @METHOD : + @GLOBALS : + @CALLS : + @CREATED : January 28, 1997 (Peter Neelin) + @MODIFIED : + * $Log: dicom_to_minc.c,v $ + * Revision 1.13.2.1 2005-05-12 21:16:47 bert + * Initial checkin + * + * Revision 1.13 2005/05/09 15:33:20 bert + * Fix handing of descending mosaic sequences; don't print GEMS field missing warnings by default + * + * Revision 1.12 2005/04/28 17:38:06 bert + * Insert and retrieve width information when sorting a dimension + * + * Revision 1.11 2005/04/21 22:31:29 bert + * Copy SPI_Magnetic_field_strength to standard field in copy_spi_to_acr(); Relax some tests to avoid spurious warnings + * + * Revision 1.10 2005/04/20 23:25:50 bert + * Add copy_spi_to_acr() function, minor name and comment changes + * + * Revision 1.9 2005/04/20 17:48:16 bert + * Copy SPI_Number_of_slices_nominal to ACR_Image_in_acquisition for Siemens + * + * Revision 1.8 2005/04/18 21:04:25 bert + * Get rid of old is_reversed behavior, always use most natural sort of the image. Also tried to fix listing function somewhat. + * + * Revision 1.7 2005/04/05 21:55:33 bert + * Update handling of Siemens ASCCONV to reflect value of uc2DInterpolation field for mosaic images. Additional cleanup of mosaic code and minor tweak to suppress GEMS warning message for PET data. + * + * Revision 1.6 2005/03/29 20:20:42 bert + * Add checks for GE Medical Systems proprietary files + * + * Revision 1.5 2005/03/14 22:26:40 bert + * Dump the entire coordinate array for each MRI dimension after sorting. Also detect GE scans. + * + * Revision 1.4 2005/03/13 19:35:11 bert + * Lots of changes for dealing with some proprietary Philips stuff + * + * Revision 1.3 2005/03/03 18:59:15 bert + * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) + * + * Revision 1.2 2005/03/02 20:16:24 bert + * Latest changes and cleanup + * + * Revision 1.1 2005/02/17 16:38:10 bert + * Initial checkin, revised DICOM to MINC converter + * + * Revision 1.1.1.1 2003/08/15 19:52:55 leili + * Leili's dicom server for sonata + * + * 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. + ---------------------------------------------------------------------------- */ + +static const char rcsid[] = "$Header: /private-cvsroot/minc/conversion/dcm2mnc/dicom_to_minc.c,v 1.13.2.1 2005-05-12 21:16:47 bert Exp $"; +#include "dcm2mnc.h" +#include <math.h> + +const char *World_Names[WORLD_NDIMS] = { "X", "Y", "Z" }; +const char *Volume_Names[VOL_NDIMS] = { "Slice", "Row", "Column" }; +const char *Mri_Names[MRI_NDIMS] = {"Slice", "Echo", "Time", "Phase", "ChmSh"}; + +/* Private structure definitions. */ + +/* multi-image (mosaic) info */ +typedef struct { + int packed; + mosaic_seq_t 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 slice_count; + double normal[WORLD_NDIMS]; + double step[WORLD_NDIMS]; + double position[WORLD_NDIMS]; +} Mosaic_Info; + +/* Structure for sorting dimensions */ +typedef struct { + int identifier; + int original_index; + double value; + double width; +} Sort_Element; + +/* Private function definitions */ +static int mosaic_init(Acr_Group, Mosaic_Info *, int); +static void mosaic_cleanup(Acr_Group, Mosaic_Info *); +static int mosaic_modify_group_list(Acr_Group, Mosaic_Info *, int, int); + +static void free_info(General_Info *gi_ptr, File_Info *fi_ptr, + int num_files); +static int dimension_sort_function(const void *v1, const void *v2); +static void sort_dimensions(General_Info *gi_ptr); +static int prot_find_string(Acr_Element Protocol, const char *name, + char *value); +static char *dump_protocol_text(Acr_Element Protocol); + +/* ----------------------------- MNI Header ----------------------------------- + @NAME : dicom_to_minc + @INPUT : num_files - number of image files + file_list - list of file names + minc_file - name of minc file to create, or NULL to 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 : + @CALLS : + @CREATED : November 25, 1993 (Peter Neelin) + @MODIFIED : + ---------------------------------------------------------------------------- */ +int +dicom_to_minc(int num_files, + const char *file_list[], + const char *minc_file, + int clobber, + const char *file_prefix, + char **output_file_name) +{ + Acr_Group group_list; /* List of ACR/NEMA groups & elements */ + File_Info *fi_ptr; /* Array of per-file information */ + General_Info gi; /* General (common) DICOM file information */ + int max_group; /* Maximum group number to read */ + Image_Data image; /* Actual image data */ + int icvid; /* MINC Image Conversion Variable */ + int ifile; /* File index */ + Mri_Index imri; /* MRI axis index */ + char *out_file_name; /* Output MINC filename */ + int isep; /* Loop counter */ + const Loop_Type loop_type = NONE; /* MINC loop type always none for now */ + int subimage; /* Loop counter for MOSAIC images per file */ + int iimage; /* Loop counter for all files/images */ + int num_images_allocated; /* Total number of slices (>= # files) */ + Mosaic_Info mi; /* Mosaic (multi-image) information */ + int n_slices_in_file; + + /* Allocate space for the file information */ + fi_ptr = malloc(num_files * sizeof(*fi_ptr)); + CHKMEM(fi_ptr); + + num_images_allocated = num_files; + + /* Last group needed for first pass + */ + max_group = ACR_IMAGE_GID - 1; + + /* Add all control characters as numeric array separators to handle + * odd behaviour with Siemens dicom files + */ + for (isep = 0; isep < 31; isep++) { + acr_element_numeric_array_separator(isep); + } + + /* Initialize some values for general info */ + gi.initialized = FALSE; + gi.group_list = NULL; + for (imri = 0; imri < MRI_NDIMS; imri++) { + gi.indices[imri] = NULL; + gi.coordinates[imri] = NULL; + gi.widths[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 (G.Debug >= HI_LOGGING) { + printf("\nFile %s\n", file_list[ifile]); + } + + if (!G.Debug) { + progress(ifile, num_files, "-Parsing series info"); + } + + /* Read the file + */ + if (G.file_type == N4DCM) { + group_list = read_numa4_dicom(file_list[ifile], max_group); + } + else if (G.file_type == IMA) { + group_list = siemens_to_dicom(file_list[ifile], max_group); + } + + if (group_list == NULL) { + fprintf(stderr, "Error parsing file '%s' on 1st pass.\n", + file_list[ifile]); + exit(-1); + } + + if (G.opts & OPTS_NO_MOSAIC) { + n_slices_in_file = 1; + } + else { + n_slices_in_file = acr_find_int(group_list, EXT_Slices_in_file, 1); + } + + /* initialize big and small images, if mosaic + */ + if (n_slices_in_file > 1) { + + mosaic_init(group_list, &mi, FALSE); + + num_images_allocated += n_slices_in_file - 1; + + fi_ptr = realloc(fi_ptr, num_images_allocated * sizeof(*fi_ptr)); + CHKMEM(fi_ptr); + } + + /* loop over subimages in mosaic + */ + for (subimage = 0; subimage < n_slices_in_file; subimage++) { + + /* Modify the group list for this image if mosaic + */ + if (n_slices_in_file > 1) { + mosaic_modify_group_list(group_list, &mi, subimage, FALSE); + } + + /* Get file-specific information + */ + get_file_info(group_list, &fi_ptr[iimage], &gi); + + /* increment iimage here + */ + iimage++; + } + + /* Delete the group list + */ + acr_delete_group_list(group_list); + + /* cleanup mosaic struct if used + */ + if (n_slices_in_file > 1) { + mosaic_cleanup(group_list, &mi); + } + } + + /* Sort the dimensions */ + sort_dimensions(&gi); + + /* Create the output file + */ + if (gi.initialized) { + icvid = create_minc_file(minc_file, + clobber, + &gi, + 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 ((!gi.initialized) || (icvid == MI_ERROR)) { + if (gi.initialized) { + fprintf(stderr, "Error creating MINC file %s.\n", out_file_name); + } + free_info(&gi, fi_ptr, num_files); + free(fi_ptr); + return EXIT_FAILURE; + } + + if (G.Debug) { + printf("Writing %d images to MINC file\n", num_files); + } + + /* Last group needed for second pass + * we now have to read up to and including the image, + * since image pointers are needed in mosaic_init + */ + max_group = ACR_IMAGE_GID; + + /* Loop through the files again and put images into the minc file + */ + iimage = 0; + for (ifile = 0; ifile < num_files; ifile++) { + + if (!G.Debug) { + progress(ifile, num_files, "-Creating minc file"); + } + + /* Check that we have a valid file + */ + if (!fi_ptr[ifile].valid) { + printf("WARNING: file %s was marked invalid\n", + file_list[ifile]); + continue; + } + + /* Read the file + */ + if (G.file_type == N4DCM) { + group_list = read_numa4_dicom(file_list[ifile], max_group); + } + else if (G.file_type == IMA) { + group_list = siemens_to_dicom(file_list[ifile], max_group); + } + + if (group_list == NULL) { + fprintf(stderr, "Error parsing file '%s' during 2nd pass.\n", + file_list[ifile]); + exit(-1); + } + + /* initialize big and small images, if mosaic + */ + if (n_slices_in_file > 1) { + mosaic_init(group_list, &mi, TRUE); + } + + /* loop over subimages in mosaic + */ + for (subimage = 0; subimage < n_slices_in_file; subimage++) { + + /* Modify the group list for this image if mosaic + */ + if (n_slices_in_file > 1) { + mosaic_modify_group_list(group_list, &mi, + subimage, TRUE); + } + + /* Get image + */ + get_siemens_dicom_image(group_list, &image); + + /* Save the image and any other information + */ + save_minc_image(icvid, &gi, &fi_ptr[iimage], &image); + + /* increment image counter + */ + iimage++; + } + + /* Delete the group list + */ + acr_delete_group_list(group_list); + + /* cleanup mosaic struct if used + */ + if (n_slices_in_file > 1) { + mosaic_cleanup(group_list, &mi); + } + + /* Free the image data */ + if ((image.data != NULL) && (image.free)) { + free(image.data); + } + } + + /* Close the output file */ + close_minc_file(icvid); + + /* Free the gi and fi_ptr stuff */ + free_info(&gi, fi_ptr, num_files); + free(fi_ptr); + + return EXIT_SUCCESS; +} + +/* ----------------------------- MNI Header ----------------------------------- + @NAME : read_std_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 : + ---------------------------------------------------------------------------- */ + +Acr_Group +read_std_dicom(const char *filename, int max_group) +{ + FILE *fp; + Acr_File *afp; + Acr_Group group_list; + int status; + + /* Open the file + */ + fp = fopen(filename, "rb"); + if (fp == NULL) { + return NULL; + } + + /* Connect to input stream + */ + afp = acr_file_initialize(fp, 0, acr_stdio_read); + if (afp == NULL) { + return NULL; + } + + if (acr_test_dicom_file(afp) != ACR_OK) { + return NULL; + } + + // Read in group list + status = acr_input_group_list(afp, &group_list, max_group); + if (status != ACR_END_OF_INPUT && status != ACR_OK) { + return NULL; + } + + // Close the file + acr_file_free(afp); + fclose(fp); + + return (group_list); +} + +static Acr_Group +add_siemens_info(Acr_Group group_list) +{ + /* 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; + int num_encodings; + int enc_ix; + string_t str_buf; + char *str_ptr; + int interpolation_flag; + + /* 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!) + */ + + /* read in Protocol group */ + + protocol = acr_find_group_element(group_list, SPI_Protocol); + if (protocol == NULL) { + if (G.Debug >= HI_LOGGING) { + printf("No Siemens protocol structure found...\n"); + } + return group_list; + } + + if (G.Debug >= HI_LOGGING) { + printf("Incorporating Siemens protocol structure...\n"); + } + + /* Add number of dynamic scans: + */ + prot_find_string(protocol, "lRepetitions", str_buf); + acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, + atoi(str_buf) + 1); + + /* add number of echoes: + */ + prot_find_string(protocol, "lContrasts", str_buf); + acr_insert_numeric(&group_list, SPI_Number_of_echoes, atoi(str_buf)); + + /* Add receiving coil (for some reason this isn't in generic groups) + */ + prot_find_string(protocol, + "sCOIL_SELECT_MEAS.asList[0].sCoilElementID.tCoilID", + str_buf); + acr_insert_string(&group_list, ACR_Receive_coil_name, str_buf); + + /* add MrProt dump + */ + str_ptr = dump_protocol_text(protocol); + acr_insert_string(&group_list, EXT_MrProt_dump, str_ptr); + free(str_ptr); + + /* add number of slices: (called `Partitions' for 3D) */ + prot_find_string(protocol, "sSliceArray.lSize", str_buf); + num_slices = atoi(str_buf); + + prot_find_string(protocol, "sKSpace.lPartitions", str_buf); + num_partitions = atoi(str_buf); + + /* This is a hack based upon the observation that for at least some + * conversions, this value seems to give the true number of slices + * rather than the sKSpace.lPartitions value (bert) + */ + prot_find_string(protocol, "sKSpace.lImagesPerSlab", str_buf); + if (str_buf[0] != '\0') { + int num_images_per_slab = atoi(str_buf); + if (num_images_per_slab > num_partitions) { + num_partitions = num_images_per_slab; + } + } + + /* 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); + } + + str_ptr = acr_find_string(group_list, ACR_Image_type, ""); + if (strstr(str_ptr, "MOSAIC") != NULL) { + /* Now figure out mosaic rows and columns, and put in EXT shadow group + * Check for interpolation - will require 2x scaling of rows and columns. + */ + prot_find_string(protocol, "sKSpace.uc2DInterpolation", str_buf); + interpolation_flag = strtol(str_buf, NULL, 0); + + /* Assign defaults in case something goes wrong below... + */ + subimage_rows = 0; + subimage_cols = 0; + + /* 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); + if (element == NULL) { + printf("WARNING: Can't find acquisition matrix\n"); + } + else if (acr_get_element_short_array(element, 4, subimage_size) != 4) { + printf("WARNING: Can't read acquisition matrix\n"); + } + else { + if (G.Debug >= HI_LOGGING) { + printf(" * Acquisition matrix %d %d %d %d\n", + subimage_size[0], + subimage_size[1], + subimage_size[2], + subimage_size[3]); + } + /* Get subimage dimensions, assuming the OPPOSITE of the + * reported phase-encode direction!! + */ + str_ptr = acr_find_string(group_list, ACR_Phase_encoding_direction,""); + if (!strncmp(str_ptr, "COL", 3)) { + subimage_rows = subimage_size[3]; + subimage_cols = subimage_size[0]; + } + else if (!strncmp(str_ptr, "ROW", 3)) { + subimage_rows = subimage_size[2]; + subimage_cols = subimage_size[1]; + } + else { + printf("WARNING: Unknown phase encoding direction '%s'\n", + str_ptr); + } + + /* If interpolation, multiply rows and columns by 2 */ + if (interpolation_flag) { + subimage_rows *= 2; + subimage_cols *= 2; + } + } + + /* If these are not set or still zero, assume this is NOT a mosaic + * format file. + */ + if (subimage_rows == 0 || subimage_cols == 0) { + subimage_rows = acr_find_int(group_list, ACR_Rows, 1); + subimage_cols = acr_find_int(group_list, ACR_Columns, 1); + mosaic_rows = 1; + mosaic_cols = 1; + } + else { + if (G.Debug >= HI_LOGGING) { + printf("Assuming MOSAIC, %dx%d\n", + subimage_rows, subimage_cols); + } + 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) { + + str_ptr = acr_find_string(group_list, ACR_MR_acquisition_type, ""); + + /* 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(str_ptr, "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); + } + + /* if 3D mosaiced scan, write number of partitions to number of + * slices in dicom group + */ + else if (!strncmp(str_ptr, "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 + */ + prot_find_string(protocol, "sDiffusion.ucMode", str_buf); + if (!strcmp(str_buf, "0x4")) { + + /* try to get b value */ + prot_find_string(protocol, "sDiffusion.alBValue[1]", str_buf); + acr_insert_numeric(&group_list, EXT_Diffusion_b_value, + atoi(str_buf)); + + /* if all averages in one series: */ + prot_find_string(protocol, "ucOneSeriesForAllMeas", str_buf); + if (!strcmp(str_buf, "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 + */ + str_ptr = strstr(acr_find_string(group_list, + ACR_Sequence_name,""), "#"); + if (str_ptr == NULL) { + enc_ix = 0; + } + else { + enc_ix = atoi(str_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!! + */ + str_ptr = strstr(acr_find_string(group_list, + ACR_Sequence_name, ""), "#"); + if (str_ptr == NULL) { + enc_ix = 0; + } + else { + enc_ix = atoi(str_ptr + sizeof(char)); + } + acr_insert_numeric(&group_list, ACR_Acquisition, enc_ix); + } + } // end of diffusion scan handling + return (group_list); +} + +#define PMS_SET_CREATOR(el, cr) \ + (el)->element_id = ((el)->element_id & 0xff) + ((cr)->element_id << 8) + +static Acr_Group +add_philips_info(Acr_Group group_list) +{ + struct Acr_Element_Id creator_id; + Acr_Group pms_group; + Acr_Element pms_element; + Acr_Element pms_element_list; + char *str_ptr; + int slice_count; + int slice_index; + + /* To use the Philips proprietary group, we have to figure out the + * DICOM private creator ID in use. The group ID is always 0x2001, + * but the upper eight bits of the element ID are somewhat variable. + * To tell what they are, we have to search through the element ID's + * from 0x0001-0x00ff and find one that contains the text + * "PHILIPS IMAGING DD 001" or "Philips Imaging DD 001". The value + * of this element is then used for the upper eight bits of all + * subsequent Philips private element ID's. + */ + pms_group = acr_find_group(group_list, PMS_PRIVATE_GROUP_ID); + if (pms_group != NULL) { + pms_element_list = acr_get_group_element_list(pms_group); + creator_id.group_id = PMS_PRIVATE_GROUP_ID; + creator_id.vr_code = ACR_VR_LO; + for (creator_id.element_id = 0x0001; + creator_id.element_id <= 0x00ff; + creator_id.element_id++) { + pms_element = acr_find_element_id(pms_element_list, &creator_id); + if (pms_element != NULL) { + str_ptr = acr_get_element_string(pms_element); + if (str_ptr != NULL && + (!strcmp(str_ptr, "Philips Imaging DD 001") || + !strcmp(str_ptr, "PHILIPS IMAGING DD 001"))) { + /* Found it!!! */ + break; + } + } + } + } + if (creator_id.element_id > 0xff) { + printf("WARNING: Can't find Philips private creator ID.\n"); + } + else { + if (G.Debug >= HI_LOGGING) { + printf("Found Philips private creator ID at %#x\n", + creator_id.element_id); + } + + PMS_SET_CREATOR(PMS_Number_of_Slices_MR, &creator_id); + slice_count = acr_find_int(group_list, PMS_Number_of_Slices_MR, -1); + if (slice_count < 0) { + printf("WARNING: Can't find Philips slice count\n"); + } + else { + acr_insert_short(&group_list, ACR_Images_in_acquisition, + slice_count); + } + PMS_SET_CREATOR(PMS_Slice_Number_MR, &creator_id); + slice_index = acr_find_int(group_list, PMS_Slice_Number_MR, -1); + if (slice_index < 0) { + printf("WARNING: Can't find Philips slice index\n"); + } + else { + /* TODO: It is really quite gross that we have to resort to + * using a Siemens-proprietary field to tell the rest of the + * code which slice we are on. But such is life, for now... + */ + acr_insert_numeric(&group_list, SPI_Current_slice_number, + (double) slice_index); + } + } + return (group_list); +} + +Acr_Group +add_gems_info(Acr_Group group_list) +{ + char *tmp_str; + int image_count; + int image_index; + double tr; + int ok; + + ok = 1; + tmp_str = acr_find_string(group_list, GEMS_Acqu_private_creator_id, ""); + if (strcmp(tmp_str, "GEMS_ACQU_01")) { + ok = 0; + } + + tmp_str = acr_find_string(group_list, GEMS_Sers_private_creator_id, ""); + if (strcmp(tmp_str, "GEMS_SERS_01")) { + ok = 0; + } + + if (!ok) { + /* Warn only for non-PET things. We know the PET scanner doesn't + * rely on this proprietary nonsense. + */ + tmp_str = acr_find_string(group_list, ACR_Modality, ""); + if (strcmp(tmp_str, "PT") && G.Debug) { + printf("WARNING: GEMS data not found\n"); + } + } + + tmp_str = acr_find_string(group_list, ACR_Image_type, ""); + image_index = acr_find_int(group_list, ACR_Image, -1); + image_count = acr_find_int(group_list, GEMS_Images_in_series, -1); + tr = acr_find_double(group_list, ACR_Repetition_time, 0.0); + + /* If we found a valid image count in the proprietary field, copy it + * into the standard field if the standard field is not already set. + */ + if (image_count > 0 && + acr_find_int(group_list, ACR_Images_in_acquisition, -1) < 0) { + acr_insert_long(&group_list, ACR_Images_in_acquisition, image_count); + } + + /* Do this only for EPI images for now + */ + if (strstr(tmp_str, "\\EPI") != NULL && + image_index >= 0 && + image_count > 0 && + tr != 0.0) { + int frame_count = acr_find_int(group_list, + GEMS_Fast_phases, -1); + if (frame_count > 0) { + if (acr_find_int(group_list, ACR_Acquisitions_in_series, -1) < 0) { + acr_insert_long(&group_list, ACR_Acquisitions_in_series, + frame_count); + } + } + + if (acr_find_double(group_list, ACR_Frame_reference_time, -1.0) < 0) { + acr_insert_numeric(&group_list, ACR_Frame_reference_time, + ((image_index - 1) / image_count) * tr); + } + if (acr_find_double(group_list, ACR_Actual_frame_duration, -1.0) < 0) { + acr_insert_numeric(&group_list, ACR_Actual_frame_duration, + tr); + } + + if (G.Debug >= HI_LOGGING) { + printf("EPI slice %d timing information: %f, %f\n", + image_index, ((image_index - 1) / image_count) * tr, + tr); + } + } + return (group_list); +} + + +Acr_Group +copy_spi_to_acr(Acr_Group group_list) +{ + int itemp; + double dtemp; + + itemp = acr_find_int(group_list, SPI_Number_of_slices_nominal, 0); + if (itemp != 0) { + acr_insert_numeric(&group_list, ACR_Images_in_acquisition, itemp); + } + + itemp = acr_find_int(group_list, SPI_Number_of_echoes, 0); + if (itemp != 0) { + acr_insert_numeric(&group_list, ACR_Echo_train_length, itemp); + } + + dtemp = acr_find_double(group_list, SPI_Magnetic_field_strength, 0); + if (dtemp != 0.0) { + acr_insert_numeric(&group_list, ACR_Magnetic_field_strength, dtemp); + } + return (group_list); +} + +/* ----------------------------- MNI Header ----------------------------------- + @NAME : read_numa4_dicom + @INPUT : filename - read a standard DICOM file + 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 : + ---------------------------------------------------------------------------- */ + +Acr_Group +read_numa4_dicom(const char *filename, int max_group) +{ + Acr_Group group_list; + char *str_ptr; + + group_list = read_std_dicom(filename, max_group); + if (group_list == NULL) { + return NULL; + } + + /* Check the manufacturer. If it is one we know, try to interpret + * the private/proprietary data if present. This is converted to + * standard DICOM fields whereever it makes sense to do so. + */ + str_ptr = acr_find_string(group_list, ACR_Manufacturer, ""); + if (strstr(str_ptr, "SIEMENS") != NULL || + strstr(str_ptr, "Siemens") != NULL) { + + group_list = add_siemens_info(group_list); + + /* Now copy proprietary fields into the correct places in the + * standard groups. + */ + group_list = copy_spi_to_acr(group_list); + + } + else if (strstr(str_ptr, "Philips") != NULL) { + group_list = add_philips_info(group_list); + } + else if (strstr(str_ptr, "GEMS") != NULL || + strstr(str_ptr, "GE MEDICAL") != NULL) { + group_list = add_gems_info(group_list); + } + return (group_list); +} + +/* ----------------------------- MNI Header ----------------------------------- + @NAME : free_info + @INPUT : gi_ptr + fi_ptr + 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 : + ---------------------------------------------------------------------------- */ + +static void +free_info(General_Info *gi_ptr, File_Info *fi_ptr, int num_files) +{ + Mri_Index imri; + + /* Free the general info pointers */ + for (imri = 0; imri < MRI_NDIMS; imri++) { + if (gi_ptr->indices[imri] != NULL) { + free(gi_ptr->indices[imri]); + } + if (gi_ptr->coordinates[imri] != NULL) { + free(gi_ptr->coordinates[imri]); + } + if (gi_ptr->widths[imri] != NULL) { + free(gi_ptr->widths[imri]); + } + } + + /* Free the group list */ + if (gi_ptr->group_list != NULL) { + acr_delete_group_list(gi_ptr->group_list); + } + + return; + +} + +/* ----------------------------- MNI Header ----------------------------------- + @NAME : search_list + @INPUT : value + list_ptr + list_length + start_index - 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 : + ---------------------------------------------------------------------------- */ +int +search_list(int value, const int *list_ptr, int list_length, int start_index) +{ + int index; + + /* If nothing on list, just return. */ + if (list_length <= 0) { + return (-1); + } + + /* If starting point is invalid, start at zero. + */ + if ((start_index >= list_length) || (start_index < 0)) { + start_index = 0; + } + + /* Loop over indices, wrapping at the end of the list */ + index = start_index; + do { + if (list_ptr[index] == value) { + return index; /* Found it. */ + } + index++; + if (index >= list_length) { + index = 0; + } + } while (index != start_index); + + return -1; /* Search failed. */ +} + +/* ----------------------------- MNI Header ----------------------------------- + @NAME : sort_dimensions + @INPUT : gi_ptr + @OUTPUT : gi_ptr + @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 : + ---------------------------------------------------------------------------- */ +static void +sort_dimensions(General_Info *gi_ptr) +{ + Mri_Index imri; + Sort_Element *sort_array; + int nvalues; + int i; + + /* Sort the dimensions, if needed. + */ + for (imri = 0; imri < MRI_NDIMS; imri++) { + /* Sort each dimension iff the size is greater than 1 and it is not + * a time dimension in an N4 mosaic. + */ + if (gi_ptr->cur_size[imri] <= 1 || + /* Don't sort on time for N4 mosaics (TODO: Why not??) + */ + ((G.file_type == N4DCM) && + (imri == TIME) && + (gi_ptr->num_slices_in_file > 1))) { + if (G.Debug >= HI_LOGGING) { + printf("Not sorting %s dimension\n", Mri_Names[imri]); + } + continue; + } + + if (G.Debug >= HI_LOGGING) { + printf("Sorting %s dimension\n", Mri_Names[imri]); + } + + /* Set up the array for sorting. + */ + nvalues = gi_ptr->cur_size[imri]; + sort_array = malloc(nvalues * sizeof(*sort_array)); + CHKMEM(sort_array); + for (i = 0; i < nvalues; i++) { + sort_array[i].identifier = gi_ptr->indices[imri][i]; + sort_array[i].original_index = i; + sort_array[i].value = gi_ptr->coordinates[imri][i]; + sort_array[i].width = gi_ptr->widths[imri][i]; + } + + /* Sort the array. + */ + qsort((void *) sort_array, (size_t) nvalues, sizeof(*sort_array), + dimension_sort_function); + + /* Copy the information back into the appropriate arrays + */ + for (i = 0; i < nvalues; i++) { + gi_ptr->indices[imri][i] = sort_array[i].identifier; + gi_ptr->coordinates[imri][i] = sort_array[i].value; + gi_ptr->widths[imri][i] = sort_array[i].width; + } + + if (G.Debug >= HI_LOGGING) { + /* Print out all of the information about this dimension. + */ + printf(" nvalues %d min %f max %f\n", + nvalues, + gi_ptr->coordinates[imri][0], + gi_ptr->coordinates[imri][nvalues - 1]); + for (i = 0; i < nvalues; i++) { + printf("%3d %6d %8.3f\n", + i, + gi_ptr->indices[imri][i], + gi_ptr->coordinates[imri][i]); + } + } + + /* Free the temporary array we used to sort the coordinate axis. + */ + free(sort_array); + + /* Now verify that the slice coordinate array looks sane. We + * don't complain about things like missing time slices, since + * many PET scanners produce irregular timings. + */ + if (nvalues >= 2 && imri == SLICE) { + double delta = (gi_ptr->coordinates[imri][1] - + gi_ptr->coordinates[imri][0]); + + for (i = 1; i < nvalues; i++) { + if (!fcmp(delta, + (gi_ptr->coordinates[imri][i] - + gi_ptr->coordinates[imri][i - 1]), + 1.0e-3)) { + printf("WARNING: Missing data for %s dimension\n", + Mri_Names[imri]); + printf(" slice # %d %.12f %.12f\n", + i, + delta, + (gi_ptr->coordinates[imri][i] - + gi_ptr->coordinates[imri][i - 1])); + } + } + } + + /* Update slice step and start. + */ + if (imri == SLICE) { + if (gi_ptr->coordinates[imri][0] != + gi_ptr->coordinates[imri][nvalues-1]) { + double dbl_tmp1; + double dbl_tmp2; + + dbl_tmp1 = gi_ptr->step[gi_ptr->slice_world]; + dbl_tmp2 = (gi_ptr->coordinates[imri][nvalues - 1] - + gi_ptr->coordinates[imri][0]) / + ((double) gi_ptr->cur_size[imri] - 1.0); + + /* OK, so now we have to figure out who wins the great + * battle to become the slice step value. If the value + * we have been assuming up until now is the same as the + * calculated value with only a sign change, or if the + * assumed value is the default (1.0), we adopt the + * calculated value. + */ + if (!fcmp(dbl_tmp1, dbl_tmp2, 2.0e-5)) { + printf("WARNING: calculated slice width (%.10f) disagrees with file's slice width (%.10f)\n", dbl_tmp2, dbl_tmp1); + if (dbl_tmp1 == 1.0) { + gi_ptr->step[gi_ptr->slice_world] = dbl_tmp2; + } + } + + } + gi_ptr->start[gi_ptr->slice_world] = gi_ptr->coordinates[imri][0]; + if (G.Debug >= HI_LOGGING) { + printf("Set slice %d step to %f, start to %f\n", + gi_ptr->slice_world, + gi_ptr->step[gi_ptr->slice_world], + gi_ptr->start[gi_ptr->slice_world]); + } + } /* If slice dimension */ + } /* for all mri 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 : + ---------------------------------------------------------------------------- */ +static 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; +} + +int +prot_find_string(Acr_Element elem_ptr, const char *name_str, char *value) +{ + static const char prot_head[] = "### ASCCONV BEGIN ###"; + long cur_offset; + long max_offset; + char *field_ptr; + int ix1, ix2; + + max_offset = elem_ptr->data_length - sizeof(prot_head); + + // Scan through the element containing the protocol, to find the + // ASCII dump of the MrProt structure. + // + for (cur_offset = 0; cur_offset < max_offset; cur_offset++) { + if (!memcmp(elem_ptr->data_pointer + cur_offset, + prot_head, sizeof(prot_head) - 1)) { + break; + } + } + + /* bail if we didn't find the protocol + */ + if (cur_offset == max_offset) { + return (0); + } + + field_ptr = strstr(elem_ptr->data_pointer + cur_offset, name_str); + if (field_ptr != NULL) { + sscanf(field_ptr, "%*s %*s %s", value); + ix1 = 0; + for (ix2 = 0; value[ix2] != '\0'; ix2++) { + if (value[ix2] != '"') { + value[ix1++] = value[ix2]; + } + } + } + else { + strcpy(value, "0"); + } + return (1); +} + +static char * +dump_protocol_text(Acr_Element elem_ptr) +{ + const char prot_head[] = "### ASCCONV BEGIN ###"; + const char prot_tail[] = "### ASCCONV END ###"; + char *output = malloc(elem_ptr->data_length); + int prot_found = FALSE; + long cur_offset; + long max_offset; + + CHKMEM(output); + + // scan throught the group containing the protocol, to find the + // ascii dump of the MrProt structure + max_offset = elem_ptr->data_length - sizeof (prot_head); + + for (cur_offset = 0; cur_offset < max_offset; cur_offset++) { + if (!memcmp(elem_ptr->data_pointer + cur_offset, + prot_head, sizeof(prot_head) - 1)) { + prot_found = TRUE; + break; + } + } + + if (prot_found) { + int ix1 = 0; + char *tmp_ptr = elem_ptr->data_pointer + cur_offset; + + for ( ; cur_offset < max_offset; cur_offset++) { + if (!memcmp(tmp_ptr, prot_tail, sizeof(prot_tail) - 1)) { + break; + } + if (*tmp_ptr != '"') { + output[ix1++] = *tmp_ptr; + } + tmp_ptr++; + } + output[ix1] = '\0'; + } + return (output); +} + +static int +mosaic_init(Acr_Group group_list, Mosaic_Info *mi_ptr, int load_image) +{ + int group_id, element_id; + int grid_size; + long new_image_size; + void *data; + Acr_Element element; + int i; + double pixel_spacing[2]; + double separation; + double RowColVec[6]; + double dircos[VOL_NDIMS][WORLD_NDIMS]; + char *str_tmp; + + if (G.Debug >= HI_LOGGING) { + printf("mosaic_init(%lx, %lx, %d)\n", + (unsigned long) group_list, (unsigned long) mi_ptr, + load_image); + } + + str_tmp = acr_find_string(group_list, SPI_Order_of_slices, ""); + if (!strncmp(str_tmp, "INTERLEAVED", 11)) { + mi_ptr->mosaic_seq = MOSAIC_SEQ_INTERLEAVED; + } + else if (!strncmp(str_tmp, "ASCENDING", 9)) { + mi_ptr->mosaic_seq = MOSAIC_SEQ_ASCENDING; + } + else if (!strncmp(str_tmp, "DESCENDING", 10)) { + mi_ptr->mosaic_seq = MOSAIC_SEQ_DESCENDING; + } + else { + mi_ptr->mosaic_seq = G.mosaic_seq; + } + + if (G.Debug >= HI_LOGGING) { + printf(" ordering is %s\n", str_tmp); + } + + /* Get some basic image information. + * big[0/1] is number of columns/rows in whole mosaic. + */ + mi_ptr->big[0] = acr_find_int(group_list, ACR_Columns, 1); + mi_ptr->big[1] = acr_find_int(group_list, ACR_Rows, 1); + mi_ptr->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) + */ + mi_ptr->size[0] = acr_find_short(group_list, EXT_Sub_image_columns, 1); + mi_ptr->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 + mi_ptr->grid[0] = mi_ptr->big[0] / mi_ptr->size[0]; + mi_ptr->grid[1] = mi_ptr->big[1] / mi_ptr->size[1]; + if ((mi_ptr->grid[0] < 1) || (mi_ptr->grid[0] < 1)) { + fprintf(stderr, "Grid too small: %d x %d\n", + mi_ptr->grid[0], mi_ptr->grid[1]); + exit(EXIT_FAILURE); + } + + // Check whether we need to do anything (1x1 grid may be the whole image) + grid_size = mi_ptr->grid[0] * mi_ptr->grid[1]; + if ((grid_size == 1) && + (mi_ptr->size[0] == mi_ptr->big[0]) && + (mi_ptr->size[1] == mi_ptr->big[1])) { + /* had to remove this as now ANY images acquired with + the mosaic sequence need special treatment */ + mi_ptr->packed = FALSE; + return 1; + } + + /* Update the number of image rows and columns + */ + acr_insert_short(&group_list, ACR_Rows, mi_ptr->size[1]); + acr_insert_short(&group_list, ACR_Columns, mi_ptr->size[0]); + + /* Get image image index info (number of slices in file) + */ + mi_ptr->slice_count = 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 + */ + mi_ptr->sub_images = mi_ptr->grid[0] * mi_ptr->grid[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)) { + if (G.Debug) { + printf("WARNING: Can't get pixel size element\n"); + } + } + + /* Get step between slices + */ + separation = acr_find_double(group_list, ACR_Slice_thickness, 0.0); + if (separation == 0.0) { + 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) + * TODO: This code (and other code in this function) could probably + * be broken out and made redundant with other code in the converter. + */ + element = acr_find_group_element(group_list, ACR_Image_orientation_patient); + if (element != NULL) { + acr_get_element_numeric_array(element, WORLD_NDIMS * 2, RowColVec); + + memcpy(dircos[VCOLUMN], RowColVec, sizeof(RowColVec[0]) * WORLD_NDIMS); + memcpy(dircos[VROW], &RowColVec[3], sizeof(RowColVec[0]) * WORLD_NDIMS); + + /* compute slice normal as cross product of row/column unit vectors + * (should check for unit length?) + */ + mi_ptr->normal[XCOORD] = + dircos[VCOLUMN][YCOORD] * dircos[VROW][ZCOORD] - + dircos[VCOLUMN][ZCOORD] * dircos[VROW][YCOORD]; + + mi_ptr->normal[YCOORD] = + dircos[VCOLUMN][ZCOORD] * dircos[VROW][XCOORD] - + dircos[VCOLUMN][XCOORD] * dircos[VROW][ZCOORD]; + + mi_ptr->normal[ZCOORD] = + dircos[VCOLUMN][XCOORD] * dircos[VROW][YCOORD] - + dircos[VCOLUMN][YCOORD] * dircos[VROW][XCOORD]; + } + else { + if (G.Debug) { + printf("WARNING: No image orientation found\n"); + } + } + + /* compute slice-to-slice step vector + */ + for (i = 0; i < WORLD_NDIMS; i++) { + mi_ptr->step[i] = separation * mi_ptr->normal[i]; + } + + /* Get position and correct to first slice + */ + element = acr_find_group_element(group_list, ACR_Image_position_patient); + if (element == NULL) { + element = acr_find_group_element(group_list, + ACR_Image_position_patient_old); + } + if (element != NULL) { + acr_get_element_numeric_array(element, WORLD_NDIMS, + mi_ptr->position); + } + else { + if (G.Debug) { + printf("WARNING: No image position found\n"); + } + mi_ptr->position[XCOORD] = mi_ptr->position[YCOORD] = + mi_ptr->position[ZCOORD] = 0.0; + } + + if (G.Debug >= HI_LOGGING) { + printf(" step %.3f %.3f %.3f position %.3f %.3f %.3f\n", + mi_ptr->step[0], + mi_ptr->step[1], + mi_ptr->step[2], + mi_ptr->position[0], + mi_ptr->position[1], + mi_ptr->position[2]); + } + + if (mi_ptr->mosaic_seq != MOSAIC_SEQ_INTERLEAVED) { + /* Numaris 4 mosaic correction: + * - position given is edge of huge slice constructed as if + * real slice was at center of mosaic + * - mi_ptr->big[0,1] are number of columns and rows of mosaic + * - mi_ptr->size[0,1] are number of columns and rows of sub-image + */ + + for (i = 0; i < WORLD_NDIMS; i++) { + /* Correct offset from mosaic Center + */ + mi_ptr->position[i] += (double) + ((dircos[VCOLUMN][i] * mi_ptr->big[0] * pixel_spacing[0]/2.0) + + (dircos[VROW][i] * mi_ptr->big[1] * pixel_spacing[1]/2)); + + /* Move from center to corner of slice + */ + mi_ptr->position[i] -= + ((dircos[VCOLUMN][i] * mi_ptr->size[0] * pixel_spacing[0]/2.0) + + (dircos[VROW][i] * mi_ptr->size[1] * pixel_spacing[1]/2.0)); + } + } + + if (G.Debug >= HI_LOGGING) { + printf(" corrected position %.3f %.3f %.3f\n", + mi_ptr->position[0], + mi_ptr->position[1], + mi_ptr->position[2]); + } + + if (load_image) { + + /* Steal the image element from the group list + */ + mi_ptr->big_image = acr_find_group_element(group_list, ACR_Pixel_data); + if (mi_ptr->big_image == NULL) { + fprintf(stderr, "Couldn't find an image\n"); + exit(EXIT_FAILURE); + } + group_id = acr_get_element_group(mi_ptr->big_image); + element_id = acr_get_element_element(mi_ptr->big_image); + acr_group_steal_element(acr_find_group(group_list, group_id), + mi_ptr->big_image); + + /* Add a small image + */ + new_image_size = + mi_ptr->size[0] * mi_ptr->size[1] * mi_ptr->pixel_size; + data = malloc(new_image_size); + CHKMEM(data); + mi_ptr->small_image = acr_create_element(group_id, element_id, + acr_get_element_vr(mi_ptr->big_image), + new_image_size, data); + acr_set_element_vr(mi_ptr->small_image, + acr_get_element_vr(mi_ptr->big_image)); + acr_set_element_byte_order(mi_ptr->small_image, + acr_get_element_byte_order(mi_ptr->big_image)); + acr_set_element_vr_encoding(mi_ptr->small_image, + acr_get_element_vr_encoding(mi_ptr->big_image)); + acr_insert_element_into_group_list(&group_list, mi_ptr->small_image); + } + + /* Return number of sub-images in this image */ + return mi_ptr->sub_images; +} + +static int +mosaic_modify_group_list(Acr_Group group_list, Mosaic_Info *mi_ptr, + int iimage, int load_image) +{ + int irow; + int idim; + int nbyte; + int isub; + int jsub; + char *new; + char *old; + long old_offset; + long new_offset; + double position[WORLD_NDIMS]; + string_t string; + int islice; + + if (G.Debug >= HI_LOGGING) { + printf("mosaic_modify_group_list(%lx, %lx, %d, %d)\n", + (unsigned long)group_list, (unsigned long)mi_ptr, + iimage, load_image); + } + + /* Figure out what to do based upon the mosaic sequencing. + */ + switch (mi_ptr->mosaic_seq) { + case MOSAIC_SEQ_INTERLEAVED: + /* For interleaved sequences, we have to map the odd slices to + * the range slice_count/2..slice_count-1 and the even slices + * from zero to slice_count/2-1 + */ + if (iimage & 1) { /* Odd?? */ + islice = (mi_ptr->slice_count / 2) + (iimage / 2); + } + else { + islice = iimage / 2; + } + break; + + default: + /* Otherwise, just use the image number without modification for + * ascending or unknown slice ordering. + */ + islice = iimage; + break; + } + + /* Check the image number + */ + if ((iimage < 0) || (iimage > mi_ptr->sub_images)) { + fprintf(stderr, "Invalid image number to send: %d of %d\n", + iimage, mi_ptr->sub_images); + exit(EXIT_FAILURE); + } + + /* Update the index + */ + acr_insert_numeric(&group_list, SPI_Current_slice_number, (double) iimage); + + /* Update the position + */ + for (idim = 0; idim < WORLD_NDIMS; idim++) { + position[idim] = mi_ptr->position[idim] + + (double) iimage * mi_ptr->step[idim]; + } + + /* If the sequence is descending, invert the slice coordinate. + * This involves subtracting the step * index from the mosaic + * slice position. + */ + if (mi_ptr->mosaic_seq == MOSAIC_SEQ_DESCENDING) { + position[ZCOORD] = mi_ptr->position[ZCOORD] - + (double) islice * mi_ptr->step[ZCOORD]; + } + + sprintf(string, "%.15g\\%.15g\\%.15g", + position[XCOORD], position[YCOORD], position[ZCOORD]); + acr_insert_string(&group_list, SPI_Image_position, string); + acr_insert_string(&group_list, ACR_Image_position_patient, string); + + if (G.Debug >= HI_LOGGING) { + printf(" position %s\n", string); + } + + if (load_image) { + /* Figure out the sub-image indices + */ + isub = islice % mi_ptr->grid[0]; + jsub = islice / mi_ptr->grid[0]; + + /* Get pointers + */ + old = acr_get_element_data(mi_ptr->big_image); + new = acr_get_element_data(mi_ptr->small_image); + + /* Copy the image + */ + nbyte = mi_ptr->size[0] * mi_ptr->pixel_size; + for (irow = 0; irow < mi_ptr->size[1]; irow++) { + old_offset = isub * mi_ptr->size[0] + + (jsub * mi_ptr->size[1] + irow) * mi_ptr->big[0]; + old_offset *= mi_ptr->pixel_size; + new_offset = (irow * mi_ptr->size[0]) * mi_ptr->pixel_size; + memcpy(&new[new_offset], &old[old_offset], nbyte); + } + + /* 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(mi_ptr->small_image, + acr_get_element_byte_order(mi_ptr->big_image)); + acr_set_element_vr_encoding(mi_ptr->small_image, + acr_get_element_vr_encoding(mi_ptr->big_image)); + } + return 1; + +} + +static void +mosaic_cleanup(Acr_Group group_list, Mosaic_Info *mi_ptr) +{ + if (mi_ptr->packed && mi_ptr->big_image != NULL) { + acr_delete_element(mi_ptr->big_image); + } +} +
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/dicom_to_minc.h @@ -0,0 +1,246 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : dicom_to_minc.h +@DESCRIPTION: Header file for dicom_to_minc.h +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : January 28, 1997 (Peter Neelin) +@MODIFIED : + * $Log: dicom_to_minc.h,v $ + * Revision 1.4.2.1 2005-05-12 21:16:47 bert + * Initial checkin + * + * Revision 1.4 2005/04/28 17:10:22 bert + * Added width information to General_Info and File_Info structures + * + * Revision 1.3 2005/04/20 23:14:32 bert + * Remove unnecessary fields, add copy_spi_to_acr() function definition + * + * Revision 1.2 2005/03/02 18:25:13 bert + * Add Mri_Names and Volume_Names + * + * Revision 1.1 2005/02/17 16:38:10 bert + * Initial checkin, revised DICOM to MINC converter + * + * Revision 1.1.1.1 2003/08/15 19:52:55 leili + * Leili's dicom server for sonata + * + * 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; + +extern const char *Mri_Names[MRI_NDIMS]; + +/* World dimensions */ +typedef enum { XCOORD = 0, YCOORD, ZCOORD, WORLD_NDIMS } World_Index; + +extern const char *World_Names[WORLD_NDIMS]; + +/* Volume dimensions */ +typedef enum { VSLICE = 0, VROW, VCOLUMN, VOL_NDIMS } Volume_Index; + +extern const char *Volume_Names[VOL_NDIMS]; + +/* Orientations */ +typedef enum {TRANSVERSE = 0, SAGITTAL, CORONAL, NUM_ORIENTATIONS} Orientation; + +/* Structure for general info about files */ +typedef struct { + int initialized; + double study_id; + int acq_id; /* Time of scan */ + int rec_num; + string_t image_type_string; + int nrows; + int ncolumns; + int default_index[MRI_NDIMS]; /* Index for dimensions with size == 1 */ + int cur_size[MRI_NDIMS]; /* Size of dimension across these files */ + int max_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 */ + double *widths[MRI_NDIMS]; /* Array indicating width 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; /* netCDF (and therefore MINC) datatype */ + int is_signed; /* TRUE of 2's compliment data */ + double pixel_min; + double pixel_max; + string_t 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 { + string_t name; + string_t identification; + string_t birth_date; + string_t age; + string_t sex; + string_t reg_date; + string_t reg_time; + double weight; + } patient; + struct { + string_t start_time; + string_t modality; + string_t manufacturer; + string_t model; + double field_value; + string_t software_version; + string_t serial_no; + string_t calibration_date; + string_t institution; + string_t station_id; + string_t referring_physician; + string_t performing_physician; + string_t operator; + string_t procedure; + string_t study_id; + string_t acquisition_id; + } study; + struct { + string_t scan_seq; + string_t protocol_name; + string_t receive_coil; + string_t 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 imaging_freq; + string_t imaged_nucl; + double win_center; + double win_width; + double num_phase_enc_steps; + double percent_sampling; + double percent_phase_fov; + double pixel_bandwidth; + string_t phase_enc_dir; + string_t mr_acq_type; + string_t image_type; + double sar; + string_t 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]; + double width[MRI_NDIMS]; /* Sample width along each MRI dimension */ +} File_Info; + +/* Structure for storing the actual image data */ +typedef struct { + int free; + int nrows; + int ncolumns; + unsigned short *data; +} Image_Data; + +/* function definitions */ +extern int dicom_to_minc(int num_files, + const char **file_list, + const char *minc_file, + int clobber, + const char *file_prefix, + char **output_file_name); +extern Acr_Group read_std_dicom(const char *filename, int max_group); +extern Acr_Group read_numa4_dicom(const char *filename, int max_group); +extern int search_list(int value, const int *list_ptr, int list_length, + int start_index); +extern Acr_Group copy_spi_to_acr(Acr_Group group_list); +
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/ext_element_defs.h @@ -0,0 +1,36 @@ +/* ----------------------------- 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 */ +/* bert- These appear to be completely nonstandard - I believe they were + * created by Peter and Rick to facilitate communication among the various + * pieces of Siemens Mosaic handling code. They do not represent any + * externally defined DICOM standard and therefore they may conflict with + * other manufacturer's proprietary fields. + */ +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/dcm2mnc/gems_element_defs.h @@ -0,0 +1,35 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : gems_element_defs.h +@DESCRIPTION: Element definitions for GE Medical Systems +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : March 14, 2005 +@MODIFIED : +@COPYRIGHT : + Copyright (C) 2005 Robert D. Vincent, + 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. +---------------------------------------------------------------------------- */ + +/* This information was derived from the publically available "Advance(TM) + * 4.05 Conformance Statement for DICOM V3.0" from GE Medical Systems, + * copyright 2000 by GE Medical Systems. + */ + +GLOBAL_ELEMENT(GEMS_Acqu_private_creator_id, 0x0019, 0x0010, SH); +GLOBAL_ELEMENT(GEMS_Frame_acq_start , 0x0019, 0x106c, DT); +GLOBAL_ELEMENT(GEMS_Frame_acq_duration , 0x0019, 0x106d, SL); +GLOBAL_ELEMENT(GEMS_Image_slice_number , 0x0019, 0x10a6, SL); +GLOBAL_ELEMENT(GEMS_Fast_phases , 0x0019, 0x10f2, SS); + +GLOBAL_ELEMENT(GEMS_Sers_private_creator_id, 0x0025, 0x0010, SH); +GLOBAL_ELEMENT(GEMS_Images_in_series , 0x0025, 0x1007, SL); +
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/minc_file.c @@ -0,0 +1,1088 @@ +/* ----------------------------- 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.6.2.1 2005-05-12 21:16:48 bert + * Initial checkin + * + * Revision 1.6 2005/04/29 23:09:06 bert + * Write sample-width information to file for irregular time dimensions + * + * Revision 1.5 2005/04/20 23:15:06 bert + * Don't save attributes that are no longer set + * + * Revision 1.4 2005/04/18 16:21:42 bert + * Add debugging information for intensity scaling + * + * Revision 1.3 2005/03/13 19:34:21 bert + * Minor change to avoid core dump with strange files + * + * Revision 1.2 2005/03/03 18:59:15 bert + * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) + * + * Revision 1.1 2005/02/17 16:38:10 bert + * Initial checkin, revised DICOM to MINC converter + * + * Revision 1.1.1.1 2003/08/15 19:52:55 leili + * Leili's dicom server for sonata + * + * 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. +---------------------------------------------------------------------------- */ +static const char rcsid[] = "$Header: /private-cvsroot/minc/conversion/dcm2mnc/minc_file.c,v 1.6.2.1 2005-05-12 21:16:48 bert Exp $"; + +#include "dcm2mnc.h" + +#include <sys/stat.h> +#include <ctype.h> + +/* Define mri dimension names */ +static char *mri_dim_names[] = { + NULL, "echo_time", MItime, "phase_number", "chemical_shift", NULL}; + +/* ----------------------------- 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 + ---------------------------------------------------------------------------- */ +int +create_minc_file(const char *minc_file, + int clobber, + General_Info *general_info, + const char *file_prefix, + char **output_file_name, + Loop_Type loop_type) +{ + static char temp_name[256]; + char patient_name[256]; + char reg_time[256]; + char temp_str[256]; + const 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 (G.Name[0] != '\0') { + strcpy(patient_name, G.Name); + } + else { + string_to_filename(general_info->patient.name, patient_name, + sizeof(patient_name)); + } + + if (strlen(patient_name) == 0) { + strcpy(patient_name, "no_name"); + } + + /******************************************/ + /* 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) { + 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->cur_size[imri] < general_info->max_size[imri]) && + (general_info->cur_size[imri] == 1)) { + sprintf(scan_label[imri], "%s%d", scan_prefix[imri], + general_info->default_index[imri]); + } + else { + strcpy(scan_label[imri], ""); + } + } + + /* rhoge: add session directory to prefix */ + + strcpy(full_path, file_prefix); + + sprintf(temp_name, "%s_%s_%s/", + patient_name, + 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) { + + sprintf(general_info->study.acquisition_id, "%06d", + general_info->acq_id); + + } + + /* Create file name */ + /* changed by leili, omitted the scanner info, changed - to _ */ + sprintf(temp_name, "%s%s_%s_%s_%s%s%s%s%s%s_mri.mnc", + full_path, + patient_name, + 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 (G.Debug) { + printf("MINC file name: %s\n", filename); + printf("File prefix: %s\n", full_path); + printf("Patient name: %s\n", patient_name); + printf("Study ID: %s\n", + general_info->study.study_id); + printf("Acquisition ID: %s\n", + general_info->study.acquisition_id); + printf("Registration date: %s\n", + general_info->patient.reg_date); + printf("Registration time: %s\n", + general_info->patient.reg_time); + printf("Rows %d columns %d slices %d/%d\n", + general_info->nrows, + general_info->ncolumns, + general_info->cur_size[SLICE], + general_info->max_size[SLICE]); + if (general_info->max_size[TIME] != 1) { + printf("Time axis length: %d/%d\n", + general_info->cur_size[TIME], + general_info->max_size[TIME]); + } + } + } + + /* create the session directory if none exists */ + + if (mkdir(full_path, 0777) && G.Debug) { + printf("Directory %s exists...\n", full_path); + } + + /* Set output file name */ + if (output_file_name != NULL) { + *output_file_name = (char *) filename; + } + + /* Set the clobber value */ + if (clobber) + minc_clobber = NC_CLOBBER; + else + minc_clobber = NC_NOCLOBBER; + + /* Create the file */ + mincid = micreate((char *) 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 */ + ncsetfill(mincid, NC_NOFILL); + if (ncendef(mincid) == MI_ERROR) { + return MI_ERROR; + } + + /* Create the icv */ + icvid = miicv_create(); + + /* Set the type and range */ + miicv_setint(icvid, MI_ICV_TYPE, NC_SHORT); + if (general_info->is_signed) + miicv_setstr(icvid, MI_ICV_SIGN, MI_SIGNED); + else + miicv_setstr(icvid, MI_ICV_SIGN, MI_UNSIGNED); + miicv_setdbl(icvid, MI_ICV_VALID_MIN, general_info->pixel_min); + miicv_setdbl(icvid, MI_ICV_VALID_MAX, general_info->pixel_max); + + /* Attach the icv */ + miicv_attach(icvid, mincid, ncvarid(mincid, MIimage)); + + return icvid; + +} + +/* ----------------------------- MNI Header ----------------------------------- + @NAME : minc_set_spacing + @INPUT : mincid + varid + imri + gi_ptr + @OUTPUT : (nothing) + @RETURNS : (nothing) + @DESCRIPTION: This function checks the given MRI dimension (most typically + the TIME dimension) to see if it has a "regular" structure. + If so, the MINC file is updated accordingly. If not, the + function creates a "xxxx-width" variable corresponding to the + dimension which will contain the width information from this + dimension. NOTE: At present only the time-width variable + is defined by MINC. + @METHOD : + @GLOBALS : + CALLS : + @CREATED : April 27, 2005 (Bert Vincent) + @MODIFIED : + ---------------------------------------------------------------------------- + */ + +static void +minc_set_spacing(int mincid, int varid, Mri_Index imri, General_Info *gi_ptr) +{ + double sum; /* Sum of differences for computing average */ + double avg; /* Average */ + double diff; /* Difference between adjacent coordinates */ + double step; /* Step size from widths */ + int regular; /* TRUE if dimension is regular */ + int index; /* Loop/array index */ + long length; /* Length of this dimension (> 1) */ + + regular = TRUE; + + length = gi_ptr->cur_size[imri]; + + /* First, see if the widths were set, and if so, if they are consistent. + */ + for (index = 1; index < length; index++) { + if (gi_ptr->widths[imri][0] != gi_ptr->widths[imri][index]) { + regular = FALSE; + break; + } + } + + /* OK, now set the step value according to the widths, if possible. + */ + if (regular) { + step = gi_ptr->widths[imri][0]; + + /* Now calculate the average value for the coordinate spacing. + */ + sum = 0.0; + for (index = 1; index < length; index++) { + sum += gi_ptr->coordinates[imri][index] - + gi_ptr->coordinates[imri][index-1]; + } + + avg = sum / length; /* compute mean */ + + if (step != 0.0 && avg != step) { + printf("WARNING: Sample width %f not equal to average delta %f\n", + step, avg); + } + + step = avg; /* Use the average anyway. */ + + /* Check for uniformity of spacing */ + + for (index = 1; index < length; index++) { + + /* Calculate the difference between two adjacent locations, + * less the average step value. + */ + + diff = gi_ptr->coordinates[imri][index] - + gi_ptr->coordinates[imri][index - 1] - step; + + if (diff < 0.0) { + diff = -diff; + } + if (step != 0.0) { + diff /= step; + } + if (diff > COORDINATE_EPSILON) { + regular = FALSE; + break; + } + } + } + else { + /* We have widths provided for us, so use them to calculate the + * average step size. + */ + for (index = 0; index < length; index++) { + sum += gi_ptr->widths[imri][index]; + } + step = sum / length; + } + + /* + * Write the step value. According to the MINC specifications, it is + * always valid to store a step value even for irregular dimensions. + * The step should always equal the average spacing of the dimension. + */ + miattputdbl(mincid, varid, MIstep, step); + + if (regular) { + miattputstr(mincid, varid, MIspacing, MI_REGULAR); + } + else { + miattputstr(mincid, varid, MIspacing, MI_IRREGULAR); + + /* Create the <dimension-name>-width variable. At present, this + * is only a valid operation if the dimension in question is the + * time dimension. MINC does not define a width variable for any of + * the other, non-standard dimensions. So for now this code is + * very much a special case. + */ + if (imri == TIME) { + int dimid; + + dimid = ncdimid(mincid, MItime); + if (dimid >= 0) { + micreate_std_variable(mincid, MItime_width, NC_DOUBLE, + 1, &dimid); + } + } + } +} + +/* ----------------------------- 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 : + ---------------------------------------------------------------------------- */ +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; + + /* 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->cur_size[TIME]; + if (general_info->cur_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]); + miattputstr(mincid, varid, MIspacing, MI_REGULAR); + 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) */ + + minc_set_spacing(mincid, varid, TIME, general_info); + } else { + + /* assume ACQ loop and use TR for time step */ + miattputdbl(mincid, varid, MIstep, + general_info->acq.rep_time); + } + miattputdbl(mincid, varid, MIstart,0); + + general_info->image_index[TIME] = ndims; + ndims++; + } + + } else { /* NORMAL CODE */ + + dimsize = general_info->cur_size[imri]; + if (general_info->cur_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]); + miattputstr(mincid, varid, MIunits, "s"); + minc_set_spacing(mincid, varid, TIME, general_info); + } + else if (imri == ECHO) { + varid = ncvardef(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); + miattputstr(mincid, varid, MIvartype, MI_DIMENSION); + miattputstr(mincid, varid, MIspacing, MI_IRREGULAR); + 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->cur_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; + default: + fprintf(stderr, "Should not happen!!"); + exit(-1); + } + 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->cur_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) + miattputstr(mincid, varid, MIspacing, MI_REGULAR); + } + else + varid = micreate_std_variable(mincid, dimname, NC_LONG, 0, NULL); + miattputdbl(mincid, varid, MIstep, + general_info->step[iworld]); + miattputdbl(mincid, varid, MIstart, + general_info->start[iworld]); + miattputstr(mincid, varid, MIspacetype, MI_NATIVE); + 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) + miattputstr(mincid, imgid, MIsigntype, MI_SIGNED); + else + miattputstr(mincid, imgid, MIsigntype, MI_UNSIGNED); + valid_range[0] = general_info->pixel_min; + valid_range[1] = general_info->pixel_max; + ncattput(mincid, imgid, MIvalid_range, NC_DOUBLE, 2, valid_range); + 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) + miattputstr(mincid, varid, MIunits, general_info->units); + varid = micreate_std_variable(mincid, MIimagemax, NC_DOUBLE, ndims-2, dim); + if (strlen(general_info->units) > 0) + 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 (G.Anon) { + miattputstr(mincid, varid, MIfull_name, "anonymous"); + } + else { + miattputstr(mincid, varid, MIfull_name, + general_info->patient.name); + } + } + if (strlen(general_info->patient.identification) > 0) + miattputstr(mincid, varid, MIidentification, + general_info->patient.identification); + if (strlen(general_info->patient.birth_date) > 0) + miattputstr(mincid, varid, MIbirthdate, + general_info->patient.birth_date); + if (strlen(general_info->patient.age) > 0) + miattputstr(mincid, varid, "age", + general_info->patient.age); + if (strlen(general_info->patient.sex) > 0) + miattputstr(mincid, varid, MIsex, + general_info->patient.sex); + if (general_info->patient.weight != -DBL_MAX) + 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) + miattputstr(mincid, varid, "start_date", + general_info->patient.reg_date); + if (strlen(general_info->patient.reg_time) > 0) + miattputstr(mincid, varid, "start_time", + general_info->patient.reg_time); + if (strlen(general_info->study.modality) > 0) + miattputstr(mincid, varid, MImodality, + general_info->study.modality); + if (strlen(general_info->study.manufacturer) > 0) + miattputstr(mincid, varid, "manufacturer", + general_info->study.manufacturer); + if (strlen(general_info->study.model) > 0) + miattputstr(mincid, varid, "model", + general_info->study.model); + if (general_info->study.field_value != -DBL_MAX) + miattputdbl(mincid, varid, "field_value", + general_info->study.field_value); + if (strlen(general_info->study.software_version) > 0) + miattputstr(mincid, varid, "software_version", + general_info->study.software_version); + if (strlen(general_info->study.serial_no) > 0) + miattputstr(mincid, varid, "serial_no", + general_info->study.serial_no); + if (strlen(general_info->study.calibration_date) > 0) + miattputstr(mincid, varid, "calibration_date", + general_info->study.calibration_date); + if (strlen(general_info->study.institution) > 0) + miattputstr(mincid, varid, MIinstitution, + general_info->study.institution); + if (strlen(general_info->study.station_id) > 0) + miattputstr(mincid, varid, MIstation_id, + general_info->study.station_id); + if (strlen(general_info->study.referring_physician) > 0) + miattputstr(mincid, varid, MIreferring_physician, + general_info->study.referring_physician); + + if (strlen(general_info->study.performing_physician) > 0) + miattputstr(mincid, varid, "performing_physician", + general_info->study.referring_physician); + if (strlen(general_info->study.operator) > 0) + miattputstr(mincid, varid, "operator", + general_info->study.operator); + + if (strlen(general_info->study.procedure) > 0) + miattputstr(mincid, varid, MIprocedure, + general_info->study.procedure); + if (strlen(general_info->study.study_id) > 0) + 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) + miattputstr(mincid, varid, "acquisition_id", + general_info->study.acquisition_id); + if (strlen(general_info->study.start_time) > 0) + miattputstr(mincid, varid, MIstart_time, + general_info->study.start_time); + + if (strlen(general_info->acq.scan_seq) > 0) + miattputstr(mincid, varid, MIscanning_sequence, + general_info->acq.scan_seq); + + if (strlen(general_info->acq.protocol_name) > 0) + miattputstr(mincid, varid, "protocol_name", + general_info->acq.protocol_name); + if (strlen(general_info->acq.receive_coil) > 0) + miattputstr(mincid, varid, "receive_coil", + general_info->acq.receive_coil); + if (strlen(general_info->acq.transmit_coil) > 0) + miattputstr(mincid, varid, "transmit_coil", + general_info->acq.transmit_coil); + + if (general_info->acq.rep_time != -DBL_MAX) + miattputdbl(mincid, varid, MIrepetition_time, + general_info->acq.rep_time); + if ((general_info->acq.echo_time != -DBL_MAX) && + (general_info->cur_size[ECHO] <= 1)) + miattputdbl(mincid, varid, MIecho_time, + general_info->acq.echo_time); + if (general_info->acq.echo_number != -DBL_MAX) + miattputdbl(mincid, varid, "echo_number", + general_info->acq.echo_number); + if (general_info->acq.inv_time != -DBL_MAX) + miattputdbl(mincid, varid, MIinversion_time, + general_info->acq.inv_time); + if (general_info->acq.flip_angle != -DBL_MAX) + miattputdbl(mincid, varid, "flip_angle", + general_info->acq.flip_angle); + if (general_info->acq.slice_thickness != -DBL_MAX) + miattputdbl(mincid, varid, "slice_thickness", + general_info->acq.slice_thickness); + if (general_info->acq.num_slices != -DBL_MAX) + miattputdbl(mincid, varid, "num_slices", + general_info->acq.num_slices); + if (general_info->acq.b_value != -DBL_MAX) + 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) + miattputdbl(mincid, varid, "num_dyn_scans", + general_info->acq.num_dyn_scans); + + if (general_info->acq.num_avg != -DBL_MAX) + miattputdbl(mincid, varid, MInum_averages, + general_info->acq.num_avg); + + if (general_info->acq.imaging_freq != -DBL_MAX) + miattputdbl(mincid, varid, MIimaging_frequency, + general_info->acq.imaging_freq); + if (strlen(general_info->acq.imaged_nucl) > 0) + miattputstr(mincid, varid, MIimaged_nucleus, + general_info->acq.imaged_nucl); + + if (general_info->acq.win_center != -DBL_MAX) + miattputdbl(mincid, varid, "window_center", + general_info->acq.win_center); + + if (general_info->acq.win_width != -DBL_MAX) + miattputdbl(mincid, varid, "window_width", + general_info->acq.win_width); + + if (general_info->acq.num_phase_enc_steps != -DBL_MAX) + miattputdbl(mincid, varid, "num_phase_enc_steps", + general_info->acq.num_phase_enc_steps); + if (general_info->acq.percent_sampling != -DBL_MAX) + miattputdbl(mincid, varid, "percent_sampling", + general_info->acq.percent_sampling); + if (general_info->acq.percent_phase_fov != -DBL_MAX) + miattputdbl(mincid, varid, "percent_phase_fov", + general_info->acq.percent_phase_fov); + if (general_info->acq.pixel_bandwidth != -DBL_MAX) + miattputdbl(mincid, varid, "pixel_bandwidth", + general_info->acq.pixel_bandwidth); + if (strlen(general_info->acq.phase_enc_dir) > 0) + miattputstr(mincid, varid, "phase_enc_dir", + general_info->acq.phase_enc_dir); + if (general_info->acq.sar != -DBL_MAX) + miattputdbl(mincid, varid, "SAR", + general_info->acq.sar); + if (strlen(general_info->acq.mr_acq_type) > 0) + miattputstr(mincid, varid, "mr_acq_type", + general_info->acq.mr_acq_type); + if (strlen(general_info->acq.image_type) > 0) + miattputstr(mincid, varid, "image_type", + general_info->acq.image_type); + + if (strlen(general_info->acq.comments) > 0) + miattputstr(mincid, varid, MIcomments, + general_info->acq.comments); + + // this is Siemens Numaris 4 specific! + if (strlen(general_info->acq.MrProt) > 0) + miattputstr(mincid, varid, "MrProt_dump", + general_info->acq.MrProt); + + /* Create the dicom info variable */ + varid = ncvardef(mincid, "dicominfo", NC_LONG, 0, NULL); + miattputstr(mincid, varid, MIvartype, MI_GROUP); + miattputstr(mincid, varid, MIvarid, + "MNI DICOM information variable"); + miadd_child(mincid, ncvarid(mincid, MIrootvariable), varid); + if (strlen(general_info->image_type_string) > 0) + miattputstr(mincid, varid, "image_type", + general_info->image_type_string); + miattputdbl(mincid, varid, "window_min", general_info->window_min); + 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); + miattputstr(mincid, dicomvar, MIvartype, MI_GROUP); + miattputstr(mincid, dicomvar, MIvarid, "MNI DICOM variable"); + miadd_child(mincid, ncvarid(mincid, MIrootvariable), dicomvar); + while (cur_group != NULL) { + + /* Create variable for group */ + sprintf(name, "dicom_0x%04x", acr_get_group_group(cur_group)); + varid = ncvardef(mincid, name, NC_LONG, 0, NULL); + miattputstr(mincid, varid, MIvartype, MI_GROUP); + miattputstr(mincid, varid, MIvarid, "MNI DICOM variable"); + miadd_child(mincid, dicomvar, varid); + + /* Loop through elements of group */ + cur_element = acr_get_group_element_list(cur_group); + while (cur_element != NULL) { + 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); + if (data == NULL) { + length = 0; + } + 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 (G.minc_history != NULL) { + miattputstr(mincid, NC_GLOBAL, MIhistory, G.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 : + ---------------------------------------------------------------------------- */ +void +save_minc_image(int icvid, General_Info *general_info, + File_Info *file_info, Image_Data *image) +{ + int mincid; + 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 */ + miicv_inqint(icvid, MI_ICV_CDFID, &mincid); + + /* 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->cur_size[imri] > 1) { + array_index = search_list(file_info->index[imri], + general_info->indices[imri], + general_info->cur_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; + } + 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->cur_size[TIME] > 1) { + mivarput1(mincid, ncvarid(mincid, mri_dim_names[TIME]), + &start[general_info->image_index[TIME]], + NC_DOUBLE, NULL, &file_info->coordinate[TIME]); + + /* If width information is present, save it to the appropriate + * location in the time-width variable. + */ + if (file_info->width[TIME] != 0.0) { + int ncopts_prev; + int varid; + + /* Since it is possible for width information to be present + * in circumstances where we do not want to save it, the + * time-width variable may not even exist when we get here. + * In order to avoid a nasty and unnecessary error message + * we have to disable netCDF errors here. + */ + ncopts_prev = ncopts; + ncopts = 0; + varid = ncvarid(mincid, MItime_width); /* Get the variable id */ + ncopts = ncopts_prev; + + /* If the variable was created, update it as needed. + */ + if (varid >= 0) { + mivarput1(mincid, varid, + &start[general_info->image_index[TIME]], + NC_DOUBLE, NULL, &file_info->width[TIME]); + } + } + } + + /* Write out echo time of slice, if needed */ + if (general_info->cur_size[ECHO] > 1) { + 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 + */ + if (G.Debug >= HI_LOGGING) { + printf("global range: %.2f %.2f file range: %.2f %.2f pmax: %u\n", + general_info->pixel_min, + general_info->pixel_max, + file_info->slice_min, + 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 */ + mivarput1(mincid, ncvarid(mincid, MIimagemin), start, NC_DOUBLE, + NULL, &minimum); + mivarput1(mincid, ncvarid(mincid, MIimagemax), start, NC_DOUBLE, + NULL, &maximum); + + /* Write out the image */ + 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 : + ---------------------------------------------------------------------------- */ +void +close_minc_file(int icvid) +{ + int mincid; + + /* Get the minc file id */ + miicv_inqint(icvid, MI_ICV_CDFID, &mincid); + + /* Write out the complete attribute */ + miattputstr(mincid, ncvarid(mincid, MIimage), MIcomplete, MI_TRUE); + + /* Close the file */ + miclose(mincid); + + miicv_free(icvid); +}
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/minc_file.h @@ -0,0 +1,11 @@ +extern int create_minc_file(const char *minc_file, + int clobber, + General_Info *general_info, + const char *file_prefix, + char **output_file_name, + Loop_Type loop_type); +extern void setup_minc_variables(int mincid, General_Info *general_info, + Loop_Type loop_type); +extern void save_minc_image(int icvid, General_Info *general_info, + File_Info *file_info, Image_Data *image); +extern void close_minc_file(int icvid);
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/numaris.txt @@ -0,0 +1,179 @@ +Some Siemens-specific field definitions I found floating around the net. + +// Identifying group +GROUP_LENGTH 0x0009 0x0000 UL +PRIVATE_CREATOR 0x0009 0x0010 LO +PRIVATE_CREATOR 0x0009 0x0012 LO +PRIVATE_CREATOR 0x0009 0x0013 LO +COMMENTS 0x0009 0x1010 LO +UNIQUE_IDENTIFIER 0x0009 0x1015 LO +DATA_OBJECT_TYPE 0x0009 0x1040 US +DATA_OBJECT_SUBTYPE 0x0009 0x1041 SH +STORAGE_MODE 0x0009 0x1210 CS +EVALUATION_MASK_IMAGE 0x0009 0x1212 UL +TABLE_ZERO_DATE 0x0009 0x1226 DA +TABLE_ZERO_TIME 0x0009 0x1227 TM +CPU_IDENTIFICATION_LABEL 0x0009 0x1316 LO +HEADER_VERSION 0x0009 0x1320 SH + +// Patient group +GROUP_LENGTH 0x0011 0x0000 UL +PRIVATE_CREATOR 0x0011 0x0010 LO +PRIVATE_CREATOR 0x0011 0x0011 LO +ORGAN 0x0011 0x1010 LO +REGISTRATION_DATE 0x0011 0x1110 DA +REGISTRATION_TIME 0x0011 0x1111 TM +USED_PATIENT_WEIGHT 0x0011 0x1123 DS + +// Acquisition group +GROUP_LENGTH 0x0019 0x0000 UL +PRIVATE_CREATOR 0x0019 0x0010 LO +PRIVATE_CREATOR 0x0019 0x0012 LO +PRIVATE_CREATOR 0x0019 0x0014 LO +PRIVATE_CREATOR 0x0019 0x0015 LO +NET_FREQUENCY 0x0019 0x1010 IS +MEASUREMENT_MODE 0x0019 0x1020 CS +CALCULATION_MODE 0x0019 0x1030 CS +NOISE_LEVEL 0x0019 0x1050 IS +NUMBER_OF_DATABYTES 0x0019 0x1060 IS +AC_SCALE_VECTOR 0x0019 0x1070 DS +AC_ELEMENT_CONNECTED 0x0019 0x1080 LO +TOTAL_MEASUREMENT_TIME_NOM 0x0019 0x1210 DS +TOTAL_MEASUREMENT_TIME_CUR 0x0019 0x1211 DS +START_DELAY_TIME 0x0019 0x1212 DS +DWELL_TIME 0x0019 0x1213 DS +NUMBER_OF_PHASES 0x0019 0x1214 IS +SEQUENCE_CONTROL_MASK 0x0019 0x1216 UL +MEASUREMENT_STATUS_MASK 0x0019 0x1218 UL +FOURIER_LINES_NOM 0x0019 0x1220 IS +FOURIER_LINES_CUR 0x0019 0x1221 IS +FOURIER_LINES_AFTER_ZERO 0x0019 0x1226 IS +FIRST_FOURIER_LINE 0x0019 0x1228 IS +ACQUISITION_COLUMNS 0x0019 0x1230 IS +RECONSTRUCTION_COLUMNS 0x0019 0x1231 IS +AC_ELEMENT_NUMBER 0x0019 0x1240 IS +AC_ELEMENT_SELECT_MASK 0x0019 0x1241 UL +AC_ELEMENT_DATA_MASK 0x0019 0x1242 UL +AC_ELEMENT_TO_ADC_CONNECT 0x0019 0x1243 IS +AC_ADC_PAIR_NUMBER 0x0019 0x1245 IS +AC_COMBINATION_MASK 0x0019 0x1246 UL +NUMBER_OF_AVERAGES 0x0019 0x1250 IS +FLIP_ANGLE 0x0019 0x1260 DS +NUMBER_OF_PRESCANS 0x0019 0x1270 IS +RAW_DATA_FILTER_TYPE 0x0019 0x1281 CS +IMAGE_DATA_FILTER_TYPE 0x0019 0x1283 CS +PHASE_CORRECTION_FILTER_TYPE 0X0019 0x1285 CS +NORMALIZATION_FILTER_TYPE 0x0019 0x1287 CS +SATURATION_REGIONS 0x0019 0x1290 IS +IMAGE_ROTATION_ANGLE 0x0019 0x1294 DS +COIL_ID_MASK 0x0019 0x1296 UL +COIL_CLASS_MASK 0x0019 0x1297 UL +COIL_POSITION 0x0019 0x1298 DS +MAGNETIC_FIELD_STRENGTH 0x0019 0x1412 DS +ADC_VOLTAGE 0x0019 0x1414 DS +ADC_OFFSET 0x0019 0x1416 DS +TRANSMITTER_AMPLITUDE 0x0019 0x1420 DS +NUMBER_OF_TRANS_AMPS 0x0019 0x1421 IS +TRANSMITTER_CALIBRATION 0x0019 0x1424 DS +RECEIVER_AMPLIFIER_GAIN 0x0019 0x1451 DS +RECEIVER_PREAMP_GAIN 0x0019 0x1452 DS +PHASE_GRADIENT_AMPLITUDE 0x0019 0x1470 DS +READOUT_GRADIENT_AMPLITUDE 0x0019 0x1471 DS +SELECTION_GRADIENT_AMPLITUDE 0x0019 0x1472 DS +GRADIENT_DELAY_TIME 0x0019 0x1480 DS +TOTAL_GRADIENT_DELAY_TIME 0x0019 0x1482 DS +SENSITIVITY_CORRECTION_LABEL 0x0019 0x1490 LO +RF_WATCHDOG_MASK 0x0019 0x14a0 IS +RF_POWER_ERROR_INDICATOR 0x0019 0x14a2 DS +SPECIFIC_ABSORPTION_RATE 0x0019 0x14a5 DS +SPECIFIC_ENERGY_DOSE 0x0019 0x14a6 DS +ADJUSTMENT_STATUS_MASK 0x0019 0x14b0 UL +FLOW_SENSITIVITY 0x0019 0x14d1 DS +CALCULATION_SUBMODE 0x0019 0x14d2 CS +FIELD_OF_VIEW_RATIO 0x0019 0x14d3 DS +BASE_RAW_MATRIX_SIZE 0x0019 0x14d4 IS +2D_PHASE_OVERSAMPLING_LINES 0x0019 0x14d5 IS +2D_PHASE_OVERSAMPLING_PART 0x0019 0x14d6 IS +ECHO_LINE_POSITION 0x0019 0x14d7 IS +ECHO_COLUMN_POSITION 0x0019 0x14d8 IS +LINES_PER_SEGMENT 0x0019 0x14d9 IS +PHASE_CODING_DIRECTION 0x0019 0x14da CS +PARAMETER_FILE_NAME 0x0019 0x1510 LO +SEQUENCE_FILE_NAME 0x0019 0x1511 LO +SEQUENCE_FILE_OWNER 0x0019 0x1512 LO +SEQUENCE_DESCRIPTION 0x0019 0x1513 LO + +// Relationship group +GROUP_LENGTH 0x0021 0x0000 UL +PRIVATE_CREATOR 0x0021 0x0010 LO +PRIVATE_CREATOR 0x0021 0x0011 LO +PRIVATE_CREATOR 0x0021 0x0013 LO +PRIVATE_CREATOR 0x0021 0x0023 LO +ZOOM 0x0021 0x1010 DS +TARGET 0x0021 0x1011 DS +ROI_MASK 0x0021 0x1020 US +FIELD_OF_VIEW 0x0021 0x1120 DS +IMAGE_MAGNIFICATION_FACTOR 0x0021 0x1122 DS +IMAGE_SCROLL_OFFSET 0x0021 0x1124 DS +IMAGE_PIXEL_OFFSET 0x0021 0x1126 IS +VIEW_DIRECTION 0x0021 0x1130 CS +REST_DIRECTION 0x0021 0x1132 CS +IMAGE_POSITION 0x0021 0x1160 DS +IMAGE_NORMAL 0x0021 0x1161 DS +IMAGE_DISTANCE 0x0021 0x1163 DS +IMAGE_POSITIONING_HISTORY_MASK 0x0021 0x1165 US +IMAGE_ROW 0x0021 0x116a DS +IMAGE_COLUMN 0x0021 0x116b DS +PATIENT_ORIENTATION_SET_1 0x0021 0x1170 CS +PATIENT_ORIENTATION_SET_2 0x0021 0x1171 CS +STUDY_TYPE 0x0021 0x1182 SH +PHASE_COR_ROW_SEQ 0x0021 0x1320 IS +PHASE_COR_COL_SEQ 0x0021 0x1321 IS +PHASE_CORRECTION_ROWS 0x0021 0x1322 IS +PHASE_CORRECTION_COLUMNS 0x0021 0x1324 IS +3D_RAW_PARTITIONS_NOMINAL 0x0021 0x1330 IS +3D_RAW_PARTITIONS_CURRENT 0x0021 0x1331 IS +IMAGE_PARTITIONS 0x0021 0x1334 IS +PARTITION_NUMBER 0x0021 0x1336 IS +SLAB_THICKNESS 0x0021 0x1339 DS +NUMBER_OF_SLICES_NOMINAL 0x0021 0x1340 IS +NUMBER_OF_SLICES_CURRENT 0x0021 0x1341 IS +CURRENT_SLICE_NUMBER 0x0021 0x1342 IS +CURRENT_GROUP_NUMBER 0x0021 0x1343 IS +CURRENT_SLICE_DISTANCE_FACTOR 0x0021 0x1344 DS +ORDER_OF_SLICES 0x0021 0x134f ST +SIGNAL_MASK 0x0021 0x1350 IS +EFFECTIVE_REPETITION_TIME 0x0021 0x1356 DS +NUMBER_OF_ECHOES 0x0021 0x1370 IS +SEQUENCE_TYPE 0x0021 0x2300 CS +VECTOR_SIZE_ORIGINAL 0x0021 0x2301 IS +VECTOR_SIZE_EXTENDED 0x0021 0x2302 IS +ACQUIRED_SPECTRAL_RANGE 0x0021 0x2303 DS +VOI_POSITION 0x0021 0x2304 DS +VOI_SIZE 0x0021 0x2305 DS +CSI_MATRIX_SIZE_ORIGINAL 0x0021 0x2306 IS +CSI_MATRIX_SIZE_EXTENDED 0x0021 0x2307 IS +SPATIAL_GRID_SHIFT 0x0021 0x2308 DS +SIGNAL_LIMITS_MINIMUM 0x0021 0x2309 DS +SIGNAL_LIMITS_MAXIMUM 0x0021 0x2310 DS +SPECTROSCOPY_INFO_MASK 0x0021 0x2311 IS +AC_ADC_OFFSET 0x0021 0x2330 DS +AC_PREAMPLIFIER_GAIN 0x0021 0x2331 DS + +// Image presentation group +GROUP_LENGTH 0x0029 0x0000 UL +PRIVATE_CREATOR 0x0029 0x0010 LO +PRIVATE_CREATOR 0x0029 0x0011 LO +PRIVATE_CREATOR 0x0029 0x0012 LO +WINDOW_STYLE 0x0029 0x1110 CS +PIXEL_QUALITY_CODE 0x0029 0x1120 CS +SORT_CODE 0x0029 0x1152 IS +PMTF_INFORMATION_1 0x0029 0x1131 LO +PMTF_INFORMATION_2 0x0029 0x1132 UL +PMTF_INFORMATION_3 0x0029 0x1133 UL +PMTF_INFORMATION_4 0x0029 0x1134 CS + +// Device group +GROUP_LENGTH 0x0051 0x0000 UL +PRIVATE_CREATOR 0x0051 0x0010 LO +IMAGE_TEXT 0x0051 0x1010 LO
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/pms_element_defs.h @@ -0,0 +1,85 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : pms_element_defs.h +@DESCRIPTION: Element definitions for Philips Medical Systems (no, really) +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : March 11, 2005 +@MODIFIED : +@COPYRIGHT : + Copyright (C) 2005 Robert D. Vincent, + 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. +---------------------------------------------------------------------------- */ + +/* This information was derived from the publically available "DICOM + * Conformance Statement" for the Philips Medical Systems MR Intera + * 10.1, 10 April 2003. + * + * However, not all Philips scanners use the fixed element numbers + * implied here. The upper eight bits of the element ID's are not + * entirely deterministic. It is necessary to root around in the + * image for the private groups. + */ + +#define PMS_PRIVATE_GROUP_ID 0x2001 + +GLOBAL_ELEMENT(PMS_Chemical_Shift , 0x2001, 0x1001, FL); +GLOBAL_ELEMENT(PMS_Chemical_Shift_Number_MR , 0x2001, 0x1002, IS); +GLOBAL_ELEMENT(PMS_Diffusion_B_Factor , 0x2001, 0x1003, FL); +GLOBAL_ELEMENT(PMS_Diffusion_Direction , 0x2001, 0x1004, CS); +GLOBAL_ELEMENT(PMS_Image_Enhanced , 0x2001, 0x1006, CS); +GLOBAL_ELEMENT(PMS_Image_Type_ED_ES , 0x2001, 0x1007, CS); +GLOBAL_ELEMENT(PMS_Phase_number , 0x2001, 0x1008, IS); +/* xx09 is present but not defined */ +GLOBAL_ELEMENT(PMS_Slice_Number_MR, /* Slice index in series */ + 0x2001, 0x100a, IS); +GLOBAL_ELEMENT(PMS_Slice_Orientation, /* SAGITTAL, TRANSVERSAL, e.g. */ + 0x2001, 0x100b, CS); +/* xx0c, xx0e, xx0f, xx10 are present but not defined */ +GLOBAL_ELEMENT(PMS_Diffusion_Echo_Time , 0x2001, 0x1011, FL); +GLOBAL_ELEMENT(PMS_Dynamic_Series , 0x2001, 0x1012, CS); +GLOBAL_ELEMENT(PMS_EPI_Factor , 0x2001, 0x1013, SL); +GLOBAL_ELEMENT(PMS_Number_of_Echoes , 0x2001, 0x1014, SL); +GLOBAL_ELEMENT(PMS_Number_of_Locations , 0x2001, 0x1015, SS); +GLOBAL_ELEMENT(PMS_Number_of_PC_Locations , 0x2001, 0x1016, SS); +GLOBAL_ELEMENT(PMS_Number_of_Phases_MR , 0x2001, 0x1017, SL); +GLOBAL_ELEMENT(PMS_Number_of_Slices_MR , 0x2001, 0x1018, SL); +GLOBAL_ELEMENT(PMS_Partial_Matrix_Scanned , 0x2001, 0x1019, CS); +GLOBAL_ELEMENT(PMS_PC_Velocity , 0x2001, 0x101a, FL); +GLOBAL_ELEMENT(PMS_Prepulse_Delay , 0x2001, 0x101b, FL); +GLOBAL_ELEMENT(PMS_Prepulse_Type , 0x2001, 0x101c, CS); +GLOBAL_ELEMENT(PMS_Reconstruction_Number , 0x2001, 0x101d, IS); +/* xx1e is present but not defined */ +GLOBAL_ELEMENT(PMS_Respiration_Sync , 0x2001, 0x101f, CS); +GLOBAL_ELEMENT(PMS_Scanning_Technique_Description_MR , 0x2001, 0x1020, CS); +GLOBAL_ELEMENT(PMS_SPIR , 0x2001, 0x1021, CS); +GLOBAL_ELEMENT(PMS_Water_Fat_Shift , 0x2001, 0x1022, FL); +/* xx23 xx24 both present but undefined + xx23 appears to be the flip angle, as in DICOM (0018,1314) + */ +GLOBAL_ELEMENT(PMS_Echo_Time_Display , 0x2001, 0x1025, SH); +GLOBAL_ELEMENT(PMS_Number_of_Stack_Slices , 0x2001, 0x102d, SS); +GLOBAL_ELEMENT(PMS_Stack_Radial_Angle , 0x2001, 0x1032, FL); +GLOBAL_ELEMENT(PMS_Stack_Radial_Axis , 0x2001, 0x1033, CS); +GLOBAL_ELEMENT(PMS_Stack_Slice_Number , 0x2001, 0x1035, SS); +GLOBAL_ELEMENT(PMS_Stack_Type , 0x2001, 0x1036, CS); +/* xx52, xx5f both present but undefined */ +GLOBAL_ELEMENT(PMS_Number_of_Stacks , 0x2001, 0x1060, SL); +GLOBAL_ELEMENT(PMS_Examination_Source , 0x2001, 0x1063, CS); + +/* additional undefined elements include: + xx60-xx62, xx6e, xx7b, xx81-xx8b, 9000 + + xx83 appears to be the imaging frequency, as in DICOM (0018,0084) + + */ + +
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/progress.c @@ -0,0 +1,55 @@ + +// 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++) { ... + +static const char rcsid[] = "$Header: /private-cvsroot/minc/conversion/dcm2mnc/progress.c,v 1.2.2.1 2005-05-12 21:16:48 bert Exp $"; + +#include <stdio.h> +#include <math.h> +#include <string.h> + +#include "progress.h" + +void +progress(long index, int end, const char *message) +{ + int ix; + const int width = 50; + int nchars; + + if (index == 0) { + printf("%-20.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->"); + 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"); + } +} + + + + +
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/progress.h @@ -0,0 +1,1 @@ +extern void progress(long index, int end, const char *message);
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/siemens_header_defs.h @@ -0,0 +1,491 @@ +#ifndef _SIEMENS_HEADER_DEFS_H_ +#define _SIEMENS_HEADER_DEFS_H_ 1 + +/* CONSTANTS */ + +#define N_STRING 26 +#define N_AGE 4 +#define N_DIAGNOSIS 40 +#define N_NUCLEUS 8 +#define N_MANUFACTURER 8 +#define N_ORIENTATION 3 +#define N_PATIENTID 12 +#define N_SWVERSION 8 + +typedef double flt64_t; + +/* ACR-NEMA specific types */ + +/* Analogous to DICOM field (0028, 0030) */ +typedef struct pixel_spacing +{ + flt64_t row; + flt64_t col; +} pixel_spacing_t; + +/* Analogous to DICOM fields (0028, 1050) and (0028, 1051) */ +typedef struct window +{ + int32_t x; + int32_t y; +} window_t; + +/* IMA specific types */ +typedef struct ima_date +{ + int32_t year; /* Full year including century */ + int32_t month; /* Month from 1(Jan) to 12(Dec) */ + int32_t day; /* Day of month from 1 to 31 */ +} ima_date_t; + +typedef struct ima_time +{ + int32_t hour; /* Hour from 0 to 23 */ + int32_t minute; /* Minute from 0 to 59 */ + int32_t second; /* Second from 0 to 59 */ + int32_t msec; /* Milliseconds from 0 to 999 */ +} ima_time_t; + +typedef struct ima_vector +{ + flt64_t x; + flt64_t y; + flt64_t z; +} ima_vector_t; + +typedef enum ima_slice_order +{ + SO_ASCENDING = 1, + SO_DESCENDING = 2, + SO_FREE = 3, + SO_INTERLEAVED = 4, + SO_NONE = 5 +} ima_slice_order_t; + +typedef enum ima_position +{ + PP_LEFT = 1, + PP_PRONE = 2, + PP_RIGHT = 3, + PP_SUPINE = 4, +} ima_position_t; + +typedef enum +{ + RD_FEET = 1, + RD_HEAD = 2 +} ima_rest_direction_t; + +typedef enum +{ + VD_FEET = 1, + VD_HEAD = 2, + VD_AtoP = 3, + VD_LtoR = 4, + VD_PtoA = 5, + VD_RtoL = 6 +} ima_view_direction_t; + +typedef struct +{ + flt64_t height; + flt64_t width; +} ima_field_of_view_t; + +typedef struct +{ + char y[N_ORIENTATION + 1]; /* up - down */ + char x[N_ORIENTATION + 1]; /* left - right */ + char z[N_ORIENTATION + 1]; /* back - front */ +} ima_orientation_t; + +/* + * Identifying information group 0x0008 + */ +struct ima_acr_0008 /* Item# FOffs SOffs */ +{ + ima_date_t StudyDate; /* 0020 0000 */ + ima_date_t AcquisitionDate; /* 0022 000C */ + ima_date_t ContentDate; /* 0023 0018 */ + ima_time_t StudyTime; /* 0030 0024 */ + ima_time_t AcquisitionTime; /* 0032 0034 */ + ima_time_t ContentTime; /* 0033 0044 */ + char pad1[8]; + int32_t Modality; /* 0060 005C */ + char Manufacturer[N_MANUFACTURER + 1]; /* 0070 0060 */ + char InstitutionName[N_STRING + 1]; /* 0080 0069 */ + char PhysicianName[N_STRING + 1]; /* 0090 0084 */ + char StationName[N_STRING + 1]; /* 1010 009F */ + char StudyDescription[N_STRING + 1]; /* 1030 00BA */ + char pad2[N_STRING + 1]; + char AdmittingDiagnoses[N_DIAGNOSIS + 1]; /* 1080 00F0 */ + char ModelName[N_STRING + 1]; /* 1090 0119 */ + char pad3[76]; +}; + + +/* + * Patient information group 0x0010 + */ +struct ima_acr_0010 /* Item# FOffs SOffs */ +{ + char PatientName[N_STRING + 1]; /* 0010 0300 */ + char PatientID[N_PATIENTID + 1]; /* 0020 031B */ + ima_date_t PatientDOB; /* 0030 0328 */ + int32_t PatientSex; /* 0040 0334 */ + char PatientBirthName[N_STRING + 1]; /* 1005 0338 */ + char PatientAge[N_AGE + 1]; /* 1010 0353 */ + flt64_t PatientSize; /* 1020 0358 */ + int32_t PatientWeight; /* 1030 0360 */ + char pad1[156]; +}; + +/* + * Acquisition information group 0x0018 + */ +struct ima_acr_0018 +{ /* Item# FOffs SOffs */ + char pad1[8]; /* Dummy padding */ + flt64_t SliceThickness; /* 0050 0608 */ + char pad2[8]; + flt64_t RepetitionTime; /* 0080 0618 */ + flt64_t EchoTime; /* 0081 0620 */ + flt64_t InversionTime; /* 0082 0628 */ + int32_t NumberOfAverages; /* 0083 0630 */ + char pad3[4]; /* Dummy padding */ + flt64_t ImagingFrequency; /* 0084 0638 */ + char pad4[4]; /* 0085 0640 */ + int32_t EchoNumber; /* 0086 0644 */ + int32_t DataCollectionDiameter; /* 0090 0648 */ + char SerialNumber[N_STRING + 1]; /* 1000 064C */ + char SoftwareVersion[N_SWVERSION + 1]; /* 1020 0667 */ + char pad5[61]; /* Dummy padding */ + ima_date_t CalibrationDate; /* 1200 06B0 */ + ima_time_t CalibrationTime; /* 1201 06BC */ + char pad6[N_STRING + 1]; + char ReceiveCoilName[N_STRING + 1]; /* 1250 06E7 */ + char pad7[N_STRING + 1]; + ima_position_t PatientPosition; /* 5100 0720 */ + char ImagedNucleus[N_NUCLEUS + 1]; /* 0085 0724 */ + char pad8[80]; /* Pad to 384 bytes */ +}; + +/* + * Relationship information group 0x0020 + */ +struct ima_acr_0020 +{ /* Item# FOffs */ + int32_t StudyID; /* 0010 0C80 */ + char pad1[4]; /* */ + int32_t AcquisitionNumber; /* 0012 0C88 */ + int32_t InstanceNumber; /* 0013 0C8C */ + int32_t ImagePosition[3]; /* 0030 0C90 */ + char pad2[4]; /* Dummy padding */ + flt64_t ImageOrientation[6]; /* 0035 0C94 */ + int32_t Location; /* 0050 0CD0 */ + int32_t Laterality; /* 0060 0CD4 */ + char pad3[4]; + int32_t AcquisitionsInSeries; /* 1001 0CDC */ + char pad4[416]; /* Pad to 512 bytes */ +}; + +/* + * Image presentation Information group 0x0028 + */ +struct ima_acr_0028 +{ /* Item# FOffs */ + int16_t ImageDimension; /* 0005 1380 */ + int16_t Rows; /* 0010 1382 */ + int16_t Columns; /* 0011 1384 */ + char pad1[2]; + pixel_spacing_t PixelSpacing; /* 0030 1388 */ + char pad2[8]; + int16_t BitsAllocated; /* 0100 13A0 */ + int16_t BitsStored; /* 0101 13A2 */ + int16_t HighBit; /* 0102 13A4 */ + int16_t PixelRepresentation; /* 0103 13A6 */ + window_t WindowCenter; /* 1050 13A8 */ + window_t WindowWidth; /* 1051 13B0 */ + int32_t RescaleIntercept; /* 1052 13B8 */ + int32_t RescaleSlope; /* 1053 13BC */ + char pad3[192]; /* Pad to 256 bytes */ +}; + +/***** Siemens private group structures *****/ + +/* + * Siemens acquisition group 0x0019 + */ +struct ima_siemens_0019 +{ + char pad1[20]; /* Padding */ + int32_t NumberOfDataBytes; /* 1060 */ + char pad2[140]; + int32_t FourierLinesNominal; /* 1220 */ + char pad3[4]; + int32_t FourierLinesAfterZero; /* 1226 */ + int32_t FirstMeasuredFourierLine; /* 1228 */ + int32_t AcquisitionColumns; /* 1230 */ + int32_t ReconstructionColumns; /* 1231 */ + int32_t NumberOfAverages; /* 1250 */ + flt64_t FlipAngle; /* 1260 */ + int32_t NumberOfPrescans; /* 1270 */ + char pad4[116]; /* Dummy padding */ + int32_t SaturationRegions; /* 1290 */ + char pad5[316]; /* Dummy padding */ + flt64_t MagneticFieldStrength; /* 1412 */ + char pad6[631]; /* Dummy padding */ +}; + +/* + * Siemens Relationship group 0x0021 + */ +struct ima_siemens_0021 +{ + char pad1[32]; /* Dummy padding */ + ima_field_of_view_t FieldOfView; /* 1120 0EA0 */ + ima_view_direction_t ViewDirection; /* 1130 0EB0 */ + ima_rest_direction_t RestDirection; /* 1132 0EB4 */ + ima_vector_t ImagePosition; /* 1160 0EB8 */ + ima_vector_t ImageNormal; /* 1161 0ED0 */ + flt64_t ImageDistance; /* 1163 0EE8 */ + char pad3[8]; /* Dummy padding */ + ima_vector_t ImageRow; /* 116A 0EF8 */ + ima_vector_t ImageColumn; /* 116B 0F10 */ + ima_orientation_t OrientationSet1; /* 1170 0F28 */ + ima_orientation_t OrientationSet2; /* 1171 0F34 */ + char StudyName[N_STRING + 1]; /* 1180 0F40 */ + int32_t StudyType; /* 1182 */ + flt64_t ImageMagnificationFactor; /* 1122 */ + char pad4[40]; + int32_t NumberOf3DRawPartNom; /* 1330 */ + int32_t NumberOf3DRawPartCur; /* 1331 */ + int32_t NumberOf3DImaPart; /* 1334 */ + int32_t Actual3DImaPartNumber; /* 1336 */ + char pad5[4]; + int32_t NumberOfSlicesNom; /* 1340 */ + int32_t NumberOfSlicesCur; /* 1341 */ + int32_t CurrentSliceNumber; /* 1342 */ + int32_t CurrentGroupNumber; /* 1343 */ + char pad7[88]; /* Dummy padding */ + int32_t NumberOfEchoes; /* 1370 */ + char pad9[32]; /* Dummy padding */ + ima_slice_order_t SliceOrder; /* 134F */ + char pad10[4]; /* Dummy padding */ + flt64_t SlabThickness; /* 1339 */ + char pad11[829]; /* Padding */ +}; + +/* this is a work in progress, based on David Clunie's website */ + +#define PATIENT_NUMBER_SIZE 12 +#define PATIENT_DATE_SIZE 11 +#define PATIENT_POSITION_SIZE 11 +#define IMAGE_NUMBER_SIZE 11 +#define IMAGE_NUMBER_TEXT "IMAGE" +#define LABEL_SIZE 5 +#define DATE_OF_MEASUREMENT_SIZE 11 +#define TIME_OF_MEASUREMENT_SIZE 5 +#define TIME_OF_ACQUISITION_SIZE 11 +#define TIME_OF_ACQUISITION_TEXT_CT "TI" +#define TIME_OF_ACQUISITION_TEXT_MR "TA " +#define NUMBER_OF_ACQUISITIONS_SIZE 11 +#define NUMBER_OF_ACQUISITIONS_TEXT "AC" +#define COMMENT_NO1_SIZE 26 +#define COMMENT_NO2_SIZE 26 +#define INSTALLATION_NAME_SIZE 26 +#define SOFTWARE_VERSION_SIZE 11 +#define MATRIX_SIZE 11 +#define TYPE_OF_MEASUREMENT_SIZE 11 +#define SCAN_NUMBER_SIZE 11 +#define SCAN_NUMBER_TEXT "SCAN" +#define REPETITION_TIME_SIZE 11 +#define REPETITION_TIME_TEXT "TR" +#define ECHO_TIME_SIZE 11 +#define ECHO_TIME_TEXT "TE" +#define GATING_AND_TRIGGER_SIZE 11 +#define GATING_AND_TRIGGER_TEXT "TD" +#define TUBE_CURRENT_SIZE 11 +#define TUBE_CURRENT_TEXT "mA" +#define TUBE_VOLTAGE_SIZE 11 +#define TUBE_VOLTAGE_TEXT "kV" +#define SLICE_THICKNESS_SIZE 11 +#define SLICE_THICKNESS_TEXT "SL" +#define SLICE_POSITION_SIZE 11 +#define SLICE_POSITION_TEXT "SP" +#define SLICE_ORIENTATION_NO1_SIZE 11 +#define SLICE_ORIENTATION_NO2_SIZE SLICE_ORIENTATION_NO1_SIZE +#define COR_TEXT "Cor" +#define SAG_TEXT "Sag" +#define TRA_TEXT "Tra" +#define FIELD_OF_VIEW_SIZE 11 +#define FIELD_OF_VIEW_TEXT "FoV" +#define ZOOM_CENTER_SIZE 11 +#define ZOOM_CENTER_TEXT "CE" +#define ZOOM_CENTER_TEXT_MR "MF" +#define GANTRY_TILT_SIZE 11 +#define GANTRY_TILT_TEXT "GT" +#define TABLE_POSITION_SIZE 11 +#define TABLE_POSITION_TEXT "TP" +#define MIP_HEADLINE_SIZE 3 +#define MIP_HEADLINE_TEXT "VOI" +#define MIP_LINE_SIZE 15 +#define MIP_LINE_TEXT "Lin" +#define MIP_COLUMN_SIZE 15 +#define MIP_COLUMN_TEXT "Col" +#define MIP_SLICE_SIZE 15 +#define MIP_SLICE_TEXT "Sli" +#define STUDY_NUMBER_SIZE 11 +#define STUDY_NUMBER_TEXT "STUDY" +#define CONTRAST_SIZE 5 +#define CONTRAST_TEXT_CT "+C IV" +#define CONTRAST_TEXT_MR "+C " +#define CONTRAST_TEXT_NONE " " +#define PATIENT_BIRTHDATE_SIZE 11 +#define SEQUENCE_INFO_SIZE 11 +#define SATURATION_REGIONS_SIZE 11 +#define SATURATION_REGIONS_TEXT "SAT" +#define DATA_SET_ID_SIZE 26 +#define DATA_SET_ID_TEXT_STUDY "STU" +#define DATA_SET_ID_TEXT_IMAGE "IMA" +#define DATA_SET_ID_TEXT_DELIMITER "/" +#define MAGNIFICATION_FACTOR_SIZE 11 +#define MAGNIFICATION_FACTOR_TEXT "MF" +#define MANUFACTURER_MODEL_SIZE 26 +#define PATIENT_NAME_SIZE 26 +#define TIME_OF_SCANNING_SIZE 8 + +typedef struct text_info +{ + char PatientNumber[PATIENT_NUMBER_SIZE + 1]; /* Patient Id */ + char PatientSexAndAge[PATIENT_DATE_SIZE + 1]; /* Patient Sex, Patient Age */ + char PatientPosition[PATIENT_POSITION_SIZE + 1]; /* Patient Rest Direction, ... */ + char ImageNumber[IMAGE_NUMBER_SIZE + 1]; /* Image */ + char Label[LABEL_SIZE + 1]; /* Archiving Mark Mask, ... */ + char DateOfMeasurement[DATE_OF_MEASUREMENT_SIZE + 1]; /* Acquisition Date */ + char TimeOfMeasurement[TIME_OF_MEASUREMENT_SIZE + 1]; /* Acquisition Time */ + char TimeOfAcquisition[TIME_OF_ACQUISITION_SIZE + 1]; /* CT: Exposure Time MR: + Total Measurement Time */ + char NumberOfAcquisitions[NUMBER_OF_ACQUISITIONS_SIZE + 1]; /* Number of Averages */ + char CommentNo1[COMMENT_NO1_SIZE + 1]; /* Procedure Description */ + char CommentNo2[COMMENT_NO2_SIZE + 1]; + char InstallationName[INSTALLATION_NAME_SIZE + 1]; /* Institution ID */ + char SoftwareVersion[SOFTWARE_VERSION_SIZE + 1]; /* Software Version */ + char Matrix[MATRIX_SIZE + 1]; /* Rows, Columns */ + char TypeOfMeasurement[TYPE_OF_MEASUREMENT_SIZE + 1]; /* Calculation Mode */ + char ScanNumber[SCAN_NUMBER_SIZE + 1]; /* Acquisition */ + char RepetitionTime[REPETITION_TIME_SIZE + 1]; /* Repetition Time */ + char EchoTime[ECHO_TIME_SIZE + 1]; /* Echo Time */ + char GatingAndTrigger[GATING_AND_TRIGGER_SIZE + 1]; /* Signal Mask */ + char TubeCurrent[TUBE_CURRENT_SIZE + 1]; /* Exposure */ + char TubeVoltage[TUBE_VOLTAGE_SIZE + 1]; /* Generator Power */ + char SliceThickness[SLICE_THICKNESS_SIZE + 1]; /* Slice Thickness */ + char SlicePosition[SLICE_POSITION_SIZE + 1]; /* Image Distance */ + char SliceOrientationNo1[SLICE_ORIENTATION_NO1_SIZE + 1]; /* Image Position, ... */ + char SliceOrientationNo2[SLICE_ORIENTATION_NO2_SIZE + 1]; + char FieldOfView[FIELD_OF_VIEW_SIZE + 1]; /* Field of View */ + char ZoomCenter[ZOOM_CENTER_SIZE + 1]; /* Target */ + char GantryTilt[GANTRY_TILT_SIZE + 1]; /* Gantry Tilt */ + char TablePosition[TABLE_POSITION_SIZE + 1]; /* Location */ + char MipHeadLine[MIP_HEADLINE_SIZE + 1]; /* <string> */ + char MipLine[MIP_LINE_SIZE + 1]; /* MIP x Row */ + char MipColumn[MIP_COLUMN_SIZE + 1]; /* MIP x Column */ + char MipSlice[MIP_SLICE_SIZE + 1]; /* MIP x Slice */ + char StudyNumber[STUDY_NUMBER_SIZE + 1]; /* Study */ + char Contrast[CONTRAST_SIZE + 1]; /* Contrast Agent */ + char PatientBirthdate[PATIENT_BIRTHDATE_SIZE + 1]; /* Patient Birthday */ + char SequenceInformation[SEQUENCE_INFO_SIZE + 1]; /* Sequence File Owner, ... */ + char SaturationRegions[SATURATION_REGIONS_SIZE + 1]; /* Saturation Regions, ... */ + char DataSetId[DATA_SET_ID_SIZE + 1]; /* Image, Study */ + char MagnificationFactor[MAGNIFICATION_FACTOR_SIZE + 1]; /* Image Maginification Factor */ + char ManufacturerModel[MANUFACTURER_MODEL_SIZE + 1]; /* Manufacturer Model */ + char PatientName[PATIENT_NAME_SIZE + 1]; /* Patient Name */ + char TimeOfScanning[TIME_OF_SCANNING_SIZE + 1]; /* Acquisition Time */ +} text_info_t; + +#if 0 +struct text_info { + char PatientID[12+1]; /* 5504 */ + char PatientSex[1]; /* 5517 */ + char PatientAge[3]; /* 5518 */ + char PatientAgeUnits[1]; /* 5521 */ + char pad1[7]; /* 5522 */ + char PatientPosition[12]; /* 5529 */ + char ImageNumberFlag[5]; /* 5541 */ + char ImageNumber[3]; /* 5546 */ + char pad2[10]; /* 5551 */ + char Date[11+1]; /* 5559 */ + char Time[5+1]; /* 5571 */ + char AcquisitionTimeFlag[6]; /* 5577 */ + char AcquisitionTime[5+1]; /* 5583 */ + char AcquisitionCountFlag[6]; /* 5589 */ + char AcquisitionCount[5+1]; /* 5595 */ + char Annotation[27]; /* 5601 */ + char AdmittingDiagnosis[27]; /* 5628 */ + char Organization[27]; /* 5655 */ + char Station[12]; /* 5682 */ + char AcquisitionMatrixPhase[3]; /* 5695 */ + char AcquisitionMatrixPhaseAxis[1]; + char AcquisitionMatrixFreq[3]; + char AcquisitionMatrixFreq0[1]; + char AcquisitionMatrixFreqS[1]; + char Sequence[8]; + char FlipAngle[3]; + char ScanNumberFlag[4]; + char ScanNumberA[3]; + char ScanNumberB[3]; + char RepetitionTimeFlag[2]; + char RepetitionTime[7]; + char EchoTimeFlag[2]; + char EchoTime[5]; + char EchoNumber[1]; + char SliceThicknessFlag[2]; + char SliceThickness[7]; + char SlicePositionFlag[2]; + char SlicePosition[7]; + char AngleFlag1[3]; + char AngleFlag2[1]; + char AngleFlag3[3]; + char Angle[4]; + char FOVFlag[3]; + char FOVH[3]; + char FOVV[3]; + char TablePositionFlag[2]; + char TablePosition[7]; + char StudyNumberFlag[5]; + char StudyNumber[2]; + char DOBDD[2]; + char DOBMM[3]; + char DOBYYYY[4]; + char StudyNumberFlag2[3]; + char ImageNumberFlag2[3]; + char StudyNumber2[2]; + char ImageNumber2[2]; + char StudyImageNumber3[5]; + char ModelName[15]; + char PatientName[27]; /* 6058 */ + char ScanStartTimeHH[3]; /* 6085 */ + char ScanStartTimeMM[3]; /* 6088 */ + char ScanStartTimeSS[3]; /* 6091 */ +}; +#endif + +/* Siemens IMA header - total size is 0x1800 bytes */ + +typedef struct +{ /* Offset - Description */ + struct ima_acr_0008 G08; /* 0x0000 - Identifying Information */ + char G09[0x0180]; /* 0x0180 - Siemens specific */ + struct ima_acr_0010 G10; /* 0x0300 - Patient Information */ + char G11[0x0080]; /* 0x0400 - Siemens specific */ + char G13[0x0180]; /* 0x0480 - Siemens specific */ + struct ima_acr_0018 G18; /* 0x0600 - Acquisition Information */ + struct ima_siemens_0019 G19; /* 0x0780 - Siemens specific */ + struct ima_acr_0020 G20; /* 0x0C80 - Relationship Information */ + struct ima_siemens_0021 G21; /* 0x0E80 - Siemens specific */ + struct ima_acr_0028 G28; /* 0x1380 - Image Presentation Information */ + char G29[0x0100]; /* 0x1480 - Siemens specific */ + text_info_t ti; +} ima_header_t; + +#endif /* _SIEMENS_HEADER_DEFS_H_ */
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/siemens_header_table.h @@ -0,0 +1,97 @@ +Siemens_hdr_entry Siemens_hdr_table[] = { +{0x0008, 0x0020, &IMA_hdr.G08.StudyDate, create_ima_date_t_element, 1}, +{0x0008, 0x0022, &IMA_hdr.G08.AcquisitionDate, create_ima_date_t_element, 1}, +{0x0008, 0x0023, &IMA_hdr.G08.ContentDate, create_ima_date_t_element, 1}, +{0x0008, 0x0030, &IMA_hdr.G08.StudyTime, create_ima_time_t_element, 1}, +{0x0008, 0x0032, &IMA_hdr.G08.AcquisitionTime, create_ima_time_t_element, 1}, +{0x0008, 0x0033, &IMA_hdr.G08.ContentTime, create_ima_time_t_element, 1}, +{0x0008, 0x0060, &IMA_hdr.G08.Modality, create_modality_element, 1}, +{0x0008, 0x0070, &IMA_hdr.G08.Manufacturer, create_char_element, N_MANUFACTURER+1}, +{0x0008, 0x0080, &IMA_hdr.G08.InstitutionName, create_char_element, N_STRING+1}, +{0x0008, 0x0090, &IMA_hdr.G08.PhysicianName, create_char_element, N_STRING+1}, +{0x0008, 0x1010, &IMA_hdr.G08.StationName, create_char_element, N_STRING+1}, +{0x0008, 0x1030, &IMA_hdr.G08.StudyDescription, create_char_element, N_STRING+1}, +{0x0008, 0x1080, &IMA_hdr.G08.AdmittingDiagnoses, create_char_element, N_DIAGNOSIS+1}, +{0x0008, 0x1090, &IMA_hdr.G08.ModelName, create_char_element, N_STRING+1}, + +{0x0010, 0x0010, &IMA_hdr.G10.PatientName, create_char_element, N_STRING+1}, +{0x0010, 0x0020, &IMA_hdr.G10.PatientID, create_char_element, N_PATIENTID+1}, +{0x0010, 0x0030, &IMA_hdr.G10.PatientDOB, create_ima_date_t_element, 1}, +{0x0010, 0x0040, &IMA_hdr.G10.PatientSex, create_sex_element, 1}, +{0x0010, 0x1005, &IMA_hdr.G10.PatientBirthName, create_char_element, N_STRING+1}, +{0x0010, 0x1010, &IMA_hdr.G10.PatientAge, create_age_element, 1}, +{0x0010, 0x1030, &IMA_hdr.G10.PatientWeight, create_long_element, 1}, + +{0x0018, 0x0050, &IMA_hdr.G18.SliceThickness, create_double_element, 1}, +{0x0018, 0x0080, &IMA_hdr.G18.RepetitionTime, create_double_element, 1}, +{0x0018, 0x0081, &IMA_hdr.G18.EchoTime, create_double_element, 1}, +{0x0018, 0x0082, &IMA_hdr.G18.InversionTime, create_double_element, 1}, +{0x0018, 0x0083, &IMA_hdr.G18.NumberOfAverages, create_long_element, 1}, +{0x0018, 0x0084, &IMA_hdr.G18.ImagingFrequency, create_double_element, 1}, +{0x0018, 0x0085, &IMA_hdr.G18.ImagedNucleus, create_char_element, N_NUCLEUS+1}, +{0x0018, 0x0086, &IMA_hdr.G18.EchoNumber, create_long_element, 1}, +{0x0018, 0x0090, &IMA_hdr.G18.DataCollectionDiameter, create_long_element, 1}, +{0x0018, 0x1000, &IMA_hdr.G18.SerialNumber, create_char_element, N_STRING+1}, +{0x0018, 0x1020, &IMA_hdr.G18.SoftwareVersion, create_char_element, N_SWVERSION+1}, +{0x0018, 0x1200, &IMA_hdr.G18.CalibrationDate, create_ima_date_t_element, 1}, +{0x0018, 0x1201, &IMA_hdr.G18.CalibrationTime, create_ima_time_t_element, 1}, +{0x0018, 0x1250, &IMA_hdr.G18.ReceiveCoilName, create_char_element, N_STRING+1}, +{0x0018, 0x5100, &IMA_hdr.G18.PatientPosition, create_ima_position_t_element, 1}, + +{0x0019, 0x1060, &IMA_hdr.G19.NumberOfDataBytes, create_long_element, 1}, +{0x0019, 0x1220, &IMA_hdr.G19.FourierLinesNominal, create_long_element, 1}, +{0x0019, 0x1226, &IMA_hdr.G19.FourierLinesAfterZero, create_long_element, 1}, +{0x0019, 0x1228, &IMA_hdr.G19.FirstMeasuredFourierLine, create_long_element, 1}, +{0x0019, 0x1230, &IMA_hdr.G19.AcquisitionColumns, create_long_element, 1}, +{0x0019, 0x1231, &IMA_hdr.G19.ReconstructionColumns, create_long_element, 1}, +{0x0019, 0x1250, &IMA_hdr.G19.NumberOfAverages, create_long_element, 1}, +{0x0019, 0x1260, &IMA_hdr.G19.FlipAngle, create_double_element, 1}, +{0x0019, 0x1270, &IMA_hdr.G19.NumberOfPrescans, create_long_element, 1}, +{0x0019, 0x1290, &IMA_hdr.G19.SaturationRegions, create_long_element, 1}, +{0x0019, 0x1412, &IMA_hdr.G19.MagneticFieldStrength, create_double_element, 1}, + +{0x0020, 0x0010, &IMA_hdr.G20.StudyID, create_long_element, 1}, +{0x0020, 0x0012, &IMA_hdr.G20.AcquisitionNumber, create_long_element, 1}, +{0x0020, 0x0013, &IMA_hdr.G20.InstanceNumber, create_long_element, 1}, +{0x0020, 0x0050, &IMA_hdr.G20.Location, create_long_element, 1}, +{0x0020, 0x0060, &IMA_hdr.G20.Laterality, create_laterality_element, 1}, +{0x0020, 0x1001, &IMA_hdr.G20.AcquisitionsInSeries, create_long_element, 1}, + +{0x0021, 0x1120, &IMA_hdr.G21.FieldOfView, create_field_of_view_t_element, 1}, +{0x0021, 0x1122, &IMA_hdr.G21.ImageMagnificationFactor, create_double_element, 1}, +{0x0021, 0x1130, &IMA_hdr.G21.ViewDirection, create_view_direction_t_element, 1}, +{0x0021, 0x1132, &IMA_hdr.G21.RestDirection, create_rest_direction_t_element, 1}, +{0x0021, 0x1160, &IMA_hdr.G21.ImagePosition, create_ima_vector_t_element, 1}, +{0x0021, 0x1161, &IMA_hdr.G21.ImageNormal, create_ima_vector_t_element, 1}, +{0x0021, 0x1163, &IMA_hdr.G21.ImageDistance, create_double_element, 1}, +{0x0021, 0x116a, &IMA_hdr.G21.ImageRow, create_ima_vector_t_element, 1}, +{0x0021, 0x116b, &IMA_hdr.G21.ImageColumn, create_ima_vector_t_element, 1}, +{0x0021, 0x1170, &IMA_hdr.G21.OrientationSet1, create_ima_orientation_t_element, 1}, +{0x0021, 0x1171, &IMA_hdr.G21.OrientationSet2, create_ima_orientation_t_element, 1}, +{0x0021, 0x1180, &IMA_hdr.G21.StudyName, create_char_element, N_STRING+1}, +{0x0021, 0x1330, &IMA_hdr.G21.NumberOf3DRawPartNom, create_long_element, 1}, +{0x0021, 0x1331, &IMA_hdr.G21.NumberOf3DRawPartCur, create_long_element, 1}, +{0x0021, 0x1334, &IMA_hdr.G21.NumberOf3DImaPart, create_long_element, 1}, +{0x0021, 0x1336, &IMA_hdr.G21.Actual3DImaPartNumber, create_long_element, 1}, +{0x0021, 0x1339, &IMA_hdr.G21.SlabThickness, create_double_element, 1}, +{0x0021, 0x1340, &IMA_hdr.G21.NumberOfSlicesNom, create_long_element, 1}, +{0x0021, 0x1341, &IMA_hdr.G21.NumberOfSlicesCur, create_long_element, 1}, +{0x0021, 0x1342, &IMA_hdr.G21.CurrentSliceNumber, create_long_element, 1}, +{0x0021, 0x1343, &IMA_hdr.G21.CurrentGroupNumber, create_long_element, 1}, +{0x0021, 0x134f, &IMA_hdr.G21.SliceOrder, create_slice_order_element, 1}, +{0x0021, 0x1370, &IMA_hdr.G21.NumberOfEchoes, create_long_element, 1}, +{0x0028, 0x0005, &IMA_hdr.G28.ImageDimension, create_short_element, 1}, +{0x0028, 0x0010, &IMA_hdr.G28.Rows, create_short_element, 1}, +{0x0028, 0x0011, &IMA_hdr.G28.Columns, create_short_element, 1}, +{0x0028, 0x0030, &IMA_hdr.G28.PixelSpacing, create_pixel_spacing_t_element, 1}, +{0x0028, 0x0100, &IMA_hdr.G28.BitsAllocated, create_short_element, 1}, +{0x0028, 0x0101, &IMA_hdr.G28.BitsStored, create_short_element, 1}, +{0x0028, 0x0102, &IMA_hdr.G28.HighBit, create_short_element, 1}, +{0x0028, 0x0103, &IMA_hdr.G28.PixelRepresentation, create_short_element, 1}, +{0x0028, 0x1050, &IMA_hdr.G28.WindowCenter, create_window_t_element, 1}, +{0x0028, 0x1051, &IMA_hdr.G28.WindowWidth, create_window_t_element, 1}, +{0x0028, 0x1052, &IMA_hdr.G28.RescaleIntercept, create_long_element, 1}, +{0x0028, 0x1053, &IMA_hdr.G28.RescaleSlope, create_long_element, 1}, +{0, 0, NULL, NULL, 0} +}; +
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/siemens_to_dicom.c @@ -0,0 +1,931 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : siemens_to_dicom.c +@DESCRIPTION: File containing routines to read in a Siemens vision internal + file (.IMA extension) and convert it to a DICOM representation. +@METHOD : +@GLOBALS : +@CREATED : July 8, 1997 (Peter Neelin) +@MODIFIED : $Log: siemens_to_dicom.c,v $ +@MODIFIED : Revision 1.6.2.1 2005-05-12 21:16:48 bert +@MODIFIED : Initial checkin +@MODIFIED : +@MODIFIED : Revision 1.6 2005/04/21 22:32:15 bert +@MODIFIED : Continue Siemens IMA code cleanup +@MODIFIED : +@MODIFIED : Revision 1.5 2005/04/18 16:21:16 bert +@MODIFIED : Fix definition of siemens_to_dicom +@MODIFIED : +@MODIFIED : Revision 1.4 2005/04/05 21:56:47 bert +@MODIFIED : Add some conversion functions, remove some more proprietary junk, and improve range-checking on some functions +@MODIFIED : +@MODIFIED : Revision 1.3 2005/03/03 18:59:16 bert +@MODIFIED : Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) +@MODIFIED : +@MODIFIED : Revision 1.2 2005/03/02 20:06:23 bert +@MODIFIED : Update conversions to reflect simplified header structures and types +@MODIFIED : +@MODIFIED : Revision 1.1 2005/02/17 16:38:11 bert +@MODIFIED : Initial checkin, revised DICOM to MINC converter +@MODIFIED : +@MODIFIED : Revision 1.1.1.1 2003/08/15 19:52:55 leili +@MODIFIED : Leili's dicom server for sonata +@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 + * +---------------------------------------------------------------------------- */ + +static const char rcsid[]="$Header: /private-cvsroot/minc/conversion/dcm2mnc/siemens_to_dicom.c,v 1.6.2.1 2005-05-12 21:16:48 bert Exp $"; + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "dcm2mnc.h" +#include "siemens_header_defs.h" + +/* Constants */ + +#define SIEMENS_IMAGE_OFFSET 6144 /* From dclunie.com */ + +#define IMAGE_NDIMS 2 + +/* Types */ +#define ELEMENT_FUNC_ARGS \ + (int grp_id, int elm_id, void *data, int length) + +#define DEFINE_ELEMENT_FUNC(name) \ + static Acr_Element name ELEMENT_FUNC_ARGS + +#define DECLARE_ELEMENT_FUNC(name) \ + static Acr_Element name ELEMENT_FUNC_ARGS + +typedef Acr_Element (*Create_Element_Function) ELEMENT_FUNC_ARGS; + +typedef struct { + int grp_id; + int elm_id; + void *data; + Create_Element_Function function; + int length; +} Siemens_hdr_entry; + +/* Functions */ + +static Acr_Element_Id get_elid(int grp_id, int elm_id, Acr_VR_Type vr_code); + +DECLARE_ELEMENT_FUNC(create_char_element); +DECLARE_ELEMENT_FUNC(create_long_element); +DECLARE_ELEMENT_FUNC(create_short_element); +DECLARE_ELEMENT_FUNC(create_double_element); +DECLARE_ELEMENT_FUNC(create_ima_date_t_element); +DECLARE_ELEMENT_FUNC(create_ima_time_t_element); +DECLARE_ELEMENT_FUNC(create_modality_element); +DECLARE_ELEMENT_FUNC(create_sex_element); +DECLARE_ELEMENT_FUNC(create_age_element); +DECLARE_ELEMENT_FUNC(create_slice_order_element); +DECLARE_ELEMENT_FUNC(create_pixel_spacing_t_element); +DECLARE_ELEMENT_FUNC(create_window_t_element); +DECLARE_ELEMENT_FUNC(create_ima_vector_t_element); +DECLARE_ELEMENT_FUNC(create_laterality_element); +DECLARE_ELEMENT_FUNC(create_ima_position_t_element); +DECLARE_ELEMENT_FUNC(create_rest_direction_t_element); +DECLARE_ELEMENT_FUNC(create_view_direction_t_element); +DECLARE_ELEMENT_FUNC(create_ima_orientation_t_element); +DECLARE_ELEMENT_FUNC(create_field_of_view_t_element); + +/* Define the table of header values */ +ima_header_t IMA_hdr; /* Must define this first */ +#include "siemens_header_table.h" /* Now include the table */ + +/* flag to print offset table, useful in debugging byte pad issues + with Linux/sun porting */ +/* #define PRINT_OFFSET_TABLE 1 */ + +/* ----------------------------- 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 (.IMA) + and store it in an ACR-NEMA group list. +@METHOD : +@GLOBALS : +@CALLS : +@CREATED : July 8, 1997 (Peter Neelin) +@MODIFIED : +---------------------------------------------------------------------------- */ + +Acr_Group +siemens_to_dicom(const char *filename, int max_group) +{ + FILE *fp; + Siemens_hdr_entry *entry; + Acr_Group group_list; + Acr_Element element; + long image_size; + long pixel_size; + void *image; + double flip_angle; + short rows; + short cols; + +#ifdef PRINT_OFFSET_TABLE + void *header_ptr; /* debug junk */ + void *data_ptr; /* debug junk */ + long offset; /* debug junk */ +#endif + + /* Check the structure offsets */ + assert(((char *)&IMA_hdr.G08 - (char *)&IMA_hdr) == 0x0000); + assert(((char *)&IMA_hdr.G10 - (char *)&IMA_hdr) == 0x0300); + assert(((char *)&IMA_hdr.G18 - (char *)&IMA_hdr) == 0x0600); + assert(((char *)&IMA_hdr.G19 - (char *)&IMA_hdr) == 0x0780); + assert(((char *)&IMA_hdr.G20 - (char *)&IMA_hdr) == 0x0C80); + assert(((char *)&IMA_hdr.G21 - (char *)&IMA_hdr) == 0x0E80); + assert(((char *)&IMA_hdr.G28 - (char *)&IMA_hdr) == 0x1380); + + if (G.Debug >= HI_LOGGING) { + printf("siemens_to_dicom(%s, %x)\n", filename, max_group); + } + + /* Open the file */ + if ((fp = fopen(filename, "rb")) == NULL) { + fprintf(stderr, "Error opening file %s\n", filename); + return NULL; + } + + /* Read in the header */ + if (fread(&IMA_hdr, sizeof(IMA_hdr), 1, fp) != 1) { + fprintf(stderr, "Error reading header in %s\n", filename); + fclose(fp); + return NULL; + } + + /* Get the image if it is needed */ + if (max_group >= ACR_IMAGE_GID) { + + /* Figure out how much space we need for the image */ + pixel_size = 2; /* Apparently this never changes?? */ + + /* Need to byte swap row/col values if needed + */ + acr_get_short(ACR_BIG_ENDIAN, 1, &IMA_hdr.G28.Rows, &rows); + acr_get_short(ACR_BIG_ENDIAN, 1, &IMA_hdr.G28.Columns, &cols); + + image_size = rows * cols; + + image = malloc(pixel_size * image_size); + CHKMEM(image); + + /* Read in the image */ + if (fseek(fp, (long) SIEMENS_IMAGE_OFFSET, SEEK_SET)) { + printf("ERROR: Error finding image in %s\n", filename); + fclose(fp); + return NULL; + } + if (fread(image, pixel_size, image_size, fp) != image_size) { + printf("ERROR: Error reading image in %s\n", filename); + fclose(fp); + return NULL; + } + + } /* If (max_group >= ACR_IMAGE_GID) */ + + /* Close the file */ + fclose(fp); + + /* Loop through the header table, creating a header */ + group_list = NULL; + for (entry = Siemens_hdr_table; entry->data != NULL; entry++) { + +#ifdef PRINT_OFFSET_TABLE + data_ptr = entry->data; + header_ptr = &IMA_hdr; + offset = (long) data_ptr - (long) header_ptr; + printf("DEBUG: group = 0x%x, element = 0x%x, offset = 0x%lx, length = 0x%x\n", + entry->grp_id, entry->elm_id, offset, entry->length); +#endif + + if (entry->function == NULL) { + continue; + } + element = entry->function(entry->grp_id, entry->elm_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); + } + } + + if (acr_find_int(group_list, SPI_Number_of_slices_nominal, 1) > 1) { + short acq_cols; + + acq_cols = acr_find_short(group_list, SPI_Acquisition_columns, + acr_find_short(group_list, ACR_Columns, 1)); + + acr_insert_long(&group_list, EXT_Mosaic_rows, + acr_find_int(group_list, ACR_Rows, 1)); + + acr_insert_long(&group_list, EXT_Mosaic_columns, + acr_find_int(group_list, ACR_Columns, 1)); + + acr_insert_long(&group_list, EXT_Slices_in_file, + acr_find_int(group_list, SPI_Number_of_slices_nominal, 1)); + acr_insert_short(&group_list, EXT_Sub_image_rows, acq_cols); + acr_insert_short(&group_list, EXT_Sub_image_columns, acq_cols); + + /* We need to set up the ACR_Acquisition (and + * ACR_Acquisitions_in_series) objects to make everyone happy. + * + * TODO: This appears to work for SOME IMA files, but it may + * not be correct for all sequences. + */ + acr_insert_long(&group_list, ACR_Acquisition, + acr_find_int(group_list, ACR_Image, 1)); + + acr_insert_long(&group_list, ACR_Acquisitions_in_series, + acr_find_int(group_list, ACR_Nr_of_averages, 1)); + } + + /* 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 (max_group >= ACR_IMAGE_GID) { + + /* 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 (max_group >= ACR_IMAGE_GID) */ + + /* 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 : +---------------------------------------------------------------------------- */ +void +update_coordinate_info(Acr_Group group_list) +{ + Acr_Element element; + int nrows, ncolumns; + int i; + double coord[WORLD_NDIMS]; + double row[WORLD_NDIMS]; + double column[WORLD_NDIMS]; + double normal[WORLD_NDIMS]; + double pixel_spacing[IMAGE_NDIMS]; + string_t string; + int n_slices; + + if (G.Debug >= HI_LOGGING) { + printf("update_coordinate_info(%lx)\n", (unsigned long) group_list); + } + + /* Look for the normal vector + */ + element = acr_find_group_element(group_list, SPI_Image_normal); + if ((element == NULL) || + (acr_get_element_numeric_array(element, WORLD_NDIMS, normal) + != WORLD_NDIMS)) { + normal[XCOORD] = 0.0; + normal[YCOORD] = 0.0; + normal[ZCOORD] = 1.0; + } + + /* 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[XCOORD] = 1.0; + row[YCOORD] = 0.0; + row[ZCOORD] = 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[XCOORD] = 0.0; + column[YCOORD] = 1.0; + column[ZCOORD] = 0.0; + } + + if (G.Debug >= HI_LOGGING) { + printf("R %.3f %.3f %.3f C %.3f %.3f %.3f N %.3f %.3f %.3f\n", + row[0], row[1], row[2], + column[0], column[1], column[2], + normal[0], normal[1], normal[2]); + } + + /* Put in the dicom orientation (patient) field + */ + sprintf(string, "%.15g\\%.15g\\%.15g\\%.15g\\%.15g\\%.15g", + row[XCOORD], -row[YCOORD], -row[ZCOORD], + column[XCOORD], -column[YCOORD], -column[ZCOORD]); + acr_insert_string(&group_list, ACR_Image_orientation_patient, 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[XCOORD] = 0.0; + coord[YCOORD] = 0.0; + coord[ZCOORD] = 0.0; + } + else { + if (G.Debug >= HI_LOGGING) { + printf(" old %.3f %.3f %.3f, ", coord[0], coord[1], coord[2]); + } + } + + /* 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)) { + printf("ERROR: Illegal image size in Siemens IMA file\n"); + exit(1); + } + + + /* Get the pixel size */ + element = acr_find_group_element(group_list, ACR_Pixel_size); + 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 (i = 0; i < WORLD_NDIMS; i++) { + coord[i] -= + pixel_spacing[0] * ((double) ncolumns - 1.0) / 2.0 * row[i] + + pixel_spacing[1] * ((double) nrows - 1.0) / 2.0 * column[i]; + } + sprintf(string, "%.15g\\%.15g\\%.15g", coord[0], -coord[1], -coord[2]); + acr_insert_string(&group_list, ACR_Image_position_patient, string); + if (G.Debug >= HI_LOGGING) { + printf(" new %.3f %.3f %.3f\n", coord[0], -coord[1], -coord[2]); + } + + /* Copy non-standard fields to standard fields. + */ + group_list = copy_spi_to_acr(group_list); + + /* If this is a Mosaic image, we need to adjust the pixel spacing + * to reflect the ratio between the number of mosaic columns + * divided the number of image columns. + */ + n_slices = acr_find_int(group_list, EXT_Slices_in_file, 1); + if (n_slices != 1) { + int acq_cols = acr_find_short(group_list, SPI_Acquisition_columns, + ncolumns); + if (G.Debug >= HI_LOGGING) { + printf("Hmmm... This appears to be a mosaic image %d %d\n", + acq_cols, ncolumns); + } + + /* Mosaic images in IMA format appear to need to have their + * pixel spacing scaled up. I don't fully understand why this + * should be necessary, but there it is... + */ + pixel_spacing[0] *= (double) nrows / (double) acq_cols; + pixel_spacing[1] *= (double) ncolumns / (double) acq_cols; + sprintf(string, "%.15g\\%.15g", pixel_spacing[0], pixel_spacing[1]); + acr_insert_string(&group_list, ACR_Pixel_size, string); + } +} + +/* ----------------------------- MNI Header ----------------------------------- +@NAME : create_<type>_element +@INPUT : grp_id + elm_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 : +---------------------------------------------------------------------------- */ + +static Acr_Element_Id +get_elid(int grp_id, int elm_id, Acr_VR_Type vr_code) +{ + static struct Acr_Element_Id elid_struct = {0, 0, ACR_VR_UNKNOWN}; + elid_struct.group_id = grp_id; + elid_struct.element_id = elm_id; + elid_struct.vr_code = vr_code; + return &elid_struct; +} + +DEFINE_ELEMENT_FUNC(create_char_element) +{ + 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) { /* Assure even length overall */ + newsize++; + } + + /* Copy the string, making sure that there is a NUL on the end */ + new = malloc(newsize); + CHKMEM(new); + 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(grp_id, elm_id, ACR_VR_ST, + (long) newsize-1, (void *) new); +} + +DEFINE_ELEMENT_FUNC(create_long_element) +{ + long data_out; + + acr_get_long(ACR_BIG_ENDIAN, 1, data, &data_out); + + return acr_create_element_numeric(get_elid(grp_id, elm_id, ACR_VR_IS), + data_out); +} + +DEFINE_ELEMENT_FUNC(create_short_element) +{ + unsigned short data_out; + + acr_get_short(ACR_BIG_ENDIAN, 1, data, &data_out); + + return acr_create_element_short(get_elid(grp_id, elm_id, ACR_VR_US), + data_out); +} + +DEFINE_ELEMENT_FUNC(create_double_element) +{ + double data_out; + + acr_get_double(ACR_BIG_ENDIAN, 1, data, &data_out); + + return acr_create_element_numeric(get_elid(grp_id, elm_id, ACR_VR_DS), + data_out); +} + +DEFINE_ELEMENT_FUNC(create_ima_date_t_element) +{ + string_t string; + ima_date_t *ptr; + long year; + long month; + long day; + + ptr = (ima_date_t *) data; + + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->year, &year); + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->month, &month); + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->day, &day); + + if ((year < 1900) || (year > 9999)) + return NULL; + if ((month < 1) || (month > 12)) + return NULL; + if ((day < 1) || (day > 31)) + return NULL; + + sprintf(string, "%04d%02d%02d", (int) year, (int) month, (int) day); + + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_DA), + string); +} + +DEFINE_ELEMENT_FUNC(create_ima_time_t_element) +{ + string_t string; + ima_time_t *ptr; + long hour; + long minute; + long second; + long msec; + + ptr = (ima_time_t *) data; + + /* Convert data from big endian to native: + */ + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->hour, &hour); + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->minute, &minute); + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->second, &second); + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->msec, &msec); + + if ((hour < 0) || (hour >= 24)) + return NULL; + if ((minute < 0) || (minute >= 60)) + return NULL; + if ((second < 0) || (second >= 60)) + return NULL; + if ((msec < 0) || (msec > 999)) + return NULL; + + sprintf(string, "%02d%02d%02d.%03d", (int) hour, (int) minute, + (int) second, (int) msec); + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_TM), + string); +} + +DEFINE_ELEMENT_FUNC(create_modality_element) +{ + char *string; + int32_t modality; + + /* Get the appropriate string */ + + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) data, (long *) &modality); + switch (modality) { + case 1: + string = "CT"; + break; + case 2: + string = "MR"; + break; + default: + return NULL; + } + + /* Return a new element */ + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), + string); +} + +DEFINE_ELEMENT_FUNC(create_sex_element) +{ + char *string; + int32_t sex; + + /* Get the appropriate string */ + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) data, (long *) &sex); + switch (sex) { + case 1: + string = "F "; + break; + case 2: + string = "M "; + break; + case 3: + string = "O "; + break; + default: + return NULL; + } + + /* Return a new element */ + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), + string); +} + +DEFINE_ELEMENT_FUNC(create_age_element) +{ + string_t string; + int i; + int is_ok; + + is_ok = 1; + + /* The age string has a fixed length of 4 */ + memcpy(string, data, 4); + string[4] = '\0'; + + for (i = 0; i < 3; i++) { + if (string[i] < '0' || string[i] > '9') { + is_ok = 0; + } + } + if (string[3] != 'Y' && + string[3] != 'M' && + string[3] != 'W' && + string[3] != 'D') { + is_ok = 0; + } + if (!is_ok) { + printf("WARNING: Invalid age field '%s'\n", string); + return NULL; + } + + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_AS), + string); +} + +DEFINE_ELEMENT_FUNC(create_slice_order_element) +{ + char *string; + ima_slice_order_t slice_order; + + /* Get the appropriate string */ + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) data, (long *) &slice_order); + switch (slice_order) { + case SO_ASCENDING: + string = "ASCENDING "; + break; + case SO_DESCENDING: + string = "DESCENDING "; + break; + case SO_INTERLEAVED: + string = "INTERLEAVED "; + break; + case SO_NONE: + string = "NONE "; + break; + default: + string = "UNDEFINED "; + break; + } + + /* Return a new element */ + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), + string); +} + +DEFINE_ELEMENT_FUNC(create_pixel_spacing_t_element) +{ + pixel_spacing_t *ptr; + string_t string; + double row; + double col; + + /* Get the pixel sizes */ + ptr = (pixel_spacing_t *) data; + + /* Convert from big endian to native format */ + acr_get_double(ACR_BIG_ENDIAN, 1, (double *) &ptr->row, &row); + acr_get_double(ACR_BIG_ENDIAN, 1, (double *) &ptr->col, &col); + + sprintf(string, "%.15g\\%.15g", row, col); + + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_DS), + string); +} + +DEFINE_ELEMENT_FUNC(create_window_t_element) +{ + window_t *ptr; + string_t string; + long x; + long y; + + ptr = (window_t *) data; /* Get the window info */ + + /* Convert from big endian to native format */ + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->x, &x); + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->y, &y); + + sprintf(string, "%ld\\%ld", x, y); + + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_IS), + string); +} + +DEFINE_ELEMENT_FUNC(create_ima_vector_t_element) +{ + ima_vector_t *ptr; + string_t string; + double x, y, z; + + /* Get the coordinate */ + ptr = (ima_vector_t *) data; + + acr_get_double(ACR_BIG_ENDIAN, 1, (double *) &ptr->x, &x); + acr_get_double(ACR_BIG_ENDIAN, 1, (double *) &ptr->y, &y); + acr_get_double(ACR_BIG_ENDIAN, 1, (double *) &ptr->z, &z); + + sprintf(string, "%.15g\\%.15g\\%.15g", x, y, z); + + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_DS), + string); +} + +DEFINE_ELEMENT_FUNC(create_laterality_element) +{ + long laterality; + char *string; + + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) data, &laterality); + + switch (laterality) { + case 1: + string = "L "; break; + case 2: + string = ""; break; + case 3: + string = "R "; break; + default: + return NULL; + } + + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), + string); +} + +DEFINE_ELEMENT_FUNC(create_ima_position_t_element) +{ + ima_position_t position; + char *string; + + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) data, (long *) &position); + switch (position) { + case PP_LEFT: + string = "HFL"; break; + case PP_RIGHT: + string = "HFR"; break; + case PP_PRONE: + string = "HFP"; break; + case PP_SUPINE: + string = "HFS"; break; + default: + string = ""; break; + } + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), + string); +} + +DEFINE_ELEMENT_FUNC(create_rest_direction_t_element) +{ + ima_rest_direction_t dir; + char *string; + + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) data, (long *) &dir); + switch (dir) { + case RD_HEAD: + string = "HEAD"; + break; + case RD_FEET: + string = "FEET"; + break; + default: + string = ""; + break; + } + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), + string); +} + +DEFINE_ELEMENT_FUNC(create_view_direction_t_element) +{ + ima_view_direction_t dir; + char *string; + + acr_get_long(ACR_BIG_ENDIAN, 1, (long *) data, (long *) &dir); + switch (dir) { + case VD_HEAD: + string = "HEAD"; + break; + case VD_FEET: + string = "FEET"; + break; + case VD_AtoP: + string = "AtoP"; + break; + case VD_LtoR: + string = "LtoR"; + break; + case VD_PtoA: + string = "PtoA"; + break; + case VD_RtoL: + string = "RtoL"; + break; + default: + string = ""; + break; + } + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), + string); +} + +static void +copy_to_space(char *dst_ptr, char *src_ptr, int max_chr) +{ + while (*src_ptr != ' ' && *src_ptr != '\0' && max_chr > 0) { + *dst_ptr++ = *src_ptr++; + max_chr--; + } + *dst_ptr = '\0'; +} + +DEFINE_ELEMENT_FUNC(create_ima_orientation_t_element) +{ + ima_orientation_t *po_ptr = (ima_orientation_t *) data; + char y[N_ORIENTATION + 1]; + char x[N_ORIENTATION + 1]; + char z[N_ORIENTATION + 1]; + string_t string; + + copy_to_space(y, po_ptr->y, N_ORIENTATION); + copy_to_space(x, po_ptr->x, N_ORIENTATION); + copy_to_space(z, po_ptr->z, N_ORIENTATION); + + sprintf(string, "%s\\%s\\%s", y, x, z); + + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), + string); +} + +DEFINE_ELEMENT_FUNC(create_field_of_view_t_element) +{ + ima_field_of_view_t *ptr; + string_t string; + double height; + double width; + + ptr = (ima_field_of_view_t *) data; + + acr_get_double(ACR_BIG_ENDIAN, 1, (double *) &ptr->height, &height); + acr_get_double(ACR_BIG_ENDIAN, 1, (double *) &ptr->width, &width); + + sprintf(string, "%.15g\\%.15g", height, width); + return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_DS), + string); +}
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/siemens_to_dicom.h @@ -0,0 +1,3 @@ +extern Acr_Group siemens_to_dicom(const char *filename, int max_group); +extern void update_coordinate_info(Acr_Group group_list); +
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/spi_element_defs.h @@ -0,0 +1,63 @@ +/* ----------------------------- MNI Header ----------------------------------- +@NAME : spi_element_defs.h +@DESCRIPTION: Element definitions for Siemens "Standard Product Interconnect" +@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. +---------------------------------------------------------------------------- */ + +/* Element id's for SPI */ +/* Most of this information is available at David Clunie's medical imaging + * website (www.dclunie.com). + */ +GLOBAL_ELEMENT(SPI_Number_of_data_bytes , 0x0019, 0x1060, IS); +GLOBAL_ELEMENT(SPI_Fourier_lines_nominal , 0x0019, 0x1220, IS); +GLOBAL_ELEMENT(SPI_Fourier_lines_after_zero , 0x0019, 0x1226, IS); +GLOBAL_ELEMENT(SPI_First_measured_fourier_line , 0x0019, 0x1228, IS); +GLOBAL_ELEMENT(SPI_Acquisition_columns , 0x0019, 0x1230, LO); +GLOBAL_ELEMENT(SPI_Reconstruction_columns , 0x0019, 0x1231, LO); +GLOBAL_ELEMENT(SPI_Number_of_averages , 0x0019, 0x1250, IS); +GLOBAL_ELEMENT(SPI_Flip_angle , 0x0019, 0x1260, DS); +GLOBAL_ELEMENT(SPI_Number_of_prescans , 0x0019, 0x1270, IS); +GLOBAL_ELEMENT(SPI_Saturation_regions , 0x0019, 0x1290, IS); +GLOBAL_ELEMENT(SPI_Magnetic_field_strength , 0x0019, 0x1412, DS); + +GLOBAL_ELEMENT(SPI_Field_of_view , 0x0021, 0x1120, DS); +GLOBAL_ELEMENT(SPI_Image_magnification_factor , 0x0021, 0x1122, DS); +GLOBAL_ELEMENT(SPI_View_direction , 0x0021, 0x1130, CS); +GLOBAL_ELEMENT(SPI_Rest_direction , 0x0021, 0x1132, CS); +GLOBAL_ELEMENT(SPI_Image_position , 0x0021, 0x1160, DS); +GLOBAL_ELEMENT(SPI_Image_normal , 0x0021, 0x1161, DS); +GLOBAL_ELEMENT(SPI_Image_distance , 0x0021, 0x1163, DS); +GLOBAL_ELEMENT(SPI_Image_row , 0x0021, 0x116a, DS); +GLOBAL_ELEMENT(SPI_Image_column , 0x0021, 0x116b, DS); +GLOBAL_ELEMENT(SPI_Patient_orientation_set1 , 0x0021, 0x1170, CS); +GLOBAL_ELEMENT(SPI_Patient_orientation_set2 , 0x0021, 0x1171, CS); +GLOBAL_ELEMENT(SPI_Study_name , 0x0021, 0x1180, CS); +GLOBAL_ELEMENT(SPI_Study_type , 0x0021, 0x1182, CS); +GLOBAL_ELEMENT(SPI_Number_of_3D_raw_partitions_nominal, 0x0021, 0x1330, IS); +GLOBAL_ELEMENT(SPI_Number_of_3d_raw_part_cur , 0x0021, 0x1331, 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_Slab_thickness , 0x0021, 0x1339, DS); +GLOBAL_ELEMENT(SPI_Number_of_slices_nominal , 0x0021, 0x1340, IS); +GLOBAL_ELEMENT(SPI_Number_of_slices_cur , 0x0021, 0x1341, IS); +GLOBAL_ELEMENT(SPI_Current_slice_number , 0x0021, 0x1342, IS); +GLOBAL_ELEMENT(SPI_Order_of_slices , 0x0021, 0x134f, IS); +GLOBAL_ELEMENT(SPI_Number_of_echoes , 0x0021, 0x1370, IS); + +GLOBAL_ELEMENT(SPI_Protocol , 0x0029, 0x1020, CS); + +
new file mode 100644 --- /dev/null +++ b/conversion/dcm2mnc/string_to_filename.c @@ -0,0 +1,323 @@ +/* ----------------------------- 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.2.2.1 2005-05-12 21:16:48 bert + * Initial checkin + * + * Revision 1.2 2005/03/03 18:59:16 bert + * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) + * + * Revision 1.1 2005/02/17 16:38:11 bert + * Initial checkin, revised DICOM to MINC converter + * + * Revision 1.1.1.1 2003/08/15 19:52:55 leili + * Leili's dicom server for sonata + * + * 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. +---------------------------------------------------------------------------- */ + +static const char rcsid[] = "$Header: /private-cvsroot/minc/conversion/dcm2mnc/string_to_filename.c,v 1.2.2.1 2005-05-12 21:16:48 bert Exp $"; + +#include "dcm2mnc.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 : +---------------------------------------------------------------------------- */ +void string_to_filename(const 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; +} + +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; +} + +