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

简介: 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;   
}

效果

 摆出最后叠加后的效果图

1a23efb9e2456b991cd1b6b02784d21.png

后续改进

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


相关文章
|
6月前
|
数据采集 监控 安全
精简高效与安全兼备:ARM32与MCU32平台上的信息协议设计新思路
精简高效与安全兼备:ARM32与MCU32平台上的信息协议设计新思路
267 1
|
6月前
|
数据可视化 安全 关系型数据库
写给工程师的 MacBook 商用级大模型知识库部署方案(上)
写给工程师的 MacBook 商用级大模型知识库部署方案(上)
568 2
|
数据安全/隐私保护 块存储
|
4月前
|
存储 监控 安全
公司电脑监控软件深度对比:软件功能、易用性和成本效益分析
随着企业对信息安全和员工效率管理的日益重视,公司电脑监控软件逐渐成为企业管理工具箱中的必备品。本文将深入探讨三款行业领先的监控软件——WorkWin、OsMonitor和ManageEngine Desktop Central,分析它们在功能、易用性和成本效益等方面的优劣,为企业管理提供参考。
119 1
|
6月前
|
缓存 算法 Java
Linux内核新特性年终大盘点-安卓杀后台现象减少的背后功臣MGLRU算法简介
MGLRU是一种新型内存管理算法,它的出现是为了弥补传统LRU(Least Recently Used)和LFU(Least Frequently Used)算法在缓存替换选择上的不足,LRU和LFU的共同缺点就是在做内存页面替换时,只考虑内存页面在最近一段时间内被访问的次数和最后一次的访问时间,但是一个页面的最近访问次数少或者最近一次的访问时间较早,可能仅仅是因为这个内存页面新近才被创建,属于刚刚完成初始化的年代代页面,它的频繁访问往往会出现在初始化之后的一段时间里,那么这时候就把这种年轻代的页面迁移出去
|
6月前
|
新零售 人工智能 供应链
写给工程师的 MacBook 商用级大模型知识库部署方案(下)
写给工程师的 MacBook 商用级大模型知识库部署方案(下)
350 2
|
6月前
|
NoSQL 关系型数据库 API
写给工程师的 MacBook 商用级大模型知识库部署方案(中)
写给工程师的 MacBook 商用级大模型知识库部署方案(中)
274 1
|
安全 数据安全/隐私保护 Android开发
三款远程控制软件对比,5大挑选指标:安全、稳定、易用、兼容、功能
众所周知,随着科技的发展和时代的进步,远程控制软件在日常工作中的运用所处可见,社会对远程控制软件的需求也在日益增长,为用户提供了便捷的远程访问和控制计算机的能力,使其在现代化社会中扮演着重要的角色。因此,市面上也出现了很多相似功能的产品,但是他们各有不同的特点和功能。本文主要介绍的是ToDesk、向日葵、TeamViewer三款远程控制软件间的不同之处,我将从安全、稳定、易用、兼容、功能几个方面来进行对比,帮助大家选择适合自己需求的远程办公软件。
332 1
三款远程控制软件对比,5大挑选指标:安全、稳定、易用、兼容、功能
|
运维 监控 安全
更安全更稳定,阿里云斩获多项云系统稳定安全运行优秀案例
近日,阿里云凭借在稳定性领域的全栈投入,获评中国信通院混沌工程实验室 2022 年度杰出贡献企业,并斩获“云系统稳定安全运行优秀案例”活动中多领域优秀案例。阿里云持续推动企业 IT 系统建设,保障千行百业安全稳定的实现数字化转型与创新。
更安全更稳定,阿里云斩获多项云系统稳定安全运行优秀案例