@face-auth/face-id-video
Version:
Utility library for capturing photos from webcam video streams in the browser. Handles camera selection, image formatting, and output for face authentication APIs.
101 lines (100 loc) • 4.49 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { DEFAULT_IMAGE_TYPE } from "./common/constants";
export class FacePhotoManager {
constructor(video) {
this.video = video;
}
/**
* Captura el frame actual, recorta un óvalo +20% de buffer, fondo negro, y devuelve un Blob.
*/
capture(options) {
return __awaiter(this, void 0, void 0, function* () {
const imageType = options.imageType || DEFAULT_IMAGE_TYPE;
const faceOvalDimensions = options.ovalDimensions || null;
const bufferPercent = options.bufferPercent || 0.2; // 20%
// Si no hay dimensiones de óvalo, devolvemos la captura completa:
if (!faceOvalDimensions) {
return this.captureFullFrame(imageType);
}
const { x = 0, y = 0, radiusX = 0, radiusY = 0, scale = 1 } = faceOvalDimensions;
// Radios escalados + buffer
const rX = radiusX * scale;
const rY = radiusY * scale;
const rXb = rX * (1 + bufferPercent);
const rYb = rY * (1 + bufferPercent);
// Bounding box del óvalo ampliado
const boxWidth = 2 * rXb;
const boxHeight = 2 * rYb;
// Canvas temporal
const canvas = document.createElement("canvas");
canvas.width = boxWidth;
canvas.height = boxHeight;
const ctx = canvas.getContext("2d");
if (!ctx)
throw new Error("No se pudo crear el contexto del canvas.");
// Fondo negro
ctx.fillStyle = "black";
ctx.fillRect(0, 0, boxWidth, boxHeight);
// Defino clip oval
ctx.beginPath();
ctx.ellipse(boxWidth / 2, // centro x en el nuevo canvas
boxHeight / 2, // centro y
rXb, // radio X con buffer
rYb, // radio Y con buffer
0, // rotación
0, // ángulo inicio
Math.PI * 2 // ángulo fin
);
ctx.clip();
// Dibujo la porción del video original, desplazando para centrar el box
ctx.drawImage(this.video, x - rXb, // origen X en el video
y - rYb, // origen Y
boxWidth, // ancho a recortar del video
boxHeight, // alto a recortar
0, // destino X en el canvas
0, // destino Y
boxWidth, // ancho en el canvas
boxHeight // alto en el canvas
);
// Devuelvo el blob
return new Promise((resolve, reject) => {
canvas.toBlob(blob => {
if (blob) {
resolve({ blob, imageType });
}
else {
reject(new Error("No se pudo crear el blob desde el canvas."));
}
}, imageType);
});
});
}
/** Helper para devolver captura completa sin recorte */
captureFullFrame(imageType) {
return __awaiter(this, void 0, void 0, function* () {
const canvas = document.createElement("canvas");
canvas.width = this.video.videoWidth;
canvas.height = this.video.videoHeight;
const ctx = canvas.getContext("2d");
if (!ctx)
throw new Error("No se pudo crear el contexto del canvas.");
ctx.drawImage(this.video, 0, 0, canvas.width, canvas.height);
return new Promise((resolve, reject) => {
canvas.toBlob(blob => {
if (blob)
resolve({ blob, imageType });
else
reject(new Error("No se pudo crear el blob del frame completo."));
}, imageType);
});
});
}
}