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
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4bc40f2
Added the feature to read DICOM images into a 4D Nifti image utilizin…
jph6366 Apr 20, 2025
1e060cd
Update README.md
jph6366 Apr 21, 2025
a4ce220
[Feature] <Allow reading of DICOM images> #68
jph6366 Apr 21, 2025
e331dfc
Merge branch 'dicom2niix' of https://github.com/jph6366/TF2.4_IVIM-MR…
jph6366 Apr 21, 2025
1247c2c
Update README.md
jph6366 Apr 21, 2025
a5f63e6
[Feature] <Allow reading of DICOM images> #68
jph6366 Apr 21, 2025
0e496b6
[Feature] <Allow reading of DICOM images> #68
jph6366 Apr 22, 2025
ae56831
Update README.md
jph6366 Apr 22, 2025
317d72d
[Feature] <Allow reading of DICOM images> #68
jph6366 Apr 22, 2025
783e2f3
Merge branch 'dicom2niix' of https://github.com/jph6366/TF2.4_IVIM-MR…
jph6366 Apr 22, 2025
5347a31
[Feature] <Allow reading of DICOM images> #68
jph6366 Apr 22, 2025
22df73a
[Feature] <Allow reading of DICOM images> #68
jph6366 Apr 22, 2025
d18c2e6
Update README.md
jph6366 Apr 23, 2025
3dbb64c
[Feature] <Allow reading of DICOM images> #68
jph6366 Apr 25, 2025
606b667
Merge branch 'dicom2niix' of https://github.com/jph6366/TF2.4_IVIM-MR…
jph6366 Apr 25, 2025
9a17fb1
Update README.md
jph6366 Apr 25, 2025
614c8f8
[Feature] <Allow reading of DICOM images> #68
jph6366 Apr 28, 2025
dda0e88
Merge branch 'dicom2niix' of https://github.com/jph6366/TF2.4_IVIM-MR…
jph6366 Apr 28, 2025
ac752cb
Update pyproject.toml
jph6366 Apr 28, 2025
92abf87
Update generate_signal_docker_test.py
jph6366 Apr 28, 2025
201f495
[Feature] <Allow reading of DICOM images> #68
jph6366 Apr 30, 2025
d32460b
Merge branch 'dicom2niix' of https://github.com/jph6366/TF2.4_IVIM-MR…
jph6366 Apr 30, 2025
9cd090f
Update pyproject.toml
jph6366 Apr 30, 2025
30a0603
Update README.md
jph6366 May 9, 2025
64dc1bc
updated job to be fixed or otherwise log error to help fix
jph6366 May 9, 2025
682d655
Merge branch 'dicom2niix' of https://github.com/jph6366/TF2.4_IVIM-MR…
jph6366 May 9, 2025
814cdc9
#68
jph6366 May 24, 2025
015f7dc
tested locally successfully
jph6366 May 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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)

&& tar -xzf v1.0.20241211.tar.gz -C /usr/local/bin \
&& rm v1.0.20241211.tar.gz \
&& apt-get remove -y wget \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

Expand Down
21 changes: 20 additions & 1 deletion Docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This project is designed to run the `nifti_wrapper` script using a Docker contai

Before running the Docker container, here are the available options for the `Docker image` script:

- `input_file`: Path to the input 4D NIfTI file.
- `input_file`: Path to the input 4D NIfTI file or dicom files 4D images like fMRI and DTI/DKI are supported.
- `bvec_file`: Path to the b-vector file.
- `bval_file`: Path to the b-value file.
- `--affine`: Affine matrix for NIfTI image (optional).
Expand Down Expand Up @@ -58,4 +58,23 @@ Before running the Docker container, here are the available options for the `Doc

Replace `brain.nii.gz`, `brain.bvec`, and `brain.bval` with the actual file names you want to use.

### Reading in DICOM images

1. you can run the same Docker container using the `docker run` command. This command runs the Docker image with the specified input files:

```sh
sudo docker run -it --rm --name TF2.4_IVIM-MRI_CodeCollection \
-v ~/TF2.4_IVIM-MRI_CodeCollection:/usr/src/app \
-v ~/TF2.4_IVIM-MRI_CodeCollection:/usr/app/output \
tf2.4_ivim-mri_codecollection Downloads/dicom_folder
```

Replace `dicom_folder` with the actual directory (containing DICOM Images) you want to use.

[Note that NIfTI and DICOM encode space differently](https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Spatial_Coordinates)


##### The goal of dcm2niix is to create FSL format bvec/bval files for processing. A crucial concern is ensuring that the gradient directions are reported in the frame of reference expected by the software you use to fit your tractography. [dicom2niix should generate a ".bvec" file that reports the tensors as expected](https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging) by FSL's dtifit, where vectors are reported relative to image frame of reference (rather than relative to the scanner bore).

#### It is strongly recommend that users check validate the b-vector directions for their hardware and sequence as [described in a dedicated document](https://www.nitrc.org/docman/?group_id=880)
---
65 changes: 59 additions & 6 deletions WrapImage/nifti_wrapper.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,51 @@
import argparse
import json
import os
from pathlib import Path
import subprocess
import nibabel as nib
from src.wrappers.OsipiBase import OsipiBase
import numpy as np
from tqdm import tqdm

def dicom_to_niix(vol_dir: Path):
"""
For converting DICOM images to a (compresssed) 4d nifti image
"""

try:
res = subprocess.run(
[
"dcm2niix",
"-f", "%s_%p", # dcm2niix attempts to provide a sensible file naming scheme
"-o", vol_dir,
"-z", "y", #specifying compressed nii.gz file
# https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage
# for further configuration for general usage see page above
vol_dir
],
capture_output=True,
text=True,
check=True
)

nifti_files = list(Path(vol_dir).glob("*.nii.gz"))
if not nifti_files:
raise RuntimeError("No NIfTI file generated by dcm2niix.")
if len(nifti_files) > 1:
raise RuntimeError("Multiple NIfTI files generated; expected one 4D NIfTI.")

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



return vol_dir / "dicom2nifti.nii.gz", bval_path, bvec_path

except subprocess.CalledProcessError as e:
raise RuntimeError(f"dcm2niix failed: {e.stderr}")


def read_nifti_file(input_file):
"""
Expand Down Expand Up @@ -81,22 +121,35 @@ def loop_over_first_n_minus_1_dimensions(arr):

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Read a 4D NIfTI phantom file along with BIDS JSON, b-vector, and b-value files.")
parser.add_argument("input_file", type=str, help="Path to the input 4D NIfTI file.")
parser.add_argument("bvec_file", type=str, help="Path to the b-vector file.")
parser.add_argument("bval_file", type=str, help="Path to the b-value file.")
parser.add_argument("input_file", type=Path, help="Path to the input 4D NIfTI file or DICOM files 4D images like fMRI and DTI/DKI are supported..")
parser.add_argument("bvec_file", type=Path, help="Path to the b-vector file.")
parser.add_argument("bval_file", type=Path, help="Path to the b-value file.")
parser.add_argument("--affine", type=float, nargs="+", help="Affine matrix for NIfTI image.")
parser.add_argument("--algorithm", type=str, default="OJ_GU_seg", help="Select the algorithm to use.")
parser.add_argument("--algorithm_args", nargs=argparse.REMAINDER, help="Additional arguments for the algorithm.")

args = parser.parse_args()

try:
if args.input_file.is_dir():
# Convert DICOM to NIfTI using dicom2niix
nifti_file, bval_path, bvec_path = dicom_to_niix(args.input_file) # read dir
if bval_path is None or bvec_path is None:
raise RuntimeError("dcm2niix did not generate bval/bvec files.")
bval_file = Path(bval_path)
bvec_file = Path(bvec_path)
else:
# Assume input is already a NIfTI file
nifti_file = args.input_file
bval_file = args.bval_file
bvec_file = args.bvec_file

# Read the 4D NIfTI file
data, _ = read_nifti_file(args.input_file)
data, _ = read_nifti_file(nifti_file)

# Read the b-vector, and b-value files
bvecs = read_bvec_file(args.bvec_file)
bvals = read_bval_file(args.bval_file)
bvecs = read_bvec_file(bvec_file)
bvals = read_bval_file(bval_file)

# Pass additional arguments to the algorithm

Expand Down