Skip to content

Commit

Permalink
Add VariableFlipAngle detection (#646)
Browse files Browse the repository at this point in the history
  • Loading branch information
neurolabusc committed Nov 9, 2022
1 parent ee5f18c commit d64f05f
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 5 deletions.
1 change: 1 addition & 0 deletions BIDS/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ Fields specific to MRI scans.
| EstimatedEffectiveEchoSpacing | s | | D |
| EstimatedTotalReadoutTime | s | | D |
| FlipAngle | deg | DICOM tag 0018,1314 | B |
| VariableFlipAngleFlag | b | DICOM tag 0018,1315 | D |
| ImageOrientationPatientDICOM | | DICOM tag 0020,0037 | D |
| ImagingFrequency | MHz | DICOM tag 0018,0084 | D |
| InPlanePhaseEncodingDirectionDICOM | | DICOM tag 0018,1312 | D |
Expand Down
10 changes: 8 additions & 2 deletions console/nii_dicom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,7 @@ struct TDICOMdata clear_dicom_data() {
strcpy(d.studyDate, "");
strcpy(d.studyTime, "");
strcpy(d.protocolName, "");
strcpy(d.patientOrient, "");
strcpy(d.seriesDescription, "");
strcpy(d.sequenceName, "");
strcpy(d.scanningSequence, "");
Expand Down Expand Up @@ -885,6 +886,7 @@ struct TDICOMdata clear_dicom_data() {
d.isHasOverlay = false;
d.isPrivateCreatorRemap = false;
d.isRealIsPhaseMapHz = false;
d.isVariableFlipAngle = false;
d.isQuadruped = false;
d.numberOfImagesInGridUIH = 0;
d.phaseEncodingRC = '?';
Expand Down Expand Up @@ -4283,8 +4285,9 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D
#define kReceiveCoilName 0x0018 + (0x1250 << 16) // SH
//#define kTransmitCoilName 0x0018 + (0x1251 << 16) // SH issue527
#define kAcquisitionMatrix 0x0018 + (0x1310 << 16) //US
#define kFlipAngle 0x0018 + (0x1314 << 16)
#define kInPlanePhaseEncodingDirection 0x0018 + (0x1312 << 16) //CS
#define kFlipAngle 0x0018 + (0x1314 << 16)
#define kVariableFlipAngleFlag 0x0018 + (0x1315 << 16) //CS
#define kSAR 0x0018 + (0x1316 << 16) //'DS' 'SAR'
#define kPatientOrient 0x0018 + (0x5100 << 16) //0018,5100. patient orientation - 'HFS'
#define kPulseSequenceName 0x0018 + uint32_t(0x9005 << 16) //'SH' 'YES'/'NO'
Expand Down Expand Up @@ -5421,7 +5424,7 @@ const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16);
if ((slen < 9) || (strstr(aotTxt, "QUADRUPED") == NULL))
break;
d.isQuadruped = true;
printError("Anatomical Orientation Type (0010,2210) is QUADRUPED: rotate coordinates accordingly\n");
//printError("Anatomical Orientation Type (0010,2210) is QUADRUPED: rotate coordinates accordingly\n");
break;
}
case kDeidentificationMethod: { //issue 383
Expand Down Expand Up @@ -6230,6 +6233,9 @@ const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16);
case kFlipAngle:
d.flipAngle = dcmStrFloat(lLength, &buffer[lPos]);
break;
case kVariableFlipAngleFlag:
d.isVariableFlipAngle = ('Y' == toupper(buffer[lPos])); //first character is either 'y'es or 'n'o
break;
case kRadionuclideHalfLife:
d.radionuclideHalfLife = dcmStrFloat(lLength, &buffer[lPos]);
break;
Expand Down
2 changes: 1 addition & 1 deletion console/nii_dicom.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8;
char scanOptions[kDICOMStrLarge], institutionAddress[kDICOMStrLarge], imageComments[kDICOMStrLarge];
uint32_t dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS];
struct TCSAdata CSA;
bool isQuadruped, isRealIsPhaseMapHz, isPrivateCreatorRemap, isHasOverlay, isEPI, isIR, isPartialFourier, isDiffusion, isVectorFromBMatrix, isRawDataStorage, isGrayscaleSoftcopyPresentationState, isStackableSeries, isCoilVaries, isNonParallelSlices, isBVecWorldCoordinates, isSegamiOasis, isXA10A, isScaleOrTEVaries, isScaleVariesEnh, isDerived, isXRay, isMultiEcho, isValid, is3DAcq, is2DAcq, isExplicitVR, isLittleEndian, isPlanarRGB, isSigned, isHasPhase, isHasImaginary, isHasReal, isHasMagnitude,isHasMixed, isFloat, isResampled, isLocalizer;
bool isVariableFlipAngle, isQuadruped, isRealIsPhaseMapHz, isPrivateCreatorRemap, isHasOverlay, isEPI, isIR, isPartialFourier, isDiffusion, isVectorFromBMatrix, isRawDataStorage, isGrayscaleSoftcopyPresentationState, isStackableSeries, isCoilVaries, isNonParallelSlices, isBVecWorldCoordinates, isSegamiOasis, isXA10A, isScaleOrTEVaries, isScaleVariesEnh, isDerived, isXRay, isMultiEcho, isValid, is3DAcq, is2DAcq, isExplicitVR, isLittleEndian, isPlanarRGB, isSigned, isHasPhase, isHasImaginary, isHasReal, isHasMagnitude,isHasMixed, isFloat, isResampled, isLocalizer;
char phaseEncodingRC, patientSex;
};
struct TDCMprefs {
Expand Down
17 changes: 15 additions & 2 deletions console/nii_dicom_batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1509,6 +1509,8 @@ tse3d: T2*/
fprintf(fp, "\t\"SpoilingType\": \"COMBINED\",\n");
json_Float(fp, "\t\"InversionTime\": %g,\n", d.TI / 1000.0);
json_Float(fp, "\t\"FlipAngle\": %g,\n", d.flipAngle);
if (d.isVariableFlipAngle)
json_Bool(fp, "\t\"VariableFlipAngleFlag\": %s,\n", true); // BIDS suggests 0018,9020 but Siemens V-series do not populate this, alternatives are CSA or (0018,0021) CS [SK\MTC\SP]
bool interp = false; //2D interpolation
float phaseOversampling = 0.0;
//n.b. https://neurostars.org/t/getting-missing-ge-information-required-by-bids-for-common-preprocessing/1357/7
Expand Down Expand Up @@ -1603,7 +1605,7 @@ tse3d: T2*/
json_FloatNotNan(fp, "\t\"PostLabelDelay\": %g,\n", csaAscii.adFree[2] * (1.0 / 1000000.0)); //usec -> sec
json_FloatNotNan(fp, "\t\"NumRFBlocks\": %g,\n", csaAscii.adFree[3]);
json_FloatNotNan(fp, "\t\"RFGap\": %g,\n", csaAscii.adFree[4] * (1.0 / 1000000.0)); //usec -> sec
json_FloatNotNan(fp, "\t\"MeanGzx10\": %g,\n", csaAscii.adFree[10]); // mT/m
json_FloatNotNan(fp, "\t\"MeanGzx10\": %g,\n", csaAscii.RepetitionTimeExcitation[10]); // mT/m
json_FloatNotNan(fp, "\t\"PhiAdjust\": %g,\n", csaAscii.adFree[11]); // percent
}
//ASL specific tags - 3D pCASL Danny J.J. Wang http://www.loft-lab.org
Expand Down Expand Up @@ -6565,6 +6567,10 @@ int saveDcm2NiiCore(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata d
dti4D->frameDuration[0] = -1;
dti4D->frameReferenceTime[0] = -1;
}
if (strlen(dcmList[indx0].patientOrient) < 3)
printWarning("PatientOrient (0018,5100) not specified (issue 642).\n");
if (dcmList[indx0].isQuadruped)
printWarning("Anatomical Orientation Type (0010,2210) is QUADRUPED: rotate coordinates accordingly (issue 642)\n");
#ifdef newTilt //see issue 254
if (((nConvert > 1) || (dcmList[indx0].xyzDim[3] > 1)) && ((dcmList[indx0].modality == kMODALITY_CT) || (dcmList[indx0].isXRay) || (dcmList[indx0].gantryTilt > 0.0))) { //issue372: enhanced DICOMs can also have gantry tilt
dcmList[indx0].gantryTilt = computeGantryTiltPrecise(dcmList[indx0], dcmList[indxEnd], opts.isVerbose);
Expand Down Expand Up @@ -7685,6 +7691,13 @@ bool isSameSet(struct TDICOMdata d1, struct TDICOMdata d2, struct TDCMopts *opts
warnings->echoVaries = true;
return false;
}
if (!(isSameFloat(d1.flipAngle, d2.flipAngle))) {
if (!warnings->echoVaries)
printMessage("Slices not stacked: flip angle varies (%g, %g, issue 646).\n", d1.TR, d2.TR);
*isMultiEcho = true;
warnings->echoVaries = true;
return false;
}
//if ((d1.TE != d2.TE) || (d1.echoNum != d2.echoNum)) {
if ((!(isSameFloat(d1.TE, d2.TE))) || (d1.echoNum != d2.echoNum)) {
if ((!warnings->echoVaries) && (d1.isXRay)) //for CT/XRay we check DICOM tag 0018,1152 (XRayExposure)
Expand Down Expand Up @@ -8906,7 +8919,7 @@ void readIniFile(struct TDCMopts *opts, const char *argv[]) {
FILE *fp = fopen(opts->optsname, "r");
if (fp == NULL)
return;
char Setting[20], Value[255];
char Setting[255], Value[255];
//while ( fscanf(fp, "%[^=]=%s\n", Setting, Value) == 2 ) {
//while ( fscanf(fp, "%[^=]=%s\n", Setting, Value) == 2 ) {
while (fscanf(fp, "%[^=]=%[^\n]\n", Setting, Value) == 2) {
Expand Down

0 comments on commit d64f05f

Please sign in to comment.