From 029275f9048a28afc52ae78d199301967396d7a1 Mon Sep 17 00:00:00 2001 From: Robert Reid Date: Fri, 22 Dec 2023 13:45:11 -0600 Subject: [PATCH 01/82] issue 769: relax the check for bvec components > 1 a bit. --- console/nii_dicom.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index f6a1387d..5d2158b7 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4040,7 +4040,13 @@ void _update_tvd(struct TVolumeDiffusion *ptvd) { return; //no B=0 if (isReady) { for (int i = 1; i < 4; ++i) { - if (ptvd->_dtiV[i] > 1) { + // Check that _dtiV is not still at its default of [-1, 2, 2, 2] from + // clear_volume(struct TVolumeDiffusion *ptvd). This is mostly avoided + // because of the ptvd->_dtiV[0] >= 0 check above, but was supposedly + // needed at some point. + // issue 769: some bvec components may be slightly (~5%) > 1. AFAIK, + // the relevant value to guard against would be +2. + if (ptvd->_dtiV[i] > 1.5) { isReady = false; break; } From 987fdfb70e2425d6cce7ac5a053cddf4908d9e59 Mon Sep 17 00:00:00 2001 From: Jaemin Shin Date: Thu, 22 Feb 2024 15:26:09 -0600 Subject: [PATCH 02/82] GE UHP/7T Diffusion SliceTiming (#796) --- console/nii_dicom.cpp | 3 ++- console/nii_dicom.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 21b496e1..741af678 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -8058,7 +8058,8 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD // cycling systems: Premier, UHP, 7.0T if ((strstr(d.manufacturersModelName, "Premier") != NULL) || (strstr(d.manufacturersModelName, "UHP") != NULL) || (strstr(d.manufacturersModelName, "7.0T") != NULL)) { // cycling special OFF mode - if (isSameFloatGE(userData15GE, 0.72)) + if (((strstr(d.manufacturersModelName, "Premier") != NULL) && (isSameFloatGE(userData15GE, 0.72))) || + (((strstr(d.manufacturersModelName, "UHP") != NULL) || (strstr(d.manufacturersModelName, "7.0T") != NULL)) && (userData15GE > 0.5) && (userData15GE < 1))) //issue 796 d.diffCyclingModeGE = kGE_DIFF_CYCLING_SPOFF; // 2TR cycling mode else if (userData12GE == 2) { diff --git a/console/nii_dicom.h b/console/nii_dicom.h index 2fbb776e..d173cd8d 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.20240202" +#define kDCMdate "v1.0.20240222" #define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic From 79bcd4c2af96747dfe5ce2a8b224185a2b640f2e Mon Sep 17 00:00:00 2001 From: Yujing Huang Date: Wed, 28 Feb 2024 13:29:59 -0500 Subject: [PATCH 03/82] DCM2NIIXFSLIB changes - dcm2niix_fswrapper: allow subset of dcm2niix options to be set through mri_convert - dcmListDump(): use environment variable MGH_DCMUNPACK_IMAGELIST to communicate with dcm2niix where to output imagelist.dat - retrieve/output 'pulseSequenceDetails (tSequenceFileName)' from DICOM CSA header (0029,1020) --- console/dcm2niix_fswrapper.cpp | 138 +++++++++++++++++++++++++++++++-- console/dcm2niix_fswrapper.h | 5 +- console/nii_dicom_batch.cpp | 21 ++++- console/nii_dicom_batch.h | 1 + 4 files changed, 156 insertions(+), 9 deletions(-) diff --git a/console/dcm2niix_fswrapper.cpp b/console/dcm2niix_fswrapper.cpp index 1286fa37..221eae39 100644 --- a/console/dcm2niix_fswrapper.cpp +++ b/console/dcm2niix_fswrapper.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "nii_dicom.h" @@ -53,15 +54,13 @@ numSeries = 0 */ // set TDCMopts defaults, overwrite settings to output in mgz orientation -void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* niioutdir, bool createBIDS, int ForceStackSameSeries) +void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* dcm2niixopts) { memset(&tdcmOpts, 0, sizeof(tdcmOpts)); setDefaultOpts(&tdcmOpts, NULL); if (dcmindir != NULL) strcpy(tdcmOpts.indir, dcmindir); - if (niioutdir != NULL) - strcpy(tdcmOpts.outdir, niioutdir); // dcmunpack actually uses seriesDescription, set FName = `printf %04d.$descr $series` // change it from "%4s.%p" to "%4s.%d" @@ -71,11 +70,136 @@ void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* niioutdir, bo tdcmOpts.isRotate3DAcq = false; tdcmOpts.isFlipY = false; tdcmOpts.isIgnoreSeriesInstanceUID = true; - tdcmOpts.isCreateBIDS = createBIDS; + tdcmOpts.isCreateBIDS = false; tdcmOpts.isGz = false; - tdcmOpts.isForceStackSameSeries = ForceStackSameSeries; // merge 2D slice '-m y', tdcmOpts.isForceStackSameSeries = 1 + tdcmOpts.isForceStackSameSeries = 1; // merge 2D slice '-m y', tdcmOpts.isForceStackSameSeries = 1 tdcmOpts.isForceStackDCE = false; //tdcmOpts.isForceOnsetTimes = false; + + if (dcm2niixopts != NULL) + __setDcm2niixOpts(dcm2niixopts); +} + +/* set user dcm2niix conversion options in struct TDCMopts tdcmOpts + * only subset of dcm2niix command line options are recognized: (https://manpages.ubuntu.com/manpages/jammy/en/man1/dcm2niix.1.html) + * -b + * Save additional BIDS metadata to a side-car .json file (default y). + * The "i"nput-only option reads DICOMs but saves neither BIDS nor NIfTI. + * + * -ba anonymize BIDS (default y). + * If "n"o, side-car may report patient name, age and weight. + * + * -f + * Format string for the output filename(s). + * + * -i + * Ignore derived, localizer and 2D images (default n) + * + * -m + * Merge slices from the same series regardless of study time, echo, coil, orientation, etc. (default 2). + * If "2", automatic based on image modality. + * + * -v <2/y/n> + * Enable verbose output. "n" for succinct, "y" for verbose, "2" for high verbosity + * + * -o + * Output directory where the converted files should be saved. + * + * -t + * Save patient details as text notes. + * + * -p + * Use Philips precise float (rather than display) scaling. + * + * -x + * Crop images. This will attempt to remove excess neck from 3D acquisitions. + * If "i", images are neither cropped nor rotated to canonical space. + */ +void dcm2niix_fswrapper::__setDcm2niixOpts(const char *dcm2niixopts) +{ + //printf("[DEBUG] dcm2niix_fswrapper::__setDcm2niixOpts(%s)\n", dcm2niixopts); + + char *restOpts = (char*)malloc(strlen(dcm2niixopts)+1); + memset(restOpts, 0, strlen(dcm2niixopts)+1); + memcpy(restOpts, dcm2niixopts, strlen(dcm2niixopts)); + + char *nextOpt = strtok_r((char*)dcm2niixopts, ",", &restOpts); + while (nextOpt != NULL) + { + char *k = nextOpt; + char *v = strchr(nextOpt, '='); + if (v != NULL) + *v = '\0'; + v++; // move past '=' + + // skip leading white spaces + while (*k == ' ') + k++; + + if (strcmp(k, "b") == 0) + { + if (*v == 'n' || *v == 'N' || *v == '0') + tdcmOpts.isCreateBIDS = false; + else if (*v == 'i' || *v == 'I') + { + tdcmOpts.isCreateBIDS = false; + tdcmOpts.isOnlyBIDS = true; + } + else if (*v == 'y' || *v == 'Y') + tdcmOpts.isCreateBIDS = true; + } + else if (strcmp(k, "ba") == 0) + tdcmOpts.isAnonymizeBIDS = (*v == 'n' || *v == 'N') ? false : true; + else if (strcmp(k, "f") == 0) + strcpy(tdcmOpts.filename, v); + else if (strcmp(k, "i") == 0) + tdcmOpts.isIgnoreDerivedAnd2D = (*v == 'y' || *v == 'Y') ? true : false; + else if (strcmp(k, "m") == 0) + { + if (*v == 'n' || *v == 'N' || *v == '0') + tdcmOpts.isForceStackSameSeries = 0; + else if (*v == 'y' || *v == 'Y' || *v == '1') + tdcmOpts.isForceStackSameSeries = 1; + else if (*v == '2') + tdcmOpts.isForceStackSameSeries = 2; + else if (*v == 'o' || *v == 'O') + tdcmOpts.isForceStackDCE = false; + } + else if (strcmp(k, "v") == 0) + { + if (*v == 'n' || *v == 'N' || *v == '0') + tdcmOpts.isVerbose = 0; + else if (*v == 'h' || *v == 'H' || *v == '2') + tdcmOpts.isVerbose = 2; + else + tdcmOpts.isVerbose = 1; + } + else if (strcmp(k, "o") == 0) + strcpy(tdcmOpts.outdir, v); + else if (strcmp(k, "t") == 0) + tdcmOpts.isCreateText = (*v == 'y' || *v == 'Y') ? true : false; + else if (strcmp(k, "p") == 0) + { + if (*v == 'n' || *v == 'N' || *v == '0') + tdcmOpts.isPhilipsFloatNotDisplayScaling = false; + } + else if (strcmp(k, "x") ==0) + { + if (*v == 'y' || *v == 'Y') + tdcmOpts.isCrop = true; + else if (*v == 'i' || *v == 'I') + { + tdcmOpts.isRotate3DAcq = false; + tdcmOpts.isCrop = false; + } + } + else + { + printf("[WARN] dcm2niix option %s=%s skipped\n", k, v); + } + + nextOpt = strtok_r(NULL, ",", &restOpts); + } } // interface to isDICOMfile() in nii_dicom.cpp @@ -157,10 +281,10 @@ void dcm2niix_fswrapper::dicomDump(const char* dicomdir, const char *series_info fclose(fp_dcmLst); // output series_info - fprintf(fpout, "%ld %s %f %f %f %f\\%f %c %f %s %s", + fprintf(fpout, "%ld %s %f %f %f %f\\%f %c %f %s %s %s", tdicomData->seriesNum, tdicomData->seriesDescription, tdicomData->TE, tdicomData->TR, tdicomData->flipAngle, tdicomData->xyzMM[1], tdicomData->xyzMM[2], - tdicomData->phaseEncodingRC, tdicomData->pixelBandwidth, (*mrifsStruct_vector)[n].dicomfile, tdicomData->imageType); + tdicomData->phaseEncodingRC, tdicomData->pixelBandwidth, (*mrifsStruct_vector)[n].dicomfile, tdicomData->imageType, (*mrifsStruct_vector)[n].pulseSequenceDetails); #if 0 if (max) { diff --git a/console/dcm2niix_fswrapper.h b/console/dcm2niix_fswrapper.h index 0a71a8da..bfb417ad 100644 --- a/console/dcm2niix_fswrapper.h +++ b/console/dcm2niix_fswrapper.h @@ -17,7 +17,7 @@ class dcm2niix_fswrapper { public: // set TDCMopts defaults, overwrite settings to output in mgz orientation. - static void setOpts(const char* dcmindir, const char* niioutdir=NULL, bool createBIDS=false, int ForceStackSameSeries=1); + static void setOpts(const char* dcmindir, const char* dcm2niixopts=NULL); // interface to isDICOMfile() in nii_dicom.cpp static bool isDICOM(const char* file); @@ -47,6 +47,9 @@ class dcm2niix_fswrapper private: static struct TDCMopts tdcmOpts; + + // + static void __setDcm2niixOpts(const char *dcm2niixopts); }; #endif diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 3b2eb676..839921d5 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -8342,6 +8342,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); @@ -10008,10 +10021,16 @@ 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); + fclose(fp); } } #endif diff --git a/console/nii_dicom_batch.h b/console/nii_dicom_batch.h index f568fe79..efb7b88f 100644 --- a/console/nii_dicom_batch.h +++ b/console/nii_dicom_batch.h @@ -16,6 +16,7 @@ struct MRIFSSTRUCT size_t imgsz; unsigned char *imgM; + char pulseSequenceDetails[kDICOMStr]; struct TDICOMdata tdicomData; char namePostFixes[256]; char *dicomfile; From cb60b430e590e22d6da65898871ae97e5727bef5 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Fri, 1 Mar 2024 15:30:26 -0500 Subject: [PATCH 04/82] Philips renaming (https://github.com/rordenlab/dcm2niix/issues/795) --- console/nii_dicom.cpp | 44 +++++++++++++++++++++++++------------ console/nii_dicom.h | 2 +- console/nii_dicom_batch.cpp | 2 +- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 741af678..361b3a26 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -5166,6 +5166,10 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD privateCreatorRemap = 0; char privateCreator[kDICOMStr]; dcmStr(lLength, &buffer[lPos], privateCreator); + // + int s_len = strlen(privateCreator); + for (int i = 0; i < s_len; i++) + privateCreator[i] = toupper(privateCreator[i]); //next lines determine remapping, append as needed //Siemens https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/siemens.tpl if (strstr(privateCreator, "SIEMENS MR HEADER") != NULL) @@ -5194,18 +5198,30 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if ((grp == 0x07a3) && (strstr(privateCreator, "ELSCINT1") != NULL)) privateCreatorRemap = 0x07a3 + (0x1000 << 16); //Philips https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/philips.tpl - if (strstr(privateCreator, "PHILIPS IMAGING DD 001") != NULL) - privateCreatorRemap = 0x2001 + (0x1000 << 16); - if (strstr(privateCreator, "Philips Imaging DD 001") != NULL) - privateCreatorRemap = 0x2001 + (0x1000 << 16); - if (strstr(privateCreator, "PHILIPS MR IMAGING DD 001") != NULL) - privateCreatorRemap = 0x2005 + (0x1000 << 16); - if (strstr(privateCreator, "Philips MR Imaging DD 001") != NULL) - privateCreatorRemap = 0x2005 + (0x1000 << 16); - if (strstr(privateCreator, "PHILIPS MR IMAGING DD 005") != NULL) - privateCreatorRemap = 0x2005 + (0x1400 << 16); - if (strstr(privateCreator, "Philips MR Imaging DD 005") != NULL) - privateCreatorRemap = 0x2005 + (0x1400 << 16); + if (strstr(privateCreator, "PHILIPS IMAGING") != NULL) { + if (strstr(privateCreator, "DD 001") != NULL) + privateCreatorRemap = 0x2001 + (0x1000 << 16); + if (strstr(privateCreator, "DD 002") != NULL) + privateCreatorRemap = 0x2001 + (0x1100 << 16); + if (strstr(privateCreator, "DD 003") != NULL) + privateCreatorRemap = 0x2001 + (0x1200 << 16); + if (strstr(privateCreator, "DD 004") != NULL) + privateCreatorRemap = 0x2001 + (0x1300 << 16); + if (strstr(privateCreator, "DD 005") != NULL) + privateCreatorRemap = 0x2001 + (0x1400 << 16); + } + if (strstr(privateCreator, "PHILIPS MR IMAGING") != NULL) { + if (strstr(privateCreator, "DD 001") != NULL) + privateCreatorRemap = 0x2005 + (0x1000 << 16); + if (strstr(privateCreator, "DD 002") != NULL) + privateCreatorRemap = 0x2005 + (0x1100 << 16); + if (strstr(privateCreator, "DD 003") != NULL) + privateCreatorRemap = 0x2005 + (0x1200 << 16); + if (strstr(privateCreator, "DD 004") != NULL) + privateCreatorRemap = 0x2005 + (0x1300 << 16); + if (strstr(privateCreator, "DD 005") != NULL) + privateCreatorRemap = 0x2005 + (0x1400 << 16); + } //UIH https://github.com/neurolabusc/dcm_qa_uih if (strstr(privateCreator, "Image Private Header") != NULL) privateCreatorRemap = 0x0065 + (0x1000 << 16); @@ -6392,8 +6408,8 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kTE: TE = dcmStrFloat(lLength, &buffer[lPos]); - if (d.TE <= 0.0) - d.TE = TE; + //20240229 if (d.TE <= 0.0) + d.TE = TE; break; case kNumberOfAverages: d.numberOfAverages = dcmStrFloat(lLength, &buffer[lPos]); diff --git a/console/nii_dicom.h b/console/nii_dicom.h index d173cd8d..8008468a 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.20240222" +#define kDCMdate "v1.0.20240229" #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 839921d5..11cba2d4 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -6859,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) From c975d1f39a6d9b5c34c152a3c337fecd08158488 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Mon, 11 Mar 2024 08:13:58 -0400 Subject: [PATCH 05/82] PET BIDS (https://github.com/rordenlab/dcm2niix/issues/802) --- BIDS/README.md | 47 +++++++++++++++++++++++-------------- console/nii_dicom.cpp | 37 +++++++++++++++++++++++++++++ console/nii_dicom.h | 6 ++--- console/nii_dicom_batch.cpp | 10 ++++---- 4 files changed, 75 insertions(+), 25 deletions(-) diff --git a/BIDS/README.md b/BIDS/README.md index c48d5a43..f6f0dbc6 100644 --- a/BIDS/README.md +++ b/BIDS/README.md @@ -78,7 +78,7 @@ These fields are present regardless of modality (e.g. MR, CT, PET). | Field | Unit | Comments | Defined By | |--------------------------|------|---------------------|------------| -| BodyPartExamined | | DICOM tag 0018,0015 | D | +| BodyPart | | DICOM tag 0018,0015 | B | | PatientPosition | | DICOM tag 0020,0032 | D | | ProcedureStepDescription | | DICOM tag 0040,0254 | D | | SoftwareVersions | | DICOM tag 0020,1020 | B | @@ -107,6 +107,9 @@ These fields contain personally identifiable information. By default dcm2niix wi | PatientID | | DICOM tag 0010,0020 | D | | AccessionNumber | | DICOM tag 0008,0050 | D | | PatientBirthDate | | DICOM tag 0010,0030 | D | +| PatientSex | | DICOM tag 0010,0040 | D | +| PatientAge | | DICOM tag 0010,1010 | D | +| PatientSize | | DICOM tag 0010,1020 | D | | PatientWeight | kg | DICOM tag 0010,1030 | D | | AcquisitionDateTime | | DICOM tag 0008,002A | D | @@ -181,23 +184,31 @@ PET fields extracted from [DICOM tags](http://dicom.nema.org/medical/dicom/curre The term ECAT in the comments suggests that values are defined by the [ECAT7](http://www.turkupetcentre.net/petanalysis/format_image_ecat.html) format. Therefore, these fields will not be populated for DICOM data. -| Field | Unit | Comments | Defined By | -|------------------------------|------|-----------------------------|------------| -| Radiopharmaceutical | | DICOM tag 0018,0031 or ECAT | D | -| RadionuclidePositronFraction | f | DICOM tag 0018,1076 | D | -| RadionuclideTotalDose | MBq | DICOM tag 0018,1074 | D | -| RadionuclideHalfLife | s | DICOM tag 0018,1075 | D | -| DoseCalibrationFactor | | DICOM tag 0054,1322 | D | -| IsotopeHalfLife | | ECAT | D | -| Dosage | | ECAT | D | -| ConvolutionKernel | | DICOM tag 0018,1210 | D | -| Units | | DICOM tag 0054,1001 | D | -| DecayCorrection | | DICOM tag 0054,1102 | D | -| AttenuationCorrectionMethod | | DICOM tag 0054,1101 | D | -| ReconstructionMethod | | DICOM tag 0054,1103 | D | -| DecayFactor | | DICOM tag 0054,1321 | D | -| FrameTimesStart | s | DICOM tags 0008,0022 | D | -| FrameDuration | s | DICOM tag 0018,1242 | D | +| Field | Unit | Comments | Defined By | +|------------------------------|------|----------------------------------|------------| +| IsotopeHalfLife | | ECAT | D | +| Dosage | | ECAT | D | +| FrameTimesStart | s | DICOM tag 0008,0022 | D | +| TracerRadionuclide | | DICOM tag 0008,0100 or 0008,0104 | B | +| Radiopharmaceutical | | DICOM tag 0018,0031 or ECAT | D | +| InjectedRadioactivity | MBq | DICOM tag 0018,1074 | B | +| RadionuclideHalfLife | s | DICOM tag 0018,1075 | D | +| RadionuclidePositronFraction | f | DICOM tag 0018,1076 | D | +| ConvolutionKernel | | DICOM tag 0018,1210 | D | +| Units | | DICOM tag 0054,1001 | B | +| AttenuationCorrectionMethod | | DICOM tag 0054,1101 | B | +| DecayCorrection | | DICOM tag 0054,1102 | D | +| ReconstructionMethod | | DICOM tag 0054,1103 | B | +| DecayCorrectionFactor | | DICOM tag 0054,1321 | B | +| DoseCalibrationFactor | | DICOM tag 0054,1322 | B | +| ScatterFraction | | DICOM tag 0054,1323 | B | +| FrameDuration | s | DICOM tag 0018,1242 | B | + +n.b. ConvolutionKernel (0018,1210) can be parsed to BIDS `ReconFilterType` and `ReconFilterSize`. However, manufacturer variations (Siemens: `XYZGAUSSIAN3.00` GE `Rad: \ rectangle \ 4.000000 mm \ Ax: \ rectangle \ 8.500000 mm` require complex logic). + +n.b. ReconstructionMethod (0054,1103) can be parsed to ReconMethodName, ReconMethodParameterLabels, ReconMethodParameterUnits and ReconMethodParameterValues. However, this requires maniufacturer specific logic (e.g. Siemens `OP-OSEM4i21s`, GE `3D Kinahan - Rogers` + +For Philips scanners, [Source Isotope Name](https://dicom.innolitics.com/ciods/rt-brachy-treatment-record/rt-brachy-session-record/30080100/300a0226) may be a good source for TracerRadionuclide ## Manufacturer Fields diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 361b3a26..8acc262a 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -786,6 +786,7 @@ struct TDICOMdata clear_dicom_data() { strcpy(d.convolutionKernel, ""); strcpy(d.parallelAcquisitionTechnique, ""); strcpy(d.imageOrientationText, ""); + strcpy(d.tracerRadionuclide, ""); strcpy(d.unitsPT, ""); strcpy(d.decayCorrection, ""); strcpy(d.attenuationCorrectionMethod, ""); @@ -833,6 +834,7 @@ struct TDICOMdata clear_dicom_data() { d.shimGradientZ = -33333;//impossible value for UINT16 strcpy(d.prescanReuseString, ""); d.decayFactor = 0.0; + d.scatterFraction = 0.0; d.percentSampling = 0.0; d.phaseFieldofView = 0.0; d.dwellTime = 0; @@ -936,6 +938,7 @@ struct TDICOMdata clear_dicom_data() { d.numberOfImagesInGridUIH = 0; d.phaseEncodingRC = '?'; d.patientSex = '?'; + d.patientSize = 0.0; d.patientWeight = 0.0; strcpy(d.patientBirthDate, ""); strcpy(d.patientAge, ""); @@ -4362,6 +4365,8 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D #define kInstitutionName 0x0008 + (0x0080 << 16) #define kInstitutionAddress 0x0008 + (0x0081 << 16) #define kReferringPhysicianName 0x0008 + (0x0090 << 16) +#define kTracerRadionuclide1 0x0008 + (0x0100 << 16) //SH +#define kTracerRadionuclide2 0x0008 + (0x0104 << 16) //LO #define kStationName 0x0008 + (0x1010 << 16) #define kSeriesDescription 0x0008 + (0x103E << 16) // '0008' '103E' 'LO' 'SeriesDescription' #define kInstitutionalDepartmentName 0x0008 + (0x1040 << 16) @@ -4376,6 +4381,7 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D #define kPatientBirthDate 0x0010 + (0x0030 << 16) #define kPatientSex 0x0010 + (0x0040 << 16) #define kPatientAge 0x0010 + (0x1010 << 16) +#define kPatientSize 0x0010 + (0x1020 << 16) //DS #define kPatientWeight 0x0010 + (0x1030 << 16) #define kAnatomicalOrientationType 0x0010 + (0x2210 << 16) #define kDeidentificationMethod 0x0012 + (0x0063 << 16) //[DICOMANON, issue 383 @@ -4581,6 +4587,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kReconstructionMethod 0x0054 + (0x1103 << 16) //LO #define kFrameReferenceTime 0x0054 + (0x1300 << 16) //DS #define kDecayFactor 0x0054 + (0x1321 << 16) //DS +#define kScatterFraction 0x0054 + (0x1323 << 16) //DS //ftp://dicom.nema.org/MEDICAL/dicom/2014c/output/chtml/part03/sect_C.8.9.4.html //If ImageType is REPROJECTION we slice direction is reversed - need example to test // #define kSeriesType 0x0054+(0x1000 << 16 ) @@ -5656,9 +5663,35 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kPatientAge: dcmStr(lLength, &buffer[lPos], d.patientAge); break; + case kPatientSize: + d.patientSize = dcmStrFloat(lLength, &buffer[lPos]); + break; case kPatientWeight: d.patientWeight = dcmStrFloat(lLength, &buffer[lPos]); break; +/* n.b. check GeneralElectricAdvance-NIMH example dataset: need to speficy SQ + (0018,0031) LO [FDG -- fluorodeoxyglucose ] # 26,1 Radiopharmaceutical + (0018,1071) DS [0 ] # 2,1 Radiopharmaceutical Volume + (0018,1072) TM [092345.00 ] # 10,1 Radiopharmaceutical Start Time + (0018,1074) DS [ 75850000] # 14,1 Radionuclide Total Dose + (0018,1075) DS [6588] # 4,1 Radionuclide Half Life + (0018,1076) DS [0.97000002861023] # 16,1 Radionuclide Positron Fraction + (0054,0300) SQ (Sequence with undefined length) # u/l,1 Radionuclide Code Sequence + (fffe,e000) na (Item with defined length) + (0008,0100) SH [C-111A1 ] # 8,1 Code Value + (0008,0102) SH [99SDM ] # 6,1 Coding Scheme Designator + (0008,0104) LO [18F ] # 4,1 Code Meaning + (fffe,e0dd) + (0054,0304) SQ (Sequence with undefined length) # u/l,1 Radiopharmaceutical Code Sequence + (fffe,e000) na (Item with defined length) + (0008,0100) SH [Y-X1743 ] # 8,1 Code Value + (0008,0102) SH [99SDM ] # 6,1 Coding Scheme Designator + (0008,0104) LO [FDG -- fluorodeoxyglucose ] # 26,1 Code Meaning + (fffe,e0dd) + case kTracerRadionuclide1: + case kTracerRadionuclide2: + dcmStr(lLength, &buffer[lPos], d.tracerRadionuclide); + break;*/ case kStationName: dcmStr(lLength, &buffer[lPos], d.stationName); break; @@ -6645,6 +6678,10 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kDecayFactor: d.decayFactor = dcmStrFloat(lLength, &buffer[lPos]); break; + case kScatterFraction: + d.scatterFraction = dcmStrFloat(lLength, &buffer[lPos]); + printf("SF%g\n", d.scatterFraction); //for each slice? + break; case kIconImageSequence: if (lLength > 8) break; //issue638: we will skip entire icon if there is an explicit length diff --git a/console/nii_dicom.h b/console/nii_dicom.h index 8008468a..ccc92240 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.20240229" +#define kDCMdate "v1.0.20240308" #define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic @@ -241,13 +241,13 @@ static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8; uint32_t coilCrc, seriesUidCrc, instanceUidCrc; int overlayStart[kMaxOverlay]; int postLabelDelay, shimGradientX, shimGradientY, shimGradientZ, phaseNumber, spoiling, mtState, partialFourierDirection, interp3D, aslFlags, durationLabelPulseGE, epiVersionGE, internalepiVersionGE, maxEchoNumGE, rawDataRunNumber, numberOfTR, numberOfImagesInGridUIH, numberOfDiffusionT2GE, numberOfDiffusionDirectionGE, tensorFileGE, diffCyclingModeGE, phaseEncodingGE, protocolBlockStartGE, protocolBlockLengthGE, modality, dwellTime, effectiveEchoSpacingGE, phaseEncodingLines, phaseEncodingSteps, frequencyEncodingSteps, phaseEncodingStepsOutOfPlane, echoTrainLength, echoNum, sliceOrient, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,locationsInAcquisition, locationsInAcquisitionConflict, compressionScheme; - float compressedSensingFactor, xRayTubeCurrent, exposureTimeMs, numberOfExcitations, numberOfArms, numberOfPointsPerArm, groupDelay, decayFactor, percentSampling,waterFatShift, numberOfAverages, patientWeight, zSpacing, zThick, pixelBandwidth, SAR, phaseFieldofView, accelFactPE, accelFactOOP, flipAngle, fieldStrength, TE, TI, TR, intenScale, intenIntercept, intenScalePhilips, gantryTilt, lastScanLoc, angulation[4], velocityEncodeScaleGE; + float compressedSensingFactor, xRayTubeCurrent, exposureTimeMs, numberOfExcitations, numberOfArms, numberOfPointsPerArm, groupDelay, decayFactor, scatterFraction, percentSampling,waterFatShift, numberOfAverages, patientSize, patientWeight, zSpacing, zThick, pixelBandwidth, SAR, phaseFieldofView, accelFactPE, accelFactOOP, flipAngle, fieldStrength, TE, TI, TR, intenScale, intenIntercept, intenScalePhilips, gantryTilt, lastScanLoc, angulation[4], velocityEncodeScaleGE; float orient[7], patientPosition[4], patientPositionLast[4], xyzMM[4], stackOffcentre[4]; float rtia_timerGE, radionuclidePositronFraction, radionuclideTotalDose, radionuclideHalfLife, doseCalibrationFactor; //PET ISOTOPE MODULE ATTRIBUTES (C.8-57) float frameReferenceTime, frameDuration, ecat_isotope_halflife, ecat_dosage; float pixelPaddingValue; // used for both FloatPixelPaddingValue (0028, 0122) and PixelPaddingValue (0028, 0120); NaN if not present. double imagingFrequency, acquisitionDuration, triggerDelayTime, RWVScale, RWVIntercept, dateTime, acquisitionTime, acquisitionDate, bandwidthPerPixelPhaseEncode; - char parallelAcquisitionTechnique[kDICOMStr], radiopharmaceutical[kDICOMStr], convolutionKernel[kDICOMStr], unitsPT[kDICOMStr], decayCorrection[kDICOMStr], attenuationCorrectionMethod[kDICOMStr],reconstructionMethod[kDICOMStr], transferSyntax[kDICOMStr]; + char parallelAcquisitionTechnique[kDICOMStr], radiopharmaceutical[kDICOMStr], convolutionKernel[kDICOMStr], unitsPT[kDICOMStr], tracerRadionuclide[kDICOMStr], decayCorrection[kDICOMStr], attenuationCorrectionMethod[kDICOMStr],reconstructionMethod[kDICOMStr], transferSyntax[kDICOMStr]; char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr]; char deepLearningText[kDICOMStrLarge], scanOptions[kDICOMStrLarge], institutionAddress[kDICOMStrLarge], imageComments[kDICOMStrLarge]; uint32_t dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS]; diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 11cba2d4..3ac8913b 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,19 +1498,20 @@ 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); 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\"DecayCorrection\": \"%s\",\n", d.decayCorrection); + 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\"AttenuationCorrectionMethod\": \"%s\",\n", d.attenuationCorrectionMethod); json_Str(fp, "\t\"ReconstructionMethod\": \"%s\",\n", d.reconstructionMethod); - //json_Float(fp, "\t\"DecayFactor\": %g,\n", d.decayFactor); + 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++) { From cc7d7f3115cd39b716b107caef8a67afa33a7702 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Mon, 11 Mar 2024 08:56:54 -0400 Subject: [PATCH 06/82] codespell --- console/nii_dicom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 8acc262a..1d972c7a 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -5669,7 +5669,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kPatientWeight: d.patientWeight = dcmStrFloat(lLength, &buffer[lPos]); break; -/* n.b. check GeneralElectricAdvance-NIMH example dataset: need to speficy SQ +/* n.b. check GeneralElectricAdvance-NIMH example dataset: need to specify SQ (0018,0031) LO [FDG -- fluorodeoxyglucose ] # 26,1 Radiopharmaceutical (0018,1071) DS [0 ] # 2,1 Radiopharmaceutical Volume (0018,1072) TM [092345.00 ] # 10,1 Radiopharmaceutical Start Time From f8baa94756decadf38a2f5fdbeb604871ae9867f Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Mon, 11 Mar 2024 16:06:36 -0400 Subject: [PATCH 07/82] more PET support (https://github.com/rordenlab/dcm2niix/issues/802) --- console/nii_dicom.cpp | 2 +- console/nii_dicom_batch.cpp | 90 +++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 1d972c7a..612efdb7 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -6680,7 +6680,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kScatterFraction: d.scatterFraction = dcmStrFloat(lLength, &buffer[lPos]); - printf("SF%g\n", d.scatterFraction); //for each slice? + //printf("SF%g\n", d.scatterFraction); //for each slice? break; case kIconImageSequence: if (lLength > 8) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 3ac8913b..bca5d118 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1511,6 +1511,96 @@ tse3d: T2*/ 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\"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); if (dti4D->decayFactor[0] >= 0.0) { //see BEP009 PET https://docs.google.com/document/d/1mqMLnxVdLwZjDd4ZiWFqjEAmOmfcModA_R535v3eQs0 fprintf(fp, "\t\"DecayFactor\": [\n"); From e5256f8d247acc93f6e6b6e87a4e4ac1c724e329 Mon Sep 17 00:00:00 2001 From: Jaemin Shin Date: Wed, 13 Mar 2024 16:27:43 -0400 Subject: [PATCH 08/82] GE UHP/7T Diffusion SliceTiming Update (#796) --- console/nii_dicom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 612efdb7..c498e186 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -8112,7 +8112,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if ((strstr(d.manufacturersModelName, "Premier") != NULL) || (strstr(d.manufacturersModelName, "UHP") != NULL) || (strstr(d.manufacturersModelName, "7.0T") != NULL)) { // cycling special OFF mode if (((strstr(d.manufacturersModelName, "Premier") != NULL) && (isSameFloatGE(userData15GE, 0.72))) || - (((strstr(d.manufacturersModelName, "UHP") != NULL) || (strstr(d.manufacturersModelName, "7.0T") != NULL)) && (userData15GE > 0.5) && (userData15GE < 1))) //issue 796 + (((strstr(d.manufacturersModelName, "UHP") != NULL) || (strstr(d.manufacturersModelName, "7.0T") != NULL)) && (userData15GE > 0.5) && (userData15GE <= 1.0))) //issue 796 d.diffCyclingModeGE = kGE_DIFF_CYCLING_SPOFF; // 2TR cycling mode else if (userData12GE == 2) { From 8becfeb5c6b6fa86635f7c26b224dabdb9915736 Mon Sep 17 00:00:00 2001 From: Jaemin Shin Date: Thu, 14 Mar 2024 10:49:37 -0400 Subject: [PATCH 09/82] GE UHP/7T Diffusion SliceTiming Update2 --- console/nii_dicom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index c498e186..3d1b8bb0 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -8112,7 +8112,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if ((strstr(d.manufacturersModelName, "Premier") != NULL) || (strstr(d.manufacturersModelName, "UHP") != NULL) || (strstr(d.manufacturersModelName, "7.0T") != NULL)) { // cycling special OFF mode if (((strstr(d.manufacturersModelName, "Premier") != NULL) && (isSameFloatGE(userData15GE, 0.72))) || - (((strstr(d.manufacturersModelName, "UHP") != NULL) || (strstr(d.manufacturersModelName, "7.0T") != NULL)) && (userData15GE > 0.5) && (userData15GE <= 1.0))) //issue 796 + (((strstr(d.manufacturersModelName, "UHP") != NULL) || (strstr(d.manufacturersModelName, "7.0T") != NULL)) && (userData15GE >= 0.5) && (userData15GE <= 1.0))) //issue 796 d.diffCyclingModeGE = kGE_DIFF_CYCLING_SPOFF; // 2TR cycling mode else if (userData12GE == 2) { From 484de5bf5ec76cdc668e5e547c9803e58e6f3cc7 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Thu, 14 Mar 2024 11:30:03 -0400 Subject: [PATCH 10/82] 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 From 1858e1c1c2dbda1a5a0c676ae7a91a18b937a7e5 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Fri, 15 Mar 2024 08:15:49 -0400 Subject: [PATCH 11/82] Restore PR modifications (https://github.com/rordenlab/dcm2niix/commit/484de5bf5ec76cdc668e5e547c9803e58e6f3cc7) --- console/nii_dicom_batch.cpp | 126 ++++++++++++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 6 deletions(-) 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 From 04d6d49378dee9ecef42c0270c033479bd020a1b Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Fri, 15 Mar 2024 08:23:56 -0400 Subject: [PATCH 12/82] ASL BIDS suggestions from @jan-petr --- console/nii_dicom_batch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 08d5bde0..6335ca61 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1796,7 +1796,7 @@ tse3d: T2*/ if ((strstr(pulseSequenceDetails, "ep2d_pcasl")) || (strstr(pulseSequenceDetails, "ep2d_pcasl_UI_PHC"))) { isPCASL = true; repetitionTimePreparation = d.TR; - json_FloatNotNan(fp, "\t\"LabelOffset\": %g,\n", csaAscii.adFree[1]); //mm + json_FloatNotNan(fp, "\t\"LabelingDistance\": %g,\n", csaAscii.adFree[1]); //mm, renamed for BIDS https://bids-specification.readthedocs.io/en/stable/glossary.html#labelingdistance-metadata json_FloatNotNan(fp, "\t\"PostLabelingDelay\": %g,\n", csaAscii.adFree[2] * (1.0 / 1000000.0)); //usec -> sec float num_RF_Block = csaAscii.adFree[3]; json_FloatNotNan(fp, "\t\"NumRFBlocks\": %g,\n", num_RF_Block); From d635970cff10720cbb53f792a2b6c97dc2251eb7 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Fri, 15 Mar 2024 08:25:22 -0400 Subject: [PATCH 13/82] Another suggestion from @jan-petr --- console/nii_dicom_batch.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 6335ca61..5f080c66 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1810,6 +1810,7 @@ tse3d: T2*/ if (strstr(pulseSequenceDetails, "tgse_pcasl")) { isPCASL = true; repetitionTimePreparation = d.TR; + json_FloatNotNan(fp, "\t\"LabelingDuration\": %g,\n", csaAscii.adFree[2] * (1.0 / 1000000.0)); //usec -> sec 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\"T1\": %g,\n", csaAscii.adFree[12] * (1.0 / 1000000.0)); //usec -> sec From 1b4505ee973420a965c220a8fa76666aba01ca5d Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Thu, 21 Mar 2024 11:43:15 -0400 Subject: [PATCH 14/82] Experimental start for reproin support --- BIDS/README.md | 1 + console/nii_dicom.cpp | 5 +++ console/nii_dicom.h | 2 +- console/nii_dicom_batch.cpp | 86 +++++++++++++++++++++++++++++++++++-- 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/BIDS/README.md b/BIDS/README.md index f6f0dbc6..31a440da 100644 --- a/BIDS/README.md +++ b/BIDS/README.md @@ -82,6 +82,7 @@ These fields are present regardless of modality (e.g. MR, CT, PET). | PatientPosition | | DICOM tag 0020,0032 | D | | ProcedureStepDescription | | DICOM tag 0040,0254 | D | | SoftwareVersions | | DICOM tag 0020,1020 | B | +| StudyDescription | | DICOM tag 0008,1030 | D | | SeriesDescription | | DICOM tag 0008,103E | D | | ProtocolName | | DICOM tag 0018,1030 | D | | ScanningSequence | | DICOM tag 0018,0020 | B | diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 3d1b8bb0..c2a5f2c2 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -772,6 +772,7 @@ struct TDICOMdata clear_dicom_data() { strcpy(d.deviceSerialNumber, ""); strcpy(d.softwareVersions, ""); strcpy(d.stationName, ""); + strcpy(d.studyDescription, ""); strcpy(d.scanOptions, ""); //strcpy(d.mrAcquisitionType, ""); strcpy(d.seriesInstanceUID, ""); @@ -4368,6 +4369,7 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D #define kTracerRadionuclide1 0x0008 + (0x0100 << 16) //SH #define kTracerRadionuclide2 0x0008 + (0x0104 << 16) //LO #define kStationName 0x0008 + (0x1010 << 16) +#define kStudyDescription 0x0008 + (0x1030 << 16) //LO #define kSeriesDescription 0x0008 + (0x103E << 16) // '0008' '103E' 'LO' 'SeriesDescription' #define kInstitutionalDepartmentName 0x0008 + (0x1040 << 16) #define kManufacturersModelName 0x0008 + (0x1090 << 16) @@ -5695,6 +5697,9 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kStationName: dcmStr(lLength, &buffer[lPos], d.stationName); break; + case kStudyDescription: + dcmStr(lLength, &buffer[lPos], d.studyDescription); + break; case kSeriesDescription: dcmStr(lLength, &buffer[lPos], d.seriesDescription); #ifdef USING_DCM2NIIXFSWRAPPER diff --git a/console/nii_dicom.h b/console/nii_dicom.h index ccc92240..74cde92c 100644 --- a/console/nii_dicom.h +++ b/console/nii_dicom.h @@ -248,7 +248,7 @@ static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8; float pixelPaddingValue; // used for both FloatPixelPaddingValue (0028, 0122) and PixelPaddingValue (0028, 0120); NaN if not present. double imagingFrequency, acquisitionDuration, triggerDelayTime, RWVScale, RWVIntercept, dateTime, acquisitionTime, acquisitionDate, bandwidthPerPixelPhaseEncode; char parallelAcquisitionTechnique[kDICOMStr], radiopharmaceutical[kDICOMStr], convolutionKernel[kDICOMStr], unitsPT[kDICOMStr], tracerRadionuclide[kDICOMStr], decayCorrection[kDICOMStr], attenuationCorrectionMethod[kDICOMStr],reconstructionMethod[kDICOMStr], transferSyntax[kDICOMStr]; - char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr]; + char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], studyDescription[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr]; char deepLearningText[kDICOMStrLarge], scanOptions[kDICOMStrLarge], institutionAddress[kDICOMStrLarge], imageComments[kDICOMStrLarge]; uint32_t dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS]; struct TCSAdata CSA; diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 5f080c66..a07fe3df 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1395,6 +1395,7 @@ tse3d: T2*/ fprintf(fp, "\t\"MRAcquisitionType\": \"2D\",\n"); if (d.is3DAcq) fprintf(fp, "\t\"MRAcquisitionType\": \"3D\",\n"); + json_Str(fp, "\t\"StudyDescription\": \"%s\",\n", d.studyDescription); json_Str(fp, "\t\"SeriesDescription\": \"%s\",\n", d.seriesDescription); json_Str(fp, "\t\"ProtocolName\": \"%s\",\n", d.protocolName); json_Str(fp, "\t\"ScanningSequence\": \"%s\",\n", d.scanningSequence); @@ -3416,6 +3417,66 @@ void cleanISO8859(char *cString) { } } +void heudiconvStrPth(char *cString) { + int len = strlen(cString); + int j = 0; + int i = 0; + bool hasCaret = false; + while (i < len) { + if (cString[i] == '^') { + cString[j++] = kPathSeparator; + hasCaret = true; + } else if ((!hasCaret) && (cString[i] == '_') ) { + cString[j++] = kPathSeparator; + } else if (cString[i] != '-') { + cString[j] = cString[i]; + j ++; + } + i++; + } + cString[j] = '\0'; +} + +void heudiconvStr(char *cString) { + int len = strlen(cString); + int j = 0; + int i = 0; + const char kTempPathSeparator = '\a'; + bool hasCaret = false; + while (i < len) { + if (cString[i] != '-') { + cString[j] = cString[i]; + j ++; + } + i++; + } + cString[j] = '\0'; +} + +void mkDirs(char *pth) { + if (strlen(pth) < 1) return; + char newdir[2048] = {""}; + for (size_t pos = 0; pos < strlen(pth); pos++) { + if (pth[pos] == kPathSeparator) { + if (!is_dir(newdir, true)) +#if defined(_WIN64) || defined(_WIN32) + mkdir(newdir); +#else + mkdir(newdir, 0700); +#endif + } + char ch[128] = {""}; + snprintf(ch, 128, "%c", pth[pos]); + strcat(newdir, ch); + } + if (!is_dir(newdir, true)) +#if defined(_WIN64) || defined(_WIN32) + mkdir(newdir); +#else + mkdir(newdir, 0700); +#endif +} //mkDirs() + void createDummyBidsBoilerplate(char *pth, bool isFunc) { //https://remi-gau.github.io/bids_cookbook/#starters char pathSep[2] = {"a"}; @@ -3539,13 +3600,30 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts strcat(outname, opts.indirParent); if (f == 'G') strcat(outname, dcm.accessionNumber); - if (f == 'H') { - printWarning("hazardous (%%h) bids naming experimental\n"); + if ((f == 'H') || (f == 'V')) { + printWarning("hazardous (%%h) or volatile (%%v) bids naming experimental\n"); + bool isReproin = (f == 'V'); + if (isReproin) { + //https://dbic-handbook.readthedocs.io/en/latest/mri/reproin.html + //reproin convention is hard for one-pass, as `ses` may only be reported in one series in the session (e.g. localizer) + //printf("study %s\n", dcm.studyDescription); + //printf("series %s\n", dcm.seriesDescription); + //printf("id %s\n", dcm.patientID); + snprintf(newstr, PATH_MAX, "%s", dcm.studyDescription); + heudiconvStrPth(newstr); + if ((strlen(pth) > 0) && (pth[strlen(pth)-1] != kPathSeparator)) + strcat(pth, kFileSep);//kPathSeparator); + strcat(pth, newstr); + mkDirs(pth); + strcpy(opts.bidsSubject, dcm.patientID); + heudiconvStr(opts.bidsSubject); + } char bidsSubject[kOptsStr] = "sub-"; if (strlen(opts.bidsSubject) <= 0) strcat(bidsSubject, "1"); else strcat(bidsSubject, opts.bidsSubject); + printf("%s<<<:::\n", bidsSubject); char bidsSession[kOptsStr] = "ses-"; if (strlen(opts.bidsSession) <= 0) strcat(bidsSession, "1"); @@ -3626,13 +3704,13 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts if (strlen(dcm.protocolName) < 1) printWarning("Unable to append protocol name (0018,1030) to filename (it is empty).\n"); } + if (f == 'Q') + strcat(outname, dcm.scanningSequence); if (f == 'R') { snprintf(newstr, PATH_MAX, "%d", dcm.imageNum); strcat(outname, newstr); isImageNumReported = true; } - if (f == 'Q') - strcat(outname, dcm.scanningSequence); if (f == 'S') { snprintf(newstr, PATH_MAX, "%ld", dcm.seriesNum); strcat(outname, newstr); From 550b9e23a64bac409ed3ad2ede4a02b029bb7421 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Mon, 25 Mar 2024 12:55:36 -0400 Subject: [PATCH 15/82] Unlatch 0400,0561 based on nesting depth (https://github.com/rordenlab/dcm2niix/issues/807) --- console/nii_dicom.cpp | 19 +++++++++++++------ console/nii_dicom.h | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index c2a5f2c2..c236576c 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4675,7 +4675,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD bool isHasBMatrix = false; bool isHasBVec = false; bool is2005140FSQ = false; - bool is4000561SQ = false; //Original Attributes SQ + int sqDepth04000561 = -1; bool is00089092SQ = false; //Referenced Image Evidence SQ bool overlayOK = true; int userData11GE = 0; @@ -4863,6 +4863,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } //transfer syntax requests switching VR after group 0001 //uint32_t group = (groupElement & 0xFFFF); lPos += 4; + uint32_t realGroupElement = groupElement; //issue409 - icons can have their own sub-sections... keep reading until we get to the icon image? //if ((groupElement == kItemDelimitationTag) || (groupElement == kSequenceDelimitationItemTag)) isIconImageSequence = false; //if (groupElement == kItemTag) sqDepth++; @@ -4885,8 +4886,10 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } } if (unNest) { + if ((groupElement != kItemTag) && (sqDepth <= sqDepth04000561)) { + sqDepth04000561 = -1; //unlatch + } is2005140FSQ = false; - is4000561SQ = false; is00089092SQ = false; if (sqDepth < 0) sqDepth = 0; //should not happen, but protect for faulty anonymization @@ -5144,7 +5147,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD encapsulatedDataFragmentStart = (int)lPos + (int)lFileOffset; } } - if ((is4000561SQ) || (is00089092SQ)) + if ((sqDepth04000561 >= 0) || (is00089092SQ)) groupElement = kUnused; //ignore Original Attributes if ((isIconImageSequence) && ((groupElement & 0x0028) == 0x0028)) groupElement = kUnused; //ignore icon dimensions @@ -5311,6 +5314,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; } case kTransferSyntax: { + //if (sqDepth04000561 >= 0) break; char transferSyntax[kDICOMStr]; strcpy(transferSyntax, ""); dcmStr(lLength, &buffer[lPos], transferSyntax); @@ -7084,7 +7088,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kOriginalAttributesSq: if (lLength > 8) break; //issue639: we will skip entire icon if there is an explicit length - is4000561SQ = true; + sqDepth04000561 = sqDepth; break; case kWaveformSq: d.imageStart = 1; //abort!!! @@ -7496,9 +7500,12 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD // this section will report very little for implicit data //if (d.isHasReal) printf("r");else printf("m"); char str[kDICOMStr]; - snprintf(str, kDICOMStr, "%*c%04x,%04x %u@%zu ", sqDepth + 1, ' ', groupElement & 65535, groupElement >> 16, lLength, lFileOffset + lPos); + char realCh = ' '; + if (realGroupElement != groupElement) + realCh = '~'; //indicate we are ignoring this tag (e.g. wrapped in 0400,0561) + snprintf(str, kDICOMStr, "%*c%04x,%04x%c %u@%zu ", sqDepth + 1, ' ', realGroupElement & 65535, realGroupElement >> 16, realCh, lLength, lFileOffset + lPos); bool isStr = false; - if (d.isExplicitVR) { + if ((d.isExplicitVR) && (realGroupElement != kItemTag)) { //snprintf(str, kDICOMStr, "%s%c%c ", str, vr[0], vr[1]); //if (snprintf(str2, kDICOMStr-1, "%s%c%c", str, vr[0], vr[1]) < 0) exit(EXIT_FAILURE); strncat(str, &vr[0], 1); diff --git a/console/nii_dicom.h b/console/nii_dicom.h index 74cde92c..830c7576 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.20240308" +#define kDCMdate "v1.0.20240325" #define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic From b256504aefc30a415a2d256af2bb7c5f5ca154ea Mon Sep 17 00:00:00 2001 From: Jaemin Shin Date: Wed, 27 Mar 2024 23:11:19 -0500 Subject: [PATCH 16/82] GE AcquisitionDuration (#808) --- console/nii_dicom.cpp | 7 +++++++ console/nii_dicom.h | 2 +- console/nii_dicom_batch.cpp | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index c236576c..5aa8db7e 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4477,6 +4477,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kLastScanLoc 0x0019 + (0x101B << 16) #define kBandwidthPerPixelPhaseEncode 0x0019 + (0x1028 << 16) //FD #define kSliceTimeSiemens 0x0019 + (0x1029 << 16) ///FD +#define kAcquisitionDurationGE 0x0019 + (0x105a << 16) //FL Acquisition Duration in microsecond, Duration of Scan (series) #define kPulseSequenceNameGE 0x0019 + (0x109C << 16) //LO 'epiRT' or 'epi' #define kInternalPulseSequenceNameGE 0x0019 + (0x109E << 16) //LO 'EPI' or 'EPI2' #define kRawDataRunNumberGE 0x0019 + (0x10A2 << 16)//SL @@ -5838,6 +5839,12 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD printf("%s\t FrameAcquisitionDateTime %0.4f \n", dateTime, dTime); //d.triggerDelayTime = dTime; }*/ + case kAcquisitionDurationGE: // issue 808 + if (d.manufacturer != kMANUFACTURER_GE) + break; + d.acquisitionDuration = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); + d.acquisitionDuration /= 1000000.0; //convert microsec to sec + break; case kDiffusionDirectionality: { // 0018, 9075 set_directionality0018_9075(&volDiffusion, (&buffer[lPos])); if ((d.manufacturer != kMANUFACTURER_PHILIPS) || (lLength < 10)) diff --git a/console/nii_dicom.h b/console/nii_dicom.h index 830c7576..6d3546df 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.20240325" +#define kDCMdate "v1.0.20240327" #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 a07fe3df..7eb6d206 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -2256,6 +2256,11 @@ tse3d: T2*/ // FSL definition is start of first line until start of last line. // Other than the use of (n-1), the value is basically just 1.0/bandwidthPerPixelPhaseEncode. // https://github.com/rordenlab/dcm2niix/issues/130 + if (d.manufacturer == kMANUFACTURER_GE){ //issue808 + char acquisitionDurationbuffer[50]; + sprintf(acquisitionDurationbuffer, "%02d:%02d",(int)round(d.acquisitionDuration)/60,(int)round(d.acquisitionDuration)%60); + json_Str(fp, "\t\"AcquisitionDurationGE\": \"%s\",\n", acquisitionDurationbuffer); + } if (d.manufacturer != kMANUFACTURER_UIH) //issue606 json_Float(fp, "\t\"AcquisitionDuration\": %g,\n", d.acquisitionDuration); if ((d.manufacturer == kMANUFACTURER_UIH) && (effectiveEchoSpacing <= 0.0)) //issue225, issue531 From 608f636d097d8c4a2fb059e6ba885a4743088556 Mon Sep 17 00:00:00 2001 From: Jaemin Shin Date: Fri, 29 Mar 2024 08:51:12 -0500 Subject: [PATCH 17/82] GE AcquisitionDuration minor (#808) --- console/nii_dicom.cpp | 2 ++ console/nii_dicom_batch.cpp | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 5aa8db7e..f42f5fd1 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -5826,6 +5826,8 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.accelFactPE = accelFactPE; break; case kAcquisitionDuration: + if (!isSameFloatGE(d.acquisitionDuration, 0.0)) + break; //issue 808: give precedence to more precise measures, e.g kAcquisitionDurationGE (0019,105a) //n.b. used differently by different vendors https://github.com/rordenlab/dcm2niix/issues/225 d.acquisitionDuration = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); break; diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 7eb6d206..a07fe3df 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -2256,11 +2256,6 @@ tse3d: T2*/ // FSL definition is start of first line until start of last line. // Other than the use of (n-1), the value is basically just 1.0/bandwidthPerPixelPhaseEncode. // https://github.com/rordenlab/dcm2niix/issues/130 - if (d.manufacturer == kMANUFACTURER_GE){ //issue808 - char acquisitionDurationbuffer[50]; - sprintf(acquisitionDurationbuffer, "%02d:%02d",(int)round(d.acquisitionDuration)/60,(int)round(d.acquisitionDuration)%60); - json_Str(fp, "\t\"AcquisitionDurationGE\": \"%s\",\n", acquisitionDurationbuffer); - } if (d.manufacturer != kMANUFACTURER_UIH) //issue606 json_Float(fp, "\t\"AcquisitionDuration\": %g,\n", d.acquisitionDuration); if ((d.manufacturer == kMANUFACTURER_UIH) && (effectiveEchoSpacing <= 0.0)) //issue225, issue531 From eba090b9de107db14dddbe2aee22c7b65dff710d Mon Sep 17 00:00:00 2001 From: "Christopher G. Schwarz" Date: Wed, 17 Apr 2024 10:08:52 -0500 Subject: [PATCH 18/82] Add DeidentificationMethod and DeidentificationMethodCodeSequence to json --- console/dcm2niix_fswrapper.cpp | 4 ++++ console/nii_dicom.cpp | 42 ++++++++++++++++++++++++++++++---- console/nii_dicom.h | 13 ++++++++++- console/nii_dicom_batch.cpp | 18 +++++++++++++++ 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/console/dcm2niix_fswrapper.cpp b/console/dcm2niix_fswrapper.cpp index 221eae39..8c5008b1 100644 --- a/console/dcm2niix_fswrapper.cpp +++ b/console/dcm2niix_fswrapper.cpp @@ -476,4 +476,8 @@ void dcm2niix_fswrapper::seriesInfoDump(FILE *fpdump, const MRIFSSTRUCT *pmrifsS // dcm2niix doesn't seem to retrieve this 0x51, 0x1016 //fprintf(fpdump, "SiemensCrit %s\n",e->d.string); + + // kDeidentificationMethod 0x0012 + (0x0063 << 16) // '0012' '0063' 'LO' 'DeidentificationMethod' + fprintf(fpdump, "DeidentificationMethod %s\n", tdicomData->deidentificationMethod); + } diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index f42f5fd1..21caa33b 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4387,6 +4387,11 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D #define kPatientWeight 0x0010 + (0x1030 << 16) #define kAnatomicalOrientationType 0x0010 + (0x2210 << 16) #define kDeidentificationMethod 0x0012 + (0x0063 << 16) //[DICOMANON, issue 383 +#define kDeidentificationMethodCodeSequence 0x0012 + (0x0064 << 16) +#define kCodeValue 0x0008 + (0x0100 << 16) +#define kCodingSchemeDesignator 0x0008 + (0x0102 << 16) +#define kCodingSchemeVersion 0x0008 + (0x0103 << 16) +#define kCodeMeaning 0x0008 + (0x0104 << 16) #define kBodyPartExamined 0x0018 + (0x0015 << 16) #define kBodyPartExamined 0x0018 + (0x0015 << 16) #define kScanningSequence 0x0018 + (0x0020 << 16) @@ -4774,6 +4779,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD bool isPaletteColor = false; bool isInterpolated = false; bool isIconImageSequence = false; + bool isDeidentificationMethodCodeSequence = false; int sqDepthIcon = -1; bool isSwitchToImplicitVR = false; bool isSwitchToBigEndian = false; @@ -5280,6 +5286,8 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD //return d; } if (lLength > 0) //issue695: skip empty tags, "gdcmanon --dumb --empty 0018,0089 good.dcm bad.dcm" + if(sqDepth < 1 ) + isDeidentificationMethodCodeSequence = false; switch (groupElement) { case kMediaStorageSOPClassUID: { char mediaUID[kDICOMStr]; @@ -5640,15 +5648,41 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; } case kDeidentificationMethod: { //issue 383 - char anonTxt[kDICOMStr]; - dcmStr(lLength, &buffer[lPos], anonTxt); - int slen = (int)strlen(anonTxt); - if ((slen < 10) || (strstr(anonTxt, "DICOMANON") == NULL)) + dcmStr(lLength, &buffer[lPos], d.deidentificationMethod); + int slen = (int)strlen(d.deidentificationMethod); + if ((slen < 10) || (strstr(d.deidentificationMethod, "DICOMANON") == NULL)) break; isDICOMANON = true; printWarning("Matlab DICOMANON can scramble SeriesInstanceUID (0020,000e) and remove crucial data (see issue 383). \n"); break; } + case kDeidentificationMethodCodeSequence: { + isDeidentificationMethodCodeSequence = true; + break; + } + case kCodeValue: { + if(isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) + dcmStr(lLength, &buffer[lPos], d.deID_CS[d.deID_CS_n].CodeValue); + break; + } + case kCodingSchemeDesignator: { + if(isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) + dcmStr(lLength, &buffer[lPos], d.deID_CS[d.deID_CS_n].CodingSchemeDesignator); + break; + } + case kCodingSchemeVersion: { + if(isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) + dcmStr(lLength, &buffer[lPos], d.deID_CS[d.deID_CS_n].CodingSchemeVersion); + break; + } + case kCodeMeaning: { + if(isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) + { + dcmStr(lLength, &buffer[lPos], d.deID_CS[d.deID_CS_n].CodeMeaning); + d.deID_CS_n++; + } + break; + } case kPatientID: if (strlen(d.patientID) > 1) break; diff --git a/console/nii_dicom.h b/console/nii_dicom.h index 6d3546df..aee288d9 100644 --- a/console/nii_dicom.h +++ b/console/nii_dicom.h @@ -184,6 +184,9 @@ static const int kMaxOverlay = 16; //even group values 0x6000..0x601E // Maximum number of dimensions for .dimensionIndexValues, i.e. possibly the // number of axes in the output .nii. static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8; +// Maximum supported number of entries in DeidentificationMethodCodeSequence +// Any additional will be ignored +static const uint8_t MAX_DEID_CS = 10; struct TDTI { float V[4]; //int totalSlicesIn4DOrder; @@ -235,9 +238,16 @@ static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8; char bidsEntitySuffix[kDICOMStrLarge]; //anat, func, dwi char bidsTask[kDICOMStr]; //rest, naming40 }; + struct TDeIDCodeSequence { + char CodeValue[kDICOMStr]; + char CodeMeaning[kDICOMStrLarge]; + char CodingSchemeDesignator[kDICOMStr]; + char CodingSchemeVersion[kDICOMStr]; + }; struct TDICOMdata { long seriesNum; int xyzDim[5]; + int deID_CS_n = 0; uint32_t coilCrc, seriesUidCrc, instanceUidCrc; int overlayStart[kMaxOverlay]; int postLabelDelay, shimGradientX, shimGradientY, shimGradientZ, phaseNumber, spoiling, mtState, partialFourierDirection, interp3D, aslFlags, durationLabelPulseGE, epiVersionGE, internalepiVersionGE, maxEchoNumGE, rawDataRunNumber, numberOfTR, numberOfImagesInGridUIH, numberOfDiffusionT2GE, numberOfDiffusionDirectionGE, tensorFileGE, diffCyclingModeGE, phaseEncodingGE, protocolBlockStartGE, protocolBlockLengthGE, modality, dwellTime, effectiveEchoSpacingGE, phaseEncodingLines, phaseEncodingSteps, frequencyEncodingSteps, phaseEncodingStepsOutOfPlane, echoTrainLength, echoNum, sliceOrient, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,locationsInAcquisition, locationsInAcquisitionConflict, compressionScheme; @@ -248,12 +258,13 @@ static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8; float pixelPaddingValue; // used for both FloatPixelPaddingValue (0028, 0122) and PixelPaddingValue (0028, 0120); NaN if not present. double imagingFrequency, acquisitionDuration, triggerDelayTime, RWVScale, RWVIntercept, dateTime, acquisitionTime, acquisitionDate, bandwidthPerPixelPhaseEncode; char parallelAcquisitionTechnique[kDICOMStr], radiopharmaceutical[kDICOMStr], convolutionKernel[kDICOMStr], unitsPT[kDICOMStr], tracerRadionuclide[kDICOMStr], decayCorrection[kDICOMStr], attenuationCorrectionMethod[kDICOMStr],reconstructionMethod[kDICOMStr], transferSyntax[kDICOMStr]; - char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], studyDescription[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr]; + char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], studyDescription[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr], deidentificationMethod[kDICOMStr]; char deepLearningText[kDICOMStrLarge], scanOptions[kDICOMStrLarge], institutionAddress[kDICOMStrLarge], imageComments[kDICOMStrLarge]; uint32_t dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS]; struct TCSAdata CSA; bool isDeepLearning, 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 TDeIDCodeSequence deID_CS[MAX_DEID_CS]; }; struct TDCMprefs { int isVerbose, compressFlag, isIgnoreTriggerTimes; diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index a07fe3df..153da34f 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1447,6 +1447,24 @@ tse3d: T2*/ fprintf(fp, "\t\"NonlinearGradientCorrection\": false,\n"); if (d.isDerived) //DICOM is derived image or non-spatial file (sounds, etc) fprintf(fp, "\t\"RawImage\": false,\n"); + json_Str(fp, "\t\"DeidentificationMethod\": \"%s\",\n", d.deidentificationMethod); +if(d.deID_CS_n>0) +{ + fprintf(fp, "\t\"DeidentificationMethodCodeSequence\": [ \n"); + for (int i = 0; i < d.deID_CS_n && i < MAX_DEID_CS; i++) + { + fprintf(fp, "\t { \n"); + json_Str(fp, "\t\t\"CodeValue\": \"%s\",\n", d.deID_CS[i].CodeValue); + json_Str(fp, "\t\t\"CodingSchemeDesignator\": \"%s\",\n", d.deID_CS[i].CodingSchemeDesignator); + json_Str(fp, "\t\t\"CodingSchemeVersion\": \"%s\",\n", d.deID_CS[i].CodingSchemeVersion); + json_Str(fp, "\t\t\"CodeMeaning\": \"%s\"\n", d.deID_CS[i].CodeMeaning); + if(i+1 < d.deID_CS_n) + fprintf(fp, "\t },\n"); + else + fprintf(fp, "\t }\n"); + } + fprintf(fp, "\t],\n"); +} if (d.seriesNum > 0) fprintf(fp, "\t\"SeriesNumber\": %ld,\n", d.seriesNum); //Chris Gorgolewski: BIDS standard specifies ISO8601 date-time format (Example: 2016-07-06T12:49:15.679688) From 70abd5e2fab1ab9765592162da2d0ac5fc0f16aa Mon Sep 17 00:00:00 2001 From: Robert Reid Date: Thu, 18 Apr 2024 15:57:28 -0500 Subject: [PATCH 19/82] Disable XA partial vol detection for single slice files (rordenlab#742) --- console/nii_dicom.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index f42f5fd1..522e75f8 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -8041,7 +8041,19 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD //} if (numberOfFramesICEdims < 2) //issue751: icedims[20] frames for EPI only numberOfFramesICEdims = 0; - if ((numberOfFramesICEdims > 0) && (d.xyzDim[3] != numberOfFramesICEdims)) { + // Issue 742: Detect *currently* enhanced Siemens XA volumes with fewer + // than the expected number of slices, and mark them as derived, with + // SeriesNumber + 1000. However, valid XA series are often unenhanced, + // likely post-scanner, and can arrive as files with just 1 slice each + // in d.xyzDim[3] but the total number of z locations for the series in + // their ICEdims, so they appear to be partial volumes until the + // dcmList is completed (nii_dicom_batch.cpp). Thus we use the + // heuristic that d.xyzDim[3] == 1 is probably OK but between 1 and + // numberOfFramesICEdims (exclusively) is an enhanced partial + // volume. It would miss the case of a true enhanced partial volume + // with just 1 slice, but that seems much less likely than unenhanced + // DICOM with unmodified ICEDims tags. + if ((numberOfFramesICEdims > 0) && (d.xyzDim[3] > 1) && (d.xyzDim[3] != numberOfFramesICEdims)) { printWarning("Series %ld includes partial volume (issue 742): %d slices acquired but ICE dims (0021,118e) specifies %d \n", d.seriesNum, d.xyzDim[3], numberOfFramesICEdims); d.seriesNum += 1000; d.isDerived = true; From d53a237d5078bd35150a33ccaa3f25ad6cf34775 Mon Sep 17 00:00:00 2001 From: Robert Reid Date: Sat, 20 Apr 2024 15:13:26 -0500 Subject: [PATCH 20/82] Fix CSA n_tags check (#633) --- console/nii_dicom.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 522e75f8..45dcc5c7 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -1446,6 +1446,10 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i return EXIT_FAILURE; int lPos = 8; //skip 8 bytes of data, 'SV10' plus 2 32-bit values unused1 and unused2 int lnTag = buff[lPos] + (buff[lPos + 1] << 8) + (buff[lPos + 2] << 16) + (buff[lPos + 3] << 24); + if ((lnTag > 128) || (lnTag < 1)){ + printError("%d n_tags CSA Image Header corrupted (0029,1010) see issue 633.\n", lnTag); + return EXIT_FAILURE; + } if (buff[lPos + 4] != 77) return EXIT_FAILURE; lPos += 8; //skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0 @@ -1459,10 +1463,6 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i // Storage order is always little-endian, so byte-swap required values if necessary if (!littleEndianPlatform()) nifti_swap_4bytes(1, &tagCSA.nitems); - if (tagCSA.nitems > 128) { - printError("%d n_tags CSA Image Header corrupted (0029,1010) see issue 633.\n", tagCSA.nitems); - return EXIT_FAILURE; - } if (isVerbose > 1) //extreme verbosity: show every CSA tag printMessage(" %d CSA of %s %d\n", lPos, tagCSA.name, tagCSA.nitems); if (tagCSA.nitems > 0) { From ed3f8a54d49fa54a32ac89d286cff408793ebf1d Mon Sep 17 00:00:00 2001 From: "Christopher G. Schwarz" Date: Thu, 25 Apr 2024 16:46:01 -0500 Subject: [PATCH 21/82] More robust detection of whether tags are inside DeidentificationMethodCodeSequence --- console/nii_dicom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 21caa33b..2868a942 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -5286,7 +5286,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD //return d; } if (lLength > 0) //issue695: skip empty tags, "gdcmanon --dumb --empty 0018,0089 good.dcm bad.dcm" - if(sqDepth < 1 ) + if(sqDepth < 1 && isDeidentificationMethodCodeSequence && groupElement != kItemDelimitationTag && groupElement != kItemTag ) isDeidentificationMethodCodeSequence = false; switch (groupElement) { case kMediaStorageSOPClassUID: { From 772d8ad8b9c704a11a4cdce149e22d1a1fb04b56 Mon Sep 17 00:00:00 2001 From: Chris Rorden Date: Sun, 19 Mar 2023 08:09:59 -0400 Subject: [PATCH 22/82] Update issue templates Use `make` to compile development branch --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a69ea02e..dd0a5a47 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -42,6 +42,6 @@ Please try the following steps to resolve your issue: ``` git clone --branch development https://github.com/rordenlab/dcm2niix.git cd dcm2niix/console -g++ -I. main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -o dcm2niix -DmyDisableOpenJPEG +make ./dcm2niix .... ``` From 70007b68471af2e00b19967085200bbcf84715fa Mon Sep 17 00:00:00 2001 From: Tom Hampshire Date: Mon, 22 Apr 2024 07:57:31 +0100 Subject: [PATCH 23/82] Console script return status code (https://github.com/rordenlab/dcm2niix/issues/811) --- dcm2niix/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dcm2niix/__init__.py b/dcm2niix/__init__.py index 42b26c2d..91b1cb1c 100644 --- a/dcm2niix/__init__.py +++ b/dcm2niix/__init__.py @@ -24,10 +24,10 @@ def main(args=None, **run_kwargs): Arguments: args: defaults to `sys.argv[1:]`. **run_kwargs: passed to `subprocess.run`. - Returns: `subprocess.CompletedProcess` instance. + Returns: `int` exit code from dcm2niix execution. """ if args is None: import sys args = sys.argv[1:] from subprocess import run - return run([bin] + args, **run_kwargs) + return run([bin] + args, **run_kwargs).returncode From 08aeeef9ddb814115eaebdbcf813cc9b33485bed Mon Sep 17 00:00:00 2001 From: bpinsard Date: Thu, 30 May 2024 20:22:55 -0400 Subject: [PATCH 24/82] add MAGNITUDE to ImageType for GE data, necessary for heudiconv logic --- console/nii_dicom_batch.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index a07fe3df..7e6fa9e0 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1415,6 +1415,8 @@ tse3d: T2*/ } else isSep = true; } + if ((d.isHasMagnitude) && ((d.manufacturer == kMANUFACTURER_GE) || (strstr(d.imageType, "_MAGNITUDE_") == NULL))) + fprintf(fp, "\", \"MAGNITUDE"); if ((d.isHasPhase) && ((d.manufacturer == kMANUFACTURER_GE) || (strstr(d.imageType, "_PHASE_") == NULL))) fprintf(fp, "\", \"PHASE"); //"_IMAGINARY_" if ((d.isHasReal) && ((d.manufacturer == kMANUFACTURER_GE) || (strstr(d.imageType, "_REAL_") == NULL))) From a7ed66c8ac287675617a15800a403c9b4f9f28a8 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 3 Jul 2024 20:21:30 +0100 Subject: [PATCH 25/82] CI: PyPI release - fixes #829 - follow-up to #605 --- .github/workflows/release.yml | 13 +++++++++++++ .gitignore | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..46e02975 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,13 @@ +name: Release +on: {push: {tags: ['v*.*.*']}} +jobs: + pypi: + permissions: {id-token: write} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: {fetch-depth: 0, submodules: recursive} + - uses: actions/setup-python@v5 + with: {python-version: '3.x'} + - uses: casperdcl/deploy-pypi@v2 + with: {build: -s, upload: true} diff --git a/.gitignore b/.gitignore index 0b86f87c..98ba3f41 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,6 @@ __pycache__/ /_cmake_test_compile/ /dcm2niix/_dist_ver.py /dcm2niix/dcm2niix -MANIFEST +/MANIFEST* /*.egg*/ /dist/ From cd7e4810363798c680ccd830f66c7a2999f14cb3 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Wed, 24 Jul 2024 16:34:57 -0700 Subject: [PATCH 26/82] GE slice timing (https://github.com/rordenlab/dcm2niix/issues/838) --- console/makefile | 7 ++++++- console/nii_dicom_batch.cpp | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/console/makefile b/console/makefile index 33131075..077b9d91 100644 --- a/console/makefile +++ b/console/makefile @@ -1,6 +1,8 @@ # Regular use CFLAGS=-s -O3 +# Universal files used for everything +UFILES=main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -o dcm2niix -DmyDisableOpenJPEG # Debugging #CFLAGS=-g @@ -44,4 +46,7 @@ ifneq ($(OS),Windows_NT) endif endif all: - g++ $(CFLAGS) -I. $(JSFLAGS) $(JFLAGS) $(LFLAGS) main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -o dcm2niix -DmyDisableOpenJPEG + g++ $(CFLAGS) -I. $(JSFLAGS) $(JFLAGS) $(LFLAGS) $(UFILES) + +sanitize: + g++ -O1 -g -fsanitize=address -fno-omit-frame-pointer $(LFLAGS) $(UFILES) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 7e6fa9e0..fc9b9b41 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -7255,6 +7255,11 @@ void sliceTimingGE(struct TDICOMdata *d, const char *filename, struct TDCMopts o printWarning("Unable to determine GE Slice timing, no Protocol Data Block GE (0025,101B): %s\n", filename); return; } + if (strlen(d->softwareVersions) < 10) { //issue838 + d->CSA.sliceTiming[0] = -1; + printWarning("Unable to determine GE Slice timing, invalid SoftwareVersions (0018,1020): %s\n", filename); + return; + } //start version check: float geMajorVersion = 0; int geMajorVersionInt = 0, geMinorVersionInt = 0, geReleaseVersionInt = 0; From 0001daa00bd449409d7f642b8becefec9954dd2a Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Wed, 24 Jul 2024 16:46:45 -0700 Subject: [PATCH 27/82] UIH DWI (https://github.com/rordenlab/dcm2niix/issues/836) --- console/nii_dicom.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 45dcc5c7..149450ee 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4690,6 +4690,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD //bool isProspectiveSynced = false; bool isDICOMANON = false; //issue383 bool isMATLAB = false; //issue383 + bool isDWI_UIH = false; //issue836 //bool isASL = false; bool has00200013 = false; //double contentTime = 0.0; @@ -6423,6 +6424,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer != kMANUFACTURER_UIH) break; float v[4]; + isDWI_UIH = true; //issue836 dcmMultiFloatDouble(lLength, &buffer[lPos], 3, v, d.isLittleEndian); //dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, v); //printf(">>>%g %g %g\n", v[0], v[1], v[2]); @@ -6908,7 +6910,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD // ((d.manufacturer == kMANUFACTURER_PHILIPS) && !is2005140FSQ)) && // (isAtFirstPatientPosition || isnan(d.patientPosition[1]))) //if((d.manufacturer == kMANUFACTURER_SIEMENS) || ((d.manufacturer == kMANUFACTURER_PHILIPS) && !is2005140FSQ)) - if (true) { + if (!isDWI_UIH) { //issue836 //if ((d.manufacturer == kMANUFACTURER_MEDISO) || (d.manufacturer == kMANUFACTURER_TOSHIBA) || (d.manufacturer == kMANUFACTURER_CANON) || (d.manufacturer == kMANUFACTURER_HITACHI) || (d.manufacturer == kMANUFACTURER_SIEMENS) || (d.manufacturer == kMANUFACTURER_PHILIPS)) { //for kMANUFACTURER_HITACHI see https://nciphub.org/groups/qindicom/wiki/StandardcompliantenhancedmultiframeDWI float v[4]; From 25ce4d0b4a07c01664973e3ac11a12069a853a3f Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Wed, 24 Jul 2024 17:16:27 -0700 Subject: [PATCH 28/82] myNoRois option to ignore ROIs (https://github.com/rordenlab/dcm2niix/issues/832) --- FILENAMING.md | 2 +- GE/README.md | 1 - console/main_console.cpp | 2 +- console/makefile | 3 +++ console/nii_dicom_batch.cpp | 4 ++++ 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/FILENAMING.md b/FILENAMING.md index 436114a1..5f46d0c5 100644 --- a/FILENAMING.md +++ b/FILENAMING.md @@ -63,7 +63,7 @@ DICOM images can have up to [16](https://www.medicalconnections.co.uk/kb/Number- ## File Name Conflicts -dcm2niix will attempt to write your image using the naming scheme you specify with the '-f' parameter. However, if an image already exists with the specified output name, dcm2niix will append a letter (e.g. 'a') to the end of a file name to avoid overwriting existing images. Consider a situation where dcm2niix is run with '-f %t'. This will name images based on the study time. If a single study has multiple series (for example, a T1 sequence and a fMRI scan, the reulting file names will conflict with each other. In order to avoid overwriting images, dcm2niix will resort to adding the post fix 'a', 'b', etc. There are a few solutions to avoiding these situations. You may want to consider using both of these: +dcm2niix will attempt to write your image using the naming scheme you specify with the '-f' parameter. However, if an image already exists with the specified output name, dcm2niix will append a letter (e.g. 'a') to the end of a file name to avoid overwriting existing images. Consider a situation where dcm2niix is run with '-f %t'. This will name images based on the study time. If a single study has multiple series (for example, a T1 sequence and a fMRI scan, the resulting file names will conflict with each other. In order to avoid overwriting images, dcm2niix will resort to adding the post fix 'a', 'b', etc. There are a few solutions to avoiding these situations. You may want to consider using both of these: - Make sure you specify a naming scheme that can discriminate between your images. For example '-f %t' will not disambiguate different series acquired in the same session. However, '-f %t_%s' will discriminate between series. - Localizer (scout) images are the first scans acquired for any scanning session, and are used to plan the location for subsequent images. Localizers are not used in subsequent analyses (due to resolution, artefacts, etc). Localizers are often acquired with three orthogonal image planes (sagittal, coronal and axial). The NIfTI format requires that all slices in a volume are co-planar, so these localizers will generate naming conflicts. The solution is to use '-i y' which will ignore (not convert) localizers (it will also ignore derived images and 2D slices). This command helps exclude images that are not required for subsequent analyses. - Be aware that if you run dcm2niix twice with the same parameters on the same data, you will encounter file naming conflicts. diff --git a/GE/README.md b/GE/README.md index 5bfbfceb..39e740dd 100644 --- a/GE/README.md +++ b/GE/README.md @@ -118,7 +118,6 @@ Anatomical localizers (e.g. scout images) are quick-and-dirty scans used to posi ## Sample Datasets - [A validation dataset for dcm2niix commits](https://github.com/neurolabusc/dcm_qa_nih). - - [Slice Timing and Phase Encoding examples](https://github.com/jannikadon/cc-dcm2bids-wrapper/tree/main/dicom-qa-examples) - [Slice timing validation](https://github.com/neurolabusc/dcm_qa_stc) for different varieties of GE EPI sequences. - [Examples of phase encoding polarity, slice timing and diffusion gradients](https://github.com/neurolabusc/dcm_qa_ge). - The dcm2niix [wiki](https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage) includes examples of diffusion data, slice timing, and other variations. diff --git a/console/main_console.cpp b/console/main_console.cpp index 8865955b..e87c1481 100644 --- a/console/main_console.cpp +++ b/console/main_console.cpp @@ -117,7 +117,7 @@ void showHelp(const char *argv[], struct TDCMopts opts) { //#define kNAME_CONFLICT_OVERWRITE 1 //1 = overwrite existing file with same name //#define kNAME_CONFLICT_ADD_SUFFIX 2 //default 2 = write with new suffix as a new file printf(" -w : write behavior for name conflicts (0,1,2, default 2: 0=skip duplicates, 1=overwrite, 2=add suffix)\n"); - printf(" -x : crop 3D acquisitions (y/n/i, default n, use 'i'gnore to neither crop nor rotate 3D acquistions)\n"); + printf(" -x : crop 3D acquisitions (y/n/i, default n, use 'i'gnore to neither crop nor rotate 3D acquisitions)\n"); char gzCh = 'n'; if (opts.isGz) gzCh = 'y'; diff --git a/console/makefile b/console/makefile index 077b9d91..0b236b13 100644 --- a/console/makefile +++ b/console/makefile @@ -50,3 +50,6 @@ all: sanitize: g++ -O1 -g -fsanitize=address -fno-omit-frame-pointer $(LFLAGS) $(UFILES) + +noroi: + g++ $(CFLAGS) -I. $(JSFLAGS) $(JFLAGS) $(LFLAGS) $(UFILES) -DmyNoRois diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index fc9b9b41..efff6862 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1745,8 +1745,10 @@ tse3d: T2*/ fprintf(fp, "\t\"DiffGradientCyclingGE\": \"2TR\",\n"); if (d.diffCyclingModeGE == kGE_DIFF_CYCLING_3TR) fprintf(fp, "\t\"DiffGradientCyclingGE\": \"3TR\",\n"); + // codespell:disable if (d.diffCyclingModeGE == kGE_DIFF_CYCLING_SPOFF) fprintf(fp, "\t\"DiffGradientCyclingGE\": \"SPOFF\",\n"); + // codespell:enable } } #ifdef myReadAsciiCsa @@ -8401,6 +8403,7 @@ int saveDcm2NiiCore(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata d else nii_saveNII(pathoutnameADC, hdr0, imgM, opts, dcmList[dcmSort[0].indx]); } + #ifndef myNoRois if (isHasOverlay) { //each series can have up to 16 overlays, overlays may not be on all slices for (int j = 0; j < kMaxOverlay; j++) { bool isOverlay = false; @@ -8452,6 +8455,7 @@ int saveDcm2NiiCore(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata d nii_saveNII(pathoutnameROI, hdrr, imgR, opts, dcmList[dcmSort[0].indx]); } } + #endif //myNoRois imgM = removeADC(&hdr0, imgM, numADC); if (iVaries) printMessage("Saving as 32-bit float (slope, intercept or bits allocated varies).\n"); From 720ba601c432235c098aeada4929c697ea9acca6 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Wed, 24 Jul 2024 17:24:59 -0700 Subject: [PATCH 29/82] minBvalThreshold (https://github.com/rordenlab/dcm2niix/issues/827) --- console/nii_dicom_batch.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index efff6862..387184d0 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -412,6 +412,9 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI printMessage("Check bvecs: expected Patient Position (0018,5100) to be 'HFS' not '%s'\n", d->patientOrient); //return; //see https://github.com/rordenlab/dcm2niix/issues/238 } + float minBvalThreshold = 6.0; + if (d->manufacturer == kMANUFACTURER_SIEMENS) + minBvalThreshold = 50.0; vec3 read_vector = setVec3(d->orient[1], d->orient[2], d->orient[3]); vec3 phase_vector = setVec3(d->orient[4], d->orient[5], d->orient[6]); vec3 slice_vector = crossProduct(read_vector, phase_vector); @@ -421,7 +424,7 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI for (int i = 0; i < d->CSA.numDti; i++) { float vLen = sqrt((vx[i].V[1] * vx[i].V[1]) + (vx[i].V[2] * vx[i].V[2]) + (vx[i].V[3] * vx[i].V[3])); if ((vx[i].V[0] <= FLT_EPSILON) || (vLen <= FLT_EPSILON)) { //bvalue=0 - if ((vx[i].V[0] > 50.0) && (!d->isDerived)) //Philip stores n.b. UIH B=1.25126 Vec=0,0,0 while Philips stored isotropic images + if ((vx[i].V[0] > minBvalThreshold) && (!d->isDerived)) //Philip stores n.b. UIH B=1.25126 Vec=0,0,0 while Philips stored isotropic images printWarning("Volume %d appears to be derived image ADC/Isotropic (non-zero b-value with zero vector length)\n", i); continue; //do not normalize or reorient b0 vectors } //if bvalue=0 @@ -2498,6 +2501,9 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st ImageList *images = (ImageList *)opts.imageList; #endif //https://github.com/rordenlab/dcm2niix/issues/352 + float minBvalThreshold = 6.0; + if (dcmList[indx0].manufacturer == kMANUFACTURER_SIEMENS) + minBvalThreshold = 50.0; bool allB0 = dcmList[indx0].isDiffusion; bool isDerived = dcmList[indx0].isDerived; if (dcmList[indx0].isDerived) @@ -2513,7 +2519,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st allB0 = false; } } - if ((numDti == 1) && (dti4D->S[0].V[0] > 50.0)) + if ((numDti == 1) && (dti4D->S[0].V[0] > minBvalThreshold)) allB0 = false; if (allB0) { if (opts.isVerbose) @@ -2579,7 +2585,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st for (int i = 1; i < numDti; i++) //check if all bvalues match first volume if (vx[i].V[0] < minBval) minBval = vx[i].V[0]; - if (minBval > 50.0) + if (minBval > minBvalThreshold) bValueVaries = true; //start issue394: experimental, single volume per series, Siemens XA if (!isAllZeroFloat(vx[0].V[1], vx[0].V[2], vx[0].V[3])) @@ -4644,11 +4650,14 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im b_max = dti4D->S[i].V[0]; fprintf(fp, "DWMRI_b-value:=%g\n", b_max); //gradient tag, e.g. DWMRI_gradient_0000:=0.0 0.0 0.0 + float minBvalThreshold = 6.0; + if (d.manufacturer == kMANUFACTURER_SIEMENS) + minBvalThreshold = 50.0; for (int i = 0; i < numDTI; i++) { float factor = 0.0; if (b_max > 0) factor = sqrt(dti4D->S[i].V[0] / b_max); - if ((dti4D->S[i].V[0] > 50.0) && (isSameFloatGE(0.0, dti4D->S[i].V[1])) && (isSameFloatGE(0.0, dti4D->S[i].V[2])) && (isSameFloatGE(0.0, dti4D->S[i].V[3]))) { + if ((dti4D->S[i].V[0] > minBvalThreshold) && (isSameFloatGE(0.0, dti4D->S[i].V[1])) && (isSameFloatGE(0.0, dti4D->S[i].V[2])) && (isSameFloatGE(0.0, dti4D->S[i].V[3]))) { //On May 2, 2019, at 10:47 AM, Gordon L. Kindlmann <> wrote: //(assuming b_max 2000, we write "isotropic" for the b=2000 isotropic image, and specify the b-value if it is an isotropic image but not b-bax // DWMRI_gradient_0003:=isotropic b=1000 From 1eab7243aabd8dcf873ef86d8547a481f39172f1 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Thu, 25 Jul 2024 10:47:05 -0700 Subject: [PATCH 30/82] Siemens instance number re-use (https://github.com/rordenlab/dcm2niix/issues/837) --- console/nii_dicom.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 149450ee..b6c6b83f 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4517,6 +4517,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kSequenceVariant21 0x0021 + (0x105B << 16) //CS Siemens ONLY: For GE this is TaggingFlipAngle #define kScanOptionsSiemens 0x0021 + (0x105C << 16) //CS Siemens ONLY #define kPATModeText 0x0021 + (0x1009 << 16) //LO, see kImaPATModeText +#define kFrameNumberInSeries 0x0021 + (0x118A << 16) //IS issue837 #define kCSASeriesHeaderInfoXA 0x0021 + (0x1019 << 16) #define kCSASeriesHeaderInfoXA2 0x0021 + (0x11FE << 16) #define kTimeAfterStart 0x0021 + (0x1104 << 16) //DS @@ -4727,6 +4728,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD int gradientOrientationNumberPhilips = -1; int numberOfFrames = 0; int numberOfFramesICEdims = 0; + int frameNumberInSeries = -1; //issue837 //int MRImageGradientOrientationNumber = 0; //int minGradNum = kMaxDTI4D + 1; //int maxGradNum = -1; @@ -6227,6 +6229,9 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kScanOptionsSiemens: dcmStr(lLength, &buffer[lPos], scanOptionsSiemens, true); break; + case kFrameNumberInSeries: //issue837 + frameNumberInSeries = dcmStrInt(lLength, &buffer[lPos]); + break; case kPATModeText: { //e.g. Siemens iPAT x2 listed as "p2" char accelStr[kDICOMStr]; dcmStr(lLength, &buffer[lPos], accelStr); @@ -8072,6 +8077,8 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.seriesUidCrc = mz_crc32X((unsigned char *)&d.protocolName, strlen(d.protocolName)); } } + if (frameNumberInSeries >= 0) //issue837 + d.imageNum = frameNumberInSeries; //TODO533: alias Philips ASL PLD as frameDuration? isKludgeIssue533 //if ((d.manufacturer == kMANUFACTURER_PHILIPS) && ((!isTriggerSynced) || (!isProspectiveSynced)) ) //issue408 // d.triggerDelayTime = 0.0; //Philips ASL use "(0018,9037) CS [NONE]" but "(2001,1010) CS [TRIGGERED]", a situation not described in issue408 From e039cb387b788fe6a6d80a08e683c801e5a7dabc Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Thu, 25 Jul 2024 10:54:01 -0700 Subject: [PATCH 31/82] Specific for Siemens --- console/nii_dicom.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index b6c6b83f..1204773c 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -6230,6 +6230,8 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD dcmStr(lLength, &buffer[lPos], scanOptionsSiemens, true); break; case kFrameNumberInSeries: //issue837 + if (d.manufacturer != kMANUFACTURER_SIEMENS) + break; frameNumberInSeries = dcmStrInt(lLength, &buffer[lPos]); break; case kPATModeText: { //e.g. Siemens iPAT x2 listed as "p2" From 446419e23494e040884a68997f3db84cf8d0f0b4 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Thu, 25 Jul 2024 16:48:56 -0700 Subject: [PATCH 32/82] TablePosition (https://github.com/rordenlab/dcm2niix/issues/726) --- BIDS/README.md | 12 ++++++++---- console/nii_dicom.cpp | 27 +++++++++++++++++++++++++++ console/nii_dicom.h | 2 +- console/nii_dicom_batch.cpp | 4 ++++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/BIDS/README.md b/BIDS/README.md index 31a440da..1b5a677b 100644 --- a/BIDS/README.md +++ b/BIDS/README.md @@ -244,8 +244,9 @@ Data unique to [GE](https://github.com/rordenlab/dcm2niix/tree/master/GE). Deter | ASLLabelingTechnique | | DICOM tag 0043,10A4 | D | | LabelingDuration | s | DICOM tag 0043,10A5 | B | | SliceTiming | s | [see notes](https://github.com/rordenlab/dcm2niix/tree/master/GE#slice-timing) | B | -| CompressedSensingFactor | | DICOM tag 0043,10b7 | D | -| DeepLearningFactor | | DICOM tag 0043,10ca | D | +| CompressedSensingFactor | | DICOM tag 0043,10B7 | D | +| TablePosition | mm | DICOM tag 0043,10B2 | B | +| DeepLearningFactor | | DICOM tag 0043,10CA | D | ### Manufacturer Philips @@ -258,11 +259,12 @@ Data unique to Philips, including [custom intensity scaling](https://www.ncbi.nl | PhilipsRWVIntercept | | DICOM tag 0040,9224 | D | | PhilipsRescaleSlope | | DICOM tag 0028,1053 | D | | PhilipsRescaleIntercept | | DICOM tag 0028,1052 | D | -| PhilipsScaleSlope | | DICOM tag 2005,100E | D | | UsePhilipsFloatNotDisplayScaling | | dcm2niix option `-p y` or `-p n` | D | | PartialFourierEnabled | | DICOM tag 0018,9081, `YES` | D | | PhaseEncodingStepsNoPartialFourier | | DICOM tag 0018,9231 | D | | WaterFatShift | | DICOM tag 2001,1022 | D | +| PhilipsScaleSlope | | DICOM tag 2005,100E | D | +| TablePosition | mm | DICOM tag 2005,143D | B | ### Manufacturer Siemens (Arterial Spin Labeling) @@ -332,6 +334,7 @@ Fields specific to Siemens V*-series (e.g. VB, VE) MRI systems (e.g. Verio, Trio | ConsistencyInfo | | The more complete software version, e.g. VE11C or VE11E instead of just VE11. | D | | CoilCombinationMethod | | Detects `Sum of Squares` and `Adaptive Combine` | B | | MatrixCoilMode | | Detects `SENSE` and `GRAPPA` | B | +| TablePosition | mm | DICOM tag 0019,1014 | B | | DwellTime | | DICOM tag 0019,1018 | B | | BandwidthPerPixelPhaseEncode | Hz | DICOM tag 0019,1028 | D | | ImageOrientationText | | DICOM tag 0051,100E | D | @@ -347,9 +350,10 @@ Fields specific to [Siemens XA-series](https://github.com/rordenlab/dcm2niix/tre | BandwidthPerPixelPhaseEncode | Hz | DICOM tag 0021,1153 | D | | ScanningSequence | | DICOM tag 0021,105a | D | | PostLabelingDelay | s | DICOM tag 0018,9258 | B | +| TablePosition | mm | DICOM tag 0021,1005 | B | | NonlinearGradientCorrection | b | 0008,0008 or 0021,1175 | B | | PhaseEncodingDirection | | polarity from 0021,111c | B | -| SpoilingState | | 0021,105B | B | +| SpoilingState | | DICOM tag 0021,105B | B | Siemens also includes some sequence information in the private MRPhoenixProtocol (0021,1019) tag. You can view this with [gdcmdump](https://gdcm.sourceforge.net/html/gdcmdump.html), e.g. `gdcmdump --mrprotocol img.dcm`. Fields that dcm2niix inspects include `sPat.lAccelFact3D `, `sPat.lAccelFactPE`, `sPat.lRefLinesPE` and `sPat.ucPATMode`. The behavior of dcm2niix will be more well documented as our understanding of this tag improves. diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 1204773c..7e192940 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -945,6 +945,10 @@ struct TDICOMdata clear_dicom_data() { strcpy(d.patientAge, ""); d.CSA.bandwidthPerPixelPhaseEncode = 0.0; d.CSA.mosaicSlices = 0; + d.CSA.tablePos[0] = -1.0; //unset + d.CSA.tablePos[1] = 0.0; // x + d.CSA.tablePos[2] = 0.0; // y + d.CSA.tablePos[3] = 0.0; // z d.CSA.sliceNormV[1] = 0.0; d.CSA.sliceNormV[2] = 0.0; d.CSA.sliceNormV[3] = 1.0; //default Siemens Image Numbering is F>>H https://www.mccauslandcenter.sc.edu/crnl/tools/stc @@ -4472,6 +4476,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD // https://nciphub.org/groups/qindicom/wiki/DiffusionrelatedDICOMtags:experienceacrosssites?action=pdf #define kDiffusion_bValueSiemens 0x0019 + (0x100C << 16) //IS #define kDiffusionGradientDirectionSiemens 0x0019 + (0x100E << 16) //FD +#define kImaRelTablePosition 0x0019 + (0x1014 << 16) //IS[] #define kSeriesPlaneGE 0x0019 + (0x1017 << 16) //SS #define kDwellTime 0x0019 + (0x1018 << 16) //IS in NSec, see https://github.com/rordenlab/dcm2niix/issues/127 #define kLastScanLoc 0x0019 + (0x101B << 16) @@ -4513,6 +4518,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kTemporalPositionIndex 0x0020 + uint32_t(0x9128 << 16) // UL #define kDimensionIndexPointer 0x0020 + uint32_t(0x9165 << 16) //Private Group 21 as Used by Siemens: +#define kRelTablePosition 0x0021 + (0x1005 << 16) //IS #define kScanningSequenceSiemens 0x0021 + (0x105A << 16) //CS n.b. for GE this is Diffusion direction of SL! #define kSequenceVariant21 0x0021 + (0x105B << 16) //CS Siemens ONLY: For GE this is TaggingFlipAngle #define kScanOptionsSiemens 0x0021 + (0x105C << 16) //CS Siemens ONLY @@ -4643,6 +4649,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kDiffusionDirectionFH 0x2005 + (0x10B2 << 16) #define kDeepLearningPhilips 0x2005 + (0x1110 << 16) #define kPrivatePerFrameSq 0x2005 + (0x140F << 16) +#define kMRStackTablePosLong 0x2005 + (0x143C << 16) //FL #define kMRImageDiffBValueNumber 0x2005 + (0x1412 << 16) //IS #define kMRImageGradientOrientationNumber 0x2005+(0x1413 << 16) //IS #define kMRImageLabelType 0x2005 + (0x1429 << 16) //CS ASL LBL_CTL https://github.com/physimals/dcm_convert_phillips/ @@ -5950,6 +5957,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.CSA.dtiV[3] = v[2]; break; } + case kImaRelTablePosition: { + if (d.manufacturer != kMANUFACTURER_SIEMENS) + break; + dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &d.CSA.tablePos[0]); //slice position + d.CSA.tablePos[0] = 1.0; //set + break; + } case kNumberOfDiffusionT2GE: { if (d.manufacturer != kMANUFACTURER_GE) break; @@ -6755,6 +6769,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.isEPI = true; break; //warp } + case kRelTablePosition: { + if (d.manufacturer != kMANUFACTURER_SIEMENS) + break; + dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &d.CSA.tablePos[0]); //slice position + d.CSA.tablePos[0] = 1.0; //set + break; + } case kScanningSequenceSiemens: if (d.manufacturer == kMANUFACTURER_SIEMENS) dcmStr(lLength, &buffer[lPos], scanningSequenceSiemens); @@ -7093,6 +7114,12 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (toupper(buffer[lPos]) == 'L') d.aslFlags = kASL_FLAG_PHILIPS_LABEL; if (toupper(buffer[lPos]) == 'C') d.aslFlags = kASL_FLAG_PHILIPS_CONTROL; break; + case kMRStackTablePosLong: //FL + if (d.manufacturer != kMANUFACTURER_PHILIPS) + break; + d.CSA.tablePos[3] = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); + d.CSA.tablePos[0] = 1.0; + break; case kMRImageDiffBValueNumber: if (d.manufacturer != kMANUFACTURER_PHILIPS) break; diff --git a/console/nii_dicom.h b/console/nii_dicom.h index 6d3546df..39200ef0 100644 --- a/console/nii_dicom.h +++ b/console/nii_dicom.h @@ -228,7 +228,7 @@ static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8; } TCSAitem; //Siemens csa item structure #endif struct TCSAdata { - float sliceTiming[kMaxEPI3D], dtiV[4], sliceNormV[4], bandwidthPerPixelPhaseEncode, sliceMeasurementDuration; + float sliceTiming[kMaxEPI3D], dtiV[4], sliceNormV[4], bandwidthPerPixelPhaseEncode, sliceMeasurementDuration, tablePos[4]; int coilNumber, numDti, SeriesHeader_offset, SeriesHeader_length, multiBandFactor, sliceOrder, slice_start, slice_end, mosaicSlices, protocolSliceNumber1, phaseEncodingDirectionPositive; bool isPhaseMap; char bidsDataType[kDICOMStr]; //anat, func, dwi diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 387184d0..cd65c820 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1674,7 +1674,11 @@ tse3d: T2*/ json_Float(fp, "\t\"SpacingBetweenSlices\": %g,\n", d.zSpacing); } //if (!opts.isAnonymizeBIDS) //issue668 is SAR identifiable?? + //an identical sequence can create different SAR and TablePosition depending on participant json_Float(fp, "\t\"SAR\": %g,\n", d.SAR); + if (d.CSA.tablePos[0] > 0.0) + fprintf(fp, "\t\"TablePosition\": [\n\t\t%g,\n\t\t%g,\n\t\t%g\t],\n", d.CSA.tablePos[1], d.CSA.tablePos[2], d.CSA.tablePos[3]); + if (d.numberOfAverages > 1.0) json_Float(fp, "\t\"NumberOfAverages\": %g,\n", d.numberOfAverages); if ((d.echoNum > 1) || ((d.isMultiEcho) && (d.echoNum > 0))) From eb1eb11912887bb549d75ad93bea2ab349434c96 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Thu, 25 Jul 2024 17:10:22 -0700 Subject: [PATCH 33/82] GE TablePosition (https://github.com/rordenlab/dcm2niix/issues/726) --- console/nii_dicom.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 7e192940..46dad9bf 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4583,6 +4583,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kASLContrastTechniqueGE 0x0043 + (0x10A3 << 16) //CS #define kASLLabelingTechniqueGE 0x0043 + (0x10A4 << 16) //LO #define kDurationLabelPulseGE 0x0043 + (0x10A5 << 16) //IS +#define kMRTablePositionInformation 0x0043 + (0x10B2 << 16) //LO #define kMultiBandGE 0x0043 + (0x10B6 << 16) //LO #define kCompressedSensingParameters 0x0043 + (0x10B7 << 16) //LO #define kDeepLearningParameters 0x0043 + (0x10CA << 16) //LO "0.75\High" @@ -7368,6 +7369,18 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.durationLabelPulseGE = dcmStrInt(lLength, &buffer[lPos]); break; } + case kMRTablePositionInformation: { //LO issue427GE + if (d.manufacturer != kMANUFACTURER_GE) + break; + //LO array of floats stored in LONG STRING! + // [960.5\400\17.9108\0\-9999\-9999] + //we want 3rd value, e.g. 17.9: + float v[5]; + dcmMultiFloat(lLength, (char *)&buffer[lPos], 5, v); + d.CSA.tablePos[3] = v[3]; + d.CSA.tablePos[0] = 1.0; + break; + } case kMultiBandGE: { //LO issue427GE if (d.manufacturer != kMANUFACTURER_GE) break; From 041310ef667e230dc41b78ed82d403ea7e24405e Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Thu, 25 Jul 2024 17:26:28 -0700 Subject: [PATCH 34/82] codespell:disable does not seem to work --- .codespellrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.codespellrc b/.codespellrc index a9022c82..157701d1 100644 --- a/.codespellrc +++ b/.codespellrc @@ -3,9 +3,10 @@ skip = .git,*.json,dcm_qa* # te - the TE used in the code often # clen - another common variable for length of smth # tage - for \tAge, inline ignores are yet to be released +# SPOFF - GE diffusion cycling mode # nd - there is some kind of ND whi # ❯ grep -e 'trace or MD ' -e 'Trace/ND' ./console/nii_dicom_batch.cpp # // the isotropic trace or MD can be calculated) often come as # /*if (!dcmList[indx0].isDerived) //no need to warn if images are derived Trace/ND pair # ser - used in printMessage(" acq %d img %d ser %ld ... -ignore-words-list = te,clen,tage,nd,ser +ignore-words-list = te,clen,tage,nd,ser,SPOFF From 7d62584d68524587b11fe52648489c19edd752ee Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Tue, 30 Jul 2024 10:17:22 -0700 Subject: [PATCH 35/82] Bruker kludge (https://github.com/rordenlab/dcm2niix/issues/839) --- console/nii_dicom.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++ console/nii_dicom.h | 2 +- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 46dad9bf..680316e2 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4717,6 +4717,10 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD int locationsInAcquisitionGE = 0; int PETImageIndex = 0; int inStackPositionNumber = 0; + int isIssue839 = false; + float patientPosition1[kMaxSlice2D]; + float patientPosition2[kMaxSlice2D]; + float patientPosition3[kMaxSlice2D]; bool isKludgeIssue533 = false; uint32_t dimensionIndexPointer[MAX_NUMBER_OF_DIMENSIONS]; size_t dimensionIndexPointerCounter = 0; @@ -4930,10 +4934,24 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD acquisitionTimePhilips = - 1.0; } int ndim = nDimIndxVal; + //issue 839 + patientPosition1[numDimensionIndexValues] = patientPosition[1]; + patientPosition2[numDimensionIndexValues] = patientPosition[2]; + patientPosition3[numDimensionIndexValues] = patientPosition[3]; if (inStackPositionNumber > 0) { //for images without SliceNumberMrPhilips (2001,100A) int sliceNumber = inStackPositionNumber; //printf("slice %d \n", sliceNumber); + if ((!isIssue839) && (sliceNumber == 1) && (!isnan(patientPositionStartPhilips[1]))) { + float dx = sqrt( + pow(patientPositionStartPhilips[1] - patientPosition[1], 2) + + pow(patientPositionStartPhilips[2] - patientPosition[2], 2) + + pow(patientPositionStartPhilips[3] - patientPosition[3], 2)); + if (!isSameFloatGE(dx, 0.0)) { + isIssue839 = true; + printWarning("repeats of first inStackPositionNumber are at different locations (dx = %gmm; issue 839)\n", dx); + } + } if ((sliceNumber == 1) && (!isnan(patientPosition[1]))) { for (int k = 0; k < 4; k++) patientPositionStartPhilips[k] = patientPosition[k]; @@ -7901,6 +7919,51 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD // printWarning("3D EPI with FrameAcquisitionDuration = %gs volumes = %d (see issue 369)\n", frameAcquisitionDuration/1000.0, d.xyzDim[4]); if (numDimensionIndexValues > 1) strcpy(d.imageType, imageType1st); //for multi-frame datasets, return name of book, not name of last chapter + if (((d.isValid)) && (isIssue839) && (numDimensionIndexValues > 2)) { + //rewrite + for (int j = 0; j < MAX_NUMBER_OF_DIMENSIONS; j++) { + for (int i = 0; i < numDimensionIndexValues; i++) { + dcmDim[i].dimIdx[j] = 0; + } + } + // we need a scalar value for sorting + // first pass: find slice that is furthest from isocenter, n.b. arbitrary if tied + float mxDx = 0.0; + int mxIdx = 0; // + for (int i = 0; i < numDimensionIndexValues; i++) { + float dx = sqrt(pow(patientPosition1[i],2)+pow(patientPosition2[i],2)+pow(patientPosition3[i],2)); + if (dx > mxDx) { + mxDx = dx; + mxIdx = i; + } + if (isVerbose > 1) printf("slice %d is %gmm from isocenter\n", i, dx); + } + if (isVerbose > 1) printf("slice %d is furthest (%gmm) from isocenter\n", mxIdx, mxDx); + // second pass: measure each slices scalar distance from furthest slice + // ensure their are no repeated slice positions + fidx *objects = (fidx *)malloc(sizeof(struct fidx) * numDimensionIndexValues); + bool isSamePosition = false; + for (int i = 0; i < numDimensionIndexValues; i++) { + float dx = sqrt(pow(patientPosition1[i]-patientPosition1[mxIdx],2)+pow(patientPosition2[i]-patientPosition2[mxIdx],2)+pow(patientPosition3[i]-patientPosition3[mxIdx],2)); + if (isVerbose > 1) printf("slice %d is %gmm from furthest slice\n", i, dx); + objects[i].value = dx; + //sliceMM[i]; + objects[i].index = i; + if ((i != mxIdx) && (isSameFloatGE(dx, 0.0))) + isSamePosition = true; + } + if (isSamePosition) { + printError("Multivolume file with invalid DimensionIndexValues (issue839)\n"); + d.isValid = false; + } + qsort(objects, numberOfFrames, sizeof(struct fidx), fcmp); + numDimensionIndexValues = numberOfFrames; + for (int i = 0; i < numDimensionIndexValues; i++) { + dcmDim[objects[i].index].dimIdx[0] = i; + } + d.xyzDim[4] = 1; + d.xyzDim[3] = numDimensionIndexValues; + } //issue839 if ((numDimensionIndexValues > 1) && (numDimensionIndexValues == numberOfFrames)) { //Philips enhanced datasets can have custom slice orders and pack images with different TE, Phase/Magnitude/Etc. int maxVariableItem = 0; diff --git a/console/nii_dicom.h b/console/nii_dicom.h index 39200ef0..e8f2b07a 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.20240327" +#define kDCMdate "v1.0.20240730" #define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic From fe4021dd1bfc8fedbb4a2acc29272aa966cc4457 Mon Sep 17 00:00:00 2001 From: Andras Lasso Date: Wed, 31 Jul 2024 14:57:52 +0200 Subject: [PATCH 36/82] Fix corrupted raw nrrd file output on Windows On Windows, when NRRD file was written into .nrrd format with raw pixels then the image was corrupted. The root cause was that the file was opened in text mode and therefore each 0a byte was converted to 0d0a resulting in an incorrect byte stream. --- console/nii_dicom_batch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index cd65c820..cff0b91c 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -4452,7 +4452,7 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im strcat(fname, ".nhdr"); //nrrd or nhdr else strcat(fname, ".nrrd"); //nrrd or nhdr - FILE *fp = fopen(fname, "w"); + FILE *fp = fopen(fname, "wb"); fprintf(fp, "NRRD0005\n"); fprintf(fp, "# Complete NRRD file format specification at:\n"); fprintf(fp, "# http://teem.sourceforge.net/nrrd/format.html\n"); From 6a01168aec66a77db81de512dec566a3bc5144cf Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Sun, 11 Aug 2024 11:48:23 -0400 Subject: [PATCH 37/82] TablePosition (https://github.com/rordenlab/dcm2niix/issues/726) --- BIDS/README.md | 2 +- console/nii_dicom.cpp | 8 ++++---- console/nii_dicom.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/BIDS/README.md b/BIDS/README.md index 1b5a677b..e2b5e6c2 100644 --- a/BIDS/README.md +++ b/BIDS/README.md @@ -264,7 +264,7 @@ Data unique to Philips, including [custom intensity scaling](https://www.ncbi.nl | PhaseEncodingStepsNoPartialFourier | | DICOM tag 0018,9231 | D | | WaterFatShift | | DICOM tag 2001,1022 | D | | PhilipsScaleSlope | | DICOM tag 2005,100E | D | -| TablePosition | mm | DICOM tag 2005,143D | B | +| TablePosition | mm | DICOM tag 2005,143C | B | ### Manufacturer Siemens (Arterial Spin Labeling) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 680316e2..440e568e 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4476,7 +4476,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD // https://nciphub.org/groups/qindicom/wiki/DiffusionrelatedDICOMtags:experienceacrosssites?action=pdf #define kDiffusion_bValueSiemens 0x0019 + (0x100C << 16) //IS #define kDiffusionGradientDirectionSiemens 0x0019 + (0x100E << 16) //FD -#define kImaRelTablePosition 0x0019 + (0x1014 << 16) //IS[] +#define kImaRelTablePosition 0x0019 + (0x1014 << 16) //IS[] Siemens V* #define kSeriesPlaneGE 0x0019 + (0x1017 << 16) //SS #define kDwellTime 0x0019 + (0x1018 << 16) //IS in NSec, see https://github.com/rordenlab/dcm2niix/issues/127 #define kLastScanLoc 0x0019 + (0x101B << 16) @@ -4518,7 +4518,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kTemporalPositionIndex 0x0020 + uint32_t(0x9128 << 16) // UL #define kDimensionIndexPointer 0x0020 + uint32_t(0x9165 << 16) //Private Group 21 as Used by Siemens: -#define kRelTablePosition 0x0021 + (0x1005 << 16) //IS +#define kRelTablePosition 0x0021 + (0x1005 << 16) //IS Siemens XA #define kScanningSequenceSiemens 0x0021 + (0x105A << 16) //CS n.b. for GE this is Diffusion direction of SL! #define kSequenceVariant21 0x0021 + (0x105B << 16) //CS Siemens ONLY: For GE this is TaggingFlipAngle #define kScanOptionsSiemens 0x0021 + (0x105C << 16) //CS Siemens ONLY @@ -4583,7 +4583,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kASLContrastTechniqueGE 0x0043 + (0x10A3 << 16) //CS #define kASLLabelingTechniqueGE 0x0043 + (0x10A4 << 16) //LO #define kDurationLabelPulseGE 0x0043 + (0x10A5 << 16) //IS -#define kMRTablePositionInformation 0x0043 + (0x10B2 << 16) //LO +#define kMRTablePositionInformation 0x0043 + (0x10B2 << 16) //LO GE #define kMultiBandGE 0x0043 + (0x10B6 << 16) //LO #define kCompressedSensingParameters 0x0043 + (0x10B7 << 16) //LO #define kDeepLearningParameters 0x0043 + (0x10CA << 16) //LO "0.75\High" @@ -4650,7 +4650,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kDiffusionDirectionFH 0x2005 + (0x10B2 << 16) #define kDeepLearningPhilips 0x2005 + (0x1110 << 16) #define kPrivatePerFrameSq 0x2005 + (0x140F << 16) -#define kMRStackTablePosLong 0x2005 + (0x143C << 16) //FL +#define kMRStackTablePosLong 0x2005 + (0x143C << 16) //FL Philips #define kMRImageDiffBValueNumber 0x2005 + (0x1412 << 16) //IS #define kMRImageGradientOrientationNumber 0x2005+(0x1413 << 16) //IS #define kMRImageLabelType 0x2005 + (0x1429 << 16) //CS ASL LBL_CTL https://github.com/physimals/dcm_convert_phillips/ diff --git a/console/nii_dicom.h b/console/nii_dicom.h index e8f2b07a..cfc8450a 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.20240730" +#define kDCMdate "v1.0.20240731" #define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic From ba03669a5dd064eafff5a9a14aa29efd533a498c Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Sun, 11 Aug 2024 12:41:13 -0400 Subject: [PATCH 38/82] Siemens version details (https://github.com/rordenlab/dcm2niix/issues/848) --- console/nii_dicom_batch.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index cff0b91c..b44f0bea 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -836,7 +836,20 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i char keyStrCoil[] = "sCoilElementID.tCoilID"; readKeyStr(keyStrCoil, keyPos, csaLengthTrim, coilID); char keyStrCI[] = "sProtConsistencyInfo.tMeasuredBaselineString"; + // issue848 VE11 reports N4_VE11C_LATEST_20160120 readKeyStr(keyStrCI, keyPos, csaLengthTrim, consistencyInfo); + // issue848 VB17 reports N4_VB17A_LATEST_20090307 + if (strlen(consistencyInfo) < 1) { + char keyStrCI2[] = "sProtConsistencyInfo.tBaselineString"; + readKeyStr(keyStrCI2, keyPos, csaLengthTrim, consistencyInfo); + } + // issue848 XA30 reports 63010001 + if (strlen(consistencyInfo) < 1) { + char keyStrCI3[] = "sProtConsistencyInfo.ulConvFromVersion"; + int vers = readKey(keyStrCI3, keyPos, csaLengthTrim); + if (vers > 0) + snprintf(consistencyInfo, 16, "%d", vers); + } char keyStrCS[] = "sCoilSelectMeas.sCoilStringForConversion"; readKeyStr(keyStrCS, keyPos, csaLengthTrim, coilElements); char keyStrSeq[] = "tSequenceFileName"; From d675aefe94abb014cb9ca5ae18e9cd657a6f9b03 Mon Sep 17 00:00:00 2001 From: Jaemin Shin Date: Mon, 12 Aug 2024 17:09:38 -0500 Subject: [PATCH 39/82] GE TablePosition tableDelta --- BIDS/README.md | 2 +- console/nii_dicom.cpp | 11 +++++++++-- console/nii_dicom.h | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/BIDS/README.md b/BIDS/README.md index e2b5e6c2..132f478e 100644 --- a/BIDS/README.md +++ b/BIDS/README.md @@ -245,7 +245,7 @@ Data unique to [GE](https://github.com/rordenlab/dcm2niix/tree/master/GE). Deter | LabelingDuration | s | DICOM tag 0043,10A5 | B | | SliceTiming | s | [see notes](https://github.com/rordenlab/dcm2niix/tree/master/GE#slice-timing) | B | | CompressedSensingFactor | | DICOM tag 0043,10B7 | D | -| TablePosition | mm | DICOM tag 0043,10B2 | B | +| TablePosition | mm | The 3rd value of DICOM tag 0043,10B2 - the value of DICOM tag 0019,107F | B | | DeepLearningFactor | | DICOM tag 0043,10CA | D | ### Manufacturer Philips diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 440e568e..0be768d7 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4483,6 +4483,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kBandwidthPerPixelPhaseEncode 0x0019 + (0x1028 << 16) //FD #define kSliceTimeSiemens 0x0019 + (0x1029 << 16) ///FD #define kAcquisitionDurationGE 0x0019 + (0x105a << 16) //FL Acquisition Duration in microsecond, Duration of Scan (series) +#define kTableDeltaGE 0x0019 + (0x107f << 16) //DS Table delta #define kPulseSequenceNameGE 0x0019 + (0x109C << 16) //LO 'epiRT' or 'epi' #define kInternalPulseSequenceNameGE 0x0019 + (0x109E << 16) //LO 'EPI' or 'EPI2' #define kRawDataRunNumberGE 0x0019 + (0x10A2 << 16)//SL @@ -4692,6 +4693,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD int userData12GE = 0; float userData15GE = 0; float accelFactPE = 0.0; + float tableDeltaGE = 0.0; int overlayRows = 0; int overlayCols = 0; bool isNeologica = false; @@ -7387,7 +7389,12 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.durationLabelPulseGE = dcmStrInt(lLength, &buffer[lPos]); break; } - case kMRTablePositionInformation: { //LO issue427GE + case kTableDeltaGE: { //DS issue726 + if (d.manufacturer != kMANUFACTURER_GE) + break; + tableDeltaGE = dcmStrFloat(lLength, &buffer[lPos]); + } + case kMRTablePositionInformation: { //LO issue726 if (d.manufacturer != kMANUFACTURER_GE) break; //LO array of floats stored in LONG STRING! @@ -7395,7 +7402,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD //we want 3rd value, e.g. 17.9: float v[5]; dcmMultiFloat(lLength, (char *)&buffer[lPos], 5, v); - d.CSA.tablePos[3] = v[3]; + d.CSA.tablePos[3] = v[3] - tableDeltaGE; d.CSA.tablePos[0] = 1.0; break; } diff --git a/console/nii_dicom.h b/console/nii_dicom.h index cfc8450a..d54908b8 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.20240731" +#define kDCMdate "v1.0.20240812" #define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic From 74401219b85712281077807b419a6fe03123f350 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Tue, 13 Aug 2024 08:50:49 -0400 Subject: [PATCH 40/82] Do not report PhaseEncodingDirection for 3D sequences (https://github.com/rordenlab/dcm2niix/issues/849) --- console/nii_dicom_batch.cpp | 66 +++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index b44f0bea..1d714960 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -2303,38 +2303,40 @@ following logic now occurs earlier to aid bids guess 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 - // This will typically correspond with InPlanePhaseEncodingDirectionDICOM - if (d.phaseEncodingRC == 'C') //Values should be "R"ow, "C"olumn or "?"Unknown - fprintf(fp, "\t\"PhaseEncodingAxis\": \"j\",\n"); - else if (d.phaseEncodingRC == 'R') - fprintf(fp, "\t\"PhaseEncodingAxis\": \"i\",\n"); - } - if (((d.phaseEncodingRC == 'R') || (d.phaseEncodingRC == 'C')) && (!isSkipPhaseEncodingAxis) && (phPos >= 0)) { - //printf("%ld %d %d %c %d\n", d.seriesNum, d.echoTrainLength, isSkipPhaseEncodingAxis, d.phaseEncodingRC, phPos); //test issue 371 - if (d.phaseEncodingRC == 'C') //Values should be "R"ow, "C"olumn or "?"Unknown - fprintf(fp, "\t\"PhaseEncodingDirection\": \"j"); - else if (d.phaseEncodingRC == 'R') - fprintf(fp, "\t\"PhaseEncodingDirection\": \"i"); - else - fprintf(fp, "\t\"PhaseEncodingDirection\": \"?"); - //phaseEncodingDirectionPositive has one of three values: UNKNOWN (-1), NEGATIVE (0), POSITIVE (1) - //However, DICOM and NIfTI are reversed in the j (ROW) direction - //Equivalent to dicm2nii's "if flp(iPhase), phPos = ~phPos; end" - //for samples see https://github.com/rordenlab/dcm2niix/issues/125 - if (phPos < 0) - fprintf(fp, "?"); //unknown - else if ((phPos == 0) && (d.phaseEncodingRC != 'C')) - fprintf(fp, "-"); - else if ((d.phaseEncodingRC == 'C') && (phPos == 1) && (opts.isFlipY)) - fprintf(fp, "-"); - else if ((d.phaseEncodingRC == 'C') && (phPos == 0) && (!opts.isFlipY)) - fprintf(fp, "-"); - fprintf(fp, "\",\n"); - } //only save PhaseEncodingDirection if BOTH direction and POLARITY are known + if (!d.is3DAcq) { //issue849 + 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 + // This will typically correspond with InPlanePhaseEncodingDirectionDICOM + if (d.phaseEncodingRC == 'C') //Values should be "R"ow, "C"olumn or "?"Unknown + fprintf(fp, "\t\"PhaseEncodingAxis\": \"j\",\n"); + else if (d.phaseEncodingRC == 'R') + fprintf(fp, "\t\"PhaseEncodingAxis\": \"i\",\n"); + } + if (((d.phaseEncodingRC == 'R') || (d.phaseEncodingRC == 'C')) && (!isSkipPhaseEncodingAxis) && (phPos >= 0)) { + //printf("%ld %d %d %c %d\n", d.seriesNum, d.echoTrainLength, isSkipPhaseEncodingAxis, d.phaseEncodingRC, phPos); //test issue 371 + if (d.phaseEncodingRC == 'C') //Values should be "R"ow, "C"olumn or "?"Unknown + fprintf(fp, "\t\"PhaseEncodingDirection\": \"j"); + else if (d.phaseEncodingRC == 'R') + fprintf(fp, "\t\"PhaseEncodingDirection\": \"i"); + else + fprintf(fp, "\t\"PhaseEncodingDirection\": \"?"); + //phaseEncodingDirectionPositive has one of three values: UNKNOWN (-1), NEGATIVE (0), POSITIVE (1) + //However, DICOM and NIfTI are reversed in the j (ROW) direction + //Equivalent to dicm2nii's "if flp(iPhase), phPos = ~phPos; end" + //for samples see https://github.com/rordenlab/dcm2niix/issues/125 + if (phPos < 0) + fprintf(fp, "?"); //unknown + else if ((phPos == 0) && (d.phaseEncodingRC != 'C')) + fprintf(fp, "-"); + else if ((d.phaseEncodingRC == 'C') && (phPos == 1) && (opts.isFlipY)) + fprintf(fp, "-"); + else if ((d.phaseEncodingRC == 'C') && (phPos == 0) && (!opts.isFlipY)) + fprintf(fp, "-"); + fprintf(fp, "\",\n"); + } //only save PhaseEncodingDirection if BOTH direction and POLARITY are known + } // if (!d.is3DAcq), e.g. only for 2D issue849 //Slice Timing UIH or GE >>>> //in theory, we should also report XA10 slice times here, but see series 24 of https://github.com/rordenlab/dcm2niix/issues/236 if ((d.modality != kMODALITY_CT) && (d.modality != kMODALITY_PT) && (!d.is3DAcq) && (h->dim[3] > 1) && (d.CSA.sliceTiming[1] >= 0.0) && (d.CSA.sliceTiming[0] >= 0.0)) { From de3b205bd6522ea8604b5c8e2fbf5d5ea42db703 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Tue, 13 Aug 2024 09:03:31 -0400 Subject: [PATCH 41/82] Revese polarity of Philips/Siemens TablePos Z (https://github.com/rordenlab/dcm2niix/issues/726) --- console/nii_dicom.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 0be768d7..13605a85 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -5982,6 +5982,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer != kMANUFACTURER_SIEMENS) break; dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &d.CSA.tablePos[0]); //slice position + d.CSA.tablePos[3] = -d.CSA.tablePos[3]; //reverse Z polarity, issue 726 d.CSA.tablePos[0] = 1.0; //set break; } @@ -6794,6 +6795,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer != kMANUFACTURER_SIEMENS) break; dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &d.CSA.tablePos[0]); //slice position + d.CSA.tablePos[3] = -d.CSA.tablePos[3]; //reverse Z polarity, issue 726 d.CSA.tablePos[0] = 1.0; //set break; } @@ -7138,7 +7140,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kMRStackTablePosLong: //FL if (d.manufacturer != kMANUFACTURER_PHILIPS) break; - d.CSA.tablePos[3] = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); + d.CSA.tablePos[3] = - dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); d.CSA.tablePos[0] = 1.0; break; case kMRImageDiffBValueNumber: From 373618d80069decf22286046de60d8b112304898 Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Wed, 14 Aug 2024 09:38:09 -0400 Subject: [PATCH 42/82] Only add "MAGNITUDE" if neither ?MAGNITUDE" or "M" is listed as ImageType (https://github.com/rordenlab/dcm2niix/issues/851) --- console/nii_dicom_batch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 1d714960..539b0e19 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1431,7 +1431,7 @@ tse3d: T2*/ } else isSep = true; } - if ((d.isHasMagnitude) && ((d.manufacturer == kMANUFACTURER_GE) || (strstr(d.imageType, "_MAGNITUDE_") == NULL))) + if ((d.isHasMagnitude) && ((d.manufacturer == kMANUFACTURER_GE) || ( (strstr(d.imageType, "_M_") == NULL) && (strstr(d.imageType, "_MAGNITUDE_") == NULL)))) fprintf(fp, "\", \"MAGNITUDE"); if ((d.isHasPhase) && ((d.manufacturer == kMANUFACTURER_GE) || (strstr(d.imageType, "_PHASE_") == NULL))) fprintf(fp, "\", \"PHASE"); //"_IMAGINARY_" From 5bff3c91adf74829b729d0db38b005b8f1dc07de Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Wed, 14 Aug 2024 09:54:17 -0400 Subject: [PATCH 43/82] Only append PHASE, REAL, etc to ImageType if term or alias not already present (https://github.com/rordenlab/dcm2niix/issues/851) --- console/nii_dicom_batch.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 539b0e19..fcf22971 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1431,15 +1431,15 @@ tse3d: T2*/ } else isSep = true; } - if ((d.isHasMagnitude) && ((d.manufacturer == kMANUFACTURER_GE) || ( (strstr(d.imageType, "_M_") == NULL) && (strstr(d.imageType, "_MAGNITUDE_") == NULL)))) + if ((d.isHasMagnitude) && ((strstr(d.imageType, "_M_") == NULL) && (strstr(d.imageType, "_MAGNITUDE_") == NULL))) fprintf(fp, "\", \"MAGNITUDE"); - if ((d.isHasPhase) && ((d.manufacturer == kMANUFACTURER_GE) || (strstr(d.imageType, "_PHASE_") == NULL))) - fprintf(fp, "\", \"PHASE"); //"_IMAGINARY_" - if ((d.isHasReal) && ((d.manufacturer == kMANUFACTURER_GE) || (strstr(d.imageType, "_REAL_") == NULL))) + if ((d.isHasPhase) && ((strstr(d.imageType, "_P_") == NULL) && (strstr(d.imageType, "_PHASE_") == NULL))) + fprintf(fp, "\", \"PHASE"); + if ((d.isHasReal) && ((strstr(d.imageType, "_R_") == NULL) && (strstr(d.imageType, "_REAL_") == NULL))) fprintf(fp, "\", \"REAL"); - if ((d.isHasImaginary) && ((d.manufacturer == kMANUFACTURER_GE) || (strstr(d.imageType, "_IMAGINARY_") == NULL))) + if ((d.isHasImaginary) && ((strstr(d.imageType, "_I_") == NULL) && (strstr(d.imageType, "_IMAGINARY_") == NULL))) fprintf(fp, "\", \"IMAGINARY"); - if ((d.isRealIsPhaseMapHz)) // && ((d.manufacturer == kMANUFACTURER_GE) || (strstr(d.imageType, "_IMAGINARY_") == NULL)) ) + if ((d.isRealIsPhaseMapHz) && ((strstr(d.imageType, "_FIELDMAPHZ_") == NULL))) fprintf(fp, "\", \"FIELDMAPHZ"); fprintf(fp, "\"],\n"); } From 23eb1eedde4190d8a8b359f988647518dc59a276 Mon Sep 17 00:00:00 2001 From: Jaemin Shin Date: Fri, 16 Aug 2024 10:44:51 -0500 Subject: [PATCH 44/82] Total Read-Out Time Documentation update for GEHC --- GE/README.md | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/GE/README.md b/GE/README.md index 39e740dd..d09ebef2 100644 --- a/GE/README.md +++ b/GE/README.md @@ -50,24 +50,25 @@ Some sequences allow the user to interpolate images in plane (e.g. saving a 2D 6 ## Total Readout Time -One often wants to determine [echo spacing, bandwidth](https://support.brainvoyager.com/brainvoyager/functional-analysis-preparation/29-pre-processing/78-epi-distortion-correction-echo-spacing-and-bandwidth) and total read-out time for EPI data so they can be undistorted. Specifically, we are interested in FSL's definition of total read-out time, which may differ from the actual read-out time. FSL expects “the time from the middle of the first echo to the middle of the last echo, as it would have been had partial k-space not been used”. So total read-out time is influenced by parallel acceleration factor, bandwidth, number of EPI lines, but not partial Fourier. For GE data we can use the Acquisition Matrix (0018,1310) in the phase-encoding direction, the in-plane acceleration ASSET R factor (the reciprocal of this is stored as the first element of 0043,1083) and the Effective Echo Spacing (0043,102C). Note that the Effective Echo Spacing (0043,102C) in GE DICOMS is defined as the time between two consecutives acquired phase encoding lines divided by the number of shots (usually, this is equal to 1 for fMRI and Diffusion). -While GE does not tell us the [partial Fourier fraction](https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/01-magnetic-resonance-imaging-data.html), is does reveal if it is present with the ScanOptions (0018,1022) reporting [PFF](http://dicomlookup.com/lookup.asp?sw=Ttable&q=C.8-4) (in my experience, GE does not populate [(0018,9081)](http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0018,9081))). While partial Fourier does not impact FSL's totalReadoutTime directly, it can interact with the number of lines acquired when combined with parallel imaging (the `Round_factor` 2 (Full Fourier) or 4 (Partial Fourier)). +One often wants to determine [echo spacing, bandwidth](https://support.brainvoyager.com/brainvoyager/functional-analysis-preparation/29-pre-processing/78-epi-distortion-correction-echo-spacing-and-bandwidth) and total read-out time for EPI data so they can be corrected for susceptibility distortions. Specifically, we are interested in FSL's definition of total read-out time, which may differ from the actual read-out time. FSL expects “the time from the middle of the first echo to the middle of the last echo, as it would have been had partial k-space not been used”. So total read-out time is influenced by parallel acceleration factor, bandwidth, number of EPI lines, but not partial Fourier. For GE data we can use the Acquisition Matrix (0018,1310) in the phase-encoding direction, the in-plane acceleration ASSET R factor (the reciprocal of this is stored as the first element of "0043,1083") and the value of (0043,102C), which is named “Effective Echo Spacing” in GE’s DICOM conformance statements, but which is NOT the same as the desired FSL definition. Specifically, (0043,102C) in GE DICOMS is defined as the time between two consecutively acquired phase encoding lines in microseconds divided by the number of shots (usually, this is equal to 1 for fMRI and Diffusion). Note that this GE DICOM field does not consider the impact of parallel acceleration on its “effective” echo spacing (which is not consistent with the FSL definition). So, to avoid confusion, we will name the value of (0043,102C) `EchoSpacingMicroSecondsGE`. -Let `NotPhysicalNumberOfAcquiredPELinesGE` be the number of acquired phase encoding lines if there was no partial Fourier and `NotPhysicalTotalReadOutTimeGE` be the physical total read-out time if there was no partial Fourier. Please, note that these two intermediate variables do not take partial Fourier into account. These two variables can be computed as +GE DICOMs do not (currently) provide the actual [partial Fourier fraction](https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/01-magnetic-resonance-imaging-data.html), but they do reveal if any amount of partial Fourier was used by reporting [PFF](http://dicomlookup.com/dicomtags/(0018,0022)) in the ScanOptions field (0018,1022). (In my experience, GE does not populate [(0018,9081)](http://dicomlookup.com/dicomtags/(0018,9081))). While partial Fourier does not impact FSL's TotalReadoutTime directly, it can interact with the number of lines acquired when combined with parallel imaging (via the `Round_factor` in the equations below, which is 2 for Full Fourier but 4 for Partial Fourier). + +Let `NotPhysicalNumberOfAcquiredPELinesGE` be the number of acquired phase encoding lines if there was no partial Fourier (but reflecting the impact of parallel acceleration on the number of *acquired* PE lines) and `NotPhysicalTotalReadOutTimeGE` be the associated read-out time if there was no partial Fourier. These two intermediate variables can be computed as ``` -NotPhysicalNumberOfAcquiredPELinesGE = (ceil((1/Round_factor) * PE_AcquisitionMatrix / Asset_R_factor) * Round_factor) -NotPhysicalTotalReadOutTimeGE = (NotPhysicalNumberOfAcquiredPELinesGE - 1) * EchoSpacing * 0.000001 +NotPhysicalNumberOfAcquiredPELinesGE = (ceil((1/Round_factor) * AcquisitionMatrixPE / ASSET_R_factor) * Round_factor) +NotPhysicalTotalReadOutTimeGE = (NotPhysicalNumberOfAcquiredPELinesGE - 1) * EchoSpacingMicroSecondsGE * 1e-6 ``` - -with `EchoSpacing` referring to the GE private DICOM field "(0043,102C) Effective Echo Spacing". + Then, the formula for FSL's definition of `EffectiveEchoSpacing` and `TotalReadoutTime` (in seconds) are: ``` -EffectiveEchoSpacing = NotPhysicalTotalReadOutTimeGE / (PE_AcquisitionMatrix - 1) +EffectiveEchoSpacing = NotPhysicalTotalReadOutTimeGE / (AcquisitionMatrixPE - 1) TotalReadoutTime = EffectiveEchoSpacing * (ReconMatrixPE - 1) ``` -When there is no image interpolation (i.e. `ReconMatrixPE = PE_AcquisitionMatrix`) the `TotalReadoutTime` has the same value as `NotPhysicalTotalReadOutTimeGE`. In other words, `NotPhysicalTotalReadOutTimeGE` is the `TotalReadoutTime` without any image interpolation. + +When there is no image interpolation or phase oversampling (i.e., `ReconMatrixPE = AcquisitionMatrixPE`) `TotalReadoutTime` has the same value as `NotPhysicalTotalReadOutTimeGE`. Consider an example: @@ -78,12 +79,20 @@ Consider an example: (0043,1083) DS [0.666667\1] # 10, 2 Acceleration ``` -From this we can derive: +From this, and knowing from the image data that the reconstructed volume was 128 voxels in the phase dimension (i.e., `ReconMatrixPE` = 128), we can derive: ``` -ASSET= 1.5 PE_AcquisitionMatrix= 128 EchoSpacing= 636 Round_Factor= 4 TotalReadoutTime= 0.055332 +ASSET_R_factor = 1.5 (reciprocal of 1st value in "0043,1083") +AcquisitionMatrixPE = 128 (3rd or 4th value in "0018,1310" (whichever is non-zero)) +EchoSpacingMicroSecondsGE = 636 (us) +Round_factor = 4 (because "0018,0022" contains "PFF"; otherwise = 2) +NotPhysicalNumberOfAcquiredPELinesGE = (ceil((1/4) * 128 / 1.5) * 4) = 88 +NotPhysicalTotalReadOutTimeGE = (88 - 1) * 636 * 1e-6 = 0.055332 (sec) +EffectiveEchoSpacing = 0.055332 / (128 - 1) = 0.435685e-3 (sec) +TotalReadoutTime = 0.435685e-3 * (128 - 1) = 0.055332 (sec) ``` -For debugging purposes, the intermediate variables `NotPhysicalTotalReadOutTimeGE`, `NotPhysicalNumberOfAcquiredPELinesGE` and `EchoSpacing` (renamed `EchoSpacingMicroSecondsGE`) are written to the BIDS-sidecar JSON file when dcm2niix is compiled with the flag `MY_DEBUG`. + +For debugging purposes, the intermediate variables `NotPhysicalTotalReadOutTimeGE`, `NotPhysicalNumberOfAcquiredPELinesGE` and `EchoSpacingMicroSecondsGE` are written to the BIDS-sidecar JSON file when dcm2niix is compiled with the flag `MY_DEBUG`. ## Image Acceleration @@ -118,6 +127,7 @@ Anatomical localizers (e.g. scout images) are quick-and-dirty scans used to posi ## Sample Datasets - [A validation dataset for dcm2niix commits](https://github.com/neurolabusc/dcm_qa_nih). + - [Slice Timing and Phase Encoding examples](https://github.com/jannikadon/cc-dcm2bids-wrapper/tree/main/dicom-qa-examples) - [Slice timing validation](https://github.com/neurolabusc/dcm_qa_stc) for different varieties of GE EPI sequences. - [Examples of phase encoding polarity, slice timing and diffusion gradients](https://github.com/neurolabusc/dcm_qa_ge). - The dcm2niix [wiki](https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage) includes examples of diffusion data, slice timing, and other variations. From 64e6b43cf52d72c930d1d1f8ee61a209cb472e9b Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Tue, 20 Aug 2024 14:29:54 -0400 Subject: [PATCH 45/82] WASM build --- console/makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/console/makefile b/console/makefile index 0b236b13..db22282b 100644 --- a/console/makefile +++ b/console/makefile @@ -53,3 +53,8 @@ sanitize: noroi: g++ $(CFLAGS) -I. $(JSFLAGS) $(JFLAGS) $(LFLAGS) $(UFILES) -DmyNoRois + +wasm: +wasm: + emcc -O3 $(UFILES) -s DEMANGLE_SUPPORT=1 -s EXPORTED_RUNTIME_METHODS='["callMain", "ccall", "cwrap", "FS_createDataFile", "FS_readFile", "FS_unlink", "allocateUTF8", "getValue", "stringToUTF8", "setValue"]' -s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -s EXPORT_ES6=1 -s MODULARIZE=1 -s EXPORTED_FUNCTIONS='["_main", "_malloc", "_free"]' -s INVOKE_RUN=0 -o ../js/src/niimath.js + From db97160528535361b295e52de747a5ffccb539d3 Mon Sep 17 00:00:00 2001 From: Taylor Hanayik Date: Mon, 26 Aug 2024 09:41:10 +0100 Subject: [PATCH 46/82] add wasm build for npm package --- .gitignore | 5 + README.md | 6 + console/makefile | 4 +- js/README.md | 100 +++++++++ js/esbuild.config.js | 24 +++ js/index.html | 75 +++++++ js/package-lock.json | 464 ++++++++++++++++++++++++++++++++++++++++ js/package.json | 38 ++++ js/scripts/pre-build.js | 39 ++++ js/src/index.js | 287 +++++++++++++++++++++++++ js/src/worker.js | 159 ++++++++++++++ 11 files changed, 1199 insertions(+), 2 deletions(-) create mode 100644 js/README.md create mode 100644 js/esbuild.config.js create mode 100644 js/index.html create mode 100644 js/package-lock.json create mode 100644 js/package.json create mode 100644 js/scripts/pre-build.js create mode 100644 js/src/index.js create mode 100644 js/src/worker.js diff --git a/.gitignore b/.gitignore index 98ba3f41..9e350170 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,8 @@ __pycache__/ /MANIFEST* /*.egg*/ /dist/ + +dist +node_modules +dcm2niix.js +dcm2niix.wasm diff --git a/README.md b/README.md index 9a15240b..0593e4e9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# dcm2niix + [![Build status](https://ci.appveyor.com/api/projects/status/7o0xp2fgbhadkgn1?svg=true)](https://ci.appveyor.com/project/neurolabusc/dcm2niix) ## About @@ -7,6 +9,10 @@ dcm2niix is designed to convert neuroimaging data from the DICOM format to the N The DICOM format is the standard image format generated by modern medical imaging devices. However, DICOM is very [complicated](https://github.com/jonclayden/divest) and has been interpreted differently by different vendors. The NIfTI format is popular with scientists, it is very simple and explicit. However, this simplicity also imposes limitations (e.g. it demands equidistant slices). dcm2niix is also able to generate a [BIDS JSON format](https://bids-specification.readthedocs.io/en/stable/) `sidecar` which includes relevant information for brain scientists in a vendor agnostic and human readable form. The [Neuroimaging DICOM and NIfTI Primer](https://github.com/DataCurationNetwork/data-primers/blob/master/Neuroimaging%20DICOM%20and%20NIfTI%20Data%20Curation%20Primer/neuroimaging-dicom-and-nifti-data-curation-primer.md) provides details. +## JavaScript/WebAssembly + +To view the WASM specific README, please click [here](./js/README.md). The rest of this README is for the `dcm2niix` CLI program. + ## License This software is open source. The bulk of the code is covered by the BSD license. Some units are either public domain (nifti*.*, miniz.c) or use the MIT license (ujpeg.cpp). See the license.txt file for more details. diff --git a/console/makefile b/console/makefile index db22282b..c156f452 100644 --- a/console/makefile +++ b/console/makefile @@ -55,6 +55,6 @@ noroi: g++ $(CFLAGS) -I. $(JSFLAGS) $(JFLAGS) $(LFLAGS) $(UFILES) -DmyNoRois wasm: -wasm: - emcc -O3 $(UFILES) -s DEMANGLE_SUPPORT=1 -s EXPORTED_RUNTIME_METHODS='["callMain", "ccall", "cwrap", "FS_createDataFile", "FS_readFile", "FS_unlink", "allocateUTF8", "getValue", "stringToUTF8", "setValue"]' -s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -s EXPORT_ES6=1 -s MODULARIZE=1 -s EXPORTED_FUNCTIONS='["_main", "_malloc", "_free"]' -s INVOKE_RUN=0 -o ../js/src/niimath.js + emcc -O3 $(UFILES) -s DEMANGLE_SUPPORT=1 -s EXPORTED_RUNTIME_METHODS='["callMain", "ccall", "cwrap", "FS", "FS_createDataFile", "FS_readFile", "FS_unlink", "allocateUTF8", "getValue", "stringToUTF8", "setValue"]' -s STACK_OVERFLOW_CHECK=2 -s STACK_SIZE=16MB -s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -s EXPORT_ES6=1 -s MODULARIZE=1 -s EXPORTED_FUNCTIONS='["_main", "_malloc", "_free"]' -s FORCE_FILESYSTEM=1 -s INVOKE_RUN=0 -o ../js/src/dcm2niix.js + # STACK_SIZE=16MB is the minimum value found to work with the current codebase when targeting WASM diff --git a/js/README.md b/js/README.md new file mode 100644 index 00000000..6db619ea --- /dev/null +++ b/js/README.md @@ -0,0 +1,100 @@ +# @niivue/dcm2niix + +`@niivue/dcm2niix` is a JavaScript + WASM library for converting DICOM files to nifti. This library is intended to be **used in the browser**, not in a Node.js environment. + +> All operations are performed using the WASM build of [dcm2niix](https://github.com/rordenlab/dcm2niix). The processing takes place in a separate worker thread, so it won't block the main thread in your application. + +## Usage + +The `@niivue/dcm2niix` JavaScript library offers an object oriented API for working with the `dcm2niix` CLI. Since `dcm2niix` is a CLI tool, the API implemented in `@niivue/dcm2niix` is just a wrapper around the CLI options and arguments. + +## Example + +```javascript +// assuming you have an html input element to get directories. +// +import { Dcm2niix } from '@niivue/dcm2niix'; + +const dcm2niix = new Dcm2niix(); +// call the init() method to load the wasm before processing any data +await dcm2niix.init(); +// fileInput is the id of the input element with options: webkitdirectory and multiple +fileInput.addEventListener('change', async (event) => { + inputFileList = event.target.files; +}); +// inputFileList is the value from the input element with options: webkitdirectory and multiple +const resultFileList = await dcm2niix.input(inputFileList).run() +console.log(resultFileList); +// Do something with the resultFileList (normal browser File Objects) +// perhaps view them with @niivue/niivue :) +``` + +## Installation + +To install `@niivue/dcm2niix` in your project, run the following command: + +```bash +npm install @niivue/dcm2niix +``` + +### To install a local build of the library + +Fist, `cd` into the `js` directory of the `dcm2niix` repository. + +```bash +# from dcm2niix root directory +cd js +``` + +To install a local build of the library, run the following command: + +```bash +npm run build +``` + +Then, install the library using the following command: + +```bash +npm pack # will create a .tgz file in the root directory +``` + +Then, install the `@niivue/dcm2niix` library in your application locally using the following command: + +```bash +npm install /path/to/niivue-dcm2niix.tgz +``` + +## Development + +First `cd` into the `js` directory of the `dcm2niix` repository. + +```bash +# from dcm2niix root directory +cd js +``` + +To install the dependencies, run the following command: + +```bash +npm install +``` + +To build the library, run the following command + +```bash +npm run build +``` + +To run the tests, run the following command: + +```bash +npm run test +``` + +### Test using a simple demo + +To test that the `@niivue/dcm2niix` library is working correctly, you can run the following command: + +```bash +npm run demo +``` \ No newline at end of file diff --git a/js/esbuild.config.js b/js/esbuild.config.js new file mode 100644 index 00000000..191a31b6 --- /dev/null +++ b/js/esbuild.config.js @@ -0,0 +1,24 @@ +const esbuild = require('esbuild'); +const fs = require('fs'); + +esbuild.build({ + entryPoints: ['./src/index.js'], + outfile: './dist/index.js', + bundle: true, + format: 'esm', + target: ['es2020'], + minify: false, + define: { + 'process.env.NODE_ENV': '"production"', + }, +}).then(() => { + // copy worker.js, dcm2niix.wasm, dcm2niix.js to dist folder + // (they do not require any processing by esbuild). + // Technically, none of the files in the src folder require processing by esbuild, + // but it does allow minification (optional), and ES version target specification if needed. + // In the future, if we use Typescript, we can use esbuild to transpile the Typescript to JS. + fs.copyFileSync('./src/worker.js', './dist/worker.js'); + fs.copyFileSync('./src/dcm2niix.wasm', './dist/dcm2niix.wasm'); + fs.copyFileSync('./src/dcm2niix.js', './dist/dcm2niix.js'); + console.log('Build completed!'); +}).catch(() => process.exit(1)); \ No newline at end of file diff --git a/js/index.html b/js/index.html new file mode 100644 index 00000000..8ee503b4 --- /dev/null +++ b/js/index.html @@ -0,0 +1,75 @@ + + + + + + + dcm2niix WASM Demo + + + +

dcm2niix WASM Demo

+ + +

Please select a dicom folder to process.

+ + + + + + \ No newline at end of file diff --git a/js/package-lock.json b/js/package-lock.json new file mode 100644 index 00000000..95bf52ef --- /dev/null +++ b/js/package-lock.json @@ -0,0 +1,464 @@ +{ + "name": "@niivue/dcm2niix", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@niivue/dcm2niix", + "version": "0.1.0", + "license": "BSD-2-Clause", + "devDependencies": { + "esbuild": "^0.23.1" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + } + } +} diff --git a/js/package.json b/js/package.json new file mode 100644 index 00000000..635d37b6 --- /dev/null +++ b/js/package.json @@ -0,0 +1,38 @@ +{ + "name": "@niivue/dcm2niix", + "version": "0.1.0", + "main": "dist/index.js", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js" + } + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "node esbuild.config.js", + "makeWasm": "make wasm -C ../console", + "prebuild": "npm run makeWasm && node scripts/pre-build.js -i src/dcm2niix.js -o src/dcm2niix.js", + "demo": "npm run build && npx http-server .", + "pub": "npm run build && npm publish --access public" + }, + "keywords": [ + "niivue", + "dcm2niix", + "nifti", + "dicom", + "medical", + "imaging", + "conversion", + "wasm" + ], + "author": "dcm2niix developers", + "license": "BSD-2-Clause", + "description": "A javascript library to easily use the WASM build of Chris Rorden's dcm2niix command line program but in the browser.", + "files": [ + "dist/" + ], + "devDependencies": { + "esbuild": "^0.23.1" + } +} diff --git a/js/scripts/pre-build.js b/js/scripts/pre-build.js new file mode 100644 index 00000000..d4729a27 --- /dev/null +++ b/js/scripts/pre-build.js @@ -0,0 +1,39 @@ +const fs = require('fs'); + +function getArgs() { + const args = {}; + process.argv.slice(2).forEach((arg, index, array) => { + if (arg.startsWith('-')) { + args[arg] = array[index + 1]; + } + }); + return args; +} + +const args = getArgs(); + +const inputFilePath = args['-i']; +const outputFilePath = args['-o']; + +if (!inputFilePath || !outputFilePath) { + console.error('Please provide both input and output file paths using -i and -o flags.'); + process.exit(1); +} + +fs.readFile(inputFilePath, 'utf8', (err, data) => { + if (err) { + console.error('Error reading the input file:', err); + return; + } + + // Replace all occurrences of "args=[]" with "args" + const modifiedData = data.replace(/args=\[\]/g, 'args'); + + fs.writeFile(outputFilePath, modifiedData, 'utf8', (err) => { + if (err) { + console.error('Error writing to the output file:', err); + return; + } + console.log('File successfully updated and saved to', outputFilePath); + }); +}); \ No newline at end of file diff --git a/js/src/index.js b/js/src/index.js new file mode 100644 index 00000000..f35781af --- /dev/null +++ b/js/src/index.js @@ -0,0 +1,287 @@ +export class Dcm2niix { + constructor() { + this.worker = null; + } + + init() { + this.worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' }); + return new Promise((resolve, reject) => { + // handle worker ready message. + // This gets reassigned in the run() method, + // but we need to handle the ready message before that. + // Maybe there is a less hacky way to do this? + this.worker.onmessage = (event) => { + if (event.data && event.data.type === 'ready') { + resolve(true); // Resolve the promise when the worker is ready + } + } + + // Handle worker init errors. + this.worker.onerror = (error) => { + reject(new Error(`Worker failed to load: ${error.message}`)); + } + }); + } + + input(fileList) { + return new Processor({ worker: this.worker, fileList}); + } +} + +class Processor { + + constructor({ worker, fileList }) { + this.worker = worker; + this.fileList = fileList; + this.commands = []; // default to verbose output for testing + } + + _addCommand(cmd, ...args) { + this.commands.push(cmd, ...args.map(String)); + return this; + } + + // --version + version() { + return this._addCommand('--version'); + } + + // -1..-9 gz compression level (1=fastest..9=smallest, default 6) + compressionLevel(level) { + return this._addCommand(`-${level}`); + } + + // -a adjacent DICOMs (images from same series always in same folder) for faster conversion (n/y, default n) + a(value){ + return this._addCommand('-a', value); + } + // alias for -a + adjacent(value){ + return this.a(value); + } + + // -b BIDS sidecar (y/n/o [o=only: no NIfTI], default y) + b(value){ + return this._addCommand('-b', value); + } + // alias for -b + bids(value){ + return this.b(value); + } + + // -ba BIDS anonymize (y/n, default y) + ba(value){ + return this._addCommand('-ba', value); + } + // alias for -ba + bidsAnonymize(value){ + return this.ba(value); + } + + // -c comment stored as NIfTI aux_file (up to 24 characters) + c(value){ + return this._addCommand('-c', value); + } + // alias for -c + comment(value){ + return this.c(value); + } + + // -d directory search depth (0..9, default 5) + // Note: not used in browser/wasm since file list is a flat list + d(value){ + return this._addCommand('-d', value); + } + // alias for -d + directorySearchDepth(value){ + return this.d(value); + } + + // export as NRRD (y) or MGH (o) or JSON/JNIfTI (j) or BJNIfTI (b) instead of NIfTI (y/n/o/j/b, default n) + e(value){ + return this._addCommand('-e', value); + } + // alias for -e + exportFormat(value){ + return this.e(value); + } + + // -f : filename (%a=antenna (coil) name, %b=basename, %c=comments, %d=description, + // %e=echo number, %f=folder name, %g=accession number, %i=ID of patient, %j=seriesInstanceUID, + // %k=studyInstanceUID, %m=manufacturer, %n=name of patient, %o=mediaObjectInstanceUID, + // %p=protocol, %r=instance number, %s=series number, %t=time, %u=acquisition number, + // %v=vendor, %x=study ID; %z=sequence name; + // + // default '%f_%p_%t_%s') + f(value){ + return this._addCommand('-f', value); + } + // alias for -f + filenameformat(value){ + return this.f(value); + } + + // -i : ignore derived, localizer and 2D images (y/n, default n) + i(value){ + return this._addCommand('-i', value); + } + // alias for -i + ignoreDerived(value){ + return this.i(value); + } + + // -l : losslessly scale 16-bit integers to use dynamic range (y/n/o [yes=scale, no=no, but uint16->int16, o=original], default o) + l(value){ + return this._addCommand('-l', value); + } + // alias for -l + losslessScale(value){ + return this.l(value); + } + + // -m : merge 2D slices from same series regardless of echo, exposure, etc. (n/y or 0/1/2, default 2) [no, yes, auto] + m(value){ + return this._addCommand('-m', value); + } + // alias for -m + merge2DSlices(value){ + return this.m(value); + } + + // -n : only convert this series CRC number - can be used up to 16 times (default convert all) + n(value){ + return this._addCommand('-n', value); + } + // alias for -n + seriesCRC(value){ + return this.n(value); + } + + // -o : output directory (omit to save to input folder) + // o(value){ + // return this._addCommand('-o', value); + // } + // alias for -o + // outputDirectory(value){ + // return this.o(value); + // } + + // -p : Philips precise float (not display) scaling (y/n, default y) + p(value){ + return this._addCommand('-p', value); + } + // alias for -p + philipsPreciseFloat(value){ + return this.p(value); + } + + // -q : only search directory for DICOMs (y/l/n, default y) [y=show number of DICOMs found, l=additionally list DICOMs found, n=no] + q(value){ + return this._addCommand('-q', value); + } + // alias for -q + searchDirectory(value){ + return this.q(value); + } + + // -r : rename instead of convert DICOMs (y/n, default n) + r(value){ + return this._addCommand('-r', value); + } + // alias for -r + renameOnly(value){ + return this.r(value); + } + + // -s : single file mode, do not convert other images in folder (y/n, default n) + s(value){ + return this._addCommand('-s', value); + } + // alias for -s + singleFileMode(value){ + return this.s(value); + } + + // -v : verbose (n/y or 0/1/2, default 0) [no, yes, logorrheic] + v(value){ + return this._addCommand('-v', value); + } + // alias for -v + verbose(value){ + return this.v(value); + } + + // -w : write behavior for name conflicts (0,1,2, default 2: 0=skip duplicates, 1=overwrite, 2=add suffix) + w(value){ + return this._addCommand('-w', value); + } + // alias for -w + writeBehavior(value){ + return this.w(value); + } + + // -x : crop 3D acquisitions (y/n/i, default n, use 'i'gnore to neither crop nor rotate 3D acquisitions) + x(value){ + return this._addCommand('-x', value); + } + // alias for -x + crop(value){ + return this.x(value); + } + + // -z : gz compress images (y/o/i/n/3, default n) [y=pigz, o=optimal pigz, i=internal:miniz, n=no, 3=no,3D] + z(value){ + return this._addCommand('-z', value); + } + // alias for -z + gzip(value){ + return this.z(value); + } + + // --big-endian : byte order (y/n/o, default o) [y=big-end, n=little-end, o=optimal/native] + bigEndian(value){ + return this._addCommand('--big-endian', value); + } + + // --ignore_trigger_times : disregard values in 0018,1060 and 0020,9153 + ignoreTriggerTimes(){ + return this._addCommand('--ignore_trigger_times'); + } + + // --terse : omit filename post-fixes (can cause overwrites) + terse(){ + return this._addCommand('--terse'); + } + + // --xml : Slicer format features + xml(){ + return this._addCommand('--xml'); + } + + async run() { + return new Promise((resolve, reject) => { + this.worker.onmessage = (e) => { + if (e.data.type === 'error') { + reject(new Error(e.data.message)); + } else { + // get the output file and the exit code from niimath wasm + const { convertedFiles, exitCode } = e.data; + // --version gives exit code 3 in dcm2niix CLI and wasm + if (exitCode === 0 || exitCode === 3) { + // success + resolve(convertedFiles); + } else { + // error + reject(new Error(`dcm2niix processing failed with exit code ${exitCode}`)); + } + } + }; + + const args = [...this.commands]; + if (this.worker === null) { + reject(new Error('Worker not initialized. Did you await the init() method?')); + } + this.worker.postMessage({ fileList: this.fileList, cmd: args}); + }); + } +} \ No newline at end of file diff --git a/js/src/worker.js b/js/src/worker.js new file mode 100644 index 00000000..6753a0a6 --- /dev/null +++ b/js/src/worker.js @@ -0,0 +1,159 @@ +// Load the Emscripten-generated JavaScript, which will handle the WASM binary loading. +// The worker is of type "module" so that it can use ES6 module syntax. +// Importantly, the emscripten code must be compiled with: -s EXPORT_ES6=1 -s MODULARIZE=1. +// This allows proper module bundlers to import the worker and wasm properly with code splitting. +import Module from './dcm2niix.js'; + +// initialise an instance of the Emscripten Module so that +// it is ready when the worker receives a message. +// We keep a reference to the module so that the worker can reuse it +// for all subsequent calls without having to reinitialise it (which could be slow due to the WASM loading) +let mod = null +Module().then((initializedMod) => { + mod = initializedMod + // Send a ready message once initialization is complete + // so we can signal to the main thread that the worker is ready. + // The Niimath.init() method will wait for this message before resolving the promise. + self.postMessage({ type: 'ready' }); +}) + +// error handler in the worker +self.onerror = (message, error) => { + self.postMessage({ type: 'error', message: message, error: error ? error.stack : null }); +}; +// unhandled promise rejection handler in the worker +self.onunhandledrejection = (event) => { + self.postMessage({ type: 'error', message: event.reason ? event.reason.message : 'Unhandled rejection', error: event.reason ? event.reason.stack : null }); +}; + +// copy the files to the emscripten filesystem +const copyFilesToFS = async (fileList, inDir, outDir) => { + // create a directory for dcm2niix to use as its input + mod.FS.mkdir(inDir); + + // create a directory for dcm2niix to use as its output + mod.FS.mkdir(outDir); + + // an array to hold all the promises for copying files + const promises = []; + for (let file of fileList) { + const promise = new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = (e) => { + try { + const data = new Uint8Array(e.target.result); + // webkitRelativePath has the file directory and filename separated by '/', + // such as 'some_dir/some_file.dcm'. + // We need to replace the '/' with '_' to create a valid name for the WASM filesystem + // since some_dir does not exist at out mount point. That directory stub, + // doesn't provide any useful information to dcm2niix anyway. + const fileName = `${file.webkitRelativePath.split('/').join('_')}`; + const filePath = `${inDir}/${fileName}`; + mod.FS.createDataFile(inDir, fileName, data, true, true); + resolve(); // Resolve the promise when the file is successfully written + } catch (error) { + console.error(error); + reject(error); // Reject the promise if there's an error + } + }; + reader.onerror = () => { + console.error(reader.error); + reject(reader.error); // Reject the promise if there's an error reading the file + }; + reader.readAsArrayBuffer(file); + }); + + promises.push(promise); + } + + // return a promise that resolves when all files are written + return Promise.all(promises); +} + +const typeFromExtension = (fileName) => { + const ext = fileName.split('.').pop(); + switch (ext) { + case 'nii': + return 'application/sla'; + case 'json': + return 'application/json'; + case 'txt': + return 'text/plain'; + case 'gz': + return 'application/gzip'; + case 'bvec': + return 'text/plain'; + case 'bval': + return 'text/plain'; + case 'nrrd': + return 'application/octet-stream'; + default: + return 'application/octet-stream'; + } +} + +const handleMessage = async (e) => { + try { + // name the input and output directories that will get created. + const inDir = '/input'; + const outDir = '/output'; + const fileList = e.data.fileList; + const args = e.data.cmd; + // always put ['-o', outDir] at the beginning of the args array. + // The user does not need to specify the output directory since it + // will be a temporary directory that gets created by the worker in the emscripten filesystem. + args.unshift('-o', outDir); + + if (!fileList || args.length < 1) { + throw new Error("Expected a flat file list and at least one command"); + } + + if (!Array.isArray(args)) { + throw new Error("Expected args to be an array"); + } + + if (!mod) { + throw new Error("WASM module not loaded yet!"); + } + + + // copy the files to the emscripten filesystem + await copyFilesToFS(fileList, inDir, outDir); + + // then add the input directory at the end of the args array + args.push(inDir); + // call the main function of the WASM module with the args + const exitCode = mod.callMain(args); + + // read all files from outDir and return them + const files = mod.FS.readdir(outDir); + // filter out any file from the files array that starts + // with a dot. FS.readdir returns '.' and '..' which is not useful. + const filteredFiles = files.filter(file => !file.startsWith('.')); + + const convertedFiles = []; + for (let file of filteredFiles) { + const filePath = outDir + '/' + file; + // const blob = new Blob([mod.FS.readFile(filePath)], { type: 'application/sla' }); + // make a file Object from the return value of readFile + const fileData = mod.FS.readFile(filePath); + const f = new File([fileData], file, { type: typeFromExtension(file) }); + convertedFiles.push(f); + } + + // send a message back to the main thread with the output file, exit code and output file name + self.postMessage({ convertedFiles: convertedFiles, exitCode: exitCode }); + + // --------- only for version test + // const exitCode = mod.callMain(args); + // // send a message back to the main thread with the output file, exit code and output file name + // // self.postMessage({ blob: outputFile, outName: outName, exitCode: exitCode }); + // self.postMessage({ blob: "blob", exitCode: exitCode }); + } catch (err) { + // Send error details back to the main thread + self.postMessage({ type: 'error', message: err.message, error: err.stack }); + } +} + +// Handle messages from the main thread +self.addEventListener('message', handleMessage, false); \ No newline at end of file From 404771c37c75bbd64e81bf98d5433644be3b813a Mon Sep 17 00:00:00 2001 From: Taylor Hanayik Date: Wed, 11 Sep 2024 14:07:18 +0100 Subject: [PATCH 47/82] workaround for safari and allow nested directories --- js/src/index.js | 102 ++++++++++++++++++++++++++--------------------- js/src/worker.js | 12 ++++-- 2 files changed, 65 insertions(+), 49 deletions(-) diff --git a/js/src/index.js b/js/src/index.js index f35781af..4b1bc450 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -24,7 +24,7 @@ export class Dcm2niix { } input(fileList) { - return new Processor({ worker: this.worker, fileList}); + return new Processor({ worker: this.worker, fileList }); } } @@ -52,57 +52,57 @@ class Processor { } // -a adjacent DICOMs (images from same series always in same folder) for faster conversion (n/y, default n) - a(value){ + a(value) { return this._addCommand('-a', value); } // alias for -a - adjacent(value){ + adjacent(value) { return this.a(value); } // -b BIDS sidecar (y/n/o [o=only: no NIfTI], default y) - b(value){ + b(value) { return this._addCommand('-b', value); } // alias for -b - bids(value){ + bids(value) { return this.b(value); } // -ba BIDS anonymize (y/n, default y) - ba(value){ + ba(value) { return this._addCommand('-ba', value); } // alias for -ba - bidsAnonymize(value){ + bidsAnonymize(value) { return this.ba(value); } // -c comment stored as NIfTI aux_file (up to 24 characters) - c(value){ + c(value) { return this._addCommand('-c', value); } // alias for -c - comment(value){ + comment(value) { return this.c(value); } // -d directory search depth (0..9, default 5) // Note: not used in browser/wasm since file list is a flat list - d(value){ + d(value) { return this._addCommand('-d', value); } // alias for -d - directorySearchDepth(value){ + directorySearchDepth(value) { return this.d(value); } // export as NRRD (y) or MGH (o) or JSON/JNIfTI (j) or BJNIfTI (b) instead of NIfTI (y/n/o/j/b, default n) - e(value){ + e(value) { return this._addCommand('-e', value); } // alias for -e - exportFormat(value){ + exportFormat(value) { return this.e(value); } @@ -113,47 +113,47 @@ class Processor { // %v=vendor, %x=study ID; %z=sequence name; // // default '%f_%p_%t_%s') - f(value){ + f(value) { return this._addCommand('-f', value); } // alias for -f - filenameformat(value){ + filenameformat(value) { return this.f(value); } // -i : ignore derived, localizer and 2D images (y/n, default n) - i(value){ + i(value) { return this._addCommand('-i', value); } // alias for -i - ignoreDerived(value){ + ignoreDerived(value) { return this.i(value); } // -l : losslessly scale 16-bit integers to use dynamic range (y/n/o [yes=scale, no=no, but uint16->int16, o=original], default o) - l(value){ + l(value) { return this._addCommand('-l', value); } // alias for -l - losslessScale(value){ + losslessScale(value) { return this.l(value); } // -m : merge 2D slices from same series regardless of echo, exposure, etc. (n/y or 0/1/2, default 2) [no, yes, auto] - m(value){ + m(value) { return this._addCommand('-m', value); } // alias for -m - merge2DSlices(value){ + merge2DSlices(value) { return this.m(value); } // -n : only convert this series CRC number - can be used up to 16 times (default convert all) - n(value){ + n(value) { return this._addCommand('-n', value); } // alias for -n - seriesCRC(value){ + seriesCRC(value) { return this.n(value); } @@ -165,96 +165,96 @@ class Processor { // outputDirectory(value){ // return this.o(value); // } - + // -p : Philips precise float (not display) scaling (y/n, default y) - p(value){ + p(value) { return this._addCommand('-p', value); } // alias for -p - philipsPreciseFloat(value){ + philipsPreciseFloat(value) { return this.p(value); } // -q : only search directory for DICOMs (y/l/n, default y) [y=show number of DICOMs found, l=additionally list DICOMs found, n=no] - q(value){ + q(value) { return this._addCommand('-q', value); } // alias for -q - searchDirectory(value){ + searchDirectory(value) { return this.q(value); } // -r : rename instead of convert DICOMs (y/n, default n) - r(value){ + r(value) { return this._addCommand('-r', value); } // alias for -r - renameOnly(value){ + renameOnly(value) { return this.r(value); } // -s : single file mode, do not convert other images in folder (y/n, default n) - s(value){ + s(value) { return this._addCommand('-s', value); } // alias for -s - singleFileMode(value){ + singleFileMode(value) { return this.s(value); } // -v : verbose (n/y or 0/1/2, default 0) [no, yes, logorrheic] - v(value){ + v(value) { return this._addCommand('-v', value); } // alias for -v - verbose(value){ + verbose(value) { return this.v(value); } // -w : write behavior for name conflicts (0,1,2, default 2: 0=skip duplicates, 1=overwrite, 2=add suffix) - w(value){ + w(value) { return this._addCommand('-w', value); } // alias for -w - writeBehavior(value){ + writeBehavior(value) { return this.w(value); } // -x : crop 3D acquisitions (y/n/i, default n, use 'i'gnore to neither crop nor rotate 3D acquisitions) - x(value){ + x(value) { return this._addCommand('-x', value); } // alias for -x - crop(value){ + crop(value) { return this.x(value); } // -z : gz compress images (y/o/i/n/3, default n) [y=pigz, o=optimal pigz, i=internal:miniz, n=no, 3=no,3D] - z(value){ + z(value) { return this._addCommand('-z', value); } // alias for -z - gzip(value){ + gzip(value) { return this.z(value); } // --big-endian : byte order (y/n/o, default o) [y=big-end, n=little-end, o=optimal/native] - bigEndian(value){ + bigEndian(value) { return this._addCommand('--big-endian', value); } // --ignore_trigger_times : disregard values in 0018,1060 and 0020,9153 - ignoreTriggerTimes(){ + ignoreTriggerTimes() { return this._addCommand('--ignore_trigger_times'); } // --terse : omit filename post-fixes (can cause overwrites) - terse(){ + terse() { return this._addCommand('--terse'); } // --xml : Slicer format features - xml(){ + xml() { return this._addCommand('--xml'); } @@ -281,7 +281,19 @@ class Processor { if (this.worker === null) { reject(new Error('Worker not initialized. Did you await the init() method?')); } - this.worker.postMessage({ fileList: this.fileList, cmd: args}); + // prepare files with their relative paths. + // annoyingly, safari strips the webkitRelativePath property when sending files to the worker. + // fileList is a FileList object, not an array, so we need to convert it to an array. + // filesWithRelativePaths is an array of objects with the file and webkitRelativePath properties. + // Now we can use the webkitRelativePath property in the worker. + // This is important for dcm2niix to work with nested dicom directories in safari. + const filesWithRelativePaths = Array.from(this.fileList).map((file) => ({ + file, + webkitRelativePath: file.webkitRelativePath || '' + })); + + // send files and commands to the worker + this.worker.postMessage({ fileList: filesWithRelativePaths, cmd: args }); }); } } \ No newline at end of file diff --git a/js/src/worker.js b/js/src/worker.js index 6753a0a6..02e835b4 100644 --- a/js/src/worker.js +++ b/js/src/worker.js @@ -36,7 +36,11 @@ const copyFilesToFS = async (fileList, inDir, outDir) => { // an array to hold all the promises for copying files const promises = []; - for (let file of fileList) { + for (let fileItem of fileList) { + const file = fileItem.file; + // Note: Safari strips webkitRelativePath in the worker, + // so we use the name property of the file object instead. + const webkitRelativePath = fileItem.webkitRelativePath || file.name; const promise = new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => { @@ -46,9 +50,8 @@ const copyFilesToFS = async (fileList, inDir, outDir) => { // such as 'some_dir/some_file.dcm'. // We need to replace the '/' with '_' to create a valid name for the WASM filesystem // since some_dir does not exist at out mount point. That directory stub, - // doesn't provide any useful information to dcm2niix anyway. - const fileName = `${file.webkitRelativePath.split('/').join('_')}`; - const filePath = `${inDir}/${fileName}`; + // doesn't provide any useful information to dcm2niix anyway. + const fileName = `${webkitRelativePath.split('/').join('_')}`; mod.FS.createDataFile(inDir, fileName, data, true, true); resolve(); // Resolve the promise when the file is successfully written } catch (error) { @@ -68,6 +71,7 @@ const copyFilesToFS = async (fileList, inDir, outDir) => { // return a promise that resolves when all files are written return Promise.all(promises); + } const typeFromExtension = (fileName) => { From c07f3251fb7b9147c39a541faf1d1385ea001803 Mon Sep 17 00:00:00 2001 From: Taylor Hanayik Date: Wed, 11 Sep 2024 14:23:01 +0100 Subject: [PATCH 48/82] bump version for npm release --- js/package-lock.json | 4 ++-- js/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/package-lock.json b/js/package-lock.json index 95bf52ef..42fd6f0a 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -1,12 +1,12 @@ { "name": "@niivue/dcm2niix", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@niivue/dcm2niix", - "version": "0.1.0", + "version": "0.1.1", "license": "BSD-2-Clause", "devDependencies": { "esbuild": "^0.23.1" diff --git a/js/package.json b/js/package.json index 635d37b6..7999a06c 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "@niivue/dcm2niix", - "version": "0.1.0", + "version": "0.1.1", "main": "dist/index.js", "module": "dist/index.js", "exports": { From 4aca344350755e2c9d802e749c71c06f315ad85b Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Sat, 14 Sep 2024 10:01:13 -0400 Subject: [PATCH 49/82] Make PR813 compilation conditional (https://github.com/rordenlab/dcm2niix/issues/861) --- console/dcm2niix_fswrapper.cpp | 3 ++- console/nii_dicom.cpp | 33 ++++++++++++++++++++++++--------- console/nii_dicom.h | 17 ++++++++++++----- console/nii_dicom_batch.cpp | 2 ++ 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/console/dcm2niix_fswrapper.cpp b/console/dcm2niix_fswrapper.cpp index 8c5008b1..21b8baa2 100644 --- a/console/dcm2niix_fswrapper.cpp +++ b/console/dcm2niix_fswrapper.cpp @@ -477,7 +477,8 @@ void dcm2niix_fswrapper::seriesInfoDump(FILE *fpdump, const MRIFSSTRUCT *pmrifsS // dcm2niix doesn't seem to retrieve this 0x51, 0x1016 //fprintf(fpdump, "SiemensCrit %s\n",e->d.string); +#ifdef myDeidentificationMethod // kDeidentificationMethod 0x0012 + (0x0063 << 16) // '0012' '0063' 'LO' 'DeidentificationMethod' fprintf(fpdump, "DeidentificationMethod %s\n", tdicomData->deidentificationMethod); - +#endif // myDeidentificationMethod } diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index b34eda4c..0b4d8d27 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -4391,11 +4391,13 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D #define kPatientWeight 0x0010 + (0x1030 << 16) #define kAnatomicalOrientationType 0x0010 + (0x2210 << 16) #define kDeidentificationMethod 0x0012 + (0x0063 << 16) //[DICOMANON, issue 383 -#define kDeidentificationMethodCodeSequence 0x0012 + (0x0064 << 16) -#define kCodeValue 0x0008 + (0x0100 << 16) -#define kCodingSchemeDesignator 0x0008 + (0x0102 << 16) -#define kCodingSchemeVersion 0x0008 + (0x0103 << 16) -#define kCodeMeaning 0x0008 + (0x0104 << 16) +#ifdef myDeidentificationMethod + #define kDeidentificationMethodCodeSequence 0x0012 + (0x0064 << 16) + #define kCodeValue 0x0008 + (0x0100 << 16) + #define kCodingSchemeDesignator 0x0008 + (0x0102 << 16) + #define kCodingSchemeVersion 0x0008 + (0x0103 << 16) + #define kCodeMeaning 0x0008 + (0x0104 << 16) +#endif #define kBodyPartExamined 0x0018 + (0x0015 << 16) #define kBodyPartExamined 0x0018 + (0x0015 << 16) #define kScanningSequence 0x0018 + (0x0020 << 16) @@ -4796,7 +4798,9 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD bool isPaletteColor = false; bool isInterpolated = false; bool isIconImageSequence = false; - bool isDeidentificationMethodCodeSequence = false; + #ifdef myDeidentificationMethod + bool isDeidentificationMethodCodeSequence = false; + #endif int sqDepthIcon = -1; bool isSwitchToImplicitVR = false; bool isSwitchToBigEndian = false; @@ -5317,8 +5321,10 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD //return d; } if (lLength > 0) //issue695: skip empty tags, "gdcmanon --dumb --empty 0018,0089 good.dcm bad.dcm" + #ifdef myDeidentificationMethod if(sqDepth < 1 && isDeidentificationMethodCodeSequence && groupElement != kItemDelimitationTag && groupElement != kItemTag ) isDeidentificationMethodCodeSequence = false; + #endif // myDeidentificationMethod switch (groupElement) { case kMediaStorageSOPClassUID: { char mediaUID[kDICOMStr]; @@ -5678,10 +5684,18 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD //printError("Anatomical Orientation Type (0010,2210) is QUADRUPED: rotate coordinates accordingly\n"); break; } + #ifdef myDeidentificationMethod case kDeidentificationMethod: { //issue 383 - dcmStr(lLength, &buffer[lPos], d.deidentificationMethod); - int slen = (int)strlen(d.deidentificationMethod); - if ((slen < 10) || (strstr(d.deidentificationMethod, "DICOMANON") == NULL)) + #ifdef myDeidentificationMethod + dcmStr(lLength, &buffer[lPos], d.deidentificationMethod); + int slen = (int)strlen(d.deidentificationMethod); + if ((slen < 10) || (strstr(d.deidentificationMethod, "DICOMANON") == NULL)) + #else + char anonTxt[kDICOMStr]; + dcmStr(lLength, &buffer[lPos], anonTxt); + int slen = (int)strlen(anonTxt); + if ((slen < 10) || (strstr(anonTxt, "DICOMANON") == NULL)) + #endif // break; isDICOMANON = true; printWarning("Matlab DICOMANON can scramble SeriesInstanceUID (0020,000e) and remove crucial data (see issue 383). \n"); @@ -5714,6 +5728,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } break; } + #endif // myDeidentificationMethod case kPatientID: if (strlen(d.patientID) > 1) break; diff --git a/console/nii_dicom.h b/console/nii_dicom.h index 24915813..a3725967 100644 --- a/console/nii_dicom.h +++ b/console/nii_dicom.h @@ -184,9 +184,12 @@ static const int kMaxOverlay = 16; //even group values 0x6000..0x601E // Maximum number of dimensions for .dimensionIndexValues, i.e. possibly the // number of axes in the output .nii. static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8; -// Maximum supported number of entries in DeidentificationMethodCodeSequence -// Any additional will be ignored -static const uint8_t MAX_DEID_CS = 10; + +#ifdef myDeidentificationMethod + // Maximum supported number of entries in DeidentificationMethodCodeSequence + // Any additional will be ignored + static const uint8_t MAX_DEID_CS = 10; +#endif // myDeidentificationMethod struct TDTI { float V[4]; //int totalSlicesIn4DOrder; @@ -247,7 +250,6 @@ static const uint8_t MAX_DEID_CS = 10; struct TDICOMdata { long seriesNum; int xyzDim[5]; - int deID_CS_n = 0; uint32_t coilCrc, seriesUidCrc, instanceUidCrc; int overlayStart[kMaxOverlay]; int postLabelDelay, shimGradientX, shimGradientY, shimGradientZ, phaseNumber, spoiling, mtState, partialFourierDirection, interp3D, aslFlags, durationLabelPulseGE, epiVersionGE, internalepiVersionGE, maxEchoNumGE, rawDataRunNumber, numberOfTR, numberOfImagesInGridUIH, numberOfDiffusionT2GE, numberOfDiffusionDirectionGE, tensorFileGE, diffCyclingModeGE, phaseEncodingGE, protocolBlockStartGE, protocolBlockLengthGE, modality, dwellTime, effectiveEchoSpacingGE, phaseEncodingLines, phaseEncodingSteps, frequencyEncodingSteps, phaseEncodingStepsOutOfPlane, echoTrainLength, echoNum, sliceOrient, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,locationsInAcquisition, locationsInAcquisitionConflict, compressionScheme; @@ -258,14 +260,19 @@ static const uint8_t MAX_DEID_CS = 10; float pixelPaddingValue; // used for both FloatPixelPaddingValue (0028, 0122) and PixelPaddingValue (0028, 0120); NaN if not present. double imagingFrequency, acquisitionDuration, triggerDelayTime, RWVScale, RWVIntercept, dateTime, acquisitionTime, acquisitionDate, bandwidthPerPixelPhaseEncode; char parallelAcquisitionTechnique[kDICOMStr], radiopharmaceutical[kDICOMStr], convolutionKernel[kDICOMStr], unitsPT[kDICOMStr], tracerRadionuclide[kDICOMStr], decayCorrection[kDICOMStr], attenuationCorrectionMethod[kDICOMStr],reconstructionMethod[kDICOMStr], transferSyntax[kDICOMStr]; - char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], studyDescription[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr], deidentificationMethod[kDICOMStr]; + char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], studyDescription[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr]; char deepLearningText[kDICOMStrLarge], scanOptions[kDICOMStrLarge], institutionAddress[kDICOMStrLarge], imageComments[kDICOMStrLarge]; uint32_t dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS]; struct TCSAdata CSA; bool isDeepLearning, 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; +#ifdef myDeidentificationMethod + int deID_CS_n = 0; + char deidentificationMethod[kDICOMStr]; struct TDeIDCodeSequence deID_CS[MAX_DEID_CS]; +#endif }; + struct TDCMprefs { int isVerbose, compressFlag, isIgnoreTriggerTimes; }; diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 828f03f3..63970edf 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1465,6 +1465,7 @@ tse3d: T2*/ fprintf(fp, "\t\"NonlinearGradientCorrection\": false,\n"); if (d.isDerived) //DICOM is derived image or non-spatial file (sounds, etc) fprintf(fp, "\t\"RawImage\": false,\n"); +#ifdef myDeidentificationMethod json_Str(fp, "\t\"DeidentificationMethod\": \"%s\",\n", d.deidentificationMethod); if(d.deID_CS_n>0) { @@ -1483,6 +1484,7 @@ if(d.deID_CS_n>0) } fprintf(fp, "\t],\n"); } +#endif // myDeidentificationMethod if (d.seriesNum > 0) fprintf(fp, "\t\"SeriesNumber\": %ld,\n", d.seriesNum); //Chris Gorgolewski: BIDS standard specifies ISO8601 date-time format (Example: 2016-07-06T12:49:15.679688) From 4d4d23fc75ec0b466ceb0fd964f775bb93478ccc Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Sat, 14 Sep 2024 10:05:24 -0400 Subject: [PATCH 50/82] clang-format --- CONTRIBUTE.md | 2 +- console/base64.cpp | 54 +- console/base64.h | 8 +- console/cJSON.cpp | 4673 +++++++++++++++---------------- console/cJSON.h | 297 +- console/dcm2niix_fswrapper.cpp | 584 ++-- console/dcm2niix_fswrapper.h | 57 +- console/jpg_0XC3.cpp | 331 +-- console/jpg_0XC3.h | 24 +- console/main_console.cpp | 185 +- console/main_console_batch.cpp | 164 +- console/nifti1.h | 1426 +++++----- console/nifti1_io_core.cpp | 1542 ++++++----- console/nifti1_io_core.h | 117 +- console/nii_dicom.cpp | 3868 +++++++++++++------------- console/nii_dicom.h | 422 ++- console/nii_dicom_batch.cpp | 4717 ++++++++++++++++---------------- console/nii_dicom_batch.h | 105 +- console/nii_foreign.cpp | 76 +- console/nii_foreign.h | 12 +- console/nii_ortho.cpp | 116 +- console/nii_ortho.h | 10 +- console/print.h | 140 +- console/tinydir.h | 148 +- console/ujpeg.cpp | 1282 +++++---- console/ujpeg.h | 20 +- 26 files changed, 10179 insertions(+), 10201 deletions(-) diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md index 6d84a0a6..114c4e94 100644 --- a/CONTRIBUTE.md +++ b/CONTRIBUTE.md @@ -6,7 +6,7 @@ Like the [Brain Imaging Data Structure](https://bids.neuroimaging.io/get_involve The easiest way to contribute to dcm2niix is to ask questions you have by [generating Github issues](https://github.com/rordenlab/dcm2niix/issues) or [asking a question on the NITRC forum](https://www.nitrc.org/forum/?group_id=880). -The code is open source, and you can share your improvements by [creating a pull request](https://github.com/rordenlab/dcm2niix/pulls). +The code is open source, and you can share your improvements by [creating a pull request](https://github.com/rordenlab/dcm2niix/pulls) to the **development** branch. Please note that the **master** branch is always the current stable release and does not accept pull requests. dcm2niix is a community project that has benefitted from many [contributors](https://github.com/rordenlab/dcm2niix/graphs/contributors). The INCF suggests indicating who is responsible for maintaining software for [stability and support](https://incf.org/incf-standards-review-criteria-v20). Therefore, below we indicate several active contributors and their primary domain of expertise. However, this list is not comprehensive, and it is noted that the project has been supported by contributions from many users. This list does not reflect magnitude of prior contributions, rather it is a non-exhaustive list of members who are actively maintaining the project. diff --git a/console/base64.cpp b/console/base64.cpp index a645b77c..3d3a9ec3 100644 --- a/console/base64.cpp +++ b/console/base64.cpp @@ -6,14 +6,14 @@ * See README for more details. */ -//#include "includes.h" +// #include "includes.h" -//#include "os.h" +// #include "os.h" #include "base64.h" -#include #include -#include +#include #include +#include static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -30,22 +30,21 @@ static const unsigned char base64_table[65] = * nul terminated to make it easier to use as a C string. The nul terminator is * not included in out_len. */ -unsigned char * base64_encode(const unsigned char *src, size_t len, - size_t *out_len) -{ +unsigned char *base64_encode(const unsigned char *src, size_t len, + size_t *out_len) { unsigned char *out, *pos; const unsigned char *end, *in; size_t olen; - + olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ - #ifdef USE_EOLN +#ifdef USE_EOLN int line_len 0; olen += olen / 72; /* line feeds */ - #endif +#endif olen++; /* nul termination */ if (olen < len) - return NULL; /* integer overflow */ - out = (unsigned char *) malloc(olen); //os_ + return NULL; /* integer overflow */ + out = (unsigned char *)malloc(olen); // os_ if (out == NULL) return NULL; end = src + len; @@ -57,13 +56,13 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; *pos++ = base64_table[in[2] & 0x3f]; in += 3; - #ifdef USE_EOLN +#ifdef USE_EOLN line_len += 4; if (line_len >= 72) { *pos++ = '\n'; line_len = 0; } - #endif +#endif } if (end - in) { @@ -73,18 +72,18 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, *pos++ = '='; } else { *pos++ = base64_table[((in[0] & 0x03) << 4) | - (in[1] >> 4)]; + (in[1] >> 4)]; *pos++ = base64_table[(in[1] & 0x0f) << 2]; } *pos++ = '='; - #ifdef USE_EOLN +#ifdef USE_EOLN line_len += 4; - #endif +#endif } - #ifdef USE_EOLN +#ifdef USE_EOLN if (line_len) *pos++ = '\n'; - #endif +#endif *pos = '\0'; if (out_len) *out_len = pos - out; @@ -101,18 +100,17 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, * * Caller is responsible for freeing the returned buffer. */ -unsigned char * base64_decode(const unsigned char *src, size_t len, - size_t *out_len) -{ +unsigned char *base64_decode(const unsigned char *src, size_t len, + size_t *out_len) { unsigned char dtable[256], *out, *pos, block[4], tmp; size_t i, count, olen; int pad = 0; - memset(dtable, 0x80, 256); //os_ + memset(dtable, 0x80, 256); // os_ for (i = 0; i < sizeof(base64_table) - 1; i++) - dtable[base64_table[i]] = (unsigned char) i; - //next line rewritten to avoid warning -Wchar-subscripts - dtable[61] = 0; //dtable['='] = 0; + dtable[base64_table[i]] = (unsigned char)i; + // next line rewritten to avoid warning -Wchar-subscripts + dtable[61] = 0; // dtable['='] = 0; count = 0; for (i = 0; i < len; i++) { @@ -124,7 +122,7 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, return NULL; olen = count / 4 * 3; - pos = out = (unsigned char *) malloc(olen); //os_ + pos = out = (unsigned char *)malloc(olen); // os_ if (out == NULL) return NULL; @@ -150,7 +148,7 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, pos -= 2; else { /* Invalid padding */ - free(out); //os_ + free(out); // os_ return NULL; } break; diff --git a/console/base64.h b/console/base64.h index c05eb158..9caa43ce 100644 --- a/console/base64.h +++ b/console/base64.h @@ -11,14 +11,14 @@ #include -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif -unsigned char * base64_encode(const unsigned char *src, size_t len,size_t *out_len); -unsigned char * base64_decode(const unsigned char *src, size_t len,size_t *out_len); +unsigned char *base64_encode(const unsigned char *src, size_t len, size_t *out_len); +unsigned char *base64_decode(const unsigned char *src, size_t len, size_t *out_len); -#ifdef __cplusplus +#ifdef __cplusplus } #endif diff --git a/console/cJSON.cpp b/console/cJSON.cpp index d694214d..6540f1f7 100644 --- a/console/cJSON.cpp +++ b/console/cJSON.cpp @@ -32,24 +32,24 @@ #pragma GCC visibility push(default) #endif #if defined(_MSC_VER) -#pragma warning (push) +#pragma warning(push) /* disable warning about single line comments in system headers */ -#pragma warning (disable : 4001) +#pragma warning(disable : 4001) #endif -#include -#include +#include +#include #include +#include #include -#include -#include +#include #ifdef ENABLE_LOCALES #include #endif #if defined(_MSC_VER) -#pragma warning (pop) +#pragma warning(pop) #endif #ifdef __GNUC__ #pragma GCC visibility pop @@ -69,81 +69,73 @@ #define false ((cJSON_bool)0) typedef struct { - const unsigned char *json; - size_t position; + const unsigned char *json; + size_t position; } error; -static error global_error = { NULL, 0 }; +static error global_error = {NULL, 0}; -CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) -{ - return (const char*) (global_error.json + global_error.position); +CJSON_PUBLIC(const char *) +cJSON_GetErrorPtr(void) { + return (const char *)(global_error.json + global_error.position); } -CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) { - if (!cJSON_IsString(item)) { - return NULL; - } +CJSON_PUBLIC(char *) +cJSON_GetStringValue(const cJSON *const item) { + if (!cJSON_IsString(item)) { + return NULL; + } - return item->valuestring; + return item->valuestring; } /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 12) - #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif -CJSON_PUBLIC(const char*) cJSON_Version(void) -{ - static char version[15]; - snprintf(version, sizeof(version), "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); +CJSON_PUBLIC(const char *) +cJSON_Version(void) { + static char version[15]; + snprintf(version, sizeof(version), "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); - return version; + return version; } /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ -static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) -{ - if ((string1 == NULL) || (string2 == NULL)) - { - return 1; - } +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) { + if ((string1 == NULL) || (string2 == NULL)) { + return 1; + } - if (string1 == string2) - { - return 0; - } + if (string1 == string2) { + return 0; + } - for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) - { - if (*string1 == '\0') - { - return 0; - } - } + for (; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { + if (*string1 == '\0') { + return 0; + } + } - return tolower(*string1) - tolower(*string2); + return tolower(*string1) - tolower(*string2); } -typedef struct internal_hooks -{ - void *(CJSON_CDECL *allocate)(size_t size); - void (CJSON_CDECL *deallocate)(void *pointer); - void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +typedef struct internal_hooks { + void *(CJSON_CDECL *allocate)(size_t size); + void(CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); } internal_hooks; #if defined(_MSC_VER) /* work around MSVC error C2322: '...' address of dllimport '...' is not static */ -static void * CJSON_CDECL internal_malloc(size_t size) -{ - return malloc(size); +static void *CJSON_CDECL internal_malloc(size_t size) { + return malloc(size); } -static void CJSON_CDECL internal_free(void *pointer) -{ - free(pointer); +static void CJSON_CDECL internal_free(void *pointer) { + free(pointer); } -static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) -{ - return realloc(pointer, size); +static void *CJSON_CDECL internal_realloc(void *pointer, size_t size) { + return realloc(pointer, size); } #else #define internal_malloc malloc @@ -154,114 +146,100 @@ static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) /* strlen of character literals resolved at compile time */ #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) -static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; +static internal_hooks global_hooks = {internal_malloc, internal_free, internal_realloc}; -static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) -{ - size_t length = 0; - unsigned char *copy = NULL; +static unsigned char *cJSON_strdup(const unsigned char *string, const internal_hooks *const hooks) { + size_t length = 0; + unsigned char *copy = NULL; - if (string == NULL) - { - return NULL; - } + if (string == NULL) { + return NULL; + } - length = strlen((const char*)string) + sizeof(""); - copy = (unsigned char*)hooks->allocate(length); - if (copy == NULL) - { - return NULL; - } - memcpy(copy, string, length); + length = strlen((const char *)string) + sizeof(""); + copy = (unsigned char *)hooks->allocate(length); + if (copy == NULL) { + return NULL; + } + memcpy(copy, string, length); - return copy; + return copy; } -CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) -{ - if (hooks == NULL) - { - /* Reset hooks */ - global_hooks.allocate = malloc; - global_hooks.deallocate = free; - global_hooks.reallocate = realloc; - return; - } - - global_hooks.allocate = malloc; - if (hooks->malloc_fn != NULL) - { - global_hooks.allocate = hooks->malloc_fn; - } - - global_hooks.deallocate = free; - if (hooks->free_fn != NULL) - { - global_hooks.deallocate = hooks->free_fn; - } - - /* use realloc only if both free and malloc are used */ - global_hooks.reallocate = NULL; - if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) - { - global_hooks.reallocate = realloc; - } +CJSON_PUBLIC(void) +cJSON_InitHooks(cJSON_Hooks *hooks) { + if (hooks == NULL) { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { + global_hooks.reallocate = realloc; + } } /* Internal constructor. */ -static cJSON *cJSON_New_Item(const internal_hooks * const hooks) -{ - cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); - if (node) - { - memset(node, '\0', sizeof(cJSON)); - } +static cJSON *cJSON_New_Item(const internal_hooks *const hooks) { + cJSON *node = (cJSON *)hooks->allocate(sizeof(cJSON)); + if (node) { + memset(node, '\0', sizeof(cJSON)); + } - return node; + return node; } /* Delete a cJSON structure. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) -{ - cJSON *next = NULL; - while (item != NULL) - { - next = item->next; - if (!(item->type & cJSON_IsReference) && (item->child != NULL)) - { - cJSON_Delete(item->child); - } - if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) - { - global_hooks.deallocate(item->valuestring); - } - if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) - { - global_hooks.deallocate(item->string); - } - global_hooks.deallocate(item); - item = next; - } +CJSON_PUBLIC(void) +cJSON_Delete(cJSON *item) { + cJSON *next = NULL; + while (item != NULL) { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } } /* get the decimal point character of the current locale */ -static unsigned char get_decimal_point(void) -{ +static unsigned char get_decimal_point(void) { #ifdef ENABLE_LOCALES - struct lconv *lconv = localeconv(); - return (unsigned char) lconv->decimal_point[0]; + struct lconv *lconv = localeconv(); + return (unsigned char)lconv->decimal_point[0]; #else - return '.'; + return '.'; #endif } typedef struct { - const unsigned char *content; - size_t length; - size_t offset; - size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ - internal_hooks hooks; + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; } parse_buffer; /* check if the given size is left to read in a given parse buffer (starting with 1) */ @@ -273,2707 +251,2340 @@ typedef struct #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) /* Parse the input text to generate a number, and populate the result into item. */ -static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) -{ - double number = 0; - unsigned char *after_end = NULL; - unsigned char number_c_string[64]; - unsigned char decimal_point = get_decimal_point(); - size_t i = 0; - - if ((input_buffer == NULL) || (input_buffer->content == NULL)) - { - return false; - } - - /* copy the number into a temporary buffer and replace '.' with the decimal point - * of the current locale (for strtod) - * This also takes care of '\0' not necessarily being available for marking the end of the input */ - for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) - { - switch (buffer_at_offset(input_buffer)[i]) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '+': - case '-': - case 'e': - case 'E': - number_c_string[i] = buffer_at_offset(input_buffer)[i]; - break; - - case '.': - number_c_string[i] = decimal_point; - break; - - default: - goto loop_end; - } - } +static cJSON_bool parse_number(cJSON *const item, parse_buffer *const input_buffer) { + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) { + switch (buffer_at_offset(input_buffer)[i]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } loop_end: - number_c_string[i] = '\0'; + number_c_string[i] = '\0'; - number = strtod((const char*)number_c_string, (char**)&after_end); - if (number_c_string == after_end) - { - return false; /* parse_error */ - } + number = strtod((const char *)number_c_string, (char **)&after_end); + if (number_c_string == after_end) { + return false; /* parse_error */ + } - item->valuedouble = number; + item->valuedouble = number; - /* use saturation in case of overflow */ - if (number >= INT_MAX) - { - item->valueint = INT_MAX; - } - else if (number <= (double)INT_MIN) - { - item->valueint = INT_MIN; - } - else - { - item->valueint = (int)number; - } + /* use saturation in case of overflow */ + if (number >= INT_MAX) { + item->valueint = INT_MAX; + } else if (number <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)number; + } - item->type = cJSON_Number; + item->type = cJSON_Number; - input_buffer->offset += (size_t)(after_end - number_c_string); - return true; + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; } /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) -{ - if (number >= INT_MAX) - { - object->valueint = INT_MAX; - } - else if (number <= (double)INT_MIN) - { - object->valueint = INT_MIN; - } - else - { - object->valueint = (int)number; - } - - return object->valuedouble = number; +CJSON_PUBLIC(double) +cJSON_SetNumberHelper(cJSON *object, double number) { + if (number >= INT_MAX) { + object->valueint = INT_MAX; + } else if (number <= (double)INT_MIN) { + object->valueint = INT_MIN; + } else { + object->valueint = (int)number; + } + + return object->valuedouble = number; } typedef struct { - unsigned char *buffer; - size_t length; - size_t offset; - size_t depth; /* current nesting depth (for formatted printing) */ - cJSON_bool noalloc; - cJSON_bool format; /* is this print a formatted print */ - internal_hooks hooks; + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; } printbuffer; /* realloc printbuffer if necessary to have at least "needed" bytes more */ -static unsigned char* ensure(printbuffer * const p, size_t needed) -{ - unsigned char *newbuffer = NULL; - size_t newsize = 0; - - if ((p == NULL) || (p->buffer == NULL)) - { - return NULL; - } - - if ((p->length > 0) && (p->offset >= p->length)) - { - /* make sure that offset is valid */ - return NULL; - } - - if (needed > INT_MAX) - { - /* sizes bigger than INT_MAX are currently not supported */ - return NULL; - } - - needed += p->offset + 1; - if (needed <= p->length) - { - return p->buffer + p->offset; - } - - if (p->noalloc) { - return NULL; - } - - /* calculate new buffer size */ - if (needed > (INT_MAX / 2)) - { - /* overflow of int, use INT_MAX if possible */ - if (needed <= INT_MAX) - { - newsize = INT_MAX; - } - else - { - return NULL; - } - } - else - { - newsize = needed * 2; - } - - if (p->hooks.reallocate != NULL) - { - /* reallocate with realloc if available */ - newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); - if (newbuffer == NULL) - { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } - } - else - { - /* otherwise reallocate manually */ - newbuffer = (unsigned char*)p->hooks.allocate(newsize); - if (!newbuffer) - { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } - if (newbuffer) - { - memcpy(newbuffer, p->buffer, p->offset + 1); - } - p->hooks.deallocate(p->buffer); - } - p->length = newsize; - p->buffer = newbuffer; - - return newbuffer + p->offset; +static unsigned char *ensure(printbuffer *const p, size_t needed) { + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) { + newsize = INT_MAX; + } else { + return NULL; + } + } else { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) { + /* reallocate with realloc if available */ + newbuffer = (unsigned char *)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } else { + /* otherwise reallocate manually */ + newbuffer = (unsigned char *)p->hooks.allocate(newsize); + if (!newbuffer) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; } /* calculate the new length of the string in a printbuffer and update the offset */ -static void update_offset(printbuffer * const buffer) -{ - const unsigned char *buffer_pointer = NULL; - if ((buffer == NULL) || (buffer->buffer == NULL)) - { - return; - } - buffer_pointer = buffer->buffer + buffer->offset; +static void update_offset(printbuffer *const buffer) { + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; - buffer->offset += strlen((const char*)buffer_pointer); + buffer->offset += strlen((const char *)buffer_pointer); } /* securely comparison of floating-point variables */ -static cJSON_bool compare_double(double a, double b) -{ - return (fabs(a - b) <= CJSON_DOUBLE_PRECISION); +static cJSON_bool compare_double(double a, double b) { + return (fabs(a - b) <= CJSON_DOUBLE_PRECISION); } /* Render the number nicely from the given item into a string. */ -static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - double d = item->valuedouble; - int length = 0; - size_t i = 0; - unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ - unsigned char decimal_point = get_decimal_point(); - double test = 0.0; - - if (output_buffer == NULL) - { - return false; - } - - /* This checks for NaN and Infinity */ - if ((d * 0) != 0) - { - length = snprintf((char*)number_buffer, sizeof(number_buffer), "null"); - } - else - { - /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ - length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.15g", d); - - /* Check whether the original double can be recovered */ - if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) - { - /* If not, print with 17 decimal places of precision */ - length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.17g", d); - } - } - - /* snprintf failed or buffer overrun occurred */ - if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) - { - return false; - } - - /* reserve appropriate space in the output */ - output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); - if (output_pointer == NULL) - { - return false; - } - - /* copy the printed number to the output and replace locale - * dependent decimal point with '.' */ - for (i = 0; i < ((size_t)length); i++) - { - if (number_buffer[i] == decimal_point) - { - output_pointer[i] = '.'; - continue; - } - - output_pointer[i] = number_buffer[i]; - } - output_pointer[i] = '\0'; - - output_buffer->offset += (size_t)length; - - return true; +static cJSON_bool print_number(const cJSON *const item, printbuffer *const output_buffer) { + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) { + length = snprintf((char *)number_buffer, sizeof(number_buffer), "null"); + } else { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = snprintf((char *)number_buffer, sizeof(number_buffer), "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char *)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { + /* If not, print with 17 decimal places of precision */ + length = snprintf((char *)number_buffer, sizeof(number_buffer), "%1.17g", d); + } + } + + /* snprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) { + if (number_buffer[i] == decimal_point) { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; } /* parse 4 digit hexadecimal number */ -static unsigned parse_hex4(const unsigned char * const input) -{ - unsigned int h = 0; - size_t i = 0; - - for (i = 0; i < 4; i++) - { - /* parse digit */ - if ((input[i] >= '0') && (input[i] <= '9')) - { - h += (unsigned int) input[i] - '0'; - } - else if ((input[i] >= 'A') && (input[i] <= 'F')) - { - h += (unsigned int) 10 + input[i] - 'A'; - } - else if ((input[i] >= 'a') && (input[i] <= 'f')) - { - h += (unsigned int) 10 + input[i] - 'a'; - } - else /* invalid */ - { - return 0; - } - - if (i < 3) - { - /* shift left to make place for the next nibble */ - h = h << 4; - } - } - - return h; +static unsigned parse_hex4(const unsigned char *const input) { + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) { + h += (unsigned int)input[i] - '0'; + } else if ((input[i] >= 'A') && (input[i] <= 'F')) { + h += (unsigned int)10 + input[i] - 'A'; + } else if ((input[i] >= 'a') && (input[i] <= 'f')) { + h += (unsigned int)10 + input[i] - 'a'; + } else /* invalid */ + { + return 0; + } + + if (i < 3) { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; } /* converts a UTF-16 literal to UTF-8 * A literal can be one or two sequences of the form \uXXXX */ -static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) -{ - long unsigned int codepoint = 0; - unsigned int first_code = 0; - const unsigned char *first_sequence = input_pointer; - unsigned char utf8_length = 0; - unsigned char utf8_position = 0; - unsigned char sequence_length = 0; - unsigned char first_byte_mark = 0; - - if ((input_end - first_sequence) < 6) - { - /* input ends unexpectedly */ - goto fail; - } - - /* get the first utf16 sequence */ - first_code = parse_hex4(first_sequence + 2); - - /* check that the code is valid */ - if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) - { - goto fail; - } - - /* UTF16 surrogate pair */ - if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) - { - const unsigned char *second_sequence = first_sequence + 6; - unsigned int second_code = 0; - sequence_length = 12; /* \uXXXX\uXXXX */ - - if ((input_end - second_sequence) < 6) - { - /* input ends unexpectedly */ - goto fail; - } - - if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) - { - /* missing second half of the surrogate pair */ - goto fail; - } - - /* get the second utf16 sequence */ - second_code = parse_hex4(second_sequence + 2); - /* check that the code is valid */ - if ((second_code < 0xDC00) || (second_code > 0xDFFF)) - { - /* invalid second half of the surrogate pair */ - goto fail; - } - - - /* calculate the unicode codepoint from the surrogate pair */ - codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); - } - else - { - sequence_length = 6; /* \uXXXX */ - codepoint = first_code; - } - - /* encode as UTF-8 - * takes at maximum 4 bytes to encode: - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - if (codepoint < 0x80) - { - /* normal ascii, encoding 0xxxxxxx */ - utf8_length = 1; - } - else if (codepoint < 0x800) - { - /* two bytes, encoding 110xxxxx 10xxxxxx */ - utf8_length = 2; - first_byte_mark = 0xC0; /* 11000000 */ - } - else if (codepoint < 0x10000) - { - /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ - utf8_length = 3; - first_byte_mark = 0xE0; /* 11100000 */ - } - else if (codepoint <= 0x10FFFF) - { - /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ - utf8_length = 4; - first_byte_mark = 0xF0; /* 11110000 */ - } - else - { - /* invalid unicode codepoint */ - goto fail; - } - - /* encode as utf8 */ - for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) - { - /* 10xxxxxx */ - (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); - codepoint >>= 6; - } - /* encode first byte */ - if (utf8_length > 1) - { - (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); - } - else - { - (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); - } - - *output_pointer += utf8_length; - - return sequence_length; +static unsigned char utf16_literal_to_utf8(const unsigned char *const input_pointer, const unsigned char *const input_end, unsigned char **output_pointer) { + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) { + /* invalid second half of the surrogate pair */ + goto fail; + } + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } else { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } else if (codepoint < 0x800) { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } else if (codepoint < 0x10000) { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } else if (codepoint <= 0x10FFFF) { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } else { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } else { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; fail: - return 0; + return 0; } /* Parse the input text into an unescaped cinput, and populate item. */ -static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) -{ - const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; - const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; - unsigned char *output_pointer = NULL; - unsigned char *output = NULL; - - /* not a string */ - if (buffer_at_offset(input_buffer)[0] != '\"') - { - goto fail; - } - - { - /* calculate approximate size of the output (overestimate) */ - size_t allocation_length = 0; - size_t skipped_bytes = 0; - while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) - { - /* is escape sequence */ - if (input_end[0] == '\\') - { - if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) - { - /* prevent buffer overflow when last input character is a backslash */ - goto fail; - } - skipped_bytes++; - input_end++; - } - input_end++; - } - if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) - { - goto fail; /* string ended unexpectedly */ - } - - /* This is at most how much we need for the output */ - allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; - output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); - if (output == NULL) - { - goto fail; /* allocation failure */ - } - } - - output_pointer = output; - /* loop through the string literal */ - while (input_pointer < input_end) - { - if (*input_pointer != '\\') - { - *output_pointer++ = *input_pointer++; - } - /* escape sequence */ - else - { - unsigned char sequence_length = 2; - if ((input_end - input_pointer) < 1) - { - goto fail; - } - - switch (input_pointer[1]) - { - case 'b': - *output_pointer++ = '\b'; - break; - case 'f': - *output_pointer++ = '\f'; - break; - case 'n': - *output_pointer++ = '\n'; - break; - case 'r': - *output_pointer++ = '\r'; - break; - case 't': - *output_pointer++ = '\t'; - break; - case '\"': - case '\\': - case '/': - *output_pointer++ = input_pointer[1]; - break; - - /* UTF-16 literal */ - case 'u': - sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); - if (sequence_length == 0) - { - /* failed to convert UTF16-literal to UTF-8 */ - goto fail; - } - break; - - default: - goto fail; - } - input_pointer += sequence_length; - } - } - - /* zero terminate the output */ - *output_pointer = '\0'; - - item->type = cJSON_String; - item->valuestring = (char*)output; - - input_buffer->offset = (size_t) (input_end - input_buffer->content); - input_buffer->offset++; - - return true; +static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buffer) { + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) { + /* is escape sequence */ + if (input_end[0] == '\\') { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char *)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) { + if (*input_pointer != '\\') { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) { + goto fail; + } + + switch (input_pointer[1]) { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char *)output; + + input_buffer->offset = (size_t)(input_end - input_buffer->content); + input_buffer->offset++; + + return true; fail: - if (output != NULL) - { - input_buffer->hooks.deallocate(output); - } + if (output != NULL) { + input_buffer->hooks.deallocate(output); + } - if (input_pointer != NULL) - { - input_buffer->offset = (size_t)(input_pointer - input_buffer->content); - } + if (input_pointer != NULL) { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } - return false; + return false; } /* Render the cstring provided to an escaped version that can be printed. */ -static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) -{ - const unsigned char *input_pointer = NULL; - unsigned char *output = NULL; - unsigned char *output_pointer = NULL; - size_t output_length = 0; - /* numbers of additional characters needed for escaping */ - size_t escape_characters = 0; - - if (output_buffer == NULL) - { - return false; - } - - /* empty string */ - if (input == NULL) - { - output = ensure(output_buffer, sizeof("\"\"")); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "\"\""); - - return true; - } - - /* set "flag" to 1 if something needs to be escaped */ - for (input_pointer = input; *input_pointer; input_pointer++) - { - switch (*input_pointer) - { - case '\"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - /* one character escape sequence */ - escape_characters++; - break; - default: - if (*input_pointer < 32) - { - /* UTF-16 escape sequence uXXXX */ - escape_characters += 5; - } - break; - } - } - output_length = (size_t)(input_pointer - input) + escape_characters; - - output = ensure(output_buffer, output_length + sizeof("\"\"")); - if (output == NULL) - { - return false; - } - - /* no characters have to be escaped */ - if (escape_characters == 0) - { - output[0] = '\"'; - memcpy(output + 1, input, output_length); - output[output_length + 1] = '\"'; - output[output_length + 2] = '\0'; - - return true; - } - - output[0] = '\"'; - output_pointer = output + 1; - /* copy the string */ - for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) - { - if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) - { - /* normal character, copy */ - *output_pointer = *input_pointer; - } - else - { - /* character needs to be escaped */ - *output_pointer++ = '\\'; - switch (*input_pointer) - { - case '\\': - *output_pointer = '\\'; - break; - case '\"': - *output_pointer = '\"'; - break; - case '\b': - *output_pointer = 'b'; - break; - case '\f': - *output_pointer = 'f'; - break; - case '\n': - *output_pointer = 'n'; - break; - case '\r': - *output_pointer = 'r'; - break; - case '\t': - *output_pointer = 't'; - break; - default: - /* escape and print as unicode codepoint */ - snprintf((char*)output_pointer, output_buffer->length - (output_pointer - output_buffer->buffer), "u%04x", *input_pointer); - output_pointer += 4; - break; - } - } - } - output[output_length + 1] = '\"'; - output[output_length + 2] = '\0'; - - return true; +static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer *const output_buffer) { + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) { + return false; + } + + /* empty string */ + if (input == NULL) { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) { + return false; + } + strcpy((char *)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) { + switch (*input_pointer) { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { + /* normal character, copy */ + *output_pointer = *input_pointer; + } else { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + snprintf((char *)output_pointer, output_buffer->length - (output_pointer - output_buffer->buffer), "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; } /* Invoke print_string_ptr (which is useful) on an item. */ -static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) -{ - return print_string_ptr((unsigned char*)item->valuestring, p); +static cJSON_bool print_string(const cJSON *const item, printbuffer *const p) { + return print_string_ptr((unsigned char *)item->valuestring, p); } /* Predeclare these prototypes. */ -static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); -static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); -static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer); +static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer); +static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer); +static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer); +static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer); +static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer); /* Utility to jump whitespace and cr/lf */ -static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) -{ - if ((buffer == NULL) || (buffer->content == NULL)) - { - return NULL; - } +static parse_buffer *buffer_skip_whitespace(parse_buffer *const buffer) { + if ((buffer == NULL) || (buffer->content == NULL)) { + return NULL; + } - while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) - { - buffer->offset++; - } + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { + buffer->offset++; + } - if (buffer->offset == buffer->length) - { - buffer->offset--; - } + if (buffer->offset == buffer->length) { + buffer->offset--; + } - return buffer; + return buffer; } /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ -static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) -{ - if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) - { - return NULL; - } +static parse_buffer *skip_utf8_bom(parse_buffer *const buffer) { + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) { + return NULL; + } - if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) - { - buffer->offset += 3; - } + if (can_access_at_index(buffer, 4) && (strncmp((const char *)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) { + buffer->offset += 3; + } - return buffer; + return buffer; } /* Parse an object - create a new root, and populate. */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) -{ - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; - cJSON *item = NULL; - - /* reset error position */ - global_error.json = NULL; - global_error.position = 0; - - if (value == NULL) - { - goto fail; - } - - buffer.content = (const unsigned char*)value; - buffer.length = strlen((const char*)value) + sizeof(""); - buffer.offset = 0; - buffer.hooks = global_hooks; - - item = cJSON_New_Item(&global_hooks); - if (item == NULL) /* memory fail */ - { - goto fail; - } - - if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) - { - /* parse failure. ep is set. */ - goto fail; - } - - /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ - if (require_null_terminated) - { - buffer_skip_whitespace(&buffer); - if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') - { - goto fail; - } - } - if (return_parse_end) - { - *return_parse_end = (const char*)buffer_at_offset(&buffer); - } - - return item; +CJSON_PUBLIC(cJSON *) +cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) { + parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) { + goto fail; + } + + buffer.content = (const unsigned char *)value; + buffer.length = strlen((const char *)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { + goto fail; + } + } + if (return_parse_end) { + *return_parse_end = (const char *)buffer_at_offset(&buffer); + } + + return item; fail: - if (item != NULL) - { - cJSON_Delete(item); - } + if (item != NULL) { + cJSON_Delete(item); + } - if (value != NULL) - { - error local_error; - local_error.json = (const unsigned char*)value; - local_error.position = 0; + if (value != NULL) { + error local_error; + local_error.json = (const unsigned char *)value; + local_error.position = 0; - if (buffer.offset < buffer.length) - { - local_error.position = buffer.offset; - } - else if (buffer.length > 0) - { - local_error.position = buffer.length - 1; - } + if (buffer.offset < buffer.length) { + local_error.position = buffer.offset; + } else if (buffer.length > 0) { + local_error.position = buffer.length - 1; + } - if (return_parse_end != NULL) - { - *return_parse_end = (const char*)local_error.json + local_error.position; - } + if (return_parse_end != NULL) { + *return_parse_end = (const char *)local_error.json + local_error.position; + } - global_error = local_error; - } + global_error = local_error; + } - return NULL; + return NULL; } /* Default options for cJSON_Parse */ -CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) -{ - return cJSON_ParseWithOpts(value, 0, 0); +CJSON_PUBLIC(cJSON *) +cJSON_Parse(const char *value) { + return cJSON_ParseWithOpts(value, 0, 0); } #define cjson_min(a, b) ((a < b) ? a : b) -static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) -{ - static const size_t default_buffer_size = 256; - printbuffer buffer[1]; - unsigned char *printed = NULL; - - memset(buffer, 0, sizeof(buffer)); - - /* create buffer */ - buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); - buffer->length = default_buffer_size; - buffer->format = format; - buffer->hooks = *hooks; - if (buffer->buffer == NULL) - { - goto fail; - } - - /* print the value */ - if (!print_value(item, buffer)) - { - goto fail; - } - update_offset(buffer); - - /* check if reallocate is available */ - if (hooks->reallocate != NULL) - { - printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); - if (printed == NULL) { - goto fail; - } - buffer->buffer = NULL; - } - else /* otherwise copy the JSON over to a new buffer */ - { - printed = (unsigned char*) hooks->allocate(buffer->offset + 1); - if (printed == NULL) - { - goto fail; - } - memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); - printed[buffer->offset] = '\0'; /* just to be sure */ - - /* free the buffer */ - hooks->deallocate(buffer->buffer); - } - - return printed; +static unsigned char *print(const cJSON *const item, cJSON_bool format, const internal_hooks *const hooks) { + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char *)hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) { + printed = (unsigned char *)hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char *)hooks->allocate(buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; fail: - if (buffer->buffer != NULL) - { - hooks->deallocate(buffer->buffer); - } + if (buffer->buffer != NULL) { + hooks->deallocate(buffer->buffer); + } - if (printed != NULL) - { - hooks->deallocate(printed); - } + if (printed != NULL) { + hooks->deallocate(printed); + } - return NULL; + return NULL; } /* Render a cJSON item/entity/structure to text. */ -CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) -{ - return (char*)print(item, true, &global_hooks); +CJSON_PUBLIC(char *) +cJSON_Print(const cJSON *item) { + return (char *)print(item, true, &global_hooks); } -CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) -{ - return (char*)print(item, false, &global_hooks); +CJSON_PUBLIC(char *) +cJSON_PrintUnformatted(const cJSON *item) { + return (char *)print(item, false, &global_hooks); } -CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) -{ - printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; +CJSON_PUBLIC(char *) +cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; - if (prebuffer < 0) - { - return NULL; - } + if (prebuffer < 0) { + return NULL; + } - p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); - if (!p.buffer) - { - return NULL; - } + p.buffer = (unsigned char *)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) { + return NULL; + } - p.length = (size_t)prebuffer; - p.offset = 0; - p.noalloc = false; - p.format = fmt; - p.hooks = global_hooks; + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; - if (!print_value(item, &p)) - { - global_hooks.deallocate(p.buffer); - return NULL; - } + if (!print_value(item, &p)) { + global_hooks.deallocate(p.buffer); + return NULL; + } - return (char*)p.buffer; + return (char *)p.buffer; } -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) -{ - printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; +CJSON_PUBLIC(cJSON_bool) +cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; - if ((length < 0) || (buffer == NULL)) - { - return false; - } + if ((length < 0) || (buffer == NULL)) { + return false; + } - p.buffer = (unsigned char*)buffer; - p.length = (size_t)length; - p.offset = 0; - p.noalloc = true; - p.format = format; - p.hooks = global_hooks; + p.buffer = (unsigned char *)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; - return print_value(item, &p); + return print_value(item, &p); } /* Parser core - when encountering text, process appropriately. */ -static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) -{ - if ((input_buffer == NULL) || (input_buffer->content == NULL)) - { - return false; /* no input */ - } - - /* parse the different types of values */ - /* null */ - if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) - { - item->type = cJSON_NULL; - input_buffer->offset += 4; - return true; - } - /* false */ - if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) - { - item->type = cJSON_False; - input_buffer->offset += 5; - return true; - } - /* true */ - if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) - { - item->type = cJSON_True; - item->valueint = 1; - input_buffer->offset += 4; - return true; - } - /* string */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) - { - return parse_string(item, input_buffer); - } - /* number */ - if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) - { - return parse_number(item, input_buffer); - } - /* array */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) - { - return parse_array(item, input_buffer); - } - /* object */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) - { - return parse_object(item, input_buffer); - } - - return false; +static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer) { + if ((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "null", 4) == 0)) { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char *)buffer_at_offset(input_buffer), "false", 5) == 0)) { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "true", 4) == 0)) { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { + return parse_object(item, input_buffer); + } + + return false; } /* Render a value to text. */ -static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output = NULL; - - if ((item == NULL) || (output_buffer == NULL)) - { - return false; - } - - switch ((item->type) & 0xFF) - { - case cJSON_NULL: - output = ensure(output_buffer, 5); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "null"); - return true; - - case cJSON_False: - output = ensure(output_buffer, 6); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "false"); - return true; - - case cJSON_True: - output = ensure(output_buffer, 5); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "true"); - return true; - - case cJSON_Number: - return print_number(item, output_buffer); - - case cJSON_Raw: - { - size_t raw_length = 0; - if (item->valuestring == NULL) - { - return false; - } - - raw_length = strlen(item->valuestring) + sizeof(""); - output = ensure(output_buffer, raw_length); - if (output == NULL) - { - return false; - } - memcpy(output, item->valuestring, raw_length); - return true; - } - - case cJSON_String: - return print_string(item, output_buffer); - - case cJSON_Array: - return print_array(item, output_buffer); - - case cJSON_Object: - return print_object(item, output_buffer); - - default: - return false; - } +static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer) { + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) { + return false; + } + + switch ((item->type) & 0xFF) { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) { + return false; + } + strcpy((char *)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) { + return false; + } + strcpy((char *)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) { + return false; + } + strcpy((char *)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: { + size_t raw_length = 0; + if (item->valuestring == NULL) { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } } /* Build an array from input text. */ -static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) -{ - cJSON *head = NULL; /* head of the linked list */ - cJSON *current_item = NULL; - - if (input_buffer->depth >= CJSON_NESTING_LIMIT) - { - return false; /* to deeply nested */ - } - input_buffer->depth++; - - if (buffer_at_offset(input_buffer)[0] != '[') - { - /* not an array */ - goto fail; - } - - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) - { - /* empty array */ - goto success; - } - - /* check if we skipped to the end of the buffer */ - if (cannot_access_at_index(input_buffer, 0)) - { - input_buffer->offset--; - goto fail; - } - - /* step back to character in front of the first element */ - input_buffer->offset--; - /* loop through the comma separated array elements */ - do - { - /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); - if (new_item == NULL) - { - goto fail; /* allocation failure */ - } - - /* attach next item to list */ - if (head == NULL) - { - /* start the linked list */ - current_item = head = new_item; - } - else - { - /* add to the end and advance */ - current_item->next = new_item; - new_item->prev = current_item; - current_item = new_item; - } - - /* parse next value */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) - { - goto fail; /* failed to parse value */ - } - buffer_skip_whitespace(input_buffer); - } - while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - - if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') - { - goto fail; /* expected end of array */ - } +static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer) { + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { + goto fail; /* expected end of array */ + } success: - input_buffer->depth--; + input_buffer->depth--; - item->type = cJSON_Array; - item->child = head; + item->type = cJSON_Array; + item->child = head; - input_buffer->offset++; + input_buffer->offset++; - return true; + return true; fail: - if (head != NULL) - { - cJSON_Delete(head); - } + if (head != NULL) { + cJSON_Delete(head); + } - return false; + return false; } /* Render an array to text */ -static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - size_t length = 0; - cJSON *current_element = item->child; - - if (output_buffer == NULL) - { - return false; - } - - /* Compose the output array. */ - /* opening square bracket */ - output_pointer = ensure(output_buffer, 1); - if (output_pointer == NULL) - { - return false; - } - - *output_pointer = '['; - output_buffer->offset++; - output_buffer->depth++; - - while (current_element != NULL) - { - if (!print_value(current_element, output_buffer)) - { - return false; - } - update_offset(output_buffer); - if (current_element->next) - { - length = (size_t) (output_buffer->format ? 2 : 1); - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ','; - if(output_buffer->format) - { - *output_pointer++ = ' '; - } - *output_pointer = '\0'; - output_buffer->offset += length; - } - current_element = current_element->next; - } - - output_pointer = ensure(output_buffer, 2); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ']'; - *output_pointer = '\0'; - output_buffer->depth--; - - return true; +static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer) { + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) { + if (!print_value(current_element, output_buffer)) { + return false; + } + update_offset(output_buffer); + if (current_element->next) { + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) { + return false; + } + *output_pointer++ = ','; + if (output_buffer->format) { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; } /* Build an object from the text. */ -static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) -{ - cJSON *head = NULL; /* linked list head */ - cJSON *current_item = NULL; - - if (input_buffer->depth >= CJSON_NESTING_LIMIT) - { - return false; /* to deeply nested */ - } - input_buffer->depth++; - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) - { - goto fail; /* not an object */ - } - - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) - { - goto success; /* empty object */ - } - - /* check if we skipped to the end of the buffer */ - if (cannot_access_at_index(input_buffer, 0)) - { - input_buffer->offset--; - goto fail; - } - - /* step back to character in front of the first element */ - input_buffer->offset--; - /* loop through the comma separated array elements */ - do - { - /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); - if (new_item == NULL) - { - goto fail; /* allocation failure */ - } - - /* attach next item to list */ - if (head == NULL) - { - /* start the linked list */ - current_item = head = new_item; - } - else - { - /* add to the end and advance */ - current_item->next = new_item; - new_item->prev = current_item; - current_item = new_item; - } - - /* parse the name of the child */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_string(current_item, input_buffer)) - { - goto fail; /* failed to parse name */ - } - buffer_skip_whitespace(input_buffer); - - /* swap valuestring and string, because we parsed the name */ - current_item->string = current_item->valuestring; - current_item->valuestring = NULL; - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) - { - goto fail; /* invalid object */ - } - - /* parse the value */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) - { - goto fail; /* failed to parse value */ - } - buffer_skip_whitespace(input_buffer); - } - while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) - { - goto fail; /* expected end of object */ - } +static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer) { + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) { + goto fail; /* expected end of object */ + } success: - input_buffer->depth--; + input_buffer->depth--; - item->type = cJSON_Object; - item->child = head; + item->type = cJSON_Object; + item->child = head; - input_buffer->offset++; - return true; + input_buffer->offset++; + return true; fail: - if (head != NULL) - { - cJSON_Delete(head); - } + if (head != NULL) { + cJSON_Delete(head); + } - return false; + return false; } /* Render an object to text. */ -static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - size_t length = 0; - cJSON *current_item = item->child; - - if (output_buffer == NULL) - { - return false; - } - - /* Compose the output: */ - length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - - *output_pointer++ = '{'; - output_buffer->depth++; - if (output_buffer->format) - { - *output_pointer++ = '\n'; - } - output_buffer->offset += length; - - while (current_item) - { - if (output_buffer->format) - { - size_t i; - output_pointer = ensure(output_buffer, output_buffer->depth); - if (output_pointer == NULL) - { - return false; - } - for (i = 0; i < output_buffer->depth; i++) - { - *output_pointer++ = '\t'; - } - output_buffer->offset += output_buffer->depth; - } - - /* print key */ - if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) - { - return false; - } - update_offset(output_buffer); - - length = (size_t) (output_buffer->format ? 2 : 1); - output_pointer = ensure(output_buffer, length); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ':'; - if (output_buffer->format) - { - *output_pointer++ = '\t'; - } - output_buffer->offset += length; - - /* print value */ - if (!print_value(current_item, output_buffer)) - { - return false; - } - update_offset(output_buffer); - - /* print comma if not last */ - length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - if (current_item->next) - { - *output_pointer++ = ','; - } - - if (output_buffer->format) - { - *output_pointer++ = '\n'; - } - *output_pointer = '\0'; - output_buffer->offset += length; - - current_item = current_item->next; - } - - output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); - if (output_pointer == NULL) - { - return false; - } - if (output_buffer->format) - { - size_t i; - for (i = 0; i < (output_buffer->depth - 1); i++) - { - *output_pointer++ = '\t'; - } - } - *output_pointer++ = '}'; - *output_pointer = '\0'; - output_buffer->depth--; - - return true; +static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer) { + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) { + return false; + } + + /* Compose the output: */ + length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) { + if (output_buffer->format) { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) { + return false; + } + for (i = 0; i < output_buffer->depth; i++) { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char *)current_item->string, output_buffer)) { + return false; + } + update_offset(output_buffer); + + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) { + return false; + } + if (current_item->next) { + *output_pointer++ = ','; + } + + if (output_buffer->format) { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) { + return false; + } + if (output_buffer->format) { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; } /* Get Array size/item / object item. */ -CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) -{ - cJSON *child = NULL; - size_t size = 0; +CJSON_PUBLIC(int) +cJSON_GetArraySize(const cJSON *array) { + cJSON *child = NULL; + size_t size = 0; - if (array == NULL) - { - return 0; - } + if (array == NULL) { + return 0; + } - child = array->child; + child = array->child; - while(child != NULL) - { - size++; - child = child->next; - } + while (child != NULL) { + size++; + child = child->next; + } - /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ - return (int)size; + return (int)size; } -static cJSON* get_array_item(const cJSON *array, size_t index) -{ - cJSON *current_child = NULL; +static cJSON *get_array_item(const cJSON *array, size_t index) { + cJSON *current_child = NULL; - if (array == NULL) - { - return NULL; - } + if (array == NULL) { + return NULL; + } - current_child = array->child; - while ((current_child != NULL) && (index > 0)) - { - index--; - current_child = current_child->next; - } + current_child = array->child; + while ((current_child != NULL) && (index > 0)) { + index--; + current_child = current_child->next; + } - return current_child; + return current_child; } -CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) -{ - if (index < 0) - { - return NULL; - } +CJSON_PUBLIC(cJSON *) +cJSON_GetArrayItem(const cJSON *array, int index) { + if (index < 0) { + return NULL; + } - return get_array_item(array, (size_t)index); + return get_array_item(array, (size_t)index); } -static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) -{ - cJSON *current_element = NULL; - - if ((object == NULL) || (name == NULL)) - { - return NULL; - } - - current_element = object->child; - if (case_sensitive) - { - while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) - { - current_element = current_element->next; - } - } - else - { - while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) - { - current_element = current_element->next; - } - } - - if ((current_element == NULL) || (current_element->string == NULL)) { - return NULL; - } - - return current_element; -} - -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) -{ - return get_object_item(object, string, false); +static cJSON *get_object_item(const cJSON *const object, const char *const name, const cJSON_bool case_sensitive) { + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) { + return NULL; + } + + current_element = object->child; + if (case_sensitive) { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) { + current_element = current_element->next; + } + } else { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char *)name, (const unsigned char *)(current_element->string)) != 0)) { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; } -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) -{ - return get_object_item(object, string, true); +CJSON_PUBLIC(cJSON *) +cJSON_GetObjectItem(const cJSON *const object, const char *const string) { + return get_object_item(object, string, false); } -CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) -{ - return cJSON_GetObjectItem(object, string) ? 1 : 0; +CJSON_PUBLIC(cJSON *) +cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string) { + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_HasObjectItem(const cJSON *object, const char *string) { + return cJSON_GetObjectItem(object, string) ? 1 : 0; } /* Utility for array list handling. */ -static void suffix_object(cJSON *prev, cJSON *item) -{ - prev->next = item; - item->prev = prev; +static void suffix_object(cJSON *prev, cJSON *item) { + prev->next = item; + item->prev = prev; } /* Utility for handling references. */ -static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) -{ - cJSON *reference = NULL; - if (item == NULL) - { - return NULL; - } - - reference = cJSON_New_Item(hooks); - if (reference == NULL) - { - return NULL; - } - - memcpy(reference, item, sizeof(cJSON)); - reference->string = NULL; - reference->type |= cJSON_IsReference; - reference->next = reference->prev = NULL; - return reference; -} - -static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) -{ - cJSON *child = NULL; +static cJSON *create_reference(const cJSON *item, const internal_hooks *const hooks) { + cJSON *reference = NULL; + if (item == NULL) { + return NULL; + } - if ((item == NULL) || (array == NULL)) - { - return false; - } + reference = cJSON_New_Item(hooks); + if (reference == NULL) { + return NULL; + } - child = array->child; + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) { + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) { + return false; + } - if (child == NULL) - { - /* list is empty, start new one */ - array->child = item; - } - else - { - /* append to the end */ - while (child->next) - { - child = child->next; - } - suffix_object(child, item); - } + child = array->child; - return true; + if (child == NULL) { + /* list is empty, start new one */ + array->child = item; + } else { + /* append to the end */ + while (child->next) { + child = child->next; + } + suffix_object(child, item); + } + + return true; } /* Add item to array/object. */ -CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) -{ - add_item_to_array(array, item); +CJSON_PUBLIC(void) +cJSON_AddItemToArray(cJSON *array, cJSON *item) { + add_item_to_array(array, item); } -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) - #pragma GCC diagnostic push +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic push #endif #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wcast-qual" #endif /* helper function to cast away const */ -static void* cast_away_const(const void* string) -{ - return (void*)string; +static void *cast_away_const(const void *string) { + return (void *)string; } -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) - #pragma GCC diagnostic pop +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic pop #endif +static cJSON_bool add_item_to_object(cJSON *const object, const char *const string, cJSON *const item, const internal_hooks *const hooks, const cJSON_bool constant_key) { + char *new_key = NULL; + int new_type = cJSON_Invalid; -static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) -{ - char *new_key = NULL; - int new_type = cJSON_Invalid; - - if ((object == NULL) || (string == NULL) || (item == NULL)) - { - return false; - } - - if (constant_key) - { - new_key = (char*)cast_away_const(string); - new_type = item->type | cJSON_StringIsConst; - } - else - { - new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); - if (new_key == NULL) - { - return false; - } - - new_type = item->type & ~cJSON_StringIsConst; - } - - if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) - { - hooks->deallocate(item->string); - } - - item->string = new_key; - item->type = new_type; - - return add_item_to_array(object, item); -} - -CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) -{ - add_item_to_object(object, string, item, &global_hooks, false); + if ((object == NULL) || (string == NULL) || (item == NULL)) { + return false; + } + + if (constant_key) { + new_key = (char *)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } else { + new_key = (char *)cJSON_strdup((const unsigned char *)string, hooks); + if (new_key == NULL) { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(void) +cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) { + add_item_to_object(object, string, item, &global_hooks, false); } /* Add an item to an object with constant string as key */ -CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) -{ - add_item_to_object(object, string, item, &global_hooks, true); +CJSON_PUBLIC(void) +cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) { + add_item_to_object(object, string, item, &global_hooks, true); } -CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) -{ - if (array == NULL) - { - return; - } +CJSON_PUBLIC(void) +cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) { + if (array == NULL) { + return; + } - add_item_to_array(array, create_reference(item, &global_hooks)); + add_item_to_array(array, create_reference(item, &global_hooks)); } -CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) -{ - if ((object == NULL) || (string == NULL)) - { - return; - } +CJSON_PUBLIC(void) +cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) { + if ((object == NULL) || (string == NULL)) { + return; + } - add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); + add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); } -CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) -{ - cJSON *null = cJSON_CreateNull(); - if (add_item_to_object(object, name, null, &global_hooks, false)) - { - return null; - } +CJSON_PUBLIC(cJSON *) +cJSON_AddNullToObject(cJSON *const object, const char *const name) { + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) { + return null; + } - cJSON_Delete(null); - return NULL; + cJSON_Delete(null); + return NULL; } -CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) -{ - cJSON *true_item = cJSON_CreateTrue(); - if (add_item_to_object(object, name, true_item, &global_hooks, false)) - { - return true_item; - } +CJSON_PUBLIC(cJSON *) +cJSON_AddTrueToObject(cJSON *const object, const char *const name) { + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) { + return true_item; + } - cJSON_Delete(true_item); - return NULL; + cJSON_Delete(true_item); + return NULL; } -CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) -{ - cJSON *false_item = cJSON_CreateFalse(); - if (add_item_to_object(object, name, false_item, &global_hooks, false)) - { - return false_item; - } +CJSON_PUBLIC(cJSON *) +cJSON_AddFalseToObject(cJSON *const object, const char *const name) { + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) { + return false_item; + } - cJSON_Delete(false_item); - return NULL; + cJSON_Delete(false_item); + return NULL; } -CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) -{ - cJSON *bool_item = cJSON_CreateBool(boolean); - if (add_item_to_object(object, name, bool_item, &global_hooks, false)) - { - return bool_item; - } +CJSON_PUBLIC(cJSON *) +cJSON_AddBoolToObject(cJSON *const object, const char *const name, const cJSON_bool boolean) { + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) { + return bool_item; + } - cJSON_Delete(bool_item); - return NULL; + cJSON_Delete(bool_item); + return NULL; } -CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) -{ - cJSON *number_item = cJSON_CreateNumber(number); - if (add_item_to_object(object, name, number_item, &global_hooks, false)) - { - return number_item; - } +CJSON_PUBLIC(cJSON *) +cJSON_AddNumberToObject(cJSON *const object, const char *const name, const double number) { + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) { + return number_item; + } - cJSON_Delete(number_item); - return NULL; + cJSON_Delete(number_item); + return NULL; } -CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) -{ - cJSON *string_item = cJSON_CreateString(string); - if (add_item_to_object(object, name, string_item, &global_hooks, false)) - { - return string_item; - } +CJSON_PUBLIC(cJSON *) +cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string) { + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) { + return string_item; + } - cJSON_Delete(string_item); - return NULL; + cJSON_Delete(string_item); + return NULL; } -CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) -{ - cJSON *raw_item = cJSON_CreateRaw(raw); - if (add_item_to_object(object, name, raw_item, &global_hooks, false)) - { - return raw_item; - } +CJSON_PUBLIC(cJSON *) +cJSON_AddRawToObject(cJSON *const object, const char *const name, const char *const raw) { + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) { + return raw_item; + } - cJSON_Delete(raw_item); - return NULL; + cJSON_Delete(raw_item); + return NULL; } -CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) -{ - cJSON *object_item = cJSON_CreateObject(); - if (add_item_to_object(object, name, object_item, &global_hooks, false)) - { - return object_item; - } +CJSON_PUBLIC(cJSON *) +cJSON_AddObjectToObject(cJSON *const object, const char *const name) { + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) { + return object_item; + } - cJSON_Delete(object_item); - return NULL; + cJSON_Delete(object_item); + return NULL; } -CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) -{ - cJSON *array = cJSON_CreateArray(); - if (add_item_to_object(object, name, array, &global_hooks, false)) - { - return array; - } +CJSON_PUBLIC(cJSON *) +cJSON_AddArrayToObject(cJSON *const object, const char *const name) { + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) { + return array; + } - cJSON_Delete(array); - return NULL; + cJSON_Delete(array); + return NULL; } -CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) -{ - if ((parent == NULL) || (item == NULL)) - { - return NULL; - } - - if (item->prev != NULL) - { - /* not the first element */ - item->prev->next = item->next; - } - if (item->next != NULL) - { - /* not the last element */ - item->next->prev = item->prev; - } - - if (item == parent->child) - { - /* first element */ - parent->child = item->next; - } - /* make sure the detached item doesn't point anywhere anymore */ - item->prev = NULL; - item->next = NULL; - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) -{ - if (which < 0) - { - return NULL; - } +CJSON_PUBLIC(cJSON *) +cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item) { + if ((parent == NULL) || (item == NULL)) { + return NULL; + } + + if (item->prev != NULL) { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) { + /* not the last element */ + item->next->prev = item->prev; + } - return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); + if (item == parent->child) { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; } -CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) -{ - cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +CJSON_PUBLIC(cJSON *) +cJSON_DetachItemFromArray(cJSON *array, int which) { + if (which < 0) { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); } -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) -{ - cJSON *to_detach = cJSON_GetObjectItem(object, string); +CJSON_PUBLIC(void) +cJSON_DeleteItemFromArray(cJSON *array, int which) { + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} - return cJSON_DetachItemViaPointer(object, to_detach); +CJSON_PUBLIC(cJSON *) +cJSON_DetachItemFromObject(cJSON *object, const char *string) { + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); } -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) -{ - cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); +CJSON_PUBLIC(cJSON *) +cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) { + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); - return cJSON_DetachItemViaPointer(object, to_detach); + return cJSON_DetachItemViaPointer(object, to_detach); } -CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) -{ - cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +CJSON_PUBLIC(void) +cJSON_DeleteItemFromObject(cJSON *object, const char *string) { + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); } -CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) -{ - cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +CJSON_PUBLIC(void) +cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) { + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); } /* Replace array/object items with new ones. */ -CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) -{ - cJSON *after_inserted = NULL; - - if (which < 0) - { - return; - } - - after_inserted = get_array_item(array, (size_t)which); - if (after_inserted == NULL) - { - add_item_to_array(array, newitem); - return; - } - - newitem->next = after_inserted; - newitem->prev = after_inserted->prev; - after_inserted->prev = newitem; - if (after_inserted == array->child) - { - array->child = newitem; - } - else - { - newitem->prev->next = newitem; - } -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) -{ - if ((parent == NULL) || (replacement == NULL) || (item == NULL)) - { - return false; - } - - if (replacement == item) - { - return true; - } - - replacement->next = item->next; - replacement->prev = item->prev; - - if (replacement->next != NULL) - { - replacement->next->prev = replacement; - } - if (replacement->prev != NULL) - { - replacement->prev->next = replacement; - } - if (parent->child == item) - { - parent->child = replacement; - } - - item->next = NULL; - item->prev = NULL; - cJSON_Delete(item); - - return true; -} - -CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) -{ - if (which < 0) - { - return; - } +CJSON_PUBLIC(void) +cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { + cJSON *after_inserted = NULL; + + if (which < 0) { + return; + } - cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) { + add_item_to_array(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) { + array->child = newitem; + } else { + newitem->prev->next = newitem; + } } -static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) -{ - if ((replacement == NULL) || (string == NULL)) - { - return false; - } +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item, cJSON *replacement) { + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) { + return false; + } - /* replace the name in the replacement */ - if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) - { - cJSON_free(replacement->string); - } - replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); - replacement->type &= ~cJSON_StringIsConst; + if (replacement == item) { + return true; + } - cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + replacement->next = item->next; + replacement->prev = item->prev; - return true; -} + if (replacement->next != NULL) { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) { + replacement->prev->next = replacement; + } + if (parent->child == item) { + parent->child = replacement; + } -CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) -{ - replace_item_in_object(object, string, newitem, false); -} + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); -CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) -{ - replace_item_in_object(object, string, newitem, true); + return true; } -/* Create basic types: */ -CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_NULL; - } +CJSON_PUBLIC(void) +cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) { + if (which < 0) { + return; + } - return item; + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); } -CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_True; - } +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) { + if ((replacement == NULL) || (string == NULL)) { + return false; + } - return item; -} + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) { + cJSON_free(replacement->string); + } + replacement->string = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; -CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_False; - } + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); - return item; + return true; } -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = boolean ? cJSON_True : cJSON_False; - } +CJSON_PUBLIC(void) +cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { + replace_item_in_object(object, string, newitem, false); +} - return item; +CJSON_PUBLIC(void) +cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) { + replace_item_in_object(object, string, newitem, true); } -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_Number; - item->valuedouble = num; - - /* use saturation in case of overflow */ - if (num >= INT_MAX) - { - item->valueint = INT_MAX; - } - else if (num <= (double)INT_MIN) - { - item->valueint = INT_MIN; - } - else - { - item->valueint = (int)num; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_String; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); - if(!item->valuestring) - { - cJSON_Delete(item); - return NULL; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) - { - item->type = cJSON_String | cJSON_IsReference; - item->valuestring = (char*)cast_away_const(string); - } +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) +cJSON_CreateNull(void) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_NULL; + } - return item; + return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) { - item->type = cJSON_Object | cJSON_IsReference; - item->child = (cJSON*)cast_away_const(child); - } +CJSON_PUBLIC(cJSON *) +cJSON_CreateTrue(void) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) +cJSON_CreateFalse(void) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) +cJSON_CreateBool(cJSON_bool boolean) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = boolean ? cJSON_True : cJSON_False; + } - return item; + return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) { - item->type = cJSON_Array | cJSON_IsReference; - item->child = (cJSON*)cast_away_const(child); - } +CJSON_PUBLIC(cJSON *) +cJSON_CreateNumber(double num) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_Number; + item->valuedouble = num; - return item; + /* use saturation in case of overflow */ + if (num >= INT_MAX) { + item->valueint = INT_MAX; + } else if (num <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)num; + } + } + + return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_Raw; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); - if(!item->valuestring) - { - cJSON_Delete(item); - return NULL; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type=cJSON_Array; - } +CJSON_PUBLIC(cJSON *) +cJSON_CreateString(const char *string) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_String; + item->valuestring = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); + if (!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } - return item; + return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) - { - item->type = cJSON_Object; - } +CJSON_PUBLIC(cJSON *) +cJSON_CreateStringReference(const char *string) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char *)cast_away_const(string); + } - return item; + return item; } -/* Create Arrays: */ -CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - for(i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber(numbers[i]); - if (!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for(i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber((double)numbers[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for(i = 0;a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber(numbers[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (strings == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for (i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateString(strings[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p,n); - } - p = n; - } - - return a; +CJSON_PUBLIC(cJSON *) +cJSON_CreateObjectReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON *)cast_away_const(child); + } + + return item; } -/* Duplication */ -CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) -{ - cJSON *newitem = NULL; - cJSON *child = NULL; - cJSON *next = NULL; - cJSON *newchild = NULL; - - /* Bail on bad ptr */ - if (!item) - { - goto fail; - } - /* Create new item */ - newitem = cJSON_New_Item(&global_hooks); - if (!newitem) - { - goto fail; - } - /* Copy over all vars */ - newitem->type = item->type & (~cJSON_IsReference); - newitem->valueint = item->valueint; - newitem->valuedouble = item->valuedouble; - if (item->valuestring) - { - newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); - if (!newitem->valuestring) - { - goto fail; - } - } - if (item->string) - { - newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); - if (!newitem->string) - { - goto fail; - } - } - /* If non-recursive, then we're done! */ - if (!recurse) - { - return newitem; - } - /* Walk the ->next chain for the child. */ - child = item->child; - while (child != NULL) - { - newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) - { - goto fail; - } - if (next != NULL) - { - /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - next->next = newchild; - newchild->prev = next; - next = newchild; - } - else - { - /* Set newitem->child and move to it */ - newitem->child = newchild; - next = newchild; - } - child = child->next; - } - - return newitem; +CJSON_PUBLIC(cJSON *) +cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON *)cast_away_const(child); + } -fail: - if (newitem != NULL) - { - cJSON_Delete(newitem); - } + return item; +} + +CJSON_PUBLIC(cJSON *) +cJSON_CreateRaw(const char *raw) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_Raw; + item->valuestring = (char *)cJSON_strdup((const unsigned char *)raw, &global_hooks); + if (!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } - return NULL; + return item; } -static void skip_oneline_comment(char **input) -{ - *input += static_strlen("//"); +CJSON_PUBLIC(cJSON *) +cJSON_CreateArray(void) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_Array; + } - for (; (*input)[0] != '\0'; ++(*input)) - { - if ((*input)[0] == '\n') { - *input += static_strlen("\n"); - return; - } - } + return item; } -static void skip_multiline_comment(char **input) -{ - *input += static_strlen("/*"); +CJSON_PUBLIC(cJSON *) +cJSON_CreateObject(void) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) { + item->type = cJSON_Object; + } - for (; (*input)[0] != '\0'; ++(*input)) - { - if (((*input)[0] == '*') && ((*input)[1] == '/')) - { - *input += static_strlen("*/"); - return; - } - } + return item; } -static void minify_string(char **input, char **output) { - (*output)[0] = (*input)[0]; - *input += static_strlen("\""); - *output += static_strlen("\""); +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) +cJSON_CreateIntArray(const int *numbers, int count) { + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + for (i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if (!n) { + cJSON_Delete(a); + return NULL; + } + if (!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) +cJSON_CreateFloatArray(const float *numbers, int count) { + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber((double)numbers[i]); + if (!n) { + cJSON_Delete(a); + return NULL; + } + if (!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) +cJSON_CreateDoubleArray(const double *numbers, int count) { + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if (!n) { + cJSON_Delete(a); + return NULL; + } + if (!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) +cJSON_CreateStringArray(const char *const *strings, int count) { + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateString(strings[i]); + if (!n) { + cJSON_Delete(a); + return NULL; + } + if (!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + return a; +} +/* Duplication */ +CJSON_PUBLIC(cJSON *) +cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) { + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) { + newitem->valuestring = (char *)cJSON_strdup((unsigned char *)item->valuestring, &global_hooks); + if (!newitem->valuestring) { + goto fail; + } + } + if (item->string) { + newitem->string = (item->type & cJSON_StringIsConst) ? item->string : (char *)cJSON_strdup((unsigned char *)item->string, &global_hooks); + if (!newitem->string) { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) { + goto fail; + } + if (next != NULL) { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } else { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; - for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { - (*output)[0] = (*input)[0]; +fail: + if (newitem != NULL) { + cJSON_Delete(newitem); + } - if ((*input)[0] == '\"') { - (*output)[0] = '\"'; - *input += static_strlen("\""); - *output += static_strlen("\""); - return; - } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { - (*output)[1] = (*input)[1]; - *input += static_strlen("\""); - *output += static_strlen("\""); - } - } + return NULL; } -CJSON_PUBLIC(void) cJSON_Minify(char *json) -{ - char *into = json; - - if (json == NULL) - { - return; - } - - while (json[0] != '\0') - { - switch (json[0]) - { - case ' ': - case '\t': - case '\r': - case '\n': - json++; - break; - - case '/': - if (json[1] == '/') - { - skip_oneline_comment(&json); - } - else if (json[1] == '*') - { - skip_multiline_comment(&json); - } else { - json++; - } - break; - - case '\"': - minify_string(&json, (char**)&into); - break; - - default: - into[0] = json[0]; - json++; - into++; - } - } - - /* and null-terminate. */ - *into = '\0'; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } +static void skip_oneline_comment(char **input) { + *input += static_strlen("//"); - return (item->type & 0xFF) == cJSON_Invalid; + for (; (*input)[0] != '\0'; ++(*input)) { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } } -CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } +static void skip_multiline_comment(char **input) { + *input += static_strlen("/*"); - return (item->type & 0xFF) == cJSON_False; + for (; (*input)[0] != '\0'; ++(*input)) { + if (((*input)[0] == '*') && ((*input)[1] == '/')) { + *input += static_strlen("*/"); + return; + } + } } -CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; - return (item->type & 0xff) == cJSON_True; + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } } +CJSON_PUBLIC(void) +cJSON_Minify(char *json) { + char *into = json; + + if (json == NULL) { + return; + } -CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } + while (json[0] != '\0') { + switch (json[0]) { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; - return (item->type & (cJSON_True | cJSON_False)) != 0; -} -CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } + case '/': + if (json[1] == '/') { + skip_oneline_comment(&json); + } else if (json[1] == '*') { + skip_multiline_comment(&json); + } else { + json++; + } + break; - return (item->type & 0xFF) == cJSON_NULL; -} + case '\"': + minify_string(&json, (char **)&into); + break; -CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } + default: + into[0] = json[0]; + json++; + into++; + } + } - return (item->type & 0xFF) == cJSON_Number; + /* and null-terminate. */ + *into = '\0'; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } +CJSON_PUBLIC(cJSON_bool) +cJSON_IsInvalid(const cJSON *const item) { + if (item == NULL) { + return false; + } - return (item->type & 0xFF) == cJSON_String; + return (item->type & 0xFF) == cJSON_Invalid; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } +CJSON_PUBLIC(cJSON_bool) +cJSON_IsFalse(const cJSON *const item) { + if (item == NULL) { + return false; + } - return (item->type & 0xFF) == cJSON_Array; + return (item->type & 0xFF) == cJSON_False; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } +CJSON_PUBLIC(cJSON_bool) +cJSON_IsTrue(const cJSON *const item) { + if (item == NULL) { + return false; + } - return (item->type & 0xFF) == cJSON_Object; + return (item->type & 0xff) == cJSON_True; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_IsBool(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) +cJSON_IsNull(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_IsNumber(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_IsString(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_String; } -CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } +CJSON_PUBLIC(cJSON_bool) +cJSON_IsArray(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_IsObject(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_IsRaw(const cJSON *const item) { + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} - return (item->type & 0xFF) == cJSON_Raw; -} +CJSON_PUBLIC(cJSON_bool) +cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive) { + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) { + return false; + } -CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) -{ - if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) - { - return false; - } - - /* check if type is valid */ - switch (a->type & 0xFF) - { - case cJSON_False: - case cJSON_True: - case cJSON_NULL: - case cJSON_Number: - case cJSON_String: - case cJSON_Raw: - case cJSON_Array: - case cJSON_Object: - break; - - default: - return false; - } - - /* identical objects are equal */ - if (a == b) - { - return true; - } - - switch (a->type & 0xFF) - { - /* in these cases and equal type is enough */ - case cJSON_False: - case cJSON_True: - case cJSON_NULL: - return true; - - case cJSON_Number: - if (compare_double(a->valuedouble, b->valuedouble)) - { - return true; - } - return false; - - case cJSON_String: - case cJSON_Raw: - if ((a->valuestring == NULL) || (b->valuestring == NULL)) - { - return false; - } - if (strcmp(a->valuestring, b->valuestring) == 0) - { - return true; - } - - return false; - - case cJSON_Array: - { - cJSON *a_element = a->child; - cJSON *b_element = b->child; - - for (; (a_element != NULL) && (b_element != NULL);) - { - if (!cJSON_Compare(a_element, b_element, case_sensitive)) - { - return false; - } - - a_element = a_element->next; - b_element = b_element->next; - } - - /* one of the arrays is longer than the other */ - if (a_element != b_element) { - return false; - } - - return true; - } - - case cJSON_Object: - { - cJSON *a_element = NULL; - cJSON *b_element = NULL; - cJSON_ArrayForEach(a_element, a) - { - /* TODO This has O(n^2) runtime, which is horrible! */ - b_element = get_object_item(b, a_element->string, case_sensitive); - if (b_element == NULL) - { - return false; - } - - if (!cJSON_Compare(a_element, b_element, case_sensitive)) - { - return false; - } - } - - /* doing this twice, once on a and b to prevent true comparison if a subset of b - * TODO: Do this the proper way, this is just a fix for now */ - cJSON_ArrayForEach(b_element, b) - { - a_element = get_object_item(a, b_element->string, case_sensitive); - if (a_element == NULL) - { - return false; - } - - if (!cJSON_Compare(b_element, a_element, case_sensitive)) - { - return false; - } - } - - return true; - } - - default: - return false; - } -} - -CJSON_PUBLIC(void *) cJSON_malloc(size_t size) -{ - return global_hooks.allocate(size); -} + /* check if type is valid */ + switch (a->type & 0xFF) { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; -CJSON_PUBLIC(void) cJSON_free(void *object) -{ - global_hooks.deallocate(object); + default: + return false; + } + + /* identical objects are equal */ + if (a == b) { + return true; + } + + switch (a->type & 0xFF) { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) { + return true; + } + + return false; + + case cJSON_Array: { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) +cJSON_malloc(size_t size) { + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) +cJSON_free(void *object) { + global_hooks.deallocate(object); } diff --git a/console/cJSON.h b/console/cJSON.h index 204b40e0..00b0f3fd 100644 --- a/console/cJSON.h +++ b/console/cJSON.h @@ -24,8 +24,7 @@ #define cJSON__h #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) @@ -61,18 +60,18 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ #endif #if defined(CJSON_HIDE_SYMBOLS) -#define CJSON_PUBLIC(type) type CJSON_STDCALL +#define CJSON_PUBLIC(type) type CJSON_STDCALL #elif defined(CJSON_EXPORT_SYMBOLS) -#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL #elif defined(CJSON_IMPORT_SYMBOLS) -#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL #endif #else /* !__WINDOWS__ */ #define CJSON_CDECL #define CJSON_STDCALL -#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) -#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type #else #define CJSON_PUBLIC(type) type #endif @@ -87,46 +86,44 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ /* cJSON Types: */ #define cJSON_Invalid (0) -#define cJSON_False (1 << 0) -#define cJSON_True (1 << 1) -#define cJSON_NULL (1 << 2) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) #define cJSON_Number (1 << 3) #define cJSON_String (1 << 4) -#define cJSON_Array (1 << 5) +#define cJSON_Array (1 << 5) #define cJSON_Object (1 << 6) -#define cJSON_Raw (1 << 7) /* raw json */ +#define cJSON_Raw (1 << 7) /* raw json */ #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 /* The cJSON structure: */ -typedef struct cJSON -{ - /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *next; - struct cJSON *prev; - /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ - struct cJSON *child; - - /* The type of the item, as above. */ - int type; - - /* The item's string, if type==cJSON_String and type == cJSON_Raw */ - char *valuestring; - /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ - int valueint; - /* The item's number, if type==cJSON_Number */ - double valuedouble; - - /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ - char *string; +typedef struct cJSON { + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; } cJSON; -typedef struct cJSON_Hooks -{ - /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ - void *(CJSON_CDECL *malloc_fn)(size_t sz); - void (CJSON_CDECL *free_fn)(void *ptr); +typedef struct cJSON_Hooks { + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void(CJSON_CDECL *free_fn)(void *ptr); } cJSON_Hooks; typedef int cJSON_bool; @@ -143,148 +140,222 @@ typedef int cJSON_bool; #endif /* returns the version of cJSON as a string */ -CJSON_PUBLIC(const char*) cJSON_Version(void); +CJSON_PUBLIC(const char *) +cJSON_Version(void); /* Supply malloc, realloc and free functions to cJSON */ -CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); +CJSON_PUBLIC(void) +cJSON_InitHooks(cJSON_Hooks *hooks); /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ -CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) +cJSON_Parse(const char *value); /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) +cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); /* Render a cJSON entity to text for transfer/storage. */ -CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +CJSON_PUBLIC(char *) +cJSON_Print(const cJSON *item); /* Render a cJSON entity to text for transfer/storage without any formatting. */ -CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +CJSON_PUBLIC(char *) +cJSON_PrintUnformatted(const cJSON *item); /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ -CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +CJSON_PUBLIC(char *) +cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +CJSON_PUBLIC(cJSON_bool) +cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); /* Delete a cJSON entity and all subentities. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); +CJSON_PUBLIC(void) +cJSON_Delete(cJSON *item); /* Returns the number of items in an array (or object). */ -CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +CJSON_PUBLIC(int) +cJSON_GetArraySize(const cJSON *array); /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ -CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +CJSON_PUBLIC(cJSON *) +cJSON_GetArrayItem(const cJSON *array, int index); /* Get item "string" from object. Case insensitive. */ -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); -CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) +cJSON_GetObjectItem(const cJSON *const object, const char *const string); +CJSON_PUBLIC(cJSON *) +cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string); +CJSON_PUBLIC(cJSON_bool) +cJSON_HasObjectItem(const cJSON *object, const char *string); /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ -CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); +CJSON_PUBLIC(const char *) +cJSON_GetErrorPtr(void); /* Check if the item is a string and return its valuestring */ -CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(char *) +cJSON_GetStringValue(const cJSON *const item); /* These functions check the type of an item */ -CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); -CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) +cJSON_IsInvalid(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) +cJSON_IsFalse(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) +cJSON_IsTrue(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) +cJSON_IsBool(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) +cJSON_IsNull(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) +cJSON_IsNumber(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) +cJSON_IsString(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) +cJSON_IsArray(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) +cJSON_IsObject(const cJSON *const item); +CJSON_PUBLIC(cJSON_bool) +cJSON_IsRaw(const cJSON *const item); /* These calls create a cJSON item of the appropriate type. */ -CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); -CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +CJSON_PUBLIC(cJSON *) +cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) +cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) +cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) +cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) +cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) +cJSON_CreateString(const char *string); /* raw json */ -CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); -CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); -CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); +CJSON_PUBLIC(cJSON *) +cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) +cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) +cJSON_CreateObject(void); /* Create a string where valuestring references a string so * it will not be freed by cJSON_Delete */ -CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +CJSON_PUBLIC(cJSON *) +cJSON_CreateStringReference(const char *string); /* Create an object/array that only references it's elements so * they will not be freed by cJSON_Delete */ -CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); -CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) +cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) +cJSON_CreateArrayReference(const cJSON *child); /* These utilities create an Array of count items. * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ -CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); +CJSON_PUBLIC(cJSON *) +cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) +cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) +cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) +cJSON_CreateStringArray(const char *const *strings, int count); /* Append item to the specified array/object. */ -CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(void) +cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) +cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before * writing to `item->string` */ -CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(void) +cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ -CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(void) +cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) +cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); /* Remove/Detach items from Arrays/Objects. */ -CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); -CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); -CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); -CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) +cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item); +CJSON_PUBLIC(cJSON *) +cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) +cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) +cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) +cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) +cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) +cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); /* Update array items. */ -CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); -CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); -CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); -CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) +cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item, cJSON *replacement); +CJSON_PUBLIC(void) +cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) +cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem); +CJSON_PUBLIC(void) +cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem); /* Duplicate a cJSON item */ -CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +CJSON_PUBLIC(cJSON *) +cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will * need to be released. With recurse!=0, it will duplicate any children connected to the item. * The item->next and ->prev pointers are always zero on return from Duplicate. */ /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ -CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); +CJSON_PUBLIC(cJSON_bool) +cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive); /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. - * The input pointer json cannot point to a read-only address area, such as a string constant, + * The input pointer json cannot point to a read-only address area, such as a string constant, * but should point to a readable and writable address area. */ -CJSON_PUBLIC(void) cJSON_Minify(char *json); +CJSON_PUBLIC(void) +cJSON_Minify(char *json); /* Helper functions for creating and adding items to an object at the same time. * They return the added item or NULL on failure. */ -CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); -CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); -CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); -CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); -CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); -CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON *) +cJSON_AddNullToObject(cJSON *const object, const char *const name); +CJSON_PUBLIC(cJSON *) +cJSON_AddTrueToObject(cJSON *const object, const char *const name); +CJSON_PUBLIC(cJSON *) +cJSON_AddFalseToObject(cJSON *const object, const char *const name); +CJSON_PUBLIC(cJSON *) +cJSON_AddBoolToObject(cJSON *const object, const char *const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) +cJSON_AddNumberToObject(cJSON *const object, const char *const name, const double number); +CJSON_PUBLIC(cJSON *) +cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string); +CJSON_PUBLIC(cJSON *) +cJSON_AddRawToObject(cJSON *const object, const char *const name, const char *const raw); +CJSON_PUBLIC(cJSON *) +cJSON_AddObjectToObject(cJSON *const object, const char *const name); +CJSON_PUBLIC(cJSON *) +cJSON_AddArrayToObject(cJSON *const object, const char *const name); /* When assigning an integer value, it needs to be propagated to valuedouble too. */ #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) /* helper for the cJSON_SetNumberValue macro */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +CJSON_PUBLIC(double) +cJSON_SetNumberHelper(cJSON *object, double number); #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) /* Macro for iterating over an array or object */ -#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) +#define cJSON_ArrayForEach(element, array) for (element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ -CJSON_PUBLIC(void *) cJSON_malloc(size_t size); -CJSON_PUBLIC(void) cJSON_free(void *object); +CJSON_PUBLIC(void *) +cJSON_malloc(size_t size); +CJSON_PUBLIC(void) +cJSON_free(void *object); #ifdef __cplusplus } diff --git a/console/dcm2niix_fswrapper.cpp b/console/dcm2niix_fswrapper.cpp index 21b8baa2..33f2721b 100644 --- a/console/dcm2niix_fswrapper.cpp +++ b/console/dcm2niix_fswrapper.cpp @@ -1,9 +1,9 @@ +#include #include #include -#include -#include "nii_dicom.h" #include "dcm2niix_fswrapper.h" +#include "nii_dicom.h" struct TDCMopts dcm2niix_fswrapper::tdcmOpts; @@ -49,35 +49,34 @@ pigzname = '\000' optsname = "~/.dcm2nii.ini" indirParent = "..." imageComments = "" -seriesNumber = nnn +seriesNumber = nnn numSeries = 0 */ // set TDCMopts defaults, overwrite settings to output in mgz orientation -void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* dcm2niixopts) -{ - memset(&tdcmOpts, 0, sizeof(tdcmOpts)); - setDefaultOpts(&tdcmOpts, NULL); - - if (dcmindir != NULL) - strcpy(tdcmOpts.indir, dcmindir); - - // dcmunpack actually uses seriesDescription, set FName = `printf %04d.$descr $series` - // change it from "%4s.%p" to "%4s.%d" - strcpy(tdcmOpts.filename, "%4s.%d"); - - // set the options for freesurfer mgz orientation - tdcmOpts.isRotate3DAcq = false; - tdcmOpts.isFlipY = false; - tdcmOpts.isIgnoreSeriesInstanceUID = true; - tdcmOpts.isCreateBIDS = false; - tdcmOpts.isGz = false; - tdcmOpts.isForceStackSameSeries = 1; // merge 2D slice '-m y', tdcmOpts.isForceStackSameSeries = 1 - tdcmOpts.isForceStackDCE = false; - //tdcmOpts.isForceOnsetTimes = false; - - if (dcm2niixopts != NULL) - __setDcm2niixOpts(dcm2niixopts); +void dcm2niix_fswrapper::setOpts(const char *dcmindir, const char *dcm2niixopts) { + memset(&tdcmOpts, 0, sizeof(tdcmOpts)); + setDefaultOpts(&tdcmOpts, NULL); + + if (dcmindir != NULL) + strcpy(tdcmOpts.indir, dcmindir); + + // dcmunpack actually uses seriesDescription, set FName = `printf %04d.$descr $series` + // change it from "%4s.%p" to "%4s.%d" + strcpy(tdcmOpts.filename, "%4s.%d"); + + // set the options for freesurfer mgz orientation + tdcmOpts.isRotate3DAcq = false; + tdcmOpts.isFlipY = false; + tdcmOpts.isIgnoreSeriesInstanceUID = true; + tdcmOpts.isCreateBIDS = false; + tdcmOpts.isGz = false; + tdcmOpts.isForceStackSameSeries = 1; // merge 2D slice '-m y', tdcmOpts.isForceStackSameSeries = 1 + tdcmOpts.isForceStackDCE = false; + // tdcmOpts.isForceOnsetTimes = false; + + if (dcm2niixopts != NULL) + __setDcm2niixOpts(dcm2niixopts); } /* set user dcm2niix conversion options in struct TDCMopts tdcmOpts @@ -90,7 +89,7 @@ void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* dcm2niixopts) * If "n"o, side-car may report patient name, age and weight. * * -f - * Format string for the output filename(s). + * Format string for the output filename(s). * * -i * Ignore derived, localizer and 2D images (default n) @@ -115,176 +114,152 @@ void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* dcm2niixopts) * Crop images. This will attempt to remove excess neck from 3D acquisitions. * If "i", images are neither cropped nor rotated to canonical space. */ -void dcm2niix_fswrapper::__setDcm2niixOpts(const char *dcm2niixopts) -{ - //printf("[DEBUG] dcm2niix_fswrapper::__setDcm2niixOpts(%s)\n", dcm2niixopts); - - char *restOpts = (char*)malloc(strlen(dcm2niixopts)+1); - memset(restOpts, 0, strlen(dcm2niixopts)+1); - memcpy(restOpts, dcm2niixopts, strlen(dcm2niixopts)); - - char *nextOpt = strtok_r((char*)dcm2niixopts, ",", &restOpts); - while (nextOpt != NULL) - { - char *k = nextOpt; - char *v = strchr(nextOpt, '='); - if (v != NULL) - *v = '\0'; - v++; // move past '=' - - // skip leading white spaces - while (*k == ' ') - k++; - - if (strcmp(k, "b") == 0) - { - if (*v == 'n' || *v == 'N' || *v == '0') - tdcmOpts.isCreateBIDS = false; - else if (*v == 'i' || *v == 'I') - { - tdcmOpts.isCreateBIDS = false; - tdcmOpts.isOnlyBIDS = true; - } - else if (*v == 'y' || *v == 'Y') - tdcmOpts.isCreateBIDS = true; - } - else if (strcmp(k, "ba") == 0) - tdcmOpts.isAnonymizeBIDS = (*v == 'n' || *v == 'N') ? false : true; - else if (strcmp(k, "f") == 0) - strcpy(tdcmOpts.filename, v); - else if (strcmp(k, "i") == 0) - tdcmOpts.isIgnoreDerivedAnd2D = (*v == 'y' || *v == 'Y') ? true : false; - else if (strcmp(k, "m") == 0) - { - if (*v == 'n' || *v == 'N' || *v == '0') - tdcmOpts.isForceStackSameSeries = 0; - else if (*v == 'y' || *v == 'Y' || *v == '1') - tdcmOpts.isForceStackSameSeries = 1; - else if (*v == '2') - tdcmOpts.isForceStackSameSeries = 2; - else if (*v == 'o' || *v == 'O') - tdcmOpts.isForceStackDCE = false; - } - else if (strcmp(k, "v") == 0) - { - if (*v == 'n' || *v == 'N' || *v == '0') - tdcmOpts.isVerbose = 0; - else if (*v == 'h' || *v == 'H' || *v == '2') - tdcmOpts.isVerbose = 2; - else - tdcmOpts.isVerbose = 1; - } - else if (strcmp(k, "o") == 0) - strcpy(tdcmOpts.outdir, v); - else if (strcmp(k, "t") == 0) - tdcmOpts.isCreateText = (*v == 'y' || *v == 'Y') ? true : false; - else if (strcmp(k, "p") == 0) - { - if (*v == 'n' || *v == 'N' || *v == '0') - tdcmOpts.isPhilipsFloatNotDisplayScaling = false; - } - else if (strcmp(k, "x") ==0) - { - if (*v == 'y' || *v == 'Y') - tdcmOpts.isCrop = true; - else if (*v == 'i' || *v == 'I') - { - tdcmOpts.isRotate3DAcq = false; - tdcmOpts.isCrop = false; - } - } - else - { - printf("[WARN] dcm2niix option %s=%s skipped\n", k, v); - } - - nextOpt = strtok_r(NULL, ",", &restOpts); - } +void dcm2niix_fswrapper::__setDcm2niixOpts(const char *dcm2niixopts) { + // printf("[DEBUG] dcm2niix_fswrapper::__setDcm2niixOpts(%s)\n", dcm2niixopts); + + char *restOpts = (char *)malloc(strlen(dcm2niixopts) + 1); + memset(restOpts, 0, strlen(dcm2niixopts) + 1); + memcpy(restOpts, dcm2niixopts, strlen(dcm2niixopts)); + + char *nextOpt = strtok_r((char *)dcm2niixopts, ",", &restOpts); + while (nextOpt != NULL) { + char *k = nextOpt; + char *v = strchr(nextOpt, '='); + if (v != NULL) + *v = '\0'; + v++; // move past '=' + + // skip leading white spaces + while (*k == ' ') + k++; + + if (strcmp(k, "b") == 0) { + if (*v == 'n' || *v == 'N' || *v == '0') + tdcmOpts.isCreateBIDS = false; + else if (*v == 'i' || *v == 'I') { + tdcmOpts.isCreateBIDS = false; + tdcmOpts.isOnlyBIDS = true; + } else if (*v == 'y' || *v == 'Y') + tdcmOpts.isCreateBIDS = true; + } else if (strcmp(k, "ba") == 0) + tdcmOpts.isAnonymizeBIDS = (*v == 'n' || *v == 'N') ? false : true; + else if (strcmp(k, "f") == 0) + strcpy(tdcmOpts.filename, v); + else if (strcmp(k, "i") == 0) + tdcmOpts.isIgnoreDerivedAnd2D = (*v == 'y' || *v == 'Y') ? true : false; + else if (strcmp(k, "m") == 0) { + if (*v == 'n' || *v == 'N' || *v == '0') + tdcmOpts.isForceStackSameSeries = 0; + else if (*v == 'y' || *v == 'Y' || *v == '1') + tdcmOpts.isForceStackSameSeries = 1; + else if (*v == '2') + tdcmOpts.isForceStackSameSeries = 2; + else if (*v == 'o' || *v == 'O') + tdcmOpts.isForceStackDCE = false; + } else if (strcmp(k, "v") == 0) { + if (*v == 'n' || *v == 'N' || *v == '0') + tdcmOpts.isVerbose = 0; + else if (*v == 'h' || *v == 'H' || *v == '2') + tdcmOpts.isVerbose = 2; + else + tdcmOpts.isVerbose = 1; + } else if (strcmp(k, "o") == 0) + strcpy(tdcmOpts.outdir, v); + else if (strcmp(k, "t") == 0) + tdcmOpts.isCreateText = (*v == 'y' || *v == 'Y') ? true : false; + else if (strcmp(k, "p") == 0) { + if (*v == 'n' || *v == 'N' || *v == '0') + tdcmOpts.isPhilipsFloatNotDisplayScaling = false; + } else if (strcmp(k, "x") == 0) { + if (*v == 'y' || *v == 'Y') + tdcmOpts.isCrop = true; + else if (*v == 'i' || *v == 'I') { + tdcmOpts.isRotate3DAcq = false; + tdcmOpts.isCrop = false; + } + } else { + printf("[WARN] dcm2niix option %s=%s skipped\n", k, v); + } + + nextOpt = strtok_r(NULL, ",", &restOpts); + } } // interface to isDICOMfile() in nii_dicom.cpp -bool dcm2niix_fswrapper::isDICOM(const char* file) -{ - return isDICOMfile(file); +bool dcm2niix_fswrapper::isDICOM(const char *file) { + return isDICOMfile(file); } /* * interface to nii_loadDirCore() to search all dicom files from the directory input file is in, * and convert dicom files with the same series as given file. */ -int dcm2niix_fswrapper::dcm2NiiOneSeries(const char* dcmfile) -{ - // get seriesNo for given dicom file - struct TDICOMdata tdicomData = readDICOM((char*)dcmfile); +int dcm2niix_fswrapper::dcm2NiiOneSeries(const char *dcmfile) { + // get seriesNo for given dicom file + struct TDICOMdata tdicomData = readDICOM((char *)dcmfile); - double seriesNo = (double)tdicomData.seriesUidCrc; - if (tdcmOpts.isIgnoreSeriesInstanceUID) - seriesNo = (double)tdicomData.seriesNum; + double seriesNo = (double)tdicomData.seriesUidCrc; + if (tdcmOpts.isIgnoreSeriesInstanceUID) + seriesNo = (double)tdicomData.seriesNum; - // set TDCMopts to convert just one series - tdcmOpts.seriesNumber[0] = seriesNo; - tdcmOpts.numSeries = 1; + // set TDCMopts to convert just one series + tdcmOpts.seriesNumber[0] = seriesNo; + tdcmOpts.numSeries = 1; - return nii_loadDirCore(tdcmOpts.indir, &tdcmOpts); + return nii_loadDirCore(tdcmOpts.indir, &tdcmOpts); } // interface to nii_getMrifsStruct() -MRIFSSTRUCT* dcm2niix_fswrapper::getMrifsStruct(void) -{ - return nii_getMrifsStruct(); +MRIFSSTRUCT *dcm2niix_fswrapper::getMrifsStruct(void) { + return nii_getMrifsStruct(); } // interface to nii_getMrifsStructVector() -std::vector* dcm2niix_fswrapper::getMrifsStructVector(void) -{ - return nii_getMrifsStructVector(); +std::vector *dcm2niix_fswrapper::getMrifsStructVector(void) { + return nii_getMrifsStructVector(); } // return nifti header saved in MRIFSSTRUCT -nifti_1_header* dcm2niix_fswrapper::getNiiHeader(void) -{ - MRIFSSTRUCT* mrifsStruct = getMrifsStruct(); - return &mrifsStruct->hdr0; +nifti_1_header *dcm2niix_fswrapper::getNiiHeader(void) { + MRIFSSTRUCT *mrifsStruct = getMrifsStruct(); + return &mrifsStruct->hdr0; } // return image data saved in MRIFSSTRUCT -const unsigned char* dcm2niix_fswrapper::getMRIimg(void) -{ - MRIFSSTRUCT* mrifsStruct = getMrifsStruct(); - return mrifsStruct->imgM; +const unsigned char *dcm2niix_fswrapper::getMRIimg(void) { + MRIFSSTRUCT *mrifsStruct = getMrifsStruct(); + return mrifsStruct->imgM; } -void dcm2niix_fswrapper::dicomDump(const char* dicomdir, const char *series_info, bool max, bool extrainfo) -{ - strcpy(tdcmOpts.indir, dicomdir); - tdcmOpts.isDumpNotConvert = true; - nii_loadDirCore(tdcmOpts.indir, &tdcmOpts); - - char fnamecopy[2048] = {'\0'}; - memcpy(fnamecopy, series_info, strlen(series_info)); - char *logdir = dirname(fnamecopy); - - FILE *fpout = fopen(series_info, "w"); - - std::vector *mrifsStruct_vector = dcm2niix_fswrapper::getMrifsStructVector(); - int nitems = (*mrifsStruct_vector).size(); - for (int n = 0; n < nitems; n++) - { - struct TDICOMdata *tdicomData = &(*mrifsStruct_vector)[n].tdicomData; - - // output the dicom list for the series into seriesNum-dicomflst.txt - char dicomflst[2048] = {'\0'}; - sprintf(dicomflst, "%s/%ld-dicomflst.txt", logdir, tdicomData->seriesNum); - FILE *fp_dcmLst = fopen(dicomflst, "w"); - for (int nDcm = 0; nDcm < (*mrifsStruct_vector)[n].nDcm; nDcm++) - fprintf(fp_dcmLst, "%s\n", (*mrifsStruct_vector)[n].dicomlst[nDcm]); - fclose(fp_dcmLst); - - // output series_info - fprintf(fpout, "%ld %s %f %f %f %f\\%f %c %f %s %s %s", - tdicomData->seriesNum, tdicomData->seriesDescription, - tdicomData->TE, tdicomData->TR, tdicomData->flipAngle, tdicomData->xyzMM[1], tdicomData->xyzMM[2], - tdicomData->phaseEncodingRC, tdicomData->pixelBandwidth, (*mrifsStruct_vector)[n].dicomfile, tdicomData->imageType, (*mrifsStruct_vector)[n].pulseSequenceDetails); +void dcm2niix_fswrapper::dicomDump(const char *dicomdir, const char *series_info, bool max, bool extrainfo) { + strcpy(tdcmOpts.indir, dicomdir); + tdcmOpts.isDumpNotConvert = true; + nii_loadDirCore(tdcmOpts.indir, &tdcmOpts); + + char fnamecopy[2048] = {'\0'}; + memcpy(fnamecopy, series_info, strlen(series_info)); + char *logdir = dirname(fnamecopy); + + FILE *fpout = fopen(series_info, "w"); + + std::vector *mrifsStruct_vector = dcm2niix_fswrapper::getMrifsStructVector(); + int nitems = (*mrifsStruct_vector).size(); + for (int n = 0; n < nitems; n++) { + struct TDICOMdata *tdicomData = &(*mrifsStruct_vector)[n].tdicomData; + + // output the dicom list for the series into seriesNum-dicomflst.txt + char dicomflst[2048] = {'\0'}; + sprintf(dicomflst, "%s/%ld-dicomflst.txt", logdir, tdicomData->seriesNum); + FILE *fp_dcmLst = fopen(dicomflst, "w"); + for (int nDcm = 0; nDcm < (*mrifsStruct_vector)[n].nDcm; nDcm++) + fprintf(fp_dcmLst, "%s\n", (*mrifsStruct_vector)[n].dicomlst[nDcm]); + fclose(fp_dcmLst); + + // output series_info + fprintf(fpout, "%ld %s %f %f %f %f\\%f %c %f %s %s %s", + tdicomData->seriesNum, tdicomData->seriesDescription, + tdicomData->TE, tdicomData->TR, tdicomData->flipAngle, tdicomData->xyzMM[1], tdicomData->xyzMM[2], + tdicomData->phaseEncodingRC, tdicomData->pixelBandwidth, (*mrifsStruct_vector)[n].dicomfile, tdicomData->imageType, (*mrifsStruct_vector)[n].pulseSequenceDetails); #if 0 if (max) { @@ -292,193 +267,190 @@ void dcm2niix_fswrapper::dicomDump(const char* dicomdir, const char *series_info fprintf(fpout, " max-value"); } #endif - if (extrainfo) - fprintf(fpout, " %s %s %s %s %f %s", tdicomData->patientName, tdicomData->studyDate, mfrCode2Str(tdicomData->manufacturer), tdicomData->manufacturersModelName, tdicomData->fieldStrength, tdicomData->deviceSerialNumber); + if (extrainfo) + fprintf(fpout, " %s %s %s %s %f %s", tdicomData->patientName, tdicomData->studyDate, mfrCode2Str(tdicomData->manufacturer), tdicomData->manufacturersModelName, tdicomData->fieldStrength, tdicomData->deviceSerialNumber); - fprintf(fpout, "\n"); - } - fclose(fpout); + fprintf(fpout, "\n"); + } + fclose(fpout); - return; + return; } -const char *dcm2niix_fswrapper::mfrCode2Str(int code) -{ - if (code == kMANUFACTURER_SIEMENS) - return "SIEMENS"; - else if (code == kMANUFACTURER_GE) - return "GE"; - else if (code == kMANUFACTURER_PHILIPS) - return "PHILIPS"; - else if (code == kMANUFACTURER_TOSHIBA) - return "TOSHIBA"; - else if (code == kMANUFACTURER_UIH) - return "UIH"; - else if (code == kMANUFACTURER_BRUKER) - return "BRUKER"; - else if (code == kMANUFACTURER_HITACHI) - return "HITACHI"; - else if (code == kMANUFACTURER_CANON) - return "CANON"; - else if (code == kMANUFACTURER_MEDISO) - return "MEDISO"; - else - return "UNKNOWN"; +const char *dcm2niix_fswrapper::mfrCode2Str(int code) { + if (code == kMANUFACTURER_SIEMENS) + return "SIEMENS"; + else if (code == kMANUFACTURER_GE) + return "GE"; + else if (code == kMANUFACTURER_PHILIPS) + return "PHILIPS"; + else if (code == kMANUFACTURER_TOSHIBA) + return "TOSHIBA"; + else if (code == kMANUFACTURER_UIH) + return "UIH"; + else if (code == kMANUFACTURER_BRUKER) + return "BRUKER"; + else if (code == kMANUFACTURER_HITACHI) + return "HITACHI"; + else if (code == kMANUFACTURER_CANON) + return "CANON"; + else if (code == kMANUFACTURER_MEDISO) + return "MEDISO"; + else + return "UNKNOWN"; } +void dcm2niix_fswrapper::seriesInfoDump(FILE *fpdump, const MRIFSSTRUCT *pmrifsStruct) { + // print dicom-info for the series converted using (*mrifsStruct_vector)[0] + // see output from mri_probedicom --i $f --no-siemens-ascii -void dcm2niix_fswrapper::seriesInfoDump(FILE *fpdump, const MRIFSSTRUCT *pmrifsStruct) -{ - // print dicom-info for the series converted using (*mrifsStruct_vector)[0] - // see output from mri_probedicom --i $f --no-siemens-ascii - - fprintf(fpdump, "###### %s ######\n", pmrifsStruct->dicomfile); + fprintf(fpdump, "###### %s ######\n", pmrifsStruct->dicomfile); - const struct TDICOMdata *tdicomData = &(pmrifsStruct->tdicomData); + const struct TDICOMdata *tdicomData = &(pmrifsStruct->tdicomData); - // kManufacturer 0x0008 + (0x0070 << 16) - fprintf(fpdump, "Manufacturer %s\n", dcm2niix_fswrapper::mfrCode2Str(tdicomData->manufacturer)); + // kManufacturer 0x0008 + (0x0070 << 16) + fprintf(fpdump, "Manufacturer %s\n", dcm2niix_fswrapper::mfrCode2Str(tdicomData->manufacturer)); - // kManufacturersModelName 0x0008 + (0x1090 << 16) - fprintf(fpdump, "ScannerModel %s\n", tdicomData->manufacturersModelName); + // kManufacturersModelName 0x0008 + (0x1090 << 16) + fprintf(fpdump, "ScannerModel %s\n", tdicomData->manufacturersModelName); - // kSoftwareVersions 0x0018 + (0x1020 << 16) //LO - fprintf(fpdump, "SoftwareVersion %s\n", tdicomData->softwareVersions); + // kSoftwareVersions 0x0018 + (0x1020 << 16) //LO + fprintf(fpdump, "SoftwareVersion %s\n", tdicomData->softwareVersions); - // kDeviceSerialNumber 0x0018 + (0x1000 << 16) //LO - fprintf(fpdump, "ScannerSerialNo %s\n", tdicomData->deviceSerialNumber); + // kDeviceSerialNumber 0x0018 + (0x1000 << 16) //LO + fprintf(fpdump, "ScannerSerialNo %s\n", tdicomData->deviceSerialNumber); - // kInstitutionName 0x0008 + (0x0080 << 16) - fprintf(fpdump, "Institution %s\n", tdicomData->institutionName); + // kInstitutionName 0x0008 + (0x0080 << 16) + fprintf(fpdump, "Institution %s\n", tdicomData->institutionName); - // kSeriesDescription 0x0008 + (0x103E << 16) // '0008' '103E' 'LO' 'SeriesDescription' - fprintf(fpdump, "SeriesDescription %s\n", tdicomData->seriesDescription); + // kSeriesDescription 0x0008 + (0x103E << 16) // '0008' '103E' 'LO' 'SeriesDescription' + fprintf(fpdump, "SeriesDescription %s\n", tdicomData->seriesDescription); - // kStudyInstanceUID 0x0020 + (0x000D << 16) - fprintf(fpdump, "StudyUID %s\n", tdicomData->studyInstanceUID); + // kStudyInstanceUID 0x0020 + (0x000D << 16) + fprintf(fpdump, "StudyUID %s\n", tdicomData->studyInstanceUID); - // kStudyDate 0x0008 + (0x0020 << 16) - fprintf(fpdump, "StudyDate %s\n", tdicomData->studyDate); + // kStudyDate 0x0008 + (0x0020 << 16) + fprintf(fpdump, "StudyDate %s\n", tdicomData->studyDate); - // kStudyTime 0x0008 + (0x0030 << 16) - fprintf(fpdump, "StudyTime %s\n", tdicomData->studyTime); + // kStudyTime 0x0008 + (0x0030 << 16) + fprintf(fpdump, "StudyTime %s\n", tdicomData->studyTime); - // kPatientName 0x0010 + (0x0010 << 16) - fprintf(fpdump, "PatientName %s\n", tdicomData->patientName); + // kPatientName 0x0010 + (0x0010 << 16) + fprintf(fpdump, "PatientName %s\n", tdicomData->patientName); - // kSeriesNum 0x0020 + (0x0011 << 16) - fprintf(fpdump, "SeriesNo %ld\n", tdicomData->seriesNum); + // kSeriesNum 0x0020 + (0x0011 << 16) + fprintf(fpdump, "SeriesNo %ld\n", tdicomData->seriesNum); - // kImageNum 0x0020 + (0x0013 << 16) - fprintf(fpdump, "ImageNo %d\n", tdicomData->imageNum); + // kImageNum 0x0020 + (0x0013 << 16) + fprintf(fpdump, "ImageNo %d\n", tdicomData->imageNum); - // kMRAcquisitionType 0x0018 + (0x0023 << 16) - fprintf(fpdump, "AcquisitionType %s\n", (tdicomData->is3DAcq) ? "3D" : ((tdicomData->is3DAcq) ? "2D" : "unknown")); //dcm2niix has two values: d.is2DAcq, d.is3DAcq + // kMRAcquisitionType 0x0018 + (0x0023 << 16) + fprintf(fpdump, "AcquisitionType %s\n", (tdicomData->is3DAcq) ? "3D" : ((tdicomData->is3DAcq) ? "2D" : "unknown")); // dcm2niix has two values: d.is2DAcq, d.is3DAcq - // kImageTypeTag 0x0008 + (0x0008 << 16) - fprintf(fpdump, "ImageType %s\n", tdicomData->imageType); + // kImageTypeTag 0x0008 + (0x0008 << 16) + fprintf(fpdump, "ImageType %s\n", tdicomData->imageType); - // kImagingFrequency 0x0018 + (0x0084 << 16) //DS - fprintf(fpdump, "ImagingFrequency %f\n", tdicomData->imagingFrequency); + // kImagingFrequency 0x0018 + (0x0084 << 16) //DS + fprintf(fpdump, "ImagingFrequency %f\n", tdicomData->imagingFrequency); - // kPixelBandwidth 0x0018 + (0x0095 << 16) //'DS' 'PixelBandwidth' - fprintf(fpdump, "PixelFrequency %f\n", tdicomData->pixelBandwidth); + // kPixelBandwidth 0x0018 + (0x0095 << 16) //'DS' 'PixelBandwidth' + fprintf(fpdump, "PixelFrequency %f\n", tdicomData->pixelBandwidth); - // dcm2niix doesn't seem to retrieve this 0x18, 0x85 - //fprintf(fpdump, "ImagedNucleus %s\n",e->d.string); + // dcm2niix doesn't seem to retrieve this 0x18, 0x85 + // fprintf(fpdump, "ImagedNucleus %s\n",e->d.string); - // kEchoNum 0x0018 + (0x0086 << 16) //IS - fprintf(fpdump, "EchoNumber %d\n", tdicomData->echoNum); + // kEchoNum 0x0018 + (0x0086 << 16) //IS + fprintf(fpdump, "EchoNumber %d\n", tdicomData->echoNum); - // kMagneticFieldStrength 0x0018 + (0x0087 << 16) //DS - fprintf(fpdump, "FieldStrength %f\n", tdicomData->fieldStrength); + // kMagneticFieldStrength 0x0018 + (0x0087 << 16) //DS + fprintf(fpdump, "FieldStrength %f\n", tdicomData->fieldStrength); - // kSequenceName 0x0018 + (0x0024 << 16) - fprintf(fpdump, "PulseSequence %s\n", tdicomData->sequenceName); + // kSequenceName 0x0018 + (0x0024 << 16) + fprintf(fpdump, "PulseSequence %s\n", tdicomData->sequenceName); - // kProtocolName 0x0018 + (0x1030 << 16) - fprintf(fpdump, "ProtocolName %s\n", tdicomData->protocolName); + // kProtocolName 0x0018 + (0x1030 << 16) + fprintf(fpdump, "ProtocolName %s\n", tdicomData->protocolName); - // kScanningSequence 0x0018 + (0x0020 << 16) - fprintf(fpdump, "ScanningSequence %s\n", tdicomData->scanningSequence); + // kScanningSequence 0x0018 + (0x0020 << 16) + fprintf(fpdump, "ScanningSequence %s\n", tdicomData->scanningSequence); - // dcm2niix doesn't seem to retrieve this - // kTransmitCoilName 0x0018 + (0x1251 << 16) // SH issue527 - //fprintf(fpdump, "TransmittingCoil %s\n",e->d.string); + // dcm2niix doesn't seem to retrieve this + // kTransmitCoilName 0x0018 + (0x1251 << 16) // SH issue527 + // fprintf(fpdump, "TransmittingCoil %s\n",e->d.string); - // kPatientOrient 0x0018 + (0x5100 << 16) //0018,5100. patient orientation - 'HFS' - fprintf(fpdump, "PatientPosition %s\n", tdicomData->patientOrient); + // kPatientOrient 0x0018 + (0x5100 << 16) //0018,5100. patient orientation - 'HFS' + fprintf(fpdump, "PatientPosition %s\n", tdicomData->patientOrient); - // kFlipAngle 0x0018 + (0x1314 << 16) - fprintf(fpdump, "FlipAngle %f\n", tdicomData->flipAngle); + // kFlipAngle 0x0018 + (0x1314 << 16) + fprintf(fpdump, "FlipAngle %f\n", tdicomData->flipAngle); - // kTE 0x0018 + (0x0081 << 16) - fprintf(fpdump, "EchoTime %f\n", tdicomData->TE); + // kTE 0x0018 + (0x0081 << 16) + fprintf(fpdump, "EchoTime %f\n", tdicomData->TE); - // kTR 0x0018 + (0x0080 << 16) - fprintf(fpdump, "RepetitionTime %f\n", tdicomData->TR); + // kTR 0x0018 + (0x0080 << 16) + fprintf(fpdump, "RepetitionTime %f\n", tdicomData->TR); - // kTI 0x0018 + (0x0082 << 16) // Inversion time - fprintf(fpdump, "InversionTime %f\n", tdicomData->TI); + // kTI 0x0018 + (0x0082 << 16) // Inversion time + fprintf(fpdump, "InversionTime %f\n", tdicomData->TI); - // kPhaseEncodingSteps 0x0018 + (0x0089 << 16) //'IS' - fprintf(fpdump, "NPhaseEnc %d\n", tdicomData->phaseEncodingSteps); + // kPhaseEncodingSteps 0x0018 + (0x0089 << 16) //'IS' + fprintf(fpdump, "NPhaseEnc %d\n", tdicomData->phaseEncodingSteps); - // kInPlanePhaseEncodingDirection 0x0018 + (0x1312 << 16) //CS - fprintf(fpdump, "PhaseEncDir %c\n", tdicomData->phaseEncodingRC); + // kInPlanePhaseEncodingDirection 0x0018 + (0x1312 << 16) //CS + fprintf(fpdump, "PhaseEncDir %c\n", tdicomData->phaseEncodingRC); - // kZSpacing 0x0018 + (0x0088 << 16) //'DS' 'SpacingBetweenSlices' - fprintf(fpdump, "SliceDistance %f\n", tdicomData->zSpacing); + // kZSpacing 0x0018 + (0x0088 << 16) //'DS' 'SpacingBetweenSlices' + fprintf(fpdump, "SliceDistance %f\n", tdicomData->zSpacing); - // kZThick 0x0018 + (0x0050 << 16) - fprintf(fpdump, "SliceThickness %f\n", tdicomData->zThick); // d.zThick=d.xyzMM[3] + // kZThick 0x0018 + (0x0050 << 16) + fprintf(fpdump, "SliceThickness %f\n", tdicomData->zThick); // d.zThick=d.xyzMM[3] - // kXYSpacing 0x0028 + (0x0030 << 16) //DS 'PixelSpacing' - fprintf(fpdump, "PixelSpacing %f\\%f\n", tdicomData->xyzMM[1], tdicomData->xyzMM[2]); + // kXYSpacing 0x0028 + (0x0030 << 16) //DS 'PixelSpacing' + fprintf(fpdump, "PixelSpacing %f\\%f\n", tdicomData->xyzMM[1], tdicomData->xyzMM[2]); - // kDim2 0x0028 + (0x0010 << 16) - fprintf(fpdump, "NRows %d\n", tdicomData->xyzDim[2]); + // kDim2 0x0028 + (0x0010 << 16) + fprintf(fpdump, "NRows %d\n", tdicomData->xyzDim[2]); - // kDim1 0x0028 + (0x0011 << 16) - fprintf(fpdump, "NCols %d\n", tdicomData->xyzDim[1]); + // kDim1 0x0028 + (0x0011 << 16) + fprintf(fpdump, "NCols %d\n", tdicomData->xyzDim[1]); - // kBitsAllocated 0x0028 + (0x0100 << 16) - fprintf(fpdump, "BitsPerPixel %d\n", tdicomData->bitsAllocated); + // kBitsAllocated 0x0028 + (0x0100 << 16) + fprintf(fpdump, "BitsPerPixel %d\n", tdicomData->bitsAllocated); - // dcm2niix doesn't seem to retrieve this - //fprintf(fpdump, "HighBit %d\n",*(e->d.us)); + // dcm2niix doesn't seem to retrieve this + // fprintf(fpdump, "HighBit %d\n",*(e->d.us)); - // dcm2niix doesn't seem to retrieve this - //fprintf(fpdump, "SmallestValue %d\n",*(e->d.us)); + // dcm2niix doesn't seem to retrieve this + // fprintf(fpdump, "SmallestValue %d\n",*(e->d.us)); - // dcm2niix doesn't seem to retrieve this - //fprintf(fpdump, "LargestValue %d\n",*(e->d.us)); + // dcm2niix doesn't seem to retrieve this + // fprintf(fpdump, "LargestValue %d\n",*(e->d.us)); - // kOrientation 0x0020 + (0x0037 << 16) - //fprintf(fpdump, "ImageOrientation %f\\%f\\%f\\%f\\%f\\%f\n", - fprintf(fpdump, "ImageOrientation %g\\%g\\%g\\%g\\%g\\%g\n", - tdicomData->orient[1], tdicomData->orient[2], tdicomData->orient[3], - tdicomData->orient[4], tdicomData->orient[5], tdicomData->orient[6]); // orient[7] ??? + // kOrientation 0x0020 + (0x0037 << 16) + // fprintf(fpdump, "ImageOrientation %f\\%f\\%f\\%f\\%f\\%f\n", + fprintf(fpdump, "ImageOrientation %g\\%g\\%g\\%g\\%g\\%g\n", + tdicomData->orient[1], tdicomData->orient[2], tdicomData->orient[3], + tdicomData->orient[4], tdicomData->orient[5], tdicomData->orient[6]); // orient[7] ??? - // kImagePositionPatient 0x0020 + (0x0032 << 16) // Actually ! patientPosition[4] ??? - fprintf(fpdump, "ImagePosition %f\\%f\\%f\n", - tdicomData->patientPosition[1], tdicomData->patientPosition[2], tdicomData->patientPosition[3]); //two values: d.patientPosition, d.patientPositionLast + // kImagePositionPatient 0x0020 + (0x0032 << 16) // Actually ! patientPosition[4] ??? + fprintf(fpdump, "ImagePosition %f\\%f\\%f\n", + tdicomData->patientPosition[1], tdicomData->patientPosition[2], tdicomData->patientPosition[3]); // two values: d.patientPosition, d.patientPositionLast - // dcm2niix doesn't seem to retrieve this - // kSliceLocation 0x0020+(0x1041 << 16 ) //DS would be useful if not optional type 3 - //case kSliceLocation : //optional so useless, infer from image position patient (0020,0032) and image orientation (0020,0037) - // sliceLocation = dcmStrFloat(lLength, &buffer[lPos]); - // break; - //fprintf(fpdump, "SliceLocation %s\n",e->d.string); + // dcm2niix doesn't seem to retrieve this + // kSliceLocation 0x0020+(0x1041 << 16 ) //DS would be useful if not optional type 3 + // case kSliceLocation : //optional so useless, infer from image position patient (0020,0032) and image orientation (0020,0037) + // sliceLocation = dcmStrFloat(lLength, &buffer[lPos]); + // break; + // fprintf(fpdump, "SliceLocation %s\n",e->d.string); - // kTransferSyntax 0x0002 + (0x0010 << 16) - fprintf(fpdump, "TransferSyntax %s\n", tdicomData->transferSyntax); + // kTransferSyntax 0x0002 + (0x0010 << 16) + fprintf(fpdump, "TransferSyntax %s\n", tdicomData->transferSyntax); - // dcm2niix doesn't seem to retrieve this 0x51, 0x1016 - //fprintf(fpdump, "SiemensCrit %s\n",e->d.string); + // dcm2niix doesn't seem to retrieve this 0x51, 0x1016 + // fprintf(fpdump, "SiemensCrit %s\n",e->d.string); #ifdef myDeidentificationMethod - // kDeidentificationMethod 0x0012 + (0x0063 << 16) // '0012' '0063' 'LO' 'DeidentificationMethod' - fprintf(fpdump, "DeidentificationMethod %s\n", tdicomData->deidentificationMethod); + // kDeidentificationMethod 0x0012 + (0x0063 << 16) // '0012' '0063' 'LO' 'DeidentificationMethod' + fprintf(fpdump, "DeidentificationMethod %s\n", tdicomData->deidentificationMethod); #endif // myDeidentificationMethod } diff --git a/console/dcm2niix_fswrapper.h b/console/dcm2niix_fswrapper.h index bfb417ad..443d2e9a 100644 --- a/console/dcm2niix_fswrapper.h +++ b/console/dcm2niix_fswrapper.h @@ -1,55 +1,54 @@ #ifndef DCM2NIIX_FSWRAPPER_H #define DCM2NIIX_FSWRAPPER_H -#include "nii_dicom_batch.h" #include "nii_dicom.h" +#include "nii_dicom_batch.h" /* * This is a wrapper class to interface with dcm2niix functions. * 1. The wrapper class provides interface to convert dicom in mgz orientation. - * 2. The wrapper class and dcm2niix functions are compiled into libdcm2niixfs.a + * 2. The wrapper class and dcm2niix functions are compiled into libdcm2niixfs.a * with -DUSING_DCM2NIIXFSWRAPPER -DUSING_MGH_NIFTI_IO. - * 3. When using libdcm2niixfs.a, instead of outputting .nii, *.bval, *.bvec to disk, + * 3. When using libdcm2niixfs.a, instead of outputting .nii, *.bval, *.bvec to disk, * nifti header, image data, TDICOMdata, & TDTI information are saved in MRIFSSTRUCT struct. * 4. If libdcm2niixfs.a is compiled with -DUSING_MGH_NIFTI_IO, the application needs to link with nifti library. */ -class dcm2niix_fswrapper -{ -public: - // set TDCMopts defaults, overwrite settings to output in mgz orientation. - static void setOpts(const char* dcmindir, const char* dcm2niixopts=NULL); +class dcm2niix_fswrapper { + public: + // set TDCMopts defaults, overwrite settings to output in mgz orientation. + static void setOpts(const char *dcmindir, const char *dcm2niixopts = NULL); - // interface to isDICOMfile() in nii_dicom.cpp - static bool isDICOM(const char* file); + // interface to isDICOMfile() in nii_dicom.cpp + static bool isDICOM(const char *file); - // interface to nii_loadDirCore() to search all dicom files from the directory input file is in, - // and convert dicom files with the same series as given file. - static int dcm2NiiOneSeries(const char* dcmfile); + // interface to nii_loadDirCore() to search all dicom files from the directory input file is in, + // and convert dicom files with the same series as given file. + static int dcm2NiiOneSeries(const char *dcmfile); - // interface to nii_getMrifsStruct() - static MRIFSSTRUCT* getMrifsStruct(void); + // interface to nii_getMrifsStruct() + static MRIFSSTRUCT *getMrifsStruct(void); - // return nifti header saved in MRIFSSTRUCT - static nifti_1_header* getNiiHeader(void); + // return nifti header saved in MRIFSSTRUCT + static nifti_1_header *getNiiHeader(void); - // interface to nii_getMrifsStructVector() - static std::vector* getMrifsStructVector(void); + // interface to nii_getMrifsStructVector() + static std::vector *getMrifsStructVector(void); - // return image data saved in MRIFSSTRUCT - static const unsigned char* getMRIimg(void); + // return image data saved in MRIFSSTRUCT + static const unsigned char *getMRIimg(void); - static void dicomDump(const char* dicomdir, const char *series_info, bool max=false, bool extrainfo=false); + static void dicomDump(const char *dicomdir, const char *series_info, bool max = false, bool extrainfo = false); - // - static void seriesInfoDump(FILE *fpdump, const MRIFSSTRUCT *pmrifsStruct); + // + static void seriesInfoDump(FILE *fpdump, const MRIFSSTRUCT *pmrifsStruct); - static const char *mfrCode2Str(int code); + static const char *mfrCode2Str(int code); -private: - static struct TDCMopts tdcmOpts; + private: + static struct TDCMopts tdcmOpts; - // - static void __setDcm2niixOpts(const char *dcm2niixopts); + // + static void __setDcm2niixOpts(const char *dcm2niixopts); }; #endif diff --git a/console/jpg_0XC3.cpp b/console/jpg_0XC3.cpp index 54e5dc4b..a8c50939 100644 --- a/console/jpg_0XC3.cpp +++ b/console/jpg_0XC3.cpp @@ -18,7 +18,7 @@ uint16_t readWord(unsigned char *lRawRA, long *lRawPos, long lRawSz) { return ((readByte(lRawRA, lRawPos, lRawSz) << 8) + readByte(lRawRA, lRawPos, lRawSz)); } // readWord() -int readBit(unsigned char *lRawRA, long *lRawPos, int *lCurrentBitPos) { //Read the next single bit +int readBit(unsigned char *lRawRA, long *lRawPos, int *lCurrentBitPos) { // Read the next single bit int result = (lRawRA[*lRawPos] >> (7 - *lCurrentBitPos)) & 1; (*lCurrentBitPos)++; if (*lCurrentBitPos == 8) { @@ -32,15 +32,15 @@ int bitMask(int bits) { return ((2 << (bits - 1)) - 1); } // bitMask() -int readBits(unsigned char *lRawRA, long *lRawPos, int *lCurrentBitPos, int lNum) { //lNum: bits to read, not to exceed 16 +int readBits(unsigned char *lRawRA, long *lRawPos, int *lCurrentBitPos, int lNum) { // lNum: bits to read, not to exceed 16 int result = lRawRA[*lRawPos]; result = (result << 8) + lRawRA[(*lRawPos) + 1]; result = (result << 8) + lRawRA[(*lRawPos) + 2]; - result = (result >> (24 - *lCurrentBitPos - lNum)) & bitMask(lNum); //lCurrentBitPos is incremented from 1, so -1 + result = (result >> (24 - *lCurrentBitPos - lNum)) & bitMask(lNum); // lCurrentBitPos is incremented from 1, so -1 *lCurrentBitPos = *lCurrentBitPos + lNum; if (*lCurrentBitPos > 7) { *lRawPos = *lRawPos + (*lCurrentBitPos >> 3); // div 8 - *lCurrentBitPos = *lCurrentBitPos & 7; //mod 8 + *lCurrentBitPos = *lCurrentBitPos & 7; // mod 8 } return result; } // readBits() @@ -65,26 +65,26 @@ int decodePixelDifference(unsigned char *lRawRA, long *lRawPos, int *lCurrentBit *lCurrentBitPos = l.SSSSszRA[lHufValSSSS] + *lCurrentBitPos; *lRawPos = *lRawPos + (*lCurrentBitPos >> 3); *lCurrentBitPos = *lCurrentBitPos & 7; - } else { //full SSSS is not in the first 8-bits + } else { // full SSSS is not in the first 8-bits int lInput = lByte; int lInputBits = 8; (*lRawPos)++; // forward 8 bits = precisely 1 byte do { lInputBits++; lInput = (lInput << 1) + readBit(lRawRA, lRawPos, lCurrentBitPos); - if (l.DHTliRA[lInputBits] != 0) { //if any entries with this length + if (l.DHTliRA[lInputBits] != 0) { // if any entries with this length for (int lI = l.DHTstartRA[lInputBits]; lI <= (l.DHTstartRA[lInputBits] + l.DHTliRA[lInputBits] - 1); lI++) { if (lInput == l.HufCode[lI]) lHufValSSSS = l.HufVal[lI]; - } //check each code - } //if any entries with this length - if ((lInputBits >= l.MaxHufSi) && (lHufValSSSS > 254)) { //exhausted options CR: added rev13 + } // check each code + } // if any entries with this length + if ((lInputBits >= l.MaxHufSi) && (lHufValSSSS > 254)) { // exhausted options CR: added rev13 lHufValSSSS = l.MaxHufVal; } } while (!(lHufValSSSS < 255)); // found; - } //answer in first 8 bits - //The HufVal is referred to as the SSSS in the Codec, so it is called 'lHufValSSSS' - if (lHufValSSSS == 0) //NO CHANGE + } // answer in first 8 bits + // The HufVal is referred to as the SSSS in the Codec, so it is called 'lHufValSSSS' + if (lHufValSSSS == 0) // NO CHANGE return 0; if (lHufValSSSS == 1) { if (readBit(lRawRA, lRawPos, lCurrentBitPos) == 0) @@ -92,47 +92,52 @@ int decodePixelDifference(unsigned char *lRawRA, long *lRawPos, int *lCurrentBit else return 1; } - if (lHufValSSSS == 16) { //ALL CHANGE 16 bit difference: Codec H.1.2.2 "No extra bits are appended after SSSS = 16 is encoded." Osiris fails here + if (lHufValSSSS == 16) { // ALL CHANGE 16 bit difference: Codec H.1.2.2 "No extra bits are appended after SSSS = 16 is encoded." Osiris fails here return 32768; } - //to get here - there is a 2..15 bit difference + // to get here - there is a 2..15 bit difference int lDiff = readBits(lRawRA, lRawPos, lCurrentBitPos, lHufValSSSS); - if (lDiff <= bitMask(lHufValSSSS - 1)) //add + if (lDiff <= bitMask(lHufValSSSS - 1)) // add lDiff = lDiff - bitMask(lHufValSSSS); return lDiff; } // decodePixelDifference() unsigned char *decode_JPEG_SOF_0XC3(const char *fn, int skipBytes, bool verbose, int *dimX, int *dimY, int *bits, int *frames, int diskBytes) { -//decompress JPEG image named "fn" where image data is located skipBytes into file. diskBytes is compressed size of image (set to 0 if unknown) -//next line breaks MSVC -#define abortGoto(...) do {printError(__VA_ARGS__); free(lRawRA); return NULL;} while(0) +// decompress JPEG image named "fn" where image data is located skipBytes into file. diskBytes is compressed size of image (set to 0 if unknown) +// next line breaks MSVC +#define abortGoto(...) \ + do { \ + printError(__VA_ARGS__); \ + free(lRawRA); \ + return NULL; \ + } while (0) unsigned char *lImgRA8 = NULL; FILE *reader = fopen(fn, "rb"); int lSuccess = fseek(reader, 0, SEEK_END); long lRawSz = ftell(reader) - skipBytes; - if ((diskBytes > 0) && (diskBytes < lRawSz)) //only if diskBytes is known and does not exceed length of file + if ((diskBytes > 0) && (diskBytes < lRawSz)) // only if diskBytes is known and does not exceed length of file lRawSz = diskBytes; if ((lSuccess != 0) || (lRawSz <= 8)) { printError("Unable to load 0XC3 JPEG %s\n", fn); - return NULL; //read failure + return NULL; // read failure } - lSuccess = fseek(reader, skipBytes, SEEK_SET); //If successful, the function returns zero + lSuccess = fseek(reader, skipBytes, SEEK_SET); // If successful, the function returns zero if (lSuccess != 0) { printError("Unable to open 0XC3 JPEG %s\n", fn); - return NULL; //read failure + return NULL; // read failure } unsigned char *lRawRA = (unsigned char *)malloc(lRawSz); size_t lSz = fread(lRawRA, 1, lRawSz, reader); fclose(reader); if ((lSz < (size_t)lRawSz) || (lRawRA[0] != 0xFF) || (lRawRA[1] != 0xD8) || (lRawRA[2] != 0xFF)) { - abortGoto("JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn); //signature failure http://en.wikipedia.org/wiki/List_of_file_signatures + abortGoto("JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn); // signature failure http://en.wikipedia.org/wiki/List_of_file_signatures } if (verbose) printMessage("JPEG signature 0xFFD8FF found at offset %d of %s\n", skipBytes, fn); - //next: read header - long lRawPos = 2; //Skip initial 0xFFD8, begin with third byte - //long lRawPos = 0; //Skip initial 0xFFD8, begin with third byte - unsigned char btS1, btS2, SOSse, SOSahal, btMarkerType, SOSns = 0x00; //tag + // next: read header + long lRawPos = 2; // Skip initial 0xFFD8, begin with third byte + // long lRawPos = 0; //Skip initial 0xFFD8, begin with third byte + unsigned char btS1, btS2, SOSse, SOSahal, btMarkerType, SOSns = 0x00; // tag unsigned char SOSpttrans = 0; unsigned char SOSss = 0; uint8_t SOFnf = 0; @@ -144,7 +149,7 @@ unsigned char *decode_JPEG_SOF_0XC3(const char *fn, int skipBytes, bool verbose, int lFrameCount = 1; const int kmaxFrames = 4; struct HufTables l[kmaxFrames + 1]; - do { //read each marker in the header + do { // read each marker in the header do { btS1 = readByte(lRawRA, &lRawPos, lRawSz); if (btS1 != 0xFF) { @@ -152,10 +157,10 @@ unsigned char *decode_JPEG_SOF_0XC3(const char *fn, int skipBytes, bool verbose, } btMarkerType = readByte(lRawRA, &lRawPos, lRawSz); if ((btMarkerType == 0x01) || (btMarkerType == 0xFF) || ((btMarkerType >= 0xD0) && (btMarkerType <= 0xD7))) - btMarkerType = 0; //only process segments with length fields + btMarkerType = 0; // only process segments with length fields } while ((lRawPos < lRawSz) && (btMarkerType == 0)); - uint16_t lSegmentLength = readWord(lRawRA, &lRawPos, lRawSz); //read marker length + uint16_t lSegmentLength = readWord(lRawRA, &lRawPos, lRawSz); // read marker length long lSegmentEnd = lRawPos + (lSegmentLength - 2); if (lSegmentEnd > lRawSz) { abortGoto("Segment larger than image\n"); @@ -163,27 +168,27 @@ unsigned char *decode_JPEG_SOF_0XC3(const char *fn, int skipBytes, bool verbose, if (verbose) printMessage("btMarkerType %#02X length %d@%ld\n", btMarkerType, lSegmentLength, lRawPos); if (((btMarkerType >= 0xC0) && (btMarkerType <= 0xC3)) || ((btMarkerType >= 0xC5) && (btMarkerType <= 0xCB)) || ((btMarkerType >= 0xCD) && (btMarkerType <= 0xCF))) { - //if Start-Of-Frame (SOF) marker + // if Start-Of-Frame (SOF) marker SOFprecision = readByte(lRawRA, &lRawPos, lRawSz); SOFydim = readWord(lRawRA, &lRawPos, lRawSz); SOFxdim = readWord(lRawRA, &lRawPos, lRawSz); SOFnf = readByte(lRawRA, &lRawPos, lRawSz); - //SOFarrayPos = lRawPos; + // SOFarrayPos = lRawPos; lRawPos = (lSegmentEnd); if (verbose) printMessage(" [Precision %d X*Y %d*%d Frames %d]\n", SOFprecision, SOFxdim, SOFydim, SOFnf); - if (btMarkerType != 0xC3) { //lImgTypeC3 = true; + if (btMarkerType != 0xC3) { // lImgTypeC3 = true; abortGoto("This JPEG decoder can only decompress lossless JPEG ITU-T81 images (SoF must be 0XC3, not %#02X)\n", btMarkerType); } if ((SOFprecision < 1) || (SOFprecision > 16) || (SOFnf < 1) || (SOFnf == 2) || (SOFnf > 3) || ((SOFnf == 3) && (SOFprecision > 8))) { abortGoto("Scalar data must be 1..16 bit, RGB data must be 8-bit (%d-bit, %d frames)\n", SOFprecision, SOFnf); } - } else if (btMarkerType == 0xC4) { //if SOF marker else if define-Huffman-tables marker (DHT) + } else if (btMarkerType == 0xC4) { // if SOF marker else if define-Huffman-tables marker (DHT) if (verbose) printMessage(" [Huffman Length %d]\n", lSegmentLength); do { - uint8_t DHTnLi = readByte(lRawRA, &lRawPos, lRawSz); //we read but ignore DHTtcth. -#pragma unused(DHTnLi) //we need to increment the input file position, but we do not care what the value is + uint8_t DHTnLi = readByte(lRawRA, &lRawPos, lRawSz); // we read but ignore DHTtcth. +#pragma unused(DHTnLi) // we need to increment the input file position, but we do not care what the value is DHTnLi = 0; for (int lInc = 1; lInc <= 16; lInc++) { l[lFrameCount].DHTliRA[lInc] = readByte(lRawRA, &lRawPos, lRawSz); @@ -196,13 +201,13 @@ unsigned char *decode_JPEG_SOF_0XC3(const char *fn, int skipBytes, bool verbose, if (DHTnLi > 17) { abortGoto("Huffman table corrupted.\n"); } - int lIncY = 0; //frequency - for (int lInc = 0; lInc <= 31; lInc++) { //lInc := 0 to 31 do begin + int lIncY = 0; // frequency + for (int lInc = 0; lInc <= 31; lInc++) { // lInc := 0 to 31 do begin l[lFrameCount].HufVal[lInc] = -1; l[lFrameCount].HufSz[lInc] = -1; l[lFrameCount].HufCode[lInc] = -1; } - for (int lInc = 1; lInc <= 16; lInc++) { //set the huffman size values + for (int lInc = 1; lInc <= 16; lInc++) { // set the huffman size values if (l[lFrameCount].DHTliRA[lInc] > 0) { l[lFrameCount].DHTstartRA[lInc] = lIncY + 1; for (int lIncX = 1; lIncX <= l[lFrameCount].DHTliRA[lInc]; lIncX++) { @@ -212,14 +217,14 @@ unsigned char *decode_JPEG_SOF_0XC3(const char *fn, int skipBytes, bool verbose, l[lFrameCount].MaxHufVal = btS1; if (verbose) printMessage("DHT combination %d has a value of %d\n", lIncY, btS1); - if (btS1 <= 16) //unsigned ints ALWAYS >0, so no need for(btS1 >= 0) + if (btS1 <= 16) // unsigned ints ALWAYS >0, so no need for(btS1 >= 0) l[lFrameCount].HufSz[lIncY] = lInc; else { abortGoto("Huffman size array corrupted.\n"); } } } - } //set huffman size values + } // set huffman size values int K = 1; int Code = 0; int Si = l[lFrameCount].HufSz[K]; @@ -231,278 +236,278 @@ unsigned char *decode_JPEG_SOF_0XC3(const char *fn, int skipBytes, bool verbose, } if (K <= DHTnLi) { while (l[lFrameCount].HufSz[K] > Si) { - Code = Code << 1; //Shl!!! + Code = Code << 1; // Shl!!! Si = Si + 1; - } //while Si - } //K <= 17 + } // while Si + } // K <= 17 } while (K <= DHTnLi); - //if (verbose) - // for (int j = 1; j <= DHTnLi; j++) - // printMessage(" [%d Sz %d Code %d Value %d]\n", j, l[lFrameCount].HufSz[j], l[lFrameCount].HufCode[j], l[lFrameCount].HufVal[j]); + // if (verbose) + // for (int j = 1; j <= DHTnLi; j++) + // printMessage(" [%d Sz %d Code %d Value %d]\n", j, l[lFrameCount].HufSz[j], l[lFrameCount].HufCode[j], l[lFrameCount].HufVal[j]); lFrameCount++; } while ((lSegmentEnd - lRawPos) >= 18); lnHufTables = lFrameCount - 1; lRawPos = (lSegmentEnd); if (verbose) printMessage(" [FrameCount %d]\n", lnHufTables); - } else if (btMarkerType == 0xDD) { //if DHT marker else if Define restart interval (DRI) marker + } else if (btMarkerType == 0xDD) { // if DHT marker else if Define restart interval (DRI) marker abortGoto("btMarkerType == 0xDD: unsupported Restart Segments\n"); - //lRestartSegmentSz = ReadWord(lRawRA, &lRawPos, lRawSz); - //lRawPos = lSegmentEnd; - } else if (btMarkerType == 0xDA) { //if DRI marker else if read Start of Scan (SOS) marker + // lRestartSegmentSz = ReadWord(lRawRA, &lRawPos, lRawSz); + // lRawPos = lSegmentEnd; + } else if (btMarkerType == 0xDA) { // if DRI marker else if read Start of Scan (SOS) marker SOSns = readByte(lRawRA, &lRawPos, lRawSz); - //if Ns = 1 then NOT interleaved, else interleaved: see B.2.3 - // SOSarrayPos = lRawPos; //not required... + // if Ns = 1 then NOT interleaved, else interleaved: see B.2.3 + // SOSarrayPos = lRawPos; //not required... if (SOSns > 0) { for (int lInc = 1; lInc <= SOSns; lInc++) { - btS1 = readByte(lRawRA, &lRawPos, lRawSz); //component identifier 1=Y,2=Cb,3=Cr,4=I,5=Q -#pragma unused(btS1) //dummy value used to increment file position - btS2 = readByte(lRawRA, &lRawPos, lRawSz); //horizontal and vertical sampling factors -#pragma unused(btS2) //dummy value used to increment file position + btS1 = readByte(lRawRA, &lRawPos, lRawSz); // component identifier 1=Y,2=Cb,3=Cr,4=I,5=Q +#pragma unused(btS1) // dummy value used to increment file position + btS2 = readByte(lRawRA, &lRawPos, lRawSz); // horizontal and vertical sampling factors +#pragma unused(btS2) // dummy value used to increment file position } } - SOSss = readByte(lRawRA, &lRawPos, lRawSz); //predictor selection B.3 + SOSss = readByte(lRawRA, &lRawPos, lRawSz); // predictor selection B.3 SOSse = readByte(lRawRA, &lRawPos, lRawSz); -#pragma unused(SOSse) //dummy value used to increment file position - SOSahal = readByte(lRawRA, &lRawPos, lRawSz); //lower 4bits= pointtransform +#pragma unused(SOSse) // dummy value used to increment file position + SOSahal = readByte(lRawRA, &lRawPos, lRawSz); // lower 4bits= pointtransform SOSpttrans = SOSahal & 16; if (verbose) printMessage(" [Predictor: %d Transform %d]\n", SOSss, SOSahal); lRawPos = (lSegmentEnd); - } else //if SOS marker else skip marker + } else // if SOS marker else skip marker lRawPos = (lSegmentEnd); - } while ((lRawPos < lRawSz) && (btMarkerType != 0xDA)); //0xDA=Start of scan: loop for reading header - //NEXT: Huffman decoding + } while ((lRawPos < lRawSz) && (btMarkerType != 0xDA)); // 0xDA=Start of scan: loop for reading header + // NEXT: Huffman decoding if (lnHufTables < 1) { abortGoto("Decoding error: no Huffman tables.\n"); } - //NEXT: unpad data - delete byte that follows $FF - //int lIsRestartSegments = 0; - long lIncI = lRawPos; //input position - long lIncO = lRawPos; //output position + // NEXT: unpad data - delete byte that follows $FF + // int lIsRestartSegments = 0; + long lIncI = lRawPos; // input position + long lIncO = lRawPos; // output position do { lRawRA[lIncO] = lRawRA[lIncI]; if (lRawRA[lIncI] == 255) { if (lRawRA[lIncI + 1] == 0) lIncI = lIncI + 1; else if (lRawRA[lIncI + 1] == 0xD9) - lIncO = -666; //end of padding - //else - // lIsRestartSegments = lRawRA[lIncI+1]; + lIncO = -666; // end of padding + // else + // lIsRestartSegments = lRawRA[lIncI+1]; } lIncI++; lIncO++; } while (lIncO > 0); - //if (lIsRestartSegments != 0) //detects both restart and corruption https://groups.google.com/forum/#!topic/comp.protocols.dicom/JUuz0B_aE5o - // printWarning("Detected restart segments, decompress with dcmdjpeg or gdcmconv 0xFF%02X.\n", lIsRestartSegments); - //NEXT: some RGB images use only a single Huffman table for all 3 colour planes. In this case, replicate the correct values - //NEXT: prepare lookup table + // if (lIsRestartSegments != 0) //detects both restart and corruption https://groups.google.com/forum/#!topic/comp.protocols.dicom/JUuz0B_aE5o + // printWarning("Detected restart segments, decompress with dcmdjpeg or gdcmconv 0xFF%02X.\n", lIsRestartSegments); + // NEXT: some RGB images use only a single Huffman table for all 3 colour planes. In this case, replicate the correct values + // NEXT: prepare lookup table for (int lFrameCount = 1; lFrameCount <= lnHufTables; lFrameCount++) { for (int lInc = 0; lInc <= 17; lInc++) - l[lFrameCount].SSSSszRA[lInc] = 123; //Impossible value for SSSS, suggests 8-bits can not describe answer + l[lFrameCount].SSSSszRA[lInc] = 123; // Impossible value for SSSS, suggests 8-bits can not describe answer for (int lInc = 0; lInc <= 255; lInc++) - l[lFrameCount].LookUpRA[lInc] = 255; //Impossible value for SSSS, suggests 8-bits can not describe answer + l[lFrameCount].LookUpRA[lInc] = 255; // Impossible value for SSSS, suggests 8-bits can not describe answer } - //NEXT: fill lookuptable + // NEXT: fill lookuptable for (int lFrameCount = 1; lFrameCount <= lnHufTables; lFrameCount++) { int lIncY = 0; - for (int lSz = 1; lSz <= 8; lSz++) { //set the huffman lookup table for keys with lengths <=8 + for (int lSz = 1; lSz <= 8; lSz++) { // set the huffman lookup table for keys with lengths <=8 if (l[lFrameCount].DHTliRA[lSz] > 0) { for (int lIncX = 1; lIncX <= l[lFrameCount].DHTliRA[lSz]; lIncX++) { lIncY++; - int lHufVal = l[lFrameCount].HufVal[lIncY]; //SSSS + int lHufVal = l[lFrameCount].HufVal[lIncY]; // SSSS l[lFrameCount].SSSSszRA[lHufVal] = lSz; - int k = (l[lFrameCount].HufCode[lIncY] << (8 - lSz)) & 255; //K= most sig bits for hufman table - if (lSz < 8) { //fill in all possible bits that exceed the huffman table + int k = (l[lFrameCount].HufCode[lIncY] << (8 - lSz)) & 255; // K= most sig bits for hufman table + if (lSz < 8) { // fill in all possible bits that exceed the huffman table int lInc = bitMask(8 - lSz); for (int lCurrentBitPos = 0; lCurrentBitPos <= lInc; lCurrentBitPos++) { l[lFrameCount].LookUpRA[k + lCurrentBitPos] = lHufVal; } } else - l[lFrameCount].LookUpRA[k] = lHufVal; //SSSS - //printMessage("Frame %d SSSS %d Size %d Code %d SHL %d EmptyBits %ld\n", lFrameCount, lHufRA[lFrameCount][lIncY].HufVal, lHufRA[lFrameCount][lIncY].HufSz,lHufRA[lFrameCount][lIncY].HufCode, k, lInc); - } //Set SSSS - } //Length of size lInc > 0 - } //for lInc := 1 to 8 - } //For each frame, e.g. once each for Red/Green/Blue - //NEXT: some RGB images use only a single Huffman table for all 3 colour planes. In this case, replicate the correct values - if (lnHufTables < SOFnf) { //use single Hufman table for each frame + l[lFrameCount].LookUpRA[k] = lHufVal; // SSSS + // printMessage("Frame %d SSSS %d Size %d Code %d SHL %d EmptyBits %ld\n", lFrameCount, lHufRA[lFrameCount][lIncY].HufVal, lHufRA[lFrameCount][lIncY].HufSz,lHufRA[lFrameCount][lIncY].HufCode, k, lInc); + } // Set SSSS + } // Length of size lInc > 0 + } // for lInc := 1 to 8 + } // For each frame, e.g. once each for Red/Green/Blue + // NEXT: some RGB images use only a single Huffman table for all 3 colour planes. In this case, replicate the correct values + if (lnHufTables < SOFnf) { // use single Hufman table for each frame for (int lFrameCount = lnHufTables + 1; lFrameCount <= SOFnf; lFrameCount++) { l[lFrameCount] = l[lnHufTables]; - } //for each frame - } // if lnHufTables < SOFnf - //NEXT: uncompress data: different loops for different predictors + } // for each frame + } // if lnHufTables < SOFnf + // NEXT: uncompress data: different loops for different predictors int lItems = SOFxdim * SOFydim * SOFnf; // lRawPos++;// <- only for Pascal where array is indexed from 1 not 0 first byte of data - int lCurrentBitPos = 0; //read in a new byte - //depending on SOSss, we see Table H.1 + int lCurrentBitPos = 0; // read in a new byte + // depending on SOSss, we see Table H.1 int lPredA = 0; int lPredB = 0; int lPredC = 0; - if (SOSss == 2) //predictor selection 2: above + if (SOSss == 2) // predictor selection 2: above lPredA = SOFxdim - 1; - else if (SOSss == 3) //predictor selection 3: above+left + else if (SOSss == 3) // predictor selection 3: above+left lPredA = SOFxdim; - else if ((SOSss == 4) || (SOSss == 5)) { //these use left, above and above+left WEIGHT LEFT - lPredA = 0; //Ra left - lPredB = SOFxdim - 1; //Rb directly above - lPredC = SOFxdim; //Rc UpperLeft:above and to the left - } else if (SOSss == 6) { //also use left, above and above+left, WEIGHT ABOVE + else if ((SOSss == 4) || (SOSss == 5)) { // these use left, above and above+left WEIGHT LEFT + lPredA = 0; // Ra left + lPredB = SOFxdim - 1; // Rb directly above + lPredC = SOFxdim; // Rc UpperLeft:above and to the left + } else if (SOSss == 6) { // also use left, above and above+left, WEIGHT ABOVE lPredB = 0; - lPredA = SOFxdim - 1; //Rb directly above - lPredC = SOFxdim; //Rc UpperLeft:above and to the left + lPredA = SOFxdim - 1; // Rb directly above + lPredC = SOFxdim; // Rc UpperLeft:above and to the left } else - lPredA = 0; //Ra: directly to left) - if (SOFprecision > 8) { //start - 16 bit data + lPredA = 0; // Ra: directly to left) + if (SOFprecision > 8) { // start - 16 bit data *bits = 16; - int lPx = -1; //pixel position + int lPx = -1; // pixel position int lPredicted = 1 << (SOFprecision - 1 - SOSpttrans); lImgRA8 = (unsigned char *)malloc(lItems * 2); uint16_t *lImgRA16 = (uint16_t *)lImgRA8; for (int i = 0; i < lItems; i++) - lImgRA16[i] = 0; //zero array + lImgRA16[i] = 0; // zero array int frame = 1; - for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { //for first row - here we ALWAYS use LEFT as predictor - lPx++; //writenext voxel + for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { // for first row - here we ALWAYS use LEFT as predictor + lPx++; // writenext voxel if (lIncX > 1) lPredicted = lImgRA16[lPx - 1]; lImgRA16[lPx] = lPredicted + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); } - for (int lIncY = 2; lIncY <= SOFydim; lIncY++) { //for all subsequent rows - lPx++; //write next voxel - lPredicted = lImgRA16[lPx - SOFxdim]; //use ABOVE + for (int lIncY = 2; lIncY <= SOFydim; lIncY++) { // for all subsequent rows + lPx++; // write next voxel + lPredicted = lImgRA16[lPx - SOFxdim]; // use ABOVE lImgRA16[lPx] = lPredicted + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); if (SOSss == 4) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA16[lPx - lPredA] + lImgRA16[lPx - lPredB] - lImgRA16[lPx - lPredC]; - lPx++; //writenext voxel + lPx++; // writenext voxel lImgRA16[lPx] = lPredicted + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); - } //for lIncX + } // for lIncX } else if ((SOSss == 5) || (SOSss == 6)) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA16[lPx - lPredA] + ((lImgRA16[lPx - lPredB] - lImgRA16[lPx - lPredC]) >> 1); - lPx++; //writenext voxel + lPx++; // writenext voxel lImgRA16[lPx] = lPredicted + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); - } //for lIncX + } // for lIncX } else if (SOSss == 7) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { - lPx++; //writenext voxel + lPx++; // writenext voxel lPredicted = (lImgRA16[lPx - 1] + lImgRA16[lPx - SOFxdim]) >> 1; lImgRA16[lPx] = lPredicted + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); - } //for lIncX - } else { //SOSss 1,2,3 read single values + } // for lIncX + } else { // SOSss 1,2,3 read single values for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA16[lPx - lPredA]; - lPx++; //writenext voxel + lPx++; // writenext voxel lImgRA16[lPx] = lPredicted + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]); - } //for lIncX - } // if..else possible predictors - } //for lIncY - } else if (SOFnf == 3) { //if 16-bit data; else 8-bit 3 frames + } // for lIncX + } // if..else possible predictors + } // for lIncY + } else if (SOFnf == 3) { // if 16-bit data; else 8-bit 3 frames *bits = 8; lImgRA8 = (unsigned char *)malloc(lItems); - int lPx[kmaxFrames + 1], lPredicted[kmaxFrames + 1]; //pixel position + int lPx[kmaxFrames + 1], lPredicted[kmaxFrames + 1]; // pixel position for (int f = 1; f <= SOFnf; f++) { lPx[f] = ((f - 1) * (SOFxdim * SOFydim)) - 1; lPredicted[f] = 1 << (SOFprecision - 1 - SOSpttrans); } for (int i = 0; i < lItems; i++) - lImgRA8[i] = 255; //zero array - for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { //for first row - here we ALWAYS use LEFT as predictor + lImgRA8[i] = 255; // zero array + for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { // for first row - here we ALWAYS use LEFT as predictor for (int f = 1; f <= SOFnf; f++) { - lPx[f]++; //writenext voxel + lPx[f]++; // writenext voxel if (lIncX > 1) lPredicted[f] = lImgRA8[lPx[f] - 1]; lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); } - } //first row always predicted by LEFT - for (int lIncY = 2; lIncY <= SOFydim; lIncY++) { //for all subsequent rows + } // first row always predicted by LEFT + for (int lIncY = 2; lIncY <= SOFydim; lIncY++) { // for all subsequent rows for (int f = 1; f <= SOFnf; f++) { - lPx[f]++; //write next voxel - lPredicted[f] = lImgRA8[lPx[f] - SOFxdim]; //use ABOVE + lPx[f]++; // write next voxel + lPredicted[f] = lImgRA8[lPx[f] - SOFxdim]; // use ABOVE lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); - } //first column of row always predicted by ABOVE + } // first column of row always predicted by ABOVE if (SOSss == 4) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { for (int f = 1; f <= SOFnf; f++) { lPredicted[f] = lImgRA8[lPx[f] - lPredA] + lImgRA8[lPx[f] - lPredB] - lImgRA8[lPx[f] - lPredC]; - lPx[f]++; //writenext voxel + lPx[f]++; // writenext voxel lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); } - } //for lIncX + } // for lIncX } else if ((SOSss == 5) || (SOSss == 6)) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { for (int f = 1; f <= SOFnf; f++) { lPredicted[f] = lImgRA8[lPx[f] - lPredA] + ((lImgRA8[lPx[f] - lPredB] - lImgRA8[lPx[f] - lPredC]) >> 1); - lPx[f]++; //writenext voxel + lPx[f]++; // writenext voxel lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); } - } //for lIncX + } // for lIncX } else if (SOSss == 7) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { for (int f = 1; f <= SOFnf; f++) { - lPx[f]++; //writenext voxel + lPx[f]++; // writenext voxel lPredicted[f] = (lImgRA8[lPx[f] - 1] + lImgRA8[lPx[f] - SOFxdim]) >> 1; lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); } - } //for lIncX - } else { //SOSss 1,2,3 read single values + } // for lIncX + } else { // SOSss 1,2,3 read single values for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { for (int f = 1; f <= SOFnf; f++) { lPredicted[f] = lImgRA8[lPx[f] - lPredA]; - lPx[f]++; //writenext voxel + lPx[f]++; // writenext voxel lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]); } - } //for lIncX - } // if..else possible predictors - } //for lIncY - } else { //if 8-bit data 3frames; else 8-bit 1 frames + } // for lIncX + } // if..else possible predictors + } // for lIncY + } else { // if 8-bit data 3frames; else 8-bit 1 frames *bits = 8; lImgRA8 = (unsigned char *)malloc(lItems); - int lPx = -1; //pixel position + int lPx = -1; // pixel position int lPredicted = 1 << (SOFprecision - 1 - SOSpttrans); for (int i = 0; i < lItems; i++) - lImgRA8[i] = 0; //zero array - for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { //for first row - here we ALWAYS use LEFT as predictor - lPx++; //writenext voxel + lImgRA8[i] = 0; // zero array + for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { // for first row - here we ALWAYS use LEFT as predictor + lPx++; // writenext voxel if (lIncX > 1) lPredicted = lImgRA8[lPx - 1]; int dx = decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); lImgRA8[lPx] = lPredicted + dx; } - for (int lIncY = 2; lIncY <= SOFydim; lIncY++) { //for all subsequent rows - lPx++; //write next voxel - lPredicted = lImgRA8[lPx - SOFxdim]; //use ABOVE + for (int lIncY = 2; lIncY <= SOFydim; lIncY++) { // for all subsequent rows + lPx++; // write next voxel + lPredicted = lImgRA8[lPx - SOFxdim]; // use ABOVE lImgRA8[lPx] = lPredicted + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); if (SOSss == 4) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA8[lPx - lPredA] + lImgRA8[lPx - lPredB] - lImgRA8[lPx - lPredC]; - lPx++; //writenext voxel + lPx++; // writenext voxel lImgRA8[lPx] = lPredicted + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); - } //for lIncX + } // for lIncX } else if ((SOSss == 5) || (SOSss == 6)) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA8[lPx - lPredA] + ((lImgRA8[lPx - lPredB] - lImgRA8[lPx - lPredC]) >> 1); - lPx++; //writenext voxel + lPx++; // writenext voxel lImgRA8[lPx] = lPredicted + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); - } //for lIncX + } // for lIncX } else if (SOSss == 7) { for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { - lPx++; //writenext voxel + lPx++; // writenext voxel lPredicted = (lImgRA8[lPx - 1] + lImgRA8[lPx - SOFxdim]) >> 1; lImgRA8[lPx] = lPredicted + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); - } //for lIncX - } else { //SOSss 1,2,3 read single values + } // for lIncX + } else { // SOSss 1,2,3 read single values for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) { lPredicted = lImgRA8[lPx - lPredA]; - lPx++; //writenext voxel + lPx++; // writenext voxel lImgRA8[lPx] = lPredicted + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]); - } //for lIncX + } // for lIncX } // if..else possible predictors - } //for lIncY - } //if 16bit else 8bit + } // for lIncY + } // if 16bit else 8bit free(lRawRA); *dimX = SOFxdim; *dimY = SOFydim; diff --git a/console/jpg_0XC3.h b/console/jpg_0XC3.h index d04b667c..3e2daf78 100644 --- a/console/jpg_0XC3.h +++ b/console/jpg_0XC3.h @@ -1,22 +1,22 @@ -//Decode DICOM Transfer Syntax 1.2.840.10008.1.2.4.70 and 1.2.840.10008.1.2.4.57 -// JPEG Lossless, Nonhierarchical -// see ISO/IEC 10918-1 / ITU T.81 -// specifically, format with 'Start of Frame' (SOF) code 0xC3 -// http://www.w3.org/Graphics/JPEG/itu-t81.pdf -// This code decodes data with 1..16 bits per pixel -// It appears unique to medical imaging, and is not supported by most JPEG libraries -// http://www.dicomlibrary.com/dicom/transfer-syntax/ -// https://en.wikipedia.org/wiki/Lossless_JPEG#Lossless_mode_of_operation +// Decode DICOM Transfer Syntax 1.2.840.10008.1.2.4.70 and 1.2.840.10008.1.2.4.57 +// JPEG Lossless, Nonhierarchical +// see ISO/IEC 10918-1 / ITU T.81 +// specifically, format with 'Start of Frame' (SOF) code 0xC3 +// http://www.w3.org/Graphics/JPEG/itu-t81.pdf +// This code decodes data with 1..16 bits per pixel +// It appears unique to medical imaging, and is not supported by most JPEG libraries +// http://www.dicomlibrary.com/dicom/transfer-syntax/ +// https://en.wikipedia.org/wiki/Lossless_JPEG#Lossless_mode_of_operation #ifndef _JPEG_SOF_0XC3_ #define _JPEG_SOF_0XC3_ -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif -unsigned char * decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbose, int *dimX, int *dimY, int *bits, int *frames, int diskBytes); +unsigned char *decode_JPEG_SOF_0XC3(const char *fn, int skipBytes, bool verbose, int *dimX, int *dimY, int *bits, int *frames, int diskBytes); -#ifdef __cplusplus +#ifdef __cplusplus } #endif diff --git a/console/main_console.cpp b/console/main_console.cpp index e87c1481..76fdca52 100644 --- a/console/main_console.cpp +++ b/console/main_console.cpp @@ -1,30 +1,30 @@ -//main_console.cpp dcm2niix -// by Chris Rorden on 3/22/14, see license.txt -// Copyright (c) 2014-2021 Chris Rorden. All rights reserved. +// main_console.cpp dcm2niix +// by Chris Rorden on 3/22/14, see license.txt +// Copyright (c) 2014-2021 Chris Rorden. All rights reserved. -//g++ -O3 main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -s -o dcm2niix -lz +// g++ -O3 main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -s -o dcm2niix -lz -//if you do not have zlib,you can compile without it -// g++ -O3 -DmyDisableZLib main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -s -o dcm2niix -//or you can build your own copy: -// to compile you will first want to build the Z library, then compile the project -// cd zlib-1.2.8 -// sudo ./configure; -// sudo make +// if you do not have zlib,you can compile without it +// g++ -O3 -DmyDisableZLib main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -s -o dcm2niix +// or you can build your own copy: +// to compile you will first want to build the Z library, then compile the project +// cd zlib-1.2.8 +// sudo ./configure; +// sudo make -//to generate combined 32-bit and 64-bit builds for OSX : -// g++ -O3 -x c++ main_console.c nii_dicom.c nifti1_io_core.c nii_ortho.c nii_dicom_batch.c -s -arch x86_64 -o dcm2niix64 -lz -// g++ -O3 -x c++ main_console.c nii_dicom.c nifti1_io_core.c nii_ortho.c nii_dicom_batch.c -s -arch i386 -o dcm2niix32 -lz -// lipo -create dcm2niix32 dcm2niix64 -o dcm2niix +// to generate combined 32-bit and 64-bit builds for OSX : +// g++ -O3 -x c++ main_console.c nii_dicom.c nifti1_io_core.c nii_ortho.c nii_dicom_batch.c -s -arch x86_64 -o dcm2niix64 -lz +// g++ -O3 -x c++ main_console.c nii_dicom.c nifti1_io_core.c nii_ortho.c nii_dicom_batch.c -s -arch i386 -o dcm2niix32 -lz +// lipo -create dcm2niix32 dcm2niix64 -o dcm2niix -//On windows with mingw you may get "fatal error: zlib.h: No such file -// to remedy, run "mingw-get install libz-dev" from mingw +// On windows with mingw you may get "fatal error: zlib.h: No such file +// to remedy, run "mingw-get install libz-dev" from mingw -//Alternatively, windows users with VisualStudio can compile this project -// vcvarsall amd64 -// cl /EHsc main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -DmyDisableOpenJPEG /o dcm2niix +// Alternatively, windows users with VisualStudio can compile this project +// vcvarsall amd64 +// cl /EHsc main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -DmyDisableOpenJPEG /o dcm2niix -//#define mydebugtest //automatically process directory specified in main, ignore input arguments +// #define mydebugtest //automatically process directory specified in main, ignore input arguments #include #include @@ -33,7 +33,7 @@ #include #include #include -//#include +// #include #include "nifti1_io_core.h" #include "nii_dicom.h" #include "nii_dicom_batch.h" @@ -62,7 +62,7 @@ const char *removePath(const char *path) { // "/usr/path/filename.exe" -> "filen if (pDelimeter) path = pDelimeter + 1; return path; -} //removePath() +} // removePath() char bool2Char(bool b) { if (b) @@ -107,22 +107,22 @@ void showHelp(const char *argv[], struct TDCMopts opts) { printf(" -q : only search directory for DICOMs (y/l/n, default y) [y=show number of DICOMs found, l=additionally list DICOMs found, n=no]\n"); printf(" -r : rename instead of convert DICOMs (y/n, default n)\n"); printf(" -s : single file mode, do not convert other images in folder (y/n, default n)\n"); -//text notes replaced with BIDS: this function is deprecated -//printf(" -t : text notes includes private patient details (y/n, default n)\n"); -#if !defined(_WIN64) && !defined(_WIN32) //shell script for Unix only +// text notes replaced with BIDS: this function is deprecated +// printf(" -t : text notes includes private patient details (y/n, default n)\n"); +#if !defined(_WIN64) && !defined(_WIN32) // shell script for Unix only printf(" -u : up-to-date check\n"); #endif printf(" -v : verbose (n/y or 0/1/2, default 0) [no, yes, logorrheic]\n"); - //#define kNAME_CONFLICT_SKIP 0 //0 = write nothing for a file that exists with desired name - //#define kNAME_CONFLICT_OVERWRITE 1 //1 = overwrite existing file with same name - //#define kNAME_CONFLICT_ADD_SUFFIX 2 //default 2 = write with new suffix as a new file + // #define kNAME_CONFLICT_SKIP 0 //0 = write nothing for a file that exists with desired name + // #define kNAME_CONFLICT_OVERWRITE 1 //1 = overwrite existing file with same name + // #define kNAME_CONFLICT_ADD_SUFFIX 2 //default 2 = write with new suffix as a new file printf(" -w : write behavior for name conflicts (0,1,2, default 2: 0=skip duplicates, 1=overwrite, 2=add suffix)\n"); printf(" -x : crop 3D acquisitions (y/n/i, default n, use 'i'gnore to neither crop nor rotate 3D acquisitions)\n"); char gzCh = 'n'; if (opts.isGz) gzCh = 'y'; #if defined(_WIN64) || defined(_WIN32) -//n.b. the optimal use of pigz requires pipes that are not provided for Windows +// n.b. the optimal use of pigz requires pipes that are not provided for Windows #ifdef myDisableZLib if (strlen(opts.pigzname) > 0) printf(" -z : gz compress images (y/n/3, default %c) [y=pigz, n=no, 3=no,3D]\n", gzCh); @@ -176,25 +176,25 @@ void showHelp(const char *argv[], struct TDCMopts opts) { printf(" %s -f mystudy%%s ~/dicomdir\n", cstr); printf(" %s -o \"~/dir with spaces/dir\" ~/dicomdir\n", cstr); #endif -} //showHelp() +} // showHelp() int invalidParam(int i, const char *argv[]) { - if (strchr("yYnNoOhHiIjlLJBb01234",argv[i][0])) + if (strchr("yYnNoOhHiIjlLJBb01234", argv[i][0])) return 0; - //if (argv[i][0] != '-') return 0; + // if (argv[i][0] != '-') return 0; printf(" Error: invalid option '%s %s'\n", argv[i - 1], argv[i]); return 1; } -#if !defined(_WIN64) && !defined(_WIN32) //shell script for Unix only +#if !defined(_WIN64) && !defined(_WIN32) // shell script for Unix only int checkUpToDate() { #define URL "/rordenlab/dcm2niix/releases/" #define APIURL "\"https://api.github.com/repos" URL "latest\"" #define HTMURL "https://github.com" URL #define SHELLSCRIPT "#!/usr/bin/env bash\n curl --silent " APIURL " | grep '\"tag_name\":' | sed -E 's/.*\"([^\"]+)\".*/\\1/'" -//check first 13 characters, e.g. "v1.0.20171204" +// check first 13 characters, e.g. "v1.0.20171204" #define versionChars 13 FILE *pipe = popen(SHELLSCRIPT, "r"); char ch, gitvers[versionChars + 1]; @@ -209,34 +209,34 @@ int checkUpToDate() { } } pclose(pipe); - gitvers[n] = 0; //null terminate - if (n < 1) { //script reported nothing + gitvers[n] = 0; // null terminate + if (n < 1) { // script reported nothing printf("Error: unable to check version with script:\n %s\n", SHELLSCRIPT); - return 3; //different from EXIT_SUCCESS (0) and EXIT_FAILURE (1) + return 3; // different from EXIT_SUCCESS (0) and EXIT_FAILURE (1) } - if (nMatch == versionChars) { //versions match + if (nMatch == versionChars) { // versions match printf("Good news: Your version is up to date: %s\n", gitvers); return EXIT_SUCCESS; } - //report error + // report error char myvers[versionChars + 1]; for (int i = 0; i < versionChars; i++) myvers[i] = kDCMvers[i]; - myvers[versionChars] = 0; //null terminate - int myv = atoi(myvers + 5); //skip "v1.0." - int gitv = atoi(gitvers + 5); //skip "v1.0." + myvers[versionChars] = 0; // null terminate + int myv = atoi(myvers + 5); // skip "v1.0." + int gitv = atoi(gitvers + 5); // skip "v1.0." if (myv > gitv) { printf("Warning: your version ('%s') more recent than stable release ('%s')\n %s\n", myvers, gitvers, HTMURL); - return 2; //different from EXIT_SUCCESS (0) and EXIT_FAILURE (1) + return 2; // different from EXIT_SUCCESS (0) and EXIT_FAILURE (1) } printf("Error: your version ('%s') is not the latest release ('%s')\n %s\n", myvers, gitvers, HTMURL); return EXIT_FAILURE; -} //checkUpToDate() +} // checkUpToDate() -#endif //shell script for UNIX only +#endif // shell script for UNIX only void showXML() { - //https://www.slicer.org/wiki/Documentation/Nightly/Developers/SlicerExecutionModel#XML_Schema + // https://www.slicer.org/wiki/Documentation/Nightly/Developers/SlicerExecutionModel#XML_Schema printf("\n"); } -//#define mydebugtest +// #define mydebugtest int main(int argc, const char *argv[]) { struct TDCMopts opts; bool isSaveIni = false; bool isOutNameSpecified = false; bool isResetDefaults = false; - readIniFile(&opts, argv); //set default preferences + readIniFile(&opts, argv); // set default preferences #ifdef mydebugtest - //strcpy(opts.indir, "/Users/rorden/desktop/sliceOrder/dicom2/Philips_PARREC_Rotation/NoRotation/DBIEX_4_1.PAR"); - //strcpy(opts.indir, "/Users/rorden/desktop/sliceOrder/dicom2/test"); + // strcpy(opts.indir, "/Users/rorden/desktop/sliceOrder/dicom2/Philips_PARREC_Rotation/NoRotation/DBIEX_4_1.PAR"); + // strcpy(opts.indir, "/Users/rorden/desktop/sliceOrder/dicom2/test"); strcpy(opts.indir, "e:\\t1s"); #else #if defined(__APPLE__) @@ -275,11 +275,11 @@ int main(int argc, const char *argv[]) { showHelp(argv, opts); return 0; } - //for (int i = 1; i < argc; i++) { printf(" argument %d= '%s'\n", i, argv[i]);} + // for (int i = 1; i < argc; i++) { printf(" argument %d= '%s'\n", i, argv[i]);} int i = 1; int lastCommandArg = 0; while (i < (argc)) { //-1 as final parameter is DICOM directory - if ((strlen(argv[i]) > 1) && (argv[i][0] == '-')) { //command + if ((strlen(argv[i]) > 1) && (argv[i][0] == '-')) { // command if (argv[i][1] == 'h') { showHelp(argv, opts); } else if ((!strcmp(argv[i], "--big-endian")) && ((i + 1) < argc)) { @@ -307,11 +307,11 @@ int main(int argc, const char *argv[]) { else opts.isProgress = 1; if (argv[i][0] == '2') - opts.isProgress = 2; //logorrheic + opts.isProgress = 2; // logorrheic } else if (!strcmp(argv[i], "--xml")) { showXML(); return EXIT_SUCCESS; - } else if ((argv[i][1] == 'a') && ((i + 1) < argc)) { //adjacent DICOMs + } else if ((argv[i][1] == 'a') && ((i + 1) < argc)) { // adjacent DICOMs i++; if (invalidParam(i, argv)) return 0; @@ -331,7 +331,7 @@ int main(int argc, const char *argv[]) { if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isCreateBIDS = false; else if ((argv[i][0] == 'i') || (argv[i][0] == 'I')) { - //input only mode (for development): does not create NIfTI or BIDS outputs! + // input only mode (for development): does not create NIfTI or BIDS outputs! opts.isCreateBIDS = false; opts.isOnlyBIDS = true; } else { @@ -349,16 +349,16 @@ int main(int argc, const char *argv[]) { opts.isAnonymizeBIDS = true; } else if (argv[i][2] == 'i') { //"-bi M2022" provide BIDS subject ID i++; - snprintf(opts.bidsSubject, kOptsStr-1, "%s", argv[i]); + snprintf(opts.bidsSubject, kOptsStr - 1, "%s", argv[i]); } else if (argv[i][2] == 'v') { //"-bv 1222" provide BIDS subject visit i++; - snprintf(opts.bidsSession, kOptsStr-1, "%s", argv[i]); + snprintf(opts.bidsSession, kOptsStr - 1, "%s", argv[i]); } else printf("Error: Unknown command line argument: '%s'\n", argv[i]); } else if ((argv[i][1] == 'c') && ((i + 1) < argc)) { i++; snprintf(opts.imageComments, 24, "%s", argv[i]); - if (strlen(opts.imageComments) == 0) //empty string is flag to anonymize DICOM image comments + if (strlen(opts.imageComments) == 0) // empty string is flag to anonymize DICOM image comments snprintf(opts.imageComments, 24, "%s", "\t"); } else if ((argv[i][1] == 'd') && ((i + 1) < argc)) { i++; @@ -376,12 +376,12 @@ int main(int argc, const char *argv[]) { opts.saveFormat = kSaveFormatJNII; if ((argv[i][0] == 'b') || (argv[i][0] == 'B') || (argv[i][0] == '4')) opts.saveFormat = kSaveFormatBNII; - #ifndef myEnableJNIFTI +#ifndef myEnableJNIFTI if ((opts.saveFormat == kSaveFormatJNII) || (opts.saveFormat == kSaveFormatBNII)) { printf("Recompile for JNIfTI support.\n"); return EXIT_SUCCESS; } - #endif +#endif } else if ((argv[i][1] == 'g') && ((i + 1) < argc)) { i++; if (invalidParam(i, argv)) @@ -392,16 +392,16 @@ int main(int argc, const char *argv[]) { isResetDefaults = true; printf("Defaults ignored\n"); setDefaultOpts(&opts, argv); - i = 0; //re-read all settings for this pass, e.g. "dcm2niix -f %p_%s -d o" should save filename as "%p_%s" + i = 0; // re-read all settings for this pass, e.g. "dcm2niix -f %p_%s -d o" should save filename as "%p_%s" } if (((argv[i][0] == 'o') || (argv[i][0] == 'O')) && (!isResetDefaults)) { - //reset defaults - do not read, but do write defaults + // reset defaults - do not read, but do write defaults isSaveIni = true; isResetDefaults = true; printf("Defaults reset\n"); setDefaultOpts(&opts, argv); - //this next line is optional, otherwise "dcm2niix -f %p_%s -d o" and "dcm2niix -d o -f %p_%s" will create different results - i = 0; //re-read all settings for this pass, e.g. "dcm2niix -f %p_%s -d o" should save filename as "%p_%s" + // this next line is optional, otherwise "dcm2niix -f %p_%s -d o" and "dcm2niix -d o -f %p_%s" will create different results + i = 0; // re-read all settings for this pass, e.g. "dcm2niix -f %p_%s -d o" should save filename as "%p_%s" } } else if ((argv[i][1] == 'i') && ((i + 1) < argc)) { i++; @@ -425,19 +425,16 @@ int main(int argc, const char *argv[]) { if (argv[i][0] == '0') { opts.diffCyclingModeGE = 0; printf("undocumented '--diffCyclingModeGE 0' cycling OFF\n"); - } - else if (argv[i][0] == '1') { + } else if (argv[i][0] == '1') { opts.diffCyclingModeGE = 1; printf("undocumented '--diffCyclingModeGE 1' cycling All-TR\n"); - } - else if (argv[i][0] == '2') { + } else if (argv[i][0] == '2') { opts.diffCyclingModeGE = 2; printf("undocumented '--diffCyclingModeGE 2' cycling 2-TR\n"); - } - else if (argv[i][0] == '3') { + } else if (argv[i][0] == '3') { opts.diffCyclingModeGE = 3; printf("undocumented '--diffCyclingModeGE 3' cycling 3-TR\n"); - } + } } else if ((argv[i][1] == 'l') && ((i + 1) < argc)) { i++; if (invalidParam(i, argv)) @@ -460,7 +457,7 @@ int main(int argc, const char *argv[]) { opts.isForceStackSameSeries = 2; if ((argv[i][0] == 'o') || (argv[i][0] == 'O')) { opts.isForceStackDCE = false; - //printf("Advanced feature: '-m o' merges images despite varying series number\n"); + // printf("Advanced feature: '-m o' merges images despite varying series number\n"); } if ((argv[i][0] == '2')) { opts.isIgnoreSeriesInstanceUID = true; @@ -504,7 +501,7 @@ int main(int argc, const char *argv[]) { opts.isCreateText = false; else opts.isCreateText = true; -#if !defined(_WIN64) && !defined(_WIN32) //shell script for Unix only +#if !defined(_WIN64) && !defined(_WIN32) // shell script for Unix only } else if (argv[i][1] == 'u') { return checkUpToDate(); #endif @@ -515,12 +512,12 @@ int main(int argc, const char *argv[]) { i++; if (invalidParam(i, argv)) return 0; - if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) //0: verbose OFF + if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) // 0: verbose OFF opts.isVerbose = 0; - else if ((argv[i][0] == 'h') || (argv[i][0] == 'H') || (argv[i][0] == '2')) //2: verbose HYPER + else if ((argv[i][0] == 'h') || (argv[i][0] == 'H') || (argv[i][0] == '2')) // 2: verbose HYPER opts.isVerbose = 2; else - opts.isVerbose = 1; //1: verbose ON + opts.isVerbose = 1; // 1: verbose ON } else if ((argv[i][1] == 'w') && ((i + 1) < argc)) { i++; if (invalidParam(i, argv)) @@ -548,7 +545,7 @@ int main(int argc, const char *argv[]) { if (invalidParam(i, argv)) return 0; if ((argv[i][0] == 'y') || (argv[i][0] == 'Y')) { - opts.isFlipY = true; //force use of internal compression instead of pigz + opts.isFlipY = true; // force use of internal compression instead of pigz strcpy(opts.pigzname, ""); } else if ((argv[i][0] == 'n') || (argv[i][0] == 'N')) opts.isFlipY = false; @@ -559,19 +556,19 @@ int main(int argc, const char *argv[]) { if (invalidParam(i, argv)) return 0; if ((argv[i][0] == '3')) { - opts.isGz = false; //uncompressed 3D + opts.isGz = false; // uncompressed 3D opts.isSave3D = true; } else if ((argv[i][0] == 'i') || (argv[i][0] == 'I')) { opts.isGz = true; #ifndef myDisableZLib - strcpy(opts.pigzname, ""); //force use of internal compression instead of pigz + strcpy(opts.pigzname, ""); // force use of internal compression instead of pigz #endif } else if ((argv[i][0] == 'n') || (argv[i][0] == 'N') || (argv[i][0] == '0')) opts.isGz = false; else opts.isGz = true; if (argv[i][0] == 'o') - opts.isPipedGz = true; //pipe to pigz without saving uncompressed to disk + opts.isPipedGz = true; // pipe to pigz without saving uncompressed to disk } else if ((argv[i][1] == 'f') && ((i + 1) < argc)) { i++; strcpy(opts.filename, argv[i]); @@ -583,7 +580,7 @@ int main(int argc, const char *argv[]) { i++; double seriesNumber = atof(argv[i]); if (seriesNumber < 0) - opts.numSeries = -1.0; //report series: convert none + opts.numSeries = -1.0; // report series: convert none else if ((opts.numSeries >= 0) && (opts.numSeries < MAX_NUM_SERIES)) { opts.seriesNumber[opts.numSeries] = seriesNumber; opts.numSeries += 1; @@ -594,32 +591,32 @@ int main(int argc, const char *argv[]) { printf(" Error: invalid option '%s %s'\n", argv[i], argv[i + 1]); ; lastCommandArg = i; - } //if parameter is a command - i++; //read next parameter - } //while parameters to read + } // if parameter is a command + i++; // read next parameter + } // while parameters to read #ifndef myDisableZLib if ((opts.isGz) && (opts.dirSearchDepth < 1) && (strlen(opts.pigzname) > 0)) { strcpy(opts.pigzname, ""); printf("n.b. Setting directory search depth of zero invokes internal gz (network mode)\n"); } #endif - if ((opts.isRenameNotConvert) && (!isOutNameSpecified)) { //sensible naming scheme for renaming option -//strcpy(opts.filename,argv[i]); -//2019 - now include "%o" to append media SOP UID, as instance number is not required to be unique + if ((opts.isRenameNotConvert) && (!isOutNameSpecified)) { // sensible naming scheme for renaming option +// strcpy(opts.filename,argv[i]); +// 2019 - now include "%o" to append media SOP UID, as instance number is not required to be unique #if defined(_WIN64) || defined(_WIN32) - strcpy(opts.filename, "%t\\%s_%p\\%4r_%o.dcm"); //nrrd or nhdr (windows folders) + strcpy(opts.filename, "%t\\%s_%p\\%4r_%o.dcm"); // nrrd or nhdr (windows folders) #else - strcpy(opts.filename, "%t/%s_%p/%4r_%o.dcm"); //nrrd or nhdr (unix folders) + strcpy(opts.filename, "%t/%s_%p/%4r_%o.dcm"); // nrrd or nhdr (unix folders) #endif printf("renaming without output filename, assuming '-f %s'\n", opts.filename); } if (isSaveIni) saveIniFile(opts); - //printf("%d %d",argc,lastCommandArg); + // printf("%d %d",argc,lastCommandArg); if (argc == (lastCommandArg + 1)) { //+1 as array indexed from 0 - //the user did not provide an input filename, report filename structure + // the user did not provide an input filename, report filename structure char niiFilename[1024]; - strcpy(opts.outdir, ""); //no input supplied + strcpy(opts.outdir, ""); // no input supplied nii_createDummyFilename(niiFilename, opts); printf("%s\n", niiFilename); return EXIT_SUCCESS; @@ -649,7 +646,7 @@ int main(int argc, const char *argv[]) { printf("Conversion required %f seconds.\n", ((float)(clock() - start)) / CLOCKS_PER_SEC); #endif } - //if (isSaveIni) //we now save defaults earlier, in case of early termination. + // if (isSaveIni) //we now save defaults earlier, in case of early termination. // saveIniFile(opts); return EXIT_SUCCESS; } diff --git a/console/main_console_batch.cpp b/console/main_console_batch.cpp index e968962e..30ad7bd4 100644 --- a/console/main_console_batch.cpp +++ b/console/main_console_batch.cpp @@ -5,45 +5,45 @@ #include //requires VS 2015 or later #ifdef _MSC_VER - #include //access() - #ifndef F_OK - #define F_OK 0 /* existence check */ - #endif +#include //access() +#ifndef F_OK +#define F_OK 0 /* existence check */ +#endif #else - #include //access() +#include //access() #endif +#include "nii_dicom.h" +#include "nii_dicom_batch.h" #include #include #include #include #include #include -#include // clock_t, clock, CLOCKS_PER_SEC +#include // clock_t, clock, CLOCKS_PER_SEC #include -#include "nii_dicom.h" -#include "nii_dicom_batch.h" -const char* removePath(const char* path) { // "/usr/path/filename.exe" -> "filename.exe" - const char* pDelimeter = strrchr (path, '\\'); - if (pDelimeter) - path = pDelimeter+1; - pDelimeter = strrchr (path, '/'); - if (pDelimeter) - path = pDelimeter+1; - return path; -} //removePath() +const char *removePath(const char *path) { // "/usr/path/filename.exe" -> "filename.exe" + const char *pDelimeter = strrchr(path, '\\'); + if (pDelimeter) + path = pDelimeter + 1; + pDelimeter = strrchr(path, '/'); + if (pDelimeter) + path = pDelimeter + 1; + return path; +} // removePath() int rmainbatch(TDCMopts opts) { - clock_t start = clock(); - nii_loadDir(&opts); - printf ("Conversion required %f seconds.\n",((float)(clock()-start))/CLOCKS_PER_SEC); - return EXIT_SUCCESS; -} //rmainbatch() + clock_t start = clock(); + nii_loadDir(&opts); + printf("Conversion required %f seconds.\n", ((float)(clock() - start)) / CLOCKS_PER_SEC); + return EXIT_SUCCESS; +} // rmainbatch() -void showHelp(const char * argv[]) { - const char *cstr = removePath(argv[0]); - printf("Usage: %s \n", cstr); - printf("\n"); +void showHelp(const char *argv[]) { + const char *cstr = removePath(argv[0]); + printf("Usage: %s \n", cstr); + printf("\n"); printf("The configuration file must be in yaml format as shown below\n"); printf("\n"); printf("### START YAML FILE ###\n"); @@ -65,66 +65,66 @@ void showHelp(const char * argv[]) { printf("### END YAML FILE ###\n"); printf("\n"); #if defined(_WIN64) || defined(_WIN32) - printf(" Example :\n"); - printf(" %s c:\\dir\\yaml.yml\n", cstr); + printf(" Example :\n"); + printf(" %s c:\\dir\\yaml.yml\n", cstr); - printf(" %s \"c:\\dir with spaces\\yaml.yml\"\n", cstr); + printf(" %s \"c:\\dir with spaces\\yaml.yml\"\n", cstr); #else - printf(" Examples :\n"); - printf(" %s /Users/chris/yaml.yml\n", cstr); - printf(" %s \"/Users/dir with spaces/yaml.yml\"\n", cstr); + printf(" Examples :\n"); + printf(" %s /Users/chris/yaml.yml\n", cstr); + printf(" %s \"/Users/dir with spaces/yaml.yml\"\n", cstr); #endif -} //showHelp() +} // showHelp() -int main(int argc, const char * argv[]) { - #if defined(__APPLE__) - #define kOS "MacOS" - #elif (defined(__linux) || defined(__linux__)) - #define kOS "Linux" - #else - #define kOS "Windows" - #endif - printf("dcm2niibatch using Chris Rorden's dcm2niiX version %s (%llu-bit %s)\n",kDCMvers, (unsigned long long) sizeof(size_t)*8, kOS); - if (argc != 2) { - if (argc < 2) - printf(" Please provide location of config file\n"); - else - printf(" Do not include additional inputs with a config file\n"); - printf("\n"); - showHelp(argv); - return EXIT_FAILURE; - } - if( access( argv[1], F_OK ) == -1 ) { - printf(" Please provide location of config file\n"); - printf("\n"); - showHelp(argv); - return EXIT_FAILURE; +int main(int argc, const char *argv[]) { +#if defined(__APPLE__) +#define kOS "MacOS" +#elif (defined(__linux) || defined(__linux__)) +#define kOS "Linux" +#else +#define kOS "Windows" +#endif + printf("dcm2niibatch using Chris Rorden's dcm2niiX version %s (%llu-bit %s)\n", kDCMvers, (unsigned long long)sizeof(size_t) * 8, kOS); + if (argc != 2) { + if (argc < 2) + printf(" Please provide location of config file\n"); + else + printf(" Do not include additional inputs with a config file\n"); + printf("\n"); + showHelp(argv); + return EXIT_FAILURE; } - // Process it all via a yaml file - std::string yaml_file = argv[1]; - std::cout << "yaml_path: " << yaml_file << std::endl; - YAML::Node config = YAML::LoadFile(yaml_file); - struct TDCMopts opts; - readIniFile(&opts, argv); //setup defaults, e.g. path to pigz - opts.isCreateBIDS = config["Options"]["isCreateBIDS"].as(); - opts.isOnlySingleFile = config["Options"]["isOnlySingleFile"].as(); - opts.isFlipY = config["Options"]["isFlipY"].as(); - opts.isCreateText = false; - opts.isVerbose = 0; - opts.isGz = config["Options"]["isGz"].as(); //save data as compressed (.nii.gz) or raw (.nii) - /*bool isInternalGz = config["Options"]["isInternalGz"].as(); - if (isInternalGz) { - strcpy(opts.pigzname, "”); //do NOT use pigz: force internal compressor + if (access(argv[1], F_OK) == -1) { + printf(" Please provide location of config file\n"); + printf("\n"); + showHelp(argv); + return EXIT_FAILURE; + } + // Process it all via a yaml file + std::string yaml_file = argv[1]; + std::cout << "yaml_path: " << yaml_file << std::endl; + YAML::Node config = YAML::LoadFile(yaml_file); + struct TDCMopts opts; + readIniFile(&opts, argv); // setup defaults, e.g. path to pigz + opts.isCreateBIDS = config["Options"]["isCreateBIDS"].as(); + opts.isOnlySingleFile = config["Options"]["isOnlySingleFile"].as(); + opts.isFlipY = config["Options"]["isFlipY"].as(); + opts.isCreateText = false; + opts.isVerbose = 0; + opts.isGz = config["Options"]["isGz"].as(); // save data as compressed (.nii.gz) or raw (.nii) + /*bool isInternalGz = config["Options"]["isInternalGz"].as(); + if (isInternalGz) { + strcpy(opts.pigzname, "”); //do NOT use pigz: force internal compressor //in general, pigz is faster unless you have a very slow network, in which case the internal compressor is better - }*/ - for (auto i: config["Files"]) { - std::string indir = i["in_dir"].as(); - strcpy(opts.indir, indir.c_str()); - std::string outdir = i["out_dir"].as(); - strcpy(opts.outdir, outdir.c_str()); - std::string filename = i["filename"].as(); - strcpy(opts.filename, filename.c_str()); - rmainbatch(opts); - } - return EXIT_SUCCESS; + }*/ + for (auto i : config["Files"]) { + std::string indir = i["in_dir"].as(); + strcpy(opts.indir, indir.c_str()); + std::string outdir = i["out_dir"].as(); + strcpy(opts.outdir, outdir.c_str()); + std::string filename = i["filename"].as(); + strcpy(opts.filename, filename.c_str()); + rmainbatch(opts); + } + return EXIT_SUCCESS; } // main() diff --git a/console/nifti1.h b/console/nifti1.h index eb935e05..2f6d422f 100644 --- a/console/nifti1.h +++ b/console/nifti1.h @@ -1,15 +1,15 @@ /** \file nifti1.h - \brief Official definition of the nifti1 header. Written by Bob Cox, SSCC, NIMH. + \brief Official definition of the nifti1 header. Written by Bob Cox, SSCC, NIMH. - HISTORY: + HISTORY: - 29 Nov 2007 [rickr] - - added DT_RGBA32 and NIFTI_TYPE_RGBA32 - - added NIFTI_INTENT codes: - TIME_SERIES, NODE_INDEX, RGB_VECTOR, RGBA_VECTOR, SHAPE + 29 Nov 2007 [rickr] + - added DT_RGBA32 and NIFTI_TYPE_RGBA32 + - added NIFTI_INTENT codes: + TIME_SERIES, NODE_INDEX, RGB_VECTOR, RGBA_VECTOR, SHAPE - 08 Mar 2019 [PT,DRG] - - Updated to include [qs]form_code = 5 + 08 Mar 2019 [PT,DRG] + - Updated to include [qs]form_code = 5 */ @@ -17,30 +17,30 @@ #define _NIFTI_HEADER_ /***************************************************************************** - ** This file defines the "NIFTI-1" header format. ** - ** It is derived from 2 meetings at the NIH (31 Mar 2003 and ** - ** 02 Sep 2003) of the Data Format Working Group (DFWG), ** - ** chartered by the NIfTI (Neuroimaging Informatics Technology ** - ** Initiative) at the National Institutes of Health (NIH). ** - **--------------------------------------------------------------** - ** Neither the National Institutes of Health (NIH), the DFWG, ** - ** nor any of the members or employees of these institutions ** - ** imply any warranty of usefulness of this material for any ** - ** purpose, and do not assume any liability for damages, ** - ** incidental or otherwise, caused by any use of this document. ** - ** If these conditions are not acceptable, do not use this! ** - **--------------------------------------------------------------** - ** Author: Robert W Cox (NIMH, Bethesda) ** - ** Advisors: John Ashburner (FIL, London), ** - ** Stephen Smith (FMRIB, Oxford), ** - ** Mark Jenkinson (FMRIB, Oxford) ** -******************************************************************************/ + ** This file defines the "NIFTI-1" header format. ** + ** It is derived from 2 meetings at the NIH (31 Mar 2003 and ** + ** 02 Sep 2003) of the Data Format Working Group (DFWG), ** + ** chartered by the NIfTI (Neuroimaging Informatics Technology ** + ** Initiative) at the National Institutes of Health (NIH). ** + **--------------------------------------------------------------** + ** Neither the National Institutes of Health (NIH), the DFWG, ** + ** nor any of the members or employees of these institutions ** + ** imply any warranty of usefulness of this material for any ** + ** purpose, and do not assume any liability for damages, ** + ** incidental or otherwise, caused by any use of this document. ** + ** If these conditions are not acceptable, do not use this! ** + **--------------------------------------------------------------** + ** Author: Robert W Cox (NIMH, Bethesda) ** + ** Advisors: John Ashburner (FIL, London), ** + ** Stephen Smith (FMRIB, Oxford), ** + ** Mark Jenkinson (FMRIB, Oxford) ** + ******************************************************************************/ /*---------------------------------------------------------------------------*/ /* Note that the ANALYZE 7.5 file header (dbh.h) is - (c) Copyright 1986-1995 - Biomedical Imaging Resource - Mayo Foundation + (c) Copyright 1986-1995 + Biomedical Imaging Resource + Mayo Foundation Incorporation of components of dbh.h are by permission of the Mayo Foundation. @@ -53,17 +53,17 @@ ------------------------ The twin (and somewhat conflicting) goals of this modified ANALYZE 7.5 format are: - (a) To add information to the header that will be useful for functional - neuroimaging data analysis and display. These additions include: - - More basic data types. - - Two affine transformations to specify voxel coordinates. - - "Intent" codes and parameters to describe the meaning of the data. - - Affine scaling of the stored data values to their "true" values. - - Optional storage of the header and image data in one file (.nii). - (b) To maintain compatibility with non-NIFTI-aware ANALYZE 7.5 compatible - software (i.e., such a program should be able to do something useful - with a NIFTI-1 dataset -- at least, with one stored in a traditional - .img/.hdr file pair). + (a) To add information to the header that will be useful for functional + neuroimaging data analysis and display. These additions include: + - More basic data types. + - Two affine transformations to specify voxel coordinates. + - "Intent" codes and parameters to describe the meaning of the data. + - Affine scaling of the stored data values to their "true" values. + - Optional storage of the header and image data in one file (.nii). + (b) To maintain compatibility with non-NIFTI-aware ANALYZE 7.5 compatible + software (i.e., such a program should be able to do something useful + with a NIFTI-1 dataset -- at least, with one stored in a traditional + .img/.hdr file pair). Most of the unused fields in the ANALYZE 7.5 header have been taken, and some of the lesser-used fields have been co-opted for other purposes. @@ -76,7 +76,7 @@ To flag such a struct as being conformant to the NIFTI-1 spec, the last 4 bytes of the header must be either the C String "ni1" or "n+1"; in hexadecimal, the 4 bytes - 6E 69 31 00 or 6E 2B 31 00 + 6E 69 31 00 or 6E 2B 31 00 (in any future version of this format, the '1' will be upgraded to '2', etc.). Normally, such a "magic number" or flag goes at the start of the file, but trying to avoid clobbering widely-used ANALYZE 7.5 fields led to @@ -130,84 +130,84 @@ (Also see the UNUSED FIELDS comment section, far below.) The presumption below is that the various C types have particular sizes: - sizeof(int) = sizeof(float) = 4 ; sizeof(short) = 2 + sizeof(int) = sizeof(float) = 4 ; sizeof(short) = 2 -----------------------------------------------------------------------------*/ /*=================*/ -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif /*=================*/ /*! \struct nifti_1_header - \brief Data structure defining the fields in the nifti1 header. - This binary header should be found at the beginning of a valid - NIFTI-1 header file. + \brief Data structure defining the fields in the nifti1 header. + This binary header should be found at the beginning of a valid + NIFTI-1 header file. */ - /*************************/ /************************/ -struct nifti_1_header { /* NIFTI-1 usage */ /* ANALYZE 7.5 field(s) */ - /*************************/ /************************/ - - /*--- was header_key substruct ---*/ - int sizeof_hdr; /*!< MUST be 348 */ /* int sizeof_hdr; */ - char data_type[10]; /*!< ++UNUSED++ */ /* char data_type[10]; */ - char db_name[18]; /*!< ++UNUSED++ */ /* char db_name[18]; */ - int extents; /*!< ++UNUSED++ */ /* int extents; */ - short session_error; /*!< ++UNUSED++ */ /* short session_error; */ - char regular; /*!< ++UNUSED++ */ /* char regular; */ - char dim_info; /*!< MRI slice ordering. */ /* char hkey_un0; */ - - /*--- was image_dimension substruct ---*/ - short dim[8]; /*!< Data array dimensions.*/ /* short dim[8]; */ - float intent_p1 ; /*!< 1st intent parameter. */ /* short unused8; */ - /* short unused9; */ - float intent_p2 ; /*!< 2nd intent parameter. */ /* short unused10; */ - /* short unused11; */ - float intent_p3 ; /*!< 3rd intent parameter. */ /* short unused12; */ - /* short unused13; */ - short intent_code ; /*!< NIFTI_INTENT_* code. */ /* short unused14; */ - short datatype; /*!< Defines data type! */ /* short datatype; */ - short bitpix; /*!< Number bits/voxel. */ /* short bitpix; */ - short slice_start; /*!< First slice index. */ /* short dim_un0; */ - float pixdim[8]; /*!< Grid spacings. */ /* float pixdim[8]; */ - float vox_offset; /*!< Offset into .nii file */ /* float vox_offset; */ - float scl_slope ; /*!< Data scaling: slope. */ /* float funused1; */ - float scl_inter ; /*!< Data scaling: offset. */ /* float funused2; */ - short slice_end; /*!< Last slice index. */ /* float funused3; */ - char slice_code ; /*!< Slice timing order. */ - char xyzt_units ; /*!< Units of pixdim[1..4] */ - float cal_max; /*!< Max display intensity */ /* float cal_max; */ - float cal_min; /*!< Min display intensity */ /* float cal_min; */ - float slice_duration;/*!< Time for 1 slice. */ /* float compressed; */ - float toffset; /*!< Time axis shift. */ /* float verified; */ - int glmax; /*!< ++UNUSED++ */ /* int glmax; */ - int glmin; /*!< ++UNUSED++ */ /* int glmin; */ - - /*--- was data_history substruct ---*/ - char descrip[80]; /*!< any text you like. */ /* char descrip[80]; */ - char aux_file[24]; /*!< auxiliary filename. */ /* char aux_file[24]; */ - - short qform_code ; /*!< NIFTI_XFORM_* code. */ /*-- all ANALYZE 7.5 ---*/ - short sform_code ; /*!< NIFTI_XFORM_* code. */ /* fields below here */ - /* are replaced */ - float quatern_b ; /*!< Quaternion b param. */ - float quatern_c ; /*!< Quaternion c param. */ - float quatern_d ; /*!< Quaternion d param. */ - float qoffset_x ; /*!< Quaternion x shift. */ - float qoffset_y ; /*!< Quaternion y shift. */ - float qoffset_z ; /*!< Quaternion z shift. */ - - float srow_x[4] ; /*!< 1st row affine transform. */ - float srow_y[4] ; /*!< 2nd row affine transform. */ - float srow_z[4] ; /*!< 3rd row affine transform. */ - - char intent_name[16];/*!< 'name' or meaning of data. */ - - char magic[4] ; /*!< MUST be "ni1\0" or "n+1\0". */ - -} ; /**** 348 bytes total ****/ - -typedef struct nifti_1_header nifti_1_header ; +/*************************/ /************************/ +struct nifti_1_header { /* NIFTI-1 usage */ /* ANALYZE 7.5 field(s) */ + /*************************/ /************************/ + + /*--- was header_key substruct ---*/ + int sizeof_hdr; /*!< MUST be 348 */ /* int sizeof_hdr; */ + char data_type[10]; /*!< ++UNUSED++ */ /* char data_type[10]; */ + char db_name[18]; /*!< ++UNUSED++ */ /* char db_name[18]; */ + int extents; /*!< ++UNUSED++ */ /* int extents; */ + short session_error; /*!< ++UNUSED++ */ /* short session_error; */ + char regular; /*!< ++UNUSED++ */ /* char regular; */ + char dim_info; /*!< MRI slice ordering. */ /* char hkey_un0; */ + + /*--- was image_dimension substruct ---*/ + short dim[8]; /*!< Data array dimensions.*/ /* short dim[8]; */ + float intent_p1; /*!< 1st intent parameter. */ /* short unused8; */ + /* short unused9; */ + float intent_p2; /*!< 2nd intent parameter. */ /* short unused10; */ + /* short unused11; */ + float intent_p3; /*!< 3rd intent parameter. */ /* short unused12; */ + /* short unused13; */ + short intent_code; /*!< NIFTI_INTENT_* code. */ /* short unused14; */ + short datatype; /*!< Defines data type! */ /* short datatype; */ + short bitpix; /*!< Number bits/voxel. */ /* short bitpix; */ + short slice_start; /*!< First slice index. */ /* short dim_un0; */ + float pixdim[8]; /*!< Grid spacings. */ /* float pixdim[8]; */ + float vox_offset; /*!< Offset into .nii file */ /* float vox_offset; */ + float scl_slope; /*!< Data scaling: slope. */ /* float funused1; */ + float scl_inter; /*!< Data scaling: offset. */ /* float funused2; */ + short slice_end; /*!< Last slice index. */ /* float funused3; */ + char slice_code; /*!< Slice timing order. */ + char xyzt_units; /*!< Units of pixdim[1..4] */ + float cal_max; /*!< Max display intensity */ /* float cal_max; */ + float cal_min; /*!< Min display intensity */ /* float cal_min; */ + float slice_duration; /*!< Time for 1 slice. */ /* float compressed; */ + float toffset; /*!< Time axis shift. */ /* float verified; */ + int glmax; /*!< ++UNUSED++ */ /* int glmax; */ + int glmin; /*!< ++UNUSED++ */ /* int glmin; */ + + /*--- was data_history substruct ---*/ + char descrip[80]; /*!< any text you like. */ /* char descrip[80]; */ + char aux_file[24]; /*!< auxiliary filename. */ /* char aux_file[24]; */ + + short qform_code; /*!< NIFTI_XFORM_* code. */ /*-- all ANALYZE 7.5 ---*/ + short sform_code; /*!< NIFTI_XFORM_* code. */ /* fields below here */ + /* are replaced */ + float quatern_b; /*!< Quaternion b param. */ + float quatern_c; /*!< Quaternion c param. */ + float quatern_d; /*!< Quaternion d param. */ + float qoffset_x; /*!< Quaternion x shift. */ + float qoffset_y; /*!< Quaternion y shift. */ + float qoffset_z; /*!< Quaternion z shift. */ + + float srow_x[4]; /*!< 1st row affine transform. */ + float srow_y[4]; /*!< 2nd row affine transform. */ + float srow_z[4]; /*!< 3rd row affine transform. */ + + char intent_name[16]; /*!< 'name' or meaning of data. */ + + char magic[4]; /*!< MUST be "ni1\0" or "n+1\0". */ + +}; /**** 348 bytes total ****/ + +typedef struct nifti_1_header nifti_1_header; /*---------------------------------------------------------------------------*/ /* HEADER EXTENSIONS: @@ -232,19 +232,19 @@ typedef struct nifti_1_header nifti_1_header ; The format of extended header data is weakly specified. Each extension must be an integer multiple of 16 bytes long. The first 8 bytes of each extension comprise 2 integers: - int esize , ecode ; + int esize , ecode ; These values may need to be byte-swapped, as indicated by dim[0] for the rest of the header. - * esize is the number of bytes that form the extended header data - + esize must be a positive integral multiple of 16 - + this length includes the 8 bytes of esize and ecode themselves - * ecode is a non-negative integer that indicates the format of the - extended header data that follows - + different ecode values are assigned to different developer groups - + at present, the "registered" values for code are - = 0 = unknown private format (not recommended!) - = 2 = DICOM format (i.e., attribute tags and values) - = 4 = AFNI group (i.e., ASCII XML-ish elements) + * esize is the number of bytes that form the extended header data + + esize must be a positive integral multiple of 16 + + this length includes the 8 bytes of esize and ecode themselves + * ecode is a non-negative integer that indicates the format of the + extended header data that follows + + different ecode values are assigned to different developer groups + + at present, the "registered" values for code are + = 0 = unknown private format (not recommended!) + = 2 = DICOM format (i.e., attribute tags and values) + = 4 = AFNI group (i.e., ASCII XML-ish elements) In the interests of interoperability (a primary rationale for NIfTI), groups developing software that uses this extension mechanism are encouraged to document and publicize the format of their extensions. @@ -282,82 +282,84 @@ typedef struct nifti_1_header nifti_1_header ; -----------------------------------------------------------------------------*/ /*! \struct nifti1_extender - \brief This structure represents a 4-byte string that should follow the - binary nifti_1_header data in a NIFTI-1 header file. If the char - values are {1,0,0,0}, the file is expected to contain extensions, - values of {0,0,0,0} imply the file does not contain extensions. - Other sequences of values are not currently defined. + \brief This structure represents a 4-byte string that should follow the + binary nifti_1_header data in a NIFTI-1 header file. If the char + values are {1,0,0,0}, the file is expected to contain extensions, + values of {0,0,0,0} imply the file does not contain extensions. + Other sequences of values are not currently defined. */ -struct nifti1_extender { char extension[4] ; } ; -typedef struct nifti1_extender nifti1_extender ; +struct nifti1_extender { + char extension[4]; +}; +typedef struct nifti1_extender nifti1_extender; /*! \struct nifti1_extension - \brief Data structure defining the fields of a header extension. + \brief Data structure defining the fields of a header extension. */ struct nifti1_extension { - int esize ; /*!< size of extension, in bytes (must be multiple of 16) */ - int ecode ; /*!< extension code, one of the NIFTI_ECODE_ values */ - char * edata ; /*!< raw data, with no byte swapping (length is esize-8) */ -} ; -typedef struct nifti1_extension nifti1_extension ; + int esize; /*!< size of extension, in bytes (must be multiple of 16) */ + int ecode; /*!< extension code, one of the NIFTI_ECODE_ values */ + char *edata; /*!< raw data, with no byte swapping (length is esize-8) */ +}; +typedef struct nifti1_extension nifti1_extension; /*---------------------------------------------------------------------------*/ /* DATA DIMENSIONALITY (as in ANALYZE 7.5): --------------------------------------- - dim[0] = number of dimensions; - - if dim[0] is outside range 1..7, then the header information - needs to be byte swapped appropriately - - ANALYZE supports dim[0] up to 7, but NIFTI-1 reserves - dimensions 1,2,3 for space (x,y,z), 4 for time (t), and - 5,6,7 for anything else needed. + dim[0] = number of dimensions; + - if dim[0] is outside range 1..7, then the header information + needs to be byte swapped appropriately + - ANALYZE supports dim[0] up to 7, but NIFTI-1 reserves + dimensions 1,2,3 for space (x,y,z), 4 for time (t), and + 5,6,7 for anything else needed. - dim[i] = length of dimension #i, for i=1..dim[0] (must be positive) - - also see the discussion of intent_code, far below + dim[i] = length of dimension #i, for i=1..dim[0] (must be positive) + - also see the discussion of intent_code, far below - pixdim[i] = voxel width along dimension #i, i=1..dim[0] (positive) - - cf. ORIENTATION section below for use of pixdim[0] - - the units of pixdim can be specified with the xyzt_units - field (also described far below). + pixdim[i] = voxel width along dimension #i, i=1..dim[0] (positive) + - cf. ORIENTATION section below for use of pixdim[0] + - the units of pixdim can be specified with the xyzt_units + field (also described far below). Number of bits per voxel value is in bitpix, which MUST correspond with the datatype field. The total number of bytes in the image data is - dim[1] * ... * dim[dim[0]] * bitpix / 8 + dim[1] * ... * dim[dim[0]] * bitpix / 8 In NIFTI-1 files, dimensions 1,2,3 are for space, dimension 4 is for time, and dimension 5 is for storing multiple values at each spatiotemporal voxel. Some examples: - - A typical whole-brain FMRI experiment's time series: - - dim[0] = 4 - - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM - - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC - - dim[3] = 20 pixdim[3] = 5.0 - - dim[4] = 120 pixdim[4] = 2.0 - - A typical T1-weighted anatomical volume: - - dim[0] = 3 - - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM - - dim[2] = 256 pixdim[2] = 1.0 - - dim[3] = 128 pixdim[3] = 1.1 - - A single slice EPI time series: - - dim[0] = 4 - - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM - - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC - - dim[3] = 1 pixdim[3] = 5.0 - - dim[4] = 1200 pixdim[4] = 0.2 - - A 3-vector stored at each point in a 3D volume: - - dim[0] = 5 - - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM - - dim[2] = 256 pixdim[2] = 1.0 - - dim[3] = 128 pixdim[3] = 1.1 - - dim[4] = 1 pixdim[4] = 0.0 - - dim[5] = 3 intent_code = NIFTI_INTENT_VECTOR - - A single time series with a 3x3 matrix at each point: - - dim[0] = 5 - - dim[1] = 1 xyzt_units = NIFTI_UNITS_SEC - - dim[2] = 1 - - dim[3] = 1 - - dim[4] = 1200 pixdim[4] = 0.2 - - dim[5] = 9 intent_code = NIFTI_INTENT_GENMATRIX - - intent_p1 = intent_p2 = 3.0 (indicates matrix dimensions) + - A typical whole-brain FMRI experiment's time series: + - dim[0] = 4 + - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM + - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC + - dim[3] = 20 pixdim[3] = 5.0 + - dim[4] = 120 pixdim[4] = 2.0 + - A typical T1-weighted anatomical volume: + - dim[0] = 3 + - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM + - dim[2] = 256 pixdim[2] = 1.0 + - dim[3] = 128 pixdim[3] = 1.1 + - A single slice EPI time series: + - dim[0] = 4 + - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM + - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC + - dim[3] = 1 pixdim[3] = 5.0 + - dim[4] = 1200 pixdim[4] = 0.2 + - A 3-vector stored at each point in a 3D volume: + - dim[0] = 5 + - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM + - dim[2] = 256 pixdim[2] = 1.0 + - dim[3] = 128 pixdim[3] = 1.1 + - dim[4] = 1 pixdim[4] = 0.0 + - dim[5] = 3 intent_code = NIFTI_INTENT_VECTOR + - A single time series with a 3x3 matrix at each point: + - dim[0] = 5 + - dim[1] = 1 xyzt_units = NIFTI_UNITS_SEC + - dim[2] = 1 + - dim[3] = 1 + - dim[4] = 1200 pixdim[4] = 0.2 + - dim[5] = 9 intent_code = NIFTI_INTENT_GENMATRIX + - intent_p1 = intent_p2 = 3.0 (indicates matrix dimensions) -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ @@ -398,13 +400,13 @@ typedef struct nifti1_extension nifti1_extension ; location of the image data bytes in that file. In a .hdr/.img file pair, the vox_offset field value is the start location of the image data bytes in the .img file. - * If vox_offset is less than 352 in a .nii file, it is equivalent - to 352 (i.e., image data never starts before byte #352 in a .nii file). - * The default value for vox_offset in a .nii file is 352. - * In a .hdr file, the default value for vox_offset is 0. - * vox_offset should be an integer multiple of 16; otherwise, some - programs may not work properly (e.g., SPM). This is to allow - memory-mapped input to be properly byte-aligned. + * If vox_offset is less than 352 in a .nii file, it is equivalent + to 352 (i.e., image data never starts before byte #352 in a .nii file). + * The default value for vox_offset in a .nii file is 352. + * In a .hdr file, the default value for vox_offset is 0. + * vox_offset should be an integer multiple of 16; otherwise, some + programs may not work properly (e.g., SPM). This is to allow + memory-mapped input to be properly byte-aligned. Note that since vox_offset is an IEEE-754 32 bit float (for compatibility with the ANALYZE-7.5 format), it effectively has a 24 bit mantissa. All integers from 0 to 2^24 can be represented exactly in this format, but not @@ -438,27 +440,27 @@ typedef struct nifti1_extension nifti1_extension ; ------------ If the scl_slope field is nonzero, then each voxel value in the dataset should be scaled as - y = scl_slope * x + scl_inter + y = scl_slope * x + scl_inter where x = voxel value stored - y = "true" voxel value + y = "true" voxel value Normally, we would expect this scaling to be used to store "true" floating values in a smaller integer datatype, but that is not required. That is, it is legal to use scaling even if the datatype is a float type (crazy, perhaps, but legal). - - However, the scaling is to be ignored if datatype is DT_RGB24. - - If datatype is a complex type, then the scaling is to be - applied to both the real and imaginary parts. + - However, the scaling is to be ignored if datatype is DT_RGB24. + - If datatype is a complex type, then the scaling is to be + applied to both the real and imaginary parts. The cal_min and cal_max fields (if nonzero) are used for mapping (possibly scaled) dataset values to display colors: - - Minimum display intensity (black) corresponds to dataset value cal_min. - - Maximum display intensity (white) corresponds to dataset value cal_max. - - Dataset values below cal_min should display as black also, and values - above cal_max as white. - - Colors "black" and "white", of course, may refer to any scalar display - scheme (e.g., a color lookup table specified via aux_file). - - cal_min and cal_max only make sense when applied to scalar-valued - datasets (i.e., dim[0] < 5 or dim[5] = 1). + - Minimum display intensity (black) corresponds to dataset value cal_min. + - Maximum display intensity (white) corresponds to dataset value cal_max. + - Dataset values below cal_min should display as black also, and values + above cal_max as white. + - Colors "black" and "white", of course, may refer to any scalar display + scheme (e.g., a color lookup table specified via aux_file). + - cal_min and cal_max only make sense when applied to scalar-valued + datasets (i.e., dim[0] < 5 or dim[5] = 1). -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ @@ -478,88 +480,87 @@ typedef struct nifti1_extension nifti1_extension ; when it encounters a dataset with a type it doesn't like. -----------------------------------------------------------------------------*/ -#undef DT_UNKNOWN /* defined in dirent.h on some Unix systems */ +#undef DT_UNKNOWN /* defined in dirent.h on some Unix systems */ /*! \defgroup NIFTI1_DATATYPES - \brief nifti1 datatype codes - @{ + \brief nifti1 datatype codes + @{ */ - /*--- the original ANALYZE 7.5 type codes ---*/ -#define DT_NONE 0 -#define DT_UNKNOWN 0 /* what it says, dude */ -#define DT_BINARY 1 /* binary (1 bit/voxel) */ -#define DT_UNSIGNED_CHAR 2 /* unsigned char (8 bits/voxel) */ -#define DT_SIGNED_SHORT 4 /* signed short (16 bits/voxel) */ -#define DT_SIGNED_INT 8 /* signed int (32 bits/voxel) */ -#define DT_FLOAT 16 /* float (32 bits/voxel) */ -#define DT_COMPLEX 32 /* complex (64 bits/voxel) */ -#define DT_DOUBLE 64 /* double (64 bits/voxel) */ -#define DT_RGB 128 /* RGB triple (24 bits/voxel) */ -#define DT_ALL 255 /* not very useful (?) */ - - /*----- another set of names for the same ---*/ -#define DT_UINT8 2 -#define DT_INT16 4 -#define DT_INT32 8 -#define DT_FLOAT32 16 -#define DT_COMPLEX64 32 -#define DT_FLOAT64 64 -#define DT_RGB24 128 - - /*------------------- new codes for NIFTI ---*/ -#define DT_INT8 256 /* signed char (8 bits) */ -#define DT_UINT16 512 /* unsigned short (16 bits) */ -#define DT_UINT32 768 /* unsigned int (32 bits) */ -#define DT_INT64 1024 /* long long (64 bits) */ -#define DT_UINT64 1280 /* unsigned long long (64 bits) */ -#define DT_FLOAT128 1536 /* long double (128 bits) */ -#define DT_COMPLEX128 1792 /* double pair (128 bits) */ -#define DT_COMPLEX256 2048 /* long double pair (256 bits) */ -#define DT_RGBA32 2304 /* 4 byte RGBA (32 bits/voxel) */ +/*--- the original ANALYZE 7.5 type codes ---*/ +#define DT_NONE 0 +#define DT_UNKNOWN 0 /* what it says, dude */ +#define DT_BINARY 1 /* binary (1 bit/voxel) */ +#define DT_UNSIGNED_CHAR 2 /* unsigned char (8 bits/voxel) */ +#define DT_SIGNED_SHORT 4 /* signed short (16 bits/voxel) */ +#define DT_SIGNED_INT 8 /* signed int (32 bits/voxel) */ +#define DT_FLOAT 16 /* float (32 bits/voxel) */ +#define DT_COMPLEX 32 /* complex (64 bits/voxel) */ +#define DT_DOUBLE 64 /* double (64 bits/voxel) */ +#define DT_RGB 128 /* RGB triple (24 bits/voxel) */ +#define DT_ALL 255 /* not very useful (?) */ + +/*----- another set of names for the same ---*/ +#define DT_UINT8 2 +#define DT_INT16 4 +#define DT_INT32 8 +#define DT_FLOAT32 16 +#define DT_COMPLEX64 32 +#define DT_FLOAT64 64 +#define DT_RGB24 128 + +/*------------------- new codes for NIFTI ---*/ +#define DT_INT8 256 /* signed char (8 bits) */ +#define DT_UINT16 512 /* unsigned short (16 bits) */ +#define DT_UINT32 768 /* unsigned int (32 bits) */ +#define DT_INT64 1024 /* long long (64 bits) */ +#define DT_UINT64 1280 /* unsigned long long (64 bits) */ +#define DT_FLOAT128 1536 /* long double (128 bits) */ +#define DT_COMPLEX128 1792 /* double pair (128 bits) */ +#define DT_COMPLEX256 2048 /* long double pair (256 bits) */ +#define DT_RGBA32 2304 /* 4 byte RGBA (32 bits/voxel) */ /* @} */ - - /*------- aliases for all the above codes ---*/ +/*------- aliases for all the above codes ---*/ /*! \defgroup NIFTI1_DATATYPE_ALIASES - \brief aliases for the nifti1 datatype codes - @{ + \brief aliases for the nifti1 datatype codes + @{ */ - /*! unsigned char. */ -#define NIFTI_TYPE_UINT8 2 - /*! signed short. */ -#define NIFTI_TYPE_INT16 4 - /*! signed int. */ -#define NIFTI_TYPE_INT32 8 - /*! 32 bit float. */ -#define NIFTI_TYPE_FLOAT32 16 - /*! 64 bit complex = 2 32 bit floats. */ -#define NIFTI_TYPE_COMPLEX64 32 - /*! 64 bit float = double. */ -#define NIFTI_TYPE_FLOAT64 64 - /*! 3 8 bit bytes. */ -#define NIFTI_TYPE_RGB24 128 - /*! signed char. */ -#define NIFTI_TYPE_INT8 256 - /*! unsigned short. */ -#define NIFTI_TYPE_UINT16 512 - /*! unsigned int. */ -#define NIFTI_TYPE_UINT32 768 - /*! signed long long. */ -#define NIFTI_TYPE_INT64 1024 - /*! unsigned long long. */ -#define NIFTI_TYPE_UINT64 1280 - /*! 128 bit float = long double. */ -#define NIFTI_TYPE_FLOAT128 1536 - /*! 128 bit complex = 2 64 bit floats. */ -#define NIFTI_TYPE_COMPLEX128 1792 - /*! 256 bit complex = 2 128 bit floats */ -#define NIFTI_TYPE_COMPLEX256 2048 - /*! 4 8 bit bytes. */ -#define NIFTI_TYPE_RGBA32 2304 +/*! unsigned char. */ +#define NIFTI_TYPE_UINT8 2 +/*! signed short. */ +#define NIFTI_TYPE_INT16 4 +/*! signed int. */ +#define NIFTI_TYPE_INT32 8 +/*! 32 bit float. */ +#define NIFTI_TYPE_FLOAT32 16 +/*! 64 bit complex = 2 32 bit floats. */ +#define NIFTI_TYPE_COMPLEX64 32 +/*! 64 bit float = double. */ +#define NIFTI_TYPE_FLOAT64 64 +/*! 3 8 bit bytes. */ +#define NIFTI_TYPE_RGB24 128 +/*! signed char. */ +#define NIFTI_TYPE_INT8 256 +/*! unsigned short. */ +#define NIFTI_TYPE_UINT16 512 +/*! unsigned int. */ +#define NIFTI_TYPE_UINT32 768 +/*! signed long long. */ +#define NIFTI_TYPE_INT64 1024 +/*! unsigned long long. */ +#define NIFTI_TYPE_UINT64 1280 +/*! 128 bit float = long double. */ +#define NIFTI_TYPE_FLOAT128 1536 +/*! 128 bit complex = 2 64 bit floats. */ +#define NIFTI_TYPE_COMPLEX128 1792 +/*! 256 bit complex = 2 128 bit floats */ +#define NIFTI_TYPE_COMPLEX256 2048 +/*! 4 8 bit bytes. */ +#define NIFTI_TYPE_RGBA32 2304 /* @} */ - /*-------- sample typedefs for complicated types ---*/ +/*-------- sample typedefs for complicated types ---*/ #if 0 typedef struct { float r,i; } complex_float ; typedef struct { double r,i; } complex_double ; @@ -580,14 +581,14 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; The 5th dimension of the dataset, if present (i.e., dim[0]=5 and dim[5] > 1), contains multiple values (e.g., a vector) to be stored at each spatiotemporal location. For example, the header values - - dim[0] = 5 - - dim[1] = 64 - - dim[2] = 64 - - dim[3] = 20 - - dim[4] = 1 (indicates no time axis) - - dim[5] = 3 - - datatype = DT_FLOAT - - intent_code = NIFTI_INTENT_VECTOR + - dim[0] = 5 + - dim[1] = 64 + - dim[2] = 64 + - dim[3] = 20 + - dim[4] = 1 (indicates no time axis) + - dim[5] = 3 + - datatype = DT_FLOAT + - intent_code = NIFTI_INTENT_VECTOR mean that this dataset should be interpreted as a 3D volume (64x64x20), with a 3-vector of floats defined at each point in the 3D grid. @@ -610,14 +611,14 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; If the dataset DOES have a 5th dimension, then the auxiliary parameters are different for each voxel. For example, the header values - - dim[0] = 5 - - dim[1] = 128 - - dim[2] = 128 - - dim[3] = 1 (indicates a single slice) - - dim[4] = 1 (indicates no time axis) - - dim[5] = 2 - - datatype = DT_FLOAT - - intent_code = NIFTI_INTENT_TTEST + - dim[0] = 5 + - dim[1] = 128 + - dim[2] = 128 + - dim[3] = 1 (indicates a single slice) + - dim[4] = 1 (indicates no time axis) + - dim[5] = 2 + - datatype = DT_FLOAT + - intent_code = NIFTI_INTENT_TTEST mean that this is a 2D dataset (128x128) of t-statistics, with the t-statistic being in the first "plane" of data and the degrees-of-freedom parameter being in the second "plane" of data. @@ -638,324 +639,323 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; The intent_name field provides space for a 15 character (plus 0 byte) 'name' string for the type of data stored. Examples: - - intent_code = NIFTI_INTENT_ESTIMATE; intent_name = "T1"; - could be used to signify that the voxel values are estimates of the - NMR parameter T1. - - intent_code = NIFTI_INTENT_TTEST; intent_name = "House"; - could be used to signify that the voxel values are t-statistics - for the significance of 'activation' response to a House stimulus. - - intent_code = NIFTI_INTENT_DISPVECT; intent_name = "ToMNI152"; - could be used to signify that the voxel values are a displacement - vector that transforms each voxel (x,y,z) location to the - corresponding location in the MNI152 standard brain. - - intent_code = NIFTI_INTENT_SYMMATRIX; intent_name = "DTI"; - could be used to signify that the voxel values comprise a diffusion - tensor image. + - intent_code = NIFTI_INTENT_ESTIMATE; intent_name = "T1"; + could be used to signify that the voxel values are estimates of the + NMR parameter T1. + - intent_code = NIFTI_INTENT_TTEST; intent_name = "House"; + could be used to signify that the voxel values are t-statistics + for the significance of 'activation' response to a House stimulus. + - intent_code = NIFTI_INTENT_DISPVECT; intent_name = "ToMNI152"; + could be used to signify that the voxel values are a displacement + vector that transforms each voxel (x,y,z) location to the + corresponding location in the MNI152 standard brain. + - intent_code = NIFTI_INTENT_SYMMATRIX; intent_name = "DTI"; + could be used to signify that the voxel values comprise a diffusion + tensor image. If no data name is implied or needed, intent_name[0] should be set to 0. -----------------------------------------------------------------------------*/ - /*! default: no intention is indicated in the header. */ +/*! default: no intention is indicated in the header. */ -#define NIFTI_INTENT_NONE 0 +#define NIFTI_INTENT_NONE 0 - /*-------- These codes are for probability distributions ---------------*/ - /* Most distributions have a number of parameters, - below denoted by p1, p2, and p3, and stored in - - intent_p1, intent_p2, intent_p3 if dataset doesn't have 5th dimension - - image data array if dataset does have 5th dimension +/*-------- These codes are for probability distributions ---------------*/ +/* Most distributions have a number of parameters, + below denoted by p1, p2, and p3, and stored in + - intent_p1, intent_p2, intent_p3 if dataset doesn't have 5th dimension + - image data array if dataset does have 5th dimension - Functions to compute with many of the distributions below can be found - in the CDF library from U Texas. + Functions to compute with many of the distributions below can be found + in the CDF library from U Texas. - Formulas for and discussions of these distributions can be found in the - following books: + Formulas for and discussions of these distributions can be found in the + following books: - [U] Univariate Discrete Distributions, - NL Johnson, S Kotz, AW Kemp. + [U] Univariate Discrete Distributions, + NL Johnson, S Kotz, AW Kemp. - [C1] Continuous Univariate Distributions, vol. 1, - NL Johnson, S Kotz, N Balakrishnan. + [C1] Continuous Univariate Distributions, vol. 1, + NL Johnson, S Kotz, N Balakrishnan. - [C2] Continuous Univariate Distributions, vol. 2, - NL Johnson, S Kotz, N Balakrishnan. */ - /*----------------------------------------------------------------------*/ + [C2] Continuous Univariate Distributions, vol. 2, + NL Johnson, S Kotz, N Balakrishnan. */ +/*----------------------------------------------------------------------*/ - /*! [C2, chap 32] Correlation coefficient R (1 param): - p1 = degrees of freedom - R/sqrt(1-R*R) is t-distributed with p1 DOF. */ +/*! [C2, chap 32] Correlation coefficient R (1 param): + p1 = degrees of freedom + R/sqrt(1-R*R) is t-distributed with p1 DOF. */ /*! \defgroup NIFTI1_INTENT_CODES - \brief nifti1 intent codes, to describe intended meaning of dataset contents - @{ + \brief nifti1 intent codes, to describe intended meaning of dataset contents + @{ */ -#define NIFTI_INTENT_CORREL 2 +#define NIFTI_INTENT_CORREL 2 - /*! [C2, chap 28] Student t statistic (1 param): p1 = DOF. */ +/*! [C2, chap 28] Student t statistic (1 param): p1 = DOF. */ -#define NIFTI_INTENT_TTEST 3 +#define NIFTI_INTENT_TTEST 3 - /*! [C2, chap 27] Fisher F statistic (2 params): - p1 = numerator DOF, p2 = denominator DOF. */ +/*! [C2, chap 27] Fisher F statistic (2 params): + p1 = numerator DOF, p2 = denominator DOF. */ -#define NIFTI_INTENT_FTEST 4 +#define NIFTI_INTENT_FTEST 4 - /*! [C1, chap 13] Standard normal (0 params): Density = N(0,1). */ +/*! [C1, chap 13] Standard normal (0 params): Density = N(0,1). */ -#define NIFTI_INTENT_ZSCORE 5 +#define NIFTI_INTENT_ZSCORE 5 - /*! [C1, chap 18] Chi-squared (1 param): p1 = DOF. - Density(x) proportional to exp(-x/2) * x^(p1/2-1). */ +/*! [C1, chap 18] Chi-squared (1 param): p1 = DOF. + Density(x) proportional to exp(-x/2) * x^(p1/2-1). */ -#define NIFTI_INTENT_CHISQ 6 +#define NIFTI_INTENT_CHISQ 6 - /*! [C2, chap 25] Beta distribution (2 params): p1=a, p2=b. - Density(x) proportional to x^(a-1) * (1-x)^(b-1). */ +/*! [C2, chap 25] Beta distribution (2 params): p1=a, p2=b. + Density(x) proportional to x^(a-1) * (1-x)^(b-1). */ -#define NIFTI_INTENT_BETA 7 +#define NIFTI_INTENT_BETA 7 - /*! [U, chap 3] Binomial distribution (2 params): - p1 = number of trials, p2 = probability per trial. - Prob(x) = (p1 choose x) * p2^x * (1-p2)^(p1-x), for x=0,1,...,p1. */ +/*! [U, chap 3] Binomial distribution (2 params): + p1 = number of trials, p2 = probability per trial. + Prob(x) = (p1 choose x) * p2^x * (1-p2)^(p1-x), for x=0,1,...,p1. */ -#define NIFTI_INTENT_BINOM 8 +#define NIFTI_INTENT_BINOM 8 - /*! [C1, chap 17] Gamma distribution (2 params): - p1 = shape, p2 = scale. - Density(x) proportional to x^(p1-1) * exp(-p2*x). */ +/*! [C1, chap 17] Gamma distribution (2 params): + p1 = shape, p2 = scale. + Density(x) proportional to x^(p1-1) * exp(-p2*x). */ -#define NIFTI_INTENT_GAMMA 9 +#define NIFTI_INTENT_GAMMA 9 - /*! [U, chap 4] Poisson distribution (1 param): p1 = mean. - Prob(x) = exp(-p1) * p1^x / x! , for x=0,1,2,.... */ +/*! [U, chap 4] Poisson distribution (1 param): p1 = mean. + Prob(x) = exp(-p1) * p1^x / x! , for x=0,1,2,.... */ -#define NIFTI_INTENT_POISSON 10 +#define NIFTI_INTENT_POISSON 10 - /*! [C1, chap 13] Normal distribution (2 params): - p1 = mean, p2 = standard deviation. */ +/*! [C1, chap 13] Normal distribution (2 params): + p1 = mean, p2 = standard deviation. */ -#define NIFTI_INTENT_NORMAL 11 +#define NIFTI_INTENT_NORMAL 11 - /*! [C2, chap 30] Noncentral F statistic (3 params): - p1 = numerator DOF, p2 = denominator DOF, - p3 = numerator noncentrality parameter. */ +/*! [C2, chap 30] Noncentral F statistic (3 params): + p1 = numerator DOF, p2 = denominator DOF, + p3 = numerator noncentrality parameter. */ #define NIFTI_INTENT_FTEST_NONC 12 - /*! [C2, chap 29] Noncentral chi-squared statistic (2 params): - p1 = DOF, p2 = noncentrality parameter. */ +/*! [C2, chap 29] Noncentral chi-squared statistic (2 params): + p1 = DOF, p2 = noncentrality parameter. */ #define NIFTI_INTENT_CHISQ_NONC 13 - /*! [C2, chap 23] Logistic distribution (2 params): - p1 = location, p2 = scale. - Density(x) proportional to sech^2((x-p1)/(2*p2)). */ +/*! [C2, chap 23] Logistic distribution (2 params): + p1 = location, p2 = scale. + Density(x) proportional to sech^2((x-p1)/(2*p2)). */ -#define NIFTI_INTENT_LOGISTIC 14 +#define NIFTI_INTENT_LOGISTIC 14 - /*! [C2, chap 24] Laplace distribution (2 params): - p1 = location, p2 = scale. - Density(x) proportional to exp(-abs(x-p1)/p2). */ +/*! [C2, chap 24] Laplace distribution (2 params): + p1 = location, p2 = scale. + Density(x) proportional to exp(-abs(x-p1)/p2). */ -#define NIFTI_INTENT_LAPLACE 15 +#define NIFTI_INTENT_LAPLACE 15 - /*! [C2, chap 26] Uniform distribution: p1 = lower end, p2 = upper end. */ +/*! [C2, chap 26] Uniform distribution: p1 = lower end, p2 = upper end. */ -#define NIFTI_INTENT_UNIFORM 16 +#define NIFTI_INTENT_UNIFORM 16 - /*! [C2, chap 31] Noncentral t statistic (2 params): - p1 = DOF, p2 = noncentrality parameter. */ +/*! [C2, chap 31] Noncentral t statistic (2 params): + p1 = DOF, p2 = noncentrality parameter. */ #define NIFTI_INTENT_TTEST_NONC 17 - /*! [C1, chap 21] Weibull distribution (3 params): - p1 = location, p2 = scale, p3 = power. - Density(x) proportional to - ((x-p1)/p2)^(p3-1) * exp(-((x-p1)/p2)^p3) for x > p1. */ - -#define NIFTI_INTENT_WEIBULL 18 +/*! [C1, chap 21] Weibull distribution (3 params): + p1 = location, p2 = scale, p3 = power. + Density(x) proportional to + ((x-p1)/p2)^(p3-1) * exp(-((x-p1)/p2)^p3) for x > p1. */ - /*! [C1, chap 18] Chi distribution (1 param): p1 = DOF. - Density(x) proportional to x^(p1-1) * exp(-x^2/2) for x > 0. - p1 = 1 = 'half normal' distribution - p1 = 2 = Rayleigh distribution - p1 = 3 = Maxwell-Boltzmann distribution. */ +#define NIFTI_INTENT_WEIBULL 18 -#define NIFTI_INTENT_CHI 19 +/*! [C1, chap 18] Chi distribution (1 param): p1 = DOF. + Density(x) proportional to x^(p1-1) * exp(-x^2/2) for x > 0. + p1 = 1 = 'half normal' distribution + p1 = 2 = Rayleigh distribution + p1 = 3 = Maxwell-Boltzmann distribution. */ - /*! [C1, chap 15] Inverse Gaussian (2 params): - p1 = mu, p2 = lambda - Density(x) proportional to - exp(-p2*(x-p1)^2/(2*p1^2*x)) / x^3 for x > 0. */ +#define NIFTI_INTENT_CHI 19 -#define NIFTI_INTENT_INVGAUSS 20 +/*! [C1, chap 15] Inverse Gaussian (2 params): + p1 = mu, p2 = lambda + Density(x) proportional to + exp(-p2*(x-p1)^2/(2*p1^2*x)) / x^3 for x > 0. */ - /*! [C2, chap 22] Extreme value type I (2 params): - p1 = location, p2 = scale - cdf(x) = exp(-exp(-(x-p1)/p2)). */ +#define NIFTI_INTENT_INVGAUSS 20 -#define NIFTI_INTENT_EXTVAL 21 +/*! [C2, chap 22] Extreme value type I (2 params): + p1 = location, p2 = scale + cdf(x) = exp(-exp(-(x-p1)/p2)). */ - /*! Data is a 'p-value' (no params). */ +#define NIFTI_INTENT_EXTVAL 21 -#define NIFTI_INTENT_PVAL 22 +/*! Data is a 'p-value' (no params). */ - /*! Data is ln(p-value) (no params). - To be safe, a program should compute p = exp(-abs(this_value)). - The nifti_stats.c library returns this_value - as positive, so that this_value = -log(p). */ +#define NIFTI_INTENT_PVAL 22 +/*! Data is ln(p-value) (no params). + To be safe, a program should compute p = exp(-abs(this_value)). + The nifti_stats.c library returns this_value + as positive, so that this_value = -log(p). */ -#define NIFTI_INTENT_LOGPVAL 23 +#define NIFTI_INTENT_LOGPVAL 23 - /*! Data is log10(p-value) (no params). - To be safe, a program should compute p = pow(10.,-abs(this_value)). - The nifti_stats.c library returns this_value - as positive, so that this_value = -log10(p). */ +/*! Data is log10(p-value) (no params). + To be safe, a program should compute p = pow(10.,-abs(this_value)). + The nifti_stats.c library returns this_value + as positive, so that this_value = -log10(p). */ -#define NIFTI_INTENT_LOG10PVAL 24 +#define NIFTI_INTENT_LOG10PVAL 24 - /*! Smallest intent_code that indicates a statistic. */ +/*! Smallest intent_code that indicates a statistic. */ -#define NIFTI_FIRST_STATCODE 2 +#define NIFTI_FIRST_STATCODE 2 - /*! Largest intent_code that indicates a statistic. */ +/*! Largest intent_code that indicates a statistic. */ -#define NIFTI_LAST_STATCODE 24 +#define NIFTI_LAST_STATCODE 24 - /*---------- these values for intent_code aren't for statistics ----------*/ +/*---------- these values for intent_code aren't for statistics ----------*/ - /*! To signify that the value at each voxel is an estimate - of some parameter, set intent_code = NIFTI_INTENT_ESTIMATE. - The name of the parameter may be stored in intent_name. */ +/*! To signify that the value at each voxel is an estimate + of some parameter, set intent_code = NIFTI_INTENT_ESTIMATE. + The name of the parameter may be stored in intent_name. */ -#define NIFTI_INTENT_ESTIMATE 1001 +#define NIFTI_INTENT_ESTIMATE 1001 - /*! To signify that the value at each voxel is an index into - some set of labels, set intent_code = NIFTI_INTENT_LABEL. - The filename with the labels may stored in aux_file. */ +/*! To signify that the value at each voxel is an index into + some set of labels, set intent_code = NIFTI_INTENT_LABEL. + The filename with the labels may stored in aux_file. */ -#define NIFTI_INTENT_LABEL 1002 +#define NIFTI_INTENT_LABEL 1002 - /*! To signify that the value at each voxel is an index into the - NeuroNames labels set, set intent_code = NIFTI_INTENT_NEURONAME. */ +/*! To signify that the value at each voxel is an index into the + NeuroNames labels set, set intent_code = NIFTI_INTENT_NEURONAME. */ #define NIFTI_INTENT_NEURONAME 1003 - /*! To store an M x N matrix at each voxel: - - dataset must have a 5th dimension (dim[0]=5 and dim[5]>1) - - intent_code must be NIFTI_INTENT_GENMATRIX - - dim[5] must be M*N - - intent_p1 must be M (in float format) - - intent_p2 must be N (ditto) - - the matrix values A[i][[j] are stored in row-order: - - A[0][0] A[0][1] ... A[0][N-1] - - A[1][0] A[1][1] ... A[1][N-1] - - etc., until - - A[M-1][0] A[M-1][1] ... A[M-1][N-1] */ +/*! To store an M x N matrix at each voxel: + - dataset must have a 5th dimension (dim[0]=5 and dim[5]>1) + - intent_code must be NIFTI_INTENT_GENMATRIX + - dim[5] must be M*N + - intent_p1 must be M (in float format) + - intent_p2 must be N (ditto) + - the matrix values A[i][[j] are stored in row-order: + - A[0][0] A[0][1] ... A[0][N-1] + - A[1][0] A[1][1] ... A[1][N-1] + - etc., until + - A[M-1][0] A[M-1][1] ... A[M-1][N-1] */ #define NIFTI_INTENT_GENMATRIX 1004 - /*! To store an NxN symmetric matrix at each voxel: - - dataset must have a 5th dimension - - intent_code must be NIFTI_INTENT_SYMMATRIX - - dim[5] must be N*(N+1)/2 - - intent_p1 must be N (in float format) - - the matrix values A[i][[j] are stored in row-order: - - A[0][0] - - A[1][0] A[1][1] - - A[2][0] A[2][1] A[2][2] - - etc.: row-by-row */ +/*! To store an NxN symmetric matrix at each voxel: + - dataset must have a 5th dimension + - intent_code must be NIFTI_INTENT_SYMMATRIX + - dim[5] must be N*(N+1)/2 + - intent_p1 must be N (in float format) + - the matrix values A[i][[j] are stored in row-order: + - A[0][0] + - A[1][0] A[1][1] + - A[2][0] A[2][1] A[2][2] + - etc.: row-by-row */ #define NIFTI_INTENT_SYMMATRIX 1005 - /*! To signify that the vector value at each voxel is to be taken - as a displacement field or vector: - - dataset must have a 5th dimension - - intent_code must be NIFTI_INTENT_DISPVECT - - dim[5] must be the dimensionality of the displacement - vector (e.g., 3 for spatial displacement, 2 for in-plane) */ - -#define NIFTI_INTENT_DISPVECT 1006 /* specifically for displacements */ -#define NIFTI_INTENT_VECTOR 1007 /* for any other type of vector */ - - /*! To signify that the vector value at each voxel is really a - spatial coordinate (e.g., the vertices or nodes of a surface mesh): - - dataset must have a 5th dimension - - intent_code must be NIFTI_INTENT_POINTSET - - dim[0] = 5 - - dim[1] = number of points - - dim[2] = dim[3] = dim[4] = 1 - - dim[5] must be the dimensionality of space (e.g., 3 => 3D space). - - intent_name may describe the object these points come from - (e.g., "pial", "gray/white" , "EEG", "MEG"). */ - -#define NIFTI_INTENT_POINTSET 1008 - - /*! To signify that the vector value at each voxel is really a triple - of indexes (e.g., forming a triangle) from a pointset dataset: - - dataset must have a 5th dimension - - intent_code must be NIFTI_INTENT_TRIANGLE - - dim[0] = 5 - - dim[1] = number of triangles - - dim[2] = dim[3] = dim[4] = 1 - - dim[5] = 3 - - datatype should be an integer type (preferably DT_INT32) - - the data values are indexes (0,1,...) into a pointset dataset. */ - -#define NIFTI_INTENT_TRIANGLE 1009 - - /*! To signify that the vector value at each voxel is a quaternion: - - dataset must have a 5th dimension - - intent_code must be NIFTI_INTENT_QUATERNION - - dim[0] = 5 - - dim[5] = 4 - - datatype should be a floating point type */ +/*! To signify that the vector value at each voxel is to be taken + as a displacement field or vector: + - dataset must have a 5th dimension + - intent_code must be NIFTI_INTENT_DISPVECT + - dim[5] must be the dimensionality of the displacement + vector (e.g., 3 for spatial displacement, 2 for in-plane) */ + +#define NIFTI_INTENT_DISPVECT 1006 /* specifically for displacements */ +#define NIFTI_INTENT_VECTOR 1007 /* for any other type of vector */ + +/*! To signify that the vector value at each voxel is really a + spatial coordinate (e.g., the vertices or nodes of a surface mesh): + - dataset must have a 5th dimension + - intent_code must be NIFTI_INTENT_POINTSET + - dim[0] = 5 + - dim[1] = number of points + - dim[2] = dim[3] = dim[4] = 1 + - dim[5] must be the dimensionality of space (e.g., 3 => 3D space). + - intent_name may describe the object these points come from + (e.g., "pial", "gray/white" , "EEG", "MEG"). */ + +#define NIFTI_INTENT_POINTSET 1008 + +/*! To signify that the vector value at each voxel is really a triple + of indexes (e.g., forming a triangle) from a pointset dataset: + - dataset must have a 5th dimension + - intent_code must be NIFTI_INTENT_TRIANGLE + - dim[0] = 5 + - dim[1] = number of triangles + - dim[2] = dim[3] = dim[4] = 1 + - dim[5] = 3 + - datatype should be an integer type (preferably DT_INT32) + - the data values are indexes (0,1,...) into a pointset dataset. */ + +#define NIFTI_INTENT_TRIANGLE 1009 + +/*! To signify that the vector value at each voxel is a quaternion: + - dataset must have a 5th dimension + - intent_code must be NIFTI_INTENT_QUATERNION + - dim[0] = 5 + - dim[5] = 4 + - datatype should be a floating point type */ #define NIFTI_INTENT_QUATERNION 1010 - /*! Dimensionless value - no params - although, as in _ESTIMATE - the name of the parameter may be stored in intent_name. */ +/*! Dimensionless value - no params - although, as in _ESTIMATE + the name of the parameter may be stored in intent_name. */ -#define NIFTI_INTENT_DIMLESS 1011 +#define NIFTI_INTENT_DIMLESS 1011 - /*---------- these values apply to GIFTI datasets ----------*/ +/*---------- these values apply to GIFTI datasets ----------*/ - /*! To signify that the value at each location is from a time series. */ +/*! To signify that the value at each location is from a time series. */ -#define NIFTI_INTENT_TIME_SERIES 2001 +#define NIFTI_INTENT_TIME_SERIES 2001 - /*! To signify that the value at each location is a node index, from - a complete surface dataset. */ +/*! To signify that the value at each location is a node index, from + a complete surface dataset. */ -#define NIFTI_INTENT_NODE_INDEX 2002 +#define NIFTI_INTENT_NODE_INDEX 2002 - /*! To signify that the vector value at each location is an RGB triplet, - of whatever type. - - dataset must have a 5th dimension - - dim[0] = 5 - - dim[1] = number of nodes - - dim[2] = dim[3] = dim[4] = 1 - - dim[5] = 3 - */ +/*! To signify that the vector value at each location is an RGB triplet, + of whatever type. + - dataset must have a 5th dimension + - dim[0] = 5 + - dim[1] = number of nodes + - dim[2] = dim[3] = dim[4] = 1 + - dim[5] = 3 + */ -#define NIFTI_INTENT_RGB_VECTOR 2003 +#define NIFTI_INTENT_RGB_VECTOR 2003 - /*! To signify that the vector value at each location is a 4 valued RGBA - vector, of whatever type. - - dataset must have a 5th dimension - - dim[0] = 5 - - dim[1] = number of nodes - - dim[2] = dim[3] = dim[4] = 1 - - dim[5] = 4 - */ +/*! To signify that the vector value at each location is a 4 valued RGBA + vector, of whatever type. + - dataset must have a 5th dimension + - dim[0] = 5 + - dim[1] = number of nodes + - dim[2] = dim[3] = dim[4] = 1 + - dim[5] = 4 + */ -#define NIFTI_INTENT_RGBA_VECTOR 2004 +#define NIFTI_INTENT_RGBA_VECTOR 2004 - /*! To signify that the value at each location is a shape value, such - as the curvature. */ +/*! To signify that the value at each location is a shape value, such + as the curvature. */ -#define NIFTI_INTENT_SHAPE 2005 +#define NIFTI_INTENT_SHAPE 2005 /* @} */ @@ -967,39 +967,39 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; the continuous coordinates are referred to as (x,y,z). The voxel index coordinates (i.e., the array indexes) are referred to as (i,j,k), with valid ranges: - i = 0 .. dim[1]-1 - j = 0 .. dim[2]-1 (if dim[0] >= 2) - k = 0 .. dim[3]-1 (if dim[0] >= 3) + i = 0 .. dim[1]-1 + j = 0 .. dim[2]-1 (if dim[0] >= 2) + k = 0 .. dim[3]-1 (if dim[0] >= 3) The (x,y,z) coordinates refer to the CENTER of a voxel. In methods 2 and 3, the (x,y,z) axes refer to a subject-based coordinate system, with - +x = Right +y = Anterior +z = Superior. + +x = Right +y = Anterior +z = Superior. This is a right-handed coordinate system. However, the exact direction these axes point with respect to the subject depends on qform_code (Method 2) and sform_code (Method 3). N.B.: The i index varies most rapidly, j index next, k index slowest. - Thus, voxel (i,j,k) is stored starting at location - (i + j*dim[1] + k*dim[1]*dim[2]) * (bitpix/8) - into the dataset array. + Thus, voxel (i,j,k) is stored starting at location + (i + j*dim[1] + k*dim[1]*dim[2]) * (bitpix/8) + into the dataset array. N.B.: The ANALYZE 7.5 coordinate system is - +x = Left +y = Anterior +z = Superior - which is a left-handed coordinate system. This backwardness is - too difficult to tolerate, so this NIFTI-1 standard specifies the - coordinate order which is most common in functional neuroimaging. + +x = Left +y = Anterior +z = Superior + which is a left-handed coordinate system. This backwardness is + too difficult to tolerate, so this NIFTI-1 standard specifies the + coordinate order which is most common in functional neuroimaging. N.B.: The 3 methods below all give the locations of the voxel centers - in the (x,y,z) coordinate system. In many cases, programs will wish - to display image data on some other grid. In such a case, the program - will need to convert its desired (x,y,z) values into (i,j,k) values - in order to extract (or interpolate) the image data. This operation - would be done with the inverse transformation to those described below. + in the (x,y,z) coordinate system. In many cases, programs will wish + to display image data on some other grid. In such a case, the program + will need to convert its desired (x,y,z) values into (i,j,k) values + in order to extract (or interpolate) the image data. This operation + would be done with the inverse transformation to those described below. N.B.: Method 2 uses a factor 'qfac' which is either -1 or 1; qfac is - stored in the otherwise unused pixdim[0]. If pixdim[0]=0.0 (which - should not occur), we take qfac=1. Of course, pixdim[0] is only used - when reading a NIFTI-1 header, not when reading an ANALYZE 7.5 header. + stored in the otherwise unused pixdim[0]. If pixdim[0]=0.0 (which + should not occur), we take qfac=1. Of course, pixdim[0] is only used + when reading a NIFTI-1 header, not when reading an ANALYZE 7.5 header. N.B.: The units of (x,y,z) can be specified using the xyzt_units field. @@ -1008,9 +1008,9 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; The coordinate mapping from (i,j,k) to (x,y,z) is the ANALYZE 7.5 way. This is a simple scaling relationship: - x = pixdim[1] * i - y = pixdim[2] * j - z = pixdim[3] * k + x = pixdim[1] * i + y = pixdim[2] * j + z = pixdim[3] * k No particular spatial orientation is attached to these (x,y,z) coordinates. (NIFTI-1 does not have the ANALYZE 7.5 orient field, @@ -1032,9 +1032,9 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; orientation from the tilted position of the subject in the scanner). The formula for (x,y,z) in terms of header parameters and (i,j,k) is: - [ x ] [ R11 R12 R13 ] [ pixdim[1] * i ] [ qoffset_x ] - [ y ] = [ R21 R22 R23 ] [ pixdim[2] * j ] + [ qoffset_y ] - [ z ] [ R31 R32 R33 ] [ qfac * pixdim[3] * k ] [ qoffset_z ] + [ x ] [ R11 R12 R13 ] [ pixdim[1] * i ] [ qoffset_x ] + [ y ] = [ R21 R22 R23 ] [ pixdim[2] * j ] + [ qoffset_y ] + [ z ] [ R31 R32 R33 ] [ qfac * pixdim[3] * k ] [ qoffset_z ] The qoffset_* shifts are in the NIFTI-1 header. Note that the center of the (i,j,k)=(0,0,0) voxel (first value in the dataset array) is @@ -1047,21 +1047,21 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; defined by the quaternion parameters is "proper" (has determinant 1). This may not fit the needs of the data; for example, if the image grid is - i increases from Left-to-Right - j increases from Anterior-to-Posterior - k increases from Inferior-to-Superior + i increases from Left-to-Right + j increases from Anterior-to-Posterior + k increases from Inferior-to-Superior Then (i,j,k) is a left-handed triple. In this example, if qfac=1, the R matrix would have to be - [ 1 0 0 ] - [ 0 -1 0 ] which is "improper" (determinant = -1). - [ 0 0 1 ] + [ 1 0 0 ] + [ 0 -1 0 ] which is "improper" (determinant = -1). + [ 0 0 1 ] If we set qfac=-1, then the R matrix would be - [ 1 0 0 ] - [ 0 -1 0 ] which is proper. - [ 0 0 -1 ] + [ 1 0 0 ] + [ 0 -1 0 ] which is proper. + [ 0 0 -1 ] This R matrix is represented by quaternion [a,b,c,d] = [0,1,0,0] (which encodes a 180 degree rotation about the x-axis). @@ -1071,9 +1071,9 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; The (x,y,z) coordinates are given by a general affine transformation of the (i,j,k) indexes: - x = srow_x[0] * i + srow_x[1] * j + srow_x[2] * k + srow_x[3] - y = srow_y[0] * i + srow_y[1] * j + srow_y[2] * k + srow_y[3] - z = srow_z[0] * i + srow_z[1] * j + srow_z[2] * k + srow_z[3] + x = srow_x[0] * i + srow_x[1] * j + srow_x[2] * k + srow_x[3] + y = srow_y[0] * i + srow_y[1] * j + srow_y[2] * k + srow_y[3] + z = srow_z[0] * i + srow_z[1] * j + srow_z[2] * k + srow_z[3] The srow_* vectors are in the NIFTI_1 header. Note that no use is made of pixdim[] in this method. @@ -1115,18 +1115,18 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; representing rotations. The (proper) 3x3 rotation matrix that corresponds to [a,b,c,d] is - [ a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c ] - R = [ 2*b*c+2*a*d a*a+c*c-b*b-d*d 2*c*d-2*a*b ] - [ 2*b*d-2*a*c 2*c*d+2*a*b a*a+d*d-c*c-b*b ] + [ a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c ] + R = [ 2*b*c+2*a*d a*a+c*c-b*b-d*d 2*c*d-2*a*b ] + [ 2*b*d-2*a*c 2*c*d+2*a*b a*a+d*d-c*c-b*b ] - [ R11 R12 R13 ] - = [ R21 R22 R23 ] - [ R31 R32 R33 ] + [ R11 R12 R13 ] + = [ R21 R22 R23 ] + [ R31 R32 R33 ] If (p,q,r) is a unit 3-vector, then rotation of angle h about that direction is represented by the quaternion - [a,b,c,d] = [cos(h/2), p*sin(h/2), q*sin(h/2), r*sin(h/2)]. + [a,b,c,d] = [cos(h/2), p*sin(h/2), q*sin(h/2), r*sin(h/2)]. Requiring a >= 0 is equivalent to requiring -Pi <= h <= Pi. (Note that [-a,-b,-c,-d] represents the same rotation as [a,b,c,d]; there are 2 @@ -1134,34 +1134,34 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; To rotate a 3-vector (x,y,z) using quaternions, we compute the quaternion product - [0,x',y',z'] = [a,b,c,d] * [0,x,y,z] * [a,-b,-c,-d] + [0,x',y',z'] = [a,b,c,d] * [0,x,y,z] * [a,-b,-c,-d] which is equivalent to the matrix-vector multiply - [ x' ] [ x ] - [ y' ] = R [ y ] (equivalence depends on a*a+b*b+c*c+d*d=1) - [ z' ] [ z ] + [ x' ] [ x ] + [ y' ] = R [ y ] (equivalence depends on a*a+b*b+c*c+d*d=1) + [ z' ] [ z ] Multiplication of 2 quaternions is defined by the following: - [a,b,c,d] = a*1 + b*I + c*J + d*K - where - I*I = J*J = K*K = -1 (I,J,K are square roots of -1) - I*J = K J*K = I K*I = J - J*I = -K K*J = -I I*K = -J (not commutative!) - For example - [a,b,0,0] * [0,0,0,1] = [0,0,-b,a] - since this expands to - (a+b*I)*(K) = (a*K+b*I*K) = (a*K-b*J). + [a,b,c,d] = a*1 + b*I + c*J + d*K + where + I*I = J*J = K*K = -1 (I,J,K are square roots of -1) + I*J = K J*K = I K*I = J + J*I = -K K*J = -I I*K = -J (not commutative!) + For example + [a,b,0,0] * [0,0,0,1] = [0,0,-b,a] + since this expands to + (a+b*I)*(K) = (a*K+b*I*K) = (a*K-b*J). The above formula shows how to go from quaternion (b,c,d) to rotation matrix and direction cosines. Conversely, given R, we can compute the fields for the NIFTI-1 header by - a = 0.5 * sqrt(1+R11+R22+R33) (not stored) - b = 0.25 * (R32-R23) / a => quatern_b - c = 0.25 * (R13-R31) / a => quatern_c - d = 0.25 * (R21-R12) / a => quatern_d + a = 0.5 * sqrt(1+R11+R22+R33) (not stored) + b = 0.25 * (R32-R23) / a => quatern_b + c = 0.25 * (R13-R31) / a => quatern_c + d = 0.25 * (R21-R12) / a => quatern_d If a=0 (a 180 degree rotation), alternative formulas are needed. See the nifti1_io.c function mat44_to_quatern() for an implementation @@ -1177,9 +1177,9 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; Here, (Xd,Yd,Zd) refer to DICOM coordinates, and Xd=-x, Yd=-y, Zd=z, where (x,y,z) refers to the NIFTI coordinate system discussed above. (i.e., DICOM +Xd is Left, +Yd is Posterior, +Zd is Superior, - whereas +x is Right, +y is Anterior , +z is Superior. ) + whereas +x is Right, +y is Anterior , +z is Superior. ) Thus, if the (0020,0032) DICOM attribute is extracted into (px,py,pz), then - qoffset_x = -px qoffset_y = -py qoffset_z = pz + qoffset_x = -px qoffset_y = -py qoffset_z = pz is a reasonable setting when qform_code=NIFTI_XFORM_SCANNER_ANAT. That is, DICOM's coordinate system is 180 degrees rotated about the z-axis @@ -1192,9 +1192,9 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; along the y-axis. If the (0020,0037) attribute is extracted into the value (xa,xb,xc,ya,yb,yc), then the first two columns of the R matrix would be - [ -xa -ya ] - [ -xb -yb ] - [ xc yc ] + [ -xa -ya ] + [ -xb -yb ] + [ xc yc ] The negations are because DICOM's x- and y-axes are reversed relative to NIFTI's. The third column of the R matrix gives the direction of displacement (relative to the subject) along the slice-wise direction. @@ -1207,40 +1207,40 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; (RW Cox) do not understand. -----------------------------------------------------------------------------*/ - /* [qs]form_code value: */ /* x,y,z coordinate system refers to: */ - /*-----------------------*/ /*---------------------------------------*/ +/* [qs]form_code value: */ /* x,y,z coordinate system refers to: */ +/*-----------------------*/ /*---------------------------------------*/ /*! \defgroup NIFTI1_XFORM_CODES - \brief nifti1 xform codes to describe the "standard" coordinate system - @{ + \brief nifti1 xform codes to describe the "standard" coordinate system + @{ */ - /*! Arbitrary coordinates (Method 1). */ +/*! Arbitrary coordinates (Method 1). */ -#define NIFTI_XFORM_UNKNOWN 0 +#define NIFTI_XFORM_UNKNOWN 0 - /*! Scanner-based anatomical coordinates */ +/*! Scanner-based anatomical coordinates */ #define NIFTI_XFORM_SCANNER_ANAT 1 - /*! Coordinates aligned to another file's, - or to anatomical "truth". */ +/*! Coordinates aligned to another file's, + or to anatomical "truth". */ #define NIFTI_XFORM_ALIGNED_ANAT 2 - /*! Coordinates aligned to Talairach- - Tournoux Atlas; (0,0,0)=AC, etc. */ +/*! Coordinates aligned to Talairach- + Tournoux Atlas; (0,0,0)=AC, etc. */ -#define NIFTI_XFORM_TALAIRACH 3 +#define NIFTI_XFORM_TALAIRACH 3 - /*! MNI 152 normalized coordinates. */ +/*! MNI 152 normalized coordinates. */ -#define NIFTI_XFORM_MNI_152 4 +#define NIFTI_XFORM_MNI_152 4 - /*! Normalized coordinates (for - any general standard template - space). Added March 8, 2019. */ +/*! Normalized coordinates (for + any general standard template + space). Added March 8, 2019. */ -#define NIFTI_XFORM_TEMPLATE_OTHER 5 +#define NIFTI_XFORM_TEMPLATE_OTHER 5 /* @} */ @@ -1250,19 +1250,19 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; The codes below can be used in xyzt_units to indicate the units of pixdim. As noted earlier, dimensions 1,2,3 are for x,y,z; dimension 4 is for time (t). - - If dim[4]=1 or dim[0] < 4, there is no time axis. - - A single time series (no space) would be specified with - - dim[0] = 4 (for scalar data) or dim[0] = 5 (for vector data) - - dim[1] = dim[2] = dim[3] = 1 - - dim[4] = number of time points - - pixdim[4] = time step - - xyzt_units indicates units of pixdim[4] - - dim[5] = number of values stored at each time point + - If dim[4]=1 or dim[0] < 4, there is no time axis. + - A single time series (no space) would be specified with + - dim[0] = 4 (for scalar data) or dim[0] = 5 (for vector data) + - dim[1] = dim[2] = dim[3] = 1 + - dim[4] = number of time points + - pixdim[4] = time step + - xyzt_units indicates units of pixdim[4] + - dim[5] = number of values stored at each time point Bits 0..2 of xyzt_units specify the units of pixdim[1..3] - (e.g., spatial units are values 1..7). + (e.g., spatial units are values 1..7). Bits 3..5 of xyzt_units specify the units of pixdim[4] - (e.g., temporal units are multiples of 8). + (e.g., temporal units are multiples of 8). This compression of 2 distinct concepts into 1 byte is due to the limited space available in the 348 byte ANALYZE 7.5 header. The @@ -1282,46 +1282,45 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; -----------------------------------------------------------------------------*/ /*! \defgroup NIFTI1_UNITS - \brief nifti1 units codes to describe the unit of measurement for - each dimension of the dataset - @{ + \brief nifti1 units codes to describe the unit of measurement for + each dimension of the dataset + @{ */ - /*! NIFTI code for unspecified units. */ +/*! NIFTI code for unspecified units. */ #define NIFTI_UNITS_UNKNOWN 0 - /** Space codes are multiples of 1. **/ - /*! NIFTI code for meters. */ -#define NIFTI_UNITS_METER 1 - /*! NIFTI code for millimeters. */ -#define NIFTI_UNITS_MM 2 - /*! NIFTI code for micrometers. */ -#define NIFTI_UNITS_MICRON 3 - - /** Time codes are multiples of 8. **/ - /*! NIFTI code for seconds. */ -#define NIFTI_UNITS_SEC 8 - /*! NIFTI code for milliseconds. */ -#define NIFTI_UNITS_MSEC 16 - /*! NIFTI code for microseconds. */ -#define NIFTI_UNITS_USEC 24 - - /*** These units are for spectral data: ***/ - /*! NIFTI code for Hertz. */ -#define NIFTI_UNITS_HZ 32 - /*! NIFTI code for ppm. */ -#define NIFTI_UNITS_PPM 40 - /*! NIFTI code for radians per second. */ -#define NIFTI_UNITS_RADS 48 +/** Space codes are multiples of 1. **/ +/*! NIFTI code for meters. */ +#define NIFTI_UNITS_METER 1 +/*! NIFTI code for millimeters. */ +#define NIFTI_UNITS_MM 2 +/*! NIFTI code for micrometers. */ +#define NIFTI_UNITS_MICRON 3 + +/** Time codes are multiples of 8. **/ +/*! NIFTI code for seconds. */ +#define NIFTI_UNITS_SEC 8 +/*! NIFTI code for milliseconds. */ +#define NIFTI_UNITS_MSEC 16 +/*! NIFTI code for microseconds. */ +#define NIFTI_UNITS_USEC 24 + +/*** These units are for spectral data: ***/ +/*! NIFTI code for Hertz. */ +#define NIFTI_UNITS_HZ 32 +/*! NIFTI code for ppm. */ +#define NIFTI_UNITS_PPM 40 +/*! NIFTI code for radians per second. */ +#define NIFTI_UNITS_RADS 48 /* @} */ -#undef XYZT_TO_SPACE -#undef XYZT_TO_TIME -#define XYZT_TO_SPACE(xyzt) ( (xyzt) & 0x07 ) -#define XYZT_TO_TIME(xyzt) ( (xyzt) & 0x38 ) +#undef XYZT_TO_SPACE +#undef XYZT_TO_TIME +#define XYZT_TO_SPACE(xyzt) ((xyzt) & 0x07) +#define XYZT_TO_TIME(xyzt) ((xyzt) & 0x38) -#undef SPACE_TIME_TO_XYZT -#define SPACE_TIME_TO_XYZT(ss,tt) ( (((char)(ss)) & 0x07) \ - | (((char)(tt)) & 0x38) ) +#undef SPACE_TIME_TO_XYZT +#define SPACE_TIME_TO_XYZT(ss, tt) ((((char)(ss)) & 0x07) | (((char)(tt)) & 0x38)) /*---------------------------------------------------------------------------*/ /* MRI-SPECIFIC SPATIAL AND TEMPORAL INFORMATION: @@ -1335,39 +1334,39 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; { freq_dim } = These fields encode which spatial dimension (1,2, or 3) { phase_dim } = corresponds to which acquisition dimension for MRI data. { slice_dim } = - Examples: - Rectangular scan multi-slice EPI: - freq_dim = 1 phase_dim = 2 slice_dim = 3 (or some permutation) - Spiral scan multi-slice EPI: - freq_dim = phase_dim = 0 slice_dim = 3 - since the concepts of frequency- and phase-encoding directions - don't apply to spiral scan - - slice_duration = If this is positive, AND if slice_dim is nonzero, - indicates the amount of time used to acquire 1 slice. - slice_duration*dim[slice_dim] can be less than pixdim[4] - with a clustered acquisition method, for example. - - slice_code = If this is nonzero, AND if slice_dim is nonzero, AND - if slice_duration is positive, indicates the timing - pattern of the slice acquisition. The following codes - are defined: - NIFTI_SLICE_SEQ_INC == sequential increasing - NIFTI_SLICE_SEQ_DEC == sequential decreasing - NIFTI_SLICE_ALT_INC == alternating increasing - NIFTI_SLICE_ALT_DEC == alternating decreasing - NIFTI_SLICE_ALT_INC2 == alternating increasing #2 - NIFTI_SLICE_ALT_DEC2 == alternating decreasing #2 + Examples: + Rectangular scan multi-slice EPI: + freq_dim = 1 phase_dim = 2 slice_dim = 3 (or some permutation) + Spiral scan multi-slice EPI: + freq_dim = phase_dim = 0 slice_dim = 3 + since the concepts of frequency- and phase-encoding directions + don't apply to spiral scan + + slice_duration = If this is positive, AND if slice_dim is nonzero, + indicates the amount of time used to acquire 1 slice. + slice_duration*dim[slice_dim] can be less than pixdim[4] + with a clustered acquisition method, for example. + + slice_code = If this is nonzero, AND if slice_dim is nonzero, AND + if slice_duration is positive, indicates the timing + pattern of the slice acquisition. The following codes + are defined: + NIFTI_SLICE_SEQ_INC == sequential increasing + NIFTI_SLICE_SEQ_DEC == sequential decreasing + NIFTI_SLICE_ALT_INC == alternating increasing + NIFTI_SLICE_ALT_DEC == alternating decreasing + NIFTI_SLICE_ALT_INC2 == alternating increasing #2 + NIFTI_SLICE_ALT_DEC2 == alternating decreasing #2 { slice_start } = Indicates the start and end of the slice acquisition { slice_end } = pattern, when slice_code is nonzero. These values - are present to allow for the possible addition of - "padded" slices at either end of the volume, which - don't fit into the slice timing pattern. If there - are no padding slices, then slice_start=0 and - slice_end=dim[slice_dim]-1 are the correct values. - For these values to be meaningful, slice_start must - be non-negative and slice_end must be greater than - slice_start. Otherwise, they should be ignored. + are present to allow for the possible addition of + "padded" slices at either end of the volume, which + don't fit into the slice timing pattern. If there + are no padding slices, then slice_start=0 and + slice_end=dim[slice_dim]-1 are the correct values. + For these values to be meaningful, slice_start must + be non-negative and slice_end must be greater than + slice_start. Otherwise, they should be ignored. The following table indicates the slice timing pattern, relative to time=0 for the first slice acquired, for some sample cases. Here, @@ -1376,13 +1375,13 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; slice index SEQ_INC SEQ_DEC ALT_INC ALT_DEC ALT_INC2 ALT_DEC2 - 6 : n/a n/a n/a n/a n/a n/a n/a = not applicable - 5 : 0.4 0.0 0.2 0.0 0.4 0.2 (slice time offset - 4 : 0.3 0.1 0.4 0.3 0.1 0.0 doesn't apply to - 3 : 0.2 0.2 0.1 0.1 0.3 0.3 slices outside - 2 : 0.1 0.3 0.3 0.4 0.0 0.1 the range - 1 : 0.0 0.4 0.0 0.2 0.2 0.4 slice_start .. - 0 : n/a n/a n/a n/a n/a n/a slice_end) + 6 : n/a n/a n/a n/a n/a n/a n/a = not applicable + 5 : 0.4 0.0 0.2 0.0 0.4 0.2 (slice time offset + 4 : 0.3 0.1 0.4 0.3 0.1 0.0 doesn't apply to + 3 : 0.2 0.2 0.1 0.1 0.3 0.3 slices outside + 2 : 0.1 0.3 0.3 0.4 0.0 0.1 the range + 1 : 0.0 0.4 0.0 0.2 0.2 0.4 slice_start .. + 0 : n/a n/a n/a n/a n/a n/a slice_end) The SEQ slice_codes are sequential ordering (uncommon but not unknown), either increasing in slice number or decreasing (INC or DEC), as @@ -1409,31 +1408,31 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; into the dim_info byte. -----------------------------------------------------------------------------*/ -#undef DIM_INFO_TO_FREQ_DIM -#undef DIM_INFO_TO_PHASE_DIM -#undef DIM_INFO_TO_SLICE_DIM +#undef DIM_INFO_TO_FREQ_DIM +#undef DIM_INFO_TO_PHASE_DIM +#undef DIM_INFO_TO_SLICE_DIM -#define DIM_INFO_TO_FREQ_DIM(di) ( ((di) ) & 0x03 ) -#define DIM_INFO_TO_PHASE_DIM(di) ( ((di) >> 2) & 0x03 ) -#define DIM_INFO_TO_SLICE_DIM(di) ( ((di) >> 4) & 0x03 ) +#define DIM_INFO_TO_FREQ_DIM(di) (((di)) & 0x03) +#define DIM_INFO_TO_PHASE_DIM(di) (((di) >> 2) & 0x03) +#define DIM_INFO_TO_SLICE_DIM(di) (((di) >> 4) & 0x03) -#undef FPS_INTO_DIM_INFO -#define FPS_INTO_DIM_INFO(fd,pd,sd) ( ( ( ((char)(fd)) & 0x03) ) | \ - ( ( ((char)(pd)) & 0x03) << 2 ) | \ - ( ( ((char)(sd)) & 0x03) << 4 ) ) +#undef FPS_INTO_DIM_INFO +#define FPS_INTO_DIM_INFO(fd, pd, sd) (((((char)(fd)) & 0x03)) | \ + ((((char)(pd)) & 0x03) << 2) | \ + ((((char)(sd)) & 0x03) << 4)) /*! \defgroup NIFTI1_SLICE_ORDER - \brief nifti1 slice order codes, describing the acquisition order - of the slices - @{ + \brief nifti1 slice order codes, describing the acquisition order + of the slices + @{ */ -#define NIFTI_SLICE_UNKNOWN 0 -#define NIFTI_SLICE_SEQ_INC 1 -#define NIFTI_SLICE_SEQ_DEC 2 -#define NIFTI_SLICE_ALT_INC 3 -#define NIFTI_SLICE_ALT_DEC 4 -#define NIFTI_SLICE_ALT_INC2 5 /* 05 May 2005: RWCox */ -#define NIFTI_SLICE_ALT_DEC2 6 /* 05 May 2005: RWCox */ +#define NIFTI_SLICE_UNKNOWN 0 +#define NIFTI_SLICE_SEQ_INC 1 +#define NIFTI_SLICE_SEQ_DEC 2 +#define NIFTI_SLICE_ALT_INC 3 +#define NIFTI_SLICE_ALT_DEC 4 +#define NIFTI_SLICE_ALT_INC2 5 /* 05 May 2005: RWCox */ +#define NIFTI_SLICE_ALT_DEC2 6 /* 05 May 2005: RWCox */ /* @} */ /*---------------------------------------------------------------------------*/ @@ -1448,10 +1447,10 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; Some of the fields that may need to be set for other (non-NIFTI aware) software to be happy are: - extents dbh.h says this should be 16384 - regular dbh.h says this should be the character 'r' - glmin, } dbh.h says these values should be the min and max voxel - glmax } values for the entire dataset + extents dbh.h says this should be 16384 + regular dbh.h says this should be the character 'r' + glmin, } dbh.h says these values should be the min and max voxel + glmax } values for the entire dataset It is best to initialize ALL fields in the NIFTI-1 header to 0 (e.g., with calloc()), then fill in what is needed. @@ -1463,37 +1462,38 @@ typedef struct { unsigned char r,g,b; } rgb_byte ; /*.................*/ /*! Given a nifti_1_header struct, check if it has a good magic number. - Returns NIFTI version number (1..9) if magic is good, 0 if it is not. */ + Returns NIFTI version number (1..9) if magic is good, 0 if it is not. */ -#define NIFTI_VERSION(h) \ - ( ( (h).magic[0]=='n' && (h).magic[3]=='\0' && \ - ( (h).magic[1]=='i' || (h).magic[1]=='+' ) && \ - ( (h).magic[2]>='1' && (h).magic[2]<='9' ) ) \ - ? (h).magic[2]-'0' : 0 ) +#define NIFTI_VERSION(h) \ + (((h).magic[0] == 'n' && (h).magic[3] == '\0' && \ + ((h).magic[1] == 'i' || (h).magic[1] == '+') && \ + ((h).magic[2] >= '1' && (h).magic[2] <= '9')) \ + ? (h).magic[2] - '0' \ + : 0) /*.................*/ /*! Check if a nifti_1_header struct says if the data is stored in the - same file or in a separate file. Returns 1 if the data is in the same - file as the header, 0 if it is not. */ + same file or in a separate file. Returns 1 if the data is in the same + file as the header, 0 if it is not. */ -#define NIFTI_ONEFILE(h) ( (h).magic[1] == '+' ) +#define NIFTI_ONEFILE(h) ((h).magic[1] == '+') /*.................*/ /*! Check if a nifti_1_header struct needs to be byte swapped. - Returns 1 if it needs to be swapped, 0 if it does not. */ + Returns 1 if it needs to be swapped, 0 if it does not. */ -#define NIFTI_NEEDS_SWAP(h) ( (h).dim[0] < 0 || (h).dim[0] > 7 ) +#define NIFTI_NEEDS_SWAP(h) ((h).dim[0] < 0 || (h).dim[0] > 7) /*.................*/ /*! Check if a nifti_1_header struct contains a 5th (vector) dimension. - Returns size of 5th dimension if > 1, returns 0 otherwise. */ + Returns size of 5th dimension if > 1, returns 0 otherwise. */ -#define NIFTI_5TH_DIM(h) ( ((h).dim[0]>4 && (h).dim[5]>1) ? (h).dim[5] : 0 ) +#define NIFTI_5TH_DIM(h) (((h).dim[0] > 4 && (h).dim[5] > 1) ? (h).dim[5] : 0) /*****************************************************************************/ /*=================*/ -#ifdef __cplusplus +#ifdef __cplusplus } #endif /*=================*/ diff --git a/console/nifti1_io_core.cpp b/console/nifti1_io_core.cpp index 10567a3c..704a3476 100644 --- a/console/nifti1_io_core.cpp +++ b/console/nifti1_io_core.cpp @@ -1,6 +1,6 @@ -//This unit uses a subset of the functions from the nifti1_io available from -// https://sourceforge.net/projects/niftilib/files/nifticlib/ -//These functions were extended by Chris Rorden (2014) and maintain the same license +// This unit uses a subset of the functions from the nifti1_io available from +// https://sourceforge.net/projects/niftilib/files/nifticlib/ +// These functions were extended by Chris Rorden (2014) and maintain the same license /*****===================================================================*****/ /***** Sample functions to deal with NIFTI-1 and ANALYZE files *****/ /*****...................................................................*****/ @@ -15,801 +15,889 @@ /***** incidental or otherwise, caused by any use of this document. *****/ /*****===================================================================*****/ -#include //requires VS 2015 or later #include "nifti1_io_core.h" -#include -#include -#include #include -#include -#include #include +#include +#include //requires VS 2015 or later +#include #include +#include +#include +#include #ifndef _MSC_VER #include #endif #include "print.h" - #if !defined(USING_R) && !defined(USING_MGH_NIFTI_IO) -void nifti_swap_8bytes( size_t n , void *ar ) // 4 bytes at a time +void nifti_swap_8bytes(size_t n, void *ar) // 4 bytes at a time { - size_t ii ; - unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; - unsigned char tval ; - for( ii=0 ; ii < n ; ii++ ){ - cp1 = cp0; cp2 = cp0+7; - tval = *cp1; *cp1 = *cp2; *cp2 = tval; - cp1++; cp2--; - tval = *cp1; *cp1 = *cp2; *cp2 = tval; - cp1++; cp2--; - tval = *cp1; *cp1 = *cp2; *cp2 = tval; - cp1++; cp2--; - tval = *cp1; *cp1 = *cp2; *cp2 = tval; - cp0 += 8; - } - return ; -} - -void nifti_swap_4bytes( size_t n , void *ar ) // 4 bytes at a time + size_t ii; + unsigned char *cp0 = (unsigned char *)ar, *cp1, *cp2; + unsigned char tval; + for (ii = 0; ii < n; ii++) { + cp1 = cp0; + cp2 = cp0 + 7; + tval = *cp1; + *cp1 = *cp2; + *cp2 = tval; + cp1++; + cp2--; + tval = *cp1; + *cp1 = *cp2; + *cp2 = tval; + cp1++; + cp2--; + tval = *cp1; + *cp1 = *cp2; + *cp2 = tval; + cp1++; + cp2--; + tval = *cp1; + *cp1 = *cp2; + *cp2 = tval; + cp0 += 8; + } + return; +} + +void nifti_swap_4bytes(size_t n, void *ar) // 4 bytes at a time { - size_t ii ; - unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; - unsigned char tval ; - for( ii=0 ; ii < n ; ii++ ){ - cp1 = cp0; cp2 = cp0+3; - tval = *cp1; *cp1 = *cp2; *cp2 = tval; - cp1++; cp2--; - tval = *cp1; *cp1 = *cp2; *cp2 = tval; - cp0 += 4; - } - return ; -} - -void nifti_swap_2bytes( size_t n , void *ar ) // 2 bytes at a time + size_t ii; + unsigned char *cp0 = (unsigned char *)ar, *cp1, *cp2; + unsigned char tval; + for (ii = 0; ii < n; ii++) { + cp1 = cp0; + cp2 = cp0 + 3; + tval = *cp1; + *cp1 = *cp2; + *cp2 = tval; + cp1++; + cp2--; + tval = *cp1; + *cp1 = *cp2; + *cp2 = tval; + cp0 += 4; + } + return; +} + +void nifti_swap_2bytes(size_t n, void *ar) // 2 bytes at a time { - size_t ii ; - unsigned char * cp1 = (unsigned char *)ar, * cp2 ; - unsigned char tval; - for( ii=0 ; ii < n ; ii++ ){ - cp2 = cp1 + 1; - tval = *cp1; *cp1 = *cp2; *cp2 = tval; - cp1 += 2; - } - return ; + size_t ii; + unsigned char *cp1 = (unsigned char *)ar, *cp2; + unsigned char tval; + for (ii = 0; ii < n; ii++) { + cp2 = cp1 + 1; + tval = *cp1; + *cp1 = *cp2; + *cp2 = tval; + cp1 += 2; + } + return; } /*-------------------------------------------------------------------------*/ /*! Byte swap NIFTI-1 file header in various places and ways. - If is_nifti, swap all (even UNUSED) fields of NIfTI header. - Else, swap as a nifti_analyze75 struct. -*//*---------------------------------------------------------------------- */ -void swap_nifti_header( struct nifti_1_header *h ) -{ + If is_nifti, swap all (even UNUSED) fields of NIfTI header. + Else, swap as a nifti_analyze75 struct. +*/ +/*---------------------------------------------------------------------- */ +void swap_nifti_header(struct nifti_1_header *h) { - /* otherwise, swap all NIFTI fields */ + /* otherwise, swap all NIFTI fields */ - nifti_swap_4bytes(1, &h->sizeof_hdr); - nifti_swap_4bytes(1, &h->extents); - nifti_swap_2bytes(1, &h->session_error); + nifti_swap_4bytes(1, &h->sizeof_hdr); + nifti_swap_4bytes(1, &h->extents); + nifti_swap_2bytes(1, &h->session_error); - nifti_swap_2bytes(8, h->dim); - nifti_swap_4bytes(1, &h->intent_p1); - nifti_swap_4bytes(1, &h->intent_p2); - nifti_swap_4bytes(1, &h->intent_p3); + nifti_swap_2bytes(8, h->dim); + nifti_swap_4bytes(1, &h->intent_p1); + nifti_swap_4bytes(1, &h->intent_p2); + nifti_swap_4bytes(1, &h->intent_p3); - nifti_swap_2bytes(1, &h->intent_code); - nifti_swap_2bytes(1, &h->datatype); - nifti_swap_2bytes(1, &h->bitpix); - nifti_swap_2bytes(1, &h->slice_start); + nifti_swap_2bytes(1, &h->intent_code); + nifti_swap_2bytes(1, &h->datatype); + nifti_swap_2bytes(1, &h->bitpix); + nifti_swap_2bytes(1, &h->slice_start); - nifti_swap_4bytes(8, h->pixdim); + nifti_swap_4bytes(8, h->pixdim); - nifti_swap_4bytes(1, &h->vox_offset); - nifti_swap_4bytes(1, &h->scl_slope); - nifti_swap_4bytes(1, &h->scl_inter); - nifti_swap_2bytes(1, &h->slice_end); + nifti_swap_4bytes(1, &h->vox_offset); + nifti_swap_4bytes(1, &h->scl_slope); + nifti_swap_4bytes(1, &h->scl_inter); + nifti_swap_2bytes(1, &h->slice_end); - nifti_swap_4bytes(1, &h->cal_max); - nifti_swap_4bytes(1, &h->cal_min); - nifti_swap_4bytes(1, &h->slice_duration); - nifti_swap_4bytes(1, &h->toffset); - nifti_swap_4bytes(1, &h->glmax); - nifti_swap_4bytes(1, &h->glmin); + nifti_swap_4bytes(1, &h->cal_max); + nifti_swap_4bytes(1, &h->cal_min); + nifti_swap_4bytes(1, &h->slice_duration); + nifti_swap_4bytes(1, &h->toffset); + nifti_swap_4bytes(1, &h->glmax); + nifti_swap_4bytes(1, &h->glmin); - nifti_swap_2bytes(1, &h->qform_code); - nifti_swap_2bytes(1, &h->sform_code); + nifti_swap_2bytes(1, &h->qform_code); + nifti_swap_2bytes(1, &h->sform_code); - nifti_swap_4bytes(1, &h->quatern_b); - nifti_swap_4bytes(1, &h->quatern_c); - nifti_swap_4bytes(1, &h->quatern_d); - nifti_swap_4bytes(1, &h->qoffset_x); - nifti_swap_4bytes(1, &h->qoffset_y); - nifti_swap_4bytes(1, &h->qoffset_z); + nifti_swap_4bytes(1, &h->quatern_b); + nifti_swap_4bytes(1, &h->quatern_c); + nifti_swap_4bytes(1, &h->quatern_d); + nifti_swap_4bytes(1, &h->qoffset_x); + nifti_swap_4bytes(1, &h->qoffset_y); + nifti_swap_4bytes(1, &h->qoffset_z); - nifti_swap_4bytes(4, h->srow_x); - nifti_swap_4bytes(4, h->srow_y); - nifti_swap_4bytes(4, h->srow_z); + nifti_swap_4bytes(4, h->srow_x); + nifti_swap_4bytes(4, h->srow_y); + nifti_swap_4bytes(4, h->srow_z); - return ; + return; } #endif -bool littleEndianPlatform () -{ - uint32_t value = 1; - return (*((char *) &value) == 1); +bool littleEndianPlatform() { + uint32_t value = 1; + return (*((char *)&value) == 1); } -int isSameFloat (float a, float b) { - return (fabs (a - b) <= FLT_EPSILON); +int isSameFloat(float a, float b) { + return (fabs(a - b) <= FLT_EPSILON); } -int isSameDouble (double a, double b) { - return (fabs (a - b) <= DBL_EPSILON); +int isSameDouble(double a, double b) { + return (fabs(a - b) <= DBL_EPSILON); } -ivec3 setiVec3(int x, int y, int z) -{ - ivec3 v = {{x, y, z}}; - return v; +ivec3 setiVec3(int x, int y, int z) { + ivec3 v = {{x, y, z}}; + return v; } -vec3 setVec3(float x, float y, float z) -{ - vec3 v = {{x, y, z}}; - return v; +vec3 setVec3(float x, float y, float z) { + vec3 v = {{x, y, z}}; + return v; } -vec4 setVec4(float x, float y, float z) -{ - vec4 v= {{x, y, z, 1}}; - return v; +vec4 setVec4(float x, float y, float z) { + vec4 v = {{x, y, z, 1}}; + return v; } -vec3 crossProduct(vec3 u, vec3 v) -{ - return setVec3(u.v[1]*v.v[2] - v.v[1]*u.v[2], - -u.v[0]*v.v[2] + v.v[0]*u.v[2], - u.v[0]*v.v[1] - v.v[0]*u.v[1]); +vec3 crossProduct(vec3 u, vec3 v) { + return setVec3(u.v[1] * v.v[2] - v.v[1] * u.v[2], + -u.v[0] * v.v[2] + v.v[0] * u.v[2], + u.v[0] * v.v[1] - v.v[0] * u.v[1]); } -float dotProduct(vec3 u, vec3 v) -{ - return (u.v[0]*v.v[0] + v.v[1]*u.v[1] + v.v[2]*u.v[2]); -} - -vec3 nifti_vect33_norm (vec3 v) { //normalize vector length - vec3 vO = v; - float vLen = sqrt( (v.v[0]*v.v[0]) - + (v.v[1]*v.v[1]) - + (v.v[2]*v.v[2])); - if (vLen <= FLT_EPSILON) return vO; //avoid divide by zero - for (int i = 0; i < 3; i++) - vO.v[i] = v.v[i]/vLen; - return vO; -} - -vec4 nifti_vect44_norm (vec4 v) { //normalize vector length - vec4 vO = v; - float vLen = sqrt( (v.v[0]*v.v[0]) - + (v.v[1]*v.v[1]) - + (v.v[2]*v.v[2]) - + (v.v[3]*v.v[3])); - if (vLen <= FLT_EPSILON) return vO; //avoid divide by zero - for (int i = 0; i < 4; i++) - vO.v[i] = v.v[i]/vLen; - return vO; -} - -vec3 nifti_vect33mat33_mul(vec3 v, mat33 m ) { //multiply vector * 3x3matrix - vec3 vO; - for (int i=0; i<3; i++) { //multiply Pcrs * m - vO.v[i] = 0; - for(int j=0; j<3; j++) - vO.v[i] += m.m[i][j]*v.v[j]; - } - return vO; -} - -vec4 nifti_vect44mat44_mul(vec4 v, mat44 m ) { //multiply vector * 4x4matrix - vec4 vO; - for (int i=0; i<4; i++) { //multiply Pcrs * m - vO.v[i] = 0; - for(int j=0; j<4; j++) - vO.v[i] += m.m[i][j]*v.v[j]; - } - return vO; +float dotProduct(vec3 u, vec3 v) { + return (u.v[0] * v.v[0] + v.v[1] * u.v[1] + v.v[2] * u.v[2]); } -mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4]) { - //create NIfTI header based on values from DICOM header - //note orient has 6 values, indexed from 1, patient position and xyzMM have 3 values indexed from 1 - mat33 Q, diagVox; - Q.m[0][0] = orient[1]; Q.m[0][1] = orient[2] ; Q.m[0][2] = orient[3] ; // load Q - Q.m[1][0] = orient[4]; Q.m[1][1] = orient[5] ; Q.m[1][2] = orient[6]; - //printMessage("Orient %g %g %g %g %g %g\n",orient[1],orient[2],orient[3],orient[4],orient[5],orient[6] ); - /* normalize row 1 */ - double val = Q.m[0][0]*Q.m[0][0] + Q.m[0][1]*Q.m[0][1] + Q.m[0][2]*Q.m[0][2] ; - if( val > 0.0l ){ - val = 1.0l / sqrt(val) ; - Q.m[0][0] *= (float)val ; Q.m[0][1] *= (float)val ; Q.m[0][2] *= (float)val ; - } else { - Q.m[0][0] = 1.0l ; Q.m[0][1] = 0.0l ; Q.m[0][2] = 0.0l ; - } - /* normalize row 2 */ - val = Q.m[1][0]*Q.m[1][0] + Q.m[1][1]*Q.m[1][1] + Q.m[1][2]*Q.m[1][2] ; - if( val > 0.0l ){ - val = 1.0l / sqrt(val) ; - Q.m[1][0] *= (float)val ; Q.m[1][1] *= (float)val ; Q.m[1][2] *= (float)val ; - } else { - Q.m[1][0] = 0.0l ; Q.m[1][1] = 1.0l ; Q.m[1][2] = 0.0l ; - } - /* row 3 is the cross product of rows 1 and 2*/ - Q.m[2][0] = Q.m[0][1]*Q.m[1][2] - Q.m[0][2]*Q.m[1][1] ; /* cross */ - Q.m[2][1] = Q.m[0][2]*Q.m[1][0] - Q.m[0][0]*Q.m[1][2] ; /* product */ - Q.m[2][2] = Q.m[0][0]*Q.m[1][1] - Q.m[0][1]*Q.m[1][0] ; - Q = nifti_mat33_transpose(Q); - if (nifti_mat33_determ(Q) < 0.0) { - Q.m[0][2] = -Q.m[0][2]; - Q.m[1][2] = -Q.m[1][2]; - Q.m[2][2] = -Q.m[2][2]; - } - //next scale matrix - LOAD_MAT33(diagVox, xyzMM[1],0.0l,0.0l, 0.0l,xyzMM[2],0.0l, 0.0l,0.0l, xyzMM[3]); - Q = nifti_mat33_mul(Q,diagVox); - mat44 Q44; //4x4 matrix includes translations - LOAD_MAT44(Q44, Q.m[0][0],Q.m[0][1],Q.m[0][2],patientPosition[1], - Q.m[1][0],Q.m[1][1],Q.m[1][2],patientPosition[2], - Q.m[2][0],Q.m[2][1],Q.m[2][2],patientPosition[3]); - return Q44; +vec3 nifti_vect33_norm(vec3 v) { // normalize vector length + vec3 vO = v; + float vLen = sqrt((v.v[0] * v.v[0]) + (v.v[1] * v.v[1]) + (v.v[2] * v.v[2])); + if (vLen <= FLT_EPSILON) + return vO; // avoid divide by zero + for (int i = 0; i < 3; i++) + vO.v[i] = v.v[i] / vLen; + return vO; } -#if !defined(USING_R) && !defined(USING_MGH_NIFTI_IO) -float nifti_mat33_determ( mat33 R ) /* determinant of 3x3 matrix */ -{ - double r11,r12,r13,r21,r22,r23,r31,r32,r33 ; - /* INPUT MATRIX: */ - r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; /* [ r11 r12 r13 ] */ - r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; /* [ r21 r22 r23 ] */ - r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; /* [ r31 r32 r33 ] */ - return (float)(r11*r22*r33-r11*r32*r23-r21*r12*r33 - +r21*r32*r13+r31*r12*r23-r31*r22*r13) ; +vec4 nifti_vect44_norm(vec4 v) { // normalize vector length + vec4 vO = v; + float vLen = sqrt((v.v[0] * v.v[0]) + (v.v[1] * v.v[1]) + (v.v[2] * v.v[2]) + (v.v[3] * v.v[3])); + if (vLen <= FLT_EPSILON) + return vO; // avoid divide by zero + for (int i = 0; i < 4; i++) + vO.v[i] = v.v[i] / vLen; + return vO; } -mat33 nifti_mat33_mul( mat33 A , mat33 B ) /* multiply 2 3x3 matrices */ -//see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c -{ - mat33 C ; int i,j ; - for( i=0 ; i < 3 ; i++ ) - for( j=0 ; j < 3 ; j++ ) - C.m[i][j] = A.m[i][0] * B.m[0][j] - + A.m[i][1] * B.m[1][j] - + A.m[i][2] * B.m[2][j] ; - return C ; +vec3 nifti_vect33mat33_mul(vec3 v, mat33 m) { // multiply vector * 3x3matrix + vec3 vO; + for (int i = 0; i < 3; i++) { // multiply Pcrs * m + vO.v[i] = 0; + for (int j = 0; j < 3; j++) + vO.v[i] += m.m[i][j] * v.v[j]; + } + return vO; } -#endif -mat44 nifti_mat44_mul( mat44 A , mat44 B ) /* multiply 2 3x3 matrices */ -{ - mat44 C ; int i,j ; - for( i=0 ; i < 4 ; i++ ) - for( j=0 ; j < 4; j++ ) - C.m[i][j] = A.m[i][0] * B.m[0][j] - + A.m[i][1] * B.m[1][j] - + A.m[i][2] * B.m[2][j] - + A.m[i][3] * B.m[3][j]; - return C ; -} - -mat33 nifti_mat33_transpose( mat33 A ) /* transpose 3x3 matrix */ -//see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c -{ - mat33 B; int i,j ; - for( i=0 ; i < 3 ; i++ ) - for( j=0 ; j < 3 ; j++ ) - B.m[i][j] = A.m[j][i]; - return B; +vec4 nifti_vect44mat44_mul(vec4 v, mat44 m) { // multiply vector * 4x4matrix + vec4 vO; + for (int i = 0; i < 4; i++) { // multiply Pcrs * m + vO.v[i] = 0; + for (int j = 0; j < 4; j++) + vO.v[i] += m.m[i][j] * v.v[j]; + } + return vO; +} + +mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4]) { + // create NIfTI header based on values from DICOM header + // note orient has 6 values, indexed from 1, patient position and xyzMM have 3 values indexed from 1 + mat33 Q, diagVox; + Q.m[0][0] = orient[1]; + Q.m[0][1] = orient[2]; + Q.m[0][2] = orient[3]; // load Q + Q.m[1][0] = orient[4]; + Q.m[1][1] = orient[5]; + Q.m[1][2] = orient[6]; + // printMessage("Orient %g %g %g %g %g %g\n",orient[1],orient[2],orient[3],orient[4],orient[5],orient[6] ); + /* normalize row 1 */ + double val = Q.m[0][0] * Q.m[0][0] + Q.m[0][1] * Q.m[0][1] + Q.m[0][2] * Q.m[0][2]; + if (val > 0.0l) { + val = 1.0l / sqrt(val); + Q.m[0][0] *= (float)val; + Q.m[0][1] *= (float)val; + Q.m[0][2] *= (float)val; + } else { + Q.m[0][0] = 1.0l; + Q.m[0][1] = 0.0l; + Q.m[0][2] = 0.0l; + } + /* normalize row 2 */ + val = Q.m[1][0] * Q.m[1][0] + Q.m[1][1] * Q.m[1][1] + Q.m[1][2] * Q.m[1][2]; + if (val > 0.0l) { + val = 1.0l / sqrt(val); + Q.m[1][0] *= (float)val; + Q.m[1][1] *= (float)val; + Q.m[1][2] *= (float)val; + } else { + Q.m[1][0] = 0.0l; + Q.m[1][1] = 1.0l; + Q.m[1][2] = 0.0l; + } + /* row 3 is the cross product of rows 1 and 2*/ + Q.m[2][0] = Q.m[0][1] * Q.m[1][2] - Q.m[0][2] * Q.m[1][1]; /* cross */ + Q.m[2][1] = Q.m[0][2] * Q.m[1][0] - Q.m[0][0] * Q.m[1][2]; /* product */ + Q.m[2][2] = Q.m[0][0] * Q.m[1][1] - Q.m[0][1] * Q.m[1][0]; + Q = nifti_mat33_transpose(Q); + if (nifti_mat33_determ(Q) < 0.0) { + Q.m[0][2] = -Q.m[0][2]; + Q.m[1][2] = -Q.m[1][2]; + Q.m[2][2] = -Q.m[2][2]; + } + // next scale matrix + LOAD_MAT33(diagVox, xyzMM[1], 0.0l, 0.0l, 0.0l, xyzMM[2], 0.0l, 0.0l, 0.0l, xyzMM[3]); + Q = nifti_mat33_mul(Q, diagVox); + mat44 Q44; // 4x4 matrix includes translations + LOAD_MAT44(Q44, Q.m[0][0], Q.m[0][1], Q.m[0][2], patientPosition[1], + Q.m[1][0], Q.m[1][1], Q.m[1][2], patientPosition[2], + Q.m[2][0], Q.m[2][1], Q.m[2][2], patientPosition[3]); + return Q44; } #if !defined(USING_R) && !defined(USING_MGH_NIFTI_IO) -mat33 nifti_mat33_inverse( mat33 R ) /* inverse of 3x3 matrix */ +float nifti_mat33_determ(mat33 R) /* determinant of 3x3 matrix */ { - double r11,r12,r13,r21,r22,r23,r31,r32,r33 , deti ; - mat33 Q ; - // INPUT MATRIX: - r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; // [ r11 r12 r13 ] - r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; // [ r21 r22 r23 ] - r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; // [ r31 r32 r33 ] - deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 - +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; - if( deti != 0.0l ) deti = 1.0l / deti ; - Q.m[0][0] = deti*( r22*r33-r32*r23) ; - Q.m[0][1] = deti*(-r12*r33+r32*r13) ; - Q.m[0][2] = deti*( r12*r23-r22*r13) ; - Q.m[1][0] = deti*(-r21*r33+r31*r23) ; - Q.m[1][1] = deti*( r11*r33-r31*r13) ; - Q.m[1][2] = deti*(-r11*r23+r21*r13) ; - Q.m[2][0] = deti*( r21*r32-r31*r22) ; - Q.m[2][1] = deti*(-r11*r32+r31*r12) ; - Q.m[2][2] = deti*( r11*r22-r21*r12) ; - return Q ; -} - -float nifti_mat33_rownorm( mat33 A ) // max row norm of 3x3 matrix + double r11, r12, r13, r21, r22, r23, r31, r32, r33; + /* INPUT MATRIX: */ + r11 = R.m[0][0]; + r12 = R.m[0][1]; + r13 = R.m[0][2]; /* [ r11 r12 r13 ] */ + r21 = R.m[1][0]; + r22 = R.m[1][1]; + r23 = R.m[1][2]; /* [ r21 r22 r23 ] */ + r31 = R.m[2][0]; + r32 = R.m[2][1]; + r33 = R.m[2][2]; /* [ r31 r32 r33 ] */ + return (float)(r11 * r22 * r33 - r11 * r32 * r23 - r21 * r12 * r33 + r21 * r32 * r13 + r31 * r12 * r23 - r31 * r22 * r13); +} + +mat33 nifti_mat33_mul(mat33 A, mat33 B) /* multiply 2 3x3 matrices */ +// see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c { - float r1,r2,r3 ; - r1 = fabs(A.m[0][0])+fabs(A.m[0][1])+fabs(A.m[0][2]) ; - r2 = fabs(A.m[1][0])+fabs(A.m[1][1])+fabs(A.m[1][2]) ; - r3 = fabs(A.m[2][0])+fabs(A.m[2][1])+fabs(A.m[2][2]) ; - if( r1 < r2 ) r1 = r2 ; - if( r1 < r3 ) r1 = r3 ; - return r1 ; + mat33 C; + int i, j; + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + C.m[i][j] = A.m[i][0] * B.m[0][j] + A.m[i][1] * B.m[1][j] + A.m[i][2] * B.m[2][j]; + return C; } +#endif -float nifti_mat33_colnorm( mat33 A ) // max column norm of 3x3 matrix +mat44 nifti_mat44_mul(mat44 A, mat44 B) /* multiply 2 3x3 matrices */ { - float r1,r2,r3 ; - r1 = fabs(A.m[0][0])+fabs(A.m[1][0])+fabs(A.m[2][0]) ; - r2 = fabs(A.m[0][1])+fabs(A.m[1][1])+fabs(A.m[2][1]) ; - r3 = fabs(A.m[0][2])+fabs(A.m[1][2])+fabs(A.m[2][2]) ; - if( r1 < r2 ) r1 = r2 ; - if( r1 < r3 ) r1 = r3 ; - return r1 ; + mat44 C; + int i, j; + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) + C.m[i][j] = A.m[i][0] * B.m[0][j] + A.m[i][1] * B.m[1][j] + A.m[i][2] * B.m[2][j] + A.m[i][3] * B.m[3][j]; + return C; } -mat33 nifti_mat33_polar( mat33 A ) +mat33 nifti_mat33_transpose(mat33 A) /* transpose 3x3 matrix */ +// see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c { - mat33 X , Y , Z ; - float alp,bet,gam,gmi , dif=1.0 ; - int k=0 ; - X = A ; - // force matrix to be nonsingular - gam = nifti_mat33_determ(X) ; - while( gam == 0.0 ){ // perturb matrix - gam = 0.00001 * ( 0.001 + nifti_mat33_rownorm(X) ) ; - X.m[0][0] += gam ; X.m[1][1] += gam ; X.m[2][2] += gam ; - gam = nifti_mat33_determ(X) ; - } - while(1){ - Y = nifti_mat33_inverse(X) ; - if( dif > 0.3 ){ // far from convergence - alp = sqrt( nifti_mat33_rownorm(X) * nifti_mat33_colnorm(X) ) ; - bet = sqrt( nifti_mat33_rownorm(Y) * nifti_mat33_colnorm(Y) ) ; - gam = sqrt( bet / alp ) ; - gmi = 1.0 / gam ; - } else - gam = gmi = 1.0 ; // close to convergence - Z.m[0][0] = 0.5 * ( gam*X.m[0][0] + gmi*Y.m[0][0] ) ; - Z.m[0][1] = 0.5 * ( gam*X.m[0][1] + gmi*Y.m[1][0] ) ; - Z.m[0][2] = 0.5 * ( gam*X.m[0][2] + gmi*Y.m[2][0] ) ; - Z.m[1][0] = 0.5 * ( gam*X.m[1][0] + gmi*Y.m[0][1] ) ; - Z.m[1][1] = 0.5 * ( gam*X.m[1][1] + gmi*Y.m[1][1] ) ; - Z.m[1][2] = 0.5 * ( gam*X.m[1][2] + gmi*Y.m[2][1] ) ; - Z.m[2][0] = 0.5 * ( gam*X.m[2][0] + gmi*Y.m[0][2] ) ; - Z.m[2][1] = 0.5 * ( gam*X.m[2][1] + gmi*Y.m[1][2] ) ; - Z.m[2][2] = 0.5 * ( gam*X.m[2][2] + gmi*Y.m[2][2] ) ; - dif = fabs(Z.m[0][0]-X.m[0][0])+fabs(Z.m[0][1]-X.m[0][1]) - +fabs(Z.m[0][2]-X.m[0][2])+fabs(Z.m[1][0]-X.m[1][0]) - +fabs(Z.m[1][1]-X.m[1][1])+fabs(Z.m[1][2]-X.m[1][2]) - +fabs(Z.m[2][0]-X.m[2][0])+fabs(Z.m[2][1]-X.m[2][1]) - +fabs(Z.m[2][2]-X.m[2][2]) ; - k = k+1 ; - if( k > 100 || dif < 3.e-6 ) break ; // convergence or exhaustion - X = Z ; - } - return Z ; -} - -void nifti_mat44_to_quatern( mat44 R , - float *qb, float *qc, float *qd, - float *qx, float *qy, float *qz, - float *dx, float *dy, float *dz, float *qfac ) -{ - double r11,r12,r13 , r21,r22,r23 , r31,r32,r33 ; - double xd,yd,zd , a,b,c,d ; - mat33 P,Q ; - // offset outputs are read write out of input matrix - ASSIF(qx,R.m[0][3]) ; ASSIF(qy,R.m[1][3]) ; ASSIF(qz,R.m[2][3]) ; - // load 3x3 matrix into local variables */ - r11 = R.m[0][0] ; r12 = R.m[0][1] ; r13 = R.m[0][2] ; - r21 = R.m[1][0] ; r22 = R.m[1][1] ; r23 = R.m[1][2] ; - r31 = R.m[2][0] ; r32 = R.m[2][1] ; r33 = R.m[2][2] ; - // compute lengths of each column; these determine grid spacings - xd = sqrt( r11*r11 + r21*r21 + r31*r31 ) ; - yd = sqrt( r12*r12 + r22*r22 + r32*r32 ) ; - zd = sqrt( r13*r13 + r23*r23 + r33*r33 ) ; - // if a column length is zero, patch the trouble - if( xd == 0.0l ){ r11 = 1.0l ; r21 = r31 = 0.0l ; xd = 1.0l ; } - if( yd == 0.0l ){ r22 = 1.0l ; r12 = r32 = 0.0l ; yd = 1.0l ; } - if( zd == 0.0l ){ r33 = 1.0l ; r13 = r23 = 0.0l ; zd = 1.0l ; } - // assign the output lengths */ - ASSIF(dx,xd) ; ASSIF(dy,yd) ; ASSIF(dz,zd) ; - // normalize the columns */ - r11 /= xd ; r21 /= xd ; r31 /= xd ; - r12 /= yd ; r22 /= yd ; r32 /= yd ; - r13 /= zd ; r23 /= zd ; r33 /= zd ; - /* At this point, the matrix has normal columns, but we have to allow - for the fact that the hideous user may not have given us a matrix - with orthogonal columns. - So, now find the orthogonal matrix closest to the current matrix. - One reason for using the polar decomposition to get this - orthogonal matrix, rather than just directly orthogonalizing - the columns, is so that inputting the inverse matrix to R - will result in the inverse orthogonal matrix at this point. - If we just orthogonalized the columns, this wouldn't necessarily hold. */ - Q.m[0][0] = r11 ; Q.m[0][1] = r12 ; Q.m[0][2] = r13 ; // load Q - Q.m[1][0] = r21 ; Q.m[1][1] = r22 ; Q.m[1][2] = r23 ; - Q.m[2][0] = r31 ; Q.m[2][1] = r32 ; Q.m[2][2] = r33 ; - P = nifti_mat33_polar(Q) ; // P is orthog matrix closest to Q - r11 = P.m[0][0] ; r12 = P.m[0][1] ; r13 = P.m[0][2] ; // unload - r21 = P.m[1][0] ; r22 = P.m[1][1] ; r23 = P.m[1][2] ; - r31 = P.m[2][0] ; r32 = P.m[2][1] ; r33 = P.m[2][2] ; - // [ r11 r12 r13 ] - // at this point, the matrix [ r21 r22 r23 ] is orthogonal - // [ r31 r32 r33 ] - // compute the determinant to determine if it is proper - zd = r11*r22*r33-r11*r32*r23-r21*r12*r33 - +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; // should be -1 or 1 - if( zd > 0 ){ // proper - ASSIF(qfac,1.0) ; - } else { // improper ==> flip 3rd column - ASSIF(qfac,-1.0) ; - r13 = -r13 ; r23 = -r23 ; r33 = -r33 ; - } - // now, compute quaternion parameters - a = r11 + r22 + r33 + 1.0l ; - if( a > 0.5l ){ // simplest case - a = 0.5l * sqrt(a) ; - b = 0.25l * (r32-r23) / a ; - c = 0.25l * (r13-r31) / a ; - d = 0.25l * (r21-r12) / a ; - } else { // trickier case - xd = 1.0 + r11 - (r22+r33) ; // 4*b*b - yd = 1.0 + r22 - (r11+r33) ; // 4*c*c - zd = 1.0 + r33 - (r11+r22) ; // 4*d*d - if( xd > 1.0 ){ - b = 0.5l * sqrt(xd) ; - c = 0.25l* (r12+r21) / b ; - d = 0.25l* (r13+r31) / b ; - a = 0.25l* (r32-r23) / b ; - } else if( yd > 1.0 ){ - c = 0.5l * sqrt(yd) ; - b = 0.25l* (r12+r21) / c ; - d = 0.25l* (r23+r32) / c ; - a = 0.25l* (r13-r31) / c ; - } else { - d = 0.5l * sqrt(zd) ; - b = 0.25l* (r13+r31) / d ; - c = 0.25l* (r23+r32) / d ; - a = 0.25l* (r21-r12) / d ; - } - // if( a < 0.0l ){ b=-b ; c=-c ; d=-d; a=-a; } - if( a < 0.0l ){ b=-b ; c=-c ; d=-d; } //a discarded... - } - ASSIF(qb,b) ; ASSIF(qc,c) ; ASSIF(qd,d) ; - return ; -} - -mat44 nifti_quatern_to_mat44( float qb, float qc, float qd, - float qx, float qy, float qz, - float dx, float dy, float dz, float qfac ) -{ - mat44 R ; - double a,b=qb,c=qc,d=qd , xd,yd,zd ; - - /* last row is always [ 0 0 0 1 ] */ - - R.m[3][0]=R.m[3][1]=R.m[3][2] = 0.0f ; R.m[3][3]= 1.0f ; - - /* compute a parameter from b,c,d */ - - a = 1.0l - (b*b + c*c + d*d) ; - if( a < 1.e-7l ){ /* special case */ - a = 1.0l / sqrt(b*b+c*c+d*d) ; - b *= a ; c *= a ; d *= a ; /* normalize (b,c,d) vector */ - a = 0.0l ; /* a = 0 ==> 180 degree rotation */ - } else{ - a = sqrt(a) ; /* angle = 2*arccos(a) */ - } - - /* load rotation matrix, including scaling factors for voxel sizes */ - - xd = (dx > 0.0) ? dx : 1.0l ; /* make sure are positive */ - yd = (dy > 0.0) ? dy : 1.0l ; - zd = (dz > 0.0) ? dz : 1.0l ; - - if( qfac < 0.0 ) zd = -zd ; /* left handedness? */ - - R.m[0][0] = (float)( (a*a+b*b-c*c-d*d) * xd) ; - R.m[0][1] = 2.0l * (b*c-a*d ) * yd ; - R.m[0][2] = 2.0l * (b*d+a*c ) * zd ; - R.m[1][0] = 2.0l * (b*c+a*d ) * xd ; - R.m[1][1] = (float)( (a*a+c*c-b*b-d*d) * yd) ; - R.m[1][2] = 2.0l * (c*d-a*b ) * zd ; - R.m[2][0] = 2.0l * (b*d-a*c ) * xd ; - R.m[2][1] = 2.0l * (c*d+a*b ) * yd ; - R.m[2][2] = (float)( (a*a+d*d-c*c-b*b) * zd) ; - - /* load offsets */ - - R.m[0][3] = qx ; R.m[1][3] = qy ; R.m[2][3] = qz ; - - return R ; + mat33 B; + int i, j; + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + B.m[i][j] = A.m[j][i]; + return B; } -mat44 nifti_mat44_inverse( mat44 R ) +#if !defined(USING_R) && !defined(USING_MGH_NIFTI_IO) +mat33 nifti_mat33_inverse(mat33 R) /* inverse of 3x3 matrix */ { - double r11,r12,r13,r21,r22,r23,r31,r32,r33,v1,v2,v3 , deti ; - mat44 Q ; - /* INPUT MATRIX IS: */ - r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; // [ r11 r12 r13 v1 ] - r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; // [ r21 r22 r23 v2 ] - r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; // [ r31 r32 r33 v3 ] - v1 = R.m[0][3]; v2 = R.m[1][3]; v3 = R.m[2][3]; // [ 0 0 0 1 ] - deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 - +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; - if( deti != 0.0l ) deti = 1.0l / deti ; - Q.m[0][0] = deti*( r22*r33-r32*r23) ; - Q.m[0][1] = deti*(-r12*r33+r32*r13) ; - Q.m[0][2] = deti*( r12*r23-r22*r13) ; - Q.m[0][3] = deti*(-r12*r23*v3+r12*v2*r33+r22*r13*v3 - -r22*v1*r33-r32*r13*v2+r32*v1*r23) ; - Q.m[1][0] = deti*(-r21*r33+r31*r23) ; - Q.m[1][1] = deti*( r11*r33-r31*r13) ; - Q.m[1][2] = deti*(-r11*r23+r21*r13) ; - Q.m[1][3] = deti*( r11*r23*v3-r11*v2*r33-r21*r13*v3 - +r21*v1*r33+r31*r13*v2-r31*v1*r23) ; - Q.m[2][0] = deti*( r21*r32-r31*r22) ; - Q.m[2][1] = deti*(-r11*r32+r31*r12) ; - Q.m[2][2] = deti*( r11*r22-r21*r12) ; - Q.m[2][3] = deti*(-r11*r22*v3+r11*r32*v2+r21*r12*v3 - -r21*r32*v1-r31*r12*v2+r31*r22*v1) ; - Q.m[3][0] = Q.m[3][1] = Q.m[3][2] = 0.0l ; - Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; // failure flag if deti == 0 - return Q ; + double r11, r12, r13, r21, r22, r23, r31, r32, r33, deti; + mat33 Q; + // INPUT MATRIX: + r11 = R.m[0][0]; + r12 = R.m[0][1]; + r13 = R.m[0][2]; // [ r11 r12 r13 ] + r21 = R.m[1][0]; + r22 = R.m[1][1]; + r23 = R.m[1][2]; // [ r21 r22 r23 ] + r31 = R.m[2][0]; + r32 = R.m[2][1]; + r33 = R.m[2][2]; // [ r31 r32 r33 ] + deti = r11 * r22 * r33 - r11 * r32 * r23 - r21 * r12 * r33 + r21 * r32 * r13 + r31 * r12 * r23 - r31 * r22 * r13; + if (deti != 0.0l) + deti = 1.0l / deti; + Q.m[0][0] = deti * (r22 * r33 - r32 * r23); + Q.m[0][1] = deti * (-r12 * r33 + r32 * r13); + Q.m[0][2] = deti * (r12 * r23 - r22 * r13); + Q.m[1][0] = deti * (-r21 * r33 + r31 * r23); + Q.m[1][1] = deti * (r11 * r33 - r31 * r13); + Q.m[1][2] = deti * (-r11 * r23 + r21 * r13); + Q.m[2][0] = deti * (r21 * r32 - r31 * r22); + Q.m[2][1] = deti * (-r11 * r32 + r31 * r12); + Q.m[2][2] = deti * (r11 * r22 - r21 * r12); + return Q; +} + +float nifti_mat33_rownorm(mat33 A) // max row norm of 3x3 matrix +{ + float r1, r2, r3; + r1 = fabs(A.m[0][0]) + fabs(A.m[0][1]) + fabs(A.m[0][2]); + r2 = fabs(A.m[1][0]) + fabs(A.m[1][1]) + fabs(A.m[1][2]); + r3 = fabs(A.m[2][0]) + fabs(A.m[2][1]) + fabs(A.m[2][2]); + if (r1 < r2) + r1 = r2; + if (r1 < r3) + r1 = r3; + return r1; +} + +float nifti_mat33_colnorm(mat33 A) // max column norm of 3x3 matrix +{ + float r1, r2, r3; + r1 = fabs(A.m[0][0]) + fabs(A.m[1][0]) + fabs(A.m[2][0]); + r2 = fabs(A.m[0][1]) + fabs(A.m[1][1]) + fabs(A.m[2][1]); + r3 = fabs(A.m[0][2]) + fabs(A.m[1][2]) + fabs(A.m[2][2]); + if (r1 < r2) + r1 = r2; + if (r1 < r3) + r1 = r3; + return r1; +} + +mat33 nifti_mat33_polar(mat33 A) { + mat33 X, Y, Z; + float alp, bet, gam, gmi, dif = 1.0; + int k = 0; + X = A; + // force matrix to be nonsingular + gam = nifti_mat33_determ(X); + while (gam == 0.0) { // perturb matrix + gam = 0.00001 * (0.001 + nifti_mat33_rownorm(X)); + X.m[0][0] += gam; + X.m[1][1] += gam; + X.m[2][2] += gam; + gam = nifti_mat33_determ(X); + } + while (1) { + Y = nifti_mat33_inverse(X); + if (dif > 0.3) { // far from convergence + alp = sqrt(nifti_mat33_rownorm(X) * nifti_mat33_colnorm(X)); + bet = sqrt(nifti_mat33_rownorm(Y) * nifti_mat33_colnorm(Y)); + gam = sqrt(bet / alp); + gmi = 1.0 / gam; + } else + gam = gmi = 1.0; // close to convergence + Z.m[0][0] = 0.5 * (gam * X.m[0][0] + gmi * Y.m[0][0]); + Z.m[0][1] = 0.5 * (gam * X.m[0][1] + gmi * Y.m[1][0]); + Z.m[0][2] = 0.5 * (gam * X.m[0][2] + gmi * Y.m[2][0]); + Z.m[1][0] = 0.5 * (gam * X.m[1][0] + gmi * Y.m[0][1]); + Z.m[1][1] = 0.5 * (gam * X.m[1][1] + gmi * Y.m[1][1]); + Z.m[1][2] = 0.5 * (gam * X.m[1][2] + gmi * Y.m[2][1]); + Z.m[2][0] = 0.5 * (gam * X.m[2][0] + gmi * Y.m[0][2]); + Z.m[2][1] = 0.5 * (gam * X.m[2][1] + gmi * Y.m[1][2]); + Z.m[2][2] = 0.5 * (gam * X.m[2][2] + gmi * Y.m[2][2]); + dif = fabs(Z.m[0][0] - X.m[0][0]) + fabs(Z.m[0][1] - X.m[0][1]) + fabs(Z.m[0][2] - X.m[0][2]) + fabs(Z.m[1][0] - X.m[1][0]) + fabs(Z.m[1][1] - X.m[1][1]) + fabs(Z.m[1][2] - X.m[1][2]) + fabs(Z.m[2][0] - X.m[2][0]) + fabs(Z.m[2][1] - X.m[2][1]) + fabs(Z.m[2][2] - X.m[2][2]); + k = k + 1; + if (k > 100 || dif < 3.e-6) + break; // convergence or exhaustion + X = Z; + } + return Z; +} + +void nifti_mat44_to_quatern(mat44 R, + float *qb, float *qc, float *qd, + float *qx, float *qy, float *qz, + float *dx, float *dy, float *dz, float *qfac) { + double r11, r12, r13, r21, r22, r23, r31, r32, r33; + double xd, yd, zd, a, b, c, d; + mat33 P, Q; + // offset outputs are read write out of input matrix + ASSIF(qx, R.m[0][3]); + ASSIF(qy, R.m[1][3]); + ASSIF(qz, R.m[2][3]); + // load 3x3 matrix into local variables */ + r11 = R.m[0][0]; + r12 = R.m[0][1]; + r13 = R.m[0][2]; + r21 = R.m[1][0]; + r22 = R.m[1][1]; + r23 = R.m[1][2]; + r31 = R.m[2][0]; + r32 = R.m[2][1]; + r33 = R.m[2][2]; + // compute lengths of each column; these determine grid spacings + xd = sqrt(r11 * r11 + r21 * r21 + r31 * r31); + yd = sqrt(r12 * r12 + r22 * r22 + r32 * r32); + zd = sqrt(r13 * r13 + r23 * r23 + r33 * r33); + // if a column length is zero, patch the trouble + if (xd == 0.0l) { + r11 = 1.0l; + r21 = r31 = 0.0l; + xd = 1.0l; + } + if (yd == 0.0l) { + r22 = 1.0l; + r12 = r32 = 0.0l; + yd = 1.0l; + } + if (zd == 0.0l) { + r33 = 1.0l; + r13 = r23 = 0.0l; + zd = 1.0l; + } + // assign the output lengths */ + ASSIF(dx, xd); + ASSIF(dy, yd); + ASSIF(dz, zd); + // normalize the columns */ + r11 /= xd; + r21 /= xd; + r31 /= xd; + r12 /= yd; + r22 /= yd; + r32 /= yd; + r13 /= zd; + r23 /= zd; + r33 /= zd; + /* At this point, the matrix has normal columns, but we have to allow + for the fact that the hideous user may not have given us a matrix + with orthogonal columns. + So, now find the orthogonal matrix closest to the current matrix. + One reason for using the polar decomposition to get this + orthogonal matrix, rather than just directly orthogonalizing + the columns, is so that inputting the inverse matrix to R + will result in the inverse orthogonal matrix at this point. + If we just orthogonalized the columns, this wouldn't necessarily hold. */ + Q.m[0][0] = r11; + Q.m[0][1] = r12; + Q.m[0][2] = r13; // load Q + Q.m[1][0] = r21; + Q.m[1][1] = r22; + Q.m[1][2] = r23; + Q.m[2][0] = r31; + Q.m[2][1] = r32; + Q.m[2][2] = r33; + P = nifti_mat33_polar(Q); // P is orthog matrix closest to Q + r11 = P.m[0][0]; + r12 = P.m[0][1]; + r13 = P.m[0][2]; // unload + r21 = P.m[1][0]; + r22 = P.m[1][1]; + r23 = P.m[1][2]; + r31 = P.m[2][0]; + r32 = P.m[2][1]; + r33 = P.m[2][2]; + // [ r11 r12 r13 ] + // at this point, the matrix [ r21 r22 r23 ] is orthogonal + // [ r31 r32 r33 ] + // compute the determinant to determine if it is proper + zd = r11 * r22 * r33 - r11 * r32 * r23 - r21 * r12 * r33 + r21 * r32 * r13 + r31 * r12 * r23 - r31 * r22 * r13; // should be -1 or 1 + if (zd > 0) { // proper + ASSIF(qfac, 1.0); + } else { // improper ==> flip 3rd column + ASSIF(qfac, -1.0); + r13 = -r13; + r23 = -r23; + r33 = -r33; + } + // now, compute quaternion parameters + a = r11 + r22 + r33 + 1.0l; + if (a > 0.5l) { // simplest case + a = 0.5l * sqrt(a); + b = 0.25l * (r32 - r23) / a; + c = 0.25l * (r13 - r31) / a; + d = 0.25l * (r21 - r12) / a; + } else { // trickier case + xd = 1.0 + r11 - (r22 + r33); // 4*b*b + yd = 1.0 + r22 - (r11 + r33); // 4*c*c + zd = 1.0 + r33 - (r11 + r22); // 4*d*d + if (xd > 1.0) { + b = 0.5l * sqrt(xd); + c = 0.25l * (r12 + r21) / b; + d = 0.25l * (r13 + r31) / b; + a = 0.25l * (r32 - r23) / b; + } else if (yd > 1.0) { + c = 0.5l * sqrt(yd); + b = 0.25l * (r12 + r21) / c; + d = 0.25l * (r23 + r32) / c; + a = 0.25l * (r13 - r31) / c; + } else { + d = 0.5l * sqrt(zd); + b = 0.25l * (r13 + r31) / d; + c = 0.25l * (r23 + r32) / d; + a = 0.25l * (r21 - r12) / d; + } + // if( a < 0.0l ){ b=-b ; c=-c ; d=-d; a=-a; } + if (a < 0.0l) { + b = -b; + c = -c; + d = -d; + } // a discarded... + } + ASSIF(qb, b); + ASSIF(qc, c); + ASSIF(qd, d); + return; +} + +mat44 nifti_quatern_to_mat44(float qb, float qc, float qd, + float qx, float qy, float qz, + float dx, float dy, float dz, float qfac) { + mat44 R; + double a, b = qb, c = qc, d = qd, xd, yd, zd; + + /* last row is always [ 0 0 0 1 ] */ + + R.m[3][0] = R.m[3][1] = R.m[3][2] = 0.0f; + R.m[3][3] = 1.0f; + + /* compute a parameter from b,c,d */ + + a = 1.0l - (b * b + c * c + d * d); + if (a < 1.e-7l) { /* special case */ + a = 1.0l / sqrt(b * b + c * c + d * d); + b *= a; + c *= a; + d *= a; /* normalize (b,c,d) vector */ + a = 0.0l; /* a = 0 ==> 180 degree rotation */ + } else { + a = sqrt(a); /* angle = 2*arccos(a) */ + } + + /* load rotation matrix, including scaling factors for voxel sizes */ + + xd = (dx > 0.0) ? dx : 1.0l; /* make sure are positive */ + yd = (dy > 0.0) ? dy : 1.0l; + zd = (dz > 0.0) ? dz : 1.0l; + + if (qfac < 0.0) + zd = -zd; /* left handedness? */ + + R.m[0][0] = (float)((a * a + b * b - c * c - d * d) * xd); + R.m[0][1] = 2.0l * (b * c - a * d) * yd; + R.m[0][2] = 2.0l * (b * d + a * c) * zd; + R.m[1][0] = 2.0l * (b * c + a * d) * xd; + R.m[1][1] = (float)((a * a + c * c - b * b - d * d) * yd); + R.m[1][2] = 2.0l * (c * d - a * b) * zd; + R.m[2][0] = 2.0l * (b * d - a * c) * xd; + R.m[2][1] = 2.0l * (c * d + a * b) * yd; + R.m[2][2] = (float)((a * a + d * d - c * c - b * b) * zd); + + /* load offsets */ + + R.m[0][3] = qx; + R.m[1][3] = qy; + R.m[2][3] = qz; + + return R; +} + +mat44 nifti_mat44_inverse(mat44 R) { + double r11, r12, r13, r21, r22, r23, r31, r32, r33, v1, v2, v3, deti; + mat44 Q; + /* INPUT MATRIX IS: */ + r11 = R.m[0][0]; + r12 = R.m[0][1]; + r13 = R.m[0][2]; // [ r11 r12 r13 v1 ] + r21 = R.m[1][0]; + r22 = R.m[1][1]; + r23 = R.m[1][2]; // [ r21 r22 r23 v2 ] + r31 = R.m[2][0]; + r32 = R.m[2][1]; + r33 = R.m[2][2]; // [ r31 r32 r33 v3 ] + v1 = R.m[0][3]; + v2 = R.m[1][3]; + v3 = R.m[2][3]; // [ 0 0 0 1 ] + deti = r11 * r22 * r33 - r11 * r32 * r23 - r21 * r12 * r33 + r21 * r32 * r13 + r31 * r12 * r23 - r31 * r22 * r13; + if (deti != 0.0l) + deti = 1.0l / deti; + Q.m[0][0] = deti * (r22 * r33 - r32 * r23); + Q.m[0][1] = deti * (-r12 * r33 + r32 * r13); + Q.m[0][2] = deti * (r12 * r23 - r22 * r13); + Q.m[0][3] = deti * (-r12 * r23 * v3 + r12 * v2 * r33 + r22 * r13 * v3 - r22 * v1 * r33 - r32 * r13 * v2 + r32 * v1 * r23); + Q.m[1][0] = deti * (-r21 * r33 + r31 * r23); + Q.m[1][1] = deti * (r11 * r33 - r31 * r13); + Q.m[1][2] = deti * (-r11 * r23 + r21 * r13); + Q.m[1][3] = deti * (r11 * r23 * v3 - r11 * v2 * r33 - r21 * r13 * v3 + r21 * v1 * r33 + r31 * r13 * v2 - r31 * v1 * r23); + Q.m[2][0] = deti * (r21 * r32 - r31 * r22); + Q.m[2][1] = deti * (-r11 * r32 + r31 * r12); + Q.m[2][2] = deti * (r11 * r22 - r21 * r12); + Q.m[2][3] = deti * (-r11 * r22 * v3 + r11 * r32 * v2 + r21 * r12 * v3 - r21 * r32 * v1 - r31 * r12 * v2 + r31 * r22 * v1); + Q.m[3][0] = Q.m[3][1] = Q.m[3][2] = 0.0l; + Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l; // failure flag if deti == 0 + return Q; } #endif // Eigen decomposition for symmetric 3x3 matrices, port of public domain Java Matrix library JAMA. // Connelly Barnes http://barnesc.blogspot.com/2007/02/eigenvectors-of-3x3-symmetric-matrix.html // see also https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf -//begin: Connelly Barnes code +// begin: Connelly Barnes code #include #ifdef MAX #undef MAX #endif -#define MAX(a, b) ((a)>(b)?(a):(b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) #define n 3 static double hypot2(double x, double y) { - return sqrt(x*x+y*y); + return sqrt(x * x + y * y); } // Symmetric Householder reduction to tridiagonal form. static void tred2(double V[n][n], double d[n], double e[n]) { -// This is derived from the Algol procedures tred2 by -// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for -// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding -// Fortran subroutine in EISPACK. - for (int j = 0; j < n; j++) { - d[j] = V[n-1][j]; - } - // Householder reduction to tridiagonal form. - for (int i = n-1; i > 0; i--) { - // Scale to avoid under/overflow. - double scale = 0.0; - double h = 0.0; - for (int k = 0; k < i; k++) { - scale = scale + fabs(d[k]); - } - if (scale == 0.0) { - e[i] = d[i-1]; - for (int j = 0; j < i; j++) { - d[j] = V[i-1][j]; - V[i][j] = 0.0; - V[j][i] = 0.0; - } - } else { - // Generate Householder vector. - for (int k = 0; k < i; k++) { - d[k] /= scale; - h += d[k] * d[k]; - } - double f = d[i-1]; - double g = sqrt(h); - if (f > 0) { - g = -g; - } - e[i] = scale * g; - h = h - f * g; - d[i-1] = f - g; - for (int j = 0; j < i; j++) { - e[j] = 0.0; - } - // Apply similarity transformation to remaining columns. - for (int j = 0; j < i; j++) { - f = d[j]; - V[j][i] = f; - g = e[j] + V[j][j] * f; - for (int k = j+1; k <= i-1; k++) { - g += V[k][j] * d[k]; - e[k] += V[k][j] * f; - } - e[j] = g; - } - f = 0.0; - for (int j = 0; j < i; j++) { - e[j] /= h; - f += e[j] * d[j]; - } - double hh = f / (h + h); - for (int j = 0; j < i; j++) { - e[j] -= hh * d[j]; - } - for (int j = 0; j < i; j++) { - f = d[j]; - g = e[j]; - for (int k = j; k <= i-1; k++) { - V[k][j] -= (f * e[k] + g * d[k]); - } - d[j] = V[i-1][j]; - V[i][j] = 0.0; - } - } - d[i] = h; - } - // Accumulate transformations. - for (int i = 0; i < n-1; i++) { - V[n-1][i] = V[i][i]; - V[i][i] = 1.0; - double h = d[i+1]; - if (h != 0.0) { - for (int k = 0; k <= i; k++) { - d[k] = V[k][i+1] / h; - } - for (int j = 0; j <= i; j++) { - double g = 0.0; - for (int k = 0; k <= i; k++) { - g += V[k][i+1] * V[k][j]; - } - for (int k = 0; k <= i; k++) { - V[k][j] -= g * d[k]; - } - } - } - for (int k = 0; k <= i; k++) { - V[k][i+1] = 0.0; - } - } - for (int j = 0; j < n; j++) { - d[j] = V[n-1][j]; - V[n-1][j] = 0.0; - } - V[n-1][n-1] = 1.0; - e[0] = 0.0; + // This is derived from the Algol procedures tred2 by + // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + for (int j = 0; j < n; j++) { + d[j] = V[n - 1][j]; + } + // Householder reduction to tridiagonal form. + for (int i = n - 1; i > 0; i--) { + // Scale to avoid under/overflow. + double scale = 0.0; + double h = 0.0; + for (int k = 0; k < i; k++) { + scale = scale + fabs(d[k]); + } + if (scale == 0.0) { + e[i] = d[i - 1]; + for (int j = 0; j < i; j++) { + d[j] = V[i - 1][j]; + V[i][j] = 0.0; + V[j][i] = 0.0; + } + } else { + // Generate Householder vector. + for (int k = 0; k < i; k++) { + d[k] /= scale; + h += d[k] * d[k]; + } + double f = d[i - 1]; + double g = sqrt(h); + if (f > 0) { + g = -g; + } + e[i] = scale * g; + h = h - f * g; + d[i - 1] = f - g; + for (int j = 0; j < i; j++) { + e[j] = 0.0; + } + // Apply similarity transformation to remaining columns. + for (int j = 0; j < i; j++) { + f = d[j]; + V[j][i] = f; + g = e[j] + V[j][j] * f; + for (int k = j + 1; k <= i - 1; k++) { + g += V[k][j] * d[k]; + e[k] += V[k][j] * f; + } + e[j] = g; + } + f = 0.0; + for (int j = 0; j < i; j++) { + e[j] /= h; + f += e[j] * d[j]; + } + double hh = f / (h + h); + for (int j = 0; j < i; j++) { + e[j] -= hh * d[j]; + } + for (int j = 0; j < i; j++) { + f = d[j]; + g = e[j]; + for (int k = j; k <= i - 1; k++) { + V[k][j] -= (f * e[k] + g * d[k]); + } + d[j] = V[i - 1][j]; + V[i][j] = 0.0; + } + } + d[i] = h; + } + // Accumulate transformations. + for (int i = 0; i < n - 1; i++) { + V[n - 1][i] = V[i][i]; + V[i][i] = 1.0; + double h = d[i + 1]; + if (h != 0.0) { + for (int k = 0; k <= i; k++) { + d[k] = V[k][i + 1] / h; + } + for (int j = 0; j <= i; j++) { + double g = 0.0; + for (int k = 0; k <= i; k++) { + g += V[k][i + 1] * V[k][j]; + } + for (int k = 0; k <= i; k++) { + V[k][j] -= g * d[k]; + } + } + } + for (int k = 0; k <= i; k++) { + V[k][i + 1] = 0.0; + } + } + for (int j = 0; j < n; j++) { + d[j] = V[n - 1][j]; + V[n - 1][j] = 0.0; + } + V[n - 1][n - 1] = 1.0; + e[0] = 0.0; } // Symmetric tridiagonal QL algorithm. static void tql2(double V[n][n], double d[n], double e[n]) { -// This is derived from the Algol procedures tql2, by -// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for -// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding -// Fortran subroutine in EISPACK. - for (int i = 1; i < n; i++) { - e[i-1] = e[i]; - } - e[n-1] = 0.0; - double f = 0.0; - double tst1 = 0.0; - double eps = pow(2.0,-52.0); - for (int l = 0; l < n; l++) { - // Find small subdiagonal element - tst1 = MAX(tst1,fabs(d[l]) + fabs(e[l])); - int m = l; - while (m < n) { - if (fabs(e[m]) <= eps*tst1) { - break; - } - m++; - } - // If m == l, d[l] is an eigenvalue, - // otherwise, iterate. - if (m > l) { - int iter = 0; - do { - iter = iter + 1; // (Could check iteration count here.) - // Compute implicit shift - double g = d[l]; - double p = (d[l+1] - g) / (2.0 * e[l]); - double r = hypot2(p,1.0); - if (p < 0) { - r = -r; - } - d[l] = e[l] / (p + r); - d[l+1] = e[l] * (p + r); - double dl1 = d[l+1]; - double h = g - d[l]; - for (int i = l+2; i < n; i++) { - d[i] -= h; - } - f = f + h; - // Implicit QL transformation. - p = d[m]; - double c = 1.0; - double c2 = c; - double c3 = c; - double el1 = e[l+1]; - double s = 0.0; - double s2 = 0.0; - for (int i = m-1; i >= l; i--) { - c3 = c2; - c2 = c; - s2 = s; - g = c * e[i]; - h = c * p; - r = hypot2(p,e[i]); - e[i+1] = s * r; - s = e[i] / r; - c = p / r; - p = c * d[i] - s * g; - d[i+1] = h + s * (c * g + s * d[i]); - // Accumulate transformation. - for (int k = 0; k < n; k++) { - h = V[k][i+1]; - V[k][i+1] = s * V[k][i] + c * h; - V[k][i] = c * V[k][i] - s * h; - } - } - p = -s * s2 * c3 * el1 * e[l] / dl1; - e[l] = s * p; - d[l] = c * p; - // Check for convergence. - } while (fabs(e[l]) > eps*tst1); - } - d[l] = d[l] + f; - e[l] = 0.0; - } - // Sort eigenvalues and corresponding vectors. - for (int i = 0; i < n-1; i++) { - int k = i; - double p = d[i]; - for (int j = i+1; j < n; j++) { - if (d[j] < p) { - k = j; - p = d[j]; - } - } - if (k != i) { - d[k] = d[i]; - d[i] = p; - for (int j = 0; j < n; j++) { - p = V[j][i]; - V[j][i] = V[j][k]; - V[j][k] = p; - } - } - } + // This is derived from the Algol procedures tql2, by + // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + for (int i = 1; i < n; i++) { + e[i - 1] = e[i]; + } + e[n - 1] = 0.0; + double f = 0.0; + double tst1 = 0.0; + double eps = pow(2.0, -52.0); + for (int l = 0; l < n; l++) { + // Find small subdiagonal element + tst1 = MAX(tst1, fabs(d[l]) + fabs(e[l])); + int m = l; + while (m < n) { + if (fabs(e[m]) <= eps * tst1) { + break; + } + m++; + } + // If m == l, d[l] is an eigenvalue, + // otherwise, iterate. + if (m > l) { + int iter = 0; + do { + iter = iter + 1; // (Could check iteration count here.) + // Compute implicit shift + double g = d[l]; + double p = (d[l + 1] - g) / (2.0 * e[l]); + double r = hypot2(p, 1.0); + if (p < 0) { + r = -r; + } + d[l] = e[l] / (p + r); + d[l + 1] = e[l] * (p + r); + double dl1 = d[l + 1]; + double h = g - d[l]; + for (int i = l + 2; i < n; i++) { + d[i] -= h; + } + f = f + h; + // Implicit QL transformation. + p = d[m]; + double c = 1.0; + double c2 = c; + double c3 = c; + double el1 = e[l + 1]; + double s = 0.0; + double s2 = 0.0; + for (int i = m - 1; i >= l; i--) { + c3 = c2; + c2 = c; + s2 = s; + g = c * e[i]; + h = c * p; + r = hypot2(p, e[i]); + e[i + 1] = s * r; + s = e[i] / r; + c = p / r; + p = c * d[i] - s * g; + d[i + 1] = h + s * (c * g + s * d[i]); + // Accumulate transformation. + for (int k = 0; k < n; k++) { + h = V[k][i + 1]; + V[k][i + 1] = s * V[k][i] + c * h; + V[k][i] = c * V[k][i] - s * h; + } + } + p = -s * s2 * c3 * el1 * e[l] / dl1; + e[l] = s * p; + d[l] = c * p; + // Check for convergence. + } while (fabs(e[l]) > eps * tst1); + } + d[l] = d[l] + f; + e[l] = 0.0; + } + // Sort eigenvalues and corresponding vectors. + for (int i = 0; i < n - 1; i++) { + int k = i; + double p = d[i]; + for (int j = i + 1; j < n; j++) { + if (d[j] < p) { + k = j; + p = d[j]; + } + } + if (k != i) { + d[k] = d[i]; + d[i] = p; + for (int j = 0; j < n; j++) { + p = V[j][i]; + V[j][i] = V[j][k]; + V[j][k] = p; + } + } + } } void eigen_decomposition(double A[n][n], double V[n][n], double d[n]) { - double e[n]; - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - V[i][j] = A[i][j]; - } - } - tred2(V, d, e); - tql2(V, d, e); + double e[n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + V[i][j] = A[i][j]; + } + } + tred2(V, d, e); + tql2(V, d, e); } -//end: Connelly Barnes code +// end: Connelly Barnes code /*void printMat(char ch, double A[3][3]) { printf("%c=[%g %g %g; %g %g %g; %g %g %g];\n", ch, @@ -819,39 +907,39 @@ void eigen_decomposition(double A[n][n], double V[n][n], double d[n]) { }*/ vec3 nifti_mat33_eig3(double bxx, double bxy, double bxz, double byy, double byz, double bzz) { - double A[3][3]; - A[0][0] = bxx; - A[0][1] = bxy; - A[0][2] = bxz; - A[1][0] = bxy; - A[1][1] = byy; - A[1][2] = byz; - A[2][0] = bxz; - A[2][1] = byz; - A[2][2] = bzz; + double A[3][3]; + A[0][0] = bxx; + A[0][1] = bxy; + A[0][2] = bxz; + A[1][0] = bxy; + A[1][1] = byy; + A[1][2] = byz; + A[2][0] = bxz; + A[2][1] = byz; + A[2][2] = bzz; double V[3][3]; double d[3]; - eigen_decomposition(A,V,d); - //printMat('A',A); - //printf("[V,D] = eig(A) %%where A*V = V*D\n"); - //printMat('V',V); - //printf("D = [%g 0 0; 0 %g 0; 0 0 %g]\n", d[0], d[1], d[2]); - vec3 v3; - v3.v[0] = V[0][2]; - v3.v[1] = V[1][2]; - v3.v[2] = V[2][2]; + eigen_decomposition(A, V, d); + // printMat('A',A); + // printf("[V,D] = eig(A) %%where A*V = V*D\n"); + // printMat('V',V); + // printf("D = [%g 0 0; 0 %g 0; 0 0 %g]\n", d[0], d[1], d[2]); + vec3 v3; + v3.v[0] = V[0][2]; + v3.v[1] = V[1][2]; + v3.v[2] = V[2][2]; if (v3.v[0] < 0.0) { - //B-matrix underspecified to describe B-vector - // Describes direction but not polarity of B-vector - // e.g. We get an eigenvector, but eigenvalue can be + or - - // https://github.com/rordenlab/dcm2niix/issues/265 - // Like B2q We set the vector to have a positive x component by convention. - // https://raw.githubusercontent.com/matthew-brett/nibabel/master/nibabel/nicom/dwiparams.py - // This ensures eddy will see this as a half shell and not attempt to interpret arbitrary signs + // B-matrix underspecified to describe B-vector + // Describes direction but not polarity of B-vector + // e.g. We get an eigenvector, but eigenvalue can be + or - + // https://github.com/rordenlab/dcm2niix/issues/265 + // Like B2q We set the vector to have a positive x component by convention. + // https://raw.githubusercontent.com/matthew-brett/nibabel/master/nibabel/nicom/dwiparams.py + // This ensures eddy will see this as a half shell and not attempt to interpret arbitrary signs v3.v[0] = -v3.v[0]; v3.v[1] = -v3.v[1]; v3.v[2] = -v3.v[2]; } - //printf("bvec = [%g 0 0; 0 %g 0; 0 0 %g]\n", v3.v[0], v3.v[1], v3.v[2]); - return v3; + // printf("bvec = [%g 0 0; 0 %g 0; 0 0 %g]\n", v3.v[0], v3.v[1], v3.v[2]); + return v3; } diff --git a/console/nifti1_io_core.h b/console/nifti1_io_core.h index 487b782f..20b98172 100644 --- a/console/nifti1_io_core.h +++ b/console/nifti1_io_core.h @@ -1,19 +1,19 @@ -//this minimal set of nifti routines is based on nifti1_io without the dependencies (zlib) and a few extra functions -// http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.h -// http://niftilib.sourceforge.net +// this minimal set of nifti routines is based on nifti1_io without the dependencies (zlib) and a few extra functions +// http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.h +// http://niftilib.sourceforge.net #ifndef _NIFTI_IO_CORE_HEADER_ #define _NIFTI_IO_CORE_HEADER_ #ifdef USING_R #define STRICT_R_HEADERS -#include #include "RNifti.h" +#include #else #include "nifti1.h" #include #endif -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif @@ -22,78 +22,79 @@ extern "C" { #include #ifndef USING_R -typedef struct { /** 3x3 matrix struct **/ - float m[3][3] ; -} mat33 ; -typedef struct { /** 4x4 matrix struct **/ - float m[4][4] ; -} mat44 ; +typedef struct { /** 3x3 matrix struct **/ + float m[3][3]; +} mat33; +typedef struct { /** 4x4 matrix struct **/ + float m[4][4]; +} mat44; #endif -typedef struct { /** x4 vector struct **/ - float v[4] ; -} vec4 ; -typedef struct { /** x3 vector struct **/ - float v[3] ; -} vec3 ; -typedef struct { /** x4 vector struct INTEGER**/ - int v[3] ; -} ivec3 ; +typedef struct { /** x4 vector struct **/ + float v[4]; +} vec4; +typedef struct { /** x3 vector struct **/ + float v[3]; +} vec3; +typedef struct { /** x4 vector struct INTEGER**/ + int v[3]; +} ivec3; -#define LOAD_MAT33(AA,a11,a12,a13 ,a21,a22,a23 ,a31,a32,a33) \ -( AA.m[0][0]=a11 , AA.m[0][1]=a12 , AA.m[0][2]=a13 , \ -AA.m[1][0]=a21 , AA.m[1][1]=a22 , AA.m[1][2]=a23 , \ -AA.m[2][0]=a31 , AA.m[2][1]=a32 , AA.m[2][2]=a33 ) +#define LOAD_MAT33(AA, a11, a12, a13, a21, a22, a23, a31, a32, a33) \ + (AA.m[0][0] = a11, AA.m[0][1] = a12, AA.m[0][2] = a13, \ + AA.m[1][0] = a21, AA.m[1][1] = a22, AA.m[1][2] = a23, \ + AA.m[2][0] = a31, AA.m[2][1] = a32, AA.m[2][2] = a33) -#define LOAD_MAT44(AA,a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34) \ -( AA.m[0][0]=a11 , AA.m[0][1]=a12 , AA.m[0][2]=a13 , AA.m[0][3]=a14 , \ -AA.m[1][0]=a21 , AA.m[1][1]=a22 , AA.m[1][2]=a23 , AA.m[1][3]=a24 , \ -AA.m[2][0]=a31 , AA.m[2][1]=a32 , AA.m[2][2]=a33 , AA.m[2][3]=a34 , \ -AA.m[3][0]=AA.m[3][1]=AA.m[3][2]=0.0f , AA.m[3][3]=1.0f ) +#define LOAD_MAT44(AA, a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34) \ + (AA.m[0][0] = a11, AA.m[0][1] = a12, AA.m[0][2] = a13, AA.m[0][3] = a14, \ + AA.m[1][0] = a21, AA.m[1][1] = a22, AA.m[1][2] = a23, AA.m[1][3] = a24, \ + AA.m[2][0] = a31, AA.m[2][1] = a32, AA.m[2][2] = a33, AA.m[2][3] = a34, \ + AA.m[3][0] = AA.m[3][1] = AA.m[3][2] = 0.0f, AA.m[3][3] = 1.0f) -#undef ASSIF // assign v to *p, if possible -#define ASSIF(p,v) if( (p)!=NULL ) *(p) = (v) +#undef ASSIF // assign v to *p, if possible +#define ASSIF(p, v) \ + if ((p) != NULL) \ + *(p) = (v) float dotProduct(vec3 u, vec3 v); -float nifti_mat33_determ( mat33 R ) ; -int isSameFloat (float a, float b) ; -int isSameDouble (double a, double b) ; -bool littleEndianPlatform (); - +float nifti_mat33_determ(mat33 R); +int isSameFloat(float a, float b); +int isSameDouble(double a, double b); +bool littleEndianPlatform(); vec3 nifti_mat33_eig3(double bxx, double bxy, double bxz, double byy, double byz, double bzz); -mat33 nifti_mat33_inverse( mat33 R ); -mat33 nifti_mat33_mul( mat33 A , mat33 B ); -mat33 nifti_mat33_transpose( mat33 A ) ; +mat33 nifti_mat33_inverse(mat33 R); +mat33 nifti_mat33_mul(mat33 A, mat33 B); +mat33 nifti_mat33_transpose(mat33 A); mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4]); -mat44 nifti_mat44_inverse( mat44 R ); -mat44 nifti_mat44_mul( mat44 A , mat44 B ); +mat44 nifti_mat44_inverse(mat44 R); +mat44 nifti_mat44_mul(mat44 A, mat44 B); vec3 crossProduct(vec3 u, vec3 v); -vec3 nifti_vect33_norm (vec3 v); -vec4 nifti_vect44_norm (vec4 v); -vec3 nifti_vect33mat33_mul(vec3 v, mat33 m ); +vec3 nifti_vect33_norm(vec3 v); +vec4 nifti_vect44_norm(vec4 v); +vec3 nifti_vect33mat33_mul(vec3 v, mat33 m); ivec3 setiVec3(int x, int y, int z); vec3 setVec3(float x, float y, float z); vec4 setVec4(float x, float y, float z); #ifndef USING_R #ifndef USING_MGH_NIFTI_IO // This declaration differs from the equivalent function in the current nifti1_io.h, so avoid the clash -void swap_nifti_header ( struct nifti_1_header *h ) ; +void swap_nifti_header(struct nifti_1_header *h); #else -void swap_nifti_header ( struct nifti_1_header *h , int is_nifti ) ; +void swap_nifti_header(struct nifti_1_header *h, int is_nifti); #endif #endif -vec4 nifti_vect44mat44_mul(vec4 v, mat44 m ); -void nifti_swap_2bytes( size_t n , void *ar ); // 2 bytes at a time -void nifti_swap_4bytes( size_t n , void *ar ); // 4 bytes at a time -void nifti_swap_8bytes( size_t n , void *ar ); // 8 bytes at a time -void nifti_mat44_to_quatern( mat44 R , - float *qb, float *qc, float *qd, - float *qx, float *qy, float *qz, - float *dx, float *dy, float *dz, float *qfac ); -mat44 nifti_quatern_to_mat44( float qb, float qc, float qd, - float qx, float qy, float qz, - float dx, float dy, float dz, float qfac ); +vec4 nifti_vect44mat44_mul(vec4 v, mat44 m); +void nifti_swap_2bytes(size_t n, void *ar); // 2 bytes at a time +void nifti_swap_4bytes(size_t n, void *ar); // 4 bytes at a time +void nifti_swap_8bytes(size_t n, void *ar); // 8 bytes at a time +void nifti_mat44_to_quatern(mat44 R, + float *qb, float *qc, float *qd, + float *qx, float *qy, float *qz, + float *dx, float *dy, float *dz, float *qfac); +mat44 nifti_quatern_to_mat44(float qb, float qc, float qd, + float qx, float qy, float qz, + float dx, float dy, float dz, float qfac); -#ifdef __cplusplus +#ifdef __cplusplus } #endif diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 0b4d8d27..9f0d4f4c 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -1,4 +1,4 @@ -//#define MY_DEBUG +// #define MY_DEBUG #if defined(_WIN64) || defined(_WIN32) #define NOMINMAX #include //write to registry @@ -9,8 +9,8 @@ #define chdir _chrdir #include "io.h" #include -//#define snprintf _snprintf -//#define vsnprintf _vsnprintf +// #define snprintf _snprintf +// #define vsnprintf _vsnprintf #define strcasecmp _stricmp #define strncasecmp _strnicmp #ifdef _WIN32 @@ -19,7 +19,7 @@ #else #include #endif -//#include //clock() +// #include //clock() #ifndef USING_R #include "nifti1.h" #endif @@ -63,16 +63,17 @@ ERROR : YOU CAN NOT COMPILE WITH myEnableJasper AND NOT myDisableOpenJPEG OPTIONS SET SIMULTANEOUSLY #endif -unsigned char * imagetoimg(opj_image_t *image) { + unsigned char * + imagetoimg(opj_image_t *image) { int numcmpts = image->numcomps; int sgnd = image->comps[0].sgnd; int width = image->comps[0].w; int height = image->comps[0].h; - int bpp = (image->comps[0].prec + 7) >> 3; //e.g. 12 bits requires 2 bytes + int bpp = (image->comps[0].prec + 7) >> 3; // e.g. 12 bits requires 2 bytes int imgbytes = bpp * width * height * numcmpts; bool isOK = true; if (numcmpts > 1) { - for (int comp = 1; comp < numcmpts; comp++) { //check RGB data + for (int comp = 1; comp < numcmpts; comp++) { // check RGB data if (image->comps[0].w != image->comps[comp].w) isOK = false; if (image->comps[0].h != image->comps[comp].h) @@ -87,12 +88,12 @@ unsigned char * imagetoimg(opj_image_t *image) { isOK = false; } if (numcmpts != 3) - isOK = false; //we only handle Gray and RedGreenBlue, not GrayAlpha or RedGreenBlueAlpha + isOK = false; // we only handle Gray and RedGreenBlue, not GrayAlpha or RedGreenBlueAlpha if (image->comps[0].prec != 8) - isOK = false; //only 8-bit for RGB data + isOK = false; // only 8-bit for RGB data } if ((image->comps[0].prec < 1) || (image->comps[0].prec > 16)) - isOK = false; //currently we only handle 1 and 2 byte data + isOK = false; // currently we only handle 1 and 2 byte data if (!isOK) { printMessage("jpeg decode failure w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts, opj_version()); return NULL; @@ -100,14 +101,14 @@ unsigned char * imagetoimg(opj_image_t *image) { #ifdef MY_DEBUG printMessage("w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts, opj_version()); #endif - //extract the data + // extract the data if ((bpp < 1) || (bpp > 2) || (width < 1) || (height < 1) || (imgbytes < 1)) { printError("Catastrophic decompression error\n"); return NULL; } unsigned char *img = (unsigned char *)malloc(imgbytes); - uint16_t *img16ui = (uint16_t *)img; //unsigned 16-bit - int16_t *img16i = (int16_t *)img; //signed 16-bit + uint16_t *img16ui = (uint16_t *)img; // unsigned 16-bit + int16_t *img16i = (int16_t *)img; // signed 16-bit if (sgnd) bpp = -bpp; if (bpp == -1) { @@ -115,10 +116,10 @@ unsigned char * imagetoimg(opj_image_t *image) { printError("Signed 8-bit DICOM?\n"); return NULL; } - //n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB - int pix = 0; //output pixel + // n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB + int pix = 0; // output pixel for (int cmptno = 0; cmptno < numcmpts; ++cmptno) { - int cpix = 0; //component pixel + int cpix = 0; // component pixel int *v = image->comps[cmptno].data; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { @@ -135,9 +136,9 @@ unsigned char * imagetoimg(opj_image_t *image) { } pix++; cpix++; - } //for x - } //for y - } //for each component + } // for x + } // for y + } // for each component return img; } // imagetoimg() @@ -147,9 +148,9 @@ typedef struct bufinfo { size_t len; } BufInfo; -static void my_stream_free(void *p_user_data) { //do nothing - //BufInfo d = (BufInfo) p_user_data; - //free(d.buf); +static void my_stream_free(void *p_user_data) { // do nothing + // BufInfo d = (BufInfo) p_user_data; + // free(d.buf); } // my_stream_free() static OPJ_UINT32 opj_read_from_buffer(void *p_buffer, OPJ_UINT32 p_nb_bytes, BufInfo *p_file) { @@ -163,7 +164,7 @@ static OPJ_UINT32 opj_read_from_buffer(void *p_buffer, OPJ_UINT32 p_nb_bytes, Bu p_file->cur += l_nb_read; return l_nb_read ? l_nb_read : ((OPJ_UINT32)-1); -} //opj_read_from_buffer() +} // opj_read_from_buffer() static OPJ_UINT32 opj_write_from_buffer(void *p_buffer, OPJ_UINT32 p_nb_bytes, BufInfo *p_file) { memcpy(p_file->cur, p_buffer, p_nb_bytes); @@ -179,18 +180,18 @@ static OPJ_SIZE_T opj_skip_from_buffer(OPJ_SIZE_T p_nb_bytes, BufInfo *p_file) { } p_file->cur = p_file->buf + p_file->len; return (OPJ_SIZE_T)-1; -} //opj_skip_from_buffer() +} // opj_skip_from_buffer() -//fix for https://github.com/neurolabusc/dcm_qa/issues/5 +// fix for https://github.com/neurolabusc/dcm_qa/issues/5 static OPJ_BOOL opj_seek_from_buffer(OPJ_SIZE_T p_nb_bytes, BufInfo *p_file) { -//printf("opj_seek_from_buffer %d + %d -> %d + %d\n", p_file->cur , p_nb_bytes, p_file->buf, p_file->len); + // printf("opj_seek_from_buffer %d + %d -> %d + %d\n", p_file->cur , p_nb_bytes, p_file->buf, p_file->len); if (p_nb_bytes < p_file->len) { p_file->cur = p_file->buf + p_nb_bytes; return OPJ_TRUE; } p_file->cur = p_file->buf + p_file->len; return OPJ_FALSE; -} //opj_seek_from_buffer() +} // opj_seek_from_buffer() opj_stream_t *opj_stream_create_buffer_stream(BufInfo *p_file, OPJ_UINT32 p_size, OPJ_BOOL p_is_read_stream) { opj_stream_t *l_stream; @@ -206,12 +207,12 @@ opj_stream_t *opj_stream_create_buffer_stream(BufInfo *p_file, OPJ_UINT32 p_size opj_stream_set_skip_function(l_stream, (opj_stream_skip_fn)opj_skip_from_buffer); opj_stream_set_seek_function(l_stream, (opj_stream_seek_fn)opj_seek_from_buffer); return l_stream; -} //opj_stream_create_buffer_stream() +} // opj_stream_create_buffer_stream() unsigned char *nii_loadImgCoreOpenJPEG(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, int compressFlag) { - //OpenJPEG library is not well documented and has changed between versions - //Since the JPEG is embedded in a DICOM we need to skip bytes at the start of the file - // In theory we might also want to strip data that exists AFTER the image, see gdcmJPEG2000Codec.c + // OpenJPEG library is not well documented and has changed between versions + // Since the JPEG is embedded in a DICOM we need to skip bytes at the start of the file + // In theory we might also want to strip data that exists AFTER the image, see gdcmJPEG2000Codec.c unsigned char *ret = NULL; opj_dparameters_t params; opj_codec_t *codec; @@ -229,7 +230,7 @@ unsigned char *nii_loadImgCoreOpenJPEG(char *imgname, struct nifti_1_header hdr, if (sz < size) return NULL; OPJ_CODEC_FORMAT format = OPJ_CODEC_JP2; - //DICOM JPEG2k is SUPPOSED to start with codestream, but some vendors include a header + // DICOM JPEG2k is SUPPOSED to start with codestream, but some vendors include a header if (data[0] == 0xFF && data[1] == 0x4F && data[2] == 0xFF && data[3] == 0x51) format = OPJ_CODEC_J2K; opj_set_default_decoder_parameters(¶ms); @@ -247,9 +248,9 @@ unsigned char *nii_loadImgCoreOpenJPEG(char *imgname, struct nifti_1_header hdr, // Read the main header of the codestream and if necessary the JP2 boxes if (!opj_read_header(stream, codec, &jpx)) { printError("OpenJPEG failed to read the header %s (offset %d)\n", imgname, dcm.imageStart); -//comment these next lines to abort: include these to create zero-padded slice +// comment these next lines to abort: include these to create zero-padded slice #ifdef MY_ZEROFILLBROKENJPGS - //fix broken slices https://github.com/scitran-apps/dcm2niix/issues/4 + // fix broken slices https://github.com/scitran-apps/dcm2niix/issues/4 printError("Zero-filled slice created\n"); int imgbytes = (hdr.bitpix / 8) * hdr.dim[1] * hdr.dim[2]; ret = (unsigned char *)calloc(imgbytes, 1); @@ -270,7 +271,7 @@ unsigned char *nii_loadImgCoreOpenJPEG(char *imgname, struct nifti_1_header hdr, opj_destroy_codec(codec); return ret; } -#endif //myDisableOpenJPEG +#endif // myDisableOpenJPEG #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -286,31 +287,31 @@ float deFuzz(float v) { #ifdef MY_DEBUG void reportMat33(char *str, mat33 A) { printMessage("%s = [%g %g %g ; %g %g %g; %g %g %g ]\n", str, - deFuzz(A.m[0][0]), deFuzz(A.m[0][1]), deFuzz(A.m[0][2]), - deFuzz(A.m[1][0]), deFuzz(A.m[1][1]), deFuzz(A.m[1][2]), - deFuzz(A.m[2][0]), deFuzz(A.m[2][1]), deFuzz(A.m[2][2])); + deFuzz(A.m[0][0]), deFuzz(A.m[0][1]), deFuzz(A.m[0][2]), + deFuzz(A.m[1][0]), deFuzz(A.m[1][1]), deFuzz(A.m[1][2]), + deFuzz(A.m[2][0]), deFuzz(A.m[2][1]), deFuzz(A.m[2][2])); } void reportMat44(char *str, mat44 A) { -//example: reportMat44((char*)"out",*R); + // example: reportMat44((char*)"out",*R); printMessage("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n", str, - deFuzz(A.m[0][0]), deFuzz(A.m[0][1]), deFuzz(A.m[0][2]), deFuzz(A.m[0][3]), - deFuzz(A.m[1][0]), deFuzz(A.m[1][1]), deFuzz(A.m[1][2]), deFuzz(A.m[1][3]), - deFuzz(A.m[2][0]), deFuzz(A.m[2][1]), deFuzz(A.m[2][2]), deFuzz(A.m[2][3])); + deFuzz(A.m[0][0]), deFuzz(A.m[0][1]), deFuzz(A.m[0][2]), deFuzz(A.m[0][3]), + deFuzz(A.m[1][0]), deFuzz(A.m[1][1]), deFuzz(A.m[1][2]), deFuzz(A.m[1][3]), + deFuzz(A.m[2][0]), deFuzz(A.m[2][1]), deFuzz(A.m[2][2]), deFuzz(A.m[2][3])); } #endif int verify_slice_dir(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, mat44 *R, int isVerbose) { -//returns slice direction: 1=sag,2=coronal,3=axial, -= flipped + // returns slice direction: 1=sag,2=coronal,3=axial, -= flipped if (h->dim[3] < 2) - return 0; //don't care direction for single slice - int iSL = 1; //find Z-slice direction: row with highest magnitude of 3rd column + return 0; // don't care direction for single slice + int iSL = 1; // find Z-slice direction: row with highest magnitude of 3rd column if ((fabs(R->m[1][2]) >= fabs(R->m[0][2])) && (fabs(R->m[1][2]) >= fabs(R->m[2][2]))) iSL = 2; // if ((fabs(R->m[2][2]) >= fabs(R->m[0][2])) && (fabs(R->m[2][2]) >= fabs(R->m[1][2]))) - iSL = 3; //axial acquisition + iSL = 3; // axial acquisition float pos = NAN; - if (!isnan(d2.patientPosition[iSL])) { //patient position fields exist + if (!isnan(d2.patientPosition[iSL])) { // patient position fields exist pos = d2.patientPosition[iSL]; if (isSameFloat(pos, d.patientPosition[iSL])) pos = NAN; @@ -319,7 +320,7 @@ int verify_slice_dir(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_h printMessage("position determined using lastFile %f\n", pos); #endif } - if (isnan(pos) && (!isnan(d.patientPositionLast[iSL]))) { //patient position fields exist + if (isnan(pos) && (!isnan(d.patientPositionLast[iSL]))) { // patient position fields exist pos = d.patientPositionLast[iSL]; if (isSameFloat(pos, d.patientPosition[iSL])) pos = NAN; @@ -340,18 +341,18 @@ int verify_slice_dir(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_h vec4 pos1v = nifti_vect44mat44_mul(x, *R); float pos1 = pos1v.v[iSL - 1]; //-1 as C indexed from 0 bool flip = false; - if (!isnan(pos)) // we have real SliceLocation for last slice or volume center + if (!isnan(pos)) // we have real SliceLocation for last slice or volume center flip = (pos > R->m[iSL - 1][3]) != (pos1 > R->m[iSL - 1][3]); // same direction?, note C indices from 0 - else { // we do some guess work and warn user + else { // we do some guess work and warn user vec3 readV = setVec3(d.orient[1], d.orient[2], d.orient[3]); vec3 phaseV = setVec3(d.orient[4], d.orient[5], d.orient[6]); - //printMessage("rd %g %g %g\n",readV.v[0],readV.v[1],readV.v[2]); - //printMessage("ph %g %g %g\n",phaseV.v[0],phaseV.v[1],phaseV.v[2]); - vec3 sliceV = crossProduct(readV, phaseV); //order important: this is our hail mary + // printMessage("rd %g %g %g\n",readV.v[0],readV.v[1],readV.v[2]); + // printMessage("ph %g %g %g\n",phaseV.v[0],phaseV.v[1],phaseV.v[2]); + vec3 sliceV = crossProduct(readV, phaseV); // order important: this is our hail mary flip = ((sliceV.v[0] + sliceV.v[1] + sliceV.v[2]) < 0); - //printMessage("verify slice dir %g %g %g\n",sliceV.v[0],sliceV.v[1],sliceV.v[2]); - if (isVerbose) { //1st pass only - if (!d.isDerived) { //do not warn user if image is derived + // printMessage("verify slice dir %g %g %g\n",sliceV.v[0],sliceV.v[1],sliceV.v[2]); + if (isVerbose) { // 1st pass only + if (!d.isDerived) { // do not warn user if image is derived printWarning("Unable to determine slice direction: please check whether slices are flipped\n"); } else { printWarning("Unable to determine slice direction: please check whether slices are flipped (derived image)\n"); @@ -366,15 +367,15 @@ int verify_slice_dir(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_h iSL = -iSL; #ifdef MY_DEBUG printMessage("verify slice dir %d %d %d\n", h->dim[1], h->dim[2], h->dim[3]); - //reportMat44((char*)"Rout",*R); + // reportMat44((char*)"Rout",*R); printMessage("flip = %d\n", flip); printMessage("sliceDir = %d\n", iSL); printMessage(" pos1 = %f\n", pos1); #endif return iSL; -} //verify_slice_dir() +} // verify_slice_dir() -mat44 noNaN(mat44 Q44, bool isVerbose, bool *isBogus) //simplify any headers that have NaN values +mat44 noNaN(mat44 Q44, bool isVerbose, bool *isBogus) // simplify any headers that have NaN values { mat44 ret = Q44; bool isNaN44 = false; @@ -393,11 +394,11 @@ mat44 noNaN(mat44 Q44, bool isVerbose, bool *isBogus) //simplify any headers tha else ret.m[i][j] = 0; ret.m[1][1] = -1; - } //if isNaN detected + } // if isNaN detected return ret; } -#define kYYYYMMDDlen 8 //how many characters to encode year,month,day in "YYYYDDMM" format +#define kYYYYMMDDlen 8 // how many characters to encode year,month,day in "YYYYDDMM" format /*double dicomTimeToSecX(double dicomTime) { //convert HHMMSS to seconds, 135300.024 -> 135259.731 are 0.293 sec apart @@ -456,24 +457,24 @@ void setQSForm(struct nifti_1_header *h, mat44 Q44i, bool isVerbose) { float dumdx, dumdy, dumdz; nifti_mat44_to_quatern(Q44, &h->quatern_b, &h->quatern_c, &h->quatern_d, &h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz, &h->pixdim[0]); h->qform_code = h->sform_code; -} //setQSForm() +} // setQSForm() #ifdef my_unused ivec3 maxCol(mat33 R) { -//return index of maximum column in 3x3 matrix, e.g. [1 0 0; 0 1 0; 0 0 1] -> 1,2,3 + // return index of maximum column in 3x3 matrix, e.g. [1 0 0; 0 1 0; 0 0 1] -> 1,2,3 ivec3 ixyz; mat33 foo; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) foo.m[i][j] = fabs(R.m[i][j]); - //ixyz.v[0] : row with largest value in column 1 + // ixyz.v[0] : row with largest value in column 1 ixyz.v[0] = 1; if ((foo.m[1][0] > foo.m[0][0]) && (foo.m[1][0] >= foo.m[2][0])) - ixyz.v[0] = 2; //2nd column largest column + ixyz.v[0] = 2; // 2nd column largest column else if ((foo.m[2][0] > foo.m[0][0]) && (foo.m[2][0] > foo.m[1][0])) - ixyz.v[0] = 3; //3rd column largest column - //ixyz.v[1] : row with largest value in column 2, but not the same row as ixyz.v[1] + ixyz.v[0] = 3; // 3rd column largest column + // ixyz.v[1] : row with largest value in column 2, but not the same row as ixyz.v[1] if (ixyz.v[0] == 1) { ixyz.v[1] = 2; if (foo.m[2][1] > foo.m[1][1]) @@ -482,18 +483,18 @@ ivec3 maxCol(mat33 R) { ixyz.v[1] = 1; if (foo.m[2][1] > foo.m[0][1]) ixyz.v[1] = 3; - } else { //ixyz.v[0] == 3 + } else { // ixyz.v[0] == 3 ixyz.v[1] = 1; if (foo.m[1][1] > foo.m[0][1]) ixyz.v[1] = 2; } - //ixyz.v[2] : 3rd row, constrained by previous rows - ixyz.v[2] = 6 - ixyz.v[1] - ixyz.v[0]; //sum of 1+2+3 + // ixyz.v[2] : 3rd row, constrained by previous rows + ixyz.v[2] = 6 - ixyz.v[1] - ixyz.v[0]; // sum of 1+2+3 return ixyz; } int sign(float x) { -//returns -1,0,1 depending on if X is less than, equal to or greater than zero + // returns -1,0,1 depending on if X is less than, equal to or greater than zero if (x < 0) return -1; else if (x > 0) @@ -509,57 +510,57 @@ mat44 xform_mat(struct TDICOMdata d) { vec3 sliceV = crossProduct(readV, phaseV); mat33 R; LOAD_MAT33(R, readV.v[0], readV.v[1], readV.v[2], - phaseV.v[0], phaseV.v[1], phaseV.v[2], - sliceV.v[0], sliceV.v[1], sliceV.v[2]); + phaseV.v[0], phaseV.v[1], phaseV.v[2], + sliceV.v[0], sliceV.v[1], sliceV.v[2]); R = nifti_mat33_transpose(R); - //reportMat33((char*)"R",R); + // reportMat33((char*)"R",R); ivec3 ixyz = maxCol(R); - //printMessage("%d %d %d\n", ixyz.v[0], ixyz.v[1], ixyz.v[2]); + // printMessage("%d %d %d\n", ixyz.v[0], ixyz.v[1], ixyz.v[2]); int iSL = ixyz.v[2]; // 1/2/3 for Sag/Cor/Tra slice float cosSL = R.m[iSL - 1][2]; - //printMessage("cosSL\t%g\n", cosSL); - //vec3 pixdim = setVec3(d.xyzMM[1], d.xyzMM[2], d.xyzMM[3]); - //printMessage("%g %g %g\n", pixdim.v[0], pixdim.v[1], pixdim.v[2]); + // printMessage("cosSL\t%g\n", cosSL); + // vec3 pixdim = setVec3(d.xyzMM[1], d.xyzMM[2], d.xyzMM[3]); + // printMessage("%g %g %g\n", pixdim.v[0], pixdim.v[1], pixdim.v[2]); mat33 pixdim; LOAD_MAT33(pixdim, d.xyzMM[1], 0.0, 0.0, - 0.0, d.xyzMM[2], 0.0, - 0.0, 0.0, d.xyzMM[3]); + 0.0, d.xyzMM[2], 0.0, + 0.0, 0.0, d.xyzMM[3]); R = nifti_mat33_mul(R, pixdim); - //reportMat33((char*)"R",R); + // reportMat33((char*)"R",R); mat44 R44; LOAD_MAT44(R44, R.m[0][0], R.m[0][1], R.m[0][2], d.patientPosition[1], - R.m[1][0], R.m[1][1], R.m[1][2], d.patientPosition[2], - R.m[2][0], R.m[2][1], R.m[2][2], d.patientPosition[3]); - //reportMat44((char*)"R",R44); - //rest are former: R = verify_slice_dir(R, s, dim, iSL) + R.m[1][0], R.m[1][1], R.m[1][2], d.patientPosition[2], + R.m[2][0], R.m[2][1], R.m[2][2], d.patientPosition[3]); + // reportMat44((char*)"R",R44); + // rest are former: R = verify_slice_dir(R, s, dim, iSL) if ((d.xyzDim[3] < 2) && (d.CSA.mosaicSlices < 2)) - return R44; //don't care direction for single slice + return R44; // don't care direction for single slice vec3 dim = setVec3(d.xyzDim[1], d.xyzDim[2], d.xyzDim[3]); - if (d.CSA.mosaicSlices > 1) { //Siemens mosaic: use dim(1) since no transpose to img + if (d.CSA.mosaicSlices > 1) { // Siemens mosaic: use dim(1) since no transpose to img float nRowCol = ceil(sqrt((double)d.CSA.mosaicSlices)); dim.v[0] = dim.v[0] / nRowCol; dim.v[1] = dim.v[1] / nRowCol; dim.v[2] = d.CSA.mosaicSlices; vec4 dim4 = setVec4((nRowCol - 1) * dim.v[0] / 2.0f, (nRowCol - 1) * dim.v[1] / 2.0f, 0); vec4 offset = nifti_vect44mat44_mul(dim4, R44); - //printMessage("%g %g %g\n", dim.v[0], dim.v[1], dim.v[2]); - //printMessage("%g %g %g\n", dim4.v[0], dim4.v[1], dim4.v[2]); - //printMessage("%g %g %g %g\n", offset.v[0], offset.v[1], offset.v[2], offset.v[3]); - //printMessage("nRowCol\t%g\n", nRowCol); + // printMessage("%g %g %g\n", dim.v[0], dim.v[1], dim.v[2]); + // printMessage("%g %g %g\n", dim4.v[0], dim4.v[1], dim4.v[2]); + // printMessage("%g %g %g %g\n", offset.v[0], offset.v[1], offset.v[2], offset.v[3]); + // printMessage("nRowCol\t%g\n", nRowCol); R44.m[0][3] = offset.v[0]; R44.m[1][3] = offset.v[1]; R44.m[2][3] = offset.v[2]; - //R44.m[3][3] = offset.v[3]; + // R44.m[3][3] = offset.v[3]; if (sign(d.CSA.sliceNormV[iSL]) != sign(cosSL)) { R44.m[0][2] = -R44.m[0][2]; R44.m[1][2] = -R44.m[1][2]; R44.m[2][2] = -R44.m[2][2]; R44.m[3][2] = -R44.m[3][2]; } - //reportMat44((char*)"iR44",R44); + // reportMat44((char*)"iR44",R44); return R44; } else if (true) { - //SliceNormalVector TO DO + // SliceNormalVector TO DO printMessage("Not completed"); #ifndef USING_R exit(2); @@ -576,7 +577,7 @@ mat44 xform_mat(struct TDICOMdata d) { mat44 set_nii_header(struct TDICOMdata d) { mat44 R = xform_mat(d); - //R(1:2,:) = -R(1:2,:); % dicom LPS to nifti RAS, xform matrix before reorient + // R(1:2,:) = -R(1:2,:); % dicom LPS to nifti RAS, xform matrix before reorient for (int i = 0; i < 2; i++) for (int j = 0; j < 4; j++) R.m[i][j] = -R.m[i][j]; @@ -590,26 +591,26 @@ mat44 set_nii_header(struct TDICOMdata d) { mat44 set_nii_header_x(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int *sliceDir, int isVerbose) { *sliceDir = 0; mat44 Q44 = nifti_dicom2mat(d.orient, d.patientPosition, d.xyzMM); - //Q44 = doQuadruped(Q44); + // Q44 = doQuadruped(Q44); if (d.isSegamiOasis == true) { - //Segami reconstructions appear to disregard DICOM spatial parameters: assume center of volume is isocenter and no table tilt - // Consider sample image with d.orient (0020,0037) = -1 0 0; 0 1 0: this suggests image RAI (L->R, P->A, S->I) but the vendors viewing software suggests LPS - //Perhaps we should ignore 0020,0037 and 0020,0032 as they are hidden in sequence 0054,0022, but in this case no positioning is provided - // http://www.cs.ucl.ac.uk/fileadmin/cmic/Documents/DavidAtkinson/DICOM.pdf - // https://www.slicer.org/wiki/Coordinate_systems - LOAD_MAT44(Q44, -h->pixdim[1], 0, 0, 0, 0, -h->pixdim[2], 0, 0, 0, 0, h->pixdim[3], 0); //X and Y dimensions flipped in NIfTI (RAS) vs DICOM (LPS) + // Segami reconstructions appear to disregard DICOM spatial parameters: assume center of volume is isocenter and no table tilt + // Consider sample image with d.orient (0020,0037) = -1 0 0; 0 1 0: this suggests image RAI (L->R, P->A, S->I) but the vendors viewing software suggests LPS + // Perhaps we should ignore 0020,0037 and 0020,0032 as they are hidden in sequence 0054,0022, but in this case no positioning is provided + // http://www.cs.ucl.ac.uk/fileadmin/cmic/Documents/DavidAtkinson/DICOM.pdf + // https://www.slicer.org/wiki/Coordinate_systems + LOAD_MAT44(Q44, -h->pixdim[1], 0, 0, 0, 0, -h->pixdim[2], 0, 0, 0, 0, h->pixdim[3], 0); // X and Y dimensions flipped in NIfTI (RAS) vs DICOM (LPS) vec4 originVx = setVec4((h->dim[1] + 1.0f) / 2.0f, (h->dim[2] + 1.0f) / 2.0f, (h->dim[3] + 1.0f) / 2.0f); vec4 originMm = nifti_vect44mat44_mul(originVx, Q44); for (int i = 0; i < 3; i++) - Q44.m[i][3] = -originMm.v[i]; //set origin to center voxel + Q44.m[i][3] = -originMm.v[i]; // set origin to center voxel if (isVerbose) { - //printMessage("origin (vx) %g %g %g\n",originVx.v[0],originVx.v[1],originVx.v[2]); - //printMessage("origin (mm) %g %g %g\n",originMm.v[0],originMm.v[1],originMm.v[2]); + // printMessage("origin (vx) %g %g %g\n",originVx.v[0],originVx.v[1],originVx.v[2]); + // printMessage("origin (mm) %g %g %g\n",originMm.v[0],originMm.v[1],originMm.v[2]); printWarning("Segami coordinates defy DICOM convention, please check orientation\n"); } return Q44; } - //next line only for Siemens mosaic: ignore for UIH grid + // next line only for Siemens mosaic: ignore for UIH grid // https://github.com/xiangruili/dicm2nii/commit/47ad9e6d9bc8a999344cbd487d602d420fb1509f if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (d.CSA.mosaicSlices > 1)) { double nRowCol = ceil(sqrt((double)d.CSA.mosaicSlices)); @@ -623,19 +624,19 @@ mat44 set_nii_header_x(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1 Q44.m[c][r] = -Q44.m[c][r]; mat33 Q; LOAD_MAT33(Q, d.orient[1], d.orient[4], d.CSA.sliceNormV[1], - d.orient[2], d.orient[5], d.CSA.sliceNormV[2], - d.orient[3], d.orient[6], d.CSA.sliceNormV[3]); - if (nifti_mat33_determ(Q) < 0) { //Siemens sagittal are R>>L, whereas NIfTI is L>>R, we retain Siemens order on disk so ascending is still ascending, but we need to have the spatial transform reflect this. + d.orient[2], d.orient[5], d.CSA.sliceNormV[2], + d.orient[3], d.orient[6], d.CSA.sliceNormV[3]); + if (nifti_mat33_determ(Q) < 0) { // Siemens sagittal are R>>L, whereas NIfTI is L>>R, we retain Siemens order on disk so ascending is still ascending, but we need to have the spatial transform reflect this. mat44 det; - *sliceDir = kSliceOrientMosaicNegativeDeterminant; //we need to handle DTI vectors accordingly + *sliceDir = kSliceOrientMosaicNegativeDeterminant; // we need to handle DTI vectors accordingly LOAD_MAT44(det, 1.0l, 0.0l, 0.0l, 0.0l, 0.0l, 1.0l, 0.0l, 0.0l, 0.0l, 0.0l, -1.0l, 0.0l); - //patient_to_tal.m[2][3] = 1-d.CSA.MosaicSlices; + // patient_to_tal.m[2][3] = 1-d.CSA.MosaicSlices; Q44 = nifti_mat44_mul(Q44, det); } - } else { //not a mosaic + } else { // not a mosaic *sliceDir = verify_slice_dir(d, d2, h, &Q44, isVerbose); - for (int c = 0; c < 4; c++) // LPS to nifti RAS, xform matrix before reorient - for (int r = 0; r < 2; r++) //swap rows 1 & 2 + for (int c = 0; c < 4; c++) // LPS to nifti RAS, xform matrix before reorient + for (int r = 0; r < 2; r++) // swap rows 1 & 2 Q44.m[r][c] = -Q44.m[r][c]; } #ifdef MY_DEBUG @@ -644,14 +645,14 @@ mat44 set_nii_header_x(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1 return Q44; } -int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int isVerbose) { //fill header s and q form - //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c - //returns sliceDir: 0=unknown,1=sag,2=coro,3=axial,-=reversed slices +int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int isVerbose) { // fill header s and q form + // see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c + // returns sliceDir: 0=unknown,1=sag,2=coro,3=axial,-=reversed slices int sliceDir = 0; if (h->dim[3] < 2) { mat44 Q44 = set_nii_header_x(d, d2, h, &sliceDir, isVerbose); setQSForm(h, Q44, isVerbose); - return sliceDir; //don't care direction for single slice + return sliceDir; // don't care direction for single slice } h->sform_code = NIFTI_XFORM_UNKNOWN; h->qform_code = NIFTI_XFORM_UNKNOWN; @@ -660,7 +661,7 @@ int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1 if (d.orient[i] != 0.0) isOK = true; if (!isOK) { - //we will have to guess, assume axial acquisition saved in standard Siemens style? + // we will have to guess, assume axial acquisition saved in standard Siemens style? d.orient[1] = 1.0f; d.orient[2] = 0.0f; d.orient[3] = 0.0f; @@ -676,14 +677,14 @@ int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1 mat44 Q44 = set_nii_header_x(d, d2, h, &sliceDir, isVerbose); setQSForm(h, Q44, isVerbose); return sliceDir; -} //headerDcm2NiiSForm() +} // headerDcm2NiiSForm() -int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int isVerbose) { //final pass after de-mosaic +int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int isVerbose) { // final pass after de-mosaic char txt[1024] = {""}; if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d.CSA.sliceOrder; if (h->slice_code == NIFTI_SLICE_UNKNOWN) - h->slice_code = d2.CSA.sliceOrder; //sometimes the first slice order is screwed up https://github.com/eauerbach/CMRR-MB/issues/29 + h->slice_code = d2.CSA.sliceOrder; // sometimes the first slice order is screwed up https://github.com/eauerbach/CMRR-MB/issues/29 if (d.modality == kMODALITY_MR) snprintf(txt, 1024, "TE=%.2g;Time=%.3f", d.TE, d.acquisitionTime); else @@ -693,7 +694,7 @@ int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_hea snprintf(dtxt, 1024, ";phase=%d", d.CSA.phaseEncodingDirectionPositive); strcat(txt, dtxt); } - //from dicm2nii 20151117 InPlanePhaseEncodingDirection + // from dicm2nii 20151117 InPlanePhaseEncodingDirection if (d.phaseEncodingRC == 'R') h->dim_info = (3 << 4) + (1 << 2) + 2; if (d.phaseEncodingRC == 'C') @@ -707,39 +708,39 @@ int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_hea // snprintf(h->descrip,80, "%s",txt); memcpy(h->descrip, txt, 79); h->descrip[79] = '\0'; - if ((strlen(d.imageComments) > 0) && (h->aux_file[0] == 0)) //issue691 + if ((strlen(d.imageComments) > 0) && (h->aux_file[0] == 0)) // issue691 snprintf(h->aux_file, 24, "%.23s", d.imageComments); if ((h->aux_file[0] == '\t') && (h->aux_file[1] == 0)) - h->aux_file[0] = 0; //issue691 + h->aux_file[0] = 0; // issue691 return headerDcm2NiiSForm(d, d2, h, isVerbose); -} //headerDcm2Nii2() +} // headerDcm2Nii2() int dcmStrLen(int len, int kMaxLen) { if (len < kMaxLen) return len + 1; else return kMaxLen; -} //dcmStrLen() +} // dcmStrLen() struct TDICOMdata clear_dicom_data() { struct TDICOMdata d; - //d.dti4D = NULL; + // d.dti4D = NULL; d.locationsInAcquisition = 0; - d.locationsInAcquisitionConflict = 0; //for GE discrepancy between tags 0020,1002; 0021,104F; 0054,0081 + d.locationsInAcquisitionConflict = 0; // for GE discrepancy between tags 0020,1002; 0021,104F; 0054,0081 d.modality = kMODALITY_UNKNOWN; d.effectiveEchoSpacingGE = 0; for (int i = 0; i < 4; i++) { d.CSA.dtiV[i] = 0; d.patientPosition[i] = NAN; - //d.patientPosition2nd[i] = NAN; //used to distinguish XYZT vs XYTZ for Philips 4D - d.patientPositionLast[i] = NAN; //used to compute slice direction for Philips 4D + // d.patientPosition2nd[i] = NAN; //used to distinguish XYZT vs XYTZ for Philips 4D + d.patientPositionLast[i] = NAN; // used to compute slice direction for Philips 4D d.stackOffcentre[i] = NAN; d.angulation[i] = 0.0f; d.xyzMM[i] = 1; } for (int i = 0; i < MAX_NUMBER_OF_DIMENSIONS; ++i) d.dimensionIndexValues[i] = 0; - //d.CSA.sliceTiming[0] = -1.0f; //impossible value denotes not known + // d.CSA.sliceTiming[0] = -1.0f; //impossible value denotes not known for (int z = 0; z < kMaxEPI3D; z++) d.CSA.sliceTiming[z] = -1.0; d.CSA.numDti = 0; @@ -774,7 +775,7 @@ struct TDICOMdata clear_dicom_data() { strcpy(d.stationName, ""); strcpy(d.studyDescription, ""); strcpy(d.scanOptions, ""); - //strcpy(d.mrAcquisitionType, ""); + // strcpy(d.mrAcquisitionType, ""); strcpy(d.seriesInstanceUID, ""); strcpy(d.instanceUID, ""); strcpy(d.studyID, ""); @@ -799,7 +800,7 @@ struct TDICOMdata clear_dicom_data() { d.isHasReal = false; d.isHasImaginary = false; d.isHasMagnitude = false; - //d.maxGradDynVol = -1; //PAR/REC only + // d.maxGradDynVol = -1; //PAR/REC only d.sliceOrient = kSliceOrientUnknown; d.dateTime = (double)19770703150928.0; d.acquisitionTime = 0.0f; @@ -830,9 +831,9 @@ struct TDICOMdata clear_dicom_data() { d.waterFatShift = 0.0; d.groupDelay = 0.0; d.postLabelDelay = 0; - d.shimGradientX = -33333;//impossible value for UINT16 - d.shimGradientY = -33333;//impossible value for UINT16 - d.shimGradientZ = -33333;//impossible value for UINT16 + d.shimGradientX = -33333; // impossible value for UINT16 + d.shimGradientY = -33333; // impossible value for UINT16 + d.shimGradientZ = -33333; // impossible value for UINT16 strcpy(d.prescanReuseString, ""); d.decayFactor = 0.0; d.scatterFraction = 0.0; @@ -852,7 +853,7 @@ struct TDICOMdata clear_dicom_data() { d.compressedSensingFactor = 0.0; d.isDeepLearning = false; strcpy(d.deepLearningText, ""); - //d.patientPositionNumPhilips = 0; + // d.patientPositionNumPhilips = 0; d.imageBytes = 0; d.intenScale = 1; d.intenScalePhilips = 0; @@ -872,11 +873,11 @@ struct TDICOMdata clear_dicom_data() { d.acquNum = 0; d.imageNum = 1; d.imageStart = 0; - d.is3DAcq = false; //e.g. MP-RAGE, SPACE, TFE - d.is2DAcq = false; // - d.isDerived = false; //0008,0008 = DERIVED,CSAPARALLEL,POSDISP - d.isSegamiOasis = false; //these images do not store spatial coordinates - d.isBVecWorldCoordinates = false; //bvecs can be in image space (GE) or world coordinates (Siemens) + d.is3DAcq = false; // e.g. MP-RAGE, SPACE, TFE + d.is2DAcq = false; // + d.isDerived = false; // 0008,0008 = DERIVED,CSAPARALLEL,POSDISP + d.isSegamiOasis = false; // these images do not store spatial coordinates + d.isBVecWorldCoordinates = false; // bvecs can be in image space (GE) or world coordinates (Siemens) d.isGrayscaleSoftcopyPresentationState = false; d.isRawDataStorage = false; d.isPartialFourier = false; @@ -884,14 +885,14 @@ struct TDICOMdata clear_dicom_data() { d.isEPI = false; d.isDiffusion = false; d.isVectorFromBMatrix = false; - d.isStackableSeries = false; //combine DCE series https://github.com/rordenlab/dcm2niix/issues/252 - d.isXA10A = false; //https://github.com/rordenlab/dcm2niix/issues/236 + d.isStackableSeries = false; // combine DCE series https://github.com/rordenlab/dcm2niix/issues/252 + d.isXA10A = false; // https://github.com/rordenlab/dcm2niix/issues/236 d.triggerDelayTime = 0.0; d.RWVScale = 0.0; d.RWVIntercept = 0.0; d.isScaleOrTEVaries = false; - d.isScaleVariesEnh = false; //issue363 - d.bitsAllocated = 16; //bits + d.isScaleVariesEnh = false; // issue363 + d.bitsAllocated = 16; // bits d.bitsStored = 0; d.samplesPerPixel = 1; d.pixelPaddingValue = NAN; @@ -899,15 +900,15 @@ struct TDICOMdata clear_dicom_data() { d.isXRay = false; d.isMultiEcho = false; d.numberOfTR = 1; - d.isSigned = false; //default is unsigned! - d.isFloat = false; //default is for integers, not single or double precision - d.isResampled = false; //assume data not resliced to remove gantry tilt problems + d.isSigned = false; // default is unsigned! + d.isFloat = false; // default is for integers, not single or double precision + d.isResampled = false; // assume data not resliced to remove gantry tilt problems d.isLocalizer = false; d.isNonParallelSlices = false; d.isCoilVaries = false; - d.compressionScheme = 0; //none + d.compressionScheme = 0; // none d.isExplicitVR = true; - d.isLittleEndian = true; //DICOM initially always little endian + d.isLittleEndian = true; // DICOM initially always little endian d.converted2NII = 0; d.numberOfDiffusionDirectionGE = -1; d.velocityEncodeScaleGE = 1.0; @@ -926,7 +927,7 @@ struct TDICOMdata clear_dicom_data() { d.numberOfExcitations = -1; d.numberOfArms = -1; d.numberOfPointsPerArm = -1; - d.phaseNumber = - 1; //Philips Multi-Phase ASL + d.phaseNumber = -1; // Philips Multi-Phase ASL d.spoiling = kSPOILING_UNKOWN; d.interp3D = -1; for (int i = 0; i < kMaxOverlay; i++) @@ -945,18 +946,18 @@ struct TDICOMdata clear_dicom_data() { strcpy(d.patientAge, ""); d.CSA.bandwidthPerPixelPhaseEncode = 0.0; d.CSA.mosaicSlices = 0; - d.CSA.tablePos[0] = -1.0; //unset - d.CSA.tablePos[1] = 0.0; // x - d.CSA.tablePos[2] = 0.0; // y - d.CSA.tablePos[3] = 0.0; // z + d.CSA.tablePos[0] = -1.0; // unset + d.CSA.tablePos[1] = 0.0; // x + d.CSA.tablePos[2] = 0.0; // y + d.CSA.tablePos[3] = 0.0; // z d.CSA.sliceNormV[1] = 0.0; d.CSA.sliceNormV[2] = 0.0; - d.CSA.sliceNormV[3] = 1.0; //default Siemens Image Numbering is F>>H https://www.mccauslandcenter.sc.edu/crnl/tools/stc + d.CSA.sliceNormV[3] = 1.0; // default Siemens Image Numbering is F>>H https://www.mccauslandcenter.sc.edu/crnl/tools/stc d.CSA.sliceOrder = NIFTI_SLICE_UNKNOWN; d.CSA.slice_start = 0; d.CSA.slice_end = 0; d.CSA.protocolSliceNumber1 = 0; - d.CSA.phaseEncodingDirectionPositive = -1; //unknown + d.CSA.phaseEncodingDirectionPositive = -1; // unknown d.CSA.isPhaseMap = false; d.CSA.multiBandFactor = 1; d.CSA.SeriesHeader_offset = 0; @@ -966,16 +967,16 @@ struct TDICOMdata clear_dicom_data() { strcpy(d.CSA.bidsEntitySuffix, ""); strcpy(d.CSA.bidsTask, ""); return d; -} //clear_dicom_data() +} // clear_dicom_data() -int isdigitdot(int c) { //returns true if digit or '.' +int isdigitdot(int c) { // returns true if digit or '.' if (c == '.') return 1; return isdigit(c); } void dcmStrDigitsDotOnlyKey(char key, char *lStr) { -//e.g. string "F:2.50" returns 2.50 if key==":" + // e.g. string "F:2.50" returns 2.50 if key==":" size_t len = strlen(lStr); if (len < 1) return; @@ -987,10 +988,10 @@ void dcmStrDigitsDotOnlyKey(char key, char *lStr) { } else if (!isKey) lStr[i] = ' '; } -} //dcmStrDigitsDotOnlyKey() +} // dcmStrDigitsDotOnlyKey() void dcmStrDigitsOnlyKey(char key, char *lStr) { -//e.g. string "p2s3" returns 2 if key=="p" and 3 if key=="s" + // e.g. string "p2s3" returns 2 if key=="p" and 3 if key=="s" size_t len = strlen(lStr); if (len < 1) return; @@ -1002,10 +1003,10 @@ void dcmStrDigitsOnlyKey(char key, char *lStr) { } else if (!isKey) lStr[i] = ' '; } -} //dcmStrDigitsOnlyKey() +} // dcmStrDigitsOnlyKey() void dcmStrDigitsOnly(char *lStr) { -//e.g. change "H11" to " 11" + // e.g. change "H11" to " 11" size_t len = strlen(lStr); if (len < 1) return; @@ -1017,8 +1018,8 @@ void dcmStrDigitsOnly(char *lStr) { // Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ uint32_t mz_crc32X(unsigned char *ptr, size_t buf_len) { static const uint32_t s_crc32[16] = {0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, - 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,0xedb88320, 0xf00f9344, - 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c}; + 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, + 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c}; uint32_t crcu32 = 0; if (!ptr) return crcu32; @@ -1037,11 +1038,11 @@ void dcmStr(int lLength, unsigned char lBuffer[], char *lOut, bool isStrLarge = char *cString = (char *)malloc(sizeof(char) * (lLength + 1)); cString[lLength] = 0; memcpy(cString, (char *)&lBuffer[0], lLength); -//memcpy(cString, test, lLength); -//printMessage("X%dX\n", (unsigned char)d.patientName[1]); +// memcpy(cString, test, lLength); +// printMessage("X%dX\n", (unsigned char)d.patientName[1]); #ifdef ISO8859 for (int i = 0; i < lLength; i++) - //assume specificCharacterSet (0008,0005) is ISO_IR 100 http://en.wikipedia.org/wiki/ISO/IEC_8859-1 + // assume specificCharacterSet (0008,0005) is ISO_IR 100 http://en.wikipedia.org/wiki/ISO/IEC_8859-1 if (cString[i] < 1) { unsigned char c = (unsigned char)cString[i]; if ((c >= 192) && (c <= 198)) @@ -1090,29 +1091,29 @@ void dcmStr(int lLength, unsigned char lBuffer[], char *lOut, bool isStrLarge = cString[i] = 'y'; } #endif - //we no longer sanitize strings, see issue 425 + // we no longer sanitize strings, see issue 425 int len = lLength; if (cString[len - 1] == ' ') len--; - //while ((len > 0) && (cString[len]=='_')) len--; //remove trailing '_' - cString[len] = 0; //null-terminate, strlcpy does this anyway + // while ((len > 0) && (cString[len]=='_')) len--; //remove trailing '_' + cString[len] = 0; // null-terminate, strlcpy does this anyway int maxLen = kDICOMStr; if (isStrLarge) maxLen = kDICOMStrLarge; len = dcmStrLen(len, maxLen); - if (len == maxLen) { //we need space for null-termination + if (len == maxLen) { // we need space for null-termination if (cString[len - 2] == '_') len = len - 2; } memcpy(lOut, cString, len - 1); lOut[len - 1] = 0; free(cString); -} //dcmStr() +} // dcmStr() #ifdef MY_OLD -//this code works on Intel but not some older systems https://github.com/rordenlab/dcm2niix/issues/327 -float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 32-bit float -//http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian +// this code works on Intel but not some older systems https://github.com/rordenlab/dcm2niix/issues/327 +float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) { // read binary 32-bit float + // http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian bool swap = (littleEndian != littleEndianPlatform()); float retVal = 0; if (lByteLength < 4) @@ -1127,12 +1128,12 @@ float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) { // outFloat[1] = inFloat[2]; outFloat[2] = inFloat[1]; outFloat[3] = inFloat[0]; - //printMessage("swapped val = %f\n",swapVal); + // printMessage("swapped val = %f\n",swapVal); return swapVal; -} //dcmFloat() +} // dcmFloat() -double dcmFloatDouble(const size_t lByteLength, const unsigned char lBuffer[], const bool littleEndian) { //read binary 64-bit float -//http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian +double dcmFloatDouble(const size_t lByteLength, const unsigned char lBuffer[], const bool littleEndian) { // read binary 64-bit float + // http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian bool swap = (littleEndian != littleEndianPlatform()); double retVal = 0.0f; if (lByteLength < 8) @@ -1142,7 +1143,7 @@ double dcmFloatDouble(const size_t lByteLength, const unsigned char lBuffer[], c return retVal; char *floatToConvert = (char *)&lBuffer; char *returnFloat = (char *)&retVal; - //swap the bytes into a temporary buffer + // swap the bytes into a temporary buffer returnFloat[0] = floatToConvert[7]; returnFloat[1] = floatToConvert[6]; returnFloat[2] = floatToConvert[5]; @@ -1151,13 +1152,13 @@ double dcmFloatDouble(const size_t lByteLength, const unsigned char lBuffer[], c returnFloat[5] = floatToConvert[2]; returnFloat[6] = floatToConvert[1]; returnFloat[7] = floatToConvert[0]; - //printMessage("swapped val = %f\n",retVal); + // printMessage("swapped val = %f\n",retVal); return retVal; -} //dcmFloatDouble() +} // dcmFloatDouble() #else -float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 32-bit float - //http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian +float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) { // read binary 32-bit float + // http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian if (lByteLength < 4) return 0.0; bool swap = (littleEndian != littleEndianPlatform()); @@ -1167,19 +1168,19 @@ float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) { // uint8_t c[4]; } i, o; memcpy(&i.i, (char *)&lBuffer[0], 4); - //printf("%02x%02x%02x%02x\n",i.c[0], i.c[1], i.c[2], i.c[3]); + // printf("%02x%02x%02x%02x\n",i.c[0], i.c[1], i.c[2], i.c[3]); if (!swap) return i.f; o.c[0] = i.c[3]; o.c[1] = i.c[2]; o.c[2] = i.c[1]; o.c[3] = i.c[0]; - //printf("swp %02x%02x%02x%02x\n",o.c[0], o.c[1], o.c[2], o.c[3]); + // printf("swp %02x%02x%02x%02x\n",o.c[0], o.c[1], o.c[2], o.c[3]); return o.f; -} //dcmFloat() +} // dcmFloat() -double dcmFloatDouble(const size_t lByteLength, const unsigned char lBuffer[], const bool littleEndian) { //read binary 64-bit float - //http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian +double dcmFloatDouble(const size_t lByteLength, const unsigned char lBuffer[], const bool littleEndian) { // read binary 64-bit float + // http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian if (lByteLength < 8) return 0.0; bool swap = (littleEndian != littleEndianPlatform()); @@ -1200,27 +1201,27 @@ double dcmFloatDouble(const size_t lByteLength, const unsigned char lBuffer[], c o.c[6] = i.c[1]; o.c[7] = i.c[0]; return o.d; -} //dcmFloatDouble() +} // dcmFloatDouble() #endif // SS/IS datatype -int16_t dcmIntSS(int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 16 or 32 bit integer +int16_t dcmIntSS(int lByteLength, unsigned char lBuffer[], bool littleEndian) { // read binary 16 or 32 bit integer if (littleEndian) - return (uint16_t)lBuffer[0] | ((uint16_t)lBuffer[1] << 8); //shortint vs word? - return (uint16_t)lBuffer[1] | ((uint16_t)lBuffer[1] << 0); //shortint vs word? -} //dcmInt() + return (uint16_t)lBuffer[0] | ((uint16_t)lBuffer[1] << 8); // shortint vs word? + return (uint16_t)lBuffer[1] | ((uint16_t)lBuffer[1] << 0); // shortint vs word? +} // dcmInt() -//UL/US unsigned integer -int dcmInt(int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 16 or 32 bit integer +// UL/US unsigned integer +int dcmInt(int lByteLength, unsigned char lBuffer[], bool littleEndian) { // read binary 16 or 32 bit integer if (littleEndian) { if (lByteLength <= 3) - return lBuffer[0] | (lBuffer[1] << 8); //shortint vs word? - return lBuffer[0] + (lBuffer[1] << 8) + (lBuffer[2] << 16) + (lBuffer[3] << 24); //shortint vs word? + return lBuffer[0] | (lBuffer[1] << 8); // shortint vs word? + return lBuffer[0] + (lBuffer[1] << 8) + (lBuffer[2] << 16) + (lBuffer[3] << 24); // shortint vs word? } if (lByteLength <= 3) - return lBuffer[1] | (lBuffer[0] << 8); //shortint vs word? - return lBuffer[3] + (lBuffer[2] << 8) + (lBuffer[1] << 16) + (lBuffer[0] << 24); //shortint vs word? -} //dcmInt() + return lBuffer[1] | (lBuffer[0] << 8); // shortint vs word? + return lBuffer[3] + (lBuffer[2] << 8) + (lBuffer[1] << 16) + (lBuffer[0] << 24); // shortint vs word? +} // dcmInt() uint32_t dcmAttributeTag(unsigned char lBuffer[], bool littleEndian) { // read Attribute Tag (AT) value @@ -1228,25 +1229,25 @@ uint32_t dcmAttributeTag(unsigned char lBuffer[], bool littleEndian) { if (littleEndian) return lBuffer[0] + (lBuffer[1] << 8) + (lBuffer[2] << 16) + (lBuffer[3] << 24); return lBuffer[1] + (lBuffer[0] << 8) + (lBuffer[3] << 16) + (lBuffer[2] << 24); -} //dcmInt() +} // dcmInt() -int dcmStrInt(const int lByteLength, const unsigned char lBuffer[]) { //read int stored as a string +int dcmStrInt(const int lByteLength, const unsigned char lBuffer[]) { // read int stored as a string char *cString = (char *)malloc(sizeof(char) * (lByteLength + 1)); cString[lByteLength] = 0; memcpy(cString, (const unsigned char *)(&lBuffer[0]), lByteLength); int ret = atoi(cString); free(cString); return ret; -} //dcmStrInt() +} // dcmStrInt() -int dcmStrManufacturer(const int lByteLength, unsigned char lBuffer[]) { //read float stored as a string +int dcmStrManufacturer(const int lByteLength, unsigned char lBuffer[]) { // read float stored as a string if (lByteLength < 2) return kMANUFACTURER_UNKNOWN; - //#ifdef _MSC_VER + // #ifdef _MSC_VER char *cString = (char *)malloc(sizeof(char) * (lByteLength + 1)); - //#else + // #else // char cString[lByteLength + 1]; - //#endif + // #endif int ret = kMANUFACTURER_UNKNOWN; cString[lByteLength] = 0; memcpy(cString, (char *)&lBuffer[0], lByteLength); @@ -1265,7 +1266,7 @@ int dcmStrManufacturer(const int lByteLength, unsigned char lBuffer[]) { //read if ((toupper(cString[0]) == 'C') && (toupper(cString[1]) == 'A')) ret = kMANUFACTURER_CANON; if ((toupper(cString[0]) == 'C') && (toupper(cString[1]) == 'P')) - ret = kMANUFACTURER_SIEMENS; //CPS is a Siemens venture, issue 568 + ret = kMANUFACTURER_SIEMENS; // CPS is a Siemens venture, issue 568 if ((toupper(cString[0]) == 'U') && (toupper(cString[1]) == 'I')) ret = kMANUFACTURER_UIH; if ((toupper(cString[0]) == 'B') && (toupper(cString[1]) == 'R')) @@ -1274,17 +1275,17 @@ int dcmStrManufacturer(const int lByteLength, unsigned char lBuffer[]) { //read ret = kMANUFACTURER_MRSOLUTIONS; if ((toupper(cString[0]) == 'H') && (toupper(cString[1]) == 'Y')) ret = kMANUFACTURER_HYPERFINE; - //if (ret == kMANUFACTURER_UNKNOWN) //reduce verbosity: single warning for series : Unable to determine manufacturer (0008,0070) + // if (ret == kMANUFACTURER_UNKNOWN) //reduce verbosity: single warning for series : Unable to determine manufacturer (0008,0070) // printWarning("Unknown manufacturer %s\n", cString); - //#ifdef _MSC_VER + // #ifdef _MSC_VER free(cString); - //#endif + // #endif return ret; -} //dcmStrManufacturer +} // dcmStrManufacturer float csaMultiFloat(unsigned char buff[], int nItems, float Floats[], int *ItemsOK) { - //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats] - //if lnItems == 1, returns first item, if lnItems > 1 returns index of final successful conversion + // warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats] + // if lnItems == 1, returns first item, if lnItems > 1 returns index of final successful conversion TCSAitem itemCSA; *ItemsOK = 0; if (nItems < 1) @@ -1299,21 +1300,21 @@ float csaMultiFloat(unsigned char buff[], int nItems, float Floats[], int *Items nifti_swap_4bytes(1, &itemCSA.xx2_Len); if (itemCSA.xx2_Len > 0) { char *cString = (char *)malloc(sizeof(char) * (itemCSA.xx2_Len)); - memcpy(cString, &buff[lPos], itemCSA.xx2_Len); //TPX memcpy(&cString, &buff[lPos], sizeof(cString)); + memcpy(cString, &buff[lPos], itemCSA.xx2_Len); // TPX memcpy(&cString, &buff[lPos], sizeof(cString)); lPos += ((itemCSA.xx2_Len + 3) / 4) * 4; - //printMessage(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString); + // printMessage(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString); Floats[lI] = (float)atof(cString); - *ItemsOK = lI; //some sequences have store empty items + *ItemsOK = lI; // some sequences have store empty items free(cString); } - } //for each item + } // for each item return Floats[1]; -} //csaMultiFloat() +} // csaMultiFloat() int csaICEdims(unsigned char buff[]) { - //determine coil number from CSA header - //Combined images start with a letter: X_1_1_1_1_1_1_1_1_1_1_1_106 - //Coil data starts with coil number: 29_1_1_1_1_1_7_1_1_1_1_1_3000012 + // determine coil number from CSA header + // Combined images start with a letter: X_1_1_1_1_1_1_1_1_1_1_1_106 + // Coil data starts with coil number: 29_1_1_1_1_1_7_1_1_1_1_1_3000012 TCSAitem itemCSA; int lPos = 0; memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA)); @@ -1321,10 +1322,10 @@ int csaICEdims(unsigned char buff[]) { if (itemCSA.xx2_Len > 0) { lPos += sizeof(itemCSA); char *cString = (char *)malloc(sizeof(char) * (itemCSA.xx2_Len)); - memcpy(cString, &buff[lPos], itemCSA.xx2_Len); //TPX memcpy(&cString, &buff[lPos], sizeof(cString)); + memcpy(cString, &buff[lPos], itemCSA.xx2_Len); // TPX memcpy(&cString, &buff[lPos], sizeof(cString)); lPos += ((itemCSA.xx2_Len + 3) / 4) * 4; char c = cString[0]; - if( c >= '0' && c <= '9' ){ + if (c >= '0' && c <= '9') { dcmStrDigitsOnly(cString); char *end; coilNumber = (int)strtol(cString, &end, 10); @@ -1332,10 +1333,10 @@ int csaICEdims(unsigned char buff[]) { free(cString); } return coilNumber; -} //csaICEdims() +} // csaICEdims() bool csaIsPhaseMap(unsigned char buff[], int nItems) { - //returns true if the tag "ImageHistory" has an item named "CC:ComplexAdd" + // returns true if the tag "ImageHistory" has an item named "CC:ComplexAdd" TCSAitem itemCSA; if (nItems < 1) return false; @@ -1348,19 +1349,19 @@ bool csaIsPhaseMap(unsigned char buff[], int nItems) { nifti_swap_4bytes(1, &itemCSA.xx2_Len); if (itemCSA.xx2_Len > 0) { char *cString = (char *)malloc(sizeof(char) * (itemCSA.xx2_Len + 1)); - memcpy(cString, &buff[lPos], sizeof(itemCSA.xx2_Len)); //TPX memcpy(&cString, &buff[lPos], sizeof(cString)); + memcpy(cString, &buff[lPos], sizeof(itemCSA.xx2_Len)); // TPX memcpy(&cString, &buff[lPos], sizeof(cString)); lPos += ((itemCSA.xx2_Len + 3) / 4) * 4; - //printMessage(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString); + // printMessage(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString); if (strcmp(cString, "CC:ComplexAdd") == 0) return true; free(cString); } - } //for each item + } // for each item return false; -} //csaIsPhaseMap() +} // csaIsPhaseMap() void checkSliceTimes(struct TCSAdata *CSA, int itemsOK, int isVerbose, bool is3DAcq) { - if ((is3DAcq) || (itemsOK < 1)) //we expect 3D sequences to be simultaneous + if ((is3DAcq) || (itemsOK < 1)) // we expect 3D sequences to be simultaneous return; if (itemsOK > kMaxEPI3D) { printError("Please increase kMaxEPI3D and recompile\n"); @@ -1371,13 +1372,13 @@ void checkSliceTimes(struct TCSAdata *CSA, int itemsOK, int isVerbose, bool is3D for (int z = 0; z < itemsOK; z++) if (CSA->sliceTiming[z] < minTimeValue) minTimeValue = CSA->sliceTiming[z]; - //CSA can report negative slice times - // https://neurostars.org/t/slice-timing-illegal-values-in-fmriprep/1516/8 - // Nov 1, 2018 wrote: - // If you have an interleaved dataset we can more definitively validate this formula (aka sliceTime(i) - min(sliceTimes())). + // CSA can report negative slice times + // https://neurostars.org/t/slice-timing-illegal-values-in-fmriprep/1516/8 + // Nov 1, 2018 wrote: + // If you have an interleaved dataset we can more definitively validate this formula (aka sliceTime(i) - min(sliceTimes())). if (minTimeValue < 0) { - //printWarning("Adjusting for negative MosaicRefAcqTimes (issue 271).\n"); //if uncommented, overwhelming number of warnings (one per DICOM input), better once per series - CSA->sliceTiming[kMaxEPI3D - 1] = -2.0; //issue 271: flag for unified warning + // printWarning("Adjusting for negative MosaicRefAcqTimes (issue 271).\n"); //if uncommented, overwhelming number of warnings (one per DICOM input), better once per series + CSA->sliceTiming[kMaxEPI3D - 1] = -2.0; // issue 271: flag for unified warning for (int z = 0; z < itemsOK; z++) CSA->sliceTiming[z] = CSA->sliceTiming[z] - minTimeValue; } @@ -1392,7 +1393,7 @@ void checkSliceTimes(struct TCSAdata *CSA, int itemsOK, int isVerbose, bool is3D maxTimeValue = minTimeValue; if (isVerbose > 1) printMessage(" sliceTimes %g\t", CSA->sliceTiming[0]); - for (int z = 1; z < itemsOK; z++) { //find index and value of fastest time + for (int z = 1; z < itemsOK; z++) { // find index and value of fastest time if (isVerbose > 1) printMessage("%g\t", CSA->sliceTiming[z]); if (CSA->sliceTiming[z] == 0) @@ -1416,7 +1417,7 @@ void checkSliceTimes(struct TCSAdata *CSA, int itemsOK, int isVerbose, bool is3D if (isVerbose) printMessage("No variability in slice times (3D EPI?)\n"); } - if (nTimeZero < 2) { //not for multi-band, not 3D + if (nTimeZero < 2) { // not for multi-band, not 3D if (minTimeIndex == 1) CSA->sliceOrder = NIFTI_SLICE_ALT_INC2; // e.g. 3,1,4,2 else if (minTimeIndex == (itemsOK - 2)) @@ -1424,13 +1425,13 @@ void checkSliceTimes(struct TCSAdata *CSA, int itemsOK, int isVerbose, bool is3D else if ((minTimeIndex == 0) && (CSA->sliceTiming[1] < CSA->sliceTiming[2])) CSA->sliceOrder = NIFTI_SLICE_SEQ_INC; // e.g. 1,2,3,4 else if ((minTimeIndex == 0) && (CSA->sliceTiming[1] > CSA->sliceTiming[2])) - CSA->sliceOrder = NIFTI_SLICE_ALT_INC; //e.g. 1,3,2,4 + CSA->sliceOrder = NIFTI_SLICE_ALT_INC; // e.g. 1,3,2,4 else if ((minTimeIndex == (itemsOK - 1)) && (CSA->sliceTiming[itemsOK - 3] > CSA->sliceTiming[itemsOK - 2])) - CSA->sliceOrder = NIFTI_SLICE_SEQ_DEC; //e.g. 4,3,2,1 or 5,4,3,2,1 + CSA->sliceOrder = NIFTI_SLICE_SEQ_DEC; // e.g. 4,3,2,1 or 5,4,3,2,1 else if ((minTimeIndex == (itemsOK - 1)) && (CSA->sliceTiming[itemsOK - 3] < CSA->sliceTiming[itemsOK - 2])) - CSA->sliceOrder = NIFTI_SLICE_ALT_DEC; //e.g. 4,2,3,1 or 3,5,2,4,1 + CSA->sliceOrder = NIFTI_SLICE_ALT_DEC; // e.g. 4,2,3,1 or 3,5,2,4,1 else { - if (!is3DAcq) //we expect 3D sequences to be simultaneous + if (!is3DAcq) // we expect 3D sequences to be simultaneous printWarning("Unable to determine slice order from CSA tag MosaicRefAcqTimes\n"); } } @@ -1439,35 +1440,35 @@ void checkSliceTimes(struct TCSAdata *CSA, int itemsOK, int isVerbose, bool is3D printMessage(" Multiband x%d sequence: setting slice order as UNKNOWN (instead of %d)\n", nTimeZero, CSA->sliceOrder); CSA->sliceOrder = NIFTI_SLICE_UNKNOWN; } -} //checkSliceTimes() +} // checkSliceTimes() int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, int isVerbose, bool is3DAcq) { -//see also http://afni.nimh.nih.gov/pub/dist/src/siemens_dicom_csa.c -//printMessage("%c%c%c%c\n",buff[0],buff[1],buff[2],buff[3]); + // see also http://afni.nimh.nih.gov/pub/dist/src/siemens_dicom_csa.c + // printMessage("%c%c%c%c\n",buff[0],buff[1],buff[2],buff[3]); if (lLength < 36) return EXIT_FAILURE; if ((buff[0] != 'S') || (buff[1] != 'V') || (buff[2] != '1') || (buff[3] != '0')) return EXIT_FAILURE; - int lPos = 8; //skip 8 bytes of data, 'SV10' plus 2 32-bit values unused1 and unused2 + int lPos = 8; // skip 8 bytes of data, 'SV10' plus 2 32-bit values unused1 and unused2 int lnTag = buff[lPos] + (buff[lPos + 1] << 8) + (buff[lPos + 2] << 16) + (buff[lPos + 3] << 24); - if ((lnTag > 128) || (lnTag < 1)){ - printError("%d n_tags CSA Image Header corrupted (0029,1010) see issue 633.\n", lnTag); - return EXIT_FAILURE; - } + if ((lnTag > 128) || (lnTag < 1)) { + printError("%d n_tags CSA Image Header corrupted (0029,1010) see issue 633.\n", lnTag); + return EXIT_FAILURE; + } if (buff[lPos + 4] != 77) return EXIT_FAILURE; - lPos += 8; //skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0 + lPos += 8; // skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0 TCSAtag tagCSA; TCSAitem itemCSA; int itemsOK; float lFloats[7]; for (int lT = 1; lT <= lnTag; lT++) { - memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag + memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); // read tag lPos += sizeof(tagCSA); // Storage order is always little-endian, so byte-swap required values if necessary if (!littleEndianPlatform()) nifti_swap_4bytes(1, &tagCSA.nitems); - if (isVerbose > 1) //extreme verbosity: show every CSA tag + if (isVerbose > 1) // extreme verbosity: show every CSA tag printMessage(" %d CSA of %s %d\n", lPos, tagCSA.name, tagCSA.nitems); if (tagCSA.nitems > 0) { if (strcmp(tagCSA.name, "ImageHistory") == 0) @@ -1476,13 +1477,13 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i CSA->coilNumber = csaICEdims(&buff[lPos]); else if (strcmp(tagCSA.name, "NumberOfImagesInMosaic") == 0) CSA->mosaicSlices = (int)round(csaMultiFloat(&buff[lPos], 1, lFloats, &itemsOK)); - else if (strcmp(tagCSA.name, "B_value") == 0) { + else if (strcmp(tagCSA.name, "B_value") == 0) { CSA->dtiV[0] = csaMultiFloat(&buff[lPos], 1, lFloats, &itemsOK); if (CSA->dtiV[0] < 0.0) { printWarning("(Corrupt) CSA reports negative b-value! %g\n", CSA->dtiV[0]); CSA->dtiV[0] = 0.0; } - CSA->numDti = 1; //triggered by b-value, as B0 images do not have DiffusionGradientDirection tag + CSA->numDti = 1; // triggered by b-value, as B0 images do not have DiffusionGradientDirection tag } else if ((strcmp(tagCSA.name, "DiffusionGradientDirection") == 0) && (tagCSA.nitems > 2)) { CSA->dtiV[1] = csaMultiFloat(&buff[lPos], 3, lFloats, &itemsOK); CSA->dtiV[2] = lFloats[2]; @@ -1524,7 +1525,7 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i nifti_swap_4bytes(1, &itemCSA.xx2_Len); lPos += ((itemCSA.xx2_Len + 3) / 4) * 4; } - } //if at least 1 item + } // if at least 1 item } // for lT 1..lnTag if (CSA->protocolSliceNumber1 > 1) CSA->sliceOrder = NIFTI_SLICE_UNKNOWN; @@ -1532,32 +1533,32 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i } // readCSAImageHeader() void dcmMultiShorts(int lByteLength, unsigned char lBuffer[], int lnShorts, uint16_t *lShorts, bool littleEndian) { -//read array of unsigned shorts US http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html + // read array of unsigned shorts US http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html if ((lnShorts < 1) || (lByteLength != (lnShorts * 2))) return; memcpy(&lShorts[0], (uint16_t *)&lBuffer[0], lByteLength); bool swap = (littleEndian != littleEndianPlatform()); if (swap) nifti_swap_2bytes(lnShorts, &lShorts[0]); -} //dcmMultiShorts() +} // dcmMultiShorts() void dcmMultiLongs(int lByteLength, unsigned char lBuffer[], int lnLongs, uint32_t *lLongs, bool littleEndian) { -//read array of unsigned longs UL http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html + // read array of unsigned longs UL http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html if ((lnLongs < 1) || (lByteLength != (lnLongs * 4))) return; memcpy(&lLongs[0], (uint32_t *)&lBuffer[0], lByteLength); bool swap = (littleEndian != littleEndianPlatform()); if (swap) nifti_swap_4bytes(lnLongs, &lLongs[0]); -} //dcmMultiLongs() +} // dcmMultiLongs() void dcmMultiFloat(int lByteLength, char lBuffer[], int lnFloats, float *lFloats) { -//warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats] + // warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats] if ((lnFloats < 1) || (lByteLength < 1)) return; char *cString = (char *)malloc(sizeof(char) * (lByteLength + 1)); memcpy(cString, (char *)&lBuffer[0], lByteLength); - cString[lByteLength] = 0; //null terminate + cString[lByteLength] = 0; // null terminate char *temp = (char *)malloc(lByteLength + 1); int f = 0, lStart = 0; bool isOK = false; @@ -1566,40 +1567,40 @@ void dcmMultiFloat(int lByteLength, char lBuffer[], int lnFloats, float *lFloats isOK = true; if ((isOK) && ((i == (lByteLength)) || (lBuffer[i] == '/') || (lBuffer[i] == ' ') || (lBuffer[i] == '\\'))) { snprintf(temp, i - lStart + 1, "%s", &cString[lStart]); - //printMessage("dcmMultiFloat %s\n",temp); + // printMessage("dcmMultiFloat %s\n",temp); if (f < lnFloats) { f++; lFloats[f] = (float)atof(temp); isOK = false; - //printMessage("%d == %f\n", f, atof(temp)); - } //if f <= nFloats + // printMessage("%d == %f\n", f, atof(temp)); + } // if f <= nFloats lStart = i + 1; - } //if isOK - } //for i to length + } // if isOK + } // for i to length free(temp); free(cString); -} //dcmMultiFloat() +} // dcmMultiFloat() -double dcmStrDouble(const int lByteLength, const unsigned char lBuffer[]) { //read float stored as a string +double dcmStrDouble(const int lByteLength, const unsigned char lBuffer[]) { // read float stored as a string char *cString = (char *)malloc(sizeof(char) * (lByteLength + 1)); memcpy(cString, (char *)&lBuffer[0], lByteLength); - cString[lByteLength] = 0; //null terminate + cString[lByteLength] = 0; // null terminate double ret = (double)atof(cString); free(cString); return ret; -} //dcmStrDouble() +} // dcmStrDouble() -float dcmStrFloat(const int lByteLength, const unsigned char lBuffer[]) { //read float stored as a string +float dcmStrFloat(const int lByteLength, const unsigned char lBuffer[]) { // read float stored as a string char *cString = (char *)malloc(sizeof(char) * (lByteLength + 1)); memcpy(cString, (char *)&lBuffer[0], lByteLength); - cString[lByteLength] = 0; //null terminate + cString[lByteLength] = 0; // null terminate float ret = (float)atof(cString); free(cString); return ret; -} //dcmStrFloat() +} // dcmStrFloat() int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeSForm) { - memset(h, 0, sizeof(nifti_1_header)); //zero-fill structure so unused items are consistent + memset(h, 0, sizeof(nifti_1_header)); // zero-fill structure so unused items are consistent for (int i = 0; i < 80; i++) h->descrip[i] = 0; for (int i = 0; i < 24; i++) @@ -1611,7 +1612,7 @@ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeS for (int i = 0; i < 16; i++) h->intent_name[i] = 0; if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3)) { - h->intent_code = NIFTI_INTENT_ESTIMATE; //make sure we treat this as RGBRGB...RGB + h->intent_code = NIFTI_INTENT_ESTIMATE; // make sure we treat this as RGBRGB...RGB h->datatype = DT_RGB24; } else if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 1)) h->datatype = DT_UINT8; @@ -1639,13 +1640,13 @@ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeS h->pixdim[i] = 0.0f; h->dim[i] = 0; } - //next items listed as unused in NIfTI format, but zeroed for consistency across runs + // next items listed as unused in NIfTI format, but zeroed for consistency across runs h->extents = 0; h->session_error = kSessionOK; - h->glmin = 0; //unused, but make consistent - h->glmax = 0; //unused, but make consistent - h->regular = 114; //in legacy Analyze this was always 114 - //these are important + h->glmin = 0; // unused, but make consistent + h->glmax = 0; // unused, but make consistent + h->regular = 114; // in legacy Analyze this was always 114 + // these are important h->scl_inter = d.intenIntercept; h->scl_slope = d.intenScale; h->cal_max = 0; @@ -1664,7 +1665,7 @@ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeS h->pixdim[1] = d.xyzMM[1]; h->pixdim[2] = d.xyzMM[2]; h->pixdim[3] = d.xyzMM[3]; - h->pixdim[4] = d.TR / 1000.0; //TR reported in msec, time is in sec + h->pixdim[4] = d.TR / 1000.0; // TR reported in msec, time is in sec h->dim[1] = d.xyzDim[1]; h->dim[2] = d.xyzDim[2]; h->dim[3] = d.xyzDim[3]; @@ -1693,14 +1694,14 @@ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeS h->sform_code = NIFTI_XFORM_UNKNOWN; h->toffset = 0; h->intent_code = NIFTI_INTENT_NONE; - h->dim_info = 0; //Freq, Phase and Slice all unknown + h->dim_info = 0; // Freq, Phase and Slice all unknown h->xyzt_units = NIFTI_UNITS_MM + NIFTI_UNITS_SEC; - h->slice_duration = 0; //avoid +inf/-inf, NaN - h->intent_p1 = 0; //avoid +inf/-inf, NaN - h->intent_p2 = 0; //avoid +inf/-inf, NaN - h->intent_p3 = 0; //avoid +inf/-inf, NaN - h->pixdim[0] = 1; //QFactor should be 1 or -1 - h->sizeof_hdr = 348; //used to signify header does not need to be byte-swapped + h->slice_duration = 0; // avoid +inf/-inf, NaN + h->intent_p1 = 0; // avoid +inf/-inf, NaN + h->intent_p2 = 0; // avoid +inf/-inf, NaN + h->intent_p3 = 0; // avoid +inf/-inf, NaN + h->pixdim[0] = 1; // QFactor should be 1 or -1 + h->sizeof_hdr = 348; // used to signify header does not need to be byte-swapped h->slice_code = d.CSA.sliceOrder; if (isComputeSForm) headerDcm2Nii2(d, d, h, false); @@ -1709,27 +1710,27 @@ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeS bool isFloatDiff(float a, float b) { return (fabs(a - b) > FLT_EPSILON); -} //isFloatDiff() +} // isFloatDiff() mat33 nifti_mat33_reorder_cols(mat33 m, ivec3 v) { -// matlab equivalent ret = m(:, v); where v is 1,2,3 [INDEXED FROM ONE!!!!] + // matlab equivalent ret = m(:, v); where v is 1,2,3 [INDEXED FROM ONE!!!!] mat33 ret; for (int r = 0; r < 3; r++) { for (int c = 0; c < 3; c++) ret.m[r][c] = m.m[r][v.v[c] - 1]; } return ret; -} //nifti_mat33_reorder_cols() +} // nifti_mat33_reorder_cols() void changeExt(char *file_name, const char *ext) { char *p_extension; p_extension = strrchr(file_name, '.'); if (p_extension) strcpy(++p_extension, ext); -} //changeExt() +} // changeExt() void cleanStr(char *lOut) { -//e.g. strings such as image comments with special characters (e.g. "G/6/2009") can disrupt file saves + // e.g. strings such as image comments with special characters (e.g. "G/6/2009") can disrupt file saves size_t lLength = strlen(lOut); if (lLength < 1) return; @@ -1737,7 +1738,7 @@ void cleanStr(char *lOut) { cString[lLength] = 0; memcpy(cString, (char *)&lOut[0], lLength); for (int i = 0; i < (int)lLength; i++) - //assume specificCharacterSet (0008,0005) is ISO_IR 100 http://en.wikipedia.org/wiki/ISO/IEC_8859-1 + // assume specificCharacterSet (0008,0005) is ISO_IR 100 http://en.wikipedia.org/wiki/ISO/IEC_8859-1 if (cString[i] < 1) { unsigned char c = (unsigned char)cString[i]; if ((c >= 192) && (c <= 198)) @@ -1787,32 +1788,32 @@ void cleanStr(char *lOut) { } for (int i = 0; i < (int)lLength; i++) if ((cString[i] < 1) || (cString[i] == ' ') || (cString[i] == ',') || (cString[i] == '/') || (cString[i] == '\\') || (cString[i] == '%') || (cString[i] == '*') || (cString[i] == 9) || (cString[i] == 10) || (cString[i] == 11) || (cString[i] == 13)) - cString[i] = '_'; //issue398 - //if ((cString[i]<1) || (cString[i]==' ') || (cString[i]==',') || (cString[i]=='^') || (cString[i]=='/') || (cString[i]=='\\') || (cString[i]=='%') || (cString[i]=='*') || (cString[i] == 9) || (cString[i] == 10) || (cString[i] == 11) || (cString[i] == 13)) cString[i] = '_'; + cString[i] = '_'; // issue398 + // if ((cString[i]<1) || (cString[i]==' ') || (cString[i]==',') || (cString[i]=='^') || (cString[i]=='/') || (cString[i]=='\\') || (cString[i]=='%') || (cString[i]=='*') || (cString[i] == 9) || (cString[i] == 10) || (cString[i] == 11) || (cString[i] == 13)) cString[i] = '_'; int len = 1; - for (int i = 1; i < (int)lLength; i++) { //remove repeated "_" + for (int i = 1; i < (int)lLength; i++) { // remove repeated "_" if ((cString[i - 1] != '_') || (cString[i] != '_')) { cString[len] = cString[i]; len++; } - } //for each item + } // for each item if (cString[len - 1] == '_') len--; - cString[len] = 0; //null-terminate, strlcpy does this anyway + cString[len] = 0; // null-terminate, strlcpy does this anyway int maxLen = kDICOMStr; len = dcmStrLen(len, maxLen); - if (len == maxLen) { //we need space for null-termination + if (len == maxLen) { // we need space for null-termination if (cString[len - 2] == '_') len = len - 2; } memcpy(lOut, cString, len - 1); lOut[len - 1] = 0; free(cString); -} //cleanStr() +} // cleanStr() int isSameFloatGE(float a, float b) { - //Kludge for bug in 0002,0016="DIGITAL_JACKET", 0008,0070="GE MEDICAL SYSTEMS" DICOM data: Orient field (0020:0037) can vary 0.00604261 == 0.00604273 !!! - //return (a == b); //naive approach does not have any tolerance for rounding errors + // Kludge for bug in 0002,0016="DIGITAL_JACKET", 0008,0070="GE MEDICAL SYSTEMS" DICOM data: Orient field (0020:0037) can vary 0.00604261 == 0.00604273 !!! + // return (a == b); //naive approach does not have any tolerance for rounding errors return (fabs(a - b) <= 0.0001); } @@ -1823,12 +1824,12 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt dti4D->decayFactor[0] = -1; dti4D->frameDuration[0] = -1; dti4D->frameReferenceTime[0] = -1; - //dti4D->contentTime[0] = -1; - //dti4D->fragmentOffset[0] = -1; + // dti4D->contentTime[0] = -1; + // dti4D->fragmentOffset[0] = -1; dti4D->intenScale[0] = 0.0; - strcpy(d.protocolName, ""); //erase dummy with empty - strcpy(d.seriesDescription, ""); //erase dummy with empty - strcpy(d.sequenceName, ""); //erase dummy with empty + strcpy(d.protocolName, ""); // erase dummy with empty + strcpy(d.seriesDescription, ""); // erase dummy with empty + strcpy(d.sequenceName, ""); // erase dummy with empty strcpy(d.scanningSequence, ""); FILE *fp = fopen(parname, "r"); if (fp == NULL) @@ -1841,57 +1842,57 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt #define kImageType 4 #define kSequence 5 #define kIndex 6 -//V3 only identical for columns 1..6 -#define kBitsPerVoxel 7 //V3: not per slice: "Image pixel size [8 or 16 bits]" -#define kXdim 9 //V3: not per slice: "Recon resolution (x, y)" -#define kYdim 10 //V3: not per slice: "Recon resolution (x, y)" - int kRI = 11; //V3: 7 - int kRS = 12; //V3: 8 - int kSS = 13; //V3: 9 - int kAngulationAPs = 16; //V3: 12 - int kAngulationFHs = 17; //V3: 13 - int kAngulationRLs = 18; //V3: 14 - int kPositionAP = 19; //V3: 15 - int kPositionFH = 20; //V3: 16 - int kPositionRL = 21; //V3: 17 -#define kThickmm 22 //V3: not per slice: "Slice thickness [mm]" -#define kGapmm 23 //V3: not per slice: "Slice gap [mm]" - int kSliceOrients = 25; //V3: 19 - int kXmm = 28; //V3: 22 - int kYmm = 29; //V3: 23 - int kTEcho = 30; //V3: 24 - int kDynTime = 31; //V3: 25 - int kTriggerTime = 32; //V3: 26 - int kbval = 33;//V3: 27 -//the following do not exist in V3 +// V3 only identical for columns 1..6 +#define kBitsPerVoxel 7 // V3: not per slice: "Image pixel size [8 or 16 bits]" +#define kXdim 9 // V3: not per slice: "Recon resolution (x, y)" +#define kYdim 10 // V3: not per slice: "Recon resolution (x, y)" + int kRI = 11; // V3: 7 + int kRS = 12; // V3: 8 + int kSS = 13; // V3: 9 + int kAngulationAPs = 16; // V3: 12 + int kAngulationFHs = 17; // V3: 13 + int kAngulationRLs = 18; // V3: 14 + int kPositionAP = 19; // V3: 15 + int kPositionFH = 20; // V3: 16 + int kPositionRL = 21; // V3: 17 +#define kThickmm 22 // V3: not per slice: "Slice thickness [mm]" +#define kGapmm 23 // V3: not per slice: "Slice gap [mm]" + int kSliceOrients = 25; // V3: 19 + int kXmm = 28; // V3: 22 + int kYmm = 29; // V3: 23 + int kTEcho = 30; // V3: 24 + int kDynTime = 31; // V3: 25 + int kTriggerTime = 32; // V3: 26 + int kbval = 33; // V3: 27 +// the following do not exist in V3 #define kInversionDelayMs 40 #define kbvalNumber 41 #define kGradientNumber 42 -//the following do not exist in V40 or earlier +// the following do not exist in V40 or earlier #define kv1 47 #define kv2 45 #define kv3 46 -//the following do not exist in V41 or earlier +// the following do not exist in V41 or earlier #define kASL 48 -#define kMaxImageType 4 //4 observed image types: real, imag, mag, phase (in theory also subsequent calculation such as B1) +#define kMaxImageType 4 // 4 observed image types: real, imag, mag, phase (in theory also subsequent calculation such as B1) printWarning("dcm2niix PAR is not actively supported (hint: use dicm2nii)\n"); if (isReadPhase) printWarning(" Reading phase images from PAR/REC\n"); char buff[LINESZ]; - //next values: PAR V3 only - int v3BitsPerVoxel = 16; //V3: not per slice: "Image pixel size [8 or 16 bits]" - int v3Xdim = 128; //not per slice: "Recon resolution (x, y)" - int v3Ydim = 128; //V3: not per slice: "Recon resolution (x, y)" - float v3Thickmm = 2.0; //V3: not per slice: "Slice thickness [mm]" - float v3Gapmm = 0.0; //V3: not per slice: "Slice gap [mm]" - //from top of header + // next values: PAR V3 only + int v3BitsPerVoxel = 16; // V3: not per slice: "Image pixel size [8 or 16 bits]" + int v3Xdim = 128; // not per slice: "Recon resolution (x, y)" + int v3Ydim = 128; // V3: not per slice: "Recon resolution (x, y)" + float v3Thickmm = 2.0; // V3: not per slice: "Slice thickness [mm]" + float v3Gapmm = 0.0; // V3: not per slice: "Slice gap [mm]" + // from top of header int maxNumberOfDiffusionValues = 1; int maxNumberOfGradientOrients = 1; int maxNumberOfCardiacPhases = 1; int maxNumberOfEchoes = 1; int maxNumberOfDynamics = 1; int maxNumberOfMixes = 1; - int maxNumberOfLabels = 1; //Number of label types <0=no ASL> + int maxNumberOfLabels = 1; // Number of label types <0=no ASL> float maxBValue = 0.0f; float maxDynTime = 0.0f; float minDynTime = 999999.0f; @@ -1909,21 +1910,21 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt bool dynNotAscending = false; int maxSlice2D = 0; int parVers = 0; - int maxSeq = -1; //maximum value of Seq column - int seq1 = -1; //value of Seq volume for first slice + int maxSeq = -1; // maximum value of Seq column + int seq1 = -1; // value of Seq volume for first slice int maxEcho = 1; int maxCardiac = 1; int nCols = 26; - //int diskSlice = 0; - int num3DExpected = 0; //number of 3D volumes in the top part of the header - int num2DExpected = 0; //number of 2D slices described in the top part of the header + // int diskSlice = 0; + int num3DExpected = 0; // number of 3D volumes in the top part of the header + int num2DExpected = 0; // number of 2D slices described in the top part of the header int maxVol = -1; int patientPositionNumPhilips = 0; d.isValid = false; const int kMaxCols = 49; float *cols = (float *)malloc(sizeof(float) * (kMaxCols + 1)); for (int i = 0; i < kMaxCols; i++) - cols[i] = 0.0; //old versions of PAR do not fill all columns - beware of buffer overflow + cols[i] = 0.0; // old versions of PAR do not fill all columns - beware of buffer overflow char *p = fgets(buff, LINESZ, fp); bool isIntenScaleVaries = false; for (int i = 0; i < kMaxDTI4D; i++) { @@ -1935,7 +1936,7 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt while (p) { if (strlen(buff) < 1) continue; - if (buff[0] == '#') { //comment + if (buff[0] == '#') { // comment char Comment[7][50]; sscanf(buff, "# %s %s %s %s %s %s V%s\n", Comment[0], Comment[1], Comment[2], Comment[3], Comment[4], Comment[5], Comment[6]); if ((strcmp(Comment[0], "sl") == 0) && (strcmp(Comment[1], "ec") == 0)) { @@ -1944,19 +1945,19 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt if ((num2DExpected) >= kMaxSlice2D) { printError("Use dicm2nii or increase kMaxSlice2D to be more than %d\n", num2DExpected); printMessage(" slices*grad*bval*cardiac*echo*dynamic*mix*label = %d*%d*%d*%d*%d*%d*%d*%d\n", - d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, - maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels); + d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, + maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels); free(cols); return d; } } if (strcmp(Comment[1], "TRYOUT") == 0) { - //sscanf(buff, "# %s %s %s %s %s %s V%s\n", Comment[0], Comment[1], Comment[2], Comment[3],Comment[4], Comment[5],Comment[6]); - parVers = (int)round(atof(Comment[6]) * 10); //4.2 = 42 etc + // sscanf(buff, "# %s %s %s %s %s %s V%s\n", Comment[0], Comment[1], Comment[2], Comment[3],Comment[4], Comment[5],Comment[6]); + parVers = (int)round(atof(Comment[6]) * 10); // 4.2 = 42 etc if (parVers <= 29) { printMessage("Unsupported old PAR version %0.2f (use dicm2nii)\n", parVers / 10.0); return d; - //nCols = 26; //e.g. PAR 3.0 has 26 relevant columns + // nCols = 26; //e.g. PAR 3.0 has 26 relevant columns } if (parVers < 40) { nCols = 29; // PAR 3.0? @@ -1977,17 +1978,17 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt kTriggerTime = 26; kbval = 27; } else if (parVers < 41) - nCols = kv1; //e.g PAR 4.0 + nCols = kv1; // e.g PAR 4.0 else if (parVers < 42) - nCols = kASL; //e.g. PAR 4.1 - last column is final diffusion b-value + nCols = kASL; // e.g. PAR 4.1 - last column is final diffusion b-value else - nCols = kMaxCols; //e.g. PAR 4.2 + nCols = kMaxCols; // e.g. PAR 4.2 } - //the following do not exist in V3 - p = fgets(buff, LINESZ, fp); //get next line + // the following do not exist in V3 + p = fgets(buff, LINESZ, fp); // get next line continue; - } //process '#' comment - if (buff[0] == '.') { //tag + } // process '#' comment + if (buff[0] == '.') { // tag char Comment[9][50]; for (int i = 0; i < 9; i++) strcpy(Comment[i], ""); @@ -1999,19 +2000,19 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt if ((strcmp(Comment[0], "Recon") == 0) && (strcmp(Comment[1], "resolution") == 0)) { v3Xdim = (int)atoi(Comment[5]); v3Ydim = (int)atoi(Comment[6]); - //printMessage("recon %d,%d\n", v3Xdim,v3Ydim); + // printMessage("recon %d,%d\n", v3Xdim,v3Ydim); } if ((strcmp(Comment[1], "pixel") == 0) && (strcmp(Comment[2], "size") == 0)) { v3BitsPerVoxel = (int)atoi(Comment[8]); - //printMessage("bits %d\n", v3BitsPerVoxel); + // printMessage("bits %d\n", v3BitsPerVoxel); } if ((strcmp(Comment[0], "Slice") == 0) && (strcmp(Comment[1], "gap") == 0)) { v3Gapmm = (float)atof(Comment[4]); - //printMessage("gap %g\n", v3Gapmm); + // printMessage("gap %g\n", v3Gapmm); } if ((strcmp(Comment[0], "Slice") == 0) && (strcmp(Comment[1], "thickness") == 0)) { v3Thickmm = (float)atof(Comment[4]); - //printMessage("thick %g\n", v3Thickmm); + // printMessage("thick %g\n", v3Thickmm); } if ((strcmp(Comment[0], "Repetition") == 0) && (strcmp(Comment[1], "time") == 0)) d.TR = (float)atof(Comment[4]); @@ -2022,7 +2023,7 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt strcat(d.patientName, Comment[6]); strcat(d.patientName, Comment[7]); cleanStr(d.patientName); - //printMessage("%s\n",d.patientName); + // printMessage("%s\n",d.patientName); } if ((strcmp(Comment[0], "Technique") == 0) && (strcmp(Comment[1], ":") == 0)) { strcpy(d.patientID, Comment[2]); @@ -2062,7 +2063,7 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt } if ((strcmp(Comment[0], "Examination") == 0) && (strcmp(Comment[1], "date/time") == 0)) { if ((strlen(Comment[3]) >= 10) && (strlen(Comment[5]) >= 8)) { - //DICOM date format is YYYYMMDD, but PAR stores YYYY.MM.DD 2016.03.25 + // DICOM date format is YYYYMMDD, but PAR stores YYYY.MM.DD 2016.03.25 d.studyDate[0] = Comment[3][0]; d.studyDate[1] = Comment[3][1]; d.studyDate[2] = Comment[3][2]; @@ -2072,7 +2073,7 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt d.studyDate[6] = Comment[3][8]; d.studyDate[7] = Comment[3][9]; d.studyDate[8] = '\0'; - //DICOM time format is HHMMSS.FFFFFF, but PAR stores HH:MM:SS, e.g. 18:00:42 or 09:34:16 + // DICOM time format is HHMMSS.FFFFFF, but PAR stores HH:MM:SS, e.g. 18:00:42 or 09:34:16 d.studyTime[0] = Comment[5][0]; d.studyTime[1] = Comment[5][1]; d.studyTime[2] = Comment[5][3]; @@ -2084,13 +2085,13 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt } } if ((strcmp(Comment[0], "Off") == 0) && (strcmp(Comment[1], "Centre") == 0)) { - //Off Centre midslice(ap,fh,rl) [mm] + // Off Centre midslice(ap,fh,rl) [mm] d.stackOffcentre[2] = (float)atof(Comment[5]); d.stackOffcentre[3] = (float)atof(Comment[6]); d.stackOffcentre[1] = (float)atof(Comment[7]); } if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "position") == 0)) { - //Off Centre midslice(ap,fh,rl) [mm] + // Off Centre midslice(ap,fh,rl) [mm] d.patientOrient[0] = toupper(Comment[3][0]); d.patientOrient[1] = toupper(Comment[4][0]); d.patientOrient[2] = toupper(Comment[5][0]); @@ -2101,11 +2102,11 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt } if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "diffusion") == 0)) { maxNumberOfDiffusionValues = atoi(Comment[6]); - //if (maxNumberOfDiffusionValues > 1) maxNumberOfDiffusionValues -= 1; //if two listed, one is B=0 + // if (maxNumberOfDiffusionValues > 1) maxNumberOfDiffusionValues -= 1; //if two listed, one is B=0 } if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "gradient") == 0)) { maxNumberOfGradientOrients = atoi(Comment[6]); - //Warning ISOTROPIC scans may be stored that are not reported here! 32 directions plus isotropic = 33 volumes + // Warning ISOTROPIC scans may be stored that are not reported here! 32 directions plus isotropic = 33 volumes } if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "cardiac") == 0)) { maxNumberOfCardiacPhases = atoi(Comment[6]); @@ -2128,11 +2129,11 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt if (maxNumberOfLabels < 1) maxNumberOfLabels = 1; } - p = fgets(buff, LINESZ, fp); //get next line + p = fgets(buff, LINESZ, fp); // get next line continue; - } //process '.' tag - if (strlen(buff) < 24) { //empty line - p = fgets(buff, LINESZ, fp); //get next line + } // process '.' tag + if (strlen(buff) < 24) { // empty line + p = fgets(buff, LINESZ, fp); // get next line continue; } if (parVers < 20) { @@ -2142,12 +2143,12 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt } for (int i = 0; i <= nCols; i++) cols[i] = strtof(p, &p); // p+1 skip comma, read a float - //printMessage("xDim %dv%d yDim %dv%d bits %dv%d\n", d.xyzDim[1],(int)cols[kXdim], d.xyzDim[2], (int)cols[kYdim], d.bitsAllocated, (int)cols[kBitsPerVoxel]); - if ((int)cols[kSlice] == 0) { //line does not contain attributes - p = fgets(buff, LINESZ, fp); //get next line + // printMessage("xDim %dv%d yDim %dv%d bits %dv%d\n", d.xyzDim[1],(int)cols[kXdim], d.xyzDim[2], (int)cols[kYdim], d.bitsAllocated, (int)cols[kBitsPerVoxel]); + if ((int)cols[kSlice] == 0) { // line does not contain attributes + p = fgets(buff, LINESZ, fp); // get next line continue; } - //diskSlice ++; + // diskSlice ++; bool isADC = false; if ((maxNumberOfGradientOrients >= 2) && (cols[kbval] > 50) && isSameFloat(0.0, cols[kv1]) && isSameFloat(0.0, cols[kv2]) && isSameFloat(0.0, cols[kv3])) { isADC = true; @@ -2156,8 +2157,8 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt if (numSlice2D < 1) { d.xyzMM[1] = cols[kXmm]; d.xyzMM[2] = cols[kYmm]; - if (parVers < 40) { //v3 does things differently - //cccc + if (parVers < 40) { // v3 does things differently + // cccc d.xyzDim[1] = v3Xdim; d.xyzDim[2] = v3Ydim; d.xyzMM[3] = v3Thickmm + v3Gapmm; @@ -2195,8 +2196,8 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt isIntenScaleVaries = true; } if (isSameFloat(cols[kImageType], 18)) { - //printWarning("Field map in Hz will be saved as the 'real' image.\n"); - //isTypeWarning = true; + // printWarning("Field map in Hz will be saved as the 'real' image.\n"); + // isTypeWarning = true; d.isRealIsPhaseMapHz = true; } else if (((cols[kImageType] < 0.0) || (cols[kImageType] > 4.0)) && (!isTypeWarning)) { printError("Unknown type %g: not magnitude[0], real[1], imaginary[2] or phase[3].\n", cols[kImageType]); @@ -2225,7 +2226,7 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt } patientPositionNumPhilips++; } - if (true) { //for every slice + if (true) { // for every slice int slice = (int)cols[kSlice]; if (slice < minSlice) minSlice = slice; @@ -2240,17 +2241,17 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt #ifdef old int gradDynVol = (int)cols[kGradientNumber] - 1; if (gradDynVol < 0) - gradDynVol = 0; //old PAREC without cols[kGradientNumber] + gradDynVol = 0; // old PAREC without cols[kGradientNumber] vol = vol + (volStep * (gradDynVol)); if (vol < 0) vol = 0; volStep = volStep * maxNumberOfGradientOrients; int bval = (int)cols[kbvalNumber]; - if (bval > 2) //b=0 is 0, b=1000 is 1, b=2000 is 2 - b=0 does not have multiple directions + if (bval > 2) // b=0 is 0, b=1000 is 1, b=2000 is 2 - b=0 does not have multiple directions bval = bval - 1; else bval = 1; - //if (slice == 1) printMessage("bVal %d bVec %d isADC %d nbVal %d nGrad %d\n",(int) cols[kbvalNumber], (int)cols[kGradientNumber], isADC, maxNumberOfDiffusionValues, maxNumberOfGradientOrients); + // if (slice == 1) printMessage("bVal %d bVec %d isADC %d nbVal %d nGrad %d\n",(int) cols[kbvalNumber], (int)cols[kGradientNumber], isADC, maxNumberOfDiffusionValues, maxNumberOfGradientOrients); vol = vol + (volStep * (bval - 1)); volStep = volStep * (maxNumberOfDiffusionValues - 1); if (isADC) @@ -2259,17 +2260,17 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt if (maxNumberOfDiffusionValues > 1) { int grad = (int)cols[kGradientNumber] - 1; if (grad < 0) - grad = 0; //old v4 does not have this tag + grad = 0; // old v4 does not have this tag int bval = (int)cols[kbvalNumber] - 1; if (bval < 0) - bval = 0; //old v4 does not have this tag + bval = 0; // old v4 does not have this tag if (isADC) vol = vol + (volStep * maxNumberOfDiffusionValues * maxNumberOfGradientOrients) + bval; else vol = vol + (volStep * grad) + (bval * maxNumberOfGradientOrients); volStep = volStep * (maxNumberOfDiffusionValues + 1) * maxNumberOfGradientOrients; - //if (slice == 1) printMessage("vol %d step %d bVal %d bVec %d isADC %d nbVal %d nGrad %d\n", vol, volStep, (int) cols[kbvalNumber], (int)cols[kGradientNumber], isADC, maxNumberOfDiffusionValues, maxNumberOfGradientOrients); + // if (slice == 1) printMessage("vol %d step %d bVal %d bVec %d isADC %d nbVal %d nGrad %d\n", vol, volStep, (int) cols[kbvalNumber], (int)cols[kGradientNumber], isADC, maxNumberOfDiffusionValues, maxNumberOfGradientOrients); } #endif vol = vol + (volStep * ((int)cols[kEcho] - 1)); @@ -2281,13 +2282,13 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt ASL = 1; vol = vol + (volStep * (ASL - 1)); volStep = volStep * maxNumberOfLabels; - //if ((int)cols[kSequence] > 0) + // if ((int)cols[kSequence] > 0) int seq = (int)cols[kSequence]; if (seq1 < 0) seq1 = seq; if (seq > maxSeq) maxSeq = seq; - if (seq != seq1) { //sequence varies within this PAR file + if (seq != seq1) { // sequence varies within this PAR file if (!isSequenceWarning) { isSequenceWarning = true; printWarning("'scanning sequence' column varies within a single file. This behavior is not described at the top of the header.\n"); @@ -2295,11 +2296,11 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt vol = vol + (volStep * 1); volStep = volStep * 2; } - //if (slice == 1) printMessage("%d\t%d\t%d\t%d\t%d\n", isADC,(int)cols[kbvalNumber], (int)cols[kGradientNumber], bval, vol); + // if (slice == 1) printMessage("%d\t%d\t%d\t%d\t%d\n", isADC,(int)cols[kbvalNumber], (int)cols[kGradientNumber], bval, vol); if (vol > maxVol) maxVol = vol; - int imageType = (int) cols[kImageType]; - bool isMagnitude = (imageType == 0); + int imageType = (int)cols[kImageType]; + bool isMagnitude = (imageType == 0); bool isReal = (imageType == 1); bool isImaginary = (imageType == 2); bool isPhase = (imageType == 3); @@ -2322,7 +2323,7 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt printWarning("Unknown image type (4). Be aware the 'phase' image is of an unknown type.\n"); isType4Warning = true; } - isPhase = true; //2019 + isPhase = true; // 2019 } if ((cols[kImageType] != 18) && ((cols[kImageType] < 0.0) || (cols[kImageType] > 3.0))) { if (!isType4Warning) { @@ -2340,24 +2341,24 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt if (vol >= kMaxDTI4D) { printError("Use dicm2nii or increase kMaxDTI4D (currently %d)to be more than %d\n", kMaxDTI4D, kMaxImageType * num2DExpected); printMessage(" slices*grad*bval*cardiac*echo*dynamic*mix*label = %d*%d*%d*%d*%d*%d*%d*%d\n", - d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, - maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels); + d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, + maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels); free(cols); return d; } // dti4D->S[vol].V[0] = cols[kbval]; - //dti4D->gradDynVol[vol] = gradDynVol; + // dti4D->gradDynVol[vol] = gradDynVol; dti4D->TE[vol] = cols[kTEcho]; if (isSameFloatGE(cols[kTEcho], 0)) - dti4D->TE[vol] = TE; //kludge for cols[kImageType]==18 where TE set as 0 + dti4D->TE[vol] = TE; // kludge for cols[kImageType]==18 where TE set as 0 else TE = cols[kTEcho]; dti4D->triggerDelayTime[vol] = cols[kTriggerTime]; if (dti4D->TE[vol] < 0) - dti4D->TE[vol] = 0; //used to detect sparse volumes - //dti4D->intenIntercept[vol] = cols[kRI]; - //dti4D->intenScale[vol] = cols[kRS]; - //dti4D->intenScalePhilips[vol] = cols[kSS]; + dti4D->TE[vol] = 0; // used to detect sparse volumes + // dti4D->intenIntercept[vol] = cols[kRI]; + // dti4D->intenScale[vol] = cols[kRS]; + // dti4D->intenScalePhilips[vol] = cols[kSS]; dti4D->isReal[vol] = isReal; dti4D->isImaginary[vol] = isImaginary; dti4D->isPhase[vol] = isPhase; @@ -2369,30 +2370,30 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt if ((vol + 1) > d.CSA.numDti) d.CSA.numDti = vol + 1; } - if (numSlice2D < kMaxDTI4D) { //issue 363: intensity can vary with each 2D slice of 4D volume + if (numSlice2D < kMaxDTI4D) { // issue 363: intensity can vary with each 2D slice of 4D volume dti4D->intenIntercept[numSlice2D] = cols[kRI]; dti4D->intenScale[numSlice2D] = cols[kRS]; dti4D->intenScalePhilips[numSlice2D] = cols[kSS]; } - //if (slice == 1) printWarning("%d\n", (int)cols[kEcho]); + // if (slice == 1) printWarning("%d\n", (int)cols[kEcho]); slice = slice + (vol * d.xyzDim[3]); - //offset images by type: mag+0,real+1, imag+2,phase+3 - //if (cols[kImageType] != 0) //yikes - phase maps! + // offset images by type: mag+0,real+1, imag+2,phase+3 + // if (cols[kImageType] != 0) //yikes - phase maps! // slice = slice + numExpected; #ifdef USING_DCM2NIIXFSWRAPPER // fix ubuntu18 compiler error maxSlice2D = (slice > maxSlice2D) ? slice : maxSlice2D; #else - maxSlice2D = std::max(slice, maxSlice2D); + maxSlice2D = std::max(slice, maxSlice2D); #endif if ((slice >= 0) && (slice < kMaxSlice2D) && (numSlice2D < kMaxSlice2D) && (numSlice2D >= 0)) { dti4D->sliceOrder[slice - 1] = numSlice2D; - //printMessage("%d\t%d\t%d\n", numSlice2D, slice, (int)cols[kSlice],(int)vol); + // printMessage("%d\t%d\t%d\n", numSlice2D, slice, (int)cols[kSlice],(int)vol); } numSlice2D++; } - //printMessage("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff)) - p = fgets(buff, LINESZ, fp); //get next line + // printMessage("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff)) + p = fgets(buff, LINESZ, fp); // get next line } free(cols); fclose(fp); @@ -2400,26 +2401,26 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt printError("Invalid PAR format header (unable to detect version or slices) %s\n", parname); return d; } - if (numSlice2D > kMaxSlice2D) { //check again after reading, as top portion of header does not report image types or isotropics + if (numSlice2D > kMaxSlice2D) { // check again after reading, as top portion of header does not report image types or isotropics printError("Increase kMaxSlice2D from %d to at least %d (or use dicm2nii).\n", kMaxSlice2D, numSlice2D); return d; } - if (numSlice2D > kMaxDTI4D) { //since issue460, kMaxSlice2D == kMaxSlice4D, so we should never get here + if (numSlice2D > kMaxDTI4D) { // since issue460, kMaxSlice2D == kMaxSlice4D, so we should never get here printError("Increase kMaxDTI4D from %d to at least %d (or use dicm2nii).\n", kMaxDTI4D, numSlice2D); return d; } d.manufacturer = kMANUFACTURER_PHILIPS; d.isValid = true; d.isSigned = true; - //remove unused volumes - this will happen if unless we have all 4 image types: real, imag, mag, phase + // remove unused volumes - this will happen if unless we have all 4 image types: real, imag, mag, phase maxVol = 0; for (int i = 0; i < kMaxDTI4D; i++) { if (dti4D->TE[i] > -1.0) { dti4D->TE[maxVol] = dti4D->TE[i]; dti4D->triggerDelayTime[maxVol] = dti4D->triggerDelayTime[i]; - //dti4D->intenIntercept[maxVol] = dti4D->intenIntercept[i]; - //dti4D->intenScale[maxVol] = dti4D->intenScale[i]; - //dti4D->intenScalePhilips[maxVol] = dti4D->intenScalePhilips[i]; + // dti4D->intenIntercept[maxVol] = dti4D->intenIntercept[i]; + // dti4D->intenScale[maxVol] = dti4D->intenScale[i]; + // dti4D->intenScalePhilips[maxVol] = dti4D->intenScalePhilips[i]; dti4D->isReal[maxVol] = dti4D->isReal[i]; dti4D->isImaginary[maxVol] = dti4D->isImaginary[i]; dti4D->isPhase[maxVol] = dti4D->isPhase[i]; @@ -2431,51 +2432,55 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt } } if (d.CSA.numDti > 0) - d.CSA.numDti = maxVol; //e.g. gradient 2 can skip B=0 but include isotropic - //remove unused slices - this will happen if unless we have all 4 image types: real, imag, mag, phase + d.CSA.numDti = maxVol; // e.g. gradient 2 can skip B=0 but include isotropic + // remove unused slices - this will happen if unless we have all 4 image types: real, imag, mag, phase int slice = 0; for (int i = 0; i < kMaxSlice2D; i++) { - if (dti4D->sliceOrder[i] > -1) { //this slice was populated + if (dti4D->sliceOrder[i] > -1) { // this slice was populated dti4D->sliceOrder[slice] = dti4D->sliceOrder[i]; slice = slice + 1; } } - if (maxSlice2D >= kMaxSlice2D) {//issue659 + if (maxSlice2D >= kMaxSlice2D) { // issue659 printError("Use dicm2nii or increase kMaxSlice2D (issue 659) %d\n", maxSlice2D); d.isValid = false; } - //number of image types: issue659 + // number of image types: issue659 int nType = 0; - if (d.isHasMagnitude) nType ++; - if (d.isHasImaginary) nType ++; - if (d.isHasPhase) nType ++; - if (d.isHasReal) nType ++; + if (d.isHasMagnitude) + nType++; + if (d.isHasImaginary) + nType++; + if (d.isHasPhase) + nType++; + if (d.isHasReal) + nType++; #ifdef USING_DCM2NIIXFSWRAPPER - // fix ubuntu18 compiler error + // fix ubuntu18 compiler error nType = (nType > 1) ? nType : 1; #else - nType = std::max(nType, 1); + nType = std::max(nType, 1); #endif if (slice != numSlice2D) { printError("Catastrophic error: found %d but expected %d slices. %s\n", slice, numSlice2D, parname); printMessage(" slices*grad*bval*cardiac*echo*dynamic*mix*labels*types = %d*%d*%d*%d*%d*%d*%d*%d*%d\n", - d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, - maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels, nType); + d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, + maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels, nType); d.isValid = false; } - for (int i = 0; i < numSlice2D; i++) { //issue363 + for (int i = 0; i < numSlice2D; i++) { // issue363 if (dti4D->intenIntercept[i] != dti4D->intenIntercept[0]) d.isScaleVariesEnh = true; if (dti4D->intenScale[i] != dti4D->intenScale[0]) d.isScaleVariesEnh = true; if (dti4D->intenScalePhilips[i] != dti4D->intenScalePhilips[0]) d.isScaleVariesEnh = true; - //printf("%g --> %g\n", dti4D->intenIntercept[i], dti4D->intenScale[i]); + // printf("%g --> %g\n", dti4D->intenIntercept[i], dti4D->intenScale[i]); } - if (d.isScaleVariesEnh) { //juggle to sorted order, required for subsequent rescaling + if (d.isScaleVariesEnh) { // juggle to sorted order, required for subsequent rescaling printWarning("PAR/REC intensity scaling varies between slices (please validate output).\n"); TDTI4D tmp; - for (int i = 0; i < numSlice2D; i++) { //issue363 + for (int i = 0; i < numSlice2D; i++) { // issue363 tmp.intenIntercept[i] = dti4D->intenIntercept[i]; tmp.intenScale[i] = dti4D->intenScale[i]; tmp.intenScalePhilips[i] = dti4D->intenScalePhilips[i]; @@ -2501,7 +2506,7 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt d.xyzDim[3] = numSlice; num2DExpected = d.xyzDim[3] * num3DExpected; } - if ((maxBValue <= 0.0f) && (maxDyn > minDyn) && (maxDynTime > minDynTime)) { //use max vs min Dyn instead of && (d.CSA.numDti > 1) + if ((maxBValue <= 0.0f) && (maxDyn > minDyn) && (maxDynTime > minDynTime)) { // use max vs min Dyn instead of && (d.CSA.numDti > 1) int numDyn = (maxDyn - minDyn) + 1; if (numDyn != maxNumberOfDynamics) { printWarning("Expected %d dynamics, but found %d (%d..%d).\n", maxNumberOfDynamics, numDyn, minDyn, maxDyn); @@ -2524,8 +2529,8 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt } if ((numSlice2D % num2DExpected) != 0) { printMessage("Found %d slices, but expected divisible by %d: slices*grad*bval*cardiac*echo*dynamic*mix*labels = %d*%d*%d*%d*%d*%d*%d*%d %s\n", numSlice2D, num2DExpected, - d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, - maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels, parname); + d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, + maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels, parname); d.isValid = false; } if (dynNotAscending) { @@ -2544,27 +2549,27 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt printWarning("Intensity slope/intercept varies between slices! [check resulting images]\n"); if ((isVerbose) && (d.isValid)) { printMessage(" slices*grad*bval*cardiac*echo*dynamic*mix*labels = %d*%d*%d*%d*%d*%d*%d*%d\n", - d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, - maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels); + d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, + maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels); } - if ((d.xyzDim[3] > 1) && (minSlice == 1) && (maxSlice > minSlice)) { //issue 273 + if ((d.xyzDim[3] > 1) && (minSlice == 1) && (maxSlice > minSlice)) { // issue 273 float dx[4]; dx[1] = (d.patientPosition[1] - d.patientPositionLast[1]); dx[2] = (d.patientPosition[2] - d.patientPositionLast[2]); dx[3] = (d.patientPosition[3] - d.patientPositionLast[3]); - //compute error using 3D pythagorean theorm + // compute error using 3D pythagorean theorm float sliceMM = sqrt(pow(dx[1], 2) + pow(dx[2], 2) + pow(dx[3], 2)); sliceMM = sliceMM / (maxSlice - minSlice); if (!(isSameFloatGE(sliceMM, d.xyzMM[3]))) { - //if (d.xyzMM[3] > 0.0) + // if (d.xyzMM[3] > 0.0) printWarning("Distance between slices reported by slice gap+thick does not match estimate from slice positions (issue 273).\n"); d.xyzMM[3] = sliceMM; } - } //issue 273 + } // issue 273 printMessage("Done reading PAR header version %.1f, with %d slices\n", (float)parVers / 10, numSlice2D); - //see Xiangrui Li 's dicm2nii (also BSD license) - // http://www.mathworks.com/matlabcentral/fileexchange/42997-dicom-to-nifti-converter - // Rotation order and signs are figured out by trial and error, not 100% sure + // see Xiangrui Li 's dicm2nii (also BSD license) + // http://www.mathworks.com/matlabcentral/fileexchange/42997-dicom-to-nifti-converter + // Rotation order and signs are figured out by trial and error, not 100% sure float d2r = (float)(M_PI / 180.0); vec3 ca = setVec3(cos(d.angulation[1] * d2r), cos(d.angulation[2] * d2r), cos(d.angulation[3] * d2r)); vec3 sa = setVec3(sin(d.angulation[1] * d2r), sin(d.angulation[2] * d2r), sin(d.angulation[3] * d2r)); @@ -2580,13 +2585,13 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt for (int r = 0; r < 3; r++) for (int c = 0; c < 3; c++) if (c != 1) - R.m[r][c] = -R.m[r][c]; //invert first and final columns + R.m[r][c] = -R.m[r][c]; // invert first and final columns } else if (d.sliceOrient == kSliceOrientCor) { ixyz = setiVec3(1, 3, 2); for (int r = 0; r < 3; r++) - R.m[r][2] = -R.m[r][2]; //invert rows of final column + R.m[r][2] = -R.m[r][2]; // invert rows of final column } - R = nifti_mat33_reorder_cols(R, ixyz); //dicom rotation matrix + R = nifti_mat33_reorder_cols(R, ixyz); // dicom rotation matrix d.orient[1] = R.m[0][0]; d.orient[2] = R.m[1][0]; d.orient[3] = R.m[2][0]; @@ -2598,18 +2603,18 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt R = nifti_mat33_mul(R, diag); mat44 R44; LOAD_MAT44(R44, R.m[0][0], R.m[0][1], R.m[0][2], d.stackOffcentre[1], - R.m[1][0], R.m[1][1], R.m[1][2], d.stackOffcentre[2], - R.m[2][0], R.m[2][1], R.m[2][2], d.stackOffcentre[3]); + R.m[1][0], R.m[1][1], R.m[1][2], d.stackOffcentre[2], + R.m[2][0], R.m[2][1], R.m[2][2], d.stackOffcentre[3]); vec3 x; - if (parVers > 40) //guess + if (parVers > 40) // guess x = setVec3(((float)d.xyzDim[1] - 1) / 2, ((float)d.xyzDim[2] - 1) / 2, ((float)d.xyzDim[3] - 1) / 2); else x = setVec3((float)d.xyzDim[1] / 2, (float)d.xyzDim[2] / 2, ((float)d.xyzDim[3] - 1) / 2); mat44 eye; LOAD_MAT44(eye, 1.0f, 0.0f, 0.0f, x.v[0], - 0.0f, 1.0f, 0.0f, x.v[1], - 0.0f, 0.0f, 1.0f, x.v[2]); - eye = nifti_mat44_inverse(eye); //we wish to compute R/eye, so compute invEye and calculate R*invEye + 0.0f, 1.0f, 0.0f, x.v[1], + 0.0f, 0.0f, 1.0f, x.v[2]); + eye = nifti_mat44_inverse(eye); // we wish to compute R/eye, so compute invEye and calculate R*invEye R44 = nifti_mat44_mul(R44, eye); vec4 y; y.v[0] = 0.0f; @@ -2617,70 +2622,70 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt y.v[2] = (float)d.xyzDim[3] - 1.0f; y.v[3] = 1.0f; y = nifti_vect44mat44_mul(y, R44); - int iOri = 2; //for axial, slices are 3rd dimension (indexed from 0) (k) + int iOri = 2; // for axial, slices are 3rd dimension (indexed from 0) (k) if (d.sliceOrient == kSliceOrientSag) - iOri = 0; //for sagittal, slices are 1st dimension (i) + iOri = 0; // for sagittal, slices are 1st dimension (i) if (d.sliceOrient == kSliceOrientCor) - iOri = 1; //for coronal, slices are 2nd dimension (j) - if (d.xyzDim[3] > 1) { //detect and fix Philips Bug - //Est: assuming "image offcentre (ap,fh,rl in mm )" is correct + iOri = 1; // for coronal, slices are 2nd dimension (j) + if (d.xyzDim[3] > 1) { // detect and fix Philips Bug + // Est: assuming "image offcentre (ap,fh,rl in mm )" is correct float stackOffcentreEst[4]; stackOffcentreEst[1] = (d.patientPosition[1] + d.patientPositionLast[1]) * 0.5; stackOffcentreEst[2] = (d.patientPosition[2] + d.patientPositionLast[2]) * 0.5; stackOffcentreEst[3] = (d.patientPosition[3] + d.patientPositionLast[3]) * 0.5; - //compute error using 3D pythagorean theorm + // compute error using 3D pythagorean theorm stackOffcentreEst[0] = sqrt(pow(stackOffcentreEst[1] - d.stackOffcentre[1], 2) + pow(stackOffcentreEst[2] - d.stackOffcentre[2], 2) + pow(stackOffcentreEst[3] - d.stackOffcentre[3], 2)); - //Est: assuming "image offcentre (ap,fh,rl in mm )" is stored in order rl,ap,fh + // Est: assuming "image offcentre (ap,fh,rl in mm )" is stored in order rl,ap,fh float stackOffcentreRev[4]; stackOffcentreRev[1] = (d.patientPosition[2] + d.patientPositionLast[2]) * 0.5; stackOffcentreRev[2] = (d.patientPosition[3] + d.patientPositionLast[3]) * 0.5; stackOffcentreRev[3] = (d.patientPosition[1] + d.patientPositionLast[1]) * 0.5; - //compute error using 3D pythagorean theorm + // compute error using 3D pythagorean theorm stackOffcentreRev[0] = sqrt(pow(stackOffcentreRev[1] - d.stackOffcentre[1], 2) + pow(stackOffcentreRev[2] - d.stackOffcentre[2], 2) + pow(stackOffcentreRev[3] - d.stackOffcentre[3], 2)); - //detect, report and fix error + // detect, report and fix error if ((stackOffcentreEst[0] > 1.0) && (stackOffcentreRev[0] < stackOffcentreEst[0])) { - //error detected: the ">1.0" handles the low precision of the "Off Centre" values + // error detected: the ">1.0" handles the low precision of the "Off Centre" values printMessage("Order of 'image offcentre (ap,fh,rl in mm )' appears incorrect (assuming rl,ap,fh)\n"); printMessage(" err[ap,fh,rl]= %g (%g %g %g) \n", stackOffcentreEst[0], stackOffcentreEst[1], stackOffcentreEst[2], stackOffcentreEst[3]); printMessage(" err[rl,ap,fh]= %g (%g %g %g) \n", stackOffcentreRev[0], stackOffcentreRev[1], stackOffcentreRev[2], stackOffcentreRev[3]); printMessage(" orient\t%d\tOffCentre 1st->mid->nth\t%g\t%g\t%g\t->\t%g\t%g\t%g\t->\t%g\t%g\t%g\t=\t%g\t%s\n", iOri, - d.patientPosition[1], d.patientPosition[2], d.patientPosition[3], - d.stackOffcentre[1], d.stackOffcentre[2], d.stackOffcentre[3], - d.patientPositionLast[1], d.patientPositionLast[2], d.patientPositionLast[3], (d.patientPosition[iOri + 1] - d.patientPositionLast[iOri + 1]), parname); - //correct patientPosition + d.patientPosition[1], d.patientPosition[2], d.patientPosition[3], + d.stackOffcentre[1], d.stackOffcentre[2], d.stackOffcentre[3], + d.patientPositionLast[1], d.patientPositionLast[2], d.patientPositionLast[3], (d.patientPosition[iOri + 1] - d.patientPositionLast[iOri + 1]), parname); + // correct patientPosition for (int i = 1; i < 4; i++) stackOffcentreRev[i] = d.patientPosition[i]; d.patientPosition[1] = stackOffcentreRev[2]; d.patientPosition[2] = stackOffcentreRev[3]; d.patientPosition[3] = stackOffcentreRev[1]; - //correct patientPositionLast + // correct patientPositionLast for (int i = 1; i < 4; i++) stackOffcentreRev[i] = d.patientPositionLast[i]; d.patientPositionLast[1] = stackOffcentreRev[2]; d.patientPositionLast[2] = stackOffcentreRev[3]; d.patientPositionLast[3] = stackOffcentreRev[1]; - } //if bug: report and fix - } //if 3D data + } // if bug: report and fix + } // if 3D data bool flip = false; - //assume head first supine + // assume head first supine if ((iOri == 0) && (((d.patientPosition[iOri + 1] - d.patientPositionLast[iOri + 1]) > 0))) - flip = true; //6/2018 : TODO, not sure if this is >= or > + flip = true; // 6/2018 : TODO, not sure if this is >= or > if ((iOri == 1) && (((d.patientPosition[iOri + 1] - d.patientPositionLast[iOri + 1]) <= 0))) flip = true; //<= not <, leslie_dti_6_1.PAR if ((iOri == 2) && (((d.patientPosition[iOri + 1] - d.patientPositionLast[iOri + 1]) <= 0))) flip = true; //<= not <, see leslie_dti_3_1.PAR if (flip) { - //if ((d.patientPosition[iOri+1] - d.patientPositionLast[iOri+1]) < 0) { - //if (( (y.v[iOri]-R44.m[iOri][3])>0 ) == ( (y.v[iOri]-d.stackOffcentre[iOri+1])>0 ) ) { + // if ((d.patientPosition[iOri+1] - d.patientPositionLast[iOri+1]) < 0) { + // if (( (y.v[iOri]-R44.m[iOri][3])>0 ) == ( (y.v[iOri]-d.stackOffcentre[iOri+1])>0 ) ) { d.patientPosition[1] = R44.m[0][3]; d.patientPosition[2] = R44.m[1][3]; d.patientPosition[3] = R44.m[2][3]; d.patientPositionLast[1] = y.v[0]; d.patientPositionLast[2] = y.v[1]; d.patientPositionLast[3] = y.v[2]; - //printWarning(" Flipping slice order: please verify %s\n", parname); + // printWarning(" Flipping slice order: please verify %s\n", parname); } else { - //printWarning(" NOT Flipping slice order: please verify %s\n", parname); + // printWarning(" NOT Flipping slice order: please verify %s\n", parname); d.patientPosition[1] = y.v[0]; d.patientPosition[2] = y.v[1]; d.patientPosition[3] = y.v[2]; @@ -2688,9 +2693,9 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt d.patientPositionLast[2] = R44.m[1][3]; d.patientPositionLast[3] = R44.m[2][3]; } - //finish up + // finish up changeExt(parname, "REC"); -#ifndef _MSC_VER //Linux is case sensitive, #include +#ifndef _MSC_VER // Linux is case sensitive, #include if (access(parname, F_OK) != 0) changeExt(parname, "rec"); #endif @@ -2701,14 +2706,14 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt printMessage(" slices*grad*bval*cardiac*echo*dynamic*mix*label = %d*%d*%d*%d*%d*%d*%d*%d\n", d.xyzDim[3], maxNumberOfGradientOrients, maxNumberOfDiffusionValues, maxNumberOfCardiacPhases, maxNumberOfEchoes, maxNumberOfDynamics, maxNumberOfMixes, maxNumberOfLabels); d.CSA.numDti = 0; }; - //check if dimensions vary - if (maxVol > 0) { //maxVol indexed from 0 + // check if dimensions vary + if (maxVol > 0) { // maxVol indexed from 0 for (int i = 1; i <= maxVol; i++) { - //if (dti4D->gradDynVol[i] > d.maxGradDynVol) d.maxGradDynVol = dti4D->gradDynVol[i]; - //issue363 slope/intercept can vary for each 2D slice, not only between 3D volumes in a 4D time series - //if (dti4D->intenIntercept[i] != dti4D->intenIntercept[0]) d.isScaleOrTEVaries = true; - //if (dti4D->intenScale[i] != dti4D->intenScale[0]) d.isScaleOrTEVaries = true; - //if (dti4D->intenScalePhilips[i] != dti4D->intenScalePhilips[0]) d.isScaleOrTEVaries = true; + // if (dti4D->gradDynVol[i] > d.maxGradDynVol) d.maxGradDynVol = dti4D->gradDynVol[i]; + // issue363 slope/intercept can vary for each 2D slice, not only between 3D volumes in a 4D time series + // if (dti4D->intenIntercept[i] != dti4D->intenIntercept[0]) d.isScaleOrTEVaries = true; + // if (dti4D->intenScale[i] != dti4D->intenScale[0]) d.isScaleOrTEVaries = true; + // if (dti4D->intenScalePhilips[i] != dti4D->intenScalePhilips[0]) d.isScaleOrTEVaries = true; if (dti4D->isPhase[i] != dti4D->isPhase[0]) d.isScaleOrTEVaries = true; if (dti4D->isReal[i] != dti4D->isReal[0]) @@ -2718,13 +2723,13 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt if (dti4D->triggerDelayTime[i] != dti4D->triggerDelayTime[0]) d.isScaleOrTEVaries = true; } - //if (d.isScaleOrTEVaries) + // if (d.isScaleOrTEVaries) // printWarning("Varying dimensions (echoes, phase maps, intensity scaling) will require volumes to be saved separately (hint: you may prefer dicm2nii output)\n"); } - //if (d.CSA.numDti > 1) + // if (d.CSA.numDti > 1) // for (int i = 0; i < d.CSA.numDti; i++) // printMessage("%d\tb=\t%g\tv=\t%g\t%g\t%g\n",i,dti4D->S[i].V[0],dti4D->S[i].V[1],dti4D->S[i].V[2],dti4D->S[i].V[3]); - //check DTI makes sense + // check DTI makes sense if (d.CSA.numDti > 1) { bool v1varies = false; bool v2varies = false; @@ -2745,16 +2750,16 @@ struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dt if ((maxEcho > 1) || (maxCardiac > 1)) d.isScaleOrTEVaries = true; return d; -} //nii_readParRec() +} // nii_readParRec() size_t nii_SliceBytes(struct nifti_1_header hdr) { - //size of 2D slice + // size of 2D slice size_t imgsz = hdr.bitpix / 8; for (int i = 1; i < 3; i++) if (hdr.dim[i] > 1) imgsz = imgsz * hdr.dim[i]; return imgsz; -} //nii_SliceBytes() +} // nii_SliceBytes() size_t nii_ImgBytes(struct nifti_1_header hdr) { size_t imgsz = hdr.bitpix / 8; @@ -2762,21 +2767,21 @@ size_t nii_ImgBytes(struct nifti_1_header hdr) { if (hdr.dim[i] > 1) imgsz = imgsz * hdr.dim[i]; return imgsz; -} //nii_ImgBytes() +} // nii_ImgBytes() -//unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, int nMosaicSlices, int ProtocolSliceNumber1) { +// unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, int nMosaicSlices, int ProtocolSliceNumber1) { unsigned char *nii_demosaic(unsigned char *inImg, struct nifti_1_header *hdr, int nMosaicSlices, bool isUIH) { - //demosaic http://nipy.org/nibabel/dicom/dicom_mosaic.html + // demosaic http://nipy.org/nibabel/dicom/dicom_mosaic.html if (nMosaicSlices < 2) return inImg; - //Byte inImg[ [img length] ]; + // Byte inImg[ [img length] ]; //[img getBytes:&inImg length:[img length]]; int nCol = (int)ceil(sqrt((double)nMosaicSlices)); int nRow = nCol; - //n.b. Siemens store 20 images as 5x5 grid, UIH as 5rows, 4 Col https://github.com/rordenlab/dcm2niix/issues/225 + // n.b. Siemens store 20 images as 5x5 grid, UIH as 5rows, 4 Col https://github.com/rordenlab/dcm2niix/issues/225 if (isUIH) nRow = ceil((float)nMosaicSlices / (float)nCol); - //printf("%d = %dx%d\n", nMosaicSlices, nCol, nRow); + // printf("%d = %dx%d\n", nMosaicSlices, nCol, nRow); int colBytes = hdr->dim[1] / nCol * hdr->bitpix / 8; int lineBytes = hdr->dim[1] * hdr->bitpix / 8; int rowBytes = hdr->dim[1] * hdr->dim[2] / nRow * hdr->bitpix / 8; @@ -2799,46 +2804,46 @@ unsigned char *nii_demosaic(unsigned char *inImg, struct nifti_1_header *hdr, in if (col >= nCol) { row++; col = 0; - } //start new column - } //for m = each mosaic slice + } // start new column + } // for m = each mosaic slice free(inImg); return outImg; } // nii_demosaic() unsigned char *nii_flipImgY(unsigned char *bImg, struct nifti_1_header *hdr) { - //DICOM row order opposite from NIfTI + // DICOM row order opposite from NIfTI int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i]; size_t lineBytes = hdr->dim[1] * hdr->bitpix / 8; if ((hdr->datatype == DT_RGB24) && (hdr->bitpix == 24) && (hdr->intent_code == NIFTI_INTENT_NONE)) { - //we use the intent code to indicate planar vs triplet... + // we use the intent code to indicate planar vs triplet... lineBytes = hdr->dim[1]; dim3to7 = dim3to7 * 3; - } //rgb data saved planar (RRR..RGGGG..GBBB..B + } // rgb data saved planar (RRR..RGGGG..GBBB..B unsigned char *line = (unsigned char *)malloc(sizeof(unsigned char) * (lineBytes)); size_t sliceBytes = hdr->dim[2] * lineBytes; - int halfY = hdr->dim[2] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns - for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice + int halfY = hdr->dim[2] / 2; // note truncated toward zero, so halfY=2 regardless of 4 or 5 columns + for (int sl = 0; sl < dim3to7; sl++) { // for each 2D slice size_t slBottom = (size_t)sl * sliceBytes; size_t slTop = (((size_t)sl + 1) * sliceBytes) - lineBytes; for (int y = 0; y < halfY; y++) { - //swap order of lines - memcpy(line, &bImg[slBottom], lineBytes); //memcpy(&line, &bImg[slBottom], lineBytes); + // swap order of lines + memcpy(line, &bImg[slBottom], lineBytes); // memcpy(&line, &bImg[slBottom], lineBytes); memcpy(&bImg[slBottom], &bImg[slTop], lineBytes); - memcpy(&bImg[slTop], line, lineBytes); //tpx memcpy(&bImg[slTop], &line, lineBytes); + memcpy(&bImg[slTop], line, lineBytes); // tpx memcpy(&bImg[slTop], &line, lineBytes); slTop -= lineBytes; slBottom += lineBytes; - } //for y - } //for each slice + } // for y + } // for each slice free(line); return bImg; } // nii_flipImgY() unsigned char *nii_flipImgZ(unsigned char *bImg, struct nifti_1_header *hdr) { - //DICOM row order opposite from NIfTI - int halfZ = hdr->dim[3] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns + // DICOM row order opposite from NIfTI + int halfZ = hdr->dim[3] / 2; // note truncated toward zero, so halfY=2 regardless of 4 or 5 columns if (halfZ < 1) return bImg; int dim4to7 = 1; @@ -2848,44 +2853,44 @@ unsigned char *nii_flipImgZ(unsigned char *bImg, struct nifti_1_header *hdr) { size_t sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix / 8; size_t volBytes = sliceBytes * hdr->dim[3]; unsigned char *slice = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes)); - for (int vol = 0; vol < dim4to7; vol++) { //for each 2D slice + for (int vol = 0; vol < dim4to7; vol++) { // for each 2D slice size_t slBottom = vol * volBytes; size_t slTop = ((vol + 1) * volBytes) - sliceBytes; for (int z = 0; z < halfZ; z++) { - //swap order of lines - memcpy(slice, &bImg[slBottom], sliceBytes); //TPX memcpy(&slice, &bImg[slBottom], sliceBytes); + // swap order of lines + memcpy(slice, &bImg[slBottom], sliceBytes); // TPX memcpy(&slice, &bImg[slBottom], sliceBytes); memcpy(&bImg[slBottom], &bImg[slTop], sliceBytes); - memcpy(&bImg[slTop], slice, sliceBytes); //TPX + memcpy(&bImg[slTop], slice, sliceBytes); // TPX slTop -= sliceBytes; slBottom += sliceBytes; - } //for Z - } //for each volume + } // for Z + } // for each volume free(slice); return bImg; } // nii_flipImgZ() unsigned char *nii_flipZ(unsigned char *bImg, struct nifti_1_header *h) { - //flip slice order + // flip slice order if (h->dim[3] < 2) return bImg; mat33 s; mat44 Q44; LOAD_MAT33(s, h->srow_x[0], h->srow_x[1], h->srow_x[2], h->srow_y[0], h->srow_y[1], h->srow_y[2], - h->srow_z[0], h->srow_z[1], h->srow_z[2]); + h->srow_z[0], h->srow_z[1], h->srow_z[2]); LOAD_MAT44(Q44, h->srow_x[0], h->srow_x[1], h->srow_x[2], h->srow_x[3], - h->srow_y[0], h->srow_y[1], h->srow_y[2], h->srow_y[3], - h->srow_z[0], h->srow_z[1], h->srow_z[2], h->srow_z[3]); + h->srow_y[0], h->srow_y[1], h->srow_y[2], h->srow_y[3], + h->srow_z[0], h->srow_z[1], h->srow_z[2], h->srow_z[3]); vec4 v = setVec4(0.0f, 0.0f, (float)h->dim[3] - 1.0f); - v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin + v = nifti_vect44mat44_mul(v, Q44); // after flip this voxel will be the origin mat33 mFlipZ; LOAD_MAT33(mFlipZ, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f); s = nifti_mat33_mul(s, mFlipZ); LOAD_MAT44(Q44, s.m[0][0], s.m[0][1], s.m[0][2], v.v[0], - s.m[1][0], s.m[1][1], s.m[1][2], v.v[1], - s.m[2][0], s.m[2][1], s.m[2][2], v.v[2]); - //printMessage(" ----------> %f %f %f\n",v.v[0],v.v[1],v.v[2]); + s.m[1][0], s.m[1][1], s.m[1][2], v.v[1], + s.m[2][0], s.m[2][1], s.m[2][2], v.v[2]); + // printMessage(" ----------> %f %f %f\n",v.v[0],v.v[1],v.v[2]); setQSForm(h, Q44, true); - //printMessage("nii_flipImgY dims %dx%dx%d %d \n",h->dim[1],h->dim[2], dim3to7,h->bitpix/8); + // printMessage("nii_flipImgY dims %dx%dx%d %d \n",h->dim[1],h->dim[2], dim3to7,h->bitpix/8); return nii_flipImgZ(bImg, h); } // nii_flipZ() @@ -2893,40 +2898,40 @@ unsigned char *nii_flipY(unsigned char *bImg, struct nifti_1_header *h) { mat33 s; mat44 Q44; LOAD_MAT33(s, h->srow_x[0], h->srow_x[1], h->srow_x[2], h->srow_y[0], h->srow_y[1], h->srow_y[2], - h->srow_z[0], h->srow_z[1], h->srow_z[2]); + h->srow_z[0], h->srow_z[1], h->srow_z[2]); LOAD_MAT44(Q44, h->srow_x[0], h->srow_x[1], h->srow_x[2], h->srow_x[3], - h->srow_y[0], h->srow_y[1], h->srow_y[2], h->srow_y[3], - h->srow_z[0], h->srow_z[1], h->srow_z[2], h->srow_z[3]); + h->srow_y[0], h->srow_y[1], h->srow_y[2], h->srow_y[3], + h->srow_z[0], h->srow_z[1], h->srow_z[2], h->srow_z[3]); vec4 v = setVec4(0, (float)h->dim[2] - 1, 0); - v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin + v = nifti_vect44mat44_mul(v, Q44); // after flip this voxel will be the origin mat33 mFlipY; LOAD_MAT33(mFlipY, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f); s = nifti_mat33_mul(s, mFlipY); LOAD_MAT44(Q44, s.m[0][0], s.m[0][1], s.m[0][2], v.v[0], - s.m[1][0], s.m[1][1], s.m[1][2], v.v[1], - s.m[2][0], s.m[2][1], s.m[2][2], v.v[2]); + s.m[1][0], s.m[1][1], s.m[1][2], v.v[1], + s.m[2][0], s.m[2][1], s.m[2][2], v.v[2]); setQSForm(h, Q44, true); - //printMessage("nii_flipImgY dims %dx%d %d \n",h->dim[1],h->dim[2], h->bitpix/8); + // printMessage("nii_flipImgY dims %dx%d %d \n",h->dim[1],h->dim[2], h->bitpix/8); return nii_flipImgY(bImg, h); } // nii_flipY() -void conv1bit16bit(unsigned char *img, struct nifti_1_header hdr) { //issue572 +void conv1bit16bit(unsigned char *img, struct nifti_1_header hdr) { // issue572 printWarning("Support for images that allocate 1 bits is experimental\n"); int nVox = (int)nii_ImgBytes(hdr) / (hdr.bitpix / 8); for (int i = (nVox - 1); i >= 0; i--) { - int ibyte = i >> 3; //byte to sample - int ibit = (i % 8); //bit 0..7 - //if (highBit > 0) + int ibyte = i >> 3; // byte to sample + int ibit = (i % 8); // bit 0..7 + // if (highBit > 0) // ibit = 7 - ibit; int val = img[ibyte] >> ibit; img[i] = (val & 1); } -} //conv1bit16bit() +} // conv1bit16bit() void conv12bit16bit(unsigned char *img, struct nifti_1_header hdr) { - //convert 12-bit allocated data to 16-bit - // works for MR-MONO2-12-angio-an1 from http://www.barre.nom.fr/medical/samples/ - // looks wrong: this sample toggles between big and little endian stores + // convert 12-bit allocated data to 16-bit + // works for MR-MONO2-12-angio-an1 from http://www.barre.nom.fr/medical/samples/ + // looks wrong: this sample toggles between big and little endian stores int nVox = (int)nii_ImgBytes(hdr) / (hdr.bitpix / 8); for (int i = (nVox - 1); i >= 0; i--) { int i16 = i * 2; @@ -2941,7 +2946,7 @@ void conv12bit16bit(unsigned char *img, struct nifti_1_header hdr) { img[i16 + 0] = val & 0xFF; img[i16 + 1] = (val >> 8) & 0xFF; } -} //conv12bit16bit() +} // conv12bit16bit() unsigned char *nii_loadImgCore(char *imgname, struct nifti_1_header hdr, int bitsAllocated, int imageStart32) { size_t imgsz = nii_ImgBytes(hdr); @@ -2956,27 +2961,27 @@ unsigned char *nii_loadImgCore(char *imgname, struct nifti_1_header hdr, int bit printError("Unable to open '%s'\n", imgname); return NULL; } - //fseek(file, 0, SEEK_END); - //long fileLen = ftell(file); - #ifdef _MSC_VER +// fseek(file, 0, SEEK_END); +// long fileLen = ftell(file); +#ifdef _MSC_VER _fseeki64(file, 0, SEEK_END); size_t fileLen = _ftelli64(file); - #else - fseeko(file, 0, SEEK_END); //Windows _fseeki64 - size_t fileLen = ftello(file); //Windows _ftelli64 - #endif +#else + fseeko(file, 0, SEEK_END); // Windows _fseeki64 + size_t fileLen = ftello(file); // Windows _ftelli64 +#endif if (fileLen < (imgszRead + imageStart)) { - //note hdr.vox_offset is a float: issue507 - //https://www.nitrc.org/forum/message.php?msg_id=27155 + // note hdr.vox_offset is a float: issue507 + // https://www.nitrc.org/forum/message.php?msg_id=27155 printMessage("FileSize < (ImageSize+HeaderSize): %zu < (%zu+%zu) \n", fileLen, imgszRead, imageStart); printWarning("File not large enough to store image data: %s\n", imgname); return NULL; } fseek(file, (long)imageStart, SEEK_SET); unsigned char *bImg = (unsigned char *)malloc(imgsz); - //int i = 0; - //while (bImg[i] == 0) i++; - //printMessage("%d %d<\n",i,bImg[i]); + // int i = 0; + // while (bImg[i] == 0) i++; + // printMessage("%d %d<\n",i,bImg[i]); size_t sz = fread(bImg, 1, imgszRead, file); fclose(file); if (sz < imgszRead) { @@ -2988,10 +2993,10 @@ unsigned char *nii_loadImgCore(char *imgname, struct nifti_1_header hdr, int bit if (bitsAllocated == 12) conv12bit16bit(bImg, hdr); return bImg; -} //nii_loadImgCore() +} // nii_loadImgCore() unsigned char *nii_planar2rgb(unsigned char *bImg, struct nifti_1_header *hdr, int isPlanar) { - //DICOM data saved in triples RGBRGBRGB, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B + // DICOM data saved in triples RGBRGBRGB, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B if (bImg == NULL) return NULL; if (hdr->datatype != DT_RGB24) @@ -3009,9 +3014,9 @@ unsigned char *nii_planar2rgb(unsigned char *bImg, struct nifti_1_header *hdr, i int sliceOffsetR = 0; int sliceOffsetG = sliceOffsetR + sliceBytes8; int sliceOffsetB = sliceOffsetR + 2 * sliceBytes8; - //printMessage("planar->rgb %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7); + // printMessage("planar->rgb %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7); int i = 0; - for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice + for (int sl = 0; sl < dim3to7; sl++) { // for each 2D slice memcpy(slice24, &bImg[sliceOffsetRGB], sliceBytes24); for (int rgb = 0; rgb < sliceBytes8; rgb++) { bImg[i++] = slice24[sliceOffsetR + rgb]; @@ -3019,19 +3024,19 @@ unsigned char *nii_planar2rgb(unsigned char *bImg, struct nifti_1_header *hdr, i bImg[i++] = slice24[sliceOffsetB + rgb]; } sliceOffsetRGB += sliceBytes24; - } //for each slice + } // for each slice free(slice24); return bImg; -} //nii_planar2rgb() +} // nii_planar2rgb() unsigned char *nii_rgb2planar(unsigned char *bImg, struct nifti_1_header *hdr, int isPlanar) { - //DICOM data saved in triples RGBRGBRGB, Analyze RGB saved in planes RRR..RGGG..GBBBB..B + // DICOM data saved in triples RGBRGBRGB, Analyze RGB saved in planes RRR..RGGG..GBBBB..B if (bImg == NULL) return NULL; if (hdr->datatype != DT_RGB24) return bImg; if (isPlanar == 1) - return bImg; //return nii_bgr2rgb(bImg,hdr); + return bImg; // return nii_bgr2rgb(bImg,hdr); int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) @@ -3039,10 +3044,10 @@ unsigned char *nii_rgb2planar(unsigned char *bImg, struct nifti_1_header *hdr, i int sliceBytes8 = hdr->dim[1] * hdr->dim[2]; int sliceBytes24 = sliceBytes8 * 3; unsigned char *slice24 = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes24)); - //printMessage("rgb->planar %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7); + // printMessage("rgb->planar %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7); int sliceOffsetR = 0; - for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice - memcpy(slice24, &bImg[sliceOffsetR], sliceBytes24); //TPX memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24); + for (int sl = 0; sl < dim3to7; sl++) { // for each 2D slice + memcpy(slice24, &bImg[sliceOffsetR], sliceBytes24); // TPX memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24); int sliceOffsetG = sliceOffsetR + sliceBytes8; int sliceOffsetB = sliceOffsetR + 2 * sliceBytes8; int i = 0; @@ -3054,15 +3059,15 @@ unsigned char *nii_rgb2planar(unsigned char *bImg, struct nifti_1_header *hdr, i j++; } sliceOffsetR += sliceBytes24; - } //for each slice + } // for each slice free(slice24); return bImg; -} //nii_rgb2Planar() +} // nii_rgb2Planar() unsigned char *nii_iVaries(unsigned char *img, struct nifti_1_header *hdr, struct TDTI4D *dti4D) { - //each DICOM image can have its own intensity scaling, whereas NIfTI requires the same scaling for all images in a file - //WARNING: do this BEFORE nii_check16bitUnsigned!!!! - //if (hdr->datatype != DT_INT16) return img; + // each DICOM image can have its own intensity scaling, whereas NIfTI requires the same scaling for all images in a file + // WARNING: do this BEFORE nii_check16bitUnsigned!!!! + // if (hdr->datatype != DT_INT16) return img; int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) @@ -3088,18 +3093,18 @@ unsigned char *nii_iVaries(unsigned char *img, struct nifti_1_header *hdr, struc for (int i = 0; i < nVox; i++) img32[i] = (float)img32i[i]; } - free(img); //release previous image - if ((dti4D != NULL) && (dti4D->intenScale[0] != 0.0)) { //enhanced dataset, intensity varies across slices of a single file + free(img); // release previous image + if ((dti4D != NULL) && (dti4D->intenScale[0] != 0.0)) { // enhanced dataset, intensity varies across slices of a single file if (dti4D->RWVScale[0] != 0.0) - printWarning("Intensity scale/slope using 0028,1053 and 0028,1052\n"); //to do: real-world values and precise values + printWarning("Intensity scale/slope using 0028,1053 and 0028,1052\n"); // to do: real-world values and precise values int dim1to2 = hdr->dim[1] * hdr->dim[2]; int slice = -1; //(0028,1052) SS = scale slope (2005,100E) RealWorldIntercept = (0040,9224) Real World Slope = (0040,9225) - //printf("vol\tRS(0028,1053)\tRI(0028,1052)\tSS(2005,100E)\trwS(0040,9225)\trwI(0040,9224)\n"); - for (int i = 0; i < nVox; i++) { //issue 363 + // printf("vol\tRS(0028,1053)\tRI(0028,1052)\tSS(2005,100E)\trwS(0040,9225)\trwI(0040,9224)\n"); + for (int i = 0; i < nVox; i++) { // issue 363 if ((i % dim1to2) == 0) { slice++; - //printf("%d\t%g\t%g\t%g\t%g\t%g\n", slice, dti4D->intenScale[slice], dti4D->intenIntercept[slice],dti4D->intenScalePhilips[slice], dti4D->RWVScale[slice], dti4D->RWVIntercept[slice]); + // printf("%d\t%g\t%g\t%g\t%g\t%g\n", slice, dti4D->intenScale[slice], dti4D->intenIntercept[slice],dti4D->intenScalePhilips[slice], dti4D->RWVScale[slice], dti4D->RWVIntercept[slice]); } img32[i] = (img32[i] * dti4D->intenScale[slice]) + dti4D->intenIntercept[slice]; } @@ -3112,10 +3117,10 @@ unsigned char *nii_iVaries(unsigned char *img, struct nifti_1_header *hdr, struc hdr->datatype = DT_FLOAT; hdr->bitpix = 32; return (unsigned char *)img32; -} //nii_iVaries() +} // nii_iVaries() unsigned char *nii_reorderSlicesX(unsigned char *bImg, struct nifti_1_header *hdr, struct TDTI4D *dti4D) { - //Philips can save slices in any random order... rearrange all of them + // Philips can save slices in any random order... rearrange all of them int dim3to7 = 1; for (int i = 3; i < 8; i++) if (hdr->dim[i] > 1) @@ -3128,10 +3133,10 @@ unsigned char *nii_reorderSlicesX(unsigned char *bImg, struct nifti_1_header *hd uint64_t sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix / 8; unsigned char *outImg = (unsigned char *)malloc(imgSz); memcpy(&outImg[0], &bImg[0], imgSz); - for (int i = 0; i < dim3to7; i++) { //for each volume + for (int i = 0; i < dim3to7; i++) { // for each volume int fromSlice = dti4D->sliceOrder[i]; - //if (i < 10) printMessage(" ===> Moving slice from/to positions\t%d\t%d\n", i, toSlice); - //printMessage(" ===> Moving slice from/to positions\t%d\t%d\n", fromSlice, i); + // if (i < 10) printMessage(" ===> Moving slice from/to positions\t%d\t%d\n", i, toSlice); + // printMessage(" ===> Moving slice from/to positions\t%d\t%d\n", fromSlice, i); if ((i < 0) || (fromSlice >= dim3to7)) { printError("Re-ordered slice out-of-volume %d\n", fromSlice); } else if (i != fromSlice) { @@ -3156,14 +3161,14 @@ unsigned char *nii_byteswap(unsigned char *img, struct nifti_1_header *hdr) { if (hdr->bitpix == 64) nifti_swap_8bytes(nvox, ar); return img; -} //nii_byteswap() +} // nii_byteswap() #ifdef myEnableJasper unsigned char *nii_loadImgCoreJasper(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, int compressFlag) { #if defined(JAS_VERSION_MAJOR) && JAS_VERSION_MAJOR >= 3 jas_conf_clear(); jas_conf_set_debug_level(0); - jas_conf_set_max_mem_usage(1 << 30); // 1 GiB + jas_conf_set_max_mem_usage(1 << 30); // 1 GiB if (jas_init_library()) { return NULL; } @@ -3183,7 +3188,7 @@ unsigned char *nii_loadImgCoreJasper(char *imgname, struct nifti_1_header hdr, s printError("Cannot open input image file %s\n", imgname); return NULL; } - //int isSeekable = jas_stream_isseekable(in); + // int isSeekable = jas_stream_isseekable(in); jas_stream_seek(in, dcm.imageStart, 0); int infmt = jas_image_getfmt(in); if (infmt < 0) { @@ -3248,8 +3253,8 @@ unsigned char *nii_loadImgCoreJasper(char *imgname, struct nifti_1_header hdr, s return NULL; } } - //extract the data - int bpp = (prec + 7) >> 3; //e.g. 12 bits requires 2 bytes + // extract the data + int bpp = (prec + 7) >> 3; // e.g. 12 bits requires 2 bytes int imgbytes = bpp * width * height * numcmpts; if ((bpp < 1) || (bpp > 2) || (width < 1) || (height < 1) || (imgbytes < 1)) { printError("Catastrophic decompression error\n"); @@ -3257,8 +3262,8 @@ unsigned char *nii_loadImgCoreJasper(char *imgname, struct nifti_1_header hdr, s } jas_seqent_t v; unsigned char *img = (unsigned char *)malloc(imgbytes); - uint16_t *img16ui = (uint16_t *)img; //unsigned 16-bit - int16_t *img16i = (int16_t *)img; //signed 16-bit + uint16_t *img16ui = (uint16_t *)img; // unsigned 16-bit + int16_t *img16i = (int16_t *)img; // signed 16-bit if (sgnd) bpp = -bpp; if (bpp == -1) { @@ -3276,7 +3281,7 @@ unsigned char *nii_loadImgCoreJasper(char *imgname, struct nifti_1_header hdr, s return NULL; } } - //n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB + // n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB for (cmptno = 0; cmptno < numcmpts; ++cmptno) { for (y = 0; y < height; ++y) { if (jas_image_readcmpt(image, cmpts[cmptno], 0, y, width, 1, data)) { @@ -3299,9 +3304,9 @@ unsigned char *nii_loadImgCoreJasper(char *imgname, struct nifti_1_header hdr, s } pix++; ++d; - } //for x - } //for y - } //for each component + } // for x + } // for y + } // for each component jas_matrix_destroy(data); jas_image_destroy(image); jas_image_clearfmts(); @@ -3310,7 +3315,7 @@ unsigned char *nii_loadImgCoreJasper(char *imgname, struct nifti_1_header hdr, s jas_cleanup_library(); #endif return img; -} //nii_loadImgCoreJasper() +} // nii_loadImgCoreJasper() #endif struct TJPEG { @@ -3319,14 +3324,16 @@ struct TJPEG { }; TJPEG *decode_JPEG_SOF_0XC3_stack(const char *fn, int skipBytes, int isVerbose, int frames, bool isLittleEndian) { -#define abortGoto() free(lOffsetRA); return NULL; +#define abortGoto() \ + free(lOffsetRA); \ + return NULL; TJPEG *lOffsetRA = (TJPEG *)malloc(frames * sizeof(TJPEG)); FILE *reader = fopen(fn, "rb"); fseek(reader, 0, SEEK_END); long lRawSz = ftell(reader) - skipBytes; if (lRawSz <= 8) { printError("Unable to open %s\n", fn); - abortGoto(); //read failure + abortGoto(); // read failure } fseek(reader, skipBytes, SEEK_SET); unsigned char *lRawRA = (unsigned char *)malloc(lRawSz); @@ -3334,18 +3341,18 @@ TJPEG *decode_JPEG_SOF_0XC3_stack(const char *fn, int skipBytes, int isVerbose, fclose(reader); if (lSz < (size_t)lRawSz) { printError("Unable to read %s\n", fn); - abortGoto(); //read failure + abortGoto(); // read failure } - long lRawPos = 0; //starting position + long lRawPos = 0; // starting position int frame = 0; while ((frame < frames) && ((lRawPos + 10) < lRawSz)) { int tag = dcmInt(4, &lRawRA[lRawPos], isLittleEndian); - lRawPos += 4; //read tag + lRawPos += 4; // read tag int tagLength = dcmInt(4, &lRawRA[lRawPos], isLittleEndian); long tagEnd = lRawPos + tagLength + 4; if (isVerbose) printMessage("Frame %d Tag %#x length %d end at %ld\n", frame + 1, tag, tagLength, tagEnd + skipBytes); - lRawPos += 4; //read tag length + lRawPos += 4; // read tag length if ((lRawRA[lRawPos] != 0xFF) || (lRawRA[lRawPos + 1] != 0xD8) || (lRawRA[lRawPos + 2] != 0xFF)) { if (isVerbose) printWarning("JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn); @@ -3365,19 +3372,19 @@ TJPEG *decode_JPEG_SOF_0XC3_stack(const char *fn, int skipBytes, int isVerbose, } unsigned char *nii_loadImgJPEGC3(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, int isVerbose) { - //arcane and inefficient lossless compression method popularized by dcmcjpeg, examples at http://www.osirix-viewer.com/resources/dicom-image-library/ + // arcane and inefficient lossless compression method popularized by dcmcjpeg, examples at http://www.osirix-viewer.com/resources/dicom-image-library/ int dimX, dimY, bits, frames; - //clock_t start = clock(); - // https://github.com/rii-mango/JPEGLosslessDecoderJS/blob/master/tests/data/jpeg_lossless_sel1-8bit.dcm - //N.B. this current code can not extract a 2D image that is saved as multiple fragments, for example see the JPLL files at - // ftp://medical.nema.org/MEDICAL/Dicom/DataSets/WG04/ - //Live javascript code that can handle these is at - // https://github.com/chafey/cornerstoneWADOImageLoader - //I have never seen these segmented images in the wild, so we will simply warn the user if we encounter such a file - //int Sz = JPEG_SOF_0XC3_sz (imgname, (dcm.imageStart - 4), dcm.isLittleEndian); - //printMessage("Sz %d %d\n", Sz, dcm.imageBytes ); - //This behavior is legal but appears extremely rare - //ftp://medical.nema.org/medical/dicom/final/cp900_ft.pdf + // clock_t start = clock(); + // https://github.com/rii-mango/JPEGLosslessDecoderJS/blob/master/tests/data/jpeg_lossless_sel1-8bit.dcm + // N.B. this current code can not extract a 2D image that is saved as multiple fragments, for example see the JPLL files at + // ftp://medical.nema.org/MEDICAL/Dicom/DataSets/WG04/ + // Live javascript code that can handle these is at + // https://github.com/chafey/cornerstoneWADOImageLoader + // I have never seen these segmented images in the wild, so we will simply warn the user if we encounter such a file + // int Sz = JPEG_SOF_0XC3_sz (imgname, (dcm.imageStart - 4), dcm.isLittleEndian); + // printMessage("Sz %d %d\n", Sz, dcm.imageBytes ); + // This behavior is legal but appears extremely rare + // ftp://medical.nema.org/medical/dicom/final/cp900_ft.pdf if (65536 == dcm.imageBytes) printError("One frame may span multiple fragments. SOFxC3 lossless JPEG. Please extract with dcmdjpeg or gdcmconv.\n"); unsigned char *ret = decode_JPEG_SOF_0XC3(imgname, dcm.imageStart, isVerbose, &dimX, &dimY, &bits, &frames, 0); @@ -3385,8 +3392,8 @@ unsigned char *nii_loadImgJPEGC3(char *imgname, struct nifti_1_header hdr, struc printMessage("Unable to decode JPEG. Please use dcmdjpeg to uncompress data.\n"); return NULL; } - if (hdr.dim[3] != frames) { //multi-slice image saved as multiple image fragments rather than a single image - //printMessage("Unable to decode all slices (%d/%d). Please use dcmdjpeg to uncompress data.\n", frames, hdr.dim[3]); + if (hdr.dim[3] != frames) { // multi-slice image saved as multiple image fragments rather than a single image + // printMessage("Unable to decode all slices (%d/%d). Please use dcmdjpeg to uncompress data.\n", frames, hdr.dim[3]); if (ret != NULL) free(ret); TJPEG *offsetRA = decode_JPEG_SOF_0XC3_stack(imgname, dcm.imageStart - 8, isVerbose, hdr.dim[3], dcm.isLittleEndian); @@ -3405,7 +3412,7 @@ unsigned char *nii_loadImgJPEGC3(char *imgname, struct nifti_1_header hdr, struc free(bImg); return NULL; } - memcpy(&bImg[pos], ret, slicesz); //dest, src, size + memcpy(&bImg[pos], ret, slicesz); // dest, src, size free(ret); pos += slicesz; } @@ -3421,12 +3428,12 @@ unsigned char *nii_loadImgJPEGC3(char *imgname, struct nifti_1_header hdr, struc #ifndef myDisableClassicJPEG -#ifdef myTurboJPEG //if turboJPEG instead of nanoJPEG for classic JPEG decompression +#ifdef myTurboJPEG // if turboJPEG instead of nanoJPEG for classic JPEG decompression -//unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) { +// unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) { unsigned char *nii_loadImgJPEG50(char *imgname, struct TDICOMdata dcm) { - //decode classic JPEG using nanoJPEG - //printMessage("50 offset %d\n", dcm.imageStart); + // decode classic JPEG using nanoJPEG + // printMessage("50 offset %d\n", dcm.imageStart); if ((dcm.samplesPerPixel != 1) && (dcm.samplesPerPixel != 3)) { printError("%d components (expected 1 or 3) in a JPEG image '%s'\n", dcm.samplesPerPixel, imgname); return NULL; @@ -3435,7 +3442,7 @@ unsigned char *nii_loadImgJPEG50(char *imgname, struct TDICOMdata dcm) { printError("Unable to find '%s'\n", imgname); return NULL; } - //load compressed data + // load compressed data FILE *f = fopen(imgname, "rb"); fseek(f, 0, SEEK_END); long unsigned int _jpegSize = (long unsigned int)ftell(f); @@ -3450,39 +3457,39 @@ unsigned char *nii_loadImgJPEG50(char *imgname, struct TDICOMdata dcm) { _jpegSize = (long unsigned int)fread(_compressedImage, 1, _jpegSize, f); fclose(f); int jpegSubsamp, width, height; - //printMessage("Decoding with turboJPEG\n"); + // printMessage("Decoding with turboJPEG\n"); tjhandle _jpegDecompressor = tjInitDecompress(); tjDecompressHeader2(_jpegDecompressor, _compressedImage, _jpegSize, &width, &height, &jpegSubsamp); int COLOR_COMPONENTS = dcm.samplesPerPixel; - //printMessage("turboJPEG h*w %d*%d sampling %d components %d\n", width, height, jpegSubsamp, COLOR_COMPONENTS); + // printMessage("turboJPEG h*w %d*%d sampling %d components %d\n", width, height, jpegSubsamp, COLOR_COMPONENTS); if ((jpegSubsamp == TJSAMP_GRAY) && (COLOR_COMPONENTS != 1)) { printError("Grayscale jpegs should not have %d components '%s'\n", COLOR_COMPONENTS, imgname); } if ((jpegSubsamp != TJSAMP_GRAY) && (COLOR_COMPONENTS != 3)) { printError("Color jpegs should not have %d components '%s'\n", COLOR_COMPONENTS, imgname); } - //unsigned char bImg[width*height*COLOR_COMPONENTS]; //!< will contain the decompressed image + // unsigned char bImg[width*height*COLOR_COMPONENTS]; //!< will contain the decompressed image unsigned char *bImg = (unsigned char *)malloc(width * height * COLOR_COMPONENTS); - if (COLOR_COMPONENTS == 1) //TJPF_GRAY + if (COLOR_COMPONENTS == 1) // TJPF_GRAY tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, bImg, width, 0 /*pitch*/, height, TJPF_GRAY, TJFLAG_FASTDCT); else tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, bImg, width, 0 /*pitch*/, height, TJPF_RGB, TJFLAG_FASTDCT); - //printMessage("turboJPEG h*w %d*%d (sampling %d)\n", width, height, jpegSubsamp); + // printMessage("turboJPEG h*w %d*%d (sampling %d)\n", width, height, jpegSubsamp); tjDestroy(_jpegDecompressor); return bImg; } -#else //if turboJPEG else use nanojpeg... +#else // if turboJPEG else use nanojpeg... -//unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) { +// unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) { unsigned char *nii_loadImgJPEG50(char *imgname, struct TDICOMdata dcm) { - //decode classic JPEG using nanoJPEG - //printMessage("50 offset %d\n", dcm.imageStart); + // decode classic JPEG using nanoJPEG + // printMessage("50 offset %d\n", dcm.imageStart); if (access(imgname, F_OK) == -1) { printError("Unable to find '%s'\n", imgname); return NULL; } - //load compressed data + // load compressed data FILE *f = fopen(imgname, "rb"); fseek(f, 0, SEEK_END); int size = (int)ftell(f); @@ -3496,7 +3503,7 @@ unsigned char *nii_loadImgJPEG50(char *imgname, struct TDICOMdata dcm) { fseek(f, dcm.imageStart, SEEK_SET); size = (int)fread(buf, 1, size, f); fclose(f); - //decode + // decode njInit(); if (njDecode(buf, size)) { printError("Unable to decode JPEG image.\n"); @@ -3504,14 +3511,14 @@ unsigned char *nii_loadImgJPEG50(char *imgname, struct TDICOMdata dcm) { } free(buf); unsigned char *bImg = (unsigned char *)malloc(njGetImageSize()); - memcpy(bImg, njGetImage(), njGetImageSize()); //dest, src, size + memcpy(bImg, njGetImage(), njGetImageSize()); // dest, src, size njDone(); return bImg; } #endif #endif -uint32_t rleInt(int lIndex, unsigned char lBuffer[], bool swap) { //read binary 32-bit integer +uint32_t rleInt(int lIndex, unsigned char lBuffer[], bool swap) { // read binary 32-bit integer uint32_t retVal = 0; memcpy(&retVal, (char *)&lBuffer[lIndex * 4], 4); if (!swap) @@ -3524,18 +3531,18 @@ uint32_t rleInt(int lIndex, unsigned char lBuffer[], bool swap) { //read binary outInt[2] = inInt[1]; outInt[3] = inInt[0]; return swapVal; -} //rleInt() +} // rleInt() unsigned char *nii_loadImgPMSCT_RLE1(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) { - //Transfer Syntax 1.3.46.670589.33.1.4.1 also handled by TomoVision and GDCM's rle2img - //https://github.com/malaterre/GDCM/blob/a923f206060e85e8d81add565ae1b9dd7b210481/Examples/Cxx/rle2img.cxx - //see rle2img: Philips/ELSCINT1 run-length compression 07a1,1011= PMSCT_RLE1 - if (dcm.imageBytes < 66) { //64 for header+ 2 byte minimum image + // Transfer Syntax 1.3.46.670589.33.1.4.1 also handled by TomoVision and GDCM's rle2img + // https://github.com/malaterre/GDCM/blob/a923f206060e85e8d81add565ae1b9dd7b210481/Examples/Cxx/rle2img.cxx + // see rle2img: Philips/ELSCINT1 run-length compression 07a1,1011= PMSCT_RLE1 + if (dcm.imageBytes < 66) { // 64 for header+ 2 byte minimum image printError("%d is not enough bytes for PMSCT_RLE1 compression '%s'\n", dcm.imageBytes, imgname); return NULL; } int bytesPerSample = dcm.samplesPerPixel * (dcm.bitsAllocated / 8); - if (bytesPerSample != 2) { //there is an RGB variation of this format, but we have not seen it in the wild + if (bytesPerSample != 2) { // there is an RGB variation of this format, but we have not seen it in the wild printError("PMSCT_RLE1 should be 16-bits per sample (please report on Github and use pmsct_rgb1).\n"); return NULL; } @@ -3553,7 +3560,7 @@ unsigned char *nii_loadImgPMSCT_RLE1(char *imgname, struct nifti_1_header hdr, s } fseek(file, (long)dcm.imageStart, SEEK_SET); size_t imgsz = nii_ImgBytes(hdr); - char *cImg = (char *)malloc(dcm.imageBytes); //compressed input + char *cImg = (char *)malloc(dcm.imageBytes); // compressed input size_t sz = fread(cImg, 1, dcm.imageBytes, file); fclose(file); if (sz < (size_t)dcm.imageBytes) { @@ -3564,9 +3571,9 @@ unsigned char *nii_loadImgPMSCT_RLE1(char *imgname, struct nifti_1_header hdr, s if ((int)imgsz == dcm.imageBytes) { // Handle special case that data is not compressed: return (unsigned char *)cImg; } - unsigned char *bImg = (unsigned char *)malloc(imgsz); //binary output + unsigned char *bImg = (unsigned char *)malloc(imgsz); // binary output // RLE pass: compressed -> temp (bImg -> tImg) - char *tImg = (char *)malloc(imgsz); //temp output + char *tImg = (char *)malloc(imgsz); // temp output size_t o = 0; for (size_t i = 0; (int)i < dcm.imageBytes; ++i) { if (cImg[i] == (char)0xa5) { @@ -3582,12 +3589,12 @@ unsigned char *nii_loadImgPMSCT_RLE1(char *imgname, struct nifti_1_header hdr, s tImg[o] = cImg[i]; o++; } - } //for i + } // for i free(cImg); size_t tempsize = o; - //Looks like this RLE is pretty ineffective... - // printMessage("RLE %d -> %d\n", dcm.imageBytes, o); - //Delta encoding pass: temp -> output (tImg -> bImg) + // Looks like this RLE is pretty ineffective... + // printMessage("RLE %d -> %d\n", dcm.imageBytes, o); + // Delta encoding pass: temp -> output (tImg -> bImg) unsigned short delta = 0; o = 0; size_t n16 = (int)imgsz >> 1; @@ -3609,15 +3616,15 @@ unsigned char *nii_loadImgPMSCT_RLE1(char *imgname, struct nifti_1_header hdr, s o++; delta = value; } - } //for i - //printMessage("Delta %d -> %d (of %d)\n", tempsize, 2*(o-1), imgsz); + } // for i + // printMessage("Delta %d -> %d (of %d)\n", tempsize, 2*(o-1), imgsz); free(tImg); return bImg; } // nii_loadImgPMSCT_RLE1() unsigned char *nii_loadImgRLE(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) { - //decompress PackBits run-length encoding https://en.wikipedia.org/wiki/PackBits - if (dcm.imageBytes < 66) { //64 for header+ 2 byte minimum image + // decompress PackBits run-length encoding https://en.wikipedia.org/wiki/PackBits + if (dcm.imageBytes < 66) { // 64 for header+ 2 byte minimum image printError("%d is not enough bytes for RLE compression '%s'\n", dcm.imageBytes, imgname); return NULL; } @@ -3635,7 +3642,7 @@ unsigned char *nii_loadImgRLE(char *imgname, struct nifti_1_header hdr, struct T } fseek(file, (long)dcm.imageStart, SEEK_SET); size_t imgsz = nii_ImgBytes(hdr); - unsigned char *cImg = (unsigned char *)malloc(dcm.imageBytes); //compressed input + unsigned char *cImg = (unsigned char *)malloc(dcm.imageBytes); // compressed input size_t sz = fread(cImg, 1, dcm.imageBytes, file); fclose(file); if (sz < (size_t)dcm.imageBytes) { @@ -3643,7 +3650,7 @@ unsigned char *nii_loadImgRLE(char *imgname, struct nifti_1_header hdr, struct T free(cImg); return NULL; } - //read header http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_G.3.html + // read header http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_G.3.html bool swap = (dcm.isLittleEndian != littleEndianPlatform()); int bytesPerSample = dcm.samplesPerPixel * (dcm.bitsAllocated / 8); uint32_t bytesPerSampleRLE = rleInt(0, cImg, swap); @@ -3652,7 +3659,7 @@ unsigned char *nii_loadImgRLE(char *imgname, struct nifti_1_header hdr, struct T free(cImg); return NULL; } - unsigned char *bImg = (unsigned char *)malloc(imgsz); //binary output + unsigned char *bImg = (unsigned char *)malloc(imgsz); // binary output for (size_t i = 0; i < imgsz; i++) bImg[i] = 0; for (int i = 0; i < bytesPerSample; i++) { @@ -3663,66 +3670,66 @@ unsigned char *nii_loadImgRLE(char *imgname, struct nifti_1_header hdr, struct T free(bImg); return NULL; } - //save in platform's endian: - // The first Segment is generated by stripping off the most significant byte of each Padded Composite Pixel Code... + // save in platform's endian: + // The first Segment is generated by stripping off the most significant byte of each Padded Composite Pixel Code... size_t vx = i; - if ((dcm.samplesPerPixel == 1) && (littleEndianPlatform())) //endian, except for RGB + if ((dcm.samplesPerPixel == 1) && (littleEndianPlatform())) // endian, except for RGB vx = (bytesPerSample - 1) - i; while (vx < imgsz) { int8_t n = (int8_t)cImg[offset]; offset++; - //http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_G.3.html - //if ((n >= 0) && (n <= 127)) { //not needed: int8_t always <=127 - if (n >= 0) { //literal bytes + // http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_G.3.html + // if ((n >= 0) && (n <= 127)) { //not needed: int8_t always <=127 + if (n >= 0) { // literal bytes int reps = 1 + (int)n; for (int r = 0; r < reps; r++) { int8_t v = cImg[offset]; offset++; if (vx >= imgsz) - ; //printMessage("literal overflow %d %d\n", r, reps); + ; // printMessage("literal overflow %d %d\n", r, reps); else bImg[vx] = v; vx = vx + bytesPerSample; } - } else if ((n <= -1) && (n >= -127)) { //repeated run + } else if ((n <= -1) && (n >= -127)) { // repeated run int8_t v = cImg[offset]; offset++; int reps = -(int)n + 1; for (int r = 0; r < reps; r++) { if (vx >= imgsz) - ; //printMessage("repeat overflow %d\n", reps); + ; // printMessage("repeat overflow %d\n", reps); else bImg[vx] = v; vx = vx + bytesPerSample; } - }; //n.b. we ignore -128! - } //while vx < imgsz - } //for i < bytesPerSample + }; // n.b. we ignore -128! + } // while vx < imgsz + } // for i < bytesPerSample free(cImg); return bImg; } // nii_loadImgRLE() #ifdef myDisableOpenJPEG #ifndef myEnableJasper -//avoid compiler warning, see https://stackoverflow.com/questions/3599160/unused-parameter-warnings-in-c +// avoid compiler warning, see https://stackoverflow.com/questions/3599160/unused-parameter-warnings-in-c #define UNUSED(x) (void)(x) #endif #endif -#if defined(myEnableJPEGLS) || defined(myEnableJPEGLS1) //Support for JPEG-LS -//JPEG-LS: Transfer Syntaxes 1.2.840.10008.1.2.4.80 1.2.840.10008.1.2.4.81 +#if defined(myEnableJPEGLS) || defined(myEnableJPEGLS1) // Support for JPEG-LS +// JPEG-LS: Transfer Syntaxes 1.2.840.10008.1.2.4.80 1.2.840.10008.1.2.4.81 -#ifdef myEnableJPEGLS1 //use CharLS v1.* requires c++03 +#ifdef myEnableJPEGLS1 // use CharLS v1.* requires c++03 //-std=c++03 -DmyEnableJPEGLS1 charls1/header.cpp charls1/jpegls.cpp charls1/jpegmarkersegment.cpp charls1/interface.cpp charls1/jpegstreamwriter.cpp #include "charls1/interface.h" -#else //use latest release of CharLS: CharLS 2.x requires c++14 +#else // use latest release of CharLS: CharLS 2.x requires c++14 //-std=c++14 -DmyEnableJPEGLS charls/jpegls.cpp charls/jpegmarkersegment.cpp charls/interface.cpp charls/jpegstreamwriter.cpp charls/jpegstreamreader.cpp #include "charls/charls.h" #endif #include "charls/publictypes.h" unsigned char *nii_loadImgJPEGLS(char *imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) { - //load compressed data + // load compressed data FILE *file = fopen(imgname, "rb"); if (!file) { printError("Unable to open %s\n", imgname); @@ -3736,7 +3743,7 @@ unsigned char *nii_loadImgJPEGLS(char *imgname, struct nifti_1_header hdr, struc return NULL; } fseek(file, (long)dcm.imageStart, SEEK_SET); - unsigned char *cImg = (unsigned char *)malloc(dcm.imageBytes); //compressed input + unsigned char *cImg = (unsigned char *)malloc(dcm.imageBytes); // compressed input size_t sz = fread(cImg, 1, dcm.imageBytes, file); fclose(file); if (sz < (size_t)dcm.imageBytes) { @@ -3744,9 +3751,9 @@ unsigned char *nii_loadImgJPEGLS(char *imgname, struct nifti_1_header hdr, struc free(cImg); return NULL; } - //create buffer for uncompressed data + // create buffer for uncompressed data size_t imgsz = nii_ImgBytes(hdr); - unsigned char *bImg = (unsigned char *)malloc(imgsz); //binary output + unsigned char *bImg = (unsigned char *)malloc(imgsz); // binary output JlsParameters params = {}; #ifdef myEnableJPEGLS1 if (JpegLsReadHeader(cImg, dcm.imageBytes, ¶ms) != OK) { @@ -3771,25 +3778,25 @@ unsigned char *nii_loadImgJPEGLS(char *imgname, struct nifti_1_header hdr, struc #endif unsigned char *nii_loadImgXL(char *imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries, int compressFlag, int isVerbose, struct TDTI4D *dti4D) { - //provided with a filename (imgname) and DICOM header (dcm), creates NIfTI header (hdr) and img + // provided with a filename (imgname) and DICOM header (dcm), creates NIfTI header (hdr) and img if (headerDcm2Nii(dcm, hdr, true) == EXIT_FAILURE) - return NULL; //TOFU + return NULL; // TOFU unsigned char *img; if (dcm.compressionScheme == kCompress50) { #ifdef myDisableClassicJPEG printMessage("Software not compiled to decompress classic JPEG DICOM images\n"); return NULL; #else - //img = nii_loadImgJPEG50(imgname, *hdr, dcm); + // img = nii_loadImgJPEG50(imgname, *hdr, dcm); img = nii_loadImgJPEG50(imgname, dcm); - if (hdr->datatype == DT_RGB24) //convert to planar - img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); //do this BEFORE Y-Flip, or RGB order can be flipped + if (hdr->datatype == DT_RGB24) // convert to planar + img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); // do this BEFORE Y-Flip, or RGB order can be flipped #endif } else if (dcm.compressionScheme == kCompressJPEGLS) { #if defined(myEnableJPEGLS) || defined(myEnableJPEGLS1) img = nii_loadImgJPEGLS(imgname, *hdr, dcm); - if (hdr->datatype == DT_RGB24) //convert to planar - img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); //do this BEFORE Y-Flip, or RGB order can be flipped + if (hdr->datatype == DT_RGB24) // convert to planar + img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); // do this BEFORE Y-Flip, or RGB order can be flipped #else printMessage("Software not compiled to decompress JPEG-LS DICOM images\n"); return NULL; @@ -3798,8 +3805,8 @@ unsigned char *nii_loadImgXL(char *imgname, struct nifti_1_header *hdr, struct T img = nii_loadImgPMSCT_RLE1(imgname, *hdr, dcm); } else if (dcm.compressionScheme == kCompressRLE) { img = nii_loadImgRLE(imgname, *hdr, dcm); - if (hdr->datatype == DT_RGB24) //convert to planar - img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); //do this BEFORE Y-Flip, or RGB order can be flipped + if (hdr->datatype == DT_RGB24) // convert to planar + img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); // do this BEFORE Y-Flip, or RGB order can be flipped } else if (dcm.compressionScheme == kCompressC3) img = nii_loadImgJPEGC3(imgname, *hdr, dcm, isVerbose); else @@ -3824,12 +3831,12 @@ unsigned char *nii_loadImgXL(char *imgname, struct nifti_1_header *hdr, struct T if ((dcm.compressionScheme == kCompressNone) && (dcm.isLittleEndian != littleEndianPlatform()) && (hdr->bitpix > 8)) img = nii_byteswap(img, hdr); if ((dcm.compressionScheme == kCompressNone) && (hdr->datatype == DT_RGB24)) - img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); //do this BEFORE Y-Flip, or RGB order can be flipped + img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); // do this BEFORE Y-Flip, or RGB order can be flipped dcm.isPlanarRGB = true; if (dcm.CSA.mosaicSlices > 1) { img = nii_demosaic(img, hdr, dcm.CSA.mosaicSlices, (dcm.manufacturer == kMANUFACTURER_UIH)); //, dcm.CSA.protocolSliceNumber1); } - if ((dti4D == NULL) && (!dcm.isFloat) && (iVaries)) //must do afte + if ((dti4D == NULL) && (!dcm.isFloat) && (iVaries)) // must do afte img = nii_iVaries(img, hdr, NULL); int nAcq = dcm.locationsInAcquisition; if ((nAcq > 1) && (hdr->dim[0] < 4) && ((hdr->dim[3] % nAcq) == 0) && (hdr->dim[3] > nAcq)) { @@ -3843,26 +3850,26 @@ unsigned char *nii_loadImgXL(char *imgname, struct nifti_1_header *hdr, struct T img = nii_iVaries(img, hdr, dti4D); headerDcm2NiiSForm(dcm, dcm, hdr, false); return img; -} //nii_loadImgXL() +} // nii_loadImgXL() -int isSQ(uint32_t groupElement, bool isPhilips) { //Detect sequence VR ("SQ") for implicit tags +int isSQ(uint32_t groupElement, bool isPhilips) { // Detect sequence VR ("SQ") for implicit tags static const int array_size = 35; uint32_t array[array_size] = {0x0008 + (uint32_t(0x1111) << 16), 0x0008 + (uint32_t(0x1115) << 16), 0x0008 + (uint32_t(0x1140) << 16), 0x0008 + (uint32_t(0x1199) << 16), 0x0008 + (uint32_t(0x2218) << 16), 0x0008 + (uint32_t(0x9092) << 16), 0x0018 + (uint32_t(0x9006) << 16), 0x0018 + (uint32_t(0x9042) << 16), 0x0018 + (uint32_t(0x9045) << 16), 0x0018 + (uint32_t(0x9049) << 16), 0x0018 + (uint32_t(0x9112) << 16), 0x0018 + (uint32_t(0x9114) << 16), 0x0018 + (uint32_t(0x9115) << 16), 0x0018 + (uint32_t(0x9117) << 16), 0x0018 + (uint32_t(0x9119) << 16), 0x0018 + (uint32_t(0x9125) << 16), 0x0018 + (uint32_t(0x9152) << 16), 0x0018 + (uint32_t(0x9176) << 16), 0x0018 + (uint32_t(0x9226) << 16), 0x0018 + (uint32_t(0x9239) << 16), 0x0020 + (uint32_t(0x9071) << 16), 0x0020 + (uint32_t(0x9111) << 16), 0x0020 + (uint32_t(0x9113) << 16), 0x0020 + (uint32_t(0x9116) << 16), 0x0020 + (uint32_t(0x9221) << 16), 0x0020 + (uint32_t(0x9222) << 16), 0x0028 + (uint32_t(0x9110) << 16), 0x0028 + (uint32_t(0x9132) << 16), 0x0028 + (uint32_t(0x9145) << 16), 0x0040 + (uint32_t(0x0260) << 16), 0x0040 + (uint32_t(0x0555) << 16), 0x0040 + (uint32_t(0xa170) << 16), 0x5200 + (uint32_t(0x9229) << 16), 0x5200 + (uint32_t(0x9230) << 16)}; - //uint32_t array[array_size] = {0x2005 + (uint32_t(0x140F) << 16), 0x0008 + (uint32_t(0x1111) << 16), 0x0008 + (uint32_t(0x1115) << 16), 0x0008 + (uint32_t(0x1140) << 16), 0x0008 + (uint32_t(0x1199) << 16), 0x0008 + (uint32_t(0x2218) << 16), 0x0008 + (uint32_t(0x9092) << 16), 0x0018 + (uint32_t(0x9006) << 16), 0x0018 + (uint32_t(0x9042) << 16), 0x0018 + (uint32_t(0x9045) << 16), 0x0018 + (uint32_t(0x9049) << 16), 0x0018 + (uint32_t(0x9112) << 16), 0x0018 + (uint32_t(0x9114) << 16), 0x0018 + (uint32_t(0x9115) << 16), 0x0018 + (uint32_t(0x9117) << 16), 0x0018 + (uint32_t(0x9119) << 16), 0x0018 + (uint32_t(0x9125) << 16), 0x0018 + (uint32_t(0x9152) << 16), 0x0018 + (uint32_t(0x9176) << 16), 0x0018 + (uint32_t(0x9226) << 16), 0x0018 + (uint32_t(0x9239) << 16), 0x0020 + (uint32_t(0x9071) << 16), 0x0020 + (uint32_t(0x9111) << 16), 0x0020 + (uint32_t(0x9113) << 16), 0x0020 + (uint32_t(0x9116) << 16), 0x0020 + (uint32_t(0x9221) << 16), 0x0020 + (uint32_t(0x9222) << 16), 0x0028 + (uint32_t(0x9110) << 16), 0x0028 + (uint32_t(0x9132) << 16), 0x0028 + (uint32_t(0x9145) << 16), 0x0040 + (uint32_t(0x0260) << 16), 0x0040 + (uint32_t(0x0555) << 16), 0x0040 + (uint32_t(0xa170) << 16), 0x5200 + (uint32_t(0x9229) << 16), 0x5200 + (uint32_t(0x9230) << 16)}; + // uint32_t array[array_size] = {0x2005 + (uint32_t(0x140F) << 16), 0x0008 + (uint32_t(0x1111) << 16), 0x0008 + (uint32_t(0x1115) << 16), 0x0008 + (uint32_t(0x1140) << 16), 0x0008 + (uint32_t(0x1199) << 16), 0x0008 + (uint32_t(0x2218) << 16), 0x0008 + (uint32_t(0x9092) << 16), 0x0018 + (uint32_t(0x9006) << 16), 0x0018 + (uint32_t(0x9042) << 16), 0x0018 + (uint32_t(0x9045) << 16), 0x0018 + (uint32_t(0x9049) << 16), 0x0018 + (uint32_t(0x9112) << 16), 0x0018 + (uint32_t(0x9114) << 16), 0x0018 + (uint32_t(0x9115) << 16), 0x0018 + (uint32_t(0x9117) << 16), 0x0018 + (uint32_t(0x9119) << 16), 0x0018 + (uint32_t(0x9125) << 16), 0x0018 + (uint32_t(0x9152) << 16), 0x0018 + (uint32_t(0x9176) << 16), 0x0018 + (uint32_t(0x9226) << 16), 0x0018 + (uint32_t(0x9239) << 16), 0x0020 + (uint32_t(0x9071) << 16), 0x0020 + (uint32_t(0x9111) << 16), 0x0020 + (uint32_t(0x9113) << 16), 0x0020 + (uint32_t(0x9116) << 16), 0x0020 + (uint32_t(0x9221) << 16), 0x0020 + (uint32_t(0x9222) << 16), 0x0028 + (uint32_t(0x9110) << 16), 0x0028 + (uint32_t(0x9132) << 16), 0x0028 + (uint32_t(0x9145) << 16), 0x0040 + (uint32_t(0x0260) << 16), 0x0040 + (uint32_t(0x0555) << 16), 0x0040 + (uint32_t(0xa170) << 16), 0x5200 + (uint32_t(0x9229) << 16), 0x5200 + (uint32_t(0x9230) << 16)}; for (int i = 0; i < array_size; i++) { - //if (array[i] == groupElement) printMessage(" implicitSQ %04x,%04x\n", groupElement & 65535,groupElement>>16); + // if (array[i] == groupElement) printMessage(" implicitSQ %04x,%04x\n", groupElement & 65535,groupElement>>16); if (array[i] == groupElement) return 1; } - //issue 775: Canon/Toshiba use 2005,140f but not as a SQ? + // issue 775: Canon/Toshiba use 2005,140f but not as a SQ? uint32_t kPhilipsSQ = 0x2005 + (uint32_t(0x140F) << 16); if ((isPhilips) && (kPhilipsSQ == groupElement)) return 1; return 0; -} //isSQ() +} // isSQ() -int isDICOMfile(const char *fname) { //0=NotDICOM, 1=DICOM, 2=Maybe(not Part 10 compliant) - //Someday: it might be worthwhile to detect "IMGF" at offset 3228 to warn user if they attempt to convert Signa data +int isDICOMfile(const char *fname) { // 0=NotDICOM, 1=DICOM, 2=Maybe(not Part 10 compliant) + // Someday: it might be worthwhile to detect "IMGF" at offset 3228 to warn user if they attempt to convert Signa data FILE *fp = fopen(fname, "rb"); if (!fp) return 0; @@ -3879,22 +3886,22 @@ int isDICOMfile(const char *fname) { //0=NotDICOM, 1=DICOM, 2=Maybe(not Part 10 if (sz < 256) return 0; if ((buffer[128] == 'D') && (buffer[129] == 'I') && (buffer[130] == 'C') && (buffer[131] == 'M')) - return 1; //valid DICOM + return 1; // valid DICOM if ((buffer[0] == 8) && (buffer[1] == 0) && (buffer[3] == 0)) - return 2; //not valid Part 10 file, perhaps DICOM object + return 2; // not valid Part 10 file, perhaps DICOM object return 0; -} //isDICOMfile() +} // isDICOMfile() -//START RIR 12/2017 Robert I. Reid +// START RIR 12/2017 Robert I. Reid // Gathering spot for all the info needed to get the b value and direction // for a volume. struct TVolumeDiffusion { struct TDICOMdata *pdd; // The multivolume struct TDTI4D *pdti4D; // permanent records. - uint8_t manufacturer; // kMANUFACTURER_UNKNOWN, kMANUFACTURER_SIEMENS, etc. + uint8_t manufacturer; // kMANUFACTURER_UNKNOWN, kMANUFACTURER_SIEMENS, etc. - //void set_manufacturer(const uint8_t m) {manufacturer = m; update();} // unnecessary + // void set_manufacturer(const uint8_t m) {manufacturer = m; update();} // unnecessary // Everything after this in the structure would be private if it were a C++ // class, but it has been rewritten as a struct for C compatibility. I am @@ -3904,23 +3911,23 @@ struct TVolumeDiffusion { bool _isAtFirstPatientPosition; // Limit b vals and vecs to 1 per volume. - //float bVal0018_9087; // kDiffusion_b_value, always present in Philips/Siemens. - //float bVal2001_1003; // kDiffusionBFactor - // float dirRL2005_10b0; // kDiffusionDirectionRL - // float dirAP2005_10b1; // kDiffusionDirectionAP - // float dirFH2005_10b2; // kDiffusionDirectionFH - // Philips diffusion scans tend to have a "trace" (average of the diffusion - // weighted volumes) volume tacked on, usually but not always at the end, - // so b is > 0, but the direction is meaningless. Most software versions - // explicitly set the direction to 0, but version 3 does not, making (0x18, - // 0x9075) necessary. + // float bVal0018_9087; // kDiffusion_b_value, always present in Philips/Siemens. + // float bVal2001_1003; // kDiffusionBFactor + // float dirRL2005_10b0; // kDiffusionDirectionRL + // float dirAP2005_10b1; // kDiffusionDirectionAP + // float dirFH2005_10b2; // kDiffusionDirectionFH + // Philips diffusion scans tend to have a "trace" (average of the diffusion + // weighted volumes) volume tacked on, usually but not always at the end, + // so b is > 0, but the direction is meaningless. Most software versions + // explicitly set the direction to 0, but version 3 does not, making (0x18, + // 0x9075) necessary. bool _isPhilipsNonDirectional; - //char _directionality0018_9075[16]; // DiffusionDirectionality, not in Philips 2.6. - // float _orientation0018_9089[3]; // kDiffusionOrientation, always present in Philips/Siemens for volumes with a direction. - //char _seq0018_9117[64]; // MRDiffusionSequence, not in Philips 2.6. + // char _directionality0018_9075[16]; // DiffusionDirectionality, not in Philips 2.6. + // float _orientation0018_9089[3]; // kDiffusionOrientation, always present in Philips/Siemens for volumes with a direction. + // char _seq0018_9117[64]; // MRDiffusionSequence, not in Philips 2.6. float _dtiV[4]; double _symBMatrix[6]; - //uint16_t numDti; + // uint16_t numDti; }; struct TVolumeDiffusion initTVolumeDiffusion(struct TDICOMdata *ptdd, struct TDTI4D *dti4D); void clear_volume(struct TVolumeDiffusion *ptvd); // Blank the volume-specific members or set them to impossible values. @@ -3940,33 +3947,33 @@ struct TVolumeDiffusion initTVolumeDiffusion(struct TDICOMdata *ptdd, struct TDT tvd.pdti4D = dti4D; clear_volume(&tvd); return tvd; -} //initTVolumeDiffusion() +} // initTVolumeDiffusion() void clear_volume(struct TVolumeDiffusion *ptvd) { ptvd->_isAtFirstPatientPosition = false; ptvd->manufacturer = kMANUFACTURER_UNKNOWN; - //bVal0018_9087 = -1; - //ptvd->_directionality0018_9075[0] = 0; - //ptvd->seq0018_9117[0] = 0; - //bVal2001_1003 = -1; - // dirRL2005_10b0 = 2; - // dirAP2005_10b1 = 2; - // dirFH2005_10b2 = 2; + // bVal0018_9087 = -1; + // ptvd->_directionality0018_9075[0] = 0; + // ptvd->seq0018_9117[0] = 0; + // bVal2001_1003 = -1; + // dirRL2005_10b0 = 2; + // dirAP2005_10b1 = 2; + // dirFH2005_10b2 = 2; ptvd->_isPhilipsNonDirectional = false; ptvd->_dtiV[0] = -1; for (int i = 1; i < 4; ++i) ptvd->_dtiV[i] = 2; for (int i = 1; i < 6; ++i) ptvd->_symBMatrix[i] = NAN; - //numDti = 0; -} //clear_volume() + // numDti = 0; +} // clear_volume() void set_directionality0018_9075(struct TVolumeDiffusion *ptvd, unsigned char *inbuf) { - //if(!strncmp(( char*)(inbuf), "BMATRIX", 4)) printf("FOUND BMATRIX----%s\n",inbuf ); - //n.b. strncmp returns 0 if the contents of both strings are equal, for boolean 0 = false! - // elsewhere we use strstr() which returns 0/null if match is not present + // if(!strncmp(( char*)(inbuf), "BMATRIX", 4)) printf("FOUND BMATRIX----%s\n",inbuf ); + // n.b. strncmp returns 0 if the contents of both strings are equal, for boolean 0 = false! + // elsewhere we use strstr() which returns 0/null if match is not present if (strncmp((char *)(inbuf), "DIRECTIONAL", 11) && // strncmp = 0 for ==. - //strncmp(( char*)(inbuf), "NONE", 4) && //issue 256 + // strncmp(( char*)(inbuf), "NONE", 4) && //issue 256 strncmp((char *)(inbuf), "BMATRIX", 7)) { // Siemens XA10 ptvd->_isPhilipsNonDirectional = true; // Explicitly set the direction to 0 now, because there may @@ -3978,46 +3985,46 @@ void set_directionality0018_9075(struct TVolumeDiffusion *ptvd, unsigned char *i // Wait for 0018,9089 to get the direction. } _update_tvd(ptvd); -} //set_directionality0018_9075() +} // set_directionality0018_9075() int set_bValGE(struct TVolumeDiffusion *ptvd, int lLength, unsigned char *inbuf) { - //see Series 16 https://github.com/nikadon/cc-dcm2bids-wrapper/tree/master/dicom-qa-examples/ge-mr750-dwi-b-vals#table b750 = 1000000750\8\0\0 b1500 = 1000001500\8\0\0 + // see Series 16 https://github.com/nikadon/cc-dcm2bids-wrapper/tree/master/dicom-qa-examples/ge-mr750-dwi-b-vals#table b750 = 1000000750\8\0\0 b1500 = 1000001500\8\0\0 int bVal = dcmStrInt(lLength, inbuf); // GE bias value for b-value https://github.com/rordenlab/dcm2niix/issues/602 bVal = (bVal % 1000000000); ptvd->_dtiV[0] = bVal; - //printf("(0043,1039) '%s' Slop_int_6 -->%d \n", inbuf, bVal); - //dd.CSA.numDti = 1; // Always true for GE. + // printf("(0043,1039) '%s' Slop_int_6 -->%d \n", inbuf, bVal); + // dd.CSA.numDti = 1; // Always true for GE. _update_tvd(ptvd); return bVal; -} //set_bValGE() +} // set_bValGE() // axis: 0 -> x, 1 -> y , 2 -> z void set_diffusion_directionPhilips(struct TVolumeDiffusion *ptvd, float vec, const int axis) { ptvd->_dtiV[axis + 1] = vec; - //printf("(2005,10b0..2) v[%d]=%g\n", axis, ptvd->_dtiV[axis + 1]); + // printf("(2005,10b0..2) v[%d]=%g\n", axis, ptvd->_dtiV[axis + 1]); _update_tvd(ptvd); -} //set_diffusion_directionPhilips() +} // set_diffusion_directionPhilips() // axis: 0 -> x, 1 -> y , 2 -> z void set_diffusion_directionGE(struct TVolumeDiffusion *ptvd, int lLength, unsigned char *inbuf, const int axis) { ptvd->_dtiV[axis + 1] = dcmStrFloat(lLength, inbuf); - //printf("(0019,10bb..d) v[%d]=%g\n", axis, ptvd->_dtiV[axis + 1]); + // printf("(0019,10bb..d) v[%d]=%g\n", axis, ptvd->_dtiV[axis + 1]); _update_tvd(ptvd); -} //set_diffusion_directionGE() +} // set_diffusion_directionGE() void dcmMultiFloatDouble(size_t lByteLength, unsigned char lBuffer[], size_t lnFloats, float *lFloats, bool isLittleEndian) { size_t floatlen = lByteLength / lnFloats; for (size_t i = 0; i < lnFloats; ++i) lFloats[i] = dcmFloatDouble((int)floatlen, lBuffer + i * floatlen, isLittleEndian); -} //dcmMultiFloatDouble() +} // dcmMultiFloatDouble() void dcmMultiFloatSingle(size_t lByteLength, unsigned char lBuffer[], size_t lnFloats, float *lFloats, bool isLittleEndian) { -// dcmFloat kTRArray + // dcmFloat kTRArray size_t floatlen = lByteLength / lnFloats; for (size_t i = 0; i < lnFloats; ++i) lFloats[i] = dcmFloat((int)floatlen, lBuffer + i * floatlen, isLittleEndian); -} //dcmMultiFloatSingle() +} // dcmMultiFloatSingle() void set_orientation0018_9089(struct TVolumeDiffusion *ptvd, int lLength, unsigned char *inbuf, bool isLittleEndian) { if (ptvd->_isPhilipsNonDirectional) { @@ -4026,12 +4033,12 @@ void set_orientation0018_9089(struct TVolumeDiffusion *ptvd, int lLength, unsign } else dcmMultiFloatDouble(lLength, inbuf, 3, ptvd->_dtiV + 1, isLittleEndian); _update_tvd(ptvd); -} //set_orientation0018_9089() +} // set_orientation0018_9089() void set_bVal(struct TVolumeDiffusion *ptvd, const float b) { ptvd->_dtiV[0] = b; _update_tvd(ptvd); -} //set_bVal() +} // set_bVal() void set_bMatrix(struct TVolumeDiffusion *ptvd, double b, int idx) { if ((idx < 0) || (idx > 5)) @@ -4043,7 +4050,7 @@ void set_bMatrix(struct TVolumeDiffusion *ptvd, double b, int idx) { void set_isAtFirstPatientPosition_tvd(struct TVolumeDiffusion *ptvd, const bool iafpp) { ptvd->_isAtFirstPatientPosition = iafpp; _update_tvd(ptvd); -} //set_isAtFirstPatientPosition_tvd() +} // set_isAtFirstPatientPosition_tvd() // Update the diffusion info in dd and *pdti4D for a volume once all the // diffusion info for that volume has been read into pvd. @@ -4067,15 +4074,15 @@ void _update_tvd(struct TVolumeDiffusion *ptvd) { // dtiV[0] = 0; // Implied 0. bool isReady = (ptvd->_isAtFirstPatientPosition && (ptvd->_dtiV[0] >= 0)); if (!isReady) - return; //no B=0 + return; // no B=0 if (isReady) { for (int i = 1; i < 4; ++i) { - // Check that _dtiV is not still at its default of [-1, 2, 2, 2] from - // clear_volume(struct TVolumeDiffusion *ptvd). This is mostly avoided - // because of the ptvd->_dtiV[0] >= 0 check above, but was supposedly - // needed at some point. - // issue 769: some bvec components may be slightly (~5%) > 1. AFAIK, - // the relevant value to guard against would be +2. + // Check that _dtiV is not still at its default of [-1, 2, 2, 2] from + // clear_volume(struct TVolumeDiffusion *ptvd). This is mostly avoided + // because of the ptvd->_dtiV[0] >= 0 check above, but was supposedly + // needed at some point. + // issue 769: some bvec components may be slightly (~5%) > 1. AFAIK, + // the relevant value to guard against would be +2. if (ptvd->_dtiV[i] > 1.5) { isReady = false; break; @@ -4083,12 +4090,11 @@ void _update_tvd(struct TVolumeDiffusion *ptvd) { } } if ((!isReady) && (ptvd->_dtiV[0] < 100.0) && (!isnan(ptvd->_symBMatrix[0]))) { - //issue265: Bruker omits 0018,9089 for low b-values though it includes a bmatrix + // issue265: Bruker omits 0018,9089 for low b-values though it includes a bmatrix ptvd->_dtiV[1] = 0.0; ptvd->_dtiV[2] = 0.0; ptvd->_dtiV[3] = 0.0; isReady = true; - } /*if (!isReady) { //bvecs NOT filled: see if symBMatrix filled isReady = true; @@ -4144,22 +4150,22 @@ void _update_tvd(struct TVolumeDiffusion *ptvd) { return; // If still here, update dd and *pdti4D. ptvd->pdd->CSA.numDti++; - if (ptvd->pdd->CSA.numDti == 2) { // First time we know that this is a 4D DTI dataset; - for (int i = 0; i < 4; ++i) // Start *pdti4D before ptvd->pdd->CSA.dtiV + if (ptvd->pdd->CSA.numDti == 2) { // First time we know that this is a 4D DTI dataset; + for (int i = 0; i < 4; ++i) // Start *pdti4D before ptvd->pdd->CSA.dtiV ptvd->pdti4D->S[0].V[i] = ptvd->pdd->CSA.dtiV[i]; // is updated. } for (int i = 0; i < 4; ++i) // Update pdd ptvd->pdd->CSA.dtiV[i] = ptvd->_dtiV[i]; if ((ptvd->pdd->CSA.numDti > 1) && (ptvd->pdd->CSA.numDti < kMaxDTI4D)) { // Update *pdti4D - //d.dti4D = (TDTI *)malloc(kMaxDTI4D * sizeof(TDTI)); + // d.dti4D = (TDTI *)malloc(kMaxDTI4D * sizeof(TDTI)); for (int i = 0; i < 4; ++i) ptvd->pdti4D->S[ptvd->pdd->CSA.numDti - 1].V[i] = ptvd->_dtiV[i]; } clear_volume(ptvd); // clear the slate for the next volume. } //_update_tvd() -//END RIR +// END RIR -struct TDCMdim { //DimensionIndexValues +struct TDCMdim { // DimensionIndexValues uint32_t dimIdx[MAX_NUMBER_OF_DIMENSIONS]; uint32_t diskPos; float triggerDelayTime, TE, TR, intenScale, intenIntercept, intenScalePhilips, RWVScale, RWVIntercept; @@ -4169,15 +4175,15 @@ struct TDCMdim { //DimensionIndexValues bool isImaginary; }; -void getFileNameX(char *pathParent, const char *path, int maxLen) { //if path is c:\d1\d2 then filename is 'd2' - const char *filename = strrchr(path, '/'); //UNIX - const char *filenamew = strrchr(path, '\\'); //Windows +void getFileNameX(char *pathParent, const char *path, int maxLen) { // if path is c:\d1\d2 then filename is 'd2' + const char *filename = strrchr(path, '/'); // UNIX + const char *filenamew = strrchr(path, '\\'); // Windows if (filename == NULL) filename = filenamew; - //if ((filename != NULL) && (filenamew != NULL)) filename = std::max(filename, filenamew); + // if ((filename != NULL) && (filenamew != NULL)) filename = std::max(filename, filenamew); if ((filename != NULL) && (filenamew != NULL) && (filenamew > filename)) - filename = filenamew; //for mixed file separators, e.g. "C:/dir\filenane.tmp" - if (filename == NULL) { //no path separator + filename = filenamew; // for mixed file separators, e.g. "C:/dir\filenane.tmp" + if (filename == NULL) { // no path separator strcpy(pathParent, path); return; } @@ -4185,7 +4191,7 @@ void getFileNameX(char *pathParent, const char *path, int maxLen) { //if path is strncpy(pathParent, filename, maxLen - 1); } -void getFileName(char *pathParent, const char *path) { //if path is c:\d1\d2 then filename is 'd2' +void getFileName(char *pathParent, const char *path) { // if path is c:\d1\d2 then filename is 'd2' getFileNameX(pathParent, path, kDICOMStr); } @@ -4216,7 +4222,7 @@ bool compareTDCMdim(const TDCMdim &dcm1, const TDCMdim &dcm2) { return false; } return false; -} //compareTDCMdim() +} // compareTDCMdim() bool compareTDCMdimRev(const TDCMdim &dcm1, const TDCMdim &dcm2) { for (int i = 0; i < MAX_NUMBER_OF_DIMENSIONS; i++) { @@ -4226,14 +4232,14 @@ bool compareTDCMdimRev(const TDCMdim &dcm1, const TDCMdim &dcm2) { return false; } return false; -} //compareTDCMdimRev() +} // compareTDCMdimRev() #else int compareTDCMdim(void const *item1, void const *item2) { struct TDCMdim const *dcm1 = (const struct TDCMdim *)item1; struct TDCMdim const *dcm2 = (const struct TDCMdim *)item2; - //for (int i = 0; i < MAX_NUMBER_OF_DIMENSIONS; i++) { + // for (int i = 0; i < MAX_NUMBER_OF_DIMENSIONS; i++) { for (int i = MAX_NUMBER_OF_DIMENSIONS - 1; i >= 0; i--) { if (dcm1->dimIdx[i] < dcm2->dimIdx[i]) return -1; @@ -4241,7 +4247,7 @@ int compareTDCMdim(void const *item1, void const *item2) { return 1; } return 0; -} //compareTDCMdim() +} // compareTDCMdim() int compareTDCMdimRev(void const *item1, void const *item2) { struct TDCMdim const *dcm1 = (const struct TDCMdim *)item1; @@ -4253,27 +4259,27 @@ int compareTDCMdimRev(void const *item1, void const *item2) { return 1; } return 0; -} //compareTDCMdimRev() +} // compareTDCMdimRev() #endif // USING_R struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D *dti4D) { - //struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, struct TDTI4D *dti4D) { + // struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, struct TDTI4D *dti4D) { int isVerbose = prefs->isVerbose; int compressFlag = prefs->compressFlag; struct TDICOMdata d = clear_dicom_data(); - d.imageNum = 0; //not set - strcpy(d.protocolName, ""); //erase dummy with empty - strcpy(d.protocolName, ""); //erase dummy with empty - strcpy(d.seriesDescription, ""); //erase dummy with empty - strcpy(d.sequenceName, ""); //erase dummy with empty - //do not read folders - code specific to GCC (LLVM/Clang seems to recognize a small file size) + d.imageNum = 0; // not set + strcpy(d.protocolName, ""); // erase dummy with empty + strcpy(d.protocolName, ""); // erase dummy with empty + strcpy(d.seriesDescription, ""); // erase dummy with empty + strcpy(d.sequenceName, ""); // erase dummy with empty + // do not read folders - code specific to GCC (LLVM/Clang seems to recognize a small file size) dti4D->sliceOrder[0] = -1; dti4D->volumeOnsetTime[0] = -1; dti4D->decayFactor[0] = -1; dti4D->frameDuration[0] = -1; dti4D->frameReferenceTime[0] = -1; - //dti4D->fragmentOffset[0] = -1; + // dti4D->fragmentOffset[0] = -1; dti4D->intenScale[0] = 0.0; struct TVolumeDiffusion volDiffusion = initTVolumeDiffusion(&d, dti4D); struct stat s; @@ -4296,45 +4302,45 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D printMessage("Unable to open file %s\n", fname); return d; } - #ifdef _MSC_VER +#ifdef _MSC_VER _fseeki64(file, 0, SEEK_END); size_t fileLen = _ftelli64(file); - #else - fseeko(file, 0, SEEK_END); //Windows _fseeki64 - size_t fileLen = ftello(file); //Windows _ftelli64 - #endif +#else + fseeko(file, 0, SEEK_END); // Windows _fseeki64 + size_t fileLen = ftello(file); // Windows _ftelli64 +#endif if (fileLen < 256) { printMessage("File too small to be a DICOM image %s\n", fname); return d; } -//Since size of DICOM header is unknown, we will load it in 1mb segments -//This uses less RAM and makes is faster for computers with slow disk access -//Benefit is largest for 4D images. -//To disable caching and load entire file to RAM, compile with "-dmyLoadWholeFileToReadHeader" -//To implement the segments, we define these variables: -// fileLen = size of file in bytes -// MaxBufferSz = maximum size of buffer in bytes -// Buffer = array with n elements, where n is smaller of fileLen or MaxBufferSz -// lPos = position in Buffer (indexed from 0), 0..(n-1) -// lFileOffset = offset of Buffer in file: true file position is lOffset+lPos (initially 0) +// Since size of DICOM header is unknown, we will load it in 1mb segments +// This uses less RAM and makes is faster for computers with slow disk access +// Benefit is largest for 4D images. +// To disable caching and load entire file to RAM, compile with "-dmyLoadWholeFileToReadHeader" +// To implement the segments, we define these variables: +// fileLen = size of file in bytes +// MaxBufferSz = maximum size of buffer in bytes +// Buffer = array with n elements, where n is smaller of fileLen or MaxBufferSz +// lPos = position in Buffer (indexed from 0), 0..(n-1) +// lFileOffset = offset of Buffer in file: true file position is lOffset+lPos (initially 0) #ifdef myLoadWholeFileToReadHeader size_t MaxBufferSz = fileLen; #else - size_t MaxBufferSz = 1000000; //ideally size of DICOM header, but this varies from 2D to 4D files + size_t MaxBufferSz = 1000000; // ideally size of DICOM header, but this varies from 2D to 4D files #endif if (MaxBufferSz > (size_t)fileLen) MaxBufferSz = fileLen; - //printf("%d -> %d\n", MaxBufferSz, fileLen); + // printf("%d -> %d\n", MaxBufferSz, fileLen); size_t lFileOffset = 0; fseek(file, 0, SEEK_SET); - //Allocate memory + // Allocate memory unsigned char *buffer = (unsigned char *)malloc(MaxBufferSz + 1); if (!buffer) { printError("Memory exhausted!"); fclose(file); return d; } - //Read file contents into buffer + // Read file contents into buffer size_t sz = fread(buffer, 1, MaxBufferSz, file); if (sz < MaxBufferSz) { printError("Only loaded %zu of %zu bytes for %s\n", sz, MaxBufferSz, fname); @@ -4344,7 +4350,7 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D #ifdef myLoadWholeFileToReadHeader fclose(file); #endif - //DEFINE DICOM TAGS + // DEFINE DICOM TAGS #define kUnused 0x0001 + (0x0001 << 16) #define kStart 0x0002 + (0x0000 << 16) #define kMediaStorageSOPClassUID 0x0002 + (0x0002 << 16) @@ -4353,50 +4359,50 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D #define kImplementationVersionName 0x0002 + (0x0013 << 16) #define kSourceApplicationEntityTitle 0x0002 + (0x0016 << 16) #define kDirectoryRecordSequence 0x0004 + (0x1220 << 16) -//#define kSpecificCharacterSet 0x0008+(0x0005 << 16 ) //someday we should handle foreign characters... +// #define kSpecificCharacterSet 0x0008+(0x0005 << 16 ) //someday we should handle foreign characters... #define kImageTypeTag 0x0008 + (0x0008 << 16) -#define kSOPInstanceUID 0x0008+(0x0018 << 16 ) //Philips inserts time as last item, e.g. ?.?.?.YYYYMMDDHHmmSS.SSSS +#define kSOPInstanceUID 0x0008 + (0x0018 << 16) // Philips inserts time as last item, e.g. ?.?.?.YYYYMMDDHHmmSS.SSSS // not reliable https://neurostars.org/t/heudiconv-no-extraction-of-slice-timing-data-based-on-philips-dicoms/2201/21 #define kStudyDate 0x0008 + (0x0020 << 16) #define kAcquisitionDate 0x0008 + (0x0022 << 16) #define kAcquisitionDateTime 0x0008 + (0x002A << 16) #define kStudyTime 0x0008 + (0x0030 << 16) #define kSeriesTime 0x0008 + (0x0031 << 16) -#define kAcquisitionTime 0x0008 + (0x0032 << 16) //TM -//#define kContentTime 0x0008+(0x0033 << 16 ) //TM +#define kAcquisitionTime 0x0008 + (0x0032 << 16) // TM +// #define kContentTime 0x0008+(0x0033 << 16 ) //TM #define kAccessionNumber 0x0008 + (0x0050 << 16) -#define kModality 0x0008 + (0x0060 << 16) //CS +#define kModality 0x0008 + (0x0060 << 16) // CS #define kManufacturer 0x0008 + (0x0070 << 16) #define kInstitutionName 0x0008 + (0x0080 << 16) #define kInstitutionAddress 0x0008 + (0x0081 << 16) #define kReferringPhysicianName 0x0008 + (0x0090 << 16) -#define kTracerRadionuclide1 0x0008 + (0x0100 << 16) //SH -#define kTracerRadionuclide2 0x0008 + (0x0104 << 16) //LO +#define kTracerRadionuclide1 0x0008 + (0x0100 << 16) // SH +#define kTracerRadionuclide2 0x0008 + (0x0104 << 16) // LO #define kStationName 0x0008 + (0x1010 << 16) -#define kStudyDescription 0x0008 + (0x1030 << 16) //LO +#define kStudyDescription 0x0008 + (0x1030 << 16) // LO #define kSeriesDescription 0x0008 + (0x103E << 16) // '0008' '103E' 'LO' 'SeriesDescription' #define kInstitutionalDepartmentName 0x0008 + (0x1040 << 16) #define kManufacturersModelName 0x0008 + (0x1090 << 16) #define kDerivationDescription 0x0008 + (0x2111 << 16) #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 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 kPatientBirthDate 0x0010 + (0x0030 << 16) #define kPatientSex 0x0010 + (0x0040 << 16) #define kPatientAge 0x0010 + (0x1010 << 16) -#define kPatientSize 0x0010 + (0x1020 << 16) //DS +#define kPatientSize 0x0010 + (0x1020 << 16) // DS #define kPatientWeight 0x0010 + (0x1030 << 16) #define kAnatomicalOrientationType 0x0010 + (0x2210 << 16) #define kDeidentificationMethod 0x0012 + (0x0063 << 16) //[DICOMANON, issue 383 #ifdef myDeidentificationMethod - #define kDeidentificationMethodCodeSequence 0x0012 + (0x0064 << 16) - #define kCodeValue 0x0008 + (0x0100 << 16) - #define kCodingSchemeDesignator 0x0008 + (0x0102 << 16) - #define kCodingSchemeVersion 0x0008 + (0x0103 << 16) - #define kCodeMeaning 0x0008 + (0x0104 << 16) +#define kDeidentificationMethodCodeSequence 0x0012 + (0x0064 << 16) +#define kCodeValue 0x0008 + (0x0100 << 16) +#define kCodingSchemeDesignator 0x0008 + (0x0102 << 16) +#define kCodingSchemeVersion 0x0008 + (0x0103 << 16) +#define kCodeMeaning 0x0008 + (0x0104 << 16) #endif #define kBodyPartExamined 0x0018 + (0x0015 << 16) #define kBodyPartExamined 0x0018 + (0x0015 << 16) @@ -4405,105 +4411,105 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D #define kScanOptions 0x0018 + (0x0022 << 16) #define kMRAcquisitionType 0x0018 + (0x0023 << 16) #define kSequenceName 0x0018 + (0x0024 << 16) -#define kRadiopharmaceutical 0x0018 + (0x0031 << 16) //LO +#define kRadiopharmaceutical 0x0018 + (0x0031 << 16) // LO #define kZThick 0x0018 + (0x0050 << 16) #define kTR 0x0018 + (0x0080 << 16) #define kTE 0x0018 + (0x0081 << 16) -#define kTI 0x0018 + (0x0082 << 16) // Inversion time -#define kNumberOfAverages 0x0018 + (0x0083 << 16) //DS -#define kImagingFrequency 0x0018 + (0x0084 << 16) //DS -#define kEchoNum 0x0018 + (0x0086 << 16) //IS -#define kMagneticFieldStrength 0x0018 + (0x0087 << 16) //DS -#define kZSpacing 0x0018 + (0x0088 << 16) //'DS' 'SpacingBetweenSlices' -#define kPhaseEncodingSteps 0x0018 + (0x0089 << 16) //IS -#define kEchoTrainLength 0x0018 + (0x0091 << 16) //IS -#define kPercentSampling 0x0018 + (0x0093 << 16) //'DS' -#define kPhaseFieldofView 0x0018 + (0x0094 << 16) //'DS' -#define kPixelBandwidth 0x0018 + (0x0095 << 16) //'DS' 'PixelBandwidth' -#define kDeviceSerialNumber 0x0018 + (0x1000 << 16) //LO -#define kSoftwareVersions 0x0018 + (0x1020 << 16) //LO +#define kTI 0x0018 + (0x0082 << 16) // Inversion time +#define kNumberOfAverages 0x0018 + (0x0083 << 16) // DS +#define kImagingFrequency 0x0018 + (0x0084 << 16) // DS +#define kEchoNum 0x0018 + (0x0086 << 16) // IS +#define kMagneticFieldStrength 0x0018 + (0x0087 << 16) // DS +#define kZSpacing 0x0018 + (0x0088 << 16) //'DS' 'SpacingBetweenSlices' +#define kPhaseEncodingSteps 0x0018 + (0x0089 << 16) // IS +#define kEchoTrainLength 0x0018 + (0x0091 << 16) // IS +#define kPercentSampling 0x0018 + (0x0093 << 16) //'DS' +#define kPhaseFieldofView 0x0018 + (0x0094 << 16) //'DS' +#define kPixelBandwidth 0x0018 + (0x0095 << 16) //'DS' 'PixelBandwidth' +#define kDeviceSerialNumber 0x0018 + (0x1000 << 16) // LO +#define kSoftwareVersions 0x0018 + (0x1020 << 16) // LO #define kProtocolName 0x0018 + (0x1030 << 16) -#define kTriggerTime 0x0018 + (0x1060 << 16) //DS +#define kTriggerTime 0x0018 + (0x1060 << 16) // DS #define kRadionuclideTotalDose 0x0018 + (0x1074 << 16) #define kRadionuclideHalfLife 0x0018 + (0x1075 << 16) #define kRadionuclidePositronFraction 0x0018 + (0x1076 << 16) #define kGantryTilt 0x0018 + (0x1120 << 16) -#define kXRayTimeMS 0x0018 + (0x1150 << 16) //IS -#define kXRayTubeCurrent 0x0018 + (0x1151 << 16) //IS +#define kXRayTimeMS 0x0018 + (0x1150 << 16) // IS +#define kXRayTubeCurrent 0x0018 + (0x1151 << 16) // IS #define kXRayExposure 0x0018 + (0x1152 << 16) -#define kConvolutionKernel 0x0018 + (0x1210 << 16) //SH -#define kFrameDuration 0x0018 + (0x1242 << 16) //IS -#define kReceiveCoilName 0x0018 + (0x1250 << 16) // SH -//#define kTransmitCoilName 0x0018 + (0x1251 << 16) // SH issue527 -#define kAcquisitionMatrix 0x0018 + (0x1310 << 16) //US -#define kInPlanePhaseEncodingDirection 0x0018 + (0x1312 << 16) //CS +#define kConvolutionKernel 0x0018 + (0x1210 << 16) // SH +#define kFrameDuration 0x0018 + (0x1242 << 16) // IS +#define kReceiveCoilName 0x0018 + (0x1250 << 16) // SH +// #define kTransmitCoilName 0x0018 + (0x1251 << 16) // SH issue527 +#define kAcquisitionMatrix 0x0018 + (0x1310 << 16) // US +#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' -#define kInversionRecovery 0x0018 + uint32_t(0x9009 << 16) //'CS' 'YES'/'NO' -#define kSpoiling 0x0018 + uint32_t(0x9016 << 16) //'CS' -#define kEchoPlanarPulseSequence 0x0018 + uint32_t(0x9018 << 16) //'CS' 'YES'/'NO' -#define kMagnetizationTransferAttribute 0x0018 + uint32_t(0x9020 << 16) //'CS' 'ON_RESONANCE','OFF_RESONANCE','NONE' -#define kRectilinearPhaseEncodeReordering 0x0018 + uint32_t(0x9034 << 16) //'CS' 'REVERSE_LINEAR'/'LINEAR' -#define kPartialFourierDirection 0x0018 + uint32_t(0x9036 << 16) //'CS' -#define kCardiacSynchronizationTechnique 0x0018 + uint32_t(0x9037 << 16) //'CS' -#define kMRAcquisitionFrequencyEncodingSteps 0x0018 + uint32_t(0x9058 << 16) //US -#define kParallelReductionFactorInPlane 0x0018 + uint32_t(0x9069 << 16) //FD -#define kAcquisitionDuration 0x0018 + uint32_t(0x9073 << 16) //FD -#define kFrameAcquisitionDateTime 0x0018+uint32_t(0x9074<< 16 ) //DT "20181019212528.232500" -#define kDiffusionDirectionality 0x0018 + uint32_t(0x9075 << 16) // NONE, ISOTROPIC, or DIRECTIONAL -#define kParallelAcquisitionTechnique 0x0018 + uint32_t(0x9078 << 16) //CS: SENSE, SMASH -#define kInversionTimes 0x0018 + uint32_t(0x9079 << 16) //FD -#define kPartialFourier 0x0018 + uint32_t(0x9081 << 16) //CS -const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD -//#define kDiffusionBFactorSiemens 0x0019+(0x100C<< 16 ) // 0019;000C;SIEMENS MR HEADER;B_value -#define kDiffusionGradientDirectionSQ 0x0018 + uint32_t(0x9076 << 16) // SQ -#define kDiffusion_bValue 0x0018 + uint32_t(0x9087 << 16) // FD -#define kDiffusionOrientation 0x0018 + uint32_t(0x9089 << 16) // FD, seen in enhanced DICOM from Philips 5.* and Siemens XA10. -#define kImagingFrequencyFD 0x0018 + uint32_t(0x9098 << 16) //FD -#define kMREchoSequence 0x0018 + uint32_t(0x9114 << 16) //SQ -#define kParallelReductionFactorOutOfPlane 0x0018 + uint32_t(0x9155 << 16) //FD -#define kSARFD 0x0018 + uint32_t(0x9181 << 16) //FD -#define kMRAcquisitionPhaseEncodingStepsInPlane 0x0018 + uint32_t(0x9231 << 16) //US -#define kMRAcquisitionPhaseEncodingStepsOutOfPlane 0x0018 + uint32_t(0x9232 << 16) //US -#define kGradientEchoTrainLength 0x0018 + uint32_t(0x9241 << 16) //US -//#define kFrameAcquisitionDuration 0x0018+uint32_t(0x9220 << 16 ) //FD -#define kArterialSpinLabelingContrast 0x0018 + uint32_t(0x9250 << 16) //CS -#define kASLPulseTrainDuration 0x0018 + uint32_t(0x9258 << 16) //UL -#define kDiffusionBValueXX 0x0018 + uint32_t(0x9602 << 16) //FD -//#define kDiffusionBValueXY 0x0018 + uint32_t(0x9603 << 16) //FD -//#define kDiffusionBValueXZ 0x0018 + uint32_t(0x9604 << 16) //FD -//#define kDiffusionBValueYY 0x0018 + uint32_t(0x9605 << 16) //FD -//#define kDiffusionBValueYZ 0x0018 + uint32_t(0x9606 << 16) //FD -//#define kDiffusionBValueZZ 0x0018 + uint32_t(0x9607 << 16) //FD -#define kNumberOfImagesInMosaic 0x0019 + (0x100A << 16) //US NumberOfImagesInMosaic -//https://nmrimaging.wordpress.com/2011/12/20/when-we-process/ -// https://nciphub.org/groups/qindicom/wiki/DiffusionrelatedDICOMtags:experienceacrosssites?action=pdf -#define kDiffusion_bValueSiemens 0x0019 + (0x100C << 16) //IS -#define kDiffusionGradientDirectionSiemens 0x0019 + (0x100E << 16) //FD -#define kImaRelTablePosition 0x0019 + (0x1014 << 16) //IS[] Siemens V* -#define kSeriesPlaneGE 0x0019 + (0x1017 << 16) //SS -#define kDwellTime 0x0019 + (0x1018 << 16) //IS in NSec, see https://github.com/rordenlab/dcm2niix/issues/127 +#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' +#define kInversionRecovery 0x0018 + uint32_t(0x9009 << 16) //'CS' 'YES'/'NO' +#define kSpoiling 0x0018 + uint32_t(0x9016 << 16) //'CS' +#define kEchoPlanarPulseSequence 0x0018 + uint32_t(0x9018 << 16) //'CS' 'YES'/'NO' +#define kMagnetizationTransferAttribute 0x0018 + uint32_t(0x9020 << 16) //'CS' 'ON_RESONANCE','OFF_RESONANCE','NONE' +#define kRectilinearPhaseEncodeReordering 0x0018 + uint32_t(0x9034 << 16) //'CS' 'REVERSE_LINEAR'/'LINEAR' +#define kPartialFourierDirection 0x0018 + uint32_t(0x9036 << 16) //'CS' +#define kCardiacSynchronizationTechnique 0x0018 + uint32_t(0x9037 << 16) //'CS' +#define kMRAcquisitionFrequencyEncodingSteps 0x0018 + uint32_t(0x9058 << 16) // US +#define kParallelReductionFactorInPlane 0x0018 + uint32_t(0x9069 << 16) // FD +#define kAcquisitionDuration 0x0018 + uint32_t(0x9073 << 16) // FD +#define kFrameAcquisitionDateTime 0x0018 + uint32_t(0x9074 << 16) // DT "20181019212528.232500" +#define kDiffusionDirectionality 0x0018 + uint32_t(0x9075 << 16) // NONE, ISOTROPIC, or DIRECTIONAL +#define kParallelAcquisitionTechnique 0x0018 + uint32_t(0x9078 << 16) // CS: SENSE, SMASH +#define kInversionTimes 0x0018 + uint32_t(0x9079 << 16) // FD +#define kPartialFourier 0x0018 + uint32_t(0x9081 << 16) // CS + const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); // FD +// #define kDiffusionBFactorSiemens 0x0019+(0x100C<< 16 ) // 0019;000C;SIEMENS MR HEADER;B_value +#define kDiffusionGradientDirectionSQ 0x0018 + uint32_t(0x9076 << 16) // SQ +#define kDiffusion_bValue 0x0018 + uint32_t(0x9087 << 16) // FD +#define kDiffusionOrientation 0x0018 + uint32_t(0x9089 << 16) // FD, seen in enhanced DICOM from Philips 5.* and Siemens XA10. +#define kImagingFrequencyFD 0x0018 + uint32_t(0x9098 << 16) // FD +#define kMREchoSequence 0x0018 + uint32_t(0x9114 << 16) // SQ +#define kParallelReductionFactorOutOfPlane 0x0018 + uint32_t(0x9155 << 16) // FD +#define kSARFD 0x0018 + uint32_t(0x9181 << 16) // FD +#define kMRAcquisitionPhaseEncodingStepsInPlane 0x0018 + uint32_t(0x9231 << 16) // US +#define kMRAcquisitionPhaseEncodingStepsOutOfPlane 0x0018 + uint32_t(0x9232 << 16) // US +#define kGradientEchoTrainLength 0x0018 + uint32_t(0x9241 << 16) // US +// #define kFrameAcquisitionDuration 0x0018+uint32_t(0x9220 << 16 ) //FD +#define kArterialSpinLabelingContrast 0x0018 + uint32_t(0x9250 << 16) // CS +#define kASLPulseTrainDuration 0x0018 + uint32_t(0x9258 << 16) // UL +#define kDiffusionBValueXX 0x0018 + uint32_t(0x9602 << 16) // FD +// #define kDiffusionBValueXY 0x0018 + uint32_t(0x9603 << 16) //FD +// #define kDiffusionBValueXZ 0x0018 + uint32_t(0x9604 << 16) //FD +// #define kDiffusionBValueYY 0x0018 + uint32_t(0x9605 << 16) //FD +// #define kDiffusionBValueYZ 0x0018 + uint32_t(0x9606 << 16) //FD +// #define kDiffusionBValueZZ 0x0018 + uint32_t(0x9607 << 16) //FD +#define kNumberOfImagesInMosaic 0x0019 + (0x100A << 16) // US NumberOfImagesInMosaic +// https://nmrimaging.wordpress.com/2011/12/20/when-we-process/ +// https://nciphub.org/groups/qindicom/wiki/DiffusionrelatedDICOMtags:experienceacrosssites?action=pdf +#define kDiffusion_bValueSiemens 0x0019 + (0x100C << 16) // IS +#define kDiffusionGradientDirectionSiemens 0x0019 + (0x100E << 16) // FD +#define kImaRelTablePosition 0x0019 + (0x1014 << 16) // IS[] Siemens V* +#define kSeriesPlaneGE 0x0019 + (0x1017 << 16) // SS +#define kDwellTime 0x0019 + (0x1018 << 16) // IS in NSec, see https://github.com/rordenlab/dcm2niix/issues/127 #define kLastScanLoc 0x0019 + (0x101B << 16) -#define kBandwidthPerPixelPhaseEncode 0x0019 + (0x1028 << 16) //FD -#define kSliceTimeSiemens 0x0019 + (0x1029 << 16) ///FD -#define kAcquisitionDurationGE 0x0019 + (0x105a << 16) //FL Acquisition Duration in microsecond, Duration of Scan (series) -#define kTableDeltaGE 0x0019 + (0x107f << 16) //DS Table delta -#define kPulseSequenceNameGE 0x0019 + (0x109C << 16) //LO 'epiRT' or 'epi' -#define kInternalPulseSequenceNameGE 0x0019 + (0x109E << 16) //LO 'EPI' or 'EPI2' -#define kRawDataRunNumberGE 0x0019 + (0x10A2 << 16)//SL -#define kMaxEchoNumGE 0x0019 + (0x10A9 << 16) //DS -#define kUserData11GE 0x0019 + (0x10B2 << 16) //DS Diffusion tensor filename -#define kUserData12GE 0x0019 + (0x10B3 << 16) //DS phase diffusion direction; diffusion gradient cycling mode -#define kUserData15GE 0x0019 + (0x10B6 << 16) //DS Diffusion Gradient Derating; cycling special OFF -#define kDiffusionDirectionGEX 0x0019 + (0x10BB << 16) //DS phase diffusion direction -#define kDiffusionDirectionGEY 0x0019 + (0x10BC << 16) //DS frequency diffusion direction -#define kDiffusionDirectionGEZ 0x0019 + (0x10BD << 16) //DS slice diffusion direction -#define kNumberOfDiffusionT2GE 0x0019 + (0x10DF << 16) ///DS NumberOfDiffusionT2:UserData23 (release 10+) -#define kNumberOfDiffusionDirectionGE 0x0019 + (0x10E0 << 16) ///DS NumberOfDiffusionDirection:UserData24 (release 10+) -#define kVelocityEncodeScaleGE 0x0019 + (0x10E2 << 16) ///DS Velocity Encode Scale +#define kBandwidthPerPixelPhaseEncode 0x0019 + (0x1028 << 16) // FD +#define kSliceTimeSiemens 0x0019 + (0x1029 << 16) /// FD +#define kAcquisitionDurationGE 0x0019 + (0x105a << 16) // FL Acquisition Duration in microsecond, Duration of Scan (series) +#define kTableDeltaGE 0x0019 + (0x107f << 16) // DS Table delta +#define kPulseSequenceNameGE 0x0019 + (0x109C << 16) // LO 'epiRT' or 'epi' +#define kInternalPulseSequenceNameGE 0x0019 + (0x109E << 16) // LO 'EPI' or 'EPI2' +#define kRawDataRunNumberGE 0x0019 + (0x10A2 << 16) // SL +#define kMaxEchoNumGE 0x0019 + (0x10A9 << 16) // DS +#define kUserData11GE 0x0019 + (0x10B2 << 16) // DS Diffusion tensor filename +#define kUserData12GE 0x0019 + (0x10B3 << 16) // DS phase diffusion direction; diffusion gradient cycling mode +#define kUserData15GE 0x0019 + (0x10B6 << 16) // DS Diffusion Gradient Derating; cycling special OFF +#define kDiffusionDirectionGEX 0x0019 + (0x10BB << 16) // DS phase diffusion direction +#define kDiffusionDirectionGEY 0x0019 + (0x10BC << 16) // DS frequency diffusion direction +#define kDiffusionDirectionGEZ 0x0019 + (0x10BD << 16) // DS slice diffusion direction +#define kNumberOfDiffusionT2GE 0x0019 + (0x10DF << 16) /// DS NumberOfDiffusionT2:UserData23 (release 10+) +#define kNumberOfDiffusionDirectionGE 0x0019 + (0x10E0 << 16) /// DS NumberOfDiffusionDirection:UserData24 (release 10+) +#define kVelocityEncodeScaleGE 0x0019 + (0x10E2 << 16) /// DS Velocity Encode Scale #define kStudyID 0x0020 + (0x0010 << 16) #define kSeriesNum 0x0020 + (0x0011 << 16) #define kAcquNum 0x0020 + (0x0012 << 16) @@ -4513,161 +4519,161 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD #define kImagePositionPatient 0x0020 + (0x0032 << 16) // Actually ! #define kOrientationACR 0x0020 + (0x0035 << 16) #define kOrientation 0x0020 + (0x0037 << 16) -#define kTemporalPosition 0x0020+(0x0100 << 16 ) //IS -//#define kNumberOfTemporalPositions 0x0020+(0x0105 << 16 ) //IS public tag for NumberOfDynamicScans -#define kTemporalResolution 0x0020 + (0x0110 << 16) //DS -#define kImagesInAcquisition 0x0020 + (0x1002 << 16) //IS -//#define kSliceLocation 0x0020+(0x1041 << 16 ) //DS would be useful if not optional type 3 -#define kImageComments 0x0020 + (0x4000 << 16) // '0020' '4000' 'LT' 'ImageComments' -#define kFrameContentSequence 0x0020 + uint32_t(0x9111 << 16) //SQ -#define kTriggerDelayTime 0x0020 + uint32_t(0x9153 << 16) //FD -#define kDimensionIndexValues 0x0020 + uint32_t(0x9157 << 16) // UL n-dimensional index of frame. +#define kTemporalPosition 0x0020 + (0x0100 << 16) // IS +// #define kNumberOfTemporalPositions 0x0020+(0x0105 << 16 ) //IS public tag for NumberOfDynamicScans +#define kTemporalResolution 0x0020 + (0x0110 << 16) // DS +#define kImagesInAcquisition 0x0020 + (0x1002 << 16) // IS +// #define kSliceLocation 0x0020+(0x1041 << 16 ) //DS would be useful if not optional type 3 +#define kImageComments 0x0020 + (0x4000 << 16) // '0020' '4000' 'LT' 'ImageComments' +#define kFrameContentSequence 0x0020 + uint32_t(0x9111 << 16) // SQ +#define kTriggerDelayTime 0x0020 + uint32_t(0x9153 << 16) // FD +#define kDimensionIndexValues 0x0020 + uint32_t(0x9157 << 16) // UL n-dimensional index of frame. #define kInStackPositionNumber 0x0020 + uint32_t(0x9057 << 16) // UL can help determine slices in volume #define kTemporalPositionIndex 0x0020 + uint32_t(0x9128 << 16) // UL #define kDimensionIndexPointer 0x0020 + uint32_t(0x9165 << 16) -//Private Group 21 as Used by Siemens: -#define kRelTablePosition 0x0021 + (0x1005 << 16) //IS Siemens XA -#define kScanningSequenceSiemens 0x0021 + (0x105A << 16) //CS n.b. for GE this is Diffusion direction of SL! -#define kSequenceVariant21 0x0021 + (0x105B << 16) //CS Siemens ONLY: For GE this is TaggingFlipAngle -#define kScanOptionsSiemens 0x0021 + (0x105C << 16) //CS Siemens ONLY -#define kPATModeText 0x0021 + (0x1009 << 16) //LO, see kImaPATModeText -#define kFrameNumberInSeries 0x0021 + (0x118A << 16) //IS issue837 +// Private Group 21 as Used by Siemens: +#define kRelTablePosition 0x0021 + (0x1005 << 16) // IS Siemens XA +#define kScanningSequenceSiemens 0x0021 + (0x105A << 16) // CS n.b. for GE this is Diffusion direction of SL! +#define kSequenceVariant21 0x0021 + (0x105B << 16) // CS Siemens ONLY: For GE this is TaggingFlipAngle +#define kScanOptionsSiemens 0x0021 + (0x105C << 16) // CS Siemens ONLY +#define kPATModeText 0x0021 + (0x1009 << 16) // LO, see kImaPATModeText +#define kFrameNumberInSeries 0x0021 + (0x118A << 16) // IS issue837 #define kCSASeriesHeaderInfoXA 0x0021 + (0x1019 << 16) #define kCSASeriesHeaderInfoXA2 0x0021 + (0x11FE << 16) -#define kTimeAfterStart 0x0021 + (0x1104 << 16) //DS -#define kICE_dims 0x0021 + (0x1106 << 16) //LO [X_4_1_1_1_1_160_1_1_1_1_1_277] -#define kPhaseEncodingDirectionPositiveSiemens 0x0021 + (0x111C << 16) //IS -#define kRealDwellTime 0x0021+(0x1142<< 16 )//IS -//#define kPATModeText2 0x0021 + (0x1156 << 16) //LO, always same as 0021,1009 -#define kBandwidthPerPixelPhaseEncode21 0x0021 + (0x1153 << 16) //FD -#define kCoilElements 0x0021 + (0x114F << 16) //LO +#define kTimeAfterStart 0x0021 + (0x1104 << 16) // DS +#define kICE_dims 0x0021 + (0x1106 << 16) // LO [X_4_1_1_1_1_160_1_1_1_1_1_277] +#define kPhaseEncodingDirectionPositiveSiemens 0x0021 + (0x111C << 16) // IS +#define kRealDwellTime 0x0021 + (0x1142 << 16) // IS +// #define kPATModeText2 0x0021 + (0x1156 << 16) //LO, always same as 0021,1009 +#define kBandwidthPerPixelPhaseEncode21 0x0021 + (0x1153 << 16) // FD +#define kCoilElements 0x0021 + (0x114F << 16) // LO #define kICE_dimsLong 0x0021 + (0x118e << 16) -#define kAcquisitionMatrixText21 0x0021 + (0x1158 << 16) //SH -#define kImageTypeText 0x0021 + (0x1175 << 16) //CS -#define kDeepLearningText 0x0021 + (0x1176 << 16) //LO -//Private Group 21 as used by GE: -#define kLocationsInAcquisitionGE 0x0021 + (0x104F << 16) //SS 'LocationsInAcquisitionGE' -#define kRTIA_timer 0x0021 + (0x105E << 16) //DS -#define kProtocolDataBlockGE 0x0025 + (0x101B << 16) //OB -#define kNumberOfPointsPerArm 0x0027 + (0x1060 << 16) //FL -#define kNumberOfArms 0x0027 + (0x1061 << 16) //FL -#define kNumberOfExcitations 0x0027 + (0x1062 << 16) //FL +#define kAcquisitionMatrixText21 0x0021 + (0x1158 << 16) // SH +#define kImageTypeText 0x0021 + (0x1175 << 16) // CS +#define kDeepLearningText 0x0021 + (0x1176 << 16) // LO +// Private Group 21 as used by GE: +#define kLocationsInAcquisitionGE 0x0021 + (0x104F << 16) // SS 'LocationsInAcquisitionGE' +#define kRTIA_timer 0x0021 + (0x105E << 16) // DS +#define kProtocolDataBlockGE 0x0025 + (0x101B << 16) // OB +#define kNumberOfPointsPerArm 0x0027 + (0x1060 << 16) // FL +#define kNumberOfArms 0x0027 + (0x1061 << 16) // FL +#define kNumberOfExcitations 0x0027 + (0x1062 << 16) // FL #define kSamplesPerPixel 0x0028 + (0x0002 << 16) #define kPhotometricInterpretation 0x0028 + (0x0004 << 16) #define kPlanarRGB 0x0028 + (0x0006 << 16) -#define kDim3 0x0028 + (0x0008 << 16) //number of frames - for Philips this is Dim3*Dim4 +#define kDim3 0x0028 + (0x0008 << 16) // number of frames - for Philips this is Dim3*Dim4 #define kDim2 0x0028 + (0x0010 << 16) #define kDim1 0x0028 + (0x0011 << 16) -#define kXYSpacing 0x0028 + (0x0030 << 16) //DS 'PixelSpacing' +#define kXYSpacing 0x0028 + (0x0030 << 16) // DS 'PixelSpacing' #define kBitsAllocated 0x0028 + (0x0100 << 16) -#define kBitsStored 0x0028 + (0x0101 << 16) //US 'BitsStored' -#define kHighBit 0x0028 + (0x0102 << 16) //US 'HighBit' -#define kIsSigned 0x0028 + (0x0103 << 16) //PixelRepresentation -#define kPixelPaddingValue 0x0028 + (0x0120 << 16) // https://github.com/rordenlab/dcm2niix/issues/262 +#define kBitsStored 0x0028 + (0x0101 << 16) // US 'BitsStored' +#define kHighBit 0x0028 + (0x0102 << 16) // US 'HighBit' +#define kIsSigned 0x0028 + (0x0103 << 16) // PixelRepresentation +#define kPixelPaddingValue 0x0028 + (0x0120 << 16) // https://github.com/rordenlab/dcm2niix/issues/262 #define kFloatPixelPaddingValue 0x0028 + (0x0122 << 16) // https://github.com/rordenlab/dcm2niix/issues/262 #define kIntercept 0x0028 + (0x1052 << 16) #define kSlope 0x0028 + (0x1053 << 16) -//#define kRescaleType 0x0028+(0x1053 << 16 ) //LO e.g. for Philips Fieldmap: [Hz] -//#define kSpectroscopyDataPointColumns 0x0028+(0x9002 << 16 ) //IS -#define kGeiisFlag 0x0029 + (0x0010 << 16) //warn user if dreaded GEIIS was used to process image +// #define kRescaleType 0x0028+(0x1053 << 16 ) //LO e.g. for Philips Fieldmap: [Hz] +// #define kSpectroscopyDataPointColumns 0x0028+(0x9002 << 16 ) //IS +#define kGeiisFlag 0x0029 + (0x0010 << 16) // warn user if dreaded GEIIS was used to process image #define kCSAImageHeaderInfo 0x0029 + (0x1010 << 16) #define kCSASeriesHeaderInfo 0x0029 + (0x1020 << 16) -#define kStudyComments 0x0032 + (0x4000 << 16) //LT StudyComments -//#define kObjectGraphics 0x0029+(0x1210 << 16 ) //0029,1210 syngoPlatformOOGInfo Object Oriented Graphics +#define kStudyComments 0x0032 + (0x4000 << 16) // LT StudyComments +// #define kObjectGraphics 0x0029+(0x1210 << 16 ) //0029,1210 syngoPlatformOOGInfo Object Oriented Graphics #define kProcedureStepDescription 0x0040 + (0x0254 << 16) -#define kRealWorldIntercept 0x0040 + uint32_t(0x9224 << 16) //IS dicm2nii's SlopInt_6_9 -#define kRealWorldSlope 0x0040 + uint32_t(0x9225 << 16) //IS dicm2nii's SlopInt_6_9 -#define kShimGradientX 0x0043 + (0x1002 << 16) //SS -#define kShimGradientY 0x0043 + (0x1003 << 16) //SS -#define kShimGradientZ 0x0043 + (0x1004 << 16) //SS -#define kVasCollapseFlagGE 0x0043 + (0x1030 << 16) //SS issue690 -#define kPrescanReuseString 0x0043 + (0x1095 << 16) //LO -#define kUserDefineDataGE 0x0043 + (0x102A << 16) //OB -#define kEffectiveEchoSpacingGE 0x0043 + (0x102C << 16) //SS -#define kImageTypeGE 0x0043 + (0x102F << 16) //SS 0/1/2/3 for magnitude/phase/real/imaginary -#define kDiffusion_bValueGE 0x0043 + (0x1039 << 16) //IS dicm2nii's SlopInt_6_9 -#define kEpiRTGroupDelayGE 0x0043 + (0x107C << 16) //FL -#define kAssetRFactorsGE 0x0043 + (0x1083 << 16) //DS -#define kASLContrastTechniqueGE 0x0043 + (0x10A3 << 16) //CS -#define kASLLabelingTechniqueGE 0x0043 + (0x10A4 << 16) //LO -#define kDurationLabelPulseGE 0x0043 + (0x10A5 << 16) //IS -#define kMRTablePositionInformation 0x0043 + (0x10B2 << 16) //LO GE -#define kMultiBandGE 0x0043 + (0x10B6 << 16) //LO -#define kCompressedSensingParameters 0x0043 + (0x10B7 << 16) //LO -#define kDeepLearningParameters 0x0043 + (0x10CA << 16) //LO "0.75\High" -#define kAcquisitionMatrixText 0x0051 + (0x100B << 16) //LO -#define kImageOrientationText 0x0051 + (0x100E << 16) // +#define kRealWorldIntercept 0x0040 + uint32_t(0x9224 << 16) // IS dicm2nii's SlopInt_6_9 +#define kRealWorldSlope 0x0040 + uint32_t(0x9225 << 16) // IS dicm2nii's SlopInt_6_9 +#define kShimGradientX 0x0043 + (0x1002 << 16) // SS +#define kShimGradientY 0x0043 + (0x1003 << 16) // SS +#define kShimGradientZ 0x0043 + (0x1004 << 16) // SS +#define kVasCollapseFlagGE 0x0043 + (0x1030 << 16) // SS issue690 +#define kPrescanReuseString 0x0043 + (0x1095 << 16) // LO +#define kUserDefineDataGE 0x0043 + (0x102A << 16) // OB +#define kEffectiveEchoSpacingGE 0x0043 + (0x102C << 16) // SS +#define kImageTypeGE 0x0043 + (0x102F << 16) // SS 0/1/2/3 for magnitude/phase/real/imaginary +#define kDiffusion_bValueGE 0x0043 + (0x1039 << 16) // IS dicm2nii's SlopInt_6_9 +#define kEpiRTGroupDelayGE 0x0043 + (0x107C << 16) // FL +#define kAssetRFactorsGE 0x0043 + (0x1083 << 16) // DS +#define kASLContrastTechniqueGE 0x0043 + (0x10A3 << 16) // CS +#define kASLLabelingTechniqueGE 0x0043 + (0x10A4 << 16) // LO +#define kDurationLabelPulseGE 0x0043 + (0x10A5 << 16) // IS +#define kMRTablePositionInformation 0x0043 + (0x10B2 << 16) // LO GE +#define kMultiBandGE 0x0043 + (0x10B6 << 16) // LO +#define kCompressedSensingParameters 0x0043 + (0x10B7 << 16) // LO +#define kDeepLearningParameters 0x0043 + (0x10CA << 16) // LO "0.75\High" +#define kAcquisitionMatrixText 0x0051 + (0x100B << 16) // LO +#define kImageOrientationText 0x0051 + (0x100E << 16) // #define kCoilSiemens 0x0051 + (0x100F << 16) #define kImaPATModeText 0x0051 + (0x1011 << 16) #define kLocationsInAcquisition 0x0054 + (0x0081 << 16) -#define kUnitsPT 0x0054 + (0x1001 << 16) //CS -#define kAttenuationCorrectionMethod 0x0054 + (0x1101 << 16) //LO -#define kDecayCorrection 0x0054 + (0x1102 << 16) //CS -#define kReconstructionMethod 0x0054 + (0x1103 << 16) //LO -#define kFrameReferenceTime 0x0054 + (0x1300 << 16) //DS -#define kDecayFactor 0x0054 + (0x1321 << 16) //DS -#define kScatterFraction 0x0054 + (0x1323 << 16) //DS -//ftp://dicom.nema.org/MEDICAL/dicom/2014c/output/chtml/part03/sect_C.8.9.4.html -//If ImageType is REPROJECTION we slice direction is reversed - need example to test -// #define kSeriesType 0x0054+(0x1000 << 16 ) +#define kUnitsPT 0x0054 + (0x1001 << 16) // CS +#define kAttenuationCorrectionMethod 0x0054 + (0x1101 << 16) // LO +#define kDecayCorrection 0x0054 + (0x1102 << 16) // CS +#define kReconstructionMethod 0x0054 + (0x1103 << 16) // LO +#define kFrameReferenceTime 0x0054 + (0x1300 << 16) // DS +#define kDecayFactor 0x0054 + (0x1321 << 16) // DS +#define kScatterFraction 0x0054 + (0x1323 << 16) // DS +// ftp://dicom.nema.org/MEDICAL/dicom/2014c/output/chtml/part03/sect_C.8.9.4.html +// If ImageType is REPROJECTION we slice direction is reversed - need example to test +// #define kSeriesType 0x0054+(0x1000 << 16 ) #define kDoseCalibrationFactor 0x0054 + (0x1322 << 16) #define kPETImageIndex 0x0054 + (0x1330 << 16) -#define kReferencedSegmentNumber 0x0062 + (0x000B << 16) //US -#define kPEDirectionDisplayedUIH 0x0065 + (0x1005 << 16) //SH -#define kDiffusion_bValueUIH 0x0065 + (0x1009 << 16) //FD -#define kParallelInformationUIH 0x0065 + (0x100D << 16) //SH -#define kNumberOfImagesInGridUIH 0x0065 + (0x1050 << 16) //DS -#define kDiffusionGradientDirectionUIH 0x0065 + (0x1037 << 16) //FD -//#define kMRVFrameSequenceUIH 0x0065+(0x1050<< 16 ) //SQ -#define kPhaseEncodingDirectionPositiveUIH 0x0065 + (0x1058 << 16) //IS issue410 +#define kReferencedSegmentNumber 0x0062 + (0x000B << 16) // US +#define kPEDirectionDisplayedUIH 0x0065 + (0x1005 << 16) // SH +#define kDiffusion_bValueUIH 0x0065 + (0x1009 << 16) // FD +#define kParallelInformationUIH 0x0065 + (0x100D << 16) // SH +#define kNumberOfImagesInGridUIH 0x0065 + (0x1050 << 16) // DS +#define kDiffusionGradientDirectionUIH 0x0065 + (0x1037 << 16) // FD +// #define kMRVFrameSequenceUIH 0x0065+(0x1050<< 16 ) //SQ +#define kPhaseEncodingDirectionPositiveUIH 0x0065 + (0x1058 << 16) // IS issue410 #define kIconImageSequence 0x0088 + (0x0200 << 16) -#define kElscintIcon 0x07a3 + (0x10ce << 16) //see kGeiisFlag and https://github.com/rordenlab/dcm2niix/issues/239 -#define kPMSCT_RLE1 0x07a1 + (0x100a << 16) //Elscint/Philips compression -#define kPrivateCreator 0x2001 + (0x0010 << 16) // LO (Private creator is any tag where group is odd and element is x0010-x00FF +#define kElscintIcon 0x07a3 + (0x10ce << 16) // see kGeiisFlag and https://github.com/rordenlab/dcm2niix/issues/239 +#define kPMSCT_RLE1 0x07a1 + (0x100a << 16) // Elscint/Philips compression +#define kPrivateCreator 0x2001 + (0x0010 << 16) // LO (Private creator is any tag where group is odd and element is x0010-x00FF #define kDiffusion_bValuePhilips 0x2001 + (0x1003 << 16) // FL -#define kPhaseNumber 0x2001 + (0x1008 << 16) //IS -#define kCardiacSync 0x2001 + (0x1010 << 16) //CS -//#define kDiffusionDirectionPhilips 0x2001+(0x1004 << 16 )//CS Diffusion Direction -#define kSliceNumberMrPhilips 0x2001 + (0x100A << 16) //IS Slice_Number_MR -#define kSliceOrient 0x2001 + (0x100B << 16) //2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL) -#define kEPIFactorPhilips 0x2001 + (0x1013 << 16) //SL -#define kPrepulseDelay 0x2001 + (0x101B << 16) //FL -#define kPrepulseType 0x2001 + (0x101C << 16) //CS -#define kRespirationSync 0x2001 + (0x101F << 16) //CS -#define kNumberOfSlicesMrPhilips 0x2001 + (0x1018 << 16) //SL 0x2001, 0x1018 ), "Number_of_Slices_MR" +#define kPhaseNumber 0x2001 + (0x1008 << 16) // IS +#define kCardiacSync 0x2001 + (0x1010 << 16) // CS +// #define kDiffusionDirectionPhilips 0x2001+(0x1004 << 16 )//CS Diffusion Direction +#define kSliceNumberMrPhilips 0x2001 + (0x100A << 16) // IS Slice_Number_MR +#define kSliceOrient 0x2001 + (0x100B << 16) // 2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL) +#define kEPIFactorPhilips 0x2001 + (0x1013 << 16) // SL +#define kPrepulseDelay 0x2001 + (0x101B << 16) // FL +#define kPrepulseType 0x2001 + (0x101C << 16) // CS +#define kRespirationSync 0x2001 + (0x101F << 16) // CS +#define kNumberOfSlicesMrPhilips 0x2001 + (0x1018 << 16) // SL 0x2001, 0x1018 ), "Number_of_Slices_MR" #define kPartialMatrixScannedPhilips 0x2001 + (0x1019 << 16) // CS -#define kWaterFatShiftPhilips 0x2001 + (0x1022 << 16) //FL -//#define kMRSeriesAcquisitionNumber 0x2001+(0x107B << 16 ) //IS -//#define kNumberOfLocationsPhilips 0x2001+(0x1015 << 16 ) //SS -//#define kStackSliceNumber 0x2001+(0x1035 << 16 )//? Potential way to determine slice order for Philips? +#define kWaterFatShiftPhilips 0x2001 + (0x1022 << 16) // FL +// #define kMRSeriesAcquisitionNumber 0x2001+(0x107B << 16 ) //IS +// #define kNumberOfLocationsPhilips 0x2001+(0x1015 << 16 ) //SS +// #define kStackSliceNumber 0x2001+(0x1035 << 16 )//? Potential way to determine slice order for Philips? #define kNumberOfDynamicScans 0x2001 + (0x1081 << 16) //'2001' '1081' 'IS' 'NumberOfDynamicScans' -//#define kTRPhilips 0x2005 + (0x1030 << 16) //(2005,1030) FL 30\150 -#define kTRArray 0x2005 + (0x1030 << 16) //FL Array, e.g. "15\75", see issue 743 +// #define kTRPhilips 0x2005 + (0x1030 << 16) //(2005,1030) FL 30\150 +#define kTRArray 0x2005 + (0x1030 << 16) // FL Array, e.g. "15\75", see issue 743 #define kMRfMRIStatusIndicationPhilips 0x2005 + (0x1063 << 16) -#define kMRAcquisitionTypePhilips 0x2005 + (0x106F << 16) //SS -#define kAngulationAP 0x2005 + (0x1071 << 16) //'2005' '1071' 'FL' 'MRStackAngulationAP' -#define kAngulationFH 0x2005 + (0x1072 << 16) //'2005' '1072' 'FL' 'MRStackAngulationFH' -#define kAngulationRL 0x2005 + (0x1073 << 16) //'2005' '1073' 'FL' 'MRStackAngulationRL' +#define kMRAcquisitionTypePhilips 0x2005 + (0x106F << 16) // SS +#define kAngulationAP 0x2005 + (0x1071 << 16) //'2005' '1071' 'FL' 'MRStackAngulationAP' +#define kAngulationFH 0x2005 + (0x1072 << 16) //'2005' '1072' 'FL' 'MRStackAngulationFH' +#define kAngulationRL 0x2005 + (0x1073 << 16) //'2005' '1073' 'FL' 'MRStackAngulationRL' #define kMRStackOffcentreAP 0x2005 + (0x1078 << 16) #define kMRStackOffcentreFH 0x2005 + (0x1079 << 16) #define kMRStackOffcentreRL 0x2005 + (0x107A << 16) #define kPhilipsSlope 0x2005 + (0x100E << 16) -#define kMRImageDynamicScanBeginTime 0x2005 + (0x10A0 << 16) //FL +#define kMRImageDynamicScanBeginTime 0x2005 + (0x10A0 << 16) // FL #define kDiffusionDirectionRL 0x2005 + (0x10B0 << 16) #define kDiffusionDirectionAP 0x2005 + (0x10B1 << 16) #define kDiffusionDirectionFH 0x2005 + (0x10B2 << 16) #define kDeepLearningPhilips 0x2005 + (0x1110 << 16) #define kPrivatePerFrameSq 0x2005 + (0x140F << 16) -#define kMRStackTablePosLong 0x2005 + (0x143C << 16) //FL Philips -#define kMRImageDiffBValueNumber 0x2005 + (0x1412 << 16) //IS -#define kMRImageGradientOrientationNumber 0x2005+(0x1413 << 16) //IS -#define kMRImageLabelType 0x2005 + (0x1429 << 16) //CS ASL LBL_CTL https://github.com/physimals/dcm_convert_phillips/ -#define kMRImageDiffVolumeNumber 0x2005+(0x1596 << 16) //IS +#define kMRStackTablePosLong 0x2005 + (0x143C << 16) // FL Philips +#define kMRImageDiffBValueNumber 0x2005 + (0x1412 << 16) // IS +#define kMRImageGradientOrientationNumber 0x2005 + (0x1413 << 16) // IS +#define kMRImageLabelType 0x2005 + (0x1429 << 16) // CS ASL LBL_CTL https://github.com/physimals/dcm_convert_phillips/ +#define kMRImageDiffVolumeNumber 0x2005 + (0x1596 << 16) // IS #define kOriginalAttributesSq 0x0400 + (0x0561 << 16) -#define kSharedFunctionalGroupsSequence 0x5200 + uint32_t(0x9229 << 16) // SQ +#define kSharedFunctionalGroupsSequence 0x5200 + uint32_t(0x9229 << 16) // SQ #define kPerFrameFunctionalGroupsSequence 0x5200 + uint32_t(0x9230 << 16) // SQ #define kWaveformSq 0x5400 + (0x0100 << 16) -#define kSpectroscopyData 0x5600 + (0x0020 << 16) //OF +#define kSpectroscopyData 0x5600 + (0x0020 << 16) // OF #define kImageStart 0x7FE0 + (0x0010 << 16) #define kImageStartFloat 0x7FE0 + (0x0008 << 16) #define kImageStartDouble 0x7FE0 + (0x0009 << 16) @@ -4675,26 +4681,26 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD uint32_t kItemDelimitationTag = 0xFFFE + (0xE00D << 16); uint32_t kSequenceDelimitationItemTag = 0xFFFE + (0xE0DD << 16); #define salvageAgfa -#ifdef salvageAgfa //issue435 -// handle PrivateCreator renaming e.g. 0021,10xx -> 0021,11xx -// https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/siemens.tpl -// https://github.com/neurolabusc/dcm_qa_agfa -// http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_7.8.html - #define kMaxRemaps 16 //no vendor uses more than 5 private creator groups - //we need to keep track of multiple remappings, e.g. issue 437 2005,0014->2005,0012; 2005,0015->2005,0011 +#ifdef salvageAgfa // issue435 + // handle PrivateCreator renaming e.g. 0021,10xx -> 0021,11xx + // https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/siemens.tpl + // https://github.com/neurolabusc/dcm_qa_agfa + // http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_7.8.html +#define kMaxRemaps 16 // no vendor uses more than 5 private creator groups + // we need to keep track of multiple remappings, e.g. issue 437 2005,0014->2005,0012; 2005,0015->2005,0011 int nRemaps = 0; - uint32_t privateCreatorMasks[kMaxRemaps]; //0 -> none - uint32_t privateCreatorRemaps[kMaxRemaps]; //0 -> none + uint32_t privateCreatorMasks[kMaxRemaps]; // 0 -> none + uint32_t privateCreatorRemaps[kMaxRemaps]; // 0 -> none #endif double maxSAR = -INFINITY; - double TE = 0.0; //most recent echo time recorded + double TE = 0.0; // most recent echo time recorded float temporalResolutionMS = 0.0; float MRImageDynamicScanBeginTime = 0.0; bool isHasBMatrix = false; bool isHasBVec = false; bool is2005140FSQ = false; int sqDepth04000561 = -1; - bool is00089092SQ = false; //Referenced Image Evidence SQ + bool is00089092SQ = false; // Referenced Image Evidence SQ bool overlayOK = true; int userData11GE = 0; int userData12GE = 0; @@ -4704,25 +4710,25 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD int overlayRows = 0; int overlayCols = 0; bool isNeologica = false; - //bool isTriggerSynced = false; - //bool isProspectiveSynced = false; - bool isDICOMANON = false; //issue383 - bool isMATLAB = false; //issue383 - bool isDWI_UIH = false; //issue836 - //bool isASL = false; + // bool isTriggerSynced = false; + // bool isProspectiveSynced = false; + bool isDICOMANON = false; // issue383 + bool isMATLAB = false; // issue383 + bool isDWI_UIH = false; // issue836 + // bool isASL = false; bool has00200013 = false; - //double contentTime = 0.0; + // double contentTime = 0.0; int echoTrainLengthPhil = 0; int philMRImageDiffBValueNumber = 0; int philMRImageDiffVolumeNumber = -1; int sqDepth = 0; - int seriesInstanceUIDsqDepth = 65535; //issue655 + int seriesInstanceUIDsqDepth = 65535; // issue655 int acquisitionTimesGE_UIH = 0; int sqDepth00189114 = -1; bool hasDwiDirectionality = false; - //float sliceLocation = INFINITY; //useless since this tag is optional - //int numFirstPatientPosition = 0; - int nDimIndxVal = -1; //tracks Philips kDimensionIndexValues + // float sliceLocation = INFINITY; //useless since this tag is optional + // int numFirstPatientPosition = 0; + int nDimIndxVal = -1; // tracks Philips kDimensionIndexValues int locationsInAcquisitionGE = 0; int PETImageIndex = 0; int inStackPositionNumber = 0; @@ -4739,36 +4745,36 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD int maxTemporalPositionIndex = 0; int minReferencedSegmentNumber = 65535; int maxReferencedSegmentNumber = 0; - //int temporalPositionIdentifier = 0; + // int temporalPositionIdentifier = 0; int locationsInAcquisitionPhilips = 0; int imagesInAcquisition = 0; int highBit = 0; - //int sumSliceNumberMrPhilips = 0; + // int sumSliceNumberMrPhilips = 0; int sliceNumberMrPhilips = 0; int volumeNumber = -1; int gradientOrientationNumberPhilips = -1; int numberOfFrames = 0; int numberOfFramesICEdims = 0; - int frameNumberInSeries = -1; //issue837 - //int MRImageGradientOrientationNumber = 0; - //int minGradNum = kMaxDTI4D + 1; - //int maxGradNum = -1; + int frameNumberInSeries = -1; // issue837 + // int MRImageGradientOrientationNumber = 0; + // int minGradNum = kMaxDTI4D + 1; + // int maxGradNum = -1; int numberOfDynamicScans = 0; - //int mRSeriesAcquisitionNumber = 0; + // int mRSeriesAcquisitionNumber = 0; uint32_t lLength; uint32_t groupElement; size_t lPos = 0; bool isPhilipsDerived = false; - //bool isPhilipsDiffusion = false; - if (isPart10prefix) { //for part 10 files, skip preamble and prefix - lPos = 128 + 4; //4-byte signature starts at 128 + // bool isPhilipsDiffusion = false; + if (isPart10prefix) { // for part 10 files, skip preamble and prefix + lPos = 128 + 4; // 4-byte signature starts at 128 groupElement = buffer[lPos] | (buffer[lPos + 1] << 8) | (buffer[lPos + 2] << 16) | (buffer[lPos + 3] << 24); if (groupElement != kStart) printMessage("DICOM appears corrupt: first group:element should be 0x0002:0x0000 '%s'\n", fname); - } else { //no isPart10prefix - need to work out if this is explicit VR! + } else { // no isPart10prefix - need to work out if this is explicit VR! if (isVerbose > 1) printMessage("DICOM preamble and prefix missing: this is not a valid DICOM image.\n"); - //See Toshiba Aquilion images from https://www.aliza-dicom-viewer.com/download/datasets + // See Toshiba Aquilion images from https://www.aliza-dicom-viewer.com/download/datasets lLength = buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24); if (lLength > fileLen) { if (isVerbose > 1) @@ -4777,50 +4783,50 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } } char vr[2]; - //float intenScalePhilips = 0.0; + // float intenScalePhilips = 0.0; char scanOptionsSiemens[kDICOMStrLarge] = ""; char seriesTimeTxt[kDICOMStr] = ""; char acquisitionDateTimeTxt[kDICOMStr] = ""; char scanningSequenceSiemens[kDICOMStr] = ""; char imageType1st[kDICOMStr] = ""; bool isEncapsulatedData = false; - int diffusionDirectionTypeGE = 0; //issue690 - int seriesdiffusionDirectionTypeGE = 0; //issue690, 777 + int diffusionDirectionTypeGE = 0; // issue690 + int seriesdiffusionDirectionTypeGE = 0; // issue690, 777 int multiBandFactor = 0; int frequencyRows = 0; int numberOfImagesInMosaic = 0; int encapsulatedDataFragments = 0; - int encapsulatedDataFragmentStart = 0; //position of first FFFE,E000 for compressed images - int encapsulatedDataImageStart = 0; //position of 7FE0,0010 for compressed images (where actual image start should be start of first fragment) + int encapsulatedDataFragmentStart = 0; // position of first FFFE,E000 for compressed images + int encapsulatedDataImageStart = 0; // position of 7FE0,0010 for compressed images (where actual image start should be start of first fragment) bool isOrient = false; - //bool isDcm4Che = false; + // bool isDcm4Che = false; bool isMoCo = false; bool isPaletteColor = false; bool isInterpolated = false; bool isIconImageSequence = false; - #ifdef myDeidentificationMethod - bool isDeidentificationMethodCodeSequence = false; - #endif +#ifdef myDeidentificationMethod + bool isDeidentificationMethodCodeSequence = false; +#endif int sqDepthIcon = -1; bool isSwitchToImplicitVR = false; bool isSwitchToBigEndian = false; - bool isAtFirstPatientPosition = false; //for 3d and 4d files: flag is true for slices at same position as first slice + bool isAtFirstPatientPosition = false; // for 3d and 4d files: flag is true for slices at same position as first slice bool isMosaic = false; - bool isGEfieldMap = false; //issue501 + bool isGEfieldMap = false; // issue501 int patientPositionNum = 0; float B0Philips = -1.0; float vRLPhilips = 0.0; float vAPPhilips = 0.0; float vFHPhilips = 0.0; - //float TRPhilips = -1.0; + // float TRPhilips = -1.0; double acquisitionTimePhilips = -1.0; bool isPhase = false; bool isReal = false; bool isImaginary = false; bool isMagnitude = false; d.seriesNum = -1; - //start issue 372: - vec3 sliceV; //cross-product of kOrientation 0020,0037 + // start issue 372: + vec3 sliceV; // cross-product of kOrientation 0020,0037 sliceV.v[0] = NAN; float sliceMM[kMaxSlice2D]; int nSliceMM = 0; @@ -4830,17 +4836,17 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD float maxDynamicScanBeginTime = -INFINITY; float minPatientPosition[4] = {NAN, NAN, NAN, NAN}; float maxPatientPosition[4] = {NAN, NAN, NAN, NAN}; - //end issue 372 - //float frameAcquisitionDuration = 0.0; //issue369 + // end issue 372 + // float frameAcquisitionDuration = 0.0; //issue369 float patientPositionPrivate[4] = {NAN, NAN, NAN, NAN}; - float patientPosition[4] = {NAN, NAN, NAN, NAN}; //used to compute slice direction for Philips 4D - //float patientPositionPublic[4] = {NAN, NAN, NAN, NAN}; //used to compute slice direction for Philips 4D + float patientPosition[4] = {NAN, NAN, NAN, NAN}; // used to compute slice direction for Philips 4D + // float patientPositionPublic[4] = {NAN, NAN, NAN, NAN}; //used to compute slice direction for Philips 4D float patientPositionEndPhilips[4] = {NAN, NAN, NAN, NAN}; float patientPositionStartPhilips[4] = {NAN, NAN, NAN, NAN}; - //struct TDTI philDTI[kMaxDTI4D]; - //for (int i = 0; i < kMaxDTI4D; i++) + // struct TDTI philDTI[kMaxDTI4D]; + // for (int i = 0; i < kMaxDTI4D; i++) // philDTI[i].V[0] = -1; - //array for storing DimensionIndexValues + // array for storing DimensionIndexValues int numDimensionIndexValues = 0; #ifdef USING_R // Allocating a large array on the stack, as below, vexes valgrind and may cause overflow @@ -4853,16 +4859,16 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD for (int j = 0; j < MAX_NUMBER_OF_DIMENSIONS; j++) dcmDim[i].dimIdx[j] = 0; } -//http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_7.5.html -//The array nestPos tracks explicit lengths for Data Element Tag of Value (FFFE,E000) -//a delimiter (fffe,e000) can have an explicit length, in which case there is no delimiter (fffe,e00d) -// fffe,e000 can provide explicit lengths, to demonstrate ./dcmconv +ti ex.DCM im.DCM +// http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_7.5.html +// The array nestPos tracks explicit lengths for Data Element Tag of Value (FFFE,E000) +// a delimiter (fffe,e000) can have an explicit length, in which case there is no delimiter (fffe,e00d) +// fffe,e000 can provide explicit lengths, to demonstrate ./dcmconv +ti ex.DCM im.DCM #define kMaxNestPost 128 int nNestPos = 0; size_t nestPos[kMaxNestPost]; while ((d.imageStart == 0) && ((lPos + 8 + lFileOffset) < fileLen)) { -#ifndef myLoadWholeFileToReadHeader //read one segment at a time - if ((size_t)(lPos + 128) > MaxBufferSz) { //avoid overreading the file +#ifndef myLoadWholeFileToReadHeader // read one segment at a time + if ((size_t)(lPos + 128) > MaxBufferSz) { // avoid overreading the file lFileOffset = lFileOffset + lPos; if ((lFileOffset + MaxBufferSz) > (size_t)fileLen) MaxBufferSz = fileLen - lFileOffset; @@ -4884,76 +4890,77 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD isSwitchToBigEndian = false; d.isLittleEndian = false; groupElement = buffer[lPos + 1] | (buffer[lPos] << 8) | (buffer[lPos + 3] << 16) | (buffer[lPos + 2] << 24); - } //transfer syntax requests switching endian after group 0002 + } // transfer syntax requests switching endian after group 0002 if ((isSwitchToImplicitVR) && ((groupElement & 0xFFFF) != 2)) { isSwitchToImplicitVR = false; d.isExplicitVR = false; - } //transfer syntax requests switching VR after group 0001 - //uint32_t group = (groupElement & 0xFFFF); + } // transfer syntax requests switching VR after group 0001 + // uint32_t group = (groupElement & 0xFFFF); lPos += 4; uint32_t realGroupElement = groupElement; - //issue409 - icons can have their own sub-sections... keep reading until we get to the icon image? - //if ((groupElement == kItemDelimitationTag) || (groupElement == kSequenceDelimitationItemTag)) isIconImageSequence = false; - //if (groupElement == kItemTag) sqDepth++; + // issue409 - icons can have their own sub-sections... keep reading until we get to the icon image? + // if ((groupElement == kItemDelimitationTag) || (groupElement == kSequenceDelimitationItemTag)) isIconImageSequence = false; + // if (groupElement == kItemTag) sqDepth++; bool unNest = false; while ((nNestPos > 0) && (nestPos[nNestPos] <= (lFileOffset + lPos))) { nNestPos--; sqDepth--; unNest = true; - if ((sqDepthIcon >= 0) && (sqDepth <= sqDepthIcon)) { //issue415 + if ((sqDepthIcon >= 0) && (sqDepth <= sqDepthIcon)) { // issue415 sqDepthIcon = -1; isIconImageSequence = false; } } - if (groupElement == kItemDelimitationTag) { //end of item with undefined length + if (groupElement == kItemDelimitationTag) { // end of item with undefined length sqDepth--; unNest = true; - if ((sqDepthIcon >= 0) && (sqDepth <= sqDepthIcon)) { //issue415 + if ((sqDepthIcon >= 0) && (sqDepth <= sqDepthIcon)) { // issue415 sqDepthIcon = -1; isIconImageSequence = false; } } if (unNest) { if ((groupElement != kItemTag) && (sqDepth <= sqDepth04000561)) { - sqDepth04000561 = -1; //unlatch + sqDepth04000561 = -1; // unlatch } is2005140FSQ = false; is00089092SQ = false; if (sqDepth < 0) - sqDepth = 0; //should not happen, but protect for faulty anonymization - //if we leave the folder MREchoSequence 0018,9114 + sqDepth = 0; // should not happen, but protect for faulty anonymization + // if we leave the folder MREchoSequence 0018,9114 if ((nDimIndxVal > 0) && ((d.manufacturer == kMANUFACTURER_UNKNOWN) || (d.manufacturer == kMANUFACTURER_CANON) || (d.manufacturer == kMANUFACTURER_BRUKER) || (d.manufacturer == kMANUFACTURER_PHILIPS)) && (sqDepth00189114 >= sqDepth)) { - sqDepth00189114 = -1; //triggered - //printf("slice %d---> 0020,9157 = %d %d %d\n", inStackPositionNumber, d.dimensionIndexValues[0], d.dimensionIndexValues[1], d.dimensionIndexValues[2]); - // d.aslFlags = kASL_FLAG_PHILIPS_LABEL; kASL_FLAG_PHILIPS_LABEL + sqDepth00189114 = -1; // triggered + // printf("slice %d---> 0020,9157 = %d %d %d\n", inStackPositionNumber, d.dimensionIndexValues[0], d.dimensionIndexValues[1], d.dimensionIndexValues[2]); + // d.aslFlags = kASL_FLAG_PHILIPS_LABEL; kASL_FLAG_PHILIPS_LABEL if ((nDimIndxVal > 1) && (volumeNumber > 0) && (inStackPositionNumber > 0) && ((d.aslFlags == kASL_FLAG_PHILIPS_LABEL) || (d.aslFlags == kASL_FLAG_PHILIPS_CONTROL))) { isKludgeIssue533 = true; for (int i = 0; i < nDimIndxVal; i++) d.dimensionIndexValues[i] = 0; int phase = d.phaseNumber; - if (d.phaseNumber < 0) phase = 0; //if not set: we are saving as UINT - d.dimensionIndexValues[0] = inStackPositionNumber; //dim[3] slice changes fastest - d.dimensionIndexValues[1] = phase; //dim[4] successive volumes are phase - d.dimensionIndexValues[2] = d.aslFlags == kASL_FLAG_PHILIPS_LABEL; //dim[5] Control/Label - d.dimensionIndexValues[3] = volumeNumber; //dim[6] Repeat changes slowest - nDimIndxVal = 4; //slice < phase < control/label < volume - //printf("slice %d phase %d control/label %d repeat %d\n", inStackPositionNumber, d.phaseNumber, d.aslFlags == kASL_FLAG_PHILIPS_LABEL, volumeNumber); + if (d.phaseNumber < 0) + phase = 0; // if not set: we are saving as UINT + d.dimensionIndexValues[0] = inStackPositionNumber; // dim[3] slice changes fastest + d.dimensionIndexValues[1] = phase; // dim[4] successive volumes are phase + d.dimensionIndexValues[2] = d.aslFlags == kASL_FLAG_PHILIPS_LABEL; // dim[5] Control/Label + d.dimensionIndexValues[3] = volumeNumber; // dim[6] Repeat changes slowest + nDimIndxVal = 4; // slice < phase < control/label < volume + // printf("slice %d phase %d control/label %d repeat %d\n", inStackPositionNumber, d.phaseNumber, d.aslFlags == kASL_FLAG_PHILIPS_LABEL, volumeNumber); } if ((volumeNumber == 1) && (acquisitionTimePhilips >= 0.0) && (inStackPositionNumber > 0)) { d.CSA.sliceTiming[inStackPositionNumber - 1] = acquisitionTimePhilips; - //printf("%d\t%f\n", inStackPositionNumber, acquisitionTimePhilips); - acquisitionTimePhilips = - 1.0; + // printf("%d\t%f\n", inStackPositionNumber, acquisitionTimePhilips); + acquisitionTimePhilips = -1.0; } int ndim = nDimIndxVal; - //issue 839 + // issue 839 patientPosition1[numDimensionIndexValues] = patientPosition[1]; patientPosition2[numDimensionIndexValues] = patientPosition[2]; patientPosition3[numDimensionIndexValues] = patientPosition[3]; if (inStackPositionNumber > 0) { - //for images without SliceNumberMrPhilips (2001,100A) + // for images without SliceNumberMrPhilips (2001,100A) int sliceNumber = inStackPositionNumber; - //printf("slice %d \n", sliceNumber); + // printf("slice %d \n", sliceNumber); if ((!isIssue839) && (sliceNumber == 1) && (!isnan(patientPositionStartPhilips[1]))) { float dx = sqrt( pow(patientPositionStartPhilips[1] - patientPosition[1], 2) + @@ -4991,13 +4998,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD dimensionIndexOrder[i] = i; // Bruker Enhanced MR IOD: reorder dimensions to ensure InStackPositionNumber corresponds to the first one // This will ensure correct ordering of slices in 4D datasets - //Canon and Bruker reverse dimensionIndexItem order relative to Philips: new versions introduce compareTDCMdimRev - //printf("%d: %d %d %d %d\n", ndim, d.dimensionIndexValues[0], d.dimensionIndexValues[1], d.dimensionIndexValues[2], d.dimensionIndexValues[3]); - if ((philMRImageDiffVolumeNumber > 0) && (sliceNumberMrPhilips > 0)) { //issue546: 2005,1596 provides temporal order + // Canon and Bruker reverse dimensionIndexItem order relative to Philips: new versions introduce compareTDCMdimRev + // printf("%d: %d %d %d %d\n", ndim, d.dimensionIndexValues[0], d.dimensionIndexValues[1], d.dimensionIndexValues[2], d.dimensionIndexValues[3]); + if ((philMRImageDiffVolumeNumber > 0) && (sliceNumberMrPhilips > 0)) { // issue546: 2005,1596 provides temporal order dcmDim[numDimensionIndexValues].dimIdx[0] = 1; dcmDim[numDimensionIndexValues].dimIdx[1] = sliceNumberMrPhilips; dcmDim[numDimensionIndexValues].dimIdx[2] = philMRImageDiffVolumeNumber; - } else { + } else { for (int i = 0; i < ndim; i++) dcmDim[numDimensionIndexValues].dimIdx[i] = d.dimensionIndexValues[dimensionIndexOrder[i]]; } @@ -5011,15 +5018,15 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD dcmDim[numDimensionIndexValues].intenScalePhilips = d.intenScalePhilips; dcmDim[numDimensionIndexValues].RWVScale = d.RWVScale; dcmDim[numDimensionIndexValues].RWVIntercept = d.RWVIntercept; - //printf("%d %d %g????\n", isTriggerSynced, isProspectiveSynced, d.triggerDelayTime); - //TODO533: isKludgeIssue533 alias Philips ASL as FrameDuration? - //if ((d.triggerDelayTime > 0.0) && (d.manufacturer == kMANUFACTURER_PHILIPS) && (d.aslFlags != kASL_FLAG_NONE)) - //printf(">>>%g\n", d.triggerDelayTime); - //if ((isASL) || (d.aslFlags != kASL_FLAG_NONE)) d.triggerDelayTime = 0.0; //see dcm_qa_philips_asl - //if ((d.manufacturer == kMANUFACTURER_PHILIPS) && ((!isTriggerSynced) || (!isProspectiveSynced)) ) //issue408 + // printf("%d %d %g????\n", isTriggerSynced, isProspectiveSynced, d.triggerDelayTime); + // TODO533: isKludgeIssue533 alias Philips ASL as FrameDuration? + // if ((d.triggerDelayTime > 0.0) && (d.manufacturer == kMANUFACTURER_PHILIPS) && (d.aslFlags != kASL_FLAG_NONE)) + // printf(">>>%g\n", d.triggerDelayTime); + // if ((isASL) || (d.aslFlags != kASL_FLAG_NONE)) d.triggerDelayTime = 0.0; //see dcm_qa_philips_asl + // if ((d.manufacturer == kMANUFACTURER_PHILIPS) && ((!isTriggerSynced) || (!isProspectiveSynced)) ) //issue408 // d.triggerDelayTime = 0.0; - if (isSameFloat(MRImageDynamicScanBeginTime * 1000.0, d.triggerDelayTime) ) - dcmDim[numDimensionIndexValues].triggerDelayTime = 0.0; //issue395 + if (isSameFloat(MRImageDynamicScanBeginTime * 1000.0, d.triggerDelayTime)) + dcmDim[numDimensionIndexValues].triggerDelayTime = 0.0; // issue395 else dcmDim[numDimensionIndexValues].triggerDelayTime = d.triggerDelayTime; dcmDim[numDimensionIndexValues].V[0] = -1.0; @@ -5029,12 +5036,12 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD for (int i = 0; i < ndim; i++) printMessage("%d ", d.dimensionIndexValues[i]); printMessage("]\n"); - //printMessage("B0= %g num=%d\n", B0Philips, gradNum); + // printMessage("B0= %g num=%d\n", B0Philips, gradNum); } else return d; #endif - //next: add diffusion if reported - if (B0Philips >= 0.0) { //diffusion parameters + // next: add diffusion if reported + if (B0Philips >= 0.0) { // diffusion parameters // Philips does not always provide 2005,1413 (MRImageGradientOrientationNumber) and sometimes after dimensionIndexValues /*int gradNum = 0; for (int i = 0; i < ndim; i++) @@ -5049,19 +5056,19 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD we could also increment gradNum for ADC if we wanted... */ if (isPhilipsDerived) { - //gradNum ++; + // gradNum ++; B0Philips = 2000.0; vRLPhilips = 0.0; vAPPhilips = 0.0; vFHPhilips = 0.0; } if (B0Philips == 0.0) { - //printMessage(" DimensionIndexValues grad %d b=%g vec=%gx%gx%g\n", gradNum, B0Philips, vRLPhilips, vAPPhilips, vFHPhilips); + // printMessage(" DimensionIndexValues grad %d b=%g vec=%gx%gx%g\n", gradNum, B0Philips, vRLPhilips, vAPPhilips, vFHPhilips); vRLPhilips = 0.0; vAPPhilips = 0.0; vFHPhilips = 0.0; } - //if ((MRImageGradientOrientationNumber > 0) && ((gradNum != MRImageGradientOrientationNumber)) break; + // if ((MRImageGradientOrientationNumber > 0) && ((gradNum != MRImageGradientOrientationNumber)) break; /*if (gradNum < minGradNum) minGradNum = gradNum; if (gradNum >= maxGradNum) maxGradNum = gradNum; if (gradNum >= kMaxDTI4D) { @@ -5078,18 +5085,18 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD dcmDim[numDimensionIndexValues].V[2] = vAPPhilips; dcmDim[numDimensionIndexValues].V[3] = vFHPhilips; isPhilipsDerived = false; - //printMessage(" DimensionIndexValues grad %d b=%g vec=%gx%gx%g\n", gradNum, B0Philips, vRLPhilips, vAPPhilips, vFHPhilips); + // printMessage(" DimensionIndexValues grad %d b=%g vec=%gx%gx%g\n", gradNum, B0Philips, vRLPhilips, vAPPhilips, vFHPhilips); //!!! 16032018 : next line as well as definition of B0Philips may need to be set to zero if Philips omits DiffusionBValue tag for B=0 - B0Philips = -1.0; //Philips may skip reporting B-values for B=0 volumes, so zero these + B0Philips = -1.0; // Philips may skip reporting B-values for B=0 volumes, so zero these vRLPhilips = 0.0; vAPPhilips = 0.0; vFHPhilips = 0.0; - //MRImageGradientOrientationNumber = 0; - } //diffusion parameters + // MRImageGradientOrientationNumber = 0; + } // diffusion parameters numDimensionIndexValues++; - nDimIndxVal = -1; //we need DimensionIndexValues - } //record dimensionIndexValues slice information - } //groupElement == kItemDelimitationTag : delimit item exits folder + nDimIndxVal = -1; // we need DimensionIndexValues + } // record dimensionIndexValues slice information + } // groupElement == kItemDelimitationTag : delimit item exits folder uint32_t itemTagLength = 0; if (groupElement == kItemTag) { itemTagLength = dcmInt(4, &buffer[lPos], d.isLittleEndian); @@ -5102,7 +5109,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } lLength = 4; sqDepth++; - //return d; + // return d; } else if (((groupElement == kItemDelimitationTag) || (groupElement == kSequenceDelimitationItemTag)) && (!isEncapsulatedData)) { vr[0] = 'N'; vr[1] = 'A'; @@ -5110,75 +5117,76 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } else if (d.isExplicitVR) { vr[0] = buffer[lPos]; vr[1] = buffer[lPos + 1]; - if (buffer[lPos + 1] < 'A') { //implicit vr with 32-bit length + if (buffer[lPos + 1] < 'A') { // implicit vr with 32-bit length if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos + 1] << 8) | (buffer[lPos + 2] << 16) | (buffer[lPos + 3] << 24); else lLength = buffer[lPos + 3] | (buffer[lPos + 2] << 8) | (buffer[lPos + 1] << 16) | (buffer[lPos] << 24); lPos += 4; - } else if (((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'N')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'C')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'R')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'T')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'V')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'B')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'D')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'F')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'L')) | ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'V')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'W')) || ((buffer[lPos] == 'S') && (buffer[lPos + 1] == 'V'))) { //VR= UN, OB, OW, SQ || ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q')) - //for example of UC/UR/UV/OD/OF/OL/OV/SV see VR conformance test https://www.aliza-dicom-viewer.com/download/datasets - lPos = lPos + 4; //skip 2 byte VR string and 2 reserved bytes = 4 bytes + } else if (((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'N')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'C')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'R')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'T')) || ((buffer[lPos] == 'U') && (buffer[lPos + 1] == 'V')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'B')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'D')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'F')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'L')) | ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'V')) || ((buffer[lPos] == 'O') && (buffer[lPos + 1] == 'W')) || ((buffer[lPos] == 'S') && (buffer[lPos + 1] == 'V'))) { // VR= UN, OB, OW, SQ || ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q')) + // for example of UC/UR/UV/OD/OF/OL/OV/SV see VR conformance test https://www.aliza-dicom-viewer.com/download/datasets + lPos = lPos + 4; // skip 2 byte VR string and 2 reserved bytes = 4 bytes if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos + 1] << 8) | (buffer[lPos + 2] << 16) | (buffer[lPos + 3] << 24); else lLength = buffer[lPos + 3] | (buffer[lPos + 2] << 8) | (buffer[lPos + 1] << 16) | (buffer[lPos] << 24); - lPos = lPos + 4; //skip 4 byte length + lPos = lPos + 4; // skip 4 byte length } else if ((buffer[lPos] == 'S') && (buffer[lPos + 1] == 'Q')) { - lLength = 8; //Sequence Tag - //printMessage(" !!!SQ\t%04x,%04x\n", groupElement & 65535,groupElement>>16); - } else { //explicit VR with 16-bit length + lLength = 8; // Sequence Tag + // printMessage(" !!!SQ\t%04x,%04x\n", groupElement & 65535,groupElement>>16); + } else { // explicit VR with 16-bit length if ((d.isLittleEndian)) lLength = buffer[lPos + 2] | (buffer[lPos + 3] << 8); else lLength = buffer[lPos + 3] | (buffer[lPos + 2] << 8); - lPos += 4; //skip 2 byte VR string and 2 length bytes = 4 bytes + lPos += 4; // skip 2 byte VR string and 2 length bytes = 4 bytes } - } else { //implicit VR + } else { // implicit VR vr[0] = 'U'; vr[1] = 'N'; if (d.isLittleEndian) lLength = buffer[lPos] | (buffer[lPos + 1] << 8) | (buffer[lPos + 2] << 16) | (buffer[lPos + 3] << 24); else lLength = buffer[lPos + 3] | (buffer[lPos + 2] << 8) | (buffer[lPos + 1] << 16) | (buffer[lPos] << 24); - lPos += 4; //we have loaded the 32-bit length - if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isSQ(groupElement, true))) { //https://github.com/rordenlab/dcm2niix/issues/144 + lPos += 4; // we have loaded the 32-bit length + if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isSQ(groupElement, true))) { // https://github.com/rordenlab/dcm2niix/issues/144 vr[0] = 'S'; vr[1] = 'Q'; - lLength = 0; //Do not skip kItemTag - required to determine nesting of Philips Enhanced + lLength = 0; // Do not skip kItemTag - required to determine nesting of Philips Enhanced } - if ((d.manufacturer != kMANUFACTURER_PHILIPS) && (isSQ(groupElement, false))) { //https://github.com/rordenlab/dcm2niix/issues/144 + if ((d.manufacturer != kMANUFACTURER_PHILIPS) && (isSQ(groupElement, false))) { // https://github.com/rordenlab/dcm2niix/issues/144 vr[0] = 'S'; vr[1] = 'Q'; - lLength = 0; //Do not skip kItemTag - required to determine nesting of Philips Enhanced + lLength = 0; // Do not skip kItemTag - required to determine nesting of Philips Enhanced } - if (groupElement == kDiffusionGradientDirectionSQ) { //https://github.com/rordenlab/dcm2niix/issues/144 + if (groupElement == kDiffusionGradientDirectionSQ) { // https://github.com/rordenlab/dcm2niix/issues/144 vr[0] = 'S'; vr[1] = 'Q'; - lLength = 0; //Do not skip kItemTag - required to determine nesting of Philips Enhanced + lLength = 0; // Do not skip kItemTag - required to determine nesting of Philips Enhanced } - } //if explicit else implicit VR + } // if explicit else implicit VR if ((lLength == 0xFFFFFFFF) && (vr[0] == 'O') && (vr[1] == 'B') && (isIconImageSequence)) { - lLength = 0; //encapuslated data of unspecified length - } //issue713 + lLength = 0; // encapuslated data of unspecified length + } // issue713 if (lLength == 0xFFFFFFFF) { - lLength = 8; //SQ (Sequences) use 0xFFFFFFFF [4294967295] to denote unknown length - //09032018 - do not count these as SQs: Horos does not count even groups - //uint32_t special = dcmInt(4,&buffer[lPos],d.isLittleEndian); - //http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_7.5.html - //if (special != ksqDelim) { + lLength = 8; // SQ (Sequences) use 0xFFFFFFFF [4294967295] to denote unknown length + // 09032018 - do not count these as SQs: Horos does not count even groups + // uint32_t special = dcmInt(4,&buffer[lPos],d.isLittleEndian); + // http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_7.5.html + // if (special != ksqDelim) { vr[0] = 'S'; vr[1] = 'Q'; //} } if ((groupElement == kItemTag) && (vr[0] == 'O') && (vr[1] == 'B') && (isIconImageSequence)) - lLength += itemTagLength; //issue713 - //printf("issue713::%c%c %04x,%04x %d@%lu\n", vr[0], vr[1], groupElement & 65535, groupElement >> 16, lLength, lPos); - if ((groupElement == kItemTag) && (isEncapsulatedData)) { //use this to find image fragment for compressed datasets, e.g. JPEG transfer syntax + lLength += itemTagLength; // issue713 + // printf("issue713::%c%c %04x,%04x %d@%lu\n", vr[0], vr[1], groupElement & 65535, groupElement >> 16, lLength, lPos); + if ((groupElement == kItemTag) && (isEncapsulatedData)) { // use this to find image fragment for compressed datasets, e.g. JPEG transfer syntax d.imageBytes = dcmInt(4, &buffer[lPos], d.isLittleEndian); lPos = lPos + 4; lLength = d.imageBytes; - if (d.imageBytes <= 0) goto skipRemap; + if (d.imageBytes <= 0) + goto skipRemap; if (d.imageBytes > 24) { /*if (encapsulatedDataFragments < kMaxDTI4D) { dti4D->fragmentOffset[encapsulatedDataFragments] = (int)lPos + (int)lFileOffset; @@ -5190,25 +5198,25 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } } if ((sqDepth04000561 >= 0) || (is00089092SQ)) - groupElement = kUnused; //ignore Original Attributes + groupElement = kUnused; // ignore Original Attributes if ((isIconImageSequence) && ((groupElement & 0x0028) == 0x0028)) - groupElement = kUnused; //ignore icon dimensions -#ifdef salvageAgfa //issue435 - //Handle remapping using integers, and slower but simpler approach is with strings: - // https://github.com/pydicom/pydicom/blob/master/pydicom/_private_dict.py + groupElement = kUnused; // ignore icon dimensions +#ifdef salvageAgfa // issue435 + // Handle remapping using integers, and slower but simpler approach is with strings: + // https://github.com/pydicom/pydicom/blob/master/pydicom/_private_dict.py if (((groupElement & 65535) % 2) == 0) - goto skipRemap; //remap odd (private) groups - //printf("tag %04x,%04x\n", groupElement & 65535, groupElement >> 16); - if (((groupElement >> 16) >= 0x10) && ((groupElement >> 16) <= 0xFF)) { //tags (gggg,0010-00FF) may define new remapping - //if remapping tag - //first: see if this remapping overwrites existing tag - uint32_t privateCreatorMask = 0; //0 -> none - uint32_t privateCreatorRemap = 0; //0 -> none + goto skipRemap; // remap odd (private) groups + // printf("tag %04x,%04x\n", groupElement & 65535, groupElement >> 16); + if (((groupElement >> 16) >= 0x10) && ((groupElement >> 16) <= 0xFF)) { // tags (gggg,0010-00FF) may define new remapping + // if remapping tag + // first: see if this remapping overwrites existing tag + uint32_t privateCreatorMask = 0; // 0 -> none + uint32_t privateCreatorRemap = 0; // 0 -> none privateCreatorMask = (groupElement & 65535) + ((groupElement & 0xFFFF0000) << 8); if (nRemaps > 0) { int j = 0; - for (int i = 0; i < nRemaps; i++) //remove duplicate remapping - //copy all remaps except exact match + for (int i = 0; i < nRemaps; i++) // remove duplicate remapping + // copy all remaps except exact match if (privateCreatorMasks[i] != privateCreatorMask) { privateCreatorMasks[j] = privateCreatorMasks[i]; privateCreatorRemaps[j] = privateCreatorRemaps[i]; @@ -5216,16 +5224,16 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } nRemaps = j; } - //see if this is known private vendor tag + // see if this is known private vendor tag privateCreatorRemap = 0; char privateCreator[kDICOMStr]; dcmStr(lLength, &buffer[lPos], privateCreator); // int s_len = strlen(privateCreator); for (int i = 0; i < s_len; i++) - privateCreator[i] = toupper(privateCreator[i]); - //next lines determine remapping, append as needed - //Siemens https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/siemens.tpl + privateCreator[i] = toupper(privateCreator[i]); + // next lines determine remapping, append as needed + // Siemens https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/siemens.tpl if (strstr(privateCreator, "SIEMENS MR HEADER") != NULL) privateCreatorRemap = 0x0019 + (0x1000 << 16); if (strstr(privateCreator, "SIEMENS MR SDS 01") != NULL) @@ -5236,7 +5244,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD privateCreatorRemap = 0x0029 + (0x1000 << 16); if (strstr(privateCreator, "SIEMENS MR HEADER") != NULL) privateCreatorRemap = 0x0051 + (0x1000 << 16); - //GE https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/gems.tpl + // GE https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/gems.tpl if (strstr(privateCreator, "GEMS_ACQU_01") != NULL) privateCreatorRemap = 0x0019 + (0x1000 << 16); if (strstr(privateCreator, "GEMS_RELA_01") != NULL) @@ -5245,13 +5253,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD privateCreatorRemap = 0x0025 + (0x1000 << 16); if (strstr(privateCreator, "GEMS_PARM_01") != NULL) privateCreatorRemap = 0x0043 + (0x1000 << 16); - //ELSCINT https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/elscint.tpl + // ELSCINT https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/elscint.tpl int grp = (groupElement & 65535); if ((grp == 0x07a1) && (strstr(privateCreator, "ELSCINT1") != NULL)) privateCreatorRemap = 0x07a1 + (0x1000 << 16); if ((grp == 0x07a3) && (strstr(privateCreator, "ELSCINT1") != NULL)) privateCreatorRemap = 0x07a3 + (0x1000 << 16); - //Philips https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/philips.tpl + // Philips https://github.com/dcm4che/dcm4che/blob/master/dcm4che-dict/src/main/dicom3tools/libsrc/standard/elmdict/philips.tpl if (strstr(privateCreator, "PHILIPS IMAGING") != NULL) { if (strstr(privateCreator, "DD 001") != NULL) privateCreatorRemap = 0x2001 + (0x1000 << 16); @@ -5276,26 +5284,26 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (strstr(privateCreator, "DD 005") != NULL) privateCreatorRemap = 0x2005 + (0x1400 << 16); } - //UIH https://github.com/neurolabusc/dcm_qa_uih + // UIH https://github.com/neurolabusc/dcm_qa_uih if (strstr(privateCreator, "Image Private Header") != NULL) privateCreatorRemap = 0x0065 + (0x1000 << 16); - //sanity check: group should match + // sanity check: group should match if (grp != (int)(privateCreatorRemap & 65535)) privateCreatorRemap = 0; if (privateCreatorRemap == 0) - goto skipRemap; //this is not a known private group + goto skipRemap; // this is not a known private group if (privateCreatorRemap == privateCreatorMask) - goto skipRemap; //the remapping and mask are identical 2005,1000 -> 2005,1000 + goto skipRemap; // the remapping and mask are identical 2005,1000 -> 2005,1000 if ((nRemaps + 1) >= kMaxRemaps) - goto skipRemap; //all slots full (should never happen) - //add new remapping + goto skipRemap; // all slots full (should never happen) + // add new remapping privateCreatorMasks[nRemaps] = privateCreatorMask; privateCreatorRemaps[nRemaps] = privateCreatorRemap; - //printf("new remapping %04x,%04x -> %04x,%04x\n", privateCreatorMask & 65535, privateCreatorMask >> 16, privateCreatorRemap & 65535, privateCreatorRemap >> 16); + // printf("new remapping %04x,%04x -> %04x,%04x\n", privateCreatorMask & 65535, privateCreatorMask >> 16, privateCreatorRemap & 65535, privateCreatorRemap >> 16); if (isVerbose > 1) printMessage("new remapping (%d) %04x,%02xxy -> %04x,%02xxy\n", nRemaps, privateCreatorMask & 65535, privateCreatorMask >> 24, privateCreatorRemap & 65535, privateCreatorRemap >> 24); nRemaps += 1; - //for (int i = 0; i < nRemaps; i++) + // for (int i = 0; i < nRemaps; i++) // printf(" %d = %04x,%02xxy -> %04x,%02xxy\n", i, privateCreatorMasks[i] & 65535, privateCreatorMasks[i] >> 24, privateCreatorRemaps[i] & 65535, privateCreatorRemaps[i] >> 24); goto skipRemap; } @@ -5314,38 +5322,38 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } #endif // salvageAgfa skipRemap: - if ((lLength % 2) != 0) { //https://www.nitrc.org/forum/forum.php?thread_id=11827&forum_id=4703 + if ((lLength % 2) != 0) { // https://www.nitrc.org/forum/forum.php?thread_id=11827&forum_id=4703 printMessage("Illegal DICOM tag %04x,%04x (odd element length %d): %s\n", groupElement & 65535, groupElement >> 16, lLength, fname); - //proper to return here, but we can carry on as a hail mary - // d.isValid = false; - //return d; - } - if (lLength > 0) //issue695: skip empty tags, "gdcmanon --dumb --empty 0018,0089 good.dcm bad.dcm" - #ifdef myDeidentificationMethod - if(sqDepth < 1 && isDeidentificationMethodCodeSequence && groupElement != kItemDelimitationTag && groupElement != kItemTag ) - isDeidentificationMethodCodeSequence = false; - #endif // myDeidentificationMethod + // proper to return here, but we can carry on as a hail mary + // d.isValid = false; + // return d; + } + if (lLength > 0) // issue695: skip empty tags, "gdcmanon --dumb --empty 0018,0089 good.dcm bad.dcm" +#ifdef myDeidentificationMethod + if (sqDepth < 1 && isDeidentificationMethodCodeSequence && groupElement != kItemDelimitationTag && groupElement != kItemTag) + isDeidentificationMethodCodeSequence = false; +#endif // myDeidentificationMethod switch (groupElement) { case kMediaStorageSOPClassUID: { char mediaUID[kDICOMStr]; dcmStr(lLength, &buffer[lPos], mediaUID); - //Philips "XX_" files - //see https://github.com/rordenlab/dcm2niix/issues/328 - if (strstr(mediaUID, "1.2.840.10008.5.1.4.1.1.66.4") != NULL) //Segmentation Storage - d.isDerived = true; //Segmentation IOD, issue 572 + // Philips "XX_" files + // see https://github.com/rordenlab/dcm2niix/issues/328 + if (strstr(mediaUID, "1.2.840.10008.5.1.4.1.1.66.4") != NULL) // Segmentation Storage + d.isDerived = true; // Segmentation IOD, issue 572 else if (strstr(mediaUID, "1.2.840.10008.5.1.4.1.1.66") != NULL) - d.isRawDataStorage = true; //e.g. Raw Data IOD, https://dicom.nema.org/dicom/2013/output/chtml/part04/sect_i.4.html + d.isRawDataStorage = true; // e.g. Raw Data IOD, https://dicom.nema.org/dicom/2013/output/chtml/part04/sect_i.4.html if (strstr(mediaUID, "1.3.46.670589.11.0.0.12.1") != NULL) - d.isRawDataStorage = true; //Private MR Spectrum Storage + d.isRawDataStorage = true; // Private MR Spectrum Storage if (strstr(mediaUID, "1.3.46.670589.11.0.0.12.2") != NULL) - d.isRawDataStorage = true; //Private MR Series Data Storage + d.isRawDataStorage = true; // Private MR Series Data Storage if (strstr(mediaUID, "1.3.46.670589.11.0.0.12.4") != NULL) - d.isRawDataStorage = true; //Private MR Examcard Storage + d.isRawDataStorage = true; // Private MR Examcard Storage if (d.isRawDataStorage) d.isDerived = true; if (d.isRawDataStorage) printMessage("Skipping non-image DICOM: %s\n", fname); - //Philips "PS_" files + // Philips "PS_" files if (strstr(mediaUID, "1.2.840.10008.5.1.4.1.1.11.1") != NULL) d.isGrayscaleSoftcopyPresentationState = true; if (d.isGrayscaleSoftcopyPresentationState) @@ -5353,73 +5361,73 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; } case kMediaStorageSOPInstanceUID: { // 0002, 0003 - //char SOPInstanceUID[kDICOMStr]; + // char SOPInstanceUID[kDICOMStr]; dcmStr(lLength, &buffer[lPos], d.instanceUID); - //printMessage(">>%s\n", d.seriesInstanceUID); + // printMessage(">>%s\n", d.seriesInstanceUID); d.instanceUidCrc = mz_crc32X((unsigned char *)&d.instanceUID, strlen(d.instanceUID)); break; } case kTransferSyntax: { - //if (sqDepth04000561 >= 0) break; + // if (sqDepth04000561 >= 0) break; char transferSyntax[kDICOMStr]; strcpy(transferSyntax, ""); dcmStr(lLength, &buffer[lPos], transferSyntax); #ifdef USING_DCM2NIIXFSWRAPPER - strcpy(d.transferSyntax, transferSyntax); + strcpy(d.transferSyntax, transferSyntax); #endif if (strcmp(transferSyntax, "1.2.840.10008.1.2.1") == 0) - ; //default isExplicitVR=true; //d.isLittleEndian=true + ; // default isExplicitVR=true; //d.isLittleEndian=true else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.50") == 0) { d.compressionScheme = kCompress50; - //printMessage("Lossy JPEG: please decompress with Osirix or dcmdjpg. %s\n", transferSyntax); - //d.imageStart = 1; //abort as invalid (imageStart MUST be >128) + // printMessage("Lossy JPEG: please decompress with Osirix or dcmdjpg. %s\n", transferSyntax); + // d.imageStart = 1; //abort as invalid (imageStart MUST be >128) } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.51") == 0) { d.compressionScheme = kCompress50; } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.57") == 0) { - //d.isCompressed = true; - //https://www.medicalconnections.co.uk/kb/Transfer_Syntax should be SOF = 0xC3 + // d.isCompressed = true; + // https://www.medicalconnections.co.uk/kb/Transfer_Syntax should be SOF = 0xC3 d.compressionScheme = kCompressC3; - //printMessage("Ancient JPEG-lossless (SOF type 0xc3): please check conversion\n"); + // printMessage("Ancient JPEG-lossless (SOF type 0xc3): please check conversion\n"); } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.70") == 0) { d.compressionScheme = kCompressC3; } else if ((strcmp(transferSyntax, "1.2.840.10008.1.2.4.80") == 0) || (strcmp(transferSyntax, "1.2.840.10008.1.2.4.81") == 0)) { #if defined(myEnableJPEGLS) || defined(myEnableJPEGLS1) d.compressionScheme = kCompressJPEGLS; #else - printWarning("Unsupported transfer syntax '%s' (decode with 'dcmdjpls jpg.dcm raw.dcm' or 'gdcmconv -w jpg.dcm raw.dcm', or recompile dcm2niix with JPEGLS support)\n", transferSyntax); - d.imageStart = 1; //abort as invalid (imageStart MUST be >128) + printWarning("Unsupported transfer syntax '%s' (decode with 'dcmdjpls jpg.dcm raw.dcm' or 'gdcmconv -w jpg.dcm raw.dcm', or recompile dcm2niix with JPEGLS support)\n", transferSyntax); + d.imageStart = 1; // abort as invalid (imageStart MUST be >128) #endif } else if (strcmp(transferSyntax, "1.3.46.670589.33.1.4.1") == 0) { d.compressionScheme = kCompressPMSCT_RLE1; - //printMessage("Unsupported transfer syntax '%s' (decode with rle2img)\n",transferSyntax); - //d.imageStart = 1; //abort as invalid (imageStart MUST be >128) + // printMessage("Unsupported transfer syntax '%s' (decode with rle2img)\n",transferSyntax); + // d.imageStart = 1; //abort as invalid (imageStart MUST be >128) } else if ((compressFlag != kCompressNone) && (strcmp(transferSyntax, "1.2.840.10008.1.2.4.90") == 0)) { d.compressionScheme = kCompressYes; - //printMessage("JPEG2000 Lossless support is new: please validate conversion\n"); + // printMessage("JPEG2000 Lossless support is new: please validate conversion\n"); } else if ((strcmp(transferSyntax, "1.2.840.10008.1.2.1.99") == 0)) { - //n.b. Deflate compression applied applies to the encoding of the **entire** DICOM Data Set, not just image data - // see https://www.medicalconnections.co.uk/kb/Transfer-Syntax/ - //#ifndef myDisableZLib - //d.compressionScheme = kCompressDeflate; - //#else + // n.b. Deflate compression applied applies to the encoding of the **entire** DICOM Data Set, not just image data + // see https://www.medicalconnections.co.uk/kb/Transfer-Syntax/ + // #ifndef myDisableZLib + // d.compressionScheme = kCompressDeflate; + // #else printWarning("Unsupported transfer syntax '%s' (inflate files with 'dcmconv +te gz.dcm raw.dcm' or 'gdcmconv -w gz.dcm raw.dcm)'\n", transferSyntax); - d.imageStart = 1; //abort as invalid (imageStart MUST be >128) - //#endif + d.imageStart = 1; // abort as invalid (imageStart MUST be >128) + // #endif } else if ((compressFlag != kCompressNone) && (strcmp(transferSyntax, "1.2.840.10008.1.2.4.91") == 0)) { d.compressionScheme = kCompressYes; - //printMessage("JPEG2000 support is new: please validate conversion\n"); + // printMessage("JPEG2000 support is new: please validate conversion\n"); } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.5") == 0) - d.compressionScheme = kCompressRLE; //run length + d.compressionScheme = kCompressRLE; // run length else if (strcmp(transferSyntax, "1.2.840.10008.1.2.2") == 0) - isSwitchToBigEndian = true; //isExplicitVR=true; + isSwitchToBigEndian = true; // isExplicitVR=true; else if (strcmp(transferSyntax, "1.2.840.10008.1.2") == 0) - isSwitchToImplicitVR = true; //d.isLittleEndian=true + isSwitchToImplicitVR = true; // d.isLittleEndian=true else { if (lLength < 1) //"1.2.840.10008.1.2" printWarning("Missing transfer syntax: assuming default (1.2.840.10008.1.2)\n"); else { printWarning("Unsupported transfer syntax '%s' (see www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage)\n", transferSyntax); - d.imageStart = 1; //abort as invalid (imageStart MUST be >128) + d.imageStart = 1; // abort as invalid (imageStart MUST be >128) } } break; @@ -5461,7 +5469,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (is1st) strcpy(imageType1st, d.imageType); if ((slen > 5) && strstr(d.imageType, "_MOCO_")) { - //d.isDerived = true; //this would have 'i- y' skip MoCo images + // d.isDerived = true; //this would have 'i- y' skip MoCo images isMoCo = true; } if ((slen > 5) && strstr(d.imageType, "B0") && strstr(d.imageType, "MAP")) @@ -5476,21 +5484,21 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.isDerived = true; if ((slen > 12) && strstr(d.imageType, "_DIFFUSION_")) d.isDiffusion = true; - //if (strcmp(transferSyntax, "ORIGINAL_PRIMARY_M_ND_MOSAIC") == 0) + // if (strcmp(transferSyntax, "ORIGINAL_PRIMARY_M_ND_MOSAIC") == 0) if ((slen > 5) && !strcmp(d.imageType + slen - 6, "MOSAIC")) isMosaic = true; - //const char* prefix = "MOSAIC"; + // const char* prefix = "MOSAIC"; const char *pos = strstr(d.imageType, "MOSAIC"); - //const char p = (const char *) d.imageType; - //p = (const char) strstr(d.imageType, "MOSAIC"); - //const char* p = strstr(d.imageType, "MOSAIC"); + // const char p = (const char *) d.imageType; + // p = (const char) strstr(d.imageType, "MOSAIC"); + // const char* p = strstr(d.imageType, "MOSAIC"); if (pos != NULL) isMosaic = true; - //isNonImage 0008,0008 = DERIVED,CSAPARALLEL,POSDISP - // sometime ComplexImageComponent 0008,9208 is missing - see ADNI data - // attempt to detect non-images, see https://github.com/scitran/data/blob/a516fdc39d75a6e4ac75d0e179e18f3a5fc3c0af/scitran/data/medimg/dcm/mr/siemens.py - //For Philips combinations see Table 3-28 Table 3-28: Valid combinations of Image Type applied values - // http://incenter.medical.philips.com/doclib/enc/fetch/2000/4504/577242/577256/588723/5144873/5144488/5144982/DICOM_Conformance_Statement_Intera_R7%2c_R8_and_R9.pdf%3fnodeid%3d5147977%26vernum%3d-2 + // isNonImage 0008,0008 = DERIVED,CSAPARALLEL,POSDISP + // sometime ComplexImageComponent 0008,9208 is missing - see ADNI data + // attempt to detect non-images, see https://github.com/scitran/data/blob/a516fdc39d75a6e4ac75d0e179e18f3a5fc3c0af/scitran/data/medimg/dcm/mr/siemens.py + // For Philips combinations see Table 3-28 Table 3-28: Valid combinations of Image Type applied values + // http://incenter.medical.philips.com/doclib/enc/fetch/2000/4504/577242/577256/588723/5144873/5144488/5144982/DICOM_Conformance_Statement_Intera_R7%2c_R8_and_R9.pdf%3fnodeid%3d5147977%26vernum%3d-2 if ((slen > 3) && (strstr(d.imageType, "_R_") != NULL)) { d.isHasReal = true; isReal = true; @@ -5525,9 +5533,9 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } if ((slen > 6) && (strstr(d.imageType, "DERIVED") != NULL)) d.isDerived = true; - //if((slen > 4) && (strstr(typestr, "DIS2D") != NULL) ) + // if((slen > 4) && (strstr(typestr, "DIS2D") != NULL) ) // d.isNonImage = true; - //not mutually exclusive: possible for Philips enhanced DICOM to store BOTH magnitude and phase in the same image + // not mutually exclusive: possible for Philips enhanced DICOM to store BOTH magnitude and phase in the same image break; } case kAcquisitionDate: @@ -5536,36 +5544,36 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.acquisitionDate = atof(acquisitionDateTxt); break; case kAcquisitionDateTime: - //char acquisitionDateTimeTxt[kDICOMStr]; + // char acquisitionDateTimeTxt[kDICOMStr]; dcmStr(lLength, &buffer[lPos], acquisitionDateTimeTxt); - //printMessage("%s\n",acquisitionDateTimeTxt); - break; -/* Failed attempt to infer slice timing for Philips MRI -https://neurostars.org/t/how-dcm2niix-handles-different-imaging-types/22697/6 - - case kSOPInstanceUID: { - if (d.manufacturer != kMANUFACTURER_PHILIPS) break; - char uid[kDICOMStrLarge]; - dcmStr(lLength, &buffer[lPos], uid, true); - char *timeStr = strrchr(uid, '.'); - //nb Manufacturer (0008,0070) comes AFTER (0008,0018) SOPInstanceUID. - //format of (0008,0018) UI - //[1.23.4.2019051416101221842 - // .YYYYMMDDHHmmssxxxxx - timeStr++; //skip "." - if ((strlen(timeStr) != 19) || (strlen(d.studyDate) < 8)) break; - bool sameDay = true; - for (int z = 0; z < 8; z++) - if (timeStr[z] != d.studyDate[z]) sameDay = false; - if (!sameDay) - printf("SOPInstanceUID does not match StudyDate: assuming study cross midnight\n"); - char *hourStr = timeStr + 8; //Skip 8 charactersYear,Month,Day YYYYMMDD - acquisitionTimePhilips = (double) atof(hourStr) * (double) 0.00001; - //printf(" %s %s %f\n", timeStr, hourStr, acquisitionTimePhilips); - - break; - } -*/ + // printMessage("%s\n",acquisitionDateTimeTxt); + break; + /* Failed attempt to infer slice timing for Philips MRI + https://neurostars.org/t/how-dcm2niix-handles-different-imaging-types/22697/6 + + case kSOPInstanceUID: { + if (d.manufacturer != kMANUFACTURER_PHILIPS) break; + char uid[kDICOMStrLarge]; + dcmStr(lLength, &buffer[lPos], uid, true); + char *timeStr = strrchr(uid, '.'); + //nb Manufacturer (0008,0070) comes AFTER (0008,0018) SOPInstanceUID. + //format of (0008,0018) UI + //[1.23.4.2019051416101221842 + // .YYYYMMDDHHmmssxxxxx + timeStr++; //skip "." + if ((strlen(timeStr) != 19) || (strlen(d.studyDate) < 8)) break; + bool sameDay = true; + for (int z = 0; z < 8; z++) + if (timeStr[z] != d.studyDate[z]) sameDay = false; + if (!sameDay) + printf("SOPInstanceUID does not match StudyDate: assuming study cross midnight\n"); + char *hourStr = timeStr + 8; //Skip 8 charactersYear,Month,Day YYYYMMDD + acquisitionTimePhilips = (double) atof(hourStr) * (double) 0.00001; + //printf(" %s %s %f\n", timeStr, hourStr, acquisitionTimePhilips); + + break; + } + */ case kStudyDate: dcmStr(lLength, &buffer[lPos], d.studyDate); if (((int)strlen(d.studyDate) > 7) && (strstr(d.studyDate, "19000101") != NULL)) @@ -5593,7 +5601,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kInstitutionName: dcmStr(lLength, &buffer[lPos], d.institutionName); break; - case kInstitutionAddress: //VR is "ST": 1024 chars maximum + case kInstitutionAddress: // VR is "ST": 1024 chars maximum dcmStr(lLength, &buffer[lPos], d.institutionAddress, true); break; case kReferringPhysicianName: @@ -5601,20 +5609,20 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kReferencedImageEvidenceSQ: if (lLength > 8) - break; //issue639: we will skip entire icon if there is an explicit length + break; // issue639: we will skip entire icon if there is an explicit length is00089092SQ = true; break; case kComplexImageComponent: if (is2005140FSQ) - break; //see Maastricht DICOM data for magnitude data with this field set as REAL! https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging + break; // see Maastricht DICOM data for magnitude data with this field set as REAL! https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging if (lLength < 2) break; - //issue 256: Philips files report real ComplexImageComponent but Magnitude ImageType https://github.com/rordenlab/dcm2niix/issues/256 + // issue 256: Philips files report real ComplexImageComponent but Magnitude ImageType https://github.com/rordenlab/dcm2niix/issues/256 isPhase = false; isReal = false; isImaginary = false; isMagnitude = false; - //see Table C.8-85 http://dicom.nema.org/medical/Dicom/2017c/output/chtml/part03/sect_C.8.13.3.html + // see Table C.8-85 http://dicom.nema.org/medical/Dicom/2017c/output/chtml/part03/sect_C.8.13.3.html if ((buffer[lPos] == 'R') && (toupper(buffer[lPos + 1]) == 'E')) isReal = true; if ((buffer[lPos] == 'I') && (toupper(buffer[lPos + 1]) == 'M')) @@ -5623,7 +5631,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD isPhase = true; if ((buffer[lPos] == 'M') && (toupper(buffer[lPos + 1]) == 'A')) isMagnitude = true; - //not mutually exclusive: possible for Philips enhanced DICOM to store BOTH magnitude and phase in the same image + // not mutually exclusive: possible for Philips enhanced DICOM to store BOTH magnitude and phase in the same image if (isPhase) d.isHasPhase = true; if (isReal) @@ -5638,7 +5646,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD dcmStr(lLength, &buffer[lPos], acqContrast); if (((int)strlen(acqContrast) > 8) && (strstr(acqContrast, "DIFFUSION") != NULL)) d.isDiffusion = true; - //if (((int)strlen(acqContrast) > 8) && (strstr(acqContrast, "PERFUSION") != NULL)) + // if (((int)strlen(acqContrast) > 8) && (strstr(acqContrast, "PERFUSION") != NULL)) // isASL = true; //see series 301 of dcm_qa_philips_asl break; case kAcquisitionTime: { @@ -5647,7 +5655,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.acquisitionTime = atof(acquisitionTimeTxt); if (d.manufacturer != kMANUFACTURER_UIH) break; - //UIH slice timing- do not use for Siemens as Siemens de-identification can corrupt this field https://github.com/rordenlab/dcm2niix/issues/236 + // UIH slice timing- do not use for Siemens as Siemens de-identification can corrupt this field https://github.com/rordenlab/dcm2niix/issues/236 d.CSA.sliceTiming[acquisitionTimesGE_UIH] = d.acquisitionTime; acquisitionTimesGE_UIH++; break; @@ -5664,8 +5672,9 @@ 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 + 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; @@ -5675,27 +5684,27 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD dcmStr(lLength, &buffer[lPos], d.patientName); break; case kAnatomicalOrientationType: { - char aotTxt[kDICOMStr]; //ftp://dicom.nema.org/MEDICAL/dicom/2015b/output/chtml/part03/sect_C.7.6.2.html#sect_C.7.6.2.1.1 + char aotTxt[kDICOMStr]; // ftp://dicom.nema.org/MEDICAL/dicom/2015b/output/chtml/part03/sect_C.7.6.2.html#sect_C.7.6.2.1.1 dcmStr(lLength, &buffer[lPos], aotTxt); int slen = (int)strlen(aotTxt); if ((slen < 9) || (strstr(aotTxt, "QUADRUPED") == NULL)) break; d.isQuadruped = true; - //printError("Anatomical Orientation Type (0010,2210) is QUADRUPED: rotate coordinates accordingly\n"); - break; - } - #ifdef myDeidentificationMethod - case kDeidentificationMethod: { //issue 383 - #ifdef myDeidentificationMethod - dcmStr(lLength, &buffer[lPos], d.deidentificationMethod); - int slen = (int)strlen(d.deidentificationMethod); - if ((slen < 10) || (strstr(d.deidentificationMethod, "DICOMANON") == NULL)) - #else - char anonTxt[kDICOMStr]; - dcmStr(lLength, &buffer[lPos], anonTxt); - int slen = (int)strlen(anonTxt); - if ((slen < 10) || (strstr(anonTxt, "DICOMANON") == NULL)) - #endif // + // printError("Anatomical Orientation Type (0010,2210) is QUADRUPED: rotate coordinates accordingly\n"); + break; + } +#ifdef myDeidentificationMethod + case kDeidentificationMethod: { // issue 383 +#ifdef myDeidentificationMethod + dcmStr(lLength, &buffer[lPos], d.deidentificationMethod); + int slen = (int)strlen(d.deidentificationMethod); + if ((slen < 10) || (strstr(d.deidentificationMethod, "DICOMANON") == NULL)) +#else + char anonTxt[kDICOMStr]; + dcmStr(lLength, &buffer[lPos], anonTxt); + int slen = (int)strlen(anonTxt); + if ((slen < 10) || (strstr(anonTxt, "DICOMANON") == NULL)) +#endif // break; isDICOMANON = true; printWarning("Matlab DICOMANON can scramble SeriesInstanceUID (0020,000e) and remove crucial data (see issue 383). \n"); @@ -5706,29 +5715,28 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; } case kCodeValue: { - if(isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) + if (isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) dcmStr(lLength, &buffer[lPos], d.deID_CS[d.deID_CS_n].CodeValue); break; } case kCodingSchemeDesignator: { - if(isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) + if (isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) dcmStr(lLength, &buffer[lPos], d.deID_CS[d.deID_CS_n].CodingSchemeDesignator); break; } case kCodingSchemeVersion: { - if(isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) + if (isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) dcmStr(lLength, &buffer[lPos], d.deID_CS[d.deID_CS_n].CodingSchemeVersion); break; } case kCodeMeaning: { - if(isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) - { + if (isDeidentificationMethodCodeSequence && d.deID_CS_n < MAX_DEID_CS) { dcmStr(lLength, &buffer[lPos], d.deID_CS[d.deID_CS_n].CodeMeaning); d.deID_CS_n++; } break; } - #endif // myDeidentificationMethod +#endif // myDeidentificationMethod case kPatientID: if (strlen(d.patientID) > 1) break; @@ -5741,7 +5749,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD dcmStr(lLength, &buffer[lPos], d.patientBirthDate); break; case kPatientSex: { - //must be M,F,O: http://dicom.nema.org/dicom/2013/output/chtml/part03/sect_C.2.html + // must be M,F,O: http://dicom.nema.org/dicom/2013/output/chtml/part03/sect_C.2.html char patientSex = toupper(buffer[lPos]); if ((patientSex == 'M') || (patientSex == 'F') || (patientSex == 'O')) d.patientSex = patientSex; @@ -5756,29 +5764,29 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kPatientWeight: d.patientWeight = dcmStrFloat(lLength, &buffer[lPos]); break; -/* n.b. check GeneralElectricAdvance-NIMH example dataset: need to specify SQ - (0018,0031) LO [FDG -- fluorodeoxyglucose ] # 26,1 Radiopharmaceutical - (0018,1071) DS [0 ] # 2,1 Radiopharmaceutical Volume - (0018,1072) TM [092345.00 ] # 10,1 Radiopharmaceutical Start Time - (0018,1074) DS [ 75850000] # 14,1 Radionuclide Total Dose - (0018,1075) DS [6588] # 4,1 Radionuclide Half Life - (0018,1076) DS [0.97000002861023] # 16,1 Radionuclide Positron Fraction - (0054,0300) SQ (Sequence with undefined length) # u/l,1 Radionuclide Code Sequence - (fffe,e000) na (Item with defined length) - (0008,0100) SH [C-111A1 ] # 8,1 Code Value - (0008,0102) SH [99SDM ] # 6,1 Coding Scheme Designator - (0008,0104) LO [18F ] # 4,1 Code Meaning - (fffe,e0dd) - (0054,0304) SQ (Sequence with undefined length) # u/l,1 Radiopharmaceutical Code Sequence - (fffe,e000) na (Item with defined length) - (0008,0100) SH [Y-X1743 ] # 8,1 Code Value - (0008,0102) SH [99SDM ] # 6,1 Coding Scheme Designator - (0008,0104) LO [FDG -- fluorodeoxyglucose ] # 26,1 Code Meaning - (fffe,e0dd) - case kTracerRadionuclide1: - case kTracerRadionuclide2: - dcmStr(lLength, &buffer[lPos], d.tracerRadionuclide); - break;*/ + /* n.b. check GeneralElectricAdvance-NIMH example dataset: need to specify SQ + (0018,0031) LO [FDG -- fluorodeoxyglucose ] # 26,1 Radiopharmaceutical + (0018,1071) DS [0 ] # 2,1 Radiopharmaceutical Volume + (0018,1072) TM [092345.00 ] # 10,1 Radiopharmaceutical Start Time + (0018,1074) DS [ 75850000] # 14,1 Radionuclide Total Dose + (0018,1075) DS [6588] # 4,1 Radionuclide Half Life + (0018,1076) DS [0.97000002861023] # 16,1 Radionuclide Positron Fraction + (0054,0300) SQ (Sequence with undefined length) # u/l,1 Radionuclide Code Sequence + (fffe,e000) na (Item with defined length) + (0008,0100) SH [C-111A1 ] # 8,1 Code Value + (0008,0102) SH [99SDM ] # 6,1 Coding Scheme Designator + (0008,0104) LO [18F ] # 4,1 Code Meaning + (fffe,e0dd) + (0054,0304) SQ (Sequence with undefined length) # u/l,1 Radiopharmaceutical Code Sequence + (fffe,e000) na (Item with defined length) + (0008,0100) SH [Y-X1743 ] # 8,1 Code Value + (0008,0102) SH [99SDM ] # 6,1 Coding Scheme Designator + (0008,0104) LO [FDG -- fluorodeoxyglucose ] # 26,1 Code Meaning + (fffe,e0dd) + case kTracerRadionuclide1: + case kTracerRadionuclide2: + dcmStr(lLength, &buffer[lPos], d.tracerRadionuclide); + break;*/ case kStationName: dcmStr(lLength, &buffer[lPos], d.stationName); break; @@ -5798,9 +5806,9 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD dcmStr(lLength, &buffer[lPos], d.manufacturersModelName); break; case kDerivationDescription: { - //strcmp(transferSyntax, "1.2.840.10008.1.2") + // strcmp(transferSyntax, "1.2.840.10008.1.2") char derivationDescription[kDICOMStr]; - dcmStr(lLength, &buffer[lPos], derivationDescription); //strcasecmp, strcmp + dcmStr(lLength, &buffer[lPos], derivationDescription); // strcasecmp, strcmp if (strcasecmp(derivationDescription, "MEDCOM_RESAMPLED") == 0) d.isResampled = true; break; @@ -5863,23 +5871,23 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kMagnetizationTransferAttribute: //'CS' 'ON_RESONANCE','OFF_RESONANCE','NONE' if (lLength < 2) - break; //https://github.com/bids-standard/bids-specification/pull/681 - if ((toupper(buffer[lPos]) == 'O') && (toupper(buffer[lPos+1]) == 'F')) // OFF_RESONANCE - d.mtState = 1; //TRUE - if ((toupper(buffer[lPos]) == 'O') && (toupper(buffer[lPos+1]) == 'N')) // ON_RESONANCE and NONE - d.mtState = 0; //FALSE - if ((toupper(buffer[lPos]) == 'N') && (toupper(buffer[lPos+1]) == 'O')) // ON_RESONANCE and NONE - d.mtState = 0; //FALSE + break; // https://github.com/bids-standard/bids-specification/pull/681 + if ((toupper(buffer[lPos]) == 'O') && (toupper(buffer[lPos + 1]) == 'F')) // OFF_RESONANCE + d.mtState = 1; // TRUE + if ((toupper(buffer[lPos]) == 'O') && (toupper(buffer[lPos + 1]) == 'N')) // ON_RESONANCE and NONE + d.mtState = 0; // FALSE + if ((toupper(buffer[lPos]) == 'N') && (toupper(buffer[lPos + 1]) == 'O')) // ON_RESONANCE and NONE + d.mtState = 0; // FALSE break; case kRectilinearPhaseEncodeReordering: { //'CS' [REVERSE_LINEAR],[LINEAR],[CENTRIC],[REVERSE_CENTRIC] if (d.manufacturer != kMANUFACTURER_GE) - break; //only found in GE software beginning with RX27 + break; // only found in GE software beginning with RX27 if (lLength < 2) break; if (toupper(buffer[lPos]) == 'L') - d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED; //issue674, (0018, 9034) LINEAR-->FLIPPED + d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED; // issue674, (0018, 9034) LINEAR-->FLIPPED if (toupper(buffer[lPos]) == 'R') - d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED; //issue674, R(0018, 9034) REVERSE_LINEAR-->UNFLIPPED + d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED; // issue674, R(0018, 9034) REVERSE_LINEAR-->UNFLIPPED break; } case kPartialFourierDirection: { //'CS' PHASE FREQUENCY SLICE_SELECT COMBINATION @@ -5907,11 +5915,11 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kAcquisitionDuration: if (!isSameFloatGE(d.acquisitionDuration, 0.0)) - break; //issue 808: give precedence to more precise measures, e.g kAcquisitionDurationGE (0019,105a) - //n.b. used differently by different vendors https://github.com/rordenlab/dcm2niix/issues/225 + break; // issue 808: give precedence to more precise measures, e.g kAcquisitionDurationGE (0019,105a) + // n.b. used differently by different vendors https://github.com/rordenlab/dcm2niix/issues/225 d.acquisitionDuration = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); break; - //in theory, 0018,9074 could provide XA10 slice time information, but scrambled by XA10 de-identification: better to use 0021,1104 + // in theory, 0018,9074 could provide XA10 slice time information, but scrambled by XA10 de-identification: better to use 0021,1104 /*case kFrameAcquisitionDateTime: { //(0018,9074) DT [20190621095516.140000] YYYYMMDDHHMMSS //see https://github.com/rordenlab/dcm2niix/issues/303 @@ -5925,7 +5933,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer != kMANUFACTURER_GE) break; d.acquisitionDuration = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); - d.acquisitionDuration /= 1000000.0; //convert microsec to sec + d.acquisitionDuration /= 1000000.0; // convert microsec to sec break; case kDiffusionDirectionality: { // 0018, 9075 set_directionality0018_9075(&volDiffusion, (&buffer[lPos])); @@ -5937,10 +5945,10 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD isPhilipsDerived = true; break; } - case kParallelAcquisitionTechnique: //CS + case kParallelAcquisitionTechnique: // CS dcmStr(lLength, &buffer[lPos], d.parallelAcquisitionTechnique); break; - case kInversionTimes: { //issue 380 + case kInversionTimes: { // issue 380 if ((lLength < 8) || ((lLength % 8) != 0)) break; d.TI = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); @@ -5956,7 +5964,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer != kMANUFACTURER_BRUKER) break; if (sqDepth == 0) - sqDepth = 1; //should not happen, in case faulty anonymization + sqDepth = 1; // should not happen, in case faulty anonymization sqDepth00189114 = sqDepth - 1; break; case kMRAcquisitionPhaseEncodingStepsInPlane: @@ -5970,7 +5978,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kGradientEchoTrainLength: { int etl = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); - if (etl < 2) //issue 717 + if (etl < 2) // issue 717 break; d.echoTrainLength = etl; break; @@ -5979,7 +5987,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer == kMANUFACTURER_SIEMENS) numberOfImagesInMosaic = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; - case kSeriesPlaneGE: //SS 2=Axi, 4=Sag, 8=Cor, 16=Obl, 256=3plane + case kSeriesPlaneGE: // SS 2=Axi, 4=Sag, 8=Cor, 16=Obl, 256=3plane if (d.manufacturer != kMANUFACTURER_GE) break; if (dcmInt(lLength, &buffer[lPos], d.isLittleEndian) == 256) @@ -5991,11 +5999,11 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kDiffusion_bValueSiemens: if (d.manufacturer != kMANUFACTURER_SIEMENS) break; - //issue409 + // issue409 B0Philips = dcmStrInt(lLength, &buffer[lPos]); set_bVal(&volDiffusion, B0Philips); break; - case kSliceTimeSiemens: { //Array of FD (64-bit double) + case kSliceTimeSiemens: { // Array of FD (64-bit double) if (d.manufacturer != kMANUFACTURER_SIEMENS) break; if ((lLength < 8) || ((lLength % 8) != 0)) @@ -6004,15 +6012,15 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (nSlicesTimes > kMaxEPI3D) break; d.CSA.mosaicSlices = nSlicesTimes; - //printf(">>>> %d\n", nSlicesTimes); - //issue 296: for images de-identified to remove readCSAImageHeader + // printf(">>>> %d\n", nSlicesTimes); + // issue 296: for images de-identified to remove readCSAImageHeader for (int z = 0; z < nSlicesTimes; z++) d.CSA.sliceTiming[z] = dcmFloatDouble(8, &buffer[lPos + (z * 8)], d.isLittleEndian); - //for (int z = 0; z < nSlicesTimes; z++) + // for (int z = 0; z < nSlicesTimes; z++) // printf("%d>>>%g\n", z+1, d.CSA.sliceTiming[z]); checkSliceTimes(&d.CSA, nSlicesTimes, isVerbose, d.is3DAcq); - //d.CSA.dtiV[0] = dcmStrInt(lLength, &buffer[lPos]); - //d.CSA.numDti = 1; + // d.CSA.dtiV[0] = dcmStrInt(lLength, &buffer[lPos]); + // d.CSA.numDti = 1; break; } case kDiffusionGradientDirectionSiemens: { @@ -6020,8 +6028,8 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; float v[4]; dcmMultiFloatDouble(lLength, &buffer[lPos], 3, v, d.isLittleEndian); - //dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, v); - //printf(">>>%g %g %g\n", v[0], v[1], v[2]); + // dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, v); + // printf(">>>%g %g %g\n", v[0], v[1], v[2]); d.CSA.dtiV[1] = v[0]; d.CSA.dtiV[2] = v[1]; d.CSA.dtiV[3] = v[2]; @@ -6030,9 +6038,9 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kImaRelTablePosition: { if (d.manufacturer != kMANUFACTURER_SIEMENS) break; - dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &d.CSA.tablePos[0]); //slice position - d.CSA.tablePos[3] = -d.CSA.tablePos[3]; //reverse Z polarity, issue 726 - d.CSA.tablePos[0] = 1.0; //set + dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &d.CSA.tablePos[0]); // slice position + d.CSA.tablePos[3] = -d.CSA.tablePos[3]; // reverse Z polarity, issue 726 + d.CSA.tablePos[0] = 1.0; // set break; } case kNumberOfDiffusionT2GE: { @@ -6058,12 +6066,12 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kLastScanLoc: d.lastScanLoc = dcmStrFloat(lLength, &buffer[lPos]); break; - //GE bug: multiple echos can create identical instance numbers - // in theory, one could detect as kRawDataRunNumberGE varies - // sliceN of echoE will have the same value for all timepoints - // this value does not appear indexed - // different echoes record same echo time. - // use multiEchoSortGEDICOM.py to salvage + // GE bug: multiple echos can create identical instance numbers + // in theory, one could detect as kRawDataRunNumberGE varies + // sliceN of echoE will have the same value for all timepoints + // this value does not appear indexed + // different echoes record same echo time. + // use multiEchoSortGEDICOM.py to salvage case kRawDataRunNumberGE: if (d.manufacturer != kMANUFACTURER_GE) break; @@ -6078,18 +6086,21 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer != kMANUFACTURER_GE) break; userData11GE = round(dcmStrFloat(lLength, &buffer[lPos])); - break; } + break; + } case kUserData12GE: { if (d.manufacturer != kMANUFACTURER_GE) break; userData12GE = round(dcmStrFloat(lLength, &buffer[lPos])); - //printf("%d<<<<\n", userData12GE); - break; } + // printf("%d<<<<\n", userData12GE); + break; + } case kUserData15GE: { if (d.manufacturer != kMANUFACTURER_GE) break; userData15GE = dcmStrFloat(lLength, &buffer[lPos]); - break; } + break; + } case kDiffusionDirectionGEX: if (d.manufacturer == kMANUFACTURER_GE) set_diffusion_directionGE(&volDiffusion, lLength, (&buffer[lPos]), 0); @@ -6102,31 +6113,31 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer == kMANUFACTURER_GE) set_diffusion_directionGE(&volDiffusion, lLength, (&buffer[lPos]), 2); break; - case kPulseSequenceNameGE: { //LO 'epi'/'epiRT' + case kPulseSequenceNameGE: { // LO 'epi'/'epiRT' if (d.manufacturer != kMANUFACTURER_GE) break; dcmStr(lLength, &buffer[lPos], d.pulseSequenceName); - if (strstr( d.pulseSequenceName, "epi_pepolar") != NULL) { - //if ((strstr( d.pulseSequenceName, "epi_pepolar") != NULL) || (strstr( d.pulseSequenceName, "epi2_pepolar") != NULL)){ - d.epiVersionGE = kGE_EPI_PEPOLAR_FWD; //n.b. combine with 0019,10B3 - } else if (strstr( d.pulseSequenceName, "epi2") != NULL) { + if (strstr(d.pulseSequenceName, "epi_pepolar") != NULL) { + // if ((strstr( d.pulseSequenceName, "epi_pepolar") != NULL) || (strstr( d.pulseSequenceName, "epi2_pepolar") != NULL)){ + d.epiVersionGE = kGE_EPI_PEPOLAR_FWD; // n.b. combine with 0019,10B3 + } else if (strstr(d.pulseSequenceName, "epi2") != NULL) { d.epiVersionGE = kGE_EPI_EPI2; //-1 = not epi, 0 = epi, 1 = epiRT, 2 = epi2 - } else if (strstr( d.pulseSequenceName, "epiRT") != NULL) { + } else if (strstr(d.pulseSequenceName, "epiRT") != NULL) { d.epiVersionGE = kGE_EPI_EPIRT; //-1 = not epi, 0 = epi, 1 = epiRT - } else if (strstr( d.pulseSequenceName, "epi") != NULL) { + } else if (strstr(d.pulseSequenceName, "epi") != NULL) { d.epiVersionGE = kGE_EPI_EPI; //-1 = not epi, 0 = epi, 1 = epiRT } - if (strstr( d.pulseSequenceName, "b0map")) { - isGEfieldMap = true; //issue501 + if (strstr(d.pulseSequenceName, "b0map")) { + isGEfieldMap = true; // issue501 } break; } - case kInternalPulseSequenceNameGE: { //LO 'EPI'(gradient echo)/'EPI2'(spin echo): + case kInternalPulseSequenceNameGE: { // LO 'EPI'(gradient echo)/'EPI2'(spin echo): if (d.manufacturer != kMANUFACTURER_GE) break; char epiStr[kDICOMStr]; dcmStr(lLength, &buffer[lPos], epiStr); - strcat(d.phaseEncodingDirectionDisplayedUIH, epiStr); //overload + strcat(d.phaseEncodingDirectionDisplayedUIH, epiStr); // overload if ((d.epiVersionGE < kGE_EPI_PEPOLAR_FWD) && (strcmp(epiStr, "EPI") == 0)) { d.internalepiVersionGE = 1; //-1 = not EPI, 1 = EPI, 2 = EPI2 if (d.epiVersionGE != 1) { // 1 = epiRT by kEpiRTGroupDelayGE or kPulseSequenceNameGE @@ -6137,7 +6148,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.internalepiVersionGE = 2; //-1 = not epi, 1 = EPI, 2 = EPI2 } if (strcmp(epiStr, "B0map") == 0) { - isGEfieldMap = true; //issue501 + isGEfieldMap = true; // issue501 } break; } @@ -6148,7 +6159,8 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD dcmStr(lLength, &buffer[lPos], d.studyInstanceUID); break; case kSeriesInstanceUID: // 0020,000E - if (sqDepth > seriesInstanceUIDsqDepth) break; //issue655 + if (sqDepth > seriesInstanceUIDsqDepth) + break; // issue655 seriesInstanceUIDsqDepth = sqDepth; dcmStr(lLength, &buffer[lPos], d.seriesInstanceUID); d.seriesUidCrc = mz_crc32X((unsigned char *)&d.seriesInstanceUID, strlen(d.seriesInstanceUID)); @@ -6160,29 +6172,29 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } patientPositionNum++; isAtFirstPatientPosition = true; - //char dx[kDICOMStr]; - //dcmStr(lLength, &buffer[lPos], dx); - //printMessage("*%s*", dx); - dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &patientPosition[0]); //slice position + // char dx[kDICOMStr]; + // dcmStr(lLength, &buffer[lPos], dx); + // printMessage("*%s*", dx); + dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &patientPosition[0]); // slice position if (isnan(d.patientPosition[1])) { - //dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPosition[0]); //slice position + // dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPosition[0]); //slice position for (int k = 0; k < 4; k++) d.patientPosition[k] = patientPosition[k]; } else { - //dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPositionLast[0]); //slice direction for 4D + // dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPositionLast[0]); //slice direction for 4D for (int k = 0; k < 4; k++) d.patientPositionLast[k] = patientPosition[k]; if ((isFloatDiff(d.patientPositionLast[1], d.patientPosition[1])) || (isFloatDiff(d.patientPositionLast[2], d.patientPosition[2])) || (isFloatDiff(d.patientPositionLast[3], d.patientPosition[3]))) { - isAtFirstPatientPosition = false; //this slice is not at position of 1st slice - //if (d.patientPositionSequentialRepeats == 0) //this is the first slice with different position - // d.patientPositionSequentialRepeats = patientPositionNum-1; - } //if different position from 1st slice in file - } //if not first slice in file + isAtFirstPatientPosition = false; // this slice is not at position of 1st slice + // if (d.patientPositionSequentialRepeats == 0) //this is the first slice with different position + // d.patientPositionSequentialRepeats = patientPositionNum-1; + } // if different position from 1st slice in file + } // if not first slice in file set_isAtFirstPatientPosition_tvd(&volDiffusion, isAtFirstPatientPosition); - //if (isAtFirstPatientPosition) numFirstPatientPosition++; - if (isVerbose > 1) //verbose > 1 will report full DICOM tag + // if (isAtFirstPatientPosition) numFirstPatientPosition++; + if (isVerbose > 1) // verbose > 1 will report full DICOM tag printMessage(" Patient Position 0020,0032 (#,@,X,Y,Z)\t%d\t%zu\t%g\t%g\t%g\n", patientPositionNum, lPos, patientPosition[1], patientPosition[2], patientPosition[3]); if ((isOrient) && (nSliceMM < kMaxSlice2D)) { vec3 pos = setVec3(patientPosition[1], patientPosition[2], patientPosition[3]); @@ -6202,7 +6214,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; } case kInPlanePhaseEncodingDirection: - d.phaseEncodingRC = toupper(buffer[lPos]); //first character is either 'R'ow or 'C'ol + d.phaseEncodingRC = toupper(buffer[lPos]); // first character is either 'R'ow or 'C'ol break; case kSAR: d.SAR = dcmStrFloat(lLength, &buffer[lPos]); @@ -6218,18 +6230,18 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.acquNum = dcmStrInt(lLength, &buffer[lPos]); break; case kImageNum: - //Enhanced Philips also uses this in once per file SQ 0008,1111 - //Enhanced Philips also uses this once per slice in SQ 2005,140f + // Enhanced Philips also uses this in once per file SQ 0008,1111 + // Enhanced Philips also uses this once per slice in SQ 2005,140f has00200013 = true; if (d.imageNum < 1) - d.imageNum = dcmStrInt(lLength, &buffer[lPos]); //Philips renames each image again in 2001,9000, which can lead to duplicates + d.imageNum = dcmStrInt(lLength, &buffer[lPos]); // Philips renames each image again in 2001,9000, which can lead to duplicates break; case kInStackPositionNumber: if ((d.manufacturer != kMANUFACTURER_CANON) && (d.manufacturer != kMANUFACTURER_HITACHI) && (d.manufacturer != kMANUFACTURER_UNKNOWN) && (d.manufacturer != kMANUFACTURER_PHILIPS) && (d.manufacturer != kMANUFACTURER_BRUKER)) break; inStackPositionNumber = dcmInt(4, &buffer[lPos], d.isLittleEndian); - //if (inStackPositionNumber == 1) numInStackPositionNumber1 ++; - //printf("<%d>\n",inStackPositionNumber); + // if (inStackPositionNumber == 1) numInStackPositionNumber1 ++; + // printf("<%d>\n",inStackPositionNumber); if (inStackPositionNumber > maxInStackPositionNumber) maxInStackPositionNumber = inStackPositionNumber; break; @@ -6241,25 +6253,26 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD minTemporalPositionIndex = temporalPositionIndex; break; case kDimensionIndexPointer: - if (dimensionIndexPointerCounter >= MAX_NUMBER_OF_DIMENSIONS) break; + if (dimensionIndexPointerCounter >= MAX_NUMBER_OF_DIMENSIONS) + break; dimensionIndexPointer[dimensionIndexPointerCounter++] = dcmAttributeTag(&buffer[lPos], d.isLittleEndian); break; case kFrameContentSequence: - //if (!(d.manufacturer == kMANUFACTURER_BRUKER)) break; //see https://github.com/rordenlab/dcm2niix/issues/241 + // if (!(d.manufacturer == kMANUFACTURER_BRUKER)) break; //see https://github.com/rordenlab/dcm2niix/issues/241 if (sqDepth == 0) - sqDepth = 1; //should not happen, in case faulty anonymization + sqDepth = 1; // should not happen, in case faulty anonymization sqDepth00189114 = sqDepth - 1; break; - case kTriggerDelayTime: { //0x0020+uint32_t(0x9153<< 16 ) //FD + case kTriggerDelayTime: { // 0x0020+uint32_t(0x9153<< 16 ) //FD if (prefs->isIgnoreTriggerTimes) - break; //issue499 + break; // issue499 if (d.manufacturer != kMANUFACTURER_PHILIPS) break; - //if (isVerbose < 2) break; + // if (isVerbose < 2) break; double trigger = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); d.triggerDelayTime = trigger; if (isSameFloatGE(d.triggerDelayTime, 0.0)) - d.triggerDelayTime = 0.0; //double to single + d.triggerDelayTime = 0.0; // double to single break; } case kDimensionIndexValues: { // kImageNum is not enough for 4D series from Philips 5.*. @@ -6278,14 +6291,14 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD dcmStr(lLength, &buffer[lPos], interp); if (strcmp(interp, "PALETTE_COLOR") == 0) isPaletteColor = true; - //printError("Photometric Interpretation 'PALETTE COLOR' not supported\n"); + // printError("Photometric Interpretation 'PALETTE COLOR' not supported\n"); break; } case kPlanarRGB: d.isPlanarRGB = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; case kDim3: - if (lLength < 1) //issue 695 + if (lLength < 1) // issue 695 break; d.xyzDim[3] = dcmStrInt(lLength, &buffer[lPos]); numberOfFrames = d.xyzDim[3]; @@ -6299,7 +6312,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kDim1: d.xyzDim[1] = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; - //order is Row,Column e.g. YX + // order is Row,Column e.g. YX case kXYSpacing: { float yx[3]; dcmMultiFloat(lLength, (char *)&buffer[lPos], 2, yx); @@ -6310,34 +6323,34 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kImageComments: dcmStr(lLength, &buffer[lPos], d.imageComments, true); break; - //group 21: siemens + // group 21: siemens case kScanOptionsSiemens: dcmStr(lLength, &buffer[lPos], scanOptionsSiemens, true); break; - case kFrameNumberInSeries: //issue837 + case kFrameNumberInSeries: // issue837 if (d.manufacturer != kMANUFACTURER_SIEMENS) break; frameNumberInSeries = dcmStrInt(lLength, &buffer[lPos]); break; - case kPATModeText: { //e.g. Siemens iPAT x2 listed as "p2" + case kPATModeText: { // e.g. Siemens iPAT x2 listed as "p2" char accelStr[kDICOMStr]; dcmStr(lLength, &buffer[lPos], accelStr); char *ptr; - dcmStrDigitsDotOnlyKey('p', accelStr); //e.g. if "p2s4" return "2", if "s4" return "" + dcmStrDigitsDotOnlyKey('p', accelStr); // e.g. if "p2s4" return "2", if "s4" return "" d.accelFactPE = (float)strtof(accelStr, &ptr); if (*ptr != '\0') d.accelFactPE = 0.0; - //between slice accel + // between slice accel dcmStr(lLength, &buffer[lPos], accelStr); - dcmStrDigitsDotOnlyKey('s', accelStr); //e.g. if "p2s4" return "4", if "p2" return "" + dcmStrDigitsDotOnlyKey('s', accelStr); // e.g. if "p2s4" return "4", if "p2" return "" multiBandFactor = (int)strtol(accelStr, &ptr, 10); if (*ptr != '\0') multiBandFactor = 0.0; - //printMessage("p%gs%d\n", d.accelFactPE, multiBandFactor); + // printMessage("p%gs%d\n", d.accelFactPE, multiBandFactor); break; } -// case kCSASeriesHeaderInfoXA2: -// printf("do something profound\n"); + // case kCSASeriesHeaderInfoXA2: + // printf("do something profound\n"); case kCSASeriesHeaderInfoXA: if (d.manufacturer != kMANUFACTURER_SIEMENS) break; @@ -6347,57 +6360,57 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.CSA.SeriesHeader_length = lLength; break; case kTimeAfterStart: - //0021,1104 see https://github.com/rordenlab/dcm2niix/issues/303 - // 0021,1104 6@159630 DS 4.635 - // 0021,1104 2@161164 DS 0 + // 0021,1104 see https://github.com/rordenlab/dcm2niix/issues/303 + // 0021,1104 6@159630 DS 4.635 + // 0021,1104 2@161164 DS 0 if (d.manufacturer != kMANUFACTURER_SIEMENS) break; if (acquisitionTimesGE_UIH >= kMaxEPI3D) break; d.CSA.sliceTiming[acquisitionTimesGE_UIH] = dcmStrFloat(lLength, &buffer[lPos]); - d.CSA.sliceTiming[acquisitionTimesGE_UIH] *= 1000.0; //convert sec to msec - //printf("\t%d\t%g\tTimeAfterStart(0021,1104)\n", d.imageNum, d.CSA.sliceTiming[acquisitionTimesGE_UIH]); + d.CSA.sliceTiming[acquisitionTimesGE_UIH] *= 1000.0; // convert sec to msec + // printf("\t%d\t%g\tTimeAfterStart(0021,1104)\n", d.imageNum, d.CSA.sliceTiming[acquisitionTimesGE_UIH]); acquisitionTimesGE_UIH++; break; - case kICE_dims: { //issue568: LO (0021,1106) [X_4_1_1_1_1_160_1_1_1_1_1_277] + case kICE_dims: { // issue568: LO (0021,1106) [X_4_1_1_1_1_160_1_1_1_1_1_277] if ((d.manufacturer != kMANUFACTURER_SIEMENS) || (d.echoNum > 1)) break; char iceStr[kDICOMStr]; dcmStr(lLength, &buffer[lPos], iceStr); dcmStrDigitsOnly(iceStr); char *end, *echoStr; - //read the first item ('X' or numeric if uncombined) + // read the first item ('X' or numeric if uncombined) char c = iceStr[0]; - if( c >= '0' && c <= '9' ){ + if (c >= '0' && c <= '9') { int coilNumber = (int)strtol(iceStr, &end, 10); - //if ((iceStr != end) && (coilNumber > 0) && (strlen(d.coilName) < 1)) { //nb with uncombined coil will still have a name, e.g. 'HeadNeck_64' + // if ((iceStr != end) && (coilNumber > 0) && (strlen(d.coilName) < 1)) { //nb with uncombined coil will still have a name, e.g. 'HeadNeck_64' if ((iceStr != end) && (coilNumber > 0)) { snprintf(d.coilName, kDICOMStr, "%d", coilNumber); - //printf("issue631 coil name '%s'\n", d.coilName); + // printf("issue631 coil name '%s'\n", d.coilName); d.coilCrc = mz_crc32X((unsigned char *)&d.coilName, strlen(d.coilName)); } } - //read the second item: the echo number ('4') + // read the second item: the echo number ('4') echoStr = strchr(iceStr, ' '); int echo = (int)strtol(echoStr, &end, 10); - //printMessage("%d:%d:'%s'\n", d.echoNum, echo, iceStr); + // printMessage("%d:%d:'%s'\n", d.echoNum, echo, iceStr); if (iceStr != end) d.echoNum = echo; break; } - case kICE_dimsLong: { //issue568: LO (0021,1106) [X_4_1_1_1_1_160_1_1_1_1_1_277] + case kICE_dimsLong: { // issue568: LO (0021,1106) [X_4_1_1_1_1_160_1_1_1_1_1_277] if ((d.manufacturer != kMANUFACTURER_SIEMENS) || (numberOfFramesICEdims > 0)) break; char iceStr[kDICOMStr]; dcmStr(lLength, &buffer[lPos], iceStr); int idx = 0; - char * pch = strtok (iceStr,"_"); + char *pch = strtok(iceStr, "_"); char *end; while (pch != NULL) { - if (idx ==20) + if (idx == 20) numberOfFramesICEdims = (int)strtol(pch, &end, 10); - idx ++; - pch = strtok (NULL, "_"); + idx++; + pch = strtok(NULL, "_"); } break; } @@ -6411,8 +6424,9 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED; break; } - case kRealDwellTime : //https://github.com/rordenlab/dcm2niix/issues/240 - if (d.manufacturer != kMANUFACTURER_SIEMENS) break; + case kRealDwellTime: // https://github.com/rordenlab/dcm2niix/issues/240 + if (d.manufacturer != kMANUFACTURER_SIEMENS) + break; d.dwellTime = dcmStrInt(lLength, &buffer[lPos]); break; case kBandwidthPerPixelPhaseEncode21: @@ -6425,35 +6439,35 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; dcmStr(lLength, &buffer[lPos], d.coilElements); break; - //group 21: GE + // group 21: GE case kLocationsInAcquisitionGE: locationsInAcquisitionGE = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; case kRTIA_timer: if (d.manufacturer != kMANUFACTURER_GE) break; - //see dicm2nii slice timing from 0021,105E DS RTIA_timer - d.rtia_timerGE = dcmStrFloat(lLength, &buffer[lPos]); //RefAcqTimes = t/10; end % in ms - //printf("%s\t%g\n", fname, d.rtia_timerGE); + // see dicm2nii slice timing from 0021,105E DS RTIA_timer + d.rtia_timerGE = dcmStrFloat(lLength, &buffer[lPos]); // RefAcqTimes = t/10; end % in ms + // printf("%s\t%g\n", fname, d.rtia_timerGE); break; case kProtocolDataBlockGE: if (d.manufacturer != kMANUFACTURER_GE) break; d.protocolBlockLengthGE = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); d.protocolBlockStartGE = (int)lPos + (int)lFileOffset + 4; - //printError("ProtocolDataBlockGE %d @ %d\n", d.protocolBlockLengthGE, d.protocolBlockStartGE); + // printError("ProtocolDataBlockGE %d @ %d\n", d.protocolBlockLengthGE, d.protocolBlockStartGE); break; - case kNumberOfExcitations: //FL + case kNumberOfExcitations: // FL if (d.manufacturer != kMANUFACTURER_GE) break; d.numberOfExcitations = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); break; - case kNumberOfArms: //FL + case kNumberOfArms: // FL if (d.manufacturer != kMANUFACTURER_GE) break; d.numberOfArms = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); break; - case kNumberOfPointsPerArm: //FL + case kNumberOfPointsPerArm: // FL if (d.manufacturer != kMANUFACTURER_GE) break; d.numberOfPointsPerArm = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); @@ -6464,7 +6478,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kPETImageIndex: PETImageIndex = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; - case kReferencedSegmentNumber: { //US issue706 + case kReferencedSegmentNumber: { // US issue706 int segn = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); if (segn < minReferencedSegmentNumber) minReferencedSegmentNumber = segn; @@ -6486,13 +6500,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD set_bVal(&volDiffusion, v[0]); break; } - case kParallelInformationUIH: { //SENSE factor (0065,100d) SH [F:2S] + case kParallelInformationUIH: { // SENSE factor (0065,100d) SH [F:2S] if (d.manufacturer != kMANUFACTURER_UIH) break; char accelStr[kDICOMStr]; dcmStr(lLength, &buffer[lPos], accelStr); - //char *ptr; - dcmStrDigitsDotOnlyKey(':', accelStr); //e.g. if "p2s4" return "2", if "s4" return "" + // char *ptr; + dcmStrDigitsDotOnlyKey(':', accelStr); // e.g. if "p2s4" return "2", if "s4" return "" d.accelFactPE = atof(accelStr); break; } @@ -6516,16 +6530,16 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer != kMANUFACTURER_UIH) break; float v[4]; - isDWI_UIH = true; //issue836 + isDWI_UIH = true; // issue836 dcmMultiFloatDouble(lLength, &buffer[lPos], 3, v, d.isLittleEndian); - //dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, v); - //printf(">>>%g %g %g\n", v[0], v[1], v[2]); + // dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, v); + // printf(">>>%g %g %g\n", v[0], v[1], v[2]); d.CSA.dtiV[1] = v[0]; d.CSA.dtiV[2] = v[1]; d.CSA.dtiV[3] = v[2]; - //vRLPhilips = v[0]; - //vAPPhilips = v[1]; - //vFHPhilips = v[2]; + // vRLPhilips = v[0]; + // vAPPhilips = v[1]; + // vFHPhilips = v[2]; break; } case kBitsAllocated: @@ -6537,7 +6551,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kHighBit: highBit = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; - case kIsSigned: //http://dicomiseasy.blogspot.com/2012/08/chapter-12-pixel-data.html + case kIsSigned: // http://dicomiseasy.blogspot.com/2012/08/chapter-12-pixel-data.html d.isSigned = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; case kPixelPaddingValue: @@ -6553,7 +6567,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kTE: TE = dcmStrFloat(lLength, &buffer[lPos]); - //20240229 if (d.TE <= 0.0) + // 20240229 if (d.TE <= 0.0) d.TE = TE; break; case kNumberOfAverages: @@ -6564,16 +6578,16 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kTriggerTime: { if (prefs->isIgnoreTriggerTimes) - break; //issue499 - //untested method to detect slice timing for GE PSD “epi” with multiphase option - // will not work for current PSD “epiRT” (BrainWave RT, fMRI/DTI package provided by Medical Numerics) + break; // issue499 + // untested method to detect slice timing for GE PSD “epi” with multiphase option + // will not work for current PSD “epiRT” (BrainWave RT, fMRI/DTI package provided by Medical Numerics) if ((d.manufacturer != kMANUFACTURER_GE) && (d.manufacturer != kMANUFACTURER_PHILIPS)) - break; //issue384 + break; // issue384 d.triggerDelayTime = dcmStrFloat(lLength, &buffer[lPos]); //???? issue 336 if (d.manufacturer != kMANUFACTURER_GE) break; d.CSA.sliceTiming[acquisitionTimesGE_UIH] = d.triggerDelayTime; - //printf("%g\n", d.CSA.sliceTiming[acquisitionTimesGE_UIH]); + // printf("%g\n", d.CSA.sliceTiming[acquisitionTimesGE_UIH]); acquisitionTimesGE_UIH++; break; } @@ -6611,13 +6625,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kPixelBandwidth: d.pixelBandwidth = dcmStrFloat(lLength, &buffer[lPos]); - //printWarning(" PixelBandwidth (0018,0095)====> %g @%d\n", d.pixelBandwidth, lPos); + // printWarning(" PixelBandwidth (0018,0095)====> %g @%d\n", d.pixelBandwidth, lPos); break; case kAcquisitionMatrix: if (lLength == 8) { uint16_t acquisitionMatrix[4]; - dcmMultiShorts(lLength, &buffer[lPos], 4, &acquisitionMatrix[0], d.isLittleEndian); //slice position - //phaseEncodingLines stored in either image columns or rows + dcmMultiShorts(lLength, &buffer[lPos], 4, &acquisitionMatrix[0], d.isLittleEndian); // slice position + // phaseEncodingLines stored in either image columns or rows if (acquisitionMatrix[3] > 0) d.phaseEncodingLines = acquisitionMatrix[3]; if (acquisitionMatrix[2] > 0) @@ -6632,7 +6646,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.flipAngle = dcmStrFloat(lLength, &buffer[lPos]); break; case kVariableFlipAngleFlag: - d.isVariableFlipAngle = ('Y' == toupper(buffer[lPos])); //first character is either 'y'es or 'n'o + d.isVariableFlipAngle = ('Y' == toupper(buffer[lPos])); // first character is either 'y'es or 'n'o break; case kRadionuclideHalfLife: d.radionuclideHalfLife = dcmStrFloat(lLength, &buffer[lPos]); @@ -6643,19 +6657,21 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kGantryTilt: d.gantryTilt = dcmStrFloat(lLength, &buffer[lPos]); break; - case kXRayTimeMS: //IS - d.exposureTimeMs = dcmStrInt(lLength, &buffer[lPos]);; + case kXRayTimeMS: // IS + d.exposureTimeMs = dcmStrInt(lLength, &buffer[lPos]); + ; break; - case kXRayTubeCurrent: //IS - d.xRayTubeCurrent = dcmStrInt(lLength, &buffer[lPos]);; + case kXRayTubeCurrent: // IS + d.xRayTubeCurrent = dcmStrInt(lLength, &buffer[lPos]); + ; break; - case kXRayExposure: //CTs do not have echo times, we use this field to detect different exposures: https://github.com/neurolabusc/dcm2niix/pull/48 + case kXRayExposure: // CTs do not have echo times, we use this field to detect different exposures: https://github.com/neurolabusc/dcm2niix/pull/48 if (d.TE == 0) { // for CT we will use exposure (0018,1152) whereas for MR we use echo time (0018,0081) d.isXRay = true; d.TE = dcmStrFloat(lLength, &buffer[lPos]); } break; - case kConvolutionKernel: //CS + case kConvolutionKernel: // CS dcmStr(lLength, &buffer[lPos], d.convolutionKernel); break; case kFrameDuration: @@ -6670,14 +6686,14 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kSlope: d.intenScale = dcmStrFloat(lLength, &buffer[lPos]); break; - //case kSpectroscopyDataPointColumns : + // case kSpectroscopyDataPointColumns : // d.xyzDim[4] = dcmInt(4,&buffer[lPos],d.isLittleEndian); // break; case kPhilipsSlope: if ((lLength == 4) && (d.manufacturer == kMANUFACTURER_PHILIPS)) d.intenScalePhilips = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); break; - case kMRImageDynamicScanBeginTime: { //FL + case kMRImageDynamicScanBeginTime: { // FL if (lLength != 4) break; MRImageDynamicScanBeginTime = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); @@ -6716,7 +6732,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; } case kAcquisitionMatrixText21: - //fall through to kAcquisitionMatrixText + // fall through to kAcquisitionMatrixText case kAcquisitionMatrixText: { if (d.manufacturer == kMANUFACTURER_SIEMENS) { char matStr[kDICOMStr]; @@ -6727,43 +6743,43 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } break; } - case kImageOrientationText: //issue522 + case kImageOrientationText: // issue522 if (d.manufacturer != kMANUFACTURER_SIEMENS) break; dcmStr(lLength, &buffer[lPos], d.imageOrientationText); break; case kCoilSiemens: { if (d.manufacturer == kMANUFACTURER_SIEMENS) { - //see if image from single coil "H12" or an array "HEA;HEP" - //char coilStr[kDICOMStr]; - //int coilNum; + // see if image from single coil "H12" or an array "HEA;HEP" + // char coilStr[kDICOMStr]; + // int coilNum; dcmStr(lLength, &buffer[lPos], d.coilName); if (strlen(d.coilName) < 1) break; - //printf("-->%s\n", coilStr); - //d.coilName = coilStr; - //if (coilStr[0] == 'C') break; //kludge as Nova 32-channel defaults to "C:A32" https://github.com/rordenlab/dcm2niix/issues/187 - //char *ptr; - //dcmStrDigitsOnly(coilStr); - //coilNum = (int)strtol(coilStr, &ptr, 10); + // printf("-->%s\n", coilStr); + // d.coilName = coilStr; + // if (coilStr[0] == 'C') break; //kludge as Nova 32-channel defaults to "C:A32" https://github.com/rordenlab/dcm2niix/issues/187 + // char *ptr; + // dcmStrDigitsOnly(coilStr); + // coilNum = (int)strtol(coilStr, &ptr, 10); d.coilCrc = mz_crc32X((unsigned char *)&d.coilName, strlen(d.coilName)); - //printf("%d:%s\n", d.coilNum, coilStr); - //if (*ptr != '\0') - // d.coilNum = 0; + // printf("%d:%s\n", d.coilNum, coilStr); + // if (*ptr != '\0') + // d.coilNum = 0; } break; } - case kImaPATModeText: { //e.g. Siemens iPAT x2 listed as "p2" + case kImaPATModeText: { // e.g. Siemens iPAT x2 listed as "p2" char accelStr[kDICOMStr]; dcmStr(lLength, &buffer[lPos], accelStr); char *ptr; - dcmStrDigitsOnlyKey('p', accelStr); //e.g. if "p2s4" return "2", if "s4" return "" + dcmStrDigitsOnlyKey('p', accelStr); // e.g. if "p2s4" return "2", if "s4" return "" d.accelFactPE = (float)strtof(accelStr, &ptr); if (*ptr != '\0') d.accelFactPE = 0.0; - //between slice accel + // between slice accel dcmStr(lLength, &buffer[lPos], accelStr); - dcmStrDigitsOnlyKey('s', accelStr); //e.g. if "p2s4" return "4", if "p2" return "" + dcmStrDigitsOnlyKey('s', accelStr); // e.g. if "p2s4" return "4", if "p2" return "" multiBandFactor = (int)strtol(accelStr, &ptr, 10); if (*ptr != '\0') multiBandFactor = 0.0; @@ -6772,16 +6788,16 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kLocationsInAcquisition: d.locationsInAcquisition = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; - case kUnitsPT: //CS + case kUnitsPT: // CS dcmStr(lLength, &buffer[lPos], d.unitsPT); break; - case kAttenuationCorrectionMethod: //LO + case kAttenuationCorrectionMethod: // LO dcmStr(lLength, &buffer[lPos], d.attenuationCorrectionMethod); break; - case kDecayCorrection: //CS + case kDecayCorrection: // CS dcmStr(lLength, &buffer[lPos], d.decayCorrection); break; - case kReconstructionMethod: //LO + case kReconstructionMethod: // LO dcmStr(lLength, &buffer[lPos], d.reconstructionMethod); break; case kFrameReferenceTime: @@ -6792,16 +6808,16 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kScatterFraction: d.scatterFraction = dcmStrFloat(lLength, &buffer[lPos]); - //printf("SF%g\n", d.scatterFraction); //for each slice? + // printf("SF%g\n", d.scatterFraction); //for each slice? break; case kIconImageSequence: if (lLength > 8) - break; //issue638: we will skip entire icon if there is an explicit length + break; // issue638: we will skip entire icon if there is an explicit length isIconImageSequence = true; if (sqDepthIcon < 0) sqDepthIcon = sqDepth; break; - //case kMRSeriesAcquisitionNumber: // 0x2001+(0x107B << 16 ) //IS + // case kMRSeriesAcquisitionNumber: // 0x2001+(0x107B << 16 ) //IS // mRSeriesAcquisitionNumber = dcmStrInt(lLength, &buffer[lPos]); // break; case kNumberOfDynamicScans: @@ -6812,12 +6828,12 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer != kMANUFACTURER_PHILIPS) break; TRPhilips = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); break;*/ - case kMRAcquisitionType: //detect 3D acquisition: we can reorient these without worrying about slice time correct or BVEC/BVAL orientation + case kMRAcquisitionType: // detect 3D acquisition: we can reorient these without worrying about slice time correct or BVEC/BVAL orientation if (lLength > 1) d.is2DAcq = (buffer[lPos] == '2') && (toupper(buffer[lPos + 1]) == 'D'); if (lLength > 1) d.is3DAcq = (buffer[lPos] == '3') && (toupper(buffer[lPos + 1]) == 'D'); - //dcmStr(lLength, &buffer[lPos], d.mrAcquisitionType); + // dcmStr(lLength, &buffer[lPos], d.mrAcquisitionType); break; case kBodyPartExamined: { dcmStr(lLength, &buffer[lPos], d.bodyPartExamined); @@ -6825,39 +6841,39 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } case kScanningSequence: { dcmStr(lLength, &buffer[lPos], d.scanningSequence); - //According to the DICOM standard 0018,9018 is REQUIRED for EPI raw data - // http://dicom.nema.org/MEDICAL/Dicom/2015c/output/chtml/part03/sect_C.8.13.4.html - //In practice, this is not the case for all vendors - //Fortunately, the combination of 0018,0020 and 0018,9018 appears to reliably detect EPI data - //Siemens (pre-XA) omits 0018,9018, but reports [EP] for 0018,0020 (regardless of SE/GR) - //Siemens (XA) reports 0018,9018 but omits 0018,0020 - //Canon/Toshiba omits 0018,9018, but reports [SE\EP];[GR\EP] for 0018,0020 - //GE omits 0018,9018, but reports [EP\GR];[EP\SE] for 0018,0020 - //Philips reports 0018,9018, but reports [SE];[GR] for 0018,0020 + // According to the DICOM standard 0018,9018 is REQUIRED for EPI raw data + // http://dicom.nema.org/MEDICAL/Dicom/2015c/output/chtml/part03/sect_C.8.13.4.html + // In practice, this is not the case for all vendors + // Fortunately, the combination of 0018,0020 and 0018,9018 appears to reliably detect EPI data + // Siemens (pre-XA) omits 0018,9018, but reports [EP] for 0018,0020 (regardless of SE/GR) + // Siemens (XA) reports 0018,9018 but omits 0018,0020 + // Canon/Toshiba omits 0018,9018, but reports [SE\EP];[GR\EP] for 0018,0020 + // GE omits 0018,9018, but reports [EP\GR];[EP\SE] for 0018,0020 + // Philips reports 0018,9018, but reports [SE];[GR] for 0018,0020 if ((lLength > 1) && (strstr(d.scanningSequence, "IR") != NULL)) d.isIR = true; if ((lLength > 1) && (strstr(d.scanningSequence, "EP") != NULL)) d.isEPI = true; - break; //warp + break; // warp } case kRelTablePosition: { if (d.manufacturer != kMANUFACTURER_SIEMENS) break; - dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &d.CSA.tablePos[0]); //slice position - d.CSA.tablePos[3] = -d.CSA.tablePos[3]; //reverse Z polarity, issue 726 - d.CSA.tablePos[0] = 1.0; //set + dcmMultiFloat(lLength, (char *)&buffer[lPos], 3, &d.CSA.tablePos[0]); // slice position + d.CSA.tablePos[3] = -d.CSA.tablePos[3]; // reverse Z polarity, issue 726 + d.CSA.tablePos[0] = 1.0; // set break; } case kScanningSequenceSiemens: - if (d.manufacturer == kMANUFACTURER_SIEMENS) + if (d.manufacturer == kMANUFACTURER_SIEMENS) dcmStr(lLength, &buffer[lPos], scanningSequenceSiemens); - if (d.manufacturer == kMANUFACTURER_GE) //issue690, series-level 16=DFAXDTI + if (d.manufacturer == kMANUFACTURER_GE) // issue690, series-level 16=DFAXDTI seriesdiffusionDirectionTypeGE = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; case kSequenceVariant21: if (d.manufacturer != kMANUFACTURER_SIEMENS) - break; //n.b. for GE 0021,105B' TaggingFlipAngle - //fall through... + break; // n.b. for GE 0021,105B' TaggingFlipAngle + // fall through... case kSequenceVariant: { dcmStr(lLength, &buffer[lPos], d.sequenceVariant); break; @@ -6865,7 +6881,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kScanOptions: dcmStr(lLength, &buffer[lPos], d.scanOptions, true); if ((lLength > 1) && (strstr(d.scanOptions, "PFF") != NULL)) - d.isPartialFourier = true; //e.g. GE does not populate (0018,9081) + d.isPartialFourier = true; // e.g. GE does not populate (0018,9081) break; case kSequenceName: { dcmStr(lLength, &buffer[lPos], d.sequenceName); @@ -6874,30 +6890,32 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kTRArray: { if (d.manufacturer != kMANUFACTURER_PHILIPS) break; - //support multiple TRs: issue 743 + // support multiple TRs: issue 743 const int kMax = 4; - float v[kMax] = { 0.0 }; + float v[kMax] = {0.0}; int nFloat32 = (lLength / 4); - if (nFloat32 < 2) break; + if (nFloat32 < 2) + break; if (nFloat32 > kMax) nFloat32 = kMax; - dcmMultiFloatSingle(nFloat32 * 4, &buffer[lPos], nFloat32, v, d.isLittleEndian); + dcmMultiFloatSingle(nFloat32 * 4, &buffer[lPos], nFloat32, v, d.isLittleEndian); d.numberOfTR = 0; - //count number of TRs > 0, see issue 749 - for(int i = 0; i< nFloat32; i++) + // count number of TRs > 0, see issue 749 + for (int i = 0; i < nFloat32; i++) if (v[i] > 0.0) - d.numberOfTR ++; + d.numberOfTR++; } - case kMRfMRIStatusIndicationPhilips: {//fmri volume number + case kMRfMRIStatusIndicationPhilips: { // fmri volume number if (d.manufacturer != kMANUFACTURER_PHILIPS) break; - if (volumeNumber > 0) break; //issue567 + if (volumeNumber > 0) + break; // issue567 int i = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); - if (i > 0) //only if positive value, see Magdeburg_2014 sample data from dcm_qa_philips Philips MR 51.0 + if (i > 0) // only if positive value, see Magdeburg_2014 sample data from dcm_qa_philips Philips MR 51.0 volumeNumber = i; break; } - case kMRAcquisitionTypePhilips: //kMRAcquisitionType + case kMRAcquisitionTypePhilips: // kMRAcquisitionType if (lLength > 1) d.is3DAcq = (buffer[lPos] == '3') && (toupper(buffer[lPos + 1]) == 'D'); break; @@ -6921,14 +6939,14 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kSliceOrient: { char orientStr[kDICOMStr]; - orientStr[0] = 'X'; //avoid compiler warning: orientStr filled by dcmStr + orientStr[0] = 'X'; // avoid compiler warning: orientStr filled by dcmStr dcmStr(lLength, &buffer[lPos], orientStr); if (toupper(orientStr[0]) == 'S') - d.sliceOrient = kSliceOrientSag; //sagittal + d.sliceOrient = kSliceOrientSag; // sagittal else if (toupper(orientStr[0]) == 'C') - d.sliceOrient = kSliceOrientCor; //coronal + d.sliceOrient = kSliceOrientCor; // coronal else - d.sliceOrient = kSliceOrientTra; //transverse (axial) + d.sliceOrient = kSliceOrientTra; // transverse (axial) break; } case kElscintIcon: @@ -6938,9 +6956,9 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD sqDepthIcon = sqDepth; break; case kPMSCT_RLE1: - //https://groups.google.com/forum/#!topic/comp.protocols.dicom/8HuP_aNy9Pc - //https://discourse.slicer.org/t/fail-to-load-pet-ct-gemini/8158/3 - // d.compressionScheme = kCompressPMSCT_RLE1; //force RLE + // https://groups.google.com/forum/#!topic/comp.protocols.dicom/8HuP_aNy9Pc + // https://discourse.slicer.org/t/fail-to-load-pet-ct-gemini/8158/3 + // d.compressionScheme = kCompressPMSCT_RLE1; //force RLE if (d.compressionScheme != kCompressPMSCT_RLE1) break; d.imageStart = (int)lPos + (int)lFileOffset; @@ -6959,10 +6977,10 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD B0Philips = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); set_bVal(&volDiffusion, B0Philips); break; - case kPhaseNumber: //IS issue529 + case kPhaseNumber: // IS issue529 if (d.manufacturer != kMANUFACTURER_PHILIPS) break; - d.phaseNumber = dcmStrInt(lLength, &buffer[lPos]); //see dcm_qa_philips_asl + d.phaseNumber = dcmStrInt(lLength, &buffer[lPos]); // see dcm_qa_philips_asl break; /*case kCardiacSync: //CS [TRIGGERED],[NO] if (lLength < 2) @@ -6984,7 +7002,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD // value *unset*! // GE started using this tag in 27, and annoyingly, NOT including // the b value if it is 0 for the slice. - //if((d.manufacturer != kMANUFACTURER_PHILIPS) || !is2005140FSQ){ + // if((d.manufacturer != kMANUFACTURER_PHILIPS) || !is2005140FSQ){ // d.CSA.numDti++; // if (d.CSA.numDti == 2) { //First time we know that this is a 4D DTI dataset // dti4D->S[0].V[0] = d.CSA.dtiV[0]; @@ -6993,7 +7011,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD // dti4D->S[0].V[3] = d.CSA.dtiV[3]; //} B0Philips = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); - //d.CSA.dtiV[0] = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); + // d.CSA.dtiV[0] = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); set_bVal(&volDiffusion, dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian)); // if ((d.CSA.numDti > 1) && (d.CSA.numDti < kMaxDTI4D)) // dti4D->S[d.CSA.numDti-1].V[0] = d.CSA.dtiV[0]; @@ -7009,24 +7027,24 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD // if (((d.manufacturer == kMANUFACTURER_SIEMENS) || // ((d.manufacturer == kMANUFACTURER_PHILIPS) && !is2005140FSQ)) && // (isAtFirstPatientPosition || isnan(d.patientPosition[1]))) - //if((d.manufacturer == kMANUFACTURER_SIEMENS) || ((d.manufacturer == kMANUFACTURER_PHILIPS) && !is2005140FSQ)) - if (!isDWI_UIH) { //issue836 - //if ((d.manufacturer == kMANUFACTURER_MEDISO) || (d.manufacturer == kMANUFACTURER_TOSHIBA) || (d.manufacturer == kMANUFACTURER_CANON) || (d.manufacturer == kMANUFACTURER_HITACHI) || (d.manufacturer == kMANUFACTURER_SIEMENS) || (d.manufacturer == kMANUFACTURER_PHILIPS)) { - //for kMANUFACTURER_HITACHI see https://nciphub.org/groups/qindicom/wiki/StandardcompliantenhancedmultiframeDWI + // if((d.manufacturer == kMANUFACTURER_SIEMENS) || ((d.manufacturer == kMANUFACTURER_PHILIPS) && !is2005140FSQ)) + if (!isDWI_UIH) { // issue836 + // if ((d.manufacturer == kMANUFACTURER_MEDISO) || (d.manufacturer == kMANUFACTURER_TOSHIBA) || (d.manufacturer == kMANUFACTURER_CANON) || (d.manufacturer == kMANUFACTURER_HITACHI) || (d.manufacturer == kMANUFACTURER_SIEMENS) || (d.manufacturer == kMANUFACTURER_PHILIPS)) { + // for kMANUFACTURER_HITACHI see https://nciphub.org/groups/qindicom/wiki/StandardcompliantenhancedmultiframeDWI float v[4]; - //dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, v); - //dcmMultiFloatDouble(lLength, &buffer[lPos], 3, v, d.isLittleEndian); + // dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, v); + // dcmMultiFloatDouble(lLength, &buffer[lPos], 3, v, d.isLittleEndian); dcmMultiFloatDouble(lLength, &buffer[lPos], 3, v, d.isLittleEndian); vRLPhilips = v[0]; vAPPhilips = v[1]; vFHPhilips = v[2]; - //printMessage("><>< 0018,9089:\t%g\t%g\t%g\n", v[0], v[1], v[2]); - //https://github.com/rordenlab/dcm2niix/issues/256 - //d.CSA.dtiV[1] = v[0]; - //d.CSA.dtiV[2] = v[1]; - //d.CSA.dtiV[3] = v[2]; + // printMessage("><>< 0018,9089:\t%g\t%g\t%g\n", v[0], v[1], v[2]); + // https://github.com/rordenlab/dcm2niix/issues/256 + // d.CSA.dtiV[1] = v[0]; + // d.CSA.dtiV[2] = v[1]; + // d.CSA.dtiV[3] = v[2]; hasDwiDirectionality = true; - d.isBVecWorldCoordinates = true; //e.g. Canon saved image space coordinates in Comments, world space in 0018, 9089 + d.isBVecWorldCoordinates = true; // e.g. Canon saved image space coordinates in Comments, world space in 0018, 9089 set_orientation0018_9089(&volDiffusion, lLength, &buffer[lPos], d.isLittleEndian); isHasBVec = true; } @@ -7040,17 +7058,17 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.accelFactOOP = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); break; case kSARFD: - //Siemens XA uses kSARFD instead of kSAR - // ignore as we also need to know the definition issue 668 - //d.SAR = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); - //maxSAR = fmax(maxSAR, d.SAR); + // Siemens XA uses kSARFD instead of kSAR + // ignore as we also need to know the definition issue 668 + // d.SAR = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); + // maxSAR = fmax(maxSAR, d.SAR); break; - //case kFrameAcquisitionDuration : + // case kFrameAcquisitionDuration : // frameAcquisitionDuration = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); //issue369 // break; - case kArterialSpinLabelingContrast: { //CS + case kArterialSpinLabelingContrast: { // CS char st[kDICOMStr]; - //aslFlags + // aslFlags dcmStr(lLength, &buffer[lPos], st); if (strstr(st, "PSEUDOCONTINUOUS") != NULL) d.aslFlags = (d.aslFlags | kASL_FLAG_GE_PSEUDOCONTINUOUS); @@ -7066,7 +7084,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } case kDiffusionBValueXX: { if (!(d.manufacturer == kMANUFACTURER_BRUKER)) - break; //other manufacturers provide bvec directly, rather than bmatrix + break; // other manufacturers provide bvec directly, rather than bmatrix double bMat = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); set_bMatrix(&volDiffusion, bMat, 0); isHasBMatrix = true; @@ -7077,7 +7095,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; sliceNumberMrPhilips = dcmStrInt(lLength, &buffer[lPos]); int sliceNumber = sliceNumberMrPhilips; - //use public patientPosition if it exists - fall back to private patient position + // use public patientPosition if it exists - fall back to private patient position if ((sliceNumber == 1) && (!isnan(patientPosition[1]))) { for (int k = 0; k < 4; k++) patientPositionStartPhilips[k] = patientPosition[k]; @@ -7099,12 +7117,12 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; echoTrainLengthPhil = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; - case kPrepulseDelay: //FL + case kPrepulseDelay: // FL if (d.manufacturer != kMANUFACTURER_PHILIPS) break; d.TI = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); break; - case kPrepulseType: //CS [INV] + case kPrepulseType: // CS [INV] if (d.manufacturer != kMANUFACTURER_PHILIPS) break; if (lLength < 3) @@ -7122,7 +7140,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer != kMANUFACTURER_PHILIPS) break; locationsInAcquisitionPhilips = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); - //printMessage("====> locationsInAcquisitionPhilips\t%d\n", locationsInAcquisitionPhilips); + // printMessage("====> locationsInAcquisitionPhilips\t%d\n", locationsInAcquisitionPhilips); break; case kPartialMatrixScannedPhilips: if (d.manufacturer != kMANUFACTURER_PHILIPS) @@ -7155,11 +7173,11 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD vFHPhilips = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); set_diffusion_directionPhilips(&volDiffusion, vFHPhilips, 2); break; - case kDeepLearningPhilips: { //CS + case kDeepLearningPhilips: { // CS if (d.manufacturer != kMANUFACTURER_PHILIPS) break; char st[kDICOMStr]; - //see dcm_qa_cs_dl reports `none` or `CS_SENSE_AI` + // see dcm_qa_cs_dl reports `none` or `CS_SENSE_AI` dcmStr(lLength, &buffer[lPos], st); if (strstr(st, "_AI") != NULL) { d.isDeepLearning = true; @@ -7169,27 +7187,30 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kPrivatePerFrameSq: if (d.manufacturer != kMANUFACTURER_PHILIPS) break; - //if ((vr[0] == 'S') && (vr[1] == 'Q')) break; - //if (!is2005140FSQwarned) + // if ((vr[0] == 'S') && (vr[1] == 'Q')) break; + // if (!is2005140FSQwarned) // printWarning("expected VR of 2005,140F to be 'SQ' (prior DICOM->DICOM conversion error?)\n"); is2005140FSQ = true; - //is2005140FSQwarned = true; + // is2005140FSQwarned = true; break; - case kMRImageGradientOrientationNumber : + case kMRImageGradientOrientationNumber: if (d.manufacturer == kMANUFACTURER_PHILIPS) gradientOrientationNumberPhilips = dcmStrInt(lLength, &buffer[lPos]); break; - case kMRImageLabelType : //CS ??? LBL CTL - if ((d.manufacturer != kMANUFACTURER_PHILIPS) || (lLength < 2)) break; - //TODO529: issue529 for ASL LBL/CTL "LABEL" - //if (toupper(buffer[lPos]) == 'L') isLabel = true; - if (toupper(buffer[lPos]) == 'L') d.aslFlags = kASL_FLAG_PHILIPS_LABEL; - if (toupper(buffer[lPos]) == 'C') d.aslFlags = kASL_FLAG_PHILIPS_CONTROL; + case kMRImageLabelType: // CS ??? LBL CTL + if ((d.manufacturer != kMANUFACTURER_PHILIPS) || (lLength < 2)) + break; + // TODO529: issue529 for ASL LBL/CTL "LABEL" + // if (toupper(buffer[lPos]) == 'L') isLabel = true; + if (toupper(buffer[lPos]) == 'L') + d.aslFlags = kASL_FLAG_PHILIPS_LABEL; + if (toupper(buffer[lPos]) == 'C') + d.aslFlags = kASL_FLAG_PHILIPS_CONTROL; break; - case kMRStackTablePosLong: //FL + case kMRStackTablePosLong: // FL if (d.manufacturer != kMANUFACTURER_PHILIPS) break; - d.CSA.tablePos[3] = - dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); + d.CSA.tablePos[3] = -dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); d.CSA.tablePos[0] = 1.0; break; case kMRImageDiffBValueNumber: @@ -7204,16 +7225,16 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kOriginalAttributesSq: if (lLength > 8) - break; //issue639: we will skip entire icon if there is an explicit length + break; // issue639: we will skip entire icon if there is an explicit length sqDepth04000561 = sqDepth; break; case kWaveformSq: - d.imageStart = 1; //abort!!! + d.imageStart = 1; // abort!!! printMessage("Skipping DICOM (audio not image) '%s'\n", fname); break; - case kSpectroscopyData: //kSpectroscopyDataPointColumns + case kSpectroscopyData: // kSpectroscopyDataPointColumns printMessage("Skipping Spectroscopy DICOM '%s'\n", fname); - d.xyzDim[1] = 0; //issue606 + d.xyzDim[1] = 0; // issue606 d.imageStart = (int)lPos + (int)lFileOffset; break; case kCSAImageHeaderInfo: @@ -7224,7 +7245,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.isHasPhase = d.CSA.isPhaseMap; if ((d.CSA.coilNumber > 0) && (strlen(d.coilName) < 1)) { snprintf(d.coilName, kDICOMStr, "%d", d.CSA.coilNumber); - //printf("issue631 coil name '%s'\n", d.coilName); + // printf("issue631 coil name '%s'\n", d.coilName); d.coilCrc = mz_crc32X((unsigned char *)&d.coilName, strlen(d.coilName)); } break; @@ -7238,7 +7259,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer != kMANUFACTURER_PHILIPS) break; d.RWVIntercept = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); - if (isSameFloat(0.0, d.intenIntercept)) //give precedence to standard value + if (isSameFloat(0.0, d.intenIntercept)) // give precedence to standard value d.intenIntercept = d.RWVIntercept; break; case kRealWorldSlope: @@ -7247,48 +7268,48 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.RWVScale = dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian); if (d.RWVScale > 1.0E38) d.RWVScale = 0.0; - else if (isSameFloat(1.0, d.intenScale)) //give precedence to standard value + else if (isSameFloat(1.0, d.intenScale)) // give precedence to standard value d.intenScale = d.RWVScale; break; - case kShimGradientX: + case kShimGradientX: if (d.manufacturer != kMANUFACTURER_GE) break; d.shimGradientX = dcmIntSS(lLength, &buffer[lPos], d.isLittleEndian); break; - case kShimGradientY: + case kShimGradientY: if (d.manufacturer != kMANUFACTURER_GE) break; d.shimGradientY = dcmIntSS(lLength, &buffer[lPos], d.isLittleEndian); break; - case kShimGradientZ: + case kShimGradientZ: if (d.manufacturer != kMANUFACTURER_GE) break; d.shimGradientZ = dcmIntSS(lLength, &buffer[lPos], d.isLittleEndian); break; - case kVasCollapseFlagGE: //SS issue 690 image-level 16=DiffusionDtiDicomValue or 14=DiffusionT2DicomValue (initial b0) + case kVasCollapseFlagGE: // SS issue 690 image-level 16=DiffusionDtiDicomValue or 14=DiffusionT2DicomValue (initial b0) if (d.manufacturer != kMANUFACTURER_GE) break; diffusionDirectionTypeGE = dcmIntSS(lLength, &buffer[lPos], d.isLittleEndian); break; - case kPrescanReuseString: //LO + case kPrescanReuseString: // LO if (d.manufacturer != kMANUFACTURER_GE) break; dcmStr(lLength, &buffer[lPos], d.prescanReuseString); - break; - case kUserDefineDataGE: { //0043,102A + break; + case kUserDefineDataGE: { // 0043,102A if ((d.manufacturer != kMANUFACTURER_GE) || (lLength < 128)) break; #define MY_DEBUG_GE // <- uncomment this to use following code to infer GE phase encoding direction #ifdef MY_DEBUG_GE - int isVerboseX = isVerbose; //for debugging only - in standard release we will enable user defined "isVerbose" - //int isVerboseX = 2; + int isVerboseX = isVerbose; // for debugging only - in standard release we will enable user defined "isVerbose" + // int isVerboseX = 2; if (isVerboseX > 1) printMessage(" UserDefineDataGE file offset/length %zu %u\n", lFileOffset + lPos, lLength); - if (lLength < 916) { //minimum size is hdr_offset=0, read 0x0394 + if (lLength < 916) { // minimum size is hdr_offset=0, read 0x0394 printMessage(" GE header too small to be valid (A)\n"); break; } - //debug code to export binary data + // debug code to export binary data /* char str[kDICOMStr]; snprintf(str, kDICOMStr, "%s_ge.bin",fname); @@ -7297,26 +7318,26 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD fclose (pFile); */ if ((size_t)(lPos + lLength) > MaxBufferSz) { - //we could re-read the buffer in this case, however in practice GE headers are concise so we never see this issue + // we could re-read the buffer in this case, however in practice GE headers are concise so we never see this issue printMessage(" GE header overflows buffer\n"); break; } size_t hdr_offset = dcmInt(2, &buffer[lPos + 24], true); if (isVerboseX > 1) printMessage(" header offset: %zu\n", hdr_offset); - if (lLength < (hdr_offset + 916)) { //minimum size is hdr_offset=0, read 0x0394 + if (lLength < (hdr_offset + 916)) { // minimum size is hdr_offset=0, read 0x0394 printMessage(" GE header too small to be valid (B)\n"); break; } - //size_t hdr = lPos+hdr_offset; + // size_t hdr = lPos+hdr_offset; float version = dcmFloat(4, &buffer[lPos + hdr_offset], true); if (isVerboseX > 1) printMessage(" version %g\n", version); if (version < 5.0 || version > 40.0) { - //printMessage(" GE header file format incorrect %g\n", version); + // printMessage(" GE header file format incorrect %g\n", version); break; } - //char const *hdr = &buffer[lPos + hdr_offset]; + // char const *hdr = &buffer[lPos + hdr_offset]; char *hdr = (char *)&buffer[lPos + hdr_offset]; int epi_chk_off = 0x003a; int pepolar_off = 0x0030; @@ -7325,12 +7346,12 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD hdr += 0x004c; kydir_off -= 0x008c; } - //int seqOrInter =dcmInt(2,(unsigned char*)(hdr + pepolar_off-638),true); - //int seqOrInter2 =dcmInt(2,(unsigned char*)(hdr + kydir_off-638),true); - //printf("%d %d<<<\n", seqOrInter,seqOrInter2); - //check if EPI + // int seqOrInter =dcmInt(2,(unsigned char*)(hdr + pepolar_off-638),true); + // int seqOrInter2 =dcmInt(2,(unsigned char*)(hdr + kydir_off-638),true); + // printf("%d %d<<<\n", seqOrInter,seqOrInter2); + // check if EPI if (true) { - //int check = *(short const *)(hdr + epi_chk_off) & 0x800; + // int check = *(short const *)(hdr + epi_chk_off) & 0x800; int check = dcmInt(2, (unsigned char *)hdr + epi_chk_off, true) & 0x800; if (check == 0) { if (isVerboseX > 1) @@ -7338,31 +7359,31 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; } } - //Check for PE polarity - // int flag1 = *(short const *)(hdr + pepolar_off) & 0x0004; - //Check for ky direction (view order) - // int flag2 = *(int const *)(hdr + kydir_off); + // Check for PE polarity + // int flag1 = *(short const *)(hdr + pepolar_off) & 0x0004; + // Check for ky direction (view order) + // int flag2 = *(int const *)(hdr + kydir_off); int phasePolarityFlag = dcmInt(2, (unsigned char *)hdr + pepolar_off, true) & 0x0004; - //Check for ky direction (view order) + // Check for ky direction (view order) int sliceOrderFlag = dcmInt(2, (unsigned char *)hdr + kydir_off, true); if (isVerboseX > 1) printMessage(" GE phasePolarity/sliceOrder flags %d %d\n", phasePolarityFlag, sliceOrderFlag); - if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_UNKNOWN) { //issue674 precedence of 0018,9034 over 0043,102A + if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_UNKNOWN) { // issue674 precedence of 0018,9034 over 0043,102A if (phasePolarityFlag == kGE_PHASE_ENCODING_POLARITY_FLIPPED) d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED; if (phasePolarityFlag == kGE_PHASE_ENCODING_POLARITY_UNFLIPPED) d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED; if (sliceOrderFlag == kGE_SLICE_ORDER_BOTTOM_UP) { - //https://cfmriweb.ucsd.edu/Howto/3T/operatingtips.html + // https://cfmriweb.ucsd.edu/Howto/3T/operatingtips.html if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_UNFLIPPED) d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED; else d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED; } } -//if (sliceOrderFlag == kGE_SLICE_ORDER_TOP_DOWN) +// if (sliceOrderFlag == kGE_SLICE_ORDER_TOP_DOWN) // d.sliceOrderGE = kGE_SLICE_ORDER_TOP_DOWN; -//if (sliceOrderFlag == kGE_SLICE_ORDER_BOTTOM_UP) +// if (sliceOrderFlag == kGE_SLICE_ORDER_BOTTOM_UP) // d.sliceOrderGE = kGE_SLICE_ORDER_BOTTOM_UP; #endif break; @@ -7371,7 +7392,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (d.manufacturer == kMANUFACTURER_GE) d.effectiveEchoSpacingGE = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; - case kImageTypeGE: { //0/1/2/3 for magnitude/phase/real/imaginary + case kImageTypeGE: { // 0/1/2/3 for magnitude/phase/real/imaginary if (d.manufacturer != kMANUFACTURER_GE) break; int dt = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); @@ -7392,15 +7413,15 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.isDiffusion = true; } break; - case kEpiRTGroupDelayGE: //FL + case kEpiRTGroupDelayGE: // FL if (d.manufacturer != kMANUFACTURER_GE) break; d.groupDelay = dcmFloat(lLength, &buffer[lPos], d.isLittleEndian); - d.groupDelay *= 1000.0; //sec -> ms + d.groupDelay *= 1000.0; // sec -> ms // If kEpiRTGroupDelayGE (0043,107C) exists, epiRT d.epiVersionGE = 1; //-1 = not epi, 0 = epi, 1 = epiRT break; - case kAssetRFactorsGE: { //DS issue427GE + case kAssetRFactorsGE: { // DS issue427GE if (d.manufacturer != kMANUFACTURER_GE) break; float PhaseSlice[3]; @@ -7411,11 +7432,11 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.accelFactOOP = 1.0f / PhaseSlice[2]; break; } - case kASLContrastTechniqueGE: { //CS + case kASLContrastTechniqueGE: { // CS if (d.manufacturer != kMANUFACTURER_GE) break; char st[kDICOMStr]; - //aslFlags + // aslFlags dcmStr(lLength, &buffer[lPos], st); if (strstr(st, "PSEUDOCONTINUOUS") != NULL) d.aslFlags = (d.aslFlags | kASL_FLAG_GE_PSEUDOCONTINUOUS); @@ -7423,7 +7444,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.aslFlags = (d.aslFlags | kASL_FLAG_GE_CONTINUOUS); break; } - case kASLLabelingTechniqueGE: { //LO issue427GE + case kASLLabelingTechniqueGE: { // LO issue427GE if (d.manufacturer != kMANUFACTURER_GE) break; char st[kDICOMStr]; @@ -7434,49 +7455,49 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.aslFlags = (d.aslFlags | kASL_FLAG_GE_3DPCASL); break; } - case kDurationLabelPulseGE: { //IS + case kDurationLabelPulseGE: { // IS if (d.manufacturer != kMANUFACTURER_GE) break; d.durationLabelPulseGE = dcmStrInt(lLength, &buffer[lPos]); break; } - case kTableDeltaGE: { //DS issue726 + case kTableDeltaGE: { // DS issue726 if (d.manufacturer != kMANUFACTURER_GE) break; tableDeltaGE = dcmStrFloat(lLength, &buffer[lPos]); } - case kMRTablePositionInformation: { //LO issue726 + case kMRTablePositionInformation: { // LO issue726 if (d.manufacturer != kMANUFACTURER_GE) break; - //LO array of floats stored in LONG STRING! - // [960.5\400\17.9108\0\-9999\-9999] - //we want 3rd value, e.g. 17.9: + // LO array of floats stored in LONG STRING! + // [960.5\400\17.9108\0\-9999\-9999] + // we want 3rd value, e.g. 17.9: float v[5]; dcmMultiFloat(lLength, (char *)&buffer[lPos], 5, v); d.CSA.tablePos[3] = v[3] - tableDeltaGE; d.CSA.tablePos[0] = 1.0; break; } - case kMultiBandGE: { //LO issue427GE + case kMultiBandGE: { // LO issue427GE if (d.manufacturer != kMANUFACTURER_GE) break; - //LO array: Value 1 = Multiband factor, Value 2 = Slice FOV Shift Factor, Value 3 = Calibration method + // LO array: Value 1 = Multiband factor, Value 2 = Slice FOV Shift Factor, Value 3 = Calibration method int mb = dcmStrInt(lLength, &buffer[lPos]); if (mb > 1) d.CSA.multiBandFactor = mb; break; } - case kCompressedSensingParameters: { //LO issue672 + case kCompressedSensingParameters: { // LO issue672 if ((d.manufacturer != kMANUFACTURER_GE) || (lLength < 2)) break; - //0043,10b7) LO [1.24\1\10\0] # 12, 4 Compressed Sensing Parameters + // 0043,10b7) LO [1.24\1\10\0] # 12, 4 Compressed Sensing Parameters float cs = dcmStrFloat(lLength, &buffer[lPos]); if (cs > 1.0) d.compressedSensingFactor = cs; - //dcmStr(lLength, &buffer[lPos], d.compressedSensingText); + // dcmStr(lLength, &buffer[lPos], d.compressedSensingText); break; } - case kDeepLearningParameters: { //LO issue672 + case kDeepLearningParameters: { // LO issue672 if ((d.manufacturer != kMANUFACTURER_GE) || (lLength < 2)) break; //(0043,10ca) LO [0.75\High] @@ -7486,37 +7507,37 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } case kGeiisFlag: if ((lLength > 4) && (buffer[lPos] == 'G') && (buffer[lPos + 1] == 'E') && (buffer[lPos + 2] == 'I') && (buffer[lPos + 3] == 'I')) { - //read a few digits, as bug is specific to GEIIS, while GEMS are fine + // read a few digits, as bug is specific to GEIIS, while GEMS are fine printWarning("GEIIS violates the DICOM standard. Inspect results and admonish your vendor.\n"); isIconImageSequence = true; if (sqDepthIcon < 0) sqDepthIcon = sqDepth; - //geiisBug = true; //compressed thumbnails do not follow transfer syntax! GE should not reuse pulbic tags for these proprietary images http://sonca.kasshin.net/gdcm/Doc/GE_ImageThumbnails + // geiisBug = true; //compressed thumbnails do not follow transfer syntax! GE should not reuse pulbic tags for these proprietary images http://sonca.kasshin.net/gdcm/Doc/GE_ImageThumbnails } break; case kStudyComments: { - //char commentStr[kDICOMStr]; - //dcmStr(lLength, &buffer[lPos], commentStr); - //printf(">> %s\n", commentStr); + // char commentStr[kDICOMStr]; + // dcmStr(lLength, &buffer[lPos], commentStr); + // printf(">> %s\n", commentStr); break; } case kProcedureStepDescription: dcmStr(lLength, &buffer[lPos], d.procedureStepDescription); break; - case kOrientationACR: //use in emergency if kOrientation is not present! + case kOrientationACR: // use in emergency if kOrientation is not present! if (!isOrient) dcmMultiFloat(lLength, (char *)&buffer[lPos], 6, d.orient); break; case kOrientation: { - if (isOrient) { //already read orient - read for this slice to see if it varies (localizer) + if (isOrient) { // already read orient - read for this slice to see if it varies (localizer) float orient[7]; dcmMultiFloat(lLength, (char *)&buffer[lPos], 6, orient); if ((!isSameFloatGE(d.orient[1], orient[1]) || !isSameFloatGE(d.orient[2], orient[2]) || !isSameFloatGE(d.orient[3], orient[3]) || - !isSameFloatGE(d.orient[4], orient[4]) || !isSameFloatGE(d.orient[5], orient[5]) || !isSameFloatGE(d.orient[6], orient[6]))) { + !isSameFloatGE(d.orient[4], orient[4]) || !isSameFloatGE(d.orient[5], orient[5]) || !isSameFloatGE(d.orient[6], orient[6]))) { if (!d.isLocalizer) printMessage("slice orientation varies (localizer?) [%g %g %g %g %g %g] != [%g %g %g %g %g %g]\n", - d.orient[1], d.orient[2], d.orient[3], d.orient[4], d.orient[5], d.orient[6], - orient[1], orient[2], orient[3], orient[4], orient[5], orient[6]); + d.orient[1], d.orient[2], d.orient[3], d.orient[4], d.orient[5], d.orient[6], + orient[1], orient[2], orient[3], orient[4], orient[5], orient[6]); d.isLocalizer = true; } } @@ -7524,13 +7545,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD vec3 readV = setVec3(d.orient[1], d.orient[2], d.orient[3]); vec3 phaseV = setVec3(d.orient[4], d.orient[5], d.orient[6]); sliceV = crossProduct(readV, phaseV); - //printf("sliceV %g %g %g\n", sliceV.v[0], sliceV.v[1], sliceV.v[2]); + // printf("sliceV %g %g %g\n", sliceV.v[0], sliceV.v[1], sliceV.v[2]); isOrient = true; break; } - case kTemporalPosition: //fall through, both kSliceNumberMrPhilips (2001,100A) and kTemporalPosition are is + case kTemporalPosition: // fall through, both kSliceNumberMrPhilips (2001,100A) and kTemporalPosition are is volumeNumber = dcmStrInt(lLength, &buffer[lPos]); - //temporalPositionIdentifier = volumeNumber; + // temporalPositionIdentifier = volumeNumber; break; case kTemporalResolution: temporalResolutionMS = dcmStrFloat(lLength, &buffer[lPos]); @@ -7538,25 +7559,25 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD case kImagesInAcquisition: imagesInAcquisition = dcmStrInt(lLength, &buffer[lPos]); break; - //case kSliceLocation : //optional so useless, infer from image position patient (0020,0032) and image orientation (0020,0037) + // case kSliceLocation : //optional so useless, infer from image position patient (0020,0032) and image orientation (0020,0037) // sliceLocation = dcmStrFloat(lLength, &buffer[lPos]); // break; case kImageStart: - //if ((!geiisBug) && (!isIconImageSequence)) //do not exit for proprietary thumbnails + // if ((!geiisBug) && (!isIconImageSequence)) //do not exit for proprietary thumbnails if (isIconImageSequence) { - //20200116 see example from Tashrif Bilah that saves GEIIS thumbnails uncompressed - // therefore, the next couple lines are not a perfect detection for GEIIS thumbnail icons - //int imgBytes = (d.xyzDim[1] * d.xyzDim[2] * int(d.bitsAllocated / 8)); - //if (imgBytes == lLength) + // 20200116 see example from Tashrif Bilah that saves GEIIS thumbnails uncompressed + // therefore, the next couple lines are not a perfect detection for GEIIS thumbnail icons + // int imgBytes = (d.xyzDim[1] * d.xyzDim[2] * int(d.bitsAllocated / 8)); + // if (imgBytes == lLength) // isIconImageSequence = false; if ((isIconImageSequence) && (sqDepth < 1)) printWarning("Assuming 7FE0,0010 refers to an icon not the main image\n"); } - if ((d.compressionScheme == kCompressNone) && (!isIconImageSequence)) //do not exit for proprietary thumbnails + if ((d.compressionScheme == kCompressNone) && (!isIconImageSequence)) // do not exit for proprietary thumbnails d.imageStart = (int)lPos + (int)lFileOffset; - //geiisBug = false; - //http://www.dclunie.com/medical-image-faq/html/part6.html - //unlike raw data, Encapsulated data is stored as Fragments contained in Items that are the Value field of Pixel Data + // geiisBug = false; + // http://www.dclunie.com/medical-image-faq/html/part6.html + // unlike raw data, Encapsulated data is stored as Fragments contained in Items that are the Value field of Pixel Data if ((d.compressionScheme != kCompressNone) && (!isIconImageSequence)) { isEncapsulatedData = true; encapsulatedDataImageStart = (int)lPos + (int)lFileOffset; @@ -7566,31 +7587,31 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; case kImageStartFloat: d.isFloat = true; - if (!isIconImageSequence) //do not exit for proprietary thumbnails + if (!isIconImageSequence) // do not exit for proprietary thumbnails d.imageStart = (int)lPos + (int)lFileOffset; isIconImageSequence = false; break; case kImageStartDouble: printWarning("Double-precision DICOM conversion untested: please provide samples to developer\n"); d.isFloat = true; - if (!isIconImageSequence) //do not exit for proprietary thumbnails + if (!isIconImageSequence) // do not exit for proprietary thumbnails d.imageStart = (int)lPos + (int)lFileOffset; isIconImageSequence = false; break; - } //switch/case for groupElement - if ((((groupElement >> 8) & 0xFF) == 0x60) && (groupElement % 2 == 0) && ((groupElement & 0xFF) < 0x1E)) { //Group 60xx: OverlayGroup http://dicom.nema.org/dicom/2013/output/chtml/part03/sect_C.9.html - //even group numbers 0x6000..0x601E + } // switch/case for groupElement + if ((((groupElement >> 8) & 0xFF) == 0x60) && (groupElement % 2 == 0) && ((groupElement & 0xFF) < 0x1E)) { // Group 60xx: OverlayGroup http://dicom.nema.org/dicom/2013/output/chtml/part03/sect_C.9.html + // even group numbers 0x6000..0x601E int overlayN = ((groupElement & 0xFF) >> 1); - //printf("%08x %d %d\n", groupElement, (groupElement & 0xFF), overlayN); + // printf("%08x %d %d\n", groupElement, (groupElement & 0xFF), overlayN); int element = groupElement >> 16; switch (element) { - case 0x0010: //US OverlayRows + case 0x0010: // US OverlayRows overlayRows = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; - case 0x0011: //US OverlayColumns + case 0x0011: // US OverlayColumns overlayCols = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); break; - case 0x0050: { //SSx2! OverlayOrigin + case 0x0050: { // SSx2! OverlayOrigin if (lLength != 4) break; int row = dcmInt(2, &buffer[lPos], d.isLittleEndian); @@ -7601,20 +7622,20 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD overlayOK = false; break; } - case 0x0100: { //US OverlayBitsAllocated + case 0x0100: { // US OverlayBitsAllocated int bits = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); if (bits == 1) break; - //old style Burned-In + // old style Burned-In printMessage("Illegal/Obsolete DICOM (%s): Overlay Bits Allocated must be 1, not %d\n", fname, bits); overlayOK = false; break; } - case 0x0102: { //US OverlayBitPosition + case 0x0102: { // US OverlayBitPosition int pos = dcmInt(lLength, &buffer[lPos], d.isLittleEndian); if (pos == 0) break; - //old style Burned-In + // old style Burned-In printMessage("Illegal/Obsolete DICOM (%s): Overlay Bit Position shall be 0, not %d\n", fname, pos); overlayOK = false; break; @@ -7625,29 +7646,29 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD break; } } - } //Group 60xx even values 0x6000..0x601E https://www.medicalconnections.co.uk/kb/Number-Of-Overlays-In-Image/ + } // Group 60xx even values 0x6000..0x601E https://www.medicalconnections.co.uk/kb/Number-Of-Overlays-In-Image/ #ifndef USING_R if (isVerbose > 1) { - //dcm2niix i fast because it does not use a dictionary. - // this is a very incomplete DICOM header report, and not a substitute for tools like dcmdump - // the purpose is to see how dcm2niix has parsed the image for diagnostics - // this section will report very little for implicit data - //if (d.isHasReal) printf("r");else printf("m"); + // dcm2niix i fast because it does not use a dictionary. + // this is a very incomplete DICOM header report, and not a substitute for tools like dcmdump + // the purpose is to see how dcm2niix has parsed the image for diagnostics + // this section will report very little for implicit data + // if (d.isHasReal) printf("r");else printf("m"); char str[kDICOMStr]; char realCh = ' '; if (realGroupElement != groupElement) - realCh = '~'; //indicate we are ignoring this tag (e.g. wrapped in 0400,0561) + realCh = '~'; // indicate we are ignoring this tag (e.g. wrapped in 0400,0561) snprintf(str, kDICOMStr, "%*c%04x,%04x%c %u@%zu ", sqDepth + 1, ' ', realGroupElement & 65535, realGroupElement >> 16, realCh, lLength, lFileOffset + lPos); bool isStr = false; if ((d.isExplicitVR) && (realGroupElement != kItemTag)) { - //snprintf(str, kDICOMStr, "%s%c%c ", str, vr[0], vr[1]); - //if (snprintf(str2, kDICOMStr-1, "%s%c%c", str, vr[0], vr[1]) < 0) exit(EXIT_FAILURE); + // snprintf(str, kDICOMStr, "%s%c%c ", str, vr[0], vr[1]); + // if (snprintf(str2, kDICOMStr-1, "%s%c%c", str, vr[0], vr[1]) < 0) exit(EXIT_FAILURE); strncat(str, &vr[0], 1); - str[kDICOMStr-1] = '\0'; //silence warning -Wstringop-truncation + str[kDICOMStr - 1] = '\0'; // silence warning -Wstringop-truncation strncat(str, &vr[1], 1); - str[kDICOMStr-1] = '\0'; //silence warning -Wstringop-truncation + str[kDICOMStr - 1] = '\0'; // silence warning -Wstringop-truncation strcat(str, " "); - //snprintf(str, kDICOMStr, "%s%c%c ", str2, vr[0], vr[1]); + // snprintf(str, kDICOMStr, "%s%c%c ", str2, vr[0], vr[1]); char str2[kDICOMStr] = ""; if ((vr[0] == 'F') && (vr[1] == 'D')) snprintf(str2, kDICOMStr, "%g ", dcmFloatDouble(lLength, &buffer[lPos], d.isLittleEndian)); @@ -7691,38 +7712,38 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD isStr = true; if ((vr[0] == 'U') && (vr[1] == 'T')) isStr = true; - strncat(str, str2, kDICOMStr-4); - str[kDICOMStr-1] = '\0'; + strncat(str, str2, kDICOMStr - 4); + str[kDICOMStr - 1] = '\0'; } else - isStr = (lLength > 12); //implicit encoding: not always true as binary vectors may exceed 12 bytes, but often true + isStr = (lLength > 12); // implicit encoding: not always true as binary vectors may exceed 12 bytes, but often true if (lLength > 128) { printMessage("%s<%d bytes>\n", str, lLength); - } else if (isStr) { //if length is greater than 8 bytes (+4 hdr) the MIGHT be a string + } else if (isStr) { // if length is greater than 8 bytes (+4 hdr) the MIGHT be a string char tagStr[kDICOMStr]; - //tagStr[0] = 'X'; //avoid compiler warning: orientStr filled by dcmStr + // tagStr[0] = 'X'; //avoid compiler warning: orientStr filled by dcmStr strcpy(tagStr, ""); if (lLength > 0) dcmStr(lLength, &buffer[lPos], tagStr); if (strlen(tagStr) > 1) { for (size_t pos = 0; pos < strlen(tagStr); pos++) - if ((tagStr[pos] == '<') || (tagStr[pos] == '>') || (tagStr[pos] == ':') || (tagStr[pos] == '"') || (tagStr[pos] == '\\') || (tagStr[pos] == '/') || (tagStr[pos] < 32) //issue398 - //|| (tagStr[pos] == '^') || (tagStr[pos] < 33) + if ((tagStr[pos] == '<') || (tagStr[pos] == '>') || (tagStr[pos] == ':') || (tagStr[pos] == '"') || (tagStr[pos] == '\\') || (tagStr[pos] == '/') || (tagStr[pos] < 32) // issue398 + //|| (tagStr[pos] == '^') || (tagStr[pos] < 33) || (tagStr[pos] == '*') || (tagStr[pos] == '|') || (tagStr[pos] == '?')) tagStr[pos] = '_'; } printMessage("%s %s\n", str, tagStr); } else printMessage("%s\n", str); - //if (d.isExplicitVR) printMessage(" VR=%c%c\n", vr[0], vr[1]); - } //printMessage(" tag=%04x,%04x length=%u pos=%ld %c%c nest=%d\n", groupElement & 65535,groupElement>>16, lLength, lPos,vr[0], vr[1], nest); + // if (d.isExplicitVR) printMessage(" VR=%c%c\n", vr[0], vr[1]); + } // printMessage(" tag=%04x,%04x length=%u pos=%ld %c%c nest=%d\n", groupElement & 65535,groupElement>>16, lLength, lPos,vr[0], vr[1], nest); #endif lPos = lPos + (lLength); - } //while d.imageStart == 0 + } // while d.imageStart == 0 free(buffer); if (d.bitsStored < 0) d.isValid = false; - //printf("%d bval=%g bvec=%g %g %g<<<\n", d.CSA.numDti, d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]); - //printMessage("><>< DWI bxyz %g %g %g %g\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]); + // printf("%d bval=%g bvec=%g %g %g<<<\n", d.CSA.numDti, d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]); + // printMessage("><>< DWI bxyz %g %g %g %g\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]); if (encapsulatedDataFragmentStart > 0) { if ((encapsulatedDataFragments > 1) && (encapsulatedDataFragments == numberOfFrames) && (encapsulatedDataFragments < kMaxDTI4D)) { printWarning("Compressed image stored as %d fragments: if conversion fails decompress with gdcmconv, Osirix, dcmdjpeg or dcmjp2k %s\n", encapsulatedDataFragments, fname); @@ -7731,28 +7752,28 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD printError("Compressed image stored as %d fragments: decompress with gdcmconv, Osirix, dcmdjpeg or dcmjp2k %s\n", encapsulatedDataFragments, fname); } else { d.imageStart = encapsulatedDataFragmentStart; - //dti4D->fragmentOffset[0] = -1; + // dti4D->fragmentOffset[0] = -1; } } else if ((isEncapsulatedData) && (d.imageStart < 128)) { - //http://www.dclunie.com/medical-image-faq/html/part6.html - //Uncompressed data (unencapsulated) is sent in DICOM as a series of raw bytes or words (little or big endian) in the Value field of the Pixel Data element (7FE0,0010). Encapsulated data on the other hand is sent not as raw bytes or words but as Fragments contained in Items that are the Value field of Pixel Data + // http://www.dclunie.com/medical-image-faq/html/part6.html + // Uncompressed data (unencapsulated) is sent in DICOM as a series of raw bytes or words (little or big endian) in the Value field of the Pixel Data element (7FE0,0010). Encapsulated data on the other hand is sent not as raw bytes or words but as Fragments contained in Items that are the Value field of Pixel Data printWarning("DICOM violation (contact vendor): compressed image without image fragments, assuming image offset defined by 0x7FE0,x0010: %s\n", fname); d.imageStart = encapsulatedDataImageStart; } else if ((!isEncapsulatedData) && (d.imageStart < 128)) { - //issue639 d.samplesPerPixel == 3 - int imageStart = (int) (lPos- lLength); - int imgBytes = (int) (lLength); - int imgBytesExpected = (d.bitsAllocated >> 3) * d.samplesPerPixel * d.xyzDim[1] * d.xyzDim[2] ; + // issue639 d.samplesPerPixel == 3 + int imageStart = (int)(lPos - lLength); + int imgBytes = (int)(lLength); + int imgBytesExpected = (d.bitsAllocated >> 3) * d.samplesPerPixel * d.xyzDim[1] * d.xyzDim[2]; if ((imgBytes >= imgBytesExpected) && (d.xyzDim[1] > 1) && (d.xyzDim[2] > 1)) { printf("Assuming final tag is Pixel Data (7fe0,0010) (issue 639)\n"); d.imageStart = imageStart; } } if ((d.manufacturer == kMANUFACTURER_GE) && (d.groupDelay > 0.0)) - d.TR += d.groupDelay; //Strangely, for GE the sample rate is (0018,0080) + ((0043,107c) * 1000.0) + d.TR += d.groupDelay; // Strangely, for GE the sample rate is (0018,0080) + ((0043,107c) * 1000.0) if ((d.modality == kMODALITY_PT) && (PETImageIndex > 0)) { - d.imageNum = PETImageIndex; //https://github.com/rordenlab/dcm2niix/issues/184 - //printWarning("PET scan using 0054,1330 for image number %d\n", PETImageIndex); + d.imageNum = PETImageIndex; // https://github.com/rordenlab/dcm2niix/issues/184 + // printWarning("PET scan using 0054,1330 for image number %d\n", PETImageIndex); } if (d.isHasOverlay) { if ((overlayCols > 0) && (d.xyzDim[1] != overlayCols)) @@ -7762,10 +7783,10 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (!overlayOK) d.isHasOverlay = false; } -//Recent Philips images include DateTime (0008,002A) but not separate date and time (0008,0022 and 0008,0032) + // Recent Philips images include DateTime (0008,002A) but not separate date and time (0008,0022 and 0008,0032) if ((strlen(acquisitionDateTimeTxt) > (kYYYYMMDDlen + 5)) && (!isFloatDiff(d.acquisitionTime, 0.0f)) && (!isFloatDiff(d.acquisitionDate, 0.0f))) { // 20161117131643.80000 -> date 20161117 time 131643.80000 - //printMessage("acquisitionDateTime %s\n",acquisitionDateTimeTxt); + // printMessage("acquisitionDateTime %s\n",acquisitionDateTimeTxt); char acquisitionDateTxt[kDICOMStr]; memcpy(acquisitionDateTxt, acquisitionDateTimeTxt, kYYYYMMDDlen); acquisitionDateTxt[kYYYYMMDDlen] = '\0'; // IMPORTANT! @@ -7777,11 +7798,11 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.acquisitionTime = atof(acquisitionTimeTxt); } d.dateTime = (atof(d.studyDate) * 1000000) + atof(d.studyTime); - //printMessage("slices in Acq %d %d %g %g\n",locationsInAcquisitionGE, d.locationsInAcquisition, d.xyzMM[3], d.zSpacing); + // printMessage("slices in Acq %d %d %g %g\n",locationsInAcquisitionGE, d.locationsInAcquisition, d.xyzMM[3], d.zSpacing); if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.locationsInAcquisition == 0)) d.locationsInAcquisition = locationsInAcquisitionPhilips; if ((d.manufacturer == kMANUFACTURER_GE) && (imagesInAcquisition > 0)) - d.locationsInAcquisition = imagesInAcquisition; //e.g. if 72 slices acquired but interpolated as 144 + d.locationsInAcquisition = imagesInAcquisition; // e.g. if 72 slices acquired but interpolated as 144 if ((d.manufacturer == kMANUFACTURER_GE) && (d.locationsInAcquisition > 0) && (locationsInAcquisitionGE > 0) && (d.locationsInAcquisition != locationsInAcquisitionGE)) { if (isVerbose) printMessage("Check number of slices, discrepancy between tags (0020,1002; 0021,104F; 0054,0081) (%d vs %d) %s\n", locationsInAcquisitionGE, d.locationsInAcquisition, fname); @@ -7789,13 +7810,13 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD int zipFactor = (int)roundf(d.xyzMM[3] / d.zSpacing); if (zipFactor > 1) { d.interp3D = zipFactor; - //printMessage("Issue 373: Check for ZIP2 Factor: %d SliceThickness+SliceGap: %f, SpacingBetweenSlices: %f \n", zipFactor, d.xyzMM[3], d.zSpacing); + // printMessage("Issue 373: Check for ZIP2 Factor: %d SliceThickness+SliceGap: %f, SpacingBetweenSlices: %f \n", zipFactor, d.xyzMM[3], d.zSpacing); locationsInAcquisitionGE *= zipFactor; // Multiply number of slices by ZIP factor. Do this prior to checking for conflict below (?). } - if (isGEfieldMap) { //issue501 : to do check zip factor - //Volume 1) derived phase field map [Hz] and 2) magnitude volume. - //issue 777 while a fieldmap is technically derived, do not exclude with -i y - bool isDerived = (d.imageNum <= locationsInAcquisitionGE); //first volume + if (isGEfieldMap) { // issue501 : to do check zip factor + // Volume 1) derived phase field map [Hz] and 2) magnitude volume. + // issue 777 while a fieldmap is technically derived, do not exclude with -i y + bool isDerived = (d.imageNum <= locationsInAcquisitionGE); // first volume d.isRealIsPhaseMapHz = isDerived; d.isHasReal = isDerived; } @@ -7809,15 +7830,15 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if ((d.manufacturer == kMANUFACTURER_GE) && (d.locationsInAcquisition == 0)) d.locationsInAcquisition = locationsInAcquisitionGE; if (d.zSpacing > 0.0) - d.xyzMM[3] = d.zSpacing; //use zSpacing if provided: depending on vendor, kZThick may or may not include a slice gap - //printMessage("patientPositions = %d XYZT = %d slicePerVol = %d numberOfDynamicScans %d\n",patientPositionNum,d.xyzDim[3], d.locationsInAcquisition, d.numberOfDynamicScans); + d.xyzMM[3] = d.zSpacing; // use zSpacing if provided: depending on vendor, kZThick may or may not include a slice gap + // printMessage("patientPositions = %d XYZT = %d slicePerVol = %d numberOfDynamicScans %d\n",patientPositionNum,d.xyzDim[3], d.locationsInAcquisition, d.numberOfDynamicScans); if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (patientPositionNum > d.xyzDim[3])) { - d.CSA.numDti = d.xyzDim[3]; //issue506 - printMessage("Please check slice thicknesses: Philips R3.2.2 bug can disrupt estimation (%d positions reported for %d slices)\n", patientPositionNum, d.xyzDim[3]); //Philips reported different positions for each slice! + d.CSA.numDti = d.xyzDim[3]; // issue506 + printMessage("Please check slice thicknesses: Philips R3.2.2 bug can disrupt estimation (%d positions reported for %d slices)\n", patientPositionNum, d.xyzDim[3]); // Philips reported different positions for each slice! } if ((d.imageStart > 144) && (d.xyzDim[1] > 1) && (d.xyzDim[2] > 1)) d.isValid = true; - //if ((d.imageStart > 144) && (d.xyzDim[1] >= 1) && (d.xyzDim[2] >= 1) && (d.xyzDim[4] > 1)) //Spectroscopy + // if ((d.imageStart > 144) && (d.xyzDim[1] >= 1) && (d.xyzDim[2] >= 1) && (d.xyzDim[4] > 1)) //Spectroscopy // d.isValid = true; if ((d.xyzMM[1] > FLT_EPSILON) && (d.xyzMM[2] < FLT_EPSILON)) { printMessage("Please check voxel size\n"); @@ -7831,48 +7852,48 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD printMessage("Unable to determine slice thickness: please check voxel size\n"); d.xyzMM[3] = 1.0; } - //printMessage("Patient Position\t%g\t%g\t%g\tThick\t%g\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3]); - //printMessage("Patient Position\t%g\t%g\t%g\tThick\t%g\tStart\t%d\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3], d.imageStart); - // printMessage("ser %ld\n", d.seriesNum); - //int kEchoMult = 100; //For Siemens/GE Series 1,2,3... save 2nd echo as 201, 3rd as 301, etc - //if (d.seriesNum > 100) - // kEchoMult = 10; //For Philips data Saved as Series 101,201,301... save 2nd echo as 111, 3rd as 121, etc - //if (coilNum > 0) //segment images with multiple coils - // d.seriesNum = d.seriesNum + (100*coilNum); - //if (d.echoNum > 1) //segment images with multiple echoes - // d.seriesNum = d.seriesNum + (kEchoMult*d.echoNum); + // printMessage("Patient Position\t%g\t%g\t%g\tThick\t%g\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3]); + // printMessage("Patient Position\t%g\t%g\t%g\tThick\t%g\tStart\t%d\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3], d.imageStart); + // printMessage("ser %ld\n", d.seriesNum); + // int kEchoMult = 100; //For Siemens/GE Series 1,2,3... save 2nd echo as 201, 3rd as 301, etc + // if (d.seriesNum > 100) + // kEchoMult = 10; //For Philips data Saved as Series 101,201,301... save 2nd echo as 111, 3rd as 121, etc + // if (coilNum > 0) //segment images with multiple coils + // d.seriesNum = d.seriesNum + (100*coilNum); + // if (d.echoNum > 1) //segment images with multiple echoes + // d.seriesNum = d.seriesNum + (kEchoMult*d.echoNum); if (isPaletteColor) { d.isValid = false; - d.isDerived = true; //to my knowledge, palette images always derived + d.isDerived = true; // to my knowledge, palette images always derived printWarning("Photometric Interpretation 'PALETTE COLOR' not supported\n"); } if ((isHasBMatrix) && (!isHasBVec)) printWarning("Underspecified BMatrix without BVector (issue 265)\n"); if ((d.compressionScheme == kCompress50) && (d.bitsAllocated > 8)) { - //dcmcjpg with +ee can create .51 syntax images that are 8,12,16,24-bit: we can only decode 8/24-bit + // dcmcjpg with +ee can create .51 syntax images that are 8,12,16,24-bit: we can only decode 8/24-bit printError("Unable to decode %d-bit images with Transfer Syntax 1.2.840.10008.1.2.4.51, decompress with dcmdjpg or gdcmconv\n", d.bitsAllocated); d.isValid = false; } if ((isMosaic) && (d.CSA.mosaicSlices < 1) && (numberOfImagesInMosaic < 1) && (!isInterpolated) && (d.phaseEncodingLines > 0) && (frequencyRows > 0) && ((d.xyzDim[1] % frequencyRows) == 0) && ((d.xyzDim[1] / frequencyRows) > 2) && ((d.xyzDim[2] % d.phaseEncodingLines) == 0) && ((d.xyzDim[2] / d.phaseEncodingLines) > 2)) { - //n.b. in future check if frequency is in row or column direction (and same with phase) - // >2 avoids detecting interpolated as mosaic, in future perhaps check "isInterpolated" + // n.b. in future check if frequency is in row or column direction (and same with phase) + // >2 avoids detecting interpolated as mosaic, in future perhaps check "isInterpolated" numberOfImagesInMosaic = (d.xyzDim[1] / frequencyRows) * (d.xyzDim[2] / d.phaseEncodingLines); printWarning("Guessing this is a mosaic up to %d slices (issue 337).\n", numberOfImagesInMosaic); } if ((numberOfImagesInMosaic > 1) && (d.CSA.mosaicSlices < 1)) d.CSA.mosaicSlices = numberOfImagesInMosaic; if (d.isXA10A) - d.manufacturer = kMANUFACTURER_SIEMENS; //XA10A mosaics omit Manufacturer 0008,0070! + d.manufacturer = kMANUFACTURER_SIEMENS; // XA10A mosaics omit Manufacturer 0008,0070! if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (isMosaic) && (d.CSA.mosaicSlices < 1) && (d.phaseEncodingSteps > 0) && ((d.xyzDim[1] % d.phaseEncodingSteps) == 0) && ((d.xyzDim[2] % d.phaseEncodingSteps) == 0)) { d.CSA.mosaicSlices = (d.xyzDim[1] / d.phaseEncodingSteps) * (d.xyzDim[2] / d.phaseEncodingSteps); printWarning("Mosaic inferred without CSA header (check number of slices and spatial orientation)\n"); } if ((d.isXA10A) && (isMosaic) && (d.CSA.mosaicSlices < 1)) - d.CSA.mosaicSlices = -1; //mark as bogus DICOM - if ((!d.isXA10A) && (isMosaic) && (d.CSA.mosaicSlices < 1)) //See Erlangen Vida dataset - never reports "XA10" but mosaics have no attributes + d.CSA.mosaicSlices = -1; // mark as bogus DICOM + if ((!d.isXA10A) && (isMosaic) && (d.CSA.mosaicSlices < 1)) // See Erlangen Vida dataset - never reports "XA10" but mosaics have no attributes printWarning("0008,0008=MOSAIC but number of slices not specified: %s\n", fname); if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (d.CSA.dtiV[1] < -1.0) && (d.CSA.dtiV[2] < -1.0) && (d.CSA.dtiV[3] < -1.0)) - d.CSA.dtiV[0] = 0; //SiemensTrio-Syngo2004A reports B=0 images as having impossible b-vectors. + d.CSA.dtiV[0] = 0; // SiemensTrio-Syngo2004A reports B=0 images as having impossible b-vectors. if ((strlen(d.scanningSequence) < 1) && (strlen(scanningSequenceSiemens) > 1)) strcpy(d.scanningSequence, scanningSequenceSiemens); if ((strlen(d.scanOptions) < 1) && (strlen(scanOptionsSiemens) > 1)) @@ -7880,9 +7901,9 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if ((strlen(d.protocolName) < 1) && (strlen(d.seriesDescription) > 1)) strcpy(d.protocolName, d.seriesDescription); if ((strlen(d.protocolName) > 1) && (isMoCo)) - strcat(d.protocolName, "_MoCo"); //disambiguate MoCo https://github.com/neurolabusc/MRIcroGL/issues/31 + strcat(d.protocolName, "_MoCo"); // disambiguate MoCo https://github.com/neurolabusc/MRIcroGL/issues/31 if ((strlen(d.protocolName) < 1) && (strlen(d.sequenceName) > 1) && (d.manufacturer != kMANUFACTURER_SIEMENS)) - strcpy(d.protocolName, d.sequenceName); //protocolName (0018,1030) optional, sequence name (0018,0024) is not a good substitute for Siemens as it can vary per volume: *ep_b0 *ep_b1000#1, *ep_b1000#2, etc https://www.nitrc.org/forum/forum.php?thread_id=8771&forum_id=4703 + strcpy(d.protocolName, d.sequenceName); // protocolName (0018,1030) optional, sequence name (0018,0024) is not a good substitute for Siemens as it can vary per volume: *ep_b0 *ep_b1000#1, *ep_b1000#2, etc https://www.nitrc.org/forum/forum.php?thread_id=8771&forum_id=4703 if (numberOfFrames == 0) numberOfFrames = d.xyzDim[3]; if ((numberOfDynamicScans < 1) && (maxTemporalPositionIndex > minTemporalPositionIndex)) @@ -7895,7 +7916,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.xyzDim[4] = numberOfDynamicScans; } int nSegment = maxReferencedSegmentNumber - minReferencedSegmentNumber + 1; - if ((nSegment > 1) && (d.xyzDim[4] < 2) && (d.xyzDim[3] > 1) && ((d.xyzDim[3] % nSegment) == 0)) { //issue706 + if ((nSegment > 1) && (d.xyzDim[4] < 2) && (d.xyzDim[3] > 1) && ((d.xyzDim[3] % nSegment) == 0)) { // issue706 d.xyzDim[3] = d.xyzDim[3] / nSegment; d.xyzDim[4] = nSegment; } @@ -7909,7 +7930,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.patientPositionLast[k] = patientPositionEndPhilips[k]; } } - if ((numberOfFrames > 1) && (locationsInAcquisitionPhilips > 0) && ((numberOfFrames % locationsInAcquisitionPhilips) != 0)) { //issue515 + if ((numberOfFrames > 1) && (locationsInAcquisitionPhilips > 0) && ((numberOfFrames % locationsInAcquisitionPhilips) != 0)) { // issue515 printWarning("Number of frames (%d) not divisible by locations in acquisition (2001,1018) %d (issue 515)\n", numberOfFrames, locationsInAcquisitionPhilips); d.xyzDim[4] = d.xyzDim[3] / locationsInAcquisitionPhilips; d.xyzDim[3] = locationsInAcquisitionPhilips; @@ -7918,8 +7939,8 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if ((B0Philips >= 0) && (d.CSA.numDti == 0)) { d.CSA.dtiV[0] = B0Philips; d.CSA.numDti = 1; - } //issue409 Siemens XA saved as classic 2D not enhanced - if (!isnan(patientPositionStartPhilips[1])) //for Philips data without + } // issue409 Siemens XA saved as classic 2D not enhanced + if (!isnan(patientPositionStartPhilips[1])) // for Philips data without for (int k = 0; k < 4; k++) d.patientPosition[k] = patientPositionStartPhilips[k]; if (isVerbose) { @@ -7933,8 +7954,8 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD printMessage(" DWI bxyz %g %g %g %g\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]); } if ((d.isValid) && (d.xyzDim[1] > 1) && (d.xyzDim[2] > 1) && (d.imageStart < 132) && (!d.isRawDataStorage)) { - //20190524: Philips MR 55.1 creates non-image files that report kDim1/kDim2 - we can detect them since 0008,0016 reports "RawDataStorage" - //see https://neurostars.org/t/dcm2niix-error-from-philips-dicom-qsm-data-can-this-be-skipped/4883 + // 20190524: Philips MR 55.1 creates non-image files that report kDim1/kDim2 - we can detect them since 0008,0016 reports "RawDataStorage" + // see https://neurostars.org/t/dcm2niix-error-from-philips-dicom-qsm-data-can-this-be-skipped/4883 printError("Conversion aborted due to corrupt file: %s %dx%d %d\n", fname, d.xyzDim[1], d.xyzDim[2], d.imageStart); #ifdef USING_R Rf_error("Irrecoverable error during conversion"); @@ -7942,7 +7963,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD exit(kEXIT_CORRUPT_FILE_FOUND); #endif } - if ((numberOfFrames > 1) && (numDimensionIndexValues == 0) && (numberOfFrames == nSliceMM)) { //issue 372 + if ((numberOfFrames > 1) && (numDimensionIndexValues == 0) && (numberOfFrames == nSliceMM)) { // issue 372 fidx *objects = (fidx *)malloc(sizeof(struct fidx) * numberOfFrames); for (int i = 0; i < numberOfFrames; i++) { objects[i].value = sliceMM[i]; @@ -7951,34 +7972,34 @@ 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++) { d.patientPosition[i] = minPatientPosition[i]; d.patientPositionLast[i] = maxPatientPosition[i]; } - //printf("%g -> %g\n", objects[0].value, objects[numberOfFrames-1].value); - //printf("%g %g %g -> %g %g %g\n", d.patientPosition[1], d.patientPosition[2], d.patientPosition[3], d.patientPositionLast[1], d.patientPositionLast[2], d.patientPositionLast[3]); + // printf("%g -> %g\n", objects[0].value, objects[numberOfFrames-1].value); + // printf("%g %g %g -> %g %g %g\n", d.patientPosition[1], d.patientPosition[2], d.patientPosition[3], d.patientPositionLast[1], d.patientPositionLast[2], d.patientPositionLast[3]); free(objects); - } //issue 372 + } // issue 372 if ((d.echoTrainLength == 0) && (echoTrainLengthPhil)) - d.echoTrainLength = echoTrainLengthPhil; //+1 ?? to convert "EPI factor" to echo train length, see issue 377 - if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.xyzDim[4] > 1) && (d.is3DAcq) && (d.echoTrainLength > 1) && (minDynamicScanBeginTime < maxDynamicScanBeginTime)) { //issue369 - float TR = 1000.0 * ((maxDynamicScanBeginTime - minDynamicScanBeginTime) / (d.xyzDim[4] - 1)); //-1 : fence post problem + d.echoTrainLength = echoTrainLengthPhil; //+1 ?? to convert "EPI factor" to echo train length, see issue 377 + if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.xyzDim[4] > 1) && (d.is3DAcq) && (d.echoTrainLength > 1) && (minDynamicScanBeginTime < maxDynamicScanBeginTime)) { // issue369 + float TR = 1000.0 * ((maxDynamicScanBeginTime - minDynamicScanBeginTime) / (d.xyzDim[4] - 1)); //-1 : fence post problem if (fabs(TR - d.TR) > 0.001) { printWarning("Assuming TR = %gms, not 0018,0080 = %gms (see issue 369)\n", TR, d.TR); d.TR = TR; } } // printWarning("3D EPI with FrameAcquisitionDuration = %gs volumes = %d (see issue 369)\n", frameAcquisitionDuration/1000.0, d.xyzDim[4]); - //printf("%g %g\n", minDynamicScanBeginTime, maxDynamicScanBeginTime); - //if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.xyzDim[4] > 1) && (d.is3DAcq) && (d.echoTrainLength > 1) && (frameAcquisitionDuration > 0.0)) //issue369 + // printf("%g %g\n", minDynamicScanBeginTime, maxDynamicScanBeginTime); + // if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.xyzDim[4] > 1) && (d.is3DAcq) && (d.echoTrainLength > 1) && (frameAcquisitionDuration > 0.0)) //issue369 // printWarning("3D EPI with FrameAcquisitionDuration = %gs volumes = %d (see issue 369)\n", frameAcquisitionDuration/1000.0, d.xyzDim[4]); if (numDimensionIndexValues > 1) - strcpy(d.imageType, imageType1st); //for multi-frame datasets, return name of book, not name of last chapter + strcpy(d.imageType, imageType1st); // for multi-frame datasets, return name of book, not name of last chapter if (((d.isValid)) && (isIssue839) && (numDimensionIndexValues > 2)) { - //rewrite + // rewrite for (int j = 0; j < MAX_NUMBER_OF_DIMENSIONS; j++) { for (int i = 0; i < numDimensionIndexValues; i++) { dcmDim[i].dimIdx[j] = 0; @@ -7986,26 +8007,29 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } // we need a scalar value for sorting // first pass: find slice that is furthest from isocenter, n.b. arbitrary if tied - float mxDx = 0.0; + float mxDx = 0.0; int mxIdx = 0; // for (int i = 0; i < numDimensionIndexValues; i++) { - float dx = sqrt(pow(patientPosition1[i],2)+pow(patientPosition2[i],2)+pow(patientPosition3[i],2)); + float dx = sqrt(pow(patientPosition1[i], 2) + pow(patientPosition2[i], 2) + pow(patientPosition3[i], 2)); if (dx > mxDx) { mxDx = dx; mxIdx = i; } - if (isVerbose > 1) printf("slice %d is %gmm from isocenter\n", i, dx); + if (isVerbose > 1) + printf("slice %d is %gmm from isocenter\n", i, dx); } - if (isVerbose > 1) printf("slice %d is furthest (%gmm) from isocenter\n", mxIdx, mxDx); + if (isVerbose > 1) + printf("slice %d is furthest (%gmm) from isocenter\n", mxIdx, mxDx); // second pass: measure each slices scalar distance from furthest slice // ensure their are no repeated slice positions fidx *objects = (fidx *)malloc(sizeof(struct fidx) * numDimensionIndexValues); bool isSamePosition = false; for (int i = 0; i < numDimensionIndexValues; i++) { - float dx = sqrt(pow(patientPosition1[i]-patientPosition1[mxIdx],2)+pow(patientPosition2[i]-patientPosition2[mxIdx],2)+pow(patientPosition3[i]-patientPosition3[mxIdx],2)); - if (isVerbose > 1) printf("slice %d is %gmm from furthest slice\n", i, dx); + float dx = sqrt(pow(patientPosition1[i] - patientPosition1[mxIdx], 2) + pow(patientPosition2[i] - patientPosition2[mxIdx], 2) + pow(patientPosition3[i] - patientPosition3[mxIdx], 2)); + if (isVerbose > 1) + printf("slice %d is %gmm from furthest slice\n", i, dx); objects[i].value = dx; - //sliceMM[i]; + // sliceMM[i]; objects[i].index = i; if ((i != mxIdx) && (isSameFloatGE(dx, 0.0))) isSamePosition = true; @@ -8021,9 +8045,9 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } d.xyzDim[4] = 1; d.xyzDim[3] = numDimensionIndexValues; - } //issue839 + } // issue839 if ((numDimensionIndexValues > 1) && (numDimensionIndexValues == numberOfFrames)) { - //Philips enhanced datasets can have custom slice orders and pack images with different TE, Phase/Magnitude/Etc. + // Philips enhanced datasets can have custom slice orders and pack images with different TE, Phase/Magnitude/Etc. int maxVariableItem = 0; int nVariableItems = 0; if (true) { // @@ -8049,31 +8073,31 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (mn[i] != mx[i]) printMessage(" Dimension %d Range: %d..%d\n", i, mn[i], mx[i]); } - } //verbose > 1 - //see http://dicom.nema.org/medical/Dicom/2018d/output/chtml/part03/sect_C.8.24.3.3.html - //Philips puts spatial position as lower item than temporal position, the reverse is true for Bruker and Canon + } // verbose > 1 + // see http://dicom.nema.org/medical/Dicom/2018d/output/chtml/part03/sect_C.8.24.3.3.html + // Philips puts spatial position as lower item than temporal position, the reverse is true for Bruker and Canon int stackPositionItem = 0; if (dimensionIndexPointerCounter > 0) for (size_t i = 0; i < dimensionIndexPointerCounter; i++) - if ((dimensionIndexPointer[i] == kInStackPositionNumber) || (dimensionIndexPointer[i] == kImagePositionPatient)) //issue706 + if ((dimensionIndexPointer[i] == kInStackPositionNumber) || (dimensionIndexPointer[i] == kImagePositionPatient)) // issue706 stackPositionItem = i; if ((d.manufacturer == kMANUFACTURER_CANON) && (nVariableItems == 1) && (d.xyzDim[4] > 1)) { - //WARNING: Canon CANON V6.0SP2001* (0018,9005) = "AX fMRI" strangely sets TemporalPositionIndex(0020,9128) as 1 for all volumes: (0020,9157) and (0020,9128) are INCORRECT! + // WARNING: Canon CANON V6.0SP2001* (0018,9005) = "AX fMRI" strangely sets TemporalPositionIndex(0020,9128) as 1 for all volumes: (0020,9157) and (0020,9128) are INCORRECT! printMessage("Invalid enhanced DICOM created by Canon: Only single dimension in DimensionIndexValues (0020,9157) varies, for 4D file (e.g. BOTH space and time should vary)\n"); printMessage("%d %d\n", stackPositionItem, maxVariableItem); int stackTimeItem = 0; if (stackPositionItem == 0) { maxVariableItem++; - stackTimeItem++; //e.g. slot 0 = space, slot 1 = time + stackTimeItem++; // e.g. slot 0 = space, slot 1 = time } int vol = 0; for (int i = 0; i < numDimensionIndexValues; i++) { if (1 == dcmDim[i].dimIdx[stackPositionItem]) vol++; dcmDim[i].dimIdx[stackTimeItem] = vol; - //printf("vol %d slice %d\n", dcmDim[i].dimIdx[stackTimeItem], dcmDim[i].dimIdx[stackPositionItem]); + // printf("vol %d slice %d\n", dcmDim[i].dimIdx[stackTimeItem], dcmDim[i].dimIdx[stackPositionItem]); } - } //Kuldge for corrupted CANON 0020,9157 + } // Kuldge for corrupted CANON 0020,9157 /* //issue533: this code fragment will replicate dicm2nii (reverse order of volume hierarchy) https://github.com/xiangruili/dicm2nii/blob/f918366731162e6895be6e5ee431444642e0e7f9/dicm2nii.m#L2205 if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (stackPositionItem == 1) && ( maxVariableItem > 2)) { //replicate dicm2nii: s2.DimensionIndexValues([3:end 1]) @@ -8087,7 +8111,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD }*/ if ((isKludgeIssue533) && (numDimensionIndexValues > 1)) printWarning("Guessing temporal order for Philips enhanced DICOM ASL (issue 532).\n"); - //sort dimensions + // sort dimensions #ifdef USING_R if (stackPositionItem < maxVariableItem) std::sort(dcmDim.begin(), dcmDim.begin() + numberOfFrames, compareTDCMdim); @@ -8099,7 +8123,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD else qsort(dcmDim, numberOfFrames, sizeof(struct TDCMdim), compareTDCMdimRev); #endif - //for (int i = 0; i < numberOfFrames; i++) + // for (int i = 0; i < numberOfFrames; i++) // printf("i %d diskPos= %d dimIdx= %d %d %d %d TE= %g\n", i, dcmDim[i].diskPos, dcmDim[i].dimIdx[0], dcmDim[i].dimIdx[1], dcmDim[i].dimIdx[2], dcmDim[i].dimIdx[3], dti4D->TE[i]); for (int i = 0; i < numberOfFrames; i++) { dti4D->sliceOrder[i] = dcmDim[i].diskPos; @@ -8115,18 +8139,18 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD if (dti4D->intenScalePhilips[i] != dti4D->intenScalePhilips[0]) d.isScaleVariesEnh = true; } - //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 + // 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; - //setting j = 1 in next few lines is a hack, just in case TE/scale/intercept listed AFTER dimensionIndexValues + // setting j = 1 in next few lines is a hack, just in case TE/scale/intercept listed AFTER dimensionIndexValues int j = 0; for (int i = 0; i < d.xyzDim[4]; i++) { int slice = j + (i * d.xyzDim[3]); - //dti4D->gradDynVol[i] = 0; //only PAR/REC + // dti4D->gradDynVol[i] = 0; //only PAR/REC dti4D->TE[i] = dcmDim[slice].TE; dti4D->TR[i] = dcmDim[slice].TR; dti4D->isPhase[i] = dcmDim[slice].isPhase; @@ -8137,7 +8161,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD dti4D->S[i].V[1] = dcmDim[slice].V[1]; dti4D->S[i].V[2] = dcmDim[slice].V[2]; dti4D->S[i].V[3] = dcmDim[slice].V[3]; - //printf("te=\t%g\tscl=\t%g\tintercept=\t%g\n",dti4D->TE[i], dti4D->intenScale[i],dti4D->intenIntercept[i]); + // printf("te=\t%g\tscl=\t%g\tintercept=\t%g\n",dti4D->TE[i], dti4D->intenScale[i],dti4D->intenIntercept[i]); if ((!isSameFloatGE(dti4D->TE[i], 0.0)) && (dti4D->TE[i] != d.TE)) isTEvaries = true; if (dti4D->isPhase[i] != isPhase) @@ -8161,7 +8185,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.isScaleOrTEVaries = true; if (isTEvaries) d.isMultiEcho = true; - //if echoVaries,count number of echoes + // if echoVaries,count number of echoes /*int echoNum = 1; for (int i = 1; i < d.xyzDim[4]; i++) { if (dti4D->TE[i-1] != dti4D->TE[i]) @@ -8179,59 +8203,59 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD pow(d.patientPosition[2] - d.patientPositionLast[2], 2) + pow(d.patientPosition[3] - d.patientPositionLast[3], 2)); dx = dx / (maxInStackPositionNumber - 1); - if ((dx > 0.0) && (!isSameFloatGE(dx, d.xyzMM[3]))) //patientPosition has some rounding error + if ((dx > 0.0) && (!isSameFloatGE(dx, d.xyzMM[3]))) // patientPosition has some rounding error d.xyzMM[3] = dx; - } //d.zSpacing <= 0.0: Bruker does not populate 0018,0088 https://github.com/rordenlab/dcm2niix/issues/241 - } //if numDimensionIndexValues > 1 : enhanced DICOM + } // d.zSpacing <= 0.0: Bruker does not populate 0018,0088 https://github.com/rordenlab/dcm2niix/issues/241 + } // if numDimensionIndexValues > 1 : enhanced DICOM if (d.CSA.numDti >= kMaxDTI4D) { printError("Unable to convert DTI [recompile with increased kMaxDTI4D] detected=%d, max = %d\n", d.CSA.numDti, kMaxDTI4D); d.CSA.numDti = 0; } if ((hasDwiDirectionality) && (d.CSA.numDti < 1)) d.CSA.numDti = 1; - if ((d.isValid) && (! has00200013)) { //Philips non-image DICOMs (PS_*, XX_*) are not valid images and do not include instance numbers - //TYPE 1 for MR image http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0020,0013) - // Only type 2 for some other DICOMs! Therefore, generate warning not error + if ((d.isValid) && (!has00200013)) { // Philips non-image DICOMs (PS_*, XX_*) are not valid images and do not include instance numbers + // TYPE 1 for MR image http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0020,0013) + // Only type 2 for some other DICOMs! Therefore, generate warning not error printWarning("Instance number (0020,0013) not found: %s\n", fname); - d.imageNum = abs((int)d.instanceUidCrc) % 2147483647; //INT_MAX; + d.imageNum = abs((int)d.instanceUidCrc) % 2147483647; // INT_MAX; if (d.imageNum == 0) - d.imageNum = 1; //https://github.com/rordenlab/dcm2niix/issues/341 - //d.imageNum = 1; //not set + d.imageNum = 1; // https://github.com/rordenlab/dcm2niix/issues/341 + // d.imageNum = 1; //not set } if ((numDimensionIndexValues < 1) && (d.manufacturer == kMANUFACTURER_PHILIPS) && (d.seriesNum > 99999) && (philMRImageDiffBValueNumber > 0)) { - //Ugly kludge to distinguish Philips classic DICOM dti - // images from a single sequence can have identical series number, instance number, gradient number - // the ONLY way to distinguish slices is using the private tag MRImageDiffBValueNumber - // confusingly, the same sequence can also generate MULTIPLE series numbers! - // for examples see https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging + // Ugly kludge to distinguish Philips classic DICOM dti + // images from a single sequence can have identical series number, instance number, gradient number + // the ONLY way to distinguish slices is using the private tag MRImageDiffBValueNumber + // confusingly, the same sequence can also generate MULTIPLE series numbers! + // for examples see https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging d.seriesNum += (philMRImageDiffBValueNumber * 1000); } - //if (contentTime != 0.0) && (numDimensionIndexValues < (MAX_NUMBER_OF_DIMENSIONS - 1)){ + // if (contentTime != 0.0) && (numDimensionIndexValues < (MAX_NUMBER_OF_DIMENSIONS - 1)){ // uint_32t timeCRC = mz_crc32X((unsigned char*) &contentTime, sizeof(double)); - //} - if (numberOfFramesICEdims < 2) //issue751: icedims[20] frames for EPI only + // } + if (numberOfFramesICEdims < 2) // issue751: icedims[20] frames for EPI only numberOfFramesICEdims = 0; - // Issue 742: Detect *currently* enhanced Siemens XA volumes with fewer - // than the expected number of slices, and mark them as derived, with - // SeriesNumber + 1000. However, valid XA series are often unenhanced, - // likely post-scanner, and can arrive as files with just 1 slice each - // in d.xyzDim[3] but the total number of z locations for the series in - // their ICEdims, so they appear to be partial volumes until the - // dcmList is completed (nii_dicom_batch.cpp). Thus we use the - // heuristic that d.xyzDim[3] == 1 is probably OK but between 1 and - // numberOfFramesICEdims (exclusively) is an enhanced partial - // volume. It would miss the case of a true enhanced partial volume - // with just 1 slice, but that seems much less likely than unenhanced - // DICOM with unmodified ICEDims tags. + // Issue 742: Detect *currently* enhanced Siemens XA volumes with fewer + // than the expected number of slices, and mark them as derived, with + // SeriesNumber + 1000. However, valid XA series are often unenhanced, + // likely post-scanner, and can arrive as files with just 1 slice each + // in d.xyzDim[3] but the total number of z locations for the series in + // their ICEdims, so they appear to be partial volumes until the + // dcmList is completed (nii_dicom_batch.cpp). Thus we use the + // heuristic that d.xyzDim[3] == 1 is probably OK but between 1 and + // numberOfFramesICEdims (exclusively) is an enhanced partial + // volume. It would miss the case of a true enhanced partial volume + // with just 1 slice, but that seems much less likely than unenhanced + // DICOM with unmodified ICEDims tags. if ((numberOfFramesICEdims > 0) && (d.xyzDim[3] > 1) && (d.xyzDim[3] != numberOfFramesICEdims)) { printWarning("Series %ld includes partial volume (issue 742): %d slices acquired but ICE dims (0021,118e) specifies %d \n", d.seriesNum, d.xyzDim[3], numberOfFramesICEdims); d.seriesNum += 1000; d.isDerived = true; } - //if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (strcmp(d.sequenceName, "fldyn3d1")== 0)) { + // if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (strcmp(d.sequenceName, "fldyn3d1")== 0)) { if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (strstr(d.sequenceName, "fldyn3d1") != NULL)) { - //combine DCE series https://github.com/rordenlab/dcm2niix/issues/252 - if (d.isXA10A) //issue689 + // combine DCE series https://github.com/rordenlab/dcm2niix/issues/252 + if (d.isXA10A) // issue689 d.imageNum += (d.acquNum * 1000); else { d.isStackableSeries = true; @@ -8240,28 +8264,28 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.seriesUidCrc = mz_crc32X((unsigned char *)&d.protocolName, strlen(d.protocolName)); } } - if (frameNumberInSeries >= 0) //issue837 + if (frameNumberInSeries >= 0) // issue837 d.imageNum = frameNumberInSeries; - //TODO533: alias Philips ASL PLD as frameDuration? isKludgeIssue533 - //if ((d.manufacturer == kMANUFACTURER_PHILIPS) && ((!isTriggerSynced) || (!isProspectiveSynced)) ) //issue408 + // TODO533: alias Philips ASL PLD as frameDuration? isKludgeIssue533 + // if ((d.manufacturer == kMANUFACTURER_PHILIPS) && ((!isTriggerSynced) || (!isProspectiveSynced)) ) //issue408 // d.triggerDelayTime = 0.0; //Philips ASL use "(0018,9037) CS [NONE]" but "(2001,1010) CS [TRIGGERED]", a situation not described in issue408 - if (isSameFloat(MRImageDynamicScanBeginTime * 1000.0, d.triggerDelayTime)) //issue395 + if (isSameFloat(MRImageDynamicScanBeginTime * 1000.0, d.triggerDelayTime)) // issue395 d.triggerDelayTime = 0.0; - if (d.phaseNumber > 0) //Philips TurboQUASAR set this uniquely for each slice + 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); + // 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)) { - //printWarning(">>Ignoring series number of XA data saved as classic DICOM (issue 394)\n"); + // printWarning(">>Ignoring series number of XA data saved as classic DICOM (issue 394)\n"); d.isStackableSeries = true; d.imageNum += (d.seriesNum * 1000); strcpy(d.seriesInstanceUID, seriesTimeTxt); // dest <- src d.seriesUidCrc = mz_crc32X((unsigned char *)&seriesTimeTxt, strlen(seriesTimeTxt)); } - if (((d.manufacturer == kMANUFACTURER_TOSHIBA) || (d.manufacturer == kMANUFACTURER_CANON)) && (B0Philips > 0.0)) { //issue 388 + if (((d.manufacturer == kMANUFACTURER_TOSHIBA) || (d.manufacturer == kMANUFACTURER_CANON)) && (B0Philips > 0.0)) { // issue 388 char txt[1024] = {""}; snprintf(txt, 1024, "b=%d(", (int)round(B0Philips)); if (strstr(d.imageComments, txt) != NULL) { - //printf("%s>>>%s %g\n", txt, d.imageComments, B0Philips); + // printf("%s>>>%s %g\n", txt, d.imageComments, B0Philips); int len = strlen(txt); strcpy(txt, (char *)&d.imageComments[len]); len = strlen(txt); @@ -8275,7 +8299,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD float v[4]; dcmMultiFloat(len, (char *)&txt[0], 3, &v[0]); d.CSA.dtiV[0] = B0Philips; -#ifdef swizzleCanon //see issue422 and dcm_qa_canon +#ifdef swizzleCanon // see issue422 and dcm_qa_canon d.CSA.dtiV[1] = v[2]; d.CSA.dtiV[2] = v[1]; d.CSA.dtiV[3] = -v[3]; @@ -8285,14 +8309,14 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.CSA.dtiV[3] = v[3]; d.manufacturer = kMANUFACTURER_CANON; #endif - //d.CSA.dtiV[1] = v[1]; - //d.CSA.dtiV[2] = v[2]; - //d.CSA.dtiV[3] = v[3]; + // d.CSA.dtiV[1] = v[1]; + // d.CSA.dtiV[2] = v[2]; + // d.CSA.dtiV[3] = v[3]; d.CSA.numDti = 1; } } if ((isDICOMANON) && (isMATLAB)) { - //issue 383 + // issue 383 strcpy(d.seriesInstanceUID, d.studyDate); // This check is unlikely to be important in practice, but it silences a warning from GCC with -Wrestrict if (strlen(d.studyDate) < kDICOMStr) { @@ -8316,8 +8340,8 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD // cycling systems: Premier, UHP, 7.0T if ((strstr(d.manufacturersModelName, "Premier") != NULL) || (strstr(d.manufacturersModelName, "UHP") != NULL) || (strstr(d.manufacturersModelName, "7.0T") != NULL)) { // cycling special OFF mode - if (((strstr(d.manufacturersModelName, "Premier") != NULL) && (isSameFloatGE(userData15GE, 0.72))) || - (((strstr(d.manufacturersModelName, "UHP") != NULL) || (strstr(d.manufacturersModelName, "7.0T") != NULL)) && (userData15GE >= 0.5) && (userData15GE <= 1.0))) //issue 796 + if (((strstr(d.manufacturersModelName, "Premier") != NULL) && (isSameFloatGE(userData15GE, 0.72))) || + (((strstr(d.manufacturersModelName, "UHP") != NULL) || (strstr(d.manufacturersModelName, "7.0T") != NULL)) && (userData15GE >= 0.5) && (userData15GE <= 1.0))) // issue 796 d.diffCyclingModeGE = kGE_DIFF_CYCLING_SPOFF; // 2TR cycling mode else if (userData12GE == 2) { @@ -8332,7 +8356,7 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.tensorFileGE = 3; } // (Default) ALLTR cycling mode - else + else d.diffCyclingModeGE = kGE_DIFF_CYCLING_ALLTR; } // Non-cylcing systems: all other systems including MR750, Architect, etc @@ -8342,12 +8366,12 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD } if ((d.accelFactPE < accelFactPE) && (accelFactPE > 1.0)) { d.accelFactPE = accelFactPE; - //printf("Determining accelFactPE from 0018,9069 not 0021,1009 or 0051,1011\n"); + // printf("Determining accelFactPE from 0018,9069 not 0021,1009 or 0051,1011\n"); } - //avoid false positives: non-EPI GE scans can report b-value = 0 + // avoid false positives: non-EPI GE scans can report b-value = 0 if ((d.isDiffusion) && (d.manufacturer == kMANUFACTURER_GE)) d.isDiffusion = ((d.internalepiVersionGE == kGE_EPI_EPI2) || (d.epiVersionGE == kGE_EPI_EPI2)); - //detect pepolar https://github.com/nipy/heudiconv/issues/479 + // detect pepolar https://github.com/nipy/heudiconv/issues/479 if ((d.epiVersionGE == kGE_EPI_PEPOLAR_FWD) && (userData12GE == 1)) d.epiVersionGE = kGE_EPI_PEPOLAR_REV; if ((d.epiVersionGE == kGE_EPI_PEPOLAR_FWD) && (userData12GE == 2)) @@ -8358,21 +8382,22 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.epiVersionGE = kGE_EPI_PEPOLAR_REV_FWD_FLIP; if ((d.epiVersionGE == kGE_EPI_PEPOLAR_FWD_REV) && (volumeNumber > 0) && ((volumeNumber % 2) == 0)) d.epiVersionGE = kGE_EPI_PEPOLAR_FWD_REV_FLIP; - #ifndef myDisableGEPEPolarFlip //e.g. to disable patch for issue 532 "make CFLAGS=-DmyDisableGEPEPolarFlip" - if ((d.epiVersionGE == kGE_EPI_PEPOLAR_REV) || (d.epiVersionGE == kGE_EPI_PEPOLAR_FWD_REV_FLIP) || (d.epiVersionGE == kGE_EPI_PEPOLAR_REV_FWD_FLIP)) { - if (d.epiVersionGE != kGE_EPI_PEPOLAR_REV) d.seriesNum += 1000; +#ifndef myDisableGEPEPolarFlip // e.g. to disable patch for issue 532 "make CFLAGS=-DmyDisableGEPEPolarFlip" + if ((d.epiVersionGE == kGE_EPI_PEPOLAR_REV) || (d.epiVersionGE == kGE_EPI_PEPOLAR_FWD_REV_FLIP) || (d.epiVersionGE == kGE_EPI_PEPOLAR_REV_FWD_FLIP)) { + if (d.epiVersionGE != kGE_EPI_PEPOLAR_REV) + d.seriesNum += 1000; if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_UNFLIPPED) - d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED; + d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_FLIPPED; else if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_FLIPPED) - d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED; + d.phaseEncodingGE = kGE_PHASE_ENCODING_POLARITY_UNFLIPPED; } - #endif - //UIH 3D T1 scans report echo train length, which is interpreted as 3D EPI +#endif + // UIH 3D T1 scans report echo train length, which is interpreted as 3D EPI if ((d.manufacturer == kMANUFACTURER_UIH) && (strstr(d.sequenceName, "gre_fsp") != NULL)) d.echoTrainLength = 0; - //printf(">>%s\n", d.sequenceName); d.isValid = false; - // Andrey Fedorov has requested keeping GE bvaecues, see issue 264 - //if ((d.CSA.numDti > 0) && (d.manufacturer == kMANUFACTURER_GE) && (d.numberOfDiffusionDirectionGE < 1)) + // printf(">>%s\n", d.sequenceName); d.isValid = false; + // Andrey Fedorov has requested keeping GE bvaecues, see issue 264 + // if ((d.CSA.numDti > 0) && (d.manufacturer == kMANUFACTURER_GE) && (d.numberOfDiffusionDirectionGE < 1)) // d.CSA.numDti = 0; //https://github.com/rordenlab/dcm2niix/issues/264 if ((!d.isLocalizer) && (isInterpolated) && (d.imageNum <= 1)) printWarning("interpolated protocol '%s' may be unsuitable for dwidenoise/mrdegibbs. %s\n", d.protocolName, fname); @@ -8382,77 +8407,77 @@ const uint32_t kEffectiveTE = 0x0018 + uint32_t(0x9082 << 16); //FD d.dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS - 3] = d.instanceUidCrc; if ((numDimensionIndexValues + 1) < MAX_NUMBER_OF_DIMENSIONS) d.dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS - 2] = d.echoNum; - if (numDimensionIndexValues < MAX_NUMBER_OF_DIMENSIONS) //https://github.com/rordenlab/dcm2niix/issues/221 + if (numDimensionIndexValues < MAX_NUMBER_OF_DIMENSIONS) // https://github.com/rordenlab/dcm2niix/issues/221 d.dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS - 1] = mz_crc32X((unsigned char *)&d.seriesInstanceUID, strlen(d.seriesInstanceUID)); if ((d.isValid) && (d.seriesUidCrc == 0)) { if (d.seriesNum < 1) - d.seriesUidCrc = 1; //no series information + d.seriesUidCrc = 1; // no series information else - d.seriesUidCrc = d.seriesNum; //file does not have Series UID, use series number instead + d.seriesUidCrc = d.seriesNum; // file does not have Series UID, use series number instead } - if (d.seriesNum < 1) //https://github.com/rordenlab/dcm2niix/issues/218 + if (d.seriesNum < 1) // https://github.com/rordenlab/dcm2niix/issues/218 d.seriesNum = mz_crc32X((unsigned char *)&d.seriesInstanceUID, strlen(d.seriesInstanceUID)); getFileName(d.imageBaseName, fname); if (multiBandFactor > d.CSA.multiBandFactor) - d.CSA.multiBandFactor = multiBandFactor; //SMS reported in 0051,1011 but not CSA header + d.CSA.multiBandFactor = multiBandFactor; // SMS reported in 0051,1011 but not CSA header #ifndef myLoadWholeFileToReadHeader fclose(file); #endif if ((temporalResolutionMS > 0.0) && (isSameFloatGE(d.TR, temporalResolutionMS))) { - //do something profound - //in practice 0020,0110 not used - //https://github.com/bids-standard/bep001/blob/repetitiontime/Proposal_RepetitionTime.md + // do something profound + // in practice 0020,0110 not used + // https://github.com/bids-standard/bep001/blob/repetitiontime/Proposal_RepetitionTime.md } - //issue690, 777 - // detect non-DTI for GE + // issue690, 777 + // detect non-DTI for GE if ((d.manufacturer == kMANUFACTURER_GE) && (diffusionDirectionTypeGE > 0) && (diffusionDirectionTypeGE != 16) && (diffusionDirectionTypeGE != 14)) d.numberOfDiffusionDirectionGE = 0; if ((d.manufacturer == kMANUFACTURER_GE) && (seriesdiffusionDirectionTypeGE > 0) && (seriesdiffusionDirectionTypeGE != 16)) d.numberOfDiffusionDirectionGE = 0; - //issue 542 - if ((d.manufacturer == kMANUFACTURER_GE) && (isNeologica) && (!isSameFloat(d.CSA.dtiV[0], 0.0f)) && ((isSameFloat(d.CSA.dtiV[1], 0.0f)) && (isSameFloat(d.CSA.dtiV[2], 0.0f)) && (isSameFloat(d.CSA.dtiV[3], 0.0f)) ) ) + // issue 542 + if ((d.manufacturer == kMANUFACTURER_GE) && (isNeologica) && (!isSameFloat(d.CSA.dtiV[0], 0.0f)) && ((isSameFloat(d.CSA.dtiV[1], 0.0f)) && (isSameFloat(d.CSA.dtiV[2], 0.0f)) && (isSameFloat(d.CSA.dtiV[3], 0.0f)))) printWarning("GE DWI vectors may have been removed by Neologica DICOM Anonymizer Pro (Issue 542)\n"); - //start: issue529 TODO JJJJ - if ((!isSameFloat(d.CSA.dtiV[0], 0.0f)) && ((isSameFloat(d.CSA.dtiV[1], 0.0f)) && (isSameFloat(d.CSA.dtiV[2], 0.0f)) && (isSameFloat(d.CSA.dtiV[3], 0.0f)) ) ) - gradientOrientationNumberPhilips = kMaxDTI4D + 1; //Philips includes derived Trace/ADC images into raw DTI, these should be removed... - //if (sliceNumberMrPhilips == 1) printf("instance\t %d\ttime\t%g\tvolume\t%d\tgradient\t%d\tphase\t%d\tisLabel\t%d\n", d.imageNum, MRImageDynamicScanBeginTime, volumeNumber, gradientOrientationNumberPhilips, d.phaseNumber, d.aslFlags == kASL_FLAG_PHILIPS_LABEL); - //if (sliceNumberMrPhilips == 1) printf("ACQtime\t%g\tinstance\t %d\ttime\t%g\tvolume\t%d\tphase\t%d\tisLabel\t%d\n", d.acquisitionTime, d.imageNum, MRImageDynamicScanBeginTime, volumeNumber, d.phaseNumber, d.aslFlags == kASL_FLAG_PHILIPS_LABEL); - //if (sliceNumberMrPhilips == 1) printf("%d\t%d\t%g\t%d\n", philMRImageDiffBValueNumber, gradientOrientationNumberPhilips, d.CSA.dtiV[0], philMRImageDiffVolumeNumber); //issue546 - d.phaseNumber = (d.phaseNumber > philMRImageDiffBValueNumber) ? d.phaseNumber : philMRImageDiffBValueNumber; //we need both BValueNumber(2005,1412) and GradientOrientationNumber(2005,1413) to resolve volumes: issue546 - d.rawDataRunNumber = (d.rawDataRunNumber > volumeNumber) ? d.rawDataRunNumber : volumeNumber; - d.rawDataRunNumber = (d.rawDataRunNumber > gradientOrientationNumberPhilips) ? d.rawDataRunNumber : gradientOrientationNumberPhilips; + // start: issue529 TODO JJJJ + if ((!isSameFloat(d.CSA.dtiV[0], 0.0f)) && ((isSameFloat(d.CSA.dtiV[1], 0.0f)) && (isSameFloat(d.CSA.dtiV[2], 0.0f)) && (isSameFloat(d.CSA.dtiV[3], 0.0f)))) + gradientOrientationNumberPhilips = kMaxDTI4D + 1; // Philips includes derived Trace/ADC images into raw DTI, these should be removed... + // if (sliceNumberMrPhilips == 1) printf("instance\t %d\ttime\t%g\tvolume\t%d\tgradient\t%d\tphase\t%d\tisLabel\t%d\n", d.imageNum, MRImageDynamicScanBeginTime, volumeNumber, gradientOrientationNumberPhilips, d.phaseNumber, d.aslFlags == kASL_FLAG_PHILIPS_LABEL); + // if (sliceNumberMrPhilips == 1) printf("ACQtime\t%g\tinstance\t %d\ttime\t%g\tvolume\t%d\tphase\t%d\tisLabel\t%d\n", d.acquisitionTime, d.imageNum, MRImageDynamicScanBeginTime, volumeNumber, d.phaseNumber, d.aslFlags == kASL_FLAG_PHILIPS_LABEL); + // if (sliceNumberMrPhilips == 1) printf("%d\t%d\t%g\t%d\n", philMRImageDiffBValueNumber, gradientOrientationNumberPhilips, d.CSA.dtiV[0], philMRImageDiffVolumeNumber); //issue546 + d.phaseNumber = (d.phaseNumber > philMRImageDiffBValueNumber) ? d.phaseNumber : philMRImageDiffBValueNumber; // we need both BValueNumber(2005,1412) and GradientOrientationNumber(2005,1413) to resolve volumes: issue546 + d.rawDataRunNumber = (d.rawDataRunNumber > volumeNumber) ? d.rawDataRunNumber : volumeNumber; + d.rawDataRunNumber = (d.rawDataRunNumber > gradientOrientationNumberPhilips) ? d.rawDataRunNumber : gradientOrientationNumberPhilips; if ((d.rawDataRunNumber < 0) && (d.manufacturer == kMANUFACTURER_PHILIPS) && (nDimIndxVal > 1) && (d.dimensionIndexValues[nDimIndxVal - 1] > 0)) - d.rawDataRunNumber = d.dimensionIndexValues[nDimIndxVal - 1]; //Philips enhanced scans converted to classic with dcuncat - if (philMRImageDiffVolumeNumber > 0) { //use 2005,1596 for Philips DWI >= R5.6 + d.rawDataRunNumber = d.dimensionIndexValues[nDimIndxVal - 1]; // Philips enhanced scans converted to classic with dcuncat + if (philMRImageDiffVolumeNumber > 0) { // use 2005,1596 for Philips DWI >= R5.6 d.rawDataRunNumber = philMRImageDiffVolumeNumber; - d.phaseNumber = 0; + d.phaseNumber = 0; } // Phase encoding polarity - //next conditionals updated: make GE match Siemens + // 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) + // 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 - //end: issue529 + // end: issue529 if (hasDwiDirectionality) - d.isVectorFromBMatrix = false; //issue 265: Philips/Siemens have both directionality and bmatrix, Bruker only has bmatrix - //printf("%s\t%s\t%s\t%s\t%s_%s\n",d.patientBirthDate, d.procedureStepDescription,d.patientName, fname, d.studyDate, d.studyTime); - //d.isValid = false; - //printMessage(" patient position (0020,0032)\t%g\t%g\t%g\n", d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]); - //printf("%d\t%g\t%g\t%g\t%g\n", d.imageNum, d.rtia_timerGE, d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]); - //printf("%g\t\t%g\t%g\t%g\t%s\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3], fname); - //printMessage("buffer usage %d %d %d\n",d.imageStart, lPos+lFileOffset, MaxBufferSz); + d.isVectorFromBMatrix = false; // issue 265: Philips/Siemens have both directionality and bmatrix, Bruker only has bmatrix + // printf("%s\t%s\t%s\t%s\t%s_%s\n",d.patientBirthDate, d.procedureStepDescription,d.patientName, fname, d.studyDate, d.studyTime); + // d.isValid = false; + // printMessage(" patient position (0020,0032)\t%g\t%g\t%g\n", d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]); + // printf("%d\t%g\t%g\t%g\t%g\n", d.imageNum, d.rtia_timerGE, d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]); + // printf("%g\t\t%g\t%g\t%g\t%s\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3], fname); + // printMessage("buffer usage %d %d %d\n",d.imageStart, lPos+lFileOffset, MaxBufferSz); if ((d.bitsStored == 1) && (highBit != 0)) { printWarning("1-bit binary with high bit = %d not supported (issue 572)\n", highBit); d.isValid = false; } - //printf("%g\t%g\t%s\n", d.intenIntercept, d.intenScale, fname); - if ((d.isLocalizer) && (strstr(d.seriesDescription, "b1map"))) //issue751 b1map uses same base as scout - d.isLocalizer = false; + // printf("%g\t%g\t%s\n", d.intenIntercept, d.intenScale, fname); + if ((d.isLocalizer) && (strstr(d.seriesDescription, "b1map"))) // issue751 b1map uses same base as scout + d.isLocalizer = false; return d; } // readDICOMx() @@ -8472,37 +8497,34 @@ struct TDICOMdata readDICOMv(char *fname, int isVerbose, int compressFlag, struc } struct TDICOMdata readDICOM(char *fname) { - struct TDTI4D *dti4D = (struct TDTI4D *)malloc(sizeof(struct TDTI4D)); //unused + struct TDTI4D *dti4D = (struct TDTI4D *)malloc(sizeof(struct TDTI4D)); // unused TDICOMdata ret = readDICOMv(fname, false, kCompressSupport, dti4D); free(dti4D); return ret; } // readDICOM() - #ifdef USING_DCM2NIIXFSWRAPPER // remove spaces, '[', ']' in given buf // ??? also remove <, >, & -void remove_specialchars(char *buf) -{ - if (strchr(buf, ' ') == NULL) - return; - - int buflen = strlen(buf)+1; - char *newbuf = new char[buflen]; - memset(newbuf, '\0' , buflen); - - char *ptr_newbuf = &newbuf[0]; - for (int i = 0; i < buflen; i++) - { - // skip spaces, '[', ']' - if (buf[i] == ' ' || buf[i] == '[' || buf[i] == ']') - continue; - - *ptr_newbuf = buf[i]; - ptr_newbuf++; - } - - memcpy(buf, newbuf, ptr_newbuf-newbuf); - free(newbuf); +void remove_specialchars(char *buf) { + if (strchr(buf, ' ') == NULL) + return; + + int buflen = strlen(buf) + 1; + char *newbuf = new char[buflen]; + memset(newbuf, '\0', buflen); + + char *ptr_newbuf = &newbuf[0]; + for (int i = 0; i < buflen; i++) { + // skip spaces, '[', ']' + if (buf[i] == ' ' || buf[i] == '[' || buf[i] == ']') + continue; + + *ptr_newbuf = buf[i]; + ptr_newbuf++; + } + + memcpy(buf, newbuf, ptr_newbuf - newbuf); + free(newbuf); } #endif diff --git a/console/nii_dicom.h b/console/nii_dicom.h index a3725967..bb473104 100644 --- a/console/nii_dicom.h +++ b/console/nii_dicom.h @@ -1,7 +1,7 @@ +#include "nifti1_io_core.h" #include //requires VS 2015 or later -#include #include -#include "nifti1_io_core.h" +#include #ifndef USING_R #include "nifti1.h" #endif @@ -10,137 +10,136 @@ #define MRIpro_nii_dcm_h -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) - #if defined(myEnableJPEGLS) || defined(myEnableJPEGLS1) - #define kLSsuf " (JP-LS:CharLS)" - #else - #define kLSsuf "" - #endif - #ifdef myEnableJasper - #define kJP2suf " (JP2:JasPer)" - #else - #ifdef myDisableOpenJPEG - #define kJP2suf "" - #else - #define kJP2suf " (JP2:OpenJPEG)" - #endif - #endif +#if defined(myEnableJPEGLS) || defined(myEnableJPEGLS1) +#define kLSsuf " (JP-LS:CharLS)" +#else +#define kLSsuf "" +#endif +#ifdef myEnableJasper +#define kJP2suf " (JP2:JasPer)" +#else +#ifdef myDisableOpenJPEG +#define kJP2suf "" +#else +#define kJP2suf " (JP2:OpenJPEG)" +#endif +#endif #if defined(__ICC) || defined(__INTEL_COMPILER) - #define kCCsuf " IntelCC" STR(__INTEL_COMPILER) +#define kCCsuf " IntelCC" STR(__INTEL_COMPILER) #elif defined(_MSC_VER) - #define kCCsuf " MSC" STR(_MSC_VER) +#define kCCsuf " MSC" STR(_MSC_VER) #elif defined(__clang__) - #define kCCsuf " Clang" STR(__clang_major__) "." STR(__clang_minor__) "." STR(__clang_patchlevel__) +#define kCCsuf " Clang" STR(__clang_major__) "." STR(__clang_minor__) "." STR(__clang_patchlevel__) #elif defined(__GNUC__) || defined(__GNUG__) - #define kCCsuf " GCC" STR(__GNUC__) "." STR(__GNUC_MINOR__) "." STR(__GNUC_PATCHLEVEL__) +#define kCCsuf " GCC" STR(__GNUC__) "." STR(__GNUC_MINOR__) "." STR(__GNUC_PATCHLEVEL__) #else - #define kCCsuf " CompilerNA" //unknown compiler! +#define kCCsuf " CompilerNA" // unknown compiler! #endif #if defined(__arm__) || defined(__ARM_ARCH) - #define kCPUsuf " ARM" +#define kCPUsuf " ARM" #elif defined(__x86_64) - #define kCPUsuf " x86-64" +#define kCPUsuf " x86-64" #else - #define kCPUsuf " " //unknown CPU +#define kCPUsuf " " // unknown CPU #endif #define kDCMdate "v1.0.20240812" #define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf -static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic +static const int kMaxEPI3D = 1024; // maximum number of EPI images in Siemens Mosaic -#if defined(__linux__) //Unix users must use setrlimit - static const int kMaxSlice2D = 65535; //issue460 maximum number of 2D slices in 4D (Philips) images +#if defined(__linux__) // Unix users must use setrlimit +static const int kMaxSlice2D = 65535; // issue460 maximum number of 2D slices in 4D (Philips) images #else - static const int kMaxSlice2D = 131070;// 65535; //issue460 maximum number of 2D slices in 4D (Philips) images +static const int kMaxSlice2D = 131070; // 65535; //issue460 maximum number of 2D slices in 4D (Philips) images #endif -static const int kMaxDTI4D = kMaxSlice2D; //issue460: maximum number of DTI directions for 4D (Philips) images, also maximum number of 2D slices for Enhanced DICOM and PAR/REC +static const int kMaxDTI4D = kMaxSlice2D; // issue460: maximum number of DTI directions for 4D (Philips) images, also maximum number of 2D slices for Enhanced DICOM and PAR/REC -#define kDICOMStr 66 //64 characters plus NULL https://github.com/rordenlab/dcm2niix/issues/268 +#define kDICOMStr 66 // 64 characters plus NULL https://github.com/rordenlab/dcm2niix/issues/268 #define kDICOMStrLarge 256 #define kDICOMStrExtraLarge 65536 // for Siemens WipMemBlock only -#define kMANUFACTURER_UNKNOWN 0 -#define kMANUFACTURER_SIEMENS 1 -#define kMANUFACTURER_GE 2 -#define kMANUFACTURER_PHILIPS 3 -#define kMANUFACTURER_TOSHIBA 4 -#define kMANUFACTURER_UIH 5 -#define kMANUFACTURER_BRUKER 6 -#define kMANUFACTURER_HITACHI 7 -#define kMANUFACTURER_CANON 8 -#define kMANUFACTURER_MEDISO 9 -#define kMANUFACTURER_MRSOLUTIONS 10 -#define kMANUFACTURER_HYPERFINE 11 +#define kMANUFACTURER_UNKNOWN 0 +#define kMANUFACTURER_SIEMENS 1 +#define kMANUFACTURER_GE 2 +#define kMANUFACTURER_PHILIPS 3 +#define kMANUFACTURER_TOSHIBA 4 +#define kMANUFACTURER_UIH 5 +#define kMANUFACTURER_BRUKER 6 +#define kMANUFACTURER_HITACHI 7 +#define kMANUFACTURER_CANON 8 +#define kMANUFACTURER_MEDISO 9 +#define kMANUFACTURER_MRSOLUTIONS 10 +#define kMANUFACTURER_HYPERFINE 11 -//note: note a complete modality list, e.g. XA,PX, etc -#define kMODALITY_UNKNOWN 0 -#define kMODALITY_CR 1 -#define kMODALITY_CT 2 -#define kMODALITY_MR 3 -#define kMODALITY_PT 4 -#define kMODALITY_US 5 +// note: note a complete modality list, e.g. XA,PX, etc +#define kMODALITY_UNKNOWN 0 +#define kMODALITY_CR 1 +#define kMODALITY_CT 2 +#define kMODALITY_MR 3 +#define kMODALITY_PT 4 +#define kMODALITY_US 5 // PartialFourierDirection 0018,9036 -#define kPARTIAL_FOURIER_DIRECTION_UNKNOWN 0 -#define kPARTIAL_FOURIER_DIRECTION_PHASE 1 -#define kPARTIAL_FOURIER_DIRECTION_FREQUENCY 2 -#define kPARTIAL_FOURIER_DIRECTION_SLICE_SELECT 3 -#define kPARTIAL_FOURIER_DIRECTION_COMBINATION 4 +#define kPARTIAL_FOURIER_DIRECTION_UNKNOWN 0 +#define kPARTIAL_FOURIER_DIRECTION_PHASE 1 +#define kPARTIAL_FOURIER_DIRECTION_FREQUENCY 2 +#define kPARTIAL_FOURIER_DIRECTION_SLICE_SELECT 3 +#define kPARTIAL_FOURIER_DIRECTION_COMBINATION 4 -//GE EPI settings -#define kGE_EPI_UNKNOWN -1 -#define kGE_EPI_EPI 0 -#define kGE_EPI_EPIRT 1 -#define kGE_EPI_EPI2 2 -#define kGE_EPI_PEPOLAR_FWD 3 -#define kGE_EPI_PEPOLAR_REV 4 -#define kGE_EPI_PEPOLAR_REV_FWD 5 -#define kGE_EPI_PEPOLAR_FWD_REV 6 -#define kGE_EPI_PEPOLAR_REV_FWD_FLIP 7 -#define kGE_EPI_PEPOLAR_FWD_REV_FLIP 8 +// GE EPI settings +#define kGE_EPI_UNKNOWN -1 +#define kGE_EPI_EPI 0 +#define kGE_EPI_EPIRT 1 +#define kGE_EPI_EPI2 2 +#define kGE_EPI_PEPOLAR_FWD 3 +#define kGE_EPI_PEPOLAR_REV 4 +#define kGE_EPI_PEPOLAR_REV_FWD 5 +#define kGE_EPI_PEPOLAR_FWD_REV 6 +#define kGE_EPI_PEPOLAR_REV_FWD_FLIP 7 +#define kGE_EPI_PEPOLAR_FWD_REV_FLIP 8 -//GE Diff Gradient Cycling Mode +// GE Diff Gradient Cycling Mode #define kGE_DIFF_CYCLING_UNKNOWN -1 -#define kGE_DIFF_CYCLING_OFF 0 -#define kGE_DIFF_CYCLING_ALLTR 1 +#define kGE_DIFF_CYCLING_OFF 0 +#define kGE_DIFF_CYCLING_ALLTR 1 #define kGE_DIFF_CYCLING_2TR 2 #define kGE_DIFF_CYCLING_3TR 3 -#define kGE_DIFF_CYCLING_SPOFF 100 +#define kGE_DIFF_CYCLING_SPOFF 100 -//GE phase encoding -#define kGE_PHASE_ENCODING_POLARITY_UNKNOWN -1 -#define kGE_PHASE_ENCODING_POLARITY_UNFLIPPED 0 -#define kGE_PHASE_ENCODING_POLARITY_FLIPPED 4 +// GE phase encoding +#define kGE_PHASE_ENCODING_POLARITY_UNKNOWN -1 +#define kGE_PHASE_ENCODING_POLARITY_UNFLIPPED 0 +#define kGE_PHASE_ENCODING_POLARITY_FLIPPED 4 #define kGE_SLICE_ORDER_UNKNOWN -1 -#define kGE_SLICE_ORDER_TOP_DOWN 0 -#define kGE_SLICE_ORDER_BOTTOM_UP 2 - +#define kGE_SLICE_ORDER_TOP_DOWN 0 +#define kGE_SLICE_ORDER_BOTTOM_UP 2 -//#define kGE_PHASE_DIRECTION_CENTER_OUT_REV 3 -//#define kGE_PHASE_DIRECTION_CENTER_OUT 4 +// #define kGE_PHASE_DIRECTION_CENTER_OUT_REV 3 +// #define kGE_PHASE_DIRECTION_CENTER_OUT 4 -//EXIT_SUCCESS 0 -//EXIT_FAILURE 1 -#define kEXIT_NO_VALID_FILES_FOUND 2 -#define kEXIT_REPORT_VERSION 3 -#define kEXIT_CORRUPT_FILE_FOUND 4 -#define kEXIT_INPUT_FOLDER_INVALID 5 -#define kEXIT_OUTPUT_FOLDER_INVALID 6 -#define kEXIT_OUTPUT_FOLDER_READ_ONLY 7 -#define kEXIT_SOME_OK_SOME_BAD 8 -#define kEXIT_RENAME_ERROR 9 -#define kEXIT_INCOMPLETE_VOLUMES_FOUND 10 //issue 515 -#define kEXIT_NOMINAL 11 //did not expect to convert files +// EXIT_SUCCESS 0 +// EXIT_FAILURE 1 +#define kEXIT_NO_VALID_FILES_FOUND 2 +#define kEXIT_REPORT_VERSION 3 +#define kEXIT_CORRUPT_FILE_FOUND 4 +#define kEXIT_INPUT_FOLDER_INVALID 5 +#define kEXIT_OUTPUT_FOLDER_INVALID 6 +#define kEXIT_OUTPUT_FOLDER_READ_ONLY 7 +#define kEXIT_SOME_OK_SOME_BAD 8 +#define kEXIT_RENAME_ERROR 9 +#define kEXIT_INCOMPLETE_VOLUMES_FOUND 10 // issue 515 +#define kEXIT_NOMINAL 11 // did not expect to convert files -//0043,10A3 ---: PSEUDOCONTINUOUS -//0043,10A4 ---: 3D pulsed continuous ASL technique +// 0043,10A3 ---: PSEUDOCONTINUOUS +// 0043,10A4 ---: 3D pulsed continuous ASL technique #define kASL_FLAG_NONE 0 #define kASL_FLAG_GE_3DPCASL 1 #define kASL_FLAG_GE_3DCASL 2 @@ -150,8 +149,7 @@ static const int kMaxDTI4D = kMaxSlice2D; //issue460: maximum number of DTI dire #define kASL_FLAG_PHILIPS_LABEL 32 #define kASL_FLAG_GE_PULSED 64 - -//for spoiling 0018,9016 +// for spoiling 0018,9016 #define kSPOILING_UNKOWN -1 #define kSPOILING_NONE 0 #define kSPOILING_RF 1 @@ -165,20 +163,20 @@ static const int kSliceOrientCor = 3; static const int kSliceOrientMosaicNegativeDeterminant = 4; static const int kCompressNone = 0; static const int kCompressYes = 1; -static const int kCompressC3 = 2; //obsolete JPEG lossless -static const int kCompress50 = 3; //obsolete JPEG lossy -static const int kCompressRLE = 4; //run length encoding -static const int kCompressPMSCT_RLE1 = 5; //see rle2img: Philips/ELSCINT1 run-length compression 07a1,1011= PMSCT_RLE1 -static const int kCompressJPEGLS = 6; //LoCo JPEG-LS -static const int kMaxOverlay = 16; //even group values 0x6000..0x601E +static const int kCompressC3 = 2; // obsolete JPEG lossless +static const int kCompress50 = 3; // obsolete JPEG lossy +static const int kCompressRLE = 4; // run length encoding +static const int kCompressPMSCT_RLE1 = 5; // see rle2img: Philips/ELSCINT1 run-length compression 07a1,1011= PMSCT_RLE1 +static const int kCompressJPEGLS = 6; // LoCo JPEG-LS +static const int kMaxOverlay = 16; // even group values 0x6000..0x601E #ifdef myEnableJasper - static const int kCompressSupport = kCompressYes; //JASPER for JPEG2000 +static const int kCompressSupport = kCompressYes; // JASPER for JPEG2000 +#else +#ifdef myDisableOpenJPEG +static const int kCompressSupport = kCompressNone; // no decompressor #else - #ifdef myDisableOpenJPEG - static const int kCompressSupport = kCompressNone; //no decompressor - #else - static const int kCompressSupport = kCompressYes; //OPENJPEG for JPEG2000 - #endif +static const int kCompressSupport = kCompressYes; // OPENJPEG for JPEG2000 +#endif #endif // Maximum number of dimensions for .dimensionIndexValues, i.e. possibly the @@ -186,122 +184,122 @@ static const int kMaxOverlay = 16; //even group values 0x6000..0x601E static const uint8_t MAX_NUMBER_OF_DIMENSIONS = 8; #ifdef myDeidentificationMethod - // Maximum supported number of entries in DeidentificationMethodCodeSequence - // Any additional will be ignored - static const uint8_t MAX_DEID_CS = 10; +// Maximum supported number of entries in DeidentificationMethodCodeSequence +// Any additional will be ignored +static const uint8_t MAX_DEID_CS = 10; #endif // myDeidentificationMethod - struct TDTI { - float V[4]; - //int totalSlicesIn4DOrder; - }; - struct TDTI4D { - struct TDTI S[kMaxDTI4D]; - int sliceOrder[kMaxSlice2D]; // [7,3,2] means the first slice on disk should be moved to 7th position - int gradDynVol[kMaxDTI4D]; //used to parse dimensions of Philips data, e.g. file with multiple dynamics, echoes, phase+magnitude - //int fragmentOffset[kMaxDTI4D], fragmentLength[kMaxDTI4D]; //for images with multiple compressed fragments - float frameReferenceTime[kMaxDTI4D], frameDuration[kMaxDTI4D], decayFactor[kMaxDTI4D], volumeOnsetTime[kMaxDTI4D], triggerDelayTime[kMaxDTI4D], TE[kMaxDTI4D], TR[kMaxDTI4D], RWVScale[kMaxDTI4D], RWVIntercept[kMaxDTI4D], intenScale[kMaxDTI4D], intenIntercept[kMaxDTI4D], intenScalePhilips[kMaxDTI4D]; - bool isReal[kMaxDTI4D]; - bool isImaginary[kMaxDTI4D]; - bool isPhase[kMaxDTI4D]; - float repetitionTimeExcitation, repetitionTimeInversion; - }; +struct TDTI { + float V[4]; + // int totalSlicesIn4DOrder; +}; +struct TDTI4D { + struct TDTI S[kMaxDTI4D]; + int sliceOrder[kMaxSlice2D]; // [7,3,2] means the first slice on disk should be moved to 7th position + int gradDynVol[kMaxDTI4D]; // used to parse dimensions of Philips data, e.g. file with multiple dynamics, echoes, phase+magnitude + // int fragmentOffset[kMaxDTI4D], fragmentLength[kMaxDTI4D]; //for images with multiple compressed fragments + float frameReferenceTime[kMaxDTI4D], frameDuration[kMaxDTI4D], decayFactor[kMaxDTI4D], volumeOnsetTime[kMaxDTI4D], triggerDelayTime[kMaxDTI4D], TE[kMaxDTI4D], TR[kMaxDTI4D], RWVScale[kMaxDTI4D], RWVIntercept[kMaxDTI4D], intenScale[kMaxDTI4D], intenIntercept[kMaxDTI4D], intenScalePhilips[kMaxDTI4D]; + bool isReal[kMaxDTI4D]; + bool isImaginary[kMaxDTI4D]; + bool isPhase[kMaxDTI4D]; + float repetitionTimeExcitation, repetitionTimeInversion; +}; -#ifdef _MSC_VER //Microsoft nomenclature for packed structures is different... - #pragma pack(2) - typedef struct { - char name[64]; //null-terminated - int32_t vm; - char vr[4]; // possibly nul-term string - int32_t syngodt;// ?? - int32_t nitems;// number of items in CSA - int32_t xx;// maybe == 77 or 205 - } TCSAtag; //Siemens csa tag structure - typedef struct { - int32_t xx1, xx2_Len, xx3_77, xx4; - } TCSAitem; //Siemens csa item structure - #pragma pack() +#ifdef _MSC_VER // Microsoft nomenclature for packed structures is different... +#pragma pack(2) +typedef struct { + char name[64]; // null-terminated + int32_t vm; + char vr[4]; // possibly nul-term string + int32_t syngodt; // ?? + int32_t nitems; // number of items in CSA + int32_t xx; // maybe == 77 or 205 +} TCSAtag; // Siemens csa tag structure +typedef struct { + int32_t xx1, xx2_Len, xx3_77, xx4; +} TCSAitem; // Siemens csa item structure +#pragma pack() #else - typedef struct __attribute__((packed)) { - char name[64]; //null-terminated - int32_t vm; - char vr[4]; // possibly nul-term string - int32_t syngodt;// ?? - int32_t nitems;// number of items in CSA - int32_t xx;// maybe == 77 or 205 - } TCSAtag; //Siemens csa tag structure - typedef struct __attribute__((packed)) { - int32_t xx1, xx2_Len, xx3_77, xx4; - } TCSAitem; //Siemens csa item structure +typedef struct __attribute__((packed)) { + char name[64]; // null-terminated + int32_t vm; + char vr[4]; // possibly nul-term string + int32_t syngodt; // ?? + int32_t nitems; // number of items in CSA + int32_t xx; // maybe == 77 or 205 +} TCSAtag; // Siemens csa tag structure +typedef struct __attribute__((packed)) { + int32_t xx1, xx2_Len, xx3_77, xx4; +} TCSAitem; // Siemens csa item structure #endif - struct TCSAdata { - float sliceTiming[kMaxEPI3D], dtiV[4], sliceNormV[4], bandwidthPerPixelPhaseEncode, sliceMeasurementDuration, tablePos[4]; - int coilNumber, numDti, SeriesHeader_offset, SeriesHeader_length, multiBandFactor, sliceOrder, slice_start, slice_end, mosaicSlices, protocolSliceNumber1, phaseEncodingDirectionPositive; - bool isPhaseMap; - char bidsDataType[kDICOMStr]; //anat, func, dwi - char bidsEntitySuffix[kDICOMStrLarge]; //anat, func, dwi - char bidsTask[kDICOMStr]; //rest, naming40 - }; - struct TDeIDCodeSequence { - char CodeValue[kDICOMStr]; - char CodeMeaning[kDICOMStrLarge]; - char CodingSchemeDesignator[kDICOMStr]; - char CodingSchemeVersion[kDICOMStr]; - }; - struct TDICOMdata { - long seriesNum; - int xyzDim[5]; - uint32_t coilCrc, seriesUidCrc, instanceUidCrc; - int overlayStart[kMaxOverlay]; - int postLabelDelay, shimGradientX, shimGradientY, shimGradientZ, phaseNumber, spoiling, mtState, partialFourierDirection, interp3D, aslFlags, durationLabelPulseGE, epiVersionGE, internalepiVersionGE, maxEchoNumGE, rawDataRunNumber, numberOfTR, numberOfImagesInGridUIH, numberOfDiffusionT2GE, numberOfDiffusionDirectionGE, tensorFileGE, diffCyclingModeGE, phaseEncodingGE, protocolBlockStartGE, protocolBlockLengthGE, modality, dwellTime, effectiveEchoSpacingGE, phaseEncodingLines, phaseEncodingSteps, frequencyEncodingSteps, phaseEncodingStepsOutOfPlane, echoTrainLength, echoNum, sliceOrient, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,locationsInAcquisition, locationsInAcquisitionConflict, compressionScheme; - float compressedSensingFactor, xRayTubeCurrent, exposureTimeMs, numberOfExcitations, numberOfArms, numberOfPointsPerArm, groupDelay, decayFactor, scatterFraction, percentSampling,waterFatShift, numberOfAverages, patientSize, patientWeight, zSpacing, zThick, pixelBandwidth, SAR, phaseFieldofView, accelFactPE, accelFactOOP, flipAngle, fieldStrength, TE, TI, TR, intenScale, intenIntercept, intenScalePhilips, gantryTilt, lastScanLoc, angulation[4], velocityEncodeScaleGE; - float orient[7], patientPosition[4], patientPositionLast[4], xyzMM[4], stackOffcentre[4]; - float rtia_timerGE, radionuclidePositronFraction, radionuclideTotalDose, radionuclideHalfLife, doseCalibrationFactor; //PET ISOTOPE MODULE ATTRIBUTES (C.8-57) - float frameReferenceTime, frameDuration, ecat_isotope_halflife, ecat_dosage; - float pixelPaddingValue; // used for both FloatPixelPaddingValue (0028, 0122) and PixelPaddingValue (0028, 0120); NaN if not present. - double imagingFrequency, acquisitionDuration, triggerDelayTime, RWVScale, RWVIntercept, dateTime, acquisitionTime, acquisitionDate, bandwidthPerPixelPhaseEncode; - char parallelAcquisitionTechnique[kDICOMStr], radiopharmaceutical[kDICOMStr], convolutionKernel[kDICOMStr], unitsPT[kDICOMStr], tracerRadionuclide[kDICOMStr], decayCorrection[kDICOMStr], attenuationCorrectionMethod[kDICOMStr],reconstructionMethod[kDICOMStr], transferSyntax[kDICOMStr]; - char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], studyDescription[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr],studyTime[kDICOMStr]; - char deepLearningText[kDICOMStrLarge], scanOptions[kDICOMStrLarge], institutionAddress[kDICOMStrLarge], imageComments[kDICOMStrLarge]; - uint32_t dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS]; - struct TCSAdata CSA; - bool isDeepLearning, 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 TCSAdata { + float sliceTiming[kMaxEPI3D], dtiV[4], sliceNormV[4], bandwidthPerPixelPhaseEncode, sliceMeasurementDuration, tablePos[4]; + int coilNumber, numDti, SeriesHeader_offset, SeriesHeader_length, multiBandFactor, sliceOrder, slice_start, slice_end, mosaicSlices, protocolSliceNumber1, phaseEncodingDirectionPositive; + bool isPhaseMap; + char bidsDataType[kDICOMStr]; // anat, func, dwi + char bidsEntitySuffix[kDICOMStrLarge]; // anat, func, dwi + char bidsTask[kDICOMStr]; // rest, naming40 +}; +struct TDeIDCodeSequence { + char CodeValue[kDICOMStr]; + char CodeMeaning[kDICOMStrLarge]; + char CodingSchemeDesignator[kDICOMStr]; + char CodingSchemeVersion[kDICOMStr]; +}; +struct TDICOMdata { + long seriesNum; + int xyzDim[5]; + uint32_t coilCrc, seriesUidCrc, instanceUidCrc; + int overlayStart[kMaxOverlay]; + int postLabelDelay, shimGradientX, shimGradientY, shimGradientZ, phaseNumber, spoiling, mtState, partialFourierDirection, interp3D, aslFlags, durationLabelPulseGE, epiVersionGE, internalepiVersionGE, maxEchoNumGE, rawDataRunNumber, numberOfTR, numberOfImagesInGridUIH, numberOfDiffusionT2GE, numberOfDiffusionDirectionGE, tensorFileGE, diffCyclingModeGE, phaseEncodingGE, protocolBlockStartGE, protocolBlockLengthGE, modality, dwellTime, effectiveEchoSpacingGE, phaseEncodingLines, phaseEncodingSteps, frequencyEncodingSteps, phaseEncodingStepsOutOfPlane, echoTrainLength, echoNum, sliceOrient, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel, locationsInAcquisition, locationsInAcquisitionConflict, compressionScheme; + float compressedSensingFactor, xRayTubeCurrent, exposureTimeMs, numberOfExcitations, numberOfArms, numberOfPointsPerArm, groupDelay, decayFactor, scatterFraction, percentSampling, waterFatShift, numberOfAverages, patientSize, patientWeight, zSpacing, zThick, pixelBandwidth, SAR, phaseFieldofView, accelFactPE, accelFactOOP, flipAngle, fieldStrength, TE, TI, TR, intenScale, intenIntercept, intenScalePhilips, gantryTilt, lastScanLoc, angulation[4], velocityEncodeScaleGE; + float orient[7], patientPosition[4], patientPositionLast[4], xyzMM[4], stackOffcentre[4]; + float rtia_timerGE, radionuclidePositronFraction, radionuclideTotalDose, radionuclideHalfLife, doseCalibrationFactor; // PET ISOTOPE MODULE ATTRIBUTES (C.8-57) + float frameReferenceTime, frameDuration, ecat_isotope_halflife, ecat_dosage; + float pixelPaddingValue; // used for both FloatPixelPaddingValue (0028, 0122) and PixelPaddingValue (0028, 0120); NaN if not present. + double imagingFrequency, acquisitionDuration, triggerDelayTime, RWVScale, RWVIntercept, dateTime, acquisitionTime, acquisitionDate, bandwidthPerPixelPhaseEncode; + char parallelAcquisitionTechnique[kDICOMStr], radiopharmaceutical[kDICOMStr], convolutionKernel[kDICOMStr], unitsPT[kDICOMStr], tracerRadionuclide[kDICOMStr], decayCorrection[kDICOMStr], attenuationCorrectionMethod[kDICOMStr], reconstructionMethod[kDICOMStr], transferSyntax[kDICOMStr]; + char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], studyDescription[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr], sequenceVariant[kDICOMStr], scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr], studyTime[kDICOMStr]; + char deepLearningText[kDICOMStrLarge], scanOptions[kDICOMStrLarge], institutionAddress[kDICOMStrLarge], imageComments[kDICOMStrLarge]; + uint32_t dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS]; + struct TCSAdata CSA; + bool isDeepLearning, 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; #ifdef myDeidentificationMethod - int deID_CS_n = 0; - char deidentificationMethod[kDICOMStr]; - struct TDeIDCodeSequence deID_CS[MAX_DEID_CS]; + int deID_CS_n = 0; + char deidentificationMethod[kDICOMStr]; + struct TDeIDCodeSequence deID_CS[MAX_DEID_CS]; #endif - }; +}; - struct TDCMprefs { - int isVerbose, compressFlag, isIgnoreTriggerTimes; - }; +struct TDCMprefs { + int isVerbose, compressFlag, isIgnoreTriggerTimes; +}; - size_t nii_ImgBytes(struct nifti_1_header hdr); - void setDefaultPrefs (struct TDCMprefs *prefs); - int isSameFloatGE (float a, float b); - void getFileNameX( char *pathParent, const char *path, int maxLen); - struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, struct TDTI4D *dti4D); - struct TDICOMdata readDICOMx(char * fname, struct TDCMprefs* prefs, struct TDTI4D *dti4D); +size_t nii_ImgBytes(struct nifti_1_header hdr); +void setDefaultPrefs(struct TDCMprefs *prefs); +int isSameFloatGE(float a, float b); +void getFileNameX(char *pathParent, const char *path, int maxLen); +struct TDICOMdata readDICOMv(char *fname, int isVerbose, int compressFlag, struct TDTI4D *dti4D); +struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D *dti4D); - struct TDICOMdata readDICOM(char * fname); - struct TDICOMdata clear_dicom_data(void); - struct TDICOMdata nii_readParRec (char * parname, int isVerbose, struct TDTI4D *dti4D, bool isReadPhase); - unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h); - unsigned char *nii_flipImgY(unsigned char *bImg, struct nifti_1_header *hdr); - unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h); - //*unsigned char * nii_reorderSlices(unsigned char* bImg, struct nifti_1_header *h, struct TDTI4D *dti4D); - void changeExt (char *file_name, const char* ext); - unsigned char * nii_planar2rgb(unsigned char* bImg, struct nifti_1_header *hdr, int isPlanar); - int isDICOMfile(const char * fname); //0=not DICOM, 1=DICOM, 2=NOTSURE(not part 10 compliant) - void setQSForm(struct nifti_1_header *h, mat44 Q44i, bool isVerbose); - int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int isVerbose); - int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeSForm) ; - unsigned char * nii_loadImgXL(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries, int compressFlag, int isVerbose, struct TDTI4D *dti4D); +struct TDICOMdata readDICOM(char *fname); +struct TDICOMdata clear_dicom_data(void); +struct TDICOMdata nii_readParRec(char *parname, int isVerbose, struct TDTI4D *dti4D, bool isReadPhase); +unsigned char *nii_flipY(unsigned char *bImg, struct nifti_1_header *h); +unsigned char *nii_flipImgY(unsigned char *bImg, struct nifti_1_header *hdr); +unsigned char *nii_flipZ(unsigned char *bImg, struct nifti_1_header *h); +//*unsigned char * nii_reorderSlices(unsigned char* bImg, struct nifti_1_header *h, struct TDTI4D *dti4D); +void changeExt(char *file_name, const char *ext); +unsigned char *nii_planar2rgb(unsigned char *bImg, struct nifti_1_header *hdr, int isPlanar); +int isDICOMfile(const char *fname); // 0=not DICOM, 1=DICOM, 2=NOTSURE(not part 10 compliant) +void setQSForm(struct nifti_1_header *h, mat44 Q44i, bool isVerbose); +int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int isVerbose); +int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeSForm); +unsigned char *nii_loadImgXL(char *imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries, int compressFlag, int isVerbose, struct TDTI4D *dti4D); #ifdef USING_DCM2NIIXFSWRAPPER - void remove_specialchars(char *buf); +void remove_specialchars(char *buf); #endif -#ifdef __cplusplus +#ifdef __cplusplus } #endif diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 63970edf..e8c5dbd9 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -1,10 +1,10 @@ -//#define myNoSave //do not save images to disk +// #define myNoSave //do not save images to disk #ifdef _MSC_VER #include #define getcwd _getcwd #define chdir _chrdir #include "io.h" -//#include +// #include #define MiniZ #else #include @@ -26,9 +26,9 @@ #else #undef MiniZ #endif -#include "tinydir.h" -#include "print.h" #include "nifti1_io_core.h" +#include "print.h" +#include "tinydir.h" #ifndef USING_R #include "nifti1.h" #endif @@ -39,8 +39,8 @@ #include "nii_dicom.h" #include "nii_ortho.h" #ifdef myEnableJNIFTI - #include "base64.h" - #include "cJSON.h" +#include "base64.h" +#include "cJSON.h" #endif #include //toupper #include @@ -116,51 +116,45 @@ MRIFSSTRUCT mrifsStruct; std::vector mrifsStruct_vector; // retrieve the struct -MRIFSSTRUCT* nii_getMrifsStruct() -{ +MRIFSSTRUCT *nii_getMrifsStruct() { return &mrifsStruct; } // free the memory used for the image and dti -void nii_clrMrifsStruct() -{ +void nii_clrMrifsStruct() { free(mrifsStruct.imgM); free(mrifsStruct.tdti); - + for (int n = 0; n < mrifsStruct.nDcm; n++) - free(mrifsStruct.dicomlst[n]); - + free(mrifsStruct.dicomlst[n]); + if (mrifsStruct.dicomlst != NULL) - free(mrifsStruct.dicomlst); - + free(mrifsStruct.dicomlst); } // retrieve the struct -std::vector* nii_getMrifsStructVector() -{ - return &mrifsStruct_vector; +std::vector *nii_getMrifsStructVector() { + return &mrifsStruct_vector; } // free the memory used for the image and dti -void nii_clrMrifsStructVector() -{ - int nitem = mrifsStruct_vector.size(); - for (int n = 0; n < nitem; n++) - { - free(mrifsStruct_vector[n].imgM); - free(mrifsStruct_vector[n].tdti); - - for (int n = 0; n < mrifsStruct.nDcm; n++) - free(mrifsStruct_vector[n].dicomlst[n]); - - if (mrifsStruct_vector[n].dicomlst != NULL) - free(mrifsStruct_vector[n].dicomlst); - } +void nii_clrMrifsStructVector() { + int nitem = mrifsStruct_vector.size(); + for (int n = 0; n < nitem; n++) { + free(mrifsStruct_vector[n].imgM); + free(mrifsStruct_vector[n].tdti); + + for (int n = 0; n < mrifsStruct.nDcm; n++) + free(mrifsStruct_vector[n].dicomlst[n]); + + if (mrifsStruct_vector[n].dicomlst != NULL) + free(mrifsStruct_vector[n].dicomlst); + } } #endif -bool isADCnotDTI(TDTI bvec) { //returns true if bval!=0 but all bvecs == 0 (Philips code for derived ADC image) - return ((!isSameFloat(bvec.V[0], 0.0f)) && //not a B-0 image +bool isADCnotDTI(TDTI bvec) { // returns true if bval!=0 but all bvecs == 0 (Philips code for derived ADC image) + return ((!isSameFloat(bvec.V[0], 0.0f)) && // not a B-0 image ((isSameFloat(bvec.V[1], 0.0f)) && (isSameFloat(bvec.V[2], 0.0f)) && (isSameFloat(bvec.V[3], 0.0f)))); } @@ -179,15 +173,15 @@ struct TSearchList { #endif void dropFilenameFromPath(char *path) { - const char *dirPath = strrchr(path, '/'); //UNIX + const char *dirPath = strrchr(path, '/'); // UNIX if (dirPath == 0) - dirPath = strrchr(path, '\\'); //Windows + dirPath = strrchr(path, '\\'); // Windows if (dirPath == NULL) { strcpy(path, ""); } else path[dirPath - path] = 0; // please make sure there is enough space in TargetDirectory - if (strlen(path) == 0) { //file name did not specify path, assume relative path and return current working directory - //strcat (path,"."); //relative path - use cwd <- not sure if this works on Windows! + if (strlen(path) == 0) { // file name did not specify path, assume relative path and return current working directory + // strcat (path,"."); //relative path - use cwd <- not sure if this works on Windows! char cwd[PATH_MAX]; char *ok = getcwd(cwd, sizeof(cwd)); if (ok != NULL) @@ -215,31 +209,31 @@ bool is_fileexists(const char *filename) { } #ifndef S_ISDIR -#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif #ifndef S_ISREG -#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif -bool is_fileNotDir(const char *path) { //returns false if path is a folder; requires #include +bool is_fileNotDir(const char *path) { // returns false if path is a folder; requires #include struct stat buf; stat(path, &buf); return S_ISREG(buf.st_mode); -} //is_file() +} // is_file() -bool is_exe(const char *path) { //requires #include +bool is_exe(const char *path) { // requires #include struct stat buf; if (stat(path, &buf) != 0) - return false; //file does not eist + return false; // file does not eist if (!S_ISREG(buf.st_mode)) - return false; //not regular file, e.g. '..' + return false; // not regular file, e.g. '..' return (buf.st_mode & 0111); - //return (S_ISREG(buf.st_mode) && (buf.st_mode & 0111) ); -} //is_exe() + // return (S_ISREG(buf.st_mode) && (buf.st_mode & 0111) ); +} // is_exe() #if defined(_WIN64) || defined(_WIN32) -//Windows does not support lstat +// Windows does not support lstat int is_dir(const char *pathname, int follow_link) { struct stat s; if ((NULL == pathname) || (0 == strlen(pathname))) @@ -255,7 +249,7 @@ int is_dir(const char *pathname, int follow_link) { } } } // is_dir() -#else //if windows else Unix +#else // if windows else Unix int is_dir(const char *pathname, int follow_link) { struct stat s; int retval; @@ -264,7 +258,7 @@ int is_dir(const char *pathname, int follow_link) { retval = follow_link ? stat(pathname, &s) : lstat(pathname, &s); if ((-1 != retval) && (S_ISDIR(s.st_mode))) return 1; // it's a dir - return 0; // exists but is no dir + return 0; // exists but is no dir } // is_dir() #endif @@ -276,26 +270,26 @@ void opts2Prefs(struct TDCMopts *opts, struct TDCMprefs *prefs) { } void geCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx, int isVerbose) { - //0018,1312 phase encoding is either in row or column direction - //0043,1039 (or 0043,a039). b value (as the first number in the string). - //0019,10bb (or 0019,a0bb). phase diffusion direction - //0019,10bc (or 0019,a0bc). frequency diffusion direction - //0019,10bd (or 0019,a0bd). slice diffusion direction - //These directions are relative to freq,phase,slice, so although no - //transformations are required, you need to check the direction of the - //phase encoding. This is in DICOM message 0018,1312. If this has value - //COL then if swap the x and y value and reverse the sign on the z value. - //If the phase encoding is not COL, then just reverse the sign on the x value. + // 0018,1312 phase encoding is either in row or column direction + // 0043,1039 (or 0043,a039). b value (as the first number in the string). + // 0019,10bb (or 0019,a0bb). phase diffusion direction + // 0019,10bc (or 0019,a0bc). frequency diffusion direction + // 0019,10bd (or 0019,a0bd). slice diffusion direction + // These directions are relative to freq,phase,slice, so although no + // transformations are required, you need to check the direction of the + // phase encoding. This is in DICOM message 0018,1312. If this has value + // COL then if swap the x and y value and reverse the sign on the z value. + // If the phase encoding is not COL, then just reverse the sign on the x value. if ((d->manufacturer != kMANUFACTURER_GE) && (d->manufacturer != kMANUFACTURER_CANON)) return; if (d->isBVecWorldCoordinates) - return; //Canon classic DICOMs use image space, enhanced use world space! + return; // Canon classic DICOMs use image space, enhanced use world space! if ((!d->isEPI) && (d->CSA.numDti == 1)) - d->CSA.numDti = 0; //issue449 + d->CSA.numDti = 0; // issue449 if (d->CSA.numDti < 1) return; if ((toupper(d->patientOrient[0]) == 'H') && (toupper(d->patientOrient[1]) == 'F') && (toupper(d->patientOrient[2]) == 'S')) - ; //participant was head first supine + ; // participant was head first supine else { printWarning("Limited validation for non-HFS (Head first supine) GE DTI: confirm gradient vector transformation\n"); } @@ -308,17 +302,17 @@ void geCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx, int isV } if (abs(sliceDir) != 3) printWarning("Limited validation for non-Axial DTI: confirm gradient vector transformation.\n"); - //GE vectors from Xiangrui Li' dicm2nii, validated with datasets from https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging + // GE vectors from Xiangrui Li' dicm2nii, validated with datasets from https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging ivec3 flp; if (abs(sliceDir) == 1) - flp = setiVec3(1, 1, 0); //SAGITTAL + flp = setiVec3(1, 1, 0); // SAGITTAL else if (abs(sliceDir) == 2) - flp = setiVec3(0, 1, 1); //CORONAL + flp = setiVec3(0, 1, 1); // CORONAL else if (abs(sliceDir) == 3) - flp = setiVec3(0, 0, 1); //AXIAL + flp = setiVec3(0, 0, 1); // AXIAL else { printMessage("Impossible GE slice orientation!"); - flp = setiVec3(0, 0, 1); //AXIAL??? + flp = setiVec3(0, 0, 1); // AXIAL??? } if (sliceDir < 0) flp.v[2] = 1 - flp.v[2]; @@ -330,13 +324,13 @@ void geCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx, int isV bool scaledBValWarning = false; for (int i = 0; i < d->CSA.numDti; i++) { float vLen = sqrt((vx[i].V[1] * vx[i].V[1]) + (vx[i].V[2] * vx[i].V[2]) + (vx[i].V[3] * vx[i].V[3])); - if ((vx[i].V[0] <= FLT_EPSILON) || (vLen <= FLT_EPSILON)) { //bvalue=0 + if ((vx[i].V[0] <= FLT_EPSILON) || (vLen <= FLT_EPSILON)) { // bvalue=0 for (int v = 1; v < 4; v++) vx[i].V[v] = 0.0f; - continue; //do not normalize or reorient 0 vectors + continue; // do not normalize or reorient 0 vectors } if ((vLen > 0.03) && (vLen < 0.97)) { - //bVal scaled by norm(g)^2 issue163,245 + // bVal scaled by norm(g)^2 issue163,245 float bValtemp = 0, bVal = 0, bVecScale = 0; // rounding by 5 with minimum of 5 if b-value > 0 bValtemp = vx[i].V[0] * (vLen * vLen); @@ -359,26 +353,26 @@ void geCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx, int isV vx[i].V[2] = vx[i].V[2] * bVecScale; vx[i].V[3] = vx[i].V[3] * bVecScale; } - if (!col) { //rows need to be swizzled - //see Stanford dataset Ax_DWI_Tetrahedral_7 unable to resolve between possible solutions - // http://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging + if (!col) { // rows need to be swizzled + // see Stanford dataset Ax_DWI_Tetrahedral_7 unable to resolve between possible solutions + // http://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging float swap = vx[i].V[1]; vx[i].V[1] = vx[i].V[2]; vx[i].V[2] = swap; - vx[i].V[2] = -vx[i].V[2]; //because of transpose? + vx[i].V[2] = -vx[i].V[2]; // because of transpose? } for (int v = 0; v < 3; v++) if (flp.v[v] == 1) vx[i].V[v + 1] = -vx[i].V[v + 1]; - vx[i].V[2] = -vx[i].V[2]; //we read out Y-direction opposite order as dicm2nii, see also opts.isFlipY + vx[i].V[2] = -vx[i].V[2]; // we read out Y-direction opposite order as dicm2nii, see also opts.isFlipY } - //These next lines are only so files appear identical to old versions of dcm2niix: - // dicm2nii and dcm2niix generate polar opposite gradient directions. - // this does not matter, since intensity is the normal of the gradient vector. + // These next lines are only so files appear identical to old versions of dcm2niix: + // dicm2nii and dcm2niix generate polar opposite gradient directions. + // this does not matter, since intensity is the normal of the gradient vector. for (int i = 0; i < d->CSA.numDti; i++) for (int v = 1; v < 4; v++) vx[i].V[v] = -vx[i].V[v]; - //These next lines convert any "-0" values to "0" + // These next lines convert any "-0" values to "0" for (int i = 0; i < d->CSA.numDti; i++) for (int v = 1; v < 4; v++) if (isSameFloat(vx[i].V[v], -0)) @@ -386,10 +380,10 @@ void geCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx, int isV } // geCorrectBvecs() void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx, int isVerbose) { - //see Matthew Robson's http://users.fmrib.ox.ac.uk/~robson/internal/Dicom2Nifti111.m - //convert DTI vectors from scanner coordinates to image frame of reference - //Uses 6 orient values from ImageOrientationPatient (0020,0037) - // requires PatientPosition 0018,5100 is HFS (head first supine) + // see Matthew Robson's http://users.fmrib.ox.ac.uk/~robson/internal/Dicom2Nifti111.m + // convert DTI vectors from scanner coordinates to image frame of reference + // Uses 6 orient values from ImageOrientationPatient (0020,0037) + // requires PatientPosition 0018,5100 is HFS (head first supine) if ((!d->isBVecWorldCoordinates) && (d->manufacturer != kMANUFACTURER_MEDISO) && (d->manufacturer != kMANUFACTURER_BRUKER) && (d->manufacturer != kMANUFACTURER_TOSHIBA) && (d->manufacturer != kMANUFACTURER_HITACHI) && (d->manufacturer != kMANUFACTURER_UIH) && (d->manufacturer != kMANUFACTURER_SIEMENS) && (d->manufacturer != kMANUFACTURER_PHILIPS)) return; if (d->CSA.numDti < 1) @@ -399,18 +393,18 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI vx[i].V[2] = -vx[i].V[2]; for (int v = 0; v < 4; v++) if (vx[i].V[v] == -0.0f) - vx[i].V[v] = 0.0f; //remove sign from values that are virtually zero + vx[i].V[v] = 0.0f; // remove sign from values that are virtually zero } - //simple diagnostics for data prior to realignment: useful as first direction is the same for al Philips sequences - //for (int i = 0; i < 3; i++) + // simple diagnostics for data prior to realignment: useful as first direction is the same for al Philips sequences + // for (int i = 0; i < 3; i++) // printf("%g = %g %g %g\n", vx[i].V[0], vx[i].V[1], vx[i].V[2], vx[i].V[3]); return; - } //https://github.com/rordenlab/dcm2niix/issues/225 + } // https://github.com/rordenlab/dcm2niix/issues/225 if ((toupper(d->patientOrient[0]) == 'H') && (toupper(d->patientOrient[1]) == 'F') && (toupper(d->patientOrient[2]) == 'S')) - ; //participant was head first supine + ; // participant was head first supine else { printMessage("Check bvecs: expected Patient Position (0018,5100) to be 'HFS' not '%s'\n", d->patientOrient); - //return; //see https://github.com/rordenlab/dcm2niix/issues/238 + // return; //see https://github.com/rordenlab/dcm2niix/issues/238 } float minBvalThreshold = 6.0; if (d->manufacturer == kMANUFACTURER_SIEMENS) @@ -423,11 +417,11 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI slice_vector = nifti_vect33_norm(slice_vector); for (int i = 0; i < d->CSA.numDti; i++) { float vLen = sqrt((vx[i].V[1] * vx[i].V[1]) + (vx[i].V[2] * vx[i].V[2]) + (vx[i].V[3] * vx[i].V[3])); - if ((vx[i].V[0] <= FLT_EPSILON) || (vLen <= FLT_EPSILON)) { //bvalue=0 - if ((vx[i].V[0] > minBvalThreshold) && (!d->isDerived)) //Philip stores n.b. UIH B=1.25126 Vec=0,0,0 while Philips stored isotropic images + if ((vx[i].V[0] <= FLT_EPSILON) || (vLen <= FLT_EPSILON)) { // bvalue=0 + if ((vx[i].V[0] > minBvalThreshold) && (!d->isDerived)) // Philip stores n.b. UIH B=1.25126 Vec=0,0,0 while Philips stored isotropic images printWarning("Volume %d appears to be derived image ADC/Isotropic (non-zero b-value with zero vector length)\n", i); - continue; //do not normalize or reorient b0 vectors - } //if bvalue=0 + continue; // do not normalize or reorient b0 vectors + } // if bvalue=0 vec3 bvecs_old = setVec3(vx[i].V[1], vx[i].V[2], vx[i].V[3]); vec3 bvecs_new = setVec3(dotProduct(bvecs_old, read_vector), dotProduct(bvecs_old, phase_vector), dotProduct(bvecs_old, slice_vector)); bvecs_new = nifti_vect33_norm(bvecs_new); @@ -438,12 +432,12 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI vx[i].V[2] = -vx[i].V[2]; for (int v = 0; v < 4; v++) if (vx[i].V[v] == -0.0f) - vx[i].V[v] = 0.0f; //remove sign from values that are virtually zero - } //for each direction + vx[i].V[v] = 0.0f; // remove sign from values that are virtually zero + } // for each direction if (d->isVectorFromBMatrix) { printWarning("Saving %d DTI gradients. Eddy users: B-matrix does not encode b-vector polarity (issue 265).\n", d->CSA.numDti); } else if (abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant) { - printWarning("Saving %d DTI gradients. Validate vectors (matrix had a negative determinant).\n", d->CSA.numDti); //perhaps Siemens sagittal + printWarning("Saving %d DTI gradients. Validate vectors (matrix had a negative determinant).\n", d->CSA.numDti); // perhaps Siemens sagittal } else if ((d->sliceOrient == kSliceOrientTra) || (d->manufacturer != kMANUFACTURER_PHILIPS)) { if (isVerbose) printMessage("Saving %d DTI gradients. Validate vectors.\n", d->CSA.numDti); @@ -453,7 +447,7 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI printWarning("Bruker DTI support experimental (issue 265).\n"); } // siemensPhilipsCorrectBvecs() -bool isNanPosition(struct TDICOMdata d) { //in 2007 some Siemens RGB DICOMs did not include the PatientPosition 0020,0032 tag +bool isNanPosition(struct TDICOMdata d) { // in 2007 some Siemens RGB DICOMs did not include the PatientPosition 0020,0032 tag if (isnan(d.patientPosition[1])) return true; if (isnan(d.patientPosition[2])) @@ -481,7 +475,7 @@ void nii_saveText(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, char txtname[2048] = {""}; strcpy(txtname, pathoutname); strcat(txtname, ".txt"); - //printMessage("Saving text %s\n",txtname); + // printMessage("Saving text %s\n",txtname); FILE *fp = fopen(txtname, "w"); fprintf(fp, "%s\tField Strength:\t%g\tProtocolName:\t%s\tScanningSequence00180020:\t%s\tTE:\t%g\tTR:\t%g\tSeriesNum:\t%ld\tAcquNum:\t%d\tImageNum:\t%d\tImageComments:\t%s\tDateTime:\t%f\tName:\t%s\tConvVers:\t%s\tDoB:\t%s\tGender:\t%c\tAge:\t%s\tDimXYZT:\t%d\t%d\t%d\t%d\tCoil:\t%d\tEchoNum:\t%d\tOrient(6)\t%g\t%g\t%g\t%g\t%g\t%g\tbitsAllocated\t%d\tInputName\t%s\n", pathoutname, d.fieldStrength, d.protocolName, d.scanningSequence, d.TE, d.TR, d.seriesNum, d.acquNum, d.imageNum, d.imageComments, @@ -494,12 +488,12 @@ void nii_saveText(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, #define myReadAsciiCsa #ifdef myReadAsciiCsa -//read from the ASCII portion of the Siemens CSA series header -// this is not recommended: poorly documented -// it is better to stick to the binary portion of the Siemens CSA image header +// read from the ASCII portion of the Siemens CSA series header +// this is not recommended: poorly documented +// it is better to stick to the binary portion of the Siemens CSA image header #if defined(_WIN64) || defined(_WIN32) || defined(__sun) || (defined(__APPLE__) && defined(__POWERPC__)) -//https://opensource.apple.com/source/Libc/Libc-1044.1.2/string/FreeBSD/memmem.c +// https://opensource.apple.com/source/Libc/Libc-1044.1.2/string/FreeBSD/memmem.c /*- * Copyright (c) 2005 Pascal Gloor * @@ -547,10 +541,10 @@ const void *memmem(const char *l, size_t l_len, const char *s, size_t s_len) { return cur; return NULL; } -//n.b. memchr returns "const void *" not "void *" for Windows C++ https://msdn.microsoft.com/en-us/library/d7zdhf37.aspx -#endif //for systems without memmem +// n.b. memchr returns "const void *" not "void *" for Windows C++ https://msdn.microsoft.com/en-us/library/d7zdhf37.aspx +#endif // for systems without memmem -int readKeyN1(const char *key, char *buffer, int remLength) { //look for text key in binary data stream, return subsequent integer value +int readKeyN1(const char *key, char *buffer, int remLength) { // look for text key in binary data stream, return subsequent integer value char *keyPos = (char *)memmem(buffer, remLength, key, strlen(key)); if (!keyPos) return -1; @@ -562,9 +556,9 @@ int readKeyN1(const char *key, char *buffer, int remLength) { //look for text ke i++; } return ret; -} //readKeyN1() //return -1 if key not found +} // readKeyN1() //return -1 if key not found -int readKey(const char *key, char *buffer, int remLength) { //look for text key in binary data stream, return subsequent integer value +int readKey(const char *key, char *buffer, int remLength) { // look for text key in binary data stream, return subsequent integer value int ret = 0; char *keyPos = (char *)memmem(buffer, remLength, key, strlen(key)); if (!keyPos) @@ -576,9 +570,9 @@ int readKey(const char *key, char *buffer, int remLength) { //look for text key i++; } return ret; -} //readKey() //return 0 if key not found +} // readKey() //return 0 if key not found -float readKeyFloatNan(const char *key, char *buffer, int remLength) { //look for text key in binary data stream, return subsequent integer value +float readKeyFloatNan(const char *key, char *buffer, int remLength) { // look for text key in binary data stream, return subsequent integer value char *keyPos = (char *)memmem(buffer, remLength, key, strlen(key)); if (!keyPos) return NAN; @@ -597,9 +591,9 @@ float readKeyFloatNan(const char *key, char *buffer, int remLength) { //look for if (strlen(str) < 1) return NAN; return atof(str); -} //readKeyFloatNan() +} // readKeyFloatNan() -float readKeyFloat(const char *key, char *buffer, int remLength) { //look for text key in binary data stream, return subsequent integer value +float readKeyFloat(const char *key, char *buffer, int remLength) { // look for text key in binary data stream, return subsequent integer value char *keyPos = (char *)memmem(buffer, remLength, key, strlen(key)); if (!keyPos) return 0.0; @@ -618,10 +612,10 @@ float readKeyFloat(const char *key, char *buffer, int remLength) { //look for te if (strlen(str) < 1) return 0.0; return atof(str); -} //readKeyFloat() +} // readKeyFloat() void readKeyStrLen(const char *key, char *buffer, int remLength, char *outStr, int outStrLen) { - //if key is CoilElementID.tCoilID the string 'CoilElementID.tCoilID = ""Head_32""' returns 'Head32' + // if key is CoilElementID.tCoilID the string 'CoilElementID.tCoilID = ""Head_32""' returns 'Head32' strcpy(outStr, ""); char *keyPos = (char *)memmem(buffer, remLength, key, strlen(key)); if (!keyPos) @@ -644,34 +638,34 @@ void readKeyStrLen(const char *key, char *buffer, int remLength, char *outStr, i } i++; } -} //readKeyStr() +} // readKeyStr() void readKeyStr(const char *key, char *buffer, int remLength, char *outStr) { - readKeyStrLen(key, buffer, remLength, outStr, kDICOMStrLarge); -} //readKeyStr() + readKeyStrLen(key, buffer, remLength, outStr, kDICOMStrLarge); +} // readKeyStr() int phoenixOffsetCSASeriesHeader(unsigned char *buff, int lLength) { - //returns offset to ASCII Phoenix data + // returns offset to ASCII Phoenix data if (lLength < 36) return 0; if ((buff[0] != 'S') || (buff[1] != 'V') || (buff[2] != '1') || (buff[3] != '0')) return EXIT_FAILURE; - int lPos = 8; //skip 8 bytes of data, 'SV10' plus 2 32-bit values unused1 and unused2 + int lPos = 8; // skip 8 bytes of data, 'SV10' plus 2 32-bit values unused1 and unused2 int lnTag = buff[lPos] + (buff[lPos + 1] << 8) + (buff[lPos + 2] << 16) + (buff[lPos + 3] << 24); if ((buff[lPos + 4] != 77) || (lnTag < 1)) return 0; - lPos += 8; //skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0 + lPos += 8; // skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0 TCSAtag tagCSA; TCSAitem itemCSA; for (int lT = 1; lT <= lnTag; lT++) { - memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag + memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); // read tag if (!littleEndianPlatform()) nifti_swap_4bytes(1, &tagCSA.nitems); if (tagCSA.nitems > 128) { printError("%d n_tags CSA Series Header corrupted (0029,1020 ) see issue 633.\n", tagCSA.nitems); return EXIT_FAILURE; } - //printf("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems); + // printf("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems); lPos += sizeof(tagCSA); if (strcmp(tagCSA.name, "MrPhoenixProtocol") == 0) return lPos; @@ -684,13 +678,13 @@ int phoenixOffsetCSASeriesHeader(unsigned char *buff, int lLength) { } } return 0; -} //phoenixOffsetCSASeriesHeader() +} // phoenixOffsetCSASeriesHeader() #define kMaxWipFree 64 #define freeDiffusionMaxN 512 typedef struct { float TE0, TE1, delayTimeInTR, phaseOversampling, phaseResolution, txRefAmp, accelFactTotal; - int lInvContrasts, lContrasts ,phaseEncodingLines, existUcImageNumb, ucMode, baseResolution, interp, partialFourier, echoSpacing, + int lInvContrasts, lContrasts, phaseEncodingLines, existUcImageNumb, ucMode, baseResolution, interp, partialFourier, echoSpacing, difBipolar, parallelReductionFactorInPlane, refLinesPE, combineMode, patMode, ucMTC, accelFact3D, freeDiffusionN; float alFree[kMaxWipFree]; float adFree[kMaxWipFree]; @@ -700,8 +694,8 @@ typedef struct { } TCsaAscii; void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, int csaLength, float *shimSetting, char *coilID, char *consistencyInfo, char *coilElements, char *pulseSequenceDetails, char *fmriExternalInfo, char *protocolName, char *wipMemBlock) { - //reads ASCII portion of CSASeriesHeaderInfo and returns lEchoTrainDuration or lEchoSpacing value - // returns 0 if no value found + // reads ASCII portion of CSASeriesHeaderInfo and returns lEchoTrainDuration or lEchoSpacing value + // returns 0 if no value found csaAscii->sPostLabelingDelay = 0.0; csaAscii->ulLabelingDuration = 0.0; csaAscii->TE0 = 0.0; @@ -719,9 +713,9 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i csaAscii->echoSpacing = 0; csaAscii->lInvContrasts = 0; csaAscii->lContrasts = 0; - csaAscii->difBipolar = 0; //0=not assigned,1=bipolar,2=monopolar + csaAscii->difBipolar = 0; // 0=not assigned,1=bipolar,2=monopolar csaAscii->parallelReductionFactorInPlane = 0; - csaAscii->accelFact3D = 0;//lAccelFact3D + csaAscii->accelFact3D = 0; // lAccelFact3D csaAscii->accelFactTotal = 0.0; csaAscii->refLinesPE = 0; csaAscii->combineMode = 0; @@ -756,34 +750,34 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i if ((int)result != csaLength) return; fclose(pFile); - //next bit complicated: restrict to ASCII portion to avoid buffer overflow errors in BINARY portion + // next bit complicated: restrict to ASCII portion to avoid buffer overflow errors in BINARY portion int startAscii = phoenixOffsetCSASeriesHeader((unsigned char *)buffer, csaLength); - //n.b. previous function parses binary V* "SV10" portion of header - // it will return "EXIT_FAILURE for text based X* "" + // n.b. previous function parses binary V* "SV10" portion of header + // it will return "EXIT_FAILURE for text based X* "" int csaLengthTrim = csaLength; char *bufferTrim = buffer; - if ((startAscii > 0) && (startAscii < csaLengthTrim)) { //ignore binary data at start + if ((startAscii > 0) && (startAscii < csaLengthTrim)) { // ignore binary data at start bufferTrim += startAscii; csaLengthTrim -= startAscii; } - char keyStr[] = "### ASCCONV BEGIN"; //skip to start of ASCII often "### ASCCONV BEGIN ###" but also "### ASCCONV BEGIN object=MrProtDataImpl@MrProtocolData" + char keyStr[] = "### ASCCONV BEGIN"; // skip to start of ASCII often "### ASCCONV BEGIN ###" but also "### ASCCONV BEGIN object=MrProtDataImpl@MrProtocolData" char *keyPos = (char *)memmem(bufferTrim, csaLengthTrim, keyStr, strlen(keyStr)); if (keyPos) { - //We can detect multi-echo MPRAGE here, e.g. "lContrasts = 4"- but ideally we want an earlier detection + // We can detect multi-echo MPRAGE here, e.g. "lContrasts = 4"- but ideally we want an earlier detection csaLengthTrim -= (keyPos - bufferTrim); -//FmriExternalInfo listed AFTER AscConvEnd and uses different delimiter || -// char keyStrExt[] = "FmriExternalInfo"; -// readKeyStr(keyStrExt, keyPos, csaLengthTrim, fmriExternalInfo); +// FmriExternalInfo listed AFTER AscConvEnd and uses different delimiter || +// char keyStrExt[] = "FmriExternalInfo"; +// readKeyStr(keyStrExt, keyPos, csaLengthTrim, fmriExternalInfo); #define myCropAtAscConvEnd #ifdef myCropAtAscConvEnd char keyStrEnd[] = "### ASCCONV END"; char *keyPosEnd = (char *)memmem(keyPos, csaLengthTrim, keyStrEnd, strlen(keyStrEnd)); - if ((keyPosEnd) && ((keyPosEnd - keyPos) < csaLengthTrim)) //ignore binary data at end + if ((keyPosEnd) && ((keyPosEnd - keyPos) < csaLengthTrim)) // ignore binary data at end csaLengthTrim = (int)(keyPosEnd - keyPos); #endif char keyStrLns[] = "sKSpace.lPhaseEncodingLines"; csaAscii->phaseEncodingLines = readKey(keyStrLns, keyPos, csaLengthTrim); - char keyStrUcImg[] = "sSliceArray.ucImageNumb"; //some non-mosaics like ToF include "sSliceArray.ucImageNumbSag" + char keyStrUcImg[] = "sSliceArray.ucImageNumb"; // some non-mosaics like ToF include "sSliceArray.ucImageNumbSag" csaAscii->existUcImageNumb = readKey(keyStrUcImg, keyPos, csaLengthTrim); char keyStrUcMode[] = "sSliceArray.ucMode"; csaAscii->ucMode = readKeyN1(keyStrUcMode, keyPos, csaLengthTrim); @@ -804,9 +798,9 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i if (csaAscii->difBipolar == 0) { char keyStrROM[] = "ucReadOutMode"; csaAscii->difBipolar = readKey(keyStrROM, keyPos, csaLengthTrim); - if ((csaAscii->difBipolar >= 1) && (csaAscii->difBipolar <= 2)) { //E11C Siemens/CMRR dsScheme: 1=bipolar, 2=unipolar, B17 CMRR ucReadOutMode 0x1=monopolar, 0x2=bipolar + if ((csaAscii->difBipolar >= 1) && (csaAscii->difBipolar <= 2)) { // E11C Siemens/CMRR dsScheme: 1=bipolar, 2=unipolar, B17 CMRR ucReadOutMode 0x1=monopolar, 0x2=bipolar csaAscii->difBipolar = 3 - csaAscii->difBipolar; - } //https://github.com/poldracklab/fmriprep/pull/1359#issuecomment-448379329 + } // https://github.com/poldracklab/fmriprep/pull/1359#issuecomment-448379329 } char keyStrAF[] = "sPat.lAccelFactPE"; csaAscii->parallelReductionFactorInPlane = readKey(keyStrAF, keyPos, csaLengthTrim); @@ -814,25 +808,25 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i csaAscii->accelFact3D = readKey(keyStrAF3D, keyPos, csaLengthTrim); char keyStrAFTotal[] = "sPat.dTotalAccelFact"; csaAscii->accelFactTotal = readKeyFloat(keyStrAFTotal, keyPos, csaLengthTrim); - //issue 672: the tag "sSliceAcceleration.lMultiBandFactor" is not reliable: - // series 7 dcm_qa_xa30 has x3 multiband, but this tag reports "1" (perhaps cmrr sequences) - //char keyStrMB[] = "sSliceAcceleration.lMultiBandFactor"; - //csaAscii->multiBandFactor = readKey(keyStrMB, keyPos, csaLengthTrim); + // issue 672: the tag "sSliceAcceleration.lMultiBandFactor" is not reliable: + // series 7 dcm_qa_xa30 has x3 multiband, but this tag reports "1" (perhaps cmrr sequences) + // char keyStrMB[] = "sSliceAcceleration.lMultiBandFactor"; + // csaAscii->multiBandFactor = readKey(keyStrMB, keyPos, csaLengthTrim); char keyStrRef[] = "sPat.lRefLinesPE"; csaAscii->refLinesPE = readKey(keyStrRef, keyPos, csaLengthTrim); char keyStrCombineMode[] = "ucCoilCombineMode"; csaAscii->combineMode = readKeyN1(keyStrCombineMode, keyPos, csaLengthTrim); - //BIDS CoilCombinationMethod <- Siemens 'Coil Combine Mode' CSA ucCoilCombineMode 1 = Sum of Squares, 2 = Adaptive Combine, - //printf("CoilCombineMode %d\n", csaAscii->combineMode); - char keyStrPATMode[] = "sPat.ucPATMode"; //n.b. field set even if PAT not enabled, e.g. will list SENSE for a R-factor of 1 + // BIDS CoilCombinationMethod <- Siemens 'Coil Combine Mode' CSA ucCoilCombineMode 1 = Sum of Squares, 2 = Adaptive Combine, + // printf("CoilCombineMode %d\n", csaAscii->combineMode); + char keyStrPATMode[] = "sPat.ucPATMode"; // n.b. field set even if PAT not enabled, e.g. will list SENSE for a R-factor of 1 csaAscii->patMode = readKeyN1(keyStrPATMode, keyPos, csaLengthTrim); - char keyStrucMTC[] = "sPrepPulses.ucMTC"; //n.b. field set even if PAT not enabled, e.g. will list SENSE for a R-factor of 1 + char keyStrucMTC[] = "sPrepPulses.ucMTC"; // n.b. field set even if PAT not enabled, e.g. will list SENSE for a R-factor of 1 csaAscii->ucMTC = readKeyN1(keyStrucMTC, keyPos, csaLengthTrim); - //printf("PATMODE %d\n", csaAscii->patMode); - //char keyStrETD[] = "sFastImaging.lEchoTrainDuration"; + // printf("PATMODE %d\n", csaAscii->patMode); + // char keyStrETD[] = "sFastImaging.lEchoTrainDuration"; //*echoTrainDuration = readKey(keyStrETD, keyPos, csaLengthTrim); - //char keyStrEF[] = "sFastImaging.lEPIFactor"; - //ret = readKey(keyStrEF, keyPos, csaLengthTrim); + // char keyStrEF[] = "sFastImaging.lEPIFactor"; + // ret = readKey(keyStrEF, keyPos, csaLengthTrim); char keyStrCoil[] = "sCoilElementID.tCoilID"; readKeyStr(keyStrCoil, keyPos, csaLengthTrim, coilID); char keyStrCI[] = "sProtConsistencyInfo.tMeasuredBaselineString"; @@ -866,11 +860,11 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i csaAscii->sPostLabelingDelay = readKeyFloatNan(keyStrPLD, keyPos, csaLengthTrim); char keyStrLD[] = "sAsl.ulLabelingDuration"; csaAscii->ulLabelingDuration = readKeyFloatNan(keyStrLD, keyPos, csaLengthTrim); - //read ALL alTI[*] values + // read ALL alTI[*] values for (int k = 0; k < kMaxWipFree; k++) csaAscii->alTI[k] = NAN; char keyStrTiFree[] = "alTI["; - //check if ANY csaAscii.alFree tags exist + // check if ANY csaAscii.alFree tags exist char *keyPosTi = (char *)memmem(keyPos, csaLengthTrim, keyStrTiFree, strlen(keyStrTiFree)); if (keyPosTi) { for (int k = 0; k < kMaxWipFree; k++) { @@ -879,11 +873,11 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i csaAscii->alTI[k] = readKeyFloatNan(txt, keyPos, csaLengthTrim); } } - //read ALL csaAscii.alFree[*] values + // read ALL csaAscii.alFree[*] values for (int k = 0; k < kMaxWipFree; k++) csaAscii->alFree[k] = 0.0; char keyStrAlFree[] = "sWipMemBlock.alFree["; - //check if ANY csaAscii.alFree tags exist + // check if ANY csaAscii.alFree tags exist char *keyPosFree = (char *)memmem(keyPos, csaLengthTrim, keyStrAlFree, strlen(keyStrAlFree)); if (keyPosFree) { for (int k = 0; k < kMaxWipFree; k++) { @@ -892,13 +886,13 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i csaAscii->alFree[k] = readKeyFloat(txt, keyPos, csaLengthTrim); } } - //read ALL csaAscii.adFree[*] values + // read ALL csaAscii.adFree[*] values for (int k = 0; k < kMaxWipFree; k++) csaAscii->adFree[k] = NAN; char keyStrAdFree[50]; strcpy(keyStrAdFree, "sWipMemBlock.adFree["); - //char keyStrAdFree[] = "sWipMemBlock.adFree["; - //check if ANY csaAscii.adFree tags exist + // char keyStrAdFree[] = "sWipMemBlock.adFree["; + // check if ANY csaAscii.adFree tags exist keyPosFree = (char *)memmem(keyPos, csaLengthTrim, keyStrAdFree, strlen(keyStrAdFree)); if (!keyPosFree) { //"Wip" -> "WiP", modern -> old Siemens strcpy(keyStrAdFree, "sWiPMemBlock.adFree["); @@ -911,7 +905,7 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i csaAscii->adFree[k] = readKeyFloatNan(txt, keyPos, csaLengthTrim); } } - //read labelling plane + // read labelling plane char keyStrDThickness[] = "sRSatArray.asElm[1].dThickness"; csaAscii->dThickness = readKeyFloat(keyStrDThickness, keyPos, csaLengthTrim); if (csaAscii->dThickness > 0.0) { @@ -922,10 +916,10 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i char keyStrSNormalDTra[] = "sRSatArray.asElm[1].sNormal.dTra"; csaAscii->sNormalDTra = readKeyFloat(keyStrSNormalDTra, keyPos, csaLengthTrim); } - //Read NEX number of averages + // Read NEX number of averages char keyStrDAveragesDouble[] = "dAveragesDouble"; csaAscii->dAveragesDouble = readKeyFloat(keyStrDAveragesDouble, keyPos, csaLengthTrim); - //read delay time + // read delay time char keyStrDelay[] = "lDelayTimeInTR"; csaAscii->delayTimeInTR = readKeyFloat(keyStrDelay, keyPos, csaLengthTrim); char keyStrOver[] = "sKSpace.dPhaseOversamplingForDialog"; @@ -934,14 +928,14 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i csaAscii->phaseResolution = readKeyFloat(keyStrPhase, keyPos, csaLengthTrim); char keyStrAmp[] = "sTXSPEC.asNucleusInfo[0].flReferenceAmplitude"; csaAscii->txRefAmp = readKeyFloat(keyStrAmp, keyPos, csaLengthTrim); - //lower order shims: newer sequences + // lower order shims: newer sequences char keyStrSh0[] = "sGRADSPEC.asGPAData[0].lOffsetX"; shimSetting[0] = readKeyFloat(keyStrSh0, keyPos, csaLengthTrim); char keyStrSh1[] = "sGRADSPEC.asGPAData[0].lOffsetY"; shimSetting[1] = readKeyFloat(keyStrSh1, keyPos, csaLengthTrim); char keyStrSh2[] = "sGRADSPEC.asGPAData[0].lOffsetZ"; shimSetting[2] = readKeyFloat(keyStrSh2, keyPos, csaLengthTrim); - //lower order shims: older sequences + // lower order shims: older sequences char keyStrSh0s[] = "sGRADSPEC.lOffsetX"; if (shimSetting[0] == 0.0) shimSetting[0] = readKeyFloat(keyStrSh0s, keyPos, csaLengthTrim); @@ -951,7 +945,7 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i char keyStrSh2s[] = "sGRADSPEC.lOffsetZ"; if (shimSetting[2] == 0.0) shimSetting[2] = readKeyFloat(keyStrSh2s, keyPos, csaLengthTrim); - //higher order shims: older sequences + // higher order shims: older sequences char keyStrSh3[] = "sGRADSPEC.alShimCurrent[0]"; shimSetting[3] = readKeyFloat(keyStrSh3, keyPos, csaLengthTrim); char keyStrSh4[] = "sGRADSPEC.alShimCurrent[1]"; @@ -962,38 +956,38 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i shimSetting[6] = readKeyFloat(keyStrSh6, keyPos, csaLengthTrim); char keyStrSh7[] = "sGRADSPEC.alShimCurrent[4]"; shimSetting[7] = readKeyFloat(keyStrSh7, keyPos, csaLengthTrim); - // pull out the directions in the DVI - char keyStrDVIn[] = "sDiffusion.sFreeDiffusionData.lDiffDirections"; - int nDiffDir = readKey(keyStrDVIn, keyPos, csaLengthTrim); - csaAscii->freeDiffusionN = min(nDiffDir, freeDiffusionMaxN); + // pull out the directions in the DVI + char keyStrDVIn[] = "sDiffusion.sFreeDiffusionData.lDiffDirections"; + int nDiffDir = readKey(keyStrDVIn, keyPos, csaLengthTrim); + csaAscii->freeDiffusionN = min(nDiffDir, freeDiffusionMaxN); - //printMessage("Free diffusion: %i\n", csaAscii->freeDiffusionN); + // printMessage("Free diffusion: %i\n", csaAscii->freeDiffusionN); - for (int k = 0; k < csaAscii->freeDiffusionN; k++) { - char txt[128]; + for (int k = 0; k < csaAscii->freeDiffusionN; k++) { + char txt[128]; - snprintf(txt, 128, "sDiffusion.sFreeDiffusionData.asDiffDirVector[%i].dSag", k); - float x = readKeyFloat(txt, keyPos, csaLengthTrim); + snprintf(txt, 128, "sDiffusion.sFreeDiffusionData.asDiffDirVector[%i].dSag", k); + float x = readKeyFloat(txt, keyPos, csaLengthTrim); - snprintf(txt, 128, "sDiffusion.sFreeDiffusionData.asDiffDirVector[%i].dCor", k); - float y = readKeyFloat(txt, keyPos, csaLengthTrim); + snprintf(txt, 128, "sDiffusion.sFreeDiffusionData.asDiffDirVector[%i].dCor", k); + float y = readKeyFloat(txt, keyPos, csaLengthTrim); - snprintf(txt, 128, "sDiffusion.sFreeDiffusionData.asDiffDirVector[%i].dTra", k); - float z = readKeyFloat(txt, keyPos, csaLengthTrim); + snprintf(txt, 128, "sDiffusion.sFreeDiffusionData.asDiffDirVector[%i].dTra", k); + float z = readKeyFloat(txt, keyPos, csaLengthTrim); - csaAscii->freeDiffusionVec[k].v[0] = x; - csaAscii->freeDiffusionVec[k].v[1] = y; - csaAscii->freeDiffusionVec[k].v[2] = z; - } + csaAscii->freeDiffusionVec[k].v[0] = x; + csaAscii->freeDiffusionVec[k].v[1] = y; + csaAscii->freeDiffusionVec[k].v[2] = z; + } } free(buffer); return; } // siemensCsaAscii() -#endif //myReadAsciiCsa() +#endif // myReadAsciiCsa() #ifndef myDisableZLib -//Uncomment next line to decode GE Protocol Data Block, for caveats see https://github.com/rordenlab/dcm2niix/issues/163 +// Uncomment next line to decode GE Protocol Data Block, for caveats see https://github.com/rordenlab/dcm2niix/issues/163 #define myReadGeProtocolBlock #endif #ifdef myReadGeProtocolBlock @@ -1016,37 +1010,37 @@ int geProtocolBlock(const char *filename, int geOffset, int geLength, int isVerb return ret; } fseek(pFile, geOffset, SEEK_SET); - uint8_t *pCmp = (uint8_t *)malloc(geLength); //uint8_t -> mz_uint8 + uint8_t *pCmp = (uint8_t *)malloc(geLength); // uint8_t -> mz_uint8 if (pCmp == NULL) return ret; size_t result = fread(pCmp, 1, geLength, pFile); if ((int)result != geLength) return ret; int cmpSz = geLength; - //http://www.forensicswiki.org/wiki/Gzip - // always little endia! http://www.onicos.com/staff/iz/formats/gzip.html + // http://www.forensicswiki.org/wiki/Gzip + // always little endia! http://www.onicos.com/staff/iz/formats/gzip.html if (cmpSz < 20) return ret; if ((pCmp[0] != 31) || (pCmp[1] != 139) || (pCmp[2] != 8)) - return ret; //check signature and deflate algorithm + return ret; // check signature and deflate algorithm uint8_t flags = pCmp[3]; bool isFNAME = ((flags & 0x08) == 0x08); bool isFCOMMENT = ((flags & 0x10) == 0x10); int hdrSz = 10; - if (isFNAME) { //skip null-terminated string FNAME + if (isFNAME) { // skip null-terminated string FNAME for (; hdrSz < cmpSz; hdrSz++) if (pCmp[hdrSz] == 0) break; hdrSz++; } - if (isFCOMMENT) { //skip null-terminated string COMMENT + if (isFCOMMENT) { // skip null-terminated string COMMENT for (; hdrSz < cmpSz; hdrSz++) if (pCmp[hdrSz] == 0) break; hdrSz++; } uint32_t unCmpSz = ((uint32_t)pCmp[cmpSz - 4]) + ((uint32_t)pCmp[cmpSz - 3] << 8) + ((uint32_t)pCmp[cmpSz - 2] << 16) + ((uint32_t)pCmp[cmpSz - 1] << 24); - //printf(">> %d %d %zu %zu %zu\n", isFNAME, isFCOMMENT, cmpSz, unCmpSz, hdrSz); + // printf(">> %d %d %zu %zu %zu\n", isFNAME, isFCOMMENT, cmpSz, unCmpSz, hdrSz); z_stream s; memset(&s, 0, sizeof(z_stream)); #ifdef myDisableMiniZ @@ -1071,9 +1065,9 @@ int geProtocolBlock(const char *filename, int geOffset, int geLength, int isVerb return EXIT_FAILURE; } #endif - //https://groups.google.com/forum/#!msg/comp.protocols.dicom/mxnCkv8A-i4/W_uc6SxLwHQJ - // DISCOVERY MR750 / 24\MX\MR Software release:DV24.0_R01_1344.a) are now storing an XML file - // + // https://groups.google.com/forum/#!msg/comp.protocols.dicom/mxnCkv8A-i4/W_uc6SxLwHQJ + // DISCOVERY MR750 / 24\MX\MR Software release:DV24.0_R01_1344.a) are now storing an XML file + // if ((pUnCmp[0] == '<') && (pUnCmp[1] == '?')) printWarning("New XML-based GE Protocol Block is not yet supported: please report issue on dcm2niix Github page\n"); char keyStrSO[] = "\nSLICEORDER"; @@ -1117,7 +1111,7 @@ int geProtocolBlock(const char *filename, int geOffset, int geLength, int isVerb free(pUnCmp); return EXIT_SUCCESS; } -#endif //myReadGeProtocolBlock() +#endif // myReadGeProtocolBlock() void json_Str(FILE *fp, const char *sLabel, char *sVal) { // issue131,425 if (strlen(sVal) < 1) @@ -1126,21 +1120,21 @@ void json_Str(FILE *fp, const char *sLabel, char *sVal) { // issue131,425 unsigned char *iVal = (unsigned char *)sVal; int o = 0; for (int i = 0; i < (int)strlen(sVal); i++) { - //escape double quote (") and Backslash - //if ((sVal[i] == '"') || (sVal[i] == '\\') || (sVal[i] == '/')) { //issue640: escape double quotes, back slash, or slash - if ((sVal[i] == '"') || (sVal[i] == '\\')) { //escape double quotes and back slash + // escape double quote (") and Backslash + // if ((sVal[i] == '"') || (sVal[i] == '\\') || (sVal[i] == '/')) { //issue640: escape double quotes, back slash, or slash + if ((sVal[i] == '"') || (sVal[i] == '\\')) { // escape double quotes and back slash sValEsc[o] = '\\'; o++; } if ((sVal[i] >= 0x01) && (sVal[i] <= 0x07)) - continue; //control characters like "bell" - //http://dicom.nema.org/medical/dicom/current/output/html/part05.html - //0x08 Backspace is replaced with \b - //0x09 Tab is replaced with \t - //0x0A Newline is replaced with \n - //0x0B Escape ?? - //0x0C Form feed is replaced with \f - //0x0D Carriage return is replaced with \r + continue; // control characters like "bell" + // http://dicom.nema.org/medical/dicom/current/output/html/part05.html + // 0x08 Backspace is replaced with \b + // 0x09 Tab is replaced with \t + // 0x0A Newline is replaced with \n + // 0x0B Escape ?? + // 0x0C Form feed is replaced with \f + // 0x0D Carriage return is replaced with \r if ((sVal[i] >= 0x08) && (sVal[i] <= 0x0D)) { sValEsc[o] = '\\'; o++; @@ -1159,7 +1153,7 @@ void json_Str(FILE *fp, const char *sLabel, char *sVal) { // issue131,425 o++; continue; } - //https://stackoverflow.com/questions/4059775/convert-iso-8859-1-strings-to-utf-8-in-c-c + // https://stackoverflow.com/questions/4059775/convert-iso-8859-1-strings-to-utf-8-in-c-c if (iVal[i] >= 128) { sValEsc[o] = 0xc2 + (iVal[i] > 0xbf); o++; @@ -1171,22 +1165,22 @@ void json_Str(FILE *fp, const char *sLabel, char *sVal) { // issue131,425 } sValEsc[o] = '\0'; fprintf(fp, sLabel, sValEsc); -} //json_Str +} // json_Str void json_FloatNotNan(FILE *fp, const char *sLabel, float sVal) { if (isnan(sVal)) return; fprintf(fp, sLabel, sVal); -} //json_Float +} // json_Float void print_FloatNotNan(const char *sLabel, int iVal, float sVal) { if (isnan(sVal)) return; printMessage(sLabel, iVal, sVal); -} //json_Float +} // json_Float void json_Float(FILE *fp, const char *sLabel, float sVal) { - if (!isfinite(sVal)) { //isfinite() defined in C99 + if (!isfinite(sVal)) { // isfinite() defined in C99 // https://github.com/bids-standard/bids-2-devel/issues/12 printWarning(sLabel, sVal); return; @@ -1194,20 +1188,20 @@ void json_Float(FILE *fp, const char *sLabel, float sVal) { if (sVal <= 0.0) return; fprintf(fp, sLabel, sVal); -} //json_Float +} // json_Float void json_Bool(FILE *fp, const char *sLabel, int sVal) { // json_Str(fp, "\t\"MTState\"", d.mtState); - //n.b. in JSON, true and false are lower case, whereas in Python they are capitalized + // n.b. in JSON, true and false are lower case, whereas in Python they are capitalized // only print 0 and >=1 for false and true, ignore negative values if (sVal == 0) fprintf(fp, sLabel, "false"); if (sVal > 0) fprintf(fp, sLabel, "true"); -} //json_Bool +} // json_Bool void rescueProtocolName(struct TDICOMdata *d, const char *filename) { - //tools like gdcmanon strip protocol name (0018,1030) but for Siemens we can recover it from CSASeriesHeaderInfo (0029,1020) + // tools like gdcmanon strip protocol name (0018,1030) but for Siemens we can recover it from CSASeriesHeaderInfo (0029,1020) if ((d->manufacturer != kMANUFACTURER_SIEMENS) || (d->CSA.SeriesHeader_offset < 1) || (d->CSA.SeriesHeader_length < 1)) return; if (strlen(d->protocolName) > 0) @@ -1224,12 +1218,12 @@ void rescueProtocolName(struct TDICOMdata *d, const char *filename) { } void nii_SaveBIDSX(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct nifti_1_header *h, const char *filename, struct TDTI4D *dti4D) { - //https://docs.google.com/document/d/1HFUkAEE-pB-angVcYe6pf_-fVf4sCpOHKesUvfb8Grc/edit# - // Generate Brain Imaging Data Structure (BIDS) info - // sidecar JSON file (with the same filename as the .nii.gz file, but with .json extension). - // we will use %g for floats since exponents are allowed - // we will not set the locale, so decimal separator is always a period, as required - // https://www.ietf.org/rfc/rfc4627.txt + // https://docs.google.com/document/d/1HFUkAEE-pB-angVcYe6pf_-fVf4sCpOHKesUvfb8Grc/edit# + // Generate Brain Imaging Data Structure (BIDS) info + // sidecar JSON file (with the same filename as the .nii.gz file, but with .json extension). + // we will use %g for floats since exponents are allowed + // we will not set the locale, so decimal separator is always a period, as required + // https://www.ietf.org/rfc/rfc4627.txt if ((!opts.isCreateBIDS) && (opts.isOnlyBIDS)) printMessage("Input-only mode: no BIDS/NIfTI output generated for '%s'\n", pathoutname); if (!opts.isCreateBIDS) @@ -1239,8 +1233,7 @@ void nii_SaveBIDSX(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts strcat(txtname, ".json"); #ifdef USING_R FILE *fp = NULL; - if (opts.isImageInMemory) - { + if (opts.isImageInMemory) { ImageList *images = (ImageList *)opts.imageList; fp = images->jsonHandle(); } @@ -1267,7 +1260,7 @@ void nii_SaveBIDSX(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts fprintf(fp, "\t\"Modality\": \"US\",\n"); break; }; - //attempt to determine BIDS sequence type + // attempt to determine BIDS sequence type /*(0018,0024) SequenceName ep_b: dwi epfid2d: perf @@ -1310,11 +1303,11 @@ tse3d: T2*/ json_Str(fp, "\t\"ModalityLabel\": \"%s\",\n", tLabel); } }*/ - //report vendor + // report vendor if (d.fieldStrength > 0.0) fprintf(fp, "\t\"MagneticFieldStrength\": %g,\n", d.fieldStrength); - //Imaging Frequency (0018,0084) can be useful https://support.brainvoyager.com/brainvoyager/functional-analysis-preparation/29-pre-processing/78-epi-distortion-correction-echo-spacing-and-bandwidth - // however, UIH stores 128176031 not 128.176031 https://github.com/rordenlab/dcm2niix/issues/225 + // Imaging Frequency (0018,0084) can be useful https://support.brainvoyager.com/brainvoyager/functional-analysis-preparation/29-pre-processing/78-epi-distortion-correction-echo-spacing-and-bandwidth + // however, UIH stores 128176031 not 128.176031 https://github.com/rordenlab/dcm2niix/issues/225 if ((d.imagingFrequency < 9000000.0) && (d.imagingFrequency > 0.0)) fprintf(fp, "\t\"ImagingFrequency\": %.10g,\n", d.imagingFrequency); switch (d.manufacturer) { @@ -1352,16 +1345,16 @@ tse3d: T2*/ fprintf(fp, "\t\"Manufacturer\": \"Hyperfine\",\n"); break; }; - //if (d.epiVersionGE == 0) + // if (d.epiVersionGE == 0) // fprintf(fp, "\t\"PulseSequenceName\": \"epi\",\n"); - //if (d.epiVersionGE == 1) + // if (d.epiVersionGE == 1) // fprintf(fp, "\t\"PulseSequenceName\": \"epiRT\",\n"); if (d.internalepiVersionGE == 1) fprintf(fp, "\t\"InternalPulseSequenceName\": \"EPI\",\n"); if (d.internalepiVersionGE == 2) fprintf(fp, "\t\"InternalPulseSequenceName\": \"EPI2\",\n"); - //GE pepolar with phase encoding direction reversed within a series - //if (d.epiVersionGE >= kGE_EPI_PEPOLAR_FWD) + // GE pepolar with phase encoding direction reversed within a series + // if (d.epiVersionGE >= kGE_EPI_PEPOLAR_FWD) // printWarning("Validate results for custom ABCD GE pepolar sequence\n"); json_Str(fp, "\t\"ManufacturersModelName\": \"%s\",\n", d.manufacturersModelName); json_Str(fp, "\t\"InstitutionName\": \"%s\",\n", d.institutionName); @@ -1374,11 +1367,11 @@ tse3d: T2*/ json_Str(fp, "\t\"StudyInstanceUID\": \"%s\",\n", d.studyInstanceUID); json_Str(fp, "\t\"ReferringPhysicianName\": \"%s\",\n", d.referringPhysicianName); json_Str(fp, "\t\"StudyID\": \"%s\",\n", d.studyID); - //Next lines directly reveal patient identity + // Next lines directly reveal patient identity json_Str(fp, "\t\"PatientName\": \"%s\",\n", d.patientName); json_Str(fp, "\t\"PatientID\": \"%s\",\n", d.patientID); json_Str(fp, "\t\"AccessionNumber\": \"%s\",\n", d.accessionNumber); - if (strlen(d.patientBirthDate) == 8) { //DICOM DA YYYYMMDD -> ISO 8601 "YYYY-MM-DD" + if (strlen(d.patientBirthDate) == 8) { // DICOM DA YYYYMMDD -> ISO 8601 "YYYY-MM-DD" int ayear, amonth, aday; sscanf(d.patientBirthDate, "%4d%2d%2d", &ayear, &amonth, &aday); fprintf(fp, "\t\"PatientBirthDate\": "); @@ -1389,24 +1382,24 @@ tse3d: T2*/ 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; - //issue 763 following BIDS standard, unit for Age is YEARS + // d.patientBirthDate //convert from DICOM YYYYMMDD to JSON + // d.patientAge //4-digit Age String: nnnD, nnnW, nnnM, nnnY; + // issue 763 following BIDS standard, unit for Age is YEARS int ageLen = strlen(d.patientAge); - if ((ageLen > 1) && (d.patientAge[ageLen-1] == 'Y')) { + if ((ageLen > 1) && (d.patientAge[ageLen - 1] == 'Y')) { char ageStr[kDICOMStr]; strcpy(ageStr, d.patientAge); - ageStr[ageLen -1] = '\0'; + ageStr[ageLen - 1] = '\0'; fprintf(fp, "\t\"PatientAge\": %d,\n", atoi(ageStr)); } } 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_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\"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); - //json_Str(fp, "\t\"MRAcquisitionType\": \"%s\",\n", d.mrAcquisitionType); + // json_Str(fp, "\t\"MRAcquisitionType\": \"%s\",\n", d.mrAcquisitionType); if (d.is2DAcq) fprintf(fp, "\t\"MRAcquisitionType\": \"2D\",\n"); if (d.is3DAcq) @@ -1439,7 +1432,7 @@ tse3d: T2*/ fprintf(fp, "\", \"REAL"); if ((d.isHasImaginary) && ((strstr(d.imageType, "_I_") == NULL) && (strstr(d.imageType, "_IMAGINARY_") == NULL))) fprintf(fp, "\", \"IMAGINARY"); - if ((d.isRealIsPhaseMapHz) && ((strstr(d.imageType, "_FIELDMAPHZ_") == NULL))) + if ((d.isRealIsPhaseMapHz) && ((strstr(d.imageType, "_FIELDMAPHZ_") == NULL))) fprintf(fp, "\", \"FIELDMAPHZ"); fprintf(fp, "\"],\n"); } @@ -1459,101 +1452,99 @@ tse3d: T2*/ } if ((strstr(d.imageTypeText, "_DIS2D") != NULL) || (strstr(d.imageType, "_DIS2D") != NULL) || (strstr(d.imageTypeText, "_DIS3D") != NULL) || (strstr(d.imageType, "_DIS3D") != NULL)) - fprintf(fp, "\t\"NonlinearGradientCorrection\": true,\n"); + fprintf(fp, "\t\"NonlinearGradientCorrection\": true,\n"); if ((strstr(d.imageTypeText, "_ND") != NULL) || (strstr(d.imageType, "_ND") != NULL) || (strstr(d.imageTypeText, "_ND") != NULL) || (strstr(d.imageType, "_ND") != NULL)) - fprintf(fp, "\t\"NonlinearGradientCorrection\": false,\n"); - if (d.isDerived) //DICOM is derived image or non-spatial file (sounds, etc) + fprintf(fp, "\t\"NonlinearGradientCorrection\": false,\n"); + if (d.isDerived) // DICOM is derived image or non-spatial file (sounds, etc) fprintf(fp, "\t\"RawImage\": false,\n"); #ifdef myDeidentificationMethod json_Str(fp, "\t\"DeidentificationMethod\": \"%s\",\n", d.deidentificationMethod); -if(d.deID_CS_n>0) -{ - fprintf(fp, "\t\"DeidentificationMethodCodeSequence\": [ \n"); - for (int i = 0; i < d.deID_CS_n && i < MAX_DEID_CS; i++) - { - fprintf(fp, "\t { \n"); - json_Str(fp, "\t\t\"CodeValue\": \"%s\",\n", d.deID_CS[i].CodeValue); - json_Str(fp, "\t\t\"CodingSchemeDesignator\": \"%s\",\n", d.deID_CS[i].CodingSchemeDesignator); - json_Str(fp, "\t\t\"CodingSchemeVersion\": \"%s\",\n", d.deID_CS[i].CodingSchemeVersion); - json_Str(fp, "\t\t\"CodeMeaning\": \"%s\"\n", d.deID_CS[i].CodeMeaning); - if(i+1 < d.deID_CS_n) - fprintf(fp, "\t },\n"); - else - fprintf(fp, "\t }\n"); + if (d.deID_CS_n > 0) { + fprintf(fp, "\t\"DeidentificationMethodCodeSequence\": [ \n"); + for (int i = 0; i < d.deID_CS_n && i < MAX_DEID_CS; i++) { + fprintf(fp, "\t { \n"); + json_Str(fp, "\t\t\"CodeValue\": \"%s\",\n", d.deID_CS[i].CodeValue); + json_Str(fp, "\t\t\"CodingSchemeDesignator\": \"%s\",\n", d.deID_CS[i].CodingSchemeDesignator); + json_Str(fp, "\t\t\"CodingSchemeVersion\": \"%s\",\n", d.deID_CS[i].CodingSchemeVersion); + json_Str(fp, "\t\t\"CodeMeaning\": \"%s\"\n", d.deID_CS[i].CodeMeaning); + if (i + 1 < d.deID_CS_n) + fprintf(fp, "\t },\n"); + else + fprintf(fp, "\t }\n"); + } + fprintf(fp, "\t],\n"); } - fprintf(fp, "\t],\n"); -} #endif // myDeidentificationMethod if (d.seriesNum > 0) fprintf(fp, "\t\"SeriesNumber\": %ld,\n", d.seriesNum); - //Chris Gorgolewski: BIDS standard specifies ISO8601 date-time format (Example: 2016-07-06T12:49:15.679688) - //Lines below directly save DICOM values + // Chris Gorgolewski: BIDS standard specifies ISO8601 date-time format (Example: 2016-07-06T12:49:15.679688) + // Lines below directly save DICOM values if (d.acquisitionTime > 0.0 && d.acquisitionDate > 0.0) { long acquisitionDate = d.acquisitionDate; double acquisitionTime = d.acquisitionTime; char acqDateTimeBuf[64]; - //snprintf(acqDateTimeBuf, sizeof acqDateTimeBuf, "%+08ld%+08f", acquisitionDate, acquisitionTime); - snprintf(acqDateTimeBuf, sizeof acqDateTimeBuf, "%+08ld%+013.5f", acquisitionDate, acquisitionTime); //CR 20170404 add zero pad so 1:23am appears as +012300.00000 not +12300.00000 - //printMessage("acquisitionDateTime %s\n",acqDateTimeBuf); + // snprintf(acqDateTimeBuf, sizeof acqDateTimeBuf, "%+08ld%+08f", acquisitionDate, acquisitionTime); + snprintf(acqDateTimeBuf, sizeof acqDateTimeBuf, "%+08ld%+013.5f", acquisitionDate, acquisitionTime); // CR 20170404 add zero pad so 1:23am appears as +012300.00000 not +12300.00000 + // printMessage("acquisitionDateTime %s\n",acqDateTimeBuf); int ayear, amonth, aday, ahour, amin; double asec; int count = 0; - sscanf(acqDateTimeBuf, "%5d%2d%2d%3d%2d%lf%n", &ayear, &amonth, &aday, &ahour, &amin, &asec, &count); //CR 20170404 %lf not %f for double precision - //printf("-%02d-%02dT%02d:%02d:%02.6f\",\n", amonth, aday, ahour, amin, asec); + sscanf(acqDateTimeBuf, "%5d%2d%2d%3d%2d%lf%n", &ayear, &amonth, &aday, &ahour, &amin, &asec, &count); // CR 20170404 %lf not %f for double precision + // printf("-%02d-%02dT%02d:%02d:%02.6f\",\n", amonth, aday, ahour, amin, asec); if (count) { // ISO 8601 specifies a sign must exist for distant years. - //report time of the day only format, https://www.cs.tut.fi/~jkorpela/iso8601.html + // report time of the day only format, https://www.cs.tut.fi/~jkorpela/iso8601.html fprintf(fp, "\t\"AcquisitionTime\": \"%02d:%02d:%02.6f\",\n", ahour, amin, asec); - //report date and time together + // report date and time together if (!opts.isAnonymizeBIDS) { fprintf(fp, "\t\"AcquisitionDateTime\": "); fprintf(fp, (ayear >= 0 && ayear <= 9999) ? "\"%4d" : "\"%+4d", ayear); fprintf(fp, "-%02d-%02dT%02d:%02d:%02.6f\",\n", amonth, aday, ahour, amin, asec); } - } //if (count) - } //if acquisitionTime and acquisitionDate recorded + } // if (count) + } // if acquisitionTime and acquisitionDate recorded // if (d.acquisitionTime > 0.0) fprintf(fp, "\t\"AcquisitionTime\": %f,\n", d.acquisitionTime ); // if (d.acquisitionDate > 0.0) fprintf(fp, "\t\"AcquisitionDate\": %8.0f,\n", d.acquisitionDate ); if (d.acquNum > 0) fprintf(fp, "\t\"AcquisitionNumber\": %d,\n", d.acquNum); - bool maskComments = (strlen(opts.imageComments) == 1) && (opts.imageComments[0] == '\t'); + bool maskComments = (strlen(opts.imageComments) == 1) && (opts.imageComments[0] == '\t'); if (!maskComments) { json_Str(fp, "\t\"ImageComments\": \"%s\",\n", d.imageComments); json_Str(fp, "\t\"ConversionComments\": \"%s\",\n", opts.imageComments); } - //if conditionals: the following values are required for DICOM MRI, but not available for CT + // if conditionals: the following values are required for DICOM MRI, but not available for CT json_Float(fp, "\t\"TriggerDelayTime\": %g,\n", d.triggerDelayTime); if (d.RWVScale != 0) { fprintf(fp, "\t\"PhilipsRWVSlope\": %g,\n", d.RWVScale); fprintf(fp, "\t\"PhilipsRWVIntercept\": %g,\n", d.RWVIntercept); } - if ((!d.isScaleVariesEnh) && (d.intenScalePhilips != 0) && (d.manufacturer == kMANUFACTURER_PHILIPS)) { //for details, see PhilipsPrecise() + if ((!d.isScaleVariesEnh) && (d.intenScalePhilips != 0) && (d.manufacturer == kMANUFACTURER_PHILIPS)) { // for details, see PhilipsPrecise() fprintf(fp, "\t\"PhilipsRescaleSlope\": %g,\n", d.intenScale); fprintf(fp, "\t\"PhilipsRescaleIntercept\": %g,\n", d.intenIntercept); fprintf(fp, "\t\"PhilipsScaleSlope\": %g,\n", d.intenScalePhilips); fprintf(fp, "\t\"UsePhilipsFloatNotDisplayScaling\": %d,\n", opts.isPhilipsFloatNotDisplayScaling); } - //https://bids-specification--622.org.readthedocs.build/en/622/04-modality-specific-files/01-magnetic-resonance-imaging-data.html#case-3-direct-field-mapping + // https://bids-specification--622.org.readthedocs.build/en/622/04-modality-specific-files/01-magnetic-resonance-imaging-data.html#case-3-direct-field-mapping if ((d.isRealIsPhaseMapHz) && ((d.manufacturer == kMANUFACTURER_GE) || (d.isHasReal))) fprintf(fp, "\t\"Units\": \"Hz\",\n"); // - //PET ISOTOPE MODULE ATTRIBUTES + // 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\"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); 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\"Units\": \"%s\",\n", d.unitsPT); // https://github.com/bids-standard/bids-specification/pull/773 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 + // 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")) @@ -1570,7 +1561,7 @@ if(d.deID_CS_n>0) strcpy(reconMethodName, "3DRP"); else if (strstr(d.reconstructionMethod, "3D Kinahan-Rogers")) strcpy(reconMethodName, "3D Kinahan-Rogers"); - //end of autogenerated text + // end of autogenerated text if (strlen(reconMethodName) < 1) { if (strstr(d.reconstructionMethod, "OSEM")) strcat(reconMethodName, "Ordered Subset Expectation Maximization "); @@ -1604,15 +1595,15 @@ if(d.deID_CS_n>0) 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'; + // 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'; + // 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) @@ -1638,11 +1629,11 @@ if(d.deID_CS_n>0) 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 + // 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 + 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++) { if (i != 0) @@ -1653,7 +1644,7 @@ if(d.deID_CS_n>0) } fprintf(fp, "\t],\n"); } - if ((h->dim[4] > 1) && (dti4D->volumeOnsetTime[0] >= 0.0)) { //see BEP009 PET https://docs.google.com/document/d/1mqMLnxVdLwZjDd4ZiWFqjEAmOmfcModA_R535v3eQs0 + if ((h->dim[4] > 1) && (dti4D->volumeOnsetTime[0] >= 0.0)) { // see BEP009 PET https://docs.google.com/document/d/1mqMLnxVdLwZjDd4ZiWFqjEAmOmfcModA_R535v3eQs0 fprintf(fp, "\t\"FrameTimesStart\": [\n"); for (int i = 0; i < h->dim[4]; i++) { if (i != 0) @@ -1664,7 +1655,7 @@ if(d.deID_CS_n>0) } fprintf(fp, "\t],\n"); } - if ((h->dim[4] > 1) && (dti4D->frameDuration[0] >= 0.0)) { //see BEP009 PET https://docs.google.com/document/d/1mqMLnxVdLwZjDd4ZiWFqjEAmOmfcModA_R535v3eQs0 + if ((h->dim[4] > 1) && (dti4D->frameDuration[0] >= 0.0)) { // see BEP009 PET https://docs.google.com/document/d/1mqMLnxVdLwZjDd4ZiWFqjEAmOmfcModA_R535v3eQs0 fprintf(fp, "\t\"FrameDuration\": [\n"); for (int i = 0; i < h->dim[4]; i++) { if (i != 0) @@ -1678,7 +1669,7 @@ if(d.deID_CS_n>0) } fprintf(fp, "\t],\n"); } - if ((dti4D->frameReferenceTime[0] >= 0.0) && (h->dim[4] > 1)) { //see BEP009 PET https://docs.google.com/document/d/1mqMLnxVdLwZjDd4ZiWFqjEAmOmfcModA_R535v3eQs0 + if ((dti4D->frameReferenceTime[0] >= 0.0) && (h->dim[4] > 1)) { // see BEP009 PET https://docs.google.com/document/d/1mqMLnxVdLwZjDd4ZiWFqjEAmOmfcModA_R535v3eQs0 bool varies = false; for (int i = 0; i < h->dim[4]; i++) if (dti4D->frameReferenceTime[i] != dti4D->frameReferenceTime[0]) @@ -1695,19 +1686,19 @@ if(d.deID_CS_n>0) fprintf(fp, "\t],\n"); } } - //CT parameters + // CT parameters json_Float(fp, "\t\"ExposureTime\": %g,\n", d.exposureTimeMs / 1000.0); json_Float(fp, "\t\"XRayTubeCurrent\": %g,\n", d.xRayTubeCurrent); if ((d.TE > 0.0) && (d.isXRay)) fprintf(fp, "\t\"XRayExposure\": %g,\n", d.TE); - //MRI parameters - if (!d.isXRay) { //with CT scans, slice thickness often varies - //beware, not used correctly by all vendors https://public.kitware.com/pipermail/insight-users/2005-September/014711.html + // MRI parameters + if (!d.isXRay) { // with CT scans, slice thickness often varies + // beware, not used correctly by all vendors https://public.kitware.com/pipermail/insight-users/2005-September/014711.html json_Float(fp, "\t\"SliceThickness\": %g,\n", d.zThick); json_Float(fp, "\t\"SpacingBetweenSlices\": %g,\n", d.zSpacing); } - //if (!opts.isAnonymizeBIDS) //issue668 is SAR identifiable?? - //an identical sequence can create different SAR and TablePosition depending on participant + // if (!opts.isAnonymizeBIDS) //issue668 is SAR identifiable?? + // an identical sequence can create different SAR and TablePosition depending on participant json_Float(fp, "\t\"SAR\": %g,\n", d.SAR); if (d.CSA.tablePos[0] > 0.0) fprintf(fp, "\t\"TablePosition\": [\n\t\t%g,\n\t\t%g,\n\t\t%g\t],\n", d.CSA.tablePos[1], d.CSA.tablePos[2], d.CSA.tablePos[3]); @@ -1717,26 +1708,25 @@ if(d.deID_CS_n>0) if ((d.echoNum > 1) || ((d.isMultiEcho) && (d.echoNum > 0))) fprintf(fp, "\t\"EchoNumber\": %d,\n", d.echoNum); if ((d.TE > 0.0) && (!d.isXRay)) { - if ((d.manufacturer == kMANUFACTURER_GE) && (d.isRealIsPhaseMapHz) && (d.velocityEncodeScaleGE < 0)) { //issue617, only set for GE fieldmaphz - json_Float(fp, "\t\"EchoTime1\": %g,\n", d.TE / 1000.0 ); - json_Float(fp, "\t\"EchoTime2\": %g,\n", d.TE / 1000.0 - 1.0/(2.0 * M_PI * d.velocityEncodeScaleGE)); + if ((d.manufacturer == kMANUFACTURER_GE) && (d.isRealIsPhaseMapHz) && (d.velocityEncodeScaleGE < 0)) { // issue617, only set for GE fieldmaphz + json_Float(fp, "\t\"EchoTime1\": %g,\n", d.TE / 1000.0); + json_Float(fp, "\t\"EchoTime2\": %g,\n", d.TE / 1000.0 - 1.0 / (2.0 * M_PI * d.velocityEncodeScaleGE)); // delta TE = -1/(2 * pi * velocityEncodeScaleGE) - } - else + } else fprintf(fp, "\t\"EchoTime\": %g,\n", d.TE / 1000.0); - } - //if ((d.TE2 > 0.0) && (!d.isXRay)) fprintf(fp, "\t\"EchoTime2\": %g,\n", d.TE2 / 1000.0 ); - if (dti4D->frameDuration[0] < 0.0) //e.g. PET scans can have variable TR + } + // if ((d.TE2 > 0.0) && (!d.isXRay)) fprintf(fp, "\t\"EchoTime2\": %g,\n", d.TE2 / 1000.0 ); + if (dti4D->frameDuration[0] < 0.0) // e.g. PET scans can have variable TR json_Float(fp, "\t\"RepetitionTime\": %g,\n", d.TR / 1000.0); json_Float(fp, "\t\"RepetitionTimeExcitation\": %g,\n", dti4D->repetitionTimeExcitation); json_Float(fp, "\t\"RepetitionTimeInversion\": %g,\n", dti4D->repetitionTimeInversion); json_Bool(fp, "\t\"MTState\": %s,\n", d.mtState); // BIDS suggests 0018,9020 but Siemens V-series do not populate this, alternatives are CSA or (0018,0021) CS [SK\MTC\SP] - //SpoilingState + // SpoilingState bool isSpoiled = (d.spoiling > kSPOILING_NONE); - if ((d.spoiling == kSPOILING_UNKOWN) && (strstr(d.sequenceVariant, "\\SP") != NULL)) //BIDS suggests 0018,9016 Siemens V-series do not populate this, (0018,0021) CS [SK\MTC\SP] + if ((d.spoiling == kSPOILING_UNKOWN) && (strstr(d.sequenceVariant, "\\SP") != NULL)) // BIDS suggests 0018,9016 Siemens V-series do not populate this, (0018,0021) CS [SK\MTC\SP] isSpoiled = true; if (isSpoiled) - json_Bool(fp, "\t\"SpoilingState\": %s,\n", true); //Siemens reports SpoilingState but not SpoilingType + json_Bool(fp, "\t\"SpoilingState\": %s,\n", true); // Siemens reports SpoilingState but not SpoilingType if (d.spoiling == kSPOILING_RF) fprintf(fp, "\t\"SpoilingType\": \"RF\",\n"); if (d.spoiling == kSPOILING_GRADIENT) @@ -1747,18 +1737,18 @@ if(d.deID_CS_n>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 + 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 + // n.b. https://neurostars.org/t/getting-missing-ge-information-required-by-bids-for-common-preprocessing/1357/7 if (d.manufacturer == kMANUFACTURER_UIH) json_Str(fp, "\t\"PhaseEncodingDirectionDisplayed\": \"%s\",\n", d.phaseEncodingDirectionDisplayedUIH); - if ((d.manufacturer == kMANUFACTURER_GE) && (d.phaseEncodingGE != kGE_PHASE_ENCODING_POLARITY_UNKNOWN)) { //only set for GE + if ((d.manufacturer == kMANUFACTURER_GE) && (d.phaseEncodingGE != kGE_PHASE_ENCODING_POLARITY_UNKNOWN)) { // only set for GE if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_UNFLIPPED) fprintf(fp, "\t\"PhaseEncodingPolarityGE\": \"Unflipped\",\n"); if (d.phaseEncodingGE == kGE_PHASE_ENCODING_POLARITY_FLIPPED) fprintf(fp, "\t\"PhaseEncodingPolarityGE\": \"Flipped\",\n"); } - //GE specific fields + // GE specific fields if (d.shimGradientX > -33333) fprintf(fp, "\t\"ShimSetting\": [\n\t\t%d,\n\t\t%d,\n\t\t%d\t],\n", d.shimGradientX, d.shimGradientY, d.shimGradientZ); json_Str(fp, "\t\"PrescanReuseString\": \"%s\",\n", d.prescanReuseString); @@ -1793,18 +1783,18 @@ if(d.deID_CS_n>0) } #ifdef myReadAsciiCsa if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (d.CSA.SeriesHeader_offset > 0) && (d.CSA.SeriesHeader_length > 0)) { - float pf = 1.0f; //partial fourier + float pf = 1.0f; // partial fourier float shimSetting[8]; char protocolName[kDICOMStrLarge], fmriExternalInfo[kDICOMStrLarge], coilID[kDICOMStrLarge], consistencyInfo[kDICOMStrLarge], coilElements[kDICOMStrLarge], pulseSequenceDetails[kDICOMStrLarge], wipMemBlock[kDICOMStrExtraLarge]; TCsaAscii csaAscii; siemensCsaAscii(filename, &csaAscii, d.CSA.SeriesHeader_offset, d.CSA.SeriesHeader_length, shimSetting, coilID, consistencyInfo, coilElements, pulseSequenceDetails, fmriExternalInfo, protocolName, wipMemBlock); if ((d.phaseEncodingLines < 1) && (csaAscii.phaseEncodingLines > 0)) d.phaseEncodingLines = csaAscii.phaseEncodingLines; - //if (d.phaseEncodingLines != csaAscii.phaseEncodingLines) //e.g. phaseOversampling + // if (d.phaseEncodingLines != csaAscii.phaseEncodingLines) //e.g. phaseOversampling // printWarning("PhaseEncodingLines reported in DICOM (%d) header does not match value CSA-ASCII (%d) %s\n", d.phaseEncodingLines, csaAscii.phaseEncodingLines, pathoutname); delayTimeInTR = csaAscii.delayTimeInTR; - if ((d.isHasPhase) && (csaAscii.TE0 > 0.0) && (csaAscii.TE1 > 0.0)) { //issue400 - //https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/01-magnetic-resonance-imaging-data.html + if ((d.isHasPhase) && (csaAscii.TE0 > 0.0) && (csaAscii.TE1 > 0.0)) { // issue400 + // https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/01-magnetic-resonance-imaging-data.html json_Float(fp, "\t\"EchoTime1\": %g,\n", csaAscii.TE0 / 1000000.0); json_Float(fp, "\t\"EchoTime2\": %g,\n", csaAscii.TE1 / 1000000.0); } @@ -1814,11 +1804,11 @@ if(d.deID_CS_n>0) if ((d.CSA.mosaicSlices > 1) && (csaAscii.existUcImageNumb > 0)) { if (d.CSA.protocolSliceNumber1 < 2) { printWarning("Assuming mosaics saved in reverse order due to 'sSliceArray.ucImageNumb'\n"); - //never seen such an image in the wild.... sliceDir may need to be reversed + // never seen such an image in the wild.... sliceDir may need to be reversed } d.CSA.protocolSliceNumber1 = 2; } - //ultra-verbose output for deciphering adFree/alFree/alTI values: + // ultra-verbose output for deciphering adFree/alFree/alTI values: /* if (opts.isVerbose > 1) { for (int i = 0; i < kMaxWipFree; i++) @@ -1831,88 +1821,88 @@ if(d.deID_CS_n>0) */ bool isPCASL = false; bool isPASL = false; - //ASL specific tags - 2D pCASL Danny J.J. Wang http://www.loft-lab.org - //ASL specific tags - 2D PASL Siemens Product XA30, n.b pasl/casl/pcasl set using 0018,9250 + // ASL specific tags - 2D pCASL Danny J.J. Wang http://www.loft-lab.org + // ASL specific tags - 2D PASL Siemens Product XA30, n.b pasl/casl/pcasl set using 0018,9250 if (strstr(pulseSequenceDetails, "ep2d_asl")) { - json_FloatNotNan(fp, "\t\"LabelingDuration\": %g,\n", csaAscii.ulLabelingDuration * (1.0 / 1000000.0)); //usec->sec - json_FloatNotNan(fp, "\t\"PostLabelingDelay\": %g,\n", csaAscii.sPostLabelingDelay * (1.0 / 1000000.0)); //usec -> sec + json_FloatNotNan(fp, "\t\"LabelingDuration\": %g,\n", csaAscii.ulLabelingDuration * (1.0 / 1000000.0)); // usec->sec + json_FloatNotNan(fp, "\t\"PostLabelingDelay\": %g,\n", csaAscii.sPostLabelingDelay * (1.0 / 1000000.0)); // usec -> sec } - //ASL specific tags - 2D pCASL Danny J.J. Wang http://www.loft-lab.org + // ASL specific tags - 2D pCASL Danny J.J. Wang http://www.loft-lab.org if ((strstr(pulseSequenceDetails, "ep2d_pcasl")) || (strstr(pulseSequenceDetails, "ep2d_pcasl_UI_PHC"))) { isPCASL = true; repetitionTimePreparation = d.TR; - json_FloatNotNan(fp, "\t\"LabelingDistance\": %g,\n", csaAscii.adFree[1]); //mm, renamed for BIDS https://bids-specification.readthedocs.io/en/stable/glossary.html#labelingdistance-metadata - json_FloatNotNan(fp, "\t\"PostLabelingDelay\": %g,\n", csaAscii.adFree[2] * (1.0 / 1000000.0)); //usec -> sec + json_FloatNotNan(fp, "\t\"LabelingDistance\": %g,\n", csaAscii.adFree[1]); // mm, renamed for BIDS https://bids-specification.readthedocs.io/en/stable/glossary.html#labelingdistance-metadata + json_FloatNotNan(fp, "\t\"PostLabelingDelay\": %g,\n", csaAscii.adFree[2] * (1.0 / 1000000.0)); // usec -> sec float num_RF_Block = csaAscii.adFree[3]; json_FloatNotNan(fp, "\t\"NumRFBlocks\": %g,\n", num_RF_Block); // Sep 5, 2023, at 7:56 PM, Danny JJ Wang the labeling duration is (0.92*20*Num_RF_Block) ms - json_FloatNotNan(fp, "\t\"LabelingDuration\": %g,\n", (0.92 *20.0 *num_RF_Block)/1000.0); //in seconds - json_FloatNotNan(fp, "\t\"RFGap\": %g,\n", csaAscii.adFree[4] * (1.0 / 1000000.0)); //usec -> sec + json_FloatNotNan(fp, "\t\"LabelingDuration\": %g,\n", (0.92 * 20.0 * num_RF_Block) / 1000.0); // in seconds + 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]); - json_FloatNotNan(fp, "\t\"PhiAdjust\": %g,\n", csaAscii.adFree[11]); // percent + 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 + // ASL specific tags - 3D pCASL Danny J.J. Wang http://www.loft-lab.org if (strstr(pulseSequenceDetails, "tgse_pcasl")) { isPCASL = true; repetitionTimePreparation = d.TR; - json_FloatNotNan(fp, "\t\"LabelingDuration\": %g,\n", csaAscii.adFree[2] * (1.0 / 1000000.0)); //usec -> sec - 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\"T1\": %g,\n", csaAscii.adFree[12] * (1.0 / 1000000.0)); //usec -> sec + json_FloatNotNan(fp, "\t\"LabelingDuration\": %g,\n", csaAscii.adFree[2] * (1.0 / 1000000.0)); // usec -> sec + 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\"T1\": %g,\n", csaAscii.adFree[12] * (1.0 / 1000000.0)); // usec -> sec float num_RF_Block = csaAscii.adFree[3]; json_FloatNotNan(fp, "\t\"NumRFBlocks\": %g,\n", num_RF_Block); // Sep 5, 2023, at 7:56 PM, Danny JJ Wang the labeling duration is (0.92*20*Num_RF_Block) ms - json_FloatNotNan(fp, "\t\"LabelingDuration\": %g,\n", (0.92 *20.0 *num_RF_Block)/1000.0); //in seconds + json_FloatNotNan(fp, "\t\"LabelingDuration\": %g,\n", (0.92 * 20.0 * num_RF_Block) / 1000.0); // in seconds } - //ASL specific tags - 2D PASL Siemens Product + // ASL specific tags - 2D PASL Siemens Product if (strstr(pulseSequenceDetails, "ep2d_pasl")) { isPASL = true; - json_FloatNotNan(fp, "\t\"BolusDuration\": %g,\n", csaAscii.alTI[0] * (1.0 / 1000000.0)); //usec->sec - json_FloatNotNan(fp, "\t\"InversionTime\": %g,\n", csaAscii.alTI[2] * (1.0 / 1000000.0)); //usec -> sec + json_FloatNotNan(fp, "\t\"BolusDuration\": %g,\n", csaAscii.alTI[0] * (1.0 / 1000000.0)); // usec->sec + json_FloatNotNan(fp, "\t\"InversionTime\": %g,\n", csaAscii.alTI[2] * (1.0 / 1000000.0)); // usec -> sec } - //ASL specific tags - 3D PASL Siemens Product http://adni.loni.usc.edu/wp-content/uploads/2010/05/ADNI3_Basic_Siemens_Skyra_E11.pdf + // ASL specific tags - 3D PASL Siemens Product http://adni.loni.usc.edu/wp-content/uploads/2010/05/ADNI3_Basic_Siemens_Skyra_E11.pdf if (strstr(pulseSequenceDetails, "tgse_pasl")) { isPASL = true; - json_FloatNotNan(fp, "\t\"BolusDuration\": %g,\n", csaAscii.alTI[0] * (1.0 / 1000000.0)); //usec->sec - json_FloatNotNan(fp, "\t\"InversionTime\": %g,\n", csaAscii.alTI[2] * (1.0 / 1000000.0)); //usec -> sec - //json_FloatNotNan(fp, "\t\"SaturationStopTime\": %g,\n", csaAscii.alTI[2] * (1.0/1000.0)); + json_FloatNotNan(fp, "\t\"BolusDuration\": %g,\n", csaAscii.alTI[0] * (1.0 / 1000000.0)); // usec->sec + json_FloatNotNan(fp, "\t\"InversionTime\": %g,\n", csaAscii.alTI[2] * (1.0 / 1000000.0)); // usec -> sec + // json_FloatNotNan(fp, "\t\"SaturationStopTime\": %g,\n", csaAscii.alTI[2] * (1.0/1000.0)); } - //PASL http://www.pubmed.com/11746944 http://www.pubmed.com/21606572 + // PASL http://www.pubmed.com/11746944 http://www.pubmed.com/21606572 if (strstr(pulseSequenceDetails, "ep2d_fairest")) { isPASL = true; - json_FloatNotNan(fp, "\t\"PostInversionDelay\": %g,\n", csaAscii.adFree[2] * (1.0 / 1000.0)); //usec->sec - json_FloatNotNan(fp, "\t\"PostLabelingDelay\": %g,\n", csaAscii.adFree[4] * (1.0 / 1000.0)); //usec -> sec + json_FloatNotNan(fp, "\t\"PostInversionDelay\": %g,\n", csaAscii.adFree[2] * (1.0 / 1000.0)); // usec->sec + json_FloatNotNan(fp, "\t\"PostLabelingDelay\": %g,\n", csaAscii.adFree[4] * (1.0 / 1000.0)); // usec -> sec } - //ASL specific tags - Oxford (Thomas OKell) + // ASL specific tags - Oxford (Thomas OKell) bool isOxfordASL = false; - if (strstr(pulseSequenceDetails, "to_ep2d_VEPCASL")) { //Oxford 2D pCASL + if (strstr(pulseSequenceDetails, "to_ep2d_VEPCASL")) { // Oxford 2D pCASL isOxfordASL = true; isPCASL = true; 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\"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\"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 + 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\"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\"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 int nPLD = 0; - bool isValid = true; //detect gaps in PLD array: If user sets PLD1=250, PLD2=0 PLD3=375 only PLD1 was acquired + bool isValid = true; // detect gaps in PLD array: If user sets PLD1=250, PLD2=0 PLD3=375 only PLD1 was acquired for (int k = 11; k < 31; k++) { if ((isnan(csaAscii.alFree[k])) || (csaAscii.alFree[k] <= 0.0)) isValid = false; if (isValid) nPLD++; - } //for k - if (nPLD > 0) { // record PostLabelDelays, these are listed as "PLD0","PLD1",etc in PDF - fprintf(fp, "\t\"PostLabelingDelay\": [\n"); //BIDS renaming InitialPostLabelDelay -> PostLabelingDelay https://docs.google.com/document/d/15tnn5F10KpgHypaQJNNGiNKsni9035GtDqJzWqkkP6c/edit# + } // for k + if (nPLD > 0) { // record PostLabelDelays, these are listed as "PLD0","PLD1",etc in PDF + 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"); - fprintf(fp, "\t\t%g", csaAscii.alFree[i + 11] / 1000.0); //ms -> sec + fprintf(fp, "\t\t%g", csaAscii.alFree[i + 11] / 1000.0); // ms -> sec } fprintf(fp, "\t],\n"); } @@ -1925,37 +1915,37 @@ if(d.deID_CS_n>0) if (csaAscii.alFree[k] <= 0.0) isValid = false; }//isValid } //for k */ - for (int k = 3; k < 11; k++) { //vessel locations + for (int k = 3; k < 11; k++) { // vessel locations char newstr[256]; - snprintf(newstr, 256, "\t\"sWipMemBlockAdFree%d\": %%g,\n", k); //issue483: sWipMemBlock.AdFree -> sWipMemBlockAdFree + snprintf(newstr, 256, "\t\"sWipMemBlockAdFree%d\": %%g,\n", k); // issue483: sWipMemBlock.AdFree -> sWipMemBlockAdFree json_FloatNotNan(fp, newstr, csaAscii.adFree[k]); } } - if (strstr(pulseSequenceDetails, "jw_tgse_VEPCASL")) { //Oxford 3D pCASL + if (strstr(pulseSequenceDetails, "jw_tgse_VEPCASL")) { // Oxford 3D pCASL isPCASL = true; isOxfordASL = true; json_Float(fp, "\t\"TagRFFlipAngle\": %g,\n", csaAscii.alFree[6]); - json_Float(fp, "\t\"TagRFDuration\": %g,\n", csaAscii.alFree[7] / 1000000.0); //usec -> sec - json_Float(fp, "\t\"TagRFSeparation\": %g,\n", csaAscii.alFree[8] / 1000000.0); //usec -> sec - json_Float(fp, "\t\"MaximumT1Opt\": %g,\n", csaAscii.alFree[9] / 1000.0); //ms -> sec - json_Float(fp, "\t\"Tag0\": %g,\n", csaAscii.alFree[10] / 1000.0); //DelayTimeInTR usec -> sec - json_Float(fp, "\t\"Tag1\": %g,\n", csaAscii.alFree[11] / 1000.0); //DelayTimeInTR usec -> sec - json_Float(fp, "\t\"Tag2\": %g,\n", csaAscii.alFree[12] / 1000.0); //DelayTimeInTR usec -> sec - json_Float(fp, "\t\"Tag3\": %g,\n", csaAscii.alFree[13] / 1000.0); //DelayTimeInTR usec -> sec + json_Float(fp, "\t\"TagRFDuration\": %g,\n", csaAscii.alFree[7] / 1000000.0); // usec -> sec + json_Float(fp, "\t\"TagRFSeparation\": %g,\n", csaAscii.alFree[8] / 1000000.0); // usec -> sec + json_Float(fp, "\t\"MaximumT1Opt\": %g,\n", csaAscii.alFree[9] / 1000.0); // ms -> sec + json_Float(fp, "\t\"Tag0\": %g,\n", csaAscii.alFree[10] / 1000.0); // DelayTimeInTR usec -> sec + json_Float(fp, "\t\"Tag1\": %g,\n", csaAscii.alFree[11] / 1000.0); // DelayTimeInTR usec -> sec + json_Float(fp, "\t\"Tag2\": %g,\n", csaAscii.alFree[12] / 1000.0); // DelayTimeInTR usec -> sec + json_Float(fp, "\t\"Tag3\": %g,\n", csaAscii.alFree[13] / 1000.0); // DelayTimeInTR usec -> sec int nPLD = 0; - bool isValid = true; //detect gaps in PLD array: If user sets PLD1=250, PLD2=0 PLD3=375 only PLD1 was acquired + bool isValid = true; // detect gaps in PLD array: If user sets PLD1=250, PLD2=0 PLD3=375 only PLD1 was acquired for (int k = 30; k < 38; k++) { if ((isnan(csaAscii.alFree[k])) || (csaAscii.alFree[k] <= 0.0)) isValid = false; if (isValid) 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# + } // 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# for (int i = 0; i < nPLD; i++) { if (i != 0) fprintf(fp, ",\n"); - fprintf(fp, "\t\t%g", csaAscii.alFree[i + 30] / 1000.0); //ms -> sec + fprintf(fp, "\t\t%g", csaAscii.alFree[i + 30] / 1000.0); // ms -> sec } fprintf(fp, "\t],\n"); } @@ -1968,8 +1958,8 @@ if(d.deID_CS_n>0) json_Float(fp, "\t\"PLD5\": %g,\n", csaAscii.alFree[35]/1000.0); */ } - if (isOxfordASL) { //properties common to 2D and 3D ASL - //labelling plane + if (isOxfordASL) { // properties common to 2D and 3D ASL + // labelling plane fprintf(fp, "\t\"TagPlaneDThickness\": %g,\n", csaAscii.dThickness); fprintf(fp, "\t\"TagPlaneUlShape\": %g,\n", csaAscii.ulShape); fprintf(fp, "\t\"TagPlaneSPositionDTra\": %g,\n", csaAscii.sPositionDTra); @@ -1979,12 +1969,12 @@ if(d.deID_CS_n>0) fprintf(fp, "\t\"ArterialSpinLabelingType\": \"PCASL\",\n"); if (isPASL) fprintf(fp, "\t\"ArterialSpinLabelingType\": \"PASL\",\n"); - //AcquisitionVoxelSize uses slice thickness (without gap) - // https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/01-magnetic-resonance-imaging-data.html#common-metadata-fields-applicable-to-both-pcasl-and-pasl + // AcquisitionVoxelSize uses slice thickness (without gap) + // https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/01-magnetic-resonance-imaging-data.html#common-metadata-fields-applicable-to-both-pcasl-and-pasl if (((isPASL) || (isPCASL)) && (csaAscii.interp <= 0)) fprintf(fp, "\t\"AcquisitionVoxelSize\": [\n\t\t%g,\n\t\t%g,\n\t\t%g\t],\n", d.xyzMM[1], d.xyzMM[2], d.zThick); // lund free waveform sequence, see https://github.com/filip-szczepankiewicz/fwf_sequence_tools - if ( (strstr(pulseSequenceDetails, "ep2d_diff_fwf") != 0) || (strstr(pulseSequenceDetails, "ep2d_diff_sms_fwf_simple") != 0)) { + if ((strstr(pulseSequenceDetails, "ep2d_diff_fwf") != 0) || (strstr(pulseSequenceDetails, "ep2d_diff_sms_fwf_simple") != 0)) { for (int i = 0; i < kMaxWipFree; i++) { if (!isnan(csaAscii.adFree[i])) fprintf(fp, "\t\"FWF_adFree[%i]\": %g,\n", i, csaAscii.adFree[i]); @@ -1995,24 +1985,23 @@ if(d.deID_CS_n>0) fprintf(fp, "\t\"FWF_alFree[%i]\": %g,\n", i, csaAscii.alFree[i]); } - for (int d = 0; d < 3; d++) - { - char str [4096]; + for (int d = 0; d < 3; d++) { + char str[4096]; strcpy(str, ""); - for (int i = 0; i < csaAscii.freeDiffusionN;i++) { + for (int i = 0; i < csaAscii.freeDiffusionN; i++) { char tmp[10]; snprintf(tmp, 10, "%1.4f", csaAscii.freeDiffusionVec[i].v[d]); strcat(str, tmp); - if ( (i+1) < csaAscii.freeDiffusionN) + if ((i + 1) < csaAscii.freeDiffusionN) strcat(str, ", "); } char dchar = 'x' + d; fprintf(fp, "\t\"FWF_DVS%c\": [ %s ],\n", dchar, str); } } - //general properties - if ((csaAscii.partialFourier > 0) && ((d.modality == kMODALITY_MR))) { //check MR, e.g. do not report for Siemens PET - //https://github.com/ismrmrd/siemens_to_ismrmrd/blob/master/parameter_maps/IsmrmrdParameterMap_Siemens_EPI_FLASHREF.xsl + // general properties + if ((csaAscii.partialFourier > 0) && ((d.modality == kMODALITY_MR))) { // check MR, e.g. do not report for Siemens PET + // https://github.com/ismrmrd/siemens_to_ismrmrd/blob/master/parameter_maps/IsmrmrdParameterMap_Siemens_EPI_FLASHREF.xsl if (csaAscii.partialFourier == 1) pf = 0.5; // 4/8 if (csaAscii.partialFourier == 2) @@ -2023,7 +2012,7 @@ if(d.deID_CS_n>0) pf = 0.875; fprintf(fp, "\t\"PartialFourier\": %g,\n", pf); } - if (csaAscii.interp > 0) { //in-plane interpolation + if (csaAscii.interp > 0) { // in-plane interpolation interp = true; fprintf(fp, "\t\"Interpolation2D\": %d,\n", interp); } @@ -2044,19 +2033,19 @@ if(d.deID_CS_n>0) if (csaAscii.difBipolar == 2) fprintf(fp, "\t\"DiffusionScheme\": \"Monopolar\",\n"); } - //DelayTimeInTR - // https://groups.google.com/forum/#!topic/bids-discussion/nmg1BOVH1SU - // https://groups.google.com/forum/#!topic/bids-discussion/seD7AtJfaFE - json_Float(fp, "\t\"DelayTime\": %g,\n", delayTimeInTR / 1000000.0); //DelayTimeInTR usec -> sec + // DelayTimeInTR + // https://groups.google.com/forum/#!topic/bids-discussion/nmg1BOVH1SU + // https://groups.google.com/forum/#!topic/bids-discussion/seD7AtJfaFE + json_Float(fp, "\t\"DelayTime\": %g,\n", delayTimeInTR / 1000000.0); // DelayTimeInTR usec -> sec if (d.modality == kMODALITY_MR) json_Float(fp, "\t\"TxRefAmp\": %g,\n", csaAscii.txRefAmp); if (d.modality == kMODALITY_MR) json_Float(fp, "\t\"PhaseResolution\": %g,\n", csaAscii.phaseResolution); json_Float(fp, "\t\"PhaseOversampling\": %g,\n", phaseOversampling); - json_Float(fp, "\t\"VendorReportedEchoSpacing\": %g,\n", csaAscii.echoSpacing / 1000000.0); //usec -> sec - //ETD and epiFactor not useful/reliable https://github.com/rordenlab/dcm2niix/issues/127 - //if (echoTrainDuration > 0) fprintf(fp, "\t\"EchoTrainDuration\": %g,\n", echoTrainDuration / 1000000.0); //usec -> sec - //if (epiFactor > 0) fprintf(fp, "\t\"EPIFactor\": %d,\n", epiFactor); + json_Float(fp, "\t\"VendorReportedEchoSpacing\": %g,\n", csaAscii.echoSpacing / 1000000.0); // usec -> sec + // ETD and epiFactor not useful/reliable https://github.com/rordenlab/dcm2niix/issues/127 + // if (echoTrainDuration > 0) fprintf(fp, "\t\"EchoTrainDuration\": %g,\n", echoTrainDuration / 1000000.0); //usec -> sec + // if (epiFactor > 0) fprintf(fp, "\t\"EPIFactor\": %d,\n", epiFactor); json_Str(fp, "\t\"ReceiveCoilName\": \"%s\",\n", coilID); if (d.modality == kMODALITY_MR) json_Str(fp, "\t\"ReceiveCoilActiveElements\": \"%s\",\n", coilElements); @@ -2066,58 +2055,58 @@ if(d.deID_CS_n>0) json_Str(fp, "\t\"PulseSequenceDetails\": \"%s\",\n", pulseSequenceDetails); json_Str(fp, "\t\"FmriExternalInfo\": \"%s\",\n", fmriExternalInfo); json_Str(fp, "\t\"WipMemBlock\": \"%s\",\n", wipMemBlock); - if (strlen(d.protocolName) < 1) //insert protocol name if it exists in CSA but not DICOM header: https://github.com/nipy/heudiconv/issues/80 + if (strlen(d.protocolName) < 1) // insert protocol name if it exists in CSA but not DICOM header: https://github.com/nipy/heudiconv/issues/80 json_Str(fp, "\t\"ProtocolName\": \"%s\",\n", protocolName); if (csaAscii.refLinesPE > 0) fprintf(fp, "\t\"RefLinesPE\": %d,\n", csaAscii.refLinesPE); - //https://github.com/bids-standard/bids-specification/pull/681#issuecomment-861767213 + // https://github.com/bids-standard/bids-specification/pull/681#issuecomment-861767213 if (csaAscii.combineMode == 1) fprintf(fp, "\t\"CoilCombinationMethod\": \"Sum of Squares\",\n"); if (csaAscii.combineMode == 2) fprintf(fp, "\t\"CoilCombinationMethod\": \"Adaptive Combine\",\n"); - if ((csaAscii.ucMTC == 1) && (d.mtState < 0)) //precedence for 0018,9020 over CSA + if ((csaAscii.ucMTC == 1) && (d.mtState < 0)) // precedence for 0018,9020 over CSA json_Bool(fp, "\t\"MTState\": %s,\n", 1); json_Str(fp, "\t\"ConsistencyInfo\": \"%s\",\n", consistencyInfo); if (csaAscii.accelFact3D > 0) d.accelFactOOP = csaAscii.accelFact3D; - //see issue 672 if (csaAscii.accelFact3D > 1.01) json_Float(fp, "\t\"AccelFact3D\": %g,\n", csaAscii.accelFact3D); //see *spcR_44ns where "sPat.lAccelFactPE = 1", "sPat.lAccelFact3D = 2" (0051,1011) LO [p2], perhaps ParallelReductionFactorInPlane should be 1? - if (csaAscii.parallelReductionFactorInPlane > 0) { //AccelFactorPE -> phase encoding - //1=SENSE, 2=GRAPPA, 32=SMS??, 256=CompressedSense? + // see issue 672 if (csaAscii.accelFact3D > 1.01) json_Float(fp, "\t\"AccelFact3D\": %g,\n", csaAscii.accelFact3D); //see *spcR_44ns where "sPat.lAccelFactPE = 1", "sPat.lAccelFact3D = 2" (0051,1011) LO [p2], perhaps ParallelReductionFactorInPlane should be 1? + if (csaAscii.parallelReductionFactorInPlane > 0) { // AccelFactorPE -> phase encoding + // 1=SENSE, 2=GRAPPA, 32=SMS??, 256=CompressedSense? if (csaAscii.patMode == 1) fprintf(fp, "\t\"MatrixCoilMode\": \"SENSE\",\n"); if (csaAscii.patMode == 2) fprintf(fp, "\t\"MatrixCoilMode\": \"GRAPPA\",\n"); - d.accelFactPE = csaAscii.parallelReductionFactorInPlane; //issue672: csa precedence over value found in DICOM (0051,1011) + d.accelFactPE = csaAscii.parallelReductionFactorInPlane; // issue672: csa precedence over value found in DICOM (0051,1011) if ((csaAscii.accelFact3D < 1.01) && (csaAscii.parallelReductionFactorInPlane != (int)(d.accelFactPE))) printWarning("ParallelReductionFactorInPlane reported in DICOM [0051,1011] (%d) does not match CSA series value %d\n", (int)(d.accelFactPE), csaAscii.parallelReductionFactorInPlane); } - if ((csaAscii.patMode == 256) && (!isnan(csaAscii.accelFactTotal)) && (csaAscii.accelFactTotal > (d.accelFactPE * d.accelFactOOP) )) - d.compressedSensingFactor = csaAscii.accelFactTotal; //see dcm_qa_cs_dl - } else { //e.g. Siemens Vida does not have CSA header, but has many attributes + if ((csaAscii.patMode == 256) && (!isnan(csaAscii.accelFactTotal)) && (csaAscii.accelFactTotal > (d.accelFactPE * d.accelFactOOP))) + d.compressedSensingFactor = csaAscii.accelFactTotal; // see dcm_qa_cs_dl + } else { // e.g. Siemens Vida does not have CSA header, but has many attributes json_Str(fp, "\t\"ReceiveCoilActiveElements\": \"%s\",\n", d.coilElements); if (strcmp(d.coilElements, d.coilName) != 0) json_Str(fp, "\t\"CoilString\": \"%s\",\n", d.coilName); int phaseEncodingLines = d.phaseEncodingLines; - if (phaseEncodingLines < 1) //support enhanced DICOM terminology + if (phaseEncodingLines < 1) // support enhanced DICOM terminology phaseEncodingLines = d.phaseEncodingSteps; if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (!d.is3DAcq) && (phaseEncodingLines > d.echoTrainLength) && (d.echoTrainLength > 1)) { - //ETL is > 1, as some GE files list 1, as an example see series mr_0005 in dcm_qa_nih + // ETL is > 1, as some GE files list 1, as an example see series mr_0005 in dcm_qa_nih float pf = (float)phaseEncodingLines; if (d.accelFactPE > 1) - pf = (float)pf / (float)d.accelFactPE; //estimate: not sure if we round up or down + pf = (float)pf / (float)d.accelFactPE; // estimate: not sure if we round up or down pf = (float)d.echoTrainLength / (float)pf; - if (pf < 1.0) //e.g. if difference between lines and echo length not all explained by iPAT (SENSE/GRAPPA) + if (pf < 1.0) // e.g. if difference between lines and echo length not all explained by iPAT (SENSE/GRAPPA) fprintf(fp, "\t\"PartialFourier\": %g,\n", pf); - } //compute partial Fourier: not reported in XA10, so infer - //printf("PhaseLines=%d EchoTrainLength=%d SENSE=%g\n", d.phaseEncodingLines, d.echoTrainLength, d.accelFactPE); //n.b. we can not distinguish pF from SENSE/GRAPPA for UIH + } // compute partial Fourier: not reported in XA10, so infer + // printf("PhaseLines=%d EchoTrainLength=%d SENSE=%g\n", d.phaseEncodingLines, d.echoTrainLength, d.accelFactPE); //n.b. we can not distinguish pF from SENSE/GRAPPA for UIH } #endif - if (d.interp3D > 1) //through-plane interpolation e.g. GE ZIP2 through-plane http://mriquestions.com/zip.html - fprintf(fp, "\t\"Interpolation3D\": %d,\n", d.interp3D); + if (d.interp3D > 1) // through-plane interpolation e.g. GE ZIP2 through-plane http://mriquestions.com/zip.html + fprintf(fp, "\t\"Interpolation3D\": %d,\n", d.interp3D); // https://neurostars.org/t/repetitiontime-parameters-what-are-they-and-where-to-find-them/20020/6 json_Float(fp, "\t\"RepetitionTimePreparation\": %g,\n", repetitionTimePreparation); - //Philips ASL specific tags, issue533 - //Philips ASL issue 533 + // Philips ASL specific tags, issue533 + // Philips ASL issue 533 /* //see dcm_qa_philips_asl: this works for the mulit-PLD sequence, but not for other sequences. Also beware that value varies per slice, so incorrect values for descending if ( (d.aslFlags != kASL_FLAG_NONE) && (dti4D->triggerDelayTime[0] >= 0.0)) { //see BEP009 PET https://docs.google.com/document/d/1mqMLnxVdLwZjDd4ZiWFqjEAmOmfcModA_R535v3eQs0 @@ -2139,29 +2128,30 @@ if(d.deID_CS_n>0) json_Float(fp, "\t\"InitialPostLabelDelay\": %g,\n", dti4D->triggerDelayTime[0] / 1000.0); } */ - //generic public ASL tags + // generic public ASL tags if (d.postLabelDelay > 0) json_Float(fp, "\t\"PostLabelingDelay\": %g,\n", float(d.postLabelDelay) / 1000.0); - //ASL BIDS tags + // ASL BIDS tags if ((d.aslFlags & kASL_FLAG_GE_CONTINUOUS) || (d.aslFlags & kASL_FLAG_GE_3DCASL)) fprintf(fp, "\t\"ArterialSpinLabelingType\": \"CASL\",\n"); - if ((d.aslFlags & kASL_FLAG_GE_PSEUDOCONTINUOUS) || (d.aslFlags & kASL_FLAG_GE_3DPCASL)) + if ((d.aslFlags & kASL_FLAG_GE_PSEUDOCONTINUOUS) || (d.aslFlags & kASL_FLAG_GE_3DPCASL)) fprintf(fp, "\t\"ArterialSpinLabelingType\": \"PCASL\",\n"); if (d.aslFlags & kASL_FLAG_GE_PULSED) fprintf(fp, "\t\"ArterialSpinLabelingType\": \"PASL\",\n"); if (d.durationLabelPulseGE > 0) { json_Float(fp, "\t\"LabelingDuration\": %g,\n", d.durationLabelPulseGE / 1000.0); - json_Float(fp, "\t\"PostLabelingDelay\": %g,\n", d.TI / 1000.0); //For GE ASL: InversionTime -> Post-label delay + json_Float(fp, "\t\"PostLabelingDelay\": %g,\n", d.TI / 1000.0); // For GE ASL: InversionTime -> Post-label delay json_Float(fp, "\t\"NumberOfPointsPerArm\": %g,\n", d.numberOfPointsPerArm); json_Float(fp, "\t\"NumberOfArms\": %g,\n", d.numberOfArms); } - if (d.numberOfExcitations > 1) json_Float(fp, "\t\"NumberOfExcitations\": %g,\n", d.numberOfExcitations); - if ((d.CSA.multiBandFactor > 1) && (d.modality == kMODALITY_MR)) //AccelFactorSlice + if (d.numberOfExcitations > 1) + json_Float(fp, "\t\"NumberOfExcitations\": %g,\n", d.numberOfExcitations); + if ((d.CSA.multiBandFactor > 1) && (d.modality == kMODALITY_MR)) // AccelFactorSlice fprintf(fp, "\t\"MultibandAccelerationFactor\": %d,\n", d.CSA.multiBandFactor); json_Float(fp, "\t\"PercentPhaseFOV\": %g,\n", d.phaseFieldofView); json_Float(fp, "\t\"PercentSampling\": %g,\n", d.percentSampling); - if (d.echoTrainLength > 1) //>1 as for Siemens EPI this is 1, Siemens uses EPI factor http://mriquestions.com/echo-planar-imaging.html - fprintf(fp, "\t\"EchoTrainLength\": %d,\n", d.echoTrainLength); //0018,0091 Combination of partial fourier and in-plane parallel imaging + if (d.echoTrainLength > 1) //>1 as for Siemens EPI this is 1, Siemens uses EPI factor http://mriquestions.com/echo-planar-imaging.html + fprintf(fp, "\t\"EchoTrainLength\": %d,\n", d.echoTrainLength); // 0018,0091 Combination of partial fourier and in-plane parallel imaging if (d.partialFourierDirection == kPARTIAL_FOURIER_DIRECTION_PHASE) fprintf(fp, "\t\"PartialFourierDirection\": \"PHASE\",\n"); if (d.partialFourierDirection == kPARTIAL_FOURIER_DIRECTION_FREQUENCY) @@ -2171,7 +2161,7 @@ if(d.deID_CS_n>0) if (d.partialFourierDirection == kPARTIAL_FOURIER_DIRECTION_COMBINATION) fprintf(fp, "\t\"PartialFourierDirection\": \"COMBINATION\",\n"); if ((d.phaseEncodingSteps > 0) && (d.isPartialFourier) && (d.manufacturer == kMANUFACTURER_PHILIPS)) { - //issue 377 + // issue 377 fprintf(fp, "\t\"PartialFourierEnabled\": \"YES\",\n"); fprintf(fp, "\t\"PhaseEncodingStepsNoPartialFourier\": %d,\n", d.phaseEncodingSteps); } else if (d.phaseEncodingSteps > 0) @@ -2182,24 +2172,24 @@ if(d.deID_CS_n>0) fprintf(fp, "\t\"PhaseEncodingStepsOutOfPlane\": %d,\n", d.phaseEncodingStepsOutOfPlane); if ((d.phaseEncodingLines > 0) && (d.modality == kMODALITY_MR)) fprintf(fp, "\t\"AcquisitionMatrixPE\": %d,\n", d.phaseEncodingLines); - //Compute ReconMatrixPE - // Actual size of the *reconstructed* data in the PE dimension, which does NOT match - // phaseEncodingLines in the case of interpolation or phaseResolution < 100% - // We'll need this for generating a value for effectiveEchoSpacing that is consistent - // with the *reconstructed* data. + // Compute ReconMatrixPE + // Actual size of the *reconstructed* data in the PE dimension, which does NOT match + // phaseEncodingLines in the case of interpolation or phaseResolution < 100% + // We'll need this for generating a value for effectiveEchoSpacing that is consistent + // with the *reconstructed* data. int reconMatrixPE = d.phaseEncodingLines; if ((h->dim[2] > 0) && (h->dim[1] > 0)) { - if (h->dim[1] == h->dim[2]) //phase encoding does not matter + if (h->dim[1] == h->dim[2]) // phase encoding does not matter reconMatrixPE = h->dim[2]; else if (d.phaseEncodingRC == 'C') - reconMatrixPE = h->dim[2]; //see dcm_qa: NOPF_NOPAT_NOPOS_PERES100_ES0P59_BW2222_200PFOV_AP_0034 + reconMatrixPE = h->dim[2]; // see dcm_qa: NOPF_NOPAT_NOPOS_PERES100_ES0P59_BW2222_200PFOV_AP_0034 else if (d.phaseEncodingRC == 'R') reconMatrixPE = h->dim[1]; } if ((d.modality == kMODALITY_MR) && (reconMatrixPE > 0)) fprintf(fp, "\t\"ReconMatrixPE\": %d,\n", reconMatrixPE); - if ((d.accelFactPE > 1.0) && (d.manufacturer == kMANUFACTURER_PHILIPS) && strstr(d.parallelAcquisitionTechnique, "CSENSE") ) { - //see dcm_qa_cs_dl: while GE allows you to set ASSET and compressed sense, Philips reports only CSENSE + if ((d.accelFactPE > 1.0) && (d.manufacturer == kMANUFACTURER_PHILIPS) && strstr(d.parallelAcquisitionTechnique, "CSENSE")) { + // see dcm_qa_cs_dl: while GE allows you to set ASSET and compressed sense, Philips reports only CSENSE d.compressedSensingFactor = d.accelFactPE; d.accelFactPE = 1.0; d.parallelAcquisitionTechnique[0] = '\0'; @@ -2208,42 +2198,42 @@ if(d.deID_CS_n>0) if (bandwidthPerPixelPhaseEncode == 0.0) bandwidthPerPixelPhaseEncode = d.CSA.bandwidthPerPixelPhaseEncode; json_Float(fp, "\t\"BandwidthPerPixelPhaseEncode\": %g,\n", bandwidthPerPixelPhaseEncode); - //if ((!d.is3DAcq) && (d.accelFactPE > 1.0)) fprintf(fp, "\t\"ParallelReductionFactorInPlane\": %g,\n", d.accelFactPE); + // if ((!d.is3DAcq) && (d.accelFactPE > 1.0)) fprintf(fp, "\t\"ParallelReductionFactorInPlane\": %g,\n", d.accelFactPE); if (d.accelFactPE > 1.0) fprintf(fp, "\t\"ParallelReductionFactorInPlane\": %g,\n", d.accelFactPE); json_Str(fp, "\t\"ParallelAcquisitionTechnique\": \"%s\",\n", d.parallelAcquisitionTechnique); - //https://github.com/rordenlab/dcm2niix/issues/314 + // https://github.com/rordenlab/dcm2niix/issues/314 if (d.accelFactOOP > 1.0) - json_Float(fp, "\t\"ParallelReductionFactorOutOfPlane\": %g,\n", d.accelFactOOP); //issue672 + json_Float(fp, "\t\"ParallelReductionFactorOutOfPlane\": %g,\n", d.accelFactOOP); // issue672 if (d.compressedSensingFactor > 1.0) json_Float(fp, "\t\"CompressedSensingFactor\": %g,\n", d.compressedSensingFactor); - //detect if Siemens data is DeepLearning: see dcm_qa_cs_dl + // detect if Siemens data is DeepLearning: see dcm_qa_cs_dl if (d.manufacturer == kMANUFACTURER_SIEMENS) { - //DRB,DRG,DRS DeepReveal Boost,Gain,Sharp - d.isDeepLearning = (strstr(d.imageType, "_DRB_")|| strstr(d.imageType, "_DRG_") || strstr(d.imageType, "_DRS_") || - strstr(d.imageTypeText, "_DRB_")|| strstr(d.imageTypeText, "_DRG_") || strstr(d.imageTypeText, "_DRS_")); + // DRB,DRG,DRS DeepReveal Boost,Gain,Sharp + d.isDeepLearning = (strstr(d.imageType, "_DRB_") || strstr(d.imageType, "_DRG_") || strstr(d.imageType, "_DRS_") || + strstr(d.imageTypeText, "_DRB_") || strstr(d.imageTypeText, "_DRG_") || strstr(d.imageTypeText, "_DRS_")); } if (d.isDeepLearning) { json_Bool(fp, "\t\"DeepLearning\": %s,\n", 1); json_Str(fp, "\t\"DeepLearningDetails\": \"%s\",\n", d.deepLearningText); } - //EffectiveEchoSpacing - // Siemens bandwidthPerPixelPhaseEncode already accounts for the effects of parallel imaging, - // interpolation, phaseOversampling, and phaseResolution, in the context of the size of the - // *reconstructed* data in the PE dimension + // EffectiveEchoSpacing + // Siemens bandwidthPerPixelPhaseEncode already accounts for the effects of parallel imaging, + // interpolation, phaseOversampling, and phaseResolution, in the context of the size of the + // *reconstructed* data in the PE dimension double effectiveEchoSpacing = 0.0; - //next: dicm2nii's method for determining effectiveEchoSpacing if bandwidthPerPixelPhaseEncode is unknown, see issue 315 - //if ((reconMatrixPE > 0) && (bandwidthPerPixelPhaseEncode <= 0.0) && (d.CSA.sliceMeasurementDuration >= 0)) + // next: dicm2nii's method for determining effectiveEchoSpacing if bandwidthPerPixelPhaseEncode is unknown, see issue 315 + // if ((reconMatrixPE > 0) && (bandwidthPerPixelPhaseEncode <= 0.0) && (d.CSA.sliceMeasurementDuration >= 0)) // effectiveEchoSpacing = d.CSA.sliceMeasurementDuration / (reconMatrixPE * 1000.0); if ((reconMatrixPE > 0) && (bandwidthPerPixelPhaseEncode > 0.0)) effectiveEchoSpacing = 1.0 / (bandwidthPerPixelPhaseEncode * reconMatrixPE); json_Float(fp, "\t\"WaterFatShift\": %g,\n", d.waterFatShift); if ((effectiveEchoSpacing == 0.0) && (d.imagingFrequency > 0.0) && (d.waterFatShift != 0.0) && (d.echoTrainLength > 0) && (reconMatrixPE > 1)) { - //in theory we could use either fieldStrength or imagingFrequency, but the former is typically provided with low precision - //https://github.com/rordenlab/dcm2niix/issues/377 - // EchoSpacing 1/BW/EPI_factor https://www.jiscmail.ac.uk/cgi-bin/webadmin?A2=ind1308&L=FSL&D=0&P=113520 - // this formula from https://support.brainvoyager.com/brainvoyager/functional-analysis-preparation/29-pre-processing/78-epi-distortion-correction-echo-spacing-and-bandwidth - // https://neurostars.org/t/consolidating-epi-echo-spacing-and-readout-time-for-philips-scanner/4406 + // in theory we could use either fieldStrength or imagingFrequency, but the former is typically provided with low precision + // https://github.com/rordenlab/dcm2niix/issues/377 + // EchoSpacing 1/BW/EPI_factor https://www.jiscmail.ac.uk/cgi-bin/webadmin?A2=ind1308&L=FSL&D=0&P=113520 + // this formula from https://support.brainvoyager.com/brainvoyager/functional-analysis-preparation/29-pre-processing/78-epi-distortion-correction-echo-spacing-and-bandwidth + // https://neurostars.org/t/consolidating-epi-echo-spacing-and-readout-time-for-philips-scanner/4406 /* ActualEchoSpacing = WaterFatShift / (ImagingFrequency * 3.4 * (EPI_Factor + 1)) TotalReadoutTIme = ActualEchoSpacing * EPI_Factor @@ -2260,22 +2250,22 @@ if(d.deID_CS_n>0) fprintf(fp, "\t\"EstimatedTotalReadoutTime\": %g,\n", totalReadoutTime); } if ((d.effectiveEchoSpacingGE > 0.0) && (reconMatrixPE > 1) && (d.accelFactPE > 0.0)) { - //TotalReadoutTime = [ ceil (PE_AcquisitionMatrix / Asset_R_factor) - 1] * ESP + // TotalReadoutTime = [ ceil (PE_AcquisitionMatrix / Asset_R_factor) - 1] * ESP float roundFactor = 2.0; if (d.isPartialFourier) roundFactor = 4.0; float NotPhysicalNumberOfAcquiredPELinesGE = (ceil(1 / roundFactor * d.phaseEncodingLines / d.accelFactPE) * roundFactor); - float NotPhysicalTotalReadOutTimeGE = ( NotPhysicalNumberOfAcquiredPELinesGE - 1.0) * d.effectiveEchoSpacingGE * 0.000001; - // printf("ASSET= %g PE_AcquisitionMatrix= %d ESP= %d TotalReadoutTimeGE= %g NumKyLineGE= %d\n", + float NotPhysicalTotalReadOutTimeGE = (NotPhysicalNumberOfAcquiredPELinesGE - 1.0) * d.effectiveEchoSpacingGE * 0.000001; + // printf("ASSET= %g PE_AcquisitionMatrix= %d ESP= %d TotalReadoutTimeGE= %g NumKyLineGE= %d\n", // d.accelFactPE, d.phaseEncodingLines, d.effectiveEchoSpacingGE, NotPhysicalTotalReadOutTimeGE, (int)NotPhysicalNumberOfAcquiredPELinesGE); - //json_Float(fp, "\t\"TotalReadoutTime\": %g,\n", totalReadoutTime); + // json_Float(fp, "\t\"TotalReadoutTime\": %g,\n", totalReadoutTime); effectiveEchoSpacing = NotPhysicalTotalReadOutTimeGE / (d.phaseEncodingLines - 1); - // if this is considered acceptable, meaningful intermediate variables can be written, this might help the end-user. + // if this is considered acceptable, meaningful intermediate variables can be written, this might help the end-user. #ifdef MY_DEBUG fprintf(fp, "\t\"EchoSpacingMicroSecondsGE\": %d,\n", d.effectiveEchoSpacingGE); fprintf(fp, "\t\"NotPhysicalNumberOfAcquiredPELinesGE\": %d,\n", (int)(NotPhysicalNumberOfAcquiredPELinesGE)); json_Float(fp, "\t\"NotPhysicalTotalReadOutTimeGE\": %g,\n", NotPhysicalTotalReadOutTimeGE); -#endif +#endif } json_Float(fp, "\t\"EffectiveEchoSpacing\": %g,\n", effectiveEchoSpacing); // Calculate true echo spacing (should match what Siemens reports on the console) @@ -2290,64 +2280,64 @@ if(d.deID_CS_n>0) if (derivedEchoSpacing != 0) derivedEchoSpacing = 1 / derivedEchoSpacing; json_Float(fp, "\t\"DerivedVendorReportedEchoSpacing\": %g,\n", derivedEchoSpacing); - //TotalReadOutTime: Really should be called "EffectiveReadOutTime", by analogy with "EffectiveEchoSpacing". - // But BIDS spec calls it "TotalReadOutTime". - // So, we DO NOT USE EchoTrainLength, because not trying to compute the actual (physical) readout time. - // Rather, the point of computing "EffectiveEchoSpacing" properly is so that this - // "Total(Effective)ReadOutTime" can be computed straightforwardly as the product of the - // EffectiveEchoSpacing and the size of the *reconstructed* matrix in the PE direction. - // see https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/TopupUsersGuide#A--datain - // FSL definition is start of first line until start of last line. - // Other than the use of (n-1), the value is basically just 1.0/bandwidthPerPixelPhaseEncode. - // https://github.com/rordenlab/dcm2niix/issues/130 - if (d.manufacturer != kMANUFACTURER_UIH) //issue606 + // TotalReadOutTime: Really should be called "EffectiveReadOutTime", by analogy with "EffectiveEchoSpacing". + // But BIDS spec calls it "TotalReadOutTime". + // So, we DO NOT USE EchoTrainLength, because not trying to compute the actual (physical) readout time. + // Rather, the point of computing "EffectiveEchoSpacing" properly is so that this + // "Total(Effective)ReadOutTime" can be computed straightforwardly as the product of the + // EffectiveEchoSpacing and the size of the *reconstructed* matrix in the PE direction. + // see https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/TopupUsersGuide#A--datain + // FSL definition is start of first line until start of last line. + // Other than the use of (n-1), the value is basically just 1.0/bandwidthPerPixelPhaseEncode. + // https://github.com/rordenlab/dcm2niix/issues/130 + if (d.manufacturer != kMANUFACTURER_UIH) // issue606 json_Float(fp, "\t\"AcquisitionDuration\": %g,\n", d.acquisitionDuration); - if ((d.manufacturer == kMANUFACTURER_UIH) && (effectiveEchoSpacing <= 0.0)) //issue225, issue531 + if ((d.manufacturer == kMANUFACTURER_UIH) && (effectiveEchoSpacing <= 0.0)) // issue225, issue531 json_Float(fp, "\t\"TotalReadoutTime\": %g,\n", d.acquisitionDuration / 1000.0); - else if ((reconMatrixPE > 0) && (effectiveEchoSpacing > 0.0) ) - fprintf(fp, "\t\"TotalReadoutTime\": %g,\n", effectiveEchoSpacing * (reconMatrixPE - 1.0)); + else if ((reconMatrixPE > 0) && (effectiveEchoSpacing > 0.0)) + fprintf(fp, "\t\"TotalReadoutTime\": %g,\n", effectiveEchoSpacing * (reconMatrixPE - 1.0)); json_Float(fp, "\t\"PixelBandwidth\": %g,\n", d.pixelBandwidth); 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 + /* + 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 - if (!d.is3DAcq) { //issue849 + isSkipPhaseEncodingAxis = false; // issue 371: ignore phaseEncoding for 3D MP-RAGE/SPACE, but report for 3D EPI + if (!d.is3DAcq) { // issue849 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 - // This will typically correspond with InPlanePhaseEncodingDirectionDICOM - if (d.phaseEncodingRC == 'C') //Values should be "R"ow, "C"olumn or "?"Unknown + // when phase encoding axis is known but we do not know phase encoding polarity + // https://github.com/rordenlab/dcm2niix/issues/163 + // This will typically correspond with InPlanePhaseEncodingDirectionDICOM + if (d.phaseEncodingRC == 'C') // Values should be "R"ow, "C"olumn or "?"Unknown fprintf(fp, "\t\"PhaseEncodingAxis\": \"j\",\n"); else if (d.phaseEncodingRC == 'R') fprintf(fp, "\t\"PhaseEncodingAxis\": \"i\",\n"); } if (((d.phaseEncodingRC == 'R') || (d.phaseEncodingRC == 'C')) && (!isSkipPhaseEncodingAxis) && (phPos >= 0)) { - //printf("%ld %d %d %c %d\n", d.seriesNum, d.echoTrainLength, isSkipPhaseEncodingAxis, d.phaseEncodingRC, phPos); //test issue 371 - if (d.phaseEncodingRC == 'C') //Values should be "R"ow, "C"olumn or "?"Unknown + // printf("%ld %d %d %c %d\n", d.seriesNum, d.echoTrainLength, isSkipPhaseEncodingAxis, d.phaseEncodingRC, phPos); //test issue 371 + if (d.phaseEncodingRC == 'C') // Values should be "R"ow, "C"olumn or "?"Unknown fprintf(fp, "\t\"PhaseEncodingDirection\": \"j"); else if (d.phaseEncodingRC == 'R') fprintf(fp, "\t\"PhaseEncodingDirection\": \"i"); else fprintf(fp, "\t\"PhaseEncodingDirection\": \"?"); - //phaseEncodingDirectionPositive has one of three values: UNKNOWN (-1), NEGATIVE (0), POSITIVE (1) - //However, DICOM and NIfTI are reversed in the j (ROW) direction - //Equivalent to dicm2nii's "if flp(iPhase), phPos = ~phPos; end" - //for samples see https://github.com/rordenlab/dcm2niix/issues/125 + // phaseEncodingDirectionPositive has one of three values: UNKNOWN (-1), NEGATIVE (0), POSITIVE (1) + // However, DICOM and NIfTI are reversed in the j (ROW) direction + // Equivalent to dicm2nii's "if flp(iPhase), phPos = ~phPos; end" + // for samples see https://github.com/rordenlab/dcm2niix/issues/125 if (phPos < 0) - fprintf(fp, "?"); //unknown + fprintf(fp, "?"); // unknown else if ((phPos == 0) && (d.phaseEncodingRC != 'C')) fprintf(fp, "-"); else if ((d.phaseEncodingRC == 'C') && (phPos == 1) && (opts.isFlipY)) @@ -2355,10 +2345,10 @@ following logic now occurs earlier to aid bids guess else if ((d.phaseEncodingRC == 'C') && (phPos == 0) && (!opts.isFlipY)) fprintf(fp, "-"); fprintf(fp, "\",\n"); - } //only save PhaseEncodingDirection if BOTH direction and POLARITY are known + } // only save PhaseEncodingDirection if BOTH direction and POLARITY are known } // if (!d.is3DAcq), e.g. only for 2D issue849 - //Slice Timing UIH or GE >>>> - //in theory, we should also report XA10 slice times here, but see series 24 of https://github.com/rordenlab/dcm2niix/issues/236 + // Slice Timing UIH or GE >>>> + // in theory, we should also report XA10 slice times here, but see series 24 of https://github.com/rordenlab/dcm2niix/issues/236 if ((d.modality != kMODALITY_CT) && (d.modality != kMODALITY_PT) && (!d.is3DAcq) && (h->dim[3] > 1) && (d.CSA.sliceTiming[1] >= 0.0) && (d.CSA.sliceTiming[0] >= 0.0)) { fprintf(fp, "\t\"SliceTiming\": [\n"); for (int i = 0; i < h->dim[3]; i++) { @@ -2368,11 +2358,11 @@ following logic now occurs earlier to aid bids guess } fprintf(fp, "\t],\n"); } - //DICOM orientation and phase encoding: useful for 3D undistortion. Original DICOM values: DICOM not NIfTI space, ignores if 3D image re-oriented + // DICOM orientation and phase encoding: useful for 3D undistortion. Original DICOM values: DICOM not NIfTI space, ignores if 3D image re-oriented float mxOrient = 0.0; for (int i = 1; i < 7; i++) mxOrient = max(mxOrient, fabs(d.orient[i])); - if (! isSameFloatGE(mxOrient, 0.0)) { //if set + if (!isSameFloatGE(mxOrient, 0.0)) { // if set fprintf(fp, "\t\"ImageOrientationPatientDICOM\": [\n"); for (int i = 1; i < 7; i++) { if (i != 1) @@ -2387,21 +2377,21 @@ following logic now occurs earlier to aid bids guess if (d.phaseEncodingRC == 'R') fprintf(fp, "\t\"InPlanePhaseEncodingDirectionDICOM\": \"ROW\",\n"); if ((opts.isGuessBidsFilename) && (strlen(d.CSA.bidsDataType)) && (strlen(d.CSA.bidsDataType))) - fprintf(fp, "\t\"BidsGuess\": [\"%s\",\"%s\"],\n",d.CSA.bidsDataType, d.CSA.bidsEntitySuffix); - //json_Str(fp, "\t\"StationName\": \"%s\",\n", d.stationName); + fprintf(fp, "\t\"BidsGuess\": [\"%s\",\"%s\"],\n", d.CSA.bidsDataType, d.CSA.bidsEntitySuffix); + // json_Str(fp, "\t\"StationName\": \"%s\",\n", d.stationName); // Finish up with info on the conversion tool fprintf(fp, "\t\"ConversionSoftware\": \"dcm2niix\",\n"); fprintf(fp, "\t\"ConversionSoftwareVersion\": \"%s\"\n", kDCMdate); - //fprintf(fp, "\t\"ConversionSoftwareVersion\": \"%s\"\n", kDCMvers );kDCMdate + // fprintf(fp, "\t\"ConversionSoftwareVersion\": \"%s\"\n", kDCMvers );kDCMdate fprintf(fp, "}\n"); fclose(fp); } // nii_SaveBIDSX() void swapEndian(struct nifti_1_header *hdr, unsigned char *im, bool isNative) { - //swap endian from big->little or little->big - // must be told which is native to detect datatype and number of voxels - // one could also auto-detect: hdr->sizeof_hdr==348 + // swap endian from big->little or little->big + // must be told which is native to detect datatype and number of voxels + // one could also auto-detect: hdr->sizeof_hdr==348 if (!isNative) #if defined(USING_MGH_NIFTI_IO) || defined(USING_R) swap_nifti_header(hdr, 1); @@ -2422,7 +2412,7 @@ void swapEndian(struct nifti_1_header *hdr, unsigned char *im, bool isNative) { #endif if (datatype == DT_RGBA32) return; - //n.b. do not swap 8-bit, 24-bit RGB, and 32-bit RGBA + // n.b. do not swap 8-bit, 24-bit RGB, and 32-bit RGBA if (bitpix == 16) nifti_swap_2bytes(nVox, im); if (bitpix == 32) @@ -2449,23 +2439,23 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, #endif unsigned char *removeADC(struct nifti_1_header *hdr, unsigned char *inImg, int numADC) { - //for speed we just clip the number of volumes, the realloc routine would be nice - // we do not want to copy input to a new smaller array since 4D DTI datasets can be huge - // and that would require almost twice as much RAM + // for speed we just clip the number of volumes, the realloc routine would be nice + // we do not want to copy input to a new smaller array since 4D DTI datasets can be huge + // and that would require almost twice as much RAM if (numADC < 1) return inImg; hdr->dim[4] = hdr->dim[4] - numADC; if (hdr->dim[4] < 2) - hdr->dim[0] = 3; //e.g. 4D 2-volume DWI+ADC becomes 3D DWI if ADC is removed + hdr->dim[0] = 3; // e.g. 4D 2-volume DWI+ADC becomes 3D DWI if ADC is removed return inImg; -} //removeADC() +} // removeADC() -//#define naive_reorder_vols //for simple, fast re-ordering that consumes a lot of RAM +// #define naive_reorder_vols //for simple, fast re-ordering that consumes a lot of RAM #ifdef naive_reorder_vols unsigned char *reorderVolumes(struct nifti_1_header *hdr, unsigned char *inImg, int *volOrderIndex) { - //reorder volumes to place ADC at end and (optionally) B=0 at start - // volOrderIndex[0] reports location of desired first volume - // naive solution creates an output buffer that doubles RAM usage (2 *numVol) + // reorder volumes to place ADC at end and (optionally) B=0 at start + // volOrderIndex[0] reports location of desired first volume + // naive solution creates an output buffer that doubles RAM usage (2 *numVol) int numVol = hdr->dim[4]; int numVolBytes = hdr->dim[1] * hdr->dim[2] * hdr->dim[3] * (hdr->bitpix / 8); if ((!volOrderIndex) || (numVol < 1) || (numVolBytes < 1)) @@ -2475,18 +2465,18 @@ unsigned char *reorderVolumes(struct nifti_1_header *hdr, unsigned char *inImg, for (int i = 0; i < numVol; i++) { memcpy(&outImg[outPos], &inImg[volOrderIndex[i] * numVolBytes], numVolBytes); // dest, src, bytes outPos += numVolBytes; - } //for each volume + } // for each volume free(volOrderIndex); free(inImg); return outImg; -} //reorderVolumes() -#else // naive_reorder_vols +} // reorderVolumes() +#else // naive_reorder_vols unsigned char *reorderVolumes(struct nifti_1_header *hdr, unsigned char *inImg, int *volOrderIndex) { - //reorder volumes to place ADC at end and (optionally) B=0 at start - // volOrderIndex[0] reports location of desired first volume - // complicated by fact that 4D DTI data is often huge - // simple solutions would create an output buffer that would double RAM usage (2 *numVol) - // here we bubble-sort volumes in place to use numVols+1 memory + // reorder volumes to place ADC at end and (optionally) B=0 at start + // volOrderIndex[0] reports location of desired first volume + // complicated by fact that 4D DTI data is often huge + // simple solutions would create an output buffer that would double RAM usage (2 *numVol) + // here we bubble-sort volumes in place to use numVols+1 memory int numVol = hdr->dim[4]; int numVolBytes = hdr->dim[1] * hdr->dim[2] * hdr->dim[3] * (hdr->bitpix / 8); int *inPos = (int *)malloc(numVol * sizeof(int)); @@ -2494,26 +2484,26 @@ unsigned char *reorderVolumes(struct nifti_1_header *hdr, unsigned char *inImg, inPos[i] = i; unsigned char *tempVol = (unsigned char *)malloc(numVolBytes); for (int o = 0; o < numVol; o++) { - int i = inPos[volOrderIndex[o]]; //input volume + int i = inPos[volOrderIndex[o]]; // input volume if (i == o) - continue; //volume in correct order - memcpy(&tempVol[0], &inImg[o * numVolBytes], numVolBytes); //make temp - memcpy(&inImg[o * numVolBytes], &inImg[i * numVolBytes], numVolBytes); //copy volume to desire location dest, src, bytes - memcpy(&inImg[i * numVolBytes], &tempVol[0], numVolBytes); //copy unsorted volume + continue; // volume in correct order + memcpy(&tempVol[0], &inImg[o * numVolBytes], numVolBytes); // make temp + memcpy(&inImg[o * numVolBytes], &inImg[i * numVolBytes], numVolBytes); // copy volume to desire location dest, src, bytes + memcpy(&inImg[i * numVolBytes], &tempVol[0], numVolBytes); // copy unsorted volume inPos[o] = i; - } //for each volume + } // for each volume free(inPos); free(volOrderIndex); free(tempVol); return inImg; -} //reorderVolumes() +} // reorderVolumes() #endif // naive_reorder_vols -float *bvals; //global variable for cmp_bvals +float *bvals; // global variable for cmp_bvals int cmp_bvals(const void *a, const void *b) { int ia = *(int *)a; int ib = *(int *)b; - //return bvals[ia] > bvals[ib] ? -1 : bvals[ia] < bvals[ib]; + // return bvals[ia] > bvals[ib] ? -1 : bvals[ia] < bvals[ib]; return bvals[ia] < bvals[ib] ? -1 : bvals[ia] > bvals[ib]; } // cmp_bvals() @@ -2528,31 +2518,31 @@ bool isAllZeroFloat(float v1, float v2, float v3) { } int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata dcmList[], struct TDCMopts opts, int sliceDir, struct TDTI4D *dti4D, int *numADC, int numVol) { - //reports non-zero if any volumes should be excluded (e.g. philip stores an ADC maps) + // reports non-zero if any volumes should be excluded (e.g. philip stores an ADC maps) *numADC = 0; if (opts.isOnlyBIDS) return NULL; - uint64_t indx0 = dcmSort[0].indx; //first volume + uint64_t indx0 = dcmSort[0].indx; // first volume if (isnan(dcmList[indx0].patientPosition[1])) - return NULL; //issue606 do not save bvec for non-spatial data (e.g. physio) + return NULL; // issue606 do not save bvec for non-spatial data (e.g. physio) int numDti = dcmList[indx0].CSA.numDti; #ifdef USING_R ImageList *images = (ImageList *)opts.imageList; #endif - //https://github.com/rordenlab/dcm2niix/issues/352 + // https://github.com/rordenlab/dcm2niix/issues/352 float minBvalThreshold = 6.0; if (dcmList[indx0].manufacturer == kMANUFACTURER_SIEMENS) minBvalThreshold = 50.0; bool allB0 = dcmList[indx0].isDiffusion; bool isDerived = dcmList[indx0].isDerived; if (dcmList[indx0].isDerived) - allB0 = false; //e.g. FA map + allB0 = false; // e.g. FA map if ((numDti == numVol) && (numDti > 1)) allB0 = false; if (numDti > 1) allB0 = false; if (nConvert > 1) { - for (int i = 0; i < nConvert; i++) { //for each image + for (int i = 0; i < nConvert; i++) { // for each image float b0 = dcmList[dcmSort[i].indx].CSA.dtiV[0]; if (!isSameFloat(b0, 0.0)) allB0 = false; @@ -2571,7 +2561,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st char sep = '\t'; if (opts.isCreateBIDS) sep = ' '; - //save bval + // save bval char txtname[2048] = {""}; strcpy(txtname, pathoutname); strcat(txtname, ".bval"); @@ -2580,7 +2570,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st fprintf(fp, "%d%c", 0, sep); fprintf(fp, "\n"); fclose(fp); - //save bvec + // save bvec strcpy(txtname, pathoutname); strcat(txtname, ".bvec"); fp = fopen(txtname, "w"); @@ -2599,40 +2589,40 @@ 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 + } 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 + 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) - 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]; + // if (numDti < kMaxDTIv) + 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++; - } //for slices with repeats - } //for each file - dcmList[indx0].CSA.numDti = numDti; //warning structure not changed outside scope! + } // for slices with repeats + } // for each file + dcmList[indx0].CSA.numDti = numDti; // warning structure not changed outside scope! } bool bValueVaries = false; - for (int i = 1; i < numDti; i++) //check if all bvalues match first volume + for (int i = 1; i < numDti; i++) // check if all bvalues match first volume if (vx[i].V[0] != vx[0].V[0]) bValueVaries = true; - //optional: record b-values even without variability + // optional: record b-values even without variability float minBval = vx[0].V[0]; - for (int i = 1; i < numDti; i++) //check if all bvalues match first volume + for (int i = 1; i < numDti; i++) // check if all bvalues match first volume if (vx[i].V[0] < minBval) minBval = vx[i].V[0]; if (minBval > minBvalThreshold) bValueVaries = true; - //start issue394: experimental, single volume per series, Siemens XA + // start issue394: experimental, single volume per series, Siemens XA if (!isAllZeroFloat(vx[0].V[1], vx[0].V[2], vx[0].V[3])) bValueVaries = true; - //end issue394 + // end issue394 if (!bValueVaries) { bool bVecVaries = false; - for (int i = 1; i < numDti; i++) { //check if all bvalues match first volume + for (int i = 1; i < numDti; i++) { // check if all bvalues match first volume if (vx[i].V[1] != vx[0].V[1]) bVecVaries = true; if (vx[i].V[2] != vx[0].V[2]) @@ -2648,12 +2638,12 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st for (int i = 0; i < numDti; i++) printMessage("bxyz %g %g %g %g\n", vx[i].V[0], vx[i].V[1], vx[i].V[2], vx[i].V[3]); } - //Stutters XINAPSE7 seem to save B=0 as B=2000, but these are not derived? https://github.com/rordenlab/dcm2niix/issues/182 + // Stutters XINAPSE7 seem to save B=0 as B=2000, but these are not derived? https://github.com/rordenlab/dcm2niix/issues/182 bool bZeroBvec = false; - for (int i = 0; i < numDti; i++) { //check if all bvalues match first volume + for (int i = 0; i < numDti; i++) { // check if all bvalues match first volume if (isAllZeroFloat(vx[i].V[1], vx[i].V[2], vx[i].V[3])) { vx[i].V[0] = 0; - //printWarning("volume %d might be B=0\n", i); + // printWarning("volume %d might be B=0\n", i); bZeroBvec = true; } } @@ -2665,9 +2655,9 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st return NULL; } } - //report values: - //for (int i = 1; i < numDti; i++) //check if all bvalues match first volume - // printMessage("%d bval= %g bvec= %g %g %g\n",i, vx[i].V[0], vx[i].V[1], vx[i].V[2], vx[i].V[3]); + // report values: + // for (int i = 1; i < numDti; i++) //check if all bvalues match first volume + // printMessage("%d bval= %g bvec= %g %g %g\n",i, vx[i].V[0], vx[i].V[1], vx[i].V[2], vx[i].V[3]); int minB0idx = 0; float minB0 = vx[0].V[0]; for (int i = 0; i < numDti; i++) @@ -2679,38 +2669,38 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st for (int i = 0; i < numDti; i++) if (vx[i].V[0] > maxB0) maxB0 = vx[i].V[0]; - if (!isDerived) { //no warnings for derived data - //for CMRR sequences unweighted volumes are not actually B=0 but they have B near zero + if (!isDerived) { // no warnings for derived data + // for CMRR sequences unweighted volumes are not actually B=0 but they have B near zero if (minB0 > 50) printWarning("This diffusion series does not have a B0 (reference) volume\n"); if ((!opts.isSortDTIbyBVal) && (minB0idx > 0)) printMessage("Note: B0 not the first volume in the series (FSL eddy reference volume is %d)\n", minB0idx); } - float kADCval = maxB0 + 1; //mark as unusual + float kADCval = maxB0 + 1; // mark as unusual *numADC = 0; bvals = (float *)malloc(numDti * sizeof(float)); int numGEwarn = 0; bool isGEADC = (dcmList[indx0].numberOfDiffusionDirectionGE == 0); // GE non-DTI for (int i = 0; i < numDti; i++) { bvals[i] = vx[i].V[0]; - //printMessage("---bxyz %g %g %g %g\n",vx[i].V[0],vx[i].V[1],vx[i].V[2],vx[i].V[3]); - //Philips includes derived isotropic images - //if (((dcmList[indx0].manufacturer == kMANUFACTURER_GE) || (dcmList[indx0].manufacturer == kMANUFACTURER_PHILIPS)) && (isADCnotDTI(vx[i]))) { + // printMessage("---bxyz %g %g %g %g\n",vx[i].V[0],vx[i].V[1],vx[i].V[2],vx[i].V[3]); + // Philips includes derived isotropic images + // if (((dcmList[indx0].manufacturer == kMANUFACTURER_GE) || (dcmList[indx0].manufacturer == kMANUFACTURER_PHILIPS)) && (isADCnotDTI(vx[i]))) { if (((dcmList[indx0].manufacturer == kMANUFACTURER_GE)) && (isADCnotDTI(vx[i]))) { numGEwarn += 1; - if (isGEADC) { //e.g. GE Trace where bval=900, bvec=0,0,0 + if (isGEADC) { // e.g. GE Trace where bval=900, bvec=0,0,0 *numADC = *numADC + 1; - //printWarning("GE ADC volume %d\n", i+1); + // printWarning("GE ADC volume %d\n", i+1); bvals[i] = kADCval; } else - vx[i].V[0] = 0; //e.g. GE raw B=0 where bval=900, bvec=0,0,0 - } //see issue 245, however this does impact some anonymized files where bvec but not bval removed https://neurostars.org/t/dcm2bids-after-conversion-to-bids-bvals-are-zeros/20198/11 + vx[i].V[0] = 0; // e.g. GE raw B=0 where bval=900, bvec=0,0,0 + } // see issue 245, however this does impact some anonymized files where bvec but not bval removed https://neurostars.org/t/dcm2bids-after-conversion-to-bids-bvals-are-zeros/20198/11 if (((dcmList[indx0].manufacturer == kMANUFACTURER_PHILIPS)) && (isADCnotDTI(vx[i]))) { *numADC = *numADC + 1; bvals[i] = kADCval; - //printMessage("+++bxyz %d\n",i); + // printMessage("+++bxyz %d\n",i); } - bvals[i] = bvals[i] + (0.5 * i / numDti); //add a small bias so ties are kept in sequential order + bvals[i] = bvals[i] + (0.5 * i / numDti); // add a small bias so ties are kept in sequential order } // See issue 777: removed the warning because GE DTI b=0 with bval>0 but bvec=0 (prior to version 29.1) will be handled by geCorrectBvecs() // if (numGEwarn > 0) @@ -2723,7 +2713,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st return NULL; }*/ bool isIsotropic = false; - if ((*numADC == numDti) || (numGEwarn == numDti)) //issue 405: we now save bvals file for isotropic series + if ((*numADC == numDti) || (numGEwarn == numDti)) // issue 405: we now save bvals file for isotropic series isIsotropic = true; if (*numADC > 0) { // DWIs (i.e. short diffusion scans with too few directions to @@ -2744,19 +2734,19 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st printMessage("Note: this appears to be a b=0+trace DWI; ADC/trace removal has been disabled.\n"); } else { if ((numDti - *numADC) < 2) { - /*if (!dcmList[indx0].isDerived) //no need to warn if images are derived Trace/ND pair - printWarning("No bvec/bval files created: only single value after ADC excluded\n"); - *numADC = 0; - free(bvals); - free(vx); - return NULL;*/ + /*if (!dcmList[indx0].isDerived) //no need to warn if images are derived Trace/ND pair + printWarning("No bvec/bval files created: only single value after ADC excluded\n"); + *numADC = 0; + free(bvals); + free(vx); + return NULL;*/ printMessage("Warning: Isotropic DWI series, all bvecs are zero (issue 405)\n"); *numADC = 0; } else printMessage("Note: %d volumes appear to be ADC or trace images that will be removed to allow processing\n", *numADC); } } - //sort ALL including ADC + // sort ALL including ADC int *volOrderIndex = (int *)malloc(numDti * sizeof(int)); for (int i = 0; i < numDti; i++) volOrderIndex[i] = i; @@ -2768,22 +2758,22 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st if (bvals[i] < kADCval) { volOrderIndex[o] = i; o++; - } //if not ADC - } //for each volume - } //if sort else if has ADC + } // if not ADC + } // for each volume + } // if sort else if has ADC free(bvals); - //save VX as sorted + // save VX as sorted TDTI *vxOrig = (TDTI *)malloc(numDti * sizeof(TDTI)); for (int i = 0; i < numDti; i++) vxOrig[i] = vx[i]; - //remove ADC + // remove ADC numDti = numDti - *numADC; free(vx); vx = (TDTI *)malloc(numDti * sizeof(TDTI)); for (int i = 0; i < numDti; i++) vx[i] = vxOrig[volOrderIndex[i]]; free(vxOrig); - //if no ADC or sequential, the is no need to re-order volumes + // if no ADC or sequential, the is no need to re-order volumes bool isSequential = true; for (int i = 1; i < (numDti + *numADC); i++) if (volOrderIndex[i] <= volOrderIndex[i - 1]) @@ -2794,7 +2784,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st } if (!isSequential) printMessage("DTI volumes re-ordered by ascending b-value\n"); -#ifdef RAW_BVEC //save raw bvecs as reported by DICOM, not rotated to image space +#ifdef RAW_BVEC // save raw bvecs as reported by DICOM, not rotated to image space char txtname[2048] = {""}; strcpy(txtname, pathoutname); strcat(txtname, ".rvec"); @@ -2810,107 +2800,94 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st fprintf(fp, "\n"); fclose(fp); #endif - dcmList[indx0].CSA.numDti = numDti; //warning structure not changed outside scope! + dcmList[indx0].CSA.numDti = numDti; // warning structure not changed outside scope! geCorrectBvecs(&dcmList[indx0], sliceDir, vx, opts.isVerbose); siemensPhilipsCorrectBvecs(&dcmList[indx0], sliceDir, vx, opts.isVerbose); - if (dcmList[indx0].CSA.numDti < 1) { //issue449 + if (dcmList[indx0].CSA.numDti < 1) { // issue449 free(vx); return NULL; } - if (!opts.isFlipY) { //!FLIP_Y&& (dcmList[indx0].CSA.mosaicSlices < 2) mosaics are always flipped in the Y direction - //the order of rows is flipped: flip the y-polarity + if (!opts.isFlipY) { //! FLIP_Y&& (dcmList[indx0].CSA.mosaicSlices < 2) mosaics are always flipped in the Y direction + // the order of rows is flipped: flip the y-polarity for (int i = 0; i < (numDti); i++) { if (fabs(vx[i].V[2]) > FLT_EPSILON) vx[i].V[2] = -vx[i].V[2]; - } //for each direction - //less intuitively: we have now flipped the determinant, so we must swap the x-polarity + } // for each direction + // less intuitively: we have now flipped the determinant, so we must swap the x-polarity for (int i = 0; i < (numDti); i++) { if (fabs(vx[i].V[1]) > FLT_EPSILON) vx[i].V[1] = -vx[i].V[1]; - } //for each direction - } //if not a mosaic + } // for each direction + } // if not a mosaic #ifdef USING_DCM2NIIXFSWRAPPER - // make adjustments for MGH bvecs output - for (int i = 0; i < (numDti); i++) { - if (sliceDir < 0) - { - // at this point, bvecs output is calculated as not isFlipY, assuming isFlipZ, determinant is positive. - // So, bvecs first column is reversed for FSL. - // MGH conversion: not isFlipY, slice direction not flipped, determinant is negative, - // 1. we need to reverse bvecs column 1 back, - // 2. also need to reverse bvecs column 3 - - float tmp = vx[i].V[1]; - vx[i].V[1] = -vx[i].V[1]; - if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0) - { - if (i < 6) - printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. sliceDir < 0) flip bvecs sign column 1: %f => %f\n", tmp, vx[i].V[1]); - } - - if (fabs(vx[i].V[3]) > FLT_EPSILON) - { - tmp = vx[i].V[3]; - vx[i].V[3] = -vx[i].V[3]; - if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0) - { - if (i < 6) - printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. sliceDir < 0) flip bvecs sign column 3: %f => %f\n", tmp, vx[i].V[3]); - } - } - } - else if (abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant) - { - // swap signs for every column - for (int j = 1; j < 4; j++) - { - if (fabs(vx[i].V[j]) > FLT_EPSILON) - { - float tmp = vx[i].V[j]; - vx[i].V[j] = -vx[i].V[j]; - if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0) - { - if (i < 6) - printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant) flip bvecs sign column j: %f => %f\n", tmp, vx[i].V[j]); - } - } - } - } - else // sliceDir >= 0 && abs(sliceDir) != kSliceOrientMosaicNegativeDeterminant - { - // MGH conversion: not flip Y, image determinant is positive, bvecs first column is reversed for FSL. - // So, we need to flip bvecs first column. - if (fabs(vx[i].V[1]) > FLT_EPSILON) - { - float tmp = vx[i].V[1]; - vx[i].V[1] = -vx[i].V[1]; - if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0) - { - if (i < 6) - printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. abs(sliceDir) != kSliceOrientMosaicNegativeDeterminant) flip bvecs sign column 1: %f => %f\n", tmp, vx[i].V[1]); - } - } - } - } //for each direction - - mrifsStruct.numDti = numDti; - mrifsStruct.tdti = (TDTI *)malloc(numDti * sizeof(TDTI)); - for (int i = 0; i < numDti; i++) - { - mrifsStruct.tdti[i].V[0] = vx[i].V[0]; - mrifsStruct.tdti[i].V[1] = vx[i].V[1]; - mrifsStruct.tdti[i].V[2] = vx[i].V[2]; - mrifsStruct.tdti[i].V[3] = vx[i].V[3]; - } + // make adjustments for MGH bvecs output + for (int i = 0; i < (numDti); i++) { + if (sliceDir < 0) { + // at this point, bvecs output is calculated as not isFlipY, assuming isFlipZ, determinant is positive. + // So, bvecs first column is reversed for FSL. + // MGH conversion: not isFlipY, slice direction not flipped, determinant is negative, + // 1. we need to reverse bvecs column 1 back, + // 2. also need to reverse bvecs column 3 + + float tmp = vx[i].V[1]; + vx[i].V[1] = -vx[i].V[1]; + if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0) { + if (i < 6) + printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. sliceDir < 0) flip bvecs sign column 1: %f => %f\n", tmp, vx[i].V[1]); + } + + if (fabs(vx[i].V[3]) > FLT_EPSILON) { + tmp = vx[i].V[3]; + vx[i].V[3] = -vx[i].V[3]; + if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0) { + if (i < 6) + printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. sliceDir < 0) flip bvecs sign column 3: %f => %f\n", tmp, vx[i].V[3]); + } + } + } else if (abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant) { + // swap signs for every column + for (int j = 1; j < 4; j++) { + if (fabs(vx[i].V[j]) > FLT_EPSILON) { + float tmp = vx[i].V[j]; + vx[i].V[j] = -vx[i].V[j]; + if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0) { + if (i < 6) + printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant) flip bvecs sign column j: %f => %f\n", tmp, vx[i].V[j]); + } + } + } + } else // sliceDir >= 0 && abs(sliceDir) != kSliceOrientMosaicNegativeDeterminant + { + // MGH conversion: not flip Y, image determinant is positive, bvecs first column is reversed for FSL. + // So, we need to flip bvecs first column. + if (fabs(vx[i].V[1]) > FLT_EPSILON) { + float tmp = vx[i].V[1]; + vx[i].V[1] = -vx[i].V[1]; + if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0) { + if (i < 6) + printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. abs(sliceDir) != kSliceOrientMosaicNegativeDeterminant) flip bvecs sign column 1: %f => %f\n", tmp, vx[i].V[1]); + } + } + } + } // for each direction + + mrifsStruct.numDti = numDti; + mrifsStruct.tdti = (TDTI *)malloc(numDti * sizeof(TDTI)); + for (int i = 0; i < numDti; i++) { + mrifsStruct.tdti[i].V[0] = vx[i].V[0]; + mrifsStruct.tdti[i].V[1] = vx[i].V[1]; + mrifsStruct.tdti[i].V[2] = vx[i].V[2]; + mrifsStruct.tdti[i].V[3] = vx[i].V[3]; + } #endif if (opts.isVerbose) { for (int i = 0; i < (numDti); i++) { printMessage("%d\tB=\t%g\tVec=\t%g\t%g\t%g\n", i, vx[i].V[0], vx[i].V[1], vx[i].V[2], vx[i].V[3]); - } //for each direction + } // for each direction } - //printMessage("%f\t%f\t%f",dcmList[indx0].CSA.dtiV[1][1],dcmList[indx0].CSA.dtiV[1][2],dcmList[indx0].CSA.dtiV[1][3]); + // printMessage("%f\t%f\t%f",dcmList[indx0].CSA.dtiV[1][1],dcmList[indx0].CSA.dtiV[1][2],dcmList[indx0].CSA.dtiV[1][3]); #ifdef USING_R std::vector bValues(numDti); std::vector bVectors(numDti * 3); @@ -2926,8 +2903,8 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st if (opts.saveFormat != kSaveFormatNIfTI) { if (numDti < kMaxDTI4D) { dcmList[indx0].CSA.numDti = numDti; - 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 dti4D->S[i].V[v] = vx[i].V[v]; } free(vx); @@ -2938,7 +2915,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st char txtname[2048] = {""}; strcpy(txtname, pathoutname); strcat(txtname, ".bval"); - //printMessage("Saving DTI %s\n",txtname); + // printMessage("Saving DTI %s\n",txtname); FILE *fp = fopen(txtname, "w"); if (fp == NULL) { free(vx); @@ -2954,7 +2931,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st fprintf(fp, "%g\n", vx[numDti - 1].V[0]); fclose(fp); #endif - if (isIsotropic) { //issue 405: ISOTROPIC images have bval but not bvec + if (isIsotropic) { // issue 405: ISOTROPIC images have bval but not bvec free(vx); return volOrderIndex; } @@ -2965,7 +2942,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st strcat(txtname, ".mvec"); else strcat(txtname, ".bvec"); - //printMessage("Saving DTI %s\n",txtname); + // printMessage("Saving DTI %s\n",txtname); fp = fopen(txtname, "w"); if (fp == NULL) { free(vx); @@ -2993,12 +2970,12 @@ float sqr(float v) { return v * v; } // sqr() -#ifdef newTilt //see issue 254 -float vec3Length(vec3 v) { //normalize vector length +#ifdef newTilt // see issue 254 +float vec3Length(vec3 v) { // normalize vector length return sqrt((v.v[0] * v.v[0]) + (v.v[1] * v.v[1]) + (v.v[2] * v.v[2])); } -float vec3maxMag(vec3 v) { //return signed vector with maximum magnitude +float vec3maxMag(vec3 v) { // return signed vector with maximum magnitude float mx = v.v[0]; if (fabs(v.v[1]) > fabs(mx)) mx = v.v[1]; @@ -3008,8 +2985,8 @@ float vec3maxMag(vec3 v) { //return signed vector with maximum magnitude } vec3 makePositive(vec3 v) { - //we do not no order of cross product or order of instance number (e.g. head->foot, foot->head) - // this function matches the polarity of slice direction inferred from patient position and image orient + // we do not no order of cross product or order of instance number (e.g. head->foot, foot->head) + // this function matches the polarity of slice direction inferred from patient position and image orient vec3 ret = v; if (vec3maxMag(v) >= 0.0) return ret; @@ -3019,28 +2996,28 @@ vec3 makePositive(vec3 v) { return ret; } -void vecRep(vec3 v) { //normalize vector length +void vecRep(vec3 v) { // normalize vector length printMessage("[%g %g %g]\n", v.v[0], v.v[1], v.v[2]); } -//Precise method for determining gantry tilt -// rationale: -// gantry tilt (0018,1120) is optional -// some tools may correct gantry tilt but not reset 0018,1120 -// 0018,1120 might be saved at low precision (though patientPosition, orient might be as well) -//https://github.com/rordenlab/dcm2niix/issues/253 +// Precise method for determining gantry tilt +// rationale: +// gantry tilt (0018,1120) is optional +// some tools may correct gantry tilt but not reset 0018,1120 +// 0018,1120 might be saved at low precision (though patientPosition, orient might be as well) +// https://github.com/rordenlab/dcm2niix/issues/253 float computeGantryTiltPrecise(struct TDICOMdata d1, struct TDICOMdata d2, int isVerbose) { float ret = 0.0; if (isNanPosition(d1)) return ret; vec3 slice_vector = setVec3(d2.patientPosition[1] - d1.patientPosition[1], - d2.patientPosition[2] - d1.patientPosition[2], - d2.patientPosition[3] - d1.patientPosition[3]); + d2.patientPosition[2] - d1.patientPosition[2], + d2.patientPosition[3] - d1.patientPosition[3]); float len = vec3Length(slice_vector); if (isSameFloat(len, 0.0)) { slice_vector = setVec3(d1.patientPositionLast[1] - d1.patientPosition[1], - d1.patientPositionLast[2] - d1.patientPosition[2], - d1.patientPositionLast[3] - d1.patientPosition[3]); + d1.patientPositionLast[2] - d1.patientPosition[2], + d1.patientPositionLast[3] - d1.patientPosition[3]); len = vec3Length(slice_vector); if (isSameFloat(len, 0.0)) return ret; @@ -3050,27 +3027,27 @@ float computeGantryTiltPrecise(struct TDICOMdata d1, struct TDICOMdata d2, int i slice_vector = makePositive(slice_vector); vec3 read_vector = setVec3(d1.orient[1], d1.orient[2], d1.orient[3]); vec3 phase_vector = setVec3(d1.orient[4], d1.orient[5], d1.orient[6]); - vec3 slice_vector90 = crossProduct(read_vector, phase_vector); //perpendicular + vec3 slice_vector90 = crossProduct(read_vector, phase_vector); // perpendicular slice_vector90 = makePositive(slice_vector90); float len90 = vec3Length(slice_vector90); if (isSameFloat(len90, 0.0)) return ret; float dotX = dotProduct(slice_vector90, slice_vector); float cosX = dotX / (len * len90); - float degX = acos(cosX) * (180.0 / M_PI); //arccos, radian -> degrees + float degX = acos(cosX) * (180.0 / M_PI); // arccos, radian -> degrees if (!isSameFloatGE(cosX, 1.0)) ret = degX; if ((isSameFloat(ret, 0.0)) && (isSameFloat(ret, d1.gantryTilt))) return 0.0; - //determine if gantry tilt is positive or negative + // determine if gantry tilt is positive or negative vec3 signv = crossProduct(slice_vector, slice_vector90); float sign = vec3maxMag(signv); if (isSameFloatGE(ret, 0.0)) - return 0.0; //parallel vectors + return 0.0; // parallel vectors if (sign > 0.0) - ret = -ret; //the length of len90 was negative, negative gantry tilt - //while (ret >= 89.99) ret -= 90; - //while (ret <= -89.99) ret += 90; + ret = -ret; // the length of len90 was negative, negative gantry tilt + // while (ret >= 89.99) ret -= 90; + // while (ret <= -89.99) ret += 90; if (isSameFloatGE(ret, 0.0)) return 0.0; if ((isVerbose) || (isnan(ret))) { @@ -3087,39 +3064,39 @@ float computeGantryTiltPrecise(struct TDICOMdata d1, struct TDICOMdata d2, int i printMessage("Gantry Tilt based on 0018,1120 %g, estimated from slice vector %g\n", d1.gantryTilt, ret); return ret; } -#endif //newTilt //see issue 254 +#endif // newTilt //see issue 254 float intersliceDistance(struct TDICOMdata d1, struct TDICOMdata d2) { - //some MRI scans have gaps between slices, some CT have overlapping slices. Comparing adjacent slices provides measure for dx between slices + // some MRI scans have gaps between slices, some CT have overlapping slices. Comparing adjacent slices provides measure for dx between slices if (isNanPosition(d1) || isNanPosition(d2)) return d1.xyzMM[3]; float tilt = 1.0; - //printMessage("0020,0032 %g %g %g -> %g %g %g\n",d1.patientPosition[1],d1.patientPosition[2],d1.patientPosition[3],d2.patientPosition[1],d2.patientPosition[2],d2.patientPosition[3]); + // printMessage("0020,0032 %g %g %g -> %g %g %g\n",d1.patientPosition[1],d1.patientPosition[2],d1.patientPosition[3],d2.patientPosition[1],d2.patientPosition[2],d2.patientPosition[3]); if (d1.gantryTilt != 0) - tilt = (float)cos(d1.gantryTilt * M_PI / 180); //for CT scans with gantry tilt, we need to compute distance between slices, not distance along bed + tilt = (float)cos(d1.gantryTilt * M_PI / 180); // for CT scans with gantry tilt, we need to compute distance between slices, not distance along bed return tilt * sqrt(sqr(d1.patientPosition[1] - d2.patientPosition[1]) + - sqr(d1.patientPosition[2] - d2.patientPosition[2]) + - sqr(d1.patientPosition[3] - d2.patientPosition[3])); -} //intersliceDistance() - -//#define myInstanceNumberOrderIsNotSpatial -//instance number is virtually always ordered based on spatial position. -// interleaved/multi-band conversion will be disrupted if instance number refers to temporal order -// these functions reorder images based on spatial position -// this situation is exceptionally rare, and there is a performance penalty -// further, there may be unintended consequences. -// Therefore, use of myInstanceNumberOrderIsNotSpatial is NOT recommended -// a better solution is to fix the sequences that generated those files -// as such images will probably disrupt most tools. -// This option is only to salvage borked data. -// This code has also not been tested on data stored in TXYZ rather than XYZT order -//#ifdef myInstanceNumberOrderIsNotSpatial + sqr(d1.patientPosition[2] - d2.patientPosition[2]) + + sqr(d1.patientPosition[3] - d2.patientPosition[3])); +} // intersliceDistance() + +// #define myInstanceNumberOrderIsNotSpatial +// instance number is virtually always ordered based on spatial position. +// interleaved/multi-band conversion will be disrupted if instance number refers to temporal order +// these functions reorder images based on spatial position +// this situation is exceptionally rare, and there is a performance penalty +// further, there may be unintended consequences. +// Therefore, use of myInstanceNumberOrderIsNotSpatial is NOT recommended +// a better solution is to fix the sequences that generated those files +// as such images will probably disrupt most tools. +// This option is only to salvage borked data. +// This code has also not been tested on data stored in TXYZ rather than XYZT order +// #ifdef myInstanceNumberOrderIsNotSpatial float intersliceDistanceSigned(struct TDICOMdata d1, struct TDICOMdata d2) { -// Compute the signed slice position on the through-slice axis -// https://nipy.org/nibabel/dicom/dicom_orientation.html#working-out-the-z-coordinates-for-a-set-of-slices -// https://itk.org/pipermail/insight-users/2003-September/004762.html -//reports distance between two slices, signed as 2nd slice can be in front or behind 1st + // Compute the signed slice position on the through-slice axis + // https://nipy.org/nibabel/dicom/dicom_orientation.html#working-out-the-z-coordinates-for-a-set-of-slices + // https://itk.org/pipermail/insight-users/2003-September/004762.html + // reports distance between two slices, signed as 2nd slice can be in front or behind 1st vec3 slice_vector = setVec3(d2.patientPosition[1] - d1.patientPosition[1], d2.patientPosition[2] - d1.patientPosition[2], d2.patientPosition[3] - d1.patientPosition[3]); @@ -3130,14 +3107,14 @@ float intersliceDistanceSigned(struct TDICOMdata d1, struct TDICOMdata d2) { len = len * cos(d1.gantryTilt * M_PI / 180); vec3 read_vector = setVec3(d1.orient[1], d1.orient[2], d1.orient[3]); vec3 phase_vector = setVec3(d1.orient[4], d1.orient[5], d1.orient[6]); - vec3 slice_vector90 = crossProduct(read_vector, phase_vector); //perpendicular + vec3 slice_vector90 = crossProduct(read_vector, phase_vector); // perpendicular float dot = dotProduct(slice_vector90, slice_vector); if (dot < 0.0) return -len; return len; } -//https://stackoverflow.com/questions/36714030/c-sort-float-array-while-keeping-track-of-indices/36714204 +// https://stackoverflow.com/questions/36714030/c-sort-float-array-while-keeping-track-of-indices/36714204 struct TFloatSort { float position; int volume, index; @@ -3154,7 +3131,7 @@ int compareTFloatSort(const void *a, const void *b) { return 1; if ((*a1).position < (*a2).position) return -1; - //if value is tied, retain index order (useful for TXYZ images?) + // if value is tied, retain index order (useful for TXYZ images?) if ((*a1).index > (*a2).index) return 1; if ((*a1).index < (*a2).index) @@ -3162,34 +3139,33 @@ int compareTFloatSort(const void *a, const void *b) { return 0; } // compareTFloatSort() - int isSameFloatT(float a, float b, float tolerance) { return (fabs(a - b) <= tolerance); } bool ensureSequentialSlicePositions(int d3, int d4, struct TDCMsort dcmSort[], struct TDICOMdata dcmList[], int verbose) { - //ensure slice position is sequential: either ascending [1 2 3] or descending [3 2 1], not [1 3 2], [3 1 2] etc. - //n.b. as currently designed, this will force swapDim3Dim4() for 4D data + // ensure slice position is sequential: either ascending [1 2 3] or descending [3 2 1], not [1 3 2], [3 1 2] etc. + // n.b. as currently designed, this will force swapDim3Dim4() for 4D data int nConvert = d3 * d4; if (d3 < 3) - return true; //always consistent - //first pass: check order: issue 622 + return true; // always consistent + // first pass: check order: issue 622 int i = 0; bool isSequential = true; - for (int t = 0; t < d4; t++) { //for each volume + for (int t = 0; t < d4; t++) { // for each volume float dx = intersliceDistanceSigned(dcmList[dcmSort[i].indx], dcmList[dcmSort[i + 1].indx]); - for (int z = 0; z < d3; z++) { //for slice + for (int z = 0; z < d3; z++) { // for slice if (z > 0) { float dx2 = intersliceDistanceSigned(dcmList[dcmSort[i - 1].indx], dcmList[dcmSort[i].indx]); if (!isSameFloatT(dx, dx2, kSliceTolerance)) isSequential = false; - } //if not 1st slice (which does not have prior slice) + } // if not 1st slice (which does not have prior slice) i++; - } //for each slice - } //for each volume + } // for each slice + } // for each volume if (isSequential) return true; - //second pass: fix if required + // second pass: fix if required printWarning("Instance Number (0020,0013) order is not spatial.\n"); TFloatSort *floatSort = (TFloatSort *)malloc(nConvert * sizeof(TFloatSort)); int minVol = dcmList[dcmSort[0].indx].rawDataRunNumber; @@ -3197,7 +3173,7 @@ bool ensureSequentialSlicePositions(int d3, int d4, struct TDCMsort dcmSort[], s int maxVolNotADC = -1; int minInstance = dcmList[dcmSort[0].indx].imageNum; int maxInstance = minInstance; - int maxPhase = 1; //Philips Multi-Phase + int maxPhase = 1; // Philips Multi-Phase for (int i = 0; i < nConvert; i++) { int vol = dcmList[dcmSort[i].indx].rawDataRunNumber; minVol = min(minVol, vol); @@ -3217,17 +3193,19 @@ bool ensureSequentialSlicePositions(int d3, int d4, struct TDCMsort dcmSort[], s printWarning("Volume number does not vary (0019,10A2; 0020,0100; 2005,1063; 2005,1413), assuming meaningful instance number (0020,0013).\n"); isUseInstanceNumberForVolume = true; } - bool isVerbose = (verbose > 1); //issue533 + bool isVerbose = (verbose > 1); // issue533 if (isVerbose) - printMessage("Ranges volume %d..%d instance %d..%d\n", minVol, maxVolNotADC, minInstance, maxInstance); //TODO + printMessage("Ranges volume %d..%d instance %d..%d\n", minVol, maxVolNotADC, minInstance, maxInstance); // TODO bool isASL = (dcmList[dcmSort[0].indx].aslFlags != kASL_FLAG_NONE); - //we will renumber volumes for Philips ASL (Contrast/Label, phase) and DWI (derived trace) + // we will renumber volumes for Philips ASL (Contrast/Label, phase) and DWI (derived trace) int minVolOut = kMaxDTI4D + 1; int maxVolOut = -1; bool isUsePhaseForVol = false; - if ((!isASL) && (minVol == maxVol) && (maxPhase > 1)) isUsePhaseForVol = true;//e.g. TurboQUASAR - bool isPhaseIsBValNumber =false; - if ((!isASL) && (minVol < maxVol) && (maxPhase > 1)) isPhaseIsBValNumber = true;//DWI track both gradient number and vector number issue 546 + if ((!isASL) && (minVol == maxVol) && (maxPhase > 1)) + isUsePhaseForVol = true; // e.g. TurboQUASAR + bool isPhaseIsBValNumber = false; + if ((!isASL) && (minVol < maxVol) && (maxPhase > 1)) + isPhaseIsBValNumber = true; // DWI track both gradient number and vector number issue 546 if (isVerbose) printMessage("InstanceNumber\tPosition\tVolume\tRepeat\tASLlabel\tPhase\tTriggerTime\n"); for (int i = 0; i < nConvert; i++) { @@ -3235,30 +3213,32 @@ bool ensureSequentialSlicePositions(int d3, int d4, struct TDCMsort dcmSort[], s int rawvol = vol; int instance = dcmList[dcmSort[i].indx].imageNum; int phase = max(1, dcmList[dcmSort[i].indx].phaseNumber); - int refTime = (int)dcmList[dcmSort[i].indx].frameReferenceTime; //issue577: refTime in ms, so int conversion sufficient - if (isUsePhaseForVol) vol = phase; - if (isPhaseIsBValNumber) vol += phase * maxVol; + int refTime = (int)dcmList[dcmSort[i].indx].frameReferenceTime; // issue577: refTime in ms, so int conversion sufficient + if (isUsePhaseForVol) + vol = phase; + if (isPhaseIsBValNumber) + vol += phase * maxVol; int isAslLabel = dcmList[dcmSort[i].indx].aslFlags == kASL_FLAG_PHILIPS_LABEL; float dx = intersliceDistanceSigned(dcmList[dcmSort[0].indx], dcmList[dcmSort[i].indx]); if (isASL) { - #ifdef myMatchEnhanced00209157 //issue533: make classic DICOMs match enhanced DICOM volume order - //disk order: slice < repeat < phase < label/control - vol += (phase - 1) * maxVol; - if (isAslLabel) - vol += maxPhase * maxVol; - #else - //"temporal" disk order: slice < phase < label/control < repeat : should match instance number - vol = phase; - if (isAslLabel) - vol += maxPhase; - vol += (rawvol - 1) * (2 * maxPhase); - #endif +#ifdef myMatchEnhanced00209157 // issue533: make classic DICOMs match enhanced DICOM volume order + // disk order: slice < repeat < phase < label/control + vol += (phase - 1) * maxVol; + if (isAslLabel) + vol += maxPhase * maxVol; +#else + //"temporal" disk order: slice < phase < label/control < repeat : should match instance number + vol = phase; + if (isAslLabel) + vol += maxPhase; + vol += (rawvol - 1) * (2 * maxPhase); +#endif } if (isUseInstanceNumberForVolume) vol = instance; - if (isVerbose) //only report slice data for logorrheic verbosity + if (isVerbose) // only report slice data for logorrheic verbosity printMessage("%d\t%g\t%d\t%d\t%d\t%d\t%g\n", instance, dx, vol, rawvol, isAslLabel, phase, dcmList[dcmSort[i].indx].triggerDelayTime); - if (vol > kMaxDTI4D) //issue529 Philips derived Trace/ADC embedded into DWI + if (vol > kMaxDTI4D) // issue529 Philips derived Trace/ADC embedded into DWI vol = maxVol + 1; if (isUseFrameReferenceTimeForVolume) vol = refTime; @@ -3268,15 +3248,15 @@ bool ensureSequentialSlicePositions(int d3, int d4, struct TDCMsort dcmSort[], s floatSort[i].position = dx; floatSort[i].index = i; } - //n.b. should we change dim[3] and dim[4] if number of volumes = dim[3]? + // n.b. should we change dim[3] and dim[4] if number of volumes = dim[3]? if (isUseFrameReferenceTimeForVolume) printWarning("Reordering volumes based on FrameReferenceTime (0054,1300; issue 577)\n"); - else if ((!isPhaseIsBValNumber) && ((maxVolOut-minVolOut+1) != d4)) + else if ((!isPhaseIsBValNumber) && ((maxVolOut - minVolOut + 1) != d4)) printError("Check sorted order: 4D dataset has %d volumes, but volume index ranges from %d..%d\n", d4, minVolOut, maxVolOut); TDCMsort *dcmSortIn = (TDCMsort *)malloc(nConvert * sizeof(TDCMsort)); for (int i = 0; i < nConvert; i++) dcmSortIn[i] = dcmSort[i]; - qsort(floatSort, nConvert, sizeof(struct TFloatSort), compareTFloatSort); //sort based on series and image numbers.... + qsort(floatSort, nConvert, sizeof(struct TFloatSort), compareTFloatSort); // sort based on series and image numbers.... for (int i = 0; i < nConvert; i++) dcmSort[i] = dcmSortIn[floatSort[i].index]; free(floatSort); @@ -3285,7 +3265,7 @@ bool ensureSequentialSlicePositions(int d3, int d4, struct TDCMsort dcmSort[], s } // ensureSequentialSlicePositions() void swapDim3Dim4(int d3, int d4, struct TDCMsort dcmSort[]) { - //swap space and time: input A0,A1...An,B0,B1...Bn output A0,B0,A1,B1,... + // swap space and time: input A0,A1...An,B0,B1...Bn output A0,B0,A1,B1,... int nConvert = d3 * d4; TDCMsort *dcmSortIn = (TDCMsort *)malloc(nConvert * sizeof(TDCMsort)); for (int i = 0; i < nConvert; i++) @@ -3294,23 +3274,23 @@ void swapDim3Dim4(int d3, int d4, struct TDCMsort dcmSort[]) { for (int b = 0; b < d3; b++) for (int a = 0; a < d4; a++) { int k = (a * d3) + b; - //printMessage("%d -> %d %d ->%d\n",i,a, b, k); + // printMessage("%d -> %d %d ->%d\n",i,a, b, k); dcmSort[k] = dcmSortIn[i]; i++; } free(dcmSortIn); -} //swapDim3Dim4() +} // swapDim3Dim4() bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata dcmList[]) { - //detect whether some DICOM images report different intensity scaling - //some Siemens PET scanners generate 16-bit images where slice has its own scaling factor. - // since NIfTI provides a single scaling factor for each file, these images require special consideration + // detect whether some DICOM images report different intensity scaling + // some Siemens PET scanners generate 16-bit images where slice has its own scaling factor. + // since NIfTI provides a single scaling factor for each file, these images require special consideration if (nConvert < 2) return false; int dt = dcmList[dcmSort[0].indx].bitsAllocated; float iScale = dcmList[dcmSort[0].indx].intenScale; float iInter = dcmList[dcmSort[0].indx].intenIntercept; - for (int i = 1; i < nConvert; i++) { //stack additional images + for (int i = 1; i < nConvert; i++) { // stack additional images uint64_t indx = dcmSort[i].indx; if (dcmList[indx].bitsAllocated != dt) return true; @@ -3320,7 +3300,7 @@ bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[], struct TDICOM return true; } return false; -} //intensityScaleVaries() +} // intensityScaleVaries() /*unsigned char * nii_bgr2rgb(unsigned char* bImg, struct nifti_1_header *hdr) { //DICOM planarappears to be BBB..B,GGG..G,RRR..R, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B @@ -3356,7 +3336,7 @@ void niiDeleteFnm(const char *outname, const char *ext) { } void niiDelete(const char *niiname) { - //for niiname "~/d/img" delete img.nii, img.bvec, img.bval, img.json + // for niiname "~/d/img" delete img.nii, img.bvec, img.bval, img.json niiDeleteFnm(niiname, ".nii"); niiDeleteFnm(niiname, ".nii.gz"); niiDeleteFnm(niiname, ".nrrd"); @@ -3387,13 +3367,13 @@ bool niiExists(const char *pathoutname) { if (is_fileexists(niiname)) return true; return false; -} //niiExists() +} // niiExists() #ifndef W_OK #define W_OK 2 /* write mode check */ #endif -int strcicmp(char const *a, char const *b) //case insensitive compare +int strcicmp(char const *a, char const *b) // case insensitive compare { for (;; a++, b++) { int d = tolower(*a) - tolower(*b); @@ -3407,7 +3387,7 @@ bool isExt(char *file_name, const char *ext) { if ((p_extension = strrchr(file_name, '.')) != NULL) if (strcicmp(p_extension, ext) == 0) return true; - //if(strcmp(p_extension,ext) == 0) return true; + // if(strcmp(p_extension,ext) == 0) return true; return false; } // isExt() @@ -3416,7 +3396,7 @@ void cleanISO8859(char *cString) { if (len < 1) return; for (int i = 0; i < len; i++) - //assume specificCharacterSet (0008,0005) is ISO_IR 100 http://en.wikipedia.org/wiki/ISO/IEC_8859-1 + // assume specificCharacterSet (0008,0005) is ISO_IR 100 http://en.wikipedia.org/wiki/ISO/IEC_8859-1 if (cString[i] < 1) { unsigned char c = (unsigned char)cString[i]; if ((c >= 192) && (c <= 198)) @@ -3475,11 +3455,11 @@ void heudiconvStrPth(char *cString) { if (cString[i] == '^') { cString[j++] = kPathSeparator; hasCaret = true; - } else if ((!hasCaret) && (cString[i] == '_') ) { + } else if ((!hasCaret) && (cString[i] == '_')) { cString[j++] = kPathSeparator; } else if (cString[i] != '-') { cString[j] = cString[i]; - j ++; + j++; } i++; } @@ -3495,7 +3475,7 @@ void heudiconvStr(char *cString) { while (i < len) { if (cString[i] != '-') { cString[j] = cString[i]; - j ++; + j++; } i++; } @@ -3503,7 +3483,8 @@ void heudiconvStr(char *cString) { } void mkDirs(char *pth) { - if (strlen(pth) < 1) return; + if (strlen(pth) < 1) + return; char newdir[2048] = {""}; for (size_t pos = 0; pos < strlen(pth); pos++) { if (pth[pos] == kPathSeparator) { @@ -3524,10 +3505,10 @@ void mkDirs(char *pth) { #else mkdir(newdir, 0700); #endif -} //mkDirs() +} // mkDirs() void createDummyBidsBoilerplate(char *pth, bool isFunc) { - //https://remi-gau.github.io/bids_cookbook/#starters + // https://remi-gau.github.io/bids_cookbook/#starters char pathSep[2] = {"a"}; pathSep[0] = kPathSeparator; char descfnm[PATH_MAX] = {""}; @@ -3545,8 +3526,8 @@ void createDummyBidsBoilerplate(char *pth, bool isFunc) { if (fp != NULL) fprintf(fp, readmePre); - fprintf(fp, kDCMdate); - fprintf(fp, readmePost); + fprintf(fp, kDCMdate); + fprintf(fp, readmePost); fclose(fp); } snprintf(descfnm + strlen(descfnm), PATH_MAX - strlen(descfnm), "%s", "dataset_description.json"); @@ -3558,7 +3539,7 @@ void createDummyBidsBoilerplate(char *pth, bool isFunc) { fclose(fp); } if (!isFunc) - return; //only functional data gets a task file + return; // only functional data gets a task file snprintf(taskfnm + strlen(taskfnm), PATH_MAX - strlen(taskfnm), "%s", "task-rest_bold.json"); if (!is_fileexists(taskfnm)) { FILE *fp = fopen(taskfnm, "w"); @@ -3567,7 +3548,6 @@ void createDummyBidsBoilerplate(char *pth, bool isFunc) { fprintf(fp, taskRest); fclose(fp); } - } int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts opts) { @@ -3576,10 +3556,10 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts strcpy(pth, opts.outdir); int w = access(pth, W_OK); if (w != 0) { - //should never happen except with "-b i": see kEXIT_OUTPUT_FOLDER_READ_ONLY for early termination - // with "-b i" the code below generates a warning but no files are created + // should never happen except with "-b i": see kEXIT_OUTPUT_FOLDER_READ_ONLY for early termination + // with "-b i" the code below generates a warning but no files are created if (getcwd(pth, sizeof(pth)) != NULL) { -#ifdef USE_CWD_IF_OUTDIR_NO_WRITE //optional: fall back to current working directory +#ifdef USE_CWD_IF_OUTDIR_NO_WRITE // optional: fall back to current working directory w = access(pth, W_OK); if (w != 0) { printError("You do not have write permissions for the directory %s\n", opts.outdir); @@ -3617,7 +3597,7 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts bool isCoilReported = false; bool isEchoReported = false; bool isSeriesReported = false; - //bool isAcquisitionReported = false; + // bool isAcquisitionReported = false; bool isImageNumReported = false; while (pos < strlen(inname)) { if (inname[pos] == '%') { @@ -3626,7 +3606,7 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts newstr[pos - start] = '\0'; strcat(outname, newstr); } - pos++; //extra increment: skip both % and following character + pos++; // extra increment: skip both % and following character char f = 'P'; if (pos < strlen(inname)) f = toupper(inname[pos]); @@ -3653,15 +3633,15 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts printWarning("hazardous (%%h) or volatile (%%v) bids naming experimental\n"); bool isReproin = (f == 'V'); if (isReproin) { - //https://dbic-handbook.readthedocs.io/en/latest/mri/reproin.html - //reproin convention is hard for one-pass, as `ses` may only be reported in one series in the session (e.g. localizer) - //printf("study %s\n", dcm.studyDescription); - //printf("series %s\n", dcm.seriesDescription); - //printf("id %s\n", dcm.patientID); + // https://dbic-handbook.readthedocs.io/en/latest/mri/reproin.html + // reproin convention is hard for one-pass, as `ses` may only be reported in one series in the session (e.g. localizer) + // printf("study %s\n", dcm.studyDescription); + // printf("series %s\n", dcm.seriesDescription); + // printf("id %s\n", dcm.patientID); snprintf(newstr, PATH_MAX, "%s", dcm.studyDescription); heudiconvStrPth(newstr); - if ((strlen(pth) > 0) && (pth[strlen(pth)-1] != kPathSeparator)) - strcat(pth, kFileSep);//kPathSeparator); + if ((strlen(pth) > 0) && (pth[strlen(pth) - 1] != kPathSeparator)) + strcat(pth, kFileSep); // kPathSeparator); strcat(pth, newstr); mkDirs(pth); strcpy(opts.bidsSubject, dcm.patientID); @@ -3687,7 +3667,7 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts strcat(outname, newstr); strcat(outname, "_"); strcat(outname, dcm.protocolName); - + } else { isAddNamePostFixes = false; strcat(outname, bidsSubject); @@ -3739,14 +3719,14 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts else if (dcm.manufacturer == kMANUFACTURER_HYPERFINE) strcat(outname, "Hy"); else - strcat(outname, "NA"); //manufacturer name not available + strcat(outname, "NA"); // manufacturer name not available } if (f == 'N') strcat(outname, dcm.patientName); if (f == 'O') { strcat(outname, dcm.instanceUID); if (strlen(dcm.instanceUID) > 0) - isAddNamePostFixes = false; //should be unique, so no need to post-fix + isAddNamePostFixes = false; // should be unique, so no need to post-fix } if (f == 'P') { strcat(outname, dcm.protocolName); @@ -3773,12 +3753,12 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts if (opts.isRenameNotConvert) { snprintf(newstr, PATH_MAX, "%d", dcm.acquNum); strcat(outname, newstr); - //isAcquisitionReported = true; + // isAcquisitionReported = true; } else { snprintf(newstr, PATH_MAX, "%d", dcm.acquNum); strcat(outname, newstr); #ifdef mySegmentByAcq - //isAcquisitionReported = true; + // isAcquisitionReported = true; #else printWarning("'%%u' in output filename can be misleading (issue 526)\n"); #endif @@ -3808,20 +3788,20 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts else strcat(outname, "NA"); } - if (f == 'W') {//Weird includes personal data in filename patientWeight + if (f == 'W') { // Weird includes personal data in filename patientWeight snprintf(newstr, PATH_MAX, "dob%sg%cwt%d", dcm.patientBirthDate, dcm.patientSex, (int)round(dcm.patientWeight)); if (strstr(dcm.institutionName, "Richland")) strcat(newstr, "R"); strcat(outname, newstr); } if ((f == 'Y') && (dcm.rawDataRunNumber >= 0)) { - snprintf(newstr, PATH_MAX, "%d", dcm.rawDataRunNumber); //GE (0019,10A2) else (0020,0100) + snprintf(newstr, PATH_MAX, "%d", dcm.rawDataRunNumber); // GE (0019,10A2) else (0020,0100) strcat(outname, newstr); } if (f == 'X') strcat(outname, dcm.studyID); if ((f == 'Y') && (dcm.rawDataRunNumber >= 0)) { - snprintf(newstr, PATH_MAX, "%d", dcm.rawDataRunNumber); //GE (0019,10A2) else (0020,0100) + snprintf(newstr, PATH_MAX, "%d", dcm.rawDataRunNumber); // GE (0019,10A2) else (0020,0100) strcat(outname, newstr); } if (f == 'Z') @@ -3845,23 +3825,23 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts if ((pos < strlen(inname)) && (toupper(inname[pos + 1]) == 'Y') && (dcm.rawDataRunNumber >= 0)) { char zeroPad[128] = {""}; snprintf(zeroPad, 128, "%%0%dd", f - '0'); - snprintf(newstr, PATH_MAX, zeroPad, dcm.rawDataRunNumber); //GE (0019,10A2) else (0020,0100) + snprintf(newstr, PATH_MAX, zeroPad, dcm.rawDataRunNumber); // GE (0019,10A2) else (0020,0100) strcat(outname, newstr); pos++; // e.g. %3f requires extra increment: skip both number and following character } } start = pos + 1; - } //found a % character + } // found a % character pos++; - } //for each character in input - if (pos > start) { //append any trailing characters + } // for each character in input + if (pos > start) { // append any trailing characters strncpy(&newstr[0], &inname[0] + start, pos - start); newstr[pos - start] = '\0'; strcat(outname, newstr); } if ((isAddNamePostFixes) && (!isCoilReported) && (dcm.isCoilVaries)) { - //snprintf(newstr, PATH_MAX, "_c%d", dcm.coilNum); - //strcat (outname,newstr); + // snprintf(newstr, PATH_MAX, "_c%d", dcm.coilNum); + // strcat (outname,newstr); strcat(outname, "_c"); strcat(outname, dcm.coilName); #ifdef USING_DCM2NIIXFSWRAPPER @@ -3874,12 +3854,12 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts } // myMultiEchoFilenameSkipEcho1 https://github.com/rordenlab/dcm2niix/issues/237 #ifdef myMultiEchoFilenameSkipEcho1 - if ((isAddNamePostFixes) && (!isEchoReported) && (dcm.isMultiEcho)) { //multiple echoes saved as same series + if ((isAddNamePostFixes) && (!isEchoReported) && (dcm.isMultiEcho)) { // multiple echoes saved as same series #else - if ((isAddNamePostFixes) && (!isEchoReported) && ((dcm.isMultiEcho) || (dcm.echoNum > 1))) { //multiple echoes saved as same series + if ((isAddNamePostFixes) && (!isEchoReported) && ((dcm.isMultiEcho) || (dcm.echoNum > 1))) { // multiple echoes saved as same series #endif if ((dcm.echoNum < 1) && (dcm.TE > 0)) - snprintf(newstr, PATH_MAX, "_e%g", dcm.TE); //issue568: Siemens XA20 might omit echo number + snprintf(newstr, PATH_MAX, "_e%g", dcm.TE); // issue568: Siemens XA20 might omit echo number else snprintf(newstr, PATH_MAX, "_e%d", dcm.echoNum); strcat(outname, newstr); @@ -3888,7 +3868,7 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts sprintf(mrifsStruct.namePostFixes, "%s%s", mrifsStruct.namePostFixes, newstr); #endif } - if ((isAddNamePostFixes) && (!isSeriesReported) && (!isEchoReported) && (dcm.echoNum > 1)) { //last resort: user provided no method to disambiguate echo number in filename + if ((isAddNamePostFixes) && (!isSeriesReported) && (!isEchoReported) && (dcm.echoNum > 1)) { // last resort: user provided no method to disambiguate echo number in filename snprintf(newstr, PATH_MAX, "_e%d", dcm.echoNum); strcat(outname, newstr); isEchoReported = true; @@ -3908,66 +3888,66 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts strcat (outname,newstr); }*/ if ((isAddNamePostFixes) && (dcm.isHasImaginary)) { - strcat(outname, "_imaginary"); //has phase map + strcat(outname, "_imaginary"); // has phase map #ifdef USING_DCM2NIIXFSWRAPPER sprintf(mrifsStruct.namePostFixes, "%s_imaginary", mrifsStruct.namePostFixes); #endif } if ((isAddNamePostFixes) && (dcm.isHasReal) && (dcm.isRealIsPhaseMapHz)) { - strcat(outname, "_fieldmaphz"); //has field map + strcat(outname, "_fieldmaphz"); // has field map #ifdef USING_DCM2NIIXFSWRAPPER sprintf(mrifsStruct.namePostFixes, "%s_fieldmaphz", mrifsStruct.namePostFixes); #endif } if ((isAddNamePostFixes) && (dcm.isHasReal) && (!dcm.isRealIsPhaseMapHz)) { - strcat(outname, "_real"); //has phase map + strcat(outname, "_real"); // has phase map #ifdef USING_DCM2NIIXFSWRAPPER sprintf(mrifsStruct.namePostFixes, "%s_real", mrifsStruct.namePostFixes); #endif } if ((isAddNamePostFixes) && (dcm.isHasPhase)) { - strcat(outname, "_ph"); //has phase map + strcat(outname, "_ph"); // has phase map if (dcm.isHasMagnitude) - strcat(outname, "Mag"); //Philips enhanced with BOTH phase and Magnitude in single file + strcat(outname, "Mag"); // Philips enhanced with BOTH phase and Magnitude in single file #ifdef USING_DCM2NIIXFSWRAPPER sprintf(mrifsStruct.namePostFixes, "%s_ph", mrifsStruct.namePostFixes); #endif } - if ((isAddNamePostFixes) && (dcm.aslFlags == kASL_FLAG_NONE) && (dcm.triggerDelayTime >= 1) && (dcm.manufacturer != kMANUFACTURER_GE)) { //issue 336 GE uses this for slice timing + if ((isAddNamePostFixes) && (dcm.aslFlags == kASL_FLAG_NONE) && (dcm.triggerDelayTime >= 1) && (dcm.manufacturer != kMANUFACTURER_GE)) { // issue 336 GE uses this for slice timing snprintf(newstr, PATH_MAX, "_t%d", (int)roundf(dcm.triggerDelayTime)); strcat(outname, newstr); #ifdef USING_DCM2NIIXFSWRAPPER sprintf(mrifsStruct.namePostFixes, "%s%s", mrifsStruct.namePostFixes, newstr); #endif } - //could add (isAddNamePostFixes) to these next two, but consequences could be catastrophic - if (dcm.isRawDataStorage) //avoid name clash for Philips XX_ files + // could add (isAddNamePostFixes) to these next two, but consequences could be catastrophic + if (dcm.isRawDataStorage) // avoid name clash for Philips XX_ files strcat(outname, "_Raw"); - if (dcm.isGrayscaleSoftcopyPresentationState) //avoid name clash for Philips PS_ files + if (dcm.isGrayscaleSoftcopyPresentationState) // avoid name clash for Philips PS_ files strcat(outname, "_PS"); if (isDcmExt) strcat(outname, ".dcm"); if (strlen(outname) < 1) strcpy(outname, "dcm2nii_invalidName"); if (outname[0] == '.') - outname[0] = '_'; //make sure not a hidden file -//eliminate illegal characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx -// https://github.com/rordenlab/dcm2niix/issues/237 + outname[0] = '_'; // make sure not a hidden file +// eliminate illegal characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx +// https://github.com/rordenlab/dcm2niix/issues/237 #ifdef myOsSpecificFilenameMask #define kMASK_WINDOWS_SPECIAL_CHARACTERS 0 #else #define kMASK_WINDOWS_SPECIAL_CHARACTERS 1 #endif -#if defined(_WIN64) || defined(_WIN32) || defined(kMASK_WINDOWS_SPECIAL_CHARACTERS) //https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names +#if defined(_WIN64) || defined(_WIN32) || defined(kMASK_WINDOWS_SPECIAL_CHARACTERS) // https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names for (size_t pos = 0; pos < strlen(outname); pos++) if ((outname[pos] == '\\') || (outname[pos] == '/') || (outname[pos] == ' ') || (outname[pos] == '<') || (outname[pos] == '>') || (outname[pos] == ':') || (outname[pos] == ';') || (outname[pos] == '"') // || (outname[pos] == '/') || (outname[pos] == '\\') - //|| (outname[pos] == '^') issue398 - || (outname[pos] == '$') //issue749 + //|| (outname[pos] == '^') issue398 + || (outname[pos] == '$') // issue749 || (outname[pos] == '*') || (outname[pos] == '|') || (outname[pos] == '?')) outname[pos] = '_'; #else for (size_t pos = 0; pos < strlen(outname); pos++) - if (outname[pos] == ':') //not allowed by MacOS + if (outname[pos] == ':') // not allowed by MacOS outname[pos] = '_'; #endif #if !defined(_WIN64) || !defined(_WIN32) @@ -3976,11 +3956,11 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts outname[pos] = '_'; #endif cleanISO8859(outname); - //re-insert explicit path separators: -f %t/%s_%p will have folder for time, but will not segment a protocol named "fMRI\bold" + // re-insert explicit path separators: -f %t/%s_%p will have folder for time, but will not segment a protocol named "fMRI\bold" for (int pos = 0; pos < (int)strlen(outname); pos++) { if (outname[pos] == kTempPathSeparator) - outname[pos] = kPathSeparator; //e.g. for Windows, convert "/" to "\" - if (outname[pos] < 32) //https://en.wikipedia.org/wiki/ASCII#Control_characters + outname[pos] = kPathSeparator; // e.g. for Windows, convert "/" to "\" + if (outname[pos] < 32) // https://en.wikipedia.org/wiki/ASCII#Control_characters outname[pos] = '_'; } char baseoutname[2048] = {""}; @@ -3989,7 +3969,7 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts appendChar[0] = kPathSeparator; if ((strlen(pth) > 0) && (pth[strlen(pth) - 1] != kPathSeparator) && (outname[0] != kPathSeparator)) strcat(baseoutname, appendChar); - //remove redundant underscores + // remove redundant underscores int len = strlen(outname); int outpos = 0; for (int inpos = 0; inpos < len; inpos++) { @@ -3999,8 +3979,8 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts outpos++; } outname[outpos] = 0; - //Allow user to specify new folders, e.g. "-f dir/%p" or "-f %s/%p/%m" - // These folders are created if they do not exist + // Allow user to specify new folders, e.g. "-f dir/%p" or "-f %s/%p/%m" + // These folders are created if they do not exist char *sep = strchr(outname, kPathSeparator); #if defined(USING_R) && (defined(_WIN64) || defined(_WIN32)) // R also uses forward slash on Windows, so allow it here @@ -4010,10 +3990,10 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts if (sep) { char newdir[2048] = {""}; strcat(newdir, baseoutname); - //struct stat st = {0}; + // struct stat st = {0}; for (size_t pos = 0; pos < strlen(outname); pos++) { if (outname[pos] == kPathSeparator) { - //if (stat(newdir, &st) == -1) + // if (stat(newdir, &st) == -1) if (!is_dir(newdir, true)) #if defined(_WIN64) || defined(_WIN32) mkdir(newdir); @@ -4026,8 +4006,8 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts strcat(newdir, ch); } } - //printMessage("path='%s' name='%s'\n", pathoutname, outname); - //make sure outname is unique + // printMessage("path='%s' name='%s'\n", pathoutname, outname); + // make sure outname is unique strcat(baseoutname, outname); char pathoutname[2048] = {""}; strcat(pathoutname, baseoutname); @@ -4052,14 +4032,14 @@ int nii_createFilename(struct TDICOMdata dcm, char *niiFilename, struct TDCMopts printError("Too many NIFTI images with the name %s\n", baseoutname); return EXIT_FAILURE; } - //printMessage("-->%s\n",pathoutname); return EXIT_SUCCESS; - //printMessage("outname=%s\n", pathoutname); + // printMessage("-->%s\n",pathoutname); return EXIT_SUCCESS; + // printMessage("outname=%s\n", pathoutname); strcpy(niiFilename, pathoutname); return EXIT_SUCCESS; -} //nii_createFilename() +} // nii_createFilename() void nii_createDummyFilename(char *niiFilename, struct TDCMopts opts) { - //generate string that illustrating sample of filename + // generate string that illustrating sample of filename struct TDICOMdata d = clear_dicom_data(); strcpy(d.patientName, "John_Doe"); strcpy(d.patientID, "ID123"); @@ -4093,12 +4073,12 @@ void nii_createDummyFilename(char *niiFilename, struct TDCMopts opts) { strcat(niiFilename, ".nhdr'"); else strcat(niiFilename, ".nrrd'"); - #ifdef myEnableJNIFTI +#ifdef myEnableJNIFTI } else if (opts.saveFormat == kSaveFormatJNII) { strcat(niiFilename, ".jnii'"); } else if (opts.saveFormat == kSaveFormatBNII) { strcat(niiFilename, ".bnii'"); - #endif +#endif } else { if (opts.isGz) strcat(niiFilename, ".nii.gz'"); @@ -4119,7 +4099,7 @@ unsigned long mz_crc32(unsigned long crc, const unsigned char *ptr, size_t buf_l } #endif -#ifndef MZ_UBER_COMPRESSION //defined in miniz, not defined in zlib +#ifndef MZ_UBER_COMPRESSION // defined in miniz, not defined in zlib #define MZ_UBER_COMPRESSION 9 #endif @@ -4128,13 +4108,13 @@ unsigned long mz_crc32(unsigned long crc, const unsigned char *ptr, size_t buf_l #endif void writeNiiGz(char *baseName, struct nifti_1_header hdr, unsigned char *src_buffer, unsigned long src_len, int gzLevel, bool isSkipHeader) { - //create gz file in RAM, save to disk http://www.zlib.net/zlib_how.html - // in general this single-threaded approach is slower than PIGZ but is useful for slow (network attached) disk drives + // create gz file in RAM, save to disk http://www.zlib.net/zlib_how.html + // in general this single-threaded approach is slower than PIGZ but is useful for slow (network attached) disk drives char fname[2048] = {""}; strcpy(fname, baseName); if (!isSkipHeader) strcat(fname, ".nii.gz"); - unsigned long hdrPadBytes = sizeof(hdr) + 4; //348 byte header + 4 byte pad + unsigned long hdrPadBytes = sizeof(hdr) + 4; // 348 byte header + 4 byte pad if (isSkipHeader) hdrPadBytes = 0; unsigned long cmp_len = mz_compressBound(src_len + hdrPadBytes); @@ -4147,7 +4127,7 @@ void writeNiiGz(char *baseName, struct nifti_1_header hdr, unsigned char *src_bu strm.opaque = Z_NULL; strm.next_out = pCmp; // output char array strm.avail_out = (unsigned int)cmp_len; // size of output - int zLevel = MZ_DEFAULT_LEVEL; //Z_DEFAULT_COMPRESSION; + int zLevel = MZ_DEFAULT_LEVEL; // Z_DEFAULT_COMPRESSION; if ((gzLevel > 0) && (gzLevel < 11)) zLevel = gzLevel; if (zLevel > MZ_UBER_COMPRESSION) @@ -4158,7 +4138,7 @@ void writeNiiGz(char *baseName, struct nifti_1_header hdr, unsigned char *src_bu } unsigned char *pHdr; if (!isSkipHeader) { - //add header + // add header pHdr = (unsigned char *)malloc(hdrPadBytes); pHdr[hdrPadBytes - 1] = 0; pHdr[hdrPadBytes - 2] = 0; @@ -4166,14 +4146,14 @@ void writeNiiGz(char *baseName, struct nifti_1_header hdr, unsigned char *src_bu pHdr[hdrPadBytes - 4] = 0; memcpy(pHdr, &hdr, sizeof(hdr)); strm.avail_in = (unsigned int)hdrPadBytes; // size of input - strm.next_in = (uint8_t *)pHdr; // input header -- TPX strm.next_in = (Bytef *)pHdr; uint32_t + strm.next_in = (uint8_t *)pHdr; // input header -- TPX strm.next_in = (Bytef *)pHdr; uint32_t deflate(&strm, Z_NO_FLUSH); } - //add image + // add image strm.avail_in = (unsigned int)src_len; // size of input - strm.next_in = (uint8_t *)src_buffer; // input image -- TPX strm.next_in = (Bytef *)src_buffer; - deflate(&strm, Z_FINISH); //Z_NO_FLUSH; - //finish up + strm.next_in = (uint8_t *)src_buffer; // input image -- TPX strm.next_in = (Bytef *)src_buffer; + deflate(&strm, Z_FINISH); // Z_NO_FLUSH; + // finish up deflateEnd(&strm); unsigned long file_crc32 = mz_crc32(0L, Z_NULL, 0); if (!isSkipHeader) @@ -4191,20 +4171,20 @@ void writeNiiGz(char *baseName, struct nifti_1_header hdr, unsigned char *src_bu free(src_buffer); return; } - //write header http://www.gzip.org/zlib/rfc-gzip.html - fputc((char)0x1f, fileGz); //ID1 - fputc((char)0x8b, fileGz); //ID2 - fputc((char)0x08, fileGz); //CM - use deflate compression method - fputc((char)0x00, fileGz); //FLG - no addition fields - fputc((char)0x00, fileGz); //MTIME0 - fputc((char)0x00, fileGz); //MTIME1 - fputc((char)0x00, fileGz); //MTIME2 - fputc((char)0x00, fileGz); //MTIME2 - fputc((char)0x00, fileGz); //XFL - fputc((char)0xff, fileGz); //OS - //write Z-compressed data + // write header http://www.gzip.org/zlib/rfc-gzip.html + fputc((char)0x1f, fileGz); // ID1 + fputc((char)0x8b, fileGz); // ID2 + fputc((char)0x08, fileGz); // CM - use deflate compression method + fputc((char)0x00, fileGz); // FLG - no addition fields + fputc((char)0x00, fileGz); // MTIME0 + fputc((char)0x00, fileGz); // MTIME1 + fputc((char)0x00, fileGz); // MTIME2 + fputc((char)0x00, fileGz); // MTIME2 + fputc((char)0x00, fileGz); // XFL + fputc((char)0xff, fileGz); // OS + // write Z-compressed data fwrite(&pCmp[2], sizeof(char), cmp_len - 6, fileGz); //-6 as LZ78 format has 2 bytes header (typically 0x789C) and 4 bytes tail (ADLER 32) - //write tail: write redundancy check and uncompressed size as bytes to ensure LITTLE-ENDIAN order + // write tail: write redundancy check and uncompressed size as bytes to ensure LITTLE-ENDIAN order fputc((unsigned char)(file_crc32), fileGz); fputc((unsigned char)(file_crc32 >> 8), fileGz); fputc((unsigned char)(file_crc32 >> 16), fileGz); @@ -4217,13 +4197,13 @@ void writeNiiGz(char *baseName, struct nifti_1_header hdr, unsigned char *src_bu free(pCmp); if (!isSkipHeader) free(pHdr); -} //writeNiiGz() +} // writeNiiGz() #endif #ifndef USING_R int pigz_File(char *fname, struct TDCMopts opts, size_t imgsz) { - //given "/dir/file.nii" creates "/dir/file.nii.gz" + // given "/dir/file.nii" creates "/dir/file.nii.gz" char blockSize[768]; strcpy(blockSize, ""); //-b 960 increases block size from 128 to 960: each block has 32kb lead in... so less redundancy @@ -4235,36 +4215,36 @@ int pigz_File(char *fname, struct TDCMopts opts, size_t imgsz) { if ((opts.gzLevel > 0) && (opts.gzLevel < 12)) { char newstr[256]; snprintf(newstr, 256, "\"%s -n -f -%d \"", blockSize, opts.gzLevel); - //749 snprintf(newstr, 256, "\"%s -n -f -%d '", blockSize, opts.gzLevel); + // 749 snprintf(newstr, 256, "\"%s -n -f -%d '", blockSize, opts.gzLevel); strcat(command, newstr); } else { char newstr[256]; snprintf(newstr, 256, "\"%s -n \"", blockSize); - //749 snprintf(newstr, 256, "\"%s -n '", blockSize); + // 749 snprintf(newstr, 256, "\"%s -n '", blockSize); strcat(command, newstr); } strcat(command, fname); - //issue749 use single quote to prevent expansion of $ - //749 strcat(command, "'"); //add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"' - strcat(command, "\""); //add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"' -#if defined(_WIN64) || defined(_WIN32) //using CreateProcess instead of system to run in background (avoids screen flicker) + // issue749 use single quote to prevent expansion of $ + // 749 strcat(command, "'"); //add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"' + strcat(command, "\""); // add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"' +#if defined(_WIN64) || defined(_WIN32) // using CreateProcess instead of system to run in background (avoids screen flicker) DWORD exitCode; PROCESS_INFORMATION ProcessInfo = {0}; STARTUPINFO startupInfo = {0}; startupInfo.cb = sizeof(startupInfo); - //StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field + // StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field if (CreateProcess(NULL, command, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL, &startupInfo, &ProcessInfo)) { - //printMessage("compression --- %s\n",command); + // printMessage("compression --- %s\n",command); WaitForSingleObject(ProcessInfo.hProcess, INFINITE); CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); } else printMessage("Compression failed %s\n", command); -#else //if win else linux +#else // if win else linux int ret = system(command); if (ret == -1) printWarning("Failed to execute: %s\n", command); -#endif //else linux +#endif // else linux printMessage("Compress: %s\n", command); return EXIT_SUCCESS; } // pigz_File() @@ -4278,22 +4258,24 @@ int pigz_File(char *fname, struct TDCMopts opts, size_t imgsz) { #endif PACKD(typedef struct { - int32_t version, width,height,depth,nframes,type,dof; + int32_t version, width, height, depth, nframes, type, dof; int16_t goodRASFlag; - float spacingX,spacingY,spacingZ,xr,xa,xs,yr,ya,ys,zr,za,zs,cr,ca,cs; + float spacingX, spacingY, spacingZ, xr, xa, xs, yr, ya, ys, zr, za, zs, cr, ca, cs; int16_t pad[kMGHpad]; -}) Tmgh; +}) +Tmgh; PACKD(typedef struct { float TR, FlipAngle, TE, TI; -}) TmghFooter; +}) +TmghFooter; void writeMghGz(char *baseName, Tmgh hdr, TmghFooter footer, unsigned char *src_buffer, unsigned long src_len, int gzLevel) { - //create gz file in RAM, save to disk http://www.zlib.net/zlib_how.html - // in general this single-threaded approach is slower than PIGZ but is useful for slow (network attached) disk drives + // create gz file in RAM, save to disk http://www.zlib.net/zlib_how.html + // in general this single-threaded approach is slower than PIGZ but is useful for slow (network attached) disk drives char fname[2048] = {""}; strcpy(fname, baseName); - unsigned long hdrPadBytes = sizeof(hdr); //348 byte header + 4 byte pad + unsigned long hdrPadBytes = sizeof(hdr); // 348 byte header + 4 byte pad unsigned long cmp_len = mz_compressBound(src_len + hdrPadBytes); unsigned char *pCmp = (unsigned char *)malloc(cmp_len); z_stream strm; @@ -4304,7 +4286,7 @@ void writeMghGz(char *baseName, Tmgh hdr, TmghFooter footer, unsigned char *src_ strm.opaque = Z_NULL; strm.next_out = pCmp; // output char array strm.avail_out = (unsigned int)cmp_len; // size of output - int zLevel = MZ_DEFAULT_LEVEL; //Z_DEFAULT_COMPRESSION; + int zLevel = MZ_DEFAULT_LEVEL; // Z_DEFAULT_COMPRESSION; if ((gzLevel > 0) && (gzLevel < 11)) zLevel = gzLevel; if (zLevel > MZ_UBER_COMPRESSION) @@ -4313,24 +4295,24 @@ void writeMghGz(char *baseName, Tmgh hdr, TmghFooter footer, unsigned char *src_ free(pCmp); return; } - //add header + // add header strm.avail_in = (unsigned int)sizeof(hdr); // size of input - strm.next_in = (uint8_t *) &hdr.version; + strm.next_in = (uint8_t *)&hdr.version; deflate(&strm, Z_NO_FLUSH); - //add image + // add image strm.avail_in = (unsigned int)src_len; // size of input - strm.next_in = (uint8_t *)src_buffer; // input image -- TPX strm.next_in = (Bytef *)src_buffer; + strm.next_in = (uint8_t *)src_buffer; // input image -- TPX strm.next_in = (Bytef *)src_buffer; deflate(&strm, Z_FINISH); - //add footer + // add footer strm.avail_in = (unsigned int)sizeof(footer); // size of input - strm.next_in = (uint8_t *) &footer.TR; + strm.next_in = (uint8_t *)&footer.TR; deflate(&strm, Z_NO_FLUSH); - //finish up + // finish up deflateEnd(&strm); unsigned long file_crc32 = mz_crc32(0L, Z_NULL, 0); - file_crc32 = mz_crc32(file_crc32, (uint8_t *) &hdr.version, (unsigned int)sizeof(hdr)); + file_crc32 = mz_crc32(file_crc32, (uint8_t *)&hdr.version, (unsigned int)sizeof(hdr)); file_crc32 = mz_crc32(file_crc32, src_buffer, (unsigned int)src_len); - file_crc32 = mz_crc32(file_crc32, (uint8_t *) &footer.TR, (unsigned int)sizeof(footer)); + file_crc32 = mz_crc32(file_crc32, (uint8_t *)&footer.TR, (unsigned int)sizeof(footer)); cmp_len = strm.total_out; if (cmp_len <= 0) { free(pCmp); @@ -4343,20 +4325,20 @@ void writeMghGz(char *baseName, Tmgh hdr, TmghFooter footer, unsigned char *src_ free(src_buffer); return; } - //write header http://www.gzip.org/zlib/rfc-gzip.html - fputc((char)0x1f, fileGz); //ID1 - fputc((char)0x8b, fileGz); //ID2 - fputc((char)0x08, fileGz); //CM - use deflate compression method - fputc((char)0x00, fileGz); //FLG - no addition fields - fputc((char)0x00, fileGz); //MTIME0 - fputc((char)0x00, fileGz); //MTIME1 - fputc((char)0x00, fileGz); //MTIME2 - fputc((char)0x00, fileGz); //MTIME2 - fputc((char)0x00, fileGz); //XFL - fputc((char)0xff, fileGz); //OS - //write Z-compressed data + // write header http://www.gzip.org/zlib/rfc-gzip.html + fputc((char)0x1f, fileGz); // ID1 + fputc((char)0x8b, fileGz); // ID2 + fputc((char)0x08, fileGz); // CM - use deflate compression method + fputc((char)0x00, fileGz); // FLG - no addition fields + fputc((char)0x00, fileGz); // MTIME0 + fputc((char)0x00, fileGz); // MTIME1 + fputc((char)0x00, fileGz); // MTIME2 + fputc((char)0x00, fileGz); // MTIME2 + fputc((char)0x00, fileGz); // XFL + fputc((char)0xff, fileGz); // OS + // write Z-compressed data fwrite(&pCmp[2], sizeof(char), cmp_len - 6, fileGz); //-6 as LZ78 format has 2 bytes header (typically 0x789C) and 4 bytes tail (ADLER 32) - //write tail: write redundancy check and uncompressed size as bytes to ensure LITTLE-ENDIAN order + // write tail: write redundancy check and uncompressed size as bytes to ensure LITTLE-ENDIAN order fputc((unsigned char)(file_crc32), fileGz); fputc((unsigned char)(file_crc32 >> 8), fileGz); fputc((unsigned char)(file_crc32 >> 16), fileGz); @@ -4367,13 +4349,13 @@ void writeMghGz(char *baseName, Tmgh hdr, TmghFooter footer, unsigned char *src_ fputc((unsigned char)(strm.total_in >> 24), fileGz); fclose(fileGz); free(pCmp); -} //writeMghGz() +} // writeMghGz() int nii_saveMGH(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, struct TDCMopts opts, struct TDICOMdata d, struct TDTI4D *dti4D, int numDTI) { -// FreeeSurfer does not use a permissive license, so we must reverse engineer code -// https://surfer.nmr.mgh.harvard.edu/fswiki/FsTutorial/MghFormat + // FreeeSurfer does not use a permissive license, so we must reverse engineer code + // https://surfer.nmr.mgh.harvard.edu/fswiki/FsTutorial/MghFormat int nDim = hdr.dim[0]; - //printMessage("NRRD writer is experimental\n"); + // printMessage("NRRD writer is experimental\n"); if (nDim < 1) return EXIT_FAILURE; bool isGz = opts.isGz; @@ -4382,22 +4364,22 @@ int nii_saveMGH(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, printWarning("Saving huge image uncompressed (many GZip tools have 2 Gb limit).\n"); isGz = false; } - //fill the footer + // fill the footer TmghFooter footer; footer.TR = d.TR; footer.FlipAngle = d.flipAngle; footer.TE = d.TE; footer.TI = d.TI; - #ifdef __LITTLE_ENDIAN__ //mgh data ALWAYS big endian! - nifti_swap_4bytes(4, &footer.TR); - #endif - //fill the header +#ifdef __LITTLE_ENDIAN__ // mgh data ALWAYS big endian! + nifti_swap_4bytes(4, &footer.TR); +#endif + // fill the header Tmgh mgh; mgh.version = 1; mgh.width = hdr.dim[1]; mgh.height = hdr.dim[2]; mgh.depth = hdr.dim[3]; - mgh.nframes = max(hdr.dim[4],1); + mgh.nframes = max(hdr.dim[4], 1); if (hdr.datatype == DT_UINT8) mgh.type = 0; else if (hdr.datatype == DT_INT16) @@ -4415,10 +4397,13 @@ int nii_saveMGH(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, float xmm = hdr.pixdim[1]; float ymm = hdr.pixdim[2]; float zmm = hdr.pixdim[3]; - //avoid divide by zero errors: - if (xmm <= 0.0) xmm = 1.0; - if (ymm <= 0.0) ymm = 1.0; - if (zmm <= 0.0) zmm = 1.0; + // avoid divide by zero errors: + if (xmm <= 0.0) + xmm = 1.0; + if (ymm <= 0.0) + ymm = 1.0; + if (zmm <= 0.0) + zmm = 1.0; mgh.spacingX = xmm; mgh.spacingY = ymm; mgh.spacingZ = zmm; @@ -4435,22 +4420,22 @@ int nii_saveMGH(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, vec[0] = hdr.dim[1] * 0.5; vec[1] = hdr.dim[2] * 0.5; vec[2] = hdr.dim[3] * 0.5; - mgh.cr = hdr.srow_x[0]*vec[0] + hdr.srow_x[1]*vec[1] + hdr.srow_x[2]*vec[2] + hdr.srow_x[3]; - mgh.ca = hdr.srow_y[0]*vec[0] + hdr.srow_y[1]*vec[1] + hdr.srow_y[2]*vec[2] + hdr.srow_y[3]; - mgh.cs = hdr.srow_z[0]*vec[0] + hdr.srow_z[1]*vec[1] + hdr.srow_z[2]*vec[2] + hdr.srow_z[3]; - #ifdef __LITTLE_ENDIAN__ //mgh data ALWAYS big endian! - nifti_swap_4bytes(7, &mgh.version); - nifti_swap_2bytes(1, &mgh.goodRASFlag); - nifti_swap_4bytes(15, &mgh.spacingX); - #endif + mgh.cr = hdr.srow_x[0] * vec[0] + hdr.srow_x[1] * vec[1] + hdr.srow_x[2] * vec[2] + hdr.srow_x[3]; + mgh.ca = hdr.srow_y[0] * vec[0] + hdr.srow_y[1] * vec[1] + hdr.srow_y[2] * vec[2] + hdr.srow_y[3]; + mgh.cs = hdr.srow_z[0] * vec[0] + hdr.srow_z[1] * vec[1] + hdr.srow_z[2] * vec[2] + hdr.srow_z[3]; +#ifdef __LITTLE_ENDIAN__ // mgh data ALWAYS big endian! + nifti_swap_4bytes(7, &mgh.version); + nifti_swap_2bytes(1, &mgh.goodRASFlag); + nifti_swap_4bytes(15, &mgh.spacingX); +#endif for (int i = 0; i < kMGHpad; i++) mgh.pad[i] = 0; - //write the data + // write the data char fname[2048] = {""}; strcpy(fname, niiFilename); - #ifdef __LITTLE_ENDIAN__ //mgh data ALWAYS big endian! - swapEndian(&hdr, im, true); //byte-swap endian (e.g. little->big) - #endif +#ifdef __LITTLE_ENDIAN__ // mgh data ALWAYS big endian! + swapEndian(&hdr, im, true); // byte-swap endian (e.g. little->big) +#endif if (isGz) { strcat(fname, ".mgz"); writeMghGz(fname, mgh, footer, im, imgsz, opts.gzLevel); @@ -4464,15 +4449,15 @@ int nii_saveMGH(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, fwrite(&footer, sizeof(TmghFooter), 1, fp); fclose(fp); } - #ifdef __LITTLE_ENDIAN__ //mgh data ALWAYS big endian! - swapEndian(&hdr, im, false); //byte-swap endian (e.g. little->big) - #endif +#ifdef __LITTLE_ENDIAN__ // mgh data ALWAYS big endian! + swapEndian(&hdr, im, false); // byte-swap endian (e.g. little->big) +#endif return EXIT_SUCCESS; } // nii_saveMGH() int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, struct TDCMopts opts, struct TDICOMdata d, struct TDTI4D *dti4D, int numDTI) { int n, nDim = hdr.dim[0]; - //printMessage("NRRD writer is experimental\n"); + // printMessage("NRRD writer is experimental\n"); if (nDim < 1) return EXIT_FAILURE; bool isGz = opts.isGz; @@ -4484,16 +4469,16 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im char fname[2048] = {""}; strcpy(fname, niiFilename); if (isGz) - strcat(fname, ".nhdr"); //nrrd or nhdr + strcat(fname, ".nhdr"); // nrrd or nhdr else - strcat(fname, ".nrrd"); //nrrd or nhdr + strcat(fname, ".nrrd"); // nrrd or nhdr FILE *fp = fopen(fname, "wb"); fprintf(fp, "NRRD0005\n"); fprintf(fp, "# Complete NRRD file format specification at:\n"); fprintf(fp, "# http://teem.sourceforge.net/nrrd/format.html\n"); fprintf(fp, "# dcm2niix %s NRRD export transforms by Tashrif Billah\n", kDCMdate); char rgbNoneStr[10] = {""}; - //type tag + // type tag switch (hdr.datatype) { case DT_RGB24: fprintf(fp, "type: uint8\n"); @@ -4522,21 +4507,21 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im fclose(fp); return EXIT_FAILURE; } - //dimension tag + // dimension tag if (hdr.datatype == DT_RGB24) - fprintf(fp, "dimension: %d\n", nDim + 1); //RGB is first dimension + fprintf(fp, "dimension: %d\n", nDim + 1); // RGB is first dimension else fprintf(fp, "dimension: %d\n", nDim); - //space tag + // space tag fprintf(fp, "space: right-anterior-superior\n"); - //sizes tag + // sizes tag fprintf(fp, "sizes:"); if (hdr.datatype == DT_RGB24) fprintf(fp, " 3"); for (int i = 1; i <= hdr.dim[0]; i++) fprintf(fp, " %d", hdr.dim[i]); fprintf(fp, "\n"); - //thicknesses + // thicknesses if ((d.zThick > 0.0) && (nDim >= 3)) { fprintf(fp, "thicknesses: NaN NaN %g", d.zThick); int n = 3; @@ -4546,8 +4531,8 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im } fprintf(fp, "\n"); } - //byteskip only for .nhdr, not .nrrd - if (littleEndianPlatform()) //raw data in native format + // byteskip only for .nhdr, not .nrrd + if (littleEndianPlatform()) // raw data in native format fprintf(fp, "endian: little\n"); else fprintf(fp, "endian: big\n"); @@ -4561,9 +4546,9 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im } else fprintf(fp, "encoding: raw\n"); fprintf(fp, "space units: \"mm\" \"mm\" \"mm\"\n"); - //origin + // origin fprintf(fp, "space origin: (%g,%g,%g)\n", hdr.srow_x[3], hdr.srow_y[3], hdr.srow_z[3]); - //space directions: + // space directions: fprintf(fp, "space directions:%s (%g,%g,%g) (%g,%g,%g) (%g,%g,%g)", rgbNoneStr, hdr.srow_x[0], hdr.srow_y[0], hdr.srow_z[0], hdr.srow_x[1], hdr.srow_y[1], hdr.srow_z[1], hdr.srow_x[2], hdr.srow_y[2], hdr.srow_z[2]); n = 3; @@ -4572,36 +4557,36 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im n++; } fprintf(fp, "\n"); - //centerings tag + // centerings tag if (hdr.dim[0] < 4) //*check RGB, more dims fprintf(fp, "centerings:%s cell cell cell\n", rgbNoneStr); else fprintf(fp, "centerings:%s cell cell cell ???\n", rgbNoneStr); - //kinds tag + // kinds tag fprintf(fp, "kinds:"); if (hdr.datatype == DT_RGB24) fprintf(fp, " RGB-color"); n = 0; while ((n < nDim) && (n < 3)) { - fprintf(fp, " space"); //dims 1..3 + fprintf(fp, " space"); // dims 1..3 n++; } while (n < nDim) { - fprintf(fp, " list"); //dims 4..7 + fprintf(fp, " list"); // dims 4..7 n++; } fprintf(fp, "\n"); - //http://teem.sourceforge.net/nrrd/format.html + // http://teem.sourceforge.net/nrrd/format.html bool isFloat = (hdr.datatype == DT_FLOAT64) || (hdr.datatype == DT_FLOAT32); if (((!isSameFloat(hdr.scl_inter, 0.0)) || (!isSameFloat(hdr.scl_slope, 1.0))) && (!isFloat)) { - //http://teem.sourceforge.net/nrrd/format.html - double dtMin = 0.0; //DT_UINT8, DT_RGB24, DT_UINT16 + // http://teem.sourceforge.net/nrrd/format.html + double dtMin = 0.0; // DT_UINT8, DT_RGB24, DT_UINT16 if (hdr.datatype == DT_INT16) dtMin = -32768.0; if (hdr.datatype == DT_INT32) dtMin = -2147483648.0; fprintf(fp, "oldmin: %8.8f\n", (dtMin * hdr.scl_slope) + hdr.scl_inter); - double dtMax = 255.00; //DT_UINT8, DT_RGB24 + double dtMax = 255.00; // DT_UINT8, DT_RGB24 if (hdr.datatype == DT_INT16) dtMax = 32767.0; if (hdr.datatype == DT_UINT16) @@ -4610,7 +4595,7 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im dtMax = 2147483647.0; fprintf(fp, "oldmax: %8.8f\n", (dtMax * hdr.scl_slope) + hdr.scl_inter); } - //Slicer DWIconvert values + // Slicer DWIconvert values if (d.modality == kMODALITY_MR) fprintf(fp, "DICOM_0008_0060_Modality:=MR\n"); if (d.modality == kMODALITY_CT) @@ -4629,7 +4614,7 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im fprintf(fp, "DICOM_0018_0023_MRAcquisitionType:=2D\n"); if (d.is3DAcq) fprintf(fp, "DICOM_0018_0023_MRAcquisitionType:=3D\n"); - //if (strlen(d.mrAcquisitionType) > 0) fprintf(fp,"DICOM_0018_0023_MRAcquisitionType:=%s\n",d.mrAcquisitionType); + // if (strlen(d.mrAcquisitionType) > 0) fprintf(fp,"DICOM_0018_0023_MRAcquisitionType:=%s\n",d.mrAcquisitionType); if (d.TR > 0.0) fprintf(fp, "DICOM_0018_0080_RepetitionTime:=%g\n", d.TR); if ((d.TE > 0.0) && (!d.isXRay)) @@ -4644,13 +4629,13 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im fprintf(fp, "DICOM_0018_1020_SoftwareVersions:=%s\n", d.softwareVersions); if (d.flipAngle > 0.0) fprintf(fp, "DICOM_0018_1314_FlipAngle:=%g\n", d.flipAngle); - //multivolume but NOT DTI, e.g. fMRI/DCE see https://www.slicer.org/wiki/Documentation/4.4/Modules/MultiVolumeExplorer - // https://github.com/QIICR/PkModeling/blob/master/PkSolver/IO/MultiVolumeMetaDictReader.cxx#L34-L58 - // for "MultiVolume.FrameLabels:=" - // https://www.slicer.org/wiki/Documentation/4.4/Modules/MultiVolumeExplorer - // for "axis 0 index values:=" - // https://github.com/mhe/pynrrd/issues/71 - // "I don't know if it is a good idea for dcm2niix to mimic Slicer converter tags" Andrey Fedorov + // multivolume but NOT DTI, e.g. fMRI/DCE see https://www.slicer.org/wiki/Documentation/4.4/Modules/MultiVolumeExplorer + // https://github.com/QIICR/PkModeling/blob/master/PkSolver/IO/MultiVolumeMetaDictReader.cxx#L34-L58 + // for "MultiVolume.FrameLabels:=" + // https://www.slicer.org/wiki/Documentation/4.4/Modules/MultiVolumeExplorer + // for "axis 0 index values:=" + // https://github.com/mhe/pynrrd/issues/71 + // "I don't know if it is a good idea for dcm2niix to mimic Slicer converter tags" Andrey Fedorov /* if ((nDim > 3) && (hdr.dim[4] > 1) && (numDTI < 1)) { if ((d.TE > 0.0) && (!d.isXRay)) fprintf(fp,"MultiVolume.DICOM.EchoTime:=%g\n",d.TE); @@ -4667,28 +4652,28 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im fprintf(fp,"%g\n", (hdr.dim[4]-1) * frameTime); fprintf(fp,"MultiVolume.NumberOfFrames:=%d\n",hdr.dim[4]); } */ - //DWI values + // DWI values if ((nDim > 3) && (numDTI > 0) && (numDTI < kMaxDTI4D)) { mat33 inv; LOAD_MAT33(inv, hdr.pixdim[1], 0.0, 0.0, 0.0, hdr.pixdim[2], 0.0, 0.0, 0.0, hdr.pixdim[3]); inv = nifti_mat33_inverse(inv); mat33 s; LOAD_MAT33(s, hdr.srow_x[0], hdr.srow_x[1], hdr.srow_x[2], - hdr.srow_y[0], hdr.srow_y[1], hdr.srow_y[2], - hdr.srow_z[0], hdr.srow_z[1], hdr.srow_z[2]); + hdr.srow_y[0], hdr.srow_y[1], hdr.srow_y[2], + hdr.srow_z[0], hdr.srow_z[1], hdr.srow_z[2]); mat33 mf = nifti_mat33_mul(inv, s); fprintf(fp, "measurement frame: (%g,%g,%g) (%g,%g,%g) (%g,%g,%g)\n", mf.m[0][0], mf.m[1][0], mf.m[2][0], mf.m[0][1], mf.m[1][1], mf.m[2][1], mf.m[0][2], mf.m[1][2], mf.m[2][2]); - //modality tag + // modality tag fprintf(fp, "modality:=DWMRI\n"); float b_max = 0.0; for (int i = 0; i < numDTI; i++) if (dti4D->S[i].V[0] > b_max) b_max = dti4D->S[i].V[0]; fprintf(fp, "DWMRI_b-value:=%g\n", b_max); - //gradient tag, e.g. DWMRI_gradient_0000:=0.0 0.0 0.0 + // gradient tag, e.g. DWMRI_gradient_0000:=0.0 0.0 0.0 float minBvalThreshold = 6.0; if (d.manufacturer == kMANUFACTURER_SIEMENS) minBvalThreshold = 50.0; @@ -4697,39 +4682,39 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im if (b_max > 0) factor = sqrt(dti4D->S[i].V[0] / b_max); if ((dti4D->S[i].V[0] > minBvalThreshold) && (isSameFloatGE(0.0, dti4D->S[i].V[1])) && (isSameFloatGE(0.0, dti4D->S[i].V[2])) && (isSameFloatGE(0.0, dti4D->S[i].V[3]))) { - //On May 2, 2019, at 10:47 AM, Gordon L. Kindlmann <> wrote: + // On May 2, 2019, at 10:47 AM, Gordon L. Kindlmann <> wrote: //(assuming b_max 2000, we write "isotropic" for the b=2000 isotropic image, and specify the b-value if it is an isotropic image but not b-bax - // DWMRI_gradient_0003:=isotropic b=1000 - // DWMRI_gradient_0004:=isotropic + // DWMRI_gradient_0003:=isotropic b=1000 + // DWMRI_gradient_0004:=isotropic if (isSameFloatGE(b_max, dti4D->S[i].V[0])) fprintf(fp, "DWMRI_gradient_%04d:=isotropic\n", i); else fprintf(fp, "DWMRI_gradient_%04d:=isotropic b=%g\n", i, dti4D->S[i].V[0]); } else fprintf(fp, "DWMRI_gradient_%04d:=%.17g %.17g %.17g\n", i, factor * dti4D->S[i].V[1], factor * dti4D->S[i].V[2], factor * dti4D->S[i].V[3]); - //printf("%g = %g %g %g>>>>\n",dti4D->S[i].V[0], dti4D->S[i].V[1],dti4D->S[i].V[2],dti4D->S[i].V[3]); + // printf("%g = %g %g %g>>>>\n",dti4D->S[i].V[0], dti4D->S[i].V[1],dti4D->S[i].V[2],dti4D->S[i].V[3]); } } - fprintf(fp, "\n"); //blank line: end of NRRD header + fprintf(fp, "\n"); // blank line: end of NRRD header if (!isGz) fwrite(&im[0], imgsz, 1, fp); fclose(fp); if (!isGz) return EXIT_SUCCESS; -//below: gzip file +// below: gzip file #ifdef myDisableZLib - if (strlen(opts.pigzname) < 1) { //internal compression + if (strlen(opts.pigzname) < 1) { // internal compression printError("Compiled without gz support, unable to compress %s\n", fname); return EXIT_FAILURE; } #else - if (strlen(opts.pigzname) < 1) { //internal compression + if (strlen(opts.pigzname) < 1) { // internal compression writeNiiGz(fname, hdr, im, imgsz, opts.gzLevel, true); return EXIT_SUCCESS; } #endif - //below pigz - strcpy(fname, niiFilename); //without gz + // below pigz + strcpy(fname, niiFilename); // without gz strcat(fname, ".raw"); fp = fopen(fname, "wb"); fwrite(&im[0], imgsz, 1, fp); @@ -4737,324 +4722,479 @@ int nii_saveNRRD(char *niiFilename, struct nifti_1_header hdr, unsigned char *im return pigz_File(fname, opts, imgsz); } // nii_saveNRRD() -enum TZipMethod {zmZlib, zmGzip, zmBase64}; +enum TZipMethod { zmZlib, + zmGzip, + zmBase64 }; #ifdef myEnableJNIFTI #ifdef Z_DEFLATED -int zmat_run(const size_t inputsize, unsigned char *inputstr, size_t *outputsize, unsigned char **outputbuf, const int zipid, int *ret, const int iscompress){ +int zmat_run(const size_t inputsize, unsigned char *inputstr, size_t *outputsize, unsigned char **outputbuf, const int zipid, int *ret, const int iscompress) { z_stream zs; - size_t buflen[2]={0}; - *outputbuf=NULL; + size_t buflen[2] = {0}; + *outputbuf = NULL; zs.zalloc = Z_NULL; - zs.zfree = Z_NULL; + zs.zfree = Z_NULL; zs.opaque = Z_NULL; - if(inputsize==0) + if (inputsize == 0) return -1; - if(iscompress){ + if (iscompress) { /** perform compression or encoding */ - if(zipid==zmBase64){ + if (zipid == zmBase64) { /** base64 encoding */ - *outputbuf=base64_encode((const unsigned char*)inputstr, inputsize, outputsize); - }else if(zipid==zmZlib || zipid==zmGzip){ + *outputbuf = base64_encode((const unsigned char *)inputstr, inputsize, outputsize); + } else if (zipid == zmZlib || zipid == zmGzip) { /** zlib (.zip) or gzip (.gz) compression */ - if(zipid==zmZlib){ - if(deflateInit(&zs, (iscompress>0) ? Z_DEFAULT_COMPRESSION : (-iscompress)) != Z_OK) + if (zipid == zmZlib) { + if (deflateInit(&zs, (iscompress > 0) ? Z_DEFAULT_COMPRESSION : (-iscompress)) != Z_OK) return -2; - }else{ - if(deflateInit2(&zs, (iscompress>0) ? Z_DEFAULT_COMPRESSION : (-iscompress), Z_DEFLATED, 15|16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) + } else { + if (deflateInit2(&zs, (iscompress > 0) ? Z_DEFAULT_COMPRESSION : (-iscompress), Z_DEFLATED, 15 | 16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) return -2; } - buflen[0] =deflateBound(&zs,inputsize); - *outputbuf=(unsigned char *)malloc(buflen[0]); - zs.avail_in = inputsize; /* size of input, string + terminator*/ + buflen[0] = deflateBound(&zs, inputsize); + *outputbuf = (unsigned char *)malloc(buflen[0]); + zs.avail_in = inputsize; /* size of input, string + terminator*/ zs.next_in = (Bytef *)inputstr; /* input char array*/ - zs.avail_out = buflen[0]; /* size of output*/ + zs.avail_out = buflen[0]; /* size of output*/ - zs.next_out = (Bytef *)(*outputbuf); /*(Bytef *)(); // output char array*/ + zs.next_out = (Bytef *)(*outputbuf); /*(Bytef *)(); // output char array*/ - *ret=deflate(&zs, Z_FINISH); - *outputsize=zs.total_out; - if(*ret!=Z_STREAM_END && *ret!=Z_OK) + *ret = deflate(&zs, Z_FINISH); + *outputsize = zs.total_out; + if (*ret != Z_STREAM_END && *ret != Z_OK) return -3; deflateEnd(&zs); - }else{ + } else { return -7; } - }else{ + } else { /** perform decompression or decoding */ - if(zipid==zmBase64){ + if (zipid == zmBase64) { /** base64 decoding */ - *outputbuf=base64_decode((const unsigned char*)inputstr, inputsize, outputsize); - }else if(zipid==zmZlib || zipid==zmGzip){ + *outputbuf = base64_decode((const unsigned char *)inputstr, inputsize, outputsize); + } else if (zipid == zmZlib || zipid == zmGzip) { /** zlib (.zip) or gzip (.gz) decompression */ - int count=1; - if(zipid==zmZlib){ - if(inflateInit(&zs) != Z_OK) + int count = 1; + if (zipid == zmZlib) { + if (inflateInit(&zs) != Z_OK) return -2; - }else{ - if(inflateInit2(&zs, 15|32) != Z_OK) + } else { + if (inflateInit2(&zs, 15 | 32) != Z_OK) return -2; } - buflen[0] =inputsize*20; - *outputbuf=(unsigned char *)malloc(buflen[0]); + buflen[0] = inputsize * 20; + *outputbuf = (unsigned char *)malloc(buflen[0]); - zs.avail_in = inputsize; /* size of input, string + terminator*/ - zs.next_in =inputstr; /* input char array*/ + zs.avail_in = inputsize; /* size of input, string + terminator*/ + zs.next_in = inputstr; /* input char array*/ zs.avail_out = buflen[0]; /* size of output*/ - zs.next_out = (Bytef *)(*outputbuf); /*(Bytef *)(); // output char array*/ + zs.next_out = (Bytef *)(*outputbuf); /*(Bytef *)(); // output char array*/ - while((*ret=inflate(&zs, Z_SYNC_FLUSH))!=Z_STREAM_END && count<=10){ - *outputbuf=(unsigned char *)realloc(*outputbuf, (buflen[0]<1) - dim[ndim++]=jdataelemlen; - - for(int i=0;i0 && output[i][0]!='?'){ // take care all name-tags and constant string values - if(!(slen==1 && output[i][0]=='N')) - fwrite(output[i],1,slen,fp); - if(slen==1 && (output[i][0]=='N' || output[i][0]=='S') && i+2 1) + dim[ndim++] = jdataelemlen; + + for (int i = 0; i < markerlen; i++) { + int slen = strlen(output[i]); + if (slen > 0 && output[i][0] != '?') { // take care all name-tags and constant string values + if (!(slen == 1 && output[i][0] == 'N')) + fwrite(output[i], 1, slen, fp); + if (slen == 1 && (output[i][0] == 'N' || output[i][0] == 'S') && i + 2 < markerlen && output[i + 1][0] == '\0' && output[i + 2][0] != '?') { + unsigned int keylen = strlen(output[i + 2]); + if (keylen < 256) { + unsigned char keylenbyte = keylen; + fputc('U', fp); + fwrite(&keylenbyte, 1, sizeof(keylenbyte), fp); + } else { + fputc('l', fp); + write_ubjsonint(&keylen, sizeof(keylen), 1, fp); } } - }else if(slen>0){ - int slotid=0; - if(sscanf(output[i],"\?%d",&slotid)==1 && slotid>0){ - unsigned char *compressed=NULL; + } else if (slen > 0) { + int slotid = 0; + if (sscanf(output[i], "\?%d", &slotid) == 1 && slotid > 0) { + unsigned char *compressed = NULL; size_t compressedbytes; - int ret=0, status=0; - switch(slotid){ // mapping data to the pre-defined slots in the form of "?number" in the template - case 1: {write_ubjsonint(&hdr.sizeof_hdr,sizeof(hdr.sizeof_hdr),1,fp);break;} - case 2: {fputc(ndim,fp);break;} - case 3: {write_ubjsonint(hdr.dim+1, sizeof(hdr.dim[0]), ndim,fp);break;} - case 4: {write_ubjsonint(&hdr.intent_p1,1,sizeof(hdr.intent_p1),fp);break;} - case 5: {write_ubjsonint(&hdr.intent_p2,1,sizeof(hdr.intent_p2),fp);break;} - case 6: {write_ubjsonint(&hdr.intent_p3,1,sizeof(hdr.intent_p3),fp);break;} - case 7: {unsigned char val=strlen(intent);fputc('U',fp);fputc(val,fp); fwrite(intent,1,val,fp);break;} - case 8: {unsigned char val=strlen(dtype); fputc('U',fp);fputc(val,fp); fwrite(dtype,1,val,fp);break;} - case 9: {fputc(hdr.bitpix,fp);break;} - case 10: {write_ubjsonint(&hdr.slice_start, sizeof(hdr.slice_start),1,fp);break;} - case 11: {fputc(ndim,fp);break;} - case 12: {write_ubjsonint(&hdr.pixdim[1],ndim,sizeof(hdr.pixdim[1]),fp);break;} - case 13: {fputc((int)hdr.pixdim[0] ? 'l' : 'r',fp);break;} - case 14: {write_ubjsonint(&hdr.scl_slope,1,sizeof(hdr.scl_slope),fp);break;} - case 15: {write_ubjsonint(&hdr.scl_inter,1,sizeof(hdr.scl_inter),fp);break;} - case 16: {write_ubjsonint(&hdr.slice_end,sizeof(hdr.slice_end),1,fp);break;} - case 17: {unsigned char val=strlen(slicetype);fputc('U',fp);fputc(val,fp); fwrite(slicetype,1,val,fp);break;} - case 18: {unsigned char val=strlen(lunit);fputc('U',fp);fputc(val,fp); fwrite(lunit,1,val,fp);break;} - case 19: {unsigned char val=strlen(tunit);fputc('U',fp);fputc(val,fp); fwrite(tunit,1,val,fp);break;} - case 20: {write_ubjsonint(&hdr.cal_max,1,sizeof(hdr.cal_max),fp);break;} - case 21: {write_ubjsonint(&hdr.cal_min,1,sizeof(hdr.cal_min),fp);break;} - case 22: {write_ubjsonint(&hdr.slice_duration,1,sizeof(hdr.slice_duration),fp);break;} - case 23: {write_ubjsonint(&hdr.toffset,1,sizeof(hdr.toffset),fp);break;} - case 24: {unsigned char val=strlen(hdr.descrip);fputc('U',fp);fputc(val,fp); fwrite(hdr.descrip,1,val,fp);break;} - case 25: {unsigned char val=strlen(hdr.aux_file);fputc('U',fp);fputc(val,fp); fwrite(hdr.aux_file,1,val,fp);break;} - case 26: {write_ubjsonint(&hdr.qform_code,sizeof(hdr.qform_code),1,fp);break;} - case 27: {write_ubjsonint(&hdr.sform_code,sizeof(hdr.sform_code),1,fp);break;} - case 28: {write_ubjsonint(&hdr.quatern_b,1,sizeof(hdr.quatern_b),fp);break;} - case 29: {write_ubjsonint(&hdr.quatern_c,1,sizeof(hdr.quatern_c),fp);break;} - case 30: {write_ubjsonint(&hdr.quatern_d,1,sizeof(hdr.quatern_d),fp);break;} - case 31: {write_ubjsonint(&hdr.qoffset_x,1,sizeof(hdr.qoffset_x),fp);break;} - case 32: {write_ubjsonint(&hdr.qoffset_y,1,sizeof(hdr.qoffset_y),fp);break;} - case 33: {write_ubjsonint(&hdr.qoffset_z,1,sizeof(hdr.qoffset_z),fp);break;} - case 34: { - write_ubjsonint(&hdr.srow_x[0],4,sizeof(hdr.srow_x[0]),fp); - write_ubjsonint(&hdr.srow_y[0],4,sizeof(hdr.srow_y[0]),fp); - write_ubjsonint(&hdr.srow_z[0],4,sizeof(hdr.srow_z[0]),fp); - break; - } - case 35: {unsigned char val=strlen(hdr.intent_name);fputc('U',fp);fputc(val,fp); fwrite(hdr.intent_name,1,val,fp);break;} - case 36: {unsigned char val=strlen(hdr.magic);fputc('U',fp);fputc(val,fp); fwrite(hdr.magic,1,val,fp);break;} - case 37: {int val=hdr.vox_offset;write_ubjsonint(&val, sizeof(val), 1,fp);break;} - case 38: {unsigned char val=strlen(jdtype);fputc('U',fp);fputc(val,fp); fwrite(jdtype,1,val,fp);break;} - case 39: {fputc(ndim,fp);break;} - case 40: { - write_ubjsonint(dim, sizeof(dim[0]), ndim, fp); - if(!opts.isGz){ - const char *datastub="U\x0b_ArrayData_[$"; - fwrite(datastub,1,strlen(datastub),fp); - fputc(jdatamarker,fp); - fputc('#',fp); - - size_t totalelem=(totalbytes/(hdr.bitpix>>3)); - unsigned int clen=totalelem; - if((size_t)clen==totalelem){ - fputc('l',fp); - write_ubjsonint(&clen, sizeof(clen), 1,fp); - }else{ - fputc('L',fp); - write_ubjsonint(&totalelem, sizeof(totalelem), 1,fp); - } - fwrite(im,1,totalbytes,fp); - fputc('}',fp); // end of NIFTIData - fputc('}',fp); // end of the root object + int ret = 0, status = 0; + switch (slotid) { // mapping data to the pre-defined slots in the form of "?number" in the template + case 1: { + write_ubjsonint(&hdr.sizeof_hdr, sizeof(hdr.sizeof_hdr), 1, fp); + break; + } + case 2: { + fputc(ndim, fp); + break; + } + case 3: { + write_ubjsonint(hdr.dim + 1, sizeof(hdr.dim[0]), ndim, fp); + break; + } + case 4: { + write_ubjsonint(&hdr.intent_p1, 1, sizeof(hdr.intent_p1), fp); + break; + } + case 5: { + write_ubjsonint(&hdr.intent_p2, 1, sizeof(hdr.intent_p2), fp); + break; + } + case 6: { + write_ubjsonint(&hdr.intent_p3, 1, sizeof(hdr.intent_p3), fp); + break; + } + case 7: { + unsigned char val = strlen(intent); + fputc('U', fp); + fputc(val, fp); + fwrite(intent, 1, val, fp); + break; + } + case 8: { + unsigned char val = strlen(dtype); + fputc('U', fp); + fputc(val, fp); + fwrite(dtype, 1, val, fp); + break; + } + case 9: { + fputc(hdr.bitpix, fp); + break; + } + case 10: { + write_ubjsonint(&hdr.slice_start, sizeof(hdr.slice_start), 1, fp); + break; + } + case 11: { + fputc(ndim, fp); + break; + } + case 12: { + write_ubjsonint(&hdr.pixdim[1], ndim, sizeof(hdr.pixdim[1]), fp); + break; + } + case 13: { + fputc((int)hdr.pixdim[0] ? 'l' : 'r', fp); + break; + } + case 14: { + write_ubjsonint(&hdr.scl_slope, 1, sizeof(hdr.scl_slope), fp); + break; + } + case 15: { + write_ubjsonint(&hdr.scl_inter, 1, sizeof(hdr.scl_inter), fp); + break; + } + case 16: { + write_ubjsonint(&hdr.slice_end, sizeof(hdr.slice_end), 1, fp); + break; + } + case 17: { + unsigned char val = strlen(slicetype); + fputc('U', fp); + fputc(val, fp); + fwrite(slicetype, 1, val, fp); + break; + } + case 18: { + unsigned char val = strlen(lunit); + fputc('U', fp); + fputc(val, fp); + fwrite(lunit, 1, val, fp); + break; + } + case 19: { + unsigned char val = strlen(tunit); + fputc('U', fp); + fputc(val, fp); + fwrite(tunit, 1, val, fp); + break; + } + case 20: { + write_ubjsonint(&hdr.cal_max, 1, sizeof(hdr.cal_max), fp); + break; + } + case 21: { + write_ubjsonint(&hdr.cal_min, 1, sizeof(hdr.cal_min), fp); + break; + } + case 22: { + write_ubjsonint(&hdr.slice_duration, 1, sizeof(hdr.slice_duration), fp); + break; + } + case 23: { + write_ubjsonint(&hdr.toffset, 1, sizeof(hdr.toffset), fp); + break; + } + case 24: { + unsigned char val = strlen(hdr.descrip); + fputc('U', fp); + fputc(val, fp); + fwrite(hdr.descrip, 1, val, fp); + break; + } + case 25: { + unsigned char val = strlen(hdr.aux_file); + fputc('U', fp); + fputc(val, fp); + fwrite(hdr.aux_file, 1, val, fp); + break; + } + case 26: { + write_ubjsonint(&hdr.qform_code, sizeof(hdr.qform_code), 1, fp); + break; + } + case 27: { + write_ubjsonint(&hdr.sform_code, sizeof(hdr.sform_code), 1, fp); + break; + } + case 28: { + write_ubjsonint(&hdr.quatern_b, 1, sizeof(hdr.quatern_b), fp); + break; + } + case 29: { + write_ubjsonint(&hdr.quatern_c, 1, sizeof(hdr.quatern_c), fp); + break; + } + case 30: { + write_ubjsonint(&hdr.quatern_d, 1, sizeof(hdr.quatern_d), fp); + break; + } + case 31: { + write_ubjsonint(&hdr.qoffset_x, 1, sizeof(hdr.qoffset_x), fp); + break; + } + case 32: { + write_ubjsonint(&hdr.qoffset_y, 1, sizeof(hdr.qoffset_y), fp); + break; + } + case 33: { + write_ubjsonint(&hdr.qoffset_z, 1, sizeof(hdr.qoffset_z), fp); + break; + } + case 34: { + write_ubjsonint(&hdr.srow_x[0], 4, sizeof(hdr.srow_x[0]), fp); + write_ubjsonint(&hdr.srow_y[0], 4, sizeof(hdr.srow_y[0]), fp); + write_ubjsonint(&hdr.srow_z[0], 4, sizeof(hdr.srow_z[0]), fp); + break; + } + case 35: { + unsigned char val = strlen(hdr.intent_name); + fputc('U', fp); + fputc(val, fp); + fwrite(hdr.intent_name, 1, val, fp); + break; + } + case 36: { + unsigned char val = strlen(hdr.magic); + fputc('U', fp); + fputc(val, fp); + fwrite(hdr.magic, 1, val, fp); + break; + } + case 37: { + int val = hdr.vox_offset; + write_ubjsonint(&val, sizeof(val), 1, fp); + break; + } + case 38: { + unsigned char val = strlen(jdtype); + fputc('U', fp); + fputc(val, fp); + fwrite(jdtype, 1, val, fp); + break; + } + case 39: { + fputc(ndim, fp); + break; + } + case 40: { + write_ubjsonint(dim, sizeof(dim[0]), ndim, fp); + if (!opts.isGz) { + const char *datastub = "U\x0b_ArrayData_[$"; + fwrite(datastub, 1, strlen(datastub), fp); + fputc(jdatamarker, fp); + fputc('#', fp); + + size_t totalelem = (totalbytes / (hdr.bitpix >> 3)); + unsigned int clen = totalelem; + if ((size_t)clen == totalelem) { + fputc('l', fp); + write_ubjsonint(&clen, sizeof(clen), 1, fp); + } else { + fputc('L', fp); + write_ubjsonint(&totalelem, sizeof(totalelem), 1, fp); } - break; + fwrite(im, 1, totalbytes, fp); + fputc('}', fp); // end of NIFTIData + fputc('}', fp); // end of the root object } + break; + } #ifdef Z_DEFLATED - case 41: {fputc('U',fp);fputc(4,fp);fwrite("zlib",1,4,fp);break;} - case 42: {unsigned int val=(totalbytes/(hdr.bitpix>>3));write_ubjsonint(&val,sizeof(val), 1,fp);break;} - case 43: - ret=zmat_run(totalbytes, im, &compressedbytes, (unsigned char **)&compressed, zmZlib, &status, -opts.gzLevel); - if(!ret){ - unsigned int clen=compressedbytes; - if((size_t)clen==compressedbytes){ - fputc('l',fp); - write_ubjsonint(&clen, sizeof(clen), 1,fp); - }else{ - fputc('L',fp); - write_ubjsonint(&compressedbytes, sizeof(compressedbytes), 1,fp); - } - fwrite(compressed,1,compressedbytes,fp); - }else{ - printError("Failed to compress data stream, error code=%d, status code=%d\n", ret, status); - return EXIT_FAILURE; + case 41: { + fputc('U', fp); + fputc(4, fp); + fwrite("zlib", 1, 4, fp); + break; + } + case 42: { + unsigned int val = (totalbytes / (hdr.bitpix >> 3)); + write_ubjsonint(&val, sizeof(val), 1, fp); + break; + } + case 43: + ret = zmat_run(totalbytes, im, &compressedbytes, (unsigned char **)&compressed, zmZlib, &status, -opts.gzLevel); + if (!ret) { + unsigned int clen = compressedbytes; + if ((size_t)clen == compressedbytes) { + fputc('l', fp); + write_ubjsonint(&clen, sizeof(clen), 1, fp); + } else { + fputc('L', fp); + write_ubjsonint(&compressedbytes, sizeof(compressedbytes), 1, fp); } - if(compressed) - free(compressed); - break; + fwrite(compressed, 1, compressedbytes, fp); + } else { + printError("Failed to compress data stream, error code=%d, status code=%d\n", ret, status); + return EXIT_FAILURE; + } + if (compressed) + free(compressed); + break; #endif } - if(!opts.isGz && slotid==40) + if (!opts.isGz && slotid == 40) break; } } @@ -5064,71 +5204,71 @@ int nii_savebnii(char *bniifile, struct nifti_1_header hdr, unsigned char *im, s } int nii_savejnii(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, struct TDCMopts opts, struct TDICOMdata d, struct TDTI4D *dti4D, int numDTI) { -// JNIfTI is a JSON wrapper to the NIfTI-1/2 format, supports both plain-text (.jnii) and binary (.bnii) formats -// Specification: https://github.com/NeuroJSON/jnifti/blob/master/JNIfTI_specification.md -// jnii is a plain JSON file and can be parsed in nearly all JSON parsers; to decode the -// embedded binary data as strings, the NIFTIData section can be be parsed in MATLAB using -// jnifty (https://github.com/NeuroJSON/jnifty) and Python using jdata (https://github.com/NeuroJSON/pyjdata) + // JNIfTI is a JSON wrapper to the NIfTI-1/2 format, supports both plain-text (.jnii) and binary (.bnii) formats + // Specification: https://github.com/NeuroJSON/jnifti/blob/master/JNIfTI_specification.md + // jnii is a plain JSON file and can be parsed in nearly all JSON parsers; to decode the + // embedded binary data as strings, the NIFTIData section can be be parsed in MATLAB using + // jnifty (https://github.com/NeuroJSON/jnifty) and Python using jdata (https://github.com/NeuroJSON/pyjdata) FILE *fp; - int dim[8]={0}, ndim=0; /* extra dimension in dim to hold the split data from complex voxel types, such as complex64 */ + int dim[8] = {0}, ndim = 0; /* extra dimension in dim to hold the split data from complex voxel types, such as complex64 */ - cJSON *root=NULL, *info=NULL, *jhdr=NULL, *dat=NULL, *sub=NULL; - char *jsonstr=NULL; + cJSON *root = NULL, *info = NULL, *jhdr = NULL, *dat = NULL, *sub = NULL; + char *jsonstr = NULL; size_t compressedbytes, totalbytes; - unsigned char *compressed=NULL, *buf=NULL; + unsigned char *compressed = NULL, *buf = NULL; /*jnifti converts code-based header fields to human-readable/standardized strings*/ int datatypeidx; - const char *datatypestr[]={"uint8","int16","int32","single","complex64","double", - "rgb24" ,"int8","uint16","uint32","int64","uint64", - "double128","complex128","complex256","rgba32",""}; - const char *jdatatypestr[]={"uint8","int16","int32","single","double","double", - "uint8" ,"int8","uint16","uint32","int64","uint64", - "uint8","double","uint8","uint8","uint8"}; - const char jdatamarker[]={'U','I','l','d','D','D','U','i','u','m','L','M', - 'U','D','U','U','U'}; - unsigned char jdataelemlen[]={1,1,1,1,2,1,3,1,1,1,1,1,16,2,32,4,1}; - int datatypeid[]={NIFTI_TYPE_UINT8,NIFTI_TYPE_INT16,NIFTI_TYPE_INT32, - NIFTI_TYPE_FLOAT32,NIFTI_TYPE_COMPLEX64,NIFTI_TYPE_FLOAT64, - NIFTI_TYPE_RGB24,NIFTI_TYPE_INT8,NIFTI_TYPE_UINT16, - NIFTI_TYPE_UINT32,NIFTI_TYPE_INT64,NIFTI_TYPE_UINT64, - NIFTI_TYPE_FLOAT128,NIFTI_TYPE_COMPLEX128, - NIFTI_TYPE_COMPLEX256,NIFTI_TYPE_RGBA32}; + const char *datatypestr[] = {"uint8", "int16", "int32", "single", "complex64", "double", + "rgb24", "int8", "uint16", "uint32", "int64", "uint64", + "double128", "complex128", "complex256", "rgba32", ""}; + const char *jdatatypestr[] = {"uint8", "int16", "int32", "single", "double", "double", + "uint8", "int8", "uint16", "uint32", "int64", "uint64", + "uint8", "double", "uint8", "uint8", "uint8"}; + const char jdatamarker[] = {'U', 'I', 'l', 'd', 'D', 'D', 'U', 'i', 'u', 'm', 'L', 'M', + 'U', 'D', 'U', 'U', 'U'}; + unsigned char jdataelemlen[] = {1, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 16, 2, 32, 4, 1}; + int datatypeid[] = {NIFTI_TYPE_UINT8, NIFTI_TYPE_INT16, NIFTI_TYPE_INT32, + NIFTI_TYPE_FLOAT32, NIFTI_TYPE_COMPLEX64, NIFTI_TYPE_FLOAT64, + NIFTI_TYPE_RGB24, NIFTI_TYPE_INT8, NIFTI_TYPE_UINT16, + NIFTI_TYPE_UINT32, NIFTI_TYPE_INT64, NIFTI_TYPE_UINT64, + NIFTI_TYPE_FLOAT128, NIFTI_TYPE_COMPLEX128, + NIFTI_TYPE_COMPLEX256, NIFTI_TYPE_RGBA32}; int lunitidx, tunitidx; - const char *unitstr[]={"m","mm","um","s","ms","us","hz","ppm","rad",""}; - int unitid[]={NIFTI_UNITS_METER,NIFTI_UNITS_MM,NIFTI_UNITS_MICRON, - NIFTI_UNITS_SEC,NIFTI_UNITS_MSEC,NIFTI_UNITS_USEC, - NIFTI_UNITS_HZ,NIFTI_UNITS_PPM,NIFTI_UNITS_RADS}; + const char *unitstr[] = {"m", "mm", "um", "s", "ms", "us", "hz", "ppm", "rad", ""}; + int unitid[] = {NIFTI_UNITS_METER, NIFTI_UNITS_MM, NIFTI_UNITS_MICRON, + NIFTI_UNITS_SEC, NIFTI_UNITS_MSEC, NIFTI_UNITS_USEC, + NIFTI_UNITS_HZ, NIFTI_UNITS_PPM, NIFTI_UNITS_RADS}; int slicetypeidx; - const char *slicetypestr[]={"","seq+","seq-","alt+","alt-","alt2+","alt2-",""}; - int slicetypeid[]={NIFTI_SLICE_UNKNOWN,NIFTI_SLICE_SEQ_INC, - NIFTI_SLICE_SEQ_DEC,NIFTI_SLICE_ALT_INC,NIFTI_SLICE_ALT_DEC, - NIFTI_SLICE_ALT_INC2,NIFTI_SLICE_ALT_DEC2}; + const char *slicetypestr[] = {"", "seq+", "seq-", "alt+", "alt-", "alt2+", "alt2-", ""}; + int slicetypeid[] = {NIFTI_SLICE_UNKNOWN, NIFTI_SLICE_SEQ_INC, + NIFTI_SLICE_SEQ_DEC, NIFTI_SLICE_ALT_INC, NIFTI_SLICE_ALT_DEC, + NIFTI_SLICE_ALT_INC2, NIFTI_SLICE_ALT_DEC2}; int intentidx; - const char *intentstr[]={"","corr","ttest","ftest","zscore","chi2","beta","binomial", - "gamma","poisson","normal","ncftest","ncchi2","logistic","laplace", - "uniform","ncttest","weibull","chi","invgauss","extval","pvalue", - "logpvalue","log10pvalue","estimate","label","neuronames","matrix", - "symmatrix","dispvec","vector","point","triangle","quaternion", - "unitless","tseries","elem","rgb","rgba","shape",""}; - int intentid[]={NIFTI_INTENT_NONE,NIFTI_INTENT_CORREL,NIFTI_INTENT_TTEST, - NIFTI_INTENT_FTEST,NIFTI_INTENT_ZSCORE,NIFTI_INTENT_CHISQ, - NIFTI_INTENT_BETA,NIFTI_INTENT_BINOM,NIFTI_INTENT_GAMMA, - NIFTI_INTENT_POISSON,NIFTI_INTENT_NORMAL,NIFTI_INTENT_FTEST_NONC, - NIFTI_INTENT_CHISQ_NONC,NIFTI_INTENT_LOGISTIC,NIFTI_INTENT_LAPLACE, - NIFTI_INTENT_UNIFORM,NIFTI_INTENT_TTEST_NONC,NIFTI_INTENT_WEIBULL, - NIFTI_INTENT_CHI,NIFTI_INTENT_INVGAUSS,NIFTI_INTENT_EXTVAL, - NIFTI_INTENT_PVAL,NIFTI_INTENT_LOGPVAL,NIFTI_INTENT_LOG10PVAL, - NIFTI_INTENT_ESTIMATE,NIFTI_INTENT_LABEL,NIFTI_INTENT_NEURONAME, - NIFTI_INTENT_GENMATRIX,NIFTI_INTENT_SYMMATRIX,NIFTI_INTENT_DISPVECT, - NIFTI_INTENT_VECTOR,NIFTI_INTENT_POINTSET,NIFTI_INTENT_TRIANGLE, - NIFTI_INTENT_QUATERNION,NIFTI_INTENT_DIMLESS,NIFTI_INTENT_TIME_SERIES, - NIFTI_INTENT_NODE_INDEX,NIFTI_INTENT_RGB_VECTOR,NIFTI_INTENT_RGBA_VECTOR, - NIFTI_INTENT_SHAPE}; + const char *intentstr[] = {"", "corr", "ttest", "ftest", "zscore", "chi2", "beta", "binomial", + "gamma", "poisson", "normal", "ncftest", "ncchi2", "logistic", "laplace", + "uniform", "ncttest", "weibull", "chi", "invgauss", "extval", "pvalue", + "logpvalue", "log10pvalue", "estimate", "label", "neuronames", "matrix", + "symmatrix", "dispvec", "vector", "point", "triangle", "quaternion", + "unitless", "tseries", "elem", "rgb", "rgba", "shape", ""}; + int intentid[] = {NIFTI_INTENT_NONE, NIFTI_INTENT_CORREL, NIFTI_INTENT_TTEST, + NIFTI_INTENT_FTEST, NIFTI_INTENT_ZSCORE, NIFTI_INTENT_CHISQ, + NIFTI_INTENT_BETA, NIFTI_INTENT_BINOM, NIFTI_INTENT_GAMMA, + NIFTI_INTENT_POISSON, NIFTI_INTENT_NORMAL, NIFTI_INTENT_FTEST_NONC, + NIFTI_INTENT_CHISQ_NONC, NIFTI_INTENT_LOGISTIC, NIFTI_INTENT_LAPLACE, + NIFTI_INTENT_UNIFORM, NIFTI_INTENT_TTEST_NONC, NIFTI_INTENT_WEIBULL, + NIFTI_INTENT_CHI, NIFTI_INTENT_INVGAUSS, NIFTI_INTENT_EXTVAL, + NIFTI_INTENT_PVAL, NIFTI_INTENT_LOGPVAL, NIFTI_INTENT_LOG10PVAL, + NIFTI_INTENT_ESTIMATE, NIFTI_INTENT_LABEL, NIFTI_INTENT_NEURONAME, + NIFTI_INTENT_GENMATRIX, NIFTI_INTENT_SYMMATRIX, NIFTI_INTENT_DISPVECT, + NIFTI_INTENT_VECTOR, NIFTI_INTENT_POINTSET, NIFTI_INTENT_TRIANGLE, + NIFTI_INTENT_QUATERNION, NIFTI_INTENT_DIMLESS, NIFTI_INTENT_TIME_SERIES, + NIFTI_INTENT_NODE_INDEX, NIFTI_INTENT_RGB_VECTOR, NIFTI_INTENT_RGBA_VECTOR, + NIFTI_INTENT_SHAPE}; char fname[2048] = {""}; strcpy(fname, niiFilename); if (opts.saveFormat == kSaveFormatBNII) @@ -5137,30 +5277,30 @@ int nii_savejnii(char *niiFilename, struct nifti_1_header hdr, unsigned char *im strcat(fname, ".jnii"); /* preprocess nifti header to convert to human-readable jnifti fields */ - totalbytes=nii_ImgBytes(hdr); - ndim=hdr.dim[0]; - for(int i=1;i<8;i++) - dim[i-1]=hdr.dim[i]; - - datatypeidx=jnifti_lookup(datatypeid, sizeof(datatypeid)/sizeof(int), hdr.datatype); - slicetypeidx=jnifti_lookup(slicetypeid, sizeof(slicetypeid)/sizeof(int), hdr.slice_code); - intentidx=jnifti_lookup(intentid, sizeof(intentid)/sizeof(int), hdr.intent_code); - lunitidx=sizeof(unitid)/sizeof(unitid[0]); - tunitidx=sizeof(unitid)/sizeof(unitid[0]); - for(int i=0;i=NIFTI_UNITS_HZ && hdr.xyzt_units==unitid[i]) + for (int i = 0; i < sizeof(unitid) / sizeof(unitid[0]); i++) { + if (hdr.xyzt_units >= NIFTI_UNITS_HZ && hdr.xyzt_units == unitid[i]) cJSON_AddStringToObject(sub, "Special", unitstr[i]); } cJSON_AddNumberToObject(jhdr, "MaxIntensity", hdr.cal_max); @@ -5211,117 +5351,117 @@ int nii_savejnii(char *niiFilename, struct nifti_1_header hdr, unsigned char *im cJSON_AddStringToObject(jhdr, "AuxFile", hdr.aux_file); cJSON_AddNumberToObject(jhdr, "QForm", hdr.qform_code); cJSON_AddNumberToObject(jhdr, "SForm", hdr.sform_code); - cJSON_AddItemToObject(jhdr, "Quatern", sub=cJSON_CreateObject()); + cJSON_AddItemToObject(jhdr, "Quatern", sub = cJSON_CreateObject()); cJSON_AddNumberToObject(sub, "b", hdr.quatern_b); cJSON_AddNumberToObject(sub, "c", hdr.quatern_c); cJSON_AddNumberToObject(sub, "d", hdr.quatern_d); - cJSON_AddItemToObject(jhdr, "QuaternOffset", sub=cJSON_CreateObject()); + cJSON_AddItemToObject(jhdr, "QuaternOffset", sub = cJSON_CreateObject()); cJSON_AddNumberToObject(sub, "x", hdr.qoffset_x); cJSON_AddNumberToObject(sub, "y", hdr.qoffset_y); cJSON_AddNumberToObject(sub, "z", hdr.qoffset_z); - cJSON_AddItemToObject(jhdr, "Affine", sub=cJSON_CreateArray()); - cJSON_AddItemToArray(sub, cJSON_CreateFloatArray(hdr.srow_x,4)); - cJSON_AddItemToArray(sub, cJSON_CreateFloatArray(hdr.srow_y,4)); - cJSON_AddItemToArray(sub, cJSON_CreateFloatArray(hdr.srow_z,4)); + cJSON_AddItemToObject(jhdr, "Affine", sub = cJSON_CreateArray()); + cJSON_AddItemToArray(sub, cJSON_CreateFloatArray(hdr.srow_x, 4)); + cJSON_AddItemToArray(sub, cJSON_CreateFloatArray(hdr.srow_y, 4)); + cJSON_AddItemToArray(sub, cJSON_CreateFloatArray(hdr.srow_z, 4)); cJSON_AddStringToObject(jhdr, "Name", hdr.intent_name); cJSON_AddStringToObject(jhdr, "NIIFormat", hdr.magic); /* save depreciated header flags if non-trival values are provided */ - if(hdr.vox_offset) + if (hdr.vox_offset) cJSON_AddNumberToObject(jhdr, "NIIByteOffset", hdr.vox_offset); - if(strlen(hdr.data_type)) + if (strlen(hdr.data_type)) cJSON_AddStringToObject(jhdr, "A75DataTypeName", hdr.data_type); - if(strlen(hdr.db_name)) + if (strlen(hdr.db_name)) cJSON_AddStringToObject(jhdr, "A75DBName", hdr.db_name); - if(hdr.extents) + if (hdr.extents) cJSON_AddNumberToObject(jhdr, "A75Extends", hdr.extents); - if(hdr.session_error) + if (hdr.session_error) cJSON_AddNumberToObject(jhdr, "A75SessionError", hdr.session_error); - if(hdr.regular) + if (hdr.regular) cJSON_AddNumberToObject(jhdr, "A75DataTypeName", hdr.regular); - if(hdr.glmax) + if (hdr.glmax) cJSON_AddNumberToObject(jhdr, "A75GlobalMax", hdr.glmax); - if(hdr.glmin) + if (hdr.glmin) cJSON_AddNumberToObject(jhdr, "A75GlobalMin", hdr.glmin); /* the "NIFTIData" section stores volumetric data */ - cJSON_AddItemToObject(root, "NIFTIData",dat = cJSON_CreateObject()); - cJSON_AddStringToObject(dat,"_ArrayType_",jdatatypestr[datatypeidx]); - if(jdataelemlen[datatypeidx]>1) - dim[ndim++]=jdataelemlen[datatypeidx]; - cJSON_AddItemToObject(dat, "_ArraySize_",cJSON_CreateIntArray(dim,ndim)); - cJSON_AddStringToObject(dat,"_ArrayOrder_","c"); // NIfTI array is column-major + cJSON_AddItemToObject(root, "NIFTIData", dat = cJSON_CreateObject()); + cJSON_AddStringToObject(dat, "_ArrayType_", jdatatypestr[datatypeidx]); + if (jdataelemlen[datatypeidx] > 1) + dim[ndim++] = jdataelemlen[datatypeidx]; + cJSON_AddItemToObject(dat, "_ArraySize_", cJSON_CreateIntArray(dim, ndim)); + cJSON_AddStringToObject(dat, "_ArrayOrder_", "c"); // NIfTI array is column-major #ifdef Z_DEFLATED - if(opts.isGz){ - int ret=0, status=0; + if (opts.isGz) { + int ret = 0, status = 0; cJSON_AddStringToObject(dat, "_ArrayZipType_", "zlib"); - cJSON_AddNumberToObject(dat, "_ArrayZipSize_",totalbytes/(hdr.bitpix>>3)); + cJSON_AddNumberToObject(dat, "_ArrayZipSize_", totalbytes / (hdr.bitpix >> 3)); - ret=zmat_run(totalbytes, im, &compressedbytes, (unsigned char **)&compressed, zmZlib, &status, -opts.gzLevel); + ret = zmat_run(totalbytes, im, &compressedbytes, (unsigned char **)&compressed, zmZlib, &status, -opts.gzLevel); - if(!ret){ - ret=zmat_run(compressedbytes, compressed, &totalbytes, (unsigned char **)&buf, zmBase64, &status,1); - cJSON_AddStringToObject(dat, "_ArrayZipData_",(const char *)buf); - }else{ + if (!ret) { + ret = zmat_run(compressedbytes, compressed, &totalbytes, (unsigned char **)&buf, zmBase64, &status, 1); + cJSON_AddStringToObject(dat, "_ArrayZipData_", (const char *)buf); + } else { printError("Failed to compress data stream, error code=%d, status code=%d\n", ret, status); return EXIT_FAILURE; } - if(compressed) + if (compressed) free(compressed); - }else{ + } else { cJSON_AddStringToObject(dat, "_ArrayZipType_", "base64"); - cJSON_AddNumberToObject(dat, "_ArrayZipSize_",totalbytes/(hdr.bitpix>>3)); - buf=base64_encode(im, totalbytes, &compressedbytes); - cJSON_AddStringToObject(dat,"_ArrayZipData_", (const char*)buf); + cJSON_AddNumberToObject(dat, "_ArrayZipSize_", totalbytes / (hdr.bitpix >> 3)); + buf = base64_encode(im, totalbytes, &compressedbytes); + cJSON_AddStringToObject(dat, "_ArrayZipData_", (const char *)buf); } #else cJSON_AddStringToObject(dat, "_ArrayZipType_", "base64"); - cJSON_AddNumberToObject(dat, "_ArrayZipSize_",totalbytes/(hdr.bitpix>>3)); - buf=base64_encode(im, totalbytes, &compressedbytes); - cJSON_AddStringToObject(dat,"_ArrayZipData_", (const char*)buf); + cJSON_AddNumberToObject(dat, "_ArrayZipSize_", totalbytes / (hdr.bitpix >> 3)); + buf = base64_encode(im, totalbytes, &compressedbytes); + cJSON_AddStringToObject(dat, "_ArrayZipData_", (const char *)buf); #endif - if(buf) + if (buf) free(buf); /* now save JSON to file */ - jsonstr=cJSON_Print(root); - if(jsonstr==NULL){ + jsonstr = cJSON_Print(root); + if (jsonstr == NULL) { printMessage("Error: error when converting to JNIfTI\n"); return EXIT_FAILURE; } - fp=fopen(fname,"wt"); - if(fp==NULL){ + fp = fopen(fname, "wt"); + if (fp == NULL) { printMessage("Error: error when writing to JNIfTI file\n"); return EXIT_FAILURE; } - fprintf(fp,"%s\n",jsonstr); + fprintf(fp, "%s\n", jsonstr); fclose(fp); - if(jsonstr) + if (jsonstr) free(jsonstr); - if(root) + if (root) cJSON_Delete(root); return EXIT_SUCCESS; } // nii_savejnii() -#endif //#ifdef myEnableJNIFTI +#endif // #ifdef myEnableJNIFTI int nii_saveForeign(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, struct TDCMopts opts, struct TDICOMdata d, struct TDTI4D *dti4D, int numDTI) { if (opts.saveFormat == kSaveFormatMGH) return nii_saveMGH(niiFilename, hdr, im, opts, d, dti4D, numDTI); - #ifdef myEnableJNIFTI +#ifdef myEnableJNIFTI else if (opts.saveFormat == kSaveFormatJNII || opts.saveFormat == kSaveFormatBNII) return nii_savejnii(niiFilename, hdr, im, opts, d, dti4D, numDTI); - #endif +#endif return nii_saveNRRD(niiFilename, hdr, im, opts, d, dti4D, numDTI); -}// nii_saveForeign() +} // nii_saveForeign() #endif void removeSclSlopeInter(struct nifti_1_header *hdr, unsigned char *img) { - //NRRD does not have scl_slope scl_inter. Adjust data if possible - // https://discourse.slicer.org/t/preserve-image-rescale-and-slope-when-saving-in-nrrd-file/13357 + // NRRD does not have scl_slope scl_inter. Adjust data if possible + // https://discourse.slicer.org/t/preserve-image-rescale-and-slope-when-saving-in-nrrd-file/13357 if (isSameFloat(hdr->scl_inter, 0.0) && isSameFloat(hdr->scl_slope, 1.0)) return; if ((!isSameFloat(fmod(hdr->scl_inter, 1.0f), 0.0)) || (!isSameFloat(fmod(hdr->scl_slope, 1.0f), 0.0))) @@ -5372,27 +5512,27 @@ void removeSclSlopeInter(struct nifti_1_header *hdr, unsigned char *img) { hdr->scl_inter = 0.0; return; } - //printWarning("NRRD unable to record scl_slope/scl_inter %g/%g\n", hdr->scl_slope, hdr->scl_inter); + // printWarning("NRRD unable to record scl_slope/scl_inter %g/%g\n", hdr->scl_slope, hdr->scl_inter); } int nii_saveNII(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, struct TDCMopts opts, struct TDICOMdata d) { #ifdef USING_R ImageList *images = (ImageList *)opts.imageList; - if (opts.isImageInMemory) { - hdr.vox_offset = 352; - // Extract the basename from the full file path - char *start = niiFilename + strlen(niiFilename); - while (start >= niiFilename && *start != '/' && *start != kPathSeparator) - start--; - std::string name(++start); - nifti_image *image = nifti_convert_nhdr2nim(hdr, niiFilename); - if (image == NULL) - return EXIT_FAILURE; - image->data = (void *)im; - images->append(image, name); - free(image); - return EXIT_SUCCESS; - } + if (opts.isImageInMemory) { + hdr.vox_offset = 352; + // Extract the basename from the full file path + char *start = niiFilename + strlen(niiFilename); + while (start >= niiFilename && *start != '/' && *start != kPathSeparator) + start--; + std::string name(++start); + nifti_image *image = nifti_convert_nhdr2nim(hdr, niiFilename); + if (image == NULL) + return EXIT_FAILURE; + image->data = (void *)im; + images->append(image, name); + free(image); + return EXIT_SUCCESS; + } #else if (opts.isOnlyBIDS) return EXIT_SUCCESS; @@ -5410,9 +5550,9 @@ int nii_saveNII(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, return EXIT_FAILURE; } #ifndef myDisableGzSizeLimits - //see https://github.com/rordenlab/dcm2niix/issues/124 + // see https://github.com/rordenlab/dcm2niix/issues/124 uint64_t kMaxPigz = 4294967264; -//https://stackoverflow.com/questions/5272825/detecting-64bit-compile-in-c +// https://stackoverflow.com/questions/5272825/detecting-64bit-compile-in-c #ifndef UINTPTR_MAX uint64_t kMaxGz = 2147483647; #elif UINTPTR_MAX == 0xffffffff @@ -5423,19 +5563,19 @@ int nii_saveNII(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, compiler error : unable to determine is 32 or 64 bit #endif #ifndef myDisableZLib - if ((opts.isGz) && (strlen(opts.pigzname) < 1) && ((imgsz + hdr.vox_offset) >= kMaxGz)) { //use internal compressor + if ((opts.isGz) && (strlen(opts.pigzname) < 1) && ((imgsz + hdr.vox_offset) >= kMaxGz)) { // use internal compressor printWarning("Saving uncompressed data: internal compressor unable to process such large files.\n"); if ((imgsz + hdr.vox_offset) < kMaxPigz) printWarning(" Hint: using external compressor (pigz) should help.\n"); - } else if ((opts.isGz) && (strlen(opts.pigzname) < 1) && ((imgsz + hdr.vox_offset) < kMaxGz)) { //use internal compressor + } else if ((opts.isGz) && (strlen(opts.pigzname) < 1) && ((imgsz + hdr.vox_offset) < kMaxGz)) { // use internal compressor if (!opts.isSaveNativeEndian) - swapEndian(&hdr, im, true); //byte-swap endian (e.g. little->big) + swapEndian(&hdr, im, true); // byte-swap endian (e.g. little->big) writeNiiGz(niiFilename, hdr, im, imgsz, opts.gzLevel, false); #ifdef USING_R images->appendPath(std::string(niiFilename) + ".nii.gz"); #endif if (!opts.isSaveNativeEndian) - swapEndian(&hdr, im, false); //unbyte-swap endian (e.g. big->little) + swapEndian(&hdr, im, false); // unbyte-swap endian (e.g. big->little) return EXIT_SUCCESS; } #endif @@ -5446,9 +5586,9 @@ int nii_saveNII(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, #if defined(_WIN64) || defined(_WIN32) if ((opts.isGz) && (opts.isPipedGz)) printWarning("The 'optimal' piped gz is only available for Unix\n"); -#else //if windows else Unix +#else // if windows else Unix if ((opts.isGz) && (opts.isPipedGz) && (strlen(opts.pigzname) > 0)) { - //piped gz + // piped gz if (opts.isVerbose) printMessage(" Optimal piped gz will fail if pigz version < 2.3.4.\n"); char command[768]; @@ -5457,15 +5597,15 @@ int nii_saveNII(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, if ((opts.gzLevel > 0) && (opts.gzLevel < 12)) { char newstr[256]; snprintf(newstr, 256, "\" -n -f -%d > \"", opts.gzLevel); - //749 snprintf(newstr, 256, "\" -n -f -%d > '", opts.gzLevel); + // 749 snprintf(newstr, 256, "\" -n -f -%d > '", opts.gzLevel); strcat(command, newstr); } else - strcat(command, "\" -n -f > \""); //current versions of pigz (2.3) built on Windows can hang if the filename is included, presumably because it is not finding the path characters ':\' - //749 strcat(command, "\" -n -f > '"); //current versions of pigz (2.3) built on Windows can hang if the filename is included, presumably because it is not finding the path characters ':\' + strcat(command, "\" -n -f > \""); // current versions of pigz (2.3) built on Windows can hang if the filename is included, presumably because it is not finding the path characters ':\' + // 749 strcat(command, "\" -n -f > '"); //current versions of pigz (2.3) built on Windows can hang if the filename is included, presumably because it is not finding the path characters ':\' strcat(command, fname); - //issue749 single not double quotes so $ character does not cause issues - //749 strcat(command, ".gz'"); //add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"' - strcat(command, ".gz\""); //add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"' + // issue749 single not double quotes so $ character does not cause issues + // 749 strcat(command, ".gz'"); //add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"' + strcat(command, ".gz\""); // add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"' if (opts.isVerbose) printMessage("Compress: %s\n", command); FILE *pigzPipe; @@ -5474,14 +5614,14 @@ int nii_saveNII(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, return EXIT_FAILURE; } if (!opts.isSaveNativeEndian) - swapEndian(&hdr, im, true); //byte-swap endian (e.g. little->big) + swapEndian(&hdr, im, true); // byte-swap endian (e.g. little->big) fwrite(&hdr, sizeof(hdr), 1, pigzPipe); uint32_t pad = 0; fwrite(&pad, sizeof(pad), 1, pigzPipe); fwrite(&im[0], imgsz, 1, pigzPipe); pclose(pigzPipe); if (!opts.isSaveNativeEndian) - swapEndian(&hdr, im, false); //unbyte-swap endian (e.g. big->little) + swapEndian(&hdr, im, false); // unbyte-swap endian (e.g. big->little) return EXIT_SUCCESS; } #endif @@ -5491,7 +5631,7 @@ int nii_saveNII(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, if (!fp) return EXIT_FAILURE; if (!opts.isSaveNativeEndian) - swapEndian(&hdr, im, true); //byte-swap endian (e.g. little->big) + swapEndian(&hdr, im, true); // byte-swap endian (e.g. little->big) fwrite(&hdr, sizeof(hdr), 1, fp); uint32_t pad = 0; fwrite(&pad, sizeof(pad), 1, fp); @@ -5499,7 +5639,7 @@ int nii_saveNII(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, fclose(fp); if (!opts.isSaveNativeEndian) - swapEndian(&hdr, im, false); //unbyte-swap endian (e.g. big->little) + swapEndian(&hdr, im, false); // unbyte-swap endian (e.g. big->little) #endif #ifdef USING_R @@ -5524,7 +5664,7 @@ int nii_saveNIIx(char *niiFilename, struct nifti_1_header hdr, unsigned char *im } int nii_saveNII3D(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, struct TDCMopts opts, struct TDICOMdata d) { - //save 4D series as sequence of 3D volumes + // save 4D series as sequence of 3D volumes struct nifti_1_header hdr1 = hdr; int nVol = 1; for (int i = 4; i < 8; i++) { @@ -5532,7 +5672,7 @@ int nii_saveNII3D(char *niiFilename, struct nifti_1_header hdr, unsigned char *i nVol = nVol * hdr.dim[i]; hdr1.dim[i] = 0; } - hdr1.dim[0] = 3; //save as 3D file + hdr1.dim[0] = 3; // save as 3D file size_t imgsz = nii_ImgBytes(hdr1); size_t pos = 0; char fname[2048] = {""}; @@ -5550,7 +5690,7 @@ int nii_saveNII3D(char *niiFilename, struct nifti_1_header hdr, unsigned char *i } // nii_saveNII3D() void nii_storeIntegerScaleFactor(int scale, struct nifti_1_header *hdr) { - //appends NIfTI header description field with " isN" where N is integer scaling + // appends NIfTI header description field with " isN" where N is integer scaling char newstr[256]; snprintf(newstr, 256, " is%d", scale); if ((strlen(newstr) + strlen(hdr->descrip)) < 80) @@ -5562,7 +5702,7 @@ int int12toint16(int U12) { } void nii_mask12bit(unsigned char *img, struct nifti_1_header *hdr, bool isSigned) { - //https://github.com/rordenlab/dcm2niix/issues/251 + // https://github.com/rordenlab/dcm2niix/issues/251 if (hdr->datatype != DT_INT16) return; int dim3to7 = 1; @@ -5573,18 +5713,18 @@ void nii_mask12bit(unsigned char *img, struct nifti_1_header *hdr, bool isSigned if (nVox < 1) return; int16_t *img16 = (int16_t *)img; - //issue 688 + // issue 688 if (isSigned) { for (int i = 0; i < nVox; i++) - img16[i] = int12toint16(img16[i]); //signed 12 bit data ranges from 0..4095, any other values are overflow - + img16[i] = int12toint16(img16[i]); // signed 12 bit data ranges from 0..4095, any other values are overflow + } else { for (int i = 0; i < nVox; i++) - img16[i] = img16[i] & 4095; //12 bit data ranges from 0..4095, any other values are overflow + img16[i] = img16[i] & 4095; // 12 bit data ranges from 0..4095, any other values are overflow } } -unsigned char * nii_uint16toFloat32(unsigned char *img, struct nifti_1_header *hdr, int isVerbose) { +unsigned char *nii_uint16toFloat32(unsigned char *img, struct nifti_1_header *hdr, int isVerbose) { if (hdr->datatype != DT_UINT16) return img; int dim3to7 = 1; @@ -5610,8 +5750,8 @@ unsigned char * nii_uint16toFloat32(unsigned char *img, struct nifti_1_header *h } // nii_uint16toFloat32() void nii_scale16bitSigned(unsigned char *img, struct nifti_1_header *hdr, int isVerbose) { - //lossless scaling of INT16 data: e.g. input with range -100...3200 and scl_slope=1 - // will be stored as -1000...32000 with scl_slope 0.1 + // lossless scaling of INT16 data: e.g. input with range -100...3200 and scl_slope=1 + // will be stored as -1000...32000 with scl_slope 0.1 if (hdr->datatype != DT_INT16) return; int dim3to7 = 1; @@ -5630,14 +5770,14 @@ void nii_scale16bitSigned(unsigned char *img, struct nifti_1_header *hdr, int is if (img16[i] > max16) max16 = img16[i]; } - int kMx = 32000; //actually 32767 - maybe a bit of padding for interpolation ringing + int kMx = 32000; // actually 32767 - maybe a bit of padding for interpolation ringing int scale = kMx / (int)max16; if (abs(min16) > max16) scale = kMx / (int)abs(min16); if (scale < 2) { if (isVerbose) printMessage("Sufficient 16-bit range: raw %d..%d\n", min16, max16); - return; //already uses dynamic range + return; // already uses dynamic range } hdr->scl_slope = hdr->scl_slope / scale; for (int i = 0; i < nVox; i++) @@ -5647,8 +5787,8 @@ void nii_scale16bitSigned(unsigned char *img, struct nifti_1_header *hdr, int is } void nii_scale16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr, int isVerbose) { - //lossless scaling of UINT16 data: e.g. input with range 0...3200 and scl_slope=1 - // will be stored as 0...64000 with scl_slope 0.05 + // lossless scaling of UINT16 data: e.g. input with range 0...3200 and scl_slope=1 + // will be stored as 0...64000 with scl_slope 0.05 if (hdr->datatype != DT_UINT16) return; int dim3to7 = 1; @@ -5663,12 +5803,12 @@ void nii_scale16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr, int for (int i = 0; i < nVox; i++) if (img16[i] > max16) max16 = img16[i]; - int kMx = 64000; //actually 65535 - maybe a bit of padding for interpolation ringing + int kMx = 64000; // actually 65535 - maybe a bit of padding for interpolation ringing int scale = kMx / (int)max16; if (scale < 2) { if (isVerbose > 0) printMessage("Sufficient unsigned 16-bit range: raw max %d\n", max16); - return; //already uses dynamic range + return; // already uses dynamic range } hdr->scl_slope = hdr->scl_slope / scale; for (int i = 0; i < nVox; i++) @@ -5680,7 +5820,7 @@ void nii_scale16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr, int #define UINT16_TO_INT16_IF_LOSSLESS #ifdef UINT16_TO_INT16_IF_LOSSLESS void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr, int isVerbose) { - //default NIfTI 16-bit is signed, set to unusual 16-bit unsigned if required... + // default NIfTI 16-bit is signed, set to unusual 16-bit unsigned if required... if (hdr->datatype != DT_UINT16) return; int dim3to7 = 1; @@ -5692,11 +5832,11 @@ void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr, int return; unsigned short *img16 = (unsigned short *)img; unsigned short max16 = img16[0]; - //clock_t start = clock(); + // clock_t start = clock(); for (int i = 0; i < nVox; i++) if (img16[i] > max16) max16 = img16[i]; - //printMessage("max16= %d vox=%d %fms\n",max16, nVox, ((double)(clock()-start))/1000); + // printMessage("max16= %d vox=%d %fms\n",max16, nVox, ((double)(clock()-start))/1000); if (max16 > 32767) { if (isVerbose > 0) printMessage("Note: 16-bit UNSIGNED integer image. Some tools will convert to 32-bit.\n"); @@ -5704,7 +5844,7 @@ void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr, int hdr->datatype = DT_INT16; printMessage("UINT16->INT16 Future release will change default. github.com/rordenlab/dcm2niix/issues/338\n"); } -} //nii_check16bitUnsigned() +} // nii_check16bitUnsigned() #else void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr, int isVerbose) { if (hdr->datatype != DT_UINT16) @@ -5715,13 +5855,13 @@ void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr, int } #endif -//void reportPos(struct TDICOMdata d1) { +// void reportPos(struct TDICOMdata d1) { // printMessage("Instance\t%d\t0020,0032\t%g\t%g\t%g\n", d1.imageNum, d1.patientPosition[1],d1.patientPosition[2],d1.patientPosition[3]); -//} +// } int siemensCtKludge(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata dcmList[]) { - //Siemens CT bug: when a user draws an open object graphics object onto a 2D slice this is appended as an additional image, - //regardless of slice position. These images do not report number of positions in the volume, so we need tedious leg work to detect + // Siemens CT bug: when a user draws an open object graphics object onto a 2D slice this is appended as an additional image, + // regardless of slice position. These images do not report number of positions in the volume, so we need tedious leg work to detect uint64_t indx0 = dcmSort[0].indx; if ((nConvert < 2) || (dcmList[indx0].manufacturer != kMANUFACTURER_SIEMENS) || (!isSameFloat(dcmList[indx0].TR, 0.0f))) return nConvert; @@ -5729,14 +5869,14 @@ int siemensCtKludge(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata d for (int i = 1; i < nConvert; i++) { float dx = intersliceDistance(dcmList[indx0], dcmList[dcmSort[i].indx]); if ((!isSameFloat(dx, 0.0f)) && (dx < prevDx)) { - //for (int j = 1; j < nConvert; j++) + // for (int j = 1; j < nConvert; j++) // reportPos(dcmList[dcmSort[j].indx]); printMessage("Slices skipped: image position not sequential, admonish your vendor (Siemens OOG?)\n"); return i; } prevDx = dx; } - return nConvert; //all images in sequential order + return nConvert; // all images in sequential order } // siemensCtKludge() void adjustOriginForNegativeTilt(struct nifti_1_header *hdr, float shiftPxY) { @@ -5749,8 +5889,8 @@ void adjustOriginForNegativeTilt(struct nifti_1_header *hdr, float shiftPxY) { if (hdr->qform_code > 0) { // Adjust the quaternion offsets using quatern_* and pixdim mat44 mat = nifti_quatern_to_mat44(hdr->quatern_b, hdr->quatern_c, hdr->quatern_d, - hdr->qoffset_x, hdr->qoffset_y, hdr->qoffset_z, - hdr->pixdim[1], hdr->pixdim[2], hdr->pixdim[3], hdr->pixdim[0]); + hdr->qoffset_x, hdr->qoffset_y, hdr->qoffset_z, + hdr->pixdim[1], hdr->pixdim[2], hdr->pixdim[3], hdr->pixdim[0]); hdr->qoffset_x -= shiftPxY * mat.m[1][0]; hdr->qoffset_y -= shiftPxY * mat.m[1][1]; hdr->qoffset_z -= shiftPxY * mat.m[1][2]; @@ -5758,7 +5898,7 @@ void adjustOriginForNegativeTilt(struct nifti_1_header *hdr, float shiftPxY) { } unsigned char *nii_saveNII3DtiltFloat32(char *niiFilename, struct nifti_1_header *hdr, unsigned char *im, struct TDCMopts opts, struct TDICOMdata d, float *sliceMMarray, float gantryTiltDeg, int manufacturer) { - //correct for gantry tilt - http://www.mathworks.com/matlabcentral/fileexchange/24458-dicom-gantry-tilt-correction + // correct for gantry tilt - http://www.mathworks.com/matlabcentral/fileexchange/24458-dicom-gantry-tilt-correction if (opts.isOnlyBIDS) return im; if (gantryTiltDeg == 0.0) @@ -5772,24 +5912,24 @@ unsigned char *nii_saveNII3DtiltFloat32(char *niiFilename, struct nifti_1_header return im; } printMessage("Gantry Tilt Correction is new: please validate conversions\n"); - float GNTtanPx = tan(gantryTiltDeg / (180 / M_PI)) / hdrIn.pixdim[2]; //tangent(degrees->radian) -//unintuitive step: reverse sign for negative gantry tilt, therefore -27deg == +27deg (why @!?#) -// seen in http://www.mathworks.com/matlabcentral/fileexchange/28141-gantry-detector-tilt-correction/content/gantry2.m -// also validated with actual data... + float GNTtanPx = tan(gantryTiltDeg / (180 / M_PI)) / hdrIn.pixdim[2]; // tangent(degrees->radian) +// unintuitive step: reverse sign for negative gantry tilt, therefore -27deg == +27deg (why @!?#) +// seen in http://www.mathworks.com/matlabcentral/fileexchange/28141-gantry-detector-tilt-correction/content/gantry2.m +// also validated with actual data... #ifndef newTilt - if (manufacturer == kMANUFACTURER_PHILIPS) //see 'Manix' example from Osirix + if (manufacturer == kMANUFACTURER_PHILIPS) // see 'Manix' example from Osirix GNTtanPx = -GNTtanPx; else if ((manufacturer == kMANUFACTURER_SIEMENS) && (gantryTiltDeg > 0.0)) GNTtanPx = -GNTtanPx; else if (manufacturer == kMANUFACTURER_GE) - ; //do nothing + ; // do nothing else if (gantryTiltDeg < 0.0) - GNTtanPx = -GNTtanPx; //see Toshiba examples from John Muschelli -#endif //newTilt + GNTtanPx = -GNTtanPx; // see Toshiba examples from John Muschelli +#endif // newTilt float *imIn32 = (float *)im; - //create new output image: larger due to skew - // compute how many pixels slice must be extended due to skew - int s = hdrIn.dim[3] - 1; //top slice + // create new output image: larger due to skew + // compute how many pixels slice must be extended due to skew + int s = hdrIn.dim[3] - 1; // top slice float maxSliceMM = fabs(s * hdrIn.pixdim[3]); if (sliceMMarray != NULL) maxSliceMM = fabs(sliceMMarray[s]); @@ -5804,7 +5944,7 @@ unsigned char *nii_saveNII3DtiltFloat32(char *niiFilename, struct nifti_1_header int nVox2D = hdr->dim[1] * hdr->dim[2]; unsigned char *imOut = (unsigned char *)malloc(nVox2D * hdrIn.dim[3] * 4); // *4 as 32-bits per voxel, sizeof(float) ); float *imOut32 = (float *)imOut; - //set surrounding voxels to padding (if present) or darkest observed value + // set surrounding voxels to padding (if present) or darkest observed value bool hasPixelPaddingValue = !isnan(d.pixelPaddingValue); float pixelPaddingValue; if (hasPixelPaddingValue) { @@ -5819,28 +5959,28 @@ unsigned char *nii_saveNII3DtiltFloat32(char *niiFilename, struct nifti_1_header } for (int v = 0; v < (nVox2D * hdrIn.dim[3]); v++) imOut32[v] = pixelPaddingValue; - //copy skewed voxels - for (int s = 0; s < hdrIn.dim[3]; s++) { //for each slice + // copy skewed voxels + for (int s = 0; s < hdrIn.dim[3]; s++) { // for each slice float sliceMM = s * hdrIn.pixdim[3]; if (sliceMMarray != NULL) - sliceMM = sliceMMarray[s]; //variable slice thicknesses - //sliceMM -= mmMidZ; //adjust so tilt relative to middle slice + sliceMM = sliceMMarray[s]; // variable slice thicknesses + // sliceMM -= mmMidZ; //adjust so tilt relative to middle slice if (GNTtanPx < 0) sliceMM -= maxSliceMM; float Offset = GNTtanPx * sliceMM; - float fracHi = ceil(Offset) - Offset; //ceil not floor since rI=r-Offset not rI=r+Offset + float fracHi = ceil(Offset) - Offset; // ceil not floor since rI=r-Offset not rI=r+Offset float fracLo = 1.0f - fracHi; - for (int r = 0; r < hdr->dim[2]; r++) { //for each row of output - float rI = (float)r - Offset; //input row + for (int r = 0; r < hdr->dim[2]; r++) { // for each row of output + float rI = (float)r - Offset; // input row if ((rI >= 0.0) && (rI < hdrIn.dim[2])) { int rLo = floor(rI); int rHi = rLo + 1; if (rHi >= hdrIn.dim[2]) rHi = rLo; - rLo = (rLo * hdrIn.dim[1]) + (s * nVox2DIn); //offset to start of row below - rHi = (rHi * hdrIn.dim[1]) + (s * nVox2DIn); //offset to start of row above - int rOut = (r * hdrIn.dim[1]) + (s * nVox2D); //offset to output row - for (int c = 0; c < hdrIn.dim[1]; c++) { //for each column + rLo = (rLo * hdrIn.dim[1]) + (s * nVox2DIn); // offset to start of row below + rHi = (rHi * hdrIn.dim[1]) + (s * nVox2DIn); // offset to start of row above + int rOut = (r * hdrIn.dim[1]) + (s * nVox2D); // offset to output row + for (int c = 0; c < hdrIn.dim[1]; c++) { // for each column float valLo = (float)imIn32[rLo + c]; float valHi = (float)imIn32[rHi + c]; if (hasPixelPaddingValue && (valLo == pixelPaddingValue || valHi == pixelPaddingValue)) { @@ -5850,13 +5990,13 @@ unsigned char *nii_saveNII3DtiltFloat32(char *niiFilename, struct nifti_1_header } else { imOut32[rOut + c] = round(valLo * fracLo + valHi * fracHi); } - } //for c (each column) - } //rI (input row) in range - } //for r (each row) - } //for s (each slice)*/ + } // for c (each column) + } // rI (input row) in range + } // for r (each row) + } // for s (each slice)*/ free(im); if (sliceMMarray != NULL) - return imOut; //we will save after correcting for variable slice thicknesses + return imOut; // we will save after correcting for variable slice thicknesses char niiFilenameTilt[2048] = {""}; strcat(niiFilenameTilt, niiFilename); strcat(niiFilenameTilt, "_Tilt"); @@ -5865,7 +6005,7 @@ unsigned char *nii_saveNII3DtiltFloat32(char *niiFilename, struct nifti_1_header } // nii_saveNII3DtiltFloat32() unsigned char *nii_saveNII3Dtilt(char *niiFilename, struct nifti_1_header *hdr, unsigned char *im, struct TDCMopts opts, struct TDICOMdata d, float *sliceMMarray, float gantryTiltDeg, int manufacturer) { - //correct for gantry tilt - http://www.mathworks.com/matlabcentral/fileexchange/24458-dicom-gantry-tilt-correction + // correct for gantry tilt - http://www.mathworks.com/matlabcentral/fileexchange/24458-dicom-gantry-tilt-correction if (opts.isOnlyBIDS) return im; if (gantryTiltDeg == 0.0) @@ -5881,24 +6021,24 @@ unsigned char *nii_saveNII3Dtilt(char *niiFilename, struct nifti_1_header *hdr, return im; } printMessage("Gantry Tilt Correction is new: please validate conversions\n"); - float GNTtanPx = tan(gantryTiltDeg / (180 / M_PI)) / hdrIn.pixdim[2]; //tangent(degrees->radian) -//unintuitive step: reverse sign for negative gantry tilt, therefore -27deg == +27deg (why @!?#) -// seen in http://www.mathworks.com/matlabcentral/fileexchange/28141-gantry-detector-tilt-correction/content/gantry2.m -// also validated with actual data... + float GNTtanPx = tan(gantryTiltDeg / (180 / M_PI)) / hdrIn.pixdim[2]; // tangent(degrees->radian) +// unintuitive step: reverse sign for negative gantry tilt, therefore -27deg == +27deg (why @!?#) +// seen in http://www.mathworks.com/matlabcentral/fileexchange/28141-gantry-detector-tilt-correction/content/gantry2.m +// also validated with actual data... #ifndef newTilt - if (manufacturer == kMANUFACTURER_PHILIPS) //see 'Manix' example from Osirix + if (manufacturer == kMANUFACTURER_PHILIPS) // see 'Manix' example from Osirix GNTtanPx = -GNTtanPx; else if ((manufacturer == kMANUFACTURER_SIEMENS) && (gantryTiltDeg > 0.0)) GNTtanPx = -GNTtanPx; else if (manufacturer == kMANUFACTURER_GE) - ; //do nothing + ; // do nothing else if (gantryTiltDeg < 0.0) - GNTtanPx = -GNTtanPx; //see Toshiba examples from John Muschelli -#endif //newTilt + GNTtanPx = -GNTtanPx; // see Toshiba examples from John Muschelli +#endif // newTilt short *imIn16 = (short *)im; - //create new output image: larger due to skew - // compute how many pixels slice must be extended due to skew - int s = hdrIn.dim[3] - 1; //top slice + // create new output image: larger due to skew + // compute how many pixels slice must be extended due to skew + int s = hdrIn.dim[3] - 1; // top slice float maxSliceMM = fabs(s * hdrIn.pixdim[3]); if (sliceMMarray != NULL) maxSliceMM = fabs(sliceMMarray[s]); @@ -5914,7 +6054,7 @@ unsigned char *nii_saveNII3Dtilt(char *niiFilename, struct nifti_1_header *hdr, int nVox2D = hdr->dim[1] * hdr->dim[2]; unsigned char *imOut = (unsigned char *)malloc(nVox2D * hdrIn.dim[3] * 2); // *2 as 16-bits per voxel, sizeof( short) ); short *imOut16 = (short *)imOut; - //set surrounding voxels to padding (if present) or darkest observed value + // set surrounding voxels to padding (if present) or darkest observed value bool hasPixelPaddingValue = !isnan(d.pixelPaddingValue); short pixelPaddingValue; if (hasPixelPaddingValue) { @@ -5929,28 +6069,28 @@ unsigned char *nii_saveNII3Dtilt(char *niiFilename, struct nifti_1_header *hdr, } for (int v = 0; v < (nVox2D * hdrIn.dim[3]); v++) imOut16[v] = pixelPaddingValue; - //copy skewed voxels - for (int s = 0; s < hdrIn.dim[3]; s++) { //for each slice + // copy skewed voxels + for (int s = 0; s < hdrIn.dim[3]; s++) { // for each slice float sliceMM = s * hdrIn.pixdim[3]; if (sliceMMarray != NULL) - sliceMM = sliceMMarray[s]; //variable slice thicknesses - //sliceMM -= mmMidZ; //adjust so tilt relative to middle slice + sliceMM = sliceMMarray[s]; // variable slice thicknesses + // sliceMM -= mmMidZ; //adjust so tilt relative to middle slice if (GNTtanPx < 0) sliceMM -= maxSliceMM; float Offset = GNTtanPx * sliceMM; - float fracHi = ceil(Offset) - Offset; //ceil not floor since rI=r-Offset not rI=r+Offset + float fracHi = ceil(Offset) - Offset; // ceil not floor since rI=r-Offset not rI=r+Offset float fracLo = 1.0f - fracHi; - for (int r = 0; r < hdr->dim[2]; r++) { //for each row of output - float rI = (float)r - Offset; //input row + for (int r = 0; r < hdr->dim[2]; r++) { // for each row of output + float rI = (float)r - Offset; // input row if ((rI >= 0.0) && (rI < hdrIn.dim[2])) { int rLo = floor(rI); int rHi = rLo + 1; if (rHi >= hdrIn.dim[2]) rHi = rLo; - rLo = (rLo * hdrIn.dim[1]) + (s * nVox2DIn); //offset to start of row below - rHi = (rHi * hdrIn.dim[1]) + (s * nVox2DIn); //offset to start of row above - int rOut = (r * hdrIn.dim[1]) + (s * nVox2D); //offset to output row - for (int c = 0; c < hdrIn.dim[1]; c++) { //for each row + rLo = (rLo * hdrIn.dim[1]) + (s * nVox2DIn); // offset to start of row below + rHi = (rHi * hdrIn.dim[1]) + (s * nVox2DIn); // offset to start of row above + int rOut = (r * hdrIn.dim[1]) + (s * nVox2D); // offset to output row + for (int c = 0; c < hdrIn.dim[1]; c++) { // for each row short valLo = imIn16[rLo + c]; short valHi = imIn16[rHi + c]; if (hasPixelPaddingValue && (valLo == pixelPaddingValue || valHi == pixelPaddingValue)) { @@ -5960,13 +6100,13 @@ unsigned char *nii_saveNII3Dtilt(char *niiFilename, struct nifti_1_header *hdr, } else { imOut16[rOut + c] = round((((float)valLo) * fracLo) + ((float)valHi) * fracHi); } - } //for c (each column) - } //rI (input row) in range - } //for r (each row) - } //for s (each slice)*/ + } // for c (each column) + } // rI (input row) in range + } // for r (each row) + } // for s (each slice)*/ free(im); if (sliceMMarray != NULL) - return imOut; //we will save after correcting for variable slice thicknesses + return imOut; // we will save after correcting for variable slice thicknesses char niiFilenameTilt[2048] = {""}; strcat(niiFilenameTilt, niiFilename); strcat(niiFilenameTilt, "_Tilt"); @@ -5975,8 +6115,8 @@ unsigned char *nii_saveNII3Dtilt(char *niiFilename, struct nifti_1_header *hdr, } // nii_saveNII3Dtilt() int nii_saveNII3Deq(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, struct TDCMopts opts, struct TDICOMdata d, float *sliceMMarray) { - //convert image with unequal slice distances to equal slice distances - //sliceMMarray = 0.0 3.0 6.0 12.0 22.0 <- ascending distance from first slice + // convert image with unequal slice distances to equal slice distances + // sliceMMarray = 0.0 3.0 6.0 12.0 22.0 <- ascending distance from first slice if (opts.isOnlyBIDS) return EXIT_SUCCESS; int nVox2D = hdr.dim[1] * hdr.dim[2]; @@ -6007,7 +6147,7 @@ int nii_saveNII3Deq(char *niiFilename, struct nifti_1_header hdr, unsigned char slices = 2 * hdr.dim[3]; mn = (sliceMMarray[hdr.dim[3] - 1]) / (slices - 1); } - //printMessage("-->%g mn slices %d orig %d\n", mn, slices, hdr.dim[3]); + // printMessage("-->%g mn slices %d orig %d\n", mn, slices, hdr.dim[3]); if (slices < 3) return EXIT_FAILURE; struct nifti_1_header hdrX = hdr; @@ -6015,7 +6155,7 @@ int nii_saveNII3Deq(char *niiFilename, struct nifti_1_header hdr, unsigned char hdrX.pixdim[3] = mn; if ((hdr.pixdim[3] != 0.0) && (hdr.pixdim[3] != hdrX.pixdim[3])) { float Scale = hdrX.pixdim[3] / hdr.pixdim[3]; - //to do: do I change srow_z or srow_x[2], srow_y[2], srow_z[2], + // to do: do I change srow_z or srow_x[2], srow_y[2], srow_z[2], hdrX.srow_z[0] = hdr.srow_z[0] * Scale; hdrX.srow_z[1] = hdr.srow_z[1] * Scale; hdrX.srow_z[2] = hdr.srow_z[2] * Scale; @@ -6023,11 +6163,11 @@ int nii_saveNII3Deq(char *niiFilename, struct nifti_1_header hdr, unsigned char unsigned char *imX; if (hdr.datatype == DT_FLOAT32) { float *im32 = (float *)im; - imX = (unsigned char *)malloc((nVox2D * slices) * 4); //sizeof(float) + imX = (unsigned char *)malloc((nVox2D * slices) * 4); // sizeof(float) float *imX32 = (float *)imX; for (int s = 0; s < slices; s++) { - float sliceXmm = s * mn; //distance from first slice - int sliceXi = (s * nVox2D); //offset for this slice + float sliceXmm = s * mn; // distance from first slice + int sliceXi = (s * nVox2D); // offset for this slice int sHi = 0; while ((sHi < (hdr.dim[3] - 1)) && (sliceMMarray[sHi] < sliceXmm)) sHi += 1; @@ -6038,25 +6178,25 @@ int nii_saveNII3Deq(char *niiFilename, struct nifti_1_header hdr, unsigned char float mmLo = sliceMMarray[sLo]; sLo = sLo * nVox2D; sHi = sHi * nVox2D; - if ((mmHi == mmLo) || (sliceXmm > mmHi)) { //select only from upper slice TPX - //for (int v=0; v < nVox2D; v++) - // imX16[sliceXi+v] = im16[sHi+v]; - memcpy(&imX32[sliceXi], &im32[sHi], nVox2D * sizeof(float)); //memcpy( dest, src, bytes) + if ((mmHi == mmLo) || (sliceXmm > mmHi)) { // select only from upper slice TPX + // for (int v=0; v < nVox2D; v++) + // imX16[sliceXi+v] = im16[sHi+v]; + memcpy(&imX32[sliceXi], &im32[sHi], nVox2D * sizeof(float)); // memcpy( dest, src, bytes) } else { float fracHi = (sliceXmm - mmLo) / (mmHi - mmLo); float fracLo = 1.0 - fracHi; - //weight between two slices + // weight between two slices for (int v = 0; v < nVox2D; v++) imX32[sliceXi + v] = round(((float)im32[sLo + v] * fracLo) + (float)im32[sHi + v] * fracHi); } } } else if (hdr.datatype == DT_INT16) { short *im16 = (short *)im; - imX = (unsigned char *)malloc((nVox2D * slices) * 2); //sizeof( short) ); + imX = (unsigned char *)malloc((nVox2D * slices) * 2); // sizeof( short) ); short *imX16 = (short *)imX; for (int s = 0; s < slices; s++) { - float sliceXmm = s * mn; //distance from first slice - int sliceXi = (s * nVox2D); //offset for this slice + float sliceXmm = s * mn; // distance from first slice + int sliceXi = (s * nVox2D); // offset for this slice int sHi = 0; while ((sHi < (hdr.dim[3] - 1)) && (sliceMMarray[sHi] < sliceXmm)) sHi += 1; @@ -6067,14 +6207,14 @@ int nii_saveNII3Deq(char *niiFilename, struct nifti_1_header hdr, unsigned char float mmLo = sliceMMarray[sLo]; sLo = sLo * nVox2D; sHi = sHi * nVox2D; - if ((mmHi == mmLo) || (sliceXmm > mmHi)) { //select only from upper slice TPX - //for (int v=0; v < nVox2D; v++) - // imX16[sliceXi+v] = im16[sHi+v]; - memcpy(&imX16[sliceXi], &im16[sHi], nVox2D * sizeof(unsigned short)); //memcpy( dest, src, bytes) + if ((mmHi == mmLo) || (sliceXmm > mmHi)) { // select only from upper slice TPX + // for (int v=0; v < nVox2D; v++) + // imX16[sliceXi+v] = im16[sHi+v]; + memcpy(&imX16[sliceXi], &im16[sHi], nVox2D * sizeof(unsigned short)); // memcpy( dest, src, bytes) } else { float fracHi = (sliceXmm - mmLo) / (mmHi - mmLo); float fracLo = 1.0 - fracHi; - //weight between two slices + // weight between two slices for (int v = 0; v < nVox2D; v++) imX16[sliceXi + v] = round(((float)im16[sLo + v] * fracLo) + (float)im16[sHi + v] * fracHi); } @@ -6082,10 +6222,10 @@ int nii_saveNII3Deq(char *niiFilename, struct nifti_1_header hdr, unsigned char } else { if (hdr.datatype == DT_RGB24) nVox2D = nVox2D * 3; - imX = (unsigned char *)malloc((nVox2D * slices) * 2); //sizeof( short) ); + imX = (unsigned char *)malloc((nVox2D * slices) * 2); // sizeof( short) ); for (int s = 0; s < slices; s++) { - float sliceXmm = s * mn; //distance from first slice - int sliceXi = (s * nVox2D); //offset for this slice + float sliceXmm = s * mn; // distance from first slice + int sliceXi = (s * nVox2D); // offset for this slice int sHi = 0; while ((sHi < (hdr.dim[3] - 1)) && (sliceMMarray[sHi] < sliceXmm)) sHi += 1; @@ -6096,11 +6236,11 @@ int nii_saveNII3Deq(char *niiFilename, struct nifti_1_header hdr, unsigned char float mmLo = sliceMMarray[sLo]; sLo = sLo * nVox2D; sHi = sHi * nVox2D; - if ((mmHi == mmLo) || (sliceXmm > mmHi)) { //select only from upper slice TPX - memcpy(&imX[sliceXi], &im[sHi], nVox2D); //memcpy( dest, src, bytes) + if ((mmHi == mmLo) || (sliceXmm > mmHi)) { // select only from upper slice TPX + memcpy(&imX[sliceXi], &im[sHi], nVox2D); // memcpy( dest, src, bytes) } else { float fracHi = (sliceXmm - mmLo) / (mmHi - mmLo); - float fracLo = 1.0 - fracHi; //weight between two slices + float fracLo = 1.0 - fracHi; // weight between two slices for (int v = 0; v < nVox2D; v++) imX[sliceXi + v] = round(((float)im[sLo + v] * fracLo) + (float)im[sHi + v] * fracHi); } @@ -6115,7 +6255,7 @@ int nii_saveNII3Deq(char *niiFilename, struct nifti_1_header hdr, unsigned char } // nii_saveNII3Deq() float PhilipsPreciseVal(float lPV, float lRS, float lRI, float lSS) { - if ((lRS * lSS) == 0) //avoid divide by zero + if ((lRS * lSS) == 0) // avoid divide by zero return 0.0; else return (lPV * lRS + lRI) / (lRS * lSS); @@ -6123,9 +6263,9 @@ float PhilipsPreciseVal(float lPV, float lRS, float lRI, float lSS) { void PhilipsPrecise(struct TDICOMdata *d, bool isPhilipsFloatNotDisplayScaling, struct nifti_1_header *h, int verbose) { if (d->manufacturer != kMANUFACTURER_PHILIPS) - return; //not Philips + return; // not Philips if (d->isScaleVariesEnh) - return; //issue363 rescaled before slice reordering + return; // issue363 rescaled before slice reordering /* if (!isSameFloatGE(0.0, d->RWVScale)) { //https://github.com/rordenlab/dcm2niix/issues/493 h->scl_slope = d->RWVScale; @@ -6140,8 +6280,8 @@ void PhilipsPrecise(struct TDICOMdata *d, bool isPhilipsFloatNotDisplayScaling, return; }*/ if (d->intenScalePhilips == 0) - return; //no Philips Precise - //we will report calibrated "FP" values http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3998685/ + return; // no Philips Precise + // we will report calibrated "FP" values http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3998685/ float l0 = PhilipsPreciseVal(0, d->intenScale, d->intenIntercept, d->intenScalePhilips); float l1 = PhilipsPreciseVal(1, d->intenScale, d->intenIntercept, d->intenScalePhilips); float intenScaleP = d->intenScale; @@ -6151,7 +6291,7 @@ void PhilipsPrecise(struct TDICOMdata *d, bool isPhilipsFloatNotDisplayScaling, intenScaleP = l1 - l0; } if (isSameFloat(d->intenIntercept, intenInterceptP) && isSameFloat(d->intenScale, intenScaleP)) - return; //same result for both methods: nothing to do or report! + return; // same result for both methods: nothing to do or report! printMessage("Philips Scaling Values RS:RI:SS = %g:%g:%g (see PMC3998685)\n", d->intenScale, d->intenIntercept, d->intenScalePhilips); if (verbose > 0) { printMessage(" R = raw value, P = precise value, D = displayed value\n"); @@ -6160,26 +6300,26 @@ void PhilipsPrecise(struct TDICOMdata *d, bool isPhilipsFloatNotDisplayScaling, printMessage(" D scl_slope:scl_inter = %g:%g\n", d->intenScale, d->intenIntercept); printMessage(" P scl_slope:scl_inter = %g:%g\n", intenScaleP, intenInterceptP); } - //#define myUsePhilipsPrecise + // #define myUsePhilipsPrecise if (isPhilipsFloatNotDisplayScaling) { if (verbose > 0) printMessage(" Using P values ('-p n ' for D values)\n"); - //to change DICOM: - //d->intenScale = intenScaleP; - //d->intenIntercept = intenInterceptP; - //to change NIfTI + // to change DICOM: + // d->intenScale = intenScaleP; + // d->intenIntercept = intenInterceptP; + // to change NIfTI h->scl_slope = intenScaleP; h->scl_inter = intenInterceptP; - d->intenScalePhilips = 0; //so we never run this TWICE! + d->intenScalePhilips = 0; // so we never run this TWICE! } else if (verbose > 0) printMessage(" Using D values ('-p y ' for P values)\n"); -} //PhilipsPrecise() +} // PhilipsPrecise() void smooth1D(int num, double *im) { if (num < 3) return; double *src = (double *)malloc(sizeof(double) * num); - memcpy(&src[0], &im[0], num * sizeof(double)); //memcpy( dest, src, bytes) + memcpy(&src[0], &im[0], num * sizeof(double)); // memcpy( dest, src, bytes) double frac = 0.25; for (int i = 1; i < (num - 1); i++) im[i] = (src[i - 1] * frac) + (src[i] * frac * 2) + (src[i + 1] * frac); @@ -6187,7 +6327,7 @@ void smooth1D(int num, double *im) { } // smooth1D() int nii_saveCrop(char *niiFilename, struct nifti_1_header hdr, unsigned char *im, struct TDCMopts opts, struct TDICOMdata d) { - //remove excess neck slices - assumes output of nii_setOrtho() + // remove excess neck slices - assumes output of nii_setOrtho() if (opts.isOnlyBIDS) return EXIT_SUCCESS; int nVox2D = hdr.dim[1] * hdr.dim[2]; @@ -6199,9 +6339,9 @@ int nii_saveCrop(char *niiFilename, struct nifti_1_header hdr, unsigned char *im } short *im16 = (short *)im; unsigned short *imu16 = (unsigned short *)im; - float kThresh = 0.09; //more than 9% of max brightness + float kThresh = 0.09; // more than 9% of max brightness int ventralCrop = 0; - //find max value for each slice + // find max value for each slice int slices = hdr.dim[3]; double *sliceSums = (double *)malloc(sizeof(double) * slices); double maxSliceVal = 0.0; @@ -6223,8 +6363,8 @@ int nii_saveCrop(char *niiFilename, struct nifti_1_header hdr, unsigned char *im } smooth1D(slices, sliceSums); for (int i = 0; i < slices; i++) - sliceSums[i] = sliceSums[i] / maxSliceVal; //so brightest slice has value 1 - //dorsal crop: eliminate slices with more than 5% brightness + sliceSums[i] = sliceSums[i] / maxSliceVal; // so brightest slice has value 1 + // dorsal crop: eliminate slices with more than 5% brightness int dorsalCrop; for (dorsalCrop = (slices - 1); dorsalCrop >= 1; dorsalCrop--) if (sliceSums[dorsalCrop - 1] > kThresh) @@ -6237,25 +6377,25 @@ int nii_saveCrop(char *niiFilename, struct nifti_1_header hdr, unsigned char *im ventralCrop = dorsalCrop - round(kMaxDVmm / hdr.pixdim[3]); if (ventralCrop < 0) ventralCrop = 0; - //apply crop + // apply crop printMessage(" Cropping from slice %d to %d (of %d)\n", ventralCrop, dorsalCrop, slices); struct nifti_1_header hdrX = hdr; slices = dorsalCrop - ventralCrop + 1; hdrX.dim[3] = slices; - //translate origin to account for missing slices + // translate origin to account for missing slices hdrX.srow_x[3] += hdr.srow_x[2] * ventralCrop; hdrX.srow_y[3] += hdr.srow_y[2] * ventralCrop; hdrX.srow_z[3] += hdr.srow_z[2] * ventralCrop; - //convert data + // convert data unsigned char *imX; - imX = (unsigned char *)malloc((nVox2D * slices) * 2); //sizeof( short) ); + imX = (unsigned char *)malloc((nVox2D * slices) * 2); // sizeof( short) ); short *imX16 = (short *)imX; for (int s = 0; s < slices; s++) { int sIn = s + ventralCrop; int sOut = s; sOut = sOut * nVox2D; sIn = sIn * nVox2D; - memcpy(&imX16[sOut], &im16[sIn], nVox2D * sizeof(unsigned short)); //memcpy( dest, src, bytes) + memcpy(&imX16[sOut], &im16[sIn], nVox2D * sizeof(unsigned short)); // memcpy( dest, src, bytes) } char niiFilenameCrop[2048] = {""}; strcat(niiFilenameCrop, niiFilename); @@ -6266,7 +6406,7 @@ int nii_saveCrop(char *niiFilename, struct nifti_1_header hdr, unsigned char *im } // nii_saveCrop() double dicomTimeToSec(double dicomTime) { - //convert HHMMSS to seconds, 135300.024 -> 135259.731 are 0.293 sec apart + // convert HHMMSS to seconds, 135300.024 -> 135259.731 are 0.293 sec apart char acqTimeBuf[64]; snprintf(acqTimeBuf, sizeof acqTimeBuf, "%+013.5f", (double)dicomTime); int ahour, amin; @@ -6280,10 +6420,10 @@ double dicomTimeToSec(double dicomTime) { double acquisitionTimeDifference(struct TDICOMdata *d1, struct TDICOMdata *d2) { if (d1->acquisitionDate != d2->acquisitionDate) - return -1; //to do: scans running across midnight + return -1; // to do: scans running across midnight double sec1 = dicomTimeToSec(d1->acquisitionTime); double sec2 = dicomTimeToSec(d2->acquisitionTime); - //printMessage("%g\n",d2->acquisitionTime); + // printMessage("%g\n",d2->acquisitionTime); if ((sec1 < 0) || (sec2 < 0)) return -1; return (sec2 - sec1); @@ -6291,9 +6431,9 @@ double acquisitionTimeDifference(struct TDICOMdata *d1, struct TDICOMdata *d2) { void checkDateTimeOrder(struct TDICOMdata *d, struct TDICOMdata *d1) { if (d->acquisitionDate < d1->acquisitionDate) - return; //d1 occurred on later date + return; // d1 occurred on later date if (d->acquisitionTime <= d1->acquisitionTime) - return; //d1 occurred on later (or same) time + return; // d1 occurred on later (or same) time if (d->imageNum > d1->imageNum) printWarning("Images not sorted in ascending instance number (0020,0013)\n"); else @@ -6301,28 +6441,28 @@ void checkDateTimeOrder(struct TDICOMdata *d, struct TDICOMdata *d1) { } void checkSliceTiming(struct TDICOMdata *d, struct TDICOMdata *d1, int verbose, int isForceSliceTimeHHMMSS) { - //detect images with slice timing errors. https://github.com/rordenlab/dcm2niix/issues/126 - //modified 20190704: this function now ensures all slice times are in msec + // detect images with slice timing errors. https://github.com/rordenlab/dcm2niix/issues/126 + // modified 20190704: this function now ensures all slice times are in msec if ((d->TR < 0.0) || (d->CSA.sliceTiming[0] < 0.0)) - return; //no slice timing + return; // no slice timing if (d->manufacturer == kMANUFACTURER_PHILIPS) - return; //Philips does not provide slice timing details in DICOM + return; // Philips does not provide slice timing details in DICOM if (d->manufacturer == kMANUFACTURER_GE) - return; //compute directly from Protocol Block + return; // compute directly from Protocol Block if (d->modality == kMODALITY_PT) - return; //issue407 + return; // issue407 int nSlices = 0; while ((nSlices < kMaxEPI3D) && (d->CSA.sliceTiming[nSlices] >= 0.0)) nSlices++; if (nSlices < 2) return; - if (d->CSA.sliceTiming[kMaxEPI3D - 1] < -1.0) //the value -2.0 is used as a flag for negative MosaicRefAcqTimes in checkSliceTimes(), see issue 271 + if (d->CSA.sliceTiming[kMaxEPI3D - 1] < -1.0) // the value -2.0 is used as a flag for negative MosaicRefAcqTimes in checkSliceTimes(), see issue 271 printWarning("Adjusting for negative MosaicRefAcqTimes (issue 271).\n"); bool isSliceTimeHHMMSS = (d->manufacturer == kMANUFACTURER_UIH); if (isForceSliceTimeHHMMSS) isSliceTimeHHMMSS = true; - //if (d->isXA10A) isSliceTimeHHMMSS = true; //for XA10 use TimeAfterStart 0x0021,0x1104 -> Siemens de-identification can corrupt acquisition ties https://github.com/rordenlab/dcm2niix/issues/236 - if (isSliceTimeHHMMSS) { //handle midnight crossing + // if (d->isXA10A) isSliceTimeHHMMSS = true; //for XA10 use TimeAfterStart 0x0021,0x1104 -> Siemens de-identification can corrupt acquisition ties https://github.com/rordenlab/dcm2niix/issues/236 + if (isSliceTimeHHMMSS) { // handle midnight crossing for (int i = 0; i < nSlices; i++) d->CSA.sliceTiming[i] = dicomTimeToSec(d->CSA.sliceTiming[i]); float minT = d->CSA.sliceTiming[0]; @@ -6333,11 +6473,11 @@ void checkSliceTiming(struct TDICOMdata *d, struct TDICOMdata *d1, int verbose, if (d->CSA.sliceTiming[i] < maxT) maxT = d->CSA.sliceTiming[i]; } - //printf("%d %g ---> %g..%g\n", nSlices, d->TR, minT, maxT); + // printf("%d %g ---> %g..%g\n", nSlices, d->TR, minT, maxT); float kMidnightSec = 86400; float kNoonSec = 43200; - if ((maxT - minT) > kNoonSec) { //volume started before midnight but ended next day! - //identify and fix 'Cinderella error' where clock resets at midnight: untested + if ((maxT - minT) > kNoonSec) { // volume started before midnight but ended next day! + // identify and fix 'Cinderella error' where clock resets at midnight: untested printWarning("Acquisition crossed midnight: check slice timing\n"); for (int i = 0; i < nSlices; i++) if (d->CSA.sliceTiming[i] > kNoonSec) @@ -6349,7 +6489,7 @@ void checkSliceTiming(struct TDICOMdata *d, struct TDICOMdata *d1, int verbose, } for (int i = 0; i < nSlices; i++) d->CSA.sliceTiming[i] = d->CSA.sliceTiming[i] - minT; - } //XA10/UIH: HHMMSS -> Sec + } // XA10/UIH: HHMMSS -> Sec float minT = d->CSA.sliceTiming[0]; float maxT = minT; for (int i = 0; i < kMaxEPI3D; i++) { @@ -6360,28 +6500,28 @@ void checkSliceTiming(struct TDICOMdata *d, struct TDICOMdata *d1, int verbose, if (d->CSA.sliceTiming[i] > maxT) maxT = d->CSA.sliceTiming[i]; } - if (isSliceTimeHHMMSS) //convert HHMMSS to msec + if (isSliceTimeHHMMSS) // convert HHMMSS to msec for (int i = 0; i < kMaxEPI3D; i++) d->CSA.sliceTiming[i] = dicomTimeToSec(d->CSA.sliceTiming[i]) * 1000.0; - float TRms = d->TR; //d->TR in msec! + float TRms = d->TR; // d->TR in msec! if ((minT != maxT) && (maxT <= TRms)) { if (verbose != 0) printMessage("Slice timing range appears reasonable (range %g..%g, TR=%g ms)\n", minT, maxT, TRms); - return; //looks fine + return; // looks fine } if ((minT == maxT) && (d->is3DAcq)) - return; //fine: 3D EPI + return; // fine: 3D EPI if ((minT == maxT) && (d->CSA.multiBandFactor == d->CSA.mosaicSlices)) - return; //fine: all slices single excitation + return; // fine: all slices single excitation if ((strlen(d->seriesDescription) > 0) && (strstr(d->seriesDescription, "SBRef") != NULL)) - return; //fine: single-band calibration data, the slice timing WILL exceed the TR + return; // fine: single-band calibration data, the slice timing WILL exceed the TR if (verbose > 1) printMessage("Slice timing range of first volume: range %g..%g, TR=%g ms)\n", minT, maxT, TRms); - //check if 2nd image has valid slice timing + // check if 2nd image has valid slice timing float minT1 = d1->CSA.sliceTiming[0]; float maxT1 = minT1; for (int i = 0; i < nSlices; i++) { - //if (d1->CSA.sliceTiming[i] < 0.0) break; + // if (d1->CSA.sliceTiming[i] < 0.0) break; if (d1->CSA.sliceTiming[i] < minT1) minT1 = d1->CSA.sliceTiming[i]; if (d1->CSA.sliceTiming[i] > maxT1) @@ -6389,16 +6529,16 @@ void checkSliceTiming(struct TDICOMdata *d, struct TDICOMdata *d1, int verbose, } if (verbose > 1) printMessage("Slice timing range of 2nd volume: range %g..%g, TR=%g ms)\n", minT, maxT, TRms); - if ((minT1 < maxT1) && (minT1 > 0.0) && ((maxT1 - minT1) <= TRms)) { //issue 429: 2nd volume may not start from zero + if ((minT1 < maxT1) && (minT1 > 0.0) && ((maxT1 - minT1) <= TRms)) { // issue 429: 2nd volume may not start from zero for (int i = 0; i < nSlices; i++) d1->CSA.sliceTiming[i] -= minT1; maxT1 -= minT1; minT1 -= minT1; } if ((minT1 < 0.0) && (d->rtia_timerGE >= 0.0)) - return; //use rtia timer - if (minT1 < 0.0) { //https://github.com/neurolabusc/MRIcroGL/issues/31 - if (d->isDerived) { //slice timing not relevant for derived data, values mangled with Siemens XA30 + return; // use rtia timer + if (minT1 < 0.0) { // https://github.com/neurolabusc/MRIcroGL/issues/31 + if (d->isDerived) { // slice timing not relevant for derived data, values mangled with Siemens XA30 d->CSA.sliceTiming[0] = -1.0; return; } @@ -6406,11 +6546,11 @@ void checkSliceTiming(struct TDICOMdata *d, struct TDICOMdata *d1, int verbose, printWarning("Siemens MoCo? Bogus slice timing (range %g..%g, TR=%g seconds)\n", minT1, maxT1, TRms); return; } - if ((minT1 == maxT1) || (maxT1 >= TRms)) { //both first and second image corrupted + if ((minT1 == maxT1) || (maxT1 >= TRms)) { // both first and second image corrupted printWarning("Slice timing appears corrupted (range %g..%g, TR=%g ms)\n", minT1, maxT1, TRms); return; } - //1st image corrupted, but 2nd looks ok - substitute values from 2nd image + // 1st image corrupted, but 2nd looks ok - substitute values from 2nd image for (int i = 0; i < kMaxEPI3D; i++) { d->CSA.sliceTiming[i] = d1->CSA.sliceTiming[i]; if (d1->CSA.sliceTiming[i] < 0.0) @@ -6418,45 +6558,45 @@ void checkSliceTiming(struct TDICOMdata *d, struct TDICOMdata *d1, int verbose, } d->CSA.multiBandFactor = d1->CSA.multiBandFactor; printMessage("CSA slice timing based on 2nd volume, 1st volume corrupted (CMRR bug, range %g..%g, TR=%g ms)\n", minT, maxT, TRms); -} //checkSliceTiming() +} // checkSliceTiming() void setMultiBandFactor(int dim3, uint64_t indx0, struct TDICOMdata *dcmList) { - float mn = dcmList[indx0].CSA.sliceTiming[0]; - //first pass: find minimum - for (int v = 0; v < dim3; v++) - mn = fminf(dcmList[indx0].CSA.sliceTiming[v],mn); - //second pass: all times relative to min (i.e. make min = 0) - int mb = 0; - for (int v = 0; v < dim3; v++) { - dcmList[indx0].CSA.sliceTiming[v] -= mn; - if (isSameFloatGE(dcmList[indx0].CSA.sliceTiming[v], 0.0)) - mb++; - } - if ((dcmList[indx0].CSA.multiBandFactor < 2) && (mb > 1) && (mb < dim3)) - dcmList[indx0].CSA.multiBandFactor = mb; + float mn = dcmList[indx0].CSA.sliceTiming[0]; + // first pass: find minimum + for (int v = 0; v < dim3; v++) + mn = fminf(dcmList[indx0].CSA.sliceTiming[v], mn); + // second pass: all times relative to min (i.e. make min = 0) + int mb = 0; + for (int v = 0; v < dim3; v++) { + dcmList[indx0].CSA.sliceTiming[v] -= mn; + if (isSameFloatGE(dcmList[indx0].CSA.sliceTiming[v], 0.0)) + mb++; + } + if ((dcmList[indx0].CSA.multiBandFactor < 2) && (mb > 1) && (mb < dim3)) + dcmList[indx0].CSA.multiBandFactor = mb; } // setMultiBandFactor() void sliceTimingXA(struct TDCMsort *dcmSort, struct TDICOMdata *dcmList, struct nifti_1_header *hdr, int verbose, const char *filename, int nConvert) { - //Siemens XA10 slice timing - // Ignore first volume: For an example of erroneous first volume timing, see series 10 (Functional_w_SMS=3) https://github.com/rordenlab/dcm2niix/issues/240 - // an alternative would be to use 0018,9074 - this would need to be converted from DT to Secs, and is scrambled if de-identifies data see enhanced de-identified series 26 from issue 236 - uint64_t indx0 = dcmSort[0].indx; //first volume + // Siemens XA10 slice timing + // Ignore first volume: For an example of erroneous first volume timing, see series 10 (Functional_w_SMS=3) https://github.com/rordenlab/dcm2niix/issues/240 + // an alternative would be to use 0018,9074 - this would need to be converted from DT to Secs, and is scrambled if de-identifies data see enhanced de-identified series 26 from issue 236 + uint64_t indx0 = dcmSort[0].indx; // first volume if ((!dcmList[indx0].isXA10A) || (hdr->dim[3] < 1) || (hdr->dim[4] < 1)) return; if ((nConvert == (hdr->dim[3] * hdr->dim[4])) && (hdr->dim[3] < (kMaxEPI3D - 1)) && (hdr->dim[3] > 1)) { - //XA11 2D classic: nb XA30 in `MFSPLIT` will save each 3D volume from 4D timeseries as a unique series number! + // XA11 2D classic: nb XA30 in `MFSPLIT` will save each 3D volume from 4D timeseries as a unique series number! for (int v = 0; v < hdr->dim[3]; v++) dcmList[indx0].CSA.sliceTiming[v] = dcmList[dcmSort[v].indx].CSA.sliceTiming[0]; setMultiBandFactor(hdr->dim[3], indx0, dcmList); } else if ((nConvert == (hdr->dim[4])) && (hdr->dim[3] < (kMaxEPI3D - 1)) && (hdr->dim[3] > 1) && (hdr->dim[4] > 1)) { - //XA10 mosaics - these are missing a lot of information - //get slice timing from second volume + // XA10 mosaics - these are missing a lot of information + // get slice timing from second volume for (int v = 0; v < hdr->dim[3]; v++) dcmList[indx0].CSA.sliceTiming[v] = dcmList[dcmSort[1].indx].CSA.sliceTiming[v]; setMultiBandFactor(hdr->dim[3], indx0, dcmList); - return; //we have subtracted min + return; // we have subtracted min } - //issue429: subtract min + // issue429: subtract min float mn = dcmList[indx0].CSA.sliceTiming[0]; for (int v = 0; v < hdr->dim[3]; v++) mn = min(mn, dcmList[indx0].CSA.sliceTiming[v]); @@ -6464,27 +6604,27 @@ void sliceTimingXA(struct TDCMsort *dcmSort, struct TDICOMdata *dcmList, struct return; for (int v = 0; v < hdr->dim[3]; v++) dcmList[indx0].CSA.sliceTiming[v] -= mn; -} //sliceTimingXA() +} // sliceTimingXA() void sliceTimeGE(struct TDICOMdata *d, int mb, int dim3, float TR, bool isInterleaved, float geMajorVersion, bool is27r3, float groupDelaysec) { - //mb : multiband factor - //dim3 : number of slices in volume - //TRsec : repetition time in seconds - //isInterleaved : interleaved or sequential slice order - //geMajorVersion: version, e.g. 29.0 - //is27r3 : software release 27.0 R03 or later + // mb : multiband factor + // dim3 : number of slices in volume + // TRsec : repetition time in seconds + // isInterleaved : interleaved or sequential slice order + // geMajorVersion: version, e.g. 29.0 + // is27r3 : software release 27.0 R03 or later float sliceTiming[kMaxEPI3D]; - //multiband can be fractional! 'extra' slices discarded + // multiband can be fractional! 'extra' slices discarded int nExcitations = ceil(float(dim3) / float(mb)); if ((mb > 1) && (geMajorVersion < 26.0)) { printWarning("Unable to determine slice times for early GE HyperBand.\n"); d->CSA.sliceTiming[0] = -1; return; } - if ((mb > 1) && (!is27r3) && ((nExcitations % 2) == 0) ) { //number of slices divided by MB factor should is Even - nExcitations ++; //https://osf.io/q4d53/wiki/home/; Figure 3 of https://pubmed.ncbi.nlm.nih.gov/26308571/ - } - //int nDiscardedSlices = (nExcitations * mb) - dim3; + if ((mb > 1) && (!is27r3) && ((nExcitations % 2) == 0)) { // number of slices divided by MB factor should is Even + nExcitations++; // https://osf.io/q4d53/wiki/home/; Figure 3 of https://pubmed.ncbi.nlm.nih.gov/26308571/ + } + // int nDiscardedSlices = (nExcitations * mb) - dim3; float secPerSlice = (TR - groupDelaysec) / (nExcitations); if (!isInterleaved) { for (int i = 0; i < nExcitations; i++) @@ -6492,26 +6632,26 @@ void sliceTimeGE(struct TDICOMdata *d, int mb, int dim3, float TR, bool isInterl } else { int nOdd = (nExcitations - 1) / 2; for (int i = 0; i < nExcitations; i++) { - if (i % 2 == 0) //ODD slices since we index from 0! + if (i % 2 == 0) // ODD slices since we index from 0! sliceTiming[i] = (i / 2) * secPerSlice; else sliceTiming[i] = (nOdd + ((i + 1) / 2)) * secPerSlice; - } //for each slice + } // for each slice if ((mb > 1) && (is27r3) && (isInterleaved) && (nExcitations > 2) && ((nExcitations % 2) == 0)) { float tmp = sliceTiming[nExcitations - 1]; sliceTiming[nExcitations - 1] = sliceTiming[nExcitations - 3]; sliceTiming[nExcitations - 3] = tmp; - //printf("SWAP!\n"); + // printf("SWAP!\n"); } - } //if interleaved + } // if interleaved for (int i = 0; i < dim3; i++) sliceTiming[i] = sliceTiming[i % nExcitations]; -//#define testSliceTimesGE //note that early GE HyperBand sequences reported single-band values in x0021x105E +// #define testSliceTimesGE //note that early GE HyperBand sequences reported single-band values in x0021x105E #ifdef testSliceTimesGE float maxErr = 0.0; for (int i = 0; i < dim3; i++) maxErr = max(maxErr, fabs(sliceTiming[i] - d->CSA.sliceTiming[i])); - if ((d->CSA.sliceTiming[0] >= 0.0) && (maxErr > 1.0)) { //allow a 1.0 msec tolerance for rounding + if ((d->CSA.sliceTiming[0] >= 0.0) && (maxErr > 1.0)) { // allow a 1.0 msec tolerance for rounding printMessage("GE estimated slice times differ from reported (max error: %g)\n", maxErr); printMessage("Slice\tEstimated\tReported\n"); for (int i = 0; i < dim3; i++) { @@ -6555,7 +6695,7 @@ void readSoftwareVersionsGE(char softwareVersionsGE[], int verbose, char geVersi if (sepStart != NULL) { ismatched = true; substrlen = strlen("SIGNA_LX1"); - sepStart += substrlen+1; + sepStart += substrlen + 1; } } // If softwareVersionsGE is 27\LX\MR Software release:RX27.0_R02_1831.a @@ -6564,7 +6704,7 @@ void readSoftwareVersionsGE(char softwareVersionsGE[], int verbose, char geVersi if (sepStart != NULL) { ismatched = true; substrlen = strlen("MR Software release"); - sepStart += substrlen+1; + sepStart += substrlen + 1; } } // If softwareVersionsGE is 28\LX\MR29.1_EA_2039.g @@ -6618,7 +6758,7 @@ void sliceTimingGE_Testx0021x105E(struct TDICOMdata *d, struct TDCMopts opts, st return; float mxErr = 0.0; for (int v = 0; v < hdr->dim[3]; v++) { - sliceTiming[v] = (sliceTiming[v] - mn) * 1000.0; //subtract offset, convert sec -> ms + sliceTiming[v] = (sliceTiming[v] - mn) * 1000.0; // subtract offset, convert sec -> ms mxErr = max(mxErr, float(fabs(sliceTiming[v] - d->CSA.sliceTiming[v]))); } printMessage("Slice Timing Error between calculated and RTIA timer(0021,105E): %gms\n", mxErr); @@ -6633,7 +6773,7 @@ void reportProtocolBlockGE(struct TDICOMdata *d, const char *filename, int isVer if ((d->manufacturer != kMANUFACTURER_GE) || (d->modality != kMODALITY_MR)) return; if ((d->protocolBlockStartGE < 1) || (d->protocolBlockLengthGE < 19)) { - //if (isVerbose) + // if (isVerbose) printWarning("Missing GE protocol data block (0025,101B)\n"); return; } @@ -6645,11 +6785,11 @@ void reportProtocolBlockGE(struct TDICOMdata *d, const char *filename, int isVer char ioptGE[3000] = ""; char seqName[kDICOMStr] = ""; geProtocolBlock(filename, d->protocolBlockStartGE, d->protocolBlockLengthGE, isVerbose, &sliceOrderGE, &viewOrderGE, &mbAccel, &nSlices, &groupDelay, ioptGE, seqName); - //if (strlen(d->procedureStepDescription) < 2) + // if (strlen(d->procedureStepDescription) < 2) // strcpy(d->procedureStepDescription, seqName); - strcat(d->procedureStepDescription, seqName); //issue790 + strcat(d->procedureStepDescription, seqName); // issue790 #endif -} //bidsGE +} // bidsGE void setBidsSiemens(struct TDICOMdata *d, int nConvert, int isVerbose, const char *filename) { char seqDetails[kDICOMStrLarge] = ""; @@ -6658,10 +6798,10 @@ void setBidsSiemens(struct TDICOMdata *d, int nConvert, int isVerbose, const cha float inv1 = NAN; float inv2 = NAN; bool isDualTI = false; - int lContrasts = 0; //detect me-mprage - #ifdef myReadAsciiCsa + int lContrasts = 0; // detect me-mprage +#ifdef myReadAsciiCsa if ((d->CSA.SeriesHeader_offset > 0) && (d->CSA.SeriesHeader_length > 0)) { - float pf = 1.0f; //partial fourier + float pf = 1.0f; // partial fourier float shimSetting[8]; char protocolName[kDICOMStrLarge], fmriExternalInfo[kDICOMStrLarge], coilID[kDICOMStrLarge], consistencyInfo[kDICOMStrLarge], coilElements[kDICOMStrLarge], wipMemBlock[kDICOMStrExtraLarge]; TCsaAscii csaAscii; @@ -6669,81 +6809,80 @@ void setBidsSiemens(struct TDICOMdata *d, int nConvert, int isVerbose, const cha inv1 = csaAscii.alTI[0] / 1000.0; inv2 = csaAscii.alTI[1] / 1000.0; lContrasts = csaAscii.lContrasts; - //If parameter lInvContrasts exists in the protocol, a value of 1 indicates MPRAGE and a value of 2 MP2RAGE. Note that lInvContrasts is different from lContrasts and that only lInvContrasts must be considered. - //If parameter lInvContrasts does not exist, then the presence of alTI[1] indicates that this is an MP2RAGE protocol. An MPRAGE protocol will only contain alTI[0]. - if (csaAscii.lInvContrasts == 1) //explicitly reports one TI + // If parameter lInvContrasts exists in the protocol, a value of 1 indicates MPRAGE and a value of 2 MP2RAGE. Note that lInvContrasts is different from lContrasts and that only lInvContrasts must be considered. + // If parameter lInvContrasts does not exist, then the presence of alTI[1] indicates that this is an MP2RAGE protocol. An MPRAGE protocol will only contain alTI[0]. + if (csaAscii.lInvContrasts == 1) // explicitly reports one TI inv2 = NAN; if ((!isnan(inv1)) && (!isnan(inv2)) && (inv1 > 0.0) && (inv2 > 0.0)) isDualTI = true; - } - #endif // myReadAsciiCsa +#endif // myReadAsciiCsa char *dataTypeBIDS = d->CSA.bidsDataType; strcpy(dataTypeBIDS, ""); char *suffixBIDS = d->CSA.bidsEntitySuffix; strcpy(suffixBIDS, ""); - char modalityBIDS[kDICOMStrLarge] = ""; //T1w, bold, dwi - char recBIDS[kDICOMStrLarge] = ""; //_desc-preproc + char modalityBIDS[kDICOMStrLarge] = ""; // T1w, bold, dwi + char recBIDS[kDICOMStrLarge] = ""; //_desc-preproc if (d->manufacturer != kMANUFACTURER_SIEMENS) return; - bool isReportEcho = true; //do not report _echo for PD/T2 pair or fieldmap + bool isReportEcho = true; // do not report _echo for PD/T2 pair or fieldmap bool isMultiEcho = false; - bool isDerived = d->isDerived; //report phase encoding direction + bool isDerived = d->isDerived; // report phase encoding direction bool isDirLabel = false; bool isPart = false; if (d->isHasPhase) isPart = true; - bool isAddSeriesToRun = true; //except phasemap, where phasediff and magnitude have different series numbers but must share acq + bool isAddSeriesToRun = true; // except phasemap, where phasediff and magnitude have different series numbers but must share acq char seqName[kDICOMStrLarge]; strcpy(seqName, d->sequenceName); - if (strlen(d->sequenceName) < 2) //e.g. XA uses 0018,9005 while VE uses 0018,0024 + if (strlen(d->sequenceName) < 2) // e.g. XA uses 0018,9005 while VE uses 0018,0024 strcpy(seqName, d->pulseSequenceName); if (strlen(seqDetails) < 2) strcpy(seqDetails, seqName); if (strstr(d->imageType, "DERIVED")) { - isDerived = true; //to do: respond to derived images + isDerived = true; // to do: respond to derived images } if (d->modality != kMODALITY_MR) return; - if (((d->xyzDim[3] < 2) && (nConvert < 1)) || (d->isLocalizer)) { //need nConvert or nifti header + if (((d->xyzDim[3] < 2) && (nConvert < 1)) || (d->isLocalizer)) { // need nConvert or nifti header strcpy(dataTypeBIDS, "discard"); strcpy(modalityBIDS, "localizer"); - } else if (strstr(seqDetails,"b1map")) { - //issue 751 nb both T1 and b1map can use tfl base - //https://bids-specification.readthedocs.io/en/stable/appendices/qmri.html#tb1tfl-and-tb1rfm-specific-notes + } else if (strstr(seqDetails, "b1map")) { + // issue 751 nb both T1 and b1map can use tfl base + // https://bids-specification.readthedocs.io/en/stable/appendices/qmri.html#tb1tfl-and-tb1rfm-specific-notes strcpy(dataTypeBIDS, "fmap"); strcpy(modalityBIDS, "TB1TFL"); if ((strstr(d->imageType, "FLIP ANGLE MAP")) || (strstr(d->imageType, "FLIP ANGLE MAP"))) strcpy(preAcqStr, "famp"); else strcpy(preAcqStr, "anat"); - } else if ((strstr(seqDetails, "tfl") != NULL) || (strstr(seqDetails, "mp2rage") != NULL) || (strstr(seqDetails, "wip925") != NULL)) { //prog_mprage + } else if ((strstr(seqDetails, "tfl") != NULL) || (strstr(seqDetails, "mp2rage") != NULL) || (strstr(seqDetails, "wip925") != NULL)) { // prog_mprage strcpy(dataTypeBIDS, "anat"); if (isDualTI) strcpy(modalityBIDS, "MP2RAGE"); - else { //bizarrely mprage can populate both alTI[0] alTI[1] see dcm_qa_xa30 + else { // bizarrely mprage can populate both alTI[0] alTI[1] see dcm_qa_xa30 strcpy(modalityBIDS, "T1w"); - //bork inv2 = NAN; + // bork inv2 = NAN; } if (strstr(d->imageType, "T1 MAP") != NULL) { strcpy(modalityBIDS, "T1map"); - isDualTI = false; //issue 750 derived from two images + isDualTI = false; // issue 750 derived from two images } if (strstr(d->imageType, "_UNI") != NULL) { - isDualTI = false; //issue 750 derived from two images + isDualTI = false; // issue 750 derived from two images strcpy(modalityBIDS, "UNIT1"); if (strstr(d->imageComments, "DENOISED IMAGE") != NULL) strcat(recBIDS, "denoise"); } - isDerived = false; //issue750 Siemens E11 considers UNIT1 derived, but BIDS does not - } else if ((d->CSA.numDti > 0) || (strstr(seqDetails, "_diff") != NULL) || (strstr(seqDetails, "resolve") != NULL) || (strstr(seqDetails, "PtkSmsVB13ADwDualSpinEchoEpi") != NULL) || (strstr(seqDetails, "ep2d_stejskal_386") != NULL)) { //prog_diff + isDerived = false; // issue750 Siemens E11 considers UNIT1 derived, but BIDS does not + } else if ((d->CSA.numDti > 0) || (strstr(seqDetails, "_diff") != NULL) || (strstr(seqDetails, "resolve") != NULL) || (strstr(seqDetails, "PtkSmsVB13ADwDualSpinEchoEpi") != NULL) || (strstr(seqDetails, "ep2d_stejskal_386") != NULL)) { // prog_diff strcpy(dataTypeBIDS, "dwi"); strcpy(modalityBIDS, "dwi"); if (strstr(d->seriesDescription, "_SBRef") != NULL) strcpy(modalityBIDS, "sbref"); - //if seriesDesc trace", "fa", "adc" isDerived = true; + // if seriesDesc trace", "fa", "adc" isDerived = true; isDirLabel = true; - } else if ((strstr(seqDetails, "fairest")) || (strstr(seqDetails, "_asl") != NULL) || (strstr(seqDetails, "_pasl") != NULL) || (strstr(seqDetails, "pcasl") != NULL) || (strstr(seqDetails, "PCASL") != NULL)) { //prog_asl + } else if ((strstr(seqDetails, "fairest")) || (strstr(seqDetails, "_asl") != NULL) || (strstr(seqDetails, "_pasl") != NULL) || (strstr(seqDetails, "pcasl") != NULL) || (strstr(seqDetails, "PCASL") != NULL)) { // prog_asl strcpy(dataTypeBIDS, "perf"); strcpy(modalityBIDS, "asl"); if (strstr(d->seriesDescription, "_m0") != NULL) @@ -6751,14 +6890,14 @@ void setBidsSiemens(struct TDICOMdata *d, int nConvert, int isVerbose, const cha } else if (strstr(d->pulseSequenceName, "spcR") != NULL) { strcpy(dataTypeBIDS, "anat"); strcpy(modalityBIDS, "T2w"); - } else if (strstr(seqDetails, "tse_vfl") != NULL) { //prog_tse_vfl + } else if (strstr(seqDetails, "tse_vfl") != NULL) { // prog_tse_vfl strcpy(dataTypeBIDS, "anat"); if ((strstr(seqDetails, "spcir") != NULL) || (strstr(d->sequenceName, "spcir") != NULL)) strcpy(modalityBIDS, "FLAIR"); else strcpy(modalityBIDS, "T2w"); - } else if (strstr(seqDetails, "tse") != NULL) { //prog_tse - isReportEcho = false; //do not report _echo for T2/PDw pair + } else if (strstr(seqDetails, "tse") != NULL) { // prog_tse + isReportEcho = false; // do not report _echo for T2/PDw pair strcpy(dataTypeBIDS, "anat"); if (strstr(d->sequenceName, "tir") != NULL) strcpy(modalityBIDS, "FLAIR"); @@ -6768,17 +6907,17 @@ void setBidsSiemens(struct TDICOMdata *d, int nConvert, int isVerbose, const cha else strcpy(modalityBIDS, "T2w"); } - } else if ((strstr(seqDetails, "ep2d_ase") != NULL)) { //prog_ep2d_se + } else if ((strstr(seqDetails, "ep2d_ase") != NULL)) { // prog_ep2d_se // oxygen extraction fraction(OEF) Asymmetric Spin Echo (ASE) - //printWarning("BIDS does not yet specify `ase` data\n"); - //n.b. we do not specify dataTypeBIDS as not yet defined + // printWarning("BIDS does not yet specify `ase` data\n"); + // n.b. we do not specify dataTypeBIDS as not yet defined strcpy(modalityBIDS, "oef_ase"); - } else if ((strstr(seqDetails, "ep2d_se") != NULL)) { //prog_ep2d_se + } else if ((strstr(seqDetails, "ep2d_se") != NULL)) { // prog_ep2d_se strcpy(dataTypeBIDS, "fmap"); strcpy(modalityBIDS, "epi"); isDirLabel = true; - } else if ((strstr(seqDetails, "gre_field_mapping") != NULL)) { //prog_fmap - isReportEcho = false; //echo encoded in "_magnitude" + } else if ((strstr(seqDetails, "gre_field_mapping") != NULL)) { // prog_fmap + isReportEcho = false; // echo encoded in "_magnitude" isAddSeriesToRun = false; isPart = false; strcpy(dataTypeBIDS, "fmap"); @@ -6786,30 +6925,30 @@ void setBidsSiemens(struct TDICOMdata *d, int nConvert, int isVerbose, const cha strcpy(modalityBIDS, "phasediff"); if (d->isHasMagnitude) snprintf(modalityBIDS, kDICOMStrLarge - strlen(modalityBIDS), "magnitude%d", d->echoNum); - //printf("magnitude%d\n", d->echoNum); - //} else if (( seqName,"_tfl2d1") == 0) || (strcmp(dcmList[indx].sequenceName, "_fl3d1_ns") == 0) || (strcmp(dcmList[indx].sequenceName, "_fl2d1" - } else if ((strstr(seqDetails, "\\trufi") != NULL) || (strstr(seqName, "fl3d1_ns") != NULL) || (strstr(seqName, "fl2d1") != NULL) || (strstr(seqName, "tfl2d1") != NULL)) { //localizers + // printf("magnitude%d\n", d->echoNum); + //} else if (( seqName,"_tfl2d1") == 0) || (strcmp(dcmList[indx].sequenceName, "_fl3d1_ns") == 0) || (strcmp(dcmList[indx].sequenceName, "_fl2d1" + } else if ((strstr(seqDetails, "\\trufi") != NULL) || (strstr(seqName, "fl3d1_ns") != NULL) || (strstr(seqName, "fl2d1") != NULL) || (strstr(seqName, "tfl2d1") != NULL)) { // localizers strcpy(dataTypeBIDS, "discard"); strcpy(modalityBIDS, "localizer"); - } else if ((strstr(seqName, "fl3d1r") != NULL) || (strstr(seqName, "fl2d1") != NULL) || (strstr(seqName, "tfl2d1") != NULL)) { //localizers - //nb ToF fl3d1r_ts fl3d1r_t70 fl3d1r7t but check for fl3d1r SWI + } else if ((strstr(seqName, "fl3d1r") != NULL) || (strstr(seqName, "fl2d1") != NULL) || (strstr(seqName, "tfl2d1") != NULL)) { // localizers + // nb ToF fl3d1r_ts fl3d1r_t70 fl3d1r7t but check for fl3d1r SWI strcpy(dataTypeBIDS, "anat"); strcpy(modalityBIDS, "angio"); } else if (strstr(seqDetails, "ep_seg_fid") != NULL) { - //n.b. large echoTrainLength even for single echo acquisition + // n.b. large echoTrainLength even for single echo acquisition strcpy(dataTypeBIDS, "anat"); strcpy(modalityBIDS, "T2starw"); isPart = true; - if (strstr(d->seriesDescription, "mIP") != NULL) { //derived minimum intensity + if (strstr(d->seriesDescription, "mIP") != NULL) { // derived minimum intensity strcpy(dataTypeBIDS, "discard"); strcpy(modalityBIDS, "mIP"); } - if (strstr(d->seriesDescription, "SWI_Images") != NULL) { //derived SWI + if (strstr(d->seriesDescription, "SWI_Images") != NULL) { // derived SWI strcpy(dataTypeBIDS, "discard"); strcpy(modalityBIDS, "SWI_Images"); } - } else if (strstr(seqDetails, "gre") != NULL) { //prog_gre - //printf("GRE T2starw or if MEGRE\n"); + } else if (strstr(seqDetails, "gre") != NULL) { // prog_gre + // printf("GRE T2starw or if MEGRE\n"); strcpy(dataTypeBIDS, "anat"); strcpy(modalityBIDS, "T2starw"); if ((d->echoNum > 1) || (lContrasts > 1)) { @@ -6817,22 +6956,21 @@ void setBidsSiemens(struct TDICOMdata *d, int nConvert, int isVerbose, const cha isMultiEcho = true; } isPart = true; - } else if ((strstr(seqDetails, "AALScout") != NULL) || (strstr(seqDetails, "haste") != NULL)) { //localizer: unused + } else if ((strstr(seqDetails, "AALScout") != NULL) || (strstr(seqDetails, "haste") != NULL)) { // localizer: unused strcpy(dataTypeBIDS, "discard"); strcpy(modalityBIDS, "localizer"); - } else if ((strstr(seqDetails, "_bold")) || (strstr(seqDetails, "pace")) - || (strstr(d->imageType, "FMRI")) || (strstr(seqDetails, "ep2d_fid"))) { //prog_bold - //n.b. "Space" is not "pace" + } else if ((strstr(seqDetails, "_bold")) || (strstr(seqDetails, "pace")) || (strstr(d->imageType, "FMRI")) || (strstr(seqDetails, "ep2d_fid"))) { // prog_bold + // n.b. "Space" is not "pace" strcpy(dataTypeBIDS, "func"); strcpy(modalityBIDS, "bold"); isDirLabel = true; - char* p = strstr(d->protocolName, "func_"); + char *p = strstr(d->protocolName, "func_"); if (p == d->protocolName) - //todo issue753 infer task from d->protocolName - if (strstr(d->seriesDescription, "_SBRef") != NULL) - strcpy(modalityBIDS, "sbref"); + // todo issue753 infer task from d->protocolName + if (strstr(d->seriesDescription, "_SBRef") != NULL) + strcpy(modalityBIDS, "sbref"); } else if (strstr(d->sequenceName, "*epse2d") != NULL) { - //pepolar? + // pepolar? strcpy(dataTypeBIDS, "fmap"); strcpy(modalityBIDS, "epi"); isDirLabel = true; @@ -6843,28 +6981,30 @@ void setBidsSiemens(struct TDICOMdata *d, int nConvert, int isVerbose, const cha if (len > 0) { for (int i = 0; i < len; i++) { char ch = seqName[i]; - if (ch == '*') continue; + if (ch == '*') + continue; if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) strncat(acqStr, &ch, 1); - if (isdigit(ch)) break; + if (isdigit(ch)) + break; } - } //len > 0 + } // len > 0 if (d->accelFactPE > 1) snprintf(acqStr + strlen(acqStr), kDICOMStrLarge - strlen(acqStr), "p%d", (int)round(d->accelFactPE)); if (d->CSA.multiBandFactor > 1) snprintf(acqStr + strlen(acqStr), kDICOMStrLarge - strlen(acqStr), "m%d", (int)round(d->CSA.multiBandFactor)); strcat(suffixBIDS, acqStr); - //add _rec - // https://bids-specification.readthedocs.io/en/stable/05-derivatives/02-common-data-types.html#common-file-level-metadata-fields + // add _rec + // https://bids-specification.readthedocs.io/en/stable/05-derivatives/02-common-data-types.html#common-file-level-metadata-fields if (strlen(recBIDS) > 0) { strcat(suffixBIDS, "_rec-"); strcat(suffixBIDS, recBIDS); } - //add dir - //nb for func dir comes before echo + // add dir + // nb for func dir comes before echo int phPos = d->CSA.phaseEncodingDirectionPositive; if (isDirLabel) { - char dirLabel[kDICOMStrLarge] = "_dir-"; + char dirLabel[kDICOMStrLarge] = "_dir-"; if (d->phaseEncodingRC == 'C') { if (phPos) strcat(dirLabel, "AP"); @@ -6878,23 +7018,23 @@ void setBidsSiemens(struct TDICOMdata *d, int nConvert, int isVerbose, const cha } strcat(suffixBIDS, dirLabel); } - //add run + // add run if (isAddSeriesToRun) snprintf(suffixBIDS + strlen(suffixBIDS), kDICOMStrLarge - strlen(suffixBIDS), "_run-%ld", d->seriesNum); - //add echo + // add echo if (isReportEcho) if ((d->echoNum > 1) || (isMultiEcho) || ((d->isMultiEcho) && (d->echoNum > 0))) snprintf(suffixBIDS + strlen(suffixBIDS), kDICOMStrLarge - strlen(suffixBIDS), "_echo-%d", d->echoNum); - //add inv - // https://bids-specification.readthedocs.io/en/stable/appendices/entities.html#inv + // add inv + // https://bids-specification.readthedocs.io/en/stable/appendices/entities.html#inv if (isDualTI) { - //n.b. Siemens uses alTI[0]/alTI[1] for inversion times: alTI[2] used by ASL + // n.b. Siemens uses alTI[0]/alTI[1] for inversion times: alTI[2] used by ASL int invIdx = 1; if (isSameFloatGE(d->TI, inv2)) invIdx = 2; snprintf(suffixBIDS + strlen(suffixBIDS), kDICOMStrLarge - strlen(suffixBIDS), "_inv-%d", invIdx); } - //add part + // add part if (isPart) { if (d->isHasPhase) strcat(suffixBIDS, "_part-phase"); @@ -6906,11 +7046,11 @@ void setBidsSiemens(struct TDICOMdata *d, int nConvert, int isVerbose, const cha strcat(suffixBIDS, modalityBIDS); } if ((isVerbose > 0) || (strlen(dataTypeBIDS) < 1)) - printf("::autoBids:Siemens CSAseqFname:'%s' pulseSeq:'%s' seqName:'%s'\n", - seqDetails, d->pulseSequenceName, d->sequenceName); + printf("::autoBids:Siemens CSAseqFname:'%s' pulseSeq:'%s' seqName:'%s'\n", + seqDetails, d->pulseSequenceName, d->sequenceName); if (isDerived) strcpy(dataTypeBIDS, "derived"); - //bork - ARC data follows + // bork - ARC data follows /* if (strstr(dataTypeBIDS, "dwi")) { if (strstr(d->protocolName, "12 dirs")) @@ -6945,61 +7085,61 @@ void setBidsPhilips(struct TDICOMdata *d, int nConvert, int isVerbose) { return; char seqName[kDICOMStr] = ""; strcpy(seqName, d->sequenceVariant); - char modalityBIDS[kDICOMStrLarge] = ""; //_acq-FL3p2m2 + char modalityBIDS[kDICOMStrLarge] = ""; //_acq-FL3p2m2 bool isReportEcho = true; - bool isDirLabel = false; //report phase encoding direction - bool isDerived = d->isDerived; //report phase encoding direction + bool isDirLabel = false; // report phase encoding direction + bool isDerived = d->isDerived; // report phase encoding direction bool isAddSeriesToRun = true; bool isPart = false; - //d->pulseSequenceName, d->scanningSequence, d->sequenceVariant) + // d->pulseSequenceName, d->scanningSequence, d->sequenceVariant) if (((d->xyzDim[3] < 4) && (nConvert < 4)) || (d->isLocalizer)) { strcpy(dataTypeBIDS, "discard"); strcpy(modalityBIDS, "localizer"); } else if (strstr(seqName, "MP") != NULL) { strcpy(dataTypeBIDS, "anat"); strcpy(modalityBIDS, "T1w"); - } else if ((d->isDiffusion) && (strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "SE") != NULL) ) { + } else if ((d->isDiffusion) && (strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "SE") != NULL)) { strcpy(dataTypeBIDS, "dwi"); strcpy(modalityBIDS, "dwi"); } else if (strstr(d->imageType, "PERFUSION") != NULL) { - //scanSeq:'GR' seqVariant:'SK' + // scanSeq:'GR' seqVariant:'SK' strcpy(dataTypeBIDS, "perf"); strcpy(modalityBIDS, "asl"); - } else if ((strstr(d->pulseSequenceName, "SEEPI") != NULL) && (!d->isDiffusion) && (strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "SE") != NULL) ) { - //pepolar? d->pulseSequenceName + } else if ((strstr(d->pulseSequenceName, "SEEPI") != NULL) && (!d->isDiffusion) && (strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "SE") != NULL)) { + // pepolar? d->pulseSequenceName isAddSeriesToRun = false; strcpy(dataTypeBIDS, "fmap"); strcpy(modalityBIDS, "epi"); printWarning("Unable to estimate BIDS `_dir` for fmap epi as Philips DICOMs do not report phase encoding polarity\n"); isDirLabel = true; - } else if ((!d->isDiffusion) && (strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "SE") != NULL) ) { + } else if ((!d->isDiffusion) && (strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "SE") != NULL)) { strcpy(dataTypeBIDS, "anat"); - if (false)//((strstr(d->scanningSequence, "IR") != NULL)) + if (false) //((strstr(d->scanningSequence, "IR") != NULL)) strcpy(modalityBIDS, "FLAIR"); else if (d->TE < 40) strcpy(modalityBIDS, "PDw"); else strcpy(modalityBIDS, "T2w"); - } else if ((strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "IR") != NULL) ) { + } else if ((strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "IR") != NULL)) { strcpy(dataTypeBIDS, "anat"); strcpy(modalityBIDS, "FLAIR"); - } else if ((strstr(d->imageType, "PERFUSION") != NULL) && (strstr(d->pulseSequenceName, "FEEPI") != NULL) && (strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "GR") != NULL) ) { + } else if ((strstr(d->imageType, "PERFUSION") != NULL) && (strstr(d->pulseSequenceName, "FEEPI") != NULL) && (strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "GR") != NULL)) { strcpy(dataTypeBIDS, "perf"); strcpy(modalityBIDS, "asl"); - } else if (((d->rawDataRunNumber >= 1) || (strstr(d->pulseSequenceName, "FEEPI") != NULL)) && (strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "GR") != NULL) ) { - //nb older Philips data does not record EPI so use 2005,1063 fMRIStatusIndication + } else if (((d->rawDataRunNumber >= 1) || (strstr(d->pulseSequenceName, "FEEPI") != NULL)) && (strstr(seqName, "SK") != NULL) && (strstr(d->scanningSequence, "GR") != NULL)) { + // nb older Philips data does not record EPI so use 2005,1063 fMRIStatusIndication strcpy(dataTypeBIDS, "func"); strcpy(modalityBIDS, "bold"); isDirLabel = true; - } else if ((strstr(seqName, "SS") != NULL) && (strstr(d->scanningSequence, "GR") != NULL) ) { + } else if ((strstr(seqName, "SS") != NULL) && (strstr(d->scanningSequence, "GR") != NULL)) { strcpy(dataTypeBIDS, "anat"); strcpy(modalityBIDS, "T2starw"); isPart = true; } else if ((d->isRealIsPhaseMapHz) && (strstr(seqName, "SS") != NULL) && (strstr(d->scanningSequence, "RM") != NULL)) { - //https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/01-magnetic-resonance-imaging-data.html#expressing-the-mr-protocol-intent-for-fieldmaps - //isRealIsPhaseMapHz for 'Case 3: Direct field mapping' - //sub-