UNPKG

clmtrackr

Version:

Javascript library for precise tracking of facial features via Constrained Local Models

504 lines (457 loc) 17.6 kB
<!doctype html> <html lang="en"> <head> <title>Face deformation</title> <meta charset="utf-8"> <link href="./styles/bootstrap.min.css" rel="stylesheet" type="text/css"> <style> @import url(https://fonts.googleapis.com/css?family=Lato:300italic,700italic,300,700); body { font-family: 'Lato'; background-color: #f0f0f0; margin: 0px auto; max-width: 1150px; } #overlay, #canvasel { position: absolute; top: 0px; left: 0px; -o-transform : scaleX(-1); -webkit-transform : scaleX(-1); transform : scaleX(-1); -ms-filter : fliph; /*IE*/ filter : fliph; /*IE*/ } #videoel { -o-transform : scaleX(-1); -webkit-transform : scaleX(-1); transform : scaleX(-1); -ms-filter : fliph; /*IE*/ filter : fliph; /*IE*/ } #container, #webglcontainer { position : relative; width : 370px; /*margin : 0px auto;*/ } #content { margin-top : 70px; margin-left : 100px; margin-right : 100px; max-width: 950px; } h2 { font-weight : 400; } .nogum { display : none; } .btn { font-family: 'Lato'; font-size: 16px; } .hide { display : none; } .nohide { display : block; } </style> <script> // getUserMedia only works over https in Chrome 47+, so we redirect to https. Also notify user if running from file. if (window.location.protocol == "file:") { alert("You seem to be running this example directly from a file. Note that these examples only work when served from a server or localhost due to canvas cross-domain restrictions."); } else if (window.location.hostname !== "localhost" && window.location.protocol !== "https:"){ window.location.protocol = "https"; } </script> <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-32642923-1']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> </head> <body> <script src="./js/libs/dat.gui.min.js"></script> <script src="./js/libs/utils.js"></script> <script src="./js/libs/webgl-utils.js"></script> <script src="../build/clmtrackr.js"></script> <script src="../models/model_pca_20_svm.js"></script> <script src="./js/libs/Stats.js"></script> <script src="./js/face_deformer.js"></script> <div id="content"> <h2>Face deformation</h2> <div id="container" class="nohide"> <video id="videoel" width="370" height="288" preload="auto" playsinline autoplay></video> <canvas id="canvasel" width="370" height="288" class="hide"></canvas> <canvas id="overlay" width="370" height="288"></canvas> </div> <div id="webglcontainer" class="hide"> <canvas id="webgl" width="306" height="342"></canvas> </div> <br/> <div id="buttons"> <input class="btn" type="button" value="wait, loading video" disabled="disabled" onclick="startVideo()" id="startbutton"></input> </div> <p id="score"></p> <div id="text"> <p>This is an example of face deformation using the javascript library <a href="https://github.com/auduno/clmtrackr"><em>clmtrackr</em></a>.</p> <p>Note that this example needs support for WebGL, and works best in Google Chrome.</p> <div id="gum" class="nohide"> <p>To try it out: <ol> <li>allow the page to your use webcamera</li> <li>make sure that your face is clearly visible in the video, and click start</li> <li>keep your head still until the model fits your face properly</li> <li>choose a preset or fiddle with the parameters on the right hand side, to deform the captured face</li> <ol> </p> </div> <div id="nogum" class="hide"> <p> There was some problem trying to capture your webcamera, please check that your browser supports WebRTC. Instead we'll use a static image. To try it out: <ol> <li>click start</li> <li>wait till the model fits the face</li> <li>choose a preset or fiddle with the parameters on the right hand side, to deform the captured face</li> </ol> </p> </div> </div> <a href="https://github.com/auduno/clmtrackr"><img style="position: absolute; top: 0; left: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_left_green_007200.png" alt="Fork me on GitHub"></a> <p id='gUMMessage'></p> <script> var vid = document.getElementById('videoel'); var vid_width = vid.width; var vid_height = vid.height; var overlay = document.getElementById('overlay'); var overlayCC = overlay.getContext('2d'); /*********** Setup of video/webcam and checking for webGL support *********/ function enablestart() { var startbutton = document.getElementById('startbutton'); startbutton.value = "start"; startbutton.disabled = null; } var insertAltVideo = function(video) { // insert alternate video if getUserMedia not available if (supports_video()) { if (supports_webm_video()) { video.src = "./media/franck.webm"; } else if (supports_h264_baseline_video()) { video.src = "./media/franck.mp4"; } else { return false; } fd.init(document.getElementById('webgl')); return true; } else return false; } var insertAltImage = function() { var canvas = document.getElementById('canvasel'); var cc = canvas.getContext('2d'); var img = new Image(); img.onload = function() { cc.drawImage(img, 0, 0, vid_width, vid_height); }; img.src = './media/franck_02221.jpg'; vid.className = "hide"; vid = canvas; canvas.className = "nohide"; var startbutton = document.getElementById('startbutton'); startbutton.onclick = function() { ctrack.start(vid); drawLoop(); } startbutton.value = "start"; startbutton.disabled = null; } function adjustVideoProportions() { // resize overlay and video if proportions are not 4:3 // keep same height, just change width var proportion = vid.videoWidth/vid.videoHeight; vid_width = Math.round(vid_height * proportion); vid.width = vid_width; overlay.width = vid_width; } // check whether browser supports webGL var webGLContext; var webGLTestCanvas = document.createElement('canvas'); if (window.WebGLRenderingContext) { webGLContext = webGLTestCanvas.getContext('webgl') || webGLTestCanvas.getContext('experimental-webgl'); if (!webGLContext || !webGLContext.getExtension('OES_texture_float')) { webGLContext = null; } } if (webGLContext == null) { alert("Your browser does not seem to support WebGL. Unfortunately this face deformation example depends on WebGL, so you'll have to try it in another browser. :("); } function gumSuccess( stream ) { // add camera stream if getUserMedia succeeded if ("srcObject" in vid) { vid.srcObject = stream; } else { vid.src = (window.URL && window.URL.createObjectURL(stream)); } vid.onloadedmetadata = function() { adjustVideoProportions(); fd.init(document.getElementById('webgl')); vid.play(); } vid.onresize = function() { adjustVideoProportions(); fd.init(document.getElementById('webgl')); if (trackingStarted) { ctrack.stop(); ctrack.reset(); ctrack.start(vid); } } } function gumFail() { // fall back to video if getUserMedia failed insertAltVideo(vid); document.getElementById('gum').className = "hide"; document.getElementById('nogum').className = "nohide"; alert("There was some problem trying to fetch video from your webcam, using a static image instead."); } navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; window.URL = window.URL || window.webkitURL || window.msURL || window.mozURL; // check for camerasupport if (navigator.mediaDevices) { navigator.mediaDevices.getUserMedia({video : true}).then(gumSuccess).catch(gumFail); } else if (navigator.getUserMedia) { navigator.getUserMedia({video : true}, gumSuccess, gumFail); } else { insertAltImage(); //insertAltVideo(vid); document.getElementById('gum').className = "hide"; document.getElementById('nogum').className = "nohide"; alert("Your browser does not seem to support getUserMedia, using a static image instead."); } vid.addEventListener('canplay', enablestart, false); /*********** Code for face tracking and face deformation *********/ var ctrack = new clm.tracker(); ctrack.init(pModel); var trackingStarted = false; function startVideo() { // start video vid.play(); // start tracking ctrack.start(vid); trackingStarted = true; //start drawing faces drawLoop(); } var positions; var fd = new faceDeformer(); function drawLoop() { // track in video positions = ctrack.getCurrentPosition(); overlayCC.clearRect(0, 0, vid_width, vid_height); if (positions) { ctrack.draw(overlay); } // hide buttons elem = document.getElementById('buttons'); elem.setAttribute('class', 'hide'); // show message var scoreel = document.getElementById('score'); scoreel.innerHTML = "Please keep head still while model fits"; var pn = ctrack.getConvergence(); if (pn < 1.8) { requestAnimFrame(setupFaceDeformation); scoreel.innerHTML = ""; if (vid.tagName == "VIDEO") { vid.pause(); } } else { requestAnimFrame(drawLoop); } } var ph, gui; var rotation = 0; var scale = 3; var xOffset = -10; var yOffset = 0; function setupFaceDeformation() { // draw face deformation model positions = ctrack.getCurrentPosition(); overlayCC.clearRect(0, 0, vid_width, vid_height); ctrack.draw(overlay); fd.load(vid, positions, pModel); fd.draw(positions); // hide video var elem = document.getElementById('container'); elem.setAttribute('class', 'hide'); // show facial deformation element elem = document.getElementById('webglcontainer'); elem.setAttribute('class', 'nohide'); // hide message element document.getElementById('score').setAttribute('class', 'hide'); // set up controls ph = new parameterHolder(); gui = new dat.GUI(); var guiSelect = gui.add(ph, 'presets', presets); var gui1 = gui.add(ph, 'param1', -50, 50).listen(); var gui2 = gui.add(ph, 'param2', -20, 20).listen(); var gui3 = gui.add(ph, 'param3', -20, 20).listen(); var gui4 = gui.add(ph, 'param4', -20, 20).listen(); var gui5 = gui.add(ph, 'param5', -20, 20).listen(); var gui6 = gui.add(ph, 'param6', -20, 20).listen(); var gui7 = gui.add(ph, 'param7', -20, 20).listen(); var gui8 = gui.add(ph, 'param8', -20, 20).listen(); var gui9 = gui.add(ph, 'param9', -20, 20).listen(); var gui10 = gui.add(ph, 'param10', -20, 20).listen(); var gui11 = gui.add(ph, 'param11', -20, 20).listen(); var gui12 = gui.add(ph, 'param12', -20, 20).listen(); var gui13 = gui.add(ph, 'param13', -20, 20).listen(); var gui14 = gui.add(ph, 'param14', -20, 20).listen(); var gui15 = gui.add(ph, 'param15', -20, 20).listen(); var gui16 = gui.add(ph, 'param16', -20, 20).listen(); var gui17 = gui.add(ph, 'param17', -20, 20).listen(); var gui18 = gui.add(ph, 'param18', -20, 20).listen(); var gui19 = gui.add(ph, 'param19', -20, 20).listen(); var gui20 = gui.add(ph, 'param20', -20, 20).listen(); var guiGrid = gui.add(ph, 'draw_grid', false); var guiFace = gui.add(ph, 'draw_face', true); gui1.onChange(drawDeformedFace); gui2.onChange(drawDeformedFace); gui3.onChange(drawDeformedFace); gui4.onChange(drawDeformedFace); gui5.onChange(drawDeformedFace); gui6.onChange(drawDeformedFace); gui7.onChange(drawDeformedFace); gui8.onChange(drawDeformedFace); gui9.onChange(drawDeformedFace); gui10.onChange(drawDeformedFace); gui11.onChange(drawDeformedFace); gui12.onChange(drawDeformedFace); gui13.onChange(drawDeformedFace); gui14.onChange(drawDeformedFace); gui15.onChange(drawDeformedFace); gui16.onChange(drawDeformedFace); gui17.onChange(drawDeformedFace); gui18.onChange(drawDeformedFace); gui19.onChange(drawDeformedFace); gui20.onChange(drawDeformedFace); guiSelect.onChange(switchDeformedFace); guiGrid.onChange(drawDeformedFace); guiFace.onChange(drawDeformedFace); drawDeformedFace(); } function drawDeformedFace() { // draw model var parameters = ctrack.getCurrentParameters(); parameters[0] = scale*Math.cos(rotation)-1; parameters[1] = scale*Math.sin(rotation); parameters[2] = xOffset; parameters[3] = yOffset; parameters[0+4] = ph.param1; parameters[1+4] = ph.param2; parameters[2+4] = ph.param3; parameters[3+4] = ph.param4; parameters[4+4] = ph.param5; parameters[5+4] = ph.param6; parameters[6+4] = ph.param7; parameters[7+4] = ph.param8; parameters[8+4] = ph.param9; parameters[9+4] = ph.param10; parameters[10+4] = ph.param11; parameters[11+4] = ph.param12; parameters[12+4] = ph.param13; parameters[13+4] = ph.param14; parameters[14+4] = ph.param15; parameters[15+4] = ph.param16; parameters[16+4] = ph.param17; parameters[17+4] = ph.param18; parameters[18+4] = ph.param19; parameters[19+4] = ph.param20; positions = fd.calculatePositions(parameters, true); fd.clear(); if (ph.draw_face) fd.draw(positions); if (ph.draw_grid) fd.drawGrid(positions); } function switchDeformedFace() { // draw model var parameters = ctrack.getCurrentParameters(); parameters[0] = scale*Math.cos(rotation)-1; parameters[1] = scale*Math.sin(rotation); parameters[2] = xOffset; parameters[3] = yOffset; var split = ph.presets.split(","); parameters[0+4] = ph.param1 = parseInt(split[0],10); parameters[1+4] = ph.param2 = parseInt(split[1],10); parameters[2+4] = ph.param3 = parseInt(split[2],10); parameters[3+4] = ph.param4 = parseInt(split[3],10); parameters[4+4] = ph.param5 = parseInt(split[4],10); parameters[5+4] = ph.param6 = parseInt(split[5],10); parameters[6+4] = ph.param7 = parseInt(split[6],10); parameters[7+4] = ph.param8 = parseInt(split[7],10); parameters[8+4] = ph.param9 = parseInt(split[8],10); parameters[9+4] = ph.param10 = parseInt(split[9],10); parameters[10+4] = ph.param11 = parseInt(split[10],10); parameters[11+4] = ph.param12 = parseInt(split[11],10); parameters[12+4] = ph.param13 = parseInt(split[12],10); parameters[13+4] = ph.param14 = parseInt(split[13],10); parameters[14+4] = ph.param15 = parseInt(split[14],10); parameters[15+4] = ph.param16 = parseInt(split[15],10); parameters[16+4] = ph.param17 = parseInt(split[16],10); parameters[17+4] = ph.param18 = parseInt(split[17],10); parameters[18+4] = ph.param19 = parseInt(split[18],10); parameters[19+4] = ph.param20 = parseInt(split[19],10); positions = fd.calculatePositions(parameters, true); if (ph.draw_face) fd.draw(positions); if (ph.draw_grid) fd.drawGrid(positions); } var parameterHolder = function() { this.param1 = 0.0001; this.param2 = 0.0001; this.param3 = 0.0001; this.param4 = 0.0001; this.param5 = 0.0001; this.param6 = 0.0001; this.param7 = 0.0001; this.param8 = 0.0001; this.param9 = 0.0001; this.param10 = 0.0001; this.param11 = 0.0001; this.param12 = 0.0001; this.param13 = 0.0001; this.param14 = 0.0001; this.param15 = 0.0001; this.param16 = 0.0001; this.param17 = 0.0001; this.param18 = 0.0001; this.param19 = 0.0001; this.param20 = 0.0001; this.presets = 0; this.draw_face = true; this.draw_grid = false; } var presets = { "default" : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "oi mate" : [0, 0, 13, 1.2, 0, -15, 0, 1, 8, -5, 0, 0, 0, 0, 0, 0, 0, 11.6, 0, -7], "unhappy" : [0, 0, 0, 0, 0, 0, 0, 0, 0, -13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "greek" : [0, 0, 0, 1.6, 0, -6, 0, 0, 0, -13, 0, 4.7, 1, 0, 11, -1, 8, 8, 0, 0], "cheery" : [0, 0, 0, 0, 10.7, 0, 16.8, 0, 0, -5, 0, -4, 13, 0, 0, 0, 0, 0, 0, 0], "luke" : [0, 0, -1.7, -8.7, -8, -4.8, 12.5, -1, 14.6, -11, 0, -2, -13, 0, 0, 0, 0, 7, 0, -3], "chum" : [0, 0, 13, 1.2, 0, 2.5, 0, 1, 16.8, -5, 0, 0, 0, 0, 0, 0, 0, 11.6, 0, -7], "disgust" : [-4, -14, 8, 2, 3, -5.6, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10, 0, -5], }; /*********** Code for stats **********/ stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.top = '0px'; document.getElementById('container').appendChild( stats.domElement ); document.addEventListener("clmtrackrIteration", function(event) { stats.update(); }, false); </script> </div> </body> </html>