かみのメモ

コンピュータビジョン・プログラムな話題中心の勉強メモ(記事一覧は https://kamino.hatenablog.com/archive へ)

勝手に作るCMake入門 その4 外部ライブラリを利用する

勝手に作るCMake入門の4本目です。

1本目の記事ではCMakeのhello_worldプロジェクトを作成し、2本目の記事では静的ライブラリを利用してプロジェクトを階層化し、3本目の記事ではキャッシュ変数を利用したプロジェクト設定の方法を紹介しました。

今回はCMakeで外部ライブラリを利用する方法についてまとめていきます。 主にOSSで公開されているライブラリをインストールする方法や、find_package()コマンドの使い方について解説します。

全体のもくじ

  1. 基本的な使い方
  2. プロジェクトの階層化
  3. プロジェクトの設定
  4. 外部ライブラリを利用する【今ここ】

この記事のもくじ

7. find_packageとは

具体的な外部ライブラリの使い方を説明する前にfind_package()コマンドについて解説しておきます。

find_package(<PackageName>)は特定の名前のCMakeスクリプトを探し出し、それを実行するコマンドです。 対象となるCMakeスクリプトはその役割に応じてModule, Configの2種類に分類されます。 find_packageはModule, Configの順で探索を行い、目的のスクリプトファイルが見つかったらその中身を実行してコマンドを終了します。

具体的な動作は以下のとおりです。

  1. Moduleモード:以下のパスで<PackageName>.cmakeもしくはFind<PackageName>.cmakeを探す
    1. CMakeキャッシュCMAKE_MODULE_PATHに指定されたパス
    2. CMakeにデフォルトで付いてくるモジュールの置き場(/usr/local/share/cmake/Modulesなど)
  2. Configモード:以下のパスとそれに適当なサフィックスを付けたパスで<PackageName>Config.cmakeもしくは<lower-case-package-name>-config.cmakeを探す
    1. CMake変数<PackageName>_DIRで指定されたパス
    2. CMakeキャッシュ変数<PackageName>_ROOTで指定されたパス
    3. 環境変数<PackageName>_ROOTで指定されたパス
    4. CMakeキャッシュ変数CMAKE_PREFIX_PATH, CMAKE_FRAMEWORK_PATH, CMAKE_APPBUNDLE_PATHで指定されたパス
    5. 環境変数<PackageName>_DIR, CMAKE_PREFIX_PATH, CMAKE_FRAMEWORK_PATH, CMAKE_APPBUNDLE_PATHで指定されたパス
    6. 環境変数PATHで指定されたパス(bin, sbinで終わるパスはその親ディレクトリに読み替える)
    7. (他にもあるが省略)

なおコマンド終了時に<PackageName>_FOUNDというCMake変数に0 or 1 (FALSE or TRUE)が格納されるので、発見できたかどうかによって分岐処理を書くこともできます。

「適当なサフィックス」には./cmake/, ./<name>*/, ./lib/x64/cmake/<name>*/などが含まれます。 詳しいことは公式ドキュメントを参照してください。

以上がfind_packageの概要です。 それではModuleとConfigについて解説していきましょう。

7.1. Module

まずはModuleと呼ばれるCMakeスクリプトについてです。

Moduleは以下の2種類に分類されます。

  1. よく使うCMake関数をまとめたUtility Modules
  2. 外部ライブラリの情報を収集するFind Modules

CMakeではインストール時にデフォルトで多くのモジュールがインストールされています。 デフォルトのモジュールの一覧は公式ドキュメントから確認できます。

また自分でCMakeスクリプトを書いて自作Moduleを作ることもできます。

Utility Modules

Utility Modulesはよく使うCMake関数をまとめたものです。 例えばCheckLanguage.cmake(あるプログラミング言語の開発ツールがインストールされているか確認するための機能をまとめたもの)やBundleUtilities.cmake(主にMac*.appの中身にアクセスするための機能をまとめたもの)などです。 これらのスクリプトを読み込むとcheck_language(), get_bundle_main_executable()などの関数が定義されるので、それらを呼び出すことで機能を利用できます。

Find Modules

一方、Find ModulesはPC内にインストールされているライブラリを発見するためのスクリプトです。 ファイル名はFind<PackageName>.cmakeです。

ご存知の通り、C++のライブラリを自分のプログラムにリンクするときには①ヘッダファイルのディレクトリ, ②静的リンクライブラリファイルのディレクトリ, ③静的リンクライブラリファイルの名前, の3つの情報が必要になりますが、これらの情報をConfigureの度に手入力するのは非常に面倒です。 そこでこれらの情報を自動で収集してくれるのがFindモジュールです。

多くの開発環境ではライブラリをインストールするときにapt, yum, brewなどのパッケージマネージャーや開発元が配布しているインストーラーを使います。 すると「このOSならこのパスにライブラリがインストールされているだろう」というのが推測できます。 Findモジュールはそのパスを探索して①, ②の情報とライブラリのバージョン情報などを取得します。 ③はほぼ固定なのでFindモジュールのスクリプト内で決め打ちされていることが多いです。

CMakeではBoost, BLAS, GLEW, OpenGL, X11, Qt4などのライブラリのFindモジュールがデフォルトで用意されています。

大抵のFindモジュールは以下のようなCMake変数に値をセットします。

  • <PackageName>_INCLUDE_DIRS:インクルードディレクトリのパス
  • <PackageName>_LIBRARY_DIRS:ライブラリファイルがあるディレクトリのパス
  • <PackageName>_LIBRARIES:ライブラリファイル名のリスト
  • <PackageName>_DEFINITIONSコンパイル時のdefinitionフラグ

ただし変数名は必ずしもこの通りになっているわけではないですし、ライブラリの情報を変数にまとめるのではなくターゲット(ライブラリ)を作成しそのプロパティにまとめるケースもあります。 親切なFindスクリプトは冒頭にコメントで仕様を書いてくれているので確認してみてください。

自作のModule

CMakeに付属しているModuleの他にも、自分で書いたCMakeスクリプトを自作Moduleとして読み込ませることもできます。 書いたスクリプト<PackageName>.cmakeFind<PackageName>.cmakeのファイル名で適当なパスに保存しておき、Configure時にCMake変数CMAKE_MODULE_PATHにそのパスを指定してやればfind_package(<PackageName>)で読み込めるようになります。

多くのOSSではプロジェクト直下にcmakeというフォルダを作りset(CMAKE_MODULE_PATH './cmake')のようにしてからfind_packageで読み込んでいます。

7.2. Config

次にConfigです。

ConfigファイルはCMakeプロジェクトをビルドしてライブラリを作成したときに一緒に作成できる設定ファイルです。 ファイル名は<PackageName>Config.cmakeもしくは<lower-case-package-name>-config.cmakeです。

ライブラリの情報を提供するという点ではFindモジュールと同じですが、Configファイルはライブラリの開発元が直接提供している設定ファイルであるという違いがあります。 CMakeプロジェクトを使って開発されているライブラリでは、大抵配布時にConfigファイルが含まれています。

Findモジュールと同じく、大抵のConfigファイルは以下のようなCMake変数に値をセットします。

  • <PackageName>_INCLUDE_DIRS:インクルードディレクトリのパス
  • <PackageName>_LIBRARY_DIRS:ライブラリファイルがあるディレクトリのパス
  • <PackageName>_LIBRARIES:ライブラリファイル名のリスト
  • <PackageName>_DEFINITIONSコンパイル時のdefinitionフラグ

こちらも変数名が必ずしもこの通りになっているとは限りません。 最終的には開発元が公開しているドキュメントやConfigファイルの中身を読んで、Configファイルによってどのような変数やターゲットが作成されるのかを確認する必要があります。

*nix系のConfigファイル事情

*nix系のOSにライブラリをインストールするときは、/usr/local/lib/の中に静的ライブラリファイル, /usr/local/include/の中にヘッダファイル, /usr/local/bin/に実行ファイルを設置するのが標準的です。 パッケージマネージャによっては、ライブラリ自体は別のディレクトリにインストールしておき/usr/local/*/シンボリックリンクだけ貼っておくというパターンもあります。 どちらにせよ/usr/local/*/にライブラリ関係のファイルが配置されるというわけです。

ライブラリのConfigファイルは/usr/local/share/<PackageName>//usr/local/lib/cmake/<PackageName>/に配置されます。 大抵の場合/usr/local/bin/はPATHに追加されているので、CMakeはここに置かれたConfigファイルを発見することができます。

つまり*nix系のOSではパッケージマネージャを通してライブラリをインストールするだけで(ほぼ)find_packageで発見できるようになるということです。 もしライブラリにConfigファイルが付属していなかった場合は自分でFindモジュールを用意する必要があります。 また変則的な場所にライブラリをインストールしている場合は、Configure時にキャッシュ変数<PackageName>_DIRにそのパスをセットすることでCMakeにConfigファイルを発見させることができます。

WindowsのConfigファイル事情

Windowsにライブラリをインストールするときは、C:\Program Files\C:\Program Files(x86)\以下に<ライブラリ名>もしくは<ベンダー名\ライブラリ名>のようなフォルダを作ってその中に配置されることが多いです。 面倒なことに、*nix系OSの/usr/localのようにパスが通っている場所にまとめるという文化はないので、個別にパスを通さない限りfind_package()で発見できるようになりません。

とはいえPATHにどんどんライブラリのパスを追加していくのは環境が汚染されるので遠慮したいところです。 ライブラリをCMakeから利用する前提なら環境変数<PackageName>_DIRを作成するか、CMAKE_PREFIX_PATHにConfigファイルへのパスを追加していくのがよいでしょう。

8. CMakeで外部ライブラリを利用する

それではfind_package()の挙動を踏まえた上で外部のCMakeライブラリを利用する方法を見ていきます。 ここでは一例としてGoogleが開発している最適化ライブラリceres-solverを利用する手順を紹介していきます。 最終目標はceres-solverのサンプルコードを動かしてみることです。

ceres-solver線形代数ライブラリEigenglogに依存しているので、作業手順は以下のようになります。

  • Eigenとglogをインストールする
  • CMakeを使ってceres-solverをインストールする
  • 自分用のプロジェクトを作りceres-solverのサンプルコードを動かす

8.1. Eigenとglogをインストールする

Eigenとglogはかなり有名なOSSですので色々なインストール方法が用意されています。

*nix系OSの場合

*nix系のOSを使っているならパッケージマネージャーを通してインストールすることができるはずです。 例えばaptからインストールするにはapt install libeigen3-dev libgoogle-glog-devを実行すればいいです。

前述のようにOSパッケージマネージャーを通してインストールされたライブラリは/usr/local以下に適切にシンボリックリンクが作成されるので、そのままfind_package()で発見できるようになります。

Windowsの場合

一方、WindowsなどのこれというOSパッケージマネージャーに乏しい環境ではCMakeを使ってソースからビルドするのが無難だと思います。

ビルドとインストールの手順はこれまでのCMakeプロジェクトと同じです。

git clone https://github.com/eigenteam/eigen-git-mirror.git
cd eigen-git-mirror
git checkout 3.3.7
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX='C:/lib/eigen3.3.7'
cmake --build . --target install

cd ..

git clone https://github.com/google/glog.git
cd glog
git checkout v0.4.0
mkdir build_dir
cd build_dir
cmake .. -DCMAKE_INSTALL_PREFIX='C:/lib/glog0.4.0'
cmake --build . --target install

デフォルトではC:\Program Files(x86)\にインストールしようとするのでC:\lib\eigen3.3.7C:\lib\glog0.4.0にインストールするよう設定を変えておきます。 またglogではbazel用のBUILDというファイルが存在するのでbuild_dirというディレクトリの中でConfigureしています。 他にも色々ビルドオプションを設定できるので、必要に応じてccmakeやcmake-guiでキャッシュ変数を編集してからビルドしてください。

Eigenはヘッダオンリーライブラリなのでビルドは行われず所定の場所にファイルがコピーされるだけですが、これでインストールは完了です。 glogはビルド後に必要なファイルがコピーされているはずです。

次にC:/lib/eigen3.3.7/share/eigen3/cmake/の中にEigen3Config.cmakeが作成されているはずなので、これをfind_package()が発見できるように設定しておきます。 環境変数CMAKE_PREFIX_PATHを作成し、そこにC:/lib/eigen3.3.7/;C:/lib/glog0.4.0;を設定しておきます。


これでEigenとglogをCMakeから利用するための環境が整いました。

8.2. ceres-solverをインストールする

次にceres-solverをインストールします。

まずは適当なディレクトリにGitHubのリポジトリをクローンします。 ceres-solverは1.9から2.0に移行するに当たってbazelをサポートしたりCMakeスクリプトを改善したりしているようなので、開発中の2.0.0を使うためにmasterをビルドします。 一応執筆時のHEADは059bcb7fです。

git clone https://github.com/ceres-solver/ceres-solver
cd ceres-solver
mkdir build_dir
cd build_dir

cmake .. -DCMAKE_INSTALL_PREFIX='install_dir' -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF
cmake --build . --target install

先ほども述べたように、CMakeはデフォルトでは/usr/local/C:/Program Files(x86)/にインストールしようとします。 しかし、今回はceres-solverを試してみたいだけなので本格的にインストールするのは避けたいところです。 そこでceres-solver/build_dir/install_dirに仮インストールするよう設定しておきます。

またceres-solverのサンプルとテストのビルドは必要ないのでOFFにしておきます。

ビルドに数分かかりますがceres-solver/build_dir/install_dir以下にceres-solverのライブラリがインストールされます。

8.3. サンプルプロジェクトを作る

次にceres-solverを利用するCMakeプロジェクトを書いてみます。

適当なディレクトリにmain.cppを作成し、以下のコードをコピーしましょう。 ceres-solverのサンプルcurve_fitting.ccを流用しています。

main.cppソースコード

// Ceres Solver - A fast non-linear least squares minimizer
// Copyright 2010, 2011, 2012 Google Inc. All rights reserved.
// http://code.google.com/p/ceres-solver/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors may be
//   used to endorse or promote products derived from this software without
//   specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Author: sameeragarwal@google.com (Sameer Agarwal)

#include "ceres/ceres.h"

using ceres::AutoDiffCostFunction;
using ceres::CostFunction;
using ceres::Problem;
using ceres::Solver;
using ceres::Solve;

// Data generated using the following octave code.
//   randn('seed', 23497);
//   m = 0.3;
//   c = 0.1;
//   x=[0:0.075:5];
//   y = exp(m * x + c);
//   noise = randn(size(x)) * 0.2;
//   y_observed = y + noise;
//   data = [x', y_observed'];

const int kNumObservations = 67;
const double data[] = {
  0.000000e+00, 1.133898e+00,
  7.500000e-02, 1.334902e+00,
  1.500000e-01, 1.213546e+00,
  2.250000e-01, 1.252016e+00,
  3.000000e-01, 1.392265e+00,
  3.750000e-01, 1.314458e+00,
  4.500000e-01, 1.472541e+00,
  5.250000e-01, 1.536218e+00,
  6.000000e-01, 1.355679e+00,
  6.750000e-01, 1.463566e+00,
  7.500000e-01, 1.490201e+00,
  8.250000e-01, 1.658699e+00,
  9.000000e-01, 1.067574e+00,
  9.750000e-01, 1.464629e+00,
  1.050000e+00, 1.402653e+00,
  1.125000e+00, 1.713141e+00,
  1.200000e+00, 1.527021e+00,
  1.275000e+00, 1.702632e+00,
  1.350000e+00, 1.423899e+00,
  1.425000e+00, 1.543078e+00,
  1.500000e+00, 1.664015e+00,
  1.575000e+00, 1.732484e+00,
  1.650000e+00, 1.543296e+00,
  1.725000e+00, 1.959523e+00,
  1.800000e+00, 1.685132e+00,
  1.875000e+00, 1.951791e+00,
  1.950000e+00, 2.095346e+00,
  2.025000e+00, 2.361460e+00,
  2.100000e+00, 2.169119e+00,
  2.175000e+00, 2.061745e+00,
  2.250000e+00, 2.178641e+00,
  2.325000e+00, 2.104346e+00,
  2.400000e+00, 2.584470e+00,
  2.475000e+00, 1.914158e+00,
  2.550000e+00, 2.368375e+00,
  2.625000e+00, 2.686125e+00,
  2.700000e+00, 2.712395e+00,
  2.775000e+00, 2.499511e+00,
  2.850000e+00, 2.558897e+00,
  2.925000e+00, 2.309154e+00,
  3.000000e+00, 2.869503e+00,
  3.075000e+00, 3.116645e+00,
  3.150000e+00, 3.094907e+00,
  3.225000e+00, 2.471759e+00,
  3.300000e+00, 3.017131e+00,
  3.375000e+00, 3.232381e+00,
  3.450000e+00, 2.944596e+00,
  3.525000e+00, 3.385343e+00,
  3.600000e+00, 3.199826e+00,
  3.675000e+00, 3.423039e+00,
  3.750000e+00, 3.621552e+00,
  3.825000e+00, 3.559255e+00,
  3.900000e+00, 3.530713e+00,
  3.975000e+00, 3.561766e+00,
  4.050000e+00, 3.544574e+00,
  4.125000e+00, 3.867945e+00,
  4.200000e+00, 4.049776e+00,
  4.275000e+00, 3.885601e+00,
  4.350000e+00, 4.110505e+00,
  4.425000e+00, 4.345320e+00,
  4.500000e+00, 4.161241e+00,
  4.575000e+00, 4.363407e+00,
  4.650000e+00, 4.161576e+00,
  4.725000e+00, 4.619728e+00,
  4.800000e+00, 4.737410e+00,
  4.875000e+00, 4.727863e+00,
  4.950000e+00, 4.669206e+00,
};

struct ExponentialResidual {
  ExponentialResidual(double x, double y)
      : x_(x), y_(y) {}

  template <typename T> bool operator()(const T* const m,
                                        const T* const c,
                                        T* residual) const {
    residual[0] = T(y_) - exp(m[0] * T(x_) + c[0]);
    return true;
  }

 private:
  const double x_;
  const double y_;
};

int main(int argc, char** argv) {
  double m = 0.0;
  double c = 0.0;

  Problem problem;
  for (int i = 0; i < kNumObservations; ++i) {
    problem.AddResidualBlock(
        new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1>(
            new ExponentialResidual(data[2 * i], data[2 * i + 1])),
        NULL,
        &m, &c);
  }

  Solver::Options options;
  options.max_num_iterations = 25;
  options.linear_solver_type = ceres::DENSE_QR;
  options.minimizer_progress_to_stdout = true;

  Solver::Summary summary;
  Solve(options, &problem, &summary);
  std::cout << summary.BriefReport() << "\n";
  std::cout << "Initial m: " << 0.0 << " c: " << 0.0 << "\n";
  std::cout << "Final   m: " << m << " c: " << c << "\n";
  return 0;
}

次にCMakeLists.txtを書いていきます。

ライブラリのリンクの基本

通常C++のライブラリはヘッダファイル, 静的リンクライブラリファイル(, 動的リンクライブラリファイル)の組み合わせで配布されます。 これを自分の実行ファイルにリンクするときには①ヘッダファイルのディレクトリ, ②静的リンクライブラリファイルのディレクトリ, ③静的リンクライブラリファイルの名前, の3つが必要になります。

CMakeでこの3つを行うときの基本構文は以下のようになっています。

# ライブラリディレクトリの指定
link_directories("xxxx/lib")

add_executable(main_app main.cpp)

# インクルードディレクトリの指定
target_include_directories(main_app PRIVATE
  "xxxx/include")
# ライブラリファイル名の指定
target_link_libraries(main_app xxxx)

ライブラリディレクトリの指定にはlink_directories()コマンドを使います。 この設定はこのプロジェクト全体で共有されます。

インクルードディレクトリの指定にはtarget_include_directories()コマンドを使います。

ライブラリファイル名の指定にはtarget_link_libraries()コマンドを使います。 以前の記事で登場したときは自分で作成したライブラリをリンクするために利用していましたが、このように外部ライブラリのファイル名を指定するためにも利用されます。

この構文が外部ライブラリを利用するときの基本となります。

CeresConfig.cmakeを利用したリンク

しかし今回のケースではmain.cppをビルドするときにceres-solverのヘッダファイル群を参照させ、ライブラリファイルlibceres.aをリンクする必要があり、またEigenやglogといった間接的に参照しているライブラリもリンクする必要があります。 これはかなり面倒な作業です。

そこで、この辺りの面倒な作業は全てCeresConfig.cmakeに任せます。 CeresConfig.cmakeは必要な設定を詰め込んだターゲットceresを作成してくれるので、我々はこれをmain_appにリンクするだけで必要なセッティングが完了します。 ターゲットceresに対しては以前の記事で紹介したのと同じようにtarget_include_directories()などで依存ターゲットへのインクルードやリンクの設定が行われているのでtarget_link_libraries(main_app ceres)を呼び出すだけで必要な設定が行われるというわけです。

cmake_minimum_required(VERSION 3.1)

find_package(Ceres REQUIRED)

add_executable(main_app main.cpp)

target_link_libraries(main_app ceres)

if(MSVC AND ${MSVC_VERSION} GREATER_EQUAL 1915)
  target_compile_definitions(main_app PRIVATE _ENABLE_EXTENDED_ALIGNED_STORAGE)
endif()

最後の部分は、MSVCでのみ必要なオプションの設定です。 CeresではC++alignof()を利用していますが、過去のMSVCではこの挙動にバグがありバグ修正の前後でMSVCの挙動が変わっています。 この挙動の変化を把握していることを明示するため_ENABLE_EXTENDED_ALIGNED_STORAGEを指定しています。

このCMakeプロジェクトをビルドします。 今回はceres-solverを変則的な場所にインストールしているので以下のようにCeresConfig.cmakeの場所を教えてやります。 ここではフルパスを指定する必要があります。

mkdir build
cd build
cmake .. -DCeres_DIR='<path to directory of CeresConfig.cmake>'
cmake --build .

作成されたmain_appを実行してみます。 main_appは区間 {x = [0, 5)} において { y = e^{mx + c} \ | _ {m=0.3, c=0.1}} にノイズを混ぜたサンプル点に対してカーブフィッティングを行うことで {m, c} の値を復元するプログラムです。

iter      cost      cost_change  |gradient|   |step|    tr_ratio  tr_radius  ls_iter  iter_time  total_time
   0  1.211734e+02    0.00e+00    3.61e+02   0.00e+00   0.00e+00  1.00e+04        0    4.41e-04    5.31e-04
   1  2.334822e+03   -2.21e+03    0.00e+00   7.52e-01  -1.87e+01  5.00e+03        1    1.50e-04    7.78e-04
   2  2.331438e+03   -2.21e+03    0.00e+00   7.51e-01  -1.86e+01  1.25e+03        1    9.20e-05    9.23e-04
   3  2.311313e+03   -2.19e+03    0.00e+00   7.48e-01  -1.85e+01  1.56e+02        1    8.68e-05    1.07e-03
   4  2.137268e+03   -2.02e+03    0.00e+00   7.22e-01  -1.70e+01  9.77e+00        1    1.12e-04    1.44e-03
   5  8.553131e+02   -7.34e+02    0.00e+00   5.78e-01  -6.32e+00  3.05e-01        1    1.27e-04    1.69e-03
   6  3.306595e+01    8.81e+01    4.10e+02   3.18e-01   1.37e+00  9.16e-01        1    7.88e-04    2.62e-03
   7  6.426770e+00    2.66e+01    1.81e+02   1.29e-01   1.10e+00  2.75e+00        1    6.55e-04    3.33e-03
   8  3.344546e+00    3.08e+00    5.51e+01   3.05e-02   1.03e+00  8.24e+00        1    6.38e-04    4.00e-03
   9  1.987485e+00    1.36e+00    2.33e+01   8.87e-02   9.94e-01  2.47e+01        1    7.29e-04    4.79e-03
  10  1.211585e+00    7.76e-01    8.22e+00   1.05e-01   9.89e-01  7.42e+01        1    6.92e-04    5.51e-03
  11  1.063265e+00    1.48e-01    1.44e+00   6.06e-02   9.97e-01  2.22e+02        1    7.33e-04    6.31e-03
  12  1.056795e+00    6.47e-03    1.18e-01   1.47e-02   1.00e+00  6.67e+02        1    8.05e-04    7.17e-03
  13  1.056751e+00    4.39e-05    3.79e-03   1.28e-03   1.00e+00  2.00e+03        1    9.01e-04    8.13e-03
Ceres Solver Report: Iterations: 14, Initial cost: 1.211734e+02, Final cost: 1.056751e+00, Termination: CONVERGENCE
Initial m: 0 c: 0
Final   m: 0.291861 c: 0.131439

{m=0.3, c=0.1} に近い値が復元されていますね。


以上、CMakeで外部ライブラリを利用するために必要な知識を解説しました。

とりあえずこのCMake連載はこの記事で完結のつもりです。 まだ紹介していないトピックとして、プロジェクトのインストール設定, Configファイルの作成, CMake関数の挙動などが残っていますが、このあたりの書き方はケースバイケースなのでうまくまとめるのが難しいかな、と感じています。

紹介したサンプルが動かない、間違いを見つけた等ありましたらお知らせいただけると幸いです。


もしよければ↓の☆を1クリックお願いします!