用 Python 為人臉照片添加口罩

效果展示

數(shù)據(jù)集展示
數(shù)據(jù)集來(lái)源:使用了開源數(shù)據(jù)集 FaceMask_CelebA
github 地址:https://github.com/sevenHsu/FaceMask_CelebA.git
部分人臉數(shù)據(jù)集:

口罩樣本數(shù)據(jù)集:

為人臉照片添加口罩代碼
這部分有個(gè)庫(kù) face_recognition 需要安裝,如果之前沒有用過的小伙伴可能得費(fèi)點(diǎn)功夫。
Face Recognition 庫(kù)主要封裝了 dlib 這一 C++ 圖形庫(kù),通過 Python 語(yǔ)言將它封裝為一個(gè)非常簡(jiǎn)單就可以實(shí)現(xiàn)人臉識(shí)別的 API 庫(kù),屏蔽了人臉識(shí)別的算法細(xì)節(jié),大大降低了人臉識(shí)別功能的開發(fā)難度。

#!/usr/bin/env?python
#?-*-?coding:?utf-8?-*-
#?@Author??:?2014Vee
import?os
import?numpy?as?np
from?PIL?import?Image,?ImageFile
__version__?=?'0.3.0'
IMAGE_DIR?=?os.path.dirname('E:/play/FaceMask_CelebA-master/facemask_image/')
WHITE_IMAGE_PATH?=?os.path.join(IMAGE_DIR,?'front_14.png')
BLUE_IMAGE_PATH?=?os.path.join(IMAGE_DIR,?'front_14.png')
SAVE_PATH?=?os.path.dirname('E:/play/FaceMask_CelebA-master/save/synthesis/')
SAVE_PATH2?=?os.path.dirname('E:/play/FaceMask_CelebA-master/save/masks/')
class?FaceMasker:
????KEY_FACIAL_FEATURES?=?('nose_bridge',?'chin')
????def?__init__(self,?face_path,?mask_path,?white_mask_path,?save_path,?save_path2,?model='hog'):
????????self.face_path?=?face_path
????????self.mask_path?=?mask_path
????????self.save_path?=?save_path
????????self.save_path2?=?save_path2
????????self.white_mask_path?=?white_mask_path
????????self.model?=?model
????????self._face_img:?ImageFile?=?None
????????self._black_face_img?=?None
????????self._mask_img:?ImageFile?=?None
????????self._white_mask_img?=?None
????def?mask(self):
????????import?face_recognition
????????face_image_np?=?face_recognition.load_image_file(self.face_path)
????????face_locations?=?face_recognition.face_locations(face_image_np,?model=self.model)
????????face_landmarks?=?face_recognition.face_landmarks(face_image_np,?face_locations)
????????self._face_img?=?Image.fromarray(face_image_np)
????????self._mask_img?=?Image.open(self.mask_path)
????????self._white_mask_img?=?Image.open(self.white_mask_path)
????????self._black_face_img?=?Image.new('RGB',?self._face_img.size,?0)
????????found_face?=?False
????????for?face_landmark?in?face_landmarks:
????????????#?check?whether?facial?features?meet?requirement
????????????skip?=?False
????????????for?facial_feature?in?self.KEY_FACIAL_FEATURES:
????????????????if?facial_feature?not?in?face_landmark:
????????????????????skip?=?True
????????????????????break
????????????if?skip:
????????????????continue
????????????#?mask?face
????????????found_face?=?True
????????????self._mask_face(face_landmark)
????????if?found_face:
????????????#?save
????????????self._save()
????????else:
????????????print('Found?no?face.')
????def?_mask_face(self,?face_landmark:?dict):
????????nose_bridge?=?face_landmark['nose_bridge']
????????nose_point?=?nose_bridge[len(nose_bridge)?*?1?//?4]
????????nose_v?=?np.array(nose_point)
????????chin?=?face_landmark['chin']
????????chin_len?=?len(chin)
????????chin_bottom_point?=?chin[chin_len?//?2]
????????chin_bottom_v?=?np.array(chin_bottom_point)
????????chin_left_point?=?chin[chin_len?//?8]
????????chin_right_point?=?chin[chin_len?*?7?//?8]
????????#?split?mask?and?resize
????????width?=?self._mask_img.width
????????height?=?self._mask_img.height
????????width_ratio?=?1.2
????????new_height?=?int(np.linalg.norm(nose_v?-?chin_bottom_v))
????????#?left
????????mask_left_img?=?self._mask_img.crop((0,?0,?width?//?2,?height))
????????mask_left_width?=?self.get_distance_from_point_to_line(chin_left_point,?nose_point,?chin_bottom_point)
????????mask_left_width?=?int(mask_left_width?*?width_ratio)
????????mask_left_img?=?mask_left_img.resize((mask_left_width,?new_height))
????????#?right
????????mask_right_img?=?self._mask_img.crop((width?//?2,?0,?width,?height))
????????mask_right_width?=?self.get_distance_from_point_to_line(chin_right_point,?nose_point,?chin_bottom_point)
????????mask_right_width?=?int(mask_right_width?*?width_ratio)
????????mask_right_img?=?mask_right_img.resize((mask_right_width,?new_height))
????????#?merge?mask
????????size?=?(mask_left_img.width?+?mask_right_img.width,?new_height)
????????mask_img?=?Image.new('RGBA',?size)
????????mask_img.paste(mask_left_img,?(0,?0),?mask_left_img)
????????mask_img.paste(mask_right_img,?(mask_left_img.width,?0),?mask_right_img)
????????#?rotate?mask
????????angle?=?np.arctan2(chin_bottom_point[1]?-?nose_point[1],?chin_bottom_point[0]?-?nose_point[0])
????????rotated_mask_img?=?mask_img.rotate(angle,?expand=True)
????????#?calculate?mask?location
????????center_x?=?(nose_point[0]?+?chin_bottom_point[0])?//?2
????????center_y?=?(nose_point[1]?+?chin_bottom_point[1])?//?2
????????offset?=?mask_img.width?//?2?-?mask_left_img.width
????????radian?=?angle?*?np.pi?/?180
????????box_x?=?center_x?+?int(offset?*?np.cos(radian))?-?rotated_mask_img.width?//?2
????????box_y?=?center_y?+?int(offset?*?np.sin(radian))?-?rotated_mask_img.height?//?2
????????#?add?mask
????????self._face_img.paste(mask_img,?(box_x,?box_y),?mask_img)
????????#?split?mask?and?resize
????????width?=?self._white_mask_img.width
????????height?=?self._white_mask_img.height
????????width_ratio?=?1.2
????????new_height?=?int(np.linalg.norm(nose_v?-?chin_bottom_v))
????????#?left
????????mask_left_img?=?self._white_mask_img.crop((0,?0,?width?//?2,?height))
????????mask_left_width?=?self.get_distance_from_point_to_line(chin_left_point,?nose_point,?chin_bottom_point)
????????mask_left_width?=?int(mask_left_width?*?width_ratio)
????????mask_left_img?=?mask_left_img.resize((mask_left_width,?new_height))
????????#?right
????????mask_right_img?=?self._white_mask_img.crop((width?//?2,?0,?width,?height))
????????mask_right_width?=?self.get_distance_from_point_to_line(chin_right_point,?nose_point,?chin_bottom_point)
????????mask_right_width?=?int(mask_right_width?*?width_ratio)
????????mask_right_img?=?mask_right_img.resize((mask_right_width,?new_height))
????????#?merge?mask
????????size?=?(mask_left_img.width?+?mask_right_img.width,?new_height)
????????mask_img?=?Image.new('RGBA',?size)
????????mask_img.paste(mask_left_img,?(0,?0),?mask_left_img)
????????mask_img.paste(mask_right_img,?(mask_left_img.width,?0),?mask_right_img)
????????#?rotate?mask
????????angle?=?np.arctan2(chin_bottom_point[1]?-?nose_point[1],?chin_bottom_point[0]?-?nose_point[0])
????????rotated_mask_img?=?mask_img.rotate(angle,?expand=True)
????????#?calculate?mask?location
????????center_x?=?(nose_point[0]?+?chin_bottom_point[0])?//?2
????????center_y?=?(nose_point[1]?+?chin_bottom_point[1])?//?2
????????offset?=?mask_img.width?//?2?-?mask_left_img.width
????????radian?=?angle?*?np.pi?/?180
????????box_x?=?center_x?+?int(offset?*?np.cos(radian))?-?rotated_mask_img.width?//?2
????????box_y?=?center_y?+?int(offset?*?np.sin(radian))?-?rotated_mask_img.height?//?2
????????#?add?mask
????????self._black_face_img.paste(mask_img,?(box_x,?box_y),?mask_img)
????def?_save(self):
????????path_splits?=?os.path.splitext(self.face_path)
????????#?new_face_path?=?self.save_path?+?'/'?+?os.path.basename(self.face_path)?+?'-with-mask'?+?path_splits[1]
????????#?new_face_path2?=?self.save_path2?+?'/'?+?os.path.basename(self.face_path)?+?'-binary'?+?path_splits[1]
????????new_face_path?=?self.save_path?+?'/'?+?os.path.basename(self.face_path)?+?'-with-mask'?+?path_splits[1]
????????new_face_path2?=?self.save_path2?+?'/'??+?os.path.basename(self.face_path)?+?'-binary'?+?path_splits[1]
????????self._face_img.save(new_face_path)
????????self._black_face_img.save(new_face_path2)
????#?????????print(f'Save?to?{new_face_path}')
????@staticmethod
????def?get_distance_from_point_to_line(point,?line_point1,?line_point2):
????????distance?=?np.abs((line_point2[1]?-?line_point1[1])?*?point[0]?+
??????????????????????????(line_point1[0]?-?line_point2[0])?*?point[1]?+
??????????????????????????(line_point2[0]?-?line_point1[0])?*?line_point1[1]?+
??????????????????????????(line_point1[1]?-?line_point2[1])?*?line_point1[0])?/?\
???????????????????np.sqrt((line_point2[1]?-?line_point1[1])?*?(line_point2[1]?-?line_point1[1])?+
???????????????????????????(line_point1[0]?-?line_point2[0])?*?(line_point1[0]?-?line_point2[0]))
????????return?int(distance)
????#?FaceMasker("/home/aistudio/data/人臉.png",?WHITE_IMAGE_PATH,?True,?'hog').mask()
from?pathlib?import?Path
images?=?Path("E:/play/FaceMask_CelebA-master/bbox_align_celeba").glob("*")
cnt?=?0
for?image?in?images:
????if?cnt?1:
????????cnt?+=?1
????????continue
????FaceMasker(image,?BLUE_IMAGE_PATH,?WHITE_IMAGE_PATH,?SAVE_PATH,?SAVE_PATH2,?'hog').mask()
????cnt?+=?1
????print(f"正在處理第{cnt}張圖片,還有{99?-?cnt}張圖片")
掩膜生成代碼
這部分其實(shí)就是對(duì)使用的口罩樣本的二值化,因?yàn)楹罄m(xù)要相關(guān)模型會(huì)用到

import?os
from?PIL?import?Image
#?源目錄
#?MyPath?=?'E:/play/FaceMask_CelebA-master/facemask_image/'
MyPath?=?'E:/play/FaceMask_CelebA-master/save/masks/'
#?輸出目錄
OutPath?=?'E:/play/FaceMask_CelebA-master/save/Binarization/'
def?processImage(filesoure,?destsoure,?name,?imgtype):
????'''
????filesoure是存放待轉(zhuǎn)換圖片的目錄
????destsoure是存在輸出轉(zhuǎn)換后圖片的目錄
????name是文件名
????imgtype是文件類型
????'''
????imgtype?=?'bmp'?if?imgtype?==?'.bmp'?else?'png'
????#?打開圖片
????im?=?Image.open(filesoure?+?name)
????#?=============================================================================
????#?????#縮放比例
????#?????rate?=max(im.size[0]/640.0?if?im.size[0]?>?60?else?0,?im.size[1]/1136.0?if?im.size[1]?>?1136?else?0)
????#?????if?rate:
????#?????????im.thumbnail((im.size[0]/rate,?im.size[1]/rate))
????#?=============================================================================
????img?=?im.convert("RGBA")
????pixdata?=?img.load()
????#?二值化
????for?y?in?range(img.size[1]):
????????for?x?in?range(img.size[0]):
????????????if?pixdata[x,?y][0]?90:
????????????????pixdata[x,?y]?=?(0,?0,?0,?255)
????for?y?in?range(img.size[1]):
????????for?x?in?range(img.size[0]):
????????????if?pixdata[x,?y][1]?136:
????????????????pixdata[x,?y]?=?(0,?0,?0,?255)
????for?y?in?range(img.size[1]):
????????for?x?in?range(img.size[0]):
????????????if?pixdata[x,?y][2]?>?0:
????????????????pixdata[x,?y]?=?(255,?255,?255,?255)
????img.save(destsoure?+?name,?imgtype)
def?run():
????#?切換到源目錄,遍歷源目錄下所有圖片
????os.chdir(MyPath)
????for?i?in?os.listdir(os.getcwd()):
????????#?檢查后綴
????????postfix?=?os.path.splitext(i)[1]
????????name?=?os.path.splitext(i)[0]
????????name2?=?name.split('.')
????????if?name2[1]?==?'jpg-binary'?or?name2[1]?==?'png-binary':
????????????processImage(MyPath,?OutPath,?i,?postfix)
if?__name__?==?'__main__':
????run()
鏈接:https://blog.csdn.net/qq_52118067/article/details/124334872
(版權(quán)歸原作者所有,侵刪)
評(píng)論
圖片
表情
