通過libjpeg-turbo實現對jpeg圖像的解碼

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

GitHubhttps://github.com//fengbingchun/OCR_Test

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章