vcpkg提供ライブラリ使ってOpenCV build

この記事は[OpenCV Advent Calendar 2024](https://qiita.com/advent-calendar/2024/opencv)の8日目の記事です。
他の記事は目次にまとめられています。

■ TL;DR
========
- OpenCVにvcpkg提供ライブラリを組み込む事ができるよ！これで、比較的最新コードがいつでも試せるよ！
- だけど、デフォルトだとライブラリはstatic linkだから、依存関係解決に手を加えないといけない事も・・・できなくはない。

■ 本題
=====================================================

〇 （背景・モチベ）最新版ライブラリをもっと手軽にOpenCVに組み込みたい！
------------------------------------------------------------
OpenCVにJPEG XL Encoder/Decoder追加のpull requestが出ていた。Great Job! なのだが、ちょっとOpenCVの詳細は分からないから助けてーとあったので、手助けしてみた。

- https://github.com/opencv/opencv/pull/26379
- https://github.com/opencv/opencv/pull/26401

その際に、vcpkg提供のlibjxl(JPEG XLライブラリ）使えば最新版のが簡単にインストールできるよ！と言われていた。言われていたのだけど…。

"OpenCV vcpkg"で検索すると、大体「vcpkg提供OpenCVの使い方」が引っ掛かる。
しかし、vcpkg提供ライブラリを、OpenCVのbuildで使う、という情報が少ない！

@UnaNancyOwen さんのこちらの記事とかですね。
https://qiita.com/UnaNancyOwen/items/54c6334f269d03646cad

〇（現状分析）ライブラリインストール方法と、手軽さ・最新追従できるか
--------------------------------------------------------------

雑に分析すると、下記の通り。`vcpkg`を使えば、簡単にライブラリをインストールでき、メジャーなものであればそこそこ最新になっている！！
（※インストールできるとは言っているが、OpenCVに組み込めるとはいっていない）。

| 手段 | 手軽さ | 最新追従 |
|:-|:-|:-|
| sourceからbuild | ライブラリ提供に依存(cmakeがあれば...) | 常に最新版。 |
| OpenCVにbuild-in | 実績あるコードが最初から！ | 若干、古い事も。 |
| system library | `apt`コマンドで簡単に入る！  | Distributor任せ。若干、古い事も  |
| vcpkg | `vcpkg`コマンドで簡単に入る！ | 最新版試せることが多い |

今回はOpenCVにvcpkg提供ライブラリを繋げる手順をまとめておく。
誰得？私得だよ！ボロ儲けだよ！

〇 (Step.1) OpenCV開発環境整備
-----------------------------
Ubuntu 24.04環境をベースにOpenCV 開発環境を整備する。

```bash
sudo apt update -y && sudo apt install -y build-essential git cmake ccache cmake-curses-gui ninja-build curl zip unzip tar pkg-config
cd && mkdir work && cd work && git clone --depth=1 https://github.com/opencv/opencv.git opencv4
cd && cd work && cmake -S opencv4 -B build4-main -GNinja
```

この時のCMake出力はこんな感じ。 

```plaintext
--   Media I/O:
--     ZLib:                        zlib (ver 1.3.1)
--     JPEG:                        build-libjpeg-turbo (ver 3.0.3-70)
--       SIMD Support Request:      YES
--       SIMD Support:              NO
--     WEBP:                        build (ver encoder: 0x020f)
--     AVIF:                        NO
--     PNG:                         build (ver 1.6.43)
--     TIFF:                        build (ver 42 - 4.6.0)
--     JPEG 2000:                   build (ver 2.5.0)
--     OpenEXR:                     build (ver 2.3.0)
--     HDR:                         YES
--     SUNRASTER:                   YES
--     PXM:                         YES
--     PFM:                         YES
```

〇(Step2) vcpkg環境にライブラリをインストール
------------------------------------------
大まかな手順は、Microsoftさんの https://learn.microsoft.com/ja-jp/vcpkg/get_started/get-started?pivots=shell-bash を参考に。

```bash
cd && git clone https://github.com/microsoft/vcpkg.git
cd && cd vcpkg && ./bootstrap-vcpkg.sh
```

bashrcに環境変数もセット

```
export VCPKG_ROOT=/home/kmtr/vcpkg
export PATH=$VCPKG_ROOT:$PATH
```

手始めに、libjpeg-turboを入れてみる。
```
vcpkg libjpeg-turbo[jpeg7] 
```

インストールできたかの確認。善哉善哉。

```
$ vcpkg list
libjpeg-turbo:x64-linux                           3.0.4               libjpeg-turbo is a JPEG image codec that uses SI...
libjpeg-turbo[jpeg7]:x64-linux                                        Emulate libjpeg v7 API/ABI (this makes libjpeg-t...
vcpkg-cmake-config:x64-linux                      2024-05-23
vcpkg-cmake:x64-linux                             2024-04-23
```

〇(Step3) OpenCVをvcpkg提供ライブラリ指定してbuild
------------------------------------------------

`-DCMAKE_TOOLCHAIN_FILE`指定をしてcmakeすると、vcpkg提供ライブラリを参照するようになっている

```bash
cd && cd work && cmake -S opencv4 -B build4-main.vckpkg -GNinja -DCMAKE_TOOLCHAIN_FILE=/home/kmtr/vcpkg/scripts/buildsystems/vcpkg.cmake
```

この時の出力はこんな感じ。JPEGの行が変わっている。

```
--   Media I/O:
--     ZLib:                        zlib (ver 1.3.1)
--     JPEG:                        /home/kmtr/vcpkg/installed/x64-linux/lib/libjpeg.a (ver 70)
--     WEBP:                        build (ver encoder: 0x020f)
--     AVIF:                        NO
--     PNG:                         build (ver 1.6.43)
--     TIFF:                        build (ver 42 - 4.6.0)
--     JPEG 2000:                   build (ver 2.5.0)
--     OpenEXR:                     build (ver 2.3.0)
--     HDR:                         YES
--     SUNRASTER:                   YES
--     PXM:                         YES
--     PFM:                         YES
```

これで、OpenCV with vcpkg提供ライブラリは成功だ！

〇 まとめ
--------
- vcpkgを使う事で、最新に近いライブラリを手軽に組み込む事ができます！


■ (Appendix)JPEG以外も繋いでみよう
=================================

〇(Step4) もーっといっぱい他ライブラリも繋ぎたい！
-----------------------------------------------
私の中のハンチョウが囁くのです。

> **？？？「フフ……　へただなあ、熊太郎くん。へたっぴさ……！
 欲望の解放のさせ方がへた....。
 熊太郎くん……　贅沢ってやつはさ……　小出しはダメなんだ……!　やる時はきっちりやった方がいい……!
 それでこそ次の節制の励みになるってもんさ……!　違うかい……？」**

はっ！！！　私は、libjpeg turboだけじゃ足らん・・・そうだ！アレもコレもみんなやってこそ、vcpkg提供ライブラリの活用だあああああ（目ギラギラ）

`vcpkg install  zlib-ng libwebp tiff libpng libavif openexr`

結果：ライブラリ不足でbuildは失敗。とほほほほほ・・・

```
--   Media I/O:
--     ZLib:                        optimized /home/kmtr/vcpkg/installed/x64-linux/lib/libz.a debug /home/kmtr/vcpkg/installed/x64-linux/debug/lib/libz.a (ver 1.3.1)
--     JPEG:                        /home/kmtr/vcpkg/installed/x64-linux/lib/libjpeg.a (ver 70)
--     WEBP:                        /home/kmtr/vcpkg/installed/x64-linux/lib/libwebp.a (ver encoder: 0x020f)
--     AVIF:                        /home/kmtr/vcpkg/installed/x64-linux/debug/lib/libavif.a (ver 1.1.1)
--     PNG:                         optimized /home/kmtr/vcpkg/installed/x64-linux/lib/libpng16.a debug /home/kmtr/vcpkg/installed/x64-linux/debug/lib/libpng16d.a (ver 1.6.43)
--     TIFF:                        optimized /home/kmtr/vcpkg/installed/x64-linux/lib/libtiff.a debug /home/kmtr/vcpkg/installed/x64-linux/debug/lib/libtiffd.a (ver 42 / 4.7.0)
--     JPEG 2000:                   OpenJPEG (ver 2.5.2)
--     OpenEXR:                     OpenEXR::OpenEXR (ver 3.3.1)
--     HDR:                         YES
--     SUNRASTER:                   YES
--     PXM:                         YES
--     PFM:                         YES
```

Dynamic linkするときは依存性が解決できるけど、static linkする時はそうもいかない。

```plantuml
@startuml
hide members
class libtiff
libtiff --> liblzma
libavid --> libyuv
libwebp --> libsharpyuv
@enduml
```

よし、強引にパッチを書こう（非純正）

ということで、もし試す方がいれば参考になるだろうということで、これもおまけで残しておきます。

<details><summary>修正パッチ本体</summary>

```diff
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c8e4ef0..b584658 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -310,6 +310,12 @@ OCV_OPTION(WITH_WEBP "Include WebP support" ON
 OCV_OPTION(WITH_OPENEXR "Include ILM support via OpenEXR" ((WIN32 OR ANDROID OR APPLE) OR BUILD_OPENEXR) OR NOT CMAKE_CROSSCOMPILING
   VISIBLE_IF NOT APPLE_FRAMEWORK AND NOT WINRT
   VERIFY HAVE_OPENEXR)
+OCV_OPTION(WITH_LZMA "Include LZMA support" OFF
+  VISIBLE_IF TRUE
+  VERIFY HAVE_LZMA)
+OCV_OPTION(WITH_LIBYUV "Include LibYUV support" OFF
+  VISIBLE_IF TRUE
+  VERIFY HAVE_LIBYUV)
 OCV_OPTION(WITH_OPENGL "Include OpenGL support" OFF
   VISIBLE_IF NOT ANDROID AND NOT WINRT
   VERIFY HAVE_OPENGL)
@@ -1442,6 +1448,12 @@ if(WITH_ZLIB_NG OR HAVE_ZLIB_NG)
 else()
   status("    ZLib:"   ZLIB_FOUND THEN "${ZLIB_LIBRARIES} (ver ${ZLIB_VERSION_STRING})" ELSE "build (ver ${ZLIB_VERSION_STRING})")
 endif()
+if(WITH_LZMA OR HAVE_LZMA)
+  status("    LZMA" LIBLZMA_FOUND THEN "${LIBLZMA_LIBRARIES} (ver ${LIBLZMA_VERSION}) " ELSE "NO")
+endif()
+if(WITH_LIBYUV OR HAVE_LIBYUV)
+  status("    LibYUV" LIBYUV_FOUND THEN "${LIBYUV_LIBRARIES}" ELSE "NO")
+endif()

 if(WITH_JPEG OR HAVE_JPEG)
   if(NOT HAVE_JPEG)
diff --git a/cmake/OpenCVFindLibsGrfmt.cmake b/cmake/OpenCVFindLibsGrfmt.cmake
index e059305..4061929 100644
--- a/cmake/OpenCVFindLibsGrfmt.cmake
+++ b/cmake/OpenCVFindLibsGrfmt.cmake
@@ -57,6 +57,25 @@ else()
   endif()
 endif()

+# --- liblzma (optional for libtiff) ---
+if(WITH_LZMA)
+  find_package(LibLZMA REQUIRED)
+  if(LibLZMA_FOUND)
+    set(HAVE_LZMA TRUE)
+  endif()
+endif()
+
+# --- libyuv (optional for AVIF) ---
+if(WITH_LIBYUV)
+  find_package(libyuv CONFIG REQUIRED)
+  if(libyuv_FOUND)
+    set(LIBYUV_INCLUDE_DIRS ${libyuv_INCLUDE_DIRS})
+    find_file(LIBYUV_LIBRARIES NAME libyuv.a PATHS "${libyuv_DIR}/../../lib")
+    set(LIBYUV_FOUND TRUE)
+    set(HAVE_LIBYUV TRUE)
+  endif()
+endif()
+
 # --- libavif (optional) ---

 if(WITH_AVIF)
diff --git a/cmake/OpenCVFindWebP.cmake b/cmake/OpenCVFindWebP.cmake
index 24f4f93..161b46f 100644
--- a/cmake/OpenCVFindWebP.cmake
+++ b/cmake/OpenCVFindWebP.cmake
@@ -23,11 +23,16 @@ else()
     FIND_LIBRARY(WEBP_LIBRARY NAMES webp)
     MARK_AS_ADVANCED(WEBP_LIBRARY)

+    FIND_LIBRARY(SHARPYUV_LIBRARY NAMES sharpyuv)
+    MARK_AS_ADVANCED(SHARPYUV_LIBRARY)
+
     # handle the QUIETLY and REQUIRED arguments and set WEBP_FOUND to TRUE if
     # all listed variables are TRUE
     INCLUDE(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
     FIND_PACKAGE_HANDLE_STANDARD_ARGS(WebP DEFAULT_MSG WEBP_LIBRARY WEBP_INCLUDE_DIR)

     SET(WEBP_LIBRARIES ${WEBP_LIBRARY})
+    SET(SHARPYUV_LIBRARIES ${SHARPYUV_LIBRARY})
     SET(WEBP_INCLUDE_DIRS ${WEBP_INCLUDE_DIR})
+
 endif()
diff --git a/modules/imgcodecs/CMakeLists.txt b/modules/imgcodecs/CMakeLists.txt
index 1468d4d..7a78a68 100644
--- a/modules/imgcodecs/CMakeLists.txt
+++ b/modules/imgcodecs/CMakeLists.txt
@@ -27,6 +27,7 @@ if(HAVE_WEBP)
   add_definitions(-DHAVE_WEBP)
   ocv_include_directories(${WEBP_INCLUDE_DIR})
   list(APPEND GRFMT_LIBS ${WEBP_LIBRARIES})
+  list(APPEND GRFMT_LIBS ${SHARPYUV_LIBRARIES})
 endif()

 if(HAVE_SPNG)
@@ -78,6 +79,16 @@ if(HAVE_OPENEXR)
   endif()
 endif()

+if(HAVE_LIBYUV)
+  ocv_include_directories(${LIBYUV_INCLUDE_DIRS})
+  list(APPEND GRFMT_LIBS ${LIBYUV_LIBRARIES})
+endif()
+
+if(HAVE_LZMA)
+  ocv_include_directories(${LIBLZMA_INCLUDE_DIRS})
+  list(APPEND GRFMT_LIBS ${LIBLZMA_LIBRARIES})
+endif()
+
 if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR OR HAVE_SPNG)
   ocv_include_directories(${ZLIB_INCLUDE_DIRS})
   list(APPEND GRFMT_LIBS ${ZLIB_LIBRARIES})
kmtr@kmtr-VMware-Virtual-Platform:~/work/opencv4$

```

</details>


これでばっちぐーなのです！！

```bash
kmtr@kmtr-VMware-Virtual-Platform:~/work/build4-main.vckpkg$ nm lib/libopencv_imgcodecs.so | c++filt | grep "cv\:\:AvifDecoder::readHeader"
0000000000116b60 t cv::AvifDecoder::readHeader()
00000000000e299e t cv::AvifDecoder::readHeader() [clone .cold]
kmtr@kmtr-VMware-Virtual-Platform:~/work/build4-main.vckpkg$ nm lib/libopencv_imgcodecs.so | c++filt | grep "cv\:\:WebPDecoder::readHeader"
0000000000143350 t cv::WebPDecoder::readHeader()
00000000000e7a20 t cv::WebPDecoder::readHeader() [clone .cold]
000000000084ce60 d cv::WebPDecoder::readHeader()::__cv_check__113
000000000084ce20 d cv::WebPDecoder::readHeader()::__cv_check__114
000000000084cde0 d cv::WebPDecoder::readHeader()::__cv_check__121
000000000084cda0 d cv::WebPDecoder::readHeader()::__cv_check__129
kmtr@kmtr-VMware-Virtual-Platform:~/work/build4-main.vckpkg$ nm lib/libopencv_imgcodecs.so | c++filt | grep "cv\:\:TiffDecoder::readHeader"
0000000000135e70 t cv::TiffDecoder::readHeader()
00000000000e618a t cv::TiffDecoder::readHeader() [clone .cold]
000000000084cae0 d cv::TiffDecoder::readHeader()::__cv_check__303
000000000084caa0 d cv::TiffDecoder::readHeader()::__cv_check__313
000000000084ca60 d cv::TiffDecoder::readHeader()::__cv_check__325
000000000084ca20 d cv::TiffDecoder::readHeader()::__cv_check__339
000000000084c9e0 d cv::TiffDecoder::readHeader()::__cv_check__347
000000000084c9a0 d cv::TiffDecoder::readHeader()::__cv_check__354

```