针对opencv imdecode 解码性能低的解决方案--libjpeg-turbo

opencv 对JPEG的解码,其内部实质上是基于第三方库libjpeg进行解码的。可是libjpeg自己的性能并非很快。经测试对一张2336x4160分辨率的jpg文件进行解码,在android 环境下使用opencv imdecode 解码耗时竟然有700+ms,这对于一些高性能的应用是不可取的。本文主要讲如何使用libjpeg-turbo 对JPEG 数据流进行解码,并转换成opencv cv::Mat结构。android

对于如何在NDK下使用编译好的so库,在前几篇的文章都有提到,包括libjpeg-turbo 库的编译,调用等等。(不明白的同窗能够参考前两篇文章)ios

那就直接贴代码吧: jpeg2Mat.hpp性能

#include "include/turbojpeg.h"
#include <opencv2/core/core.hpp>
#include <tuple>
#include <vector>
#include <fstream>

/**
* decode JPEG format to XXX format
* @param pJpegData
* @param JpegdataSize
* @param convrt_flag
*        "RGB" - see TJPF_RGB
*	     "BGR" - see TJPF_BGR
*		  ...
*  see all support format at enum TJPF
*/
std::tuple<bool,std::vector<uint8_t>,uint64_t,uint64_t,uint64_t> decodeJpeg2X(uint8_t* pJpegData,uint64_t JpegdataSize,const char* convrt_flag)
{
	assert( pJpegData != NULL );
	int width = 0,height = 0,jpegsubsamp = 0;

	tjhandle jpeg = tjInitDecompress();

	if(jpeg == nullptr)
	{
		return std::make_tuple(false, std::vector<uint8_t>(0), 0, 0, 0); 
	}

	if(tjDecompressHeader2(jpeg,pJpegData,JpegdataSize,&width,&height,&jpegsubsamp) != 0)
	{
		return std::make_tuple(false, std::vector<uint8_t>(0), 0, 0, 0);
	}

	TJPF eformat;
	if(convrt_flag == "ABGR")
		eformat = TJPF::TJPF_ABGR;
	else if(convrt_flag == "ARGB")
		eformat = TJPF::TJPF_ARGB;
	else if(convrt_flag == "BGR")
		eformat = TJPF::TJPF_BGR;
	else if(convrt_flag == "BGRA")
		eformat = TJPF::TJPF_BGRA;
	else if(convrt_flag == "BGRX")
		eformat = TJPF::TJPF_BGRX;
	else if(convrt_flag == "CMYK")
		eformat = TJPF::TJPF_CMYK;
	else if(convrt_flag == "GRAY")
		eformat = TJPF::TJPF_GRAY;
	else if(convrt_flag == "RGB")
		eformat = TJPF::TJPF_RGB;
	else if(convrt_flag == "RGBA")
		eformat = TJPF::TJPF_RGBA;
	else if(convrt_flag == "RGBX")
		eformat = TJPF::TJPF_RGBX;
	else if(convrt_flag == "XBGR")
		eformat = TJPF::TJPF_XBGR;
	else if(convrt_flag == "XRGB")
		eformat = TJPF::TJPF_XRGB;
	
	uint64_t pitch = tjPixelSize[eformat] * width;
	uint64_t size = pitch * height;
	std::vector<uint8_t> output(size);

	if(tjDecompress2(jpeg,pJpegData,JpegdataSize,&output.front(),width,pitch,height,eformat,0) != 0)
	{
		return std::make_tuple(false, std::vector<uint8_t>(0), 0, 0, 0);
	}

	return std::make_tuple(true, std::move(output), size, width, height);
}

std::tuple<bool,std::vector<uint8_t>,uint64_t,uint64_t,uint64_t> decodeJpeg2X(std::string filename,const char* convrt_flag)
{
	std::ifstream ifs(filename.c_str(),std::ios_base::binary | std::ios_base::in);

	if(!ifs.good())
		return std::make_tuple(false, std::vector<uint8_t>(0), 0, 0, 0);
	ifs.seekg(0,std::ios::end);
	uint64_t size = ifs.tellg();
	ifs.seekg(0,std::ios::beg);

	std::vector<char> buffer(size);
	ifs.read(&buffer.front(),size);

	return decodeJpeg2X((uint8_t*)&buffer.front(),size,convrt_flag);
}

cv::Mat Jpeg2Mat(uint8_t *jpegData, uint64_t jpegSize)
{
	auto res = decodeJpeg2X( (uint8_t*)jpegData,jpegSize,"BGR");
	bool success = false;
	std::vector<uint8_t> buff;
	int width,height,size;

	std::tie(success,buff,size,width,height) = res;
	Mat dst(height,width,CV_8UC3,(uint8_t*)&buff.front());
	return dst.clone();
}

cv::Mat Jpeg2Mat(std::string filename)
{
	auto res = decodeJpeg2X( filename ,"BGR");
	bool success = false;
	std::vector<uint8_t> buff;
	int width,height,size;

	std::tie(success,buff,size,width,height) = res;

	Mat dst(height,width,CV_8UC3,(uint8_t*)&buff.front());
	return dst.clone();
}

有个疑惑的是:turbojpeg对JPEG 转 RGB, BGR的都正常,可是对其余的如GRAY , RGBA , BGRA ,或者是其余4通道的格式,都会挂掉,不知道是否是跟平台的指令集有关系。不过对opencv 的使用JPEG转BGR就足够了。测试

文件结构:ui

  1. --include
  2. |--jconfig.h
  3. |--jerror.h
  4. |--jmorecfg.h
  5. |--jpeglib.h
  6. |--turbojpeg.h
  7. --jpeg2Mat.hpp

测试结果: 对2336x4160分辨率的解码耗时code

相关文章
相关标签/搜索