Procházet zdrojové kódy

Remove unnecessary dependency

Manav Rathi před 1 rokem
rodič
revize
b37f67d6ed

+ 3 - 3
web/apps/photos/src/services/machineLearning/yoloFaceDetectionService.ts

@@ -1,5 +1,6 @@
 import { workerBridge } from "@/next/worker/worker-bridge";
 import { euclidean } from "hdbscan";
+import { Box, Point, boxFromBoundingBox } from "services/ml/geom";
 import {
     FaceDetection,
     FaceDetectionMethod,
@@ -20,7 +21,6 @@ import {
     normalizePixelBetween0And1,
 } from "utils/image";
 import { newBox } from "utils/machineLearning";
-import { Box, Point } from "../../../thirdparty/face-api/classes";
 
 class YoloFaceDetectionService implements FaceDetectionService {
     public method: Versioned<FaceDetectionMethod>;
@@ -296,7 +296,7 @@ function getDetectionCenter(detection: FaceDetection) {
         center.y += p.y;
     });
 
-    return center.div({ x: 4, y: 4 });
+    return center.div(new Point(4, 4));
 }
 
 function computeTransformToBox(inBox: Box, toBox: Box): Matrix {
@@ -328,5 +328,5 @@ function newBoxFromPoints(
     right: number,
     bottom: number,
 ) {
-    return new Box({ left, top, right, bottom });
+    return boxFromBoundingBox({ left, top, right, bottom });
 }

+ 108 - 0
web/apps/photos/src/services/ml/geom.ts

@@ -0,0 +1,108 @@
+export class Point {
+    public x: number;
+    public y: number;
+
+    constructor(x: number, y: number) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public add(pt: Point): Point {
+        return new Point(this.x + pt.x, this.y + pt.y);
+    }
+
+    public sub(pt: Point): Point {
+        return new Point(this.x - pt.x, this.y - pt.y);
+    }
+
+    public div(pt: Point): Point {
+        return new Point(this.x / pt.x, this.y / pt.y);
+    }
+}
+
+export interface IBoundingBox {
+    left: number;
+    top: number;
+    right: number;
+    bottom: number;
+}
+
+export interface IRect {
+    x: number;
+    y: number;
+    width: number;
+    height: number;
+}
+
+export const boxFromBoundingBox = ({
+    left,
+    top,
+    right,
+    bottom,
+}: IBoundingBox) => {
+    return new Box({
+        x: left,
+        y: top,
+        width: right - left,
+        height: bottom - top,
+    });
+};
+
+export class Box implements IRect {
+    public x: number;
+    public y: number;
+    public width: number;
+    public height: number;
+
+    constructor({ x, y, width, height }: IRect) {
+        this.x = x;
+        this.y = y;
+        this.width = width;
+        this.height = height;
+    }
+
+    public get left(): number {
+        return this.x;
+    }
+    public get top(): number {
+        return this.y;
+    }
+    public get right(): number {
+        return this.x + this.width;
+    }
+    public get bottom(): number {
+        return this.y + this.height;
+    }
+    public get area(): number {
+        return this.width * this.height;
+    }
+    public get topLeft(): Point {
+        return new Point(this.left, this.top);
+    }
+    public get topRight(): Point {
+        return new Point(this.right, this.top);
+    }
+    public get bottomLeft(): Point {
+        return new Point(this.left, this.bottom);
+    }
+    public get bottomRight(): Point {
+        return new Point(this.right, this.bottom);
+    }
+
+    public round(): Box {
+        const [x, y, width, height] = [
+            this.x,
+            this.y,
+            this.width,
+            this.height,
+        ].map((val) => Math.round(val));
+        return new Box({ x, y, width, height });
+    }
+}
+
+export function isValidNumber(num: any) {
+    return (
+        (!!num && num !== Infinity && num !== -Infinity && !isNaN(num)) ||
+        num === 0
+    );
+}

+ 1 - 1
web/apps/photos/src/services/ml/types.ts

@@ -2,7 +2,7 @@ import { DebugInfo } from "hdbscan";
 import PQueue from "p-queue";
 import { EnteFile } from "types/file";
 import { Dimensions } from "types/image";
-import { Box, Point } from "../../../thirdparty/face-api/classes";
+import { Box, Point } from "./geom";
 
 export interface MLSyncResult {
     nOutOfSyncFiles: number;

+ 1 - 1
web/apps/photos/src/utils/image/index.ts

@@ -4,7 +4,7 @@ import { Matrix, inverse } from "ml-matrix";
 import { FaceAlignment } from "services/ml/types";
 import { BlobOptions, Dimensions } from "types/image";
 import { enlargeBox } from "utils/machineLearning";
-import { Box } from "../../../thirdparty/face-api/classes";
+import { Box } from "services/ml/geom";
 
 export function normalizePixelBetween0And1(pixelValue: number) {
     return pixelValue / 255.0;

+ 1 - 1
web/apps/photos/src/utils/machineLearning/faceAlign.ts

@@ -1,7 +1,7 @@
 import { Matrix } from "ml-matrix";
 import { FaceAlignment, FaceDetection } from "services/ml/types";
 import { getSimilarityTransformation } from "similarity-transformation";
-import { Point } from "../../../thirdparty/face-api/classes";
+import { Point } from "services/ml/geom";
 
 const ARCFACE_LANDMARKS = [
     [38.2946, 51.6963],

+ 1 - 1
web/apps/photos/src/utils/machineLearning/faceCrop.ts

@@ -1,7 +1,7 @@
 import { FaceAlignment, FaceCrop, FaceCropConfig } from "services/ml/types";
 import { cropWithRotation } from "utils/image";
 import { enlargeBox } from ".";
-import { Box } from "../../../thirdparty/face-api/classes";
+import { Box } from "services/ml/geom";
 
 export function getFaceCrop(
     imageBitmap: ImageBitmap,

+ 2 - 2
web/apps/photos/src/utils/machineLearning/index.ts

@@ -4,6 +4,7 @@ import log from "@/next/log";
 import PQueue from "p-queue";
 import DownloadManager from "services/download";
 import { getLocalFiles } from "services/fileService";
+import { Box, Point, boxFromBoundingBox } from "services/ml/geom";
 import {
     DetectedFace,
     Face,
@@ -17,7 +18,6 @@ import { Dimensions } from "types/image";
 import { getRenderableImage } from "utils/file";
 import { clamp, warpAffineFloat32List } from "utils/image";
 import mlIDbStorage from "utils/storage/mlIDbStorage";
-import { Box, Point } from "../../../thirdparty/face-api/classes";
 
 export function newBox(x: number, y: number, width: number, height: number) {
     return new Box({ x, y, width, height });
@@ -36,7 +36,7 @@ export function enlargeBox(box: Box, factor: number = 1.5) {
     const size = new Point(box.width, box.height);
     const newHalfSize = new Point((factor * size.x) / 2, (factor * size.y) / 2);
 
-    return new Box({
+    return boxFromBoundingBox({
         left: center.x - newHalfSize.x,
         top: center.y - newHalfSize.y,
         right: center.x + newHalfSize.x,

+ 0 - 14
web/apps/photos/thirdparty/face-api/classes/BoundingBox.ts

@@ -1,14 +0,0 @@
-import { Box } from './Box';
-
-export interface IBoundingBox {
-  left: number
-  top: number
-  right: number
-  bottom: number
-}
-
-export class BoundingBox extends Box<BoundingBox> implements IBoundingBox {
-  constructor(left: number, top: number, right: number, bottom: number, allowNegativeDimensions: boolean = false) {
-    super({ left, top, right, bottom }, allowNegativeDimensions)
-  }
-}

+ 0 - 182
web/apps/photos/thirdparty/face-api/classes/Box.ts

@@ -1,182 +0,0 @@
-import { IBoundingBox } from './BoundingBox';
-import { IDimensions } from './Dimensions';
-import { Point } from './Point';
-import { IRect } from './Rect';
-
-export class Box<BoxType = any> implements IBoundingBox, IRect {
-
-  public static isRect(rect: any): boolean {
-    return !!rect && [rect.x, rect.y, rect.width, rect.height].every(isValidNumber)
-  }
-
-  public static assertIsValidBox(box: any, callee: string, allowNegativeDimensions: boolean = false) {
-    if (!Box.isRect(box)) {
-      throw new Error(`${callee} - invalid box: ${JSON.stringify(box)}, expected object with properties x, y, width, height`)
-    }
-
-    if (!allowNegativeDimensions && (box.width < 0 || box.height < 0)) {
-      throw new Error(`${callee} - width (${box.width}) and height (${box.height}) must be positive numbers`)
-    }
-  }
-
-  public x: number
-  public y: number
-  public width: number
-  public height: number
-
-  constructor(_box: IBoundingBox | IRect, allowNegativeDimensions: boolean = true) {
-    const box = (_box || {}) as any
-
-    const isBbox = [box.left, box.top, box.right, box.bottom].every(isValidNumber)
-    const isRect = [box.x, box.y, box.width, box.height].every(isValidNumber)
-
-    if (!isRect && !isBbox) {
-      throw new Error(`Box.constructor - expected box to be IBoundingBox | IRect, instead have ${JSON.stringify(box)}`)
-    }
-
-    const [x, y, width, height] = isRect
-      ? [box.x, box.y, box.width, box.height]
-      : [box.left, box.top, box.right - box.left, box.bottom - box.top]
-
-    Box.assertIsValidBox({ x, y, width, height }, 'Box.constructor', allowNegativeDimensions)
-
-    this.x = x
-    this.y = y
-    this.width = width
-    this.height = height
-  }
-
-  // public get x(): number { return this._x }
-  // public get y(): number { return this._y }
-  // public get width(): number { return this._width }
-  // public get height(): number { return this._height }
-  public get left(): number { return this.x }
-  public get top(): number { return this.y }
-  public get right(): number { return this.x + this.width }
-  public get bottom(): number { return this.y + this.height }
-  public get area(): number { return this.width * this.height }
-  public get topLeft(): Point { return new Point(this.left, this.top) }
-  public get topRight(): Point { return new Point(this.right, this.top) }
-  public get bottomLeft(): Point { return new Point(this.left, this.bottom) }
-  public get bottomRight(): Point { return new Point(this.right, this.bottom) }
-
-  public round(): Box<BoxType> {
-    const [x, y, width, height] = [this.x, this.y, this.width, this.height]
-      .map(val => Math.round(val))
-    return new Box({ x, y, width, height })
-  }
-
-  public floor(): Box<BoxType> {
-    const [x, y, width, height] = [this.x, this.y, this.width, this.height]
-      .map(val => Math.floor(val))
-    return new Box({ x, y, width, height })
-  }
-
-  public toSquare(): Box<BoxType> {
-    let { x, y, width, height } = this
-    const diff = Math.abs(width - height)
-    if (width < height) {
-      x -= (diff / 2)
-      width += diff
-    }
-    if (height < width) {
-      y -= (diff / 2)
-      height += diff
-    }
-
-    return new Box({ x, y, width, height })
-  }
-
-  public rescale(s: IDimensions | number): Box<BoxType> {
-    const scaleX = isDimensions(s) ? (s as IDimensions).width : s as number
-    const scaleY = isDimensions(s) ? (s as IDimensions).height : s as number
-    return new Box({
-      x: this.x * scaleX,
-      y: this.y * scaleY,
-      width: this.width * scaleX,
-      height: this.height * scaleY
-    })
-  }
-
-  public pad(padX: number, padY: number): Box<BoxType> {
-    let [x, y, width, height] = [
-      this.x - (padX / 2),
-      this.y - (padY / 2),
-      this.width + padX,
-      this.height + padY
-    ]
-    return new Box({ x, y, width, height })
-  }
-
-  public clipAtImageBorders(imgWidth: number, imgHeight: number): Box<BoxType> {
-    const { x, y, right, bottom } = this
-    const clippedX = Math.max(x, 0)
-    const clippedY = Math.max(y, 0)
-
-    const newWidth = right - clippedX
-    const newHeight = bottom - clippedY
-    const clippedWidth = Math.min(newWidth, imgWidth - clippedX)
-    const clippedHeight = Math.min(newHeight, imgHeight - clippedY)
-
-    return (new Box({ x: clippedX, y: clippedY, width: clippedWidth, height: clippedHeight})).floor()
-  }
-
-  public shift(sx: number, sy: number): Box<BoxType> {
-    const { width, height } = this
-    const x = this.x + sx
-    const y = this.y + sy
-
-    return new Box({ x, y, width, height })
-  }
-
-  public padAtBorders(imageHeight: number, imageWidth: number) {
-    const w = this.width + 1
-    const h = this.height + 1
-
-    let dx = 1
-    let dy = 1
-    let edx = w
-    let edy = h
-
-    let x = this.left
-    let y = this.top
-    let ex = this.right
-    let ey = this.bottom
-
-    if (ex > imageWidth) {
-      edx = -ex + imageWidth + w
-      ex = imageWidth
-    }
-    if (ey > imageHeight) {
-      edy = -ey + imageHeight + h
-      ey = imageHeight
-    }
-    if (x < 1) {
-      edy = 2 - x
-      x = 1
-    }
-    if (y < 1) {
-      edy = 2 - y
-      y = 1
-    }
-
-    return { dy, edy, dx, edx, y, ey, x, ex, w, h }
-  }
-
-  public calibrate(region: Box) {
-    return new Box({
-      left: this.left + (region.left * this.width),
-      top: this.top + (region.top * this.height),
-      right: this.right + (region.right * this.width),
-      bottom: this.bottom + (region.bottom * this.height)
-    }).toSquare().round()
-  }
-}
-
-export function isValidNumber(num: any) {
-  return !!num && num !== Infinity && num !== -Infinity && !isNaN(num) || num === 0
-}
-
-export function isDimensions(obj: any): boolean {
-    return obj && obj.width && obj.height
-}

+ 0 - 28
web/apps/photos/thirdparty/face-api/classes/Dimensions.ts

@@ -1,28 +0,0 @@
-import { isValidNumber } from './Box';
-
-export interface IDimensions {
-  width: number
-  height: number
-}
-
-export class Dimensions implements IDimensions {
-
-  private _width: number
-  private _height: number
-
-  constructor(width: number, height: number) {
-    if (!isValidNumber(width) || !isValidNumber(height)) {
-      throw new Error(`Dimensions.constructor - expected width and height to be valid numbers, instead have ${JSON.stringify({ width, height })}`)
-    }
-
-    this._width = width
-    this._height = height
-  }
-
-  public get width(): number { return this._width }
-  public get height(): number { return this._height }
-
-  public reverse(): Dimensions {
-    return new Dimensions(1 / this.width, 1 / this.height)
-  }
-}

+ 0 - 55
web/apps/photos/thirdparty/face-api/classes/Point.ts

@@ -1,55 +0,0 @@
-export interface IPoint {
-  x: number
-  y: number
-}
-
-export class Point implements IPoint {
-  public x: number
-  public y: number
-
-  constructor(x: number, y: number) {
-    this.x = x
-    this.y = y
-  }
-
-  // get x(): number { return this._x }
-  // get y(): number { return this._y }
-
-  public add(pt: IPoint): Point {
-    return new Point(this.x + pt.x, this.y + pt.y)
-  }
-
-  public sub(pt: IPoint): Point {
-    return new Point(this.x - pt.x, this.y - pt.y)
-  }
-
-  public mul(pt: IPoint): Point {
-    return new Point(this.x * pt.x, this.y * pt.y)
-  }
-
-  public div(pt: IPoint): Point {
-    return new Point(this.x / pt.x, this.y / pt.y)
-  }
-
-  public abs(): Point {
-    return new Point(Math.abs(this.x), Math.abs(this.y))
-  }
-
-  public magnitude(): number {
-    return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2))
-  }
-
-  public floor(): Point {
-    return new Point(Math.floor(this.x), Math.floor(this.y))
-  }
-
-  public round(): Point {
-    return new Point(Math.round(this.x), Math.round(this.y))
-  }
-
-  public bound(lower: number, higher: number): Point {
-    const x = Math.max(lower, Math.min(higher, this.x));
-    const y = Math.max(lower, Math.min(higher, this.y));
-    return new Point(x, y);
-  }
-}

+ 0 - 14
web/apps/photos/thirdparty/face-api/classes/Rect.ts

@@ -1,14 +0,0 @@
-import { Box } from './Box';
-
-export interface IRect {
-  x: number
-  y: number
-  width: number
-  height: number
-}
-
-export class Rect extends Box<Rect> implements IRect {
-  constructor(x: number, y: number, width: number, height: number, allowNegativeDimensions: boolean = false) {
-    super({ x, y, width, height }, allowNegativeDimensions)
-  }
-}

+ 0 - 5
web/apps/photos/thirdparty/face-api/classes/index.ts

@@ -1,5 +0,0 @@
-export * from './BoundingBox'
-export * from './Box'
-export * from './Dimensions'
-export * from './Point'
-export * from './Rect'