From 8de79a92b25a4a278a95395c48f87750c8f72b19 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 12 Mar 2021 16:43:36 -0500 Subject: [PATCH] guard against invalid input images --- CHANGELOG.md | 5 +++-- TODO.md | 13 ------------- demo/browser.js | 4 +--- package.json | 12 ++++++------ src/draw.ts | 21 +++++++++++---------- src/image.ts | 9 +++++++++ 6 files changed, 30 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60d09c25..099c0932 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # @vladmandic/human -Version: **1.1.0** +Version: **1.1.1** Description: **Human: AI-powered 3D Face Detection, Face Embedding & Recognition, Body Pose Tracking, Hand & Finger Tracking, Iris Analysis, Age & Gender & Emotion Prediction & Gesture Recognition** Author: **Vladimir Mandic ** @@ -9,8 +9,9 @@ Repository: **** ## Changelog -### **HEAD -> main** 2021/03/11 mandic00@live.com +### **1.1.1** 2021/03/12 mandic00@live.com +- switched face embedding to mobileface ### **1.0.4** 2021/03/11 mandic00@live.com diff --git a/TODO.md b/TODO.md index efd94380..c3a5817b 100644 --- a/TODO.md +++ b/TODO.md @@ -2,19 +2,6 @@ - Strong typing - Automated testing -- Guard against corrupt input -- Improve face embedding -- Dynamic sample processing - Explore EfficientPose - -## WiP: Embedding - -- Implement offsetRaw - -full with and without rotation -full with and without embedding -full with any without mesh -embedding with and without mesh -boxRaw and meshRaw with and without mesh diff --git a/demo/browser.js b/demo/browser.js index bf2e7469..f12eb53e 100644 --- a/demo/browser.js +++ b/demo/browser.js @@ -3,9 +3,8 @@ import Human from '../src/human'; import Menu from './menu.js'; import GLBench from './gl-bench.js'; -const userConfig = { backend: 'webgl' }; // add any user configuration overrides +// const userConfig = { backend: 'webgl' }; // add any user configuration overrides -/* const userConfig = { backend: 'wasm', async: false, @@ -16,7 +15,6 @@ const userConfig = { gesture: { enabled: false }, body: { enabled: false, modelPath: '../models/blazepose.json' }, }; -*/ const human = new Human(userConfig); diff --git a/package.json b/package.json index 87a1bd1a..d7874ef2 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,11 @@ "url": "git+https://github.com/vladmandic/human.git" }, "scripts": { - "start": "node --trace-warnings --unhandled-rejections=strict --trace-uncaught --no-deprecation src/node.js", - "dev": "npm install && node server/serve.js", - "build": "rimraf dist/* && rimraf types/* && node server/build.js && node server/changelog.js", - "lint": "eslint src demo server", - "test": "eslint src demo server" + "start": "node --trace-warnings --unhandled-rejections=strict --trace-uncaught --no-deprecation demo/node.js", + "dev": "node --trace-warnings --unhandled-rejections=strict --trace-uncaught server/serve.js", + "build": "rimraf dist/* && rimraf types/* && node --trace-warnings --unhandled-rejections=strict --trace-uncaught server/build.js && node server/changelog.js", + "lint": "eslint src server demo", + "test": "npm run lint && npm run start" }, "keywords": [ "tensorflowjs", @@ -60,7 +60,7 @@ "@vladmandic/pilogger": "^0.2.14", "chokidar": "^3.5.1", "dayjs": "^1.10.4", - "esbuild": "=0.9.0", + "esbuild": "^0.9.2", "eslint": "^7.21.0", "eslint-config-airbnb-base": "^14.2.1", "eslint-plugin-import": "^2.22.1", diff --git a/src/draw.ts b/src/draw.ts index e7729692..7bb8923e 100644 --- a/src/draw.ts +++ b/src/draw.ts @@ -20,8 +20,8 @@ export const options = { bufferedOutput: false, }; -function point(ctx, x, y) { - ctx.fillStyle = options.color; +function point(ctx, x, y, z = null) { + ctx.fillStyle = options.useDepth && z ? `rgba(${127.5 + (2 * (z || 0))}, ${127.5 - (2 * (z || 0))}, 255, 0.3)` : options.color; ctx.beginPath(); ctx.arc(x, y, options.pointSize, 0, 2 * Math.PI); ctx.fill(); @@ -53,7 +53,11 @@ function lines(ctx, points: number[] = []) { if (points === undefined || points.length === 0) return; ctx.beginPath(); ctx.moveTo(points[0][0], points[0][1]); - for (const pt of points) ctx.lineTo(pt[0], parseInt(pt[1])); + for (const pt of points) { + ctx.strokeStyle = options.useDepth && pt[2] ? `rgba(${127.5 + (2 * pt[2])}, ${127.5 - (2 * pt[2])}, 255, 0.3)` : options.color; + ctx.fillStyle = options.useDepth && pt[2] ? `rgba(${127.5 + (2 * pt[2])}, ${127.5 - (2 * pt[2])}, 255, 0.3)` : options.color; + ctx.lineTo(pt[0], parseInt(pt[1])); + } ctx.stroke(); if (options.fillPolygons) { ctx.closePath(); @@ -118,6 +122,7 @@ export async function face(inCanvas, result) { ctx.fillStyle = options.color; if (options.drawBoxes) { rect(ctx, f.box[0], f.box[1], f.box[2], f.box[3]); + // rect(ctx, inCanvas.width * f.boxRaw[0], inCanvas.height * f.boxRaw[1], inCanvas.width * f.boxRaw[2], inCanvas.height * f.boxRaw[3]); } // silly hack since fillText does not suport new line const labels:string[] = []; @@ -146,21 +151,17 @@ export async function face(inCanvas, result) { ctx.lineWidth = 1; if (f.mesh) { if (options.drawPoints) { - for (const pt of f.mesh) { - ctx.fillStyle = options.useDepth ? `rgba(${127.5 + (2 * pt[2])}, ${127.5 - (2 * pt[2])}, 255, 0.5)` : options.color; - point(ctx, pt[0], pt[1]); - } + for (const pt of f.mesh) point(ctx, pt[0], pt[1], pt[2]); + // for (const pt of f.meshRaw) point(ctx, pt[0] * inCanvas.offsetWidth, pt[1] * inCanvas.offsetHeight, pt[2]); } if (options.drawPolygons) { + ctx.lineWidth = 1; 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) => f.mesh[index]); - ctx.strokeStyle = options.useDepth ? `rgba(${127.5 + (2 * points[0][2])}, ${127.5 - (2 * points[0][2])}, 255, 0.3)` : options.color; - ctx.fillStyle = options.useDepth ? `rgba(${127.5 + (2 * points[0][2])}, ${127.5 - (2 * points[0][2])}, 255, 0.3)` : options.color; - ctx.lineWidth = 1; lines(ctx, points); } // iris: array[center, left, top, right, bottom] diff --git a/src/image.ts b/src/image.ts index e726eac3..8b5f8084 100644 --- a/src/image.ts +++ b/src/image.ts @@ -4,6 +4,7 @@ import { log } from './log'; import * as tf from '../dist/tfjs.esm.js'; import * as fxImage from './imagefx'; +const maxSize = 2048; // internal temp canvases let inCanvas = null; let outCanvas = null; @@ -22,6 +23,14 @@ export function process(input, config): { tensor, canvas } { const originalHeight = input.naturalHeight || input.videoHeight || input.height || (input.shape && (input.shape[2] > 0)); let targetWidth = originalWidth; let targetHeight = originalHeight; + if (targetWidth > maxSize) { + targetWidth = maxSize; + targetHeight = targetWidth * originalHeight / originalWidth; + } + if (targetHeight > maxSize) { + targetHeight = maxSize; + targetWidth = targetHeight * originalWidth / originalHeight; + } if (config.filter.width > 0) targetWidth = config.filter.width; else if (config.filter.height > 0) targetWidth = originalWidth * (config.filter.height / originalHeight); if (config.filter.height > 0) targetHeight = config.filter.height;