一、增加圣诞帽
hat.png
person.png
效果图.png
- 注意事项
- 需要计算帽子的放置位置,数据超过人形图片会报错,可以增加方法,将帽子缩小
import cv2
import paddlehub as hub
from PIL import Image
from matplotlib import pyplot as plt
def predict_face(predict_path):
"""加载图片标注人脸"""
# 加载人脸预测的算法
model = hub.Module(name="ultra_light_fast_generic_face_detector_1mb_320")
# 人脸源图片地址
input_dict = {'image': [predict_path]}
# 预测人脸:一张图多个人脸,data里面多个数据
# {'data': [{'left': 167, 'right': 255, 'top': 76, 'bottom': 172, 'confidence': 0.9}],
# 'path': '原始图片地址', 'save_path': '预测后保存的地址'}
predict_results = model.face_detection(data=input_dict, visualization=True)
for result in predict_results:
print("人脸预测结果:", result)
if len(predict_results) == 0:
print("没有检测到人脸")
return
predict_result = predict_results[0]
# 显示预测的图片
# predict_img = mpimg.imread(predict_result.get('save_path'))
# # 指定figure的宽和高,单位为英寸
# plt.figure(figsize=(10, 10))
# plt.imshow(predict_img)
# plt.axis('off')
# plt.show()
face_left = int(predict_result['data'][0]['left'])
face_right = int(predict_result['data'][0]['right'])
face_top = int(predict_result['data'][0]['top'])
face_bottom = int(predict_result['data'][0]['bottom'])
face_w = face_right - face_left
face_h = face_bottom - face_top
return dict(left=face_left, right=face_right, top=face_top, bottom=face_bottom, w=face_w,
h=face_h)
def read_image(image_path):
"""读取帽子的图片
:param image_path: 图片的保存路径
:return 图片的宽高
"""
hat = cv2.imread(image_path)
# 用cv2.imread()读到的图像,是BGR三通道图像,可以用cvtColor()函数转换一下
hat_rgb = cv2.cvtColor(hat, cv2.COLOR_BGR2RGB)
plt.imshow(hat_rgb)
# plt.show()
hat_h, hat_w, _ = hat.shape
return dict(h=hat_h, w=hat_w), hat
def change_hat_size(hat_image, person_data, hat_data):
"""调整帽子大小"""
# 用来调整帽子的大小
factor = 1.5
ratio = person_data.get('w', 0) / hat_data.get('w', 1)
resized_hat_h = int(round(hat_data.get('h', 1) * ratio * factor))
resized_hat_w = int(round(hat_data.get('w', 1) * ratio * factor))
print(" 帽子应该调整的大小为:", resized_hat_h, resized_hat_w)
resized_hat = cv2.resize(hat_image, (resized_hat_w, resized_hat_h))
return dict(w=resized_hat_w, h=resized_hat_h), resized_hat
def wipe_white_hat(resized_hat):
"""
对帽子进行二值化,确定掩膜,和取反的
:param resized_hat:
:return:
"""
# 转换为灰度图像
hat2gray = cv2.cvtColor(resized_hat, cv2.COLOR_BGR2GRAY)
# 设置阈值(灰度大于某个数值像素点保留),大于175的置为255,小于175的置为0
# 灰度图片,起始阈值,最大值,处理数据和阈值的关系
ret, mask = cv2.threshold(hat2gray, 250, 255, cv2.THRESH_BINARY)
# 取反操作
mask_inv = cv2.bitwise_not(mask)
# 用来调整阈值,避免将圣诞帽白色部分剔除
plt.imshow(mask_inv)
return mask, mask_inv
def confirm_hat_position(person, resized_hat, face_data, resize_data):
"""确定帽子的位置"""
print("确定帽子放置位置")
face_right = face_data.get('right', 0)
face_left = face_data.get('left', 0)
face_top = face_data.get('top', 0)
hat_width = resize_data.get('w', 0)
hat_height = resize_data.get('h', 0)
print("face_right:", face_right, "face_left:", face_left, "face_top:", face_top, "hat_width:", hat_width,
"hat_height:", hat_height)
dw = int((hat_width - face_data.get('w')) / 2)
# 计算帽子的放置位置
hat_left = int(face_left - dw)
hat_left = hat_left if hat_left > 0 else 0
hat_right = int(hat_left + hat_width)
hat_right = hat_right if hat_right < person.shape[0] else person.shape[0]
hat_bottom = face_top + 10
hat_top = hat_bottom - hat_height
hat_top = hat_top if hat_top > 0 else 0
print("hat_left:", hat_left, "hat_right", hat_right, 'hat_top', hat_top)
roi = person[hat_top:hat_bottom, hat_left:hat_right]
print("person roi shape", roi.shape)
# 删除了ROI中的logo区域
person_bg = cv2.bitwise_and(roi, roi, mask=mask)
# 删除了logo中的空白区域
hat_fg = cv2.bitwise_and(resized_hat, resized_hat, mask=mask_inv)
# print("===合成===")
# # 合成
dst = cv2.add(person_bg, hat_fg)
person[hat_top:hat_bottom, hat_left:hat_right] = dst
person_rgb = cv2.cvtColor(person, cv2.COLOR_BGR2RGB)
img = Image.fromarray(person_rgb)
img.save('temp/mix_person3.png')
plt.imshow(person_rgb)
plt.show()
- 调用
person_pic_path = 'temp/person.png'
hat_pic_path = 'temp/hat.png'
face_digital = predict_face(person_pic_path)
print("人脸预测结果的第一个框: ", face_digital)
hat_digital, hat_image = read_image(hat_pic_path)
print("读取帽子的图片,", hat_digital)
resize_digital, resize_hat_image = change_hat_size(hat_image, face_digital, hat_digital)
print("改变帽子的大小")
_, person_image = read_image(person_pic_path)
mask, mask_inv = wipe_white_hat(resize_hat_image)
confirm_hat_position(person_image, resize_hat_image, face_digital, resize_digital)
二、 增加口罩
-
人脸关键点标注返回的结果:
检测出来的关键点图.png 辅助方法,展现人脸keypoint
def detect_keypoint():
"""
展示人脸检测点
:return:
"""
src_img = cv2.imread('temp/person2.png')
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]):
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()
image.png
- 检测关键点增加口罩
import math
import os
import cv2
import numpy as np
import paddlehub as hub
from PIL import Image
from matplotlib import pyplot as plt
def rotate(image, angle):
w = image.shape[1]
h = image.shape[0]
M = cv2.getRotationMatrix2D((w / 2, h / 2), angle, 1)
# 旋转,修改旋转后对应的
if 180 > angle > 90:
angle = 180 - angle
elif 270 > angle > 180:
angle = angle - 180
elif 360 > angle > 270:
angle = 360 - angle
angle = angle / 180 * np.pi # 转化为弧度制
# 新的宽高
h1 = abs(int(w * np.sin(angle) + h * np.cos(angle)))
w1 = abs(int(w * np.cos(angle) + h * np.sin(angle)))
# 2.3 平移
M[0, 2] += (w1 - w) / 2
M[1, 2] += (h1 - h) / 2
image = cv2.warpAffine(image, M, (w1, h1), borderValue=(255, 255, 255))
return image
def wipe_white_bg(image):
"""
对帽子进行二值化,确定掩膜,和取反的
:param image:
:return:
"""
# 转换为灰度图像
hat2gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 设置阈值(灰度大于某个数值像素点保留),大于175的置为255,小于175的置为0
# 灰度图片,起始阈值,最大值,处理数据和阈值的关系
ret, mask = cv2.threshold(hat2gray, 250, 255, cv2.THRESH_BINARY)
# 取反操作
mask_inv = cv2.bitwise_not(mask)
# 用来调整阈值,避免将圣诞帽白色部分剔除
plt.imshow(mask_inv)
# plt.show()
return mask, mask_inv
def get_face_left_point(landmarks):
all_value = []
for landmark_index, landmark in enumerate(landmarks):
if landmark_index in range(0, 9):
all_value.append(landmark[0])
return int(min(all_value))
def mask(person_path, mask_path=None):
"""
遮罩
:param mask_path: 遮挡的口罩图片路径
:param person_path: 人形图片
:return:
"""
# 读取图片
file_name = person_path.split('/')[-1]
if not os.path.exists(person_path) or not os.path.isfile(person_path):
print("图片不存在")
return
print("====检测图片==", person_path)
if mask_path is None:
mask_path = os.path.abspath('common/libs/transform/mask.png')
person_image = cv2.imread(person_path)
mask_image = cv2.imread(mask_path)
# 加载关键点标注的方法
module = hub.Module(name="face_landmark_localization")
detect_results = module.keypoint_detection(images=[person_image])
if len(detect_results) == 0 or len(detect_results[0]['data']) == 0:
print("没有检测到人脸的关键点不能进行遮罩处理")
return None
# 检测的关键点根据图片
detect_result = detect_results[0]['data']
for detect in detect_result:
person_image = mask_one_person(person_image, mask_image, detect)
person_image = cv2.cvtColor(person_image, cv2.COLOR_BGR2RGB)
img = Image.fromarray(person_image)
img.save(f'temp/source2/detect/mask_{file_name}')
# plt.imshow(img)
# plt.show()
return person_image
def mask_one_person(person_image, mask_image, landmarks):
# 改变口罩的大小
a = landmarks[1]
b = landmarks[15]
c = landmarks[28]
d = landmarks[8]
print(" ==a:", a, ",b:", b, ",c:", c, ",d:", d)
mask_width = int(math.sqrt(((b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2))) + 10
mask_height = int(math.sqrt(((d[0] - c[0]) ** 2 + (d[1] - c[1]) ** 2)))
mask_top_locate = int(min([c[1], a[1], b[1]]))
mask_left_locate = get_face_left_point(landmarks)
print(" ==mask_width:", mask_width, ",mask_height:", mask_height)
mask_image = cv2.resize(mask_image, (mask_width, mask_height), interpolation=cv2.INTER_CUBIC)
try:
degree_a = int(math.degrees(math.acos((b[0] - a[0]) / (mask_width - 10))))
if a[1] < b[1]:
degree_a = 360 - degree_a
except Exception as error:
degree_a = 0
print(" ======Get degree Error====", error.__str__())
if 90 > abs(degree_a) > 0 or 360 > abs(degree_a) > 270:
print(" ==旋转角度:", degree_a)
left_value = 3
if 360 > abs(degree_a) > 270:
left_value = 8
mask_left_locate -= left_value
mask_image = rotate(mask_image, degree_a)
mask_width, mask_height = Image.fromarray(mask_image).size
mask, mask_inv = wipe_white_bg(mask_image)
# 删除了logo中的空白区域
mask_bg = cv2.bitwise_and(mask_image, mask_image, mask=mask_inv)
# 定位
print(" ==mask_left_locate", mask_left_locate, ",mask_top_locate:", mask_top_locate)
roi = person_image[mask_top_locate:int(mask_top_locate + mask_height),
mask_left_locate:int(mask_left_locate + mask_width)]
# 删除了ROI中的logo区域
person_bg = cv2.bitwise_and(roi, roi, mask=mask)
person_image[mask_top_locate:int(mask_top_locate + mask_height),
mask_left_locate:int(mask_left_locate + mask_width)] = cv2.add(
person_bg, mask_bg)
return person_image
- 调用
for pic_name in os.listdir('temp/source'):
mask(f'temp/source/{pic_name}')
-
效果
mask.png
image.png