From 808985ebd2f3df308d7097aabeef9fd997472d5c Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Thu, 5 Oct 2023 13:14:24 -0400 Subject: [PATCH] GEIIS icons and Bruker diffusion (https://github.com/rordenlab/dcm2niix/issues/760; https://github.com/rordenlab/dcm2niix/issues/761) --- console/nii_dicom.cpp | 22 ++++++++++++++++++++-- console/nii_dicom.h | 2 +- console/nii_dicom_batch.cpp | 21 ++++++++++++++++----- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 37104fe1..c1fdcfcf 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4340,6 +4340,7 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D #define kReferencedImageEvidenceSQ (uint32_t)0x0008 + (0x9092 << 16) #define kComplexImageComponent (uint32_t)0x0008 + (0x9208 << 16) //'0008' '9208' 'CS' 'ComplexImageComponent' #define kAcquisitionContrast (uint32_t)0x0008 + (0x9209 << 16) //'0008' '9209' 'CS' 'AcquisitionContrast' +#define kIconSQ 0x0009 + (0x1110 << 16) #define kPatientName 0x0010 + (0x0010 << 16) #define kPatientID 0x0010 + (0x0020 << 16) #define kAccessionNumber 0x0008 + (0x0050 << 16) @@ -5557,6 +5558,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (strlen(d.studyTime) < 2) dcmStr(lLength, &buffer[lPos], d.studyTime); break; + case kIconSQ: {//issue760 GEIIS icon strikes again + if ((vr[0] != 'S') || (vr[1] != 'Q') || (lLength != 8)) break; //only risk kludge for explicit VR with length + isIconImageSequence = true; + if (sqDepthIcon < 0) + sqDepthIcon = sqDepth; + break; + } case kPatientName: dcmStr(lLength, &buffer[lPos], d.patientName); break; @@ -7711,7 +7719,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD qsort(objects, numberOfFrames, sizeof(struct fidx), fcmp); numDimensionIndexValues = numberOfFrames; for (int i = 0; i < numberOfFrames; i++) { - // printf("%d > %g\n", objects[i].index, objects[i].value); + //printf("%d > %g\n", objects[i].index, objects[i].value); dcmDim[objects[i].index].dimIdx[0] = i; } for (int i = 0; i < 4; i++) { @@ -7830,7 +7838,10 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (dti4D->intenScalePhilips[i] != dti4D->intenScalePhilips[0]) d.isScaleVariesEnh = true; } - if (!(d.manufacturer == kMANUFACTURER_BRUKER && d.isDiffusion) && (d.xyzDim[4] > 1) && (d.xyzDim[4] < kMaxDTI4D)) { //record variations in TE + //Revert Bruker change as new Bruker data uses DimensionIndexValues 0020,9157 for diffusion vectors + // https://github.com/rordenlab/dcm2niix/commit/8207877de984dab59e0374906c7ad212756f85a6#diff-120bb215d28dcbddeca8ea56dbb97e237501901ac2dfa1fbe4182282a8dd2b75 + //if (!(d.manufacturer == kMANUFACTURER_BRUKER && d.isDiffusion) && (d.xyzDim[4] > 1) && (d.xyzDim[4] < kMaxDTI4D)) { //record variations in TE + if ((d.xyzDim[4] > 1) && (d.xyzDim[4] < kMaxDTI4D)) { //record variations in TE d.isScaleOrTEVaries = false; bool isTEvaries = false; bool isScaleVaries = false; @@ -8121,6 +8132,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.rawDataRunNumber = philMRImageDiffVolumeNumber; d.phaseNumber = 0; } + // Phase encoding polarity + //next conditionals updated: make GE match Siemens + // it also rescues Siemens XA20 images without CSA header but with 0021,111C + if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_UNFLIPPED) + d.CSA.phaseEncodingDirectionPositive = 1; + if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_FLIPPED) + d.CSA.phaseEncodingDirectionPositive = 0; //issue 668: several SAR levels for different regions (IEC_HEAD, IEC_LOCAL, etc) d.SAR = fmax(maxSAR, d.SAR); // d.rawDataRunNumber = (d.rawDataRunNumber > d.phaseNumber) ? d.rawDataRunNumber : d.phaseNumber; //will not work: conflict for MultiPhase ASL with multiple averages diff --git a/console/nii_dicom.h b/console/nii_dicom.h index e822872b..9bcf3a65 100644 --- a/console/nii_dicom.h +++ b/console/nii_dicom.h @@ -50,7 +50,7 @@ extern "C" { #define kCPUsuf " " //unknown CPU #endif -#define kDCMdate "v1.0.20230904" +#define kDCMdate "v1.0.20231005" #define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 6c8d32c2..0670318b 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -2094,16 +2094,20 @@ tse3d: T2*/ if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (d.dwellTime > 0)) fprintf(fp, "\t\"DwellTime\": %g,\n", d.dwellTime * 1E-9); // Phase encoding polarity +/* +following logic now occurs earlier to aid bids guess int phPos = d.CSA.phaseEncodingDirectionPositive; //next two conditionals updated: make GE match Siemens if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_UNFLIPPED) phPos = 1; if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_FLIPPED) phPos = 0; +*/ //if ((phPos >= 0) && (d.phaseEncodingRC == 'R') && (d.manufacturer == kMANUFACTURER_UIH)) phPos = 1 - phPos; //issue410 bool isSkipPhaseEncodingAxis = d.is3DAcq; if (d.echoTrainLength > 1) isSkipPhaseEncodingAxis = false; //issue 371: ignore phaseEncoding for 3D MP-RAGE/SPACE, but report for 3D EPI + int phPos = d.CSA.phaseEncodingDirectionPositive; if (((d.phaseEncodingRC == 'R') || (d.phaseEncodingRC == 'C')) && (!isSkipPhaseEncodingAxis) && (phPos < 0)) { //when phase encoding axis is known but we do not know phase encoding polarity // https://github.com/rordenlab/dcm2niix/issues/163 @@ -6661,15 +6665,16 @@ void setBidsSiemens(struct TDICOMdata *d, int nConvert, int isVerbose, const cha } //add dir //nb for func dir comes before echo + int phPos = d->CSA.phaseEncodingDirectionPositive; if (isDirLabel) { char dirLabel[kDICOMStrLarge] = "_dir-"; if (d->phaseEncodingRC == 'C') { - if (d->CSA.phaseEncodingDirectionPositive) + if (phPos) strcat(dirLabel, "AP"); else strcat(dirLabel, "PA"); } else { - if (d->CSA.phaseEncodingDirectionPositive) + if (phPos) strcat(dirLabel, "RL"); else strcat(dirLabel, "LR"); @@ -6783,11 +6788,17 @@ void setBidsPhilips(struct TDICOMdata *d, int nConvert, int isVerbose) { else strcpy(modalityBIDS, "magnitude"); } else if ((strstr(seqName, "SP") != NULL) && (strstr(d->scanningSequence, "GR") != NULL)) { + //issue 753 need to distinguish different types of phase map see Levitas example B0_DTI_MED_RES + // https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/01-magnetic-resonance-imaging-data.html#case-3-direct-field-mapping + printWarning("Unable to distinguish Philips fieldmaps: phase difference, two phase/magnitude, direct fieldmapping."); isReportEcho = false; strcpy(dataTypeBIDS, "fmap"); - strcpy(modalityBIDS, "phasediff"); + if (d->isHasPhase) + strcpy(modalityBIDS, "phasediff"); + else if (d->echoTrainLength < 2) + snprintf(modalityBIDS, kDICOMStrLarge - strlen(modalityBIDS), "magnitude%d", d->echoNum); + if (d->echoTrainLength > 1) { - isReportEcho = false; if (d->isHasPhase) snprintf(modalityBIDS, kDICOMStrLarge - strlen(modalityBIDS), "phase%d", d->echoNum); else if (d->isHasImaginary) @@ -6853,7 +6864,7 @@ void setBidsPhilips(struct TDICOMdata *d, int nConvert, int isVerbose) { strcat(suffixBIDS,modalityBIDS); } //if ((isVerbose > 0) || (strlen(dataTypeBIDS) < 1)) - if (isVerbose > 0) + //if (isVerbose > 0) printf("::autoBids:Philips pulseSeq:'%s' scanSeq:'%s' seqVariant:'%s'\n", d->pulseSequenceName, d->scanningSequence, d->sequenceVariant); if (isDerived)