mirror of https://github.com/vladmandic/human
fix safari incopatibility
parent
1d5a73f16b
commit
01aeb1cd30
|
@ -144,14 +144,14 @@ export default {
|
|||
modelType: 'MobileNet', // Human includes MobileNet version, but you can switch to ResNet
|
||||
},
|
||||
|
||||
pose: {
|
||||
pose: { // TBD: not currently in use
|
||||
enabled: false,
|
||||
scoreThreshold: 0.6, // threshold for deciding when to remove boxes based on score
|
||||
// in non-maximum suppression
|
||||
iouThreshold: 0.3, // threshold for deciding whether boxes overlap too much
|
||||
// in non-maximum suppression
|
||||
modelPath: '../models/blazepose.json',
|
||||
inputSize: 128, // fixed value
|
||||
inputSize: 256, // fixed value
|
||||
},
|
||||
|
||||
hand: {
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"inputs": {
|
||||
"dist/human.esm.js": {
|
||||
"bytes": 1912580,
|
||||
"bytes": 1907681,
|
||||
"imports": []
|
||||
},
|
||||
"demo/draw.js": {
|
||||
|
@ -38,14 +38,14 @@
|
|||
"dist/demo-browser-index.js.map": {
|
||||
"imports": [],
|
||||
"inputs": {},
|
||||
"bytes": 2040931
|
||||
"bytes": 2030116
|
||||
},
|
||||
"dist/demo-browser-index.js": {
|
||||
"imports": [],
|
||||
"exports": [],
|
||||
"inputs": {
|
||||
"dist/human.esm.js": {
|
||||
"bytesInOutput": 1905205
|
||||
"bytesInOutput": 1900306
|
||||
},
|
||||
"demo/draw.js": {
|
||||
"bytesInOutput": 7736
|
||||
|
@ -60,7 +60,7 @@
|
|||
"bytesInOutput": 19424
|
||||
}
|
||||
},
|
||||
"bytes": 1958909
|
||||
"bytes": 1954010
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -264,7 +264,7 @@
|
|||
]
|
||||
},
|
||||
"src/blazepose/blazepose.js": {
|
||||
"bytes": 8999,
|
||||
"bytes": 856,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/log.js"
|
||||
|
@ -361,7 +361,7 @@
|
|||
]
|
||||
},
|
||||
"config.js": {
|
||||
"bytes": 10062,
|
||||
"bytes": 10110,
|
||||
"imports": []
|
||||
},
|
||||
"src/sample.js": {
|
||||
|
@ -373,7 +373,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytes": 17787,
|
||||
"bytes": 18921,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/log.js"
|
||||
|
@ -433,7 +433,7 @@
|
|||
"dist/human.esm.js.map": {
|
||||
"imports": [],
|
||||
"inputs": {},
|
||||
"bytes": 1945558
|
||||
"bytes": 1934763
|
||||
},
|
||||
"dist/human.esm.js": {
|
||||
"imports": [],
|
||||
|
@ -442,7 +442,7 @@
|
|||
],
|
||||
"inputs": {
|
||||
"src/blazeface/blazeface.js": {
|
||||
"bytesInOutput": 5046
|
||||
"bytesInOutput": 5039
|
||||
},
|
||||
"src/blazeface/box.js": {
|
||||
"bytesInOutput": 1567
|
||||
|
@ -517,7 +517,7 @@
|
|||
"bytesInOutput": 127037
|
||||
},
|
||||
"src/handpose/handpose.js": {
|
||||
"bytesInOutput": 2025
|
||||
"bytesInOutput": 2023
|
||||
},
|
||||
"src/gesture/gesture.js": {
|
||||
"bytesInOutput": 3162
|
||||
|
@ -538,10 +538,10 @@
|
|||
"bytesInOutput": 918
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytesInOutput": 11921
|
||||
"bytesInOutput": 12660
|
||||
},
|
||||
"src/blazepose/blazepose.js": {
|
||||
"bytesInOutput": 6201
|
||||
"bytesInOutput": 572
|
||||
},
|
||||
"src/handpose/box.js": {
|
||||
"bytesInOutput": 1420
|
||||
|
@ -559,7 +559,7 @@
|
|||
"bytesInOutput": 23
|
||||
}
|
||||
},
|
||||
"bytes": 1912580
|
||||
"bytes": 1907681
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -264,7 +264,7 @@
|
|||
]
|
||||
},
|
||||
"src/blazepose/blazepose.js": {
|
||||
"bytes": 8999,
|
||||
"bytes": 856,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/log.js"
|
||||
|
@ -361,7 +361,7 @@
|
|||
]
|
||||
},
|
||||
"config.js": {
|
||||
"bytes": 10062,
|
||||
"bytes": 10110,
|
||||
"imports": []
|
||||
},
|
||||
"src/sample.js": {
|
||||
|
@ -373,7 +373,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytes": 17787,
|
||||
"bytes": 18921,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/log.js"
|
||||
|
@ -433,14 +433,14 @@
|
|||
"dist/human.js.map": {
|
||||
"imports": [],
|
||||
"inputs": {},
|
||||
"bytes": 1945575
|
||||
"bytes": 1934780
|
||||
},
|
||||
"dist/human.js": {
|
||||
"imports": [],
|
||||
"exports": [],
|
||||
"inputs": {
|
||||
"src/blazeface/blazeface.js": {
|
||||
"bytesInOutput": 5046
|
||||
"bytesInOutput": 5039
|
||||
},
|
||||
"src/blazeface/box.js": {
|
||||
"bytesInOutput": 1567
|
||||
|
@ -515,7 +515,7 @@
|
|||
"bytesInOutput": 127037
|
||||
},
|
||||
"src/handpose/handpose.js": {
|
||||
"bytesInOutput": 2025
|
||||
"bytesInOutput": 2023
|
||||
},
|
||||
"src/gesture/gesture.js": {
|
||||
"bytesInOutput": 3162
|
||||
|
@ -527,7 +527,7 @@
|
|||
"bytesInOutput": 3652
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytesInOutput": 11986
|
||||
"bytesInOutput": 12725
|
||||
},
|
||||
"src/log.js": {
|
||||
"bytesInOutput": 266
|
||||
|
@ -539,7 +539,7 @@
|
|||
"bytesInOutput": 918
|
||||
},
|
||||
"src/blazepose/blazepose.js": {
|
||||
"bytesInOutput": 6201
|
||||
"bytesInOutput": 572
|
||||
},
|
||||
"src/handpose/box.js": {
|
||||
"bytesInOutput": 1420
|
||||
|
@ -557,7 +557,7 @@
|
|||
"bytesInOutput": 23
|
||||
}
|
||||
},
|
||||
"bytes": 1912658
|
||||
"bytes": 1907759
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -16,7 +16,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"src/face/blazeface.js": {
|
||||
"src/blazeface/blazeface.js": {
|
||||
"bytes": 7024,
|
||||
"imports": [
|
||||
{
|
||||
|
@ -27,7 +27,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"src/face/box.js": {
|
||||
"src/blazeface/box.js": {
|
||||
"bytes": 1935,
|
||||
"imports": [
|
||||
{
|
||||
|
@ -35,35 +35,35 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"src/face/util.js": {
|
||||
"src/blazeface/util.js": {
|
||||
"bytes": 3087,
|
||||
"imports": []
|
||||
},
|
||||
"src/face/coords.js": {
|
||||
"src/blazeface/coords.js": {
|
||||
"bytes": 37915,
|
||||
"imports": []
|
||||
},
|
||||
"src/face/facepipeline.js": {
|
||||
"src/blazeface/facepipeline.js": {
|
||||
"bytes": 14306,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js"
|
||||
},
|
||||
{
|
||||
"path": "src/face/box.js"
|
||||
"path": "src/blazeface/box.js"
|
||||
},
|
||||
{
|
||||
"path": "src/face/util.js"
|
||||
"path": "src/blazeface/util.js"
|
||||
},
|
||||
{
|
||||
"path": "src/face/coords.js"
|
||||
"path": "src/blazeface/coords.js"
|
||||
},
|
||||
{
|
||||
"path": "src/log.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/face/facemesh.js": {
|
||||
"src/blazeface/facemesh.js": {
|
||||
"bytes": 2991,
|
||||
"imports": [
|
||||
{
|
||||
|
@ -73,13 +73,13 @@
|
|||
"path": "dist/tfjs.esm.js"
|
||||
},
|
||||
{
|
||||
"path": "src/face/blazeface.js"
|
||||
"path": "src/blazeface/blazeface.js"
|
||||
},
|
||||
{
|
||||
"path": "src/face/facepipeline.js"
|
||||
"path": "src/blazeface/facepipeline.js"
|
||||
},
|
||||
{
|
||||
"path": "src/face/coords.js"
|
||||
"path": "src/blazeface/coords.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -147,7 +147,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"src/body/modelBase.js": {
|
||||
"src/posenet/modelBase.js": {
|
||||
"bytes": 1343,
|
||||
"imports": [
|
||||
{
|
||||
|
@ -155,78 +155,78 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"src/body/heapSort.js": {
|
||||
"src/posenet/heapSort.js": {
|
||||
"bytes": 1590,
|
||||
"imports": []
|
||||
},
|
||||
"src/body/buildParts.js": {
|
||||
"src/posenet/buildParts.js": {
|
||||
"bytes": 1775,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/body/heapSort.js"
|
||||
"path": "src/posenet/heapSort.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/body/keypoints.js": {
|
||||
"src/posenet/keypoints.js": {
|
||||
"bytes": 2011,
|
||||
"imports": []
|
||||
},
|
||||
"src/body/vectors.js": {
|
||||
"src/posenet/vectors.js": {
|
||||
"bytes": 1273,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/body/keypoints.js"
|
||||
"path": "src/posenet/keypoints.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/body/decoders.js": {
|
||||
"src/posenet/decoders.js": {
|
||||
"bytes": 2083,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/keypoints.js"
|
||||
"path": "src/posenet/keypoints.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/body/decodePose.js": {
|
||||
"src/posenet/decodePose.js": {
|
||||
"bytes": 5368,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/body/keypoints.js"
|
||||
"path": "src/posenet/keypoints.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/vectors.js"
|
||||
"path": "src/posenet/vectors.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/decoders.js"
|
||||
"path": "src/posenet/decoders.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/body/decodeMultiple.js": {
|
||||
"src/posenet/decodeMultiple.js": {
|
||||
"bytes": 2373,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/body/buildParts.js"
|
||||
"path": "src/posenet/buildParts.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/decodePose.js"
|
||||
"path": "src/posenet/decodePose.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/vectors.js"
|
||||
"path": "src/posenet/vectors.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/body/util.js": {
|
||||
"src/posenet/util.js": {
|
||||
"bytes": 2262,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/body/keypoints.js"
|
||||
"path": "src/posenet/keypoints.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/body/modelPoseNet.js": {
|
||||
"src/posenet/modelPoseNet.js": {
|
||||
"bytes": 2519,
|
||||
"imports": [
|
||||
{
|
||||
|
@ -236,34 +236,45 @@
|
|||
"path": "dist/tfjs.esm.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/modelBase.js"
|
||||
"path": "src/posenet/modelBase.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/decodeMultiple.js"
|
||||
"path": "src/posenet/decodeMultiple.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/decodePose.js"
|
||||
"path": "src/posenet/decodePose.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/util.js"
|
||||
"path": "src/posenet/util.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/body/posenet.js": {
|
||||
"src/posenet/posenet.js": {
|
||||
"bytes": 712,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/body/modelPoseNet.js"
|
||||
"path": "src/posenet/modelPoseNet.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/keypoints.js"
|
||||
"path": "src/posenet/keypoints.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/util.js"
|
||||
"path": "src/posenet/util.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/hand/box.js": {
|
||||
"src/blazepose/blazepose.js": {
|
||||
"bytes": 856,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/log.js"
|
||||
},
|
||||
{
|
||||
"path": "dist/tfjs.esm.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/handpose/box.js": {
|
||||
"bytes": 2522,
|
||||
"imports": [
|
||||
{
|
||||
|
@ -271,43 +282,43 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"src/hand/handdetector.js": {
|
||||
"src/handpose/handdetector.js": {
|
||||
"bytes": 3548,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js"
|
||||
},
|
||||
{
|
||||
"path": "src/hand/box.js"
|
||||
"path": "src/handpose/box.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/hand/util.js": {
|
||||
"src/handpose/util.js": {
|
||||
"bytes": 2346,
|
||||
"imports": []
|
||||
},
|
||||
"src/hand/handpipeline.js": {
|
||||
"src/handpose/handpipeline.js": {
|
||||
"bytes": 7246,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js"
|
||||
},
|
||||
{
|
||||
"path": "src/hand/box.js"
|
||||
"path": "src/handpose/box.js"
|
||||
},
|
||||
{
|
||||
"path": "src/hand/util.js"
|
||||
"path": "src/handpose/util.js"
|
||||
},
|
||||
{
|
||||
"path": "src/log.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"src/hand/anchors.js": {
|
||||
"src/handpose/anchors.js": {
|
||||
"bytes": 224151,
|
||||
"imports": []
|
||||
},
|
||||
"src/hand/handpose.js": {
|
||||
"src/handpose/handpose.js": {
|
||||
"bytes": 2578,
|
||||
"imports": [
|
||||
{
|
||||
|
@ -317,13 +328,13 @@
|
|||
"path": "dist/tfjs.esm.js"
|
||||
},
|
||||
{
|
||||
"path": "src/hand/handdetector.js"
|
||||
"path": "src/handpose/handdetector.js"
|
||||
},
|
||||
{
|
||||
"path": "src/hand/handpipeline.js"
|
||||
"path": "src/handpose/handpipeline.js"
|
||||
},
|
||||
{
|
||||
"path": "src/hand/anchors.js"
|
||||
"path": "src/handpose/anchors.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -350,7 +361,7 @@
|
|||
]
|
||||
},
|
||||
"config.js": {
|
||||
"bytes": 9644,
|
||||
"bytes": 10110,
|
||||
"imports": []
|
||||
},
|
||||
"src/sample.js": {
|
||||
|
@ -362,7 +373,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytes": 17544,
|
||||
"bytes": 18921,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/log.js"
|
||||
|
@ -374,7 +385,7 @@
|
|||
"path": "src/tfjs/backend.js"
|
||||
},
|
||||
{
|
||||
"path": "src/face/facemesh.js"
|
||||
"path": "src/blazeface/facemesh.js"
|
||||
},
|
||||
{
|
||||
"path": "src/age/age.js"
|
||||
|
@ -389,10 +400,13 @@
|
|||
"path": "src/embedding/embedding.js"
|
||||
},
|
||||
{
|
||||
"path": "src/body/posenet.js"
|
||||
"path": "src/posenet/posenet.js"
|
||||
},
|
||||
{
|
||||
"path": "src/hand/handpose.js"
|
||||
"path": "src/blazepose/blazepose.js"
|
||||
},
|
||||
{
|
||||
"path": "src/handpose/handpose.js"
|
||||
},
|
||||
{
|
||||
"path": "src/gesture/gesture.js"
|
||||
|
@ -419,7 +433,7 @@
|
|||
"dist/human.node-gpu.js.map": {
|
||||
"imports": [],
|
||||
"inputs": {},
|
||||
"bytes": 707547
|
||||
"bytes": 711946
|
||||
},
|
||||
"dist/human.node-gpu.js": {
|
||||
"imports": [],
|
||||
|
@ -428,82 +442,82 @@
|
|||
"dist/tfjs.esm.js": {
|
||||
"bytesInOutput": 971
|
||||
},
|
||||
"src/face/blazeface.js": {
|
||||
"src/blazeface/blazeface.js": {
|
||||
"bytesInOutput": 5194
|
||||
},
|
||||
"src/face/box.js": {
|
||||
"src/blazeface/box.js": {
|
||||
"bytesInOutput": 1617
|
||||
},
|
||||
"src/face/util.js": {
|
||||
"src/blazeface/util.js": {
|
||||
"bytesInOutput": 2429
|
||||
},
|
||||
"src/face/coords.js": {
|
||||
"src/blazeface/coords.js": {
|
||||
"bytesInOutput": 30729
|
||||
},
|
||||
"src/face/facepipeline.js": {
|
||||
"src/blazeface/facepipeline.js": {
|
||||
"bytesInOutput": 9436
|
||||
},
|
||||
"src/face/facemesh.js": {
|
||||
"src/blazeface/facemesh.js": {
|
||||
"bytesInOutput": 2365
|
||||
},
|
||||
"src/profile.js": {
|
||||
"bytesInOutput": 851
|
||||
},
|
||||
"src/age/age.js": {
|
||||
"bytesInOutput": 1244
|
||||
"bytesInOutput": 1251
|
||||
},
|
||||
"src/gender/gender.js": {
|
||||
"bytesInOutput": 1996
|
||||
"bytesInOutput": 2004
|
||||
},
|
||||
"src/emotion/emotion.js": {
|
||||
"bytesInOutput": 1888
|
||||
},
|
||||
"src/embedding/embedding.js": {
|
||||
"bytesInOutput": 1374
|
||||
},
|
||||
"src/body/modelBase.js": {
|
||||
"bytesInOutput": 1091
|
||||
},
|
||||
"src/body/heapSort.js": {
|
||||
"bytesInOutput": 1144
|
||||
},
|
||||
"src/body/buildParts.js": {
|
||||
"bytesInOutput": 1289
|
||||
},
|
||||
"src/body/keypoints.js": {
|
||||
"bytesInOutput": 1824
|
||||
},
|
||||
"src/body/vectors.js": {
|
||||
"bytesInOutput": 1047
|
||||
},
|
||||
"src/body/decoders.js": {
|
||||
"bytesInOutput": 1780
|
||||
},
|
||||
"src/body/decodePose.js": {
|
||||
"bytesInOutput": 4101
|
||||
},
|
||||
"src/body/decodeMultiple.js": {
|
||||
"bytesInOutput": 1645
|
||||
},
|
||||
"src/body/util.js": {
|
||||
"bytesInOutput": 1895
|
||||
},
|
||||
"src/body/modelPoseNet.js": {
|
||||
"src/embedding/embedding.js": {
|
||||
"bytesInOutput": 1381
|
||||
},
|
||||
"src/posenet/modelBase.js": {
|
||||
"bytesInOutput": 1091
|
||||
},
|
||||
"src/posenet/heapSort.js": {
|
||||
"bytesInOutput": 1144
|
||||
},
|
||||
"src/posenet/buildParts.js": {
|
||||
"bytesInOutput": 1289
|
||||
},
|
||||
"src/posenet/keypoints.js": {
|
||||
"bytesInOutput": 1824
|
||||
},
|
||||
"src/posenet/vectors.js": {
|
||||
"bytesInOutput": 1047
|
||||
},
|
||||
"src/posenet/decoders.js": {
|
||||
"bytesInOutput": 1780
|
||||
},
|
||||
"src/posenet/decodePose.js": {
|
||||
"bytesInOutput": 4101
|
||||
},
|
||||
"src/posenet/decodeMultiple.js": {
|
||||
"bytesInOutput": 1645
|
||||
},
|
||||
"src/posenet/util.js": {
|
||||
"bytesInOutput": 1895
|
||||
},
|
||||
"src/posenet/modelPoseNet.js": {
|
||||
"bytesInOutput": 2025
|
||||
},
|
||||
"src/body/posenet.js": {
|
||||
"src/posenet/posenet.js": {
|
||||
"bytesInOutput": 639
|
||||
},
|
||||
"src/hand/handdetector.js": {
|
||||
"src/handpose/handdetector.js": {
|
||||
"bytesInOutput": 2881
|
||||
},
|
||||
"src/hand/handpipeline.js": {
|
||||
"src/handpose/handpipeline.js": {
|
||||
"bytesInOutput": 4524
|
||||
},
|
||||
"src/hand/anchors.js": {
|
||||
"src/handpose/anchors.js": {
|
||||
"bytesInOutput": 127039
|
||||
},
|
||||
"src/hand/handpose.js": {
|
||||
"src/handpose/handpose.js": {
|
||||
"bytesInOutput": 2065
|
||||
},
|
||||
"src/gesture/gesture.js": {
|
||||
|
@ -516,7 +530,7 @@
|
|||
"bytesInOutput": 3669
|
||||
},
|
||||
"src/human.js": {
|
||||
"bytesInOutput": 11382
|
||||
"bytesInOutput": 12754
|
||||
},
|
||||
"src/log.js": {
|
||||
"bytesInOutput": 266
|
||||
|
@ -524,14 +538,17 @@
|
|||
"src/tfjs/backend.js": {
|
||||
"bytesInOutput": 985
|
||||
},
|
||||
"src/hand/box.js": {
|
||||
"src/blazepose/blazepose.js": {
|
||||
"bytesInOutput": 627
|
||||
},
|
||||
"src/handpose/box.js": {
|
||||
"bytesInOutput": 1463
|
||||
},
|
||||
"src/hand/util.js": {
|
||||
"src/handpose/util.js": {
|
||||
"bytesInOutput": 1790
|
||||
},
|
||||
"config.js": {
|
||||
"bytesInOutput": 1514
|
||||
"bytesInOutput": 1617
|
||||
},
|
||||
"src/sample.js": {
|
||||
"bytesInOutput": 55299
|
||||
|
@ -540,7 +557,7 @@
|
|||
"bytesInOutput": 21
|
||||
}
|
||||
},
|
||||
"bytes": 309482
|
||||
"bytes": 311613
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -1,214 +1,27 @@
|
|||
import { log } from '../log.js';
|
||||
import * as tf from '../../dist/tfjs.esm.js';
|
||||
// import * as helpers from './helpers.js';
|
||||
// import * as profile from '../profile.js';
|
||||
|
||||
const models = {};
|
||||
let config = {};
|
||||
const anchors = [];
|
||||
const kMidHipCenter = 0;
|
||||
const kPoseDetectKeyNum = 2;
|
||||
const kFullBodySizeRot = 1;
|
||||
|
||||
function calculateScale(min_scale, max_scale, stride_index, num_strides) {
|
||||
if (num_strides === 1) return (min_scale + max_scale) * 0.5;
|
||||
return min_scale + (max_scale - min_scale) * 1.0 * stride_index / (num_strides - 1.0);
|
||||
}
|
||||
|
||||
export function generateAnchors() {
|
||||
const options = {};
|
||||
options.strides = [];
|
||||
options.aspect_ratios = [];
|
||||
options.feature_map_height = [];
|
||||
options.num_layers = 4;
|
||||
options.min_scale = 0.1484375;
|
||||
options.max_scale = 0.75;
|
||||
options.input_size_height = 128;
|
||||
options.input_size_width = 128;
|
||||
options.anchor_offset_x = 0.5;
|
||||
options.anchor_offset_y = 0.5;
|
||||
options.strides.push(8);
|
||||
options.strides.push(16);
|
||||
options.strides.push(16);
|
||||
options.strides.push(16);
|
||||
options.aspect_ratios.push(1.0);
|
||||
options.reduce_boxes_in_lowest_layer = false;
|
||||
options.interpolated_scale_aspect_ratio = 1.0;
|
||||
options.fixed_anchor_size = true;
|
||||
let layer_id = 0;
|
||||
while (layer_id < options.strides.length) {
|
||||
const anchor_height = [];
|
||||
const anchor_width = [];
|
||||
const aspect_ratios = [];
|
||||
const scales = [];
|
||||
// For same strides, we merge the anchors in the same order.
|
||||
let last_same_stride_layer = layer_id;
|
||||
while (last_same_stride_layer < options.strides.length && options.strides[last_same_stride_layer] === options.strides[layer_id]) {
|
||||
const scale = calculateScale(options.min_scale, options.max_scale, last_same_stride_layer, options.strides.length);
|
||||
if (last_same_stride_layer === 0 && options.reduce_boxes_in_lowest_layer) {
|
||||
// For first layer, it can be specified to use predefined anchors.
|
||||
aspect_ratios.push(1.0);
|
||||
aspect_ratios.push(2.0);
|
||||
aspect_ratios.push(0.5);
|
||||
scales.push(0.1);
|
||||
scales.push(scale);
|
||||
scales.push(scale);
|
||||
} else {
|
||||
for (let aspect_ratio_id = 0; aspect_ratio_id < options.aspect_ratios.length; ++aspect_ratio_id) {
|
||||
aspect_ratios.push(options.aspect_ratios[aspect_ratio_id]);
|
||||
scales.push(scale);
|
||||
}
|
||||
if (options.interpolated_scale_aspect_ratio > 0.0) {
|
||||
const scale_next = last_same_stride_layer === options.strides.length - 1 ? 1.0 : calculateScale(options.min_scale, options.max_scale, last_same_stride_layer + 1, options.strides.length);
|
||||
scales.push(Math.sqrt(scale * scale_next));
|
||||
aspect_ratios.push(options.interpolated_scale_aspect_ratio);
|
||||
}
|
||||
}
|
||||
last_same_stride_layer++;
|
||||
}
|
||||
for (let i = 0; i < aspect_ratios.length; ++i) {
|
||||
const ratio_sqrts = Math.sqrt(aspect_ratios[i]);
|
||||
anchor_height.push(scales[i] / ratio_sqrts);
|
||||
anchor_width.push(scales[i] * ratio_sqrts);
|
||||
}
|
||||
let feature_map_height = 0;
|
||||
let feature_map_width = 0;
|
||||
if (options.feature_map_height.length) {
|
||||
feature_map_height = options.feature_map_height[layer_id];
|
||||
feature_map_width = options.feature_map_width[layer_id];
|
||||
} else {
|
||||
const stride = options.strides[layer_id];
|
||||
feature_map_height = Math.ceil(1.0 * options.input_size_height / stride);
|
||||
feature_map_width = Math.ceil(1.0 * options.input_size_width / stride);
|
||||
}
|
||||
for (let y = 0; y < feature_map_height; ++y) {
|
||||
for (let x = 0; x < feature_map_width; ++x) {
|
||||
for (let anchor_id = 0; anchor_id < anchor_height.length; ++anchor_id) {
|
||||
const x_center = (x + options.anchor_offset_x) * 1.0 / feature_map_width;
|
||||
const y_center = (y + options.anchor_offset_y) * 1.0 / feature_map_height;
|
||||
const new_anchor = {};
|
||||
new_anchor.x_center = x_center;
|
||||
new_anchor.y_center = y_center;
|
||||
if (options.fixed_anchor_size) {
|
||||
new_anchor.w = 1.0;
|
||||
new_anchor.h = 1.0;
|
||||
} else {
|
||||
new_anchor.w = anchor_width[anchor_id];
|
||||
new_anchor.h = anchor_height[anchor_id];
|
||||
}
|
||||
anchors.push(new_anchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
layer_id = last_same_stride_layer;
|
||||
}
|
||||
}
|
||||
|
||||
export async function load(cfg) {
|
||||
config = cfg;
|
||||
export async function load(config) {
|
||||
if (!models.blazepose) {
|
||||
models.blazepose = await tf.loadGraphModel(config.pose.modelPath);
|
||||
log(`load model: ${config.pose.modelPath.match(/\/(.*)\./)[1]}`);
|
||||
}
|
||||
generateAnchors();
|
||||
return models.blazepose;
|
||||
}
|
||||
|
||||
function rotateRegion(region) {
|
||||
const x0 = region.keys[kMidHipCenter].x;
|
||||
const y0 = region.keys[kMidHipCenter].y;
|
||||
const x1 = (region.box[0] + region.box[2]) * 0.5;
|
||||
const y1 = (region.box[1] + region.box[3]) * 0.5;
|
||||
const target_angle = Math.PI * 0.5;
|
||||
const angle = target_angle - Math.atan2(-(y1 - y0), x1 - x0);
|
||||
return Math.round(1000 * (angle - 2 * Math.PI * Math.floor((angle - (-Math.PI)) / (2 * Math.PI)))) / 1000;
|
||||
}
|
||||
|
||||
function rotateVecor(vec, rotation) {
|
||||
const sx = vec.x;
|
||||
const sy = vec.y;
|
||||
vec.x = sx * Math.cos(rotation) - sy * Math.sin(rotation);
|
||||
vec.y = sx * Math.sin(rotation) + sy * Math.cos(rotation);
|
||||
}
|
||||
|
||||
async function decode(logits) {
|
||||
const scores = await logits[0].data();
|
||||
const boxes = await logits[1].data();
|
||||
// todo: add nms
|
||||
// todo scale output with image.shape
|
||||
const regions = [];
|
||||
for (let i = 0; i < anchors.length; i++) {
|
||||
const region = {};
|
||||
const score = 1.0 / (1.0 + Math.exp(-scores[i]));
|
||||
if (score > config.pose.scoreThreshold) {
|
||||
const idx = (4 + 2 * kPoseDetectKeyNum) * i;
|
||||
/* boundary box */
|
||||
const sx = boxes[idx + 0];
|
||||
const sy = boxes[idx + 1];
|
||||
const w = boxes[idx + 2] / config.pose.inputSize;
|
||||
const h = boxes[idx + 3] / config.pose.inputSize;
|
||||
const cx = (sx + anchors[i].x_center * config.pose.inputSize) / config.pose.inputSize;
|
||||
const cy = (sy + anchors[i].y_center * config.pose.inputSize) / config.pose.inputSize;
|
||||
region.score = Math.round(1000 * score) / 1000;
|
||||
region.box = [cx - w * 0.5, cy - h * 0.5, w * 0.5, h * 0.5];
|
||||
/* landmark positions (6 keys) */
|
||||
const keys = new Array(kPoseDetectKeyNum);
|
||||
for (let j = 0; j < kPoseDetectKeyNum; j++) {
|
||||
const lx = (boxes[idx + 4 + (2 * j) + 0] + anchors[i].x_center * config.pose.inputSize) / config.pose.inputSize;
|
||||
const ly = (boxes[idx + 4 + (2 * j) + 1] + anchors[i].y_center * config.pose.inputSize) / config.pose.inputSize;
|
||||
keys[j] = { x: lx, y: ly };
|
||||
}
|
||||
region.keys = keys;
|
||||
region.angle = rotateRegion(region);
|
||||
// add points
|
||||
const x_center = region.keys[kMidHipCenter].x * config.pose.inputSize;
|
||||
const y_center = region.keys[kMidHipCenter].y * config.pose.inputSize;
|
||||
const x_scale = region.keys[kFullBodySizeRot].x * config.pose.inputSize;
|
||||
const y_scale = region.keys[kFullBodySizeRot].y * config.pose.inputSize;
|
||||
// Bounding box size as double distance from center to scale point.
|
||||
const box_size = Math.sqrt((x_scale - x_center) * (x_scale - x_center) + (y_scale - y_center) * (y_scale - y_center)) * 2.0;
|
||||
/* RectTransformationCalculator::TransformNormalizedRect() */
|
||||
const roi_cx = region.angle === 0.0 ? x_center + box_size : x_center + box_size * Math.cos(region.angle) - box_size * Math.sin(region.angle);
|
||||
const roi_cy = region.angle === 0.0 ? y_center + box_size : y_center + box_size * Math.sin(region.angle) + box_size * Math.cos(region.angle);
|
||||
const long_side = Math.max(box_size, box_size);
|
||||
const roi_w = long_side * 1.5;
|
||||
const roi_h = long_side * 1.5;
|
||||
region.center = { x: roi_cx / config.pose.inputSize, y: roi_cy / config.pose.inputSize };
|
||||
region.size = { x: roi_w / config.pose.inputSize, y: roi_h / config.pose.inputSize };
|
||||
/* calculate ROI coordinates */
|
||||
const dx = roi_w * 0.5;
|
||||
const dy = roi_h * 0.5;
|
||||
region.coords = [];
|
||||
region.coords[0] = { x: -dx, y: -dy };
|
||||
region.coords[1] = { x: +dx, y: -dy };
|
||||
region.coords[2] = { x: +dx, y: +dy };
|
||||
region.coords[3] = { x: -dx, y: +dy };
|
||||
for (let j = 0; j < 4; j++) {
|
||||
rotateVecor(region.coords[i], region.angle);
|
||||
region.coords[j].x = (region.coords[j].x + roi_cx) / config.pose.inputSize;
|
||||
region.coords[j].y = (region.coords[j].y + roi_cy) / config.pose.inputSize;
|
||||
}
|
||||
|
||||
regions.push(region);
|
||||
}
|
||||
}
|
||||
return regions;
|
||||
}
|
||||
|
||||
export async function predict(image, cfg) {
|
||||
export async function predict(image, config) {
|
||||
if (!models.blazepose) return null;
|
||||
return new Promise(async (resolve) => {
|
||||
config = cfg;
|
||||
const resize = tf.image.resizeBilinear(image, [config.pose.inputSize, config.pose.inputSize], false);
|
||||
const enhance = tf.div(resize, 127.5).sub(1);
|
||||
tf.dispose(resize);
|
||||
const logits = await models.blazepose.predict(enhance);
|
||||
// todo: add landmarks model
|
||||
//
|
||||
tf.dispose(enhance);
|
||||
const regions = await decode(logits);
|
||||
logits[0].dispose();
|
||||
logits[1].dispose();
|
||||
log('poses', regions);
|
||||
resolve(regions);
|
||||
logits.map((logit) => logit.dispose());
|
||||
resolve(logits);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,295 +0,0 @@
|
|||
import * as tf from '../../dist/tfjs.esm.js';
|
||||
import * as mp from './helpers.js';
|
||||
|
||||
/* ------------------------------------------------ *
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2020 terryky1220@gmail.com
|
||||
* ------------------------------------------------ */
|
||||
|
||||
const kMidHipCenter = 0;
|
||||
const kFullBodySizeRot = 1;
|
||||
const kMidShoulderCenter = 2;
|
||||
const kUpperBodySizeRot = 3;
|
||||
const kPoseDetectKeyNum = 2;
|
||||
const POSE_JOINT_NUM = 33;
|
||||
let s_detect_model;
|
||||
let s_detect_tensor_input;
|
||||
let s_landmark_model;
|
||||
let s_landmark_tensor_input;
|
||||
const s_anchors = [];
|
||||
|
||||
function create_ssd_anchors() {
|
||||
const anchor_options = {};
|
||||
anchor_options.strides = [];
|
||||
anchor_options.aspect_ratios = [];
|
||||
anchor_options.feature_map_height = [];
|
||||
anchor_options.num_layers = 4;
|
||||
anchor_options.min_scale = 0.1484375;
|
||||
anchor_options.max_scale = 0.75;
|
||||
anchor_options.input_size_height = 128;
|
||||
anchor_options.input_size_width = 128;
|
||||
anchor_options.anchor_offset_x = 0.5;
|
||||
anchor_options.anchor_offset_y = 0.5;
|
||||
anchor_options.strides.push(8);
|
||||
anchor_options.strides.push(16);
|
||||
anchor_options.strides.push(16);
|
||||
anchor_options.strides.push(16);
|
||||
anchor_options.aspect_ratios.push(1.0);
|
||||
anchor_options.reduce_boxes_in_lowest_layer = false;
|
||||
anchor_options.interpolated_scale_aspect_ratio = 1.0;
|
||||
anchor_options.fixed_anchor_size = true;
|
||||
mp.GenerateAnchors(s_anchors, anchor_options);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- *
|
||||
* Create TensorFlow.js Model
|
||||
* -------------------------------------------------- */
|
||||
async function init_tfjs_blazepose() {
|
||||
const url = './model/tfjs_model_full_pose_detection_float32/model.json';
|
||||
s_detect_model = await tf.loadGraphModel(url);
|
||||
const url_landmark = './model/tfjs_model_full_pose_landmark_39kp_float32/model.json';
|
||||
s_landmark_model = await tf.loadGraphModel(url_landmark);
|
||||
/* Pose detect */
|
||||
s_detect_tensor_input = tfjs_get_tensor_by_name(s_detect_model, 0, 'input');
|
||||
/* Pose Landmark */
|
||||
s_landmark_tensor_input = tfjs_get_tensor_by_name(s_landmark_model, 0, 'input');
|
||||
const det_input_w = s_detect_tensor_input.shape[2];
|
||||
const det_input_h = s_detect_tensor_input.shape[1];
|
||||
create_ssd_anchors(det_input_w, det_input_h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- *
|
||||
* Invoke TensorFlow.js (Pose detection)
|
||||
* -------------------------------------------------- */
|
||||
async function decode_bounds(region_list, logits, score_thresh, input_img_w, input_img_h) {
|
||||
const scores_ptr = await logits[0].data();
|
||||
const bbox_ptr = await logits[1].data();
|
||||
for (let i = 0; i < s_anchors.length; i++) {
|
||||
const region = {};
|
||||
const anchor = s_anchors[i];
|
||||
const score0 = scores_ptr[i];
|
||||
const score = 1.0 / (1.0 + Math.exp(-score0));
|
||||
if (score > score_thresh) {
|
||||
/*
|
||||
* cx, cy, width, height
|
||||
* key0_x, key0_y
|
||||
* key1_x, key1_y
|
||||
*/
|
||||
const numkey = kPoseDetectKeyNum;
|
||||
const bbx_idx = (4 + 2 * numkey) * i;
|
||||
/* boundary box */
|
||||
const sx = bbox_ptr[bbx_idx + 0];
|
||||
const sy = bbox_ptr[bbx_idx + 1];
|
||||
let w = bbox_ptr[bbx_idx + 2];
|
||||
let h = bbox_ptr[bbx_idx + 3];
|
||||
let cx = sx + anchor.x_center * input_img_w;
|
||||
let cy = sy + anchor.y_center * input_img_h;
|
||||
cx /= input_img_w;
|
||||
cy /= input_img_h;
|
||||
w /= input_img_w;
|
||||
h /= input_img_h;
|
||||
const topleft = {};
|
||||
const btmright = {};
|
||||
topleft.x = cx - w * 0.5;
|
||||
topleft.y = cy - h * 0.5;
|
||||
btmright.x = cx + w * 0.5;
|
||||
btmright.y = cy + h * 0.5;
|
||||
region.score = score;
|
||||
region.topleft = topleft;
|
||||
region.btmright = btmright;
|
||||
/* landmark positions (6 keys) */
|
||||
const keys = new Array(kPoseDetectKeyNum);
|
||||
for (let j = 0; j < kPoseDetectKeyNum; j++) {
|
||||
let lx = bbox_ptr[bbx_idx + 4 + (2 * j) + 0];
|
||||
let ly = bbox_ptr[bbx_idx + 4 + (2 * j) + 1];
|
||||
lx += anchor.x_center * input_img_w;
|
||||
ly += anchor.y_center * input_img_h;
|
||||
lx /= input_img_w;
|
||||
ly /= input_img_h;
|
||||
keys[j] = { x: lx, y: ly };
|
||||
}
|
||||
region.keys = keys;
|
||||
region_list.push(region);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- *
|
||||
* extract ROI
|
||||
* based on:
|
||||
* - mediapipe/calculators/util/alignment_points_to_rects_calculator.cc
|
||||
* AlignmentPointsRectsCalculator::DetectionToNormalizedRect()
|
||||
* - mediapipe\calculators\util\rect_transformation_calculator.cc
|
||||
* RectTransformationCalculator::TransformNormalizedRect()
|
||||
* -------------------------------------------------- */
|
||||
function normalize_radians(angle) {
|
||||
return angle - 2 * Math.PI * Math.floor((angle - (-Math.PI)) / (2 * Math.PI));
|
||||
}
|
||||
|
||||
function compute_rotation(region) {
|
||||
const x0 = region.keys[kMidHipCenter].x;
|
||||
const y0 = region.keys[kMidHipCenter].y;
|
||||
const x1 = (region.topleft.x + region.btmright.x) * 0.5;
|
||||
const y1 = (region.topleft.y + region.btmright.y) * 0.5;
|
||||
const target_angle = Math.PI * 0.5;
|
||||
const rotation = target_angle - Math.atan2(-(y1 - y0), x1 - x0);
|
||||
region.rotation = normalize_radians(rotation);
|
||||
}
|
||||
|
||||
function rot_vec(vec, rotation) {
|
||||
const sx = vec.x;
|
||||
const sy = vec.y;
|
||||
vec.x = sx * Math.cos(rotation) - sy * Math.sin(rotation);
|
||||
vec.y = sx * Math.sin(rotation) + sy * Math.cos(rotation);
|
||||
}
|
||||
|
||||
function compute_detect_to_roi(region) {
|
||||
const input_img_w = s_detect_tensor_input.shape[2];
|
||||
const input_img_h = s_detect_tensor_input.shape[1];
|
||||
const x_center = region.keys[kMidHipCenter].x * input_img_w;
|
||||
const y_center = region.keys[kMidHipCenter].y * input_img_h;
|
||||
const x_scale = region.keys[kFullBodySizeRot].x * input_img_w;
|
||||
const y_scale = region.keys[kFullBodySizeRot].y * input_img_h;
|
||||
// Bounding box size as double distance from center to scale point.
|
||||
const box_size = Math.sqrt((x_scale - x_center) * (x_scale - x_center) + (y_scale - y_center) * (y_scale - y_center)) * 2.0;
|
||||
/* RectTransformationCalculator::TransformNormalizedRect() */
|
||||
const width = box_size;
|
||||
const height = box_size;
|
||||
const rotation = region.rotation;
|
||||
const shift_x = 0.0;
|
||||
const shift_y = 0.0;
|
||||
let roi_cx;
|
||||
let roi_cy;
|
||||
if (rotation === 0.0) {
|
||||
roi_cx = x_center + (width * shift_x);
|
||||
roi_cy = y_center + (height * shift_y);
|
||||
} else {
|
||||
const dx = (width * shift_x) * Math.cos(rotation) - (height * shift_y) * Math.sin(rotation);
|
||||
const dy = (width * shift_x) * Math.sin(rotation) + (height * shift_y) * Math.cos(rotation);
|
||||
roi_cx = x_center + dx;
|
||||
roi_cy = y_center + dy;
|
||||
}
|
||||
/*
|
||||
* calculate ROI width and height.
|
||||
* scale parameter is based on
|
||||
* "mediapipe/modules/pose_landmark/pose_detection_to_roi.pbtxt"
|
||||
*/
|
||||
const scale_x = 1.5;
|
||||
const scale_y = 1.5;
|
||||
const long_side = Math.max(width, height);
|
||||
const roi_w = long_side * scale_x;
|
||||
const roi_h = long_side * scale_y;
|
||||
region.roi_center = { x: roi_cx / input_img_w, y: roi_cy / input_img_h };
|
||||
region.roi_size = { x: roi_w / input_img_w, y: roi_h / input_img_h };
|
||||
/* calculate ROI coordinates */
|
||||
const dx = roi_w * 0.5;
|
||||
const dy = roi_h * 0.5;
|
||||
region.roi_coord = [];
|
||||
region.roi_coord[0] = { x: -dx, y: -dy };
|
||||
region.roi_coord[1] = { x: +dx, y: -dy };
|
||||
region.roi_coord[2] = { x: +dx, y: +dy };
|
||||
region.roi_coord[3] = { x: -dx, y: +dy };
|
||||
for (let i = 0; i < 4; i++) {
|
||||
rot_vec(region.roi_coord[i], rotation);
|
||||
region.roi_coord[i].x += roi_cx;
|
||||
region.roi_coord[i].y += roi_cy;
|
||||
region.roi_coord[i].x /= input_img_h;
|
||||
region.roi_coord[i].y /= input_img_h;
|
||||
}
|
||||
}
|
||||
|
||||
function pack_detect_result(detect_result, region_list) {
|
||||
for (let i = 0; i < region_list.length; i++) {
|
||||
region = region_list[i];
|
||||
compute_rotation(region);
|
||||
compute_detect_to_roi(region);
|
||||
detect_result.push(region);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- *
|
||||
* Invoke TensorFlow.js (Pose detection)
|
||||
* -------------------------------------------------- */
|
||||
function exec_tfjs(img) {
|
||||
const w = s_detect_tensor_input.shape[2];
|
||||
const h = s_detect_tensor_input.shape[1];
|
||||
const logits = tf.tidy(() => {
|
||||
img_tensor1d = tf.tensor1d(img);
|
||||
img_tensor = img_tensor1d.reshape([h, w, 3]);
|
||||
// normalize [0, 255] to [-1, 1].
|
||||
const min = -1;
|
||||
const max = 1;
|
||||
const normalized = img_tensor.toFloat().mul((max - min) / 255.0).add(min);
|
||||
// resize, reshape
|
||||
const batched = normalized.reshape([-1, w, h, 3]);
|
||||
return s_detect_model.predict(batched);
|
||||
});
|
||||
return logits;
|
||||
}
|
||||
|
||||
async function invoke_pose_detect(img) {
|
||||
const logits = exec_tfjs(img);
|
||||
const score_thresh = 0.75;
|
||||
const detect_result = [];
|
||||
const region_list = [];
|
||||
const w = s_detect_tensor_input.shape[2];
|
||||
const h = s_detect_tensor_input.shape[1];
|
||||
await decode_bounds(region_list, logits, score_thresh, w, h);
|
||||
if (true) { /* USE NMS */
|
||||
const iou_thresh = 0.3;
|
||||
const region_nms_list = [];
|
||||
non_max_suppression(region_list, region_nms_list, iou_thresh);
|
||||
pack_detect_result(detect_result, region_nms_list);
|
||||
} else {
|
||||
pack_detect_result(detect_result, region_list);
|
||||
}
|
||||
/* release the resource of output tensor */
|
||||
logits[0].dispose();
|
||||
logits[1].dispose();
|
||||
return detect_result;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- *
|
||||
* Invoke TensorFlow.js (Pose landmark)
|
||||
* -------------------------------------------------- */
|
||||
function exec_tfjs_landmark(img) {
|
||||
const w = s_landmark_tensor_input.shape[2];
|
||||
const h = s_landmark_tensor_input.shape[1];
|
||||
const logits = tf.tidy(() => {
|
||||
img_tensor1d = tf.tensor1d(img);
|
||||
img_tensor = img_tensor1d.reshape([h, w, 3]);
|
||||
// normalize [0, 255] to [-1, 1].
|
||||
const min = -1;
|
||||
const max = 1;
|
||||
const normalized = img_tensor.toFloat().mul((max - min) / 255.0).add(min);
|
||||
// resize, reshape
|
||||
const batched = normalized.reshape([-1, w, h, 3]);
|
||||
return s_landmark_model.predict(batched);
|
||||
});
|
||||
return logits;
|
||||
}
|
||||
|
||||
async function invoke_pose_landmark(img) {
|
||||
const logits = exec_tfjs_landmark(img);
|
||||
const poseflag_ptr = await logits[1].data(); /* shape: (1, 1, 1, 1) */
|
||||
const landmark_ptr = await logits[2].data(); /* shape: (1, 156) */
|
||||
const img_w = s_landmark_tensor_input.shape[2];
|
||||
const img_h = s_landmark_tensor_input.shape[1];
|
||||
const landmark_result = {};
|
||||
landmark_result.joint = [];
|
||||
landmark_result.score = poseflag_ptr[0];
|
||||
for (let i = 0; i < POSE_JOINT_NUM; i++) {
|
||||
landmark_result.joint[i] = {
|
||||
x: landmark_ptr[4 * i + 0] / img_w,
|
||||
y: landmark_ptr[4 * i + 1] / img_h,
|
||||
z: landmark_ptr[4 * i + 2],
|
||||
};
|
||||
}
|
||||
logits[0].dispose();
|
||||
logits[1].dispose();
|
||||
logits[2].dispose();
|
||||
return landmark_result;
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
/* ------------------------------------------------ *
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2020 terryky1220@gmail.com
|
||||
* ------------------------------------------------ */
|
||||
|
||||
// Copyright 2019 The MediaPipe Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
function CalculateScale(min_scale, max_scale, stride_index, num_strides) {
|
||||
if (num_strides === 1) return (min_scale + max_scale) * 0.5;
|
||||
return min_scale + (max_scale - min_scale) * 1.0 * stride_index / (num_strides - 1.0);
|
||||
}
|
||||
|
||||
export function GenerateAnchors(anchors, options) {
|
||||
let layer_id = 0;
|
||||
while (layer_id < options.strides.length) {
|
||||
const anchor_height = [];
|
||||
const anchor_width = [];
|
||||
const aspect_ratios = [];
|
||||
const scales = [];
|
||||
// For same strides, we merge the anchors in the same order.
|
||||
let last_same_stride_layer = layer_id;
|
||||
while (last_same_stride_layer < options.strides.length
|
||||
&& options.strides[last_same_stride_layer] === options.strides[layer_id]) {
|
||||
const scale = CalculateScale(options.min_scale, options.max_scale,
|
||||
last_same_stride_layer, options.strides.length);
|
||||
if (last_same_stride_layer === 0 && options.reduce_boxes_in_lowest_layer) {
|
||||
// For first layer, it can be specified to use predefined anchors.
|
||||
aspect_ratios.push(1.0);
|
||||
aspect_ratios.push(2.0);
|
||||
aspect_ratios.push(0.5);
|
||||
scales.push(0.1);
|
||||
scales.push(scale);
|
||||
scales.push(scale);
|
||||
} else {
|
||||
for (let aspect_ratio_id = 0;
|
||||
aspect_ratio_id < options.aspect_ratios.length;
|
||||
++aspect_ratio_id) {
|
||||
aspect_ratios.push(options.aspect_ratios[aspect_ratio_id]);
|
||||
scales.push(scale);
|
||||
}
|
||||
if (options.interpolated_scale_aspect_ratio > 0.0) {
|
||||
const scale_next = last_same_stride_layer === options.strides.length - 1
|
||||
? 1.0
|
||||
: CalculateScale(options.min_scale, options.max_scale,
|
||||
last_same_stride_layer + 1,
|
||||
options.strides.length);
|
||||
scales.push(Math.sqrt(scale * scale_next));
|
||||
aspect_ratios.push(options.interpolated_scale_aspect_ratio);
|
||||
}
|
||||
}
|
||||
last_same_stride_layer++;
|
||||
}
|
||||
for (let i = 0; i < aspect_ratios.length; ++i) {
|
||||
const ratio_sqrts = Math.sqrt(aspect_ratios[i]);
|
||||
anchor_height.push(scales[i] / ratio_sqrts);
|
||||
anchor_width.push(scales[i] * ratio_sqrts);
|
||||
}
|
||||
let feature_map_height = 0;
|
||||
let feature_map_width = 0;
|
||||
if (options.feature_map_height.length) {
|
||||
feature_map_height = options.feature_map_height[layer_id];
|
||||
feature_map_width = options.feature_map_width[layer_id];
|
||||
} else {
|
||||
const stride = options.strides[layer_id];
|
||||
feature_map_height = Math.ceil(1.0 * options.input_size_height / stride);
|
||||
feature_map_width = Math.ceil(1.0 * options.input_size_width / stride);
|
||||
}
|
||||
for (let y = 0; y < feature_map_height; ++y) {
|
||||
for (let x = 0; x < feature_map_width; ++x) {
|
||||
for (let anchor_id = 0; anchor_id < anchor_height.length; ++anchor_id) {
|
||||
// TODO: Support specifying anchor_offset_x, anchor_offset_y.
|
||||
const x_center = (x + options.anchor_offset_x) * 1.0 / feature_map_width;
|
||||
const y_center = (y + options.anchor_offset_y) * 1.0 / feature_map_height;
|
||||
const new_anchor = {};
|
||||
new_anchor.x_center = x_center;
|
||||
new_anchor.y_center = y_center;
|
||||
if (options.fixed_anchor_size) {
|
||||
new_anchor.w = 1.0;
|
||||
new_anchor.h = 1.0;
|
||||
} else {
|
||||
new_anchor.w = anchor_width[anchor_id];
|
||||
new_anchor.h = anchor_height[anchor_id];
|
||||
}
|
||||
anchors.push(new_anchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
layer_id = last_same_stride_layer;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- *
|
||||
* Apply NonMaxSuppression:
|
||||
* https://github.com/tensorflow/tfjs/blob/master/tfjs-core/src/ops/image_ops.ts
|
||||
* -------------------------------------------------- */
|
||||
function calc_intersection_over_union(region0, region1) {
|
||||
const sx0 = region0.box[0];
|
||||
const sy0 = region0.box[1];
|
||||
const ex0 = region0.box[2];
|
||||
const ey0 = region0.box[3];
|
||||
const sx1 = region1.box[0];
|
||||
const sy1 = region1.box[1];
|
||||
const ex1 = region1.box[2];
|
||||
const ey1 = region1.box[3];
|
||||
const xmin0 = Math.min(sx0, ex0);
|
||||
const ymin0 = Math.min(sy0, ey0);
|
||||
const xmax0 = Math.max(sx0, ex0);
|
||||
const ymax0 = Math.max(sy0, ey0);
|
||||
const xmin1 = Math.min(sx1, ex1);
|
||||
const ymin1 = Math.min(sy1, ey1);
|
||||
const xmax1 = Math.max(sx1, ex1);
|
||||
const ymax1 = Math.max(sy1, ey1);
|
||||
const area0 = (ymax0 - ymin0) * (xmax0 - xmin0);
|
||||
const area1 = (ymax1 - ymin1) * (xmax1 - xmin1);
|
||||
if (area0 <= 0 || area1 <= 0) return 0.0;
|
||||
const intersect_xmin = Math.max(xmin0, xmin1);
|
||||
const intersect_ymin = Math.max(ymin0, ymin1);
|
||||
const intersect_xmax = Math.min(xmax0, xmax1);
|
||||
const intersect_ymax = Math.min(ymax0, ymax1);
|
||||
const intersect_area = Math.max(intersect_ymax - intersect_ymin, 0.0) * Math.max(intersect_xmax - intersect_xmin, 0.0);
|
||||
return intersect_area / (area0 + area1 - intersect_area);
|
||||
}
|
||||
|
||||
function compare(v1, v2) {
|
||||
if (v1.score > v2.score) return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function NMS(region_list, region_nms_list, iou_thresh) {
|
||||
region_list.sort(compare);
|
||||
for (let i = 0; i < region_list.length; i++) {
|
||||
const region_candidate = region_list[i];
|
||||
let ignore_candidate = false;
|
||||
for (let j = 0; j < region_nms_list.length; j++) {
|
||||
const region_nms = region_nms_list[j];
|
||||
const iou = calc_intersection_over_union(region_candidate, region_nms);
|
||||
if (iou >= iou_thresh) {
|
||||
ignore_candidate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ignore_candidate) {
|
||||
region_nms_list.push(region_candidate);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,374 +0,0 @@
|
|||
import * as detect from './detect.js';
|
||||
|
||||
/* ------------------------------------------------ *
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2020 terryky1220@gmail.com
|
||||
* ------------------------------------------------ */
|
||||
// tf.setBackend('wasm').then(() => startWebGL());
|
||||
|
||||
let s_debug_log;
|
||||
let s_rtarget_main;
|
||||
let s_rtarget_feed;
|
||||
let s_rtarget_src;
|
||||
|
||||
class GuiProperty {
|
||||
constructor() {
|
||||
this.draw_roi_rect = false;
|
||||
this.draw_pmeter = false;
|
||||
}
|
||||
}
|
||||
const s_gui_prop = new GuiProperty();
|
||||
|
||||
function generate_input_image(gl, texid, win_w, win_h) {
|
||||
const dims = detect.get_pose_detect_input_dims();
|
||||
const buf_rgba = new Uint8Array(dims.w * dims.h * 4);
|
||||
const buf_rgb = new Uint8Array(dims.w * dims.h * 3);
|
||||
GLUtil.set_render_target(gl, s_rtarget_feed);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
r2d.draw_2d_texture(gl, texid, 0, win_h - dims.h, dims.w, dims.h, 1);
|
||||
gl.readPixels(0, 0, dims.w, dims.h, gl.RGBA, gl.UNSIGNED_BYTE, buf_rgba);
|
||||
for (let i = 0, j = 0; i < buf_rgba.length; i++) {
|
||||
if (i % 4 !== 3) buf_rgb[j++] = buf_rgba[i];
|
||||
}
|
||||
GLUtil.set_render_target(gl, s_rtarget_main);
|
||||
return buf_rgb;
|
||||
}
|
||||
|
||||
function generate_landmark_input_image(gl, srctex, texw, texh, detection, pose_id) {
|
||||
const dims = get_pose_landmark_input_dims();
|
||||
const buf_rgba = new Uint8Array(dims.w * dims.h * 4);
|
||||
const buf_rgb = new Uint8Array(dims.w * dims.h * 3);
|
||||
const texcoord = [];
|
||||
if (detection.length > pose_id) {
|
||||
region = detection[pose_id];
|
||||
const x0 = region.roi_coord[0].x;
|
||||
const y0 = region.roi_coord[0].y;
|
||||
const x1 = region.roi_coord[1].x; // 0--------1
|
||||
const y1 = region.roi_coord[1].y; // | |
|
||||
const x2 = region.roi_coord[2].x; // | |
|
||||
const y2 = region.roi_coord[2].y; // 3--------2
|
||||
const x3 = region.roi_coord[3].x;
|
||||
const y3 = region.roi_coord[3].y;
|
||||
texcoord[0] = x3; texcoord[1] = y3;
|
||||
texcoord[2] = x0; texcoord[3] = y0;
|
||||
texcoord[4] = x2; texcoord[5] = y2;
|
||||
texcoord[6] = x1; texcoord[7] = y1;
|
||||
}
|
||||
GLUtil.set_render_target(gl, s_rtarget_feed);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
r2d.draw_2d_texture_texcoord_rot(gl, srctex, 0, texh - dims.h, dims.w, dims.h, texcoord, 0, 0, 0);
|
||||
gl.readPixels(0, 0, dims.w, dims.h, gl.RGBA, gl.UNSIGNED_BYTE, buf_rgba);
|
||||
for (let i = 0, j = 0; i < buf_rgba.length; i++) {
|
||||
if (i % 4 != 3) buf_rgb[j++] = buf_rgba[i];
|
||||
}
|
||||
GLUtil.set_render_target(gl, s_rtarget_main);
|
||||
return buf_rgb;
|
||||
}
|
||||
|
||||
function
|
||||
render_detect_region(gl, ofstx, ofsty, texw, texh, detection) {
|
||||
const col_white = [1.0, 1.0, 1.0, 1.0];
|
||||
const col_red = [1.0, 0.0, 0.0, 1.0];
|
||||
const col_frame = [1.0, 0.0, 0.0, 1.0];
|
||||
for (let i = 0; i < detection.length; i++) {
|
||||
region = detection[i];
|
||||
const x1 = region.topleft.x * texw + ofstx;
|
||||
const y1 = region.topleft.y * texh + ofsty;
|
||||
const x2 = region.btmright.x * texw + ofstx;
|
||||
const y2 = region.btmright.y * texh + ofsty;
|
||||
const score = region.score;
|
||||
/* rectangle region */
|
||||
r2d.draw_2d_rect(gl, x1, y1, x2 - x1, y2 - y1, col_frame, 2.0);
|
||||
/* class name */
|
||||
const buf = '' + (score * 100).toFixed(0);
|
||||
dbgstr.draw_dbgstr_ex(gl, buf, x1, y1, 1.0, col_white, col_frame);
|
||||
/* key points */
|
||||
const hx = region.keys[kMidHipCenter].x * texw + ofstx;
|
||||
const hy = region.keys[kMidHipCenter].y * texh + ofsty;
|
||||
let sx = (region.topleft.x + region.btmright.x) * 0.5;
|
||||
let sy = (region.topleft.y + region.btmright.y) * 0.5;
|
||||
sx = sx * texw + ofstx;
|
||||
sy = sy * texh + ofsty;
|
||||
r2d.draw_2d_line(gl, hx, hy, sx, sy, col_white, 2.0);
|
||||
let r = 4;
|
||||
r2d.draw_2d_fillrect(gl, hx - (r / 2), hy - (r / 2), r, r, col_frame);
|
||||
r2d.draw_2d_fillrect(gl, sx - (r / 2), sy - (r / 2), r, r, col_frame);
|
||||
for (let j0 = 0; j0 < 4; j0++) {
|
||||
const j1 = (j0 + 1) % 4;
|
||||
const x1 = region.roi_coord[j0].x * texw + ofstx;
|
||||
const y1 = region.roi_coord[j0].y * texh + ofsty;
|
||||
const x2 = region.roi_coord[j1].x * texw + ofstx;
|
||||
const y2 = region.roi_coord[j1].y * texh + ofsty;
|
||||
r2d.draw_2d_line(gl, x1, y1, x2, y2, col_red, 2.0);
|
||||
}
|
||||
const cx = region.roi_center.x * texw + ofstx;
|
||||
const cy = region.roi_center.y * texh + ofsty;
|
||||
r = 10;
|
||||
r2d.draw_2d_fillrect(gl, cx - (r / 2), cy - (r / 2), r, r, col_red);
|
||||
}
|
||||
}
|
||||
|
||||
function transform_pose_landmark(transformed_pos, landmark, region) {
|
||||
const scale_x = region.roi_size.x;
|
||||
const scale_y = region.roi_size.y;
|
||||
const pivot_x = region.roi_center.x;
|
||||
const pivot_y = region.roi_center.y;
|
||||
const rotation = region.rotation;
|
||||
const mat = new Array(16);
|
||||
matrix_identity(mat);
|
||||
matrix_translate(mat, pivot_x, pivot_y, 0);
|
||||
matrix_rotate(mat, RAD_TO_DEG(rotation), 0, 0, 1);
|
||||
matrix_scale(mat, scale_x, scale_y, 1.0);
|
||||
matrix_translate(mat, -0.5, -0.5, 0);
|
||||
for (let i = 0; i < POSE_JOINT_NUM; i++) {
|
||||
const vec = [landmark.joint[i].x, landmark.joint[i].y];
|
||||
matrix_multvec2(mat, vec, vec);
|
||||
transformed_pos[i] = { x: vec[0], y: vec[1] };
|
||||
}
|
||||
}
|
||||
|
||||
function render_bone(gl, ofstx, ofsty, drw_w, drw_h,
|
||||
transformed_pos, id0, id1, col) {
|
||||
const x0 = transformed_pos[id0].x * drw_w + ofstx;
|
||||
const y0 = transformed_pos[id0].y * drw_h + ofsty;
|
||||
const x1 = transformed_pos[id1].x * drw_w + ofstx;
|
||||
const y1 = transformed_pos[id1].y * drw_h + ofsty;
|
||||
r2d.draw_2d_line(gl, x0, y0, x1, y1, col, 5.0);
|
||||
}
|
||||
|
||||
function render_pose_landmark(gl, ofstx, ofsty, texw, texh, landmakr_ret,
|
||||
detection, pose_id) {
|
||||
const col_red = [1.0, 0.0, 0.0, 1.0];
|
||||
const col_orange = [1.0, 0.6, 0.0, 1.0];
|
||||
const col_cyan = [0.0, 1.0, 1.0, 1.0];
|
||||
const col_lime = [0.0, 1.0, 0.3, 1.0];
|
||||
const col_pink = [1.0, 0.0, 1.0, 1.0];
|
||||
const col_blue = [0.0, 0.5, 1.0, 1.0];
|
||||
const col_white = [1.0, 1.0, 1.0, 1.0];
|
||||
if (landmakr_ret.length <= pose_id) return;
|
||||
const landmark = landmakr_ret[pose_id];
|
||||
const score = landmark.score;
|
||||
const buf = 'score:' + (score * 100).toFixed(1);
|
||||
dbgstr.draw_dbgstr_ex(gl, buf, texw - 120, 0, 1.0, col_white, col_red);
|
||||
const transformed_pos = new Array(POSE_JOINT_NUM);
|
||||
transform_pose_landmark(transformed_pos, landmark, detection[pose_id]);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 11, 12, col_cyan);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 12, 24, col_cyan);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 24, 23, col_cyan);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 23, 11, col_cyan);
|
||||
/* right arm */
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 11, 13, col_orange);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 13, 15, col_orange);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 15, 21, col_orange);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 15, 19, col_orange);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 15, 17, col_orange);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 17, 19, col_orange);
|
||||
/* left arm */
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 12, 14, col_lime);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 14, 16, col_lime);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 16, 22, col_lime);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 16, 20, col_lime);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 16, 18, col_lime);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 18, 20, col_lime);
|
||||
/* face */
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 9, 10, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 0, 1, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 1, 2, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 2, 3, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 3, 7, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 0, 4, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 4, 5, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 5, 6, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 6, 8, col_blue);
|
||||
/* right leg */
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 23, 25, col_pink);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 25, 27, col_pink);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 27, 31, col_pink);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 31, 29, col_pink);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 29, 27, col_pink);
|
||||
/* left leg */
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 24, 26, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 26, 28, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 28, 32, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 32, 30, col_blue);
|
||||
render_bone(gl, ofstx, ofsty, texw, texh, transformed_pos, 30, 28, col_blue);
|
||||
for (let i = 0; i < POSE_JOINT_NUM; i++) {
|
||||
const x = transformed_pos[i].x * texw + ofstx;
|
||||
const y = transformed_pos[i].y * texh + ofsty;
|
||||
const r = 9;
|
||||
r2d.draw_2d_fillrect(gl, x - (r / 2), y - (r / 2), r, r, col_red);
|
||||
}
|
||||
}
|
||||
|
||||
function render_cropped_pose_image(gl, srctex, ofstx, ofsty, texw, texh, detection, pose_id) {
|
||||
const texcoord = [];
|
||||
if (detection.length <= pose_id) return;
|
||||
region = detection[pose_id];
|
||||
const x0 = region.roi_coord[0].x;
|
||||
const y0 = region.roi_coord[0].y;
|
||||
const x1 = region.roi_coord[1].x; // 0--------1
|
||||
const y1 = region.roi_coord[1].y; // | |
|
||||
const x2 = region.roi_coord[2].x; // | |
|
||||
const y2 = region.roi_coord[2].y; // 3--------2
|
||||
const x3 = region.roi_coord[3].x;
|
||||
const y3 = region.roi_coord[3].y;
|
||||
texcoord[0] = x0; texcoord[1] = y0;
|
||||
texcoord[2] = x3; texcoord[3] = y3;
|
||||
texcoord[4] = x1; texcoord[5] = y1;
|
||||
texcoord[6] = x2; texcoord[7] = y2;
|
||||
r2d.draw_2d_texture_texcoord_rot(gl, srctex, ofstx, ofsty, texw, texh, texcoord, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* Adjust the texture size to fit the window size
|
||||
*
|
||||
* Portrait
|
||||
* Landscape +------+
|
||||
* +-+------+-+ +------+
|
||||
* | | | | | |
|
||||
* | | | | | |
|
||||
* +-+------+-+ +------+
|
||||
* +------+
|
||||
*/
|
||||
function
|
||||
generate_squared_src_image(gl, texid, src_w, src_h, win_w, win_h) {
|
||||
const win_aspect = win_w / win_h;
|
||||
const tex_aspect = src_w / src_h;
|
||||
let scale;
|
||||
let scaled_w;
|
||||
let scaled_h;
|
||||
let offset_x;
|
||||
let offset_y;
|
||||
if (win_aspect > tex_aspect) {
|
||||
scale = win_h / src_h;
|
||||
scaled_w = scale * src_w;
|
||||
scaled_h = scale * src_h;
|
||||
offset_x = (win_w - scaled_w) * 0.5;
|
||||
offset_y = 0;
|
||||
} else {
|
||||
scale = win_w / src_w;
|
||||
scaled_w = scale * src_w;
|
||||
scaled_h = scale * src_h;
|
||||
offset_x = 0;
|
||||
offset_y = (win_h - scaled_h) * 0.5;
|
||||
}
|
||||
GLUtil.set_render_target(gl, s_rtarget_src);
|
||||
gl.clearColor(0.7, 0.7, 0.7, 1.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
r2d.draw_2d_texture(gl, texid, offset_x, offset_y, scaled_w, scaled_h, 1);
|
||||
}
|
||||
|
||||
function init_gui() {
|
||||
const gui = new dat.GUI();
|
||||
gui.add(s_gui_prop, 'draw_roi_rect');
|
||||
gui.add(s_gui_prop, 'draw_pmeter');
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- *
|
||||
* M A I N F U N C T I O N
|
||||
* ---------------------------------------------------------------- */
|
||||
async function startWebGL() {
|
||||
s_debug_log = document.getElementById('debug_log');
|
||||
const canvas = document.querySelector('#glcanvas');
|
||||
const gl = canvas.getContext('webgl');
|
||||
if (!gl) {
|
||||
alert('Failed to initialize WebGL.');
|
||||
return;
|
||||
}
|
||||
gl.clearColor(0.7, 0.7, 0.7, 1.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
init_gui();
|
||||
const camtex = GLUtil.create_camera_texture(gl);
|
||||
// const camtex = GLUtil.create_video_texture (gl, "pexels_dance.mp4");
|
||||
const imgtex = GLUtil.create_image_texture2(gl, 'pexels.jpg');
|
||||
const win_w = canvas.clientWidth;
|
||||
const win_h = canvas.clientHeight;
|
||||
r2d.init_2d_render(gl, win_w, win_h);
|
||||
init_dbgstr(gl, win_w, win_h);
|
||||
pmeter.init_pmeter(gl, win_w, win_h, win_h - 40);
|
||||
const stats = init_stats();
|
||||
await init_tfjs_blazepose();
|
||||
s_debug_log.innerHTML = 'tfjs.Backend = ' + tf.getBackend() + '<br>';
|
||||
s_rtarget_main = GLUtil.create_render_target(gl, win_w, win_h, 0);
|
||||
s_rtarget_feed = GLUtil.create_render_target(gl, win_w, win_w, 1);
|
||||
s_rtarget_src = GLUtil.create_render_target(gl, win_w, win_w, 1);
|
||||
/* stop loading spinner */
|
||||
const spinner = document.getElementById('loading');
|
||||
spinner.classList.add('loaded');
|
||||
let prev_time_ms = performance.now();
|
||||
|
||||
async function render(now) {
|
||||
pmeter.reset_lap(0);
|
||||
pmeter.set_lap(0);
|
||||
const cur_time_ms = performance.now();
|
||||
const interval_ms = cur_time_ms - prev_time_ms;
|
||||
prev_time_ms = cur_time_ms;
|
||||
stats.begin();
|
||||
let src_w = imgtex.image.width;
|
||||
let src_h = imgtex.image.height;
|
||||
let texid = imgtex.texid;
|
||||
if (GLUtil.is_camera_ready(camtex)) {
|
||||
GLUtil.update_camera_texture(gl, camtex);
|
||||
src_w = camtex.video.videoWidth;
|
||||
src_h = camtex.video.videoHeight;
|
||||
texid = camtex.texid;
|
||||
}
|
||||
generate_squared_src_image(gl, texid, src_w, src_h, win_w, win_h);
|
||||
texid = s_rtarget_src.texid;
|
||||
/* --------------------------------------- *
|
||||
* invoke TF.js (Pose detection)
|
||||
* --------------------------------------- */
|
||||
const feed_image = generate_input_image(gl, texid, win_w, win_h);
|
||||
const time_invoke0_start = performance.now();
|
||||
const predictions = await invoke_pose_detect(feed_image);
|
||||
const time_invoke0 = performance.now() - time_invoke0_start;
|
||||
/* --------------------------------------- *
|
||||
* invoke TF.js (Pose landmark)
|
||||
* --------------------------------------- */
|
||||
const landmark_ret = [];
|
||||
let time_invoke1 = 0;
|
||||
for (let pose_id = 0; pose_id < predictions.length; pose_id++) {
|
||||
const feed_image = generate_landmark_input_image(gl, texid, win_w, win_h, predictions, pose_id);
|
||||
const time_invoke1_start = performance.now();
|
||||
landmark_ret[pose_id] = await invoke_pose_landmark(feed_image);
|
||||
time_invoke1 += performance.now() - time_invoke1_start;
|
||||
}
|
||||
|
||||
/* --------------------------------------- *
|
||||
* render scene
|
||||
* --------------------------------------- */
|
||||
GLUtil.set_render_target(gl, s_rtarget_main);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
r2d.draw_2d_texture(gl, texid, 0, 0, win_w, win_h, 0);
|
||||
if (s_gui_prop.draw_roi_rect) {
|
||||
render_detect_region(gl, 0, 0, win_w, win_h, predictions);
|
||||
/* draw cropped image of the pose area */
|
||||
for (let pose_id = 0; pose_id < predictions.length; pose_id++) {
|
||||
const w = 100;
|
||||
const h = 100;
|
||||
const x = win_w - w - 10;
|
||||
const y = h * pose_id + 20;
|
||||
const col_white = [1.0, 1.0, 1.0, 1.0];
|
||||
render_cropped_pose_image(gl, texid, x, y, w, h, predictions, pose_id);
|
||||
r2d.draw_2d_rect(gl, x, y, w, h, col_white, 2.0);
|
||||
}
|
||||
}
|
||||
for (let pose_id = 0; pose_id < predictions.length; pose_id++) {
|
||||
render_pose_landmark(gl, 0, 0, win_w, win_h, landmark_ret, predictions, pose_id);
|
||||
}
|
||||
/* --------------------------------------- *
|
||||
* post process
|
||||
* --------------------------------------- */
|
||||
if (s_gui_prop.draw_pmeter) {
|
||||
pmeter.draw_pmeter(gl, 0, 40);
|
||||
}
|
||||
let str = 'Interval: ' + interval_ms.toFixed(1) + ' [ms]';
|
||||
dbgstr.draw_dbgstr(gl, str, 10, 10);
|
||||
str = 'TF.js0 : ' + time_invoke0.toFixed(1) + ' [ms]';
|
||||
dbgstr.draw_dbgstr(gl, str, 10, 10 + 22 * 1);
|
||||
str = 'TF.js1 : ' + time_invoke1.toFixed(1) + ' [ms]';
|
||||
dbgstr.draw_dbgstr(gl, str, 10, 10 + 22 * 2);
|
||||
stats.end();
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
render();
|
||||
}
|
63
src/human.js
63
src/human.js
|
@ -446,27 +446,66 @@ class Human {
|
|||
});
|
||||
}
|
||||
|
||||
async warmup(userConfig) {
|
||||
async warmupBitmap() {
|
||||
const b64toBlob = (base64, type = 'application/octet-stream') => fetch(`data:${type};base64,${base64}`).then((res) => res.blob());
|
||||
|
||||
if (userConfig) this.config = mergeDeep(this.config, userConfig);
|
||||
const video = this.config.videoOptimized;
|
||||
this.config.videoOptimized = false;
|
||||
let blob;
|
||||
let res;
|
||||
switch (this.config.warmup) {
|
||||
case 'face': blob = await b64toBlob(sample.face); break;
|
||||
case 'full': blob = await b64toBlob(sample.body); break;
|
||||
default: blob = null;
|
||||
}
|
||||
if (!blob) return null;
|
||||
const bitmap = await createImageBitmap(blob);
|
||||
if (blob) {
|
||||
const bitmap = await createImageBitmap(blob);
|
||||
res = await this.detect(bitmap, config);
|
||||
bitmap.close();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
async warmupCanvas() {
|
||||
return new Promise((resolve) => {
|
||||
let src;
|
||||
let size = 0;
|
||||
switch (this.config.warmup) {
|
||||
case 'face':
|
||||
size = 256;
|
||||
src = 'data:image/jpeg;base64,' + sample.face;
|
||||
break;
|
||||
case 'full':
|
||||
size = 1200;
|
||||
src = 'data:image/jpeg;base64,' + sample.body;
|
||||
break;
|
||||
default:
|
||||
src = null;
|
||||
}
|
||||
const img = new Image(size, size);
|
||||
img.onload = () => {
|
||||
const canvas = (typeof OffscreenCanvas !== 'undefined') ? new OffscreenCanvas(size, size) : document.createElement('canvas');
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
const data = ctx.getImageData(0, 0, size, size);
|
||||
this.detect(data, config).then((res) => resolve(res));
|
||||
};
|
||||
if (src) img.src = src;
|
||||
else resolve(null);
|
||||
});
|
||||
}
|
||||
|
||||
async warmup(userConfig) {
|
||||
const t0 = now();
|
||||
const warmup = await this.detect(bitmap, config);
|
||||
const t1 = now();
|
||||
bitmap.close();
|
||||
log('Warmup', this.config.warmup, (t1 - t0), warmup);
|
||||
if (userConfig) this.config = mergeDeep(this.config, userConfig);
|
||||
const video = this.config.videoOptimized;
|
||||
this.config.videoOptimized = false;
|
||||
let res;
|
||||
if (typeof createImageBitmap === 'function') res = await this.warmupBitmap();
|
||||
else res = await this.warmupCanvas();
|
||||
this.config.videoOptimized = video;
|
||||
return warmup;
|
||||
const t1 = now();
|
||||
log('Warmup', this.config.warmup, (t1 - t0), res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
2
wiki
2
wiki
|
@ -1 +1 @@
|
|||
Subproject commit 5aa552da6706eb513e7dc6b49302b689b682ca40
|
||||
Subproject commit f31fa056967450ba8427bb2768db92cbe9b8cd8e
|
Loading…
Reference in New Issue