Skip to content

Commit

Permalink
Merge pull request #702 from yhuang43/development
Browse files Browse the repository at this point in the history
dcm2niix_fswrapper and libdcm2niixfs.a
  • Loading branch information
neurolabusc authored Apr 22, 2023
2 parents 4c1e2b9 + 1cae4ee commit 8df37c7
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 28 deletions.
16 changes: 13 additions & 3 deletions console/dcm2niix_fswrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ numSeries = 0
*/

// set TDCMopts defaults, overwrite settings to output in mgz orientation
void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* niioutdir)
void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* niioutdir, bool createBIDS)
{
memset(&tdcmOpts, 0, sizeof(tdcmOpts));
setDefaultOpts(&tdcmOpts, NULL);
Expand All @@ -62,13 +62,15 @@ void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* niioutdir)
if (niioutdir != NULL)
strcpy(tdcmOpts.outdir, niioutdir);

strcpy(tdcmOpts.filename, "%4s.%p");

// set the options for freesurfer mgz orientation
tdcmOpts.isRotate3DAcq = false;
tdcmOpts.isFlipY = false;
tdcmOpts.isIgnoreSeriesInstanceUID = true;
tdcmOpts.isCreateBIDS = false;
tdcmOpts.isCreateBIDS = createBIDS;
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 +121,11 @@ 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;
}
4 changes: 3 additions & 1 deletion console/dcm2niix_fswrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class dcm2niix_fswrapper
{
public:
// set TDCMopts defaults, overwrite settings to output in mgz orientation.
static void setOpts(const char* dcmindir, const char* niioutdir);
static void setOpts(const char* dcmindir, const char* niioutdir=NULL, bool createBIDS=false);

// interface to isDICOMfile() in nii_dicom.cpp
static bool isDICOM(const char* file);
Expand All @@ -35,6 +35,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
131 changes: 114 additions & 17 deletions console/nii_dicom_batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2539,6 +2539,82 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st
vx[i].V[1] = -vx[i].V[1];
} //for each direction
} //if not a mosaic

#ifdef USING_DCM2NIIXFSWRAPPER
// make adjustments for MGH bvecs output
for (int i = 0; i < (numDti); i++) {
if (sliceDir < 0)
{
// at this point, bvecs output is calculated as not isFlipY, assuming isFlipZ, determinant is positive.
// So, bvecs first column is reversed for FSL.
// MGH conversion: not isFlipY, slice direction not flipped, determinant is negative,
// 1. we need to reverse bvecs column 1 back,
// 2. also need to reverse bvecs column 3

float tmp = vx[i].V[1];
vx[i].V[1] = -vx[i].V[1];
if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0)
{
if (i < 6)
printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. sliceDir < 0) flip bvecs sign column 1: %f => %f\n", tmp, vx[i].V[1]);
}

if (fabs(vx[i].V[3]) > FLT_EPSILON)
{
tmp = vx[i].V[3];
vx[i].V[3] = -vx[i].V[3];
if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0)
{
if (i < 6)
printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. sliceDir < 0) flip bvecs sign column 3: %f => %f\n", tmp, vx[i].V[3]);
}
}
}
else if (abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant)
{
// swap signs for every column
for (int j = 1; j < 4; j++)
{
if (fabs(vx[i].V[j]) > FLT_EPSILON)
{
float tmp = vx[i].V[j];
vx[i].V[j] = -vx[i].V[j];
if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0)
{
if (i < 6)
printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant) flip bvecs sign column j: %f => %f\n", tmp, vx[i].V[j]);
}
}
}
}
else // sliceDir >= 0 && abs(sliceDir) != kSliceOrientMosaicNegativeDeterminant
{
// MGH conversion: not flip Y, image determinant is positive, bvecs first column is reversed for FSL.
// So, we need to flip bvecs first column.
if (fabs(vx[i].V[1]) > FLT_EPSILON)
{
float tmp = vx[i].V[1];
vx[i].V[1] = -vx[i].V[1];
if (getenv("DCM2NIIXFSWRAPPER_DEBUG") != NULL && strcmp(getenv("DCM2NIIXFSWRAPPER_DEBUG"), "yes") == 0)
{
if (i < 6)
printf("nii_saveDTI() (BVECS_DEBUG) (mgh adj. abs(sliceDir) != kSliceOrientMosaicNegativeDeterminant) flip bvecs sign column 1: %f => %f\n", tmp, vx[i].V[1]);
}
}
}
} //for each direction

mrifsStruct.numDti = numDti;
mrifsStruct.tdti = (TDTI *)malloc(numDti * sizeof(TDTI));
for (int i = 0; i < numDti; i++)
{
mrifsStruct.tdti[i].V[0] = vx[i].V[0];
mrifsStruct.tdti[i].V[1] = vx[i].V[1];
mrifsStruct.tdti[i].V[2] = vx[i].V[2];
mrifsStruct.tdti[i].V[3] = vx[i].V[3];
}
#endif

if (opts.isVerbose) {
for (int i = 0; i < (numDti); i++) {
printMessage("%d\tB=\t%g\tVec=\t%g\t%g\t%g\n", i, vx[i].V[0], vx[i].V[1], vx[i].V[2], vx[i].V[3]);
Expand All @@ -2564,12 +2640,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st
for (int v = 0; v < 4; v++) //for each vector+B-value
dti4D->S[i].V[v] = vx[i].V[v];
}
#ifdef USING_DCM2NIIXFSWRAPPER
mrifsStruct.tdti = vx;
mrifsStruct.numDti = numDti;
#else
free(vx);
#endif
return volOrderIndex;
}

Expand All @@ -2594,12 +2665,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st
fclose(fp);
#endif
if (isIsotropic) { //issue 405: ISOTROPIC images have bval but not bvec
#ifdef USING_DCM2NIIXFSWRAPPER
mrifsStruct.tdti = vx;
mrifsStruct.numDti = numDti;
#else
free(vx);
#endif
return volOrderIndex;
}

Expand Down Expand Up @@ -2629,12 +2695,7 @@ int *nii_saveDTI(char pathoutname[], int nConvert, struct TDCMsort dcmSort[], st
#endif
#endif

#ifdef USING_DCM2NIIXFSWRAPPER
mrifsStruct.tdti = vx;
mrifsStruct.numDti = numDti;
#else
free(vx);
#endif
return volOrderIndex;
} // nii_saveDTI()

Expand Down Expand Up @@ -7448,6 +7509,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 1
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 @@ -7772,7 +7854,18 @@ 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);
/* for mgh conversion set opts->isForceStackSameSeries = 1 by default, *isMultiEcho, *isNonParallelSlices, and *isCoilVaries remain unchanged.
*
* local variable isForceStackSeries is set to true if following condition met:
* if ((opts->isForceStackDCE) && (d1.isStackableSeries) && (d2.isStackableSeries) && (d1.seriesNum != d2.seriesNum)) {
* if (!warnings->forceStackSeries)
* printMessage("Volumes stacked despite varying series number (use '-m o' to turn off merging).\n");
* warnings->forceStackSeries = true;
* isForceStackSeries = true;
* }
*/

//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
}
Expand Down Expand Up @@ -8466,7 +8559,7 @@ int nii_loadDirCore(char *indir, struct TDCMopts *opts) {
fillTDCMsort(dcmSort[nConvert], j, dcmList[j]);
nConvert++;
} else {
if (isNonParallelSlices) {
if (isNonParallelSlices) {
dcmList[i].isNonParallelSlices = true;
dcmList[j].isNonParallelSlices = true;
}
Expand Down Expand Up @@ -8535,6 +8628,8 @@ int nii_loadDirCore(char *indir, struct TDCMopts *opts) {
nConvert++;
}
} //for all images with same seriesUID as first one

// MGH set Opts.isForceStackSameSeries = 1 by default, isMultiEcho, isNonParallelSlices, isCoilVaries remain false for MGH default run after isSameSet
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 Down Expand Up @@ -8986,6 +9081,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
15 changes: 8 additions & 7 deletions console/nii_dicom_batch.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ extern "C" {

typedef struct
{
struct nifti_1_header hdr0;
struct nifti_1_header hdr0;

size_t imgsz;
unsigned char *imgM;
size_t imgsz;
unsigned char *imgM;

struct TDICOMdata tdicomData;
struct TDICOMdata tdicomData;

struct TDTI *tdti;
int numDti;
struct TDTI *tdti;
int numDti;
} MRIFSSTRUCT;

MRIFSSTRUCT* nii_getMrifsStruct();
Expand All @@ -57,6 +57,7 @@ void nii_clrMrifsStruct();
#define kOptsStr 512

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, onlySearchDirForDICOM, gzLevel, diffCyclingModeGE; //support for compressed data 0=none,
char filename[kOptsStr], outdir[kOptsStr], indir[kOptsStr], pigzname[kOptsStr], optsname[kOptsStr], indirParent[kOptsStr], imageComments[24];
Expand All @@ -78,7 +79,7 @@ void nii_clrMrifsStruct();
void readIniFile (struct TDCMopts *opts, const char * argv[]);
int nii_saveNIIx(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts);
int nii_loadDir(struct TDCMopts *opts);
int nii_loadDirCore(char *indir, struct TDCMopts* opts);
int nii_loadDirCore(char *indir, struct TDCMopts* opts);
void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, 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);
Expand Down

0 comments on commit 8df37c7

Please sign in to comment.