# HandKeypoint **Repository Path**: MakerStudio_OH/handkeypoint ## Basic Information - **Project Name**: HandKeypoint - **Description**: 手部关节点识别库可精准检测手部21个关节点(包括每个手指指尖、关节点,以及手腕点)的位置坐标,具体应用主要集中在人机交互,虚拟现实,人体动画,智能家居,智能安防,运动员辅助训练等多种场景下。 - **Primary Language**: TypeScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2025-05-12 - **Last Updated**: 2025-08-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: HarmonyOS, 鸿蒙 ## README # HandKeyPoint ### 简介 手部关节点识别库可精准检测手部21个关节点(包括每个手指指尖、关节点,以及手腕点)的位置坐标,具体应用主要集中在人机交互,虚拟现实,人体动画,智能家居,智能安防,运动员辅助训练等多种场景下。 ### 场景说明 1. 集成了手部关键点识别能力后,在进行艺术创作时,可以将检测到的手部关键点转换为2D模型,并将生成的手部模型同步到人物模型中。 2. 开发者通过手部骨骼关节点编辑手部调用指令,无需手动操作即可实现对智能家电的控制,提高人机交互的效率。 3. ...... ### 注意事项 | 能力 | 手部遮挡范围 | 佩戴手套 | |:-------:|:-------:|:----:| | 手部关节点识别 | 不超过50%✅ | 不支持 | ### 🔨使用 #### 📦快速接入 ``` # ohpm ohpm install @maker/handkeypoint ``` #### API说明 ##### 👉getKeyPoint:获取关节点识别输出 `getKeyPoint(): Promise` `import { getKeyPoint } from '@maker/handkeypoint';` **返回值** | 类型 | 说明 | |:----------------|:--------------------| | Promise | Promise对象,返回关节点识别结果 | ##### ⚡output:关节点识别输出定义类 `import { output } from '@maker/handkeypoint';` | 名称 | 类型 | 说明 | |:-------|:-----------|:-----------------------| | uri | string | 所选图片uri | | origin | string[][] | 关节点识别模型原始输出 | | out | KeyPoint | 手部21个关节点输出 | |message | string | 手部检测输出信息,“手部关节点捕捉成功”/“未检测到手部,请重新尝试”| ##### 💡KeyPoint:手部21个关节点定义类 `import { KeyPoint } from '@maker/handkeypoint';` | 名称 | 类型 | 值 | 说明 | |:------------------|:------------|:---|:----------| | WRIST | Coordinates | 0 | 手腕 | | THUMB_CMC | Coordinates | 1 | 拇指腕掌关节 | | THUMB_MCP | Coordinates | 2 | 拇指掌指关节 | | THUMB_IP | Coordinates | 3 | 拇指指间关节 | | THUMB_TIP | Coordinates | 4 | 拇指尖端 | | INDEX_FINGER_MCP | Coordinates | 5 | 食指掌指关节 | | INDEX_FINGER_PIP | Coordinates | 6 | 食指近端指间关节 | | INDEX_FINGER_DIP | Coordinates | 7 | 食指远端指间关节 | | INDEX_FINGER_TIP | Coordinates | 8 | 食指尖端 | | MIDDLE_FINGER_MCP | Coordinates | 9 | 中指掌指关节 | | MIDDLE_FINGER_PIP | Coordinates | 10 | 中指近端指间关节 | | MIDDLE_FINGER_DIP | Coordinates | 11 | 中指远端指间关节 | | MIDDLE_FINGER_TIP | Coordinates | 12 | 中指尖端 | | RING_FINGER_MCP | Coordinates | 13 | 无名指掌指关节 | | RING_FINGER_PIP | Coordinates | 14 | 无名指近端指间关节 | | RING_FINGER_DIP | Coordinates | 15 | 无名指远端指间关节 | | RING_FINGER_TIP | Coordinates | 16 | 无名指尖端 | | PINKY_MCP | Coordinates | 17 | 小指掌指关节 | | PINKY_PIP | Coordinates | 18 | 小指近端指间关节 | | PINKY_DIP | Coordinates | 19 | 小指远端指间关节 | | PINKY_TIP | Coordinates | 20 | 小指尖端 | ##### 🌟Coordinates:x,y坐标定义类 `import { Coordinates } from '@maker/handkeypoint';` | 名称 | 类型 | 说明 | |:---|:-------|:----| | x | number | x坐标 | | y | number | y坐标 | ##### 📈DrawCanvas:根据关节点识别输出绘制手部骨骼关节点图 `import { DrawCanvas } from '@maker/handkeypoint';` | 名称 | 类型 | 说明 | |:--------|:-------------------------|:---------------------------------------| |settings| RenderingContextSettings | 用来配置CanvasRenderingContext2D对象的参数(非必传) | |context| CanvasRenderingContext2D | CanvasRenderingContext2D对象(非必传) | |pairs| string[][] | 手部21个关节点原始输出(必传) | **连接关系图** **使用示例** ```c DrawCanvas({ pairs: this.pairs, context: this.context, settings: this.settings }) ``` ### 🚀示例一 手部关节点识别骨骼绘制展示 ```c import { KeyPoint,getKeyPoint,DrawCanvas } from '@maker/handkeypoint'; @Entry @Component struct Page1 { @State keypoint: Array = []; @State message: string = ''; @State pairs:string[][]=[] @State uri:string = '' private settings: RenderingContextSettings = new RenderingContextSettings(true) private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) build() { Column({space:10}){ if(this.message=='手部关节点捕捉成功') { Row() { Image(this.uri) .width(150) .height(200) DrawCanvas({ context: this.context, settings: this.settings, pairs: this.pairs }) } .padding(3) } else { Text(this.message) } Button('手部关节点识别') .onClick(async ()=>{ this.pairs = [] // 保证上次推理结果清空 let out = await getKeyPoint() this.pairs = out.origin this.uri = out.uri this.keypoint[0] = out.out this.message = out.message }) .margin({top: 20}) } .height('100%') .width('100%') } } ``` #### 🏆效果展示 ### 🚀示例二 手部21个关节点输出展示 ```c import { getKeyPoint, KeyPoint } from '@maker/handkeypoint' @Entry @Component struct Page2 { @State keypoint: Array = []; @State message: string = ''; @State pairs:string[][]=[] @State uri:string = '' build() { Column({space:10}){ if(this.message=='手部关节点捕捉成功') { List() { ListItem(){ Column({space:5}) { Text('wrist点坐标:' +'('+ (this.keypoint[0].wrist.x) + ',' + (this.keypoint[0].wrist.y)+')') Text('thumbCmc点坐标:' +'('+ (this.keypoint[0].thumbCmc.x) + ',' + (this.keypoint[0].thumbCmc.y)+')') Text('thumbMcp点坐标:' +'('+ (this.keypoint[0].thumbMcp.x) + ',' + (this.keypoint[0].thumbMcp.y)+')') Text('thumbIp点坐标:' +'('+ (this.keypoint[0].thumbIp.x) + ',' + (this.keypoint[0].thumbIp.y)+')') Text('thumbTip点坐标:' +'('+ (this.keypoint[0].thumbTip.x) + ',' + (this.keypoint[0].thumbTip.y)+')') Text('indexFingerMcp点坐标:' +'('+ (this.keypoint[0].indexFingerMcp.x) + ',' + (this.keypoint[0].indexFingerMcp.y)+')') Text('indexFingerPip点坐标:' +'('+ (this.keypoint[0].indexFingerPip.x) + ',' + (this.keypoint[0].indexFingerPip.y)+')') Text('indexFingerDip点坐标:' +'('+ (this.keypoint[0].indexFingerDip.x) + ',' + (this.keypoint[0].indexFingerDip.y)+')') Text('indexFingerTip点坐标:' +'('+ (this.keypoint[0].indexFingerTip.x) + ',' + (this.keypoint[0].indexFingerTip.y)+')') Text('middleFingerMcp点坐标:' +'('+ (this.keypoint[0].middleFingerMcp.x) + ',' + (this.keypoint[0].middleFingerMcp.y)+')') Text('middleFingerPip点坐标:' +'('+ (this.keypoint[0].middleFingerPip.x) + ',' + (this.keypoint[0].middleFingerPip.y)+')') Text('middleFingerDip点坐标:' +'('+ (this.keypoint[0].middleFingerDip.x) + ',' + (this.keypoint[0].middleFingerDip.y)+')') Text('middleFingerTip点坐标:' +'('+ (this.keypoint[0].middleFingerTip.x) + ',' + (this.keypoint[0].middleFingerTip.y)+')') Text('ringFingerMcp点坐标:' +'('+ (this.keypoint[0].ringFingerMcp.x) + ',' + (this.keypoint[0].ringFingerMcp.y)+')') Text('ringFingerPip点坐标:' +'('+ (this.keypoint[0].ringFingerPip.x) + ',' + (this.keypoint[0].ringFingerPip.y)+')') Text('ringFingerDip点坐标:' +'('+ (this.keypoint[0].ringFingerDip.x) + ',' + (this.keypoint[0].ringFingerDip.y)+')') Text('ringFingerTip点坐标:' + '('+(this.keypoint[0].ringFingerTip.x) + ',' + (this.keypoint[0].ringFingerTip.y)+')') Text('pinkyMcp点坐标:' +'('+ (this.keypoint[0].pinkyMcp.x) + ',' + (this.keypoint[0].pinkyMcp.y)+')') Text('pinkyPip点坐标:' +'('+ (this.keypoint[0].pinkyPip.x) + ',' + (this.keypoint[0].pinkyPip.y)+')') Text('pinkyDip点坐标:' +'('+(this.keypoint[0].pinkyDip.x) + ',' + (this.keypoint[0].pinkyDip.y)+')') Text('pinkyTip点坐标:' +'('+(this.keypoint[0].pinkyTip.x) + ',' + (this.keypoint[0].pinkyTip.y)+')') } .alignItems(HorizontalAlign.Start) } } .scrollBar(BarState.Off) .width('88%') .height('83%') .alignListItem(ListItemAlign.Start) } else { Text(this.message) } Button('手部关节点识别') .onClick(async ()=>{ this.pairs = [] let out = await getKeyPoint() this.pairs = out.origin this.uri = out.uri this.keypoint[0] = out.out this.message = out.message console.log("message is:"+this.message) }) .margin({top: 20}) } .height('100%') .width('100%') } } ``` #### 🏆效果展示