add check for null face descriptor
parent
09b2e8215d
commit
8890c01162
|
@ -46,6 +46,7 @@
|
|||
"node/no-missing-import": "off",
|
||||
"node/no-unsupported-features/es-syntax": "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
|
@ -18,7 +18,7 @@
|
|||
]
|
||||
},
|
||||
"src/utils/index.ts": {
|
||||
"bytes": 1842,
|
||||
"bytes": 1793,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -843,7 +843,7 @@
|
|||
]
|
||||
},
|
||||
"src/NeuralNetwork.ts": {
|
||||
"bytes": 5289,
|
||||
"bytes": 5291,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1326,7 +1326,7 @@
|
|||
]
|
||||
},
|
||||
"src/xception/TinyXception.ts": {
|
||||
"bytes": 3148,
|
||||
"bytes": 3143,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1553,7 +1553,7 @@
|
|||
]
|
||||
},
|
||||
"src/faceLandmarkNet/index.ts": {
|
||||
"bytes": 195,
|
||||
"bytes": 194,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/faceLandmarkNet/FaceLandmark68Net.ts",
|
||||
|
@ -1609,7 +1609,7 @@
|
|||
]
|
||||
},
|
||||
"src/faceRecognitionNet/extractParamsFromWeightMap.ts": {
|
||||
"bytes": 3101,
|
||||
"bytes": 3098,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/common/index.ts",
|
||||
|
@ -1635,7 +1635,7 @@
|
|||
]
|
||||
},
|
||||
"src/faceRecognitionNet/FaceRecognitionNet.ts": {
|
||||
"bytes": 2981,
|
||||
"bytes": 2959,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1735,7 +1735,7 @@
|
|||
]
|
||||
},
|
||||
"src/ssdMobilenetv1/extractParams.ts": {
|
||||
"bytes": 8339,
|
||||
"bytes": 8330,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1748,7 +1748,7 @@
|
|||
]
|
||||
},
|
||||
"src/ssdMobilenetv1/extractParamsFromWeightMap.ts": {
|
||||
"bytes": 5782,
|
||||
"bytes": 5775,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/common/index.ts",
|
||||
|
@ -1783,11 +1783,11 @@
|
|||
]
|
||||
},
|
||||
"src/ssdMobilenetv1/nonMaxSuppression.ts": {
|
||||
"bytes": 2187,
|
||||
"bytes": 2170,
|
||||
"imports": []
|
||||
},
|
||||
"src/ssdMobilenetv1/outputLayer.ts": {
|
||||
"bytes": 2187,
|
||||
"bytes": 2183,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1796,7 +1796,7 @@
|
|||
]
|
||||
},
|
||||
"src/ssdMobilenetv1/boxPredictionLayer.ts": {
|
||||
"bytes": 617,
|
||||
"bytes": 598,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1830,7 +1830,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
||||
"bytes": 3826,
|
||||
"bytes": 3675,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1913,7 +1913,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/tinyYolov2/leaky.ts": {
|
||||
"bytes": 271,
|
||||
"bytes": 238,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1922,7 +1922,7 @@
|
|||
]
|
||||
},
|
||||
"src/tinyYolov2/convWithBatchNorm.ts": {
|
||||
"bytes": 532,
|
||||
"bytes": 530,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1935,7 +1935,7 @@
|
|||
]
|
||||
},
|
||||
"src/tinyYolov2/depthwiseSeparableConv.ts": {
|
||||
"bytes": 502,
|
||||
"bytes": 500,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1948,7 +1948,7 @@
|
|||
]
|
||||
},
|
||||
"src/tinyYolov2/extractParams.ts": {
|
||||
"bytes": 3918,
|
||||
"bytes": 3911,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1969,7 +1969,7 @@
|
|||
]
|
||||
},
|
||||
"src/tinyYolov2/extractParamsFromWeightMap.ts": {
|
||||
"bytes": 3284,
|
||||
"bytes": 3282,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/common/disposeUnusedWeightTensors.ts",
|
||||
|
@ -1990,7 +1990,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/tinyYolov2/TinyYolov2Base.ts": {
|
||||
"bytes": 9700,
|
||||
"bytes": 9606,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -2080,7 +2080,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/tinyYolov2/index.ts": {
|
||||
"bytes": 349,
|
||||
"bytes": 347,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/tinyYolov2/TinyYolov2.ts",
|
||||
|
@ -2501,7 +2501,7 @@
|
|||
]
|
||||
},
|
||||
"src/index.ts": {
|
||||
"bytes": 1024,
|
||||
"bytes": 987,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -2590,7 +2590,7 @@
|
|||
"dist/face-api.esm.js.map": {
|
||||
"imports": [],
|
||||
"inputs": {},
|
||||
"bytes": 1456658
|
||||
"bytes": 1456195
|
||||
},
|
||||
"dist/face-api.esm.js": {
|
||||
"imports": [],
|
||||
|
@ -2892,7 +2892,7 @@
|
|||
"bytesInOutput": 102
|
||||
},
|
||||
"src/NeuralNetwork.ts": {
|
||||
"bytesInOutput": 2592
|
||||
"bytesInOutput": 2606
|
||||
},
|
||||
"src/common/depthwiseSeparableConv.ts": {
|
||||
"bytesInOutput": 117
|
||||
|
@ -3048,7 +3048,7 @@
|
|||
"bytesInOutput": 397
|
||||
},
|
||||
"src/faceRecognitionNet/FaceRecognitionNet.ts": {
|
||||
"bytesInOutput": 1012
|
||||
"bytesInOutput": 1106
|
||||
},
|
||||
"src/faceRecognitionNet/index.ts": {
|
||||
"bytesInOutput": 58
|
||||
|
@ -3186,7 +3186,7 @@
|
|||
"bytesInOutput": 446
|
||||
}
|
||||
},
|
||||
"bytes": 1143701
|
||||
"bytes": 1143809
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -18,7 +18,7 @@
|
|||
]
|
||||
},
|
||||
"src/utils/index.ts": {
|
||||
"bytes": 1842,
|
||||
"bytes": 1793,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -843,7 +843,7 @@
|
|||
]
|
||||
},
|
||||
"src/NeuralNetwork.ts": {
|
||||
"bytes": 5289,
|
||||
"bytes": 5291,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1326,7 +1326,7 @@
|
|||
]
|
||||
},
|
||||
"src/xception/TinyXception.ts": {
|
||||
"bytes": 3148,
|
||||
"bytes": 3143,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1553,7 +1553,7 @@
|
|||
]
|
||||
},
|
||||
"src/faceLandmarkNet/index.ts": {
|
||||
"bytes": 195,
|
||||
"bytes": 194,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/faceLandmarkNet/FaceLandmark68Net.ts",
|
||||
|
@ -1609,7 +1609,7 @@
|
|||
]
|
||||
},
|
||||
"src/faceRecognitionNet/extractParamsFromWeightMap.ts": {
|
||||
"bytes": 3101,
|
||||
"bytes": 3098,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/common/index.ts",
|
||||
|
@ -1635,7 +1635,7 @@
|
|||
]
|
||||
},
|
||||
"src/faceRecognitionNet/FaceRecognitionNet.ts": {
|
||||
"bytes": 2981,
|
||||
"bytes": 2959,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1735,7 +1735,7 @@
|
|||
]
|
||||
},
|
||||
"src/ssdMobilenetv1/extractParams.ts": {
|
||||
"bytes": 8339,
|
||||
"bytes": 8330,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1748,7 +1748,7 @@
|
|||
]
|
||||
},
|
||||
"src/ssdMobilenetv1/extractParamsFromWeightMap.ts": {
|
||||
"bytes": 5782,
|
||||
"bytes": 5775,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/common/index.ts",
|
||||
|
@ -1783,11 +1783,11 @@
|
|||
]
|
||||
},
|
||||
"src/ssdMobilenetv1/nonMaxSuppression.ts": {
|
||||
"bytes": 2187,
|
||||
"bytes": 2170,
|
||||
"imports": []
|
||||
},
|
||||
"src/ssdMobilenetv1/outputLayer.ts": {
|
||||
"bytes": 2187,
|
||||
"bytes": 2183,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1796,7 +1796,7 @@
|
|||
]
|
||||
},
|
||||
"src/ssdMobilenetv1/boxPredictionLayer.ts": {
|
||||
"bytes": 617,
|
||||
"bytes": 598,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1830,7 +1830,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/ssdMobilenetv1/SsdMobilenetv1.ts": {
|
||||
"bytes": 3826,
|
||||
"bytes": 3675,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1913,7 +1913,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/tinyYolov2/leaky.ts": {
|
||||
"bytes": 271,
|
||||
"bytes": 238,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1922,7 +1922,7 @@
|
|||
]
|
||||
},
|
||||
"src/tinyYolov2/convWithBatchNorm.ts": {
|
||||
"bytes": 532,
|
||||
"bytes": 530,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1935,7 +1935,7 @@
|
|||
]
|
||||
},
|
||||
"src/tinyYolov2/depthwiseSeparableConv.ts": {
|
||||
"bytes": 502,
|
||||
"bytes": 500,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1948,7 +1948,7 @@
|
|||
]
|
||||
},
|
||||
"src/tinyYolov2/extractParams.ts": {
|
||||
"bytes": 3918,
|
||||
"bytes": 3911,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -1969,7 +1969,7 @@
|
|||
]
|
||||
},
|
||||
"src/tinyYolov2/extractParamsFromWeightMap.ts": {
|
||||
"bytes": 3284,
|
||||
"bytes": 3282,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/common/disposeUnusedWeightTensors.ts",
|
||||
|
@ -1990,7 +1990,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/tinyYolov2/TinyYolov2Base.ts": {
|
||||
"bytes": 9700,
|
||||
"bytes": 9606,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -2080,7 +2080,7 @@
|
|||
"imports": []
|
||||
},
|
||||
"src/tinyYolov2/index.ts": {
|
||||
"bytes": 349,
|
||||
"bytes": 347,
|
||||
"imports": [
|
||||
{
|
||||
"path": "src/tinyYolov2/TinyYolov2.ts",
|
||||
|
@ -2501,7 +2501,7 @@
|
|||
]
|
||||
},
|
||||
"src/index.ts": {
|
||||
"bytes": 1024,
|
||||
"bytes": 987,
|
||||
"imports": [
|
||||
{
|
||||
"path": "dist/tfjs.esm.js",
|
||||
|
@ -2590,7 +2590,7 @@
|
|||
"dist/face-api.js.map": {
|
||||
"imports": [],
|
||||
"inputs": {},
|
||||
"bytes": 1456665
|
||||
"bytes": 1456202
|
||||
},
|
||||
"dist/face-api.js": {
|
||||
"imports": [],
|
||||
|
@ -2774,7 +2774,7 @@
|
|||
"bytesInOutput": 101
|
||||
},
|
||||
"src/NeuralNetwork.ts": {
|
||||
"bytesInOutput": 2592
|
||||
"bytesInOutput": 2606
|
||||
},
|
||||
"src/common/depthwiseSeparableConv.ts": {
|
||||
"bytesInOutput": 117
|
||||
|
@ -2927,7 +2927,7 @@
|
|||
"bytesInOutput": 397
|
||||
},
|
||||
"src/faceRecognitionNet/FaceRecognitionNet.ts": {
|
||||
"bytesInOutput": 1012
|
||||
"bytesInOutput": 1106
|
||||
},
|
||||
"src/faceRecognitionNet/index.ts": {
|
||||
"bytesInOutput": 57
|
||||
|
@ -3065,7 +3065,7 @@
|
|||
"bytesInOutput": 446
|
||||
}
|
||||
},
|
||||
"bytes": 1143864
|
||||
"bytes": 1143972
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,10 @@ import { loadWeightMap } from './dom/index';
|
|||
import { env } from './env/index';
|
||||
|
||||
export abstract class NeuralNetwork<TNetParams> {
|
||||
constructor(name: string) {
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
protected _params: TNetParams | undefined = undefined
|
||||
|
||||
protected _paramMappings: ParamMapping[] = []
|
||||
|
@ -81,7 +85,6 @@ export abstract class NeuralNetwork<TNetParams> {
|
|||
this.extractWeights(weightsOrUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.loadFromUri(weightsOrUrl);
|
||||
}
|
||||
|
||||
|
@ -89,7 +92,6 @@ export abstract class NeuralNetwork<TNetParams> {
|
|||
if (uri && typeof uri !== 'string') {
|
||||
throw new Error(`${this._name}.loadFromUri - expected model uri`);
|
||||
}
|
||||
|
||||
const weightMap = await loadWeightMap(uri, this.getDefaultModelName());
|
||||
this.loadFromWeightMap(weightMap);
|
||||
}
|
||||
|
@ -98,37 +100,23 @@ export abstract class NeuralNetwork<TNetParams> {
|
|||
if (filePath && typeof filePath !== 'string') {
|
||||
throw new Error(`${this._name}.loadFromDisk - expected model file path`);
|
||||
}
|
||||
|
||||
const { readFile } = env.getEnv();
|
||||
|
||||
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 manifest = JSON.parse((await readFile(manifestUri)).toString());
|
||||
const weightMap = await loadWeights(manifest, modelBaseUri);
|
||||
|
||||
this.loadFromWeightMap(weightMap);
|
||||
}
|
||||
|
||||
public loadFromWeightMap(weightMap: tf.NamedTensorMap) {
|
||||
const {
|
||||
paramMappings,
|
||||
params,
|
||||
} = this.extractParamsFromWeightMap(weightMap);
|
||||
|
||||
const { paramMappings, params } = this.extractParamsFromWeightMap(weightMap);
|
||||
this._paramMappings = paramMappings;
|
||||
this._params = params;
|
||||
}
|
||||
|
||||
public extractWeights(weights: Float32Array) {
|
||||
const {
|
||||
paramMappings,
|
||||
params,
|
||||
} = this.extractParams(weights);
|
||||
|
||||
const { paramMappings, params } = this.extractParams(weights);
|
||||
this._paramMappings = paramMappings;
|
||||
this._params = params;
|
||||
}
|
||||
|
@ -143,7 +131,6 @@ export abstract class NeuralNetwork<TNetParams> {
|
|||
if (!res.nextObj.hasOwnProperty(objProp)) {
|
||||
throw new Error(`traversePropertyPath - object does not have property ${objProp}, for path ${paramPath}`);
|
||||
}
|
||||
|
||||
return { obj: res.nextObj, objProp, nextObj: res.nextObj[objProp] };
|
||||
}, { nextObj: this.params });
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
export class PlatformBrowser {
|
||||
private textEncoder: TextEncoder;
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
fetch(path: string, init?: RequestInit): Promise<Response> {
|
||||
fetch(path: string, init?: any): Promise<Response> {
|
||||
return fetch(path, init);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,5 +2,4 @@ import { FaceLandmark68Net } from './FaceLandmark68Net';
|
|||
|
||||
export * from './FaceLandmark68Net';
|
||||
export * from './FaceLandmark68TinyNet';
|
||||
|
||||
export class FaceLandmarkNet extends FaceLandmark68Net {}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from './FaceProcessor';
|
|
@ -22,7 +22,6 @@ export class FaceRecognitionNet extends NeuralNetwork<NetParams> {
|
|||
}
|
||||
|
||||
return tf.tidy(() => {
|
||||
// const batchTensor = input.toBatchTensor(150, true).toFloat()
|
||||
const batchTensor = tf.cast(input.toBatchTensor(150, true), 'float32');
|
||||
|
||||
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[]> {
|
||||
// When faces have a detected dimension of 0, tensorflow will crash the whole process.
|
||||
// Sidestep this by returning an empty descriptor instead.
|
||||
if (input.shape.some(dimension => dimension <= 0) return new Float32Array(128);
|
||||
|
||||
if (input?.shape?.some((dim) => dim <= 0)) return new Float32Array(128);
|
||||
const netInput = await toNetInput(input);
|
||||
|
||||
const faceDescriptorTensors = tf.tidy(
|
||||
() => 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());
|
||||
|
||||
return netInput.isBatchInput
|
||||
? faceDescriptorsForBatch
|
||||
: faceDescriptorsForBatch[0];
|
||||
return netInput.isBatchInput ? faceDescriptorsForBatch : faceDescriptorsForBatch[0];
|
||||
}
|
||||
|
||||
protected getDefaultModelName(): string {
|
||||
|
|
|
@ -2,9 +2,7 @@ import * as tf from '../../dist/tfjs.esm';
|
|||
|
||||
import { disposeUnusedWeightTensors, extractWeightEntryFactory, ParamMapping } from '../common/index';
|
||||
import { isTensor2D } from '../utils/index';
|
||||
import {
|
||||
ConvLayerParams, NetParams, ResidualLayerParams, ScaleLayerParams,
|
||||
} from './types';
|
||||
import { ConvLayerParams, NetParams, ResidualLayerParams, ScaleLayerParams } from './types';
|
||||
|
||||
function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) {
|
||||
const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings);
|
||||
|
|
|
@ -23,6 +23,5 @@ export * from './NeuralNetwork';
|
|||
export * from './resizeResults';
|
||||
|
||||
const node = (typeof process !== 'undefined');
|
||||
// eslint-disable-next-line no-undef
|
||||
const browser = (typeof navigator !== 'undefined') && (typeof navigator.userAgent !== 'undefined');
|
||||
export const version = { faceapi: pkg.version as string, node, browser };
|
||||
|
|
|
@ -26,9 +26,7 @@ export class SsdMobilenetv1 extends NeuralNetwork<NetParams> {
|
|||
}
|
||||
|
||||
return tf.tidy(() => {
|
||||
// const batchTensor = input.toBatchTensor(512, false).toFloat()
|
||||
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 features = mobileNetV1(x, params.mobilenetv1);
|
||||
|
||||
|
@ -58,7 +56,6 @@ export class SsdMobilenetv1 extends NeuralNetwork<NetParams> {
|
|||
scores: _scores,
|
||||
} = this.forwardInput(netInput);
|
||||
|
||||
// TODO batches
|
||||
const boxes = _boxes[0];
|
||||
const scores = _scores[0];
|
||||
for (let i = 1; i < _boxes.length; i++) {
|
||||
|
@ -66,9 +63,7 @@ export class SsdMobilenetv1 extends NeuralNetwork<NetParams> {
|
|||
_scores[i].dispose();
|
||||
}
|
||||
|
||||
// TODO find a better way to filter by minConfidence
|
||||
const scoresData = Array.from(await scores.data());
|
||||
|
||||
const iouThreshold = 0.5;
|
||||
const indices = nonMaxSuppression(
|
||||
boxes,
|
||||
|
@ -111,7 +106,6 @@ export class SsdMobilenetv1 extends NeuralNetwork<NetParams> {
|
|||
|
||||
boxes.dispose();
|
||||
scores.dispose();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ export function boxPredictionLayer(
|
|||
) {
|
||||
return tf.tidy(() => {
|
||||
const batchSize = x.shape[0];
|
||||
|
||||
const boxPredictionEncoding = tf.reshape(
|
||||
convLayer(x, params.box_encoding_predictor),
|
||||
[batchSize, -1, 1, 4],
|
||||
|
@ -18,10 +17,6 @@ export function boxPredictionLayer(
|
|||
convLayer(x, params.class_predictor),
|
||||
[batchSize, -1, 3],
|
||||
);
|
||||
|
||||
return {
|
||||
boxPredictionEncoding,
|
||||
classPrediction,
|
||||
};
|
||||
return { boxPredictionEncoding, classPrediction };
|
||||
});
|
||||
}
|
||||
|
|
|
@ -83,7 +83,6 @@ function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings
|
|||
|
||||
function extractMobilenetV1Params(): MobileNetV1.Params {
|
||||
const conv_0 = extractPointwiseConvParams(3, 32, 3, 'mobilenetv1/conv_0');
|
||||
|
||||
const conv_1 = extractConvPairParams(32, 64, 'mobilenetv1/conv_1');
|
||||
const conv_2 = extractConvPairParams(64, 128, 'mobilenetv1/conv_2');
|
||||
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_12 = extractConvPairParams(512, 1024, 'mobilenetv1/conv_12');
|
||||
const conv_13 = extractConvPairParams(1024, 1024, 'mobilenetv1/conv_13');
|
||||
|
||||
return {
|
||||
conv_0,
|
||||
conv_1,
|
||||
|
@ -125,7 +123,6 @@ function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings
|
|||
const conv_5 = extractPointwiseConvParams(128, 256, 3, 'prediction_layer/conv_5');
|
||||
const conv_6 = extractPointwiseConvParams(256, 64, 1, 'prediction_layer/conv_6');
|
||||
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 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');
|
||||
|
@ -163,7 +160,6 @@ function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings
|
|||
box_encoding_predictor: box_encoding_5_predictor,
|
||||
class_predictor: class_predictor_5,
|
||||
};
|
||||
|
||||
return {
|
||||
conv_0,
|
||||
conv_1,
|
||||
|
@ -190,17 +186,14 @@ function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings
|
|||
|
||||
export function extractParams(weights: Float32Array): { params: NetParams, paramMappings: ParamMapping[] } {
|
||||
const paramMappings: ParamMapping[] = [];
|
||||
|
||||
const {
|
||||
extractWeights,
|
||||
getRemainingWeights,
|
||||
} = extractWeightsFactory(weights);
|
||||
|
||||
const {
|
||||
extractMobilenetV1Params,
|
||||
extractPredictionLayerParams,
|
||||
} = extractorsFactory(extractWeights, paramMappings);
|
||||
|
||||
const mobilenetv1 = extractMobilenetV1Params();
|
||||
const prediction_layer = extractPredictionLayerParams();
|
||||
const extra_dim = tf.tensor3d(
|
||||
|
@ -210,9 +203,7 @@ export function extractParams(weights: Float32Array): { params: NetParams, param
|
|||
const output_layer = {
|
||||
extra_dim,
|
||||
};
|
||||
|
||||
paramMappings.push({ paramPath: 'output_layer/extra_dim' });
|
||||
|
||||
if (getRemainingWeights().length !== 0) {
|
||||
throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) {
|
|||
function extractPointwiseConvParams(prefix: string, idx: number, mappedPrefix: string): PointwiseConvParams {
|
||||
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`);
|
||||
|
||||
return { filters, batch_norm_offset };
|
||||
}
|
||||
|
||||
|
@ -64,7 +63,6 @@ function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) {
|
|||
function extractConvParams(prefix: string, mappedPrefix: string): ConvParams {
|
||||
const filters = extractWeightEntry(`${prefix}/weights`, 4, `${mappedPrefix}/filters`);
|
||||
const bias = extractWeightEntry(`${prefix}/biases`, 1, `${mappedPrefix}/bias`);
|
||||
|
||||
return { filters, bias };
|
||||
}
|
||||
|
||||
|
@ -77,7 +75,6 @@ function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) {
|
|||
`Prediction/BoxPredictor_${idx}/ClassPredictor`,
|
||||
`prediction_layer/box_predictor_${idx}/class_predictor`,
|
||||
);
|
||||
|
||||
return { box_encoding_predictor, class_predictor };
|
||||
}
|
||||
|
||||
|
@ -110,15 +107,12 @@ export function extractParamsFromWeightMap(
|
|||
weightMap: tf.NamedTensorMap,
|
||||
): { params: NetParams, paramMappings: ParamMapping[] } {
|
||||
const paramMappings: ParamMapping[] = [];
|
||||
|
||||
const {
|
||||
extractMobilenetV1Params,
|
||||
extractPredictionLayerParams,
|
||||
} = extractorsFactory(weightMap, paramMappings);
|
||||
|
||||
const extra_dim = weightMap['Output/extra_dim'];
|
||||
paramMappings.push({ originalPath: 'Output/extra_dim', paramPath: 'output_layer/extra_dim' });
|
||||
|
||||
if (!isTensor3D(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);
|
||||
|
||||
return { params, paramMappings };
|
||||
}
|
||||
|
|
|
@ -43,15 +43,11 @@ export function nonMaxSuppression(
|
|||
.sort((c1, c2) => c2.score - c1.score);
|
||||
|
||||
const suppressFunc = (x: number) => (x <= iouThreshold ? 1 : 0);
|
||||
|
||||
const selected: number[] = [];
|
||||
|
||||
candidates.forEach((c) => {
|
||||
if (selected.length >= outputSize) {
|
||||
return;
|
||||
}
|
||||
if (selected.length >= outputSize) return;
|
||||
const originalScore = c.score;
|
||||
|
||||
for (let j = selected.length - 1; j >= 0; --j) {
|
||||
const iou = IOU(boxes, c.boxIndex, selected[j]);
|
||||
if (iou === 0.0) continue;
|
||||
|
@ -62,6 +58,5 @@ export function nonMaxSuppression(
|
|||
selected.push(c.boxIndex);
|
||||
}
|
||||
});
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
|
|
@ -9,12 +9,10 @@ function getCenterCoordinatesAndSizesLayer(x: tf.Tensor2D) {
|
|||
tf.sub(vec[2], vec[0]),
|
||||
tf.sub(vec[3], vec[1]),
|
||||
];
|
||||
|
||||
const centers = [
|
||||
tf.add(vec[0], tf.div(sizes[0], tf.scalar(2))),
|
||||
tf.add(vec[1], tf.div(sizes[1], tf.scalar(2))),
|
||||
];
|
||||
|
||||
return {
|
||||
sizes,
|
||||
centers,
|
||||
|
@ -28,10 +26,8 @@ function decodeBoxesLayer(x0: tf.Tensor2D, x1: tf.Tensor2D) {
|
|||
} = getCenterCoordinatesAndSizesLayer(x0);
|
||||
|
||||
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 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 add1_out = tf.add(tf.mul(tf.div(vec[1], tf.scalar(10)), sizes[1]), centers[1]);
|
||||
|
||||
|
|
|
@ -22,9 +22,7 @@ import { ITinyYolov2Options, TinyYolov2Options } from './TinyYolov2Options';
|
|||
import { DefaultTinyYolov2NetParams, MobilenetParams, TinyYolov2NetParams } from './types';
|
||||
|
||||
export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
|
||||
public static DEFAULT_FILTER_SIZES = [
|
||||
3, 16, 32, 64, 128, 256, 512, 1024, 1024,
|
||||
]
|
||||
public static DEFAULT_FILTER_SIZES = [3, 16, 32, 64, 128, 256, 512, 1024, 1024];
|
||||
|
||||
private _config: TinyYolov2Config
|
||||
|
||||
|
@ -61,7 +59,6 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
|
|||
out = tf.maxPool(out, [2, 2], [1, 1], 'same');
|
||||
out = convWithBatchNorm(out, params.conv6);
|
||||
out = convWithBatchNorm(out, params.conv7);
|
||||
|
||||
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 = params.conv6 ? depthwiseSeparableConv(out, params.conv6) : out;
|
||||
out = params.conv7 ? depthwiseSeparableConv(out, params.conv7) : out;
|
||||
|
||||
return convLayer(out, params.conv8, 'valid', false);
|
||||
}
|
||||
|
||||
|
@ -94,13 +90,11 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
|
|||
}
|
||||
|
||||
return tf.tidy(() => {
|
||||
// let batchTensor = input.toBatchTensor(inputSize, false).toFloat()
|
||||
let batchTensor = tf.cast(input.toBatchTensor(inputSize, false), 'float32');
|
||||
batchTensor = this.config.meanRgb
|
||||
? normalize(batchTensor, this.config.meanRgb)
|
||||
: batchTensor;
|
||||
batchTensor = batchTensor.div(tf.scalar(256)) as tf.Tensor4D;
|
||||
|
||||
return this.config.withSeparableConvs
|
||||
? this.runMobilenet(batchTensor, params as MobilenetParams)
|
||||
: 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[]> {
|
||||
const { inputSize, scoreThreshold } = new TinyYolov2Options(forwardParams);
|
||||
|
||||
const netInput = await toNetInput(input);
|
||||
const out = await this.forwardInput(netInput, inputSize);
|
||||
const out0 = tf.tidy(() => tf.unstack(out)[0].expandDims()) as tf.Tensor4D;
|
||||
|
||||
const inputDimensions = {
|
||||
width: netInput.getInputWidth(0),
|
||||
height: netInput.getInputHeight(0),
|
||||
|
@ -146,7 +138,6 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
|
|||
boxes[idx],
|
||||
inputDimensions,
|
||||
));
|
||||
|
||||
return detections;
|
||||
}
|
||||
|
||||
|
@ -193,7 +184,6 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
|
|||
});
|
||||
|
||||
const results = [] as any;
|
||||
|
||||
const scoresData = await scoresTensor.array();
|
||||
const boxesData = await boxesTensor.array();
|
||||
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 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 x = (ctX - (widthLocal / 2));
|
||||
const y = (ctY - (heightLocal / 2));
|
||||
|
||||
const pos = { row, col, anchor };
|
||||
const { classScore, label } = this.withClassScores
|
||||
? await this.extractPredictedClass(classScoresTensor as tf.Tensor4D, pos)
|
||||
: { classScore: 1, label: 0 };
|
||||
|
||||
results.push({
|
||||
box: new BoundingBox(x, y, x + widthLocal, y + heightLocal),
|
||||
score,
|
||||
|
@ -229,7 +216,6 @@ export class TinyYolov2Base extends NeuralNetwork<TinyYolov2NetParams> {
|
|||
boxesTensor.dispose();
|
||||
scoresTensor.dispose();
|
||||
classScoresTensor.dispose();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,10 @@ import { ConvWithBatchNorm } from './types';
|
|||
export function convWithBatchNorm(x: tf.Tensor4D, params: ConvWithBatchNorm): tf.Tensor4D {
|
||||
return tf.tidy(() => {
|
||||
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.sub(out, params.bn.sub);
|
||||
out = tf.mul(out, params.bn.truediv);
|
||||
out = tf.add(out, params.conv.bias);
|
||||
|
||||
return leaky(out);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,10 +6,8 @@ import { leaky } from './leaky';
|
|||
export function depthwiseSeparableConv(x: tf.Tensor4D, params: SeparableConvParams): tf.Tensor4D {
|
||||
return tf.tidy(() => {
|
||||
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.add(out, params.bias);
|
||||
|
||||
return leaky(out);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,14 +18,12 @@ function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings
|
|||
{ paramPath: `${mappedPrefix}/sub` },
|
||||
{ paramPath: `${mappedPrefix}/truediv` },
|
||||
);
|
||||
|
||||
return { sub, truediv };
|
||||
}
|
||||
|
||||
function extractConvWithBatchNormParams(channelsIn: number, channelsOut: number, mappedPrefix: string): ConvWithBatchNorm {
|
||||
const conv = extractConvParams(channelsIn, channelsOut, 3, `${mappedPrefix}/conv`);
|
||||
const bn = extractBatchNormParams(channelsOut, `${mappedPrefix}/bn`);
|
||||
|
||||
return { conv, bn };
|
||||
}
|
||||
const extractSeparableConvParams = extractSeparableConvParamsFactory(extractWeights, paramMappings);
|
||||
|
@ -49,18 +47,15 @@ export function extractParams(
|
|||
} = extractWeightsFactory(weights);
|
||||
|
||||
const paramMappings: ParamMapping[] = [];
|
||||
|
||||
const {
|
||||
extractConvParams,
|
||||
extractConvWithBatchNormParams,
|
||||
extractSeparableConvParams,
|
||||
} = extractorsFactory(extractWeights, paramMappings);
|
||||
|
||||
let params: TinyYolov2NetParams;
|
||||
|
||||
if (config.withSeparableConvs) {
|
||||
const [s0, s1, s2, s3, s4, s5, s6, s7, s8] = filterSizes;
|
||||
|
||||
const conv0 = config.isFirstLayerConv2d
|
||||
? extractConvParams(s0, s1, 3, 'conv0')
|
||||
: extractSeparableConvParams(s0, s1, 'conv0');
|
||||
|
@ -90,10 +85,8 @@ export function extractParams(
|
|||
conv0, conv1, conv2, conv3, conv4, conv5, conv6, conv7, conv8,
|
||||
};
|
||||
}
|
||||
|
||||
if (getRemainingWeights().length !== 0) {
|
||||
throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
|
||||
}
|
||||
|
||||
return { params, paramMappings };
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) {
|
|||
}
|
||||
|
||||
const extractSeparableConvParams = loadSeparableConvParamsFactory(extractWeightEntry);
|
||||
|
||||
return {
|
||||
extractConvParams,
|
||||
extractConvWithBatchNormParams,
|
||||
|
@ -81,6 +80,5 @@ export function extractParamsFromWeightMap(
|
|||
}
|
||||
|
||||
disposeUnusedWeightTensors(weightMap, paramMappings);
|
||||
|
||||
return { params, paramMappings };
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import { TinyYolov2 } from './TinyYolov2';
|
||||
|
||||
export * from './TinyYolov2Options';
|
||||
|
||||
export * from './config';
|
||||
export * from './types';
|
||||
|
||||
export { TinyYolov2 };
|
||||
|
||||
export function createTinyYolov2(weights: Float32Array, withSeparableConvs: boolean = true) {
|
||||
|
|
|
@ -4,6 +4,5 @@ export function leaky(x: tf.Tensor4D): tf.Tensor4D {
|
|||
return tf.tidy(() => {
|
||||
const min = tf.mul(x, tf.scalar(0.10000000149011612));
|
||||
return tf.add(tf.relu(tf.sub(x, min)), min);
|
||||
// return tf.maximum(x, min)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ export function range(num: number, start: number, step: number): number[] {
|
|||
}
|
||||
|
||||
export function isValidNumber(num: any) {
|
||||
// eslint-disable-next-line no-mixed-operators
|
||||
return !!num && (num !== Infinity) && (num !== -Infinity) && !Number.isNaN(num) || num === 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,24 +40,19 @@ export class TinyXception extends NeuralNetwork<TinyXceptionParams> {
|
|||
|
||||
public forwardInput(input: NetInput): tf.Tensor4D {
|
||||
const { params } = this;
|
||||
|
||||
if (!params) {
|
||||
throw new Error('TinyXception - load model before inference');
|
||||
}
|
||||
|
||||
return tf.tidy(() => {
|
||||
const batchTensor = tf.cast(input.toBatchTensor(112, true), 'float32');
|
||||
const meanRgb = [122.782, 117.001, 104.298];
|
||||
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]));
|
||||
out = reductionBlock(out, params.entry_flow.reduction_block_0, false);
|
||||
out = reductionBlock(out, params.entry_flow.reduction_block_1);
|
||||
|
||||
range(this._numMainBlocks, 0, 1).forEach((idx) => {
|
||||
out = mainBlock(out, params.middle_flow[`main_block_${idx}`]);
|
||||
});
|
||||
|
||||
out = reductionBlock(out, params.exit_flow.reduction_block);
|
||||
out = tf.relu(depthwiseSeparableConv(out, params.exit_flow.separable_conv, [1, 1]));
|
||||
return out;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from './TinyXception';
|
|
@ -1,6 +1,7 @@
|
|||
import * as tf from '../dist/tfjs.esm';
|
||||
import { ParamMapping } from './common/index';
|
||||
export declare abstract class NeuralNetwork<TNetParams> {
|
||||
constructor(name: string);
|
||||
protected _params: TNetParams | undefined;
|
||||
protected _paramMappings: ParamMapping[];
|
||||
_name: any;
|
||||
|
|
Loading…
Reference in New Issue