HI3559V200获取IMX458摄像头数据_(3)实例分析+问题解决

1.整体流程设计

在这里插入图片描述

2.模块分析

2.1 ipcm

2.1.1 ipcm初始化
参考sample_msg_client 和 sample_msg_server的 Media_Msg_Init(void)函数

服务重名导致添加服务失败
开始的时候直接把sample_vio_server放在liteos上运行,运行报错

[IPCMSG]:Fun:HI_IPCMSG_AddService,Line:135,Service HiMPP_MSG already exist.
HI_IPCMSG_AddService fail

提示就是这个HiMPP_MSG已经运行了,然后就去代码里面搜索这个字符串,确实./ndk/code/mediaserver/msg/msg_server.c这个服务已经创建了同样的服务,所以只能修改咯,把服务端和客户端的 服务名+端口号都修改

2.1.2 收发消息

客户端:
HI_S32 start_server_video(){
    HI_S32 s32Ret=HI_FAILURE;
    HI_IPCMSG_MESSAGE_S *pReq = NULL;
    HI_IPCMSG_MESSAGE_S *pResp = NULL;
    pReq = HI_IPCMSG_CreateMessage(IPCM_MOD_START_CAMERA, 0, NULL, 0);
    HI_IPCMSG_SendSync(g_MCmsgId, pReq, &pResp, SEND_MSG_TIMEOUT);//阻塞型,等待回复消息
    s32Ret = pResp->s32RetVal;//取出回复的值
    // printf("jjcc s32Ret=%d\n",s32Ret);
    HI_IPCMSG_DestroyMessage(pReq);
    HI_IPCMSG_DestroyMessage(pResp);
    return s32Ret;
}
服务端:
HI_S32 MSG_Start_Camera(HI_S32 s32MsgId, HI_IPCMSG_MESSAGE_S *pstMsg)
{
    printf("jjcc MSG_Start_Camera\n");
    HI_S32 s32Ret;
    HI_IPCMSG_MESSAGE_S *respMsg;
    s32Ret = SAMPLE_VIO_StartViOnlineVpssOnlineRoute();
    printf("jjcc MSG_Start_Camera ret=%#x\n",s32Ret);
    respMsg = HI_IPCMSG_CreateRespMessage(pstMsg, s32Ret , NULL, 0);//把执行结果s32Ret 放到回复消息中
    HI_IPCMSG_SendAsync(s32MsgId, respMsg, NULL);
    HI_IPCMSG_DestroyMessage(respMsg);
    return HI_SUCCESS;
}

2.2 VB初始化

HI_S32 SAMPLE_VIO_StartViOnlineVpssOnlineRoute(HI_VOID)
{
   。。。
    /*config vb*/
    memset_s(&stVbConf, sizeof(VB_CONFIG_S), 0, sizeof(VB_CONFIG_S));
    stVbConf.u32MaxPoolCnt              = 2;

    u32BlkSize = COMMON_GetPicBufferSize(stSize.u32Width, stSize.u32Height, SAMPLE_PIXEL_FORMAT, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, DEFAULT_ALIGN);
    stVbConf.astCommPool[0].u64BlkSize  = u32BlkSize;
    stVbConf.astCommPool[0].u32BlkCnt   = 10;

    u32BlkSize = VI_GetRawBufferSize(stSize.u32Width, stSize.u32Height, PIXEL_FORMAT_RGB_BAYER_16BPP, COMPRESS_MODE_NONE, DEFAULT_ALIGN);
    stVbConf.astCommPool[1].u64BlkSize  = u32BlkSize;
    stVbConf.astCommPool[1].u32BlkCnt   = 4;

    s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
    。。。

SAMPLE_COMM_SYS_Init里面就进行了VB的初始化,这里出现了报错:

[SAMPLE_COMM_SYS_Init]-381: HI_MPI_VB_Init failed!

分析步骤:
1.看log,可以看到是HI_MPI_VB_Init这个接口返回失败,一般HI_MPI接口失败的ret值,文档都会有说明,可以打印出来,查一下什么原因。我这边当时没有保存下来,现在不知道返回值是多少了。
2.查询mpp log。在linux端的命令是 cat /dev/logmpp,但是现在是运行在liteos,所以运行cat_logmpp
这边可以看到是VB申请MMZ失败。

Huawei LiteOS # cat_logmpp
<3>[(null)] mmz malloc failed!
<0>[    vb] [Func]:create_pool [Line]:252 [Info]:[size = 3136512, cnt = 10]vb mmz alloc:(null) failed!

3.查看MMZ,MMZ分为linux和liteos两端,独立的,内存分配文件是SDK根目录的.config

CONFIG_MEM_TOTAL_SIZE="512"
CONFIG_MEM_IPCM_BASE="0x80000000"
CONFIG_MEM_PARAM_BASE="0x80100000"
CONFIG_MEM_PARAM_SIZE="0x00100000"

CONFIG_MEM_HUAWEILITE_SYS_BASE="0x80200000"
CONFIG_MEM_HUAWEILITE_SYS_SIZE="0x01d00000"

CONFIG_MEM_RES_BASE="0x81f00000"
CONFIG_MEM_RES_SIZE="0x00100000"

CONFIG_MEM_LINUX_SYS_BASE="0x82000000"
CONFIG_MEM_LINUX_SYS_SIZE="0x06000000"

CONFIG_MEM_HUAWEILITE_MMZ_BASE="0x88000000"
CONFIG_MEM_HUAWEILITE_MMZ_SIZE="0x10000000"

CONFIG_MEM_LINUX_MMZ_BASE="0x98000000"
CONFIG_MEM_LINUX_MMZ_ANONYMOUS_SIZE="0x03000000"
CONFIG_MEM_LINUX_MMZ_HIGO_SIZE="0x05000000"

可以看到CONFIG_MEM_HUAWEILITE_MMZ_SIZE=0x10000000=256M,CONFIG_MEM_LINUX_MMZ_=0x03000000+0x05000000=128M.
这么大还不够?
那我们看看板端实际MMZ大小。
linux端查看MMZ命令如下,可以看到是128M,符合配置。

/ # cat /proc/media-mem
+---ZONE: PHYS(0x9B000000, 0x9FFFFFFF), GFP=1, nBYTES=81920KB,    NAME="higo_mmz"
+---ZONE: PHYS(0x98000000, 0x9AFFFFFF), GFP=0, nBYTES=49152KB,    NAME="anonymous"
   |-MMB: phys(0x98000000, 0x9801FFFF), kvirt=0x00000000, flags=0x00000000, length=128KB,    name="TDE_MEMPOOL_MMB"

---MMZ_USE_INFO:
 total size=131072KB(128MB),used=128KB(0MB + 128KB),remain=130944KB(127MB + 896KB),zone_number=2,block_number=1

liteos端查看MMZ命令如下,可以看到只有16M!!!

Huawei LiteOS # cat /proc/umap/media-mem
Huawei LiteOS # +---ZONE: PHYS(0x88000000, 0x88FFFFFF), GFP=0, nBYTES=16384KB,    NAME="anonymous"
   |-MMB: phys(0x88000000, 0x88007FFF), kvirt=0x88000000, flags=0x00000001, length=32KB,    name="sys_scale_coef"
   |-MMB: phys(0x88008000, 0x88023FFF), kvirt=0x88008000, flags=0x00000001, length=112KB,    name="gdc_node_buf"
   |-MMB: phys(0x88024000, 0x88024FFF), kvirt=0x88024000, flags=0x00000001, length=4KB,    name="GDC int_pole_co"
   |-MMB: phys(0x88025000, 0x88031FFF), kvirt=0x88025000, flags=0x00000001, length=52KB,    name="VGS_NodeBuf"

---MMZ_USE_INFO:
 total size=16384KB(16MB),used=200KB(0MB + 200KB),remain=16184KB(15MB + 824KB),zone_number=1,block_number=4

我这边改了几次.config,也make clean;make all全编译,还是16M。后面报问题给海思,他们给了个命令,
make osdrv_clean;make osdrv;make drv_clean;make drv;cd reference;make samplecam_clean;make samplecam
这个命令编译出来正常,256M。可能是我环境的问题,大家如果遇到这种问题,也可以试试这个命令。

4.上面分析到是vb申请MMZ内存失败,MMZ太小的时候,由于一时没法调整大MMZ,所以分析如何减小申请的MMZ内存。
由2.2最开始的SAMPLE_VIO_StartViOnlineVpssOnlineRoute函数,可以看到申请的内存就是astCommPool[0]和astCommPool[1],由于vio_app例程能在我的板子上跑起来1280720,也是基于16M的MMZ,那为什么sample_vio这边跑1280720就不行呢?
对比sample_vio和vio_app 720P时候申请的内存
vio_app 申请 13996805+1228805=7M
sample_vio 申请 139968010+18432004=20M
sample_vio超过了16M,所以失败。为什么都是720P,sample_vio申请的空间会比vio_app大?
可以看到第一个size 1399680是一样的,只需要把个数改为5就能减小。
第二个size很大,需要把数据宽度PIXEL_FORMAT_RGB_BAYER_16BPP改为PIXEL_FORMAT_RGB_BAYER_8BPP,或者改为PIXEL_FORMAT_YVU_PLANAR_420,就会变小,申请的内存就够。

2.3 vpss dump frame

由于我们需要获取一帧数据用于人脸识别,VI-VPSS-VO整个流程里面,我们选择了VPSS这个阶段获取,因为这里能进行一些图像处理,例如放大缩小,所以就用到了HI_MPI_VPSS_GetChnFrame这个函数。
调试阶段,我们需要验证获取的帧是否正常,那么我们就在liteos端需要dump下一帧,并且保存为文件,然后电脑端用工具点开查看显示是否正常。
这里我找到了一个sdk自带的tool,amp/a7_liteos/mpp/tools/vpss_chn_dump.c,里面就有SAMPLE_MISC_VpssDump函数,负责从vpss dump一帧图像,保存yuv文件。把这个文件拷贝到我们要用到地方,然后调用这个SAMPLE_MISC_VpssDump接口。
其中会有些问题:
1.保存文件的路径 /sharefs/XXX 改为 /app/sharefs/XXX
2.需要先启动linux和liteos共享目录 /sharefs,开机后linux端运行:sharefs & ,这样共享目录就建立了。

成功获取了yuv文件之后,我们在电脑端可以用工具7yuv工具打开看。
在这里插入图片描述
怎么会是偏绿色,首先是怀疑摄像头驱动问题,排查了一轮没查出原因,后面配好了LCD显示屏,VO输出到显示器上,画面颜色是正常的。所以VI-VPSS-VO整个流程是没问题的,所以是我dump这边出的问题。
首先怀疑是不是数据个数读取存储出错了,用到的函数是sample_yuv_8bit_dump,对照文档YUV420SP的存储格式
在这里插入图片描述
sample_yuv_8bit_dump的逻辑过了一遍,也没问题。问题陷入了困境。
最后是怎么解决问题的呢?看2.4

2.4 YUV420SP转RGB

既然YUV显示偏绿色,那我dump一帧后转为RGB,再存图片看看是不是还是偏绿色咯,另外我肯定要做这个转化的,因为mtcnn模型支持的输入是RGB的。然后就了解到IVE模块支持这个颜色空间转化,HI_MPI_IVE_CSC这个接口

HI_VOID YUV2RGB(VIDEO_FRAME_S *stVFrame,cv::Mat& rgb_img )
{
    HI_S32 s32Ret = HI_FAILURE;
//YUV 2 RGB
    IVE_DST_IMAGE_S stDstData; //转换后
    IVE_SRC_IMAGE_S stSrcData; //转换前
    IVE_HANDLE hIveHandle;
    HI_BOOL bInstant = HI_TRUE;
    HI_BOOL bFinish = HI_FALSE;
    HI_BOOL bBlock = HI_TRUE;
    IVE_CSC_CTRL_S stCscCtrl;
    stCscCtrl.enMode = IVE_CSC_MODE_VIDEO_BT601_YUV2RGB;
    HI_U32 u32Size = 0;
    /* 颜色空间转换 */
    stSrcData.enType = IVE_IMAGE_TYPE_YUV420SP;   //广义图像的物理地址数组
    stSrcData.au64PhyAddr[0] = stVFrame->u64PhyAddr[0];  //0
    stSrcData.au64PhyAddr[1] = stVFrame->u64PhyAddr[1];  //
    stSrcData.au64VirAddr[0] = stVFrame->u64VirAddr[0];  //0
    stSrcData.au64VirAddr[1] = stVFrame->u64VirAddr[1];  //
    stSrcData.au32Stride[0] = stVFrame->u32Stride[0];  //
    stSrcData.au32Stride[1] = stVFrame->u32Stride[1];  //
    stSrcData.u32Width   = stVFrame->u32Width;//
    stSrcData.u32Height  = stVFrame->u32Height; //
    stDstData.enType = IVE_IMAGE_TYPE_U8C3_PACKAGE;
    stDstData.u32Width   = stSrcData.u32Width; //高宽同stSrcData
    stDstData.u32Height  = stSrcData.u32Height;
    stDstData.au32Stride[0]  = stVFrame->u32Stride[0]; //
    stDstData.au32Stride[1]  = stVFrame->u32Stride[1];
    stDstData.au32Stride[2]  = stVFrame->u32Stride[2];
    u32Size = stDstData.au32Stride[0] * stDstData.u32Height * 3;
    s32Ret = HI_MPI_SYS_MmzAlloc_Cached(&stDstData.au64PhyAddr[0], (HI_VOID**)&stDstData.au64VirAddr[0], "User", HI_NULL, u32Size);  //
    if(HI_SUCCESS != s32Ret)
    {
        HI_MPI_SYS_MmzFree(stSrcData.au64PhyAddr[0], (HI_VOID**)stSrcData.au64VirAddr[0]);
        return;
    }
    memset((HI_VOID**)stDstData.au64VirAddr[0], 0, u32Size);
    s32Ret = HI_MPI_SYS_MmzFlushCache(stDstData.au64PhyAddr[0], (HI_VOID**)stDstData.au64VirAddr[0], u32Size);
    if(HI_SUCCESS != s32Ret)
    {       
        printf("HI_MPI_SYS_MmzFlushCache FAILED,s32Ret=%#x\n",s32Ret);
        HI_MPI_SYS_MmzFree(stDstData.au64PhyAddr[0], (HI_VOID**)stDstData.au64VirAddr[0]);
        return;
    }
    /* 颜色空间转换 */
    s32Ret = HI_MPI_IVE_CSC(&hIveHandle,&stSrcData,&stDstData,&stCscCtrl,bInstant);
    if(HI_SUCCESS != s32Ret)
    {       
        printf("HI_MPI_IVE_CSC FAILED,s32Ret=%#x\n",s32Ret);
        HI_MPI_SYS_MmzFree(stDstData.au64PhyAddr[0], (HI_VOID**)stDstData.au64VirAddr[0]);
        return;
    }
    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);
    }
//=====================================================================
    rgb_img.create(stVFrame->u32Height, stVFrame->u32Width, CV_8UC3);
    memcpy(rgb_img.data, (unsigned char*)stDstData.au64VirAddr[0], u32Size);
    HI_MPI_SYS_MmzFree(stDstData.au64PhyAddr[0], (HI_VOID**)stDstData.au64VirAddr[0]);
}

之前我不明白为什么au64PhyAddr明明是定义了[3] 有三个的,为什么只赋值了0,1。这个可以看2.2上面YUV420SP的存储结构,Y存了一组,UV合在一起存在一组。另外由于是4:2,所以总的数据长度是Y长度的3/2。
好了这个接口在liteos上运行起来又出问题了:
HI_MPI_IVE_CSC失败,返回 0xa01d8010,对照文档查询就是:系统没有初始化或者没有加载相应的模块。
再在liteos初始化的文件里面搜索IVE初始化相关的代码。
在这里插入图片描述
找到了,ive模块初始化接口被SVP_BIND_LITEOS控制了,这个宏就是说SVP部署在liteos端还是linux端,默认是部署在linux端的,我们识别算法也是跑在linux端的,所以,,,,liteos端没有IVE,,,所以只能把这个转化的动作放在linux端来做了。
后面打通了datafifo之后,把YUV数据传到了linux,转为了RGB之后,颜色正常!!!,再看转RGB之前的YUV,颜色也正常!!!dump的函数和liteos端一模一样,可是出来的图片颜色不一样,好吧,不关我代码的事情了。

2.4.1 利用IVE转换出MMZ内存图像直接送给模型运算

检测模型有forward(cv::Mat &mat_img)接口,Mat图片需要转化为mmz内存传给模型Blob。需要增加forward(MMZIMG *mmz_img)接口,加速检测
在这里插入图片描述
问题:
mmz图像检测失败。摄像头传过来的YUV图像经过IVE转化为BGR图像后,一份拷贝给Mat,一份直接把mmz指针传给mmz_img。进入forward后,分别打印mmz_img.VirAddr数据和mat_img.data数据是一样的,可是只有后者能检测到人脸,前者检测不到。
定位问题:
forward传入的参数数据是一样的,打印实际模型输入Blob不一样,那就是img——Blob出现了异常。
原因:
Mat2Blob用的不是mat.data赋值,而是mat.atcv::Vec3b(h, w)[c]方式。
由下图Mat像素存储分布看,mat.data取值是顺序逐个逐个取,也就是BGRBGRBGR(rgb_package格式)。而后者取值方式是按通道取值,是BBBGGGRRR(rgb_planar格式)。所以mmz_img图像检测失败的原因就是数据顺序不对。
解决方法:mmz_img进行像素顺序调整后送入模型forward,问题解决。
在这里插入图片描述
然而,利用指针逐个调整mmz_img像素顺序,耗时较大。
继续分析优化,阅读IVE文档,发现如下说明:
在这里插入图片描述
在这里插入图片描述
看来IVE是支持这两种格式的,是否YUV2RGB就能直接转换出rgb_planar呢?答案是可以的。
只要配置:
stCscCtrl.enMode=IVE_CSC_MODE_PIC_BT601_YUV2RGB
stSrcData.enType=IVE_IMAGE_TYPE_YUV420SP
stDstData.enType=IVE_IMAGE_TYPE_U8C3_PLANAR
另外要注意:
如果使用IVE_IMAGE_TYPE_U8C3_PACKAGE,则stDstData只需要配置1组地址,因为package格式输出是BGRBGRBGR,只需要一个起始指针就行了
stDstData.au64PhyAddr[0]
stDstData.au64VirAddr[0]
如果使用IVE_IMAGE_TYPE_U8C3_PLANAR,则stDstData只需要配置3组地址,因为planar格式输出是分开3通道,分别存B,G,R,需要3通道地址。
stDstData.au64PhyAddr[0],stDstData.au64PhyAddr[1],stDstData.au64PhyAddr[2]
stDstData.au64VirAddr[0],stDstData.au64VirAddr[1],stDstData.au64VirAddr[2]
否则会出现HI_MPI_IVE_CSC 0xa01d8003 参数非法的报错。

2.5 datafifo使用

2.4最后提到,需要把从VPSS获取的一帧图像,传到linux端处理,这就要用到datafifo了。
先去看官方文档:《HiSysLink API 开发参考.pdf》
了解完原理之后就看看SDK的demo代码:
amp/a7_liteos/mpp/sample/common/media_msg/client/sample_msg_venc.c
amp/a7_liteos/mpp/sample/common/media_msg/server/sample_msg_venc.c
里面就有客户端和服务端的datafifo启动,写数据,读数据逻辑。
在这里插入图片描述
之前出现过个问题,运行一段时间之后,客户端会报错:

==========================getOneFrame==========================
jjccmmz_userdev:ioctl_mmb_alloc:  getOneFrame
jjcc pu64PhyAddr=2291351552,g_DatafifoHandle=2291351552,readLen=328
[Func]:HI_MPI_SYmmz_userdev:get_mmbinfo_safe: S_MmzAlloc_Cached [Line]:936 [Info]:System alloc mmz memory failed!
HI_MPI_SYS_MmzAlloc_Cached FAILED,s32Ret=0xffffffff
[Func]:HI_MPI_SYS_MmzFree [Line]:969 [Info]:System unmap mmz memory failed!
HI_MPI_SYS_MmzFree,s32Ret=0xffffffff

又是MMZ内存失败,通过linux端查看mmz命令:cat /proc/media-mem 发现,MMZ剩余空间逐渐变小,原来是出现了内存泄漏,后面查到是调用了HI_MPI_SYS_MmzAlloc_Cached结束后没有调用HI_MPI_SYS_MmzFree释放空间,修复了就好了。

2.5 VO显示屏配置

1.对于vio_app例程来说
屏幕初始化流程:https://www.cnblogs.com/linhaostudy/p/11077703.html
屏幕驱动路径:amp/a7_liteos/drv/extdrv/screen/st7789/libhi_ssp_st7789.a
加载屏幕驱动:reference/samplecam/modules/init/amp/liteos/Makefile

else ifeq ($(CONFIG_SCREEN_ST7789),y)
VSS_LIB += -lhi_ssp_st7789 -lhalscreen_st7789_server

.config配置屏幕类型

CONFIG_SCREEN_ST7789=y

app初始化屏幕:
reference/samplecam/app/vio/vio_main.c

VIO_LcdPreview
    HI_HAL_SCREEN_Register(HI_HAL_SCREEN_IDX_0, &stHALSCREENObj)

stHALSCREENObj来自于reference/hal/screen/st7789/hal_screen_st7789.c

HI_HAL_SCREEN_OBJ_S stHALSCREENObj
    HAL_SCREEN_ST7789_GetAttr

2.对于sample_viol例程

/*config vo*/
    SAMPLE_COMM_VO_GetDefConfig(&stVoConfig);
    stVoConfig.enDstDynamicRange = enDynamicRange;
    stVoConfig.enVoIntfType = VO_INTF_LCD_6BIT;
    stVoConfig.enIntfSync = VO_OUTPUT_240x320_50;
    stVoConfig.enPicSize = enPicSize;
    //HI_MPI_VO_SetChnRotation(stVoConfig.VoDev,0,ROTATION_90);//旋转
    /*start vo*/
    s32Ret = SAMPLE_COMM_VO_StartVO(&stVoConfig);
    /*vpss bind vo*/
    s32Ret = SAMPLE_COMM_VPSS_Bind_VO(VpssGrp, VpssChn, stVoConfig.VoDev, VoChn);
  • 8
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值