apptainer build of 3dslicer in an nvidia/pytorch container with the slicer/jupyter extension enabled.
This is a test using https://github.com/Slicer/SlicerJupyter and much input from https://github.com/Slicer/SlicerDocker .
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 | |
apptainer build of 3dslicer in an nvidia/pytorch container with the slicer/jupyter extension enabled.
This is a test using https://github.com/Slicer/SlicerJupyter and much input from https://github.com/Slicer/SlicerDocker .
[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 |