mirror of https://github.com/vladmandic/human
fine tuning age and face models
parent
4a99831c59
commit
8667da95c2
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"MD012": false,
|
||||||
|
"MD013": false,
|
||||||
|
"MD033": false,
|
||||||
|
"MD036": false
|
||||||
|
}
|
10
config.js
10
config.js
|
@ -82,10 +82,10 @@ export default {
|
||||||
// in short time (10 * 1/25 = 0.25 sec)
|
// in short time (10 * 1/25 = 0.25 sec)
|
||||||
skipInitial: false, // if previous detection resulted in no faces detected,
|
skipInitial: false, // if previous detection resulted in no faces detected,
|
||||||
// should skipFrames be reset immediately
|
// should skipFrames be reset immediately
|
||||||
minConfidence: 0.2, // threshold for discarding a prediction
|
minConfidence: 0.1, // threshold for discarding a prediction
|
||||||
iouThreshold: 0.2, // threshold for deciding whether boxes overlap too much in
|
iouThreshold: 0.1, // threshold for deciding whether boxes overlap too much in
|
||||||
// non-maximum suppression (0.1 means drop if overlap 10%)
|
// non-maximum suppression (0.1 means drop if overlap 10%)
|
||||||
scoreThreshold: 0.2, // threshold for deciding when to remove boxes based on score
|
scoreThreshold: 0.1, // threshold for deciding when to remove boxes based on score
|
||||||
// in non-maximum suppression,
|
// in non-maximum suppression,
|
||||||
// this is applied on detection objects only and before minConfidence
|
// this is applied on detection objects only and before minConfidence
|
||||||
},
|
},
|
||||||
|
@ -114,7 +114,7 @@ export default {
|
||||||
|
|
||||||
gender: {
|
gender: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
minConfidence: 0.4, // threshold for discarding a prediction
|
minConfidence: 0.1, // threshold for discarding a prediction
|
||||||
modelPath: '../models/gender.json', // can be 'gender', 'gender-ssrnet-imdb' or 'gender-ssrnet-wiki'
|
modelPath: '../models/gender.json', // can be 'gender', 'gender-ssrnet-imdb' or 'gender-ssrnet-wiki'
|
||||||
inputSize: 64, // fixed value
|
inputSize: 64, // fixed value
|
||||||
skipFrames: 41, // how many frames to go without re-running the detector
|
skipFrames: 41, // how many frames to go without re-running the detector
|
||||||
|
@ -124,7 +124,7 @@ export default {
|
||||||
emotion: {
|
emotion: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
inputSize: 64, // fixed value
|
inputSize: 64, // fixed value
|
||||||
minConfidence: 0.2, // threshold for discarding a prediction
|
minConfidence: 0.1, // threshold for discarding a prediction
|
||||||
skipFrames: 21, // how many frames to go without re-running the detector
|
skipFrames: 21, // how many frames to go without re-running the detector
|
||||||
modelPath: '../models/emotion-large.json', // can be 'mini', 'large'
|
modelPath: '../models/emotion-large.json', // can be 'mini', 'large'
|
||||||
},
|
},
|
||||||
|
|
|
@ -31,7 +31,7 @@ const ui = {
|
||||||
baseFontProto: 'small-caps {size} "Segoe UI"',
|
baseFontProto: 'small-caps {size} "Segoe UI"',
|
||||||
baseLineWidth: 12,
|
baseLineWidth: 12,
|
||||||
crop: true,
|
crop: true,
|
||||||
columns: 2,
|
columns: 4,
|
||||||
busy: false,
|
busy: false,
|
||||||
facing: true,
|
facing: true,
|
||||||
useWorker: false,
|
useWorker: false,
|
||||||
|
@ -339,7 +339,7 @@ async function processImage(input) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
image.onload = async () => {
|
image.onload = async () => {
|
||||||
log('Processing image:', image.src);
|
log('Processing image:', encodeURI(image.src));
|
||||||
const canvas = document.getElementById('canvas');
|
const canvas = document.getElementById('canvas');
|
||||||
image.width = image.naturalWidth;
|
image.width = image.naturalWidth;
|
||||||
image.height = image.naturalHeight;
|
image.height = image.naturalHeight;
|
||||||
|
@ -351,7 +351,12 @@ async function processImage(input) {
|
||||||
const thumb = document.createElement('canvas');
|
const thumb = document.createElement('canvas');
|
||||||
thumb.className = 'thumbnail';
|
thumb.className = 'thumbnail';
|
||||||
thumb.width = window.innerWidth / (ui.columns + 0.1);
|
thumb.width = window.innerWidth / (ui.columns + 0.1);
|
||||||
thumb.height = canvas.height / (window.innerWidth / thumb.width);
|
thumb.height = thumb.width * canvas.height / canvas.width;
|
||||||
|
if (result.face && result.face.length > 0) {
|
||||||
|
thumb.title = result.face.map((a, i) => `#${i} face: ${Math.trunc(100 * a.faceConfidence)}% box: ${Math.trunc(100 * a.boxConfidence)}% age: ${Math.trunc(a.age)} gender: ${Math.trunc(100 * a.genderConfidence)}% ${a.gender}`).join(' | ');
|
||||||
|
} else {
|
||||||
|
thumb.title = 'no face detected';
|
||||||
|
}
|
||||||
const ctx = thumb.getContext('2d');
|
const ctx = thumb.getContext('2d');
|
||||||
ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, thumb.width, thumb.height);
|
ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, thumb.width, thumb.height);
|
||||||
document.getElementById('samples-container').appendChild(thumb);
|
document.getElementById('samples-container').appendChild(thumb);
|
||||||
|
@ -403,6 +408,7 @@ async function detectSampleImages() {
|
||||||
log('Running detection of sample images');
|
log('Running detection of sample images');
|
||||||
status('processing images');
|
status('processing images');
|
||||||
document.getElementById('samples-container').innerHTML = '';
|
document.getElementById('samples-container').innerHTML = '';
|
||||||
|
for (const m of Object.values(menu)) m.hide();
|
||||||
for (const image of ui.samples) await processImage(image);
|
for (const image of ui.samples) await processImage(image);
|
||||||
status('');
|
status('');
|
||||||
}
|
}
|
||||||
|
|
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": {
|
"inputs": {
|
||||||
"dist/human.esm.js": {
|
"dist/human.esm.js": {
|
||||||
"bytes": 1339457,
|
"bytes": 3275334,
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"demo/draw.js": {
|
"demo/draw.js": {
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"demo/browser.js": {
|
"demo/browser.js": {
|
||||||
"bytes": 26838,
|
"bytes": 27252,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "dist/human.esm.js",
|
"path": "dist/human.esm.js",
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"exports": [],
|
"exports": [],
|
||||||
"inputs": {},
|
"inputs": {},
|
||||||
"bytes": 2039549
|
"bytes": 2144153
|
||||||
},
|
},
|
||||||
"dist/demo-browser-index.js": {
|
"dist/demo-browser-index.js": {
|
||||||
"imports": [],
|
"imports": [],
|
||||||
|
@ -51,22 +51,22 @@
|
||||||
"entryPoint": "demo/browser.js",
|
"entryPoint": "demo/browser.js",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"dist/human.esm.js": {
|
"dist/human.esm.js": {
|
||||||
"bytesInOutput": 1331979
|
"bytesInOutput": 3274217
|
||||||
},
|
},
|
||||||
"demo/draw.js": {
|
"demo/draw.js": {
|
||||||
"bytesInOutput": 6241
|
"bytesInOutput": 10662
|
||||||
},
|
},
|
||||||
"demo/menu.js": {
|
"demo/menu.js": {
|
||||||
"bytesInOutput": 10696
|
"bytesInOutput": 13698
|
||||||
},
|
},
|
||||||
"demo/gl-bench.js": {
|
"demo/gl-bench.js": {
|
||||||
"bytesInOutput": 6759
|
"bytesInOutput": 9352
|
||||||
},
|
},
|
||||||
"demo/browser.js": {
|
"demo/browser.js": {
|
||||||
"bytesInOutput": 17423
|
"bytesInOutput": 24591
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bytes": 1380483
|
"bytes": 3332784
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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
|
@ -5,7 +5,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"dist/tfjs.esm.js": {
|
"dist/tfjs.esm.js": {
|
||||||
"bytes": 1065627,
|
"bytes": 2912363,
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/tfjs/backend.ts": {
|
"src/tfjs/backend.ts": {
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/blazeface/facepipeline.ts": {
|
"src/blazeface/facepipeline.ts": {
|
||||||
"bytes": 14151,
|
"bytes": 14154,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "dist/tfjs.esm.js",
|
"path": "dist/tfjs.esm.js",
|
||||||
|
@ -73,7 +73,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/blazeface/facemesh.ts": {
|
"src/blazeface/facemesh.ts": {
|
||||||
"bytes": 2922,
|
"bytes": 3095,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/log.ts",
|
"path": "src/log.ts",
|
||||||
|
@ -421,7 +421,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/human.ts": {
|
"src/human.ts": {
|
||||||
"bytes": 19965,
|
"bytes": 20100,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/log.ts",
|
"path": "src/log.ts",
|
||||||
|
@ -499,7 +499,7 @@
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"exports": [],
|
"exports": [],
|
||||||
"inputs": {},
|
"inputs": {},
|
||||||
"bytes": 1941474
|
"bytes": 2158647
|
||||||
},
|
},
|
||||||
"dist/human.esm.js": {
|
"dist/human.esm.js": {
|
||||||
"imports": [],
|
"imports": [],
|
||||||
|
@ -509,121 +509,121 @@
|
||||||
"entryPoint": "src/human.ts",
|
"entryPoint": "src/human.ts",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"src/blazeface/facemesh.ts": {
|
"src/blazeface/facemesh.ts": {
|
||||||
"bytesInOutput": 1463
|
"bytesInOutput": 3035
|
||||||
},
|
},
|
||||||
"src/posenet/keypoints.ts": {
|
"src/posenet/keypoints.ts": {
|
||||||
"bytesInOutput": 1690
|
"bytesInOutput": 2522
|
||||||
},
|
},
|
||||||
"src/log.ts": {
|
"src/log.ts": {
|
||||||
"bytesInOutput": 252
|
"bytesInOutput": 307
|
||||||
},
|
},
|
||||||
"dist/tfjs.esm.js": {
|
"dist/tfjs.esm.js": {
|
||||||
"bytesInOutput": 1056626
|
"bytesInOutput": 2833855
|
||||||
},
|
},
|
||||||
"src/tfjs/backend.ts": {
|
"src/tfjs/backend.ts": {
|
||||||
"bytesInOutput": 1053
|
"bytesInOutput": 1780
|
||||||
},
|
},
|
||||||
"src/blazeface/blazeface.ts": {
|
"src/blazeface/blazeface.ts": {
|
||||||
"bytesInOutput": 2194
|
"bytesInOutput": 4949
|
||||||
},
|
},
|
||||||
"src/blazeface/box.ts": {
|
"src/blazeface/box.ts": {
|
||||||
"bytesInOutput": 834
|
"bytesInOutput": 1663
|
||||||
},
|
},
|
||||||
"src/blazeface/util.ts": {
|
"src/blazeface/util.ts": {
|
||||||
"bytesInOutput": 858
|
"bytesInOutput": 2253
|
||||||
},
|
},
|
||||||
"src/blazeface/coords.ts": {
|
"src/blazeface/coords.ts": {
|
||||||
"bytesInOutput": 28983
|
"bytesInOutput": 40670
|
||||||
},
|
},
|
||||||
"src/blazeface/facepipeline.ts": {
|
"src/blazeface/facepipeline.ts": {
|
||||||
"bytesInOutput": 5051
|
"bytesInOutput": 11648
|
||||||
},
|
},
|
||||||
"src/human.ts": {
|
"src/human.ts": {
|
||||||
"bytesInOutput": 10550
|
"bytesInOutput": 17108
|
||||||
},
|
},
|
||||||
"src/faceboxes/faceboxes.ts": {
|
"src/faceboxes/faceboxes.ts": {
|
||||||
"bytesInOutput": 1576
|
"bytesInOutput": 2761
|
||||||
},
|
},
|
||||||
"src/profile.ts": {
|
"src/profile.ts": {
|
||||||
"bytesInOutput": 606
|
"bytesInOutput": 952
|
||||||
},
|
},
|
||||||
"src/age/age.ts": {
|
"src/age/age.ts": {
|
||||||
"bytesInOutput": 784
|
"bytesInOutput": 1493
|
||||||
},
|
},
|
||||||
"src/gender/gender.ts": {
|
"src/gender/gender.ts": {
|
||||||
"bytesInOutput": 1246
|
"bytesInOutput": 2600
|
||||||
},
|
},
|
||||||
"src/emotion/emotion.ts": {
|
"src/emotion/emotion.ts": {
|
||||||
"bytesInOutput": 1189
|
"bytesInOutput": 2304
|
||||||
},
|
},
|
||||||
"src/embedding/embedding.ts": {
|
"src/embedding/embedding.ts": {
|
||||||
"bytesInOutput": 804
|
"bytesInOutput": 1600
|
||||||
},
|
},
|
||||||
"src/posenet/posenet.ts": {
|
"src/posenet/posenet.ts": {
|
||||||
"bytesInOutput": 1016
|
"bytesInOutput": 2195
|
||||||
},
|
},
|
||||||
"src/posenet/modelBase.ts": {
|
"src/posenet/modelBase.ts": {
|
||||||
"bytesInOutput": 646
|
"bytesInOutput": 1258
|
||||||
},
|
},
|
||||||
"src/posenet/heapSort.ts": {
|
"src/posenet/heapSort.ts": {
|
||||||
"bytesInOutput": 1017
|
"bytesInOutput": 1442
|
||||||
},
|
},
|
||||||
"src/posenet/buildParts.ts": {
|
"src/posenet/buildParts.ts": {
|
||||||
"bytesInOutput": 456
|
"bytesInOutput": 1506
|
||||||
},
|
},
|
||||||
"src/posenet/decodePose.ts": {
|
"src/posenet/decodePose.ts": {
|
||||||
"bytesInOutput": 1292
|
"bytesInOutput": 4651
|
||||||
},
|
},
|
||||||
"src/posenet/vectors.ts": {
|
"src/posenet/vectors.ts": {
|
||||||
"bytesInOutput": 346
|
"bytesInOutput": 779
|
||||||
},
|
},
|
||||||
"src/posenet/decoders.ts": {
|
"src/posenet/decoders.ts": {
|
||||||
"bytesInOutput": 768
|
"bytesInOutput": 1851
|
||||||
},
|
},
|
||||||
"src/posenet/decodeMultiple.ts": {
|
"src/posenet/decodeMultiple.ts": {
|
||||||
"bytesInOutput": 557
|
"bytesInOutput": 1710
|
||||||
},
|
},
|
||||||
"src/posenet/util.ts": {
|
"src/posenet/util.ts": {
|
||||||
"bytesInOutput": 354
|
"bytesInOutput": 791
|
||||||
},
|
},
|
||||||
"src/handpose/handpose.ts": {
|
"src/handpose/handpose.ts": {
|
||||||
"bytesInOutput": 1281
|
"bytesInOutput": 2325
|
||||||
},
|
},
|
||||||
"src/handpose/box.ts": {
|
"src/handpose/box.ts": {
|
||||||
"bytesInOutput": 938
|
"bytesInOutput": 1893
|
||||||
},
|
},
|
||||||
"src/handpose/handdetector.ts": {
|
"src/handpose/handdetector.ts": {
|
||||||
"bytesInOutput": 1668
|
"bytesInOutput": 3379
|
||||||
},
|
},
|
||||||
"src/handpose/util.ts": {
|
"src/handpose/util.ts": {
|
||||||
"bytesInOutput": 816
|
"bytesInOutput": 2198
|
||||||
},
|
},
|
||||||
"src/handpose/handpipeline.ts": {
|
"src/handpose/handpipeline.ts": {
|
||||||
"bytesInOutput": 2510
|
"bytesInOutput": 5897
|
||||||
},
|
},
|
||||||
"src/handpose/anchors.ts": {
|
"src/handpose/anchors.ts": {
|
||||||
"bytesInOutput": 126985
|
"bytesInOutput": 221202
|
||||||
},
|
},
|
||||||
"src/gesture/gesture.ts": {
|
"src/gesture/gesture.ts": {
|
||||||
"bytesInOutput": 2391
|
"bytesInOutput": 4071
|
||||||
},
|
},
|
||||||
"src/imagefx.js": {
|
"src/imagefx.js": {
|
||||||
"bytesInOutput": 10975
|
"bytesInOutput": 18706
|
||||||
},
|
},
|
||||||
"src/image.ts": {
|
"src/image.ts": {
|
||||||
"bytesInOutput": 2355
|
"bytesInOutput": 4511
|
||||||
},
|
},
|
||||||
"config.js": {
|
"config.js": {
|
||||||
"bytesInOutput": 1453
|
"bytesInOutput": 2225
|
||||||
},
|
},
|
||||||
"src/sample.ts": {
|
"src/sample.ts": {
|
||||||
"bytesInOutput": 55295
|
"bytesInOutput": 55311
|
||||||
},
|
},
|
||||||
"package.json": {
|
"package.json": {
|
||||||
"bytesInOutput": 2572
|
"bytesInOutput": 3333
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bytes": 1339457
|
"bytes": 3275334
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"dist/tfjs.esm.js": {
|
"dist/tfjs.esm.js": {
|
||||||
"bytes": 1065627,
|
"bytes": 2912363,
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/tfjs/backend.ts": {
|
"src/tfjs/backend.ts": {
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/blazeface/facepipeline.ts": {
|
"src/blazeface/facepipeline.ts": {
|
||||||
"bytes": 14151,
|
"bytes": 14154,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "dist/tfjs.esm.js",
|
"path": "dist/tfjs.esm.js",
|
||||||
|
@ -73,7 +73,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/blazeface/facemesh.ts": {
|
"src/blazeface/facemesh.ts": {
|
||||||
"bytes": 2922,
|
"bytes": 3095,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/log.ts",
|
"path": "src/log.ts",
|
||||||
|
@ -421,7 +421,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/human.ts": {
|
"src/human.ts": {
|
||||||
"bytes": 19965,
|
"bytes": 20100,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/log.ts",
|
"path": "src/log.ts",
|
||||||
|
@ -499,7 +499,7 @@
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"exports": [],
|
"exports": [],
|
||||||
"inputs": {},
|
"inputs": {},
|
||||||
"bytes": 1941485
|
"bytes": 2161920
|
||||||
},
|
},
|
||||||
"dist/human.ts": {
|
"dist/human.ts": {
|
||||||
"imports": [],
|
"imports": [],
|
||||||
|
@ -507,121 +507,121 @@
|
||||||
"entryPoint": "src/human.ts",
|
"entryPoint": "src/human.ts",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"src/blazeface/facemesh.ts": {
|
"src/blazeface/facemesh.ts": {
|
||||||
"bytesInOutput": 1463
|
"bytesInOutput": 3167
|
||||||
},
|
},
|
||||||
"src/posenet/keypoints.ts": {
|
"src/posenet/keypoints.ts": {
|
||||||
"bytesInOutput": 1690
|
"bytesInOutput": 2710
|
||||||
},
|
},
|
||||||
"src/human.ts": {
|
"src/human.ts": {
|
||||||
"bytesInOutput": 10586
|
"bytesInOutput": 18089
|
||||||
},
|
},
|
||||||
"src/log.ts": {
|
"src/log.ts": {
|
||||||
"bytesInOutput": 252
|
"bytesInOutput": 319
|
||||||
},
|
},
|
||||||
"dist/tfjs.esm.js": {
|
"dist/tfjs.esm.js": {
|
||||||
"bytesInOutput": 1056626
|
"bytesInOutput": 2975201
|
||||||
},
|
},
|
||||||
"src/tfjs/backend.ts": {
|
"src/tfjs/backend.ts": {
|
||||||
"bytesInOutput": 1053
|
"bytesInOutput": 1910
|
||||||
},
|
},
|
||||||
"src/blazeface/blazeface.ts": {
|
"src/blazeface/blazeface.ts": {
|
||||||
"bytesInOutput": 2194
|
"bytesInOutput": 5171
|
||||||
},
|
},
|
||||||
"src/blazeface/box.ts": {
|
"src/blazeface/box.ts": {
|
||||||
"bytesInOutput": 834
|
"bytesInOutput": 1753
|
||||||
},
|
},
|
||||||
"src/blazeface/util.ts": {
|
"src/blazeface/util.ts": {
|
||||||
"bytesInOutput": 858
|
"bytesInOutput": 2381
|
||||||
},
|
},
|
||||||
"src/blazeface/coords.ts": {
|
"src/blazeface/coords.ts": {
|
||||||
"bytesInOutput": 28983
|
"bytesInOutput": 47204
|
||||||
},
|
},
|
||||||
"src/blazeface/facepipeline.ts": {
|
"src/blazeface/facepipeline.ts": {
|
||||||
"bytesInOutput": 5051
|
"bytesInOutput": 12116
|
||||||
},
|
},
|
||||||
"src/faceboxes/faceboxes.ts": {
|
"src/faceboxes/faceboxes.ts": {
|
||||||
"bytesInOutput": 1576
|
"bytesInOutput": 2897
|
||||||
},
|
},
|
||||||
"src/profile.ts": {
|
"src/profile.ts": {
|
||||||
"bytesInOutput": 606
|
"bytesInOutput": 996
|
||||||
},
|
},
|
||||||
"src/age/age.ts": {
|
"src/age/age.ts": {
|
||||||
"bytesInOutput": 784
|
"bytesInOutput": 1597
|
||||||
},
|
},
|
||||||
"src/gender/gender.ts": {
|
"src/gender/gender.ts": {
|
||||||
"bytesInOutput": 1246
|
"bytesInOutput": 2758
|
||||||
},
|
},
|
||||||
"src/emotion/emotion.ts": {
|
"src/emotion/emotion.ts": {
|
||||||
"bytesInOutput": 1189
|
"bytesInOutput": 2444
|
||||||
},
|
},
|
||||||
"src/embedding/embedding.ts": {
|
"src/embedding/embedding.ts": {
|
||||||
"bytesInOutput": 804
|
"bytesInOutput": 1684
|
||||||
},
|
},
|
||||||
"src/posenet/posenet.ts": {
|
"src/posenet/posenet.ts": {
|
||||||
"bytesInOutput": 1016
|
"bytesInOutput": 2305
|
||||||
},
|
},
|
||||||
"src/posenet/modelBase.ts": {
|
"src/posenet/modelBase.ts": {
|
||||||
"bytesInOutput": 646
|
"bytesInOutput": 1322
|
||||||
},
|
},
|
||||||
"src/posenet/heapSort.ts": {
|
"src/posenet/heapSort.ts": {
|
||||||
"bytesInOutput": 1017
|
"bytesInOutput": 1564
|
||||||
},
|
},
|
||||||
"src/posenet/buildParts.ts": {
|
"src/posenet/buildParts.ts": {
|
||||||
"bytesInOutput": 456
|
"bytesInOutput": 1578
|
||||||
},
|
},
|
||||||
"src/posenet/decodePose.ts": {
|
"src/posenet/decodePose.ts": {
|
||||||
"bytesInOutput": 1292
|
"bytesInOutput": 4827
|
||||||
},
|
},
|
||||||
"src/posenet/vectors.ts": {
|
"src/posenet/vectors.ts": {
|
||||||
"bytesInOutput": 346
|
"bytesInOutput": 839
|
||||||
},
|
},
|
||||||
"src/posenet/decoders.ts": {
|
"src/posenet/decoders.ts": {
|
||||||
"bytesInOutput": 768
|
"bytesInOutput": 1945
|
||||||
},
|
},
|
||||||
"src/posenet/decodeMultiple.ts": {
|
"src/posenet/decodeMultiple.ts": {
|
||||||
"bytesInOutput": 557
|
"bytesInOutput": 1772
|
||||||
},
|
},
|
||||||
"src/posenet/util.ts": {
|
"src/posenet/util.ts": {
|
||||||
"bytesInOutput": 354
|
"bytesInOutput": 837
|
||||||
},
|
},
|
||||||
"src/handpose/handpose.ts": {
|
"src/handpose/handpose.ts": {
|
||||||
"bytesInOutput": 1281
|
"bytesInOutput": 2439
|
||||||
},
|
},
|
||||||
"src/handpose/box.ts": {
|
"src/handpose/box.ts": {
|
||||||
"bytesInOutput": 938
|
"bytesInOutput": 1991
|
||||||
},
|
},
|
||||||
"src/handpose/handdetector.ts": {
|
"src/handpose/handdetector.ts": {
|
||||||
"bytesInOutput": 1668
|
"bytesInOutput": 3525
|
||||||
},
|
},
|
||||||
"src/handpose/util.ts": {
|
"src/handpose/util.ts": {
|
||||||
"bytesInOutput": 816
|
"bytesInOutput": 2320
|
||||||
},
|
},
|
||||||
"src/handpose/handpipeline.ts": {
|
"src/handpose/handpipeline.ts": {
|
||||||
"bytesInOutput": 2510
|
"bytesInOutput": 6155
|
||||||
},
|
},
|
||||||
"src/handpose/anchors.ts": {
|
"src/handpose/anchors.ts": {
|
||||||
"bytesInOutput": 126985
|
"bytesInOutput": 256534
|
||||||
},
|
},
|
||||||
"src/gesture/gesture.ts": {
|
"src/gesture/gesture.ts": {
|
||||||
"bytesInOutput": 2391
|
"bytesInOutput": 4241
|
||||||
},
|
},
|
||||||
"src/imagefx.js": {
|
"src/imagefx.js": {
|
||||||
"bytesInOutput": 10975
|
"bytesInOutput": 20118
|
||||||
},
|
},
|
||||||
"src/image.ts": {
|
"src/image.ts": {
|
||||||
"bytesInOutput": 2355
|
"bytesInOutput": 4717
|
||||||
},
|
},
|
||||||
"config.js": {
|
"config.js": {
|
||||||
"bytesInOutput": 1453
|
"bytesInOutput": 2447
|
||||||
},
|
},
|
||||||
"src/sample.ts": {
|
"src/sample.ts": {
|
||||||
"bytesInOutput": 55295
|
"bytesInOutput": 55315
|
||||||
},
|
},
|
||||||
"package.json": {
|
"package.json": {
|
||||||
"bytesInOutput": 2572
|
"bytesInOutput": 3569
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bytes": 1339499
|
"bytes": 3465540
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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
|
@ -5,7 +5,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"dist/tfjs.esm.js": {
|
"dist/tfjs.esm.js": {
|
||||||
"bytes": 690,
|
"bytes": 1287,
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/tfjs/backend.ts": {
|
"src/tfjs/backend.ts": {
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/blazeface/facepipeline.ts": {
|
"src/blazeface/facepipeline.ts": {
|
||||||
"bytes": 14151,
|
"bytes": 14154,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "dist/tfjs.esm.js",
|
"path": "dist/tfjs.esm.js",
|
||||||
|
@ -73,7 +73,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/blazeface/facemesh.ts": {
|
"src/blazeface/facemesh.ts": {
|
||||||
"bytes": 2922,
|
"bytes": 3095,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/log.ts",
|
"path": "src/log.ts",
|
||||||
|
@ -421,7 +421,7 @@
|
||||||
"imports": []
|
"imports": []
|
||||||
},
|
},
|
||||||
"src/human.ts": {
|
"src/human.ts": {
|
||||||
"bytes": 19965,
|
"bytes": 20100,
|
||||||
"imports": [
|
"imports": [
|
||||||
{
|
{
|
||||||
"path": "src/log.ts",
|
"path": "src/log.ts",
|
||||||
|
@ -499,7 +499,7 @@
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"exports": [],
|
"exports": [],
|
||||||
"inputs": {},
|
"inputs": {},
|
||||||
"bytes": 708229
|
"bytes": 823869
|
||||||
},
|
},
|
||||||
"dist/human.node-gpu.js": {
|
"dist/human.node-gpu.js": {
|
||||||
"imports": [],
|
"imports": [],
|
||||||
|
@ -507,121 +507,121 @@
|
||||||
"entryPoint": "src/human.ts",
|
"entryPoint": "src/human.ts",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"dist/tfjs.esm.js": {
|
"dist/tfjs.esm.js": {
|
||||||
"bytesInOutput": 551
|
"bytesInOutput": 1238
|
||||||
},
|
},
|
||||||
"src/blazeface/facemesh.ts": {
|
"src/blazeface/facemesh.ts": {
|
||||||
"bytesInOutput": 1494
|
"bytesInOutput": 3095
|
||||||
},
|
},
|
||||||
"src/posenet/keypoints.ts": {
|
"src/posenet/keypoints.ts": {
|
||||||
"bytesInOutput": 1677
|
"bytesInOutput": 2527
|
||||||
},
|
},
|
||||||
"src/human.ts": {
|
"src/human.ts": {
|
||||||
"bytesInOutput": 10558
|
"bytesInOutput": 17167
|
||||||
},
|
},
|
||||||
"src/log.ts": {
|
"src/log.ts": {
|
||||||
"bytesInOutput": 251
|
"bytesInOutput": 307
|
||||||
},
|
},
|
||||||
"src/tfjs/backend.ts": {
|
"src/tfjs/backend.ts": {
|
||||||
"bytesInOutput": 1145
|
"bytesInOutput": 1845
|
||||||
},
|
},
|
||||||
"src/blazeface/blazeface.ts": {
|
"src/blazeface/blazeface.ts": {
|
||||||
"bytesInOutput": 2338
|
"bytesInOutput": 5097
|
||||||
},
|
},
|
||||||
"src/blazeface/facepipeline.ts": {
|
"src/blazeface/facepipeline.ts": {
|
||||||
"bytesInOutput": 5098
|
"bytesInOutput": 11710
|
||||||
},
|
},
|
||||||
"src/blazeface/box.ts": {
|
"src/blazeface/box.ts": {
|
||||||
"bytesInOutput": 854
|
"bytesInOutput": 1713
|
||||||
},
|
},
|
||||||
"src/blazeface/util.ts": {
|
"src/blazeface/util.ts": {
|
||||||
"bytesInOutput": 848
|
"bytesInOutput": 2247
|
||||||
},
|
},
|
||||||
"src/blazeface/coords.ts": {
|
"src/blazeface/coords.ts": {
|
||||||
"bytesInOutput": 28973
|
"bytesInOutput": 40670
|
||||||
},
|
},
|
||||||
"src/faceboxes/faceboxes.ts": {
|
"src/faceboxes/faceboxes.ts": {
|
||||||
"bytesInOutput": 1613
|
"bytesInOutput": 2814
|
||||||
},
|
},
|
||||||
"src/profile.ts": {
|
"src/profile.ts": {
|
||||||
"bytesInOutput": 604
|
"bytesInOutput": 950
|
||||||
},
|
},
|
||||||
"src/age/age.ts": {
|
"src/age/age.ts": {
|
||||||
"bytesInOutput": 831
|
"bytesInOutput": 1550
|
||||||
},
|
},
|
||||||
"src/gender/gender.ts": {
|
"src/gender/gender.ts": {
|
||||||
"bytesInOutput": 1319
|
"bytesInOutput": 2688
|
||||||
},
|
},
|
||||||
"src/emotion/emotion.ts": {
|
"src/emotion/emotion.ts": {
|
||||||
"bytesInOutput": 1256
|
"bytesInOutput": 2385
|
||||||
},
|
},
|
||||||
"src/embedding/embedding.ts": {
|
"src/embedding/embedding.ts": {
|
||||||
"bytesInOutput": 848
|
"bytesInOutput": 1653
|
||||||
},
|
},
|
||||||
"src/posenet/posenet.ts": {
|
"src/posenet/posenet.ts": {
|
||||||
"bytesInOutput": 1039
|
"bytesInOutput": 2233
|
||||||
},
|
},
|
||||||
"src/posenet/modelBase.ts": {
|
"src/posenet/modelBase.ts": {
|
||||||
"bytesInOutput": 656
|
"bytesInOutput": 1303
|
||||||
},
|
},
|
||||||
"src/posenet/heapSort.ts": {
|
"src/posenet/heapSort.ts": {
|
||||||
"bytesInOutput": 1017
|
"bytesInOutput": 1440
|
||||||
},
|
},
|
||||||
"src/posenet/buildParts.ts": {
|
"src/posenet/buildParts.ts": {
|
||||||
"bytesInOutput": 454
|
"bytesInOutput": 1506
|
||||||
},
|
},
|
||||||
"src/posenet/decodePose.ts": {
|
"src/posenet/decodePose.ts": {
|
||||||
"bytesInOutput": 1282
|
"bytesInOutput": 4649
|
||||||
},
|
},
|
||||||
"src/posenet/vectors.ts": {
|
"src/posenet/vectors.ts": {
|
||||||
"bytesInOutput": 345
|
"bytesInOutput": 772
|
||||||
},
|
},
|
||||||
"src/posenet/decoders.ts": {
|
"src/posenet/decoders.ts": {
|
||||||
"bytesInOutput": 823
|
"bytesInOutput": 1937
|
||||||
},
|
},
|
||||||
"src/posenet/decodeMultiple.ts": {
|
"src/posenet/decodeMultiple.ts": {
|
||||||
"bytesInOutput": 553
|
"bytesInOutput": 1710
|
||||||
},
|
},
|
||||||
"src/posenet/util.ts": {
|
"src/posenet/util.ts": {
|
||||||
"bytesInOutput": 352
|
"bytesInOutput": 788
|
||||||
},
|
},
|
||||||
"src/handpose/handpose.ts": {
|
"src/handpose/handpose.ts": {
|
||||||
"bytesInOutput": 1322
|
"bytesInOutput": 2374
|
||||||
},
|
},
|
||||||
"src/handpose/handdetector.ts": {
|
"src/handpose/handdetector.ts": {
|
||||||
"bytesInOutput": 1813
|
"bytesInOutput": 3551
|
||||||
},
|
},
|
||||||
"src/handpose/box.ts": {
|
"src/handpose/box.ts": {
|
||||||
"bytesInOutput": 958
|
"bytesInOutput": 1945
|
||||||
},
|
},
|
||||||
"src/handpose/handpipeline.ts": {
|
"src/handpose/handpipeline.ts": {
|
||||||
"bytesInOutput": 2531
|
"bytesInOutput": 5956
|
||||||
},
|
},
|
||||||
"src/handpose/util.ts": {
|
"src/handpose/util.ts": {
|
||||||
"bytesInOutput": 806
|
"bytesInOutput": 2198
|
||||||
},
|
},
|
||||||
"src/handpose/anchors.ts": {
|
"src/handpose/anchors.ts": {
|
||||||
"bytesInOutput": 126985
|
"bytesInOutput": 221202
|
||||||
},
|
},
|
||||||
"src/gesture/gesture.ts": {
|
"src/gesture/gesture.ts": {
|
||||||
"bytesInOutput": 2391
|
"bytesInOutput": 4071
|
||||||
},
|
},
|
||||||
"src/image.ts": {
|
"src/image.ts": {
|
||||||
"bytesInOutput": 2339
|
"bytesInOutput": 4539
|
||||||
},
|
},
|
||||||
"src/imagefx.js": {
|
"src/imagefx.js": {
|
||||||
"bytesInOutput": 10973
|
"bytesInOutput": 18690
|
||||||
},
|
},
|
||||||
"config.js": {
|
"config.js": {
|
||||||
"bytesInOutput": 1453
|
"bytesInOutput": 2225
|
||||||
},
|
},
|
||||||
"src/sample.ts": {
|
"src/sample.ts": {
|
||||||
"bytesInOutput": 55295
|
"bytesInOutput": 55311
|
||||||
},
|
},
|
||||||
"package.json": {
|
"package.json": {
|
||||||
"bytesInOutput": 2569
|
"bytesInOutput": 3341
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bytes": 276986
|
"bytes": 444119
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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
File diff suppressed because it is too large
Load Diff
|
@ -7,8 +7,7 @@ const app = require('../package.json');
|
||||||
|
|
||||||
const git = simpleGit();
|
const git = simpleGit();
|
||||||
|
|
||||||
let text = `
|
let text = `# ${app.name}
|
||||||
# ${app.name}
|
|
||||||
|
|
||||||
Version: **${app.version}**
|
Version: **${app.version}**
|
||||||
Description: **${app.description}**
|
Description: **${app.description}**
|
||||||
|
@ -26,12 +25,18 @@ async function update(f) {
|
||||||
const log = gitLog.all.sort((a, b) => (new Date(b.date).getTime() - new Date(a.date).getTime()));
|
const log = gitLog.all.sort((a, b) => (new Date(b.date).getTime() - new Date(a.date).getTime()));
|
||||||
|
|
||||||
let previous = '';
|
let previous = '';
|
||||||
|
const headings = [];
|
||||||
for (const l of log) {
|
for (const l of log) {
|
||||||
const msg = l.message.toLowerCase();
|
const msg = l.message.toLowerCase();
|
||||||
if ((l.refs !== '') || msg.match(/^[0-9].[0-9].[0-9]/)) {
|
if ((l.refs !== '') || msg.match(/^[0-99].[0-99].[0-99]/)) {
|
||||||
const dt = dayjs(l.date).format('YYYY/MM/DD');
|
const dt = dayjs(l.date).format('YYYY/MM/DD');
|
||||||
const ver = msg.match(/[0-9].[0-9].[0-9]/) ? msg : l.refs;
|
let ver = msg.match(/[0-99].[0-99].[0-99]/) ? msg : l.refs;
|
||||||
text += `\n### **${ver}** ${dt} ${l.author_email}\n`;
|
ver = ver.replace('tag: v', '').replace('tag: ', 'release: ').split(',')[0];
|
||||||
|
const heading = `\n### **${ver}** ${dt} ${l.author_email}\n\n`;
|
||||||
|
if (!headings.includes(heading) && !ver.startsWith('tag')) {
|
||||||
|
headings.push(heading);
|
||||||
|
text += heading;
|
||||||
|
}
|
||||||
} else if ((msg.length > 2) && !msg.startsWith('update') && (previous !== msg)) {
|
} else if ((msg.length > 2) && !msg.startsWith('update') && (previous !== msg)) {
|
||||||
previous = msg;
|
previous = msg;
|
||||||
text += `- ${msg}\n`;
|
text += `- ${msg}\n`;
|
||||||
|
|
|
@ -87,7 +87,7 @@ async function watch() {
|
||||||
function handle(url) {
|
function handle(url) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let obj = { ok: false };
|
let obj = { ok: false };
|
||||||
obj.file = url;
|
obj.file = decodeURI(url);
|
||||||
if (!fs.existsSync(obj.file)) resolve(null);
|
if (!fs.existsSync(obj.file)) resolve(null);
|
||||||
obj.stat = fs.statSync(obj.file);
|
obj.stat = fs.statSync(obj.file);
|
||||||
if (obj.stat.isFile()) obj.ok = true;
|
if (obj.stat.isFile()) obj.ok = true;
|
||||||
|
|
|
@ -22,9 +22,7 @@ export class MediaPipeFaceMesh {
|
||||||
const meshRaw = prediction.rawCoords;
|
const meshRaw = prediction.rawCoords;
|
||||||
const annotations = {};
|
const annotations = {};
|
||||||
if (mesh && mesh.length > 0) {
|
if (mesh && mesh.length > 0) {
|
||||||
for (const key of Object.keys(coords.MESH_ANNOTATIONS)) {
|
for (const key of Object.keys(coords.MESH_ANNOTATIONS)) annotations[key] = coords.MESH_ANNOTATIONS[key].map((index) => mesh[index]);
|
||||||
annotations[key] = coords.MESH_ANNOTATIONS[key].map((index) => mesh[index]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const boxRaw = (config.face.mesh.returnRawData && prediction.box) ? { topLeft: prediction.box.startPoint, bottomRight: prediction.box.endPoint } : null;
|
const boxRaw = (config.face.mesh.returnRawData && prediction.box) ? { topLeft: prediction.box.startPoint, bottomRight: prediction.box.endPoint } : null;
|
||||||
const box = prediction.box ? [
|
const box = prediction.box ? [
|
||||||
|
@ -33,7 +31,17 @@ export class MediaPipeFaceMesh {
|
||||||
Math.min(input.shape[2], prediction.box.endPoint[0]) - prediction.box.startPoint[0],
|
Math.min(input.shape[2], prediction.box.endPoint[0]) - prediction.box.startPoint[0],
|
||||||
Math.min(input.shape[1], prediction.box.endPoint[1]) - prediction.box.startPoint[1],
|
Math.min(input.shape[1], prediction.box.endPoint[1]) - prediction.box.startPoint[1],
|
||||||
] : 0;
|
] : 0;
|
||||||
results.push({ confidence: prediction.confidence || 0, box, mesh, boxRaw, meshRaw, annotations, image: prediction.image ? tf.clone(prediction.image) : null });
|
results.push({
|
||||||
|
confidence: prediction.faceConfidence || prediction.boxConfidence || 0,
|
||||||
|
boxConfidence: prediction.boxConfidence,
|
||||||
|
faceConfidence: prediction.faceConfidence,
|
||||||
|
box,
|
||||||
|
mesh,
|
||||||
|
boxRaw,
|
||||||
|
meshRaw,
|
||||||
|
annotations,
|
||||||
|
image: prediction.image ? tf.clone(prediction.image) : null,
|
||||||
|
});
|
||||||
if (prediction.coords) prediction.coords.dispose();
|
if (prediction.coords) prediction.coords.dispose();
|
||||||
if (prediction.image) prediction.image.dispose();
|
if (prediction.image) prediction.image.dispose();
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,7 +251,7 @@ export class Pipeline {
|
||||||
coords: transformedCoords,
|
coords: transformedCoords,
|
||||||
box: landmarksBox,
|
box: landmarksBox,
|
||||||
faceConfidence,
|
faceConfidence,
|
||||||
confidence: box.confidence,
|
boxConfidence: box.confidence,
|
||||||
image: face,
|
image: face,
|
||||||
rawCoords,
|
rawCoords,
|
||||||
};
|
};
|
||||||
|
|
|
@ -235,7 +235,7 @@ class Human {
|
||||||
let genderRes;
|
let genderRes;
|
||||||
let emotionRes;
|
let emotionRes;
|
||||||
let embeddingRes;
|
let embeddingRes;
|
||||||
const faceRes: Array<{ confidence: number, box: any, mesh: any, meshRaw: any, boxRaw: any, annotations: any, age: number, gender: string, genderConfidence: number, emotion: string, embedding: any, iris: number }> = [];
|
const faceRes: Array<{ confidence: number, boxConfidence: number, faceConfidence: number, box: any, mesh: any, meshRaw: any, boxRaw: any, annotations: any, age: number, gender: string, genderConfidence: number, emotion: string, embedding: any, iris: number }> = [];
|
||||||
this.state = 'run:face';
|
this.state = 'run:face';
|
||||||
timeStamp = now();
|
timeStamp = now();
|
||||||
const faces = await this.models.face?.estimateFaces(input, this.config);
|
const faces = await this.models.face?.estimateFaces(input, this.config);
|
||||||
|
@ -316,6 +316,8 @@ class Human {
|
||||||
// combine results
|
// combine results
|
||||||
faceRes.push({
|
faceRes.push({
|
||||||
confidence: face.confidence,
|
confidence: face.confidence,
|
||||||
|
faceConfidence: face.faceConfidence,
|
||||||
|
boxConfidence: face.boxConfidence,
|
||||||
box: face.box,
|
box: face.box,
|
||||||
mesh: face.mesh,
|
mesh: face.mesh,
|
||||||
boxRaw: face.boxRaw,
|
boxRaw: face.boxRaw,
|
||||||
|
|
|
@ -26,6 +26,8 @@ declare class Human {
|
||||||
checkBackend(force?: boolean): Promise<void>;
|
checkBackend(force?: boolean): Promise<void>;
|
||||||
detectFace(input: any): Promise<{
|
detectFace(input: any): Promise<{
|
||||||
confidence: number;
|
confidence: number;
|
||||||
|
boxConfidence: number;
|
||||||
|
faceConfidence: number;
|
||||||
box: any;
|
box: any;
|
||||||
mesh: any;
|
mesh: any;
|
||||||
meshRaw: any;
|
meshRaw: any;
|
||||||
|
|
Loading…
Reference in New Issue