美文网首页
(十五)场景切换检测函数:scenecut

(十五)场景切换检测函数:scenecut

作者: 奔向火星005 | 来源:发表于2018-09-16 09:02 被阅读0次

x264会对每一帧检查它与前面一帧的差异,当差异达到一定阈值时,就认为发生了场景切换,并把该帧置位I帧或IDR帧。在x264_slicetype_analyse函数中,我们可以看到scenecut的调用:

void x264_slicetype_analyse( x264_t *h, int intra_minigop )
{
//省略
    //如果frames[1]还未确定类型,且i_scenecut_threshold场景切换的阈值不为0,则进行scenecut判断,若返回非0,则将frames[1]设为I帧
    if( IS_X264_TYPE_AUTO_OR_I( frames[1]->i_type ) &&
        h->param.i_scenecut_threshold && scenecut( h, &a, frames, 0, 1, 1, orig_num_frames, i_max_search ) )
    {
        if( frames[1]->i_type == X264_TYPE_AUTO )  //如果是场景切换,则设为I帧
            frames[1]->i_type = X264_TYPE_I;
        return;
    }
//省略
}

下面是scenecut的源码:

static int scenecut( x264_t *h, x264_mb_analysis_t *a, x264_frame_t **frames, int p0, int p1, int real_scenecut, int num_frames, int i_max_search )
{
    /* Only do analysis during a normal scenecut check. */
    if( real_scenecut && h->param.i_bframe )
    {
        int origmaxp1 = p0 + 1;
        /* Look ahead to avoid coding short flashes as scenecuts. */
        if( h->param.i_bframe_adaptive == X264_B_ADAPT_TRELLIS )
            /* Don't analyse any more frames than the trellis would have covered. */
            origmaxp1 += h->param.i_bframe;
        else
            origmaxp1++;
        int maxp1 = X264_MIN( origmaxp1, num_frames );

        /* Where A and B are scenes: AAAAAABBBAAAAAA
         * If BBB is shorter than (maxp1-p0), it is detected as a flash
         * and not considered a scenecut. */
        for( int curp1 = p1; curp1 <= maxp1; curp1++ )
            if( !scenecut_internal( h, a, frames, p0, curp1, 0 ) )  
                /* Any frame in between p0 and cur_p1 cannot be a real scenecut. */
                for( int i = curp1; i > p0; i-- )
                    frames[i]->b_scenecut = 0;

        /* Where A-F are scenes: AAAAABBCCDDEEFFFFFF
         * If each of BB ... EE are shorter than (maxp1-p0), they are
         * detected as flashes and not considered scenecuts.
         * Instead, the first F frame becomes a scenecut.
         * If the video ends before F, no frame becomes a scenecut. */
        for( int curp0 = p0; curp0 <= maxp1; curp0++ )
            if( origmaxp1 > i_max_search || (curp0 < maxp1 && scenecut_internal( h, a, frames, curp0, maxp1, 0 )) )
                /* If cur_p0 is the p0 of a scenecut, it cannot be the p1 of a scenecut. */
                    frames[curp0]->b_scenecut = 0;
    }

    /* Ignore frames that are part of a flash, i.e. cannot be real scenecuts. */
    if( !frames[p1]->b_scenecut )
        return 0;
    return scenecut_internal( h, a, frames, p0, p1, real_scenecut );
}

如源码所示,scenecut主要是比较p0和p1两帧之间的差异,当差异大于某个阈值时,则判断为场景切换,调用它的函数会将p1设为I帧或IDR帧。主要的判断逻辑是在scenecut_internal函数中,我们稍后分析,现在我们先看下scenecut中的两个for循环。

第一个for循环:当存在A和B两种场景,它们出现的顺序像:AAAAAABBBAAAAAA这样的,如果场景B的数目小于(maxp1-p0),则认为这是一个flash(闪屏或抖动的意思吧...),不认为是场景切换。也就是说,如果一开始是A场景,然后突然来了几帧B场景,但后面又恢复到A场景,则不能认为是一个场景切换,需要把B场景的frames的b_scenecut清空。

第二个for循环:当出现AAAAABBCCDDEEFFFFFF这样的场景,如果BB ... EE这些场景的图像数目小于(maxp1-p0),则把他们当做是flashes而不能认为是scenecuts。而第一个F则会被当成一个场景切换(因为后面一直是F了,没有恢复到A或变为其他场景)。但如果视频流在F之前结束,那么没有图像被认为是场景切换。

下面我们来看下scenecut_internal源码如何判断场景切换:

static int scenecut_internal( x264_t *h, x264_mb_analysis_t *a, x264_frame_t **frames, int p0, int p1, int real_scenecut )
{
    x264_frame_t *frame = frames[p1];

    /* Don't do scenecuts on the right view of a frame-packed video. */
    if( real_scenecut && h->param.i_frame_packing == 5 && (frame->i_frame&1) )
        return 0;

    x264_slicetype_frame_cost( h, a, frames, p0, p1, p1 );

    int icost = frame->i_cost_est[0][0];  //帧内编码损耗
    int pcost = frame->i_cost_est[p1-p0][0];  //帧间编码损耗
    float f_bias;
    int i_gop_size = frame->i_frame - h->lookahead->i_last_keyframe;
    float f_thresh_max = h->param.i_scenecut_threshold / 100.0;
    /* magic numbers pulled out of thin air */
    float f_thresh_min = f_thresh_max * 0.25;
    int res;

    //通过i_scenecut_threshold计算出f_bias系数
    if( h->param.i_keyint_min == h->param.i_keyint_max )
        f_thresh_min = f_thresh_max;
    if( i_gop_size <= h->param.i_keyint_min / 4 || h->param.b_intra_refresh )
        f_bias = f_thresh_min / 4;
    else if( i_gop_size <= h->param.i_keyint_min )
        f_bias = f_thresh_min * i_gop_size / h->param.i_keyint_min;
    else
    {
        f_bias = f_thresh_min
                 + ( f_thresh_max - f_thresh_min )
                 * ( i_gop_size - h->param.i_keyint_min )
                 / ( h->param.i_keyint_max - h->param.i_keyint_min );
    }

    res = pcost >= (1.0 - f_bias) * icost;  //帧间损耗大于帧内损耗的某个比例,因为帧间损耗越大,说明两帧之间差异越大
    if( res && real_scenecut )
    {
        int imb = frame->i_intra_mbs[p1-p0];
        int pmb = NUM_MBS - imb;
        x264_log( h, X264_LOG_DEBUG, "scene cut at %d Icost:%d Pcost:%d ratio:%.4f bias:%.4f gop:%d (imb:%d pmb:%d)\n",
                  frame->i_frame,
                  icost, pcost, 1. - (double)pcost / icost,
                  f_bias, i_gop_size, imb, pmb );
    }
    return res;
}

具体看注释。

相关文章

  • (十五)场景切换检测函数:scenecut

    x264会对每一帧检查它与前面一帧的差异,当差异达到一定阈值时,就认为发生了场景切换,并把该帧置位I帧或IDR帧。...

  • 场景切换

    SceneManager.LoadScene()//等同于Application.LoadLevel();Scen...

  • 场景切换

    ——卓夫文暄 一条沿海大通道,在海岛的辖区, 亦是另一个湾区,拐了一个优美的弧度 风景在拐点,开发商当年也在此实现...

  • cocos2d-x 3.x场景跳转

    场景: HelloWorldScene, firstScene 场景切换特效 场景切换一般有两个参数: float...

  • 文件检测函数 feof, ferror,clearerr C库函

    文件检测函数C语言中常用的文件检测函数有以下几个。 文件结束检测函数feof函数调用格式: 功能:判断文件是否处于...

  • cocos2d-x 随记

    获取子图层 获取设备语言 场景切换动画 //创建待切换的场景 下面列举下场景切换的动画效果,可以参考上面的代码来运...

  • 人该如何度过这一生

    所谓人生、就是不同的场景切换!从这个场景切换到那个场景;再从那个场景到这个场景、亦或是其他的场景。记得、先前有人做...

  • Unity场景切换

    场景切换在游戏中很常见。 切换其他场景比如点击start按钮后开始游戏。代码如下: 这里的场景名start_gam...

  • threejs制作全景看房一

    网页部分 container 存放场景图片,bts多个场景切换,rotate感应切换; 需要引入three.min...

  • 2019-05-03 小猫妮:调整好自己的心态,随时可以做到场景

    主题: 调整好自己的心态,随时可以做到场景切换 关键词: 调整好心态 场景切换 取舍和平衡 注意力切换 ...

网友评论

      本文标题:(十五)场景切换检测函数:scenecut

      本文链接:https://www.haomeiwen.com/subject/qeyxnftx.html