0


摄像头实时换脸,上网课老师都不认识我了,哈哈

环境与效果

  • python3.9.6
  • pycharm 2021

库环境:

  • dlib
  • opencv-python

视频图片效果如下:

摄像头实时换脸,老师都不认识我了!!

在这里插入图片描述

基本原理

  1. 使用dlib的shape_predictor_68_face_landmarks.dat模型获取一张有正脸的图片(1.png)和摄像头的自己的68个人脸特征点。
  2. 根据人脸特征点获取分别获取人脸掩模
  3. 对第一个图片仿射变换使其脸部对准摄像头图片中的脸部得到新的图片
  4. 对人脸掩模执行相同的操作仿射
  5. 将两个性的得到图取并集(不能让别的地方空了)
  6. 用opencv对两上面操作,对仿射变换后的a图片和摄像头图片进行泊松融合

完整源码

  1. # -*- coding: utf-8-*-
  2. import cv2
  3. import dlib
  4. import numpy as np
  5. detector = dlib.get_frontal_face_detector() # dlib的正向人脸检测器
  6. predictor = dlib.shape_predictor(r'shape_predictor_68_face_landmarks.dat') # dlib的人脸形状检测器
  7. def get_image_size(image):"""
  8. 获取图片大小(高度,宽度)
  9. :param image: image
  10. :return: (高度,宽度)
  11. """
  12. image_size =(image.shape[0], image.shape[1])return image_size
  13. def get_face_landmarks(image, face_detector, shape_predictor):"""
  14. 获取人脸标志,68个特征点
  15. :param image: image
  16. :param face_detector: dlib.get_frontal_face_detector
  17. :param shape_predictor: dlib.shape_predictor
  18. :return: np.array([[],[]]),68个特征点
  19. """
  20. dets =face_detector(image,1)
  21. shape =shape_predictor(image, dets[0])
  22. face_landmarks = np.array([[p.x, p.y]for p in shape.parts()])return face_landmarks
  23. def get_face_mask(image_size, face_landmarks):"""
  24. 获取人脸掩模
  25. :param image_size: 图片大小
  26. :param face_landmarks:68个特征点
  27. :return: image_mask, 掩模图片
  28. """
  29. mask = np.zeros(image_size, dtype=np.uint8)
  30. points = np.concatenate([face_landmarks[0:16], face_landmarks[26:17:-1]])
  31. cv2.fillPoly(img=mask, pts=[points], color=255)return mask
  32. def get_affine_image(image1, image2, face_landmarks1, face_landmarks2):"""
  33. 获取图片1仿射变换后的图片
  34. :param image1: 图片1, 要进行仿射变换的图片
  35. :param image2: 图片2, 只要用来获取图片大小,生成与之大小相同的仿射变换图片
  36. :param face_landmarks1: 图片1的人脸特征点
  37. :param face_landmarks2: 图片2的人脸特征点
  38. :return: 仿射变换后的图片
  39. """
  40. three_points_index =[18,8,25]
  41. M = cv2.getAffineTransform(face_landmarks1[three_points_index].astype(np.float32),
  42. face_landmarks2[three_points_index].astype(np.float32))
  43. dsize =(image2.shape[1], image2.shape[0])
  44. affine_image = cv2.warpAffine(image1, M, dsize)return affine_image.astype(np.uint8)
  45. def get_mask_center_point(image_mask):"""
  46. 获取掩模的中心点坐标
  47. :param image_mask: 掩模图片
  48. :return: 掩模中心
  49. """
  50. image_mask_index = np.argwhere(image_mask >0)
  51. miny, minx = np.min(image_mask_index, axis=0)
  52. maxy, maxx = np.max(image_mask_index, axis=0)
  53. center_point =((maxx + minx)// 2, (maxy + miny) // 2)return center_point
  54. def get_mask_union(mask1, mask2):"""
  55. 获取两个掩模掩盖部分的并集
  56. :param mask1: mask_image, 掩模1:param mask2: mask_image, 掩模2:return: 两个掩模掩盖部分的并集
  57. """
  58. mask = np.min([mask1, mask2], axis=0) # 掩盖部分并集
  59. mask =((cv2.blur(mask,(5,5))==255)*255).astype(np.uint8) # 缩小掩模大小
  60. mask = cv2.blur(mask,(3,3)).astype(np.uint8) # 模糊掩模
  61. return mask
  62. def skin_color_adjustment(im1, im2, mask=None):"""
  63. 肤色调整
  64. :param im1: 图片1:param im2: 图片2:param mask: 人脸 mask. 如果存在,使用人脸部分均值来求肤色变换系数;否则,使用高斯模糊来求肤色变换系数
  65. :return: 根据图片2的颜色调整的图片1"""
  66. if mask is None:
  67. im1_ksize =55
  68. im2_ksize =55
  69. im1_factor = cv2.GaussianBlur(im1,(im1_ksize, im1_ksize),0).astype(np.float)
  70. im2_factor = cv2.GaussianBlur(im2,(im2_ksize, im2_ksize),0).astype(np.float)else:
  71. im1_face_image = cv2.bitwise_and(im1, im1, mask=mask)
  72. im2_face_image = cv2.bitwise_and(im2, im2, mask=mask)
  73. im1_factor = np.mean(im1_face_image, axis=(0,1))
  74. im2_factor = np.mean(im2_face_image, axis=(0,1))
  75. im1 = np.clip((im1.astype(np.float)* im2_factor / np.clip(im1_factor,1e-6, None)),0,255).astype(np.uint8)return im1
  76. def main():
  77. im1 = cv2.imread('1.png') # face_image
  78. im1 = cv2.resize(im1,(600, im1.shape[0]*600// im1.shape[1]))
  79. landmarks1 =get_face_landmarks(im1, detector, predictor) # 68_face_landmarks
  80. if landmarks1 is None:print('{}:检测不到人脸'.format(image_face_path))exit(1)
  81. im1_size =get_image_size(im1) # 脸图大小
  82. im1_mask =get_face_mask(im1_size, landmarks1) # 脸图人脸掩模
  83. cam = cv2.VideoCapture(0)while True:
  84. ret_val, im2 = cam.read() # camera_image
  85. landmarks2 =get_face_landmarks(im2, detector, predictor) # 68_face_landmarks
  86. if landmarks2 is not None:
  87. im2_size =get_image_size(im2) # 摄像头图片大小
  88. im2_mask =get_face_mask(im2_size, landmarks2) # 摄像头图片人脸掩模
  89. affine_im1 =get_affine_image(im1, im2, landmarks1, landmarks2) # im1(脸图)仿射变换后的图片
  90. affine_im1_mask =get_affine_image(im1_mask, im2, landmarks1, landmarks2) # im1(脸图)仿射变换后的图片的人脸掩模
  91. union_mask =get_mask_union(im2_mask, affine_im1_mask) # 掩模合并
  92. affine_im1 =skin_color_adjustment(affine_im1, im2, mask=union_mask) # 肤色调整
  93. point =get_mask_center_point(affine_im1_mask) # im1(脸图)仿射变换后的图片的人脸掩模的中心点
  94. seamless_im = cv2.seamlessClone(affine_im1, im2, mask=union_mask, p=point, flags=cv2.NORMAL_CLONE) # 进行泊松融合
  95. cv2.imshow('seamless_im', seamless_im)else:
  96. cv2.imshow('seamless_im', im2)if cv2.waitKey(1)==27: # 按Esc退出
  97. break
  98. cv2.destroyAllWindows()if __name__ =='__main__':main()

完整项目文件请看b站评论区置顶:换脸了!


本文转载自: https://blog.csdn.net/weixin_46211269/article/details/123471039
版权归原作者 川川菜鸟 所有, 如有侵权,请联系我们删除。

“摄像头实时换脸,上网课老师都不认识我了,哈哈”的评论:

还没有评论