機器的心臟被釋放
不久前,AV視頻變臉明星DeepFake走紅。這篇文章將教你如何一步一步地改變你的臉。
如果這是你第一次聽說DeepFake,一定要點擊上面的視頻,親身感受一下尼古拉斯的臉是如何占據(jù)世界上每一部電影的。
項目實戰(zhàn)
我們在視頻中是如何變臉的?
因為視頻是一個連續(xù)的畫面,我們只需要切換每張畫面中的人臉,就可以得到一個新的變了臉的視頻。那么如何在一個視頻中切換畫面呢?這就需要我們先找到視頻中的人臉,然后切換人臉。我們會發(fā)現(xiàn)變臉的問題可以分解成以下幾個過程。
所以后續(xù)我們會按照這五個步驟來做。
視頻到圖像
FFmpeg
FFmpeg提供了一個用于處理音頻、視頻、字幕和相關(guān)源數(shù)據(jù)的工具庫。核心庫包括:
libavcodec 提供了處理編碼的能力libavformat 實現(xiàn)了流協(xié)議、容器類型、基本的 I/O 訪問libavutil 包括哈希、解壓縮等多樣的功能libavfilter 提供了鏈式修改音頻和視頻的能力libavdevice 提供了對設(shè)備訪問的抽象libswresample 實現(xiàn)了混音等能力libswscale 實現(xiàn)了顏色和尺度變換的能力主要向外界提供三種工具:
ffmpeg 用來處理多媒體內(nèi)容ffplay 是一個極簡的播放器ffprobe 是多媒體內(nèi)容的分析工具所以我們的視頻轉(zhuǎn)圖片功能可以通過下面的命令來實現(xiàn)。
ffmpeg-I clip name-VF fps = frame rate-qscale:v 2 " imagename % 04d . jpg "/>
(來源:DLIB、OpenCV和Python的面部標志)
這個算法的效果如上所示。它將人臉分為以下幾個區(qū)域:
眼睛 (左/右)眉毛 (左/右)鼻子嘴下巴基于這些標記,我們不僅可以改變我們的臉,還可以檢測我們的臉的特定形狀和眨眼狀態(tài)。例如,我們可以連接這些點來獲得更多的特征。
(來源:實時人臉姿態(tài)估計)
尋找面部標志是一個預(yù)測問題。輸入是圖片和感興趣區(qū)域,輸出是感興趣區(qū)域的關(guān)鍵點。
HOG是怎么找到臉的?這是一個通用的檢測算法:
從數(shù)據(jù)集中找到正樣本,并且計算 HOG 描述從數(shù)據(jù)集中找到負樣本,并且計算 HOG 描述基于 HOG 的描述使用分類算法在負樣本上在不同的起點和尺度進行分類,并且找到誤判的 HOG基于上一步的負樣本,對模型進行重新的訓練這里有個問題,HOG的描述怎么算?我們可以計算每個點的亮度,然后將每個點表示為指向更暗方向的向量。如下圖所示:
(來源:機器學習很好玩!第四部分:深度學習的現(xiàn)代人臉識別)
(來源:機器學習很好玩!第四部分:深度學習的現(xiàn)代人臉識別)
我們?yōu)槭裁匆@么做?因為每個點的絕對值都會受到環(huán)境的影響,但是相對值是相對穩(wěn)定的。因此,我們可以通過梯度變化的表達來準備高質(zhì)量的數(shù)據(jù)。當然,我們可以進一步聚合相鄰點,產(chǎn)生更具代表性的數(shù)據(jù)。
現(xiàn)在你可以測試了
首先在新的圖片上基于不同的起點和尺度尋找可行的區(qū)間;基于非極大抑制的方法來減少冗余和重復(fù)的,下圖就是一個有冗余和去除冗余的情況,這個方法說白了就是找一個最大概率的矩陣去覆蓋掉和它過于重合的矩陣,并且不斷重復(fù)這個過程。(來源:方向梯度直方圖和目標檢測)
有了輪廓,我們就能找到面部標志。尋找面部標記的算法是基于論文“用回歸樹集合進行一百萬張面部對齊”。簡而言之,它使用標記的訓練集來訓練回歸樹的組合進行預(yù)測。
(來源:回歸樹集合的1毫秒面對齊)
在此基礎(chǔ)上,可以對這68點進行標注。
(來源:DLIB、OpenCV和Python的面部標志)
基于人臉68個標記的坐標,可以計算出人臉的度數(shù),進而可以挑出拉直的人臉。但是dlib需要全臉識別,會減少我們的樣本集和一些特定的樣本場景。同時,由于人臉尺寸為64*64像素,所以需要處理清晰度的問題。
另一種方法是利用CNN訓練一個人臉識別模型。CNN可以檢測更多的學位,但是需要更多的資源,可能會在大文件上失敗。
數(shù)據(jù)準備
我們的目標是將原始人臉轉(zhuǎn)換成目標人臉,所以我們需要收集原始人臉和目標人臉的圖片。如果選擇名人,可以直接用Google image得到想要的圖片。雖然視頻中的圖片也可以使用,但也可以采集各種數(shù)據(jù)。當然,我使用我和我妻子的照片,所以我可以直接從我們的照片導(dǎo)出它們。生成人臉數(shù)據(jù)時,最好仔細檢查,避免不合適的人臉或其他東西出現(xiàn)在你的訓練集中。
extract.py
Deepfake用來定位人臉的算法如下:
Importcv2 #開源計算機視覺庫
Frompathlib importPath #提供面向?qū)ο蟮奈募L問
Fromtqdm importtqdm #提供進度條顯示功能
Importos #提供與操作系統(tǒng)相關(guān)的訪問
Importnumpy asnp #提供與科學計算相關(guān)的功能
from lib . cliimportdirectory processor,rotate _ image #處理一個目錄的文件,然后將它們保存在一個新目錄中;旋轉(zhuǎn)圖片實際上是在實用程序中
從lib.utils importget_folder #中獲取一個文件夾,如果它不存在,則創(chuàng)建它。
來自lib。多線程導(dǎo)入pool _ process #多進程并發(fā)計算
來自lib。detect _ blur import is _ blur #判斷圖片是否模糊
來自插件。pluginloader Importpluginloader #加載相應(yīng)的算法
分類追蹤數(shù)據(jù)(目錄處理器):#從訓練集中提取頭像
defcreate_parser(self,subparser,command,deion):
self . optional _ arguments = self . get _ optional _ arguments()
self . parser = submisser . add _ parser(
命令,
help= "從圖片中提取人臉。",
deion=deion,
epilog= "問題和反饋:
https://github.com/deepfakes/faceswap-playground"
)
#省略參數(shù)配置
defprocess(自我):
提取器名稱=“對齊#”對應(yīng)于提取對齊
self . extractor = PluginLoader . get _ extractor(extractor _ name)()
processes = self . arguments . processes
嘗試:
ifprocesses!= 1: #多進程處理圖片
files = list(self.read_directory())
forfilename,faces int qdm(pool _ process(self . process Files,files,processes=processes),total = len(files)):
self.num_faces_detected += 1
self . faces _ detected[OS . path . base name(filename)]= faces
Else: #單進程處理圖片
for filename int qdm(self . read _ directory()):
嘗試:
image = cv2.imread(文件名)
self . faces _ detected[OS . path . basename(文件名)] = self.handleImage(圖像,文件名)
例外情況:
ifself.arguments.verbose:
打印('無法從圖像提取:{}。原因:{} '。格式(文件名,e))
及格
最后:
self.write_alignments()
Defprocessfiles (self,filename): #處理單個圖片的函數(shù)
嘗試:
image = cv2.imread(文件名)
returnfilename,self.handleImage(圖像,文件名)
例外情況:
ifself.arguments.verbose:
打印('無法從圖像提取:{}。原因:{} '。格式(文件名,e))
及格
returnfilename,[]
DefgetRotatedImageFaces(自身,圖像,角度):#獲取以固定角度旋轉(zhuǎn)的圖片的面
旋轉(zhuǎn)圖像=旋轉(zhuǎn)圖像(圖像,角度)
face = self . get _ faces(rotated _ image,rotation=angle)
rotated_faces = [(idx,face) foridx,face infaces]
returnrotated_faces,rotated_image
定義旋轉(zhuǎn)體(自我,圖像):#獲得一系列旋轉(zhuǎn)的面
''通過rotation_angles旋轉(zhuǎn)圖像以嘗試找到一張臉'''
forangle inself.rotation_angles:
旋轉(zhuǎn)的面,旋轉(zhuǎn)的圖像=自。獲取旋轉(zhuǎn)的圖像面(圖像,角度)
iflen(旋轉(zhuǎn)面)>;0:
ifself.arguments.verbose:
通過旋轉(zhuǎn)圖像{}度打印('找到的面')。格式(角度))
破裂
returnrotated_faces,rotated_image
defhandleImage(自身、圖像、文件名):
faces = self.get_faces(圖像)
process_faces = [(idx,face) foridx,face infaces]
#未找到人臉,嘗試旋轉(zhuǎn)圖片
if self . rotation _ angles is not none and len(process _ faces)= 0:
process_faces,image = self.imageRotator(image)
rvals = []
foridx,face inprocess_faces:
#標記面部
if self . arguments . debug _ landmarks:
for(x,y)inface . landmarksaxy():
cv2.circle(圖像,(x,y),2,(0,0,255),-1)
resized_image,t _ mat = self . extractor . extract(image,face,256,self.arguments.align_eyes)
output _ file = get _ folder(self . output _ dir)/Path(filename)。阻止
#檢查圖片是否模糊
if self . arguments . blur _ thresh is notnone:
aligned _ landmark = self . extractor . transform _ points(face . landmarksaxy(),t_mat,256,48)
feature _ mask = self . extractor . get _ feature _ mask(aligned _ landmarks/256,256,48)
feature _ mask = cv2 . blur(feature _ mask,(10,10))
isolated _ face = cv2 . multiply(feature _ mask,resized_image.astype(float))。astype(np.uint8)
模糊,focus _ measure = is _ fuzzy(isolated _ face,self.arguments.blur_thresh)
# print(“{}焦點度量:{ }”。格式(路徑(文件名)。stem,focus_measure))
# cv2.imshow("孤立臉",孤立臉)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
如果模糊:
print(“{}”的焦點度量{ }低于模糊閾值,移至“模糊”。格式(路徑(文件名)。stem,focus_measure))
output_file = get_folder(路徑(self . output _ dir)/Path(" further "))/Path(文件名)。阻止
通過cv2.imwrite ('{} _ {} {} '生成新圖片。格式(str (output _ file)、str (idx)、路徑(文件名)。后綴),調(diào)整大小_圖像)#
f = {
“r”:face . r,
“x”:face . x,
“w”:face . w,
“y”:臉. y,
“h”:臉. h,
【landmarksXY】:face . landmarksXY()
}
rvals.append(f)
returnrvals
注意,基于特征標簽的算法對于傾斜面是無效的,也可以引入CNN。
面部轉(zhuǎn)換
人臉變換的基本原理是什么?假設(shè)你連續(xù)盯著一個人的視頻看100個小時,然后給你看另一個人的照片,然后讓你憑記憶畫出剛才的照片,你一定會像第一個人一樣畫出來。
我們使用的模型是自動編碼器。有趣的是,這個模型所做的是在原始圖像的基礎(chǔ)上重新生成原始圖像。Autoencoder的編碼器壓縮圖片,解碼器還原圖片。一個例子如下:
(資料來源:在喀拉斯建立自動編碼器)
在此基礎(chǔ)上,即使我們輸入另一個人臉,也會被Autoencoder編碼成與原人臉相似的人臉。
為了提高我們的最終效果,我們還需要學習與人臉共性和人臉特征相關(guān)的屬性。因此,我們對所有人臉使用統(tǒng)一的編碼器。這個編碼器的目的是學習人臉的共性;然后,我們對每張臉都有一個單獨的解碼器。這個解碼器是學習臉的個性。這樣,當你用B的臉通過編碼器,然后用A的解碼器,你會得到一個和B的表情一致但A的臉。
該過程由以下公式表示:
' X' =解碼器(編碼器(洗牌(X)))
損失= L1損失(X'-X)
A' = Decoder_A(編碼器(Shuffle(A)))
損失_ A =損失(A'-A)
' B' = Decoder_B(編碼器(Shuffle(B)))
損失_ B =損失(B'-B)
具體來說,在訓練過程中,我們輸入A的圖片,通過編碼器和解碼器還原A的臉;然后我們輸入B的圖片,通過相同的編碼器但是不同的解碼器還原B的臉。重復(fù)這個過程,直到損失降到一個閾值。在模型的訓練過程中,我建議把損失降到0.02,這樣效果會更好。
這里用的是比較標準的建模方法。值得注意的是,作者通過添加PixelShuffler()的功能來扭曲圖像,增加了學習的難度,使模型達到了最終的效果。仔細想想這背后的原因。如果你一直做簡單的問題,你就不能解決困難的問題。不過我只需要在題目上做一些變動,就足以讓你成功了。
因為在建模中用原始圖像A的失真來還原A,在應(yīng)用中用B來還原A,所以失真的方式會對最終結(jié)果產(chǎn)生很大的影響。因此,如何選擇更好的失真公式也是一個重要的問題。
我們的圖片合并的時候會出現(xiàn)一個難題,如何保證效果,防止圖片抖動。因此,我們需要引入相關(guān)算法來處理這些情況。所以我們可以知道,一個看似直接的人臉轉(zhuǎn)換算法,在實際操作中需要考慮各種特殊情況,這才是真正的接地氣。
train.py
以下是用于訓練的算法邏輯:
Importcv2 #開源計算機視覺庫
Importnumpy #提供與科學計算相關(guān)的功能
Importtime #提供與時間相關(guān)的功能
Importthreading #提供多線程相關(guān)功能
來自lib。utils import get _ image _ paths,get _ folder #獲取目錄中的圖片;獲取一個文件夾,如果它不存在,就創(chuàng)建它
fromlib.cli importFullPaths,argparse,os,sys
來自插件。pluginloader Importpluginloader #加載相應(yīng)的算法
tf =無
set_session =無
Defimport_tensorflow_keras():#需要時加載tensorflow和keras模塊
'''僅當需要時才導(dǎo)入張量流和keras集_會話模塊'''
globaltf
globalset_session
iftf isNoneorset _ session isNone:
importtensorflow
Importkeras。后端. tensorflow _后端# keras依賴底層tensorflow來實現(xiàn)特定的操作
tf =張量流
set_session = keras .后端. tensorflow _后端. set_session
分類訓練處理器(對象):#訓練器
參數(shù)=無
Def _ _ init _ _ (self,subparser,command,deion =' default'): #初始化教練
self . argument _ list = self . get _ argument _ list()
self . optional _ arguments = self . get _ optional _ arguments()
self.parse_arguments(deion,subparser,command)
自鎖=穿線。鎖定()
defprocess_arguments(self,arguments):
self.arguments =參數(shù)
打印(“型號A目錄:{}”。格式(self.arguments.input_A))
打印(“型號B目錄:{}”。格式(self.arguments.input_B))
打印(“培訓數(shù)據(jù)目錄:{}”。格式(self.arguments.model_dir))
self.process()
#省略參數(shù)配置
@staticmethod
Defget_optional_arguments():#創(chuàng)建一個數(shù)組來保存參數(shù)
''將參數(shù)放在列表中,以便可以從argparse和gui訪問它們'''
#為自定義參數(shù)覆蓋此選項
參數(shù)列表= []
返回文檔列表(_ l)
defparse_arguments(self,deion,subparser,command):
parser = subparser.add _ parser(
命令,
help= "這個命令為A和b兩個面訓練模型。
deion=deion,
epilog= "問題和反饋:
https://github.com/deepfakes/faceswap-playground")
foroption inself.argument_list:
args = option['opts']
kwargs = { key:option[key]forkey in operation . keys()if key!= 'opts'}
parser.add_argument(*args,**kwargs)
parser = self . add _ optional _ arguments(parser)
parser . set _ defaults(func = self . process _ arguments)
def addd _ optional _ arguments(self,parser):
for option in self . optional _ arguments:
args = option['opts']
kwargs = { key:option[key]forkey in operation . keys()if key!= 'opts'}
parser.add_argument(*args,**kwargs)
返回分析器
定義過程(自我):#具體執(zhí)行
自我停止=假
self.save_now = False
Thr =線程。線程(target = self。processthread,args =(),kwargs = {}) #線程執(zhí)行
thr.start()
ifself.arguments.preview:
打印(“使用實時預(yù)覽”)
同時:
嘗試:
withself.lock:
forname,image in self . preview _ buffer . items():
cv2.imshow(名稱,圖像)
key = cv2.waitKey(1000)
if key = = order(' n ')orkey = = order(' r '):
破裂
if key = = order(' s '):
自我。保存_現(xiàn)在=真
例外鍵盤中斷:
破裂
else:
嘗試:
input() # TODO如何捕捉特定的鍵而不是Enter?
#沒有好的多平臺解決方案:https://stack overflow . com/questions/3523174/raw-input-in-python-with-press-enter
例外鍵盤中斷:
及格
打印(“要求退出!教練將完成當前周期,保存模型并退出(根據(jù)您的訓練速度,可能需要幾秒鐘)。如果你現(xiàn)在想干掉它,按Ctrl + c”)
自我停止=真
thr.join() #等待線程結(jié)束
defprocessThread(自):
嘗試:
ifself.arguments.allow_growth:
self.set_tf_allow_growth()
打印('正在加載數(shù)據(jù),這可能需要一段時間...')#加載數(shù)據(jù)
#這樣您就可以為trainer輸入不區(qū)分大小寫的值
培訓師= self.arguments .培訓師
trainer = " LowMem " if trainer . lower()= = " LowMem " else trainer
Model = pluginloader。get _ model(trainer)(get _ folder(self。arguments.model _ dir),self。arguments.gpus) #讀取模型
模型加載(交換=假)
images _ a = get _ image _ path(self。arguments.input _ a) #圖片a
images _ b = get _ image _ path(self。arguments.input _ b) #圖片b
trainer = pluginloader . get _ trainer(trainer)#創(chuàng)建一個教練
培訓師=培訓師(模型,images _ a,images _ b,self。arguments.batch _ size,self。arguments.perceptual _ loss) #設(shè)置教練參數(shù)
打印('開始。按“回車”停止訓練并保存模型)
前傳范圍(0,self . arguments . epoch):
save _ iteration = epoch % self . arguments . save _ interval = = 0
火車。train _ one _ step (epoch,self。顯示if (save _ iteration或self。立即保存)其他無)#
ifsave_iteration:
model.save_weights()
ifself.stop:
破裂
ifself.save_now:
model.save_weights()
self.save_now = False
model.save_weights()
退出(0)
例外鍵盤中斷:
嘗試:
model.save_weights()
例外鍵盤中斷:
打印('保存模型重量已被取消!')
退出(0)
例外情況:
raisee
出口(1)
defset_tf_allow_growth(自我):
import_tensorflow_keras()
config = tf。ConfigProto()
允許增長=真
config . GPU _ options . visible _ device _ list = " 0 "
set_session(tf。會話(配置=配置))
preview_buffer = {}
Defshow(self,image,name=''):#提供預(yù)覽
嘗試:
ifself.arguments.redirect_gui:
path = OS . path . real path(OS . path . dirname(sys . argv[0])
img = '。' gui_preview.png '
imgfile = os.path.join(path,img)
cv2.imwrite(imgfile, image)cv2.imwrite(imgfile,image)
elifself.arguments.preview:
withself.lock:
self . preview _ buffer[name]= image
elifself.arguments.write_image:
cv2.imwrite('_sample_{}。jpg '。格式(名稱、圖像)
例外情況:
打印(“無法預(yù)覽樣本”)
raisee
培訓師. py
下面實現(xiàn)了一個具體的訓練:
importtime
importnumpy
from lib . training _ data importtrainingdata generator,stack_images
classTrainer():
Random_transform_args = {#初始化參數(shù)
rotation_range': 10,
zoom_range': 0.05,
shift_range': 0.05,
random_flip': 0.4,
}
def__init__(自,模型,fn_A,fn_B,批處理大小,*參數(shù)):
self.batch_size = batch_size
self.model =模型
發(fā)電機=訓練數(shù)據(jù)發(fā)電機(自。random _ transform _ args,160) #讀取所需數(shù)據(jù)
self . images _ A = generator . minibatchab(fn _ A,self.batch_size)
self . images _ B = generator . minibatchab(fn _ B,self.batch_size)
Def train _ one _ step (self,ITER,觀眾):# train one step
紀元,扭曲_A,目標_A =下一個
紀元,扭曲_B,目標_B =下一個
Loss _ a = self。模特。autoencoder _ a.train _ on _ batch(翹曲_ a,target _ a) #計算損失
loss _ B = self . model . auto encoder _ B . train _ on _ batch(warped _ B,target_B)
打印(“[{0}] [#{1:05d}] loss_A: {2:.5f},loss_B: {3:.5f}”。格式(time . str time(" % H:% M:% S "),iter,loss_A,loss_B),
end='r ')
ifviewer不是無:
查看器(self . show _ sample(target _ A[0:14],target_B[0:14]),“培訓”/>
來源:朋友圈視頻人物變臉是怎么煉成的?
我們可以看到,經(jīng)過四個卷積層、擴展層和全連接層,我們開始了整個高檔模型。在我們高檔的中間,我們進行了編碼器和解碼器
去切割,從而保證共性與個性的分離。
convert.py
在訓練的基礎(chǔ)上,現(xiàn)在可以轉(zhuǎn)換圖片了。
importcv2
進口
importos
frompathlib導(dǎo)入路徑
從qdm導(dǎo)入
fromlib.cli導(dǎo)入目錄處理器,完整路徑
from lib . utils importBackgroundGenerator,get_folder,get _ image _ paths,rotate_image
fromplugins。插件加載器導(dǎo)入插件加載器
類別轉(zhuǎn)換圖像(目錄處理器):
文件名= ' '
defcreate_parser(self,subparser,command,deion):
self . optional _ arguments = self . get _ optional _ arguments()
self . parser = submisser . add _ parser(
命令,
help= "將源圖像轉(zhuǎn)換為新圖像,并交換臉部。",
deion=deion,
epilog= "問題和反饋:
https://github.com/deepfakes/faceswap-playground"
)
#省略參數(shù)配置
定義過程(自):#轉(zhuǎn)換和拼接模型
#原創(chuàng)& amp低內(nèi)存型號配有調(diào)整或屏蔽轉(zhuǎn)換器
#注意:GAN預(yù)測輸出一個遮罩+一個圖像,而其他預(yù)測只輸出一個圖像
model _ name = self . arguments . trainer
conv _ name = self . arguments . converter
self.input_aligned_dir =無
model = pluginloader . get _ model(model _ name)(get _ folder(self . arguments . model _ dir),self.arguments.gpus)
if not model . load(self . arguments . swap _ model):
打印('找不到型號!必須提供有效的模型才能繼續(xù)!)
出口(1)
input _ aligned _ dir = Path(self . arguments . input _ dir)/Path(' aligned ')
if self . arguments . input _ aligned _ dir is notnone:
input _ aligned _ dir = self . arguments . input _ aligned _ dir
嘗試:
self . input _ aligned _ dir =[Path(Path)for Path inget _ image _ Path(input _ aligned _ dir)]
iflen(self . input _ aligned _ dir)= 0:
打印('對齊目錄為空,不會轉(zhuǎn)換任何面!')
Eli flen(self . input _ aligned _ dir)& lt;= len(self.input_dir)/3:
打印('對齊目錄包含的圖像數(shù)量遠遠少于輸入,您確定這是正確的目錄嗎?')
除了:
打印('找不到對齊的目錄。將轉(zhuǎn)換路線文件中列出的所有面。)
converter = pluginloader . get _ converter(conv _ name)(model . converter(False),
培訓師=self.arguments .培訓師,
blur _ size = self . arguments . blur _ size,
無縫_克隆=self.arguments .無縫_克隆,
銳化_圖像=self.arguments .銳化_圖像,
mask _ type = self . arguments . mask _ type,
糜爛_內(nèi)核_大小=self.arguments .糜爛_內(nèi)核_大小,
match _直方圖= self . arguments . match _直方圖,
small _ mask = self . arguments . small _ mask,
avg _ color _ adjust = self . arguments . avg _ color _ adjust
)
batch = background generator(self . prepare _ images(),1)
#框架范圍素材...
自框架_范圍=無
#分割出幀范圍并解析出“最小值”和“最大值”
minmax = {
“最小”:0,#從不小于0的任何幀
“最大”:浮點(“inf”)
}
ifself.arguments.frame_ranges:
self . frame _ ranges =[tuple(map(lambdaq:minmax[q]ifq in max . keys()else int(q),v . split("-"))for v in self . arguments . frame _ ranges]
#最后一個數(shù)字regex。我知道regex是hacky,但是它的可靠性很差(tm)。
self . imageidxre = re . compile(r '(d+)(?!。*d)')
foritem inbatch.iterator():
self.convert(轉(zhuǎn)換器,項目)
defcheck_skipframe(self,filename):
嘗試:
idx = int(self . imageidxre . findall(文件名)[0])
returnnotany(map(lambda b:b[0]& lt;=idx<。=b[1],self.frame_ranges))
除了:
returnFalse
defcheck_skipface(self,filename,face_idx):
aligned_face_name = '{}_{}{} '。格式(路徑(文件名)。詞干,face_idx,路徑(文件名)。后綴)
對齊_面_文件=路徑(自參數(shù).輸入_對齊_目錄)/路徑(對齊_面_名稱)
#待辦事項:刪除文件名向后兼容性的臨時修復(fù)
bk _ compat _ aligned _ face _ name = ' { } { } { } '。格式(路徑(文件名)。詞干,face_idx,路徑(文件名)。后綴)
bk _ compat _ aligned _ face _ file = Path(self . arguments . input _ aligned _ dir)/Path(bk _ compat _ aligned _ face _ name)
returnaligned _ face _ file not senself . input _ aligned _ dir和k _ compat _ aligned _ face _ file not senself . input _ aligned _ dir
defconvert(自身、轉(zhuǎn)換器、項目):
嘗試:
(文件名、圖像、面孔)=項目
skip = self.check_skipframe(文件名)
if self . arguments . discard _ frames and kip:
返回
ifnotskip: #正常處理框架
foridx,face infaces:
if self . input _ aligned _ dir isnotnoneandself . check _ skip face(文件名,idx):
打印(框架{}的面{}已刪除,跳過)。格式(idx,os.path.basename(filename)))
繼續(xù)
#在映射面之前,檢查圖像旋轉(zhuǎn)和旋轉(zhuǎn)
ifface.r!= 0:
高度,寬度= image.shape[:2]
image = rotate_image(image,face.r)
image = converter . patch _ image(image,face,64if " 128 " not senself . arguments . trainer else 128)
# TODO:64和128之間的這個切換目前是一個黑客。對于大小,我們應(yīng)該有一個單獨的cli選項
image = rotate_image(image,face.r * -1,rotated_width=width,rotated_height=height)
else:
image = converter . patch _ image(image,face,64if " 128 " not senself . arguments . trainer else 128)
# TODO:64和128之間的這個切換目前是一個黑客。對于大小,我們應(yīng)該有一個單獨的cli選項
output _ file = get _ folder(self . output _ dir)/Path(filename)。姓名
cv2.imwrite(str(output_file),image)
例外情況:
打印('轉(zhuǎn)換圖像失敗:{}。原因:{} '。格式(文件名,e))
defprepare_images(自):
self.read_alignments()
is _ has _ alignments = self . has _ alignments()
for filename int qdm(self . read _ directory()):
image = cv2.imread(文件名)
ifis _ have _ alignments:
ifself.have_face(文件名):
faces = self.get _ faces _ alignments(文件名,圖像)
else:
tqdm . write({找不到}的對齊方式,跳過)。格式(os.path.basename(filename)))
繼續(xù)
else:
faces = self.get_faces(圖像)
yieldfilename,image,faces
當然,我們也可以用GAN算法進行優(yōu)化,那么我們來看看使用GAN的模型。
(資料來源:邵安路/faceswap-GAN)
如上圖所示,我們首先推導(dǎo)出A的人臉,然后對其進行變換,再經(jīng)過編碼和解碼,生成重構(gòu)的人臉和Mask。以下是我們的學習目標。
(資料來源:邵安路/faceswap-GAN)
從圖片到視頻
根據(jù)我們對FFmpeg的解釋,我們可以使用以下命令將一批圖片組合成一個視頻:
ffmpeg-f image2-I imagename % 04d . jpg-vcodec libx 264-CRF 15-pix _ fmt YUV 420 p output _ filename . MP4
如果希望新生成的視頻有聲音,可以將音頻視頻中的聲音拼接到最終的目標視頻。
云平臺部署
我們可以在谷歌云部署云平臺。詳情請看視頻秀。我將在這里展示幾個關(guān)鍵步驟:
(來源:如何用谷歌云GPU服務(wù)創(chuàng)建深度假貨)
(來源:如何用谷歌云GPU服務(wù)創(chuàng)建深度假貨)
(來源:如何用谷歌云GPU服務(wù)創(chuàng)建深度假貨)
(來源:如何用谷歌云GPU服務(wù)創(chuàng)建深度假貨)
最后是我在Google Cloud上培訓的截圖。
項目架構(gòu)
最后,讓我們從一個較高的層次來理解整個DeepFake項目的架構(gòu)。
社會影響
我們已經(jīng)談過Deepfake的原理,那么它真正的社會價值是什么?我們可以和任何人一起拍電影,然后成為我們想要的任何人。我們可以創(chuàng)造出更逼真的虛擬人物。穿衣購物可以更逼真的模擬。
摘要
我們使用了以下技術(shù)堆棧、框架和平臺:
Dlib:基于 C++的機器學習算法庫 OpenCV:計算機視覺算法庫 Keras:在底層機器學習框架之上的高級 API 架構(gòu) TensorFlow:Google 開源的機器學習算法框架 CUDA:Nvidia 提供的針對 GPU 加速的開發(fā)環(huán)境Google Cloud Platform:Google 提供的云計算服務(wù)平臺 Virtualenv:創(chuàng)建獨立的 Python 環(huán)境 FFmpeg:多媒體音視頻處理開源庫現(xiàn)在就來上手,把你心愛的另一半的人臉搬上好萊塢吧。這篇文章是為機器的心臟而發(fā)表的。請聯(lián)系本微信官方賬號進行授權(quán)。
1.《視頻換臉 教程 | 如何使用DeepFake實現(xiàn)視頻換臉》援引自互聯(lián)網(wǎng),旨在傳遞更多網(wǎng)絡(luò)信息知識,僅代表作者本人觀點,與本網(wǎng)站無關(guān),侵刪請聯(lián)系頁腳下方聯(lián)系方式。
2.《視頻換臉 教程 | 如何使用DeepFake實現(xiàn)視頻換臉》僅供讀者參考,本網(wǎng)站未對該內(nèi)容進行證實,對其原創(chuàng)性、真實性、完整性、及時性不作任何保證。
3.文章轉(zhuǎn)載時請保留本站內(nèi)容來源地址,http://f99ss.com/junshi/1379030.html