11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IMREAD_COLOR_RGBの性能検証

Last updated at Posted at 2024-12-10

この記事は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 OrderBGR 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に戻してユーザアプリに返却するだなんて、現実的ではございませんので…

以上となります、ご精読ありがとうございました!

11
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?