vid2vid簡介 - Video-to-Video Synthesis

Ting-Chun Wang, Ming-Yu Liu, Jun-Yan Zhu, Guilin Liu, Andrew Tao, Jan Kautz, Bryan Catanzaro. “Video-to-Video Synthesis”. In NIPS 2018.

NIPS 2018 paper

Project 主頁 : https://tcwang0509.github.io/vid2vid/

Paper Link : https://arxiv.org/abs/1808.06601

Video Link(推薦觀看) : https://www.youtube.com/watch?v=GrP_aOSXt5U&feature=youtu.be

Github code(Pytorch)) : https://github.com/NVIDIA/vid2vid

此篇我認為是 pix2pixHD 的延伸,

如果沒看過此篇的話建議去看一下,

或是看我之前寫過的pix2pixHD簡介

Ting-Chun Wang, Ming-Yu Liu, Jun-Yan Zhu, Andrew Tao, Jan Kautz, Bryan Catanzaro, High-Resolution Image Synthesis and Semantic Manipulation with Conditional GANs. In CVPR 2018.

應用到了許多觀念

Coarse-to-fine generator

Multi-scale discriminators

Feature matching

Instance-level Feature Embedding

簡介

video-to-video 是將label(e.g. semantic segmentation)的影片轉成真實世界的影片,

可達到畫面連貫、2048 X 1024的高解析度、可調整每個物件的外觀,

對於特定的任務(街景)還可生成出30秒連續的影片,

超越了以往 video-to-video 的成果。

與其相似的問題為 Image-to-image,

但 video 更要注重的是每個 frame 之間的連貫性,

如果我們單純用image-to-image的方法來製作圖片會變成下面這樣(右上方的 pix2pixHD)

下圖擷取自 vid2vid official video

而此模型提出的解決辦法為基於 GAN 架構,

透過不同功能的 Discriminator((e.g., 確保畫面解析度,畫面是否連貫,

才能達成上方動圖(gif的右下角Ours)的成效。

基本方法

下方我們都以街道資料集(GTA5/Cityscapes)做說明。

先定義參數

T:各個時間點的圖片(per-frame) 1…T 。

S:Input labels (街道圖就是Semantic segmentation)

X:Ground Truth - S對應的圖片 ( X 經由 Semantic segmentation model 生成 S)

\widetilde{x} :經由vid2vid模型所生成之圖片

基於 s 生成 \widetilde{x},寫成數學式會像下面這樣,

而本文提出透過 GAN 讓他們的機率分佈相近,

提出 conditional GAN ((D 會加入原本 G 的 input

可是上方公式並沒有考慮到 frame 之間的連續關係,

所以提出 Markov assumption 透過下方的公式來定義與前 L 個frame 之間有序列關係

function F in a recursive manner

我們在生成圖片時會往回看 L 張圖片,

當 L = 1 只往回看一張,訓練模型時比較不穩定

本文設定 L = 2,

並且說明 L 越大訓練越久並且需要更多的 GPU memory,

但是只會改善一點點成效。

如同上面所述說的,影片在幾個 Frame 中有著極大的相關性,

我們甚至能從上一張 Frame 提取出大量相同的部分,

因此這邊提出要使用光流法來取代至下一張 Frame,

而光流法是什麼呢?

簡單來說是可以知道圖片中的每個pixel朝向哪個地方位移,

下圖轉載自FlowNet 2.0: Evolution of Optical Flow Estimation with Deep Networks

下圖的顏色代表著不同方向以及位移量。

而透過這方式所得出的結果通常會正確。

因此我們的模型可以注重在空洞的地方,即為光流法對應過去後仍然為空洞的部分。

因此將剛剛的想法寫成公式會變成這樣。

簡單介紹參數:

m: 0 ~ 1 當0的時候代表是光流法可處理的部分,反之則為須藉由我們模型輸出的圖片填補。

\widetilde{w}_{t-1}(\widetilde{x}_{t-1}):給定上一張圖片再經過光流法後,產出一張新圖片,我稱作 Xb(background)

ht:模型生成的圖片,我稱作 Xf(foreground)

簡單公式說明

m 會決定每個pixel佔有多少 Xb 與 Xf,

如果 m 覺得這個pixel,經由光流法所產生的 Xb 能夠處理好的,那我們就相信他直接取代,

如果 m 覺得這個pixel,經由光流法所產生的 Xb 沒辦法處理好,我們再用我們所生成的圖片Xf填補。

原文定義:

下方為個人理解,如果有錯的話再麻煩指正:

w, m, h 都是經由 G 所產生的,

本文最後方有附上部分程式碼方便理解。

\widetilde{w}_{t-1}(\widetilde{x}_{t-1}) 預測出的光流法結果

這邊挺有意思的,以往的光流法我們都是使用目前圖片 xt 與前一張圖片 xt-1 得來,

但是我們任務比較不一樣,我們的模型輸入是label(Semantic segmentation),

我們希望模型可以明白先前產生的圖片與label之間的光流法關係。

因此我們的模型是要基於 L 筆資料的 st…st-L 和 xt-1…Xt-L 來預測出光流法的結果,

至於訓練方法就是預測出來的結果與 FlowNet2.0(xt, xt-1)所輸出的光流法結果做 l1 loss。

上圖出自vid2vid official video

loss function

\widetilde{h}_{t} 模型所生成之結果

即為上圖 Intermediate 的部分

\widetilde{m}_{t} - mask,決定每個 pixel 要給 w 和 h 多少權重

這邊的 m: 0~1,意味著pixel有可能是由光流法和生成的圖片合成而來,

而這邊給出的說明為,當物體走遠或走近時,

其實物體是會有視覺殘影(motion blur)之類的,

為了應付這問題,才讓 mask 為 0~1 之間。

BTW,

我當初看到這個 m 的時候,

就在想說其實從 w 轉換過後的結果就能夠拿到 m 了,

為什麼還要特地訓練出一個 m,

後來再看論文和code才理解到要達到 motion blur 的效果。

Discriminator

Conditional image discriminator - DI

讓 Discriminator 可以辨別是自己生成的還是Ground truth,

conditional gan 因此 D 也會輸入原本 G 的輸入(label)

輸入 label 以及 圖片(Ground / 自己生成的)

輸入 (xt, st) 輸出 1

輸入 (\widetilde{x}t, st) 輸出 0

loss function L_{i}:

Conditional video discriminator - DV

DV 注意的是生成的 Frame 之間是否有連貫,

透過給定生成的圖片以及光流法 w 的輸出。

直接看圖片最能理解,

trick 是可以挑不連續的圖片,會學得更好。

上圖出自vid2vid official video

因為影片通常 1 秒鐘 30 frames,

所以間隔多一點也是合理的。

loss function L_{v}:

objective function

最基本的如下:

參數:λw 設定為10, Lw可往上看到光流法那部分。

後續還會介紹幾個loss,所以objective function還會更複雜

  • discriminator feature matching loss
  • VGG feature matching loss

更多的trick

Foreground-background prior

因為我們是輸入label,如街景的資料集有著馬路、建築物、人物、車輛等等。

而對於人物、車輛這種在圖片中相對小,並且移動幅度較大的物體我們應該要分開處理,

因為對於這種物體用光流法其實無法準確測量,

因此提出前景與後景需要分開做,

m_{B,t}是從輸入的 S 直接提取背景的 mask

Coarse-to-fine generator(Multi-scale discriminator)

pix2pixHD coarse to fine 的概念,

細節可去看pix2pixHD

會訓練多個 discriminator

分別處理 512X256 、 1024X512 、 2048X1024

下圖出自vid2vid official video

Multimodal synthesis

可以讓調整生成影片的物件

請去看pix2pixHD

Feature matching loss

請去看pix2pixHD

實驗細節

訓練40個epochs,

ADAM optimizer with lr = 0.0002 and (β1, β2) =(0.5, 0.999)

LSGAN loss

使用DGX1 (8 V100 GPUs, each with 16GB memory)

4個GPU訓練G,4個GPU訓練D。

訓練10天才將2K解析度的vid2vid訓練完。

資料集(簡單帶過)

此模型雖然厲害,但是要訓練也不是這麼的方便,因為我們要有一個輸入labels的輸入影片,

下圖轉載自GTA5 dataset

因此此 Model 訓練方式會依照不同的任務做調整,

Cityscapes:

使用Deeplabv3 取得 Semantic segmentation label,

並且使用 FlowNet2 做光流法的訓練,

使用Mask R-CNN訓練instance segmentation。

Apolloscape:相似Cityscapes

Face video dataset:

使用 Dlib 切出人臉的landmark

Dance video dataset:

pose 分割會使用 DensePose / Openpose

實驗結果

Trace code - 對於光流法部分 W, M

為了方便各位 trace code 我貼出幾段我看到比較關鍵的部分,

請注意註解處有行號 L,只貼出一些我覺得重要的,

如果還是不懂的可以去看原始的程式碼,挺複雜的呢。

https://github.com/NVIDIA/vid2vid/blob/master/train.py

real_B_prev, real_B = real_Bp[:, :-1], real_Bp[:, 1:]   # L:108 the collection of previous and current real frames

# 用下方這個去當 L1 loss 去訓練 G 的 w 部分
flow_ref, conf_ref = flowNet(real_B, real_B_prev)  # L:112 reference flows and confidences

https://github.com/NVIDIA/vid2vid/blob/master/models/flownet.py

# resample 看作將原有圖片經過光流法轉換後的新圖片
def compute_flow_and_conf(self, im1, im2): #L:38
	flow1 = self.flowNet(data1) #L:50
    conf = (self.norm(im1 - self.resample(im2, flow1)) < 0.02).float() #L:51
def norm(self, t): #L:57
        return torch.sum(t*t, dim=1, keepdim=True)   #L:58
def forward(self, scale_T, tensors_list): #L:144
	real_B, fake_B, fake_B_raw, real_A, real_B_prev, fake_B_prev, flow, weight, flow_ref, conf_ref = tensors_list
    ################### Flow loss #################
    if flow is not None:
        # similar to flownet flow        
        loss_F_Flow = self.criterionFlow(flow, flow_ref, conf_ref) * lambda_F / (2 ** (scale_S-1))        
        # warped prev image should be close to current image            
        real_B_warp = self.resample(real_B_prev, flow)                
        loss_F_Warp = self.criterionFlow(real_B_warp, real_B, conf_ref) * lambda_T
        
        ################## weight loss ##################
        loss_W = torch.zeros_like(weight)
        if self.opt.no_first_img:
            dummy0 = torch.zeros_like(weight)
            loss_W = self.criterionFlow(weight, dummy0, conf_ref)
    else:
        loss_F_Flow = loss_F_Warp = loss_W = torch.zeros_like(conf_ref)

    ### Warp loss
    fake_B_warp_ref = self.resample(fake_B_prev, flow_ref)
    loss_G_Warp = self.criterionWarp(fake_B, fake_B_warp_ref.detach(), conf_ref) * lambda_T

參考資料:

Video-to-Video Synthesis

github:vid2vid

vid2vid official video

pix2pixHD

pix2pixHD簡介

光流介绍以及FlowNet學習筆記

FlowNet 2.0: Evolution of Optical Flow Estimation with Deep Networks


© XiaoSean 2018. All rights reserved.

Powered by Hydejack v7.5.1