[med-svn] [dcm2niix] 01/03: New upstream version 1.0.20170528

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Sat Jun 3 08:19:48 UTC 2017


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 <ghisvail at gmail.com>
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 <pascal.gloor at spale.com>
+ *
+ * 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 at 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



More information about the debian-med-commit mailing list