其中 face_landmark_localization 使用1.0.2版。
paddlepaddle 环境为1.6.2。
paddlehub 版本为1.6.1。
2020-4-28更新 :基于 @意疏 的意见,添加了一些关键性注释,和参数的优化。
其中最大改动为删除了 single_image_area参数以及相关代码。
注:博客和GitHub中的代码在环境配置正确的情况下,只需添加自己的图片链接,就可以在本地直接运行成功。
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()
'''
# 解压图片
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 # 总的图片数量 # 在本地运行时,按自己需要改变
注:在下面代码中,为了尝试更好看的效果,只计算了其中一个通道的特征
# 参数定义每张图片最后最短边所占的像素长度
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]
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()
由于贴图数量不足,故显示效果不是很理想,有大量重复照片。有待改进。
2020-4-28:
改变最后图片所占像素大小,以及特征向量的类别。产生一些更有意思的图片。
下图为每小图7个像素高,只使用第一个通道的颜色特征来计算欧式距离。
早在年初的时候,导师就给我们分享了飞桨的B站直播课程。不过起初忙于项目,并未在意。后来项目进展遇到瓶颈,就想继续学习人工智能。本是去学习换脸教程,想去蹭google的资源,可属实不方便。就想着看看国内有什么相似的平台,然后就发现了AIStudio。于是开始学习基础,趁此就报名了这个新手比赛。同时还参加了AIStudio的基础教学课程,七日打卡营。不得不说,这种产学研的方式着实方便了大众了。比赛和课程主要是为了熟悉这个平台和PaddleHub的使用,PaddleHub是飞桨(PaddlePaddle)一个预训练模型管理工具。我们可以自己开发模型并发布,但最重要的是我们可以通过它很方便的使用其它开发者预训练好的模型,这极大的简化了开发者对于AI开发的基本模型的训练以及维护的工作量。使得开发者可以把更多的精力和时间放在AI的应用与创意之上。比如本次参加的人脸识别创意赛就是如此,实现人脸关键点识别只需两三句代码就足够了,大家的重心自是放在了创意上。这自然给了我们极大的便利。