如何用 ModelScope 實現 “AI 換臉” 視頻
前言
當下,視頻內容火爆,帶有爭議性或反差大的換臉視頻總能吸引人視線。雖然 AI 換臉在市面上已經流行了許久,相關制作工具或移動應用也是數不勝數。但是多數制作工具多數情況下不是會員就是收費,而且替換模板有限。以下在實戰的角度,用阿里 的圖像人臉融合實現一下 AI 視頻換臉。
流程
提供一段視頻和一張替換的人臉圖片,用 - 將視頻根據幀率拆成圖片,用 提取視頻里的音頻為單獨文件 (mp3)。遍歷目錄下的每一幀的圖片,通過 的人臉融合模型,傳入新的人臉和幀率圖片,得到替換過人臉的幀圖片。最后再通過 - 將替換的人臉圖片組合成新視頻, 添加提取出的音頻文件。
環境
1. 3.7.16
2. 1.4.2
3. - 4.7.0
4. 12.2.0
環境安裝
1. 虛擬環境添加
conda create -n modelscope python=3.7 && conda activate modelscope
2. 安裝 ,使用了國內鏡像源
pip install modelscope --upgrade -i https://pypi.tuna.tsinghua.edu.cn/simple
3. 安裝
pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple
4. 安裝
因為單是圖片用不上,所以安裝方法放在下面視頻換臉里
圖片換臉
1. 素材準備
我這里分別準備了一個畫面里出現正臉,側臉和兩張臉的圖片,然后分別用一張圖片替換,最后運行代碼查看效果。(可能是模型原因,感覺光看圖片的換臉好像相差不大,倒有點像只是開了個美顏,也有可能是倆演員有點像,認真看還有有點不同)。
2. 代碼部分
import cv2
from modelscope.outputs import OutputKeys
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
image_face_fusion = pipeline(Tasks.image_face_fusion,
model='damo/cv_unet-image-face-fusion_damo')
template_path = '181.jpg'
user_path = 'face.jpg'
result = image_face_fusion(dict(template=template_path, user=user_path))
cv2.imwrite('result.png', result[OutputKeys.OUTPUT_IMG])
print('finished!')
視頻換臉
1. 安裝
如果是 可以按我下面的選擇, 是動態版本,不帶的是靜態版本,所有的功能都集合在一起。
2. 環境配置
下載后解壓會生成一下目錄,將 bin 文件放入電腦環境變量中,然后通過 - 查看是否安裝成功。
3. 用法
3.1. 從視頻中抽取音頻 (輸入視頻和輸出音頻的地址可以是相對路徑)
ffmpeg -i videos\11.mp4 -q:a 0 -map a audio\audio.mp3
3.2. 將獨立音頻文件添加到視頻里 (接收輸入視頻,輸入音頻,輸出新視頻)
ffmpeg -i videos/ldh.mp4 -i audio/audio.mp3 -c:v copy -c:a aac -strict experimental videos/new_ldh.mp4
4. 開始編碼
from pathlib import Path
import cv2
import os
def video2mp3_img(video_path, save_path):
def video_split(video_path, save_path):
if not os.path.exists(save_path):
os.makedirs(save_path)
cap = cv2.VideoCapture(video_path)
i = 0
while True:
ret, frame = cap.read()
if ret:
cv2.imwrite(save_path + '/' + str(i) + '.jpg', frame)
i += 1
else:
break
cap.release()
if not os.path.exists(save_path):
os.makedirs(save_path)
# 視頻分割
video_split(video_path, save_path)
# 視頻轉音頻
os.system("ffmpeg -i {} -q:a 0 -map a {}/audio.mp3".format(video_path, save_path))
def face_replace(user_path=""):
from pathlib import Path
import cv2
from modelscope.outputs import OutputKeys
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'
def my_function(img_path):
image_face_fusion = pipeline(Tasks.image_face_fusion, model='damo/cv_unet-image-face-fusion_damo')
template_path = img_path
filename = os.path.splitext(os.path.basename(img_path))[0]
# 替換面部依賴
result = image_face_fusion(dict(template=template_path, user=user_path))
cv2.imwrite(f'video_imgout/{filename}.jpg', result[OutputKeys.OUTPUT_IMG])
threads = []
BASE_PATH = os.path.dirname(__file__)
for dirpath, dirnames, filenames in os.walk(r"D:\3code\3Python\modelscope\mv_face_change-main"):
for filename in filenames:
print(filename)
if filename.endswith('.jpg'):
file_path = Path(os.path.join(dirpath, filename))
print(file_path)
my_function(str(file_path))
def img2mp4(video_path, save_name):
BASE_PATH = "D:\3code\3Python\modelscope\mv_face_change-main"
img = cv2.imread("video_img/0.jpg")
imgInfo = img.shape
size = (imgInfo[1], imgInfo[0])
files = []
for dirpath, dirnames, filenames in os.walk(r"D:\3code\3Python\modelscope\mv_face_change-main\video_imgout"):
for filename in filenames:
fileName = Path(os.path.join(dirpath, filename))
files.append(os.path.join(dirpath, filename))
files = [file.replace('\\', '/') for file in files]
files.sort(key=lambda x: int(x.split('/')[-1].split('.')[0]))
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
videoWrite = cv2.VideoWriter(r"D:\3code\3Python\modelscope\mv_face_change-main\videos\ldh.mp4", fourcc, 25, size) # 寫入對象 1 file name 3: 視頻幀率
for i in files:
print(i)
img = cv2.imread(str(i))
videoWrite.write(img)
# 將video_img中的音頻文件添加到視頻中
os.system("ffmpeg -i {} -i {} -c:v copy -c:a aac -strict experimental {}".format("videos/ldh.mp4", "audio/audio.mp3", "videos/newlest_ldh.mp4"))
if __name__ == '__main__':
BASE = os.path.dirname(__file__)
video_path = os.path.join(BASE, "videos/demo.mp4")
save_path = os.path.join(BASE, "video_img")
# 視頻 ==> imgs
video2mp3_img(video_path, save_path)
# 人臉替換
face_replace(user_path='zsy.jpg')
# imgs ==> 視頻
img2mp4(video_path, save_name='zsy')
5. 報錯匯總
當運行上面代碼,如果出現 file does not any ,那就是分離音頻或追加音頻到視頻的這兩個地方報的錯誤,大部分情況下輸出路徑不正確或命令參數不對。還有一個錯誤我沒有記錄,就是讓視頻壓根沒有聲音,再執行分離操作時也會報錯。這個是我上班時隨便拿的視頻測試 (因為不能戴耳機,剛好視頻就是沒聲音的),所以使勁測使勁報錯,換了視頻就好了,關鍵是錯誤提示也沒說是視頻沒聲音。
6. 效果演示
由于時間原因,沒有用楊過的視頻,就用沒有聲音的視頻做了換臉演示。以后還要對換臉圖片替換做多線程處理。