顯示具有 opencv 標籤的文章。 顯示所有文章
顯示具有 opencv 標籤的文章。 顯示所有文章

2020年12月1日 星期二

[DL]Static Gestures Recognition(4): Preparing appropriate dataset

從構想、規劃、試錯到完成深度學習的手勢辨識模型。





第4關 
準備資料<I>   Get dataset from Kaggle.

進行深度學習的過程中,尋找合適的訓練資料所耗費的成本並不亞於在學習的過程。


既然要很多圖片,從哪開始找呢?

我從 Kaggle 上找尋了關於手勢的圖片集,

心想就先照著圖片分類來定義預測 0,1,2,3,4,5  的手勢吧!

依序照著流程處理,在jupyter lab 中看來測試時相當正常,但很快就發現失敗了…………

Why?

問題出在哪? 

 

 

讓機器訓練的圖片集(有雜訊背景),

但是實際上測試時卻是透過OpenCV取得的閾值畫面,這兩者根本不一樣!  

 (情境好比讓學生讀國文,但實際考題卻是考日文,雖然都是語言,但對機器來說這難以找出相同的特徵進行預測。)

再做一次嘗試,如果Kaggle來源圖片直接轉成閾值圖片去訓練呢?

在直接用圖片測試的過程中表現不錯,可是實際由Webcam擷取影像的過程依舊有問題。

奇怪?   

跟預期不同的地方在哪裡?




苦思許久,得出的結論是:

雖然是用現成的圖片集,但實際上機器預測所接收的圖片還是有落差,

圖片集已經先將手臂去背,僅留下手掌與手指;

但實際情境中,仍然有整隻手臂的圖像,訓練資料與現實生活中的影像有著明顯差異,極可能是失敗的主因;

而且,我認為沒有必要透過影像處理非得將手腕以下的影像去除,

因為在手勢操作中,手臂也是手勢的一部份才合理。 

其次是這部分的程式我還不會寫XD 

與達成目標不見得有直接相關,評估目前太難兼無法確認效益的issue 沒有必要非去挑戰不可呀…… 其實是偷懶





準備資料<II>   Try again, capture the gesture by myself.


因為沒有辦法一次找到令人滿意的資料,所以我決定要重新錄製自己的手勢,

並同時參考網友做過類似的範例,發現確實有類似的成功案例,

既然有前例可循,也就代表自己所設想的主要方向沒錯。並設定條件如下:


● 用OpenCV拍照截圖 + 從Kaggle 找到適合的訓練圖,雙管齊下。

這裡自行取得圖片時,有將背景極簡化 + 並設定cv2.threshold()方法,

將訓練的內容盡可能的簡化,去除非必要的因素,採用灰階單色圖片。

再分別配置訓練training、驗證validation、測試test 等資料夾。


● 各個手勢應先定義有明確不同的形狀。

如果太過接近模稜兩可,以訓練圖片量不夠多的情況下,

我無法保證預測的效果是否可能令人滿意? 因此這個有疑點的因素需要先排除。


● 設定的手勢有沒有不符各國當地民情?  查了一下還真的有! 

大家習以為常的OK,居然在法國與比利時是罵人的意思,

發現幾乎不可能所有手勢都能夠在各地通用,大概只有 I LOVE YOU 是最沒問題的。

這9種手勢 出國亂比會出事




在這一階段,定義了以下5種手勢:


這裡先特別說明為什麼有none = 漆黑一片

因為在先前測試過程中,發現在沒有手勢進入ROI之前,

機器仍會將當下畫面判斷為一種結果,這可能導致預測將與真實偏離太遠。

而現階段目的是為了要先能確認透過CNN可以訓練辨識手勢,

因此先將無訊號也定義為一種輸入。


後話:

測試成功後,我參考 Gesture Jseter 定義的手勢,又再添加了兩種手勢,Straight & Thumb。

之所以增加新的手勢定義除了豐富性之外,也考慮到新增定義要與原本的手勢有明顯區別會比較好(免得增加誤判機率)。

本來也有想到 Star Trek 瓦肯人的問候手勢,但我實在比不出來!  只能放棄......這也是很講天份






Series link:

[DL]Static Gestures Recognition(1): Conception

[DL]Static Gestures Recognition(2): Solution finding

[DL]Static Gestures Recognition(3): Define workflow

[DL]Static Gestures Recognition(4): Preparing appropriate dataset

[DL]Static Gestures Recognition(5): Time is worth more than money

[DL]Static Gestures Recognition(6): Training Model & Next Step






[DL]Static Gestures Recognition(2): Solution finding

從構想、規劃、試錯到完成深度學習的手勢辨識模型。


第2關 – 最初的方向與實驗


首先,我們必須先將判斷的區域限縮定義出ROI (Region Of Interest),

而不想因為其他的東西加入而造成影響判斷結果。

既然要辨識手勢,就應該先將圈選出手部區域,並去除之外不需要的部分。過多的背景資訊也是暫時不需要的。







發想/試錯/縮小目標範圍過程 - Using OpenCV 

 一開始從網路上找範例參考,使用 OpenCV 來檢測手部輪廓,

但發現光線因素將影響偵測結果的好壞,

如果沒有準確判斷出手部輪廓,且手指之間的凹點若沒有精準求出,並無法算出目前手勢中有伸出幾根指頭,

而且再複雜一點的手勢並無法用同一套演算法求得。


很快地我得出了結論,這並非是一個有效的解決方案。

必定要找更確實,能夠實現功能的方式。






接著,我回頭將最初的問題再思考 →

如何讓手部的動作被電腦記住,並依據可靠的資訊判斷出正確手勢並給予回應?


答案是


在影像偵測與辨識上已經有廣泛應用的

深度學習 

Deep Learning











2020年7月27日 星期一

[Python+OpenCV] Build Document Scanner

原文出處  www.pyimagesearch.com

本文為紀錄心得與添加學習過程中的註解。 

How to Build a Document Scanner in Just 5 Minutes


如何將文件變成像是掃描的效果?

如果你手邊有些筆記MEMO、收據、明細等等之類的小物,有時想要整理一下變得井然有序,

作為練習OpenCV, 這是一個很不錯的練習範例。

先來看看結果,圖片前後對比:



     





要達成這個效果,必要步驟如下:

1. 偵測圖像邊緣

2. 透過邊緣去找出--要被轉成掃描效果的紙張輪廓

3. 套用透視變換取得鳥瞰圖(俯視)



偵測圖像邊緣的必要步驟

◎ 轉為灰階

◎ 運用高斯模糊

◎ Canny方法

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5,5), 0) # 這裡有關鍵細節...
# 高斯模糊: cv2.GaussianBlur(img, ksize, sigmaX, sigmaY, borderType)
# ksize: 濾波核的大小, 必須是奇數。 如果數值越大就越模糊。
# sigmaX: 是卷積核在X軸上的標準差, 通常與sigmaY都可設為0
# borderType: 邊界類型, 直接採用預設值即可。

edged = cv2.Canny(gray, 75, 200)


確認取得邊緣




找出圖形輪廓並將所有輪廓排序,只保留前5個最大的輪廓。 (但為什麼是5? 不是6?  不甚明白)

總之,目標是要找出矩形輪廓。

我們可以想像:一張紙是個矩形,right !? 

矩形有四個邊,所以若能找出正好有4個點的最大輪廓,那就是我們要掃描的紙張矩形。


# find Contours, 面積從大到小排序, 只保留前5個
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key =cv2.contourArea, reverse=True)[:5]  

for cnt in cnts:
    # 近似輪廓
    peri = cv2.arcLength(cnt, True)    # 取得(perimeter), True: 邊界Closed 無中斷
    # 多邊形逼近, 目標只要取4個點就好
    # 可以想成是用粗一點的線來描邊, 來忽略掉細微的毛邊或雜點
    # 實務上建議取Contour周長的1~5%作為其epsilon值
    # 值愈大,則Contour Approximation結果的點數愈少,反之則愈多。
    approx = cv2.approxPolyDP(cnt, 0.01 * peri, True)  

    if len(approx) == 4:
        screenCnt = approx

cv2.drawContours(image, [screenCnt], -1, (0,255,0), 2)






應用透視轉換 and threshold


# 對原圖進行透視轉換, 目標圖檔的輪廓乘以之前調整的比例 (因為之前的處理是縮小後的圖), 
# 但要對原圖進行處理, 所以要恢復的話就必須乘回去
wraped = four_point_transform(ori, screenCnt.reshape(4,2)* ratio)

# 換成掃描黑白效果, 先轉成灰階後 + 自適應閾值
wraped = cv2.cvtColor(wraped, cv2.COLOR_BGR2GRAY)
T = threshold_local(wraped, 11, offset=10, method="gaussian")
wraped = (wraped > T).astype("uint8") * 255

cv2.imshow("Scanned", imutils.resize(wraped, height = 650))
cv2.imwrite("images\\recipt.png", wraped)




看起來確實是像掃描過的圖片呢

原範例中使用了作者提供的類別

from pyimagesearch.transform import four_point_transform # 進行透視轉換用
import imutils # 相當便利的圖像處理工具
from skimage.filters import threshold_local # 將圖片應用threshold使其成為黑白狀態