睿洛医疗

参考:

利用paddlehub实现视频换脸 - 飞桨AI Studio - 人工智能学习与实训社区

换脸,苍*空变赵*颖,单身福利(懂得都懂),赶紧上车。开个玩笑哦,纯粹为技术学习和交流。

1,准备一张人脸图片与一个视频,使用opencv与moviepy将视频分割成图片;
2,使用PaddleHub的face_landmark_localization模型获取人脸图片im1和视频图片im2的68个人脸特征点;
3,根据上一步获得的特征点得到两张图片的人脸掩模im1_mask和im2_mask;
4,利用68个特征点中的3个特征点,对人脸图片im1进行仿射变换使其脸部对准替换图像的脸部,得到图片affine_im1;
5,对人脸图片的掩模im1_mask也进行相同的仿射变换得到affine_im1_mask;
6,对掩模im2_mask和掩模affine_im1_mask的掩盖部分取并集得到union_mask;
7,利用opencv里的seamlessClone函数对仿射变换后的affine_im1和摄像头图片im2进行泊松融合,掩模为union_mask,得到融合后的图像seamless_im。

待优化

1,目标图像形态训练,单张图变换自由度太低,增加素材合成各形态;

2,空间位置及图层校准,面部遮挡、比例等需优化处理。

原视频

目标人脸图像

安装模块

pip install moviepy==0.2.1.9.7
hub install face_landmark_localization

导入模块

import cv2
import numpy as np
import paddlehub as hub
from moviepy.editor import *
import shutil, os

获取目标图像尺寸

def get_image_size(image):"""获取图片大小(高度,宽度):param image: image:return: (高度,宽度)"""image_size = (image.shape[0], image.shape[1])return image_size

获取人脸标志,68个特征点

def get_face_landmarks(image):"""获取人脸标志,68个特征点:param image: image:param face_detector: dlib.get_frontal_face_detector:param shape_predictor: dlib.shape_predictor:return: np.array([[],[]]), 68个特征点"""dets = face_landmark.keypoint_detection([image])# num_faces = len(dets[0]['data'][0])if len(dets) == 0:print("Sorry, there were no faces found.")return None# shape = shape_predictor(image, dets[0])face_landmarks = np.array([[p[0], p[1]] for p in dets[0]['data'][0]])return face_landmarks

获取人脸掩模

def get_face_mask(image_size, face_landmarks):"""获取人脸掩模:param image_size: 图片大小:param face_landmarks: 68个特征点:return: image_mask, 掩模图片"""mask = np.zeros(image_size, dtype=np.int32)points = np.concatenate([face_landmarks[0:16], face_landmarks[26:17:-1]])points = np.array(points, dtype=np.int32)cv2.fillPoly(img=mask, pts=[points], color=255)# mask = np.zeros(image_size, dtype=np.uint8)# points = cv2.convexHull(face_landmarks)  # 凸包# cv2.fillConvexPoly(mask, points, color=255)return mask.astype(np.uint8)

获取图片1仿射变换后的图片

def get_affine_image(image1, image2, face_landmarks1, face_landmarks2):"""获取图片1仿射变换后的图片:param image1: 图片1, 要进行仿射变换的图片:param image2: 图片2, 只要用来获取图片大小,生成与之大小相同的仿射变换图片:param face_landmarks1: 图片1的人脸特征点:param face_landmarks2: 图片2的人脸特征点:return: 仿射变换后的图片"""three_points_index = [18, 8, 25]M = cv2.getAffineTransform(face_landmarks1[three_points_index].astype(np.float32),face_landmarks2[three_points_index].astype(np.float32))dsize = (image2.shape[1], image2.shape[0])affine_image = cv2.warpAffine(image1, M, dsize)return affine_image.astype(np.uint8)

获取掩模的中心点坐标

def get_mask_center_point(image_mask):"""获取掩模的中心点坐标:param image_mask: 掩模图片:return: 掩模中心"""image_mask_index = np.argwhere(image_mask > 0)miny, minx = np.min(image_mask_index, axis=0)maxy, maxx = np.max(image_mask_index, axis=0)center_point = ((maxx + minx) // 2, (maxy + miny) // 2)return center_point

获取两个掩模掩盖部分的并集

def get_mask_union(mask1, mask2):"""获取两个掩模掩盖部分的并集:param mask1: mask_image, 掩模1:param mask2: mask_image, 掩模2:return: 两个掩模掩盖部分的并集"""mask = np.min([mask1, mask2], axis=0)  # 掩盖部分并集mask = ((cv2.blur(mask, (5, 5)) == 255) * 255).astype(np.uint8)  # 缩小掩模大小mask = cv2.blur(mask, (3, 3)).astype(np.uint8)  # 模糊掩模return mask

肤色调整

def skin_color_adjustment(im1, im2, mask=None):"""肤色调整:param im1: 图片1:param im2: 图片2:param mask: 人脸 mask. 如果存在,使用人脸部分均值来求肤色变换系数;否则,使用高斯模糊来求肤色变换系数:return: 根据图片2的颜色调整的图片1"""if mask is None:im1_ksize = 55im2_ksize = 55im1_factor = cv2.GaussianBlur(im1, (im1_ksize, im1_ksize), 0).astype(np.float)im2_factor = cv2.GaussianBlur(im2, (im2_ksize, im2_ksize), 0).astype(np.float)else:im1_face_image = cv2.bitwise_and(im1, im1, mask=mask)im2_face_image = cv2.bitwise_and(im2, im2, mask=mask)im1_factor = np.mean(im1_face_image, axis=(0, 1))im2_factor = np.mean(im2_face_image, axis=(0, 1))im1 = np.clip((im1.astype(np.float) * im2_factor / np.clip(im1_factor, 1e-6, None)), 0, 255).astype(np.uint8)return im1

面部替换

def change_face(im_name1, im_name2, new_path):""":param im1: 要替换成的人脸图片或文件名:param im_name2: 原始人脸图片文件名:param new_path: 替换后图片目录"""if isinstance(im_name1, str):im1 = cv2.imread(im_name1)  # face_imageelse:im1 = im_name1im1 = cv2.resize(im1, (600, im1.shape[0] * 600 // im1.shape[1]))landmarks1 = get_face_landmarks(im1)  # 68_face_landmarksim1_size = get_image_size(im1)  # 脸图大小im1_mask = get_face_mask(im1_size, landmarks1)  # 脸图人脸掩模im2 = cv2.imread(im_name2)landmarks2 = get_face_landmarks(im2)  # 68_face_landmarksif landmarks2 is not None:im2_size = get_image_size(im2)  # 摄像头图片大小im2_mask = get_face_mask(im2_size, landmarks2)  # 摄像头图片人脸掩模affine_im1 = get_affine_image(im1, im2, landmarks1, landmarks2)  # im1(脸图)仿射变换后的图片affine_im1_mask = get_affine_image(im1_mask, im2, landmarks1, landmarks2)  # im1(脸图)仿射变换后的图片的人脸掩模union_mask = get_mask_union(im2_mask, affine_im1_mask)  # 掩模合并affine_im1 = skin_color_adjustment(affine_im1, im2, mask=union_mask)  # 肤色调整point = get_mask_center_point(affine_im1_mask)  # im1(脸图)仿射变换后的图片的人脸掩模的中心点seamless_im = cv2.seamlessClone(affine_im1, im2, mask=union_mask, p=point, flags=cv2.NORMAL_CLONE)  # 进行泊松融合new_im = os.path.join(new_path, os.path.split(im_name2)[-1])cv2.imwrite(new_im, seamless_im)else:shutil.copy(im_name2, new_path)

分解视频

def cut_video_to_image(video_path, img_path):"""将视频分解为图片输入参数: 分割视频地址,保存图片地址输出参数: 声明了两个全局变量,用来保存分割图片的大小功能:将视频按帧分割成图片"""cap = cv2.VideoCapture(video_path)index = 0global size_y, size_x, fps# Find OpenCV version(major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')if int(major_ver) < 3:fps = cap.get(cv2.cv.CV_CAP_PROP_FPS)else:fps = cap.get(cv2.CAP_PROP_FPS)while (True):ret, frame = cap.read()if ret:cv2.imwrite(img_path + '/%d.jpg' % index, frame)index += 1else:breaksize_x = frame.shape[0]size_y = frame.shape[1]cap.release()print('Video cut finish, all %d frame' % index)print("imge size:x is {1},y is {0}".format(size_x, size_y))

批处理

def get_faces_changed(im_name1, old_path, new_path):"""循环读取图片,并进行换脸:param im_name1: 要替换成的人脸图片文件名:param old_path: 原始图片目录:param new_path: 换脸后输出图片目录"""im1 = cv2.imread(im_name1)im_names = os.listdir(old_path)for im_name2 in os.listdir(old_path):if im_name2.startswith("."):continuechange_face(im1, old_path + "/" + im_name2, new_path)

视频合成

def combine_image_to_video(image_path, video_name):"""视频合成"""fourcc = cv2.VideoWriter_fourcc(*'MP4V')#print(video_name)out = cv2.VideoWriter(video_name, fourcc, fps, (size_y, size_x))files = os.listdir(image_path)print("一共有{}帧图片要合成".format(len(files)))for i in range(len(files)):pic_name = str(i)+".jpg"#print(pic_name)img = cv2.imread(image_path + "/" + pic_name)out.write(img)    # 保存帧out.release()

提取原视频中的音频,并与修改后的视频融合

def combine_audio_video(orig_video, new_video_wot_audio, final_video):"""提取原视频中的音频,并与修改后的视频融合"""audioclip = AudioFileClip(orig_video)clip_finall = VideoFileClip(new_video_wot_audio)videoclip = clip_finall.set_audio(audioclip)videoclip.write_videofile(final_video)

调试

def main(orig_video, face_img, out_video, video_to_img_path="./data/frame", face_changed_img_path="./data/frame_changed"):## 视频切割为图片print("将视频切割为图片", end="\n\n")cut_video_to_image(orig_video, video_to_img_path)## 换脸print("开始换脸", end="\n\n")get_faces_changed(face_img, video_to_img_path, face_changed_img_path)## 将图片合成视频print("将图片合成视频", end="\n\n")combine_image_to_video(face_changed_img_path, "temp.mp4")## 添加音频print("添加音频", end="\n\n")combine_audio_video(orig_video, "temp.mp4", out_video)print("Done!")##
for i in os.listdir("./data/frame"):if i.startswith("."): continueos.remove("./data/frame/" + i)for i in os.listdir("./data/frame_changed"):if i.startswith("."): continueos.remove("./data/frame_changed/" + i)## 人脸关键点模型face_landmark = hub.Module(name="face_landmark_localization")
# 图片人脸替换
#im1 = cv2.imread("luffy.jpg")
#change_face(im1, "bchelor.jpg", "test")## 视频人脸替换
main("./data/video/shejian_zsl.mp4", "./data/zly.jpg", "./data/video/shejian_zly.mp4")
##main("video/popping_Hozin.mp4", "luffy.jpg", "video/popping_luffy.mp4")

PaddlePaddle笔记6-换脸(懂得都懂)相关推荐

  1. 懂得都懂,一行代码解决无法复制粘贴问题

    不多说这个懂得都懂 document.querySelectorAll('[onpaste]').forEach(i=>i.attributes.onpaste.value='')

  2. 懂得都懂(4D语法)

    用来阴阳怪气恶心别人的4D语法(懂得都懂,dddd拼音首字母). 正文 只想说懂得都懂,不懂的我也不多说了,细细品吧,你也别来问我怎么回事,这里面利益牵扯太大了,说了对你我都没有好处,你就当不知道就行 ...

  3. 懂得都懂robotcom

    7-1 懂的都懂 分数 20 全屏浏览题目切换布局 作者 DAI, Longao 单位 杭州百腾教育科技有限公司 众所周知,在互联网上有很多话是不好直接说出来的,不过一些模糊的图片仍然能让网友看懂你在 ...

  4. RoboCom大赛——7-1懂得都懂

    题目: 7-1 懂的都懂 (20 分) 众所周知,在互联网上有很多话是不好直接说出来的,不过一些模糊的图片仍然能让网友看懂你在说什么.然而对这种言论依然一定要出重拳,所以请你实现一个简单的匹配算法. ...

  5. 兄弟们,1024,懂得都懂。

    10.24程序员节 许多人不明白为什么今天是程序员节,这里我给大家科普一下(虽然我朋友圈80%都是程序员):因为计算机的存储单位是满1024才进入下一个单位,就和我们十进制里满10进1一样,所以102 ...

  6. java多线程爬取养眼福利图片(懂得都懂,带注释)

    上次做了个python版的福利爬虫,不过咱主业还是java,所以再发个java版本的,附带每一步的注释,又看不懂的地方欢迎提问首先导入jar包,我这里用的maven,没有maven的可以自己在网上下载 ...

  7. “学习网站”懂得都懂

    在我们前端的工作不可避免的使用到一些网站. 这里给大家分享的[一点] 博客园.知乎.简书.掘金,思否.程序员客栈.这些都是比较大众的,大家都知道的. http://codeif.xinke.org.c ...

  8. 百度网盘的最新插件(懂得都懂)

    下面先给大家介绍一下油猴插件. 这个插件为什么叫油猴? 现在我们经常提到的油猴插件,常指Tampermonkey,但Tampermonkey翻译过来是叫篡改猴,为什么会叫油猴呢?原因是因为另一个插件G ...

  9. 《人人都懂的宏观经济》笔记

    <人人都懂的宏观经济>笔记 前言 大三的时候在网易云课堂接触到理财,于是搜集了不少相关课程看.这个<人人都懂的宏观经济>还不错. 文章目录 <人人都懂的宏观经济>笔 ...

最新文章

  1. 【图论专题】BFS中的双向广搜 和 A-star
  2. 百度二级网页打不开_网页打不开,原因在这里!
  3. MFC_Combo_Box(组合框)的详细用法
  4. mysql数据库 数据类型自动编号选哪个_MySQL表类型、选择合适数据类型、字符集...
  5. linux c配置文件书写格式,读取配置文件源代码[linux c]
  6. 编写与优化 Go 代码(一)
  7. python随机数种子通俗_在种子中生成随机数
  8. 共享计算机后无法访问磁盘,win10电脑共享硬盘无法访问如何解决
  9. Uva12325 Zombie's Treasure Chest [二分区间+模拟退火]
  10. 解决ORA-00257: 归档程序错误。在释放之前仅限于内部连接
  11. SQL 语句 - Select(2): 指定表中的字段
  12. 显卡算力排行2020_AMD正式发布RX6000系列显卡
  13. 客户端与服务站《Socket》
  14. 如何安装Ruby(Windows)
  15. 订阅github release(官方+IFTTT)
  16. _block 的使用 详细介绍
  17. web gooflow流程图实现带公式的流程配置
  18. 计算机攻击方式有哪些,常用的dos攻击方式是什么
  19. 技巧_MFC_标题栏背景
  20. 京东云李道兵:对象存储的新挑战

热门文章

  1. 超越顾客期望,企业必须了解的5件事儿
  2. 用计算机弹不可说,U盘无法弹出,到底是什么程序被占用了?
  3. pd对焦速度_完美捕捉5次爆炸的瞬间,魅蓝Note6的双PD对焦堪称逆天!
  4. 聊聊通用会员卡/通用打折卡
  5. Steve Jobs 2005年在Stanford毕业典礼上的演讲
  6. 【Bug】ubuntu下sougou输入法安装以及中文字词提示栏乱码解决
  7. 2024湖南大学计算机考研信息汇总
  8. 光纤收发器能不能用作量子计算机,【求助】国内的量子计算机除了郭光灿和潘建伟他们还有谁在做啊?...
  9. 拓嘉启远电商:拼多多恶意买家行为主要表现
  10. 如何使用ChatGPT快速构建一个网站模板?