之前在https://blog.csdn.net/fengbingchun/article/details/89715416中介绍过通过libjpeg-turbo接口实现将数据编码或压缩成jpeg数据并通过FILE的fwrite接口将其直接保存成*.jpg图像,当时用的是libjpeg的接口,其实还可以使用turbojpeg api的接口即tjCompress2实现对数据的编码,见下面的code:
int get_jpeg_compress_data2(const unsigned char* data, int width, int height, int pixelFormat, unsigned char** jpegBuf, unsigned long* jpegSize, int jpegSubsamp, int jpegQual, int flags)
{
tjhandle handle = tjInitCompress();
int ret = tjCompress2(handle, data, width, 0, height, pixelFormat, jpegBuf, jpegSize, jpegSubsamp, jpegQual, flags);
tjDestroy(handle);
return 0;
}
相对应的实现图像的解码或解压缩也有两套接口,一套是libjpeg的,即头文件为jpeglib.h,一套是turbojpeg的,即头文件为turbojpeg.h。测试代码如下:
#include <string>
#include <memory>
#include "funset.hpp"
#include <opencv2/opencv.hpp>
int test_libjpeg_turbo_decompress()
{
#ifdef _MSC_VER
std::string image_path{ "E:/GitCode/OCR_Test/test_data/" };
#else
std::string image_path{ "test_data/" };
#endif
std::string image_name = image_path + "tirg.jpg";
int width, height, channels;
long long t1 = Timer::getNowTime();
std::unique_ptr<unsigned char[]> data = get_jpeg_decompress_data(image_name.c_str(), width, height, channels);
long long t2 = Timer::getNowTime();
if (data == nullptr) {
fprintf(stderr, "fail to decompress: %s\n", image_name.c_str());
return -1;
}
fprintf(stdout, "decompress time 1: %lldms, width: %d, height: %d, channels: %d\n", t2 - t1, width, height, channels);
std::string result_image = image_path + "result_tirg.png";
cv::Mat mat(height, width, CV_8UC3, data.get());
cv::cvtColor(mat, mat, CV_RGB2BGR);
cv::imwrite(result_image, mat); // save *.jpg will crash in linux
int width2, height2, channels2;
t1 = Timer::getNowTime();
std::unique_ptr<unsigned char[]> data2 = get_jpeg_decompress_data2(image_name.c_str(), width2, height2, channels2);
t2 = Timer::getNowTime();
if (data2 == nullptr) {
fprintf(stderr, "fail to decompress: %s\n", image_name.c_str());
return -1;
}
fprintf(stdout, "decompress time 2: %lldms, width2: %d, height2: %d, channels2: %d\n", t2 - t1, width2, height2, channels2);
std::string result_image2 = image_path + "result_tirg2.png";
cv::Mat mat2(height2, width2, CV_8UC3, data2.get());
cv::cvtColor(mat2, mat2, CV_RGB2BGR);
cv::imwrite(result_image2, mat2);
return 0;
}
std::unique_ptr<unsigned char[]> get_jpeg_decompress_data2(const char* image_name, int& width, int& height, int& channels)
{
FILE* infile = fopen(image_name, "rb");
if (infile == nullptr) {
fprintf(stderr, "can't open %s\n", image_name);
return nullptr;
}
fseek(infile, 0, SEEK_END);
unsigned long srcSize = ftell(infile);
std::unique_ptr<unsigned char[]> srcBuf(new unsigned char[srcSize]);
fseek(infile, 0, SEEK_SET);
fread(srcBuf.get(), srcSize, 1, infile);
fclose(infile);
tjhandle handle = tjInitDecompress();
int subsamp, cs;
int ret = tjDecompressHeader3(handle, srcBuf.get(), srcSize, &width, &height, &subsamp, &cs);
if (cs == TJCS_GRAY) channels = 1;
else channels = 3;
int pf = TJCS_RGB;
int ps = tjPixelSize[pf];
std::unique_ptr<unsigned char[]> data(new unsigned char[width * height * channels]);
ret = tjDecompress2(handle, srcBuf.get(), srcSize, data.get(), width, width * channels, height, TJPF_RGB, TJFLAG_NOREALLOC);
tjDestroy(handle);
return data;
}
std::unique_ptr<unsigned char[]> get_jpeg_decompress_data(const char* image_name, int& width, int& height, int& channels)
{
FILE* infile = fopen(image_name, "rb");
if (infile == nullptr) {
fprintf(stderr, "can't open %s\n", image_name);
return nullptr;
}
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
/* Step 1: allocate and initialize JPEG decompression object */
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
/* Step 2: specify data source (eg, a file) */
jpeg_stdio_src(&cinfo, infile);
/* Step 3: read file parameters with jpeg_read_header() */
jpeg_read_header(&cinfo, TRUE);
/* Step 4: set parameters for decompression */
/* Step 5: Start decompressor */
jpeg_start_decompress(&cinfo);
cinfo.out_color_space = JCS_RGB; //JCS_EXT_BGR;
int row_stride = cinfo.output_width * cinfo.output_components;
/* Output row buffer */
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);
width = cinfo.output_width;
height = cinfo.output_height;
channels = cinfo.output_components;
std::unique_ptr<unsigned char[]> data(new unsigned char[width * height * channels]);
/* Step 6: while (scan lines remain to be read) */
for (int j = 0; j < cinfo.output_height; ++j) {
jpeg_read_scanlines(&cinfo, buffer, 1);
unsigned char* p = data.get() + j * row_stride;
memcpy(p, buffer[0], row_stride);
}
/* Step 7: Finish decompression */
jpeg_finish_decompress(&cinfo);
/* Step 8: Release JPEG decompression object */
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return data;
}
执行结果如下:对于小图来说,两套接口执行时间上差不多,对于400*330*3的图像,两种方式都大约在3ms至5ms之间;对于大图来说,反复测试多次,大多数turbojpeg.h的接口要比jpeglib.h的接口耗时更少一些。而且turbojpeg.h的实现要不jpeglib.h代码量更少一些。因此推荐直接使用turbojpeg.h中的接口。