华为机器学习(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<T>接口,应用此类中的“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.4 创立LensEngine

LensEngine lensEngine = new LensEngine.Creator(getApplicationContext(), analyzer)setLensType(LensEngine.BACK_LENS)applyDisplayDimension(width, height) // adjust width and height depending on the orientationapplyFps(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 = 0private 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 xdouble diff = datapointSampleH1 [position] .getX() - avgFingerPosition ;//vector either positive or negative representing the directionint 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 Hif (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 Eif (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最新技术~