Socket
Socket
Sign inDemoInstall

@tensorflow-models/blazeface

Package Overview
Dependencies
Maintainers
6
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tensorflow-models/blazeface - npm Package Compare versions

Comparing version 0.0.3 to 0.0.4

2

dist/blazeface.esm.js

@@ -17,2 +17,2 @@ /**

*/
import{slice,add,div,sub,mul,concat2d,Tensor,concat,tensor1d,tensor2d,tidy,sigmoid,image,browser}from"@tensorflow/tfjs-core";import{loadGraphModel}from"@tensorflow/tfjs-converter";const disposeBox=t=>{t.startEndTensor.dispose(),t.startPoint.dispose(),t.endPoint.dispose()},createBox=t=>({startEndTensor:t,startPoint:slice(t,[0,0],[-1,2]),endPoint:slice(t,[0,2],[-1,2])}),scaleBox=(t,s)=>{const e=mul(t.startPoint,s),o=mul(t.endPoint,s),i=concat2d([e,o],1);return createBox(i)},ANCHORS_CONFIG={strides:[8,16],anchors:[2,6]},NUM_LANDMARKS=6;function generateAnchors(t,s,e){const o=[];for(let i=0;i<e.strides.length;i++){const a=e.strides[i],n=Math.floor((s+a-1)/a),r=Math.floor((t+a-1)/a),c=e.anchors[i];for(let t=0;t<n;t++){const s=a*(t+.5);for(let t=0;t<r;t++){const e=a*(t+.5);for(let t=0;t<c;t++)o.push([e,s])}}}return o}function decodeBounds(t,s,e){const o=slice(t,[0,1],[-1,2]),i=add(o,s),a=slice(t,[0,3],[-1,2]),n=div(a,e),r=div(i,e),c=div(n,2),l=sub(r,c),d=add(r,c),h=mul(l,e),p=mul(d,e);return concat2d([h,p],1)}function getInputTensorDimensions(t){return t instanceof Tensor?[t.shape[0],t.shape[1]]:[t.height,t.width]}function flipFaceHorizontal(t,s){return t.topLeft instanceof Tensor?{topLeft:concat([sub(s-1,t.topLeft.slice(0,1)),t.topLeft.slice(1,1)]),bottomRight:concat([sub(s-1,t.bottomRight.slice(0,1)),t.bottomRight.slice(1,1)]),landmarks:sub(tensor1d([s-1,0]),t.landmarks).mul(tensor1d([1,-1])),probability:t.probability}:{topLeft:[s-1-t.topLeft[0],t.topLeft[1]],bottomRight:[s-1-t.bottomRight[0],t.bottomRight[1]],landmarks:t.landmarks.map(t=>[s-1-t[0],t[1]]),probability:t.probability}}class BlazeFaceModel{constructor(t,s,e,o,i,a){this.blazeFaceModel=t,this.width=s,this.height=e,this.maxFaces=o,this.anchorsData=generateAnchors(s,e,ANCHORS_CONFIG),this.anchors=tensor2d(this.anchorsData),this.inputSizeData=[s,e],this.inputSize=tensor1d([s,e]),this.iouThreshold=i,this.scoreThreshold=a}async getBoundingBoxes(t,s){const[e,o,i]=tidy(()=>{const s=t.resizeBilinear([this.width,this.height]),e=mul(sub(s.div(255),.5),2),o=this.blazeFaceModel.predict(e).squeeze(),i=decodeBounds(o,this.anchors,this.inputSize),a=slice(o,[0,0],[-1,1]);return[o,i,sigmoid(a).squeeze()]}),a=console.warn;console.warn=(()=>{});const n=image.nonMaxSuppression(o,i,this.maxFaces,this.iouThreshold,this.scoreThreshold);console.warn=a;const r=await n.array();n.dispose();let c=r.map(t=>slice(o,[t,0],[1,-1]));s||(c=await Promise.all(c.map(async t=>{const s=await t.array();return t.dispose(),s})));const l=t.shape[1],d=t.shape[2];let h;h=s?div([d,l],this.inputSize):[d/this.inputSizeData[0],l/this.inputSizeData[1]];const p=c.map((t,o)=>tidy(()=>{const a=r[o];let n;return n=s?this.anchors.slice([a,0],[1,2]):this.anchorsData[a],{box:createBox(t instanceof Tensor?t:tensor2d(t)),landmarks:slice(e,[a,NUM_LANDMARKS-1],[1,-1]).squeeze().reshape([NUM_LANDMARKS,-1]),probability:slice(i,[a],[1]),anchor:n}}));return o.dispose(),i.dispose(),e.dispose(),[p,h]}async estimateFaces(t,s=!1,e=!1){const[,o]=getInputTensorDimensions(t),i=tidy(()=>(t instanceof Tensor||(t=browser.fromPixels(t)),t.toFloat().expandDims(0))),[a,n]=await this.getBoundingBoxes(i,s);return i.dispose(),s?a.map(t=>{const s=scaleBox(t.box,n).startEndTensor.squeeze();let i={topLeft:s.slice([0],[2]),bottomRight:s.slice([2],[2]),landmarks:t.landmarks.add(t.anchor).mul(n),probability:t.probability};return e&&(i=flipFaceHorizontal(i,o)),i}):Promise.all(a.map(async t=>{const s=tidy(()=>scaleBox(t.box,n).startEndTensor.squeeze()),[i,a,r]=await Promise.all([t.landmarks,s,t.probability].map(async t=>t.array())),c=t.anchor,l=i.map(t=>[(t[0]+c[0])*n[0],(t[1]+c[1])*n[1]]);s.dispose(),disposeBox(t.box),t.landmarks.dispose(),t.probability.dispose();let d={topLeft:a.slice(0,2),bottomRight:a.slice(2),landmarks:l,probability:r};return e&&(d=flipFaceHorizontal(d,o)),d}))}}const BLAZEFACE_MODEL_URL="https://tfhub.dev/tensorflow/tfjs-model/blazeface/1/default/1";async function load({maxFaces:t=10,inputWidth:s=128,inputHeight:e=128,iouThreshold:o=.3,scoreThreshold:i=.75}={}){const a=await loadGraphModel(BLAZEFACE_MODEL_URL,{fromTFHub:!0});return new BlazeFaceModel(a,s,e,t,o,i)}export{load,BlazeFaceModel};
import{slice,add,div,sub,mul,concat2d,Tensor,tidy,concat,tensor1d,tensor2d,sigmoid,image,browser}from"@tensorflow/tfjs-core";import{loadGraphModel}from"@tensorflow/tfjs-converter";const disposeBox=t=>{t.startEndTensor.dispose(),t.startPoint.dispose(),t.endPoint.dispose()},createBox=t=>({startEndTensor:t,startPoint:slice(t,[0,0],[-1,2]),endPoint:slice(t,[0,2],[-1,2])}),scaleBox=(t,o)=>{const s=mul(t.startPoint,o),e=mul(t.endPoint,o),i=concat2d([s,e],1);return createBox(i)},ANCHORS_CONFIG={strides:[8,16],anchors:[2,6]},NUM_LANDMARKS=6;function generateAnchors(t,o,s){const e=[];for(let i=0;i<s.strides.length;i++){const n=s.strides[i],a=Math.floor((o+n-1)/n),r=Math.floor((t+n-1)/n),c=s.anchors[i];for(let t=0;t<a;t++){const o=n*(t+.5);for(let t=0;t<r;t++){const s=n*(t+.5);for(let t=0;t<c;t++)e.push([s,o])}}}return e}function decodeBounds(t,o,s){const e=slice(t,[0,1],[-1,2]),i=add(e,o),n=slice(t,[0,3],[-1,2]),a=div(n,s),r=div(i,s),c=div(a,2),l=sub(r,c),d=add(r,c),h=mul(l,s),p=mul(d,s);return concat2d([h,p],1)}function getInputTensorDimensions(t){return t instanceof Tensor?[t.shape[0],t.shape[1]]:[t.height,t.width]}function flipFaceHorizontal(t,o){let s;if(null!=t.probability&&(s.probability=t.probability instanceof Tensor?t.probability.clone():t.probability),t.topLeft instanceof Tensor&&t.bottomRight instanceof Tensor){const[e,i]=tidy(()=>[concat([sub(o-1,t.topLeft.slice(0,1)),t.topLeft.slice(1,1)]),concat([sub(o-1,t.bottomRight.slice(0,1)),t.bottomRight.slice(1,1)])]);if(s={topLeft:e,bottomRight:i},null!=t.landmarks){const e=tidy(()=>sub(tensor1d([o-1,0]),t.landmarks).mul(tensor1d([1,-1])));s.landmarks=e}}else{const[e,i]=t.topLeft,[n,a]=t.bottomRight;s={topLeft:[o-1-e,i],bottomRight:[o-1-n,a]},null!=t.landmarks&&(s.landmarks=t.landmarks.map(t=>[o-1-t[0],t[1]]))}return s}function scaleBoxFromPrediction(t,o){return tidy(()=>{let s;return s=t.hasOwnProperty("box")?t.box:t,scaleBox(s,o).startEndTensor.squeeze()})}class BlazeFaceModel{constructor(t,o,s,e,i,n){this.blazeFaceModel=t,this.width=o,this.height=s,this.maxFaces=e,this.anchorsData=generateAnchors(o,s,ANCHORS_CONFIG),this.anchors=tensor2d(this.anchorsData),this.inputSizeData=[o,s],this.inputSize=tensor1d([o,s]),this.iouThreshold=i,this.scoreThreshold=n}async getBoundingBoxes(t,o,s=!0){const[e,i,n]=tidy(()=>{const o=t.resizeBilinear([this.width,this.height]),s=mul(sub(o.div(255),.5),2),e=this.blazeFaceModel.predict(s).squeeze(),i=decodeBounds(e,this.anchors,this.inputSize),n=slice(e,[0,0],[-1,1]);return[e,i,sigmoid(n).squeeze()]}),a=console.warn;console.warn=(()=>{});const r=image.nonMaxSuppression(i,n,this.maxFaces,this.iouThreshold,this.scoreThreshold);console.warn=a;const c=await r.array();r.dispose();let l=c.map(t=>slice(i,[t,0],[1,-1]));o||(l=await Promise.all(l.map(async t=>{const o=await t.array();return t.dispose(),o})));const d=t.shape[1],h=t.shape[2];let p;p=o?div([h,d],this.inputSize):[h/this.inputSizeData[0],d/this.inputSizeData[1]];const u=[];for(let t=0;t<l.length;t++){const i=l[t],a=tidy(()=>{const a=createBox(i instanceof Tensor?i:tensor2d(i));if(!s)return a;const r=c[t];let l;return l=o?this.anchors.slice([r,0],[1,2]):this.anchorsData[r],{box:a,landmarks:slice(e,[r,NUM_LANDMARKS-1],[1,-1]).squeeze().reshape([NUM_LANDMARKS,-1]),probability:slice(n,[r],[1]),anchor:l}});u.push(a)}return i.dispose(),n.dispose(),e.dispose(),{boxes:u,scaleFactor:p}}async estimateFaces(t,o=!1,s=!1,e=!0){const[,i]=getInputTensorDimensions(t),n=tidy(()=>(t instanceof Tensor||(t=browser.fromPixels(t)),t.toFloat().expandDims(0))),{boxes:a,scaleFactor:r}=await this.getBoundingBoxes(n,o,e);return n.dispose(),o?a.map(t=>{const o=scaleBoxFromPrediction(t,r);let n={topLeft:o.slice([0],[2]),bottomRight:o.slice([2],[2])};if(e){const{landmarks:o,probability:s,anchor:e}=t,i=o.add(e).mul(r);n.landmarks=i,n.probability=s}return s&&(n=flipFaceHorizontal(n,i)),n}):Promise.all(a.map(async t=>{const o=scaleBoxFromPrediction(t,r);let n;if(e){const[s,e,i]=await Promise.all([t.landmarks,o,t.probability].map(async t=>t.array())),a=t.anchor,[c,l]=r,d=s.map(t=>[(t[0]+a[0])*c,(t[1]+a[1])*l]);n={topLeft:e.slice(0,2),bottomRight:e.slice(2),landmarks:d,probability:i},disposeBox(t.box),t.landmarks.dispose(),t.probability.dispose()}else{const t=await o.array();n={topLeft:t.slice(0,2),bottomRight:t.slice(2)}}return o.dispose(),s&&(n=flipFaceHorizontal(n,i)),n}))}}const BLAZEFACE_MODEL_URL="https://tfhub.dev/tensorflow/tfjs-model/blazeface/1/default/1";async function load({maxFaces:t=10,inputWidth:o=128,inputHeight:s=128,iouThreshold:e=.3,scoreThreshold:i=.75}={}){const n=await loadGraphModel(BLAZEFACE_MODEL_URL,{fromTFHub:!0});return new BlazeFaceModel(n,o,s,t,e,i)}export{load,BlazeFaceModel};

@@ -83,29 +83,58 @@ /**

function flipFaceHorizontal(face, imageWidth) {
if (face.topLeft instanceof tf.Tensor) {
return {
topLeft: tf.concat([
tf.sub(imageWidth - 1, face.topLeft.slice(0, 1)),
face.topLeft.slice(1, 1)
]),
bottomRight: tf.concat([
tf.sub(imageWidth - 1, face.bottomRight.slice(0, 1)),
face.bottomRight.slice(1, 1)
]),
landmarks: tf.sub(tf.tensor1d([imageWidth - 1, 0]), face.landmarks)
.mul(tf.tensor1d([1, -1])),
probability: face.probability
let flipped;
if (face.probability != null) {
flipped.probability = face.probability instanceof tf.Tensor ?
face.probability.clone() :
face.probability;
}
if (face.topLeft instanceof tf.Tensor &&
face.bottomRight instanceof tf.Tensor) {
const [topLeft, bottomRight] = tf.tidy(() => {
return [
tf.concat([
tf.sub(imageWidth - 1, face.topLeft.slice(0, 1)),
face.topLeft.slice(1, 1)
]),
tf.concat([
tf.sub(imageWidth - 1, face.bottomRight.slice(0, 1)),
face.bottomRight.slice(1, 1)
])
];
});
flipped = { topLeft, bottomRight };
if (face.landmarks != null) {
const flippedLandmarks = tf.tidy(() => tf.sub(tf.tensor1d([imageWidth - 1, 0]), face.landmarks)
.mul(tf.tensor1d([1, -1])));
flipped.landmarks = flippedLandmarks;
}
}
else {
const [topLeftX, topLeftY] = face.topLeft;
const [bottomRightX, bottomRightY] = face.bottomRight;
flipped = {
topLeft: [imageWidth - 1 - topLeftX, topLeftY],
bottomRight: [imageWidth - 1 - bottomRightX, bottomRightY]
};
if (face.landmarks != null) {
flipped.landmarks =
face.landmarks.map((coord) => ([
imageWidth - 1 - coord[0],
coord[1]
]));
}
}
return {
topLeft: [imageWidth - 1 - face.topLeft[0], face.topLeft[1]],
bottomRight: [
imageWidth - 1 - face.bottomRight[0],
face.bottomRight[1]
],
landmarks: face.landmarks.map((coord) => ([
imageWidth - 1 - coord[0], coord[1]
])),
probability: face.probability
};
return flipped;
}
function scaleBoxFromPrediction(face, scaleFactor) {
return tf.tidy(() => {
let box;
if (face.hasOwnProperty('box')) {
box = face.box;
}
else {
box = face;
}
return scaleBox(box, scaleFactor).startEndTensor.squeeze();
});
}
class BlazeFaceModel {

@@ -124,3 +153,3 @@ constructor(model, width, height, maxFaces, iouThreshold, scoreThreshold) {

}
async getBoundingBoxes(inputImage, returnTensors) {
async getBoundingBoxes(inputImage, returnTensors, annotateBoxes = true) {
const [detectedOutputs, boxes, scores] = tf.tidy(() => {

@@ -133,3 +162,4 @@ const resizedImage = inputImage.resizeBilinear([this.width, this.height]);

const logits = tf.slice(prediction, [0, 0], [-1, 1]);
return [prediction, decodedBounds, tf.sigmoid(logits).squeeze()];
const scores = tf.sigmoid(logits).squeeze();
return [prediction, decodedBounds, scores];
});

@@ -162,27 +192,37 @@ const savedConsoleWarnFn = console.warn;

}
const annotatedBoxes = boundingBoxes
.map((boundingBox, i) => tf.tidy(() => {
const boxIndex = boxIndices[i];
let anchor;
if (returnTensors) {
anchor = this.anchors.slice([boxIndex, 0], [1, 2]);
}
else {
anchor = this.anchorsData[boxIndex];
}
const box = boundingBox instanceof tf.Tensor ?
createBox(boundingBox) :
createBox(tf.tensor2d(boundingBox));
const landmarks = tf.slice(detectedOutputs, [boxIndex, NUM_LANDMARKS - 1], [1, -1])
.squeeze()
.reshape([NUM_LANDMARKS, -1]);
const probability = tf.slice(scores, [boxIndex], [1]);
return { box, landmarks, probability, anchor };
}));
const annotatedBoxes = [];
for (let i = 0; i < boundingBoxes.length; i++) {
const boundingBox = boundingBoxes[i];
const annotatedBox = tf.tidy(() => {
const box = boundingBox instanceof tf.Tensor ?
createBox(boundingBox) :
createBox(tf.tensor2d(boundingBox));
if (!annotateBoxes) {
return box;
}
const boxIndex = boxIndices[i];
let anchor;
if (returnTensors) {
anchor = this.anchors.slice([boxIndex, 0], [1, 2]);
}
else {
anchor = this.anchorsData[boxIndex];
}
const landmarks = tf.slice(detectedOutputs, [boxIndex, NUM_LANDMARKS - 1], [1, -1])
.squeeze()
.reshape([NUM_LANDMARKS, -1]);
const probability = tf.slice(scores, [boxIndex], [1]);
return { box, landmarks, probability, anchor };
});
annotatedBoxes.push(annotatedBox);
}
boxes.dispose();
scores.dispose();
detectedOutputs.dispose();
return [annotatedBoxes, scaleFactor];
return {
boxes: annotatedBoxes,
scaleFactor
};
}
async estimateFaces(input, returnTensors = false, flipHorizontal = false) {
async estimateFaces(input, returnTensors = false, flipHorizontal = false, annotateBoxes = true) {
const [, width] = getInputTensorDimensions(input);

@@ -195,17 +235,19 @@ const image = tf.tidy(() => {

});
const [prediction, scaleFactor] = await this.getBoundingBoxes(image, returnTensors);
const { boxes, scaleFactor } = await this.getBoundingBoxes(image, returnTensors, annotateBoxes);
image.dispose();
if (returnTensors) {
return prediction.map((face) => {
const scaledBox = scaleBox(face.box, scaleFactor)
.startEndTensor.squeeze();
return boxes.map((face) => {
const scaledBox = scaleBoxFromPrediction(face, scaleFactor);
let normalizedFace = {
topLeft: scaledBox.slice([0], [2]),
bottomRight: scaledBox.slice([2], [2]),
landmarks: face.landmarks.add(face.anchor).mul(scaleFactor),
probability: face.probability
bottomRight: scaledBox.slice([2], [2])
};
if (annotateBoxes) {
const { landmarks, probability, anchor } = face;
const normalizedLandmarks = landmarks.add(anchor).mul(scaleFactor);
normalizedFace.landmarks = normalizedLandmarks;
normalizedFace.probability = probability;
}
if (flipHorizontal) {
normalizedFace =
flipFaceHorizontal(normalizedFace, width);
normalizedFace = flipFaceHorizontal(normalizedFace, width);
}

@@ -215,26 +257,32 @@ return normalizedFace;

}
return Promise.all(prediction.map(async (face) => {
const scaledBox = tf.tidy(() => {
return scaleBox(face.box, scaleFactor)
.startEndTensor.squeeze();
});
const [landmarkData, boxData, probabilityData] = await Promise.all([face.landmarks, scaledBox, face.probability].map(async (d) => d.array()));
const anchor = face.anchor;
const scaledLandmarks = landmarkData
.map((landmark) => ([
(landmark[0] + anchor[0]) *
scaleFactor[0],
(landmark[1] + anchor[1]) *
scaleFactor[1]
]));
return Promise.all(boxes.map(async (face) => {
const scaledBox = scaleBoxFromPrediction(face, scaleFactor);
let normalizedFace;
if (!annotateBoxes) {
const boxData = await scaledBox.array();
normalizedFace = {
topLeft: boxData.slice(0, 2),
bottomRight: boxData.slice(2)
};
}
else {
const [landmarkData, boxData, probabilityData] = await Promise.all([face.landmarks, scaledBox, face.probability].map(async (d) => d.array()));
const anchor = face.anchor;
const [scaleFactorX, scaleFactorY] = scaleFactor;
const scaledLandmarks = landmarkData
.map(landmark => ([
(landmark[0] + anchor[0]) * scaleFactorX,
(landmark[1] + anchor[1]) * scaleFactorY
]));
normalizedFace = {
topLeft: boxData.slice(0, 2),
bottomRight: boxData.slice(2),
landmarks: scaledLandmarks,
probability: probabilityData
};
disposeBox(face.box);
face.landmarks.dispose();
face.probability.dispose();
}
scaledBox.dispose();
disposeBox(face.box);
face.landmarks.dispose();
face.probability.dispose();
let normalizedFace = {
topLeft: boxData.slice(0, 2),
bottomRight: boxData.slice(2),
landmarks: scaledLandmarks,
probability: probabilityData
};
if (flipHorizontal) {

@@ -241,0 +289,0 @@ normalizedFace = flipFaceHorizontal(normalizedFace, width);

@@ -17,2 +17,2 @@ /**

*/
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@tensorflow/tfjs-core"),require("@tensorflow/tfjs-converter")):"function"==typeof define&&define.amd?define(["exports","@tensorflow/tfjs-core","@tensorflow/tfjs-converter"],e):e(t.blazeface={},t.tf,t.tf)}(this,function(t,e,s){"use strict";const o=t=>{t.startEndTensor.dispose(),t.startPoint.dispose(),t.endPoint.dispose()},i=t=>({startEndTensor:t,startPoint:e.slice(t,[0,0],[-1,2]),endPoint:e.slice(t,[0,2],[-1,2])}),n=(t,s)=>{const o=e.mul(t.startPoint,s),n=e.mul(t.endPoint,s),a=e.concat2d([o,n],1);return i(a)},a={strides:[8,16],anchors:[2,6]},r=6;function c(t,s){return t.topLeft instanceof e.Tensor?{topLeft:e.concat([e.sub(s-1,t.topLeft.slice(0,1)),t.topLeft.slice(1,1)]),bottomRight:e.concat([e.sub(s-1,t.bottomRight.slice(0,1)),t.bottomRight.slice(1,1)]),landmarks:e.sub(e.tensor1d([s-1,0]),t.landmarks).mul(e.tensor1d([1,-1])),probability:t.probability}:{topLeft:[s-1-t.topLeft[0],t.topLeft[1]],bottomRight:[s-1-t.bottomRight[0],t.bottomRight[1]],landmarks:t.landmarks.map(t=>[s-1-t[0],t[1]]),probability:t.probability}}class l{constructor(t,s,o,i,n,r){this.blazeFaceModel=t,this.width=s,this.height=o,this.maxFaces=i,this.anchorsData=function(t,e,s){const o=[];for(let i=0;i<s.strides.length;i++){const n=s.strides[i],a=Math.floor((e+n-1)/n),r=Math.floor((t+n-1)/n),c=s.anchors[i];for(let t=0;t<a;t++){const e=n*(t+.5);for(let t=0;t<r;t++){const s=n*(t+.5);for(let t=0;t<c;t++)o.push([s,e])}}}return o}(s,o,a),this.anchors=e.tensor2d(this.anchorsData),this.inputSizeData=[s,o],this.inputSize=e.tensor1d([s,o]),this.iouThreshold=n,this.scoreThreshold=r}async getBoundingBoxes(t,s){const[o,n,a]=e.tidy(()=>{const s=t.resizeBilinear([this.width,this.height]),o=e.mul(e.sub(s.div(255),.5),2),i=this.blazeFaceModel.predict(o).squeeze(),n=function(t,s,o){const i=e.slice(t,[0,1],[-1,2]),n=e.add(i,s),a=e.slice(t,[0,3],[-1,2]),r=e.div(a,o),c=e.div(n,o),l=e.div(r,2),d=e.sub(c,l),h=e.add(c,l),p=e.mul(d,o),u=e.mul(h,o);return e.concat2d([p,u],1)}(i,this.anchors,this.inputSize),a=e.slice(i,[0,0],[-1,1]);return[i,n,e.sigmoid(a).squeeze()]}),c=console.warn;console.warn=(()=>{});const l=e.image.nonMaxSuppression(n,a,this.maxFaces,this.iouThreshold,this.scoreThreshold);console.warn=c;const d=await l.array();l.dispose();let h=d.map(t=>e.slice(n,[t,0],[1,-1]));s||(h=await Promise.all(h.map(async t=>{const e=await t.array();return t.dispose(),e})));const p=t.shape[1],u=t.shape[2];let f;f=s?e.div([u,p],this.inputSize):[u/this.inputSizeData[0],p/this.inputSizeData[1]];const m=h.map((t,n)=>e.tidy(()=>{const c=d[n];let l;return l=s?this.anchors.slice([c,0],[1,2]):this.anchorsData[c],{box:t instanceof e.Tensor?i(t):i(e.tensor2d(t)),landmarks:e.slice(o,[c,r-1],[1,-1]).squeeze().reshape([r,-1]),probability:e.slice(a,[c],[1]),anchor:l}}));return n.dispose(),a.dispose(),o.dispose(),[m,f]}async estimateFaces(t,s=!1,i=!1){const[,a]=function(t){return t instanceof e.Tensor?[t.shape[0],t.shape[1]]:[t.height,t.width]}(t),r=e.tidy(()=>(t instanceof e.Tensor||(t=e.browser.fromPixels(t)),t.toFloat().expandDims(0))),[l,d]=await this.getBoundingBoxes(r,s);return r.dispose(),s?l.map(t=>{const e=n(t.box,d).startEndTensor.squeeze();let s={topLeft:e.slice([0],[2]),bottomRight:e.slice([2],[2]),landmarks:t.landmarks.add(t.anchor).mul(d),probability:t.probability};return i&&(s=c(s,a)),s}):Promise.all(l.map(async t=>{const s=e.tidy(()=>n(t.box,d).startEndTensor.squeeze()),[r,l,h]=await Promise.all([t.landmarks,s,t.probability].map(async t=>t.array())),p=t.anchor,u=r.map(t=>[(t[0]+p[0])*d[0],(t[1]+p[1])*d[1]]);s.dispose(),o(t.box),t.landmarks.dispose(),t.probability.dispose();let f={topLeft:l.slice(0,2),bottomRight:l.slice(2),landmarks:u,probability:h};return i&&(f=c(f,a)),f}))}}const d="https://tfhub.dev/tensorflow/tfjs-model/blazeface/1/default/1";t.load=async function({maxFaces:t=10,inputWidth:e=128,inputHeight:o=128,iouThreshold:i=.3,scoreThreshold:n=.75}={}){const a=await s.loadGraphModel(d,{fromTFHub:!0});return new l(a,e,o,t,i,n)},t.BlazeFaceModel=l,Object.defineProperty(t,"__esModule",{value:!0})});
!function(t,s){"object"==typeof exports&&"undefined"!=typeof module?s(exports,require("@tensorflow/tfjs-core"),require("@tensorflow/tfjs-converter")):"function"==typeof define&&define.amd?define(["exports","@tensorflow/tfjs-core","@tensorflow/tfjs-converter"],s):s(t.blazeface={},t.tf,t.tf)}(this,function(t,s,e){"use strict";const o=t=>{t.startEndTensor.dispose(),t.startPoint.dispose(),t.endPoint.dispose()},i=t=>({startEndTensor:t,startPoint:s.slice(t,[0,0],[-1,2]),endPoint:s.slice(t,[0,2],[-1,2])}),n=(t,e)=>{const o=s.mul(t.startPoint,e),n=s.mul(t.endPoint,e),a=s.concat2d([o,n],1);return i(a)},a={strides:[8,16],anchors:[2,6]},r=6;function c(t,e){let o;if(null!=t.probability&&(o.probability=t.probability instanceof s.Tensor?t.probability.clone():t.probability),t.topLeft instanceof s.Tensor&&t.bottomRight instanceof s.Tensor){const[i,n]=s.tidy(()=>[s.concat([s.sub(e-1,t.topLeft.slice(0,1)),t.topLeft.slice(1,1)]),s.concat([s.sub(e-1,t.bottomRight.slice(0,1)),t.bottomRight.slice(1,1)])]);if(o={topLeft:i,bottomRight:n},null!=t.landmarks){const i=s.tidy(()=>s.sub(s.tensor1d([e-1,0]),t.landmarks).mul(s.tensor1d([1,-1])));o.landmarks=i}}else{const[s,i]=t.topLeft,[n,a]=t.bottomRight;o={topLeft:[e-1-s,i],bottomRight:[e-1-n,a]},null!=t.landmarks&&(o.landmarks=t.landmarks.map(t=>[e-1-t[0],t[1]]))}return o}function l(t,e){return s.tidy(()=>{let s;return s=t.hasOwnProperty("box")?t.box:t,n(s,e).startEndTensor.squeeze()})}class d{constructor(t,e,o,i,n,r){this.blazeFaceModel=t,this.width=e,this.height=o,this.maxFaces=i,this.anchorsData=function(t,s,e){const o=[];for(let i=0;i<e.strides.length;i++){const n=e.strides[i],a=Math.floor((s+n-1)/n),r=Math.floor((t+n-1)/n),c=e.anchors[i];for(let t=0;t<a;t++){const s=n*(t+.5);for(let t=0;t<r;t++){const e=n*(t+.5);for(let t=0;t<c;t++)o.push([e,s])}}}return o}(e,o,a),this.anchors=s.tensor2d(this.anchorsData),this.inputSizeData=[e,o],this.inputSize=s.tensor1d([e,o]),this.iouThreshold=n,this.scoreThreshold=r}async getBoundingBoxes(t,e,o=!0){const[n,a,c]=s.tidy(()=>{const e=t.resizeBilinear([this.width,this.height]),o=s.mul(s.sub(e.div(255),.5),2),i=this.blazeFaceModel.predict(o).squeeze(),n=function(t,e,o){const i=s.slice(t,[0,1],[-1,2]),n=s.add(i,e),a=s.slice(t,[0,3],[-1,2]),r=s.div(a,o),c=s.div(n,o),l=s.div(r,2),d=s.sub(c,l),h=s.add(c,l),u=s.mul(d,o),p=s.mul(h,o);return s.concat2d([u,p],1)}(i,this.anchors,this.inputSize),a=s.slice(i,[0,0],[-1,1]);return[i,n,s.sigmoid(a).squeeze()]}),l=console.warn;console.warn=(()=>{});const d=s.image.nonMaxSuppression(a,c,this.maxFaces,this.iouThreshold,this.scoreThreshold);console.warn=l;const h=await d.array();d.dispose();let u=h.map(t=>s.slice(a,[t,0],[1,-1]));e||(u=await Promise.all(u.map(async t=>{const s=await t.array();return t.dispose(),s})));const p=t.shape[1],f=t.shape[2];let b;b=e?s.div([f,p],this.inputSize):[f/this.inputSizeData[0],p/this.inputSizeData[1]];const m=[];for(let t=0;t<u.length;t++){const a=u[t],l=s.tidy(()=>{const l=a instanceof s.Tensor?i(a):i(s.tensor2d(a));if(!o)return l;const d=h[t];let u;return u=e?this.anchors.slice([d,0],[1,2]):this.anchorsData[d],{box:l,landmarks:s.slice(n,[d,r-1],[1,-1]).squeeze().reshape([r,-1]),probability:s.slice(c,[d],[1]),anchor:u}});m.push(l)}return a.dispose(),c.dispose(),n.dispose(),{boxes:m,scaleFactor:b}}async estimateFaces(t,e=!1,i=!1,n=!0){const[,a]=function(t){return t instanceof s.Tensor?[t.shape[0],t.shape[1]]:[t.height,t.width]}(t),r=s.tidy(()=>(t instanceof s.Tensor||(t=s.browser.fromPixels(t)),t.toFloat().expandDims(0))),{boxes:d,scaleFactor:h}=await this.getBoundingBoxes(r,e,n);return r.dispose(),e?d.map(t=>{const s=l(t,h);let e={topLeft:s.slice([0],[2]),bottomRight:s.slice([2],[2])};if(n){const{landmarks:s,probability:o,anchor:i}=t,n=s.add(i).mul(h);e.landmarks=n,e.probability=o}return i&&(e=c(e,a)),e}):Promise.all(d.map(async t=>{const s=l(t,h);let e;if(n){const[i,n,a]=await Promise.all([t.landmarks,s,t.probability].map(async t=>t.array())),r=t.anchor,[c,l]=h,d=i.map(t=>[(t[0]+r[0])*c,(t[1]+r[1])*l]);e={topLeft:n.slice(0,2),bottomRight:n.slice(2),landmarks:d,probability:a},o(t.box),t.landmarks.dispose(),t.probability.dispose()}else{const t=await s.array();e={topLeft:t.slice(0,2),bottomRight:t.slice(2)}}return s.dispose(),i&&(e=c(e,a)),e}))}}const h="https://tfhub.dev/tensorflow/tfjs-model/blazeface/1/default/1";t.load=async function({maxFaces:t=10,inputWidth:s=128,inputHeight:o=128,iouThreshold:i=.3,scoreThreshold:n=.75}={}){const a=await e.loadGraphModel(h,{fromTFHub:!0});return new d(a,s,o,t,i,n)},t.BlazeFaceModel=d,Object.defineProperty(t,"__esModule",{value:!0})});

@@ -7,6 +7,6 @@ import * as tfconv from '@tensorflow/tfjs-converter';

bottomRight: [number, number] | tf.Tensor1D;
landmarks: number[][] | tf.Tensor2D;
probability: number | tf.Tensor1D;
landmarks?: number[][] | tf.Tensor2D;
probability?: number | tf.Tensor1D;
}
declare type BlazeFacePrediction = {
export declare type BlazeFacePrediction = {
box: Box;

@@ -29,5 +29,7 @@ landmarks: tf.Tensor2D;

constructor(model: tfconv.GraphModel, width: number, height: number, maxFaces: number, iouThreshold: number, scoreThreshold: number);
getBoundingBoxes(inputImage: tf.Tensor4D, returnTensors: boolean): Promise<[BlazeFacePrediction[], tf.Tensor | [number, number]]>;
estimateFaces(input: tf.Tensor3D | ImageData | HTMLVideoElement | HTMLImageElement | HTMLCanvasElement, returnTensors?: boolean, flipHorizontal?: boolean): Promise<NormalizedFace[]>;
getBoundingBoxes(inputImage: tf.Tensor4D, returnTensors: boolean, annotateBoxes?: boolean): Promise<{
boxes: Array<BlazeFacePrediction | Box>;
scaleFactor: tf.Tensor | [number, number];
}>;
estimateFaces(input: tf.Tensor3D | ImageData | HTMLVideoElement | HTMLImageElement | HTMLCanvasElement, returnTensors?: boolean, flipHorizontal?: boolean, annotateBoxes?: boolean): Promise<NormalizedFace[]>;
}
export {};

@@ -48,29 +48,58 @@ "use strict";

function flipFaceHorizontal(face, imageWidth) {
if (face.topLeft instanceof tf.Tensor) {
return {
topLeft: tf.concat([
tf.sub(imageWidth - 1, face.topLeft.slice(0, 1)),
face.topLeft.slice(1, 1)
]),
bottomRight: tf.concat([
tf.sub(imageWidth - 1, face.bottomRight.slice(0, 1)),
face.bottomRight.slice(1, 1)
]),
landmarks: tf.sub(tf.tensor1d([imageWidth - 1, 0]), face.landmarks)
.mul(tf.tensor1d([1, -1])),
probability: face.probability
let flipped;
if (face.probability != null) {
flipped.probability = face.probability instanceof tf.Tensor ?
face.probability.clone() :
face.probability;
}
if (face.topLeft instanceof tf.Tensor &&
face.bottomRight instanceof tf.Tensor) {
const [topLeft, bottomRight] = tf.tidy(() => {
return [
tf.concat([
tf.sub(imageWidth - 1, face.topLeft.slice(0, 1)),
face.topLeft.slice(1, 1)
]),
tf.concat([
tf.sub(imageWidth - 1, face.bottomRight.slice(0, 1)),
face.bottomRight.slice(1, 1)
])
];
});
flipped = { topLeft, bottomRight };
if (face.landmarks != null) {
const flippedLandmarks = tf.tidy(() => tf.sub(tf.tensor1d([imageWidth - 1, 0]), face.landmarks)
.mul(tf.tensor1d([1, -1])));
flipped.landmarks = flippedLandmarks;
}
}
else {
const [topLeftX, topLeftY] = face.topLeft;
const [bottomRightX, bottomRightY] = face.bottomRight;
flipped = {
topLeft: [imageWidth - 1 - topLeftX, topLeftY],
bottomRight: [imageWidth - 1 - bottomRightX, bottomRightY]
};
if (face.landmarks != null) {
flipped.landmarks =
face.landmarks.map((coord) => ([
imageWidth - 1 - coord[0],
coord[1]
]));
}
}
return {
topLeft: [imageWidth - 1 - face.topLeft[0], face.topLeft[1]],
bottomRight: [
imageWidth - 1 - face.bottomRight[0],
face.bottomRight[1]
],
landmarks: face.landmarks.map((coord) => ([
imageWidth - 1 - coord[0], coord[1]
])),
probability: face.probability
};
return flipped;
}
function scaleBoxFromPrediction(face, scaleFactor) {
return tf.tidy(() => {
let box;
if (face.hasOwnProperty('box')) {
box = face.box;
}
else {
box = face;
}
return box_1.scaleBox(box, scaleFactor).startEndTensor.squeeze();
});
}
class BlazeFaceModel {

@@ -89,3 +118,3 @@ constructor(model, width, height, maxFaces, iouThreshold, scoreThreshold) {

}
async getBoundingBoxes(inputImage, returnTensors) {
async getBoundingBoxes(inputImage, returnTensors, annotateBoxes = true) {
const [detectedOutputs, boxes, scores] = tf.tidy(() => {

@@ -98,3 +127,4 @@ const resizedImage = inputImage.resizeBilinear([this.width, this.height]);

const logits = tf.slice(prediction, [0, 0], [-1, 1]);
return [prediction, decodedBounds, tf.sigmoid(logits).squeeze()];
const scores = tf.sigmoid(logits).squeeze();
return [prediction, decodedBounds, scores];
});

@@ -127,27 +157,37 @@ const savedConsoleWarnFn = console.warn;

}
const annotatedBoxes = boundingBoxes
.map((boundingBox, i) => tf.tidy(() => {
const boxIndex = boxIndices[i];
let anchor;
if (returnTensors) {
anchor = this.anchors.slice([boxIndex, 0], [1, 2]);
}
else {
anchor = this.anchorsData[boxIndex];
}
const box = boundingBox instanceof tf.Tensor ?
box_1.createBox(boundingBox) :
box_1.createBox(tf.tensor2d(boundingBox));
const landmarks = tf.slice(detectedOutputs, [boxIndex, NUM_LANDMARKS - 1], [1, -1])
.squeeze()
.reshape([NUM_LANDMARKS, -1]);
const probability = tf.slice(scores, [boxIndex], [1]);
return { box, landmarks, probability, anchor };
}));
const annotatedBoxes = [];
for (let i = 0; i < boundingBoxes.length; i++) {
const boundingBox = boundingBoxes[i];
const annotatedBox = tf.tidy(() => {
const box = boundingBox instanceof tf.Tensor ?
box_1.createBox(boundingBox) :
box_1.createBox(tf.tensor2d(boundingBox));
if (!annotateBoxes) {
return box;
}
const boxIndex = boxIndices[i];
let anchor;
if (returnTensors) {
anchor = this.anchors.slice([boxIndex, 0], [1, 2]);
}
else {
anchor = this.anchorsData[boxIndex];
}
const landmarks = tf.slice(detectedOutputs, [boxIndex, NUM_LANDMARKS - 1], [1, -1])
.squeeze()
.reshape([NUM_LANDMARKS, -1]);
const probability = tf.slice(scores, [boxIndex], [1]);
return { box, landmarks, probability, anchor };
});
annotatedBoxes.push(annotatedBox);
}
boxes.dispose();
scores.dispose();
detectedOutputs.dispose();
return [annotatedBoxes, scaleFactor];
return {
boxes: annotatedBoxes,
scaleFactor
};
}
async estimateFaces(input, returnTensors = false, flipHorizontal = false) {
async estimateFaces(input, returnTensors = false, flipHorizontal = false, annotateBoxes = true) {
const [, width] = getInputTensorDimensions(input);

@@ -160,17 +200,19 @@ const image = tf.tidy(() => {

});
const [prediction, scaleFactor] = await this.getBoundingBoxes(image, returnTensors);
const { boxes, scaleFactor } = await this.getBoundingBoxes(image, returnTensors, annotateBoxes);
image.dispose();
if (returnTensors) {
return prediction.map((face) => {
const scaledBox = box_1.scaleBox(face.box, scaleFactor)
.startEndTensor.squeeze();
return boxes.map((face) => {
const scaledBox = scaleBoxFromPrediction(face, scaleFactor);
let normalizedFace = {
topLeft: scaledBox.slice([0], [2]),
bottomRight: scaledBox.slice([2], [2]),
landmarks: face.landmarks.add(face.anchor).mul(scaleFactor),
probability: face.probability
bottomRight: scaledBox.slice([2], [2])
};
if (annotateBoxes) {
const { landmarks, probability, anchor } = face;
const normalizedLandmarks = landmarks.add(anchor).mul(scaleFactor);
normalizedFace.landmarks = normalizedLandmarks;
normalizedFace.probability = probability;
}
if (flipHorizontal) {
normalizedFace =
flipFaceHorizontal(normalizedFace, width);
normalizedFace = flipFaceHorizontal(normalizedFace, width);
}

@@ -180,26 +222,32 @@ return normalizedFace;

}
return Promise.all(prediction.map(async (face) => {
const scaledBox = tf.tidy(() => {
return box_1.scaleBox(face.box, scaleFactor)
.startEndTensor.squeeze();
});
const [landmarkData, boxData, probabilityData] = await Promise.all([face.landmarks, scaledBox, face.probability].map(async (d) => d.array()));
const anchor = face.anchor;
const scaledLandmarks = landmarkData
.map((landmark) => ([
(landmark[0] + anchor[0]) *
scaleFactor[0],
(landmark[1] + anchor[1]) *
scaleFactor[1]
]));
return Promise.all(boxes.map(async (face) => {
const scaledBox = scaleBoxFromPrediction(face, scaleFactor);
let normalizedFace;
if (!annotateBoxes) {
const boxData = await scaledBox.array();
normalizedFace = {
topLeft: boxData.slice(0, 2),
bottomRight: boxData.slice(2)
};
}
else {
const [landmarkData, boxData, probabilityData] = await Promise.all([face.landmarks, scaledBox, face.probability].map(async (d) => d.array()));
const anchor = face.anchor;
const [scaleFactorX, scaleFactorY] = scaleFactor;
const scaledLandmarks = landmarkData
.map(landmark => ([
(landmark[0] + anchor[0]) * scaleFactorX,
(landmark[1] + anchor[1]) * scaleFactorY
]));
normalizedFace = {
topLeft: boxData.slice(0, 2),
bottomRight: boxData.slice(2),
landmarks: scaledLandmarks,
probability: probabilityData
};
box_1.disposeBox(face.box);
face.landmarks.dispose();
face.probability.dispose();
}
scaledBox.dispose();
box_1.disposeBox(face.box);
face.landmarks.dispose();
face.probability.dispose();
let normalizedFace = {
topLeft: boxData.slice(0, 2),
bottomRight: boxData.slice(2),
landmarks: scaledLandmarks,
probability: probabilityData
};
if (flipHorizontal) {

@@ -206,0 +254,0 @@ normalizedFace = flipFaceHorizontal(normalizedFace, width);

@@ -9,2 +9,2 @@ import { BlazeFaceModel } from './face';

}): Promise<BlazeFaceModel>;
export { NormalizedFace, BlazeFaceModel } from './face';
export { NormalizedFace, BlazeFaceModel, BlazeFacePrediction } from './face';

@@ -1,2 +0,2 @@

declare const version = "0.0.3";
declare const version = "0.0.4";
export { version };
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const version = '0.0.3';
const version = '0.0.4';
exports.version = version;
//# sourceMappingURL=version.js.map
{
"name": "@tensorflow-models/blazeface",
"version": "0.0.3",
"version": "0.0.4",
"description": "Pretrained face detection model in TensorFlow.js",

@@ -16,8 +16,8 @@ "main": "dist/index.js",

"peerDependencies": {
"@tensorflow/tfjs-core": "^1.3.2",
"@tensorflow/tfjs-converter": "^1.3.2"
"@tensorflow/tfjs-core": "^1.5.2",
"@tensorflow/tfjs-converter": "^1.5.2"
},
"devDependencies": {
"@tensorflow/tfjs-core": "^1.3.2",
"@tensorflow/tfjs-converter": "^1.3.2",
"@tensorflow/tfjs-core": "^1.5.2",
"@tensorflow/tfjs-converter": "^1.5.2",
"@types/jasmine": "~2.5.53",

@@ -24,0 +24,0 @@ "jasmine": "~3.2.0",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc