【数据增强】遮罩

一、增加圣诞帽

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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容