accurafaceplugin 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/accura.xml +33314 -0
- package/accuraface1.js +45 -0
- package/accuraface2.js +267 -0
- package/build/accuramain.js +1 -0
- package/package.json +31 -0
package/accuraface2.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
function Utils(errorOutputId) {
|
|
2
|
+
// eslint-disable-line no-unused-vars
|
|
3
|
+
let self = this;
|
|
4
|
+
this.errorOutput = document.getElementById(errorOutputId);
|
|
5
|
+
this.apiKey = "168508101488d7JV8yvF32yRGRti0hyatBHFwev0KInaUAgtGv";
|
|
6
|
+
|
|
7
|
+
const OPENCV_URL = "opencv.js";
|
|
8
|
+
this.loadOpenCv = function (onloadCallback) {
|
|
9
|
+
let script = document.createElement("script");
|
|
10
|
+
script.setAttribute("async", "");
|
|
11
|
+
script.setAttribute("type", "text/javascript");
|
|
12
|
+
script.addEventListener("load", () => {
|
|
13
|
+
if (cv.getBuildInformation) {
|
|
14
|
+
console.log(cv.getBuildInformation());
|
|
15
|
+
onloadCallback();
|
|
16
|
+
} else {
|
|
17
|
+
// WASM
|
|
18
|
+
cv["onRuntimeInitialized"] = () => {
|
|
19
|
+
console.log(cv.getBuildInformation());
|
|
20
|
+
onloadCallback();
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
script.addEventListener("error", () => {
|
|
25
|
+
self.printError("Failed to load " + OPENCV_URL);
|
|
26
|
+
});
|
|
27
|
+
script.src = OPENCV_URL;
|
|
28
|
+
let node = document.getElementsByTagName("script")[0];
|
|
29
|
+
node.parentNode.insertBefore(script, node);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
this.createFileFromUrl = function (path, url, callback) {
|
|
33
|
+
let request = new XMLHttpRequest();
|
|
34
|
+
request.open("GET", url, true);
|
|
35
|
+
request.responseType = "arraybuffer";
|
|
36
|
+
request.onload = function (ev) {
|
|
37
|
+
if (request.readyState === 4) {
|
|
38
|
+
if (request.status === 200) {
|
|
39
|
+
let data = new Uint8Array(request.response);
|
|
40
|
+
cv.FS_createDataFile("/", path, data, true, false, false);
|
|
41
|
+
callback();
|
|
42
|
+
} else {
|
|
43
|
+
self.printError(
|
|
44
|
+
"Failed to load " + url + " status: " + request.status
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
request.send();
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
this.loadImageToCanvas = function (url, cavansId) {
|
|
53
|
+
let canvas = document.getElementById(cavansId);
|
|
54
|
+
let ctx = canvas.getContext("2d");
|
|
55
|
+
let img = new Image();
|
|
56
|
+
img.crossOrigin = "anonymous";
|
|
57
|
+
img.onload = function () {
|
|
58
|
+
canvas.width = img.width;
|
|
59
|
+
canvas.height = img.height;
|
|
60
|
+
ctx.drawImage(img, 0, 0, img.width, img.height);
|
|
61
|
+
};
|
|
62
|
+
img.src = url;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
this.executeCode = function (textAreaId) {
|
|
66
|
+
try {
|
|
67
|
+
this.clearError();
|
|
68
|
+
let code = document.getElementById(textAreaId).value;
|
|
69
|
+
eval(code);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
this.printError(err);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
this.clearError = function () {
|
|
76
|
+
this.errorOutput.innerHTML = "";
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
this.printError = function (err) {
|
|
80
|
+
if (typeof err === "undefined") {
|
|
81
|
+
err = "";
|
|
82
|
+
} else if (typeof err === "number") {
|
|
83
|
+
if (!isNaN(err)) {
|
|
84
|
+
if (typeof cv !== "undefined") {
|
|
85
|
+
err = "Exception: " + cv.exceptionFromPtr(err).msg;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} else if (typeof err === "string") {
|
|
89
|
+
let ptr = Number(err.split(" ")[0]);
|
|
90
|
+
if (!isNaN(ptr)) {
|
|
91
|
+
if (typeof cv !== "undefined") {
|
|
92
|
+
err = "Exception: " + cv.exceptionFromPtr(ptr).msg;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
} else if (err instanceof Error) {
|
|
96
|
+
err = err.stack.replace(/\n/g, "<br>");
|
|
97
|
+
}
|
|
98
|
+
this.errorOutput.innerHTML = err;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
this.loadCode = function (scriptId, textAreaId) {
|
|
102
|
+
let scriptNode = document.getElementById(scriptId);
|
|
103
|
+
let textArea = document.getElementById(textAreaId);
|
|
104
|
+
if (scriptNode.type !== "text/code-snippet") {
|
|
105
|
+
throw Error("Unknown code snippet type");
|
|
106
|
+
}
|
|
107
|
+
textArea.value = scriptNode.text.replace(/^\n/, "");
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
this.addFileInputHandler = function (fileInputId, canvasId) {
|
|
111
|
+
let inputElement = document.getElementById(fileInputId);
|
|
112
|
+
inputElement.addEventListener(
|
|
113
|
+
"change",
|
|
114
|
+
(e) => {
|
|
115
|
+
let files = e.target.files;
|
|
116
|
+
if (files.length > 0) {
|
|
117
|
+
let imgUrl = URL.createObjectURL(files[0]);
|
|
118
|
+
self.loadImageToCanvas(imgUrl, canvasId);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
false
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
function onVideoCanPlay() {
|
|
126
|
+
if (self.onCameraStartedCallback) {
|
|
127
|
+
self.onCameraStartedCallback(self.stream, self.video);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.startCamera = function (resolution, callback, videoId) {
|
|
132
|
+
const constraints = {
|
|
133
|
+
qvga: { width: { exact: 320 }, height: { exact: 240 } },
|
|
134
|
+
vga: { width: { exact: 640 }, height: { exact: 480 } },
|
|
135
|
+
};
|
|
136
|
+
let video = document.getElementById(videoId);
|
|
137
|
+
if (!video) {
|
|
138
|
+
video = document.createElement("video");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let videoConstraint = constraints[resolution];
|
|
142
|
+
if (!videoConstraint) {
|
|
143
|
+
videoConstraint = true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
navigator.mediaDevices
|
|
147
|
+
.getUserMedia({ video: videoConstraint, audio: false })
|
|
148
|
+
.then(function (stream) {
|
|
149
|
+
video.srcObject = stream;
|
|
150
|
+
video.play();
|
|
151
|
+
self.video = video;
|
|
152
|
+
self.stream = stream;
|
|
153
|
+
self.onCameraStartedCallback = callback;
|
|
154
|
+
video.addEventListener("canplay", onVideoCanPlay, false);
|
|
155
|
+
})
|
|
156
|
+
.catch(function (err) {
|
|
157
|
+
self.printError("Camera Error: " + err.name + " " + err.message);
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
this.stopCamera = function () {
|
|
162
|
+
if (this.video) {
|
|
163
|
+
this.video.pause();
|
|
164
|
+
this.video.srcObject = null;
|
|
165
|
+
this.video.removeEventListener("canplay", onVideoCanPlay);
|
|
166
|
+
}
|
|
167
|
+
if (this.stream) {
|
|
168
|
+
this.stream.getVideoTracks()[0].stop();
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
this.captureImage = function (canvasId) {
|
|
173
|
+
let canvas = document.getElementById(canvasId);
|
|
174
|
+
let dataUrl = canvas.toDataURL("image/jpeg");
|
|
175
|
+
return dataUrl;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
this.stringFunction = function (dynamicString) {
|
|
179
|
+
console.log("Dynamic string:", dynamicString);
|
|
180
|
+
|
|
181
|
+
document.getElementById("status-text").innerText = dynamicString;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
this.captureAndSend = function () {
|
|
185
|
+
let video = document.getElementById("cam_input");
|
|
186
|
+
let canvas = document.getElementById("canvas_output");
|
|
187
|
+
|
|
188
|
+
// Hide the video and show the canvas after capturing the photo
|
|
189
|
+
video.style.display = "none";
|
|
190
|
+
canvas.style.display = "block";
|
|
191
|
+
isCanvasVisible = true;
|
|
192
|
+
|
|
193
|
+
// Create a canvas element to temporarily hold the image data
|
|
194
|
+
let tempCanvas = document.createElement("canvas");
|
|
195
|
+
tempCanvas.width = video.videoWidth;
|
|
196
|
+
tempCanvas.height = video.videoHeight;
|
|
197
|
+
let ctx = tempCanvas.getContext("2d");
|
|
198
|
+
ctx.drawImage(video, 0, 0, tempCanvas.width, tempCanvas.height);
|
|
199
|
+
|
|
200
|
+
// Draw the oval shape on the temporary canvas
|
|
201
|
+
ctx.save();
|
|
202
|
+
ctx.beginPath();
|
|
203
|
+
ctx.moveTo(tempCanvas.width / 2, 0);
|
|
204
|
+
ctx.bezierCurveTo(
|
|
205
|
+
tempCanvas.width,
|
|
206
|
+
0,
|
|
207
|
+
tempCanvas.width,
|
|
208
|
+
tempCanvas.height,
|
|
209
|
+
tempCanvas.width / 2,
|
|
210
|
+
tempCanvas.height
|
|
211
|
+
);
|
|
212
|
+
ctx.bezierCurveTo(0, tempCanvas.height, 0, 0, tempCanvas.width / 2, 0);
|
|
213
|
+
ctx.clip();
|
|
214
|
+
ctx.drawImage(tempCanvas, 0, 0);
|
|
215
|
+
ctx.restore();
|
|
216
|
+
|
|
217
|
+
let imageDataUrl = tempCanvas.toDataURL("image/jpeg");
|
|
218
|
+
|
|
219
|
+
// // Check if imageDataUrl is valid
|
|
220
|
+
// if (!imageDataUrl) {
|
|
221
|
+
// console.error("Invalid imageDataUrl");
|
|
222
|
+
// return;
|
|
223
|
+
// }
|
|
224
|
+
|
|
225
|
+
// // Send the captured image to the API
|
|
226
|
+
// fetch("https://accurascan.com/v2/api/liveness", {
|
|
227
|
+
// method: "POST",
|
|
228
|
+
// headers: {
|
|
229
|
+
// "Api-Key": self.apiKey,
|
|
230
|
+
// "Content-Type": "application/json",
|
|
231
|
+
// },
|
|
232
|
+
// body: JSON.stringify({
|
|
233
|
+
// liveness_image: imageDataUrl,
|
|
234
|
+
// }),
|
|
235
|
+
// })
|
|
236
|
+
// .then((response) => {
|
|
237
|
+
// // Check if the response is successful (status code 2xx)
|
|
238
|
+
// if (!response.ok) {
|
|
239
|
+
// throw new Error(
|
|
240
|
+
// `Network response was not ok, status: ${response.status}`
|
|
241
|
+
// );
|
|
242
|
+
// }
|
|
243
|
+
// return response.json(); // Parse response body as JSON
|
|
244
|
+
// })
|
|
245
|
+
// .then(function (data) {
|
|
246
|
+
// console.log("API response:", data);
|
|
247
|
+
|
|
248
|
+
// // Check the "status" property in the data object and display appropriate messages
|
|
249
|
+
// if (data.status === true) {
|
|
250
|
+
// self.stringFunction(
|
|
251
|
+
// "Face detected! Quality: " +
|
|
252
|
+
// data.data.quality +
|
|
253
|
+
// ", Score: " +
|
|
254
|
+
// data.data.score
|
|
255
|
+
// );
|
|
256
|
+
// //When face is detected
|
|
257
|
+
// } else {
|
|
258
|
+
// self.stringFunction("No face detected or other status");
|
|
259
|
+
// // When no face is detected or handle other status scenarios if needed
|
|
260
|
+
// }
|
|
261
|
+
// })
|
|
262
|
+
// .catch((error) => {
|
|
263
|
+
// console.error("Error sending image to API:", error);
|
|
264
|
+
// self.stringFunction("Error sending image to API");
|
|
265
|
+
// });
|
|
266
|
+
};
|
|
267
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default class FacePlugin{constructor(e,t){this.haarcascadeUrl=e,this.faceDetectionCount=0,this.onResultReceived=t,this.FPS=30,this.ZOOM_FACTOR=1.5,this.lastFaceDetectionTime=Date.now(),this.opencvLoaded=!1,this.createUI(),this.addStyles(),this.video=document.getElementById("videoInput"),this.canvas=document.getElementById("canvasOutput"),this.instructionText=document.getElementById("instructionText"),this.loader=document.getElementById("loader");const n=document.createElement("script");n.src="https://cdn.jsdelivr.net/npm/piexifjs@1.0.6/piexif.min.js",document.head.appendChild(n),this.checkOpenCV()}createUI(){const e=document.createElement("div");e.className="container";const t=document.createElement("div");t.className="camera-input",t.style.position="relative";const video=document.createElement("video");video.id="videoInput",video.width=640,video.height=480,video.autoplay=!0,video.muted=!0;const canvas=document.createElement("canvas");canvas.id="canvasOutput",canvas.style.display="none";const n=document.createElement("div");n.id="loader",n.className="loader",n.style.display="none";const i=document.createElement("p");i.id="instructionText",t.appendChild(video),t.appendChild(canvas),t.appendChild(n),e.appendChild(t),e.appendChild(i),document.body.appendChild(e)}checkOpenCV(){const e=()=>{window.cv?(this.opencvLoaded=!0,this.openCvReady()):setTimeout(e,100)};e()}openCvReady(){navigator.mediaDevices.getUserMedia({video:!0,audio:!1}).then((e=>{this.video.srcObject=e,this.video.play(),this.isCanvasVisible=!1,this.video.addEventListener("loadedmetadata",(()=>{if(this.video.videoWidth&&this.video.videoHeight){this.canvas.width=640,this.canvas.height=480;const e=new window.cv.Mat(this.video.videoHeight,this.video.videoWidth,window.cv.CV_8UC4),t=new window.cv.Mat(this.video.videoHeight,this.video.videoWidth,window.cv.CV_8UC4),n=new window.cv.VideoCapture(this.video),i=new window.cv.CascadeClassifier,o=new window.Utils("errorMessage"),s=this.haarcascadeUrl;o.createFileFromUrl(s,s,(()=>{i.load(s),this.facepluginopencv(n,e,t,i)}))}}))})).catch((e=>{console.error("An error occurred while accessing media devices: ",e)}))}isFaceInsideOval(e,canvas){const t=canvas.width/2,n=canvas.height/2,i=canvas.width/2,o=canvas.height/2;return(e.x+e.width/2-t)**2/i**2+(e.y+e.height/2-n)**2/o**2<=1}facepluginopencv(e,t,n,i){const o=Date.now();e.read(t);const s=new window.cv.Rect(t.cols/2-t.cols/(2*this.ZOOM_FACTOR),t.rows/2-t.rows/(2*this.ZOOM_FACTOR),t.cols/this.ZOOM_FACTOR,t.rows/this.ZOOM_FACTOR);let a=t.roi(s);window.cv.resize(a,n,new window.cv.Size(t.cols,t.rows),0,0,window.cv.INTER_LINEAR),this.instructionText.textContent="Keep Your Face In The Frame";let c=new window.cv.RectVector;try{if(i.detectMultiScale(t,c,1.5,3,0),1===c.size()){const e=c.get(0),n=(new window.cv.Rect(e.x,e.y,e.width,e.height),.14);let i=Math.max(0,e.x-e.width*n),o=Math.max(0,e.y-e.height*n),s=Math.min(t.cols-i,e.width*(1+2*n)),a=Math.min(t.rows-o,e.height*(1+2*n));const d=new window.cv.Rect(i,o,s,a);window.cv.rectangle(t,new window.cv.Point(d.x,d.y),new window.cv.Point(d.x+d.width,d.y+d.height),[5,255,0,255],2);const h=e.width*e.height,r=h/(Math.PI*(this.canvas.width/2)*(this.canvas.height/2))*100;if(this.isFaceInsideOval(e,this.canvas)){if(r<30)this.instructionText.textContent="Stay closer to the window";else if(r>=30&&r<=100&&(this.instructionText.textContent="Processing....",this.faceDetectionCount++,2===this.faceDetectionCount)){this.convertToBase64WithMetadata(d);const e=this.video.srcObject;if(e){e.getTracks().forEach((e=>e.stop()))}return}}else this.instructionText.textContent="Move inside the oval window";this.lastFaceDetectionTime=Date.now()}}catch(e){console.error("Error processing video:",e)}finally{c.delete()}this.isCanvasVisible&&(window.cv.flip(t,t,1),window.cv.imshow(this.canvas.id,t));const d=1e3/this.FPS-(Date.now()-o);setTimeout((()=>this.facepluginopencv(e,t,n,i)),d)}convertToBase64WithMetadata(e){const t=document.createElement("canvas");t.width=this.video.videoWidth,t.height=this.video.videoHeight;const n=t.getContext("2d");n.drawImage(this.video,0,0,t.width,t.height);const i=document.createElement("canvas");i.width=e.width,i.height=e.height;const o=i.getContext("2d"),s=n.getImageData(e.x,e.y,e.width,e.height);o.putImageData(s,0,0);const a={"0th":{[piexif.ImageIFD.Make]:"AccuraFace",[piexif.ImageIFD.Model]:"FaceDetectionCamera"}},c=piexif.dump(a),d=piexif.insert(c,i.toDataURL("image/jpeg",.92));this.onResultReceived&&this.onResultReceived({base64:d,metadata:a})}addStyles(){const e=document.createElement("style");e.type="text/css",e.innerText="\n body {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n margin: 0;\n background-color: rgb(255, 255, 255);\n font-family: Arial, sans-serif;\n flex-direction: column;\n }\n \n .container {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 20px;\n background-color: transparent;\n border-radius: 10px;\n }\n \n #instructionText {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 10px;\n background-color: #fff;\n color: rgb(10, 18, 131);\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n border-radius: 5px;\n margin-bottom: 20px;\n font-size: 30px;\n font-weight: bold;\n }\n \n .camera-input {\n width: 250px;\n height: 370px;\n position: relative;\n overflow: hidden;\n border-radius: 50%;\n transform: scale(1.0); \n }\n \n #videoInput {\n width: 100%;\n height: 100%;\n object-fit: cover;\n transition: transform 0.3s ease-in-out;\n transform: scaleX(-1) scale(1.5); \n }\n \n #canvasOutput {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n transform: scale(1.5);\n }\n \n #response-text {\n font-size: 18px;\n color: #333;\n margin-top: 10px;\n }\n ",document.head.appendChild(e)}}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "accurafaceplugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "./src/accura.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "terser ./src/accura.js -o build/accuramain.js --compress --mangle reserved=['video','canvas','context'] --source-map"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"build/accuramain.js",
|
|
11
|
+
"accuraface1.js",
|
|
12
|
+
"accuraface2.js",
|
|
13
|
+
"accura.xml"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [],
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@babel/core": "^7.25.2",
|
|
20
|
+
"@babel/preset-env": "^7.25.4",
|
|
21
|
+
"babel-loader": "^9.2.1",
|
|
22
|
+
"file-loader": "^6.2.0",
|
|
23
|
+
"raw-loader": "^4.0.2",
|
|
24
|
+
"terser": "^5.34.1",
|
|
25
|
+
"webpack": "^5.95.0",
|
|
26
|
+
"webpack-cli": "^5.1.4"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
}
|