この記事はOpenCV Advent Calendar 2024の11日目の記事です.
他の記事は目次にまとめられています.
TL;DR
- RGB orderで画像を読み込みたい場合は、
IMREAD_COLOR_RGB
を指定しましょう - JPEG/PNGの場合、BGR orderでもRGB orderでも性能差はほとんどありません
-
cvtColor(cv::COLOR_BGR2RGB)
は遅いので、ぜひフラグで指定しましょう!
はじめに
OpenCVのMat(e.g. CV_8UC3)はBGR Orderが前提。imgcodecsでカラー画像を読み込むときは、codecs(libjpeg, libtiff)が出力した RGB Order を BGR order にわざわざ並び替えておりました。しかも最近、DNNモジュールでモデルに適用するべく RGB order へ更に並び替えるケースもあり、なかなか問題となっておりました。
そこで! OpenCV 4.11/5.0から、画像をRGB orderで直接読み込むフラグ IMREAD_COLOR_RGB がございますよ!というご紹介です。
Enumerator | |
---|---|
IMREAD_COLOR_BGR Python: cv.IMREAD_COLOR_BGR |
If set, always convert image to the 3 channel BGR color image. |
IMREAD_COLOR Python: cv.IMREAD_COLOR |
Same as IMREAD_COLOR_BGR. |
IMREAD_COLOR_RGB Python: cv.IMREAD_COLOR_RGB |
If set, always convert image to the 3 channel RGB color image. |
性能検証
測定環境・ライブラリ
built-inのlibjpeg-turboとlibpngを、SIMD有効にして用いる。
-- Media I/O:
-- ZLib: /usr/lib/x86_64-linux-gnu/libz.so (ver 1.3)
-- JPEG: build-libjpeg-turbo (ver 3.0.3-70)
-- SIMD Support Request: YES
-- SIMD Support: YES
-- WEBP: /usr/lib/x86_64-linux-gnu/libwebp.so (ver encoder: 0x020f)
-- AVIF: /usr/lib/x86_64-linux-gnu/libavif.so.16.0.4 (ver 1.0.4)
-- PNG: build (ver 1.6.43)
-- SIMD Support Request: YES
-- SIMD Support: YES (Intel SSE)
-- TIFF: /usr/lib/x86_64-linux-gnu/libtiff.so (ver 42 / 4.5.1)
-- JPEG 2000: OpenJPEG (ver 2.5.0)
-- OpenEXR: build (ver 2.3.0)
-- HDR: YES
-- SUNRASTER: YES
-- PXM: YES
-- PFM: YES
なお、i7-4790Sくんで測定です!今年のアドカレが最後だなあ・・・
kmtr@kmtr-VMware-Virtual-Platform:~/work/build4-main$ cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 60
model name : Intel(R) Core(TM) i7-4790S CPU @ 3.20GHz
測定結果
modules/imgcodecs/perf以下にパフォーマンス測定ツールが入っているので、これをちょっと改造して測定する。
コード差分
kmtr@kmtr-VMware-Virtual-Platform:~/work/opencv4$ git --no-pager diff
diff --git a/modules/imgcodecs/perf/perf_jpeg.cpp b/modules/imgcodecs/perf/perf_jpeg.cpp
index 16610a3044..861ea4533e 100644
--- a/modules/imgcodecs/perf/perf_jpeg.cpp
+++ b/modules/imgcodecs/perf/perf_jpeg.cpp
@@ -44,6 +44,24 @@ PERF_TEST(JPEG, Decode_rgb)
SANITY_CHECK_NOTHING();
}
+PERF_TEST(JPEG, Decode_bgr_and_reorder)
+{
+ String filename = getDataPath("stitching/boat1.jpg");
+
+ FILE *f = fopen(filename.c_str(), "rb");
+ fseek(f, 0, SEEK_END);
+ long len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ vector<uchar> file_buf((size_t)len);
+ EXPECT_EQ(len, (long)fread(&file_buf[0], 1, (size_t)len, f));
+ fclose(f); f = NULL;
+
+ TEST_CYCLE() { cv::Mat img = imdecode(file_buf, IMREAD_COLOR_BGR); cv::cvtColor(img,img, cv::COLOR_BGR2RGB ); }
+
+ SANITY_CHECK_NOTHING();
+}
+
+
PERF_TEST(JPEG, Encode)
{
String filename = getDataPath("stitching/boat1.jpg");
@@ -57,4 +75,4 @@ PERF_TEST(JPEG, Encode)
#endif // HAVE_JPEG
-} // namespace
\ No newline at end of file
+} // namespace
diff --git a/modules/imgcodecs/perf/perf_png.cpp b/modules/imgcodecs/perf/perf_png.cpp
index 5bbef590bd..52dabda5af 100644
--- a/modules/imgcodecs/perf/perf_png.cpp
+++ b/modules/imgcodecs/perf/perf_png.cpp
@@ -47,6 +47,23 @@ PERF_TEST(PNG, decode_rgb)
SANITY_CHECK_NOTHING();
}
+PERF_TEST(PNG, decode_bgr_and_reorder)
+{
+ String filename = getDataPath("perf/2560x1600.png");
+
+ FILE *f = fopen(filename.c_str(), "rb");
+ fseek(f, 0, SEEK_END);
+ long len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ vector<uchar> file_buf((size_t)len);
+ EXPECT_EQ(len, (long)fread(&file_buf[0], 1, (size_t)len, f));
+ fclose(f); f = NULL;
+
+ TEST_CYCLE() { cv::Mat img = imdecode(file_buf, IMREAD_COLOR_BGR); cv::cvtColor(img,img, cv::COLOR_BGR2RGB ); }
+
+ SANITY_CHECK_NOTHING();
+}
+
PERF_TEST(PNG, encode)
{
String filename = getDataPath("perf/2560x1600.png");
測定結果は、こちら。(1)BGR orderでもRGB orderでも性能差はほぼない、(2)BGR orderで読み込んだ後にcvtColor
でRGB orderに並び替えると、明らかに性能が低下している。
Format | BGR | RGB | BGR+reorder |
---|---|---|---|
JPG | 34.30 | 34.40 | 99.77 |
PNG | 26.76 | 24.49 | 51.77 |
ログ
kmtr@kmtr-VMware-Virtual-Platform:~/work/build4-main$ ./bin/opencv_perf_imgcodecs --perf_min_samples=100
[ERROR:0@0.001] global persistence.cpp:566 open Can't open file: '/home/kmtr/work/opencv_extra4/testdata/perf/imgcodecs.xml' in read mode
TEST: Skip tests with tags: 'mem_6gb', 'verylong'
CTEST_FULL_OUTPUT
OpenCV version: 4.10.0-dev
OpenCV VCS version: 4.10.0-431-g7fbf3c1fec
Build type: Release
Compiler: /usr/bin/c++ (ver 13.2.0)
Algorithm hint: ALGO_HINT_ACCURATE
HAL: NO
Parallel framework: pthreads (nthreads=8)
CPU features: SSE SSE2 SSE3 *SSE4.1 *SSE4.2 *AVX *FP16 *AVX2 *AVX512-SKX?
Intel(R) IPP version: ippIP AVX2 (l9) 2021.12.0 (-) Apr 25 2024
Intel(R) IPP features code: 0x8000
OpenCL is disabled
[==========] Running 8 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 4 tests from JPEG
[ RUN ] JPEG.Decode
[ PERFSTAT ] (samples=100 mean=34.30 median=34.34 min=33.29 stddev=0.56 (1.6%))
[ OK ] JPEG.Decode (3550 ms)
[ RUN ] JPEG.Decode_rgb
[ PERFSTAT ] (samples=100 mean=34.40 median=34.40 min=33.86 stddev=0.27 (0.8%))
[ OK ] JPEG.Decode_rgb (3480 ms)
[ RUN ] JPEG.Decode_bgr_and_reorder
.
[ PERFSTAT ] (samples=100 mean=99.77 median=99.70 min=98.11 stddev=0.93 (0.9%))
[ OK ] JPEG.Decode_bgr_and_reorder (10007 ms)
[ RUN ] JPEG.Encode
[ PERFSTAT ] (samples=100 mean=30.59 median=30.68 min=29.69 stddev=0.41 (1.3%))
[ OK ] JPEG.Encode (3130 ms)
[----------] 4 tests from JPEG (20167 ms total)
[----------] 4 tests from PNG
[ RUN ] PNG.decode
[ PERFSTAT ] (samples=100 mean=26.76 median=26.77 min=25.98 stddev=0.25 (0.9%))
[ OK ] PNG.decode (2682 ms)
[ RUN ] PNG.decode_rgb
[ PERFSTAT ] (samples=100 mean=24.49 median=24.49 min=23.89 stddev=0.20 (0.8%))
[ OK ] PNG.decode_rgb (2455 ms)
[ RUN ] PNG.decode_bgr_and_reorder
[ PERFSTAT ] (samples=100 mean=31.69 median=31.67 min=31.04 stddev=0.26 (0.8%))
[ OK ] PNG.decode_bgr_and_reorder (3178 ms)
[ RUN ] PNG.encode
[ PERFSTAT ] (samples=100 mean=51.77 median=51.79 min=51.02 stddev=0.43 (0.8%))
[ OK ] PNG.encode (5218 ms)
[----------] 4 tests from PNG (13533 ms total)
[----------] Global test environment tear-down
[==========] 8 tests from 2 test cases ran. (33700 ms total)
[ PASSED ] 8 tests.
kmtr@kmtr-VMware-Virtual-Platform:~/work/build4-main$
ちょっとだけコード確認
JPEGの場合、jpegturboには、元々BGR orderで画像を読み込む拡張機能がございます。
なのでBGR -> RGBでの性能差は出にくいです。
#ifdef JCS_EXTENSIONS
cinfo->out_color_space = m_use_rgb ? JCS_EXT_RGB : JCS_EXT_BGR;
cinfo->out_color_components = 3;
doDirectRead = true; // BGR -> BGR
#else
cinfo->out_color_space = JCS_RGB;
cinfo->out_color_components = 3;
doDirectRead = m_use_rgb ? true : false; // RGB -> BGR
#endif
一方、PNGの場合、libpngに元々はBGR decodeをするように指定をしていました。このフラグで、出力データが並び替えされるようになっていた、ということになります。
if( (m_color_type & PNG_COLOR_MASK_COLOR) && color && !m_use_rgb)
png_set_bgr( png_ptr ); // convert RGB to BGR
else if( color )
png_set_gray_to_rgb( png_ptr ); // Gray->RGB
else
png_set_rgb_to_gray( png_ptr, 1, 0.299, 0.587 ); // RGB->Gray
まとめ
OpenCVのimgcodecsで、画像をRGB order順に読み込みたい人は、cvtColor(cv::COLOR_BGR2RGB)
を投げ捨て、IMREAD_COLOR_RGB
が使えるOpenCV 4.11/5.0 を使いましょう!!
補足(2024/12/11)
dandelion先生がご指摘いただきました。
もうちょっと詳しく書くと、こんな感じで「OpenCVのMatに含まれるデータは、RGB並びにしよう!」という提案は幾度となく繰り返されております。
ところが、既存モジュールとの互換性などなどの問題で色々トラブルになるため、実現はしていないと・・・。
個人的な意見で申し上げますと。
- 内部データは基本的にBGR orderのまま
- a) IMREAD_COLOR_RGBで画像データを読み込んだ場合、 b) Mat.setOrder(COLOR_RGB)が呼ばれたら、MatにRGB Orderフラグを立てる。チャネルごとに入れ替えるとか、そういうのは気にしない!
- 当該RGB orderフラグが立っている状態で、BGR Order前提の関数が呼び出されたら、Order不整合のログを表示する
ぐらいでいいんじゃないのかしら・・・と思いまする。RGB Orderで入力されたら、自動的にテンポラリなMatにBGR Orderに並び替えて処理し、必要があればRGB Orderに戻してユーザアプリに返却するだなんて、現実的ではございませんので…
以上となります、ご精読ありがとうございました!