4

I would like to make an easily runnable jupyter notebook that supports OpenCV and is delivered as a Docker image.

The concept is to have a docker container running the jupyter kernel and accessing the notebook through the browser in the host. Something similar to this.

However, the issue is that OpenCV seems to be dependent on a running Gtk environment.So trying to run the following code:

import numpy as np
import cv2
img = cv2.imread('pendulum.png',0)
cv2.imshow('image',img) 

Results in jupyter kernel crashing with:

(image:603): Gtk-WARNING **: cannot open display: 
[I 15:23:49.808 NotebookApp] KernelRestarter: restarting kernel (1/5)

Is there a way to bypass this dependency and have OpenCV running in a docker container show images in the browser of the host system ?


Steps to reproduce the issue:

Dockerfile:

FROM ubuntu:16.04

RUN apt-get update

RUN apt-get update --fix-missing && apt-get install -y wget bzip2 ca-certificates \
    libglib2.0-0 libxext6 libsm6 libxrender1 \
    git mercurial subversion

RUN echo 'export PATH=/opt/conda/bin:$PATH' > /etc/profile.d/conda.sh && \
    wget --quiet https://repo.continuum.io/archive/Anaconda3-4.0.0-Linux-x86_64.sh && \
    /bin/bash /Anaconda3-4.0.0-Linux-x86_64.sh -b -p /opt/conda && \
    rm /Anaconda3-4.0.0-Linux-x86_64.sh

ENV PATH /opt/conda/bin:$PATH

RUN conda install -y -c https://conda.binstar.org/menpo opencv3

RUN apt-get install -y libgomp1
RUN apt-get install -y libgtk2.0-0 x11-xserver-utils libcanberra-gtk3-module
RUN mkdir /home/user
RUN groupadd -r user -g 777 && \
useradd -u 431 -r -g user -d /home/user -s /sbin/nologin -c "Docker image user" user

RUN apt-get install -y libcanberra-gtk*

RUN chown -R user:user /home/user

USER user
WORKDIR /home/user

Commands to execute:

docker build -t opencv-play .
docker run -v /home/user/.Xauthority:/home/user/.Xauthority -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$DISPLAY -u user -v `pwd`:/home/user -p 8008:8008 -t -i opencv-play
jupyter notebook --ip='*' --no-browser --port=8008   #Inside the container
#Open the browser, URL-> http://localhost:8008
#Run the above code in jupyter
TheMeaningfulEngineer
  • 15,679
  • 27
  • 85
  • 143

4 Answers4

4

There are two possible solutions:

  1. Allow your container to access your local XServer. This will show all graphical output as if you ran the software directly on the host instead of in a Docker container. In order to do this, you need to set the DISPLAY environment variable and you need to pass the X11 socket to the container. In your example, you would:

    $ docker run -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY -v `pwd`:/home/workspace -p 8008:8008 -t -i opencv-play
    
    • -v /tmp/.X11-unix:/tmp/.X11-unix passes the X11 socket to the container
    • -e DISPLAY=$DISPLAY sets the display environment variable to the one on your host system
  2. If you don't need the graphical output, you can run a 'fake' X server that doesn't show any output. Xvfb is such a display server that doesn't need any access to a monitor. In order to use it, you would need to install Xvfb in your image, i.e. add apt-get install xvfb to your Dockerfile. Then, when you run the container, you first need to start xvfb and set DISPLAY accordingly. I usually do this with a small script that then starts your command, e.g.

    #!/bin/bash
    export DISPLAY=0:0
    Xvfb $DISPLAY &
    jupyter notebook --ip='*' --no-browser --port=8008
    

    This starts Xvfb in the background and then starts jupyter, which passes all graphical output to the Xvfb display server on 0:0. Add this script to the image and then run it in the following way:

    docker run -v `pwd`:/home/workspace -p 8008:8008 -t -i opencv-play /path/to/the/script
    

    Note that you do not need to pass DISPLAY to the container.

morxa
  • 3,221
  • 3
  • 27
  • 43
2

My suggestion is to not use OpenCV for plotting and instead use matplotlib. This gives you better plotting (the OpenCV plot functions are incredibly limited), and also allows direct notebook output.

You can choose to either use the inline backend which renders each plot as a PNG(?) image and displays it in the notebook. This however, does not allow zooming, panning, etc.

The notebook backend allows zooming, etc, but was quite rough around the edges last time I tried it.

Either way, to use it you can select the backend with the %matplotlib magic.

Example

import matplotlib.pyplot as plt
%matplotlib inline

plt.imshow(img)
plt.title('My image')
plt.show()

For the notebook backend simply do %matplotlib notebook instead.

Working with images, it is also usually a good idea to call plt.imshow with no interpolation:

plt.imshow(img, interpolation='none')

The call to plt.show() is required with the "normal" desktop backends (Gtk, Qt), but not for the inline backend (and the notebook backend? I don't remember).

Hannes Ovrén
  • 21,229
  • 9
  • 65
  • 75
1

To be able to run opencv on inside docker's jupyter I did as followed same as other user but in a more clear way:

In the host:

xhost +local:root

docker run -it --name yolo_cuda8 -v "/home/dl001/dhiren/robust_tracking/camera/:/home/notebooks" -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY -p 8888:8888 new_cuda8 bash

Inside container:

jupyter notebook --port=8888 --ip=0.0.0.0 --allow-root --no-browser

It's worthy to check DISPLAY variable in the host by issuing echo $DISPLAY command and verify and set if there is not inside docker container.

env
export DISPLAY=:1

Reference:

link

link2

Dhiren Hamal
  • 874
  • 1
  • 12
  • 31
0

After the comments and additional research I managed to get the windows to load up but they can't be closed once open. :p

Here is how to do it:

FROM ubuntu:16.04

RUN apt-get update

RUN apt-get update --fix-missing && apt-get install -y wget bzip2 ca-certificates \
    libglib2.0-0 libxext6 libsm6 libxrender1 \
    git mercurial subversion

RUN echo 'export PATH=/opt/conda/bin:$PATH' > /etc/profile.d/conda.sh && \
    wget --quiet https://repo.continuum.io/archive/Anaconda3-4.0.0-Linux-x86_64.sh && \
    /bin/bash /Anaconda3-4.0.0-Linux-x86_64.sh -b -p /opt/conda && \
    rm /Anaconda3-4.0.0-Linux-x86_64.sh

ENV PATH /opt/conda/bin:$PATH

RUN conda install -y -c https://conda.binstar.org/menpo opencv3

RUN apt-get install -y libgomp1
RUN apt-get install -y libgtk2.0-0 x11-xserver-utils libcanberra-gtk3-module


RUN mkdir /home/user
RUN groupadd -r user -g 777 && \
useradd -u 431 -r -g user -d /home/user -s /sbin/nologin -c "Docker image user" user

RUN apt-get install -y libcanberra-gtk*

RUN chown -R user:user /home/user

USER user
WORKDIR /home/user
CMD jupyter notebook --ip='*' --no-browser --port=8008

Run the image:

docker run -v /home/user/.Xauthority:/home/user/.Xauthority -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$DISPLAY -u root -v `pwd`:/home/user -p 8008:8008 -t -i opencv-play

Start the notebook (localhost:8008).

Run the code below:

import numpy as np
import matplotlib.pyplot as plt
import cv2

img = cv2.imread('pendulum.jpg',0)    #Make sure you have the image
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
TheMeaningfulEngineer
  • 15,679
  • 27
  • 85
  • 143
  • container os doesn't have /home/.Xauthority or /home/user/.Xauthority but in the host side I have /home/ddd/.Xauthority – Dhiren Hamal Dec 11 '19 at 06:40