針對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

相關文章
相關標籤/搜索