From ffeb942a9aae3d0036fc86d1162053f16eebc34e Mon Sep 17 00:00:00 2001 From: Philip Schaten Date: Fri, 6 Sep 2024 11:46:09 +0200 Subject: [PATCH 1/4] Make BART run on the scanner. - mmapping on TMPDIR /tmp/share does not work on the scanner, where this is a CIFS mounted directory. /tmp should be a tmpfs in the chroot on the scanner, thus this should not fill up the chroot either. - ENV variables aren't carried through to the chroot-image. Thus 'import bart' wouldn't work in the chroot. (on the github main branch we already have an installable python module; but for now stay at v0.9.00) - static linking leads to segfault in gfortran lib. To resolve this for now, changed to dynamic linking and install the runtime dependencies in the container. - Switched bart-build container to same base image as the fire-python container. --- bartfire.py | 3 +++ docker/bart/Dockerfile | 11 ++++++++--- start-fire-python-server.sh | 4 ++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/bartfire.py b/bartfire.py index d527693..c5e2c3a 100755 --- a/bartfire.py +++ b/bartfire.py @@ -12,6 +12,9 @@ # Folder for debug output files debugFolder = "/tmp/share/debug" +# Don't write to /tmp/share in cfl +os.environ["TMPDIR"] = "/tmp" + def process(connection, config, metadata): logging.info("Config: \n%s", config) diff --git a/docker/bart/Dockerfile b/docker/bart/Dockerfile index a28e446..1c7e4be 100644 --- a/docker/bart/Dockerfile +++ b/docker/bart/Dockerfile @@ -1,5 +1,5 @@ # ----- First stage to build BART ----- -FROM python:3.9-slim AS bart_build +FROM python:3.12.0-slim AS bart_build ARG DEBIAN_FRONTEND=noninteractive ENV TZ=America/Chicago @@ -8,15 +8,17 @@ RUN mkdir -p /opt/code # BART (static linked) RUN cd /opt/code && \ - git clone https://github.com/mrirecon/bart.git --branch v0.7.00 && \ + git clone https://github.com/mrirecon/bart.git --branch v0.9.00 && \ cd bart && \ - make SLINK=1 -j $(nproc) && \ + make -j $(nproc) && \ make install # ----- Main stage without build dependencies ----- # Re-use already built Docker image, but the contents of \docker\Dockerfile can also be # recapitulated here instead to ensure the latest build FROM kspacekelvin/fire-python + +# this does *not* work in the chroot image! ENV PYTHONPATH=/opt/code/bart/python # Copy BART from previous stage @@ -24,3 +26,6 @@ COPY --from=bart_build /usr/local/bin/bart /usr/local/bin/ COPY --from=bart_build /usr/local/lib/bart/commands /usr/local/lib/bart/commands COPY --from=bart_build /usr/local/share/doc/bart /usr/local/share/doc/bart COPY --from=bart_build /opt/code/bart /opt/code/bart + +# Install BART runtime dependencies +RUN apt-get update && apt-get install -y libfftw3-single3 libgomp1 liblapacke libpng16-16 libblas3 libunwind8 diff --git a/start-fire-python-server.sh b/start-fire-python-server.sh index 1df0d1d..7c93a7b 100755 --- a/start-fire-python-server.sh +++ b/start-fire-python-server.sh @@ -9,6 +9,10 @@ # it's less likely to accidentally fill up the chroot export TMPDIR=/tmp/share +# This is needed for the chroot-images to run BART. +export PYTHONPATH=/opt/code/bart/python:${PYTHONPATH} + + if [ $# -eq 1 ]; then LOG_FILE=${1} python3 /opt/code/python-ismrmrd-server/main.py -v -r -H=0.0.0.0 -p=9002 -l=${LOG_FILE} & From f3923ebde689aba35deece5cd538e6035d390377 Mon Sep 17 00:00:00 2001 From: Philip Schaten Date: Thu, 5 Sep 2024 18:27:19 +0200 Subject: [PATCH 2/4] remove created directory in docker_tar_to_chroot --- docker/docker_tar_to_chroot.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker/docker_tar_to_chroot.sh b/docker/docker_tar_to_chroot.sh index 6ba4080..bffe71b 100755 --- a/docker/docker_tar_to_chroot.sh +++ b/docker/docker_tar_to_chroot.sh @@ -2,6 +2,8 @@ # This script takes a Docker container export (.tar) creates a chroot image (.img) # Note that root privileges are required to mount the loopback images +set -eu + # Syntax: ./docker_tar_to_chroot.sh docker-export.tar chroot.img EXPORT_FILE=${1} @@ -40,6 +42,7 @@ tar -xf ${EXPORT_FILE} --directory=/mnt/chroot --totals df -h umount /mnt/chroot +rmdir /mnt/chroot echo Finished! Verify that no errors have occured and that available space on the echo last row of the above df output is greater than 100 MB From 7281557fd8d8a74b19ed6a109c49b88658f1ebc6 Mon Sep 17 00:00:00 2001 From: Philip Schaten Date: Tue, 10 Sep 2024 16:02:37 +0200 Subject: [PATCH 3/4] Use RSS in BART. --- bartfire.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/bartfire.py b/bartfire.py index c5e2c3a..0f79945 100755 --- a/bartfire.py +++ b/bartfire.py @@ -138,15 +138,9 @@ def process_raw(group, config, metadata): logging.info("Calling BART FFT") data = bart(1, 'fft -u -i 3', data) - # Re-format as [cha row col phs] - data = data.transpose((3, 0, 1, 2)) - - # Sum of squares coil combination - # Data will be [PE RO phs] - data = np.abs(data) - data = np.square(data) - data = np.sum(data, axis=0) - data = np.sqrt(data) + # RSS with BART + data = bart(1, 'rss 8', data) + data = np.atleast_3d(data.real) logging.debug("Image data is size %s" % (data.shape,)) np.save(debugFolder + "/" + "img.npy", data) From 42f3a93eb5861afee81318938d33cd9e3e971fe9 Mon Sep 17 00:00:00 2001 From: Philip Schaten Date: Fri, 6 Sep 2024 11:15:19 +0200 Subject: [PATCH 4/4] Add NLINV reco example. --- bartfire.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/bartfire.py b/bartfire.py index 0f79945..1dda4d3 100755 --- a/bartfire.py +++ b/bartfire.py @@ -15,6 +15,8 @@ # Don't write to /tmp/share in cfl os.environ["TMPDIR"] = "/tmp" +use_nlinv = False + def process(connection, config, metadata): logging.info("Config: \n%s", config) @@ -48,7 +50,7 @@ def process(connection, config, metadata): if isinstance(item, ismrmrd.Acquisition): # Accumulate all imaging readouts in a group if (not item.is_flag_set(ismrmrd.ACQ_IS_NOISE_MEASUREMENT) and - not item.is_flag_set(ismrmrd.ACQ_IS_PARALLEL_CALIBRATION) and + (use_nlinv or not item.is_flag_set(ismrmrd.ACQ_IS_PARALLEL_CALIBRATION)) and not item.is_flag_set(ismrmrd.ACQ_IS_PHASECORR_DATA)): acqGroup.append(item) @@ -134,13 +136,26 @@ def process_raw(group, config, metadata): logging.debug("Raw data is size %s" % (data.shape,)) np.save(debugFolder + "/" + "raw.npy", data) - # Fourier Transform with BART - logging.info("Calling BART FFT") - data = bart(1, 'fft -u -i 3', data) + # Reconstruction with BART. + if use_nlinv: + logging.info("Calling BART NLINV") + + # coil compression to 8 channels + data = bart(1, "cc -p8", data) + + # nlinv with debug level 4 and 13 gauss-newton steps. + data = bart(1, "nlinv -d4 -i13", data) + + data = np.atleast_3d(abs(data)) + + else: + logging.info("Calling BART FFT") + data = bart(1, 'fft -u -i 3', data) + + # RSS with BART + data = bart(1, 'rss 8', data) - # RSS with BART - data = bart(1, 'rss 8', data) - data = np.atleast_3d(data.real) + data = np.atleast_3d(data.real) logging.debug("Image data is size %s" % (data.shape,)) np.save(debugFolder + "/" + "img.npy", data)