Unverified Commit 9db970d6 by justadudewhohacks Committed by GitHub

Merge pull request #13 from justadudewhohacks/unit-tests

some unit and end to end tests
parents be40997b 01e37211
export declare class Rect { export interface IRect {
x: number;
y: number;
width: number;
height: number;
}
export declare class Rect implements IRect {
x: number; x: number;
y: number; y: number;
width: number; width: number;
......
{"version":3,"file":"Rect.js","sourceRoot":"","sources":["../src/Rect.ts"],"names":[],"mappings":"AAAA;IAME,cAAY,CAAS,EAAE,CAAS,EAAE,KAAa,EAAE,MAAc;QAC7D,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACV,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACV,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IAEM,oBAAK,GAAZ;QACE,OAAO,IAAI,IAAI,CACb,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAClB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAClB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EACtB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CACxB,CAAA;IACH,CAAC;IACH,WAAC;AAAD,CAAC,AArBD,IAqBC"} {"version":3,"file":"Rect.js","sourceRoot":"","sources":["../src/Rect.ts"],"names":[],"mappings":"AAOA;IAME,cAAY,CAAS,EAAE,CAAS,EAAE,KAAa,EAAE,MAAc;QAC7D,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACV,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACV,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IAEM,oBAAK,GAAZ;QACE,OAAO,IAAI,IAAI,CACb,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAClB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAClB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EACtB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CACxB,CAAA;IACH,CAAC;IACH,WAAC;AAAD,CAAC,AArBD,IAqBC"}
\ No newline at end of file \ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -6,5 +6,5 @@ export declare function faceDetectionNet(weights: Float32Array): { ...@@ -6,5 +6,5 @@ export declare function faceDetectionNet(weights: Float32Array): {
boxes: tf.Tensor<tf.Rank.R2>[]; boxes: tf.Tensor<tf.Rank.R2>[];
scores: tf.Tensor<tf.Rank.R1>[]; scores: tf.Tensor<tf.Rank.R1>[];
}; };
locateFaces: (input: tf.Tensor<tf.Rank> | NetInput, minConfidence?: number, maxResults?: number) => Promise<FaceDetection[]>; locateFaces: (input: string | HTMLCanvasElement | HTMLImageElement | HTMLVideoElement | (string | HTMLCanvasElement | HTMLImageElement | HTMLVideoElement)[] | tf.Tensor<tf.Rank> | NetInput, minConfidence?: number, maxResults?: number) => Promise<FaceDetection[]>;
}; };
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/faceDetectionNet/index.ts"],"names":[],"mappings":"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"} {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/faceDetectionNet/index.ts"],"names":[],"mappings":"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"}
\ No newline at end of file \ No newline at end of file
import * as tf from '@tensorflow/tfjs-core'; import * as tf from '@tensorflow/tfjs-core';
import { isEven } from './utils';
/** /**
* Pads the smaller dimension of an image tensor with zeros, such that width === height. * Pads the smaller dimension of an image tensor with zeros, such that width === height.
* *
...@@ -13,12 +14,17 @@ export function padToSquare(imgTensor, isCenterImage) { ...@@ -13,12 +14,17 @@ export function padToSquare(imgTensor, isCenterImage) {
if (height === width) { if (height === width) {
return imgTensor; return imgTensor;
} }
var paddingAmount = Math.floor(Math.abs(height - width) * (isCenterImage ? 0.5 : 1)); var dimDiff = Math.abs(height - width);
var paddingAmount = Math.floor(dimDiff * (isCenterImage ? 0.5 : 1));
var paddingAxis = height > width ? 2 : 1; var paddingAxis = height > width ? 2 : 1;
var paddingTensorShape = imgTensor.shape.slice(); var getPaddingTensorShape = function (isRoundUp) {
paddingTensorShape[paddingAxis] = paddingAmount; if (isRoundUp === void 0) { isRoundUp = false; }
var tensorsToStack = (isCenterImage ? [tf.fill(paddingTensorShape, 0)] : []) var paddingTensorShape = imgTensor.shape.slice();
.concat([imgTensor, tf.fill(paddingTensorShape, 0)]); paddingTensorShape[paddingAxis] = paddingAmount + (isRoundUp ? 1 : 0);
return paddingTensorShape;
};
var tensorsToStack = (isCenterImage ? [tf.fill(getPaddingTensorShape(!isEven(dimDiff)), 0)] : [])
.concat([imgTensor, tf.fill(getPaddingTensorShape(), 0)]);
return tf.concat(tensorsToStack, paddingAxis); return tf.concat(tensorsToStack, paddingAxis);
}); });
} }
......
{"version":3,"file":"padToSquare.js","sourceRoot":"","sources":["../src/padToSquare.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,sBACJ,SAAsB,EACtB,aAA8B;IAA9B,8BAAA,EAAA,qBAA8B;IAE9B,OAAO,EAAE,CAAC,IAAI,CAAC;QAEP,IAAA,6BAA0C,EAAzC,cAAM,EAAE,aAAK,CAA4B;QAChD,IAAI,MAAM,KAAK,KAAK,EAAE;YACpB,OAAO,SAAS,CAAA;SACjB;QAED,IAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACtF,IAAM,WAAW,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1C,IAAM,kBAAkB,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,EAAsC,CAAA;QACtF,kBAAkB,CAAC,WAAW,CAAC,GAAG,aAAa,CAAA;QAE/C,IAAM,cAAc,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3E,MAAM,CAAC,CAAC,SAAS,EAAG,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,CAAkB,CAAA;QACxE,OAAO,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;AACJ,CAAC"} {"version":3,"file":"padToSquare.js","sourceRoot":"","sources":["../src/padToSquare.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC;;;;;;GAMG;AACH,MAAM,sBACJ,SAAsB,EACtB,aAA8B;IAA9B,8BAAA,EAAA,qBAA8B;IAE9B,OAAO,EAAE,CAAC,IAAI,CAAC;QAEP,IAAA,6BAA0C,EAAzC,cAAM,EAAE,aAAK,CAA4B;QAChD,IAAI,MAAM,KAAK,KAAK,EAAE;YACpB,OAAO,SAAS,CAAA;SACjB;QAED,IAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,CAAA;QACxC,IAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACrE,IAAM,WAAW,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAE1C,IAAM,qBAAqB,GAAG,UAAC,SAA0B;YAA1B,0BAAA,EAAA,iBAA0B;YACvD,IAAM,kBAAkB,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;YAClD,kBAAkB,CAAC,WAAW,CAAC,GAAG,aAAa,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACrE,OAAO,kBAAkB,CAAA;QAC3B,CAAC,CAAA;QAED,IAAM,cAAc,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;aAChG,MAAM,CAAC,CAAC,SAAS,EAAG,EAAE,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAkB,CAAA;QAC7E,OAAO,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;AACJ,CAAC"}
\ No newline at end of file \ No newline at end of file
...@@ -3,6 +3,7 @@ import { FaceDetection } from './faceDetectionNet/FaceDetection'; ...@@ -3,6 +3,7 @@ import { FaceDetection } from './faceDetectionNet/FaceDetection';
import { FaceLandmarks } from './faceLandmarkNet/FaceLandmarks'; import { FaceLandmarks } from './faceLandmarkNet/FaceLandmarks';
import { Dimensions, DrawBoxOptions, DrawLandmarksOptions, DrawOptions, DrawTextOptions } from './types'; import { Dimensions, DrawBoxOptions, DrawLandmarksOptions, DrawOptions, DrawTextOptions } from './types';
export declare function isFloat(num: number): boolean; export declare function isFloat(num: number): boolean;
export declare function isEven(num: number): boolean;
export declare function round(num: number): number; export declare function round(num: number): number;
export declare function getElement(arg: string | any): any; export declare function getElement(arg: string | any): any;
export declare function getContext2dOrThrow(canvas: HTMLCanvasElement): CanvasRenderingContext2D; export declare function getContext2dOrThrow(canvas: HTMLCanvasElement): CanvasRenderingContext2D;
......
...@@ -3,6 +3,9 @@ import * as tf from '@tensorflow/tfjs-core'; ...@@ -3,6 +3,9 @@ import * as tf from '@tensorflow/tfjs-core';
export function isFloat(num) { export function isFloat(num) {
return num % 1 !== 0; return num % 1 !== 0;
} }
export function isEven(num) {
return num % 2 === 0;
}
export function round(num) { export function round(num) {
return Math.floor(num * 100) / 100; return Math.floor(num * 100) / 100;
} }
......
const dataFiles = [
'test/images/*.jpg',
'test/images/*.png',
'test/data/*.json',
'weights/*.weights'
].map(pattern => ({
pattern,
watched: false,
included: false,
served: true,
nocache: false
}))
module.exports = function(config) {
config.set({
frameworks: ['jasmine', 'karma-typescript'],
files: [
'src/**/*.ts',
'test/**/*.ts'
].concat(dataFiles),
preprocessors: {
'**/*.ts': ['karma-typescript']
},
karmaTypescriptConfig: {
tsconfig: 'tsconfig.test.json'
},
browsers: ['Chrome']
})
}
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
"scripts": { "scripts": {
"rollup-min": "rollup -c rollup.config.js --environment minify:true", "rollup-min": "rollup -c rollup.config.js --environment minify:true",
"rollup": "rollup -c rollup.config.js", "rollup": "rollup -c rollup.config.js",
"build": "npm run rollup && npm run rollup-min && tsc" "build": "npm run rollup && npm run rollup-min && tsc",
"test": "karma start"
}, },
"keywords": [ "keywords": [
"face", "face",
...@@ -22,7 +23,15 @@ ...@@ -22,7 +23,15 @@
"@tensorflow/tfjs-core": "^0.11.0" "@tensorflow/tfjs-core": "^0.11.0"
}, },
"devDependencies": { "devDependencies": {
"@types/axios": "^0.14.0",
"@types/jasmine": "^2.8.8",
"@types/node": "^10.1.1", "@types/node": "^10.1.1",
"axios": "^0.18.0",
"jasmine-core": "^3.1.0",
"karma": "^2.0.3",
"karma-chrome-launcher": "^2.2.0",
"karma-jasmine": "^1.1.2",
"karma-typescript": "^3.0.12",
"rollup": "^0.59.1", "rollup": "^0.59.1",
"rollup-plugin-commonjs": "^9.1.3", "rollup-plugin-commonjs": "^9.1.3",
"rollup-plugin-node-resolve": "^3.3.0", "rollup-plugin-node-resolve": "^3.3.0",
......
export class Rect { export interface IRect {
x: number
y: number
width: number
height: number
}
export class Rect implements IRect {
public x: number public x: number
public y: number public y: number
public width: number public width: number
......
...@@ -38,7 +38,7 @@ export function faceDetectionNet(weights: Float32Array) { ...@@ -38,7 +38,7 @@ export function faceDetectionNet(weights: Float32Array) {
} }
async function locateFaces( async function locateFaces(
input: tf.Tensor | NetInput, input: tf.Tensor | NetInput | TNetInput,
minConfidence: number = 0.8, minConfidence: number = 0.8,
maxResults: number = 100, maxResults: number = 100,
): Promise<FaceDetection[]> { ): Promise<FaceDetection[]> {
......
import * as tf from '@tensorflow/tfjs-core'; import * as tf from '@tensorflow/tfjs-core';
import { isEven } from './utils';
/** /**
* Pads the smaller dimension of an image tensor with zeros, such that width === height. * Pads the smaller dimension of an image tensor with zeros, such that width === height.
* *
...@@ -18,13 +20,18 @@ export function padToSquare( ...@@ -18,13 +20,18 @@ export function padToSquare(
return imgTensor return imgTensor
} }
const paddingAmount = Math.floor(Math.abs(height - width) * (isCenterImage ? 0.5 : 1)) const dimDiff = Math.abs(height - width)
const paddingAmount = Math.floor(dimDiff * (isCenterImage ? 0.5 : 1))
const paddingAxis = height > width ? 2 : 1 const paddingAxis = height > width ? 2 : 1
const paddingTensorShape = imgTensor.shape.slice() as [number, number, number, number]
paddingTensorShape[paddingAxis] = paddingAmount
const tensorsToStack = (isCenterImage ? [tf.fill(paddingTensorShape, 0)] : []) const getPaddingTensorShape = (isRoundUp: boolean = false): number[] => {
.concat([imgTensor, tf.fill(paddingTensorShape, 0)]) as tf.Tensor4D[] const paddingTensorShape = imgTensor.shape.slice()
paddingTensorShape[paddingAxis] = paddingAmount + (isRoundUp ? 1 : 0)
return paddingTensorShape
}
const tensorsToStack = (isCenterImage ? [tf.fill(getPaddingTensorShape(!isEven(dimDiff)), 0)] : [])
.concat([imgTensor, tf.fill(getPaddingTensorShape(), 0)]) as tf.Tensor4D[]
return tf.concat(tensorsToStack, paddingAxis) return tf.concat(tensorsToStack, paddingAxis)
}) })
} }
\ No newline at end of file
...@@ -9,6 +9,10 @@ export function isFloat(num: number) { ...@@ -9,6 +9,10 @@ export function isFloat(num: number) {
return num % 1 !== 0 return num % 1 !== 0
} }
export function isEven(num: number) {
return num % 2 === 0
}
export function round(num: number) { export function round(num: number) {
return Math.floor(num * 100) / 100 return Math.floor(num * 100) / 100
} }
......
[-0.04457738250494003, -0.043990395963191986, -0.025750599801540375, 0.016651155427098274, -0.05218124762177467, -0.05193494260311127, 0.0099308080971241, -0.016631772741675377, 0.1175108402967453, 0.04090115427970886, 0.22149613499641418, -0.08035797625780106, -0.23618969321250916, -0.02510468289256096, -0.02730529010295868, 0.10455037653446198, -0.12426768243312836, -0.0886617973446846, -0.14048148691654205, -0.040384188294410706, -0.041014235466718674, 0.11965534090995789, -0.015900742262601852, 0.08545806258916855, -0.10701242089271545, -0.2919914722442627, -0.09832733124494553, -0.15806683897972107, 0.007252881303429604, -0.09897305816411972, 0.02866348624229431, 0.17405937612056732, -0.17985646426677704, -0.11151747405529022, 0.1291065365076065, 0.023578189313411713, -0.04872015863656998, -0.023027952760457993, 0.24042242765426636, 0.09909330308437347, -0.14796072244644165, -0.034880466759204865, 0.023121334612369537, 0.35197964310646057, 0.13432590663433075, 0.025992823764681816, 0.027310356497764587, -0.07258806377649307, 0.09854947030544281, -0.2654016613960266, 0.09129738807678223, 0.18322113156318665, 0.14081577956676483, 0.08943498134613037, 0.07813337445259094, -0.1866082400083542, 0.012191163375973701, 0.13849540054798126, -0.1988903284072876, 0.14307452738285065, 0.04158562421798706, -0.0641125738620758, -0.0019705239683389664, -0.10619331151247025, 0.1386488378047943, 0.019395820796489716, -0.10675815492868423, -0.06835387647151947, 0.19760596752166748, -0.10146244615316391, -0.005424058996140957, 0.13664256036281586, -0.10927096754312515, -0.2449037879705429, -0.2788461446762085, 0.025820128619670868, 0.40477481484413147, 0.08949816972017288, -0.2150505781173706, -0.00494978204369545, -0.05536277964711189, -0.06972620636224747, -0.008085237815976143, 0.027283620089292526, -0.0860084593296051, 0.017455315217375755, -0.07179906964302063, 0.04873117804527283, 0.20279690623283386, 0.011654914356768131, -0.007634453475475311, 0.1842338889837265, -0.06324955075979233, -0.07316181063652039, 0.122040756046772, 0.08807040750980377, -0.0505395382642746, -0.08226525038480759, -0.11426772177219391, 0.000004230067133903503, 0.02770165540277958, -0.2017858773469925, -0.02197061851620674, 0.056306831538677216, -0.17306528985500336, 0.20115001499652863, 0.008511766791343689, -0.07041959464550018, -0.08531103283166885, -0.043383363634347916, -0.07298384606838226, 0.07277849316596985, 0.25687724351882935, -0.24525471031665802, 0.21955141425132751, 0.07719043642282486, 0.011913364753127098, 0.17791582643985748, -0.057796183973550797, 0.10519653558731079, -0.15350784361362457, -0.09388457238674164, -0.1587708741426468, -0.14040742814540863, 0.035962484776973724, 0.040929798036813736, 0.04888196289539337, 0.0146100465208292]
\ No newline at end of file
[{"x": 15.145603939890862, "y": 42.00849384069443}, {"x": 16.228551417589188, "y": 58.02471041679382}, {"x": 18.641504645347595, "y": 73.78255426883698}, {"x": 21.99183627963066, "y": 89.28244113922119}, {"x": 27.241109311580658, "y": 103.89149487018585}, {"x": 35.77959090471268, "y": 117.20520257949829}, {"x": 47.162988781929016, "y": 128.52083444595337}, {"x": 60.60639023780823, "y": 137.3870998620987}, {"x": 76.22466087341309, "y": 139.8476779460907}, {"x": 91.47888422012329, "y": 136.8298441171646}, {"x": 104.65206205844879, "y": 127.64202654361725}, {"x": 115.92219471931458, "y": 116.16075038909912}, {"x": 124.51052069664001, "y": 102.82219648361206}, {"x": 129.76784706115723, "y": 87.81199157238007}, {"x": 132.6519101858139, "y": 72.15077430009842}, {"x": 134.51203107833862, "y": 56.30408674478531}, {"x": 135.33487915992737, "y": 40.10292738676071}, {"x": 24.932710826396942, "y": 32.96273946762085}, {"x": 32.57601857185364, "y": 28.00302878022194}, {"x": 42.381374537944794, "y": 27.10871994495392}, {"x": 52.198585867881775, "y": 29.124750941991806}, {"x": 61.5922674536705, "y": 32.90563523769379}, {"x": 85.18133461475372, "y": 31.272751092910767}, {"x": 94.89730596542358, "y": 26.85772553086281}, {"x": 105.1171749830246, "y": 24.783611297607422}, {"x": 115.2367115020752, "y": 25.725752860307693}, {"x": 123.13878536224365, "y": 30.079219490289688}, {"x": 73.67254793643951, "y": 43.12345236539841}, {"x": 73.7981915473938, "y": 55.55519610643387}, {"x": 73.8839328289032, "y": 67.69467741250992}, {"x": 73.96439015865326, "y": 79.91722226142883}, {"x": 62.394945323467255, "y": 85.0008487701416}, {"x": 68.01678389310837, "y": 87.24332749843597}, {"x": 74.27898198366165, "y": 88.8382226228714}, {"x": 80.42970299720764, "y": 86.84954345226288}, {"x": 85.87614297866821, "y": 84.31319296360016}, {"x": 35.7684463262558, "y": 44.57894414663315}, {"x": 42.30532944202423, "y": 41.117119789123535}, {"x": 50.501713156700134, "y": 41.50397926568985}, {"x": 56.56731426715851, "y": 45.83812057971954}, {"x": 50.084468722343445, "y": 48.037388920784}, {"x": 41.91972613334656, "y": 47.92611300945282}, {"x": 91.28680229187012, "y": 44.677162170410156}, {"x": 97.76994287967682, "y": 40.084308385849}, {"x": 105.72623312473297, "y": 39.8311972618103}, {"x": 111.95003092288971, "y": 42.97384321689606}, {"x": 106.4166247844696, "y": 46.30742222070694}, {"x": 98.37331473827362, "y": 46.70642763376236}, {"x": 51.94934159517288, "y": 102.49188244342804}, {"x": 60.71081757545471, "y": 101.18674635887146}, {"x": 68.67328137159348, "y": 100.15718936920166}, {"x": 74.38371777534485, "y": 101.07522904872894}, {"x": 80.5251270532608, "y": 99.71560835838318}, {"x": 88.56176733970642, "y": 100.32560527324677}, {"x": 96.980881690979, "y": 100.81177353858948}, {"x": 89.01146650314331, "y": 108.55080485343933}, {"x": 81.3762366771698, "y": 112.2200220823288}, {"x": 74.73004907369614, "y": 113.34742605686188}, {"x": 68.42194050550461, "y": 112.92429864406586}, {"x": 60.53387671709061, "y": 110.02114713191986}, {"x": 55.876149237155914, "y": 103.3714234828949}, {"x": 68.54872852563858, "y": 104.03825640678406}, {"x": 74.44304376840591, "y": 104.46503162384033}, {"x": 80.63703775405884, "y": 103.56011688709259}, {"x": 93.1486040353775, "y": 102.0165503025055}, {"x": 80.84633946418762, "y": 104.64600920677185}, {"x": 74.63692724704742, "y": 105.62551617622375}, {"x": 68.74832957983017, "y": 105.22661805152893}]
\ No newline at end of file
import axios from 'axios';
import * as faceapi from '../../src';
import { FaceDetection } from '../../src/faceDetectionNet/FaceDetection';
import { IRect } from '../../src/Rect';
function expectFaceDetectionEquals(result: FaceDetection, score: number, expectedBox: IRect) {
const { x, y, width, height } = result.getBox()
expect(result.getScore()).toBeCloseTo(score, 2)
expect(Math.floor(x)).toEqual(expectedBox.x)
expect(Math.floor(y)).toEqual(expectedBox.y)
expect(Math.floor(width)).toEqual(expectedBox.width)
expect(Math.floor(height)).toEqual(expectedBox.height)
}
describe('faceDetectionNet', () => {
let faceDetectionNet: any, imgEl: HTMLImageElement
beforeAll(async () => {
const res = await axios.get('base/weights/face_detection_model.weights', { responseType: 'arraybuffer' })
const weights = new Float32Array(res.data)
faceDetectionNet = faceapi.faceDetectionNet(weights)
const img = await axios.get('base/test/images/faces.jpg', { responseType: 'blob' })
imgEl = await faceapi.bufferToImage(img.data)
})
it('scores > 0.9', async () => {
const { width, height } = imgEl
const result = await faceDetectionNet.locateFaces(imgEl) as FaceDetection[]
expect(result.length).toEqual(3)
result.forEach(res => {
expect(res.getImageWidth()).toEqual(width)
expect(res.getImageHeight()).toEqual(height)
})
const [d0, d1, d2] = result
expectFaceDetectionEquals(d0, 0.98, { x: 48, y: 253, width: 104, height: 129 })
expectFaceDetectionEquals(d1, 0.89, { x: 260, y: 227, width: 76, height: 117 })
expectFaceDetectionEquals(d2, 0.82, { x: 466, y: 165, width: 88, height: 130 })
})
it('scores > 0.5', async () => {
const { width, height } = imgEl
const result = await faceDetectionNet.locateFaces(imgEl, 0.5) as FaceDetection[]
expect(result.length).toEqual(6)
result.forEach(res => {
expect(res.getImageWidth()).toEqual(width)
expect(res.getImageHeight()).toEqual(height)
})
const [d0, d1, d2, d3, d4, d5] = result
expectFaceDetectionEquals(d0, 0.98, { x: 48, y: 253, width: 104, height: 129 })
expectFaceDetectionEquals(d1, 0.89, { x: 260, y: 227, width: 76, height: 117 })
expectFaceDetectionEquals(d2, 0.82, { x: 466, y: 165, width: 88, height: 130 })
expectFaceDetectionEquals(d3, 0.75, { x: 234, y: 36, width: 84, height: 119 })
expectFaceDetectionEquals(d4, 0.58, { x: 577, y: 65, width: 84, height: 105 })
expectFaceDetectionEquals(d5, 0.55, { x: 84, y: 14, width: 79, height: 132 })
})
})
\ No newline at end of file
import axios from 'axios';
import * as faceapi from '../../src';
import { FaceLandmarks } from '../../src/faceLandmarkNet/FaceLandmarks';
import { Point } from '../../src/Point';
describe('faceLandmarkNet', () => {
let faceLandmarkNet: any, imgEl: HTMLImageElement, faceLandmarkPositions: Point[]
beforeAll(async () => {
const res = await axios.get('base/weights/face_landmark_68_model.weights', { responseType: 'arraybuffer' })
const weights = new Float32Array(res.data)
faceLandmarkNet = faceapi.faceLandmarkNet(weights)
const img = await axios.get('base/test/images/face.png', { responseType: 'blob' })
imgEl = await faceapi.bufferToImage(img.data)
faceLandmarkPositions = (await axios.get('base/test/data/faceLandmarkPositions.json')).data
})
it('computes face descriptor', async () => {
const { width, height } = imgEl
const result = await faceLandmarkNet.detectLandmarks(imgEl) as FaceLandmarks
expect(result.getImageWidth()).toEqual(width)
expect(result.getImageHeight()).toEqual(height)
expect(result.getShift().x).toEqual(0)
expect(result.getShift().y).toEqual(0)
expect(result.getPositions().map(({ x, y }) => ({ x, y }))).toEqual(faceLandmarkPositions)
})
})
import axios from 'axios';
import * as faceapi from '../../src';
describe('faceRecognitionNet', () => {
let faceRecognitionNet: any, imgEl: HTMLImageElement, faceDescriptor: number[]
beforeAll(async () => {
const res = await axios.get('base/weights/face_recognition_model.weights', { responseType: 'arraybuffer' })
const weights = new Float32Array(res.data)
faceRecognitionNet = faceapi.faceRecognitionNet(weights)
const img = await axios.get('base/test/images/face.png', { responseType: 'blob' })
imgEl = await faceapi.bufferToImage(img.data)
faceDescriptor = (await axios.get('base/test/data/faceDescriptor.json')).data
})
it('computes face descriptor', async () => {
const result = await faceRecognitionNet.computeFaceDescriptor(imgEl) as number[]
expect(result.length).toEqual(128)
expect(result).toEqual(new Float32Array(faceDescriptor))
})
})
\ No newline at end of file
import * as tf from '@tensorflow/tfjs-core';
import { padToSquare } from '../src/padToSquare';
import { ones, zeros } from './utils';
describe('padToSquare', () => {
describe('even size', () => {
it('is padded to square', () => tf.tidy(() => {
const imgTensor = tf.tensor4d(Array(24).fill(1), [1, 4, 2, 3])
const result = padToSquare(imgTensor)
expect(result.shape).toEqual([1, 4, 4, 3])
const paddedCols = tf.unstack(result, 2)
expect(paddedCols.length).toEqual(4)
expect(paddedCols[0].dataSync()).toEqual(ones(12))
expect(paddedCols[1].dataSync()).toEqual(ones(12))
expect(paddedCols[2].dataSync()).toEqual(zeros(12))
expect(paddedCols[3].dataSync()).toEqual(zeros(12))
}))
it('is padded to square and centered', () => tf.tidy(() => {
const imgTensor = tf.tensor4d(Array(24).fill(1), [1, 4, 2, 3])
const result = padToSquare(imgTensor, true)
expect(result.shape).toEqual([1, 4, 4, 3])
const paddedCols = tf.unstack(result, 2)
expect(paddedCols.length).toEqual(4)
expect(paddedCols[0].dataSync()).toEqual(zeros(12))
expect(paddedCols[1].dataSync()).toEqual(ones(12))
expect(paddedCols[2].dataSync()).toEqual(ones(12))
expect(paddedCols[3].dataSync()).toEqual(zeros(12))
}))
})
describe('uneven size', () => {
it('is padded to square', () => tf.tidy(() => {
const imgTensor = tf.tensor4d(Array(30).fill(1), [1, 5, 2, 3])
const result = padToSquare(imgTensor)
expect(result.shape).toEqual([1, 5, 5, 3])
const paddedCols = tf.unstack(result, 2)
expect(paddedCols.length).toEqual(5)
expect(paddedCols[0].dataSync()).toEqual(ones(15))
expect(paddedCols[1].dataSync()).toEqual(ones(15))
expect(paddedCols[2].dataSync()).toEqual(zeros(15))
expect(paddedCols[3].dataSync()).toEqual(zeros(15))
expect(paddedCols[4].dataSync()).toEqual(zeros(15))
}))
it('is padded to square and centered', () => tf.tidy(() => {
const imgTensor = tf.tensor4d(Array(30).fill(1), [1, 5, 2, 3])
const result = padToSquare(imgTensor, true)
expect(result.shape).toEqual([1, 5, 5, 3])
const data = result.dataSync()
const paddedCols = tf.unstack(result, 2)
expect(paddedCols.length).toEqual(5)
expect(paddedCols[0].dataSync()).toEqual(zeros(15))
expect(paddedCols[1].dataSync()).toEqual(zeros(15))
expect(paddedCols[2].dataSync()).toEqual(ones(15))
expect(paddedCols[3].dataSync()).toEqual(ones(15))
expect(paddedCols[4].dataSync()).toEqual(zeros(15))
}))
})
})
export function zeros(length: number): Float32Array {
return new Float32Array(length)
}
export function ones(length: number): Float32Array {
return new Float32Array(length).fill(1)
}
\ No newline at end of file
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs"
},
"include": [
"src",
"test"
]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment