[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