Created
May 13, 2017 10:08
-
-
Save krhoyt/5b19e64fc58276a7c2f55ded7fd93a99 to your computer and use it in GitHub Desktop.
Tracking.JS Face Detection on Canvas
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>People</title> | |
<link href="/style/people.css" rel="stylesheet" type="text/css"> | |
</head> | |
<body> | |
<!-- Center the resulting content --> | |
<div id="layout"> | |
<canvas id="surface"></canvas> | |
</div> | |
<!-- File upload --> | |
<!-- Label acts as button --> | |
<!-- https://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way --> | |
<input id="uploader" type="file"> | |
<label for="uploader"></label> | |
<!-- Full size image --> | |
<!-- Not shown --> | |
<img id="holder"> | |
<!-- Face detection --> | |
<script src="/lib/tracking/tracking.js"></script> | |
<script src="/lib/tracking/data/face.js"></script> | |
<!-- Application --> | |
<script src="/script/people.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
background-color: #fafafa; | |
margin: 0; | |
padding: 0; | |
} | |
#holder { | |
position: absolute; | |
visibility: hidden; | |
} | |
#layout { | |
align-items: center; | |
display: flex; | |
flex-direction: column; | |
justify-content: center; | |
bottom: 0; | |
left: 0; | |
position: absolute; | |
right: 0; | |
top: 0; | |
} | |
#surface { | |
box-shadow: 0 2px 5px rgba( 0, 0, 0, 0.26 ); | |
opacity: 0; | |
} | |
#uploader { | |
height: 0; | |
opacity: 0; | |
position: absolute; | |
width: 0; | |
} | |
#uploader + label { | |
background-color: #d32f37; | |
background-image: url( /img/upload.svg ); | |
background-position: center; | |
background-repeat: no-repeat; | |
background-size: 24px; | |
border-radius: 56px; | |
bottom: 16px; | |
box-shadow: 0 2px 5px rgba( 0, 0, 0, 0.26 ); | |
height: 56px; | |
position: absolute; | |
right: 16px; | |
width: 56px; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class People { | |
// References and event hooks | |
constructor() { | |
this.layout = document.querySelector( '#layout' ); | |
this.layout.addEventListener( 'dragover', evt => this.doDragOver( evt ) ); | |
this.layout.addEventListener( 'drop', evt => this.doDragDrop( evt ) ); | |
this.holder = document.querySelector( '#holder' ); | |
this.holder.addEventListener( 'load', evt => this.doImage( evt ) ); | |
this.reader = new FileReader(); | |
this.reader.addEventListener( 'load', evt => this.doRead( evt ) ); | |
this.surface = document.querySelector( '#surface' ); | |
this.context = null; | |
this.uploader = document.querySelector( '#uploader' ); | |
this.uploader.addEventListener( 'change', evt => this.doUpload( evt ) ); | |
this.tracker = new tracking.ObjectTracker( 'face' ); | |
this.tracker.setStepSize( People.STEP_SIZE ); | |
this.tracker.on( 'track', evt => this.doTrack( evt ) ); | |
} | |
// Read file locally | |
process( file ) { | |
this.reader.readAsDataURL( file ); | |
} | |
// File dropped on viewport | |
// Prevent default behavior (view) | |
// Start analyzing | |
doDragDrop( evt ) { | |
evt.stopPropagation(); | |
evt.preventDefault(); | |
this.process( evt.dataTransfer.files[0] ); | |
} | |
// File is dragged over viewport | |
// Prevent default behavior (view) | |
// Enable drop | |
doDragOver( evt ) { | |
evt.stopPropagation(); | |
evt.preventDefault(); | |
evt.dataTransfer.dropEffect = 'copy'; | |
} | |
// Image element loaded | |
// Scale respective canvas surface | |
// Paint content | |
doImage( evt ) { | |
console.log( evt ); | |
console.log( 'Dimensions: ' + this.holder.clientWidth + 'x' + this.holder.clientHeight ); | |
// Original image ratio | |
// Used to keep dimensions consistent | |
// TODO: Account for images that already fit viewport | |
let ratio = this.holder.clientWidth / this.holder.clientHeight; | |
console.log( 'Ratio: ' + ratio ); | |
// Landscape or portrait | |
// Size canvas respectively | |
if( this.holder.clientWidth > this.holder.clientHeight ) { | |
console.log( 'Landscape.' ); | |
this.surface.width = Math.round( window.innerWidth * People.LANDSCAPE_SCALE ); | |
this.surface.height = this.surface.width / ratio; | |
} else { | |
console.log( 'Portrait.' ); | |
this.surface.height = Math.round( window.innerHeight * People.PORTRAIT_SCALE ); | |
this.surface.width = this.surface.height * ratio; | |
} | |
// Get context | |
// Draw scaled image on to canvas | |
this.context = this.surface.getContext( '2d' ); | |
this.context.drawImage( this.holder, 0, 0, this.surface.width, this.surface.height ); | |
// Find faces | |
tracking.track( '#surface', this.tracker ); | |
} | |
// Finished reading local file | |
// Populate image element | |
doRead( evt ) { | |
console.log( evt ); | |
this.holder.src = evt.target.result; | |
} | |
// Facial tracking completed | |
// Highlight faces | |
doTrack( evt ) { | |
console.log( evt ); | |
// Style | |
this.context.beginPath(); | |
this.context.lineWidth = 6; | |
this.context.strokeStyle = 'yellow'; | |
// Faces | |
for( let face of evt.data ) { | |
this.context.rect( face.x, face.y, face.width, face.height ); | |
} | |
// Draw | |
this.context.stroke(); | |
// Show | |
this.surface.style.opacity = 1.0; | |
// Reset for same file selection | |
this.uploader.value = ''; | |
} | |
// Called when a file is selected | |
// Start analyzing | |
doUpload( evt ) { | |
console.log( evt ); | |
this.process( evt.target.files[0] ); | |
} | |
} | |
// Constants | |
People.LANDSCAPE_SCALE = 0.50; | |
People.PORTRAIT_SCALE = 0.75; | |
People.STEP_SIZE = 1.7; | |
// Instantiate application | |
let app = new People(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment