話不多說了,代碼展示環(huán)節(jié)!寫代碼之前,要首先確定一下代碼的基礎(chǔ)框架以及集成方式。考慮到SDK中提供的PXP相關(guān)代碼,包含了很多NXP相關(guān)的頭文件以及驅(qū)動(dòng)文件。將他們?nèi)考蛇M(jìn)OpenCV是一件繁瑣而枯燥的事情,因此,我們退而求其次,將這些基礎(chǔ)驅(qū)動(dòng)的添加和維護(hù)任務(wù)直接丟給用戶來處理。 本著誰用,誰負(fù)責(zé)的原則。用戶需要自己將這些PXP相關(guān)的驅(qū)動(dòng)文件以及相關(guān)的頭文件導(dǎo)入到示例工程中。而在OpenCV中,只需要添加相應(yīng)的外部符號(hào)即可。這樣,鏈接器會(huì)幫助OpenCV找到心中的她。? 下面的事情就變得簡(jiǎn)單了,我們只需要在OpenCV中添加一些調(diào)用PXP的外圍代碼即可。 首先是resize函數(shù),在resize.cpp中:
voidcv::resize(InputArray_src,OutputArray_dst,Sizedsize double inv_scale_x, double inv_scale_y, int interpolation )通過查看代碼,發(fā)現(xiàn)其中有一個(gè)基于OCL的優(yōu)化代碼:
CV_OCL_RUN(_src.dims()<=2&&_dst.isUMat()&&_src.cols()>10&&_src.rows()>10 ocl_resize(_src, _dst, dsize, inv_scale_x, inv_scale_y, interpolation))這樣一來,問題就更簡(jiǎn)單了,讓我們直接發(fā)揮學(xué)習(xí)精神,寫個(gè)我們自己的:
CV_PXP_RUN(_src.dims()<=2&&_dst.isMat()&&(interpolation==INTER_LINEAR), resize_pxp(_src,_dst,dsize,inv_scale_x,inv_scale_y)這里有一點(diǎn)要注意,PXP只能支持INTER_LINEAR類型的resize。因此需要我們對(duì)傳入的resize方式進(jìn)行查看。如果是其他類型,就需要調(diào)用OpenCV自帶的resize代碼了。 接下來將上述宏定義實(shí)現(xiàn)在private.hpp中:
#ifdef HAVE_PXP int resize_pxp(cv::InputArray _src, cv::OutputArray _dst, cv::Size dsize, float fx=0, float fy=0, int rotateCode=-1, int flipCode=-2); #define CV_PXP_RUN_(condition, func, ...) try { if((condition)&&func) { return__VA_ARGS__; } } catch(constcv::Exception&e) { CV_UNUSED(e); /*TODO:Addsomelogginghere*/ } #else #define CV_PXP_RUN_(condition,func,...) #endif #define CV_PXP_RUN(condition,func) CV_PXP_RUN_(condition,func)以上代碼的意圖就顯而易見了,如果使能了WITH_PXP功能,那么HAVE_PXP的宏就會(huì)被定義,這樣一來就會(huì)調(diào)用外部resize_pxp函數(shù)進(jìn)行圖像的resize操作。 現(xiàn)在,OpenCV端的代碼編寫工作就完成了,接下來輪到重頭戲了:編寫用戶端的PXP相關(guān)代碼,包括PXP初始化,resize_pxp等,姑且叫它pxp_nxp.cpp :? 1. ?既然是C++,就要凸顯C++的樣子,定義PXP類:
#include “opencv2/opencv.hpp” using namespace cv; /************************************************************** construct the pxp class **************************************************************/ class pxp_handler{ public: pxp_handler(); int resize(cv::InputArray _src, cv::OutputArray _dst, cv::Size dsize, float fx=0, float fy=0, int rotate_code=-1, int flip_code=-2); };2. 函數(shù)實(shí)現(xiàn):
pxp_handler::pxp_handler(){ pxp_init(); } static inline void* get_pxp_handler(){ static pxp_handler s_pxp_handler; return (void*)(&s_pxp_handler); } int resize_pxp(cv::InputArray _src, cv::OutputArray _dst, cv::Size dsize, float fx=0, float fy=0, int rotate_code=-1, int flip_code=-2){ pxp_handler* handler = (pxp_handler*)get_pxp_handler(); return handler->resize(_src, _dst, dsize, fx, fy, rotate_code, flip_code); }這里,我們定義了一個(gè)靜態(tài)類,一旦被使用,其構(gòu)造函數(shù)就會(huì)被直接調(diào)用,完成PXP的初始化函數(shù)pxp_init()調(diào)用。 3.不過在編寫resize函數(shù)之前,還有一件事兒要考慮。PXP本身有一個(gè)限制,只能支持ARGB32,RGB565或是YUV數(shù)據(jù),就是沒有RGB24。。。:
/*! @brief PXP process surface buffer pixel format. */ typedef enum _pxp_ps_pixel_format { kPXP_PsPixelFormatRGB888 = 0x4, /*!< 32-bit pixels without alpha (unpacked 24-bit format) */ kPXP_PsPixelFormatRGB555 = 0xC, /*!< 16-bit pixels without alpha. */ kPXP_PsPixelFormatRGB444 = 0xD, /*!< 16-bit pixels without alpha. */ kPXP_PsPixelFormatRGB565 = 0xE, /*!< 16-bit pixels without alpha. */ kPXP_PsPixelFormatYUV1P444 = 0x10, /*!< 32-bit pixels (1-plane XYUV unpacked). */ kPXP_PsPixelFormatUYVY1P422 = 0x12, /*!< 16-bit pixels (1-plane U0,Y0,V0,Y1 interleaved bytes) */ kPXP_PsPixelFormatVYUY1P422 = 0x13, /*!< 16-bit pixels (1-plane V0,Y0,U0,Y1 interleaved bytes) */ kPXP_PsPixelFormatY8 = 0x14, /*!< 8-bit monochrome pixels (1-plane Y luma output) */ kPXP_PsPixelFormatY4 = 0x15, /*!< 4-bit monochrome pixels (1-plane Y luma, 4 bit truncation) */ kPXP_PsPixelFormatYUV2P422 = 0x18, /*!< 16-bit pixels (2-plane UV interleaved bytes) */ kPXP_PsPixelFormatYUV2P420 = 0x19, /*!< 16-bit pixels (2-plane UV) */ kPXP_PsPixelFormatYVU2P422 = 0x1A, /*!< 16-bit pixels (2-plane VU interleaved bytes) */ kPXP_PsPixelFormatYVU2P420 = 0x1B, /*!< 16-bit pixels (2-plane VU) */ kPXP_PsPixelFormatYVU422 = 0x1E, /*!< 16-bit pixels (3-plane) */ kPXP_PsPixelFormatYVU420 = 0x1F, /*!< 16-bit pixels (3-plane) */ } pxp_ps_pixel_format_t;是不是很氣人,,,,因?yàn)镺penCV中大量使用RGB24的像素?cái)?shù)據(jù)。如果想要使用PXP進(jìn)行加速優(yōu)化,就要實(shí)現(xiàn)一個(gè)高效的RGB24轉(zhuǎn)RGB565的轉(zhuǎn)化函數(shù)。否則,這個(gè)轉(zhuǎn)換部分可能會(huì)成為性能瓶頸,讓PXP辛辛苦苦做的優(yōu)化工作蕩然無存。 經(jīng)過多次嘗試,我們最終得到了如下代碼:
#define zip_v(v, bits, shift_l) ((v >> (8 - bits)) << shift_l) #define RGB2RGB565(r, g, b) (zip_v(r, 5, 11) | zip_v(g, 6, 5) | zip_v(b, 5, 0)) typedef struct { union { rgb_clip_t rgb_clip[4]; uint8_t rgb[12]; uint32_t rgbx4[3]; } rgb_rgb565; } color_t; int RGB888toRGB565_struct(uint32_t *prgb888, uint32_t *prgb565, uint32_t pixCnt) { color_t color; uint32_t rgb565x2[2]; while (pixCnt >= 4) { memcpy(color.rgb_rgb565.rgbx4, prgb888, 12); rgb888+=3; rgb565x2[0] = RGB2RGB565(color.rgb_rgb565.rgb[2], color.rgb_rgb565.rgb[1], color.rgb_rgb565.rgb[0]) | RGB2RGB565(color.rgb_rgb565.rgb[5], color.rgb_rgb565.rgb[4], color.rgb_rgb565.rgb[3]) << 16 ; rgb565x2[1] = RGB2RGB565(color.rgb_rgb565.rgb[8], color.rgb_rgb565.rgb[7], color.rgb_rgb565.rgb[6]) | RGB2RGB565(color.rgb_rgb565.rgb[11], color.rgb_rgb565.rgb[10], color.rgb_rgb565.rgb[9]) << 16 ; memcpy(prgb565,rgb565x2,8); prgb565 += 2; pixCnt -= 4; } return 0; } void bgr2rgb565(cv::InputArray _src, cv::OutputArray _dst, uint32_t image_len){ Mat src = _src.getMat(); _dst.create(src.size(), CV_16U); Mat dst = _dst.getMat(); uint16_t *dst_rgb16 = (uint16_t*)dst.data; uint8_t *src_rgb8 = src.data; RGB888toRGB565_struct((uint32_t*)src_rgb8, (uint32_t *)dst_rgb16, image_len); }
最終的pxp_handler::resize函數(shù)如下:
int pxp_handler::InputArray _src, cv::OutputArray _dst, cv::Size dsize, float fx, float fy, int rotate_code, int flip_code){ Mat src = _src.getMat(); Mat dst = _dst.getMat(); if(src.data == dst.data || dst.data == nullptr){ // only 90/270 need create new one if((rotate_code == ROTATE_90_CLOCKWISE) || (rotate_code == ROTATE_90_COUNTERCLOCKWISE)) _dst.create(Size(dsize.height,dsize.width),src.type()); else _dst.create(Size(dsize.width, dsize.height), src.type()); dst=_dst.getMat(); } uint32_t src_w = src.cols, src_h = src.rows, src_c = src.channels(), src_ptr = (uint32_t)src.data; uint32_t dst_w = dst.cols, dst_h = dst.rows, dst_c = dst.channels(), dst_ptr = (uint32_t)dst.data; Mat tmp(src); if(src_c != 2){ bgr2rgb565(src, tmp, src_w * src_h); src_ptr = (uint32_t)tmp.data; } PXP_CFG(dsize.width, dsize.height); PXP_SetProcessSurfaceScaler(PXP, src_w, src_h, dsize.width, dsize.height); WAIT_PXP_DONE(); return 1; }
這樣一來,基于PXP的優(yōu)化代碼就已經(jīng)編寫完畢了。下一步就是用事實(shí)說話來看看優(yōu)化效果了,由于前幾期中已經(jīng)講過如何構(gòu)建一個(gè)基于OpenCV的測(cè)試工程了,這里就直接開始測(cè)試即可。
還是去手機(jī)里找張圖片,這是一張500*500的彩圖,作為測(cè)試樣本:
將其resize到(320, 240), 看下各個(gè)條件下的運(yùn)行時(shí)間:
首先是CV默認(rèn)算法:
接下來是優(yōu)化算法:
這里,還是要替可憐的PXP解釋下的,15ms很大程度上是被RGB24轉(zhuǎn)RGB565的軟件算法給拖累了,實(shí)際上PXP只需要大概7ms左右。不過,相比較OpenCV的原始代碼,優(yōu)化后的代碼節(jié)省了21ms的運(yùn)行時(shí)間,性能提升了58%。
終于是到了說再見的時(shí)刻,時(shí)至今日,我們的《這個(gè)秋天系列》已經(jīng)更新了5期了。本期迎來了最終章。
內(nèi)容很多,也寫出了感情,還是有些戀戀不舍的。。。。省略10k的感慨。。。。
終于可以下班收工啦!!!!
編輯:黃飛
?
評(píng)論
查看更多