human/demo/tracker/index.js

1357 lines
50 KiB
JavaScript

/*
Human
homepage: <https://github.com/vladmandic/human>
author: <https://github.com/vladmandic>'
*/
// demo/tracker/index.ts
import * as H from "../../dist/human.esm.js";
// demo/tracker/tracker.js
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var require_rng_browser = __commonJS({
"node_modules/.pnpm/uuid@3.2.1/node_modules/uuid/lib/rng-browser.js"(exports, module) {
var getRandomValues = typeof crypto != "undefined" && crypto.getRandomValues.bind(crypto) || typeof msCrypto != "undefined" && msCrypto.getRandomValues.bind(msCrypto);
if (getRandomValues) {
rnds8 = new Uint8Array(16);
module.exports = __name(function whatwgRNG() {
getRandomValues(rnds8);
return rnds8;
}, "whatwgRNG");
} else {
rnds = new Array(16);
module.exports = __name(function mathRNG() {
for (var i = 0, r; i < 16; i++) {
if ((i & 3) === 0)
r = Math.random() * 4294967296;
rnds[i] = r >>> ((i & 3) << 3) & 255;
}
return rnds;
}, "mathRNG");
}
var rnds8;
var rnds;
}
});
var require_bytesToUuid = __commonJS({
"node_modules/.pnpm/uuid@3.2.1/node_modules/uuid/lib/bytesToUuid.js"(exports, module) {
var byteToHex = [];
for (i = 0; i < 256; ++i) {
byteToHex[i] = (i + 256).toString(16).substr(1);
}
function bytesToUuid(buf, offset) {
var i2 = offset || 0;
var bth = byteToHex;
return bth[buf[i2++]] + bth[buf[i2++]] + bth[buf[i2++]] + bth[buf[i2++]] + "-" + bth[buf[i2++]] + bth[buf[i2++]] + "-" + bth[buf[i2++]] + bth[buf[i2++]] + "-" + bth[buf[i2++]] + bth[buf[i2++]] + "-" + bth[buf[i2++]] + bth[buf[i2++]] + bth[buf[i2++]] + bth[buf[i2++]] + bth[buf[i2++]] + bth[buf[i2++]];
}
__name(bytesToUuid, "bytesToUuid");
module.exports = bytesToUuid;
var i;
}
});
var require_v4 = __commonJS({
"node_modules/.pnpm/uuid@3.2.1/node_modules/uuid/v4.js"(exports, module) {
var rng = require_rng_browser();
var bytesToUuid = require_bytesToUuid();
function v4(options, buf, offset) {
var i = buf && offset || 0;
if (typeof options == "string") {
buf = options === "binary" ? new Array(16) : null;
options = null;
}
options = options || {};
var rnds = options.random || (options.rng || rng)();
rnds[6] = rnds[6] & 15 | 64;
rnds[8] = rnds[8] & 63 | 128;
if (buf) {
for (var ii = 0; ii < 16; ++ii) {
buf[i + ii] = rnds[ii];
}
}
return buf || bytesToUuid(rnds);
}
__name(v4, "v4");
module.exports = v4;
}
});
var require_utils = __commonJS({
"utils.js"(exports) {
exports.isDetectionTooLarge = (detections, largestAllowed) => {
if (detections.w >= largestAllowed) {
return true;
} else {
return false;
}
};
var isInsideArea = __name((area, point) => {
const xMin = area.x - area.w / 2;
const xMax = area.x + area.w / 2;
const yMin = area.y - area.h / 2;
const yMax = area.y + area.h / 2;
if (point.x >= xMin && point.x <= xMax && point.y >= yMin && point.y <= yMax) {
return true;
} else {
return false;
}
}, "isInsideArea");
exports.isInsideArea = isInsideArea;
exports.isInsideSomeAreas = (areas, point) => {
const isInside = areas.some((area) => isInsideArea(area, point));
return isInside;
};
exports.ignoreObjectsNotToDetect = (detections, objectsToDetect) => {
return detections.filter((detection) => objectsToDetect.indexOf(detection.name) > -1);
};
var getRectangleEdges = __name((item) => {
return {
x0: item.x - item.w / 2,
y0: item.y - item.h / 2,
x1: item.x + item.w / 2,
y1: item.y + item.h / 2
};
}, "getRectangleEdges");
exports.getRectangleEdges = getRectangleEdges;
exports.iouAreas = (item1, item2) => {
var rect1 = getRectangleEdges(item1);
var rect2 = getRectangleEdges(item2);
var overlap_x0 = Math.max(rect1.x0, rect2.x0);
var overlap_y0 = Math.max(rect1.y0, rect2.y0);
var overlap_x1 = Math.min(rect1.x1, rect2.x1);
var overlap_y1 = Math.min(rect1.y1, rect2.y1);
if (overlap_x1 - overlap_x0 <= 0 || overlap_y1 - overlap_y0 <= 0) {
return 0;
} else {
const area_rect1 = item1.w * item1.h;
const area_rect2 = item2.w * item2.h;
const area_intersection = (overlap_x1 - overlap_x0) * (overlap_y1 - overlap_y0);
const area_union = area_rect1 + area_rect2 - area_intersection;
return area_intersection / area_union;
}
};
exports.computeVelocityVector = (item1, item2, nbFrame) => {
return {
dx: (item2.x - item1.x) / nbFrame,
dy: (item2.y - item1.y) / nbFrame
};
};
exports.computeBearingIn360 = function(dx, dy) {
var angle = Math.atan(dx / dy) / (Math.PI / 180);
if (angle > 0) {
if (dy > 0)
return angle;
else
return 180 + angle;
} else {
if (dx > 0)
return 180 + angle;
else
return 360 + angle;
}
};
}
});
var require_ItemTracked = __commonJS({
"ItemTracked.js"(exports) {
var uuidv4 = require_v4();
var computeBearingIn360 = require_utils().computeBearingIn360;
var computeVelocityVector = require_utils().computeVelocityVector;
exports.ITEM_HISTORY_MAX_LENGTH = 15;
var idDisplay = 0;
exports.ItemTracked = function(properties, frameNb, unMatchedFramesTolerance, fastDelete) {
var DEFAULT_UNMATCHEDFRAMES_TOLERANCE = unMatchedFramesTolerance;
var itemTracked = {};
itemTracked.available = true;
itemTracked.delete = false;
itemTracked.fastDelete = fastDelete;
itemTracked.frameUnmatchedLeftBeforeDying = unMatchedFramesTolerance;
itemTracked.isZombie = false;
itemTracked.appearFrame = frameNb;
itemTracked.disappearFrame = null;
itemTracked.disappearArea = {};
itemTracked.nameCount = {};
itemTracked.nameCount[properties.name] = 1;
itemTracked.x = properties.x;
itemTracked.y = properties.y;
itemTracked.w = properties.w;
itemTracked.h = properties.h;
itemTracked.name = properties.name;
itemTracked.confidence = properties.confidence;
itemTracked.itemHistory = [];
itemTracked.itemHistory.push({
x: properties.x,
y: properties.y,
w: properties.w,
h: properties.h,
confidence: properties.confidence
});
if (itemTracked.itemHistory.length >= exports.ITEM_HISTORY_MAX_LENGTH) {
itemTracked.itemHistory.shift();
}
itemTracked.velocity = {
dx: 0,
dy: 0
};
itemTracked.nbTimeMatched = 1;
itemTracked.id = uuidv4();
itemTracked.idDisplay = idDisplay;
idDisplay++;
itemTracked.update = function(properties2, frameNb2) {
if (this.disappearFrame) {
this.disappearFrame = null;
this.disappearArea = {};
}
this.isZombie = false;
this.nbTimeMatched += 1;
this.x = properties2.x;
this.y = properties2.y;
this.w = properties2.w;
this.h = properties2.h;
this.confidence = properties2.confidence;
this.itemHistory.push({
x: this.x,
y: this.y,
w: this.w,
h: this.h,
confidence: this.confidence
});
if (itemTracked.itemHistory.length >= exports.ITEM_HISTORY_MAX_LENGTH) {
itemTracked.itemHistory.shift();
}
this.name = properties2.name;
if (this.nameCount[properties2.name]) {
this.nameCount[properties2.name]++;
} else {
this.nameCount[properties2.name] = 1;
}
this.frameUnmatchedLeftBeforeDying = DEFAULT_UNMATCHEDFRAMES_TOLERANCE;
this.velocity = this.updateVelocityVector();
};
itemTracked.makeAvailable = function() {
this.available = true;
return this;
};
itemTracked.makeUnavailable = function() {
this.available = false;
return this;
};
itemTracked.countDown = function(frameNb2) {
if (this.disappearFrame === null) {
this.disappearFrame = frameNb2;
this.disappearArea = {
x: this.x,
y: this.y,
w: this.w,
h: this.h
};
}
this.frameUnmatchedLeftBeforeDying--;
this.isZombie = true;
if (this.fastDelete && this.nbTimeMatched <= 1) {
this.frameUnmatchedLeftBeforeDying = -1;
}
};
itemTracked.updateTheoricalPositionAndSize = function() {
this.itemHistory.push({
x: this.x,
y: this.y,
w: this.w,
h: this.h,
confidence: this.confidence
});
if (itemTracked.itemHistory.length >= exports.ITEM_HISTORY_MAX_LENGTH) {
itemTracked.itemHistory.shift();
}
this.x = this.x + this.velocity.dx;
this.y = this.y + this.velocity.dy;
};
itemTracked.predictNextPosition = function() {
return {
x: this.x + this.velocity.dx,
y: this.y + this.velocity.dy,
w: this.w,
h: this.h
};
};
itemTracked.isDead = function() {
return this.frameUnmatchedLeftBeforeDying < 0;
};
itemTracked.updateVelocityVector = function() {
if (exports.ITEM_HISTORY_MAX_LENGTH <= 2) {
return { dx: void 0, dy: void 0 };
}
if (this.itemHistory.length <= exports.ITEM_HISTORY_MAX_LENGTH) {
const start = this.itemHistory[0];
const end = this.itemHistory[this.itemHistory.length - 1];
return computeVelocityVector(start, end, this.itemHistory.length);
} else {
const start = this.itemHistory[this.itemHistory.length - exports.ITEM_HISTORY_MAX_LENGTH];
const end = this.itemHistory[this.itemHistory.length - 1];
return computeVelocityVector(start, end, exports.ITEM_HISTORY_MAX_LENGTH);
}
};
itemTracked.getMostlyMatchedName = function() {
var nameMostlyMatchedOccurences = 0;
var nameMostlyMatched = "";
Object.keys(this.nameCount).map((name) => {
if (this.nameCount[name] > nameMostlyMatchedOccurences) {
nameMostlyMatched = name;
nameMostlyMatchedOccurences = this.nameCount[name];
}
});
return nameMostlyMatched;
};
itemTracked.toJSONDebug = function(roundInt = true) {
return {
id: this.id,
idDisplay: this.idDisplay,
x: roundInt ? parseInt(this.x, 10) : this.x,
y: roundInt ? parseInt(this.y, 10) : this.y,
w: roundInt ? parseInt(this.w, 10) : this.w,
h: roundInt ? parseInt(this.h, 10) : this.h,
confidence: Math.round(this.confidence * 100) / 100,
// Here we negate dy to be in "normal" carthesian coordinates
bearing: parseInt(computeBearingIn360(this.velocity.dx, -this.velocity.dy)),
name: this.getMostlyMatchedName(),
isZombie: this.isZombie,
appearFrame: this.appearFrame,
disappearFrame: this.disappearFrame
};
};
itemTracked.toJSON = function(roundInt = true) {
return {
id: this.idDisplay,
x: roundInt ? parseInt(this.x, 10) : this.x,
y: roundInt ? parseInt(this.y, 10) : this.y,
w: roundInt ? parseInt(this.w, 10) : this.w,
h: roundInt ? parseInt(this.h, 10) : this.h,
confidence: Math.round(this.confidence * 100) / 100,
// Here we negate dy to be in "normal" carthesian coordinates
bearing: parseInt(computeBearingIn360(this.velocity.dx, -this.velocity.dy), 10),
name: this.getMostlyMatchedName(),
isZombie: this.isZombie
};
};
itemTracked.toMOT = function(frameIndex) {
return `${frameIndex},${this.idDisplay},${this.x - this.w / 2},${this.y - this.h / 2},${this.w},${this.h},${this.confidence / 100},-1,-1,-1`;
};
itemTracked.toJSONGenericInfo = function() {
return {
id: this.id,
idDisplay: this.idDisplay,
appearFrame: this.appearFrame,
disappearFrame: this.disappearFrame,
disappearArea: this.disappearArea,
nbActiveFrame: this.disappearFrame - this.appearFrame,
name: this.getMostlyMatchedName()
};
};
return itemTracked;
};
exports.reset = function() {
idDisplay = 0;
};
}
});
var require_kdTree_min = __commonJS({
"lib/kdTree-min.js"(exports) {
(function(root, factory) {
if (typeof define === "function" && define.amd) {
define(["exports"], factory);
} else if (typeof exports === "object") {
factory(exports);
} else {
factory(root);
}
})(exports, function(exports2) {
function Node(obj, dimension, parent) {
this.obj = obj;
this.left = null;
this.right = null;
this.parent = parent;
this.dimension = dimension;
}
__name(Node, "Node");
function kdTree(points, metric, dimensions) {
var self = this;
function buildTree(points2, depth, parent) {
var dim = depth % dimensions.length, median, node;
if (points2.length === 0) {
return null;
}
if (points2.length === 1) {
return new Node(points2[0], dim, parent);
}
points2.sort(function(a, b) {
return a[dimensions[dim]] - b[dimensions[dim]];
});
median = Math.floor(points2.length / 2);
node = new Node(points2[median], dim, parent);
node.left = buildTree(points2.slice(0, median), depth + 1, node);
node.right = buildTree(points2.slice(median + 1), depth + 1, node);
return node;
}
__name(buildTree, "buildTree");
function loadTree(data) {
self.root = data;
function restoreParent(root) {
if (root.left) {
root.left.parent = root;
restoreParent(root.left);
}
if (root.right) {
root.right.parent = root;
restoreParent(root.right);
}
}
__name(restoreParent, "restoreParent");
restoreParent(self.root);
}
__name(loadTree, "loadTree");
if (!Array.isArray(points))
loadTree(points, metric, dimensions);
else
this.root = buildTree(points, 0, null);
this.toJSON = function(src) {
if (!src)
src = this.root;
var dest = new Node(src.obj, src.dimension, null);
if (src.left)
dest.left = self.toJSON(src.left);
if (src.right)
dest.right = self.toJSON(src.right);
return dest;
};
this.insert = function(point) {
function innerSearch(node, parent) {
if (node === null) {
return parent;
}
var dimension2 = dimensions[node.dimension];
if (point[dimension2] < node.obj[dimension2]) {
return innerSearch(node.left, node);
} else {
return innerSearch(node.right, node);
}
}
__name(innerSearch, "innerSearch");
var insertPosition = innerSearch(this.root, null), newNode, dimension;
if (insertPosition === null) {
this.root = new Node(point, 0, null);
return;
}
newNode = new Node(point, (insertPosition.dimension + 1) % dimensions.length, insertPosition);
dimension = dimensions[insertPosition.dimension];
if (point[dimension] < insertPosition.obj[dimension]) {
insertPosition.left = newNode;
} else {
insertPosition.right = newNode;
}
};
this.remove = function(point) {
var node;
function nodeSearch(node2) {
if (node2 === null) {
return null;
}
if (node2.obj === point) {
return node2;
}
var dimension = dimensions[node2.dimension];
if (point[dimension] < node2.obj[dimension]) {
return nodeSearch(node2.left, node2);
} else {
return nodeSearch(node2.right, node2);
}
}
__name(nodeSearch, "nodeSearch");
function removeNode(node2) {
var nextNode, nextObj, pDimension;
function findMin(node3, dim) {
var dimension, own, left, right, min;
if (node3 === null) {
return null;
}
dimension = dimensions[dim];
if (node3.dimension === dim) {
if (node3.left !== null) {
return findMin(node3.left, dim);
}
return node3;
}
own = node3.obj[dimension];
left = findMin(node3.left, dim);
right = findMin(node3.right, dim);
min = node3;
if (left !== null && left.obj[dimension] < own) {
min = left;
}
if (right !== null && right.obj[dimension] < min.obj[dimension]) {
min = right;
}
return min;
}
__name(findMin, "findMin");
if (node2.left === null && node2.right === null) {
if (node2.parent === null) {
self.root = null;
return;
}
pDimension = dimensions[node2.parent.dimension];
if (node2.obj[pDimension] < node2.parent.obj[pDimension]) {
node2.parent.left = null;
} else {
node2.parent.right = null;
}
return;
}
if (node2.right !== null) {
nextNode = findMin(node2.right, node2.dimension);
nextObj = nextNode.obj;
removeNode(nextNode);
node2.obj = nextObj;
} else {
nextNode = findMin(node2.left, node2.dimension);
nextObj = nextNode.obj;
removeNode(nextNode);
node2.right = node2.left;
node2.left = null;
node2.obj = nextObj;
}
}
__name(removeNode, "removeNode");
node = nodeSearch(self.root);
if (node === null) {
return;
}
removeNode(node);
};
this.nearest = function(point, maxNodes, maxDistance) {
var i, result, bestNodes;
bestNodes = new BinaryHeap(
function(e) {
return -e[1];
}
);
function nearestSearch(node) {
var bestChild, dimension = dimensions[node.dimension], ownDistance = metric(point, node.obj), linearPoint = {}, linearDistance, otherChild, i2;
function saveNode(node2, distance) {
bestNodes.push([node2, distance]);
if (bestNodes.size() > maxNodes) {
bestNodes.pop();
}
}
__name(saveNode, "saveNode");
for (i2 = 0; i2 < dimensions.length; i2 += 1) {
if (i2 === node.dimension) {
linearPoint[dimensions[i2]] = point[dimensions[i2]];
} else {
linearPoint[dimensions[i2]] = node.obj[dimensions[i2]];
}
}
linearDistance = metric(linearPoint, node.obj);
if (node.right === null && node.left === null) {
if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
saveNode(node, ownDistance);
}
return;
}
if (node.right === null) {
bestChild = node.left;
} else if (node.left === null) {
bestChild = node.right;
} else {
if (point[dimension] < node.obj[dimension]) {
bestChild = node.left;
} else {
bestChild = node.right;
}
}
nearestSearch(bestChild);
if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
saveNode(node, ownDistance);
}
if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) {
if (bestChild === node.left) {
otherChild = node.right;
} else {
otherChild = node.left;
}
if (otherChild !== null) {
nearestSearch(otherChild);
}
}
}
__name(nearestSearch, "nearestSearch");
if (maxDistance) {
for (i = 0; i < maxNodes; i += 1) {
bestNodes.push([null, maxDistance]);
}
}
if (self.root)
nearestSearch(self.root);
result = [];
for (i = 0; i < Math.min(maxNodes, bestNodes.content.length); i += 1) {
if (bestNodes.content[i][0]) {
result.push([bestNodes.content[i][0].obj, bestNodes.content[i][1]]);
}
}
return result;
};
this.balanceFactor = function() {
function height(node) {
if (node === null) {
return 0;
}
return Math.max(height(node.left), height(node.right)) + 1;
}
__name(height, "height");
function count(node) {
if (node === null) {
return 0;
}
return count(node.left) + count(node.right) + 1;
}
__name(count, "count");
return height(self.root) / (Math.log(count(self.root)) / Math.log(2));
};
}
__name(kdTree, "kdTree");
function BinaryHeap(scoreFunction) {
this.content = [];
this.scoreFunction = scoreFunction;
}
__name(BinaryHeap, "BinaryHeap");
BinaryHeap.prototype = {
push: function(element) {
this.content.push(element);
this.bubbleUp(this.content.length - 1);
},
pop: function() {
var result = this.content[0];
var end = this.content.pop();
if (this.content.length > 0) {
this.content[0] = end;
this.sinkDown(0);
}
return result;
},
peek: function() {
return this.content[0];
},
remove: function(node) {
var len = this.content.length;
for (var i = 0; i < len; i++) {
if (this.content[i] == node) {
var end = this.content.pop();
if (i != len - 1) {
this.content[i] = end;
if (this.scoreFunction(end) < this.scoreFunction(node))
this.bubbleUp(i);
else
this.sinkDown(i);
}
return;
}
}
throw new Error("Node not found.");
},
size: function() {
return this.content.length;
},
bubbleUp: function(n) {
var element = this.content[n];
while (n > 0) {
var parentN = Math.floor((n + 1) / 2) - 1, parent = this.content[parentN];
if (this.scoreFunction(element) < this.scoreFunction(parent)) {
this.content[parentN] = element;
this.content[n] = parent;
n = parentN;
} else {
break;
}
}
},
sinkDown: function(n) {
var length = this.content.length, element = this.content[n], elemScore = this.scoreFunction(element);
while (true) {
var child2N = (n + 1) * 2, child1N = child2N - 1;
var swap = null;
if (child1N < length) {
var child1 = this.content[child1N], child1Score = this.scoreFunction(child1);
if (child1Score < elemScore)
swap = child1N;
}
if (child2N < length) {
var child2 = this.content[child2N], child2Score = this.scoreFunction(child2);
if (child2Score < (swap == null ? elemScore : child1Score)) {
swap = child2N;
}
}
if (swap != null) {
this.content[n] = this.content[swap];
this.content[swap] = element;
n = swap;
} else {
break;
}
}
}
};
exports2.kdTree = kdTree;
exports2.BinaryHeap = BinaryHeap;
});
}
});
var require_munkres = __commonJS({
"node_modules/.pnpm/munkres-js@1.2.2/node_modules/munkres-js/munkres.js"(exports, module) {
var MAX_SIZE = parseInt(Number.MAX_SAFE_INTEGER / 2) || (1 << 26) * (1 << 26);
var DEFAULT_PAD_VALUE = 0;
function Munkres() {
this.C = null;
this.row_covered = [];
this.col_covered = [];
this.n = 0;
this.Z0_r = 0;
this.Z0_c = 0;
this.marked = null;
this.path = null;
}
__name(Munkres, "Munkres");
Munkres.prototype.pad_matrix = function(matrix, pad_value) {
pad_value = pad_value || DEFAULT_PAD_VALUE;
var max_columns = 0;
var total_rows = matrix.length;
var i;
for (i = 0; i < total_rows; ++i)
if (matrix[i].length > max_columns)
max_columns = matrix[i].length;
total_rows = max_columns > total_rows ? max_columns : total_rows;
var new_matrix = [];
for (i = 0; i < total_rows; ++i) {
var row = matrix[i] || [];
var new_row = row.slice();
while (total_rows > new_row.length)
new_row.push(pad_value);
new_matrix.push(new_row);
}
return new_matrix;
};
Munkres.prototype.compute = function(cost_matrix, options) {
options = options || {};
options.padValue = options.padValue || DEFAULT_PAD_VALUE;
this.C = this.pad_matrix(cost_matrix, options.padValue);
this.n = this.C.length;
this.original_length = cost_matrix.length;
this.original_width = cost_matrix[0].length;
var nfalseArray = [];
while (nfalseArray.length < this.n)
nfalseArray.push(false);
this.row_covered = nfalseArray.slice();
this.col_covered = nfalseArray.slice();
this.Z0_r = 0;
this.Z0_c = 0;
this.path = this.__make_matrix(this.n * 2, 0);
this.marked = this.__make_matrix(this.n, 0);
var step = 1;
var steps = {
1: this.__step1,
2: this.__step2,
3: this.__step3,
4: this.__step4,
5: this.__step5,
6: this.__step6
};
while (true) {
var func = steps[step];
if (!func)
break;
step = func.apply(this);
}
var results = [];
for (var i = 0; i < this.original_length; ++i)
for (var j = 0; j < this.original_width; ++j)
if (this.marked[i][j] == 1)
results.push([i, j]);
return results;
};
Munkres.prototype.__make_matrix = function(n, val) {
var matrix = [];
for (var i = 0; i < n; ++i) {
matrix[i] = [];
for (var j = 0; j < n; ++j)
matrix[i][j] = val;
}
return matrix;
};
Munkres.prototype.__step1 = function() {
for (var i = 0; i < this.n; ++i) {
var minval = Math.min.apply(Math, this.C[i]);
for (var j = 0; j < this.n; ++j)
this.C[i][j] -= minval;
}
return 2;
};
Munkres.prototype.__step2 = function() {
for (var i = 0; i < this.n; ++i) {
for (var j = 0; j < this.n; ++j) {
if (this.C[i][j] === 0 && !this.col_covered[j] && !this.row_covered[i]) {
this.marked[i][j] = 1;
this.col_covered[j] = true;
this.row_covered[i] = true;
break;
}
}
}
this.__clear_covers();
return 3;
};
Munkres.prototype.__step3 = function() {
var count = 0;
for (var i = 0; i < this.n; ++i) {
for (var j = 0; j < this.n; ++j) {
if (this.marked[i][j] == 1 && this.col_covered[j] == false) {
this.col_covered[j] = true;
++count;
}
}
}
return count >= this.n ? 7 : 4;
};
Munkres.prototype.__step4 = function() {
var done = false;
var row = -1, col = -1, star_col = -1;
while (!done) {
var z = this.__find_a_zero();
row = z[0];
col = z[1];
if (row < 0)
return 6;
this.marked[row][col] = 2;
star_col = this.__find_star_in_row(row);
if (star_col >= 0) {
col = star_col;
this.row_covered[row] = true;
this.col_covered[col] = false;
} else {
this.Z0_r = row;
this.Z0_c = col;
return 5;
}
}
};
Munkres.prototype.__step5 = function() {
var count = 0;
this.path[count][0] = this.Z0_r;
this.path[count][1] = this.Z0_c;
var done = false;
while (!done) {
var row = this.__find_star_in_col(this.path[count][1]);
if (row >= 0) {
count++;
this.path[count][0] = row;
this.path[count][1] = this.path[count - 1][1];
} else {
done = true;
}
if (!done) {
var col = this.__find_prime_in_row(this.path[count][0]);
count++;
this.path[count][0] = this.path[count - 1][0];
this.path[count][1] = col;
}
}
this.__convert_path(this.path, count);
this.__clear_covers();
this.__erase_primes();
return 3;
};
Munkres.prototype.__step6 = function() {
var minval = this.__find_smallest();
for (var i = 0; i < this.n; ++i) {
for (var j = 0; j < this.n; ++j) {
if (this.row_covered[i])
this.C[i][j] += minval;
if (!this.col_covered[j])
this.C[i][j] -= minval;
}
}
return 4;
};
Munkres.prototype.__find_smallest = function() {
var minval = MAX_SIZE;
for (var i = 0; i < this.n; ++i)
for (var j = 0; j < this.n; ++j)
if (!this.row_covered[i] && !this.col_covered[j]) {
if (minval > this.C[i][j])
minval = this.C[i][j];
}
return minval;
};
Munkres.prototype.__find_a_zero = function() {
for (var i = 0; i < this.n; ++i)
for (var j = 0; j < this.n; ++j)
if (this.C[i][j] === 0 && !this.row_covered[i] && !this.col_covered[j])
return [i, j];
return [-1, -1];
};
Munkres.prototype.__find_star_in_row = function(row) {
for (var j = 0; j < this.n; ++j)
if (this.marked[row][j] == 1)
return j;
return -1;
};
Munkres.prototype.__find_star_in_col = function(col) {
for (var i = 0; i < this.n; ++i)
if (this.marked[i][col] == 1)
return i;
return -1;
};
Munkres.prototype.__find_prime_in_row = function(row) {
for (var j = 0; j < this.n; ++j)
if (this.marked[row][j] == 2)
return j;
return -1;
};
Munkres.prototype.__convert_path = function(path, count) {
for (var i = 0; i <= count; ++i)
this.marked[path[i][0]][path[i][1]] = this.marked[path[i][0]][path[i][1]] == 1 ? 0 : 1;
};
Munkres.prototype.__clear_covers = function() {
for (var i = 0; i < this.n; ++i) {
this.row_covered[i] = false;
this.col_covered[i] = false;
}
};
Munkres.prototype.__erase_primes = function() {
for (var i = 0; i < this.n; ++i)
for (var j = 0; j < this.n; ++j)
if (this.marked[i][j] == 2)
this.marked[i][j] = 0;
};
function make_cost_matrix(profit_matrix, inversion_function) {
var i, j;
if (!inversion_function) {
var maximum = -1 / 0;
for (i = 0; i < profit_matrix.length; ++i)
for (j = 0; j < profit_matrix[i].length; ++j)
if (profit_matrix[i][j] > maximum)
maximum = profit_matrix[i][j];
inversion_function = __name(function(x) {
return maximum - x;
}, "inversion_function");
}
var cost_matrix = [];
for (i = 0; i < profit_matrix.length; ++i) {
var row = profit_matrix[i];
cost_matrix[i] = [];
for (j = 0; j < row.length; ++j)
cost_matrix[i][j] = inversion_function(profit_matrix[i][j]);
}
return cost_matrix;
}
__name(make_cost_matrix, "make_cost_matrix");
function format_matrix(matrix) {
var columnWidths = [];
var i, j;
for (i = 0; i < matrix.length; ++i) {
for (j = 0; j < matrix[i].length; ++j) {
var entryWidth = String(matrix[i][j]).length;
if (!columnWidths[j] || entryWidth >= columnWidths[j])
columnWidths[j] = entryWidth;
}
}
var formatted = "";
for (i = 0; i < matrix.length; ++i) {
for (j = 0; j < matrix[i].length; ++j) {
var s = String(matrix[i][j]);
while (s.length < columnWidths[j])
s = " " + s;
formatted += s;
if (j != matrix[i].length - 1)
formatted += " ";
}
if (i != matrix[i].length - 1)
formatted += "\n";
}
return formatted;
}
__name(format_matrix, "format_matrix");
function computeMunkres(cost_matrix, options) {
var m = new Munkres();
return m.compute(cost_matrix, options);
}
__name(computeMunkres, "computeMunkres");
computeMunkres.version = "1.2.2";
computeMunkres.format_matrix = format_matrix;
computeMunkres.make_cost_matrix = make_cost_matrix;
computeMunkres.Munkres = Munkres;
if (typeof module !== "undefined" && module.exports) {
module.exports = computeMunkres;
}
}
});
var require_tracker = __commonJS({
"tracker.js"(exports) {
var itemTrackedModule = require_ItemTracked();
var ItemTracked = itemTrackedModule.ItemTracked;
var kdTree = require_kdTree_min().kdTree;
var iouAreas = require_utils().iouAreas;
var munkres = require_munkres();
var DEBUG_MODE = false;
var iouDistance = __name(function(item1, item2) {
var iou = iouAreas(item1, item2);
var distance = 1 - iou;
if (distance > 1 - params.iouLimit) {
distance = params.distanceLimit + 1;
}
return distance;
}, "iouDistance");
var params = {
// DEFAULT_UNMATCHEDFRAMES_TOLERANCE
// This the number of frame we wait when an object isn't matched before considering it gone
unMatchedFramesTolerance: 5,
// DEFAULT_IOU_LIMIT, exclude things from beeing matched if their IOU is lower than this
// 1 means total overlap whereas 0 means no overlap
iouLimit: 0.05,
// Remove new objects fast if they could not be matched in the next frames.
// Setting this to false ensures the object will stick around at least
// unMatchedFramesTolerance frames, even if they could neven be matched in
// subsequent frames.
fastDelete: true,
// The function to use to determine the distance between to detected objects
distanceFunc: iouDistance,
// The distance limit for matching. If values need to be excluded from
// matching set their distance to something greater than the distance limit
distanceLimit: 1e4,
// The algorithm used to match tracks with new detections. Can be either
// 'kdTree' or 'munkres'.
matchingAlgorithm: "kdTree"
};
var mapOfItemsTracked = /* @__PURE__ */ new Map();
var mapOfAllItemsTracked = /* @__PURE__ */ new Map();
var keepAllHistoryInMemory = false;
exports.computeDistance = iouDistance;
exports.updateTrackedItemsWithNewFrame = function(detectionsOfThisFrame, frameNb) {
var treeItemsTracked = new kdTree(Array.from(mapOfItemsTracked.values()), params.distanceFunc, ["x", "y", "w", "h"]);
var treeDetectionsOfThisFrame = new kdTree(detectionsOfThisFrame, params.distanceFunc, ["x", "y", "w", "h"]);
if (mapOfItemsTracked.size === 0) {
detectionsOfThisFrame.forEach(function(itemDetected) {
var newItemTracked = new ItemTracked(itemDetected, frameNb, params.unMatchedFramesTolerance, params.fastDelete);
mapOfItemsTracked.set(newItemTracked.id, newItemTracked);
treeItemsTracked.insert(newItemTracked);
});
} else {
var matchedList = new Array(detectionsOfThisFrame.length);
matchedList.fill(false);
if (detectionsOfThisFrame.length > 0) {
if (params.matchingAlgorithm === "munkres") {
var trackedItemIds = Array.from(mapOfItemsTracked.keys());
var costMatrix = Array.from(mapOfItemsTracked.values()).map((itemTracked) => {
var predictedPosition = itemTracked.predictNextPosition();
return detectionsOfThisFrame.map(
(detection) => params.distanceFunc(predictedPosition, detection)
);
});
mapOfItemsTracked.forEach(function(itemTracked) {
itemTracked.makeAvailable();
});
munkres(costMatrix).filter((m) => costMatrix[m[0]][m[1]] <= params.distanceLimit).forEach((m) => {
var itemTracked = mapOfItemsTracked.get(trackedItemIds[m[0]]);
var updatedTrackedItemProperties = detectionsOfThisFrame[m[1]];
matchedList[m[1]] = { idDisplay: itemTracked.idDisplay };
itemTracked.makeUnavailable().update(updatedTrackedItemProperties, frameNb);
});
matchedList.forEach(function(matched, index) {
if (!matched) {
if (Math.min(...costMatrix.map((m) => m[index])) > params.distanceLimit) {
var newItemTracked = ItemTracked(detectionsOfThisFrame[index], frameNb, params.unMatchedFramesTolerance, params.fastDelete);
mapOfItemsTracked.set(newItemTracked.id, newItemTracked);
newItemTracked.makeUnavailable();
costMatrix.push(detectionsOfThisFrame.map(
(detection) => params.distanceFunc(newItemTracked, detection)
));
}
}
});
} else if (params.matchingAlgorithm === "kdTree") {
mapOfItemsTracked.forEach(function(itemTracked) {
var predictedPosition = itemTracked.predictNextPosition();
itemTracked.makeAvailable();
var treeSearchResult = treeDetectionsOfThisFrame.nearest(predictedPosition, 1, params.distanceLimit)[0];
var treeSearchResultWithoutPrediction = treeDetectionsOfThisFrame.nearest(itemTracked, 1, params.distanceLimit)[0];
var treeSearchMultipleResults = treeDetectionsOfThisFrame.nearest(predictedPosition, 2, params.distanceLimit);
if (treeSearchResult) {
var indexClosestNewDetectedItem = detectionsOfThisFrame.indexOf(treeSearchResult[0]);
if (!matchedList[indexClosestNewDetectedItem]) {
matchedList[indexClosestNewDetectedItem] = {
idDisplay: itemTracked.idDisplay
};
var updatedTrackedItemProperties = detectionsOfThisFrame[indexClosestNewDetectedItem];
mapOfItemsTracked.get(itemTracked.id).makeUnavailable().update(updatedTrackedItemProperties, frameNb);
} else {
}
}
});
} else {
throw `Unknown matching algorithm "${params.matchingAlgorithm}"`;
}
} else {
if (DEBUG_MODE) {
console.log("[Tracker] Nothing detected for frame n\xBA" + frameNb);
}
mapOfItemsTracked.forEach(function(itemTracked) {
itemTracked.makeAvailable();
});
}
if (params.matchingAlgorithm === "kdTree") {
if (mapOfItemsTracked.size > 0) {
treeItemsTracked = new kdTree(Array.from(mapOfItemsTracked.values()), params.distanceFunc, ["x", "y", "w", "h"]);
matchedList.forEach(function(matched, index) {
if (!matched) {
var treeSearchResult = treeItemsTracked.nearest(detectionsOfThisFrame[index], 1, params.distanceLimit)[0];
if (!treeSearchResult) {
var newItemTracked = ItemTracked(detectionsOfThisFrame[index], frameNb, params.unMatchedFramesTolerance, params.fastDelete);
mapOfItemsTracked.set(newItemTracked.id, newItemTracked);
treeItemsTracked.insert(newItemTracked);
newItemTracked.makeUnavailable();
} else {
}
}
});
}
}
mapOfItemsTracked.forEach(function(itemTracked) {
if (itemTracked.available) {
itemTracked.countDown(frameNb);
itemTracked.updateTheoricalPositionAndSize();
if (itemTracked.isDead()) {
mapOfItemsTracked.delete(itemTracked.id);
treeItemsTracked.remove(itemTracked);
if (keepAllHistoryInMemory) {
mapOfAllItemsTracked.set(itemTracked.id, itemTracked);
}
}
}
});
}
};
exports.reset = function() {
mapOfItemsTracked = /* @__PURE__ */ new Map();
mapOfAllItemsTracked = /* @__PURE__ */ new Map();
itemTrackedModule.reset();
};
exports.setParams = function(newParams) {
Object.keys(newParams).forEach((key) => {
params[key] = newParams[key];
});
};
exports.enableKeepInMemory = function() {
keepAllHistoryInMemory = true;
};
exports.disableKeepInMemory = function() {
keepAllHistoryInMemory = false;
};
exports.getJSONOfTrackedItems = function(roundInt = true) {
return Array.from(mapOfItemsTracked.values()).map(function(itemTracked) {
return itemTracked.toJSON(roundInt);
});
};
exports.getJSONDebugOfTrackedItems = function(roundInt = true) {
return Array.from(mapOfItemsTracked.values()).map(function(itemTracked) {
return itemTracked.toJSONDebug(roundInt);
});
};
exports.getTrackedItemsInMOTFormat = function(frameNb) {
return Array.from(mapOfItemsTracked.values()).map(function(itemTracked) {
return itemTracked.toMOT(frameNb);
});
};
exports.getAllTrackedItems = function() {
return mapOfAllItemsTracked;
};
exports.getJSONOfAllTrackedItems = function() {
return Array.from(mapOfAllItemsTracked.values()).map(function(itemTracked) {
return itemTracked.toJSONGenericInfo();
});
};
}
});
var tracker_default = require_tracker();
// demo/tracker/index.ts
var humanConfig = {
// user configuration for human, used to fine-tune behavior
debug: true,
backend: "webgl",
// cacheSensitivity: 0,
// cacheModels: false,
// warmup: 'none',
modelBasePath: "https://vladmandic.github.io/human-models/models",
filter: { enabled: true, equalization: false, flip: false },
face: {
enabled: true,
detector: { rotation: false, maxDetected: 10, minConfidence: 0.3 },
mesh: { enabled: true },
attention: { enabled: false },
iris: { enabled: false },
description: { enabled: false },
emotion: { enabled: false },
antispoof: { enabled: false },
liveness: { enabled: false }
},
body: { enabled: false, maxDetected: 6, modelPath: "movenet-multipose.json" },
hand: { enabled: false },
object: { enabled: false, maxDetected: 10 },
segmentation: { enabled: false },
gesture: { enabled: false }
};
var trackerConfig = {
unMatchedFramesTolerance: 100,
iouLimit: 0.05,
fastDelete: false,
distanceLimit: 1e4,
matchingAlgorithm: "kdTree"
};
var human = new H.Human(humanConfig);
var dom = {
// grab instances of dom objects so we dont have to look them up later
video: document.getElementById("video"),
canvas: document.getElementById("canvas"),
log: document.getElementById("log"),
fps: document.getElementById("status"),
tracker: document.getElementById("tracker"),
interpolation: document.getElementById("interpolation"),
config: document.getElementById("config"),
ctx: document.getElementById("canvas").getContext("2d")
};
var timestamp = { detect: 0, draw: 0, tensors: 0, start: 0 };
var fps = { detectFPS: 0, drawFPS: 0, frames: 0, averageMs: 0 };
var log = (...msg) => {
dom.log.innerText += msg.join(" ") + "\n";
console.log(...msg);
};
var status = (msg) => dom.fps.innerText = msg;
async function detectionLoop() {
if (!dom.video.paused && dom.video.readyState >= 2) {
if (timestamp.start === 0) timestamp.start = human.now();
await human.detect(dom.video, humanConfig);
const tensors = human.tf.memory().numTensors;
if (tensors - timestamp.tensors !== 0) log("allocated tensors:", tensors - timestamp.tensors);
timestamp.tensors = tensors;
fps.detectFPS = Math.round(1e3 * 1e3 / (human.now() - timestamp.detect)) / 1e3;
fps.frames++;
fps.averageMs = Math.round(1e3 * (human.now() - timestamp.start) / fps.frames) / 1e3;
}
timestamp.detect = human.now();
requestAnimationFrame(detectionLoop);
}
function drawLoop() {
if (!dom.video.paused && dom.video.readyState >= 2) {
const res = dom.interpolation.checked ? human.next(human.result) : human.result;
let tracking = [];
if (human.config.face.enabled) tracking = res.face;
else if (human.config.body.enabled) tracking = res.body;
else if (human.config.object.enabled) tracking = res.object;
else log("unknown object type");
let data = [];
if (dom.tracker.checked) {
const items = tracking.map((obj) => ({
x: obj.box[0] + obj.box[2] / 2,
y: obj.box[1] + obj.box[3] / 2,
w: obj.box[2],
h: obj.box[3],
name: obj.label || (human.config.face.enabled ? "face" : "body"),
confidence: obj.score
}));
tracker_default.updateTrackedItemsWithNewFrame(items, fps.frames);
data = tracker_default.getJSONOfTrackedItems(true);
}
human.draw.canvas(dom.video, dom.canvas);
for (let i = 0; i < tracking.length; i++) {
const name = tracking[i].label || (human.config.face.enabled ? "face" : "body");
dom.ctx.strokeRect(tracking[i].box[0], tracking[i].box[1], tracking[i].box[1], tracking[i].box[2]);
dom.ctx.fillText(`id: ${tracking[i].id} ${Math.round(100 * tracking[i].score)}% ${name}`, tracking[i].box[0] + 4, tracking[i].box[1] + 16);
if (data[i]) {
dom.ctx.fillText(`t: ${data[i].id} ${Math.round(100 * data[i].confidence)}% ${data[i].name} ${data[i].isZombie ? "zombie" : ""}`, tracking[i].box[0] + 4, tracking[i].box[1] + 34);
}
}
}
const now = human.now();
fps.drawFPS = Math.round(1e3 * 1e3 / (now - timestamp.draw)) / 1e3;
timestamp.draw = now;
status(dom.video.paused ? "paused" : `fps: ${fps.detectFPS.toFixed(1).padStart(5, " ")} detect | ${fps.drawFPS.toFixed(1).padStart(5, " ")} draw`);
setTimeout(drawLoop, 30);
}
async function handleVideo(file) {
const url = URL.createObjectURL(file);
dom.video.src = url;
await dom.video.play();
log("loaded video:", file.name, "resolution:", [dom.video.videoWidth, dom.video.videoHeight], "duration:", dom.video.duration);
dom.canvas.width = dom.video.videoWidth;
dom.canvas.height = dom.video.videoHeight;
dom.ctx.strokeStyle = "white";
dom.ctx.fillStyle = "white";
dom.ctx.font = "16px Segoe UI";
dom.video.playbackRate = 0.25;
}
function initInput() {
document.body.addEventListener("dragenter", (evt) => evt.preventDefault());
document.body.addEventListener("dragleave", (evt) => evt.preventDefault());
document.body.addEventListener("dragover", (evt) => evt.preventDefault());
document.body.addEventListener("drop", async (evt) => {
var _a, _b;
evt.preventDefault();
if (evt.dataTransfer) evt.dataTransfer.dropEffect = "copy";
const file = (_b = (_a = evt.dataTransfer) == null ? void 0 : _a.files) == null ? void 0 : _b[0];
if (file) await handleVideo(file);
log(dom.video.readyState);
});
document.getElementById("inputvideo").onchange = async (evt) => {
var _a, _b;
evt.preventDefault();
const file = (_b = (_a = evt.target) == null ? void 0 : _a["files"]) == null ? void 0 : _b[0];
if (file) await handleVideo(file);
};
dom.config.onchange = () => {
trackerConfig.distanceLimit = document.getElementById("distanceLimit").valueAsNumber;
trackerConfig.iouLimit = document.getElementById("iouLimit").valueAsNumber;
trackerConfig.unMatchedFramesTolerance = document.getElementById("unMatchedFramesTolerance").valueAsNumber;
trackerConfig.unMatchedFramesTolerance = document.getElementById("unMatchedFramesTolerance").valueAsNumber;
trackerConfig.matchingAlgorithm = document.getElementById("matchingAlgorithm-kdTree").checked ? "kdTree" : "munkres";
tracker_default.setParams(trackerConfig);
if (document.getElementById("keepInMemory").checked) tracker_default.enableKeepInMemory();
else tracker_default.disableKeepInMemory();
tracker_default.reset();
log("tracker config change", JSON.stringify(trackerConfig));
humanConfig.face.enabled = document.getElementById("box-face").checked;
humanConfig.body.enabled = document.getElementById("box-body").checked;
humanConfig.object.enabled = document.getElementById("box-object").checked;
};
dom.tracker.onchange = (evt) => {
log("tracker", evt.target.checked ? "enabled" : "disabled");
tracker_default.setParams(trackerConfig);
tracker_default.reset();
};
}
async function main() {
log("human version:", human.version, "| tfjs version:", human.tf.version["tfjs-core"]);
log("platform:", human.env.platform, "| agent:", human.env.agent);
status("loading...");
await human.load();
log("backend:", human.tf.getBackend(), "| available:", human.env.backends);
log("models loaded:", human.models.loaded());
status("initializing...");
await human.warmup();
initInput();
await detectionLoop();
drawLoop();
}
window.onload = main;
//# sourceMappingURL=index.js.map