Skip to content

Commit

Permalink
Deidentification refactor (#877)
Browse files Browse the repository at this point in the history
  • Loading branch information
neurolabusc committed Oct 22, 2024
1 parent b48c597 commit 1657730
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 39 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ make
- [dinifti](https://as.nyu.edu/cbi/resources/Software/DINIfTI.html) is focused on conversion of classic Siemens DICOMs.
- [DWIConvert](https://github.com/BRAINSia/BRAINSTools/tree/master/DWIConvert) converts DICOM images to NRRD and NIfTI formats.
- [mcverter](http://lcni.uoregon.edu/%7Ejolinda/MRIConvert/) a great tool for classic DICOMs.
- [mri_convert](https://surfer.nmr.mgh.harvard.edu/pub/docs/html/mri_convert.help.xml.html) is part of the popular FreeSurfer package. In my limited experience this tool works well for GE and Siemens data, but fails with Philips 4D datasets.
- [mri_convert](https://surfer.nmr.mgh.harvard.edu/pub/docs/html/mri_convert.help.xml.html) is part of the popular FreeSurfer package.
- [MRtrix mrconvert](http://mrtrix.readthedocs.io/en/latest/reference/commands/mrconvert.html) is a useful general purpose image converter and handles DTI data well. It is an outstanding tool for modern Philips enhanced images.
- [nanconvert](https://github.com/spinicist/nanconvert) uses the ITK library to convert DICOM from GE and proprietary Bruker to standard formats like DICOM.
- [Plastimatch](https://plastimatch.org/) is a Swiss Army knife - it computes registration, image processing,
Expand All @@ -125,10 +125,13 @@ make

## Links

dcm2niix is a core dependency of BIDS converters:

- [Table of DICOM to BIDS converters](https://bids.neuroimaging.io/benefits#mri-and-pet-converterss)

The following tools exploit dcm2niix

- [@niivue/dcm2niix](https://www.npmjs.com/package/@niivue/dcm2niix) is a WebAssembly (WASM) package of dcm2niix, allowing it to be embedded into web pages, as seen in this [live demo](https://niivue.github.io/niivue-dcm2niix/).
- [abcd-dicom2bids](https://github.com/DCAN-Labs/abcd-dicom2bids) selectively downloads high quality ABCD datasets.
- [autobids](https://github.com/khanlab/autobids) automates dcm2bids which uses dcm2niix.
- [BiDirect_BIDS_Converter](https://github.com/wulms/BiDirect_BIDS_Converter) for conversion from DICOM to the BIDS standard.
Expand Down Expand Up @@ -182,6 +185,8 @@ The following tools exploit dcm2niix
- [kipettools](https://github.com/mathesong/kipettools) uses dcm2niix to load PET data.
- [LEAD-DBS](http://www.lead-dbs.org/) uses dcm2niix for [DICOM import](https://github.com/leaddbs/leaddbs/blob/master/ea_dicom_import.m).
- [lin4neuro](http://www.lin4neuro.net/lin4neuro/18.04bionic/vm/) releases such as the English l4n-18.04.4-amd64-20200801-en.ova include MRIcroGL and dcm2niix pre-installed. This allows user with VirtualBox or VMWarePlayer to use these tools (and many other neuroimaging tools) in a graphical virtual machine.
- [mercure-dcm2bids](https://github.com/mercure-imaging/mercure-dcm2bids) is a Mercure module to perform DICOM to BIDS conversion using dcm2bids and dcm2niix.
- [mri_convert](https://surfer.nmr.mgh.harvard.edu/pub/docs/html/mri_convert.help.xml.html) is part of the popular FreeSurfer package and wraps dcm2niix to improve DICOM support.
- [MRIcroGL](https://github.com/neurolabusc/MRIcroGL) is available for MacOS, Linux and Windows and provides a graphical interface for dcm2niix. You can get compiled copies from the [MRIcroGL NITRC web site](https://www.nitrc.org/projects/mricrogl/).
- [MrPyConvert](https://github.com/Jolinda/mrpyconvert) Python library dicom to bids conversion.
- [Nekton](https://github.com/deepc-health/nekton) is a python package for DICOM to NifTi and NifTi to DICOM-SEG and GSPS conversion.
Expand All @@ -206,7 +211,7 @@ The following tools exploit dcm2niix
- [Retina_OCT_dcm2nii](https://github.com/Choupan/Retina_OCT_dcm2nii) converts optical coherence tomography (OCT) data to NIfTI.
- [sci-tran dcm2niix](https://github.com/scitran-apps/dcm2niix) Flywheel Gear (docker).
- [shimming-toolbox](https://github.com/shimming-toolbox/shimming-toolbox) enabled static and real-time shimming, using dcm2niix to import DICOM data.
- [SlicerDcm2nii extension](https://github.com/Slicer/ExtensionsIndex/blob/master/SlicerDcm2nii.s4ext) is one method to import DICOM data into Slicer.
- [SlicerDcm2nii](https://github.com/SlicerDMRI/SlicerDcm2nii) is an extension to import DICOM data into 3D Slicer.
- [tar2bids](https://github.com/khanlab/tar2bids) converts DICOM tarball(s) to BIDS using heudiconv which invokes dcm2niix.
- [TORTOISE](https://tortoise.nibib.nih.gov) is used for processing diffusion MRI data, and uses dcm2niix to import DICOM images.
- [TractoR (Tracto­graphy with R) uses dcm2niix for image conversion](http://www.tractor-mri.org.uk/TractoR-and-DICOM).
Expand Down
18 changes: 13 additions & 5 deletions console/nii_dicom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ struct TDICOMdata clear_dicom_data() {
for (int i = 0; i < 7; i++)
d.orient[i] = 0.0f;
strcpy(d.patientName, "");
strcpy(d.deidentificationMethod, "");
strcpy(d.patientID, "");
strcpy(d.accessionNumber, "");
strcpy(d.imageType, "");
Expand Down Expand Up @@ -4254,14 +4255,21 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D
dti4D->frameReferenceTime[0] = -1;
// dti4D->fragmentOffset[0] = -1;
dti4D->intenScale[0] = 0.0;
#ifdef USING_R
// Ensure dti4D fields are initialised, as in nii_readParRec()
for (int i = 0; i < kMaxDTI4D; i++) {
dti4D->S[i].V[0] = -1.0;
dti4D->TE[i] = -1.0;
}
#endif
d.deID_CS_n = 0;
for (int i = 0; i < MAX_DEID_CS; i++) {
// n.b. knowing deID_CS_n is insufficient to know number of strings
// e.g. dcm_qa_deident CodingSchemeVersion (0008,0103) provided for only some entries
strcpy(dti4D->deID_CS[i].CodeValue, "");
strcpy(dti4D->deID_CS[i].CodeMeaning, "");
strcpy(dti4D->deID_CS[i].CodingSchemeDesignator, "");
strcpy(dti4D->deID_CS[i].CodingSchemeVersion, "");
}

struct TVolumeDiffusion volDiffusion = initTVolumeDiffusion(&d, dti4D);
struct stat s;
if (stat(fname, &s) == 0) {
Expand Down Expand Up @@ -5670,9 +5678,9 @@ struct TDICOMdata readDICOMx(char *fname, struct TDCMprefs *prefs, struct TDTI4D
break;
}
case kDeidentificationMethod: { // issue 383
dcmStr(lLength, &buffer[lPos], dti4D->deidentificationMethod);
int slen = (int)strlen(dti4D->deidentificationMethod);
if ((slen < 10) || (strstr(dti4D->deidentificationMethod, "DICOMANON") == NULL))
dcmStr(lLength, &buffer[lPos], d.deidentificationMethod);
int slen = (int)strlen(d.deidentificationMethod);
if ((slen < 10) || (strstr(d.deidentificationMethod, "DICOMANON") == NULL))
break;
isDICOMANON = true;
printWarning("Matlab DICOMANON can scramble SeriesInstanceUID (0020,000e) and remove crucial data (see issue 383). \n");
Expand Down
5 changes: 2 additions & 3 deletions console/nii_dicom.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ extern "C" {
#define kCPUsuf " " // unknown CPU
#endif

#define kDCMdate "v1.0.20241015"
#define kDCMdate "v1.0.20241022"
#define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf

static const int kMaxEPI3D = 1024; // maximum number of EPI images in Siemens Mosaic
Expand Down Expand Up @@ -207,7 +207,6 @@ struct TDTI4D {
bool isImaginary[kMaxDTI4D];
bool isPhase[kMaxDTI4D];
float repetitionTimeExcitation, repetitionTimeInversion;
char deidentificationMethod[kDICOMStr];
struct TDeIDCodeSequence deID_CS[MAX_DEID_CS];
};

Expand Down Expand Up @@ -260,7 +259,7 @@ struct TDICOMdata {
float pixelPaddingValue; // used for both FloatPixelPaddingValue (0028, 0122) and PixelPaddingValue (0028, 0120); NaN if not present.
double imagingFrequency, acquisitionDuration, triggerDelayTime, RWVScale, RWVIntercept, dateTime, acquisitionTime, acquisitionDate, bandwidthPerPixelPhaseEncode;
char parallelAcquisitionTechnique[kDICOMStr], radiopharmaceutical[kDICOMStr], convolutionKernel[kDICOMStr], unitsPT[kDICOMStr], tracerRadionuclide[kDICOMStr], decayCorrection[kDICOMStr], attenuationCorrectionMethod[kDICOMStr], reconstructionMethod[kDICOMStr], transferSyntax[kDICOMStr];
char prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], studyDescription[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr], sequenceVariant[kDICOMStr], scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr], studyTime[kDICOMStr];
char deidentificationMethod[kDICOMStr], prescanReuseString[kDICOMStr], imageOrientationText[kDICOMStr], pulseSequenceName[kDICOMStr], coilElements[kDICOMStr], coilName[kDICOMStr], phaseEncodingDirectionDisplayedUIH[kDICOMStr], imageBaseName[kDICOMStr], stationName[kDICOMStr], studyDescription[kDICOMStr], softwareVersions[kDICOMStr], deviceSerialNumber[kDICOMStr], institutionName[kDICOMStr], referringPhysicianName[kDICOMStr], instanceUID[kDICOMStr], seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageTypeText[kDICOMStr], imageType[kDICOMStr], institutionalDepartmentName[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr], accessionNumber[kDICOMStr], seriesDescription[kDICOMStr], studyID[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr], sequenceVariant[kDICOMStr], scanningSequence[kDICOMStr], patientBirthDate[kDICOMStr], patientAge[kDICOMStr], studyDate[kDICOMStr], studyTime[kDICOMStr];
char deepLearningText[kDICOMStrLarge], scanOptions[kDICOMStrLarge], institutionAddress[kDICOMStrLarge], imageComments[kDICOMStrLarge];
uint32_t dimensionIndexValues[MAX_NUMBER_OF_DIMENSIONS];
int deID_CS_n;
Expand Down
53 changes: 24 additions & 29 deletions console/nii_dicom_batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1464,49 +1464,44 @@ tse3d: T2*/
fprintf(fp, "\t\"NonlinearGradientCorrection\": false,\n");
if (d.isDerived) // DICOM is derived image or non-spatial file (sounds, etc)
fprintf(fp, "\t\"RawImage\": false,\n");
struct TDTI4D *d4D = (struct TDTI4D *)malloc(sizeof(struct TDTI4D));
struct TDICOMdata d2; // No need to preallocate with malloc()
char *fname = (char *)malloc(strlen(filename) + 1);
strcpy(fname, filename);
d2 = readDICOMv(fname, 0, 1, d4D);
if (strlen(d4D->deidentificationMethod) > 0) {
if (strlen(d.deidentificationMethod) > 0) {
fprintf(fp, "\t\"DeidentificationMethod\": [\"");
bool isSep = false;
for (size_t i = 0; i < strlen(d4D->deidentificationMethod); i++) {
if (d4D->deidentificationMethod[i] != '\\') {
for (size_t i = 0; i < strlen(d.deidentificationMethod); i++) {
if (d.deidentificationMethod[i] != '\\') {
if (isSep)
fprintf(fp, "\", \"");
isSep = false;
fprintf(fp, "%c", d4D->deidentificationMethod[i]);
fprintf(fp, "%c", d.deidentificationMethod[i]);
} else
isSep = true;
}
fprintf(fp, "\"],\n");
}
free(d4D);
free(fname);
if (d.deID_CS_n > 0) {
char *fname = (char *)malloc(strlen(filename) + 1);
strcpy(fname, filename);
//struct TDICOMdata *d2 = (struct TDICOMdata *)malloc(sizeof(struct TDICOMdata));
struct TDTI4D *d4D = (struct TDTI4D *)malloc(sizeof(struct TDTI4D));
struct TDICOMdata d2; // No need to preallocate with malloc()
d2 = readDICOMv(fname, 0, 1, d4D);
free(fname);
fprintf(fp, "\t\"DeidentificationMethodCodeSequence\": [ \n");
for (int i = 0; i < d.deID_CS_n && i < MAX_DEID_CS; i++) {
fprintf(fp, "\t { \n");
json_Str(fp, "\t\t\"CodeValue\": \"%s\",\n", d4D->deID_CS[i].CodeValue);
json_Str(fp, "\t\t\"CodingSchemeDesignator\": \"%s\",\n", d4D->deID_CS[i].CodingSchemeDesignator);
json_Str(fp, "\t\t\"CodingSchemeVersion\": \"%s\",\n", d4D->deID_CS[i].CodingSchemeVersion);
json_Str(fp, "\t\t\"CodeMeaning\": \"%s\"\n", d4D->deID_CS[i].CodeMeaning);
if (i + 1 < d.deID_CS_n)
fprintf(fp, "\t },\n");
else
fprintf(fp, "\t }\n");
if (is_fileexists(fname)) {
struct TDTI4D *d4D = (struct TDTI4D *)malloc(sizeof(struct TDTI4D));
struct TDICOMdata d2 = readDICOMv(fname, 0, 1, d4D);
fprintf(fp, "\t\"DeidentificationMethodCodeSequence\": [ \n");
for (int i = 0; i < d.deID_CS_n && i < MAX_DEID_CS; i++) {
fprintf(fp, "\t { \n");
json_Str(fp, "\t\t\"CodeValue\": \"%s\",\n", d4D->deID_CS[i].CodeValue);
json_Str(fp, "\t\t\"CodingSchemeDesignator\": \"%s\",\n", d4D->deID_CS[i].CodingSchemeDesignator);
json_Str(fp, "\t\t\"CodingSchemeVersion\": \"%s\",\n", d4D->deID_CS[i].CodingSchemeVersion);
json_Str(fp, "\t\t\"CodeMeaning\": \"%s\"\n", d4D->deID_CS[i].CodeMeaning);
if (i + 1 < d.deID_CS_n)
fprintf(fp, "\t },\n");
else
fprintf(fp, "\t }\n");
}
fprintf(fp, "\t],\n");
free(d4D);
} else {
printWarning("Issue877 unable to find file for DeidentificationMethod: %s\n", fname);
}
fprintf(fp, "\t],\n");
free(d4D);
free(fname);
} // d.deID_CS_n > 0
if (d.seriesNum > 0)
fprintf(fp, "\t\"SeriesNumber\": %ld,\n", d.seriesNum);
Expand Down

0 comments on commit 1657730

Please sign in to comment.