人脸识别
This commit is contained in:
parent
e58abcbf89
commit
def6f87d7d
|
@ -1248,6 +1248,10 @@
|
|||
<Content Include="OfficeControl\OfficeControl.ocx" />
|
||||
<Content Include="OfficeControl\signtoolcontrol.js" />
|
||||
<Content Include="OfficeControl\手工卸载安装NTKO OFFICE文档控件.txt" />
|
||||
<Content Include="res\js\cascade.js" />
|
||||
<Content Include="res\js\ccv.js" />
|
||||
<Content Include="res\js\jquery-3.2.1.min.js" />
|
||||
<Content Include="res\js\jquery.facedetection.js" />
|
||||
<Content Include="RLSB\CheckConfirm.aspx" />
|
||||
<Content Include="RLSB\FaceConfirm.aspx" />
|
||||
<Content Include="RLSB\UploadImageCheck.aspx" />
|
||||
|
@ -7448,7 +7452,6 @@
|
|||
<Folder Include="App_Themes\Default\Images\" />
|
||||
<Folder Include="common\ReportPrint\upload\" />
|
||||
<Folder Include="File\Image\" />
|
||||
<Folder Include="res\js\" />
|
||||
<Folder Include="SeetaFaceDir\modelimages\" />
|
||||
<Folder Include="tmpupload\" />
|
||||
<Folder Include="WeldMat\res\js\vendor\" />
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="FaceRecognition.aspx.cs" Inherits="FineUIPro.Web.WeldMat.UsingSentMat.FaceRecognition" %>
|
||||
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="FaceRecognition.aspx.cs" Inherits="FineUIPro.Web.HJGL.MaterialManage.FaceRecognition" %>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head id="Head1" runat="server">
|
||||
|
@ -14,135 +14,169 @@
|
|||
button { padding: 8px 16px; margin: 5px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form id="form1" runat="server">
|
||||
<f:PageManager ID="PageManager1" AutoSizePanelID="Panel1" runat="server" />
|
||||
<body >
|
||||
<form id="form1" runat="server" style="text-align:center">
|
||||
<%-- <f:PageManager ID="PageManager1" AutoSizePanelID="Panel1" runat="server" />
|
||||
<f:Panel ID="Panel1" runat="server" ShowBorder="false" ShowHeader="false" Layout="Region">
|
||||
<Items>
|
||||
<f:Panel runat="server" ID="panelCenterRegion" RegionPosition="Center" ShowBorder="true"
|
||||
Layout="VBox" ShowHeader="true" BodyPadding="5px" IconFont="PlusCircle" Title="人脸识别"
|
||||
|
||||
Layout="VBox" ShowHeader="true" BodyPadding="5px" IconFont="PlusCircle" Title="人脸识别"
|
||||
TitleToolTip="人脸识别" AutoScroll="true">
|
||||
<Items>
|
||||
<f:ContentPanel ShowBorder="false" ShowHeader="false" ID="ContentPanel1" runat ="server" CssStyle="text-align:center" >
|
||||
<video id="video" width="480" height="640" autoplay></video>
|
||||
<canvas id="canvas" width="480" height="640" style="display:none" ></canvas>
|
||||
<div>
|
||||
--%> <video id="video" width="480" height="640" style="display:none" autoplay ></video>
|
||||
<canvas id="canvas" width="480" height="640" ></canvas>
|
||||
<%-- <div>
|
||||
<button id="captureBtn" type="button">识别</button>
|
||||
</div>
|
||||
</f:ContentPanel>
|
||||
</f:ContentPanel>
|
||||
</Items>
|
||||
</f:Panel>
|
||||
</Items>
|
||||
</f:Panel>
|
||||
</f:Panel> --%>
|
||||
</form>
|
||||
|
||||
<script src="../../res/js/jquery-3.2.1.min.js"></script>
|
||||
<script src="../../res/js/ccv.js"></script>
|
||||
<script src="../../res/js/cascade.js"></script>
|
||||
<script src="../../res/js/jquery.facedetection.js"></script>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
// 返回false,来阻止浏览器右键菜单
|
||||
const video = document.getElementById('video');
|
||||
const canvas = document.getElementById('canvas');
|
||||
const captureBtn = document.getElementById('captureBtn');
|
||||
const ctx = canvas.getContext('2d');
|
||||
let stream = null;
|
||||
//// 返回false,来阻止浏览器右键菜单
|
||||
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: {
|
||||
width: { ideal:480 },
|
||||
height: { ideal:640 },
|
||||
facingMode: "environment" // 后置摄像头
|
||||
},
|
||||
audio: false
|
||||
});
|
||||
video.srcObject = stream;
|
||||
} catch (err) {
|
||||
console.log("摄像头访问错误:", err);
|
||||
alert(`无法访问摄像头: ${err.message}`);
|
||||
}
|
||||
}, 1000)
|
||||
$(function () {
|
||||
const video = document.getElementById('video');
|
||||
const canvas = document.getElementById('canvas');
|
||||
const captureBtn = document.getElementById('captureBtn');
|
||||
const context = canvas.getContext('2d');
|
||||
let stream = null;
|
||||
|
||||
|
||||
|
||||
// 截图功能
|
||||
document.getElementById('captureBtn').addEventListener('click', () => {
|
||||
if (!stream) return alert('请先开启摄像头');
|
||||
|
||||
if (captureBtn.innerHTML == '识别') {
|
||||
captureBtn.innerHTML = '重新识别'
|
||||
|
||||
canvas.style = "";
|
||||
video.style = "display:none";
|
||||
// 适配高DPI屏幕
|
||||
const scale = window.devicePixelRatio || 1;
|
||||
canvas.width = video.videoWidth * scale;
|
||||
canvas.height = video.videoHeight * scale;
|
||||
|
||||
ctx.scale(scale, scale);
|
||||
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
|
||||
|
||||
// 生成图片下载链接
|
||||
const imgUrl = canvas.toDataURL('image/jpeg');
|
||||
|
||||
upLoadFile(imgUrl)
|
||||
//const link = document.createElement('a');
|
||||
//link.download = 'capture-' + new Date().getTime() + '.jpg';
|
||||
//link.href = imgUrl;
|
||||
//link.click();
|
||||
|
||||
} else {
|
||||
captureBtn.innerHTML = '识别'
|
||||
|
||||
video.style = "";
|
||||
canvas.style = "display:none";
|
||||
}
|
||||
|
||||
});
|
||||
function upLoadFile(data) {
|
||||
$.ajax({
|
||||
url: "FaceRecognition.aspx/UploadData",
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({
|
||||
data: data
|
||||
|
||||
}),
|
||||
success: function (data) {
|
||||
/*window.parent.backData(data.d)*/
|
||||
|
||||
if (data.d.indexOf('识别失败') !== -1) {
|
||||
alert('识别失败请重新识别')
|
||||
}
|
||||
else {
|
||||
// window.location.href = 'UsingMat.aspx?welderQRCode=' + data.d ;
|
||||
//window.open('UsingMat.aspx?welderCode=' + data.d , '_blank');
|
||||
//var activeWindow = F.getActiveWindow();
|
||||
//activeWindow.window.backData(data.d);
|
||||
//activeWindow.hide();
|
||||
|
||||
var node = {
|
||||
|
||||
iframeUrl: './WeldMat/UsingSentMat/UsingMat.aspx?welderCode=' + data.d,
|
||||
title: "焊材领用",
|
||||
id: "UsingMat" + getFormattedDate()
|
||||
};
|
||||
|
||||
window.parent.addExampleTab(node);
|
||||
|
||||
}
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: {
|
||||
width: { ideal: 480 },
|
||||
height: { ideal: 640 },
|
||||
facingMode: "environment" // 后置摄像头
|
||||
},
|
||||
audio: false
|
||||
});
|
||||
video.srcObject = stream;
|
||||
} catch (err) {
|
||||
console.log("摄像头访问错误:", err);
|
||||
alert(`无法访问摄像头: ${err.message}`);
|
||||
}
|
||||
})
|
||||
}
|
||||
function getFormattedDate() {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
return year + month + day + hours + minutes + seconds;
|
||||
}
|
||||
}, 1000)
|
||||
is_stop = 0
|
||||
video.ontimeupdate = function () {
|
||||
if (is_stop) return
|
||||
context.drawImage(video, 0, 0, video.width, video.height);
|
||||
var base64 = canvas.toDataURL('images/png');
|
||||
$('#canvas').faceDetection({
|
||||
complete: function (faces) {
|
||||
|
||||
if (faces.length >= 1) {
|
||||
is_stop = 1;
|
||||
draw_face_box(faces)
|
||||
//upload(base64)
|
||||
const imgUrl = canvas.toDataURL('image/jpeg');
|
||||
|
||||
upLoadFile(imgUrl)
|
||||
}
|
||||
console.log(faces)
|
||||
}
|
||||
});
|
||||
}
|
||||
//画出人脸区域
|
||||
function draw_face_box(faces) {
|
||||
var rect;
|
||||
var i;
|
||||
debugger
|
||||
//context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
for (i = 0; i < faces.length; i++) {
|
||||
rect = faces[i];
|
||||
context.strokeStyle = '#a64ceb';
|
||||
if (rect.width < 60) return
|
||||
context.strokeRect(rect.x - 10, rect.y - 40, rect.width + 10, rect.height + 40);
|
||||
// context.font = '11px Helvetica';
|
||||
// context.fillStyle = "#fff";
|
||||
// context.fillText('x: ' + rect.x + 'px', rect.x + rect.width + 5, rect.y + 11);
|
||||
// context.fillText('y: ' + rect.y + 'px', rect.x + rect.width + 5, rect.y + 22);
|
||||
}
|
||||
}
|
||||
|
||||
// 截图功能
|
||||
document.getElementById('captureBtn').addEventListener('click', () => {
|
||||
if (!stream) return alert('请先开启摄像头');
|
||||
|
||||
if (captureBtn.innerHTML == '识别') {
|
||||
captureBtn.innerHTML = '重新识别'
|
||||
|
||||
canvas.style = "";
|
||||
video.style = "display:none";
|
||||
// 适配高DPI屏幕
|
||||
const scale = window.devicePixelRatio || 1;
|
||||
canvas.width = video.videoWidth * scale;
|
||||
canvas.height = video.videoHeight * scale;
|
||||
|
||||
context.scale(scale, scale);
|
||||
context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
|
||||
|
||||
// 生成图片下载链接
|
||||
const imgUrl = canvas.toDataURL('image/jpeg');
|
||||
|
||||
upLoadFile(imgUrl)
|
||||
//const link = document.createElement('a');
|
||||
//link.download = 'capture-' + new Date().getTime() + '.jpg';
|
||||
//link.href = imgUrl;
|
||||
//link.click();
|
||||
|
||||
} else {
|
||||
captureBtn.innerHTML = '识别'
|
||||
|
||||
video.style = "";
|
||||
canvas.style = "display:none";
|
||||
}
|
||||
|
||||
});
|
||||
function upLoadFile(data) {
|
||||
$.ajax({
|
||||
url: "FaceRecognition.aspx/UploadData",
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({
|
||||
data: data
|
||||
|
||||
}),
|
||||
success: function (data) {
|
||||
/*window.parent.backData(data.d)*/
|
||||
|
||||
if (data.d.indexOf('识别失败') !== -1) {
|
||||
//alert('识别失败请重新识别')
|
||||
is_stop = 0
|
||||
}
|
||||
else {
|
||||
// window.location.href = 'UsingMat.aspx?welderQRCode=' + data.d ;
|
||||
//window.open('UsingMat.aspx?welderCode=' + data.d , '_blank');
|
||||
//var activeWindow = F.getActiveWindow();
|
||||
//activeWindow.window.backData(data.d);
|
||||
//activeWindow.hide();
|
||||
window.location.href = 'UsingMat.aspx?welderCode=' + data.d;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// </自动生成>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace FineUIPro.Web.WeldMat.UsingSentMat
|
||||
namespace FineUIPro.Web.HJGL.MaterialManage
|
||||
{
|
||||
|
||||
|
||||
|
@ -40,41 +40,5 @@ namespace FineUIPro.Web.WeldMat.UsingSentMat
|
|||
/// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.HtmlControls.HtmlForm form1;
|
||||
|
||||
/// <summary>
|
||||
/// PageManager1 控件。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 自动生成的字段。
|
||||
/// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
|
||||
/// </remarks>
|
||||
protected global::FineUIPro.PageManager PageManager1;
|
||||
|
||||
/// <summary>
|
||||
/// Panel1 控件。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 自动生成的字段。
|
||||
/// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
|
||||
/// </remarks>
|
||||
protected global::FineUIPro.Panel Panel1;
|
||||
|
||||
/// <summary>
|
||||
/// panelCenterRegion 控件。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 自动生成的字段。
|
||||
/// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
|
||||
/// </remarks>
|
||||
protected global::FineUIPro.Panel panelCenterRegion;
|
||||
|
||||
/// <summary>
|
||||
/// ContentPanel1 控件。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 自动生成的字段。
|
||||
/// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。
|
||||
/// </remarks>
|
||||
protected global::FineUIPro.ContentPanel ContentPanel1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,6 +98,14 @@
|
|||
}
|
||||
|
||||
function welderFaceLogin() {
|
||||
window.location.href = 'FaceRecognition.aspx';
|
||||
// window.location.href = 'FaceRecognition.aspx';
|
||||
var node = {
|
||||
|
||||
iframeUrl: './WeldMat/UsingSentMat/FaceRecognition.aspx',
|
||||
title: "人脸识别",
|
||||
id: "UsingMat" + Date.now().toString()
|
||||
};
|
||||
|
||||
window.parent.addExampleTab(node);
|
||||
}
|
||||
</script>
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,532 @@
|
|||
/*
|
||||
Copyright (c) 2010, Liu Liu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the authors nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
*/
|
||||
|
||||
/* Added by Jay Salvat: Get Script Path */
|
||||
try {
|
||||
var getScriptPath = function () {
|
||||
"use strict";
|
||||
|
||||
var scripts = document.getElementsByTagName('script');
|
||||
|
||||
for (var i = 0; i < scripts.length; i++) {
|
||||
if (scripts[i].src.match(/(jquery\.facedetection(\.min)?\.js)|\/ccv\.js/)) {
|
||||
return scripts[i].src;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var scriptPath = getScriptPath();
|
||||
} catch (e) { }
|
||||
/* End Jay Salvat */
|
||||
|
||||
if (parallable === undefined) {
|
||||
var parallable = function (file, funct) {
|
||||
"use strict";
|
||||
|
||||
parallable.core[funct.toString()] = funct().core;
|
||||
|
||||
return function () {
|
||||
var i;
|
||||
var async, worker_num, params;
|
||||
if (arguments.length > 1) {
|
||||
async = arguments[arguments.length - 2];
|
||||
worker_num = arguments[arguments.length - 1];
|
||||
params = new Array(arguments.length - 2);
|
||||
for (i = 0; i < arguments.length - 2; i++)
|
||||
params[i] = arguments[i];
|
||||
} else {
|
||||
async = arguments[0].async;
|
||||
worker_num = arguments[0].worker;
|
||||
params = arguments[0];
|
||||
delete params["async"];
|
||||
delete params["worker"];
|
||||
params = [params];
|
||||
}
|
||||
var scope = { "shared": {} };
|
||||
var ctrl = funct.apply(scope, params);
|
||||
if (async) {
|
||||
return function (complete, error) {
|
||||
var executed = 0;
|
||||
var outputs = new Array(worker_num);
|
||||
var inputs = ctrl.pre.apply(scope, [worker_num]);
|
||||
/* sanitize scope shared because for Chrome/WebKit, worker only support JSONable data */
|
||||
for (i in scope.shared)
|
||||
/* delete function, if any */
|
||||
if (typeof scope.shared[i] == "function")
|
||||
delete scope.shared[i];
|
||||
/* delete DOM object, if any */
|
||||
else if (scope.shared[i].tagName !== undefined)
|
||||
delete scope.shared[i];
|
||||
for (i = 0; i < worker_num; i++) {
|
||||
var worker = new Worker(file);
|
||||
worker.onmessage = (function (i) {
|
||||
return function (event) {
|
||||
outputs[i] = (typeof event.data == "string") ? JSON.parse(event.data) : event.data;
|
||||
executed++;
|
||||
if (executed == worker_num)
|
||||
complete(ctrl.post.apply(scope, [outputs]));
|
||||
}
|
||||
})(i);
|
||||
var msg = {
|
||||
"input": inputs[i],
|
||||
"name": funct.toString(),
|
||||
"shared": scope.shared,
|
||||
"id": i,
|
||||
"worker": params.worker_num,
|
||||
"from": "jquery.facedetection"
|
||||
};
|
||||
try {
|
||||
worker.postMessage(msg);
|
||||
} catch (e) {
|
||||
worker.postMessage(JSON.stringify(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ctrl.post.apply(scope, [[ctrl.core.apply(scope, [ctrl.pre.apply(scope, [1])[0], 0, 1])]]);
|
||||
}
|
||||
}
|
||||
};
|
||||
parallable.core = {};
|
||||
}
|
||||
|
||||
function get_named_arguments(params, names) {
|
||||
if (params.length > 1) {
|
||||
var new_params = {};
|
||||
for (var i = 0; i < names.length; i++)
|
||||
new_params[names[i]] = params[i];
|
||||
return new_params;
|
||||
} else if (params.length == 1) {
|
||||
return params[0];
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
var ccv = {
|
||||
pre: function (image) {
|
||||
if (image.tagName.toLowerCase() == "img") {
|
||||
var canvas = document.createElement("canvas");
|
||||
document.body.appendChild(image);
|
||||
canvas.width = image.offsetWidth;
|
||||
canvas.style.width = image.offsetWidth.toString() + "px";
|
||||
canvas.height = image.offsetHeight;
|
||||
canvas.style.height = image.offsetHeight.toString() + "px";
|
||||
document.body.removeChild(image);
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(image, 0, 0);
|
||||
return canvas;
|
||||
}
|
||||
return image;
|
||||
},
|
||||
|
||||
grayscale: function (canvas) {
|
||||
var ctx = canvas.getContext("2d");
|
||||
console.log(canvas.width)
|
||||
console.log(canvas.height)
|
||||
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
var data = imageData.data;
|
||||
var pix1, pix2, pix = canvas.width * canvas.height * 4;
|
||||
while (pix > 0)
|
||||
data[pix -= 4] = data[pix1 = pix + 1] = data[pix2 = pix + 2] = (data[pix] * 0.3 + data[pix1] * 0.59 + data[pix2] * 0.11);
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
return canvas;
|
||||
},
|
||||
|
||||
array_group: function (seq, gfunc) {
|
||||
var i, j;
|
||||
var node = new Array(seq.length);
|
||||
for (i = 0; i < seq.length; i++)
|
||||
node[i] = {
|
||||
"parent": -1,
|
||||
"element": seq[i],
|
||||
"rank": 0
|
||||
};
|
||||
for (i = 0; i < seq.length; i++) {
|
||||
if (!node[i].element)
|
||||
continue;
|
||||
var root = i;
|
||||
while (node[root].parent != -1)
|
||||
root = node[root].parent;
|
||||
for (j = 0; j < seq.length; j++) {
|
||||
if (i != j && node[j].element && gfunc(node[i].element, node[j].element)) {
|
||||
var root2 = j;
|
||||
|
||||
while (node[root2].parent != -1)
|
||||
root2 = node[root2].parent;
|
||||
|
||||
if (root2 != root) {
|
||||
if (node[root].rank > node[root2].rank)
|
||||
node[root2].parent = root;
|
||||
else {
|
||||
node[root].parent = root2;
|
||||
if (node[root].rank == node[root2].rank)
|
||||
node[root2].rank++;
|
||||
root = root2;
|
||||
}
|
||||
|
||||
/* compress path from node2 to the root: */
|
||||
var temp, node2 = j;
|
||||
while (node[node2].parent != -1) {
|
||||
temp = node2;
|
||||
node2 = node[node2].parent;
|
||||
node[temp].parent = root;
|
||||
}
|
||||
|
||||
/* compress path from node to the root: */
|
||||
node2 = i;
|
||||
while (node[node2].parent != -1) {
|
||||
temp = node2;
|
||||
node2 = node[node2].parent;
|
||||
node[temp].parent = root;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var idx = new Array(seq.length);
|
||||
var class_idx = 0;
|
||||
for (i = 0; i < seq.length; i++) {
|
||||
j = -1;
|
||||
var node1 = i;
|
||||
if (node[node1].element) {
|
||||
while (node[node1].parent != -1)
|
||||
node1 = node[node1].parent;
|
||||
if (node[node1].rank >= 0)
|
||||
node[node1].rank = ~class_idx++;
|
||||
j = ~node[node1].rank;
|
||||
}
|
||||
idx[i] = j;
|
||||
}
|
||||
return { "index": idx, "cat": class_idx };
|
||||
},
|
||||
|
||||
detect_objects: parallable(scriptPath, function (canvas, cascade, interval, min_neighbors) {
|
||||
if (this.shared !== undefined) {
|
||||
var params = get_named_arguments(arguments, ["canvas", "cascade", "interval", "min_neighbors"]);
|
||||
this.shared.canvas = params.canvas;
|
||||
this.shared.interval = params.interval;
|
||||
this.shared.min_neighbors = params.min_neighbors;
|
||||
this.shared.cascade = params.cascade;
|
||||
this.shared.scale = Math.pow(2, 1 / (params.interval + 1));
|
||||
this.shared.next = params.interval + 1;
|
||||
this.shared.scale_upto = Math.floor(Math.log(Math.min(params.canvas.width / params.cascade.width, params.canvas.height / params.cascade.height)) / Math.log(this.shared.scale));
|
||||
var i;
|
||||
for (i = 0; i < this.shared.cascade.stage_classifier.length; i++)
|
||||
this.shared.cascade.stage_classifier[i].orig_feature = this.shared.cascade.stage_classifier[i].feature;
|
||||
}
|
||||
|
||||
function pre(worker_num) {
|
||||
var canvas = this.shared.canvas;
|
||||
var interval = this.shared.interval;
|
||||
var scale = this.shared.scale;
|
||||
var next = this.shared.next;
|
||||
var scale_upto = this.shared.scale_upto;
|
||||
var pyr = new Array((scale_upto + next * 2) * 4);
|
||||
var ret = new Array((scale_upto + next * 2) * 4);
|
||||
pyr[0] = canvas;
|
||||
ret[0] = {
|
||||
"width": pyr[0].width,
|
||||
"height": pyr[0].height,
|
||||
"data": pyr[0].getContext("2d").getImageData(0, 0, pyr[0].width, pyr[0].height).data
|
||||
};
|
||||
var i;
|
||||
for (i = 1; i <= interval; i++) {
|
||||
pyr[i * 4] = document.createElement("canvas");
|
||||
pyr[i * 4].width = Math.floor(pyr[0].width / Math.pow(scale, i));
|
||||
pyr[i * 4].height = Math.floor(pyr[0].height / Math.pow(scale, i));
|
||||
pyr[i * 4].getContext("2d").drawImage(pyr[0], 0, 0, pyr[0].width, pyr[0].height, 0, 0, pyr[i * 4].width, pyr[i * 4].height);
|
||||
ret[i * 4] = {
|
||||
"width": pyr[i * 4].width,
|
||||
"height": pyr[i * 4].height,
|
||||
"data": pyr[i * 4].getContext("2d").getImageData(0, 0, pyr[i * 4].width, pyr[i * 4].height).data
|
||||
};
|
||||
}
|
||||
for (i = next; i < scale_upto + next * 2; i++) {
|
||||
pyr[i * 4] = document.createElement("canvas");
|
||||
pyr[i * 4].width = Math.floor(pyr[i * 4 - next * 4].width / 2);
|
||||
pyr[i * 4].height = Math.floor(pyr[i * 4 - next * 4].height / 2);
|
||||
pyr[i * 4].getContext("2d").drawImage(pyr[i * 4 - next * 4], 0, 0, pyr[i * 4 - next * 4].width, pyr[i * 4 - next * 4].height, 0, 0, pyr[i * 4].width, pyr[i * 4].height);
|
||||
ret[i * 4] = {
|
||||
"width": pyr[i * 4].width,
|
||||
"height": pyr[i * 4].height,
|
||||
"data": pyr[i * 4].getContext("2d").getImageData(0, 0, pyr[i * 4].width, pyr[i * 4].height).data
|
||||
};
|
||||
}
|
||||
for (i = next * 2; i < scale_upto + next * 2; i++) {
|
||||
pyr[i * 4 + 1] = document.createElement("canvas");
|
||||
pyr[i * 4 + 1].width = Math.floor(pyr[i * 4 - next * 4].width / 2);
|
||||
pyr[i * 4 + 1].height = Math.floor(pyr[i * 4 - next * 4].height / 2);
|
||||
pyr[i * 4 + 1].getContext("2d").drawImage(pyr[i * 4 - next * 4], 1, 0, pyr[i * 4 - next * 4].width - 1, pyr[i * 4 - next * 4].height, 0, 0, pyr[i * 4 + 1].width - 2, pyr[i * 4 + 1].height);
|
||||
ret[i * 4 + 1] = {
|
||||
"width": pyr[i * 4 + 1].width,
|
||||
"height": pyr[i * 4 + 1].height,
|
||||
"data": pyr[i * 4 + 1].getContext("2d").getImageData(0, 0, pyr[i * 4 + 1].width, pyr[i * 4 + 1].height).data
|
||||
};
|
||||
pyr[i * 4 + 2] = document.createElement("canvas");
|
||||
pyr[i * 4 + 2].width = Math.floor(pyr[i * 4 - next * 4].width / 2);
|
||||
pyr[i * 4 + 2].height = Math.floor(pyr[i * 4 - next * 4].height / 2);
|
||||
pyr[i * 4 + 2].getContext("2d").drawImage(pyr[i * 4 - next * 4], 0, 1, pyr[i * 4 - next * 4].width, pyr[i * 4 - next * 4].height - 1, 0, 0, pyr[i * 4 + 2].width, pyr[i * 4 + 2].height - 2);
|
||||
ret[i * 4 + 2] = {
|
||||
"width": pyr[i * 4 + 2].width,
|
||||
"height": pyr[i * 4 + 2].height,
|
||||
"data": pyr[i * 4 + 2].getContext("2d").getImageData(0, 0, pyr[i * 4 + 2].width, pyr[i * 4 + 2].height).data
|
||||
};
|
||||
pyr[i * 4 + 3] = document.createElement("canvas");
|
||||
pyr[i * 4 + 3].width = Math.floor(pyr[i * 4 - next * 4].width / 2);
|
||||
pyr[i * 4 + 3].height = Math.floor(pyr[i * 4 - next * 4].height / 2);
|
||||
pyr[i * 4 + 3].getContext("2d").drawImage(pyr[i * 4 - next * 4], 1, 1, pyr[i * 4 - next * 4].width - 1, pyr[i * 4 - next * 4].height - 1, 0, 0, pyr[i * 4 + 3].width - 2, pyr[i * 4 + 3].height - 2);
|
||||
ret[i * 4 + 3] = {
|
||||
"width": pyr[i * 4 + 3].width,
|
||||
"height": pyr[i * 4 + 3].height,
|
||||
"data": pyr[i * 4 + 3].getContext("2d").getImageData(0, 0, pyr[i * 4 + 3].width, pyr[i * 4 + 3].height).data
|
||||
};
|
||||
}
|
||||
return [ret];
|
||||
};
|
||||
|
||||
function core(pyr, id, worker_num) {
|
||||
var cascade = this.shared.cascade;
|
||||
var interval = this.shared.interval;
|
||||
var scale = this.shared.scale;
|
||||
var next = this.shared.next;
|
||||
var scale_upto = this.shared.scale_upto;
|
||||
var i, j, k, x, y, q;
|
||||
var scale_x = 1, scale_y = 1;
|
||||
var dx = [0, 1, 0, 1];
|
||||
var dy = [0, 0, 1, 1];
|
||||
var seq = [];
|
||||
for (i = 0; i < scale_upto; i++) {
|
||||
var qw = pyr[i * 4 + next * 8].width - Math.floor(cascade.width / 4);
|
||||
var qh = pyr[i * 4 + next * 8].height - Math.floor(cascade.height / 4);
|
||||
var step = [pyr[i * 4].width * 4, pyr[i * 4 + next * 4].width * 4, pyr[i * 4 + next * 8].width * 4];
|
||||
var paddings = [pyr[i * 4].width * 16 - qw * 16,
|
||||
pyr[i * 4 + next * 4].width * 8 - qw * 8,
|
||||
pyr[i * 4 + next * 8].width * 4 - qw * 4];
|
||||
for (j = 0; j < cascade.stage_classifier.length; j++) {
|
||||
var orig_feature = cascade.stage_classifier[j].orig_feature;
|
||||
var feature = cascade.stage_classifier[j].feature = new Array(cascade.stage_classifier[j].count);
|
||||
for (k = 0; k < cascade.stage_classifier[j].count; k++) {
|
||||
feature[k] = {
|
||||
"size": orig_feature[k].size,
|
||||
"px": new Array(orig_feature[k].size),
|
||||
"pz": new Array(orig_feature[k].size),
|
||||
"nx": new Array(orig_feature[k].size),
|
||||
"nz": new Array(orig_feature[k].size)
|
||||
};
|
||||
for (q = 0; q < orig_feature[k].size; q++) {
|
||||
feature[k].px[q] = orig_feature[k].px[q] * 4 + orig_feature[k].py[q] * step[orig_feature[k].pz[q]];
|
||||
feature[k].pz[q] = orig_feature[k].pz[q];
|
||||
feature[k].nx[q] = orig_feature[k].nx[q] * 4 + orig_feature[k].ny[q] * step[orig_feature[k].nz[q]];
|
||||
feature[k].nz[q] = orig_feature[k].nz[q];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (q = 0; q < 4; q++) {
|
||||
var u8 = [pyr[i * 4].data, pyr[i * 4 + next * 4].data, pyr[i * 4 + next * 8 + q].data];
|
||||
var u8o = [dx[q] * 8 + dy[q] * pyr[i * 4].width * 8, dx[q] * 4 + dy[q] * pyr[i * 4 + next * 4].width * 4, 0];
|
||||
for (y = 0; y < qh; y++) {
|
||||
for (x = 0; x < qw; x++) {
|
||||
var sum = 0;
|
||||
var flag = true;
|
||||
for (j = 0; j < cascade.stage_classifier.length; j++) {
|
||||
sum = 0;
|
||||
var alpha = cascade.stage_classifier[j].alpha;
|
||||
var feature = cascade.stage_classifier[j].feature;
|
||||
for (k = 0; k < cascade.stage_classifier[j].count; k++) {
|
||||
var feature_k = feature[k];
|
||||
var p, pmin = u8[feature_k.pz[0]][u8o[feature_k.pz[0]] + feature_k.px[0]];
|
||||
var n, nmax = u8[feature_k.nz[0]][u8o[feature_k.nz[0]] + feature_k.nx[0]];
|
||||
if (pmin <= nmax) {
|
||||
sum += alpha[k * 2];
|
||||
} else {
|
||||
var f, shortcut = true;
|
||||
for (f = 0; f < feature_k.size; f++) {
|
||||
if (feature_k.pz[f] >= 0) {
|
||||
p = u8[feature_k.pz[f]][u8o[feature_k.pz[f]] + feature_k.px[f]];
|
||||
if (p < pmin) {
|
||||
if (p <= nmax) {
|
||||
shortcut = false;
|
||||
break;
|
||||
}
|
||||
pmin = p;
|
||||
}
|
||||
}
|
||||
if (feature_k.nz[f] >= 0) {
|
||||
n = u8[feature_k.nz[f]][u8o[feature_k.nz[f]] + feature_k.nx[f]];
|
||||
if (n > nmax) {
|
||||
if (pmin <= n) {
|
||||
shortcut = false;
|
||||
break;
|
||||
}
|
||||
nmax = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
sum += (shortcut) ? alpha[k * 2 + 1] : alpha[k * 2];
|
||||
}
|
||||
}
|
||||
if (sum < cascade.stage_classifier[j].threshold) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
seq.push({
|
||||
"x": (x * 4 + dx[q] * 2) * scale_x,
|
||||
"y": (y * 4 + dy[q] * 2) * scale_y,
|
||||
"width": cascade.width * scale_x,
|
||||
"height": cascade.height * scale_y,
|
||||
"neighbor": 1,
|
||||
"confidence": sum
|
||||
});
|
||||
}
|
||||
u8o[0] += 16;
|
||||
u8o[1] += 8;
|
||||
u8o[2] += 4;
|
||||
}
|
||||
u8o[0] += paddings[0];
|
||||
u8o[1] += paddings[1];
|
||||
u8o[2] += paddings[2];
|
||||
}
|
||||
}
|
||||
scale_x *= scale;
|
||||
scale_y *= scale;
|
||||
}
|
||||
return seq;
|
||||
};
|
||||
|
||||
function post(seq) {
|
||||
var min_neighbors = this.shared.min_neighbors;
|
||||
var cascade = this.shared.cascade;
|
||||
var interval = this.shared.interval;
|
||||
var scale = this.shared.scale;
|
||||
var next = this.shared.next;
|
||||
var scale_upto = this.shared.scale_upto;
|
||||
var i, j;
|
||||
for (i = 0; i < cascade.stage_classifier.length; i++)
|
||||
cascade.stage_classifier[i].feature = cascade.stage_classifier[i].orig_feature;
|
||||
seq = seq[0];
|
||||
if (!(min_neighbors > 0))
|
||||
return seq;
|
||||
else {
|
||||
var result = ccv.array_group(seq, function (r1, r2) {
|
||||
var distance = Math.floor(r1.width * 0.25 + 0.5);
|
||||
|
||||
return r2.x <= r1.x + distance &&
|
||||
r2.x >= r1.x - distance &&
|
||||
r2.y <= r1.y + distance &&
|
||||
r2.y >= r1.y - distance &&
|
||||
r2.width <= Math.floor(r1.width * 1.5 + 0.5) &&
|
||||
Math.floor(r2.width * 1.5 + 0.5) >= r1.width;
|
||||
});
|
||||
var ncomp = result.cat;
|
||||
var idx_seq = result.index;
|
||||
var comps = new Array(ncomp + 1);
|
||||
for (i = 0; i < comps.length; i++)
|
||||
comps[i] = {
|
||||
"neighbors": 0,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 0,
|
||||
"height": 0,
|
||||
"confidence": 0
|
||||
};
|
||||
|
||||
// count number of neighbors
|
||||
for (i = 0; i < seq.length; i++) {
|
||||
var r1 = seq[i];
|
||||
var idx = idx_seq[i];
|
||||
|
||||
if (comps[idx].neighbors == 0)
|
||||
comps[idx].confidence = r1.confidence;
|
||||
|
||||
++comps[idx].neighbors;
|
||||
|
||||
comps[idx].x += r1.x;
|
||||
comps[idx].y += r1.y;
|
||||
comps[idx].width += r1.width;
|
||||
comps[idx].height += r1.height;
|
||||
comps[idx].confidence = Math.max(comps[idx].confidence, r1.confidence);
|
||||
}
|
||||
|
||||
var seq2 = [];
|
||||
// calculate average bounding box
|
||||
for (i = 0; i < ncomp; i++) {
|
||||
var n = comps[i].neighbors;
|
||||
if (n >= min_neighbors)
|
||||
seq2.push({
|
||||
"x": (comps[i].x * 2 + n) / (2 * n),
|
||||
"y": (comps[i].y * 2 + n) / (2 * n),
|
||||
"width": (comps[i].width * 2 + n) / (2 * n),
|
||||
"height": (comps[i].height * 2 + n) / (2 * n),
|
||||
"neighbors": comps[i].neighbors,
|
||||
"confidence": comps[i].confidence
|
||||
});
|
||||
}
|
||||
|
||||
var result_seq = [];
|
||||
// filter out small face rectangles inside large face rectangles
|
||||
for (i = 0; i < seq2.length; i++) {
|
||||
var r1 = seq2[i];
|
||||
var flag = true;
|
||||
for (j = 0; j < seq2.length; j++) {
|
||||
var r2 = seq2[j];
|
||||
var distance = Math.floor(r2.width * 0.25 + 0.5);
|
||||
|
||||
if (i != j &&
|
||||
r1.x >= r2.x - distance &&
|
||||
r1.y >= r2.y - distance &&
|
||||
r1.x + r1.width <= r2.x + r2.width + distance &&
|
||||
r1.y + r1.height <= r2.y + r2.height + distance &&
|
||||
(r2.neighbors > Math.max(3, r1.neighbors) || r1.neighbors < 3)) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag)
|
||||
result_seq.push(r1);
|
||||
}
|
||||
return result_seq;
|
||||
}
|
||||
};
|
||||
return { "pre": pre, "core": core, "post": post };
|
||||
})
|
||||
}
|
||||
|
||||
// Monkey patch to avoid overriding window's onmessage handler.
|
||||
var originalOnMessage = window.onmessage || function () { };
|
||||
onmessage = function (event) {
|
||||
var data;
|
||||
try {
|
||||
data = (typeof event.data == "string") ? JSON.parse(event.data) : event.data;
|
||||
if (data.type === "jquery.facedetection") {
|
||||
// This is the event that is intended for jquery.facedetection
|
||||
var scope = { "shared": data.shared };
|
||||
var result = parallable.core[data.name].apply(scope, [data.input, data.id, data.worker]);
|
||||
try {
|
||||
postMessage(result);
|
||||
} catch (e) {
|
||||
postMessage(JSON.stringify(result));
|
||||
}
|
||||
} else {
|
||||
// Nope. This is not the event that should be handled by jquery.facedetection
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
originalOnMessage.apply(window, args);
|
||||
}
|
||||
} catch (e) {
|
||||
// `event.data` is string, but too bad it is not in JSON format.
|
||||
// so just pass it to window's original onmessage handler
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
originalOnMessage.apply(window, args);
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
FaceDetection jQuery Plugin
|
||||
Copyright (c) 2016 Jay Salvat
|
||||
*/
|
||||
|
||||
/* global $, ccv, cascade */
|
||||
|
||||
$.fn.faceDetection = function (settingsOrCallback) {
|
||||
"use strict";
|
||||
|
||||
var time;
|
||||
|
||||
var options = {
|
||||
interval: 4,
|
||||
minNeighbors: 1,
|
||||
grayscale: true,
|
||||
confidence: null,
|
||||
async: false,
|
||||
complete: function () { }, // (faces)
|
||||
error: function () { } // (code, message)
|
||||
};
|
||||
|
||||
if ($.isFunction(settingsOrCallback)) {
|
||||
options.complete = settingsOrCallback;
|
||||
} else {
|
||||
$.extend(options, settingsOrCallback);
|
||||
}
|
||||
|
||||
return this.each(function () {
|
||||
var $$ = $(this),
|
||||
offset = $$.offset(),
|
||||
position = $$.position(),
|
||||
scaleX = ($$.width() / (this.naturalWidth || this.videoWidth)) || 1,
|
||||
scaleY = ($$.height() / (this.naturalHeight || this.videoHeight)) || 1;
|
||||
|
||||
if (!$$.is('img, video, canvas')) {
|
||||
options.error.apply($$, [1, 'Face detection is possible on images, videos and canvas only.']);
|
||||
options.complete.apply($$, [[]]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function detect() {
|
||||
var source, canvas;
|
||||
|
||||
time = new Date().getTime();
|
||||
|
||||
if ($$.is('img')) {
|
||||
source = new Image();
|
||||
source.src = $$.attr('src');
|
||||
source.crossOrigin = $$.attr('crossorigin');
|
||||
canvas = ccv.pre(source);
|
||||
} else if ($$.is('video') || $$.is('canvas')) {
|
||||
var copy, context;
|
||||
|
||||
source = $$[0];
|
||||
|
||||
copy = document.createElement('canvas');
|
||||
copy.setAttribute('width', source.videoWidth || source.width);
|
||||
copy.setAttribute('height', source.videoHeight || source.height);
|
||||
|
||||
context = copy.getContext("2d");
|
||||
context.drawImage(source, 0, 0);
|
||||
|
||||
canvas = ccv.pre(copy);
|
||||
}
|
||||
|
||||
if (options.grayscale) {
|
||||
canvas = ccv.grayscale(canvas);
|
||||
}
|
||||
|
||||
try {
|
||||
if (options.async && window.Worker) {
|
||||
ccv.detect_objects({
|
||||
"canvas": canvas,
|
||||
"cascade": cascade,
|
||||
"interval": options.interval,
|
||||
"min_neighbors": options.minNeighbors,
|
||||
"worker": 1,
|
||||
"async": true
|
||||
})(done);
|
||||
} else {
|
||||
done(ccv.detect_objects({
|
||||
"canvas": canvas,
|
||||
"cascade": cascade,
|
||||
"interval": options.interval,
|
||||
"min_neighbors": options.minNeighbors
|
||||
}));
|
||||
}
|
||||
} catch (e) {
|
||||
options.error.apply($$, [2, e.message]);
|
||||
options.complete.apply($$, [false]);
|
||||
}
|
||||
}
|
||||
|
||||
function done(faces) {
|
||||
var n = faces.length,
|
||||
data = [];
|
||||
|
||||
for (var i = 0; i < n; ++i) {
|
||||
if (options.confidence !== null && faces[i].confidence <= options.confidence) {
|
||||
continue;
|
||||
}
|
||||
|
||||
faces[i].positionX = position.left + faces[i].x;
|
||||
faces[i].positionY = position.top + faces[i].y;
|
||||
faces[i].offsetX = offset.left + faces[i].x;
|
||||
faces[i].offsetY = offset.top + faces[i].y;
|
||||
faces[i].scaleX = scaleX;
|
||||
faces[i].scaleY = scaleY;
|
||||
|
||||
data.push(faces[i]);
|
||||
}
|
||||
|
||||
data.time = new Date().getTime() - time;
|
||||
|
||||
options.complete.apply($$, [data]);
|
||||
}
|
||||
|
||||
return detect();
|
||||
});
|
||||
};
|
Loading…
Reference in New Issue