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
pip install paddlehub
在這個遊戲中使用的是 PaddleHub 中的 facelandmarklocalization 模型,安裝好 PaddleHub 之後就能夠直接調用了!
模型的詳細介紹參考:
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