Skip to content

Commit

Permalink
better abstractions for VideoContainer. implemented basic clm image e…
Browse files Browse the repository at this point in the history
…xample (no manual selection yet)
  • Loading branch information
yofreke committed Sep 15, 2016
1 parent f1d2ee9 commit 62945f0
Show file tree
Hide file tree
Showing 18 changed files with 516 additions and 133 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lib/stats.js"]
path = lib/stats.js
url = https://github.com/jsio-private/stats.js
1 change: 1 addition & 0 deletions examples/clm_image.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ <h2>Face tracking in images</h2>
<canvas id="image" width="625" height="500"></canvas>
<canvas id="overlay" width="625" height="500"></canvas>
</div>

<br/>
<input type="button" class="btn" value="start" onclick="animateClean()"></input>
<input type="button" class="btn" value="manually select face" onclick="selectBox()"></input>
Expand Down
36 changes: 32 additions & 4 deletions examples/components/App.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import React from 'react';
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';

import MenuDrawer from './MenuDrawer';
import SimpleExample from './simple';
import SimpleExample from './SimpleExample';
import ClmImageExample from './ClmImageExample';

import './App.styl'

const EXAMPLE_TO_CMPT = {
simple: SimpleExample,
clmImage: ClmImageExample
}

export default class App extends React.Component {
class App extends React.Component {
render () {
const example = <SimpleExample />;
const exampleCtor = EXAMPLE_TO_CMPT[this.props.activeExample];
let example;
if (exampleCtor) {
example = React.createElement(exampleCtor);
} else {
example = <h1>Coming Soon!</h1>;
}

return (
<div className='app-cmpt'>
Expand All @@ -20,3 +32,19 @@ export default class App extends React.Component {
);
}
}

App.propTypes = {
activeExample: PropTypes.string
};

const mapStateToProps = state => {
return {
activeExample: state.examples.activeExample
};
};

const mapDispatchToProps = dispatch => {
return { };
};

export default connect(mapStateToProps, mapDispatchToProps)(App);
4 changes: 2 additions & 2 deletions examples/components/App.styl
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ body
.app-cmpt
display flex

*
flex-shrink 0
// *
// flex-shrink 0

.example-wrapper
padding 20px
Expand Down
207 changes: 207 additions & 0 deletions examples/components/ClmImageExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
import React from 'react';
import classNames from 'classnames';

import RaisedButton from 'material-ui/RaisedButton';

import Tracker from 'clmtrackr/js/Tracker';
import { resizeImage } from 'clmtrackr/js/utils/image';
import { requestAnimFrame, cancelRequestAnimFrame } from 'clmtrackr/js/utils/anim';

import TrackerContainer from 'clmtrackr/ui/container/TrackerContainer';

import './ClmImageExample.styl';


const MEDIA_SRC = 'media/franck_02159.jpg';
const MEDIA_SIZE = { width: 625, height: 500 };


export default class SimpleExample extends React.Component {
constructor () {
super();
this.state = {
tracker: null,
isTrackerRunning: false,

showLoadImageText: false,
convergenceText: 'n/a',
convergenceStatus: '',

fileList: [],
fileIndex: 0
};

this._boundOnFrame = this._onFrame.bind(this);
}

_loadMediaSrc (src) {
const img = new Image();
img.onload = () => {
const trackerContainer = this.refs.trackerContainer;
const mediaCanvas = trackerContainer.refs.media;
resizeImage(img, {
canvas: mediaCanvas,
padding: 0,
paddingJitter: 0
});
};
img.src = MEDIA_SRC;
}

componentDidMount () {
this._loadMediaSrc(MEDIA_SRC);

const tracker = new Tracker({ stopOnConvergence: true });
this.setState({ tracker });
tracker.init();

tracker.on('notFound', (event) => {
tracker.stop();
alert('The tracking had problems with finding a face in this image. Try selecting the face in the image manually.')
});

// detect if tracker loses tracking of face
tracker.on('lost', (event) => {
tracker.stop();
alert('The tracking had problems converging on a face in this image. Try selecting the face in the image manually.')
});

// detect if tracker has converged
tracker.on('converged', (event) => {
this.setState({ convergenceText: 'CONVERGED', convergenceStatus: 'good' });
// stop drawloop
cancelRequestAnimFrame(this._boundOnFrame);
});

tracker.on('started', () => this.setState({ isTrackerRunning: true }));
tracker.on('stopped', () => this.setState({ isTrackerRunning: false }));
}

_onFrame () {
// Update overlay
const trackerContainer = this.refs.trackerContainer;
if (trackerContainer) {
const tracker = this.state.tracker;
const cc = trackerContainer.refs.canvas.getContext('2d');
cc.clearRect(0, 0, MEDIA_SIZE.width, MEDIA_SIZE.height);
tracker.draw(cc.canvas);
}
requestAnimFrame(this._boundOnFrame);
}

_start (e) {
const trackerContainer = this.refs.trackerContainer;
const tracker = this.state.tracker;
tracker.start(trackerContainer.refs.media);
this._onFrame();
}

_selectBox (e) {
alert('Coming soon!');
}

_selectFile (e) {
e.preventDefault();
const fileInput = this.refs.fileInput;
fileInput.click();
}

_handleFileSelect (e) {
const files = e.target.files;
const fileList = [];
for (let i = 0; i < files.length; i++) {
if (!files[i].type.match('image.*')) {
continue;
}
fileList.push(files[i]);
}
if (files.length > 0) {
this.setState({ fileIndex: 0 });
}

this.setState({ fileList });

setTimeout(() => { this._loadImage(); });
}

_loadImage () {
const { fileList, fileIndex, tracker } = this.state;
if (fileList.indexOf(fileIndex) >= 0) { return; }

const reader = new FileReader();
reader.onload = (e) => {
// Render thumbnail.
this._loadMediaSrc(e.target.result);
};
reader.readAsDataURL(fileList[fileIndex]);

const trackerContainer = this.refs.trackerContainer;
const overlayCC = trackerContainer.refs.canvas.getContext('2d');
overlayCC.clearRect(0, 0, MEDIA_SIZE.width, MEDIA_SIZE.height);
this.setState({ convergenceText: 'n/a', convergenceStatus: '' });
tracker.stop();
tracker.reset();
}

render () {
let loadImageText;
if (this.state.showLoadImageText) {
loadImageText = <p>To try it out with your own image, choose a file above by clicking "choose file". If the tracking has problems, try selecting the face in the image manually by clicking "manually select face", and click and hold to drag a square around the face in the image.</p>;
}

return (
<div className='clm-image-example-cmpt'>
<h1>Tracking a video tag</h1>

<TrackerContainer
ref='trackerContainer'
mediaType={'image'}
mediaSrc={MEDIA_SRC}
mediaSize={MEDIA_SIZE}
showStats={true}
tracker={this.state.tracker}
/>

<div className='control-row'>
<div>
<RaisedButton
label='start'
onClick={this._start.bind(this)}
disabled={this.state.isTrackerRunning}
/>
<RaisedButton
label='manually select face'
onClick={this._selectBox.bind(this)}
/>
<RaisedButton
label='Choose File'
onClick={this._selectFile.bind(this)}
/>
<input
type='file'
className='file-input'
ref='fileInput'
onChange={this._handleFileSelect.bind(this)}
/>
</div>

<div
className={classNames(
'convergence-text',
this.state.convergenceStatus
)}
>
Convergence: {this.state.convergenceText}
</div>
</div>

<div>
<p>This is an example of precise face-tracking in an image using the javascript library <a href='https://github.com/auduno/clmtrackr'><em>clmtrackr</em></a>. To try it out, simply click start.</p>
{loadImageText}
</div>

<p>The image is from the <a href='http://www-prima.inrialpes.fr/FGnet/data/01-TalkingFace/talking_face.html‎'>FG-net Talking Face</a> project</p>
</div>
);
}
}
19 changes: 19 additions & 0 deletions examples/components/ClmImageExample.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.clm-image-example-cmpt
position relative

.control-row
display flex
justify-content space-between
align-items center

.file-input
visibility hidden
position absolute

.convergence-text
background-color #cccc00
transition background-color 0.2s
min-width 100px

&.good
background-color #00FF00
88 changes: 88 additions & 0 deletions examples/components/SimpleExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from 'react';

import Tracker from 'clmtrackr/js/Tracker';

import TrackerContainer from 'clmtrackr/ui/container/TrackerContainer';

import './ClmImageExample.styl';


const MEDIA_SRC = 'media/franck.ogv';
const MEDIA_SIZE = { width: 368, height: 288 };


export default class SimpleExample extends React.Component {
constructor () {
super();
this.state = {
tracker: null,
points: null
};
}

componentDidMount () {
const tracker = new Tracker();
this.setState({ tracker });
tracker.init();

const trackerContainer = this.refs.trackerContainer;
tracker.start(trackerContainer.refs.media);

requestAnimationFrame(this._onFrame.bind(this));
}

_onFrame () {
// Update overlay
const trackerContainer = this.refs.trackerContainer;
if (trackerContainer) {
const tracker = this.state.tracker;
const cc = trackerContainer.refs.canvas.getContext('2d');
cc.clearRect(0, 0, MEDIA_SIZE.width, MEDIA_SIZE.height);
tracker.draw(cc.canvas);

// Update the rendered points
this.setState({ points: tracker.getCurrentPosition() });
}
requestAnimationFrame(this._onFrame.bind(this));
}

render () {
const positionChildren = [];

const points = this.state.points;
if (points) {
for (let p = 0; p < 10; p++) {
const positionString = (
'featurepoint ' + p + ' : ' +
'[' +
points[p][0].toFixed(2) +
', ' +
points[p][1].toFixed(2) +
']'
);
positionChildren.push(<div key={p}>{positionString}</div>);
}
}

return (
<div className='clm-image-example-cmpt'>
<h1>Tracking a video tag</h1>

<TrackerContainer
ref='trackerContainer'
mediaType={'video'}
mediaSrc={MEDIA_SRC}
mediaSize={MEDIA_SIZE}
showStats={true}
tracker={this.state.tracker}
/>

<p>Printing coordinates of the first 10 points in facial features:</p>

<div className='positions'>
{positionChildren}
</div>
</div>
);
}
}
Loading

0 comments on commit 62945f0

Please sign in to comment.