Skip to content

Commit

Permalink
single JsfeatFace instance per tracker
Browse files Browse the repository at this point in the history
  • Loading branch information
yofreke committed Sep 14, 2016
1 parent e5ab47d commit ba19e81
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 40 deletions.
19 changes: 8 additions & 11 deletions js/Tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ export default class Tracker extends EventEmitter {
this.runnerBox = undefined;

this.pointWeights = undefined;

this.jsfeatFace = new JsfeatFace();
}

/*
Expand Down Expand Up @@ -992,20 +994,15 @@ export default class Tracker extends EventEmitter {

// detect position of face on canvas/video element
_detectPosition (el, callback) {
var canvas = document.createElement('canvas');
canvas.width = el.width;
canvas.height = el.height;
var cc = canvas.getContext('2d');
cc.drawImage(el, 0, 0, el.width, el.height);

var jf = new JsfeatFace(canvas);
// jf.faceDetected = this._faceDetected.bind(this);
jf.on('faceDetected', (comp) => {
const detectedCb = (comp) => {
this.jsfeatFace.removeListener('faceDetected', detectedCb);
this._faceDetected(comp, callback);
});
};

this.jsfeatFace.on('faceDetected', detectedCb);

// TODO Allow option that limit simultaneous trigger of WebWorkers
jf.findFace();
this.jsfeatFace.findFace(el);
}

// calculate score of current fit
Expand Down
74 changes: 45 additions & 29 deletions js/jsfeat/JsfeatFace.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,69 @@ export default class JsfeatFace extends EventEmitter {
/**
* @param {Canvas|Image|Video} image
*/
constructor (image) {
constructor () {
super();
this.work_canvas = undefined;
this.work_ctx = undefined;

this.image = image;
this.w = this.image.width;
this.h = this.image.height;

if (this.image.tagName === 'VIDEO' || this.image.tagName === 'IMG') {
this.work_canvas = document.createElement('canvas');
this.work_canvas.height = this.image.width;
this.work_canvas.width = this.image.height;
this.work_ctx = this.work_canvas.getContext('2d');
} else if (this.image.tagName === 'CANVAS') {
this.work_ctx = this.image.getContext('2d');
}

// img_u8 = new jsfeat.matrix_t(w, h, jsfeat.U8_t | jsfeat.C1_t);
// ii_sum = new Int32Array((w+1)*(h+1));
// ii_sqsum = new Int32Array((w+1)*(h+1));
// ii_tilted = new Int32Array((w+1)*(h+1));

// var classifier = frontalface;

this._waitingForResponse = false;
this.worker = findFaceWorker();

// Listen for messages coming out of the web worker
this.worker.addEventListener('message', (e) => {
if (e.data.type === 'console') {
console[e.data.func].apply(window, e.data.args);
return;
}

this._waitingForResponse = false;
this.emit('faceDetected', e.data.comp);
}, false);
}

findFace () {
if (this.image.tagName === 'VIDEO' || this.image.tagName === 'IMG') {
this.work_ctx.drawImage(this.image, 0, 0);
findFace (image) {
if (this._waitingForResponse) {
throw new Error('Already finding face');
}
this._waitingForResponse = true;

let workCtx;
const w = image.width;
const h = image.height;

if (image.tagName === 'VIDEO' || image.tagName === 'IMG') {
const workCanvas = document.createElement('canvas');
workCanvas.height = image.width;
workCanvas.width = image.height;
workCtx = workCanvas.getContext('2d');
// Draw a single frame
workCtx.drawImage(image, 0, 0);
} else if (image.tagName === 'CANVAS') {
workCtx = image.getContext('2d');
} else {
throw new Error('unknown image tagName: ' + image.tagName);
}

// img_u8 = new jsfeat.matrix_t(w, h, jsfeat.U8_t | jsfeat.C1_t);
// ii_sum = new Int32Array((w+1)*(h+1));
// ii_sqsum = new Int32Array((w+1)*(h+1));
// ii_tilted = new Int32Array((w+1)*(h+1));

// var classifier = frontalface;

let imageData;
try {
imageData = workCtx.getImageData(0, 0, w, h);
} catch (e) {
console.warn(
'Could not getImageData, is your element too large?',
`w= ${w} h= ${h}`
);
console.error(e);
}
const imageData = this.work_ctx.getImageData(0, 0, this.w, this.h);

// console.time('findFace');
this.worker.postMessage({
w: this.w,
h: this.h,
w: w,
h: h,
imageData: imageData
});
}
Expand Down
60 changes: 60 additions & 0 deletions js/utils/image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export const jitter = (value, jitter) => (
(value - jitter) + (Math.random() * jitter * 2)
);

const RESIZE_IMAGE_DEFAULT_OPTS = {
canvas: null,
maxHeight: 500,
maxWidth: 700,
padding: 50,
paddingJitter: 0.25
};

/**
* Takes an input image, resizes it, draws to canvas.
* You can translate tracker points back to texture points using the returned
* padding.
* @param {Image} image
* @param {Object} [opts]
* @param {Object} [opts.canvas]
* @param {Object} [opts.maxHeight=500]
* @param {Object} [opts.maxWidth=700]
* @param {Object} [opts.padding=50]
* @param {Object} [opts.paddingJitter=0.25]
*/
export const resizeImage = (image, opts = RESIZE_IMAGE_DEFAULT_OPTS) => {
opts.canvas = opts.canvas || document.createElement('canvas');

const c = opts.canvas;
const cc = c.getContext('2d');

let w;
let h;

if (image.height > opts.maxHeight || image.width > opts.maxWidth) {
var rel = image.height / image.width;
var neww = opts.maxHeight;
var newh = neww * rel;
if (newh > opts.maxWidth) {
newh = opts.maxWidth;
neww = newh / rel;
}
w = neww;
h = newh;
} else {
w = image.width;
h = image.height;
}

let padding = Math.max(
Math.max((opts.maxWidth - w) / 2, opts.padding),
Math.max((opts.maxHeight - h) / 2, opts.padding)
);
padding = jitter(padding, opts.paddingJitter);

c.width = w + padding * 2;
c.height = h + padding * 2;
cc.drawImage(image, padding, padding, w, h);

return { canvas: c, padding: padding };
}

0 comments on commit ba19e81

Please sign in to comment.