華為機器學習(ML Kit)提供手部關鍵點識別服務,可用於手語識別。手部關鍵點識別服務能識別手部21個關鍵點,通過每個手指的方向和手語規則作比較去找手語字母表。
應用場景
手語通常被聽力和口語有障礙的人來使用,是收集手勢包含日常互動中所使用的動作和手勢。使用ML Kit 可以建立一個智慧手語字母表識別器,它可以像一個輔助器一樣將手勢翻譯成單詞或者句子,也可以將單詞或者句子翻譯成手勢。這裡嘗試的是手勢當中的美國手語字母表,是基於關節,手指和手腕的位置進行分類。接下來小編將會嘗試從手勢中收集單詞“HELLO”。
開發步驟
1. 開發準備
詳細的準備步驟可以參考華為開發者聯盟,這裡列舉關鍵的開發步驟。
1.1 啟動ML Kit
在華為開發者AppGallery Connect, 選擇Develop > Manage APIs。確保ML Kit 啟用。
1.2 專案級gradle裡配置Maven倉地址
buildscript {
repositories {
...
maven {url 'https://developer.huawei.com/repo/'}
}
}
dependencies {
...
classpath 'com.huawei.agconnect:agcp:1.3.1.301'
}
allprojects {
repositories {
...
maven {url 'https://developer.huawei.com/repo/'}
}
}
1.3 整合SDK後,在檔案頭新增配置
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
dependencies{
// Import the base SDK.
implementation 'com.huawei.hms:ml-computer-vision-handkeypoint:2.0.2.300'
// Import the hand keypoint detection model package.
implementation 'com.huawei.hms:ml-computer-vision-handkeypoint-model:2.0.2.300'
}
1.4 將以下語句新增到AndroidManifest.xml檔案中
<meta-data
android:name="com.huawei.hms.ml.DEPENDENCY"
android:value= "handkeypoint"/>
1.5 申請攝像頭許可權和本地檔案讀取許可權
<!--Camera permission-->
<uses-permission android:name="android.permission.CAMERA" />
<!--Read permission-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2. 程式碼開發
2.1 建立用於相機預覽的Surface View,建立用於結果的Surface View。
目前我們只在UI中顯示結果,您也可以使用TTS識別擴充套件和讀取結果。
mSurfaceHolderCamera.addCallback(surfaceHolderCallback)
private val surfaceHolderCallback = object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
createAnalyzer()
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
prepareLensEngine(width, height)
mLensEngine.run(holder)
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
mLensEngine.release()
}
}
2.2 建立手部關鍵點分析器
//Creates MLKeyPointAnalyzer with MLHandKeypointAnalyzerSetting.
val settings = MLHandKeypointAnalyzerSetting.Factory()
.setSceneType(MLHandKeypointAnalyzerSetting.TYPE_ALL)
.setMaxHandResults(2)
.create()
// Set the maximum number of hand regions that can be detected within an image. A maximum of 10 hand regions can be detected by default
mAnalyzer = MLHandKeypointAnalyzerFactory.getInstance().getHandKeypointAnalyzer(settings)
mAnalyzer.setTransactor(mHandKeyPointTransactor)
2.3 開發者建立識別結果處理類“HandKeypointTransactor”
該類MLAnalyzer.MLTransactor
class HandKeyPointTransactor(surfaceHolder: SurfaceHolder? = null): MLAnalyzer.MLTransactor<MLHandKeypoints> {
override fun transactResult(result: MLAnalyzer.Result<MLHandKeypoints>?) {
var foundCharacter = findTheCharacterResult(result)
if (foundCharacter.isNotEmpty() && !foundCharacter.equals(lastCharacter)) {
lastCharacter = foundCharacter
displayText.append(lastCharacter)
}
canvas.drawText(displayText.toString(), paddingleft, paddingRight, Paint().also {
it.style = Paint.Style.FILL
it.color = Color.YELLOW
})
}
2.4 建立LensEngine
LensEngine lensEngine = new LensEngine.Creator(getApplicationContext(), analyzer)
setLensType(LensEngine.BACK_LENS)
applyDisplayDimension(width, height) // adjust width and height depending on the orientation
applyFps(5f)
enableAutomaticFocus(true)
create();
2.5 執行LensEngine
private val surfaceHolderCallback = object : SurfaceHolder.Callback {
// run the LensEngine in surfaceChanged()
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
createLensEngine(width, height)
mLensEngine.run(holder)
}
}
2.6 停止分析器,釋放檢測資源
fun stopAnalyzer() {
mAnalyzer.stop()
}
2.7 處理 transactResult() 以檢測字元
您可以使用HandKeypointTransactor類中的transtresult方法來獲取檢測結果並實現特定的服務。檢測結果除了手部各關鍵點的座標資訊外,還包括手掌和每個關鍵點的置信值。手掌和手部關鍵點識別錯誤可以根據置信值過濾掉。在實際應用中,可以根據誤認容忍度靈活設定閾值。
2.7.1 找到手指的方向
讓我們先假設可能手指的向量斜率分別在X軸和Y軸上。
private const val X_COORDINATE = 0
private const val Y_COORDINATE = 1
假設我們有手指分別在5個向量上,任意手指的方向在任意時間可以被分類為上,下,下-上,上-下,不動。
enum class FingerDirection {
VECTOR_UP, VECTOR_DOWN, VECTOR_UP_DOWN, VECTOR_DOWN_UP, VECTOR_UNDEFINED
}
enum class Finger {
THUMB, FIRST_FINGER, MIDDLE_FINGER, RING_FINGER, LITTLE_FINGER
}
首先將對應的關鍵點從結果中分離到不同手指的關鍵點陣列,像這樣:
var firstFinger = arrayListOf<MLHandKeypoint>()
var middleFinger = arrayListOf<MLHandKeypoint>()
var ringFinger = arrayListOf<MLHandKeypoint>()
var littleFinger = arrayListOf<MLHandKeypoint>()
var thumb = arrayListOf<MLHandKeypoint>()
手指上的每個關鍵點都對應手指的關節,通過計算關節與手指的平均位置值之間的距離就可以計算出斜率。根據附近關鍵點的座標,查詢該關鍵點的座標。
拿字母H的兩個簡單關鍵點來說:
int[] datapointSampleH1 = {623, 497, 377, 312, 348, 234, 162, 90, 377, 204, 126, 54, 383, 306, 413, 491, 455, 348, 419, 521 };
int [] datapointSampleH2 = {595, 463, 374, 343, 368, 223, 147, 78, 381, 217, 110, 40, 412, 311, 444, 526, 450, 406, 488, 532};
用手指座標的平均值來計算向量
//For ForeFinger - 623, 497, 377, 312
double avgFingerPosition = (datapoints[0].getX()+datapoints[1].getX()+datapoints[2].getX()+datapoints[3].getX())/4;
// find the average and subract it from the value of x
double diff = datapointSampleH1 [position] .getX() - avgFingerPosition ;
//vector either positive or negative representing the direction
int vector = (int)((diff *100)/avgFingerPosition ) ;
向量的結果將會是正值或者負值,如果它是正值它會出現X軸的正四方向,如果相反它就是負值。用這個方式對所有字母進行向量對映,一旦你掌握了所有的向量我們就可以用它們來進行程式設計。
用上述向量方向,我們可以分類向量,定義第一個為手指方向列舉
private fun getSlope(keyPoints: MutableList<MLHandKeypoint>, coordinate: Int): FingerDirection {
when (coordinate) {
X_COORDINATE -> {
if (keyPoints[0].pointX > keyPoints[3].pointX && keyPoints[0].pointX > keyPoints[2].pointX)
return FingerDirection.VECTOR_DOWN
if (keyPoints[0].pointX > keyPoints[1].pointX && keyPoints[3].pointX > keyPoints[2].pointX)
return FingerDirection.VECTOR_DOWN_UP
if (keyPoints[0].pointX < keyPoints[1].pointX && keyPoints[3].pointX < keyPoints[2].pointX)
return FingerDirection.VECTOR_UP_DOWN
if (keyPoints[0].pointX < keyPoints[3].pointX && keyPoints[0].pointX < keyPoints[2].pointX)
return FingerDirection.VECTOR_UP
}
Y_COORDINATE -> {
if (keyPoints[0].pointY > keyPoints[1].pointY && keyPoints[2].pointY > keyPoints[1].pointY && keyPoints[3].pointY > keyPoints[2].pointY)
return FingerDirection.VECTOR_UP_DOWN
if (keyPoints[0].pointY > keyPoints[3].pointY && keyPoints[0].pointY > keyPoints[2].pointY)
return FingerDirection.VECTOR_UP
if (keyPoints[0].pointY < keyPoints[1].pointY && keyPoints[3].pointY < keyPoints[2].pointY)
return FingerDirection.VECTOR_DOWN_UP
if (keyPoints[0].pointY < keyPoints[3].pointY && keyPoints[0].pointY < keyPoints[2].pointY)
return FingerDirection.VECTOR_DOWN
}
}
return FingerDirection.VECTOR_UNDEFINED
獲取每個手指的方向並且儲存在一個陣列裡。
xDirections[Finger.FIRST_FINGER] = getSlope(firstFinger, X_COORDINATE)
yDirections[Finger.FIRST_FINGER] = getSlope(firstFinger, Y_COORDINATE )
2.7.2 從手指方向找到字元:
現在我們把它當作唯一的單詞“HELLO”,它需要字母H,E,L,O。它們對應的X軸和Y軸的向量如圖所示。
假設:手的方向總是豎向的。讓手掌和手腕與手機平行,也就是與X軸成90度。姿勢至少保持3秒用來記錄字元。
開始用字元對映向量來查詢字串
// Alphabet H
if (xDirections[Finger.LITTLE_FINGER] == FingerDirection.VECTOR_DOWN_UP
&& xDirections [Finger.RING_FINGER] == FingerDirection.VECTOR_DOWN_UP
&& xDirections [Finger.MIDDLE_FINGER] == FingerDirection.VECTOR_DOWN
&& xDirections [Finger.FIRST_FINGER] == FingerDirection.VECTOR_DOWN
&& xDirections [Finger.THUMB] == FingerDirection.VECTOR_DOWN)
return "H"
//Alphabet E
if (yDirections[Finger.LITTLE_FINGER] == FingerDirection.VECTOR_UP_DOWN
&& yDirections [Finger.RING_FINGER] == FingerDirection.VECTOR_UP_DOWN
&& yDirections [Finger.MIDDLE_FINGER] == FingerDirection.VECTOR_UP_DOWN
&& yDirections [Finger.FIRST_FINGER] == FingerDirection.VECTOR_UP_DOWN
&& xDirections [Finger.THUMB] == FingerDirection.VECTOR_DOWN)
return "E"
if (yDirections[Finger.LITTLE_FINGER] == FingerDirection.VECTOR_UP_DOWN
&& yDirections [Finger.RING_FINGER] == FingerDirection.VECTOR_UP_DOWN
&& yDirections [Finger.MIDDLE_FINGER] == FingerDirection.VECTOR_UP_DOWN
&& yDirections [Finger.FIRST_FINGER] == FingerDirection.VECTOR_UP
&& yDirections [Finger.THUMB] == FingerDirection.VECTOR_UP)
return "L"
if (xDirections[Finger.LITTLE_FINGER] == FingerDirection.VECTOR_UP
&& xDirections [Finger.RING_FINGER] == FingerDirection.VECTOR_UP
&& yDirections [Finger.THUMB] == FingerDirection.VECTOR_UP)
return "O"
3. 畫面和結果
4.更多技巧和訣竅
1. 當擴充套件到26個字母時,誤差很更多。為了更精準的掃描需要2-3秒,從2-3秒的時間尋找和計算最有可能的字元,這可以減少字母表的誤差。
2. 為了能支援所有方向,在X-Y軸上增加8個或者更多的方向。首先,需要求出手指的度數和對應的手指向量。
總結
這個嘗試是強力座標技術,它可以在生成向量對映後擴充套件到所有26個字母,方向也可以擴充套件所有8個方向,所以它會有26*8*5個手指=1040個向量。為了更好的解決這一問題,我們可以利用手指的一階導數函式來代替向量從而簡化計算。
我們可以增強其它的去代替建立向量,可以使用影像分類和訓練模型,然後使用自定義模型。這個訓練是為了檢查華為ML Kit使用關鍵點處理特性的可行性。
瞭解更多相關內容>>
訪問華為機器學習服務官網
獲取華為機器學習服務開發指導文件
華為HMS Core官方論壇
華為機器學習開源倉地址:GitHub、Gitee
解決整合問題請到Stack Overflow
點選關注,第一時間瞭解HMS Core最新技術~