mirror of https://github.com/vladmandic/human
added visual results browser to demo
parent
246dd7a977
commit
c495d93dc8
|
@ -11,6 +11,7 @@
|
|||
|
||||
### **HEAD -> main** 2021/09/14 mandic00@live.com
|
||||
|
||||
- experimental custom tfjs bundle - disabled
|
||||
- add platform and backend capabilities detection
|
||||
- enhanced automated tests
|
||||
- enable canvas patching for nodejs
|
||||
|
|
|
@ -128,6 +128,12 @@ All options as presented in the demo application...
|
|||
|
||||
<br>
|
||||
|
||||
**Results Browser:**
|
||||
[ *Demo -> Display -> Show Results* ]<br>
|
||||

|
||||
|
||||
<br>
|
||||
|
||||
**Face Similarity Matching:**
|
||||
Extracts all faces from provided input images,
|
||||
sorts them by similarity to selected face
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
|
@ -0,0 +1,161 @@
|
|||
let callbackFunction = null;
|
||||
|
||||
function createElement(type, config) {
|
||||
const htmlElement = document.createElement(type);
|
||||
if (config === undefined) return htmlElement;
|
||||
if (config.className) htmlElement.className = config.className;
|
||||
if (config.content) htmlElement.textContent = config.content;
|
||||
if (config.style) htmlElement.style = config.style;
|
||||
if (config.children) config.children.forEach((el) => !el || htmlElement.appendChild(el));
|
||||
return htmlElement;
|
||||
}
|
||||
|
||||
function createExpandedElement(node) {
|
||||
const iElem = createElement('i');
|
||||
if (node.expanded) { iElem.className = 'fas fa-caret-down'; } else { iElem.className = 'fas fa-caret-right'; }
|
||||
const caretElem = createElement('div', { style: 'width: 18px; text-align: center; cursor: pointer', children: [iElem] });
|
||||
const handleClick = node.toggle.bind(node);
|
||||
caretElem.addEventListener('click', handleClick);
|
||||
const indexElem = createElement('div', { className: 'json json-index', content: node.key });
|
||||
indexElem.addEventListener('click', handleClick);
|
||||
const typeElem = createElement('div', { className: 'json json-type', content: node.type });
|
||||
const keyElem = createElement('div', { className: 'json json-key', content: node.key });
|
||||
keyElem.addEventListener('click', handleClick);
|
||||
const sizeElem = createElement('div', { className: 'json json-size' });
|
||||
sizeElem.addEventListener('click', handleClick);
|
||||
if (node.type === 'array') {
|
||||
sizeElem.innerText = `[${node.children.length} items]`;
|
||||
} else if (node.type === 'object') {
|
||||
const size = node.children.find((item) => item.key === 'size');
|
||||
sizeElem.innerText = size ? `{${size.value.toLocaleString()} bytes}` : `{${node.children.length} properties}`;
|
||||
}
|
||||
let lineChildren;
|
||||
if (node.key === null) lineChildren = [caretElem, typeElem, sizeElem];
|
||||
else if (node.parent.type === 'array') lineChildren = [caretElem, indexElem, sizeElem];
|
||||
else lineChildren = [caretElem, keyElem, sizeElem];
|
||||
const lineElem = createElement('div', { className: 'json-line', children: lineChildren });
|
||||
if (node.depth > 0) lineElem.style = `margin-left: ${node.depth * 24}px;`;
|
||||
return lineElem;
|
||||
}
|
||||
|
||||
function createNotExpandedElement(node) {
|
||||
const caretElem = createElement('div', { style: 'width: 18px' });
|
||||
const keyElem = createElement('div', { className: 'json json-key', content: node.key });
|
||||
const separatorElement = createElement('div', { className: 'json-separator', content: ':' });
|
||||
const valueType = ` json-${typeof node.value}`;
|
||||
const valueContent = node.value.toLocaleString();
|
||||
const valueElement = createElement('div', { className: `json json-value${valueType}`, content: valueContent });
|
||||
const lineElem = createElement('div', { className: 'json-line', children: [caretElem, keyElem, separatorElement, valueElement] });
|
||||
if (node.depth > 0) lineElem.style = `margin-left: ${node.depth * 24}px;`;
|
||||
return lineElem;
|
||||
}
|
||||
|
||||
function createNode() {
|
||||
return {
|
||||
key: '',
|
||||
parent: {},
|
||||
value: null,
|
||||
expanded: false,
|
||||
type: '',
|
||||
children: [],
|
||||
elem: {},
|
||||
depth: 0,
|
||||
|
||||
hideChildren() {
|
||||
if (Array.isArray(this.children)) {
|
||||
this.children.forEach((item) => {
|
||||
// @ts-ignore
|
||||
item['elem']['classList'].add('hide');
|
||||
// @ts-ignore
|
||||
if (item['expanded']) item.hideChildren();
|
||||
});
|
||||
}
|
||||
},
|
||||
showChildren() {
|
||||
if (Array.isArray(this.children)) {
|
||||
this.children.forEach((item) => {
|
||||
// @ts-ignore
|
||||
item['elem']['classList'].remove('hide');
|
||||
// @ts-ignore
|
||||
if (item['expanded']) item.showChildren();
|
||||
});
|
||||
}
|
||||
},
|
||||
toggle() {
|
||||
if (this.expanded) {
|
||||
this.hideChildren();
|
||||
const icon = this.elem?.querySelector('.fas');
|
||||
icon.classList.replace('fa-caret-down', 'fa-caret-right');
|
||||
if (callbackFunction !== null) callbackFunction(null);
|
||||
} else {
|
||||
this.showChildren();
|
||||
const icon = this.elem?.querySelector('.fas');
|
||||
icon.classList.replace('fa-caret-right', 'fa-caret-down');
|
||||
if (this.type === 'object') {
|
||||
if (callbackFunction !== null) callbackFunction(`${this.parent?.key}/${this.key}`);
|
||||
}
|
||||
}
|
||||
this.expanded = !this.expanded;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getType(val) {
|
||||
let type
|
||||
if (Array.isArray(val)) type = 'array';
|
||||
else if (val === null) type = 'null';
|
||||
else type = typeof val;
|
||||
return type;
|
||||
}
|
||||
|
||||
function traverseObject(obj, parent, filter) {
|
||||
for (const key in obj) {
|
||||
const child = createNode();
|
||||
child.parent = parent;
|
||||
child.key = key;
|
||||
child.type = getType(obj[key]);
|
||||
child.depth = parent.depth + 1;
|
||||
child.expanded = false;
|
||||
if (Array.isArray(filter)) {
|
||||
for (const filtered of filter) {
|
||||
if (key === filtered) return;
|
||||
}
|
||||
}
|
||||
if (typeof obj[key] === 'object') {
|
||||
child.children = [];
|
||||
parent.children.push(child);
|
||||
traverseObject(obj[key], child, filter);
|
||||
child.elem = createExpandedElement(child);
|
||||
} else {
|
||||
child.value = obj[key];
|
||||
child.elem = createNotExpandedElement(child);
|
||||
parent.children.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createTree(obj, title, filter) {
|
||||
const tree = createNode();
|
||||
tree.type = title;
|
||||
tree.key = title;
|
||||
tree.children = [];
|
||||
tree.expanded = true;
|
||||
traverseObject(obj, tree, filter);
|
||||
tree.elem = createExpandedElement(tree);
|
||||
return tree;
|
||||
}
|
||||
|
||||
function traverseTree(node, callback) {
|
||||
callback(node);
|
||||
if (node.children !== null) node.children.forEach((item) => traverseTree(item, callback));
|
||||
}
|
||||
|
||||
async function jsonView(json, element, title = '', filter = []) {
|
||||
const tree = createTree(json, title, filter);
|
||||
traverseTree(tree, (node) => {
|
||||
if (!node.expanded) node.hideChildren();
|
||||
element.appendChild(node.elem);
|
||||
});
|
||||
}
|
||||
|
||||
export default jsonView;
|
|
@ -66,6 +66,24 @@
|
|||
.icon:hover { background: #505050; filter: grayscale(0); }
|
||||
.hint { opacity: 0; transition-duration: 0.5s; transition-property: opacity; font-style: italic; position: fixed; top: 5rem; padding: 8px; margin: 8px; box-shadow: 0 0 2px 2px #303030; }
|
||||
.input-file { align-self: center; width: 5rem; }
|
||||
|
||||
.results { position: absolute; left: 0; top: 6rem; background: #303030; width: 20rem; height: 90%; font-size: 0.8rem; overflow-y: auto; display: none }
|
||||
.results::-webkit-scrollbar { background-color: #303030; }
|
||||
.results::-webkit-scrollbar-thumb { background: black; border-radius: 10px; }
|
||||
.json-line { margin: 4px 0; display: flex; justify-content: flex-start; }
|
||||
.json { margin-right: 8px; margin-left: 8px; }
|
||||
.json-type { color: lightyellow; }
|
||||
.json-key { color: white; }
|
||||
.json-index { color: lightcoral; }
|
||||
.json-value { margin-left: 20px; }
|
||||
.json-number { color: lightgreen; }
|
||||
.json-boolean { color: lightyellow; }
|
||||
.json-string { color: lightblue; }
|
||||
.json-size { color: gray; }
|
||||
.hide { display: none; }
|
||||
.fas { display: inline-block; width: 0; height: 0; border-style: solid; }
|
||||
.fa-caret-down { border-width: 10px 8px 0 8px; border-color: white transparent }
|
||||
.fa-caret-right { border-width: 10px 0 8px 10px; border-color: transparent transparent transparent white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -95,5 +113,6 @@
|
|||
<div id="samples-container" class="samples-container"></div>
|
||||
<div id="hint" class="hint"></div>
|
||||
<div id="log" class="log"></div>
|
||||
<div id="results" class="results"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -26,6 +26,7 @@ import Human from '../dist/human.esm.js'; // equivalent of @vladmandic/human
|
|||
import Menu from './helpers/menu.js';
|
||||
import GLBench from './helpers/gl-bench.js';
|
||||
import webRTC from './helpers/webrtc.js';
|
||||
import jsonView from './helpers/jsonview.js';
|
||||
|
||||
let human;
|
||||
|
||||
|
@ -99,6 +100,7 @@ const ui = {
|
|||
framesDraw: 0, // internal, statistics on frames drawn
|
||||
framesDetect: 0, // internal, statistics on frames detected
|
||||
bench: true, // show gl fps benchmark window
|
||||
results: false, // show results tree
|
||||
lastFrame: 0, // time of last frame processing
|
||||
viewportSet: false, // internal, has custom viewport been set
|
||||
background: null, // holds instance of segmentation background image
|
||||
|
@ -250,6 +252,14 @@ async function drawResults(input) {
|
|||
} else {
|
||||
human.draw.all(canvas, result, drawOptions);
|
||||
}
|
||||
|
||||
// show tree with results
|
||||
if (ui.results) {
|
||||
const div = document.getElementById('results');
|
||||
div.innerHTML = '';
|
||||
jsonView(result, div, 'Results', ['canvas', 'timestamp']);
|
||||
}
|
||||
|
||||
/* alternatively use individual functions
|
||||
human.draw.face(canvas, result.face);
|
||||
human.draw.body(canvas, result.body);
|
||||
|
@ -631,6 +641,10 @@ function setupMenu() {
|
|||
const top = `${document.getElementById('menubar').clientHeight}px`;
|
||||
|
||||
menu.display = new Menu(document.body, '', { top, left: x[0] });
|
||||
menu.display.addBool('results tree', ui, 'results', (val) => {
|
||||
ui.results = val;
|
||||
document.getElementById('results').style.display = ui.results ? 'block' : 'none';
|
||||
});
|
||||
menu.display.addBool('perf monitor', ui, 'bench', (val) => ui.bench = val);
|
||||
menu.display.addBool('buffer output', ui, 'buffered', (val) => ui.buffered = val);
|
||||
menu.display.addBool('crop & scale', ui, 'crop', (val) => {
|
||||
|
@ -988,6 +1002,7 @@ async function main() {
|
|||
status('human: ready');
|
||||
document.getElementById('loader').style.display = 'none';
|
||||
document.getElementById('play').style.display = 'block';
|
||||
document.getElementById('results').style.display = 'none';
|
||||
|
||||
// init drag & drop
|
||||
await dragAndDrop();
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "^4.31.1",
|
||||
"@typescript-eslint/parser": "^4.31.1",
|
||||
"@vladmandic/build": "^0.4.1",
|
||||
"@vladmandic/pilogger": "^0.3.1",
|
||||
"@vladmandic/pilogger": "^0.3.2",
|
||||
"canvas": "^2.8.0",
|
||||
"dayjs": "^1.10.7",
|
||||
"esbuild": "^0.12.28",
|
||||
|
|
1010
test/build.log
1010
test/build.log
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue