📷

pyenvのPythonと組み合わせるOpenCVのビルド: Ubuntu-22.04編

2023/05/28に公開

はじめに

OpenCVのビルドは依存ライブラリの準備など色々面倒ですが、Pythonから使えるようにビルドするのはさらに大変です。また、自前でビルドしたものを使うときは、既存の環境に影響を与えないよう/usr/local/libなどの標準のインストール先でない場所に入れたいものです。そこでここでは以下の環境でOpenCVをビルドする方法を説明します。

  • OS: Ubuntu-22.04 (WSL2上でもよい)
  • OpenCV: 4.7.0 + conrtib
  • Python: 3.11.3 (pyenvでインストールしたもの)
  • インストール先: $HOME/dev-root/opencv4

なお今回CUDAは考慮していませんが、そのうちやりたいと思います。

手順

概要

以下の順に作業を進めます。

  • pyenvに必要なパッケージのインストール
  • pyenvのインストール
  • Pythonのビルド・インストール
  • OpenCVに必要なパッケージのインストール
  • OpenCV + contribのソース取得
  • OpenCVのビルド・インストール
  • C++サンプルコードの動作確認
  • Pythonサンプルコードの動作確認

pyenvに必要なパッケージのインストール

pyenvに必要なパッケージは公式サイトに載っていて、以下コマンドでインストールできます。

$ sudo apt update; sudo apt install build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev curl \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev

pyenvのインストール

pyenvのインストールも公式サイトの説明どおりに行います。

$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv

あとは以下コマンドで.bashrcに設定を追加します。

$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
$ echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(pyenv init -)"' >> ~/.bashrc

これで再ログイン、端末を再起動などさせれば準備完了。

Pythonのビルド・インストール

pyenvでのPythonのインストールはpyenv install {version}でできますが、デフォルトでは共有ライブラリが作られず、OpenCVと組み合わせることができません。共有ライブラリを作るには以下のように--enable-sharedを指定します。

$ CONFIGURE_OPTS="--enable-shared" pyenv install 3.11.3

ビルド・インストールができたらpyenv global 3.11.3等で有効にしておきます。

OpenCVに必要なパッケージのインストール

OpenCVのビルドに必要なツール・依存ライブラリをaptでインストールします。オプショナルのものも多いので全部が必須なわけではないですが、できるだけ多くの機能が使えるようにしておきます。JavaやJuliaなどPython以外の言語との組み合わせは今回は対象外です。あと、私が作っているRubyバインディングであるopencvrはOpenCV本体インストール後にビルドして使うものなので、ここでは考慮しなくてよいです。

  • libbz2-dev
  • libsqlite3-dev
  • cmake
  • libgtk-3-dev
  • libavcodec-dev
  • libavformat-dev
  • libavutil-dev
  • libswscale-dev
  • libgstreamer1.0-dev
  • libeigen3-dev
  • liblapack-dev
  • doxygen

また、先ほどインストールしたPythonのpipコマンドで以下をインストールします。

$ pip install numpy ninja
  • numpy: Pythonと組み合わせるのに必要
  • ninja: ビルドシステム。OpenCVのビルド時に使う。apt install ninjaでもインストールできるがせっかくPythonを独自に入れたのでそちらを使って入れる。Makeを使うようにすることもできるがここではNinjaを使う。

OpenCV + contribのソース取得

ソースコードはどこに置いてもよいですが、ここでは$HOME/srcs以下に置き、ビルドに使う一時ディレクトリもここに置くことにします。

$HOME/srcs/
        +- opencv/             # OpenCV本体ソースツリー
	+- opencv_contrib/     # OpenCV Contribソースツリー
	+- build-opencv/       # ビルドディレクトリ (cmakeが生成する)
$ cd ~
$ mkdir srcs; cd srcs
$ git clone https://github.com/opencv/opencv.git
$ git clone https://github.com/opencv/opencv_contrib.git

また、OpenCV本体とContrib両方に対してビルドしたいバージョンをチェックアウトしておきます。同じバージョンになっていないとビルドに失敗します。

$ cd $HOME/srcs/opencv
$ git checkout 4.7.0
$ cd ../opencv_contrib
$ git checkout 4.7.0
$ cd ..

OpenCVのビルド・インストール

CMakeを知らない人のために簡単に説明しておくと、CMakeそれ自身はビルドツールではなく、他のビルドシステム(Make, Ninjaなど)の設定ファイルを生成するものです。ここではNinjaを使うので、cmakeコマンドはNinjaの設定ファイルであるbuild.ninjaを生成します。その後ninjaコマンドで実際のビルドを行います。なおCMakeのようなツールのことをメタビルドシステムと呼びます。

おおまかに言って実際のビルドのコマンドは以下のようになります。-Sでソースツリーのディレクトリ、-Bでビルドディレクトリを指定します。この中にbuild.ninjaも生成されます。各種オプションについてはこの後説明します。

$ cd $HOME/srcs
$ cmake -S opencv -B build-opencv {各種オプション}
$ ninja -C build-opencv
$ ninja -C build-opencv doxygen
$ ninja -C build-opencv install

CMakeのオプション

かなり長くなるためコマンドを手で打つのは大変なので、スクリプトにしたものを後述します。まずは基本的なオプション。

オプション 説明
-G Ninja 使用するビルドシステム。ここではNinjaを指定する。
-DCMAKE_BUILD_TYPE=Debug デバッグ版ビルドにする。リリース版にしたければReleaseを指定する。
-DCMAKE_INSTALL_PREFIX={path} インストール先。ここでは$HOME/dev-rootopencv4`にする。
-DOPENCV_GENERATE_PKGCONFIG=YES pkg-config用の.pcファイルを生成する。C++のサンプルコードビルド時に便利なので指定する。
-DCMAKE_EXPORT_COMPILE_COMMANDS=YES compile_commands.jsonを生成する。OpenCV自体の開発をするときにあると便利だが、利用するだけなら必要ない。
-DOPENCV_EXTRA_MODULES_PATH=./opencv_contrib/modules Contribも合わせてビルドするときに指定する。指定するのはcmakeを実行するときのカレントディレクトリから見たmodulesディレクトリの場所。
-DWITH_QUIRC QRコードを読み取るQRCodeDetectorを使えるようにする。
-DBUILD_DOCS ドキュメントを生成する。
-Dbuild_opencv_python3=YES Pythonモジュールを生成する。

これらに加えてpyenvのPythonと組み合わせるためのオプションが必要です。

-DPYTHON3_EXECUTABLE={path}

Python3実行ファイルのパスを指定します。pyenv which python3で取得できます。

-DPYTHON3_INCLUDE_DIR={path}

pyenvでインストールしたPythonのインクルードディレクトリを指定します。python3 -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())"で取得できます。結果は/home/xxx/.pyenv/versions/3.11.3/include/python3.11などのようになるはずです。

-DPYTHON3_LIBRARY={path}

pyenvでインストールしたPythonのライブラリディレクトリを指定します。python -c "import distutils.sysconfig as sysconfig; print(sysconfig.get_config_var('LIBDIR') + '/' + sysconfig.get_config_var('LDLIBRARY'))"で取得できます。

-DPYTHON3_NUMPY_INCLUDE_DIRS={path}

NumPyのインクルードディレクトリを指定します。python -c "import os, numpy.distutils; print(os.pathsep.join(numpy.distutils.misc_util.get_numpy_include_dirs()))"で取得できます。

なお最近のバージョンのNumPyだと上記コマンド実行時にdistutilsがdeprecatedになったという警告が出ますが、今のところまだ使えているのでこのまま使います。将来のバージョンで削除されたらまた考えることにします。

cmake実行スクリプト

cmake各種オプション設定・実行をひとまとめにしたスクリプトを以下に示します。このファイルをrun-cmake.shのような名前で$HOME/srcs以下に置きます。

run-cmake.sh
#!/bin/bash -e

set -e

build_dir="build-opencv"

# Set up cmake options
cmake_opts=""
cmake_opts="$cmake_opts -G Ninja"
cmake_opts="$cmake_opts -DCMAKE_BUILD_TYPE=Debug"
cmake_opts="$cmake_opts -DCMAKE_INSTALL_PREFIX=$HOME/dev-root/opencv4"
cmake_opts="$cmake_opts -DOPENCV_GENERATE_PKGCONFIG=YES"
cmake_opts="$cmake_opts -DCMAKE_EXPORT_COMPILE_COMMANDS=YES"
cmake_opts="$cmake_opts -DOPENCV_EXTRA_MODULES_PATH=./opencv_contrib/modules"
cmake_opts="$cmake_opts -DWITH_QUIRC=YES"
cmake_opts="$cmake_opts -DBUILD_DOCS=YES"
cmake_opts="$cmake_opts -Dbuild_opencv_python3=YES"

python3_executable=$(pyenv which python3)
python3_include_dir=$(python3 -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())")
python3_numpy_include_dirs=$(python3 -c "import os, numpy.distutils; print(os.pathsep.join(numpy.distutils.misc_util.get_numpy_include_dirs()))")
python3_library=$(python3 -c "import distutils.sysconfig as sysconfig; print(sysconfig.get_config_var('LIBDIR') + '/' + sysconfig.get_config_var('LDLIBRARY'))")

cmake_opts="$cmake_opts -DPYTHON3_EXECUTABLE=$python3_executable"
cmake_opts="$cmake_opts -DPYTHON3_INCLUDE_DIR=$python3_include_dir"
cmake_opts="$cmake_opts -DPYTHON3_NUMPY_INCLUDE_DIRS=$python3_numpy_include_dirs"
cmake_opts="$cmake_opts -DPYTHON3_LIBRARY=$python3_library"

# Execute cmake
set -x
cmake -S opencv -B $build_dir $cmake_opts 2>&1 | tee cmake-log.txt

スクリプト実行後、どの機能が有効かなどが表示されるので確認しましょう。cmakeのログはcmake-log.txtに保存されるようにしています。エラーになっていたらログ確認の上で修正し、build-opencvを削除してから再実行してください。

また、ここではPython3のパスしか指定していないので「Python2が見つからない」という旨の警告が出ますが、さすがにもうPython2は使わないので無視しましょう。

Ninjaによるビルド

以下コマンドでビルドとインストールをします。古いPCの場合は1時間以上かかります。

$ ninja -C build-opencv
$ ninja -C build-opencv doxygen
$ ninja -C build-opencv install

インストール後、$HOME/dev-root/opencv4ができていることを確認しましょう。これでOpenCVのビルドは完了です。

C++サンプルコードの動作確認

OpenCVで画像読んで加工するようなサンプルはあちこちにあるので、ここでは単にバージョンを表示するコードで動作確認します。以下のコードをcpp-test.cppという名前で保存します。

cpp-test.cpp
#include <opencv2/opencv.hpp>
#include <cstdio>

int main(){
    printf("OpenCV version: %s\n", CV_VERSION);
}

ビルドするにはいくつか設定が必要なのでシェルスクリプトにしておきます。

build.sh
#!/bin/bash

export PKG_CONFIG_PATH="$HOME/dev-root/opencv4/lib/pkgconfig"
cflags=`pkg-config --cflags opencv4`
libs=`pkg-config --libs opencv4`
set -x
g++ -o cpp-test cpp-test.cpp $cflags $libs -Wl,-rpath,$HOME/dev-root/opencv4/lib

まず、ビルドするにはOpenCVのヘッダファイル・ライブラリの場所を指定する必要があります。pkg-configコマンドを使うと必要なオプションを表示してくれるのですが、今回は$HOME/dev-root/opencv4という標準外の場所にインストールしたため、そのままではpkg-configが見つけてくれません。そこで環境変数PKG_CONFIG_PATHを指定して実行します。

また、標準外の場所にインストールした場合は実行時にもライブラリの場所が分かるようになっている必要があります。このときよく使われるのが環境変数LD_LIBRARY_PATHですが、毎回打つのも面倒です。そこで今回は-Wl,-rpathで実行ファイル内にパス情報を埋め込んでおきます。

以下のように"4.7.0"が表示されればOKです。

$ ./build.sh
$ ./cpp-test
OpenCV version: 4.7.0

Pythonサンプルコードの動作確認

Pythonも同様のサンプルです。こちらもやはり拡張ライブラリのパスの設定が必要です。方法はいくつかありますが、ここではsys.path.insert()で探索パスの先頭に入れています。sys.path.append()だとリストの最後に入るので、opencv-pythonをpipなどですでにインストール済みの場合にそちらが使われてしまいます。sys.pathを変更するのは良くない方法と言われることもありますが、開発目的ならこれでよいでしょう。

以下を実行して"4.7.0"が表示されればOKです。

#!/usr/bin/env python

import os
import sys
sys.path.insert(0, f"{os.environ['HOME']}/dev-root/opencv4/lib/python3.11/site-packages")
import cv2

print(cv2.__version__)

Discussion