From 57a3e9d7dcfbe485fce2bcad2d25f39594261d29 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Fri, 21 Jan 2022 08:55:45 -0500 Subject: [PATCH] Detect echo number for Siemens XA that omit 0018,0086 (https://github.com/rordenlab/dcm2niix/issues/568) --- README.md | 1 + Siemens/README.md | 20 ++++++++++++++++++++ console/nii_dicom.cpp | 17 ++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ffeabf16..9da60861 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ The following tools exploit dcm2niix - [Brain imAgiNg Analysis iN Arcana (Banana)](https://pypi.org/project/banana/) is a collection of brain imaging analysis workflows, it uses dcm2niix for format conversions. - [BraTS-Preprocessor](https://neuronflow.github.io/BraTS-Preprocessor/) uses dcm2niix to import files for [Brain Tumor Segmentation](https://www.frontiersin.org/articles/10.3389/fnins.2020.00125/full). - [clinica](https://github.com/aramis-lab/clinica) is a software platform for clinical neuroimaging studies that uses dcm2niix to convert DICOM images. + - [bidsconvertr](https://github.com/wulms/bidsconvertr) uses R to converts DICOM data to NIfTI and finally to BIDS. - [bidsify](https://github.com/spinoza-rec/bidsify) is a Python project that uses dcm2niix to convert DICOM and Philips PAR/REC images to the BIDS standard. - [bidskit](https://github.com/jmtyszka/bidskit) uses dcm2niix to create [BIDS](http://bids.neuroimaging.io/) datasets. - [BioImage Suite Web Project](https://github.com/bioimagesuiteweb/bisweb) is a JavaScript project that uses dcm2niix for its DICOM conversion module. diff --git a/Siemens/README.md b/Siemens/README.md index 8609f80a..d898e2e8 100644 --- a/Siemens/README.md +++ b/Siemens/README.md @@ -15,6 +15,7 @@ While X-series consoles allow users to export data as enhanced, mosaic or classi When creating enhanced DICOMs diffusion information is provided in public tags. Based on a limited sample, it seems that classic DICOMs do not store diffusion data for XA10, and use private tags for [XA11](https://www.nitrc.org/forum/forum.php?thread_id=10013&forum_id=4703). Public Tags + ``` (0018,9089) FD -0.20\-0.51\-0.83 #DiffusionGradientOrientation (0018,9087) FD 1000 #DiffusionBValue @@ -22,6 +23,7 @@ Public Tags ``` Private Tags + ``` (0019,100c) IS 1000 #SiemensDiffusionBValue (0019,100e) FD -0.20\-0.51\-0.83 #SiemensDiffusionGradientOrientation @@ -30,6 +32,24 @@ Private Tags In theory, the public DICOM tag 'Frame Acquisition Date Time' (0018,9074) and the private tag 'Time After Start' (0021,1104) should each allow one to infer slice timing. The tag 0018,9074 uses the DT (date time) format, for example "20190621095520.330000" providing the YYYYYMMDDHHMMSS. Unfortunately, the Siemens de-identification routines will scramble these values, as time of data could be considered an identifiable attribute. The tag 0021,1104 is saved in DS (decimal string) format, for example "4.635" reporting the number of seconds since acquisition started. Be aware that some [Siemens Vida multi-band sequences](https://github.com/rordenlab/dcm2niix/issues/303) appear to fill these tags with the single-band times rather than the actual acquisition times. Therefore, neither of these two methods is perfectly reliable in determining slice timing. +The private `ICE_Dims` (0021,1106) tag can prove useful for parsing data. The list below is specific to XA scans: [SPM12](https://github.com/spm/spm12/blob/3085dac00ac804adb190a7e82c6ef11866c8af02/spm_dicom_convert.m#L268) suggests that this tag used to contain fewer elements. dcm2niix will use 0021,1106 to deduce echo number for [XA20 sequences that do not generate the public Echo Number (0018,0086)](https://github.com/rordenlab/dcm2niix/issues/568) tag. For example, consider an image of the 4th echo and 160th slice: + +``` +(0021,1106) LO [X_4_1_1_1_1_160_1_1_1_1_1_277] # ICE_Dims +``` + +1. eco = echo number +2. phs = phase encode +3. set = +4. rep = repetition +5. seg = segment +6. par = partition +7. slc = slice +8. idA = optional index +9. idB = optional index +10. idC = optional index +11. avg = average number + ## CSA Header Many crucial Siemens parameters are stored in the [proprietary CSA header](http://nipy.org/nibabel/dicom/siemens_csa.html), in particular the CSA Image Header Info (0029, 1010) and CSA Series Header Info (0029, 1020). These have binary sections that allows quick reading for many useful parameters. They also include an ASCII text portion that includes a lot of information but is slow to parse and poorly curated. Be aware that Siemens Vida scanners do not generate a CSA header. diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index b3ed3331..4c0f4dcf 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -916,7 +916,7 @@ void dcmStrDigitsDotOnlyKey(char key, char *lStr) { } else if (!isKey) lStr[i] = ' '; } -} //dcmStrDigitsOnlyKey() +} //dcmStrDigitsDotOnlyKey() void dcmStrDigitsOnlyKey(char key, char *lStr) { //e.g. string "p2s3" returns 2 if key=="p" and 3 if key=="s" @@ -4290,6 +4290,7 @@ const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16); #define kSequenceVariant21 0x0021 + (0x105B << 16) //CS #define kPATModeText 0x0021 + (0x1009 << 16) //LO, see kImaPATModeText #define kTimeAfterStart 0x0021 + (0x1104 << 16) //DS +#define kICE_dims 0x0021 + (0x1106 << 16) //LO [X_4_1_1_1_1_160_1_1_1_1_1_277] #define kPhaseEncodingDirectionPositiveSiemens 0x0021 + (0x111C << 16) //IS //#define kRealDwellTime 0x0021+(0x1142<< 16 )//IS #define kBandwidthPerPixelPhaseEncode21 0x0021 + (0x1153 << 16) //FD @@ -5800,6 +5801,20 @@ const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16); //printf("x\t%d\t%g\tkTimeAfterStart\n", acquisitionTimesGE_UIH, d.CSA.sliceTiming[acquisitionTimesGE_UIH]); acquisitionTimesGE_UIH++; break; + case kICE_dims: { //issue568: LO (0021,1106) [X_4_1_1_1_1_160_1_1_1_1_1_277] + if ((d.manufacturer != kMANUFACTURER_SIEMENS) || (d.echoNum > 1)) + break; + char iceStr[kDICOMStr]; + dcmStr(lLength, &buffer[lPos], iceStr); + dcmStrDigitsOnly(iceStr); + char *end; + int echo = (int)strtol(iceStr, &end, 10); + //printMessage("%d:%d:'%s'\n", d.echoNum, echo, iceStr); + if (iceStr != end) + d.echoNum = echo; + //printMessage("%d:'%s'\n", echo, iceStr); + break; + } case kPhaseEncodingDirectionPositiveSiemens: { if (d.manufacturer != kMANUFACTURER_SIEMENS) break;