Skip to content

Commit

Permalink
DCM2NIIXFSLIB changes
Browse files Browse the repository at this point in the history
  - dcm2niix_fswrapper: allow subset of dcm2niix options to be set through mri_convert
  - dcmListDump(): use environment variable MGH_DCMUNPACK_IMAGELIST to communicate with dcm2niix where to output imagelist.dat
  - retrieve/output 'pulseSequenceDetails (tSequenceFileName)' from DICOM CSA header (0029,1020)
  • Loading branch information
yhuang43 committed Feb 28, 2024
1 parent 66f6427 commit 79bcd4c
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 9 deletions.
138 changes: 131 additions & 7 deletions console/dcm2niix_fswrapper.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>

#include "nii_dicom.h"
Expand Down Expand Up @@ -53,15 +54,13 @@ numSeries = 0
*/

// set TDCMopts defaults, overwrite settings to output in mgz orientation
void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* niioutdir, bool createBIDS, int ForceStackSameSeries)
void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* dcm2niixopts)
{
memset(&tdcmOpts, 0, sizeof(tdcmOpts));
setDefaultOpts(&tdcmOpts, NULL);

if (dcmindir != NULL)
strcpy(tdcmOpts.indir, dcmindir);
if (niioutdir != NULL)
strcpy(tdcmOpts.outdir, niioutdir);

// dcmunpack actually uses seriesDescription, set FName = `printf %04d.$descr $series`
// change it from "%4s.%p" to "%4s.%d"
Expand All @@ -71,11 +70,136 @@ void dcm2niix_fswrapper::setOpts(const char* dcmindir, const char* niioutdir, bo
tdcmOpts.isRotate3DAcq = false;
tdcmOpts.isFlipY = false;
tdcmOpts.isIgnoreSeriesInstanceUID = true;
tdcmOpts.isCreateBIDS = createBIDS;
tdcmOpts.isCreateBIDS = false;
tdcmOpts.isGz = false;
tdcmOpts.isForceStackSameSeries = ForceStackSameSeries; // merge 2D slice '-m y', tdcmOpts.isForceStackSameSeries = 1
tdcmOpts.isForceStackSameSeries = 1; // merge 2D slice '-m y', tdcmOpts.isForceStackSameSeries = 1
tdcmOpts.isForceStackDCE = false;
//tdcmOpts.isForceOnsetTimes = false;

if (dcm2niixopts != NULL)
__setDcm2niixOpts(dcm2niixopts);
}

/* set user dcm2niix conversion options in struct TDCMopts tdcmOpts
* only subset of dcm2niix command line options are recognized: (https://manpages.ubuntu.com/manpages/jammy/en/man1/dcm2niix.1.html)
* -b <y/i/n>
* Save additional BIDS metadata to a side-car .json file (default y).
* The "i"nput-only option reads DICOMs but saves neither BIDS nor NIfTI.
*
* -ba <y/n> anonymize BIDS (default y).
* If "n"o, side-car may report patient name, age and weight.
*
* -f <format>
* Format string for the output filename(s).
*
* -i <y/n>
* Ignore derived, localizer and 2D images (default n)
*
* -m <y/n/2>
* Merge slices from the same series regardless of study time, echo, coil, orientation, etc. (default 2).
* If "2", automatic based on image modality.
*
* -v <2/y/n>
* Enable verbose output. "n" for succinct, "y" for verbose, "2" for high verbosity
*
* -o <path>
* Output directory where the converted files should be saved.
*
* -t <y/n>
* Save patient details as text notes.
*
* -p <y/n>
* Use Philips precise float (rather than display) scaling.
*
* -x <y/n/i>
* Crop images. This will attempt to remove excess neck from 3D acquisitions.
* If "i", images are neither cropped nor rotated to canonical space.
*/
void dcm2niix_fswrapper::__setDcm2niixOpts(const char *dcm2niixopts)
{
//printf("[DEBUG] dcm2niix_fswrapper::__setDcm2niixOpts(%s)\n", dcm2niixopts);

char *restOpts = (char*)malloc(strlen(dcm2niixopts)+1);
memset(restOpts, 0, strlen(dcm2niixopts)+1);
memcpy(restOpts, dcm2niixopts, strlen(dcm2niixopts));

char *nextOpt = strtok_r((char*)dcm2niixopts, ",", &restOpts);
while (nextOpt != NULL)
{
char *k = nextOpt;
char *v = strchr(nextOpt, '=');
if (v != NULL)
*v = '\0';
v++; // move past '='

// skip leading white spaces
while (*k == ' ')
k++;

if (strcmp(k, "b") == 0)
{
if (*v == 'n' || *v == 'N' || *v == '0')
tdcmOpts.isCreateBIDS = false;
else if (*v == 'i' || *v == 'I')
{
tdcmOpts.isCreateBIDS = false;
tdcmOpts.isOnlyBIDS = true;
}
else if (*v == 'y' || *v == 'Y')
tdcmOpts.isCreateBIDS = true;
}
else if (strcmp(k, "ba") == 0)
tdcmOpts.isAnonymizeBIDS = (*v == 'n' || *v == 'N') ? false : true;
else if (strcmp(k, "f") == 0)
strcpy(tdcmOpts.filename, v);
else if (strcmp(k, "i") == 0)
tdcmOpts.isIgnoreDerivedAnd2D = (*v == 'y' || *v == 'Y') ? true : false;
else if (strcmp(k, "m") == 0)
{
if (*v == 'n' || *v == 'N' || *v == '0')
tdcmOpts.isForceStackSameSeries = 0;
else if (*v == 'y' || *v == 'Y' || *v == '1')
tdcmOpts.isForceStackSameSeries = 1;
else if (*v == '2')
tdcmOpts.isForceStackSameSeries = 2;
else if (*v == 'o' || *v == 'O')
tdcmOpts.isForceStackDCE = false;
}
else if (strcmp(k, "v") == 0)
{
if (*v == 'n' || *v == 'N' || *v == '0')
tdcmOpts.isVerbose = 0;
else if (*v == 'h' || *v == 'H' || *v == '2')
tdcmOpts.isVerbose = 2;
else
tdcmOpts.isVerbose = 1;
}
else if (strcmp(k, "o") == 0)
strcpy(tdcmOpts.outdir, v);
else if (strcmp(k, "t") == 0)
tdcmOpts.isCreateText = (*v == 'y' || *v == 'Y') ? true : false;
else if (strcmp(k, "p") == 0)
{
if (*v == 'n' || *v == 'N' || *v == '0')
tdcmOpts.isPhilipsFloatNotDisplayScaling = false;
}
else if (strcmp(k, "x") ==0)
{
if (*v == 'y' || *v == 'Y')
tdcmOpts.isCrop = true;
else if (*v == 'i' || *v == 'I')
{
tdcmOpts.isRotate3DAcq = false;
tdcmOpts.isCrop = false;
}
}
else
{
printf("[WARN] dcm2niix option %s=%s skipped\n", k, v);
}

nextOpt = strtok_r(NULL, ",", &restOpts);
}
}

// interface to isDICOMfile() in nii_dicom.cpp
Expand Down Expand Up @@ -157,10 +281,10 @@ void dcm2niix_fswrapper::dicomDump(const char* dicomdir, const char *series_info
fclose(fp_dcmLst);

// output series_info
fprintf(fpout, "%ld %s %f %f %f %f\\%f %c %f %s %s",
fprintf(fpout, "%ld %s %f %f %f %f\\%f %c %f %s %s %s",
tdicomData->seriesNum, tdicomData->seriesDescription,
tdicomData->TE, tdicomData->TR, tdicomData->flipAngle, tdicomData->xyzMM[1], tdicomData->xyzMM[2],
tdicomData->phaseEncodingRC, tdicomData->pixelBandwidth, (*mrifsStruct_vector)[n].dicomfile, tdicomData->imageType);
tdicomData->phaseEncodingRC, tdicomData->pixelBandwidth, (*mrifsStruct_vector)[n].dicomfile, tdicomData->imageType, (*mrifsStruct_vector)[n].pulseSequenceDetails);
#if 0
if (max)
{
Expand Down
5 changes: 4 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=NULL, bool createBIDS=false, int ForceStackSameSeries=1);
static void setOpts(const char* dcmindir, const char* dcm2niixopts=NULL);

// interface to isDICOMfile() in nii_dicom.cpp
static bool isDICOM(const char* file);
Expand Down Expand Up @@ -47,6 +47,9 @@ class dcm2niix_fswrapper

private:
static struct TDCMopts tdcmOpts;

//
static void __setDcm2niixOpts(const char *dcm2niixopts);
};

#endif
21 changes: 20 additions & 1 deletion console/nii_dicom_batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8342,6 +8342,19 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata dcmLi
mrifsStruct.dicomlst = new char*[nConvert];
mrifsStruct.nDcm = nConvert;

// retrieve pulseSequenceDetails (tSequenceFileName)
struct TDICOMdata *d = &(mrifsStruct.tdicomData);
strcpy(mrifsStruct.pulseSequenceDetails, "");
if ((d->manufacturer == kMANUFACTURER_SIEMENS) && (d->CSA.SeriesHeader_offset > 0) && (d->CSA.SeriesHeader_length > 0)) {
float shimSetting[8];
char protocolName[kDICOMStrLarge], fmriExternalInfo[kDICOMStrLarge], coilID[kDICOMStrLarge], consistencyInfo[kDICOMStrLarge], coilElements[kDICOMStrLarge], pulseSequenceDetails[kDICOMStrLarge], wipMemBlock[kDICOMStrLarge];
TCsaAscii csaAscii;
siemensCsaAscii(nameList->str[indx0], &csaAscii, d->CSA.SeriesHeader_offset, d->CSA.SeriesHeader_length, shimSetting, coilID, consistencyInfo, coilElements, pulseSequenceDetails, fmriExternalInfo, protocolName, wipMemBlock);
if (strlen(pulseSequenceDetails) >= kDICOMStr)
pulseSequenceDetails[kDICOMStr - 1] = 0;
strcpy(mrifsStruct.pulseSequenceDetails, pulseSequenceDetails);
}

dcmListDump(nConvert, dcmSort, dcmList, nameList, opts);

mrifsStruct_vector.push_back(mrifsStruct);
Expand Down Expand Up @@ -10008,10 +10021,16 @@ void dcmListDump(int nConvert, struct TDCMsort dcmSort[], struct TDICOMdata dcmL
memset(mrifsStruct.dicomlst[i], 0, strlen(nameList->str[indx])+1);
memcpy(mrifsStruct.dicomlst[i], nameList->str[indx], strlen(nameList->str[indx]));

printMessage("%s %ld %s %s %f %f %f %f\\%f %c %f %s %s\n",
FILE *fp = stdout;
const char *imagelist = getenv("MGH_DCMUNPACK_IMAGELIST");
if (imagelist != NULL)
fp = fopen(imagelist, "a");

fprintf(fp, "%s %ld %s %s %f %f %f %f\\%f %c %f %s %s\n",
dcmList[indx].patientName, dcmList[indx].seriesNum, dcmList[indx].studyDate, dcmList[indx].studyTime,
dcmList[indx].TE, dcmList[indx].TR, dcmList[indx].flipAngle, dcmList[indx].xyzMM[1], dcmList[indx].xyzMM[2],
dcmList[indx].phaseEncodingRC, dcmList[indx].pixelBandwidth, nameList->str[indx], dcmList[indx].imageType);
fclose(fp);
}
}
#endif
1 change: 1 addition & 0 deletions console/nii_dicom_batch.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct MRIFSSTRUCT
size_t imgsz;
unsigned char *imgM;

char pulseSequenceDetails[kDICOMStr];
struct TDICOMdata tdicomData;
char namePostFixes[256];
char *dicomfile;
Expand Down

0 comments on commit 79bcd4c

Please sign in to comment.