UNPKG

@privateid/small-age-sdk-alpha

Version:
713 lines (620 loc) 24.4 kB
/* eslint-disable no-eval */ /* eslint-disable default-param-last */ /* eslint-disable no-undef */ /* eslint-disable no-underscore-dangle */ // importScripts('https://unpkg.com/comlink/dist/umd/comlink.js'); importScripts('./unpkg.js'); let debugType; let wasmPrivGoogModule; let wasmGoogSession = null; let privid_wasm_result = null; let checkWasmLoaded = null; let isSimd; let currentVersion; let inputPtr; let barCodePtr; /** * @brief A closure to create an output 32bits pointer closure. * This is usefull for allocating a native address and pass it to the * 'wasmPrivModule' so it can return in the address of a buffer (or an object like session) * that was allocated inside the wasm. This typically, correspond to * an argument of type void** (marked output argument) to pass to a native wasm * call. * @usage var myoutput_ptr = output_ptr(); * when passing the output pointer to the 'wasmPrivModule' module use * wasmPrivModule.nativecall(myoutput_ptr.outer_ptr()); * Then pull out the the allocated buffer by the wasm call this way: * @code * my_buffer_or_structure = myoutput_ptr.inner_ptr(); * @note It is the responsability of the caller to free the pointer returned by this inner_ptr() */ const m_output_ptr = function (module) { let out_ptr = null; let in_ptr = null; const priv_module = module; const free_ptr = (ptr) => { if (ptr) { priv_module._free(ptr); // eslint-disable-next-line no-param-reassign ptr = null; } }; return { /** * @brief Allocates a pointer to contain the result and return it, * if the container is already created it will be returned */ outer_ptr: () => { // TODO: may be used SharedArrayBuffer() instead // allocate memory the expected pointer (outer pointer or container) if (!out_ptr) out_ptr = priv_module._malloc(Int32Array.BYTES_PER_ELEMENT); return out_ptr; }, /** * @brief Creates a javascript Uint32Array pointer to contain the result pointed by outer_ptr and return it, * It is the responsability of the caller to free the pointer returned by this function */ inner_ptr: () => { // If we did not allocate yet the output buffer return null if (!out_ptr) return null; // if we already have our inner pointer for this closure return it if (in_ptr) return in_ptr; // Access the outer pointer as an arry of uint32 which conatin a single cell // whose value is the pointer allocated in the wasm module (inner pointer of the output param) // and return it [in_ptr] = new Uint32Array(priv_module.HEAPU8.buffer, out_ptr, 1); return in_ptr; }, }; }; /** * @brief A closure to create a string buffer arguments that can be used with wasm calls * for a given javascript value. * This is suitable for native calls that have string input arguments represented with contigious * string_buffer,sizeofbuffer arguments. * If the 'text' argument is null or undefined or NaN then the arguments generated are [null,0] * @usage * var url_args= buffer_args(url); var key_args= buffer_args(key); var session_out_ptr = output_ptr(); const s_result = wasmPrivModule._privid_initialize_session( ...key_args.args(), ...url_args.args(), debug_type, session_out_ptr.outer_ptr(), ); url_args.free(); key_args.free(); //get var session = session_out_ptr.inner_ptr(); * * when .free() is called the closure can be reused to create a buffer for the same string with which, it was created with * over and over again. */ const stringToBufferArgs = function (module, text) { const priv_module = module; let strInputtPtr = null; let strInputSize = 0; let argsv = []; return { args: () => { do { if (argsv.length > 0) break; argsv = [null, 0]; if (text === null) break; if (text === undefined) break; // eslint-disable-next-line use-isnan if (text === NaN) break; const str = `${text}`; const encoder = new TextEncoder(); const bytes = encoder.encode(str); strInputSize = bytes.length * bytes.BYTES_PER_ELEMENT; strInputtPtr = priv_module._malloc(strInputSize); priv_module.HEAP8.set(bytes, strInputtPtr / bytes.BYTES_PER_ELEMENT); argsv = [strInputtPtr, strInputSize]; } while (false); return argsv; }, free: () => { if (strInputtPtr) { priv_module._free(strInputtPtr); strInputtPtr = null; strInputSize = 0; argsv = []; } }, }; }; const load_module = async (wasm_js_path, wasm_bin_path, moduleType, saveCache, version) => { console.log("start load", {wasm_js_path, wasm_bin_path, moduleType, saveCache, version} ) const wasm = await fetch(wasm_bin_path); const script = await fetch(wasm_js_path); console.log("Wasm, script", {wasm, script}); const scriptBuffer = await script.text(); const buffer = await wasm.arrayBuffer(); eval(scriptBuffer); const loadedModule = await createTFLiteModule({ wasmBinary: buffer }); if (saveCache) { await putKey(moduleType, buffer, scriptBuffer, version); } return loadedModule; }; const isLoad = (a_simd, a_debug_type) => new Promise(async (resolve, reject) => { if (a_debug_type === undefined) { debugType = 0; } else { debugType = parseInt(a_debug_type, 10); } isSimd = a_simd; const cachedModule = await readKey('gage'); const modulePath = a_simd ? 'simd' : 'noSimd'; const fetchdWasmVersion = await fetch(`../wasm/${modulePath}/version.json`); const fetchdVersion = await fetchdWasmVersion.json(); currentVersion = fetchdVersion.version; if (cachedModule?.version && cachedModule?.version.toString() === fetchdVersion?.version.toString()) { if (debugType >= 1) { console.log(`loading from cache.`); } if (!wasmPrivGoogModule) { const { cachedWasm, cachedScript } = cachedModule; eval(cachedScript); wasmPrivGoogModule = await createTFLiteModule({ wasmBinary: cachedWasm }); checkWasmLoaded = true; } console.log("gage modules:", { wasmPrivGoogModule }); await initializeGoogWasmSession(debugType); resolve('Cache Loaded'); } else { const wasm_path = { simd: { bin: '../wasm/simd/privid_fhe_goog.wasm', js: '../wasm/simd/privid_fhe_goog.js', }, noSimd: { bin: '../wasm/noSimd/privid_fhe_goog_no_simd.wasm', js: '../wasm/noSimd/privid_fhe_goog_no_simd.js', }, }; try { // loading wasm modules if (!wasmPrivGoogModule) { wasmPrivGoogModule = await load_module( isSimd ? wasm_path.simd.js : wasm_path.noSimd.js, isSimd ? wasm_path.simd.bin : wasm_path.noSimd.bin, 'gage', true, currentVersion.toString(), ); console.log({ wasmPrivGoogModule }); await initializeGoogWasmSession(debugType); checkWasmLoaded = true; } resolve(true); } catch (e) { console.log(e); } } }); function readKey(key) { if (!indexedDB) return Promise.reject(new Error('IndexedDB not available')); return new Promise((resolve, reject) => { const open = indexedDB.open('/privid-wasm', 21); open.onerror = function () { resolve(false); // console.log('Private Browser. '); }; open.onupgradeneeded = function () { open.result.createObjectStore('/privid-wasm'); }; open.onsuccess = function () { const db = open.result; const tx = db.transaction('/privid-wasm', 'readwrite'); const store = tx.objectStore('/privid-wasm'); const getKey = store.get(key); getKey.onsuccess = function () { resolve(getKey.result); }; tx.onerror = function () { reject(tx.error); }; tx.oncomplete = function () { try { db.close(); } catch (e) { // } }; }; }); } function putKey(key, cachedWasm, cachedScript, version) { if (!indexedDB) return Promise.reject(new Error('IndexedDB not available')); return new Promise((resolve, reject) => { const open = indexedDB.open('/privid-wasm', 21); open.onerror = function () { resolve(false); // console.log('Private Browser.'); }; open.onupgradeneeded = function () { open.result.createObjectStore('/privid-wasm'); }; open.onsuccess = function () { const db = open.result; const tx = db.transaction('/privid-wasm', 'readwrite'); const store = tx.objectStore('/privid-wasm'); const getKey = store.put({ cachedWasm, cachedScript, version }, key); getKey.onsuccess = function () { resolve('saved'); }; tx.onerror = function () { reject(tx.error); }; tx.oncomplete = function () { try { db.close(); } catch (e) { // } }; }; }); } async function initializeGoogWasmSession(debug_type) { console.log('INITIALIZE SESSION GOOGLE:'); if (!wasmGoogSession) { console.log('initializing session'); const session_out_ptr = m_output_ptr(wasmPrivGoogModule); const debug = debug_type || 0; const initializationArgs = { debug_level: debug, }; const configArgs = stringToBufferArgs(wasmPrivGoogModule, JSON.stringify(initializationArgs)); console.log("Config Args:", configArgs); // console.log('debug type in session?>??', debug); console.log("module:", wasmPrivGoogModule); const s_result = wasmPrivGoogModule._privid_initialize_session(...configArgs.args(), session_out_ptr.outer_ptr()); // console.log("Initialize session result:", s_result); if (s_result) { if (debug_type >= 1) { console.log('[WASM_RESULT] : goog session initialized successfully'); } } else { if (debug_type >= 1) { console.log('[WASM_RESULT] : goog session initialized failed'); } return; } // get our inner session created by wasm and free the outer container ptr wasmGoogSession = session_out_ptr.inner_ptr(); configArgs.free(); // console.log('WASM MODULES', wasmPrivGoogModule); // await wasmPrivGoogModule._privid_set_default_configuration(wasmGoogSession, 1); await wasmPrivGoogModule._privid_set_configuration(wasmGoogSession, 1); if (debug_type >= 1) { // console.log('Session Created'); } } else { // eslint-disable-next-line no-lonely-if if (debug_type >= 1) { // console.log('wasm goog Session', wasmGoogSession); // console.log('Wasm goog session is available. Skipping creating session'); } } } const multiframeLivenessAndAgePredict = async (data, width, height, config, cb) => { privid_wasm_result = cb; const imageSize = data.length * data.BYTES_PER_ELEMENT; const imagePtr = wasmPrivGoogModule._malloc(imageSize); wasmPrivGoogModule.HEAP8.set(data, imagePtr / data.BYTES_PER_ELEMENT); const encoder = new TextEncoder(); const config_bytes = encoder.encode(`${config}`); const configInputSize = config.length; const configInputPtr = wasmPrivGoogModule._malloc(configInputSize); wasmPrivGoogModule.HEAP8.set(config_bytes, configInputPtr / config_bytes.BYTES_PER_ELEMENT); const resultFirstPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); // create a pointer to interger to hold the length of the output buffer const resultLenPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); console.log({ wasmGoogSession /* session pointer */, imagePtr, width, height, configInputPtr, configInputSize, resultFirstPtr, resultLenPtr,}) try { // return false if failure, true if operation ran without problem. The livness code is returned by the JS call back await wasmPrivGoogModule._privid_estimate_age( wasmGoogSession /* session pointer */, imagePtr, width, height, configInputPtr, configInputSize, resultFirstPtr, resultLenPtr, ); } catch (e) { console.error('_estimate_age', e); } wasmPrivGoogModule._free(configInputPtr); wasmPrivGoogModule._free(resultFirstPtr); wasmPrivGoogModule._free(resultLenPtr); wasmPrivGoogModule._free(imagePtr); }; const prividDocumentMugshotFaceCompare = (imageInputA, imageInputB, simd, debug_type = 0, cb, config = {}) => new Promise(async (resolve) => { privid_wasm_result = cb; // First Image A const { data: imageDataA } = imageInputA; const imageInputSizeA = imageDataA.length * imageDataA.BYTES_PER_ELEMENT; const imageInputPtrA = wasmPrivGoogModule._malloc(imageInputSizeA); wasmPrivGoogModule.HEAP8.set(imageDataA, imageInputPtrA / imageDataA.BYTES_PER_ELEMENT); // Second Image B const { data: imageDataB } = imageInputB; const imageInputSizeB = imageDataB.length * imageDataB.BYTES_PER_ELEMENT; const imageInputPtrB = wasmPrivGoogModule._malloc(imageInputSizeB); wasmPrivGoogModule.HEAP8.set(imageDataB, imageInputPtrB / imageDataB.BYTES_PER_ELEMENT); const encoder = new TextEncoder(); const config_bytes = encoder.encode(`${config}`); const configInputSize = config.length; const configInputPtr = wasmPrivGoogModule._malloc(configInputSize); wasmPrivGoogModule.HEAP8.set(config_bytes, configInputPtr / config_bytes.BYTES_PER_ELEMENT); const resultFirstPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); // create a pointer to interger to hold the length of the output buffer const resultLenPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); // Initialize Session // await initializeWasmSession(apiUrl, apiKey); let result = null; try { result = wasmPrivGoogModule._privid_compare_face_and_mugshot( wasmGoogSession, configInputPtr, configInputSize, imageInputPtrA, // imageInputA.data.length, imageInputA.width, imageInputA.height, imageInputPtrB, // imageInputB.data.length, imageInputB.width, imageInputB.height, resultFirstPtr, resultLenPtr, ); } catch (e) { console.error('________ Doc mugshot face compare _______', e); } wasmPrivGoogModule._privid_free_char_buffer(configInputPtr); wasmPrivGoogModule._free(imageInputPtrA); wasmPrivGoogModule._free(imageInputPtrB); wasmPrivGoogModule._free(resultFirstPtr); wasmPrivGoogModule._free(resultLenPtr); resolve({ result }); }); const scanDocument = async (imageInput, simd, cb, doPredict, config, debug_type = 0) => { privid_wasm_result = cb; configGlobal = config; // const version = wasmPrivModule._get_version(); const encoder = new TextEncoder(); const config_bytes = encoder.encode(`${config}`); const configInputSize = config.length; const configInputPtr = wasmPrivGoogModule._malloc(configInputSize); wasmPrivGoogModule.HEAP8.set(config_bytes, configInputPtr / config_bytes.BYTES_PER_ELEMENT); const { data: imageData } = imageInput; const imageInputSize = imageData.length * imageData.BYTES_PER_ELEMENT; if (!inputPtr) { inputPtr = wasmPrivGoogModule._malloc(imageInputSize); } wasmPrivGoogModule.HEAP8.set(imageData, inputPtr / imageData.BYTES_PER_ELEMENT); // Cropped Document malloc const croppedDocumentBufferFirstPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); const croppedDocumentBufferLenPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); // Cropped Mugshot malloc const croppedMugshotBufferFirstPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); const croppedMugshotBufferLenPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); let result = null; console.log("data before front scan:", { wasmGoogSession, configInputPtr, configInputSize, inputPtr, width: imageInput.width, height: imageInput.height, croppedDocumentBufferFirstPtr, croppedDocumentBufferLenPtr, croppedMugshotBufferFirstPtr, croppedMugshotBufferLenPtr, }) try { result = wasmPrivGoogModule._privid_doc_scan_face( wasmGoogSession, configInputPtr, configInputSize, inputPtr, imageInput.width, imageInput.height, croppedDocumentBufferFirstPtr, croppedDocumentBufferLenPtr, croppedMugshotBufferFirstPtr, croppedMugshotBufferLenPtr, null, 0 ); } catch (err) { console.error('-----------------ERROR---------------', err); return; } // Document const { outputBufferData: croppedDocument } = getBufferFromPtr( croppedDocumentBufferFirstPtr, croppedDocumentBufferLenPtr, ); // Mugshot const { outputBufferData: croppedMugshot } = getBufferFromPtr( croppedMugshotBufferFirstPtr, croppedMugshotBufferLenPtr, ); const imageBuffer = getBufferFromPtrImage(inputPtr, imageInputSize); wasmPrivGoogModule._free(croppedDocumentBufferFirstPtr); wasmPrivGoogModule._free(croppedDocumentBufferLenPtr); wasmPrivGoogModule._free(croppedMugshotBufferFirstPtr); wasmPrivGoogModule._free(croppedMugshotBufferLenPtr); wasmPrivGoogModule._free(configInputPtr); wasmPrivGoogModule._free(inputPtr); inputPtr = null; // eslint-disable-next-line consistent-return, no-param-reassign return { result, croppedDocument, croppedMugshot, imageData: imageInput, }; }; const isValidBarCode = async (imageInput, simd, cb, config, debug_type = 0) => { privid_wasm_result = cb; configGlobal = config; const { data: imageData } = imageInput; const imageInputSize = imageData.length * imageData.BYTES_PER_ELEMENT; if (!barCodePtr) { barCodePtr = wasmPrivGoogModule._malloc(imageInputSize); } wasmPrivGoogModule.HEAP8.set(imageData, barCodePtr / imageData.BYTES_PER_ELEMENT); // Cropped Document malloc const croppedDocumentBufferFirstPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); const croppedDocumentBufferLenPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); // Cropped Barcode malloc const croppedBarcodeBufferFirstPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); const croppedBarcodeBufferLenPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); const encoder = new TextEncoder(); const config_bytes = encoder.encode(`${config}`); const configInputSize = config.length; const configInputPtr = wasmPrivGoogModule._malloc(configInputSize); wasmPrivGoogModule.HEAP8.set(config_bytes, configInputPtr / config_bytes.BYTES_PER_ELEMENT); let result = null; try { result = wasmPrivGoogModule._privid_doc_scan_barcode_for_age( wasmGoogSession, configInputPtr, configInputSize, barCodePtr, imageInput.width, imageInput.height, null, 0, ); } catch (err) { console.error('-----------_E_-----------', err); } // Document // const { outputBufferData: croppedDocument, outputBufferSize: croppedDocumentSize } = getBufferFromPtr( // croppedDocumentBufferFirstPtr, // croppedDocumentBufferLenPtr, // ); // // // Mugshot // const { outputBufferData: croppedBarcode, outputBufferSize: croppedBarcodeSize } = getBufferFromPtr( // croppedBarcodeBufferFirstPtr, // croppedBarcodeBufferLenPtr, // ); // let imageBuffer = null; // if (croppedBarcodeSize && croppedDocumentSize) { // imageBuffer = getBufferFromPtrImage(barCodePtr, imageInputSize); // } wasmPrivGoogModule._free(barCodePtr); barCodePtr = null; wasmPrivGoogModule._free(croppedDocumentBufferFirstPtr); wasmPrivGoogModule._free(croppedDocumentBufferLenPtr); wasmPrivGoogModule._free(croppedBarcodeBufferFirstPtr); wasmPrivGoogModule._free(croppedBarcodeBufferLenPtr); wasmPrivGoogModule._free(configInputPtr); // return { result, croppedDocument, croppedBarcode, imageData: imageBuffer }; }; const getBufferFromPtr = (bufferPtr, bufferSize) => { const [outputBufferSize] = new Uint32Array(wasmPrivGoogModule.HEAPU8.buffer, bufferSize, 1); let outputBufferSecPtr = null; if (outputBufferSize > 0) { [outputBufferSecPtr] = new Uint32Array(wasmPrivGoogModule.HEAPU8.buffer, bufferPtr, 1); } const outputBufferPtr = new Uint8Array(wasmPrivGoogModule.HEAPU8.buffer, outputBufferSecPtr, outputBufferSize); const outputBuffer = Uint8ClampedArray.from(outputBufferPtr); wasmPrivGoogModule._privid_free_char_buffer(outputBufferSecPtr); const outputBufferData = outputBufferSize > 0 ? outputBuffer : null; return { outputBufferData, outputBufferSize }; }; const getBufferFromPtrImage = (bufferPtr, outputBufferSize) => { const outputBufferPtr = new Uint8Array(wasmPrivGoogModule.HEAPU8.buffer, bufferPtr, outputBufferSize); const outputBuffer = Uint8ClampedArray.from(outputBufferPtr); return outputBufferSize > 0 ? outputBuffer : null; }; const frontDocumentOcr = (imageInput, simd, cb, config, debug_type = 0) => { console.log("params", {imageInput, simd, cb, config, debug_type }) privid_wasm_result = cb; configGlobal = config; const { data: imageData } = imageInput; const imageInputSize = imageData.length * imageData.BYTES_PER_ELEMENT; if (!barCodePtr) { barCodePtr = wasmPrivGoogModule._malloc(imageInputSize); } wasmPrivGoogModule.HEAP8.set(imageData, barCodePtr / imageData.BYTES_PER_ELEMENT); // Cropped Document malloc const croppedDocumentBufferFirstPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); const croppedDocumentBufferLenPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); // Cropped Barcode malloc const croppedBarcodeBufferFirstPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); const croppedBarcodeBufferLenPtr = wasmPrivGoogModule._malloc(Int32Array.BYTES_PER_ELEMENT); const encoder = new TextEncoder(); const config_bytes = encoder.encode(`${config}`); const configInputSize = config.length; const configInputPtr = wasmPrivGoogModule._malloc(configInputSize); wasmPrivGoogModule.HEAP8.set(config_bytes, configInputPtr / config_bytes.BYTES_PER_ELEMENT); let result = null; try { result = wasmPrivGoogModule._privid_doc_scan_face_for_age( wasmGoogSession, configInputPtr, configInputSize, barCodePtr, imageInput.width, imageInput.height, null, 0, ); } catch (err) { console.error('-----------_E_-----------', err); } // Document // const { outputBufferData: croppedDocument, outputBufferSize: croppedDocumentSize } = getBufferFromPtr( // croppedDocumentBufferFirstPtr, // croppedDocumentBufferLenPtr, // ); // // // Mugshot // const { outputBufferData: croppedBarcode, outputBufferSize: croppedBarcodeSize } = getBufferFromPtr( // croppedBarcodeBufferFirstPtr, // croppedBarcodeBufferLenPtr, // ); // let imageBuffer = null; // if (croppedBarcodeSize && croppedDocumentSize) { // imageBuffer = getBufferFromPtrImage(barCodePtr, imageInputSize); // } console.log("OCR RESULT: ", result); wasmPrivGoogModule._free(barCodePtr); barCodePtr = null; wasmPrivGoogModule._free(croppedDocumentBufferFirstPtr); wasmPrivGoogModule._free(croppedDocumentBufferLenPtr); wasmPrivGoogModule._free(croppedBarcodeBufferFirstPtr); wasmPrivGoogModule._free(croppedBarcodeBufferLenPtr); wasmPrivGoogModule._free(configInputPtr); // return { imageInput }; }; Comlink.expose({ isLoad, multiframeLivenessAndAgePredict, prividDocumentMugshotFaceCompare, scanDocument, isValidBarCode, frontDocumentOcr, });