From 484de5bf5ec76cdc668e5e547c9803e58e6f3cc7 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Thu, 14 Mar 2024 11:30:03 -0400 Subject: [PATCH] Renambed ASL fields to match new BIDS specification (https://bids-specification.readthedocs.io/en/stable/glossary.html#labelingpulseflipangle-metadata) --- console/nii_dicom_batch.cpp | 137 ++++-------------------------------- 1 file changed, 14 insertions(+), 123 deletions(-) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index bca5d118..ce3518e1 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1371,7 +1371,6 @@ tse3d: T2*/ } if (d.patientSex != '?') fprintf(fp, "\t\"PatientSex\": \"%c\",\n", d.patientSex); - json_Float(fp, "\t\"PatientSize\": %g,\n", d.patientSize); json_Float(fp, "\t\"PatientWeight\": %g,\n", d.patientWeight); //d.patientBirthDate //convert from DICOM YYYYMMDD to JSON //d.patientAge //4-digit Age String: nnnD, nnnW, nnnM, nnnY; @@ -1386,7 +1385,7 @@ tse3d: T2*/ } if (d.isQuadruped) json_Bool(fp, "\t\"Quadruped\": %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] - json_Str(fp, "\t\"BodyPart\": \"%s\",\n", d.bodyPartExamined); //renamed to match BIDS https://bids-specification.readthedocs.io/en/stable/modality-specific-files/positron-emission-tomography.html + json_Str(fp, "\t\"BodyPartExamined\": \"%s\",\n", d.bodyPartExamined); json_Str(fp, "\t\"PatientPosition\": \"%s\",\n", d.patientOrient); // 0018,5100 = PatientPosition in DICOM json_Str(fp, "\t\"ProcedureStepDescription\": \"%s\",\n", d.procedureStepDescription); json_Str(fp, "\t\"SoftwareVersions\": \"%s\",\n", d.softwareVersions); @@ -1498,110 +1497,19 @@ tse3d: T2*/ if ((d.isRealIsPhaseMapHz) && ((d.manufacturer == kMANUFACTURER_GE) || (d.isHasReal))) fprintf(fp, "\t\"Units\": \"Hz\",\n"); // //PET ISOTOPE MODULE ATTRIBUTES - json_Str(fp, "\t\"TracerRadionuclide\": \"%s\",\n", d.tracerRadionuclide); json_Str(fp, "\t\"Radiopharmaceutical\": \"%s\",\n", d.radiopharmaceutical); json_Float(fp, "\t\"RadionuclidePositronFraction\": %g,\n", d.radionuclidePositronFraction); - json_Float(fp, "\t\"InjectedRadioactivity\": %g,\n", d.radionuclideTotalDose); //renamed https://bids-specification.readthedocs.io/en/stable/glossary.html#objects.metadata.InjectedRadioactivity + json_Float(fp, "\t\"RadionuclideTotalDose\": %g,\n", d.radionuclideTotalDose); json_Float(fp, "\t\"RadionuclideHalfLife\": %g,\n", d.radionuclideHalfLife); json_Float(fp, "\t\"DoseCalibrationFactor\": %g,\n", d.doseCalibrationFactor); json_Float(fp, "\t\"IsotopeHalfLife\": %g,\n", d.ecat_isotope_halflife); json_Float(fp, "\t\"Dosage\": %g,\n", d.ecat_dosage); json_Str(fp, "\t\"ConvolutionKernel\": \"%s\",\n", d.convolutionKernel); json_Str(fp, "\t\"Units\": \"%s\",\n", d.unitsPT); //https://github.com/bids-standard/bids-specification/pull/773 - json_Str(fp, "\t\"DecayCorrectionFactor\": \"%s\",\n", d.decayCorrection); //renamed https://bids-specification.readthedocs.io/en/stable/glossary.html#objects.metadata.DecayCorrectionFactor + json_Str(fp, "\t\"DecayCorrection\": \"%s\",\n", d.decayCorrection); json_Str(fp, "\t\"AttenuationCorrectionMethod\": \"%s\",\n", d.attenuationCorrectionMethod); json_Str(fp, "\t\"ReconstructionMethod\": \"%s\",\n", d.reconstructionMethod); - //START issue 802 - char reconMethodName[kDICOMStrLarge] = ""; - //start of autogenerated text - if (strstr(d.reconstructionMethod, "PSF+TOF3i21s")) - strcpy(reconMethodName, "Point-Spread Function + Time Of Flight"); - else if (strstr(d.reconstructionMethod, "PSF TOF 3D OSEM")) - strcpy(reconMethodName, "Point-Spread Function 3D Time Of Flight"); - else if (strstr(d.reconstructionMethod, "OP-OSEM")) - strcpy(reconMethodName, "Ordinary Poisson - Ordered Subset Expectation Maximization"); - else if (strstr(d.reconstructionMethod, "OSEM3D-OP-PSF")) - strcpy(reconMethodName, "Ordinary Poisson 3D Ordered Subset Expectation Maximization + Point-Spread Function"); - else if (strstr(d.reconstructionMethod, "LOR-RAMLA")) - strcpy(reconMethodName, "Line Of Response - Row Action Maximum Likelihood"); - else if (strstr(d.reconstructionMethod, "3D-RAMLA")) - strcpy(reconMethodName, "3D Row Action Maximum Likelihood"); - else if (strstr(d.reconstructionMethod, "3DRP")) - strcpy(reconMethodName, "3DRP"); - else if (strstr(d.reconstructionMethod, "3D Kinahan-Rogers")) - strcpy(reconMethodName, "3D Kinahan-Rogers"); - //end of autogenerated text - if (strlen(reconMethodName) < 1) { - if (strstr(d.reconstructionMethod, "OSEM")) - strcat(reconMethodName, "Ordered Subset Expectation Maximization "); - else if (strstr(d.reconstructionMethod, "OS")) - strcat(reconMethodName, "Ordered Subset "); - if (strstr(d.reconstructionMethod, "LOR")) - strcat(reconMethodName, "Line Of Response "); - if (strstr(d.reconstructionMethod, "RAMLA")) - strcat(reconMethodName, "Row Action Maximum Likelihood "); - if (strstr(d.reconstructionMethod, "OP")) - strcat(reconMethodName, "Ordinary Poisson "); - if (strstr(d.reconstructionMethod, "PSF")) - strcat(reconMethodName, "Point-Spread Function modelling "); - if (strstr(d.reconstructionMethod, "TOF")) - strcat(reconMethodName, "Time Of Flight "); - if (strstr(d.reconstructionMethod, "TF")) - strcat(reconMethodName, "Time Of Flight "); - if (strstr(d.reconstructionMethod, "VPHD")) - strcat(reconMethodName, "VUE Point HD "); - else if (strstr(d.reconstructionMethod, "VPHD-S")) - strcat(reconMethodName, "3D Ordered Subset Expectation Maximization with Point-Spread Function modelling "); - if (strstr(d.reconstructionMethod, "VPFX")) - strcat(reconMethodName, "VUE Point HD using Time Of Flight "); - else if (strstr(d.reconstructionMethod, "VPFXS")) - strcat(reconMethodName, "VUE Point HD using Time Of Flight with Point-Spread Function modelling "); - if (strstr(d.reconstructionMethod, "Q.Clear")) - strcat(reconMethodName, "VUE Point HD with regularization (smoothing) "); - if (strstr(d.reconstructionMethod, "BLOB")) - strcat(reconMethodName, "3D spherically symmetric basis function "); - if (strstr(d.reconstructionMethod, "FilteredBackProjection")) - strcat(reconMethodName, "Filtered Back Projection "); - if (strstr(d.reconstructionMethod, "3DRP")) - strcat(reconMethodName, "3D Kinahan-Rogers "); - //remove trailing spaces - if ((strlen(reconMethodName) > 0) && (reconMethodName[strlen(reconMethodName) -1] == ' ')) - reconMethodName[strlen(reconMethodName) -1] = '\0'; - } - json_Str(fp, "\t\"ReconMethodName\": \"%s\",\n", reconMethodName); - int iterations = 0; - //note, some vendors write 'OSEM3D-OP-PSFi10s16' others 'OP-OSEM4i21s' - // order matters `OP-OSEM4i21s` should have i=4 NOT i=21 - bool sEnd = d.reconstructionMethod[strlen(d.reconstructionMethod) -1] == 's'; - for (int i = 1; i < 33; i++) { - char stri[12]; - if (sEnd) - snprintf(stri, 12, "%di", i); - else - snprintf(stri, 12, "i%d", i); - if (strstr(d.reconstructionMethod, stri)) - iterations = i; - } - int subsets = 0; - for (int i = 1; i < 32; i++) { - char stri[12]; - if (sEnd) - snprintf(stri, 12, "%ds", i); - else - snprintf(stri, 12, "s%d", i); - if (strstr(d.reconstructionMethod, stri)) - subsets = i; - } - if ((subsets > 0) && (iterations > 0)) { - fprintf(fp, "\t\"ReconMethodParameterLabels\": [\"subsets\", \"iterations\"],\n"); - fprintf(fp, "\t\"ReconMethodParameterValues\": [\n"); - fprintf(fp, "\t\t%d,\n", subsets); - fprintf(fp, "\t\t%d\t],\n", iterations); - } - //printf("::::%s ->'%s' : s%d i%d\n", d.reconstructionMethod, reconMethodName, subsets, iterations); - //END issue 802 - - json_Float(fp, "\t\"ScatterFraction\": %g,\n", d.scatterFraction); + //json_Float(fp, "\t\"DecayFactor\": %g,\n", d.decayFactor); if (dti4D->decayFactor[0] >= 0.0) { //see BEP009 PET https://docs.google.com/document/d/1mqMLnxVdLwZjDd4ZiWFqjEAmOmfcModA_R535v3eQs0 fprintf(fp, "\t\"DecayFactor\": [\n"); for (int i = 0; i < h->dim[4]; i++) { @@ -1844,11 +1752,11 @@ tse3d: T2*/ repetitionTimePreparation = d.TR; json_FloatNotNan(fp, "\t\"InversionTime\": %g,\n", csaAscii.alTI[2] * (1.0 / 1000000.0)); //ms->sec json_FloatNotNan(fp, "\t\"BolusDuration\": %g,\n", csaAscii.alTI[0] * (1.0 / 1000000.0)); //usec -> sec - json_Float(fp, "\t\"TagRFFlipAngle\": %g,\n", csaAscii.alFree[4]); - json_Float(fp, "\t\"TagRFDuration\": %g,\n", csaAscii.alFree[5] / 1000000.0); //usec -> sec + json_Float(fp, "\t\"LabelingPulseFlipAngle\": %g,\n", csaAscii.alFree[4]); //BIDS renaming TagRFFlipAngle -> LabelingPulseFlipAngle + json_Float(fp, "\t\"LabelingPulseDuration\": %g,\n", csaAscii.alFree[5] / 1000000.0); //BIDS renaming TagRFDuration -> LabelingPulseDuration usec -> sec json_Float(fp, "\t\"TagRFSeparation\": %g,\n", csaAscii.alFree[6] / 1000000.0); //usec -> sec - json_FloatNotNan(fp, "\t\"MeanTagGradient\": %g,\n", csaAscii.adFree[0]); //mTm - json_FloatNotNan(fp, "\t\"TagGradientAmplitude\": %g,\n", csaAscii.adFree[1]); //mTm + json_FloatNotNan(fp, "\t\"LabelingPulseAverageGradient\": %g,\n", csaAscii.adFree[0]); //BDS renaming MeanTagGradient -> LabelingPulseAverageGradient mTm + json_FloatNotNan(fp, "\t\"LabelingPulseMaximumGradient\": %g,\n", csaAscii.adFree[1]); //BIDS renaming TagGradientAmplitude -> LabelingPulseMaximumGradient mTm json_Float(fp, "\t\"TagDuration\": %g,\n", csaAscii.alFree[9] / 1000.0); //ms -> sec json_Float(fp, "\t\"MaximumT1Opt\": %g,\n", csaAscii.alFree[10] / 1000.0); //ms -> sec //report post label delay @@ -1861,7 +1769,7 @@ tse3d: T2*/ nPLD++; } //for k if (nPLD > 0) { // record PostLabelDelays, these are listed as "PLD0","PLD1",etc in PDF - fprintf(fp, "\t\"InitialPostLabelDelay\": [\n"); //https://docs.google.com/document/d/15tnn5F10KpgHypaQJNNGiNKsni9035GtDqJzWqkkP6c/edit# + fprintf(fp, "\t\"PostLabelingDelay\": [\n"); //BIDS renaming InitialPostLabelDelay -> PostLabelingDelay https://docs.google.com/document/d/15tnn5F10KpgHypaQJNNGiNKsni9035GtDqJzWqkkP6c/edit# for (int i = 0; i < nPLD; i++) { if (i != 0) fprintf(fp, ",\n"); @@ -6951,7 +6859,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) @@ -7529,6 +7437,8 @@ int sliceTimingCore(struct TDCMsort *dcmSort, struct TDICOMdata *dcmList, struct sliceDir = -1; //not sure how to handle negative determinants? } if (sliceDir < 0) { + if((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_SIEMENS) && (dcmList[dcmSort[0].indx].CSA.mosaicSlices < 2)) + dcmList[dcmSort[0].indx].CSA.protocolSliceNumber1 = -1; if ((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_UIH) || (dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_GE)) dcmList[dcmSort[0].indx].CSA.protocolSliceNumber1 = -1; } @@ -8434,19 +8344,6 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata dcmLi mrifsStruct.dicomlst = new char*[nConvert]; mrifsStruct.nDcm = nConvert; - // retrieve pulseSequenceDetails (tSequenceFileName) - struct TDICOMdata *d = &(mrifsStruct.tdicomData); - strcpy(mrifsStruct.pulseSequenceDetails, ""); - if ((d->manufacturer == kMANUFACTURER_SIEMENS) && (d->CSA.SeriesHeader_offset > 0) && (d->CSA.SeriesHeader_length > 0)) { - float shimSetting[8]; - char protocolName[kDICOMStrLarge], fmriExternalInfo[kDICOMStrLarge], coilID[kDICOMStrLarge], consistencyInfo[kDICOMStrLarge], coilElements[kDICOMStrLarge], pulseSequenceDetails[kDICOMStrLarge], wipMemBlock[kDICOMStrLarge]; - TCsaAscii csaAscii; - siemensCsaAscii(nameList->str[indx0], &csaAscii, d->CSA.SeriesHeader_offset, d->CSA.SeriesHeader_length, shimSetting, coilID, consistencyInfo, coilElements, pulseSequenceDetails, fmriExternalInfo, protocolName, wipMemBlock); - if (strlen(pulseSequenceDetails) >= kDICOMStr) - pulseSequenceDetails[kDICOMStr - 1] = 0; - strcpy(mrifsStruct.pulseSequenceDetails, pulseSequenceDetails); - } - dcmListDump(nConvert, dcmSort, dcmList, nameList, opts); mrifsStruct_vector.push_back(mrifsStruct); @@ -10113,16 +10010,10 @@ void dcmListDump(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata dcmL memset(mrifsStruct.dicomlst[i], 0, strlen(nameList->str[indx])+1); memcpy(mrifsStruct.dicomlst[i], nameList->str[indx], strlen(nameList->str[indx])); - FILE *fp = stdout; - const char *imagelist = getenv("MGH_DCMUNPACK_IMAGELIST"); - if (imagelist != NULL) - fp = fopen(imagelist, "a"); - - fprintf(fp, "%s %ld %s %s %f %f %f %f\\%f %c %f %s %s\n", + printMessage("%s %ld %s %s %f %f %f %f\\%f %c %f %s %s\n", dcmList[indx].patientName, dcmList[indx].seriesNum, dcmList[indx].studyDate, dcmList[indx].studyTime, dcmList[indx].TE, dcmList[indx].TR, dcmList[indx].flipAngle, dcmList[indx].xyzMM[1], dcmList[indx].xyzMM[2], dcmList[indx].phaseEncodingRC, dcmList[indx].pixelBandwidth, nameList->str[indx], dcmList[indx].imageType); - fclose(fp); } } -#endif +#endif \ No newline at end of file