自己动手,做Snapchat人脸特效

您所在的位置:网站首页 snapchat双人特效 自己动手,做Snapchat人脸特效

自己动手,做Snapchat人脸特效

2023-12-14 15:14| 来源: 网络整理| 查看: 265

发现,嘿嘿。

原来用Python, OpenCV, 和Dlib

我们自己也能做一个。

俗话说的好,

自己动手,随便得瑟。

所以我们自己来实现一次。

效果预览

整体思路是这样

首先,我们用OpenCV加载网络摄像头

加载贴图

用 Dlib的脸部检测来定位脸部,

然后使用面部标识来查找眼睛的位置

计算每只眼睛的贴图的大小及位置。

最后,将贴图覆盖到每只眼睛上。

并调整到合适的尺寸

在开始之前,

我们首先,要先加载必要的libraries。

import cv2

import dlib

from scipy.spatial import distance as dist

from scipy.spatial import ConvexHull

除了OpenCV和Dlib,

我们还需要从scipy.spatial package中

加载两种方法,

这些方法可以帮助我们

进行距离和大小计算。

然后,我们还需要设置Dlib Face Detector

和Face Landmark Detector的参数。

接着需要初始化参数,

这些参数将帮助我们

从Dlib返回的68个landmark中

提取个人面部landmark。

PREDICTOR_PATH = "path/to/your/shape_predictor_68_face_landmarks.dat"

FULL_POINTS = list(range(0, 68))

FACE_POINTS = list(range(17, 68))

JAWLINE_POINTS = list(range(0, 17))

RIGHT_EYEBROW_POINTS = list(range(17, 22))

LEFT_EYEBROW_POINTS = list(range(22, 27))

NOSE_POINTS = list(range(27, 36))

RIGHT_EYE_POINTS = list(range(36, 42))

LEFT_EYE_POINTS = list(range(42, 48))

MOUTH_OUTLINE_POINTS = list(range(48, 61))

MOUTH_INNER_POINTS = list(range(61, 68))

detector = dlib.get_frontal_face_detector()

predictor = dlib.shape_predictor(PREDICTOR_PATH)

接着我们就能加载用于叠加的贴图文件了。

就是这对大眼睛。

切记图片一定需要找能支持透明背景的 PNG 文件。不然会AR出你方方的眼睛。

#---------------------------------------------------------

# 加载并预处理眼睛贴图

#---------------------------------------------------------

# 加载贴图

imgEye = cv2.imread('path/to/your/Eye.png',-1)

# 给贴图创建蒙版

orig_mask = imgEye[:,:,3]

# 创建反转的眼睛图像

orig_mask_inv = cv2.bitwise_not(orig_mask)

# 将贴图转换为BGR

# 并保存原图片的尺寸

imgEye = imgEye[:,:,0:3]

origEyeHeight, origEyeWidth = imgEye.shape[:2]

其中cv2.imread参数 '-1' 用于告诉OpenCV读取Alpha 通道(透明通道)和BGR通道。从alpha通道建立一个mask。同时创建一个相反的mask,用来定义属于眼睛外部的像素。最后,将贴图转回BGR格式,并删除alpha通道。

然后我们就可以开始用摄像头获取画面了。捕捉画面后,用算法检测人脸,并检测面部landmark。当获取了landmark我们就可以从中分别提取左右眼的相关landmark 数组。.

# Start capturing the WebCam

video_capture = cv2.VideoCapture(0)

while True:

ret, frame = video_capture.read()

if ret:

gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

rects = detector(gray, 0)

for rect in rects:

x = rect.left()

y = rect.top()

x1 = rect.right()

y1 = rect.bottom()

landmarks = np.matrix([[p.x, p.y] for p in predictor(frame, rect).parts()])

left_eye = landmarks[LEFT_EYE_POINTS]

right_eye = landmarks[RIGHT_EYE_POINTS]

获取了眼睛的landmark后,

下一步该怎么做呢?

我们还需要获取眼睛的大小

以及每个眼睛的中心位置,

不然无法很好得准确叠加。

def eye_size(eye):

eyeWidth = dist.euclidean(eye[0], eye[3])

hull = ConvexHull(eye)

eyeCenter = np.mean(eye[hull.vertices, :], axis=0)

eyeCenter = eyeCenter.astype(int)

return int(eyeWidth), eyeCenter

这里就需要用到欧几里德函数

来计算眼睛的宽度,

并使用ConvexHull函数

来计算中心位置。

将左右眼的值分别传入

leftEyeSize, leftEyeCenter = eye_size(left_eye)

rightEyeSize, rightEyeCenter = eye_size(right_eye)

然后定义一个place eye function。

好了,魔法开始了。

你的二次元眼睛就要长出来了。

def place_eye(frame, eyeCenter, eyeSize):

eyeSize = int(eyeSize * 1.5)

x1 = int(eyeCenter[0,0] - (eyeSize/2))

x2 = int(eyeCenter[0,0] + (eyeSize/2))

y1 = int(eyeCenter[0,1] - (eyeSize/2))

y2 = int(eyeCenter[0,1] + (eyeSize/2))

h, w = frame.shape[:2]

# 检查裁剪

if x1 < 0:

x1 = 0

if y1 < 0:

y1 = 0

if x2 > w:

x2 = w

if y2 > h:

y2 = h

# 重新计算大小以避免裁剪

eyeOverlayWidth = x2 - x1

eyeOverlayHeight = y2 - y1

# 计算贴图的mask

eyeOverlay = cv2.resize(imgEye, (eyeOverlayWidth,eyeOverlayHeight), interpolation = cv2.INTER_AREA)

mask = cv2.resize(orig_mask, (eyeOverlayWidth,eyeOverlayHeight), interpolation = cv2.INTER_AREA)

mask_inv = cv2.resize(orig_mask_inv, (eyeOverlayWidth,eyeOverlayHeight), interpolation = cv2.INTER_AREA)

# 从背景获取背景图像的ROI,等于叠加图像的大小

roi = frame[y1:y2, x1:x2]

# roi_bg仅包含贴图层不覆盖大小的区域中的原始图像。

roi_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)

# roi_fg只包含贴图覆盖区域像素

roi_fg = cv2.bitwise_and(eyeOverlay,eyeOverlay,mask = mask)

# 合并roi_fg 和 roi_bg

dst = cv2.add(roi_bg,roi_fg)

# 将合并的图像放置在原始图像上,保存到dst

frame[y1:y2, x1:x2] = dst

这里我们根据眼睛的大小和位置,

来计算出贴图的大小和位置。

我们还需要检查剪辑。

否则,当您尝试计算

具有图像框之外的像素的mask时,

会收到以下错误消息。

OpenCV Error: Assertion failed ((mtype == CV_8U || mtype == CV_8S) && _mask.same

Size(*psrc1)) in cv::binary_op, file C:\bld\opencv_1492084805480\work\opencv-3.2

.0\modules\core\src\arithm.cpp, line 241

Traceback (most recent call last):

File "WebCam-Overlay.py", line 135, in

place_eye(frame, leftEyeCenter, leftEyeSize)

File "WebCam-Overlay.py", line 51, in place_eye

roi_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)

cv2.error: C:\bld\opencv_1492084805480\work\opencv-3.2.0\modules\core\src\arithm

.cpp:241: error: (-215) (mtype == CV_8U || mtype == CV_8S) && _mask.sameSize(*ps

rc1) in function cv::binary_op

这几个步骤基本就是,计算贴图的大小,从脸部取出在贴图位置大小的区域的像素,并用贴图来取代(不包括透明像素)。最后将替换的像素框放回到面部图像中。当然啦,我们需要一只眼睛一只眼睛得做

place_eye(frame, leftEyeCenter, leftEyeSize)

place_eye(frame, rightEyeCenter, rightEyeSize)

塔哒。。。好了,快来看看你的大眼睛。有没有变水灵

cv2.imshow("Faces with Overlay", frame)

除了给自己加装个漫画版的大眼睛。你也可以加任何其他东西到自己脸上。虽然可能你很有创意,不过还是不要加些奇怪的东西为好。

完整代码

import numpy as np

import cv2

import dlib

from scipy.spatial import distance as dist

from scipy.spatial import ConvexHull

PREDICTOR_PATH = "path/to/your/shape_predictor_68_face_landmarks.dat"

FULL_POINTS = list(range(0, 68))

FACE_POINTS = list(range(17, 68))

JAWLINE_POINTS = list(range(0, 17))

RIGHT_EYEBROW_POINTS = list(range(17, 22))

LEFT_EYEBROW_POINTS = list(range(22, 27))

NOSE_POINTS = list(range(27, 36))

RIGHT_EYE_POINTS = list(range(36, 42))

LEFT_EYE_POINTS = list(range(42, 48))

MOUTH_OUTLINE_POINTS = list(range(48, 61))

MOUTH_INNER_POINTS = list(range(61, 68))

detector = dlib.get_frontal_face_detector()

predictor = dlib.shape_predictor(PREDICTOR_PATH)

def eye_size(eye):

eyeWidth = dist.euclidean(eye[0], eye[3])

hull = ConvexHull(eye)

eyeCenter = np.mean(eye[hull.vertices, :], axis=0)

eyeCenter = eyeCenter.astype(int)

return int(eyeWidth), eyeCenter

def place_eye(frame, eyeCenter, eyeSize):

eyeSize = int(eyeSize * 1.5)

x1 = int(eyeCenter[0,0] - (eyeSize/2))

x2 = int(eyeCenter[0,0] + (eyeSize/2))

y1 = int(eyeCenter[0,1] - (eyeSize/2))

y2 = int(eyeCenter[0,1] + (eyeSize/2))

h, w = frame.shape[:2]

# check for clipping

if x1 < 0:

x1 = 0

if y1 < 0:

y1 = 0

if x2 > w:

x2 = w

if y2 > h:

y2 = h

# re-calculate the size to avoid clipping

eyeOverlayWidth = x2 - x1

eyeOverlayHeight = y2 - y1

# calculate the masks for the overlay

eyeOverlay = cv2.resize(imgEye, (eyeOverlayWidth,eyeOverlayHeight), interpolation = cv2.INTER_AREA)

mask = cv2.resize(orig_mask, (eyeOverlayWidth,eyeOverlayHeight), interpolation = cv2.INTER_AREA)

mask_inv = cv2.resize(orig_mask_inv, (eyeOverlayWidth,eyeOverlayHeight), interpolation = cv2.INTER_AREA)

# take ROI for the verlay from background, equal to size of the overlay image

roi = frame[y1:y2, x1:x2]

# roi_bg contains the original image only where the overlay is not, in the region that is the size of the overlay.

roi_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)

# roi_fg contains the image pixels of the overlay only where the overlay should be

roi_fg = cv2.bitwise_and(eyeOverlay,eyeOverlay,mask = mask)

# join the roi_bg and roi_fg

dst = cv2.add(roi_bg,roi_fg)

# place the joined image, saved to dst back over the original image

frame[y1:y2, x1:x2] = dst

#---------------------------------------------------------

# Load and pre-process the eye-overlay

#---------------------------------------------------------

# Load the image to be used as our overlay

imgEye = cv2.imread('path/to/your/Eye.png',-1)

# Create the mask from the overlay image

orig_mask = imgEye[:,:,3]

# Create the inverted mask for the overlay image

orig_mask_inv = cv2.bitwise_not(orig_mask)

# Convert the overlay image image to BGR

# and save the original image size

imgEye = imgEye[:,:,0:3]

origEyeHeight, origEyeWidth = imgEye.shape[:2]

# Start capturing the WebCam

video_capture = cv2.VideoCapture(0)

while True:

ret, frame = video_capture.read()

if ret:

gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

rects = detector(gray, 0)

for rect in rects:

x = rect.left()

y = rect.top()

x1 = rect.right()

y1 = rect.bottom()

landmarks = np.matrix([[p.x, p.y] for p in predictor(frame, rect).parts()])

left_eye = landmarks[LEFT_EYE_POINTS]

right_eye = landmarks[RIGHT_EYE_POINTS]

# cv2.rectangle(frame, (x, y), (x1, y1), (0, 255, 0), 2)

leftEyeSize, leftEyeCenter = eye_size(left_eye)

rightEyeSize, rightEyeCenter = eye_size(right_eye)

place_eye(frame, leftEyeCenter, leftEyeSize)

place_eye(frame, rightEyeCenter, rightEyeSize)

cv2.imshow("Faces with Overlay", frame)

ch = 0xFF & cv2.waitKey(1)

if ch == ord('q'):

break

cv2.destroyAllWindows()

AR酱原创,转载务必注明

微信号AR酱(ARchan_TT)

AR酱官网:www.arjiang.com返回搜狐,查看更多



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3