PaddleHub人脸关键点检测实现猫脸人嘴特效
前段时间,下班后闲来无事,参加了百度PaddleHub的AI人像抠图创意赛,凭借着大家的阅读量,获得了一个第三名,得了一个小度音响,真香啊!
PaddleHub创意赛第二期又出来了,这次要做什么呢?
人脸检测主题创意赛,爱搞事的我肯定是少不了搞一波事情的,想想这能玩出什么花样来?
下班路上刷知乎,看见有人用dlib + 猫脸检测器 + 泊松融合实现了抖音猫脸人嘴的特效,瞬间……
程序主要结合PaddleHub的人脸关键点模型截取人嘴位置,opencv猫脸检测器定位猫脸(没找到猫脸关键点检测模型)和opencv泊松融合函数实现图像的融合,共三部分。
一、人脸关键点检测
- PaddleHub关键点检测模型face_landmark_localization,该模型转换自 https://github.com/lsy17096535/face-landmark ,支持同一张图中的多个人脸检测。它可以识别人脸中的68个关键点。
NOTE: 如果您在本地运行该项目示例,需要首先安装PaddleHub。如果您在线运行,可以去底部阅读原文的百度AI Studio fork该项目。之后按照该示例操作即可。
import cv2
import paddlehub as hub
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import math
%matplotlib inline
src_img = cv2.imread('images/youngni2.jpg')
module = hub.Module(name="face_landmark_localization")
result = module.keypoint_detection(images=[src_img])
tmp_img = src_img.copy()
for face in result[0]['data']:
for index, point in enumerate(face):
# print(point)
# cv2.putText(img, str(index), (int(point[0]), int(point[1])), cv2.FONT_HERSHEY_COMPLEX, 3, (0,0,255), -1)
cv2.circle(tmp_img, (int(point[0]), int(point[1])), 2, (0, 0, 255), -1)
res_img_path = 'face_landmark.jpg'
cv2.imwrite(res_img_path, tmp_img)
img = mpimg.imread(res_img_path)
# 展示预测68个关键点结果
plt.figure(figsize=(10,10))
plt.imshow(img)
plt.axis('off')
plt.show()
二、猫脸检测
猫脸检测使用OpenCV自带的猫脸检测器(感觉喵星人真的是要统治世界了😀),主要通过detectMultiScale函数对图像金字塔进行多尺度检测。
import cv2
import numpy as np
# 猫脸检测器
cat_path = "haarcascade_frontalcatface_extended.xml"
facecascade = cv2.CascadeClassifier(cat_path)
cat = cv2.imread('cat3.jpg')
cat_gray = cv2.cvtColor(cat, cv2.COLOR_BGR2GRAY)
cat_face_loc = facecascade.detectMultiScale(cat_gray,scaleFactor = 1.1,minNeighbors=3,minSize=(100,100),flags=cv2.CASCADE_SCALE_IMAGE)
cat_face_loc = np.array(cat_face_loc[0])
# 猫嘴中心位置
center = (int(cat_face_loc[0] + cat_face_loc[2] / 2), int(cat_face_loc[1] + cat_face_loc[3]*0.8))
cv2.rectangle(cat, (cat_face_loc[0], cat_face_loc[1]), (cat_face_loc[0] + cat_face_loc[2], cat_face_loc[1] + cat_face_loc[3]), (0, 255, 0), 2, 8)
cv2.circle(cat, center, 2, (0, 0, 255), 3)
cv2.imwrite('cat_face.jpg', cat)
cv2.imshow('result', cat)
cv2.waitKey(0)
三、泊松融合
泊松融合是2004年论文《Poisson Image Editing》提出的方法,已经集成在OpenCV中,函数名:seamlessClone
泊松融合是将一个源图融合到目标图像中,放置位置根据目标图像中P点为中心的一个前景mask大小范围内。融合过程会改变源图像中颜色以及梯度,实现无缝融合效果,具体算法可以去看论文或者文末参考文献。
话不多说,下面用代码实现将人眼贴到手心上,人眼mask没有很精准,效果一般,胆小勿看😂
import cv2
import numpy as np
hand = cv2.imread('hand.jpg')
eye = cv2.imread('eye.jpg')
h, w, c = eye.shape
mask = np.ones((h, w, c)) * 255
center = (hand.shape[1] // 2 + 50, hand.shape[0] // 2 + 250)
normal_clone = cv2.seamlessClone(eye, hand, mask.astype(eye.dtype), center, cv2.NORMAL_CLONE)
cv2.imwrite('res.jpg', normal_clone)
cv2.imshow('res', normal_clone)
cv2.waitKey(0)
综合上面三部分,我们可以将人嘴定位并截取融合到猫脸嘴巴位置,由于检测视频中猫脸会出现漏检情况,效果不是很好(找一只不爱动的猫片实在太难了),于是我用了一张静态的图片进行替换,另外唱歌视频也来自网络。
import cv2
import numpy as np
import paddlehub as hub
# 人脸关键点检测器
module = hub.Module(name="face_landmark_localization")
# 猫脸检测器
cat_path = "data/model/haarcascade_frontalcatface_extended.xml"
facecascade = cv2.CascadeClassifier(cat_path)
cat = cv2.imread('data/images/cat3.jpg')
cat_gray = cv2.cvtColor(cat, cv2.COLOR_BGR2GRAY)
cat_face_loc = facecascade.detectMultiScale(cat_gray, scaleFactor=1.1, minNeighbors=3, minSize=(100, 100),
flags=cv2.CASCADE_SCALE_IMAGE)
cat_face_loc = np.array(cat_face_loc[0])
# 猫嘴中心位置
center = (int(cat_face_loc[0] + cat_face_loc[2] / 2), int(cat_face_loc[1] + cat_face_loc[3] * 0.8))
def human_mouth_paste_to_cat(human_frame, cat_frame):
result = module.keypoint_detection(images=[human_frame])
landmarks = result[0]['data'][0]
landmarks = np.array(landmarks, dtype=np.int)
mouth_landmark = landmarks[48:, :]
# 扩个边
border = 8
mouth = human_frame[int(np.min(mouth_landmark[:, 1])) - border: int(np.max(mouth_landmark[:, 1]) + border),
int(np.min(mouth_landmark[:, 0])) - border: int(np.max(mouth_landmark[:, 0])) + border, :]
mouth_landmark[:, 0] -= (np.min(mouth_landmark[:, 0]) - border)
mouth_landmark[:, 1] -= (np.min(mouth_landmark[:, 1]) - border)
# 制作用于泊松融合的mask
mask = np.zeros((mouth.shape[0], mouth.shape[1], 3)).astype(np.float32)
for i in range(mouth_landmark.shape[0]): # 先画线
cv2.line(mask, (mouth_landmark[i, 0], mouth_landmark[i, 1]), (
mouth_landmark[(i + 1) % mouth_landmark.shape[0], 0], mouth_landmark[(i + 1) % mouth_landmark.shape[0], 1]),
(255, 255, 255), 10)
mask_tmp = mask.copy()
for i in range(6, mask.shape[0] - 6): # 将线内部的范围都算作mask=255
for j in range(6, mask.shape[1] - 6):
if (np.max(mask_tmp[:i, :j, :]) == 0 or np.max(mask_tmp[i:, :j, :]) == 0 or np.max(
mask_tmp[:i, j:, :]) == 0 or np.max(mask_tmp[i:, j:, :]) == 0):
mask[i, j, :] = 0
else:
mask[i, j, :] = 255
normal_clone = cv2.seamlessClone(mouth, cat_frame, mask.astype(mouth.dtype), center, cv2.NORMAL_CLONE)
return normal_clone
# 合成视频
human_video_cap = cv2.VideoCapture("data/video/human2.mp4")
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
video_writer = cv2.VideoWriter('cat_with_humanmouth2.MP4', fourcc, 25, (1080, 2340))
index = 0
while True:
index += 1
human_ret, human_frame = human_video_cap.read()
if human_ret:
human_frame = cv2.resize(human_frame, dsize=None, fx=2, fy=2)
cat_with_human_mouth = human_mouth_paste_to_cat(human_frame, cat)
video_writer.write(cat_with_human_mouth.astype(np.uint8))
# cv2.imwrite("frame/%d.jpg" % index, cat_with_human_mouth)
else:
break
video_writer.release()
输出效果看文章最前面视频,音频是后来自己加上的。
参考: