Skip to content

Commit 250430b

Browse files
authored
[handpose] Add model. (tensorflow#414)
1 parent 81f35ab commit 250430b

30 files changed

+9384
-1
lines changed

blazeface/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Blazeface detector
22

3-
[Blazeface](https://arxiv.org/abs/1907.05047) is a lightweight model that detects faces in images. Blazeface makes use of the [Single Shot Detector](https://arxiv.org/abs/1512.02325) architecture with a custom encoder. The model may serve as a first step for face-related computer vision applications, such as facial keypoint recognition.
3+
[Blazeface](https://arxiv.org/abs/1907.05047) is a lightweight model that detects faces in images. Blazeface makes use of a modified [Single Shot Detector](https://arxiv.org/abs/1512.02325) architecture with a custom encoder. The model may serve as a first step for face-related computer vision applications, such as facial keypoint recognition.
44

55
<img src="demo/demo.gif" alt="demo" style="width: 437px;"/>
66

handpose/.npmignore

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.DS_Store
2+
.yalc/
3+
.vscode/
4+
.rpt2_cache/
5+
demo/
6+
scripts/
7+
src/
8+
coverage/
9+
node_modules/
10+
karma.conf.js
11+
*.tgz
12+
.travis.yml
13+
.npmignore
14+
tslint.json
15+
yarn.lock
16+
yalc.lock
17+
cloudbuild.yml
18+
dist/*_test.js
19+
dist/*_test.js.map
20+
dist/test_util*

handpose/README.md

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# MediaPipe Handpose
2+
3+
MediaPipe Handpose is a lightweight ML pipeline consisting of two models: A palm detector and a hand-skeleton finger tracking model. It predicts 21 2D hand keypoints per detected hand. For more details, please read our Google AI [blogpost](https://ai.googleblog.com/2019/08/on-device-real-time-hand-tracking-with.html).
4+
5+
<img src="demo/demo.gif" alt="demo" style="width:640px" />
6+
7+
Given an input, the model predicts whether it contains a hand. If so, the model returns coordinates for the bounding box around the hand, as well as 21 keypoints within the hand, outlining the location of each finger joint and the palm.
8+
9+
More background information about the model, as well as its performance characteristics on different datasets, can be found here: [https://drive.google.com/file/d/1sv4sSb9BSNVZhLzxXJ0jBv9DqD-4jnAz/view](https://drive.google.com/file/d/1sv4sSb9BSNVZhLzxXJ0jBv9DqD-4jnAz/view)
10+
11+
Check out our [demo](https://storage.googleapis.com/tfjs-models/demos/handpose/index.html), which uses the model to detect hand landmarks in a live video stream.
12+
13+
This model is also available as part of [MediaPipe](https://hand.mediapipe.dev/), a framework for building multimodal applied ML pipelines.
14+
15+
# Performance
16+
17+
MediaPipe Handpose consists of ~12MB of weights, and is well-suited for real time inference across a variety of devices (40 FPS on a 2018 MacBook Pro, 35 FPS on an iPhone11, 6 FPS on a Pixel3).
18+
19+
## Installation
20+
21+
Using `yarn`:
22+
23+
$ yarn add @tensorflow-models/handpose
24+
25+
Using `npm`:
26+
27+
$ npm install @tensorflow-models/handpose
28+
29+
Note that this package specifies `@tensorflow/tfjs-core` and `@tensorflow/tfjs-converter` as peer dependencies, so they will also need to be installed.
30+
31+
## Usage
32+
33+
To import in npm:
34+
35+
```js
36+
import * as handpose from '@tensorflow-models/handpose';
37+
```
38+
39+
or as a standalone script tag:
40+
41+
```html
42+
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core"></script>
43+
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter"></script>
44+
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/handpose"></script>
45+
```
46+
47+
Then:
48+
49+
```js
50+
async function main() {
51+
// Load the MediaPipe handpose model.
52+
const model = await handpose.load();
53+
// Pass in a video stream (or an image, canvas, or 3D tensor) to obtain a
54+
// hand prediction from the MediaPipe graph.
55+
const predictions = await model.estimateHands(document.querySelector("video"));
56+
if (predictions.length > 0) {
57+
/*
58+
`predictions` is an array of objects describing each detected hand, for example:
59+
[
60+
{
61+
handInViewConfidence: 1, // The probability of a hand being present.
62+
boundingBox: { // The bounding box surrounding the hand.
63+
topLeft: [162.91, -17.42],
64+
bottomRight: [548.56, 368.23],
65+
},
66+
landmarks: [ // The 3D coordinates of each hand landmark.
67+
[472.52, 298.59, 0.00],
68+
[412.80, 315.64, -6.18],
69+
...
70+
],
71+
annotations: { // Semantic groupings of the `landmarks` coordinates.
72+
thumb: [
73+
[412.80, 315.64, -6.18]
74+
[350.02, 298.38, -7.14],
75+
...
76+
],
77+
...
78+
}
79+
}
80+
]
81+
*/
82+
83+
for (let i = 0; i < predictions.length; i++) {
84+
const keypoints = predictions[i].landmarks;
85+
86+
// Log hand keypoints.
87+
for (let i = 0; i < keypoints.length; i++) {
88+
const [x, y, z] = keypoints[i];
89+
console.log(`Keypoint ${i}: [${x}, ${y}, ${z}]`);
90+
}
91+
}
92+
}
93+
}
94+
main();
95+
```
96+
97+
#### Parameters for handpose.load()
98+
99+
`handpose.load()` takes a configuration object with the following properties:
100+
101+
* **maxContinuousChecks** - How many frames to go without running the bounding box detector. Defaults to infinity. Set to a lower value if you want a safety net in case the mesh detector produces consistently flawed predictions.
102+
103+
* **detectionConfidence** - Threshold for discarding a prediction. Defaults to 0.8.
104+
105+
* **iouThreshold** - A float representing the threshold for deciding whether boxes overlap too much in non-maximum suppression. Must be between [0, 1]. Defaults to 0.3.
106+
107+
* **scoreThreshold** - A threshold for deciding when to remove boxes based on score in non-maximum suppression. Defaults to 0.75.
108+
109+
#### Parameters for handpose.estimateHands()
110+
111+
* **input** - The image to classify. Can be a tensor, DOM element image, video, or canvas.
112+
113+
* **flipHorizontal** - Whether to flip/mirror the facial keypoints horizontally. Should be true for videos that are flipped by default (e.g. webcams).

handpose/cloudbuild.yml

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
steps:
3+
4+
# Install common dependencies.
5+
- name: 'node:10'
6+
id: 'yarn-common'
7+
entrypoint: 'yarn'
8+
args: ['install']
9+
10+
# Install handpose dependencies.
11+
- name: 'node:10'
12+
dir: 'handpose'
13+
entrypoint: 'yarn'
14+
id: 'yarn'
15+
args: ['install']
16+
waitFor: ['yarn-common']
17+
18+
# Lint.
19+
- name: 'node:10'
20+
dir: 'handpose'
21+
entrypoint: 'yarn'
22+
id: 'lint'
23+
args: ['lint']
24+
waitFor: ['yarn']
25+
26+
# Build.
27+
- name: 'node:10'
28+
dir: 'handpose'
29+
entrypoint: 'yarn'
30+
id: 'build'
31+
args: ['build']
32+
waitFor: ['yarn']
33+
34+
# Run tests.
35+
- name: 'node:10'
36+
dir: 'handpose'
37+
entrypoint: 'yarn'
38+
id: 'test'
39+
args: ['test']
40+
waitFor: ['yarn']
41+
42+
# General configuration
43+
timeout: 1800s
44+
logsBucket: 'gs://tfjs-build-logs'
45+
substitutions:
46+
_NIGHTLY: ''
47+
options:
48+
logStreamingOption: 'STREAM_ON'
49+
substitution_option: 'ALLOW_LOOSE'

handpose/demo/.babelrc

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"presets": [
3+
[
4+
"env",
5+
{
6+
"esmodules": false,
7+
"targets": {
8+
"browsers": [
9+
"> 3%"
10+
]
11+
}
12+
}
13+
]
14+
],
15+
"plugins": [
16+
[
17+
"transform-runtime",
18+
{
19+
"polyfill": false
20+
}
21+
]
22+
]
23+
}

handpose/demo/README.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Handpose demo
2+
3+
## Contents
4+
5+
This demo shows how to use the handpose model to detect hands in a video stream.
6+
7+
## Setup
8+
9+
cd into the demos folder:
10+
11+
```sh
12+
cd handpose/demos
13+
```
14+
15+
Install dependencies and prepare the build directory:
16+
17+
```sh
18+
yarn
19+
```
20+
21+
To watch files for changes, and launch a dev server:
22+
23+
```sh
24+
yarn watch
25+
```
26+
27+
## If you are developing handpose locally, and want to test the changes in the demos
28+
29+
Cd into the handpose folder:
30+
```sh
31+
cd handpose
32+
```
33+
34+
Install dependencies:
35+
```sh
36+
yarn
37+
```
38+
39+
Publish handpose locally:
40+
```sh
41+
yarn build && yarn yalc publish
42+
```
43+
44+
Cd into the demos and install dependencies:
45+
46+
```sh
47+
cd demos
48+
yarn
49+
```
50+
51+
Link the local handpose to the demos:
52+
```sh
53+
yarn yalc link @tensorflow-models/handpose
54+
```
55+
56+
Start the dev demo server:
57+
```sh
58+
yarn watch
59+
```
60+
61+
To get future updates from the handpose source code:
62+
```
63+
# cd up into the handpose directory
64+
cd ../
65+
yarn build && yarn yalc push
66+
```

handpose/demo/demo.gif

12.7 MB
Loading

handpose/demo/index.html

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<!-- Copyright 2020 Google LLC. All Rights Reserved.
2+
Licensed under the Apache License, Version 2.0 (the "License");
3+
you may not use this file except in compliance with the License.
4+
You may obtain a copy of the License at
5+
http://www.apache.org/licenses/LICENSE-2.0
6+
Unless required by applicable law or agreed to in writing, software
7+
distributed under the License is distributed on an "AS IS" BASIS,
8+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
See the License for the specific language governing permissions and
10+
limitations under the License.
11+
==============================================================================-->
12+
13+
<meta charset="utf-8">
14+
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1.0, user-scalable=no">
15+
<!-- Load three.js -->
16+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
17+
<!-- Load scatter-gl.js -->
18+
<!-- TODO(annxingyuan): Submit a PR to scatter-gl that allows polylines to update in the render loop. -->
19+
<script src="https://storage.googleapis.com/learnjs-data/handtrack_staging/scatter-gl.js"></script>
20+
<!-- <script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/scatter-gl.min.js"></script> -->
21+
<style>
22+
#canvas-wrapper {
23+
position: relative;
24+
}
25+
#canvas-wrapper, #scatter-gl-container {
26+
display: inline-block;
27+
vertical-align: top;
28+
}
29+
</style>
30+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/stats.min.js"></script>
31+
32+
<body>
33+
<div id="info" style='display:none'></div>
34+
<div id="predictions"></div>
35+
<div id="canvas-wrapper">
36+
<canvas id="output" style=""></canvas>
37+
<video id="video" playsinline style="
38+
-webkit-transform: scaleX(-1);
39+
transform: scaleX(-1);
40+
visibility: hidden;
41+
width: auto;
42+
height: auto;
43+
position: absolute;
44+
">
45+
</video>
46+
</div>
47+
<div id="scatter-gl-container"></div>
48+
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
49+
<script src="./index.js"></script>
50+
</body>

0 commit comments

Comments
 (0)