Zenn
🐳

dockerコンテナ内でcolmap guiを動作させる方法

2025/03/02に公開

TL;DR

  • Xvfb, xfce4, VNCを使ってDockerコンテナ内に仮想ディスプレイを作成し、その中でcolmap guiを動かす。
  • noVNCを使うことで、ブラウザから仮想ディスプレイへアクセスできるようにする。
  • 同様の手法で、Dockerコンテナ内でGUIが必要なソフトを動かすことができる。

起動イメージ

起動イメージ

1. はじめに

Docker上でCOLMAPをビルドし、その中でcolmap guiを動かしたいモチベーションがありました。(macでbrew install colmapが失敗したりしたため)
しかし、Dockerコンテナ内でGUIを起動するにはそのままではうまく動きません。
そこで、Xvfb, xfce4, VNCを使ってDockerコンテナ内に仮想ディスプレイを作成し、その仮想環境内でcolmap guiを起動できるようにしました。

VNCとは?

ネットワーク経由で別のコンピューターの画面を遠隔操作するための技術で、リモートデスクトップの一種です。
今回はxvfbで作成した仮想ディスプレイを、VNCで遠隔操作できるようにしています。

noVNCとは?

VNCクライアントを Webブラウザ上で動作させるためのソフトウェアです。
通常、VNC接続にはVNC Viewer等の専用クライアントが必要なのですが、これをブラウザからでもアクセスできるようにするのがnoVNCです。

2. Dockerfile等

VNCを起動する際は、GPU版,CPU版のどちらかのDockerfilestartup.shを同じディレクトリに配置し、Usageに従って実行してください。

Dockerfile (GPU版)
ARG UBUNTU_VERSION=22.04
ARG NVIDIA_CUDA_VERSION=11.8.0

#
# Docker builder stage.
#
FROM nvidia/cuda:${NVIDIA_CUDA_VERSION}-devel-ubuntu${UBUNTU_VERSION} AS builder

ARG COLMAP_GIT_COMMIT=main

ARG CUDA_ARCHITECTURES=native

ENV QT_XCB_GL_INTEGRATION=xcb_egl

# Prevent stop building ubuntu at time zone selection.
ENV DEBIAN_FRONTEND=noninteractive

# Prepare and empty machine for building.
RUN apt-get update && \
    apt-get install -y --no-install-recommends --no-install-suggests \
        git \
        cmake \
        ninja-build \
        build-essential \
        libboost-program-options-dev \
        libboost-graph-dev \
        libboost-system-dev \
        libeigen3-dev \
        libflann-dev \
        libfreeimage-dev \
        libmetis-dev \
        libgoogle-glog-dev \
        libgtest-dev \
        libgmock-dev \
        libsqlite3-dev \
        libglew-dev \
        qtbase5-dev \
        libqt5opengl5-dev \
        libcgal-dev \
        libceres-dev \
        libcurl4-openssl-dev

# Build and install COLMAP.
RUN git clone https://github.com/colmap/colmap.git
RUN cd colmap && \
    git fetch https://github.com/colmap/colmap.git ${COLMAP_GIT_COMMIT} && \
    git checkout FETCH_HEAD && \
    mkdir build && \
    cd build && \
    cmake .. -GNinja -DCMAKE_CUDA_ARCHITECTURES=${CUDA_ARCHITECTURES} \
        -DCMAKE_INSTALL_PREFIX=/colmap-install && \
    ninja install

#
# Docker runtime stage.
#
FROM nvidia/cuda:${NVIDIA_CUDA_VERSION}-runtime-ubuntu${UBUNTU_VERSION} AS runtime

# Minimal dependencies to run COLMAP binary compiled in the builder stage.
# Note: this reduces the size of the final image considerably, since all the
# build dependencies are not needed.
RUN apt-get update && \
    apt-get install -y --no-install-recommends --no-install-suggests \
        libboost-program-options1.74.0 \
        libc6 \
        libceres2 \
        libfreeimage3 \
        libgcc-s1 \
        libgl1 \
        libglew2.2 \
        libgoogle-glog0v5 \
        libqt5core5a \
        libqt5gui5 \
        libqt5widgets5 \
        libcurl4

# Copy all files from /colmap-install/ in the builder stage to /usr/local/ in
# the runtime stage. This simulates installing COLMAP in the default location
# (/usr/local/), which simplifies environment variables. It also allows the user
# of this Docker image to use it as a base image for compiling against COLMAP as
# a library. For instance, CMake will be able to find COLMAP easily with the
# command: find_package(COLMAP REQUIRED).
COPY --from=builder /colmap-install/ /usr/local/

# ------------ VNC ------------
ENV DEBIAN_FRONTEND=noninteractive
ENV DISPLAY=:0

RUN apt-get update && \
    apt-get install -y --no-install-recommends --no-install-suggests \
    # Install VNC server.
    x11vnc \
    xvfb \
    xfce4 \
    xfce4-terminal \
    # Install noVNC.
    git \
    xinit \
    dbus-x11 && \
    git clone https://github.com/novnc/noVNC.git /opt/noVNC

COPY startup.sh /root/startup.sh
RUN chmod +x /root/startup.sh

EXPOSE 6080

CMD ["/root/startup.sh"]
Dockerfile (CPU版)
ARG UBUNTU_VERSION=22.04
#
# Docker builder stage.
#
FROM ubuntu:${UBUNTU_VERSION} AS builder

ARG COLMAP_GIT_COMMIT=main
ENV QT_XCB_GL_INTEGRATION=xcb_egl

# Prevent stop building ubuntu at time zone selection.
ENV DEBIAN_FRONTEND=noninteractive

# Prepare and empty machine for building.
RUN apt-get update && \
    apt-get install -y --no-install-recommends --no-install-suggests \
        git \
        cmake \
        ninja-build \
        build-essential \
        libboost-program-options-dev \
        libboost-graph-dev \
        libboost-system-dev \
        libeigen3-dev \
        libflann-dev \
        libfreeimage-dev \
        libmetis-dev \
        libgoogle-glog-dev \
        libgtest-dev \
        libgmock-dev \
        libsqlite3-dev \
        libglew-dev \
        qtbase5-dev \
        libqt5opengl5-dev \
        libcgal-dev \
        libceres-dev \
        libcurl4-openssl-dev

RUN apt-get install -y ca-certificates gnupg

# Build and install COLMAP.
RUN git clone https://github.com/colmap/colmap.git
RUN cd colmap && \
    git fetch https://github.com/colmap/colmap.git ${COLMAP_GIT_COMMIT} && \
    git checkout FETCH_HEAD && \
    mkdir build && \
    cd build && \
    cmake .. -GNinja \
        -DCMAKE_INSTALL_PREFIX=/colmap-install && \
    ninja install

#
# Docker runtime stage.
#
FROM ubuntu:${UBUNTU_VERSION}

# Minimal dependencies to run COLMAP binary compiled in the builder stage.
# Note: this reduces the size of the final image considerably, since all the
# build dependencies are not needed.
RUN apt-get update && \
    apt-get install -y --no-install-recommends --no-install-suggests \
        libboost-program-options1.74.0 \
        libc6 \
        libceres2 \
        libfreeimage3 \
        libgcc-s1 \
        libgl1 \
        libglew2.2 \
        libgoogle-glog0v5 \
        libqt5core5a \
        libqt5gui5 \
        libqt5widgets5 \
        libcurl4

# Copy all files from /colmap-install/ in the builder stage to /usr/local/ in
# the runtime stage. This simulates installing COLMAP in the default location
# (/usr/local/), which simplifies environment variables. It also allows the user
# of this Docker image to use it as a base image for compiling against COLMAP as
# a library. For instance, CMake will be able to find COLMAP easily with the
# command: find_package(COLMAP REQUIRED).
COPY --from=builder /colmap-install/ /usr/local/

RUN apt-get install -y ca-certificates gnupg

# ------------ VNC ------------
ENV DEBIAN_FRONTEND=noninteractive
ENV DISPLAY=:0

RUN apt-get update && \
    apt-get install -y --no-install-recommends --no-install-suggests \
    # Install VNC server.
    x11vnc \
    xvfb \
    xfce4 \
    xfce4-terminal \
    # Install noVNC.
    git \
    xinit \
    dbus-x11 && \
    git clone https://github.com/novnc/noVNC.git /opt/noVNC

COPY startup.sh /root/startup.sh
RUN chmod +x /root/startup.sh

EXPOSE 6080

CMD ["/root/startup.sh"]
startup.sh
#!/bin/bash

Xvfb :0 -screen 0 1280x720x24 &

startxfce4 &

if [ -n "$VNC_PASSWORD" ]; then
    echo -n "$VNC_PASSWORD" > /.password1
    x11vnc -storepasswd $(cat /.password1) /.password2
    chmod 400 /.password*
    x11vnc -display :0 -rfbauth /.password2 -forever -usepw -shared -xkb -reopen -rfbport 5900 -bg
    export VNC_PASSWORD=
else
    # passwordless
    x11vnc -display :0 -forever -shared -nopw -xkb -reopen -rfbport 5900 -bg
fi

/opt/noVNC/utils/novnc_proxy --vnc localhost:5900 --listen 0.0.0.0:6080

(任意) docker-compose.yml
services:
  ubuntu-vnc-colmap:
    build: .
    container_name: ubuntu-vnc-colmap
    ports:
      # noVNC
      - "6080:6080"
    volumes:
      - ./:/app/data
    # VNCのパスワードを指定したい場合
    # environment:
    #   - VNC_PASSWORD=xxxx
    tty: true

エラー関連

nvcc

GPU版Dockerfileにて、colmapのビルド時に

nvcc fatal : Unsupported gpu architecture 'compute_'

というエラーが出る場合、11行目にある CUDA_ARCHITECTURESを以下のように書き換えてください。

  1. ローカル環境で
    nvidia-smi --query-gpu=compute_cap --format=csv,noheader
    
    を実行し、GPU のアーキテクチャ(例:8.9 など)を確認。
  2. その数値から「.」を除いた値を CUDA_ARCHITECTURES に指定
    • 例: GPU のアーキテクチャが 8.9 の場合、CUDA_ARCHITECTURES=89

参考:
https://github.com/colmap/colmap/issues/2464#issuecomment-2009050889

ninjaでのビルドエラー

> ninja
[1/259] Building CXX object src/colmap/controllers/CMakeFiles/colmap_controllers.dir/bundle_adjustment.cc.o
FAILED: src/colmap/controllers/CMakeFiles/colmap_controllers.dir/bundle_adjustment.cc.o
/usr/bin/c++ -DBOOST_ALL_NO_LIB -DBOOST_GRAPH_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_REGEX_DYN_LINK -DCERES_EXPORT_INTERNAL_SYMBOLS -DCOLMAP_CGAL_ENABLED -DCOLMAP_DOWNLOAD_ENABLED -DCOLMAP_GPU_ENABLED -DCOLMAP_GUI_ENABLED -DCOLMAP_LSD_ENABLED -DCOLMAP_OPENGL_ENABLED -DCOLMAP_OPENMP_ENABLED -DGFLAGS_IS_A_DLL=0 -DGLOG_VERSION_MAJOR=0 -DGLOG_VERSION_MINOR=4 -DGOOGLE_GLOG_DLL_DECL="" -DGOOGLE_GLOG_DLL_DECL_FOR_UNITTESTS="" -DPOSELIB_DEBUG=0 -DQT_CORE_LIB -DQT_GUI_LIB -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -I/app/colmap/src -I/app/colmap/build/_deps/poselib-src -I/app/colmap/build/_deps/poselib-build/generated_headers -isystem /usr/include/eigen3 -isystem /usr/include/aarch64-linux-gnu/qt5 -isystem /usr/include/aarch64-linux-gnu/qt5/QtCore -isystem /usr/lib/aarch64-linux-gnu/qt5/mkspecs/linux-g++ -isystem /usr/include/aarch64-linux-gnu/qt5/QtOpenGL -isystem /usr/include/aarch64-linux-gnu/qt5/QtWidgets -isystem /usr/include/aarch64-linux-gnu/qt5/QtGui -Wno-maybe-uninitialized -Wall -O3 -DNDEBUG -fPIC -fopenmp -std=gnu++17 -MD -MT src/colmap/controllers/CMakeFiles/colmap_controllers.dir/bundle_adjustment.cc.o -MF src/colmap/controllers/CMakeFiles/colmap_controllers.dir/bundle_adjustment.cc.o.d -o src/colmap/controllers/CMakeFiles/colmap_controllers.dir/bundle_adjustment.cc.o -c /app/colmap/src/colmap/controllers/bundle_adjustment.cc
c++: fatal error: Killed signal terminated program cc1plus

ホスト側の性能が足りていないと、このエラーが出やすいです。
よほどのスペック不足でない限り、ninja installninja install -j1に変更して、ビルドの並列処理を無効にすることで解決すると思います。

参考:
https://github.com/colmap/colmap/issues/923

3. Usage

docker-composeを使用する場合

docker-compose up --build -d

を実行した後、ブラウザで http://localhost:6080/vnc.html にアクセスすると、noVNCが開きます。
その後、noVNC内のターミナルで

colmap gui

と打つとCOMAPのGUIが立ち上がります。

4. 最後に

COLMAP以外でも、Dockerコンテナ内でGUIが必要なソフトを動かしたい場面は多いと思います。
同様の手法でXvfb + xfce4 + x11vnc + noVNCを使うことで、手軽にブラウザ経由のリモートGUIが実現できるので、どなたかの参考になれば幸いです。

おまけ. Dockerfile, startup.shでやっていること

Dockerfileの # ------------ VNC ------------までは、COMLAP公式が公開しているマルチステージビルドをベースにしています。
https://github.com/colmap/colmap/blob/047b67756e0ddc23f6c7fd26eb5d843a7921bedc/docker/Dockerfile

ここでは、VNC周りの解説を中心に取り上げます。

Dockerfileでやってること

全体像

ENV DEBIAN_FRONTEND=noninteractive
ENV DISPLAY=:0

RUN apt-get update && \
    apt-get install -y --no-install-recommends --no-install-suggests \
    # Install VNC server.
    x11vnc \
    xvfb \
    xfce4 \
    xfce4-terminal \
    # Install noVNC.
    git \
    xinit \
    dbus-x11 && \
    git clone https://github.com/novnc/noVNC.git /opt/noVNC

COPY startup.sh /root/startup.sh
RUN chmod +x /root/startup.sh

EXPOSE 6080

CMD ["/root/startup.sh"]

詳細

ENV DEBIAN_FRONTEND=noninteractive

これをしないと、apt-get installする際にtimezoneなどをインタラクティブに設定する必要があり、入力待ち状態になってビルドが途中で止まります。

ENV DISPLAY=:0

xvfbで生成する仮想ディスプレイの番号を指定。

RUN apt-get update && \
    apt-get install -y --no-install-recommends --no-install-suggests \
    # Install VNC server.
    x11vnc \
    xvfb \
    xfce4 \
    xfce4-terminal \
    # Install noVNC.
    git \
    xinit \
    dbus-x11 && \
    git clone https://github.com/novnc/noVNC.git /opt/noVNC

VNCサーバーやnoVNCの依存関係をインストール。

startup.shでやってること

  • xvfbで仮想ディスプレイを作成
  • xfce4でデスクトップ環境を準備
  • VNC/noVNCでGUIに外部からアクセスできるように設定

全体像

#!/bin/bash

Xvfb :0 -screen 0 1280x720x24 &

startxfce4 &

if [ -n "$VNC_PASSWORD" ]; then
    echo -n "$VNC_PASSWORD" > /.password1
    x11vnc -storepasswd $(cat /.password1) /.password2
    chmod 400 /.password*
    x11vnc -display :0 -rfbauth /.password2 -forever -usepw -shared -xkb -reopen -rfbport 5900 -bg
    export VNC_PASSWORD=
else
    # passwordless
    x11vnc -display :0 -forever -shared -nopw -xkb -reopen -rfbport 5900 -bg
fi

/opt/noVNC/utils/novnc_proxy --vnc localhost:5900 --listen 0.0.0.0:6080

詳細

Xvfb :0 -screen 0 1280x720x24 &

Xvfbを起動し、仮想ディスプレイ番号0番で、サイズが1280x720、24bitカラーのディスプレイを仮想的に作成。

startxfce4 &

軽量デスクトップ環境のxfce4を起動。

if [ -n "$VNC_PASSWORD" ]; then
    echo -n "$VNC_PASSWORD" > /.password1
    x11vnc -storepasswd $(cat /.password1) /.password2
    chmod 400 /.password*
    x11vnc -display :0 -rfbauth /.password2 -forever -usepw -shared -xkb -reopen -rfbport 5900 -bg
    export VNC_PASSWORD=
else
    # passwordless
    x11vnc -display :0 -forever -shared -nopw -xkb -reopen -rfbport 5900 -bg
fi

x11vncを起動し、VNCの有無やパスワード設定を行う。

x11vncのオプションの詳細は下記マニュアルを参照してください。
https://linux.die.net/man/1/x11vnc

/opt/noVNC/utils/novnc_proxy --vnc localhost:5900 --listen 0.0.0.0:6080

noVNCを起動。

ポート5900で動作するVNCを、6080番から接続できるようにしています。

Discussion

ログインするとコメントできます