add check for null face descriptor

pull/39/head
Vladimir Mandic 2021-01-24 11:08:04 -05:00
parent 09b2e8215d
commit 8890c01162
31 changed files with 72 additions and 174 deletions

View File

@ -46,6 +46,7 @@
"node/no-missing-import": "off", "node/no-missing-import": "off",
"node/no-unsupported-features/es-syntax": "off", "node/no-unsupported-features/es-syntax": "off",
"prefer-destructuring": "off", "prefer-destructuring": "off",
"radix": "off" "radix": "off",
"object-curly-newline": "off"
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@
] ]
}, },
"src/utils/index.ts": { "src/utils/index.ts": {
"bytes": 1842, "bytes": 1793,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -843,7 +843,7 @@
] ]
}, },
"src/NeuralNetwork.ts": { "src/NeuralNetwork.ts": {
"bytes": 5289, "bytes": 5291,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1326,7 +1326,7 @@
] ]
}, },
"src/xception/TinyXception.ts": { "src/xception/TinyXception.ts": {
"bytes": 3148, "bytes": 3143,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1553,7 +1553,7 @@
] ]
}, },
"src/faceLandmarkNet/index.ts": { "src/faceLandmarkNet/index.ts": {
"bytes": 195, "bytes": 194,
"imports": [ "imports": [
{ {
"path": "src/faceLandmarkNet/FaceLandmark68Net.ts", "path": "src/faceLandmarkNet/FaceLandmark68Net.ts",
@ -1609,7 +1609,7 @@
] ]
}, },
"src/faceRecognitionNet/extractParamsFromWeightMap.ts": { "src/faceRecognitionNet/extractParamsFromWeightMap.ts": {
"bytes": 3101, "bytes": 3098,
"imports": [ "imports": [
{ {
"path": "src/common/index.ts", "path": "src/common/index.ts",
@ -1635,7 +1635,7 @@
] ]
}, },
"src/faceRecognitionNet/FaceRecognitionNet.ts": { "src/faceRecognitionNet/FaceRecognitionNet.ts": {
"bytes": 2981, "bytes": 2959,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1735,7 +1735,7 @@
] ]
}, },
"src/ssdMobilenetv1/extractParams.ts": { "src/ssdMobilenetv1/extractParams.ts": {
"bytes": 8339, "bytes": 8330,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1748,7 +1748,7 @@
] ]
}, },
"src/ssdMobilenetv1/extractParamsFromWeightMap.ts": { "src/ssdMobilenetv1/extractParamsFromWeightMap.ts": {
"bytes": 5782, "bytes": 5775,
"imports": [ "imports": [
{ {
"path": "src/common/index.ts", "path": "src/common/index.ts",
@ -1783,11 +1783,11 @@
] ]
}, },
"src/ssdMobilenetv1/nonMaxSuppression.ts": { "src/ssdMobilenetv1/nonMaxSuppression.ts": {
"bytes": 2187, "bytes": 2170,
"imports": [] "imports": []
}, },
"src/ssdMobilenetv1/outputLayer.ts": { "src/ssdMobilenetv1/outputLayer.ts": {
"bytes": 2187, "bytes": 2183,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1796,7 +1796,7 @@
] ]
}, },
"src/ssdMobilenetv1/boxPredictionLayer.ts": { "src/ssdMobilenetv1/boxPredictionLayer.ts": {
"bytes": 617, "bytes": 598,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1830,7 +1830,7 @@
"imports": [] "imports": []
}, },
"src/ssdMobilenetv1/SsdMobilenetv1.ts": { "src/ssdMobilenetv1/SsdMobilenetv1.ts": {
"bytes": 3826, "bytes": 3675,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1913,7 +1913,7 @@
"imports": [] "imports": []
}, },
"src/tinyYolov2/leaky.ts": { "src/tinyYolov2/leaky.ts": {
"bytes": 271, "bytes": 238,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1922,7 +1922,7 @@
] ]
}, },
"src/tinyYolov2/convWithBatchNorm.ts": { "src/tinyYolov2/convWithBatchNorm.ts": {
"bytes": 532, "bytes": 530,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1935,7 +1935,7 @@
] ]
}, },
"src/tinyYolov2/depthwiseSeparableConv.ts": { "src/tinyYolov2/depthwiseSeparableConv.ts": {
"bytes": 502, "bytes": 500,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1948,7 +1948,7 @@
] ]
}, },
"src/tinyYolov2/extractParams.ts": { "src/tinyYolov2/extractParams.ts": {
"bytes": 3918, "bytes": 3911,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1969,7 +1969,7 @@
] ]
}, },
"src/tinyYolov2/extractParamsFromWeightMap.ts": { "src/tinyYolov2/extractParamsFromWeightMap.ts": {
"bytes": 3284, "bytes": 3282,
"imports": [ "imports": [
{ {
"path": "src/common/disposeUnusedWeightTensors.ts", "path": "src/common/disposeUnusedWeightTensors.ts",
@ -1990,7 +1990,7 @@
"imports": [] "imports": []
}, },
"src/tinyYolov2/TinyYolov2Base.ts": { "src/tinyYolov2/TinyYolov2Base.ts": {
"bytes": 9700, "bytes": 9606,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -2080,7 +2080,7 @@
"imports": [] "imports": []
}, },
"src/tinyYolov2/index.ts": { "src/tinyYolov2/index.ts": {
"bytes": 349, "bytes": 347,
"imports": [ "imports": [
{ {
"path": "src/tinyYolov2/TinyYolov2.ts", "path": "src/tinyYolov2/TinyYolov2.ts",
@ -2501,7 +2501,7 @@
] ]
}, },
"src/index.ts": { "src/index.ts": {
"bytes": 1024, "bytes": 987,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -2590,7 +2590,7 @@
"dist/face-api.esm.js.map": { "dist/face-api.esm.js.map": {
"imports": [], "imports": [],
"inputs": {}, "inputs": {},
"bytes": 1456658 "bytes": 1456195
}, },
"dist/face-api.esm.js": { "dist/face-api.esm.js": {
"imports": [], "imports": [],
@ -2892,7 +2892,7 @@
"bytesInOutput": 102 "bytesInOutput": 102
}, },
"src/NeuralNetwork.ts": { "src/NeuralNetwork.ts": {
"bytesInOutput": 2592 "bytesInOutput": 2606
}, },
"src/common/depthwiseSeparableConv.ts": { "src/common/depthwiseSeparableConv.ts": {
"bytesInOutput": 117 "bytesInOutput": 117
@ -3048,7 +3048,7 @@
"bytesInOutput": 397 "bytesInOutput": 397
}, },
"src/faceRecognitionNet/FaceRecognitionNet.ts": { "src/faceRecognitionNet/FaceRecognitionNet.ts": {
"bytesInOutput": 1012 "bytesInOutput": 1106
}, },
"src/faceRecognitionNet/index.ts": { "src/faceRecognitionNet/index.ts": {
"bytesInOutput": 58 "bytesInOutput": 58
@ -3186,7 +3186,7 @@
"bytesInOutput": 446 "bytesInOutput": 446
} }
}, },
"bytes": 1143701 "bytes": 1143809
} }
} }
} }

2
dist/face-api.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

48
dist/face-api.json vendored
View File

@ -18,7 +18,7 @@
] ]
}, },
"src/utils/index.ts": { "src/utils/index.ts": {
"bytes": 1842, "bytes": 1793,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -843,7 +843,7 @@
] ]
}, },
"src/NeuralNetwork.ts": { "src/NeuralNetwork.ts": {
"bytes": 5289, "bytes": 5291,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1326,7 +1326,7 @@
] ]
}, },
"src/xception/TinyXception.ts": { "src/xception/TinyXception.ts": {
"bytes": 3148, "bytes": 3143,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1553,7 +1553,7 @@
] ]
}, },
"src/faceLandmarkNet/index.ts": { "src/faceLandmarkNet/index.ts": {
"bytes": 195, "bytes": 194,
"imports": [ "imports": [
{ {
"path": "src/faceLandmarkNet/FaceLandmark68Net.ts", "path": "src/faceLandmarkNet/FaceLandmark68Net.ts",
@ -1609,7 +1609,7 @@
] ]
}, },
"src/faceRecognitionNet/extractParamsFromWeightMap.ts": { "src/faceRecognitionNet/extractParamsFromWeightMap.ts": {
"bytes": 3101, "bytes": 3098,
"imports": [ "imports": [
{ {
"path": "src/common/index.ts", "path": "src/common/index.ts",
@ -1635,7 +1635,7 @@
] ]
}, },
"src/faceRecognitionNet/FaceRecognitionNet.ts": { "src/faceRecognitionNet/FaceRecognitionNet.ts": {
"bytes": 2981, "bytes": 2959,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1735,7 +1735,7 @@
] ]
}, },
"src/ssdMobilenetv1/extractParams.ts": { "src/ssdMobilenetv1/extractParams.ts": {
"bytes": 8339, "bytes": 8330,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1748,7 +1748,7 @@
] ]
}, },
"src/ssdMobilenetv1/extractParamsFromWeightMap.ts": { "src/ssdMobilenetv1/extractParamsFromWeightMap.ts": {
"bytes": 5782, "bytes": 5775,
"imports": [ "imports": [
{ {
"path": "src/common/index.ts", "path": "src/common/index.ts",
@ -1783,11 +1783,11 @@
] ]
}, },
"src/ssdMobilenetv1/nonMaxSuppression.ts": { "src/ssdMobilenetv1/nonMaxSuppression.ts": {
"bytes": 2187, "bytes": 2170,
"imports": [] "imports": []
}, },
"src/ssdMobilenetv1/outputLayer.ts": { "src/ssdMobilenetv1/outputLayer.ts": {
"bytes": 2187, "bytes": 2183,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1796,7 +1796,7 @@
] ]
}, },
"src/ssdMobilenetv1/boxPredictionLayer.ts": { "src/ssdMobilenetv1/boxPredictionLayer.ts": {
"bytes": 617, "bytes": 598,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1830,7 +1830,7 @@
"imports": [] "imports": []
}, },
"src/ssdMobilenetv1/SsdMobilenetv1.ts": { "src/ssdMobilenetv1/SsdMobilenetv1.ts": {
"bytes": 3826, "bytes": 3675,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1913,7 +1913,7 @@
"imports": [] "imports": []
}, },
"src/tinyYolov2/leaky.ts": { "src/tinyYolov2/leaky.ts": {
"bytes": 271, "bytes": 238,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1922,7 +1922,7 @@
] ]
}, },
"src/tinyYolov2/convWithBatchNorm.ts": { "src/tinyYolov2/convWithBatchNorm.ts": {
"bytes": 532, "bytes": 530,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1935,7 +1935,7 @@
] ]
}, },
"src/tinyYolov2/depthwiseSeparableConv.ts": { "src/tinyYolov2/depthwiseSeparableConv.ts": {
"bytes": 502, "bytes": 500,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1948,7 +1948,7 @@
] ]
}, },
"src/tinyYolov2/extractParams.ts": { "src/tinyYolov2/extractParams.ts": {
"bytes": 3918, "bytes": 3911,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -1969,7 +1969,7 @@
] ]
}, },
"src/tinyYolov2/extractParamsFromWeightMap.ts": { "src/tinyYolov2/extractParamsFromWeightMap.ts": {
"bytes": 3284, "bytes": 3282,
"imports": [ "imports": [
{ {
"path": "src/common/disposeUnusedWeightTensors.ts", "path": "src/common/disposeUnusedWeightTensors.ts",
@ -1990,7 +1990,7 @@
"imports": [] "imports": []
}, },
"src/tinyYolov2/TinyYolov2Base.ts": { "src/tinyYolov2/TinyYolov2Base.ts": {
"bytes": 9700, "bytes": 9606,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -2080,7 +2080,7 @@
"imports": [] "imports": []
}, },
"src/tinyYolov2/index.ts": { "src/tinyYolov2/index.ts": {
"bytes": 349, "bytes": 347,
"imports": [ "imports": [
{ {
"path": "src/tinyYolov2/TinyYolov2.ts", "path": "src/tinyYolov2/TinyYolov2.ts",
@ -2501,7 +2501,7 @@
] ]
}, },
"src/index.ts": { "src/index.ts": {
"bytes": 1024, "bytes": 987,
"imports": [ "imports": [
{ {
"path": "dist/tfjs.esm.js", "path": "dist/tfjs.esm.js",
@ -2590,7 +2590,7 @@
"dist/face-api.js.map": { "dist/face-api.js.map": {
"imports": [], "imports": [],
"inputs": {}, "inputs": {},
"bytes": 1456665 "bytes": 1456202
}, },
"dist/face-api.js": { "dist/face-api.js": {
"imports": [], "imports": [],
@ -2774,7 +2774,7 @@
"bytesInOutput": 101 "bytesInOutput": 101
}, },
"src/NeuralNetwork.ts": { "src/NeuralNetwork.ts": {
"bytesInOutput": 2592 "bytesInOutput": 2606
}, },
"src/common/depthwiseSeparableConv.ts": { "src/common/depthwiseSeparableConv.ts": {
"bytesInOutput": 117 "bytesInOutput": 117
@ -2927,7 +2927,7 @@
"bytesInOutput": 397 "bytesInOutput": 397
}, },
"src/faceRecognitionNet/FaceRecognitionNet.ts": { "src/faceRecognitionNet/FaceRecognitionNet.ts": {
"bytesInOutput": 1012 "bytesInOutput": 1106
}, },
"src/faceRecognitionNet/index.ts": { "src/faceRecognitionNet/index.ts": {
"bytesInOutput": 57 "bytesInOutput": 57
@ -3065,7 +3065,7 @@
"bytesInOutput": 446 "bytesInOutput": 446
} }
}, },
"bytes": 1143864 "bytes": 1143972
} }
} }
} }

View File

@ -6,6 +6,10 @@ import { loadWeightMap } from './dom/index';
import { env } from './env/index'; import { env } from './env/index';
export abstract class NeuralNetwork<TNetParams> { export abstract class NeuralNetwork<TNetParams> {
constructor(name: string) {
this._name = name;
}
protected _params: TNetParams | undefined = undefined protected _params: TNetParams | undefined = undefined
protected _paramMappings: ParamMapping[] = [] protected _paramMappings: ParamMapping[] = []
@ -81,7 +85,6 @@ export abstract class NeuralNetwork<TNetParams> {
this.extractWeights(weightsOrUrl); this.extractWeights(weightsOrUrl);
return; return;
} }
await this.loadFromUri(weightsOrUrl); await this.loadFromUri(weightsOrUrl);
} }
@ -89,7 +92,6 @@ export abstract class NeuralNetwork<TNetParams> {
if (uri && typeof uri !== 'string') { if (uri && typeof uri !== 'string') {
throw new Error(`${this._name}.loadFromUri - expected model uri`); throw new Error(`${this._name}.loadFromUri - expected model uri`);
} }
const weightMap = await loadWeightMap(uri, this.getDefaultModelName()); const weightMap = await loadWeightMap(uri, this.getDefaultModelName());
this.loadFromWeightMap(weightMap); this.loadFromWeightMap(weightMap);
} }
@ -98,37 +100,23 @@ export abstract class NeuralNetwork<TNetParams> {
if (filePath && typeof filePath !== 'string') { if (filePath && typeof filePath !== 'string') {
throw new Error(`${this._name}.loadFromDisk - expected model file path`); throw new Error(`${this._name}.loadFromDisk - expected model file path`);
} }
const { readFile } = env.getEnv(); const { readFile } = env.getEnv();
const { manifestUri, modelBaseUri } = getModelUris(filePath, this.getDefaultModelName()); const { manifestUri, modelBaseUri } = getModelUris(filePath, this.getDefaultModelName());
const fetchWeightsFromDisk = (filePaths: string[]) => Promise.all(filePaths.map((fp) => readFile(fp).then((buf) => buf.buffer)));
const fetchWeightsFromDisk = (filePaths: string[]) => Promise.all(
filePaths.map((fp) => readFile(fp).then((buf) => buf.buffer)),
);
const loadWeights = tf.io.weightsLoaderFactory(fetchWeightsFromDisk); const loadWeights = tf.io.weightsLoaderFactory(fetchWeightsFromDisk);
const manifest = JSON.parse((await readFile(manifestUri)).toString()); const manifest = JSON.parse((await readFile(manifestUri)).toString());
const weightMap = await loadWeights(manifest, modelBaseUri); const weightMap = await loadWeights(manifest, modelBaseUri);
this.loadFromWeightMap(weightMap); this.loadFromWeightMap(weightMap);
} }
public loadFromWeightMap(weightMap: tf.NamedTensorMap) { public loadFromWeightMap(weightMap: tf.NamedTensorMap) {
const { const { paramMappings, params } = this.extractParamsFromWeightMap(weightMap);
paramMappings,
params,
} = this.extractParamsFromWeightMap(weightMap);
this._paramMappings = paramMappings; this._paramMappings = paramMappings;
this._params = params; this._params = params;
} }
public extractWeights(weights: Float32Array) { public extractWeights(weights: Float32Array) {
const { const { paramMappings, params } = this.extractParams(weights);
paramMappings,
params,
} = this.extractParams(weights);
this._paramMappings = paramMappings; this._paramMappings = paramMappings;
this._params = params; this._params = params;
} }
@ -143,7 +131,6 @@ export abstract class NeuralNetwork<TNetParams> {
if (!res.nextObj.hasOwnProperty(objProp)) { if (!res.nextObj.hasOwnProperty(objProp)) {
throw new Error(`traversePropertyPath - object does not have property ${objProp}, for path ${paramPath}`); throw new Error(`traversePropertyPath - object does not have property ${objProp}, for path ${paramPath}`);
} }
return { obj: res.nextObj, objProp, nextObj: res.nextObj[objProp] }; return { obj: res.nextObj, objProp, nextObj: res.nextObj[objProp] };
}, { nextObj: this.params }); }, { nextObj: this.params });

View File

@ -1,8 +1,7 @@
export class PlatformBrowser { export class PlatformBrowser {
private textEncoder: TextEncoder; private textEncoder: TextEncoder;
// eslint-disable-next-line no-undef fetch(path: string, init?: any): Promise<Response> {
fetch(path: string, init?: RequestInit): Promise<Response> {
return fetch(path, init); return fetch(path, init);
} }

View File

@ -2,5 +2,4 @@ import { FaceLandmark68Net } from './FaceLandmark68Net';
export * from './FaceLandmark68Net'; export * from './FaceLandmark68Net';
export * from './FaceLandmark68TinyNet'; export * from './FaceLandmark68TinyNet';
export class FaceLandmarkNet extends FaceLandmark68Net {} export class FaceLandmarkNet extends FaceLandmark68Net {}

View File

@ -1 +0,0 @@
export * from './FaceProcessor';

View File

@ -22,7 +22,6 @@ export class FaceRecognitionNet extends NeuralNetwork<NetParams> {
} }
return tf.tidy(() => { return tf.tidy(() => {
// const batchTensor = input.toBatchTensor(150, true).toFloat()
const batchTensor = tf.cast(input.toBatchTensor(150, true), 'float32'); const batchTensor = tf.cast(input.toBatchTensor(150, true), 'float32');
const meanRgb = [122.782, 117.001, 104.298]; const meanRgb = [122.782, 117.001, 104.298];
@ -61,25 +60,14 @@ export class FaceRecognitionNet extends NeuralNetwork<NetParams> {
} }
public async computeFaceDescriptor(input: TNetInput): Promise<Float32Array|Float32Array[]> { public async computeFaceDescriptor(input: TNetInput): Promise<Float32Array|Float32Array[]> {
// When faces have a detected dimension of 0, tensorflow will crash the whole process. if (input?.shape?.some((dim) => dim <= 0)) return new Float32Array(128);
// Sidestep this by returning an empty descriptor instead.
if (input.shape.some(dimension => dimension <= 0) return new Float32Array(128);
const netInput = await toNetInput(input); const netInput = await toNetInput(input);
const faceDescriptorTensors = tf.tidy( const faceDescriptorTensors = tf.tidy(
() => tf.unstack(this.forwardInput(netInput)), () => tf.unstack(this.forwardInput(netInput)),
); );
const faceDescriptorsForBatch = await Promise.all(faceDescriptorTensors.map((t) => t.data())) as Float32Array[];
const faceDescriptorsForBatch = await Promise.all(faceDescriptorTensors.map(
(t) => t.data(),
)) as Float32Array[];
faceDescriptorTensors.forEach((t) => t.dispose()); faceDescriptorTensors.forEach((t) => t.dispose());
return netInput.isBatchInput ? faceDescriptorsForBatch : faceDescriptorsForBatch[0];
return netInput.isBatchInput
? faceDescriptorsForBatch
: faceDescriptorsForBatch[0];
} }
protected getDefaultModelName(): string { protected getDefaultModelName(): string {

View File

@ -2,9 +2,7 @@ import * as tf from '../../dist/tfjs.esm';
import { disposeUnusedWeightTensors, extractWeightEntryFactory, ParamMapping } from '../common/index'; import { disposeUnusedWeightTensors, extractWeightEntryFactory, ParamMapping } from '../common/index';
import { isTensor2D } from '../utils/index'; import { isTensor2D } from '../utils/index';
import { import { ConvLayerParams, NetParams, ResidualLayerParams, ScaleLayerParams } from './types';
ConvLayerParams, NetParams, ResidualLayerParams, ScaleLayerParams,
} from './types';
function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) { function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) {
const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings); const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings);

View File

@ -23,6 +23,5 @@ export * from './NeuralNetwork';
export * from './resizeResults'; export * from './resizeResults';
const node = (typeof process !== 'undefined'); const node = (typeof process !== 'undefined');
// eslint-disable-next-line no-undef
const browser = (typeof navigator !== 'undefined') && (typeof navigator.userAgent !== 'undefined'); const browser = (typeof navigator !== 'undefined') && (typeof navigator.userAgent !== 'undefined');
export const version = { faceapi: pkg.version as string, node, browser }; export const version = { faceapi: pkg.version as string, node, browser };

View File

@ -26,9 +26,7 @@ export class SsdMobilenetv1 extends NeuralNetwork<NetParams> {
} }
return tf.tidy(() => { return tf.tidy(() => {
// const batchTensor = input.toBatchTensor(512, false).toFloat()
const batchTensor = tf.cast(input.toBatchTensor(512, false), 'float32'); const batchTensor = tf.cast(input.toBatchTensor(512, false), 'float32');
const x = tf.sub(tf.mul(batchTensor, tf.scalar(0.007843137718737125)), tf.scalar(1)) as tf.Tensor4D; const x = tf.sub(tf.mul(batchTensor, tf.scalar(0.007843137718737125)), tf.scalar(1)) as tf.Tensor4D;
const features = mobileNetV1(x, params.mobilenetv1); const features = mobileNetV1(x, params.mobilenetv1);
@ -58,7 +56,6 @@ export class SsdMobilenetv1 extends NeuralNetwork<NetParams> {
scores: _scores, scores: _scores,
} = this.forwardInput(netInput); } = this.forwardInput(netInput);
// TODO batches
const boxes = _boxes[0]; const boxes = _boxes[0];
const scores = _scores[0]; const scores = _scores[0];
for (let i = 1; i < _boxes.length; i++) { for (let i = 1; i < _boxes.length; i++) {
@ -66,9 +63,7 @@ export class SsdMobilenetv1 extends NeuralNetwork<NetParams> {
_scores[i].dispose(); _scores[i].dispose();
} }
// TODO find a better way to filter by minConfidence
const scoresData = Array.from(await scores.data()); const scoresData = Array.from(await scores.data());
const iouThreshold = 0.5; const iouThreshold = 0.5;
const indices = nonMaxSuppression( const indices = nonMaxSuppression(
boxes, boxes,
@ -111,7 +106,6 @@ export class SsdMobilenetv1 extends NeuralNetwork<NetParams> {
boxes.dispose(); boxes.dispose();
scores.dispose(); scores.dispose();
return results; return results;
} }

View File

@ -9,7 +9,6 @@ export function boxPredictionLayer(
) { ) {
return tf.tidy(() => { return tf.tidy(() => {
const batchSize = x.shape[0]; const batchSize = x.shape[0];
const boxPredictionEncoding = tf.reshape( const boxPredictionEncoding = tf.reshape(
convLayer(x, params.box_encoding_predictor), convLayer(x, params.box_encoding_predictor),
[batchSize, -1, 1, 4], [batchSize, -1, 1, 4],
@ -18,10 +17,6 @@ export function boxPredictionLayer(
convLayer(x, params.class_predictor), convLayer(x, params.class_predictor),
[batchSize, -1, 3], [batchSize, -1, 3],
); );
return { boxPredictionEncoding, classPrediction };
return {
boxPredictionEncoding,
classPrediction,
};
}); });
} }

View File

@ -83,7 +83,6 @@ function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings
function extractMobilenetV1Params(): MobileNetV1.Params { function extractMobilenetV1Params(): MobileNetV1.Params {
const conv_0 = extractPointwiseConvParams(3, 32, 3, 'mobilenetv1/conv_0'); const conv_0 = extractPointwiseConvParams(3, 32, 3, 'mobilenetv1/conv_0');
const conv_1 = extractConvPairParams(32, 64, 'mobilenetv1/conv_1'); const conv_1 = extractConvPairParams(32, 64, 'mobilenetv1/conv_1');
const conv_2 = extractConvPairParams(64, 128, 'mobilenetv1/conv_2'); const conv_2 = extractConvPairParams(64, 128, 'mobilenetv1/conv_2');
const conv_3 = extractConvPairParams(128, 128, 'mobilenetv1/conv_3'); const conv_3 = extractConvPairParams(128, 128, 'mobilenetv1/conv_3');
@ -97,7 +96,6 @@ function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings
const conv_11 = extractConvPairParams(512, 512, 'mobilenetv1/conv_11'); const conv_11 = extractConvPairParams(512, 512, 'mobilenetv1/conv_11');
const conv_12 = extractConvPairParams(512, 1024, 'mobilenetv1/conv_12'); const conv_12 = extractConvPairParams(512, 1024, 'mobilenetv1/conv_12');
const conv_13 = extractConvPairParams(1024, 1024, 'mobilenetv1/conv_13'); const conv_13 = extractConvPairParams(1024, 1024, 'mobilenetv1/conv_13');
return { return {
conv_0, conv_0,
conv_1, conv_1,
@ -125,7 +123,6 @@ function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings
const conv_5 = extractPointwiseConvParams(128, 256, 3, 'prediction_layer/conv_5'); const conv_5 = extractPointwiseConvParams(128, 256, 3, 'prediction_layer/conv_5');
const conv_6 = extractPointwiseConvParams(256, 64, 1, 'prediction_layer/conv_6'); const conv_6 = extractPointwiseConvParams(256, 64, 1, 'prediction_layer/conv_6');
const conv_7 = extractPointwiseConvParams(64, 128, 3, 'prediction_layer/conv_7'); const conv_7 = extractPointwiseConvParams(64, 128, 3, 'prediction_layer/conv_7');
const box_encoding_0_predictor = extractConvParams(512, 12, 1, 'prediction_layer/box_predictor_0/box_encoding_predictor'); const box_encoding_0_predictor = extractConvParams(512, 12, 1, 'prediction_layer/box_predictor_0/box_encoding_predictor');
const class_predictor_0 = extractConvParams(512, 9, 1, 'prediction_layer/box_predictor_0/class_predictor'); const class_predictor_0 = extractConvParams(512, 9, 1, 'prediction_layer/box_predictor_0/class_predictor');
const box_encoding_1_predictor = extractConvParams(1024, 24, 1, 'prediction_layer/box_predictor_1/box_encoding_predictor'); const box_encoding_1_predictor = extractConvParams(1024, 24, 1, 'prediction_layer/box_predictor_1/box_encoding_predictor');
@ -163,7 +160,6 @@ function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings
box_encoding_predictor: box_encoding_5_predictor, box_encoding_predictor: box_encoding_5_predictor,
class_predictor: class_predictor_5, class_predictor: class_predictor_5,
}; };
return { return {
conv_0, conv_0,
conv_1, conv_1,
@ -190,17 +186,14 @@ function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings
export function extractParams(weights: Float32Array): { params: NetParams, paramMappings: ParamMapping[] } { export function extractParams(weights: Float32Array): { params: NetParams, paramMappings: ParamMapping[] } {
const paramMappings: ParamMapping[] = []; const paramMappings: ParamMapping[] = [];
const { const {
extractWeights, extractWeights,
getRemainingWeights, getRemainingWeights,
} = extractWeightsFactory(weights); } = extractWeightsFactory(weights);
const { const {
extractMobilenetV1Params, extractMobilenetV1Params,
extractPredictionLayerParams, extractPredictionLayerParams,
} = extractorsFactory(extractWeights, paramMappings); } = extractorsFactory(extractWeights, paramMappings);
const mobilenetv1 = extractMobilenetV1Params(); const mobilenetv1 = extractMobilenetV1Params();
const prediction_layer = extractPredictionLayerParams(); const prediction_layer = extractPredictionLayerParams();
const extra_dim = tf.tensor3d( const extra_dim = tf.tensor3d(
@ -210,9 +203,7 @@ export function extractParams(weights: Float32Array): { params: NetParams, param
const output_layer = { const output_layer = {
extra_dim, extra_dim,
}; };
paramMappings.push({ paramPath: 'output_layer/extra_dim' }); paramMappings.push({ paramPath: 'output_layer/extra_dim' });
if (getRemainingWeights().length !== 0) { if (getRemainingWeights().length !== 0) {
throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`); throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
} }

View File

@ -14,7 +14,6 @@ function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) {
function extractPointwiseConvParams(prefix: string, idx: number, mappedPrefix: string): PointwiseConvParams { function extractPointwiseConvParams(prefix: string, idx: number, mappedPrefix: string): PointwiseConvParams {
const filters = extractWeightEntry(`${prefix}/Conv2d_${idx}_pointwise/weights`, 4, `${mappedPrefix}/filters`); const filters = extractWeightEntry(`${prefix}/Conv2d_${idx}_pointwise/weights`, 4, `${mappedPrefix}/filters`);
const batch_norm_offset = extractWeightEntry(`${prefix}/Conv2d_${idx}_pointwise/convolution_bn_offset`, 1, `${mappedPrefix}/batch_norm_offset`); const batch_norm_offset = extractWeightEntry(`${prefix}/Conv2d_${idx}_pointwise/convolution_bn_offset`, 1, `${mappedPrefix}/batch_norm_offset`);
return { filters, batch_norm_offset }; return { filters, batch_norm_offset };
} }
@ -64,7 +63,6 @@ function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) {
function extractConvParams(prefix: string, mappedPrefix: string): ConvParams { function extractConvParams(prefix: string, mappedPrefix: string): ConvParams {
const filters = extractWeightEntry(`${prefix}/weights`, 4, `${mappedPrefix}/filters`); const filters = extractWeightEntry(`${prefix}/weights`, 4, `${mappedPrefix}/filters`);
const bias = extractWeightEntry(`${prefix}/biases`, 1, `${mappedPrefix}/bias`); const bias = extractWeightEntry(`${prefix}/biases`, 1, `${mappedPrefix}/bias`);
return { filters, bias }; return { filters, bias };
} }
@ -77,7 +75,6 @@ function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) {
`Prediction/BoxPredictor_${idx}/ClassPredictor`, `Prediction/BoxPredictor_${idx}/ClassPredictor`,
`prediction_layer/box_predictor_${idx}/class_predictor`, `prediction_layer/box_predictor_${idx}/class_predictor`,
); );
return { box_encoding_predictor, class_predictor }; return { box_encoding_predictor, class_predictor };
} }
@ -110,15 +107,12 @@ export function extractParamsFromWeightMap(
weightMap: tf.NamedTensorMap, weightMap: tf.NamedTensorMap,
): { params: NetParams, paramMappings: ParamMapping[] } { ): { params: NetParams, paramMappings: ParamMapping[] } {
const paramMappings: ParamMapping[] = []; const paramMappings: ParamMapping[] = [];
const { const {
extractMobilenetV1Params, extractMobilenetV1Params,
extractPredictionLayerParams, extractPredictionLayerParams,
} = extractorsFactory(weightMap, paramMappings); } = extractorsFactory(weightMap, paramMappings);
const extra_dim = weightMap['Output/extra_dim']; const extra_dim = weightMap['Output/extra_dim'];
paramMappings.push({ originalPath: 'Output/extra_dim', paramPath: 'output_layer/extra_dim' }); paramMappings.push({ originalPath: 'Output/extra_dim', paramPath: 'output_layer/extra_dim' });
if (!isTensor3D(extra_dim)) { if (!isTensor3D(extra_dim)) {
throw new Error(`expected weightMap['Output/extra_dim'] to be a Tensor3D, instead have ${extra_dim}`); throw new Error(`expected weightMap['Output/extra_dim'] to be a Tensor3D, instead have ${extra_dim}`);
} }
@ -132,6 +126,5 @@ export function extractParamsFromWeightMap(
}; };
disposeUnusedWeightTensors(weightMap, paramMappings); disposeUnusedWeightTensors(weightMap, paramMappings);
return { params, paramMappings }; return { params, paramMappings };
} }

View File

@ -43,15 +43,11 @@ export function nonMaxSuppression(
.sort((c1, c2) => c2.score - c1.score); .sort((c1, c2) => c2.score - c1.score);
const suppressFunc = (x: number) => (x <= iouThreshold ? 1 : 0); const suppressFunc = (x: number) => (x <= iouThreshold ? 1 : 0);
const selected: number[] = []; const selected: number[] = [];
candidates.forEach((c) => { candidates.forEach((c) => {
if (selected.length >= outputSize) { if (selected.length >= outputSize) return;
return;
}
const originalScore = c.score; const originalScore = c.score;
for (let j = selected.length - 1; j >= 0; --j) { for (let j = selected.length - 1; j >= 0; --j) {
const iou = IOU(boxes, c.boxIndex, selected[j]); const iou = IOU(boxes, c.boxIndex, selected[j]);
if (iou === 0.0) continue; if (iou === 0.0) continue;
@ -62,6 +58,5 @@ export function nonMaxSuppression(
selected.push(c.boxIndex); selected.push(c.boxIndex);
} }
}); });
return selected; return selected;
} }

View File

@ -9,12 +9,10 @@ function getCenterCoordinatesAndSizesLayer(x: tf.Tensor2D) {
tf.sub(vec[2], vec[0]), tf.sub(vec[2], vec[0]),
tf.sub(vec[3], vec[1]), tf.sub(vec[3], vec[1]),
]; ];
const centers = [ const centers = [
tf.add(vec[0], tf.div(sizes[0], tf.scalar(2))), tf.add(vec[0], tf.div(sizes[0], tf.scalar(2))),
tf.add(vec[1], tf.div(sizes[1], tf.scalar(2))), tf.add(vec[1], tf.div(sizes[1], tf.scalar(2))),
]; ];
return { return {
sizes, sizes,
centers, centers,
@ -28,10 +26,8 @@ function decodeBoxesLayer(x0: tf.Tensor2D, x1: tf.Tensor2D) {
} = getCenterCoordinatesAndSizesLayer(x0); } = getCenterCoordinatesAndSizesLayer(x0);
const vec = tf.unstack(tf.transpose(x1, [1, 0])); const vec = tf.unstack(tf.transpose(x1, [1, 0]));
const div0_out = tf.div(tf.mul(tf.exp(tf.div(vec[2], tf.scalar(5))), sizes[0]), tf.scalar(2)); const div0_out = tf.div(tf.mul(tf.exp(tf.div(vec[2], tf.scalar(5))), sizes[0]), tf.scalar(2));
const add0_out = tf.add(tf.mul(tf.div(vec[0], tf.scalar(10)), sizes[0]), centers[0]); const add0_out = tf.add(tf.mul(tf.div(vec[0], tf.scalar(10)), sizes[0]), centers[0]);
const div1_out = tf.div(tf.mul(tf.exp(tf.div(vec[3], tf.scalar(5))), sizes[1]), tf.scalar(2)); const div1_out = tf.div(tf.mul(tf.exp(tf.div(vec[3], tf.scalar(5))), sizes[1]), tf.scalar(2));
const add1_out = tf.add(tf.mul(tf.div(vec[1], tf.scalar(10)), sizes[1]), centers[1]); const add1_out = tf.add(tf.mul(tf.div(vec[1], tf.scalar(10)), sizes[1]), centers[1]);

View File

@ -22,9 +22,7 @@ import { ITinyYolov2Options, TinyYolov2Options } from './TinyYolov2Options';
import { DefaultTinyYolov2NetParams, MobilenetParams, TinyYolov2NetParams } from './types'; import { DefaultTinyYolov2NetParams, MobilenetParams, TinyYolov2NetParams } from './types';
export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> { export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
public static DEFAULT_FILTER_SIZES = [ public static DEFAULT_FILTER_SIZES = [3, 16, 32, 64, 128, 256, 512, 1024, 1024];
3, 16, 32, 64, 128, 256, 512, 1024, 1024,
]
private _config: TinyYolov2Config private _config: TinyYolov2Config
@ -61,7 +59,6 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
out = tf.maxPool(out, [2, 2], [1, 1], 'same'); out = tf.maxPool(out, [2, 2], [1, 1], 'same');
out = convWithBatchNorm(out, params.conv6); out = convWithBatchNorm(out, params.conv6);
out = convWithBatchNorm(out, params.conv7); out = convWithBatchNorm(out, params.conv7);
return convLayer(out, params.conv8, 'valid', false); return convLayer(out, params.conv8, 'valid', false);
} }
@ -82,7 +79,6 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
out = tf.maxPool(out, [2, 2], [1, 1], 'same'); out = tf.maxPool(out, [2, 2], [1, 1], 'same');
out = params.conv6 ? depthwiseSeparableConv(out, params.conv6) : out; out = params.conv6 ? depthwiseSeparableConv(out, params.conv6) : out;
out = params.conv7 ? depthwiseSeparableConv(out, params.conv7) : out; out = params.conv7 ? depthwiseSeparableConv(out, params.conv7) : out;
return convLayer(out, params.conv8, 'valid', false); return convLayer(out, params.conv8, 'valid', false);
} }
@ -94,13 +90,11 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
} }
return tf.tidy(() => { return tf.tidy(() => {
// let batchTensor = input.toBatchTensor(inputSize, false).toFloat()
let batchTensor = tf.cast(input.toBatchTensor(inputSize, false), 'float32'); let batchTensor = tf.cast(input.toBatchTensor(inputSize, false), 'float32');
batchTensor = this.config.meanRgb batchTensor = this.config.meanRgb
? normalize(batchTensor, this.config.meanRgb) ? normalize(batchTensor, this.config.meanRgb)
: batchTensor; : batchTensor;
batchTensor = batchTensor.div(tf.scalar(256)) as tf.Tensor4D; batchTensor = batchTensor.div(tf.scalar(256)) as tf.Tensor4D;
return this.config.withSeparableConvs return this.config.withSeparableConvs
? this.runMobilenet(batchTensor, params as MobilenetParams) ? this.runMobilenet(batchTensor, params as MobilenetParams)
: this.runTinyYolov2(batchTensor, params as DefaultTinyYolov2NetParams); : this.runTinyYolov2(batchTensor, params as DefaultTinyYolov2NetParams);
@ -113,11 +107,9 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
public async detect(input: TNetInput, forwardParams: ITinyYolov2Options = {}): Promise<ObjectDetection[]> { public async detect(input: TNetInput, forwardParams: ITinyYolov2Options = {}): Promise<ObjectDetection[]> {
const { inputSize, scoreThreshold } = new TinyYolov2Options(forwardParams); const { inputSize, scoreThreshold } = new TinyYolov2Options(forwardParams);
const netInput = await toNetInput(input); const netInput = await toNetInput(input);
const out = await this.forwardInput(netInput, inputSize); const out = await this.forwardInput(netInput, inputSize);
const out0 = tf.tidy(() => tf.unstack(out)[0].expandDims()) as tf.Tensor4D; const out0 = tf.tidy(() => tf.unstack(out)[0].expandDims()) as tf.Tensor4D;
const inputDimensions = { const inputDimensions = {
width: netInput.getInputWidth(0), width: netInput.getInputWidth(0),
height: netInput.getInputHeight(0), height: netInput.getInputHeight(0),
@ -146,7 +138,6 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
boxes[idx], boxes[idx],
inputDimensions, inputDimensions,
)); ));
return detections; return detections;
} }
@ -193,7 +184,6 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
}); });
const results = [] as any; const results = [] as any;
const scoresData = await scoresTensor.array(); const scoresData = await scoresTensor.array();
const boxesData = await boxesTensor.array(); const boxesData = await boxesTensor.array();
for (let row = 0; row < numCells; row++) { for (let row = 0; row < numCells; row++) {
@ -205,15 +195,12 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
const ctY = ((row + sigmoid(boxesData[row][col][anchor][1])) / numCells) * correctionFactorY; const ctY = ((row + sigmoid(boxesData[row][col][anchor][1])) / numCells) * correctionFactorY;
const widthLocal = ((Math.exp(boxesData[row][col][anchor][2]) * this.config.anchors[anchor].x) / numCells) * correctionFactorX; const widthLocal = ((Math.exp(boxesData[row][col][anchor][2]) * this.config.anchors[anchor].x) / numCells) * correctionFactorX;
const heightLocal = ((Math.exp(boxesData[row][col][anchor][3]) * this.config.anchors[anchor].y) / numCells) * correctionFactorY; const heightLocal = ((Math.exp(boxesData[row][col][anchor][3]) * this.config.anchors[anchor].y) / numCells) * correctionFactorY;
const x = (ctX - (widthLocal / 2)); const x = (ctX - (widthLocal / 2));
const y = (ctY - (heightLocal / 2)); const y = (ctY - (heightLocal / 2));
const pos = { row, col, anchor }; const pos = { row, col, anchor };
const { classScore, label } = this.withClassScores const { classScore, label } = this.withClassScores
? await this.extractPredictedClass(classScoresTensor as tf.Tensor4D, pos) ? await this.extractPredictedClass(classScoresTensor as tf.Tensor4D, pos)
: { classScore: 1, label: 0 }; : { classScore: 1, label: 0 };
results.push({ results.push({
box: new BoundingBox(x, y, x + widthLocal, y + heightLocal), box: new BoundingBox(x, y, x + widthLocal, y + heightLocal),
score, score,
@ -229,7 +216,6 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
boxesTensor.dispose(); boxesTensor.dispose();
scoresTensor.dispose(); scoresTensor.dispose();
classScoresTensor.dispose(); classScoresTensor.dispose();
return results; return results;
} }

View File

@ -6,12 +6,10 @@ import { ConvWithBatchNorm } from './types';
export function convWithBatchNorm(x: tf.Tensor4D, params: ConvWithBatchNorm): tf.Tensor4D { export function convWithBatchNorm(x: tf.Tensor4D, params: ConvWithBatchNorm): tf.Tensor4D {
return tf.tidy(() => { return tf.tidy(() => {
let out = tf.pad(x, [[0, 0], [1, 1], [1, 1], [0, 0]]) as tf.Tensor4D; let out = tf.pad(x, [[0, 0], [1, 1], [1, 1], [0, 0]]) as tf.Tensor4D;
out = tf.conv2d(out, params.conv.filters, [1, 1], 'valid'); out = tf.conv2d(out, params.conv.filters, [1, 1], 'valid');
out = tf.sub(out, params.bn.sub); out = tf.sub(out, params.bn.sub);
out = tf.mul(out, params.bn.truediv); out = tf.mul(out, params.bn.truediv);
out = tf.add(out, params.conv.bias); out = tf.add(out, params.conv.bias);
return leaky(out); return leaky(out);
}); });
} }

View File

@ -6,10 +6,8 @@ import { leaky } from './leaky';
export function depthwiseSeparableConv(x: tf.Tensor4D, params: SeparableConvParams): tf.Tensor4D { export function depthwiseSeparableConv(x: tf.Tensor4D, params: SeparableConvParams): tf.Tensor4D {
return tf.tidy(() => { return tf.tidy(() => {
let out = tf.pad(x, [[0, 0], [1, 1], [1, 1], [0, 0]]) as tf.Tensor4D; let out = tf.pad(x, [[0, 0], [1, 1], [1, 1], [0, 0]]) as tf.Tensor4D;
out = tf.separableConv2d(out, params.depthwise_filter, params.pointwise_filter, [1, 1], 'valid'); out = tf.separableConv2d(out, params.depthwise_filter, params.pointwise_filter, [1, 1], 'valid');
out = tf.add(out, params.bias); out = tf.add(out, params.bias);
return leaky(out); return leaky(out);
}); });
} }

View File

@ -18,14 +18,12 @@ function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings
{ paramPath: `${mappedPrefix}/sub` }, { paramPath: `${mappedPrefix}/sub` },
{ paramPath: `${mappedPrefix}/truediv` }, { paramPath: `${mappedPrefix}/truediv` },
); );
return { sub, truediv }; return { sub, truediv };
} }
function extractConvWithBatchNormParams(channelsIn: number, channelsOut: number, mappedPrefix: string): ConvWithBatchNorm { function extractConvWithBatchNormParams(channelsIn: number, channelsOut: number, mappedPrefix: string): ConvWithBatchNorm {
const conv = extractConvParams(channelsIn, channelsOut, 3, `${mappedPrefix}/conv`); const conv = extractConvParams(channelsIn, channelsOut, 3, `${mappedPrefix}/conv`);
const bn = extractBatchNormParams(channelsOut, `${mappedPrefix}/bn`); const bn = extractBatchNormParams(channelsOut, `${mappedPrefix}/bn`);
return { conv, bn }; return { conv, bn };
} }
const extractSeparableConvParams = extractSeparableConvParamsFactory(extractWeights, paramMappings); const extractSeparableConvParams = extractSeparableConvParamsFactory(extractWeights, paramMappings);
@ -49,18 +47,15 @@ export function extractParams(
} = extractWeightsFactory(weights); } = extractWeightsFactory(weights);
const paramMappings: ParamMapping[] = []; const paramMappings: ParamMapping[] = [];
const { const {
extractConvParams, extractConvParams,
extractConvWithBatchNormParams, extractConvWithBatchNormParams,
extractSeparableConvParams, extractSeparableConvParams,
} = extractorsFactory(extractWeights, paramMappings); } = extractorsFactory(extractWeights, paramMappings);
let params: TinyYolov2NetParams; let params: TinyYolov2NetParams;
if (config.withSeparableConvs) { if (config.withSeparableConvs) {
const [s0, s1, s2, s3, s4, s5, s6, s7, s8] = filterSizes; const [s0, s1, s2, s3, s4, s5, s6, s7, s8] = filterSizes;
const conv0 = config.isFirstLayerConv2d const conv0 = config.isFirstLayerConv2d
? extractConvParams(s0, s1, 3, 'conv0') ? extractConvParams(s0, s1, 3, 'conv0')
: extractSeparableConvParams(s0, s1, 'conv0'); : extractSeparableConvParams(s0, s1, 'conv0');
@ -90,10 +85,8 @@ export function extractParams(
conv0, conv1, conv2, conv3, conv4, conv5, conv6, conv7, conv8, conv0, conv1, conv2, conv3, conv4, conv5, conv6, conv7, conv8,
}; };
} }
if (getRemainingWeights().length !== 0) { if (getRemainingWeights().length !== 0) {
throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`); throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
} }
return { params, paramMappings }; return { params, paramMappings };
} }

View File

@ -30,7 +30,6 @@ function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) {
} }
const extractSeparableConvParams = loadSeparableConvParamsFactory(extractWeightEntry); const extractSeparableConvParams = loadSeparableConvParamsFactory(extractWeightEntry);
return { return {
extractConvParams, extractConvParams,
extractConvWithBatchNormParams, extractConvWithBatchNormParams,
@ -81,6 +80,5 @@ export function extractParamsFromWeightMap(
} }
disposeUnusedWeightTensors(weightMap, paramMappings); disposeUnusedWeightTensors(weightMap, paramMappings);
return { params, paramMappings }; return { params, paramMappings };
} }

View File

@ -1,10 +1,8 @@
import { TinyYolov2 } from './TinyYolov2'; import { TinyYolov2 } from './TinyYolov2';
export * from './TinyYolov2Options'; export * from './TinyYolov2Options';
export * from './config'; export * from './config';
export * from './types'; export * from './types';
export { TinyYolov2 }; export { TinyYolov2 };
export function createTinyYolov2(weights: Float32Array, withSeparableConvs: boolean = true) { export function createTinyYolov2(weights: Float32Array, withSeparableConvs: boolean = true) {

View File

@ -4,6 +4,5 @@ export function leaky(x: tf.Tensor4D): tf.Tensor4D {
return tf.tidy(() => { return tf.tidy(() => {
const min = tf.mul(x, tf.scalar(0.10000000149011612)); const min = tf.mul(x, tf.scalar(0.10000000149011612));
return tf.add(tf.relu(tf.sub(x, min)), min); return tf.add(tf.relu(tf.sub(x, min)), min);
// return tf.maximum(x, min)
}); });
} }

View File

@ -55,7 +55,6 @@ export function range(num: number, start: number, step: number): number[] {
} }
export function isValidNumber(num: any) { export function isValidNumber(num: any) {
// eslint-disable-next-line no-mixed-operators
return !!num && (num !== Infinity) && (num !== -Infinity) && !Number.isNaN(num) || num === 0; return !!num && (num !== Infinity) && (num !== -Infinity) && !Number.isNaN(num) || num === 0;
} }

View File

@ -40,24 +40,19 @@ export class TinyXception extends NeuralNetwork<TinyXceptionParams> {
public forwardInput(input: NetInput): tf.Tensor4D { public forwardInput(input: NetInput): tf.Tensor4D {
const { params } = this; const { params } = this;
if (!params) { if (!params) {
throw new Error('TinyXception - load model before inference'); throw new Error('TinyXception - load model before inference');
} }
return tf.tidy(() => { return tf.tidy(() => {
const batchTensor = tf.cast(input.toBatchTensor(112, true), 'float32'); const batchTensor = tf.cast(input.toBatchTensor(112, true), 'float32');
const meanRgb = [122.782, 117.001, 104.298]; const meanRgb = [122.782, 117.001, 104.298];
const normalized = normalize(batchTensor, meanRgb).div(tf.scalar(256)) as tf.Tensor4D; const normalized = normalize(batchTensor, meanRgb).div(tf.scalar(256)) as tf.Tensor4D;
let out = tf.relu(conv(normalized, params.entry_flow.conv_in, [2, 2])); let out = tf.relu(conv(normalized, params.entry_flow.conv_in, [2, 2]));
out = reductionBlock(out, params.entry_flow.reduction_block_0, false); out = reductionBlock(out, params.entry_flow.reduction_block_0, false);
out = reductionBlock(out, params.entry_flow.reduction_block_1); out = reductionBlock(out, params.entry_flow.reduction_block_1);
range(this._numMainBlocks, 0, 1).forEach((idx) => { range(this._numMainBlocks, 0, 1).forEach((idx) => {
out = mainBlock(out, params.middle_flow[`main_block_${idx}`]); out = mainBlock(out, params.middle_flow[`main_block_${idx}`]);
}); });
out = reductionBlock(out, params.exit_flow.reduction_block); out = reductionBlock(out, params.exit_flow.reduction_block);
out = tf.relu(depthwiseSeparableConv(out, params.exit_flow.separable_conv, [1, 1])); out = tf.relu(depthwiseSeparableConv(out, params.exit_flow.separable_conv, [1, 1]));
return out; return out;

View File

@ -1 +0,0 @@
export * from './TinyXception';

View File

@ -1,6 +1,7 @@
import * as tf from '../dist/tfjs.esm'; import * as tf from '../dist/tfjs.esm';
import { ParamMapping } from './common/index'; import { ParamMapping } from './common/index';
export declare abstract class NeuralNetwork<TNetParams> { export declare abstract class NeuralNetwork<TNetParams> {
constructor(name: string);
protected _params: TNetParams | undefined; protected _params: TNetParams | undefined;
protected _paramMappings: ParamMapping[]; protected _paramMappings: ParamMapping[];
_name: any; _name: any;