From 29c224bf94d79e6e718d1a04c7b9aa371e8de514 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Wed, 6 Nov 2024 09:46:50 -0500 Subject: [PATCH] Siemens series instance UID issues (https://github.com/rordenlab/dcm2niix/issues/394) --- console/nii_dicom.cpp | 17 +++++++++++++---- console/nii_dicom.h | 2 +- console/nii_dicom_batch.cpp | 17 ++++++++++++----- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 733a12fa..159ea43a 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4838,6 +4838,7 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D // philDTI[i].V[0] = -1; // array for storing DimensionIndexValues int numDimensionIndexValues = 0; + bool isSiemensXA = false; //don't use stack! TDCMdim dcmDim[kMaxSlice2D]; TDCMdim *dcmDim = (TDCMdim *)malloc(kMaxSlice2D * sizeof(TDCMdim)); for (int i = 0; i < kMaxSlice2D; i++) { @@ -5818,6 +5819,8 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D case kSoftwareVersions: { dcmStr(lLength, &buffer[lPos], d.softwareVersions); int slen = (int)strlen(d.softwareVersions); + if ((slen > 4) && (strstr(d.softwareVersions, "XA10") != NULL)) + d.isXA10A = true; if ((slen > 4) && (strstr(d.softwareVersions, "XA11") != NULL)) d.isXA10A = true; if ((slen > 4) && (strstr(d.softwareVersions, "XA20") != NULL)) @@ -5826,9 +5829,15 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D d.isXA10A = true; if ((slen > 4) && (strstr(d.softwareVersions, "XA31") != NULL)) d.isXA10A = true; - if ((slen < 5) || (strstr(d.softwareVersions, "XA10") == NULL)) - break; - d.isXA10A = true; + //isXA10A is designed to catch early Siemens bugs, while isSiemensXA also detect modern XA + if (d.isXA10A) + isSiemensXA = true; + if ((slen > 4) && (strstr(d.softwareVersions, "XA5") != NULL)) + isSiemensXA = true; //XA50/XA51 + if ((slen > 4) && (strstr(d.softwareVersions, "XA6") != NULL)) + isSiemensXA = true; //XA60 + if ((slen > 4) && (strstr(d.softwareVersions, "XA7") != NULL)) + isSiemensXA = true; //XA70 break; } case kProtocolName: { @@ -8265,7 +8274,7 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D if (d.phaseNumber > 0) // Philips TurboQUASAR set this uniquely for each slice d.triggerDelayTime = 0.0; // printf("%d\t%g\t%g\t%g\n", d.imageNum, d.acquisitionTime, d.triggerDelayTime, MRImageDynamicScanBeginTime); - if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (strlen(seriesTimeTxt) > 1) && (d.isXA10A) && (d.xyzDim[3] == 1) && (d.xyzDim[4] < 2)) { + if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (strlen(seriesTimeTxt) > 1) && (isSiemensXA) && (d.xyzDim[3] == 1) && (d.xyzDim[4] < 2)) { // printWarning(">>Ignoring series number of XA data saved as classic DICOM (issue 394)\n"); d.isStackableSeries = true; d.imageNum += (d.seriesNum * 1000); diff --git a/console/nii_dicom.h b/console/nii_dicom.h index a3a2daf7..fa962fe8 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.20241025" +#define kDCMdate "v1.0.20241106" #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 aac294e3..a007036a 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -2611,6 +2611,10 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st if (isnan(dcmList[indx0].patientPosition[1])) return NULL; // issue606 do not save bvec for non-spatial data (e.g. physio) int numDti = dcmList[indx0].CSA.numDti; + if ((numDti > nConvert) && (numDti > kMaxSlice2D)) { + printError("nii_saveDTI unable to save vectors.\n"); + return NULL; + } #ifdef USING_R ImageList *images = (ImageList *)opts.imageList; #endif @@ -2674,15 +2678,16 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st TDTI *vx = NULL; if (numDti > 1) { vx = (TDTI *)malloc(numDti * sizeof(TDTI)); - for (int i = 0; i < numDti; i++) // for each direction - for (int v = 0; v < 4; v++) // for each vector+B-value + for (int i = 0; i < numDti; i++) {// for each direction + for (int v = 0; v < 4; v++) // for each vector+B-value vx[i].V[v] = dti4D->S[i].V[v]; + } } else { // if (numDti == 1) {//extract DTI from different slices vx = (TDTI *)malloc(nConvert * sizeof(TDTI)); numDti = 0; for (int i = 0; i < nConvert; i++) { // for each image if ((dcmList[indx0].CSA.mosaicSlices > 1) || (isSamePosition(dcmList[indx0], dcmList[dcmSort[i].indx]))) { - // if (numDti < kMaxDTIv) + int idx = dcmSort[i].indx; for (int v = 0; v < 4; v++) // for each vector+B-value vx[numDti].V[v] = dcmList[dcmSort[i].indx].CSA.dtiV[v]; // dcmList[indx0].CSA.dtiV[numDti][v] = dcmList[dcmSort[i].indx].CSA.dtiV[0][v]; numDti++; @@ -6696,6 +6701,10 @@ void sliceTimingXA(struct TDCMsort *dcmSort, struct TDICOMdata *dcmList, struct uint64_t indx0 = dcmSort[0].indx; // first volume if ((!dcmList[indx0].isXA10A) || (hdr->dim[3] < 1) || (hdr->dim[4] < 1)) return; + if (hdr->dim[3] > kMaxEPI3D) { + printWarning("Unable to set Siemens XA sliceTiming due to excessive slices per volume (%d).\n", hdr->dim[3]); + return; + } if ((nConvert == (hdr->dim[3] * hdr->dim[4])) && (hdr->dim[3] < (kMaxEPI3D - 1)) && (hdr->dim[3] > 1)) { // issue875 use 2nd volume int offset = 0; @@ -8081,7 +8090,6 @@ int saveDcm2NiiCore(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata d #ifdef USING_DCM2NIIXFSWRAPPER printMessage("load Image %s\n", nameList->str[indx]); #endif - // printMessage(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]); bool isHasOverlay = dcmList[indx0].isHasOverlay; bool isDerived = dcmList[indx0].isDerived; @@ -8465,7 +8473,6 @@ int saveDcm2NiiCore(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata d checkDateTimeOrder(&dcmList[dcmSort[0].indx], &dcmList[dcmSort[nConvert - 1].indx]); } bool ok = setBids(&dcmList[indx0], nameList->str[dcmSort[0].indx], nConvert, opts.isVerbose); - if (opts.isIgnoreDerivedAnd2D && !ok) { printMessage("Ignoring derived image(s) of series %ld %s\n", dcmList[indx].seriesNum, nameList->str[indx]); return EXIT_SUCCESS;