通过libjpeg-turbo实现对jpeg图像的解码

mac2024-04-19  33

之前在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中的接口。

GitHub:https://github.com//fengbingchun/OCR_Test

最新回复(0)