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
|--jconfig.h
|--jerror.h
|--jmorecfg.h
|--jpeglib.h
|--turbojpeg.h
測試結果: code