1. 程式人生 > >一個手勢就可以讓你為所欲為,Python訓練神經網路來檢測手勢!

一個手勢就可以讓你為所欲為,Python訓練神經網路來檢測手勢!

 

靈感

想象一下,你正在為親人舉辦生日派對。每個人都玩得很開心,音樂正在播放,派對很吵。突然間,是生日蛋糕的時候了!使用Alexa的聲音太大了,而不是尋找你的手機或遙控器,如果你只是在談話中間舉起一隻張開的手,你的智慧家居裝置會識別出這種姿勢,然後關閉音樂?用同樣的手勢,你可以調暗燈光 - 及時看到生日蠟燭點亮了生日男孩或女孩的臉。這不是很棒嗎?

學習Python中有不明白推薦加入交流群
                號:960410445
                群裡有志同道合的小夥伴,互幫互助,
                群裡有不錯的視訊學習教程和PDF!

可以在此處找到Github專案儲存庫中的程式碼,或在此處檢視最終的簡報幻燈片。

 
https://github.com/athena15/project_kojak https://docs.google.com/presentation/d/1UY3uWE5sUjKRfV7u9DXqY0Cwk6sDNSalZoI2hbSD1o8/edit?usp=sharing 

 

背景

很長時間都對手勢檢測感到好奇。記得當第一部微軟Kinect問世的時候 - 我只用一揮手就可以玩遊戲並控制螢幕。隨著時間的推移,谷歌主頁和亞馬遜Alexa等裝置釋出,似乎手勢檢測失去了支援語音的雷達。不過,我想知道它是否有可能經歷復興,現在像Facebook門戶和亞馬遜回聲秀這樣的視訊裝置即將問世。考慮到這一點,我想看看是否有可能構建一個能夠實時識別我的手勢的神經網路 - 並執行我的智慧家居裝置!

 

資料和我的早期模型

對這個想法感到很興奮,並迅速採取行動,就像我被射出大炮一樣。開始在Kaggle.com上使用手勢識別資料庫,並探索資料。它由20,000個標記的手勢組成,如下面所示。

 
https://www.kaggle.com/benenharrington/hand-gesture-recognition-database-with-cnn 

 

 

 

奇怪的影象,但標籤和豐富

一旦讀到影象,遇到的第一個問題是我的影象是黑白的。這意味著NumPy陣列只有一個通道,而不是三個(即每個陣列的形狀是(224,224,1))。因此無法將這些影象與VGG-16預訓練模型一起使用,因為該模型需要RGB,3通道影象。這是通過在影象列表上使用np.stack解決的,X_data:

 
X_data = np.array(X_data, dtype = 'float32') X_data = np.stack((X_data,) * 3, axis=-1) 

 

一旦克服了這個障礙,就開始建立一個模型,使用一個完整地保留其中10個人中的2個人的訓練測試分割。在重新執行基於VGG-16架構的模型後,模型獲得了總體0.74的F1分數。這是非常好的,因為隨機猜測超過10個類平均會導致10%的準確率。

 

但是訓練模型以識別來自同質資料集的影象是一回事。訓練它以識別以前從未見過的影象是另一種。嘗試調整照片的光線,並使用深色背景 - 模仿模型訓練過的照片。

 

也嘗試過影象增強 - 翻轉,傾斜,旋轉等等。雖然這些影象比以前做得更好,但仍然無法預測 - 而且看來是不可接受的 - 結果。有一種嘮叨的感覺,需要重新思考這個問題,並提出一種創造性的方法來使這個專案發揮作用。

 

摘要:在儘可能接近現實世界中可能看到的影象的影象上訓練您的模型。

 

重新思考問題

決定轉發並嘗試新的東西。訓練資料的奇怪外觀與我的模型在現實生活中可能看到的影象之間存在明顯的脫節。決定嘗試構建自己的資料集。

 

一直在使用OpenCV,一個開源計算機視覺庫,需要一個工程師一個解決方案,從螢幕上抓取一個影象,然後調整大小並將影象轉換成我的模型可以理解的NumPy陣列。用來轉換資料的方法如下:

 
from keras import load_model model = load_model(path) # open saved model/weights from .h5 file def predict_image(image): image = np.array(image, dtype='float32') image /= 255 pred_array = model.predict(image) # model.predict() returns an array of probabilities - # np.argmax grabs the index of the highest probability. result = gesture_names[np.argmax(pred_array)] # A bit of magic here - the score is a float, but I wanted to # display just 2 digits beyond the decimal point. score = float("%0.2f" % (max(pred_array[0]) * 100)) print(f'Result: {result}, Score: {score}') return result, score 

 

簡而言之,一旦啟動並執行相機,可以抓取框架,對其進行轉換,並從模型中獲取預測:

 
#starts the webcam, uses it as video source camera = cv2.VideoCapture(0) #uses webcam for video while camera.isOpened(): #ret returns True if camera is running, frame grabs each frame of the video feed ret, frame = camera.read() k = cv2.waitKey(10) if k == 32: # if spacebar pressed frame = np.stack((frame,)*3, axis=-1) frame = cv2.resize(frame, (224, 224)) frame = frame.reshape(1, 224, 224, 3) prediction, score = predict_image(frame) 

 

在網路攝像頭和模型之間連線管道取得了巨大成功。開始思考什麼是理想的形象,以供我的模型。一個明顯的障礙是很難將感興趣的區域(在我們的例子中,一隻手)與背景區分開來。

 

提取手勢

採取的方法是任何熟悉Photoshop的人都熟悉的方法 - 背景減法。這是一件美好的事!從本質上講,如果你在手中有一張場景的照片,你可以建立一個“面具”,除了你的手之外,它將刪除新影象中的所有內容。

 

 

 

 

背景掩蔽和二進位制影象閾值

 

一旦我從我的影象中減去背景,然後我使用二進位制閾值使目標手勢完全變白,背景完全變黑。我選擇這種方法有兩個原因:它使手的輪廓清晰明瞭,並且使得模型更容易在具有不同膚色的使用者之間進行推廣。這創造了我最終訓練模型的照片“輪廓”般的照片。

 

構建新資料集

現在可以準確地檢測到我的手中的影象,決定嘗試新的東西。我的舊模型沒有很好地概括,我的最終目標是建立一個能夠實時識別我的手勢的模型 - 所以我決定建立自己的資料集!

 

選擇專注於5個手勢:

 

 

 

 

 

策略性地選擇了4個手勢,這些手勢也包含在Kaggle資料集中,所以可以在以後對這些影象交叉驗證我的模型。我還添加了和平標誌,儘管該手勢在Kaggle資料集中沒有類似物。

 

從這裡開始,我通過設定我的網路攝像頭來構建資料集,並在OpenCV中建立一個點選繫結來捕獲和儲存具有唯一檔名的影象。我試圖改變幀中手勢的位置和大小,這樣我的模型就會更健壯。很快,我建立了一個每個包含550個輪廓影象的資料集。是的,你讀得對 - 我拍攝了超過2700張圖片。

 

訓練新模型

然後使用Keras和TensorFlow構建了一個卷積神經網路。我開始使用優秀的VGG-16預訓練模型,並在頂部添加了4個密集層和一個輟學層。

 

然後採取了不尋常的步驟,選擇在我之前嘗試過的原始Kaggle資料集上交叉驗證我的模型。這是關鍵 - 如果我的新模型無法概括為之前沒有訓練過的其他人的手的影象,那麼它並不比我原來的模型更好。

 

為了做到這一點,我將相同的變換應用到我應用於訓練資料的每個Kaggle影象 - 背景減法和二進位制閾值處理。這給了他們一個類似我的模型熟悉的“外觀”。

 

 

 

 

Palm轉換後的Kaggle資料集手勢

 

結果

該模型的效能超出了我的預期。它幾乎可以對測試集中的每個手勢進行正確分類,最終獲得98%的F1分數,以及98%的精確度和準確度分數。這是個好訊息!

 

正如任何經驗豐富的研究人員所知道的那樣,在實驗室中表現良好而在現實生活中表現不佳的模型並不值得。在我的初始模型遇到同樣的失敗後,我對這個模型在手勢上實時表現良好持謹慎樂觀態度。

 

智慧家居整合

在測試模型之前,我想補充一點。我一直都是一個聰明的家庭愛好者,我的願景一直是用我的手勢控制我的Sonos和飛利浦Hue燈。為了方便地訪問Philips Hue和Sonos API,我分別使用了phue和SoCo庫。它們都非常簡單易用,如下所示:

https://github.com/studioimaginaire/phue

https://github.com/SoCo/SoCo

 
# Philips Hue Settings bridge_ip = '192.168.0.103' b = Bridge(bridge_ip) on_command = {'transitiontime' : 0, 'on' : True, 'bri' : 254} off_command = {'transitiontime' : 0, 'on' : False, 'bri' : 254} # Turn lights on b.set_light(6, on_command) #Turn lights off b.set_light(6, off_command) 

 

使用SoCo通過Web API控制Sonos可以說更容易:

 
sonos_ip = '192.168.0.104' sonos = SoCo(sonos_ip) # Play sonos.play() #Pause sonos.pause() 

 

然後為不同的手勢建立了繫結,以便使用我的智慧家居裝置執行不同的操作:

 
if smart_home: if prediction == 'Palm': try: action = "Lights on, music on" sonos.play() # turn off smart home actions if devices are not responding  except ConnectionError: smart_home = False pass # etc. etc. 

 

當最終實時測試模型時,我對結果非常滿意。我的模型在絕大部分時間都準確地預測了我的手勢,並且我能夠使用這些手勢來控制燈光和音樂。