From 446419e23494e040884a68997f3db84cf8d0f0b4 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Thu, 25 Jul 2024 16:48:56 -0700 Subject: [PATCH] TablePosition (https://github.com/rordenlab/dcm2niix/issues/726) --- BIDS/README.md | 12 ++++++++---- console/nii_dicom.cpp | 27 +++++++++++++++++++++++++++ console/nii_dicom.h | 2 +- console/nii_dicom_batch.cpp | 4 ++++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/BIDS/README.md b/BIDS/README.md index 31a440da..1b5a677b 100644 --- a/BIDS/README.md +++ b/BIDS/README.md @@ -244,8 +244,9 @@ Data unique to [GE](https://github.com/rordenlab/dcm2niix/tree/master/GE). Deter | ASLLabelingTechnique | | DICOM tag 0043,10A4 | D | | LabelingDuration | s | DICOM tag 0043,10A5 | B | | SliceTiming | s | [see notes](https://github.com/rordenlab/dcm2niix/tree/master/GE#slice-timing) | B | -| CompressedSensingFactor | | DICOM tag 0043,10b7 | D | -| DeepLearningFactor | | DICOM tag 0043,10ca | D | +| CompressedSensingFactor | | DICOM tag 0043,10B7 | D | +| TablePosition | mm | DICOM tag 0043,10B2 | B | +| DeepLearningFactor | | DICOM tag 0043,10CA | D | ### Manufacturer Philips @@ -258,11 +259,12 @@ Data unique to Philips, including [custom intensity scaling](https://www.ncbi.nl | PhilipsRWVIntercept | | DICOM tag 0040,9224 | D | | PhilipsRescaleSlope | | DICOM tag 0028,1053 | D | | PhilipsRescaleIntercept | | DICOM tag 0028,1052 | D | -| PhilipsScaleSlope | | DICOM tag 2005,100E | D | | UsePhilipsFloatNotDisplayScaling | | dcm2niix option `-p y` or `-p n` | D | | PartialFourierEnabled | | DICOM tag 0018,9081, `YES` | D | | PhaseEncodingStepsNoPartialFourier | | DICOM tag 0018,9231 | D | | WaterFatShift | | DICOM tag 2001,1022 | D | +| PhilipsScaleSlope | | DICOM tag 2005,100E | D | +| TablePosition | mm | DICOM tag 2005,143D | B | ### Manufacturer Siemens (Arterial Spin Labeling) @@ -332,6 +334,7 @@ Fields specific to Siemens V*-series (e.g. VB, VE) MRI systems (e.g. Verio, Trio | ConsistencyInfo | | The more complete software version, e.g. VE11C or VE11E instead of just VE11. | D | | CoilCombinationMethod | | Detects `Sum of Squares` and `Adaptive Combine` | B | | MatrixCoilMode | | Detects `SENSE` and `GRAPPA` | B | +| TablePosition | mm | DICOM tag 0019,1014 | B | | DwellTime | | DICOM tag 0019,1018 | B | | BandwidthPerPixelPhaseEncode | Hz | DICOM tag 0019,1028 | D | | ImageOrientationText | | DICOM tag 0051,100E | D | @@ -347,9 +350,10 @@ Fields specific to [Siemens XA-series](https://github.com/rordenlab/dcm2niix/tre | BandwidthPerPixelPhaseEncode | Hz | DICOM tag 0021,1153 | D | | ScanningSequence | | DICOM tag 0021,105a | D | | PostLabelingDelay | s | DICOM tag 0018,9258 | B | +| TablePosition | mm | DICOM tag 0021,1005 | B | | NonlinearGradientCorrection | b | 0008,0008 or 0021,1175 | B | | PhaseEncodingDirection | | polarity from 0021,111c | B | -| SpoilingState | | 0021,105B | B | +| SpoilingState | | DICOM tag 0021,105B | B | Siemens also includes some sequence information in the private MRPhoenixProtocol (0021,1019) tag. You can view this with [gdcmdump](https://gdcm.sourceforge.net/html/gdcmdump.html), e.g. `gdcmdump --mrprotocol img.dcm`. Fields that dcm2niix inspects include `sPat.lAccelFact3D `, `sPat.lAccelFactPE`, `sPat.lRefLinesPE` and `sPat.ucPATMode`. The behavior of dcm2niix will be more well documented as our understanding of this tag improves. diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 1204773c..7e192940 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -945,6 +945,10 @@ struct TDICOMdata clear_dicom_data() { strcpy(d.patientAge, ""); d.CSA.bandwidthPerPixelPhaseEncode = 0.0; d.CSA.mosaicSlices = 0; + d.CSA.tablePos[0] = -1.0; //unset + d.CSA.tablePos[1] = 0.0; // x + d.CSA.tablePos[2] = 0.0; // y + d.CSA.tablePos[3] = 0.0; // z d.CSA.sliceNormV[1] = 0.0; d.CSA.sliceNormV[2] = 0.0; d.CSA.sliceNormV[3] = 1.0; //default Siemens Image Numbering is F>>H https://www.mccauslandcenter.sc.edu/crnl/tools/stc @@ -4472,6 +4476,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD // https://nciphub.org/groups/qindicom/wiki/DiffusionrelatedDICOMtags:experienceacrosssites?action=pdf #define kDiffusion_bValueSiemens 0x0019 + (0x100C << 16) //IS #define kDiffusionGradientDirectionSiemens 0x0019 + (0x100E << 16) //FD +#define kImaRelTablePosition 0x0019 + (0x1014 << 16) //IS[] #define kSeriesPlaneGE 0x0019 + (0x1017 << 16) //SS #define kDwellTime 0x0019 + (0x1018 << 16) //IS in NSec, see https://github.com/rordenlab/dcm2niix/issues/127 #define kLastScanLoc 0x0019 + (0x101B << 16) @@ -4513,6 +4518,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kTemporalPositionIndex 0x0020 + uint32_t(0x9128 << 16) // UL #define kDimensionIndexPointer 0x0020 + uint32_t(0x9165 << 16) //Private Group 21 as Used by Siemens: +#define kRelTablePosition 0x0021 + (0x1005 << 16) //IS #define kScanningSequenceSiemens 0x0021 + (0x105A << 16) //CS n.b. for GE this is Diffusion direction of SL! #define kSequenceVariant21 0x0021 + (0x105B << 16) //CS Siemens ONLY: For GE this is TaggingFlipAngle #define kScanOptionsSiemens 0x0021 + (0x105C << 16) //CS Siemens ONLY @@ -4643,6 +4649,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kDiffusionDirectionFH 0x2005 + (0x10B2 << 16) #define kDeepLearningPhilips 0x2005 + (0x1110 << 16) #define kPrivatePerFrameSq 0x2005 + (0x140F << 16) +#define kMRStackTablePosLong 0x2005 + (0x143C << 16) //FL #define kMRImageDiffBValueNumber 0x2005 + (0x1412 << 16) //IS #define kMRImageGradientOrientationNumber 0x2005+(0x1413 << 16) //IS #define kMRImageLabelType 0x2005 + (0x1429 << 16) //CS ASL LBL_CTL https://github.com/physimals/dcm_convert_phillips/ @@ -5950,6 +5957,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.CSA.dtiV[3] = v[2]; break; } + case kImaRelTablePosition: { + if (d.manufacturer != kMANUFACTURER_SIEMENS) + break; + dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &d.CSA.tablePos[0]); //slice position + d.CSA.tablePos[0] = 1.0; //set + break; + } case kNumberOfDiffusionT2GE: { if (d.manufacturer != kMANUFACTURER_GE) break; @@ -6755,6 +6769,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.isEPI = true; break; //warp } + case kRelTablePosition: { + if (d.manufacturer != kMANUFACTURER_SIEMENS) + break; + dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &d.CSA.tablePos[0]); //slice position + d.CSA.tablePos[0] = 1.0; //set + break; + } case kScanningSequenceSiemens: if (d.manufacturer == kMANUFACTURER_SIEMENS) dcmStr(lLength, &buffer[lPos], scanningSequenceSiemens); @@ -7093,6 +7114,12 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (toupper(buffer[lPos]) == 'L') d.aslFlags = kASL_FLAG_PHILIPS_LABEL; if (toupper(buffer[lPos]) == 'C') d.aslFlags = kASL_FLAG_PHILIPS_CONTROL; break; + case kMRStackTablePosLong: //FL + if (d.manufacturer != kMANUFACTURER_PHILIPS) + break; + d.CSA.tablePos[3] = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); + d.CSA.tablePos[0] = 1.0; + break; case kMRImageDiffBValueNumber: if (d.manufacturer != kMANUFACTURER_PHILIPS) break; diff --git a/console/nii_dicom.h b/console/nii_dicom.h index 6d3546df..39200ef0 100644 --- a/console/nii_dicom.h +++ b/console/nii_dicom.h @@ -228,7 +228,7 @@ static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8; } TCSAitem; //Siemens csa item structure #endif struct TCSAdata { - float sliceTiming[kMaxEPI3D], dtiV[4], sliceNormV[4], bandwidthPerPixelPhaseEncode, sliceMeasurementDuration; + float sliceTiming[kMaxEPI3D], dtiV[4], sliceNormV[4], bandwidthPerPixelPhaseEncode, sliceMeasurementDuration, tablePos[4]; int coilNumber, numDti, SeriesHeader_offset, SeriesHeader_length, multiBandFactor, sliceOrder, slice_start, slice_end, mosaicSlices, protocolSliceNumber1, phaseEncodingDirectionPositive; bool isPhaseMap; char bidsDataType[kDICOMStr]; //anat, func, dwi diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 387184d0..cd65c820 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1674,7 +1674,11 @@ tse3d: T2*/ json_Float(fp, "\t\"SpacingBetweenSlices\": %g,\n", d.zSpacing); } //if (!opts.isAnonymizeBIDS) //issue668 is SAR identifiable?? + //an identical sequence can create different SAR and TablePosition depending on participant json_Float(fp, "\t\"SAR\": %g,\n", d.SAR); + if (d.CSA.tablePos[0] > 0.0) + fprintf(fp, "\t\"TablePosition\": [\n\t\t%g,\n\t\t%g,\n\t\t%g\t],\n", d.CSA.tablePos[1], d.CSA.tablePos[2], d.CSA.tablePos[3]); + if (d.numberOfAverages > 1.0) json_Float(fp, "\t\"NumberOfAverages\": %g,\n", d.numberOfAverages); if ((d.echoNum > 1) || ((d.isMultiEcho) && (d.echoNum > 0)))