@quadible/web-sdk
Version:
The web sdk for Quadible's behavioral authentication service.
235 lines • 8.65 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// tslint:disable: no-bitwise
const eventemitter2_1 = require("eventemitter2");
var THREE;
(function (THREE) {
class PerspectiveCamera {
constructor(fov, aspect, near, far) { }
position;
aspect;
updateProjectionMatrix() { }
}
THREE.PerspectiveCamera = PerspectiveCamera;
class Scene {
add(obj) { }
}
THREE.Scene = Scene;
class WebGLRenderer {
constructor(options) { }
domElement;
setSize(width, height) { }
sortObjects;
render(scene, camera) { }
}
THREE.WebGLRenderer = WebGLRenderer;
class VideoTexture {
constructor(video) { }
}
THREE.VideoTexture = VideoTexture;
class Object3D {
add(obj) { }
scale;
rotation;
}
THREE.Object3D = Object3D;
class PlaneGeometry {
constructor(width, height, canvasWidth, canvasHeight) { }
verticesNeedUpdate;
getAttribute(name) { return new BufferAttribute; }
}
THREE.PlaneGeometry = PlaneGeometry;
class Mesh {
constructor(geometry, material) { }
position;
}
THREE.Mesh = Mesh;
class MeshBasicMaterial {
constructor(options) { }
}
THREE.MeshBasicMaterial = MeshBasicMaterial;
class BufferAttribute {
setZ(index, value) { }
getZ(index) { return 0; }
needsUpdate;
}
THREE.BufferAttribute = BufferAttribute;
class Color {
constructor(color) { }
r;
g;
b;
}
THREE.Color = Color;
let AdditiveBlending;
(function (AdditiveBlending) {
})(AdditiveBlending = THREE.AdditiveBlending || (THREE.AdditiveBlending = {}));
})(THREE || (THREE = {}));
const fov = 70;
const canvasWidth = 320 / 2;
const canvasHeight = 240 / 2;
const tiltSpeed = 0.1;
const tiltAmount = 0.5;
const viewportWidth = 400;
const viewportHeight = 500;
class WebcamWireframe extends eventemitter2_1.EventEmitter2 {
options;
lastFrameWithPredictions;
debug = false;
camera = new THREE.PerspectiveCamera(fov, viewportWidth / viewportHeight, 1, 5000);
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({ antialias: true });
video;
videoTexture;
mainGroup = new THREE.Object3D();
geometry = new THREE.PlaneGeometry(640, 480, canvasWidth, canvasHeight);
vidCanvas = document.createElement('canvas');
ctx = this.vidCanvas.getContext('2d');
fdCanvas = document.createElement('canvas');
fdCtx = this.fdCanvas.getContext('2d');
pixels = new Uint8ClampedArray();
isDisposed = false;
wireMaterial = new THREE.MeshBasicMaterial({
opacity: 0.1,
color: 0xffffff,
wireframe: true,
blending: THREE.AdditiveBlending,
transparent: true
});
meshMaterial;
constructor(options) {
THREE = window['THREE'];
super();
this.options = options;
this.video = this.options.videoWorker.video;
this.videoTexture = new THREE.VideoTexture(this.video);
this.meshMaterial = new THREE.MeshBasicMaterial({
opacity: 1,
map: this.options.wireframe.showOriginalVideo ? this.videoTexture : undefined,
color: '#333'
});
this.scene.add(this.camera);
this.camera.position.z = 600;
this.scene.add(this.mainGroup);
const mirror = new THREE.Mesh(this.geometry, this.meshMaterial);
this.mainGroup.add(mirror);
const wiremirror = new THREE.Mesh(this.geometry, this.wireMaterial);
this.mainGroup.add(wiremirror);
wiremirror.position.z = 5;
this.renderer.sortObjects = false;
this.renderer.setSize(viewportWidth, viewportHeight);
this.renderer.domElement.addEventListener('dblclick', this.toggleDebug.bind(this));
Object.assign(this.renderer.domElement.style, {
position: 'absolute',
top: '0px',
left: '0px',
display: 'block'
});
Object.assign(this.vidCanvas.style, { display: 'none' });
Object.assign(this.fdCanvas.style, {
position: 'absolute',
top: '0px',
left: '0px',
width: '100%',
display: 'none'
});
this.fdCanvas.width = 400;
this.fdCanvas.height = 400;
document.addEventListener('mousewheel', this.onWheel.bind(this), false);
this.options.videoWorker.on('predictions', this.predictionsHandler);
this.options.wrapper.append(this.renderer.domElement, this.vidCanvas, this.fdCanvas);
this.onResize();
this.animate();
}
dispose() {
this.isDisposed = true;
this.options.videoWorker.off('predictions', this.predictionsHandler);
}
predictionsHandler = (async ({ predictions, imageBase64 }) => {
if (predictions.length) {
this.lastFrameWithPredictions = { predictions, imageBase64 };
}
if (this.debug) {
this.drawPredictionRectangle(predictions, imageBase64);
}
}).bind(this);
toggleDebug() {
this.debug = !this.debug;
this.fdCanvas.style.display = this.debug ? 'block' : 'none';
}
getZDepths() {
this.ctx.drawImage(this.video, 0, 0, canvasWidth + 1, canvasHeight + 1);
this.pixels = this.ctx.getImageData(0, 0, canvasWidth + 1, canvasHeight + 1).data;
for (let i = 0; i < canvasWidth + 1; i++) {
for (let j = 0; j < canvasHeight + 1; j++) {
const color = new THREE.Color(this.getColor(i, j));
const brightness = this.getBrightness(color);
const gotoZ = this.options.wireframe.zDepth * brightness - this.options.wireframe.zDepth / 2;
const position = this.geometry.getAttribute('position');
position.setZ(j * (canvasWidth + 1) + i, position.getZ(j * (canvasWidth + 1) + i) + (gotoZ - position.getZ(j * (canvasWidth + 1) + i)) / 5);
position.needsUpdate = true;
}
}
this.geometry['verticesNeedUpdate'] = true;
}
async animate() {
this.getZDepths();
this.render();
requestAnimationFrame(this.animate.bind(this));
}
async drawPredictionRectangle(predictions, imageBase64) {
const image = new Image();
image.src = imageBase64;
await new Promise(resolve => image.onload = resolve);
this.fdCtx.drawImage(image, 0, 0);
for (const prediction of predictions) {
const start = prediction.topLeft;
const end = prediction.bottomRight;
const probability = prediction.probability;
const size = [end[0] - start[0], end[1] - start[1]];
this.fdCtx.beginPath();
this.fdCtx.strokeStyle = 'green';
this.fdCtx.lineWidth = 4;
this.fdCtx.rect(start[0], start[1], size[0], size[1]);
this.fdCtx.stroke();
const prob = (probability[0] * 100).toPrecision(5).toString();
const text = prob + '%';
this.fdCtx.fillStyle = 'red';
this.fdCtx.font = '13pt sans-serif';
this.fdCtx.fillText(text, start[0] + 5, start[1] + 20);
}
}
render() {
if (!this.isDisposed) {
this.mainGroup.scale.setScalar(this.options.wireframe.zoom);
this.mainGroup.rotation.x += (0 * tiltAmount - this.mainGroup.rotation.x) * tiltSpeed;
this.mainGroup.rotation.y += (0 * tiltAmount - this.mainGroup.rotation.y) * tiltSpeed;
this.renderer.render(this.scene, this.camera);
}
}
onResize() {
this.renderer.setSize(viewportWidth, viewportHeight);
this.camera.aspect = viewportWidth / viewportHeight;
this.camera.updateProjectionMatrix();
}
getColor(x, y) {
const base = (Math.floor(y) * (canvasWidth + 1) + Math.floor(x)) * 4;
const c = {
r: this.pixels[base + 0],
g: this.pixels[base + 1],
b: this.pixels[base + 2],
a: this.pixels[base + 3]
};
return (c.r << 16) + (c.g << 8) + c.b;
}
getBrightness(c) {
return 0.34 * c.r + 0.5 * c.g + 0.16 * c.b;
}
onWheel(event) {
this.options.wireframe.zoom += event.wheelDelta * 0.002;
this.options.wireframe.zoom = Math.max(this.options.wireframe.zoom, 0.1);
this.options.wireframe.zoom = Math.min(this.options.wireframe.zoom, 5);
}
}
exports.default = WebcamWireframe;
//# sourceMappingURL=WebcamWireframe.js.map