PNG encoding performance difference between 3.1 and 3.2
Encoding a PNG using OpenCV 3.2.0 takes about 2x longer than OpenCV 3.1.0 on an ARM Cortex A9 system, and about 1.4x longer on an x64 system. Does anyone know what causes the difference and whether it can be reduced through build settings? I'll try git bisect between 3.1 and 3.2 to see what caused the difference but wanted to check if anyone already knows. Thanks
Test code (cv.cpp):
#include <iostream>
#include <opencv2/opencv.hpp>
#include <sys/time.h>
int height = 1600;
int width = 1200;
void loadFrame(cv::Mat& frame)
{
srand (time(NULL));
printf("generating random image\n");
int image_format = CV_8U;
frame.create(height, width, image_format);
uint8_t* image_ptr = frame.ptr<uint8_t>(0);
for (int pixel = 0; pixel < int(frame.total()); ++pixel)
{
image_ptr[pixel] = rand();
}
}
bool writeImageToString(const cv::Mat& image, const std::string& extension, std::vector<uint8_t>& buffer)
{
if (image.empty() || (image.cols == 0 && image.rows == 0))
return false;
bool success = true;
// Set the image compression parameters.
std::vector<int> params;
if (extension == ".png")
{
params.push_back(cv::IMWRITE_PNG_COMPRESSION);
params.push_back(6); // MAX_MEM_LEVEL = 9
success = cv::imencode(extension, image, buffer, params);
}
else if (extension == ".jpg")
{
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(90); // 0...100 (higher is better)
success = cv::imencode(extension, image, buffer, params);
}
return success;
}
void encodeTest(int argc, const char** argv)
{
int frame_tests = 10;
int write_tests = 5;
size_t png_size = 0;
size_t jpg_size = 0;
double png_time = 0;
double jpg_time = 0;
struct timeval start, end;
for ( int frame_count = 0; frame_count < frame_tests; frame_count++ )
{
cv::Mat frame;
loadFrame(frame);
std::vector<uint8_t> buffer;
gettimeofday(&start, NULL);
for ( int test_count = 0; test_count < write_tests; test_count++ )
{
writeImageToString(frame, ".png", buffer);
}
gettimeofday(&end, NULL);
png_time += ((end.tv_sec - start.tv_sec) * 1000)
+ (end.tv_usec / 1000 - start.tv_usec / 1000);
png_size += buffer.size();
gettimeofday(&start, NULL);
for ( int test_count = 0; test_count < write_tests; test_count++ )
{
writeImageToString(frame, ".jpg", buffer);
}
jpg_size += buffer.size();
gettimeofday(&end, NULL);
jpg_time += ((end.tv_sec - start.tv_sec) * 1000)
+ (end.tv_usec / 1000 - start.tv_usec / 1000);
}
std::cout << "Avg time PNG: " << png_time / frame_tests / write_tests / 1000 << "sec JPG: " << jpg_time / frame_tests / write_tests / 1000 << "sec" << std::endl;
printf("Avg size PNG: %zu JPG: %zu\n", png_size / 10, jpg_size / 10);
}
int main(int argc, const char** argv)
{
printf( "OpenCV version : %s\n" , CV_VERSION);
try
{
encodeTest(argc, argv);
}
catch (cv::Exception& e)
{
printf("OpenCV test error\n");
throw;
}
return 0;
}
This is the CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
project( cv )
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable( cv cv.cpp )
target_link_libraries( cv ${OpenCV_LIBS} )
To generate cross-compile for ARM I used following cmake command:
cmake -DCMAKE_TOOLCHAIN_FILE=../platforms/linux/arm-gnueabi.toolchain.cmake -DGCC_COMPILER_VERSION=5 -DENABLE_NEON=ON -DWITH_OPENMP=1 -DCMAKE_INSTALL_PREFIX=/usr/local ..
For intel x64 system (Ubuntu 14.04) I just used:
cmake .
Sample runs on ARM:
OpenCV version : 3.1.0
Avg time PNG: 0.26792sec JPG: 0.35374sec
Avg size PNG: 1925061 JPG: 1509208
OpenCV version : 3.1.0
Avg time PNG: 0.26778sec JPG: 0.35472sec
Avg size PNG: 1925061 JPG: 1509038
OpenCV version : 3.2.0
Avg time PNG: 0 ...
neither 3.1 nor 3.2 do matter any more (those are all outdated). do your regression test against this and report back, please !
The encode times using libraries built from the link hash (733fec0) look the same as version 3.2.0 (slower than 3.1). Here is what I see on my x64 machine:
btw, please use cv::getTickCount() or std::chrono::steady_clock for measuring (CPU clock) not gettimeofday() (wall clock)
I found the commit which changed the timing for me: https://github.com/opencv/opencv/comm...
It looks like i just needed to explicitly set the encode strategy after this commit, since the strategy is set to 'default' when setting compression level. Thanks for the advice about CPU vs wall clock