OpenCVを使ったC++コードをコンパイルする（CMake, GCC, pkg-config）

(修正)
CMakeLists.txt中の変数`SOURCE_CODE`が`SOURCE_CORDE`になっていました。
以前のままでも問題なく動きますが一応訂正しておきます。

(追記 2020/02/18)
[2.3 pkg-configでg++にインクルードパスとライブラリパスを渡す](https://qiita.com/kekenonono/items/0fcf042bca2d3d17867a#23-pkg-config%E3%81%A7g%E3%81%AB%E3%82%A4%E3%83%B3%E3%82%AF%E3%83%AB%E3%83%BC%E3%83%89%E3%83%91%E3%82%B9%E3%81%A8%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%83%91%E3%82%B9%E3%82%92%E6%B8%A1%E3%81%99) の最後に行うテストファイル実行時、
"error while loading shared libraries: libopencv_core.so.\*.*: cannot open shared object file: No such file or directory"
とエラー出てしまう場合があるらしいので[2.4](https://qiita.com/kekenonono/items/0fcf042bca2d3d17867a#24-%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E5%87%BA%E3%82%8B%E5%A0%B4%E5%90%88)に対処法を載せました。


# はじめに
[(CMake)WSLに公式OpenCVをやさしくインストール (C/C++, Python2, Python3)](https://qiita.com/kekenonono/items/031a3b41d6adb4c3e876)の続きです。
OpenCVのバージョンを出力するC++のソースコードをコンパイルします。
方法としては

**1. CMakeを使ってMakefileを作る**
**2. pkg-configを使ってパスを通す**

を説明します。

**前提**
1. WSLでUbuntuを入れてある
2. CMakeとpkg-configをインストールしてある
3. [OpenCVをCMakeでインストール](https://qiita.com/kekenonono/items/031a3b41d6adb4c3e876)している

これ以降エディタとしてGNU nanoを使用しますが各自お好みのものを使用してください。（GNU nano は `editor` コマンドで呼び出します）


##準備

**テストコード（opencv_test）の作成**

`~/opencv`直下に`cpp_version_test`というディレクトリを作成します。

```bash
$ mkdir cpp_version_test
$ cd cpp_version_test # 今cpp_version_testにいる
```


GNU nano でOpenCVのバージョンを出力するコード(opencv_test)を作成します。

```bash
$ editor opencv_test.cpp # 今もcpp_version_testにいる
```



GNU nanoが開かれたら以下のコードを貼り付けてください。
:warning:**注意：  GNU nanoでは貼り付けを`^U`（Ctrl + u）で行いますが、これはUbuntu上のバッファ領域にあるデータを貼り付けるものなのでWindowsでコピーしたデータには適用されません。
今まで通り右クリックで貼り付けてください。**

```opencv_test.cpp
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html

#include <opencv2/core/utility.hpp>
#include <iostream>

static const std::string keys = "{ b build | | print complete build info }"
                                "{ h help  | | print this help           }";

int main(int argc, const char* argv[])
{
    cv::CommandLineParser parser(argc, argv, keys);
    parser.about("This sample outputs OpenCV version and build configuration.");
    if (parser.has("help"))
    {
        parser.printMessage();
    }
    else if (!parser.check())
    {
        parser.printErrors();
    }
    else if (parser.has("build"))
    {
        std::cout << cv::getBuildInformation() << std::endl;
    }
    else
    {
        std::cout << "Welcome to OpenCV " << CV_VERSION << std::endl;
    }
    return 0;
}
```

貼り付けたら`^X`（Ctrl + x）で終了して、`y`（はい）→ `Enter`の順に打ち込んでください。


##1 CMakeを使う方法
公式ページ（[Using OpenCV with gcc and CMake](https://docs.opencv.org/4.1.2/db/df5/tutorial_linux_gcc_cmake.html)）を参考に進めていきます。

###1.1 CMakeLists.txtの作成
CMakeLists.txtを作成します。
準備と同様にGNU nanoを使っていきます。

```bash
$ editor CMakeLists.txt # 今cpp_version_testにいる
```

以下のコードを貼り付けて、保存してください。

```CMakeLists.txt
# 変数SOURCE_CORDEを宣言し、opencv_testという値を入れる。
# cmake -D SOURCE_CODE=(ソース名)で上書き可
set(SOURCE_CODE opencv_test CACHE NAME "Target object name")

# CMakeの最低バージョンを記述
cmake_minimum_required(VERSION 2.8)

# ソリューション名を指定
project( ${SOURCE_CODE} )

# OpenCVのパッケージを探す
find_package( OpenCV REQUIRED )

#ヘッダファイルのパスを指定
include_directories( ${OpenCV_INCLUDE_DIRS} )

# 実行ファイル名とソース指定（ここではソースと同じ名前の実行ファイルを作ります）
add_executable( ${SOURCE_CODE} ${SOURCE_CODE}.cpp )

#リンク先のライブラリを指定
target_link_libraries( ${SOURCE_CODE} ${OpenCV_LIBS} )
```

これで準備完了です！


###1.2 ビルドして実行
ここからはopencvをインストールしたときと同じ手順です。
まず、buildディレクトリを作成します。

```bash
$ mkdir build
$ cd build # buildに移動
```

つぎに、cmakeでmakefileを作成します。
もちろんtwo dots `..` を忘れずに。

```bash
$ cmake .. # 今buildにいる
```


これでbuildディレクトリ直下にmakefileが作成されたので`make`します。

```bash
$ make # 今もbuildにいる
```

実行ファイルが生成されたので実行します！

```bash
$ ./opencv_test # 今もbuildにいる
```

`Welcome to OpenCV (version)`と表示されれば動作確認完了です！

####opencv_test以外の名前のコードをコンパイルしたい
基本的に今回作成したCMakeLists.txtを利用すればできます。
1. 上記と同様の手順でソースと同じディレクトリにCMakeLists.txtを作成し、buildディレクトリに移動
2. `cmake -D SOURCE_CODE=(コード名(拡張子なし)) ..`と実行すれば、ターゲットが指定したコードに代わります。
3. あとはmakeビルドして実行してください。



##2. pkg-configでパスを通す方法
###2.1 pkg-configとは
そもそもpkg-configとは何なんでしょうか。

Wikipedia（[pkg-config](https://ja.wikipedia.org/wiki/Pkg-config)）にはこうあります。
>pkg-configは、環境変数PKG_CONFIG_PATHのパスに存在する *.pc ファイルに記録された情報を元に、ビルドの際に必要な文字列を返す。

ビルドの際に必要な文字列とは、今回でいうopencvのヘッダファイル`#include <opencv2/core/utility.hpp>`や、その実行に必要なライブラリのパスを示しています。

さらに、この\*.pcファイルですが、実は[**3.2**](https://qiita.com/kekenonono/items/031a3b41d6adb4c3e876#32-cmake%E3%81%AE%E5%AE%9F%E8%A1%8C)のCMake実行時のオプション指定で生成するように設定してあります。
公式ページ([Installation in Linux](https://docs.opencv.org/4.1.2/d7/d9f/tutorial_linux_install.html)) にもこうあります。
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521871/5b289932-7cdf-9ded-086b-5bf8c6337fb9.png)

つまり`-DOPENCV_GENERATE_PKGCONFIG=ON`を設定していればCMakeを使わない場合でもコンパイルできるということですね。

###2.2 pkg-configを使うには
もう一度Wikipedia（[pkg-config](https://ja.wikipedia.org/wiki/Pkg-config)）を見てみましょう。
>pkg-configは、環境変数PKG_CONFIG_PATHのパスに存在する *.pc ファイルに記録された情報を元に、ビルドの際に必要な文字列を返す。

pkg-configを使うには\*.pcファイルが存在するパスをPKG_CONFIG_PATHに教えてあげないといけないんですね。

OpenCVインストール時に`/usr/local/lib/pkgconfig`に`opencv4.pc`というファイルが作成されているのでこれをPKG_CONFIG_PATHに与えます。
以下のコマンドを実行してください。

```bash
$ export PKG_CONFIG_PATH==/usr/local/lib/pkgconfig
```

これでpkg-configを使えます。

:warning: **注意: `export`で値を追加した環境変数はターミナルを閉じるまでは値を保持し続けます。
逆に言えば、ターミナルを開くたびに上記の`export (以下省略)`を実行しなければいけません。**


####いちいちパスを通したくないよ
いちいちパスを通すのが面倒だという人は`~/.basrc`にさきほどのコマンドを追記しておきましょう。
（`.bashrc`は記述されたコマンドをターミナル起動時に自動で実行しておいてくれる設定ファイルです）

以下のコマンドを実行するか

```bash
$ echo export PKG_CONFIG_PATH==/usr/local/lib/pkgconfig >> ~/.bashrc
```

または、エディタを使って`~/.bashrc`の一番下に
`export PKG_CONFIG_PATH==/usr/local/lib/pkgconfig`をコピペしてください。
[例]

```bash
$ editor ~/.bashrc #GNU nanoで.bashrcを開く
```

一番下の行に`export PKG_CONFIG_PATH==/usr/local/lib/pkgconfig`をコピペして保存すれば、次回からUbuntuを起動するときに自動で実行されます。

###2.3 pkg-configでg++にインクルードパスとライブラリパスを渡す
さて、そろそろコンパイルの時間です。
まず、以下のコマンドを実行してopencvのインクルードパスを見てみましょう

```bash
$ pkg-config --cflags opencv4
```

おそらく`-I/usr/local/include/opencv4/opencv`や`-I/usr/local/include/opencv4`などと出てきたのではないでしょうか。
この`-I`はg++にインクルードパスを渡すオプションです。 

つぎに、以下のコマンドを実行してopencvのライブラリパスを見てみましょう

```bash
$ pkg-config --libs opencv4
```

おそらく`-L/usr/local/lib`とその横に`-l`から始まる文字列がたくさん出てきたのではないでしょうか。
この`-L`と`-l`はそれぞれ、g++にライブラリパスを渡すオプションと、使用するライブラリ指定するオプションです。


g++でコンパイルするときは上記２つの実行結果をg++に渡さなければいけません。しかし、これはかなり面倒くさいとわかると思います。

そこで、例えばopencv_test.cppをコンパイルするときは以下のように実行します

```bash
$ g++ opencv_test.cpp `pkg-config --cflags --libs opencv4` -o opencv_test
```

シングルクォート`` ` ``で囲むことでその部分を実行結果に置き換えることができるのです。

実行しましょう！

```bash
$ ./opencv_test
```

バージョンが表示されれば成功です！
お疲れ様です！

###2.4 エラーが出る場合

**2.3** の最後に行うテストファイル実行時

>error while loading shared libraries: libopencv_core.so.\*.*: cannot open shared object file: No such file or directory

とエラーが出てしまう場合があるらしいので、対処法を載せておきます。

そもそものエラーの原因は、```/etc/ld.so.cache```中のキャッシュが書き換わっていないことが原因です。

以下のコマンドを実行してください。

```bash
$ sudo ldconfig
```

以上で解消します。


#3. 参考
CMakeの使い方 その１～３（https://qiita.com/shohirose/items/45fb49c6b429e8b204ac#_reference-27dce2bd429efef9488e）