複数バージョンのOpenCVを共存させたい on macOS

# 動機
コンピュータビジョン関連の開発をしたり，ライブラリを使っていると，OpenCVの2系と3系を切り替えたいときがある．(特にPython)

そこで，

- virtualenvでOpenCVのバージョンを使い分けたいけど，どこの`cv2.so`からシンボリックリンクを作ればいいのか.
- pyenvしか使っていないときにOpenCVのバージョンを使い分けたいとき，PYTHONPATHには何を設定すればいいのか．
- g++でOpenCVを使ったプログラムをビルドする際は，cmakeするときのパスをどこに設定すればいいのか．
- 簡単にOpenCV2系とOpenCV3系を切り替える方法．

をメモしておく．

`brew unlink & brew link`で管理していたら事故を起こして不具合が出たので，別の方法を考案．
Linuxでもパスを適切に設定すれば，この方法は使えるはず．

# 準備
HomebrewでOpenCVの2系と3系をインストールする

```bash:Terminal
# まずインストールする
# Python2系に切り替えておく
$ brew update
$ brew tap homebrew/science
$ brew install cmake pkg-config jpeg libpng libtiff eigen openexr numpy
$ brew install tbb
$ brew install ffmpeg

# OpenCV2系のインストール
# Python2系バインドが一緒に入るはず
$ brew install opencv --with-tbb --with-ffmpeg —-with-vtk

# OpenCV3系のインストール
# Python2系バインドを入れる場合
$ brew install opencv3 —-with-contrib --with-ffmpeg --with-tbb —with-vtk
# Python3系バインドを入れる場合　(Python3に切り替えてから実行すること)
$ brew install opencv3 —-with-contrib --with-ffmpeg --with-python3 --with-tbb —with-vtk --without-python
```

必要であれば`unlink`しておく．

```bash:Terminal
$ brew unlink opencv opencv3
```

# パスの設定
`unlink`をしてあっても，パスを通せばコンパイル・リンク時に参照できる．

## cmakeするときのオプション

- `-D`オプションで`OpenCV_DIR`を設定 (`OpenCVConfig.cmake`があるディレクトリ)
    - OpenCV2 : `-DOpenCV_DIR=/usr/local/opt/opencv/share/OpenCV/`
    - OpenCV3 : `-DOpenCV_DIR=/usr/local/opt/opencv3/share/OpenCV/`

`OpenCV_DIR`が設定されている状態で`find_package(OpenCV REQUIRED)`をすると，`OpenCV_INCLUDE_DIRS`と`OpenCV_LIBS`が設定されるので，

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 2.8)

project(Sample)

add_executable(Project main.cpp)

find_package(OpenCV REQUIRED)

if(OpenCV_FOUND)
  include_directories(${OpenCV_INCLUDE_DIRS})
  target_link_libraries(Project ${OpenCV_LIBS})
endif(OpenCV_FOUND)
```

の`cmake`が通るようになる．

## `cv2.so`のシンボリックリンク元

OpenCV3のPython2バインドをHomebrewでインストール，OpenCV3のPython3バインドは自前ビルドしていると仮定する．
OpenCVモジュールを使いたいPythonのインストールパス以下のディレクトリ，`/path/to/python/lib/python2.7/site-packages`に，以下の`cv2.so`からシンボリックリンクを貼る．
(OpenCV, Pythonのバージョンは適宜変更する)

- OpenCV2 for Python2
    - `/usr/local/Cellar/opencv/2.4.13.2/lib/python2.7/site-packages/cv2.so`
    - または `/usr/local/opt/opencv/lib/python2.7/site-packages/cv2.so`
- OpenCV3 for Python2
    - `/usr/local/Cellar/opencv3/3.2.0/lib/python2.7/site-packages/cv2.so`
    - または `/usr/local/opt/opencv3/lib/python2.7/site-packages/cv2.so`
- OpenCV3 for Python3
    - `/path/to/own-built-opencv3/lib/python3.5/site-packages/cv2.so`

## 環境変数でシステムの設定自体をまとめて変える

### g++関連の環境変数
- `OpenCV_DIR`
    - OpenCV2 : `/usr/local/opt/opencv/share/OpenCV/`
    - OpenCV3 : `/usr/local/opt/opencv3/share/OpenCV/`
- `CPLUS_INCLUDE_PATH`
    - OpenCV2 : `/usr/local/opt/opencv/include/:$CPLUS_INCLUDE_PATH`
    - OpenCV3 : `/usr/local/opt/opencv3/include/:$CPLUS_INCLUDE_PATH`
- `LIBRARY_PATH`
    - OpenCV2 : `/usr/local/opt/opencv/lib/:$LIBRARY_PATH`
    - OpenCV3 : `/usr/local/opt/opencv3/lib/:$LIBRARY_PATH`
- `PATH`
    - OpenCV2 : `/usr/local/opt/opencv/bin:$PATH`
    - OpenCV3 : `/usr/local/opt/opencv3/bin:$PATH`

### Python関連の環境変数

OpenCV3のPython2バインドをHomebrewでインストール，OpenCV3のPython3バインドは自前ビルドしていると仮定する．
`site-packages`には`cv2.so`が存在するはず．
(`/usr/local/opt/opencv`や`/usr/local/opt/opencv3`に同様のディレクトリがあればそちらでもOKかもしれない)

- `PYTHONPATH`
    - OpenCV2 for Python2 : `/usr/local/Cellar/opencv/2.4.13.2/lib/python2.7/site-packages/:$PYTHONPATH`
    - OpenCV3 for Python2 : `/usr/local/Cellar/opencv3/3.2.0/lib/python2.7/site-packages/:$PYTHONPATH`
    - OpenCV3 for Python3 : `/path/to/own-built-opencv3/lib/python3.5/site-packages:$PYTHONPATH`
        - 自前でOpenCV3のPython3バインドをビルドし，ホームディレクトリ以下などの適当な場所に置いておく

`OpenCV_DIR`もOpenCVのバージョンに合わせて正しく設定しておいたほうがよい．

# 実例
システムの設定ごと変えるために`~/.bash_profile`に以下のようなスクリプトを書いた
(メッセージが邪魔なら`echo`の行を消す)

```bash:~/.bash_profile
# OpenCV version setting
OpenCV_VERSION="3.2.0"
export OpenCV_DIR=""
if [ "$OpenCV_VERSION" = "2.4.13.2" ]; then
  # OpenCV 2.4.13.2
  echo "OpenCV 2.4.13.2"
  export OpenCV_DIR="/usr/local/opt/opencv/share/OpenCV/"
  export CPLUS_INCLUDE_PATH="/usr/local/opt/opencv/include/:$CPLUS_INCLUDE_PATH"
  export LIBRARY_PATH="/usr/local/opt/opencv/lib/:$LIBRARY_PATH"
  export PATH="/usr/local/opt/opencv/bin:$PATH"
  # Python bindings (only Python 2)
  export PYTHONPATH="/usr/local/Cellar/opencv/2.4.13.2/lib/python2.7/site-packages/:$PYTHONPATH"
elif [ "$OpenCV_VERSION" = "3.2.0" ]; then
  # OpenCV 3.2.0
  echo "OpenCV 3.2.0"
  export OpenCV_DIR="/usr/local/opt/opencv3/share/OpenCV/"
  export CPLUS_INCLUDE_PATH="/usr/local/opt/opencv3/include/:$CPLUS_INCLUDE_PATH"
  export LIBRARY_PATH="/usr/local/opt/opencv3/lib/:$LIBRARY_PATH"
  export PATH="/usr/local/opt/opencv3/bin:$PATH"
  # Python binding (Python 2 or Python 3)
  PYTHON_VERSION="2"
  if [ "$PYTHON_VERSION" = "2" ]; then
    export PYTHONPATH="/usr/local/Cellar/opencv3/3.2.0/lib/python2.7/site-packages/:$PYTHONPATH"
  elif [ "$PYTHON_VERSION" = "3" ]; then
    # Own-built library
    export PYTHONPATH="/Users/username/usr/lib/python3.5/site-packages:$PYTHONPATH"
  else
    echo "Invalid Python version"
  fi
else
  echo "Invalid OpenCV version"
fi
```

`OpenCV_VERSION`と`PYTHON_VERSION`を切り替えて再読み込みするとOpenCVのバージョンが切り替えられる．

```bash:Terminal
$ source ~/.bash_profile
OpenCV 3.2.0
$ 
```

# 切り替えられるか確認
## g++
以下のC++コードをコンパイルする．

```cpp:main.cc
#include <iostream>
#include <opencv2/core.hpp>

int main()
{
  std::cout << "version: " << CV_VERSION << std::endl;
}
```

```bash:Terminal
# OpenCV_VERSION="3.2.0"のとき
$ g++ main.cc -o main
$ ./main
version: 3.2.0

# OpenCV_VERSION="2.4.13.2"のとき
$ g++ main.cc -o main
$ ./main
version: 2.4.13.2
```

## Python

`import cv2`して，`cv2.__version__`と`cv2.__file__`を確認する．

```bash:Terminal
# OpenCV_VERSION="3.2.0"のとき
$ python
Python 2.7.13 |Anaconda custom (x86_64)| (default, Dec 20 2016, 23:05:08) 
>>> import cv2
>>> cv2.__version__
'3.2.0'
>>> cv2.__file__
'/usr/local/Cellar/opencv3/3.2.0/lib/python2.7/site-packages/cv2.so'

# OpenCV_VERSION="2.4.13.2"のとき
$ python
Python 2.7.13 |Anaconda custom (x86_64)| (default, Dec 20 2016, 23:05:08) 
>>> import cv2
>>> cv2.__version__
'2.4.13.2'
>>> cv2.__file__
'/usr/local/Cellar/opencv/2.4.13.2/lib/python2.7/site-packages/cv2.so'
```

# おわりに
頑張れば with CUDA/with non-CUDA も切り替えられそう？