海思3559万能平台搭建:在截获的YUV图像上画框

简介: 在截获的YUV图像上画框

前言

 万里长征第二步,YUV的认识和编码还在进行中,熟悉了YUV格式的原理和储存方式后,我们就可以结合第一步中从vpss通道截获的YUV图像上尝试修改,叠加自己的算法,先简单粗暴的改改,后续在替换不是,毕竟咱是“万能”平台,嘿嘿

准备

 一般来说,海思的图像存储方式由下面一些参数决定(可以参考VIDEO_FRAME_S)

HI_U32 u32Width;
HI_U32 u32Height;
VIDEO_FIELD_E enField;
PIXEL_FORMAT_E enPixelFormat;
VIDEO_FORMAT_E enVideoFormat;
COMPRESS_MODE_E enCompressMode;

 其中enPixelFormat 决定了一个像素是YUV还是RGB,是planar还是packet,也就是我们上一篇着重介绍YUV图片的各种采样和存放的排列组合

  enVideoFormat 决定了一个图的像素是按什么方式摆放,比如linear, tile等等

  enCompressMode 定义视频压缩数据格式结构体,决定了我们的图像有没有压缩,理论上的第100个像素信息还是不是第100个(压缩完肯定变小了啊)

 如果要想看懂内存里面的图像数据,就必须按对应的格式来

 一般来说 用

 enVideoFormat = VIDEO_FORMAT_LINEAR

 enCompressMode = COMPRESS_MODE_NONE

 然后再选一个enPixelFormat,就是一般软件上用的图像存储格式

 图像存储格式是指像素的摆放方式,和我们上一篇提及的存储毫无关系,是并不牵扯到一个像素的存储方式比如packet/planar,比如说linear,内存里面的一行的像素点,就是代表图像一行的像素点,而不管一个像素点在实际在内存中可能是放在一起的(packet),还是分开的两个部分(semi-planar),或者3个部分(planar)

而tile是为了硬件处理更高效的摆放方式,通常是把实际图像中的一块,比如是16x16或者更大(通常都是16x16的倍数)的像素放成一行,如果按linear的方式去看就不是原来的图像了

HI_MPI_SYS_Mmap

 海思的坑还是比较奇怪,我们在第一步的时候HI_MPI_VPSS_GetChnFrame就已经吧所有的图像信息全部保存到了结构体VIDEO_FRAME_INFO_S中,按照常识来说图像数据的虚拟地址里应该就开始存放图片信息了,但是很遗憾,结构体里其他的成员暂时还没发现异常,这个相当重要的虚拟地址反而搞起了幺蛾子,这个虚拟地址其实是不可用的!手册的注意事项也没有提及(暂时没找见)!是需要我们根据结构体里的物理地址重新HI_MPI_SYS_Mmap的!为了避免第一步时掉的坑,对应的HI_MPI_SYS_Munmap自然也必不可少(海思里面有的释放并不是强制的,后面osd部分会提到)

typedef struct hiVIDEO_FRAME_S {
    HI_U32              u32Width;           //图像宽度。
    HI_U32              u32Height;          //图像高度。
    VIDEO_FIELD_E       enField;            //帧场模式。
    PIXEL_FORMAT_E      enPixelFormat;      //视频图像像素格式
    VIDEO_FORMAT_E      enVideoFormat;      //视频图像格式。
    COMPRESS_MODE_E     enCompressMode;     //视频压缩模式。
    DYNAMIC_RANGE_E     enDynamicRange;     //动态范围。
    COLOR_GAMUT_E       enColorGamut;       //色域范围
    HI_U32              u32HeaderStride[3]; //图像压缩头跨距。
    HI_U32              u32Stride[3];       //图像数据跨距。
    HI_U32              u32ExtStride[3];    //10bit 数据位宽的图像,有些数据格式的存储方式是前8bit 和后 2bit 分开存储,这里指后 2bit 数据跨距。
    HI_U64              u64HeaderPhyAddr[3];//压缩头物理地址
    HI_U64              u64HeaderVirAddr[3];//压缩头虚拟地址。内核态虚拟地址
    HI_U64              u64PhyAddr[3];      //图像数据物理地址。
    HI_U64              u64VirAddr[3];      //图像数据虚拟地址。内核态虚拟地址。
    HI_U64              u64ExtPhyAddr[3];   //10bit 数据位宽的图像,有些数据格式的存储方式是前8bit 和后 2bit 分开存储,这里指后 2bit 数据的物理地址。
    HI_U64              u64ExtVirAddr[3];   //10bit 数据位宽的图像,有些数据格式的存储方式是前8bit 和后 2bit 分开存储,这里指后 2bit 数据的虚拟地址。内核态虚拟地址。
    HI_S16              s16OffsetTop;       /* 图像顶部剪裁宽度top offset of show area */
    HI_S16              s16OffsetBottom;    /* 图像底部剪裁宽度。bottom offset of show area */
    HI_S16              s16OffsetLeft;      /* 图像底部剪裁宽度。left offset of show area */
    HI_S16              s16OffsetRight;     /* 图像右侧剪裁宽度。right offset of show area */
    HI_U32              u32MaxLuminance;    //显示图像的最大亮度。
    HI_U32              u32MinLuminance;    //显示图像的最小亮度。
    HI_U32              u32TimeRef;         //图像帧序列号。
    HI_U64              u64PTS;             //图像时间戳
    HI_U64              u64PrivateData;     //私有数据。
    HI_U32              u32FrameFlag;       /* 当前帧的标记,使用 FRAME_FLAG_E 里面的值标记,可以按位或操作。FRAME_FLAG_E, can be OR operation. */
    VIDEO_SUPPLEMENT_S  stSupplement;       //图像的补充信息。
} VIDEO_FRAME_S;
/* 定义视频图像帧信息结构体 */
typedef struct hiVIDEO_FRAME_INFO_S {
    VIDEO_FRAME_S stVFrame; //视频图像帧。
    HI_U32        u32PoolId;//视频缓存池 ID。
    MOD_ID_E      enModId;//当前帧数据是由哪一个硬件逻辑模块写出的
} VIDEO_FRAME_INFO_S;

y,v,u的偏移计算

 有了正确的代码里可用的base addr,结合上一篇的YVU420SP存放方式,就可以暴力进行修改了!

1db8ca9f7f9e40bcbf7a762177fd8f93.png

 反正我们也知道了YUV的取值范围,随便赋个值,uv的数量分别是四分之一y的数量,且交替进行,那么v的高和y相比就是二分之一,宽和y相比,如果对应y的宽是w的话,v的宽自然是w-w%2,u是v在偏移一位

unsigned char* yuvData = pVirAddr;
            offset = u32Stride*u32Height;
            /*YUV420SPtest ok */
            for (h = 300; h < (u32Height-500); h++) 
            {
                for (w = 300; w < (u32Stride-500); w++,yIndex++) 
                {   
                    uvIndex = (h / 2) * u32Stride + w - w % 2;
                    yIndex = h*u32Stride+w;
                    yuvData[yIndex] =210;
                    // y = yuvData[yIndex] & 0xff ;
                    // printf("y is %d \r\n",y);
                    yuvData[offset + uvIndex] = 200;
                    // u = yuvData[offset + uvIndex] & 0xff;
                    // printf("u is %d \r\n",u);
                    yuvData[offset + uvIndex + 1] =200;
                    // v = yuvData[offset + uvIndex + 1] & 0xff;
                    // printf("v is %d \r\n",v);
                }
            }

最终代码

 编写我们最终的画框“算法”线程吧

/* 
 *描述  :线程里用于处理demo并保存图像信息
 *参数  :arg 为自定义结构video_process_s,VPSS_GRP和VPSS_CHN用于传参给HI_MPI_VPSS_GetChnFrame
 *返回值:无
 *注意  :HI_MPI_VPSS_GetChnFrame完必须释放,否则再次获取VB会报错
          VIDEO_FRAME_INFO_S里的虚拟地址不可以直接使用,必须通过物理地址HI_MPI_SYS_Mmap映射后才可以作为数据存放地址用
          用完同样需要HI_MPI_SYS_Munmap解映射
 */
HI_VOID deal_myself_yuvdump(HI_VOID *arg)
{
    // HI_S32 y,u,v;
    HI_CHAR szYuvName[128];
    HI_S32 s32Ret;
    HI_S32 h,w,offset,yIndex,uvIndex;
    HI_VOID *pVirAddr;
    HI_U32 u32Height, u32Stride;
    VIDEO_FRAME_INFO_S stVideoFrame;
    VIDEO_FRAME_INFO_S* pstVideoFrame = &stVideoFrame;
    video_process_s* pstPara;
    pstPara = (video_process_s*)arg;
    while(1)
        {
        s32Ret = HI_MPI_VPSS_GetChnFrame(pstPara->VpssGrp, pstPara->VpssChn, &stVideoFrame,1000);
        if(s32Ret != HI_SUCCESS)
        {
            // SAMPLE_PRT("VPSS_GetChnFrame err for %#x!\n",s32Ret);//while1打印太多
        }
        // printf("u32TimeRef is %d\r\n",pstVideoFrame->stVFrame.u32TimeRef);
        else
        {
            // printf("enPixelFormat is %d\r\n",pstVideoFrame->stVFrame.enPixelFormat);
            // SAMPLE_PRT("VPSS_GetChnFrame success for %#x!\n",s32Ret);
            u32Height = pstVideoFrame->stVFrame.u32Height;
            u32Stride = pstVideoFrame->stVFrame.u32Stride[0];
            pVirAddr = NULL;
            pVirAddr = (unsigned char *)HI_MPI_SYS_Mmap((unsigned int)pstVideoFrame->stVFrame.u64PhyAddr[0], u32Stride*u32Height*3/2);
            snprintf(szYuvName, 128, "./YUV/YUV_chn%d_%dx%d.yuv",
                    pstPara->VpssChn,stVideoFrame.stVFrame.u32Width, stVideoFrame.stVFrame.u32Height);
            // printf("Dump YUV frame of AVS chn  to file: \"%s\"\n",  szYuvName);
            fflush(stdout);
            pfd = fopen(szYuvName, "wb");
            unsigned char* yuvData = pVirAddr;
            offset = u32Stride*u32Height;
            /*YUV420SPtest ok */
            for (h = 300; h < (u32Height-500); h++) 
            {
                for (w = 300; w < (u32Stride-500); w++,yIndex++) 
                {   
                    uvIndex = (h / 2) * u32Stride + w - w % 2;
                    yIndex = h*u32Stride+w;
                    yuvData[yIndex] =210;
                    // y = yuvData[yIndex] & 0xff ;
                    // printf("y is %d \r\n",y);
                    yuvData[offset + uvIndex] = 200;
                    // u = yuvData[offset + uvIndex] & 0xff;
                    // printf("u is %d \r\n",u);
                    yuvData[offset + uvIndex + 1] =200;
                    // v = yuvData[offset + uvIndex + 1] & 0xff;
                    // printf("v is %d \r\n",v);
                }
            }
            /* Y test ok*/
            // for(h = 300;h < (u32Height-1000);h++)
            // {
            //     for(w =300; w < (u32Stride-1000); w++)
            //         {
            //         Xil_Out8(pVirAddr+(h*u32Stride+w),200);
            //         // Xil_Out8(pVirAddr+27649+((h*u32Stride+w)/2),200);
            //         // value = Xil_In8(pVirAddr+u32offsetv+(h*u32Stride+w));  
            //         // printf("%d.%dpVirAddr number is 0x%x\r\n",h,w,value);
            //         }
            // }
            sample_yuv_8bit_dump(&stVideoFrame.stVFrame, pfd);//按标准YUV格式存为文件
            HI_MPI_SYS_Munmap((void*)pVirAddr, u32Stride*u32Height*3/2); 
            // printf("video process success\r\n");
            s32Ret = HI_MPI_VENC_SendFrame(pstPara->VpssChn, &stVideoFrame,1000);
            // printf("after send enPixelFormat is %d\r\n",pstVideoFrame->stVFrame.enPixelFormat);
            // if(s32Ret != HI_SUCCESS)
            // {
            //     SAMPLE_PRT("HI_MPI_VENC_SendFrame err for %#x!\n",s32Ret);//while1打印太多
            //     //  goto EXIT_VENC_H264_STOP;
            // }
            // else
            // {
            //     cnt++;
            //     // SAMPLE_PRT("HI_MPI_VENC_SendFrame success %d!\n",cnt);  
            // }
            // printf("u32TimeRef is %d\r\n",pstVideoFrame->stVFrame.u32TimeRef);
            HI_MPI_VPSS_ReleaseChnFrame(pstPara->VpssGrp, pstPara->VpssChn, &stVideoFrame);
        }
    } 
    // EXIT_VENC_H264_STOP:
    // SAMPLE_COMM_VENC_Stop(pstPara->VpssChn);
    // SAMPLE_COMM_SYS_Exit();
}

参考sample自带的工具,移植yuv图片的保存函数

HI_U32 u32Size = 0;
FILE* pfd = HI_NULL;
unsigned char* TmpBuff = HI_NULL;
HI_CHAR* pUserPageAddr[2] = {HI_NULL, HI_NULL};
/* 
 *描述  :用于保存8bit yuv图片信息
 *参数  :pVBuf  图像信息的结构体 
 *        pfd    用于保存文件的指针
 *返回值:无
 *注意  :保存后的格式是标准YUV420格式 
 */
HI_VOID sample_yuv_8bit_dump(VIDEO_FRAME_S* pVBuf, FILE* pfd)
{
    unsigned int w, h;
    char* pVBufVirt_Y;
    char* pVBufVirt_C;
    char* pMemContent;
    HI_U64 phy_addr;
    PIXEL_FORMAT_E  enPixelFormat = pVBuf->enPixelFormat;
    HI_U32 u32UvHeight;
    TmpBuff = (unsigned char*)malloc(MAX_FRM_WIDTH);
    if (NULL == TmpBuff)
    {
        printf("malloc fail !\n");
        return;
    }
    if (PIXEL_FORMAT_YVU_SEMIPLANAR_420 == enPixelFormat)
    {
        u32Size = (pVBuf->u32Stride[0]) * (pVBuf->u32Height) * 3 / 2;
        u32UvHeight = pVBuf->u32Height / 2;
    }
    else if (PIXEL_FORMAT_YVU_SEMIPLANAR_422 == enPixelFormat)
    {
        u32Size = (pVBuf->u32Stride[0]) * (pVBuf->u32Height) * 2;
        u32UvHeight = pVBuf->u32Height;
    }
    else
    {
        u32Size = (pVBuf->u32Stride[0]) * (pVBuf->u32Height);
        u32UvHeight = pVBuf->u32Height;
    }
    phy_addr = pVBuf->u64PhyAddr[0];
    pUserPageAddr[0] = (HI_CHAR*) HI_MPI_SYS_Mmap(phy_addr, u32Size);
    if (HI_NULL == pUserPageAddr[0])
    {
        free(TmpBuff);
        TmpBuff = HI_NULL;
        return;
    }
    pVBufVirt_Y = pUserPageAddr[0];
    pVBufVirt_C = pVBufVirt_Y + (pVBuf->u32Stride[0]) * (pVBuf->u32Height);
    /* save Y ----------------------------------------------------------------*/
    // fprintf(stderr, "saving......Y......");
    fflush(stderr);
    for (h = 0; h < pVBuf->u32Height; h++)
    {
        pMemContent = pVBufVirt_Y + h * pVBuf->u32Stride[0];
        fwrite(pMemContent, pVBuf->u32Width, 1, pfd);
    }
    if (PIXEL_FORMAT_YUV_400 != enPixelFormat)
    {
        fflush(pfd);
        /* save U ----------------------------------------------------------------*/
        // fprintf(stderr, "U......");
        fflush(stderr);
        for (h = 0; h < u32UvHeight; h++)
        {
            pMemContent = pVBufVirt_C + h * pVBuf->u32Stride[1];
            pMemContent += 1;
            for (w = 0; w < pVBuf->u32Width / 2; w++)
            {
                TmpBuff[w] = *pMemContent;
                pMemContent += 2;
            }
            fwrite(TmpBuff, pVBuf->u32Width / 2, 1, pfd);
        }
        fflush(pfd);
        /* save V ----------------------------------------------------------------*/
        // fprintf(stderr, "V......");
        fflush(stderr);
        for (h = 0; h < u32UvHeight; h++)
        {
            pMemContent = pVBufVirt_C + h * pVBuf->u32Stride[1];
            for (w = 0; w < pVBuf->u32Width / 2; w++)
            {
                TmpBuff[w] = *pMemContent;
                pMemContent += 2;
            }
            fwrite(TmpBuff, pVBuf->u32Width / 2, 1, pfd);
        }
    }
    fflush(pfd);
    // fprintf(stderr, "done %d!\n", pVBuf->u32TimeRef);
    fflush(stderr);
    HI_MPI_SYS_Munmap(pUserPageAddr[0], u32Size);
    pUserPageAddr[0] = HI_NULL;
    free(TmpBuff);
    TmpBuff = HI_NULL;
}

在不考虑对齐的情况下,自然大功告成啦!,也进一步验证了我们上一篇学习到的yuv格式的存储


相关文章
|
4月前
|
编解码 监控 网络协议
【绝密技巧】揭秘!如何用魔法般的步骤实现RTSP推送H.264与H.265(HEVC),打造震撼视听盛宴,让每一帧都充满魔力!
【8月更文挑战第15天】本文详述了如何使用RTSP流媒体服务推送H.264及H.265编码视频,适用于视频监控和直播平台。首先需确保环境支持这两种编码格式,可通过FFmpeg实现。在Ubuntu上安装FFmpeg后,可配置从摄像头捕获视频并推流至RTSP服务器。针对H.265编码,只需更改视频编码器为`libx265`。客户端可使用VLC播放器接收流。此外,还提供了C++示例代码用于自定义服务器实现,包括初始化上下文、打开编码器和循环编码视频帧。此教程旨在助力实现RTSP推送目标。
66 0
|
7月前
|
算法 安全 数据可视化
开题报告-基于嵌入式的数字水印叠加系统与实现
开题报告-基于嵌入式的数字水印叠加系统与实现
|
编解码 数据中心
你敢信,国内也有免费的高分辨率 DEM 数据?
你敢信,国内也有免费的高分辨率 DEM 数据?
291 0
你敢信,国内也有免费的高分辨率 DEM 数据?
|
传感器 安全 API
|
缓存 数据处理
海思3559万能平台搭建:协议的采集和解析
海思3559万能平台搭建:协议的采集和解析
172 0
海思3559万能平台搭建:协议的采集和解析
|
传感器 编解码 NoSQL
|
人工智能 算法 数据库
彩色条形码轻松解决盲人购物难题!无需对焦,识别速度快12倍,还能发出过敏警告
彩色条形码轻松解决盲人购物难题!无需对焦,识别速度快12倍,还能发出过敏警告
170 0
下一篇
无影云桌面