基于百度PaddleHub实现人像美颜V1.0

AI美颜核心技术之一就是人脸关键点检测。PaddleHub已经开源了人脸关键点检测模型 face_landmark_localization。

人脸关键点检测是人脸识别和分析领域中的关键一步,它是诸如自动人脸识别、表情分析、三维人脸重建及三维动画等其它人脸相关问题的前提和突破口。

该模型支持同一张图中的多个人脸检测。它可以识别人脸中的68个关键点。
在这里插入图片描述

一、加载图片,检测关键点

import cv2
import paddlehub as hub
import matplotlib.pyplot as plt 
import matplotlib.image as mpimg
import numpy as np
import math

src_img = cv2.imread('./2.jpg')

module = hub.Module(name="face_landmark_localization")
result = module.keypoint_detection(images=[src_img])

tmp_img = src_img.copy()
for index, point in enumerate(result[0]['data'][0]):
	# 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()

在这里插入图片描述

二、实现美颜方法

  1. 瘦脸
    首先介绍如何利用识别到的68个关键点完成瘦脸功能。 利用其中3号点到5号点距离作为瘦左脸距离,13号点到15号点距离作为瘦右脸距离。同时利用局部平移算法完成瘦脸。
def thin_face(image, face_landmark):
    """
    实现自动人像瘦脸
    image: 人像图片
    face_landmark: 人脸关键点
    """
    end_point = face_landmark[30]

    # 瘦左脸,3号点到5号点的距离作为瘦脸距离
    dist_left = np.linalg.norm(face_landmark[3] - face_landmark[5])
    local_traslation_warp(image, face_landmark[3], end_point, dist_left)

    # 瘦右脸,13号点到15号点的距离作为瘦脸距离
    dist_right = np.linalg.norm(face_landmark[13] - face_landmark[15])
    image = local_traslation_warp(image, face_landmark[13], end_point, dist_right)
    return image
def local_traslation_warp(image, start_point, end_point, radius):
    """
    局部平移算法
    """
    radius_square = math.pow(radius, 2)
    image_cp = image.copy()

    dist_se = math.pow(np.linalg.norm(end_point - start_point), 2)
    height, width, channel = image.shape
    for i in range(width):
        for j in range(height):
            # 计算该点是否在形变圆的范围之内
            # 优化,第一步,直接判断是会在(start_point[0], start_point[1])的矩阵框中
            if math.fabs(i - start_point[0]) > radius and math.fabs(j - start_point[1]) > radius:
                continue

            distance = (i - start_point[0]) * (i - start_point[0]) + (j - start_point[1]) * (j - start_point[1])

            if (distance < radius_square):
                # 计算出(i,j)坐标的原坐标
                # 计算公式中右边平方号里的部分
                ratio = (radius_square - distance) / (radius_square - distance + dist_se)
                ratio = ratio * ratio

                # 映射原位置
                new_x = i - ratio * (end_point[0] - start_point[0])
                new_y = j - ratio * (end_point[1] - start_point[1])

                new_x = new_x if new_x >=0 else 0
                new_x = new_x if new_x < height-1 else height-2
                new_y = new_y if new_y >= 0 else 0
                new_y = new_y if new_y < width-1 else width-2

                # 根据双线性插值法得到new_x, new_y的值
                image_cp[j, i] = bilinear_insert(image, new_x, new_y)
                
    return image_cp

def bilinear_insert(image, new_x, new_y):
    """
    双线性插值法
    """
    w, h, c = image.shape
    if c == 3:
        x1 = int(new_x)
        x2 = x1 + 1
        y1 = int(new_y)
        y2 = y1 + 1

        part1 = image[y1, x1].astype(np.float) * (float(x2) - new_x) * (float(y2) - new_y)
        part2 = image[y1, x2].astype(np.float) * (new_x - float(x1)) * (float(y2) - new_y)
        part3 = image[y2, x1].astype(np.float) * (float(x2) - new_x) * (new_y - float(y1))
        part4 = image[y2, x2].astype(np.float) * (new_x - float(x1)) * (new_y - float(y1))

        insertValue = part1 + part2 + part3 + part4

        return insertValue.astype(np.int8)
face_landmark = np.array(result[0]['data'][0], dtype='int')

src_img = thin_face(src_img, face_landmark)

res_img_path = 'res.jpg'
cv2.imwrite(res_img_path, src_img)

img = mpimg.imread(res_img_path) 
# 展示瘦脸图片
plt.figure(figsize=(10,10))
plt.imshow(img) 
plt.axis('off') 
plt.show()

在这里插入图片描述
2. 大眼
完成瘦脸之后,我们还可以对人像中的眼睛进行放大。在识别到的左右眼中的一个位置,对其进行缩放(图像局部缩放),实现大眼。

def enlarge_eyes(image, face_landmark, radius=15, strength=10):
    """
    放大眼睛
    image: 人像图片
    face_landmark: 人脸关键点
    radius: 眼睛放大范围半径
    strength:眼睛放大程度
    """
    # 以左眼最低点和最高点之间的中点为圆心
    left_eye_top = face_landmark[37]
    left_eye_bottom = face_landmark[41]
    left_eye_center = (left_eye_top + left_eye_bottom)/2
    # 以右眼最低点和最高点之间的中点为圆心
    right_eye_top = face_landmark[43]
    right_eye_bottom = face_landmark[47]
    right_eye_center = (right_eye_top + right_eye_bottom)/2

    # 放大双眼
    local_zoom_warp(image, left_eye_center, radius=radius, strength=strength)
    local_zoom_warp(image, right_eye_center, radius=radius, strength=strength)
def local_zoom_warp(image, point, radius, strength):
    """
    图像局部缩放算法
    """
    height = image.shape[0]
    width = image.shape[1]      
    left =int(point[0] - radius) if point[0] - radius >= 0 else 0
    top = int(point[1] - radius) if point[1] - radius >= 0 else 0
    right = int(point[0] + radius) if point[0] + radius < width else width-1
    bottom = int(point[1] + radius) if point[1] + radius < height  else height-1

    radius_square = math.pow(radius, 2)
    for y in range(top, bottom):
        offset_y = y - point[1]
        for x in range(left, right):
            offset_x = x - point[0]
            dist_xy = offset_x * offset_x + offset_y * offset_y

            if dist_xy <= radius_square:
                scale = 1 - dist_xy / radius_square
                scale = 1 - strength / 100 * scale
                new_x = offset_x * scale + point[0]
                new_y = offset_y * scale + point[1]
                new_x = new_x if new_x >=0 else 0
                new_x = new_x if new_x < height-1 else height-2
                new_y = new_y if new_y >= 0 else 0
                new_y = new_y if new_y < width-1 else width-2

                image[y, x] = bilinear_insert(image, new_x, new_y)
# 在瘦脸的基础上,继续放大双眼
enlarge_eyes(src_img, face_landmark, radius=13, strength=13)

cv2.imwrite(res_img_path, src_img)

img = mpimg.imread(res_img_path) 
plt.figure(figsize=(10,10))
plt.imshow(img) 
plt.axis('off') 
plt.show()

在这里插入图片描述
3. 红唇
目前已经叠加了瘦脸、大眼的美颜功能,我们还可以给人像增添气色,给人像画上红唇。我们只需将识别到的唇部位置给涂上红色即可达到相应的目的。

def rouge(image, face_landmark, ruby=True):
    """
    自动涂口红
    image: 人像图片
    face_landmark: 人脸关键点
    ruby:是否需要深色口红
    """
    image_cp = image.copy()

    if ruby:
        rouge_color = (0,0,255)
    else:
    	rouge_color = (0,0,200)

    points=face_landmark[48:68]
    
    hull = cv2.convexHull(points)
    cv2.drawContours(image, [hull], -1, rouge_color, -1)
    cv2.addWeighted(image, 0.2, image_cp, 1-0.1, 0, image_cp)
    return image_cp
# 继续叠加红唇
src_img = rouge(src_img, face_landmark)

cv2.imwrite(res_img_path, src_img)

img = mpimg.imread(res_img_path) 
plt.figure(figsize=(10,10))
plt.imshow(img) 
plt.axis('off') 
plt.show()

在这里插入图片描述
4. 美白
人像涂上了口红吗,显得气色更佳了些。同时,很多人还会追求白皙的皮肤。最后我们还可以加上美肤功能。

def whitening(img, face_landmark):
	"""
	美白
	"""
	# 简单估计额头所在区域
	# 根据0号、16号点画出额头(以0号、16号点所在线段为直径的半圆)
	radius=(np.linalg.norm(face_landmark[0] - face_landmark[16]) / 2).astype('int32')
	center_abs=tuple(((face_landmark[0] + face_landmark[16]) / 2).astype('int32'))
	angle=np.degrees(np.arctan((lambda l:l[1]/l[0])(face_landmark[16]-face_landmark[0]))).astype('int32')
	face = np.zeros_like(img)
	cv2.ellipse(face,center_abs,(radius,radius),angle,180,360,(255,255,255),2)

	points=face_landmark[0:17]
	hull = cv2.convexHull(points)
	cv2.polylines(face, [hull], True, (255,255,255), 2)

	index = face >0
	face[index] = img[index]
	dst = np.zeros_like(face)
	# v1:磨皮程度
	v1 = 3
	# v2: 细节程度
	v2 = 2

	tmp1 = cv2.bilateralFilter(face, v1 * 5, v1 * 12.5, v1 * 12.5)
	tmp1 = cv2.subtract(tmp1,face)
	tmp1 = cv2.add(tmp1,(10,10,10,128))
	tmp1 = cv2.GaussianBlur(tmp1,(2*v2 - 1,2*v2-1),0)
	tmp1 = cv2.add(img,tmp1)
	dst = cv2.addWeighted(img, 0.1, tmp1, 0.9, 0.0)
	dst = cv2.add(dst,(10, 10, 10,255))

	index = dst>0
	img[index] = dst[index]

	return img
# 美白
src_img = whitening(src_img, face_landmark)

cv2.imwrite(res_img_path, src_img)

img = mpimg.imread(res_img_path) 
plt.figure(figsize=(10,10))
plt.imshow(img) 
plt.axis('off') 
plt.show()

在这里插入图片描述
原图:
在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
基于人像分割的局部通常指的是对人脸区域进行处理,同时保留原始图像的背景。实现方式可以使用人像分割技术来分离人脸和背景,然后对人脸区域进行处理。 以下是一种基于GrabCut算法的实现方法: 1. 加载图像并将其转换为灰度图像。 ```python import cv2 img = cv2.imread('your_image.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ``` 2. 使用OpenCV的Haar级联分类器来检测人脸。 ```python face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') faces = face_cascade.detectMultiScale(gray, 1.3, 5) ``` 3. 对于每个检测到的人脸区域,使用GrabCut算法进行人像分割。 ```python # 创建一个掩码(mask),将人脸区域初始化为前景(前景=1,背景=0) mask = np.zeros(img.shape[:2], np.uint8) for (x,y,w,h) in faces: rect = (x, y, w, h) mask, bgdModel, fgdModel = cv2.grabCut(img, mask, rect, None, None, 5, cv2.GC_INIT_WITH_RECT) # 将掩码中标记为前景的区域提取出来作为人像 person = cv2.bitwise_and(img, img, mask=(mask==cv2.GC_FGD).astype(np.uint8)) ``` 4. 对人像区域应用效果。 ```python # 对人像区域应用效果 person = cv2.GaussianBlur(person, (15, 15), 0) # 高斯模糊 person = cv2.addWeighted(person, 1.5, person, 0, 10) # 图像增强 ``` 5. 将化后的人像区域和原始图像的背景合并。 ```python # 将化后的人像区域和原始图像的背景合并 background = cv2.bitwise_and(img, img, mask=(mask==cv2.GC_BGD).astype(np.uint8)) result = cv2.bitwise_or(person, background) ``` 最后,使用`cv2.imshow`函数将处理后的图像显示出来。 ```python cv2.imshow('image', result) cv2.waitKey(0) cv2.destroyAllWindows() ``` 需要注意的是,GrabCut算法需要人工指定一个矩形框来包含人脸区域,如果矩形框不准确,会影响分割的效果。另外,GrabCut算法的计算量比较大,处理较大的图像可能会比较耗时。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值