Introduction

掲題そのまま。
結構、このネタはそこら辺に転がっているんだけど、

  1. 計測していない
  2. エンコードだけ計測

みたいなネタでげんなりしたので自分で実施してみました。

実験ソースは下記になります

Condition

条件は下記になります。

  1. Intel Cire i7-8700 (3.20GHz)
  2. 32.0GB
  3. Visual Studio 2017 Update 7 (15.7)
  4. OpenCV 3.2.0 (動的リンク、world形式でビルド。IPP、CUDAは除外)
  5. libjpeg-turbo 1.5.3
  6. libjpeg-turbo.libをリンク

になります。
また、libjpeg.liblibjpeg-turbo.libのどちらをリンクするかで意見が割れていますが、ここではlibjpeg-turbo.libをリンクしました。

OpenCVのビルドのためのCMakeのコマンドは下記のようになります。

libjpeg-turbo 有効

1
2
3
4
5
6
7
8
9
10
11
$ cmake -G "Visual Studio 15 2017 Win64" ^
-D CMAKE_BUILD_TYPE=Release ^
-D BUILD_SHARED_LIBS=ON ^
-D BUILD_opencv_world=ON ^
-D WITH_CUDA=OFF ^
-D WITH_IPP=OFF ^
-D WITH_JPEG=ON ^
-D BUILD_JPEG=OFF ^
-D JPEG_INCLUDE_DIR="<libjpeg-turboのディレクトリ>" ^
-D JPEG_LIBRARY="<libjpeg-turbo.libのフルパス" ^
..

libjpeg-turbo 無効

1
2
3
4
5
6
7
$ cmake -G "Visual Studio 15 2017 Win64" ^
-D CMAKE_BUILD_TYPE=Release ^
-D BUILD_SHARED_LIBS=ON ^
-D BUILD_opencv_world=ON ^
-D WITH_CUDA=OFF ^
-D WITH_IPP=OFF ^
..

Test

実験は、

  1. libjpeg-turbo有効の自家製ビルドOpenCV
  2. libjpeg-turbo無効の自家製ビルドOpenCV
  3. 公式のOpenCVバイナリ

に対して比較します。
また、画像は、

  1. 640x360
  2. 1280x720
  3. 2560x1440

の3種類を用意し、それぞれのデータのデコードとエンコードを1000回繰り返します。
実験ソースは下記です。
ベースとして、下記のページを参考にさせていただきました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// libjpeg-turbo-decode.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"

#define TEST_COUNT 1000

int main(int argc, char *argv[])
{
// Build Information
//std::cout << cv::getBuildInformation() << std::endl;

std::ifstream f(argv[1], std::ios::binary);
if (!f.is_open())
return -1;

f.seekg(0, std::ios::end);
const unsigned int length = f.tellg();
f.seekg(0, std::ios::beg);

const auto buffer = new char[length];
f.read(buffer, length);
f.close();

const auto data = std::vector<char>(buffer, buffer + length);

// Test
int64 start = 0;
int64 end = 0;
double total = 0;
double avg = 0;

start = cv::getTickCount();
for (auto i = 0; i < TEST_COUNT; i++)
auto ret = cv::imdecode(data, cv::IMREAD_UNCHANGED);
end = cv::getTickCount();

total = (end - start) * 1000 / cv::getTickFrequency();
avg = total / TEST_COUNT;
std::cout << "[Decode]" << std::endl;
std::cout << "\tTotal = " << total << "[ms], Avg = " << avg << "[ms]" << std::endl;

const auto img = cv::imdecode(data, cv::IMREAD_UNCHANGED);
std::vector<uchar> buf( img.rows * img.cols );
const std::vector<int> params = { cv::IMWRITE_JPEG_QUALITY, 95 };

start = cv::getTickCount();
for (auto i = 0; i < TEST_COUNT; i++)
cv::imencode(".jpg", img, buf, params);
end = cv::getTickCount();

total = (end - start) * 1000 / cv::getTickFrequency();
avg = total / TEST_COUNT;
std::cout << "[Encode]" << std::endl;
std::cout << "\tTotal = " << total << "[ms], Avg = " << avg << "[ms]" << std::endl;

return 0;
}

Result

実験結果は下記になります。
速度はいずれも平均速度です。

Decode

640x360 1280x720 2560x1440
Enable libjpeg-turbo 1.16508 ms 5.11916 ms 28.0259 ms
Disable libjpeg-turbo 2.15667 ms 9.02238 ms 40.7576 ms
original binary 2.44517 ms 10.1288 ms 46.2685 ms

Encode

640x360 1280x720 2560x1440
Enable libjpeg-turbo 1.37863 ms 5.03018 ms 20.6569 ms
Disable libjpeg-turbo 5.18752 ms 19.4827 ms 83.4678 ms
original binary 5.69063 ms 21.4501 ms 91.6039 ms

Conclusion

エンコード性能は著しい改善を見せており、3.5-4.0倍の速度をたたき出しています。
デコードも、1.6-2.2倍の速度と良い感じです。

高々数十msの違いかもしれませんが、OpenCVのVideoCapture等でWebカメラをキャプチャした際、MotionJPEG形式ならデコード処理がそのままfpsに跳ね返ってきます。
1280X720で従来20ms要していたということは、30fpsのカメラでも事実上、1フレームに33ms+20msの53ms、つまり20fps程度しか性能が出なかったことになります。
これが5msになれば、33ms+5msの38msなら26fpsと改善できます。

Source Code

https://github.com/takuya-takeuchi/Demo/tree/master/Misc/02_libjpeg-turbo-decode