ente/thirdparty/blazeface/dist/blazeface.esm.js
Shailesh Pandit 4ebcddbb84 Provision to run ml sync on original images
To capture high resolution face images
Use ImageBitmap till tf models as tf manipulations on variable dimention images leads to gpu memory leak - https://github.com/tensorflow/tfjs/issues/604
2021-12-20 22:20:20 +05:30

18 lines
8.3 KiB
JavaScript

/**
* @license
* Copyright 2021 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/
import{slice,add,div,sub,mul,concat2d,Tensor,tidy,concat,tensor1d,squeeze,tensor2d,image,sigmoid,reshape,browser,expandDims,cast}from"@tensorflow/tfjs-core";import{loadGraphModel}from"@tensorflow/tfjs-converter";function __awaiter(e,t,r,n){return new(r||(r=Promise))(function(o,i){function a(e){try{c(n.next(e))}catch(e){i(e)}}function s(e){try{c(n.throw(e))}catch(e){i(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof r?t:new r(function(e){e(t)})).then(a,s)}c((n=n.apply(e,t||[])).next())})}function __generator(e,t){var r,n,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(r)throw new TypeError("Generator is already executing.");for(;a;)try{if(r=1,n&&(o=2&i[0]?n.return:i[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,i[1])).done)return o;switch(n=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,n=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=(o=a.trys).length>0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]<o[3])){a.label=i[1];break}if(6===i[0]&&a.label<o[1]){a.label=o[1],o=i;break}if(o&&a.label<o[2]){a.label=o[2],a.ops.push(i);break}o[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(e){i=[6,e],n=0}finally{r=o=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,s])}}}var disposeBox=function(e){e.startEndTensor.dispose(),e.startPoint.dispose(),e.endPoint.dispose()},createBox=function(e){return{startEndTensor:e,startPoint:slice(e,[0,0],[-1,2]),endPoint:slice(e,[0,2],[-1,2])}},scaleBox=function(e,t){var r=mul(e.startPoint,t),n=mul(e.endPoint,t),o=concat2d([r,n],1);return createBox(o)},NUM_LANDMARKS=6;function generateAnchors(e,t,r){for(var n=[],o=0;o<r.strides.length;o++)for(var i=r.strides[o],a=Math.floor((t+i-1)/i),s=Math.floor((e+i-1)/i),c=r.anchors[o],l=0;l<a;l++)for(var u=i*(l+.5),d=0;d<s;d++)for(var h=i*(d+.5),f=0;f<c;f++)n.push([h,u]);return n}function decodeBounds(e,t,r){var n=slice(e,[0,1],[-1,2]),o=add(n,t),i=slice(e,[0,3],[-1,2]),a=div(i,r),s=div(o,r),c=div(a,2),l=sub(s,c),u=add(s,c),d=mul(l,r),h=mul(u,r);return concat2d([d,h],1)}function getInputTensorDimensions(e){return e instanceof Tensor?[e.shape[0],e.shape[1]]:[e.height,e.width]}function flipFaceHorizontal(e,t){var r,n,o;if(e.topLeft instanceof Tensor&&e.bottomRight instanceof Tensor){var i=tidy(function(){return[concat([slice(sub(t-1,e.topLeft),0,1),slice(e.topLeft,1,1)]),concat([sub(t-1,slice(e.bottomRight,0,1)),slice(e.bottomRight,1,1)])]});r=i[0],n=i[1],null!=e.landmarks&&(o=tidy(function(){var r=sub(tensor1d([t-1,0]),e.landmarks),n=tensor1d([1,-1]);return mul(r,n)}))}else{var a=e.topLeft,s=a[0],c=a[1],l=e.bottomRight,u=l[0],d=l[1];r=[t-1-s,c],n=[t-1-u,d],null!=e.landmarks&&(o=e.landmarks.map(function(e){return[t-1-e[0],e[1]]}))}var h={topLeft:r,bottomRight:n};return null!=o&&(h.landmarks=o),null!=e.probability&&(h.probability=e.probability instanceof Tensor?e.probability.clone():e.probability),h}function scaleBoxFromPrediction(e,t){return tidy(function(){var r;return r=e.hasOwnProperty("box")?e.box:e,squeeze(scaleBox(r,t).startEndTensor)})}var BlazeFaceModel=function(){function e(e,t,r,n,o,i){this.blazeFaceModel=e,this.width=t,this.height=r,this.maxFaces=n;var a={strides:[t/16,t/8],anchors:[2,6]};this.anchorsData=generateAnchors(t,r,a),this.anchors=tensor2d(this.anchorsData),this.inputSize=tensor1d([t,r]),this.iouThreshold=o,this.scoreThreshold=i}return e.prototype.resizeAspectRatio=function(e,t,r){var n=e.shape[2],o=e.shape[1];if(!t||!r)return{ratio:1,image:e};var i=Math.min(t/n,r/o),a=Math.round(n*i),s=Math.round(o*i);return{ratio:i,image:image.resizeBilinear(e,[s,a])}},e.prototype.getBoundingBoxes=function(e,t,r){return void 0===r&&(r=!0),__awaiter(this,void 0,void 0,function(){var n,o,i,a,s,c,l,u,d,h,f,p,b,m=this;return __generator(this,function(v){switch(v.label){case 0:return n=tidy(function(){var t=image.resizeBilinear(e,[m.width,m.height]),r=mul(sub(div(t,255),.5),2),n=m.blazeFaceModel.predict(r).sort(function(e,t){return e.size-t.size}),o=concat([n[0],n[2]],2),i=concat([n[1],n[3]],2),a=concat([i,o],1),s=squeeze(a),c=decodeBounds(s,m.anchors,m.inputSize),l=slice(s,[0,0],[-1,1]);return[s,c,squeeze(sigmoid(l)),1]}),o=n[0],i=n[1],a=n[2],s=n[3],c=console.warn,console.warn=function(){},l=image.nonMaxSuppression(i,a,this.maxFaces,this.iouThreshold,this.scoreThreshold),console.warn=c,[4,l.array()];case 1:return u=v.sent(),l.dispose(),d=u.map(function(e){return slice(i,[e,0],[1,-1])}),t?[3,3]:[4,Promise.all(d.map(function(e){return __awaiter(m,void 0,void 0,function(){var t;return __generator(this,function(r){switch(r.label){case 0:return[4,e.array()];case 1:return t=r.sent(),e.dispose(),[2,t]}})})}))];case 2:d=v.sent(),v.label=3;case 3:for(h=t?div([1,1],s):[1/s,1/s],f=[],p=function(e){var n=d[e],i=tidy(function(){var i=createBox(n instanceof Tensor?n:tensor2d(n));if(!r)return i;var s,c=u[e];return s=t?slice(m.anchors,[c,0],[1,2]):m.anchorsData[c],{box:i,landmarks:reshape(squeeze(slice(o,[c,NUM_LANDMARKS-1],[1,-1])),[NUM_LANDMARKS,-1]),probability:slice(a,[c],[1]),anchor:s}});f.push(i)},b=0;b<d.length;b++)p(b);return i.dispose(),a.dispose(),o.dispose(),[2,{boxes:f,scaleFactor:h}]}})})},e.prototype.estimateFaces=function(e,t,r,n){return void 0===t&&(t=!1),void 0===r&&(r=!1),void 0===n&&(n=!0),__awaiter(this,void 0,void 0,function(){var o,i,a,s,c,l,u=this;return __generator(this,function(d){switch(d.label){case 0:return o=getInputTensorDimensions(e),i=o[1],a=tidy(function(){return e instanceof Tensor||(e=browser.fromPixels(e)),expandDims(cast(e,"float32"),0)}),[4,this.getBoundingBoxes(a,t,n)];case 1:return s=d.sent(),c=s.boxes,l=s.scaleFactor,a.dispose(),t?[2,c.map(function(e){var t=scaleBoxFromPrediction(e,l),o={topLeft:slice(t,[0],[2]),bottomRight:slice(t,[2],[2])};if(n){var a=e,s=a.landmarks,c=a.probability,u=a.anchor,d=mul(add(s,u),l);o.landmarks=d,o.probability=c}return r&&(o=flipFaceHorizontal(o,i)),o})]:[2,Promise.all(c.map(function(e){return __awaiter(u,void 0,void 0,function(){var t,o,a,s,c,u,d,h,f,p,b,m=this;return __generator(this,function(v){switch(v.label){case 0:return t=scaleBoxFromPrediction(e,l),n?[3,2]:[4,t.array()];case 1:return c=v.sent(),o={topLeft:c.slice(0,2),bottomRight:c.slice(2)},[3,4];case 2:return[4,Promise.all([e.landmarks,t,e.probability].map(function(e){return __awaiter(m,void 0,void 0,function(){return __generator(this,function(t){return[2,e.array()]})})}))];case 3:a=v.sent(),s=a[0],c=a[1],u=a[2],d=e.anchor,f=(h=l)[0],p=h[1],b=s.map(function(e){return[(e[0]+d[0])*f,(e[1]+d[1])*p]}),o={topLeft:c.slice(0,2),bottomRight:c.slice(2),landmarks:b,probability:u},disposeBox(e.box),e.landmarks.dispose(),e.probability.dispose(),v.label=4;case 4:return t.dispose(),r&&(o=flipFaceHorizontal(o,i)),[2,o]}})})}))]}})})},e.prototype.dispose=function(){null!=this.blazeFaceModel&&this.blazeFaceModel.dispose()},e}(),BLAZEFACE_MODEL_URL="https://tfhub.dev/tensorflow/tfjs-model/blazeface/1/default/1";function load(e){var t=void 0===e?{}:e,r=t.maxFaces,n=void 0===r?10:r,o=t.inputWidth,i=void 0===o?128:o,a=t.inputHeight,s=void 0===a?128:a,c=t.iouThreshold,l=void 0===c?.3:c,u=t.scoreThreshold,d=void 0===u?.75:u,h=t.modelUrl;return __awaiter(this,void 0,void 0,function(){var e;return __generator(this,function(t){switch(t.label){case 0:return null==h?[3,2]:[4,loadGraphModel(h)];case 1:return e=t.sent(),[3,4];case 2:return[4,loadGraphModel(BLAZEFACE_MODEL_URL,{fromTFHub:!0})];case 3:e=t.sent(),t.label=4;case 4:return[2,new BlazeFaceModel(e,i,s,n,l,d)]}})})}export{load,BlazeFaceModel};