Mercurial > hg > minc-tools
changeset 2302:f5a4d16970d6
Add functions for traversing DICOM sequences and recursively hunting for needed fields.
author | bert <bert> |
---|---|
date | Mon, 20 Jun 2005 22:03:01 +0000 |
parents | a29a17451d6b |
children | e1fb1a03d312 |
files | conversion/dcm2mnc/dicom_read.c |
diffstat | 1 files changed, 253 insertions(+), 102 deletions(-) [+] |
line wrap: on
line diff
--- a/conversion/dcm2mnc/dicom_read.c +++ b/conversion/dcm2mnc/dicom_read.c @@ -7,7 +7,10 @@ @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: dicom_read.c,v $ - * Revision 1.16.2.2 2005-06-02 17:04:26 bert + * Revision 1.16.2.3 2005-06-20 22:03:01 bert + * Add functions for traversing DICOM sequences and recursively hunting for needed fields. + * + * Revision 1.16.2.2 2005/06/02 17:04:26 bert * 1. Fix handling of signed values for pixel minimum and maximum, 2. Set found_dircos[] when we adopt the default direction cosines for files with null direction cosines * * Revision 1.16.2.1 2005/05/12 21:16:47 bert @@ -162,7 +165,7 @@ #include "dcm2mnc.h" -#include <math.h> +#define IMAGE_NDIMS 2 static double convert_time_to_seconds(double dicom_time); static void get_intensity_info(Acr_Group group_list, File_Info *file_info); @@ -177,7 +180,6 @@ 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); @@ -878,6 +880,213 @@ } +/* Function to recursively search an element list for a specific + * element, skipping a specified number of occurrences before + * returning. This is only called by acr_recurse_for_element(). + */ +static Acr_Element +acr_recursive_search(Acr_Element el_lst, int *nskip, Acr_Element_Id srch_id) +{ + Acr_Element el_ret = NULL; + Acr_Element el_tmp; + int i; + + for (el_tmp = el_lst; el_tmp != NULL; + el_tmp = acr_get_element_next(el_tmp)) { + + /* If we find what we're looking for, return it. + */ + if (acr_get_element_group(el_tmp) == srch_id->group_id && + acr_get_element_element(el_tmp) == srch_id->element_id) { + if (*nskip <= 0) { + el_ret = el_tmp; + break; + } + else { + --(*nskip); + } + } + /* See if we need to recurse. + */ + if (acr_element_is_sequence(el_tmp)) { + el_lst = (Acr_Element) acr_get_element_data(el_tmp); + el_ret = acr_recursive_search(el_lst, nskip, srch_id); + if (el_ret != NULL) { + break; + } + } + } + return (el_ret); +} + +/* acr_recurse_for_element() + * + * Function to search a group list for a particular element. Unlike other + * functions along these lines, this function will recursively descend into + * compound datatypes (DICOM sequences) to hunt for instances of a particular + * element. + * + * The search proceeds in two stages: The first is to search for + * a particular sequence object. This must be found or else the search is + * called off. Once the expected sequences is found, the function will + * recursively search all of the substructure of that sequence for the + * requested subelement. The "nskip" parameter tells the function to ignore + * the first "nskip" matches that it locates. + */ +Acr_Element +acr_recurse_for_element(Acr_Group group_list, + int nskip, + Acr_Element_Id seq_id, + Acr_Element_Id srch_id) +{ + Acr_Element el_seq; + + /* Hunt for the necessary sequence object. + */ + el_seq = acr_find_group_element(group_list, seq_id); + if (el_seq == NULL || !acr_element_is_sequence(el_seq)) { + /* If not found, or not a sequence, abort the search. + */ + return (NULL); + } + + /* Otherwise proceed to "stage 2" and hunt for the requested subelement. + */ + return acr_recursive_search((Acr_Element) acr_get_element_data(el_seq), + &nskip, srch_id); +} + +int +dicom_read_position(Acr_Group group_list, int n, double coordinate[3]) +{ + Acr_Element element; + int result; + + /* Try to read a unique element from the sequences. If this + * succeeds, we need to flag this fact so that the higher-level + * processing can adapt accordingly. + */ + element = acr_recurse_for_element(group_list, n, + ACR_Perframe_func_groups_seq, + ACR_Image_position_patient); + if (element != NULL) { + result = DICOM_POSITION_LOCAL; /* Found a slice-specific position */ + } + else { + result = DICOM_POSITION_GLOBAL; /* Found a global position */ + + 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 (acr_get_element_numeric_array(element, + WORLD_NDIMS, + coordinate) == WORLD_NDIMS) { + return (result); + } + + if (G.Debug) { + printf("WARNING: Failed to read image position ('%s')\n", + acr_get_element_string(element)); + } + } + return DICOM_POSITION_NONE; +} + +int +dicom_read_orientation(Acr_Group group_list, double orientation[6]) +{ + Acr_Element element; + int result; + + /* read in row/col vectors: + */ + /* Try to find the element buried in a sequence. + */ + element = acr_recurse_for_element(group_list, 0, + ACR_Shared_func_groups_seq, + ACR_Image_orientation_patient); + if (element == NULL) { + 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"); + return (0); + } + else if ((result = acr_get_element_numeric_array(element, 6, + orientation)) != 6) { + printf("WARNING: Failed to read image orientation! (%d, '%s')\n", + result, acr_get_element_string(element)); + return (0); + } + return (1); +} + +/* + * Read the pixel size, an array of 2 floating point numbers, from the + * DICOM group list. + */ +int +dicom_read_pixel_size(Acr_Group group_list, double pixel_size[IMAGE_NDIMS]) +{ + Acr_Element element; + int result = 0; + int i; + + for (i = 0; i < IMAGE_NDIMS; i++) { + pixel_size[i] = -DBL_MAX; + } + + element = acr_recurse_for_element(group_list, 0, + ACR_Shared_func_groups_seq, + ACR_Pixel_size); + if (element == NULL) { + element = acr_find_group_element(group_list, ACR_Pixel_size); + } + if (element == NULL) { + printf("WARNING: Can't find pixel size element\n"); + } + else { + if (acr_get_element_numeric_array(element, IMAGE_NDIMS, + + pixel_size) != IMAGE_NDIMS) { + printf("WARNING: Can't read pixel size element\n"); + } + else { + result = 1; + } + } + + /* If the values are still uninitialized, set them to some reasonable + * defaults. + */ + if (pixel_size[0] == -DBL_MAX) { + pixel_size[0] = 1.0; /* Assume 1mm spacing */ + } + + if (pixel_size[1] == -DBL_MAX) { + pixel_size[1] = pixel_size[0]; /* Assume uniform sample grid. */ + } + return (result); +} + /* ----------------------------- MNI Header ----------------------------------- @NAME : get_coordinate_info @INPUT : group_list - input data @@ -898,10 +1107,6 @@ @MODIFIED : ---------------------------------------------------------------------------- */ -#define SPECIAL_CASE_IMA 1 - -#define DARRAY_SIZE 2 - static void get_coordinate_info(Acr_Group group_list, File_Info *fi_ptr, @@ -922,7 +1127,7 @@ double start_time; double magnitude; double largest; - double darray[DARRAY_SIZE]; + double psize[IMAGE_NDIMS]; double dbl_tmp1, dbl_tmp2; int result; @@ -942,20 +1147,26 @@ } 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. + * understand, the Siemens Numaris 3 DICOM image orientation does not + * give the correct direction cosines, so we use the nonstandard Siemens + * fields instead. Someday I should figure out the relation (if any) + * between the standard fields and these fields, and try to normalize + * this mess. + * + * We only attempt this for files that are clearly marked as SIEMENS + * files, with a version string that looks like VB33 (VB33D, VB33G, etc.) + * Later versions do not seem to use these fields. */ - if (G.file_type == N3DCM || G.file_type == IMA) { + if (strstr(acr_find_string(group_list, ACR_Manufacturer, ""), "SIEMENS") && + strstr(acr_find_string(group_list, ACR_Software_versions, ""), "VB33")) { 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.) */ + * 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; @@ -968,8 +1179,8 @@ if (element == NULL) { continue; } - if (acr_get_element_numeric_array(element, WORLD_NDIMS, dircos[ivolume]) - != WORLD_NDIMS) { + if (acr_get_element_numeric_array(element, WORLD_NDIMS, + dircos[ivolume]) != WORLD_NDIMS) { continue; } /* negate the X and Z coordinates @@ -978,28 +1189,12 @@ 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 { + + /* If we did not find the Siemens proprietary image vectors, try + * the DICOM standard image position. + */ + if (!found_dircos[VCOLUMN] || !found_dircos[VROW] || !found_dircos[VSLICE]) { + if (dicom_read_orientation(group_list, RowColVec)) { dircos[VCOLUMN][XCOORD] = RowColVec[0]; dircos[VCOLUMN][YCOORD] = RowColVec[1]; dircos[VCOLUMN][ZCOORD] = RowColVec[2]; @@ -1030,9 +1225,7 @@ 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", @@ -1117,24 +1310,11 @@ /* Get step information */ - for (ivolume=0; ivolume < DARRAY_SIZE; ivolume++) { - darray[ivolume] = -DBL_MAX; - } + + dicom_read_pixel_size(group_list, psize); - /* 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? */ + steps[VCOLUMN] = psize[0]; + steps[VROW] = psize[1]; /* anisotropic resolution? */ /* Figure out the slice thickness. It could be from either one of * two possible places in the file. @@ -1167,7 +1347,9 @@ printf("WARNING: slice thickness conflict: "); printf("old = %.10f, new = %.10f\n", dbl_tmp1, dbl_tmp2); } - steps[VSLICE] = dbl_tmp2; + /* Use the minimum slice spacing. */ + steps[VSLICE] = (dbl_tmp2 < dbl_tmp1) ? dbl_tmp2 : dbl_tmp1; + /* steps[VSLICE] = dbl_tmp2; */ } /* Make sure that direction cosines point the right way (dot @@ -1208,24 +1390,9 @@ 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; - } + found_coordinate = dicom_read_position(group_list, + fi_ptr->index[SLICE] - 1, + coordinate); if (!found_coordinate) { /* Last gasp - try to interpret the slice location as our slice * position. It might work. @@ -1342,7 +1509,7 @@ @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : made new dicom version (rhoge) ---------------------------------------------------------------------------- */ -static void +void convert_dicom_coordinate(double coordinate[WORLD_NDIMS]) { /* Allow the user to override this, if only for debugging purposes... @@ -1597,7 +1764,7 @@ } /* ----------------------------- MNI Header ----------------------------------- - @NAME : get_siemens_dicom_image + @NAME : get_dicom_image_data @INPUT : group_list - input data @OUTPUT : image - image data structure (user must free data) @RETURNS : (nothing) @@ -1609,14 +1776,13 @@ @MODIFIED : ---------------------------------------------------------------------------- */ void -get_siemens_dicom_image(Acr_Group group_list, Image_Data *image) +get_dicom_image_data(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; @@ -1625,11 +1791,9 @@ /* 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); + image_group = acr_find_short(group_list, ACR_Image_location, ACR_IMAGE_GID); /* Figure out type */ if (bits_alloc > CHAR_BIT) @@ -1638,11 +1802,9 @@ 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; + image->data = (unsigned short *) malloc(imagepix * sizeof(short)); + CHKMEM(image->data); /* Get image pointer */ elid.group_id = image_group; @@ -1760,18 +1922,7 @@ 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; - } - } + di_ptr->coord_found = dicom_read_position(group_list, 0, slice_coord); /* identification info needed to generate unique session id * for file names