From e6e6e92b3ffcb2bef629c02f4d437fbe4f10af0b Mon Sep 17 00:00:00 2001 From: Soh Jun Fu Date: Wed, 11 Dec 2019 14:44:46 +0800 Subject: [PATCH 01/22] Proper linking of High Level API --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7febcd3f..7ac1de14 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ * **[face-api.js for Nodejs](#face-api.js-for-nodejs)** * **[Usage](#getting-started)** * **[Loading the Models](#getting-started-loading-models)** - * **[High Level API](#getting-started-high-level-api)** + * **[High Level API](#high-level-api)** * **[Displaying Detection Results](#getting-started-displaying-detection-results)** * **[Face Detection Options](#getting-started-face-detection-options)** * **[Utility Classes](#getting-started-utility-classes)** From 19836c13f8eaa00b922338909842611e977d915e Mon Sep 17 00:00:00 2001 From: vincent Date: Sat, 14 Dec 2019 14:30:16 +0100 Subject: [PATCH 02/22] copy contents of tfjs-image-recognition-base to face-api.js + remove dependency --- karma.conf.js | 1 + package-lock.json | 15 +- package.json | 1 - src/NeuralNetwork.ts | 159 +++++++++++ src/ageGenderNet/AgeGenderNet.ts | 3 +- src/ageGenderNet/extractParams.ts | 11 +- .../extractParamsFromWeigthMap.ts | 12 +- src/ageGenderNet/types.ts | 7 +- src/classes/BoundingBox.ts | 14 + src/classes/Box.ts | 175 ++++++++++++ src/classes/Dimensions.ts | 28 ++ src/classes/FaceDetection.ts | 5 +- src/classes/FaceLandmarks.ts | 10 +- src/classes/FaceLandmarks5.ts | 5 +- src/classes/FaceLandmarks68.ts | 6 +- src/classes/FaceMatch.ts | 2 +- src/classes/LabeledBox.ts | 25 ++ src/classes/ObjectDetection.ts | 44 +++ src/classes/Point.ts | 45 +++ src/classes/PredictedBox.ts | 31 +++ src/classes/Rect.ts | 14 + src/classes/index.ts | 10 +- src/common/convLayer.ts | 19 ++ src/common/depthwiseSeparableConv.ts | 5 +- src/common/disposeUnusedWeightTensors.ts | 9 + src/common/extractConvParamsFactory.ts | 31 +++ src/common/extractFCParamsFactory.ts | 31 +++ .../extractSeparableConvParamsFactory.ts | 46 ++++ src/common/extractWeightEntryFactory.ts | 20 ++ src/common/extractWeightsFactory.ts | 18 ++ src/common/fullyConnectedLayer.ts | 5 +- src/common/getModelUris.ts | 33 +++ src/common/index.ts | 10 + src/common/loadConvParamsFactory.ts | 5 +- src/common/types.ts | 26 ++ src/dom/NetInput.ts | 153 +++++++++++ src/dom/awaitMediaLoaded.ts | 28 ++ src/dom/bufferToImage.ts | 23 ++ src/dom/createCanvas.ts | 33 +++ src/dom/extractFaceTensors.ts | 3 +- src/dom/extractFaces.ts | 17 +- src/dom/fetchImage.ts | 12 + src/dom/fetchJson.ts | 5 + src/dom/fetchNetWeights.ts | 5 + src/dom/fetchOrThrow.ts | 14 + src/dom/getContext2dOrThrow.ts | 24 ++ src/dom/getMediaDimensions.ts | 15 + src/dom/imageTensorToCanvas.ts | 20 ++ src/dom/imageToSquare.ts | 28 ++ src/dom/index.ts | 21 +- src/dom/isMediaElement.ts | 10 + src/dom/isMediaLoaded.ts | 9 + src/dom/loadWeightMap.ts | 15 + src/dom/matchDimensions.ts | 11 + src/dom/resolveInput.ts | 8 + src/dom/toNetInput.ts | 57 ++++ src/dom/types.ts | 11 + src/draw/DrawBox.ts | 59 ++++ src/draw/DrawFaceLandmarks.ts | 4 +- src/draw/DrawTextField.ts | 108 ++++++++ src/draw/drawContour.ts | 2 +- src/draw/drawDetections.ts | 7 +- src/draw/drawFaceExpressions.ts | 7 +- src/draw/index.ts | 4 +- src/env/createBrowserEnv.ts | 24 ++ src/env/createFileSystem.ts | 30 ++ src/env/createNodejsEnv.ts | 40 +++ src/env/index.ts | 67 +++++ src/env/isBrowser.ts | 9 + src/env/isNodejs.ts | 8 + src/env/types.ts | 14 + src/faceExpressionNet/FaceExpressionNet.ts | 2 +- .../FaceFeatureExtractor.ts | 4 +- .../TinyFaceFeatureExtractor.ts | 4 +- src/faceFeatureExtractor/denseBlock.ts | 10 +- src/faceFeatureExtractor/extractParams.ts | 10 +- .../extractParamsFromWeigthMap.ts | 8 +- .../extractParamsFromWeigthMapTiny.ts | 8 +- src/faceFeatureExtractor/extractParamsTiny.ts | 10 +- src/faceFeatureExtractor/extractorsFactory.ts | 14 +- src/faceFeatureExtractor/loadParamsFactory.ts | 9 +- src/faceFeatureExtractor/types.ts | 13 +- src/faceLandmarkNet/FaceLandmark68NetBase.ts | 4 +- src/faceProcessor/FaceProcessor.ts | 3 +- src/faceProcessor/extractParams.ts | 11 +- .../extractParamsFromWeigthMap.ts | 12 +- src/faceProcessor/types.ts | 4 +- src/faceRecognitionNet/FaceRecognitionNet.ts | 4 +- src/faceRecognitionNet/extractParams.ts | 13 +- .../extractParamsFromWeigthMap.ts | 13 +- src/faceRecognitionNet/types.ts | 5 +- src/factories/WithGender.ts | 3 +- src/globalApi/ComputeFaceDescriptorsTasks.ts | 3 +- src/globalApi/DetectFaceLandmarksTasks.ts | 3 +- src/globalApi/DetectFacesTasks.ts | 6 +- src/globalApi/PredictAgeAndGenderTask.ts | 2 +- src/globalApi/PredictFaceExpressionsTask.ts | 2 +- src/globalApi/allFaces.ts | 8 +- src/globalApi/detectFaces.ts | 3 +- .../extractFacesAndComputeResults.ts | 3 +- src/globalApi/nets.ts | 7 +- src/globalApi/types.ts | 6 +- src/index.ts | 14 +- src/mtcnn/Mtcnn.ts | 4 +- src/mtcnn/MtcnnBox.ts | 2 +- src/mtcnn/ONet.ts | 4 +- src/mtcnn/PNet.ts | 6 +- src/mtcnn/extractImagePatches.ts | 11 +- src/mtcnn/extractParams.ts | 20 +- src/mtcnn/extractParamsFromWeigthMap.ts | 16 +- src/mtcnn/pyramidDown.ts | 2 +- src/mtcnn/sharedLayers.ts | 8 +- src/mtcnn/stage1.ts | 3 +- src/mtcnn/stage2.ts | 3 +- src/mtcnn/stage3.ts | 3 +- src/mtcnn/types.ts | 28 +- src/ops/index.ts | 14 + src/ops/iou.ts | 11 + src/{ => ops}/minBbox.ts | 2 +- src/ops/nonMaxSuppression.ts | 41 +++ src/ops/normalize.ts | 13 + src/ops/padToSquare.ts | 48 ++++ src/ops/shuffleArray.ts | 10 + src/resizeResults.ts | 3 +- src/ssdMobilenetv1/SsdMobilenetv1.ts | 4 +- src/ssdMobilenetv1/boxPredictionLayer.ts | 6 +- src/ssdMobilenetv1/extractParams.ts | 12 +- .../extractParamsFromWeigthMap.ts | 15 +- src/ssdMobilenetv1/types.ts | 7 +- src/tinyFaceDetector/TinyFaceDetector.ts | 14 +- .../TinyFaceDetectorOptions.ts | 6 +- src/tinyFaceDetector/const.ts | 2 +- src/tinyYolov2/TinyYolov2.ts | 14 +- src/tinyYolov2/TinyYolov2Base.ts | 256 ++++++++++++++++++ src/tinyYolov2/TinyYolov2Options.ts | 34 +++ src/tinyYolov2/config.ts | 55 ++++ src/tinyYolov2/const.ts | 2 +- src/tinyYolov2/convWithBatchNorm.ts | 17 ++ src/tinyYolov2/depthwiseSeparableConv.ts | 15 + src/tinyYolov2/extractParams.ts | 101 +++++++ src/tinyYolov2/extractParamsFromWeigthMap.ts | 88 ++++++ src/tinyYolov2/index.ts | 5 + src/tinyYolov2/leaky.ts | 9 + src/tinyYolov2/types.ts | 40 +++ src/utils/index.ts | 63 +++++ src/xception/TinyXception.ts | 20 +- src/xception/extractParams.ts | 17 +- src/xception/extractParamsFromWeigthMap.ts | 20 +- src/xception/types.ts | 18 +- test/Environment.ts | 2 +- test/NeuralNetwork.test.ts | 239 ++++++++++++++++ test/data/boxes.json | 1 + test/data/dummy.weights | 1 + test/env.node.ts | 3 +- test/env.ts | 3 +- test/images/white.png | Bin 0 -> 450 bytes test/tests/classes/BoundingBox.test.ts | 22 ++ test/tests/classes/Box.test.ts | 107 ++++++++ test/tests/classes/Rect.test.ts | 52 ++++ test/tests/common/getModelUris.test.ts | 68 +++++ test/tests/dom/NetInput.test.ts | 87 ++++++ test/tests/dom/fetchImage.browser.test.ts | 25 ++ test/tests/dom/fetchJson.browser.test.ts | 10 + .../tests/dom/fetchNetWeights.browser.test.ts | 11 + test/tests/dom/fetchOrThrow.browser.test.ts | 19 ++ test/tests/dom/toNetInput.test.ts | 115 ++++++++ .../faceLandmarkNet/faceLandmark68Net.test.ts | 5 +- .../faceLandmark68Net.uncompressed.test.ts | 3 +- .../faceLandmark68TinyNet.test.ts | 5 +- .../faceRecognitionNet.test.ts | 3 +- .../faceRecognitionNet.uncompressed.test.ts | 3 +- .../tests/factories/WithFaceDetection.test.ts | 4 +- .../tests/factories/WithFaceLandmarks.test.ts | 6 +- test/tests/globalApi/FaceMatcher.test.ts | 2 +- test/tests/globalApi/detectAllFaces.test.ts | 48 +--- test/tests/globalApi/detectSingleFace.test.ts | 61 ++--- test/tests/ops/iou.test.ts | 42 +++ test/tests/ops/padToSquare.test.ts | 131 +++++++++ test/tests/resizeResults.test.ts | 17 +- test/utils.ts | 65 ++++- test/utils/index.test.ts | 40 +++ 181 files changed, 3886 insertions(+), 413 deletions(-) create mode 100644 src/NeuralNetwork.ts create mode 100644 src/classes/BoundingBox.ts create mode 100644 src/classes/Box.ts create mode 100644 src/classes/Dimensions.ts create mode 100644 src/classes/LabeledBox.ts create mode 100644 src/classes/ObjectDetection.ts create mode 100644 src/classes/Point.ts create mode 100644 src/classes/PredictedBox.ts create mode 100644 src/classes/Rect.ts create mode 100644 src/common/convLayer.ts create mode 100644 src/common/disposeUnusedWeightTensors.ts create mode 100644 src/common/extractConvParamsFactory.ts create mode 100644 src/common/extractFCParamsFactory.ts create mode 100644 src/common/extractSeparableConvParamsFactory.ts create mode 100644 src/common/extractWeightEntryFactory.ts create mode 100644 src/common/extractWeightsFactory.ts create mode 100644 src/common/getModelUris.ts create mode 100644 src/common/index.ts create mode 100644 src/common/types.ts create mode 100644 src/dom/NetInput.ts create mode 100644 src/dom/awaitMediaLoaded.ts create mode 100644 src/dom/bufferToImage.ts create mode 100644 src/dom/createCanvas.ts create mode 100644 src/dom/fetchImage.ts create mode 100644 src/dom/fetchJson.ts create mode 100644 src/dom/fetchNetWeights.ts create mode 100644 src/dom/fetchOrThrow.ts create mode 100644 src/dom/getContext2dOrThrow.ts create mode 100644 src/dom/getMediaDimensions.ts create mode 100644 src/dom/imageTensorToCanvas.ts create mode 100644 src/dom/imageToSquare.ts create mode 100644 src/dom/isMediaElement.ts create mode 100644 src/dom/isMediaLoaded.ts create mode 100644 src/dom/loadWeightMap.ts create mode 100644 src/dom/matchDimensions.ts create mode 100644 src/dom/resolveInput.ts create mode 100644 src/dom/toNetInput.ts create mode 100644 src/dom/types.ts create mode 100644 src/draw/DrawBox.ts create mode 100644 src/draw/DrawTextField.ts create mode 100644 src/env/createBrowserEnv.ts create mode 100644 src/env/createFileSystem.ts create mode 100644 src/env/createNodejsEnv.ts create mode 100644 src/env/index.ts create mode 100644 src/env/isBrowser.ts create mode 100644 src/env/isNodejs.ts create mode 100644 src/env/types.ts create mode 100644 src/ops/index.ts create mode 100644 src/ops/iou.ts rename src/{ => ops}/minBbox.ts (86%) create mode 100644 src/ops/nonMaxSuppression.ts create mode 100644 src/ops/normalize.ts create mode 100644 src/ops/padToSquare.ts create mode 100644 src/ops/shuffleArray.ts create mode 100644 src/tinyYolov2/TinyYolov2Base.ts create mode 100644 src/tinyYolov2/TinyYolov2Options.ts create mode 100644 src/tinyYolov2/config.ts create mode 100644 src/tinyYolov2/convWithBatchNorm.ts create mode 100644 src/tinyYolov2/depthwiseSeparableConv.ts create mode 100644 src/tinyYolov2/extractParams.ts create mode 100644 src/tinyYolov2/extractParamsFromWeigthMap.ts create mode 100644 src/tinyYolov2/leaky.ts create mode 100644 src/tinyYolov2/types.ts create mode 100644 src/utils/index.ts create mode 100644 test/NeuralNetwork.test.ts create mode 100644 test/data/boxes.json create mode 100644 test/data/dummy.weights create mode 100644 test/images/white.png create mode 100644 test/tests/classes/BoundingBox.test.ts create mode 100644 test/tests/classes/Box.test.ts create mode 100644 test/tests/classes/Rect.test.ts create mode 100644 test/tests/common/getModelUris.test.ts create mode 100644 test/tests/dom/NetInput.test.ts create mode 100644 test/tests/dom/fetchImage.browser.test.ts create mode 100644 test/tests/dom/fetchJson.browser.test.ts create mode 100644 test/tests/dom/fetchNetWeights.browser.test.ts create mode 100644 test/tests/dom/fetchOrThrow.browser.test.ts create mode 100644 test/tests/dom/toNetInput.test.ts create mode 100644 test/tests/ops/iou.test.ts create mode 100644 test/tests/ops/padToSquare.test.ts create mode 100644 test/utils/index.test.ts diff --git a/karma.conf.js b/karma.conf.js index 7936a205..01364c6f 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -2,6 +2,7 @@ const dataFiles = [ 'test/images/*.jpg', 'test/images/*.png', 'test/data/*.json', + 'test/data/*.weights', 'test/media/*.mp4', 'weights/**/*', 'weights_uncompressed/**/*', diff --git a/package-lock.json b/package-lock.json index 8c31bb81..5b122fb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1487,9 +1487,9 @@ "dev": true }, "fsevents": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz", - "integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", "dev": true, "optional": true }, @@ -3917,15 +3917,6 @@ "yallist": "^3.0.3" } }, - "tfjs-image-recognition-base": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/tfjs-image-recognition-base/-/tfjs-image-recognition-base-0.6.2.tgz", - "integrity": "sha512-ukxViVfAPw7s0KiGhwr3zrwsm+EVa2Z+4aEwKBWO43Rt48nbPyVvrHdL+WbxRynZYjklEE69ft66C8zzea7vFw==", - "requires": { - "@tensorflow/tfjs-core": "^1.2.9", - "tslib": "^1.10.0" - } - }, "through2": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.0.tgz", diff --git a/package.json b/package.json index 5f114d62..32d96e3a 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "license": "MIT", "dependencies": { "@tensorflow/tfjs-core": "1.2.9", - "tfjs-image-recognition-base": "^0.6.2", "tslib": "^1.10.0" }, "devDependencies": { diff --git a/src/NeuralNetwork.ts b/src/NeuralNetwork.ts new file mode 100644 index 00000000..571e1756 --- /dev/null +++ b/src/NeuralNetwork.ts @@ -0,0 +1,159 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { ParamMapping } from './common'; +import { getModelUris } from './common/getModelUris'; +import { loadWeightMap } from './dom'; +import { env } from './env'; + +export abstract class NeuralNetwork { + + protected _params: TNetParams | undefined = undefined + protected _paramMappings: ParamMapping[] = [] + + constructor(protected _name: string) {} + + public get params(): TNetParams | undefined { return this._params } + public get paramMappings(): ParamMapping[] { return this._paramMappings } + public get isLoaded(): boolean { return !!this.params } + + public getParamFromPath(paramPath: string): tf.Tensor { + const { obj, objProp } = this.traversePropertyPath(paramPath) + return obj[objProp] + } + + public reassignParamFromPath(paramPath: string, tensor: tf.Tensor) { + const { obj, objProp } = this.traversePropertyPath(paramPath) + obj[objProp].dispose() + obj[objProp] = tensor + } + + public getParamList() { + return this._paramMappings.map(({ paramPath }) => ({ + path: paramPath, + tensor: this.getParamFromPath(paramPath) + })) + } + + public getTrainableParams() { + return this.getParamList().filter(param => param.tensor instanceof tf.Variable) + } + + public getFrozenParams() { + return this.getParamList().filter(param => !(param.tensor instanceof tf.Variable)) + } + + public variable() { + this.getFrozenParams().forEach(({ path, tensor }) => { + this.reassignParamFromPath(path, tensor.variable()) + }) + } + + public freeze() { + this.getTrainableParams().forEach(({ path, tensor: variable }) => { + const tensor = tf.tensor(variable.dataSync()) + variable.dispose() + this.reassignParamFromPath(path, tensor) + }) + } + + public dispose(throwOnRedispose: boolean = true) { + this.getParamList().forEach(param => { + if (throwOnRedispose && param.tensor.isDisposed) { + throw new Error(`param tensor has already been disposed for path ${param.path}`) + } + param.tensor.dispose() + }) + this._params = undefined + } + + public serializeParams(): Float32Array { + return new Float32Array( + this.getParamList() + .map(({ tensor }) => Array.from(tensor.dataSync()) as number[]) + .reduce((flat, arr) => flat.concat(arr)) + ) + } + + public async load(weightsOrUrl: Float32Array | string | undefined): Promise { + if (weightsOrUrl instanceof Float32Array) { + this.extractWeights(weightsOrUrl) + return + } + + await this.loadFromUri(weightsOrUrl) + } + + public async loadFromUri(uri: string | undefined) { + if (uri && typeof uri !== 'string') { + throw new Error(`${this._name}.loadFromUri - expected model uri`) + } + + const weightMap = await loadWeightMap(uri, this.getDefaultModelName()) + this.loadFromWeightMap(weightMap) + } + + public async loadFromDisk(filePath: string | undefined) { + 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(filePath => readFile(filePath).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.extractParamsFromWeigthMap(weightMap) + + this._paramMappings = paramMappings + this._params = params + } + + public extractWeights(weights: Float32Array) { + const { + paramMappings, + params + } = this.extractParams(weights) + + this._paramMappings = paramMappings + this._params = params + } + + private traversePropertyPath(paramPath: string) { + if (!this.params) { + throw new Error(`traversePropertyPath - model has no loaded params`) + } + + const result = paramPath.split('/').reduce((res: { nextObj: any, obj?: any, objProp?: string }, objProp) => { + 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 }) + + const { obj, objProp } = result + if (!obj || !objProp || !(obj[objProp] instanceof tf.Tensor)) { + throw new Error(`traversePropertyPath - parameter is not a tensor, for path ${paramPath}`) + } + + return { obj, objProp } + } + + protected abstract getDefaultModelName(): string + protected abstract extractParamsFromWeigthMap(weightMap: tf.NamedTensorMap): { params: TNetParams, paramMappings: ParamMapping[] } + protected abstract extractParams(weights: Float32Array): { params: TNetParams, paramMappings: ParamMapping[] } +} \ No newline at end of file diff --git a/src/ageGenderNet/AgeGenderNet.ts b/src/ageGenderNet/AgeGenderNet.ts index 38691179..4e1f2883 100644 --- a/src/ageGenderNet/AgeGenderNet.ts +++ b/src/ageGenderNet/AgeGenderNet.ts @@ -1,5 +1,4 @@ import * as tf from '@tensorflow/tfjs-core'; -import { NetInput, NeuralNetwork, TNetInput, toNetInput } from 'tfjs-image-recognition-base'; import { fullyConnectedLayer } from '../common/fullyConnectedLayer'; import { seperateWeightMaps } from '../faceProcessor/util'; @@ -7,6 +6,8 @@ import { TinyXception } from '../xception/TinyXception'; import { extractParams } from './extractParams'; import { extractParamsFromWeigthMap } from './extractParamsFromWeigthMap'; import { AgeAndGenderPrediction, Gender, NetOutput, NetParams } from './types'; +import { NeuralNetwork } from '../NeuralNetwork'; +import { NetInput, TNetInput, toNetInput } from '../dom'; export class AgeGenderNet extends NeuralNetwork { diff --git a/src/ageGenderNet/extractParams.ts b/src/ageGenderNet/extractParams.ts index f0d42313..bf9d8d42 100644 --- a/src/ageGenderNet/extractParams.ts +++ b/src/ageGenderNet/extractParams.ts @@ -1,17 +1,16 @@ -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; - +import { extractFCParamsFactory, extractWeightsFactory, ParamMapping } from '../common'; import { NetParams } from './types'; -export function extractParams(weights: Float32Array): { params: NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +export function extractParams(weights: Float32Array): { params: NetParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractWeights, getRemainingWeights - } = TfjsImageRecognitionBase.extractWeightsFactory(weights) + } = extractWeightsFactory(weights) - const extractFCParams = TfjsImageRecognitionBase.extractFCParamsFactory(extractWeights, paramMappings) + const extractFCParams = extractFCParamsFactory(extractWeights, paramMappings) const age = extractFCParams(512, 1, 'fc/age') const gender = extractFCParams(512, 2, 'fc/gender') diff --git a/src/ageGenderNet/extractParamsFromWeigthMap.ts b/src/ageGenderNet/extractParamsFromWeigthMap.ts index 1a55758e..15e91dc7 100644 --- a/src/ageGenderNet/extractParamsFromWeigthMap.ts +++ b/src/ageGenderNet/extractParamsFromWeigthMap.ts @@ -1,17 +1,17 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { disposeUnusedWeightTensors, extractWeightEntryFactory, FCParams, ParamMapping } from '../common'; import { NetParams } from './types'; export function extractParamsFromWeigthMap( weightMap: tf.NamedTensorMap -): { params: NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +): { params: NetParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] - const extractWeightEntry = TfjsImageRecognitionBase.extractWeightEntryFactory(weightMap, paramMappings) + const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings) - function extractFcParams(prefix: string): TfjsImageRecognitionBase.FCParams { + function extractFcParams(prefix: string): FCParams { const weights = extractWeightEntry(`${prefix}/weights`, 2) const bias = extractWeightEntry(`${prefix}/bias`, 1) return { weights, bias } @@ -24,7 +24,7 @@ export function extractParamsFromWeigthMap( } } - TfjsImageRecognitionBase.disposeUnusedWeightTensors(weightMap, paramMappings) + disposeUnusedWeightTensors(weightMap, paramMappings) return { params, paramMappings } } \ No newline at end of file diff --git a/src/ageGenderNet/types.ts b/src/ageGenderNet/types.ts index d42eaab2..3ec1b959 100644 --- a/src/ageGenderNet/types.ts +++ b/src/ageGenderNet/types.ts @@ -1,5 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; + +import { FCParams } from '../common'; export type AgeAndGenderPrediction = { age: number @@ -16,7 +17,7 @@ export type NetOutput = { age: tf.Tensor1D, gender: tf.Tensor2D } export type NetParams = { fc: { - age: TfjsImageRecognitionBase.FCParams - gender: TfjsImageRecognitionBase.FCParams + age: FCParams + gender: FCParams } } \ No newline at end of file diff --git a/src/classes/BoundingBox.ts b/src/classes/BoundingBox.ts new file mode 100644 index 00000000..7263b4b9 --- /dev/null +++ b/src/classes/BoundingBox.ts @@ -0,0 +1,14 @@ +import { Box } from './Box'; + +export interface IBoundingBox { + left: number + top: number + right: number + bottom: number +} + +export class BoundingBox extends Box implements IBoundingBox { + constructor(left: number, top: number, right: number, bottom: number, allowNegativeDimensions: boolean = false) { + super({ left, top, right, bottom }, allowNegativeDimensions) + } +} \ No newline at end of file diff --git a/src/classes/Box.ts b/src/classes/Box.ts new file mode 100644 index 00000000..d2f1dc55 --- /dev/null +++ b/src/classes/Box.ts @@ -0,0 +1,175 @@ +import { isDimensions, isValidNumber } from '../utils'; +import { IBoundingBox } from './BoundingBox'; +import { IDimensions } from './Dimensions'; +import { Point } from './Point'; +import { IRect } from './Rect'; + +export class Box implements IBoundingBox, IRect { + + public static isRect(rect: any): boolean { + return !!rect && [rect.x, rect.y, rect.width, rect.height].every(isValidNumber) + } + + public static assertIsValidBox(box: any, callee: string, allowNegativeDimensions: boolean = false) { + if (!Box.isRect(box)) { + throw new Error(`${callee} - invalid box: ${JSON.stringify(box)}, expected object with properties x, y, width, height`) + } + + if (!allowNegativeDimensions && (box.width < 0 || box.height < 0)) { + throw new Error(`${callee} - width (${box.width}) and height (${box.height}) must be positive numbers`) + } + } + + private _x: number + private _y: number + private _width: number + private _height: number + + constructor(_box: IBoundingBox | IRect, allowNegativeDimensions: boolean = true) { + const box = (_box || {}) as any + + const isBbox = [box.left, box.top, box.right, box.bottom].every(isValidNumber) + const isRect = [box.x, box.y, box.width, box.height].every(isValidNumber) + + if (!isRect && !isBbox) { + throw new Error(`Box.constructor - expected box to be IBoundingBox | IRect, instead have ${JSON.stringify(box)}`) + } + + const [x, y, width, height] = isRect + ? [box.x, box.y, box.width, box.height] + : [box.left, box.top, box.right - box.left, box.bottom - box.top] + + Box.assertIsValidBox({ x, y, width, height }, 'Box.constructor', allowNegativeDimensions) + + this._x = x + this._y = y + this._width = width + this._height = height + } + + public get x(): number { return this._x } + public get y(): number { return this._y } + public get width(): number { return this._width } + public get height(): number { return this._height } + public get left(): number { return this.x } + public get top(): number { return this.y } + public get right(): number { return this.x + this.width } + public get bottom(): number { return this.y + this.height } + public get area(): number { return this.width * this.height } + public get topLeft(): Point { return new Point(this.left, this.top) } + public get topRight(): Point { return new Point(this.right, this.top) } + public get bottomLeft(): Point { return new Point(this.left, this.bottom) } + public get bottomRight(): Point { return new Point(this.right, this.bottom) } + + public round(): Box { + const [x, y, width, height] = [this.x, this.y, this.width, this.height] + .map(val => Math.round(val)) + return new Box({ x, y, width, height }) + } + + public floor(): Box { + const [x, y, width, height] = [this.x, this.y, this.width, this.height] + .map(val => Math.floor(val)) + return new Box({ x, y, width, height }) + } + + public toSquare(): Box { + let { x, y, width, height } = this + const diff = Math.abs(width - height) + if (width < height) { + x -= (diff / 2) + width += diff + } + if (height < width) { + y -= (diff / 2) + height += diff + } + + return new Box({ x, y, width, height }) + } + + public rescale(s: IDimensions | number): Box { + const scaleX = isDimensions(s) ? (s as IDimensions).width : s as number + const scaleY = isDimensions(s) ? (s as IDimensions).height : s as number + return new Box({ + x: this.x * scaleX, + y: this.y * scaleY, + width: this.width * scaleX, + height: this.height * scaleY + }) + } + + public pad(padX: number, padY: number): Box { + let [x, y, width, height] = [ + this.x - (padX / 2), + this.y - (padY / 2), + this.width + padX, + this.height + padY + ] + return new Box({ x, y, width, height }) + } + + public clipAtImageBorders(imgWidth: number, imgHeight: number): Box { + const { x, y, right, bottom } = this + const clippedX = Math.max(x, 0) + const clippedY = Math.max(y, 0) + + const newWidth = right - clippedX + const newHeight = bottom - clippedY + const clippedWidth = Math.min(newWidth, imgWidth - clippedX) + const clippedHeight = Math.min(newHeight, imgHeight - clippedY) + + return (new Box({ x: clippedX, y: clippedY, width: clippedWidth, height: clippedHeight})).floor() + } + + public shift(sx: number, sy: number): Box { + const { width, height } = this + const x = this.x + sx + const y = this.y + sy + + return new Box({ x, y, width, height }) + } + + public padAtBorders(imageHeight: number, imageWidth: number) { + const w = this.width + 1 + const h = this.height + 1 + + let dx = 1 + let dy = 1 + let edx = w + let edy = h + + let x = this.left + let y = this.top + let ex = this.right + let ey = this.bottom + + if (ex > imageWidth) { + edx = -ex + imageWidth + w + ex = imageWidth + } + if (ey > imageHeight) { + edy = -ey + imageHeight + h + ey = imageHeight + } + if (x < 1) { + edy = 2 - x + x = 1 + } + if (y < 1) { + edy = 2 - y + y = 1 + } + + return { dy, edy, dx, edx, y, ey, x, ex, w, h } + } + + public calibrate(region: Box) { + return new Box({ + left: this.left + (region.left * this.width), + top: this.top + (region.top * this.height), + right: this.right + (region.right * this.width), + bottom: this.bottom + (region.bottom * this.height) + }).toSquare().round() + } +} \ No newline at end of file diff --git a/src/classes/Dimensions.ts b/src/classes/Dimensions.ts new file mode 100644 index 00000000..e0b61ed1 --- /dev/null +++ b/src/classes/Dimensions.ts @@ -0,0 +1,28 @@ +import { isValidNumber } from '../utils'; + +export interface IDimensions { + width: number + height: number +} + +export class Dimensions implements IDimensions { + + private _width: number + private _height: number + + constructor(width: number, height: number) { + if (!isValidNumber(width) || !isValidNumber(height)) { + throw new Error(`Dimensions.constructor - expected width and height to be valid numbers, instead have ${JSON.stringify({ width, height })}`) + } + + this._width = width + this._height = height + } + + public get width(): number { return this._width } + public get height(): number { return this._height } + + public reverse(): Dimensions { + return new Dimensions(1 / this.width, 1 / this.height) + } +} \ No newline at end of file diff --git a/src/classes/FaceDetection.ts b/src/classes/FaceDetection.ts index 079c20b0..1ffe5d68 100644 --- a/src/classes/FaceDetection.ts +++ b/src/classes/FaceDetection.ts @@ -1,4 +1,7 @@ -import { Box, IDimensions, ObjectDetection, Rect } from 'tfjs-image-recognition-base'; +import { Box } from './Box'; +import { IDimensions } from './Dimensions'; +import { ObjectDetection } from './ObjectDetection'; +import { Rect } from './Rect'; export interface IFaceDetecion { score: number diff --git a/src/classes/FaceLandmarks.ts b/src/classes/FaceLandmarks.ts index fb43b825..997c58f6 100644 --- a/src/classes/FaceLandmarks.ts +++ b/src/classes/FaceLandmarks.ts @@ -1,7 +1,11 @@ -import { Box, Dimensions, getCenterPoint, IBoundingBox, IDimensions, IRect, Point, Rect } from 'tfjs-image-recognition-base'; - -import { minBbox } from '../minBbox'; +import { minBbox } from '../ops'; +import { getCenterPoint } from '../utils'; +import { IBoundingBox } from './BoundingBox'; +import { Box } from './Box'; +import { Dimensions, IDimensions } from './Dimensions'; import { FaceDetection } from './FaceDetection'; +import { Point } from './Point'; +import { IRect, Rect } from './Rect'; // face alignment constants const relX = 0.5 diff --git a/src/classes/FaceLandmarks5.ts b/src/classes/FaceLandmarks5.ts index bddb668d..cf1439b8 100644 --- a/src/classes/FaceLandmarks5.ts +++ b/src/classes/FaceLandmarks5.ts @@ -1,6 +1,7 @@ -import { getCenterPoint, Point } from 'tfjs-image-recognition-base'; - +import { getCenterPoint } from '../utils'; import { FaceLandmarks } from './FaceLandmarks'; +import { Point } from './Point'; + export class FaceLandmarks5 extends FaceLandmarks { diff --git a/src/classes/FaceLandmarks68.ts b/src/classes/FaceLandmarks68.ts index 1ae65e05..58a2da0a 100644 --- a/src/classes/FaceLandmarks68.ts +++ b/src/classes/FaceLandmarks68.ts @@ -1,6 +1,6 @@ -import { getCenterPoint, Point } from 'tfjs-image-recognition-base'; - -import { FaceLandmarks } from '../classes/FaceLandmarks'; +import { getCenterPoint } from '../utils'; +import { FaceLandmarks } from './FaceLandmarks'; +import { Point } from './Point'; export class FaceLandmarks68 extends FaceLandmarks { public getJawOutline(): Point[] { diff --git a/src/classes/FaceMatch.ts b/src/classes/FaceMatch.ts index ca1b6cf5..94d832b9 100644 --- a/src/classes/FaceMatch.ts +++ b/src/classes/FaceMatch.ts @@ -1,4 +1,4 @@ -import { round } from 'tfjs-image-recognition-base'; +import { round } from '../utils'; export interface IFaceMatch { label: string diff --git a/src/classes/LabeledBox.ts b/src/classes/LabeledBox.ts new file mode 100644 index 00000000..19daf8a1 --- /dev/null +++ b/src/classes/LabeledBox.ts @@ -0,0 +1,25 @@ +import { isValidNumber } from '../utils'; +import { IBoundingBox } from './BoundingBox'; +import { Box } from './Box'; +import { IRect } from './Rect'; + +export class LabeledBox extends Box { + + public static assertIsValidLabeledBox(box: any, callee: string) { + Box.assertIsValidBox(box, callee) + + if (!isValidNumber(box.label)) { + throw new Error(`${callee} - expected property label (${box.label}) to be a number`) + } + } + + private _label: number + + constructor(box: IBoundingBox | IRect | any, label: number) { + super(box) + this._label = label + } + + public get label(): number { return this._label } + +} \ No newline at end of file diff --git a/src/classes/ObjectDetection.ts b/src/classes/ObjectDetection.ts new file mode 100644 index 00000000..918b2cda --- /dev/null +++ b/src/classes/ObjectDetection.ts @@ -0,0 +1,44 @@ +import { Box } from './Box'; +import { Dimensions, IDimensions } from './Dimensions'; +import { IRect, Rect } from './Rect'; + +export class ObjectDetection { + private _score: number + private _classScore: number + private _className: string + private _box: Rect + private _imageDims: Dimensions + + constructor( + score: number, + classScore: number, + className: string, + relativeBox: IRect, + imageDims: IDimensions + ) { + this._imageDims = new Dimensions(imageDims.width, imageDims.height) + this._score = score + this._classScore = classScore + this._className = className + this._box = new Box(relativeBox).rescale(this._imageDims) + } + + public get score(): number { return this._score } + public get classScore(): number { return this._classScore } + public get className(): string { return this._className } + public get box(): Box { return this._box } + public get imageDims(): Dimensions { return this._imageDims } + public get imageWidth(): number { return this.imageDims.width } + public get imageHeight(): number { return this.imageDims.height } + public get relativeBox(): Box { return new Box(this._box).rescale(this.imageDims.reverse()) } + + public forSize(width: number, height: number): ObjectDetection { + return new ObjectDetection( + this.score, + this.classScore, + this.className, + this.relativeBox, + { width, height} + ) + } +} \ No newline at end of file diff --git a/src/classes/Point.ts b/src/classes/Point.ts new file mode 100644 index 00000000..8650076e --- /dev/null +++ b/src/classes/Point.ts @@ -0,0 +1,45 @@ +export interface IPoint { + x: number + y: number +} + +export class Point implements IPoint { + private _x: number + private _y: number + + constructor(x: number, y: number) { + this._x = x + this._y = y + } + + get x(): number { return this._x } + get y(): number { return this._y } + + public add(pt: IPoint): Point { + return new Point(this.x + pt.x, this.y + pt.y) + } + + public sub(pt: IPoint): Point { + return new Point(this.x - pt.x, this.y - pt.y) + } + + public mul(pt: IPoint): Point { + return new Point(this.x * pt.x, this.y * pt.y) + } + + public div(pt: IPoint): Point { + return new Point(this.x / pt.x, this.y / pt.y) + } + + public abs(): Point { + return new Point(Math.abs(this.x), Math.abs(this.y)) + } + + public magnitude(): number { + return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)) + } + + public floor(): Point { + return new Point(Math.floor(this.x), Math.floor(this.y)) + } +} \ No newline at end of file diff --git a/src/classes/PredictedBox.ts b/src/classes/PredictedBox.ts new file mode 100644 index 00000000..bf061c68 --- /dev/null +++ b/src/classes/PredictedBox.ts @@ -0,0 +1,31 @@ +import { isValidProbablitiy } from '../utils'; +import { IBoundingBox } from './BoundingBox'; +import { LabeledBox } from './LabeledBox'; +import { IRect } from './Rect'; + +export class PredictedBox extends LabeledBox { + + public static assertIsValidPredictedBox(box: any, callee: string) { + LabeledBox.assertIsValidLabeledBox(box, callee) + + if ( + !isValidProbablitiy(box.score) + || !isValidProbablitiy(box.classScore) + ) { + throw new Error(`${callee} - expected properties score (${box.score}) and (${box.classScore}) to be a number between [0, 1]`) + } + } + + private _score: number + private _classScore: number + + constructor(box: IBoundingBox | IRect | any, label: number, score: number, classScore: number) { + super(box, label) + this._score = score + this._classScore = classScore + } + + public get score(): number { return this._score } + public get classScore(): number { return this._classScore } + +} \ No newline at end of file diff --git a/src/classes/Rect.ts b/src/classes/Rect.ts new file mode 100644 index 00000000..55067698 --- /dev/null +++ b/src/classes/Rect.ts @@ -0,0 +1,14 @@ +import { Box } from './Box'; + +export interface IRect { + x: number + y: number + width: number + height: number +} + +export class Rect extends Box implements IRect { + constructor(x: number, y: number, width: number, height: number, allowNegativeDimensions: boolean = false) { + super({ x, y, width, height }, allowNegativeDimensions) + } +} \ No newline at end of file diff --git a/src/classes/index.ts b/src/classes/index.ts index c5ed0f8e..06c5fce4 100644 --- a/src/classes/index.ts +++ b/src/classes/index.ts @@ -1,6 +1,14 @@ +export * from './BoundingBox' +export * from './Box' +export * from './Dimensions' export * from './FaceDetection'; export * from './FaceLandmarks'; export * from './FaceLandmarks5'; export * from './FaceLandmarks68'; export * from './FaceMatch'; -export * from './LabeledFaceDescriptors'; \ No newline at end of file +export * from './LabeledBox' +export * from './LabeledFaceDescriptors'; +export * from './ObjectDetection' +export * from './Point' +export * from './PredictedBox' +export * from './Rect' \ No newline at end of file diff --git a/src/common/convLayer.ts b/src/common/convLayer.ts new file mode 100644 index 00000000..764b166f --- /dev/null +++ b/src/common/convLayer.ts @@ -0,0 +1,19 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { ConvParams } from './types'; + +export function convLayer( + x: tf.Tensor4D, + params: ConvParams, + padding: 'valid' | 'same' = 'same', + withRelu: boolean = false +): tf.Tensor4D { + return tf.tidy(() => { + const out = tf.add( + tf.conv2d(x, params.filters, [1, 1], padding), + params.bias + ) as tf.Tensor4D + + return withRelu ? tf.relu(out) : out + }) +} \ No newline at end of file diff --git a/src/common/depthwiseSeparableConv.ts b/src/common/depthwiseSeparableConv.ts index ac5cf5c1..b6c6bfe2 100644 --- a/src/common/depthwiseSeparableConv.ts +++ b/src/common/depthwiseSeparableConv.ts @@ -1,9 +1,10 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; + +import { SeparableConvParams } from './types'; export function depthwiseSeparableConv( x: tf.Tensor4D, - params: TfjsImageRecognitionBase.SeparableConvParams, + params: SeparableConvParams, stride: [number, number] ): tf.Tensor4D { return tf.tidy(() => { diff --git a/src/common/disposeUnusedWeightTensors.ts b/src/common/disposeUnusedWeightTensors.ts new file mode 100644 index 00000000..520cc848 --- /dev/null +++ b/src/common/disposeUnusedWeightTensors.ts @@ -0,0 +1,9 @@ +import { ParamMapping } from './types'; + +export function disposeUnusedWeightTensors(weightMap: any, paramMappings: ParamMapping[]) { + Object.keys(weightMap).forEach(path => { + if (!paramMappings.some(pm => pm.originalPath === path)) { + weightMap[path].dispose() + } + }) +} diff --git a/src/common/extractConvParamsFactory.ts b/src/common/extractConvParamsFactory.ts new file mode 100644 index 00000000..e55fafaa --- /dev/null +++ b/src/common/extractConvParamsFactory.ts @@ -0,0 +1,31 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { ConvParams, ExtractWeightsFunction, ParamMapping } from './types'; + +export function extractConvParamsFactory( + extractWeights: ExtractWeightsFunction, + paramMappings: ParamMapping[] +) { + + return function( + channelsIn: number, + channelsOut: number, + filterSize: number, + mappedPrefix: string + ): ConvParams { + + const filters = tf.tensor4d( + extractWeights(channelsIn * channelsOut * filterSize * filterSize), + [filterSize, filterSize, channelsIn, channelsOut] + ) + const bias = tf.tensor1d(extractWeights(channelsOut)) + + paramMappings.push( + { paramPath: `${mappedPrefix}/filters` }, + { paramPath: `${mappedPrefix}/bias` } + ) + + return { filters, bias } + } + +} diff --git a/src/common/extractFCParamsFactory.ts b/src/common/extractFCParamsFactory.ts new file mode 100644 index 00000000..68a9c91a --- /dev/null +++ b/src/common/extractFCParamsFactory.ts @@ -0,0 +1,31 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { ExtractWeightsFunction, FCParams, ParamMapping } from './types'; + + +export function extractFCParamsFactory( + extractWeights: ExtractWeightsFunction, + paramMappings: ParamMapping[] +) { + + return function( + channelsIn: number, + channelsOut: number, + mappedPrefix: string + ): FCParams { + + const fc_weights = tf.tensor2d(extractWeights(channelsIn * channelsOut), [channelsIn, channelsOut]) + const fc_bias = tf.tensor1d(extractWeights(channelsOut)) + + paramMappings.push( + { paramPath: `${mappedPrefix}/weights` }, + { paramPath: `${mappedPrefix}/bias` } + ) + + return { + weights: fc_weights, + bias: fc_bias + } + } + +} diff --git a/src/common/extractSeparableConvParamsFactory.ts b/src/common/extractSeparableConvParamsFactory.ts new file mode 100644 index 00000000..d5d23cea --- /dev/null +++ b/src/common/extractSeparableConvParamsFactory.ts @@ -0,0 +1,46 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { ExtractWeightsFunction, ParamMapping, SeparableConvParams } from './types'; + +export function extractSeparableConvParamsFactory( + extractWeights: ExtractWeightsFunction, + paramMappings: ParamMapping[] +) { + + return function(channelsIn: number, channelsOut: number, mappedPrefix: string): SeparableConvParams { + const depthwise_filter = tf.tensor4d(extractWeights(3 * 3 * channelsIn), [3, 3, channelsIn, 1]) + const pointwise_filter = tf.tensor4d(extractWeights(channelsIn * channelsOut), [1, 1, channelsIn, channelsOut]) + const bias = tf.tensor1d(extractWeights(channelsOut)) + + paramMappings.push( + { paramPath: `${mappedPrefix}/depthwise_filter` }, + { paramPath: `${mappedPrefix}/pointwise_filter` }, + { paramPath: `${mappedPrefix}/bias` } + ) + + return new SeparableConvParams( + depthwise_filter, + pointwise_filter, + bias + ) + } + +} + +export function loadSeparableConvParamsFactory( + extractWeightEntry: (originalPath: string, paramRank: number) => T +) { + + return function (prefix: string): SeparableConvParams { + const depthwise_filter = extractWeightEntry(`${prefix}/depthwise_filter`, 4) + const pointwise_filter = extractWeightEntry(`${prefix}/pointwise_filter`, 4) + const bias = extractWeightEntry(`${prefix}/bias`, 1) + + return new SeparableConvParams( + depthwise_filter, + pointwise_filter, + bias + ) + } + +} diff --git a/src/common/extractWeightEntryFactory.ts b/src/common/extractWeightEntryFactory.ts new file mode 100644 index 00000000..74c9da0f --- /dev/null +++ b/src/common/extractWeightEntryFactory.ts @@ -0,0 +1,20 @@ +import { isTensor } from '../utils'; +import { ParamMapping } from './types'; + +export function extractWeightEntryFactory(weightMap: any, paramMappings: ParamMapping[]) { + + return function (originalPath: string, paramRank: number, mappedPath?: string): T { + const tensor = weightMap[originalPath] + + if (!isTensor(tensor, paramRank)) { + throw new Error(`expected weightMap[${originalPath}] to be a Tensor${paramRank}D, instead have ${tensor}`) + } + + paramMappings.push( + { originalPath, paramPath: mappedPath || originalPath } + ) + + return tensor + } + +} diff --git a/src/common/extractWeightsFactory.ts b/src/common/extractWeightsFactory.ts new file mode 100644 index 00000000..8cfcd0c0 --- /dev/null +++ b/src/common/extractWeightsFactory.ts @@ -0,0 +1,18 @@ +export function extractWeightsFactory(weights: Float32Array) { + let remainingWeights = weights + + function extractWeights(numWeights: number): Float32Array { + const ret = remainingWeights.slice(0, numWeights) + remainingWeights = remainingWeights.slice(numWeights) + return ret + } + + function getRemainingWeights(): Float32Array { + return remainingWeights + } + + return { + extractWeights, + getRemainingWeights + } +} \ No newline at end of file diff --git a/src/common/fullyConnectedLayer.ts b/src/common/fullyConnectedLayer.ts index dbec05e3..806f7644 100644 --- a/src/common/fullyConnectedLayer.ts +++ b/src/common/fullyConnectedLayer.ts @@ -1,9 +1,10 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; + +import { FCParams } from './types'; export function fullyConnectedLayer( x: tf.Tensor2D, - params: TfjsImageRecognitionBase.FCParams + params: FCParams ): tf.Tensor2D { return tf.tidy(() => tf.add( diff --git a/src/common/getModelUris.ts b/src/common/getModelUris.ts new file mode 100644 index 00000000..55f4745a --- /dev/null +++ b/src/common/getModelUris.ts @@ -0,0 +1,33 @@ +export function getModelUris(uri: string | undefined, defaultModelName: string) { + const defaultManifestFilename = `${defaultModelName}-weights_manifest.json` + + if (!uri) { + return { + modelBaseUri: '', + manifestUri: defaultManifestFilename + } + } + + if (uri === '/') { + return { + modelBaseUri: '/', + manifestUri: `/${defaultManifestFilename}` + } + } + const protocol = uri.startsWith('http://') ? 'http://' : uri.startsWith('https://') ? 'https://' : ''; + uri = uri.replace(protocol, ''); + + const parts = uri.split('/').filter(s => s) + + const manifestFile = uri.endsWith('.json') + ? parts[parts.length - 1] + : defaultManifestFilename + + let modelBaseUri = protocol + (uri.endsWith('.json') ? parts.slice(0, parts.length - 1) : parts).join('/') + modelBaseUri = uri.startsWith('/') ? `/${modelBaseUri}` : modelBaseUri + + return { + modelBaseUri, + manifestUri: modelBaseUri === '/' ? `/${manifestFile}` : `${modelBaseUri}/${manifestFile}` + } +} \ No newline at end of file diff --git a/src/common/index.ts b/src/common/index.ts new file mode 100644 index 00000000..9838af96 --- /dev/null +++ b/src/common/index.ts @@ -0,0 +1,10 @@ +export * from './convLayer' +export * from './depthwiseSeparableConv' +export * from './disposeUnusedWeightTensors' +export * from './extractConvParamsFactory' +export * from './extractFCParamsFactory' +export * from './extractSeparableConvParamsFactory' +export * from './extractWeightEntryFactory' +export * from './extractWeightsFactory' +export * from './getModelUris' +export * from './types' \ No newline at end of file diff --git a/src/common/loadConvParamsFactory.ts b/src/common/loadConvParamsFactory.ts index 0a6b525f..286aa217 100644 --- a/src/common/loadConvParamsFactory.ts +++ b/src/common/loadConvParamsFactory.ts @@ -1,8 +1,9 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; + +import { ConvParams } from './types'; export function loadConvParamsFactory(extractWeightEntry: (originalPath: string, paramRank: number) => T) { - return function(prefix: string): TfjsImageRecognitionBase.ConvParams { + return function(prefix: string): ConvParams { const filters = extractWeightEntry(`${prefix}/filters`, 4) const bias = extractWeightEntry(`${prefix}/bias`, 1) diff --git a/src/common/types.ts b/src/common/types.ts new file mode 100644 index 00000000..e3c50bb7 --- /dev/null +++ b/src/common/types.ts @@ -0,0 +1,26 @@ +import * as tf from '@tensorflow/tfjs-core'; + +export type ExtractWeightsFunction = (numWeights: number) => Float32Array + +export type ParamMapping = { + originalPath?: string + paramPath: string +} + +export type ConvParams = { + filters: tf.Tensor4D + bias: tf.Tensor1D +} + +export type FCParams = { + weights: tf.Tensor2D + bias: tf.Tensor1D +} + +export class SeparableConvParams { + constructor( + public depthwise_filter: tf.Tensor4D, + public pointwise_filter: tf.Tensor4D, + public bias: tf.Tensor1D + ) {} +} \ No newline at end of file diff --git a/src/dom/NetInput.ts b/src/dom/NetInput.ts new file mode 100644 index 00000000..7b3187cb --- /dev/null +++ b/src/dom/NetInput.ts @@ -0,0 +1,153 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { Dimensions } from '../classes/Dimensions'; +import { env } from '../env'; +import { padToSquare } from '../ops/padToSquare'; +import { computeReshapedDimensions, isTensor3D, isTensor4D, range } from '../utils'; +import { createCanvasFromMedia } from './createCanvas'; +import { imageToSquare } from './imageToSquare'; +import { TResolvedNetInput } from './types'; + +export class NetInput { + private _imageTensors: Array = [] + private _canvases: HTMLCanvasElement[] = [] + private _batchSize: number + private _treatAsBatchInput: boolean = false + + private _inputDimensions: number[][] = [] + private _inputSize: number + + constructor( + inputs: Array, + treatAsBatchInput: boolean = false + ) { + if (!Array.isArray(inputs)) { + throw new Error(`NetInput.constructor - expected inputs to be an Array of TResolvedNetInput or to be instanceof tf.Tensor4D, instead have ${inputs}`) + } + + this._treatAsBatchInput = treatAsBatchInput + this._batchSize = inputs.length + + inputs.forEach((input, idx) => { + + if (isTensor3D(input)) { + this._imageTensors[idx] = input + this._inputDimensions[idx] = input.shape + return + } + + if (isTensor4D(input)) { + const batchSize = input.shape[0] + if (batchSize !== 1) { + throw new Error(`NetInput - tf.Tensor4D with batchSize ${batchSize} passed, but not supported in input array`) + } + + this._imageTensors[idx] = input + this._inputDimensions[idx] = input.shape.slice(1) + return + } + + const canvas = input instanceof env.getEnv().Canvas ? input : createCanvasFromMedia(input) + this._canvases[idx] = canvas + this._inputDimensions[idx] = [canvas.height, canvas.width, 3] + }) + } + + public get imageTensors(): Array { + return this._imageTensors + } + + public get canvases(): HTMLCanvasElement[] { + return this._canvases + } + + public get isBatchInput(): boolean { + return this.batchSize > 1 || this._treatAsBatchInput + } + + public get batchSize(): number { + return this._batchSize + } + + public get inputDimensions(): number[][] { + return this._inputDimensions + } + + public get inputSize(): number | undefined { + return this._inputSize + } + + public get reshapedInputDimensions(): Dimensions[] { + return range(this.batchSize, 0, 1).map( + (_, batchIdx) => this.getReshapedInputDimensions(batchIdx) + ) + } + + public getInput(batchIdx: number): tf.Tensor3D | tf.Tensor4D | HTMLCanvasElement { + return this.canvases[batchIdx] || this.imageTensors[batchIdx] + } + + public getInputDimensions(batchIdx: number): number[] { + return this._inputDimensions[batchIdx] + } + + public getInputHeight(batchIdx: number): number { + return this._inputDimensions[batchIdx][0] + } + + public getInputWidth(batchIdx: number): number { + return this._inputDimensions[batchIdx][1] + } + + public getReshapedInputDimensions(batchIdx: number): Dimensions { + if (typeof this.inputSize !== 'number') { + throw new Error('getReshapedInputDimensions - inputSize not set, toBatchTensor has not been called yet') + } + + const width = this.getInputWidth(batchIdx) + const height = this.getInputHeight(batchIdx) + return computeReshapedDimensions({ width, height }, this.inputSize) + } + + /** + * Create a batch tensor from all input canvases and tensors + * with size [batchSize, inputSize, inputSize, 3]. + * + * @param inputSize Height and width of the tensor. + * @param isCenterImage (optional, default: false) If true, add an equal amount of padding on + * both sides of the minor dimension oof the image. + * @returns The batch tensor. + */ + public toBatchTensor(inputSize: number, isCenterInputs: boolean = true): tf.Tensor4D { + + this._inputSize = inputSize + + return tf.tidy(() => { + + const inputTensors = range(this.batchSize, 0, 1).map(batchIdx => { + const input = this.getInput(batchIdx) + + if (input instanceof tf.Tensor) { + let imgTensor = isTensor4D(input) ? input : input.expandDims() + imgTensor = padToSquare(imgTensor, isCenterInputs) + + if (imgTensor.shape[1] !== inputSize || imgTensor.shape[2] !== inputSize) { + imgTensor = tf.image.resizeBilinear(imgTensor, [inputSize, inputSize]) + } + + return imgTensor.as3D(inputSize, inputSize, 3) + } + + if (input instanceof env.getEnv().Canvas) { + return tf.browser.fromPixels(imageToSquare(input, inputSize, isCenterInputs)) + } + + throw new Error(`toBatchTensor - at batchIdx ${batchIdx}, expected input to be instanceof tf.Tensor or instanceof HTMLCanvasElement, instead have ${input}`) + }) + + const batchTensor = tf.stack(inputTensors.map(t => t.toFloat())).as4D(this.batchSize, inputSize, inputSize, 3) + + return batchTensor + }) + } +} \ No newline at end of file diff --git a/src/dom/awaitMediaLoaded.ts b/src/dom/awaitMediaLoaded.ts new file mode 100644 index 00000000..6bdf1e2d --- /dev/null +++ b/src/dom/awaitMediaLoaded.ts @@ -0,0 +1,28 @@ +import { env } from '../env'; +import { isMediaLoaded } from './isMediaLoaded'; + +export function awaitMediaLoaded(media: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement) { + + return new Promise((resolve, reject) => { + if (media instanceof env.getEnv().Canvas || isMediaLoaded(media)) { + return resolve() + } + + function onLoad(e: Event) { + if (!e.currentTarget) return + e.currentTarget.removeEventListener('load', onLoad) + e.currentTarget.removeEventListener('error', onError) + resolve(e) + } + + function onError(e: Event) { + if (!e.currentTarget) return + e.currentTarget.removeEventListener('load', onLoad) + e.currentTarget.removeEventListener('error', onError) + reject(e) + } + + media.addEventListener('load', onLoad) + media.addEventListener('error', onError) + }) +} \ No newline at end of file diff --git a/src/dom/bufferToImage.ts b/src/dom/bufferToImage.ts new file mode 100644 index 00000000..bcb5cf18 --- /dev/null +++ b/src/dom/bufferToImage.ts @@ -0,0 +1,23 @@ +import { env } from '../env'; + +export function bufferToImage(buf: Blob): Promise { + return new Promise((resolve, reject) => { + if (!(buf instanceof Blob)) { + return reject('bufferToImage - expected buf to be of type: Blob') + } + + const reader = new FileReader() + reader.onload = () => { + if (typeof reader.result !== 'string') { + return reject('bufferToImage - expected reader.result to be a string, in onload') + } + + const img = env.getEnv().createImageElement() + img.onload = () => resolve(img) + img.onerror = reject + img.src = reader.result + } + reader.onerror = reject + reader.readAsDataURL(buf) + }) +} \ No newline at end of file diff --git a/src/dom/createCanvas.ts b/src/dom/createCanvas.ts new file mode 100644 index 00000000..efedd99d --- /dev/null +++ b/src/dom/createCanvas.ts @@ -0,0 +1,33 @@ +import { IDimensions } from '../classes/Dimensions'; +import { env } from '../env'; +import { getContext2dOrThrow } from './getContext2dOrThrow'; +import { getMediaDimensions } from './getMediaDimensions'; +import { isMediaLoaded } from './isMediaLoaded'; + +export function createCanvas({ width, height }: IDimensions): HTMLCanvasElement { + + const { createCanvasElement } = env.getEnv() + const canvas = createCanvasElement() + canvas.width = width + canvas.height = height + return canvas +} + +export function createCanvasFromMedia(media: HTMLImageElement | HTMLVideoElement | ImageData, dims?: IDimensions): HTMLCanvasElement { + + const { ImageData } = env.getEnv() + + if (!(media instanceof ImageData) && !isMediaLoaded(media)) { + throw new Error('createCanvasFromMedia - media has not finished loading yet') + } + + const { width, height } = dims || getMediaDimensions(media) + const canvas = createCanvas({ width, height }) + + if (media instanceof ImageData) { + getContext2dOrThrow(canvas).putImageData(media, 0, 0) + } else { + getContext2dOrThrow(canvas).drawImage(media, 0, 0, width, height) + } + return canvas +} \ No newline at end of file diff --git a/src/dom/extractFaceTensors.ts b/src/dom/extractFaceTensors.ts index e1d1997f..da399447 100644 --- a/src/dom/extractFaceTensors.ts +++ b/src/dom/extractFaceTensors.ts @@ -1,7 +1,8 @@ import * as tf from '@tensorflow/tfjs-core'; -import { isTensor4D, Rect, isTensor3D } from 'tfjs-image-recognition-base'; +import { Rect } from '../classes'; import { FaceDetection } from '../classes/FaceDetection'; +import { isTensor3D, isTensor4D } from '../utils'; /** * Extracts the tensors of the image regions containing the detected faces. diff --git a/src/dom/extractFaces.ts b/src/dom/extractFaces.ts index 23611ec6..fc55ef27 100644 --- a/src/dom/extractFaces.ts +++ b/src/dom/extractFaces.ts @@ -1,14 +1,11 @@ -import { - createCanvas, - env, - getContext2dOrThrow, - imageTensorToCanvas, - Rect, - TNetInput, - toNetInput, -} from 'tfjs-image-recognition-base'; - import { FaceDetection } from '../classes/FaceDetection'; +import { Rect } from '../classes/Rect'; +import { env } from '../env'; +import { createCanvas } from './createCanvas'; +import { getContext2dOrThrow } from './getContext2dOrThrow'; +import { imageTensorToCanvas } from './imageTensorToCanvas'; +import { toNetInput } from './toNetInput'; +import { TNetInput } from './types'; /** * Extracts the image regions containing the detected faces. diff --git a/src/dom/fetchImage.ts b/src/dom/fetchImage.ts new file mode 100644 index 00000000..bde010f6 --- /dev/null +++ b/src/dom/fetchImage.ts @@ -0,0 +1,12 @@ +import { bufferToImage } from './bufferToImage'; +import { fetchOrThrow } from './fetchOrThrow'; + +export async function fetchImage(uri: string): Promise { + const res = await fetchOrThrow(uri) + const blob = await (res).blob() + + if (!blob.type.startsWith('image/')) { + throw new Error(`fetchImage - expected blob type to be of type image/*, instead have: ${blob.type}, for url: ${res.url}`) + } + return bufferToImage(blob) +} diff --git a/src/dom/fetchJson.ts b/src/dom/fetchJson.ts new file mode 100644 index 00000000..fceae291 --- /dev/null +++ b/src/dom/fetchJson.ts @@ -0,0 +1,5 @@ +import { fetchOrThrow } from './fetchOrThrow'; + +export async function fetchJson(uri: string): Promise { + return (await fetchOrThrow(uri)).json() +} diff --git a/src/dom/fetchNetWeights.ts b/src/dom/fetchNetWeights.ts new file mode 100644 index 00000000..47e49546 --- /dev/null +++ b/src/dom/fetchNetWeights.ts @@ -0,0 +1,5 @@ +import { fetchOrThrow } from './fetchOrThrow'; + +export async function fetchNetWeights(uri: string): Promise { + return new Float32Array(await (await fetchOrThrow(uri)).arrayBuffer()) +} diff --git a/src/dom/fetchOrThrow.ts b/src/dom/fetchOrThrow.ts new file mode 100644 index 00000000..f8db4adf --- /dev/null +++ b/src/dom/fetchOrThrow.ts @@ -0,0 +1,14 @@ +import { env } from '../env'; + +export async function fetchOrThrow( + url: string, + init?: RequestInit +): Promise { + + const fetch = env.getEnv().fetch + const res = await fetch(url, init) + if (!(res.status < 400)) { + throw new Error(`failed to fetch: (${res.status}) ${res.statusText}, from url: ${res.url}`) + } + return res +} \ No newline at end of file diff --git a/src/dom/getContext2dOrThrow.ts b/src/dom/getContext2dOrThrow.ts new file mode 100644 index 00000000..ca1e8e33 --- /dev/null +++ b/src/dom/getContext2dOrThrow.ts @@ -0,0 +1,24 @@ +import { env } from '../env'; +import { resolveInput } from './resolveInput'; + +export function getContext2dOrThrow(canvasArg: string | HTMLCanvasElement | CanvasRenderingContext2D): CanvasRenderingContext2D { + + const { Canvas, CanvasRenderingContext2D } = env.getEnv() + + if (canvasArg instanceof CanvasRenderingContext2D) { + return canvasArg + } + + const canvas = resolveInput(canvasArg) + + if (!(canvas instanceof Canvas)) { + throw new Error('resolveContext2d - expected canvas to be of instance of Canvas') + } + + const ctx = canvas.getContext('2d') + if (!ctx) { + throw new Error('resolveContext2d - canvas 2d context is null') + } + + return ctx +} \ No newline at end of file diff --git a/src/dom/getMediaDimensions.ts b/src/dom/getMediaDimensions.ts new file mode 100644 index 00000000..b58c801e --- /dev/null +++ b/src/dom/getMediaDimensions.ts @@ -0,0 +1,15 @@ +import { Dimensions, IDimensions } from '../classes/Dimensions'; +import { env } from '../env'; + +export function getMediaDimensions(input: HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | IDimensions): Dimensions { + + const { Image, Video } = env.getEnv() + + if (input instanceof Image) { + return new Dimensions(input.naturalWidth, input.naturalHeight) + } + if (input instanceof Video) { + return new Dimensions(input.videoWidth, input.videoHeight) + } + return new Dimensions(input.width, input.height) +} diff --git a/src/dom/imageTensorToCanvas.ts b/src/dom/imageTensorToCanvas.ts new file mode 100644 index 00000000..d77f0b26 --- /dev/null +++ b/src/dom/imageTensorToCanvas.ts @@ -0,0 +1,20 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { env } from '../env'; +import { isTensor4D } from '../utils'; + +export async function imageTensorToCanvas( + imgTensor: tf.Tensor, + canvas?: HTMLCanvasElement +): Promise { + + const targetCanvas = canvas || env.getEnv().createCanvasElement() + + const [height, width, numChannels] = imgTensor.shape.slice(isTensor4D(imgTensor) ? 1 : 0) + const imgTensor3D = tf.tidy(() => imgTensor.as3D(height, width, numChannels).toInt()) + await tf.browser.toPixels(imgTensor3D, targetCanvas) + + imgTensor3D.dispose() + + return targetCanvas +} \ No newline at end of file diff --git a/src/dom/imageToSquare.ts b/src/dom/imageToSquare.ts new file mode 100644 index 00000000..d2a68f5d --- /dev/null +++ b/src/dom/imageToSquare.ts @@ -0,0 +1,28 @@ +import { env } from '../env'; +import { createCanvas, createCanvasFromMedia } from './createCanvas'; +import { getContext2dOrThrow } from './getContext2dOrThrow'; +import { getMediaDimensions } from './getMediaDimensions'; + +export function imageToSquare(input: HTMLImageElement | HTMLCanvasElement, inputSize: number, centerImage: boolean = false) { + + const { Image, Canvas } = env.getEnv() + + if (!(input instanceof Image || input instanceof Canvas)) { + throw new Error('imageToSquare - expected arg0 to be HTMLImageElement | HTMLCanvasElement') + } + + const dims = getMediaDimensions(input) + const scale = inputSize / Math.max(dims.height, dims.width) + const width = scale * dims.width + const height = scale * dims.height + + const targetCanvas = createCanvas({ width: inputSize, height: inputSize }) + const inputCanvas = input instanceof Canvas ? input : createCanvasFromMedia(input) + + const offset = Math.abs(width - height) / 2 + const dx = centerImage && width < height ? offset : 0 + const dy = centerImage && height < width ? offset : 0 + getContext2dOrThrow(targetCanvas).drawImage(inputCanvas, dx, dy, width, height) + + return targetCanvas +} \ No newline at end of file diff --git a/src/dom/index.ts b/src/dom/index.ts index 267bb937..d36f9b47 100644 --- a/src/dom/index.ts +++ b/src/dom/index.ts @@ -1,2 +1,21 @@ +export * from './awaitMediaLoaded' +export * from './bufferToImage' +export * from './createCanvas' export * from './extractFaces' -export * from './extractFaceTensors' \ No newline at end of file +export * from './extractFaceTensors' +export * from './fetchImage' +export * from './fetchJson' +export * from './fetchNetWeights' +export * from './fetchOrThrow' +export * from './getContext2dOrThrow' +export * from './getMediaDimensions' +export * from './imageTensorToCanvas' +export * from './imageToSquare' +export * from './isMediaElement' +export * from './isMediaLoaded' +export * from './loadWeightMap' +export * from './matchDimensions' +export * from './NetInput' +export * from './resolveInput' +export * from './toNetInput' +export * from './types' \ No newline at end of file diff --git a/src/dom/isMediaElement.ts b/src/dom/isMediaElement.ts new file mode 100644 index 00000000..91079efb --- /dev/null +++ b/src/dom/isMediaElement.ts @@ -0,0 +1,10 @@ +import { env } from '../env'; + +export function isMediaElement(input: any) { + + const { Image, Canvas, Video } = env.getEnv() + + return input instanceof Image + || input instanceof Canvas + || input instanceof Video +} \ No newline at end of file diff --git a/src/dom/isMediaLoaded.ts b/src/dom/isMediaLoaded.ts new file mode 100644 index 00000000..bfac6903 --- /dev/null +++ b/src/dom/isMediaLoaded.ts @@ -0,0 +1,9 @@ +import { env } from '../env'; + +export function isMediaLoaded(media: HTMLImageElement | HTMLVideoElement) : boolean { + + const { Image, Video } = env.getEnv() + + return (media instanceof Image && media.complete) + || (media instanceof Video && media.readyState >= 3) +} diff --git a/src/dom/loadWeightMap.ts b/src/dom/loadWeightMap.ts new file mode 100644 index 00000000..c5902445 --- /dev/null +++ b/src/dom/loadWeightMap.ts @@ -0,0 +1,15 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { getModelUris } from '../common/getModelUris'; +import { fetchJson } from './fetchJson'; + +export async function loadWeightMap( + uri: string | undefined, + defaultModelName: string, +): Promise { + const { manifestUri, modelBaseUri } = getModelUris(uri, defaultModelName) + + const manifest = await fetchJson(manifestUri) + + return tf.io.loadWeights(manifest, modelBaseUri) +} \ No newline at end of file diff --git a/src/dom/matchDimensions.ts b/src/dom/matchDimensions.ts new file mode 100644 index 00000000..f81c3a23 --- /dev/null +++ b/src/dom/matchDimensions.ts @@ -0,0 +1,11 @@ +import { IDimensions } from '../classes'; +import { getMediaDimensions } from './getMediaDimensions'; + +export function matchDimensions(input: IDimensions, reference: IDimensions, useMediaDimensions: boolean = false) { + const { width, height } = useMediaDimensions + ? getMediaDimensions(reference) + : reference + input.width = width + input.height = height + return { width, height } +} \ No newline at end of file diff --git a/src/dom/resolveInput.ts b/src/dom/resolveInput.ts new file mode 100644 index 00000000..a4989ca9 --- /dev/null +++ b/src/dom/resolveInput.ts @@ -0,0 +1,8 @@ +import { env } from '../env'; + +export function resolveInput(arg: string | any) { + if (!env.isNodejs() && typeof arg === 'string') { + return document.getElementById(arg) + } + return arg +} \ No newline at end of file diff --git a/src/dom/toNetInput.ts b/src/dom/toNetInput.ts new file mode 100644 index 00000000..25293464 --- /dev/null +++ b/src/dom/toNetInput.ts @@ -0,0 +1,57 @@ +import { isTensor3D, isTensor4D } from '../utils'; +import { awaitMediaLoaded } from './awaitMediaLoaded'; +import { isMediaElement } from './isMediaElement'; +import { NetInput } from './NetInput'; +import { resolveInput } from './resolveInput'; +import { TNetInput } from './types'; + +/** + * Validates the input to make sure, they are valid net inputs and awaits all media elements + * to be finished loading. + * + * @param input The input, which can be a media element or an array of different media elements. + * @returns A NetInput instance, which can be passed into one of the neural networks. + */ +export async function toNetInput(inputs: TNetInput): Promise { + if (inputs instanceof NetInput) { + return inputs + } + + let inputArgArray = Array.isArray(inputs) + ? inputs + : [inputs] + + if (!inputArgArray.length) { + throw new Error('toNetInput - empty array passed as input') + } + + const getIdxHint = (idx: number) => Array.isArray(inputs) ? ` at input index ${idx}:` : '' + + const inputArray = inputArgArray.map(resolveInput) + + inputArray.forEach((input, i) => { + if (!isMediaElement(input) && !isTensor3D(input) && !isTensor4D(input)) { + + if (typeof inputArgArray[i] === 'string') { + throw new Error(`toNetInput -${getIdxHint(i)} string passed, but could not resolve HTMLElement for element id ${inputArgArray[i]}`) + } + + throw new Error(`toNetInput -${getIdxHint(i)} expected media to be of type HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | tf.Tensor3D, or to be an element id`) + } + + if (isTensor4D(input)) { + // if tf.Tensor4D is passed in the input array, the batch size has to be 1 + const batchSize = input.shape[0] + if (batchSize !== 1) { + throw new Error(`toNetInput -${getIdxHint(i)} tf.Tensor4D with batchSize ${batchSize} passed, but not supported in input array`) + } + } + }) + + // wait for all media elements being loaded + await Promise.all( + inputArray.map(input => isMediaElement(input) && awaitMediaLoaded(input)) + ) + + return new NetInput(inputArray, Array.isArray(inputs)) +} \ No newline at end of file diff --git a/src/dom/types.ts b/src/dom/types.ts new file mode 100644 index 00000000..44cc15f3 --- /dev/null +++ b/src/dom/types.ts @@ -0,0 +1,11 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { NetInput } from './NetInput'; + +export type TMediaElement = HTMLImageElement | HTMLVideoElement | HTMLCanvasElement + +export type TResolvedNetInput = TMediaElement | tf.Tensor3D | tf.Tensor4D + +export type TNetInputArg = string | TResolvedNetInput + +export type TNetInput = TNetInputArg | Array | NetInput | tf.Tensor4D \ No newline at end of file diff --git a/src/draw/DrawBox.ts b/src/draw/DrawBox.ts new file mode 100644 index 00000000..1f1282b7 --- /dev/null +++ b/src/draw/DrawBox.ts @@ -0,0 +1,59 @@ +import { Box, IBoundingBox, IRect } from '../classes'; +import { getContext2dOrThrow } from '../dom/getContext2dOrThrow'; +import { AnchorPosition, DrawTextField, DrawTextFieldOptions, IDrawTextFieldOptions } from './DrawTextField'; + +export interface IDrawBoxOptions { + boxColor?: string + lineWidth?: number + drawLabelOptions?: IDrawTextFieldOptions + label?: string +} + +export class DrawBoxOptions { + public boxColor: string + public lineWidth: number + public drawLabelOptions: DrawTextFieldOptions + public label?: string + + constructor(options: IDrawBoxOptions = {}) { + const { boxColor, lineWidth, label, drawLabelOptions } = options + this.boxColor = boxColor || 'rgba(0, 0, 255, 1)' + this.lineWidth = lineWidth || 2 + this.label = label + + const defaultDrawLabelOptions = { + anchorPosition: AnchorPosition.BOTTOM_LEFT, + backgroundColor: this.boxColor + } + this.drawLabelOptions = new DrawTextFieldOptions(Object.assign({}, defaultDrawLabelOptions, drawLabelOptions)) + } +} + +export class DrawBox { + public box: Box + public options: DrawBoxOptions + + constructor( + box: IBoundingBox | IRect, + options: IDrawBoxOptions = {} + ) { + this.box = new Box(box) + this.options = new DrawBoxOptions(options) + } + + draw(canvasArg: string | HTMLCanvasElement | CanvasRenderingContext2D) { + const ctx = getContext2dOrThrow(canvasArg) + + const { boxColor, lineWidth } = this.options + + const { x, y, width, height } = this.box + ctx.strokeStyle = boxColor + ctx.lineWidth = lineWidth + ctx.strokeRect(x, y, width, height) + + const { label } = this.options + if (label) { + new DrawTextField([label], { x: x - (lineWidth / 2), y }, this.options.drawLabelOptions).draw(canvasArg) + } + } +} \ No newline at end of file diff --git a/src/draw/DrawFaceLandmarks.ts b/src/draw/DrawFaceLandmarks.ts index 99a7142f..105f84a1 100644 --- a/src/draw/DrawFaceLandmarks.ts +++ b/src/draw/DrawFaceLandmarks.ts @@ -1,7 +1,7 @@ -import { getContext2dOrThrow, IPoint } from 'tfjs-image-recognition-base'; - +import { IPoint } from '../classes'; import { FaceLandmarks } from '../classes/FaceLandmarks'; import { FaceLandmarks68 } from '../classes/FaceLandmarks68'; +import { getContext2dOrThrow } from '../dom/getContext2dOrThrow'; import { WithFaceDetection } from '../factories/WithFaceDetection'; import { isWithFaceLandmarks, WithFaceLandmarks } from '../factories/WithFaceLandmarks'; import { drawContour } from './drawContour'; diff --git a/src/draw/DrawTextField.ts b/src/draw/DrawTextField.ts new file mode 100644 index 00000000..58002557 --- /dev/null +++ b/src/draw/DrawTextField.ts @@ -0,0 +1,108 @@ +import { IDimensions, IPoint } from '../classes'; +import { getContext2dOrThrow } from '../dom/getContext2dOrThrow'; +import { resolveInput } from '../dom/resolveInput'; + +export enum AnchorPosition { + TOP_LEFT = 'TOP_LEFT', + TOP_RIGHT = 'TOP_RIGHT', + BOTTOM_LEFT = 'BOTTOM_LEFT', + BOTTOM_RIGHT = 'BOTTOM_RIGHT' +} + +export interface IDrawTextFieldOptions { + anchorPosition?: AnchorPosition + backgroundColor?: string + fontColor?: string + fontSize?: number + fontStyle?: string + padding?: number +} + +export class DrawTextFieldOptions implements IDrawTextFieldOptions { + public anchorPosition: AnchorPosition + public backgroundColor: string + public fontColor: string + public fontSize: number + public fontStyle: string + public padding: number + + constructor(options: IDrawTextFieldOptions = {}) { + const { anchorPosition, backgroundColor, fontColor, fontSize, fontStyle, padding } = options + this.anchorPosition = anchorPosition || AnchorPosition.TOP_LEFT + this.backgroundColor = backgroundColor || 'rgba(0, 0, 0, 0.5)' + this.fontColor = fontColor || 'rgba(255, 255, 255, 1)' + this.fontSize = fontSize || 14 + this.fontStyle = fontStyle || 'Georgia' + this.padding = padding || 4 + } +} + +export class DrawTextField { + public text: string[] + public anchor : IPoint + public options: DrawTextFieldOptions + + constructor( + text: string | string[] | DrawTextField, + anchor: IPoint, + options: IDrawTextFieldOptions = {} + ) { + this.text = typeof text === 'string' + ? [text] + : (text instanceof DrawTextField ? text.text : text) + this.anchor = anchor + this.options = new DrawTextFieldOptions(options) + } + + measureWidth(ctx: CanvasRenderingContext2D): number { + const { padding } = this.options + return this.text.map(l => ctx.measureText(l).width).reduce((w0, w1) => w0 < w1 ? w1 : w0, 0) + (2 * padding) + } + + measureHeight(): number { + const { fontSize, padding } = this.options + return this.text.length * fontSize + (2 * padding) + } + + getUpperLeft(ctx: CanvasRenderingContext2D, canvasDims?: IDimensions): IPoint { + const { anchorPosition } = this.options + const isShiftLeft = anchorPosition === AnchorPosition.BOTTOM_RIGHT || anchorPosition === AnchorPosition.TOP_RIGHT + const isShiftTop = anchorPosition === AnchorPosition.BOTTOM_LEFT || anchorPosition === AnchorPosition.BOTTOM_RIGHT + + const textFieldWidth = this.measureWidth(ctx) + const textFieldHeight = this.measureHeight() + const x = (isShiftLeft ? this.anchor.x - textFieldWidth : this.anchor.x) + const y = isShiftTop ? this.anchor.y - textFieldHeight : this.anchor.y + + // adjust anchor if text box exceeds canvas borders + if (canvasDims) { + const { width, height } = canvasDims + const newX = Math.max(Math.min(x, width - textFieldWidth), 0) + const newY = Math.max(Math.min(y, height - textFieldHeight), 0) + return { x: newX, y: newY } + } + return { x, y } + } + + draw(canvasArg: string | HTMLCanvasElement | CanvasRenderingContext2D) { + const canvas = resolveInput(canvasArg) + const ctx = getContext2dOrThrow(canvas) + + const { backgroundColor, fontColor, fontSize, fontStyle, padding } = this.options + + ctx.font = `${fontSize}px ${fontStyle}` + const maxTextWidth = this.measureWidth(ctx) + const textHeight = this.measureHeight() + + ctx.fillStyle = backgroundColor + const upperLeft = this.getUpperLeft(ctx, canvas) + ctx.fillRect(upperLeft.x, upperLeft.y, maxTextWidth, textHeight) + + ctx.fillStyle = fontColor; + this.text.forEach((textLine, i) => { + const x = padding + upperLeft.x + const y = padding + upperLeft.y + ((i + 1) * fontSize) + ctx.fillText(textLine, x, y) + }) + } +} \ No newline at end of file diff --git a/src/draw/drawContour.ts b/src/draw/drawContour.ts index b3548b81..9967cb00 100644 --- a/src/draw/drawContour.ts +++ b/src/draw/drawContour.ts @@ -1,4 +1,4 @@ -import { Point } from 'tfjs-image-recognition-base'; +import { Point } from '../classes'; export function drawContour( ctx: CanvasRenderingContext2D, diff --git a/src/draw/drawDetections.ts b/src/draw/drawDetections.ts index 161ba0d4..e61a7b00 100644 --- a/src/draw/drawDetections.ts +++ b/src/draw/drawDetections.ts @@ -1,7 +1,8 @@ -import { Box, draw, IBoundingBox, IRect, round } from 'tfjs-image-recognition-base'; - +import { Box, IBoundingBox, IRect } from '../classes'; import { FaceDetection } from '../classes/FaceDetection'; import { isWithFaceDetection, WithFaceDetection } from '../factories/WithFaceDetection'; +import { round } from '../utils'; +import { DrawBox } from './DrawBox'; export type TDrawDetectionsInput = IRect | IBoundingBox | FaceDetection | WithFaceDetection<{}> @@ -21,6 +22,6 @@ export function drawDetections( : (isWithFaceDetection(det) ? det.detection.box : new Box(det)) const label = score ? `${round(score)}` : undefined - new draw.DrawBox(box, { label }).draw(canvasArg) + new DrawBox(box, { label }).draw(canvasArg) }) } \ No newline at end of file diff --git a/src/draw/drawFaceExpressions.ts b/src/draw/drawFaceExpressions.ts index 4fac4a36..812adb4f 100644 --- a/src/draw/drawFaceExpressions.ts +++ b/src/draw/drawFaceExpressions.ts @@ -1,8 +1,9 @@ -import { draw, IPoint, Point, round } from 'tfjs-image-recognition-base'; - +import { IPoint, Point } from '../classes'; import { FaceExpressions } from '../faceExpressionNet'; import { isWithFaceDetection } from '../factories/WithFaceDetection'; import { isWithFaceExpressions, WithFaceExpressions } from '../factories/WithFaceExpressions'; +import { round } from '../utils'; +import { DrawTextField } from './DrawTextField'; export type DrawFaceExpressionsInput = FaceExpressions | WithFaceExpressions<{}> @@ -29,7 +30,7 @@ export function drawFaceExpressions( ? e.detection.box.bottomLeft : (textFieldAnchor || new Point(0, 0)) - const drawTextField = new draw.DrawTextField( + const drawTextField = new DrawTextField( resultsToDisplay.map(expr => `${expr.expression} (${round(expr.probability)})`), anchor ) diff --git a/src/draw/index.ts b/src/draw/index.ts index 7a54d1f7..5c756511 100644 --- a/src/draw/index.ts +++ b/src/draw/index.ts @@ -1,4 +1,6 @@ export * from './drawContour' export * from './drawDetections' export * from './drawFaceExpressions' -export * from './DrawFaceLandmarks' \ No newline at end of file +export * from './DrawBox' +export * from './DrawFaceLandmarks' +export * from './DrawTextField' \ No newline at end of file diff --git a/src/env/createBrowserEnv.ts b/src/env/createBrowserEnv.ts new file mode 100644 index 00000000..28cba43c --- /dev/null +++ b/src/env/createBrowserEnv.ts @@ -0,0 +1,24 @@ +import { Environment } from './types'; + +export function createBrowserEnv(): Environment { + + const fetch = window['fetch'] || function() { + throw new Error('fetch - missing fetch implementation for browser environment') + } + + const readFile = function() { + throw new Error('readFile - filesystem not available for browser environment') + } + + return { + Canvas: HTMLCanvasElement, + CanvasRenderingContext2D: CanvasRenderingContext2D, + Image: HTMLImageElement, + ImageData: ImageData, + Video: HTMLVideoElement, + createCanvasElement: () => document.createElement('canvas'), + createImageElement: () => document.createElement('img'), + fetch, + readFile + } +} \ No newline at end of file diff --git a/src/env/createFileSystem.ts b/src/env/createFileSystem.ts new file mode 100644 index 00000000..17344bd8 --- /dev/null +++ b/src/env/createFileSystem.ts @@ -0,0 +1,30 @@ +import { FileSystem } from './types'; + +export function createFileSystem(fs?: any): FileSystem { + + let requireFsError = '' + + if (!fs) { + try { + fs = require('fs') + } catch (err) { + requireFsError = err.toString() + } + } + + const readFile = fs + ? function(filePath: string) { + return new Promise((res, rej) => { + fs.readFile(filePath, function(err: any, buffer: Buffer) { + return err ? rej(err) : res(buffer) + }) + }) + } + : function() { + throw new Error(`readFile - failed to require fs in nodejs environment with error: ${requireFsError}`) + } + + return { + readFile + } +} \ No newline at end of file diff --git a/src/env/createNodejsEnv.ts b/src/env/createNodejsEnv.ts new file mode 100644 index 00000000..4a5e6414 --- /dev/null +++ b/src/env/createNodejsEnv.ts @@ -0,0 +1,40 @@ +import { createFileSystem } from './createFileSystem'; +import { Environment } from './types'; + +export function createNodejsEnv(): Environment { + + const Canvas = global['Canvas'] || global['HTMLCanvasElement'] + const Image = global['Image'] || global['HTMLImageElement'] + + const createCanvasElement = function() { + if (Canvas) { + return new Canvas() + } + throw new Error('createCanvasElement - missing Canvas implementation for nodejs environment') + } + + const createImageElement = function() { + if (Image) { + return new Image() + } + throw new Error('createImageElement - missing Image implementation for nodejs environment') + } + + const fetch = global['fetch'] || function() { + throw new Error('fetch - missing fetch implementation for nodejs environment') + } + + const fileSystem = createFileSystem() + + return { + Canvas: Canvas || class {}, + CanvasRenderingContext2D: global['CanvasRenderingContext2D'] || class {}, + Image: Image || class {}, + ImageData: global['ImageData'] || class {}, + Video: global['HTMLVideoElement'] || class {}, + createCanvasElement, + createImageElement, + fetch, + ...fileSystem + } +} \ No newline at end of file diff --git a/src/env/index.ts b/src/env/index.ts new file mode 100644 index 00000000..dcb4e894 --- /dev/null +++ b/src/env/index.ts @@ -0,0 +1,67 @@ +import { createBrowserEnv } from './createBrowserEnv'; +import { createFileSystem } from './createFileSystem'; +import { createNodejsEnv } from './createNodejsEnv'; +import { isBrowser } from './isBrowser'; +import { isNodejs } from './isNodejs'; +import { Environment } from './types'; + +let environment: Environment | null + +function getEnv(): Environment { + if (!environment) { + throw new Error('getEnv - environment is not defined, check isNodejs() and isBrowser()') + } + return environment +} + +function setEnv(env: Environment) { + environment = env +} + +function initialize() { + // check for isBrowser() first to prevent electron renderer process + // to be initialized with wrong environment due to isNodejs() returning true + if (isBrowser()) { + setEnv(createBrowserEnv()) + } + if (isNodejs()) { + setEnv(createNodejsEnv()) + } +} + +function monkeyPatch(env: Partial) { + if (!environment) { + initialize() + } + + if (!environment) { + throw new Error('monkeyPatch - environment is not defined, check isNodejs() and isBrowser()') + } + + const { Canvas = environment.Canvas, Image = environment.Image } = env + environment.Canvas = Canvas + environment.Image = Image + environment.createCanvasElement = env.createCanvasElement || (() => new Canvas()) + environment.createImageElement = env.createImageElement || (() => new Image()) + + environment.ImageData = env.ImageData || environment.ImageData + environment.Video = env.Video || environment.Video + environment.fetch = env.fetch || environment.fetch + environment.readFile = env.readFile || environment.readFile +} + +export const env = { + getEnv, + setEnv, + initialize, + createBrowserEnv, + createFileSystem, + createNodejsEnv, + monkeyPatch, + isBrowser, + isNodejs +} + +initialize() + +export * from './types' diff --git a/src/env/isBrowser.ts b/src/env/isBrowser.ts new file mode 100644 index 00000000..9dc5914a --- /dev/null +++ b/src/env/isBrowser.ts @@ -0,0 +1,9 @@ +export function isBrowser(): boolean { + return typeof window === 'object' + && typeof document !== 'undefined' + && typeof HTMLImageElement !== 'undefined' + && typeof HTMLCanvasElement !== 'undefined' + && typeof HTMLVideoElement !== 'undefined' + && typeof ImageData !== 'undefined' + && typeof CanvasRenderingContext2D !== 'undefined' +} \ No newline at end of file diff --git a/src/env/isNodejs.ts b/src/env/isNodejs.ts new file mode 100644 index 00000000..cd43dca9 --- /dev/null +++ b/src/env/isNodejs.ts @@ -0,0 +1,8 @@ +export function isNodejs(): boolean { + return typeof global === 'object' + && typeof require === 'function' + && typeof module !== 'undefined' + // issues with gatsby.js: module.exports is undefined + // && !!module.exports + && typeof process !== 'undefined' && !!process.version +} \ No newline at end of file diff --git a/src/env/types.ts b/src/env/types.ts new file mode 100644 index 00000000..fb767c0f --- /dev/null +++ b/src/env/types.ts @@ -0,0 +1,14 @@ +export type FileSystem = { + readFile: (filePath: string) => Promise +} + +export type Environment = FileSystem & { + Canvas: typeof HTMLCanvasElement + CanvasRenderingContext2D: typeof CanvasRenderingContext2D + Image: typeof HTMLImageElement + ImageData: typeof ImageData + Video: typeof HTMLVideoElement + createCanvasElement: () => HTMLCanvasElement + createImageElement: () => HTMLImageElement + fetch: (url: string, init?: RequestInit) => Promise +} diff --git a/src/faceExpressionNet/FaceExpressionNet.ts b/src/faceExpressionNet/FaceExpressionNet.ts index 0e14680d..f7d5da48 100644 --- a/src/faceExpressionNet/FaceExpressionNet.ts +++ b/src/faceExpressionNet/FaceExpressionNet.ts @@ -1,6 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { NetInput, TNetInput, toNetInput } from 'tfjs-image-recognition-base'; +import { NetInput, TNetInput, toNetInput } from '../dom'; import { FaceFeatureExtractor } from '../faceFeatureExtractor/FaceFeatureExtractor'; import { FaceFeatureExtractorParams } from '../faceFeatureExtractor/types'; import { FaceProcessor } from '../faceProcessor/FaceProcessor'; diff --git a/src/faceFeatureExtractor/FaceFeatureExtractor.ts b/src/faceFeatureExtractor/FaceFeatureExtractor.ts index 721f72a3..199da4c2 100644 --- a/src/faceFeatureExtractor/FaceFeatureExtractor.ts +++ b/src/faceFeatureExtractor/FaceFeatureExtractor.ts @@ -1,6 +1,8 @@ import * as tf from '@tensorflow/tfjs-core'; -import { NetInput, NeuralNetwork, normalize, TNetInput, toNetInput } from 'tfjs-image-recognition-base'; +import { NetInput, TNetInput, toNetInput } from '../dom'; +import { NeuralNetwork } from '../NeuralNetwork'; +import { normalize } from '../ops'; import { denseBlock4 } from './denseBlock'; import { extractParams } from './extractParams'; import { extractParamsFromWeigthMap } from './extractParamsFromWeigthMap'; diff --git a/src/faceFeatureExtractor/TinyFaceFeatureExtractor.ts b/src/faceFeatureExtractor/TinyFaceFeatureExtractor.ts index c81e152d..a4119351 100644 --- a/src/faceFeatureExtractor/TinyFaceFeatureExtractor.ts +++ b/src/faceFeatureExtractor/TinyFaceFeatureExtractor.ts @@ -1,6 +1,8 @@ import * as tf from '@tensorflow/tfjs-core'; -import { NetInput, NeuralNetwork, normalize, TNetInput, toNetInput } from 'tfjs-image-recognition-base'; +import { NetInput, TNetInput, toNetInput } from '../dom'; +import { NeuralNetwork } from '../NeuralNetwork'; +import { normalize } from '../ops'; import { denseBlock3 } from './denseBlock'; import { extractParamsFromWeigthMapTiny } from './extractParamsFromWeigthMapTiny'; import { extractParamsTiny } from './extractParamsTiny'; diff --git a/src/faceFeatureExtractor/denseBlock.ts b/src/faceFeatureExtractor/denseBlock.ts index 83fd7e50..92728ca4 100644 --- a/src/faceFeatureExtractor/denseBlock.ts +++ b/src/faceFeatureExtractor/denseBlock.ts @@ -1,6 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { ConvParams, SeparableConvParams } from '../common'; import { depthwiseSeparableConv } from '../common/depthwiseSeparableConv'; import { DenseBlock3Params, DenseBlock4Params } from './types'; @@ -13,10 +13,10 @@ export function denseBlock3( const out1 = tf.relu( isFirstLayer ? tf.add( - tf.conv2d(x, (denseBlockParams.conv0 as TfjsImageRecognitionBase.ConvParams).filters, [2, 2], 'same'), + tf.conv2d(x, (denseBlockParams.conv0 as ConvParams).filters, [2, 2], 'same'), denseBlockParams.conv0.bias ) - : depthwiseSeparableConv(x, denseBlockParams.conv0 as TfjsImageRecognitionBase.SeparableConvParams, [2, 2]) + : depthwiseSeparableConv(x, denseBlockParams.conv0 as SeparableConvParams, [2, 2]) ) as tf.Tensor4D const out2 = depthwiseSeparableConv(out1, denseBlockParams.conv1, [1, 1]) @@ -37,10 +37,10 @@ export function denseBlock4( const out1 = tf.relu( isFirstLayer ? tf.add( - tf.conv2d(x, (denseBlockParams.conv0 as TfjsImageRecognitionBase.ConvParams).filters, isScaleDown ? [2, 2] : [1, 1], 'same'), + tf.conv2d(x, (denseBlockParams.conv0 as ConvParams).filters, isScaleDown ? [2, 2] : [1, 1], 'same'), denseBlockParams.conv0.bias ) - : depthwiseSeparableConv(x, denseBlockParams.conv0 as TfjsImageRecognitionBase.SeparableConvParams, isScaleDown ? [2, 2] : [1, 1]) + : depthwiseSeparableConv(x, denseBlockParams.conv0 as SeparableConvParams, isScaleDown ? [2, 2] : [1, 1]) ) as tf.Tensor4D const out2 = depthwiseSeparableConv(out1, denseBlockParams.conv1, [1, 1]) diff --git a/src/faceFeatureExtractor/extractParams.ts b/src/faceFeatureExtractor/extractParams.ts index 66babc1d..1a28f144 100644 --- a/src/faceFeatureExtractor/extractParams.ts +++ b/src/faceFeatureExtractor/extractParams.ts @@ -1,16 +1,16 @@ - +import { extractWeightsFactory, ParamMapping } from '../common'; import { extractorsFactory } from './extractorsFactory'; import { FaceFeatureExtractorParams } from './types'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; -export function extractParams(weights: Float32Array): { params: FaceFeatureExtractorParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] +export function extractParams(weights: Float32Array): { params: FaceFeatureExtractorParams, paramMappings: ParamMapping[] } { + + const paramMappings: ParamMapping[] = [] const { extractWeights, getRemainingWeights - } = TfjsImageRecognitionBase.extractWeightsFactory(weights) + } = extractWeightsFactory(weights) const { extractDenseBlock4Params diff --git a/src/faceFeatureExtractor/extractParamsFromWeigthMap.ts b/src/faceFeatureExtractor/extractParamsFromWeigthMap.ts index e0096959..c20b682b 100644 --- a/src/faceFeatureExtractor/extractParamsFromWeigthMap.ts +++ b/src/faceFeatureExtractor/extractParamsFromWeigthMap.ts @@ -1,14 +1,14 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { disposeUnusedWeightTensors, ParamMapping } from '../common'; import { loadParamsFactory } from './loadParamsFactory'; import { FaceFeatureExtractorParams } from './types'; export function extractParamsFromWeigthMap( weightMap: tf.NamedTensorMap -): { params: FaceFeatureExtractorParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +): { params: FaceFeatureExtractorParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractDenseBlock4Params @@ -21,7 +21,7 @@ export function extractParamsFromWeigthMap( dense3: extractDenseBlock4Params('dense3') } - TfjsImageRecognitionBase.disposeUnusedWeightTensors(weightMap, paramMappings) + disposeUnusedWeightTensors(weightMap, paramMappings) return { params, paramMappings } } \ No newline at end of file diff --git a/src/faceFeatureExtractor/extractParamsFromWeigthMapTiny.ts b/src/faceFeatureExtractor/extractParamsFromWeigthMapTiny.ts index 8c1c582b..2c61f583 100644 --- a/src/faceFeatureExtractor/extractParamsFromWeigthMapTiny.ts +++ b/src/faceFeatureExtractor/extractParamsFromWeigthMapTiny.ts @@ -1,14 +1,14 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { disposeUnusedWeightTensors, ParamMapping } from '../common'; import { loadParamsFactory } from './loadParamsFactory'; import { TinyFaceFeatureExtractorParams } from './types'; export function extractParamsFromWeigthMapTiny( weightMap: tf.NamedTensorMap -): { params: TinyFaceFeatureExtractorParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +): { params: TinyFaceFeatureExtractorParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractDenseBlock3Params @@ -20,7 +20,7 @@ export function extractParamsFromWeigthMapTiny( dense2: extractDenseBlock3Params('dense2') } - TfjsImageRecognitionBase.disposeUnusedWeightTensors(weightMap, paramMappings) + disposeUnusedWeightTensors(weightMap, paramMappings) return { params, paramMappings } } \ No newline at end of file diff --git a/src/faceFeatureExtractor/extractParamsTiny.ts b/src/faceFeatureExtractor/extractParamsTiny.ts index 984b9ec8..bf56fc7b 100644 --- a/src/faceFeatureExtractor/extractParamsTiny.ts +++ b/src/faceFeatureExtractor/extractParamsTiny.ts @@ -1,17 +1,17 @@ -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; - +import { extractWeightsFactory, ParamMapping } from '../common'; import { extractorsFactory } from './extractorsFactory'; import { TinyFaceFeatureExtractorParams } from './types'; -export function extractParamsTiny(weights: Float32Array): { params: TinyFaceFeatureExtractorParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] +export function extractParamsTiny(weights: Float32Array): { params: TinyFaceFeatureExtractorParams, paramMappings: ParamMapping[] } { + + const paramMappings: ParamMapping[] = [] const { extractWeights, getRemainingWeights - } = TfjsImageRecognitionBase.extractWeightsFactory(weights) + } = extractWeightsFactory(weights) const { extractDenseBlock3Params diff --git a/src/faceFeatureExtractor/extractorsFactory.ts b/src/faceFeatureExtractor/extractorsFactory.ts index 59d2fe77..2061d66b 100644 --- a/src/faceFeatureExtractor/extractorsFactory.ts +++ b/src/faceFeatureExtractor/extractorsFactory.ts @@ -1,11 +1,15 @@ -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; - +import { + extractConvParamsFactory, + extractSeparableConvParamsFactory, + ExtractWeightsFunction, + ParamMapping, +} from '../common'; import { DenseBlock3Params, DenseBlock4Params } from './types'; -export function extractorsFactory(extractWeights: TfjsImageRecognitionBase.ExtractWeightsFunction, paramMappings: TfjsImageRecognitionBase.ParamMapping[]) { +export function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings: ParamMapping[]) { - const extractConvParams = TfjsImageRecognitionBase.extractConvParamsFactory(extractWeights, paramMappings) - const extractSeparableConvParams = TfjsImageRecognitionBase.extractSeparableConvParamsFactory(extractWeights, paramMappings) + const extractConvParams = extractConvParamsFactory(extractWeights, paramMappings) + const extractSeparableConvParams = extractSeparableConvParamsFactory(extractWeights, paramMappings) function extractDenseBlock3Params(channelsIn: number, channelsOut: number, mappedPrefix: string, isFirstLayer: boolean = false): DenseBlock3Params { diff --git a/src/faceFeatureExtractor/loadParamsFactory.ts b/src/faceFeatureExtractor/loadParamsFactory.ts index 3ce5b9c0..fd510ccb 100644 --- a/src/faceFeatureExtractor/loadParamsFactory.ts +++ b/src/faceFeatureExtractor/loadParamsFactory.ts @@ -1,14 +1,13 @@ -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; - +import { extractWeightEntryFactory, loadSeparableConvParamsFactory, ParamMapping } from '../common'; import { loadConvParamsFactory } from '../common/loadConvParamsFactory'; import { DenseBlock3Params, DenseBlock4Params } from './types'; -export function loadParamsFactory(weightMap: any, paramMappings: TfjsImageRecognitionBase.ParamMapping[]) { +export function loadParamsFactory(weightMap: any, paramMappings: ParamMapping[]) { - const extractWeightEntry = TfjsImageRecognitionBase.extractWeightEntryFactory(weightMap, paramMappings) + const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings) const extractConvParams = loadConvParamsFactory(extractWeightEntry) - const extractSeparableConvParams = TfjsImageRecognitionBase.loadSeparableConvParamsFactory(extractWeightEntry) + const extractSeparableConvParams = loadSeparableConvParamsFactory(extractWeightEntry) function extractDenseBlock3Params(prefix: string, isFirstLayer: boolean = false): DenseBlock3Params { const conv0 = isFirstLayer diff --git a/src/faceFeatureExtractor/types.ts b/src/faceFeatureExtractor/types.ts index b8a8c235..338b3daf 100644 --- a/src/faceFeatureExtractor/types.ts +++ b/src/faceFeatureExtractor/types.ts @@ -1,5 +1,8 @@ import * as tf from '@tensorflow/tfjs-core'; -import { NetInput, NeuralNetwork, TNetInput, TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; + +import { NetInput, TNetInput } from '..'; +import { ConvParams, SeparableConvParams } from '../common'; +import { NeuralNetwork } from '../NeuralNetwork'; export type ConvWithBatchNormParams = BatchNormParams & { filter: tf.Tensor4D @@ -18,13 +21,13 @@ export type SeparableConvWithBatchNormParams = { } export type DenseBlock3Params = { - conv0: TfjsImageRecognitionBase.SeparableConvParams | TfjsImageRecognitionBase.ConvParams - conv1: TfjsImageRecognitionBase.SeparableConvParams - conv2: TfjsImageRecognitionBase.SeparableConvParams + conv0: SeparableConvParams | ConvParams + conv1: SeparableConvParams + conv2: SeparableConvParams } export type DenseBlock4Params = DenseBlock3Params & { - conv3: TfjsImageRecognitionBase.SeparableConvParams + conv3: SeparableConvParams } export type TinyFaceFeatureExtractorParams = { diff --git a/src/faceLandmarkNet/FaceLandmark68NetBase.ts b/src/faceLandmarkNet/FaceLandmark68NetBase.ts index 63e40e20..10c53567 100644 --- a/src/faceLandmarkNet/FaceLandmark68NetBase.ts +++ b/src/faceLandmarkNet/FaceLandmark68NetBase.ts @@ -1,9 +1,11 @@ import * as tf from '@tensorflow/tfjs-core'; -import { IDimensions, isEven, NetInput, Point, TNetInput, toNetInput } from 'tfjs-image-recognition-base'; +import { IDimensions, Point } from '../classes'; import { FaceLandmarks68 } from '../classes/FaceLandmarks68'; +import { NetInput, TNetInput, toNetInput } from '../dom'; import { FaceFeatureExtractorParams, TinyFaceFeatureExtractorParams } from '../faceFeatureExtractor/types'; import { FaceProcessor } from '../faceProcessor/FaceProcessor'; +import { isEven } from '../utils'; export abstract class FaceLandmark68NetBase< TExtractorParams extends FaceFeatureExtractorParams | TinyFaceFeatureExtractorParams diff --git a/src/faceProcessor/FaceProcessor.ts b/src/faceProcessor/FaceProcessor.ts index 897772c1..2c674b2e 100644 --- a/src/faceProcessor/FaceProcessor.ts +++ b/src/faceProcessor/FaceProcessor.ts @@ -1,12 +1,13 @@ import * as tf from '@tensorflow/tfjs-core'; -import { NetInput, NeuralNetwork } from 'tfjs-image-recognition-base'; import { fullyConnectedLayer } from '../common/fullyConnectedLayer'; +import { NetInput } from '../dom'; import { FaceFeatureExtractorParams, IFaceFeatureExtractor, TinyFaceFeatureExtractorParams, } from '../faceFeatureExtractor/types'; +import { NeuralNetwork } from '../NeuralNetwork'; import { extractParams } from './extractParams'; import { extractParamsFromWeigthMap } from './extractParamsFromWeigthMap'; import { NetParams } from './types'; diff --git a/src/faceProcessor/extractParams.ts b/src/faceProcessor/extractParams.ts index ee19ecac..a86b8c7d 100644 --- a/src/faceProcessor/extractParams.ts +++ b/src/faceProcessor/extractParams.ts @@ -1,17 +1,16 @@ -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; - +import { extractFCParamsFactory, extractWeightsFactory, ParamMapping } from '../common'; import { NetParams } from './types'; -export function extractParams(weights: Float32Array, channelsIn: number, channelsOut: number): { params: NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +export function extractParams(weights: Float32Array, channelsIn: number, channelsOut: number): { params: NetParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractWeights, getRemainingWeights - } = TfjsImageRecognitionBase.extractWeightsFactory(weights) + } = extractWeightsFactory(weights) - const extractFCParams = TfjsImageRecognitionBase.extractFCParamsFactory(extractWeights, paramMappings) + const extractFCParams = extractFCParamsFactory(extractWeights, paramMappings) const fc = extractFCParams(channelsIn, channelsOut, 'fc') diff --git a/src/faceProcessor/extractParamsFromWeigthMap.ts b/src/faceProcessor/extractParamsFromWeigthMap.ts index ea469867..30c923f3 100644 --- a/src/faceProcessor/extractParamsFromWeigthMap.ts +++ b/src/faceProcessor/extractParamsFromWeigthMap.ts @@ -1,17 +1,17 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { disposeUnusedWeightTensors, extractWeightEntryFactory, FCParams, ParamMapping } from '../common'; import { NetParams } from './types'; export function extractParamsFromWeigthMap( weightMap: tf.NamedTensorMap -): { params: NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +): { params: NetParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] - const extractWeightEntry = TfjsImageRecognitionBase.extractWeightEntryFactory(weightMap, paramMappings) + const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings) - function extractFcParams(prefix: string): TfjsImageRecognitionBase.FCParams { + function extractFcParams(prefix: string): FCParams { const weights = extractWeightEntry(`${prefix}/weights`, 2) const bias = extractWeightEntry(`${prefix}/bias`, 1) return { weights, bias } @@ -21,7 +21,7 @@ export function extractParamsFromWeigthMap( fc: extractFcParams('fc') } - TfjsImageRecognitionBase.disposeUnusedWeightTensors(weightMap, paramMappings) + disposeUnusedWeightTensors(weightMap, paramMappings) return { params, paramMappings } } \ No newline at end of file diff --git a/src/faceProcessor/types.ts b/src/faceProcessor/types.ts index b2b6ff8f..a9394f7d 100644 --- a/src/faceProcessor/types.ts +++ b/src/faceProcessor/types.ts @@ -1,6 +1,6 @@ -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { FCParams } from '../common'; export type NetParams = { - fc: TfjsImageRecognitionBase.FCParams + fc: FCParams } diff --git a/src/faceRecognitionNet/FaceRecognitionNet.ts b/src/faceRecognitionNet/FaceRecognitionNet.ts index 30ffddd7..daf5d214 100644 --- a/src/faceRecognitionNet/FaceRecognitionNet.ts +++ b/src/faceRecognitionNet/FaceRecognitionNet.ts @@ -1,6 +1,8 @@ import * as tf from '@tensorflow/tfjs-core'; -import { NetInput, NeuralNetwork, normalize, TNetInput, toNetInput } from 'tfjs-image-recognition-base'; +import { NetInput, TNetInput, toNetInput } from '../dom'; +import { NeuralNetwork } from '../NeuralNetwork'; +import { normalize } from '../ops'; import { convDown } from './convLayer'; import { extractParams } from './extractParams'; import { extractParamsFromWeigthMap } from './extractParamsFromWeigthMap'; diff --git a/src/faceRecognitionNet/extractParams.ts b/src/faceRecognitionNet/extractParams.ts index f9a284dc..24600cc6 100644 --- a/src/faceRecognitionNet/extractParams.ts +++ b/src/faceRecognitionNet/extractParams.ts @@ -1,9 +1,10 @@ import * as tf from '@tensorflow/tfjs-core'; -import { isFloat, TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { ConvParams, extractWeightsFactory, ExtractWeightsFunction, ParamMapping } from '../common'; +import { isFloat } from '../utils'; import { ConvLayerParams, NetParams, ResidualLayerParams, ScaleLayerParams } from './types'; -function extractorsFactory(extractWeights: TfjsImageRecognitionBase.ExtractWeightsFunction, paramMappings: TfjsImageRecognitionBase.ParamMapping[]) { +function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings: ParamMapping[]) { function extractFilterValues(numFilterValues: number, numFilters: number, filterSize: number): tf.Tensor4D { const weights = extractWeights(numFilterValues) @@ -26,7 +27,7 @@ function extractorsFactory(extractWeights: TfjsImageRecognitionBase.ExtractWeigh numFilters: number, filterSize: number, mappedPrefix: string - ): TfjsImageRecognitionBase.ConvParams { + ): ConvParams { const filters = extractFilterValues(numFilterValues, numFilters, filterSize) const bias = tf.tensor1d(extractWeights(numFilters)) @@ -89,14 +90,14 @@ function extractorsFactory(extractWeights: TfjsImageRecognitionBase.ExtractWeigh } -export function extractParams(weights: Float32Array): { params: NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +export function extractParams(weights: Float32Array): { params: NetParams, paramMappings: ParamMapping[] } { const { extractWeights, getRemainingWeights - } = TfjsImageRecognitionBase.extractWeightsFactory(weights) + } = extractWeightsFactory(weights) - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractConvLayerParams, diff --git a/src/faceRecognitionNet/extractParamsFromWeigthMap.ts b/src/faceRecognitionNet/extractParamsFromWeigthMap.ts index aaf4c187..04c5bff0 100644 --- a/src/faceRecognitionNet/extractParamsFromWeigthMap.ts +++ b/src/faceRecognitionNet/extractParamsFromWeigthMap.ts @@ -1,11 +1,12 @@ import * as tf from '@tensorflow/tfjs-core'; -import { isTensor2D, TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { disposeUnusedWeightTensors, extractWeightEntryFactory, ParamMapping } from '../common'; +import { isTensor2D } from '../utils'; import { ConvLayerParams, NetParams, ResidualLayerParams, ScaleLayerParams } from './types'; -function extractorsFactory(weightMap: any, paramMappings: TfjsImageRecognitionBase.ParamMapping[]) { +function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) { - const extractWeightEntry = TfjsImageRecognitionBase.extractWeightEntryFactory(weightMap, paramMappings) + const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings) function extractScaleLayerParams(prefix: string): ScaleLayerParams { @@ -40,9 +41,9 @@ function extractorsFactory(weightMap: any, paramMappings: TfjsImageRecognitionBa export function extractParamsFromWeigthMap( weightMap: tf.NamedTensorMap -): { params: NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +): { params: NetParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractConvLayerParams, @@ -94,7 +95,7 @@ export function extractParamsFromWeigthMap( fc } - TfjsImageRecognitionBase.disposeUnusedWeightTensors(weightMap, paramMappings) + disposeUnusedWeightTensors(weightMap, paramMappings) return { params, paramMappings } } \ No newline at end of file diff --git a/src/faceRecognitionNet/types.ts b/src/faceRecognitionNet/types.ts index 5169fee9..e6fe12bc 100644 --- a/src/faceRecognitionNet/types.ts +++ b/src/faceRecognitionNet/types.ts @@ -1,5 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; + +import { ConvParams } from '../common'; export type ScaleLayerParams = { weights: tf.Tensor1D @@ -11,7 +12,7 @@ export type ResidualLayerParams = { } export type ConvLayerParams = { - conv: TfjsImageRecognitionBase.ConvParams + conv: ConvParams scale: ScaleLayerParams } diff --git a/src/factories/WithGender.ts b/src/factories/WithGender.ts index 5dbcca88..78452fa8 100644 --- a/src/factories/WithGender.ts +++ b/src/factories/WithGender.ts @@ -1,6 +1,5 @@ -import { isValidProbablitiy } from 'tfjs-image-recognition-base'; - import { Gender } from '../ageGenderNet/types'; +import { isValidProbablitiy } from '../utils'; export type WithGender = TSource & { gender: Gender diff --git a/src/globalApi/ComputeFaceDescriptorsTasks.ts b/src/globalApi/ComputeFaceDescriptorsTasks.ts index 795b2a6b..4f2ff954 100644 --- a/src/globalApi/ComputeFaceDescriptorsTasks.ts +++ b/src/globalApi/ComputeFaceDescriptorsTasks.ts @@ -1,5 +1,4 @@ -import { TNetInput } from 'tfjs-image-recognition-base'; - +import { TNetInput } from '../dom'; import { extendWithFaceDescriptor, WithFaceDescriptor } from '../factories/WithFaceDescriptor'; import { WithFaceDetection } from '../factories/WithFaceDetection'; import { WithFaceLandmarks } from '../factories/WithFaceLandmarks'; diff --git a/src/globalApi/DetectFaceLandmarksTasks.ts b/src/globalApi/DetectFaceLandmarksTasks.ts index 1e1acb53..827f5168 100644 --- a/src/globalApi/DetectFaceLandmarksTasks.ts +++ b/src/globalApi/DetectFaceLandmarksTasks.ts @@ -1,8 +1,7 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TNetInput } from 'tfjs-image-recognition-base'; import { FaceLandmarks68 } from '../classes/FaceLandmarks68'; -import { extractFaces, extractFaceTensors } from '../dom'; +import { extractFaces, extractFaceTensors, TNetInput } from '../dom'; import { FaceLandmark68Net } from '../faceLandmarkNet/FaceLandmark68Net'; import { FaceLandmark68TinyNet } from '../faceLandmarkNet/FaceLandmark68TinyNet'; import { WithFaceDetection } from '../factories/WithFaceDetection'; diff --git a/src/globalApi/DetectFacesTasks.ts b/src/globalApi/DetectFacesTasks.ts index 6be96085..8c1644b8 100644 --- a/src/globalApi/DetectFacesTasks.ts +++ b/src/globalApi/DetectFacesTasks.ts @@ -1,10 +1,10 @@ -import { TfjsImageRecognitionBase, TNetInput } from 'tfjs-image-recognition-base'; - import { FaceDetection } from '../classes/FaceDetection'; +import { TNetInput } from '../dom'; import { extendWithFaceDetection, WithFaceDetection } from '../factories/WithFaceDetection'; import { MtcnnOptions } from '../mtcnn/MtcnnOptions'; import { SsdMobilenetv1Options } from '../ssdMobilenetv1/SsdMobilenetv1Options'; import { TinyFaceDetectorOptions } from '../tinyFaceDetector/TinyFaceDetectorOptions'; +import { TinyYolov2Options } from '../tinyYolov2'; import { ComposableTask } from './ComposableTask'; import { DetectAllFaceLandmarksTask, DetectSingleFaceLandmarksTask } from './DetectFaceLandmarksTasks'; import { nets } from './nets'; @@ -38,7 +38,7 @@ export class DetectAllFacesTask extends DetectFacesTaskBase { options instanceof SsdMobilenetv1Options ? (input: TNetInput) => nets.ssdMobilenetv1.locateFaces(input, options) : ( - options instanceof TfjsImageRecognitionBase.TinyYolov2Options + options instanceof TinyYolov2Options ? (input: TNetInput) => nets.tinyYolov2.locateFaces(input, options) : null ) diff --git a/src/globalApi/PredictAgeAndGenderTask.ts b/src/globalApi/PredictAgeAndGenderTask.ts index 3d8b46ed..def31c19 100644 --- a/src/globalApi/PredictAgeAndGenderTask.ts +++ b/src/globalApi/PredictAgeAndGenderTask.ts @@ -1,7 +1,7 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TNetInput } from 'tfjs-image-recognition-base'; import { AgeAndGenderPrediction } from '../ageGenderNet/types'; +import { TNetInput } from '../dom'; import { extendWithAge, WithAge } from '../factories/WithAge'; import { WithFaceDetection } from '../factories/WithFaceDetection'; import { WithFaceLandmarks } from '../factories/WithFaceLandmarks'; diff --git a/src/globalApi/PredictFaceExpressionsTask.ts b/src/globalApi/PredictFaceExpressionsTask.ts index 27ad893b..5eb77326 100644 --- a/src/globalApi/PredictFaceExpressionsTask.ts +++ b/src/globalApi/PredictFaceExpressionsTask.ts @@ -1,6 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TNetInput } from 'tfjs-image-recognition-base'; +import { TNetInput } from '../dom'; import { FaceExpressions } from '../faceExpressionNet/FaceExpressions'; import { WithFaceDetection } from '../factories/WithFaceDetection'; import { extendWithFaceExpressions, WithFaceExpressions } from '../factories/WithFaceExpressions'; diff --git a/src/globalApi/allFaces.ts b/src/globalApi/allFaces.ts index dc780cfa..06d7542b 100644 --- a/src/globalApi/allFaces.ts +++ b/src/globalApi/allFaces.ts @@ -1,8 +1,8 @@ -import { TfjsImageRecognitionBase, TNetInput } from 'tfjs-image-recognition-base'; - +import { TNetInput } from '../dom'; import { WithFaceDescriptor, WithFaceDetection, WithFaceLandmarks } from '../factories'; import { IMtcnnOptions, MtcnnOptions } from '../mtcnn/MtcnnOptions'; import { SsdMobilenetv1Options } from '../ssdMobilenetv1'; +import { ITinyYolov2Options, TinyYolov2Options } from '../tinyYolov2'; import { detectAllFaces } from './detectFaces'; // export allFaces API for backward compatibility @@ -18,9 +18,9 @@ export async function allFacesSsdMobilenetv1( export async function allFacesTinyYolov2( input: TNetInput, - forwardParams: TfjsImageRecognitionBase.ITinyYolov2Options = {} + forwardParams: ITinyYolov2Options = {} ): Promise>>[]> { - return await detectAllFaces(input, new TfjsImageRecognitionBase.TinyYolov2Options(forwardParams)) + return await detectAllFaces(input, new TinyYolov2Options(forwardParams)) .withFaceLandmarks() .withFaceDescriptors() } diff --git a/src/globalApi/detectFaces.ts b/src/globalApi/detectFaces.ts index 91f2c5c1..c5641dc7 100644 --- a/src/globalApi/detectFaces.ts +++ b/src/globalApi/detectFaces.ts @@ -1,5 +1,4 @@ -import { TNetInput } from 'tfjs-image-recognition-base'; - +import { TNetInput } from '../dom'; import { SsdMobilenetv1Options } from '../ssdMobilenetv1/SsdMobilenetv1Options'; import { DetectAllFacesTask, DetectSingleFaceTask } from './DetectFacesTasks'; import { FaceDetectionOptions } from './types'; diff --git a/src/globalApi/extractFacesAndComputeResults.ts b/src/globalApi/extractFacesAndComputeResults.ts index 2256218b..468b8c14 100644 --- a/src/globalApi/extractFacesAndComputeResults.ts +++ b/src/globalApi/extractFacesAndComputeResults.ts @@ -1,8 +1,7 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TNetInput } from 'tfjs-image-recognition-base'; import { FaceDetection } from '../classes/FaceDetection'; -import { extractFaces, extractFaceTensors } from '../dom'; +import { extractFaces, extractFaceTensors, TNetInput } from '../dom'; import { WithFaceDetection } from '../factories/WithFaceDetection'; import { isWithFaceLandmarks, WithFaceLandmarks } from '../factories/WithFaceLandmarks'; diff --git a/src/globalApi/nets.ts b/src/globalApi/nets.ts index 1815851d..36835da9 100644 --- a/src/globalApi/nets.ts +++ b/src/globalApi/nets.ts @@ -1,10 +1,9 @@ -import { TfjsImageRecognitionBase, TNetInput } from 'tfjs-image-recognition-base'; - import { AgeGenderNet } from '../ageGenderNet/AgeGenderNet'; import { AgeAndGenderPrediction } from '../ageGenderNet/types'; import { FaceDetection } from '../classes/FaceDetection'; import { FaceLandmarks5 } from '../classes/FaceLandmarks5'; import { FaceLandmarks68 } from '../classes/FaceLandmarks68'; +import { TNetInput } from '../dom'; import { FaceExpressionNet } from '../faceExpressionNet/FaceExpressionNet'; import { FaceExpressions } from '../faceExpressionNet/FaceExpressions'; import { FaceLandmark68Net } from '../faceLandmarkNet/FaceLandmark68Net'; @@ -18,7 +17,7 @@ import { SsdMobilenetv1 } from '../ssdMobilenetv1/SsdMobilenetv1'; import { SsdMobilenetv1Options } from '../ssdMobilenetv1/SsdMobilenetv1Options'; import { TinyFaceDetector } from '../tinyFaceDetector/TinyFaceDetector'; import { TinyFaceDetectorOptions } from '../tinyFaceDetector/TinyFaceDetectorOptions'; -import { TinyYolov2 } from '../tinyYolov2'; +import { ITinyYolov2Options, TinyYolov2 } from '../tinyYolov2'; export const nets = { ssdMobilenetv1: new SsdMobilenetv1(), @@ -59,7 +58,7 @@ export const tinyFaceDetector = (input: TNetInput, options: TinyFaceDetectorOpti * @param options (optional, default: see TinyYolov2Options constructor for default parameters). * @returns Bounding box of each face with score. */ -export const tinyYolov2 = (input: TNetInput, options: TfjsImageRecognitionBase.ITinyYolov2Options): Promise => +export const tinyYolov2 = (input: TNetInput, options: ITinyYolov2Options): Promise => nets.tinyYolov2.locateFaces(input, options) /** diff --git a/src/globalApi/types.ts b/src/globalApi/types.ts index 2b11a451..8b187502 100644 --- a/src/globalApi/types.ts +++ b/src/globalApi/types.ts @@ -1,10 +1,10 @@ -import { TfjsImageRecognitionBase, TNetInput } from 'tfjs-image-recognition-base'; - import { FaceDetection } from '../classes/FaceDetection'; +import { TNetInput } from '../dom'; import { MtcnnOptions } from '../mtcnn/MtcnnOptions'; import { SsdMobilenetv1Options } from '../ssdMobilenetv1/SsdMobilenetv1Options'; import { TinyFaceDetectorOptions } from '../tinyFaceDetector/TinyFaceDetectorOptions'; +import { TinyYolov2Options } from '../tinyYolov2'; -export type FaceDetectionOptions = TinyFaceDetectorOptions | SsdMobilenetv1Options | MtcnnOptions | TfjsImageRecognitionBase.TinyYolov2Options +export type FaceDetectionOptions = TinyFaceDetectorOptions | SsdMobilenetv1Options | MtcnnOptions | TinyYolov2Options export type FaceDetectionFunction = (input: TNetInput) => Promise \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 480388b2..bea3d299 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,29 +1,29 @@ import * as tf from '@tensorflow/tfjs-core'; -import { draw as drawBase } from 'tfjs-image-recognition-base'; -import * as drawExtended from './draw'; +import * as draw from './draw'; +import * as utils from './utils'; export { + draw, + utils, tf } -export * from 'tfjs-image-recognition-base'; - export * from './ageGenderNet/index'; -const draw = {...drawBase, ...drawExtended } -export { draw } - export * from './classes/index'; export * from './dom/index' +export * from './env/index'; export * from './faceExpressionNet/index'; export * from './faceLandmarkNet/index'; export * from './faceRecognitionNet/index'; export * from './factories/index'; export * from './globalApi/index'; export * from './mtcnn/index'; +export * from './ops/index'; export * from './ssdMobilenetv1/index'; export * from './tinyFaceDetector/index'; export * from './tinyYolov2/index'; export * from './euclideanDistance'; +export * from './NeuralNetwork'; export * from './resizeResults'; \ No newline at end of file diff --git a/src/mtcnn/Mtcnn.ts b/src/mtcnn/Mtcnn.ts index 0f1c08c8..41d2642d 100644 --- a/src/mtcnn/Mtcnn.ts +++ b/src/mtcnn/Mtcnn.ts @@ -1,9 +1,11 @@ import * as tf from '@tensorflow/tfjs-core'; -import { NetInput, NeuralNetwork, Point, Rect, TNetInput, toNetInput } from 'tfjs-image-recognition-base'; +import { Point, Rect } from '../classes'; import { FaceDetection } from '../classes/FaceDetection'; import { FaceLandmarks5 } from '../classes/FaceLandmarks5'; +import { NetInput, TNetInput, toNetInput } from '../dom'; import { extendWithFaceDetection, extendWithFaceLandmarks } from '../factories'; +import { NeuralNetwork } from '../NeuralNetwork'; import { bgrToRgbTensor } from './bgrToRgbTensor'; import { CELL_SIZE } from './config'; import { extractParams } from './extractParams'; diff --git a/src/mtcnn/MtcnnBox.ts b/src/mtcnn/MtcnnBox.ts index cc81c58a..925d8bf1 100644 --- a/src/mtcnn/MtcnnBox.ts +++ b/src/mtcnn/MtcnnBox.ts @@ -1,4 +1,4 @@ -import { Box } from 'tfjs-image-recognition-base'; +import { Box } from '../classes'; export class MtcnnBox extends Box { constructor(left: number, top: number, right: number, bottom: number) { diff --git a/src/mtcnn/ONet.ts b/src/mtcnn/ONet.ts index aa78ac8a..b0564725 100644 --- a/src/mtcnn/ONet.ts +++ b/src/mtcnn/ONet.ts @@ -1,6 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { convLayer } from '../common'; import { fullyConnectedLayer } from '../common/fullyConnectedLayer'; import { prelu } from './prelu'; import { sharedLayer } from './sharedLayers'; @@ -11,7 +11,7 @@ export function ONet(x: tf.Tensor4D, params: ONetParams): { scores: tf.Tensor1D, let out = sharedLayer(x, params) out = tf.maxPool(out, [2, 2], [2, 2], 'same') - out = TfjsImageRecognitionBase.convLayer(out, params.conv4, 'valid') + out = convLayer(out, params.conv4, 'valid') out = prelu(out, params.prelu4_alpha) const vectorized = tf.reshape(out, [out.shape[0], params.fc1.weights.shape[0]]) as tf.Tensor2D diff --git a/src/mtcnn/PNet.ts b/src/mtcnn/PNet.ts index a6d5ade1..649c6ffe 100644 --- a/src/mtcnn/PNet.ts +++ b/src/mtcnn/PNet.ts @@ -1,6 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { convLayer } from '../common'; import { sharedLayer } from './sharedLayers'; import { PNetParams } from './types'; @@ -8,10 +8,10 @@ export function PNet(x: tf.Tensor4D, params: PNetParams): { prob: tf.Tensor4D, r return tf.tidy(() => { let out = sharedLayer(x, params, true) - const conv = TfjsImageRecognitionBase.convLayer(out, params.conv4_1, 'valid') + const conv = convLayer(out, params.conv4_1, 'valid') const max = tf.expandDims(tf.max(conv, 3), 3) const prob = tf.softmax(tf.sub(conv, max), 3) as tf.Tensor4D - const regions = TfjsImageRecognitionBase.convLayer(out, params.conv4_2, 'valid') + const regions = convLayer(out, params.conv4_2, 'valid') return { prob, regions } }) diff --git a/src/mtcnn/extractImagePatches.ts b/src/mtcnn/extractImagePatches.ts index 56ad60ac..dbcb4c71 100644 --- a/src/mtcnn/extractImagePatches.ts +++ b/src/mtcnn/extractImagePatches.ts @@ -1,13 +1,8 @@ import * as tf from '@tensorflow/tfjs-core'; -import { - Box, - createCanvas, - createCanvasFromMedia, - env, - getContext2dOrThrow, - IDimensions, -} from 'tfjs-image-recognition-base'; +import { Box, IDimensions } from '../classes'; +import { createCanvas, createCanvasFromMedia, getContext2dOrThrow } from '../dom'; +import { env } from '../env'; import { normalize } from './normalize'; export async function extractImagePatches( diff --git a/src/mtcnn/extractParams.ts b/src/mtcnn/extractParams.ts index cc586a14..3e6edff4 100644 --- a/src/mtcnn/extractParams.ts +++ b/src/mtcnn/extractParams.ts @@ -1,12 +1,18 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { + extractConvParamsFactory, + extractFCParamsFactory, + extractWeightsFactory, + ExtractWeightsFunction, + ParamMapping, +} from '../common'; import { NetParams, ONetParams, PNetParams, RNetParams, SharedParams } from './types'; -function extractorsFactory(extractWeights: TfjsImageRecognitionBase.ExtractWeightsFunction, paramMappings: TfjsImageRecognitionBase.ParamMapping[]) { +function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings: ParamMapping[]) { - const extractConvParams = TfjsImageRecognitionBase.extractConvParamsFactory(extractWeights, paramMappings) - const extractFCParams = TfjsImageRecognitionBase.extractFCParamsFactory(extractWeights, paramMappings) + const extractConvParams = extractConvParamsFactory(extractWeights, paramMappings) + const extractFCParams = extractFCParamsFactory(extractWeights, paramMappings) function extractPReluParams(size: number, paramPath: string): tf.Tensor1D { const alpha = tf.tensor1d(extractWeights(size)) @@ -68,14 +74,14 @@ function extractorsFactory(extractWeights: TfjsImageRecognitionBase.ExtractWeigh } -export function extractParams(weights: Float32Array): { params: NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +export function extractParams(weights: Float32Array): { params: NetParams, paramMappings: ParamMapping[] } { const { extractWeights, getRemainingWeights - } = TfjsImageRecognitionBase.extractWeightsFactory(weights) + } = extractWeightsFactory(weights) - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractPNetParams, diff --git a/src/mtcnn/extractParamsFromWeigthMap.ts b/src/mtcnn/extractParamsFromWeigthMap.ts index 78a13353..ffcc0b72 100644 --- a/src/mtcnn/extractParamsFromWeigthMap.ts +++ b/src/mtcnn/extractParamsFromWeigthMap.ts @@ -1,20 +1,20 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { ConvParams, disposeUnusedWeightTensors, extractWeightEntryFactory, FCParams, ParamMapping } from '../common'; import { NetParams, ONetParams, PNetParams, RNetParams, SharedParams } from './types'; -function extractorsFactory(weightMap: any, paramMappings: TfjsImageRecognitionBase.ParamMapping[]) { +function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) { - const extractWeightEntry = TfjsImageRecognitionBase.extractWeightEntryFactory(weightMap, paramMappings) + const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings) - function extractConvParams(prefix: string): TfjsImageRecognitionBase.ConvParams { + function extractConvParams(prefix: string): ConvParams { const filters = extractWeightEntry(`${prefix}/weights`, 4, `${prefix}/filters`) const bias = extractWeightEntry(`${prefix}/bias`, 1) return { filters, bias } } - function extractFCParams(prefix: string): TfjsImageRecognitionBase.FCParams { + function extractFCParams(prefix: string): FCParams { const weights = extractWeightEntry(`${prefix}/weights`, 2) const bias = extractWeightEntry(`${prefix}/bias`, 1) @@ -81,9 +81,9 @@ function extractorsFactory(weightMap: any, paramMappings: TfjsImageRecognitionBa export function extractParamsFromWeigthMap( weightMap: tf.NamedTensorMap -): { params: NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +): { params: NetParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractPNetParams, @@ -95,7 +95,7 @@ export function extractParamsFromWeigthMap( const rnet = extractRNetParams() const onet = extractONetParams() - TfjsImageRecognitionBase.disposeUnusedWeightTensors(weightMap, paramMappings) + disposeUnusedWeightTensors(weightMap, paramMappings) return { params: { pnet, rnet, onet }, paramMappings } } \ No newline at end of file diff --git a/src/mtcnn/pyramidDown.ts b/src/mtcnn/pyramidDown.ts index c6bccd2f..9b6aa3b4 100644 --- a/src/mtcnn/pyramidDown.ts +++ b/src/mtcnn/pyramidDown.ts @@ -1,4 +1,4 @@ -import { CELL_SIZE } from './config'; +import { CELL_SIZE } from './config' export function pyramidDown(minFaceSize: number, scaleFactor: number, dims: number[]): number[] { diff --git a/src/mtcnn/sharedLayers.ts b/src/mtcnn/sharedLayers.ts index f226a1f1..5c43f32c 100644 --- a/src/mtcnn/sharedLayers.ts +++ b/src/mtcnn/sharedLayers.ts @@ -1,19 +1,19 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { convLayer } from '../common'; import { prelu } from './prelu'; import { SharedParams } from './types'; export function sharedLayer(x: tf.Tensor4D, params: SharedParams, isPnet: boolean = false) { return tf.tidy(() => { - let out = TfjsImageRecognitionBase.convLayer(x, params.conv1, 'valid') + let out = convLayer(x, params.conv1, 'valid') out = prelu(out, params.prelu1_alpha) out = tf.maxPool(out, isPnet ? [2, 2]: [3, 3], [2, 2], 'same') - out = TfjsImageRecognitionBase.convLayer(out, params.conv2, 'valid') + out = convLayer(out, params.conv2, 'valid') out = prelu(out, params.prelu2_alpha) out = isPnet ? out : tf.maxPool(out, [3, 3], [2, 2], 'valid') - out = TfjsImageRecognitionBase.convLayer(out, params.conv3, 'valid') + out = convLayer(out, params.conv3, 'valid') out = prelu(out, params.prelu3_alpha) return out diff --git a/src/mtcnn/stage1.ts b/src/mtcnn/stage1.ts index fabe124e..17b19ccd 100644 --- a/src/mtcnn/stage1.ts +++ b/src/mtcnn/stage1.ts @@ -1,6 +1,7 @@ import * as tf from '@tensorflow/tfjs-core'; -import { BoundingBox, nonMaxSuppression, Point } from 'tfjs-image-recognition-base'; +import { BoundingBox, Point } from '../classes'; +import { nonMaxSuppression } from '../ops'; import { CELL_SIZE, CELL_STRIDE } from './config'; import { getSizesForScale } from './getSizesForScale'; import { MtcnnBox } from './MtcnnBox'; diff --git a/src/mtcnn/stage2.ts b/src/mtcnn/stage2.ts index a172c08a..68a74cad 100644 --- a/src/mtcnn/stage2.ts +++ b/src/mtcnn/stage2.ts @@ -1,6 +1,7 @@ import * as tf from '@tensorflow/tfjs-core'; -import { Box, nonMaxSuppression } from 'tfjs-image-recognition-base'; +import { Box } from '../classes'; +import { nonMaxSuppression } from '../ops'; import { extractImagePatches } from './extractImagePatches'; import { MtcnnBox } from './MtcnnBox'; import { RNet } from './RNet'; diff --git a/src/mtcnn/stage3.ts b/src/mtcnn/stage3.ts index afbde407..3713390f 100644 --- a/src/mtcnn/stage3.ts +++ b/src/mtcnn/stage3.ts @@ -1,6 +1,7 @@ import * as tf from '@tensorflow/tfjs-core'; -import { BoundingBox, Box, nonMaxSuppression, Point } from 'tfjs-image-recognition-base'; +import { BoundingBox, Box, Point } from '../classes'; +import { nonMaxSuppression } from '../ops'; import { extractImagePatches } from './extractImagePatches'; import { MtcnnBox } from './MtcnnBox'; import { ONet } from './ONet'; diff --git a/src/mtcnn/types.ts b/src/mtcnn/types.ts index eb8175bf..c3e37a97 100644 --- a/src/mtcnn/types.ts +++ b/src/mtcnn/types.ts @@ -1,38 +1,38 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; import { FaceLandmarks5 } from '../classes/FaceLandmarks5'; +import { ConvParams, FCParams } from '../common'; import { WithFaceDetection, WithFaceLandmarks } from '../factories'; export type SharedParams = { - conv1: TfjsImageRecognitionBase.ConvParams + conv1: ConvParams prelu1_alpha: tf.Tensor1D - conv2: TfjsImageRecognitionBase.ConvParams + conv2: ConvParams prelu2_alpha: tf.Tensor1D - conv3: TfjsImageRecognitionBase.ConvParams + conv3: ConvParams prelu3_alpha: tf.Tensor1D } export type PNetParams = SharedParams & { - conv4_1: TfjsImageRecognitionBase.ConvParams - conv4_2: TfjsImageRecognitionBase.ConvParams + conv4_1: ConvParams + conv4_2: ConvParams } export type RNetParams = SharedParams & { - fc1: TfjsImageRecognitionBase.FCParams + fc1: FCParams prelu4_alpha: tf.Tensor1D - fc2_1: TfjsImageRecognitionBase.FCParams - fc2_2: TfjsImageRecognitionBase.FCParams + fc2_1: FCParams + fc2_2: FCParams } export type ONetParams = SharedParams & { - conv4: TfjsImageRecognitionBase.ConvParams + conv4: ConvParams prelu4_alpha: tf.Tensor1D - fc1: TfjsImageRecognitionBase.FCParams + fc1: FCParams prelu5_alpha: tf.Tensor1D - fc2_1: TfjsImageRecognitionBase.FCParams - fc2_2: TfjsImageRecognitionBase.FCParams - fc2_3: TfjsImageRecognitionBase.FCParams + fc2_1: FCParams + fc2_2: FCParams + fc2_3: FCParams } export type NetParams = { diff --git a/src/ops/index.ts b/src/ops/index.ts new file mode 100644 index 00000000..314dc038 --- /dev/null +++ b/src/ops/index.ts @@ -0,0 +1,14 @@ +export * from './iou' +export * from './minBbox' +export * from './nonMaxSuppression' +export * from './normalize' +export * from './padToSquare' +export * from './shuffleArray' + +export function sigmoid(x: number) { + return 1 / (1 + Math.exp(-x)) +} + +export function inverseSigmoid(x: number) { + return Math.log(x / (1 - x)) +} \ No newline at end of file diff --git a/src/ops/iou.ts b/src/ops/iou.ts new file mode 100644 index 00000000..577e11ec --- /dev/null +++ b/src/ops/iou.ts @@ -0,0 +1,11 @@ +import { Box } from '../classes/Box'; + +export function iou(box1: Box, box2: Box, isIOU: boolean = true) { + const width = Math.max(0.0, Math.min(box1.right, box2.right) - Math.max(box1.left, box2.left)) + const height = Math.max(0.0, Math.min(box1.bottom, box2.bottom) - Math.max(box1.top, box2.top)) + const interSection = width * height + + return isIOU + ? interSection / (box1.area + box2.area - interSection) + : interSection / Math.min(box1.area, box2.area) +} \ No newline at end of file diff --git a/src/minBbox.ts b/src/ops/minBbox.ts similarity index 86% rename from src/minBbox.ts rename to src/ops/minBbox.ts index 469f69d1..48f9163f 100644 --- a/src/minBbox.ts +++ b/src/ops/minBbox.ts @@ -1,4 +1,4 @@ -import { BoundingBox, IPoint } from 'tfjs-image-recognition-base'; +import { BoundingBox, IPoint } from '../classes'; export function minBbox(pts: IPoint[]): BoundingBox { const xs = pts.map(pt => pt.x) diff --git a/src/ops/nonMaxSuppression.ts b/src/ops/nonMaxSuppression.ts new file mode 100644 index 00000000..500e5181 --- /dev/null +++ b/src/ops/nonMaxSuppression.ts @@ -0,0 +1,41 @@ +import { Box } from '../classes/Box'; +import { iou } from './iou'; + +export function nonMaxSuppression( + boxes: Box[], + scores: number[], + iouThreshold: number, + isIOU: boolean = true +): number[] { + + let indicesSortedByScore = scores + .map((score, boxIndex) => ({ score, boxIndex })) + .sort((c1, c2) => c1.score - c2.score) + .map(c => c.boxIndex) + + const pick: number[] = [] + + while(indicesSortedByScore.length > 0) { + const curr = indicesSortedByScore.pop() as number + pick.push(curr) + + const indices = indicesSortedByScore + + const outputs: number[] = [] + for (let i = 0; i < indices.length; i++) { + const idx = indices[i] + + const currBox = boxes[curr] + const idxBox = boxes[idx] + + outputs.push(iou(currBox, idxBox, isIOU)) + } + + indicesSortedByScore = indicesSortedByScore.filter( + (_, j) => outputs[j] <= iouThreshold + ) + } + + return pick + +} \ No newline at end of file diff --git a/src/ops/normalize.ts b/src/ops/normalize.ts new file mode 100644 index 00000000..5360143d --- /dev/null +++ b/src/ops/normalize.ts @@ -0,0 +1,13 @@ +import * as tf from '@tensorflow/tfjs-core'; + +export function normalize(x: tf.Tensor4D, meanRgb: number[]): tf.Tensor4D { + return tf.tidy(() => { + const [r, g, b] = meanRgb + const avg_r = tf.fill([...x.shape.slice(0, 3), 1], r) + const avg_g = tf.fill([...x.shape.slice(0, 3), 1], g) + const avg_b = tf.fill([...x.shape.slice(0, 3), 1], b) + const avg_rgb = tf.concat([avg_r, avg_g, avg_b], 3) + + return tf.sub(x, avg_rgb) + }) +} \ No newline at end of file diff --git a/src/ops/padToSquare.ts b/src/ops/padToSquare.ts new file mode 100644 index 00000000..bf7222a9 --- /dev/null +++ b/src/ops/padToSquare.ts @@ -0,0 +1,48 @@ +import * as tf from '@tensorflow/tfjs-core'; + +/** + * Pads the smaller dimension of an image tensor with zeros, such that width === height. + * + * @param imgTensor The image tensor. + * @param isCenterImage (optional, default: false) If true, add an equal amount of padding on + * both sides of the minor dimension oof the image. + * @returns The padded tensor with width === height. + */ +export function padToSquare( + imgTensor: tf.Tensor4D, + isCenterImage: boolean = false +): tf.Tensor4D { + return tf.tidy(() => { + + const [height, width] = imgTensor.shape.slice(1) + if (height === width) { + return imgTensor + } + + const dimDiff = Math.abs(height - width) + const paddingAmount = Math.round(dimDiff * (isCenterImage ? 0.5 : 1)) + const paddingAxis = height > width ? 2 : 1 + + const createPaddingTensor = (paddingAmount: number): tf.Tensor => { + const paddingTensorShape = imgTensor.shape.slice() + paddingTensorShape[paddingAxis] = paddingAmount + return tf.fill(paddingTensorShape, 0) + } + + const paddingTensorAppend = createPaddingTensor(paddingAmount) + const remainingPaddingAmount = dimDiff - (paddingTensorAppend.shape[paddingAxis] as number) + + const paddingTensorPrepend = isCenterImage && remainingPaddingAmount + ? createPaddingTensor(remainingPaddingAmount) + : null + + const tensorsToStack = [ + paddingTensorPrepend, + imgTensor, + paddingTensorAppend + ] + .filter(t => !!t) + .map((t: tf.Tensor) => t.toFloat()) as tf.Tensor4D[] + return tf.concat(tensorsToStack, paddingAxis) + }) +} \ No newline at end of file diff --git a/src/ops/shuffleArray.ts b/src/ops/shuffleArray.ts new file mode 100644 index 00000000..76fd13c5 --- /dev/null +++ b/src/ops/shuffleArray.ts @@ -0,0 +1,10 @@ +export function shuffleArray(inputArray: any[]) { + const array = inputArray.slice() + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)) + const x = array[i] + array[i] = array[j] + array[j] = x + } + return array +} \ No newline at end of file diff --git a/src/resizeResults.ts b/src/resizeResults.ts index fd7955ff..4a79b311 100644 --- a/src/resizeResults.ts +++ b/src/resizeResults.ts @@ -1,5 +1,4 @@ -import { Dimensions, IDimensions } from 'tfjs-image-recognition-base'; - +import { Dimensions, IDimensions } from './classes'; import { FaceDetection } from './classes/FaceDetection'; import { FaceLandmarks } from './classes/FaceLandmarks'; import { extendWithFaceDetection, isWithFaceDetection } from './factories/WithFaceDetection'; diff --git a/src/ssdMobilenetv1/SsdMobilenetv1.ts b/src/ssdMobilenetv1/SsdMobilenetv1.ts index c6000567..64e7f565 100644 --- a/src/ssdMobilenetv1/SsdMobilenetv1.ts +++ b/src/ssdMobilenetv1/SsdMobilenetv1.ts @@ -1,7 +1,9 @@ import * as tf from '@tensorflow/tfjs-core'; -import { NetInput, NeuralNetwork, Rect, TNetInput, toNetInput } from 'tfjs-image-recognition-base'; +import { Rect } from '../classes'; import { FaceDetection } from '../classes/FaceDetection'; +import { NetInput, TNetInput, toNetInput } from '../dom'; +import { NeuralNetwork } from '../NeuralNetwork'; import { extractParams } from './extractParams'; import { extractParamsFromWeigthMap } from './extractParamsFromWeigthMap'; import { mobileNetV1 } from './mobileNetV1'; diff --git a/src/ssdMobilenetv1/boxPredictionLayer.ts b/src/ssdMobilenetv1/boxPredictionLayer.ts index 983a14b8..ff306492 100644 --- a/src/ssdMobilenetv1/boxPredictionLayer.ts +++ b/src/ssdMobilenetv1/boxPredictionLayer.ts @@ -1,6 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { convLayer } from '../common'; import { BoxPredictionParams } from './types'; @@ -13,11 +13,11 @@ export function boxPredictionLayer( const batchSize = x.shape[0] const boxPredictionEncoding = tf.reshape( - TfjsImageRecognitionBase.convLayer(x, params.box_encoding_predictor), + convLayer(x, params.box_encoding_predictor), [batchSize, -1, 1, 4] ) const classPrediction = tf.reshape( - TfjsImageRecognitionBase.convLayer(x, params.class_predictor), + convLayer(x, params.class_predictor), [batchSize, -1, 3] ) diff --git a/src/ssdMobilenetv1/extractParams.ts b/src/ssdMobilenetv1/extractParams.ts index 9a539ecc..d30c731e 100644 --- a/src/ssdMobilenetv1/extractParams.ts +++ b/src/ssdMobilenetv1/extractParams.ts @@ -1,9 +1,9 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { ExtractWeightsFunction, ParamMapping, ConvParams, extractWeightsFactory } from '../common'; import { MobileNetV1, NetParams, PointwiseConvParams, PredictionLayerParams } from './types'; -function extractorsFactory(extractWeights: TfjsImageRecognitionBase.ExtractWeightsFunction, paramMappings: TfjsImageRecognitionBase.ParamMapping[]) { +function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings: ParamMapping[]) { function extractDepthwiseConvParams(numChannels: number, mappedPrefix: string): MobileNetV1.DepthwiseConvParams { @@ -36,7 +36,7 @@ function extractorsFactory(extractWeights: TfjsImageRecognitionBase.ExtractWeigh filterSize: number, mappedPrefix: string, isPointwiseConv?: boolean - ): TfjsImageRecognitionBase.ConvParams { + ): ConvParams { const filters = tf.tensor4d( extractWeights(channelsIn * channelsOut * filterSize * filterSize), @@ -191,14 +191,14 @@ function extractorsFactory(extractWeights: TfjsImageRecognitionBase.ExtractWeigh } -export function extractParams(weights: Float32Array): { params: NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +export function extractParams(weights: Float32Array): { params: NetParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractWeights, getRemainingWeights - } = TfjsImageRecognitionBase.extractWeightsFactory(weights) + } = extractWeightsFactory(weights) const { extractMobilenetV1Params, diff --git a/src/ssdMobilenetv1/extractParamsFromWeigthMap.ts b/src/ssdMobilenetv1/extractParamsFromWeigthMap.ts index 2d6089ac..daa3f4bf 100644 --- a/src/ssdMobilenetv1/extractParamsFromWeigthMap.ts +++ b/src/ssdMobilenetv1/extractParamsFromWeigthMap.ts @@ -1,11 +1,12 @@ import * as tf from '@tensorflow/tfjs-core'; -import { isTensor3D, TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { ConvParams, disposeUnusedWeightTensors, extractWeightEntryFactory, ParamMapping } from '../common'; +import { isTensor3D } from '../utils'; import { BoxPredictionParams, MobileNetV1, NetParams, PointwiseConvParams, PredictionLayerParams } from './types'; -function extractorsFactory(weightMap: any, paramMappings: TfjsImageRecognitionBase.ParamMapping[]) { +function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) { - const extractWeightEntry = TfjsImageRecognitionBase.extractWeightEntryFactory(weightMap, paramMappings) + const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings) function extractPointwiseConvParams(prefix: string, idx: number, mappedPrefix: string): PointwiseConvParams { @@ -59,7 +60,7 @@ function extractorsFactory(weightMap: any, paramMappings: TfjsImageRecognitionBa } } - function extractConvParams(prefix: string, mappedPrefix: string): TfjsImageRecognitionBase.ConvParams { + function extractConvParams(prefix: string, mappedPrefix: string): ConvParams { const filters = extractWeightEntry(`${prefix}/weights`, 4, `${mappedPrefix}/filters`) const bias = extractWeightEntry(`${prefix}/biases`, 1, `${mappedPrefix}/bias`) @@ -107,9 +108,9 @@ function extractorsFactory(weightMap: any, paramMappings: TfjsImageRecognitionBa export function extractParamsFromWeigthMap( weightMap: tf.NamedTensorMap -): { params: NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +): { params: NetParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractMobilenetV1Params, @@ -131,7 +132,7 @@ export function extractParamsFromWeigthMap( } } - TfjsImageRecognitionBase.disposeUnusedWeightTensors(weightMap, paramMappings) + disposeUnusedWeightTensors(weightMap, paramMappings) return { params, paramMappings } } \ No newline at end of file diff --git a/src/ssdMobilenetv1/types.ts b/src/ssdMobilenetv1/types.ts index a030e0bb..c5c0afe9 100644 --- a/src/ssdMobilenetv1/types.ts +++ b/src/ssdMobilenetv1/types.ts @@ -1,5 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; + +import { ConvParams } from '../common'; export type PointwiseConvParams = { filters: tf.Tensor4D @@ -41,8 +42,8 @@ export namespace MobileNetV1 { } export type BoxPredictionParams = { - box_encoding_predictor: TfjsImageRecognitionBase.ConvParams - class_predictor: TfjsImageRecognitionBase.ConvParams + box_encoding_predictor: ConvParams + class_predictor: ConvParams } export type PredictionLayerParams = { diff --git a/src/tinyFaceDetector/TinyFaceDetector.ts b/src/tinyFaceDetector/TinyFaceDetector.ts index a4e822bb..09cb6a13 100644 --- a/src/tinyFaceDetector/TinyFaceDetector.ts +++ b/src/tinyFaceDetector/TinyFaceDetector.ts @@ -1,10 +1,14 @@ import * as tf from '@tensorflow/tfjs-core'; -import { Point, TfjsImageRecognitionBase, TNetInput } from 'tfjs-image-recognition-base'; -import { FaceDetection } from '../classes'; +import { FaceDetection, Point } from '../classes'; +import { ParamMapping } from '../common'; +import { TNetInput } from '../dom'; +import { ITinyYolov2Options } from '../tinyYolov2'; +import { TinyYolov2Base } from '../tinyYolov2/TinyYolov2Base'; +import { TinyYolov2NetParams } from '../tinyYolov2/types'; import { BOX_ANCHORS, IOU_THRESHOLD, MEAN_RGB } from './const'; -export class TinyFaceDetector extends TfjsImageRecognitionBase.TinyYolov2 { +export class TinyFaceDetector extends TinyYolov2Base { constructor() { const config = { @@ -24,7 +28,7 @@ export class TinyFaceDetector extends TfjsImageRecognitionBase.TinyYolov2 { return this.config.anchors } - public async locateFaces(input: TNetInput, forwardParams: TfjsImageRecognitionBase.ITinyYolov2Options): Promise { + public async locateFaces(input: TNetInput, forwardParams: ITinyYolov2Options): Promise { const objectDetections = await this.detect(input, forwardParams) return objectDetections.map(det => new FaceDetection(det.score, det.relativeBox, { width: det.imageWidth, height: det.imageHeight })) } @@ -33,7 +37,7 @@ export class TinyFaceDetector extends TfjsImageRecognitionBase.TinyYolov2 { return 'tiny_face_detector_model' } - protected extractParamsFromWeigthMap(weightMap: tf.NamedTensorMap): { params: TfjsImageRecognitionBase.TinyYolov2NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { + protected extractParamsFromWeigthMap(weightMap: tf.NamedTensorMap): { params: TinyYolov2NetParams, paramMappings: ParamMapping[] } { return super.extractParamsFromWeigthMap(weightMap) } } \ No newline at end of file diff --git a/src/tinyFaceDetector/TinyFaceDetectorOptions.ts b/src/tinyFaceDetector/TinyFaceDetectorOptions.ts index c76c897b..fdb68a26 100644 --- a/src/tinyFaceDetector/TinyFaceDetectorOptions.ts +++ b/src/tinyFaceDetector/TinyFaceDetectorOptions.ts @@ -1,7 +1,7 @@ -import { TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; +import { ITinyYolov2Options, TinyYolov2Options } from '../tinyYolov2'; -export interface ITinyFaceDetectorOptions extends TfjsImageRecognitionBase.ITinyYolov2Options {} +export interface ITinyFaceDetectorOptions extends ITinyYolov2Options {} -export class TinyFaceDetectorOptions extends TfjsImageRecognitionBase.TinyYolov2Options { +export class TinyFaceDetectorOptions extends TinyYolov2Options { protected _name: string = 'TinyFaceDetectorOptions' } \ No newline at end of file diff --git a/src/tinyFaceDetector/const.ts b/src/tinyFaceDetector/const.ts index c0b92c72..b97e7b3c 100644 --- a/src/tinyFaceDetector/const.ts +++ b/src/tinyFaceDetector/const.ts @@ -1,4 +1,4 @@ -import { Point } from 'tfjs-image-recognition-base'; +import { Point } from '../classes' export const IOU_THRESHOLD = 0.4 diff --git a/src/tinyYolov2/TinyYolov2.ts b/src/tinyYolov2/TinyYolov2.ts index d001dba9..725c55ba 100644 --- a/src/tinyYolov2/TinyYolov2.ts +++ b/src/tinyYolov2/TinyYolov2.ts @@ -1,7 +1,8 @@ import * as tf from '@tensorflow/tfjs-core'; -import { Point, TfjsImageRecognitionBase, TNetInput } from 'tfjs-image-recognition-base'; -import { FaceDetection } from '../classes'; +import { FaceDetection, Point } from '../classes'; +import { ParamMapping } from '../common/types'; +import { TNetInput } from '../dom/types'; import { BOX_ANCHORS, BOX_ANCHORS_SEPARABLE, @@ -10,8 +11,11 @@ import { IOU_THRESHOLD, MEAN_RGB_SEPARABLE, } from './const'; +import { TinyYolov2Base } from './TinyYolov2Base'; +import { ITinyYolov2Options } from './TinyYolov2Options'; +import { TinyYolov2NetParams } from './types'; -export class TinyYolov2 extends TfjsImageRecognitionBase.TinyYolov2 { +export class TinyYolov2 extends TinyYolov2Base { constructor(withSeparableConvs: boolean = true) { const config = Object.assign({}, { @@ -41,7 +45,7 @@ export class TinyYolov2 extends TfjsImageRecognitionBase.TinyYolov2 { return this.config.anchors } - public async locateFaces(input: TNetInput, forwardParams: TfjsImageRecognitionBase.ITinyYolov2Options): Promise { + public async locateFaces(input: TNetInput, forwardParams: ITinyYolov2Options): Promise { const objectDetections = await this.detect(input, forwardParams) return objectDetections.map(det => new FaceDetection(det.score, det.relativeBox, { width: det.imageWidth, height: det.imageHeight })) } @@ -50,7 +54,7 @@ export class TinyYolov2 extends TfjsImageRecognitionBase.TinyYolov2 { return this.withSeparableConvs ? DEFAULT_MODEL_NAME_SEPARABLE_CONV : DEFAULT_MODEL_NAME } - protected extractParamsFromWeigthMap(weightMap: tf.NamedTensorMap): { params: TfjsImageRecognitionBase.TinyYolov2NetParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { + protected extractParamsFromWeigthMap(weightMap: tf.NamedTensorMap): { params: TinyYolov2NetParams, paramMappings: ParamMapping[] } { return super.extractParamsFromWeigthMap(weightMap) } } \ No newline at end of file diff --git a/src/tinyYolov2/TinyYolov2Base.ts b/src/tinyYolov2/TinyYolov2Base.ts new file mode 100644 index 00000000..1db452c4 --- /dev/null +++ b/src/tinyYolov2/TinyYolov2Base.ts @@ -0,0 +1,256 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { BoundingBox } from '../classes/BoundingBox'; +import { Dimensions } from '../classes/Dimensions'; +import { ObjectDetection } from '../classes/ObjectDetection'; +import { convLayer } from '../common'; +import { ConvParams, SeparableConvParams } from '../common/types'; +import { toNetInput } from '../dom'; +import { NetInput } from '../dom/NetInput'; +import { TNetInput } from '../dom/types'; +import { NeuralNetwork } from '../NeuralNetwork'; +import { sigmoid } from '../ops'; +import { nonMaxSuppression } from '../ops/nonMaxSuppression'; +import { normalize } from '../ops/normalize'; +import { TinyYolov2Config, validateConfig } from './config'; +import { convWithBatchNorm } from './convWithBatchNorm'; +import { depthwiseSeparableConv } from './depthwiseSeparableConv'; +import { extractParams } from './extractParams'; +import { extractParamsFromWeigthMap } from './extractParamsFromWeigthMap'; +import { leaky } from './leaky'; +import { ITinyYolov2Options, TinyYolov2Options } from './TinyYolov2Options'; +import { DefaultTinyYolov2NetParams, MobilenetParams, TinyYolov2NetParams } from './types'; + +export class TinyYolov2Base extends NeuralNetwork { + + public static DEFAULT_FILTER_SIZES = [ + 3, 16, 32, 64, 128, 256, 512, 1024, 1024 + ] + + private _config: TinyYolov2Config + + constructor(config: TinyYolov2Config) { + super('TinyYolov2') + validateConfig(config) + this._config = config + } + + public get config(): TinyYolov2Config { + return this._config + } + + public get withClassScores(): boolean { + return this.config.withClassScores || this.config.classes.length > 1 + } + + public get boxEncodingSize(): number { + return 5 + (this.withClassScores ? this.config.classes.length : 0) + } + + public runTinyYolov2(x: tf.Tensor4D, params: DefaultTinyYolov2NetParams): tf.Tensor4D { + + let out = convWithBatchNorm(x, params.conv0) + out = tf.maxPool(out, [2, 2], [2, 2], 'same') + out = convWithBatchNorm(out, params.conv1) + out = tf.maxPool(out, [2, 2], [2, 2], 'same') + out = convWithBatchNorm(out, params.conv2) + out = tf.maxPool(out, [2, 2], [2, 2], 'same') + out = convWithBatchNorm(out, params.conv3) + out = tf.maxPool(out, [2, 2], [2, 2], 'same') + out = convWithBatchNorm(out, params.conv4) + out = tf.maxPool(out, [2, 2], [2, 2], 'same') + out = convWithBatchNorm(out, params.conv5) + 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) + } + + public runMobilenet(x: tf.Tensor4D, params: MobilenetParams): tf.Tensor4D { + + let out = this.config.isFirstLayerConv2d + ? leaky(convLayer(x, params.conv0 as ConvParams, 'valid', false)) + : depthwiseSeparableConv(x, params.conv0 as SeparableConvParams) + out = tf.maxPool(out, [2, 2], [2, 2], 'same') + out = depthwiseSeparableConv(out, params.conv1) + out = tf.maxPool(out, [2, 2], [2, 2], 'same') + out = depthwiseSeparableConv(out, params.conv2) + out = tf.maxPool(out, [2, 2], [2, 2], 'same') + out = depthwiseSeparableConv(out, params.conv3) + out = tf.maxPool(out, [2, 2], [2, 2], 'same') + out = depthwiseSeparableConv(out, params.conv4) + out = tf.maxPool(out, [2, 2], [2, 2], 'same') + out = depthwiseSeparableConv(out, params.conv5) + 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) + } + + public forwardInput(input: NetInput, inputSize: number): tf.Tensor4D { + + const { params } = this + + if (!params) { + throw new Error('TinyYolov2 - load model before inference') + } + + return tf.tidy(() => { + + let batchTensor = input.toBatchTensor(inputSize, false).toFloat() + 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) + }) + } + + public async forward(input: TNetInput, inputSize: number): Promise { + return await this.forwardInput(await toNetInput(input), inputSize) + } + + public async detect(input: TNetInput, forwardParams: ITinyYolov2Options = {}): Promise { + + 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) + } + + const results = await this.extractBoxes(out0, netInput.getReshapedInputDimensions(0), scoreThreshold) + out.dispose() + out0.dispose() + + const boxes = results.map(res => res.box) + const scores = results.map(res => res.score) + const classScores = results.map(res => res.classScore) + const classNames = results.map(res => this.config.classes[res.label]) + + const indices = nonMaxSuppression( + boxes.map(box => box.rescale(inputSize)), + scores, + this.config.iouThreshold, + true + ) + + const detections = indices.map(idx => + new ObjectDetection( + scores[idx], + classScores[idx], + classNames[idx], + boxes[idx], + inputDimensions + ) + ) + + return detections + } + + protected getDefaultModelName(): string { + return '' + } + + protected extractParamsFromWeigthMap(weightMap: tf.NamedTensorMap) { + return extractParamsFromWeigthMap(weightMap, this.config) + } + + protected extractParams(weights: Float32Array) { + const filterSizes = this.config.filterSizes || TinyYolov2Base.DEFAULT_FILTER_SIZES + + const numFilters = filterSizes ? filterSizes.length : undefined + if (numFilters !== 7 && numFilters !== 8 && numFilters !== 9) { + throw new Error(`TinyYolov2 - expected 7 | 8 | 9 convolutional filters, but found ${numFilters} filterSizes in config`) + } + return extractParams(weights, this.config, this.boxEncodingSize, filterSizes) + } + + protected async extractBoxes( + outputTensor: tf.Tensor4D, + inputBlobDimensions: Dimensions, + scoreThreshold?: number + ) { + + const { width, height } = inputBlobDimensions + const inputSize = Math.max(width, height) + const correctionFactorX = inputSize / width + const correctionFactorY = inputSize / height + + const numCells = outputTensor.shape[1] + const numBoxes = this.config.anchors.length + + const [boxesTensor, scoresTensor, classScoresTensor] = tf.tidy(() => { + const reshaped = outputTensor.reshape([numCells, numCells, numBoxes, this.boxEncodingSize]) + + const boxes = reshaped.slice([0, 0, 0, 0], [numCells, numCells, numBoxes, 4]) + const scores = reshaped.slice([0, 0, 0, 4], [numCells, numCells, numBoxes, 1]) + const classScores = this.withClassScores + ? tf.softmax(reshaped.slice([0, 0, 0, 5], [numCells, numCells, numBoxes, this.config.classes.length]), 3) + : tf.scalar(0) + return [boxes, scores, classScores] + }) + + const results = [] + + const scoresData = await scoresTensor.array() + const boxesData = await boxesTensor.array() + for (let row = 0; row < numCells; row ++) { + for (let col = 0; col < numCells; col ++) { + for (let anchor = 0; anchor < numBoxes; anchor ++) { + + const score = sigmoid(scoresData[row][col][anchor][0]); + if (!scoreThreshold || score > scoreThreshold) { + const ctX = ((col + sigmoid(boxesData[row][col][anchor][0])) / numCells) * correctionFactorX + const ctY = ((row + sigmoid(boxesData[row][col][anchor][1])) / numCells) * correctionFactorY + const width = ((Math.exp(boxesData[row][col][anchor][2]) * this.config.anchors[anchor].x) / numCells) * correctionFactorX + const height = ((Math.exp(boxesData[row][col][anchor][3]) * this.config.anchors[anchor].y) / numCells) * correctionFactorY + + const x = (ctX - (width / 2)) + const y = (ctY - (height / 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 + width, y + height), + score: score, + classScore: score * classScore, + label, + ...pos + }) + } + } + } + } + + boxesTensor.dispose() + scoresTensor.dispose() + classScoresTensor.dispose() + + return results + } + + private async extractPredictedClass(classesTensor: tf.Tensor4D, pos: { row: number, col: number, anchor: number },) { + const { row, col, anchor } = pos + const classesData = await classesTensor.array() + return Array(this.config.classes.length).fill(0) + .map((_, i) => classesData[row][col][anchor][i]) + .map((classScore, label) => ({ + classScore, + label + })) + .reduce((max, curr) => max.classScore > curr.classScore ? max : curr) + } +} \ No newline at end of file diff --git a/src/tinyYolov2/TinyYolov2Options.ts b/src/tinyYolov2/TinyYolov2Options.ts new file mode 100644 index 00000000..6b425e36 --- /dev/null +++ b/src/tinyYolov2/TinyYolov2Options.ts @@ -0,0 +1,34 @@ +export enum TinyYolov2SizeType { + XS = 224, + SM = 320, + MD = 416, + LG = 608 +} + +export interface ITinyYolov2Options { + inputSize?: number + scoreThreshold?: number +} + +export class TinyYolov2Options { + protected _name: string = 'TinyYolov2Options' + + private _inputSize: number + private _scoreThreshold: number + + constructor({ inputSize, scoreThreshold }: ITinyYolov2Options = {}) { + this._inputSize = inputSize || 416 + this._scoreThreshold = scoreThreshold || 0.5 + + if (typeof this._inputSize !== 'number' || this._inputSize % 32 !== 0) { + throw new Error(`${this._name} - expected inputSize to be a number divisible by 32`) + } + + if (typeof this._scoreThreshold !== 'number' || this._scoreThreshold <= 0 || this._scoreThreshold >= 1) { + throw new Error(`${this._name} - expected scoreThreshold to be a number between 0 and 1`) + } + } + + get inputSize(): number { return this._inputSize } + get scoreThreshold(): number { return this._scoreThreshold } +} \ No newline at end of file diff --git a/src/tinyYolov2/config.ts b/src/tinyYolov2/config.ts new file mode 100644 index 00000000..3d7c0a2b --- /dev/null +++ b/src/tinyYolov2/config.ts @@ -0,0 +1,55 @@ +import { Point } from '../classes/Point'; + +export type TinyYolov2Config = { + withSeparableConvs: boolean + iouThreshold: number + anchors: Point[] + classes: string[] + meanRgb?: [number, number, number] + withClassScores?: boolean, + filterSizes?: number[] + isFirstLayerConv2d?: boolean +} + +const isNumber = (arg: any) => typeof arg === 'number' + +export function validateConfig(config: any) { + if (!config) { + throw new Error(`invalid config: ${config}`) + } + + if (typeof config.withSeparableConvs !== 'boolean') { + throw new Error(`config.withSeparableConvs has to be a boolean, have: ${config.withSeparableConvs}`) + } + + if (!isNumber(config.iouThreshold) || config.iouThreshold < 0 || config.iouThreshold > 1.0) { + throw new Error(`config.iouThreshold has to be a number between [0, 1], have: ${config.iouThreshold}`) + } + + if ( + !Array.isArray(config.classes) + || !config.classes.length + || !config.classes.every((c: any) => typeof c === 'string') + ) { + + throw new Error(`config.classes has to be an array class names: string[], have: ${JSON.stringify(config.classes)}`) + } + + if ( + !Array.isArray(config.anchors) + || !config.anchors.length + || !config.anchors.map((a: any) => a || {}).every((a: any) => isNumber(a.x) && isNumber(a.y)) + ) { + + throw new Error(`config.anchors has to be an array of { x: number, y: number }, have: ${JSON.stringify(config.anchors)}`) + } + + if (config.meanRgb && ( + !Array.isArray(config.meanRgb) + || config.meanRgb.length !== 3 + || !config.meanRgb.every(isNumber) + )) { + + throw new Error(`config.meanRgb has to be an array of shape [number, number, number], have: ${JSON.stringify(config.meanRgb)}`) + } +} \ No newline at end of file diff --git a/src/tinyYolov2/const.ts b/src/tinyYolov2/const.ts index 7ffd2309..7affc2a2 100644 --- a/src/tinyYolov2/const.ts +++ b/src/tinyYolov2/const.ts @@ -1,4 +1,4 @@ -import { Point } from 'tfjs-image-recognition-base'; +import { Point } from '../classes'; export const IOU_THRESHOLD = 0.4 diff --git a/src/tinyYolov2/convWithBatchNorm.ts b/src/tinyYolov2/convWithBatchNorm.ts new file mode 100644 index 00000000..3e3d6f8e --- /dev/null +++ b/src/tinyYolov2/convWithBatchNorm.ts @@ -0,0 +1,17 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { leaky } from './leaky'; +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) + }) +} \ No newline at end of file diff --git a/src/tinyYolov2/depthwiseSeparableConv.ts b/src/tinyYolov2/depthwiseSeparableConv.ts new file mode 100644 index 00000000..a39a32a9 --- /dev/null +++ b/src/tinyYolov2/depthwiseSeparableConv.ts @@ -0,0 +1,15 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { SeparableConvParams } from '../common/types'; +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) + }) +} \ No newline at end of file diff --git a/src/tinyYolov2/extractParams.ts b/src/tinyYolov2/extractParams.ts new file mode 100644 index 00000000..cf31da1f --- /dev/null +++ b/src/tinyYolov2/extractParams.ts @@ -0,0 +1,101 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { extractConvParamsFactory } from '../common'; +import { extractSeparableConvParamsFactory } from '../common/extractSeparableConvParamsFactory'; +import { extractWeightsFactory } from '../common/extractWeightsFactory'; +import { ExtractWeightsFunction, ParamMapping } from '../common/types'; +import { TinyYolov2Config } from './config'; +import { BatchNorm, ConvWithBatchNorm, TinyYolov2NetParams } from './types'; + +function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings: ParamMapping[]) { + + const extractConvParams = extractConvParamsFactory(extractWeights, paramMappings) + + function extractBatchNormParams(size: number, mappedPrefix: string): BatchNorm { + + const sub = tf.tensor1d(extractWeights(size)) + const truediv = tf.tensor1d(extractWeights(size)) + + paramMappings.push( + { 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) + + return { + extractConvParams, + extractConvWithBatchNormParams, + extractSeparableConvParams + } + +} + +export function extractParams( + weights: Float32Array, + config: TinyYolov2Config, + boxEncodingSize: number, + filterSizes: number[] +): { params: TinyYolov2NetParams, paramMappings: ParamMapping[] } { + + const { + extractWeights, + getRemainingWeights + } = 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') + const conv1 = extractSeparableConvParams(s1, s2, 'conv1') + const conv2 = extractSeparableConvParams(s2, s3, 'conv2') + const conv3 = extractSeparableConvParams(s3, s4, 'conv3') + const conv4 = extractSeparableConvParams(s4, s5, 'conv4') + const conv5 = extractSeparableConvParams(s5, s6, 'conv5') + const conv6 = s7 ? extractSeparableConvParams(s6, s7, 'conv6') : undefined + const conv7 = s8 ? extractSeparableConvParams(s7, s8, 'conv7') : undefined + const conv8 = extractConvParams(s8 || s7 || s6, 5 * boxEncodingSize, 1, 'conv8') + params = { conv0, conv1, conv2, conv3, conv4, conv5, conv6, conv7, conv8 } + } else { + const [s0, s1, s2, s3, s4, s5, s6, s7, s8] = filterSizes + const conv0 = extractConvWithBatchNormParams(s0, s1, 'conv0',) + const conv1 = extractConvWithBatchNormParams(s1, s2, 'conv1') + const conv2 = extractConvWithBatchNormParams(s2, s3, 'conv2') + const conv3 = extractConvWithBatchNormParams(s3, s4, 'conv3') + const conv4 = extractConvWithBatchNormParams(s4, s5, 'conv4') + const conv5 = extractConvWithBatchNormParams(s5, s6, 'conv5') + const conv6 = extractConvWithBatchNormParams(s6, s7, 'conv6') + const conv7 = extractConvWithBatchNormParams(s7, s8, 'conv7') + const conv8 = extractConvParams(s8, 5 * boxEncodingSize, 1, 'conv8') + params = { 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 } +} \ No newline at end of file diff --git a/src/tinyYolov2/extractParamsFromWeigthMap.ts b/src/tinyYolov2/extractParamsFromWeigthMap.ts new file mode 100644 index 00000000..a18458ef --- /dev/null +++ b/src/tinyYolov2/extractParamsFromWeigthMap.ts @@ -0,0 +1,88 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { ConvParams } from '../common'; +import { disposeUnusedWeightTensors } from '../common/disposeUnusedWeightTensors'; +import { loadSeparableConvParamsFactory } from '../common/extractSeparableConvParamsFactory'; +import { extractWeightEntryFactory } from '../common/extractWeightEntryFactory'; +import { ParamMapping } from '../common/types'; +import { TinyYolov2Config } from './config'; +import { BatchNorm, ConvWithBatchNorm, TinyYolov2NetParams } from './types'; + +function extractorsFactory(weightMap: any, paramMappings: ParamMapping[]) { + + const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings) + + function extractBatchNormParams(prefix: string): BatchNorm { + const sub = extractWeightEntry(`${prefix}/sub`, 1) + const truediv = extractWeightEntry(`${prefix}/truediv`, 1) + return { sub, truediv } + } + + function extractConvParams(prefix: string): ConvParams { + const filters = extractWeightEntry(`${prefix}/filters`, 4) + const bias = extractWeightEntry(`${prefix}/bias`, 1) + return { filters, bias } + } + + function extractConvWithBatchNormParams(prefix: string): ConvWithBatchNorm { + const conv = extractConvParams(`${prefix}/conv`) + const bn = extractBatchNormParams(`${prefix}/bn`) + return { conv, bn } + } + + const extractSeparableConvParams = loadSeparableConvParamsFactory(extractWeightEntry) + + return { + extractConvParams, + extractConvWithBatchNormParams, + extractSeparableConvParams + } + +} + +export function extractParamsFromWeigthMap( + weightMap: tf.NamedTensorMap, + config: TinyYolov2Config +): { params: TinyYolov2NetParams, paramMappings: ParamMapping[] } { + + const paramMappings: ParamMapping[] = [] + + const { + extractConvParams, + extractConvWithBatchNormParams, + extractSeparableConvParams + } = extractorsFactory(weightMap, paramMappings) + + let params: TinyYolov2NetParams + + if (config.withSeparableConvs) { + const numFilters = (config.filterSizes && config.filterSizes.length || 9) + params = { + conv0: config.isFirstLayerConv2d ? extractConvParams('conv0') : extractSeparableConvParams('conv0'), + conv1: extractSeparableConvParams('conv1'), + conv2: extractSeparableConvParams('conv2'), + conv3: extractSeparableConvParams('conv3'), + conv4: extractSeparableConvParams('conv4'), + conv5: extractSeparableConvParams('conv5'), + conv6: numFilters > 7 ? extractSeparableConvParams('conv6') : undefined, + conv7: numFilters > 8 ? extractSeparableConvParams('conv7') : undefined, + conv8: extractConvParams('conv8') + } + } else { + params = { + conv0: extractConvWithBatchNormParams('conv0'), + conv1: extractConvWithBatchNormParams('conv1'), + conv2: extractConvWithBatchNormParams('conv2'), + conv3: extractConvWithBatchNormParams('conv3'), + conv4: extractConvWithBatchNormParams('conv4'), + conv5: extractConvWithBatchNormParams('conv5'), + conv6: extractConvWithBatchNormParams('conv6'), + conv7: extractConvWithBatchNormParams('conv7'), + conv8: extractConvParams('conv8') + } + } + + disposeUnusedWeightTensors(weightMap, paramMappings) + + return { params, paramMappings } +} \ No newline at end of file diff --git a/src/tinyYolov2/index.ts b/src/tinyYolov2/index.ts index 92ddc5aa..b2b3bc42 100644 --- a/src/tinyYolov2/index.ts +++ b/src/tinyYolov2/index.ts @@ -1,5 +1,10 @@ import { TinyYolov2 } from './TinyYolov2'; +export * from './TinyYolov2Options'; + +export * from './config' +export * from './types' + export { TinyYolov2 } export function createTinyYolov2(weights: Float32Array, withSeparableConvs: boolean = true) { diff --git a/src/tinyYolov2/leaky.ts b/src/tinyYolov2/leaky.ts new file mode 100644 index 00000000..6f849604 --- /dev/null +++ b/src/tinyYolov2/leaky.ts @@ -0,0 +1,9 @@ +import * as tf from '@tensorflow/tfjs-core'; + +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) + }) +} \ No newline at end of file diff --git a/src/tinyYolov2/types.ts b/src/tinyYolov2/types.ts new file mode 100644 index 00000000..abf6425c --- /dev/null +++ b/src/tinyYolov2/types.ts @@ -0,0 +1,40 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { ConvParams } from '../common'; +import { SeparableConvParams } from '../common/types'; + +export type BatchNorm = { + sub: tf.Tensor1D + truediv: tf.Tensor1D +} + +export type ConvWithBatchNorm = { + conv: ConvParams + bn: BatchNorm +} + +export type MobilenetParams = { + conv0: SeparableConvParams | ConvParams + conv1: SeparableConvParams + conv2: SeparableConvParams + conv3: SeparableConvParams + conv4: SeparableConvParams + conv5: SeparableConvParams + conv6?: SeparableConvParams + conv7?: SeparableConvParams + conv8: ConvParams +} + +export type DefaultTinyYolov2NetParams = { + conv0: ConvWithBatchNorm + conv1: ConvWithBatchNorm + conv2: ConvWithBatchNorm + conv3: ConvWithBatchNorm + conv4: ConvWithBatchNorm + conv5: ConvWithBatchNorm + conv6: ConvWithBatchNorm + conv7: ConvWithBatchNorm + conv8: ConvParams +} + +export type TinyYolov2NetParams = DefaultTinyYolov2NetParams | MobilenetParams \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 00000000..a49a6030 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,63 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { Point } from '../classes'; +import { Dimensions, IDimensions } from '../classes/Dimensions'; + +export function isTensor(tensor: any, dim: number) { + return tensor instanceof tf.Tensor && tensor.shape.length === dim +} + +export function isTensor1D(tensor: any): tensor is tf.Tensor1D { + return isTensor(tensor, 1) +} + +export function isTensor2D(tensor: any): tensor is tf.Tensor2D { + return isTensor(tensor, 2) +} + +export function isTensor3D(tensor: any): tensor is tf.Tensor3D { + return isTensor(tensor, 3) +} + +export function isTensor4D(tensor: any): tensor is tf.Tensor4D { + return isTensor(tensor, 4) +} + +export function isFloat(num: number) { + return num % 1 !== 0 +} + +export function isEven(num: number) { + return num % 2 === 0 +} + +export function round(num: number, prec: number = 2) { + const f = Math.pow(10, prec) + return Math.floor(num * f) / f +} + +export function isDimensions(obj: any): boolean { + return obj && obj.width && obj.height +} + +export function computeReshapedDimensions({ width, height }: IDimensions, inputSize: number) { + const scale = inputSize / Math.max(height, width) + return new Dimensions(Math.round(width * scale), Math.round(height * scale)) +} + +export function getCenterPoint(pts: Point[]): Point { + return pts.reduce((sum, pt) => sum.add(pt), new Point(0, 0)) + .div(new Point(pts.length, pts.length)) +} + +export function range(num: number, start: number, step: number): number[] { + return Array(num).fill(0).map((_, i) => start + (i * step)) +} + +export function isValidNumber(num: any) { + return !!num && num !== Infinity && num !== -Infinity && !isNaN(num) || num === 0 +} + +export function isValidProbablitiy(num: any) { + return isValidNumber(num) && 0 <= num && num <= 1.0 +} \ No newline at end of file diff --git a/src/xception/TinyXception.ts b/src/xception/TinyXception.ts index 8e979e57..b2fd7173 100644 --- a/src/xception/TinyXception.ts +++ b/src/xception/TinyXception.ts @@ -1,21 +1,15 @@ import * as tf from '@tensorflow/tfjs-core'; -import { - NetInput, - NeuralNetwork, - normalize, - range, - TfjsImageRecognitionBase, - TNetInput, - toNetInput, -} from 'tfjs-image-recognition-base'; - -import { depthwiseSeparableConv } from '../common/depthwiseSeparableConv'; -import { bgrToRgbTensor } from '../mtcnn/bgrToRgbTensor'; + +import { ConvParams, depthwiseSeparableConv } from '../common'; +import { NetInput, TNetInput, toNetInput } from '../dom'; +import { NeuralNetwork } from '../NeuralNetwork'; +import { normalize } from '../ops'; +import { range } from '../utils'; import { extractParams } from './extractParams'; import { extractParamsFromWeigthMap } from './extractParamsFromWeigthMap'; import { MainBlockParams, ReductionBlockParams, TinyXceptionParams } from './types'; -function conv(x: tf.Tensor4D, params: TfjsImageRecognitionBase.ConvParams, stride: [number, number]): tf.Tensor4D { +function conv(x: tf.Tensor4D, params: ConvParams, stride: [number, number]): tf.Tensor4D { return tf.add(tf.conv2d(x, params.filters, stride, 'same'), params.bias) } diff --git a/src/xception/extractParams.ts b/src/xception/extractParams.ts index 0b98b41e..67bfb902 100644 --- a/src/xception/extractParams.ts +++ b/src/xception/extractParams.ts @@ -1,11 +1,12 @@ -import { range, TfjsImageRecognitionBase } from 'tfjs-image-recognition-base'; - +import { extractConvParamsFactory, extractSeparableConvParamsFactory, extractWeightsFactory } from '../common'; +import { ExtractWeightsFunction, ParamMapping } from '../common/types'; +import { range } from '../utils'; import { MainBlockParams, ReductionBlockParams, TinyXceptionParams } from './types'; -function extractorsFactory(extractWeights: TfjsImageRecognitionBase.ExtractWeightsFunction, paramMappings: TfjsImageRecognitionBase.ParamMapping[]) { +function extractorsFactory(extractWeights: ExtractWeightsFunction, paramMappings: ParamMapping[]) { - const extractConvParams = TfjsImageRecognitionBase.extractConvParamsFactory(extractWeights, paramMappings) - const extractSeparableConvParams = TfjsImageRecognitionBase.extractSeparableConvParamsFactory(extractWeights, paramMappings) + const extractConvParams = extractConvParamsFactory(extractWeights, paramMappings) + const extractSeparableConvParams = extractSeparableConvParamsFactory(extractWeights, paramMappings) function extractReductionBlockParams(channelsIn: number, channelsOut: number, mappedPrefix: string): ReductionBlockParams { @@ -34,14 +35,14 @@ function extractorsFactory(extractWeights: TfjsImageRecognitionBase.ExtractWeigh } -export function extractParams(weights: Float32Array, numMainBlocks: number): { params: TinyXceptionParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +export function extractParams(weights: Float32Array, numMainBlocks: number): { params: TinyXceptionParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractWeights, getRemainingWeights - } = TfjsImageRecognitionBase.extractWeightsFactory(weights) + } = extractWeightsFactory(weights) const { extractConvParams, diff --git a/src/xception/extractParamsFromWeigthMap.ts b/src/xception/extractParamsFromWeigthMap.ts index 53800dfd..bd8dc456 100644 --- a/src/xception/extractParamsFromWeigthMap.ts +++ b/src/xception/extractParamsFromWeigthMap.ts @@ -1,15 +1,21 @@ import * as tf from '@tensorflow/tfjs-core'; -import { TfjsImageRecognitionBase, range } from 'tfjs-image-recognition-base'; +import { + disposeUnusedWeightTensors, + extractWeightEntryFactory, + loadSeparableConvParamsFactory, + ParamMapping, +} from '../common'; import { loadConvParamsFactory } from '../common/loadConvParamsFactory'; +import { range } from '../utils'; import { MainBlockParams, ReductionBlockParams, TinyXceptionParams } from './types'; -function loadParamsFactory(weightMap: any, paramMappings: TfjsImageRecognitionBase.ParamMapping[]) { +function loadParamsFactory(weightMap: any, paramMappings: ParamMapping[]) { - const extractWeightEntry = TfjsImageRecognitionBase.extractWeightEntryFactory(weightMap, paramMappings) + const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings) const extractConvParams = loadConvParamsFactory(extractWeightEntry) - const extractSeparableConvParams = TfjsImageRecognitionBase.loadSeparableConvParamsFactory(extractWeightEntry) + const extractSeparableConvParams = loadSeparableConvParamsFactory(extractWeightEntry) function extractReductionBlockParams(mappedPrefix: string): ReductionBlockParams { @@ -40,9 +46,9 @@ function loadParamsFactory(weightMap: any, paramMappings: TfjsImageRecognitionBa export function extractParamsFromWeigthMap( weightMap: tf.NamedTensorMap, numMainBlocks: number -): { params: TinyXceptionParams, paramMappings: TfjsImageRecognitionBase.ParamMapping[] } { +): { params: TinyXceptionParams, paramMappings: ParamMapping[] } { - const paramMappings: TfjsImageRecognitionBase.ParamMapping[] = [] + const paramMappings: ParamMapping[] = [] const { extractConvParams, @@ -74,7 +80,7 @@ export function extractParamsFromWeigthMap( separable_conv: exit_flow_separable_conv } - TfjsImageRecognitionBase.disposeUnusedWeightTensors(weightMap, paramMappings) + disposeUnusedWeightTensors(weightMap, paramMappings) return { params: { entry_flow, middle_flow, exit_flow }, paramMappings } } \ No newline at end of file diff --git a/src/xception/types.ts b/src/xception/types.ts index 7b24a8e3..0883a58d 100644 --- a/src/xception/types.ts +++ b/src/xception/types.ts @@ -1,26 +1,26 @@ -import { TfjsImageRecognitionBase } from "tfjs-image-recognition-base"; +import { ConvParams, SeparableConvParams } from '../common'; export type ReductionBlockParams = { - separable_conv0: TfjsImageRecognitionBase.SeparableConvParams - separable_conv1: TfjsImageRecognitionBase.SeparableConvParams - expansion_conv: TfjsImageRecognitionBase.ConvParams + separable_conv0: SeparableConvParams + separable_conv1: SeparableConvParams + expansion_conv: ConvParams } export type MainBlockParams = { - separable_conv0: TfjsImageRecognitionBase.SeparableConvParams - separable_conv1: TfjsImageRecognitionBase.SeparableConvParams - separable_conv2: TfjsImageRecognitionBase.SeparableConvParams + separable_conv0: SeparableConvParams + separable_conv1: SeparableConvParams + separable_conv2: SeparableConvParams } export type TinyXceptionParams = { entry_flow: { - conv_in: TfjsImageRecognitionBase.ConvParams + conv_in: ConvParams reduction_block_0: ReductionBlockParams reduction_block_1: ReductionBlockParams } middle_flow: any, exit_flow: { reduction_block: ReductionBlockParams - separable_conv: TfjsImageRecognitionBase.SeparableConvParams + separable_conv: SeparableConvParams } } \ No newline at end of file diff --git a/test/Environment.ts b/test/Environment.ts index 2cb3be5f..757a4e8e 100644 --- a/test/Environment.ts +++ b/test/Environment.ts @@ -1,4 +1,4 @@ -import { NeuralNetwork } from 'tfjs-image-recognition-base'; +import { NeuralNetwork } from '../src'; export type TestEnv = { loadImage: (uri: string) => Promise diff --git a/test/NeuralNetwork.test.ts b/test/NeuralNetwork.test.ts new file mode 100644 index 00000000..0248f56b --- /dev/null +++ b/test/NeuralNetwork.test.ts @@ -0,0 +1,239 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { NeuralNetwork } from '../src'; + +class FakeNeuralNetwork extends NeuralNetwork { + constructor( + convFilter: tf.Tensor = tf.tensor(0), + convBias: tf.Tensor = tf.tensor(0), + fcWeights: tf.Tensor = tf.tensor(0) + ) { + super('FakeNeuralNetwork') + this._params = { + conv: { + filter: convFilter, + bias: convBias, + }, + fc: fcWeights + } + + this._paramMappings = [ + { originalPath: 'conv2d/filter', paramPath: 'conv/filter' }, + { originalPath: 'conv2d/bias', paramPath: 'conv/bias' }, + { originalPath: 'dense/weights', paramPath: 'fc' } + ] + } + + protected getDefaultModelName(): any { + throw new Error('FakeNeuralNetwork - getDefaultModelName not implemented') + } + + protected extractParams(_: any): any { + throw new Error('FakeNeuralNetwork - extractParams not implemented') + } + + protected extractParamsFromWeigthMap(_: any): any { + throw new Error('FakeNeuralNetwork - extractParamsFromWeigthMap not implemented') + } +} + +describe('NeuralNetwork', () => { + + describe('getParamFromPath', () => { + + it('returns correct params', () => tf.tidy(() => { + const convFilter = tf.tensor(0) + const convBias = tf.tensor(0) + const fcWeights = tf.tensor(0) + const net = new FakeNeuralNetwork(convFilter, convBias, fcWeights) + + expect(net.getParamFromPath('conv/filter')).toEqual(convFilter) + expect(net.getParamFromPath('conv/bias')).toEqual(convBias) + expect(net.getParamFromPath('fc')).toEqual(fcWeights) + })) + + it('throws if param is not a tensor', () => tf.tidy(() => { + const net = new FakeNeuralNetwork(null as any) + const fakePath = 'conv/filter' + + expect( + () => net.getParamFromPath(fakePath) + ).toThrowError(`traversePropertyPath - parameter is not a tensor, for path ${fakePath}`) + })) + + it('throws if key path invalid', () => tf.tidy(() => { + const net = new FakeNeuralNetwork() + const fakePath = 'conv2d/foo' + + expect( + () => net.getParamFromPath(fakePath) + ).toThrowError(`traversePropertyPath - object does not have property conv2d, for path ${fakePath}`) + })) + + }) + + describe('reassignParamFromPath', () => { + + it('sets correct params', () => tf.tidy(() => { + const net = new FakeNeuralNetwork() + + const convFilter = tf.tensor(0) + const convBias = tf.tensor(0) + const fcWeights = tf.tensor(0) + net.reassignParamFromPath('conv/filter', convFilter) + net.reassignParamFromPath('conv/bias', convBias) + net.reassignParamFromPath('fc', fcWeights) + + expect(net.params.conv.filter).toEqual(convFilter) + expect(net.params.conv.bias).toEqual(convBias) + expect(net.params.fc).toEqual(fcWeights) + })) + + it('throws if param is not a tensor', () => tf.tidy(() => { + const net = new FakeNeuralNetwork(null as any) + const fakePath = 'conv/filter' + + expect( + () => net.reassignParamFromPath(fakePath, tf.tensor(0)) + ).toThrowError(`traversePropertyPath - parameter is not a tensor, for path ${fakePath}`) + })) + + it('throws if key path invalid', () => tf.tidy(() => { + const net = new FakeNeuralNetwork() + const fakePath = 'conv2d/foo' + + expect( + () => net.reassignParamFromPath(fakePath, tf.tensor(0)) + ).toThrowError(`traversePropertyPath - object does not have property conv2d, for path ${fakePath}`) + })) + + }) + + describe('getParamList', () => { + + it('returns param tensors with path', () => tf.tidy(() => { + const convFilter = tf.tensor(0) + const convBias = tf.tensor(0) + const fcWeights = tf.tensor(0) + const net = new FakeNeuralNetwork(convFilter, convBias, fcWeights) + + const paramList = net.getParamList() + + expect(paramList.length).toEqual(3) + expect(paramList[0].path).toEqual('conv/filter') + expect(paramList[1].path).toEqual('conv/bias') + expect(paramList[2].path).toEqual('fc') + expect(paramList[0].tensor).toEqual(convFilter) + expect(paramList[1].tensor).toEqual(convBias) + expect(paramList[2].tensor).toEqual(fcWeights) + })) + + }) + + describe('getFrozenParams', () => { + + it('returns all frozen params', () => tf.tidy(() => { + const convFilter = tf.tensor(0) + const convBias = tf.tensor(0) + const fcWeights = tf.variable(tf.scalar(0)) + const net = new FakeNeuralNetwork(convFilter, convBias, fcWeights) + + const frozenParams = net.getFrozenParams() + + expect(frozenParams.length).toEqual(2) + expect(frozenParams[0].path).toEqual('conv/filter') + expect(frozenParams[1].path).toEqual('conv/bias') + expect(frozenParams[0].tensor).toEqual(convFilter) + expect(frozenParams[1].tensor).toEqual(convBias) + })) + + }) + + describe('getTrainableParams', () => { + + it('returns all trainable params', () => tf.tidy(() => { + const convFilter = tf.variable(tf.scalar(0)) + const convBias = tf.variable(tf.scalar(0)) + const fcWeights = tf.tensor(0) + const net = new FakeNeuralNetwork(convFilter, convBias, fcWeights) + + const trainableParams = net.getTrainableParams() + + expect(trainableParams.length).toEqual(2) + expect(trainableParams[0].path).toEqual('conv/filter') + expect(trainableParams[1].path).toEqual('conv/bias') + expect(trainableParams[0].tensor).toEqual(convFilter) + expect(trainableParams[1].tensor).toEqual(convBias) + })) + + }) + + describe('dispose', () => { + + it('disposes all param tensors', () => tf.tidy(() => { + const numTensors = tf.memory().numTensors + const net = new FakeNeuralNetwork() + + net.dispose() + + expect(net.params).toBe(undefined) + expect(tf.memory().numTensors - numTensors).toEqual(0) + })) + + }) + + describe('variable', () => { + + it('make all param tensors trainable', () => tf.tidy(() => { + const net = new FakeNeuralNetwork() + + net.variable() + + expect(net.params.conv.filter instanceof tf.Variable).toBe(true) + expect(net.params.conv.bias instanceof tf.Variable).toBe(true) + expect(net.params.fc instanceof tf.Variable).toBe(true) + })) + + it('disposes old tensors', () => tf.tidy(() => { + const net = new FakeNeuralNetwork() + const numTensors = tf.memory().numTensors + + net.variable() + + expect(tf.memory().numTensors - numTensors).toEqual(0) + })) + + }) + + describe('freeze', () => { + + it('freezes all param variables', () => tf.tidy(() => { + const net = new FakeNeuralNetwork( + tf.variable(tf.scalar(0)), + tf.variable(tf.scalar(0)), + tf.variable(tf.scalar(0)) + ) + + net.freeze() + + expect(net.params.conv.filter instanceof tf.Variable).toBe(false) + expect(net.params.conv.bias instanceof tf.Variable).toBe(false) + expect(net.params.fc instanceof tf.Variable).toBe(false) + })) + + it('disposes old tensors', () => () => { + const net = new FakeNeuralNetwork( + tf.variable(tf.scalar(0)), + tf.variable(tf.scalar(0)), + tf.variable(tf.scalar(0)) + ) + const numTensors = tf.memory().numTensors + + net.freeze() + + expect(tf.memory().numTensors - numTensors).toEqual(0) + }) + + }) + +}) diff --git a/test/data/boxes.json b/test/data/boxes.json new file mode 100644 index 00000000..87043eb3 --- /dev/null +++ b/test/data/boxes.json @@ -0,0 +1 @@ +[{"predictions":[],"groundTruth":[{"label":14,"x":174,"y":101,"width":175,"height":250}]},{"predictions":[{"score":0.7691803822132828,"classScore":0.7687403453616095,"label":14,"x":174.80739067078562,"y":101.24652901136822,"width":107.21253846292248,"height":130.36960304401345},{"score":0.6650128354611389,"classScore":0.6648250709475522,"label":0,"x":88.00403034948206,"y":57.28796517004654,"width":346.8964870013924,"height":154.64824963698928}],"groundTruth":[{"label":0,"x":104,"y":78,"width":271,"height":105},{"label":0,"x":133,"y":88,"width":64,"height":35},{"label":14,"x":195,"y":180,"width":18,"height":49},{"label":14,"x":26,"y":189.00000000000003,"width":18,"height":49}]},{"predictions":[{"score":0.9796686439333566,"classScore":0.9796404986030244,"label":0,"x":0.7092259472062379,"y":112.93828229104882,"width":503.36772784059957,"height":125.40083611003898}],"groundTruth":[{"label":0,"x":9,"y":107,"width":490,"height":156},{"label":0,"x":421,"y":200.00000000000003,"width":61,"height":26},{"label":0,"x":325,"y":188,"width":86,"height":35}]},{"predictions":[{"score":0.6555995462593681,"classScore":0.6555995462593681,"label":19,"x":168.43528196184994,"y":103.87677334009256,"width":142.1917672089718,"height":152.21249718337248}],"groundTruth":[{"label":19,"x":156,"y":89,"width":188,"height":190.00000000000003}]},{"predictions":[{"score":0.7654244759562995,"classScore":0.7653893919815798,"label":18,"x":4.657064294604191,"y":19.396089415586324,"width":244.37305501026242,"height":295.7467446833704}],"groundTruth":[{"label":18,"x":263,"y":32,"width":237,"height":263},{"label":18,"x":1,"y":36,"width":234,"height":263}]},{"predictions":[],"groundTruth":[{"label":3,"x":274,"y":11,"width":163,"height":268},{"label":3,"x":184,"y":213.99999999999997,"width":97,"height":38}]},{"predictions":[{"score":0.6628550520040408,"classScore":0.6247242104538263,"label":11,"x":72.80442223810171,"y":96.69588943582755,"width":348.97090277779495,"height":201.39196990473874}],"groundTruth":[{"label":11,"x":123,"y":114.99999999999999,"width":256,"height":160},{"label":8,"x":75,"y":1,"width":353,"height":374}]},{"predictions":[{"score":0.9813790697369239,"classScore":0.9785855359209782,"label":2,"x":27.170210865162982,"y":71.72488948863362,"width":236.38816975654763,"height":288.48520937333933}],"groundTruth":[{"label":2,"x":27,"y":45,"width":239,"height":330}]},{"predictions":[{"score":0.994037798212397,"classScore":0.994037798212397,"label":19,"x":221.0525346736894,"y":17.11757711236289,"width":270.7030429591586,"height":238.46462518346138},{"score":0.9744214784574556,"classScore":0.9744214784574556,"label":19,"x":-8.843471823254735,"y":26.29916942997549,"width":300.1775416643035,"height":221.028915747058}],"groundTruth":[{"label":19,"x":251,"y":28.000000000000004,"width":224,"height":239},{"label":19,"x":22,"y":28.000000000000004,"width":229,"height":245}]},{"predictions":[{"score":0.9887495371422556,"classScore":0.6017762085053843,"label":16,"x":-19.909558311815623,"y":21.542763412106453,"width":414.6293719402734,"height":308.439681183784}],"groundTruth":[{"label":18,"x":1,"y":26,"width":357,"height":314}]},{"predictions":[{"score":0.9098243761845755,"classScore":0.9012128531842766,"label":14,"x":71.38750749942446,"y":54.7591392923992,"width":223.77602020255526,"height":366.18752833394205}],"groundTruth":[{"label":1,"x":70,"y":202,"width":185,"height":298},{"label":1,"x":251,"y":242,"width":83,"height":258},{"label":1,"x":1,"y":144,"width":66,"height":292},{"label":14,"x":1,"y":1,"width":65,"height":362},{"label":14,"x":74,"y":1,"width":198,"height":461},{"label":14,"x":251.99999999999997,"y":19,"width":82,"height":468}]},{"predictions":[],"groundTruth":[{"label":4,"x":87,"y":100,"width":22,"height":65},{"label":4,"x":41,"y":114,"width":32,"height":67},{"label":4,"x":324,"y":148,"width":28,"height":58},{"label":4,"x":426,"y":157,"width":17,"height":38},{"label":14,"x":3,"y":91,"width":40,"height":114.99999999999999},{"label":14,"x":4,"y":28.000000000000004,"width":457,"height":344}]},{"predictions":[{"score":0.7958273091892446,"classScore":0.7818440392104067,"label":16,"x":56.79348195528594,"y":31.70232255780979,"width":313.6508630022377,"height":233.41861230559402}],"groundTruth":[{"label":16,"x":25,"y":34,"width":394,"height":237}]},{"predictions":[{"score":0.9201249561107422,"classScore":0.9190199099731305,"label":19,"x":-26.03511346499099,"y":86.38443500732924,"width":297.6374106029245,"height":254.9645026012354}],"groundTruth":[{"label":19,"x":1,"y":95.00000000000001,"width":239,"height":241.00000000000003}]},{"predictions":[{"score":0.9161253701392962,"classScore":0.9058719094189636,"label":3,"x":32.61301936266575,"y":115.64641272085254,"width":124.58349953920403,"height":101.73433847921152},{"score":0.8166243906870652,"classScore":0.8145709063786849,"label":3,"x":377.0253934850286,"y":187.73553354763786,"width":117.8902182556456,"height":98.97330181906452},{"score":0.682690236232842,"classScore":0.6699430513504846,"label":3,"x":235.7334992854692,"y":132.159729119493,"width":107.03585321829007,"height":91.19282688695355}],"groundTruth":[{"label":3,"x":356,"y":183,"width":144,"height":97},{"label":3,"x":60,"y":109,"width":82,"height":104},{"label":3,"x":246,"y":134,"width":102,"height":83}]},{"predictions":[],"groundTruth":[{"label":0,"x":181,"y":127,"width":93,"height":66}]},{"predictions":[{"score":0.7002533672629911,"classScore":0.6986950241075993,"label":10,"x":15.059672065713514,"y":183.7041276920172,"width":442.0153237833172,"height":187.85830177400535}],"groundTruth":[{"label":10,"x":1,"y":170,"width":473,"height":205},{"label":4,"x":97,"y":124,"width":53,"height":173}]},{"predictions":[{"score":0.8785236648425084,"classScore":0.8785169098747734,"label":0,"x":5.851446853403164,"y":97.19943552245653,"width":498.761858292814,"height":123.84420987488453},{"score":0.6115907530866461,"classScore":0.50117202877969,"label":13,"x":53.19739845463284,"y":133.81894255992148,"width":226.98909377605403,"height":110.1798238654668}],"groundTruth":[{"label":0,"x":8,"y":96,"width":483,"height":136}]},{"predictions":[{"score":0.944397664196853,"classScore":0.944397664196853,"label":14,"x":-4.598343170909137,"y":82.8255836803386,"width":335.55829964238654,"height":426.5030936299306}],"groundTruth":[{"label":14,"x":25,"y":71,"width":279,"height":429}]},{"predictions":[{"score":0.8280960338115692,"classScore":0.8280960338115692,"label":14,"x":-1.5531990164389664,"y":-25.619537343989954,"width":343.5896574164529,"height":418.021221131633}],"groundTruth":[{"label":14,"x":277,"y":3,"width":223,"height":372},{"label":14,"x":12,"y":3,"width":293,"height":372}]},{"predictions":[],"groundTruth":[{"label":12,"x":54,"y":50,"width":231,"height":212}]},{"predictions":[],"groundTruth":[{"label":18,"x":1,"y":39,"width":366,"height":231}]},{"predictions":[{"score":0.9923042836360139,"classScore":0.9923042836360139,"label":14,"x":142.7915727970977,"y":103.88205108871067,"width":355.23004763212964,"height":246.3305211907232}],"groundTruth":[{"label":4,"x":124,"y":107,"width":106,"height":236},{"label":14,"x":137,"y":78,"width":360,"height":297},{"label":14,"x":89,"y":201.99999999999997,"width":40,"height":45},{"label":14,"x":72,"y":209,"width":39,"height":50}]},{"predictions":[{"score":0.7524108551868512,"classScore":0.7511045464768792,"label":2,"x":118.02073153439419,"y":100.075300105594,"width":422.1093645713623,"height":236.96557132518132},{"score":0.6675556654678318,"classScore":0.6547138694454316,"label":2,"x":73.15242855329525,"y":27.560020743027827,"width":138.40089468837368,"height":233.12522715598948}],"groundTruth":[{"label":2,"x":59,"y":15,"width":161,"height":224.00000000000003},{"label":2,"x":161,"y":122,"width":339,"height":211}]},{"predictions":[{"score":0.8762162584051255,"classScore":0.8753776565500138,"label":13,"x":316.9200075142487,"y":15.978265485680515,"width":176.55547521701214,"height":162.79950103493826},{"score":0.7377645066274695,"classScore":0.7377447182413641,"label":13,"x":4.093778050646285,"y":53.134905261691884,"width":484.3047072124648,"height":242.59882876631897}],"groundTruth":[{"label":13,"x":54,"y":25,"width":400,"height":290},{"label":13,"x":318,"y":37,"width":171,"height":124},{"label":14,"x":369,"y":1,"width":89,"height":129}]},{"predictions":[{"score":0.6987292311899743,"classScore":0.6981430425202332,"label":14,"x":190.34686802768005,"y":49.30728986502912,"width":125.24766886822394,"height":156.9966166652352}],"groundTruth":[{"label":12,"x":100,"y":96,"width":255,"height":228},{"label":14,"x":198,"y":58,"width":88,"height":139}]},{"predictions":[{"score":0.7222071563927183,"classScore":0.722190583335829,"label":14,"x":334.27473386380956,"y":84.92576867213481,"width":130.96630892426847,"height":266.5457370602929}],"groundTruth":[{"label":14,"x":51,"y":80,"width":71,"height":186},{"label":14,"x":367,"y":92,"width":94,"height":223}]},{"predictions":[],"groundTruth":[{"label":17,"x":50,"y":121,"width":245,"height":217},{"label":8,"x":474,"y":140,"width":26,"height":144}]},{"predictions":[{"score":0.9539370238688553,"classScore":0.9261299783258337,"label":9,"x":53.1153804004625,"y":182.89761062792732,"width":186.2471532372765,"height":135.7749268658459}],"groundTruth":[{"label":9,"x":71,"y":252,"width":145,"height":62},{"label":9,"x":58,"y":202,"width":183,"height":93}]},{"predictions":[{"score":0.9517353634593023,"classScore":0.9517353634593023,"label":14,"x":295.17106719541056,"y":154.82150461830355,"width":110.50320081452242,"height":211.90106600291168},{"score":0.8956736871398788,"classScore":0.858500424420289,"label":0,"x":37.63960073319886,"y":77.60772153913085,"width":428.89316111622963,"height":269.4121395866779},{"score":0.8835170300270249,"classScore":0.815483197617464,"label":14,"x":2.93943475182442,"y":180.41620151640234,"width":112.98117869748658,"height":203.70600878430784}],"groundTruth":[{"label":14,"x":293,"y":162,"width":126,"height":212.99999999999997},{"label":14,"x":114,"y":165,"width":114,"height":208},{"label":14,"x":5,"y":172,"width":111,"height":201},{"label":0,"x":2,"y":68,"width":484,"height":257}]},{"predictions":[{"score":0.9625422337065817,"classScore":0.5203173443928317,"label":5,"x":-21.917914358316192,"y":20.243043364143507,"width":496.5538125661707,"height":282.63866737628933}],"groundTruth":[{"label":9,"x":2,"y":2,"width":457,"height":332}]},{"predictions":[],"groundTruth":[{"label":9,"x":341,"y":102,"width":159,"height":304},{"label":14,"x":36,"y":34,"width":43,"height":89},{"label":14,"x":2,"y":44,"width":34,"height":76},{"label":14,"x":29,"y":42,"width":12,"height":22}]},{"predictions":[],"groundTruth":[{"label":1,"x":1,"y":198.99999999999997,"width":39,"height":94},{"label":14,"x":1,"y":85,"width":28,"height":194},{"label":6,"x":64,"y":164,"width":87,"height":46},{"label":6,"x":147,"y":167,"width":39,"height":33}]},{"predictions":[],"groundTruth":[{"label":7,"x":124,"y":68,"width":195,"height":242}]},{"predictions":[],"groundTruth":[{"label":3,"x":290,"y":143,"width":210,"height":107},{"label":6,"x":422,"y":237,"width":39,"height":18}]},{"predictions":[{"score":0.8962182918520621,"classScore":0.8930069154800067,"label":7,"x":-4.008327108320088,"y":70.37482224480168,"width":352.62230878734033,"height":434.67563823380846}],"groundTruth":[{"label":7,"x":1,"y":49,"width":340,"height":450}]},{"predictions":[{"score":0.8076964825223881,"classScore":0.8067911153833874,"label":19,"x":94.5857889149978,"y":15.249397515597346,"width":312.17182522265784,"height":232.76671025556075}],"groundTruth":[{"label":4,"x":36,"y":250,"width":43,"height":104},{"label":19,"x":160,"y":26,"width":211,"height":214.99999999999997}]},{"predictions":[],"groundTruth":[{"label":1,"x":1,"y":178,"width":234,"height":153},{"label":1,"x":8,"y":134,"width":218,"height":163}]},{"predictions":[{"score":0.8167527436184676,"classScore":0.8150299759023477,"label":17,"x":324.627645002174,"y":209.56248991054514,"width":178.6013940037733,"height":160.7920031534356}],"groundTruth":[{"label":1,"x":31,"y":74,"width":338,"height":272},{"label":17,"x":344,"y":207.00000000000003,"width":156,"height":167}]},{"predictions":[{"score":0.9407357523667703,"classScore":0.8181894101476949,"label":3,"x":-12.598615939355684,"y":77.28791949345168,"width":451.4040682919014,"height":104.75131312062308}],"groundTruth":[{"label":18,"x":34,"y":84,"width":466,"height":100},{"label":14,"x":87,"y":100,"width":20,"height":24}]},{"predictions":[{"score":0.752703742634889,"classScore":0.7477982878493062,"label":18,"x":81.62897083839832,"y":109.91140563880278,"width":329.63940263951787,"height":207.33306830787868}],"groundTruth":[{"label":18,"x":79,"y":96,"width":313,"height":204}]},{"predictions":[{"score":0.8644956045627069,"classScore":0.8612763441448485,"label":2,"x":132.49570312762185,"y":53.63054832818552,"width":361.1467077508502,"height":308.41826405754813}],"groundTruth":[{"label":2,"x":135,"y":46,"width":365,"height":328},{"label":2,"x":124,"y":146,"width":241,"height":229}]},{"predictions":[{"score":0.7878781057167802,"classScore":0.7875985926864165,"label":5,"x":-52.21908643538992,"y":93.02825869092997,"width":459.81048644960373,"height":189.74556017463232}],"groundTruth":[{"label":14,"x":394,"y":199,"width":10,"height":24},{"label":14,"x":424,"y":199,"width":12,"height":20.999999999999996},{"label":14,"x":434,"y":195.99999999999997,"width":10,"height":24},{"label":14,"x":443,"y":195,"width":9,"height":25},{"label":5,"x":29,"y":113,"width":324,"height":153},{"label":0,"x":328,"y":86,"width":146,"height":106},{"label":6,"x":2,"y":213,"width":26,"height":22}]},{"predictions":[{"score":0.743069405764908,"classScore":0.6373241114093381,"label":15,"x":78.30857626719687,"y":92.88842351228324,"width":90.06631442691248,"height":108.2613080721939},{"score":0.6118246612799929,"classScore":0.5526098988725323,"label":17,"x":123.27749832194552,"y":160.7244957117589,"width":181.02068890434398,"height":101.80487324073387}],"groundTruth":[{"label":17,"x":119,"y":177,"width":212,"height":100.99999999999999},{"label":15,"x":65,"y":90,"width":105,"height":107},{"label":8,"x":133,"y":159,"width":46,"height":38}]},{"predictions":[{"score":0.8145260519407391,"classScore":0.81435355543938,"label":5,"x":7.672527282127808,"y":32.34257681806332,"width":382.86829973169773,"height":419.57299155829344}],"groundTruth":[{"label":5,"x":12,"y":2,"width":405,"height":465},{"label":6,"x":7.000000000000001,"y":39,"width":56.00000000000001,"height":55},{"label":6,"x":362,"y":24,"width":60,"height":29},{"label":6,"x":376,"y":36,"width":46,"height":45},{"label":6,"x":373,"y":68,"width":49,"height":40},{"label":6,"x":376,"y":98,"width":46,"height":112}]},{"predictions":[{"score":0.9424702777106074,"classScore":0.9424702777106074,"label":14,"x":36.45312644547885,"y":6.678077418736085,"width":290.71862354973774,"height":363.4338941971187}],"groundTruth":[{"label":14,"x":51,"y":2,"width":274,"height":373}]},{"predictions":[],"groundTruth":[{"label":16,"x":59,"y":94,"width":413,"height":176}]},{"predictions":[{"score":0.573654529177009,"classScore":0.3838500178452209,"label":3,"x":69.91134561978498,"y":181.02671387178248,"width":207.20328569794563,"height":137.88072756263017}],"groundTruth":[{"label":3,"x":80,"y":168,"width":174,"height":144},{"label":3,"x":184,"y":160,"width":288,"height":109}]},{"predictions":[{"score":0.5588748494638404,"classScore":0.5467246329459337,"label":11,"x":201.5844166952682,"y":114.26291552404027,"width":127.83844565704325,"height":148.51310204029576}],"groundTruth":[{"label":11,"x":217,"y":118,"width":88,"height":145}]},{"predictions":[{"score":0.7826680552768471,"classScore":0.3132984973801781,"label":8,"x":110.13095572354015,"y":296.0810272956989,"width":136.69938049361562,"height":149.26902152141102}],"groundTruth":[{"label":5,"x":89,"y":323,"width":184,"height":113},{"label":6,"x":272,"y":369,"width":51.00000000000001,"height":19},{"label":6,"x":2,"y":380,"width":87,"height":55},{"label":14,"x":333,"y":356,"width":25,"height":82},{"label":14,"x":319,"y":357,"width":22,"height":67},{"label":14,"x":354,"y":363,"width":21,"height":51},{"label":6,"x":48.99999999999999,"y":367,"width":41,"height":22},{"label":6,"x":113,"y":138,"width":47,"height":26}]}] \ No newline at end of file diff --git a/test/data/dummy.weights b/test/data/dummy.weights new file mode 100644 index 00000000..aae44b89 --- /dev/null +++ b/test/data/dummy.weights @@ -0,0 +1 @@ ++��>�%�8�vZ>w�X>��ʽ��*cz \ No newline at end of file diff --git a/test/env.node.ts b/test/env.node.ts index 76577635..e8ad2d8f 100644 --- a/test/env.node.ts +++ b/test/env.node.ts @@ -1,8 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { NeuralNetwork } from 'tfjs-image-recognition-base'; -import { env } from '../src'; +import { env, NeuralNetwork } from '../src'; import { TestEnv } from './Environment'; require('@tensorflow/tfjs-node') diff --git a/test/env.ts b/test/env.ts index 1ba4021b..81f0281e 100644 --- a/test/env.ts +++ b/test/env.ts @@ -1,7 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { fetchNetWeights, NeuralNetwork } from 'tfjs-image-recognition-base'; -import { fetchImage, fetchJson } from '../src'; +import { fetchImage, fetchJson, fetchNetWeights, NeuralNetwork } from '../src'; import { TestEnv } from './Environment'; jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000 diff --git a/test/images/white.png b/test/images/white.png new file mode 100644 index 0000000000000000000000000000000000000000..1e18d398810a4fc486a7b8c1e37315a9e51b0ab5 GIT binary patch literal 450 zcmeAS@N?(olHy`uVBq!ia0vp^(?FPm2}o{Eezp}zF%}28J29*~C-V}>VM%xNb!1@J z*w6hZkrl}2EbxddW? { + + describe('constructor', () => { + + it('properties', () => { + const box = new BoundingBox(5, 10, 15, 20) + expect(box.left).toEqual(5) + expect(box.x).toEqual(5) + expect(box.top).toEqual(10) + expect(box.y).toEqual(10) + expect(box.right).toEqual(15) + expect(box.bottom).toEqual(20) + expect(box.width).toEqual(10) + expect(box.height).toEqual(10) + expect(box.area).toEqual(100) + }) + + }) + +}) \ No newline at end of file diff --git a/test/tests/classes/Box.test.ts b/test/tests/classes/Box.test.ts new file mode 100644 index 00000000..b0579bfa --- /dev/null +++ b/test/tests/classes/Box.test.ts @@ -0,0 +1,107 @@ +import { Box } from '../../../src/classes/Box'; + +describe('BoundingBox', () => { + + describe('constructor', () => { + + describe('from IBoundingBox', () => { + + it('properties', () => { + + const box = new Box({ left: 5, top: 10, right: 15, bottom: 20 }) + + expect(box.left).toEqual(5) + expect(box.x).toEqual(5) + expect(box.top).toEqual(10) + expect(box.y).toEqual(10) + expect(box.right).toEqual(15) + expect(box.bottom).toEqual(20) + expect(box.width).toEqual(10) + expect(box.height).toEqual(10) + expect(box.area).toEqual(100) + }) + + }) + + describe('from IRect', () => { + + it('properties', () => { + + const box = new Box({ x: 5, y: 10, width: 15, height: 20 }) + + expect(box.left).toEqual(5) + expect(box.x).toEqual(5) + expect(box.top).toEqual(10) + expect(box.y).toEqual(10) + expect(box.right).toEqual(20) + expect(box.bottom).toEqual(30) + expect(box.width).toEqual(15) + expect(box.height).toEqual(20) + expect(box.area).toEqual(300) + }) + + }) + + }) + + describe('rescale', () => { + + it('scale down by factor 0.5', () => { + + const box = new Box({ x: 10, y: 20, width: 20, height: 40 }) + + const rescaled = box.rescale(0.5) + + expect(rescaled.x).toEqual(5) + expect(rescaled.y).toEqual(10) + expect(rescaled.width).toEqual(10) + expect(rescaled.height).toEqual(20) + + }) + + it('scale up by factor 2', () => { + + const box = new Box({ x: 10, y: 20, width: 20, height: 40 }) + + const rescaled = box.rescale(2) + + expect(rescaled.x).toEqual(20) + expect(rescaled.y).toEqual(40) + expect(rescaled.width).toEqual(40) + expect(rescaled.height).toEqual(80) + + }) + + it('scale to dimensions ', () => { + + const box = new Box({ x: 0.1, y: 0.2, width: 0.2, height: 0.4 }) + + const rescaled = box.rescale({ width: 100, height: 200 }) + + expect(rescaled.x).toEqual(10) + expect(rescaled.y).toEqual(40) + expect(rescaled.width).toEqual(20) + expect(rescaled.height).toEqual(80) + + }) + + }) + + describe('shift', () => { + + it('should shift box by x, y', () => { + + const box = new Box({ x: 10, y: 20, width: 20, height: 40 }) + + const shifted = box.shift(20, 40) + + expect(shifted.x).toEqual(30) + expect(shifted.y).toEqual(60) + expect(shifted.width).toEqual(20) + expect(shifted.height).toEqual(40) + + }) + + }) + +}) \ No newline at end of file diff --git a/test/tests/classes/Rect.test.ts b/test/tests/classes/Rect.test.ts new file mode 100644 index 00000000..58cb260b --- /dev/null +++ b/test/tests/classes/Rect.test.ts @@ -0,0 +1,52 @@ +import { Box } from '../../../src/classes/Box'; +import { Rect } from '../../../src/classes/Rect'; + +describe('Rect', () => { + + describe('constructor', () => { + + it('can be created', () => { + const rect = new Rect(0, 10, 20, 30) + expect(rect instanceof Rect).toBe(true) + expect(rect instanceof Box).toBe(true) + expect(rect.x).toEqual(0) + expect(rect.y).toEqual(10) + expect(rect.width).toEqual(20) + expect(rect.height).toEqual(30) + }) + + it('throws if coordinates are invalid', () => { + + const expectConstructorToThrow = (x: any, y: any, width: any, height: any) => { + expect(() => new Rect(x, y, width, height)).toThrowError(`Box.constructor - expected box to be IBoundingBox | IRect, instead have ${JSON.stringify({ x, y, width, height })}`) + } + + expectConstructorToThrow(NaN, 10, 20, 30) + expectConstructorToThrow(0, Infinity, 20, 30) + expectConstructorToThrow(0, 10, -Infinity, 30) + expectConstructorToThrow(0, 10, 20, null) + expectConstructorToThrow(NaN, Infinity, undefined, null) + expectConstructorToThrow(undefined, undefined, undefined, undefined) + }) + + it('throws if height or width invalid', () => { + expect(() => new Rect(0, 10, -20, 30, false)).toThrowError('Box.constructor - width (-20) and height (30) must be positive numbers') + expect(() => new Rect(0, 10, 20, -30, false)).toThrowError('Box.constructor - width (20) and height (-30) must be positive numbers') + }) + + it('properties', () => { + const rect = new Rect(5, 10, 15, 20) + expect(rect.left).toEqual(5) + expect(rect.x).toEqual(5) + expect(rect.top).toEqual(10) + expect(rect.y).toEqual(10) + expect(rect.right).toEqual(20) + expect(rect.bottom).toEqual(30) + expect(rect.width).toEqual(15) + expect(rect.height).toEqual(20) + expect(rect.area).toEqual(300) + }) + + }) + +}) \ No newline at end of file diff --git a/test/tests/common/getModelUris.test.ts b/test/tests/common/getModelUris.test.ts new file mode 100644 index 00000000..d84f88da --- /dev/null +++ b/test/tests/common/getModelUris.test.ts @@ -0,0 +1,68 @@ +import { getModelUris } from '../../../src/common/getModelUris'; + +const FAKE_DEFAULT_MODEL_NAME = 'fake_model_name' + +describe('getModelUris', () => { + + it('returns uris from relative url if no argument passed', () => { + const result = getModelUris(undefined, FAKE_DEFAULT_MODEL_NAME) + + expect(result.manifestUri).toEqual(`${FAKE_DEFAULT_MODEL_NAME}-weights_manifest.json`) + expect(result.modelBaseUri).toEqual('') + }) + + it('returns uris from relative url for empty string', () => { + const result = getModelUris('', FAKE_DEFAULT_MODEL_NAME) + + expect(result.manifestUri).toEqual(`${FAKE_DEFAULT_MODEL_NAME}-weights_manifest.json`) + expect(result.modelBaseUri).toEqual('') + }) + + it('returns uris for top level url, leading slash preserved', () => { + const result = getModelUris('/', FAKE_DEFAULT_MODEL_NAME) + + expect(result.manifestUri).toEqual(`/${FAKE_DEFAULT_MODEL_NAME}-weights_manifest.json`) + expect(result.modelBaseUri).toEqual('/') + }) + + it('returns uris, given url path', () => { + const uri = 'path/to/modelfiles' + const result = getModelUris(uri, FAKE_DEFAULT_MODEL_NAME) + + expect(result.manifestUri).toEqual(`${uri}/${FAKE_DEFAULT_MODEL_NAME}-weights_manifest.json`) + expect(result.modelBaseUri).toEqual(uri) + }) + + it('returns uris, given url path, leading slash preserved', () => { + const uri = '/path/to/modelfiles' + const result = getModelUris(`/${uri}`, FAKE_DEFAULT_MODEL_NAME) + + expect(result.manifestUri).toEqual(`${uri}/${FAKE_DEFAULT_MODEL_NAME}-weights_manifest.json`) + expect(result.modelBaseUri).toEqual(uri) + }) + + it('returns uris, given manifest uri', () => { + const uri = 'path/to/modelfiles/model-weights_manifest.json' + const result = getModelUris(uri, FAKE_DEFAULT_MODEL_NAME) + + expect(result.manifestUri).toEqual(uri) + expect(result.modelBaseUri).toEqual('path/to/modelfiles') + }) + + it('returns uris, given manifest uri, leading slash preserved', () => { + const uri = '/path/to/modelfiles/model-weights_manifest.json' + const result = getModelUris(uri, FAKE_DEFAULT_MODEL_NAME) + + expect(result.manifestUri).toEqual(uri) + expect(result.modelBaseUri).toEqual('/path/to/modelfiles') + }) + + it('returns correct uris, given external path', () => { + const uri = 'https://example.com/path/to/modelfiles'; + const result = getModelUris(uri, FAKE_DEFAULT_MODEL_NAME) + + expect(result.manifestUri).toEqual(`${uri}/${FAKE_DEFAULT_MODEL_NAME}-weights_manifest.json`) + expect(result.modelBaseUri).toEqual(uri) + }) + +}) \ No newline at end of file diff --git a/test/tests/dom/NetInput.test.ts b/test/tests/dom/NetInput.test.ts new file mode 100644 index 00000000..6b1695ed --- /dev/null +++ b/test/tests/dom/NetInput.test.ts @@ -0,0 +1,87 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { NetInput } from '../../../src'; +import { getTestEnv } from '../../env'; +import { expectAllTensorsReleased, fakeTensor3d } from '../../utils'; + +describe('NetInput', () => { + + let imgEl: HTMLImageElement + + beforeAll(async () => { + imgEl = await getTestEnv().loadImage('test/images/white.png') + }) + + describe('toBatchTensor', () => { + + it('HTMLImageElement, batchSize === 1', () => tf.tidy(() => { + const netInput = new NetInput([imgEl]) + const batchTensor = netInput.toBatchTensor(100) + expect(batchTensor.shape).toEqual([1, 100, 100, 3]) + })) + + it('tf.Tensor3D, batchSize === 1', () => tf.tidy(() => { + const tensor = tf.zeros([200, 200, 3], 'int32') + const netInput = new NetInput([tensor]) + const batchTensor = netInput.toBatchTensor(100) + expect(batchTensor.shape).toEqual([1, 100, 100, 3]) + })) + + it('HTMLImageElements, batchSize === 4', () => tf.tidy(() => { + const netInput = new NetInput([imgEl, imgEl, imgEl, imgEl]) + const batchTensor = netInput.toBatchTensor(100) + expect(batchTensor.shape).toEqual([4, 100, 100, 3]) + })) + + it('tf.Tensor3Ds, batchSize === 4', () => tf.tidy(() => { + const tensor = tf.zeros([200, 200, 3], 'int32') + const netInput = new NetInput([tensor, tensor, tensor, tensor]) + const batchTensor = netInput.toBatchTensor(100) + expect(batchTensor.shape).toEqual([4, 100, 100, 3]) + })) + + it('tf.Tensor3Ds and HTMLImageElements, batchSize === 4', () => tf.tidy(() => { + const tensor = tf.zeros([200, 200, 3], 'int32') + const netInput = new NetInput([tensor, tensor, imgEl, imgEl]) + const batchTensor = netInput.toBatchTensor(100) + expect(batchTensor.shape).toEqual([4, 100, 100, 3]) + })) + + }) + + describe('no memory leaks', () => { + + it('constructor', async () => { + const tensors = [fakeTensor3d(), fakeTensor3d(), fakeTensor3d()] + + await expectAllTensorsReleased(() => { + new NetInput([imgEl]) + new NetInput([imgEl, imgEl, imgEl]) + new NetInput([tensors[0]]) + new NetInput(tensors) + }) + + tensors.forEach(t => t.dispose()) + }) + + describe('toBatchTensor', () => { + + it('single image element', async () => { + await expectAllTensorsReleased(() => { + const batchTensor = new NetInput([imgEl]).toBatchTensor(100, false) + batchTensor.dispose() + }) + }) + + it('multiple image elements', async () => { + await expectAllTensorsReleased(() => { + const batchTensor = new NetInput([imgEl, imgEl, imgEl]).toBatchTensor(100, false) + batchTensor.dispose() + }) + }) + + }) + + }) + +}) diff --git a/test/tests/dom/fetchImage.browser.test.ts b/test/tests/dom/fetchImage.browser.test.ts new file mode 100644 index 00000000..6cba3143 --- /dev/null +++ b/test/tests/dom/fetchImage.browser.test.ts @@ -0,0 +1,25 @@ +import { fetchImage } from '../../../src'; + +describe('fetchImage', () => { + + it('invalid mime type', async () => { + const url = 'base/test/data/boxes.json' + + let err = '' + try { + await fetchImage(url) + } catch (e) { + err = e.toString() + } + + expect(err).toContain('fetchImage - expected blob type to be of type image/*, instead have: application/json') + expect(err).toContain(url) + }) + + it('fetches image', async () => { + const url = 'base/test/images/white.png' + const img = await fetchImage(url) + expect(img instanceof HTMLImageElement).toBe(true) + }) + +}) diff --git a/test/tests/dom/fetchJson.browser.test.ts b/test/tests/dom/fetchJson.browser.test.ts new file mode 100644 index 00000000..ae06ad32 --- /dev/null +++ b/test/tests/dom/fetchJson.browser.test.ts @@ -0,0 +1,10 @@ +import { fetchJson } from '../../../src'; + +describe('fetchJson', () => { + + it('fetches json', async () => { + const url = 'test/data/boxes.json' + expect(async () => await fetchJson(url)).not.toThrow() + }) + +}) diff --git a/test/tests/dom/fetchNetWeights.browser.test.ts b/test/tests/dom/fetchNetWeights.browser.test.ts new file mode 100644 index 00000000..af7024ef --- /dev/null +++ b/test/tests/dom/fetchNetWeights.browser.test.ts @@ -0,0 +1,11 @@ +import { fetchNetWeights } from '../../../src'; + +describe('fetchNetWeights', () => { + + it('fetches .weights file', async () => { + const url = 'test/data/dummy.weights' + const weights = await fetchNetWeights(url) + expect(weights instanceof Float32Array).toBe(true) + }) + +}) diff --git a/test/tests/dom/fetchOrThrow.browser.test.ts b/test/tests/dom/fetchOrThrow.browser.test.ts new file mode 100644 index 00000000..e5c9fc79 --- /dev/null +++ b/test/tests/dom/fetchOrThrow.browser.test.ts @@ -0,0 +1,19 @@ +import { fetchOrThrow } from '../../../src'; + +describe('fetchOrThrow', () => { + + it('404, throws', async () => { + const url = '/does/not/exist' + + let err = '' + try { + await fetchOrThrow(url) + } catch (e) { + err = e.toString() + } + + expect(err).toContain('failed to fetch: (404)') + expect(err).toContain(url) + }) + +}) diff --git a/test/tests/dom/toNetInput.test.ts b/test/tests/dom/toNetInput.test.ts new file mode 100644 index 00000000..77e65420 --- /dev/null +++ b/test/tests/dom/toNetInput.test.ts @@ -0,0 +1,115 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { createCanvasFromMedia, env, NetInput, toNetInput } from '../../../src'; +import { getTestEnv } from '../../env'; +import { expectAllTensorsReleased } from '../../utils'; + + +describe('toNetInput', () => { + + let imgEl: HTMLImageElement, canvasEl: HTMLCanvasElement + + beforeAll(async () => { + imgEl = await getTestEnv().loadImage('test/images/white.png') + canvasEl = createCanvasFromMedia(imgEl) + }) + + describe('valid args', () => { + + it('from HTMLImageElement', async () => { + const netInput = await toNetInput(imgEl) + expect(netInput instanceof NetInput).toBe(true) + expect(netInput.batchSize).toEqual(1) + }) + + it('from HTMLCanvasElement', async () => { + const netInput = await toNetInput(canvasEl) + expect(netInput instanceof NetInput).toBe(true) + expect(netInput.batchSize).toEqual(1) + }) + + it('from HTMLImageElement array', async () => { + const netInput = await toNetInput([ + imgEl, + imgEl + ]) + expect(netInput instanceof NetInput).toBe(true) + expect(netInput.batchSize).toEqual(2) + }) + + it('from HTMLCanvasElement array', async () => { + const netInput = await toNetInput([ + canvasEl, + canvasEl + ]) + expect(netInput instanceof NetInput).toBe(true) + expect(netInput.batchSize).toEqual(2) + }) + + it('from mixed media array', async () => { + const netInput = await toNetInput([ + imgEl, + canvasEl, + canvasEl + ]) + expect(netInput instanceof NetInput).toBe(true) + expect(netInput.batchSize).toEqual(3) + }) + + }) + + describe('invalid args', () => { + it('undefined', async () => { + let errorMessage + try { + await toNetInput(undefined as any) + } catch (error) { + errorMessage = error.message; + } + expect(errorMessage).toBe('toNetInput - expected media to be of type HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | tf.Tensor3D, or to be an element id') + }) + + it('empty array', async () => { + let errorMessage + try { + await toNetInput([]) + } catch (error) { + errorMessage = error.message; + } + expect(errorMessage).toBe('toNetInput - empty array passed as input') + }) + + it('undefined at input index 1', async () => { + let errorMessage + try { + await toNetInput([env.getEnv().createImageElement(), undefined] as any) + } catch (error) { + errorMessage = error.message; + } + expect(errorMessage).toBe('toNetInput - at input index 1: expected media to be of type HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | tf.Tensor3D, or to be an element id') + }) + + }) + + describe('no memory leaks', () => { + + it('constructor', async () => { + const tensors = [imgEl, imgEl, imgEl].map(el => tf.browser.fromPixels(createCanvasFromMedia(el))) + const tensor4ds = tensors.map(t => t.expandDims()) + + await expectAllTensorsReleased(async () => { + await toNetInput(imgEl) + await toNetInput([imgEl, imgEl, imgEl]) + await toNetInput(tensors[0]) + await toNetInput(tensors) + await toNetInput(tensor4ds[0]) + await toNetInput(tensor4ds) + }) + + tensors.forEach(t => t.dispose()) + tensor4ds.forEach(t => t.dispose()) + }) + + }) + +}) diff --git a/test/tests/faceLandmarkNet/faceLandmark68Net.test.ts b/test/tests/faceLandmarkNet/faceLandmark68Net.test.ts index 161ab958..e14acdac 100644 --- a/test/tests/faceLandmarkNet/faceLandmark68Net.test.ts +++ b/test/tests/faceLandmarkNet/faceLandmark68Net.test.ts @@ -1,7 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { createCanvasFromMedia, IDimensions, isTensor3D, NetInput, Point, TMediaElement, toNetInput } from '../../../src'; -import { FaceLandmarks68 } from '../../../src/classes/FaceLandmarks68'; +import { createCanvasFromMedia, IDimensions, FaceLandmarks68, utils, NetInput, Point, TMediaElement, toNetInput } from '../../../src'; import { getTestEnv } from '../../env'; import { describeWithBackend, @@ -13,7 +12,7 @@ import { function getInputDims (input: tf.Tensor | TMediaElement): IDimensions { if (input instanceof tf.Tensor) { - const [height, width] = input.shape.slice(isTensor3D(input) ? 0 : 1) + const [height, width] = input.shape.slice(utils.isTensor3D(input) ? 0 : 1) return { width, height } } return input diff --git a/test/tests/faceLandmarkNet/faceLandmark68Net.uncompressed.test.ts b/test/tests/faceLandmarkNet/faceLandmark68Net.uncompressed.test.ts index f5c30526..cd635cbe 100644 --- a/test/tests/faceLandmarkNet/faceLandmark68Net.uncompressed.test.ts +++ b/test/tests/faceLandmarkNet/faceLandmark68Net.uncompressed.test.ts @@ -1,5 +1,4 @@ -import { Point } from '../../../src'; -import { FaceLandmarks68 } from '../../../src/classes/FaceLandmarks68'; +import { FaceLandmarks68, Point } from '../../../src'; import { getTestEnv } from '../../env'; import { describeWithBackend, describeWithNets, expectPointClose } from '../../utils'; diff --git a/test/tests/faceLandmarkNet/faceLandmark68TinyNet.test.ts b/test/tests/faceLandmarkNet/faceLandmark68TinyNet.test.ts index 51277452..c7bc4799 100644 --- a/test/tests/faceLandmarkNet/faceLandmark68TinyNet.test.ts +++ b/test/tests/faceLandmarkNet/faceLandmark68TinyNet.test.ts @@ -1,13 +1,12 @@ import * as tf from '@tensorflow/tfjs-core'; -import { createCanvasFromMedia, IDimensions, isTensor3D, NetInput, Point, TMediaElement, toNetInput } from '../../../src'; -import { FaceLandmarks68 } from '../../../src/classes/FaceLandmarks68'; +import { createCanvasFromMedia, IDimensions, utils, NetInput, Point, TMediaElement, toNetInput, FaceLandmarks68 } from '../../../src'; import { getTestEnv } from '../../env'; import { describeWithBackend, describeWithNets, expectAllTensorsReleased, expectPointClose } from '../../utils'; function getInputDims (input: tf.Tensor | TMediaElement): IDimensions { if (input instanceof tf.Tensor) { - const [height, width] = input.shape.slice(isTensor3D(input) ? 0 : 1) + const [height, width] = input.shape.slice(utils.isTensor3D(input) ? 0 : 1) return { width, height } } return input diff --git a/test/tests/faceRecognitionNet/faceRecognitionNet.test.ts b/test/tests/faceRecognitionNet/faceRecognitionNet.test.ts index 1b0c613f..71aa0549 100644 --- a/test/tests/faceRecognitionNet/faceRecognitionNet.test.ts +++ b/test/tests/faceRecognitionNet/faceRecognitionNet.test.ts @@ -1,7 +1,6 @@ import * as tf from '@tensorflow/tfjs-core'; -import { createCanvasFromMedia, NetInput, toNetInput } from '../../../src'; -import { euclideanDistance } from '../../../src/euclideanDistance'; +import { createCanvasFromMedia, euclideanDistance, NetInput, toNetInput } from '../../../src'; import { getTestEnv } from '../../env'; import { describeWithBackend, describeWithNets, expectAllTensorsReleased } from '../../utils'; diff --git a/test/tests/faceRecognitionNet/faceRecognitionNet.uncompressed.test.ts b/test/tests/faceRecognitionNet/faceRecognitionNet.uncompressed.test.ts index 7e26c4ff..c1093a24 100644 --- a/test/tests/faceRecognitionNet/faceRecognitionNet.uncompressed.test.ts +++ b/test/tests/faceRecognitionNet/faceRecognitionNet.uncompressed.test.ts @@ -1,5 +1,4 @@ -import { createCanvasFromMedia } from '../../../src'; -import { euclideanDistance } from '../../../src/euclideanDistance'; +import { createCanvasFromMedia, euclideanDistance } from '../../../src'; import { getTestEnv } from '../../env'; import { describeWithBackend, describeWithNets } from '../../utils'; diff --git a/test/tests/factories/WithFaceDetection.test.ts b/test/tests/factories/WithFaceDetection.test.ts index 002c3979..b12e3181 100644 --- a/test/tests/factories/WithFaceDetection.test.ts +++ b/test/tests/factories/WithFaceDetection.test.ts @@ -1,6 +1,4 @@ -import { Rect } from '../../../src'; -import { FaceDetection } from '../../../src/classes/FaceDetection'; -import { extendWithFaceDetection } from '../../../src/factories/WithFaceDetection'; +import { extendWithFaceDetection, FaceDetection, Rect } from '../../../src'; const detection = new FaceDetection(1.0, new Rect(0, 0, 0.5, 0.5), { width: 100, height: 100 }) diff --git a/test/tests/factories/WithFaceLandmarks.test.ts b/test/tests/factories/WithFaceLandmarks.test.ts index 7be25ef9..125f0b6d 100644 --- a/test/tests/factories/WithFaceLandmarks.test.ts +++ b/test/tests/factories/WithFaceLandmarks.test.ts @@ -1,8 +1,4 @@ -import { Point, Rect } from '../../../src'; -import { FaceDetection } from '../../../src/classes/FaceDetection'; -import { FaceLandmarks68 } from '../../../src/classes/FaceLandmarks68'; -import { extendWithFaceDetection } from '../../../src/factories/WithFaceDetection'; -import { extendWithFaceLandmarks } from '../../../src/factories/WithFaceLandmarks'; +import { extendWithFaceDetection, extendWithFaceLandmarks, FaceDetection, FaceLandmarks68, Point, Rect } from '../../../src'; const detection = new FaceDetection(1.0, new Rect(0.5, 0.5, 0.5, 0.5), { width: 100, height: 100 }) const unshiftedLandmarks = new FaceLandmarks68(Array(68).fill(0).map((_, i) => new Point(i / 100, i / 100)), { width: 100, height: 100 }) diff --git a/test/tests/globalApi/FaceMatcher.test.ts b/test/tests/globalApi/FaceMatcher.test.ts index a58df204..290348ca 100644 --- a/test/tests/globalApi/FaceMatcher.test.ts +++ b/test/tests/globalApi/FaceMatcher.test.ts @@ -1,4 +1,4 @@ -import { FaceMatcher } from '../../../src/globalApi/FaceMatcher'; +import { FaceMatcher } from '../../../src'; import { LabeledFaceDescriptors } from '../../../src'; describe('globalApi', () => { diff --git a/test/tests/globalApi/detectAllFaces.test.ts b/test/tests/globalApi/detectAllFaces.test.ts index 67528cdc..f335fb41 100644 --- a/test/tests/globalApi/detectAllFaces.test.ts +++ b/test/tests/globalApi/detectAllFaces.test.ts @@ -1,8 +1,4 @@ -import * as faceapi from '../../../src'; -import { WithAge } from '../../../src/factories/WithAge'; -import { WithFaceDetection } from '../../../src/factories/WithFaceDetection'; -import { WithFaceExpressions } from '../../../src/factories/WithFaceExpressions'; -import { WithGender } from '../../../src/factories/WithGender'; +import { WithAge, detectAllFaces, WithFaceExpressions, WithFaceDetection, WithGender } from '../../../src'; import { getTestEnv } from '../../env'; import { expectedTinyFaceDetectorBoxes } from '../../expectedTinyFaceDetectorBoxes'; import { expectFaceDetectionsWithLandmarks } from '../../expectFaceDetectionsWithLandmarks'; @@ -58,8 +54,7 @@ describeWithBackend('globalApi', () => { describe('without face alignment', () => { it('detectAllFaces.withFaceExpressions()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withFaceExpressions() expect(results.length).toEqual(6) @@ -67,8 +62,7 @@ describeWithBackend('globalApi', () => { }) it('detectAllFaces.withAgeAndGender()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withAgeAndGender() expect(results.length).toEqual(6) @@ -76,8 +70,7 @@ describeWithBackend('globalApi', () => { }) it('detectAllFaces.withFaceExpressions().withAgeAndGender()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withFaceExpressions() .withAgeAndGender() @@ -87,8 +80,7 @@ describeWithBackend('globalApi', () => { }) it('detectAllFaces.withAgeAndGender().withFaceExpressions()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withAgeAndGender() .withFaceExpressions() @@ -102,8 +94,7 @@ describeWithBackend('globalApi', () => { describe('with face alignment', () => { it('detectAllFaces.withFaceLandmarks().withFaceExpressions()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceExpressions() @@ -113,8 +104,7 @@ describeWithBackend('globalApi', () => { }) it('detectAllFaces.withFaceLandmarks().withAgeAndGender()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withFaceLandmarks() .withAgeAndGender() @@ -124,8 +114,7 @@ describeWithBackend('globalApi', () => { }) it('detectAllFaces.withFaceLandmarks().withFaceDescriptors()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceDescriptors() @@ -134,8 +123,7 @@ describeWithBackend('globalApi', () => { }) it('detectAllFaces.withFaceLandmarks().withFaceExpressions().withAgeAndGender()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceExpressions() .withAgeAndGender() @@ -147,8 +135,7 @@ describeWithBackend('globalApi', () => { }) it('detectAllFaces.withFaceLandmarks().withAgeAndGender().withFaceExpressions()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withFaceLandmarks() .withAgeAndGender() .withFaceExpressions() @@ -160,8 +147,7 @@ describeWithBackend('globalApi', () => { }) it('detectAllFaces.withFaceLandmarks().withFaceExpressions().withFaceDescriptors()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceExpressions() .withFaceDescriptors() @@ -172,8 +158,7 @@ describeWithBackend('globalApi', () => { }) it('detectAllFaces.withFaceLandmarks().withAgeAndGender().withFaceDescriptors()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withFaceLandmarks() .withAgeAndGender() .withFaceDescriptors() @@ -184,8 +169,7 @@ describeWithBackend('globalApi', () => { }) it('detectAllFaces.withFaceLandmarks().withFaceExpressions().withAgeAndGender().withFaceDescriptors()', async () => { - const results = await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + const results = await detectAllFaces(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceExpressions() .withAgeAndGender() @@ -203,8 +187,7 @@ describeWithBackend('globalApi', () => { it('detectAllFaces.withFaceLandmarks().withFaceDescriptors()', async () => { await expectAllTensorsReleased(async () => { - await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + await detectAllFaces(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceDescriptors() }) @@ -212,8 +195,7 @@ describeWithBackend('globalApi', () => { it('detectAllFaces.withFaceLandmarks().withFaceExpressions().withAgeAndGender().withFaceDescriptors()', async () => { await expectAllTensorsReleased(async () => { - await faceapi - .detectAllFaces(imgEl, faceDetectorOptions) + await detectAllFaces(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceExpressions() .withAgeAndGender() diff --git a/test/tests/globalApi/detectSingleFace.test.ts b/test/tests/globalApi/detectSingleFace.test.ts index 8a209a4e..a15c12c3 100644 --- a/test/tests/globalApi/detectSingleFace.test.ts +++ b/test/tests/globalApi/detectSingleFace.test.ts @@ -1,8 +1,12 @@ -import * as faceapi from '../../../src'; -import { WithAge } from '../../../src/factories/WithAge'; -import { WithFaceExpressions } from '../../../src/factories/WithFaceExpressions'; -import { WithGender } from '../../../src/factories/WithGender'; -import { getTestEnv } from '../../env'; +import { + detectSingleFace, + WithAge, + WithFaceDescriptor, + WithFaceDetection, + WithFaceExpressions, + WithFaceLandmarks, + WithGender, +} from '../../../src'; import { expectedTinyFaceDetectorBoxes } from '../../expectedTinyFaceDetectorBoxes'; import { expectFaceDetectionsWithLandmarks } from '../../expectFaceDetectionsWithLandmarks'; import { expectFullFaceDescriptions } from '../../expectFullFaceDescriptions'; @@ -14,6 +18,7 @@ import { ExpectedFullFaceDescription, } from '../../utils'; import { deltas, expectedScores, faceDetectorOptions, withNetArgs } from './consts'; +import { getTestEnv } from '../../env'; function expectFaceExpressions(result: WithFaceExpressions<{}> | undefined) { @@ -44,7 +49,7 @@ describeWithBackend('globalApi', () => { expectedFullFaceDescriptions = await assembleExpectedFullFaceDescriptions(expectedTinyFaceDetectorBoxes) }) - function expectFaceDetectionWithLandmarks(result: faceapi.WithFaceLandmarks> | undefined) { + function expectFaceDetectionWithLandmarks(result: WithFaceLandmarks> | undefined) { expect(!!result).toBeTruthy() if (result) { expectFaceDetectionsWithLandmarks( @@ -56,7 +61,7 @@ describeWithBackend('globalApi', () => { } } - function expectFullFaceDescription(result: faceapi.WithFaceDescriptor>> | undefined) { + function expectFullFaceDescription(result: WithFaceDescriptor>> | undefined) { expect(!!result).toBeTruthy() if (result) { expectFullFaceDescriptions( @@ -73,24 +78,21 @@ describeWithBackend('globalApi', () => { describe('without face alignment', () => { it('detectSingleFace.withFaceExpressions()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withFaceExpressions() expectFaceExpressions(result) }) it('detectSingleFace.withAgeAndGender()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withAgeAndGender() expectAgeAndGender(result, false) }) it('detectSingleFace.withFaceExpressions().withAgeAndGender()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withFaceExpressions() .withAgeAndGender() @@ -99,8 +101,7 @@ describeWithBackend('globalApi', () => { }) it('detectSingleFace.withAgeAndGender().withFaceExpressions()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withAgeAndGender() .withFaceExpressions() @@ -113,8 +114,7 @@ describeWithBackend('globalApi', () => { describe('with face alignment', () => { it('detectSingleFace.withFaceLandmarks().withFaceExpressions()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceExpressions() @@ -123,8 +123,7 @@ describeWithBackend('globalApi', () => { }) it('detectSingleFace.withFaceLandmarks().withAgeAndGender()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withFaceLandmarks() .withAgeAndGender() @@ -133,8 +132,7 @@ describeWithBackend('globalApi', () => { }) it('detectSingleFace.withFaceLandmarks().withFaceDescriptor()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceDescriptor() @@ -142,8 +140,7 @@ describeWithBackend('globalApi', () => { }) it('detectSingleFace.withFaceLandmarks().withFaceExpressions().withAgeAndGender()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceExpressions() .withAgeAndGender() @@ -154,8 +151,7 @@ describeWithBackend('globalApi', () => { }) it('detectSingleFace.withFaceLandmarks().withAgeAndGender().withFaceExpressions()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withFaceLandmarks() .withAgeAndGender() .withFaceExpressions() @@ -166,8 +162,7 @@ describeWithBackend('globalApi', () => { }) it('detectSingleFace.withFaceLandmarks().withFaceExpressions().withFaceDescriptor()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceExpressions() .withFaceDescriptor() @@ -177,8 +172,7 @@ describeWithBackend('globalApi', () => { }) it('detectSingleFace.withFaceLandmarks().withAgeAndGender().withFaceDescriptor()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withFaceLandmarks() .withAgeAndGender() .withFaceDescriptor() @@ -188,8 +182,7 @@ describeWithBackend('globalApi', () => { }) it('detectSingleFace.withFaceLandmarks().withFaceExpressions().withAgeAndGender().withFaceDescriptor()', async () => { - const result = await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + const result = await detectSingleFace(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceExpressions() .withAgeAndGender() @@ -206,8 +199,7 @@ describeWithBackend('globalApi', () => { it('detectSingleFace.withFaceLandmarks().withFaceDescriptor()', async () => { await expectAllTensorsReleased(async () => { - await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + await detectSingleFace(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceDescriptor() }) @@ -215,8 +207,7 @@ describeWithBackend('globalApi', () => { it('detectSingleFace.withFaceLandmarks().withFaceExpressions().withAgeAndGender().withFaceDescriptor()', async () => { await expectAllTensorsReleased(async () => { - await faceapi - .detectSingleFace(imgEl, faceDetectorOptions) + await detectSingleFace(imgEl, faceDetectorOptions) .withFaceLandmarks() .withFaceExpressions() .withAgeAndGender() diff --git a/test/tests/ops/iou.test.ts b/test/tests/ops/iou.test.ts new file mode 100644 index 00000000..eb4229cb --- /dev/null +++ b/test/tests/ops/iou.test.ts @@ -0,0 +1,42 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { iou, Rect } from '../../../src'; + +describe('iou', () => { + + it('should be 1.0', () => tf.tidy(() => { + + const box = new Rect(0, 0, 20, 20) + + expect(iou(box, box)).toEqual(1) + + })) + + it('should be 0', () => tf.tidy(() => { + + const box1 = new Rect(0, 0, 20, 20) + const box2 = new Rect(20, 20, 20, 20) + + expect(iou(box1, box2)).toEqual(0) + + })) + + it('should be 0.5', () => tf.tidy(() => { + + const box1 = new Rect(0, 0, 20, 20) + const box2 = new Rect(0, 0, 10, 20) + + expect(iou(box1, box2)).toEqual(0.5) + + })) + + it('should be 0.5', () => tf.tidy(() => { + + const box1 = new Rect(0, 0, 20, 20) + const box2 = new Rect(0, 10, 20, 10) + + expect(iou(box1, box2)).toEqual(0.5) + + })) + +}) \ No newline at end of file diff --git a/test/tests/ops/padToSquare.test.ts b/test/tests/ops/padToSquare.test.ts new file mode 100644 index 00000000..50dd6f03 --- /dev/null +++ b/test/tests/ops/padToSquare.test.ts @@ -0,0 +1,131 @@ +import * as tf from '@tensorflow/tfjs-core'; + +import { padToSquare } from '../../../src'; +import { ones, zeros } from '../../utils'; + +describe('padToSquare', () => { + + describe('even size', () => { + + it('is padded to square by 2 columns', () => tf.tidy(() => { + const imgTensor = tf.tensor4d(Array(24).fill(1), [1, 4, 2, 3]) + const result = padToSquare(imgTensor) + + expect(result.shape).toEqual([1, 4, 4, 3]) + + const paddedCols = tf.unstack(result, 2) + expect(paddedCols.length).toEqual(4) + expect(paddedCols[0].dataSync()).toEqual(ones(12)) + expect(paddedCols[1].dataSync()).toEqual(ones(12)) + expect(paddedCols[2].dataSync()).toEqual(zeros(12)) + expect(paddedCols[3].dataSync()).toEqual(zeros(12)) + })) + + it('is padded to square by 2 columns and centered', () => tf.tidy(() => { + const imgTensor = tf.tensor4d(Array(24).fill(1), [1, 4, 2, 3]) + const result = padToSquare(imgTensor, true) + + expect(result.shape).toEqual([1, 4, 4, 3]) + + const paddedCols = tf.unstack(result, 2) + expect(paddedCols.length).toEqual(4) + expect(paddedCols[0].dataSync()).toEqual(zeros(12)) + expect(paddedCols[1].dataSync()).toEqual(ones(12)) + expect(paddedCols[2].dataSync()).toEqual(ones(12)) + expect(paddedCols[3].dataSync()).toEqual(zeros(12)) + })) + + it('is padded to square by 1 column', () => tf.tidy(() => { + const imgTensor = tf.tensor4d(Array(36).fill(1), [1, 4, 3, 3]) + const result = padToSquare(imgTensor) + + expect(result.shape).toEqual([1, 4, 4, 3]) + + const paddedCols = tf.unstack(result, 2) + expect(paddedCols.length).toEqual(4) + expect(paddedCols[0].dataSync()).toEqual(ones(12)) + expect(paddedCols[1].dataSync()).toEqual(ones(12)) + expect(paddedCols[2].dataSync()).toEqual(ones(12)) + expect(paddedCols[3].dataSync()).toEqual(zeros(12)) + })) + + it('is padded to square by 1 column and centered', () => tf.tidy(() => { + const imgTensor = tf.tensor4d(Array(36).fill(1), [1, 4, 3, 3]) + const result = padToSquare(imgTensor, true) + + expect(result.shape).toEqual([1, 4, 4, 3]) + + const paddedCols = tf.unstack(result, 2) + expect(paddedCols.length).toEqual(4) + expect(paddedCols[0].dataSync()).toEqual(ones(12)) + expect(paddedCols[1].dataSync()).toEqual(ones(12)) + expect(paddedCols[2].dataSync()).toEqual(ones(12)) + expect(paddedCols[3].dataSync()).toEqual(zeros(12)) + })) + + }) + + describe('uneven size', () => { + + it('is padded to square by 3 columns', () => tf.tidy(() => { + const imgTensor = tf.tensor4d(Array(30).fill(1), [1, 5, 2, 3]) + const result = padToSquare(imgTensor) + + expect(result.shape).toEqual([1, 5, 5, 3]) + + const paddedCols = tf.unstack(result, 2) + expect(paddedCols.length).toEqual(5) + expect(paddedCols[0].dataSync()).toEqual(ones(15)) + expect(paddedCols[1].dataSync()).toEqual(ones(15)) + expect(paddedCols[2].dataSync()).toEqual(zeros(15)) + expect(paddedCols[3].dataSync()).toEqual(zeros(15)) + expect(paddedCols[4].dataSync()).toEqual(zeros(15)) + })) + + it('is padded to square by 3 columns and centered', () => tf.tidy(() => { + const imgTensor = tf.tensor4d(Array(30).fill(1), [1, 5, 2, 3]) + const result = padToSquare(imgTensor, true) + + expect(result.shape).toEqual([1, 5, 5, 3]) + + const paddedCols = tf.unstack(result, 2) + expect(paddedCols.length).toEqual(5) + expect(paddedCols[0].dataSync()).toEqual(zeros(15)) + expect(paddedCols[1].dataSync()).toEqual(ones(15)) + expect(paddedCols[2].dataSync()).toEqual(ones(15)) + expect(paddedCols[3].dataSync()).toEqual(zeros(15)) + expect(paddedCols[4].dataSync()).toEqual(zeros(15)) + })) + + it('is padded to square by 1 column', () => tf.tidy(() => { + const imgTensor = tf.tensor4d(Array(60).fill(1), [1, 5, 4, 3]) + const result = padToSquare(imgTensor) + + expect(result.shape).toEqual([1, 5, 5, 3]) + + const paddedCols = tf.unstack(result, 2) + expect(paddedCols.length).toEqual(5) + expect(paddedCols[0].dataSync()).toEqual(ones(15)) + expect(paddedCols[1].dataSync()).toEqual(ones(15)) + expect(paddedCols[2].dataSync()).toEqual(ones(15)) + expect(paddedCols[3].dataSync()).toEqual(ones(15)) + expect(paddedCols[4].dataSync()).toEqual(zeros(15)) + })) + + it('is padded to square by 1 column and centered', () => tf.tidy(() => { + const imgTensor = tf.tensor4d(Array(60).fill(1), [1, 5, 4, 3]) + const result = padToSquare(imgTensor, true) + + expect(result.shape).toEqual([1, 5, 5, 3]) + + const paddedCols = tf.unstack(result, 2) + expect(paddedCols.length).toEqual(5) + expect(paddedCols[0].dataSync()).toEqual(ones(15)) + expect(paddedCols[1].dataSync()).toEqual(ones(15)) + expect(paddedCols[2].dataSync()).toEqual(ones(15)) + expect(paddedCols[3].dataSync()).toEqual(ones(15)) + expect(paddedCols[4].dataSync()).toEqual(zeros(15)) + })) + + }) +}) diff --git a/test/tests/resizeResults.test.ts b/test/tests/resizeResults.test.ts index 6e42fb19..a52bbc65 100644 --- a/test/tests/resizeResults.test.ts +++ b/test/tests/resizeResults.test.ts @@ -1,11 +1,12 @@ -import { Point } from 'tfjs-image-recognition-base'; - -import { Rect } from '../../src'; -import { FaceDetection } from '../../src/classes/FaceDetection'; -import { FaceLandmarks68 } from '../../src/classes/FaceLandmarks68'; -import { extendWithFaceDetection } from '../../src/factories/WithFaceDetection'; -import { extendWithFaceLandmarks } from '../../src/factories/WithFaceLandmarks'; -import { resizeResults } from '../../src/resizeResults'; +import { + extendWithFaceDetection, + extendWithFaceLandmarks, + FaceDetection, + FaceLandmarks68, + Point, + Rect, + resizeResults, +} from '../../src'; import { expectPointsClose, expectRectClose } from '../utils'; const detection = new FaceDetection(1.0, new Rect(0, 0, 0.5, 0.5), { width: 100, height: 100 }) diff --git a/test/utils.ts b/test/utils.ts index ee6ddd7c..5430de96 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -1,15 +1,23 @@ import * as tf from '@tensorflow/tfjs-core'; -import * as faceapi from '../src'; -import { FaceRecognitionNet, IPoint, IRect, Mtcnn, TinyYolov2 } from '../src/'; -import { AgeGenderNet } from '../src/ageGenderNet/AgeGenderNet'; -import { FaceDetection } from '../src/classes/FaceDetection'; -import { FaceLandmarks } from '../src/classes/FaceLandmarks'; -import { FaceExpressionNet } from '../src/faceExpressionNet/FaceExpressionNet'; -import { FaceLandmark68Net } from '../src/faceLandmarkNet/FaceLandmark68Net'; -import { FaceLandmark68TinyNet } from '../src/faceLandmarkNet/FaceLandmark68TinyNet'; -import { SsdMobilenetv1 } from '../src/ssdMobilenetv1/SsdMobilenetv1'; -import { TinyFaceDetector } from '../src/tinyFaceDetector/TinyFaceDetector'; +import { + AgeGenderNet, + FaceDetection, + FaceExpressionNet, + FaceLandmark68Net, + FaceLandmark68TinyNet, + FaceLandmarks, + FaceRecognitionNet, + IPoint, + IRect, + LabeledBox, + Mtcnn, + nets, + PredictedBox, + SsdMobilenetv1, + TinyFaceDetector, + TinyYolov2, +} from '../src'; import { getTestEnv } from './env'; export function expectMaxDelta(val1: number, val2: number, maxDelta: number) { @@ -80,6 +88,41 @@ export function sortByFaceDetection(objs return sortByDistanceToOrigin(objs, d => d.detection.box) } +export function fakeTensor3d(dtype: tf.DataType = 'int32') { + return tf.tensor3d([0], [1, 1, 1], dtype) +} + +export function zeros(length: number): Float32Array { + return new Float32Array(length) +} + +export function ones(length: number): Float32Array { + return new Float32Array(length).fill(1) +} + +export function createLabeledBox( + x: number, + y: number, + width: number, + height: number, + classLabel: number = 0 +): LabeledBox { + return new LabeledBox({ x, y, width, height }, classLabel) +} + +export function createPredictedBox( + x: number, + y: number, + width: number, + height: number, + classLabel: number = 0, + score: number = 1.0, + classScore: number = 1.0 +): PredictedBox { + return new PredictedBox({ x, y, width, height }, classLabel, score, classScore) +} + + export type ExpectedFaceDetectionWithLandmarks = { detection: IRect landmarks: IPoint[] @@ -191,7 +234,7 @@ export function describeWithNets( faceExpressionNet, ageGenderNet, tinyYolov2 - } = faceapi.nets + } = nets beforeAll(async () => { const { diff --git a/test/utils/index.test.ts b/test/utils/index.test.ts new file mode 100644 index 00000000..03ee36bf --- /dev/null +++ b/test/utils/index.test.ts @@ -0,0 +1,40 @@ +import { utils } from '../../src'; + +describe('utils', () => { + + describe('isValidNumber', () => { + + it('0 is valid', () => { + expect(utils.isValidNumber(0)).toBe(true) + }) + + it('1 is valid', () => { + expect(utils.isValidNumber(1)).toBe(true) + }) + + it('-1 is valid', () => { + expect(utils.isValidNumber(-1)).toBe(true) + }) + + it('NaN is invalid', () => { + expect(utils.isValidNumber(NaN)).toBe(false) + }) + + it('Infinity is invalid', () => { + expect(utils.isValidNumber(Infinity)).toBe(false) + }) + + it('-Infinity is invalid', () => { + expect(utils.isValidNumber(-Infinity)).toBe(false) + }) + + it('null is invalid', () => { + expect(utils.isValidNumber(null)).toBe(false) + }) + + it('undefined is invalid', () => { + expect(utils.isValidNumber(undefined)).toBe(false) + }) + + }) +}) From 5df8e27a87e97568c8b76181759669334b016f54 Mon Sep 17 00:00:00 2001 From: vincent Date: Sat, 14 Dec 2019 14:59:15 +0100 Subject: [PATCH 03/22] move mtcnn and uncompressed model tests to tests-legacy --- jasmine-node.js | 7 ++----- karma.conf.js | 11 ++--------- package.json | 8 +------- .../faceLandmark68Net.uncompressed.test.ts | 6 +++--- .../faceLandmark68TinyNet.uncompressed.test.ts | 8 ++++---- .../faceRecognitionNet.uncompressed.test.ts | 6 +++--- .../mtcnn/expectMtcnnResults.ts | 0 .../mtcnn/mtcnn.forward.test.ts | 0 .../mtcnn/mtcnn.forward.uncompressed.test.ts | 0 test/{tests => tests-legacy}/mtcnn/mtcnn.test.ts | 0 .../ssdMobilenetv1.locateFaces.uncompressed.test.ts | 10 +++++----- test/tests/dom/fetchNetWeights.browser.test.ts | 2 +- 12 files changed, 21 insertions(+), 37 deletions(-) rename test/{tests/faceLandmarkNet => tests-legacy}/faceLandmark68Net.uncompressed.test.ts (94%) rename test/{tests/faceLandmarkNet => tests-legacy}/faceLandmark68TinyNet.uncompressed.test.ts (91%) rename test/{tests/faceRecognitionNet => tests-legacy}/faceRecognitionNet.uncompressed.test.ts (92%) rename test/{tests => tests-legacy}/mtcnn/expectMtcnnResults.ts (100%) rename test/{tests => tests-legacy}/mtcnn/mtcnn.forward.test.ts (100%) rename test/{tests => tests-legacy}/mtcnn/mtcnn.forward.uncompressed.test.ts (100%) rename test/{tests => tests-legacy}/mtcnn/mtcnn.test.ts (100%) rename test/{tests/ssdMobilenetv1 => tests-legacy}/ssdMobilenetv1.locateFaces.uncompressed.test.ts (81%) diff --git a/jasmine-node.js b/jasmine-node.js index dda5301b..e4dc184e 100644 --- a/jasmine-node.js +++ b/jasmine-node.js @@ -1,11 +1,8 @@ -let spec_files = ['**/*.test.ts'].concat( - process.env.EXCLUDE_UNCOMPRESSED - ? ['!**/*.uncompressed.test.ts'] - : [] -) +let spec_files = ['**/*.test.ts'] // exclude browser tests spec_files = spec_files.concat(['!**/*.browser.test.ts']) +spec_files = spec_files.concat(['!test/tests.legacy/*']) module.exports = { spec_dir: 'test', diff --git a/karma.conf.js b/karma.conf.js index 01364c6f..2fc96fef 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -22,24 +22,17 @@ let exclude = ( 'faceLandmarkNet', 'faceRecognitionNet', 'ssdMobilenetv1', - 'tinyFaceDetector', - 'mtcnn' + 'tinyFaceDetector' ] : [] ) .filter(ex => ex !== process.env.UUT) .map(ex => `test/tests/${ex}/*.ts`) - -exclude = exclude.concat( - process.env.EXCLUDE_UNCOMPRESSED - ? ['**/*.uncompressed.test.ts'] - : [] -) - // exclude nodejs tests exclude = exclude.concat(['**/*.node.test.ts']) exclude = exclude.concat(['test/env.node.ts']) +exclude = exclude.concat(['test/tests-legacy/**/*.ts']) module.exports = function(config) { diff --git a/package.json b/package.json index 32d96e3a..72ca5606 100644 --- a/package.json +++ b/package.json @@ -11,22 +11,16 @@ "tsc": "tsc", "tsc-es6": "tsc --p tsconfig.es6.json", "build": "rm -rf ./build && rm -rf ./dist && npm run rollup && npm run rollup-min && npm run tsc && npm run tsc-es6", - "test": "karma start", + "test": "npm run test-browser && npm run test-node", "test-browser": "karma start --single-run", "test-node": "ts-node -r ./test/env.node.ts node_modules/jasmine/bin/jasmine --config=jasmine-node.js", - "test-all": "npm run test-browser-exclude-uncompressed && npm run test-node-exclude-uncompressed", - "test-all-include-uncompressed": "npm run test-browser && npm run test-node", "test-facelandmarknets": "set UUT=faceLandmarkNet&& karma start", "test-facerecognitionnet": "set UUT=faceRecognitionNet&& karma start", "test-agegendernet": "set UUT=ageGenderNet&& karma start", "test-ssdmobilenetv1": "set UUT=ssdMobilenetv1&& karma start", "test-tinyfacedetector": "set UUT=tinyFaceDetector&& karma start", "test-globalapi": "set UUT=globalApi&& karma start", - "test-mtcnn": "set UUT=mtcnn&& karma start", "test-cpu": "set BACKEND_CPU=true&& karma start", - "test-exclude-uncompressed": "set EXCLUDE_UNCOMPRESSED=true&& karma start", - "test-browser-exclude-uncompressed": "set EXCLUDE_UNCOMPRESSED=true&& karma start --single-run", - "test-node-exclude-uncompressed": "set EXCLUDE_UNCOMPRESSED=true&& npm run test-node", "docs": "typedoc --options ./typedoc.config.js ./src" }, "keywords": [ diff --git a/test/tests/faceLandmarkNet/faceLandmark68Net.uncompressed.test.ts b/test/tests-legacy/faceLandmark68Net.uncompressed.test.ts similarity index 94% rename from test/tests/faceLandmarkNet/faceLandmark68Net.uncompressed.test.ts rename to test/tests-legacy/faceLandmark68Net.uncompressed.test.ts index cd635cbe..81ad26ee 100644 --- a/test/tests/faceLandmarkNet/faceLandmark68Net.uncompressed.test.ts +++ b/test/tests-legacy/faceLandmark68Net.uncompressed.test.ts @@ -1,6 +1,6 @@ -import { FaceLandmarks68, Point } from '../../../src'; -import { getTestEnv } from '../../env'; -import { describeWithBackend, describeWithNets, expectPointClose } from '../../utils'; +import { FaceLandmarks68, Point } from '../../src'; +import { getTestEnv } from '../env'; +import { describeWithBackend, describeWithNets, expectPointClose } from '../utils'; describeWithBackend('faceLandmark68Net, uncompressed', () => { diff --git a/test/tests/faceLandmarkNet/faceLandmark68TinyNet.uncompressed.test.ts b/test/tests-legacy/faceLandmark68TinyNet.uncompressed.test.ts similarity index 91% rename from test/tests/faceLandmarkNet/faceLandmark68TinyNet.uncompressed.test.ts rename to test/tests-legacy/faceLandmark68TinyNet.uncompressed.test.ts index 2913539e..39d33d0a 100644 --- a/test/tests/faceLandmarkNet/faceLandmark68TinyNet.uncompressed.test.ts +++ b/test/tests-legacy/faceLandmark68TinyNet.uncompressed.test.ts @@ -1,7 +1,7 @@ -import { Point } from '../../../src'; -import { FaceLandmarks68 } from '../../../src/classes/FaceLandmarks68'; -import { getTestEnv } from '../../env'; -import { describeWithBackend, describeWithNets, expectPointClose } from '../../utils'; +import { Point } from '../../src'; +import { FaceLandmarks68 } from '../../src/classes/FaceLandmarks68'; +import { getTestEnv } from '../env'; +import { describeWithBackend, describeWithNets, expectPointClose } from '../utils'; describeWithBackend('faceLandmark68TinyNet, uncompressed', () => { diff --git a/test/tests/faceRecognitionNet/faceRecognitionNet.uncompressed.test.ts b/test/tests-legacy/faceRecognitionNet.uncompressed.test.ts similarity index 92% rename from test/tests/faceRecognitionNet/faceRecognitionNet.uncompressed.test.ts rename to test/tests-legacy/faceRecognitionNet.uncompressed.test.ts index c1093a24..d550bdb1 100644 --- a/test/tests/faceRecognitionNet/faceRecognitionNet.uncompressed.test.ts +++ b/test/tests-legacy/faceRecognitionNet.uncompressed.test.ts @@ -1,6 +1,6 @@ -import { createCanvasFromMedia, euclideanDistance } from '../../../src'; -import { getTestEnv } from '../../env'; -import { describeWithBackend, describeWithNets } from '../../utils'; +import { createCanvasFromMedia, euclideanDistance } from '../../src'; +import { getTestEnv } from '../env'; +import { describeWithBackend, describeWithNets } from '../utils'; describeWithBackend('faceRecognitionNet, uncompressed', () => { diff --git a/test/tests/mtcnn/expectMtcnnResults.ts b/test/tests-legacy/mtcnn/expectMtcnnResults.ts similarity index 100% rename from test/tests/mtcnn/expectMtcnnResults.ts rename to test/tests-legacy/mtcnn/expectMtcnnResults.ts diff --git a/test/tests/mtcnn/mtcnn.forward.test.ts b/test/tests-legacy/mtcnn/mtcnn.forward.test.ts similarity index 100% rename from test/tests/mtcnn/mtcnn.forward.test.ts rename to test/tests-legacy/mtcnn/mtcnn.forward.test.ts diff --git a/test/tests/mtcnn/mtcnn.forward.uncompressed.test.ts b/test/tests-legacy/mtcnn/mtcnn.forward.uncompressed.test.ts similarity index 100% rename from test/tests/mtcnn/mtcnn.forward.uncompressed.test.ts rename to test/tests-legacy/mtcnn/mtcnn.forward.uncompressed.test.ts diff --git a/test/tests/mtcnn/mtcnn.test.ts b/test/tests-legacy/mtcnn/mtcnn.test.ts similarity index 100% rename from test/tests/mtcnn/mtcnn.test.ts rename to test/tests-legacy/mtcnn/mtcnn.test.ts diff --git a/test/tests/ssdMobilenetv1/ssdMobilenetv1.locateFaces.uncompressed.test.ts b/test/tests-legacy/ssdMobilenetv1.locateFaces.uncompressed.test.ts similarity index 81% rename from test/tests/ssdMobilenetv1/ssdMobilenetv1.locateFaces.uncompressed.test.ts rename to test/tests-legacy/ssdMobilenetv1.locateFaces.uncompressed.test.ts index 1176e268..afaa9f5d 100644 --- a/test/tests/ssdMobilenetv1/ssdMobilenetv1.locateFaces.uncompressed.test.ts +++ b/test/tests-legacy/ssdMobilenetv1.locateFaces.uncompressed.test.ts @@ -1,8 +1,8 @@ -import * as faceapi from '../../../src'; -import { getTestEnv } from '../../env'; -import { expectFaceDetections } from '../../expectFaceDetections'; -import { describeWithBackend, describeWithNets } from '../../utils'; -import { expectedSsdBoxes } from './expectedBoxes'; +import * as faceapi from '../../src'; +import { getTestEnv } from '../env'; +import { expectFaceDetections } from '../expectFaceDetections'; +import { describeWithBackend, describeWithNets } from '../utils'; +import { expectedSsdBoxes } from '../tests/ssdMobilenetv1/expectedBoxes'; describeWithBackend('ssdMobilenetv1.locateFaces, uncompressed', () => { diff --git a/test/tests/dom/fetchNetWeights.browser.test.ts b/test/tests/dom/fetchNetWeights.browser.test.ts index af7024ef..4fe82f50 100644 --- a/test/tests/dom/fetchNetWeights.browser.test.ts +++ b/test/tests/dom/fetchNetWeights.browser.test.ts @@ -3,7 +3,7 @@ import { fetchNetWeights } from '../../../src'; describe('fetchNetWeights', () => { it('fetches .weights file', async () => { - const url = 'test/data/dummy.weights' + const url = 'base/test/data/dummy.weights' const weights = await fetchNetWeights(url) expect(weights instanceof Float32Array).toBe(true) }) From 0b4470af97be267f05d5f86312c947e2cd65d8c6 Mon Sep 17 00:00:00 2001 From: vincent Date: Sat, 14 Dec 2019 17:36:25 +0100 Subject: [PATCH 04/22] faceapi.round -> faceapi.utils.round --- .../public/js/faceDetectionControls.js | 12 ++++++------ .../views/ageAndGenderRecognition.html | 4 ++-- examples/examples-browser/views/bbtFaceMatching.html | 2 +- .../examples-browser/views/bbtFaceSimilarity.html | 2 +- .../examples-browser/views/videoFaceTracking.html | 2 +- .../views/webcamAgeAndGenderRecognition.html | 6 +++--- .../examples-browser/views/webcamFaceDetection.html | 2 +- .../views/webcamFaceExpressionRecognition.html | 2 +- .../views/webcamFaceLandmarkDetection.html | 2 +- examples/examples-nodejs/ageAndGenderRecognition.ts | 4 ++-- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/examples-browser/public/js/faceDetectionControls.js b/examples/examples-browser/public/js/faceDetectionControls.js index 52cb9c5c..b23c18bb 100644 --- a/examples/examples-browser/public/js/faceDetectionControls.js +++ b/examples/examples-browser/public/js/faceDetectionControls.js @@ -26,13 +26,13 @@ function getFaceDetectorOptions() { } function onIncreaseMinConfidence() { - minConfidence = Math.min(faceapi.round(minConfidence + 0.1), 1.0) + minConfidence = Math.min(faceapi.utils.round(minConfidence + 0.1), 1.0) $('#minConfidence').val(minConfidence) updateResults() } function onDecreaseMinConfidence() { - minConfidence = Math.max(faceapi.round(minConfidence - 0.1), 0.1) + minConfidence = Math.max(faceapi.utils.round(minConfidence - 0.1), 0.1) $('#minConfidence').val(minConfidence) updateResults() } @@ -51,24 +51,24 @@ function changeInputSize(size) { } function onIncreaseScoreThreshold() { - scoreThreshold = Math.min(faceapi.round(scoreThreshold + 0.1), 1.0) + scoreThreshold = Math.min(faceapi.utils.round(scoreThreshold + 0.1), 1.0) $('#scoreThreshold').val(scoreThreshold) updateResults() } function onDecreaseScoreThreshold() { - scoreThreshold = Math.max(faceapi.round(scoreThreshold - 0.1), 0.1) + scoreThreshold = Math.max(faceapi.utils.round(scoreThreshold - 0.1), 0.1) $('#scoreThreshold').val(scoreThreshold) updateResults() } function onIncreaseMinFaceSize() { - minFaceSize = Math.min(faceapi.round(minFaceSize + 20), 300) + minFaceSize = Math.min(faceapi.utils.round(minFaceSize + 20), 300) $('#minFaceSize').val(minFaceSize) } function onDecreaseMinFaceSize() { - minFaceSize = Math.max(faceapi.round(minFaceSize - 20), 50) + minFaceSize = Math.max(faceapi.utils.round(minFaceSize - 20), 50) $('#minFaceSize').val(minFaceSize) } diff --git a/examples/examples-browser/views/ageAndGenderRecognition.html b/examples/examples-browser/views/ageAndGenderRecognition.html index bb70e7eb..63173e47 100644 --- a/examples/examples-browser/views/ageAndGenderRecognition.html +++ b/examples/examples-browser/views/ageAndGenderRecognition.html @@ -161,8 +161,8 @@ const { age, gender, genderProbability } = result new faceapi.draw.DrawTextField( [ - `${faceapi.round(age, 0)} years`, - `${gender} (${faceapi.round(genderProbability)})` + `${faceapi.utils.round(age, 0)} years`, + `${gender} (${faceapi.utils.round(genderProbability)})` ], result.detection.box.bottomLeft ).draw(canvas) diff --git a/examples/examples-browser/views/bbtFaceMatching.html b/examples/examples-browser/views/bbtFaceMatching.html index 458508b3..394decba 100644 --- a/examples/examples-browser/views/bbtFaceMatching.html +++ b/examples/examples-browser/views/bbtFaceMatching.html @@ -96,7 +96,7 @@ function displayTimeStats(timeInMs) { $('#time').val(`${timeInMs} ms`) - $('#fps').val(`${faceapi.round(1000 / timeInMs)}`) + $('#fps').val(`${faceapi.utils.round(1000 / timeInMs)}`) } function displayImage(src) { diff --git a/examples/examples-browser/views/bbtFaceSimilarity.html b/examples/examples-browser/views/bbtFaceSimilarity.html index 2ce768a0..e3114bca 100644 --- a/examples/examples-browser/views/bbtFaceSimilarity.html +++ b/examples/examples-browser/views/bbtFaceSimilarity.html @@ -39,7 +39,7 @@ let descriptors = { desc1: null, desc2: null } function updateResult() { - const distance = faceapi.round( + const distance = faceapi.utils.round( faceapi.euclideanDistance(descriptors.desc1, descriptors.desc2) ) let text = distance diff --git a/examples/examples-browser/views/videoFaceTracking.html b/examples/examples-browser/views/videoFaceTracking.html index 3050b8f5..1b38210e 100644 --- a/examples/examples-browser/views/videoFaceTracking.html +++ b/examples/examples-browser/views/videoFaceTracking.html @@ -156,7 +156,7 @@ forwardTimes = [timeInMs].concat(forwardTimes).slice(0, 30) const avgTimeInMs = forwardTimes.reduce((total, t) => total + t) / forwardTimes.length $('#time').val(`${Math.round(avgTimeInMs)} ms`) - $('#fps').val(`${faceapi.round(1000 / avgTimeInMs)}`) + $('#fps').val(`${faceapi.utils.round(1000 / avgTimeInMs)}`) } async function onPlay(videoEl) { diff --git a/examples/examples-browser/views/webcamAgeAndGenderRecognition.html b/examples/examples-browser/views/webcamAgeAndGenderRecognition.html index 06af23ac..8be7211e 100644 --- a/examples/examples-browser/views/webcamAgeAndGenderRecognition.html +++ b/examples/examples-browser/views/webcamAgeAndGenderRecognition.html @@ -152,7 +152,7 @@ forwardTimes = [timeInMs].concat(forwardTimes).slice(0, 30) const avgTimeInMs = forwardTimes.reduce((total, t) => total + t) / forwardTimes.length $('#time').val(`${Math.round(avgTimeInMs)} ms`) - $('#fps').val(`${faceapi.round(1000 / avgTimeInMs)}`) + $('#fps').val(`${faceapi.utils.round(1000 / avgTimeInMs)}`) } function interpolateAgePredictions(age) { @@ -192,8 +192,8 @@ const interpolatedAge = interpolateAgePredictions(age) new faceapi.draw.DrawTextField( [ - `${faceapi.round(interpolatedAge, 0)} years`, - `${gender} (${faceapi.round(genderProbability)})` + `${faceapi.utils.round(interpolatedAge, 0)} years`, + `${gender} (${faceapi.utils.round(genderProbability)})` ], result.detection.box.bottomLeft ).draw(canvas) diff --git a/examples/examples-browser/views/webcamFaceDetection.html b/examples/examples-browser/views/webcamFaceDetection.html index fd226d92..79c9027d 100644 --- a/examples/examples-browser/views/webcamFaceDetection.html +++ b/examples/examples-browser/views/webcamFaceDetection.html @@ -139,7 +139,7 @@ forwardTimes = [timeInMs].concat(forwardTimes).slice(0, 30) const avgTimeInMs = forwardTimes.reduce((total, t) => total + t) / forwardTimes.length $('#time').val(`${Math.round(avgTimeInMs)} ms`) - $('#fps').val(`${faceapi.round(1000 / avgTimeInMs)}`) + $('#fps').val(`${faceapi.utils.round(1000 / avgTimeInMs)}`) } async function onPlay() { diff --git a/examples/examples-browser/views/webcamFaceExpressionRecognition.html b/examples/examples-browser/views/webcamFaceExpressionRecognition.html index eabac231..176144f5 100644 --- a/examples/examples-browser/views/webcamFaceExpressionRecognition.html +++ b/examples/examples-browser/views/webcamFaceExpressionRecognition.html @@ -151,7 +151,7 @@ forwardTimes = [timeInMs].concat(forwardTimes).slice(0, 30) const avgTimeInMs = forwardTimes.reduce((total, t) => total + t) / forwardTimes.length $('#time').val(`${Math.round(avgTimeInMs)} ms`) - $('#fps').val(`${faceapi.round(1000 / avgTimeInMs)}`) + $('#fps').val(`${faceapi.utils.round(1000 / avgTimeInMs)}`) } async function onPlay() { diff --git a/examples/examples-browser/views/webcamFaceLandmarkDetection.html b/examples/examples-browser/views/webcamFaceLandmarkDetection.html index dc03b22e..796cbd93 100644 --- a/examples/examples-browser/views/webcamFaceLandmarkDetection.html +++ b/examples/examples-browser/views/webcamFaceLandmarkDetection.html @@ -151,7 +151,7 @@ forwardTimes = [timeInMs].concat(forwardTimes).slice(0, 30) const avgTimeInMs = forwardTimes.reduce((total, t) => total + t) / forwardTimes.length $('#time').val(`${Math.round(avgTimeInMs)} ms`) - $('#fps').val(`${faceapi.round(1000 / avgTimeInMs)}`) + $('#fps').val(`${faceapi.utils.round(1000 / avgTimeInMs)}`) } async function onPlay() { diff --git a/examples/examples-nodejs/ageAndGenderRecognition.ts b/examples/examples-nodejs/ageAndGenderRecognition.ts index 92254b18..a4bd0f4f 100644 --- a/examples/examples-nodejs/ageAndGenderRecognition.ts +++ b/examples/examples-nodejs/ageAndGenderRecognition.ts @@ -19,8 +19,8 @@ async function run() { const { age, gender, genderProbability } = result new faceapi.draw.DrawTextField( [ - `${faceapi.round(age, 0)} years`, - `${gender} (${faceapi.round(genderProbability)})` + `${faceapi.utils.round(age, 0)} years`, + `${gender} (${faceapi.utils.round(genderProbability)})` ], result.detection.box.bottomLeft ).draw(out) From c9f0a289048acc215be0b5a38793ab09bfe40785 Mon Sep 17 00:00:00 2001 From: vincent Date: Sat, 14 Dec 2019 21:30:58 +0100 Subject: [PATCH 05/22] bump tfjs-core and dev dependencies to latest --- examples/examples-nodejs/package-lock.json | 4333 ++++++++++++++++++++ examples/examples-nodejs/package.json | 4 +- package-lock.json | 768 ++-- package.json | 28 +- 4 files changed, 4693 insertions(+), 440 deletions(-) create mode 100644 examples/examples-nodejs/package-lock.json diff --git a/examples/examples-nodejs/package-lock.json b/examples/examples-nodejs/package-lock.json new file mode 100644 index 00000000..84f2dbeb --- /dev/null +++ b/examples/examples-nodejs/package-lock.json @@ -0,0 +1,4333 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@tensorflow/tfjs": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-1.4.0.tgz", + "integrity": "sha512-pvc7arrWZ0NNOpEFVL4e99Bj933ty3tmUR0xLnjKao3psts9xr7w0sfJpuSn98HKjz+dj87UEXZIi0fwsAZdwQ==", + "requires": { + "@tensorflow/tfjs-converter": "1.4.0", + "@tensorflow/tfjs-core": "1.4.0", + "@tensorflow/tfjs-data": "1.4.0", + "@tensorflow/tfjs-layers": "1.4.0" + } + }, + "@tensorflow/tfjs-converter": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-1.4.0.tgz", + "integrity": "sha512-vdZumj6zHcEALtQlonCBbVwGdEEDcPrF7prgzf4HF82QG4RpM1x/kVdvzsGvfUIPYjRImNn0ZSPC3WaKslNcXw==" + }, + "@tensorflow/tfjs-core": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-1.4.0.tgz", + "integrity": "sha512-a7dHhSsBbtaxp8/1UAeYKrjY1bQxsDy/Uhj57+mTuGLAcL8CDrTOXXZzucCgs5nvQErdscp7Gp/2VCcA1xp6XQ==", + "requires": { + "@types/offscreencanvas": "~2019.3.0", + "@types/seedrandom": "2.4.27", + "@types/webgl-ext": "0.0.30", + "@types/webgl2": "0.0.4", + "node-fetch": "~2.1.2", + "seedrandom": "2.4.3" + } + }, + "@tensorflow/tfjs-data": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-1.4.0.tgz", + "integrity": "sha512-00N/pe5IYkO3aDI9ZmavQ6mwY4VIepCteusnI7DLiCeqBO0NzGjlH3zH1LVlWIBRypW7JNqYbdl3oVvnd2wWwg==", + "requires": { + "@types/node-fetch": "^2.1.2", + "node-fetch": "~2.1.2" + } + }, + "@tensorflow/tfjs-layers": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-1.4.0.tgz", + "integrity": "sha512-PXNOShZWDVW9OzX9botHJdDD6ClHEQtkfaFjoGZ2I2qYC9+WaVgxJPxwToTS2H2trwrf34q6hZ1lAjVlTuK0Gg==" + }, + "@tensorflow/tfjs-node": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-node/-/tfjs-node-1.4.0.tgz", + "integrity": "sha512-6BVD4Jg72B2iPckz3lawB5UB4fR+pm4skhEEN0x98ykt08QF/3fgJX0xiT1K5otqfpwRDKWwcTO18ajOXgJt1Q==", + "requires": { + "@tensorflow/tfjs": "1.4.0", + "adm-zip": "^0.4.11", + "google-protobuf": "^3.9.2", + "https-proxy-agent": "^2.2.1", + "node-pre-gyp": "0.13.0", + "progress": "^2.0.0", + "rimraf": "^2.6.2", + "tar": "^4.4.6" + } + }, + "@types/node": { + "version": "12.12.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.17.tgz", + "integrity": "sha512-Is+l3mcHvs47sKy+afn2O1rV4ldZFU7W8101cNlOd+MRbjM4Onida8jSZnJdTe/0Pcf25g9BNIUsuugmE6puHA==" + }, + "@types/node-fetch": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.4.tgz", + "integrity": "sha512-Oz6id++2qAOFuOlE1j0ouk1dzl3mmI1+qINPNBhi9nt/gVOz0G+13Ao6qjhdF0Ys+eOkhu6JnFmt38bR3H0POQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/offscreencanvas": { + "version": "2019.3.0", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz", + "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==" + }, + "@types/seedrandom": { + "version": "2.4.27", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.27.tgz", + "integrity": "sha1-nbVjk33YaRX2kJK8QyWdL0hXjkE=" + }, + "@types/webgl-ext": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/webgl-ext/-/webgl-ext-0.0.30.tgz", + "integrity": "sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg==" + }, + "@types/webgl2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/webgl2/-/webgl2-0.0.4.tgz", + "integrity": "sha512-PACt1xdErJbMUOUweSrbVM7gSIYm1vTncW2hF6Os/EeWi6TXYAYMPp+8v6rzHmypE5gHrxaxZNXgMkJVIdZpHw==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "adm-zip": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", + "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==" + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "canvas": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.6.0.tgz", + "integrity": "sha512-bEO9f1ThmbknLPxCa8Es7obPlN9W3stB1bo7njlhOFKIdUTldeTqXCh9YclCPAi2pSQs84XA0jq/QEZXSzgyMw==", + "requires": { + "nan": "^2.14.0", + "node-pre-gyp": "^0.11.0", + "simple-get": "^3.0.3" + }, + "dependencies": { + "node-pre-gyp": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + } + } + }, + "chownr": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "requires": { + "mimic-response": "^2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, + "face-api.js": { + "version": "file:../..", + "requires": { + "@tensorflow/tfjs-core": "1.4.0", + "tslib": "^1.10.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@tensorflow/tfjs": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-1.4.0.tgz", + "integrity": "sha512-pvc7arrWZ0NNOpEFVL4e99Bj933ty3tmUR0xLnjKao3psts9xr7w0sfJpuSn98HKjz+dj87UEXZIi0fwsAZdwQ==", + "requires": { + "@tensorflow/tfjs-converter": "1.4.0", + "@tensorflow/tfjs-core": "1.4.0", + "@tensorflow/tfjs-data": "1.4.0", + "@tensorflow/tfjs-layers": "1.4.0" + } + }, + "@tensorflow/tfjs-converter": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-1.4.0.tgz", + "integrity": "sha512-vdZumj6zHcEALtQlonCBbVwGdEEDcPrF7prgzf4HF82QG4RpM1x/kVdvzsGvfUIPYjRImNn0ZSPC3WaKslNcXw==" + }, + "@tensorflow/tfjs-core": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-1.4.0.tgz", + "integrity": "sha512-a7dHhSsBbtaxp8/1UAeYKrjY1bQxsDy/Uhj57+mTuGLAcL8CDrTOXXZzucCgs5nvQErdscp7Gp/2VCcA1xp6XQ==", + "requires": { + "@types/offscreencanvas": "~2019.3.0", + "@types/seedrandom": "2.4.27", + "@types/webgl-ext": "0.0.30", + "@types/webgl2": "0.0.4", + "node-fetch": "~2.1.2", + "seedrandom": "2.4.3" + } + }, + "@tensorflow/tfjs-data": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-1.4.0.tgz", + "integrity": "sha512-00N/pe5IYkO3aDI9ZmavQ6mwY4VIepCteusnI7DLiCeqBO0NzGjlH3zH1LVlWIBRypW7JNqYbdl3oVvnd2wWwg==", + "requires": { + "@types/node-fetch": "^2.1.2", + "node-fetch": "~2.1.2" + } + }, + "@tensorflow/tfjs-layers": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-1.4.0.tgz", + "integrity": "sha512-PXNOShZWDVW9OzX9botHJdDD6ClHEQtkfaFjoGZ2I2qYC9+WaVgxJPxwToTS2H2trwrf34q6hZ1lAjVlTuK0Gg==" + }, + "@tensorflow/tfjs-node": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-node/-/tfjs-node-1.4.0.tgz", + "integrity": "sha512-6BVD4Jg72B2iPckz3lawB5UB4fR+pm4skhEEN0x98ykt08QF/3fgJX0xiT1K5otqfpwRDKWwcTO18ajOXgJt1Q==", + "requires": { + "@tensorflow/tfjs": "1.4.0", + "adm-zip": "^0.4.11", + "google-protobuf": "^3.9.2", + "https-proxy-agent": "^2.2.1", + "node-pre-gyp": "0.13.0", + "progress": "^2.0.0", + "rimraf": "^2.6.2", + "tar": "^4.4.6" + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + }, + "@types/jasmine": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.0.tgz", + "integrity": "sha512-kGCRI9oiCxFS6soGKlyzhMzDydfcPix9PpTkr7h11huxOxhWwP37Tg7DYBaQ18eQTNreZEuLkhpbGSqVNZPnnw==" + }, + "@types/node": { + "version": "12.12.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.17.tgz", + "integrity": "sha512-Is+l3mcHvs47sKy+afn2O1rV4ldZFU7W8101cNlOd+MRbjM4Onida8jSZnJdTe/0Pcf25g9BNIUsuugmE6puHA==" + }, + "@types/node-fetch": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.4.tgz", + "integrity": "sha512-Oz6id++2qAOFuOlE1j0ouk1dzl3mmI1+qINPNBhi9nt/gVOz0G+13Ao6qjhdF0Ys+eOkhu6JnFmt38bR3H0POQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/offscreencanvas": { + "version": "2019.3.0", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz", + "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==" + }, + "@types/resolve": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", + "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/seedrandom": { + "version": "2.4.27", + "resolved": "http://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.27.tgz", + "integrity": "sha1-nbVjk33YaRX2kJK8QyWdL0hXjkE=" + }, + "@types/webgl-ext": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/webgl-ext/-/webgl-ext-0.0.30.tgz", + "integrity": "sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg==" + }, + "@types/webgl2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/webgl2/-/webgl2-0.0.4.tgz", + "integrity": "sha512-PACt1xdErJbMUOUweSrbVM7gSIYm1vTncW2hF6Os/EeWi6TXYAYMPp+8v6rzHmypE5gHrxaxZNXgMkJVIdZpHw==" + }, + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==" + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==" + }, + "adm-zip": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", + "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==" + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "optional": true + }, + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "arg": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.2.tgz", + "integrity": "sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg==" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "requires": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "requires": { + "callsite": "1.0.0" + } + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" + }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "requires": { + "resolve": "1.1.7" + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", + "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "canvas": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.6.0.tgz", + "integrity": "sha512-bEO9f1ThmbknLPxCa8Es7obPlN9W3stB1bo7njlhOFKIdUTldeTqXCh9YclCPAi2pSQs84XA0jq/QEZXSzgyMw==", + "requires": { + "nan": "^2.14.0", + "node-pre-gyp": "^0.11.0", + "simple-get": "^3.0.3" + }, + "dependencies": { + "node-pre-gyp": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + } + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chokidar": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" + } + }, + "chownr": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "requires": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + }, + "dependencies": { + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "requires": { + "date-now": "^0.1.4" + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=" + }, + "date-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", + "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==" + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "requires": { + "mimic-response": "^2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=" + }, + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "elliptic": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", + "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "engine.io": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", + "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", + "requires": { + "accepts": "~1.3.4", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "ws": "~3.3.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "engine.io-client": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~3.3.1", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "engine.io-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", + "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.0.0", + "string.prototype.trimright": "^2.0.0" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + }, + "dependencies": { + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=" + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=" + }, + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "eventemitter3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "find-cache-dir": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.2.0.tgz", + "integrity": "sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.0", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==" + }, + "follow-redirects": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz", + "integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==", + "requires": { + "debug": "^3.0.0" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "google-protobuf": { + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.11.2.tgz", + "integrity": "sha512-T4fin7lcYLUPj2ChUZ4DvfuuHtg3xi1621qeRZt2J7SvOQusOzq+sDT4vbotWTCjUXJoR36CA016LlhtPy80uQ==" + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "handlebars": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.2.0.tgz", + "integrity": "sha512-Kb4xn5Qh1cxAKvQnzNWZ512DhABzyFNmsaJf3OAkWNa4NkaqWcNI8Tao8Tasi0/F4JD9oyG0YxuFyvyR57d+Gw==", + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "http-proxy": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", + "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "^2.0.0" + } + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "requires": { + "source-map": "~0.5.3" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-generator-function": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz", + "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=" + }, + "is-nan": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.2.1.tgz", + "integrity": "sha1-n69ltvttskt/XAYoR16nH5iEAeI=", + "requires": { + "define-properties": "^1.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-reference": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.3.tgz", + "integrity": "sha512-W1iHHv/oyBb2pPxkBxtaewxa1BC58Pn5J0hogyCdefwUIvb6R+TGbAcIa4qPNYLqLhb3EnOgUf2MQkkF76BcKw==", + "requires": { + "@types/estree": "0.0.39" + } + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "requires": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + } + } + }, + "jasmine": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.5.0.tgz", + "integrity": "sha512-DYypSryORqzsGoMazemIHUfMkXM7I7easFaxAvNM3Mr6Xz3Fy36TupTrAOxZWN8MVKEU5xECv22J4tUQf3uBzQ==", + "requires": { + "glob": "^7.1.4", + "jasmine-core": "~3.5.0" + } + }, + "jasmine-core": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", + "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==" + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + } + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "karma": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", + "integrity": "sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A==", + "requires": { + "bluebird": "^3.3.0", + "body-parser": "^1.16.1", + "braces": "^3.0.2", + "chokidar": "^3.0.0", + "colors": "^1.1.0", + "connect": "^3.6.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "flatted": "^2.0.0", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^4.17.14", + "log4js": "^4.0.0", + "mime": "^2.3.1", + "minimatch": "^3.0.2", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.6.0", + "safe-buffer": "^5.0.1", + "socket.io": "2.1.1", + "source-map": "^0.6.1", + "tmp": "0.0.33", + "useragent": "2.3.0" + } + }, + "karma-chrome-launcher": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", + "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", + "requires": { + "which": "^1.2.1" + } + }, + "karma-coverage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-1.1.2.tgz", + "integrity": "sha512-eQawj4Cl3z/CjxslYy9ariU4uDh7cCNFZHNWXWRpl0pNeblY/4wHR7M7boTYXWrn9bY0z2pZmr11eKje/S/hIw==", + "requires": { + "dateformat": "^1.0.6", + "istanbul": "^0.4.0", + "lodash": "^4.17.0", + "minimatch": "^3.0.0", + "source-map": "^0.5.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "karma-jasmine": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz", + "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==", + "requires": { + "jasmine-core": "^3.3" + }, + "dependencies": { + "jasmine-core": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.3.0.tgz", + "integrity": "sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==" + } + } + }, + "karma-typescript": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/karma-typescript/-/karma-typescript-4.1.1.tgz", + "integrity": "sha512-NiGt3Lh8pxKY6hSW4mBV7X45zfB+EA4ezVMNN/vnzLvN+du0UoEc8lTAhrD/DMrjKP3wDlpabku652svRyguXg==", + "requires": { + "acorn": "^6.0.5", + "acorn-walk": "^6.1.1", + "assert": "^2.0.0", + "async": "^3.0.1", + "browser-resolve": "^1.11.3", + "browserify-zlib": "^0.2.0", + "buffer": "^5.2.1", + "combine-source-map": "^0.8.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "convert-source-map": "^1.6.0", + "crypto-browserify": "^3.12.0", + "diff": "^4.0.1", + "domain-browser": "^1.2.0", + "events": "^3.0.0", + "glob": "^7.1.3", + "https-browserify": "^1.0.0", + "istanbul": "0.4.5", + "json-stringify-safe": "^5.0.1", + "karma-coverage": "^1.1.1", + "lodash": "^4.17.11", + "log4js": "^4.0.1", + "minimatch": "^3.0.4", + "os-browserify": "^0.3.0", + "pad": "^3.2.0", + "path-browserify": "^1.0.0", + "process": "^0.11.10", + "punycode": "^2.1.1", + "querystring-es3": "^0.2.1", + "readable-stream": "^3.1.1", + "remap-istanbul": "^0.13.0", + "source-map": "^0.7.3", + "stream-browserify": "^2.0.2", + "stream-http": "^3.0.0", + "string_decoder": "^1.2.0", + "timers-browserify": "^2.0.10", + "tmp": "^0.1.0", + "tty-browserify": "^0.0.1", + "url": "^0.11.0", + "util": "^0.12.0", + "vm-browserify": "1.1.0" + }, + "dependencies": { + "async": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.1.0.tgz", + "integrity": "sha512-4vx/aaY6j/j3Lw3fbCHNWP0pPaTCew3F6F3hYyl/tHs/ndmV1q7NW9T5yuJ2XAGwdQrP+6Wu20x06U4APo/iQQ==" + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "requires": { + "rimraf": "^2.6.3" + } + } + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=" + }, + "log4js": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz", + "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==", + "requires": { + "date-format": "^2.0.0", + "debug": "^4.1.1", + "flatted": "^2.0.0", + "rfdc": "^1.1.4", + "streamroller": "^1.0.6" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + }, + "dependencies": { + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, + "magic-string": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", + "integrity": "sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==", + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", + "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==" + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + }, + "mime-db": { + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" + }, + "mime-types": { + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", + "requires": { + "mime-db": "1.42.0" + } + }, + "mimic-response": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.0.0.tgz", + "integrity": "sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ==" + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "needle": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", + "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" + }, + "node-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", + "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" + }, + "node-pre-gyp": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", + "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "npm-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" + }, + "npm-packlist": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.7.tgz", + "integrity": "sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" + }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" + }, + "object-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", + "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + }, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + } + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "pad": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/pad/-/pad-3.2.0.tgz", + "integrity": "sha512-2u0TrjcGbOjBTJpyewEl4hBO3OeX5wWue7eIFPzQTg6wFSvoaHcBTTUY5m+n0hd04gmTCPuY0kCpVIVuw5etwg==", + "requires": { + "wcwidth": "^1.0.1" + } + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + }, + "parse-asn1": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.0.tgz", + "integrity": "sha512-Hkavx/nY4/plImrZPHRk2CL9vpOymZLgEbMNX1U0bjcBL7QN9wODxyx0yaMZURSQaUtSEvDrfAvxa9oPb0at9g==" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "picomatch": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", + "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + } + } + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "requires": { + "picomatch": "^2.0.4" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "remap-istanbul": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/remap-istanbul/-/remap-istanbul-0.13.0.tgz", + "integrity": "sha512-rS5ZpVAx3fGtKZkiBe1esXg5mKYbgW9iz8kkADFt3p6lo3NsBBUX1q6SwdhwUtYCGnr7nK6gRlbYK3i8R0jbRA==", + "requires": { + "istanbul": "0.4.5", + "minimatch": "^3.0.4", + "plugin-error": "^1.0.1", + "source-map": "0.6.1", + "through2": "3.0.0" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + }, + "rfdc": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", + "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rollup": { + "version": "1.27.13", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.27.13.tgz", + "integrity": "sha512-hDi7M07MpmNSDE8YVwGVFA8L7n8jTLJ4lG65nMAijAyqBe//rtu4JdxjUBE7JqXfdpqxqDTbCDys9WcqdpsQvw==", + "requires": { + "@types/estree": "*", + "@types/node": "*", + "acorn": "^7.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==" + } + } + }, + "rollup-plugin-commonjs": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz", + "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==", + "requires": { + "estree-walker": "^0.6.1", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.11.0", + "rollup-pluginutils": "^2.8.1" + }, + "dependencies": { + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "rollup-plugin-node-resolve": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz", + "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==", + "requires": { + "@types/resolve": "0.0.8", + "builtin-modules": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.11.1", + "rollup-pluginutils": "^2.8.1" + }, + "dependencies": { + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "rollup-plugin-typescript2": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.25.3.tgz", + "integrity": "sha512-ADkSaidKBovJmf5VBnZBZe+WzaZwofuvYdzGAKTN/J4hN7QJCFYAq7IrH9caxlru6T5qhX41PNFS1S4HqhsGQg==", + "requires": { + "find-cache-dir": "^3.0.0", + "fs-extra": "8.1.0", + "resolve": "1.12.0", + "rollup-pluginutils": "2.8.1", + "tslib": "1.10.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "rollup-plugin-uglify": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-6.0.4.tgz", + "integrity": "sha512-ddgqkH02klveu34TF0JqygPwZnsbhHVI6t8+hGTcYHngPkQb5MIHI0XiztXIN/d6V9j+efwHAqEL7LspSxQXGw==", + "requires": { + "@babel/code-frame": "^7.0.0", + "jest-worker": "^24.0.0", + "serialize-javascript": "^2.1.2", + "uglify-js": "^3.4.9" + } + }, + "rollup-pluginutils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz", + "integrity": "sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==", + "requires": { + "estree-walker": "^0.6.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "seedrandom": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz", + "integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw=" + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "socket.io": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", + "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", + "requires": { + "debug": "~3.1.0", + "engine.io": "~3.2.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.1.1", + "socket.io-parser": "~3.2.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "socket.io-adapter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" + }, + "socket.io-client": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "engine.io-client": "~3.2.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.2.0", + "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "socket.io-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sourcemap-codec": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", + "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==" + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-http": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.0.tgz", + "integrity": "sha512-cuB6RgO7BqC4FBYzmnvhob5Do3wIdIsXAgGycHJnW+981gHqoYcYz9lqjJrk8WXRddbwPuqPYRl+bag6mYv4lw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^3.0.6", + "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "streamroller": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz", + "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==", + "requires": { + "async": "^2.6.2", + "date-format": "^2.0.0", + "debug": "^3.2.6", + "fs-extra": "^7.0.1", + "lodash": "^4.17.14" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "through2": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.0.tgz", + "integrity": "sha512-8B+sevlqP4OiCjonI1Zw03Sf8PuV1eRsYQgLad5eonILOdyeRsY27A/2Ze8IlvlMvq31OH+3fz/styI7Ya62yQ==", + "requires": { + "readable-stream": "2 || 3", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "ts-node": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.5.4.tgz", + "integrity": "sha512-izbVCRV68EasEPQ8MSIGBNK9dc/4sYJJKYA+IarMQct1RtEot6Xp0bXuClsbUSnKpg50ho+aOAx8en5c+y4OFw==", + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typescript": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", + "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==" + }, + "uglify-js": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "useragent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", + "requires": { + "lru-cache": "4.1.x", + "tmp": "0.0.x" + } + }, + "util": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.1.tgz", + "integrity": "sha512-MREAtYOp+GTt9/+kwf00IYoHZyjM8VU4aVrkzUlejyqaIjd2GztVl5V9hGXKlvBKE3gENn/FMfHE5v6hElXGcQ==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "object.entries": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vm-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", + "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==" + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + } + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "google-protobuf": { + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.11.2.tgz", + "integrity": "sha512-T4fin7lcYLUPj2ChUZ4DvfuuHtg3xi1621qeRZt2J7SvOQusOzq+sDT4vbotWTCjUXJoR36CA016LlhtPy80uQ==" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "mimic-response": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.0.0.tgz", + "integrity": "sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "needle": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", + "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", + "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" + }, + "node-pre-gyp": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", + "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" + }, + "npm-packlist": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.7.tgz", + "integrity": "sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "seedrandom": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz", + "integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw=" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } +} diff --git a/examples/examples-nodejs/package.json b/examples/examples-nodejs/package.json index 925752b0..ac7fa73c 100644 --- a/examples/examples-nodejs/package.json +++ b/examples/examples-nodejs/package.json @@ -2,8 +2,8 @@ "author": "justadudewhohacks", "license": "MIT", "dependencies": { - "@tensorflow/tfjs-node": "^1.2.3", - "canvas": "^2.5.0", + "@tensorflow/tfjs-node": "1.4.0", + "canvas": "^2.6.0", "face-api.js": "../../" } } diff --git a/package-lock.json b/package-lock.json index 5b122fb7..1db6816c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,43 +25,27 @@ } }, "@tensorflow/tfjs": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-1.2.9.tgz", - "integrity": "sha512-9UAQnSp638FyM5eedYEM+j2R7VcNajiFmkeT5EXtf7YIurmMFNEm1sbajKJx7/ckz31YcYrVoUPc/iLhhDQl2A==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-1.4.0.tgz", + "integrity": "sha512-pvc7arrWZ0NNOpEFVL4e99Bj933ty3tmUR0xLnjKao3psts9xr7w0sfJpuSn98HKjz+dj87UEXZIi0fwsAZdwQ==", "dev": true, "requires": { - "@tensorflow/tfjs-converter": "1.2.9", - "@tensorflow/tfjs-core": "1.2.9", - "@tensorflow/tfjs-data": "1.2.9", - "@tensorflow/tfjs-layers": "1.2.9" - }, - "dependencies": { - "@tensorflow/tfjs-core": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-1.2.9.tgz", - "integrity": "sha512-s0hHZSx6rGTlkkB8u8gs5n7sIPv1GXDNHmISRy+kqGzmlpkfI2kr6WXqOWQy6wFgjzopRD8cJQjBZ9USPZnYTQ==", - "dev": true, - "requires": { - "@types/offscreencanvas": "~2019.3.0", - "@types/seedrandom": "2.4.27", - "@types/webgl-ext": "0.0.30", - "@types/webgl2": "0.0.4", - "node-fetch": "~2.1.2", - "seedrandom": "2.4.3" - } - } + "@tensorflow/tfjs-converter": "1.4.0", + "@tensorflow/tfjs-core": "1.4.0", + "@tensorflow/tfjs-data": "1.4.0", + "@tensorflow/tfjs-layers": "1.4.0" } }, "@tensorflow/tfjs-converter": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-1.2.9.tgz", - "integrity": "sha512-OKmiuZicIgadT3Bv9BvM+oom7wRz9eC5rTglQnuv7VN9H0syFVuhf5oD1Ff70tGDhJjJgL+cPz01fZRxTXjRWA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-1.4.0.tgz", + "integrity": "sha512-vdZumj6zHcEALtQlonCBbVwGdEEDcPrF7prgzf4HF82QG4RpM1x/kVdvzsGvfUIPYjRImNn0ZSPC3WaKslNcXw==", "dev": true }, "@tensorflow/tfjs-core": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-1.2.9.tgz", - "integrity": "sha512-s0hHZSx6rGTlkkB8u8gs5n7sIPv1GXDNHmISRy+kqGzmlpkfI2kr6WXqOWQy6wFgjzopRD8cJQjBZ9USPZnYTQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-1.4.0.tgz", + "integrity": "sha512-a7dHhSsBbtaxp8/1UAeYKrjY1bQxsDy/Uhj57+mTuGLAcL8CDrTOXXZzucCgs5nvQErdscp7Gp/2VCcA1xp6XQ==", "requires": { "@types/offscreencanvas": "~2019.3.0", "@types/seedrandom": "2.4.27", @@ -72,9 +56,9 @@ } }, "@tensorflow/tfjs-data": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-1.2.9.tgz", - "integrity": "sha512-Ti9Cj3pte9butuEsK5OPq0Lcqdi4wVUdtQXm0o7iYOZ0umseRzfbIb6zbdqucc2MQzOMTnRoxN+FL7LZmncsHg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-1.4.0.tgz", + "integrity": "sha512-00N/pe5IYkO3aDI9ZmavQ6mwY4VIepCteusnI7DLiCeqBO0NzGjlH3zH1LVlWIBRypW7JNqYbdl3oVvnd2wWwg==", "dev": true, "requires": { "@types/node-fetch": "^2.1.2", @@ -82,44 +66,25 @@ } }, "@tensorflow/tfjs-layers": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-1.2.9.tgz", - "integrity": "sha512-OlXYaIb1rCk5dYmpaNsPEkO7R+T0oxfS3vQGIztNJB+YxrN8mwCu3hqgpbdKhAITiP+jxO0o+7bny8MsOCkOSQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-1.4.0.tgz", + "integrity": "sha512-PXNOShZWDVW9OzX9botHJdDD6ClHEQtkfaFjoGZ2I2qYC9+WaVgxJPxwToTS2H2trwrf34q6hZ1lAjVlTuK0Gg==", "dev": true }, "@tensorflow/tfjs-node": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-node/-/tfjs-node-1.2.9.tgz", - "integrity": "sha512-IGD4plBGy+2rQpYiI2LI/RC0nzyLKunJ2cPIx4etJ+V6n40mvEDOVXvUZCz/a72gKUKNPfx5uSsvImfRikWNjw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-node/-/tfjs-node-1.4.0.tgz", + "integrity": "sha512-6BVD4Jg72B2iPckz3lawB5UB4fR+pm4skhEEN0x98ykt08QF/3fgJX0xiT1K5otqfpwRDKWwcTO18ajOXgJt1Q==", "dev": true, "requires": { - "@tensorflow/tfjs": "1.2.9", + "@tensorflow/tfjs": "1.4.0", "adm-zip": "^0.4.11", + "google-protobuf": "^3.9.2", "https-proxy-agent": "^2.2.1", "node-pre-gyp": "0.13.0", "progress": "^2.0.0", "rimraf": "^2.6.2", "tar": "^4.4.6" - }, - "dependencies": { - "node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "dev": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - } } }, "@types/estree": { @@ -129,21 +94,21 @@ "dev": true }, "@types/jasmine": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.4.0.tgz", - "integrity": "sha512-6pUnBg6DuSB55xnxJ5+gW9JOkFrPsXkYAuqqEE8oyrpgDiPQ+TZ+1Zt4S+CHcRJcxyNYXeIXG4vHSzdF6y9Uvw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.0.tgz", + "integrity": "sha512-kGCRI9oiCxFS6soGKlyzhMzDydfcPix9PpTkr7h11huxOxhWwP37Tg7DYBaQ18eQTNreZEuLkhpbGSqVNZPnnw==", "dev": true }, "@types/node": { - "version": "12.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz", - "integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==", + "version": "12.12.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.17.tgz", + "integrity": "sha512-Is+l3mcHvs47sKy+afn2O1rV4ldZFU7W8101cNlOd+MRbjM4Onida8jSZnJdTe/0Pcf25g9BNIUsuugmE6puHA==", "dev": true }, "@types/node-fetch": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.0.tgz", - "integrity": "sha512-TLFRywthBgL68auWj+ziWu+vnmmcHCDFC/sqCOQf1xTz4hRq8cu79z8CtHU9lncExGBsB8fXA4TiLDLt6xvMzw==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.4.tgz", + "integrity": "sha512-Oz6id++2qAOFuOlE1j0ouk1dzl3mmI1+qINPNBhi9nt/gVOz0G+13Ao6qjhdF0Ys+eOkhu6JnFmt38bR3H0POQ==", "dev": true, "requires": { "@types/node": "*" @@ -265,9 +230,9 @@ "dev": true }, "anymatch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.0.tgz", - "integrity": "sha512-Ozz7l4ixzI7Oxj2+cw+p0tVUt27BpaJ+1+q1TCeANWxHpvyn2+Un+YamBdfKu0uh8xLodGhoa1v7595NhKDAuA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -291,9 +256,9 @@ } }, "arg": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", - "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.2.tgz", + "integrity": "sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg==", "dev": true }, "argparse": { @@ -425,9 +390,9 @@ "dev": true }, "bluebird": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "bn.js": { @@ -454,14 +419,20 @@ "type-is": "~1.6.17" }, "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, @@ -475,6 +446,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -646,14 +626,34 @@ } }, "canvas": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.5.0.tgz", - "integrity": "sha512-wwRz2cLMgb9d+rnotOJCoc04Bzj3aJMpWc6JxAD6lP7bYz0ldcn0sKddoZ0vhD5T8HBxrK+XmRDJb68/2VqARw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.6.0.tgz", + "integrity": "sha512-bEO9f1ThmbknLPxCa8Es7obPlN9W3stB1bo7njlhOFKIdUTldeTqXCh9YclCPAi2pSQs84XA0jq/QEZXSzgyMw==", "dev": true, "requires": { - "nan": "^2.13.2", + "nan": "^2.14.0", "node-pre-gyp": "^0.11.0", "simple-get": "^3.0.3" + }, + "dependencies": { + "node-pre-gyp": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "dev": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + } } }, "chalk": { @@ -685,60 +685,25 @@ } }, "chokidar": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.0.2.tgz", - "integrity": "sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", "dev": true, "requires": { - "anymatch": "^3.0.1", - "braces": "^3.0.2", - "fsevents": "^2.0.6", - "glob-parent": "^5.0.0", - "is-binary-path": "^2.1.0", - "is-glob": "^4.0.1", - "normalize-path": "^3.0.0", - "readdirp": "^3.1.1" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" } }, "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "dev": true }, "cipher-base": { @@ -779,9 +744,9 @@ "dev": true }, "colors": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", - "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, "combine-source-map": { @@ -856,6 +821,23 @@ "finalhandler": "1.1.2", "parseurl": "~1.3.3", "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "console-browserify": { @@ -900,12 +882,6 @@ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", "dev": true }, - "core-js": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz", - "integrity": "sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw==", - "dev": true - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1006,12 +982,12 @@ } }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "decamelize": { @@ -1021,12 +997,12 @@ "dev": true }, "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", "dev": true, "requires": { - "mimic-response": "^1.0.0" + "mimic-response": "^2.0.0" } }, "deep-extend": { @@ -1177,6 +1153,12 @@ "requires": { "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, @@ -1207,6 +1189,12 @@ "requires": { "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, @@ -1350,9 +1338,9 @@ "dev": true }, "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", "dev": true }, "events": { @@ -1383,6 +1371,15 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -1396,12 +1393,29 @@ "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "find-cache-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.0.0.tgz", - "integrity": "sha512-t7ulV1fmbxh5G9l/492O1p5+EBbr3uwpt6odhFTMc+nWyhmbloe+ja9BZ8pIBtqFWhOmCWVjx+pTW4zDkFoclw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.2.0.tgz", + "integrity": "sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg==", "dev": true, "requires": { "commondir": "^1.0.1", @@ -1432,32 +1446,6 @@ "dev": true, "requires": { "debug": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "fs-access": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", - "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", - "dev": true, - "requires": { - "null-check": "^1.0.0" } }, "fs-extra": { @@ -1472,12 +1460,12 @@ } }, "fs-minipass": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz", - "integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "dev": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.6.0" } }, "fs.realpath": { @@ -1522,9 +1510,9 @@ "dev": true }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -1536,14 +1524,20 @@ } }, "glob-parent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", - "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, + "google-protobuf": { + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.11.2.tgz", + "integrity": "sha512-T4fin7lcYLUPj2ChUZ4DvfuuHtg3xi1621qeRZt2J7SvOQusOzq+sDT4vbotWTCjUXJoR36CA016LlhtPy80uQ==", + "dev": true + }, "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", @@ -1663,12 +1657,12 @@ } }, "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", + "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", "dev": true, "requires": { - "eventemitter3": "^3.0.0", + "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" } @@ -1680,36 +1674,19 @@ "dev": true }, "https-proxy-agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", - "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { "agent-base": "^4.3.0", "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -1722,9 +1699,9 @@ "dev": true }, "ignore-walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "dev": true, "requires": { "minimatch": "^3.0.4" @@ -1871,6 +1848,12 @@ "define-properties": "^1.1.1" } }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -1999,35 +1982,19 @@ } }, "jasmine": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.4.0.tgz", - "integrity": "sha512-sR9b4n+fnBFDEd7VS2el2DeHgKcPiMVn44rtKFumq9q7P/t8WrxsVIZPob4UDdgcDNCwyDqwxCt4k9TDRmjPoQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.5.0.tgz", + "integrity": "sha512-DYypSryORqzsGoMazemIHUfMkXM7I7easFaxAvNM3Mr6Xz3Fy36TupTrAOxZWN8MVKEU5xECv22J4tUQf3uBzQ==", "dev": true, "requires": { - "glob": "^7.1.3", - "jasmine-core": "~3.4.0" - }, - "dependencies": { - "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "glob": "^7.1.4", + "jasmine-core": "~3.5.0" } }, "jasmine-core": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.4.0.tgz", - "integrity": "sha512-HU/YxV4i6GcmiH4duATwAbJQMlE0MsDIR5XmSVxURxKHn3aGAdbY1/ZJFmVRbKtnLwIxxMJD7gYaPsypcbYimg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", + "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", "dev": true }, "jest-worker": { @@ -2097,9 +2064,9 @@ } }, "karma": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/karma/-/karma-4.3.0.tgz", - "integrity": "sha512-NSPViHOt+RW38oJklvYxQC4BSQsv737oQlr/r06pCM+slDOr4myuI1ivkRmp+3dVpJDfZt2DmaPJ2wkx+ZZuMQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", + "integrity": "sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A==", "dev": true, "requires": { "bluebird": "^3.3.0", @@ -2108,7 +2075,6 @@ "chokidar": "^3.0.0", "colors": "^1.1.0", "connect": "^3.6.0", - "core-js": "^3.1.3", "di": "^0.0.1", "dom-serialize": "^2.2.0", "flatted": "^2.0.0", @@ -2129,50 +2095,14 @@ "source-map": "^0.6.1", "tmp": "0.0.33", "useragent": "2.3.0" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } } }, "karma-chrome-launcher": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", - "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", + "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", "dev": true, "requires": { - "fs-access": "^1.0.0", "which": "^1.2.1" } }, @@ -2541,24 +2471,24 @@ "dev": true }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", "dev": true }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", "dev": true, "requires": { - "mime-db": "1.40.0" + "mime-db": "1.42.0" } }, "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.0.0.tgz", + "integrity": "sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ==", "dev": true }, "minimalistic-assert": { @@ -2589,9 +2519,9 @@ "dev": true }, "minipass": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", - "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "requires": { "safe-buffer": "^5.1.2", @@ -2599,12 +2529,12 @@ } }, "minizlib": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", - "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "dev": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.9.0" } }, "mkdirp": { @@ -2617,9 +2547,9 @@ } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "nan": { @@ -2637,23 +2567,6 @@ "debug": "^3.2.6", "iconv-lite": "^0.4.4", "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "negotiator": { @@ -2674,9 +2587,9 @@ "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" }, "node-pre-gyp": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", - "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", + "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", "dev": true, "requires": { "detect-libc": "^1.0.2", @@ -2689,18 +2602,6 @@ "rimraf": "^2.6.1", "semver": "^5.3.0", "tar": "^4" - }, - "dependencies": { - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - } } }, "nopt": { @@ -2743,15 +2644,24 @@ "dev": true }, "npm-bundled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "dev": true }, "npm-packlist": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.2.tgz", - "integrity": "sha512-pyJclkNoBBckB6K/XPcMp8fP60MaqSZBPQVsNY7Yyc9VP1TUnPMYwck5YaBejf0L7xYr8f4l16+IENeZ0by+yw==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.7.tgz", + "integrity": "sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ==", "dev": true, "requires": { "ignore-walk": "^3.0.1", @@ -2770,12 +2680,6 @@ "set-blocking": "~2.0.0" } }, - "null-check": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", - "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", - "dev": true - }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -3040,9 +2944,9 @@ } }, "picomatch": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", - "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", + "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==", "dev": true }, "pify": { @@ -3235,17 +3139,6 @@ "http-errors": "1.7.2", "iconv-lite": "0.4.24", "unpipe": "1.0.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } } }, "rc": { @@ -3305,9 +3198,9 @@ } }, "readdirp": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.1.2.tgz", - "integrity": "sha512-8rhl0xs2cxfVsqzreYCvs8EwBfn/DhVdqtoLmw19uI3SC5avYX9teCurlErfpPXGmYtMHReGaP2RsLnFvz/lnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", "dev": true, "requires": { "picomatch": "^2.0.4" @@ -3364,12 +3257,12 @@ "dev": true }, "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "^7.1.3" } }, "ripemd160": { @@ -3383,20 +3276,20 @@ } }, "rollup": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.21.2.tgz", - "integrity": "sha512-sCAHlcQ/PExU5t/kRwkEWHdhGmQrZ2IgdQzbjPVNfhWbKHMMZGYqkASVTpQqRPLtQKg15xzEscc+BnIK/TE7/Q==", + "version": "1.27.13", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.27.13.tgz", + "integrity": "sha512-hDi7M07MpmNSDE8YVwGVFA8L7n8jTLJ4lG65nMAijAyqBe//rtu4JdxjUBE7JqXfdpqxqDTbCDys9WcqdpsQvw==", "dev": true, "requires": { - "@types/estree": "0.0.39", - "@types/node": "^12.7.4", - "acorn": "^7.0.0" + "@types/estree": "*", + "@types/node": "*", + "acorn": "^7.1.0" }, "dependencies": { "acorn": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.0.0.tgz", - "integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", "dev": true } } @@ -3450,9 +3343,9 @@ } }, "rollup-plugin-typescript2": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.24.2.tgz", - "integrity": "sha512-teMsD+jxRXj0voIEoyy1xAkQceBcM+cRsCZ0srrlq5ZM8kXvoVh7AMxDYC3I6QRTpJ2RAP03AyMtYmkf0ndoxA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.25.3.tgz", + "integrity": "sha512-ADkSaidKBovJmf5VBnZBZe+WzaZwofuvYdzGAKTN/J4hN7QJCFYAq7IrH9caxlru6T5qhX41PNFS1S4HqhsGQg==", "dev": true, "requires": { "find-cache-dir": "^3.0.0", @@ -3474,9 +3367,9 @@ } }, "graceful-fs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, "resolve": { @@ -3491,14 +3384,14 @@ } }, "rollup-plugin-uglify": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-6.0.3.tgz", - "integrity": "sha512-PIv3CfhZJlOG8C85N0GX+uK09TPggmAS6Nk6fpp2ELzDAV5VUhNzOURDU2j7+MwuRr0zq9IZttUTADc/jH8Gkg==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-6.0.4.tgz", + "integrity": "sha512-ddgqkH02klveu34TF0JqygPwZnsbhHVI6t8+hGTcYHngPkQb5MIHI0XiztXIN/d6V9j+efwHAqEL7LspSxQXGw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "jest-worker": "^24.0.0", - "serialize-javascript": "^1.9.0", + "serialize-javascript": "^2.1.2", "uglify-js": "^3.4.9" } }, @@ -3541,9 +3434,9 @@ "dev": true }, "serialize-javascript": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", - "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", "dev": true }, "set-blocking": { @@ -3587,12 +3480,12 @@ "dev": true }, "simple-get": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.0.3.tgz", - "integrity": "sha512-Wvre/Jq5vgoz31Z9stYWPLn0PqRqmBDpFSdypAnHu5AvRVCYPRYGnvryNLiXu8GOBNDH82J2FRHUGMjjHUpXFw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", "dev": true, "requires": { - "decompress-response": "^3.3.0", + "decompress-response": "^4.2.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } @@ -3619,13 +3512,19 @@ "requires": { "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, "socket.io-adapter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", - "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==", "dev": true }, "socket.io-client": { @@ -3658,6 +3557,12 @@ "requires": { "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, @@ -3686,6 +3591,12 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, @@ -3696,9 +3607,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", - "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -3903,14 +3814,14 @@ } }, "tar": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz", - "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "dev": true, "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.3.5", + "minipass": "^2.8.6", "minizlib": "^1.2.1", "mkdirp": "^0.5.0", "safe-buffer": "^5.1.2", @@ -3951,6 +3862,15 @@ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", "dev": true }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -3964,9 +3884,9 @@ "dev": true }, "ts-node": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", - "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.5.4.tgz", + "integrity": "sha512-izbVCRV68EasEPQ8MSIGBNK9dc/4sYJJKYA+IarMQct1RtEot6Xp0bXuClsbUSnKpg50ho+aOAx8en5c+y4OFw==", "dev": true, "requires": { "arg": "^4.1.0", @@ -4007,9 +3927,9 @@ } }, "typescript": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", - "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", + "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==", "dev": true }, "uglify-js": { @@ -4178,9 +4098,9 @@ "dev": true }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "yeast": { @@ -4190,9 +4110,9 @@ "dev": true }, "yn": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.0.tgz", - "integrity": "sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true } } diff --git a/package.json b/package.json index 72ca5606..9295e688 100644 --- a/package.json +++ b/package.json @@ -33,26 +33,26 @@ "author": "justadudewhohacks", "license": "MIT", "dependencies": { - "@tensorflow/tfjs-core": "1.2.9", + "@tensorflow/tfjs-core": "1.4.0", "tslib": "^1.10.0" }, "devDependencies": { - "@tensorflow/tfjs-node": "^1.2.9", - "@types/jasmine": "^3.4.0", - "@types/node": "^12.7.5", - "canvas": "2.5.0", - "jasmine": "^3.4.0", - "jasmine-core": "^3.4.0", - "karma": "^4.3.0", - "karma-chrome-launcher": "^2.2.0", + "@tensorflow/tfjs-node": "1.4.0", + "@types/jasmine": "^3.5.0", + "@types/node": "^12.12.17", + "canvas": "2.6.0", + "jasmine": "^3.5.0", + "jasmine-core": "^3.5.0", + "karma": "^4.4.1", + "karma-chrome-launcher": "^3.1.0", "karma-jasmine": "^2.0.1", "karma-typescript": "^4.1.1", - "rollup": "^1.21.2", + "rollup": "^1.27.13", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-node-resolve": "^5.2.0", - "rollup-plugin-typescript2": "^0.24.2", - "rollup-plugin-uglify": "^6.0.3", - "ts-node": "^8.3.0", - "typescript": "^3.6.3" + "rollup-plugin-typescript2": "^0.25.3", + "rollup-plugin-uglify": "^6.0.4", + "ts-node": "^8.5.4", + "typescript": "^3.7.3" } } From b298c2a44c028ccd7c244f6b248d1cfb14e45d5f Mon Sep 17 00:00:00 2001 From: vincent Date: Sun, 15 Dec 2019 11:12:28 +0100 Subject: [PATCH 06/22] add depecration warnings for allFaces and mtcnn and remove mtcnn from examples --- README.md | 48 +- .../public/js/faceDetectionControls.js | 15 +- .../views/ageAndGenderRecognition.html | 24 - .../views/bbtFaceRecognition.html | 24 - .../examples-browser/views/faceDetection.html | 24 - .../views/faceExpressionRecognition.html | 24 - .../views/faceExtraction.html | 24 - .../views/faceLandmarkDetection.html | 24 - .../views/faceRecognition.html | 24 - .../views/videoFaceTracking.html | 24 - .../views/webcamAgeAndGenderRecognition.html | 24 - .../views/webcamFaceDetection.html | 24 - .../webcamFaceExpressionRecognition.html | 24 - .../views/webcamFaceLandmarkDetection.html | 24 - examples/examples-nodejs/commons/env.ts | 7 +- .../examples-nodejs/commons/faceDetection.ts | 10 +- examples/examples-nodejs/package-lock.json | 4333 ----------------- jasmine-node.js | 2 +- src/globalApi/allFaces.ts | 3 + src/mtcnn/Mtcnn.ts | 10 + 20 files changed, 21 insertions(+), 4695 deletions(-) delete mode 100644 examples/examples-nodejs/package-lock.json diff --git a/README.md b/README.md index 7ac1de14..d3ed4936 100644 --- a/README.md +++ b/README.md @@ -135,8 +135,7 @@ import * as canvas from 'canvas'; import * as faceapi from 'face-api.js'; // patch nodejs environment, we need to provide an implementation of -// HTMLCanvasElement and HTMLImageElement, additionally an implementation -// of ImageData is required, in case you want to use the MTCNN +// HTMLCanvasElement and HTMLImageElement const { Canvas, Image, ImageData } = canvas faceapi.env.monkeyPatch({ Canvas, Image, ImageData }) ``` @@ -160,7 +159,6 @@ console.log(faceapi.nets) // faceRecognitionNet // ssdMobilenetv1 // tinyFaceDetector -// mtcnn // tinyYolov2 ``` @@ -246,7 +244,6 @@ By default **detectAllFaces** and **detectSingleFace** utilize the SSD Mobilenet ``` javascript const detections1 = await faceapi.detectAllFaces(input, new faceapi.SsdMobilenetv1Options()) const detections2 = await faceapi.detectAllFaces(input, new faceapi.TinyFaceDetectorOptions()) -const detections3 = await faceapi.detectAllFaces(input, new faceapi.MtcnnOptions()) ``` You can tune the options of each face detector as shown [here](#getting-started-face-detection-options). @@ -592,40 +589,6 @@ export interface ITinyFaceDetectorOptions { const options = new faceapi.TinyFaceDetectorOptions({ inputSize: 320 }) ``` -### MtcnnOptions - -``` javascript -export interface IMtcnnOptions { - // minimum face size to expect, the higher the faster processing will be, - // but smaller faces won't be detected - // default: 20 - minFaceSize?: number - - // the score threshold values used to filter the bounding - // boxes of stage 1, 2 and 3 - // default: [0.6, 0.7, 0.7] - scoreThresholds?: number[] - - // scale factor used to calculate the scale steps of the image - // pyramid used in stage 1 - // default: 0.709 - scaleFactor?: number - - // number of scaled versions of the input image passed through the CNN - // of the first stage, lower numbers will result in lower inference time, - // but will also be less accurate - // default: 10 - maxNumScales?: number - - // instead of specifying scaleFactor and maxNumScales you can also - // set the scaleSteps manually - scaleSteps?: number[] -} - -// example -const options = new faceapi.MtcnnOptions({ minFaceSize: 100, scaleFactor: 0.8 }) -``` - ## Utility Classes @@ -726,7 +689,6 @@ Instead of using the high level API, you can directly use the forward methods of ``` javascript const detections1 = await faceapi.ssdMobilenetv1(input, options) const detections2 = await faceapi.tinyFaceDetector(input, options) -const detections3 = await faceapi.mtcnn(input, options) const landmarks1 = await faceapi.detectFaceLandmarks(faceImage) const landmarks2 = await faceapi.detectFaceLandmarksTiny(faceImage) const descriptor = await faceapi.computeFaceDescriptor(alignedFaceImage) @@ -839,14 +801,6 @@ The face detector has been trained on a custom dataset of ~14K images labeled wi This model is basically an even tinier version of Tiny Yolo V2, replacing the regular convolutions of Yolo with depthwise separable convolutions. Yolo is fully convolutional, thus can easily adapt to different input image sizes to trade off accuracy for performance (inference time). -### MTCNN - -**Note, this model is mostly kept in this repo for experimental reasons. In general the other face detectors should perform better, but of course you are free to play around with MTCNN.** - -MTCNN (Multi-task Cascaded Convolutional Neural Networks) represents an alternative face detector to SSD Mobilenet v1 and Tiny Yolo v2, which offers much more room for configuration. By tuning the input parameters, MTCNN should be able to detect a wide range of face bounding box sizes. MTCNN is a 3 stage cascaded CNN, which simultaneously returns 5 face landmark points along with the bounding boxes and scores for each face. Additionally the model size is only 2MB. - -MTCNN has been presented in the paper [Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Networks](https://kpzhang93.github.io/MTCNN_face_detection_alignment/paper/spl.pdf) by Zhang et al. and the model weights are provided in the official [repo](https://github.com/kpzhang93/MTCNN_face_detection_alignment) of the MTCNN implementation. - ## 68 Point Face Landmark Detection Models diff --git a/examples/examples-browser/public/js/faceDetectionControls.js b/examples/examples-browser/public/js/faceDetectionControls.js index b23c18bb..abe410c7 100644 --- a/examples/examples-browser/public/js/faceDetectionControls.js +++ b/examples/examples-browser/public/js/faceDetectionControls.js @@ -1,6 +1,5 @@ const SSD_MOBILENETV1 = 'ssd_mobilenetv1' const TINY_FACE_DETECTOR = 'tiny_face_detector' -const MTCNN = 'mtcnn' let selectedFaceDetector = SSD_MOBILENETV1 @@ -12,17 +11,10 @@ let minConfidence = 0.5 let inputSize = 512 let scoreThreshold = 0.5 -//mtcnn options -let minFaceSize = 20 - function getFaceDetectorOptions() { return selectedFaceDetector === SSD_MOBILENETV1 ? new faceapi.SsdMobilenetv1Options({ minConfidence }) - : ( - selectedFaceDetector === TINY_FACE_DETECTOR - ? new faceapi.TinyFaceDetectorOptions({ inputSize, scoreThreshold }) - : new faceapi.MtcnnOptions({ minFaceSize }) - ) + : new faceapi.TinyFaceDetectorOptions({ inputSize, scoreThreshold }) } function onIncreaseMinConfidence() { @@ -79,9 +71,6 @@ function getCurrentFaceDetectionNet() { if (selectedFaceDetector === TINY_FACE_DETECTOR) { return faceapi.nets.tinyFaceDetector } - if (selectedFaceDetector === MTCNN) { - return faceapi.nets.mtcnn - } } function isFaceDetectionModelLoaded() { @@ -89,7 +78,7 @@ function isFaceDetectionModelLoaded() { } async function changeFaceDetector(detector) { - ['#ssd_mobilenetv1_controls', '#tiny_face_detector_controls', '#mtcnn_controls'] + ['#ssd_mobilenetv1_controls', '#tiny_face_detector_controls'] .forEach(id => $(id).hide()) selectedFaceDetector = detector diff --git a/examples/examples-browser/views/ageAndGenderRecognition.html b/examples/examples-browser/views/ageAndGenderRecognition.html index 63173e47..da9f9c42 100644 --- a/examples/examples-browser/views/ageAndGenderRecognition.html +++ b/examples/examples-browser/views/ageAndGenderRecognition.html @@ -45,7 +45,6 @@ @@ -111,29 +110,6 @@ - - -
-
- - -
- - -
-
- -