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 26 commits
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
70 changes: 70 additions & 0 deletions .github/workflows/docker-build-and-run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,73 @@ jobs:
- name: Cleanup Docker
run: |
docker system prune -a --force
build-dicom2niix-and-run-docker:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'

- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Generate input files
run: |
python -m Docker.generate_signal_docker_test --dicom

- 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.

for file in ivim_simulation_0000.dcm, ivim_simulation_0001.dcm, ivim_simulation_0002.dcm ivim_simulation_0003.dcm ivim_simulation_0004.dcm; do
if [ ! -f "$IVIM_SIM_DIR/$file" ]; then
echo "Error: $IVIM_SIM_DIR/$file not found"
exit 1
fi
done
echo "All input files generated successfully"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image
run: |
docker build -t tf2.4_ivim-mri_codecollection -f Docker/dicom2nifti/Dockerfile .

- name: Run Docker container
run: |
docker run --rm --name TF2.4_IVIM-MRI_CodeCollection \
-v ${{ github.workspace }}:/usr/src/app \
tf2.4_ivim-mri_codecollection \
/usr/src/app/ivim_simulation

- name: Verify output files
run: |
for file in f.nii.gz dp.nii.gz d.nii.gz; do
if [ ! -f "$file" ]; then
echo "Error: $file not found"
exit 1
fi
done
echo "All output files generated successfully"

- name: Clean up artifacts and Docker image
run: |
docker rmi tf2.4_ivim-mri_codecollection || true
rm -f tf2.4_ivim-mri_codecollection.tar.gz
rm -f ${{ github.workspace }}/f.nii.gz
rm -f ${{ github.workspace }}/dp.nii.gz
rm -f ${{ github.workspace }}/d.nii.gz
rm -f ${{ github.workspace }}/ivim_simulation.nii.gz
rm -f ${{ github.workspace }}/ivim_simulation.bval
rm -f ${{ github.workspace }}/ivim_simulation.bvec
rm -r -f ${{ github.workspace }}/ivim_simulation || true
- name: Cleanup Docker
run: |
docker system prune -a --force

2 changes: 1 addition & 1 deletion Docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ RUN pip install --no-cache-dir -r requirements.txt

COPY .. .

ENTRYPOINT ["python3", "-m", "WrapImage.nifti_wrapper"]
ENTRYPOINT ["python3", "-m", "WrapImage.nifti_wrapper"]
73 changes: 71 additions & 2 deletions Docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ This project is designed to run the `nifti_wrapper` script using a Docker contai

## Prerequisites

- Docker must be installed on your system.
- Docker must be installed on your system.

## Directory Structure

```
``` sh
~/TF2.4_IVIM-MRI_CodeCollection/
├── Docker/
│ └── Dockerfile
│ └── dicom2nifti/
│ └── Dockerfile
├── WrapImage/
│ └── nifti_wrapper.py
│ └── dicom2niix_wrapper.py
└── requirements.txt
```
Expand Down Expand Up @@ -45,6 +48,12 @@ Before running the Docker container, here are the available options for the `Doc
sudo docker build -t tf2.4_ivim-mri_codecollection -f Docker/Dockerfile .
```

OR (If you need to convert your data from DICOM TO NIfTI images)

```sh
sudo docker build -t tf2.4_ivim-mri_codecollection -f Docker/dicom2nifti/Dockerfile .
```

## Running the Docker Container

1. Once the image is built, you can run the Docker container using the `docker run` command. This command runs the Docker image with the specified input files:
Expand All @@ -58,4 +67,64 @@ 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.

## Running the Docker container for reading in DICOM Images

- You can run the dicom2nifti 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 \
tf2.4_ivim-mri_codecollection \
/usr/src/app/dicom_folder
```

- You can run the dicom2niix_wrapper.py script either directly or from inside a Docker container (non-interactive only). Here are the available options:

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

- `input`: Path to the input DICOM directory. Some scanners store images in nested subfolders, so a single session might be stored in the folders "/usr/subj22/111", "/usr/subj22/112" and "/usr/subj22/123". In this case you should simply provide the parent directly ("/usr/subj22") and dcm2niix will recursively search the sub directories and organize all your images for you.
- `output`: Path to the output directory for the converted NIfTI files and sidecar BIDS JSON.
- ` -m, --merge-2d`: Merge 2D slices into a 3D or 4D NIfTI image regardless of study time, echo, coil, orientation, etc. Depending on your vendor, you may want to keep images segmented based on these attributes or merge/combine them.
- ` -s, --single-file`: Use single file mode (convert only one series per folder). For example, if the input path "~/dir/001.dcm" will only convert the file 001.dcm.
- ` -pu, --prompt-user`: Run the tool in interactive mode. This launches a terminal-based wizard where you can select DICOM folders and configure conversion interactively.



### Example usage

```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 \
/usr/src/app/dicom_folder -o /usr/app/output
```

```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 \
/usr/src/app/dicom_folder -o /usr/app/output -m
```

```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 \
/usr/src/app/dicom_file -o /usr/app/output -s
```

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

![image](https://www.nitrc.org/plugins/mwiki/images/thumb/8/8e/Dcm2nii%3AMni_v_dicom.jpg/300px-Dcm2nii%3AMni_v_dicom.jpg)

##### 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)

- NIfTI follows the Talairach/MNI coordinate system where the X value increases as we move toward the participant's right, the Y increases as we move anteriorly. In contrast, for bipeds DICOM specifies the the X increases as we more toward the participant's left, while the Y increases as we move posteriorly. Both agree that the Z coordinate increases as we move superiorly.

---
40 changes: 40 additions & 0 deletions Docker/dicom2nifti/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
FROM debian:stable-slim AS build

ARG DCM2NIIX_VERSION=v1.0.20241211

RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
cmake \
git \
libssl-dev \
wget \
pigz \
ca-certificates \
&& update-ca-certificates \
&& wget https://github.com/rordenlab/dcm2niix/archive/refs/tags/${DCM2NIIX_VERSION}.tar.gz -O /tmp/dcm2niix.tar.gz \
&& mkdir -p /tmp/dcm2niix && tar -xzf /tmp/dcm2niix.tar.gz -C /tmp/dcm2niix --strip-components=1 \
&& mkdir /tmp/dcm2niix/build && cd /tmp/dcm2niix/build \
&& cmake -DBATCH_VERSION=ON -DZLIB_IMPLEMENTATION=Cloudflare -DUSE_JPEGLS=ON -DUSE_OPENJPEG=ON .. \
&& make && make install \
&& rm -rf /tmp/dcm2niix* \
&& apt-get remove -y wget git cmake \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

FROM python:3.11-slim

WORKDIR /usr/src/app

RUN apt-get update && apt-get install -y --no-install-recommends \
pigz \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

COPY --from=build /usr/local/bin/dcm2niix /usr/local/bin/dcm2niix

COPY ../.. .

RUN pip install --no-cache-dir .

ENTRYPOINT ["python3", "-m", "WrapImage.dicom2niix_wrapper"]
71 changes: 70 additions & 1 deletion Docker/generate_signal_docker_test.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import datetime
import os
from pathlib import Path
import sys
import uuid
import numpy as np
import nibabel as nib
import itk
from utilities.data_simulation.GenerateData import GenerateData
from WrapImage.nifti_wrapper import save_nifti_file


def save_bval_bvec(filename, values):
if filename.endswith('.bval'):
# Convert list to a space-separated string for bval
Expand All @@ -17,6 +22,8 @@ def save_bval_bvec(filename, values):
with open(filename, 'w') as file:
file.write(values_string)



# Set random seed for reproducibility
np.random.seed(42)
# Create GenerateData instance
Expand All @@ -40,3 +47,65 @@ def save_bval_bvec(filename, values):
save_bval_bvec("ivim_simulation.bval", [0, 50, 100, 500, 1000])
# Save the bvec value
save_bval_bvec("ivim_simulation.bvec", [[1, 0, 0], [0, 1, 0], [0, 0, 1]])


def save_dicom_files():
os.makedirs("ivim_simulation", exist_ok=True)
InputImageType = itk.Image[itk.D, 3]
ReaderType = itk.ImageFileReader[InputImageType]
NiftiImageIOType = itk.NiftiImageIO.New()


reader = ReaderType.New()
reader.SetImageIO(NiftiImageIOType)
reader.SetFileName("ivim_simulation.nii.gz")

try:
reader.Update()
except Exception as e:
print(f"Error occured while reading NIfTI in ivim_simulation: {e}")
sys.exit(1)

OutputPixelType = itk.SS
# The casting filter output image type will be 3D with the new pixel type
CastedImageType = itk.Image[OutputPixelType, 3]
CastFilterType = itk.CastImageFilter[InputImageType, CastedImageType]
caster = CastFilterType.New()
caster.SetInput(reader.GetOutput())
caster.Update()


OutputImageType = itk.Image[OutputPixelType, 2]
FileWriterType = itk.ImageSeriesWriter[CastedImageType, OutputImageType]
GDCMImageIOType = itk.GDCMImageIO.New()
writer = FileWriterType.New()
size = reader.GetOutput().GetLargestPossibleRegion().GetSize()
fnames = itk.NumericSeriesFileNames.New()
num_slices = size[2]

fnames.SetStartIndex(0)
fnames.SetEndIndex(num_slices - 1) # Iterate over the Z dimension (slices)
fnames.SetIncrementIndex(1)
fnames.SetSeriesFormat(os.path.join("ivim_simulation", f"ivim_simulation_%04d.dcm"))

# meta_dict = itk.MetaDataDictionary()
# include correct headers here to be tuned for Vendor
# GDCMImageIOType.SetMetaDataDictionary(meta_dict)
# GDCMImageIOType.KeepOriginalUIDOn()
writer.SetInput(caster.GetOutput())
writer.SetImageIO(GDCMImageIOType)
writer.SetFileNames(fnames.GetFileNames())
try:
writer.Write()
except Exception as e:
print(f"Error occurred while writing DICOMs in ivim simulation: {e}")
sys.exit(1)

args = sys.argv[1:]
if "--dicom" in args:
# read the generated nii file to dicom files
save_dicom_files()
# Save the bval in a file
save_bval_bvec(os.path.join("ivim_simulation","ivim_simulation.bval"), [0, 50, 100, 500, 1000])
# Save the bvec value
save_bval_bvec(os.path.join("ivim_simulation","ivim_simulation.bvec"), [[1, 0, 0], [0, 1, 0], [0, 0, 1]])
Empty file added WrapImage/__init__.py
Empty file.
Loading
Loading