当前位置:网站首页>海思3559万能平台搭建:在截获的YUV图像上画框
海思3559万能平台搭建:在截获的YUV图像上画框
2022-07-02 22:09:00 【快跑bug来啦】
前言
万里长征第二步,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存放方式,就可以暴力进行修改了!
反正我们也知道了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格式的存储
边栏推荐
- Unity publishes a method of webgl playing sound
- go 多线程数据搜索
- 位的高阶运算
- 从2022年Q1财报看携程的韧性和远景
- 分享 10 个 JS 闭包面试题(图解),进来看看你能答对多少
- I admire that someone explained such an obscure subject as advanced mathematics so easily
- Uniapp wechat login returns user name and Avatar
- go 4种单例模式
- kubernetes 使用主机名将 pod 分配在指定节点上
- 【板栗糖GIS】global mapper 如何通过dsm批量制作贴地等高线
猜你喜欢
Developers share | HLS and skillfully use Axi_ Customize the master bus interface instructions and improve the data bandwidth - area exchange speed
UE4 game architecture learning notes
World Environment Day | Chow Tai Fook serves wholeheartedly to promote carbon reduction and environmental protection
Share 10 JS closure interview questions (diagrams), come in and see how many you can answer correctly
The kth largest element in the [leetcode] array [215]
从2022年Q1财报看携程的韧性和远景
Webrtc audio and video capture and playback examples and mediastream media stream analysis
小鹏P7出事故,安全气囊未弹出,这正常吗?
Oracle-游标
数据分析学习记录(二)---响应曲面法及Design-Expert的简单使用
随机推荐
数组进阶提高
PHP implements querying the data matching the date of birth according to the entered age
小鹏P7出事故,安全气囊未弹出,这正常吗?
电商系统微服务架构
从2022年Q1财报看携程的韧性和远景
Oracle-PL/SQL编程
Analyse des données dossiers d'apprentissage - - analyse simple de la variance à facteur unique avec Excel
中国信通院、清华大学、腾讯安全,云原生安全产学研用强强联合!
大话云原生之负载均衡篇-小饭馆客流量变大了
Simpleitk use - 3 Common operations
[ODX studio edit PDX] -0.1- how to quickly view the differences in supported diagnostic information between variant variants (service, sub function...)
Using rendertext() to output multiple lines of text with rendertext() in R shiny
Qt QProgressBar详解
【外刊】睡眠与减肥
【喜欢的诗词】好了歌
Share 10 JS closure interview questions (diagrams), come in and see how many you can answer correctly
NC50965 Largest Rectangle in a Histogram
佩服,竟然有人把高等数学这么晦涩难懂的科目,讲解得如此通俗易懂
World Environment Day | Chow Tai Fook serves wholeheartedly to promote carbon reduction and environmental protection
[leetcode] reverse the word III in the string [557]