mirror of https://github.com/vladmandic/human
update gaze strength calculations
parent
8a95539741
commit
748540a0cc
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -174,7 +174,7 @@ var config = {
|
|||
description: {
|
||||
enabled: true,
|
||||
modelPath: "faceres.json",
|
||||
skipFrames: 16,
|
||||
skipFrames: 11,
|
||||
minConfidence: 0.1
|
||||
},
|
||||
emotion: {
|
||||
|
@ -189,12 +189,12 @@ var config = {
|
|||
modelPath: "movenet-lightning.json",
|
||||
maxDetected: 1,
|
||||
minConfidence: 0.2,
|
||||
skipFrames: 16
|
||||
skipFrames: 1
|
||||
},
|
||||
hand: {
|
||||
enabled: true,
|
||||
rotation: true,
|
||||
skipFrames: 19,
|
||||
skipFrames: 18,
|
||||
minConfidence: 0.1,
|
||||
iouThreshold: 0.1,
|
||||
maxDetected: 2,
|
||||
|
@ -212,7 +212,7 @@ var config = {
|
|||
minConfidence: 0.2,
|
||||
iouThreshold: 0.4,
|
||||
maxDetected: 10,
|
||||
skipFrames: 20
|
||||
skipFrames: 19
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4332,20 +4332,20 @@ async function predict3(image15, config3, idx, count2) {
|
|||
}
|
||||
|
||||
// src/face.ts
|
||||
var calculateGaze = (mesh, box6) => {
|
||||
var calculateGaze = (face5) => {
|
||||
const radians = (pt1, pt2) => Math.atan2(pt1[1] - pt2[1], pt1[0] - pt2[0]);
|
||||
const offsetIris = [0, -0.1];
|
||||
const eyeRatio = 1;
|
||||
const left = mesh[33][2] > mesh[263][2];
|
||||
const irisCenter = left ? mesh[473] : mesh[468];
|
||||
const eyeCenter = left ? [(mesh[133][0] + mesh[33][0]) / 2, (mesh[133][1] + mesh[33][1]) / 2] : [(mesh[263][0] + mesh[362][0]) / 2, (mesh[263][1] + mesh[362][1]) / 2];
|
||||
const eyeSize = left ? [mesh[133][0] - mesh[33][0], mesh[23][1] - mesh[27][1]] : [mesh[263][0] - mesh[362][0], mesh[253][1] - mesh[257][1]];
|
||||
const left = face5.mesh[33][2] > face5.mesh[263][2];
|
||||
const irisCenter = left ? face5.mesh[473] : face5.mesh[468];
|
||||
const eyeCenter = left ? [(face5.mesh[133][0] + face5.mesh[33][0]) / 2, (face5.mesh[133][1] + face5.mesh[33][1]) / 2] : [(face5.mesh[263][0] + face5.mesh[362][0]) / 2, (face5.mesh[263][1] + face5.mesh[362][1]) / 2];
|
||||
const eyeSize = left ? [face5.mesh[133][0] - face5.mesh[33][0], face5.mesh[23][1] - face5.mesh[27][1]] : [face5.mesh[263][0] - face5.mesh[362][0], face5.mesh[253][1] - face5.mesh[257][1]];
|
||||
const eyeDiff = [
|
||||
(eyeCenter[0] - irisCenter[0]) / eyeSize[0] - offsetIris[0],
|
||||
eyeRatio * (irisCenter[1] - eyeCenter[1]) / eyeSize[1] - offsetIris[1]
|
||||
];
|
||||
let strength = Math.sqrt(eyeDiff[0] ** 2 + eyeDiff[1] ** 2);
|
||||
strength = Math.min(strength, box6[2] / 2, box6[3] / 2);
|
||||
strength = Math.min(strength, face5.boxRaw[2] / 2, face5.boxRaw[3] / 2);
|
||||
const bearing = (radians([0, 0], eyeDiff) + Math.PI / 2) % Math.PI;
|
||||
return { bearing, strength };
|
||||
};
|
||||
|
@ -4425,7 +4425,7 @@ var calculateFaceAngle = (face5, imageSize) => {
|
|||
z_axis[2]
|
||||
];
|
||||
const angle = rotationMatrixToEulerAngle(matrix);
|
||||
const gaze = mesh.length === 478 ? calculateGaze(mesh, face5.box) : { bearing: 0, strength: 0 };
|
||||
const gaze = mesh.length === 478 ? calculateGaze(face5) : { bearing: 0, strength: 0 };
|
||||
return { angle, matrix, gaze };
|
||||
};
|
||||
var detectFace = async (parent, input) => {
|
||||
|
|
|
@ -175,7 +175,7 @@ var config = {
|
|||
description: {
|
||||
enabled: true,
|
||||
modelPath: "faceres.json",
|
||||
skipFrames: 16,
|
||||
skipFrames: 11,
|
||||
minConfidence: 0.1
|
||||
},
|
||||
emotion: {
|
||||
|
@ -190,12 +190,12 @@ var config = {
|
|||
modelPath: "movenet-lightning.json",
|
||||
maxDetected: 1,
|
||||
minConfidence: 0.2,
|
||||
skipFrames: 16
|
||||
skipFrames: 1
|
||||
},
|
||||
hand: {
|
||||
enabled: true,
|
||||
rotation: true,
|
||||
skipFrames: 19,
|
||||
skipFrames: 18,
|
||||
minConfidence: 0.1,
|
||||
iouThreshold: 0.1,
|
||||
maxDetected: 2,
|
||||
|
@ -213,7 +213,7 @@ var config = {
|
|||
minConfidence: 0.2,
|
||||
iouThreshold: 0.4,
|
||||
maxDetected: 10,
|
||||
skipFrames: 20
|
||||
skipFrames: 19
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4333,20 +4333,20 @@ async function predict3(image15, config3, idx, count2) {
|
|||
}
|
||||
|
||||
// src/face.ts
|
||||
var calculateGaze = (mesh, box6) => {
|
||||
var calculateGaze = (face5) => {
|
||||
const radians = (pt1, pt2) => Math.atan2(pt1[1] - pt2[1], pt1[0] - pt2[0]);
|
||||
const offsetIris = [0, -0.1];
|
||||
const eyeRatio = 1;
|
||||
const left = mesh[33][2] > mesh[263][2];
|
||||
const irisCenter = left ? mesh[473] : mesh[468];
|
||||
const eyeCenter = left ? [(mesh[133][0] + mesh[33][0]) / 2, (mesh[133][1] + mesh[33][1]) / 2] : [(mesh[263][0] + mesh[362][0]) / 2, (mesh[263][1] + mesh[362][1]) / 2];
|
||||
const eyeSize = left ? [mesh[133][0] - mesh[33][0], mesh[23][1] - mesh[27][1]] : [mesh[263][0] - mesh[362][0], mesh[253][1] - mesh[257][1]];
|
||||
const left = face5.mesh[33][2] > face5.mesh[263][2];
|
||||
const irisCenter = left ? face5.mesh[473] : face5.mesh[468];
|
||||
const eyeCenter = left ? [(face5.mesh[133][0] + face5.mesh[33][0]) / 2, (face5.mesh[133][1] + face5.mesh[33][1]) / 2] : [(face5.mesh[263][0] + face5.mesh[362][0]) / 2, (face5.mesh[263][1] + face5.mesh[362][1]) / 2];
|
||||
const eyeSize = left ? [face5.mesh[133][0] - face5.mesh[33][0], face5.mesh[23][1] - face5.mesh[27][1]] : [face5.mesh[263][0] - face5.mesh[362][0], face5.mesh[253][1] - face5.mesh[257][1]];
|
||||
const eyeDiff = [
|
||||
(eyeCenter[0] - irisCenter[0]) / eyeSize[0] - offsetIris[0],
|
||||
eyeRatio * (irisCenter[1] - eyeCenter[1]) / eyeSize[1] - offsetIris[1]
|
||||
];
|
||||
let strength = Math.sqrt(eyeDiff[0] ** 2 + eyeDiff[1] ** 2);
|
||||
strength = Math.min(strength, box6[2] / 2, box6[3] / 2);
|
||||
strength = Math.min(strength, face5.boxRaw[2] / 2, face5.boxRaw[3] / 2);
|
||||
const bearing = (radians([0, 0], eyeDiff) + Math.PI / 2) % Math.PI;
|
||||
return { bearing, strength };
|
||||
};
|
||||
|
@ -4426,7 +4426,7 @@ var calculateFaceAngle = (face5, imageSize) => {
|
|||
z_axis[2]
|
||||
];
|
||||
const angle = rotationMatrixToEulerAngle(matrix);
|
||||
const gaze = mesh.length === 478 ? calculateGaze(mesh, face5.box) : { bearing: 0, strength: 0 };
|
||||
const gaze = mesh.length === 478 ? calculateGaze(face5) : { bearing: 0, strength: 0 };
|
||||
return { angle, matrix, gaze };
|
||||
};
|
||||
var detectFace = async (parent, input) => {
|
||||
|
|
|
@ -174,7 +174,7 @@ var config = {
|
|||
description: {
|
||||
enabled: true,
|
||||
modelPath: "faceres.json",
|
||||
skipFrames: 16,
|
||||
skipFrames: 11,
|
||||
minConfidence: 0.1
|
||||
},
|
||||
emotion: {
|
||||
|
@ -189,12 +189,12 @@ var config = {
|
|||
modelPath: "movenet-lightning.json",
|
||||
maxDetected: 1,
|
||||
minConfidence: 0.2,
|
||||
skipFrames: 16
|
||||
skipFrames: 1
|
||||
},
|
||||
hand: {
|
||||
enabled: true,
|
||||
rotation: true,
|
||||
skipFrames: 19,
|
||||
skipFrames: 18,
|
||||
minConfidence: 0.1,
|
||||
iouThreshold: 0.1,
|
||||
maxDetected: 2,
|
||||
|
@ -212,7 +212,7 @@ var config = {
|
|||
minConfidence: 0.2,
|
||||
iouThreshold: 0.4,
|
||||
maxDetected: 10,
|
||||
skipFrames: 20
|
||||
skipFrames: 19
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4332,20 +4332,20 @@ async function predict3(image15, config3, idx, count2) {
|
|||
}
|
||||
|
||||
// src/face.ts
|
||||
var calculateGaze = (mesh, box6) => {
|
||||
var calculateGaze = (face5) => {
|
||||
const radians = (pt1, pt2) => Math.atan2(pt1[1] - pt2[1], pt1[0] - pt2[0]);
|
||||
const offsetIris = [0, -0.1];
|
||||
const eyeRatio = 1;
|
||||
const left = mesh[33][2] > mesh[263][2];
|
||||
const irisCenter = left ? mesh[473] : mesh[468];
|
||||
const eyeCenter = left ? [(mesh[133][0] + mesh[33][0]) / 2, (mesh[133][1] + mesh[33][1]) / 2] : [(mesh[263][0] + mesh[362][0]) / 2, (mesh[263][1] + mesh[362][1]) / 2];
|
||||
const eyeSize = left ? [mesh[133][0] - mesh[33][0], mesh[23][1] - mesh[27][1]] : [mesh[263][0] - mesh[362][0], mesh[253][1] - mesh[257][1]];
|
||||
const left = face5.mesh[33][2] > face5.mesh[263][2];
|
||||
const irisCenter = left ? face5.mesh[473] : face5.mesh[468];
|
||||
const eyeCenter = left ? [(face5.mesh[133][0] + face5.mesh[33][0]) / 2, (face5.mesh[133][1] + face5.mesh[33][1]) / 2] : [(face5.mesh[263][0] + face5.mesh[362][0]) / 2, (face5.mesh[263][1] + face5.mesh[362][1]) / 2];
|
||||
const eyeSize = left ? [face5.mesh[133][0] - face5.mesh[33][0], face5.mesh[23][1] - face5.mesh[27][1]] : [face5.mesh[263][0] - face5.mesh[362][0], face5.mesh[253][1] - face5.mesh[257][1]];
|
||||
const eyeDiff = [
|
||||
(eyeCenter[0] - irisCenter[0]) / eyeSize[0] - offsetIris[0],
|
||||
eyeRatio * (irisCenter[1] - eyeCenter[1]) / eyeSize[1] - offsetIris[1]
|
||||
];
|
||||
let strength = Math.sqrt(eyeDiff[0] ** 2 + eyeDiff[1] ** 2);
|
||||
strength = Math.min(strength, box6[2] / 2, box6[3] / 2);
|
||||
strength = Math.min(strength, face5.boxRaw[2] / 2, face5.boxRaw[3] / 2);
|
||||
const bearing = (radians([0, 0], eyeDiff) + Math.PI / 2) % Math.PI;
|
||||
return { bearing, strength };
|
||||
};
|
||||
|
@ -4425,7 +4425,7 @@ var calculateFaceAngle = (face5, imageSize) => {
|
|||
z_axis[2]
|
||||
];
|
||||
const angle = rotationMatrixToEulerAngle(matrix);
|
||||
const gaze = mesh.length === 478 ? calculateGaze(mesh, face5.box) : { bearing: 0, strength: 0 };
|
||||
const gaze = mesh.length === 478 ? calculateGaze(face5) : { bearing: 0, strength: 0 };
|
||||
return { angle, matrix, gaze };
|
||||
};
|
||||
var detectFace = async (parent, input) => {
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -280,7 +280,7 @@ const config: Config = {
|
|||
// recommended to enable detector.rotation and mesh.enabled
|
||||
modelPath: 'faceres.json', // face description model
|
||||
// can be either absolute path or relative to modelBasePath
|
||||
skipFrames: 16, // how many max frames to go without re-running the detector
|
||||
skipFrames: 11, // how many max frames to go without re-running the detector
|
||||
// only used when cacheSensitivity is not zero
|
||||
minConfidence: 0.1, // threshold for discarding a prediction
|
||||
},
|
||||
|
@ -302,7 +302,7 @@ const config: Config = {
|
|||
// should be set to the minimum number for performance
|
||||
// only valid for posenet as other models detects single pose
|
||||
minConfidence: 0.2, // threshold for discarding a prediction
|
||||
skipFrames: 16, // how many max frames to go without re-running the detector
|
||||
skipFrames: 1, // how many max frames to go without re-running the detector
|
||||
// only used when cacheSensitivity is not zero
|
||||
},
|
||||
|
||||
|
@ -310,7 +310,7 @@ const config: Config = {
|
|||
enabled: true,
|
||||
rotation: true, // use best-guess rotated hand image or just box with rotation as-is
|
||||
// false means higher performance, but incorrect finger mapping if hand is inverted
|
||||
skipFrames: 19, // how many max frames to go without re-running the hand bounding box detector
|
||||
skipFrames: 18, // how many max frames to go without re-running the hand bounding box detector
|
||||
// only used when cacheSensitivity is not zero
|
||||
// e.g., if model is running st 25 FPS, we can re-use existing bounding
|
||||
// box for updated hand skeleton analysis as the hand probably
|
||||
|
@ -335,7 +335,7 @@ const config: Config = {
|
|||
minConfidence: 0.2, // threshold for discarding a prediction
|
||||
iouThreshold: 0.4, // ammount of overlap between two detected objects before one object is removed
|
||||
maxDetected: 10, // maximum number of objects detected in the input
|
||||
skipFrames: 20, // how many max frames to go without re-running the detector
|
||||
skipFrames: 19, // how many max frames to go without re-running the detector
|
||||
// only used when cacheSensitivity is not zero
|
||||
},
|
||||
};
|
||||
|
|
18
src/face.ts
18
src/face.ts
|
@ -14,27 +14,27 @@ import { Tensor } from './tfjs/types';
|
|||
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
const rad2deg = (theta) => Math.round((theta * 180) / Math.PI);
|
||||
|
||||
const calculateGaze = (mesh, box): { bearing: number, strength: number } => {
|
||||
const calculateGaze = (face): { bearing: number, strength: number } => {
|
||||
const radians = (pt1, pt2) => Math.atan2(pt1[1] - pt2[1], pt1[0] - pt2[0]); // function to calculate angle between any two points
|
||||
|
||||
const offsetIris = [0, -0.1]; // iris center may not align with average of eye extremes
|
||||
const eyeRatio = 1; // factor to normalize changes x vs y
|
||||
|
||||
const left = mesh[33][2] > mesh[263][2]; // pick left or right eye depending which one is closer bazed on outsize point z axis
|
||||
const irisCenter = left ? mesh[473] : mesh[468];
|
||||
const left = face.mesh[33][2] > face.mesh[263][2]; // pick left or right eye depending which one is closer bazed on outsize point z axis
|
||||
const irisCenter = left ? face.mesh[473] : face.mesh[468];
|
||||
const eyeCenter = left // eye center is average of extreme points on x axis for both x and y, ignoring y extreme points as eyelids naturally open/close more when gazing up/down so relative point is less precise
|
||||
? [(mesh[133][0] + mesh[33][0]) / 2, (mesh[133][1] + mesh[33][1]) / 2]
|
||||
: [(mesh[263][0] + mesh[362][0]) / 2, (mesh[263][1] + mesh[362][1]) / 2];
|
||||
? [(face.mesh[133][0] + face.mesh[33][0]) / 2, (face.mesh[133][1] + face.mesh[33][1]) / 2]
|
||||
: [(face.mesh[263][0] + face.mesh[362][0]) / 2, (face.mesh[263][1] + face.mesh[362][1]) / 2];
|
||||
const eyeSize = left // eye size is difference between extreme points for both x and y, used to normalize & squarify eye dimensions
|
||||
? [mesh[133][0] - mesh[33][0], mesh[23][1] - mesh[27][1]]
|
||||
: [mesh[263][0] - mesh[362][0], mesh[253][1] - mesh[257][1]];
|
||||
? [face.mesh[133][0] - face.mesh[33][0], face.mesh[23][1] - face.mesh[27][1]]
|
||||
: [face.mesh[263][0] - face.mesh[362][0], face.mesh[253][1] - face.mesh[257][1]];
|
||||
|
||||
const eyeDiff = [ // x distance between extreme point and center point normalized with eye size
|
||||
(eyeCenter[0] - irisCenter[0]) / eyeSize[0] - offsetIris[0],
|
||||
eyeRatio * (irisCenter[1] - eyeCenter[1]) / eyeSize[1] - offsetIris[1],
|
||||
];
|
||||
let strength = Math.sqrt((eyeDiff[0] ** 2) + (eyeDiff[1] ** 2)); // vector length is a diagonal between two differences
|
||||
strength = Math.min(strength, box[2] / 2, box[3] / 2); // limit strength to half of box size to avoid clipping due to low precision
|
||||
strength = Math.min(strength, face.boxRaw[2] / 2, face.boxRaw[3] / 2); // limit strength to half of box size to avoid clipping due to low precision
|
||||
const bearing = (radians([0, 0], eyeDiff) + (Math.PI / 2)) % Math.PI; // using eyeDiff instead eyeCenter/irisCenter combo due to manual adjustments and rotate clockwise 90degrees
|
||||
|
||||
return { bearing, strength };
|
||||
|
@ -134,7 +134,7 @@ const calculateFaceAngle = (face, imageSize): {
|
|||
// const angle = meshToEulerAngle(mesh);
|
||||
|
||||
// we have iris keypoints so we can calculate gaze direction
|
||||
const gaze = mesh.length === 478 ? calculateGaze(mesh, face.box) : { bearing: 0, strength: 0 };
|
||||
const gaze = mesh.length === 478 ? calculateGaze(face) : { bearing: 0, strength: 0 };
|
||||
|
||||
return { angle, matrix, gaze };
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue