switched face embedding to mobileface

pull/91/head
Vladimir Mandic 2021-03-12 12:54:08 -05:00
parent ce92c60b54
commit ea5c9b9be6
33 changed files with 1561 additions and 1619 deletions

View File

@ -1,6 +1,6 @@
# @vladmandic/human
Version: **1.0.3**
Version: **1.1.0**
Description: **Human: AI-powered 3D Face Detection, Face Embedding & Recognition, Body Pose Tracking, Hand & Finger Tracking, Iris Analysis, Age & Gender & Emotion Prediction & Gesture Recognition**
Author: **Vladimir Mandic <mandic00@live.com>**
@ -11,14 +11,16 @@ Repository: **<git+https://github.com/vladmandic/human.git>**
### **HEAD -> main** 2021/03/11 mandic00@live.com
### **1.0.4** 2021/03/11 mandic00@live.com
- add face return tensor
- add test for face descriptors
- wip on embedding
- simplify face box coordinate calculations
- annotated models and removed gender-ssrnet
- autodetect inputsizes
### **origin/main** 2021/03/10 mandic00@live.com
### **1.0.3** 2021/03/10 mandic00@live.com
- strong typing for public classes and hide private classes

View File

@ -69,7 +69,7 @@ Default models in Human library are:
- **Gender Detection**: Oarriaga Gender
- **Age Detection**: SSR-Net Age IMDB
- **Body Analysis**: PoseNet
- **Face Embedding**: Sirius-AI MobileFaceNet Embedding
- **Face Embedding**: BecauseofAI MobileFace Embedding
Note that alternative models are provided and can be enabled via configuration
For example, `PoseNet` model can be switched for `BlazePose` model depending on the use case

10
TODO.md
View File

@ -8,3 +8,13 @@
- Explore EfficientPose
<https://github.com/daniegr/EfficientPose>
<https://github.com/PINTO0309/PINTO_model_zoo/tree/main/084_EfficientPose>
## WiP: Embedding
- Implement offsetRaw
full with and without rotation
full with and without embedding
full with any without mesh
embedding with and without mesh
boxRaw and meshRaw with and without mesh

View File

@ -121,9 +121,9 @@ export default {
},
embedding: {
enabled: false, // to improve accuracy of face embedding extraction it is recommended
// to enable detector.rotation and mesh.enabled
modelPath: '../models/mobilefacenet.json',
enabled: false, // to improve accuracy of face embedding extraction it is
// highly recommended to enable detector.rotation and mesh.enabled
modelPath: '../models/mobileface.json',
},
},

View File

@ -84,10 +84,21 @@ let original;
async function calcSimmilariry(result) {
document.getElementById('compare-container').style.display = human.config.face.embedding.enabled ? 'block' : 'none';
if (!human.config.face.embedding.enabled) return;
if (!(result?.face?.length > 0) || (result?.face[0]?.embedding?.length !== 192)) return;
if (!(result?.face?.length > 0) || (result?.face[0]?.embedding?.length !== 256)) return;
if (!original) {
original = result;
document.getElementById('compare-canvas').getContext('2d').drawImage(original.canvas, 0, 0, 200, 200);
if (result.face[0].tensor) {
const enhanced = human.enhance(result.face[0]);
if (enhanced) {
const c = document.getElementById('orig');
const squeeze = enhanced.squeeze();
human.tf.browser.toPixels(squeeze, c);
enhanced.dispose();
squeeze.dispose();
}
} else {
document.getElementById('compare-canvas').getContext('2d').drawImage(original.canvas, 0, 0, 200, 200);
}
}
const simmilarity = human.simmilarity(original?.face[0]?.embedding, result?.face[0]?.embedding);
document.getElementById('simmilarity').innerText = `simmilarity: ${Math.trunc(1000 * simmilarity) / 10}%`;

View File

@ -26,9 +26,9 @@
<body>
<br>Sample Images:
<div id="images"></div>
<br>Selected Face<br>
<br>Selected Face (Enhanced)<br>
<canvas id="orig" style="width: 200px; height: 200px;"></canvas>
<br>Extracted Faces - click on a face to sort by simmilarity:<br>
<br><br>Extracted Faces - click on a face to sort by simmilarity:<br>
<div id="faces"></div>
</body>
</html>

View File

@ -11,7 +11,7 @@ const userConfig = {
enabled: true,
detector: { rotation: true, return: true },
mesh: { enabled: true },
embedding: { enabled: true, modelPath: '../models/mobilefacenet.json' },
embedding: { enabled: true },
iris: { enabled: false },
age: { enabled: false },
gender: { enabled: false },
@ -21,12 +21,15 @@ const userConfig = {
gesture: { enabled: false },
body: { enabled: false },
};
const human = new Human(userConfig);
const human = new Human(userConfig); // new instance of human
const samples = ['../assets/sample-me.jpg', '../assets/sample6.jpg', '../assets/sample1.jpg', '../assets/sample4.jpg', '../assets/sample5.jpg', '../assets/sample3.jpg', '../assets/sample2.jpg'];
// const samples = ['../assets/sample-me.jpg', '../assets/sample6.jpg', '../assets/sample1.jpg', '../assets/sample4.jpg', '../assets/sample5.jpg', '../assets/sample3.jpg', '../assets/sample2.jpg',
// '../private/me (1).jpg', '../private/me (2).jpg', '../private/me (3).jpg', '../private/me (4).jpg', '../private/me (5).jpg', '../private/me (6).jpg', '../private/me (7).jpg', '../private/me (8).jpg',
// '../private/me (9).jpg', '../private/me (10).jpg', '../private/me (11).jpg', '../private/me (12).jpg', '../private/me (13).jpg'];
const all = [];
// '../private/me (1).jpg', '../private/me (2).jpg', '../private/me (3).jpg', '../private/me (4).jpg', '../private/me (5).jpg', '../private/me (6).jpg', '../private/me (7).jpg', '../private/me (8).jpg',
// '../private/me (9).jpg', '../private/me (10).jpg', '../private/me (11).jpg', '../private/me (12).jpg', '../private/me (13).jpg'];
const all = []; // array that will hold all detected faces
function log(...msg) {
const dt = new Date();
@ -38,14 +41,24 @@ function log(...msg) {
async function analyze(face) {
log('Face:', face);
const box = [[0.05, 0.15, 0.90, 0.85]]; // top, left, bottom, right
const crop = human.tf.image.cropAndResize(face.tensor.expandDims(0), box, [0], [200, 200]); // optionally do a tight box crop
const c = document.getElementById('orig');
human.tf.browser.toPixels(crop.squeeze(), c);
// if we have face image tensor, enhance it and display it
if (face.tensor) {
const enhanced = human.enhance(face);
if (enhanced) {
const c = document.getElementById('orig');
const squeeze = enhanced.squeeze();
human.tf.browser.toPixels(squeeze, c);
enhanced.dispose();
squeeze.dispose();
}
}
// loop through all canvases that contain faces
const canvases = document.getElementsByClassName('face');
for (const canvas of canvases) {
// calculate simmilarity from selected face to current one in the loop
const res = human.simmilarity(face.embedding, all[canvas.tag.sample][canvas.tag.face].embedding);
// draw the canvas and simmilarity score
canvas.title = res;
await human.tf.browser.toPixels(all[canvas.tag.sample][canvas.tag.face].tensor, canvas);
const ctx = canvas.getContext('2d');
@ -55,6 +68,8 @@ async function analyze(face) {
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
ctx.fillText(`${(100 * res).toFixed(1)}%`, 4, 20);
}
// sort all faces by simmilarity
const sorted = document.getElementById('faces');
[...sorted.children]
.sort((a, b) => parseFloat(b.title) - parseFloat(a.title))
@ -70,12 +85,16 @@ async function faces(index, res) {
canvas.width = 200;
canvas.height = 200;
canvas.className = 'face';
// mouse click on any face canvas triggers analysis
canvas.addEventListener('click', (evt) => {
log('Select:', 'Image:', evt.target.tag.sample, 'Face:', evt.target.tag.face);
analyze(all[evt.target.tag.sample][evt.target.tag.face]);
});
human.tf.browser.toPixels(res.face[i].tensor, canvas);
document.getElementById('faces').appendChild(canvas);
// if we actually got face image tensor, draw canvas with that face
if (res.face[i].tensor) {
human.tf.browser.toPixels(res.face[i].tensor, canvas);
document.getElementById('faces').appendChild(canvas);
}
}
}
@ -83,9 +102,9 @@ async function add(index) {
log('Add image:', samples[index]);
return new Promise((resolve) => {
const img = new Image(100, 100);
img.onload = () => {
human.detect(img).then((res) => faces(index, res));
document.getElementById('images').appendChild(img);
img.onload = () => { // must wait until image is loaded
human.detect(img).then((res) => faces(index, res)); // then wait until image is analyzed
document.getElementById('images').appendChild(img); // and finally we can add it
resolve(true);
};
img.title = samples[index];
@ -95,7 +114,7 @@ async function add(index) {
async function main() {
await human.load();
for (const i in samples) await add(i);
for (const i in samples) await add(i); // download and analyze all images
log('Ready');
}

View File

@ -1,8 +1,10 @@
const log = require('@vladmandic/pilogger');
const fs = require('fs');
const process = require('process');
// for Node, `tfjs-node` or `tfjs-node-gpu` should be loaded before using Human
// for NodeJS, `tfjs-node` or `tfjs-node-gpu` should be loaded before using Human
const tf = require('@tensorflow/tfjs-node'); // or const tf = require('@tensorflow/tfjs-node-gpu');
// load specific version of Human library that matches TensorFlow mode
const Human = require('../dist/human.node.js').default; // or const Human = require('../dist/human.node-gpu.js').default;
@ -15,15 +17,16 @@ const myConfig = {
async: false,
face: {
enabled: true,
detector: { modelPath: 'file://models/blazeface-back.json', enabled: true },
detector: { modelPath: 'file://models/blazeface-back.json', enabled: true, rotation: false },
mesh: { modelPath: 'file://models/facemesh.json', enabled: true },
iris: { modelPath: 'file://models/iris.json', enabled: true },
age: { modelPath: 'file://models/age.json', enabled: true },
gender: { modelPath: 'file://models/gender.json', enabled: true },
emotion: { modelPath: 'file://models/emotion.json', enabled: true },
embedding: { modelPath: 'file://models/mobileface.json', enabled: true },
},
// body: { modelPath: 'file://models/blazepose.json', modelType: 'blazepose', enabled: true },
body: { modelPath: 'file://models/posenet.json', modelType: 'posenet', enabled: true },
// body: { modelPath: 'file://models/blazepose.json', enabled: true },
body: { modelPath: 'file://models/posenet.json', enabled: true },
hand: {
enabled: true,
detector: { modelPath: 'file://models/handdetect.json' },

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

742
dist/human.esm.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

742
dist/human.js vendored

File diff suppressed because one or more lines are too long

4
dist/human.js.map vendored

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

18
dist/human.node.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
models/mobileface.bin Normal file

Binary file not shown.

202
models/mobileface.json Normal file
View File

@ -0,0 +1,202 @@
{
"format": "graph-model",
"generatedBy": "2.4.1",
"convertedBy": "https://github.com/vladmandic",
"signature":
{
"inputs":
{
"data:0": {"name":"data:0","dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"-1"},{"size":"112"},{"size":"112"},{"size":"3"}]}}
},
"outputs":
{
"batchnorm0/add_1:0": {"name":"batchnorm0/add_1:0","dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"-1"},{"size":"256"}]}}
}
},
"modelTopology":
{
"node":
[
{"name":"Maximum_12/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"stage4_unit1_prelu2_gamma","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"128"}]}}}}},
{"name":"Minimum_12/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Maximum_11/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"stage4_unit1_prelu1_gamma","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"128"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Minimum_11/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Pad_11/paddings","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}},"dtype":{"type":"DT_INT32"}}},
{"name":"stage4_unit1_conv1_df","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"128"},{"size":"1"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Pad_12/paddings","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}},"dtype":{"type":"DT_INT32"}}},
{"name":"stage4_unit1_conv2_df","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"128"},{"size":"1"}]}}}}},
{"name":"Maximum_10/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"stage4_unit1_prelu0_gamma","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"128"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Minimum_10/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"Maximum_9/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"stage3_unit1_prelu2_gamma","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"64"}]}}}}},
{"name":"Minimum_9/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"Maximum_8/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"stage3_unit1_prelu1_gamma","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"64"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Minimum_8/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Pad_8/paddings","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}},"dtype":{"type":"DT_INT32"}}},
{"name":"stage3_unit1_conv1_df","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"64"},{"size":"1"}]}}}}},
{"name":"Pad_9/paddings","op":"Const","attr":{"dtype":{"type":"DT_INT32"},"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}}}},
{"name":"stage3_unit1_conv2_df","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"64"},{"size":"1"}]}}}}},
{"name":"Maximum_7/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"stage3_unit1_prelu0_gamma","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"64"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Minimum_7/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"Maximum_6/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"stage2_unit1_prelu2_gamma","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"32"}]}}}}},
{"name":"Minimum_6/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Maximum_5/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"stage2_unit1_prelu1_gamma","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"32"}]}}}}},
{"name":"Minimum_5/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"Pad_5/paddings","op":"Const","attr":{"dtype":{"type":"DT_INT32"},"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}}}},
{"name":"stage2_unit1_conv1_df","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"32"},{"size":"1"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Pad_6/paddings","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}},"dtype":{"type":"DT_INT32"}}},
{"name":"stage2_unit1_conv2_df","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"32"},{"size":"1"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Maximum_4/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"stage2_unit1_prelu0_gamma","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"32"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Minimum_4/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"Maximum_3/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"stage1_unit1_prelu2_gamma","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"32"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Minimum_3/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Maximum_2/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"stage1_unit1_prelu1_gamma","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"32"}]}}}}},
{"name":"Minimum_2/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Pad_2/paddings","op":"Const","attr":{"dtype":{"type":"DT_INT32"},"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}}}},
{"name":"stage1_unit1_conv1_df","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"32"},{"size":"1"}]}}}}},
{"name":"Pad_3/paddings","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}},"dtype":{"type":"DT_INT32"}}},
{"name":"stage1_unit1_conv2_df","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"32"},{"size":"1"}]}}}}},
{"name":"Maximum_1/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"stage1_unit1_prelu0_gamma","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"32"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Minimum_1/x","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}}}},
{"name":"Maximum/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"prelu1_gamma","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"32"}]}}}}},
{"name":"Minimum/x","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"minusscalar0_second","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"1"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"mulscalar0_second","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"1"}]}}}}},
{"name":"Pad/paddings","op":"Const","attr":{"dtype":{"type":"DT_INT32"},"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}}}},
{"name":"conv1_weight","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"3"},{"size":"32"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"stage1_unit1_conv0_0_weight","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"1"},{"size":"1"},{"size":"32"},{"size":"32"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Pad_1/paddings","op":"Const","attr":{"dtype":{"type":"DT_INT32"},"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}}}},
{"name":"stage1_unit1_conv0_1_df","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"32"},{"size":"1"}]}}}}},
{"name":"stage2_unit1_conv0_0_weight","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"1"},{"size":"1"},{"size":"32"},{"size":"32"}]}}}}},
{"name":"Pad_4/paddings","op":"Const","attr":{"dtype":{"type":"DT_INT32"},"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}}}},
{"name":"stage2_unit1_conv0_1_df","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"32"},{"size":"1"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"stage3_unit1_conv0_0_weight","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"1"},{"size":"1"},{"size":"32"},{"size":"64"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Pad_7/paddings","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}},"dtype":{"type":"DT_INT32"}}},
{"name":"stage3_unit1_conv0_1_df","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"64"},{"size":"1"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"stage4_unit1_conv0_0_weight","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"1"},{"size":"1"},{"size":"64"},{"size":"128"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"Pad_10/paddings","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"4"},{"size":"2"}]}}},"dtype":{"type":"DT_INT32"}}},
{"name":"stage4_unit1_conv0_1_df","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"3"},{"size":"3"},{"size":"128"},{"size":"1"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"flatten/Const","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_INT32","tensorShape":{"dim":[{"size":"2"}]}}},"dtype":{"type":"DT_INT32"}}},
{"name":"dense/kernel","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"2048"},{"size":"256"}]}}}}},
{"name":"batchnorm0/mul","op":"Const","attr":{"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"256"}]}}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"batchnorm0/sub","op":"Const","attr":{"dtype":{"type":"DT_FLOAT"},"value":{"tensor":{"dtype":"DT_FLOAT","tensorShape":{"dim":[{"size":"256"}]}}}}},
{"name":"data","op":"Placeholder","attr":{"shape":{"shape":{"dim":[{"size":"-1"},{"size":"112"},{"size":"112"},{"size":"3"}]}},"dtype":{"type":"DT_FLOAT"}}},
{"name":"sub","op":"Sub","input":["data","minusscalar0_second"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul","op":"Mul","input":["sub","mulscalar0_second"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Pad","op":"Pad","input":["mul","Pad/paddings"],"attr":{"Tpaddings":{"type":"DT_INT32"},"T":{"type":"DT_FLOAT"}}},
{"name":"conv1","op":"Conv2D","input":["Pad","conv1_weight"],"device":"/device:CPU:0","attr":{"padding":{"s":"VkFMSUQ="},"use_cudnn_on_gpu":{"b":true},"dilations":{"list":{"i":["1","1","1","1"]}},"strides":{"list":{"i":["1","2","2","1"]}},"data_format":{"s":"TkhXQw=="},"T":{"type":"DT_FLOAT"},"explicit_paddings":{"list":{}}}},
{"name":"Maximum","op":"Maximum","input":["Maximum/x","conv1"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum","op":"Minimum","input":["Minimum/x","conv1"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_1","op":"Mul","input":["prelu1_gamma","Minimum"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add","op":"AddV2","input":["Maximum","mul_1"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"stage1_unit1_conv0_0","op":"Conv2D","input":["add","stage1_unit1_conv0_0_weight"],"device":"/device:CPU:0","attr":{"dilations":{"list":{"i":["1","1","1","1"]}},"use_cudnn_on_gpu":{"b":true},"T":{"type":"DT_FLOAT"},"explicit_paddings":{"list":{}},"padding":{"s":"VkFMSUQ="},"data_format":{"s":"TkhXQw=="},"strides":{"list":{"i":["1","1","1","1"]}}}},
{"name":"Pad_1","op":"Pad","input":["stage1_unit1_conv0_0","Pad_1/paddings"],"attr":{"T":{"type":"DT_FLOAT"},"Tpaddings":{"type":"DT_INT32"}}},
{"name":"depthwise","op":"DepthwiseConv2dNative","input":["Pad_1","stage1_unit1_conv0_1_df"],"attr":{"dilations":{"list":{"i":["1","1","1","1"]}},"explicit_paddings":{"list":{}},"padding":{"s":"VkFMSUQ="},"data_format":{"s":"TkhXQw=="},"strides":{"list":{"i":["1","2","2","1"]}},"T":{"type":"DT_FLOAT"}}},
{"name":"Maximum_1","op":"Maximum","input":["Maximum_1/x","depthwise"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_1","op":"Minimum","input":["Minimum_1/x","depthwise"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_2","op":"Mul","input":["stage1_unit1_prelu0_gamma","Minimum_1"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_1","op":"AddV2","input":["Maximum_1","mul_2"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Pad_2","op":"Pad","input":["add_1","Pad_2/paddings"],"attr":{"Tpaddings":{"type":"DT_INT32"},"T":{"type":"DT_FLOAT"}}},
{"name":"depthwise_1","op":"DepthwiseConv2dNative","input":["Pad_2","stage1_unit1_conv1_df"],"attr":{"dilations":{"list":{"i":["1","1","1","1"]}},"explicit_paddings":{"list":{}},"T":{"type":"DT_FLOAT"},"padding":{"s":"VkFMSUQ="},"data_format":{"s":"TkhXQw=="},"strides":{"list":{"i":["1","1","1","1"]}}}},
{"name":"Maximum_2","op":"Maximum","input":["Maximum_2/x","depthwise_1"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_2","op":"Minimum","input":["Minimum_2/x","depthwise_1"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_3","op":"Mul","input":["stage1_unit1_prelu1_gamma","Minimum_2"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_2","op":"AddV2","input":["Maximum_2","mul_3"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Pad_3","op":"Pad","input":["add_2","Pad_3/paddings"],"attr":{"Tpaddings":{"type":"DT_INT32"},"T":{"type":"DT_FLOAT"}}},
{"name":"depthwise_2","op":"DepthwiseConv2dNative","input":["Pad_3","stage1_unit1_conv2_df"],"attr":{"data_format":{"s":"TkhXQw=="},"padding":{"s":"VkFMSUQ="},"dilations":{"list":{"i":["1","1","1","1"]}},"strides":{"list":{"i":["1","1","1","1"]}},"T":{"type":"DT_FLOAT"},"explicit_paddings":{"list":{}}}},
{"name":"Maximum_3","op":"Maximum","input":["Maximum_3/x","depthwise_2"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_3","op":"Minimum","input":["Minimum_3/x","depthwise_2"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_4","op":"Mul","input":["stage1_unit1_prelu2_gamma","Minimum_3"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_3","op":"AddV2","input":["Maximum_3","mul_4"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_4","op":"AddV2","input":["add_3","add_1"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"stage2_unit1_conv0_0","op":"Conv2D","input":["add_4","stage2_unit1_conv0_0_weight"],"device":"/device:CPU:0","attr":{"T":{"type":"DT_FLOAT"},"strides":{"list":{"i":["1","1","1","1"]}},"use_cudnn_on_gpu":{"b":true},"explicit_paddings":{"list":{}},"padding":{"s":"VkFMSUQ="},"dilations":{"list":{"i":["1","1","1","1"]}},"data_format":{"s":"TkhXQw=="}}},
{"name":"Pad_4","op":"Pad","input":["stage2_unit1_conv0_0","Pad_4/paddings"],"attr":{"T":{"type":"DT_FLOAT"},"Tpaddings":{"type":"DT_INT32"}}},
{"name":"depthwise_3","op":"DepthwiseConv2dNative","input":["Pad_4","stage2_unit1_conv0_1_df"],"attr":{"padding":{"s":"VkFMSUQ="},"T":{"type":"DT_FLOAT"},"strides":{"list":{"i":["1","2","2","1"]}},"data_format":{"s":"TkhXQw=="},"dilations":{"list":{"i":["1","1","1","1"]}},"explicit_paddings":{"list":{}}}},
{"name":"Maximum_4","op":"Maximum","input":["Maximum_4/x","depthwise_3"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_4","op":"Minimum","input":["Minimum_4/x","depthwise_3"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_5","op":"Mul","input":["stage2_unit1_prelu0_gamma","Minimum_4"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_5","op":"AddV2","input":["Maximum_4","mul_5"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Pad_5","op":"Pad","input":["add_5","Pad_5/paddings"],"attr":{"T":{"type":"DT_FLOAT"},"Tpaddings":{"type":"DT_INT32"}}},
{"name":"depthwise_4","op":"DepthwiseConv2dNative","input":["Pad_5","stage2_unit1_conv1_df"],"attr":{"dilations":{"list":{"i":["1","1","1","1"]}},"strides":{"list":{"i":["1","1","1","1"]}},"T":{"type":"DT_FLOAT"},"explicit_paddings":{"list":{}},"padding":{"s":"VkFMSUQ="},"data_format":{"s":"TkhXQw=="}}},
{"name":"Maximum_5","op":"Maximum","input":["Maximum_5/x","depthwise_4"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_5","op":"Minimum","input":["Minimum_5/x","depthwise_4"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_6","op":"Mul","input":["stage2_unit1_prelu1_gamma","Minimum_5"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_6","op":"AddV2","input":["Maximum_5","mul_6"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Pad_6","op":"Pad","input":["add_6","Pad_6/paddings"],"attr":{"T":{"type":"DT_FLOAT"},"Tpaddings":{"type":"DT_INT32"}}},
{"name":"depthwise_5","op":"DepthwiseConv2dNative","input":["Pad_6","stage2_unit1_conv2_df"],"attr":{"dilations":{"list":{"i":["1","1","1","1"]}},"data_format":{"s":"TkhXQw=="},"T":{"type":"DT_FLOAT"},"strides":{"list":{"i":["1","1","1","1"]}},"explicit_paddings":{"list":{}},"padding":{"s":"VkFMSUQ="}}},
{"name":"Maximum_6","op":"Maximum","input":["Maximum_6/x","depthwise_5"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_6","op":"Minimum","input":["Minimum_6/x","depthwise_5"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_7","op":"Mul","input":["stage2_unit1_prelu2_gamma","Minimum_6"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_7","op":"AddV2","input":["Maximum_6","mul_7"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_8","op":"AddV2","input":["add_7","add_5"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"stage3_unit1_conv0_0","op":"Conv2D","input":["add_8","stage3_unit1_conv0_0_weight"],"device":"/device:CPU:0","attr":{"use_cudnn_on_gpu":{"b":true},"strides":{"list":{"i":["1","1","1","1"]}},"padding":{"s":"VkFMSUQ="},"T":{"type":"DT_FLOAT"},"data_format":{"s":"TkhXQw=="},"explicit_paddings":{"list":{}},"dilations":{"list":{"i":["1","1","1","1"]}}}},
{"name":"Pad_7","op":"Pad","input":["stage3_unit1_conv0_0","Pad_7/paddings"],"attr":{"Tpaddings":{"type":"DT_INT32"},"T":{"type":"DT_FLOAT"}}},
{"name":"depthwise_6","op":"DepthwiseConv2dNative","input":["Pad_7","stage3_unit1_conv0_1_df"],"attr":{"explicit_paddings":{"list":{}},"padding":{"s":"VkFMSUQ="},"T":{"type":"DT_FLOAT"},"dilations":{"list":{"i":["1","1","1","1"]}},"data_format":{"s":"TkhXQw=="},"strides":{"list":{"i":["1","2","2","1"]}}}},
{"name":"Maximum_7","op":"Maximum","input":["Maximum_7/x","depthwise_6"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_7","op":"Minimum","input":["Minimum_7/x","depthwise_6"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_8","op":"Mul","input":["stage3_unit1_prelu0_gamma","Minimum_7"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_9","op":"AddV2","input":["Maximum_7","mul_8"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Pad_8","op":"Pad","input":["add_9","Pad_8/paddings"],"attr":{"Tpaddings":{"type":"DT_INT32"},"T":{"type":"DT_FLOAT"}}},
{"name":"depthwise_7","op":"DepthwiseConv2dNative","input":["Pad_8","stage3_unit1_conv1_df"],"attr":{"strides":{"list":{"i":["1","1","1","1"]}},"dilations":{"list":{"i":["1","1","1","1"]}},"explicit_paddings":{"list":{}},"padding":{"s":"VkFMSUQ="},"T":{"type":"DT_FLOAT"},"data_format":{"s":"TkhXQw=="}}},
{"name":"Maximum_8","op":"Maximum","input":["Maximum_8/x","depthwise_7"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_8","op":"Minimum","input":["Minimum_8/x","depthwise_7"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_9","op":"Mul","input":["stage3_unit1_prelu1_gamma","Minimum_8"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_10","op":"AddV2","input":["Maximum_8","mul_9"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Pad_9","op":"Pad","input":["add_10","Pad_9/paddings"],"attr":{"T":{"type":"DT_FLOAT"},"Tpaddings":{"type":"DT_INT32"}}},
{"name":"depthwise_8","op":"DepthwiseConv2dNative","input":["Pad_9","stage3_unit1_conv2_df"],"attr":{"data_format":{"s":"TkhXQw=="},"strides":{"list":{"i":["1","1","1","1"]}},"explicit_paddings":{"list":{}},"T":{"type":"DT_FLOAT"},"padding":{"s":"VkFMSUQ="},"dilations":{"list":{"i":["1","1","1","1"]}}}},
{"name":"Maximum_9","op":"Maximum","input":["Maximum_9/x","depthwise_8"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_9","op":"Minimum","input":["Minimum_9/x","depthwise_8"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_10","op":"Mul","input":["stage3_unit1_prelu2_gamma","Minimum_9"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_11","op":"AddV2","input":["Maximum_9","mul_10"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_12","op":"AddV2","input":["add_11","add_9"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"stage4_unit1_conv0_0","op":"Conv2D","input":["add_12","stage4_unit1_conv0_0_weight"],"device":"/device:CPU:0","attr":{"strides":{"list":{"i":["1","1","1","1"]}},"padding":{"s":"VkFMSUQ="},"T":{"type":"DT_FLOAT"},"data_format":{"s":"TkhXQw=="},"use_cudnn_on_gpu":{"b":true},"dilations":{"list":{"i":["1","1","1","1"]}},"explicit_paddings":{"list":{}}}},
{"name":"Pad_10","op":"Pad","input":["stage4_unit1_conv0_0","Pad_10/paddings"],"attr":{"T":{"type":"DT_FLOAT"},"Tpaddings":{"type":"DT_INT32"}}},
{"name":"depthwise_9","op":"DepthwiseConv2dNative","input":["Pad_10","stage4_unit1_conv0_1_df"],"attr":{"data_format":{"s":"TkhXQw=="},"padding":{"s":"VkFMSUQ="},"dilations":{"list":{"i":["1","1","1","1"]}},"explicit_paddings":{"list":{}},"T":{"type":"DT_FLOAT"},"strides":{"list":{"i":["1","2","2","1"]}}}},
{"name":"Maximum_10","op":"Maximum","input":["Maximum_10/x","depthwise_9"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_10","op":"Minimum","input":["Minimum_10/x","depthwise_9"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_11","op":"Mul","input":["stage4_unit1_prelu0_gamma","Minimum_10"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_13","op":"AddV2","input":["Maximum_10","mul_11"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Pad_11","op":"Pad","input":["add_13","Pad_11/paddings"],"attr":{"T":{"type":"DT_FLOAT"},"Tpaddings":{"type":"DT_INT32"}}},
{"name":"depthwise_10","op":"DepthwiseConv2dNative","input":["Pad_11","stage4_unit1_conv1_df"],"attr":{"data_format":{"s":"TkhXQw=="},"padding":{"s":"VkFMSUQ="},"T":{"type":"DT_FLOAT"},"explicit_paddings":{"list":{}},"strides":{"list":{"i":["1","1","1","1"]}},"dilations":{"list":{"i":["1","1","1","1"]}}}},
{"name":"Maximum_11","op":"Maximum","input":["Maximum_11/x","depthwise_10"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_11","op":"Minimum","input":["Minimum_11/x","depthwise_10"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_12","op":"Mul","input":["stage4_unit1_prelu1_gamma","Minimum_11"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_14","op":"AddV2","input":["Maximum_11","mul_12"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Pad_12","op":"Pad","input":["add_14","Pad_12/paddings"],"attr":{"T":{"type":"DT_FLOAT"},"Tpaddings":{"type":"DT_INT32"}}},
{"name":"depthwise_11","op":"DepthwiseConv2dNative","input":["Pad_12","stage4_unit1_conv2_df"],"attr":{"dilations":{"list":{"i":["1","1","1","1"]}},"strides":{"list":{"i":["1","1","1","1"]}},"T":{"type":"DT_FLOAT"},"explicit_paddings":{"list":{}},"data_format":{"s":"TkhXQw=="},"padding":{"s":"VkFMSUQ="}}},
{"name":"Maximum_12","op":"Maximum","input":["Maximum_12/x","depthwise_11"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"Minimum_12","op":"Minimum","input":["Minimum_12/x","depthwise_11"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"mul_13","op":"Mul","input":["stage4_unit1_prelu2_gamma","Minimum_12"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_15","op":"AddV2","input":["Maximum_12","mul_13"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"add_16","op":"AddV2","input":["add_15","add_13"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"flatten/Reshape","op":"Reshape","input":["add_16","flatten/Const"],"attr":{"Tshape":{"type":"DT_INT32"},"T":{"type":"DT_FLOAT"}}},
{"name":"dense/MatMul","op":"MatMul","input":["flatten/Reshape","dense/kernel"],"device":"/device:CPU:0","attr":{"T":{"type":"DT_FLOAT"},"transpose_a":{"b":false},"transpose_b":{"b":false}}},
{"name":"batchnorm0/mul_1","op":"Mul","input":["dense/MatMul","batchnorm0/mul"],"attr":{"T":{"type":"DT_FLOAT"}}},
{"name":"batchnorm0/add_1","op":"AddV2","input":["batchnorm0/mul_1","batchnorm0/sub"],"attr":{"T":{"type":"DT_FLOAT"}}}
],
"library": {},
"versions":
{
"producer": 561
}
},
"weightsManifest":
[
{
"paths": ["mobileface.bin"],
"weights": [{"name":"Maximum_12/x","shape":[],"dtype":"float32"},{"name":"stage4_unit1_prelu2_gamma","shape":[128],"dtype":"float32"},{"name":"Minimum_12/x","shape":[],"dtype":"float32"},{"name":"Maximum_11/x","shape":[],"dtype":"float32"},{"name":"stage4_unit1_prelu1_gamma","shape":[128],"dtype":"float32"},{"name":"Minimum_11/x","shape":[],"dtype":"float32"},{"name":"Pad_11/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage4_unit1_conv1_df","shape":[3,3,128,1],"dtype":"float32"},{"name":"Pad_12/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage4_unit1_conv2_df","shape":[3,3,128,1],"dtype":"float32"},{"name":"Maximum_10/x","shape":[],"dtype":"float32"},{"name":"stage4_unit1_prelu0_gamma","shape":[128],"dtype":"float32"},{"name":"Minimum_10/x","shape":[],"dtype":"float32"},{"name":"Maximum_9/x","shape":[],"dtype":"float32"},{"name":"stage3_unit1_prelu2_gamma","shape":[64],"dtype":"float32"},{"name":"Minimum_9/x","shape":[],"dtype":"float32"},{"name":"Maximum_8/x","shape":[],"dtype":"float32"},{"name":"stage3_unit1_prelu1_gamma","shape":[64],"dtype":"float32"},{"name":"Minimum_8/x","shape":[],"dtype":"float32"},{"name":"Pad_8/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage3_unit1_conv1_df","shape":[3,3,64,1],"dtype":"float32"},{"name":"Pad_9/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage3_unit1_conv2_df","shape":[3,3,64,1],"dtype":"float32"},{"name":"Maximum_7/x","shape":[],"dtype":"float32"},{"name":"stage3_unit1_prelu0_gamma","shape":[64],"dtype":"float32"},{"name":"Minimum_7/x","shape":[],"dtype":"float32"},{"name":"Maximum_6/x","shape":[],"dtype":"float32"},{"name":"stage2_unit1_prelu2_gamma","shape":[32],"dtype":"float32"},{"name":"Minimum_6/x","shape":[],"dtype":"float32"},{"name":"Maximum_5/x","shape":[],"dtype":"float32"},{"name":"stage2_unit1_prelu1_gamma","shape":[32],"dtype":"float32"},{"name":"Minimum_5/x","shape":[],"dtype":"float32"},{"name":"Pad_5/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage2_unit1_conv1_df","shape":[3,3,32,1],"dtype":"float32"},{"name":"Pad_6/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage2_unit1_conv2_df","shape":[3,3,32,1],"dtype":"float32"},{"name":"Maximum_4/x","shape":[],"dtype":"float32"},{"name":"stage2_unit1_prelu0_gamma","shape":[32],"dtype":"float32"},{"name":"Minimum_4/x","shape":[],"dtype":"float32"},{"name":"Maximum_3/x","shape":[],"dtype":"float32"},{"name":"stage1_unit1_prelu2_gamma","shape":[32],"dtype":"float32"},{"name":"Minimum_3/x","shape":[],"dtype":"float32"},{"name":"Maximum_2/x","shape":[],"dtype":"float32"},{"name":"stage1_unit1_prelu1_gamma","shape":[32],"dtype":"float32"},{"name":"Minimum_2/x","shape":[],"dtype":"float32"},{"name":"Pad_2/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage1_unit1_conv1_df","shape":[3,3,32,1],"dtype":"float32"},{"name":"Pad_3/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage1_unit1_conv2_df","shape":[3,3,32,1],"dtype":"float32"},{"name":"Maximum_1/x","shape":[],"dtype":"float32"},{"name":"stage1_unit1_prelu0_gamma","shape":[32],"dtype":"float32"},{"name":"Minimum_1/x","shape":[],"dtype":"float32"},{"name":"Maximum/x","shape":[],"dtype":"float32"},{"name":"prelu1_gamma","shape":[32],"dtype":"float32"},{"name":"Minimum/x","shape":[],"dtype":"float32"},{"name":"minusscalar0_second","shape":[1],"dtype":"float32"},{"name":"mulscalar0_second","shape":[1],"dtype":"float32"},{"name":"Pad/paddings","shape":[4,2],"dtype":"int32"},{"name":"conv1_weight","shape":[3,3,3,32],"dtype":"float32"},{"name":"stage1_unit1_conv0_0_weight","shape":[1,1,32,32],"dtype":"float32"},{"name":"Pad_1/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage1_unit1_conv0_1_df","shape":[3,3,32,1],"dtype":"float32"},{"name":"stage2_unit1_conv0_0_weight","shape":[1,1,32,32],"dtype":"float32"},{"name":"Pad_4/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage2_unit1_conv0_1_df","shape":[3,3,32,1],"dtype":"float32"},{"name":"stage3_unit1_conv0_0_weight","shape":[1,1,32,64],"dtype":"float32"},{"name":"Pad_7/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage3_unit1_conv0_1_df","shape":[3,3,64,1],"dtype":"float32"},{"name":"stage4_unit1_conv0_0_weight","shape":[1,1,64,128],"dtype":"float32"},{"name":"Pad_10/paddings","shape":[4,2],"dtype":"int32"},{"name":"stage4_unit1_conv0_1_df","shape":[3,3,128,1],"dtype":"float32"},{"name":"flatten/Const","shape":[2],"dtype":"int32"},{"name":"dense/kernel","shape":[2048,256],"dtype":"float32"},{"name":"batchnorm0/mul","shape":[256],"dtype":"float32"},{"name":"batchnorm0/sub","shape":[256],"dtype":"float32"}]
}
]
}

Binary file not shown.

File diff suppressed because one or more lines are too long

32
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "@vladmandic/human",
"version": "1.0.4",
"version": "1.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@vladmandic/human",
"version": "1.0.4",
"version": "1.1.0",
"license": "MIT",
"devDependencies": {
"@tensorflow/tfjs": "^3.3.0",
@ -19,13 +19,13 @@
"@tensorflow/tfjs-layers": "^3.3.0",
"@tensorflow/tfjs-node": "^3.3.0",
"@tensorflow/tfjs-node-gpu": "^3.3.0",
"@types/node": "^14.14.33",
"@types/node": "^14.14.34",
"@typescript-eslint/eslint-plugin": "^4.17.0",
"@typescript-eslint/parser": "^4.17.0",
"@vladmandic/pilogger": "^0.2.14",
"chokidar": "^3.5.1",
"dayjs": "^1.10.4",
"esbuild": "^0.9.0",
"esbuild": "=0.9.0",
"eslint": "^7.21.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.22.1",
@ -419,9 +419,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "14.14.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.33.tgz",
"integrity": "sha512-oJqcTrgPUF29oUP8AsUqbXGJNuPutsetaa9kTQAQce5Lx5dTYWV02ScBiT/k1BX/Z7pKeqedmvp39Wu4zR7N7g==",
"version": "14.14.34",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.34.tgz",
"integrity": "sha512-dBPaxocOK6UVyvhbnpFIj2W+S+1cBTkHQbFQfeeJhoKFbzYcVUGHvddeWPSucKATb3F0+pgDq0i6ghEaZjsugA==",
"dev": true
},
"node_modules/@types/node-fetch": {
@ -1962,9 +1962,9 @@
}
},
"node_modules/google-protobuf": {
"version": "3.15.5",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.5.tgz",
"integrity": "sha512-6bLpAI4nMIQODlegR7OevgkCoyOj5frLVDArUpeuBWad7XWUNWMGP0v5lz1/aeUI6Yf3cG9XA6acZkPxom4SEw==",
"version": "3.15.6",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.6.tgz",
"integrity": "sha512-p65NyhIZFHFUxbIPOm6cygg2rCjK+2uDCxruOG3RaWKM9R4rBGX0STmlJoSOhoyAG8Fha7U8FP4pQomAV1JXsA==",
"dev": true
},
"node_modules/graceful-fs": {
@ -4236,9 +4236,9 @@
"dev": true
},
"@types/node": {
"version": "14.14.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.33.tgz",
"integrity": "sha512-oJqcTrgPUF29oUP8AsUqbXGJNuPutsetaa9kTQAQce5Lx5dTYWV02ScBiT/k1BX/Z7pKeqedmvp39Wu4zR7N7g==",
"version": "14.14.34",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.34.tgz",
"integrity": "sha512-dBPaxocOK6UVyvhbnpFIj2W+S+1cBTkHQbFQfeeJhoKFbzYcVUGHvddeWPSucKATb3F0+pgDq0i6ghEaZjsugA==",
"dev": true
},
"@types/node-fetch": {
@ -5408,9 +5408,9 @@
}
},
"google-protobuf": {
"version": "3.15.5",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.5.tgz",
"integrity": "sha512-6bLpAI4nMIQODlegR7OevgkCoyOj5frLVDArUpeuBWad7XWUNWMGP0v5lz1/aeUI6Yf3cG9XA6acZkPxom4SEw==",
"version": "3.15.6",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.6.tgz",
"integrity": "sha512-p65NyhIZFHFUxbIPOm6cygg2rCjK+2uDCxruOG3RaWKM9R4rBGX0STmlJoSOhoyAG8Fha7U8FP4pQomAV1JXsA==",
"dev": true
},
"graceful-fs": {

View File

@ -1,6 +1,6 @@
{
"name": "@vladmandic/human",
"version": "1.0.4",
"version": "1.1.0",
"description": "Human: AI-powered 3D Face Detection, Face Embedding & Recognition, Body Pose Tracking, Hand & Finger Tracking, Iris Analysis, Age & Gender & Emotion Prediction & Gesture Recognition",
"sideEffects": false,
"main": "dist/human.node.js",
@ -54,13 +54,13 @@
"@tensorflow/tfjs-layers": "^3.3.0",
"@tensorflow/tfjs-node": "^3.3.0",
"@tensorflow/tfjs-node-gpu": "^3.3.0",
"@types/node": "^14.14.33",
"@types/node": "^14.14.34",
"@typescript-eslint/eslint-plugin": "^4.17.0",
"@typescript-eslint/parser": "^4.17.0",
"@vladmandic/pilogger": "^0.2.14",
"chokidar": "^3.5.1",
"dayjs": "^1.10.4",
"esbuild": "^0.9.0",
"esbuild": "=0.9.0",
"eslint": "^7.21.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.22.1",

View File

@ -18,19 +18,42 @@ export class MediaPipeFaceMesh {
const results: Array<{}> = [];
for (const prediction of (predictions || [])) {
if (prediction.isDisposedInternal) continue; // guard against disposed tensors on long running operations such as pause in middle of processing
const mesh = prediction.coords ? prediction.coords.arraySync() : null;
const meshRaw = prediction.rawCoords;
const mesh = prediction.coords ? prediction.coords.arraySync() : [];
const meshRaw = mesh.map((pt) => [
pt[0] / input.shape[2],
pt[1] / input.shape[1],
pt[2] / this.facePipeline.meshSize,
]);
const annotations = {};
if (mesh && mesh.length > 0) {
for (const key of Object.keys(coords.MESH_ANNOTATIONS)) annotations[key] = coords.MESH_ANNOTATIONS[key].map((index) => mesh[index]);
}
const boxRaw = (prediction.box) ? { topLeft: prediction.box.startPoint, bottomRight: prediction.box.endPoint } : null;
// const boxRaw = (prediction.box) ? { topLeft: prediction.box.startPoint, bottomRight: prediction.box.endPoint } : null;
const box = prediction.box ? [
Math.max(0, prediction.box.startPoint[0]),
Math.max(0, prediction.box.startPoint[1]),
Math.min(input.shape[2], prediction.box.endPoint[0]) - prediction.box.startPoint[0],
Math.min(input.shape[1], prediction.box.endPoint[1]) - prediction.box.startPoint[1],
] : 0;
const boxRaw = prediction.box ? [
Math.max(0, prediction.box.startPoint[0] / input.shape[2]),
Math.max(0, prediction.box.startPoint[1] / input.shape[1]),
Math.min(input.shape[2], (prediction.box.endPoint[0]) - prediction.box.startPoint[0]) / input.shape[2],
Math.min(input.shape[1], (prediction.box.endPoint[1]) - prediction.box.startPoint[1]) / input.shape[2],
] : [];
/*
let offsetRaw = <any>[];
if (meshRaw.length > 0 && boxRaw.length > 0) {
const dimX = meshRaw.map((pt) => pt[0]);
const dimY = meshRaw.map((pt) => pt[1]);
offsetRaw = [
Math.max(0, 0 + Math.min(...dimY) - boxRaw[0]), // distance of detected face border to box top edge
Math.max(0, 0 + Math.min(...dimX) - boxRaw[1]), // distance of detected face border to box left edge
Math.min(1, 1 - Math.max(...dimY) + boxRaw[2]), // distance of detected face border to box bottom edge
Math.min(1, 1 - Math.max(...dimX) + boxRaw[3]), // distance of detected face border to box right edge
];
}
*/
results.push({
confidence: prediction.faceConfidence || prediction.boxConfidence || 0,
boxConfidence: prediction.boxConfidence,
@ -39,6 +62,7 @@ export class MediaPipeFaceMesh {
mesh,
boxRaw,
meshRaw,
// offsetRaw,
annotations,
image: prediction.image ? tf.clone(prediction.image) : null,
});

View File

@ -197,6 +197,8 @@ export class Pipeline {
}
let results = tf.tidy(() => this.storedBoxes.map((box, i) => {
const boxConfidence = box.confidence;
// The facial bounding box landmarks could come either from blazeface (if we are using a fresh box), or from the mesh model (if we are reusing an old box).
let face;
let angle = 0;
@ -282,7 +284,7 @@ export class Pipeline {
coords: transformedCoords,
box,
faceConfidence,
boxConfidence: box.confidence,
boxConfidence,
image: face,
rawCoords,
};

View File

@ -2,10 +2,6 @@ import { log } from '../log';
import * as tf from '../../dist/tfjs.esm.js';
import * as profile from '../profile';
// original: https://github.com/sirius-ai/MobileFaceNet_TF
// modified: https://github.com/sirius-ai/MobileFaceNet_TF/issues/46
// download: https://github.com/sirius-ai/MobileFaceNet_TF/files/3551493/FaceMobileNet192_train_false.zip
let model;
export async function load(config) {
@ -26,32 +22,63 @@ export function simmilarity(embedding1, embedding2, order = 2) {
.map((val, i) => (Math.abs(embedding1[i] - embedding2[i]) ** order)) // distance squared
.reduce((sum, now) => (sum + now), 0) // sum all distances
** (1 / order); // get root of
const res = Math.max(Math.trunc(1000 * (1 - (50 * distance))) / 1000, 0);
const res = Math.max(Math.trunc(1000 * (1 - (1 * distance))) / 1000, 0);
return res;
}
export function enhance(input) {
const image = tf.tidy(() => {
// input received from detector is already normalized to 0..1
// input is also assumed to be straightened
// const data = tf.image.resizeBilinear(input, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false); // just resize to fit the embedding model
// do a tight crop of image and resize it to fit the model
// maybe offsets are already prepared by face model, if not use empirical values
const box = input.offsetRaw
? [input.offsetRaw] // crop based on face mesh borders
: [[0.05, 0.15, 0.85, 0.85]]; // fixed crop for top, left, bottom, right
const tensor = input.image || input.tensor;
const crop = tensor.shape.length === 3
? tf.image.cropAndResize(tensor.expandDims(0), box, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]) // add batch if missing
: tf.image.cropAndResize(tensor, box, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]);
// convert to black&white to avoid colorization impact
const rgb = [0.2989, 0.5870, 0.1140]; // factors for red/green/blue colors when converting to grayscale: https://www.mathworks.com/help/matlab/ref/rgb2gray.html
const [red, green, blue] = tf.split(crop, 3, 3);
const redNorm = tf.mul(red, rgb[0]);
const greenNorm = tf.mul(green, rgb[1]);
const blueNorm = tf.mul(blue, rgb[2]);
const grayscale = tf.addN([redNorm, greenNorm, blueNorm]);
const merge = tf.stack([grayscale, grayscale, grayscale], 3).squeeze(4);
// normalize brightness from 0..1
const darken = merge.sub(merge.min());
const lighten = darken.div(darken.max());
return lighten;
});
return image;
}
export async function predict(input, config) {
if (!model) return null;
return new Promise(async (resolve) => {
const image = tf.tidy(() => {
const data = tf.image.resizeBilinear(input, [model.inputs[0].shape[2], model.inputs[0].shape[1]], false); // input is already normalized to 0..1
const box = [[0.05, 0.15, 0.90, 0.85]]; // top, left, bottom, right
const crop = tf.image.cropAndResize(data, box, [0], [model.inputs[0].shape[2], model.inputs[0].shape[1]]); // optionally do a tight box crop
// const norm = crop.sub(crop.min()).sub(0.5); // trick to normalize around image mean value
const norm = crop.sub(0.5);
return norm;
});
let data: Array<[]> = [];
const image = enhance(input);
// let data: Array<[]> = [];
let data: Array<number> = [];
if (config.face.embedding.enabled) {
if (!config.profile) {
const res = await model.predict({ img_inputs: image });
const res = await model.predict(image);
// optional normalize outputs with l2 normalization
/*
const scaled = tf.tidy(() => {
const l2 = res.norm('euclidean');
const scale = res.div(l2);
return scale;
});
data = scaled.dataSync(); // convert object array to standard array
tf.dispose(scaled);
*/
data = res.dataSync();
// tf.dispose(scaled);
tf.dispose(res);
} else {
const profileData = await tf.profile(() => model.predict({ img_inputs: image }));
@ -64,3 +91,19 @@ export async function predict(input, config) {
resolve(data);
});
}
/*
git clone https://github.com/becauseofAI/MobileFace
cd MobileFace/MobileFace_Identification
mmconvert --srcFramework mxnet --inputWeight MobileFace_Identification_V3-0000.params --inputNetwork MobileFace_Identification_V3-symbol.json --inputShape 3,112,112 --dstFramework tensorflow --outputModel saved
saved_model_cli show --dir saved/
tensorflowjs_converter --input_format tf_saved_model --output_format tfjs_graph_model --saved_model_tags train saved/ graph/
~/dev/detector/signature.js graph/
2021-03-12 08:25:12 DATA: created on: 2021-03-12T13:17:11.960Z
2021-03-12 08:25:12 INFO: graph model: /home/vlado/dev/face/MobileFace/MobileFace_Identification/graph/model.json
2021-03-12 08:25:12 INFO: size: { unreliable: true, numTensors: 75, numDataBuffers: 75, numBytes: 2183192 }
2021-03-12 08:25:12 INFO: model inputs based on signature
2021-03-12 08:25:12 INFO: model outputs based on signature
2021-03-12 08:25:12 DATA: inputs: [ { name: 'data:0', dtype: 'DT_FLOAT', shape: [ -1, 112, 112, 3, [length]: 4 ] }, [length]: 1 ]
2021-03-12 08:25:12 DATA: outputs: [ { id: 0, name: 'batchnorm0/add_1:0', dytpe: 'DT_FLOAT', shape: [ -1, 256, [length]: 2 ] }, [length]: 1 ]
*/

View File

@ -151,6 +151,11 @@ class Human {
return 0;
}
enhance(input: any): any {
if (this.config.face.embedding.enabled) return embedding.enhance(input);
return null;
}
// preload models, not explicitly required as it's done automatically on first use
async load(userConfig = null) {
this.state = 'load';
@ -359,11 +364,11 @@ class Human {
// run emotion, inherits face from blazeface
this.#analyze('Start Embedding:');
if (this.config.async) {
embeddingRes = this.config.face.embedding.enabled ? embedding.predict(face.image, this.config) : [];
embeddingRes = this.config.face.embedding.enabled ? embedding.predict(face, this.config) : [];
} else {
this.state = 'run:embedding';
timeStamp = now();
embeddingRes = this.config.face.embedding.enabled ? await embedding.predict(face.image, this.config) : [];
embeddingRes = this.config.face.embedding.enabled ? await embedding.predict(face, this.config) : [];
this.#perf.embedding = Math.trunc(now() - timeStamp);
}
this.#analyze('End Emotion:');
@ -388,6 +393,8 @@ class Human {
// combine results
faceRes.push({
...face,
/*
confidence: face.confidence,
faceConfidence: face.faceConfidence,
boxConfidence: face.boxConfidence,
@ -395,7 +402,9 @@ class Human {
mesh: face.mesh,
boxRaw: face.boxRaw,
meshRaw: face.meshRaw,
offsetRaw: face.offsetRaw,
annotations: face.annotations,
*/
age: ageRes.age,
gender: genderRes.gender,
genderConfidence: genderRes.confidence,

View File

@ -32,6 +32,7 @@ export class PoseNet {
constructor(model) {
this.baseModel = model;
this.inputSize = model.model.inputs[0].shape[1];
if (this.inputSize < 128) this.inputSize = 257;
}
async estimatePoses(input, config) {

View File

@ -1,3 +1,4 @@
export declare function load(config: any): Promise<any>;
export declare function simmilarity(embedding1: any, embedding2: any, order?: number): number;
export declare function enhance(input: any): any;
export declare function predict(input: any, config: any): Promise<unknown>;

1
types/human.d.ts vendored
View File

@ -53,6 +53,7 @@ declare class Human {
largestKernelOps: any;
} | {};
simmilarity(embedding1: any, embedding2: any): number;
enhance(input: any): any;
load(userConfig?: null): Promise<void>;
detect(input: any, userConfig?: {}): Promise<{
face: any;