Skip to content

Commit 17b72bd

Browse files
remove FaceDetectionWithLandmarks and FullFaceDescription and instead produce dynamically extensible objects
1 parent 387a03b commit 17b72bd

32 files changed

+501
-189
lines changed

src/classes/FaceDetection.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,8 @@ export class FaceDetection extends ObjectDetection implements IFaceDetecion {
1313
) {
1414
super(score, score, '', relativeBox, imageDims)
1515
}
16+
17+
public forSize(width: number, height: number): FaceDetection {
18+
return super.forSize(width, height)
19+
}
1620
}

src/classes/FaceDetectionWithLandmarks.ts

Lines changed: 0 additions & 47 deletions
This file was deleted.

src/classes/FullFaceDescription.ts

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/classes/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
export * from './FaceDetection';
2-
export * from './FaceDetectionWithLandmarks';
32
export * from './FaceLandmarks';
43
export * from './FaceLandmarks5';
54
export * from './FaceLandmarks68';
65
export * from './FaceMatch';
7-
export * from './FullFaceDescription';
86
export * from './LabeledFaceDescriptors';

src/factories/WithFaceDescriptor.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export type WithFaceDescriptor<TSource> = TSource & {
2+
descriptor: Float32Array
3+
}
4+
5+
export function extendWithFaceDescriptor<
6+
TSource
7+
> (
8+
sourceObj: TSource,
9+
descriptor: Float32Array
10+
): WithFaceDescriptor<TSource> {
11+
12+
const extension = { descriptor }
13+
return Object.assign({}, sourceObj, extension)
14+
}
15+

src/factories/WithFaceDetection.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { FaceDetection } from '../classes/FaceDetection';
2+
3+
export type WithFaceDetection<TSource> = TSource & {
4+
detection: FaceDetection
5+
}
6+
7+
export function extendWithFaceDetection<
8+
TSource
9+
> (
10+
sourceObj: TSource,
11+
detection: FaceDetection
12+
): WithFaceDetection<TSource> {
13+
14+
const extension = { detection }
15+
return Object.assign({}, sourceObj, extension)
16+
}

src/factories/WithFaceExpressions.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// TODO
2+
export type FaceExpressions = number[]
3+
4+
export type WithFaceExpressions<TSource> = TSource & {
5+
expressions: FaceExpressions
6+
}
7+
8+
export function extendWithFaceExpressions<
9+
TSource
10+
> (
11+
sourceObj: TSource,
12+
expressions: FaceExpressions
13+
): WithFaceExpressions<TSource> {
14+
15+
const extension = { expressions }
16+
return Object.assign({}, sourceObj, extension)
17+
}

src/factories/WithFaceLandmarks.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { FaceDetection } from '../classes/FaceDetection';
2+
import { FaceLandmarks } from '../classes/FaceLandmarks';
3+
import { FaceLandmarks68 } from '../classes/FaceLandmarks68';
4+
import { WithFaceDetection } from './WithFaceDetection';
5+
6+
export type WithFaceLandmarks<
7+
TSource extends WithFaceDetection<{}>,
8+
TFaceLandmarks extends FaceLandmarks = FaceLandmarks68
9+
> = TSource & {
10+
landmarks: TFaceLandmarks
11+
unshiftedLandmarks: TFaceLandmarks
12+
alignedRect: FaceDetection
13+
}
14+
15+
export function extendWithFaceLandmarks<
16+
TSource extends WithFaceDetection<{}>,
17+
TFaceLandmarks extends FaceLandmarks = FaceLandmarks68
18+
> (
19+
sourceObj: TSource,
20+
unshiftedLandmarks: TFaceLandmarks
21+
): WithFaceLandmarks<TSource, TFaceLandmarks> {
22+
23+
const { box: shift } = sourceObj.detection
24+
const landmarks = unshiftedLandmarks.shiftBy<TFaceLandmarks>(shift.x, shift.y)
25+
26+
const rect = landmarks.align()
27+
const { imageDims } = sourceObj.detection
28+
const alignedRect = new FaceDetection(sourceObj.detection.score, rect.rescale(imageDims.reverse()), imageDims)
29+
30+
const extension = {
31+
landmarks,
32+
unshiftedLandmarks,
33+
alignedRect
34+
}
35+
36+
return Object.assign({}, sourceObj, extension)
37+
}

src/factories/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './WithFaceDescriptor'
2+
export * from './WithFaceDetection'
3+
export * from './WithFaceExpressions'
4+
export * from './WithFaceLandmarks'
Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,65 @@
11
import * as tf from '@tensorflow/tfjs-core';
22
import { TNetInput } from 'tfjs-image-recognition-base';
33

4-
import { FaceDetectionWithLandmarks } from '../classes/FaceDetectionWithLandmarks';
5-
import { FullFaceDescription } from '../classes/FullFaceDescription';
64
import { extractFaces, extractFaceTensors } from '../dom';
5+
import { extendWithFaceDescriptor, WithFaceDescriptor } from '../factories/WithFaceDescriptor';
6+
import { WithFaceDetection } from '../factories/WithFaceDetection';
7+
import { WithFaceLandmarks } from '../factories/WithFaceLandmarks';
78
import { ComposableTask } from './ComposableTask';
89
import { nets } from './nets';
910

10-
export class ComputeFaceDescriptorsTaskBase<TReturn, DetectFaceLandmarksReturnType> extends ComposableTask<TReturn> {
11+
export class ComputeFaceDescriptorsTaskBase<TReturn, TParentReturn> extends ComposableTask<TReturn> {
1112
constructor(
12-
protected detectFaceLandmarksTask: ComposableTask<DetectFaceLandmarksReturnType> | Promise<DetectFaceLandmarksReturnType>,
13+
protected parentTask: ComposableTask<TParentReturn> | Promise<TParentReturn>,
1314
protected input: TNetInput
1415
) {
1516
super()
1617
}
1718
}
1819

19-
export class ComputeAllFaceDescriptorsTask extends ComputeFaceDescriptorsTaskBase<FullFaceDescription[], FaceDetectionWithLandmarks[]> {
20+
export class ComputeAllFaceDescriptorsTask<
21+
TSource extends WithFaceLandmarks<WithFaceDetection<{}>>
22+
> extends ComputeFaceDescriptorsTaskBase<WithFaceDescriptor<TSource>[], TSource[]> {
2023

21-
public async run(): Promise<FullFaceDescription[]> {
24+
public async run(): Promise<WithFaceDescriptor<TSource>[]> {
2225

23-
const facesWithLandmarks = await this.detectFaceLandmarksTask
26+
const parentResults = await this.parentTask
2427

25-
const alignedRects = facesWithLandmarks.map(({ alignedRect }) => alignedRect)
28+
const alignedRects = parentResults.map(({ alignedRect }) => alignedRect)
2629
const alignedFaces: Array<HTMLCanvasElement | tf.Tensor3D> = this.input instanceof tf.Tensor
2730
? await extractFaceTensors(this.input, alignedRects)
2831
: await extractFaces(this.input, alignedRects)
2932

30-
const fullFaceDescriptions = await Promise.all(facesWithLandmarks.map(async ({ detection, landmarks }, i) => {
33+
const results = await Promise.all(parentResults.map(async (parentResult, i) => {
3134
const descriptor = await nets.faceRecognitionNet.computeFaceDescriptor(alignedFaces[i]) as Float32Array
32-
return new FullFaceDescription(detection, landmarks, descriptor)
35+
return extendWithFaceDescriptor<TSource>(parentResult, descriptor)
3336
}))
3437

3538
alignedFaces.forEach(f => f instanceof tf.Tensor && f.dispose())
3639

37-
return fullFaceDescriptions
40+
return results
3841
}
3942
}
4043

41-
export class ComputeSingleFaceDescriptorTask extends ComputeFaceDescriptorsTaskBase<FullFaceDescription | undefined, FaceDetectionWithLandmarks | undefined> {
44+
export class ComputeSingleFaceDescriptorTask<
45+
TSource extends WithFaceLandmarks<WithFaceDetection<{}>>
46+
> extends ComputeFaceDescriptorsTaskBase<WithFaceDescriptor<TSource> | undefined, TSource | undefined> {
4247

43-
public async run(): Promise<FullFaceDescription | undefined> {
48+
public async run(): Promise<WithFaceDescriptor<TSource> | undefined> {
4449

45-
const detectionWithLandmarks = await this.detectFaceLandmarksTask
46-
if (!detectionWithLandmarks) {
50+
const parentResult = await this.parentTask
51+
if (!parentResult) {
4752
return
4853
}
4954

50-
const { detection, landmarks, alignedRect } = detectionWithLandmarks
55+
const { alignedRect } = parentResult
5156
const alignedFaces: Array<HTMLCanvasElement | tf.Tensor3D> = this.input instanceof tf.Tensor
5257
? await extractFaceTensors(this.input, [alignedRect])
5358
: await extractFaces(this.input, [alignedRect])
5459
const descriptor = await nets.faceRecognitionNet.computeFaceDescriptor(alignedFaces[0]) as Float32Array
5560

5661
alignedFaces.forEach(f => f instanceof tf.Tensor && f.dispose())
5762

58-
return new FullFaceDescription(detection, landmarks, descriptor)
63+
return extendWithFaceDescriptor(parentResult, descriptor)
5964
}
6065
}
Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import * as tf from '@tensorflow/tfjs-core';
22
import { TNetInput } from 'tfjs-image-recognition-base';
33

4-
import { FaceDetection } from '../classes/FaceDetection';
5-
import { FaceDetectionWithLandmarks } from '../classes/FaceDetectionWithLandmarks';
64
import { FaceLandmarks68 } from '../classes/FaceLandmarks68';
75
import { extractFaces, extractFaceTensors } from '../dom';
86
import { FaceLandmark68Net } from '../faceLandmarkNet/FaceLandmark68Net';
97
import { FaceLandmark68TinyNet } from '../faceLandmarkNet/FaceLandmark68TinyNet';
8+
import { WithFaceDetection } from '../factories/WithFaceDetection';
9+
import { extendWithFaceLandmarks, WithFaceLandmarks } from '../factories/WithFaceLandmarks';
1010
import { ComposableTask } from './ComposableTask';
1111
import { ComputeAllFaceDescriptorsTask, ComputeSingleFaceDescriptorTask } from './ComputeFaceDescriptorsTasks';
1212
import { nets } from './nets';
13+
import { PredictAllFaceExpressionsTask, PredictSingleFaceExpressionTask } from './PredictFaceExpressionsTask';
1314

14-
export class DetectFaceLandmarksTaskBase<ReturnType, DetectFacesReturnType> extends ComposableTask<ReturnType> {
15+
export class DetectFaceLandmarksTaskBase<TReturn, TParentReturn> extends ComposableTask<TReturn> {
1516
constructor(
16-
protected detectFacesTask: ComposableTask<DetectFacesReturnType> | Promise<DetectFacesReturnType>,
17+
protected parentTask: ComposableTask<TParentReturn> | Promise<TParentReturn>,
1718
protected input: TNetInput,
1819
protected useTinyLandmarkNet: boolean
1920
) {
@@ -27,11 +28,14 @@ export class DetectFaceLandmarksTaskBase<ReturnType, DetectFacesReturnType> exte
2728
}
2829
}
2930

30-
export class DetectAllFaceLandmarksTask extends DetectFaceLandmarksTaskBase<FaceDetectionWithLandmarks[], FaceDetection[]> {
31+
export class DetectAllFaceLandmarksTask<
32+
TSource extends WithFaceDetection<{}>
33+
> extends DetectFaceLandmarksTaskBase<WithFaceLandmarks<TSource>[], TSource[]> {
3134

32-
public async run(): Promise<FaceDetectionWithLandmarks[]> {
35+
public async run(): Promise<WithFaceLandmarks<TSource>[]> {
3336

34-
const detections = await this.detectFacesTask
37+
const parentResults = await this.parentTask
38+
const detections = parentResults.map(res => res.detection)
3539

3640
const faces: Array<HTMLCanvasElement | tf.Tensor3D> = this.input instanceof tf.Tensor
3741
? await extractFaceTensors(this.input, detections)
@@ -43,25 +47,32 @@ export class DetectAllFaceLandmarksTask extends DetectFaceLandmarksTaskBase<Face
4347

4448
faces.forEach(f => f instanceof tf.Tensor && f.dispose())
4549

46-
return detections.map((detection, i) =>
47-
new FaceDetectionWithLandmarks(detection, faceLandmarksByFace[i])
50+
return parentResults.map((parentResult, i) =>
51+
extendWithFaceLandmarks<TSource>(parentResult, faceLandmarksByFace[i])
4852
)
4953
}
5054

51-
withFaceDescriptors(): ComputeAllFaceDescriptorsTask {
52-
return new ComputeAllFaceDescriptorsTask(this, this.input)
55+
withFaceExpressions(): PredictAllFaceExpressionsTask<WithFaceLandmarks<TSource>> {
56+
return new PredictAllFaceExpressionsTask<WithFaceLandmarks<TSource>>(this, this.input)
57+
}
58+
59+
withFaceDescriptors(): ComputeAllFaceDescriptorsTask<WithFaceLandmarks<TSource>> {
60+
return new ComputeAllFaceDescriptorsTask<WithFaceLandmarks<TSource>>(this, this.input)
5361
}
5462
}
5563

56-
export class DetectSingleFaceLandmarksTask extends DetectFaceLandmarksTaskBase<FaceDetectionWithLandmarks | undefined, FaceDetection | undefined> {
64+
export class DetectSingleFaceLandmarksTask<
65+
TSource extends WithFaceDetection<{}>
66+
> extends DetectFaceLandmarksTaskBase<WithFaceLandmarks<TSource> | undefined, TSource | undefined> {
5767

58-
public async run(): Promise<FaceDetectionWithLandmarks | undefined> {
68+
public async run(): Promise<WithFaceLandmarks<TSource> | undefined> {
5969

60-
const detection = await this.detectFacesTask
61-
if (!detection) {
70+
const parentResult = await this.parentTask
71+
if (!parentResult) {
6272
return
6373
}
6474

75+
const { detection } = parentResult
6576
const faces: Array<HTMLCanvasElement | tf.Tensor3D> = this.input instanceof tf.Tensor
6677
? await extractFaceTensors(this.input, [detection])
6778
: await extractFaces(this.input, [detection])
@@ -71,13 +82,14 @@ export class DetectSingleFaceLandmarksTask extends DetectFaceLandmarksTaskBase<F
7182

7283
faces.forEach(f => f instanceof tf.Tensor && f.dispose())
7384

74-
return new FaceDetectionWithLandmarks(
75-
detection,
76-
landmarks
77-
)
85+
return extendWithFaceLandmarks<TSource>(parentResult, landmarks)
86+
}
87+
88+
withFaceExpression(): PredictSingleFaceExpressionTask<WithFaceLandmarks<TSource>> {
89+
return new PredictSingleFaceExpressionTask<WithFaceLandmarks<TSource>>(this, this.input)
7890
}
7991

80-
withFaceDescriptor(): ComputeSingleFaceDescriptorTask {
81-
return new ComputeSingleFaceDescriptorTask(this, this.input)
92+
withFaceDescriptor(): ComputeSingleFaceDescriptorTask<WithFaceLandmarks<TSource>> {
93+
return new ComputeSingleFaceDescriptorTask<WithFaceLandmarks<TSource>>(this, this.input)
8294
}
8395
}

0 commit comments

Comments
 (0)