基於開源模型搭建實時人臉識別系統(三):人臉關鍵點、對齊模型概覽與模型選型

CoderInCV發表於2023-09-22

基於開源模型搭建實時人臉識別系統(二):人臉檢測概覽與模型選型_CodingInCV的部落格-CSDN部落格

摘要

人臉對齊(face alignment)或者人臉關鍵點(face alignment)是定位人臉上的關鍵點,是很多基於人臉的任務的前置步驟,比如人臉識別、表情分析、人臉變裝(makeup)等。人臉對齊有2D和3D對齊,本篇主要講2D對齊。

  • 人臉姿態對齊:人臉識別等演算法都需要對人臉的姿態進行對齊從而提高模型的精度。
  • 人臉美顏與編輯:基於關鍵點可以精確分析臉型、眼睛形狀、鼻子形狀等,從而對人臉的特定位置進行修飾加工,實現人臉特效美顏,貼片等娛樂功能。
  • 人臉表情分析與嘴型識別:基於關鍵點可以對人的面部表情進行分析,從而用於互動娛樂,行為預測等場景。
    image.png
    根據關鍵點個數,主要分為5點、68點、98點、106點等。
    image.png
    image.png
    image.png
    人臉關鍵點定位的困難主要來源於以下幾個方面:
  • 人臉姿態
  • 人臉遮擋
  • 人臉表情
  • 環境光照
    image.png

人臉關鍵點演算法概覽

同樣的人臉關鍵點演算法也分為傳統和深度學習時期,目前主要使用深度學習。
對於關鍵點任務,一般將其作為一個迴歸任務,即目標是迴歸每個關鍵點的位置;另一種方式是引入heatmap。這裡不展開闡述,需要更深入瞭解,可以閱讀下面的連結。
image.png
更多:
人臉關鍵點綜述 - 知乎 (zhihu.com)
Article (iasj.net)

演算法選型

Face Alignment | Papers With Code
人臉對齊開源的演算法還挺多,但是這sota演算法開源的權重都太大,無法達到我們的輕量化要求。人臉對齊作為人臉質量篩選和人臉識別前的步驟,不能計算量太大。
按照計算量要求,我們選擇了這一演算法(選擇其中的Student@128):
image.png
人臉關鍵點模型的輸入是人臉區域,具體而言是對人臉檢測模型得到的人臉框進行一定的放大後的區域。
修改後模型的推理程式碼如下:

class FaceLandmarks(BaseModel):
    def __init__(self, model_path, device="cpu", **kwargs) -> None:
        super().__init__(model_path, device, **kwargs)
        self.input_size = 128
        self.extend = [0.2, 0.3]

    def preprocess(self, image: np.ndarray, bbox: np.ndarray):
        bbox_width = bbox[2] - bbox[0]
        bbox_height = bbox[3] - bbox[1]

        face_size = bbox_width
        # face_size = int(max(bbox_width, bbox_height))
        face_width = (1 + 2 * self.extend[0]) * face_size
        center = [(bbox[0] + bbox[2]) // 2, (bbox[1] + bbox[3]) // 2]

        ### make the box as square
        crop_bbox = np.zeros(4, dtype=np.int32)
        crop_bbox[0] = center[0] - face_width // 2
        crop_bbox[1] = center[1] - face_width // 2
        crop_bbox[2] = center[0] + face_width // 2
        crop_bbox[3] = center[1] + face_width // 2

        # limit the box in the image
        crop_bbox[0] = max(0, crop_bbox[0])
        crop_bbox[1] = max(0, crop_bbox[1])
        crop_bbox[2] = min(image.shape[1], crop_bbox[2])
        crop_bbox[3] = min(image.shape[0], crop_bbox[3])
        
        # crop
        crop_bbox = crop_bbox.astype(np.int32)
        crop_image = image[crop_bbox[1] : crop_bbox[3], crop_bbox[0] : crop_bbox[2], :]
        crop_image = cv2.resize(crop_image, (self.input_size, self.input_size))

        return crop_image, crop_bbox

    def run(self, image: np.ndarray, bbox: np.ndarray) -> np.ndarray:
        input, crop_box = self.preprocess(image, bbox)
        input = input.astype(np.float32)
        input = input / 255.0
        input = input.transpose((2, 0, 1))
        input = np.expand_dims(input, axis=0)
        output, _ = self.inference(input)
        landmarks = np.array(output)[:98*2].reshape(-1, 2)
        landmarks = self.postprocess(landmarks, crop_box)

        #change 98 points to 5 points
        landmarks = landmarks[[96, 97, 54, 88, 92], :]
        return landmarks

    def postprocess(self, landmarks: np.ndarray, crop_box)->np.ndarray:
        h = crop_box[3] - crop_box[1]
        w = crop_box[2] - crop_box[0]

        landmarks[:, 0] = landmarks[:, 0] * w + crop_box[0]
        landmarks[:, 1] = landmarks[:, 1] * h + crop_box[1]
        return landmarks

測試

得到的關鍵點如下(為了方便後續的使用,我們將98關鍵點轉換為了5個關鍵點):
image.png

CPU上的平均耗時為8ms, 還是非常快的。

結語

本篇簡單介紹了人臉對齊,但只是從實用的角度淺嘗輒止,感興趣的還是需要搜尋相關文獻進一步學習。

原始碼歡迎光臨我的麵包多:CoderInCV的個人主頁 (mbd.pub)

相關文章