集成华为手部关键点识别服务轻松识别手语字母

介绍

华为机器学习(ML Kit)提供手部关键点识别服务,可用于手语识别。手部关键点识别服务能识别手部21个关键点,通过每个手指的方向和手语规则作比较去找手语字母表。

应用场景

手语通常被听力和口语有障碍的人来使用,是收集手势包含日常互动中所使用的动作和手势。

使用ML Kit 可以建立一个智能手语字母表识别器,它可以像一个辅助器一样将手势翻译成单词或者句子,也可以将单词或者句子翻译成手势。

这里尝试的是手势当中的美国手语字母表,是基于关节,手指和手腕的位置进行分类。接下来小编将会尝试从手势中收集单词“HELLO”。

开发步骤

1. 准备

详细的准备步骤可以参考华为开发者联盟:

https://developer.huawei.com/consumer/cn/doc/development/HMS-Guides/ml-process-4

这里列举关键的开发步骤。

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 defaultmAnalyzer = MLHandKeypointAnalyzerFactory.getInstance().getHandKeypointAnalyzer(settings)
mAnalyzer.setTransactor(mHandKeyPointTransactor)
2.3 开发者创建识别结果处理类“HandKeypointTransactor”,该类MLAnalyzer.MLTransactor接口,使用此类中的“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 = foundCharacterdisplayText.append(lastCharacter)}canvas.drawText(displayText.toString(), paddingleft, paddingRight, Paint().also {it.style = Paint.Style.FILLit.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, 312double 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_DOWNif (keyPoints[0].pointX > keyPoints[1].pointX && keyPoints[3].pointX > keyPoints[2].pointX)return FingerDirection.VECTOR_DOWN_UPif (keyPoints[0].pointX < keyPoints[1].pointX && keyPoints[3].pointX < keyPoints[2].pointX)return FingerDirection.VECTOR_UP_DOWNif (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_DOWNif (keyPoints[0].pointY > keyPoints[3].pointY && keyPoints[0].pointY > keyPoints[2].pointY)return FingerDirection.VECTOR_UPif (keyPoints[0].pointY < keyPoints[1].pointY && keyPoints[3].pointY < keyPoints[2].pointY)return FingerDirection.VECTOR_DOWN_UPif (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轴的矢量如图所示。

假设:

  1. 手的方向总是竖向的。

  2. 让手掌和手腕与手机平行,也就是与X轴成90度。

  3. 姿势至少保持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个方向,所以它会有2685个手指=1040个矢量。为了更好的解决这一问题,我们可以利用手指的一阶导数函数来代替矢量从而简化计算。

我们可以增强其它的去代替创建矢量,可以使用图像分类和训练模型,然后使用自定义模型。这个训练是为了检查华为ML Kit使用关键点处理特性的可行性。

欲了解更多详情,请参阅:

华为开发者联盟官网:https://developer.huawei.com/consumer/cn/hms

获取开发指导文档:https://developer.huawei.com/consumer/cn/doc/development

参与开发者讨论请到Reddit社区:https://www.reddit.com/r/HMSCore/

下载demo和示例代码请到Github:https://github.com/HMS-Core

解决集成问题请到Stack Overflow:https://stackoverflow.com/questions/tagged/huawei-mobile-services?tab=Newest


原文链接:
https://developer.huawei.com/consumer/cn/forum/topic/0204423958265820665?fid=18
作者:timer

集成华为手部关键点识别服务轻松识别手语字母相关推荐

  1. 运营数据采集难?集成华为动态标签管理服务轻松搞定!

    在日常运营中,快速获取运营数据并发送到分析平台和归因平台,是营销者工作的重中之重.华为动态标签管理,通过配置规则或可视化埋码的方式,帮助运营及开发人员实现数据的快速获取和分发,提高工作效率.今天我们主 ...

  2. 快速集成华为AGC云存储服务-Web

    华为AppGallery Connect提供了一个云存储(CloudStorage)的服务,号称提供了一个便捷的云端存储服务,应用开发者使用的时候,可以不用关注服务器的部署,直接使用就行. 这个服务近 ...

  3. 超简单集成华为HMS Core MLKit通用卡证识别SDK,一键实现各种卡绑定

    标题前言 华为HMS MLKit提供的服务越来越多了,除常见的了银行卡识别的功能,大家可能还会疑问日常生活中的银行卡,会员卡可以识别吗,或者某一类证件号识别?也没有问题~但因为不同商家的卡号位置,版面 ...

  4. 在快应用中集成华为AGC云存储服务

    目前华为AGC云存储服务已经支持在快应用中集成了,你可以使用云存储服务存储图片.视频.音频等,集成的Demo可以参考Github. 1.安装Node.js环境: 1.下载Node.js安装包:http ...

  5. 推荐1款免费在线OCR文字识别服务,识别特别准确!

    这款在线文字识别转换有别于其他的软件,罕见地可以保留识别格式,如果你选择识别结果是PDF,那么格式将会是非常精确地保留下来,如果你选择识别结果是WORD,那么系统将会为你自动排版到最优格式,总之一句话 ...

  6. Unity | 快速集成华为AGC云存储服务

    华为AppGallery Connect提供了一个云存储(CloudStorage)的服务,号称提供了一个便捷的云端存储服务,应用开发者使用的时候,可以不用关注服务器的部署,直接使用就行. 目前这个功 ...

  7. 超简单集成华为HMS Core MLKit 机器学习服务银行卡识别SDK,一键实现银行卡绑定

    前言 小编前面几期文章分别给大家介绍了使用HMS ML Kit SDK实现微笑抓拍.证件照DIY.拍照翻译的功能开发(链接见文章末尾),本次小编给大家带来的是使用HMS 机器学习服务(ML Kit)银 ...

  8. 数字中国创新大赛决赛名单出炉,与华为云 EI 一起挑战书法识别

    数字中国创新大赛决赛名单出炉,与华为云 EI 一起挑战书法识别 日前,以"软件赋能数字经济 创新驱动数字中国"为主题的 2019数字中国创新大赛分区(北京)决赛成功举办.福州市人民 ...

  9. [实训题目EmoProfo]基于深度学习的表情识别服务搭建(一)

    基于深度学习的表情识别服务搭建(一) 文章目录 基于深度学习的表情识别服务搭建(一) 背景 识别服务设计 实现方式的选择 dlib性能验证 功能实现 小结 背景 之前我完成了终端和服务端之间交流的全部 ...

最新文章

  1. 购买云服务器,为什么要购买云服务器
  2. refineFace 笔记
  3. ASP.NET-GridView数据绑定的几种方法
  4. 将Ojective-C代码移植转换为Swift代码
  5. 一年有几个月几个季度_胎教几个月开始 注意事项有哪些?
  6. python简短语法_python 简单语法入门
  7. php-fpm通道,Go语言通道(chan)——goroutine之间通信的管道
  8. 2016年高通以50%的收益份额领跑基带芯片市场
  9. [转载] C++转JAVA的转换方法及约定
  10. 新华三(H3C)校园招聘技术类笔试题2019
  11. 最新数据库可视化工具DataGrip安装教程
  12. 移动安全-Frida hook安卓So层函数实战
  13. 2023年北京师范大学应用统计考研上岸前辈备考经验指导
  14. springboot+vue幼儿园管理系统(源码+文档)
  15. MySQL面试题和答案
  16. F28335 ePWM模块简介——TMS320F28335学习笔记(四)
  17. Android中的run-as命令引出升降权限的安全问题
  18. 微信公众号开发---机器人
  19. ZCMU 1074-1079
  20. 使用python库elasticsearch操作es

热门文章

  1. 晶振工作原理及参数详解(最透彻)
  2. C++ Primer(第5版) 课后答案 第四章
  3. 进程间通信 管道特性(无名管道演示)当向管道中读出数据的时候:
  4. Vue框架---Vue模板语法(二)
  5. 学习存储原理——地址线,存储字长,存储字
  6. 蓝桥杯51单片机(一)超声波模块
  7. 什么是TE,MT,ME
  8. hive中常见的日期函数
  9. 平安好医生与29家全球行业巨头达成全面战略合作
  10. 发现【Stable-Diffusion WEBUI】的神奇插件:离线自动翻译中文提示词