Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ test/integration/*.xprot
test/integration/*.xml
.atom-build.json
#.vscode/
test/nhlbi_integration_tests/test
test/nhlbi_integration_tests/baselines
*.seq
dep-build/
stats.csv
junit.xml
73 changes: 26 additions & 47 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ARG USERNAME="vscode"
ARG USER_UID=1000
ARG USER_GID=1000

FROM ubuntu:22.04 AS gadgetron_baseimage
FROM ubuntu:24.04 AS gadgetron_baseimage
LABEL org.opencontainers.image.source=https://github.com/gadgetron/gadgetron

ARG USERNAME
Expand All @@ -12,9 +12,13 @@ ARG USER_GID
ARG HOME=/home/$USERNAME

RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y sudo wget git-core git-lfs rsync curl net-tools libxml2 \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y sudo wget git-core git-lfs rsync curl net-tools libxml2 emacs-nox \
&& apt-get clean

# Remove default ubuntu user (Ubuntu 24.04 ships with UID/GID 1000 taken)
RUN userdel -r ubuntu 2>/dev/null || true \
&& groupdel ubuntu 2>/dev/null || true

# Create the user
RUN groupadd --gid $USER_GID $USERNAME \
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /bin/bash \
Expand All @@ -41,20 +45,7 @@ RUN wget --no-hsts --quiet https://github.com/conda-forge/miniforge/releases/dow
&& chmod -R g+w /opt/conda \
&& find /opt -type d | xargs -n 1 chmod g+s

#RUN wget --no-hsts --quiet https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh -O /tmp/miniforge.sh \
# && /bin/bash /tmp/miniforge.sh -b -p /opt/conda \
# && rm /tmp/miniforge.sh \
# && /opt/conda/bin/mamba clean --tarballs --index-cache --packages --yes \
# && find /opt/conda -follow -type f -name '*.a' -delete \
# && find /opt/conda -follow -type f -name '*.pyc' -delete \
# && /opt/conda/bin/mamba clean --force-pkgs-dirs --all --yes \
# && groupadd -r conda --gid ${CONDA_GID} \
# && usermod -aG conda ${USERNAME} \
# && chown -R :conda /opt/conda \
# && chmod -R g+w /opt/conda \
# && find /opt -type d | xargs -n 1 chmod g+s

# Copy environment, which will be filtered for later staged
# Copy environment
COPY --chown=$USER_UID:conda environment.yml /tmp/build/

# Create mount points for tests
Expand All @@ -76,17 +67,21 @@ ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini

USER root
RUN mkdir -p /opt/GIRF/
COPY --chown=$USER_UID:conda toolboxes/nhlbi_gt_toolbox/GIRF/ /opt/GIRF/
#COPY --chown=$USER_UID:conda toolboxes/nhlbi_gt_toolbox/models_dl/ /opt/models/

FROM gadgetron_baseimage AS gadgetron_dev_cuda
ARG USER_UID
ARG HOME
USER ${USER_UID}
RUN mkdir -p ${HOME}/.cache/conda/notices && sudo chown -R ${USER_UID}:conda ${HOME}/.cache/conda/notices

RUN grep -v "#.*\<NOFILTER\>" /tmp/build/environment.yml > /tmp/build/filtered_environment.yml
RUN umask 0002 && /opt/conda/bin/mamba env remove -n gadgetron || true && /opt/conda/bin/mamba env create -f /tmp/build/filtered_environment.yml && /opt/conda/bin/mamba clean -afy && sudo chown -R :conda /opt/conda
RUN umask 0002 && /opt/conda/bin/mamba env remove -n gadgetron || true \
&& /opt/conda/bin/mamba env create -f /tmp/build/environment.yml \
&& /opt/conda/bin/mamba clean -afy \
&& sudo chown -R :conda /opt/conda


RUN echo 'export PATH="/opt/conda/envs/gadgetron/nvvm/bin/:$PATH"' >> ${HOME}/.bashrc


Expand All @@ -98,8 +93,7 @@ ARG USER_UID
ARG HOME
USER ${USER_UID}
RUN mkdir -p ${HOME}/.cache/conda/notices && sudo chown -R ${USER_UID}:conda ${HOME}/.cache/conda/notices
RUN grep -v "#.*\<cuda\>" /tmp/build/environment.yml > /tmp/build/filtered_environment.yml
RUN umask 0002 && /opt/conda/bin/mamba env create -f /tmp/build/filtered_environment.yml && /opt/conda/bin/mamba clean -afy && sudo chown -R :conda /opt/conda
RUN umask 0002 && /opt/conda/bin/mamba env create -f /tmp/build/environment.yml && /opt/conda/bin/mamba clean -afy && sudo chown -R :conda /opt/conda
USER root

FROM gadgetron_dev_cuda AS gadgetron_cudabuild
Expand All @@ -108,28 +102,16 @@ USER ${USER_UID}
WORKDIR /opt
ARG USER_GID=1000

RUN echo "UID=$USER_UID GID=$USER_GID"

RUN sudo chown $USER_UID:$USER_GID /opt && mkdir -p /opt/code/gadgetron && mkdir -p /opt/package
RUN sudo chown $USER_UID:$USER_GID /opt && mkdir -p /opt/code/gadgetron && mkdir -p /opt/package
COPY --chown=$USER_UID:conda . /opt/code/gadgetron/


# Ensure NHLBI-GT-Non-Cartesian is inside the build context before building
#COPY --chown=$USER_UID:conda NHLBI-GT-Non-Cartesian/ /opt/code/NHLBI-GT-Non-Cartesian/


ENV CONDA_ENV_PATH=/opt/conda/envs/gadgetron
RUN echo "export NUMBA_CUDA_ENABLE_PYNVJITLINK=1" >> $CONDA_ENV_PATH/etc/conda/activate.d/env_vars.sh

#COPY --chown=$USER_UID:conda OpticalFlow3d/ /opt/code/OpticalFlow3d/
RUN . /opt/conda/etc/profile.d/conda.sh && umask 0002 && conda activate gadgetron && \
sh -x && \
pip install git+https://github.com/ahsanjav/OpticalFlow3d.git
# cd /opt/code/OpticalFlow3d/ && \
# pip install -e .
# # && \
# pip install numpy==1.23


SHELL ["/bin/bash", "-c"]
RUN . /opt/conda/etc/profile.d/conda.sh && umask 0002 && conda activate gadgetron && \
export PATH="/opt/conda/envs/gadgetron/nvvm/bin/:$PATH" && \
Expand Down Expand Up @@ -165,28 +147,25 @@ USER ${USER_UID}
ARG USER_GID=1000

RUN mkdir -p ${HOME}/.cache/conda/notices && sudo chown -R ${USER_UID}:conda ${HOME}/.cache/conda/notices
RUN grep -v "#.*\<dev\>" /tmp/build/environment.yml > /tmp/build/filtered_environment.yml
RUN umask 0002 && /opt/conda/bin/mamba env create -f /tmp/build/filtered_environment.yml && /opt/conda/bin/mamba clean -afy && sudo chown -R :conda /opt/conda

# Copy the already-solved conda env from the build stage (eliminates redundant ~20 min conda solve)
COPY --from=gadgetron_cudabuild --chown=$USER_UID:conda /opt/conda/envs/gadgetron /opt/conda/envs/gadgetron

# Copy build artifacts and entrypoint
COPY --from=gadgetron_cudabuild --chown=$USER_UID:conda /opt/package /opt/conda/envs/gadgetron/
COPY --from=gadgetron_cudabuild --chown=$USER_UID:conda /opt/code/gadgetron/docker/entrypoint.sh /opt/
#RUN mkdir -p /opt/models/
#COPY --from=gadgetron_cudabuild --chown=$USER_UID:conda /opt/models/ /opt/models/

ENV CONDA_ENV_PATH=/opt/conda/envs/gadgetron
RUN echo "export NUMBA_CUDA_ENABLE_PYNVJITLINK=1" >> $CONDA_ENV_PATH/etc/conda/activate.d/env_vars.sh

#COPY --chown=$USER_UID:conda OpticalFlow3d/ /opt/code/OpticalFlow3d/
RUN . /opt/conda/etc/profile.d/conda.sh && umask 0002 && conda activate gadgetron && \
sh -x && \
pip install git+https://github.com/ahsanjav/OpticalFlow3d.git
# cd /opt/code/OpticalFlow3d/ && \
# pip install .

RUN sudo rm -rf /opt/code/
RUN chmod +x /opt/entrypoint.sh
RUN sudo mkdir -p /opt/integration-test && sudo chown ${USER_GID}:${USER_UID} /opt/integration-test
RUN sudo rm -rf /opt/code/ \
&& chmod +x /opt/entrypoint.sh \
&& sudo mkdir -p /opt/integration-test \
&& sudo chown ${USER_GID}:${USER_UID} /opt/integration-test
COPY --from=gadgetron_cudabuild --chown=$USER_UID:conda /opt/code/gadgetron/test/integration /opt/integration-test/
COPY --from=gadgetron_cudabuild --chown=$USER_UID:conda /opt/code/gadgetron/test/nhlbi_integration_tests /opt/nhlbi-integration-test/
ENTRYPOINT [ "/tini", "--", "/opt/entrypoint.sh" ]

# FROM gadgetron_baseimage AS gadgetron_rt_nocuda
Expand Down
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@

# “Two-for-one”: 3D cardiac and pulmonary MR imaging from a single acquisition using bSTAR

This project is a Gadgetron extension that provides a dual inline image reconstruction pipeline for simultaneous cardiac-resolved whole heart and respiratory-resolved lung imaging.
# Volumetric real-time MRI for the guidance of cardiac catheterization at 0.55 T

This project is a Gadgetron extension that provides a inline image reconstruction pipeline for volumetric, stack-of-spirals real-time acquisitions to guide invasive procedures such as right-heart catheterization.
## Publication

This open-source is described in :

- Daudé P.,Taylor J., Plummer J., Ramasawmy R., Tsakirellis A., Moss J., Bandettini W.P., Chen M., Campbell-Washburn A.E.,Javed A. “Two-for-one”: 3D cardiac and pulmonary MR imaging from a single acquisition using bSTAR
. In preparation
This project is described in :

- Kumar P., Ramasawmy R., Javed A., Le D., Daudé P., O'Brien K., Jaimes A.E., Franson D., Chow K., Dong F., Maier F., Lederman R.J., Nayak K.S., Campbell-Washburn A.E., “Volumetric real-time MRI for the guidance of cardiac catheterization at 0.55 T". In preparation

## Documentation

Expand Down
6 changes: 6 additions & 0 deletions apps/gadgetron/connection/HeaderConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,15 @@ namespace {
/* tSequenceVariant in the header give information about the hash of the pulseq seq file
Loop through all seq files in a directory and find the correct seq file (hash is in the last line )
*/

// PK commenting this out ecause the presence of tSequenceVariant is not guaranteed for only bSTAR...
/*
if (header.userParameters) {
ISMRMRD::UserParameters user_params = header.userParameters.get();
std::vector<ISMRMRD::UserParameterString> strings = user_params.userParameterString;
std::vector<ISMRMRD::UserParameterString>::iterator it;


for (it = strings.begin(); it != strings.end(); ++it) {
if (it->name == "tSequenceVariant") {
GDEBUG("Hash bstar found %s found\n", it->value.c_str());
Expand Down Expand Up @@ -159,9 +163,11 @@ namespace {
}
}
}
*/
//PD
header_callback(header);
}


private:
std::function<void(Header)> header_callback;
Expand Down
32 changes: 20 additions & 12 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ dependencies:
- numpy=1.24.4
- scipy=1.10.1
- pyfftw=0.15.0
- h5py>=3.6,<3.8 # Must match conda HDF5 1.10.x
- hdf5>=1.14.3
- h5py=3.12.1 # Requires hdf5 >=1.14.3 (provided by ismrmrd=1.15.0)
- pyyaml=6.0
- jq=1.6
- yq=2.13
Expand Down Expand Up @@ -52,27 +53,25 @@ dependencies:

# Math and Signal Processing
- fftw=3.3.9
- armadillo=9.900.5
- armadillo>=9.900
- mkl>=2024.2.2,<2025.0a0
- mkl-include>=2024.2.2,<2025.0a0
- libxml2=2.9
- libcurl=7.79.1
- libxml2>=2.9.14,<2.14
- libcurl>=7.79
- plplot=5.15.0
- pugixml=1.12.1
- onnxruntime=1.10.0
- pugixml=1.15
- onnxruntime>=1.10
- xsdata>=22.2

# Gadgetron + ISMRMRD
- ismrmrd::ismrmrd=1.13.7
- ismrmrd::ismrmrd-python>=1.9.8
- ismrmrd::siemens_to_ismrmrd=1.2.11
# Gadgetron + ISMRMRD (C libraries via conda; Python bindings via pip below)
- ismrmrd::ismrmrd=1.15.0
- ismrmrd::siemens_to_ismrmrd=1.2.13
- ismrmrd::mrd-storage-server=0.0.12
- gadgetron::gadgetron-python=1.4.1
- gadgetron::range-v3=0.11.0

# Boost
- boost=1.80.0
- icu=70.1
- icu>=70.1

# Docs
- sphinx=4.4.0
Expand All @@ -94,6 +93,10 @@ dependencies:
- pynvjitlink

- pip:
# ismrmrd and gadgetron Python bindings installed via pip
# (conda ismrmrd-python channel only has up to 1.14.2)
- ismrmrd==1.14.2
- gadgetron==1.4.1
- nvidia-cuda-runtime-cu12==12.8.57 # Required for CuPy JIT compilation (must match PyTorch cu128 requirement)
- sigpy
- scikit-image
Expand All @@ -108,3 +111,8 @@ dependencies:
- torchvision==0.22.0
- torchaudio==2.7.0
- onnxruntime-gpu
# NHLBI integration tests (Azure Blob Storage for private test data)
- azure-storage-blob
- azure-identity
- matplotlib
- pyigtl
2 changes: 1 addition & 1 deletion gadgets/python/legacy/gadgets/accumulate_and_recon.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def process(self, acq, data, *args):

if (acq.flags & (1<<7)): #Is this the last scan in slice
image = cifftn(self.myBuffer, axes=(0, 1, 2))
image = image * np.product(image.shape)*100 #Scaling for the scanner
image = image * np.prod(image.shape)*100 #Scaling for the scanner
#Create a new image header and transfer value
img_head = ismrmrd.ImageHeader()
img_head.version = 1
Expand Down
52 changes: 52 additions & 0 deletions test/nhlbi_integration_tests/cases/3drt.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[dependency.siemens]
data_file = 3drt/noise_data.h5
measurement = 0
additional_arguments = skip_converstion

[dependency.client]
configuration = default_measurement_dependencies.xml

[dependency.siemens.1]
data_file = 3drt/prescan-reference_noise.h5
measurement = 0
additional_arguments = skip_converstion

[dependency.client.1]
configuration = default_measurement_dependencies.xml

[dependency.siemens.2]
data_file = 3drt/prescan-reference.h5
measurement = 0
additional_arguments = skip_converstion

[dependency.client.2]
configuration = spiral_3DRT_CSM_test.xml

[reconstruction.siemens]
data_file = 3drt/recon_data.h5
measurement = 0
additional_arguments = skip_converstion

[reconstruction.client]
configuration = spiral_3DRT_test.xml

[reconstruction.test]
reference_file = 3drt/baseline_output.h5
reference_images = spiral_3DRT_test.xml/image_0
output_images = spiral_3DRT_test.xml/image_0
value_comparison_threshold = 0.1
scale_comparison_threshold = 0.01

[requirements]
system_memory = 8192
gpu_support = 1
gpu_memory = 8192

[tags]
tags = nhlbi,3drt

[nhlbi]
description = spiral 3drt
noise_file = 3drt/noise_data.h5
baseline_recon_time = 42.3

36 changes: 36 additions & 0 deletions test/nhlbi_integration_tests/cases/csm_prescan.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[dependency.siemens]
data_file = csm_prescan/noise_data.h5
measurement = 0
additional_arguments = skip_converstion

[dependency.client]
configuration = default_measurement_dependencies.xml

[reconstruction.siemens]
data_file = csm_prescan/recon_data.h5
measurement = 0
additional_arguments = skip_converstion

[reconstruction.client]
configuration = spiral_3DRT_CSM_test.xml

[reconstruction.test]
reference_file = csm_prescan/baseline_output.h5
reference_images = spiral_3DRT_CSM_test.xml/image_0
output_images = spiral_3DRT_CSM_test.xml/image_0
value_comparison_threshold = 0.01
scale_comparison_threshold = 0.01

[requirements]
system_memory = 8192
gpu_support = 1
gpu_memory = 8192

[tags]
tags = nhlbi,3drt

[nhlbi]
description = Prescan CSM generation
noise_file = csm_prescan/noise_data.h5
baseline_recon_time = 56.2

Loading