UNPKG

@tensorflow-models/coco-ssd

Version:

Object detection model (coco-ssd) in TensorFlow.js

685 lines 21.4 kB
"use strict"; /** * @license * Copyright 2017 Google Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ Object.defineProperty(exports, "__esModule", { value: true }); /** * Shuffles the array in-place using Fisher-Yates algorithm. * * ```js * const a = [1, 2, 3, 4, 5]; * tf.util.shuffle(a); * console.log(a); * ``` * * @param array The array to shuffle in-place. */ /** @doc {heading: 'Util'} */ // tslint:disable-next-line:no-any function shuffle(array) { var counter = array.length; var temp = 0; var index = 0; // While there are elements in the array while (counter > 0) { // Pick a random index index = (Math.random() * counter) | 0; // Decrease counter by 1 counter--; // And swap the last element with it temp = array[counter]; array[counter] = array[index]; array[index] = temp; } } exports.shuffle = shuffle; /** Clamps a value to a specified range. */ function clamp(min, x, max) { return Math.max(min, Math.min(x, max)); } exports.clamp = clamp; function nearestLargerEven(val) { return val % 2 === 0 ? val : val + 1; } exports.nearestLargerEven = nearestLargerEven; function sum(arr) { var sum = 0; for (var i = 0; i < arr.length; i++) { sum += arr[i]; } return sum; } exports.sum = sum; /** * Returns a sample from a uniform [a, b) distribution. * * @param a The minimum support (inclusive). * @param b The maximum support (exclusive). * @return A pseudorandom number on the half-open interval [a,b). */ function randUniform(a, b) { var r = Math.random(); return (b * r) + (1 - r) * a; } exports.randUniform = randUniform; /** Returns the squared Euclidean distance between two vectors. */ function distSquared(a, b) { var result = 0; for (var i = 0; i < a.length; i++) { var diff = Number(a[i]) - Number(b[i]); result += diff * diff; } return result; } exports.distSquared = distSquared; /** * Asserts that the expression is true. Otherwise throws an error with the * provided message. * * ```js * tf.util.assert(2 === 3, 'Two is not three'); * ``` * * @param expr The expression to assert (as a boolean). * @param msg The message to report when throwing an error. Can be either a * string, or a function that returns a string (for performance reasons). */ /** @doc {heading: 'Util'} */ function assert(expr, msg) { if (!expr) { throw new Error(typeof msg === 'string' ? msg : msg()); } } exports.assert = assert; function assertShapesMatch(shapeA, shapeB, errorMessagePrefix) { if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } assert(arraysEqual(shapeA, shapeB), errorMessagePrefix + (" Shapes " + shapeA + " and " + shapeB + " must match")); } exports.assertShapesMatch = assertShapesMatch; function assertNonNull(a) { assert(a != null, "The input to the tensor constructor must be a non-null value."); } exports.assertNonNull = assertNonNull; // NOTE: We explicitly type out what T extends instead of any so that // util.flatten on a nested array of number doesn't try to infer T as a // number[][], causing us to explicitly type util.flatten<number>(). /** * Flattens an arbitrarily nested array. * * ```js * const a = [[1, 2], [3, 4], [5, [6, [7]]]]; * const flat = tf.util.flatten(a); * console.log(flat); * ``` * * @param arr The nested array to flatten. * @param result The destination array which holds the elements. */ /** @doc {heading: 'Util'} */ function flatten(arr, result) { if (result === void 0) { result = []; } if (result == null) { result = []; } if (Array.isArray(arr) || isTypedArray(arr)) { for (var i = 0; i < arr.length; ++i) { flatten(arr[i], result); } } else { result.push(arr); } return result; } exports.flatten = flatten; /** * Returns the size (number of elements) of the tensor given its shape. * * ```js * const shape = [3, 4, 2]; * const size = tf.util.sizeFromShape(shape); * console.log(size); * ``` */ /** @doc {heading: 'Util'} */ function sizeFromShape(shape) { if (shape.length === 0) { // Scalar. return 1; } var size = shape[0]; for (var i = 1; i < shape.length; i++) { size *= shape[i]; } return size; } exports.sizeFromShape = sizeFromShape; function isScalarShape(shape) { return shape.length === 0; } exports.isScalarShape = isScalarShape; function arraysEqual(n1, n2) { if (n1 === n2) { return true; } if (n1 == null || n2 == null) { return false; } if (n1.length !== n2.length) { return false; } for (var i = 0; i < n1.length; i++) { if (n1[i] !== n2[i]) { return false; } } return true; } exports.arraysEqual = arraysEqual; function isInt(a) { return a % 1 === 0; } exports.isInt = isInt; function tanh(x) { // tslint:disable-next-line:no-any if (Math.tanh != null) { // tslint:disable-next-line:no-any return Math.tanh(x); } if (x === Infinity) { return 1; } else if (x === -Infinity) { return -1; } else { var e2x = Math.exp(2 * x); return (e2x - 1) / (e2x + 1); } } exports.tanh = tanh; function sizeToSquarishShape(size) { var width = Math.ceil(Math.sqrt(size)); return [width, Math.ceil(size / width)]; } exports.sizeToSquarishShape = sizeToSquarishShape; function createShuffledIndices(n) { var shuffledIndices = new Uint32Array(n); for (var i = 0; i < n; ++i) { shuffledIndices[i] = i; } shuffle(shuffledIndices); return shuffledIndices; } exports.createShuffledIndices = createShuffledIndices; function rightPad(a, size) { if (size <= a.length) { return a; } return a + ' '.repeat(size - a.length); } exports.rightPad = rightPad; function repeatedTry(checkFn, delayFn, maxCounter) { if (delayFn === void 0) { delayFn = function (counter) { return 0; }; } return new Promise(function (resolve, reject) { var tryCount = 0; var tryFn = function () { if (checkFn()) { resolve(); return; } tryCount++; var nextBackoff = delayFn(tryCount); if (maxCounter != null && tryCount >= maxCounter) { reject(); return; } setTimeout(tryFn, nextBackoff); }; tryFn(); }); } exports.repeatedTry = repeatedTry; /** * Given the full size of the array and a shape that may contain -1 as the * implicit dimension, returns the inferred shape where -1 is replaced. * E.g. For shape=[2, -1, 3] and size=24, it will return [2, 4, 3]. * * @param shape The shape, which may contain -1 in some dimension. * @param size The full size (number of elements) of the array. * @return The inferred shape where -1 is replaced with the inferred size. */ function inferFromImplicitShape(shape, size) { var shapeProd = 1; var implicitIdx = -1; for (var i = 0; i < shape.length; ++i) { if (shape[i] >= 0) { shapeProd *= shape[i]; } else if (shape[i] === -1) { if (implicitIdx !== -1) { throw Error("Shapes can only have 1 implicit size. " + ("Found -1 at dim " + implicitIdx + " and dim " + i)); } implicitIdx = i; } else if (shape[i] < 0) { throw Error("Shapes can not be < 0. Found " + shape[i] + " at dim " + i); } } if (implicitIdx === -1) { if (size > 0 && size !== shapeProd) { throw Error("Size(" + size + ") must match the product of shape " + shape); } return shape; } if (shapeProd === 0) { throw Error("Cannot infer the missing size in [" + shape + "] when " + "there are 0 elements"); } if (size % shapeProd !== 0) { throw Error("The implicit shape can't be a fractional number. " + ("Got " + size + " / " + shapeProd)); } var newShape = shape.slice(); newShape[implicitIdx] = size / shapeProd; return newShape; } exports.inferFromImplicitShape = inferFromImplicitShape; function parseAxisParam(axis, shape) { var rank = shape.length; // Normalize input axis = axis == null ? shape.map(function (s, i) { return i; }) : [].concat(axis); // Check for valid range assert(axis.every(function (ax) { return ax >= -rank && ax < rank; }), "All values in axis param must be in range [-" + rank + ", " + rank + ") but " + ("got axis " + axis)); // Check for only integers assert(axis.every(function (ax) { return isInt(ax); }), "All values in axis param must be integers but " + ("got axis " + axis)); // Handle negative axis. return axis.map(function (a) { return a < 0 ? rank + a : a; }); } exports.parseAxisParam = parseAxisParam; /** Reduces the shape by removing all dimensions of shape 1. */ function squeezeShape(shape, axis) { var newShape = []; var keptDims = []; var axes = axis == null ? null : parseAxisParam(axis, shape).sort(); var j = 0; for (var i = 0; i < shape.length; ++i) { if (axes != null) { if (axes[j] === i && shape[i] !== 1) { throw new Error("Can't squeeze axis " + i + " since its dim '" + shape[i] + "' is not 1"); } if ((axes[j] == null || axes[j] > i) && shape[i] === 1) { newShape.push(shape[i]); keptDims.push(i); } if (axes[j] <= i) { j++; } } if (shape[i] !== 1) { newShape.push(shape[i]); keptDims.push(i); } } return { newShape: newShape, keptDims: keptDims }; } exports.squeezeShape = squeezeShape; function getTypedArrayFromDType(dtype, size) { var values = null; if (dtype == null || dtype === 'float32') { values = new Float32Array(size); } else if (dtype === 'int32') { values = new Int32Array(size); } else if (dtype === 'bool') { values = new Uint8Array(size); } else { throw new Error("Unknown data type " + dtype); } return values; } exports.getTypedArrayFromDType = getTypedArrayFromDType; function getArrayFromDType(dtype, size) { var values = null; if (dtype == null || dtype === 'float32') { values = new Float32Array(size); } else if (dtype === 'int32') { values = new Int32Array(size); } else if (dtype === 'bool') { values = new Uint8Array(size); } else if (dtype === 'string') { values = new Array(size); } else { throw new Error("Unknown data type " + dtype); } return values; } exports.getArrayFromDType = getArrayFromDType; function checkComputationForErrors(vals, dtype, name) { if (dtype !== 'float32') { // Only floating point computations will generate NaN values return; } for (var i = 0; i < vals.length; i++) { var num = vals[i]; if (isNaN(num) || !isFinite(num)) { throw Error("The result of the '" + name + "' is " + num + "."); } } } exports.checkComputationForErrors = checkComputationForErrors; function checkConversionForErrors(vals, dtype) { for (var i = 0; i < vals.length; i++) { var num = vals[i]; if (isNaN(num) || !isFinite(num)) { throw Error("A tensor of type " + dtype + " being uploaded contains " + num + "."); } } } exports.checkConversionForErrors = checkConversionForErrors; /** * Returns true if the new type can't encode the old type without loss of * precision. */ function hasEncodingLoss(oldType, newType) { if (newType === 'complex64') { return false; } if (newType === 'float32' && oldType !== 'complex64') { return false; } if (newType === 'int32' && oldType !== 'float32' && oldType !== 'complex64') { return false; } if (newType === 'bool' && oldType === 'bool') { return false; } return true; } exports.hasEncodingLoss = hasEncodingLoss; function isTypedArray(a) { return a instanceof Float32Array || a instanceof Int32Array || a instanceof Uint8Array; } exports.isTypedArray = isTypedArray; function bytesPerElement(dtype) { if (dtype === 'float32' || dtype === 'int32') { return 4; } else if (dtype === 'complex64') { return 8; } else if (dtype === 'bool') { return 1; } else { throw new Error("Unknown dtype " + dtype); } } exports.bytesPerElement = bytesPerElement; /** * Returns the approximate number of bytes allocated in the string array - 2 * bytes per character. Computing the exact bytes for a native string in JS is * not possible since it depends on the encoding of the html page that serves * the website. */ function bytesFromStringArray(arr) { if (arr == null) { return 0; } var bytes = 0; arr.forEach(function (x) { return bytes += x.length * 2; }); return bytes; } exports.bytesFromStringArray = bytesFromStringArray; /** Returns true if the value is a string. */ function isString(value) { return typeof value === 'string' || value instanceof String; } exports.isString = isString; function isBoolean(value) { return typeof value === 'boolean'; } exports.isBoolean = isBoolean; function isNumber(value) { return typeof value === 'number'; } exports.isNumber = isNumber; function inferDtype(values) { if (Array.isArray(values)) { return inferDtype(values[0]); } if (values instanceof Float32Array) { return 'float32'; } else if (values instanceof Int32Array || values instanceof Uint8Array) { return 'int32'; } else if (isNumber(values)) { return 'float32'; } else if (isString(values)) { return 'string'; } else if (isBoolean(values)) { return 'bool'; } return 'float32'; } exports.inferDtype = inferDtype; function isFunction(f) { return !!(f && f.constructor && f.call && f.apply); } exports.isFunction = isFunction; function nearestDivisor(size, start) { for (var i = start; i < size; ++i) { if (size % i === 0) { return i; } } return size; } exports.nearestDivisor = nearestDivisor; function computeStrides(shape) { var rank = shape.length; if (rank < 2) { return []; } // Last dimension has implicit stride of 1, thus having D-1 (instead of D) // strides. var strides = new Array(rank - 1); strides[rank - 2] = shape[rank - 1]; for (var i = rank - 3; i >= 0; --i) { strides[i] = strides[i + 1] * shape[i + 1]; } return strides; } exports.computeStrides = computeStrides; function toTypedArray(a, dtype, debugMode) { if (dtype === 'string') { throw new Error('Cannot convert a string[] to a TypedArray'); } if (Array.isArray(a)) { a = flatten(a); } if (debugMode) { checkConversionForErrors(a, dtype); } if (noConversionNeeded(a, dtype)) { return a; } if (dtype == null || dtype === 'float32' || dtype === 'complex64') { return new Float32Array(a); } else if (dtype === 'int32') { return new Int32Array(a); } else if (dtype === 'bool') { var bool = new Uint8Array(a.length); for (var i = 0; i < bool.length; ++i) { if (Math.round(a[i]) !== 0) { bool[i] = 1; } } return bool; } else { throw new Error("Unknown data type " + dtype); } } exports.toTypedArray = toTypedArray; function createNestedArray(offset, shape, a) { var ret = new Array(); if (shape.length === 1) { var d = shape[0]; for (var i = 0; i < d; i++) { ret[i] = a[offset + i]; } } else { var d = shape[0]; var rest = shape.slice(1); var len = rest.reduce(function (acc, c) { return acc * c; }); for (var i = 0; i < d; i++) { ret[i] = createNestedArray(offset + i * len, rest, a); } } return ret; } // Provide a nested array of TypedArray in given shape. function toNestedArray(shape, a) { if (shape.length === 0) { // Scalar type should return a single number. return a[0]; } var size = shape.reduce(function (acc, c) { return acc * c; }); if (size === 0) { // A tensor with shape zero should be turned into empty list. return []; } if (size !== a.length) { throw new Error("[" + shape + "] does not match the input size."); } return createNestedArray(0, shape, a); } exports.toNestedArray = toNestedArray; function noConversionNeeded(a, dtype) { return (a instanceof Float32Array && dtype === 'float32') || (a instanceof Int32Array && dtype === 'int32') || (a instanceof Uint8Array && dtype === 'bool'); } function makeOnesTypedArray(size, dtype) { var array = makeZerosTypedArray(size, dtype); for (var i = 0; i < array.length; i++) { array[i] = 1; } return array; } exports.makeOnesTypedArray = makeOnesTypedArray; function makeZerosTypedArray(size, dtype) { if (dtype == null || dtype === 'float32' || dtype === 'complex64') { return new Float32Array(size); } else if (dtype === 'int32') { return new Int32Array(size); } else if (dtype === 'bool') { return new Uint8Array(size); } else { throw new Error("Unknown data type " + dtype); } } exports.makeZerosTypedArray = makeZerosTypedArray; /** * Returns the current high-resolution time in milliseconds relative to an * arbitrary time in the past. It works across different platforms (node.js, * browsers). * * ```js * console.log(tf.util.now()); * ``` */ /** @doc {heading: 'Util'} */ function now() { if (typeof performance !== 'undefined') { return performance.now(); } else if (typeof process !== 'undefined') { var time = process.hrtime(); return time[0] * 1000 + time[1] / 1000000; } else { throw new Error('Cannot measure time in this environment. You should run tf.js ' + 'in the browser or in Node.js'); } } exports.now = now; /** * Monitor Promise.all progress, fire onProgress callback function. * * @param promises Promise list going to be monitored * @param onProgress Callback function. Fired when a promise resolved. * @param startFraction Optional fraction start. Default to 0. * @param endFraction Optional fraction end. Default to 1. */ function monitorPromisesProgress(promises, onProgress, startFraction, endFraction) { checkPromises(promises); startFraction = startFraction == null ? 0 : startFraction; endFraction = endFraction == null ? 1 : endFraction; checkFraction(startFraction, endFraction); var resolvedPromise = 0; function registerMonitor(promise) { promise.then(function (value) { var fraction = startFraction + ++resolvedPromise / promises.length * (endFraction - startFraction); // pass fraction as parameter to callback function. onProgress(fraction); return value; }); return promise; } function checkPromises(promises) { assert(promises != null && Array.isArray(promises) && promises.length > 0, 'promises must be a none empty array'); } function checkFraction(startFraction, endFraction) { assert(startFraction >= 0 && startFraction <= 1, "Progress fraction must be in range [0, 1], but " + ("got startFraction " + startFraction)); assert(endFraction >= 0 && endFraction <= 1, "Progress fraction must be in range [0, 1], but " + ("got endFraction " + endFraction)); assert(endFraction >= startFraction, "startFraction must be no more than endFraction, but " + ("got startFraction " + startFraction + " and endFraction " + endFraction)); } return Promise.all(promises.map(registerMonitor)); } exports.monitorPromisesProgress = monitorPromisesProgress; function assertNonNegativeIntegerDimensions(shape) { shape.forEach(function (dimSize) { assert(Number.isInteger(dimSize) && dimSize >= 0, "Tensor must have a shape comprised of positive integers but got " + ("shape [" + shape + "].")); }); } exports.assertNonNegativeIntegerDimensions = assertNonNegativeIntegerDimensions; //# sourceMappingURL=util.js.map