mirror of https://github.com/vladmandic/human
fix unregistered ops in tfjs
parent
bbc45d3c39
commit
fc424b176d
|
@ -16,7 +16,7 @@ const userConfig = {
|
|||
profile: false,
|
||||
warmup: 'full',
|
||||
modelBasePath: '../../models/',
|
||||
wasmPath: 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@3.7.0/dist/',
|
||||
wasmPath: 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@3.8.0/dist/',
|
||||
filter: { enabled: false },
|
||||
face: { enabled: true,
|
||||
detector: { rotation: false, maxDetected: 1 },
|
||||
|
|
|
@ -14,7 +14,7 @@ const userConfig = {
|
|||
warmup: 'none',
|
||||
debug: true,
|
||||
modelBasePath: '../../models/',
|
||||
wasmPath: 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@3.7.0/dist/',
|
||||
wasmPath: 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@3.8.0/dist/',
|
||||
face: {
|
||||
enabled: true,
|
||||
detector: { rotation: true, return: true },
|
||||
|
@ -75,10 +75,10 @@ async function analyze(face) {
|
|||
navigator.clipboard.writeText(`{"name":"unknown", "source":"${face.fileName}", "embedding":[${embedding}]},`);
|
||||
if (enhanced) {
|
||||
const c = document.getElementById('orig');
|
||||
const squeeze = enhanced.squeeze().div(255);
|
||||
const squeeze = human.tf.div(human.tf.squeeze(enhanced), 255);
|
||||
await human.tf.browser.toPixels(squeeze, c);
|
||||
enhanced.dispose();
|
||||
squeeze.dispose();
|
||||
human.tf.dispose(enhanced);
|
||||
human.tf.dispose(squeeze);
|
||||
const ctx = c.getContext('2d');
|
||||
ctx.font = 'small-caps 0.4rem "Lato"';
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
|
||||
|
|
|
@ -30,7 +30,7 @@ let human;
|
|||
let userConfig = {
|
||||
warmup: 'none',
|
||||
backend: 'humangl',
|
||||
wasmPath: 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@3.7.0/dist/',
|
||||
wasmPath: 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@3.8.0/dist/',
|
||||
/*
|
||||
async: false,
|
||||
cacheSensitivity: 0,
|
||||
|
@ -169,10 +169,10 @@ function status(msg) {
|
|||
}
|
||||
|
||||
const compare = { enabled: false, original: null };
|
||||
async function calcSimmilariry(result) {
|
||||
async function calcSimmilarity(result) {
|
||||
document.getElementById('compare-container').style.display = compare.enabled ? 'block' : 'none';
|
||||
if (!compare.enabled) return;
|
||||
if (!result || !result.face || !result.face[0].embedding) return;
|
||||
if (!result || !result.face || !result.face[0] || !result.face[0].embedding) return;
|
||||
if (!(result.face.length > 0) || (result.face[0].embedding.length <= 64)) return;
|
||||
if (!compare.original) {
|
||||
compare.original = result;
|
||||
|
@ -181,12 +181,12 @@ async function calcSimmilariry(result) {
|
|||
const enhanced = human.enhance(result.face[0]);
|
||||
if (enhanced) {
|
||||
const c = document.getElementById('orig');
|
||||
const squeeze = enhanced.squeeze();
|
||||
const norm = squeeze.div(255);
|
||||
const squeeze = human.tf.squeeze(enhanced);
|
||||
const norm = human.tf.div(squeeze, 255);
|
||||
human.tf.browser.toPixels(norm, c);
|
||||
enhanced.dispose();
|
||||
squeeze.dispose();
|
||||
norm.dispose();
|
||||
human.tf.dispose(enhanced);
|
||||
human.tf.dispose(squeeze);
|
||||
human.tf.dispose(norm);
|
||||
}
|
||||
} else {
|
||||
document.getElementById('compare-canvas').getContext('2d').drawImage(compare.original.canvas, 0, 0, 200, 200);
|
||||
|
@ -246,7 +246,7 @@ async function drawResults(input) {
|
|||
*/
|
||||
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
const person = result.persons; // explicitly invoke person getter
|
||||
await calcSimmilariry(result);
|
||||
await calcSimmilarity(result);
|
||||
|
||||
// update log
|
||||
const engine = human.tf.engine();
|
||||
|
|
|
@ -53,7 +53,7 @@ async function detect(img) {
|
|||
process.send({ image: img, detected: result }); // send results back to main
|
||||
process.send({ ready: true }); // send signal back to main that this worker is now idle and ready for next image
|
||||
}
|
||||
tensor.dispose();
|
||||
tf.dispose(tensor);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
|
|
@ -66,13 +66,13 @@ async function process(jpegBuffer) {
|
|||
busy = true;
|
||||
const decoded = tf.node.decodeJpeg(jpegBuffer, 3); // decode jpeg buffer to raw tensor
|
||||
const tensor = tf.expandDims(decoded, 0); // almost all tf models use first dimension as batch number so we add it
|
||||
decoded.dispose();
|
||||
tf.dispose(decoded);
|
||||
|
||||
log.state('input frame:', ++count, 'size:', jpegBuffer.length, 'decoded shape:', tensor.shape);
|
||||
const res = await human.detect(tensor);
|
||||
log.data('gesture', JSON.stringify(res.gesture));
|
||||
// do processing here
|
||||
tensor.dispose(); // must dispose tensor
|
||||
tf.dispose(tensor); // must dispose tensor
|
||||
busy = false;
|
||||
}
|
||||
|
||||
|
|
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 one or more lines are too long
|
@ -498,7 +498,7 @@ var BlazeFaceModel = class {
|
|||
return null;
|
||||
const [batch, boxes, scores] = tf3.tidy(() => {
|
||||
const resizedImage = tf3.image.resizeBilinear(inputImage, [this.inputSize, this.inputSize]);
|
||||
const normalizedImage = resizedImage.div(127.5).sub(0.5);
|
||||
const normalizedImage = tf3.sub(tf3.div(resizedImage, 127.5), 0.5);
|
||||
const res = this.model.execute(normalizedImage);
|
||||
let batchOut;
|
||||
if (Array.isArray(res)) {
|
||||
|
@ -506,33 +506,33 @@ var BlazeFaceModel = class {
|
|||
const concat384 = tf3.concat([sorted[0], sorted[2]], 2);
|
||||
const concat512 = tf3.concat([sorted[1], sorted[3]], 2);
|
||||
const concat3 = tf3.concat([concat512, concat384], 1);
|
||||
batchOut = concat3.squeeze(0);
|
||||
batchOut = tf3.squeeze(concat3, 0);
|
||||
} else {
|
||||
batchOut = tf3.squeeze(res);
|
||||
}
|
||||
const boxesOut = decodeBounds(batchOut, this.anchors, [this.inputSize, this.inputSize]);
|
||||
const logits = tf3.slice(batchOut, [0, 0], [-1, 1]);
|
||||
const scoresOut = tf3.sigmoid(logits).squeeze().dataSync();
|
||||
const scoresOut = tf3.squeeze(tf3.sigmoid(logits)).dataSync();
|
||||
return [batchOut, boxesOut, scoresOut];
|
||||
});
|
||||
this.config = mergeDeep(this.config, userConfig);
|
||||
const nmsTensor = await tf3.image.nonMaxSuppressionAsync(boxes, scores, this.config.face.detector.maxDetected, this.config.face.detector.iouThreshold, this.config.face.detector.minConfidence);
|
||||
const nms = nmsTensor.arraySync();
|
||||
nmsTensor.dispose();
|
||||
tf3.dispose(nmsTensor);
|
||||
const annotatedBoxes = [];
|
||||
for (let i = 0; i < nms.length; i++) {
|
||||
const confidence = scores[nms[i]];
|
||||
if (confidence > this.config.face.detector.minConfidence) {
|
||||
const boundingBox = tf3.slice(boxes, [nms[i], 0], [1, -1]);
|
||||
const localBox = createBox(boundingBox);
|
||||
boundingBox.dispose();
|
||||
tf3.dispose(boundingBox);
|
||||
const anchor = this.anchorsData[nms[i]];
|
||||
const landmarks = tf3.tidy(() => tf3.slice(batch, [nms[i], keypointsCount - 1], [1, -1]).squeeze().reshape([keypointsCount, -1]));
|
||||
const landmarks = tf3.tidy(() => tf3.reshape(tf3.squeeze(tf3.slice(batch, [nms[i], keypointsCount - 1], [1, -1])), [keypointsCount, -1]));
|
||||
annotatedBoxes.push({ box: localBox, landmarks, anchor, confidence });
|
||||
}
|
||||
}
|
||||
batch.dispose();
|
||||
boxes.dispose();
|
||||
tf3.dispose(batch);
|
||||
tf3.dispose(boxes);
|
||||
return {
|
||||
boxes: annotatedBoxes,
|
||||
scaleFactor: [inputImage.shape[2] / this.inputSize, inputImage.shape[1] / this.inputSize]
|
||||
|
@ -3977,9 +3977,9 @@ var Pipeline = class {
|
|||
}
|
||||
if (detector && detector.boxes) {
|
||||
detector.boxes.forEach((prediction) => {
|
||||
prediction.box.startPoint.dispose();
|
||||
prediction.box.endPoint.dispose();
|
||||
prediction.landmarks.dispose();
|
||||
tf4.dispose(prediction.box.startPoint);
|
||||
tf4.dispose(prediction.box.endPoint);
|
||||
tf4.dispose(prediction.landmarks);
|
||||
});
|
||||
}
|
||||
const results = tf4.tidy(() => this.storedBoxes.map((box6, i) => {
|
||||
|
@ -3994,16 +3994,16 @@ var Pipeline = class {
|
|||
const rotatedImage = tf4.image.rotateWithOffset(input, angle, 0, faceCenterNormalized);
|
||||
rotationMatrix = buildRotationMatrix(-angle, faceCenter);
|
||||
if (config3.face.mesh.enabled)
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]), 255);
|
||||
else
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.boxSize, this.boxSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.boxSize, this.boxSize]), 255);
|
||||
} else {
|
||||
rotationMatrix = IDENTITY_MATRIX;
|
||||
const clonedImage = input.clone();
|
||||
if (config3.face.mesh.enabled)
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.meshSize, this.meshSize]), 255);
|
||||
else
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.boxSize, this.boxSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.boxSize, this.boxSize]), 255);
|
||||
}
|
||||
if (!config3.face.mesh.enabled) {
|
||||
const prediction2 = {
|
||||
|
@ -4055,9 +4055,9 @@ var Pipeline = class {
|
|||
angle = computeRotation(box6.landmarks[indexOfMouth], box6.landmarks[indexOfForehead]);
|
||||
const faceCenter = getBoxCenter({ startPoint: box6.startPoint, endPoint: box6.endPoint });
|
||||
const faceCenterNormalized = [faceCenter[0] / input.shape[2], faceCenter[1] / input.shape[1]];
|
||||
const rotatedImage = tf4.image.rotateWithOffset(input.toFloat(), angle, 0, faceCenterNormalized);
|
||||
const rotatedImage = tf4.image.rotateWithOffset(tf4.cast(input, "float32"), angle, 0, faceCenterNormalized);
|
||||
rotationMatrix = buildRotationMatrix(-angle, faceCenter);
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]), 255);
|
||||
}
|
||||
const prediction = {
|
||||
mesh,
|
||||
|
@ -4122,7 +4122,7 @@ async function predict(input, config3) {
|
|||
tensor: prediction.image
|
||||
});
|
||||
if (prediction.coords)
|
||||
prediction.coords.dispose();
|
||||
tf5.dispose(prediction.coords);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
@ -4210,7 +4210,7 @@ function enhance(input) {
|
|||
if (!model.inputs[0].shape)
|
||||
return null;
|
||||
const crop = tensor2.shape.length === 3 ? tf6.image.cropAndResize(tf6.expandDims(tensor2, 0), box6, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]) : tf6.image.cropAndResize(tensor2, box6, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]);
|
||||
const norm = crop.mul(255);
|
||||
const norm = tf6.mul(crop, 255);
|
||||
return norm;
|
||||
});
|
||||
return image18;
|
||||
|
@ -4244,7 +4244,7 @@ async function predict2(image18, config3, idx, count2) {
|
|||
obj.gender = gender[0] <= 0.5 ? "female" : "male";
|
||||
obj.genderScore = Math.min(0.99, confidence);
|
||||
}
|
||||
const age = resT.find((t) => t.shape[1] === 100).argMax(1).dataSync()[0];
|
||||
const age = tf6.argMax(resT.find((t) => t.shape[1] === 100), 1).dataSync()[0];
|
||||
const all2 = resT.find((t) => t.shape[1] === 100).dataSync();
|
||||
obj.age = Math.round(all2[age - 1] > all2[age + 1] ? 10 * age - 100 * all2[age - 1] : 10 * age + 100 * all2[age + 1]) / 10;
|
||||
const desc = resT.find((t) => t.shape[1] === 1024);
|
||||
|
@ -4288,19 +4288,19 @@ async function predict3(image18, config3, idx, count2) {
|
|||
return new Promise(async (resolve) => {
|
||||
const resize = tf7.image.resizeBilinear(image18, [model2.inputs[0].shape[2], model2.inputs[0].shape[1]], false);
|
||||
const [red, green, blue] = tf7.split(resize, 3, 3);
|
||||
resize.dispose();
|
||||
tf7.dispose(resize);
|
||||
const redNorm = tf7.mul(red, rgb[0]);
|
||||
const greenNorm = tf7.mul(green, rgb[1]);
|
||||
const blueNorm = tf7.mul(blue, rgb[2]);
|
||||
red.dispose();
|
||||
green.dispose();
|
||||
blue.dispose();
|
||||
tf7.dispose(red);
|
||||
tf7.dispose(green);
|
||||
tf7.dispose(blue);
|
||||
const grayscale = tf7.addN([redNorm, greenNorm, blueNorm]);
|
||||
redNorm.dispose();
|
||||
greenNorm.dispose();
|
||||
blueNorm.dispose();
|
||||
const normalize = tf7.tidy(() => grayscale.sub(0.5).mul(2));
|
||||
grayscale.dispose();
|
||||
tf7.dispose(redNorm);
|
||||
tf7.dispose(greenNorm);
|
||||
tf7.dispose(blueNorm);
|
||||
const normalize = tf7.tidy(() => tf7.mul(tf7.sub(grayscale, 0.5), 2));
|
||||
tf7.dispose(grayscale);
|
||||
const obj = [];
|
||||
if (config3.face.emotion.enabled) {
|
||||
const emotionT = await model2.predict(normalize);
|
||||
|
@ -4312,7 +4312,7 @@ async function predict3(image18, config3, idx, count2) {
|
|||
}
|
||||
obj.sort((a, b) => b.score - a.score);
|
||||
}
|
||||
normalize.dispose();
|
||||
tf7.dispose(normalize);
|
||||
last2[idx] = obj;
|
||||
lastCount2 = count2;
|
||||
resolve(obj);
|
||||
|
@ -4636,7 +4636,7 @@ async function predict4(input, config3) {
|
|||
if (!model3.inputs[0].shape)
|
||||
return [];
|
||||
const resized = tf8.image.resizeBilinear(input, [model3.inputs[0].shape[2], model3.inputs[0].shape[1]]);
|
||||
const normalized = resized.toFloat().div(127.5).sub(1);
|
||||
const normalized = tf8.sub(tf8.div(tf8.cast(resized, "float32"), 127.5), 1);
|
||||
const results = model3.execute(normalized, poseNetOutputs);
|
||||
const results3d = results.map((y) => tf8.squeeze(y, [0]));
|
||||
results3d[1] = results3d[1].sigmoid();
|
||||
|
@ -4644,7 +4644,7 @@ async function predict4(input, config3) {
|
|||
});
|
||||
const buffers = await Promise.all(res.map((tensor2) => tensor2.buffer()));
|
||||
for (const t of res)
|
||||
t.dispose();
|
||||
tf8.dispose(t);
|
||||
const decoded = await decode(buffers[0], buffers[1], buffers[2], buffers[3], config3.body.maxDetected, config3.body.minConfidence);
|
||||
if (!model3.inputs[0].shape)
|
||||
return [];
|
||||
|
@ -7693,43 +7693,43 @@ var HandDetector = class {
|
|||
}
|
||||
normalizeLandmarks(rawPalmLandmarks, index) {
|
||||
return tf10.tidy(() => {
|
||||
const landmarks = tf10.add(tf10.div(rawPalmLandmarks.reshape([-1, 7, 2]), this.inputSizeTensor), this.anchors[index]);
|
||||
const landmarks = tf10.add(tf10.div(tf10.reshape(rawPalmLandmarks, [-1, 7, 2]), this.inputSizeTensor), this.anchors[index]);
|
||||
return tf10.mul(landmarks, this.inputSizeTensor);
|
||||
});
|
||||
}
|
||||
async getBoxes(input, config3) {
|
||||
const batched = this.model.predict(input);
|
||||
const predictions = tf10.squeeze(batched);
|
||||
batched.dispose();
|
||||
const scoresT = tf10.tidy(() => tf10.sigmoid(tf10.slice(predictions, [0, 0], [-1, 1])).squeeze());
|
||||
tf10.dispose(batched);
|
||||
const scoresT = tf10.tidy(() => tf10.squeeze(tf10.sigmoid(tf10.slice(predictions, [0, 0], [-1, 1]))));
|
||||
const scores = scoresT.dataSync();
|
||||
const rawBoxes = tf10.slice(predictions, [0, 1], [-1, 4]);
|
||||
const boxes = this.normalizeBoxes(rawBoxes);
|
||||
rawBoxes.dispose();
|
||||
tf10.dispose(rawBoxes);
|
||||
const filteredT = await tf10.image.nonMaxSuppressionAsync(boxes, scores, config3.hand.maxDetected, config3.hand.iouThreshold, config3.hand.minConfidence);
|
||||
const filtered = filteredT.arraySync();
|
||||
scoresT.dispose();
|
||||
filteredT.dispose();
|
||||
tf10.dispose(scoresT);
|
||||
tf10.dispose(filteredT);
|
||||
const hands = [];
|
||||
for (const index of filtered) {
|
||||
if (scores[index] >= config3.hand.minConfidence) {
|
||||
const matchingBox = tf10.slice(boxes, [index, 0], [1, -1]);
|
||||
const rawPalmLandmarks = tf10.slice(predictions, [index, 5], [1, 14]);
|
||||
const palmLandmarks = tf10.tidy(() => this.normalizeLandmarks(rawPalmLandmarks, index).reshape([-1, 2]));
|
||||
rawPalmLandmarks.dispose();
|
||||
const palmLandmarks = tf10.tidy(() => tf10.reshape(this.normalizeLandmarks(rawPalmLandmarks, index), [-1, 2]));
|
||||
tf10.dispose(rawPalmLandmarks);
|
||||
hands.push({ box: matchingBox, palmLandmarks, confidence: scores[index] });
|
||||
}
|
||||
}
|
||||
predictions.dispose();
|
||||
boxes.dispose();
|
||||
tf10.dispose(predictions);
|
||||
tf10.dispose(boxes);
|
||||
return hands;
|
||||
}
|
||||
async estimateHandBounds(input, config3) {
|
||||
const inputHeight = input.shape[1];
|
||||
const inputWidth = input.shape[2];
|
||||
const image18 = tf10.tidy(() => input.resizeBilinear([this.inputSize, this.inputSize]).div(127.5).sub(1));
|
||||
const image18 = tf10.tidy(() => tf10.sub(tf10.div(tf10.image.resizeBilinear(input, [this.inputSize, this.inputSize]), 127.5), 1));
|
||||
const predictions = await this.getBoxes(image18, config3);
|
||||
image18.dispose();
|
||||
tf10.dispose(image18);
|
||||
const hands = [];
|
||||
if (!predictions || predictions.length === 0)
|
||||
return hands;
|
||||
|
@ -7738,8 +7738,8 @@ var HandDetector = class {
|
|||
const startPoint = boxes.slice(0, 2);
|
||||
const endPoint = boxes.slice(2, 4);
|
||||
const palmLandmarks = prediction.palmLandmarks.arraySync();
|
||||
prediction.box.dispose();
|
||||
prediction.palmLandmarks.dispose();
|
||||
tf10.dispose(prediction.box);
|
||||
tf10.dispose(prediction.palmLandmarks);
|
||||
hands.push(scaleBoxCoordinates2({ startPoint, endPoint, palmLandmarks, confidence: prediction.confidence }, [inputWidth / this.inputSize, inputHeight / this.inputSize]));
|
||||
}
|
||||
return hands;
|
||||
|
@ -7902,18 +7902,18 @@ var HandPipeline = class {
|
|||
const rotationMatrix = buildRotationMatrix2(-angle, palmCenter);
|
||||
const newBox = useFreshBox ? this.getBoxForPalmLandmarks(currentBox.palmLandmarks, rotationMatrix) : currentBox;
|
||||
const croppedInput = cutBoxFromImageAndResize2(newBox, rotatedImage, [this.inputSize, this.inputSize]);
|
||||
const handImage = croppedInput.div(255);
|
||||
croppedInput.dispose();
|
||||
rotatedImage.dispose();
|
||||
const handImage = tf11.div(croppedInput, 255);
|
||||
tf11.dispose(croppedInput);
|
||||
tf11.dispose(rotatedImage);
|
||||
const [confidenceT, keypoints3] = await this.handPoseModel.predict(handImage);
|
||||
handImage.dispose();
|
||||
tf11.dispose(handImage);
|
||||
const confidence = confidenceT.dataSync()[0];
|
||||
confidenceT.dispose();
|
||||
tf11.dispose(confidenceT);
|
||||
if (confidence >= config3.hand.minConfidence) {
|
||||
const keypointsReshaped = tf11.reshape(keypoints3, [-1, 3]);
|
||||
const rawCoords = keypointsReshaped.arraySync();
|
||||
keypoints3.dispose();
|
||||
keypointsReshaped.dispose();
|
||||
tf11.dispose(keypoints3);
|
||||
tf11.dispose(keypointsReshaped);
|
||||
const coords3 = this.transformRawCoords(rawCoords, newBox, angle, rotationMatrix);
|
||||
const nextBoundingBox = this.getBoxForHandLandmarks(coords3);
|
||||
this.storedBoxes[i] = { ...nextBoundingBox, confidence };
|
||||
|
@ -7926,7 +7926,7 @@ var HandPipeline = class {
|
|||
} else {
|
||||
this.storedBoxes[i] = null;
|
||||
}
|
||||
keypoints3.dispose();
|
||||
tf11.dispose(keypoints3);
|
||||
} else {
|
||||
const enlarged = enlargeBox2(squarifyBox2(currentBox), handBoxEnlargeFactor);
|
||||
const result = {
|
||||
|
@ -8131,11 +8131,11 @@ async function predict6(image18, config3) {
|
|||
const imgSize = { width: image18.shape[2] || 0, height: image18.shape[1] || 0 };
|
||||
const resize = tf13.image.resizeBilinear(image18, [model4["width"], model4["height"]], false);
|
||||
const normalize = tf13.div(resize, [255]);
|
||||
resize.dispose();
|
||||
tf13.dispose(resize);
|
||||
const resT = await model4.predict(normalize);
|
||||
const points = ((_a = resT.find((t) => t.size === 195 || t.size === 155)) == null ? void 0 : _a.dataSync()) || [];
|
||||
resT.forEach((t) => t.dispose());
|
||||
normalize.dispose();
|
||||
resT.forEach((t) => tf13.dispose(t));
|
||||
tf13.dispose(normalize);
|
||||
const keypoints3 = [];
|
||||
const labels2 = (points == null ? void 0 : points.length) === 195 ? full : upper;
|
||||
const depth = 5;
|
||||
|
@ -8223,7 +8223,7 @@ async function predict7(image18, config3) {
|
|||
let resT;
|
||||
if (config3.body.enabled)
|
||||
resT = await model5.predict(tensor2);
|
||||
tensor2.dispose();
|
||||
tf14.dispose(tensor2);
|
||||
if (resT) {
|
||||
keypoints.length = 0;
|
||||
const squeeze7 = resT.squeeze();
|
||||
|
@ -8301,13 +8301,13 @@ async function predict8(image18, config3) {
|
|||
if (!model6.inputs[0].shape)
|
||||
return null;
|
||||
const resize = tf15.image.resizeBilinear(image18, [model6.inputs[0].shape[2], model6.inputs[0].shape[1]], false);
|
||||
const cast2 = tf15.cast(resize, "int32");
|
||||
return cast2;
|
||||
const cast5 = tf15.cast(resize, "int32");
|
||||
return cast5;
|
||||
});
|
||||
let resT;
|
||||
if (config3.body.enabled)
|
||||
resT = await model6.predict(tensor2);
|
||||
tensor2.dispose();
|
||||
tf15.dispose(tensor2);
|
||||
if (resT) {
|
||||
keypoints2.length = 0;
|
||||
const res = resT.arraySync();
|
||||
|
@ -8529,14 +8529,14 @@ async function predict9(image18, config3) {
|
|||
return new Promise(async (resolve) => {
|
||||
const outputSize = [image18.shape[2], image18.shape[1]];
|
||||
const resize = tf16.image.resizeBilinear(image18, [model7.inputSize, model7.inputSize], false);
|
||||
const norm = resize.div(255);
|
||||
const norm = tf16.div(resize, 255);
|
||||
const transpose = norm.transpose([0, 3, 1, 2]);
|
||||
norm.dispose();
|
||||
resize.dispose();
|
||||
tf16.dispose(norm);
|
||||
tf16.dispose(resize);
|
||||
let objectT;
|
||||
if (config3.object.enabled)
|
||||
objectT = await model7.predict(transpose);
|
||||
transpose.dispose();
|
||||
tf16.dispose(transpose);
|
||||
const obj = await process2(objectT, model7.inputSize, outputSize, config3);
|
||||
last3 = obj;
|
||||
resolve(obj);
|
||||
|
@ -8569,20 +8569,20 @@ async function process3(res, inputSize, outputShape, config3) {
|
|||
const results = [];
|
||||
const detections = res.arraySync();
|
||||
const squeezeT = tf17.squeeze(res);
|
||||
res.dispose();
|
||||
tf17.dispose(res);
|
||||
const arr = tf17.split(squeezeT, 6, 1);
|
||||
squeezeT.dispose();
|
||||
tf17.dispose(squeezeT);
|
||||
const stackT = tf17.stack([arr[1], arr[0], arr[3], arr[2]], 1);
|
||||
const boxesT = stackT.squeeze();
|
||||
const scoresT = arr[4].squeeze();
|
||||
const classesT = arr[5].squeeze();
|
||||
arr.forEach((t) => t.dispose());
|
||||
arr.forEach((t) => tf17.dispose(t));
|
||||
const nmsT = await tf17.image.nonMaxSuppressionAsync(boxesT, scoresT, config3.object.maxDetected, config3.object.iouThreshold, config3.object.minConfidence);
|
||||
boxesT.dispose();
|
||||
scoresT.dispose();
|
||||
classesT.dispose();
|
||||
tf17.dispose(boxesT);
|
||||
tf17.dispose(scoresT);
|
||||
tf17.dispose(classesT);
|
||||
const nms = nmsT.dataSync();
|
||||
nmsT.dispose();
|
||||
tf17.dispose(nmsT);
|
||||
let i = 0;
|
||||
for (const id of nms) {
|
||||
const score3 = Math.trunc(100 * detections[0][id][4]) / 100;
|
||||
|
@ -8618,7 +8618,7 @@ async function predict10(input, config3) {
|
|||
const outputSize = [input.shape[2], input.shape[1]];
|
||||
const resize = tf17.image.resizeBilinear(input, [model8.inputSize, model8.inputSize]);
|
||||
const objectT = config3.object.enabled ? model8.execute(resize, ["tower_0/detections"]) : null;
|
||||
resize.dispose();
|
||||
tf17.dispose(resize);
|
||||
const obj = await process3(objectT, model8.inputSize, outputSize, config3);
|
||||
last4 = obj;
|
||||
resolve(obj);
|
||||
|
@ -9467,10 +9467,10 @@ function process4(input, config3) {
|
|||
pixels = tf18.browser ? tf18.browser.fromPixels(data) : null;
|
||||
}
|
||||
if (pixels) {
|
||||
const casted = pixels.toFloat();
|
||||
tensor2 = casted.expandDims(0);
|
||||
pixels.dispose();
|
||||
casted.dispose();
|
||||
const casted = tf18.cast(pixels, "float32");
|
||||
tensor2 = tf18.expandDims(casted, 0);
|
||||
tf18.dispose(pixels);
|
||||
tf18.dispose(casted);
|
||||
}
|
||||
}
|
||||
const canvas2 = config3.filter.return ? outCanvas : null;
|
||||
|
@ -9500,7 +9500,7 @@ async function predict11(input) {
|
|||
if (!model9 || !model9.inputs[0].shape)
|
||||
return null;
|
||||
const resizeInput = tf19.image.resizeBilinear(input.tensor, [model9.inputs[0].shape[1], model9.inputs[0].shape[2]], false);
|
||||
const norm = resizeInput.div(255);
|
||||
const norm = tf19.div(resizeInput, 255);
|
||||
const res = model9.predict(norm);
|
||||
tf19.dispose(resizeInput);
|
||||
tf19.dispose(norm);
|
||||
|
@ -9509,13 +9509,13 @@ async function predict11(input) {
|
|||
if (squeeze7.shape[2] === 2) {
|
||||
const softmax = squeeze7.softmax();
|
||||
const [bg, fg] = tf19.unstack(softmax, 2);
|
||||
const expand = fg.expandDims(2);
|
||||
const pad = expand.expandDims(0);
|
||||
const expand = tf19.expandDims(fg, 2);
|
||||
const pad = tf19.expandDims(expand, 0);
|
||||
tf19.dispose(softmax);
|
||||
tf19.dispose(bg);
|
||||
tf19.dispose(fg);
|
||||
const crop = tf19.image.cropAndResize(pad, [[0, 0, 0.5, 0.5]], [0], [width, height]);
|
||||
resizeOutput = crop.squeeze(0);
|
||||
resizeOutput = tf19.squeeze(crop, 0);
|
||||
tf19.dispose(crop);
|
||||
tf19.dispose(expand);
|
||||
tf19.dispose(pad);
|
||||
|
@ -11419,7 +11419,7 @@ var Human = class {
|
|||
if (this.config.cacheSensitivity === 0)
|
||||
return false;
|
||||
const resizeFact = 32;
|
||||
const reduced = input.resizeBilinear([Math.trunc(input.shape[1] / resizeFact), Math.trunc(input.shape[2] / resizeFact)]);
|
||||
const reduced = tf21.image.resizeBilinear(input, [Math.trunc(input.shape[1] / resizeFact), Math.trunc(input.shape[2] / resizeFact)]);
|
||||
const reducedData = reduced.dataSync();
|
||||
let sum = 0;
|
||||
for (let i = 0; i < reducedData.length / 3; i++)
|
||||
|
@ -11610,7 +11610,7 @@ var Human = class {
|
|||
if (elapsedTime > 0)
|
||||
this.performance.segmentation = elapsedTime;
|
||||
if (process6.canvas) {
|
||||
process6.tensor.dispose();
|
||||
tf21.dispose(process6.tensor);
|
||||
process6 = process4(process6.canvas, this.config);
|
||||
}
|
||||
this.analyze("End Segmentation:");
|
||||
|
|
|
@ -499,7 +499,7 @@ var BlazeFaceModel = class {
|
|||
return null;
|
||||
const [batch, boxes, scores] = tf3.tidy(() => {
|
||||
const resizedImage = tf3.image.resizeBilinear(inputImage, [this.inputSize, this.inputSize]);
|
||||
const normalizedImage = resizedImage.div(127.5).sub(0.5);
|
||||
const normalizedImage = tf3.sub(tf3.div(resizedImage, 127.5), 0.5);
|
||||
const res = this.model.execute(normalizedImage);
|
||||
let batchOut;
|
||||
if (Array.isArray(res)) {
|
||||
|
@ -507,33 +507,33 @@ var BlazeFaceModel = class {
|
|||
const concat384 = tf3.concat([sorted[0], sorted[2]], 2);
|
||||
const concat512 = tf3.concat([sorted[1], sorted[3]], 2);
|
||||
const concat3 = tf3.concat([concat512, concat384], 1);
|
||||
batchOut = concat3.squeeze(0);
|
||||
batchOut = tf3.squeeze(concat3, 0);
|
||||
} else {
|
||||
batchOut = tf3.squeeze(res);
|
||||
}
|
||||
const boxesOut = decodeBounds(batchOut, this.anchors, [this.inputSize, this.inputSize]);
|
||||
const logits = tf3.slice(batchOut, [0, 0], [-1, 1]);
|
||||
const scoresOut = tf3.sigmoid(logits).squeeze().dataSync();
|
||||
const scoresOut = tf3.squeeze(tf3.sigmoid(logits)).dataSync();
|
||||
return [batchOut, boxesOut, scoresOut];
|
||||
});
|
||||
this.config = mergeDeep(this.config, userConfig);
|
||||
const nmsTensor = await tf3.image.nonMaxSuppressionAsync(boxes, scores, this.config.face.detector.maxDetected, this.config.face.detector.iouThreshold, this.config.face.detector.minConfidence);
|
||||
const nms = nmsTensor.arraySync();
|
||||
nmsTensor.dispose();
|
||||
tf3.dispose(nmsTensor);
|
||||
const annotatedBoxes = [];
|
||||
for (let i = 0; i < nms.length; i++) {
|
||||
const confidence = scores[nms[i]];
|
||||
if (confidence > this.config.face.detector.minConfidence) {
|
||||
const boundingBox = tf3.slice(boxes, [nms[i], 0], [1, -1]);
|
||||
const localBox = createBox(boundingBox);
|
||||
boundingBox.dispose();
|
||||
tf3.dispose(boundingBox);
|
||||
const anchor = this.anchorsData[nms[i]];
|
||||
const landmarks = tf3.tidy(() => tf3.slice(batch, [nms[i], keypointsCount - 1], [1, -1]).squeeze().reshape([keypointsCount, -1]));
|
||||
const landmarks = tf3.tidy(() => tf3.reshape(tf3.squeeze(tf3.slice(batch, [nms[i], keypointsCount - 1], [1, -1])), [keypointsCount, -1]));
|
||||
annotatedBoxes.push({ box: localBox, landmarks, anchor, confidence });
|
||||
}
|
||||
}
|
||||
batch.dispose();
|
||||
boxes.dispose();
|
||||
tf3.dispose(batch);
|
||||
tf3.dispose(boxes);
|
||||
return {
|
||||
boxes: annotatedBoxes,
|
||||
scaleFactor: [inputImage.shape[2] / this.inputSize, inputImage.shape[1] / this.inputSize]
|
||||
|
@ -3978,9 +3978,9 @@ var Pipeline = class {
|
|||
}
|
||||
if (detector && detector.boxes) {
|
||||
detector.boxes.forEach((prediction) => {
|
||||
prediction.box.startPoint.dispose();
|
||||
prediction.box.endPoint.dispose();
|
||||
prediction.landmarks.dispose();
|
||||
tf4.dispose(prediction.box.startPoint);
|
||||
tf4.dispose(prediction.box.endPoint);
|
||||
tf4.dispose(prediction.landmarks);
|
||||
});
|
||||
}
|
||||
const results = tf4.tidy(() => this.storedBoxes.map((box6, i) => {
|
||||
|
@ -3995,16 +3995,16 @@ var Pipeline = class {
|
|||
const rotatedImage = tf4.image.rotateWithOffset(input, angle, 0, faceCenterNormalized);
|
||||
rotationMatrix = buildRotationMatrix(-angle, faceCenter);
|
||||
if (config3.face.mesh.enabled)
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]), 255);
|
||||
else
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.boxSize, this.boxSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.boxSize, this.boxSize]), 255);
|
||||
} else {
|
||||
rotationMatrix = IDENTITY_MATRIX;
|
||||
const clonedImage = input.clone();
|
||||
if (config3.face.mesh.enabled)
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.meshSize, this.meshSize]), 255);
|
||||
else
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.boxSize, this.boxSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.boxSize, this.boxSize]), 255);
|
||||
}
|
||||
if (!config3.face.mesh.enabled) {
|
||||
const prediction2 = {
|
||||
|
@ -4056,9 +4056,9 @@ var Pipeline = class {
|
|||
angle = computeRotation(box6.landmarks[indexOfMouth], box6.landmarks[indexOfForehead]);
|
||||
const faceCenter = getBoxCenter({ startPoint: box6.startPoint, endPoint: box6.endPoint });
|
||||
const faceCenterNormalized = [faceCenter[0] / input.shape[2], faceCenter[1] / input.shape[1]];
|
||||
const rotatedImage = tf4.image.rotateWithOffset(input.toFloat(), angle, 0, faceCenterNormalized);
|
||||
const rotatedImage = tf4.image.rotateWithOffset(tf4.cast(input, "float32"), angle, 0, faceCenterNormalized);
|
||||
rotationMatrix = buildRotationMatrix(-angle, faceCenter);
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]), 255);
|
||||
}
|
||||
const prediction = {
|
||||
mesh,
|
||||
|
@ -4123,7 +4123,7 @@ async function predict(input, config3) {
|
|||
tensor: prediction.image
|
||||
});
|
||||
if (prediction.coords)
|
||||
prediction.coords.dispose();
|
||||
tf5.dispose(prediction.coords);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
@ -4211,7 +4211,7 @@ function enhance(input) {
|
|||
if (!model.inputs[0].shape)
|
||||
return null;
|
||||
const crop = tensor2.shape.length === 3 ? tf6.image.cropAndResize(tf6.expandDims(tensor2, 0), box6, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]) : tf6.image.cropAndResize(tensor2, box6, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]);
|
||||
const norm = crop.mul(255);
|
||||
const norm = tf6.mul(crop, 255);
|
||||
return norm;
|
||||
});
|
||||
return image18;
|
||||
|
@ -4245,7 +4245,7 @@ async function predict2(image18, config3, idx, count2) {
|
|||
obj.gender = gender[0] <= 0.5 ? "female" : "male";
|
||||
obj.genderScore = Math.min(0.99, confidence);
|
||||
}
|
||||
const age = resT.find((t) => t.shape[1] === 100).argMax(1).dataSync()[0];
|
||||
const age = tf6.argMax(resT.find((t) => t.shape[1] === 100), 1).dataSync()[0];
|
||||
const all2 = resT.find((t) => t.shape[1] === 100).dataSync();
|
||||
obj.age = Math.round(all2[age - 1] > all2[age + 1] ? 10 * age - 100 * all2[age - 1] : 10 * age + 100 * all2[age + 1]) / 10;
|
||||
const desc = resT.find((t) => t.shape[1] === 1024);
|
||||
|
@ -4289,19 +4289,19 @@ async function predict3(image18, config3, idx, count2) {
|
|||
return new Promise(async (resolve) => {
|
||||
const resize = tf7.image.resizeBilinear(image18, [model2.inputs[0].shape[2], model2.inputs[0].shape[1]], false);
|
||||
const [red, green, blue] = tf7.split(resize, 3, 3);
|
||||
resize.dispose();
|
||||
tf7.dispose(resize);
|
||||
const redNorm = tf7.mul(red, rgb[0]);
|
||||
const greenNorm = tf7.mul(green, rgb[1]);
|
||||
const blueNorm = tf7.mul(blue, rgb[2]);
|
||||
red.dispose();
|
||||
green.dispose();
|
||||
blue.dispose();
|
||||
tf7.dispose(red);
|
||||
tf7.dispose(green);
|
||||
tf7.dispose(blue);
|
||||
const grayscale = tf7.addN([redNorm, greenNorm, blueNorm]);
|
||||
redNorm.dispose();
|
||||
greenNorm.dispose();
|
||||
blueNorm.dispose();
|
||||
const normalize = tf7.tidy(() => grayscale.sub(0.5).mul(2));
|
||||
grayscale.dispose();
|
||||
tf7.dispose(redNorm);
|
||||
tf7.dispose(greenNorm);
|
||||
tf7.dispose(blueNorm);
|
||||
const normalize = tf7.tidy(() => tf7.mul(tf7.sub(grayscale, 0.5), 2));
|
||||
tf7.dispose(grayscale);
|
||||
const obj = [];
|
||||
if (config3.face.emotion.enabled) {
|
||||
const emotionT = await model2.predict(normalize);
|
||||
|
@ -4313,7 +4313,7 @@ async function predict3(image18, config3, idx, count2) {
|
|||
}
|
||||
obj.sort((a, b) => b.score - a.score);
|
||||
}
|
||||
normalize.dispose();
|
||||
tf7.dispose(normalize);
|
||||
last2[idx] = obj;
|
||||
lastCount2 = count2;
|
||||
resolve(obj);
|
||||
|
@ -4637,7 +4637,7 @@ async function predict4(input, config3) {
|
|||
if (!model3.inputs[0].shape)
|
||||
return [];
|
||||
const resized = tf8.image.resizeBilinear(input, [model3.inputs[0].shape[2], model3.inputs[0].shape[1]]);
|
||||
const normalized = resized.toFloat().div(127.5).sub(1);
|
||||
const normalized = tf8.sub(tf8.div(tf8.cast(resized, "float32"), 127.5), 1);
|
||||
const results = model3.execute(normalized, poseNetOutputs);
|
||||
const results3d = results.map((y) => tf8.squeeze(y, [0]));
|
||||
results3d[1] = results3d[1].sigmoid();
|
||||
|
@ -4645,7 +4645,7 @@ async function predict4(input, config3) {
|
|||
});
|
||||
const buffers = await Promise.all(res.map((tensor2) => tensor2.buffer()));
|
||||
for (const t of res)
|
||||
t.dispose();
|
||||
tf8.dispose(t);
|
||||
const decoded = await decode(buffers[0], buffers[1], buffers[2], buffers[3], config3.body.maxDetected, config3.body.minConfidence);
|
||||
if (!model3.inputs[0].shape)
|
||||
return [];
|
||||
|
@ -7694,43 +7694,43 @@ var HandDetector = class {
|
|||
}
|
||||
normalizeLandmarks(rawPalmLandmarks, index) {
|
||||
return tf10.tidy(() => {
|
||||
const landmarks = tf10.add(tf10.div(rawPalmLandmarks.reshape([-1, 7, 2]), this.inputSizeTensor), this.anchors[index]);
|
||||
const landmarks = tf10.add(tf10.div(tf10.reshape(rawPalmLandmarks, [-1, 7, 2]), this.inputSizeTensor), this.anchors[index]);
|
||||
return tf10.mul(landmarks, this.inputSizeTensor);
|
||||
});
|
||||
}
|
||||
async getBoxes(input, config3) {
|
||||
const batched = this.model.predict(input);
|
||||
const predictions = tf10.squeeze(batched);
|
||||
batched.dispose();
|
||||
const scoresT = tf10.tidy(() => tf10.sigmoid(tf10.slice(predictions, [0, 0], [-1, 1])).squeeze());
|
||||
tf10.dispose(batched);
|
||||
const scoresT = tf10.tidy(() => tf10.squeeze(tf10.sigmoid(tf10.slice(predictions, [0, 0], [-1, 1]))));
|
||||
const scores = scoresT.dataSync();
|
||||
const rawBoxes = tf10.slice(predictions, [0, 1], [-1, 4]);
|
||||
const boxes = this.normalizeBoxes(rawBoxes);
|
||||
rawBoxes.dispose();
|
||||
tf10.dispose(rawBoxes);
|
||||
const filteredT = await tf10.image.nonMaxSuppressionAsync(boxes, scores, config3.hand.maxDetected, config3.hand.iouThreshold, config3.hand.minConfidence);
|
||||
const filtered = filteredT.arraySync();
|
||||
scoresT.dispose();
|
||||
filteredT.dispose();
|
||||
tf10.dispose(scoresT);
|
||||
tf10.dispose(filteredT);
|
||||
const hands = [];
|
||||
for (const index of filtered) {
|
||||
if (scores[index] >= config3.hand.minConfidence) {
|
||||
const matchingBox = tf10.slice(boxes, [index, 0], [1, -1]);
|
||||
const rawPalmLandmarks = tf10.slice(predictions, [index, 5], [1, 14]);
|
||||
const palmLandmarks = tf10.tidy(() => this.normalizeLandmarks(rawPalmLandmarks, index).reshape([-1, 2]));
|
||||
rawPalmLandmarks.dispose();
|
||||
const palmLandmarks = tf10.tidy(() => tf10.reshape(this.normalizeLandmarks(rawPalmLandmarks, index), [-1, 2]));
|
||||
tf10.dispose(rawPalmLandmarks);
|
||||
hands.push({ box: matchingBox, palmLandmarks, confidence: scores[index] });
|
||||
}
|
||||
}
|
||||
predictions.dispose();
|
||||
boxes.dispose();
|
||||
tf10.dispose(predictions);
|
||||
tf10.dispose(boxes);
|
||||
return hands;
|
||||
}
|
||||
async estimateHandBounds(input, config3) {
|
||||
const inputHeight = input.shape[1];
|
||||
const inputWidth = input.shape[2];
|
||||
const image18 = tf10.tidy(() => input.resizeBilinear([this.inputSize, this.inputSize]).div(127.5).sub(1));
|
||||
const image18 = tf10.tidy(() => tf10.sub(tf10.div(tf10.image.resizeBilinear(input, [this.inputSize, this.inputSize]), 127.5), 1));
|
||||
const predictions = await this.getBoxes(image18, config3);
|
||||
image18.dispose();
|
||||
tf10.dispose(image18);
|
||||
const hands = [];
|
||||
if (!predictions || predictions.length === 0)
|
||||
return hands;
|
||||
|
@ -7739,8 +7739,8 @@ var HandDetector = class {
|
|||
const startPoint = boxes.slice(0, 2);
|
||||
const endPoint = boxes.slice(2, 4);
|
||||
const palmLandmarks = prediction.palmLandmarks.arraySync();
|
||||
prediction.box.dispose();
|
||||
prediction.palmLandmarks.dispose();
|
||||
tf10.dispose(prediction.box);
|
||||
tf10.dispose(prediction.palmLandmarks);
|
||||
hands.push(scaleBoxCoordinates2({ startPoint, endPoint, palmLandmarks, confidence: prediction.confidence }, [inputWidth / this.inputSize, inputHeight / this.inputSize]));
|
||||
}
|
||||
return hands;
|
||||
|
@ -7903,18 +7903,18 @@ var HandPipeline = class {
|
|||
const rotationMatrix = buildRotationMatrix2(-angle, palmCenter);
|
||||
const newBox = useFreshBox ? this.getBoxForPalmLandmarks(currentBox.palmLandmarks, rotationMatrix) : currentBox;
|
||||
const croppedInput = cutBoxFromImageAndResize2(newBox, rotatedImage, [this.inputSize, this.inputSize]);
|
||||
const handImage = croppedInput.div(255);
|
||||
croppedInput.dispose();
|
||||
rotatedImage.dispose();
|
||||
const handImage = tf11.div(croppedInput, 255);
|
||||
tf11.dispose(croppedInput);
|
||||
tf11.dispose(rotatedImage);
|
||||
const [confidenceT, keypoints3] = await this.handPoseModel.predict(handImage);
|
||||
handImage.dispose();
|
||||
tf11.dispose(handImage);
|
||||
const confidence = confidenceT.dataSync()[0];
|
||||
confidenceT.dispose();
|
||||
tf11.dispose(confidenceT);
|
||||
if (confidence >= config3.hand.minConfidence) {
|
||||
const keypointsReshaped = tf11.reshape(keypoints3, [-1, 3]);
|
||||
const rawCoords = keypointsReshaped.arraySync();
|
||||
keypoints3.dispose();
|
||||
keypointsReshaped.dispose();
|
||||
tf11.dispose(keypoints3);
|
||||
tf11.dispose(keypointsReshaped);
|
||||
const coords3 = this.transformRawCoords(rawCoords, newBox, angle, rotationMatrix);
|
||||
const nextBoundingBox = this.getBoxForHandLandmarks(coords3);
|
||||
this.storedBoxes[i] = { ...nextBoundingBox, confidence };
|
||||
|
@ -7927,7 +7927,7 @@ var HandPipeline = class {
|
|||
} else {
|
||||
this.storedBoxes[i] = null;
|
||||
}
|
||||
keypoints3.dispose();
|
||||
tf11.dispose(keypoints3);
|
||||
} else {
|
||||
const enlarged = enlargeBox2(squarifyBox2(currentBox), handBoxEnlargeFactor);
|
||||
const result = {
|
||||
|
@ -8132,11 +8132,11 @@ async function predict6(image18, config3) {
|
|||
const imgSize = { width: image18.shape[2] || 0, height: image18.shape[1] || 0 };
|
||||
const resize = tf13.image.resizeBilinear(image18, [model4["width"], model4["height"]], false);
|
||||
const normalize = tf13.div(resize, [255]);
|
||||
resize.dispose();
|
||||
tf13.dispose(resize);
|
||||
const resT = await model4.predict(normalize);
|
||||
const points = ((_a = resT.find((t) => t.size === 195 || t.size === 155)) == null ? void 0 : _a.dataSync()) || [];
|
||||
resT.forEach((t) => t.dispose());
|
||||
normalize.dispose();
|
||||
resT.forEach((t) => tf13.dispose(t));
|
||||
tf13.dispose(normalize);
|
||||
const keypoints3 = [];
|
||||
const labels2 = (points == null ? void 0 : points.length) === 195 ? full : upper;
|
||||
const depth = 5;
|
||||
|
@ -8224,7 +8224,7 @@ async function predict7(image18, config3) {
|
|||
let resT;
|
||||
if (config3.body.enabled)
|
||||
resT = await model5.predict(tensor2);
|
||||
tensor2.dispose();
|
||||
tf14.dispose(tensor2);
|
||||
if (resT) {
|
||||
keypoints.length = 0;
|
||||
const squeeze7 = resT.squeeze();
|
||||
|
@ -8302,13 +8302,13 @@ async function predict8(image18, config3) {
|
|||
if (!model6.inputs[0].shape)
|
||||
return null;
|
||||
const resize = tf15.image.resizeBilinear(image18, [model6.inputs[0].shape[2], model6.inputs[0].shape[1]], false);
|
||||
const cast2 = tf15.cast(resize, "int32");
|
||||
return cast2;
|
||||
const cast5 = tf15.cast(resize, "int32");
|
||||
return cast5;
|
||||
});
|
||||
let resT;
|
||||
if (config3.body.enabled)
|
||||
resT = await model6.predict(tensor2);
|
||||
tensor2.dispose();
|
||||
tf15.dispose(tensor2);
|
||||
if (resT) {
|
||||
keypoints2.length = 0;
|
||||
const res = resT.arraySync();
|
||||
|
@ -8530,14 +8530,14 @@ async function predict9(image18, config3) {
|
|||
return new Promise(async (resolve) => {
|
||||
const outputSize = [image18.shape[2], image18.shape[1]];
|
||||
const resize = tf16.image.resizeBilinear(image18, [model7.inputSize, model7.inputSize], false);
|
||||
const norm = resize.div(255);
|
||||
const norm = tf16.div(resize, 255);
|
||||
const transpose = norm.transpose([0, 3, 1, 2]);
|
||||
norm.dispose();
|
||||
resize.dispose();
|
||||
tf16.dispose(norm);
|
||||
tf16.dispose(resize);
|
||||
let objectT;
|
||||
if (config3.object.enabled)
|
||||
objectT = await model7.predict(transpose);
|
||||
transpose.dispose();
|
||||
tf16.dispose(transpose);
|
||||
const obj = await process2(objectT, model7.inputSize, outputSize, config3);
|
||||
last3 = obj;
|
||||
resolve(obj);
|
||||
|
@ -8570,20 +8570,20 @@ async function process3(res, inputSize, outputShape, config3) {
|
|||
const results = [];
|
||||
const detections = res.arraySync();
|
||||
const squeezeT = tf17.squeeze(res);
|
||||
res.dispose();
|
||||
tf17.dispose(res);
|
||||
const arr = tf17.split(squeezeT, 6, 1);
|
||||
squeezeT.dispose();
|
||||
tf17.dispose(squeezeT);
|
||||
const stackT = tf17.stack([arr[1], arr[0], arr[3], arr[2]], 1);
|
||||
const boxesT = stackT.squeeze();
|
||||
const scoresT = arr[4].squeeze();
|
||||
const classesT = arr[5].squeeze();
|
||||
arr.forEach((t) => t.dispose());
|
||||
arr.forEach((t) => tf17.dispose(t));
|
||||
const nmsT = await tf17.image.nonMaxSuppressionAsync(boxesT, scoresT, config3.object.maxDetected, config3.object.iouThreshold, config3.object.minConfidence);
|
||||
boxesT.dispose();
|
||||
scoresT.dispose();
|
||||
classesT.dispose();
|
||||
tf17.dispose(boxesT);
|
||||
tf17.dispose(scoresT);
|
||||
tf17.dispose(classesT);
|
||||
const nms = nmsT.dataSync();
|
||||
nmsT.dispose();
|
||||
tf17.dispose(nmsT);
|
||||
let i = 0;
|
||||
for (const id of nms) {
|
||||
const score3 = Math.trunc(100 * detections[0][id][4]) / 100;
|
||||
|
@ -8619,7 +8619,7 @@ async function predict10(input, config3) {
|
|||
const outputSize = [input.shape[2], input.shape[1]];
|
||||
const resize = tf17.image.resizeBilinear(input, [model8.inputSize, model8.inputSize]);
|
||||
const objectT = config3.object.enabled ? model8.execute(resize, ["tower_0/detections"]) : null;
|
||||
resize.dispose();
|
||||
tf17.dispose(resize);
|
||||
const obj = await process3(objectT, model8.inputSize, outputSize, config3);
|
||||
last4 = obj;
|
||||
resolve(obj);
|
||||
|
@ -9468,10 +9468,10 @@ function process4(input, config3) {
|
|||
pixels = tf18.browser ? tf18.browser.fromPixels(data) : null;
|
||||
}
|
||||
if (pixels) {
|
||||
const casted = pixels.toFloat();
|
||||
tensor2 = casted.expandDims(0);
|
||||
pixels.dispose();
|
||||
casted.dispose();
|
||||
const casted = tf18.cast(pixels, "float32");
|
||||
tensor2 = tf18.expandDims(casted, 0);
|
||||
tf18.dispose(pixels);
|
||||
tf18.dispose(casted);
|
||||
}
|
||||
}
|
||||
const canvas2 = config3.filter.return ? outCanvas : null;
|
||||
|
@ -9501,7 +9501,7 @@ async function predict11(input) {
|
|||
if (!model9 || !model9.inputs[0].shape)
|
||||
return null;
|
||||
const resizeInput = tf19.image.resizeBilinear(input.tensor, [model9.inputs[0].shape[1], model9.inputs[0].shape[2]], false);
|
||||
const norm = resizeInput.div(255);
|
||||
const norm = tf19.div(resizeInput, 255);
|
||||
const res = model9.predict(norm);
|
||||
tf19.dispose(resizeInput);
|
||||
tf19.dispose(norm);
|
||||
|
@ -9510,13 +9510,13 @@ async function predict11(input) {
|
|||
if (squeeze7.shape[2] === 2) {
|
||||
const softmax = squeeze7.softmax();
|
||||
const [bg, fg] = tf19.unstack(softmax, 2);
|
||||
const expand = fg.expandDims(2);
|
||||
const pad = expand.expandDims(0);
|
||||
const expand = tf19.expandDims(fg, 2);
|
||||
const pad = tf19.expandDims(expand, 0);
|
||||
tf19.dispose(softmax);
|
||||
tf19.dispose(bg);
|
||||
tf19.dispose(fg);
|
||||
const crop = tf19.image.cropAndResize(pad, [[0, 0, 0.5, 0.5]], [0], [width, height]);
|
||||
resizeOutput = crop.squeeze(0);
|
||||
resizeOutput = tf19.squeeze(crop, 0);
|
||||
tf19.dispose(crop);
|
||||
tf19.dispose(expand);
|
||||
tf19.dispose(pad);
|
||||
|
@ -11420,7 +11420,7 @@ var Human = class {
|
|||
if (this.config.cacheSensitivity === 0)
|
||||
return false;
|
||||
const resizeFact = 32;
|
||||
const reduced = input.resizeBilinear([Math.trunc(input.shape[1] / resizeFact), Math.trunc(input.shape[2] / resizeFact)]);
|
||||
const reduced = tf21.image.resizeBilinear(input, [Math.trunc(input.shape[1] / resizeFact), Math.trunc(input.shape[2] / resizeFact)]);
|
||||
const reducedData = reduced.dataSync();
|
||||
let sum = 0;
|
||||
for (let i = 0; i < reducedData.length / 3; i++)
|
||||
|
@ -11611,7 +11611,7 @@ var Human = class {
|
|||
if (elapsedTime > 0)
|
||||
this.performance.segmentation = elapsedTime;
|
||||
if (process6.canvas) {
|
||||
process6.tensor.dispose();
|
||||
tf21.dispose(process6.tensor);
|
||||
process6 = process4(process6.canvas, this.config);
|
||||
}
|
||||
this.analyze("End Segmentation:");
|
||||
|
|
|
@ -498,7 +498,7 @@ var BlazeFaceModel = class {
|
|||
return null;
|
||||
const [batch, boxes, scores] = tf3.tidy(() => {
|
||||
const resizedImage = tf3.image.resizeBilinear(inputImage, [this.inputSize, this.inputSize]);
|
||||
const normalizedImage = resizedImage.div(127.5).sub(0.5);
|
||||
const normalizedImage = tf3.sub(tf3.div(resizedImage, 127.5), 0.5);
|
||||
const res = this.model.execute(normalizedImage);
|
||||
let batchOut;
|
||||
if (Array.isArray(res)) {
|
||||
|
@ -506,33 +506,33 @@ var BlazeFaceModel = class {
|
|||
const concat384 = tf3.concat([sorted[0], sorted[2]], 2);
|
||||
const concat512 = tf3.concat([sorted[1], sorted[3]], 2);
|
||||
const concat3 = tf3.concat([concat512, concat384], 1);
|
||||
batchOut = concat3.squeeze(0);
|
||||
batchOut = tf3.squeeze(concat3, 0);
|
||||
} else {
|
||||
batchOut = tf3.squeeze(res);
|
||||
}
|
||||
const boxesOut = decodeBounds(batchOut, this.anchors, [this.inputSize, this.inputSize]);
|
||||
const logits = tf3.slice(batchOut, [0, 0], [-1, 1]);
|
||||
const scoresOut = tf3.sigmoid(logits).squeeze().dataSync();
|
||||
const scoresOut = tf3.squeeze(tf3.sigmoid(logits)).dataSync();
|
||||
return [batchOut, boxesOut, scoresOut];
|
||||
});
|
||||
this.config = mergeDeep(this.config, userConfig);
|
||||
const nmsTensor = await tf3.image.nonMaxSuppressionAsync(boxes, scores, this.config.face.detector.maxDetected, this.config.face.detector.iouThreshold, this.config.face.detector.minConfidence);
|
||||
const nms = nmsTensor.arraySync();
|
||||
nmsTensor.dispose();
|
||||
tf3.dispose(nmsTensor);
|
||||
const annotatedBoxes = [];
|
||||
for (let i = 0; i < nms.length; i++) {
|
||||
const confidence = scores[nms[i]];
|
||||
if (confidence > this.config.face.detector.minConfidence) {
|
||||
const boundingBox = tf3.slice(boxes, [nms[i], 0], [1, -1]);
|
||||
const localBox = createBox(boundingBox);
|
||||
boundingBox.dispose();
|
||||
tf3.dispose(boundingBox);
|
||||
const anchor = this.anchorsData[nms[i]];
|
||||
const landmarks = tf3.tidy(() => tf3.slice(batch, [nms[i], keypointsCount - 1], [1, -1]).squeeze().reshape([keypointsCount, -1]));
|
||||
const landmarks = tf3.tidy(() => tf3.reshape(tf3.squeeze(tf3.slice(batch, [nms[i], keypointsCount - 1], [1, -1])), [keypointsCount, -1]));
|
||||
annotatedBoxes.push({ box: localBox, landmarks, anchor, confidence });
|
||||
}
|
||||
}
|
||||
batch.dispose();
|
||||
boxes.dispose();
|
||||
tf3.dispose(batch);
|
||||
tf3.dispose(boxes);
|
||||
return {
|
||||
boxes: annotatedBoxes,
|
||||
scaleFactor: [inputImage.shape[2] / this.inputSize, inputImage.shape[1] / this.inputSize]
|
||||
|
@ -3977,9 +3977,9 @@ var Pipeline = class {
|
|||
}
|
||||
if (detector && detector.boxes) {
|
||||
detector.boxes.forEach((prediction) => {
|
||||
prediction.box.startPoint.dispose();
|
||||
prediction.box.endPoint.dispose();
|
||||
prediction.landmarks.dispose();
|
||||
tf4.dispose(prediction.box.startPoint);
|
||||
tf4.dispose(prediction.box.endPoint);
|
||||
tf4.dispose(prediction.landmarks);
|
||||
});
|
||||
}
|
||||
const results = tf4.tidy(() => this.storedBoxes.map((box6, i) => {
|
||||
|
@ -3994,16 +3994,16 @@ var Pipeline = class {
|
|||
const rotatedImage = tf4.image.rotateWithOffset(input, angle, 0, faceCenterNormalized);
|
||||
rotationMatrix = buildRotationMatrix(-angle, faceCenter);
|
||||
if (config3.face.mesh.enabled)
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]), 255);
|
||||
else
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.boxSize, this.boxSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.boxSize, this.boxSize]), 255);
|
||||
} else {
|
||||
rotationMatrix = IDENTITY_MATRIX;
|
||||
const clonedImage = input.clone();
|
||||
if (config3.face.mesh.enabled)
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.meshSize, this.meshSize]), 255);
|
||||
else
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.boxSize, this.boxSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, clonedImage, [this.boxSize, this.boxSize]), 255);
|
||||
}
|
||||
if (!config3.face.mesh.enabled) {
|
||||
const prediction2 = {
|
||||
|
@ -4055,9 +4055,9 @@ var Pipeline = class {
|
|||
angle = computeRotation(box6.landmarks[indexOfMouth], box6.landmarks[indexOfForehead]);
|
||||
const faceCenter = getBoxCenter({ startPoint: box6.startPoint, endPoint: box6.endPoint });
|
||||
const faceCenterNormalized = [faceCenter[0] / input.shape[2], faceCenter[1] / input.shape[1]];
|
||||
const rotatedImage = tf4.image.rotateWithOffset(input.toFloat(), angle, 0, faceCenterNormalized);
|
||||
const rotatedImage = tf4.image.rotateWithOffset(tf4.cast(input, "float32"), angle, 0, faceCenterNormalized);
|
||||
rotationMatrix = buildRotationMatrix(-angle, faceCenter);
|
||||
face5 = cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
face5 = tf4.div(cutBoxFromImageAndResize({ startPoint: box6.startPoint, endPoint: box6.endPoint }, rotatedImage, [this.meshSize, this.meshSize]), 255);
|
||||
}
|
||||
const prediction = {
|
||||
mesh,
|
||||
|
@ -4122,7 +4122,7 @@ async function predict(input, config3) {
|
|||
tensor: prediction.image
|
||||
});
|
||||
if (prediction.coords)
|
||||
prediction.coords.dispose();
|
||||
tf5.dispose(prediction.coords);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
@ -4210,7 +4210,7 @@ function enhance(input) {
|
|||
if (!model.inputs[0].shape)
|
||||
return null;
|
||||
const crop = tensor2.shape.length === 3 ? tf6.image.cropAndResize(tf6.expandDims(tensor2, 0), box6, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]) : tf6.image.cropAndResize(tensor2, box6, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]);
|
||||
const norm = crop.mul(255);
|
||||
const norm = tf6.mul(crop, 255);
|
||||
return norm;
|
||||
});
|
||||
return image18;
|
||||
|
@ -4244,7 +4244,7 @@ async function predict2(image18, config3, idx, count2) {
|
|||
obj.gender = gender[0] <= 0.5 ? "female" : "male";
|
||||
obj.genderScore = Math.min(0.99, confidence);
|
||||
}
|
||||
const age = resT.find((t) => t.shape[1] === 100).argMax(1).dataSync()[0];
|
||||
const age = tf6.argMax(resT.find((t) => t.shape[1] === 100), 1).dataSync()[0];
|
||||
const all2 = resT.find((t) => t.shape[1] === 100).dataSync();
|
||||
obj.age = Math.round(all2[age - 1] > all2[age + 1] ? 10 * age - 100 * all2[age - 1] : 10 * age + 100 * all2[age + 1]) / 10;
|
||||
const desc = resT.find((t) => t.shape[1] === 1024);
|
||||
|
@ -4288,19 +4288,19 @@ async function predict3(image18, config3, idx, count2) {
|
|||
return new Promise(async (resolve) => {
|
||||
const resize = tf7.image.resizeBilinear(image18, [model2.inputs[0].shape[2], model2.inputs[0].shape[1]], false);
|
||||
const [red, green, blue] = tf7.split(resize, 3, 3);
|
||||
resize.dispose();
|
||||
tf7.dispose(resize);
|
||||
const redNorm = tf7.mul(red, rgb[0]);
|
||||
const greenNorm = tf7.mul(green, rgb[1]);
|
||||
const blueNorm = tf7.mul(blue, rgb[2]);
|
||||
red.dispose();
|
||||
green.dispose();
|
||||
blue.dispose();
|
||||
tf7.dispose(red);
|
||||
tf7.dispose(green);
|
||||
tf7.dispose(blue);
|
||||
const grayscale = tf7.addN([redNorm, greenNorm, blueNorm]);
|
||||
redNorm.dispose();
|
||||
greenNorm.dispose();
|
||||
blueNorm.dispose();
|
||||
const normalize = tf7.tidy(() => grayscale.sub(0.5).mul(2));
|
||||
grayscale.dispose();
|
||||
tf7.dispose(redNorm);
|
||||
tf7.dispose(greenNorm);
|
||||
tf7.dispose(blueNorm);
|
||||
const normalize = tf7.tidy(() => tf7.mul(tf7.sub(grayscale, 0.5), 2));
|
||||
tf7.dispose(grayscale);
|
||||
const obj = [];
|
||||
if (config3.face.emotion.enabled) {
|
||||
const emotionT = await model2.predict(normalize);
|
||||
|
@ -4312,7 +4312,7 @@ async function predict3(image18, config3, idx, count2) {
|
|||
}
|
||||
obj.sort((a, b) => b.score - a.score);
|
||||
}
|
||||
normalize.dispose();
|
||||
tf7.dispose(normalize);
|
||||
last2[idx] = obj;
|
||||
lastCount2 = count2;
|
||||
resolve(obj);
|
||||
|
@ -4636,7 +4636,7 @@ async function predict4(input, config3) {
|
|||
if (!model3.inputs[0].shape)
|
||||
return [];
|
||||
const resized = tf8.image.resizeBilinear(input, [model3.inputs[0].shape[2], model3.inputs[0].shape[1]]);
|
||||
const normalized = resized.toFloat().div(127.5).sub(1);
|
||||
const normalized = tf8.sub(tf8.div(tf8.cast(resized, "float32"), 127.5), 1);
|
||||
const results = model3.execute(normalized, poseNetOutputs);
|
||||
const results3d = results.map((y) => tf8.squeeze(y, [0]));
|
||||
results3d[1] = results3d[1].sigmoid();
|
||||
|
@ -4644,7 +4644,7 @@ async function predict4(input, config3) {
|
|||
});
|
||||
const buffers = await Promise.all(res.map((tensor2) => tensor2.buffer()));
|
||||
for (const t of res)
|
||||
t.dispose();
|
||||
tf8.dispose(t);
|
||||
const decoded = await decode(buffers[0], buffers[1], buffers[2], buffers[3], config3.body.maxDetected, config3.body.minConfidence);
|
||||
if (!model3.inputs[0].shape)
|
||||
return [];
|
||||
|
@ -7693,43 +7693,43 @@ var HandDetector = class {
|
|||
}
|
||||
normalizeLandmarks(rawPalmLandmarks, index) {
|
||||
return tf10.tidy(() => {
|
||||
const landmarks = tf10.add(tf10.div(rawPalmLandmarks.reshape([-1, 7, 2]), this.inputSizeTensor), this.anchors[index]);
|
||||
const landmarks = tf10.add(tf10.div(tf10.reshape(rawPalmLandmarks, [-1, 7, 2]), this.inputSizeTensor), this.anchors[index]);
|
||||
return tf10.mul(landmarks, this.inputSizeTensor);
|
||||
});
|
||||
}
|
||||
async getBoxes(input, config3) {
|
||||
const batched = this.model.predict(input);
|
||||
const predictions = tf10.squeeze(batched);
|
||||
batched.dispose();
|
||||
const scoresT = tf10.tidy(() => tf10.sigmoid(tf10.slice(predictions, [0, 0], [-1, 1])).squeeze());
|
||||
tf10.dispose(batched);
|
||||
const scoresT = tf10.tidy(() => tf10.squeeze(tf10.sigmoid(tf10.slice(predictions, [0, 0], [-1, 1]))));
|
||||
const scores = scoresT.dataSync();
|
||||
const rawBoxes = tf10.slice(predictions, [0, 1], [-1, 4]);
|
||||
const boxes = this.normalizeBoxes(rawBoxes);
|
||||
rawBoxes.dispose();
|
||||
tf10.dispose(rawBoxes);
|
||||
const filteredT = await tf10.image.nonMaxSuppressionAsync(boxes, scores, config3.hand.maxDetected, config3.hand.iouThreshold, config3.hand.minConfidence);
|
||||
const filtered = filteredT.arraySync();
|
||||
scoresT.dispose();
|
||||
filteredT.dispose();
|
||||
tf10.dispose(scoresT);
|
||||
tf10.dispose(filteredT);
|
||||
const hands = [];
|
||||
for (const index of filtered) {
|
||||
if (scores[index] >= config3.hand.minConfidence) {
|
||||
const matchingBox = tf10.slice(boxes, [index, 0], [1, -1]);
|
||||
const rawPalmLandmarks = tf10.slice(predictions, [index, 5], [1, 14]);
|
||||
const palmLandmarks = tf10.tidy(() => this.normalizeLandmarks(rawPalmLandmarks, index).reshape([-1, 2]));
|
||||
rawPalmLandmarks.dispose();
|
||||
const palmLandmarks = tf10.tidy(() => tf10.reshape(this.normalizeLandmarks(rawPalmLandmarks, index), [-1, 2]));
|
||||
tf10.dispose(rawPalmLandmarks);
|
||||
hands.push({ box: matchingBox, palmLandmarks, confidence: scores[index] });
|
||||
}
|
||||
}
|
||||
predictions.dispose();
|
||||
boxes.dispose();
|
||||
tf10.dispose(predictions);
|
||||
tf10.dispose(boxes);
|
||||
return hands;
|
||||
}
|
||||
async estimateHandBounds(input, config3) {
|
||||
const inputHeight = input.shape[1];
|
||||
const inputWidth = input.shape[2];
|
||||
const image18 = tf10.tidy(() => input.resizeBilinear([this.inputSize, this.inputSize]).div(127.5).sub(1));
|
||||
const image18 = tf10.tidy(() => tf10.sub(tf10.div(tf10.image.resizeBilinear(input, [this.inputSize, this.inputSize]), 127.5), 1));
|
||||
const predictions = await this.getBoxes(image18, config3);
|
||||
image18.dispose();
|
||||
tf10.dispose(image18);
|
||||
const hands = [];
|
||||
if (!predictions || predictions.length === 0)
|
||||
return hands;
|
||||
|
@ -7738,8 +7738,8 @@ var HandDetector = class {
|
|||
const startPoint = boxes.slice(0, 2);
|
||||
const endPoint = boxes.slice(2, 4);
|
||||
const palmLandmarks = prediction.palmLandmarks.arraySync();
|
||||
prediction.box.dispose();
|
||||
prediction.palmLandmarks.dispose();
|
||||
tf10.dispose(prediction.box);
|
||||
tf10.dispose(prediction.palmLandmarks);
|
||||
hands.push(scaleBoxCoordinates2({ startPoint, endPoint, palmLandmarks, confidence: prediction.confidence }, [inputWidth / this.inputSize, inputHeight / this.inputSize]));
|
||||
}
|
||||
return hands;
|
||||
|
@ -7902,18 +7902,18 @@ var HandPipeline = class {
|
|||
const rotationMatrix = buildRotationMatrix2(-angle, palmCenter);
|
||||
const newBox = useFreshBox ? this.getBoxForPalmLandmarks(currentBox.palmLandmarks, rotationMatrix) : currentBox;
|
||||
const croppedInput = cutBoxFromImageAndResize2(newBox, rotatedImage, [this.inputSize, this.inputSize]);
|
||||
const handImage = croppedInput.div(255);
|
||||
croppedInput.dispose();
|
||||
rotatedImage.dispose();
|
||||
const handImage = tf11.div(croppedInput, 255);
|
||||
tf11.dispose(croppedInput);
|
||||
tf11.dispose(rotatedImage);
|
||||
const [confidenceT, keypoints3] = await this.handPoseModel.predict(handImage);
|
||||
handImage.dispose();
|
||||
tf11.dispose(handImage);
|
||||
const confidence = confidenceT.dataSync()[0];
|
||||
confidenceT.dispose();
|
||||
tf11.dispose(confidenceT);
|
||||
if (confidence >= config3.hand.minConfidence) {
|
||||
const keypointsReshaped = tf11.reshape(keypoints3, [-1, 3]);
|
||||
const rawCoords = keypointsReshaped.arraySync();
|
||||
keypoints3.dispose();
|
||||
keypointsReshaped.dispose();
|
||||
tf11.dispose(keypoints3);
|
||||
tf11.dispose(keypointsReshaped);
|
||||
const coords3 = this.transformRawCoords(rawCoords, newBox, angle, rotationMatrix);
|
||||
const nextBoundingBox = this.getBoxForHandLandmarks(coords3);
|
||||
this.storedBoxes[i] = { ...nextBoundingBox, confidence };
|
||||
|
@ -7926,7 +7926,7 @@ var HandPipeline = class {
|
|||
} else {
|
||||
this.storedBoxes[i] = null;
|
||||
}
|
||||
keypoints3.dispose();
|
||||
tf11.dispose(keypoints3);
|
||||
} else {
|
||||
const enlarged = enlargeBox2(squarifyBox2(currentBox), handBoxEnlargeFactor);
|
||||
const result = {
|
||||
|
@ -8131,11 +8131,11 @@ async function predict6(image18, config3) {
|
|||
const imgSize = { width: image18.shape[2] || 0, height: image18.shape[1] || 0 };
|
||||
const resize = tf13.image.resizeBilinear(image18, [model4["width"], model4["height"]], false);
|
||||
const normalize = tf13.div(resize, [255]);
|
||||
resize.dispose();
|
||||
tf13.dispose(resize);
|
||||
const resT = await model4.predict(normalize);
|
||||
const points = ((_a = resT.find((t) => t.size === 195 || t.size === 155)) == null ? void 0 : _a.dataSync()) || [];
|
||||
resT.forEach((t) => t.dispose());
|
||||
normalize.dispose();
|
||||
resT.forEach((t) => tf13.dispose(t));
|
||||
tf13.dispose(normalize);
|
||||
const keypoints3 = [];
|
||||
const labels2 = (points == null ? void 0 : points.length) === 195 ? full : upper;
|
||||
const depth = 5;
|
||||
|
@ -8223,7 +8223,7 @@ async function predict7(image18, config3) {
|
|||
let resT;
|
||||
if (config3.body.enabled)
|
||||
resT = await model5.predict(tensor2);
|
||||
tensor2.dispose();
|
||||
tf14.dispose(tensor2);
|
||||
if (resT) {
|
||||
keypoints.length = 0;
|
||||
const squeeze7 = resT.squeeze();
|
||||
|
@ -8301,13 +8301,13 @@ async function predict8(image18, config3) {
|
|||
if (!model6.inputs[0].shape)
|
||||
return null;
|
||||
const resize = tf15.image.resizeBilinear(image18, [model6.inputs[0].shape[2], model6.inputs[0].shape[1]], false);
|
||||
const cast2 = tf15.cast(resize, "int32");
|
||||
return cast2;
|
||||
const cast5 = tf15.cast(resize, "int32");
|
||||
return cast5;
|
||||
});
|
||||
let resT;
|
||||
if (config3.body.enabled)
|
||||
resT = await model6.predict(tensor2);
|
||||
tensor2.dispose();
|
||||
tf15.dispose(tensor2);
|
||||
if (resT) {
|
||||
keypoints2.length = 0;
|
||||
const res = resT.arraySync();
|
||||
|
@ -8529,14 +8529,14 @@ async function predict9(image18, config3) {
|
|||
return new Promise(async (resolve) => {
|
||||
const outputSize = [image18.shape[2], image18.shape[1]];
|
||||
const resize = tf16.image.resizeBilinear(image18, [model7.inputSize, model7.inputSize], false);
|
||||
const norm = resize.div(255);
|
||||
const norm = tf16.div(resize, 255);
|
||||
const transpose = norm.transpose([0, 3, 1, 2]);
|
||||
norm.dispose();
|
||||
resize.dispose();
|
||||
tf16.dispose(norm);
|
||||
tf16.dispose(resize);
|
||||
let objectT;
|
||||
if (config3.object.enabled)
|
||||
objectT = await model7.predict(transpose);
|
||||
transpose.dispose();
|
||||
tf16.dispose(transpose);
|
||||
const obj = await process2(objectT, model7.inputSize, outputSize, config3);
|
||||
last3 = obj;
|
||||
resolve(obj);
|
||||
|
@ -8569,20 +8569,20 @@ async function process3(res, inputSize, outputShape, config3) {
|
|||
const results = [];
|
||||
const detections = res.arraySync();
|
||||
const squeezeT = tf17.squeeze(res);
|
||||
res.dispose();
|
||||
tf17.dispose(res);
|
||||
const arr = tf17.split(squeezeT, 6, 1);
|
||||
squeezeT.dispose();
|
||||
tf17.dispose(squeezeT);
|
||||
const stackT = tf17.stack([arr[1], arr[0], arr[3], arr[2]], 1);
|
||||
const boxesT = stackT.squeeze();
|
||||
const scoresT = arr[4].squeeze();
|
||||
const classesT = arr[5].squeeze();
|
||||
arr.forEach((t) => t.dispose());
|
||||
arr.forEach((t) => tf17.dispose(t));
|
||||
const nmsT = await tf17.image.nonMaxSuppressionAsync(boxesT, scoresT, config3.object.maxDetected, config3.object.iouThreshold, config3.object.minConfidence);
|
||||
boxesT.dispose();
|
||||
scoresT.dispose();
|
||||
classesT.dispose();
|
||||
tf17.dispose(boxesT);
|
||||
tf17.dispose(scoresT);
|
||||
tf17.dispose(classesT);
|
||||
const nms = nmsT.dataSync();
|
||||
nmsT.dispose();
|
||||
tf17.dispose(nmsT);
|
||||
let i = 0;
|
||||
for (const id of nms) {
|
||||
const score3 = Math.trunc(100 * detections[0][id][4]) / 100;
|
||||
|
@ -8618,7 +8618,7 @@ async function predict10(input, config3) {
|
|||
const outputSize = [input.shape[2], input.shape[1]];
|
||||
const resize = tf17.image.resizeBilinear(input, [model8.inputSize, model8.inputSize]);
|
||||
const objectT = config3.object.enabled ? model8.execute(resize, ["tower_0/detections"]) : null;
|
||||
resize.dispose();
|
||||
tf17.dispose(resize);
|
||||
const obj = await process3(objectT, model8.inputSize, outputSize, config3);
|
||||
last4 = obj;
|
||||
resolve(obj);
|
||||
|
@ -9467,10 +9467,10 @@ function process4(input, config3) {
|
|||
pixels = tf18.browser ? tf18.browser.fromPixels(data) : null;
|
||||
}
|
||||
if (pixels) {
|
||||
const casted = pixels.toFloat();
|
||||
tensor2 = casted.expandDims(0);
|
||||
pixels.dispose();
|
||||
casted.dispose();
|
||||
const casted = tf18.cast(pixels, "float32");
|
||||
tensor2 = tf18.expandDims(casted, 0);
|
||||
tf18.dispose(pixels);
|
||||
tf18.dispose(casted);
|
||||
}
|
||||
}
|
||||
const canvas2 = config3.filter.return ? outCanvas : null;
|
||||
|
@ -9500,7 +9500,7 @@ async function predict11(input) {
|
|||
if (!model9 || !model9.inputs[0].shape)
|
||||
return null;
|
||||
const resizeInput = tf19.image.resizeBilinear(input.tensor, [model9.inputs[0].shape[1], model9.inputs[0].shape[2]], false);
|
||||
const norm = resizeInput.div(255);
|
||||
const norm = tf19.div(resizeInput, 255);
|
||||
const res = model9.predict(norm);
|
||||
tf19.dispose(resizeInput);
|
||||
tf19.dispose(norm);
|
||||
|
@ -9509,13 +9509,13 @@ async function predict11(input) {
|
|||
if (squeeze7.shape[2] === 2) {
|
||||
const softmax = squeeze7.softmax();
|
||||
const [bg, fg] = tf19.unstack(softmax, 2);
|
||||
const expand = fg.expandDims(2);
|
||||
const pad = expand.expandDims(0);
|
||||
const expand = tf19.expandDims(fg, 2);
|
||||
const pad = tf19.expandDims(expand, 0);
|
||||
tf19.dispose(softmax);
|
||||
tf19.dispose(bg);
|
||||
tf19.dispose(fg);
|
||||
const crop = tf19.image.cropAndResize(pad, [[0, 0, 0.5, 0.5]], [0], [width, height]);
|
||||
resizeOutput = crop.squeeze(0);
|
||||
resizeOutput = tf19.squeeze(crop, 0);
|
||||
tf19.dispose(crop);
|
||||
tf19.dispose(expand);
|
||||
tf19.dispose(pad);
|
||||
|
@ -11419,7 +11419,7 @@ var Human = class {
|
|||
if (this.config.cacheSensitivity === 0)
|
||||
return false;
|
||||
const resizeFact = 32;
|
||||
const reduced = input.resizeBilinear([Math.trunc(input.shape[1] / resizeFact), Math.trunc(input.shape[2] / resizeFact)]);
|
||||
const reduced = tf21.image.resizeBilinear(input, [Math.trunc(input.shape[1] / resizeFact), Math.trunc(input.shape[2] / resizeFact)]);
|
||||
const reducedData = reduced.dataSync();
|
||||
let sum = 0;
|
||||
for (let i = 0; i < reducedData.length / 3; i++)
|
||||
|
@ -11610,7 +11610,7 @@ var Human = class {
|
|||
if (elapsedTime > 0)
|
||||
this.performance.segmentation = elapsedTime;
|
||||
if (process6.canvas) {
|
||||
process6.tensor.dispose();
|
||||
tf21.dispose(process6.tensor);
|
||||
process6 = process4(process6.canvas, this.config);
|
||||
}
|
||||
this.analyze("End Segmentation:");
|
||||
|
|
|
@ -42,13 +42,13 @@ export async function predict(image: Tensor, config: Config | any) {
|
|||
const obj = { age: 0 };
|
||||
|
||||
if (config.face.age.enabled) ageT = await model.predict(enhance);
|
||||
enhance.dispose();
|
||||
tf.dispose(enhance);
|
||||
|
||||
if (ageT) {
|
||||
const data = ageT.dataSync();
|
||||
obj.age = Math.trunc(10 * data[0]) / 10;
|
||||
}
|
||||
ageT.dispose();
|
||||
tf.dispose(ageT);
|
||||
|
||||
last = obj;
|
||||
resolve(obj);
|
||||
|
|
|
@ -43,7 +43,7 @@ export class BlazeFaceModel {
|
|||
if ((!inputImage) || (inputImage.isDisposedInternal) || (inputImage.shape.length !== 4) || (inputImage.shape[1] < 1) || (inputImage.shape[2] < 1)) return null;
|
||||
const [batch, boxes, scores] = tf.tidy(() => {
|
||||
const resizedImage = tf.image.resizeBilinear(inputImage, [this.inputSize, this.inputSize]);
|
||||
const normalizedImage = resizedImage.div(127.5).sub(0.5);
|
||||
const normalizedImage = tf.sub(tf.div(resizedImage, 127.5), 0.5);
|
||||
const res = this.model.execute(normalizedImage);
|
||||
let batchOut;
|
||||
if (Array.isArray(res)) { // are we using tfhub or pinto converted model?
|
||||
|
@ -51,13 +51,13 @@ export class BlazeFaceModel {
|
|||
const concat384 = tf.concat([sorted[0], sorted[2]], 2); // dim: 384, 1 + 16
|
||||
const concat512 = tf.concat([sorted[1], sorted[3]], 2); // dim: 512, 1 + 16
|
||||
const concat = tf.concat([concat512, concat384], 1);
|
||||
batchOut = concat.squeeze(0);
|
||||
batchOut = tf.squeeze(concat, 0);
|
||||
} else {
|
||||
batchOut = tf.squeeze(res); // when using tfhub model
|
||||
}
|
||||
const boxesOut = decodeBounds(batchOut, this.anchors, [this.inputSize, this.inputSize]);
|
||||
const logits = tf.slice(batchOut, [0, 0], [-1, 1]);
|
||||
const scoresOut = tf.sigmoid(logits).squeeze().dataSync();
|
||||
const scoresOut = tf.squeeze(tf.sigmoid(logits)).dataSync();
|
||||
return [batchOut, boxesOut, scoresOut];
|
||||
});
|
||||
|
||||
|
@ -65,23 +65,21 @@ export class BlazeFaceModel {
|
|||
|
||||
const nmsTensor = await tf.image.nonMaxSuppressionAsync(boxes, scores, this.config.face.detector.maxDetected, this.config.face.detector.iouThreshold, this.config.face.detector.minConfidence);
|
||||
const nms = nmsTensor.arraySync();
|
||||
nmsTensor.dispose();
|
||||
tf.dispose(nmsTensor);
|
||||
const annotatedBoxes: Array<{ box: { startPoint: Tensor, endPoint: Tensor }, landmarks: Tensor, anchor: number[], confidence: number }> = [];
|
||||
for (let i = 0; i < nms.length; i++) {
|
||||
const confidence = scores[nms[i]];
|
||||
if (confidence > this.config.face.detector.minConfidence) {
|
||||
const boundingBox = tf.slice(boxes, [nms[i], 0], [1, -1]);
|
||||
const localBox = box.createBox(boundingBox);
|
||||
boundingBox.dispose();
|
||||
tf.dispose(boundingBox);
|
||||
const anchor = this.anchorsData[nms[i]];
|
||||
const landmarks = tf.tidy(() => tf.slice(batch, [nms[i], keypointsCount - 1], [1, -1]).squeeze().reshape([keypointsCount, -1]));
|
||||
const landmarks = tf.tidy(() => tf.reshape(tf.squeeze(tf.slice(batch, [nms[i], keypointsCount - 1], [1, -1])), [keypointsCount, -1]));
|
||||
annotatedBoxes.push({ box: localBox, landmarks, anchor, confidence });
|
||||
}
|
||||
}
|
||||
// boundingBoxes.forEach((t) => t.dispose());
|
||||
batch.dispose();
|
||||
boxes.dispose();
|
||||
// scores.dispose();
|
||||
tf.dispose(batch);
|
||||
tf.dispose(boxes);
|
||||
return {
|
||||
boxes: annotatedBoxes,
|
||||
scaleFactor: [inputImage.shape[2] / this.inputSize, inputImage.shape[1] / this.inputSize],
|
||||
|
|
|
@ -60,8 +60,8 @@ export function calculateLandmarksBoundingBox(landmarks) {
|
|||
}
|
||||
|
||||
export const disposeBox = (t) => {
|
||||
t.startPoint.dispose();
|
||||
t.endPoint.dispose();
|
||||
tf.dispose(t.startPoint);
|
||||
tf.dispose(t.endPoint);
|
||||
};
|
||||
|
||||
export const createBox = (startEndTensor) => ({
|
||||
|
|
|
@ -54,7 +54,7 @@ export async function predict(input: Tensor, config: Config): Promise<Face[]> {
|
|||
image: prediction.image,
|
||||
tensor: prediction.image,
|
||||
});
|
||||
if (prediction.coords) prediction.coords.dispose();
|
||||
if (prediction.coords) tf.dispose(prediction.coords);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
|
|
@ -190,9 +190,9 @@ export class Pipeline {
|
|||
}
|
||||
if (detector && detector.boxes) {
|
||||
detector.boxes.forEach((prediction) => {
|
||||
prediction.box.startPoint.dispose();
|
||||
prediction.box.endPoint.dispose();
|
||||
prediction.landmarks.dispose();
|
||||
tf.dispose(prediction.box.startPoint);
|
||||
tf.dispose(prediction.box.endPoint);
|
||||
tf.dispose(prediction.landmarks);
|
||||
});
|
||||
}
|
||||
const results = tf.tidy(() => this.storedBoxes.map((box, i) => {
|
||||
|
@ -208,13 +208,13 @@ export class Pipeline {
|
|||
const faceCenterNormalized = [faceCenter[0] / input.shape[2], faceCenter[1] / input.shape[1]];
|
||||
const rotatedImage = tf.image.rotateWithOffset(input, angle, 0, faceCenterNormalized); // rotateWithOffset is not defined for tfjs-node
|
||||
rotationMatrix = util.buildRotationMatrix(-angle, faceCenter);
|
||||
if (config.face.mesh.enabled) face = bounding.cutBoxFromImageAndResize({ startPoint: box.startPoint, endPoint: box.endPoint }, rotatedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
else face = bounding.cutBoxFromImageAndResize({ startPoint: box.startPoint, endPoint: box.endPoint }, rotatedImage, [this.boxSize, this.boxSize]).div(255);
|
||||
if (config.face.mesh.enabled) face = tf.div(bounding.cutBoxFromImageAndResize({ startPoint: box.startPoint, endPoint: box.endPoint }, rotatedImage, [this.meshSize, this.meshSize]), 255);
|
||||
else face = tf.div(bounding.cutBoxFromImageAndResize({ startPoint: box.startPoint, endPoint: box.endPoint }, rotatedImage, [this.boxSize, this.boxSize]), 255);
|
||||
} else {
|
||||
rotationMatrix = util.IDENTITY_MATRIX;
|
||||
const clonedImage = input.clone();
|
||||
if (config.face.mesh.enabled) face = bounding.cutBoxFromImageAndResize({ startPoint: box.startPoint, endPoint: box.endPoint }, clonedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
else face = bounding.cutBoxFromImageAndResize({ startPoint: box.startPoint, endPoint: box.endPoint }, clonedImage, [this.boxSize, this.boxSize]).div(255);
|
||||
if (config.face.mesh.enabled) face = tf.div(bounding.cutBoxFromImageAndResize({ startPoint: box.startPoint, endPoint: box.endPoint }, clonedImage, [this.meshSize, this.meshSize]), 255);
|
||||
else face = tf.div(bounding.cutBoxFromImageAndResize({ startPoint: box.startPoint, endPoint: box.endPoint }, clonedImage, [this.boxSize, this.boxSize]), 255);
|
||||
}
|
||||
|
||||
// if we're not going to produce mesh, don't spend time with further processing
|
||||
|
@ -277,9 +277,9 @@ export class Pipeline {
|
|||
angle = util.computeRotation(box.landmarks[indexOfMouth], box.landmarks[indexOfForehead]);
|
||||
const faceCenter = bounding.getBoxCenter({ startPoint: box.startPoint, endPoint: box.endPoint });
|
||||
const faceCenterNormalized = [faceCenter[0] / input.shape[2], faceCenter[1] / input.shape[1]];
|
||||
const rotatedImage = tf.image.rotateWithOffset(input.toFloat(), angle, 0, faceCenterNormalized); // rotateWithOffset is not defined for tfjs-node
|
||||
const rotatedImage = tf.image.rotateWithOffset(tf.cast(input, 'float32'), angle, 0, faceCenterNormalized); // rotateWithOffset is not defined for tfjs-node
|
||||
rotationMatrix = util.buildRotationMatrix(-angle, faceCenter);
|
||||
face = bounding.cutBoxFromImageAndResize({ startPoint: box.startPoint, endPoint: box.endPoint }, rotatedImage, [this.meshSize, this.meshSize]).div(255);
|
||||
face = tf.div(bounding.cutBoxFromImageAndResize({ startPoint: box.startPoint, endPoint: box.endPoint }, rotatedImage, [this.meshSize, this.meshSize]), 255);
|
||||
}
|
||||
|
||||
const prediction = {
|
||||
|
|
|
@ -31,11 +31,11 @@ export async function predict(image: Tensor, config: Config): Promise<Body[]> {
|
|||
const imgSize = { width: (image.shape[2] || 0), height: (image.shape[1] || 0) };
|
||||
const resize = tf.image.resizeBilinear(image, [model['width'], model['height']], false);
|
||||
const normalize = tf.div(resize, [255.0]);
|
||||
resize.dispose();
|
||||
tf.dispose(resize);
|
||||
const resT = await model.predict(normalize) as Array<Tensor>;
|
||||
const points = resT.find((t) => (t.size === 195 || t.size === 155))?.dataSync() || []; // order of output tensors may change between models, full has 195 and upper has 155 items
|
||||
resT.forEach((t) => t.dispose());
|
||||
normalize.dispose();
|
||||
resT.forEach((t) => tf.dispose(t));
|
||||
tf.dispose(normalize);
|
||||
const keypoints: Array<{ id, part, position: [number, number, number], positionRaw: [number, number, number], score, presence }> = [];
|
||||
const labels = points?.length === 195 ? annotations.full : annotations.upper; // full model has 39 keypoints, upper has 31 keypoints
|
||||
const depth = 5; // each points has x,y,z,visibility,presence
|
||||
|
|
|
@ -68,7 +68,7 @@ export async function predict(image: Tensor, config: Config): Promise<Body[]> {
|
|||
|
||||
let resT;
|
||||
if (config.body.enabled) resT = await model.predict(tensor);
|
||||
tensor.dispose();
|
||||
tf.dispose(tensor);
|
||||
|
||||
if (resT) {
|
||||
keypoints.length = 0;
|
||||
|
|
|
@ -80,8 +80,8 @@ export function enhance(input): Tensor {
|
|||
*/
|
||||
|
||||
// normalize brightness from 0..1
|
||||
const darken = merge.sub(merge.min());
|
||||
const lighten = darken.div(darken.max());
|
||||
const darken = tf.sub(merge, merge.min());
|
||||
const lighten = tf.div(darken, darken.max());
|
||||
|
||||
return lighten;
|
||||
});
|
||||
|
|
|
@ -36,20 +36,20 @@ export async function predict(image: Tensor, config: Config, idx, count) {
|
|||
return new Promise(async (resolve) => {
|
||||
const resize = tf.image.resizeBilinear(image, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false);
|
||||
const [red, green, blue] = tf.split(resize, 3, 3);
|
||||
resize.dispose();
|
||||
tf.dispose(resize);
|
||||
// weighted rgb to grayscale: https://www.mathworks.com/help/matlab/ref/rgb2gray.html
|
||||
const redNorm = tf.mul(red, rgb[0]);
|
||||
const greenNorm = tf.mul(green, rgb[1]);
|
||||
const blueNorm = tf.mul(blue, rgb[2]);
|
||||
red.dispose();
|
||||
green.dispose();
|
||||
blue.dispose();
|
||||
tf.dispose(red);
|
||||
tf.dispose(green);
|
||||
tf.dispose(blue);
|
||||
const grayscale = tf.addN([redNorm, greenNorm, blueNorm]);
|
||||
redNorm.dispose();
|
||||
greenNorm.dispose();
|
||||
blueNorm.dispose();
|
||||
const normalize = tf.tidy(() => grayscale.sub(0.5).mul(2));
|
||||
grayscale.dispose();
|
||||
tf.dispose(redNorm);
|
||||
tf.dispose(greenNorm);
|
||||
tf.dispose(blueNorm);
|
||||
const normalize = tf.tidy(() => tf.mul(tf.sub(grayscale, 0.5), 2));
|
||||
tf.dispose(grayscale);
|
||||
const obj: Array<{ score: number, emotion: string }> = [];
|
||||
if (config.face.emotion.enabled) {
|
||||
const emotionT = await model.predict(normalize); // result is already in range 0..1, no need for additional activation
|
||||
|
@ -60,7 +60,7 @@ export async function predict(image: Tensor, config: Config, idx, count) {
|
|||
}
|
||||
obj.sort((a, b) => b.score - a.score);
|
||||
}
|
||||
normalize.dispose();
|
||||
tf.dispose(normalize);
|
||||
last[idx] = obj;
|
||||
lastCount = count;
|
||||
resolve(obj);
|
||||
|
|
|
@ -104,7 +104,7 @@ export function enhance(input): Tensor {
|
|||
const lighten = darken.div(darken.max());
|
||||
*/
|
||||
|
||||
const norm = crop.mul(255);
|
||||
const norm = tf.mul(crop, 255);
|
||||
|
||||
return norm;
|
||||
});
|
||||
|
@ -140,7 +140,7 @@ export async function predict(image: Tensor, config: Config, idx, count) {
|
|||
obj.gender = gender[0] <= 0.5 ? 'female' : 'male';
|
||||
obj.genderScore = Math.min(0.99, confidence);
|
||||
}
|
||||
const age = resT.find((t) => t.shape[1] === 100).argMax(1).dataSync()[0];
|
||||
const age = tf.argMax(resT.find((t) => t.shape[1] === 100), 1).dataSync()[0];
|
||||
const all = resT.find((t) => t.shape[1] === 100).dataSync();
|
||||
obj.age = Math.round(all[age - 1] > all[age + 1] ? 10 * age - 100 * all[age - 1] : 10 * age + 100 * all[age + 1]) / 10;
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ export async function predict(image: Tensor, config: Config | any) {
|
|||
const greenNorm = tf.mul(green, rgb[1]);
|
||||
const blueNorm = tf.mul(blue, rgb[2]);
|
||||
const grayscale = tf.addN([redNorm, greenNorm, blueNorm]);
|
||||
const normalize = grayscale.sub(0.5).mul(2); // range grayscale:-1..1
|
||||
const normalize = tf.mul(tf.sub(grayscale, 0.5), 2); // range grayscale:-1..1
|
||||
return normalize;
|
||||
});
|
||||
} else {
|
||||
|
@ -59,7 +59,7 @@ export async function predict(image: Tensor, config: Config | any) {
|
|||
const obj = { gender: '', confidence: 0 };
|
||||
|
||||
if (config.face.gender.enabled) genderT = await model.predict(enhance);
|
||||
enhance.dispose();
|
||||
tf.dispose(enhance);
|
||||
|
||||
if (genderT) {
|
||||
if (!Array.isArray(genderT)) {
|
||||
|
@ -78,7 +78,7 @@ export async function predict(image: Tensor, config: Config | any) {
|
|||
obj.confidence = Math.min(0.99, confidence);
|
||||
}
|
||||
}
|
||||
genderT.dispose();
|
||||
tf.dispose(genderT);
|
||||
} else {
|
||||
const gender = genderT[0].dataSync();
|
||||
const confidence = Math.trunc(200 * Math.abs((gender[0] - 0.5))) / 100;
|
||||
|
|
|
@ -35,7 +35,7 @@ export class HandDetector {
|
|||
|
||||
normalizeLandmarks(rawPalmLandmarks, index) {
|
||||
return tf.tidy(() => {
|
||||
const landmarks = tf.add(tf.div(rawPalmLandmarks.reshape([-1, 7, 2]), this.inputSizeTensor), this.anchors[index]);
|
||||
const landmarks = tf.add(tf.div(tf.reshape(rawPalmLandmarks, [-1, 7, 2]), this.inputSizeTensor), this.anchors[index]);
|
||||
return tf.mul(landmarks, this.inputSizeTensor);
|
||||
});
|
||||
}
|
||||
|
@ -43,38 +43,38 @@ export class HandDetector {
|
|||
async getBoxes(input, config) {
|
||||
const batched = this.model.predict(input) as Tensor;
|
||||
const predictions = tf.squeeze(batched);
|
||||
batched.dispose();
|
||||
const scoresT = tf.tidy(() => tf.sigmoid(tf.slice(predictions, [0, 0], [-1, 1])).squeeze());
|
||||
tf.dispose(batched);
|
||||
const scoresT = tf.tidy(() => tf.squeeze(tf.sigmoid(tf.slice(predictions, [0, 0], [-1, 1]))));
|
||||
const scores = scoresT.dataSync();
|
||||
const rawBoxes = tf.slice(predictions, [0, 1], [-1, 4]);
|
||||
const boxes = this.normalizeBoxes(rawBoxes);
|
||||
rawBoxes.dispose();
|
||||
tf.dispose(rawBoxes);
|
||||
const filteredT = await tf.image.nonMaxSuppressionAsync(boxes, scores, config.hand.maxDetected, config.hand.iouThreshold, config.hand.minConfidence);
|
||||
const filtered = filteredT.arraySync();
|
||||
|
||||
scoresT.dispose();
|
||||
filteredT.dispose();
|
||||
tf.dispose(scoresT);
|
||||
tf.dispose(filteredT);
|
||||
const hands: Array<{ box: Tensor, palmLandmarks: Tensor, confidence: number }> = [];
|
||||
for (const index of filtered) {
|
||||
if (scores[index] >= config.hand.minConfidence) {
|
||||
const matchingBox = tf.slice(boxes, [index, 0], [1, -1]);
|
||||
const rawPalmLandmarks = tf.slice(predictions, [index, 5], [1, 14]);
|
||||
const palmLandmarks = tf.tidy(() => this.normalizeLandmarks(rawPalmLandmarks, index).reshape([-1, 2]));
|
||||
rawPalmLandmarks.dispose();
|
||||
const palmLandmarks = tf.tidy(() => tf.reshape(this.normalizeLandmarks(rawPalmLandmarks, index), [-1, 2]));
|
||||
tf.dispose(rawPalmLandmarks);
|
||||
hands.push({ box: matchingBox, palmLandmarks, confidence: scores[index] });
|
||||
}
|
||||
}
|
||||
predictions.dispose();
|
||||
boxes.dispose();
|
||||
tf.dispose(predictions);
|
||||
tf.dispose(boxes);
|
||||
return hands;
|
||||
}
|
||||
|
||||
async estimateHandBounds(input, config): Promise<{ startPoint: number[]; endPoint: number[]; palmLandmarks: number[]; confidence: number }[]> {
|
||||
const inputHeight = input.shape[1];
|
||||
const inputWidth = input.shape[2];
|
||||
const image = tf.tidy(() => input.resizeBilinear([this.inputSize, this.inputSize]).div(127.5).sub(1));
|
||||
const image = tf.tidy(() => tf.sub(tf.div(tf.image.resizeBilinear(input, [this.inputSize, this.inputSize]), 127.5), 1));
|
||||
const predictions = await this.getBoxes(image, config);
|
||||
image.dispose();
|
||||
tf.dispose(image);
|
||||
const hands: Array<{ startPoint: number[]; endPoint: number[]; palmLandmarks: number[]; confidence: number }> = [];
|
||||
if (!predictions || predictions.length === 0) return hands;
|
||||
for (const prediction of predictions) {
|
||||
|
@ -82,8 +82,8 @@ export class HandDetector {
|
|||
const startPoint = boxes.slice(0, 2);
|
||||
const endPoint = boxes.slice(2, 4);
|
||||
const palmLandmarks = prediction.palmLandmarks.arraySync();
|
||||
prediction.box.dispose();
|
||||
prediction.palmLandmarks.dispose();
|
||||
tf.dispose(prediction.box);
|
||||
tf.dispose(prediction.palmLandmarks);
|
||||
hands.push(box.scaleBoxCoordinates({ startPoint, endPoint, palmLandmarks, confidence: prediction.confidence }, [inputWidth / this.inputSize, inputHeight / this.inputSize]));
|
||||
}
|
||||
return hands;
|
||||
|
|
|
@ -113,18 +113,18 @@ export class HandPipeline {
|
|||
const rotationMatrix = util.buildRotationMatrix(-angle, palmCenter);
|
||||
const newBox = useFreshBox ? this.getBoxForPalmLandmarks(currentBox.palmLandmarks, rotationMatrix) : currentBox;
|
||||
const croppedInput = box.cutBoxFromImageAndResize(newBox, rotatedImage, [this.inputSize, this.inputSize]);
|
||||
const handImage = croppedInput.div(255);
|
||||
croppedInput.dispose();
|
||||
rotatedImage.dispose();
|
||||
const handImage = tf.div(croppedInput, 255);
|
||||
tf.dispose(croppedInput);
|
||||
tf.dispose(rotatedImage);
|
||||
const [confidenceT, keypoints] = await this.handPoseModel.predict(handImage) as Array<Tensor>;
|
||||
handImage.dispose();
|
||||
tf.dispose(handImage);
|
||||
const confidence = confidenceT.dataSync()[0];
|
||||
confidenceT.dispose();
|
||||
tf.dispose(confidenceT);
|
||||
if (confidence >= config.hand.minConfidence) {
|
||||
const keypointsReshaped = tf.reshape(keypoints, [-1, 3]);
|
||||
const rawCoords = keypointsReshaped.arraySync();
|
||||
keypoints.dispose();
|
||||
keypointsReshaped.dispose();
|
||||
tf.dispose(keypoints);
|
||||
tf.dispose(keypointsReshaped);
|
||||
const coords = this.transformRawCoords(rawCoords, newBox, angle, rotationMatrix);
|
||||
const nextBoundingBox = this.getBoxForHandLandmarks(coords);
|
||||
this.storedBoxes[i] = { ...nextBoundingBox, confidence };
|
||||
|
@ -137,7 +137,7 @@ export class HandPipeline {
|
|||
} else {
|
||||
this.storedBoxes[i] = null;
|
||||
}
|
||||
keypoints.dispose();
|
||||
tf.dispose(keypoints);
|
||||
} else {
|
||||
// const enlarged = box.enlargeBox(box.squarifyBox(box.shiftBox(currentBox, HAND_BOX_SHIFT_VECTOR)), handBoxEnlargeFactor);
|
||||
const enlarged = box.enlargeBox(box.squarifyBox(currentBox), handBoxEnlargeFactor);
|
||||
|
|
|
@ -357,7 +357,7 @@ export class Human {
|
|||
#skipFrame = async (input) => {
|
||||
if (this.config.cacheSensitivity === 0) return false;
|
||||
const resizeFact = 32;
|
||||
const reduced: Tensor = input.resizeBilinear([Math.trunc(input.shape[1] / resizeFact), Math.trunc(input.shape[2] / resizeFact)]);
|
||||
const reduced: Tensor = tf.image.resizeBilinear(input, [Math.trunc(input.shape[1] / resizeFact), Math.trunc(input.shape[2] / resizeFact)]);
|
||||
// use tensor sum
|
||||
/*
|
||||
const sumT = this.tf.sum(reduced);
|
||||
|
@ -448,7 +448,7 @@ export class Human {
|
|||
if (elapsedTime > 0) this.performance.segmentation = elapsedTime;
|
||||
if (process.canvas) {
|
||||
// replace input
|
||||
process.tensor.dispose();
|
||||
tf.dispose(process.tensor);
|
||||
process = image.process(process.canvas, this.config);
|
||||
}
|
||||
this.analyze('End Segmentation:');
|
||||
|
|
|
@ -161,10 +161,10 @@ export function process(input: Input, config: Config): { tensor: Tensor | null,
|
|||
pixels = tf.browser ? tf.browser.fromPixels(data) : null;
|
||||
}
|
||||
if (pixels) {
|
||||
const casted = pixels.toFloat();
|
||||
tensor = casted.expandDims(0);
|
||||
pixels.dispose();
|
||||
casted.dispose();
|
||||
const casted = tf.cast(pixels, 'float32');
|
||||
tensor = tf.expandDims(casted, 0);
|
||||
tf.dispose(pixels);
|
||||
tf.dispose(casted);
|
||||
}
|
||||
}
|
||||
const canvas = config.filter.return ? outCanvas : null;
|
||||
|
|
|
@ -46,7 +46,7 @@ export async function predict(image: Tensor, config: Config): Promise<Body[]> {
|
|||
|
||||
let resT;
|
||||
if (config.body.enabled) resT = await model.predict(tensor);
|
||||
tensor.dispose();
|
||||
tf.dispose(tensor);
|
||||
|
||||
if (resT) {
|
||||
keypoints.length = 0;
|
||||
|
|
|
@ -30,20 +30,20 @@ async function process(res: Tensor, inputSize, outputShape, config: Config) {
|
|||
const results: Array<Item> = [];
|
||||
const detections = res.arraySync();
|
||||
const squeezeT = tf.squeeze(res);
|
||||
res.dispose();
|
||||
tf.dispose(res);
|
||||
const arr = tf.split(squeezeT, 6, 1); // x1, y1, x2, y2, score, class
|
||||
squeezeT.dispose();
|
||||
tf.dispose(squeezeT);
|
||||
const stackT = tf.stack([arr[1], arr[0], arr[3], arr[2]], 1); // reorder dims as tf.nms expects y, x
|
||||
const boxesT = stackT.squeeze();
|
||||
const scoresT = arr[4].squeeze();
|
||||
const classesT = arr[5].squeeze();
|
||||
arr.forEach((t) => t.dispose());
|
||||
arr.forEach((t) => tf.dispose(t));
|
||||
const nmsT = await tf.image.nonMaxSuppressionAsync(boxesT, scoresT, config.object.maxDetected, config.object.iouThreshold, config.object.minConfidence);
|
||||
boxesT.dispose();
|
||||
scoresT.dispose();
|
||||
classesT.dispose();
|
||||
tf.dispose(boxesT);
|
||||
tf.dispose(scoresT);
|
||||
tf.dispose(classesT);
|
||||
const nms = nmsT.dataSync();
|
||||
nmsT.dispose();
|
||||
tf.dispose(nmsT);
|
||||
let i = 0;
|
||||
for (const id of nms) {
|
||||
const score = Math.trunc(100 * detections[0][id][4]) / 100;
|
||||
|
@ -80,7 +80,7 @@ export async function predict(input: Tensor, config: Config): Promise<Item[]> {
|
|||
const outputSize = [input.shape[2], input.shape[1]];
|
||||
const resize = tf.image.resizeBilinear(input, [model.inputSize, model.inputSize]);
|
||||
const objectT = config.object.enabled ? model.execute(resize, ['tower_0/detections']) : null;
|
||||
resize.dispose();
|
||||
tf.dispose(resize);
|
||||
|
||||
const obj = await process(objectT, model.inputSize, outputSize, config);
|
||||
last = obj;
|
||||
|
|
|
@ -111,14 +111,14 @@ export async function predict(image: Tensor, config: Config): Promise<Item[]> {
|
|||
return new Promise(async (resolve) => {
|
||||
const outputSize = [image.shape[2], image.shape[1]];
|
||||
const resize = tf.image.resizeBilinear(image, [model.inputSize, model.inputSize], false);
|
||||
const norm = resize.div(255);
|
||||
const norm = tf.div(resize, 255);
|
||||
const transpose = norm.transpose([0, 3, 1, 2]);
|
||||
norm.dispose();
|
||||
resize.dispose();
|
||||
tf.dispose(norm);
|
||||
tf.dispose(resize);
|
||||
|
||||
let objectT;
|
||||
if (config.object.enabled) objectT = await model.predict(transpose);
|
||||
transpose.dispose();
|
||||
tf.dispose(transpose);
|
||||
|
||||
const obj = await process(objectT, model.inputSize, outputSize, config);
|
||||
last = obj;
|
||||
|
|
|
@ -17,7 +17,7 @@ export async function predict(input: Tensor, config: Config): Promise<Body[]> {
|
|||
const res = tf.tidy(() => {
|
||||
if (!model.inputs[0].shape) return [];
|
||||
const resized = tf.image.resizeBilinear(input, [model.inputs[0].shape[2], model.inputs[0].shape[1]]);
|
||||
const normalized = resized.toFloat().div(127.5).sub(1.0);
|
||||
const normalized = tf.sub(tf.div(tf.cast(resized, 'float32'), 127.5), 1.0);
|
||||
const results: Array<Tensor> = model.execute(normalized, poseNetOutputs) as Array<Tensor>;
|
||||
const results3d = results.map((y) => tf.squeeze(y, [0]));
|
||||
results3d[1] = results3d[1].sigmoid(); // apply sigmoid on scores
|
||||
|
@ -25,7 +25,7 @@ export async function predict(input: Tensor, config: Config): Promise<Body[]> {
|
|||
});
|
||||
|
||||
const buffers = await Promise.all(res.map((tensor) => tensor.buffer()));
|
||||
for (const t of res) t.dispose();
|
||||
for (const t of res) tf.dispose(t);
|
||||
|
||||
const decoded = await poses.decode(buffers[0], buffers[1], buffers[2], buffers[3], config.body.maxDetected, config.body.minConfidence);
|
||||
if (!model.inputs[0].shape) return [];
|
||||
|
|
|
@ -29,7 +29,7 @@ export async function predict(input: { tensor: Tensor | null, canvas: OffscreenC
|
|||
if (!input.tensor) return null;
|
||||
if (!model || !model.inputs[0].shape) return null;
|
||||
const resizeInput = tf.image.resizeBilinear(input.tensor, [model.inputs[0].shape[1], model.inputs[0].shape[2]], false);
|
||||
const norm = resizeInput.div(255);
|
||||
const norm = tf.div(resizeInput, 255);
|
||||
const res = model.predict(norm) as Tensor;
|
||||
// meet output: 1,256,256,1
|
||||
// selfie output: 1,144,256,2
|
||||
|
@ -42,8 +42,8 @@ export async function predict(input: { tensor: Tensor | null, canvas: OffscreenC
|
|||
// model meet has two channels for fg and bg
|
||||
const softmax = squeeze.softmax();
|
||||
const [bg, fg] = tf.unstack(softmax, 2);
|
||||
const expand = fg.expandDims(2);
|
||||
const pad = expand.expandDims(0);
|
||||
const expand = tf.expandDims(fg, 2);
|
||||
const pad = tf.expandDims(expand, 0);
|
||||
tf.dispose(softmax);
|
||||
tf.dispose(bg);
|
||||
tf.dispose(fg);
|
||||
|
@ -51,7 +51,7 @@ export async function predict(input: { tensor: Tensor | null, canvas: OffscreenC
|
|||
const crop = tf.image.cropAndResize(pad, [[0, 0, 0.5, 0.5]], [0], [width, height]);
|
||||
// otherwise run softmax after unstack and use standard resize
|
||||
// resizeOutput = tf.image.resizeBilinear(expand, [input.tensor?.shape[1], input.tensor?.shape[2]]);
|
||||
resizeOutput = crop.squeeze(0);
|
||||
resizeOutput = tf.squeeze(crop, 0);
|
||||
tf.dispose(crop);
|
||||
tf.dispose(expand);
|
||||
tf.dispose(pad);
|
||||
|
|
Loading…
Reference in New Issue