一、前言
人工智能(AI)技術的快速發展為各個領域帶來了革命性的變化,其中之一就是人臉識別與圖像處理技術。在這之中,AI換臉技術尤其引人注目。這種技術不僅在娛樂行業中得到廣泛應用,如電影制作、視頻特效等,還在社交媒體上掀起了一股風潮。AI換臉技術不僅可以實現實時的面部替換,還能夠在圖像和視頻中生成高度逼真的換臉效果。
AI換臉技術的核心在于多種機器學習和深度學習模型的結合。它通常涉及幾個關鍵步驟:人臉檢測、人臉特征點檢測、人臉對齊、換臉處理以及圖像增強。每個步驟都依賴于不同的深度學習模型,以確保最終的換臉效果逼真且自然。
本項目實現了一個完整的AI換臉系統,集成了多個深度學習模型,包括YOLO人臉檢測模型、68關鍵點檢測模型、ArcFace人臉識別模型、InSwapper換臉模型以及GFPGAN人臉增強模型。通過這些模型的協同工作,我們能夠從源圖像中提取人臉特征,并將其無縫地替換到目標圖像或視頻中,生成自然的換臉效果。
接下來,我們將詳細介紹這個AI換臉系統的實現細節和工作原理。通過這些介紹,讀者可以深入了解AI換臉技術的實際應用和技術實現過程。
二、系統架構與工作流程
2.1 系統整體架構
2.1 主要模塊與功能介紹(附代碼)
該項目主要由5個主要模塊組成,他們分別是人臉檢測,人臉關鍵點檢測,人臉對齊,換臉處理和圖像增強。
2.1.1 人臉檢測
首先我們需要檢測源圖像和目標圖像中的人臉相關數據,獲取圖像中包含的人臉坐標,即由左上和右下坐標決定的矩陣框,對應的面部關鍵點和置信度分數。在該部分中所采用的檢測模型是YOLOv8,它是最新一代的 YOLO(You Only Look Once)系列模型之一,專為實時目標檢測任務而設計。它在精度和速度方面相比之前的模型均有顯著提升,非常適用于需要快速響應的應用場景,如視頻監控、自動駕駛和增強現實等。所以在實時換臉項目中,YOLOv8顯然非常適合用于人臉檢測。以下是具體步驟:
-
模型初始化
首先設定模型的參數置信度閾值和iou閾值,之后加載YOLOv8的ONNX 模型,并設置推理會話的選項。需要在初始化中獲取模型的輸入名稱和形狀,以便后續進行圖像預處理。def __init__(self, modelpath, conf_thres=0.5, iou_thresh=0.4):self.conf_threshold = conf_thresself.iou_threshold = iou_threshsession_option = onnxruntime.SessionOptions()session_option.log_severity_level = 3self.session = onnxruntime.InferenceSession(modelpath, sess_options=session_option)model_inputs = self.session.get_inputs()self.input_names = [model_inputs[i].name for i in range(len(model_inputs))]self.input_shape = model_inputs[0].shapeself.input_height = int(self.input_shape[2])self.input_width = int(self.input_shape[3])
-
圖像預處理
在使用YOLOv8進行推理之前需要先調整輸入圖像大小并進行邊界填充,還需要將圖像像素值歸一化到 [-1, 1] 的范圍,并調整通道順序,使其符合模型的輸入要求。def preprocess(self, srcimg):height, width = srcimg.shape[:2]temp_image = srcimg.copy()if height > self.input_height or width > self.input_width:scale = min(self.input_height / height, self.input_width / width)new_width = int(width * scale)new_height = int(height * scale)temp_image = cv2.resize(srcimg, (new_width, new_height))self.ratio_height = height / temp_image.shape[0]self.ratio_width = width / temp_image.shape[1]input_img = cv2.copyMakeBorder(temp_image, 0, self.input_height - temp_image.shape[0], 0, self.input_width - temp_image.shape[1], cv2.BORDER_CONSTANT, value=0)input_img = (input_img.astype(np.float32) - 127.5) / 128.0input_img = input_img.transpose(2, 0, 1)input_img = input_img[np.newaxis, :, :, :]return input_img
-
進行推理
在推理過程中,首先要調用 preprocess 方法對輸入圖像進行預處理獲得符合模型要求的輸入。再使用 ONNX Runtime 進行推理,得到檢測結果。之后調用 postprocess 方法(下面提到)處理輸出結果。def detect(self, srcimg):input_tensor = self.preprocess(srcimg)outputs = self.session.run(None, {self.input_names[0]: input_tensor})[0]boxes, kpts, scores = self.postprocess(outputs)return boxes, kpts, scores
-
后處理
在執行推理后調用后處理函數解析模型輸出,獲取邊界框、關鍵點和得分。同時使用非極大值抑制(NMS)去除冗余的檢測框。再根據縮放比例調整邊界框和關鍵點的坐標。def postprocess(self, outputs):bounding_box_list, face_landmark5_list, score_list = [], [], []outputs = np.squeeze(outputs, axis=0).Tbounding_box_raw, score_raw, face_landmark_5_raw = np.split(outputs, [4, 5], axis=1)keep_indices = np.where(score_raw > self.conf_threshold)[0]if keep_indices.any():bounding_box_raw, face_landmark_5_raw, score_raw = bounding_box_raw[keep_indices], face_landmark_5_raw[keep_indices], score_raw[keep_indices]bboxes_wh = bounding_box_raw.copy()bboxes_wh[:, :2] = bounding_box_raw[:, :2] - 0.5 * bounding_box_raw[:, 2:]bboxes_wh *= np.array([[self.ratio_width, self.ratio_height, self.ratio_width, self.ratio_height]])face_landmark_5_raw *= np.tile(np.array([self.ratio_width, self.ratio_height, 1]), 5).reshape((1, 15))score_raw = score_raw.flatten()indices = cv2.dnn.NMSBoxes(bboxes_wh.tolist(), score_raw.tolist(), self.conf_threshold, self.iou_threshold)if isinstance(indices, np.ndarray):indices = indices.flatten()if len(indices) > 0:bounding_box_list = list(map(lambda x: np.array([x[0], x[1], x[0] + x[2], x[1] + x[3]], dtype=np.float64), bboxes_wh[indices]))score_list = list(score_raw[indices])face_landmark5_list = list(face_landmark_5_raw[indices])return bounding_box_list, face_landmark5_list, score_list
-
繪制檢測結果
最后將得到的邊界框,關鍵點以及對應的置信度繪制在輸入圖像上,這里為了方便換臉后前后對比,把輸入圖像復制了一份,在該副本上進行繪制。得到的結果如下:
2.1.2人臉關鍵點檢測
這里我們來介紹一個可以識別人臉圖像關鍵點的模型,2DFAN4 模型。該模型可以檢測人臉上的68個關鍵點,這些關鍵點包括眼睛、眉毛、鼻子、嘴巴和面部輪廓等。
- 模型初始化:
和上一步類似,初始化 ONNX 模型會話,設置模型路徑并獲取模型輸入信息。 - 圖像預處理
計算縮放比例和平移量,使邊界框居中到 256x256 的圖像中。使用 warp_face_by_translation 方法進行仿射變換,返回裁剪后的圖像和仿射矩陣。轉置圖像通道順序,并進行歸一化處理。def preprocess(self, srcimg, bounding_box):'''bounding_box里的數據格式是[xmin. ymin, xmax, ymax]'''scale = 195 / np.subtract(bounding_box[2:], bounding_box[:2]).max()natranslation = (256 - np.add(bounding_box[2:], bounding_box[:2]) * scale) * 0.5crop_img, affine_matrix = warp_face_by_translation(srcimg, translation, scale, (256, 256))crop_img = crop_img.transpose(2, 0, 1).astype(np.float32) / 255.0crop_img = crop_img[np.newaxis, :, :, :]return crop_img, affine_matrix
- 人臉關鍵點檢測
調用 preprocess 方法,得到輸入張量和仿射矩陣,再使用 ONNX 模型進行推理,得到人臉的 68 個關鍵點。對關鍵點進行歸一化處理,并應用逆仿射變換,將關鍵點坐標轉換回原圖像坐標系中。將 68 個關鍵點轉換為 5 個關鍵點(這里其實和上面的YOLOv8實現的功能類似)。def detect(self, srcimg, bounding_box):'''如果直接crop+resize,最后返回的人臉關鍵點有偏差'''input_tensor, affine_matrix = self.preprocess(srcimg, bounding_box)face_landmark_68 = self.session.run(None, {self.input_names[0]: input_tensor})[0]face_landmark_68 = face_landmark_68[:, :, :2][0] / 64face_landmark_68 = face_landmark_68.reshape(1, -1, 2) * 256face_landmark_68 = cv2.transform(face_landmark_68, cv2.invertAffineTransform(affine_matrix))face_landmark_68 = face_landmark_68.reshape(-1, 2)face_landmark_5of68 = convert_face_landmark_68_to_5(face_landmark_68)return face_landmark_68, face_landmark_5of68
- 繪制檢測結果
最后將得到的68個人臉面部關鍵點繪制在輸入圖像上。得到的結果如下:
2.1.3 人臉對齊
- 模型初始化
同上一步,所有onnx模型初始化的步驟都是一樣的。 - 圖像預處理
使用 warp_face_by_face_landmark_5 函數按人臉特征點進行裁剪和對齊。將圖像像素值從原始范圍 [0, 255] 轉換到范圍 [-1, 1]。轉置圖像通道順序,使其符合模型的輸入格式。def preprocess(self, srcimg, face_landmark_5):crop_img, _ = warp_face_by_face_landmark_5(srcimg, face_landmark_5, 'arcface_112_v2', (112, 112))crop_img = crop_img / 127.5 - 1crop_img = crop_img[:, :, ::-1].transpose(2, 0, 1).astype(np.float32)crop_img = np.expand_dims(crop_img, axis = 0)return crop_img
- 特征向量提取
首先調用 preprocess 方法對輸入圖像進行預處理。使用 ONNX Runtime 進行推理,提取人臉特征向量(embedding)。對特征向量進行歸一化處理,得到歸一化后的特征向量(normed_embedding)。def detect(self, srcimg, face_landmark_5):input_tensor = self.preprocess(srcimg, face_landmark_5)# Perform inference on the imageembedding = self.session.run(None, {self.input_names[0]: input_tensor})[0]embedding = embedding.ravel()normed_embedding = embedding / np.linalg.norm(embedding)return embedding, normed_embedding
該模型的主要功能是通過人臉對齊來提取人臉特征向量。人臉對齊是人臉識別任務中的關鍵步驟,它有助于將輸入的人臉圖像標準化,使其在不同的拍攝角度、光照和表情變化下具有一致的表示。
2.1.4換臉處理
前面做了那么多處理,終于我們來到了關鍵步驟:換臉處理!此處用到的模型是inswapper_128,該模型通過將源圖像中的人臉特征嵌入到目標圖像中的人臉區域,實現自然逼真的換臉效果。
- 模型初始化
繼續同樣地加載 ONNX 模型,并創建 ONNX Runtime 會話,并獲取模型的輸入名稱和輸入形狀。和之前不同的是這一步需要加載模型矩陣,用于對源人臉特征向量進行變換。def __init__(self, modelpath):# Initialize modelsession_option = onnxruntime.SessionOptions()session_option.log_severity_level = 3self.session = onnxruntime.InferenceSession(modelpath, sess_options=session_option)model_inputs = self.session.get_inputs()self.input_names = [model_inputs[i].name for i in range(len(model_inputs))]self.input_shape = model_inputs[0].shapeself.input_height = int(self.input_shape[2])self.input_width = int(self.input_shape[3])self.model_matrix = np.load('model_matrix.npy')
- 圖像處理和換臉
- 圖像預處理
- 人臉對齊:使用 warp_face_by_face_landmark_5 函數將目標圖像按人臉特征點進行裁剪和對齊。
- 創建遮罩:使用 create_static_box_mask 創建靜態盒子遮罩,方便后續將換臉結果融合回原圖像。
- 歸一化處理:將圖像像素值從原始范圍 [0, 255] 轉換到 [0, 1],并進行標準化處理,使其符合模型的輸入要求。
- 特征向量變換
- 源人臉特征變換:將源人臉特征向量進行變換,并歸一化處理,以符合模型的輸入要求。
- 模型推理
- 換臉推理:使用 ONNX Runtime 對預處理后的圖像和源人臉特征向量進行推理,得到換臉結果。
- 結果處理:將換臉結果圖像轉換回原始圖像格式。
- 融合換臉結果
- 融合處理:將換臉結果圖像融合回原圖像中,確保換臉區域自然逼真。
def process(self, target_img, source_face_embedding, target_landmark_5):###preprocesscrop_img, affine_matrix = warp_face_by_face_landmark_5(target_img, target_landmark_5, 'arcface_128_v2', (128, 128))crop_mask_list = []box_mask = create_static_box_mask((crop_img.shape[1],crop_img.shape[0]), FACE_MASK_BLUR, FACE_MASK_PADDING)crop_mask_list.append(box_mask)crop_img = crop_img[:, :, ::-1].astype(np.float32) / 255.0crop_img = (crop_img - INSWAPPER_128_MODEL_MEAN) / INSWAPPER_128_MODEL_STDcrop_img = np.expand_dims(crop_img.transpose(2, 0, 1), axis = 0).astype(np.float32)source_embedding = source_face_embedding.reshape((1, -1))source_embedding = np.dot(source_embedding, self.model_matrix) / np.linalg.norm(source_embedding)###Perform inference on the imageresult = self.session.run(None, {'target':crop_img, 'source':source_embedding})[0][0]###normalize_crop_frameresult = result.transpose(1, 2, 0)result = (result * 255.0).round()result = result[:, :, ::-1]crop_mask = np.minimum.reduce(crop_mask_list).clip(0, 1)dstimg = paste_back(target_img, result, crop_mask, affine_matrix)return dstimg
- 圖像預處理
2.1.5圖像增強
此處采用的模型是gfpgan_1.4,用于人臉圖像增強,旨在提高圖像的清晰度和質量,使得換臉效果更為自然逼真。
- 模型初始化
同上上一步一致。 - 圖像處理和增強
- 圖像預處理
- 人臉對齊:使用 warp_face_by_face_landmark_5 函數將目標圖像按人臉特征點進行裁剪和對齊。
- 創建遮罩:使用 create_static_box_mask 創建靜態盒子遮罩,方便后續將增強結果融合回原圖像。
- 歸一化處理:將圖像像素值從原始范圍 [0, 255] 轉換到 [-1, 1],這有助于提高模型的性能。
- 模型推理
- 圖像增強推理:使用 ONNX Runtime 對預處理后的圖像進行推理,得到增強后的圖像。
- 結果處理:將增強后的圖像從 [-1, 1] 轉換回 [0, 255] 的范圍,并轉換為 uint8 類型。(這一步是不是量化?)
- 融合增強結果
- 融合處理:將增強后的圖像融合回原圖像中,確保增強區域自然逼真。
def process(self, target_img, target_landmark_5):###preprocesscrop_img, affine_matrix = warp_face_by_face_landmark_5(target_img, target_landmark_5, 'ffhq_512', (512, 512))box_mask = create_static_box_mask((crop_img.shape[1],crop_img.shape[0]), FACE_MASK_BLUR, FACE_MASK_PADDING)crop_mask_list = [box_mask]crop_img = crop_img[:, :, ::-1].astype(np.float32) / 255.0crop_img = (crop_img - 0.5) / 0.5crop_img = np.expand_dims(crop_img.transpose(2, 0, 1), axis = 0).astype(np.float32)###Perform inference on the imageresult = self.session.run(None, {'input':crop_img})[0][0]###normalize_crop_frameresult = np.clip(result, -1, 1)result = (result + 1) / 2result = result.transpose(1, 2, 0)result = (result * 255.0).round()result = result.astype(np.uint8)[:, :, ::-1]crop_mask = np.minimum.reduce(crop_mask_list).clip(0, 1)paste_frame = paste_back(target_img, result, crop_mask, affine_matrix)dstimg = blend_frame(target_img, paste_frame)return dstimg
最終結果展示
- 源圖片
- 目標圖片
- 最終結果
四、結論
本項目通過使用多個先進的深度學習模型,實現了高效且逼真的AI換臉功能。首先,利用YOLOface_8n模型進行人臉檢測,并通過face_68_landmarks模型獲取面部68個關鍵點,確保了檢測結果的精確性和一致性。接著,arcface_w600k_r50.onnx模型提取源人臉的高維特征向量,通過對齊和歸一化處理,確保特征向量的穩定性和準確性。然后,inswapper_128.onnx模型負責將源人臉特征嵌入到目標人臉圖像中,實現自然逼真的人臉替換。最后,使用gfpgan_1.4.onnx模型對換臉結果進行圖像增強和修復,進一步提高圖像的清晰度和細節,使最終結果更加自然逼真。本項目展示了AI換臉技術的強大潛力和廣泛應用前景,為影視制作、社交媒體和隱私保護等領域提供了有力的技術支持