diff --git a/.babelrc b/.babelrc
index 2b7bafa..1bd836b 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,3 +1,8 @@
{
+ "plugins": [
+ ["transform-react-remove-prop-types", {
+ "removeImport": true
+ }]
+ ],
"presets": ["@babel/preset-env", "@babel/preset-react"]
-}
+}
\ No newline at end of file
diff --git a/.eslintrc.js b/.eslintrc.js
index e2020f8..27fac27 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -10,7 +10,7 @@ module.exports = {
jest: true,
browser: true
},
- plugins: ["jest", "promise", "prettier", "react", "react-hooks"],
+ plugins: ["jest", "markdown", "promise", "prettier", "react", "react-hooks"],
rules: {
"import/no-extraneous-dependencies": "off",
"no-alert": "error",
@@ -33,5 +33,14 @@ module.exports = {
"react/jsx-filename-extension": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
- }
+ },
+ overrides: [{
+ "files": ["**/*.md"],
+ "rules": {
+ "react/jsx-no-undef": "off",
+ "import/no-unresolved": "off",
+ "no-unused-expressions": "off",
+ "react/react-in-jsx-scope": "off"
+ }
+ }]
}
diff --git a/.github/workflows/github-pages.yaml b/.github/workflows/github-pages.yaml
new file mode 100644
index 0000000..d77b119
--- /dev/null
+++ b/.github/workflows/github-pages.yaml
@@ -0,0 +1,23 @@
+name: github pages
+
+on:
+ release:
+ types: [created]
+
+jobs:
+ build-deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: actions/setup-node@v1
+ with:
+ node-version: 12.x
+ - run: npm ci
+ - run: npm run docs:build
+ - run: echo "react-posenet.yoyota.dev" > ./dist-docs/CNAME
+ - name: deploy
+ uses: peaceiris/actions-gh-pages@v2
+ env:
+ PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
+ PUBLISH_BRANCH: gh-pages
+ PUBLISH_DIR: ./dist-docs
diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml
index af79b6e..89bc16d 100644
--- a/.github/workflows/publish.yaml
+++ b/.github/workflows/publish.yaml
@@ -7,7 +7,6 @@ jobs:
publish-npm:
runs-on: ubuntu-latest
steps:
- - run: echo $NPM_TOKEN
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
diff --git a/.gitignore b/.gitignore
index b173461..4c3ce05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,4 +23,5 @@ yarn-debug.log*
yarn-error.log*
# dist
-dist
\ No newline at end of file
+dist
+dist-docs
\ No newline at end of file
diff --git a/README.md b/README.md
index 3233842..4d8fb47 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,39 @@
# React PoseNet
React PoseNet is a handy wrapper component for [tfjs-models/posenet](https://github.com/tensorflow/tfjs-models/tree/master/posenet)
+
+## Documentation
+
+https://react-posenet.yoyota.dev
+
+## Example
+
+[pull up counter](https://github.com/yoyota/react-posenet-pull-up)
+
+
+## Installation
+
+```bash
+npm install --save react-posenet
+```
+
+### Usage
+
+```jsx
+import PoseNet from "react-posenet"
+
+export default function App() {
+ return
+}
+```
+
+## Core Feautres
+
+### [onEstimate](https://react-posenet.yoyota.dev/#/Props%20examples?id=section-onestimate)
+
+gets called after estimation. [poses](https://github.com/tensorflow/tfjs-models/tree/master/posenet#keypoints) is a passed parameter
+
+### [input](https://react-posenet.yoyota.dev/#/Props%20examples?id=section-input)
+the input image to feed through the network. see
+[tfjs-posenet document](https://github.com/tensorflow/tfjs-models/tree/master/posenet#params-in-estimatesinglepose)
+If input is not specified react-posenet try to [getUserMedia](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)
\ No newline at end of file
diff --git a/docs/Documentation.md b/docs/Documentation.md
new file mode 100644
index 0000000..4507fd7
--- /dev/null
+++ b/docs/Documentation.md
@@ -0,0 +1,9 @@
+- [Github repository](https://github.com/yoyota/react-posenet)
+
+React PoseNet is a handy wrapper component for [tfjs-models/posenet](https://github.com/tensorflow/tfjs-models/tree/master/posenet)
+
+## Installation
+
+```bash
+npm install --save react-posenet
+```
diff --git a/docs/input.md b/docs/input.md
new file mode 100644
index 0000000..6a2893a
--- /dev/null
+++ b/docs/input.md
@@ -0,0 +1,13 @@
+```jsx
+import { useMemo } from "react"
+import PoseNet from "react-posenet"
+
+const input = useMemo(() => {
+ const image = new Image()
+ image.crossOrigin = ""
+ image.src = "https://i.imgur.com/oV2ZNuD.jpg"
+ return image
+}, [])
+
+;
+```
diff --git a/docs/onEstimate.md b/docs/onEstimate.md
new file mode 100644
index 0000000..0d7981a
--- /dev/null
+++ b/docs/onEstimate.md
@@ -0,0 +1,19 @@
+If your device dose not have camera, you will see error message like
+Requested device not found
+
+```jsx
+import { useState } from "react"
+import PoseNet from "react-posenet"
+
+const [posesString, setPosesString] = useState([])
+
+;<>
+ {
+ setPosesString(JSON.stringify(poses))
+ }}
+ />
+ {posesString}
+>
+```
diff --git a/docs/test.md b/docs/test.md
new file mode 100644
index 0000000..d89db1d
--- /dev/null
+++ b/docs/test.md
@@ -0,0 +1 @@
+# Test3
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 54d1d73..3300fee 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "react-posenet",
- "version": "0.0.0-alpha.1",
+ "version": "0.0.0-alpha.9",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -2140,6 +2140,12 @@
"@types/babel__traverse": "^7.0.6"
}
},
+ "babel-plugin-transform-react-remove-prop-types": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz",
+ "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==",
+ "dev": true
+ },
"babel-preset-jest": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz",
@@ -4165,6 +4171,89 @@
}
}
},
+ "eslint-plugin-markdown": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-markdown/-/eslint-plugin-markdown-1.0.1.tgz",
+ "integrity": "sha512-nAUURNHJGPooBMZMP23FmTbh3LTdgoSqeFBv9FA3fYrJ+vDUJxrp6nKiQF4iDNAmnWQnmnrDvV61BmIF4X9QAQ==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.0.1",
+ "remark-parse": "^5.0.0",
+ "unified": "^6.1.2"
+ },
+ "dependencies": {
+ "is-plain-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+ "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
+ "dev": true
+ },
+ "remark-parse": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz",
+ "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==",
+ "dev": true,
+ "requires": {
+ "collapse-white-space": "^1.0.2",
+ "is-alphabetical": "^1.0.0",
+ "is-decimal": "^1.0.0",
+ "is-whitespace-character": "^1.0.0",
+ "is-word-character": "^1.0.0",
+ "markdown-escapes": "^1.0.0",
+ "parse-entities": "^1.1.0",
+ "repeat-string": "^1.5.4",
+ "state-toggle": "^1.0.0",
+ "trim": "0.0.1",
+ "trim-trailing-lines": "^1.0.0",
+ "unherit": "^1.0.4",
+ "unist-util-remove-position": "^1.0.0",
+ "vfile-location": "^2.0.0",
+ "xtend": "^4.0.1"
+ }
+ },
+ "unified": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz",
+ "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==",
+ "dev": true,
+ "requires": {
+ "bail": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^1.1.0",
+ "trough": "^1.0.0",
+ "vfile": "^2.0.0",
+ "x-is-string": "^0.1.0"
+ }
+ },
+ "unist-util-stringify-position": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz",
+ "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==",
+ "dev": true
+ },
+ "vfile": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz",
+ "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.4",
+ "replace-ext": "1.0.0",
+ "unist-util-stringify-position": "^1.0.0",
+ "vfile-message": "^1.0.0"
+ }
+ },
+ "vfile-message": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz",
+ "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==",
+ "dev": true,
+ "requires": {
+ "unist-util-stringify-position": "^1.1.1"
+ }
+ }
+ }
+ },
"eslint-plugin-prettier": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.1.tgz",
@@ -9674,8 +9763,7 @@
"regenerator-runtime": {
"version": "0.13.3",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
- "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
- "dev": true
+ "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
},
"regenerator-transform": {
"version": "0.14.1",
@@ -12492,6 +12580,12 @@
"async-limiter": "~1.0.0"
}
},
+ "x-is-string": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz",
+ "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=",
+ "dev": true
+ },
"xml-name-validator": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
diff --git a/package.json b/package.json
index 6e83d7d..024d4e5 100644
--- a/package.json
+++ b/package.json
@@ -4,9 +4,9 @@
"main": "dist/index.js",
"scripts": {
"build": "rollup --config",
- "lint": "eslint --fix-dry-run src",
+ "lint": "eslint --ext md --ext js --fix-dry-run src docs",
"start": "styleguidist server",
- "styleguide:build": "styleguidist build"
+ "docs:build": "styleguidist build"
},
"repository": {
"type": "git",
@@ -22,23 +22,27 @@
"@tensorflow-models/posenet": "^2.2.1",
"@tensorflow/tfjs": "^1.3.2",
"@tensorflow/tfjs-core": "^1.4.0",
- "await-to-js": "^2.1.1"
+ "await-to-js": "^2.1.1",
+ "regenerator-runtime": "^0.13.3"
},
"peerDependencies": {
"react": "^16.12.0",
"react-dom": "^16.12.0"
},
"devDependencies": {
+ "prop-types": "^15.7.2",
"@babel/core": "^7.7.5",
"@babel/preset-env": "^7.7.5",
"@babel/preset-react": "^7.7.4",
"babel-loader": "^8.0.6",
+ "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"eslint": "^6.7.2",
"eslint-config-airbnb": "^17.1.1",
"eslint-config-prettier": "^6.7.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jest": "^22.21.0",
"eslint-plugin-jsx-a11y": "^6.2.3",
+ "eslint-plugin-markdown": "^1.0.1",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-react": "^7.17.0",
@@ -55,4 +59,4 @@
"rollup-plugin-terser": "^5.1.3",
"webpack": "^4.41.2"
}
-}
+}
\ No newline at end of file
diff --git a/rollup.config.js b/rollup.config.js
index 15a3454..c600f3b 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -19,11 +19,11 @@ export default [
},
external: ["react", "react-dom"],
plugins: [
+ resolve(),
babel({
exclude: "node_modules/**"
}),
external(),
- resolve(),
terser()
]
}
diff --git a/src/components/PoseNet.js b/src/components/PoseNet.js
index c618359..75bf055 100644
--- a/src/components/PoseNet.js
+++ b/src/components/PoseNet.js
@@ -1,4 +1,5 @@
import React, { useRef, useState, useEffect } from "react"
+import PropTypes from "prop-types"
import to from "await-to-js"
import Loading from "./Loading"
import useLoadPoseNet from "../hooks/useLoadPoseNet"
@@ -10,18 +11,18 @@ import {
} from "../util"
export default function PoseNet({
- id = "",
- className = "",
- facingMode = "user",
- frameRate = 20,
- input = undefined,
- onEstimate = () => {},
- inferenceConfig = {},
- modelConfig = {},
- minPoseConfidence = 0.1,
- minPartConfidence = 0.5,
- width = 600,
- height = 500
+ id,
+ className,
+ facingMode,
+ frameRate,
+ input,
+ onEstimate,
+ inferenceConfig,
+ modelConfig,
+ minPoseConfidence,
+ minPartConfidence,
+ width,
+ height
}) {
const videoRef = useRef()
const canvasRef = useRef()
@@ -127,3 +128,72 @@ export default function PoseNet({
>
)
}
+
+PoseNet.propTypes = {
+ /** canvas id */
+ id: PropTypes.string,
+ /** canvas className */
+ className: PropTypes.string,
+ /** @see https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/facingMode */
+ facingMode: PropTypes.string,
+ /** First of all frameRate is parameter of [getUserMedia()](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)
+ * see [MediaTrackConstraints.frameRate](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/frameRate)
+ *
+ * second frameRate affects how often estimation occurs. react-posenet internally
+ * [setInterval](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval)(() => { estimatePose() } , (1000 / framerate))
+ * to estimate image continuously */
+ frameRate: PropTypes.number,
+ /**
+ * the input image to feed through the network.
+ * If input is not specified react-posenet try to [getUserMedia](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)
+ * @see [tfjs-posenet document](https://github.com/tensorflow/tfjs-models/tree/master/posenet#params-in-estimatesinglepose)
+ */
+ input: PropTypes.element,
+ /**
+ * gets called after estimation. [poses](https://github.com/tensorflow/tfjs-models/tree/master/posenet#keypoints) is a passed parameter
+ */
+ onEstimate: PropTypes.func,
+ /**
+ * If you want swtich between single / multi pose estimation.
+ * use decodingMethod [please check this code](https://github.com/tensorflow/tfjs-models/blob/master/posenet/demos/camera.js#L392)
+ * {decodingMethod: "single-person"} / {decodingMethod: "multi-person"}
+ * @see [tfjs-posenet documentation](https://github.com/tensorflow/tfjs-models/tree/master/posenet#params-in-estimatemultipleposes)
+ */
+ inferenceConfig: PropTypes.shape({
+ decodingMethod: PropTypes.string,
+ flipHorizontal: PropTypes.bool,
+ maxDetections: PropTypes.number,
+ scoreThreshold: PropTypes.number,
+ nmsRadius: PropTypes.number
+ }),
+ /** @see [tfjs-posenet documentation](https://github.com/tensorflow/tfjs-models/tree/master/posenet#config-params-in-posenetload) */
+ modelConfig: PropTypes.shape({
+ architecture: PropTypes.string,
+ outputStride: PropTypes.number,
+ inputResolution: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
+ quantBytes: PropTypes.number
+ }),
+ /** minimum confidence constraint for pose */
+ minPoseConfidence: PropTypes.number,
+ /** minimum confidence constraint for each [part](https://github.com/tensorflow/tfjs-models/tree/master/posenet#keypoints) */
+ minPartConfidence: PropTypes.number,
+ /** canvas width */
+ width: PropTypes.number,
+ /** canvas height */
+ height: PropTypes.number
+}
+
+PoseNet.defaultProps = {
+ id: "",
+ className: "",
+ facingMode: "user",
+ frameRate: 20,
+ input: undefined,
+ onEstimate: () => {},
+ inferenceConfig: {},
+ modelConfig: {},
+ minPoseConfidence: 0.1,
+ minPartConfidence: 0.5,
+ width: 600,
+ height: 500
+}
diff --git a/src/components/PoseNet.md b/src/components/PoseNet.md
index ce07dfd..4c00280 100644
--- a/src/components/PoseNet.md
+++ b/src/components/PoseNet.md
@@ -1,40 +1,6 @@
-#### PoseNet
+If your device dose not have camera, you will see error message like
+Requested device not found
```jsx
-import { useState } from "react";
-import PoseNet from "react-posenet";
-
-const [posesString, setPosesString] = useState([]);
-
-<>
- {
- setPosesString(JSON.stringify(poses));
- }}
- />
- {posesString}
->;
+
```
-
-
diff --git a/src/index.js b/src/index.js
index b4b67c2..fde304c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,3 +1,4 @@
+import "regenerator-runtime/runtime"
import PoseNet from "./components/PoseNet"
export default PoseNet
diff --git a/styleguide.config.js b/styleguide.config.js
index 85ed3f6..5f24e1c 100644
--- a/styleguide.config.js
+++ b/styleguide.config.js
@@ -1,7 +1,31 @@
const path = require("path")
module.exports = {
- components: ["src/components/PoseNet.js"],
+ title: "PoseNet React",
+ pagePerSection: true,
+ sections: [
+ {
+ name: "Documentation",
+ content: "docs/Documentation.md",
+ components: () => ["./src/components/PoseNet.js"]
+ },
+ {
+ name: "Props examples",
+ sections: [
+ {
+ name: "input",
+ content: "docs/input.md",
+ exampleMode: "expand"
+ },
+ {
+ name: "onEstimate",
+ content: "docs/onEstimate.md",
+ exampleMode: "expand"
+ }
+ ],
+ sectionDepth: 0
+ }
+ ],
webpackConfig: {
module: {
rules: [
@@ -13,7 +37,6 @@ module.exports = {
]
}
},
- title: "PoseNet React",
styleguideDir: "dist-docs",
moduleAliases: {
"react-posenet": path.resolve(__dirname, "src")