基於PaddleHub的人臉檢測:AI人臉打飛機(第2版)

第 1 版鏈接如下:

基於PaddleHub的AI人臉偵測:不再用手打灰機

第 2 版的改進:

  1. 大幅增加流暢度
  2. 採用新的動作偵測算法,代碼縮減 100 行 +
  3. 修改遊戲背景
  4. 修正音樂 bug
  5. 修改部分飛機參數

效果視頻請看 B 站,鏈接如下:

基於PaddleHub的人臉檢測:AI人臉打飛機(第2版)附代碼

所有代碼和素材文件請見 GitHub:

github: planegame_head_control

關於頭部角度的算法:

原來使用的是歐拉角的算法,來進行頭部的角度計算

但是分析了一下以這個項目的需求來看,不需要精度太高的角度計算,所有其實可以採用簡化的算法來代替複雜的歐拉角計算

頭部角度檢測部分的代碼片段如下

class HeadPostEstimation():
    """
    頭部姿態識別
    """

    def __init__(self, face_detector=None):
        self.module = hub.Module(name="face_landmark_localization", face_detector_module=face_detector)


    def get_face_landmark(self, image):
        """
        預測人臉的68個關鍵點坐標
        images(ndarray): 單張圖片的像素數據
        """
        try:
            # 選擇GPU運行,use_gpu=True,並且在運行整個教程代碼之前設置CUDA_VISIBLE_DEVICES環境變量
            res = self.module.keypoint_detection(images=[image], use_gpu=True)
            return True, res[0]['data'][0]
        except Exception as e:
            logger.error("Get face landmark localization failed! Exception: %s " % e)
            return False, None

    def get_lips_distance(self, face_landmark):
        """
        從face_landmark_localization的檢測結果中查看上下嘴唇的距離
        """

        lips_points = np.array([
            face_landmark[52], face_landmark[58]
        ], dtype='float')

        head_points = np.array([
            face_landmark[25], face_landmark[8]
        ], dtype='float')

        lips_distance = np.sum(np.square(lips_points[0] - lips_points[1]))
        head_distance = np.sum(np.square(head_points[0] - head_points[1]))
        relative_distance = lips_distance / head_distance
        return relative_distance

    def get_nose_distance(self,face_landmark):
        """
        從face_landmark_localization的檢測結果中獲得鼻子的位置,以此判斷頭部運動
        """

        nose_point = np.array([
            face_landmark[31]
        ], dtype='float')

        cheek_points = np.array([
            face_landmark[3], face_landmark[15]
        ], dtype='float')

        left_distance = np.sum(np.square(nose_point[0] - cheek_points[0]))
        right_distance = np.sum(np.square(nose_point[0] - cheek_points[1]))
        nose_position_h = left_distance/(left_distance+right_distance)

        nose_position_v = nose_point[0][1]-cheek_points[0][1] # 獲得鼻子和臉頰定位點的高度相對值,以此作為抬頭/低頭的判斷

        return nose_position_h, nose_position_v


    def classify_pose(self, video):
        """
        video 表示不斷產生圖片的生成器
        """

        for index, img in enumerate(video(), start=1):
            self.img_size = img.shape

            success, face_landmark = self.get_face_landmark(img)

            if not success:
                logger.info("Get face landmark localization failed! Please check your image!")
                continue

            if not success:
                logger.info("Get rotation and translation vectors failed!")
                continue

            # 計算嘴唇距離
            lips_distance = self.get_lips_distance(face_landmark)

            # 計算鼻子左右位置
            nose_position_h, nose_position_v = self.get_nose_distance(face_landmark)

            # 轉換成攝像頭可顯示的格式
            img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

            # 本地顯示預測視頻框,AIStudio項目不支持顯示視頻框
            #cv2.imshow('Pose Estimation', img_rgb)

            return nose_position_h, nose_position_v, lips_distance