UNPKG

@vladmandic/face-api

Version:

FaceAPI: AI-powered Face Detection & Rotation Tracking, Face Description & Recognition, Age & Gender & Emotion Prediction for Browser and NodeJS using TensorFlow/JS

4,709 lines 175 kB
  /*
  Face-API
  homepage: <https://github.com/vladmandic/face-api>
  author: <https://github.com/vladmandic>'
  */

var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[Object.keys(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
  __markAsModule(target);
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __reExport = (target, module2, desc) => {
  if (module2 && typeof module2 === "object" || typeof module2 === "function") {
    for (let key of __getOwnPropNames(module2))
      if (!__hasOwnProp.call(target, key) && key !== "default")
        __defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable });
  }
  return target;
};
var __toModule = (module2) => {
  return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);
};

// dist/tfjs.esm.js
var require_tfjs_esm = __commonJS({
  "dist/tfjs.esm.js"(exports) {
    var __create2 = Object.create;
    var __defProp2 = Object.defineProperty;
    var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
    var __getOwnPropNames2 = Object.getOwnPropertyNames;
    var __getProtoOf2 = Object.getPrototypeOf;
    var __hasOwnProp2 = Object.prototype.hasOwnProperty;
    var __markAsModule2 = (target) => __defProp2(target, "__esModule", { value: true });
    var __reExport2 = (target, module22, desc) => {
      if (module22 && typeof module22 === "object" || typeof module22 === "function") {
        for (let key of __getOwnPropNames2(module22))
          if (!__hasOwnProp2.call(target, key) && key !== "default")
            __defProp2(target, key, { get: () => module22[key], enumerable: !(desc = __getOwnPropDesc2(module22, key)) || desc.enumerable });
      }
      return target;
    };
    var __toModule2 = (module22) => {
      return __reExport2(__markAsModule2(__defProp2(module22 != null ? __create2(__getProtoOf2(module22)) : {}, "default", module22 && module22.__esModule && "default" in module22 ? { get: () => module22.default, enumerable: true } : { value: module22, enumerable: true })), module22);
    };
    __markAsModule2(exports);
    __reExport2(exports, __toModule2(require("@tensorflow/tfjs-node")));
  }
});

// src/index.ts
__export(exports, {
  AgeGenderNet: () => AgeGenderNet,
  BoundingBox: () => BoundingBox,
  Box: () => Box,
  ComposableTask: () => ComposableTask,
  ComputeAllFaceDescriptorsTask: () => ComputeAllFaceDescriptorsTask,
  ComputeFaceDescriptorsTaskBase: () => ComputeFaceDescriptorsTaskBase,
  ComputeSingleFaceDescriptorTask: () => ComputeSingleFaceDescriptorTask,
  DetectAllFaceLandmarksTask: () => DetectAllFaceLandmarksTask,
  DetectAllFacesTask: () => DetectAllFacesTask,
  DetectFaceLandmarksTaskBase: () => DetectFaceLandmarksTaskBase,
  DetectFacesTaskBase: () => DetectFacesTaskBase,
  DetectSingleFaceLandmarksTask: () => DetectSingleFaceLandmarksTask,
  DetectSingleFaceTask: () => DetectSingleFaceTask,
  Dimensions: () => Dimensions,
  FACE_EXPRESSION_LABELS: () => FACE_EXPRESSION_LABELS,
  FaceDetection: () => FaceDetection,
  FaceDetectionNet: () => FaceDetectionNet,
  FaceExpressionNet: () => FaceExpressionNet,
  FaceExpressions: () => FaceExpressions,
  FaceLandmark68Net: () => FaceLandmark68Net,
  FaceLandmark68TinyNet: () => FaceLandmark68TinyNet,
  FaceLandmarkNet: () => FaceLandmarkNet,
  FaceLandmarks: () => FaceLandmarks,
  FaceLandmarks5: () => FaceLandmarks5,
  FaceLandmarks68: () => FaceLandmarks68,
  FaceMatch: () => FaceMatch,
  FaceMatcher: () => FaceMatcher,
  FaceRecognitionNet: () => FaceRecognitionNet,
  Gender: () => Gender,
  LabeledBox: () => LabeledBox,
  LabeledFaceDescriptors: () => LabeledFaceDescriptors,
  NetInput: () => NetInput,
  NeuralNetwork: () => NeuralNetwork,
  ObjectDetection: () => ObjectDetection,
  Point: () => Point,
  PredictedBox: () => PredictedBox,
  Rect: () => Rect,
  SsdMobilenetv1: () => SsdMobilenetv1,
  SsdMobilenetv1Options: () => SsdMobilenetv1Options,
  TinyFaceDetector: () => TinyFaceDetector,
  TinyFaceDetectorOptions: () => TinyFaceDetectorOptions,
  TinyYolov2: () => TinyYolov2,
  TinyYolov2Options: () => TinyYolov2Options,
  allFaces: () => allFaces,
  allFacesSsdMobilenetv1: () => allFacesSsdMobilenetv1,
  allFacesTinyYolov2: () => allFacesTinyYolov2,
  awaitMediaLoaded: () => awaitMediaLoaded,
  bufferToImage: () => bufferToImage,
  computeFaceDescriptor: () => computeFaceDescriptor,
  createCanvas: () => createCanvas,
  createCanvasFromMedia: () => createCanvasFromMedia,
  createFaceDetectionNet: () => createFaceDetectionNet,
  createFaceRecognitionNet: () => createFaceRecognitionNet,
  createSsdMobilenetv1: () => createSsdMobilenetv1,
  createTinyFaceDetector: () => createTinyFaceDetector,
  createTinyYolov2: () => createTinyYolov2,
  detectAllFaces: () => detectAllFaces,
  detectFaceLandmarks: () => detectFaceLandmarks,
  detectFaceLandmarksTiny: () => detectFaceLandmarksTiny,
  detectLandmarks: () => detectLandmarks,
  detectSingleFace: () => detectSingleFace,
  draw: () => draw_exports,
  env: () => env,
  euclideanDistance: () => euclideanDistance,
  extendWithAge: () => extendWithAge,
  extendWithFaceDescriptor: () => extendWithFaceDescriptor,
  extendWithFaceDetection: () => extendWithFaceDetection,
  extendWithFaceExpressions: () => extendWithFaceExpressions,
  extendWithFaceLandmarks: () => extendWithFaceLandmarks,
  extendWithGender: () => extendWithGender,
  extractFaceTensors: () => extractFaceTensors,
  extractFaces: () => extractFaces,
  fetchImage: () => fetchImage,
  fetchJson: () => fetchJson,
  fetchNetWeights: () => fetchNetWeights,
  fetchOrThrow: () => fetchOrThrow,
  fetchVideo: () => fetchVideo,
  getContext2dOrThrow: () => getContext2dOrThrow,
  getMediaDimensions: () => getMediaDimensions,
  imageTensorToCanvas: () => imageTensorToCanvas,
  imageToSquare: () => imageToSquare,
  inverseSigmoid: () => inverseSigmoid,
  iou: () => iou,
  isMediaElement: () => isMediaElement,
  isMediaLoaded: () => isMediaLoaded,
  isWithAge: () => isWithAge,
  isWithFaceDetection: () => isWithFaceDetection,
  isWithFaceExpressions: () => isWithFaceExpressions,
  isWithFaceLandmarks: () => isWithFaceLandmarks,
  isWithGender: () => isWithGender,
  loadAgeGenderModel: () => loadAgeGenderModel,
  loadFaceDetectionModel: () => loadFaceDetectionModel,
  loadFaceExpressionModel: () => loadFaceExpressionModel,
  loadFaceLandmarkModel: () => loadFaceLandmarkModel,
  loadFaceLandmarkTinyModel: () => loadFaceLandmarkTinyModel,
  loadFaceRecognitionModel: () => loadFaceRecognitionModel,
  loadSsdMobilenetv1Model: () => loadSsdMobilenetv1Model,
  loadTinyFaceDetectorModel: () => loadTinyFaceDetectorModel,
  loadTinyYolov2Model: () => loadTinyYolov2Model,
  loadWeightMap: () => loadWeightMap,
  locateFaces: () => locateFaces,
  matchDimensions: () => matchDimensions,
  minBbox: () => minBbox,
  nets: () => nets,
  nonMaxSuppression: () => nonMaxSuppression,
  normalize: () => normalize,
  padToSquare: () => padToSquare,
  predictAgeAndGender: () => predictAgeAndGender,
  recognizeFaceExpressions: () => recognizeFaceExpressions,
  resizeResults: () => resizeResults,
  resolveInput: () => resolveInput,
  shuffleArray: () => shuffleArray,
  sigmoid: () => sigmoid,
  ssdMobilenetv1: () => ssdMobilenetv1,
  tf: () => tf42,
  tinyFaceDetector: () => tinyFaceDetector,
  tinyYolov2: () => tinyYolov2,
  toNetInput: () => toNetInput,
  utils: () => utils_exports,
  validateConfig: () => validateConfig,
  version: () => version2
});
var tf42 = __toModule(require_tfjs_esm());

// src/draw/index.ts
var draw_exports = {};
__export(draw_exports, {
  AnchorPosition: () => AnchorPosition,
  DrawBox: () => DrawBox,
  DrawBoxOptions: () => DrawBoxOptions,
  DrawFaceLandmarks: () => DrawFaceLandmarks,
  DrawFaceLandmarksOptions: () => DrawFaceLandmarksOptions,
  DrawTextField: () => DrawTextField,
  DrawTextFieldOptions: () => DrawTextFieldOptions,
  drawContour: () => drawContour,
  drawDetections: () => drawDetections,
  drawFaceExpressions: () => drawFaceExpressions,
  drawFaceLandmarks: () => drawFaceLandmarks
});

// src/draw/drawContour.ts
function drawContour(ctx, points, isClosed = false) {
  ctx.beginPath();
  points.slice(1).forEach(({ x, y }, prevIdx) => {
    const from = points[prevIdx];
    ctx.moveTo(from.x, from.y);
    ctx.lineTo(x, y);
  });
  if (isClosed) {
    const from = points[points.length - 1];
    const to = points[0];
    if (!from || !to) {
      return;
    }
    ctx.moveTo(from.x, from.y);
    ctx.lineTo(to.x, to.y);
  }
  ctx.stroke();
}

// src/utils/index.ts
var utils_exports = {};
__export(utils_exports, {
  computeReshapedDimensions: () => computeReshapedDimensions,
  getCenterPoint: () => getCenterPoint,
  isDimensions: () => isDimensions,
  isEven: () => isEven,
  isFloat: () => isFloat,
  isTensor: () => isTensor,
  isTensor1D: () => isTensor1D,
  isTensor2D: () => isTensor2D,
  isTensor3D: () => isTensor3D,
  isTensor4D: () => isTensor4D,
  isValidNumber: () => isValidNumber,
  isValidProbablitiy: () => isValidProbablitiy,
  range: () => range,
  round: () => round
});
var tf = __toModule(require_tfjs_esm());

// src/classes/Dimensions.ts
var Dimensions = class {
  constructor(width, height) {
    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;
  }
  get width() {
    return this._width;
  }
  get height() {
    return this._height;
  }
  reverse() {
    return new Dimensions(1 / this.width, 1 / this.height);
  }
};

// src/utils/index.ts
function isTensor(tensor2, dim) {
  return tensor2 instanceof tf.Tensor && tensor2.shape.length === dim;
}
function isTensor1D(tensor2) {
  return isTensor(tensor2, 1);
}
function isTensor2D(tensor2) {
  return isTensor(tensor2, 2);
}
function isTensor3D(tensor2) {
  return isTensor(tensor2, 3);
}
function isTensor4D(tensor2) {
  return isTensor(tensor2, 4);
}
function isFloat(num) {
  return num % 1 !== 0;
}
function isEven(num) {
  return num % 2 === 0;
}
function round(num, prec = 2) {
  const f = 10 ** prec;
  return Math.floor(num * f) / f;
}
function isDimensions(obj) {
  return obj && obj.width && obj.height;
}
function computeReshapedDimensions({ width, height }, inputSize) {
  const scale2 = inputSize / Math.max(height, width);
  return new Dimensions(Math.round(width * scale2), Math.round(height * scale2));
}
function getCenterPoint(pts) {
  return pts.reduce((sum, pt) => sum.add(pt), new Point(0, 0)).div(new Point(pts.length, pts.length));
}
function range(num, start, step) {
  return Array(num).fill(0).map((_, i) => start + i * step);
}
function isValidNumber(num) {
  return !!num && num !== Infinity && num !== -Infinity && !Number.isNaN(num) || num === 0;
}
function isValidProbablitiy(num) {
  return isValidNumber(num) && num >= 0 && num <= 1;
}

// src/classes/Point.ts
var Point = class {
  constructor(x, y) {
    this._x = x;
    this._y = y;
  }
  get x() {
    return this._x;
  }
  get y() {
    return this._y;
  }
  add(pt) {
    return new Point(this.x + pt.x, this.y + pt.y);
  }
  sub(pt) {
    return new Point(this.x - pt.x, this.y - pt.y);
  }
  mul(pt) {
    return new Point(this.x * pt.x, this.y * pt.y);
  }
  div(pt) {
    return new Point(this.x / pt.x, this.y / pt.y);
  }
  abs() {
    return new Point(Math.abs(this.x), Math.abs(this.y));
  }
  magnitude() {
    return Math.sqrt(this.x ** 2 + this.y ** 2);
  }
  floor() {
    return new Point(Math.floor(this.x), Math.floor(this.y));
  }
};

// src/classes/Box.ts
var Box = class {
  static isRect(rect) {
    return !!rect && [rect.x, rect.y, rect.width, rect.height].every(isValidNumber);
  }
  static assertIsValidBox(box, callee, allowNegativeDimensions = 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`);
    }
  }
  constructor(_box, allowNegativeDimensions = true) {
    const box = _box || {};
    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;
  }
  get x() {
    return this._x;
  }
  get y() {
    return this._y;
  }
  get width() {
    return this._width;
  }
  get height() {
    return this._height;
  }
  get left() {
    return this.x;
  }
  get top() {
    return this.y;
  }
  get right() {
    return this.x + this.width;
  }
  get bottom() {
    return this.y + this.height;
  }
  get area() {
    return this.width * this.height;
  }
  get topLeft() {
    return new Point(this.left, this.top);
  }
  get topRight() {
    return new Point(this.right, this.top);
  }
  get bottomLeft() {
    return new Point(this.left, this.bottom);
  }
  get bottomRight() {
    return new Point(this.right, this.bottom);
  }
  round() {
    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
    });
  }
  floor() {
    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
    });
  }
  toSquare() {
    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 });
  }
  rescale(s) {
    const scaleX = isDimensions(s) ? s.width : s;
    const scaleY = isDimensions(s) ? s.height : s;
    return new Box({
      x: this.x * scaleX,
      y: this.y * scaleY,
      width: this.width * scaleX,
      height: this.height * scaleY
    });
  }
  pad(padX, padY) {
    const [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
    });
  }
  clipAtImageBorders(imgWidth, imgHeight) {
    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();
  }
  shift(sx, sy) {
    const { width, height } = this;
    const x = this.x + sx;
    const y = this.y + sy;
    return new Box({
      x,
      y,
      width,
      height
    });
  }
  padAtBorders(imageHeight, imageWidth) {
    const w = this.width + 1;
    const h = this.height + 1;
    const dx = 1;
    const 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
    };
  }
  calibrate(region) {
    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();
  }
};

// src/classes/BoundingBox.ts
var BoundingBox = class extends Box {
  constructor(left, top, right, bottom, allowNegativeDimensions = false) {
    super({
      left,
      top,
      right,
      bottom
    }, allowNegativeDimensions);
  }
};

// src/classes/ObjectDetection.ts
var ObjectDetection = class {
  constructor(score, classScore, className, relativeBox, imageDims) {
    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);
  }
  get score() {
    return this._score;
  }
  get classScore() {
    return this._classScore;
  }
  get className() {
    return this._className;
  }
  get box() {
    return this._box;
  }
  get imageDims() {
    return this._imageDims;
  }
  get imageWidth() {
    return this.imageDims.width;
  }
  get imageHeight() {
    return this.imageDims.height;
  }
  get relativeBox() {
    return new Box(this._box).rescale(this.imageDims.reverse());
  }
  forSize(width, height) {
    return new ObjectDetection(this.score, this.classScore, this.className, this.relativeBox, { width, height });
  }
};

// src/classes/FaceDetection.ts
var FaceDetection = class extends ObjectDetection {
  constructor(score, relativeBox, imageDims) {
    super(score, score, "", relativeBox, imageDims);
  }
  forSize(width, height) {
    const { score, relativeBox, imageDims } = super.forSize(width, height);
    return new FaceDetection(score, relativeBox, imageDims);
  }
};

// src/ops/iou.ts
function iou(box1, box2, isIOU = true) {
  const width = Math.max(0, Math.min(box1.right, box2.right) - Math.max(box1.left, box2.left));
  const height = Math.max(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);
}

// src/ops/minBbox.ts
function minBbox(pts) {
  const xs = pts.map((pt) => pt.x);
  const ys = pts.map((pt) => pt.y);
  const minX = xs.reduce((min, x) => x < min ? x : min, Infinity);
  const minY = ys.reduce((min, y) => y < min ? y : min, Infinity);
  const maxX = xs.reduce((max, x) => max < x ? x : max, 0);
  const maxY = ys.reduce((max, y) => max < y ? y : max, 0);
  return new BoundingBox(minX, minY, maxX, maxY);
}

// src/ops/nonMaxSuppression.ts
function nonMaxSuppression(boxes, scores, iouThreshold, isIOU = true) {
  let indicesSortedByScore = scores.map((score, boxIndex) => ({ score, boxIndex })).sort((c1, c2) => c1.score - c2.score).map((c) => c.boxIndex);
  const pick = [];
  while (indicesSortedByScore.length > 0) {
    const curr = indicesSortedByScore.pop();
    pick.push(curr);
    const indices = indicesSortedByScore;
    const outputs = [];
    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;
}

// src/ops/normalize.ts
var tf2 = __toModule(require_tfjs_esm());
function normalize(x, meanRgb) {
  return tf2.tidy(() => {
    const [r, g, b] = meanRgb;
    const avg_r = tf2.fill([...x.shape.slice(0, 3), 1], r, "float32");
    const avg_g = tf2.fill([...x.shape.slice(0, 3), 1], g, "float32");
    const avg_b = tf2.fill([...x.shape.slice(0, 3), 1], b, "float32");
    const avg_rgb = tf2.concat([avg_r, avg_g, avg_b], 3);
    return tf2.sub(x, avg_rgb);
  });
}

// src/ops/padToSquare.ts
var tf3 = __toModule(require_tfjs_esm());
function padToSquare(imgTensor, isCenterImage = false) {
  return tf3.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 = (paddingAmountLocal) => {
      const paddingTensorShape = imgTensor.shape.slice();
      paddingTensorShape[paddingAxis] = paddingAmountLocal;
      return tf3.fill(paddingTensorShape, 0, "float32");
    };
    const paddingTensorAppend = createPaddingTensor(paddingAmount);
    const remainingPaddingAmount = dimDiff - paddingTensorAppend.shape[paddingAxis];
    const paddingTensorPrepend = isCenterImage && remainingPaddingAmount ? createPaddingTensor(remainingPaddingAmount) : null;
    const tensorsToStack = [
      paddingTensorPrepend,
      imgTensor,
      paddingTensorAppend
    ].filter((t) => !!t).map((t) => tf3.cast(t, "float32"));
    return tf3.concat(tensorsToStack, paddingAxis);
  });
}

// src/ops/shuffleArray.ts
function shuffleArray(inputArray) {
  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;
}

// src/ops/index.ts
function sigmoid(x) {
  return 1 / (1 + Math.exp(-x));
}
function inverseSigmoid(x) {
  return Math.log(x / (1 - x));
}

// src/classes/Rect.ts
var Rect = class extends Box {
  constructor(x, y, width, height, allowNegativeDimensions = false) {
    super({
      x,
      y,
      width,
      height
    }, allowNegativeDimensions);
  }
};

// src/classes/FaceLandmarks.ts
var relX = 0.5;
var relY = 0.43;
var relScale = 0.45;
var FaceLandmarks = class {
  constructor(relativeFaceLandmarkPositions, imgDims, shift = new Point(0, 0)) {
    const { width, height } = imgDims;
    this._imgDims = new Dimensions(width, height);
    this._shift = shift;
    this._positions = relativeFaceLandmarkPositions.map((pt) => pt.mul(new Point(width, height)).add(shift));
  }
  get shift() {
    return new Point(this._shift.x, this._shift.y);
  }
  get imageWidth() {
    return this._imgDims.width;
  }
  get imageHeight() {
    return this._imgDims.height;
  }
  get positions() {
    return this._positions;
  }
  get relativePositions() {
    return this._positions.map((pt) => pt.sub(this._shift).div(new Point(this.imageWidth, this.imageHeight)));
  }
  forSize(width, height) {
    return new this.constructor(this.relativePositions, { width, height });
  }
  shiftBy(x, y) {
    return new this.constructor(this.relativePositions, this._imgDims, new Point(x, y));
  }
  shiftByPoint(pt) {
    return this.shiftBy(pt.x, pt.y);
  }
  align(detection, options = {}) {
    if (detection) {
      const box = detection instanceof FaceDetection ? detection.box.floor() : new Box(detection);
      return this.shiftBy(box.x, box.y).align(null, options);
    }
    const { useDlibAlignment, minBoxPadding } = { useDlibAlignment: false, minBoxPadding: 0.2, ...options };
    if (useDlibAlignment) {
      return this.alignDlib();
    }
    return this.alignMinBbox(minBoxPadding);
  }
  alignDlib() {
    const centers = this.getRefPointsForAlignment();
    const [leftEyeCenter, rightEyeCenter, mouthCenter] = centers;
    const distToMouth = (pt) => mouthCenter.sub(pt).magnitude();
    const eyeToMouthDist = (distToMouth(leftEyeCenter) + distToMouth(rightEyeCenter)) / 2;
    const size = Math.floor(eyeToMouthDist / relScale);
    const refPoint = getCenterPoint(centers);
    const x = Math.floor(Math.max(0, refPoint.x - relX * size));
    const y = Math.floor(Math.max(0, refPoint.y - relY * size));
    return new Rect(x, y, Math.min(size, this.imageWidth + x), Math.min(size, this.imageHeight + y));
  }
  alignMinBbox(padding) {
    const box = minBbox(this.positions);
    return box.pad(box.width * padding, box.height * padding);
  }
  getRefPointsForAlignment() {
    throw new Error("getRefPointsForAlignment not implemented by base class");
  }
};

// src/classes/FaceLandmarks5.ts
var FaceLandmarks5 = class extends FaceLandmarks {
  getRefPointsForAlignment() {
    const pts = this.positions;
    return [
      pts[0],
      pts[1],
      getCenterPoint([pts[3], pts[4]])
    ];
  }
};

// src/classes/FaceLandmarks68.ts
var FaceLandmarks68 = class extends FaceLandmarks {
  getJawOutline() {
    return this.positions.slice(0, 17);
  }
  getLeftEyeBrow() {
    return this.positions.slice(17, 22);
  }
  getRightEyeBrow() {
    return this.positions.slice(22, 27);
  }
  getNose() {
    return this.positions.slice(27, 36);
  }
  getLeftEye() {
    return this.positions.slice(36, 42);
  }
  getRightEye() {
    return this.positions.slice(42, 48);
  }
  getMouth() {
    return this.positions.slice(48, 68);
  }
  getRefPointsForAlignment() {
    return [
      this.getLeftEye(),
      this.getRightEye(),
      this.getMouth()
    ].map(getCenterPoint);
  }
};

// src/classes/FaceMatch.ts
var FaceMatch = class {
  constructor(label, distance) {
    this._label = label;
    this._distance = distance;
  }
  get label() {
    return this._label;
  }
  get distance() {
    return this._distance;
  }
  toString(withDistance = true) {
    return `${this.label}${withDistance ? ` (${round(this.distance)})` : ""}`;
  }
};

// src/classes/LabeledBox.ts
var LabeledBox = class extends Box {
  static assertIsValidLabeledBox(box, callee) {
    Box.assertIsValidBox(box, callee);
    if (!isValidNumber(box.label)) {
      throw new Error(`${callee} - expected property label (${box.label}) to be a number`);
    }
  }
  constructor(box, label) {
    super(box);
    this._label = label;
  }
  get label() {
    return this._label;
  }
};

// src/classes/LabeledFaceDescriptors.ts
var LabeledFaceDescriptors = class {
  constructor(label, descriptors) {
    if (!(typeof label === "string")) {
      throw new Error("LabeledFaceDescriptors - constructor expected label to be a string");
    }
    if (!Array.isArray(descriptors) || descriptors.some((desc) => !(desc instanceof Float32Array))) {
      throw new Error("LabeledFaceDescriptors - constructor expected descriptors to be an array of Float32Array");
    }
    this._label = label;
    this._descriptors = descriptors;
  }
  get label() {
    return this._label;
  }
  get descriptors() {
    return this._descriptors;
  }
  toJSON() {
    return {
      label: this.label,
      descriptors: this.descriptors.map((d) => Array.from(d))
    };
  }
  static fromJSON(json) {
    const descriptors = json.descriptors.map((d) => new Float32Array(d));
    return new LabeledFaceDescriptors(json.label, descriptors);
  }
};

// src/classes/PredictedBox.ts
var PredictedBox = class extends LabeledBox {
  static assertIsValidPredictedBox(box, callee) {
    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]`);
    }
  }
  constructor(box, label, score, classScore) {
    super(box, label);
    this._score = score;
    this._classScore = classScore;
  }
  get score() {
    return this._score;
  }
  get classScore() {
    return this._classScore;
  }
};

// src/factories/WithFaceDetection.ts
function isWithFaceDetection(obj) {
  return obj.detection instanceof FaceDetection;
}
function extendWithFaceDetection(sourceObj, detection) {
  const extension = { detection };
  return { ...sourceObj, ...extension };
}

// src/env/createBrowserEnv.ts
function createBrowserEnv() {
  const fetch = window.fetch;
  if (!fetch)
    throw new Error("fetch - missing fetch implementation for browser environment");
  const readFile = () => {
    throw new Error("readFile - filesystem not available for browser environment");
  };
  return {
    Canvas: HTMLCanvasElement,
    CanvasRenderingContext2D,
    Image: HTMLImageElement,
    ImageData,
    Video: HTMLVideoElement,
    createCanvasElement: () => document.createElement("canvas"),
    createImageElement: () => document.createElement("img"),
    createVideoElement: () => document.createElement("video"),
    fetch,
    readFile
  };
}

// src/env/createFileSystem.ts
function createFileSystem(fs) {
  let requireFsError = "";
  if (!fs) {
    try {
      fs = require("fs");
    } catch (err) {
      requireFsError = err.toString();
    }
  }
  const readFile = fs ? (filePath) => new Promise((resolve, reject) => {
    fs.readFile(filePath, (err, buffer) => err ? reject(err) : resolve(buffer));
  }) : () => {
    throw new Error(`readFile - failed to require fs in nodejs environment with error: ${requireFsError}`);
  };
  return {
    readFile
  };
}

// src/env/createNodejsEnv.ts
function createNodejsEnv() {
  const Canvas = global["Canvas"] || global.HTMLCanvasElement;
  const Image = global.Image || global.HTMLImageElement;
  const Video = global["Video"] || global.HTMLVideoElement;
  const createCanvasElement = () => {
    if (Canvas)
      return new Canvas();
    throw new Error("createCanvasElement - missing Canvas implementation for nodejs environment");
  };
  const createImageElement = () => {
    if (Image)
      return new Image();
    throw new Error("createImageElement - missing Image implementation for nodejs environment");
  };
  const createVideoElement = () => {
    if (Video)
      return new Video();
    throw new Error("createVideoElement - missing Video implementation for nodejs environment");
  };
  const fetch = global.fetch;
  const fileSystem = createFileSystem();
  return {
    Canvas: Canvas || class {
    },
    CanvasRenderingContext2D: global.CanvasRenderingContext2D || class {
    },
    Image: Image || class {
    },
    ImageData: global.ImageData || class {
    },
    Video: global.HTMLVideoElement || class {
    },
    createCanvasElement,
    createImageElement,
    createVideoElement,
    fetch,
    ...fileSystem
  };
}

// src/env/isBrowser.ts
function isBrowser() {
  return typeof window === "object" && typeof document !== "undefined" && typeof HTMLImageElement !== "undefined" && typeof HTMLCanvasElement !== "undefined" && typeof HTMLVideoElement !== "undefined" && typeof ImageData !== "undefined" && typeof CanvasRenderingContext2D !== "undefined";
}

// src/env/isNodejs.ts
function isNodejs() {
  return typeof global === "object" && typeof require === "function" && typeof module !== "undefined" && typeof process !== "undefined" && !!process.version;
}

// src/env/index.ts
var environment;
function getEnv() {
  if (!environment) {
    throw new Error("getEnv - environment is not defined, check isNodejs() and isBrowser()");
  }
  return environment;
}
function setEnv(env2) {
  environment = env2;
}
function initialize() {
  if (isBrowser())
    return setEnv(createBrowserEnv());
  if (isNodejs())
    return setEnv(createNodejsEnv());
  return null;
}
function monkeyPatch(env2) {
  if (!environment) {
    initialize();
  }
  if (!environment) {
    throw new Error("monkeyPatch - environment is not defined, check isNodejs() and isBrowser()");
  }
  const { Canvas = environment.Canvas, Image = environment.Image } = env2;
  environment.Canvas = Canvas;
  environment.Image = Image;
  environment.createCanvasElement = env2.createCanvasElement || (() => new Canvas());
  environment.createImageElement = env2.createImageElement || (() => new Image());
  environment.ImageData = env2.ImageData || environment.ImageData;
  environment.Video = env2.Video || environment.Video;
  environment.fetch = env2.fetch || environment.fetch;
  environment.readFile = env2.readFile || environment.readFile;
}
var env = {
  getEnv,
  setEnv,
  initialize,
  createBrowserEnv,
  createFileSystem,
  createNodejsEnv,
  monkeyPatch,
  isBrowser,
  isNodejs
};
initialize();

// src/dom/resolveInput.ts
function resolveInput(arg) {
  if (!env.isNodejs() && typeof arg === "string") {
    return document.getElementById(arg);
  }
  return arg;
}

// src/dom/getContext2dOrThrow.ts
function getContext2dOrThrow(canvasArg) {
  const { Canvas, CanvasRenderingContext2D: CanvasRenderingContext2D2 } = env.getEnv();
  if (canvasArg instanceof CanvasRenderingContext2D2) {
    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;
}

// src/draw/DrawTextField.ts
var AnchorPosition;
(function(AnchorPosition2) {
  AnchorPosition2["TOP_LEFT"] = "TOP_LEFT";
  AnchorPosition2["TOP_RIGHT"] = "TOP_RIGHT";
  AnchorPosition2["BOTTOM_LEFT"] = "BOTTOM_LEFT";
  AnchorPosition2["BOTTOM_RIGHT"] = "BOTTOM_RIGHT";
})(AnchorPosition || (AnchorPosition = {}));
var DrawTextFieldOptions = class {
  constructor(options = {}) {
    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;
  }
};
var DrawTextField = class {
  constructor(text, anchor, options = {}) {
    this.text = typeof text === "string" ? [text] : text instanceof DrawTextField ? text.text : text;
    this.anchor = anchor;
    this.options = new DrawTextFieldOptions(options);
  }
  measureWidth(ctx) {
    const { padding } = this.options;
    return this.text.map((l) => ctx.measureText(l).width).reduce((w0, w1) => w0 < w1 ? w1 : w0, 0) + 2 * padding;
  }
  measureHeight() {
    const { fontSize, padding } = this.options;
    return this.text.length * fontSize + 2 * padding;
  }
  getUpperLeft(ctx, canvasDims) {
    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;
    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) {
    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);
    });
  }
};

// src/draw/DrawBox.ts
var DrawBoxOptions = class {
  constructor(options = {}) {
    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({ ...defaultDrawLabelOptions, ...drawLabelOptions });
  }
};
var DrawBox = class {
  constructor(box, options = {}) {
    this.box = new Box(box);
    this.options = new DrawBoxOptions(options);
  }
  draw(canvasArg) {
    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);
    }
  }
};

// src/draw/drawDetections.ts
function drawDetections(canvasArg, detections) {
  const detectionsArray = Array.isArray(detections) ? detections : [detections];
  detectionsArray.forEach((det) => {
    const score = det instanceof FaceDetection ? det.score : isWithFaceDetection(det) ? det.detection.score : void 0;
    const box = det instanceof FaceDetection ? det.box : isWithFaceDetection(det) ? det.detection.box : new Box(det);
    const label = score ? `${round(score)}` : void 0;
    new DrawBox(box, { label }).draw(canvasArg);
  });
}

// src/faceExpressionNet/FaceExpressionNet.ts
var tf18 = __toModule(require_tfjs_esm());

// src/dom/isMediaLoaded.ts
function isMediaLoaded(media) {
  const { Image, Video } = env.getEnv();
  return media instanceof Image && media.complete || media instanceof Video && media.readyState >= 3;
}

// src/dom/awaitMediaLoaded.ts
function awaitMediaLoaded(media) {
  return new Promise((resolve, reject) => {
    if (media instanceof env.getEnv().Canvas || isMediaLoaded(media))
      return resolve(null);
    function onError(e) {
      if (!e.currentTarget)
        return;
      e.currentTarget.removeEventListener("load", onLoad);
      e.currentTarget.removeEventListener("error", onError);
      reject(e);
    }
    function onLoad(e) {
      if (!e.currentTarget)
        return;
      e.currentTarget.removeEventListener("load", onLoad);
      e.currentTarget.removeEventListener("error", onError);
      resolve(e);
    }
    media.addEventListener("load", onLoad);
    media.addEventListener("error", onError);
  });
}

// src/dom/bufferToImage.ts
function bufferToImage(buf) {
  return new Promise((resolve, reject) => {
    if (!(buf instanceof Blob))
      reject(new Error("bufferToImage - expected buf to be of type: Blob"));
    const reader = new FileReader();
    reader.onload = () => {
      if (typeof reader.result !== "string")
        reject(new Error("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);
  });
}

// src/dom/getMediaDimensions.ts
function getMediaDimensions(input) {
  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);
}

// src/dom/createCanvas.ts
function createCanvas({ width, height }) {
  const { createCanvasElement } = env.getEnv();
  const canvas = createCanvasElement();
  canvas.width = width;
  canvas.height = height;
  return canvas;
}
function createCanvasFromMedia(media, dims) {
  const { ImageData: ImageData2 } = env.getEnv();
  if (!(media instanceof ImageData2) && !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 ImageData2) {
    getContext2dOrThrow(canvas).putImageData(media, 0, 0);
  } else {
    getContext2dOrThrow(canvas).drawImage(media, 0, 0, width, height);
  }
  return canvas;
}

// src/dom/imageTensorToCanvas.ts
var tf4 = __toModule(require_tfjs_esm());
async function imageTensorToCanvas(imgTensor, canvas) {
  const targetCanvas = canvas || env.getEnv().createCanvasElement();
  const [height, width, numChannels] = imgTensor.shape.slice(isTensor4D(imgTensor) ? 1 : 0);
  const imgTensor3D = tf4.tidy(() => imgTensor.as3D(height, width, numChannels).toInt());
  await tf4.browser.toPixels(imgTensor3D, targetCanvas);
  imgTensor3D.dispose();
  return targetCanvas;
}

// src/dom/isMediaElement.ts
function isMediaElement(input) {
  const { Image, Canvas, Video } = env.getEnv();
  return input instanceof Image || input instanceof Canvas || input instanceof Video;
}

// src/dom/NetInput.ts
var tf5 = __toModule(require_tfjs_esm());

// src/dom/imageToSquare.ts
function imageToSquare(input, inputSize, centerImage = false) {
  const { Image, Canvas } = env.getEnv();
  if (!(input instanceof Image || input instanceof Canvas)) {
    throw new Error("imageToSquare - expected arg0 to be HTMLImageElement | HTMLCanvasElement");
  }
  if (inputSize <= 0)
    return createCanvas({ width: 1, height: 1 });
  const dims = getMediaDimensions(input);
  const scale2 = inputSize / Math.max(dims.height, dims.width);
  const width = scale2 * dims.width;
  const height = scale2 * 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;
  if (inputCanvas.width > 0 && inputCanvas.height > 0)
    getContext2dOrThrow(targetCanvas).drawImage(inputCanvas, dx, dy, width, height);
  return targetCanvas;
}

// src/dom/NetInput.ts
var NetInput = class {
  constructor(inputs, treatAsBatchInput = false) {
    this._imageTensors = [];
    this._canvases = [];
    this._treatAsBatchInput = false;
    this._inputDimensions = [];
    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];
    });
  }
  get imageTensors() {
    return this._imageTensors;
  }
  get canvases() {
    return this._canvases;
  }
  get isBatchInput() {
    return this.batchSize > 1 || this._treatAsBatchInput;
  }
  get batchSize() {
    return this._batchSize;
  }
  get inputDimensions() {
    return this._inputDimensions;
  }
  get inputSize() {
    return this._inputSize;
  }
  get reshapedInputDimensions() {
    return range(this.batchSize, 0, 1).map((_, batchIdx) => this.getReshapedInputDimensions(batchIdx));
  }
  getInput(batchIdx) {
    return this.canvases[batchIdx] || this.imageTensors[batchIdx];
  }
  getInputDimensions(batchIdx) {
    return this._inputDimensions[batchIdx];
  }
  getInputHeight(batchIdx) {
    return this._inputDimensions[batchIdx][0];
  }
  getInputWidth(batchIdx) {
    return this._inputDimensions[batchIdx][1];
  }
  getReshapedInputDimensions(batchIdx) {
    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);
  }
  toBatchTensor(inputSize, isCenterInputs = true) {
    this._inputSize = inputSize;
    return tf5.tidy(() => {
      const inputTensors = range(this.batchSize, 0, 1).map((batchIdx) => {
        const input = this.getInput(batchIdx);
        if (input instanceof tf5.Tensor) {
          let imgTensor = isTensor4D(input) ? input : tf5.expandDims(input);
          imgTensor = padToSquare(imgTensor, isCenterInputs);
          if (imgTensor.shape[1] !== inputSize || imgTensor.shape[2] !== inputSize) {
            imgTensor = tf5.image.resizeBilinear(imgTensor, [inputSize, inputSize], false, false);
          }
          return imgTensor.as3D(inputSize, inputSize, 3);
        }
        if (input instanceof env.getEnv().Canvas) {
          return tf5.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 = tf5.stack(inputTensors.map((t) => tf5.cast(t, "float32"))).as4D(this.batchSize, inputSize, inputSize, 3);
      return batchTensor;
    });
  }
};

// src/dom/toNetInput.ts
async function toNetInput(inputs) {
  if (inputs instanceof NetInput)
    return inputs;
  const inputArgArray = Array.isArray(inputs) ? inputs : [inputs];
  if (!inputArgArray.length)
    throw new Error("toNetInput - empty array passed as input");
  const getIdxHint = (idx) => 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)) {
      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`);
    }
  });
  await Promise.all(inputArray.map((input) => isMediaElement(input) && awaitMediaLoaded(input)));
  return new NetInput(inputArray, Array.isArray(inputs));
}

// src/dom/extractFaces.ts
async function extractFaces(input, detections) {
  const { Canvas } = env.getEnv();
  let canvas = input;
  if (!(input instanceof Canvas)) {
    const netInput = await toNetInput(input);
    if (netInput.batchSize > 1)
      throw new Error("extractFaces - batchSize > 1 not supported");
    const tensorOrCanvas = netInput.getInput(0);
    canvas = tensorOrCanvas instanceof Canvas ? tensorOrCanvas : await imageTensorToCanvas(tensorOrCanvas);
  }
  const ctx = getContext2dOrThrow(canvas);
  const boxes = detections.map((det) => det instanceof FaceDetection ? det.forSize(canvas.width, canvas.height).box.floor() : det).map((box) => box.clipAtImageBorders(canvas.width, canvas.height));
  return boxes.map(({ x, y, width, height }) => {
    const faceImg = createCanvas({ width, height });
    if (width > 0 && height > 0)
      getContext2dOrThrow(faceImg).putImageData(ctx.getImageData(x, y, width, height), 0, 0);
    return faceImg;
  });
}

// src/dom/extractFaceTensors.ts
var tf6 = __toModule(require_tfjs_esm());
async function extractFaceTensors(imageTensor, detections) {
  if (!isTensor3D(imageTensor) && !isTensor4D(imageTensor)) {
    throw new Error("extractFaceTensors - expected image tensor to be 3D or 4D");
  }
  if (isTensor4D(imageTensor) && imageTensor.shape[0] > 1) {
    throw new Error("extractFaceTensors - batchSize > 1 not supported");
  }
  return tf6.tidy(() => {
    const [imgHeight, imgWidth, numChannels] = imageTensor.shape.slice(isTensor4D(imageTensor) ? 1 : 0);
    const boxes = detections.map((det) => det instanceof FaceDetection ? det.forSize(imgWidth, imgHeight).box : det).map((box) => box.clipAtImageBorders(imgWidth, imgHeight));
    const faceTensors = boxes.map(({
      x,
      y,
      width,
      height
    }) => tf6.slice3d(imageTensor.as3D(imgHeight, imgWidth, numChannels), [y, x, 0], [height, width, numChannels]));
    return faceTensors;
  });
}

// src/dom/fetchOrThrow.ts
async function fetchOrThrow(url, init) {
  const { fetch } = env.getEnv();
  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;
}

// src/dom/fetchImage.ts
async function fetchImage(uri) {
  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);
}

// src/dom/fetchJson.ts
async function fetchJson(uri) {
  return (await fetchOrThrow(uri)).json();
}

// src/dom/fetchNetWeights.ts
async function fetchNetWeights(uri) {
  return new Float32Array(await (await fetchOrThrow(uri)).arrayBuffer());
}

// src/dom/bufferToVideo.ts
function bufferToVideo(buf) {
  return new Promise((resolve, reject) => {
    if (!(buf instanceof Blob))
      reject(new Error("bufferToVideo - expected buf to be of type: Blob"));
    const video = env.getEnv().createVideoElement();
    video.oncanplay = () => resolve(video);
    video.onerror = reject;
    video.playsInline = true;
    video.muted = true;
    video.src = URL.createObjectURL(buf);
    video.play();
  });
}

// src/dom/fetchVideo.ts
async function fetchVideo(uri) {
  const res = await fetchOrThrow(uri);
  const blob = await res.blob();
  if (!blob.type.startsWith("video/")) {
    throw new Error(`fetchVideo - expected blob type to be of type video/*, instead have: ${blob.type}, for url: ${res.url}`);
  }
  return bufferToVideo(blob);
}

// src/dom/loadWeightMap.ts
var tf7 = __toModule(require_tfjs_esm());

// src/common/getModelUris.ts
function getModelUris(uri, defaultModelName) {
  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}`
  };
}

// src/dom/loadWeightMap.ts
async function loadWeightMap(uri, defaultModelName) {
  const { manifestUri, modelBaseUri } = getModelUris(uri, defaultModelName);
  const manifest = await fetchJson(manifestUri);
  return tf7.io.loadWeights(manifest, modelBaseUri);
}

// src/dom/matchDimensions.ts
function matchDimensions(input, reference, useMediaDimensions = false) {
  const { width, height } = useMediaDimensions ? getMediaDimensions(reference) : reference;
  input.width = width;
  input.height = height;
  return { width, height };
}

// src/faceFeatureExtractor/FaceFeatureExtractor.ts
var tf15 = __toModule(require_tfjs_esm());

// src/NeuralNetwork.ts
var tf8 = __toModule(require_tfjs_esm());
var NeuralNetwork = class {
  constructor(name) {
    this._params = void 0;
    this._paramMappings = [];
    this._name = name;
  }
  get params() {
    return this._params;
  }
  get paramMappings() {
    return this._paramMappings;
  }
  get isLoaded() {
    return !!this.params;
  }
  getParamFromPath(paramPath) {
    const { obj, objProp } = this.traversePropertyPath(paramPath);
    return obj[objProp];
  }
  reassignParamFromPath(paramPath, tensor2) {
    const { obj, objProp } = this.traversePropertyPath(paramPath);
    obj[objProp].dispose();
    obj[objProp] = tensor2;
  }
  getParamList() {
    return this._paramMappings.map(({ paramPath }) => ({
      path: paramPath,
      tensor: this.getParamFromPath(paramPath)
    }));
  }
  getTrainableParams() {
    return this.getParamList().filter((param) => param.tensor instanceof tf8.Variable);
  }
  getFrozenParams() {
    return this.getParamList().filter((param) => !(param.tensor instanceof tf8.Variable));
  }
  variable() {
    this.getFrozenParams().forEach(({ path, tensor: tensor2 }) => {
      this.reassignParamFromPath(path, tensor2.variable());
    });
  }
  freeze() {
    this.getTrainableParams().forEach(({ path, tensor: variable }) => {
      const tensor2 = tf8.tensor(variable.dataSync());
      variable.dispose();
      this.reassignParamFromPath(path, tensor2);
    });
  }
  dispose(throwOnRedispose = 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 = void 0;
  }
  serializeParams() {
    return new Float32Array(this.getParamList().map(({ tensor: tensor2 }) => Array.from(tensor2.dataSync())).reduce((flat, arr) => flat.concat(arr)));
  }
  async load(weightsOrUrl) {
    if (weightsOrUrl instanceof Float32Array) {
      this.extractWeights(weightsOrUrl);
      return;
    }
    await this.loadFromUri(weightsOrUrl);
  }
  async loadFromUri(uri) {
    if (uri && typeof uri !== "string") {
      throw new Error(`${this._name}.loadFromUri - expected model uri`);
    }
    const weightMap = await loadWeightMap(uri, this.getDefaultModelName());
    this.loadFromWeightMap(weightMap);
  }
  async loadFromDisk(filePath) {
    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) => Promise.all(filePaths.map((fp) => readFile(fp).then((buf) => buf.buffer)));
    const loadWeights = tf8.io.weightsLoaderFactory(fetchWeightsFromDisk);
    const manifest = JSON.parse((await readFile(manifestUri)).toString());
    const weightMap = await loadWeights(manifest, modelBaseUri);
    this.loadFromWeightMap(weightMap);
  }
  loadFromWeightMap(weightMap) {
    const { paramMappings, params } = this.extractParamsFromWeightMap(weightMap);
    this._paramMappings = paramMappings;
    this._params = params;
  }
  extractWeights(weights) {
    const { paramMappings, params } = this.extractParams(weights);
    this._paramMappings = paramMappings;
    this._params = params;
  }
  traversePropertyPath(paramPath) {
    if (!this.params) {
      throw new Error("traversePropertyPath - model has no loaded params");
    }
    const result = paramPath.split("/").reduce((res, objProp2) => {
      if (!res.nextObj.hasOwnProperty(objProp2)) {
        throw new Error(`traversePropertyPath - object does not have property ${objProp2}, for path ${paramPath}`);
      }
      return { obj: res.nextObj, objProp: objProp2, nextObj: res.nextObj[objProp2] };
    }, { nextObj: this.params });
    const { obj, objProp } = result;
    if (!obj || !objProp || !(obj[objProp] instanceof tf8.Tensor)) {
      throw new Error(`traversePropertyPath - parameter is not a tensor, for path ${paramPath}`);
    }
    return { obj, objProp };
  }
};

// src/faceFeatureExtractor/denseBlock.ts
var tf10 = __toModule(require_tfjs_esm());

// src/common/depthwiseSeparableConv.ts
var tf9 = __toModule(require_tfjs_esm());
function depthwiseSeparableConv(x, params, stride) {
  return tf9.tidy(() => {
    let out = tf9.separableConv2d(x, params.depthwise_filter, params.pointwise_filter, stride, "same");
    out = tf9.add(out, params.bias);
    return out;
  });
}

// src/faceFeatureExtractor/denseBlock.ts
function denseBlock3(x, denseBlockParams, isFirstLayer = false) {
  return tf10.tidy(() => {
    const out1 = tf10.relu(isFirstLayer ? tf10.add(tf10.conv2d(x, denseBlockParams.conv0.filters, [2, 2], "same"), denseBlockParams.conv0.bias) : depthwiseSeparableConv(x, denseBlockParams.conv0, [2, 2]));
    const out2 = depthwiseSeparableConv(out1, denseBlockParams.conv1, [1, 1]);
    const in3 = tf10.relu(tf10.add(out1, out2));
    const out3 = depthwiseSeparableConv(in3, denseBlockParams.conv2, [1, 1]);
    return tf10.relu(tf10.add(out1, tf10.add(out2, out3)));
  });
}
function denseBlock4(x, denseBlockParams, isFirstLayer = false, isScaleDown = true) {
  return tf10.tidy(() => {
    const out1 = tf10.relu(isFirstLayer ? tf10.add(tf10.conv2d(x, denseBlockParams.conv0.filters, isScaleDown ? [2, 2] : [1, 1], "same"), denseBlockParams.conv0.bias) : depthwiseSeparableConv(x, denseBlockParams.conv0, isScaleDown ? [2, 2] : [1, 1]));
    const out2 = depthwiseSeparableConv(out1, denseBlockParams.conv1, [1, 1]);
    const in3 = tf10.relu(tf10.add(out1, out2));
    const out3 = depthwiseSeparableConv(in3, denseBlockParams.conv2, [1, 1]);
    const in4 = tf10.relu(tf10.add(out1, tf10.add(out2, out3)));
    const out4 = depthwiseSeparableConv(in4, denseBlockParams.conv3, [1, 1]);
    return tf10.relu(tf10.add(out1, tf10.add(out2, tf10.add(out3, out4))));
  });
}

// src/common/convLayer.ts
var tf11 = __toModule(require_tfjs_esm());
function convLayer(x, params, padding = "same", withRelu = false) {
  return tf11.tidy(() => {
    const out = tf11.add(tf11.conv2d(x, params.filters, [1, 1], padding), params.bias);
    return withRelu ? tf11.relu(out) : out;
  });
}

// src/common/disposeUnusedWeightTensors.ts
function disposeUnusedWeightTensors(weightMap, paramMappings) {
  Object.keys(weightMap).forEach((path) => {
    if (!paramMappings.some((pm) => pm.originalPath === path)) {
      weightMap[path].dispose();
    }
  });
}

// src/common/extractConvParamsFactory.ts
var tf12 = __toModule(require_tfjs_esm());
function extractConvParamsFactory(extractWeights, paramMappings) {
  return (channelsIn, channelsOut, filterSize, mappedPrefix) => {
    const filters = tf12.tensor4d(extractWeights(channelsIn * channelsOut * filterSize * filterSize), [filterSize, filterSize, channelsIn, channelsOut]);
    const bias = tf12.tensor1d(extractWeights(channelsOut));
    paramMappings.push({ paramPath: `${mappedPrefix}/filters` }, { paramPath: `${mappedPrefix}/bias` });
    return { filters, bias };
  };
}

// src/common/extractFCParamsFactory.ts
var tf13 = __toModule(require_tfjs_esm());
function extractFCParamsFactory(extractWeights, paramMappings) {
  return (channelsIn, channelsOut, mappedPrefix) => {
    const fc_weights = tf13.tensor2d(extractWeights(channelsIn * channelsOut), [channelsIn, channelsOut]);
    const fc_bias = tf13.tensor1d(extractWeights(channelsOut));
    paramMappings.push({ paramPath: `${mappedPrefix}/weights` }, { paramPath: `${mappedPrefix}/bias` });
    return {
      weights: fc_weights,
      bias: fc_bias
    };
  };
}

// src/common/extractSeparableConvParamsFactory.ts
var tf14 = __toModule(require_tfjs_esm());

// src/common/types.ts
var SeparableConvParams = class {
  constructor(depthwise_filter, pointwise_filter, bias) {
    this.depthwise_filter = depthwise_filter;
    this.pointwise_filter = pointwise_filter;
    this.bias = bias;
  }
};

// src/common/extractSeparableConvParamsFactory.ts
function extractSeparableConvParamsFactory(extractWeights, paramMappings) {
  return (channelsIn, channelsOut, mappedPrefix) => {
    const depthwise_filter = tf14.tensor4d(extractWeights(3 * 3 * channelsIn), [3, 3, channelsIn, 1]);
    const pointwise_filter = tf14.tensor4d(extractWeights(channelsIn * channelsOut), [1, 1, channelsIn, channelsOut]);
    const bias = tf14.tensor1d(extractWeights(channelsOut));
    paramMappings.push({ paramPath: `${mappedPrefix}/depthwise_filter` }, { paramPath: `${mappedPrefix}/pointwise_filter` }, { paramPath: `${mappedPrefix}/bias` });
    return new SeparableConvParams(depthwise_filter, pointwise_filter, bias);
  };
}
function loadSeparableConvParamsFactory(extractWeightEntry) {
  return (prefix) => {
    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);
  };
}

// src/common/extractWeightEntryFactory.ts
function extractWeightEntryFactory(weightMap, paramMappings) {
  return (originalPath, paramRank, mappedPath) => {
    const tensor2 = weightMap[originalPath];
    if (!isTensor(tensor2, paramRank)) {
      throw new Error(`expected weightMap[${originalPath}] to be a Tensor${paramRank}D, instead have ${tensor2}`);
    }
    paramMappings.push({ originalPath, paramPath: mappedPath || originalPath });
    return tensor2;
  };
}

// src/common/extractWeightsFactory.ts
function extractWeightsFactory(weights) {
  let remainingWeights = weights;
  function extractWeights(numWeights) {
    const ret = remainingWeights.slice(0, numWeights);
    remainingWeights = remainingWeights.slice(numWeights);
    return ret;
  }
  function getRemainingWeights() {
    return remainingWeights;
  }
  return {
    extractWeights,
    getRemainingWeights
  };
}

// src/faceFeatureExtractor/extractorsFactory.ts
function extractorsFactory(extractWeights, paramMappings) {
  const extractConvParams = extractConvParamsFactory(extractWeights, paramMappings);
  const extractSeparableConvParams = extractSeparableConvParamsFactory(extractWeights, paramMappings);
  function extractDenseBlock3Params(channelsIn, channelsOut, mappedPrefix, isFirstLayer = false) {
    const conv0 = isFirstLayer ? extractConvParams(channelsIn, channelsOut, 3, `${mappedPrefix}/conv0`) : extractSeparableConvParams(channelsIn, channelsOut, `${mappedPrefix}/conv0`);
    const conv1 = extractSeparableConvParams(channelsOut, channelsOut, `${mappedPrefix}/conv1`);
    const conv22 = extractSeparableConvParams(channelsOut, channelsOut, `${mappedPrefix}/conv2`);
    return { conv0, conv1, conv2: conv22 };
  }
  function extractDenseBlock4Params(channelsIn, channelsOut, mappedPrefix, isFirstLayer = false) {
    const { conv0, conv1, conv2: conv22 } = extractDenseBlock3Params(channelsIn, channelsOut, mappedPrefix, isFirstLayer);
    const conv3 = extractSeparableConvParams(channelsOut, channelsOut, `${mappedPrefix}/conv3`);
    return {
      conv0,
      conv1,
      conv2: conv22,
      conv3
    };
  }
  return {
    extractDenseBlock3Params,
    extractDenseBlock4Params
  };
}

// src/faceFeatureExtractor/extractParams.ts
function extractParams(weights) {
  const paramMappings = [];
  const {
    extractWeights,
    getRemainingWeights
  } = extractWeightsFactory(weights);
  const {
    extractDenseBlock4Params
  } = extractorsFactory(extractWeights, paramMappings);
  const dense0 = extractDenseBlock4Params(3, 32, "dense0", true);
  const dense1 = extractDenseBlock4Params(32, 64, "dense1");
  const dense2 = extractDenseBlock4Params(64, 128, "dense2");
  const dense3 = extractDenseBlock4Params(128, 256, "dense3");
  if (getRemainingWeights().length !== 0) {
    throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
  }
  return {
    paramMappings,
    params: {
      dense0,
      dense1,
      dense2,
      dense3
    }
  };
}

// src/common/loadConvParamsFactory.ts
function loadConvParamsFactory(extractWeightEntry) {
  return (prefix) => {
    const filters = extractWeightEntry(`${prefix}/filters`, 4);
    const bias = extractWeightEntry(`${prefix}/bias`, 1);
    return { filters, bias };
  };
}

// src/faceFeatureExtractor/loadParamsFactory.ts
function loadParamsFactory(weightMap, paramMappings) {
  const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings);
  const extractConvParams = loadConvParamsFactory(extractWeightEntry);
  const extractSeparableConvParams = loadSeparableConvParamsFactory(extractWeightEntry);
  function extractDenseBlock3Params(prefix, isFirstLayer = false) {
    const conv0 = isFirstLayer ? extractConvParams(`${prefix}/conv0`) : extractSeparableConvParams(`${prefix}/conv0`);
    const conv1 = extractSeparableConvParams(`${prefix}/conv1`);
    const conv22 = extractSeparableConvParams(`${prefix}/conv2`);
    return { conv0, conv1, conv2: conv22 };
  }
  function extractDenseBlock4Params(prefix, isFirstLayer = false) {
    const conv0 = isFirstLayer ? extractConvParams(`${prefix}/conv0`) : extractSeparableConvParams(`${prefix}/conv0`);
    const conv1 = extractSeparableConvParams(`${prefix}/conv1`);
    const conv22 = extractSeparableConvParams(`${prefix}/conv2`);
    const conv3 = extractSeparableConvParams(`${prefix}/conv3`);
    return {
      conv0,
      conv1,
      conv2: conv22,
      conv3
    };
  }
  return {
    extractDenseBlock3Params,
    extractDenseBlock4Params
  };
}

// src/faceFeatureExtractor/extractParamsFromWeightMap.ts
function extractParamsFromWeightMap(weightMap) {
  const paramMappings = [];
  const {
    extractDenseBlock4Params
  } = loadParamsFactory(weightMap, paramMappings);
  const params = {
    dense0: extractDenseBlock4Params("dense0", true),
    dense1: extractDenseBlock4Params("dense1"),
    dense2: extractDenseBlock4Params("dense2"),
    dense3: extractDenseBlock4Params("dense3")
  };
  disposeUnusedWeightTensors(weightMap, paramMappings);
  return { params, paramMappings };
}

// src/faceFeatureExtractor/FaceFeatureExtractor.ts
var FaceFeatureExtractor = class extends NeuralNetwork {
  constructor() {
    super("FaceFeatureExtractor");
  }
  forwardInput(input) {
    const { params } = this;
    if (!params) {
      throw new Error("FaceFeatureExtractor - load model before inference");
    }
    return tf15.tidy(() => {
      const batchTensor = tf15.cast(input.toBatchTensor(112, true), "float32");
      const meanRgb = [122.782, 117.001, 104.298];
      const normalized = normalize(batchTensor, meanRgb).div(255);
      let out = denseBlock4(normalized, params.dense0, true);
      out = denseBlock4(out, params.dense1);
      out = denseBlock4(out, params.dense2);
      out = denseBlock4(out, params.dense3);
      out = tf15.avgPool(out, [7, 7], [2, 2], "valid");
      return out;
    });
  }
  async forward(input) {
    return this.forwardInput(await toNetInput(input));
  }
  getDefaultModelName() {
    return "face_feature_extractor_model";
  }
  extractParamsFromWeightMap(weightMap) {
    return extractParamsFromWeightMap(weightMap);
  }
  extractParams(weights) {
    return extractParams(weights);
  }
};

// src/faceProcessor/FaceProcessor.ts
var tf17 = __toModule(require_tfjs_esm());

// src/common/fullyConnectedLayer.ts
var tf16 = __toModule(require_tfjs_esm());
function fullyConnectedLayer(x, params) {
  return tf16.tidy(() => tf16.add(tf16.matMul(x, params.weights), params.bias));
}

// src/faceProcessor/extractParams.ts
function extractParams2(weights, channelsIn, channelsOut) {
  const paramMappings = [];
  const {
    extractWeights,
    getRemainingWeights
  } = extractWeightsFactory(weights);
  const extractFCParams = extractFCParamsFactory(extractWeights, paramMappings);
  const fc = extractFCParams(channelsIn, channelsOut, "fc");
  if (getRemainingWeights().length !== 0) {
    throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
  }
  return {
    paramMappings,
    params: { fc }
  };
}

// src/faceProcessor/extractParamsFromWeightMap.ts
function extractParamsFromWeightMap2(weightMap) {
  const paramMappings = [];
  const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings);
  function extractFcParams(prefix) {
    const weights = extractWeightEntry(`${prefix}/weights`, 2);
    const bias = extractWeightEntry(`${prefix}/bias`, 1);
    return { weights, bias };
  }
  const params = {
    fc: extractFcParams("fc")
  };
  disposeUnusedWeightTensors(weightMap, paramMappings);
  return { params, paramMappings };
}

// src/faceProcessor/util.ts
function seperateWeightMaps(weightMap) {
  const featureExtractorMap = {};
  const classifierMap = {};
  Object.keys(weightMap).forEach((key) => {
    const map = key.startsWith("fc") ? classifierMap : featureExtractorMap;
    map[key] = weightMap[key];
  });
  return { featureExtractorMap, classifierMap };
}

// src/faceProcessor/FaceProcessor.ts
var FaceProcessor = class extends NeuralNetwork {
  constructor(_name, faceFeatureExtractor) {
    super(_name);
    this._faceFeatureExtractor = faceFeatureExtractor;
  }
  get faceFeatureExtractor() {
    return this._faceFeatureExtractor;
  }
  runNet(input) {
    const { params } = this;
    if (!params) {
      throw new Error(`${this._name} - load model before inference`);
    }
    return tf17.tidy(() => {
      const bottleneckFeatures = input instanceof NetInput ? this.faceFeatureExtractor.forwardInput(input) : input;
      return fullyConnectedLayer(bottleneckFeatures.as2D(bottleneckFeatures.shape[0], -1), params.fc);
    });
  }
  dispose(throwOnRedispose = true) {
    this.faceFeatureExtractor.dispose(throwOnRedispose);
    super.dispose(throwOnRedispose);
  }
  loadClassifierParams(weights) {
    const { params, paramMappings } = this.extractClassifierParams(weights);
    this._params = params;
    this._paramMappings = paramMappings;
  }
  extractClassifierParams(weights) {
    return extractParams2(weights, this.getClassifierChannelsIn(), this.getClassifierChannelsOut());
  }
  extractParamsFromWeightMap(weightMap) {
    const { featureExtractorMap, classifierMap } = seperateWeightMaps(weightMap);
    this.faceFeatureExtractor.loadFromWeightMap(featureExtractorMap);
    return extractParamsFromWeightMap2(classifierMap);
  }
  extractParams(weights) {
    const cIn = this.getClassifierChannelsIn();
    const cOut = this.getClassifierChannelsOut();
    const classifierWeightSize = cOut * cIn + cOut;
    const featureExtractorWeights = weights.slice(0, weights.length - classifierWeightSize);
    const classifierWeights = weights.slice(weights.length - classifierWeightSize);
    this.faceFeatureExtractor.extractWeights(featureExtractorWeights);
    return this.extractClassifierParams(classifierWeights);
  }
};

// src/faceExpressionNet/FaceExpressions.ts
var FACE_EXPRESSION_LABELS = ["neutral", "happy", "sad", "angry", "fearful", "disgusted", "surprised"];
var FaceExpressions = class {
  constructor(probabilities) {
    if (probabilities.length !== 7) {
      throw new Error(`FaceExpressions.constructor - expected probabilities.length to be 7, have: ${probabilities.length}`);
    }
    FACE_EXPRESSION_LABELS.forEach((expression, idx) => {
      this[expression] = probabilities[idx];
    });
  }
  asSortedArray() {
    return FACE_EXPRESSION_LABELS.map((expression) => ({ expression, probability: this[expression] })).sort((e0, e1) => e1.probability - e0.probability);
  }
};

// src/faceExpressionNet/FaceExpressionNet.ts
var FaceExpressionNet = class extends FaceProcessor {
  constructor(faceFeatureExtractor = new FaceFeatureExtractor()) {
    super("FaceExpressionNet", faceFeatureExtractor);
  }
  forwardInput(input) {
    return tf18.tidy(() => tf18.softmax(this.runNet(input)));
  }
  async forward(input) {
    return this.forwardInput(await toNetInput(input));
  }
  async predictExpressions(input) {
    const netInput = await toNetInput(input);
    const out = await this.forwardInput(netInput);
    const probabilitesByBatch = await Promise.all(tf18.unstack(out).map(async (t) => {
      const data = t.dataSync();
      t.dispose();
      return data;
    }));
    out.dispose();
    const predictionsByBatch = probabilitesByBatch.map((probabilites) => new FaceExpressions(probabilites));
    return netInput.isBatchInput ? predictionsByBatch : predictionsByBatch[0];
  }
  getDefaultModelName() {
    return "face_expression_model";
  }
  getClassifierChannelsIn() {
    return 256;
  }
  getClassifierChannelsOut() {
    return 7;
  }
};

// src/factories/WithFaceExpressions.ts
function isWithFaceExpressions(obj) {
  return obj.expressions instanceof FaceExpressions;
}
function extendWithFaceExpressions(sourceObj, expressions) {
  const extension = { expressions };
  return { ...sourceObj, ...extension };
}

// src/draw/drawFaceExpressions.ts
function drawFaceExpressions(canvasArg, faceExpressions, minConfidence = 0.1, textFieldAnchor) {
  const faceExpressionsArray = Array.isArray(faceExpressions) ? faceExpressions : [faceExpressions];
  faceExpressionsArray.forEach((e) => {
    const expr = e instanceof FaceExpressions ? e : isWithFaceExpressions(e) ? e.expressions : void 0;
    if (!expr) {
      throw new Error("drawFaceExpressions - expected faceExpressions to be FaceExpressions | WithFaceExpressions<{}> or array thereof");
    }
    const sorted = expr.asSortedArray();
    const resultsToDisplay = sorted.filter((exprLocal) => exprLocal.probability > minConfidence);
    const anchor = isWithFaceDetection(e) ? e.detection.box.bottomLeft : textFieldAnchor || new Point(0, 0);
    const drawTextField = new DrawTextField(resultsToDisplay.map((exprLocal) => `${exprLocal.expression} (${round(exprLocal.probability)})`), anchor);
    drawTextField.draw(canvasArg);
  });
}

// src/factories/WithFaceLandmarks.ts
function isWithFaceLandmarks(obj) {
  return isWithFaceDetection(obj) && obj["landmarks"] instanceof FaceLandmarks && obj["unshiftedLandmarks"] instanceof FaceLandmarks && obj["alignedRect"] instanceof FaceDetection;
}
function calculateFaceAngle(mesh) {
  const radians = (a1, a2, b1, b2) => Math.atan2(b2 - a2, b1 - a1) % Math.PI;
  const degrees = (theta) => theta * 180 / Math.PI;
  const angle = { roll: void 0, pitch: void 0, yaw: void 0 };
  if (!mesh || !mesh._positions || mesh._positions.length !== 68)
    return angle;
  const pt = mesh._positions;
  angle.roll = -radians(pt[36]._x, pt[36]._y, pt[45]._x, pt[45]._y);
  angle.pitch = radians(0, Math.abs(pt[0]._x - pt[30]._x) / pt[30]._x, Math.PI, Math.abs(pt[16]._x - pt[30]._x) / pt[30]._x);
  const bottom = pt.reduce((prev, cur) => prev < cur._y ? prev : cur._y, Infinity);
  const top = pt.reduce((prev, cur) => prev > cur._y ? prev : cur._y, -Infinity);
  angle.yaw = Math.PI * (mesh._imgDims._height / (top - bottom) / 1.4 - 1);
  return angle;
}
function extendWithFaceLandmarks(sourceObj, unshiftedLandmarks) {
  const { box: shift } = sourceObj.detection;
  const landmarks = unshiftedLandmarks.shiftBy(shift.x, shift.y);
  const rect = landmarks.align();
  const { imageDims } = sourceObj.detection;
  const alignedRect = new FaceDetection(sourceObj.detection.score, rect.rescale(imageDims.reverse()), imageDims);
  const angle = calculateFaceAngle(unshiftedLandmarks);
  const extension = {
    landmarks,
    unshiftedLandmarks,
    alignedRect,
    angle
  };
  return { ...sourceObj, ...extension };
}

// src/draw/DrawFaceLandmarks.ts
var DrawFaceLandmarksOptions = class {
  constructor(options = {}) {
    const {
      drawLines = true,
      drawPoints = true,
      lineWidth,
      lineColor,
      pointSize,
      pointColor
    } = options;
    this.drawLines = drawLines;
    this.drawPoints = drawPoints;
    this.lineWidth = lineWidth || 1;
    this.pointSize = pointSize || 2;
    this.lineColor = lineColor || "rgba(0, 255, 255, 1)";
    this.pointColor = pointColor || "rgba(255, 0, 255, 1)";
  }
};
var DrawFaceLandmarks = class {
  constructor(faceLandmarks, options = {}) {
    this.faceLandmarks = faceLandmarks;
    this.options = new DrawFaceLandmarksOptions(options);
  }
  draw(canvasArg) {
    const ctx = getContext2dOrThrow(canvasArg);
    const {
      drawLines,
      drawPoints,
      lineWidth,
      lineColor,
      pointSize,
      pointColor
    } = this.options;
    if (drawLines && this.faceLandmarks instanceof FaceLandmarks68) {
      ctx.strokeStyle = lineColor;
      ctx.lineWidth = lineWidth;
      drawContour(ctx, this.faceLandmarks.getJawOutline());
      drawContour(ctx, this.faceLandmarks.getLeftEyeBrow());
      drawContour(ctx, this.faceLandmarks.getRightEyeBrow());
      drawContour(ctx, this.faceLandmarks.getNose());
      drawContour(ctx, this.faceLandmarks.getLeftEye(), true);
      drawContour(ctx, this.faceLandmarks.getRightEye(), true);
      drawContour(ctx, this.faceLandmarks.getMouth(), true);
    }
    if (drawPoints) {
      ctx.strokeStyle = pointColor;
      ctx.fillStyle = pointColor;
      const drawPoint = (pt) => {
        ctx.beginPath();
        ctx.arc(pt.x, pt.y, pointSize, 0, 2 * Math.PI);
        ctx.fill();
      };
      this.faceLandmarks.positions.forEach(drawPoint);
    }
  }
};
function drawFaceLandmarks(canvasArg, faceLandmarks) {
  const faceLandmarksArray = Array.isArray(faceLandmarks) ? faceLandmarks : [faceLandmarks];
  faceLandmarksArray.forEach((f) => {
    const landmarks = f instanceof FaceLandmarks ? f : isWithFaceLandmarks(f) ? f.landmarks : void 0;
    if (!landmarks) {
      throw new Error("drawFaceLandmarks - expected faceExpressions to be FaceLandmarks | WithFaceLandmarks<WithFaceDetection<{}>> or array thereof");
    }
    new DrawFaceLandmarks(landmarks).draw(canvasArg);
  });
}

// package.json
var version = "1.4.1";

// src/ageGenderNet/AgeGenderNet.ts
var tf20 = __toModule(require_tfjs_esm());

// src/xception/TinyXception.ts
var tf19 = __toModule(require_tfjs_esm());

// src/xception/extractParams.ts
function extractorsFactory2(extractWeights, paramMappings) {
  const extractConvParams = extractConvParamsFactory(extractWeights, paramMappings);
  const extractSeparableConvParams = extractSeparableConvParamsFactory(extractWeights, paramMappings);
  function extractReductionBlockParams(channelsIn, channelsOut, mappedPrefix) {
    const separable_conv0 = extractSeparableConvParams(channelsIn, channelsOut, `${mappedPrefix}/separable_conv0`);
    const separable_conv1 = extractSeparableConvParams(channelsOut, channelsOut, `${mappedPrefix}/separable_conv1`);
    const expansion_conv = extractConvParams(channelsIn, channelsOut, 1, `${mappedPrefix}/expansion_conv`);
    return { separable_conv0, separable_conv1, expansion_conv };
  }
  function extractMainBlockParams(channels, mappedPrefix) {
    const separable_conv0 = extractSeparableConvParams(channels, channels, `${mappedPrefix}/separable_conv0`);
    const separable_conv1 = extractSeparableConvParams(channels, channels, `${mappedPrefix}/separable_conv1`);
    const separable_conv2 = extractSeparableConvParams(channels, channels, `${mappedPrefix}/separable_conv2`);
    return { separable_conv0, separable_conv1, separable_conv2 };
  }
  return {
    extractConvParams,
    extractSeparableConvParams,
    extractReductionBlockParams,
    extractMainBlockParams
  };
}
function extractParams3(weights, numMainBlocks) {
  const paramMappings = [];
  const {
    extractWeights,
    getRemainingWeights
  } = extractWeightsFactory(weights);
  const {
    extractConvParams,
    extractSeparableConvParams,
    extractReductionBlockParams,
    extractMainBlockParams
  } = extractorsFactory2(extractWeights, paramMappings);
  const entry_flow_conv_in = extractConvParams(3, 32, 3, "entry_flow/conv_in");
  const entry_flow_reduction_block_0 = extractReductionBlockParams(32, 64, "entry_flow/reduction_block_0");
  const entry_flow_reduction_block_1 = extractReductionBlockParams(64, 128, "entry_flow/reduction_block_1");
  const entry_flow = {
    conv_in: entry_flow_conv_in,
    reduction_block_0: entry_flow_reduction_block_0,
    reduction_block_1: entry_flow_reduction_block_1
  };
  const middle_flow = {};
  range(numMainBlocks, 0, 1).forEach((idx) => {
    middle_flow[`main_block_${idx}`] = extractMainBlockParams(128, `middle_flow/main_block_${idx}`);
  });
  const exit_flow_reduction_block = extractReductionBlockParams(128, 256, "exit_flow/reduction_block");
  const exit_flow_separable_conv = extractSeparableConvParams(256, 512, "exit_flow/separable_conv");
  const exit_flow = {
    reduction_block: exit_flow_reduction_block,
    separable_conv: exit_flow_separable_conv
  };
  if (getRemainingWeights().length !== 0) {
    throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
  }
  return {
    paramMappings,
    params: { entry_flow, middle_flow, exit_flow }
  };
}

// src/xception/extractParamsFromWeightMap.ts
function loadParamsFactory2(weightMap, paramMappings) {
  const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings);
  const extractConvParams = loadConvParamsFactory(extractWeightEntry);
  const extractSeparableConvParams = loadSeparableConvParamsFactory(extractWeightEntry);
  function extractReductionBlockParams(mappedPrefix) {
    const separable_conv0 = extractSeparableConvParams(`${mappedPrefix}/separable_conv0`);
    const separable_conv1 = extractSeparableConvParams(`${mappedPrefix}/separable_conv1`);
    const expansion_conv = extractConvParams(`${mappedPrefix}/expansion_conv`);
    return { separable_conv0, separable_conv1, expansion_conv };
  }
  function extractMainBlockParams(mappedPrefix) {
    const separable_conv0 = extractSeparableConvParams(`${mappedPrefix}/separable_conv0`);
    const separable_conv1 = extractSeparableConvParams(`${mappedPrefix}/separable_conv1`);
    const separable_conv2 = extractSeparableConvParams(`${mappedPrefix}/separable_conv2`);
    return { separable_conv0, separable_conv1, separable_conv2 };
  }
  return {
    extractConvParams,
    extractSeparableConvParams,
    extractReductionBlockParams,
    extractMainBlockParams
  };
}
function extractParamsFromWeightMap3(weightMap, numMainBlocks) {
  const paramMappings = [];
  const {
    extractConvParams,
    extractSeparableConvParams,
    extractReductionBlockParams,
    extractMainBlockParams
  } = loadParamsFactory2(weightMap, paramMappings);
  const entry_flow_conv_in = extractConvParams("entry_flow/conv_in");
  const entry_flow_reduction_block_0 = extractReductionBlockParams("entry_flow/reduction_block_0");
  const entry_flow_reduction_block_1 = extractReductionBlockParams("entry_flow/reduction_block_1");
  const entry_flow = {
    conv_in: entry_flow_conv_in,
    reduction_block_0: entry_flow_reduction_block_0,
    reduction_block_1: entry_flow_reduction_block_1
  };
  const middle_flow = {};
  range(numMainBlocks, 0, 1).forEach((idx) => {
    middle_flow[`main_block_${idx}`] = extractMainBlockParams(`middle_flow/main_block_${idx}`);
  });
  const exit_flow_reduction_block = extractReductionBlockParams("exit_flow/reduction_block");
  const exit_flow_separable_conv = extractSeparableConvParams("exit_flow/separable_conv");
  const exit_flow = {
    reduction_block: exit_flow_reduction_block,
    separable_conv: exit_flow_separable_conv
  };
  disposeUnusedWeightTensors(weightMap, paramMappings);
  return { params: { entry_flow, middle_flow, exit_flow }, paramMappings };
}

// src/xception/TinyXception.ts
function conv(x, params, stride) {
  return tf19.add(tf19.conv2d(x, params.filters, stride, "same"), params.bias);
}
function reductionBlock(x, params, isActivateInput = true) {
  let out = isActivateInput ? tf19.relu(x) : x;
  out = depthwiseSeparableConv(out, params.separable_conv0, [1, 1]);
  out = depthwiseSeparableConv(tf19.relu(out), params.separable_conv1, [1, 1]);
  out = tf19.maxPool(out, [3, 3], [2, 2], "same");
  out = tf19.add(out, conv(x, params.expansion_conv, [2, 2]));
  return out;
}
function mainBlock(x, params) {
  let out = depthwiseSeparableConv(tf19.relu(x), params.separable_conv0, [1, 1]);
  out = depthwiseSeparableConv(tf19.relu(out), params.separable_conv1, [1, 1]);
  out = depthwiseSeparableConv(tf19.relu(out), params.separable_conv2, [1, 1]);
  out = tf19.add(out, x);
  return out;
}
var TinyXception = class extends NeuralNetwork {
  constructor(numMainBlocks) {
    super("TinyXception");
    this._numMainBlocks = numMainBlocks;
  }
  forwardInput(input) {
    const { params } = this;
    if (!params) {
      throw new Error("TinyXception - load model before inference");
    }
    return tf19.tidy(() => {
      const batchTensor = tf19.cast(input.toBatchTensor(112, true), "float32");
      const meanRgb = [122.782, 117.001, 104.298];
      const normalized = normalize(batchTensor, meanRgb).div(255);
      let out = tf19.relu(conv(normalized, params.entry_flow.conv_in, [2, 2]));
      out = reductionBlock(out, params.entry_flow.reduction_block_0, false);
      out = reductionBlock(out, params.entry_flow.reduction_block_1);
      range(this._numMainBlocks, 0, 1).forEach((idx) => {
        out = mainBlock(out, params.middle_flow[`main_block_${idx}`]);
      });
      out = reductionBlock(out, params.exit_flow.reduction_block);
      out = tf19.relu(depthwiseSeparableConv(out, params.exit_flow.separable_conv, [1, 1]));
      return out;
    });
  }
  async forward(input) {
    return this.forwardInput(await toNetInput(input));
  }
  getDefaultModelName() {
    return "tiny_xception_model";
  }
  extractParamsFromWeightMap(weightMap) {
    return extractParamsFromWeightMap3(weightMap, this._numMainBlocks);
  }
  extractParams(weights) {
    return extractParams3(weights, this._numMainBlocks);
  }
};

// src/ageGenderNet/extractParams.ts
function extractParams4(weights) {
  const paramMappings = [];
  const {
    extractWeights,
    getRemainingWeights
  } = extractWeightsFactory(weights);
  const extractFCParams = extractFCParamsFactory(extractWeights, paramMappings);
  const age = extractFCParams(512, 1, "fc/age");
  const gender = extractFCParams(512, 2, "fc/gender");
  if (getRemainingWeights().length !== 0) {
    throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
  }
  return {
    paramMappings,
    params: { fc: { age, gender } }
  };
}

// src/ageGenderNet/extractParamsFromWeightMap.ts
function extractParamsFromWeightMap4(weightMap) {
  const paramMappings = [];
  const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings);
  function extractFcParams(prefix) {
    const weights = extractWeightEntry(`${prefix}/weights`, 2);
    const bias = extractWeightEntry(`${prefix}/bias`, 1);
    return { weights, bias };
  }
  const params = {
    fc: {
      age: extractFcParams("fc/age"),
      gender: extractFcParams("fc/gender")
    }
  };
  disposeUnusedWeightTensors(weightMap, paramMappings);
  return { params, paramMappings };
}

// src/ageGenderNet/types.ts
var Gender;
(function(Gender2) {
  Gender2["FEMALE"] = "female";
  Gender2["MALE"] = "male";
})(Gender || (Gender = {}));

// src/ageGenderNet/AgeGenderNet.ts
var AgeGenderNet = class extends NeuralNetwork {
  constructor(faceFeatureExtractor = new TinyXception(2)) {
    super("AgeGenderNet");
    this._faceFeatureExtractor = faceFeatureExtractor;
  }
  get faceFeatureExtractor() {
    return this._faceFeatureExtractor;
  }
  runNet(input) {
    const { params } = this;
    if (!params) {
      throw new Error(`${this._name} - load model before inference`);
    }
    return tf20.tidy(() => {
      const bottleneckFeatures = input instanceof NetInput ? this.faceFeatureExtractor.forwardInput(input) : input;
      const pooled = tf20.avgPool(bottleneckFeatures, [7, 7], [2, 2], "valid").as2D(bottleneckFeatures.shape[0], -1);
      const age = fullyConnectedLayer(pooled, params.fc.age).as1D();
      const gender = fullyConnectedLayer(pooled, params.fc.gender);
      return { age, gender };
    });
  }
  forwardInput(input) {
    return tf20.tidy(() => {
      const { age, gender } = this.runNet(input);
      return { age, gender: tf20.softmax(gender) };
    });
  }
  async forward(input) {
    return this.forwardInput(await toNetInput(input));
  }
  async predictAgeAndGender(input) {
    const netInput = await toNetInput(input);
    const out = await this.forwardInput(netInput);
    const ages = tf20.unstack(out.age);
    const genders = tf20.unstack(out.gender);
    const ageAndGenderTensors = ages.map((ageTensor, i) => ({
      ageTensor,
      genderTensor: genders[i]
    }));
    const predictionsByBatch = await Promise.all(ageAndGenderTensors.map(async ({ ageTensor, genderTensor }) => {
      const age = ageTensor.dataSync()[0];
      const probMale = genderTensor.dataSync()[0];
      const isMale = probMale > 0.5;
      const gender = isMale ? Gender.MALE : Gender.FEMALE;
      const genderProbability = isMale ? probMale : 1 - probMale;
      ageTensor.dispose();
      genderTensor.dispose();
      return { age, gender, genderProbability };
    }));
    out.age.dispose();
    out.gender.dispose();
    return netInput.isBatchInput ? predictionsByBatch : predictionsByBatch[0];
  }
  getDefaultModelName() {
    return "age_gender_model";
  }
  dispose(throwOnRedispose = true) {
    this.faceFeatureExtractor.dispose(throwOnRedispose);
    super.dispose(throwOnRedispose);
  }
  loadClassifierParams(weights) {
    const { params, paramMappings } = this.extractClassifierParams(weights);
    this._params = params;
    this._paramMappings = paramMappings;
  }
  extractClassifierParams(weights) {
    return extractParams4(weights);
  }
  extractParamsFromWeightMap(weightMap) {
    const { featureExtractorMap, classifierMap } = seperateWeightMaps(weightMap);
    this.faceFeatureExtractor.loadFromWeightMap(featureExtractorMap);
    return extractParamsFromWeightMap4(classifierMap);
  }
  extractParams(weights) {
    const classifierWeightSize = 512 * 1 + 1 + (512 * 2 + 2);
    const featureExtractorWeights = weights.slice(0, weights.length - classifierWeightSize);
    const classifierWeights = weights.slice(weights.length - classifierWeightSize);
    this.faceFeatureExtractor.extractWeights(featureExtractorWeights);
    return this.extractClassifierParams(classifierWeights);
  }
};

// src/faceLandmarkNet/FaceLandmark68NetBase.ts
var tf21 = __toModule(require_tfjs_esm());
var FaceLandmark68NetBase = class extends FaceProcessor {
  postProcess(output, inputSize, originalDimensions) {
    const inputDimensions = originalDimensions.map(({ width, height }) => {
      const scale2 = inputSize / Math.max(height, width);
      return {
        width: width * scale2,
        height: height * scale2
      };
    });
    const batchSize = inputDimensions.length;
    return tf21.tidy(() => {
      const createInterleavedTensor = (fillX, fillY) => tf21.stack([tf21.fill([68], fillX, "float32"), tf21.fill([68], fillY, "float32")], 1).as2D(1, 136).as1D();
      const getPadding = (batchIdx, cond) => {
        const { width, height } = inputDimensions[batchIdx];
        return cond(width, height) ? Math.abs(width - height) / 2 : 0;
      };
      const getPaddingX = (batchIdx) => getPadding(batchIdx, (w, h) => w < h);
      const getPaddingY = (batchIdx) => getPadding(batchIdx, (w, h) => h < w);
      const landmarkTensors = output.mul(tf21.fill([batchSize, 136], inputSize, "float32")).sub(tf21.stack(Array.from(Array(batchSize), (_, batchIdx) => createInterleavedTensor(getPaddingX(batchIdx), getPaddingY(batchIdx))))).div(tf21.stack(Array.from(Array(batchSize), (_, batchIdx) => createInterleavedTensor(inputDimensions[batchIdx].width, inputDimensions[batchIdx].height))));
      return landmarkTensors;
    });
  }
  forwardInput(input) {
    return tf21.tidy(() => {
      const out = this.runNet(input);
      return this.postProcess(out, input.inputSize, input.inputDimensions.map(([height, width]) => ({ height, width })));
    });
  }
  async forward(input) {
    return this.forwardInput(await toNetInput(input));
  }
  async detectLandmarks(input) {
    const netInput = await toNetInput(input);
    const landmarkTensors = tf21.tidy(() => tf21.unstack(this.forwardInput(netInput)));
    const landmarksForBatch = await Promise.all(landmarkTensors.map(async (landmarkTensor, batchIdx) => {
      const landmarksArray = Array.from(landmarkTensor.dataSync());
      const xCoords = landmarksArray.filter((_, i) => isEven(i));
      const yCoords = landmarksArray.filter((_, i) => !isEven(i));
      return new FaceLandmarks68(Array(68).fill(0).map((_, i) => new Point(xCoords[i], yCoords[i])), {
        height: netInput.getInputHeight(batchIdx),
        width: netInput.getInputWidth(batchIdx)
      });
    }));
    landmarkTensors.forEach((t) => t.dispose());
    return netInput.isBatchInput ? landmarksForBatch : landmarksForBatch[0];
  }
  getClassifierChannelsOut() {
    return 136;
  }
};

// src/faceLandmarkNet/FaceLandmark68Net.ts
var FaceLandmark68Net = class extends FaceLandmark68NetBase {
  constructor(faceFeatureExtractor = new FaceFeatureExtractor()) {
    super("FaceLandmark68Net", faceFeatureExtractor);
  }
  getDefaultModelName() {
    return "face_landmark_68_model";
  }
  getClassifierChannelsIn() {
    return 256;
  }
};

// src/faceFeatureExtractor/TinyFaceFeatureExtractor.ts
var tf22 = __toModule(require_tfjs_esm());

// src/faceFeatureExtractor/extractParamsFromWeightMapTiny.ts
function extractParamsFromWeightMapTiny(weightMap) {
  const paramMappings = [];
  const {
    extractDenseBlock3Params
  } = loadParamsFactory(weightMap, paramMappings);
  const params = {
    dense0: extractDenseBlock3Params("dense0", true),
    dense1: extractDenseBlock3Params("dense1"),
    dense2: extractDenseBlock3Params("dense2")
  };
  disposeUnusedWeightTensors(weightMap, paramMappings);
  return { params, paramMappings };
}

// src/faceFeatureExtractor/extractParamsTiny.ts
function extractParamsTiny(weights) {
  const paramMappings = [];
  const {
    extractWeights,
    getRemainingWeights
  } = extractWeightsFactory(weights);
  const {
    extractDenseBlock3Params
  } = extractorsFactory(extractWeights, paramMappings);
  const dense0 = extractDenseBlock3Params(3, 32, "dense0", true);
  const dense1 = extractDenseBlock3Params(32, 64, "dense1");
  const dense2 = extractDenseBlock3Params(64, 128, "dense2");
  if (getRemainingWeights().length !== 0) {
    throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
  }
  return {
    paramMappings,
    params: { dense0, dense1, dense2 }
  };
}

// src/faceFeatureExtractor/TinyFaceFeatureExtractor.ts
var TinyFaceFeatureExtractor = class extends NeuralNetwork {
  constructor() {
    super("TinyFaceFeatureExtractor");
  }
  forwardInput(input) {
    const { params } = this;
    if (!params) {
      throw new Error("TinyFaceFeatureExtractor - load model before inference");
    }
    return tf22.tidy(() => {
      const batchTensor = tf22.cast(input.toBatchTensor(112, true), "float32");
      const meanRgb = [122.782, 117.001, 104.298];
      const normalized = normalize(batchTensor, meanRgb).div(255);
      let out = denseBlock3(normalized, params.dense0, true);
      out = denseBlock3(out, params.dense1);
      out = denseBlock3(out, params.dense2);
      out = tf22.avgPool(out, [14, 14], [2, 2], "valid");
      return out;
    });
  }
  async forward(input) {
    return this.forwardInput(await toNetInput(input));
  }
  getDefaultModelName() {
    return "face_feature_extractor_tiny_model";
  }
  extractParamsFromWeightMap(weightMap) {
    return extractParamsFromWeightMapTiny(weightMap);
  }
  extractParams(weights) {
    return extractParamsTiny(weights);
  }
};

// src/faceLandmarkNet/FaceLandmark68TinyNet.ts
var FaceLandmark68TinyNet = class extends FaceLandmark68NetBase {
  constructor(faceFeatureExtractor = new TinyFaceFeatureExtractor()) {
    super("FaceLandmark68TinyNet", faceFeatureExtractor);
  }
  getDefaultModelName() {
    return "face_landmark_68_tiny_model";
  }
  getClassifierChannelsIn() {
    return 128;
  }
};

// src/faceLandmarkNet/index.ts
var FaceLandmarkNet = class extends FaceLandmark68Net {
};

// src/faceRecognitionNet/FaceRecognitionNet.ts
var tf27 = __toModule(require_tfjs_esm());

// src/faceRecognitionNet/convLayer.ts
var tf24 = __toModule(require_tfjs_esm());

// src/faceRecognitionNet/scaleLayer.ts
var tf23 = __toModule(require_tfjs_esm());
function scale(x, params) {
  return tf23.add(tf23.mul(x, params.weights), params.biases);
}

// src/faceRecognitionNet/convLayer.ts
function convLayer2(x, params, strides, withRelu, padding = "same") {
  const { filters, bias } = params.conv;
  let out = tf24.conv2d(x, filters, strides, padding);
  out = tf24.add(out, bias);
  out = scale(out, params.scale);
  return withRelu ? tf24.relu(out) : out;
}
function conv2(x, params) {
  return convLayer2(x, params, [1, 1], true);
}
function convNoRelu(x, params) {
  return convLayer2(x, params, [1, 1], false);
}
function convDown(x, params) {
  return convLayer2(x, params, [2, 2], true, "valid");
}

// src/faceRecognitionNet/extractParams.ts
var tf25 = __toModule(require_tfjs_esm());
function extractorsFactory3(extractWeights, paramMappings) {
  function extractFilterValues(numFilterValues, numFilters, filterSize) {
    const weights = extractWeights(numFilterValues);
    const depth = weights.length / (numFilters * filterSize * filterSize);
    if (isFloat(depth)) {
      throw new Error(`depth has to be an integer: ${depth}, weights.length: ${weights.length}, numFilters: ${numFilters}, filterSize: ${filterSize}`);
    }
    return tf25.tidy(() => tf25.transpose(tf25.tensor4d(weights, [numFilters, depth, filterSize, filterSize]), [2, 3, 1, 0]));
  }
  function extractConvParams(numFilterValues, numFilters, filterSize, mappedPrefix) {
    const filters = extractFilterValues(numFilterValues, numFilters, filterSize);
    const bias = tf25.tensor1d(extractWeights(numFilters));
    paramMappings.push({ paramPath: `${mappedPrefix}/filters` }, { paramPath: `${mappedPrefix}/bias` });
    return { filters, bias };
  }
  function extractScaleLayerParams(numWeights, mappedPrefix) {
    const weights = tf25.tensor1d(extractWeights(numWeights));
    const biases = tf25.tensor1d(extractWeights(numWeights));
    paramMappings.push({ paramPath: `${mappedPrefix}/weights` }, { paramPath: `${mappedPrefix}/biases` });
    return {
      weights,
      biases
    };
  }
  function extractConvLayerParams(numFilterValues, numFilters, filterSize, mappedPrefix) {
    const conv3 = extractConvParams(numFilterValues, numFilters, filterSize, `${mappedPrefix}/conv`);
    const scale2 = extractScaleLayerParams(numFilters, `${mappedPrefix}/scale`);
    return { conv: conv3, scale: scale2 };
  }
  function extractResidualLayerParams(numFilterValues, numFilters, filterSize, mappedPrefix, isDown = false) {
    const conv1 = extractConvLayerParams((isDown ? 0.5 : 1) * numFilterValues, numFilters, filterSize, `${mappedPrefix}/conv1`);
    const conv22 = extractConvLayerParams(numFilterValues, numFilters, filterSize, `${mappedPrefix}/conv2`);
    return { conv1, conv2: conv22 };
  }
  return {
    extractConvLayerParams,
    extractResidualLayerParams
  };
}
function extractParams5(weights) {
  const {
    extractWeights,
    getRemainingWeights
  } = extractWeightsFactory(weights);
  const paramMappings = [];
  const {
    extractConvLayerParams,
    extractResidualLayerParams
  } = extractorsFactory3(extractWeights, paramMappings);
  const conv32_down = extractConvLayerParams(4704, 32, 7, "conv32_down");
  const conv32_1 = extractResidualLayerParams(9216, 32, 3, "conv32_1");
  const conv32_2 = extractResidualLayerParams(9216, 32, 3, "conv32_2");
  const conv32_3 = extractResidualLayerParams(9216, 32, 3, "conv32_3");
  const conv64_down = extractResidualLayerParams(36864, 64, 3, "conv64_down", true);
  const conv64_1 = extractResidualLayerParams(36864, 64, 3, "conv64_1");
  const conv64_2 = extractResidualLayerParams(36864, 64, 3, "conv64_2");
  const conv64_3 = extractResidualLayerParams(36864, 64, 3, "conv64_3");
  const conv128_down = extractResidualLayerParams(147456, 128, 3, "conv128_down", true);
  const conv128_1 = extractResidualLayerParams(147456, 128, 3, "conv128_1");
  const conv128_2 = extractResidualLayerParams(147456, 128, 3, "conv128_2");
  const conv256_down = extractResidualLayerParams(589824, 256, 3, "conv256_down", true);
  const conv256_1 = extractResidualLayerParams(589824, 256, 3, "conv256_1");
  const conv256_2 = extractResidualLayerParams(589824, 256, 3, "conv256_2");
  const conv256_down_out = extractResidualLayerParams(589824, 256, 3, "conv256_down_out");
  const fc = tf25.tidy(() => tf25.transpose(tf25.tensor2d(extractWeights(256 * 128), [128, 256]), [1, 0]));
  paramMappings.push({ paramPath: "fc" });
  if (getRemainingWeights().length !== 0) {
    throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
  }
  const params = {
    conv32_down,
    conv32_1,
    conv32_2,
    conv32_3,
    conv64_down,
    conv64_1,
    conv64_2,
    conv64_3,
    conv128_down,
    conv128_1,
    conv128_2,
    conv256_down,
    conv256_1,
    conv256_2,
    conv256_down_out,
    fc
  };
  return { params, paramMappings };
}

// src/faceRecognitionNet/extractParamsFromWeightMap.ts
function extractorsFactory4(weightMap, paramMappings) {
  const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings);
  function extractScaleLayerParams(prefix) {
    const weights = extractWeightEntry(`${prefix}/scale/weights`, 1);
    const biases = extractWeightEntry(`${prefix}/scale/biases`, 1);
    return { weights, biases };
  }
  function extractConvLayerParams(prefix) {
    const filters = extractWeightEntry(`${prefix}/conv/filters`, 4);
    const bias = extractWeightEntry(`${prefix}/conv/bias`, 1);
    const scale2 = extractScaleLayerParams(prefix);
    return { conv: { filters, bias }, scale: scale2 };
  }
  function extractResidualLayerParams(prefix) {
    return {
      conv1: extractConvLayerParams(`${prefix}/conv1`),
      conv2: extractConvLayerParams(`${prefix}/conv2`)
    };
  }
  return {
    extractConvLayerParams,
    extractResidualLayerParams
  };
}
function extractParamsFromWeightMap5(weightMap) {
  const paramMappings = [];
  const {
    extractConvLayerParams,
    extractResidualLayerParams
  } = extractorsFactory4(weightMap, paramMappings);
  const conv32_down = extractConvLayerParams("conv32_down");
  const conv32_1 = extractResidualLayerParams("conv32_1");
  const conv32_2 = extractResidualLayerParams("conv32_2");
  const conv32_3 = extractResidualLayerParams("conv32_3");
  const conv64_down = extractResidualLayerParams("conv64_down");
  const conv64_1 = extractResidualLayerParams("conv64_1");
  const conv64_2 = extractResidualLayerParams("conv64_2");
  const conv64_3 = extractResidualLayerParams("conv64_3");
  const conv128_down = extractResidualLayerParams("conv128_down");
  const conv128_1 = extractResidualLayerParams("conv128_1");
  const conv128_2 = extractResidualLayerParams("conv128_2");
  const conv256_down = extractResidualLayerParams("conv256_down");
  const conv256_1 = extractResidualLayerParams("conv256_1");
  const conv256_2 = extractResidualLayerParams("conv256_2");
  const conv256_down_out = extractResidualLayerParams("conv256_down_out");
  const { fc } = weightMap;
  paramMappings.push({ originalPath: "fc", paramPath: "fc" });
  if (!isTensor2D(fc)) {
    throw new Error(`expected weightMap[fc] to be a Tensor2D, instead have ${fc}`);
  }
  const params = {
    conv32_down,
    conv32_1,
    conv32_2,
    conv32_3,
    conv64_down,
    conv64_1,
    conv64_2,
    conv64_3,
    conv128_down,
    conv128_1,
    conv128_2,
    conv256_down,
    conv256_1,
    conv256_2,
    conv256_down_out,
    fc
  };
  disposeUnusedWeightTensors(weightMap, paramMappings);
  return { params, paramMappings };
}

// src/faceRecognitionNet/residualLayer.ts
var tf26 = __toModule(require_tfjs_esm());
function residual(x, params) {
  let out = conv2(x, params.conv1);
  out = convNoRelu(out, params.conv2);
  out = tf26.add(out, x);
  out = tf26.relu(out);
  return out;
}
function residualDown(x, params) {
  let out = convDown(x, params.conv1);
  out = convNoRelu(out, params.conv2);
  let pooled = tf26.avgPool(x, 2, 2, "valid");
  const zeros2 = tf26.zeros(pooled.shape);
  const isPad = pooled.shape[3] !== out.shape[3];
  const isAdjustShape = pooled.shape[1] !== out.shape[1] || pooled.shape[2] !== out.shape[2];
  if (isAdjustShape) {
    const padShapeX = [...out.shape];
    padShapeX[1] = 1;
    const zerosW = tf26.zeros(padShapeX);
    out = tf26.concat([out, zerosW], 1);
    const padShapeY = [...out.shape];
    padShapeY[2] = 1;
    const zerosH = tf26.zeros(padShapeY);
    out = tf26.concat([out, zerosH], 2);
  }
  pooled = isPad ? tf26.concat([pooled, zeros2], 3) : pooled;
  out = tf26.add(pooled, out);
  out = tf26.relu(out);
  return out;
}

// src/faceRecognitionNet/FaceRecognitionNet.ts
var FaceRecognitionNet = class extends NeuralNetwork {
  constructor() {
    super("FaceRecognitionNet");
  }
  forwardInput(input) {
    const { params } = this;
    if (!params) {
      throw new Error("FaceRecognitionNet - load model before inference");
    }
    return tf27.tidy(() => {
      const batchTensor = tf27.cast(input.toBatchTensor(150, true), "float32");
      const meanRgb = [122.782, 117.001, 104.298];
      const normalized = normalize(batchTensor, meanRgb).div(255);
      let out = convDown(normalized, params.conv32_down);
      out = tf27.maxPool(out, 3, 2, "valid");
      out = residual(out, params.conv32_1);
      out = residual(out, params.conv32_2);
      out = residual(out, params.conv32_3);
      out = residualDown(out, params.conv64_down);
      out = residual(out, params.conv64_1);
      out = residual(out, params.conv64_2);
      out = residual(out, params.conv64_3);
      out = residualDown(out, params.conv128_down);
      out = residual(out, params.conv128_1);
      out = residual(out, params.conv128_2);
      out = residualDown(out, params.conv256_down);
      out = residual(out, params.conv256_1);
      out = residual(out, params.conv256_2);
      out = residualDown(out, params.conv256_down_out);
      const globalAvg = out.mean([1, 2]);
      const fullyConnected = tf27.matMul(globalAvg, params.fc);
      return fullyConnected;
    });
  }
  async forward(input) {
    return this.forwardInput(await toNetInput(input));
  }
  async computeFaceDescriptor(input) {
    var _a;
    if ((_a = input == null ? void 0 : input.shape) == null ? void 0 : _a.some((dim) => dim <= 0))
      return new Float32Array(128);
    const netInput = await toNetInput(input);
    const faceDescriptorTensors = tf27.tidy(() => tf27.unstack(this.forwardInput(netInput)));
    const faceDescriptorsForBatch = await Promise.all(faceDescriptorTensors.map((t) => t.data()));
    faceDescriptorTensors.forEach((t) => t.dispose());
    return netInput.isBatchInput ? faceDescriptorsForBatch : faceDescriptorsForBatch[0];
  }
  getDefaultModelName() {
    return "face_recognition_model";
  }
  extractParamsFromWeightMap(weightMap) {
    return extractParamsFromWeightMap5(weightMap);
  }
  extractParams(weights) {
    return extractParams5(weights);
  }
};

// src/faceRecognitionNet/index.ts
function createFaceRecognitionNet(weights) {
  const net = new FaceRecognitionNet();
  net.extractWeights(weights);
  return net;
}

// src/factories/WithFaceDescriptor.ts
function extendWithFaceDescriptor(sourceObj, descriptor) {
  const extension = { descriptor };
  return { ...sourceObj, ...extension };
}

// src/factories/WithAge.ts
function isWithAge(obj) {
  return typeof obj.age === "number";
}
function extendWithAge(sourceObj, age) {
  const extension = { age };
  return { ...sourceObj, ...extension };
}

// src/factories/WithGender.ts
function isWithGender(obj) {
  return (obj.gender === Gender.MALE || obj.gender === Gender.FEMALE) && isValidProbablitiy(obj.genderProbability);
}
function extendWithGender(sourceObj, gender, genderProbability) {
  const extension = { gender, genderProbability };
  return { ...sourceObj, ...extension };
}

// src/ssdMobilenetv1/SsdMobilenetv1.ts
var tf34 = __toModule(require_tfjs_esm());

// src/ssdMobilenetv1/extractParams.ts
var tf28 = __toModule(require_tfjs_esm());
function extractorsFactory5(extractWeights, paramMappings) {
  function extractDepthwiseConvParams(numChannels, mappedPrefix) {
    const filters = tf28.tensor4d(extractWeights(3 * 3 * numChannels), [3, 3, numChannels, 1]);
    const batch_norm_scale = tf28.tensor1d(extractWeights(numChannels));
    const batch_norm_offset = tf28.tensor1d(extractWeights(numChannels));
    const batch_norm_mean = tf28.tensor1d(extractWeights(numChannels));
    const batch_norm_variance = tf28.tensor1d(extractWeights(numChannels));
    paramMappings.push({ paramPath: `${mappedPrefix}/filters` }, { paramPath: `${mappedPrefix}/batch_norm_scale` }, { paramPath: `${mappedPrefix}/batch_norm_offset` }, { paramPath: `${mappedPrefix}/batch_norm_mean` }, { paramPath: `${mappedPrefix}/batch_norm_variance` });
    return {
      filters,
      batch_norm_scale,
      batch_norm_offset,
      batch_norm_mean,
      batch_norm_variance
    };
  }
  function extractConvParams(channelsIn, channelsOut, filterSize, mappedPrefix, isPointwiseConv) {
    const filters = tf28.tensor4d(extractWeights(channelsIn * channelsOut * filterSize * filterSize), [filterSize, filterSize, channelsIn, channelsOut]);
    const bias = tf28.tensor1d(extractWeights(channelsOut));
    paramMappings.push({ paramPath: `${mappedPrefix}/filters` }, { paramPath: `${mappedPrefix}/${isPointwiseConv ? "batch_norm_offset" : "bias"}` });
    return { filters, bias };
  }
  function extractPointwiseConvParams(channelsIn, channelsOut, filterSize, mappedPrefix) {
    const {
      filters,
      bias
    } = extractConvParams(channelsIn, channelsOut, filterSize, mappedPrefix, true);
    return {
      filters,
      batch_norm_offset: bias
    };
  }
  function extractConvPairParams(channelsIn, channelsOut, mappedPrefix) {
    const depthwise_conv = extractDepthwiseConvParams(channelsIn, `${mappedPrefix}/depthwise_conv`);
    const pointwise_conv = extractPointwiseConvParams(channelsIn, channelsOut, 1, `${mappedPrefix}/pointwise_conv`);
    return { depthwise_conv, pointwise_conv };
  }
  function extractMobilenetV1Params() {
    const conv_0 = extractPointwiseConvParams(3, 32, 3, "mobilenetv1/conv_0");
    const conv_1 = extractConvPairParams(32, 64, "mobilenetv1/conv_1");
    const conv_2 = extractConvPairParams(64, 128, "mobilenetv1/conv_2");
    const conv_3 = extractConvPairParams(128, 128, "mobilenetv1/conv_3");
    const conv_4 = extractConvPairParams(128, 256, "mobilenetv1/conv_4");
    const conv_5 = extractConvPairParams(256, 256, "mobilenetv1/conv_5");
    const conv_6 = extractConvPairParams(256, 512, "mobilenetv1/conv_6");
    const conv_7 = extractConvPairParams(512, 512, "mobilenetv1/conv_7");
    const conv_8 = extractConvPairParams(512, 512, "mobilenetv1/conv_8");
    const conv_9 = extractConvPairParams(512, 512, "mobilenetv1/conv_9");
    const conv_10 = extractConvPairParams(512, 512, "mobilenetv1/conv_10");
    const conv_11 = extractConvPairParams(512, 512, "mobilenetv1/conv_11");
    const conv_12 = extractConvPairParams(512, 1024, "mobilenetv1/conv_12");
    const conv_13 = extractConvPairParams(1024, 1024, "mobilenetv1/conv_13");
    return {
      conv_0,
      conv_1,
      conv_2,
      conv_3,
      conv_4,
      conv_5,
      conv_6,
      conv_7,
      conv_8,
      conv_9,
      conv_10,
      conv_11,
      conv_12,
      conv_13
    };
  }
  function extractPredictionLayerParams() {
    const conv_0 = extractPointwiseConvParams(1024, 256, 1, "prediction_layer/conv_0");
    const conv_1 = extractPointwiseConvParams(256, 512, 3, "prediction_layer/conv_1");
    const conv_2 = extractPointwiseConvParams(512, 128, 1, "prediction_layer/conv_2");
    const conv_3 = extractPointwiseConvParams(128, 256, 3, "prediction_layer/conv_3");
    const conv_4 = extractPointwiseConvParams(256, 128, 1, "prediction_layer/conv_4");
    const conv_5 = extractPointwiseConvParams(128, 256, 3, "prediction_layer/conv_5");
    const conv_6 = extractPointwiseConvParams(256, 64, 1, "prediction_layer/conv_6");
    const conv_7 = extractPointwiseConvParams(64, 128, 3, "prediction_layer/conv_7");
    const box_encoding_0_predictor = extractConvParams(512, 12, 1, "prediction_layer/box_predictor_0/box_encoding_predictor");
    const class_predictor_0 = extractConvParams(512, 9, 1, "prediction_layer/box_predictor_0/class_predictor");
    const box_encoding_1_predictor = extractConvParams(1024, 24, 1, "prediction_layer/box_predictor_1/box_encoding_predictor");
    const class_predictor_1 = extractConvParams(1024, 18, 1, "prediction_layer/box_predictor_1/class_predictor");
    const box_encoding_2_predictor = extractConvParams(512, 24, 1, "prediction_layer/box_predictor_2/box_encoding_predictor");
    const class_predictor_2 = extractConvParams(512, 18, 1, "prediction_layer/box_predictor_2/class_predictor");
    const box_encoding_3_predictor = extractConvParams(256, 24, 1, "prediction_layer/box_predictor_3/box_encoding_predictor");
    const class_predictor_3 = extractConvParams(256, 18, 1, "prediction_layer/box_predictor_3/class_predictor");
    const box_encoding_4_predictor = extractConvParams(256, 24, 1, "prediction_layer/box_predictor_4/box_encoding_predictor");
    const class_predictor_4 = extractConvParams(256, 18, 1, "prediction_layer/box_predictor_4/class_predictor");
    const box_encoding_5_predictor = extractConvParams(128, 24, 1, "prediction_layer/box_predictor_5/box_encoding_predictor");
    const class_predictor_5 = extractConvParams(128, 18, 1, "prediction_layer/box_predictor_5/class_predictor");
    const box_predictor_0 = {
      box_encoding_predictor: box_encoding_0_predictor,
      class_predictor: class_predictor_0
    };
    const box_predictor_1 = {
      box_encoding_predictor: box_encoding_1_predictor,
      class_predictor: class_predictor_1
    };
    const box_predictor_2 = {
      box_encoding_predictor: box_encoding_2_predictor,
      class_predictor: class_predictor_2
    };
    const box_predictor_3 = {
      box_encoding_predictor: box_encoding_3_predictor,
      class_predictor: class_predictor_3
    };
    const box_predictor_4 = {
      box_encoding_predictor: box_encoding_4_predictor,
      class_predictor: class_predictor_4
    };
    const box_predictor_5 = {
      box_encoding_predictor: box_encoding_5_predictor,
      class_predictor: class_predictor_5
    };
    return {
      conv_0,
      conv_1,
      conv_2,
      conv_3,
      conv_4,
      conv_5,
      conv_6,
      conv_7,
      box_predictor_0,
      box_predictor_1,
      box_predictor_2,
      box_predictor_3,
      box_predictor_4,
      box_predictor_5
    };
  }
  return {
    extractMobilenetV1Params,
    extractPredictionLayerParams
  };
}
function extractParams6(weights) {
  const paramMappings = [];
  const {
    extractWeights,
    getRemainingWeights
  } = extractWeightsFactory(weights);
  const {
    extractMobilenetV1Params,
    extractPredictionLayerParams
  } = extractorsFactory5(extractWeights, paramMappings);
  const mobilenetv1 = extractMobilenetV1Params();
  const prediction_layer = extractPredictionLayerParams();
  const extra_dim = tf28.tensor3d(extractWeights(5118 * 4), [1, 5118, 4]);
  const output_layer = {
    extra_dim
  };
  paramMappings.push({ paramPath: "output_layer/extra_dim" });
  if (getRemainingWeights().length !== 0) {
    throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
  }
  return {
    params: {
      mobilenetv1,
      prediction_layer,
      output_layer
    },
    paramMappings
  };
}

// src/ssdMobilenetv1/extractParamsFromWeightMap.ts
function extractorsFactory6(weightMap, paramMappings) {
  const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings);
  function extractPointwiseConvParams(prefix, idx, mappedPrefix) {
    const filters = extractWeightEntry(`${prefix}/Conv2d_${idx}_pointwise/weights`, 4, `${mappedPrefix}/filters`);
    const batch_norm_offset = extractWeightEntry(`${prefix}/Conv2d_${idx}_pointwise/convolution_bn_offset`, 1, `${mappedPrefix}/batch_norm_offset`);
    return { filters, batch_norm_offset };
  }
  function extractConvPairParams(idx) {
    const mappedPrefix = `mobilenetv1/conv_${idx}`;
    const prefixDepthwiseConv = `MobilenetV1/Conv2d_${idx}_depthwise`;
    const mappedPrefixDepthwiseConv = `${mappedPrefix}/depthwise_conv`;
    const mappedPrefixPointwiseConv = `${mappedPrefix}/pointwise_conv`;
    const filters = extractWeightEntry(`${prefixDepthwiseConv}/depthwise_weights`, 4, `${mappedPrefixDepthwiseConv}/filters`);
    const batch_norm_scale = extractWeightEntry(`${prefixDepthwiseConv}/BatchNorm/gamma`, 1, `${mappedPrefixDepthwiseConv}/batch_norm_scale`);
    const batch_norm_offset = extractWeightEntry(`${prefixDepthwiseConv}/BatchNorm/beta`, 1, `${mappedPrefixDepthwiseConv}/batch_norm_offset`);
    const batch_norm_mean = extractWeightEntry(`${prefixDepthwiseConv}/BatchNorm/moving_mean`, 1, `${mappedPrefixDepthwiseConv}/batch_norm_mean`);
    const batch_norm_variance = extractWeightEntry(`${prefixDepthwiseConv}/BatchNorm/moving_variance`, 1, `${mappedPrefixDepthwiseConv}/batch_norm_variance`);
    return {
      depthwise_conv: {
        filters,
        batch_norm_scale,
        batch_norm_offset,
        batch_norm_mean,
        batch_norm_variance
      },
      pointwise_conv: extractPointwiseConvParams("MobilenetV1", idx, mappedPrefixPointwiseConv)
    };
  }
  function extractMobilenetV1Params() {
    return {
      conv_0: extractPointwiseConvParams("MobilenetV1", 0, "mobilenetv1/conv_0"),
      conv_1: extractConvPairParams(1),
      conv_2: extractConvPairParams(2),
      conv_3: extractConvPairParams(3),
      conv_4: extractConvPairParams(4),
      conv_5: extractConvPairParams(5),
      conv_6: extractConvPairParams(6),
      conv_7: extractConvPairParams(7),
      conv_8: extractConvPairParams(8),
      conv_9: extractConvPairParams(9),
      conv_10: extractConvPairParams(10),
      conv_11: extractConvPairParams(11),
      conv_12: extractConvPairParams(12),
      conv_13: extractConvPairParams(13)
    };
  }
  function extractConvParams(prefix, mappedPrefix) {
    const filters = extractWeightEntry(`${prefix}/weights`, 4, `${mappedPrefix}/filters`);
    const bias = extractWeightEntry(`${prefix}/biases`, 1, `${mappedPrefix}/bias`);
    return { filters, bias };
  }
  function extractBoxPredictorParams(idx) {
    const box_encoding_predictor = extractConvParams(`Prediction/BoxPredictor_${idx}/BoxEncodingPredictor`, `prediction_layer/box_predictor_${idx}/box_encoding_predictor`);
    const class_predictor = extractConvParams(`Prediction/BoxPredictor_${idx}/ClassPredictor`, `prediction_layer/box_predictor_${idx}/class_predictor`);
    return { box_encoding_predictor, class_predictor };
  }
  function extractPredictionLayerParams() {
    return {
      conv_0: extractPointwiseConvParams("Prediction", 0, "prediction_layer/conv_0"),
      conv_1: extractPointwiseConvParams("Prediction", 1, "prediction_layer/conv_1"),
      conv_2: extractPointwiseConvParams("Prediction", 2, "prediction_layer/conv_2"),
      conv_3: extractPointwiseConvParams("Prediction", 3, "prediction_layer/conv_3"),
      conv_4: extractPointwiseConvParams("Prediction", 4, "prediction_layer/conv_4"),
      conv_5: extractPointwiseConvParams("Prediction", 5, "prediction_layer/conv_5"),
      conv_6: extractPointwiseConvParams("Prediction", 6, "prediction_layer/conv_6"),
      conv_7: extractPointwiseConvParams("Prediction", 7, "prediction_layer/conv_7"),
      box_predictor_0: extractBoxPredictorParams(0),
      box_predictor_1: extractBoxPredictorParams(1),
      box_predictor_2: extractBoxPredictorParams(2),
      box_predictor_3: extractBoxPredictorParams(3),
      box_predictor_4: extractBoxPredictorParams(4),
      box_predictor_5: extractBoxPredictorParams(5)
    };
  }
  return {
    extractMobilenetV1Params,
    extractPredictionLayerParams
  };
}
function extractParamsFromWeightMap6(weightMap) {
  const paramMappings = [];
  const {
    extractMobilenetV1Params,
    extractPredictionLayerParams
  } = extractorsFactory6(weightMap, paramMappings);
  const extra_dim = weightMap["Output/extra_dim"];
  paramMappings.push({ originalPath: "Output/extra_dim", paramPath: "output_layer/extra_dim" });
  if (!isTensor3D(extra_dim)) {
    throw new Error(`expected weightMap['Output/extra_dim'] to be a Tensor3D, instead have ${extra_dim}`);
  }
  const params = {
    mobilenetv1: extractMobilenetV1Params(),
    prediction_layer: extractPredictionLayerParams(),
    output_layer: {
      extra_dim
    }
  };
  disposeUnusedWeightTensors(weightMap, paramMappings);
  return { params, paramMappings };
}

// src/ssdMobilenetv1/mobileNetV1.ts
var tf30 = __toModule(require_tfjs_esm());

// src/ssdMobilenetv1/pointwiseConvLayer.ts
var tf29 = __toModule(require_tfjs_esm());
function pointwiseConvLayer(x, params, strides) {
  return tf29.tidy(() => {
    let out = tf29.conv2d(x, params.filters, strides, "same");
    out = tf29.add(out, params.batch_norm_offset);
    return tf29.clipByValue(out, 0, 6);
  });
}

// src/ssdMobilenetv1/mobileNetV1.ts
var epsilon = 0.0010000000474974513;
function depthwiseConvLayer(x, params, strides) {
  return tf30.tidy(() => {
    let out = tf30.depthwiseConv2d(x, params.filters, strides, "same");
    out = tf30.batchNorm(out, params.batch_norm_mean, params.batch_norm_variance, params.batch_norm_offset, params.batch_norm_scale, epsilon);
    return tf30.clipByValue(out, 0, 6);
  });
}
function getStridesForLayerIdx(layerIdx) {
  return [2, 4, 6, 12].some((idx) => idx === layerIdx) ? [2, 2] : [1, 1];
}
function mobileNetV1(x, params) {
  return tf30.tidy(() => {
    let conv11;
    let out = pointwiseConvLayer(x, params.conv_0, [2, 2]);
    const convPairParams = [
      params.conv_1,
      params.conv_2,
      params.conv_3,
      params.conv_4,
      params.conv_5,
      params.conv_6,
      params.conv_7,
      params.conv_8,
      params.conv_9,
      params.conv_10,
      params.conv_11,
      params.conv_12,
      params.conv_13
    ];
    convPairParams.forEach((param, i) => {
      const layerIdx = i + 1;
      const depthwiseConvStrides = getStridesForLayerIdx(layerIdx);
      out = depthwiseConvLayer(out, param.depthwise_conv, depthwiseConvStrides);
      out = pointwiseConvLayer(out, param.pointwise_conv, [1, 1]);
      if (layerIdx === 11)
        conv11 = out;
    });
    if (conv11 === null) {
      throw new Error("mobileNetV1 - output of conv layer 11 is null");
    }
    return {
      out,
      conv11
    };
  });
}

// src/ssdMobilenetv1/nonMaxSuppression.ts
function IOU(boxes, i, j) {
  const boxesData = boxes.arraySync();
  const yminI = Math.min(boxesData[i][0], boxesData[i][2]);
  const xminI = Math.min(boxesData[i][1], boxesData[i][3]);
  const ymaxI = Math.max(boxesData[i][0], boxesData[i][2]);
  const xmaxI = Math.max(boxesData[i][1], boxesData[i][3]);
  const yminJ = Math.min(boxesData[j][0], boxesData[j][2]);
  const xminJ = Math.min(boxesData[j][1], boxesData[j][3]);
  const ymaxJ = Math.max(boxesData[j][0], boxesData[j][2]);
  const xmaxJ = Math.max(boxesData[j][1], boxesData[j][3]);
  const areaI = (ymaxI - yminI) * (xmaxI - xminI);
  const areaJ = (ymaxJ - yminJ) * (xmaxJ - xminJ);
  if (areaI <= 0 || areaJ <= 0)
    return 0;
  const intersectionYmin = Math.max(yminI, yminJ);
  const intersectionXmin = Math.max(xminI, xminJ);
  const intersectionYmax = Math.min(ymaxI, ymaxJ);
  const intersectionXmax = Math.min(xmaxI, xmaxJ);
  const intersectionArea = Math.max(intersectionYmax - intersectionYmin, 0) * Math.max(intersectionXmax - intersectionXmin, 0);
  return intersectionArea / (areaI + areaJ - intersectionArea);
}
function nonMaxSuppression2(boxes, scores, maxOutputSize, iouThreshold, scoreThreshold) {
  const numBoxes = boxes.shape[0];
  const outputSize = Math.min(maxOutputSize, numBoxes);
  const candidates = scores.map((score, boxIndex) => ({ score, boxIndex })).filter((c) => c.score > scoreThreshold).sort((c1, c2) => c2.score - c1.score);
  const suppressFunc = (x) => x <= iouThreshold ? 1 : 0;
  const selected = [];
  candidates.forEach((c) => {
    if (selected.length >= outputSize)
      return;
    const originalScore = c.score;
    for (let j = selected.length - 1; j >= 0; --j) {
      const iou2 = IOU(boxes, c.boxIndex, selected[j]);
      if (iou2 === 0)
        continue;
      c.score *= suppressFunc(iou2);
      if (c.score <= scoreThreshold)
        break;
    }
    if (originalScore === c.score) {
      selected.push(c.boxIndex);
    }
  });
  return selected;
}

// src/ssdMobilenetv1/outputLayer.ts
var tf31 = __toModule(require_tfjs_esm());
function getCenterCoordinatesAndSizesLayer(x) {
  const vec = tf31.unstack(tf31.transpose(x, [1, 0]));
  const sizes = [
    tf31.sub(vec[2], vec[0]),
    tf31.sub(vec[3], vec[1])
  ];
  const centers = [
    tf31.add(vec[0], tf31.div(sizes[0], 2)),
    tf31.add(vec[1], tf31.div(sizes[1], 2))
  ];
  return { sizes, centers };
}
function decodeBoxesLayer(x0, x1) {
  const { sizes, centers } = getCenterCoordinatesAndSizesLayer(x0);
  const vec = tf31.unstack(tf31.transpose(x1, [1, 0]));
  const div0_out = tf31.div(tf31.mul(tf31.exp(tf31.div(vec[2], 5)), sizes[0]), 2);
  const add0_out = tf31.add(tf31.mul(tf31.div(vec[0], 10), sizes[0]), centers[0]);
  const div1_out = tf31.div(tf31.mul(tf31.exp(tf31.div(vec[3], 5)), sizes[1]), 2);
  const add1_out = tf31.add(tf31.mul(tf31.div(vec[1], 10), sizes[1]), centers[1]);
  return tf31.transpose(tf31.stack([
    tf31.sub(add0_out, div0_out),
    tf31.sub(add1_out, div1_out),
    tf31.add(add0_out, div0_out),
    tf31.add(add1_out, div1_out)
  ]), [1, 0]);
}
function outputLayer(boxPredictions, classPredictions, params) {
  return tf31.tidy(() => {
    const batchSize = boxPredictions.shape[0];
    let boxes = decodeBoxesLayer(tf31.reshape(tf31.tile(params.extra_dim, [batchSize, 1, 1]), [-1, 4]), tf31.reshape(boxPredictions, [-1, 4]));
    boxes = tf31.reshape(boxes, [batchSize, boxes.shape[0] / batchSize, 4]);
    const scoresAndClasses = tf31.sigmoid(tf31.slice(classPredictions, [0, 0, 1], [-1, -1, -1]));
    let scores = tf31.slice(scoresAndClasses, [0, 0, 0], [-1, -1, 1]);
    scores = tf31.reshape(scores, [batchSize, scores.shape[1]]);
    const boxesByBatch = tf31.unstack(boxes);
    const scoresByBatch = tf31.unstack(scores);
    return { boxes: boxesByBatch, scores: scoresByBatch };
  });
}

// src/ssdMobilenetv1/predictionLayer.ts
var tf33 = __toModule(require_tfjs_esm());

// src/ssdMobilenetv1/boxPredictionLayer.ts
var tf32 = __toModule(require_tfjs_esm());
function boxPredictionLayer(x, params) {
  return tf32.tidy(() => {
    const batchSize = x.shape[0];
    const boxPredictionEncoding = tf32.reshape(convLayer(x, params.box_encoding_predictor), [batchSize, -1, 1, 4]);
    const classPrediction = tf32.reshape(convLayer(x, params.class_predictor), [batchSize, -1, 3]);
    return { boxPredictionEncoding, classPrediction };
  });
}

// src/ssdMobilenetv1/predictionLayer.ts
function predictionLayer(x, conv11, params) {
  return tf33.tidy(() => {
    const conv0 = pointwiseConvLayer(x, params.conv_0, [1, 1]);
    const conv1 = pointwiseConvLayer(conv0, params.conv_1, [2, 2]);
    const conv22 = pointwiseConvLayer(conv1, params.conv_2, [1, 1]);
    const conv3 = pointwiseConvLayer(conv22, params.conv_3, [2, 2]);
    const conv4 = pointwiseConvLayer(conv3, params.conv_4, [1, 1]);
    const conv5 = pointwiseConvLayer(conv4, params.conv_5, [2, 2]);
    const conv6 = pointwiseConvLayer(conv5, params.conv_6, [1, 1]);
    const conv7 = pointwiseConvLayer(conv6, params.conv_7, [2, 2]);
    const boxPrediction0 = boxPredictionLayer(conv11, params.box_predictor_0);
    const boxPrediction1 = boxPredictionLayer(x, params.box_predictor_1);
    const boxPrediction2 = boxPredictionLayer(conv1, params.box_predictor_2);
    const boxPrediction3 = boxPredictionLayer(conv3, params.box_predictor_3);
    const boxPrediction4 = boxPredictionLayer(conv5, params.box_predictor_4);
    const boxPrediction5 = boxPredictionLayer(conv7, params.box_predictor_5);
    const boxPredictions = tf33.concat([
      boxPrediction0.boxPredictionEncoding,
      boxPrediction1.boxPredictionEncoding,
      boxPrediction2.boxPredictionEncoding,
      boxPrediction3.boxPredictionEncoding,
      boxPrediction4.boxPredictionEncoding,
      boxPrediction5.boxPredictionEncoding
    ], 1);
    const classPredictions = tf33.concat([
      boxPrediction0.classPrediction,
      boxPrediction1.classPrediction,
      boxPrediction2.classPrediction,
      boxPrediction3.classPrediction,
      boxPrediction4.classPrediction,
      boxPrediction5.classPrediction
    ], 1);
    return {
      boxPredictions,
      classPredictions
    };
  });
}

// src/ssdMobilenetv1/SsdMobilenetv1Options.ts
var SsdMobilenetv1Options = class {
  constructor({ minConfidence, maxResults } = {}) {
    this._name = "SsdMobilenetv1Options";
    this._minConfidence = minConfidence || 0.5;
    this._maxResults = maxResults || 100;
    if (typeof this._minConfidence !== "number" || this._minConfidence <= 0 || this._minConfidence >= 1) {
      throw new Error(`${this._name} - expected minConfidence to be a number between 0 and 1`);
    }
    if (typeof this._maxResults !== "number") {
      throw new Error(`${this._name} - expected maxResults to be a number`);
    }
  }
  get minConfidence() {
    return this._minConfidence;
  }
  get maxResults() {
    return this._maxResults;
  }
};

// src/ssdMobilenetv1/SsdMobilenetv1.ts
var SsdMobilenetv1 = class extends NeuralNetwork {
  constructor() {
    super("SsdMobilenetv1");
  }
  forwardInput(input) {
    const { params } = this;
    if (!params)
      throw new Error("SsdMobilenetv1 - load model before inference");
    return tf34.tidy(() => {
      const batchTensor = tf34.cast(input.toBatchTensor(512, false), "float32");
      const x = tf34.sub(tf34.div(batchTensor, 127.5), 1);
      const features = mobileNetV1(x, params.mobilenetv1);
      const { boxPredictions, classPredictions } = predictionLayer(features.out, features.conv11, params.prediction_layer);
      return outputLayer(boxPredictions, classPredictions, params.output_layer);
    });
  }
  async forward(input) {
    return this.forwardInput(await toNetInput(input));
  }
  async locateFaces(input, options = {}) {
    const { maxResults, minConfidence } = new SsdMobilenetv1Options(options);
    const netInput = await toNetInput(input);
    const { boxes: _boxes, scores: _scores } = this.forwardInput(netInput);
    const boxes = _boxes[0];
    const scores = _scores[0];
    for (let i = 1; i < _boxes.length; i++) {
      _boxes[i].dispose();
      _scores[i].dispose();
    }
    const scoresData = Array.from(scores.dataSync());
    const iouThreshold = 0.5;
    const indices = nonMaxSuppression2(boxes, scoresData, maxResults, iouThreshold, minConfidence);
    const reshapedDims = netInput.getReshapedInputDimensions(0);
    const inputSize = netInput.inputSize;
    const padX = inputSize / reshapedDims.width;
    const padY = inputSize / reshapedDims.height;
    const boxesData = boxes.arraySync();
    const results = indices.map((idx) => {
      const [top, bottom] = [
        Math.max(0, boxesData[idx][0]),
        Math.min(1, boxesData[idx][2])
      ].map((val) => val * padY);
      const [left, right] = [
        Math.max(0, boxesData[idx][1]),
        Math.min(1, boxesData[idx][3])
      ].map((val) => val * padX);
      return new FaceDetection(scoresData[idx], new Rect(left, top, right - left, bottom - top), { height: netInput.getInputHeight(0), width: netInput.getInputWidth(0) });
    });
    boxes.dispose();
    scores.dispose();
    return results;
  }
  getDefaultModelName() {
    return "ssd_mobilenetv1_model";
  }
  extractParamsFromWeightMap(weightMap) {
    return extractParamsFromWeightMap6(weightMap);
  }
  extractParams(weights) {
    return extractParams6(weights);
  }
};

// src/ssdMobilenetv1/index.ts
function createSsdMobilenetv1(weights) {
  const net = new SsdMobilenetv1();
  net.extractWeights(weights);
  return net;
}
function createFaceDetectionNet(weights) {
  return createSsdMobilenetv1(weights);
}
var FaceDetectionNet = class extends SsdMobilenetv1 {
};

// src/tinyYolov2/const.ts
var IOU_THRESHOLD = 0.4;
var BOX_ANCHORS = [
  new Point(0.738768, 0.874946),
  new Point(2.42204, 2.65704),
  new Point(4.30971, 7.04493),
  new Point(10.246, 4.59428),
  new Point(12.6868, 11.8741)
];
var BOX_ANCHORS_SEPARABLE = [
  new Point(1.603231, 2.094468),
  new Point(6.041143, 7.080126),
  new Point(2.882459, 3.518061),
  new Point(4.266906, 5.178857),
  new Point(9.041765, 10.66308)
];
var MEAN_RGB_SEPARABLE = [117.001, 114.697, 97.404];
var DEFAULT_MODEL_NAME = "tiny_yolov2_model";
var DEFAULT_MODEL_NAME_SEPARABLE_CONV = "tiny_yolov2_separable_conv_model";

// src/tinyYolov2/TinyYolov2Base.ts
var tf39 = __toModule(require_tfjs_esm());

// src/tinyYolov2/config.ts
var isNumber = (arg) => typeof arg === "number";
function validateConfig(config) {
  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) {
    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) => 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) => a || {}).every((a) => 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)}`);
  }
}

// src/tinyYolov2/convWithBatchNorm.ts
var tf36 = __toModule(require_tfjs_esm());

// src/tinyYolov2/leaky.ts
var tf35 = __toModule(require_tfjs_esm());
function leaky(x) {
  return tf35.tidy(() => {
    const min = tf35.mul(x, tf35.scalar(0.10000000149011612));
    return tf35.add(tf35.relu(tf35.sub(x, min)), min);
  });
}

// src/tinyYolov2/convWithBatchNorm.ts
function convWithBatchNorm(x, params) {
  return tf36.tidy(() => {
    let out = tf36.pad(x, [[0, 0], [1, 1], [1, 1], [0, 0]]);
    out = tf36.conv2d(out, params.conv.filters, [1, 1], "valid");
    out = tf36.sub(out, params.bn.sub);
    out = tf36.mul(out, params.bn.truediv);
    out = tf36.add(out, params.conv.bias);
    return leaky(out);
  });
}

// src/tinyYolov2/depthwiseSeparableConv.ts
var tf37 = __toModule(require_tfjs_esm());
function depthwiseSeparableConv2(x, params) {
  return tf37.tidy(() => {
    let out = tf37.pad(x, [[0, 0], [1, 1], [1, 1], [0, 0]]);
    out = tf37.separableConv2d(out, params.depthwise_filter, params.pointwise_filter, [1, 1], "valid");
    out = tf37.add(out, params.bias);
    return leaky(out);
  });
}

// src/tinyYolov2/extractParams.ts
var tf38 = __toModule(require_tfjs_esm());
function extractorsFactory7(extractWeights, paramMappings) {
  const extractConvParams = extractConvParamsFactory(extractWeights, paramMappings);
  function extractBatchNormParams(size, mappedPrefix) {
    const sub6 = tf38.tensor1d(extractWeights(size));
    const truediv = tf38.tensor1d(extractWeights(size));
    paramMappings.push({ paramPath: `${mappedPrefix}/sub` }, { paramPath: `${mappedPrefix}/truediv` });
    return { sub: sub6, truediv };
  }
  function extractConvWithBatchNormParams(channelsIn, channelsOut, mappedPrefix) {
    const conv3 = extractConvParams(channelsIn, channelsOut, 3, `${mappedPrefix}/conv`);
    const bn = extractBatchNormParams(channelsOut, `${mappedPrefix}/bn`);
    return { conv: conv3, bn };
  }
  const extractSeparableConvParams = extractSeparableConvParamsFactory(extractWeights, paramMappings);
  return {
    extractConvParams,
    extractConvWithBatchNormParams,
    extractSeparableConvParams
  };
}
function extractParams7(weights, config, boxEncodingSize, filterSizes) {
  const {
    extractWeights,
    getRemainingWeights
  } = extractWeightsFactory(weights);
  const paramMappings = [];
  const {
    extractConvParams,
    extractConvWithBatchNormParams,
    extractSeparableConvParams
  } = extractorsFactory7(extractWeights, paramMappings);
  let params;
  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 conv22 = 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") : void 0;
    const conv7 = s8 ? extractSeparableConvParams(s7, s8, "conv7") : void 0;
    const conv8 = extractConvParams(s8 || s7 || s6, 5 * boxEncodingSize, 1, "conv8");
    params = {
      conv0,
      conv1,
      conv2: conv22,
      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 conv22 = 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: conv22,
      conv3,
      conv4,
      conv5,
      conv6,
      conv7,
      conv8
    };
  }
  if (getRemainingWeights().length !== 0) {
    throw new Error(`weights remaing after extract: ${getRemainingWeights().length}`);
  }
  return { params, paramMappings };
}

// src/tinyYolov2/extractParamsFromWeightMap.ts
function extractorsFactory8(weightMap, paramMappings) {
  const extractWeightEntry = extractWeightEntryFactory(weightMap, paramMappings);
  function extractBatchNormParams(prefix) {
    const sub6 = extractWeightEntry(`${prefix}/sub`, 1);
    const truediv = extractWeightEntry(`${prefix}/truediv`, 1);
    return { sub: sub6, truediv };
  }
  function extractConvParams(prefix) {
    const filters = extractWeightEntry(`${prefix}/filters`, 4);
    const bias = extractWeightEntry(`${prefix}/bias`, 1);
    return { filters, bias };
  }
  function extractConvWithBatchNormParams(prefix) {
    const conv3 = extractConvParams(`${prefix}/conv`);
    const bn = extractBatchNormParams(`${prefix}/bn`);
    return { conv: conv3, bn };
  }
  const extractSeparableConvParams = loadSeparableConvParamsFactory(extractWeightEntry);
  return {
    extractConvParams,
    extractConvWithBatchNormParams,
    extractSeparableConvParams
  };
}
function extractParamsFromWeightMap7(weightMap, config) {
  const paramMappings = [];
  const {
    extractConvParams,
    extractConvWithBatchNormParams,
    extractSeparableConvParams
  } = extractorsFactory8(weightMap, paramMappings);
  let params;
  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") : void 0,
      conv7: numFilters > 8 ? extractSeparableConvParams("conv7") : void 0,
      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 };
}

// src/tinyYolov2/TinyYolov2Options.ts
var TinyYolov2Options = class {
  constructor({ inputSize, scoreThreshold } = {}) {
    this._name = "TinyYolov2Options";
    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() {
    return this._inputSize;
  }
  get scoreThreshold() {
    return this._scoreThreshold;
  }
};

// src/tinyYolov2/TinyYolov2Base.ts
var _TinyYolov2Base = class extends NeuralNetwork {
  constructor(config) {
    super("TinyYolov2");
    validateConfig(config);
    this._config = config;
  }
  get config() {
    return this._config;
  }
  get withClassScores() {
    return this.config.withClassScores || this.config.classes.length > 1;
  }
  get boxEncodingSize() {
    return 5 + (this.withClassScores ? this.config.classes.length : 0);
  }
  runTinyYolov2(x, params) {
    let out = convWithBatchNorm(x, params.conv0);
    out = tf39.maxPool(out, [2, 2], [2, 2], "same");
    out = convWithBatchNorm(out, params.conv1);
    out = tf39.maxPool(out, [2, 2], [2, 2], "same");
    out = convWithBatchNorm(out, params.conv2);
    out = tf39.maxPool(out, [2, 2], [2, 2], "same");
    out = convWithBatchNorm(out, params.conv3);
    out = tf39.maxPool(out, [2, 2], [2, 2], "same");
    out = convWithBatchNorm(out, params.conv4);
    out = tf39.maxPool(out, [2, 2], [2, 2], "same");
    out = convWithBatchNorm(out, params.conv5);
    out = tf39.maxPool(out, [2, 2], [1, 1], "same");
    out = convWithBatchNorm(out, params.conv6);
    out = convWithBatchNorm(out, params.conv7);
    return convLayer(out, params.conv8, "valid", false);
  }
  runMobilenet(x, params) {
    let out = this.config.isFirstLayerConv2d ? leaky(convLayer(x, params.conv0, "valid", false)) : depthwiseSeparableConv2(x, params.conv0);
    out = tf39.maxPool(out, [2, 2], [2, 2], "same");
    out = depthwiseSeparableConv2(out, params.conv1);
    out = tf39.maxPool(out, [2, 2], [2, 2], "same");
    out = depthwiseSeparableConv2(out, params.conv2);
    out = tf39.maxPool(out, [2, 2], [2, 2], "same");
    out = depthwiseSeparableConv2(out, params.conv3);
    out = tf39.maxPool(out, [2, 2], [2, 2], "same");
    out = depthwiseSeparableConv2(out, params.conv4);
    out = tf39.maxPool(out, [2, 2], [2, 2], "same");
    out = depthwiseSeparableConv2(out, params.conv5);
    out = tf39.maxPool(out, [2, 2], [1, 1], "same");
    out = params.conv6 ? depthwiseSeparableConv2(out, params.conv6) : out;
    out = params.conv7 ? depthwiseSeparableConv2(out, params.conv7) : out;
    return convLayer(out, params.conv8, "valid", false);
  }
  forwardInput(input, inputSize) {
    const { params } = this;
    if (!params) {
      throw new Error("TinyYolov2 - load model before inference");
    }
    return tf39.tidy(() => {
      let batchTensor = tf39.cast(input.toBatchTensor(inputSize, false), "float32");
      batchTensor = this.config.meanRgb ? normalize(batchTensor, this.config.meanRgb) : batchTensor;
      batchTensor = batchTensor.div(255);
      return this.config.withSeparableConvs ? this.runMobilenet(batchTensor, params) : this.runTinyYolov2(batchTensor, params);
    });
  }
  async forward(input, inputSize) {
    return this.forwardInput(await toNetInput(input), inputSize);
  }
  async detect(input, forwardParams = {}) {
    const { inputSize, scoreThreshold } = new TinyYolov2Options(forwardParams);
    const netInput = await toNetInput(input);
    const out = await this.forwardInput(netInput, inputSize);
    const out0 = tf39.tidy(() => tf39.unstack(out)[0].expandDims());
    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;
  }
  getDefaultModelName() {
    return "";
  }
  extractParamsFromWeightMap(weightMap) {
    return extractParamsFromWeightMap7(weightMap, this.config);
  }
  extractParams(weights) {
    const filterSizes = this.config.filterSizes || _TinyYolov2Base.DEFAULT_FILTER_SIZES;
    const numFilters = filterSizes ? filterSizes.length : void 0;
    if (numFilters !== 7 && numFilters !== 8 && numFilters !== 9) {
      throw new Error(`TinyYolov2 - expected 7 | 8 | 9 convolutional filters, but found ${numFilters} filterSizes in config`);
    }
    return extractParams7(weights, this.config, this.boxEncodingSize, filterSizes);
  }
  async extractBoxes(outputTensor, inputBlobDimensions, scoreThreshold) {
    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] = tf39.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 ? tf39.softmax(reshaped.slice([0, 0, 0, 5], [numCells, numCells, numBoxes, this.config.classes.length]), 3) : tf39.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 widthLocal = Math.exp(boxesData[row][col][anchor][2]) * this.config.anchors[anchor].x / numCells * correctionFactorX;
            const heightLocal = Math.exp(boxesData[row][col][anchor][3]) * this.config.anchors[anchor].y / numCells * correctionFactorY;
            const x = ctX - widthLocal / 2;
            const y = ctY - heightLocal / 2;
            const pos = { row, col, anchor };
            const { classScore, label } = this.withClassScores ? await this.extractPredictedClass(classScoresTensor, pos) : { classScore: 1, label: 0 };
            results.push({
              box: new BoundingBox(x, y, x + widthLocal, y + heightLocal),
              score,
              classScore: score * classScore,
              label,
              ...pos
            });
          }
        }
      }
    }
    boxesTensor.dispose();
    scoresTensor.dispose();
    classScoresTensor.dispose();
    return results;
  }
  async extractPredictedClass(classesTensor, pos) {
    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);
  }
};
var TinyYolov2Base = _TinyYolov2Base;
TinyYolov2Base.DEFAULT_FILTER_SIZES = [3, 16, 32, 64, 128, 256, 512, 1024, 1024];

// src/tinyYolov2/TinyYolov2.ts
var TinyYolov2 = class extends TinyYolov2Base {
  constructor(withSeparableConvs = true) {
    const config = {
      withSeparableConvs,
      iouThreshold: IOU_THRESHOLD,
      classes: ["face"],
      ...withSeparableConvs ? {
        anchors: BOX_ANCHORS_SEPARABLE,
        meanRgb: MEAN_RGB_SEPARABLE
      } : {
        anchors: BOX_ANCHORS,
        withClassScores: true
      }
    };
    super(config);
  }
  get withSeparableConvs() {
    return this.config.withSeparableConvs;
  }
  get anchors() {
    return this.config.anchors;
  }
  async locateFaces(input, forwardParams) {
    const objectDetections = await this.detect(input, forwardParams);
    return objectDetections.map((det) => new FaceDetection(det.score, det.relativeBox, { width: det.imageWidth, height: det.imageHeight }));
  }
  getDefaultModelName() {
    return this.withSeparableConvs ? DEFAULT_MODEL_NAME_SEPARABLE_CONV : DEFAULT_MODEL_NAME;
  }
  extractParamsFromWeightMap(weightMap) {
    return super.extractParamsFromWeightMap(weightMap);
  }
};

// src/tinyYolov2/index.ts
function createTinyYolov2(weights, withSeparableConvs = true) {
  const net = new TinyYolov2(withSeparableConvs);
  net.extractWeights(weights);
  return net;
}

// src/tinyFaceDetector/TinyFaceDetectorOptions.ts
var TinyFaceDetectorOptions = class extends TinyYolov2Options {
  constructor() {
    super(...arguments);
    this._name = "TinyFaceDetectorOptions";
  }
};

// src/globalApi/ComposableTask.ts
var ComposableTask = class {
  async then(onfulfilled) {
    return onfulfilled(await this.run());
  }
  async run() {
    throw new Error("ComposableTask - run is not implemented");
  }
};

// src/globalApi/DetectFaceLandmarksTasks.ts
var tf41 = __toModule(require_tfjs_esm());

// src/globalApi/extractFacesAndComputeResults.ts
var tf40 = __toModule(require_tfjs_esm());
async function extractAllFacesAndComputeResults(parentResults, input, computeResults, extractedFaces, getRectForAlignment = ({ alignedRect }) => alignedRect) {
  const faceBoxes = parentResults.map((parentResult) => isWithFaceLandmarks(parentResult) ? getRectForAlignment(parentResult) : parentResult.detection);
  const faces = extractedFaces || (input instanceof tf40.Tensor ? await extractFaceTensors(input, faceBoxes) : await extractFaces(input, faceBoxes));
  const results = await computeResults(faces);
  faces.forEach((f) => f instanceof tf40.Tensor && f.dispose());
  return results;
}
async function extractSingleFaceAndComputeResult(parentResult, input, computeResult, extractedFaces, getRectForAlignment) {
  return extractAllFacesAndComputeResults([parentResult], input, async (faces) => computeResult(faces[0]), extractedFaces, getRectForAlignment);
}

// src/tinyFaceDetector/const.ts
var IOU_THRESHOLD2 = 0.4;
var BOX_ANCHORS2 = [
  new Point(1.603231, 2.094468),
  new Point(6.041143, 7.080126),
  new Point(2.882459, 3.518061),
  new Point(4.266906, 5.178857),
  new Point(9.041765, 10.66308)
];
var MEAN_RGB = [117.001, 114.697, 97.404];

// src/tinyFaceDetector/TinyFaceDetector.ts
var TinyFaceDetector = class extends TinyYolov2Base {
  constructor() {
    const config = {
      withSeparableConvs: true,
      iouThreshold: IOU_THRESHOLD2,
      classes: ["face"],
      anchors: BOX_ANCHORS2,
      meanRgb: MEAN_RGB,
      isFirstLayerConv2d: true,
      filterSizes: [3, 16, 32, 64, 128, 256, 512]
    };
    super(config);
  }
  get anchors() {
    return this.config.anchors;
  }
  async locateFaces(input, forwardParams) {
    const objectDetections = await this.detect(input, forwardParams);
    return objectDetections.map((det) => new FaceDetection(det.score, det.relativeBox, { width: det.imageWidth, height: det.imageHeight }));
  }
  getDefaultModelName() {
    return "tiny_face_detector_model";
  }
  extractParamsFromWeightMap(weightMap) {
    return super.extractParamsFromWeightMap(weightMap);
  }
};

// src/globalApi/nets.ts
var nets = {
  ssdMobilenetv1: new SsdMobilenetv1(),
  tinyFaceDetector: new TinyFaceDetector(),
  tinyYolov2: new TinyYolov2(),
  faceLandmark68Net: new FaceLandmark68Net(),
  faceLandmark68TinyNet: new FaceLandmark68TinyNet(),
  faceRecognitionNet: new FaceRecognitionNet(),
  faceExpressionNet: new FaceExpressionNet(),
  ageGenderNet: new AgeGenderNet()
};
var ssdMobilenetv1 = (input, options) => nets.ssdMobilenetv1.locateFaces(input, options);
var tinyFaceDetector = (input, options) => nets.tinyFaceDetector.locateFaces(input, options);
var tinyYolov2 = (input, options) => nets.tinyYolov2.locateFaces(input, options);
var detectFaceLandmarks = (input) => nets.faceLandmark68Net.detectLandmarks(input);
var detectFaceLandmarksTiny = (input) => nets.faceLandmark68TinyNet.detectLandmarks(input);
var computeFaceDescriptor = (input) => nets.faceRecognitionNet.computeFaceDescriptor(input);
var recognizeFaceExpressions = (input) => nets.faceExpressionNet.predictExpressions(input);
var predictAgeAndGender = (input) => nets.ageGenderNet.predictAgeAndGender(input);
var loadSsdMobilenetv1Model = (url) => nets.ssdMobilenetv1.load(url);
var loadTinyFaceDetectorModel = (url) => nets.tinyFaceDetector.load(url);
var loadTinyYolov2Model = (url) => nets.tinyYolov2.load(url);
var loadFaceLandmarkModel = (url) => nets.faceLandmark68Net.load(url);
var loadFaceLandmarkTinyModel = (url) => nets.faceLandmark68TinyNet.load(url);
var loadFaceRecognitionModel = (url) => nets.faceRecognitionNet.load(url);
var loadFaceExpressionModel = (url) => nets.faceExpressionNet.load(url);
var loadAgeGenderModel = (url) => nets.ageGenderNet.load(url);
var loadFaceDetectionModel = loadSsdMobilenetv1Model;
var locateFaces = ssdMobilenetv1;
var detectLandmarks = detectFaceLandmarks;

// src/globalApi/PredictFaceExpressionsTask.ts
var PredictFaceExpressionsTaskBase = class extends ComposableTask {
  constructor(parentTask, input, extractedFaces) {
    super();
    this.parentTask = parentTask;
    this.input = input;
    this.extractedFaces = extractedFaces;
  }
};
var PredictAllFaceExpressionsTask = class extends PredictFaceExpressionsTaskBase {
  async run() {
    const parentResults = await this.parentTask;
    const faceExpressionsByFace = await extractAllFacesAndComputeResults(parentResults, this.input, async (faces) => Promise.all(faces.map((face) => nets.faceExpressionNet.predictExpressions(face))), this.extractedFaces);
    return parentResults.map((parentResult, i) => extendWithFaceExpressions(parentResult, faceExpressionsByFace[i]));
  }
  withAgeAndGender() {
    return new PredictAllAgeAndGenderTask(this, this.input);
  }
};
var PredictSingleFaceExpressionsTask = class extends PredictFaceExpressionsTaskBase {
  async run() {
    const parentResult = await this.parentTask;
    if (!parentResult) {
      return void 0;
    }
    const faceExpressions = await extractSingleFaceAndComputeResult(parentResult, this.input, (face) => nets.faceExpressionNet.predictExpressions(face), this.extractedFaces);
    return extendWithFaceExpressions(parentResult, faceExpressions);
  }
  withAgeAndGender() {
    return new PredictSingleAgeAndGenderTask(this, this.input);
  }
};
var PredictAllFaceExpressionsWithFaceAlignmentTask = class extends PredictAllFaceExpressionsTask {
  withAgeAndGender() {
    return new PredictAllAgeAndGenderWithFaceAlignmentTask(this, this.input);
  }
  withFaceDescriptors() {
    return new ComputeAllFaceDescriptorsTask(this, this.input);
  }
};
var PredictSingleFaceExpressionsWithFaceAlignmentTask = class extends PredictSingleFaceExpressionsTask {
  withAgeAndGender() {
    return new PredictSingleAgeAndGenderWithFaceAlignmentTask(this, this.input);
  }
  withFaceDescriptor() {
    return new ComputeSingleFaceDescriptorTask(this, this.input);
  }
};

// src/globalApi/PredictAgeAndGenderTask.ts
var PredictAgeAndGenderTaskBase = class extends ComposableTask {
  constructor(parentTask, input, extractedFaces) {
    super();
    this.parentTask = parentTask;
    this.input = input;
    this.extractedFaces = extractedFaces;
  }
};
var PredictAllAgeAndGenderTask = class extends PredictAgeAndGenderTaskBase {
  async run() {
    const parentResults = await this.parentTask;
    const ageAndGenderByFace = await extractAllFacesAndComputeResults(parentResults, this.input, async (faces) => Promise.all(faces.map((face) => nets.ageGenderNet.predictAgeAndGender(face))), this.extractedFaces);
    return parentResults.map((parentResult, i) => {
      const { age, gender, genderProbability } = ageAndGenderByFace[i];
      return extendWithAge(extendWithGender(parentResult, gender, genderProbability), age);
    });
  }
  withFaceExpressions() {
    return new PredictAllFaceExpressionsTask(this, this.input);
  }
};
var PredictSingleAgeAndGenderTask = class extends PredictAgeAndGenderTaskBase {
  async run() {
    const parentResult = await this.parentTask;
    if (!parentResult)
      return void 0;
    const { age, gender, genderProbability } = await extractSingleFaceAndComputeResult(parentResult, this.input, (face) => nets.ageGenderNet.predictAgeAndGender(face), this.extractedFaces);
    return extendWithAge(extendWithGender(parentResult, gender, genderProbability), age);
  }
  withFaceExpressions() {
    return new PredictSingleFaceExpressionsTask(this, this.input);
  }
};
var PredictAllAgeAndGenderWithFaceAlignmentTask = class extends PredictAllAgeAndGenderTask {
  withFaceExpressions() {
    return new PredictAllFaceExpressionsWithFaceAlignmentTask(this, this.input);
  }
  withFaceDescriptors() {
    return new ComputeAllFaceDescriptorsTask(this, this.input);
  }
};
var PredictSingleAgeAndGenderWithFaceAlignmentTask = class extends PredictSingleAgeAndGenderTask {
  withFaceExpressions() {
    return new PredictSingleFaceExpressionsWithFaceAlignmentTask(this, this.input);
  }
  withFaceDescriptor() {
    return new ComputeSingleFaceDescriptorTask(this, this.input);
  }
};

// src/globalApi/ComputeFaceDescriptorsTasks.ts
var ComputeFaceDescriptorsTaskBase = class extends ComposableTask {
  constructor(parentTask, input) {
    super();
    this.parentTask = parentTask;
    this.input = input;
  }
};
var ComputeAllFaceDescriptorsTask = class extends ComputeFaceDescriptorsTaskBase {
  async run() {
    const parentResults = await this.parentTask;
    const descriptors = await extractAllFacesAndComputeResults(parentResults, this.input, (faces) => Promise.all(faces.map((face) => nets.faceRecognitionNet.computeFaceDescriptor(face))), null, (parentResult) => parentResult.landmarks.align(null, { useDlibAlignment: true }));
    return descriptors.map((descriptor, i) => extendWithFaceDescriptor(parentResults[i], descriptor));
  }
  withFaceExpressions() {
    return new PredictAllFaceExpressionsWithFaceAlignmentTask(this, this.input);
  }
  withAgeAndGender() {
    return new PredictAllAgeAndGenderWithFaceAlignmentTask(this, this.input);
  }
};
var ComputeSingleFaceDescriptorTask = class extends ComputeFaceDescriptorsTaskBase {
  async run() {
    const parentResult = await this.parentTask;
    if (!parentResult) {
      return void 0;
    }
    const descriptor = await extractSingleFaceAndComputeResult(parentResult, this.input, (face) => nets.faceRecognitionNet.computeFaceDescriptor(face), null, (parentResult2) => parentResult2.landmarks.align(null, { useDlibAlignment: true }));
    return extendWithFaceDescriptor(parentResult, descriptor);
  }
  withFaceExpressions() {
    return new PredictSingleFaceExpressionsWithFaceAlignmentTask(this, this.input);
  }
  withAgeAndGender() {
    return new PredictSingleAgeAndGenderWithFaceAlignmentTask(this, this.input);
  }
};

// src/globalApi/DetectFaceLandmarksTasks.ts
var DetectFaceLandmarksTaskBase = class extends ComposableTask {
  constructor(parentTask, input, useTinyLandmarkNet) {
    super();
    this.parentTask = parentTask;
    this.input = input;
    this.useTinyLandmarkNet = useTinyLandmarkNet;
  }
  get landmarkNet() {
    return this.useTinyLandmarkNet ? nets.faceLandmark68TinyNet : nets.faceLandmark68Net;
  }
};
var DetectAllFaceLandmarksTask = class extends DetectFaceLandmarksTaskBase {
  async run() {
    const parentResults = await this.parentTask;
    const detections = parentResults.map((res) => res.detection);
    const faces = this.input instanceof tf41.Tensor ? await extractFaceTensors(this.input, detections) : await extractFaces(this.input, detections);
    const faceLandmarksByFace = await Promise.all(faces.map((face) => this.landmarkNet.detectLandmarks(face)));
    faces.forEach((f) => f instanceof tf41.Tensor && f.dispose());
    return parentResults.map((parentResult, i) => extendWithFaceLandmarks(parentResult, faceLandmarksByFace[i]));
  }
  withFaceExpressions() {
    return new PredictAllFaceExpressionsWithFaceAlignmentTask(this, this.input);
  }
  withAgeAndGender() {
    return new PredictAllAgeAndGenderWithFaceAlignmentTask(this, this.input);
  }
  withFaceDescriptors() {
    return new ComputeAllFaceDescriptorsTask(this, this.input);
  }
};
var DetectSingleFaceLandmarksTask = class extends DetectFaceLandmarksTaskBase {
  async run() {
    const parentResult = await this.parentTask;
    if (!parentResult) {
      return void 0;
    }
    const { detection } = parentResult;
    const faces = this.input instanceof tf41.Tensor ? await extractFaceTensors(this.input, [detection]) : await extractFaces(this.input, [detection]);
    const landmarks = await this.landmarkNet.detectLandmarks(faces[0]);
    faces.forEach((f) => f instanceof tf41.Tensor && f.dispose());
    return extendWithFaceLandmarks(parentResult, landmarks);
  }
  withFaceExpressions() {
    return new PredictSingleFaceExpressionsWithFaceAlignmentTask(this, this.input);
  }
  withAgeAndGender() {
    return new PredictSingleAgeAndGenderWithFaceAlignmentTask(this, this.input);
  }
  withFaceDescriptor() {
    return new ComputeSingleFaceDescriptorTask(this, this.input);
  }
};

// src/globalApi/DetectFacesTasks.ts
var DetectFacesTaskBase = class extends ComposableTask {
  constructor(input, options = new SsdMobilenetv1Options()) {
    super();
    this.input = input;
    this.options = options;
  }
};
var DetectAllFacesTask = class extends DetectFacesTaskBase {
  async run() {
    const { input, options } = this;
    let result;
    if (options instanceof TinyFaceDetectorOptions)
      result = nets.tinyFaceDetector.locateFaces(input, options);
    else if (options instanceof SsdMobilenetv1Options)
      result = nets.ssdMobilenetv1.locateFaces(input, options);
    else if (options instanceof TinyYolov2Options)
      result = nets.tinyYolov2.locateFaces(input, options);
    else
      throw new Error("detectFaces - expected options to be instance of TinyFaceDetectorOptions | SsdMobilenetv1Options | TinyYolov2Options");
    return result;
  }
  runAndExtendWithFaceDetections() {
    return new Promise((resolve, reject) => {
      this.run().then((detections) => resolve(detections.map((detection) => extendWithFaceDetection({}, detection)))).catch((err) => reject(err));
    });
  }
  withFaceLandmarks(useTinyLandmarkNet = false) {
    return new DetectAllFaceLandmarksTask(this.runAndExtendWithFaceDetections(), this.input, useTinyLandmarkNet);
  }
  withFaceExpressions() {
    return new PredictAllFaceExpressionsTask(this.runAndExtendWithFaceDetections(), this.input);
  }
  withAgeAndGender() {
    return new PredictAllAgeAndGenderTask(this.runAndExtendWithFaceDetections(), this.input);
  }
};
var DetectSingleFaceTask = class extends DetectFacesTaskBase {
  async run() {
    const faceDetections = await new DetectAllFacesTask(this.input, this.options);
    let faceDetectionWithHighestScore = faceDetections[0];
    faceDetections.forEach((faceDetection) => {
      if (faceDetection.score > faceDetectionWithHighestScore.score)
        faceDetectionWithHighestScore = faceDetection;
    });
    return faceDetectionWithHighestScore;
  }
  runAndExtendWithFaceDetection() {
    return new Promise(async (resolve) => {
      const detection = await this.run();
      resolve(detection ? extendWithFaceDetection({}, detection) : void 0);
    });
  }
  withFaceLandmarks(useTinyLandmarkNet = false) {
    return new DetectSingleFaceLandmarksTask(this.runAndExtendWithFaceDetection(), this.input, useTinyLandmarkNet);
  }
  withFaceExpressions() {
    return new PredictSingleFaceExpressionsTask(this.runAndExtendWithFaceDetection(), this.input);
  }
  withAgeAndGender() {
    return new PredictSingleAgeAndGenderTask(this.runAndExtendWithFaceDetection(), this.input);
  }
};

// src/globalApi/detectFaces.ts
function detectSingleFace(input, options = new SsdMobilenetv1Options()) {
  return new DetectSingleFaceTask(input, options);
}
function detectAllFaces(input, options = new SsdMobilenetv1Options()) {
  return new DetectAllFacesTask(input, options);
}

// src/globalApi/allFaces.ts
async function allFacesSsdMobilenetv1(input, minConfidence) {
  return detectAllFaces(input, new SsdMobilenetv1Options(minConfidence ? { minConfidence } : {})).withFaceLandmarks().withFaceDescriptors();
}
async function allFacesTinyYolov2(input, forwardParams = {}) {
  return detectAllFaces(input, new TinyYolov2Options(forwardParams)).withFaceLandmarks().withFaceDescriptors();
}
var allFaces = allFacesSsdMobilenetv1;

// src/euclideanDistance.ts
function euclideanDistance(arr1, arr2) {
  if (arr1.length !== arr2.length)
    throw new Error("euclideanDistance: arr1.length !== arr2.length");
  const desc1 = Array.from(arr1);
  const desc2 = Array.from(arr2);
  return Math.sqrt(desc1.map((val, i) => val - desc2[i]).reduce((res, diff) => res + diff ** 2, 0));
}

// src/globalApi/FaceMatcher.ts
var FaceMatcher = class {
  constructor(inputs, distanceThreshold = 0.6) {
    this._distanceThreshold = distanceThreshold;
    const inputArray = Array.isArray(inputs) ? inputs : [inputs];
    if (!inputArray.length) {
      throw new Error("FaceRecognizer.constructor - expected atleast one input");
    }
    let count = 1;
    const createUniqueLabel = () => `person ${count++}`;
    this._labeledDescriptors = inputArray.map((desc) => {
      if (desc instanceof LabeledFaceDescriptors) {
        return desc;
      }
      if (desc instanceof Float32Array) {
        return new LabeledFaceDescriptors(createUniqueLabel(), [desc]);
      }
      if (desc.descriptor && desc.descriptor instanceof Float32Array) {
        return new LabeledFaceDescriptors(createUniqueLabel(), [desc.descriptor]);
      }
      throw new Error("FaceRecognizer.constructor - expected inputs to be of type LabeledFaceDescriptors | WithFaceDescriptor<any> | Float32Array | Array<LabeledFaceDescriptors | WithFaceDescriptor<any> | Float32Array>");
    });
  }
  get labeledDescriptors() {
    return this._labeledDescriptors;
  }
  get distanceThreshold() {
    return this._distanceThreshold;
  }
  computeMeanDistance(queryDescriptor, descriptors) {
    return descriptors.map((d) => euclideanDistance(d, queryDescriptor)).reduce((d1, d2) => d1 + d2, 0) / (descriptors.length || 1);
  }
  matchDescriptor(queryDescriptor) {
    return this.labeledDescriptors.map(({ descriptors, label }) => new FaceMatch(label, this.computeMeanDistance(queryDescriptor, descriptors))).reduce((best, curr) => best.distance < curr.distance ? best : curr);
  }
  findBestMatch(queryDescriptor) {
    const bestMatch = this.matchDescriptor(queryDescriptor);
    return bestMatch.distance < this.distanceThreshold ? bestMatch : new FaceMatch("unknown", bestMatch.distance);
  }
  toJSON() {
    return {
      distanceThreshold: this.distanceThreshold,
      labeledDescriptors: this.labeledDescriptors.map((ld) => ld.toJSON())
    };
  }
  static fromJSON(json) {
    const labeledDescriptors = json.labeledDescriptors.map((ld) => LabeledFaceDescriptors.fromJSON(ld));
    return new FaceMatcher(labeledDescriptors, json.distanceThreshold);
  }
};

// src/tinyFaceDetector/index.ts
function createTinyFaceDetector(weights) {
  const net = new TinyFaceDetector();
  net.extractWeights(weights);
  return net;
}

// src/resizeResults.ts
function resizeResults(results, dimensions) {
  const { width, height } = new Dimensions(dimensions.width, dimensions.height);
  if (width <= 0 || height <= 0) {
    throw new Error(`resizeResults - invalid dimensions: ${JSON.stringify({ width, height })}`);
  }
  if (Array.isArray(results)) {
    return results.map((obj) => resizeResults(obj, { width, height }));
  }
  if (isWithFaceLandmarks(results)) {
    const resizedDetection = results.detection.forSize(width, height);
    const resizedLandmarks = results.unshiftedLandmarks.forSize(resizedDetection.box.width, resizedDetection.box.height);
    return extendWithFaceLandmarks(extendWithFaceDetection(results, resizedDetection), resizedLandmarks);
  }
  if (isWithFaceDetection(results)) {
    return extendWithFaceDetection(results, results.detection.forSize(width, height));
  }
  if (results instanceof FaceLandmarks || results instanceof FaceDetection) {
    return results.forSize(width, height);
  }
  return results;
}

// src/index.ts
var node = typeof process !== "undefined";
var browser3 = typeof navigator !== "undefined" && typeof navigator.userAgent !== "undefined";
var version2 = { faceapi: version, node, browser: browser3 };
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  AgeGenderNet,
  BoundingBox,
  Box,
  ComposableTask,
  ComputeAllFaceDescriptorsTask,
  ComputeFaceDescriptorsTaskBase,
  ComputeSingleFaceDescriptorTask,
  DetectAllFaceLandmarksTask,
  DetectAllFacesTask,
  DetectFaceLandmarksTaskBase,
  DetectFacesTaskBase,
  DetectSingleFaceLandmarksTask,
  DetectSingleFaceTask,
  Dimensions,
  FACE_EXPRESSION_LABELS,
  FaceDetection,
  FaceDetectionNet,
  FaceExpressionNet,
  FaceExpressions,
  FaceLandmark68Net,
  FaceLandmark68TinyNet,
  FaceLandmarkNet,
  FaceLandmarks,
  FaceLandmarks5,
  FaceLandmarks68,
  FaceMatch,
  FaceMatcher,
  FaceRecognitionNet,
  Gender,
  LabeledBox,
  LabeledFaceDescriptors,
  NetInput,
  NeuralNetwork,
  ObjectDetection,
  Point,
  PredictedBox,
  Rect,
  SsdMobilenetv1,
  SsdMobilenetv1Options,
  TinyFaceDetector,
  TinyFaceDetectorOptions,
  TinyYolov2,
  TinyYolov2Options,
  allFaces,
  allFacesSsdMobilenetv1,
  allFacesTinyYolov2,
  awaitMediaLoaded,
  bufferToImage,
  computeFaceDescriptor,
  createCanvas,
  createCanvasFromMedia,
  createFaceDetectionNet,
  createFaceRecognitionNet,
  createSsdMobilenetv1,
  createTinyFaceDetector,
  createTinyYolov2,
  detectAllFaces,
  detectFaceLandmarks,
  detectFaceLandmarksTiny,
  detectLandmarks,
  detectSingleFace,
  draw,
  env,
  euclideanDistance,
  extendWithAge,
  extendWithFaceDescriptor,
  extendWithFaceDetection,
  extendWithFaceExpressions,
  extendWithFaceLandmarks,
  extendWithGender,
  extractFaceTensors,
  extractFaces,
  fetchImage,
  fetchJson,
  fetchNetWeights,
  fetchOrThrow,
  fetchVideo,
  getContext2dOrThrow,
  getMediaDimensions,
  imageTensorToCanvas,
  imageToSquare,
  inverseSigmoid,
  iou,
  isMediaElement,
  isMediaLoaded,
  isWithAge,
  isWithFaceDetection,
  isWithFaceExpressions,
  isWithFaceLandmarks,
  isWithGender,
  loadAgeGenderModel,
  loadFaceDetectionModel,
  loadFaceExpressionModel,
  loadFaceLandmarkModel,
  loadFaceLandmarkTinyModel,
  loadFaceRecognitionModel,
  loadSsdMobilenetv1Model,
  loadTinyFaceDetectorModel,
  loadTinyYolov2Model,
  loadWeightMap,
  locateFaces,
  matchDimensions,
  minBbox,
  nets,
  nonMaxSuppression,
  normalize,
  padToSquare,
  predictAgeAndGender,
  recognizeFaceExpressions,
  resizeResults,
  resolveInput,
  shuffleArray,
  sigmoid,
  ssdMobilenetv1,
  tf,
  tinyFaceDetector,
  tinyYolov2,
  toNetInput,
  utils,
  validateConfig,
  version
});
//# sourceMappingURL=face-api.node.js.map