2020-11-04 16:18:22 +01:00
|
|
|
async function drawGesture(result, canvas, ui) {
|
|
|
|
if (!result) return;
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
ctx.font = ui.baseFont;
|
|
|
|
ctx.fillStyle = ui.baseLabel;
|
|
|
|
let i = 1;
|
2020-11-26 16:37:04 +01:00
|
|
|
for (let gesture = 0; gesture < result.length; gesture++) {
|
2020-11-24 05:36:04 +01:00
|
|
|
const [where, what] = Object.entries(result[gesture]);
|
|
|
|
if ((what.length > 1) && (what[1].length > 0)) {
|
|
|
|
const person = where[1] > 0 ? `#${where[1]}` : '';
|
|
|
|
const label = `${where[0]} ${person}: ${what[1]}`;
|
2020-11-19 20:45:59 +01:00
|
|
|
ctx.fillStyle = 'black';
|
|
|
|
ctx.fillText(label, 8, 2 + (i * ui.baseLineHeight));
|
|
|
|
ctx.fillStyle = ui.baseLabel;
|
|
|
|
ctx.fillText(label, 6, 0 + (i * ui.baseLineHeight));
|
2020-11-04 16:18:22 +01:00
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-17 13:34:45 +02:00
|
|
|
async function drawFace(result, canvas, ui, triangulation) {
|
|
|
|
if (!result) return;
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
for (const face of result) {
|
|
|
|
ctx.font = ui.baseFont;
|
|
|
|
ctx.strokeStyle = ui.baseColor;
|
|
|
|
ctx.fillStyle = ui.baseColor;
|
|
|
|
ctx.lineWidth = ui.baseLineWidth;
|
|
|
|
ctx.beginPath();
|
|
|
|
if (ui.drawBoxes) {
|
|
|
|
ctx.rect(face.box[0], face.box[1], face.box[2], face.box[3]);
|
|
|
|
}
|
|
|
|
// silly hack since fillText does not suport new line
|
|
|
|
const labels = [];
|
2020-11-06 19:50:16 +01:00
|
|
|
// labels.push(`${Math.trunc(100 * face.confidence)}% face`);
|
2021-01-11 15:02:02 +01:00
|
|
|
if (face.genderConfidence) labels.push(`${face.gender || ''} ${Math.trunc(100 * face.genderConfidence)}% confident`);
|
2020-11-19 20:45:59 +01:00
|
|
|
// if (face.genderConfidence) labels.push(face.gender);
|
2020-10-18 20:14:05 +02:00
|
|
|
if (face.age) labels.push(`age: ${face.age || ''}`);
|
2021-01-11 15:02:02 +01:00
|
|
|
if (face.iris) labels.push(`iris distance: ${face.iris}`);
|
2020-11-05 21:38:09 +01:00
|
|
|
if (face.emotion && face.emotion.length > 0) {
|
|
|
|
const emotion = face.emotion.map((a) => `${Math.trunc(100 * a.score)}% ${a.emotion}`);
|
|
|
|
labels.push(emotion.join(' '));
|
|
|
|
}
|
2020-10-17 13:34:45 +02:00
|
|
|
ctx.fillStyle = ui.baseLabel;
|
2020-11-19 20:45:59 +01:00
|
|
|
for (let i = 0; i < labels.length; i++) {
|
|
|
|
ctx.fillStyle = 'black';
|
2021-01-11 15:02:02 +01:00
|
|
|
ctx.fillText(labels[i], face.box[0] + 1, face.box[1] - ((labels.length - i) * ui.baseLineHeight) + 6);
|
2020-11-19 20:45:59 +01:00
|
|
|
ctx.fillStyle = ui.baseLabel;
|
2021-01-11 15:02:02 +01:00
|
|
|
ctx.fillText(labels[i], face.box[0] + 0, face.box[1] - ((labels.length - i) * ui.baseLineHeight) + 5);
|
2020-11-19 20:45:59 +01:00
|
|
|
}
|
|
|
|
ctx.fillStyle = ui.baseColor;
|
2020-10-17 13:34:45 +02:00
|
|
|
ctx.stroke();
|
|
|
|
ctx.lineWidth = 1;
|
|
|
|
if (face.mesh) {
|
|
|
|
if (ui.drawPoints) {
|
|
|
|
for (const point of face.mesh) {
|
|
|
|
ctx.fillStyle = ui.useDepth ? `rgba(${127.5 + (2 * point[2])}, ${127.5 - (2 * point[2])}, 255, 0.5)` : ui.baseColor;
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(point[0], point[1], 2, 0, 2 * Math.PI);
|
|
|
|
ctx.fill();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ui.drawPolygons) {
|
|
|
|
for (let i = 0; i < triangulation.length / 3; i++) {
|
|
|
|
const points = [
|
|
|
|
triangulation[i * 3 + 0],
|
|
|
|
triangulation[i * 3 + 1],
|
|
|
|
triangulation[i * 3 + 2],
|
|
|
|
].map((index) => face.mesh[index]);
|
|
|
|
const path = new Path2D();
|
|
|
|
path.moveTo(points[0][0], points[0][1]);
|
|
|
|
for (const point of points) {
|
|
|
|
path.lineTo(point[0], point[1]);
|
|
|
|
}
|
|
|
|
path.closePath();
|
|
|
|
ctx.strokeStyle = ui.useDepth ? `rgba(${127.5 + (2 * points[0][2])}, ${127.5 - (2 * points[0][2])}, 255, 0.3)` : ui.baseColor;
|
|
|
|
ctx.stroke(path);
|
|
|
|
if (ui.fillPolygons) {
|
|
|
|
ctx.fillStyle = ui.useDepth ? `rgba(${127.5 + (2 * points[0][2])}, ${127.5 - (2 * points[0][2])}, 255, 0.3)` : ui.baseColor;
|
|
|
|
ctx.fill(path);
|
|
|
|
}
|
|
|
|
}
|
2020-11-09 14:57:24 +01:00
|
|
|
// iris: array[center, left, top, right, bottom]
|
|
|
|
if (face.annotations && face.annotations.leftEyeIris) {
|
|
|
|
ctx.strokeStyle = ui.useDepth ? 'rgba(255, 200, 255, 0.3)' : ui.baseColor;
|
|
|
|
ctx.beginPath();
|
|
|
|
const sizeX = Math.abs(face.annotations.leftEyeIris[3][0] - face.annotations.leftEyeIris[1][0]) / 2;
|
|
|
|
const sizeY = Math.abs(face.annotations.leftEyeIris[4][1] - face.annotations.leftEyeIris[2][1]) / 2;
|
|
|
|
ctx.ellipse(face.annotations.leftEyeIris[0][0], face.annotations.leftEyeIris[0][1], sizeX, sizeY, 0, 0, 2 * Math.PI);
|
|
|
|
ctx.stroke();
|
|
|
|
if (ui.fillPolygons) {
|
|
|
|
ctx.fillStyle = ui.useDepth ? 'rgba(255, 255, 200, 0.3)' : ui.baseColor;
|
|
|
|
ctx.fill();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (face.annotations && face.annotations.rightEyeIris) {
|
|
|
|
ctx.strokeStyle = ui.useDepth ? 'rgba(255, 200, 255, 0.3)' : ui.baseColor;
|
|
|
|
ctx.beginPath();
|
|
|
|
const sizeX = Math.abs(face.annotations.rightEyeIris[3][0] - face.annotations.rightEyeIris[1][0]) / 2;
|
|
|
|
const sizeY = Math.abs(face.annotations.rightEyeIris[4][1] - face.annotations.rightEyeIris[2][1]) / 2;
|
|
|
|
ctx.ellipse(face.annotations.rightEyeIris[0][0], face.annotations.rightEyeIris[0][1], sizeX, sizeY, 0, 0, 2 * Math.PI);
|
|
|
|
ctx.stroke();
|
|
|
|
if (ui.fillPolygons) {
|
|
|
|
ctx.fillStyle = ui.useDepth ? 'rgba(255, 255, 200, 0.3)' : ui.baseColor;
|
|
|
|
ctx.fill();
|
|
|
|
}
|
|
|
|
}
|
2020-10-17 13:34:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-12 15:21:26 +01:00
|
|
|
const lastDrawnPose = [];
|
2020-10-17 13:34:45 +02:00
|
|
|
async function drawBody(result, canvas, ui) {
|
|
|
|
if (!result) return;
|
|
|
|
const ctx = canvas.getContext('2d');
|
2020-10-30 12:32:35 +01:00
|
|
|
ctx.lineJoin = 'round';
|
2020-11-26 16:37:04 +01:00
|
|
|
for (let i = 0; i < result.length; i++) {
|
2020-11-12 15:21:26 +01:00
|
|
|
if (!lastDrawnPose[i] && ui.buffered) lastDrawnPose[i] = { ...result[i] };
|
2020-10-17 13:34:45 +02:00
|
|
|
ctx.fillStyle = ui.baseColor;
|
|
|
|
ctx.strokeStyle = ui.baseColor;
|
|
|
|
ctx.font = ui.baseFont;
|
|
|
|
ctx.lineWidth = ui.baseLineWidth;
|
|
|
|
if (ui.drawPoints) {
|
2020-11-26 16:37:04 +01:00
|
|
|
for (let pt = 0; pt < result[i].keypoints.length; pt++) {
|
2020-10-17 13:34:45 +02:00
|
|
|
ctx.beginPath();
|
2020-11-12 15:21:26 +01:00
|
|
|
if (ui.buffered) {
|
|
|
|
lastDrawnPose[i].keypoints[pt].position.x = (lastDrawnPose[i].keypoints[pt].position.x + result[i].keypoints[pt].position.x) / 2;
|
|
|
|
lastDrawnPose[i].keypoints[pt].position.y = (lastDrawnPose[i].keypoints[pt].position.y + result[i].keypoints[pt].position.y) / 2;
|
|
|
|
ctx.arc(lastDrawnPose[i].keypoints[pt].position.x, lastDrawnPose[i].keypoints[pt].position.y, 2, 0, 2 * Math.PI);
|
|
|
|
} else {
|
|
|
|
ctx.arc(result[i].keypoints[pt].position.x, result[i].keypoints[pt].position.y, 2, 0, 2 * Math.PI);
|
|
|
|
}
|
2020-10-17 13:34:45 +02:00
|
|
|
ctx.fill();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ui.drawPolygons) {
|
|
|
|
const path = new Path2D();
|
2020-12-17 00:36:24 +01:00
|
|
|
let root;
|
2020-10-17 13:34:45 +02:00
|
|
|
let part;
|
|
|
|
// torso
|
2020-12-17 00:36:24 +01:00
|
|
|
root = result[i].keypoints.find((a) => a.part === 'leftShoulder');
|
|
|
|
if (root) {
|
|
|
|
path.moveTo(root.position.x, root.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'rightShoulder');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'rightHip');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'leftHip');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'leftShoulder');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
}
|
|
|
|
// leg left
|
|
|
|
root = result[i].keypoints.find((a) => a.part === 'leftHip');
|
|
|
|
if (root) {
|
|
|
|
path.moveTo(root.position.x, root.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'leftKnee');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'leftAnkle');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
}
|
|
|
|
// leg right
|
|
|
|
root = result[i].keypoints.find((a) => a.part === 'rightHip');
|
|
|
|
if (root) {
|
|
|
|
path.moveTo(root.position.x, root.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'rightKnee');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'rightAnkle');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
}
|
|
|
|
// arm left
|
|
|
|
root = result[i].keypoints.find((a) => a.part === 'leftShoulder');
|
|
|
|
if (root) {
|
|
|
|
path.moveTo(root.position.x, root.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'leftElbow');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'leftWrist');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
}
|
|
|
|
// arm right
|
|
|
|
root = result[i].keypoints.find((a) => a.part === 'rightShoulder');
|
|
|
|
if (root) {
|
|
|
|
path.moveTo(root.position.x, root.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'rightElbow');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
part = result[i].keypoints.find((a) => a.part === 'rightWrist');
|
|
|
|
if (part) path.lineTo(part.position.x, part.position.y);
|
|
|
|
}
|
2020-10-17 13:34:45 +02:00
|
|
|
// draw all
|
|
|
|
ctx.stroke(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function drawHand(result, canvas, ui) {
|
|
|
|
if (!result) return;
|
|
|
|
const ctx = canvas.getContext('2d');
|
2020-10-30 12:32:35 +01:00
|
|
|
ctx.lineJoin = 'round';
|
2020-10-17 13:34:45 +02:00
|
|
|
for (const hand of result) {
|
|
|
|
ctx.font = ui.baseFont;
|
|
|
|
ctx.lineWidth = ui.baseLineWidth;
|
|
|
|
if (ui.drawBoxes) {
|
|
|
|
ctx.lineWidth = ui.baseLineWidth;
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.strokeStyle = ui.baseColor;
|
|
|
|
ctx.fillStyle = ui.baseColor;
|
|
|
|
ctx.rect(hand.box[0], hand.box[1], hand.box[2], hand.box[3]);
|
2020-11-19 20:45:59 +01:00
|
|
|
ctx.fillStyle = 'black';
|
|
|
|
ctx.fillText('hand', hand.box[0] + 3, 1 + hand.box[1] + ui.baseLineHeight, hand.box[2]);
|
2020-10-17 13:34:45 +02:00
|
|
|
ctx.fillStyle = ui.baseLabel;
|
2020-11-19 20:45:59 +01:00
|
|
|
ctx.fillText('hand', hand.box[0] + 2, 0 + hand.box[1] + ui.baseLineHeight, hand.box[2]);
|
2020-10-17 13:34:45 +02:00
|
|
|
ctx.stroke();
|
|
|
|
}
|
|
|
|
if (ui.drawPoints) {
|
2020-11-04 20:59:30 +01:00
|
|
|
if (hand.landmarks && hand.landmarks.length > 0) {
|
|
|
|
for (const point of hand.landmarks) {
|
|
|
|
ctx.fillStyle = ui.useDepth ? `rgba(${127.5 + (2 * point[2])}, ${127.5 - (2 * point[2])}, 255, 0.5)` : ui.baseColor;
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(point[0], point[1], 2, 0, 2 * Math.PI);
|
|
|
|
ctx.fill();
|
|
|
|
}
|
2020-10-17 13:34:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ui.drawPolygons) {
|
|
|
|
const addPart = (part) => {
|
2020-11-04 20:59:30 +01:00
|
|
|
if (!part) return;
|
2020-10-30 12:32:35 +01:00
|
|
|
for (let i = 0; i < part.length; i++) {
|
2020-10-17 13:34:45 +02:00
|
|
|
ctx.lineWidth = ui.baseLineWidth;
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.strokeStyle = ui.useDepth ? `rgba(${127.5 + (2 * part[i][2])}, ${127.5 - (2 * part[i][2])}, 255, 0.5)` : ui.baseColor;
|
2020-10-30 12:32:35 +01:00
|
|
|
ctx.moveTo(part[i > 0 ? i - 1 : 0][0], part[i > 0 ? i - 1 : 0][1]);
|
2020-10-17 13:34:45 +02:00
|
|
|
ctx.lineTo(part[i][0], part[i][1]);
|
|
|
|
ctx.stroke();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
addPart(hand.annotations.indexFinger);
|
|
|
|
addPart(hand.annotations.middleFinger);
|
|
|
|
addPart(hand.annotations.ringFinger);
|
|
|
|
addPart(hand.annotations.pinky);
|
|
|
|
addPart(hand.annotations.thumb);
|
2020-10-30 12:32:35 +01:00
|
|
|
// addPart(hand.annotations.palmBase);
|
2020-10-17 13:34:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-19 20:45:59 +01:00
|
|
|
// eslint-disable-next-line import/prefer-default-export
|
|
|
|
export default {
|
2020-10-17 13:34:45 +02:00
|
|
|
face: drawFace,
|
|
|
|
body: drawBody,
|
|
|
|
hand: drawHand,
|
2020-11-04 16:18:22 +01:00
|
|
|
gesture: drawGesture,
|
2020-10-17 13:34:45 +02:00
|
|
|
};
|