Skip to content

Commit

Permalink
New encodingSteps, clean up OutOfPlane (#671, #672)
Browse files Browse the repository at this point in the history
  • Loading branch information
neurolabusc committed Jan 29, 2023
1 parent 1d4413e commit e53c230
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 14 deletions.
8 changes: 6 additions & 2 deletions BIDS/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,22 @@ Fields specific to MRI scans.
| NumberOfAverages | | DICOM tag 0018,0083 | D |
| ParallelAcquisitionTechnique | | DICOM tag 0018, 9078, aka `SENSE`, `GRAPPA` | B |
| ParallelReductionFactorInPlane | | DICOM tag 0018,9069 | B |
| ParallelReductionFactorOutOfPlane | | DICOM tag 0018,9069 | B |
| ParallelReductionOutOfPlane | | DICOM tag 0018,9155 | D |
| PartialFourierDirection | | DICOM tag 0018,9036 | B |
| PercentPhaseFOV | | DICOM tag 0018,0094 | D |
| PercentSampling | | DICOM tag 0018,0093 | D |
| PhaseEncodingAxis | | When polarity unknown | B |
| PhaseEncodingDirection | | When polarity known | B |
| PhaseEncodingSteps | | DICOM tag 0018,0089 or 0018,9231 | D |
| FrequencyEncodingSteps | | DICOM tag 0018,9058 | D |
| PhaseEncodingSteps | | DICOM tag 0018,0089 or 0018,9231 aka PhaseEncodingStepsInPlane | D |
| PhaseEncodingStepsOutOfPlane | | DICOM tag 0018,9232 | D |
| PixelBandwidth | Hz | DICOM tag 0018,0095 | D |
| ReceiveCoilName | | DICOM tag 0018,1250 | B |
| RepetitionTime | s | DICOM tag 0018,0080 | B |
| RepetitionTimeExcitation | s | DICOM tag 0018, 0080 for some manufacturers | B |
| RepetitionTimeInversion | s | | D |
| SAR | | DICOM tag 0018,1316 or 0018,9181 | D |
| SAR | | DICOM tag 0018,1316 or 0018,9181 defined by 0018,9179 | D |
| SliceThickness | mm | [nb](http://dclunie.blogspot.com/2013/10/how-thick-am-i-sad-story-of-lonely-slice.html) | D |
| SliceTiming | s | | B |
| SpacingBetweenSlices | mm | [nb](http://dclunie.blogspot.com/2013/10/how-thick-am-i-sad-story-of-lonely-slice.html) | D |
Expand Down Expand Up @@ -333,6 +336,7 @@ Fields specific to [Siemens XA-series](https://github.com/rordenlab/dcm2niix/tre
| PostLabelDelay | s | DICOM tag 0018,9258 | D |
| NonlinearGradientCorrection | b | 0008,0008 or 0021,1175 | B |
| PhaseEncodingDirection | | polarity from 0021,111c | B |
| SpoilingState | | 0021,105B | B |

### Manufacturer UIH

Expand Down
21 changes: 16 additions & 5 deletions console/nii_dicom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,8 @@ struct TDICOMdata clear_dicom_data() {
d.protocolBlockStartGE = 0;
d.protocolBlockLengthGE = 0;
d.phaseEncodingSteps = 0;
d.frequencyEncodingSteps = 0;
d.phaseEncodingStepsOutOfPlane = 0;
d.coilCrc = 0;
d.seriesUidCrc = 0;
d.instanceUidCrc = 0;
Expand Down Expand Up @@ -4355,6 +4357,7 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D
#define kRectilinearPhaseEncodeReordering 0x0018 + uint32_t(0x9034 << 16) //'CS' 'REVERSE_LINEAR'/'LINEAR'
#define kPartialFourierDirection 0x0018 + uint32_t(0x9036 << 16) //'CS'
#define kCardiacSynchronizationTechnique 0x0018 + uint32_t(0x9037 << 16) //'CS'
#define kMRAcquisitionFrequencyEncodingSteps 0x0018 + uint32_t(0x9058 << 16) //US
#define kParallelReductionFactorInPlane 0x0018 + uint32_t(0x9069 << 16) //FD
#define kAcquisitionDuration 0x0018 + uint32_t(0x9073 << 16) //FD
#define kFrameAcquisitionDateTime 0x0018+uint32_t(0x9074<< 16 ) //DT "20181019212528.232500"
Expand All @@ -4367,8 +4370,11 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16);
#define kDiffusion_bValue 0x0018 + uint32_t(0x9087 << 16) // FD
#define kDiffusionOrientation 0x0018 + uint32_t(0x9089 << 16) // FD, seen in enhanced DICOM from Philips 5.* and Siemens XA10.
#define kImagingFrequencyFD 0x0018 + uint32_t(0x9098 << 16) //FD
#define kMREchoSequence 0x0018 + uint32_t(0x9114 << 16) //SQ
#define kParallelReductionFactorOutOfPlane 0x0018 + uint32_t(0x9155 << 16) //FD
#define kSARFD 0x0018 + uint32_t(0x9181 << 16) //FD
#define kMRAcquisitionPhaseEncodingStepsInPlane 0x0018 + uint32_t(0x9231 << 16) //US
#define kMRAcquisitionPhaseEncodingStepsOutOfPlane 0x0018 + uint32_t(0x9232 << 16) //US
//#define kFrameAcquisitionDuration 0x0018+uint32_t(0x9220 << 16 ) //FD
#define kArterialSpinLabelingContrast 0x0018 + uint32_t(0x9250 << 16) //CS
#define kASLPulseTrainDuration 0x0018 + uint32_t(0x9258 << 16) //UL
Expand All @@ -4378,8 +4384,6 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16);
#define kDiffusionBValueYY 0x0018 + uint32_t(0x9605 << 16) //FD
#define kDiffusionBValueYZ 0x0018 + uint32_t(0x9606 << 16) //FD
#define kDiffusionBValueZZ 0x0018 + uint32_t(0x9607 << 16) //FD
#define kMREchoSequence 0x0018 + uint32_t(0x9114 << 16) //SQ
#define kMRAcquisitionPhaseEncodingStepsInPlane 0x0018 + uint32_t(0x9231 << 16) //US
#define kNumberOfImagesInMosaic 0x0019 + (0x100A << 16) //US NumberOfImagesInMosaic
//https://nmrimaging.wordpress.com/2011/12/20/when-we-process/
// https://nciphub.org/groups/qindicom/wiki/DiffusionrelatedDICOMtags:experienceacrosssites?action=pdf
Expand Down Expand Up @@ -5686,7 +5690,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16);
sqDepth00189114 = sqDepth - 1;
break;
case kMRAcquisitionPhaseEncodingStepsInPlane:
d.phaseEncodingLines = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
d.phaseEncodingSteps = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
break;
case kMRAcquisitionFrequencyEncodingSteps:
d.frequencyEncodingSteps = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
break;
case kMRAcquisitionPhaseEncodingStepsOutOfPlane:
d.phaseEncodingStepsOutOfPlane = dcmInt(lLength, &buffer[lPos], d.isLittleEndian);
break;
case kNumberOfImagesInMosaic:
if (d.manufacturer == kMANUFACTURER_SIEMENS)
Expand Down Expand Up @@ -6670,8 +6680,9 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16);
break;
case kSARFD:
//Siemens XA uses kSARFD instead of kSAR
d.SAR = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
maxSAR = fmax(maxSAR, d.SAR);
// ignore as we also need to know the definition issue 668
//d.SAR = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian);
//maxSAR = fmax(maxSAR, d.SAR);
break;
//case kFrameAcquisitionDuration :
// frameAcquisitionDuration = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); //issue369
Expand Down
4 changes: 2 additions & 2 deletions console/nii_dicom.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ extern "C" {
#define kCPUsuf " " //unknown CPU
#endif

#define kDCMdate "v1.0.20230121"
#define kDCMdate "v1.0.20230127"
#define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf

static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic
Expand Down Expand Up @@ -235,7 +235,7 @@ static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8;
int xyzDim[5];
uint32_t coilCrc, seriesUidCrc, instanceUidCrc;
int overlayStart[kMaxOverlay];
int postLabelDelay, shimGradientX, shimGradientY, shimGradientZ, phaseNumber, spoiling, mtState, partialFourierDirection, interp3D, aslFlags, durationLabelPulseGE, epiVersionGE, internalepiVersionGE, maxEchoNumGE, rawDataRunNumber, numberOfImagesInGridUIH, numberOfDiffusionT2GE, numberOfDiffusionDirectionGE, tensorFileGE, diffCyclingModeGE, phaseEncodingGE, protocolBlockStartGE, protocolBlockLengthGE, modality, dwellTime, effectiveEchoSpacingGE, phaseEncodingLines, phaseEncodingSteps, echoTrainLength, echoNum, sliceOrient, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,locationsInAcquisition, locationsInAcquisitionConflict, compressionScheme;
int postLabelDelay, shimGradientX, shimGradientY, shimGradientZ, phaseNumber, spoiling, mtState, partialFourierDirection, interp3D, aslFlags, durationLabelPulseGE, epiVersionGE, internalepiVersionGE, maxEchoNumGE, rawDataRunNumber, numberOfImagesInGridUIH, numberOfDiffusionT2GE, numberOfDiffusionDirectionGE, tensorFileGE, diffCyclingModeGE, phaseEncodingGE, protocolBlockStartGE, protocolBlockLengthGE, modality, dwellTime, effectiveEchoSpacingGE, phaseEncodingLines, phaseEncodingSteps, frequencyEncodingSteps, phaseEncodingStepsOutOfPlane, echoTrainLength, echoNum, sliceOrient, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,locationsInAcquisition, locationsInAcquisitionConflict, compressionScheme;
float xRayTubeCurrent, exposureTimeMs, numberOfExcitations, numberOfArms, numberOfPointsPerArm, groupDelay, decayFactor, percentSampling,waterFatShift, numberOfAverages, imagingFrequency, patientWeight, zSpacing, zThick, pixelBandwidth, SAR, phaseFieldofView, accelFactPE, accelFactOOP, flipAngle, fieldStrength, TE, TI, TR, intenScale, intenIntercept, intenScalePhilips, gantryTilt, lastScanLoc, angulation[4], velocityEncodeScaleGE;
float orient[7], patientPosition[4], patientPositionLast[4], xyzMM[4], stackOffcentre[4];
float rtia_timerGE, radionuclidePositronFraction, radionuclideTotalDose, radionuclideHalfLife, doseCalibrationFactor; //PET ISOTOPE MODULE ATTRIBUTES (C.8-57)
Expand Down
18 changes: 13 additions & 5 deletions console/nii_dicom_batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,10 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i
csaAscii->parallelReductionFactorInPlane = readKey(keyStrAF, keyPos, csaLengthTrim);
char keyStrAF3D[] = "sPat.lAccelFact3D";
csaAscii->accelFact3D = readKey(keyStrAF3D, keyPos, csaLengthTrim);
//issue 672: the tag "sSliceAcceleration.lMultiBandFactor" is not reliable:
// series 7 dcm_qa_xa30 has x3 multiband, but this tag reports "1" (perhaps cmrr sequences)
//char keyStrMB[] = "sSliceAcceleration.lMultiBandFactor";
//csaAscii->multiBandFactor = readKey(keyStrMB, keyPos, csaLengthTrim);
char keyStrRef[] = "sPat.lRefLinesPE";
csaAscii->refLinesPE = readKey(keyStrRef, keyPos, csaLengthTrim);
char keyStrCombineMode[] = "ucCoilCombineMode";
Expand Down Expand Up @@ -1801,15 +1805,15 @@ tse3d: T2*/
if ((csaAscii.ucMTC == 1) && (d.mtState < 0)) //precedence for 0018,9020 over CSA
json_Bool(fp, "\t\"MTState\": %s,\n", 1);
json_Str(fp, "\t\"ConsistencyInfo\": \"%s\",\n", consistencyInfo);
if (csaAscii.accelFact3D > 1.01) json_Float(fp, "\t\"AccelFact3D\": %g,\n", csaAscii.accelFact3D); //see *spcR_44ns where "sPat.lAccelFactPE = 1", "sPat.lAccelFact3D = 2" (0051,1011) LO [p2], perhaps ParallelReductionFactorInPlane should be 1?
if (csaAscii.accelFact3D > 0)
d.accelFactOOP = csaAscii.accelFact3D;
//see issue 672 if (csaAscii.accelFact3D > 1.01) json_Float(fp, "\t\"AccelFact3D\": %g,\n", csaAscii.accelFact3D); //see *spcR_44ns where "sPat.lAccelFactPE = 1", "sPat.lAccelFact3D = 2" (0051,1011) LO [p2], perhaps ParallelReductionFactorInPlane should be 1?
if (csaAscii.parallelReductionFactorInPlane > 0) { //AccelFactorPE -> phase encoding
if (csaAscii.patMode == 1)
fprintf(fp, "\t\"MatrixCoilMode\": \"SENSE\",\n");
if (csaAscii.patMode == 2)
fprintf(fp, "\t\"MatrixCoilMode\": \"GRAPPA\",\n");
if (d.accelFactPE < 1.0) { //value not found in DICOM header, but WAS found in CSA ascii
d.accelFactPE = csaAscii.parallelReductionFactorInPlane; //value found in ASCII but not in DICOM (0051,1011)
}
d.accelFactPE = csaAscii.parallelReductionFactorInPlane; //issue672: csa precedence over value found in DICOM (0051,1011)
if ((csaAscii.accelFact3D < 1.01) && (csaAscii.parallelReductionFactorInPlane != (int)(d.accelFactPE)))
printWarning("ParallelReductionFactorInPlane reported in DICOM [0051,1011] (%d) does not match CSA series value %d\n", (int)(d.accelFactPE), csaAscii.parallelReductionFactorInPlane);
}
Expand Down Expand Up @@ -1893,6 +1897,10 @@ tse3d: T2*/
fprintf(fp, "\t\"PhaseEncodingStepsNoPartialFourier\": %d,\n", d.phaseEncodingSteps);
} else if (d.phaseEncodingSteps > 0)
fprintf(fp, "\t\"PhaseEncodingSteps\": %d,\n", d.phaseEncodingSteps);
if (d.frequencyEncodingSteps > 0)
fprintf(fp, "\t\"FrequencyEncodingSteps\": %d,\n", d.frequencyEncodingSteps);
if (d.phaseEncodingStepsOutOfPlane > 0)
fprintf(fp, "\t\"PhaseEncodingStepsOutOfPlane\": %d,\n", d.phaseEncodingStepsOutOfPlane);
if ((d.phaseEncodingLines > 0) && (d.modality == kMODALITY_MR))
fprintf(fp, "\t\"AcquisitionMatrixPE\": %d,\n", d.phaseEncodingLines);
//Compute ReconMatrixPE
Expand Down Expand Up @@ -1921,7 +1929,7 @@ tse3d: T2*/
json_Str(fp, "\t\"ParallelAcquisitionTechnique\": \"%s\",\n", d.parallelAcquisitionTechnique);
//https://github.com/rordenlab/dcm2niix/issues/314
if (d.accelFactOOP > 1.0)
fprintf(fp, "\t\"ParallelReductionOutOfPlane\": %g,\n", d.accelFactOOP);
fprintf(fp, "\t\"ParallelReductionFactorOutOfPlane\": %g,\n", d.accelFactOOP); //issue672
//EffectiveEchoSpacing
// Siemens bandwidthPerPixelPhaseEncode already accounts for the effects of parallel imaging,
// interpolation, phaseOversampling, and phaseResolution, in the context of the size of the
Expand Down

0 comments on commit e53c230

Please sign in to comment.