この記事はOpenCV Advent Calendar 2024の16日目の記事です。
はじめに
OpenCV 4.10.0以降からzlib-ng連携機能が使用できるようになりました。本記事ではOpenCVのzlib-ng連携の紹介および効果計測を行っていきます。
zlib-ngとは
zlib-ng(https://github.com/zlib-ng/zlib-ng)は、zlib(https://zlib.net/)とAPI互換を保ちつつ、モダンなCPU命令を使用した実装により高速化を図ったライブラリです。
https://github.com/zlib-ng/zlib-ngにて"ng"はnext generationを意味することが書かれています。
OpenCVにおけるzlib-ng連携の歴史
2024/12/16時点におけるOpenCVのzlib-ng連携の歴史を以下にまとめます。
- https://github.com/opencv/opencv/issues/22573でzlibからzlib-ngへの置き換え提案が行われました
- https://github.com/opencv/opencv/pull/24782でzlib-ng対応が行われました(zlib-ng 2.1.6)
- https://github.com/opencv/opencv/pull/26113でzlib-ngバージョンアップが行われました(zlib-ng 2.2.1)
そのため、stableバージョンの場合、OpenCV 4.10.0以降であればzlib-ng連携の機能が使用できます。また、今後リリースが計画されているOpenCV 4.11.0でzlib-ngバージョンアップが適用される見込みです。
OpenCVのzlib-ng連携有効化
OpenCVのcoreモジュールでzlib-ngを有効にする場合、CMakeオプションでWITH_ZLIB_NG=ON
を指定します。WITH_ZLIB_NG=ON
としてCMakeを実行したときのメッセージ例は以下の通りです。このメッセージからzlib-ng
が参照されるようになっていることがわかります。
Media I/O:
ZLib-Ng: build (zlib ver 1.3.1.zlib-ng, zlib-ng ver 2.2.1)
さらにOpenCVのimgcodecsモジュールでもzlib-ngを有効にする場合は以下のオプションも追加します。
- PNG
-
WITH_ZLIB_NG=ON
、WITH_PNG=ON
、BUILD_PNG=ON
(libpng)-
BUILD_PNG=ON
は、opencv/3rdparty
以下のlibpngがzlib-ngをリンクするため必要
-
-
WITH_ZLIB_NG=ON
、WITH_SPNG=ON
、BUILD_SPNG=ON
(libspng)-
BUILD_SPNG=ON
は、opencv/3rdparty
以下のlibspngがzlib-ngをリンクするため必要
-
-
- TIFF
-
WITH_ZLIB_NG=ON
、WITH_TIFF=ON
、BUILD_TIFF=ON
-
BUILD_TIFF=ON
は、opencv/3rdparty
以下のlibtiffがzlib-ngをリンクするため必要
-
-
- OpenEXR
-
WITH_ZLIB_NG=ON
、WITH_OPENEXR=ON
、BUILD_OPENEXR=ON
-
BUILD_OPENEXR=ON
は、opencv/3rdparty
以下のopenexrがzlib-ngをリンクするため必要
-
-
zlib-ng連携の仕組み
ここではOpenCVがzlibの代わりにzlib-ngとどのように連携しているかの仕組みについて解説します。
OpenCVにおけるzlib-ng格納場所
zlib-ngのソースコードはopencv/3rdparty
以下(https://github.com/opencv/opencv/tree/4.10.0/3rdparty/zlib-ng)に格納されています。
zlib-ng切り替え
OpenCVでzlib、zlib-ngどちらを使うかはどのように切り替えているのでしょうか?OpenCVのhttps://github.com/opencv/opencv/blob/4.10.0/cmake/OpenCVFindLibsGrfmt.cmakeに以下の記述があります。
if(WITH_ZLIB_NG)
ocv_clear_vars(ZLIB_LIBRARY ZLIB_LIBRARIES ZLIB_INCLUDE_DIR)
set(ZLIB_LIBRARY zlib CACHE INTERNAL "")
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/zlib-ng")
set(ZLIB_INCLUDE_DIR "${${ZLIB_LIBRARY}_BINARY_DIR}" CACHE INTERNAL "")
set(ZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR})
set(ZLIB_LIBRARIES ${ZLIB_LIBRARY})
ocv_parse_header_version(ZLIB "${${ZLIB_LIBRARY}_SOURCE_DIR}/zlib.h.in" ZLIB_VERSION)
ocv_parse_header_version(ZLIBNG "${${ZLIB_LIBRARY}_SOURCE_DIR}/zlib.h.in" ZLIBNG_VERSION)
set(HAVE_ZLIB_NG YES)
上記のコードでWITH_ZLIB_NG=ON
の場合にopencv/3rdparty/zlib-ng
を参照するようになっていることがわかります。冒頭で書いたようにzlib-ngはzlibとAPI互換があるため基本的にはこれだけでOKです。
効果計測
ここでは、zlib-ng対応の効果を検証するために以下の環境で処理速度の計測を行います。
計測環境、計測条件
計測環境を下表にまとめます。
名称 | スペック、バージョン |
---|---|
CPU | Intel Core i7-9800X CPU @ 3.80GHz |
メモリ | 32GB |
OS | Ubuntu 22.04 64bit |
コンパイラ | g++ 11.4.0 |
OpenCVバージョン | OpenCVリポジトリの4.xブランチでzlib-ngバージョンアップ適用時点(https://github.com/opencv/opencv/commit/85923c8f3054bc52c9c30c2a8e932dbd96cc26fc) |
zlib | 1.3.1 |
zlib-ng | 2.2.1 |
本記事ではWITH_ZLIB_NG=ON
、WITH_ZLIB_NG=OFF
それぞれで以下の処理を実行し、処理時間を計測します。入力データは3チャンネルかつ、1080p(1920 x 1080)、4K(3840 x 2160)のデータを用います。
- cv::imencode、cv::imdecode
- 今回は利用ケースが多いであろうPNGのみ
- cv::imwrite、cv::imread
- 今回は利用ケースが多いであろうPNGのみ
- cv::FileStorage
計測結果
cv::imencode、cv::imdecode
エンコードのコードは以下の通りです。
std::vector<int> params = std::vector<int>(2);
params[0] = cv::IMWRITE_PNG_COMPRESSION;
params[1] = 1;
std::vector<uchar> buf;
cv::imencode(".png", img, buf, params);
そして、デコードのコードは以下の通りです。
cv::Mat dst = cv::imdecode(cv::Mat(buf), cv::IMREAD_COLOR);
処理時間を下表に示します。この結果からimencodeはzlib-ngの方が大幅に高速に処理できており、imdecodeもわずかにzlib-ngの方が高速に処理できていることがわかります。
条件 | 処理時間(ms) (zlib利用) |
処理時間(ms) (zlib-ng利用) |
---|---|---|
1080p(1920, 1080), imencode | 136.82 | 88.75 |
4K(3840, 2160), imencode | 517.71 | 309.792 |
1080p(1920, 1080), imdecode | 38.33 | 36.90 |
4K(3840, 2160), imdecode | 133.65 | 118.09 |
cv::imwrite、cv::imread
画像書き込みのコードは以下の通りです。
std::vector<int> params = std::vector<int>(2);
params[0] = cv::IMWRITE_PNG_COMPRESSION;
params[1] = 1;
cv::imwrite("img.png", img, params);
そして、画像読み込みのコードは以下の通りです。
cv::imread("img.png", cv::IMREAD_COLOR);
処理時間を下表に示します。この結果からimwriteはzlib-ngの方が大幅に高速に処理できており、imreadもわずかにzlib-ngの方が高速に処理できていることがわかります。
条件 | 処理時間(ms) (zlib利用) |
処理時間(ms) (zlib-ng利用) |
---|---|---|
1080p(1920, 1080), imwrite | 157.59 | 103.65 |
4K(3840, 2160), imwrite | 607.81 | 385.32 |
1080p(1920, 1080), imread | 39.43 | 37.82 |
4K(3840, 2160), imread | 134.12 | 117.34 |
cv::FileStorage
https://docs.opencv.org/4.10.0/da/d56/classcv_1_1FileStorage.html#a587b4f5793a894fbe245e95cb30d6bc7で
Name of the file to open or the text string to read the data from. Extension of the file (.xml, .yml/.yaml or .json) determines its format (XML, YAML or JSON respectively). Also you can append .gz to work with compressed files, for example myHugeMatrix.xml.gz.
という説明があり、実はcv::FileStorageで圧縮形式を扱う場合にzlib、zlib-ngを使用します。
書き込みのコードは以下の通りです。
cv::FileStorage fs("img.xml.gz", cv::FileStorage::WRITE);
fs << "img" << img;
そして、読み込みのコードは以下の通りです。
cv::FileStorage fs("img.xml.gz", cv::FileStorage::READ);
cv::Mat img;
fs["img"] >> img;
処理時間を下表に示します。この結果からwriteはzlib-ngの方が大幅に高速に処理できており、readもわずかにzlib-ngの方が高速に処理できていることがわかります。
条件 | 処理時間(ms) (zlib利用) |
処理時間(ms) (zlib-ng利用) |
---|---|---|
1080p(1920, 1080), cv::FileStorage write | 838.87 | 394.33 |
4K(3840, 2160), cv::FileStorage write | 3100.47 | 1528.96 |
1080p(1920, 1080), cv::FileStorage read | 698.78 | 664.40 |
4K(3840, 2160), cv::FileStorage read | 2731.44 | 2595.08 |
おわりに
本記事ではOpenCVのzlib-ng連携の紹介および効果計測について述べました。この記事が読者のよりよいOpenCVライフに繋がれば幸いです。明日、12/17は@Kazuhitoさんの担当です、お楽しみに。