diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index ce3518e1..08d5bde0 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1371,6 +1371,7 @@ 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; @@ -1385,7 +1386,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\"BodyPartExamined\": \"%s\",\n", d.bodyPartExamined); + 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\"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); @@ -1497,9 +1498,10 @@ 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\"RadionuclideTotalDose\": %g,\n", d.radionuclideTotalDose); + 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\"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); @@ -1509,7 +1511,98 @@ tse3d: T2*/ 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); - //json_Float(fp, "\t\"DecayFactor\": %g,\n", d.decayFactor); + + //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); + 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++) { @@ -6859,7 +6952,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) @@ -7437,6 +7530,10 @@ int sliceTimingCore(struct TDCMsort *dcmSort, struct TDICOMdata *dcmList, struct sliceDir = -1; //not sure how to handle negative determinants? } if (sliceDir < 0) { + //Issue 797: For Siemens MOSAICs, slice order depends on ProtocolSliceNumber not temporal order + // for non-mosaic images, we may need to reverse slice order as saved to disk to ensure FSL's preferred negative determinant + //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_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)) @@ -8344,6 +8441,19 @@ 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); @@ -10010,10 +10120,14 @@ 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])); - printMessage("%s %ld %s %s %f %f %f %f\\%f %c %f %s %s\n", + 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", 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); } } -#endif \ No newline at end of file +#endif