This is an automated email from the git hooks/post-receive script. ghisvail-guest pushed a commit to branch debian/experimental in repository dcm2niix.
commit af70bbab8cc813d904de5f3562743f48f4298ec9 Author: Ghislain Antony Vaillant <[email protected]> Date: Sat Jun 3 09:04:22 2017 +0100 New upstream version 1.0.20170528 --- README.md | 3 +- console/CMakeLists.txt | 7 +- console/main_console.cpp | 2 +- console/nii_dicom.cpp | 68 +++++++------ console/nii_dicom.h | 39 ++++++-- console/nii_dicom_batch.cpp | 239 ++++++++++++++++++++++++++++++++++++++------ console/nii_dicom_batch.h | 2 +- console/nii_foreign.cpp | 10 +- 8 files changed, 297 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 2e1fb63..075ff2f 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,9 @@ This software is open source. The bulk of the code is covered by the BSD license ## Versions -Development +28-May-2017 - Remove all derived images from [Philips DTI series](http://www.nitrc.org/forum/message.php?msg_id=21025). + - Provide some [Siemens EPI sequence details](https://github.com/rordenlab/dcm2niix/issues). 28-April-2017 - Experimental [ECAT support](https://github.com/rordenlab/dcm2niix/issues/95). diff --git a/console/CMakeLists.txt b/console/CMakeLists.txt index e57086a..8e98cdc 100644 --- a/console/CMakeLists.txt +++ b/console/CMakeLists.txt @@ -59,7 +59,7 @@ endif() option(USE_SYSTEM_TURBOJPEG "Use the system TurboJPEG" OFF) if(USE_SYSTEM_TURBOJPEG) find_package(PkgConfig REQUIRED) - pkg_check_modules(TurboJPEG libturbojpeg) + pkg_check_modules(TurboJPEG REQUIRED libturbojpeg) add_definitions(-DmyTurboJPEG) target_include_directories(dcm2niix PRIVATE ${TurboJPEG_INCLUDE_DIRS}) target_link_libraries(dcm2niix ${TurboJPEG_LIBRARIES}) @@ -111,6 +111,11 @@ if(BATCH_VERSION) target_link_libraries(dcm2niibatch z) endif() + if(TurboJPEG_FOUND) + target_include_directories(dcm2niibatch PRIVATE ${TurboJPEG_INCLUDE_DIRS}) + target_link_libraries(dcm2niibatch ${TurboJPEG_LIBRARIES}) + endif() + if(JASPER_FOUND) target_link_libraries(dcm2niibatch ${JASPER_LIBRARIES}) else() diff --git a/console/main_console.cpp b/console/main_console.cpp index 3137e01..6434aa1 100644 --- a/console/main_console.cpp +++ b/console/main_console.cpp @@ -119,7 +119,7 @@ void showHelp(const char * argv[], struct TDCMopts opts) { printf(" Examples :\n"); printf(" %s /Users/chris/dir\n", cstr); printf(" %s -o /users/cr/outdir/ -z y ~/dicomdir\n", cstr); - printf(" %s -f %%pp_%%s -b y -ba n ~/dicomdir\n", cstr); + printf(" %s -f %%p_%%s -b y -ba n ~/dicomdir\n", cstr); printf(" %s -f mystudy%%s ~/dicomdir\n", cstr); printf(" %s -o \"~/dir with spaces/dir\" ~/dicomdir\n", cstr); #endif diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 059d5b9..667426a 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -639,6 +639,10 @@ struct TDICOMdata clear_dicom_data() { strcpy(d.sequenceVariant, ""); strcpy(d.manufacturersModelName, "N/A"); strcpy(d.procedureStepDescription, ""); + strcpy(d.institutionName, ""); + strcpy(d.institutionAddress, ""); + strcpy(d.deviceSerialNumber, ""); + strcpy(d.softwareVersions, ""); strcpy(d.seriesInstanceUID, ""); strcpy(d.studyInstanceUID, ""); strcpy(d.bodyPartExamined,""); @@ -659,6 +663,7 @@ struct TDICOMdata clear_dicom_data() { d.fieldStrength = 0.0; d.numberOfDynamicScans = 0; d.echoNum = 1; + d.echoTrainLength = 0; d.coilNum = 1; d.patientPositionNumPhilips = 0; d.imageBytes = 0; @@ -684,6 +689,7 @@ struct TDICOMdata clear_dicom_data() { d.samplesPerPixel = 1; d.isValid = false; d.isXRay = false; + d.isMultiEcho = false; 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 @@ -704,6 +710,8 @@ struct TDICOMdata clear_dicom_data() { d.CSA.phaseEncodingDirectionPositive = -1; //unknown d.CSA.isPhaseMap = false; d.CSA.multiBandFactor = 1; + d.CSA.SeriesHeader_offset = 0; + d.CSA.SeriesHeader_length = 0; return d; } //clear_dicom_data() @@ -879,34 +887,6 @@ int dcmStrManufacturer (int lByteLength, unsigned char lBuffer[]) {//read float return ret; } //dcmStrManufacturer -#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 -#endif - 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 @@ -2546,6 +2526,8 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru #define kStudyTime 0x0008+(0x0030 << 16 ) #define kAcquisitionTime 0x0008+(0x0032 << 16 ) #define kManufacturer 0x0008+(0x0070 << 16 ) +#define kInstitutionName 0x0008+(0x0080 << 16 ) +#define kInstitutionAddress 0x0008+(0x0081 << 16 ) #define kSeriesDescription 0x0008+(0x103E << 16 ) // '0008' '103E' 'LO' 'SeriesDescription' #define kManufacturersModelName 0x0008+(0x1090 << 16 ) #define kDerivationDescription 0x0008+(0x2111 << 16 ) @@ -2565,6 +2547,9 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru #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 kDeviceSerialNumber 0x0018+(0x1000 << 16 ) //LO +#define kSoftwareVersions 0x0018+(0x1020 << 16 ) //LO #define kProtocolName 0x0018+(0x1030<< 16 ) #define kRadionuclideTotalDose 0x0018+(0x1074<< 16 ) #define kRadionuclideHalfLife 0x0018+(0x1075<< 16 ) @@ -2603,6 +2588,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru #define kSlope 0x0028+(0x1053 << 16 ) #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 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 @@ -2834,6 +2820,12 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru case kManufacturer: d.manufacturer = dcmStrManufacturer (lLength, &buffer[lPos]); break; + case kInstitutionName: + dcmStr(lLength, &buffer[lPos], d.institutionName); + break; + case kInstitutionAddress: + dcmStr(lLength, &buffer[lPos], d.institutionAddress); + break; case kComplexImageComponent: d.isHasPhase = (buffer[lPos]=='P') && (toupper(buffer[lPos+1]) == 'H'); d.isHasMagnitude = (buffer[lPos]=='M') && (toupper(buffer[lPos+1]) == 'A'); @@ -2866,6 +2858,14 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru if (strcasecmp(derivationDescription, "MEDCOM_RESAMPLED") == 0) d.isResampled = true; break; } + case kDeviceSerialNumber : { + dcmStr (lLength, &buffer[lPos], d.deviceSerialNumber); + break; + } + case kSoftwareVersions : { + dcmStr (lLength, &buffer[lPos], d.softwareVersions); + break; + } case kProtocolName : { //if ((strlen(d.protocolName) < 1) || (d.manufacturer != kMANUFACTURER_GE)) //GE uses a generic session name here: do not overwrite kProtocolNameGE dcmStr (lLength, &buffer[lPos], d.protocolName); //see also kSequenceName @@ -3001,6 +3001,10 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru case kPhaseEncodingSteps : phaseEncodingSteps = dcmStrInt(lLength, &buffer[lPos]); break; + case kEchoTrainLength : + d.echoTrainLength = dcmStrInt(lLength, &buffer[lPos]); + // printf(">>>>>>>>>>>>>>>> %d", d.echoTrainLength); + break; case kFlipAngle : d.flipAngle = dcmStrFloat(lLength, &buffer[lPos]); break; @@ -3206,12 +3210,18 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru printMessage("Skipping DICOM (audio not image) '%s'\n", fname); break; case kCSAImageHeaderInfo: - readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose, dti4D); + readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose, dti4D); d.isHasPhase = d.CSA.isPhaseMap; break; //case kObjectGraphics: // printMessage("---->%d,",lLength); // break; + case kCSASeriesHeaderInfo: + //printMessage("Series %d %d\n", lPos, lLength); + if ((lPos + lLength) > fileLen) break; + d.CSA.SeriesHeader_offset = (int)lPos; + d.CSA.SeriesHeader_length = lLength; + break; case kRealWorldIntercept: if (isSameFloat(0.0, d.intenIntercept)) //give precedence to standard value d.intenIntercept = dcmFloatDouble(lLength, &buffer[lPos],d.isLittleEndian); diff --git a/console/nii_dicom.h b/console/nii_dicom.h index 7792b9a..8d1b50a 100644 --- a/console/nii_dicom.h +++ b/console/nii_dicom.h @@ -1,5 +1,6 @@ #include <stdbool.h> #include <string.h> +#include <stdint.h> #include "nifti1_io_core.h" #ifndef HAVE_R #include "nifti1.h" @@ -37,7 +38,7 @@ extern "C" { #define kCCsuf " CompilerNA" //unknown compiler! #endif - #define kDCMvers "v1.0.20170429" kDCMsuf kCCsuf + #define kDCMvers "v1.0.20170528" kDCMsuf kCCsuf static const int kMaxDTI4D = 4096; //maximum number of DTI directions for 4D (Philips) images, also maximum number of 3D slices for Philips 3D and 4D images #define kDICOMStr 64 @@ -63,24 +64,50 @@ static const int kCompress50 = 3; //obsolete JPEG lossy struct TDTI4D { struct TDTI S[kMaxDTI4D]; }; - +#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 +#endif struct TCSAdata { bool isPhaseMap; float dtiV[4], sliceNormV[4], bandwidthPerPixelPhaseEncode, sliceMeasurementDuration; - int numDti, multiBandFactor, sliceOrder, slice_start, slice_end, mosaicSlices,protocolSliceNumber1,phaseEncodingDirectionPositive; + int numDti, SeriesHeader_offset, SeriesHeader_length, multiBandFactor, sliceOrder, slice_start, slice_end, mosaicSlices,protocolSliceNumber1,phaseEncodingDirectionPositive; }; struct TDICOMdata { long seriesNum; int xyzDim[5]; - int patientPositionNumPhilips, coilNum, echoNum, sliceOrient,numberOfDynamicScans, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,patientPositionSequentialRepeats,locationsInAcquisition, compressionScheme; + int echoTrainLength, patientPositionNumPhilips, coilNum, echoNum, sliceOrient,numberOfDynamicScans, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,patientPositionSequentialRepeats,locationsInAcquisition, compressionScheme; float flipAngle, fieldStrength, TE, TI, TR, intenScale, intenIntercept, intenScalePhilips, gantryTilt, lastScanLoc, angulation[4]; float orient[7], patientPosition[4], patientPositionLast[4], xyzMM[4], stackOffcentre[4]; float radionuclidePositronFraction, radionuclideTotalDose, radionuclideHalfLife, doseCalibrationFactor; //PET ISOTOPE MODULE ATTRIBUTES (C.8-57) float ecat_isotope_halflife, ecat_dosage; double dateTime, acquisitionTime, acquisitionDate; - bool isXRay, isSlicesSpatiallySequentialPhilips, isNonImage, isValid, is3DAcq, isExplicitVR, isLittleEndian, isPlanarRGB, isSigned, isHasPhase,isHasMagnitude,isHasMixed, isFloat, isResampled; + bool isXRay, isMultiEcho, isSlicesSpatiallySequentialPhilips, isNonImage, isValid, is3DAcq, isExplicitVR, isLittleEndian, isPlanarRGB, isSigned, isHasPhase,isHasMagnitude,isHasMixed, isFloat, isResampled; char phaseEncodingRC; - char seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageType[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr],seriesDescription[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], birthDate[kDICOMStr], gender[kDICOMStr], age[kDICOMStr], studyDate[kDICOMStr],studyTime[kD [...] + char softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionAddress[kDICOMStr], institutionName[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageType[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr],seriesDescription[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],s [...] struct TCSAdata CSA; }; diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 0220742..86386e1 100755 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -332,7 +332,150 @@ bool isDerived(struct TDICOMdata d) { return true; } -void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct TDTI4D *dti4D, struct nifti_1_header *h) { +#ifdef _MSC_VER +//https://opensource.apple.com/source/Libc/Libc-1044.1.2/string/FreeBSD/memmem.c +/*- + * Copyright (c) 2005 Pascal Gloor <[email protected]> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +const void * memmem(const char *l, size_t l_len, const char *s, size_t s_len) { + register char *cur, *last; + const char *cl = (const char *)l; + const char *cs = (const char *)s; + /* we need something to compare */ + if (l_len == 0 || s_len == 0) + return NULL; + /* "s" must be smaller or equal to "l" */ + if (l_len < s_len) + return NULL; + /* special case where s_len == 1 */ + if (s_len == 1) + return memchr(l, (int)*cs, l_len); + /* the last position where its possible to find "s" in "l" */ + last = (char *)cl + l_len - s_len; + for (cur = (char *)cl; cur <= last; cur++) + if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) + 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 + +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) return 0; + int i = (int)strlen(key); + while( ( i< remLength) && (keyPos[i] != 0x0A) ) { + if( keyPos[i] >= '0' && keyPos[i] <= '9' ) + ret = (10 * ret) + keyPos[i] - '0'; + i++; + } + return ret; +} //readKey() + +int phoenixOffsetCSASeriesHeader(unsigned char *buff, int lLength) { + //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 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 + TCSAtag tagCSA; + TCSAitem itemCSA; + for (int lT = 1; lT <= lnTag; lT++) { + memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag + //if (!littleEndianPlatform()) + // nifti_swap_4bytes(1, &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; + for (int lI = 1; lI <= tagCSA.nitems; lI++) { + memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA)); + lPos +=sizeof(itemCSA); + //if (!littleEndianPlatform()) + // nifti_swap_4bytes(1, &itemCSA.xx2_Len); + lPos += ((itemCSA.xx2_Len +3)/4)*4; + } + } + return 0; +} // phoenixOffsetCSASeriesHeader() + +int siemensEchoEPIFactor(const char * filename, int csaOffset, int csaLength, int* echoSpacing, int* echoTrainDuration) { + //reads ASCII portion of CSASeriesHeaderInfo and returns lEchoTrainDuration or lEchoSpacing value + // returns 0 if no value found + *echoSpacing = 0; + *echoTrainDuration = 0; + if ((csaOffset < 0) || (csaLength < 8)) return 0; + FILE * pFile = fopen ( filename, "rb" ); + if(pFile==NULL) return 0; + fseek (pFile , 0 , SEEK_END); + long lSize = ftell (pFile); + if (lSize < (csaOffset+csaLength)) { + fclose (pFile); + return 0; + } + fseek(pFile, csaOffset, SEEK_SET); + char * buffer = (char*) malloc (csaLength); + if(buffer == NULL) return 0; + size_t result = fread (buffer,1,csaLength,pFile); + if(result != csaLength) return 0; + //next bit complicated: restrict to ASCII portion to avoid buffer overflow errors in BINARY portion + int startAscii = phoenixOffsetCSASeriesHeader((unsigned char *)buffer, csaLength); + int csaLengthTrim = csaLength; + char * bufferTrim = buffer; + if ((startAscii > 0) && (startAscii < csaLengthTrim) ){ //ignore binary data at start + bufferTrim += startAscii; + csaLengthTrim -= startAscii; + } + int ret = 0; + 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) { + csaLengthTrim -= (keyPos-bufferTrim); + char keyStrEnd[] = "### ASCCONV END"; + char *keyPosEnd = (char *)memmem(keyPos, csaLengthTrim, keyStrEnd, strlen(keyStrEnd)); + if ((keyPosEnd) && ((keyPosEnd - keyPos) < csaLengthTrim)) //ignore binary data at end + csaLengthTrim = (int)(keyPosEnd - keyPos); + char keyStrES[] = "sFastImaging.lEchoSpacing"; + *echoSpacing = readKey(keyStrES, keyPos, csaLengthTrim); + char keyStrETD[] = "sFastImaging.lEchoTrainDuration"; + *echoTrainDuration = readKey(keyStrETD, keyPos, csaLengthTrim); + char keyStrEF[] = "sFastImaging.lEPIFactor"; + ret = readKey(keyStrEF, keyPos, csaLengthTrim); + } + fclose (pFile); + free (buffer); + return ret; +} + +void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct TDTI4D *dti4D, struct nifti_1_header *h, const char * filename) { //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). @@ -366,6 +509,33 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, if (strlen(d.studyInstanceUID) > 0) fprintf(fp, "\t\"StudyInstanceUID\": \"%s\",\n", d.studyInstanceUID ); } + //printMessage("-->%d %d %s\n", d.CSA.SeriesHeader_offset, d.CSA.SeriesHeader_length, filename); + if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (d.CSA.SeriesHeader_offset > 0) && (d.CSA.SeriesHeader_length > 0) && + (strlen(d.scanningSequence) > 1) && (d.scanningSequence[0] == 'E') && (d.scanningSequence[1] == 'P')) { //for EPI scans only + int echoSpacing, echoTrainDuration, epiFactor; + epiFactor = siemensEchoEPIFactor(filename, d.CSA.SeriesHeader_offset, d.CSA.SeriesHeader_length, &echoSpacing, &echoTrainDuration); + //printMessage("ES %d ETD %d EPI %d\n", echoSpacing, echoTrainDuration, epiFactor); + if (echoSpacing > 0) + fprintf(fp, "\t\"EchoSpacing\": %d,\n", echoSpacing); + if (echoTrainDuration > 0) + fprintf(fp, "\t\"EchoTrainDuration\": %d,\n", echoTrainDuration); + if (epiFactor > 0) + fprintf(fp, "\t\"EPIFactor\": %d,\n", epiFactor); + } + 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); + if (d.isNonImage) //DICOM is derived image or non-spatial file (sounds, etc) + fprintf(fp, "\t\"RawImage\": false,\n"); + if (d.acquNum > 0) + fprintf(fp, "\t\"AcquisitionNumber\": %d,\n", d.acquNum); + if (strlen(d.institutionName) > 0) + fprintf(fp, "\t\"InstitutionName\": \"%s\",\n", d.institutionName ); + if (strlen(d.institutionAddress) > 0) + fprintf(fp, "\t\"InstitutionName\": \"%s\",\n", d.institutionAddress ); + if (strlen(d.deviceSerialNumber) > 0) + fprintf(fp, "\t\"DeviceSerialNumber\": \"%s\",\n", d.deviceSerialNumber ); + if (strlen(d.softwareVersions) > 0) + fprintf(fp, "\t\"SoftwareVersions\": \"%s\",\n", d.softwareVersions ); if (strlen(d.procedureStepDescription) > 0) fprintf(fp, "\t\"ProcedureStepDescription\": \"%s\",\n", d.procedureStepDescription ); if (strlen(d.scanningSequence) > 0) @@ -443,6 +613,7 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, if (d.TI > 0.0) fprintf(fp, "\t\"InversionTime\": %g,\n", d.TI / 1000.0 ); if (d.ecat_isotope_halflife > 0.0) fprintf(fp, "\t\"IsotopeHalfLife\": %g,\n", d.ecat_isotope_halflife); if (d.ecat_dosage > 0.0) fprintf(fp, "\t\"Dosage\": %g,\n", d.ecat_dosage); + //fprintf(fp, "\t\"XXXX\": %g,\n", d.CSA.bandwidthPerPixelPhaseEncode ); if ((d.CSA.bandwidthPerPixelPhaseEncode > 0.0) && (h->dim[2] > 0) && (h->dim[1] > 0)) { float dwellTime = 0.0f; if (h->dim[2] == h->dim[2]) //phase encoding does not matter @@ -491,7 +662,7 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, //fprintf(fp, "\t\"DicomConversion\": [\"dcm2niix\", \"%s\"]\n", kDCMvers ); fprintf(fp, "}\n"); fclose(fp); -}// nii_SaveBIDS() step +}// nii_SaveBIDS() 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 @@ -804,6 +975,7 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt int pos = 0; bool isCoilReported = false; bool isEchoReported = false; + bool isSeriesReported = false; while (pos < strlen(inname)) { if (inname[pos] == '%') { if (pos > start) { @@ -859,6 +1031,7 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt if (f == 'S') { sprintf(newstr, "%ld", dcm.seriesNum); strcat (outname,newstr); + isSeriesReported = true; } if (f == 'T') { sprintf(newstr, "%0.0f", dcm.dateTime); @@ -869,7 +1042,7 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt sprintf(newstr, "%d", dcm.acquNum); strcat (outname,newstr); #else - printWarning("Ignoring '%%f' in output filename (recompile to segment by acquisition)\n"); + printWarning("Ignoring '%%u' in output filename (recompile to segment by acquisition)\n"); #endif } if (f == 'Z') @@ -888,21 +1061,27 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt } //found a % character pos++; } //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 (!isCoilReported && (dcm.coilNum > 1)) { sprintf(newstr, "_c%d", dcm.coilNum); strcat (outname,newstr); } - if (!isEchoReported && (dcm.echoNum > 1)) { + if ((!isEchoReported) && (dcm.isMultiEcho) && (dcm.echoNum >= 1)) { //multiple echoes saved as same series sprintf(newstr, "_e%d", dcm.echoNum); strcat (outname,newstr); + isEchoReported = true; } - if (dcm.isHasPhase) - strcat (outname,"_ph"); //manufacturer name not available - if (pos > start) { //append any trailing characters - strncpy(&newstr[0], &inname[0] + start, pos - start); - newstr[pos - start] = '\0'; + if ((!isSeriesReported) && (!isEchoReported) && (dcm.echoNum > 1)) { //last resort: user provided no method to disambiguate echo number in filename + sprintf(newstr, "_e%d", dcm.echoNum); strcat (outname,newstr); } + if (dcm.isHasPhase) + strcat (outname,"_ph"); //manufacturer name not available + 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 @@ -1655,7 +1834,6 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis nConvert = siemensCtKludge(nConvert, dcmSort,dcmList); } if ((nAcq == 1 ) && (dcmList[indx0].locationsInAcquisition > 0)) nAcq = nConvert/dcmList[indx0].locationsInAcquisition; - if (nAcq < 2 ) { nAcq = 0; for (int i = 0; i < nConvert; i++) @@ -1769,7 +1947,7 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis imgM = nii_flipZ(imgM, &hdr0); sliceDir = abs(sliceDir); //change this, we have flipped the image so GE DTI bvecs no longer need to be flipped! } - nii_SaveBIDS(pathoutname, dcmList[dcmSort[0].indx], opts, dti4D, &hdr0); + nii_SaveBIDS(pathoutname, dcmList[dcmSort[0].indx], opts, dti4D, &hdr0, nameList->str[dcmSort[0].indx]); nii_SaveText(pathoutname, dcmList[dcmSort[0].indx], opts, &hdr0, nameList->str[indx]); bool * isADC = nii_SaveDTI(pathoutname,nConvert, dcmSort, dcmList, opts, sliceDir, dti4D); if ((hdr0.datatype == DT_UINT16) && (!dcmList[dcmSort[0].indx].isSigned)) nii_check16bitUnsigned(imgM, &hdr0); @@ -1884,8 +2062,9 @@ TWarnings setWarnings() { return r; } -bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2, bool isForceStackSameSeries,struct TWarnings* warnings) { +bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2, bool isForceStackSameSeries, struct TWarnings* warnings, bool *isMultiEcho) { //returns true if d1 and d2 should be stacked together as a single output + *isMultiEcho = false; if (!d1.isValid) return false; if (!d2.isValid) return false; if (d1.seriesNum != d2.seriesNum) return false; @@ -1904,7 +2083,11 @@ bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2, bool isForceStackSam warnings->bitDepthVaries = true; return false; } - if (isForceStackSameSeries) return true; //we will stack these images, even if they differ in the following attributes + if (isForceStackSameSeries) { + if ((d1.TE != d2.TE) || (d1.echoNum != d2.echoNum)) + *isMultiEcho = true; + return true; //we will stack these images, even if they differ in the following attributes + } if (!isSameFloatDouble(d1.dateTime, d2.dateTime)) { //beware, some vendors incorrectly store Image Time (0008,0033) as Study Time (0008,0030). if (!warnings->dateTimeVaries) printMessage("slices not stacked: Study Data/Time (0008,0020 / 0008,0030) varies %12.12f ~= %12.12f\n", d1.dateTime, d2.dateTime); @@ -1917,6 +2100,7 @@ bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2, bool isForceStackSam if ((!warnings->echoVaries) && (!d1.isXRay)) //for MRI printMessage("slices not stacked: echo varies (TE %g, %g; echo %d, %d)\n", d1.TE, d2.TE,d1.echoNum, d2.echoNum ); warnings->echoVaries = true; + *isMultiEcho = true; return false; } if (d1.coilNum != d2.coilNum) { @@ -2199,26 +2383,23 @@ int nii_loadDir(struct TDCMopts* opts) { #ifdef HAVE_R if (opts->isScanOnly) { TWarnings warnings = setWarnings(); - // Create the first series from the first DICOM file TDicomSeries firstSeries; firstSeries.representativeData = dcmList[0]; firstSeries.files.push_back(nameList.str[0]); opts->series.push_back(firstSeries); - // Iterate over the remaining files for (size_t i = 1; i < nDcm; i++) { bool matched = false; - // If the file matches an existing series, add it to the corresponding file list for (int j = 0; j < opts->series.size(); j++) { - if (isSameSet(opts->series[j].representativeData, dcmList[i], opts->isForceStackSameSeries, &warnings)) { + bool isMultiEchoUnused; + if (isSameSet(opts->series[j].representativeData, dcmList[i], opts->isForceStackSameSeries, &warnings, &isMultiEchoUnused)) { opts->series[j].files.push_back(nameList.str[i]); matched = true; break; } } - // If not, create a new series object if (!matched) { TDicomSeries nextSeries; @@ -2237,24 +2418,22 @@ int nii_loadDir(struct TDCMopts* opts) { if ((dcmList[i].converted2NII == 0) && (dcmList[i].isValid)) { int nConvert = 0; struct TWarnings warnings = setWarnings(); + bool isMultiEcho; for (int j = i; j < nDcm; j++) - if (isSameSet(dcmList[i], dcmList[j], opts->isForceStackSameSeries, &warnings) ) + if (isSameSet(dcmList[i], dcmList[j], opts->isForceStackSameSeries, &warnings, &isMultiEcho ) ) nConvert++; if (nConvert < 1) nConvert = 1; //prevents compiler warning for next line: never executed since j=i always causes nConvert ++ - -//#ifdef _MSC_VER TDCMsort * dcmSort = (TDCMsort *)malloc(nConvert * sizeof(TDCMsort)); -//#else -// struct TDCMsort dcmSort[nConvert]; -//#endif nConvert = 0; - //warnings = setWarnings(); for (int j = i; j < nDcm; j++) - if (isSameSet(dcmList[i], dcmList[j], opts->isForceStackSameSeries, &warnings)) { + if (isSameSet(dcmList[i], dcmList[j], opts->isForceStackSameSeries, &warnings, &isMultiEcho)) { dcmSort[nConvert].indx = j; dcmSort[nConvert].img = ((uint64_t)dcmList[j].seriesNum << 32) + dcmList[j].imageNum; dcmList[j].converted2NII = 1; nConvert++; + } else { + dcmList[i].isMultiEcho = isMultiEcho; + dcmList[j].isMultiEcho = isMultiEcho; } qsort(dcmSort, nConvert, sizeof(struct TDCMsort), compareTDCMsort); //sort based on series and image numbers.... if (opts->isVerbose) @@ -2263,9 +2442,7 @@ int nii_loadDir(struct TDCMopts* opts) { nConvert = removeDuplicates(nConvert, dcmSort); nConvertTotal += nConvert; saveDcm2Nii(nConvert, dcmSort, dcmList, &nameList, *opts, &dti4D); -//#ifdef _MSC_VER free(dcmSort); -//#endif }//convert all images of this series } #ifdef HAVE_R @@ -2381,10 +2558,10 @@ void setDefaultOpts (struct TDCMopts *opts, const char * argv[]) { //either "set opts->isFlipY = true; //false: images in raw DICOM orientation, true: image rows flipped to cartesian coordinates opts->isRGBplanar = false; opts->isCreateBIDS = true; - #ifdef isAnonymizeBIDS - opts->isAnonymizeBIDS = true; - #else + #ifdef myNoAnonymizeBIDS opts->isAnonymizeBIDS = false; + #else + opts->isAnonymizeBIDS = true; #endif opts->isCreateText = false; #ifdef myDebug diff --git a/console/nii_dicom_batch.h b/console/nii_dicom_batch.h index 37a25a8..d15459c 100644 --- a/console/nii_dicom_batch.h +++ b/console/nii_dicom_batch.h @@ -38,7 +38,7 @@ extern "C" { int nii_saveNII(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts); //void readIniFile (struct TDCMopts *opts); int nii_loadDir (struct TDCMopts *opts); - void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct TDTI4D *dti4D, struct nifti_1_header *h); + void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct TDTI4D *dti4D, struct nifti_1_header *h, const char * filename); int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopts opts); void nii_createDummyFilename(char * niiFilename, struct TDCMopts opts); //void findExe(char name[512], const char * argv[]); diff --git a/console/nii_foreign.cpp b/console/nii_foreign.cpp index 1096201..97ad9dc 100644 --- a/console/nii_foreign.cpp +++ b/console/nii_foreign.cpp @@ -48,7 +48,7 @@ */ void strClean(char * cString) { - int len = strlen(cString); + int len = (int)strlen(cString); if (len < 1) return; for (int i = 0; i < len; i++) { char c = cString[i]; @@ -304,6 +304,10 @@ PACK( typedef struct { for (int v = 0; v < num_vol; v++) { fseek(f, imgOffsets[v] * 512, SEEK_SET); size_t sz = fread( &img[v * bytesPerVolume], 1, bytesPerVolume, f); + if (sz != bytesPerVolume) { + free(img); + return NULL; + } } if ((swapEndian) && (bytesPerVoxel == 2)) nifti_swap_2bytes(ihdr.x_dimension * ihdr.y_dimension * ihdr.z_dimension * num_vol, img); if ((swapEndian) && (bytesPerVoxel == 4)) nifti_swap_4bytes(ihdr.x_dimension * ihdr.y_dimension * ihdr.z_dimension * num_vol, img); @@ -397,8 +401,8 @@ int convert_foreign (const char *fn, struct TDCMopts opts){ if (ret != EXIT_SUCCESS) return ret; struct TDTI4D dti4D; dti4D.S[0].sliceTiming = -1.0; - nii_SaveBIDS(niiFilename, dcm, opts, &dti4D, &hdr); + nii_SaveBIDS(niiFilename, dcm, opts, &dti4D, &hdr, fn); ret = nii_saveNII(niiFilename, hdr, img, opts); free(img); return ret; -}// open_foreign() +}// convert_foreign() -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/dcm2niix.git _______________________________________________ debian-med-commit mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/debian-med-commit
