@tensorflow-models/coco-ssd
Version:
Object detection model (coco-ssd) in TensorFlow.js
685 lines • 21.4 kB
JavaScript
/**
* @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
;