AI時代用臉玩「飛機大戰」,PaddleHub讓你秒變「臉控」遊戲達人

AI時代還拿着手機打飛機遊戲是否是out了?飛槳PaddleHub帶你體驗不同的遊戲玩法。算法

下載安裝命令

## CPU版本安裝命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle

## GPU版本安裝命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu

從世界上第一款遊戲誕生開始,電玩都須要依賴手柄和按鍵進行,不管是PC遊戲,仍是 XBOX 、PS 這類主機遊戲,控制器和手柄都是不可缺乏的。框架

直到2009年微軟發佈了第一代 Kinect,將人體檢測做爲遊戲控制,完全顛覆了遊戲的單一操做,開創瞭解放雙手的先河,令人機互動的理念更加完全地展示出來。可是以後,2018 年微軟完全棄用了 Kinect 實在讓人可惜!ide

大衆流行的遊戲文化中,人機互動的主流方式仍是離不開手柄,即便到了手機和Pad 橫行的移動時代,大多數的主流遊戲依然利用的是虛擬鍵盤和虛擬手柄的交互方式。函數

人類發展的動力其實很大一部分來自於人類「懶惰」的天性,因此對我而言,若是能經過更簡單、自由的交互方式玩遊戲,將是很是有意思的事情。雖然咱們離腦機接口和思惟控制還有很長的路要走,可是隨着深度學習的發展,相信不久的未來,交互方式也會產生天翻地覆的變化。post

基於此,我嘗試作了人臉打飛機的遊戲項目!學習

效果展現

 

使用普通電腦自帶的攝像頭捕捉人體動做(這裏主要是頭部),進而轉化爲對於遊戲的控制。字體

左轉頭部:飛機往左飛人工智能

右轉頭部:飛機往右飛url

擡頭:飛機向上spa

低頭:飛機向下

張嘴:丟炸彈!

操做簡單歡樂,聽說還能治好程序猿們的頸椎病~~

而這一切的實現很是簡單,只要使用 飛槳PaddleHub 封裝好的深度學習模型便可,由此得到頭部的角度檢測,而後連接到遊戲控制便可!

無需人工智能的高深技術理念,絕對小白同窗也能夠輕鬆搞定!!!


實現方法

 

打飛機遊戲的實現須要完成分以下三個步驟:

  • 使用PaddleHub中的 facelandmarklocalization 模型實現頭部運動監測。
  • 使用Pygame實現打飛機遊戲主體程序。(這裏用了最簡單易上手,一般用來作 Python 入門初體驗的 Pygame)
  • 將頭部運動監測模塊加入遊戲中。

 

下面我給你們詳細介紹一下具體的代碼實現。

01安裝 PaddleHub

 

  1. 安裝飛槳
  2. 安裝Paddlehub。

pip install paddlehub

在這個遊戲中使用的是 PaddleHub 中的 facelandmarklocalization 模型,安裝好 PaddleHub 之後就能夠直接調用了!

模型的詳細介紹參考:

https://www.paddlepaddle.org.cn/hubdetail?name=face_landmark_localization&en_category=KeyPointDetection

02 實現遊戲主體程序

 

這裏我使用的是本身初學 Python 時,用 Pygame 製做的打飛機遊戲。素材上,不管圖片、飛機模型仍是背景音樂網上很是多,很是容易獲取(由於是入門款嘛~)

pip install pygame

具體的文件和素材請參考AI Studio:

https://aistudio.baidu.com/aistudio/projectdetail/405645

文件夾中分別存放了圖片,音樂和字體素材。經過pygame的各類模塊和函數來定義各項遊戲內容的參數,好比敵機出現的時間、運動的方向、運動速度、碰撞等事件監測等,這裏就不一一贅述了。

而後開始實現最重要的遊戲主體文件,定義整個遊戲如何開始,如何循環,如何操做,如何結束。

原程序中,我是用 空格鍵、上下左右鍵來控制飛機,對應的程序片斷以下:

if bomb_num and event.key == K_SPACE:
    bomb_sound_use.play()
    bomb_num -= 1
key_pressed = pygame.key.get_pressed()
if key_pressed[K_w] or key_pressed[K_UP]:
    myplane.move_up() # 飛機向上飛
elif key_pressed[K_s] or key_pressed[K_DOWN]:
    myplane.move_down() # 飛機向下飛
elif key_pressed[K_a] or key_pressed[K_LEFT]:
    myplane.move_left() # 飛機向左飛
elif key_pressed[K_d] or key_pressed[K_RIGHT]:
    myplane.move_right() # 飛機向右飛

 

03 將 PaddleHub 的頭部運動監測模塊加入遊戲中

 

1. 加入人臉識別和頭部姿態識別的類,先經過人臉檢測找到視頻畫面中人臉的位置。

在初版程序中,使用了ultralightfastgenericfacedetector1mb_640,雖然精度更高,可是和遊戲程序結合起來後資源消耗太大,影響了速度。第二版聽取專家的建議,下降爲ultralightfastgenericfacedetector1mb_320,精度其實足夠用了,同時大幅提高了遊戲的流暢度!

 

class MyFaceDetector(object):
    """
    自定義人臉檢測器
    """

    def __init__(self):
        self.module = hub.Module(name="ultra_light_fast_generic_face_detector_1mb_320")
        self.alpha = 0.75
        self.start_flag = 1

    def face_detection(self, images, use_gpu=False, visualization=False):
        # 使用GPU運行,use_gpu=True,而且在運行整個教程代碼以前設置CUDA_VISIBLE_DEVICES環境變量
        result = self.module.face_detection(images=images, use_gpu=use_gpu, visualization=visualization)
        if not result[0]['data']:
            return result

        face = result[0]['data'][0]
        if self.start_flag == 1:

            self.left_s = result[0]['data'][0]['left']
            self.right_s = result[0]['data'][0]['right']
            self.top_s = result[0]['data'][0]['top']
            self.bottom_s = result[0]['data'][0]['bottom']

            self.start_flag = 0
        else:
            # 加權平均上一幀和當前幀人臉檢測框位置,以穩定人臉檢測框
            self.left_s = self.alpha * self.left_s + (1 - self.alpha) * face['left']
            self.right_s = self.alpha * self.right_s + (1 - self.alpha) * face['right']
            self.top_s = self.alpha * self.top_s + (1 - self.alpha) * face['top']
            self.bottom_s = self.alpha * self.bottom_s + (1 - self.alpha) * face['bottom']

        result[0]['data'][0]['left'] = self.left_s
        result[0]['data'][0]['right'] = self.right_s
        result[0]['data'][0]['top'] = self.top_s
        result[0]['data'][0]['bottom'] = self.bottom_s

        return result

 

而後經過頭部姿態識別,來斷定頭部的動做狀態。

在初版程序中,使用了歐拉角的計算來得到人頭部的運動狀態,可是計算很複雜,對於數學基礎不是很好的人很是難理解。第二版中,把頭部運動狀態的計算方式大幅簡化,只採用了facelandmarklocalization識別出的68我的臉關鍵點中的7個就達到了很好的預期效果,並且算法更簡潔明瞭,實際效果也很是流暢!

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

在遊戲程序初始化時啓動攝像頭進行頭部監測。

# 使用頭部控制飛機
    face_detector = MyFaceDetector()
    # 打開攝像頭
    capture = cv2.VideoCapture(0)

    def generate_image():
        while True:
            # frame_rgb即視頻的一幀數據
            ret, frame_rgb = capture.read()
            # 按q鍵便可退出
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
            if frame_rgb is None:
                break
            frame_bgr = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2BGR)
            yield frame_bgr
        capture.release()
        cv2.destroyAllWindows()

    head_post = HeadPostEstimation(face_detector)

在原來遊戲程序主循環中,把控制算法都替換爲頭部監測後的輸出。

# 獲取頭部運動數據,並控制飛機
nose_position_h, nose_position_v, lips_distance = head_post.classify_pose(video=generate_image)
#print(nose_position_h, nose_position_v, lips_distance) # 該語句用來查看評估參數如何設計
if nose_position_h < 0.22:
    myplane.move_left() # 因爲攝像頭演示中鏡面關係,實際使用中請設置爲myplane.move_right()
elif nose_position_h > 0.48:
    myplane.move_right() # 因爲攝像頭演示中鏡面關係,實際使用中請設置爲myplane.move_left()
elif nose_position_v < -40:
    myplane.move_up()
elif nose_position_v > -25:
    myplane.move_down()

# 張嘴就是炸彈,dis_control<0.045 爲閉嘴
if lips_distance < 0.045:
    flag = 1
if bomb_num and lips_distance > 0.055 and flag == 1:
    flag = 0
    bomb_sound_use.play()
    bomb_num -= 1

 

萬事俱備,一鍵運行,見證奇蹟的時刻到了

 

 

將全部的代碼和素材下載到本地後,就能夠啓動 mani.py 一鍵運行啦!(電腦要有攝像頭哦!)

你們也能夠把其中的代碼片斷加入到本身的遊戲程序裏,相信大家的創意能夠帶來更多不一樣凡響的呈現效果!

在嘗試的過程當中,初版和第二版的差距仍是很是明顯的,你們能夠看看效果呈現對比:

https://www.bilibili.com/video/BV1uZ4y147ur

共同探討

在實現過程當中有幾個下問題,還須要進一步研究和探討:

  • 因爲調整參數的時候,是基於我本身的臉進行的,因此不知作別人的臉控制遊戲時精度會不會有影響。
  • 原來想再作一版人臉和飛機重疊的效果呈現,可是在 pygame 的框架下還沒折騰出來怎麼實現。
  • 攝像頭視角和人類視角是鏡面關係,因此爲了拍攝視頻我調整成爲了左右相反,實際中須要對調過來。

將來可期

第二版完成後,原本想作個第三版,利用 PaddleHub 的人體骨骼監測模塊,實現經過人體運動來控制飛機,可是這個模塊目前尚未辦法直接接入實時的視頻畫面,因此做罷了。

不過好消息是,據說不久的未來 PaddleHub 會對各個模塊的接口進行進一步的豐富,到時候應該能夠實現了,有沒有小夥伴願意一塊兒一試呢?

下載安裝命令

## CPU版本安裝命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle

## GPU版本安裝命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu
相關文章
相關標籤/搜索