前言 好想玩點不一樣的,感覺平常的已經不能吸引大家了。想了又想,我今天給大家分享如何給人像添加口罩吧。畢竟最近疫情那麼 嚴重,也只能玩玩這個了,大家千萬別亂跑啊。 效果展示 數據集展示 數據集來源:使用了開源數據集FaceMask_CelebA github地址:https://github.com ...
前言
好想玩點不一樣的,感覺平常的已經不能吸引大家了。想了又想,我今天給大家分享如何給人像添加口罩吧。畢竟最近疫情那麼
嚴重,也只能玩玩這個了,大家千萬別亂跑啊。
效果展示
數據集展示
數據集來源:使用了開源數據集FaceMask_CelebA
github地址:https://github.com/sevenHsu/FaceMask_CelebA.git
部分人臉數據集:
口罩樣本數據集:
為人臉照片添加口罩代碼
這部分有個庫face_recognition需要安裝,如果之前沒有用過的小伙伴可能得費點功夫。
Face Recognition 庫主要封裝了dlib這一 C++ 圖形庫,通過 Python 語言將它封裝為一個非常簡單就可以實現人臉識別的 API
庫,屏蔽了人臉識別的演算法細節,大大降低了人臉識別功能的開發難度。
Python學習交流Q群:906715085### #!/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 = 0for 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}張圖片")
掩膜生成代碼
這部分其實就是對使用的口罩樣本的二值化,因為後續要相關模型會用到
Python學習交流Q群:906715085#### 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是存放待轉換圖片的目錄 destsoure是存在輸出轉換後圖片的目錄 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()
最後
今天又到周末了,祝大家周末愉快,玩夠了記得回來學習鴨!下一章見啦~~~