Skip to content

Added the feature to read DICOM images into a 4D Nifti image #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 28 commits into
base: main
Choose a base branch
from

Conversation

jph6366
Copy link

@jph6366 jph6366 commented Apr 20, 2025

Allow reading of DICOM images

Spent a few days getting acclimated to DICOM concepts (shoutout DicomIsEasy) and investigated the best options for this feature request.

I initially wanted to use ITK like it is here, but it seems perhaps a configure-first philosophy might suit this codebase by introducing a new feature with less complexity added to future development. I opted to use dcm2niix, which converts diffusion vectors andit is written in C, so it is very portable, and It attempts to preserve diffusion gradient information. dcm2nii is the predecessor to dcm2niix. It is deprecated for modern images, but does handle image formats that predate DICOM.

There are many good NeuroImaging Tools & Resource Collaboratory to choose from but dicom2niix is a reliable option, following a configure-first policy, we utilize the highly portable C code and simply expose the configurations relevant to the research and development of perfusion imaging. dicom2niix documentation is good and the repository is open source, also they list many alternatives and other tools that exploit dcm2niix.

EDIT:

Refactored code to access both dcm2niix and dcm2niibatch commands with access to some configurations without any need for local code modifications. Handles multiple input directories and will run nifti_wrapper cli if desired (only if user has selected to convert using dcm2niix [I did not add the functionality to call nifti_wrapper on every output from batch call] ).

There is a now a separate dockerfile because if a user has no DICOM data then we do not want to install the dicom2niix packages. The README has been updated to instruct users on how to use the different builds.

Link this PR to an issue [optional]

Fixes #68

Checklist

  • Test dicom2niix_wrapper.py CLI
  • Update Dockerfile to install dicom2niix latest release
  • Update README

Oversights

  • Grassroots DICOM, written in C++, has higher performance executables
    • 3D Slicer uses a GDCM port, presumably so does ITK and SimpleITK, I attempted to utilize python-gdcm, an unofficial GDCM package for Python owned by InVesalius developer.
  • Reading in DICOMDIR records
  • Maintaining latest updates in future developments
  • Running via SSH

Happy Testing

UnHappy Testing

  • vigorously tested using flags and seeing what fails based on missing images or mismatched images
  • providing invalid inputs for CLI runs
  • building dockerfiles

Simulating IVIM signal as DICOM data

  • ITK bridges with numpy and ITK::image is robust, see documentation here and actively maintained.
  • We crudely configure the header to read for valid conversion
  • Adding the diffusion tags are different for each Vendor
    • we simply just configure the basic tags for encoding gradient directionality and diffusion b-values; although this should be refactored with more direction of what will provide value in the future to the maintainers/end-users/researchers.

For further improvments:

  • We should utilize nibabel for its dicom reader, nicom, and its Wrapper classes for different vendor-specific format data.
  • We can also compile for the Web using JS+WebAssembly library @niivue/dcm2niix

…g dicom2niix

updated Dockerfile, README, and nifti_wrapper.py
Copy link
Contributor

@etpeterson etpeterson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start! I have a few comments in specific places.

bval_files = list(vol_dir.glob("*.bval"))
bvec_files = list(vol_dir.glob("*.bvec"))
bval_path = str(bval_files[0]) if bval_files else None
bvec_path = str(bvec_files[0]) if bvec_files else None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all making the assumption that we only want one. What if they're different, which do we choose then? I'm not exactly sure what dcm2niix outputs, but probably best is to require just one output and error if there are more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For handling batch conversion for many files we could leverage the yaml config for batch processing, as specfied in the dicom2niix repo, so we can provide that option and updated instructions in the README.

Copy link
Author

@jph6366 jph6366 Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not exactly sure what dcm2niix outputs, but probably best is to require just one output and error if there are more.

dicom2niix handles it nicely really by just adding characters in an alphabetical order

e.g.

len(nifti_files) = 0 *.bval, *.bvec *.nii.gz

len(nifti_files) = 1 *a.bval, *a.bvec *a.nii.gz

len(nifti_files) = 2 *b.bval, *b.bvec *b.nii.gz

len(nifti_files) = 3 *c.bval, *c.bvec *c.nii.gz

@@ -5,6 +5,13 @@ WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libssl-dev \
wget \
pigz \
&& wget https://github.com/rordenlab/dcm2niix/archive/refs/tags/v1.0.20241211.tar.gz \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a separate build stage in the dockerfile. That simplifies the cleanup.

I'm also wondering about the version. Is there a way we can select the latest stable?

Copy link
Author

@jph6366 jph6366 Apr 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems the easiest way to do it is just pin the latest version.

We could query the GitHub API but then our docker environment will get rate limited without a personal access token for authorization. Ideally, we dont want to increase our dependency and just lean towards manual updates based on alerting users to make an issue to update the dockerfile.

there is an update check option to add to our dicom2niix subprocess call, this command will cause the software report if there are any newer versions of dcm2niix available online. (UNIX ONLY)

jph6366 and others added 5 commits April 21, 2025 10:39
Refactored previous commits to decouple dicom2niix build from the base Dockerfile for the testing the main code collection.
- we now have a separate dockerfile, one for nifti files and one for converting dicom to nifti
- we now also handle multiple inputs seamless and the researcher/end-user has access more configuration
-  we made a separate file to inquire user input and then just inject the nifti wrapper through a subprocess call like we do with our dcm2niix and dcm2niibatch
@jph6366 jph6366 requested a review from etpeterson April 21, 2025 17:17
Copy link
Contributor

@etpeterson etpeterson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for keeping at this, it's converging. Several more comments though.

jph6366 and others added 9 commits April 22, 2025 09:18
Updating Dockerfile and refactored inquire to function more like a wrapper to the cli and writing a one-call script for easy streamlined use, but also preserving the interactives for debugging

Removed dcm2niibatch as its easier to focus on one command that is better maintained.

Also updated the readmee
updates to the CLI and conversion error handling
Fixed up readme and enabled the output to buffered line by line for our subprocess output
Fixed the --series_number validation
Squashed some bugs and tried some more use case testing. Wrote up a function to save simulated DWI as DICOM images with headers configured for all in x-direction.

provided pyproject.toml for separation of dependenccies and setuptools configured for the different root packages.
@jph6366 jph6366 requested a review from etpeterson April 25, 2025 17:00
jph6366 added 2 commits April 28, 2025 08:31
Rewrote the save dicom  function still using ITK, but instead of reading the from the Array, we just convert the nifti file generated into dicom slices and reuse the bval and bvec files.

add github workflow test for dicom2niix
@jph6366 jph6366 requested a review from etpeterson April 28, 2025 12:34
@etpeterson
Copy link
Contributor

@etpeterson
Copy link
Contributor

Getting close. Just smaller comments now.

Hopefully fixed the github action job
And updated pyproject.toml comments
@jph6366 jph6366 requested a review from etpeterson April 30, 2025 12:07
@etpeterson
Copy link
Contributor

Sorry, looks like the test still fails.

Copy link
Contributor

@etpeterson etpeterson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just because the test fails

@jph6366 jph6366 requested a review from etpeterson May 9, 2025 16:02
Copy link
Contributor

@etpeterson etpeterson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the long delay. I was at a conference. Still just that test failure.

@@ -98,9 +98,10 @@ jobs:

- name: Verify input files
run: |
IVIM_SIM_DIR = "${{ github.workspace }}/ivim_simulation"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test fails. I think because of the whitespace here.

removed whitespace so expression is read instead of command.
Copy link
Contributor

@etpeterson etpeterson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are passing! Just took another look across the code and came up with one final question.

description = "IVIM MRI signal processing and NIfTI/DICOM processing tools"
requires-python = ">=3.9"
readme = "README.md"
dependencies = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm noticing these dependencies don't completely match with those in requirements.txt. Is that intentional?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants