diff --git a/README.md b/README.md index 4055c666..ad254f8f 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Compatible with Browser, WebWorker and NodeJS execution! (and maybe with React-Native as it doesn't use any DOM objects) -*This is a pre-release project, see [issues](https://github.com/vladmandic/human/issues) for list of known limitations* +*This is a pre-release project, see [issues](https://github.com/vladmandic/human/issues) for list of known limitations and planned enhancements* *Suggestions are welcome!* @@ -124,8 +124,8 @@ And then use with: const human = require('@vladmandic/human'); // points to @vladmandic/human/dist/human.cjs ``` - Since NodeJS projects load `weights` from local filesystem instead of using `http` calls, you must modify default configuration to include correct paths with `file://` prefix + For example: ```js const config = { @@ -213,7 +213,6 @@ Note that user object and default configuration are merged using deep-merge, so Configurtion object is large, but typically you only need to modify few values: - `enabled`: Choose which models to use -- `skipFrames`: Must be set to 0 for static images - `modelPath`: Update as needed to reflect your application's relative path @@ -234,8 +233,9 @@ config = { inputSize: 256, // fixed value: 128 for front and 256 for 'back' maxFaces: 10, // maximum number of faces detected in the input, should be set to the minimum number for performance skipFrames: 10, // how many frames to go without re-running the face bounding box detector + // only used for video inputs, ignored for static inputs // if model is running st 25 FPS, we can re-use existing bounding box for updated face mesh analysis - // as face probably hasn't moved much in short time (10 * 1/25 = 0.25 sec) + // as the face probably hasn't moved much in short time (10 * 1/25 = 0.25 sec) minConfidence: 0.5, // threshold for discarding a prediction iouThreshold: 0.3, // threshold for deciding whether boxes overlap too much in non-maximum suppression scoreThreshold: 0.7, // threshold for deciding when to remove boxes based on score in non-maximum suppression @@ -256,7 +256,7 @@ config = { modelPath: '../models/ssrnet-age/imdb/model.json', // can be 'imdb' or 'wiki' // which determines training set for model inputSize: 64, // fixed value - skipFrames: 10, // how many frames to go without re-running the detector + skipFrames: 10, // how many frames to go without re-running the detector, only used for video inputs }, gender: { enabled: true, @@ -267,7 +267,7 @@ config = { enabled: true, inputSize: 64, // fixed value minConfidence: 0.5, // threshold for discarding a prediction - skipFrames: 10, // how many frames to go without re-running the detector + skipFrames: 10, // how many frames to go without re-running the detector, only used for video inputs useGrayscale: true, // convert image to grayscale before prediction or use highest channel modelPath: '../models/emotion/model.json', }, @@ -285,8 +285,9 @@ config = { enabled: true, inputSize: 256, // fixed value skipFrames: 10, // how many frames to go without re-running the hand bounding box detector + // only used for video inputs // if model is running st 25 FPS, we can re-use existing bounding box for updated hand skeleton analysis - // as face probably hasn't moved much in short time (10 * 1/25 = 0.25 sec) + // as the hand probably hasn't moved much in short time (10 * 1/25 = 0.25 sec) minConfidence: 0.5, // threshold for discarding a prediction iouThreshold: 0.3, // threshold for deciding whether boxes overlap too much in non-maximum suppression scoreThreshold: 0.7, // threshold for deciding when to remove boxes based on score in non-maximum suppression diff --git a/config.js b/config.js index eb49ce04..505d942a 100644 --- a/config.js +++ b/config.js @@ -16,7 +16,7 @@ export default { // 'front' is optimized for large faces such as front-facing camera and 'back' is optimized for distanct faces. inputSize: 256, // fixed value: 128 for front and 256 for 'back' maxFaces: 10, // maximum number of faces detected in the input, should be set to the minimum number for performance - skipFrames: 10, // how many frames to go without re-running the face bounding box detector + skipFrames: 10, // how many frames to go without re-running the face bounding box detector, only used for video inputs // if model is running st 25 FPS, we can re-use existing bounding box for updated face mesh analysis // as face probably hasn't moved much in short time (10 * 1/25 = 0.25 sec) minConfidence: 0.5, // threshold for discarding a prediction @@ -39,7 +39,7 @@ export default { modelPath: '../models/ssrnet-age/imdb/model.json', // can be 'imdb' or 'wiki' // which determines training set for model inputSize: 64, // fixed value - skipFrames: 10, // how many frames to go without re-running the detector + skipFrames: 10, // how many frames to go without re-running the detector, only used for video inputs }, gender: { enabled: true, @@ -67,9 +67,9 @@ export default { hand: { enabled: true, inputSize: 256, // fixed value - skipFrames: 10, // how many frames to go without re-running the hand bounding box detector + skipFrames: 10, // how many frames to go without re-running the hand bounding box detector, only used for video inputs // if model is running st 25 FPS, we can re-use existing bounding box for updated hand skeleton analysis - // as face probably hasn't moved much in short time (10 * 1/25 = 0.25 sec) + // as the hand probably hasn't moved much in short time (10 * 1/25 = 0.25 sec) minConfidence: 0.5, // threshold for discarding a prediction iouThreshold: 0.3, // threshold for deciding whether boxes overlap too much in non-maximum suppression scoreThreshold: 0.7, // threshold for deciding when to remove boxes based on score in non-maximum suppression diff --git a/demo/browser.js b/demo/browser.js index 82c342bd..4e2a0d39 100644 --- a/demo/browser.js +++ b/demo/browser.js @@ -180,12 +180,6 @@ function runHumanDetect(input, canvas) { // main processing function when input is image, can use direct invocation or web worker async function processImage(input) { - // must be zero for images - config.face.detector.skipFrames = 0; - config.face.emotion.skipFrames = 0; - config.face.age.skipFrames = 0; - config.hand.skipFrames = 0; - timeStamp = performance.now(); return new Promise((resolve) => { const image = document.getElementById('image'); @@ -234,7 +228,7 @@ async function detectVideo() { // just initialize everything and call main function async function detectSampleImages() { - ui.baseFont = ui.baseFontProto.replace(/{size}/, `${ui.columns}rem`); + ui.baseFont = ui.baseFontProto.replace(/{size}/, `${1.2 * ui.columns}rem`); ui.baseLineHeight = ui.baseLineHeightProto * ui.columns; document.getElementById('canvas').style.display = 'none'; document.getElementById('samples').style.display = 'block'; @@ -244,6 +238,7 @@ async function detectSampleImages() { function setupMenu() { menu = new Menu(document.body); + menu.addTitle('...'); menu.addButton('Start Video', 'Pause Video', (evt) => detectVideo(evt)); menu.addButton('Process Images', 'Process Images', () => detectSampleImages()); @@ -297,7 +292,6 @@ function setupMenu() { menu.addBool('Fill Polygons', ui, 'fillPolygons'); menu.addHTML('
1?ph(r,[1,a]):r}):i.cell.stateSize>1?[ph(r,[1,i.cell.stateSize])]:[r]})},Object.defineProperty(t.prototype,"trainableWeights",{get:function(){return this.trainable?this.cell.trainableWeights:[]},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"nonTrainableWeights",{get:function(){return this.trainable?this.cell.nonTrainableWeights:this.cell.weights},enumerable:!0,configurable:!0}),t.prototype.setFastWeightInitDuringBuild=function(e){n.prototype.setFastWeightInitDuringBuild.call(this,e),this.cell!=null&&this.cell.setFastWeightInitDuringBuild(e)},t.prototype.getConfig=function(){var e=n.prototype.getConfig.call(this),i={returnSequences:this.returnSequences,returnState:this.returnState,goBackwards:this.goBackwards,stateful:this.stateful,unroll:this.unroll};this.numConstants!=null&&(i.numConstants=this.numConstants);var r=this.cell.getConfig();return this.getClassName()===t.className&&(i.cell={className:this.cell.getClassName(),config:r}),Yt({},r,e,i)},t.fromConfig=function(e,i,r){r===void 0&&(r={});var a=i.cell,s=wn(a,r);return new e(Object.assign(i,{cell:s}))},t.className="RNN",t}(De);y.serialization.registerClass(Ii);var $r=function(n){Q(t,n);function t(){return n!==null&&n.apply(this,arguments)||this}return t}(De),Yh=function(n){Q(t,n);function t(e){var i=n.call(this,e)||this;return i.DEFAULT_ACTIVATION="tanh",i.DEFAULT_KERNEL_INITIALIZER="glorotNormal",i.DEFAULT_RECURRENT_INITIALIZER="orthogonal",i.DEFAULT_BIAS_INITIALIZER="zeros",i.units=e.units,Tt(i.units,"units"),i.activation=Li(e.activation==null?i.DEFAULT_ACTIVATION:e.activation),i.useBias=e.useBias==null?!0:e.useBias,i.kernelInitializer=tt(e.kernelInitializer||i.DEFAULT_KERNEL_INITIALIZER),i.recurrentInitializer=tt(e.recurrentInitializer||i.DEFAULT_RECURRENT_INITIALIZER),i.biasInitializer=tt(e.biasInitializer||i.DEFAULT_BIAS_INITIALIZER),i.kernelRegularizer=nt(e.kernelRegularizer),i.recurrentRegularizer=nt(e.recurrentRegularizer),i.biasRegularizer=nt(e.biasRegularizer),i.kernelConstraint=bt(e.kernelConstraint),i.recurrentConstraint=bt(e.recurrentConstraint),i.biasConstraint=bt(e.biasConstraint),i.dropout=qr([1,yi([0,e.dropout==null?0:e.dropout])]),i.recurrentDropout=qr([1,yi([0,e.recurrentDropout==null?0:e.recurrentDropout])]),i.stateSize=i.units,i.dropoutMask=null,i.recurrentDropoutMask=null,i}return t.prototype.build=function(e){e=Ke(e),this.kernel=this.addWeight("kernel",[e[e.length-1],this.units],null,this.kernelInitializer,this.kernelRegularizer,!0,this.kernelConstraint),this.recurrentKernel=this.addWeight("recurrent_kernel",[this.units,this.units],null,this.recurrentInitializer,this.recurrentRegularizer,!0,this.recurrentConstraint),this.useBias?this.bias=this.addWeight("bias",[this.units],null,this.biasInitializer,this.biasRegularizer,!0,this.biasConstraint):this.bias=null,this.built=!0},t.prototype.call=function(e,i){var r=this;return y.tidy(function(){if(e=e,e.length!==2)throw new M("SimpleRNNCell expects 2 input Tensors, got "+e.length+".");var a=e[1];e=e[0];var s=i.training==null?!1:i.training;0i){s=r-i;for(var o=[],l=0;l0){var d=void 0;i>r?d=i+r-3:d=i-1;for(var p=[],l=d;l