Skip to content

Commit

Permalink
set isForceStackSameSeries to 1 (freesurfer#904)
Browse files Browse the repository at this point in the history
* 1. check in package dcm2niix
2. add DICOMRead3/niiRead3 to convert dicom using dcm2niix
3. fix dcmGetDWIParams() to use stricmp() to compare the Manufacturer tag

* set TI to -1 when there is no TI in dicom file for MGH_FREESURFER

* remove unused codes

* This is the correct version.

* redo the check in

* For some unknown reasons, dcm2niix and dcm2niixfsexe executables failed linking in nightly builds.
They are excluded from the build until we figure out why.
libdcm2niixfs.a is renamed to libdcm2niix.a

* 1. rename dcm2fsWrapper.h => dcm2niix_fswrapper.h, dcm2fsWrapper.cpp => dcm2niix_fswrapper.cpp
2. add comments to dcm2niix changes

* fix bvecs

* 1. introduced new type MRI_USHRT (10) - unsigned short,
   convert DT_UINT16 to MRI_USHRT, MRI_USHRT to DT_UINT16
2. added macro MRIUSseq_vox & MRIUSvox to access voxel in pre-allocated memory
3. added handling of type MRI_USHRT in various functions
4. introduced environment variable FS_MGZIO_USEVOXELBUF to read/write (mgzRead/mgzWrite)
   voxels in one buffer access. FS_MGZIO_USEVOXELBUF is ignored if mri voxel buffer is
   not allocated as a big chunk.
5. introduced environment variable FS_MGZIO_TIMING to time voxel read/write in mgzRead/mgzWrite

* ignore FS_MGZIO_USEVOXELBUF if mri voxel buffer is not allocated as a big chunk

* add dcm2niix license.txt

* license files for dcm2niix - license.dcm2niix.txt and license.charls.txt

* 1. set isForceStackSameSeries = 1 in dcm2niix_fswrapper (dcm2niix command line option - '-m y' merge 2D slice)
2. print messages for slices stacked

Co-authored-by: Yujing Huang <[email protected]>
  • Loading branch information
yhuang43 and Yujing Huang authored Dec 21, 2021
1 parent 877d5e3 commit 5403612
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 10 deletions.
132 changes: 131 additions & 1 deletion packages/dcm2niix/dcm2niix_fswrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* niioutdir)
tdcmOpts.isIgnoreSeriesInstanceUID = true;
tdcmOpts.isCreateBIDS = false;
tdcmOpts.isGz = false;
//tdcmOpts.isForceStackSameSeries = 1; // merge 2D slice '-m y'
tdcmOpts.isForceStackSameSeries = 1; // merge 2D slice '-m y'
tdcmOpts.isForceStackDCE = false;
//tdcmOpts.isForceOnsetTimes = false;
}
Expand Down Expand Up @@ -119,3 +119,133 @@ const unsigned char* dcm2niix_fswrapper::getMRIimg(void)
return mrifsStruct->imgM;
}

void dcm2niix_fswrapper::dicomDump(const char* dicomdir)
{
strcpy(tdcmOpts.indir, dicomdir);
tdcmOpts.isDumpNotConvert = true;
nii_loadDirCore(tdcmOpts.indir, &tdcmOpts);

return;

#if 0
struct TSearchList nameList;
int nConvertTotal = 0;
#if defined(_WIN64) || defined(_WIN32) || defined(USING_R)
nameList.maxItems = 24000; // larger requires more memory, smaller more passes
#else //UNIX, not R
nameList.maxItems = 96000; // larger requires more memory, smaller more passes
#endif

if ((is_fileNotDir(opts->indir)) && isExt(opts->indir, ".txt")) {
nameList.str = (char **)malloc((nameList.maxItems + 1) * sizeof(char *)); //reserve one pointer (32 or 64 bits) per potential file
nameList.numItems = 0;
FILE *fp = fopen(opts->indir, "r"); //textDICOM
if (fp == NULL)
return EXIT_FAILURE;
char dcmname[2048];
while (fgets(dcmname, sizeof(dcmname), fp)) {
int sz = (int)strlen(dcmname);
if (sz > 0 && dcmname[sz - 1] == '\n')
dcmname[sz - 1] = 0; //Unix LF
if (sz > 1 && dcmname[sz - 2] == '\r')
dcmname[sz - 2] = 0; //Windows CR/LF
if ((!is_fileexists(dcmname)) || (!is_fileNotDir(dcmname))) { //<-this will accept meta data
fclose(fp);
printError("Problem with file '%s'\n", dcmname);
return EXIT_FAILURE;
}
if (nameList.numItems < nameList.maxItems) {
nameList.str[nameList.numItems] = (char *)malloc(strlen(dcmname) + 1);
strcpy(nameList.str[nameList.numItems], dcmname);
}
nameList.numItems++;
}
fclose(fp);
if (nameList.numItems >= nameList.maxItems) {
printError("Too many file names in '%s'\n", opts->indir);
return EXIT_FAILURE;
}
if (nameList.numItems < 1)
return kEXIT_NO_VALID_FILES_FOUND;
printMessage("Found %lu files in '%s'\n", nameList.numItems, opts->indir);
} else {
//1: find filenames of dicom files: up to two passes if we found more files than we allocated memory
for (int i = 0; i < 2; i++) {
nameList.str = (char **)malloc((nameList.maxItems + 1) * sizeof(char *)); //reserve one pointer (32 or 64 bits) per potential file
nameList.numItems = 0;
int ret = searchDirForDICOM(indir, &nameList, opts->dirSearchDepth, 0, opts);
if (ret == EXIT_SUCCESS) //e.g. converted ECAT
nConvertTotal++;
if (nameList.numItems <= nameList.maxItems)
break;
freeNameList(nameList);
nameList.maxItems = nameList.numItems + 1;
//printMessage("Second pass required, found %ld images\n", nameList.numItems);
}

if (nameList.numItems < 1) {
if ((opts->dirSearchDepth > 0) && (nConvertTotal < 1))
printError("Unable to find any DICOM images in %s (or subfolders %d deep)\n", indir, opts->dirSearchDepth);
else //keep silent for dirSearchDepth = 0 - presumably searching multiple folders
{
};
free(nameList.str); //ignore compile warning - memory only freed on first of 2 passes
if (nConvertTotal > 0) return EXIT_SUCCESS; //e.g. converted ECAT
return kEXIT_NO_VALID_FILES_FOUND;
}
}

size_t nDcm = nameList.numItems;
printMessage("Found %lu DICOM file(s)\n", nameList.numItems); //includes images and other non-image DICOMs

struct TDICOMdata *dcmList = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct TDICOMdata));
struct TDTI4D *dti4D = (struct TDTI4D *)malloc(sizeof(struct TDTI4D));
struct TDCMprefs prefs;
opts2Prefs(opts, &prefs);
bool compressionWarning = false;
bool convertError = false;
bool isDcmExt = isExt(opts->filename, ".dcm"); // "%r.dcm" with multi-echo should generate "1.dcm", "1e2.dcm"
if (isDcmExt)
opts->filename[strlen(opts->filename) - 4] = 0; // "%s_%r.dcm" -> "%s_%r"
//consider OpenMP
// g++-9 -I. main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -o dcm2niix -DmyDisableOpenJPEG -fopenmp

for (int i = 0; i < (int)nDcm; i++) {
if ((isExt(nameList.str[i], ".par")) && (isDICOMfile(nameList.str[i]) < 1)) {
//strcpy(opts->indir, nameList.str[i]); //set to original file name, not path
dcmList[i].converted2NII = 1;
int ret = convert_parRec(nameList.str[i], *opts);
if (ret == EXIT_SUCCESS)
nConvertTotal++;
else
convertError = true;
continue;
}

dcmList[i] = readDICOMx(nameList.str[i], &prefs, dti4D); //ignore compile warning - memory only freed on first of 2 passes
//dcmList[i] = readDICOMv(nameList.str[i], opts->isVerbose, opts->compressFlag, dti4D); //ignore compile warning - memory only freed on first of 2 passes
if (opts->isIgnoreSeriesInstanceUID)
dcmList[i].seriesUidCrc = dcmList[i].seriesNum;
//if (!dcmList[i].isValid) printf(">>>>Not a valid DICOM %s\n", nameList.str[i]);

if ((dcmList[i].isValid) && ((dti4D->sliceOrder[0] >= 0) || (dcmList[i].CSA.numDti > 1))) { //4D dataset: dti4D arrays require huge amounts of RAM - write this immediately
struct TDCMsort dcmSort[1];
fillTDCMsort(dcmSort[0], i, dcmList[i]);
//printMessage("***MGH_FREESURFER***: calling saveDcm2Nii() (%s:%s:%d)\n", __FILE__, __func__, __LINE__);
dcmList[i].converted2NII = 1;
int ret = saveDcm2Nii(1, dcmSort, dcmList, &nameList, *opts, dti4D);
if (ret == EXIT_SUCCESS)
nConvertTotal++;
else
convertError = true;
}

if ((dcmList[i].compressionScheme != kCompressNone) && (!compressionWarning) && (opts->compressFlag != kCompressNone)) {
compressionWarning = true; //generate once per conversion rather than once per image
printMessage("Image Decompression is new: please validate conversions\n");
}
if (opts->isProgress)
progressPct = reportProgress(progressPct, kStage1Frac + (kStage2Frac * (float)i / (float)nDcm)); //proportion correct, 0..100
}
#endif
}
2 changes: 2 additions & 0 deletions packages/dcm2niix/dcm2niix_fswrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class dcm2niix_fswrapper
// return image data saved in MRIFSSTRUCT
static const unsigned char* getMRIimg(void);

static void dicomDump(const char* dicomdir);

private:
static struct TDCMopts tdcmOpts;
};
Expand Down
3 changes: 3 additions & 0 deletions packages/dcm2niix/nii_dicom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3549,6 +3549,7 @@ unsigned char *nii_loadImgJPEGLS(char *imgname, struct nifti_1_header hdr, struc
#ifdef myEnableJPEGLS1
if (JpegLsReadHeader(cImg, dcm.imageBytes, &params) != OK) {
#else
printMessage("myEnableJPEGLS defined. JpegLsReadHeader() ...\n");
using namespace charls;
if (JpegLsReadHeader(cImg, dcm.imageBytes, &params, nullptr) != ApiResult::OK) {
#endif
Expand All @@ -3558,6 +3559,7 @@ unsigned char *nii_loadImgJPEGLS(char *imgname, struct nifti_1_header hdr, struc
#ifdef myEnableJPEGLS1
if (JpegLsDecode(&bImg[0], imgsz, &cImg[0], dcm.imageBytes, &params) != OK) {
#else
printMessage("myEnableJPEGLS defined. JpegLsDecode() ...\n");
if (JpegLsDecode(&bImg[0], imgsz, &cImg[0], dcm.imageBytes, &params, nullptr) != ApiResult::OK) {
#endif
free(bImg);
Expand Down Expand Up @@ -3585,6 +3587,7 @@ unsigned char *nii_loadImgXL(char *imgname, struct nifti_1_header *hdr, struct T
#endif
} else if (dcm.compressionScheme == kCompressJPEGLS) {
#if defined(myEnableJPEGLS) || defined(myEnableJPEGLS1)
printMessage("myEnableJPEGLS defined. nii_loadImgJPEGLS() ...\n");
img = nii_loadImgJPEGLS(imgname, *hdr, dcm);
if (hdr->datatype == DT_RGB24) //convert to planar
img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB); //do this BEFORE Y-Flip, or RGB order can be flipped
Expand Down
102 changes: 93 additions & 9 deletions packages/dcm2niix/nii_dicom_batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6244,7 +6244,7 @@ int saveDcm2NiiCore(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata d
} //skip if we are only creating BIDS
if (hdr0.dim[4] > 1) //for 4d datasets, last volume should be acquired before first
checkDateTimeOrder(&dcmList[dcmSort[0].indx], &dcmList[dcmSort[nConvert - 1].indx]);
}
} // end of if (nConvert > 1)
if (opts.isVerbose > 1)
reportProtocolBlockGE(&dcmList[indx0], nameList->str[dcmSort[0].indx]);
int sliceDir = sliceTimingCore(dcmSort, dcmList, &hdr0, opts.isVerbose, nameList->str[dcmSort[0].indx], nConvert, opts);
Expand Down Expand Up @@ -6577,6 +6577,27 @@ int saveDcm2NiiCore(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata d
} // saveDcm2NiiCore()

int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata dcmList[], struct TSearchList *nameList, struct TDCMopts opts, struct TDTI4D *dti4D) {
#ifdef USING_DCM2NIIXFSWRAPPER
if (opts.isDumpNotConvert) {
int indx0 = dcmSort[0].indx;
if (opts.isIgnoreSeriesInstanceUID)
printMessage("%d %s %s (total %d)\n", dcmList[indx0].seriesUidCrc, dcmList[indx0].protocolName, nameList->str[indx0], nConvert);
else
printMessage("%d %ld %s %s (total %d)\n", dcmList[indx0].seriesUidCrc, dcmList[indx0].seriesNum, dcmList[indx0].protocolName, nameList->str[indx0], nConvert);

#if 0
for (int i = 0; i < nConvert; i++) {
int indx = dcmSort[i].indx;
if (opts.isIgnoreSeriesInstanceUID)
printMessage("\t#\%d: %d %s\n", i+1, dcmList[indx].seriesUidCrc, nameList->str[indx]);
else
printMessage("\t#\%d: %d %ld %s\n", i+1, dcmList[indx].seriesUidCrc, dcmList[indx].seriesNum, nameList->str[indx]);
}
#endif

return 0;
}
#endif
//this wrapper does nothing if all the images share the same echo time and scale
// however, it segments images when these properties vary
uint64_t indx = dcmSort[0].indx;
Expand Down Expand Up @@ -6774,7 +6795,7 @@ int isSameFloatDouble(double a, double b) {
}

struct TWarnings { //generate a warning only once per set
bool manufacturerVaries, modalityVaries, derivedVaries, acqNumVaries, dimensionVaries, dateTimeVaries, studyUidVaries, echoVaries, triggerVaries, phaseVaries, coilVaries, forceStackSeries, seriesUidVaries, nameVaries, nameEmpty, orientVaries;
bool manufacturerVaries, modalityVaries, derivedVaries, acqNumVaries, dimensionVaries, dateTimeVaries, studyUidVaries, echoVaries, triggerVaries, phaseVaries, coilVaries, forceStackSeries, seriesUidVaries, nameVaries, nameEmpty, orientVaries, nonParallelMosaicsSlices;
};

TWarnings setWarnings() {
Expand All @@ -6795,6 +6816,7 @@ TWarnings setWarnings() {
r.nameVaries = false;
r.nameEmpty = false;
r.orientVaries = false;
r.nonParallelMosaicsSlices = false;
return r;
}

Expand Down Expand Up @@ -6895,9 +6917,65 @@ bool isSameSet(struct TDICOMdata d1, struct TDICOMdata d2, struct TDCMopts *opts
// *isMultiEcho = true;
//}
#ifdef USING_DCM2NIIXFSWRAPPER
printf("isForceStackSameSeries = true, seriesNum %ld, %ld, seriesInstanceUidCrc %d, %d\n", d1.seriesNum, d2.seriesNum, d1.seriesUidCrc, d2.seriesUidCrc);
#endif
return true; //we will stack these images, even if they differ in the following attributes
if ((d1.isHasImaginary != d2.isHasImaginary) || (d1.isHasPhase != d2.isHasPhase) || (d1.isHasReal != d2.isHasReal)) {
if (!warnings->phaseVaries)
printMessage("Slices stacked: some are phase/real/imaginary/phase maps, others are not. Instances %d %d\n", d1.imageNum, d2.imageNum);
warnings->phaseVaries = true;
} else if ((!(isSameFloat(d1.TE, d2.TE))) || (d1.echoNum != d2.echoNum)) {
if ((!warnings->echoVaries) && (d1.isXRay)) //for CT/XRay we check DICOM tag 0018,1152 (XRayExposure)
printMessage("Slices stacked: X-Ray Exposure varies (exposure %g, %g; number %d, %d).\n", d1.TE, d2.TE, d1.echoNum, d2.echoNum);
if ((!warnings->echoVaries) && (!d1.isXRay)) //for MRI
printMessage("Slices stacked: echo varies (TE %g, %g; echo %d, %d).\n", d1.TE, d2.TE, d1.echoNum, d2.echoNum);
warnings->echoVaries = true;
*isMultiEcho = true;
} else if ((d1.triggerDelayTime != d2.triggerDelayTime) && (d1.manufacturer == kMANUFACTURER_PHILIPS) && (d1.aslFlags == kASL_FLAG_NONE)) { //issue 384
if (!warnings->triggerVaries)
printMessage("Slices stacked: trigger time varies\n");
warnings->triggerVaries = true;
} else if (d1.coilCrc != d2.coilCrc) {
if (opts->isForceStackDCE) {
if (!warnings->coilVaries)
printMessage("Slices stacked despite coil variation '%s' vs '%s' (use '-m o' to turn off merging)\n", d1.coilName, d2.coilName);
warnings->coilVaries = true;
*isCoilVaries = true;
} else {
if (!warnings->coilVaries)
printMessage("Slices stacked: coil varies '%s' vs '%s'\n", d1.coilName, d2.coilName);
warnings->coilVaries = true;
*isCoilVaries = true;
}
} else if ((strlen(d1.protocolName) < 1) && (strlen(d2.protocolName) < 1)) {
if (!warnings->nameEmpty)
printWarning("Empty protocol name(s) (0018,1030)\n");
warnings->nameEmpty = true;
} else if ((strcmp(d1.protocolName, d2.protocolName) != 0)) {
if (!warnings->nameVaries)
printMessage("Slices stacked: protocol name varies '%s' != '%s'\n", d1.protocolName, d2.protocolName);
warnings->nameVaries = true;
} else if ((*isNonParallelSlices) && (d1.CSA.mosaicSlices > 1)) {
if (!warnings->nonParallelMosaicsSlices)//issue 481
printMessage("Slices stacked: non-parallel mosaics slices\n");
warnings->nonParallelMosaicsSlices = true;

} else if ((!isSameFloatGE(d1.orient[1], d2.orient[1]) || !isSameFloatGE(d1.orient[2], d2.orient[2]) || !isSameFloatGE(d1.orient[3], d2.orient[3]) ||
!isSameFloatGE(d1.orient[4], d2.orient[4]) || !isSameFloatGE(d1.orient[5], d2.orient[5]) || !isSameFloatGE(d1.orient[6], d2.orient[6]))) {
if ((!warnings->orientVaries) && (!d1.isNonParallelSlices) && (!d1.isLocalizer))
printMessage("Slices stacked: orientation varies (vNav or localizer?) [%g %g %g %g %g %g] != [%g %g %g %g %g %g]\n",
d1.orient[1], d1.orient[2], d1.orient[3], d1.orient[4], d1.orient[5], d1.orient[6],
d2.orient[1], d2.orient[2], d2.orient[3], d2.orient[4], d2.orient[5], d2.orient[6]);
warnings->orientVaries = true;
*isNonParallelSlices = true;
} else if (d1.acquNum != d2.acquNum) {
if ((!warnings->acqNumVaries) && (opts->isVerbose)) //virtually always people want to stack these
printMessage("Slices stacked despite varying acquisition numbers (if this is not desired recompile with 'mySegmentByAcq')\n");
warnings->acqNumVaries = true;
} else if ((!isForceStackSeries) && (d1.seriesUidCrc != d2.seriesUidCrc)) {
if (!warnings->seriesUidVaries)
printMessage("Slices stacked: series instance UID varies (duplicates all other properties)\n");
warnings->seriesUidVaries = true;
}
#endif
return true; //we will stack these images, even if they differ in the following attributes
}
if ((d1.isHasImaginary != d2.isHasImaginary) || (d1.isHasPhase != d2.isHasPhase) || (d1.isHasReal != d2.isHasReal)) {
if (!warnings->phaseVaries)
Expand Down Expand Up @@ -7639,6 +7717,8 @@ int nii_loadDirCore(char *indir, struct TDCMopts *opts) {
nConvert++;
}
} //for all images with same seriesUID as first one

#ifndef USING_DCM2NIIXFSWRAPPER
if ((isNonParallelSlices) && (dcmList[ii].CSA.mosaicSlices > 1) && (nConvert > 0)) { //issue481: if ANY volumes are non-parallel, save ALL as 3D
printWarning("Saving mosaics with non-parallel slices as 3D (issue 481)\n");
for (int j = i; j < (int)nDcm; j++) {
Expand All @@ -7661,6 +7741,8 @@ int nii_loadDirCore(char *indir, struct TDCMopts *opts) {
}
continue;
} //issue481
#endif

//issue 381: ensure all images are informed if there are variations in echo, parallel slices, coil name:
if (isMultiEcho)
for (int j = i; j <= jMax; j++) {
Expand Down Expand Up @@ -7727,7 +7809,7 @@ int nii_loadDirOneDirAtATime(char *path, struct TDCMopts *opts, int maxDepth, in
//return kEXIT_NO_VALID_FILES_FOUND if no files in ANY sub folders
//return EXIT_FAILURE if ANY failure
//return EXIT_SUCCESS if no failures and at least one image converted
int ret = nii_loadDirCore(path, opts);
int ret = nii_loadDirCore(path, opts);
if (ret == EXIT_FAILURE)
return ret;
tinydir_dir dir;
Expand Down Expand Up @@ -7828,7 +7910,7 @@ int nii_loadDir(struct TDCMopts *opts) {
#endif
if (is_fileNotDir(rname) && is_fileNotDir(pname)) {
//strcpy(opts->indir, pname); //set to original file name, not path
return convert_parRec(pname, *opts);
return convert_parRec(pname, *opts);
};
}
if (isFile && (opts->isOnlySingleFile) && isExt(indir, ".txt")) {
Expand All @@ -7847,14 +7929,14 @@ int nii_loadDir(struct TDCMopts *opts) {
return EXIT_SUCCESS;
}
if ((isFile) && (opts->isOnlySingleFile))
return singleDICOM(opts, indir);
return singleDICOM(opts, indir);
if (opts->isOneDirAtATime) {
int maxDepth = opts->dirSearchDepth;
opts->dirSearchDepth = 0;
strcpy(indir, opts->indir);
return nii_loadDirOneDirAtATime(indir, opts, maxDepth, 0);
} else
return nii_loadDirCore(opts->indir, opts);
return nii_loadDirCore(opts->indir, opts);
} // nii_loadDir()

#if defined(_WIN64) || defined(_WIN32) || defined(USING_R)
Expand Down Expand Up @@ -8071,6 +8153,8 @@ void setDefaultOpts(struct TDCMopts *opts, const char *argv[]) { //either "setDe
opts->numSeries = 0;
memset(opts->seriesNumber, 0, sizeof(opts->seriesNumber));
strcpy(opts->filename, "%f_%p_%t_%s");

opts->isDumpNotConvert = false;
} // setDefaultOpts()

#if defined(_WIN64) || defined(_WIN32)
Expand Down
1 change: 1 addition & 0 deletions packages/dcm2niix/nii_dicom_batch.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ void nii_clrMrifsStruct();
#define MAX_NUM_SERIES 16

struct TDCMopts {
bool isDumpNotConvert;
bool isIgnoreTriggerTimes, isTestx0021x105E, isAddNamePostFixes, isSaveNativeEndian, isOneDirAtATime, isRenameNotConvert, isSave3D, isGz, isPipedGz, isFlipY, isCreateBIDS, isSortDTIbyBVal, isAnonymizeBIDS, isOnlyBIDS, isCreateText, isForceOnsetTimes,isIgnoreDerivedAnd2D, isPhilipsFloatNotDisplayScaling, isTiltCorrect, isRGBplanar, isOnlySingleFile, isForceStackDCE, isIgnoreSeriesInstanceUID, isRotate3DAcq, isCrop;
int saveFormat, isMaximize16BitRange, isForceStackSameSeries, nameConflictBehavior, isVerbose, isProgress, compressFlag, dirSearchDepth, gzLevel; //support for compressed data 0=none,
char filename[512], outdir[512], indir[512], pigzname[512], optsname[512], indirParent[512], imageComments[24];
Expand Down

0 comments on commit 5403612

Please sign in to comment.