需求:
海思平台从vpss取出的图像帧为yuv420sp,数据格式为VIDEO_FRAME_INFO_S,而opencv调用时多为rgb格式,所以在海思平台使用opencv时需要将VIDEO_FRAME_INFO_S的yuv420sp转为rgb格式,如果使用传统方法将VIDEO_FRAME_INFO_S的yuv映射出来再通过opencv的颜色空间转换,这个色彩空间转换是通过cpu计算的方式软转,即繁琐又非常耗时,测试在Hi3516cv500平台640x480的图像就需要耗时20~30毫秒,基本无法再做其他处理了,好在海思提供了IVE硬件转换色彩空间,在论坛上看到有人说海思IVE转出来的是BGR格式,和opencv使用的BGR正好匹配,这样使用起来就非常简单和方便,测试640x480的图像转换耗时基本在2ms以内,而且其中的memcpy耗时占了大头;
实现
现将海思VIDEO_FRAME_INFO_S转opencv的Mat格式记录如下:
#include "hi_ive.h"
#include "hi_comm_ive.h"
#include "mpi_ive.h"
typedef struct tagIPC_IMAGE{
HI_U64 u64PhyAddr;
HI_U64 u64VirAddr;
HI_U32 u32Width;
HI_U32 u32Height;
}IPC_IMAGE;
HI_S32 frame2Mat(VIDEO_FRAME_INFO_S *srcFrame,Mat &dstMat)
{
HI_U32 w = srcFrame->stVFrame.u32Width;
HI_U32 h = srcFrame->stVFrame.u32Height;
int bufLen = w*h*3;
HI_U8 *srcRGB = NULL;
IPC_IMAGE dstImage;
if(yuvFrame2rgb(srcFrame,&dstImage)!=HI_SUCC){
LOG(ERROR)<<"yuvFrame2rgb Err ."<<endl;
return HI_FAIL;
}
srcRGB = (HI_U8 *)dstImage.u64VirAddr;
dstMat.create(h, w, CV_8UC3);
memcpy(dstMat.data, srcRGB, bufLen*sizeof(HI_U8));
HI_MPI_SYS_MmzFree(dstImage.u64PhyAddr, (void *)&(dstImage.u64VirAddr));
return HI_SUCC;
}
HI_S32 yuvFrame2rgb(VIDEO_FRAME_INFO_S *srcFrame,IPC_IMAGE *dstImage)
{
IVE_HANDLE hIveHandle;
IVE_SRC_IMAGE_S pstSrc;
IVE_DST_IMAGE_S pstDst;
IVE_CSC_CTRL_S stCscCtrl;
HI_S32 s32Ret = 0;
stCscCtrl.enMode = IVE_CSC_MODE_PIC_BT709_YUV2RGB;//IVE_CSC_MODE_VIDEO_BT601_YUV2RGB;
pstSrc.enType = IVE_IMAGE_TYPE_YUV420SP;
pstSrc.au64VirAddr[0]=srcFrame->stVFrame.u64VirAddr[0];
pstSrc.au64VirAddr[1]=srcFrame->stVFrame.u64VirAddr[1];
pstSrc.au64VirAddr[2]=srcFrame->stVFrame.u64VirAddr[2];
pstSrc.au64PhyAddr[0]=srcFrame->stVFrame.u64PhyAddr[0];
pstSrc.au64PhyAddr[1]=srcFrame->stVFrame.u64PhyAddr[1];
pstSrc.au64PhyAddr[2]=srcFrame->stVFrame.u64PhyAddr[2];
pstSrc.au32Stride[0]=srcFrame->stVFrame.u32Stride[0];
pstSrc.au32Stride[1]=srcFrame->stVFrame.u32Stride[1];
pstSrc.au32Stride[2]=srcFrame->stVFrame.u32Stride[2];
pstSrc.u32Width = srcFrame->stVFrame.u32Width;
pstSrc.u32Height = srcFrame->stVFrame.u32Height;
pstDst.enType = IVE_IMAGE_TYPE_U8C3_PACKAGE;
pstDst.u32Width = pstSrc.u32Width;
pstDst.u32Height = pstSrc.u32Height;
pstDst.au32Stride[0] = pstSrc.au32Stride[0];
pstDst.au32Stride[1] = 0;
pstDst.au32Stride[2] = 0;
s32Ret = HI_MPI_SYS_MmzAlloc_Cached(&pstDst.au64PhyAddr[0], (void **)&pstDst.au64VirAddr[0], "User", HI_NULL, pstDst.u32Height*pstDst.au32Stride[0]*3);
if(HI_SUCCESS != s32Ret)
{
HI_MPI_SYS_MmzFree(pstDst.au64PhyAddr[0], (void *)pstDst.au64VirAddr[0]);
LOG(ERROR)<<"HI_MPI_SYS_MmzAlloc_Cached Failed with 0x"<<hex<<s32Ret<<endl;
return s32Ret;
}
memset((void *)pstDst.au64VirAddr[0], 0, pstDst.u32Height*pstDst.au32Stride[0]);
s32Ret = HI_MPI_SYS_MmzFlushCache(pstDst.au64PhyAddr[0], (void *)pstDst.au64VirAddr[0], pstDst.u32Height*pstDst.au32Stride[0]);
if(HI_SUCCESS != s32Ret)
{
HI_MPI_SYS_MmzFree(pstDst.au64PhyAddr[0], (void *)pstDst.au64VirAddr[0]);
LOG(ERROR)<<"HI_MPI_SYS_MmzFlushCache Failed with 0x"<<hex<<s32Ret<<endl;
return s32Ret;
}
HI_BOOL bInstant = HI_TRUE;
s32Ret = HI_MPI_IVE_CSC(&hIveHandle,&pstSrc,&pstDst,&stCscCtrl,bInstant);
if(HI_SUCCESS != s32Ret)
{
HI_MPI_SYS_MmzFree(pstDst.au64PhyAddr[0], (void *)pstDst.au64VirAddr[0]);
LOG(ERROR)<<"HI_MPI_IVE_CSC Failed with 0x"<<hex<<s32Ret<<endl;
return s32Ret;
}
if (HI_TRUE == bInstant)
{
HI_BOOL bFinish = HI_TRUE;
HI_BOOL bBlock = HI_TRUE;
s32Ret = HI_MPI_IVE_Query(hIveHandle,&bFinish,bBlock);
while(HI_ERR_IVE_QUERY_TIMEOUT == s32Ret)
{
usleep(100);
s32Ret = HI_MPI_IVE_Query(hIveHandle,&bFinish,bBlock);
}
}
dstImage->u64PhyAddr = pstDst.au64PhyAddr[0];
dstImage->u64VirAddr = pstDst.au64VirAddr[0];
dstImage->u32Width = pstDst.u32Width;
dstImage->u32Height = pstDst.u32Height;
return HI_SUCC;
}
调用的伪代码如下:
{
Mat frame;
VIDEO_FRAME_INFO_S videoframeinfo;
HI_MPI_VPSS_GetChnFrame(,,&videoframeinfo,);
...;
frame2Mat(&videoframeinfo,frame);
imwrite("save.bmp", frame);
}
编译运行测试保存的bmp图像正常。