视频采集到录制 - 采集到显示碰到一些难点

项目中用到相机后端处理,走了一些弯路,也遇到不少问题(解决了不少问题),特意写下本文记录下当时点点滴滴。

讲一下背景,公司自研相机,用于一些高端场合,因此对后端处理也非常讲究

采集-显示 基本方案:

  1. 前端模拟信号(BNC)
  2. PCIE采集图像
  3. 应用层通过PCIE采集卡(驱动)获取YUV图像
  4. YUV转码BGR
  5. BGR进行OSD叠加
  6. BGR显示到UI

方案很简单,也很常规一种解决办法,但仍然碰到许多问题

 

1. 首先采集端

        最初方案是USB采集卡,网购号称USB3.0采集卡,实测发现根本达不到3.0,后来拆开来看,外面接口是USB3.0,但里面转接是2.0,因此做不到。

        后来采用采集到HDMI 然后再HDMI - USB3.0采集卡,虽然可以达到,但延时、占用CPU资源大的问题很突出。

        又考虑到主板是ARM方案,因此,还是PCIE的方式比较靠谱(成本高),多路也方便

        为什么非要USB3.0?1080p*25的带宽,2.0明显不够(只能跑10帧)

2. YUV-转BGR编码

转码很好理解,但是考虑到性能,绝对不可以用CPU,

        最佳办法使用RGA(rk的解决办法),也占资源,但好在还可以接受

由于1080不是16倍数(一开始不知道这个原因),因此也搞了好久

测试640、720都没问题,1080就不行,以为是性能问题

而且,官方虽然有demo,但可复杂了,如果仅仅是rga转码,只要一点点代码就够了

int RgaConvert::YuvToRgb(uint8_t *srcBuf, uint32_t w,uint32_t h, uint8_t *dstBuf)
{
    int srcFormat = RK_FORMAT_YUYV_422;
    int dstFormat = RK_FORMAT_BGR_888;

    rga_info srcInfo,dstInfo;

    memset(&srcInfo, 0, sizeof(rga_info_t));
    srcInfo.fd = -1;
    srcInfo.mmuFlag = 1;

    memset(&dstInfo, 0, sizeof(rga_info_t));
    dstInfo.fd = -1;
    dstInfo.mmuFlag = 1;

    rga_set_rect(&srcInfo.rect,0,0,w,h,w,h,srcFormat);
    rga_set_rect(&dstInfo.rect,0,0,w,h,w,h,dstFormat);

    //图像翻转也可以这里做,但会增加CPU

    srcInfo.virAddr = (void *)srcBuf;
    dstInfo.virAddr = (void *)dstBuf;
    mRkRga.RkRgaBlit(&srcInfo, &dstInfo, NULL);

    return 0;
}

3. OSD叠加

OSD叠加也是个大学问,简单叠加当然简单,但是但是,

        考虑到图像背景,叠加颜色怎么处理也是个麻烦地方

首先,你得有图像数据传进来

其次,然后要计算图像数据的灰度(总不能取中心点,也不能去全部点,然后每帧都要计算吗?显然每秒计算一次也足够了),

。。。有了背景的灰度,此时,还要考虑临界点(比如128以上是白色,如果实际刚好是127-129来回切换,那不是每次都得切换颜色?这样效果也不好:肉眼看起来没有变化的,实际却在切换颜色);还有如果字符很长,那么背景灰度也很讨厌(一个一个字符去计算?还是,显然一个个字符去计算比较合理点)

最后,是否可以考虑描边?可以,但很丑(也非常占资源),且logo图标方式也很难支持描边

        这里涉及到很多参数,算法,需要慢慢去优化,调整,也很辛酸

/*计算灰度*//*斜线方向*/
uint32_t Osd::CalculateGray(const uint8_t *rgb,const QRect &rect,int base,bool w2b)
{
    uint32_t countGray = 0;
    /*y轴检测16像素*/
    int yStep = (rect.height() > 16) ? (rect.height() / 16) : 1;
    int xStep = (rect.width() > 16) ? (rect.width() / 16) : 1;

    int32_t tY = rect.y();
    for(int32_t j = 0; j < 16; j++)
    {
        int32_t start = (tY * VIDEO_WIDTH + rect.x() + j * xStep) * 3;
        uint8_t gray = (uint8_t)((19595 * rgb[start] + 38469 * rgb[start+1]  + 7472 * rgb[start+2]) >> 16);
        if(w2b)
        {
            countGray += ((gray > base) ? 1 : 0);
        }
        else
        {
            countGray += ((gray < base) ? 1 : 0);
        }
        tY += yStep;
    }

    //countGray = countGray >> 8;
    return countGray;
}

4. 内存交互问题

        采集端获取是YUV,是个内存,然后转码成BGR,此时又得一个内存

        肯定存在内存拷贝问题

        后面还有大的环节:OSD叠加、H264编码源、显示UI,这些都是需要内存的

        如果每个环节各管各的(强解耦),虽然流程简单了,但每次都是拷贝,性能非常受限

        因此设计一个内存自动管理机制(类似智能指针或许是个好的解决办法)

5. 显示问题

        双缓冲?等等,如何快速,如何不影响其他流程,等等也是很头疼的问题

        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值