这是一个关于如何使用OpenCV和DLIB在图像上应用虚拟唇膏的快速教程。同样的原理也可以推广到其他面部特征,比如某人的眼睛、鼻子、下巴……。为了获得上图所示的结果,我们需要执行以下一系列步骤:检测面部标志使用由嘴标志组成的凸多边形创建掩码使用形态学操作增强掩码,并使其模糊,以获得更好的混合效果隔离嘴唇和脸部将颜色变换应用于嘴唇把嘴唇和脸放在一起首先要做的是检测人脸的面部标志。库Dlib提供了一种方便的方法来实现这一点;但是,请记住,被摄对象的面部需要面对摄影机。如果头部姿势不正确,检测结果将不好。
在这个例子中,我们只对嘴唇的点感兴趣。下图显示了Dlib返回的面部标志点的索引。如你所见,我们对48点到60点(嘴唇的外部“轮廓”)感兴趣。
利用这些点,我们可以制作一个掩码,让我们在不影响面部整体外观的情况下处理嘴唇的颜色。但是,等一下。在开始操作这些颜色之前,我们需要改进掩码。在这个例子中,一个带有4x4矩形内核的形态学闭合操作就足够了。注意下面的图片,这个步骤填补了由cv2生成的原始多边形右上角的一个空白。右:使用唇部创建的凸多边形/左:关闭操作后的多边形
为了获得自然的效果,我们还需要模糊掩码。模糊掩码将产生更好的混合效果。我们将cv2.GaussianBlur应用于掩码。最后,我们反转掩码(我们需要两个,一个用于嘴唇,一个用于面部)。右:模糊掩码/左:模糊反向掩码
我们将应用这些掩码,方法是将它们从0–255(uint8)转换为0–1(float)范围,然后将它们与图像相乘。右下方的图像是原始图像乘以反转掩码。左侧的图像是原始图像上的颜色变换乘以掩码的结果。颜色变换由cv2.applyColorMap(im, cv2.COLORMAP_INFERNO)给出。右:原始图像和反向模糊掩码的位与运算/左:颜色变换图像和模糊掩码的位与运算
现在,剩下要做的就是将这两个图像相加。右:原始图像/左:结果图像
这是代码。
import cv2
import dlib
import faceBlendCommon as face
import numpy as np
# 加载图像
im = cv2.imread("cv2/girl-no-makeup.jpg")
# 检测人脸关键点
PREDICTOR_PATH = r"C:Userselipe.cunhaDocumentsenvcv2week1-pytondatamodelsshape_predictor_68_face_landmarks.dat"
faceDetector = dlib.get_frontal_face_detector()
landmarkDetector = dlib.shape_predictor(PREDICTOR_PATH)
landmarks = face.getLandmarks(faceDetector, landmarkDetector, im)
# 为嘴唇制作一个掩码
lipsPoints = landmarks[48:60]
mask = np.zeros((im.shape[0], im.shape[1], 3), dtype=np.float32)
cv2.fillConvexPoly(mask, np.int32(lipsPoints), (1.0, 1.0, 1.0))
mask = 255*np.uint8(mask)
# 应用闭合操作改善掩码
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,40))
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, 1)
# 模糊掩码以获得自然效果
mask = cv2.GaussianBlur(mask,(15,15),cv2.BORDER_DEFAULT)
# 计算逆掩码
inverseMask = cv2.bitwise_not(mask)
# 将掩码转换为浮点以执行混合
mask = mask.astype(float)/255
inverseMask = inverseMask.astype(float)/255
# 为嘴唇应用颜色贴图
lips = cv2.applyColorMap(im, cv2.COLORMAP_INFERNO)
# 将嘴唇和人脸转换为0-1范围
lips = lips.astype(float)/255
ladyFace = im.astype(float)/255
# 用掩码调整嘴唇和脸
justLips = cv2.multiply(mask, lips)
justFace = cv2.multiply(inverseMask, ladyFace)
# 加上脸和嘴唇
result = justFace + justLips
# 显示结果
cv2.imshow("", result)
cv2.waitKey(0)