天天看點

用 Python 實作隐身,我可以 | 文末福利

引言:

視訊和圖像的隐身術是指在視訊或者圖像中中,在沒有任何輸入遮罩的情況下,通過框選目标體,使得程式實作自動去除視訊中的文本疊加和修複被遮擋部分的問題。并且最近的基于深度學習的修複方法隻處理單個圖像,并且大多假設損壞像素的位置是已知的,故我們的目标是在沒有蒙皮資訊的視訊序列中自動去除文本。

今天,我們通過搭建一個簡單而有效的快速視訊解碼器架構去實作視訊中物體的去除。流程是建構一個編碼器-解碼器模型,其中編碼器采用多個源幀,可以提供從場景動态顯示的可見像素。這些提示被聚合并輸入到解碼器中。然後通過應用循環回報進一步改進加強模型。循環回報不僅加強了時間相幹性,而且提供了強大的線索。

實作效果如下可見:

用 Python 實作隐身,我可以 | 文末福利
用 Python 實作隐身,我可以 | 文末福利

模型建立

1.1 環境要求

本次環境使用的是python3.6.5+windows平台

主要用的庫有:

argparse子產品是python自帶的指令行參數解析包,可以用來友善地讀取指令行參數;

subprocess是Python 2.4中新增的一個子產品,它允許你生成新的程序,連接配接到它們的 input/output/error 管道,并擷取它們的傳回(狀态)碼。這個子產品的目的在于替換幾個舊的子產品和方法

numpy子產品用來矩陣和資料的運算處理,其中也包括和深度學習架構之間的互動等。

torch子產品是一個python優先的深度學習架構,是一個和tensorflow,Caffe,MXnet一樣,非常底層的架構在這裡我們用來搭建網絡層和直接讀取資料集操作,簡單友善。

Matplotlib子產品用來可視化訓練效果等資料圖的制作。

1.2 程式的啟動

程式的運作方式如下:

1、直接運作demo.py檔案對圖檔進行處理

2、對視訊進行處理python demo.py --data data/bag.avi。

import argparse
from mask import mask
from inpaint import inpaint
parser = argparse.ArgumentParser(description='Demo')
parser.add_argument('--resume', default='cp/SiamMask_DAVIS.pth', type=str,
                    metavar='PATH', help='path to latest checkpoint (default: none)')
parser.add_argument('--data', default='data/Human6', help='videos or image files')
parser.add_argument('--mask-dilation', default=32, type=int, help='mask dilation when inpainting')
args = parser.parse_args()
mask(args)
inpaint(args)           

複制

1.3 算法概述

視訊中物體的移除目的是從有字幕、有噪聲的視訊幀中預測原始幀。恢複的區域應該和原始的相同大小,或者無縫地融合到周圍的像素中。基本的算法思想是從多個相鄰幀(源幀)中收集提示,然後恢複目标幀。這是為了利用視訊中的場景動态,在視訊中,随着物體的移動或字幕的變化,被遮擋的部分通常會在滞後或引導幀中顯示。同時還可以使用循環回報連接配接作為額外的源流。直接估計一幀中的所有像素可能會不必要地接觸到未損壞的像素。為了解決像素名額缺失的問題,采用殘差學習算法對模型進行訓練。具體來說,最終輸出是通過按像素順序将輸入中心幀和預測殘差圖像相加得到的。這使得我們的網絡明确地隻關注損壞的像素,也防止全局色調失真。

1.4模型的搭建

模型算法核心設計是一個混合的編碼器-解碼器模型,其中編碼器包括兩個子網絡:3D CNN和2D CNN。解碼器遵循一個正常的2D CNN設計,該網絡被設計成完全卷積的,可以處理任意大小的輸入。最後的輸出視訊是通過自回歸的方式應用函數得到的,我們的政策是從多個源幀中收集潛在的線索,這些幀可以提供從場景動态中顯示的可見像素。此外,我們強制目标幀的生成與前一代保持一緻。通過構造一個雙流混合編碼器,其中每個源流都經過訓練以實作我們的目标。第一個編碼器流由3D卷積組成,它可以直接從相鄰幀捕獲時空特征,第二個流是一個2D CNN,它将先前生成的尺寸為H×W×1×C的幀作為輸入。

其中模型生成如下:

try: 
    assert(opt.model == 'vinet_final')
    model = vinet.VINet_final(opt=opt)
except:
    print('Model name should be: vinet_final')
assert(opt.no_cuda is False)
model = model.cuda()
model = nn.DataParallel(model)
loaded, empty = 0,0
if opt.pretrain_path:
    print('Loading pretrained model {}'.format(opt.pretrain_path))
    pretrain = torch.load(opt.pretrain_path)
    child_dict = model.state_dict()
    parent_list = pretrain['state_dict'].keys()
    parent_dict = {}
    for chi,_ in child_dict.items():
        if chi in parent_list:
            parent_dict[chi] = pretrain['state_dict'][chi]
            #print('Loaded: ',chi)
            loaded += 1
        else:
            #print('Empty:',chi)
            empty += 1
    print('Loaded: %d/%d params'%(loaded, loaded+empty))
    child_dict.update(parent_dict)
    model.load_state_dict(child_dict)             

複制

用 Python 實作隐身,我可以 | 文末福利

視訊處理

2.1 預定義

我們的任務将視訊去除目标後盡可能的還原成背景場景。如果場景移動或者字幕在相鄰幀中消失,被遮擋的部分就會被顯示出來,這就為潛在的内容提供了關鍵的線索。為了使增益參數的最大化,需要為我們的模型找到最佳的幀采樣間隔。當最小間隔為1時,輸入幀将包含不重要的動态。另一方面,如果我們以較大的步伐跳躍,不相幹的新場景就會被包括進來。最終通過測試,設定的參數如下:

opt = Object()
opt.crop_size = 512
opt.double_size = True if opt.crop_size == 512 else False
########## DAVIS
DAVIS_ROOT =os.path.join('results', args.data)
DTset = DAVIS(DAVIS_ROOT, mask_dilation=args.mask_dilation, size=(opt.crop_size, opt.crop_size))
DTloader = data.DataLoader(DTset, batch_size=1, shuffle=False, num_workers=1)
opt.search_range = 4  # fixed as 4: search range for flow subnetworks
opt.pretrain_path = 'cp/save_agg_rec_512.pth'
opt.result_path = 'results/inpainting'
opt.model = 'vinet_final'
opt.batch_norm = False
opt.no_cuda = False  # use GPU
opt.no_train = True
opt.test = True
opt.t_stride = 3
opt.loss_on_raw = False
opt.prev_warp = True
opt.save_image = False
opt.save_video = True           

複制

2.2 視訊處理

我們的模型不僅從目前幀中收集線索,還從未來和過去相鄰幀中收集線索。另外,為了保持時間一緻性,有條件地生成每一幀到前一幀的輸出幀。

with torch.no_grad():
    for seq, (inputs, masks, info) in enumerate(DTloader):
        idx = torch.LongTensor([i for i in range(pre - 1, -1, -1)])
        pre_inputs = inputs[:, :, :pre].index_select(2, idx)
        pre_masks = masks[:, :, :pre].index_select(2, idx)
        inputs = torch.cat((pre_inputs, inputs), 2)
        masks = torch.cat((pre_masks, masks), 2)
        bs = inputs.size(0)
        num_frames = inputs.size(2)
        seq_name = info['name'][0]
        save_path = os.path.join(opt.result_path, seq_name)
        if not os.path.exists(save_path) and opt.save_image:
            os.makedirs(save_path)
        inputs = 2. * inputs - 1
        inverse_masks = 1 - masks
        masked_inputs = inputs.clone() * inverse_masks
        masks = to_var(masks)
        masked_inputs = to_var(masked_inputs)
        inputs = to_var(inputs)
        total_time = 0.
        in_frames = []
        out_frames = []
        lstm_state = None
        for t in range(num_frames):
            masked_inputs_ = []
            masks_ = []
            if t < 2 * ts:
                masked_inputs_.append(masked_inputs[0, :, abs(t - 2 * ts)])
                masked_inputs_.append(masked_inputs[0, :, abs(t - 1 * ts)])
                masked_inputs_.append(masked_inputs[0, :, t])
                masked_inputs_.append(masked_inputs[0, :, t + 1 * ts])
                masked_inputs_.append(masked_inputs[0, :, t + 2 * ts])
                masks_.append(masks[0, :, abs(t - 2 * ts)])
                masks_.append(masks[0, :, abs(t - 1 * ts)])
                masks_.append(masks[0, :, t])
                masks_.append(masks[0, :, t + 1 * ts])
                masks_.append(masks[0, :, t + 2 * ts])
            elif t > num_frames - 2 * ts - 1:
                masked_inputs_.append(masked_inputs[0, :, t - 2 * ts])
                masked_inputs_.append(masked_inputs[0, :, t - 1 * ts])
                masked_inputs_.append(masked_inputs[0, :, t])
                masked_inputs_.append(masked_inputs[0, :, -1 - abs(num_frames - 1 - t - 1 * ts)])
                masked_inputs_.append(masked_inputs[0, :, -1 - abs(num_frames - 1 - t - 2 * ts)])
                masks_.append(masks[0, :, t - 2 * ts])
                masks_.append(masks[0, :, t - 1 * ts])
                masks_.append(masks[0, :, t])
                masks_.append(masks[0, :, -1 - abs(num_frames - 1 - t - 1 * ts)])
                masks_.append(masks[0, :, -1 - abs(num_frames - 1 - t - 2 * ts)])
            else:
                masked_inputs_.append(masked_inputs[0, :, t - 2 * ts])
                masked_inputs_.append(masked_inputs[0, :, t - 1 * ts])
                masked_inputs_.append(masked_inputs[0, :, t])
                masked_inputs_.append(masked_inputs[0, :, t + 1 * ts])
                masked_inputs_.append(masked_inputs[0, :, t + 2 * ts])
                masks_.append(masks[0, :, t - 2 * ts])
                masks_.append(masks[0, :, t - 1 * ts])
                masks_.append(masks[0, :, t])
                masks_.append(masks[0, :, t + 1 * ts])
                masks_.append(masks[0, :, t + 2 * ts])
            masked_inputs_ = torch.stack(masked_inputs_).permute(1, 0, 2, 3).unsqueeze(0)
            masks_ = torch.stack(masks_).permute(1, 0, 2, 3).unsqueeze(0)
            start = time.time()

最終完成效果如下:           

複制

複制

完整代碼連結:

https://pan.baidu.com/s/1tCB0MTBbvfSokeU1AAKBQQ

提取碼:nfhk