Degroupement des filtres avec 'ReactiveFilterManager' et la classe abstraite 'ReactiveFilter' qui prend au constructeur la liste de ses attributs et qui définit les getters/setters pour dat.GUI et qui a la méthode apply() abstraite permettant d'effectuer les modifications associées

This commit is contained in:
xdrm-brackets 2016-10-07 10:50:06 +02:00
parent 7dfe16bf49
commit a096260662
5 changed files with 254 additions and 87 deletions

View File

@ -18,6 +18,7 @@
<script type='text/javascript' src='./js/lib/ajax.js' ></script> <!-- Gestion des requetes ajax -->
<script type='text/javascript' src='./js/lib/local.js' ></script> <!-- Lib locale -->
<script type='text/javascript' src='./js/lib/image-loader.js' ></script> <!-- Gestion du chargement d'image -->
<script type='text/javascript' src='./js/lib/reactive-filter.js' ></script> <!-- Gestion du des filtres dynamiques -->
<!-- dat.GUI -->
<script type='text/javascript' src='./js/lib/min/dat.gui.js' ></script> <!-- Lib externe -->

View File

@ -1,32 +1,37 @@
/* [0] Initialisation
=========================================================*/
/* (1) Elements du DOM */
var DOM = {
{ /* [0] Initialisation
=========================================================*/
/* (1) Elements du DOM */
var DOM = {
body: $('body'),
canvas: $('canvas'),
imageLoader: $('#image-loader')
};
};
/* (2) dat.GUI initialization */
var Controller = new dat.GUI();
/* (2) dat.GUI initialization */
var Controller = new dat.GUI();
/* (3) Canvas initialisation */
var _CAN = DOM.canvas;
/* (3) Canvas initialisation */
var _CAN = DOM.canvas;
_CAN.width = _CAN.height = 1000;
var _CON = _CAN.getContext('2d');
var _CON = _CAN.getContext('2d');
/* (4) Image Loader + Définitions */
var iL;
/* (4) Image Loader + Définitions */
var iL;
var process;
var exec = false;
var last;
var filterManager;
var process;
var exec = false;
var last;
}
{ /* [1] Initialisation du process
=========================================================*/
/* [1] Initialisation
=========================================================*/
var init = function(){
var init = function(){
/* (1) Image par défaut */
this.src = 'front:male:1.jpg';
@ -35,52 +40,22 @@ var init = function(){
Controller.add(this, 'src', this._images).listen();
last = this.src;
};
};
/* (2) Gestion de tracking.js */
var zones;
/* (3) Gestion du track de l'image */
var track = {update: true};
var tracker = new tracking.ObjectTracker(['face', 'eye', 'mouth']);
/* (x) Variables globales
---------------------------------------------------------*/
/* (1) Gestion du ration de l'image */
var pixelRatio = {
tracker.setStepSize(1.5);
initialized: false,
Controller.addFolder('Tracking.js');
Controller.add(track, 'update');
init: function(){
if( this.initialized )
return;
}
this.initialized = true;
this._w = this._h = 0.9;
this.width = this.height = null;
this.__defineGetter__('width', function(){ return this._w; });
this.__defineGetter__('height', function(){ return this._h; });
this.__defineSetter__('width', function(v){ this._w = v; process.bind(iL._wrapper)(); });
this.__defineSetter__('height', function(v){ this._h = v; process.bind(iL._wrapper)(); });
Controller.addFolder('Image Ratio');
Controller.add(this, 'width', 0, 2).listen();
Controller.add(this, 'height', 0, 2).listen();
}
};
/* (2) Gestion de tracking.js */
var zones;
/* (3) Gestion du track de l'image */
var track = {update: true};
var tracker = new tracking.ObjectTracker(['face', 'eye', 'mouth']);
tracker.setStepSize(1.5);
Controller.addFolder('Tracking.js');
Controller.add(track, 'update');
/* [2] Routine principale
=========================================================*/
@ -110,8 +85,7 @@ process = function(){
this.defaultHeight = this.height;
log('Image copied', '[Canvas]');
/* (2) Si `track.update` est TRUE, on lance `Tracking.js`
/* (3) Si `track.update` est TRUE, on lance `Tracking.js`
---------------------------------------------------------*/
if( track.update ){
@ -147,19 +121,20 @@ process = function(){
{ /* [2] Copie sur le `<canvas>`
=========================================================*/
/* (1) Initialisation du gestionnaire de `ratio` */
pixelRatio.init();
/* (1) Ratio */
filterManager.get('ratio').apply();
/* (2) Calcul de la taille de l'image en fonction du `ratio` */
this.width = this.defaultWidth * (pixelRatio.width * _CAN.width / this.defaultWidth);
this.height = this.defaultHeight * (pixelRatio.height * _CAN.height / this.defaultHeight);
/* (3) Copie de l'image sur le `<canvas>` */
_CON.drawImage(this, 0, 0, this.width, this.height);
}
{ /* [3] Tracking.js
{ /* [3] Filtrage pre-processing
=========================================================*/
}
{ /* [4] Tracking.js
=========================================================*/
/* (1) On reporte chaque zone trackée sur le `<canvas>` */
@ -181,11 +156,33 @@ process = function(){
}
{ /* [5] Filtrage post-processing
=========================================================*/
}
console.timeEnd('PROCESS');
};
{ /* [3] Gestion des `ReactiveFilter`
=========================================================*/
/* (1) Création du Manager */
filterManager = new ReactiveFilterManager(DOM.imageLoader, _CAN, process);
/* (2) Ajout des filtres */
filterManager.add('ratio', reactiveRatio);
/* (3) On attache tout à dat.GUI */
Controller.addFolder('Image Ratio');
Controller.add(filterManager.get('ratio'), 'width', 0, 2).listen();
Controller.add(filterManager.get('ratio'), 'height', 0, 2).listen();
}
/* [x] Chargement image
=========================================================*/
iL = new ImageLoader( DOM.imageLoader, init, process );

View File

@ -0,0 +1,5 @@
var ReactiveFilter=function(a){this._manager={_process:function(){}};this._attr=a instanceof Object?a:{};for(var b in this._attr)this.__defineGetter__(b,function(a){return this._attr[a]}.bind(this,b)),this.__defineSetter__(b,function(a,b){return function(d){a._attr[b]=d;a._manager.process()}}(this,b));this.apply=function(){}},ReactiveFilterManager=function(a,b,c){this._target=a instanceof HTMLImageElement?a:null;if(!this._target)throw Error("Param 1 expected to be an HTMLImageElement (<img>), but "+
a.constructor.name+" received");this._canvas=b instanceof HTMLCanvasElement?b:null;if(!this._canvas)throw Error("Param 2 expected to be an HTMLCanvasElement (<canvas>), but "+b.constructor.name+" received");this._context=this._canvas.getContext("2d");this._process=c instanceof Function?c:null;if(!this._process)throw Error("Param 3 expected to be a Function, but "+c.constructor.name+" received");this._filter={}};
ReactiveFilterManager.prototype.add=function(a,b){a="string"===typeof a?a:null;if(!a)throw Error("Param 1 expected to be a `string`, but "+a.constructor.name+" received");b=b instanceof ReactiveFilter?b:null;if(!b)throw Error("Param 2 expected to be a `ReactiveFilter`, but "+b.constructor.name+" received");if(null!=this._filter[a])return!0;this._filter[a]=b;b._manager=this};
ReactiveFilterManager.prototype.get=function(a){a="string"===typeof a?a:null;if(!a)throw Error("Param 1 expected to be a `string`, but "+a.constructor.name+" received");return null!=this._filter[a]?this._filter[a]:!1};ReactiveFilterManager.prototype.process=function(){this._process.bind(this._target)()};var reactiveRatio=new ReactiveFilter({width:.95,height:.95});
reactiveRatio.apply=function(){if(this._manager instanceof ReactiveFilterManager){var a=this._manager._target,b=this._manager._canvas,c=this._manager._context;a.width=this.width*b.width/a.defaultWidth*a.defaultWidth;a.height=this.height*b.height/a.defaultHeight*a.defaultHeight;c.drawImage(a,0,0,a.width,a.height)}};

View File

@ -0,0 +1,165 @@
/* INTERFACE DE `ReactiveFilter`
*
*/
var ReactiveFilter = function(attr){
/* [1] Initialisation des arguments
=========================================================*/
this._manager = {_process: function(){}};
this._attr = attr instanceof Object ? attr : {};
/* [2] Gestion des getters/setters
=========================================================*/
for( var key in this._attr ){
/* (1) Getter */
this.__defineGetter__(key, function(k){
return this._attr[k];
}.bind(this, key) );
/* (2) Setter */
this.__defineSetter__(key, (function(o, k){ return function(v){
o._attr[k] = v;
o._manager.process();
}; })(this, key) );
}
// ABSTRACT
this.apply = function(){};
};
/* CONSTRUCTEUR -> Gestionnaire des ReactiveFilter
*
* @pImageTarget<HTMLImageElement> Image cible
* @pCanvas<HTMLCanvasElement> Canvas de rendu
* @pProcessFunction<Function> Fonction qui met à jour le rendu
*
*/
var ReactiveFilterManager = function(pImageTarget, pCanvas, pProcessFunction){
/* [0] Gestion des arguments
=========================================================*/
/* (1) Image cible */
this._target = pImageTarget instanceof HTMLImageElement ? pImageTarget : null;
if( !this._target ) throw new Error('Param 1 expected to be an HTMLImageElement (<img>), but '+pImageTarget.constructor.name+' received');
/* (2) Canvas de rendu */
this._canvas = pCanvas instanceof HTMLCanvasElement ? pCanvas : null;
if( !this._canvas ) throw new Error('Param 2 expected to be an HTMLCanvasElement (<canvas>), but '+pCanvas.constructor.name+' received');
// On récupère aussi le contexte
this._context = this._canvas.getContext('2d');
/* (3) Routine de process */
this._process = pProcessFunction instanceof Function ? pProcessFunction : null;
if( !this._process ) throw new Error('Param 3 expected to be a Function, but '+pProcessFunction.constructor.name+' received');
/* [1] Initialisation des filtres
=========================================================*/
this._filter = {};
};
/* AJOUT D'UN `ReactiveFilter`
*
* @pFilterKey<String> Identifiant du filtre
* @pFilter<ReactiveFilter> Filtre en question
*
* @return added<Boolean> Etat de l'ajout (TRUE: ajouté)
*
*/
ReactiveFilterManager.prototype.add = function(pFilterKey, pFilter){
/* [0] Gestion des paramètres
=========================================================*/
/* (1) Clé <string> */
pFilterKey = typeof pFilterKey === 'string' ? pFilterKey : null;
if( !pFilterKey ) throw new Error('Param 1 expected to be a `string`, but '+pFilterKey.constructor.name+' received');
/* (2) filtre <ReactiveFilter> */
pFilter = pFilter instanceof ReactiveFilter ? pFilter : null;
if( !pFilter ) throw new Error('Param 2 expected to be a `ReactiveFilter`, but '+pFilter.constructor.name+' received');
/* [1] Si le filtre est déja présent, on ne fait rien
=========================================================*/
if( this._filter[pFilterKey] != null )
return true;
/* [2] On ajoute le filtre
=========================================================*/
/* (1) Ajout du filtre au manager */
this._filter[pFilterKey] = pFilter;
/* (2) Ajout du manager au filtre */
pFilter._manager = this;
};
/* GET D'UN `ReactiveFilter`
*
* @pFilterKey<String> Identifiant du filtre
*
* @return filter<ReactiveFilter|Boolean> Filtre ou FALSE
*
*/
ReactiveFilterManager.prototype.get = function(pFilterKey){
/* [0] Gestion des paramètres
=========================================================*/
/* (1) Clé <string> */
pFilterKey = typeof pFilterKey === 'string' ? pFilterKey : null;
if( !pFilterKey ) throw new Error('Param 1 expected to be a `string`, but '+pFilterKey.constructor.name+' received');
/* [1] Si le filtre est déja présent, on ne fait rien
=========================================================*/
if( this._filter[pFilterKey] != null )
return this._filter[pFilterKey];
/* [2] Si on a rien, on retourne FALSE
=========================================================*/
return false;
};
/* LANCEMENT DE ROUTINE DE PROCESS
*
*/
ReactiveFilterManager.prototype.process = function(){
this._process.bind(this._target)();
};
/************************************************
**** Gestion de la ratio de l'image ****
************************************************/
var reactiveRatio = new ReactiveFilter({ width: .95, height: .95 });
reactiveRatio.apply = function(){
/* [1] Si pas de manager, on exit
=========================================================*/
if( !(this._manager instanceof ReactiveFilterManager) )
return;
/* [2] On effectue notre modification
=========================================================*/
var o = {
image: this._manager._target,
canvas: this._manager._canvas,
context: this._manager._context
};
/* (2) Calcul de la taille de l'image en fonction du `ratio` */
o.image.width = o.image.defaultWidth * (this.width * o.canvas.width / o.image.defaultWidth);
o.image.height = o.image.defaultHeight * (this.height * o.canvas.height / o.image.defaultHeight);
/* (3) Copie de l'image sur le `<canvas>` */
o.context.drawImage(o.image, 0, 0, o.image.width, o.image.height);
};

View File

@ -1,6 +1,5 @@
var DOM={body:$("body"),canvas:$("canvas"),imageLoader:$("#image-loader")},Controller=new dat.GUI,_CAN=DOM.canvas;_CAN.width=_CAN.height=1E3;
var _CON=_CAN.getContext("2d"),iL,process,exec=!1,last,init=function(){this.src="front:male:1.jpg";Controller.addFolder("image source");Controller.add(this,"src",this._images).listen();last=this.src},pixelRatio={initialized:!1,init:function(){this.initialized||(this.initialized=!0,this._w=this._h=.9,this.width=this.height=null,this.__defineGetter__("width",function(){return this._w}),this.__defineGetter__("height",function(){return this._h}),this.__defineSetter__("width",function(a){this._w=a;process.bind(iL._wrapper)()}),
this.__defineSetter__("height",function(a){this._h=a;process.bind(iL._wrapper)()}),Controller.addFolder("Image Ratio"),Controller.add(this,"width",0,2).listen(),Controller.add(this,"height",0,2).listen())}},zones,track={update:!0},tracker=new tracking.ObjectTracker(["face","eye","mouth"]);tracker.setStepSize(1.5);Controller.addFolder("Tracking.js");Controller.add(track,"update");
var DOM={body:$("body"),canvas:$("canvas"),imageLoader:$("#image-loader")},Controller=new dat.GUI,_CAN=DOM.canvas;_CAN.width=_CAN.height=1E3;var _CON=_CAN.getContext("2d"),iL,filterManager,process,exec=!1,last,init=function(){this.src="front:male:1.jpg";Controller.addFolder("image source");Controller.add(this,"src",this._images).listen();last=this.src},zones,track={update:!0},tracker=new tracking.ObjectTracker(["face","eye","mouth"]);tracker.setStepSize(1.5);Controller.addFolder("Tracking.js");
Controller.add(track,"update");
process=function(){if(this instanceof HTMLImageElement){console.time("PROCESS");this.src!=last&&(exec=!1,last=this.src);exec||(this.defaultWidth=this.width,this.defaultHeight=this.height,log("Image copied","[Canvas]"),track.update&&(zones=[],tracking.track(DOM.imageLoader,tracker),tracker.on("track",function(a){a.data.forEach(function(a){zones.push({x:a.x/DOM.imageLoader.defaultWidth,y:a.y/DOM.imageLoader.defaultHeight,w:a.width/DOM.imageLoader.defaultWidth,h:a.height/DOM.imageLoader.defaultHeight})});
log("Recognition done","[Tracking.js]");process.bind(DOM.imageLoader)()})),exec=!0);_CON.clearRect(0,0,_CAN.width,_CAN.height);pixelRatio.init();this.width=pixelRatio.width*_CAN.width/this.defaultWidth*this.defaultWidth;this.height=pixelRatio.height*_CAN.height/this.defaultHeight*this.defaultHeight;_CON.drawImage(this,0,0,this.width,this.height);for(var a in zones){var b=zones[a].x*this.width,c=zones[a].y*this.height,d=zones[a].w*this.width,e=zones[a].h*this.height;_CON.beginPath();_CON.moveTo(b,
c);_CON.lineTo(b,c+e);_CON.lineTo(b+d,c+e);_CON.lineTo(b+d,c);_CON.lineTo(b,c);_CON.lineWidth=5;_CON.stroke()}console.timeEnd("PROCESS")}};iL=new ImageLoader(DOM.imageLoader,init,process);
log("Recognition done","[Tracking.js]");process.bind(DOM.imageLoader)()})),exec=!0);_CON.clearRect(0,0,_CAN.width,_CAN.height);filterManager.get("ratio").apply();for(var c in zones){var a=zones[c].x*this.width,b=zones[c].y*this.height,d=zones[c].w*this.width,e=zones[c].h*this.height;_CON.beginPath();_CON.moveTo(a,b);_CON.lineTo(a,b+e);_CON.lineTo(a+d,b+e);_CON.lineTo(a+d,b);_CON.lineTo(a,b);_CON.lineWidth=5;_CON.stroke()}console.timeEnd("PROCESS")}};
filterManager=new ReactiveFilterManager(DOM.imageLoader,_CAN,process);filterManager.add("ratio",reactiveRatio);Controller.addFolder("Image Ratio");Controller.add(filterManager.get("ratio"),"width",0,2).listen();Controller.add(filterManager.get("ratio"),"height",0,2).listen();iL=new ImageLoader(DOM.imageLoader,init,process);