Super SloMo: High Quality Estimation of Multiple Intermediate Frames for Video Interpolation论文阅读

本论文主要讲解了一个通过低帧率生成高帧率的视频的算法,训练数据就是高帧率的视频。
原文在这里
这里我结合代码讲解一下我对整个训练过程,网络和里面的公式的一些理解。
参考代码,基于tensorflow

公式讲解

要想合成0帧和1帧之间的任意帧,这里的做法是分别求到t到0和1的光流,然后用光流复原t帧图像,如公式1所示。


公式1

公式1,0和1之间的中间任意第t帧的生成方式,用I0和Ft0来生成t,用I1和Ft1来生成t,再将两个生成图加权求到It。这里的alpha权值不是一个值,这里权值的计算要考虑时间一致性和遮挡问题。所谓时间一致性,就是t离哪一帧近就更像哪一帧;遮挡问题,文章认为t中的像素,0和1中至少有一帧中会出现。所以设计了以下公式


公式2

公式2就是将公式一种的alpha具体化的效果,这里(1-t)和(t)是考虑的时间一致性;Vt0和Vt1是所谓的可视性map用来考虑遮挡问题,Vt0+Vt1=1,对于1中不可见的像素,就依靠0帧,那么Vt0接近1,如果都可见,Vt0和Vt1就55开。再乘上一个归一化因子1/Z。得到最后结果。所以实际用的时候是用公式2,在下面代码中也有说明。

使用公式2能求到t帧,但问题是我们还没有求到Ft-0和Ft-1两个光流。本文的做法是,先求F10和F01两个光流图


公式3

这里用公式3求Ft-1的光流,这里前提条件是光流场局部平滑,应该是指局部区域的光流是一样的意思,不然感觉公式3推导有问题。


公式4

通过公式三,将三的两个公式加权求和得到最终的Ft-1,同理求到Ft-0
这样就通过F1-0和F0-1求到了Ft-1和Ft-1。

网络构建

image.png

整个网络的构成就是如Figure4所示,前后两个网络用的是U-NET(具体参见本文),是个全卷积网络。
flow computation输入是I0和I1两帧图像,输出是F0-1和F1-0两个光流图,按道理然后我们通过公式4就可以算出Ft-1和Ft-0,但是文中说由于有些区域的不平滑所以这样求到的结果不好,所以用第二个网络来优化一下。
第二个网络的输入是,I1和I0两帧图像,通过网络1算出来的Ft-1和Ft-0,通过I1和Ft-1算出来的It,通过I0和Ft-0算出来的It,一共6个项组合输入,输出的是Ft-1和Ft-0的修正值,和Vt-0,三项。
Vt-0是t对于0的可见性map,这里要求Vt-0和Vt-1要和为1。
Ft-1和Ft-0的修正值加上前面算到的FT-1和Ft-0就是最后的Ft-1和Ft-0。这样要求得值都求到了。最后用公式2求到最后的T帧值It.

损失函数

损失函数

损失函数由4项损失加权求得


重构损失

重构损失计算的是gt的It和预测的It的每个像素的L1损失值


感知损失

感知损失方法是,将两个IT分别输入VGG网络中,取中间的特征层计算两者的L2损失,重构的好应该是要相同的
wrap损失

wrap有4项,I0和用I1,F0-1重构的I0之间的l1损失,I1和用I0,F1-0重构的I1之间的l1损失,IT和用I0,FT-0重构的It之间的L1损失,IT和用I1,FT-1重构的It之间的L1损失,这里Ft-1和Ft-0是用第一个网络输出的计算的,不是用的最后的修正值
平滑损失

平滑损失,就是要求所求的F0-1和F1-0的相邻像素的光流值要一样,求了他们的一阶导数。

接下来是相关代码及注释

代码讲解

# SloMo vanila model  这段代码包含了构建网络的全过程
def SloMo_model(frame0, frame1, frameT, FLAGS, reuse=False, timestamp=0.5):
    # Define the container of the parameter
    if FLAGS is None:
        raise ValueError('No FLAGS is provided for generator')

    Network = collections.namedtuple('Network', 'total_loss, reconstruction_loss, perceptual_loss, \
                                                wrapping_loss,  smoothness_loss, pred_frameT   \
                                                Ft0, Ft1, Vt0,\
                                                grads_and_vars, train, global_step, learning_rate')
    with tf.variable_scope("SloMo_model", reuse=reuse):
        with tf.variable_scope("flow_computation"):
            flow_comp_input = tf.concat([frame0, frame1], axis=3)
            flow_comp_out, flow_comp_enc_out = UNet(flow_comp_input,
                                                    output_channels=4,  # 2 channel for each flow
                                                    first_kernel=FLAGS.first_kernel,
                                                    second_kernel=FLAGS.second_kernel)   #使用UNET构建第一个光流生成网络,有两个输出
                                                    #第一个输出为两张图的双向光流,4层,每个光流两层;
                                                    #第二个输出是网络中间的的编码层
            flow_comp_out = lrelu(flow_comp_out)
            F01, F10 = flow_comp_out[:, :, :, :2], flow_comp_out[:, :, :, 2:]   #将上面的第一个输出分开成两个光流图
            print("Flow Computation Graph Initialized !!!!!! ")

        with tf.variable_scope("flow_interpolation"):
            Fdasht0 = (-1 * (1 - timestamp) * timestamp * F01) + (timestamp * timestamp * F10)
            Fdasht1 = ((1 - timestamp) * (1 - timestamp) * F01) - (timestamp * (1 - timestamp) * F10)
            #通过两张光流图计算t时刻到0和1时刻的光流图
            flow_interp_input = tf.concat([frame0, frame1,
                                           flow_back_wrap(frame1, Fdasht1),
                                           flow_back_wrap(frame0, Fdasht0),
                                           Fdasht0, Fdasht1], axis=3)
            #构建第二个网络的输入,为0,1两帧图像,用flow_back_wrap计算合成得到的t时刻的帧,分别用Fasht0和Fdasht1两个光流得到,还有Fdasht0和Fdasht1两个光流图,一共6个张图组成的输入
            flow_interp_output, _ = UNet(flow_interp_input,
                                         output_channels=5,  # 2 channels for each flow, 1 visibilty map.
                                         decoder_extra_input=flow_comp_enc_out,
                                         first_kernel=3,
                                         second_kernel=3)
            #构建第二个网络,输出5个通道,额外的输入是在中间加进去的,加入网络的解码部分
            deltaFt0, deltaFt1, Vt0 = flow_interp_output[:, :, :, :2], flow_interp_output[:, :, :, 2:4], \
                                      flow_interp_output[:, :, :, 4:5]
            #分解网络的输出,为两个光流输出图,是对t到1和0 的两个光流的的修正,还有一个可见map通道
            deltaFt0 = lrelu(deltaFt0)
            deltaFt1 = lrelu(deltaFt1)
            Vt0 = tf.sigmoid(Vt0)
            Vt0 = tf.tile(Vt0, [1, 1, 1, 3])  # Copy same in all three channels
            Vt1 = 1 - Vt0

            Ft0, Ft1 = Fdasht0 + deltaFt0, Fdasht1 + deltaFt1  #用网络2的输出修正t到1和0的两个光流,作为最后的光流输出

            normalization_factor = 1 / ((1 - timestamp) * Vt0 + timestamp * Vt1 + FLAGS.epsilon)
            pred_frameT = tf.multiply((1 - timestamp) * Vt0, flow_back_wrap(frame0, Ft0)) + \
                          tf.multiply(timestamp * Vt1, flow_back_wrap(frame1, Ft1))
            pred_frameT = tf.multiply(normalization_factor, pred_frameT)
            #这里使用公式2来计算最终的合成图像,考虑了可见性map
            print("Flow Interpolation Graph Initialized !!!!!! ")

    rec_loss = reconstruction_loss(pred_frameT, frameT)  #重构损失,就是计算gt的T帧和生成的T帧每个像素的l1损失
    percep_loss = perceptual_loss(pred_frameT, frameT, layers=FLAGS.perceptual_mode)
    #感知损失,将gt帧和生成帧分别输入到vgg网络中用中间层的特征来计算l2损失
    wrap_loss = wrapping_loss(frame0, frame1, frameT, F01, F10, Fdasht0, Fdasht1)
    #wrap损失,分别计算  用frame1和 F01生成frame0帧,用frame0和 F10生成frame1帧,用frame0和Fdasht0生成frameT帧
    #用frame1和Fdasht1生成frameT帧,和他们的真实值之间的l1损失
    smooth_loss = smoothness_loss(F01, F10)
    #平滑损失,就是要求F01和F10两个光流图尽量平滑,相邻像素之间的光流要相等

    total_loss = FLAGS.reconstruction_scaling * rec_loss + \
                 FLAGS.perceptual_scaling * percep_loss + \
                 FLAGS.wrapping_scaling * wrap_loss + \
                 FLAGS.smoothness_scaling * smooth_loss
    #将损失加权求和
    
    #以上就是整个的训练模型的构建,以下就是网络训练方式的构建,都是标准写法

    with tf.variable_scope("global_step_and_learning_rate", reuse=reuse):
        global_step = tf.contrib.framework.get_or_create_global_step()
        learning_rate = tf.train.exponential_decay(FLAGS.learning_rate, global_step, FLAGS.decay_step,
                                                   FLAGS.decay_rate,
                                                   staircase=FLAGS.stair)
        incr_global_step = tf.assign(global_step, global_step + 1)

    with tf.variable_scope("optimizer", reuse=reuse):
        with tf.control_dependencies(tf.get_collection(tf.GraphKeys.UPDATE_OPS)):
            tvars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope='SloMo_model')
            optimizer = tf.train.AdamOptimizer(learning_rate, beta1=FLAGS.beta)
            grads_and_vars = optimizer.compute_gradients(total_loss, tvars)
            train_op = optimizer.apply_gradients(grads_and_vars)

    return Network(
        total_loss=total_loss,
        reconstruction_loss=rec_loss,
        perceptual_loss=percep_loss,
        wrapping_loss=wrap_loss,
        smoothness_loss=smooth_loss,
        pred_frameT=pred_frameT,
        Ft0=Ft0,
        Ft1=Ft1,
        Vt0=Vt0,
        grads_and_vars=grads_and_vars,
        train=tf.group(total_loss, incr_global_step, train_op),
        global_step=global_step,
        learning_rate=learning_rate
    )

这里是几种损失的实现

    Ipred = tf.image.convert_image_dtype(Ipred, dtype=tf.uint8)
    Iref = tf.image.convert_image_dtype(Iref, dtype=tf.uint8)

    Ipred = tf.cast(Ipred, dtype=tf.float32)
    Iref = tf.cast(Iref, dtype=tf.float32)

    # tf.reduce_mean(tf.norm(tf.math.subtract(Ipred, Iref), ord=1, axis=[3]))
    return l1_loss(Ipred, Iref)


def perceptual_loss(Ipred, Iref, layers="VGG54"):  #用了vgg54层的特征
    # Note name scope is ignored in varibale naming (scope)
    with tf.name_scope("vgg19_Ipred"):
        Ipred_features = VGG19_slim(Ipred, layers, reuse=tf.AUTO_REUSE)
    with tf.name_scope("vgg19_Iref"):
        Iref_features = VGG19_slim(Iref, layers, reuse=tf.AUTO_REUSE)

    return l2_loss(Ipred_features, Iref_features)


def wrapping_loss(frame0, frame1, frameT, F01, F10, Fdasht0, Fdasht1):
    return l1_loss(frame0, flow_back_wrap(frame1, F01)) + \
           l1_loss(frame1, flow_back_wrap(frame0, F10)) + \
           l1_loss(frameT, flow_back_wrap(frame0, Fdasht0)) + \
           l1_loss(frameT, flow_back_wrap(frame1, Fdasht1))


def smoothness_loss(F01, F10):#计算delta,将图像平移一个像素,作差
    deltaF01 = tf.reduce_mean(tf.abs(F01[:, 1:, :, :] - F01[:, :-1, :, :])) + tf.reduce_mean(
        tf.abs(F01[:, :, 1:, :] - F01[:, :, :-1, :]))
    deltaF10 = tf.reduce_mean(tf.abs(F10[:, 1:, :, :] - F10[:, :-1, :, :])) + tf.reduce_mean(
        tf.abs(F10[:, :, 1:, :] - F10[:, :, :-1, :]))
    return 0.5 * (deltaF01 + deltaF10)

以上是个人阅读论文笔记,如有错误,希望大家批评指正,谢谢

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341