当前位置: 首页 > news >正文

海思3559万能平台搭建:OSD功能的优化

前言:

  功能测试的OSD使用还是比较简单的,随便找个位置做个时间戳,背景还是黑色,且只能显示一行,很明显效果并不是那么理想,这里做一个升级,对海思区域叠加的配置以及osd窗口的创建等都在本文一并写了。

OSD多行文字

  我们的位图需要20ms之内作一次更新,有了性能要求之后,就不能像之前一样随意的把创建位图并叠加笼统的放进循环,频繁的打开创建销毁了,虽然文件很小也不必每次都进行保存了。一个合适的做法是在最开始做个初始化,根据需要随时获得待更新的数据,最后退出的时候销毁
  那么多行文字怎么解决呢?最直接的想法肯定是生成好几个位图,拼成一整个,把整个的数据copy到region进行更新(更简单粗暴的就是多个region但是很明显没必要),那么怎么拼呢?是生成一个个位图后分别粘贴到全局的bitmap拼成大的,还是根源处解决呢…bitmap处需要大量计算,而且写起来也比较复杂,而根源处没有相应的库函数啊(SDL2更新了可以在字符串中加空格进行换行)

SDL_ConvertSurface和SDL_CreateRGBSurface

  我们之前一直在刚好生成大小合适的图像,因为SDL_ConvertSurface是库函数也没有深究以为没有暴露对应的函数接口给用户,实际上并不是的,我们可以参考该函数的更底层是怎么进行转换的

/* 
 * Convert a surface into the specified pixel format.
 */
SDL_Surface * SDL_ConvertSurface (SDL_Surface *surface,
					SDL_PixelFormat *format, Uint32 flags)
{
	SDL_Surface *convert;
	Uint32 colorkey = 0;
	Uint8 alpha = 0;
	Uint32 surface_flags;
	SDL_Rect bounds;

	/* Check for empty destination palette! (results in empty image) */
	if ( format->palette != NULL ) {
		int i;
		for ( i=0; i<format->palette->ncolors; ++i ) {
			if ( (format->palette->colors[i].r != 0) ||
			     (format->palette->colors[i].g != 0) ||
			     (format->palette->colors[i].b != 0) )
				break;
		}
		if ( i == format->palette->ncolors ) {
			SDL_SetError("Empty destination palette");
			return(NULL);
		}
	}

	/* Only create hw surfaces with alpha channel if hw alpha blits
	   are supported */
	if(format->Amask != 0 && (flags & SDL_HWSURFACE)) {
		const SDL_VideoInfo *vi = SDL_GetVideoInfo();
		if(!vi || !vi->blit_hw_A)
			flags &= ~SDL_HWSURFACE;
	}

	/* Create a new surface with the desired format */
	convert = SDL_CreateRGBSurface(flags,
				surface->w, surface->h, format->BitsPerPixel,
		format->Rmask, format->Gmask, format->Bmask, format->Amask);
	if ( convert == NULL ) {
		return(NULL);
	}

	/* Copy the palette if any */
	if ( format->palette && convert->format->palette ) {
		SDL_memcpy(convert->format->palette->colors,
				format->palette->colors,
				format->palette->ncolors*sizeof(SDL_Color));
		convert->format->palette->ncolors = format->palette->ncolors;
	}

	/* Save the original surface color key and alpha */
	surface_flags = surface->flags;
	if ( (surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) {
		/* Convert colourkeyed surfaces to RGBA if requested */
		if((flags & SDL_SRCCOLORKEY) != SDL_SRCCOLORKEY
		   && format->Amask) {
			surface_flags &= ~SDL_SRCCOLORKEY;
		} else {
			colorkey = surface->format->colorkey;
			SDL_SetColorKey(surface, 0, 0);
		}
	}
	if ( (surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
		/* Copy over the alpha channel to RGBA if requested */
		if ( format->Amask ) {
			surface->flags &= ~SDL_SRCALPHA;
		} else {
			alpha = surface->format->alpha;
			SDL_SetAlpha(surface, 0, 0);
		}
	}

	/* Copy over the image data */
	bounds.x = 0;
	bounds.y = 0;
	bounds.w = surface->w;
	bounds.h = surface->h;
	SDL_LowerBlit(surface, &bounds, convert, &bounds);

	/* Clean up the original surface, and update converted surface */
	if ( convert != NULL ) {
		SDL_SetClipRect(convert, &surface->clip_rect);
	}
	if ( (surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) {
		Uint32 cflags = surface_flags&(SDL_SRCCOLORKEY|SDL_RLEACCELOK);
		if ( convert != NULL ) {
			Uint8 keyR, keyG, keyB;

			SDL_GetRGB(colorkey,surface->format,&keyR,&keyG,&keyB);
			SDL_SetColorKey(convert, cflags|(flags&SDL_RLEACCELOK),
				SDL_MapRGB(convert->format, keyR, keyG, keyB));
		}
		SDL_SetColorKey(surface, cflags, colorkey);
	}
	if ( (surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
		Uint32 aflags = surface_flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
		if ( convert != NULL ) {
		        SDL_SetAlpha(convert, aflags|(flags&SDL_RLEACCELOK),
				alpha);
		}
		if ( format->Amask ) {
			surface->flags |= SDL_SRCALPHA;
		} else {
			SDL_SetAlpha(surface, aflags, alpha);
		}
	}

	/* We're ready to go! */
	return(convert);
}

  不难看到,除去了大量的判断后关键的操作是SDL_CreateRGBSurface创建窗口(位图)和叠加SDL_LowerBlit,SDL_LowerBlit是可以指定位置的啊,

/* 
 * Set up a blit between two surfaces -- split into three parts:
 * The upper part, SDL_UpperBlit(), performs clipping and rectangle 
 * verification.  The lower part is a pointer to a low level
 * accelerated blitting function.
 *
 * These parts are separated out and each used internally by this 
 * library in the optimimum places.  They are exported so that if
 * you know exactly what you are doing, you can optimize your code
 * by calling the one(s) you need.
 */
int SDL_LowerBlit (SDL_Surface *src, SDL_Rect *srcrect,
				SDL_Surface *dst, SDL_Rect *dstrect)
{
	SDL_blit do_blit;
	SDL_Rect hw_srcrect;
	SDL_Rect hw_dstrect;

	/* Check to make sure the blit mapping is valid */
	if ( (src->map->dst != dst) ||
             (src->map->dst->format_version != src->map->format_version) ) {
		if ( SDL_MapSurface(src, dst) < 0 ) {
			return(-1);
		}
	}

	/* Figure out which blitter to use */
	if ( (src->flags & SDL_HWACCEL) == SDL_HWACCEL ) {
		if ( src == SDL_VideoSurface ) {
			hw_srcrect = *srcrect;
			hw_srcrect.x += current_video->offset_x;
			hw_srcrect.y += current_video->offset_y;
			srcrect = &hw_srcrect;
		}
		if ( dst == SDL_VideoSurface ) {
			hw_dstrect = *dstrect;
			hw_dstrect.x += current_video->offset_x;
			hw_dstrect.y += current_video->offset_y;
			dstrect = &hw_dstrect;
		}
		do_blit = src->map->hw_blit;
	} else {
		do_blit = src->map->sw_blit;
	}
	return(do_blit(src, srcrect, dst, dstrect));
}


  那么我们就可以将TTF_RenderUTF8_Solid转位图格式后的surface按照不同的宽高叠进来,把时间信息osd_time_text,脱靶信息osd_losttarget_info_text,其他信息osd_other_info_text经过TTF_RenderUTF8_Solid转位图后设置不同的宽高SDL_LowerBlit到整个osd_bottom_left 中,在hi_memcpy到海思的结构体bitmap中用于更新

/* 
 *描述  :用于将想填写的内容生成位图左下角 用于pthread osd_create_task
 *参数  :pu8Str_losttargrt_inf 传字符串脱靶信息
 *        pu8Str_other_info 传字符串视场角信息和焦距
 *        pu8Str_time u8指针,传字符串时间
 *返回值:成功返回0
 *注意  :无
 */
 HI_S32 string_to_bmp_bottom_left(char *pu8Str_losttargrt_info,char *pu8Str_other_info,char *pu8Str_time)
{
    SDL_Rect bounddst,boundsrc;
    // printf("before TTF_RenderUTF8_Solid\n");
    osd_time_text            = TTF_RenderUTF8_Solid(font, pu8Str_time, forecol);
    osd_losttarget_info_text = TTF_RenderUTF8_Solid(font, pu8Str_losttargrt_info, forecol);
    osd_other_info_text      = TTF_RenderUTF8_Solid(font, pu8Str_other_info, forecol);
    // osd_bottom_left = SDL_ConvertSurface(osd_time_text,osd_fmt,0);
    osd_bottom_left = SDL_CreateRGBSurface(SDL_SWSURFACE, osd_time_text->w, osd_time_text->h*3, 
                                    osd_fmt->BitsPerPixel,osd_fmt->Rmask, osd_fmt->Gmask, osd_fmt->Bmask, osd_fmt->Amask);
    // printf ("w is %d ,h is %d\n",osd_time_text->w,osd_time_text->h);
    // hi_memset(osd_bottom_left, sizeof(SDL_Surface),0, sizeof(SDL_Surface));
   
    boundsrc.x = 0;
	boundsrc.y = 0;
	boundsrc.w = osd_losttarget_info_text->w;
	boundsrc.h = osd_losttarget_info_text->h;
    bounddst.x = 0;
	bounddst.y = 0;
	bounddst.w = osd_losttarget_info_text->w;
	bounddst.h = osd_losttarget_info_text->h;
	SDL_LowerBlit(osd_losttarget_info_text, &boundsrc, osd_bottom_left, &bounddst);
	boundsrc.x = 0;
	boundsrc.y = 0;
	boundsrc.w = osd_other_info_text->w;
	boundsrc.h = osd_other_info_text->h;
    bounddst.x = 0;
	bounddst.y = osd_losttarget_info_text->h;
	bounddst.w = osd_other_info_text->w;
	bounddst.h = osd_other_info_text->h;
	SDL_LowerBlit(osd_other_info_text, &boundsrc, osd_bottom_left, &bounddst);
    boundsrc.x = 0;
	boundsrc.y = 0;
	boundsrc.w = osd_time_text->w;
	boundsrc.h = osd_time_text->h;
    bounddst.x = 0;
	bounddst.y = osd_losttarget_info_text->h+osd_other_info_text->h;
	bounddst.w = osd_time_text->w;
	bounddst.h = osd_time_text->h;
	SDL_LowerBlit(osd_time_text, &boundsrc, osd_bottom_left, &bounddst);
    // printf ("w is %d ,h is %d\n",osd_bottom_left->w,osd_bottom_left->h);
    // stBitmap_bottom_left.pData = malloc(2*(osd_bottom_left->w)*(osd_bottom_left->h));
    // if(stBitmap_bottom_left.pData == NULL)
    // {
    // printf("stBitmap.pData faided\r\n");
    // }
    // pthread_mutex_lock(&mutex);
    hi_memset(stBitmap_bottom_left.pData, (2*(osd_bottom_left->w)*(osd_bottom_left->h)),0, (2*(osd_bottom_left->w)*(osd_bottom_left->h)));
	hi_memcpy(stBitmap_bottom_left.pData, (2*(osd_bottom_left->w)*(osd_bottom_left->h)),osd_bottom_left->pixels, (2*(osd_bottom_left->w)*(osd_bottom_left->h)));
    // pthread_mutex_unlock(&mutex);
    stBitmap_bottom_left.u32Width = osd_bottom_left->w;
    stBitmap_bottom_left.u32Height = osd_bottom_left->h;  
    // printf ("stBitmap_bottom_left is %d ,h is %d\n",osd_bottom_left->w,osd_bottom_left->h);//446,90

    // char savename[20] = {0};
    // snprintf(savename,20,"./osd/now_time.bmp");
    // printf("before SDL_SaveBMP\n");
    // SDL_SaveBMP(osd_bottom_left, savename); 
    
    // memset(stBitmap.pData, 0, sizeof(BITMAP_S));
    SDL_FreeSurface(osd_time_text);  
    SDL_FreeSurface(osd_losttarget_info_text); 
    SDL_FreeSurface(osd_other_info_text); 
    SDL_FreeSurface(osd_bottom_left);
    // TTF_CloseFont(font);  
    // TTF_Quit();  
 
    return 0;
}
/* 
 *描述  :用于将想填写的内容生成位图左上角 用于pthread osd_create_task
 *参数  :pu8Str_angle_info 跟踪器状态(检测/识别/跟踪)
 *        pu8Str_other_info 帧号
 *返回值:成功返回0
 *注意  :无
 */
HI_S32 string_to_bmp_top_left(char *pu8Str_trk_info,char *pu8Str_frame_info)
{
    
    // SDL_Color forecol = { 0xff, 0xff, 0xff, 0xff };
    SDL_Rect bounddst,boundsrc;
    // printf("before TTF_RenderUTF8_Solid\n");
   
    osd_trk_info_text        = TTF_RenderUTF8_Solid(font, pu8Str_trk_info, forecol);
    osd_frame_info_text      = TTF_RenderUTF8_Solid(font, pu8Str_frame_info, forecol);
    // osd_top_left = SDL_ConvertSurface(osd_time_text,osd_fmt,0);
    osd_top_left = SDL_CreateRGBSurface(SDL_SWSURFACE, osd_trk_info_text->w, osd_trk_info_text->h*2, 
                                    osd_fmt->BitsPerPixel,osd_fmt->Rmask, osd_fmt->Gmask, osd_fmt->Bmask, osd_fmt->Amask);//
    // hi_memset(osd_top_left, sizeof(SDL_Surface),0, sizeof(SDL_Surface));//也可以选择初始化的时候建一次,每次memset,最后释放,但是直接memset无法调用SDL_LowerBlit
    boundsrc.x = 0;
	boundsrc.y = 0;
	boundsrc.w = osd_trk_info_text->w;
	boundsrc.h = osd_trk_info_text->h;
    bounddst.x = 0;
	bounddst.y = 0;
	bounddst.w = osd_trk_info_text->w;
	bounddst.h = osd_trk_info_text->h;
	SDL_LowerBlit(osd_trk_info_text, &boundsrc, osd_top_left, &bounddst);
	boundsrc.x = 0;
	boundsrc.y = 0;
	boundsrc.w = osd_frame_info_text->w;
	boundsrc.h = osd_frame_info_text->h;
    bounddst.x = 0;
	bounddst.y = osd_trk_info_text->h;
	bounddst.w = osd_frame_info_text->w;
	bounddst.h = osd_frame_info_text->h;
	SDL_LowerBlit(osd_frame_info_text, &boundsrc, osd_top_left, &bounddst);
    // stBitmap_top_left.pData = malloc(2*(osd_top_left->w)*(osd_top_left->h));
    // if(stBitmap_top_left.pData == NULL)
    // {
    // printf("stBitmap_top_left.pData faided\r\n");
    // }
    // printf ("osd_top_left is %d ,h is %d\n",osd_top_left->w,osd_top_left->h);
    // pthread_mutex_lock(&mutex );
    hi_memset(stBitmap_top_left.pData, (2*(osd_top_left->w)*(osd_top_left->h)),0, (2*(osd_top_left->w)*(osd_top_left->h)));
	hi_memcpy(stBitmap_top_left.pData, (2*(osd_top_left->w)*(osd_top_left->h)),osd_top_left->pixels, (2*(osd_top_left->w)*(osd_top_left->h)));
    // pthread_mutex_unlock(&mutex );
    // memcpy(stBitmap.pData+(2*(osd_top_left->w)*(osd_top_left->h)), osd_top_left->pixels, (2*(osd_top_left->w)*(osd_top_left->h)));
  
    stBitmap_top_left.u32Width = osd_top_left->w;
    stBitmap_top_left.u32Height = osd_top_left->h;  

    // char savename[20] = {0};
    // snprintf(savename,20,"./osd/now_time.bmp");
    // printf("before SDL_SaveBMP\n");
    // SDL_SaveBMP(osd_top_left, savename); 
    
    // memset(stBitmap_top_left.pData, 0, sizeof(BITMAP_S));
    SDL_FreeSurface(osd_trk_info_text);  
    SDL_FreeSurface(osd_frame_info_text); 
    SDL_FreeSurface(osd_top_left);
    // TTF_CloseFont(font);  
    // TTF_Quit();  
 
    return 0;
}

  有需要的话可以吧保存放开

补充

  循环内我们每次都创建了新的SDL_CreateRGBSurface,必须对应的SDL_FreeSurface,不然过一会内存就会占满,卡在这里不往下进行,之后段错误啊,buserror(可能是memset bitmap数据时填错了长度,出现概率低,修改后暂未出现)out of memory之类的都出现过,不易排查。至于这个为什么不创建一次,最后free的原因是没有往下深入研究,不知道memset这个surface的结构体那部分,在长时间系统测试稳定和性能完全达标(不到5ms)后,暂时先这样处理

区域叠加的多行

在哪部分叠加

  之前的RGN叠加在venc通道,且只能叠加一个通道,一旦需要多路码流叠加,来回切通道会造成不必要的麻烦。mentor建议直接叠加到vpss上,之前没有选择叠加在vpss因为 stRgnAttr.enType选择了OVERLAY_RGN,这个模式不能在vpss叠加,选择COVEREX_RGN就可以解决

透明度

  选择了COVEREX_RGN后发现还没有做改动我们的背景色就由纯黑变成了浅黑,说明alpha值在这里生效了,那就更简单了,stChnAttr.unChnAttr.stOverlayChn.u32BgAlpha值改为0,我们的背景就变成透明的啦!

多个区域叠加

  这个也比较简单OverlayHandle选择不同值即可取值范围[0, RGN_HANDLE_MAX),创建不同区域分别叠加即可

叠加部分代码

/* 
 *描述  :用于将视频文件添加时间水印左下角
 *参数  :无
 *返回值:OverlayHandle_bottom_leftle
 *注意  :参数在HI_MPI_RGN_Create并不做检查,只有在HI_MPI_RGN_AttachToChn的时候才会报出相应的错
 */
HI_S32 RGN_AddOsdToVpss_bottom_left(HI_S32 s32ChnId)
{
    HI_S32 s32Ret;
    RGN_ATTR_S stRgnAttr;
    RGN_CHN_ATTR_S stChnAttr;
    MPP_CHN_S stChn;
   
    // RGN_CANVAS_INFO_S stCanvasInfo;
    stChn.enModId  = HI_ID_VPSS; /**模块号**///HI_ID_VPSS  HI_ID_VENC
    stChn.s32DevId = 0;          /**设备号**/
    stChn.s32ChnId = s32ChnId;          /**通道号**/
    /**创建区域**/
    sleep(2);//等待位图生成
    stRgnAttr.unAttr.stOverlay.u32CanvasNum = 1; 
    stRgnAttr.enType = OVERLAYEX_RGN;  /**区域类型:叠加**/
    stRgnAttr.unAttr.stOverlay.enPixelFmt       = PIXEL_FORMAT_ARGB_1555; /**像素格式**///PIXEL_FORMAT_BGR_565 PIXEL_FORMAT_ARGB_1555
    if (stBitmap_bottom_left.u32Width % 2 != 0)
        {
        stBitmap_bottom_left.u32Width += 1;
        }

    if (stBitmap_bottom_left.u32Height % 2 != 0)
        {
        stBitmap_bottom_left.u32Height += 1;
        }
    printf ("stBitmap_bottom_left.u32Width is %d ,stBitmap_bottom_left.u32Height is %d\n",stBitmap_bottom_left.u32Width,stBitmap_bottom_left.u32Height);
    stRgnAttr.unAttr.stOverlay.stSize.u32Width  = stBitmap_bottom_left.u32Width;//240;        /**区域宽**/
    stRgnAttr.unAttr.stOverlay.stSize.u32Height = stBitmap_bottom_left.u32Height;//192;        /**区域高**/
    stRgnAttr.unAttr.stOverlay.u32BgColor       = 0xffffff00;//0x00007c00; /**区域背景颜色**/

   
    s32Ret = HI_MPI_RGN_Create(OverlayHandle_bottom_left, &stRgnAttr);
    if(s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("RGN create failed: %#x\n", s32Ret);
    }
/**将区域叠加到通道**/
        /**设置叠加区域的通道显示属性**/
        stChnAttr.bShow  = HI_TRUE;
        stChnAttr.enType = OVERLAYEX_RGN;
        stChnAttr.unChnAttr.stOverlayChn.stPoint.s32X = 20;//240;
        stChnAttr.unChnAttr.stOverlayChn.stPoint.s32Y = 980;//192;
        stChnAttr.unChnAttr.stOverlayChn.u32BgAlpha   = 0;//128;
        stChnAttr.unChnAttr.stOverlayChn.u32FgAlpha   = 128;//80;
        stChnAttr.unChnAttr.stOverlayChn.u32Layer     = OverlayHandle_bottom_left;

        /**设置QP属性Qp(量化参数,反应压缩效果)以 H.264 编码为例,通常图像 Qp(量化参数,反应压缩效果) 越低,图像的质量越好,码率越高;图像 Qp 越高,图像质量越差,码率越低。**/
        stChnAttr.unChnAttr.stOverlayChn.stQpInfo.bAbsQp = HI_FALSE;
        stChnAttr.unChnAttr.stOverlayChn.stQpInfo.s32Qp  = 0;
        stChnAttr.unChnAttr.stOverlayChn.stQpInfo.bQpDisable = HI_FALSE;

        /**定义 OSD 反色相关属性**/
        /**单元反色区域,反色处理的基本单元,[16, 64],需 16 对齐**/
        stChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Height = 16;
        stChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Width  = 16;

        /**亮度阈值,取值范围:[0, 255]**/
        stChnAttr.unChnAttr.stOverlayChn.stInvertColor.u32LumThresh = 128;//128

        /**OSD 反色触发模式**/
        stChnAttr.unChnAttr.stOverlayChn.stInvertColor.enChgMod     = LESSTHAN_LUM_THRESH;

        /**OSD 反色开关。overlay不支持反色**/
        stChnAttr.unChnAttr.stOverlayChn.stInvertColor.bInvColEn    = HI_TRUE;

        stChnAttr.unChnAttr.stOverlayChn.enAttachDest =ATTACH_JPEG_MAIN;
        // OverlayHandle =0;
        s32Ret = HI_MPI_RGN_AttachToChn(OverlayHandle_bottom_left, &stChn, &stChnAttr);
        if(s32Ret != HI_SUCCESS)
        {
             SAMPLE_PRT("HI_MPI_RGN_AttachToChn: %#x\n", s32Ret);
        }       
        stBitmap_bottom_left.enPixelFormat = PIXEL_FORMAT_ARGB_1555;
        // stBitmap.u32Height = OVERLAY_H;
        // stBitmap.u32Width = OVERLAY_W;
   
        s32Ret = HI_MPI_RGN_SetBitMap(OverlayHandle_bottom_left,&stBitmap_bottom_left);
        if(s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("HI_MPI_RGN_SetBitMap failed with %#x!\n", s32Ret);
        }
        // hi_memset(stBitmap_bottom_left.pData, sizeof(BITMAP_S),0, sizeof(BITMAP_S));
        // s32Ret = HI_MPI_RGN_GetCanvasInfo(OverlayHandle,&stCanvasInfo);
       
        // s32Ret = HI_MPI_RGN_DetachFromChn(OverlayHandle, &stChn);//最后用户可以将该区域从通道中撤出(非必须操作),再销毁区域。
        // if(s32Ret != HI_SUCCESS)
        // {
        //      SAMPLE_PRT("HI_MPI_RGN_DetachFromChn: %#x\n", s32Ret);
        // }  
        // s32Ret = HI_MPI_RGN_Destroy(OverlayHandle);
        // if(s32Ret != HI_SUCCESS)
        // {
        //     SAMPLE_PRT("RGN create failed: %#x\n", s32Ret);
        // }
    return 0;
}
HI_S32 RGN_AddOsdToVpss_top_left(HI_S32 s32ChnId)
{
    HI_S32 s32Ret;
    RGN_ATTR_S stRgnAttr;
    RGN_CHN_ATTR_S stChnAttr;
    MPP_CHN_S stChn;

    // OverlayHandle_top_left =1;
    stChn.enModId  = HI_ID_VPSS; /**模块号**///HI_ID_VPSS  HI_ID_VENC
    stChn.s32DevId = 0;          /**设备号**/
    stChn.s32ChnId = s32ChnId;          /**通道号**/
    /**创建区域**/
    sleep(2);//等待位图生成
    stRgnAttr.unAttr.stOverlay.u32CanvasNum = 2; 
    stRgnAttr.enType = OVERLAYEX_RGN;  /**区域类型:叠加**/
    stRgnAttr.unAttr.stOverlay.enPixelFmt       = PIXEL_FORMAT_ARGB_1555; /**像素格式**///PIXEL_FORMAT_BGR_565 PIXEL_FORMAT_ARGB_1555
    if (stBitmap_top_left.u32Width % 2 != 0)
        {
        stBitmap_top_left.u32Width += 1;
        }

    if (stBitmap_top_left.u32Height % 2 != 0)
        {
        stBitmap_top_left.u32Height += 1;
        }
    // printf ("stBitmap_top_left.u32Width is %d ,stBitmap_top_left.u32Height is %d\n",stBitmap_top_left.u32Width,stBitmap_top_left.u32Height);394,60
    stRgnAttr.unAttr.stOverlay.stSize.u32Width  = stBitmap_top_left.u32Width;//240;        /**区域宽**/
    stRgnAttr.unAttr.stOverlay.stSize.u32Height = stBitmap_top_left.u32Height;//192;        /**区域高**/
    stRgnAttr.unAttr.stOverlay.u32BgColor       = 0xffffff00;//0x00007c00; /**区域背景颜色**/

   
    s32Ret = HI_MPI_RGN_Create(OverlayHandle_top_left, &stRgnAttr);
    if(s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("RGN create failed: %#x\n", s32Ret);
    }
        /**将区域叠加到通道**/
        /**设置叠加区域的通道显示属性**/
        stChnAttr.bShow  = HI_TRUE;
        stChnAttr.enType = OVERLAYEX_RGN;
        stChnAttr.unChnAttr.stOverlayChn.stPoint.s32X = 20;//240;
        stChnAttr.unChnAttr.stOverlayChn.stPoint.s32Y = 20;//192;
        stChnAttr.unChnAttr.stOverlayChn.u32BgAlpha   = 0;//128;
        stChnAttr.unChnAttr.stOverlayChn.u32FgAlpha   = 128;
        stChnAttr.unChnAttr.stOverlayChn.u32Layer     = OverlayHandle_top_left;

        /**设置QP属性Qp(量化参数,反应压缩效果)以 H.264 编码为例,通常图像 Qp(量化参数,反应压缩效果) 越低,图像的质量越好,码率越高;图像 Qp 越高,图像质量越差,码率越低。**/
        stChnAttr.unChnAttr.stOverlayChn.stQpInfo.bAbsQp = HI_FALSE;
        stChnAttr.unChnAttr.stOverlayChn.stQpInfo.s32Qp  = 0;
        stChnAttr.unChnAttr.stOverlayChn.stQpInfo.bQpDisable = HI_FALSE;

        /**定义 OSD 反色相关属性**/
        /**单元反色区域,反色处理的基本单元,[16, 64],需 16 对齐**/
        stChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Height = 16;
        stChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Width  = 16;

        /**亮度阈值,取值范围:[0, 255]**/
        stChnAttr.unChnAttr.stOverlayChn.stInvertColor.u32LumThresh = 128;//128

        /**OSD 反色触发模式**/
        stChnAttr.unChnAttr.stOverlayChn.stInvertColor.enChgMod     = LESSTHAN_LUM_THRESH;

        /**OSD 反色开关。overlay不支持反色**/
        stChnAttr.unChnAttr.stOverlayChn.stInvertColor.bInvColEn    = HI_FALSE;

        stChnAttr.unChnAttr.stOverlayChn.enAttachDest =ATTACH_JPEG_MAIN;
        // OverlayHandle =0;
        s32Ret = HI_MPI_RGN_AttachToChn(OverlayHandle_top_left, &stChn, &stChnAttr);
        if(s32Ret != HI_SUCCESS)
        {
             SAMPLE_PRT("HI_MPI_RGN_AttachToChn: %#x\n", s32Ret);
        }       
        stBitmap_top_left.enPixelFormat = PIXEL_FORMAT_ARGB_1555;
   
        s32Ret = HI_MPI_RGN_SetBitMap(OverlayHandle_top_left,&stBitmap_top_left);
        if(s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("HI_MPI_RGN_SetBitMap failed with %#x!\n", s32Ret);
        }
        // hi_memset(stBitmap_top_left.pData, sizeof(BITMAP_S),0, sizeof(BITMAP_S));
       
    return 0;
}

初始化和退出

  都配置完了就可以初始化了,初始化打开osd字体,在创建叠加通道前生成位图,区域初始化就是我们整个osd功能初始化的内容了,退出相应的关闭字体,释放打开的结构体就好

/* 
 *描述  :用于OSD 初始化,打开字体,生成位图后区域初始化
 *参数  :arg 为自定义结构video_process_s,VPSS_GRP和VPSS_CHN用于传参给HI_MPI_VPSS_GetChnFrame
 *返回值:
 *注意  :        
 */
HI_VOID osd_init(HI_VOID *arg)
{
    if (TTF_Init() < 0 ) 
    {  
        fprintf(stderr, "Couldn't initialize TTF: %s\n",SDL_GetError());  
        SDL_Quit();
    }  
 
    font = TTF_OpenFont(FONT_PATH, 29); 
    if ( font == NULL ) 
    {  
        fprintf(stderr, "Couldn't load %d pt font from %s: %s\n",18,"ptsize", SDL_GetError());  
        // return -1;

    }  
  
    video_process_s* pstPara;
    pstPara = (video_process_s*)arg;
    time_t now;
    struct tm *ptm;
    char timestr[OSD_LENGTH] = {0};
    char lost_target_info_str[OSD_LENGTH] = {0};
    char other_info_str[OSD_LENGTH] = {0};
    char trk_str[OSD_LENGTH] = {0};
    char frame_str[OSD_LENGTH] = {0};
    osd_fmt = (SDL_PixelFormat*)malloc(sizeof(SDL_PixelFormat));
    hi_memset(osd_fmt,sizeof(SDL_PixelFormat),0,sizeof(SDL_PixelFormat));
    osd_fmt->BitsPerPixel = 16;
    osd_fmt->BytesPerPixel = 2;
    osd_fmt->colorkey = 0xffffffff;
    osd_fmt->alpha = 0xff;
   
    // osd_bottom_left = SDL_CreateRGBSurface(SDL_SWSURFACE, OSD_BOTTOM_LEFT_W, OSD_BOTTOM_LEFT_H, 
    //                                 osd_fmt->BitsPerPixel,osd_fmt->Rmask, osd_fmt->Gmask, osd_fmt->Bmask, osd_fmt->Amask);
    // osd_top_left = SDL_CreateRGBSurface(SDL_SWSURFACE, OSD_TOP_LEFT_W, OSD_TOP_LEFT_H, 
    //                                 osd_fmt->BitsPerPixel,osd_fmt->Rmask, osd_fmt->Gmask, osd_fmt->Bmask, osd_fmt->Amask);
  
    time(&now);
    ptm = localtime(&now);
    snprintf(timestr,100,"当前时间:%d-%02d-%02d %02d:%02d:%02d  ",ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec); 
    snprintf(lost_target_info_str,100,"脱靶信息:X:%02d,Y:%02d",osd_fmt->BitsPerPixel,osd_fmt->BytesPerPixel);
    snprintf(other_info_str,100,"视场角信息:X:%02d,Y:%02d,焦距:%02d",osd_fmt->BitsPerPixel,osd_fmt->BytesPerPixel,osd_fmt->BytesPerPixel);
    snprintf(trk_str,100,"跟踪器状态:检测?识别?跟踪? ");
    snprintf(frame_str,100,"帧率:%02d",osd_fmt->BytesPerPixel);
    string_to_bmp_bottom_left(lost_target_info_str,other_info_str,timestr);
    string_to_bmp_top_left(trk_str,frame_str);
    RGN_AddOsdToVpss_bottom_left(pstPara->VpssChn);
    RGN_AddOsdToVpss_top_left(pstPara->VpssChn);
}
/* 
 *描述  :用于OSD 退出,关闭字体,释放配置结构体
 *参数  :无
 *注意  :        
 */
HI_VOID osd_exit()
{
    // SDL_FreeSurface(osd_time_text);  
    // SDL_FreeSurface(osd_losttarget_info_text); 
    // SDL_FreeSurface(osd_other_info_text); 
    // SDL_FreeSurface(osd_bottom_left);
    // SDL_FreeSurface(osd_trk_info_text);  
    // SDL_FreeSurface(osd_frame_info_text); 
    // SDL_FreeSurface(osd_top_left);
    free(osd_fmt);
    TTF_CloseFont(font);  
    TTF_Quit();
}

字符叠加线程

  当然,这里除了时间戳,其他要更新的消息都是随便填的哈哈

* 
 *描述  :线程里用于字符叠加
 *参数  :arg 为自定义结构video_process_s,VPSS_GRP和VPSS_CHN用于传参给HI_MPI_VPSS_GetChnFrame
 *返回值:无
 *注意  :  
 */

HI_VOID *frame_osd_task(HI_VOID *arg)
{
    cpu_set_t mask;//cpu核的集合
    cpu_set_t get;//获取在集合中的cpu

    int num = sysconf(_SC_NPROCESSORS_CONF);
    printf("frame_osd_task:system has %d processor(s)\n", num);

    CPU_ZERO(&mask);//置空
    CPU_SET(0, &mask);//设置亲和力值
     
    if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0)//设置线程CPU亲和力
    {
        fprintf(stderr, "set thread affinity failed\n");
    }

    if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0)//获取线程CPU亲和力
    {
        fprintf(stderr, "get thread affinity failed\n");
    }

    VIDEO_FRAME_INFO_S stVideoFrame_process_osd;
    video_process_s* pstPara;
    pstPara = (video_process_s*)arg;
    
    // HI_S32 s32Ret;
    time_t now;
    struct tm *ptm;
    char timestr[OSD_LENGTH] = {0};
    char lost_target_info_str[OSD_LENGTH] = {0};
    char other_info_str[OSD_LENGTH] = {0};
    char trk_str[OSD_LENGTH] = {0};
    char frame_str[OSD_LENGTH] = {0};

    stBitmap_bottom_left.pData = malloc(2*OSD_BOTTOM_LEFT_W*OSD_BOTTOM_LEFT_H);
    if(stBitmap_bottom_left.pData == NULL)
    {
    printf("stBitmap.pData faided\r\n");
    }
    stBitmap_top_left.pData = malloc(2*OSD_TOP_LEFT_W*OSD_TOP_LEFT_H);
    if(stBitmap_top_left.pData == NULL)
    {
    printf("stBitmap_top_left.pData faided\r\n");
    }
    
    osd_init(arg);
	// int timeOffset = 0;
	// int timeOffset1 = 0;
	
    while(1)
    {    
        // sleep(1);
        // sem_wait(&sem_frm_process);
        if(q_stVideoFrame_process_osd.empty() != true)
        {
            // tmp = clockGetTime(CLOCK_MONOTONIC);	//单位us
	        // timeOffset = tmp.tv_sec*1000*1000 + tmp.tv_usec;/* 测试代码执行时间 */
            // printf("osd queue not empty\n");
            stVideoFrame_process_osd = q_stVideoFrame_process_osd.front();
            q_stVideoFrame_process_osd.pop();
            // printf("osd%d\n", q_stVideoFrame_process_osd.size());
        
            time(&now);
            ptm = localtime(&now);
            snprintf(timestr,100,"当前时间:%d-%02d-%02d %02d:%02d:%02d  ",ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec); 
            snprintf(lost_target_info_str,100,"脱靶信息:X:%02d,Y:%02d",ptm->tm_year,ptm->tm_year);
            snprintf(other_info_str,100,"视场角信息:X:%02d,Y:%02d,焦距:%02d",ptm->tm_year,ptm->tm_year,ptm->tm_year);
            snprintf(trk_str,100,"跟踪器状态:检测?识别?跟踪? ");
            snprintf(frame_str,100,"帧率:%02d",ptm->tm_year);
            string_to_bmp_bottom_left(lost_target_info_str,other_info_str,timestr);
            string_to_bmp_top_left(trk_str,frame_str);
            HI_MPI_RGN_UpdateCanvas(OverlayHandle_top_left);
            HI_MPI_RGN_UpdateCanvas(OverlayHandle_bottom_left);
            // pthread_mutex_lock(&mutex);
            HI_MPI_RGN_SetBitMap(OverlayHandle_bottom_left,&stBitmap_bottom_left);//s32Ret 为RGN_HANDLE OverlayHandle
            HI_MPI_RGN_SetBitMap(OverlayHandle_top_left,&stBitmap_top_left);//s32Ret 为RGN_HANDLE OverlayHandle
            // pthread_mutex_unlock(&mutex);
            // usleep(15000);

            // memset(&tmp,0,sizeof(tmp));
            // tmp = clockGetTime(CLOCK_MONOTONIC);
            // timeOffset1 = tmp.tv_sec*1000*1000 + tmp.tv_usec;
            // printf("timeOffset:%d\n",(timeOffset1 - timeOffset));/* 测试代码执行时间 */

            // hi_memset(stBitmap_bottom_left.pData, sizeof(BITMAP_S),0, sizeof(BITMAP_S));
            // hi_memset(stBitmap_top_left.pData, sizeof(BITMAP_S),0, sizeof(BITMAP_S));
            // hi_memset(timestr,OSD_LENGTH,0,OSD_LENGTH);

            // q_stVideoFrame_venc.push(stVideoFrame_process_osd);
            // printf("osd ok\n");
            HI_MPI_VENC_SendFrame(pstPara->VpssChn, &stVideoFrame_process_osd,1000);
            HI_MPI_VPSS_ReleaseChnFrame(pstPara->VpssGrp, pstPara->VpssChn, &stVideoFrame_process_osd);
        }
        else
        {
            usleep(1000);
        }      
    }
    return NULL;   
}

效果

  摆出最后叠加后的效果图
在这里插入图片描述

后续改进

  这样的OSD已经满足大多数需求了,但是还是没有考虑到如果过亮或者过暗的情况,对于亮度的采集和反色处理将在下一篇更新

相关文章:

  • 从1到100这100个自然数中任取10个数,使他们的倒数和等于1。这10个数分别是多少?
  • 【香橙派4B】6、测试串口
  • 【408】【数据结构】【图】
  • 【架构设计】如何实现3ms内从1000w级别的用户里面随机抽奖出100名用户
  • HTB-Chatterbox
  • 矩阵乘法的消去律
  • FL Studio最新20.9版本完整FL水果中文语言更新
  • JAVA集合(二)List接口详解
  • 矩阵的秩的性质
  • Redis在SpringBoot项目中使用
  • Android AIDL跨进程通信
  • Java大牛必会|分布式缓存实现方案之Spring Cache
  • KF、EKF、IEKF、UKF卡尔曼滤波器
  • Neo4j入门+深入
  • 21年icpc上海区域赛B题Strange Permutations (容斥+生成函数)
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • CentOS从零开始部署Nodejs项目
  • Iterator 和 for...of 循环
  • java中的hashCode
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • Node项目之评分系统(二)- 数据库设计
  • react 代码优化(一) ——事件处理
  • STAR法则
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • Unix命令
  • vue-loader 源码解析系列之 selector
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 工作手记之html2canvas使用概述
  • 计算机常识 - 收藏集 - 掘金
  • 开发了一款写作软件(OSX,Windows),附带Electron开发指南
  • 理解 C# 泛型接口中的协变与逆变(抗变)
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 用jquery写贪吃蛇
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • $(selector).each()和$.each()的区别
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (五)网络优化与超参数选择--九五小庞
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • (转)nsfocus-绿盟科技笔试题目
  • (转)Oracle存储过程编写经验和优化措施
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .Net 代码性能 - (1)
  • .net 使用$.ajax实现从前台调用后台方法(包含静态方法和非静态方法调用)
  • .net安装_还在用第三方安装.NET?Win10自带.NET3.5安装
  • .pyc文件是什么?
  • .vimrc php,修改home目录下的.vimrc文件,vim配置php高亮显示