mirror of https://github.com/vladmandic/human
119 lines
4.1 KiB
JavaScript
119 lines
4.1 KiB
JavaScript
![]() |
const tf = require('@tensorflow/tfjs');
|
||
|
const kpt = require('./keypoints');
|
||
|
|
||
|
function eitherPointDoesntMeetConfidence(a, b, minConfidence) {
|
||
|
return (a < minConfidence || b < minConfidence);
|
||
|
}
|
||
|
|
||
|
function getAdjacentKeyPoints(keypoints, minConfidence) {
|
||
|
return kpt.connectedPartIndices.reduce((result, [leftJoint, rightJoint]) => {
|
||
|
if (eitherPointDoesntMeetConfidence(keypoints[leftJoint].score, keypoints[rightJoint].score, minConfidence)) {
|
||
|
return result;
|
||
|
}
|
||
|
result.push([keypoints[leftJoint], keypoints[rightJoint]]);
|
||
|
return result;
|
||
|
}, []);
|
||
|
}
|
||
|
exports.getAdjacentKeyPoints = getAdjacentKeyPoints;
|
||
|
|
||
|
const { NEGATIVE_INFINITY, POSITIVE_INFINITY } = Number;
|
||
|
function getBoundingBox(keypoints) {
|
||
|
return keypoints.reduce(({ maxX, maxY, minX, minY }, { position: { x, y } }) => ({
|
||
|
maxX: Math.max(maxX, x),
|
||
|
maxY: Math.max(maxY, y),
|
||
|
minX: Math.min(minX, x),
|
||
|
minY: Math.min(minY, y),
|
||
|
}), {
|
||
|
maxX: NEGATIVE_INFINITY,
|
||
|
maxY: NEGATIVE_INFINITY,
|
||
|
minX: POSITIVE_INFINITY,
|
||
|
minY: POSITIVE_INFINITY,
|
||
|
});
|
||
|
}
|
||
|
exports.getBoundingBox = getBoundingBox;
|
||
|
function getBoundingBoxPoints(keypoints) {
|
||
|
const { minX, minY, maxX, maxY } = getBoundingBox(keypoints);
|
||
|
return [{ x: minX, y: minY }, { x: maxX, y: minY }, { x: maxX, y: maxY }, { x: minX, y: maxY }];
|
||
|
}
|
||
|
exports.getBoundingBoxPoints = getBoundingBoxPoints;
|
||
|
async function toTensorBuffers3D(tensors) {
|
||
|
return Promise.all(tensors.map((tensor) => tensor.buffer()));
|
||
|
}
|
||
|
exports.toTensorBuffers3D = toTensorBuffers3D;
|
||
|
|
||
|
function scalePose(pose, scaleY, scaleX, offsetY = 0, offsetX = 0) {
|
||
|
return {
|
||
|
score: pose.score,
|
||
|
keypoints: pose.keypoints.map(({ score, part, position }) => ({
|
||
|
score,
|
||
|
part,
|
||
|
position: {
|
||
|
x: position.x * scaleX + offsetX,
|
||
|
y: position.y * scaleY + offsetY,
|
||
|
},
|
||
|
})),
|
||
|
};
|
||
|
}
|
||
|
exports.scalePose = scalePose;
|
||
|
|
||
|
function scalePoses(poses, scaleY, scaleX, offsetY = 0, offsetX = 0) {
|
||
|
if (scaleX === 1 && scaleY === 1 && offsetY === 0 && offsetX === 0) {
|
||
|
return poses;
|
||
|
}
|
||
|
return poses.map((pose) => scalePose(pose, scaleY, scaleX, offsetY, offsetX));
|
||
|
}
|
||
|
exports.scalePoses = scalePoses;
|
||
|
|
||
|
function getInputTensorDimensions(input) {
|
||
|
return input instanceof tf.Tensor ? [input.shape[0], input.shape[1]] : [input.height, input.width];
|
||
|
}
|
||
|
exports.getInputTensorDimensions = getInputTensorDimensions;
|
||
|
|
||
|
function toInputTensor(input) {
|
||
|
return input instanceof tf.Tensor ? input : tf.browser.fromPixels(input);
|
||
|
}
|
||
|
exports.toInputTensor = toInputTensor;
|
||
|
|
||
|
function toResizedInputTensor(input, resizeHeight, resizeWidth) {
|
||
|
return tf.tidy(() => {
|
||
|
const imageTensor = toInputTensor(input);
|
||
|
return imageTensor.resizeBilinear([resizeHeight, resizeWidth]);
|
||
|
});
|
||
|
}
|
||
|
exports.toResizedInputTensor = toResizedInputTensor;
|
||
|
|
||
|
function padAndResizeTo(input, [targetH, targetW]) {
|
||
|
const [height, width] = getInputTensorDimensions(input);
|
||
|
const targetAspect = targetW / targetH;
|
||
|
const aspect = width / height;
|
||
|
let [padT, padB, padL, padR] = [0, 0, 0, 0];
|
||
|
if (aspect < targetAspect) {
|
||
|
// pads the width
|
||
|
padT = 0;
|
||
|
padB = 0;
|
||
|
padL = Math.round(0.5 * (targetAspect * height - width));
|
||
|
padR = Math.round(0.5 * (targetAspect * height - width));
|
||
|
} else {
|
||
|
// pads the height
|
||
|
padT = Math.round(0.5 * ((1.0 / targetAspect) * width - height));
|
||
|
padB = Math.round(0.5 * ((1.0 / targetAspect) * width - height));
|
||
|
padL = 0;
|
||
|
padR = 0;
|
||
|
}
|
||
|
const resized = tf.tidy(() => {
|
||
|
let imageTensor = toInputTensor(input);
|
||
|
imageTensor = tf.pad3d(imageTensor, [[padT, padB], [padL, padR], [0, 0]]);
|
||
|
return imageTensor.resizeBilinear([targetH, targetW]);
|
||
|
});
|
||
|
return { resized, padding: { top: padT, left: padL, right: padR, bottom: padB } };
|
||
|
}
|
||
|
exports.padAndResizeTo = padAndResizeTo;
|
||
|
|
||
|
function scaleAndFlipPoses(poses, [height, width], [inputResolutionHeight, inputResolutionWidth], padding) {
|
||
|
const scaleY = (height + padding.top + padding.bottom) / (inputResolutionHeight);
|
||
|
const scaleX = (width + padding.left + padding.right) / (inputResolutionWidth);
|
||
|
const scaledPoses = scalePoses(poses, scaleY, scaleX, -padding.top, -padding.left);
|
||
|
return scaledPoses;
|
||
|
}
|
||
|
exports.scaleAndFlipPoses = scaleAndFlipPoses;
|