百度AIStudio之PaddleHub创意赛:你的名字,你的Face

百度AIStudio之PaddleHub创意赛:你的名字,你的Face

使用《你的名字》动漫中的图片拼出你的人脸。本项目为 PaddleHub:人脸检测主题创意赛。实现自动检测人脸,然后实现人脸拼接。
本次主要使用 PaddleHub 开源的人脸关键点检测模型 face_landmark_localization进行人体关键点识别。
关键点识别参考案例:PaddleHub实战——人像美颜。
之后再以 RGB 颜色均值为标准进行人脸拼图。
NOTE: 本项目在百度AIStudio实现,如果需要在本地运行该项目示例,首先要安装PaddleHub。

其中 face_landmark_localization 使用1.0.2版。
paddlepaddle 环境为1.6.2。
paddlehub 版本为1.6.1。

本项目github链接:luoshutu/YourNameYourFace
百度AIStudio链接:你的名字,你的face

2020-4-28更新 :基于 @意疏 的意见,添加了一些关键性注释,和参数的优化。
其中最大改动为删除了 single_image_area参数以及相关代码。
注:博客和GitHub中的代码在环境配置正确的情况下,只需添加自己的图片链接,就可以在本地直接运行成功。

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

使用PaddleHub 开源的人脸关键点检测模型 face_landmark_localization ,检测人脸关键点,并将关键点以红色点状的方式画在原图上,保存并显示。
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('single_face.jpg')

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

tmp_img = src_img.copy()
for index, point in enumerate(detection_result[0]['data'][0]):
    # 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=(8,8))
plt.imshow(img) 
plt.axis('off') 
plt.show()

百度AIStudio之PaddleHub创意赛:你的名字,你的Face_第1张图片

二、准备图片数据集

解压步骤在百度AIStudio环境下每次重启环境都需要重新解压。若在本地运行该项目,就不需要解压步骤,给出自己的图片路径即可。
'''
# 解压图片
import zipfile

path_zip = "./data/data31722/your_name.zip" # 所需图片的存放路径
z = zipfile.ZipFile(path_zip, "r")   # 读取zip文件
num_Image = len(z.namelist()) - 1    # 总的图片数量

# 数据解压
path_Image = './data/'
#with zipfile.ZipFile(path_zip, 'r') as zin:
#    zin.extractall(path_Image)
'''
path_Images =  './data/your_name/out'  # 图片集路径
num_Image = 638    # 总的图片数量  # 在本地运行时,按自己需要改变

三、组合图片

计算所有数据集中图片的特征向量,以 R/G/B 三个通道分别求平均值构成一个三维的特征向量。

注:在下面代码中,为了尝试更好看的效果,只计算了其中一个通道的特征


# 参数定义每张图片最后最短边所占的像素长度
single_image_len = 10
#7,13

# 压缩图像至固定大小
def pic_compression(src_pic):
    target_high = src_pic.shape[0]
    target_weight = src_pic.shape[1]
    if target_high < target_weight:
        target_weight = np.ceil(target_weight / (target_high / single_image_len))
        target_high = single_image_len
    else:
        target_high = np.ceil(target_high / (target_weight / single_image_len))
        target_weight = single_image_len
    
    return cv2.resize(src_pic,(int(target_weight),int(target_high))), target_high, target_weight

# 计算图像的RGB特征
feature_dim = 3 # 特征维度 R、G、B 共三维
pic_feature = np.zeros([num_Image,feature_dim]) # 特征向量
for indexImg in range(3,num_Image+3-1):  # 图片索引由3开始
    path_pic = path_Images + str(indexImg) +'.png' # 获取每张图片的地址
    pic = cv2.imread(path_pic)
    pic_comed,th,tw = pic_compression(pic.copy()) # 计算得到压缩图像
    # 在此尝试只计算单通道的特征
    # for idx in range(0,feature_dim):
    for idx in range(0,3):          
        pic_feature[indexImg-3,idx] = np.average(pic_comed[:-1,:-1,idx])

print(th,tw)     
取出目标区域
对检测到的关键点坐标值做一个大小判断,取出待重组的图像区域,为一个包含面部的矩形局域。
points = np.mat(detection_result[0]['data'][0])
# 获取待重组的矩形局域的两个坐标值
point_a = np.floor(np.amin(points, axis=0))
point_d = np.ceil(np.amax(points, axis=0))

# 抠出待组合区域图像
left_p = int(point_a[0, 0])
right_p = int(point_d[0, 0])
top_p = int(point_a[0, 1])
bottom_p = int(point_d[0, 1])
# 得到目标图像
temp_Image = src_img.copy()
ROI_Image = temp_Image[top_p:bottom_p, left_p:right_p]
计算脸部各块的RGB特征并贴图
block_feature = np.zeros(feature_dim) # 每一图块的特征
blo_fea_buff = np.zeros(num_Image)  # 缓存每一图块特征到所有图片特征的欧式距离值
for idx_i in range(0, len(ROI_Image)-int(th), int(th)):
    for idx_j in range(0, len(ROI_Image[1])-int(tw), int(tw)):
        # 在此尝试只计算单通道的特征
    	# for idx in range(0,feature_dim):
        for idx in range(2,3):
            block_feature[idx] = np.average(ROI_Image[idx_i:idx_i+int(th),idx_j:idx_j+int(tw),idx])
        for img_idx in range(0,num_Image):
            blo_fea_buff[img_idx] = np.linalg.norm(block_feature - pic_feature[img_idx])
        pic_idx = np.argmin(blo_fea_buff) + 3 # 获取到最小欧式距离的图片索引
        path_pic = path_Images + str(int(pic_idx)) +'.png'
        pic = cv2.imread(path_pic)
        pic_comed,_,_ = pic_compression(pic.copy()) # 计算得到压缩图像
        ROI_Image[idx_i:idx_i+int(th),idx_j:idx_j+int(tw)] = pic_comed
将重组得到的脸按形状贴到原图上
def mask(image, face_landmark):
    """
    image: 人像图片
    face_landmark: 人脸关键点
    """
    image_cp = image.copy()
    hull = cv2.convexHull(face_landmark)

    cv2.fillPoly(image, [hull], (0, 0, 0))
    for idx_i in range(top_p,bottom_p):
        for idx_j in range(left_p,right_p):
            if (image[idx_i, idx_j] == [0,0,0]).all():
                image[idx_i, idx_j] = ROI_Image[idx_i - top_p,idx_j - left_p]
    #cv2.drawContours(image, [hull], -1, ROI_Image, -1)
    #cv2.addWeighted(image, 0.2, image_cp, 0.9, 0, image_cp)

    return image

# 获取人脸关键点数据,和原始图像
face_landmark = np.array(detection_result[0]['data'][0], dtype='int')
result_image = mask(src_img.copy(), face_landmark)

cv2.imwrite('result.jpg', result_image)

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

百度AIStudio之PaddleHub创意赛:你的名字,你的Face_第2张图片

由于贴图数量不足,故显示效果不是很理想,有大量重复照片。有待改进。

2020-4-28:
改变最后图片所占像素大小,以及特征向量的类别。产生一些更有意思的图片。
百度AIStudio之PaddleHub创意赛:你的名字,你的Face_第3张图片
下图为每小图7个像素高,只使用第一个通道的颜色特征来计算欧式距离。
百度AIStudio之PaddleHub创意赛:你的名字,你的Face_第4张图片


百度AIStudio之PaddleHub创意赛:你的名字,你的Face_第5张图片

总结

早在年初的时候,导师就给我们分享了飞桨的B站直播课程。不过起初忙于项目,并未在意。后来项目进展遇到瓶颈,就想继续学习人工智能。本是去学习换脸教程,想去蹭google的资源,可属实不方便。就想着看看国内有什么相似的平台,然后就发现了AIStudio。于是开始学习基础,趁此就报名了这个新手比赛。同时还参加了AIStudio的基础教学课程,七日打卡营。不得不说,这种产学研的方式着实方便了大众了。比赛和课程主要是为了熟悉这个平台和PaddleHub的使用,PaddleHub是飞桨(PaddlePaddle)一个预训练模型管理工具。我们可以自己开发模型并发布,但最重要的是我们可以通过它很方便的使用其它开发者预训练好的模型,这极大的简化了开发者对于AI开发的基本模型的训练以及维护的工作量。使得开发者可以把更多的精力和时间放在AI的应用与创意之上。比如本次参加的人脸识别创意赛就是如此,实现人脸关键点识别只需两三句代码就足够了,大家的重心自是放在了创意上。这自然给了我们极大的便利。

你可能感兴趣的:(人工智能)