百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

自己动手,做Snapchat人脸特效

toyiye 2024-06-21 11:55 12 浏览 0 评论

看到美图秀秀,

Faceu 这样的软件,

居然能够把河马酱的圆脸

变大,变小,变漂亮。

实在是觉得好惊奇,

好意外。

太神奇了吧?

怎么做到的?

这一定是魔法吧?

然而,今天在网上发现一篇教程。

发现,嘿嘿。

原来用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(&apos;path/to/your/Eye.png&apos;,-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(&apos;q&apos;):

break

cv2.destroyAllWindows()

AR酱原创,转载务必注明

微信号AR酱(ARchan_TT)

AR酱官网:www.arjiang.com

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码