<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="a.css">
<title>Face Detector</title>
</head>
<body>
<div id="container">
<div id="header"><div class="title">A Not-so-slow JavaScript Face Detector</div><div class="subtitle">For Source Code, Visit <a href="https://github.com/liuliu/ccv/tree/unstable/js">Github</a>; For More Discussions, Visit <a href="http://liuliu.me">liuliu.me</a>; Or Paste Your Photo at <a href="http://imgur.com">imgur.com</a></div></div>
<div id="content">
<div id="urlbox"><div id="detect"><input id="url-detect" type="button" value="Detect"></div><div class="paragraph">URL: </div><div id="url"><div id="url-hint">Paste an image URL here</div><input class="no-border" id="url-image" type="text"></div></div>
<div id="stats">Stats: Web Worker - <a id="webworker-switch" href="#">Off</a>, Load Time: <span id="load-time">None</span>, Detection Time: <span id="detection-time">None</span>, <span id="num-faces">?</span> Faces Found at <span id="image-dim">?x?</span></div>
<div id="viewport"><div id="view-hint">Drag & Drop<br>or <u>Select</u> To</div><div id="view-horz"></div><div id="view-vtic"></div><div><canvas id="output"></canvas><input type="file" id="file-selector" accept="image/*"></div>
</div>
<div id="footer"><div><a href="http://facebook.com/liu.ll">Liu Liu</a> @ <a href="http://liuliu.me">liuliu.me</a></div></div>
</div>
<script type="text/javascript" src="ccv.js"></script>
<script type="text/javascript" src="face.js"></script>
<script type="text/javascript">
/* Modernizr 2.0.6 (Custom Build) | MIT & BSD
* Build: http://www.modernizr.com/download/#-borderradius-opacity-rgba-canvas-draganddrop-webworkers-addtest-testprop-testallprops-hasevent-prefixes-domprefixes-file_api
*/
;window.Modernizr=function(a,b,c){function B(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1),d=(a+" "+n.join(c+" ")+c).split(" ");return A(d,b)}function A(a,b){for(var d in a)if(j[a[d]]!==c)return b=="pfx"?a[d]:!0;return!1}function z(a,b){return!!~(""+a).indexOf(b)}function y(a,b){return typeof a===b}function x(a,b){return w(m.join(a+";")+(b||""))}function w(a){j.cssText=a}var d="2.0.6",e={},f=b.documentElement,g=b.head||b.getElementsByTagName("head")[0],h="modernizr",i=b.createElement(h),j=i.style,k,l=Object.prototype.toString,m=" -webkit- -moz- -o- -ms- -khtml- ".split(" "),n="Webkit Moz O ms Khtml".split(" "),o={},p={},q={},r=[],s=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=y(e[d],"function"),y(e[d],c)||(e[d]=c),e.removeAttribute(d))),e=null;return f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),t,u={}.hasOwnProperty,v;!y(u,c)&&!y(u.call,c)?v=function(a,b){return u.call(a,b)}:v=function(a,b){return b in a&&y(a.constructor.prototype[b],c)},o.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},o.draganddrop=function(){return s("dragstart")&&s("drop")},o.rgba=function(){w("background-color:rgba(150,255,150,.5)");return z(j.backgroundColor,"rgba")},o.borderradius=function(){return B("borderRadius")},o.opacity=function(){x("opacity:.55");return/^0.55$/.test(j.opacity)},o.webworkers=function(){return!!a.Worker};for(var C in o)v(o,C)&&(t=C.toLowerCase(),e[t]=o[C](),r.push((e[t]?"":"no-")+t));e.addTest=function(a,b){if(typeof a=="object")for(var d in a)v(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return;b=typeof b=="boolean"?b:!!b(),f.className+=" "+(b?"":"no-")+a,e[a]=b}return e},w(""),i=k=null,e._version=d,e._prefixes=m,e._domPrefixes=n,e.hasEvent=s,e.testProp=function(a){return A([a])},e.testAllProps=B;return e}(this,this.document),Modernizr.addTest("file",function(){return!!(window.File&&window.FileList&&window.FileReader)});
agent = (function( ua ) {
ua = ua.toLowerCase();
rwebkit = /(webkit)[ \/]([\w.]+)/;
ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/;
rmsie = /(msie) ([\w.]+)/;
rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/;
var match = rwebkit.exec( ua ) ||
ropera.exec( ua ) ||
rmsie.exec( ua ) ||
ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
[];
return { browser: match[1] || "", version: match[2] || "0" };
})(navigator.userAgent);
var async = false;
if (Modernizr.webworkers) {
async = (agent.browser == "mozilla");
document.getElementById("webworker-switch").innerHTML = (async) ? "On" : "Off";
document.getElementById("webworker-switch").addEventListener("click", function (e) {
if (async) {
async = false;
document.getElementById("webworker-switch").innerHTML = "Off";
} else {
async = true;
document.getElementById("webworker-switch").innerHTML = "On";
}
});
}
function getImageDim(image) {
var result = {};
document.body.appendChild(image);
result['width'] = image.offsetWidth;
result['height'] = image.offsetHeight;
document.body.removeChild(image);
return result;
}
function detectNewImage(src, async) {
document.getElementById("load-time").innerHTML = "Measuring ...";
document.getElementById("detection-time").innerHTML = "None";
document.getElementById("num-faces").innerHTML = "?";
document.getElementById("image-dim").innerHTML = "?x?";
var elapsed_time = (new Date()).getTime();
var image = new Image();
var canvas = document.getElementById("output");
var ctx = canvas.getContext("2d");
image.onload = function () {
/* load image, and draw it to canvas */
document.getElementById("load-time").innerHTML = Math.round((new Date()).getTime() - elapsed_time).toString() + "ms";
document.getElementById("detection-time").innerHTML = "Measuring ...";
var dim = getImageDim(image);
document.getElementById("image-dim").innerHTML = dim.width.toString() + "x" + dim.height.toString();
var boundingWidth = document.getElementById("content").offsetWidth - 4;
var boundingHeight = window.innerHeight - (document.getElementById("header").offsetHeight + document.getElementById("footer").offsetHeight + document.getElementById("urlbox").offsetHeight + document.getElementById("stats").offsetHeight) - 120;
var viewport = document.getElementById("viewport");
var newWidth = dim.width, newHeight = dim.height, scale = 1;
if (dim.width * boundingHeight > boundingWidth * dim.height) {
newWidth = boundingWidth;
newHeight = boundingWidth * dim.height / dim.width;
scale = newWidth / dim.width;
} else {
newHeight = boundingHeight;
newWidth = boundingHeight * dim.width / dim.height;
scale = newHeight / dim.height;
}
viewport.style.width = newWidth.toString() + "px";
viewport.style.height = newHeight.toString() + "px";
canvas.width = newWidth;
canvas.style.width = newWidth.toString() + "px";
canvas.height = newHeight;
canvas.style.height = newHeight.toString() + "px";
ctx.drawImage(image, 0, 0, newWidth, newHeight);
elapsed_time = (new Date()).getTime();
function post(comp) {
document.getElementById("num-faces").innerHTML = comp.length.toString();
document.getElementById("detection-time").innerHTML = Math.round((new Date()).getTime() - elapsed_time).toString() + "ms";
ctx.lineWidth = 2;
ctx.strokeStyle = 'rgba(230,87,0,0.8)';
/* draw detected area */
for (var i = 0; i < comp.length; i++) {
ctx.beginPath();
ctx.arc((comp[i].x + comp[i].width * 0.5) * scale, (comp[i].y + comp[i].height * 0.5) * scale,
(comp[i].width + comp[i].height) * 0.25 * scale * 1.2, 0, Math.PI * 2);
ctx.stroke();
}
}
/* call main detect_objects function */
if (async) {
ccv.detect_objects({ "canvas" : ccv.grayscale(ccv.pre(image)),
"cascade" : cascade,
"interval" : 5,
"min_neighbors" : 1,
"async" : true,
"worker" : 1 })(post);
} else {
var comp = ccv.detect_objects({ "canvas" : ccv.grayscale(ccv.pre(image)),
"cascade" : cascade,
"interval" : 5,
"min_neighbors" : 1 });
post(comp);
}
};
image.src = src;
}
function handleLocalFile(file) {
if (file.type.match(/image.*/)) {
var reader = new FileReader();
reader.onload = function (e) {
detectNewImage(e.target.result, async);
};
reader.readAsDataURL(file);
}
}
document.getElementById("viewport").addEventListener("dragover", function (e) {
e.stopPropagation();
e.preventDefault();
document.getElementById("view-hint").style.zIndex =
document.getElementById("view-horz").style.zIndex =
document.getElementById("view-vtic").style.zIndex = "1000";
}, false);
if (agent.browser == "mozilla") {
document.getElementById("file-selector").style.display = "none";
document.getElementById("file-selector").addEventListener("click", function (e) {
e.stopPropagation();
e.preventDefault();
}, false);
document.getElementById("viewport").addEventListener("click", function (e) {
e.stopPropagation();
e.preventDefault();
document.getElementById("file-selector").click();
}, false);
}
document.getElementById("viewport").addEventListener("mouseover", function (e) {
document.getElementById("view-hint").style.zIndex =
document.getElementById("view-horz").style.zIndex =
document.getElementById("view-vtic").style.zIndex = "1000";
});
document.getElementById("viewport").addEventListener("mouseout", function (e) {
document.getElementById("view-hint").style.zIndex =
document.getElementById("view-horz").style.zIndex =
document.getElementById("view-vtic").style.zIndex = "0";
});
document.getElementById("file-selector").addEventListener("change", function (e) {
var files = this.files;
if (files.length)
handleLocalFile(files[0]);
});
document.getElementById("viewport").addEventListener("drop", function (e) {
e.stopPropagation();
e.preventDefault();
var files = e.dataTransfer.files;
if (files.length)
handleLocalFile(files[0]);
document.getElementById("view-hint").style.zIndex =
document.getElementById("view-horz").style.zIndex =
document.getElementById("view-vtic").style.zIndex = "0";
}, false);
document.getElementById("url-image").addEventListener("focus", function (e) {
document.getElementById("url-hint").style.visibility = "hidden";
});
document.getElementById("url-image").addEventListener("blur", function (e) {
if (document.getElementById("url-image").value.length <= 0)
document.getElementById("url-hint").style.visibility = "visible";
});
document.getElementById("url-detect").addEventListener("click", function(e) {
var url = document.getElementById("url-image").value;
if (url.length > 0) {
window.location.hash = "#" + encodeURIComponent(url);
detectNewImage("loader.php?src=" + encodeURIComponent((url.substr(0, 7).toLowerCase() == "http://") ? url : "http://" + url), async);
}
});
var url = window.location.hash.substr(1);
if (url.length > 7) {
document.getElementById("url-image").value = decodeURIComponent(url);
document.getElementById("url-hint").style.visibility = "hidden";
}
</script>