使用paddlehub給頭像添加聖誕帽(GUI版)

我的前一篇博客:使用PaddleHub給頭像添加聖誕帽已經詳細介紹瞭如何使用paddlehub給頭像添加一個聖誕帽,但之前的版本由於時間倉促還存在一些問題:

  • 聖誕帽超出頭像邊界程序會報錯
  • 無法識別出人臉位置程序會報錯

針對這兩個問題我對代碼進行了改進。代碼的基本實現原理這裏不再詳解,如何實現可以參考之前的文章,完整代碼參見Github。下面開始正題。

1.項目背景

還記得去年聖誕節前夕在朋友圈刷屏的“微信官方,請給我一頂聖誕帽”嗎?事實上無論你怎麼@它都不會給你一頂聖誕帽的,這是聖誕節前夕的一個小小惡作劇。但是誰又不想在聖誕節給自己的頭像換上一個聖誕帽增加一些過節的氣氛呢?當打開羣組打開朋友圈看到滿屏的頭像都是聖誕帽,即使自己正在加班也能身處聖誕節的氛圍中,想想也是一件很開心的事。

現有的添加聖誕帽的方式主要有3種,如下表所示:

方法 優缺點
使用Photoshop軟件添加 優點:自由創作性強,可以選擇聖誕帽的樣式,放置位置和大小。缺點:難度較高,需要具備PS知識
使用手機APP添加 優點:比PS容易掌握。缺點:沒有針對聖誕帽的貼紙,部分軟件任然需要摳圖製作,APP種類繁多不知道該選用哪一個
公衆號、小程序自動添加 多爲營銷號

我只是想給頭像添加一個聖誕帽而已怎麼這麼麻煩T^T,有沒有那種新手小白也可以用的製作聖誕帽頭像的方式,並且也不用這麼麻煩,找貼紙,關注公衆號?當然有,針對以上這些問題,我開發了這套基於Paddlehub的一件添加聖誕帽的軟件,該軟件有以下優點:

  • 帶有GUI界面,對用戶友好;
  • 只需一鍵即可添加,不需要搜索貼紙,關注公衆號;
  • 漫畫頭像也能添加

2.解決方案

本項目採用的是Paddlehub的ultra_light_fast_generic_face_detector_1mb_320人臉檢測模塊,該模塊體積小,檢測速度快,識別精度高。

解決思路:

  1. 獲得人臉的關鍵點位置,獲得聖誕帽摳圖後的圖像;
  2. 將聖誕帽放置在人臉關鍵點位置正上方,合成用到的方法是掩膜;
  3. 如果聖誕帽超過頭像邊界有兩種解決方案:(1)裁剪掉聖誕帽超出的區域;(2)縮小聖誕帽直到不超出區域爲止。聖誕帽形狀特殊,如果裁剪過多可能無法判斷所屬類別,因此選擇方案(2),並且實現也更爲簡單;
  4. 如果無法識別出人物頭像應該終止程序並向用戶提示更換照片;
  5. 如果識別出人物頭像,顯示添加完製作完成的頭像,並向用戶提示新頭像保存位置;
  6. GUI界面用戶可以選擇任意路徑下頭像圖片

流程圖:
在這裏插入圖片描述

3.項目代碼

環境配置:

  • python 3.6
  • paddlehub 1.7.1
  • PIL 7.1.2
    *注:該代碼無法在notebook運行
import paddlehub as hub
import tkinter as tk
from PIL import ImageTk,Image
from tkinter import filedialog
import os
import cv2
 
# 添加聖誕帽
def add_hat(img_path, hat_path):
    # 加載人臉識別模型,取出關鍵點座標
    model = hub.Module(name='ultra_light_fast_generic_face_detector_1mb_320')
    input_dict = {'image': [img_path]}
    results = model.face_detection(data=input_dict, visualization=True)
    for result in results:
        try:
            left, right, top, bottom = int(result['data'][0]['left']), int(result['data'][0]['right']),\
                                int(result['data'][0]['top']), int(result['data'][0]['bottom']) 
            # 對帽子進行縮放
            hat=cv2.imread(hat_path)
            hat_h, hat_w, _ = hat.shape
            face_w = right - left
        
            ratio = face_w/hat_w
            resized_hat_h = int(round(hat_h*ratio))
            resized_hat_w = int(round(hat_w*ratio))
            resized_hat = cv2.resize(hat,(resized_hat_w,resized_hat_h))
            # 判斷ROI是否超出邊界
            while top-resized_hat_h<0:
                resized_hat = cv2.resize(resized_hat,(int(resized_hat_w*0.9),int(resized_hat_h*0.9)))
                resized_hat_h = resized_hat.shape[0]
                resized_hat_w = resized_hat.shape[1]
            # 讀取人物照片
            person = cv2.imread(img_path)
            
            # 製作掩膜
            hat2gray = cv2.cvtColor(resized_hat, cv2.COLOR_BGR2GRAY)  # 轉換爲灰度圖像
            ret, mask = cv2.threshold(hat2gray, 249, 255, cv2.THRESH_BINARY)  # 設置閾值,大於175的置爲255,小於175的置爲0
            mask_inv = cv2.bitwise_not(mask)
            
            # 掩膜
            mid_axis = int((right+left)/2)
            half_wide = int(resized_hat_w/2)
            roi = person[(top-resized_hat_h):top, (mid_axis-half_wide):(mid_axis-half_wide+resized_hat_w)]
            person_bg = cv2.bitwise_and(roi, roi, mask=mask)  #刪除了ROI中的logo區域
            hat_fg = cv2.bitwise_and(resized_hat, resized_hat, mask=mask_inv) #刪除了logo中的空白區域
            dst = cv2.add(person_bg, hat_fg)
            person[(top-resized_hat_h):top, (mid_axis-half_wide):(mid_axis-half_wide+resized_hat_w)] = dst 
            
            # 保存頭像
            profile_path = 'your_profile_photo/'+os.path.basename(img_path)
            cv2.imwrite(profile_path, person)
        except:
            profile_path = None
    return profile_path

def resize_image(img):
    w,h = img.size
    mlength = max(w,h)
    ratio = 500/mlength
    w_new = int(w*ratio)
    h_new = int(h*ratio)
    re_img = img.resize((w_new,h_new))
    return re_img

def gui():
    global image
    img_path = filedialog.askopenfilename() #獲取文件路徑
    hat_path = 'hat.jpg'
    profile_path = add_hat(img_path, hat_path)
    if profile_path is not None:
        img = Image.open(profile_path)
        re_img = resize_image(img)
        image = ImageTk.PhotoImage(re_img)
        canvas.create_image(260,260,anchor='center', image=image)  
        tk.messagebox.showinfo(title='Info', message='New profile photo has generated, please find it at the file: your_profile_photo')
    else:
        tk.messagebox.showwarning(title='Warning', message='Cant recognize your profile photo, please change a new one')

root = tk.Tk()
root.title('給你一頂聖誕帽')
root.geometry('600x600')
canvas=tk.Canvas(root,height=500,width=500)
canvas.pack()
b = tk.Button(root,text='Select your profile photo', command=gui)  #設置按鈕,並給它openpicture命令
b.pack()
root.mainloop()

4.結果演示

  • 運行程序會彈出GUI界面,用戶點擊’Select your profile photo’選擇圖片存放位置
    在這裏插入圖片描述
  • 選擇你想添加的頭像
    在這裏插入圖片描述
  • 顯示添加聖誕帽後的頭像,並提示用戶在’your_frofile_photo’文件夾獲取新生成的頭像,非常抽象的頭像也能識別
    在這裏插入圖片描述
  • 如果無法識別頭像中的臉,則提示用戶換一張頭像重試在這裏插入圖片描述
    項目地址:
    使用paddlehub給你的頭像一鍵添加聖誕帽GUI版
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章