CMake で OpenCV をリンクする方法【find_package 完全ガイド・C++】

OpenCV for C++
📌 準備: OpenCV の環境構築がまだの方はこちら → C++ 環境構築ガイドC#(OpenCvSharp)セットアップ

CMake プロジェクトから OpenCV を使う

OpenCV をインストールした後、実際のプロジェクトで使うには CMake から見つけてリンクする必要があります。ところがこの部分は「動く書き方」と「なんとなく動いてしまう書き方」の差が大きく、後者のまま進めるとバイナリに 50 個以上のライブラリがリンクされていたといったことが起こります。

この記事では、最小構成から始めて、見つからないときの対処、複数バージョンの切り替え、リンクを必要な分だけに絞る方法までを実測結果とあわせて説明します。

検証した環境

項目 内容
主な検証機 macOS(Apple M2)・CMake 4.4.3
OpenCV(macOS) Homebrew の opencv@4(4.14.0・contrib 込み)と opencv(5.0.0)を併存
比較用(Linux) Ubuntu 24.04 の apt 版(4.6.0)/ Amazon Linux 2023・Rocky Linux 9 でソースビルドした 4.10.0(いずれも Docker コンテナ)

最小の CMakeLists.txt

まずはこれだけで動きます。

cmake_minimum_required(VERSION 3.16)
project(opencv_sample CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(OpenCV REQUIRED)

add_executable(sample main.cpp)
target_link_libraries(sample PRIVATE ${OpenCV_LIBS})

対象のソースはこれです。

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

int main()
{
    std::cout << "OpenCV " << CV_VERSION << std::endl;

    cv::Mat img(240, 320, CV_8UC3, cv::Scalar(30, 120, 220));
    cv::imwrite("out.png", img);

    cv::Mat loaded = cv::imread("out.png");
    std::cout << "読み込み: " << (loaded.empty() ? "NG" : "OK") << std::endl;
    return 0;
}

ビルドと実行です。

cmake -S . -B build
cmake --build build -j4
./build/sample

include_directories() を書いていない点に注目してください。 現在の OpenCV が提供する OpenCVConfig.cmake はインクルードパスをターゲットの情報として持っているため、target_link_libraries() を書けばヘッダの検索パスも自動的に伝わります。古い記事にある include_directories(${OpenCV_INCLUDE_DIRS}) は、書かなくても動きます。


OpenCV が見つからないとき

インストール先が標準のパスでない場合、find_package は失敗します。

CMake Error at CMakeLists.txt:6 (find_package):
  Could not find a package configuration file provided by "OpenCV" with any
  of the following names:

    OpenCVConfig.cmake
    opencv-config.cmake

探しているのは OpenCVConfig.cmake が置かれているディレクトリです。場所を教える方法は2つあります。

方法1: OpenCV_DIR を指定する

OpenCVConfig.cmake があるディレクトリを直接指します。

cmake -S . -B build -DOpenCV_DIR=/usr/local/lib64/cmake/opencv4

方法2: CMAKE_PREFIX_PATH を指定する

インストールのルート(binlib の親)を指します。OpenCV 以外の依存も同じ場所にあるならこちらが楽です。

cmake -S . -B build -DCMAKE_PREFIX_PATH=/usr/local

環境ごとの OpenCVConfig.cmake の位置は次のあたりです。

環境 位置
Linux(ソースビルド・RHEL 系) /usr/local/lib64/cmake/opencv4/
Linux(ソースビルド・Debian 系) /usr/local/lib/cmake/opencv4/
Linux(Ubuntu の apt 版) /usr/lib/<アーキテクチャ>-linux-gnu/cmake/opencv4/
macOS(Homebrew opencv=5 系) /opt/homebrew/lib/cmake/opencv5/
macOS(Homebrew opencv@4 /opt/homebrew/opt/opencv@4/lib/cmake/opencv4/(keg-only のため既定の検索パスに入らない)
Windows(公式配布物) <展開先>\build\

💡 ソースビルドした OpenCV では pkg-config が使えないことがあります。 OpenCV 4 系は opencv4.pc の生成が既定で無効で、-DOPENCV_GENERATE_PKGCONFIG=ON を付けないと作られません(なお CMake 4 系で OpenCV 4.11 以前をビルドすると、このオプションでビルドが止まる問題があります。macOS 版の記事で扱っています)。一方 OpenCVConfig.cmake既定で生成されます。CMake を使うプロジェクトなら、pkg-config を経由するより find_package のほうが確実です。


複数バージョンが入っている環境での指定

開発機に OpenCV 4 系と 5 系が同居することがあります(Homebrew の opencv が 5.x、opencv@4 が 4.x など)。バージョンは find_package に書けます。

find_package(OpenCV 5 REQUIRED)

ところが、次のエラーになることがあります(opencv@4brew link --overwrite で直接リンクし、5 系のリンクが外れている環境での例です)。

CMake Error at CMakeLists.txt:5 (find_package):
  Could not find a configuration file for package "OpenCV" that is compatible
  with requested version "5".

  The following configuration files were considered but not accepted:

    /opt/homebrew/lib/cmake/opencv4/OpenCVConfig.cmake, version: 4.14.0
      The version found is not compatible with the version requested.

「4.14.0 は見たが条件に合わない」と言っていますが、5 系の名前は出てきません。5 系の OpenCVConfig.cmake が検索パスに無いためです。

find_package は、検索パス上にある候補を順に見て、バージョン条件に合わないものは捨てて次を探します。実際に 4 系と 5 系の両方を検索パスに入れて試すと、並び順に関係なく条件に合うほうが選ばれました。

検索パス(CMAKE_PREFIX_PATH)の並び 指定 選ばれたもの
opencv@4opencv find_package(OpenCV 5) 5.0.0(4.14.0 を退けて次を採用)
opencvopencv@4 find_package(OpenCV 4) 4.14.0
指定なし(4 系だけが検索パスにある状態) find_package(OpenCV 5) 上のエラー

つまり問題は「探してくれない」ことではなく、探す場所に入っていないことです。Homebrew の opencv@4 のような keg-only のパッケージは /opt/homebrew 直下にリンクされないため、既定の検索パスから見えません(逆に 5 系だけがリンクされている通常の環境では、find_package(OpenCV 4) のほうが失敗します)。

使いたい側の場所を明示します。

# 5 系を使う
cmake -S . -B build -DCMAKE_PREFIX_PATH=/opt/homebrew/opt/opencv

# 4 系を使う
cmake -S . -B build -DCMAKE_PREFIX_PATH=/opt/homebrew/opt/opencv@4

実際に前者で構成すると、次のように切り替わります。

-- OpenCV version: 5.0.0
-- OpenCV_DIR    : /opt/homebrew/opt/opencv/lib/cmake/opencv5

構成時に何を掴んだか分からなくなるのを防ぐため、CMakeLists.txt にこの2行を入れておくことを勧めます。

message(STATUS "OpenCV version: ${OpenCV_VERSION}")
message(STATUS "OpenCV_DIR    : ${OpenCV_DIR}")

${OpenCV_LIBS} は「全部」を意味する

ここが本題です。find_package(OpenCV REQUIRED) と書いた場合、${OpenCV_LIBS} にはインストールされている全モジュールが入ります

contrib 込みの Homebrew 版(4.14.0)で中身を出力したところ、56 個が並びました。

opencv_calib3d;opencv_core;opencv_dnn;opencv_features2d;opencv_flann;opencv_gapi;
opencv_highgui;opencv_imgcodecs;opencv_imgproc;opencv_ml;opencv_objdetect;
opencv_photo;opencv_stitching;opencv_video;opencv_videoio;opencv_alphamat;
opencv_aruco;opencv_bgsegm;opencv_bioinspired;opencv_ccalib; ...(以下略)

必要なモジュールだけを COMPONENTS で指定すると、この一覧が絞られます。

find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs)
-- OpenCV_LIBS : opencv_core;opencv_imgproc;opencv_imgcodecs

実測: どれだけ違うか

冒頭の main.cppcv::imwritecv::imread を呼ぶだけ)を、指定なしと COMPONENTS 3つでそれぞれビルドし、実行ファイルが実際に依存する OpenCV のライブラリ数(macOS は otool -L、Linux は readelf -dNEEDED)を数えました。

環境 find_package(OpenCV REQUIRED) COMPONENTS core imgproc imgcodecs
macOS(Homebrew 4.14.0・56 モジュール) 56 3
Amazon Linux 2023(ソースビルド 4.10.0) 10 3
Rocky Linux 9(ソースビルド 4.10.0) 10 3
Ubuntu 24.04(apt 版 4.6.0・55 モジュール) 2 2

macOS では実行ファイルのサイズも 108,920 バイト → 42,872 バイトと変わりました(Linux の3環境ではサイズは同じで、変わったのは依存の数だけでした)。

Ubuntu だけ差が出ないのは、Ubuntu の GCC が既定で --as-needed を付けてリンクし、実際に使っていないライブラリを依存から外すからです。一方、macOS や RHEL 系(AL2023・Rocky)ではそのまま依存に残ります。

「Ubuntu で試したら問題なかった」ものが、macOS や RHEL 系では使っていないモジュールの .so / .dylib まで要求する実行ファイルになります。配布物に必要なライブラリを洗い出すときに差が出るので、環境に頼らず COMPONENTS で必要な分を明示しておくのが確実です。

⚠️ COMPONENTS に書き忘れたモジュールを使うと、リンク時に未定義シンボルのエラーになります。「動かなくなったら足す」という進め方で問題ありません。最初から全部リンクしておくより、足りないものを足すほうが安全です。


ターゲット名で直接書く

${OpenCV_LIBS} を使わず、モジュール名を直接書くこともできます。

find_package(OpenCV REQUIRED COMPONENTS core imgproc)

add_executable(sample main.cpp)
target_link_libraries(sample PRIVATE opencv_core opencv_imgproc)

opencv_core などは CMake のターゲットとして定義されているため、この書き方でインクルードパスもリンク指定も伝わります。どのモジュールに依存しているかが CMakeLists.txt を見るだけで分かるので、規模が大きいプロジェクトではこちらが読みやすくなります。


つまずきポイント

⚠️ バージョンを変えたのに反映されない

CMake は一度見つけた OpenCV_DIR をキャッシュ(build/CMakeCache.txt)に保存します。CMAKE_PREFIX_PATH を変えても、キャッシュが残っていると古いほうを使い続けます。

rm -rf build
cmake -S . -B build -DCMAKE_PREFIX_PATH=...

ビルドディレクトリごと消すのが確実です。

⚠️ Windows でパスに build を指定する

Windows の公式配布物(opencv-4.x.x-windows.exe を展開したもの)では、OpenCVConfig.cmakebuild ディレクトリの直下にあります。opencv\buildOpenCV_DIR に指定するのが基本です。ここにある OpenCVConfig.cmake が、アーキテクチャ(x64 等)と Visual Studio のランタイム(vc16 等)に応じて x64\vc16\lib 側の設定を読み込みます。opencv(展開先のルート)を指定しても見つかりません。


まとめ

  • 最小構成は find_package(OpenCV REQUIRED) + target_link_libraries(... ${OpenCV_LIBS}) の2行
  • include_directories() は書かなくてよい
  • 見つからないときは OpenCV_DIROpenCVConfig.cmake のあるディレクトリ)か CMAKE_PREFIX_PATH(インストールのルート)を指定する
  • find_package は検索パス上の候補からバージョン条件に合うものを選ぶ。keg-only などで検索パスに入っていないものは選べないので、場所を明示する
  • ${OpenCV_LIBS} は全モジュール。macOS では実際に 56 個に依存する実行ファイルになり、COMPONENTS で3つに絞れる。Ubuntu は --as-needed で差が出ないが、macOS・RHEL 系では出る
  • ソースビルドでは opencv4.pc が既定で生成されない。CMake なら find_package を使うほうが確実

インストール自体がまだであれば OpenCV 環境構築 最短ガイド【C++/Windows・macOS・Linux】 を参照してください。contrib 込みでソースビルドする手順は OpenCV + opencv_contrib を Ubuntu でビルドする完全ガイド【C++】 にまとめています。Windows で opencv_world をリンクする場合は opencv_world の使い方とリンク方法【OpenCV/C++ Windows】 もあわせてご覧ください。

🛠 画像処理のプロが開発するSDK/API

本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。

→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

タイトルとURLをコピーしました