前書き
Huawei Machine Learning(ML Kit)は、サイン言語認識に使用できるハンドキーポイント認識サービスを提供します。手のキーポイント認識サービスは、手の21のキーポイントを識別し、各指の方向をサイン言語のルールと比較することによってサイン言語のアルファベットを見つけることができます。
アプリケーションシナリオ
サイン言語は通常、聴覚障害や会話障害のある人が使用します。これは、日常のやり取りで使用される動きやジェスチャーを含むジェスチャーのコレクションです。
ML Kitを使用して、ジェスチャーを援助のように単語や文に変換したり、単語や文をジェスチャーに変換したりできるスマートサイン言語アルファベット認識機能を構築します。
ここで試したのは、関節、指、手首の位置に基づいて分類された、ジェスチャーのアメリカンサインランゲージアルファベットです。次に、編集者はジェスチャーから「HELLO」という単語を収集しようとします。
開発ステップ
1.準備
詳細な準備手順については、Huawei DeveloperAllianceを参照してください。
https://developer.huawei.com/consumer/cn/doc/development/HMS-Guides/ml-process-4
主な開発手順は次のとおりです。
1.1MLキットを起動します
Huawei Developer AppGallery Connectで、[開発]> [APIの管理]を選択します。MLキットがアクティブになっていることを確認します。
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.4AndroidManifest.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カメラプレビュー用のサーフェスビューを作成し、結果用のサーフェスビューを作成します。
現在、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開発者は、このクラスのMLAnalyzer.MLTransactor <T>インターフェイスである認識結果処理クラス「HandKeypointTransactor」を作成し、このクラスの「transactResult」メソッドを使用して検出結果を取得し、特定のサービスを実装します。
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.4LensEngineを作成する
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.5LensEngineを実行する
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の2つの簡単な要点を取り上げます
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軸の正の4方向に表示され、反対の場合は負になります。このようにして、すべての文字がベクトルマッピングされます。すべてのベクトルをマスターしたら、それらを使用してプログラミングできます。
上記のベクトル方向を使用して、ベクトルを分類し、最初のベクトルを指の方向の列挙として定義できます。
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.その他のヒントとコツ
-
26文字に拡張すると、エラーはさらに大きくなります。より正確にスキャンするために、2〜3秒から最も可能性の高い文字を見つけて計算するのに2〜3秒かかります。これにより、アルファベットのエラーを減らすことができます。
- すべての方向をサポートするには、XY軸に8つ以上の方向を追加します。まず、指の角度と対応する指のベクトルが必要です。
総括する
この試みは、ベクトルマップを生成した後、すべての26文字に拡張することができる座標強力な技術であり、それがなければなりませんので、方向も、すべての8つの方向に拡張することができる26 8 5本の指= 1040のベクター。この問題をよりよく解決するために、指の一次微分関数を使用してベクトルを置き換え、計算を簡略化することができます。
ベクトルを作成する代わりに、他のベクトルを拡張し、画像分類とトレーニングモデルを使用して、カスタムモデルを使用できます。このトレーニングは、HuaweiMLキットのキーポイント処理機能の使用の実現可能性を確認するためのものです。
詳細については、以下を参照してください。
Huawei Developer Allianceの公式ウェブサイト:https://developer.huawei.com/consumer/cn/hms
開発ガイダンス文書を入手する:https://developer.huawei.com/consumer/cn/doc/development
開発者のディスカッションに参加するには、Redditコミュニティにアクセスしてください。https://www.reddit.com/r/HMSCore/
デモとサンプルコードをダウンロードするには、Githubにアクセスしてください。https://github.com/HMS-Core
統合の問題を解決するには、StackOverflowにアクセスしてください。https://stackoverflow.com/questions/tagged/huawei-mobile-services?tab=Newest
元のリンク:
https://developer.huawei.com/consumer/cn/forum/topic/0204423958265820665?fid=18
作成者:timer