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