Skip to content

Instantly share code, notes, and snippets.

@chaddupuis
Created October 31, 2024 20:42
Show Gist options
  • Save chaddupuis/ec70e2eceb8be23331b7728ab56d7b26 to your computer and use it in GitHub Desktop.
Save chaddupuis/ec70e2eceb8be23331b7728ab56d7b26 to your computer and use it in GitHub Desktop.
Apptainer Build for 3DSlicer w/SlicerJupyter Extension
Bootstrap: docker
From: nvcr.io/nvidia/pytorch:24.06-py3
%files
./confs/xorg.conf /xorg.conf
./confs/websockify /websockify
%post
export DEBIAN_FRONTEND=noninteractive
export TZ=Etc/UTC
apt -y update && apt -y upgrade
apt install -y wget
# Base 3dSlicer Requirements
apt install -y libglu1-mesa libpulse-mainloop-glib0 libnss3 libasound2 qt5dxcb-plugin libsm6
# 3dSlicer WebBrowser
apt install -y -q xserver-xorg-video-dummy x11vnc awesome cython3
# avoid webrowsify error
apt install -q -y --reinstall ca-certificates
# build tools (removed later) and others
apt install -y -q net-tools build-essential
# Get and Install 3d Slicer Base Package
mkdir -p /opt/3dslicer
wget -O /opt/slicer.tar.gz "https://download.slicer.org/download?os=linux&stability=release"
tar zxvf /opt/slicer.tar.gz -C /opt/3dslicer --strip-components=1
# General Python Setup
apt install -y python3-dev python3.10-venv
/usr/bin/python -m pip install --upgrade pip setuptools wheel
#apt update -q -y && \
#apt install -y build-essential --no-install-recommends && \
# Build rebind.so (required by websockify)
set -x && \
mkdir src && \
cd src && \
git clone https://github.com/novnc/websockify websockify && \
cd websockify && \
make && \
cp rebind.so /usr/lib/ && \
cd .. && \
rm -rf websockify && \
cd .. && \
rmdir src
# Setup websockify Launcher
cp /websockify /opt/3dslicer/bin/websockify
chmod +x /opt/3dslicer/bin/websockify
# dummy x terminal configuration
cp /xorg.conf /opt/3dslicer/xorg.conf
# Setup For and Install Monai
/usr/bin/python -m pip install --upgrade torchaudio
apt install -y qtbase5-dev
apt install -y --reinstall libxcb-xinerama0
/usr/bin/python -m pip install pyqt5
apt install -y npm
unset NODE_OPTIONS
npm install --global yarn
sh -c "export=OHIF_BUILD=true && /usr/bin/python -m pip install git+https://github.com/Project-MONAI/MONAILabel#egg=monailabel"
################################################################################
# Set up remote desktop access - step 2/2
# Install websockify
# Copy rebind.so into websockify folder so websockify can find it
# Install Jupyter desktop (configures noVNC connection adds a new "Desktop" option in New menu in Jupyter)
# First upgrade pip
/opt/3dslicer/bin/PythonSlicer -m pip install --upgrade pip
# Now install websockify and jupyter-server-proxy (fixed at tag v1.6.0)
/opt/3dslicer/bin/PythonSlicer -m pip install --upgrade websockify && \
cp /usr/lib/rebind.so /opt/3dslicer/lib/Python/lib/python3.9/site-packages/websockify/ && \
/opt/3dslicer/bin/PythonSlicer -m pip install notebook jupyterhub jupyterlab && \
/opt/3dslicer/bin/PythonSlicer -m pip install --upgrade pyzmq && \
/opt/3dslicer/bin/PythonSlicer -m pip install -e \
git+https://github.com/lassoan/jupyter-desktop-server#egg=jupyter-desktop-server \
git+https://github.com/jupyterhub/jupyter-server-proxy@v1.6.0#egg=jupyter-server-proxy
# /opt/3dslicer/bin/PythonSlicer -m pip install jupyter-server-proxy && \
# /opt/3dslicer/bin/PythonSlicer -m pip install jupyter-remote-desktop-proxy && \
# Extensions (extension names are case sensitive)
export QT_PLUGIN_PATH=/usr/lib/x86_64-linux-gnu/qt5/plugins
export QT_QPA_PLATFORM=offscreen
# Monai Label Server
touch /tmp/monai-label-extension.py
echo "extensionName = 'MONAILabel'" > /tmp/monai-label-extension.py
echo "em = slicer.app.extensionsManagerModel()" >> /tmp/monai-label-extension.py
echo "if not em.isExtensionInstalled(extensionName):" >> /tmp/monai-label-extension.py
echo " em.interactive = False # prevent display of popups" >> /tmp/monai-label-extension.py
echo " em.updateExtensionsMetadataFromServer(True, True) # update extension metadata from server now" >> /tmp/monai-label-extension.py
echo " if not em.downloadAndInstallExtensionByName(extensionName, True, True): # install dependencies, wait for installation to finish" >> /tmp/monai-label-extension.py
echo " raise ValueError(f'Failed to install {extensionName} extension')" >> /tmp/monai-label-extension.py
echo " slicer.util.restart()" >> /tmp/monai-label-extension.py
cat /tmp/monai-label-extension.py
QT_PLUGIN_PATH=/usr/lib/x86_64-linux-gnu/qt5/plugins QT_QPA_PLATFORM=offscreen /opt/3dslicer/Slicer --no-splash --no-main-window --python-script "/tmp/monai-label-extension.py"
# Monai Auto 3D Seg
touch /tmp/monai-3d-extension.py
echo "extensionName = 'MONAIAuto3DSeg'" > /tmp/monai-3d-extension.py
echo "em = slicer.app.extensionsManagerModel()" >> /tmp/monai-3d-extension.py
echo "if not em.isExtensionInstalled(extensionName):" >> /tmp/monai-3d-extension.py
echo " em.interactive = False # prevent display of popups" >> /tmp/monai-3d-extension.py
echo " em.updateExtensionsMetadataFromServer(True, True) # update extension metadata from server now" >> /tmp/monai-3d-extension.py
echo " if not em.downloadAndInstallExtensionByName(extensionName, True, True): # install dependencies, wait for installation to finish" >> /tmp/monai-3d-extension.py
echo " raise ValueError(f'Failed to install {extensionName} extension')" >> /tmp/monai-3d-extension.py
echo " slicer.util.restart()" >> /tmp/monai-3d-extension.py
cat /tmp/monai-3d-extension.py
QT_PLUGIN_PATH=/usr/lib/x86_64-linux-gnu/qt5/plugins QT_QPA_PLATFORM=offscreen /opt/3dslicer/Slicer --no-splash --no-main-window --python-script "/tmp/monai-3d-extension.py"
# SlicerJupyter
touch /tmp/slicerjupyter.py
echo "extensionName = 'SlicerJupyter'" > /tmp/slicerjupyter.py
echo "em = slicer.app.extensionsManagerModel()" >> /tmp/slicerjupyter.py
echo "if not em.isExtensionInstalled(extensionName):" >> /tmp/slicerjupyter.py
echo " em.interactive = False # prevent display of popups" >> /tmp/slicerjupyter.py
echo " em.updateExtensionsMetadataFromServer(True, True) # update extension metadata from server now" >> /tmp/slicerjupyter.py
echo " if not em.downloadAndInstallExtensionByName(extensionName, True, True): # install dependencies, wait for installation to finish" >> /tmp/slicerjupyter.py
echo " raise ValueError(f'Failed to install {extensionName} extension')" >> /tmp/slicerjupyter.py
echo " slicer.util.restart()" >> /tmp/slicerjupyter.py
cat /tmp/slicerjupyter.py
QT_PLUGIN_PATH=/usr/lib/x86_64-linux-gnu/qt5/plugins QT_QPA_PLATFORM=offscreen /opt/3dslicer/Slicer --no-splash --no-main-window --python-script "/tmp/slicerjupyter.py"
# Install Jupyter Server into Slicers Python Environment and Slicer Jupyter Kernel
npm uninstall --global yarn
apt -y remove npm
/opt/3dslicer/bin/PythonSlicer -m pip uninstall --yes pyzmq
apt -y install libzmq3-dev
# possibly build pyzmq from source?
/opt/3dslicer/bin/PythonSlicer -m pip install pyzmq
/opt/3dslicer/bin/PythonSlicer -m pip install --upgrade jupyter
/usr/bin/Xorg -noreset +extension GLX +extension RANDR +extension RENDER -config /opt/3dslicer/xorg.conf :7 &
sleep 20
export DISPLAY=:7
QT_PLUGIN_PATH=/usr/lib/x86_64-linux-gnu/qt5/plugins QT_QPA_PLATFORM=offscreen /opt/3dslicer/Slicer -c 'slicer.modules.jupyterkernel.installInternalJupyterServer()'
apt purge -y --auto-remove build-essential
apt -y autoremove
[you@machine] apptainer run --nv /scratch/testcontainers/nvidia-pytorch-3dslicer/nvidia-pytorch-ub2204-py310-3dSlicer562-wextensions.sif
## below "4" is used, but this can be any number so long as the export command matches it.
Apptainer> /usr/bin/Xorg -noreset +extension GLX +extension RANDR +extension RENDER -config /opt/3dslicer/xorg.conf :4 &
Apptainer> export DISPLAY=:4 ###the number you set above
## Now Start Slicer One Time # this should only need to be done once
Apptainer> /opt/3dslicer/Slicer
# After you see some text and then Switch to Module "Welcome" - then you should be able to ctrl-c and quit
## Slicer Kernel Setup
# see if you have ~/.local/share/jupyter/kernels - if not mkdir -p ~/.local/share/jupyter/kernels
# copy in the Slicer 5.6 kernel and restart notebook
## this only needs to be done once
Apptainer> cp -r /opt/3dslicer/slicer.org/Extensions-32448/SlicerJupyter/share/Slicer-5.6/qt-loadable-modules/JupyterKernel/Slicer-5.6/ ~/.local/share/jupyter/kernels/
## Start Notebook with the Python Environment "inside" Slicer
# below "8386" is used, this can be anything between 8000-9000
Apptainer> /opt/3dslicer/bin/PythonSlicer -m notebook --port=8388 --ip=0.0.0.0 --no-browser --allow-root
Subsequent starts should only need the xorg startup, display export and then start the notebook.
#!/usr/bin/env sh
# Launches websockify in Slicer's virtual Python environment
set -e
cd /opt/3dslicer/lib/Python/lib/python3.9/site-packages/websockify/
/opt/3dslicer/bin/PythonSlicer -m websockify "$@"
# This xorg configuration file is meant to be used by xpra
# to start a dummy X11 server.
# For details, please see:
# https://xpra.org/Xdummy.html
Section "ServerFlags"
Option "DontVTSwitch" "true"
Option "AllowMouseOpenFail" "true"
Option "PciForceNone" "true"
Option "AutoEnableDevices" "false"
Option "AutoAddDevices" "false"
EndSection
Section "InputDevice"
Identifier "dummy_mouse"
Option "CorePointer" "true"
Driver "void"
EndSection
Section "InputDevice"
Identifier "dummy_keyboard"
Option "CoreKeyboard" "true"
Driver "void"
EndSection
Section "Device"
Identifier "dummy_videocard"
Driver "dummy"
Option "ConstantDPI" "true"
#VideoRam 4096000
#VideoRam 256000
VideoRam 192000
EndSection
Section "Monitor"
Identifier "dummy_monitor"
HorizSync 5.0 - 1000.0
VertRefresh 5.0 - 200.0
#This can be used to get a specific DPI, but only for the default resolution:
#DisplaySize 508 317
#NOTE: the highest modes will not work without increasing the VideoRam
# for the dummy video card.
Modeline "1920x1440" 69.47 1920 1960 2152 2384 1440 1441 1444 1457
Modeline "1920x1200" 26.28 1920 1952 2048 2080 1200 1229 1231 1261
Modeline "1920x1080" 23.53 1920 1952 2040 2072 1080 1106 1108 1135
Modeline "1680x1050" 20.08 1680 1712 1784 1816 1050 1075 1077 1103
Modeline "1600x1200" 22.04 1600 1632 1712 1744 1200 1229 1231 1261
Modeline "1600x900" 33.92 1600 1632 1760 1792 900 921 924 946
Modeline "1440x900" 30.66 1440 1472 1584 1616 900 921 924 946
ModeLine "1366x768" 72.00 1366 1414 1446 1494 768 771 777 803
Modeline "1280x1024" 31.50 1280 1312 1424 1456 1024 1048 1052 1076
Modeline "1280x800" 24.15 1280 1312 1400 1432 800 819 822 841
Modeline "1280x768" 23.11 1280 1312 1392 1424 768 786 789 807
Modeline "1360x768" 24.49 1360 1392 1480 1512 768 786 789 807
Modeline "1024x768" 18.71 1024 1056 1120 1152 768 786 789 807
Modeline "768x1024" 19.50 768 800 872 904 1024 1048 1052 1076
Modeline "1280x800" 24.15 1280 1312 1400 1432 800 819 822 841
Option "PreferredMode" "1280x1024"
EndSection
Section "Screen"
Identifier "dummy_screen"
Device "dummy_videocard"
Monitor "dummy_monitor"
DefaultDepth 24
SubSection "Display"
Viewport 0 0
Depth 24
#Modes "32768x32768" "32768x16384" "16384x8192" "8192x4096" "5120x3200" "3840x2880" "3840x2560" "3840x2048" "2048x2048" "2560x1600" "1920x1440" "1920x1200" "1920x1080" "1600x1200" "1680x1050" "1600x900" "1400x1050" "1440x900" "1280x1024" "1366x768" "1280x800" "1024x768" "1024x600" "800x600" "320x200"
Modes "1400x1050" "1440x900" "1280x1024" "1366x768" "1280x800" "1024x768" "1024x600" "800x600" "320x200"
#Virtual 32000 32000
#Virtual 16384 8192
Virtual 1280 1024
#Virtual 5120 3200
EndSubSection
EndSection
Section "ServerLayout"
Identifier "dummy_layout"
Screen "dummy_screen"
InputDevice "dummy_mouse"
InputDevice "dummy_keyboard"
EndSection
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment