mirror of https://github.com/vladmandic/human
fix unregistered ops in tfjs
parent
92e9b87829
commit
d08b657ecd
|
@ -14,7 +14,7 @@ const userConfig = {
|
||||||
warmup: 'none',
|
warmup: 'none',
|
||||||
debug: true,
|
debug: true,
|
||||||
modelBasePath: '../../models/',
|
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: {
|
face: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
detector: { rotation: true, return: true },
|
detector: { rotation: true, return: true },
|
||||||
|
@ -75,10 +75,10 @@ async function analyze(face) {
|
||||||
navigator.clipboard.writeText(`{"name":"unknown", "source":"${face.fileName}", "embedding":[${embedding}]},`);
|
navigator.clipboard.writeText(`{"name":"unknown", "source":"${face.fileName}", "embedding":[${embedding}]},`);
|
||||||
if (enhanced) {
|
if (enhanced) {
|
||||||
const c = document.getElementById('orig');
|
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);
|
await human.tf.browser.toPixels(squeeze, c);
|
||||||
enhanced.dispose();
|
human.tf.dispose(enhanced);
|
||||||
squeeze.dispose();
|
human.tf.dispose(squeeze);
|
||||||
const ctx = c.getContext('2d');
|
const ctx = c.getContext('2d');
|
||||||
ctx.font = 'small-caps 0.4rem "Lato"';
|
ctx.font = 'small-caps 0.4rem "Lato"';
|
||||||
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
|
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
|
||||||
|
|
|
@ -30,7 +30,7 @@ let human;
|
||||||
let userConfig = {
|
let userConfig = {
|
||||||
warmup: 'none',
|
warmup: 'none',
|
||||||
backend: 'humangl',
|
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,
|
async: false,
|
||||||
cacheSensitivity: 0,
|
cacheSensitivity: 0,
|
||||||
|
@ -169,10 +169,10 @@ function status(msg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const compare = { enabled: false, original: null };
|
const compare = { enabled: false, original: null };
|
||||||
async function calcSimmilariry(result) {
|
async function calcSimmilarity(result) {
|
||||||
document.getElementById('compare-container').style.display = compare.enabled ? 'block' : 'none';
|
document.getElementById('compare-container').style.display = compare.enabled ? 'block' : 'none';
|
||||||
if (!compare.enabled) return;
|
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 (!(result.face.length > 0) || (result.face[0].embedding.length <= 64)) return;
|
||||||
if (!compare.original) {
|
if (!compare.original) {
|
||||||
compare.original = result;
|
compare.original = result;
|
||||||
|
@ -181,12 +181,12 @@ async function calcSimmilariry(result) {
|
||||||
const enhanced = human.enhance(result.face[0]);
|
const enhanced = human.enhance(result.face[0]);
|
||||||
if (enhanced) {
|
if (enhanced) {
|
||||||
const c = document.getElementById('orig');
|
const c = document.getElementById('orig');
|
||||||
const squeeze = enhanced.squeeze();
|
const squeeze = human.tf.squeeze(enhanced);
|
||||||
const norm = squeeze.div(255);
|
const norm = human.tf.div(squeeze, 255);
|
||||||
human.tf.browser.toPixels(norm, c);
|
human.tf.browser.toPixels(norm, c);
|
||||||
enhanced.dispose();
|
human.tf.dispose(enhanced);
|
||||||
squeeze.dispose();
|
human.tf.dispose(squeeze);
|
||||||
norm.dispose();
|
human.tf.dispose(norm);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('compare-canvas').getContext('2d').drawImage(compare.original.canvas, 0, 0, 200, 200);
|
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
|
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
||||||
const person = result.persons; // explicitly invoke person getter
|
const person = result.persons; // explicitly invoke person getter
|
||||||
await calcSimmilariry(result);
|
await calcSimmilarity(result);
|
||||||
|
|
||||||
// update log
|
// update log
|
||||||
const engine = human.tf.engine();
|
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({ 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
|
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() {
|
async function main() {
|
||||||
|
|
|
@ -66,13 +66,13 @@ async function process(jpegBuffer) {
|
||||||
busy = true;
|
busy = true;
|
||||||
const decoded = tf.node.decodeJpeg(jpegBuffer, 3); // decode jpeg buffer to raw tensor
|
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
|
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);
|
log.state('input frame:', ++count, 'size:', jpegBuffer.length, 'decoded shape:', tensor.shape);
|
||||||
const res = await human.detect(tensor);
|
const res = await human.detect(tensor);
|
||||||
log.data('gesture', JSON.stringify(res.gesture));
|
log.data('gesture', JSON.stringify(res.gesture));
|
||||||
// do processing here
|
// do processing here
|
||||||
tensor.dispose(); // must dispose tensor
|
tf.dispose(tensor); // must dispose tensor
|
||||||
busy = false;
|
busy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,13 +42,13 @@ export async function predict(image: Tensor, config: Config | any) {
|
||||||
const obj = { age: 0 };
|
const obj = { age: 0 };
|
||||||
|
|
||||||
if (config.face.age.enabled) ageT = await model.predict(enhance);
|
if (config.face.age.enabled) ageT = await model.predict(enhance);
|
||||||
enhance.dispose();
|
tf.dispose(enhance);
|
||||||
|
|
||||||
if (ageT) {
|
if (ageT) {
|
||||||
const data = ageT.dataSync();
|
const data = ageT.dataSync();
|
||||||
obj.age = Math.trunc(10 * data[0]) / 10;
|
obj.age = Math.trunc(10 * data[0]) / 10;
|
||||||
}
|
}
|
||||||
ageT.dispose();
|
tf.dispose(ageT);
|
||||||
|
|
||||||
last = obj;
|
last = obj;
|
||||||
resolve(obj);
|
resolve(obj);
|
||||||
|
|
|
@ -68,7 +68,7 @@ export async function predict(image: Tensor, config: Config): Promise<Body[]> {
|
||||||
|
|
||||||
let resT;
|
let resT;
|
||||||
if (config.body.enabled) resT = await model.predict(tensor);
|
if (config.body.enabled) resT = await model.predict(tensor);
|
||||||
tensor.dispose();
|
tf.dispose(tensor);
|
||||||
|
|
||||||
if (resT) {
|
if (resT) {
|
||||||
keypoints.length = 0;
|
keypoints.length = 0;
|
||||||
|
|
|
@ -36,20 +36,20 @@ export async function predict(image: Tensor, config: Config, idx, count) {
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
const resize = tf.image.resizeBilinear(image, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false);
|
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);
|
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
|
// weighted rgb to grayscale: https://www.mathworks.com/help/matlab/ref/rgb2gray.html
|
||||||
const redNorm = tf.mul(red, rgb[0]);
|
const redNorm = tf.mul(red, rgb[0]);
|
||||||
const greenNorm = tf.mul(green, rgb[1]);
|
const greenNorm = tf.mul(green, rgb[1]);
|
||||||
const blueNorm = tf.mul(blue, rgb[2]);
|
const blueNorm = tf.mul(blue, rgb[2]);
|
||||||
red.dispose();
|
tf.dispose(red);
|
||||||
green.dispose();
|
tf.dispose(green);
|
||||||
blue.dispose();
|
tf.dispose(blue);
|
||||||
const grayscale = tf.addN([redNorm, greenNorm, blueNorm]);
|
const grayscale = tf.addN([redNorm, greenNorm, blueNorm]);
|
||||||
redNorm.dispose();
|
tf.dispose(redNorm);
|
||||||
greenNorm.dispose();
|
tf.dispose(greenNorm);
|
||||||
blueNorm.dispose();
|
tf.dispose(blueNorm);
|
||||||
const normalize = tf.tidy(() => grayscale.sub(0.5).mul(2));
|
const normalize = tf.tidy(() => tf.mul(tf.sub(grayscale, 0.5), 2));
|
||||||
grayscale.dispose();
|
tf.dispose(grayscale);
|
||||||
const obj: Array<{ score: number, emotion: string }> = [];
|
const obj: Array<{ score: number, emotion: string }> = [];
|
||||||
if (config.face.emotion.enabled) {
|
if (config.face.emotion.enabled) {
|
||||||
const emotionT = await model.predict(normalize); // result is already in range 0..1, no need for additional activation
|
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);
|
obj.sort((a, b) => b.score - a.score);
|
||||||
}
|
}
|
||||||
normalize.dispose();
|
tf.dispose(normalize);
|
||||||
last[idx] = obj;
|
last[idx] = obj;
|
||||||
lastCount = count;
|
lastCount = count;
|
||||||
resolve(obj);
|
resolve(obj);
|
||||||
|
|
|
@ -104,7 +104,7 @@ export function enhance(input): Tensor {
|
||||||
const lighten = darken.div(darken.max());
|
const lighten = darken.div(darken.max());
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const norm = crop.mul(255);
|
const norm = tf.mul(crop, 255);
|
||||||
|
|
||||||
return norm;
|
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.gender = gender[0] <= 0.5 ? 'female' : 'male';
|
||||||
obj.genderScore = Math.min(0.99, confidence);
|
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();
|
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;
|
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 greenNorm = tf.mul(green, rgb[1]);
|
||||||
const blueNorm = tf.mul(blue, rgb[2]);
|
const blueNorm = tf.mul(blue, rgb[2]);
|
||||||
const grayscale = tf.addN([redNorm, greenNorm, blueNorm]);
|
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;
|
return normalize;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -59,7 +59,7 @@ export async function predict(image: Tensor, config: Config | any) {
|
||||||
const obj = { gender: '', confidence: 0 };
|
const obj = { gender: '', confidence: 0 };
|
||||||
|
|
||||||
if (config.face.gender.enabled) genderT = await model.predict(enhance);
|
if (config.face.gender.enabled) genderT = await model.predict(enhance);
|
||||||
enhance.dispose();
|
tf.dispose(enhance);
|
||||||
|
|
||||||
if (genderT) {
|
if (genderT) {
|
||||||
if (!Array.isArray(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);
|
obj.confidence = Math.min(0.99, confidence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
genderT.dispose();
|
tf.dispose(genderT);
|
||||||
} else {
|
} else {
|
||||||
const gender = genderT[0].dataSync();
|
const gender = genderT[0].dataSync();
|
||||||
const confidence = Math.trunc(200 * Math.abs((gender[0] - 0.5))) / 100;
|
const confidence = Math.trunc(200 * Math.abs((gender[0] - 0.5))) / 100;
|
||||||
|
|
|
@ -35,7 +35,7 @@ export class HandDetector {
|
||||||
|
|
||||||
normalizeLandmarks(rawPalmLandmarks, index) {
|
normalizeLandmarks(rawPalmLandmarks, index) {
|
||||||
return tf.tidy(() => {
|
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);
|
return tf.mul(landmarks, this.inputSizeTensor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -43,38 +43,38 @@ export class HandDetector {
|
||||||
async getBoxes(input, config) {
|
async getBoxes(input, config) {
|
||||||
const batched = this.model.predict(input) as Tensor;
|
const batched = this.model.predict(input) as Tensor;
|
||||||
const predictions = tf.squeeze(batched);
|
const predictions = tf.squeeze(batched);
|
||||||
batched.dispose();
|
tf.dispose(batched);
|
||||||
const scoresT = tf.tidy(() => tf.sigmoid(tf.slice(predictions, [0, 0], [-1, 1])).squeeze());
|
const scoresT = tf.tidy(() => tf.squeeze(tf.sigmoid(tf.slice(predictions, [0, 0], [-1, 1]))));
|
||||||
const scores = scoresT.dataSync();
|
const scores = scoresT.dataSync();
|
||||||
const rawBoxes = tf.slice(predictions, [0, 1], [-1, 4]);
|
const rawBoxes = tf.slice(predictions, [0, 1], [-1, 4]);
|
||||||
const boxes = this.normalizeBoxes(rawBoxes);
|
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 filteredT = await tf.image.nonMaxSuppressionAsync(boxes, scores, config.hand.maxDetected, config.hand.iouThreshold, config.hand.minConfidence);
|
||||||
const filtered = filteredT.arraySync();
|
const filtered = filteredT.arraySync();
|
||||||
|
|
||||||
scoresT.dispose();
|
tf.dispose(scoresT);
|
||||||
filteredT.dispose();
|
tf.dispose(filteredT);
|
||||||
const hands: Array<{ box: Tensor, palmLandmarks: Tensor, confidence: number }> = [];
|
const hands: Array<{ box: Tensor, palmLandmarks: Tensor, confidence: number }> = [];
|
||||||
for (const index of filtered) {
|
for (const index of filtered) {
|
||||||
if (scores[index] >= config.hand.minConfidence) {
|
if (scores[index] >= config.hand.minConfidence) {
|
||||||
const matchingBox = tf.slice(boxes, [index, 0], [1, -1]);
|
const matchingBox = tf.slice(boxes, [index, 0], [1, -1]);
|
||||||
const rawPalmLandmarks = tf.slice(predictions, [index, 5], [1, 14]);
|
const rawPalmLandmarks = tf.slice(predictions, [index, 5], [1, 14]);
|
||||||
const palmLandmarks = tf.tidy(() => this.normalizeLandmarks(rawPalmLandmarks, index).reshape([-1, 2]));
|
const palmLandmarks = tf.tidy(() => tf.reshape(this.normalizeLandmarks(rawPalmLandmarks, index), [-1, 2]));
|
||||||
rawPalmLandmarks.dispose();
|
tf.dispose(rawPalmLandmarks);
|
||||||
hands.push({ box: matchingBox, palmLandmarks, confidence: scores[index] });
|
hands.push({ box: matchingBox, palmLandmarks, confidence: scores[index] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
predictions.dispose();
|
tf.dispose(predictions);
|
||||||
boxes.dispose();
|
tf.dispose(boxes);
|
||||||
return hands;
|
return hands;
|
||||||
}
|
}
|
||||||
|
|
||||||
async estimateHandBounds(input, config): Promise<{ startPoint: number[]; endPoint: number[]; palmLandmarks: number[]; confidence: number }[]> {
|
async estimateHandBounds(input, config): Promise<{ startPoint: number[]; endPoint: number[]; palmLandmarks: number[]; confidence: number }[]> {
|
||||||
const inputHeight = input.shape[1];
|
const inputHeight = input.shape[1];
|
||||||
const inputWidth = input.shape[2];
|
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);
|
const predictions = await this.getBoxes(image, config);
|
||||||
image.dispose();
|
tf.dispose(image);
|
||||||
const hands: Array<{ startPoint: number[]; endPoint: number[]; palmLandmarks: number[]; confidence: number }> = [];
|
const hands: Array<{ startPoint: number[]; endPoint: number[]; palmLandmarks: number[]; confidence: number }> = [];
|
||||||
if (!predictions || predictions.length === 0) return hands;
|
if (!predictions || predictions.length === 0) return hands;
|
||||||
for (const prediction of predictions) {
|
for (const prediction of predictions) {
|
||||||
|
@ -82,8 +82,8 @@ export class HandDetector {
|
||||||
const startPoint = boxes.slice(0, 2);
|
const startPoint = boxes.slice(0, 2);
|
||||||
const endPoint = boxes.slice(2, 4);
|
const endPoint = boxes.slice(2, 4);
|
||||||
const palmLandmarks = prediction.palmLandmarks.arraySync();
|
const palmLandmarks = prediction.palmLandmarks.arraySync();
|
||||||
prediction.box.dispose();
|
tf.dispose(prediction.box);
|
||||||
prediction.palmLandmarks.dispose();
|
tf.dispose(prediction.palmLandmarks);
|
||||||
hands.push(box.scaleBoxCoordinates({ startPoint, endPoint, palmLandmarks, confidence: prediction.confidence }, [inputWidth / this.inputSize, inputHeight / this.inputSize]));
|
hands.push(box.scaleBoxCoordinates({ startPoint, endPoint, palmLandmarks, confidence: prediction.confidence }, [inputWidth / this.inputSize, inputHeight / this.inputSize]));
|
||||||
}
|
}
|
||||||
return hands;
|
return hands;
|
||||||
|
|
|
@ -113,18 +113,18 @@ export class HandPipeline {
|
||||||
const rotationMatrix = util.buildRotationMatrix(-angle, palmCenter);
|
const rotationMatrix = util.buildRotationMatrix(-angle, palmCenter);
|
||||||
const newBox = useFreshBox ? this.getBoxForPalmLandmarks(currentBox.palmLandmarks, rotationMatrix) : currentBox;
|
const newBox = useFreshBox ? this.getBoxForPalmLandmarks(currentBox.palmLandmarks, rotationMatrix) : currentBox;
|
||||||
const croppedInput = box.cutBoxFromImageAndResize(newBox, rotatedImage, [this.inputSize, this.inputSize]);
|
const croppedInput = box.cutBoxFromImageAndResize(newBox, rotatedImage, [this.inputSize, this.inputSize]);
|
||||||
const handImage = croppedInput.div(255);
|
const handImage = tf.div(croppedInput, 255);
|
||||||
croppedInput.dispose();
|
tf.dispose(croppedInput);
|
||||||
rotatedImage.dispose();
|
tf.dispose(rotatedImage);
|
||||||
const [confidenceT, keypoints] = await this.handPoseModel.predict(handImage) as Array<Tensor>;
|
const [confidenceT, keypoints] = await this.handPoseModel.predict(handImage) as Array<Tensor>;
|
||||||
handImage.dispose();
|
tf.dispose(handImage);
|
||||||
const confidence = confidenceT.dataSync()[0];
|
const confidence = confidenceT.dataSync()[0];
|
||||||
confidenceT.dispose();
|
tf.dispose(confidenceT);
|
||||||
if (confidence >= config.hand.minConfidence) {
|
if (confidence >= config.hand.minConfidence) {
|
||||||
const keypointsReshaped = tf.reshape(keypoints, [-1, 3]);
|
const keypointsReshaped = tf.reshape(keypoints, [-1, 3]);
|
||||||
const rawCoords = keypointsReshaped.arraySync();
|
const rawCoords = keypointsReshaped.arraySync();
|
||||||
keypoints.dispose();
|
tf.dispose(keypoints);
|
||||||
keypointsReshaped.dispose();
|
tf.dispose(keypointsReshaped);
|
||||||
const coords = this.transformRawCoords(rawCoords, newBox, angle, rotationMatrix);
|
const coords = this.transformRawCoords(rawCoords, newBox, angle, rotationMatrix);
|
||||||
const nextBoundingBox = this.getBoxForHandLandmarks(coords);
|
const nextBoundingBox = this.getBoxForHandLandmarks(coords);
|
||||||
this.storedBoxes[i] = { ...nextBoundingBox, confidence };
|
this.storedBoxes[i] = { ...nextBoundingBox, confidence };
|
||||||
|
@ -137,7 +137,7 @@ export class HandPipeline {
|
||||||
} else {
|
} else {
|
||||||
this.storedBoxes[i] = null;
|
this.storedBoxes[i] = null;
|
||||||
}
|
}
|
||||||
keypoints.dispose();
|
tf.dispose(keypoints);
|
||||||
} else {
|
} else {
|
||||||
// const enlarged = box.enlargeBox(box.squarifyBox(box.shiftBox(currentBox, HAND_BOX_SHIFT_VECTOR)), handBoxEnlargeFactor);
|
// const enlarged = box.enlargeBox(box.squarifyBox(box.shiftBox(currentBox, HAND_BOX_SHIFT_VECTOR)), handBoxEnlargeFactor);
|
||||||
const enlarged = box.enlargeBox(box.squarifyBox(currentBox), handBoxEnlargeFactor);
|
const enlarged = box.enlargeBox(box.squarifyBox(currentBox), handBoxEnlargeFactor);
|
||||||
|
|
|
@ -357,7 +357,7 @@ export class Human {
|
||||||
#skipFrame = async (input) => {
|
#skipFrame = async (input) => {
|
||||||
if (this.config.cacheSensitivity === 0) return false;
|
if (this.config.cacheSensitivity === 0) return false;
|
||||||
const resizeFact = 32;
|
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
|
// use tensor sum
|
||||||
/*
|
/*
|
||||||
const sumT = this.tf.sum(reduced);
|
const sumT = this.tf.sum(reduced);
|
||||||
|
@ -448,7 +448,7 @@ export class Human {
|
||||||
if (elapsedTime > 0) this.performance.segmentation = elapsedTime;
|
if (elapsedTime > 0) this.performance.segmentation = elapsedTime;
|
||||||
if (process.canvas) {
|
if (process.canvas) {
|
||||||
// replace input
|
// replace input
|
||||||
process.tensor.dispose();
|
tf.dispose(process.tensor);
|
||||||
process = image.process(process.canvas, this.config);
|
process = image.process(process.canvas, this.config);
|
||||||
}
|
}
|
||||||
this.analyze('End Segmentation:');
|
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;
|
pixels = tf.browser ? tf.browser.fromPixels(data) : null;
|
||||||
}
|
}
|
||||||
if (pixels) {
|
if (pixels) {
|
||||||
const casted = pixels.toFloat();
|
const casted = tf.cast(pixels, 'float32');
|
||||||
tensor = casted.expandDims(0);
|
tensor = tf.expandDims(casted, 0);
|
||||||
pixels.dispose();
|
tf.dispose(pixels);
|
||||||
casted.dispose();
|
tf.dispose(casted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const canvas = config.filter.return ? outCanvas : null;
|
const canvas = config.filter.return ? outCanvas : null;
|
||||||
|
|
|
@ -46,7 +46,7 @@ export async function predict(image: Tensor, config: Config): Promise<Body[]> {
|
||||||
|
|
||||||
let resT;
|
let resT;
|
||||||
if (config.body.enabled) resT = await model.predict(tensor);
|
if (config.body.enabled) resT = await model.predict(tensor);
|
||||||
tensor.dispose();
|
tf.dispose(tensor);
|
||||||
|
|
||||||
if (resT) {
|
if (resT) {
|
||||||
keypoints.length = 0;
|
keypoints.length = 0;
|
||||||
|
|
|
@ -30,20 +30,20 @@ async function process(res: Tensor, inputSize, outputShape, config: Config) {
|
||||||
const results: Array<Item> = [];
|
const results: Array<Item> = [];
|
||||||
const detections = res.arraySync();
|
const detections = res.arraySync();
|
||||||
const squeezeT = tf.squeeze(res);
|
const squeezeT = tf.squeeze(res);
|
||||||
res.dispose();
|
tf.dispose(res);
|
||||||
const arr = tf.split(squeezeT, 6, 1); // x1, y1, x2, y2, score, class
|
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 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 boxesT = stackT.squeeze();
|
||||||
const scoresT = arr[4].squeeze();
|
const scoresT = arr[4].squeeze();
|
||||||
const classesT = arr[5].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);
|
const nmsT = await tf.image.nonMaxSuppressionAsync(boxesT, scoresT, config.object.maxDetected, config.object.iouThreshold, config.object.minConfidence);
|
||||||
boxesT.dispose();
|
tf.dispose(boxesT);
|
||||||
scoresT.dispose();
|
tf.dispose(scoresT);
|
||||||
classesT.dispose();
|
tf.dispose(classesT);
|
||||||
const nms = nmsT.dataSync();
|
const nms = nmsT.dataSync();
|
||||||
nmsT.dispose();
|
tf.dispose(nmsT);
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (const id of nms) {
|
for (const id of nms) {
|
||||||
const score = Math.trunc(100 * detections[0][id][4]) / 100;
|
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 outputSize = [input.shape[2], input.shape[1]];
|
||||||
const resize = tf.image.resizeBilinear(input, [model.inputSize, model.inputSize]);
|
const resize = tf.image.resizeBilinear(input, [model.inputSize, model.inputSize]);
|
||||||
const objectT = config.object.enabled ? model.execute(resize, ['tower_0/detections']) : null;
|
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);
|
const obj = await process(objectT, model.inputSize, outputSize, config);
|
||||||
last = obj;
|
last = obj;
|
||||||
|
|
|
@ -111,14 +111,14 @@ export async function predict(image: Tensor, config: Config): Promise<Item[]> {
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
const outputSize = [image.shape[2], image.shape[1]];
|
const outputSize = [image.shape[2], image.shape[1]];
|
||||||
const resize = tf.image.resizeBilinear(image, [model.inputSize, model.inputSize], false);
|
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]);
|
const transpose = norm.transpose([0, 3, 1, 2]);
|
||||||
norm.dispose();
|
tf.dispose(norm);
|
||||||
resize.dispose();
|
tf.dispose(resize);
|
||||||
|
|
||||||
let objectT;
|
let objectT;
|
||||||
if (config.object.enabled) objectT = await model.predict(transpose);
|
if (config.object.enabled) objectT = await model.predict(transpose);
|
||||||
transpose.dispose();
|
tf.dispose(transpose);
|
||||||
|
|
||||||
const obj = await process(objectT, model.inputSize, outputSize, config);
|
const obj = await process(objectT, model.inputSize, outputSize, config);
|
||||||
last = obj;
|
last = obj;
|
||||||
|
|
|
@ -29,7 +29,7 @@ export async function predict(input: { tensor: Tensor | null, canvas: OffscreenC
|
||||||
if (!input.tensor) return null;
|
if (!input.tensor) return null;
|
||||||
if (!model || !model.inputs[0].shape) 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 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;
|
const res = model.predict(norm) as Tensor;
|
||||||
// meet output: 1,256,256,1
|
// meet output: 1,256,256,1
|
||||||
// selfie output: 1,144,256,2
|
// 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
|
// model meet has two channels for fg and bg
|
||||||
const softmax = squeeze.softmax();
|
const softmax = squeeze.softmax();
|
||||||
const [bg, fg] = tf.unstack(softmax, 2);
|
const [bg, fg] = tf.unstack(softmax, 2);
|
||||||
const expand = fg.expandDims(2);
|
const expand = tf.expandDims(fg, 2);
|
||||||
const pad = expand.expandDims(0);
|
const pad = tf.expandDims(expand, 0);
|
||||||
tf.dispose(softmax);
|
tf.dispose(softmax);
|
||||||
tf.dispose(bg);
|
tf.dispose(bg);
|
||||||
tf.dispose(fg);
|
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]);
|
const crop = tf.image.cropAndResize(pad, [[0, 0, 0.5, 0.5]], [0], [width, height]);
|
||||||
// otherwise run softmax after unstack and use standard resize
|
// otherwise run softmax after unstack and use standard resize
|
||||||
// resizeOutput = tf.image.resizeBilinear(expand, [input.tensor?.shape[1], input.tensor?.shape[2]]);
|
// 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(crop);
|
||||||
tf.dispose(expand);
|
tf.dispose(expand);
|
||||||
tf.dispose(pad);
|
tf.dispose(pad);
|
||||||
|
|
Loading…
Reference in New Issue