UNPKG

video-auth-js-sdk

Version:

A SDK to authenticate users with camera through a realtime stream

372 lines (371 loc) 19 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var faceapi = _interopRequireWildcard(require("@vladmandic/face-api")); var _errorHandler = require("./errorHandler"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } var FaceAPI = /*#__PURE__*/function () { function FaceAPI(app) { (0, _classCallCheck2["default"])(this, FaceAPI); this._app = app; this.optionsSSDMobileNet = null; this.minScore = 0.2; // minimum score this.maxResults = 5; // maximum number of results to return this.modelPath = app.params.modelPath; // path to model folder that will be loaded using http this._isDistroyed = false; } return (0, _createClass2["default"])(FaceAPI, [{ key: "detectIsFaceCorrect", value: function () { var _detectIsFaceCorrect = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(callback) { return _regenerator["default"].wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _context.next = 2; return this.main(); case 2: case "end": return _context.stop(); } }, _callee, this); })); function detectIsFaceCorrect(_x) { return _detectIsFaceCorrect.apply(this, arguments); } return detectIsFaceCorrect; }() }, { key: "drawFaces", value: function drawFaces(canvas, data, fps) { if (!data.length) { this._app.store.faceDetected = false; } var ctx = canvas.getContext('2d', { willReadFrequently: true }); if (!ctx) return; ctx.clearRect(0, 0, canvas.width, canvas.height); // draw title ctx.font = 'small-caps 20px "Segoe UI"'; ctx.fillStyle = 'white'; ctx.fillText("FPS: ".concat(fps), 10, 25); var _iterator = _createForOfIteratorHelper(data), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var person = _step.value; // draw box around each face ctx.lineWidth = 3; ctx.strokeStyle = 'deepskyblue'; ctx.fillStyle = 'deepskyblue'; ctx.globalAlpha = 0.6; ctx.beginPath(); var ellipseBoxPosCenter = { x: parseInt(canvas.width / 2), y: parseInt(canvas.height / 2), radiusX: 70, radiusY: 100 }; if (person.detection.box.x + person.detection.box.width / 2 < ellipseBoxPosCenter.x + 20 && person.detection.box.x + person.detection.box.width / 2 > ellipseBoxPosCenter.x - 20 && person.detection.box.y + person.detection.box.height / 2 > ellipseBoxPosCenter.y - 20 && person.detection.box.y + person.detection.box.height / 2 < ellipseBoxPosCenter.y + 20 && person.angle.yaw > -20 && person.angle.yaw < 20 && person.angle.pitch > -15 && person.angle.pitch < 15) { this._app.store.faceDetected = true; ctx.strokeStyle = 'green'; } else { ctx.strokeStyle = 'red'; this._app.store.faceDetected = false; } ctx.ellipse(ellipseBoxPosCenter.x, ellipseBoxPosCenter.y, ellipseBoxPosCenter.radiusX, ellipseBoxPosCenter.radiusY, Math.PI, 0, 2 * Math.PI); ctx.stroke(); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } }, { key: "detectVideo", value: function () { var _detectVideo = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(video, canvas) { var _this = this; var t0; return _regenerator["default"].wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: if (!(!video || video.paused || this._isDistroyed)) { _context2.next = 2; break; } return _context2.abrupt("return", false); case 2: t0 = performance.now(); faceapi.detectAllFaces(video, this.optionsSSDMobileNet).withFaceLandmarks().withFaceExpressions() // .withFaceDescriptors() .withAgeAndGender().then(function (result) { var fps = 1000 / (performance.now() - t0); var resizedDetections = faceapi.resizeResults(result, { width: canvas.width, height: canvas.height }); _this.drawFaces(canvas, resizedDetections, fps.toLocaleString()); setTimeout(function () { requestAnimationFrame(function () { return _this.detectVideo(video, canvas); }); }, 1000); return true; })["catch"](function (err) { console.log("Detect Error: ".concat(err)); return false; }); return _context2.abrupt("return", false); case 5: case "end": return _context2.stop(); } }, _callee2, this); })); function detectVideo(_x2, _x3) { return _detectVideo.apply(this, arguments); } return detectVideo; }() // just initialize everything and call main function }, { key: "setupCamera", value: function () { var _setupCamera = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4() { var _this2 = this; var video, canvas, constraints, track, settings; return _regenerator["default"].wrap(function _callee4$(_context4) { while (1) switch (_context4.prev = _context4.next) { case 0: video = this._app.store.videoTag; canvas = this._app.store.canvasTag; if (!(!video || !canvas)) { _context4.next = 4; break; } return _context4.abrupt("return", null); case 4: if (navigator.mediaDevices) { _context4.next = 8; break; } console.error('Camera Error: access not supported'); this._app.publicCallbacks.onError(_errorHandler.errorList.MEDIA_DEVICES_NOT_SUPPORTED); return _context4.abrupt("return", null); case 8: constraints = { audio: false, video: { facingMode: 'user', exact: "environment", resizeMode: 'crop-and-scale' } }; if (window.innerWidth > window.innerHeight) constraints.video.width = { ideal: window.innerWidth }; //width: 1920, height: 1280 }//ideal: 1280}//window.innerWidth }; else constraints.video.height = { ideal: window.innerHeight }; _context4.prev = 10; _context4.next = 13; return navigator.mediaDevices.getUserMedia(constraints); case 13: this._app.store.localCameraStream = _context4.sent; _context4.next = 21; break; case 16: _context4.prev = 16; _context4.t0 = _context4["catch"](10); if (_context4.t0.name === 'PermissionDeniedError' || _context4.t0.name === 'NotAllowedError') { console.error("Camera Error: camera permission denied: ".concat(_context4.t0.message || _context4.t0)); this._app.publicCallbacks.onError(_errorHandler.errorList.VIDEO_PERMISSION_ERROR); } if (_context4.t0.name === 'SourceUnavailableError') { console.error("Camera Error: camera not available: ".concat(_context4.t0.message || _context4.t0)); this._app.publicCallbacks.onError(this._app.errorHandler.getFilledErrorObject(_objectSpread(_objectSpread({}, _errorHandler.errorList.CAMERA_NOT_AVAILABLE), {}, { replacements: [_context4.t0.message || _context4.t0] }))); } return _context4.abrupt("return", null); case 21: if (!this._app.store.localCameraStream) { _context4.next = 25; break; } video.srcObject = this._app.store.localCameraStream; _context4.next = 27; break; case 25: console.error('Camera Error: stream empty'); return _context4.abrupt("return", null); case 27: track = this._app.store.localCameraStream.getVideoTracks()[0]; settings = track.getSettings(); if (settings.deviceId) delete settings.deviceId; if (settings.groupId) delete settings.groupId; // if (settings.aspectRatio) settings.aspectRatio = Math.trunc(100 * settings.aspectRatio) / 100; console.log("Camera active: ".concat(track.label)); console.log("Camera settings: ", { settings: settings }); canvas.addEventListener('click', function () { if (video && video.readyState >= 2) { if (video.paused) { video.play(); _this2.detectVideo(video, canvas); } else { video.pause(); } } console.log("Camera state: ".concat(video.paused ? 'paused' : 'playing')); }); return _context4.abrupt("return", new Promise(function (resolve) { video.onloadeddata = /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3() { var base, aspect; return _regenerator["default"].wrap(function _callee3$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: base = "videoWidth"; if (video.videoWidth < video.videoHeight) { base = "videoHeight"; } aspect = video.videoWidth / video.videoHeight; if (base == "videoWidth") { canvas.width = video.offsetWidth; canvas.height = parseInt(video.offsetWidth / aspect); canvas.style.left = '0px'; canvas.style.top = parseInt((video.offsetHeight - canvas.height) / 2) + 'px'; } else { canvas.height = video.offsetHeight; canvas.width = parseInt(video.offsetHeight / aspect); canvas.style.top = '0px'; canvas.style.left = parseInt((video.offsetWidth - canvas.width) / 2) + 'px'; } setTimeout(function () { video.play(); _this2.detectVideo(video, canvas); resolve(true); }); case 5: case "end": return _context3.stop(); } }, _callee3); })); })); case 35: case "end": return _context4.stop(); } }, _callee4, this, [[10, 16]]); })); function setupCamera() { return _setupCamera.apply(this, arguments); } return setupCamera; }() }, { key: "setupFaceAPI", value: function () { var _setupFaceAPI = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee5() { return _regenerator["default"].wrap(function _callee5$(_context5) { while (1) switch (_context5.prev = _context5.next) { case 0: _context5.next = 2; return faceapi.nets.ssdMobilenetv1.load(this.modelPath); case 2: _context5.next = 4; return faceapi.nets.ageGenderNet.load(this.modelPath); case 4: _context5.next = 6; return faceapi.nets.faceLandmark68Net.load(this.modelPath); case 6: _context5.next = 8; return faceapi.nets.faceRecognitionNet.load(this.modelPath); case 8: _context5.next = 10; return faceapi.nets.faceExpressionNet.load(this.modelPath); case 10: this.optionsSSDMobileNet = new faceapi.SsdMobilenetv1Options({ minConfidence: this.minScore, maxResults: this.maxResults }); case 11: case "end": return _context5.stop(); } }, _callee5, this); })); function setupFaceAPI() { return _setupFaceAPI.apply(this, arguments); } return setupFaceAPI; }() }, { key: "main", value: function () { var _main = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee6() { var _faceapi$tf, _faceapi$tf2, _faceapi$tf3; return _regenerator["default"].wrap(function _callee6$(_context6) { while (1) switch (_context6.prev = _context6.next) { case 0: _context6.next = 2; return faceapi.tf.setBackend('webgl'); case 2: _context6.next = 4; return faceapi.tf.ready(); case 4: if ((_faceapi$tf = faceapi.tf) !== null && _faceapi$tf !== void 0 && _faceapi$tf.env().flagRegistry.CANVAS2D_WILL_READ_FREQUENTLY) faceapi.tf.env().set('CANVAS2D_WILL_READ_FREQUENTLY', true); if ((_faceapi$tf2 = faceapi.tf) !== null && _faceapi$tf2 !== void 0 && _faceapi$tf2.env().flagRegistry.WEBGL_EXP_CONV) faceapi.tf.env().set('WEBGL_EXP_CONV', true); if ((_faceapi$tf3 = faceapi.tf) !== null && _faceapi$tf3 !== void 0 && _faceapi$tf3.env().flagRegistry.WEBGL_EXP_CONV) faceapi.tf.env().set('WEBGL_EXP_CONV', true); _context6.next = 9; return this.setupFaceAPI(); case 9: _context6.next = 11; return this.setupCamera(); case 11: case "end": return _context6.stop(); } }, _callee6, this); })); function main() { return _main.apply(this, arguments); } return main; }() }, { key: "destroy", value: function destroy() { this._isDistroyed = true; if (this._app.store.videoParentTag) { this._app.store.videoParentTag.remove(); this._app.store.videoParentTag = null; } if (this._app.store.localCameraStream) { this._app.store.localCameraStream.getTracks().forEach(function (item) { return item.stop(); }); this._app.store.localCameraStream = null; } } }]); }(); var _default = exports["default"] = FaceAPI;