Build Slicer docker image with preinstalled elastix

The title says it all. I want a docker image of slicer with elastix extension preinstalled. My current Dockerfile is the following:

# start from base with centos & qt5
FROM slicer/buildenv-qt5-centos7:latest

WORKDIR /usr/src

# get basics for the gui
RUN yum install Xvfb Xorg mesa-dri-drivers libGLEW -y

# get slicer nighly version
RUN git clone -b master https://github.com/Slicer/Slicer.git

# create slicer-build and environment
RUN mkdir /usr/src/Slicer-build && \
    cd /usr/src/Slicer-build && \
    # build slicer
    cmake \
    -DCMAKE_BUILD_TYPE:STRING=Release \
    -DQt5_DIR:PATH=${Qt5_DIR} \
    -DSlicer_USE_SimpleITK:BOOL=OFF \
    -DSlicer_USE_QtTesting:BOOL=OFF \
    /usr/src/Slicer && \
    # build dependencies
    make -j 32

# create environment variables
ENV PATH="${PATH}:/usr/src/Slicer-build/python-install/bin"
ENV PYTHONPATH="${PYTHONPATH}:/usr/src/Slicer-build/python-install/bin/PythonSlicer"
ENV SLICER=/usr/src/Slicer

# Create ExtensionsIndex
RUN git clone git://github.com/Slicer/ExtensionsIndex.git && \
    mkdir ExtensionsIndex-build && cd ExtensionsIndex-build && \
    cmake -DSlicer_DIR:PATH=/usr/src/Slicer-build/Slicer-build -DSlicer_EXTENSION_DESCRIPTION_DIR:PATH=../ExtensionsIndex \
    -DCMAKE_BUILD_TYPE:STRING=Release ${SLICER}/Extensions/CMake && \
    make -j 32

# Install Elastix
RUN PythonSlicer -c "import sys;print(sys.path)"
RUN PythonSlicer -m pip install --upgrade pip
RUN PythonSlicer -m pip install 2to3 lmfit
RUN 2to3 -w /usr/src/Slicer-build/python-install/lib/python3.6/site-packages/uncertainties

# start running container
CMD bash

But this raises an 18GB image!! All extensions are built!! The worst is: no extension is installed! They are all built and able to be installed, but not actually installed.

When I try to use it, it says slicer has no module app.

What is wrong here??

I have tried building an image from slicer-base, but it is also unsuccessful. The Dockerfile is:

# start from base with slicer-base
FROM slicer/slicer-base

WORKDIR /usr/src/Slicer

# get basics for the gui
RUN yum install Xvfb Xorg mesa-dri-drivers libGLEW -y

# create slicer package
RUN ../Slicer-build/BuildSlicer.sh && \
     package=$(head -n1 /usr/src/Slicer-build/Slicer-build/PACKAGE_FILE.txt) && \
     echo "package [${package}]" && \
     mkdir -p /Slicer-package  && \
     mv ${package} /Slicer-package/

# create environment variables
ENV PATH="${PATH}:/Slicer-package/"
ENV PYTHONPATH="${PYTHONPATH}:/usr/src/Slicer-build/python-install/bin/PythonSlicer"
ENV SLICER=/usr/src/Slicer

# Install Elastix
RUN PythonSlicer -c "import sys;print(sys.path)"
RUN PythonSlicer -m pip install --upgrade pip
RUN PythonSlicer -m pip install 2to3 lmfit
RUN 2to3 -w /usr/src/Slicer-build/python-install/lib/python3.6/site-packages/uncertainties
RUN (echo "import slicer" ; echo "emm = slicer.app.extensionsManagerModel()"; echo "md = emm.retrieveExtensionMetadataByName('SlicerElastix')"; echo "emm.dow$
RUN PythonSlicer InstallElastix.py

# start running container
CMD bash

This fails in the BuildSlicer.sh

Step 4/14 : RUN ../Slicer-build/BuildSlicer.sh &&      package=$(head -n1 /usr/src/Slicer-build/Slicer-build/PACKAGE_FILE.txt) &&      echo "package [${package}]" &&      mkdir -p /Slicer-package  &&      mv ${package} /Slicer-package/
 ---> Running in 8bb2e3f6bf82
+ set -o pipefail
+ set -o
allexport      	off
braceexpand    	on
emacs          	off
errexit        	on
errtrace       	off
functrace      	off
hashall        	on
histexpand     	off
history        	off
ignoreeof      	off
interactive-comments	on
keyword        	off
monitor        	off
noclobber      	off
noexec         	off
noglob         	off
nolog          	off
notify         	off
nounset        	off
onecmd         	off
physical       	off
pipefail       	on
posix          	off
privileged     	off
verbose        	off
vi             	off
xtrace         	on
+ cd /usr/src/Slicer-build
+ /usr/bin/cmake -E make_directory /usr/src/Slicer
+ /usr/bin/cmake -E make_directory /usr/src/Slicer-build/Slicer-build
+ /usr/bin/cmake -E make_directory /usr/src/Slicer-build/Slicer-prefix
+ /usr/bin/cmake -E make_directory /usr/src/Slicer-build/Slicer-prefix/tmp
+ /usr/bin/cmake -E make_directory /usr/src/Slicer-build/Slicer-prefix/src/Slicer-stamp
+ /usr/bin/cmake -E make_directory /usr/src/Slicer-build/Slicer-prefix/src
+ /usr/bin/cmake -E touch /usr/src/Slicer-build/Slicer-prefix/src/Slicer-stamp/Slicer-mkdir
+ cd /usr/src/Slicer-build/Slicer-prefix/src
+ /usr/bin/cmake -E echo_append
+ /usr/bin/cmake -E touch /usr/src/Slicer-build/Slicer-prefix/src/Slicer-stamp/Slicer-download
+ cd /usr/src/Slicer
+ /usr/bin/cmake -E echo_append
+ /usr/bin/cmake -E touch /usr/src/Slicer-build/Slicer-prefix/src/Slicer-stamp/Slicer-update
+ cd /usr/src/Slicer-build
+ /usr/bin/cmake -E echo_append
+ /usr/bin/cmake -E touch /usr/src/Slicer-build/Slicer-prefix/src/Slicer-stamp/Slicer-patch
+ cd /usr/src/Slicer-build/Slicer-build
+ /usr/bin/cmake -C/usr/src/Slicer-build/Slicer-prefix/tmp/Slicer-cache-Release.cmake -GNinja /usr/src/Slicer
loading initial cache file /usr/src/Slicer-build/Slicer-prefix/tmp/Slicer-cache-Release.cmake
CMake Error: The source directory "/usr/src/Slicer" does not appear to contain CMakeLists.txt.
Specify --help for usage, or press the help button on the CMake GUI.
+ exit 1
The command '/bin/sh -c ../Slicer-build/BuildSlicer.sh &&      package=$(head -n1 /usr/src/Slicer-build/Slicer-build/PACKAGE_FILE.txt) &&      echo "package [${package}]" &&      mkdir -p /Slicer-package  &&      mv ${package} /Slicer-package/' returned a non-zero code: 1

Somehow I see the slicer-base image to be empty, is this normal? Where it is Slicer installed here?

E15TY:~ alexvergaragil$ docker run -it slicer/slicer-base  /bin/bash
[root@01a14648b5d2 Slicer-build]# ls /usr/src/
cmake-3.13.4-Centos5-x86_64  debug  kernels  Slicer-build
[root@01a14648b5d2 Slicer-build]# ls Slicer-
Slicer-build/  Slicer-prefix/
[root@01a14648b5d2 Slicer-build]# ls Slicer-build/
share
[root@01a14648b5d2 Slicer-build]# ls Slicer-prefix/
src  tmp
[root@01a14648b5d2 Slicer-build]# ls Slicer-prefix/src/
Slicer-stamp
[root@01a14648b5d2 Slicer-build]# ls Slicer-prefix/src/Slicer-stamp/
[root@01a14648b5d2 Slicer-build]# ls Slicer-build/share/
Slicer-4.11
[root@01a14648b5d2 Slicer-build]# ls Slicer-build/share/Slicer-4.11/
Slicer.crt
[root@01a14648b5d2 Slicer-build]#

for the version from slicer/buildenv:

Building the Extensions Index does not install the extensions. You will still need to manually install by setting module paths

Can you post the error where slicer fails to start?

Please remember this is a docker container, I can’t start slicer there. Also the slicer-base image is supposed to have a prebuilt image of Slicer, I just need to add the elastix extension by default. I can trigger the instalation in Slicer by:

import slicer
emm = slicer.app.extensionsManagerModel()
md = emm.retrieveExtensionMetadataByName('SlicerElastix')
emm.downloadAndInstallExtension(md['extension_id'])

but in the docker environment this fails with the error:

Traceback (most recent call last):
  File "installElastix.py", line 2, in <module>
    emm = slicer.app.extensionsManagerModel()
AttributeError: module 'slicer' has no attribute 'app'
vtkDebugLeaks has found no leaks.

Also

E15TY:~ alexvergaragil$ docker run -it slicer/buildenv  /bin/bash
Unable to find image 'slicer/buildenv:latest' locally
docker: Error response from daemon: pull access denied for slicer/buildenv, repository does not exist or may require 'docker login': denied: requested access to the resource is denied.
See 'docker run --help'.
  1. Sorry slicer/buildenv should have been slicer/buildenv-qt5-centos7:latest

  2. slicer/slicer-base does not contain a slicer build, it is used for CircleCI builds and so contains everything except Slicer

  3. We currently do not have a supported full Slicer docker image. There is a very out of date one available under slicer/slicer-build

I am looking into the best way to get this working for you (@jcfr )

The Dockerfile that would be responsible to create a docker image useful for runtime could simply download the nightly build and extract it.

It could be based of the work of @ihnorton. See https://github.com/ihnorton/dockerfiles/blob/master/slicer2binder/Dockerfile

Once we have such a base image, anyone could easily create derived image with whatever extension they would like.

2 Likes

Thanks for pointing this to me, however I still got some problems:

My Dockerfile so far:


# start from base with slicer-base
FROM slicer/slicer-base

RUN mkdir /usr/src/Slicer && cd /usr/src/Slicer

WORKDIR /usr/src/Slicer

################################################################################
# Download and unpack Slicer
RUN pip install pip beautifulsoup4 2to3 -U
RUN git clone https://gist.github.com/f49c5062695562370a76222dada47e47.git && \
    mv f49c5062695562370a76222dada47e47/updateSlicer.py ./updateSlicer.py
RUN python3 updateSlicer.py /usr/src/Slicer

# Create evioronment variables
ENV PATH="${PATH}:/usr/src/Slicer/bin"
ENV PYTHONPATH="${PYTHONPATH}:/usr/src/Slicer/bin/PythonSlicer"
ENV SLICER=/usr/src/Slicer

# Install Elastix
RUN PythonSlicer -c "import sys;print(sys.path)"  && \
    PythonSlicer -m pip install --upgrade pip lmfit
RUN 2to3 -w /usr/src/Slicer/lib/Python/lib/python3.6/site-packages/uncertainties
RUN (echo "import slicer"; echo "emm = slicer.app.extensionsManagerModel()"; echo "md = emm.retrieveExtensionMetadataByName('SlicerElastix')"; echo "emm.down$
RUN PythonSlicer installElastix.py

# start running container
CMD bash

this is failing at:

Step 12/14 : RUN (echo "import slicer"; echo "emm = slicer.app.extensionsManagerModel()"; echo "md = emm.retrieveExtensionMetadataByName('SlicerElastix')"; echo "emm.downloadAndInstallExtension(md['extension_id'])" ) >> installElastix.py
 ---> Running in 5d79f9d8ac46
Removing intermediate container 5d79f9d8ac46
 ---> cbd1129c5580
Step 13/14 : RUN PythonSlicer installElastix.py
 ---> Running in 10b10c6e2546
No module named 'logic'
Traceback (most recent call last):
  File "installElastix.py", line 2, in <module>
    emm = slicer.app.extensionsManagerModel()
AttributeError: module 'slicer' has no attribute 'app'
vtkDebugLeaks has found no leaks.
The command '/bin/sh -c PythonSlicer installElastix.py' returned a non-zero code: 1

What is it wrong here? The failing script is this one:

RUN (echo "import slicer"; \
     echo "emm = slicer.app.extensionsManagerModel()"; \
     echo "md = emm.retrieveExtensionMetadataByName('SlicerElastix')"; \
     echo "emm.downloadAndInstallExtension(md['extension_id'])" ) >> installElastix.py

I have tried this approach and in the test it will fail with

CMake Error at CMakeLists.txt:17 (find_package):
  By not providing "FindSlicer.cmake" in CMAKE_MODULE_PATH this project has
  asked CMake to find a package configuration file provided by "Slicer", but
  CMake did not find one.

  Could not find a package configuration file provided by "Slicer" with any
  of the following names:

    SlicerConfig.cmake
    slicer-config.cmake

  Add the installation prefix of "Slicer" to CMAKE_PREFIX_PATH or set
  "Slicer_DIR" to a directory containing one of the above files.  If "Slicer"
  provides a separate development package or SDK, be sure it has been
  installed.

The CMakeList.txt in my extension is:

cmake_minimum_required(VERSION 3.5)

project(Dosimetry4D)

#-----------------------------------------------------------------------------
# Extension meta-information
set(EXTENSION_HOMEPAGE "http://gitlab.com/opendose/opendose4d")
set(EXTENSION_CATEGORY "Dosimetry")
set(EXTENSION_CONTRIBUTORS "Alex Vergara Gil (INSERM) and Janick Rueegger (KSA)")
set(EXTENSION_DESCRIPTION "This extension contains the entire dosimetry workflow for a multipoint study")
set(EXTENSION_ICONURL "https://opendose.org/themes/default/images/opendose_favicon.png")
set(EXTENSION_SCREENSHOTURLS "http://www.example.com/Slicer/Extensions/Dosimetry/Screenshots/1.png")
set(EXTENSION_DEPENDS SlicerElastix) # Specified as a space separated string, a list or 'NA' if any

#-----------------------------------------------------------------------------
# Extension dependencies
find_package(Slicer REQUIRED)
include(${Slicer_USE_FILE})

#-----------------------------------------------------------------------------
# Extension modules
add_subdirectory(Dosimetry4D)
## NEXT_MODULE

#-----------------------------------------------------------------------------
include(${Slicer_EXTENSION_GENERATE_CONFIG})
include(${Slicer_EXTENSION_CPACK})

#-----------------------------------------------------------------------------
option(ENABLE_TESTS "Enable tests" OFF)
if (${ENABLE_TESTS})
    enable_testing()
endif()

but my extension is just a python module, Is there a way to avoid the find_package(Slicer REQUIRED) line??

You need to pass -DSlicer_DIR:PATH=... to CMake when you configure Dosimetry4D (or the extension manager index builder project).

I have done that, but that path does not contain any cmake file in the nightly build. You can use docker run -it unnmdnwb3/slicer3d-nightly:0.8.0 /bin/bash to test this approach. It contains a slicer-base image with a preinstalled slicer-nightly. the Slicer_DIR is /usr/src/Slicer

I see. An install tree is not sufficient for build&test. As a workaround, you could create a script that runs Pytjon selftests manually. You can find the full command-line of each test on CDash.

Actually I only require the slicerMacroBuildScriptedModule macro definition to run the tests. This file does not depend on any slicer header, but is the one who builds the python module.
Are the command lines like this one?

/usr/src/Slicer-build/Slicer-build/Slicer "--no-splash" "--testing" "--no-main-window" "--additional-module-paths" "/builds/dosimetry4d-build/lib/Slicer-4.11/qt-scripted-modules" "/builds/dosimetry4d-build/lib/Slicer-4.11/cli-modules" "/builds/dosimetry4d-build/lib/Slicer-4.11/qt-loadable-modules" "--python-code" "import slicer.testing; slicer.testing.runUnitTest(['/builds/dosimetry4d-build/Dosimetry4D/Logic', '/builds/opendose/opendose4d/Dosimetry4D/Logic'], 'Dosimetry4DTest')"

Yes.

It would be nice to properly build and test your extension. It would increase the test coverage (it would test building) and you never know when you’ll need to add a loadable or CLI module to your extension.

I can perform normal test without elastix, but that will truncate half the functionality of my extension. Is it possible to have a build sequence that leaves me with a built Slicer+Elastix by default?

Are you still trying to do the following:

want a docker image of slicer with elastix extension preinstalled

If yes, there is no need to base your image of slicer/slicer-base. Using a centos7 base should be sufficient.

Also, what would you like to do with this docker image ?

I want to test my extension wich depends on elastix

I have tried to run manually the tests and this happens

$ /usr/src/Slicer/bin/PythonSlicer -c "import slicer.testing; slicer.testing.runUnitTest(['$CI_BUILDS_DIR/dosimetry4d-build/Dosimetry4D/Logic', '$CI_BUILDS_DIR/opendose/opendose4d/Dosimetry4D/Logic'], 'Dosimetry4DTest')"
Dosimetry4DTest (unittest.loader._FailedTest) ... ERROR

======================================================================
ERROR: Dosimetry4DTest (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: Dosimetry4DTest
Traceback (most recent call last):
  File "/usr/src/Slicer/lib/Python/lib/python3.6/unittest/loader.py", line 153, in loadTestsFromName
    module = __import__(module_name)
  File "/builds/dosimetry4d-build/Dosimetry4D/Logic/Dosimetry4DTest.py", line 3, in <module>
    from slicer.ScriptedLoadableModule import ScriptedLoadableModuleTest
  File "/usr/src/Slicer/bin/Python/slicer/ScriptedLoadableModule.py", line 4, in <module>
    import qt, ctk, slicer
  File "/usr/src/Slicer/bin/Python/qt/__init__.py", line 19, in <module>
    from PythonQt.private import QObject
ModuleNotFoundError: No module named 'PythonQt'


----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/src/Slicer/bin/Python/slicer/testing.py", line 26, in runUnitTest
    exitFailure()
  File "/usr/src/Slicer/bin/Python/slicer/testing.py", line 11, in exitFailure
    raise Exception(message)
Exception
No module named 'logic'
-------------------------------------------
path: ['/builds/dosimetry4d-build/Dosimetry4D/Logic', '/builds/opendose/opendose4d/Dosimetry4D/Logic']
testname: Dosimetry4DTest
-------------------------------------------
vtkDebugLeaks has found no leaks.

I think preinstalled slicer leads to even more errors than building it

I have succeeded to make a Full Test using BrainsFit, not optimal but good enough solution. The Full Test in graphical mode (Reload and test) will use elastix if it is installed.
The module is complete, I am just waiting for testers answers. Next steps will be:

  1. Write the article (shall be presented in ECMP2020 or EANM2020)
  2. Open the source (as soon as the abstract is accepted)
  3. integrating in Slicer as scripted module (I will need your help in this point). The module shall be accesible by the time the article is published.
1 Like