本文 GitHub https://github.com/Jack-Cherish/PythonPark 已收錄,技術乾貨文章、學習資料、一線大廠面試經驗等應有盡有,歡迎 Star 和 完善。html
1、前言
讓一張圖片,動起來,應該怎麼作?python
DeepFake 一階運動模型,讓萬物皆可動。git
利用這項技術,用特朗普和蒙娜麗莎的圖片,合唱一首《Unravel》,是什麼效果?github
今天,它來了!面試
讓圖片動起來,特朗普和蒙娜麗莎深情合唱《Unravel》算法
今天,繼續手把手教學。shell
算法原理、環境搭建、效果實現,一條龍服務,盡在下文!app
2、算法原理
First Order Motion,也就是一階運動模型,來自 NeurIPS 2019 論文。ide
「First Order Motion Model for Image Animation」學習
論文最初的目的是讓「靜態圖片」動起來。以下圖所示:「你動,它也動」。
這個模型能夠輕易地讓「權利的遊戲」中的人物模仿特朗普進行講話,還可讓靜態的馬跑起來等。
一階運動模型的思想是用一組自學習的關鍵點和局部仿射變換來創建複雜運動模型。
模型由運動估計模塊和圖像生成模塊兩個主要部分組成。
首先進行關鍵點檢測,而後根據關鍵點,進行運動估計,最後使用圖像生成模塊,生成最終效果。
在運動估計模塊中,該模型經過自監督學習將目標物體的外觀和運動信息進行分離,並進行特徵表示。
而在圖像生成模塊中,模型會對目標運動期間出現的遮擋進行建模,而後從給定的圖片中提取外觀信息,結合先前得到的特徵表示,生成圖片。
做者使用該算法在四個數據集上進行了訓練和測試。
VoxCeleb 數據集、UvA-Nemo 數據集、The BAIR robot pushing dataset、做者本身收集的數據集。
其中,VoxCeleb 是一個大型人聲識別數據集。
它包含來自 YouTube 視頻的 1251 位名人的約 10 萬段語音,同時數據基本上是性別平衡的(男性佔 55%),這些名人有不一樣的口音、職業和年齡。
First Order Motion 利用了這個數據集的視頻圖像,進行了模型訓練。
咱們就能夠利用這個訓練好的,人臉的運動估計模型,完成咱們今天的任務。
「特朗普和蒙娜麗莎的深情合唱」。
除了須要用到這個一階運動模型,還須要使用 OpenCV 和 ffmpeg 作視頻、音頻和圖像的處理。
具體的實現,在下文的「效果實現」中說明。
3、環境搭建
效果實現上,咱們能夠直接用已有的庫去實現咱們想要的功能。
「Real Time Image Animation」
項目地址:https://github.com/anandpawara/Real_Time_Image_Animation
Python 爲何這麼受歡迎,就是由於這一點。
有不少開源項目,方便咱們快速實現本身想要的功能,極大下降了開發成本。
真是,誰用誰知道啊。
環境搭建,仍是建議使用 Anaconda,安裝一些必要的第三方庫,能夠參考這篇開發環境搭建的內容:
《Pytorch深度學習實戰教程(一):語義分割基礎與環境搭建》
這個項目須要用到的第三方庫,也都寫的很全:
https://github.com/anandpawara/Real_Time_Image_Animation/blob/master/requirements.txt
直接使用 pip 安裝便可:
Python
1 |
python -m pip install -r requirements.txt |
此外,爲了處理音頻和視頻,還須要配置 ffmpeg。
安裝好 ffmpeg 並配置好環境變量便可。
ffmpeg 下載地址:https://ffmpeg.zeranoe.com/builds/
4、效果實現
實現也很是簡單。
首先,整理一下思路:
「Real Time Image Animation」使用一階運動模型,根據已有視頻,讓靜態圖動起來。
左圖爲原始圖片,中間爲生成結果,右側爲原始視頻。
可是,這個項目只能處理圖像,不能保留音頻。
因此,咱們須要先將音頻保存,再將處理好的視頻和音頻進行合成。
這個功能,就用咱們下載好的 ffmpeg 實現。
編寫以下代碼:
import subprocess import os from PIL import Image def video2mp3(file_name): """ 將視頻轉爲音頻 :param file_name: 傳入視頻文件的路徑 :return: """ outfile_name = file_name.split('.')[0] + '.mp3' cmd = 'ffmpeg -i ' + file_name + ' -f mp3 ' + outfile_name subprocess.call(cmd, shell=True) def video_add_mp3(file_name, mp3_file): """ 視頻添加音頻 :param file_name: 傳入視頻文件的路徑 :param mp3_file: 傳入音頻文件的路徑 :return: """ outfile_name = file_name.split('.')[0] + '-f.mp4' subprocess.call('ffmpeg -i ' + file_name + ' -i ' + mp3_file + ' -strict -2 -f mp4 ' + outfile_name, shell=True)
搞定,視頻轉音頻,以及音頻合成都搞定了。
咱們須要對「Real Time Image Animation」這個項目進行修改,修改 image_animation.py 文件。
import imageio import torch from tqdm import tqdm from animate import normalize_kp from demo import load_checkpoints import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation from skimage import img_as_ubyte from skimage.transform import resize import cv2 import os import argparse import subprocess import os from PIL import Image def video2mp3(file_name): """ 將視頻轉爲音頻 :param file_name: 傳入視頻文件的路徑 :return: """ outfile_name = file_name.split('.')[0] + '.mp3' cmd = 'ffmpeg -i ' + file_name + ' -f mp3 ' + outfile_name print(cmd) subprocess.call(cmd, shell=True) def video_add_mp3(file_name, mp3_file): """ 視頻添加音頻 :param file_name: 傳入視頻文件的路徑 :param mp3_file: 傳入音頻文件的路徑 :return: """ outfile_name = file_name.split('.')[0] + '-f.mp4' subprocess.call('ffmpeg -i ' + file_name + ' -i ' + mp3_file + ' -strict -2 -f mp4 ' + outfile_name, shell=True) ap = argparse.ArgumentParser() ap.add_argument("-i", "--input_image", required=True,help="Path to image to animate") ap.add_argument("-c", "--checkpoint", required=True,help="Path to checkpoint") ap.add_argument("-v","--input_video", required=False, help="Path to video input") args = vars(ap.parse_args()) print("[INFO] loading source image and checkpoint...") source_path = args['input_image'] checkpoint_path = args['checkpoint'] if args['input_video']: video_path = args['input_video'] else: video_path = None source_image = imageio.imread(source_path) source_image = resize(source_image,(256,256))[..., :3] generator, kp_detector = load_checkpoints(config_path='config/vox-256.yaml', checkpoint_path=checkpoint_path) if not os.path.exists('output'): os.mkdir('output') relative=True adapt_movement_scale=True cpu=False if video_path: cap = cv2.VideoCapture(video_path) print("[INFO] Loading video from the given path") else: cap = cv2.VideoCapture(0) print("[INFO] Initializing front camera...") fps = cap.get(cv2.CAP_PROP_FPS) size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))) video2mp3(file_name = video_path) fourcc = cv2.VideoWriter_fourcc('M','P','E','G') #out1 = cv2.VideoWriter('output/test.avi', fourcc, fps, (256*3 , 256), True) out1 = cv2.VideoWriter('output/test.mp4', fourcc, fps, size, True) cv2_source = cv2.cvtColor(source_image.astype('float32'),cv2.COLOR_BGR2RGB) with torch.no_grad() : predictions = [] source = torch.tensor(source_image[np.newaxis].astype(np.float32)).permute(0, 3, 1, 2) if not cpu: source = source.cuda() kp_source = kp_detector(source) count = 0 while(True): ret, frame = cap.read() frame = cv2.flip(frame,1) if ret == True: if not video_path: x = 143 y = 87 w = 322 h = 322 frame = frame[y:y+h,x:x+w] frame1 = resize(frame,(256,256))[..., :3] if count == 0: source_image1 = frame1 source1 = torch.tensor(source_image1[np.newaxis].astype(np.float32)).permute(0, 3, 1, 2) kp_driving_initial = kp_detector(source1) frame_test = torch.tensor(frame1[np.newaxis].astype(np.float32)).permute(0, 3, 1, 2) driving_frame = frame_test if not cpu: driving_frame = driving_frame.cuda() kp_driving = kp_detector(driving_frame) kp_norm = normalize_kp(kp_source=kp_source, kp_driving=kp_driving, kp_driving_initial=kp_driving_initial, use_relative_movement=relative, use_relative_jacobian=relative, adapt_movement_scale=adapt_movement_scale) out = generator(source, kp_source=kp_source, kp_driving=kp_norm) predictions.append(np.transpose(out['prediction'].data.cpu().numpy(), [0, 2, 3, 1])[0]) im = np.transpose(out['prediction'].data.cpu().numpy(), [0, 2, 3, 1])[0] im = cv2.cvtColor(im,cv2.COLOR_RGB2BGR) #joinedFrame = np.concatenate((cv2_source,im,frame1),axis=1) #joinedFrame = np.concatenate((cv2_source,im,frame1),axis=1) #cv2.imshow('Test',joinedFrame) #out1.write(img_as_ubyte(joinedFrame)) out1.write(img_as_ubyte(im)) count += 1 # if cv2.waitKey(20) & 0xFF == ord('q'): # break else: break cap.release() out1.release() cv2.destroyAllWindows() video_add_mp3(file_name='output/test.mp4', mp3_file=video_path.split('.')[0] + '.mp3')
而後下載算法須要的權重文件和視頻圖片素材。
修改好的代碼、權重文件、視頻圖片素材,我都已經打包好了,拿來直接用也能夠。
下載連接(密碼:amz5):https://pan.baidu.com/s/1TEd7SOaO5mzPaxpOh2pALQ
運行命令:
python image_animation.py -i path_to_input_file -c path_to_checkpoint -v path_to_video_file
path_to_input_file 是輸入的模板圖片
path_to_checkpoint 是權重文件路徑
path_to_video_file 是輸入的視頻文件
若是使用我打包好的程序,可使用以下指令直接運行,得到文章開頭的視頻:
python image_animation.py -i Inputs/trump2.png -c checkpoints/vox-cpk.pth.tar -v 1.mp4
最後生成的視頻存放在 output 文件夾下。
大功告成!
5、最後
算法處理視頻的速度很快,用 GPU 幾秒鐘就能搞定。
本文 GitHub https://github.com/Jack-Cherish/PythonPark 已收錄,技術乾貨文章、學習資料、一線大廠面試經驗等應有盡有,歡迎 Star 和 完善。