diff --git a/animation/moving_circle.js b/animation/moving_circle.js new file mode 100644 index 0000000000000000000000000000000000000000..a1402e43098fbd248d08a5b3f9b0713b01a4f02d --- /dev/null +++ b/animation/moving_circle.js @@ -0,0 +1,118 @@ +var animations=[] + +animations.MovingCircle=function(output_canvas_id,opt_options) { + this.radius=opt_options&&opt_options.radius?opt_options.radius:5; + + this.x0=opt_options&&opt_options.x0?opt_options.x0:5; + this.y0=opt_options&&opt_options.y0?opt_options.y0:5; + + this.step_x=opt_options&&opt_options.step_x?opt_options.step_x:1; + this.step_y=opt_options&&opt_options.step_x?opt_options.step_y:0; + + this.random=opt_options&&opt_options.random?true:false; + + this.output_cvs=document.getElementById(output_canvas_id); + this.output_ctxt=this.output_cvs.getContext("2d"); + this.fillColor=opt_options&&opt_options.fillColor?opt_options.fillColor:[255,0,0,255]; + +} + +animations.MovingCircle.prototype.move=function() { + var local_dx=Math.round(this.step_x*(!this.random?1:Math.random())); + var local_dy=Math.round(this.step_y*(!this.random?1:Math.random())); + + this.x0+=local_dx; this.y0+=local_dy; + + if ((this.x0+this.radius)>this.output_cvs.width) { + this.x0=this.output_cvs.width-this.radius; this.step_x=-this.step_x; + } else if (this.x0 < this.radius ) { + this.x0=this.radius; this.step_x=-this.step_x; + } + + if ((this.y0+this.radius) > this.output_cvs.height) { + this.y0=this.output_cvs.height-this.radius; this.step_y=-this.step_y; + } else if (this.y0 < this.radius ) { + this.y0=this.radius; this.step_y=-this.step_y; + } +} + +animations.MovingCircle.prototype.draw=function() { + this.output_ctxt.fillStyle="rgba("+this.fillColor.join(",")+")"; + this.output_ctxt.beginPath(); + this.output_ctxt.arc(this.x0,this.y0,this.radius,0,2*Math.PI); + this.output_ctxt.fill(); +} +animations.MovingCircle.prototype.animate=function() { + this.output_ctxt.beginPath(); + this.output_ctxt.clearRect(this.x0-this.radius,this.y0-this.radius,this.radius*2,this.radius*2); + this.move(); + this.draw(); +} + +animations.MovingCircleHChangingColor=function(output_canvas_id,opt_options) { + this.__proto__.__proto__=new animations.MovingCircle(output_canvas_id,opt_options); + + this.fillColor0=opt_options&&opt_options.fillColor0?opt_options.fillColor0:[255,0,0,0]; + this.fillColor1=opt_options&&opt_options.fillColor1?opt_options.fillColor1:[0,255,0,0]; + this.fillColorNbSteps=opt_options&&opt_options.fillColorNbSteps?opt_options.fillColorNbSteps:5; + this.fillColorIncrement=[]; + for (i=0;i<4;i++) { + this.fillColorIncrement[i]=(this.fillColor1[i]-this.fillColor0[i])/this.fillColorNbSteps; + }; + this.fillColor=this.fillColor0; + this.fillColorStepCount=0; + this.fillColorIncrementSign=1; + +} + +animations.MovingCircleHChangingColor.prototype.move=function() { + this.__proto__.__proto__.move(); + + if (this.fillColorStepCount==this.fillColorNbSteps) { + this.fillColorIncrementSign=-this.fillColorIncrementSign; + this.fillColorStepCount=0; + }; + + for (i=0;i<4;i++) { + this.fillColor[i]+=this.fillColorIncrementSign*this.fillColorIncrement[i]; + }; + this.fillColorStepCount++; +} + +animations.MovingCircleHChangingColorChangingBackground=function(output_canvas_id,opt_options) { + this.__proto__.__proto__=new animations.MovingCircleHChangingColor(output_canvas_id,opt_options); + + this.bgColor0=opt_options&&opt_options.bgColor0?opt_options.bgColor0:[255,0,0,255]; + this.bgColor1=opt_options&&opt_options.bgColor1?opt_options.bgColor1:[0,255,0,255]; + this.bgColorNbSteps=opt_options&&opt_options.bgColorNbSteps?opt_options.bgColorNbSteps:5; + this.bgColorIncrement=[]; + for (i=0;i<4;i++) { + this.bgColorIncrement[i]=(this.bgColor1[i]-this.bgColor0[i])/this.bgColorNbSteps; + }; + this.bgColor=this.bgColor0; + this.bgColorStepCount=0; + this.bgColorIncrementSign=1; + +} + +animations.MovingCircleHChangingColorChangingBackground.prototype.move=function() { + this.__proto__.__proto__.move(); + + if (this.bgColorStepCount==this.bgColorNbSteps) { + this.bgColorIncrementSign=-this.bgColorIncrementSign; + this.bgColorStepCount=0; + }; + + for (i=0;i<4;i++) { + this.bgColor[i]+=this.bgColorIncrementSign*this.bgColorIncrement[i]; + }; + this.bgColorStepCount++; +} + +animations.MovingCircleHChangingColorChangingBackground.prototype.animate=function() { + this.output_ctxt.fillStyle="rgba("+this.bgColor.join(",")+")"; + this.output_ctxt.beginPath(); + this.output_ctxt.fillRect(0,0,this.output_cvs.width,this.output_cvs.height); + this.move(); + this.draw(); +} diff --git a/data/red_circle.gif b/data/red_circle.gif new file mode 100644 index 0000000000000000000000000000000000000000..660e416cf70e75545dfafb805ab084eae3a61cfe Binary files /dev/null and b/data/red_circle.gif differ diff --git a/data/red_circle.jpg b/data/red_circle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5af437be485f66ac069cc735fe2e2a76d0908a64 Binary files /dev/null and b/data/red_circle.jpg differ diff --git a/data/red_circle.png b/data/red_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..908c1242aa2c135b1123925572820d65ae8d2ae7 Binary files /dev/null and b/data/red_circle.png differ diff --git a/effects/focus.js b/effects/focus.js new file mode 100644 index 0000000000000000000000000000000000000000..02a352599249b48c8b5877a4dda23c8fc2e75a05 --- /dev/null +++ b/effects/focus.js @@ -0,0 +1,117 @@ +var focus={} + +focus.MovingFocus = function(opt_options) { + this.focus_x=opt_options.focus_x; + this.focus_y=opt_options.focus_y; + this.focus_radius=opt_options.focus_radius; + this.focus_dx=opt_options&&opt_options.focus_dx?opt_options.focus_dx:this.focus_radius/10; + this.focus_dy=opt_options&&opt_options.focus_dy?opt_options.focus_dy:this.focus_radius/10; + this.width=opt_options.width; + this.height=opt_options.height; + this.random=opt_options&&opt_options.random?opt_options.random:false; +} + +focus.MovingFocus.prototype.updateFocus=function() { + var increment_x=Math.round(this.focus_dx*(this.random?Math.random():1)) + this.focus_x+=increment_x; + + if ((this.focus_x+this.focus_radius>this.width) + || (this.focus_x-this.focus_radius<0)) { + this.focus_dx=-this.focus_dx; + this.focus_x+=2*this.focus_dx; + + } + + var increment_y=Math.round(this.focus_dy*(this.random?Math.random():1)) + this.focus_y+=increment_y; + if ((this.focus_y+this.focus_radius>this.height) + ||(this.focus_y-this.focus_radius<0)) + { + this.focus_dy=-this.focus_dy; + this.focus_y+=2*this.focus_dy; + + } +} + +focus.MovingFocus.prototype.isInsideFocus=function(i,j) { + if (Math.sqrt((i-this.focus_x)*(i-this.focus_x)+ + (j-this.focus_y)*(j-this.focus_y)) + <=this.focus_radius) + return true; + else + return false; +} + +focus.MovingFocus.prototype.process = function(in_imgData,out_imgData) { + var self=this; + var w=0; + var count=0; + var pixels=in_imgData.data; + + //console.log("track "+this.focus_x+"x"+this.focus_y); + for (var i = 0; i < in_imgData.height; i++) { + for (var j = 0; j < in_imgData.width; j++) { + var mean=(pixels[w+1]+pixels[w+2]+pixels[w])/3; + + if (this.isInsideFocus(j,i)) { + var focus_i=i-this.focus_y; + var focus_j=j-this.focus_x; + var angleF=Math.atan2(focus_i,focus_j); + + var radius=Math.round(Math.sqrt(focus_i*focus_i+focus_j*focus_j)); + var angleR=Math.atan2(radius,this.focus_radius); + + var cible_i=Math.round(this.focus_y+focus_i); + var cible_j=Math.round(this.focus_x+focus_j); + + //var cible_i=Math.round(this.focus_y+ + // radius*Math.sin(-angle)); + //var cible_j=Math.round(this.focus_x+ + // radius*Math.cos(-angle)); + + //var cible_i=Math.round(this.focus_y+ + // radius*Math.sin(angle)*Math.sin(angle)); + //var cible_j=Math.round(this.focus_x+ + // radius*Math.cos(angle)*Math.cos(angle)); + + /*var cible_i=Math.round(this.focus_y+ + radius*(Math.sin(angle*angle))); + var cible_j=Math.round(this.focus_x+ + radius*(Math.cos(angle*angle))); + */ + + /* + var cible_i=Math.round(this.focus_y+ + radius*(Math.sin(angleF)*Math.sin(angleR))); + var cible_j=Math.round(this.focus_x+ + radius*(Math.cos(angleF)*Math.cos(angleR))); + */ + + /*var cible_i=Math.round(this.focus_y+ + radius*(Math.sin(angleF)/Math.sin(angleR))); + var cible_j=Math.round(this.focus_x+ + radius*(Math.cos(angleF)/Math.cos(angleR))); +*/ + + /*var cible_i=Math.round(this.focus_y+ + radius*radius/this.focus_radius + *Math.sin(angleF)); + var cible_j=Math.round(this.focus_x+ + radius*radius/this.focus_radius + *Math.cos(angleF));*/ + + var cible_w=(cible_i*in_imgData.width+cible_j)<<2; + //console.log(i+"x"+j+" "+w+" "+focus_i+"x"+focus_j+" - "+cible_i+"x"+cible_j+ " "+cible_w); + out_imgData.data[w+1]=pixels[cible_w+1]; + out_imgData.data[w+2]=pixels[cible_w+2]; + out_imgData.data[w]=pixels[cible_w]; + } else { + out_imgData.data[w + 1]=mean * 1; + out_imgData.data[w + 2]=mean * 1; + out_imgData.data[ w ]=mean * 1; + } + out_imgData.data[w+3]=pixels[w+3]; + w+=4; + } + } +}; diff --git a/effects/generic.js b/effects/generic.js new file mode 100644 index 0000000000000000000000000000000000000000..c6d3fd9e0b7a9813f9639400f9aada1821084b4a --- /dev/null +++ b/effects/generic.js @@ -0,0 +1,19 @@ +var generic_effect={} + +generic_effect.apply_region_filter=function(in_imgData,out_imgData,window_width,window_height, + pixel_form_region_rgba_filter) { + + var w=0; + + for (var y=0;y<out_imgData.height;y++) + for (var x=0;x<out_imgData.width;x++) { + w=((y*out_imgData.width)+x)<<2; + new_rgba_pixel_val=pixel_form_region_rgba_filter( + in_imgData, + x-Math.round(window_width/2), y-Math.round(window_height/2), + window_width, window_height); + for (var i=0;i<4;i++) { + out_imgData.data[w+i]=new_rgba_pixel_val[i]; + } + } +} diff --git a/effects/morpho.js b/effects/morpho.js new file mode 100644 index 0000000000000000000000000000000000000000..9a8fcb84399ab6971f56069c0dd61030caef6fae --- /dev/null +++ b/effects/morpho.js @@ -0,0 +1,42 @@ +var morpho_effects={} + +morpho_effects.DilatationWindow = function(opt_options) { + this.window_width=opt_options.window_width; + this.window_height=opt_options.window_height; +} + +morpho_effects.DilatationWindow.prototype.process=function(in_imgData,out_imgData) { + return generic_effect.apply_region_filter( + in_imgData,out_imgData,this.window_width,this.window_height, + morpho_filters.max_from_region + ); +} + +morpho_effects.ErosionWindow = function(opt_options) { + this.window_width=opt_options.window_width; + this.window_height=opt_options.window_height; +} + +morpho_effects.ErosionWindow.prototype.process=function(in_imgData,out_imgData) { + generic_effect.apply_region_filter( + in_imgData,out_imgData,this.window_width,this.window_height, + morpho_filters.min_from_region); +} + +morpho_effects.DilatationErosionWindow = function(opt_options) { + this.dilatation_width=opt_options.dilatation_width; + this.dilatation_height=opt_options.dilatation_height; + this.erosion_width=opt_options.erosion_width; + this.erosion_height=opt_options.erosion_height; + this.aux_cvs=document.createElement("canvas"); +} + +morpho_effects.DilatationErosionWindow.prototype.process=function(in_imgData,out_imgData) { + aux_imgData ={data:[],width:out_imgData.width,height:out_imgData.height}; + generic_effect.apply_region_filter( + in_imgData,aux_imgData,this.dilatation_width,this.dilatation_height, + morpho_filters.max_from_region); + generic_effect.apply_region_filter( + aux_imgData,out_imgData,this.erosion_width,this.erosion_height, + morpho_filters.min_from_region); +} diff --git a/features/pixels.js b/features/pixels.js index d471e620af382abdda1e7f3a7b092472127e88ff..973c66359093eee0cec218c8c761e634fb91c15e 100644 --- a/features/pixels.js +++ b/features/pixels.js @@ -25,7 +25,7 @@ pixels_features.mean_rgb_per_region=function(imageData,opt_options) { var mean=[]; mean[0]=0; mean[1]=0; mean[2]=0; var pos=0; var count=0; - for (var y=y0;y<y0+dy;y++) + for (var y=y0;y<y0+dy;y++) { for (var x=x0;x<x0+dx;x++) { pos=(y*imageData.width+x)<<2; if (imageData.data[pos+3]>0) { @@ -35,11 +35,15 @@ pixels_features.mean_rgb_per_region=function(imageData,opt_options) { count++; } } + } if (count>0) { + console.log("count: "+count); for (var i=0;i<3;i++) { mean[i]=Math.round(mean[i]/count); } return mean; + } else { + console.log("no positive Alpha, no rgb mean"); } return undefined; } @@ -53,3 +57,38 @@ pixels_features.mean_rgb_per_region=function(imageData,opt_options) { pixels_features.grid_mean_rgb=function(imageData,opt_options) { return generic_features.grid_descriptor(imageData,pixels_features.mean_rgb_per_region,opt_options); } + +/* + pixels_features.mean_rgb_afactor_per_region + - computes RGB mean of all pixels considering A as a weight of RGB channels + within opt_options.x0 .y0 .dx .dy + - if opt_options missing partially, + replace partially with defaults 0, 0, imageData.width, imageData.height + - returns undefined if none available +*/ +pixels_features.mean_rgb_afactor_per_region=function(imageData,opt_options) { + x0=opt_options&&opt_options.x0?opt_options.x0:0; + y0=opt_options&&opt_options.y0?opt_options.y0:0; + dx=opt_options&&opt_options.dx?opt_options.dx:imageData.width; + dy=opt_options&&opt_options.dy?opt_options.dy:imageData.height; + + + var mean=[]; + mean[0]=0; mean[1]=0; mean[2]=0; + var pos=0; var count=0; + for (var y=y0;y<y0+dy;y++) + for (var x=x0;x<x0+dx;x++) { + pos=(y*imageData.width+x)<<2; + for (var i=0;i<3;i++) { + mean[i]+=(imageData.data[pos+i]*imageData.data[pos+3]); + } + count++; + } + if (count>0) { + for (var i=0;i<3;i++) { + mean[i]=Math.round(mean[i]/count); + } + return mean; + } + return undefined; +} diff --git a/filters/linear.js b/filters/linear.js new file mode 100644 index 0000000000000000000000000000000000000000..7747d6d90c291eba8c1904ea20e03dc1405b37d6 --- /dev/null +++ b/filters/linear.js @@ -0,0 +1,6 @@ +var linear_filters={} + +linear_filters.identity=function(imgData,x0,y0,region_width,region_height) { + var w=((y0+Math.round(region_height/2))*imgData.width+(x0+Math.round(region_width/2)))<<2; + return [imgData.data[w],imgData.data[w+1],imgData.data[w+2],imgData.data[w+3]]; +} diff --git a/filters/morpho.js b/filters/morpho.js new file mode 100644 index 0000000000000000000000000000000000000000..7b6220586367833ec7dd6752b6ebb24f7feac84e --- /dev/null +++ b/filters/morpho.js @@ -0,0 +1,84 @@ +var morpho_filters={} + +morpho_filters.max_from_region=function(imgData,x0,y0,reg_width,reg_height) { + var pixels=imgData.data; + var w=((y0+Math.round(reg_height/2))*imgData.width + +(x0+Math.round(reg_width/2)))<<2; + var max_data=[pixels[w],pixels[w+1],pixels[w+2],pixels[w+3]]; + var max=(pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3]; + + for (var y=Math.max(0,y0);y<Math.min(y0+reg_height,imgData.height);y++) + for (var x=Math.max(0,x0);x<Math.min(x0+reg_width,imgData.width);x++) { + w = (y*imgData.width+x)<<2; + var val = (pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3]; + if (max < val) { + if (pixels[w+3]<255) pixels[w+3]=255; + max = val; max_data = [pixels[w],pixels[w+1],pixels[w+2],pixels[w+3]]; + } + } + return max_data; +} + +morpho_filters.max_red_from_region=function(imgData,x0,y0,reg_width,reg_height) { + var pixels=imgData.data; + var w=((y0+Math.round(reg_height/2))*imgData.width+(x0+Math.round(reg_width/2)))<<2; + var max_data=[pixels[w],pixels[w+1],pixels[w+2],pixels[w+3]]; + var max=(pixels[w]-pixels[w+1]-pixels[w+2])*pixels[w+3]; + for (var y=y0;y<y0+reg_height;y++) { + if (y<0 || y>imgData.height) continue; + for (var x=x0;x<x0+reg_width;x++) { + if (x<0 || x>imgData.width) continue; + w = (y*imgData.width+x)<<2; + var val = (pixels[w]-pixels[w+1]-pixels[w+2])*pixels[w+3]; + if (max < val) { + max = val; + max_data=[pixels[w], pixels[w+1], + pixels[w+2], pixels[w+3]]; + } + } + } + return max_data; +} + +morpho_filters.max_gray_from_region=function(imgData,x0,y0,reg_width,reg_height) { + var pixels=imgData.data; + var w=((y0+Math.round(reg_height/2))*imgData.width+(x0+Math.round(reg_width/2)))<<2; + var max_data=[pixels[w],pixels[w+1],pixels[w+2],pixels[w+3]]; + var max=(pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3]; + + for (var y=y0;y<y0+reg_height;y++) { + if (y<0 || y>imgData.height) continue; + for (var x=x0;x<x0+reg_width;x++) { + if (x<0 || x>imgData.width) continue; + w = (y*imgData.width+x)<<2; + var val = (pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3]; + if (max < val) { + max = val; + max_data=[val/3, val/3, + val/3, pixels[w+3]]; + } + } + } + return max_data; +} + +morpho_filters.min_from_region=function(imgData,x0,y0,reg_width,reg_height) { + var pixels=imgData.data; + var w=((y0+Math.round(reg_height/2))*imgData.width+(x0+Math.round(reg_width/2)))<<2; + var min_data=[pixels[w],pixels[w+1],pixels[w+2],pixels[w+3]]; + var min=(pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3]; + for (var y=y0;y<y0+reg_height;y++) { + if (y<0 || y>imgData.height) continue; + for (var x=x0;x<x0+reg_width;x++) { + if (x<0 || x>imgData.width) continue; + w = (y*imgData.width+x)<<2; + var val = (pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3]; + if (min > val) { + min = val; + min_data=[pixels[w], pixels[w+1], + pixels[w+2], pixels[w+3]]; + } + } + } + return min_data; +} diff --git a/metrics/pixels.js b/metrics/pixels.js index 2a9441cc8e86d627da0cd0027b523e51480a7609..954dd6c269d4018963d36e9e447e39321cc4c3c1 100644 --- a/metrics/pixels.js +++ b/metrics/pixels.js @@ -9,10 +9,42 @@ pixel_metrics.rgb_edist=function(pixel_rgb1, pixel_rgb2) { } /* - pixel_metrics.rgb_edist - computes euclidian distance between two grids + pixel_metrics.grid_rgb_edist - computes euclidian distance between two grids containing in each cell an rgb pixel */ pixel_metrics.grid_rgb_edist=function(pixels_rgb_grid1, pixels_rgb_grid2) { var dist_fun=pixel_metrics.rgb_edist; return generic_metrics.euclidian_distance_btw_feature_vectors(pixels_rgb_grid1.cells,pixels_rgb_grid2.cells,dist_fun); } + +/* + pixel_metrics.pixel_edist - computes euclidian distance between two pixels + regardless of color encoding +*/ +pixel_metrics.pixel_edist=function(pixel_rgb1, pixel_rgb2) { + var dist_fun=function(x,y){return x-y}; + return generic_metrics.euclidian_distance_btw_feature_vectors(pixel_rgb1,pixel_rgb2,dist_fun); +} + +/* + pixel_metrics.gray_edist - computes euclidian distance between two pixels + considering the intensity value +*/ +pixel_metrics.gray_edist=function(pixel_rgb1, pixel_rgb2) { + var pixel_gray_1=Math.round((pixel_rgb1[0]+pixel_rgb1[1]+pixel_rgb1[2])/3); + var pixel_gray_2=Math.round((pixel_rgb2[0]+pixel_rgb2[1]+pixel_rgb2[2])/3); + + return Math.abs(pixel_gray_1-pixel_gray_2); +} + +/* + pixel_metrics.visible_edist - computes euclidian distance between two pixels + considering the fact that pixels are both visible/invisible + returns 0 or 1 +*/ +pixel_metrics.visible_edist=function(pixel_rgb1, pixel_rgb2) { + var pixel_gray_1=Math.round((pixel_rgb1[0]+pixel_rgb1[1]+pixel_rgb1[2])/3); + var pixel_gray_2=Math.round((pixel_rgb2[0]+pixel_rgb2[1]+pixel_rgb2[2])/3); + return (pixel_gray_1>0 && pixel_gray_2>0) || (pixel_gray_1==0 && pixel_gray_2==0); + +} diff --git a/movement/detection.js b/movement/detection.js new file mode 100644 index 0000000000000000000000000000000000000000..d264ba932b800bc9d0797461bdfaeed446a770d9 --- /dev/null +++ b/movement/detection.js @@ -0,0 +1,55 @@ +var mvt_detection={} + +mvt_detection.SimpleMvtDetectorRegion=function(opt_options) { + this.difference = document.createElement("canvas"); + this.previous = document.createElement("canvas"); + + this.bbox=opt_options.bbox; + + this.difference.width=this.bbox.dx; this.difference.height=this.bbox.dy; + this.previous.width=this.bbox.dx; this.previous.height=this.bbox.dy; + + this.difference_ctxt = this.difference.getContext("2d"); + this.previous_ctxt = this.previous.getContext("2d"); + + this.diff_threshold=opt_options.diff_threshold; + this.mvt_threshold=opt_options.mvt_threshold; +} + +mvt_detection.SimpleMvtDetectorRegion.prototype.set_first_frame_from_eltId=function(eltId) { + var elt=document.getElementById(eltId); + var cvs=document.createElement("canvas"); + cvs.width=elt.width; cvs.height=elt.height; + var ctxt=cvs.getContext("2d"); + ctxt.drawImage(elt,0,0); + var first_imgData=ctxt.getImageData(this.bbox.x0,this.bbox.y0,this.bbox.dx,this.bbox.dy); + this.previous_ctxt.putImageData(first_imgData,0,0); +} + +mvt_detection.SimpleMvtDetectorRegion.prototype.process=function(in_imgData, out_imgData) { + var previous_imgData = this.previous_ctxt. + getImageData(0, 0, this.bbox.dx, this.bbox.dy); + in_reg_imgData=in_imgData.ctxt.getImageData(this.bbox.x0,this.bbox.y0,this.bbox.dx,this.bbox.dy); + + var w=0; + var count=0; + var min=[255,255,255], max=[0,0,0]; + for (var x=0; x<this.bbox.dx ; x++) + for (var y=0; y<this.bbox.dy ; y++) { + var diff=0; + for (var i=0; i<3; i++) { + out_imgData.data[w+i] = Math.abs(in_reg_imgData.data[w+i] - previous_imgData.data[w+i]); + diff += out_imgData.data[w+i]; + } + count+=(diff>this.diff_threshold); + out_imgData.data[w+3] = 255; + w+=4; + } + + this.previous_ctxt.putImageData(in_reg_imgData,0,0); + if (out_imgData) { + Tools.copy_partial_imageData_into_imageData(in_imgData,0,0,in_imgData.width,in_imgData.height,out_imgData,0,0); + Tools.strokeBBox_on_imageData(out_imgData,this.bbox,[255*(count>this.mvt_threshold),0,0,255]); + } + return (count>this.mvt_threshold); +} diff --git a/movement/difference.js b/movement/difference.js new file mode 100644 index 0000000000000000000000000000000000000000..466416996163f9bf4a26a00eaae8b554a0ea1891 --- /dev/null +++ b/movement/difference.js @@ -0,0 +1,77 @@ +var diff={} + +diff.DifferenceImageRGB=function(opt_options) { + this.difference = document.createElement("canvas"); + this.previous = document.createElement("canvas"); + + this.width=opt_options.width; + this.height=opt_options.height; + + this.difference.width=this.width; this.difference.height=this.height; + this.previous.width=this.width; this.previous.height=this.height; + + this.difference_ctxt = this.difference.getContext("2d"); + this.previous_ctxt = this.previous.getContext("2d"); +} + +diff.DifferenceImageRGB.prototype.set_first_frame_imgData=function(imgData) { + this.previous_ctxt.putImageData(imgData,0,0); +} + +diff.DifferenceImageRGB.prototype.set_first_frame_from_eltId=function(eltId) { + var elt=document.getElementById(eltId); + var cvs=document.createElement("canvas"); + cvs.width=elt.width; cvs.height=elt.height; + var ctxt=cvs.getContext("2d"); + ctxt.drawImage(elt,0,0); + this.set_first_frame_imgData(ctxt.getImageData(0,0,cvs.width,cvs.height)); +} + +diff.DifferenceImageRGB.prototype.process=function(in_imgData, out_imgData) { + var previous_imgData = this.previous_ctxt. + getImageData(0, 0, this.width, this.height); + var w=0; + var min=[255,255,255], max=[0,0,0]; + for (var x=0; x<this.width ; x++) + for (var y=0; y<this.height ; y++) { + for (var i=0; i<3; i++) { + out_imgData.data[w+i] = + Math.abs(in_imgData.data[w+i] - previous_imgData.data[w+i]); + } + out_imgData.data[w+3] = 255; + w+=4; + } + this.previous_ctxt.putImageData(in_imgData,0,0); +} + +diff.NormalizedDifferenceImageRGB=function(opt_options) { + this.__proto__.__proto__=new diff.DifferenceImageRGB(opt_options); +} + +diff.NormalizedDifferenceImageRGB.prototype.process=function(in_imgData, out_imgData) { + var previous_imgData = this.previous_ctxt. + getImageData(0, 0, this.width, this.height); + var w=0; + var min=[255,255,255], max=[0,0,0]; + for (var x=0; x<this.width ; x++) + for (var y=0; y<this.height ; y++) { + for (var i=0; i<3; i++) { + out_imgData.data[w+i] = Math.abs(in_imgData.data[w+i] - previous_imgData.data[w+i]); + if (min[i]>out_imgData.data[w+i]) min[i]=out_imgData.data[w+i]; + if (max[i]<out_imgData.data[w+i]) max[i]=out_imgData.data[w+i]; + } + out_imgData.data[w+3] = 255; + w+=4; + } + + w=0; + for (var x=0; x<this.width ; x++) + for (var y=0; y<this.height ; y++) { + for (var i=0; i<3; i++) { + out_imgData.data[w+i] = (out_imgData.data[w+i]-min[i])*255/(max[i]-min[i]); + } + out_imgData.data[w+3] = 255; + w+=4; + } + this.previous_ctxt.putImageData(in_imgData,0,0); +} diff --git a/processing.js b/processing.js index 0f262d9a44da8f77bfc0f710fde4e0fa518e1466..675e365e73a75b2e2a61ff400d65c6368505fa19 100644 --- a/processing.js +++ b/processing.js @@ -5,11 +5,14 @@ var processing=function(elementId,task,outputCanvasId) { this.imageData = {}; - this.processing_canvas=document.createElement('canvas'); - this.processing_canvas.width = this.width; - this.processing_canvas.height = this.height; - - this.processing_context=this.processing_canvas.getContext("2d"); + if (this.element.nodeName.toLowerCase()!="canvas") { + this.processing_canvas=document.createElement('canvas'); + this.processing_canvas.width = this.width; + this.processing_canvas.height = this.height; + this.processing_context=this.processing_canvas.getContext("2d"); + } else { + this.processing_canvas=this.element; + } this.task=task; @@ -30,8 +33,7 @@ processing.prototype.acquire_data_from_video=function() { } processing.prototype.acquire_data_from_canvas=function() { - this.processing_context.drawImage(this.element,0,0,this.width,this.height); - this.imageData = this.processing_context.getImageData(0, 0, this.width, this.height); + this.imageData = this.this.processing_canvas.getContext("2d").getImageData(0, 0, this.width, this.height); } processing.prototype.acquire_data=function() { diff --git a/processing2.js b/processing2.js index eba599fefdcc55278cb3fcaa1b54f5d1fc3c06dc..0daf79213dbe249e9f13080d3218e933cbf967e7 100644 --- a/processing2.js +++ b/processing2.js @@ -8,12 +8,15 @@ var processing2=function(elementId,task,outputCanvasId,opt_options) { this.in_imageData = {}; this.out_imageData = {}; - this.processing_canvas=document.createElement('canvas'); - this.processing_canvas.width = this.width; - this.processing_canvas.height = this.height; + if (this.element.nodeName.toLowerCase()!="canvas") { + this.processing_canvas=document.createElement('canvas'); + this.processing_canvas.width = this.width; + this.processing_canvas.height = this.height; + } else { + this.processing_canvas=this.element; + } this.processing_context=this.processing_canvas.getContext("2d"); - this.task=task; @@ -46,13 +49,14 @@ processing2.prototype.acquire_data_from_video=function() { } processing2.prototype.acquire_data_from_canvas=function() { - this.processing_context.drawImage(this.element,0,0,this.width,this.height); this.in_imageData = this.processing_context.getImageData(this.in_region.x, this.in_region.y, this.in_region.width, this.in_region.height); } processing2.prototype.acquire_data=function() { - if (this.output_canvas) + if (this.output_canvas) { this.out_imageData=this.output_context.getImageData(this.out_region.x,this.out_region.y,this.out_region.width,this.out_region.height); + this.out_imageData.ctxt=this.output_context; + } switch (this.element.nodeName.toLowerCase()) { case 'canvas': @@ -64,6 +68,7 @@ processing2.prototype.acquire_data=function() { default: throw new Error('Element not supported!'); } + this.in_imageData.ctxt = this.processing_context; } processing2.prototype.do_process=function() { diff --git a/samples/blobs_appearance.html b/samples/blobs_appearance.html new file mode 100644 index 0000000000000000000000000000000000000000..5db9f99fa188095ecc9f87a88b19d1c61c54aea8 --- /dev/null +++ b/samples/blobs_appearance.html @@ -0,0 +1,38 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> +<script lang="js" src="../effects/focus.js"></script> +<script lang="js" src="../metrics/generic.js"></script> +<script lang="js" src="../metrics/pixels.js"></script> +<script lang="js" src="../segmentation/blobs.js"></script> +</head> +<body> + + <img id="input" src="../data/rgb.png" width="50" height="50"></img> + <canvas id="output" width="50" height="50"></canvas> + + + + <script lang="javascript"> + var _opt_options={metric:pixel_metrics.gray_edist,threshold:10}; + var _task1=new blobs.Pixel8ConnectivityBlobs(_opt_options); + var _proc1=new processing2("input",_task1,"output"); + _proc1.do_process(); + var components_bbox=_proc1.get_result(); + + /* + var cvs_ctxt=document.getElementById("output").getContext("2d"); + console.log(components_bbox); + for (idx in components_bbox) { + cvs_ctxt.strokeStyle=[components_bbox[idx].mean[0], + components_bbox[idx].mean[1],components_bbox[idx].mean[2],255]; + cvs_ctxt.strokeRect( + components_bbox[idx].x, components_bbox[idx].y, + components_bbox[idx].width, components_bbox[idx].height, + ); + }*/ + </script> +</body> +</html> diff --git a/samples/blobs_diff_img_dilatation_erosion.html b/samples/blobs_diff_img_dilatation_erosion.html new file mode 100644 index 0000000000000000000000000000000000000000..bf7ae5698f6810996772c40aa4cd5872e8bf60c7 --- /dev/null +++ b/samples/blobs_diff_img_dilatation_erosion.html @@ -0,0 +1,71 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> +<script lang="js" src="../effects/focus.js"></script> +<script lang="js" src="../metrics/generic.js"></script> +<script lang="js" src="../metrics/pixels.js"></script> +<script lang="js" src="../effects/generic.js"></script> +<script lang="js" src="../effects/morpho.js"></script> +<script lang="js" src="../filters/morpho.js"></script> +<script lang="js" src="../filters/linear.js"></script> + +<script lang="js" src="../segmentation/blobs.js"></script> +</head> +<body> + + <img id="input" src="../data/16_03.jpg" width="320" height="180"></img> + <canvas id="output1" width="320" height="180"></canvas><br></br> + <canvas id="output2" width="320" height="180" style="display:none"></canvas> + <canvas id="output3" width="320" height="180"></canvas> + <canvas id="output4" width="320" height="180"></canvas> + + + <script lang="javascript"> + var _focus_options={focus_x:50,focus_y:50,focus_dx:5,focus_dy:5,focus_radius:50,width:320,height:180}; + var _task1=new focus.MovingFocus(_focus_options); + var _proc1=new processing2("input",_task1,"output1"); + + var _opt_options={width:320,height:180}; + var _task2=new diff.NormalizedDifferenceImageRGB(_opt_options); + var _proc2=new processing2("output1",_task2,"output2"); + + var _opt_options={dilatation_width:8,dilatation_height:8, + erosion_width:2,erosion_height:2}; + var _task3=new morpho_effects.DilatationErosionWindow(_opt_options); + var _proc3=new processing2("output2",_task3,"output3"); + + var _opt_options={metric:pixel_metrics.visible_edist,threshold:0}; + var _task4=new blobs.Pixel8ConnectivityBlobs(_opt_options); + var _proc4=new processing2("output3",_task4,"output4"); + + var img_elt=document.getElementById("input"); + img_elt.addEventListener("loadeddata",function(){ + _task2.set_first_frame_from_eltId("input"); + }); + var count=0; + var cvs4=document.getElementById("output4"); + var cvs4_ctxt=cvs4.getContext("2d"); + var loop=function() { + _proc1.do_process(); + _task1.updateFocus(); + + _proc2.do_process(); + _proc3.do_process(); + + cvs4_ctxt.beginPath(); + cvs4_ctxt.clearRect(0,0,cvs4.width,cvs4.height); + _proc4.do_process(); + + count++; + + if (count<1000) + setTimeout(loop,200); + } + + loop(); + + </script> +</body> +</html> diff --git a/samples/blobs_diff_img_no_normalization.html b/samples/blobs_diff_img_no_normalization.html new file mode 100644 index 0000000000000000000000000000000000000000..d39b7bde53674d460ea59a0421ca1a4c4777a316 --- /dev/null +++ b/samples/blobs_diff_img_no_normalization.html @@ -0,0 +1,61 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> +<script lang="js" src="../movement/detection.js"></script> +<script lang="js" src="../effects/focus.js"></script> +<script lang="js" src="../metrics/generic.js"></script> +<script lang="js" src="../metrics/pixels.js"></script> +<script lang="js" src="../segmentation/blobs.js"></script> +</head> +<body> + + <img id="input" src="../data/16_03.jpg" width="320" height="180"></img> + <canvas id="output1" width="320" height="180"></canvas> + <canvas id="output2" width="320" height="180"></canvas> + <canvas id="output3" width="320" height="180"></canvas> + + + <script lang="javascript"> + var _focus_options={focus_x:20,focus_y:20,focus_radius:20,width:320,height:180}; + var _task1=new focus.MovingFocus(_focus_options); + var _proc1=new processing2("input",_task1,"output1"); + + var _opt_options={width:320,height:180}; + var _task2=new diff.DifferenceImageRGB(_opt_options); + var _proc2=new processing2("output1",_task2,"output2"); + + var _opt_options={metric:pixel_metrics.visible_edist,threshold:0}; + var _task3=new blobs.Pixel8ConnectivityBlobs(_opt_options); + var _proc3=new processing2("output2",_task3,"output3"); + + var img_elt=document.getElementById("input"); + img_elt.addEventListener("loadeddata",function(){ + _task2.set_first_frame_from_eltId("input"); + }); + + var cvs3=document.getElementById("output3"); + var cvs3_ctxt=cvs3.getContext("2d"); + + var count=0; + var loop=function() { + _proc1.do_process(); + _task1.updateFocus(); + + _proc2.do_process(); + + cvs3_ctxt.beginPath(); + cvs3_ctxt.clearRect(0,0,cvs3.width,cvs3.height); + _proc3.do_process(); + count++; + + if (count<100) + setTimeout(loop,100); + } + + loop(); + + </script> +</body> +</html> diff --git a/samples/blobs_diff_img_with_normalization.html b/samples/blobs_diff_img_with_normalization.html new file mode 100644 index 0000000000000000000000000000000000000000..8fd06614323158694e93120a64a448c6f4fcf05b --- /dev/null +++ b/samples/blobs_diff_img_with_normalization.html @@ -0,0 +1,61 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> +<script lang="js" src="../effects/focus.js"></script> +<script lang="js" src="../metrics/generic.js"></script> +<script lang="js" src="../metrics/pixels.js"></script> +<script lang="js" src="../segmentation/blobs.js"></script> +</head> +<body> + + <img id="input" src="../data/16_03.jpg" width="320" height="180"></img> + <canvas id="output1" width="320" height="180"></canvas><br></br> + <canvas id="output2" width="320" height="180"></canvas> + <canvas id="output3" width="320" height="180"></canvas> + + + + <script lang="javascript"> + var _focus_options={focus_x:50,focus_y:50,focus_radius:50,width:320,height:180}; + var _task1=new focus.MovingFocus(_focus_options); + var _proc1=new processing2("input",_task1,"output1"); + + var _opt_options={width:320,height:180}; + var _task2=new diff.NormalizedDifferenceImageRGB(_opt_options); + var _proc2=new processing2("output1",_task2,"output2"); + + var _opt_options={metric:pixel_metrics.visible_edist,threshold:0}; + var _task3=new blobs.Pixel8ConnectivityBlobs(_opt_options); + var _proc3=new processing2("output2",_task3,"output3"); + + var img_elt=document.getElementById("input"); + img_elt.addEventListener("loadeddata",function(){ + _task2.set_first_frame_from_eltId("input"); + }); + + var cvs3=document.getElementById("output3"); + var cvs3_ctxt=cvs3.getContext("2d"); + + var count=0; + var loop=function() { + _proc1.do_process(); + _task1.updateFocus(); + + _proc2.do_process(); + + cvs3_ctxt.beginPath(); + cvs3_ctxt.clearRect(0,0,cvs3.width,cvs3.height); + _proc3.do_process(); + count++; + console.log(count); + if (count<100) + setTimeout(loop,200); + } + + loop(); + + </script> +</body> +</html> diff --git a/samples/difference_animation.html b/samples/difference_animation.html new file mode 100644 index 0000000000000000000000000000000000000000..c42fcd155435905a40e0264ca58d2c3b2af56f5f --- /dev/null +++ b/samples/difference_animation.html @@ -0,0 +1,43 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> +<script lang="js" src="../effects/focus.js"></script> +<script lang="js" src="../features/generic.js"></script> +<script lang="js" src="../features/pixels.js"></script> +<script lang="js" src="../metrics/generic.js"></script> +<script lang="js" src="../metrics/pixels.js"></script> +<script lang="js" src="../animation/moving_circle.js"></script> +<script lang="js" src="../tracking/appearance.js"></script> +</head> +<body><!--style="background-color:black; vertical-align: top;"--> + + <canvas id="input" width="300" height="200" style="vertical-align: top;"></canvas> + <canvas id="output" width="300" height="200" style="vertical-align: top;"></canvas> + + + <script lang="javascript"> + var _opt_options={ + x0:40,y0:40,radius:40,step_x:10,step_y:10,random:true, + }; + var _task1=new animations.MovingCircle("input",_opt_options); + + var _opt_options={width:300,height:200,threshold:1}; + var _task2=new diff.NormalizedDifferenceImageRGB(_opt_options); + var _proc2=new processing2("input",_task2,"output"); + + _task1.draw(); + _task2.set_first_frame_from_eltId("input"); + + var loop=function() { + _task1.animate(); + _proc2.do_process(); + + setTimeout(loop,200); + } + loop(); + + </script> +</body> +</html> diff --git a/samples/difference_effect.html b/samples/difference_effect.html new file mode 100644 index 0000000000000000000000000000000000000000..23e89256b20a0ac3d7a6d2ce64c9cf4285de88cc --- /dev/null +++ b/samples/difference_effect.html @@ -0,0 +1,47 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> +<script lang="js" src="../effects/focus.js"></script> +</head> +<body> + + <img id="input" src="../data/16_05.jpg" width="320" height="180"></img> + <canvas id="output1" width="320" height="180"></canvas><br/> + <canvas id="output2" width="320" height="180"></canvas> + <canvas id="output3" width="320" height="180"></canvas> + + + <script lang="javascript"> + var _focus_options={focus_x:50,focus_y:50,focus_radius:50,width:320,height:180,random:true, + focus_dx:20,focus_dy:20}; + var _task1=new focus.MovingFocus(_focus_options); + var _proc1=new processing2("input",_task1,"output1"); + + var _opt_options={width:320,height:180,threshold:50}; + var _task2=new diff.DifferenceImageRGB(_opt_options); + var _proc2=new processing2("output1",_task2,"output2"); + + var _opt_options={width:320,height:180,threshold:1}; + var _task3=new diff.NormalizedDifferenceImageRGB(_opt_options); + var _proc3=new processing2("output1",_task3,"output3"); + + var img_elt=document.getElementById("input"); + img_elt.addEventListener("loadeddata",function(){ + _task2.set_first_frame_from_eltId("input"); + }); + var loop=function() { + _proc1.do_process(); + _task1.updateFocus(); + + _proc2.do_process(); + _proc3.do_process(); + + setTimeout(loop,200); + } + loop(); + + </script> +</body> +</html> diff --git a/samples/difference_video.html b/samples/difference_video.html new file mode 100644 index 0000000000000000000000000000000000000000..bbdbe06318c9a9b6a9bb4c5e3e60d009644eb9dd --- /dev/null +++ b/samples/difference_video.html @@ -0,0 +1,31 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> + +</head> +<body> + <video autoplay loop src="../data/surprise.mp4" id="input" width="320" height="180"></video> + <canvas id="output1" width="320" height="180"></canvas> + + <script lang="javascript"> + var _opt_options={width:320,height:180}; + + var _task1=new diff.DifferenceImageRGB(_opt_options); + var video_elt=document.getElementById("input"); + video_elt.addEventListener("loadeddata",function(){ + _task1.set_first_frame_from_eltId("input"); + }); + + var _proc1=new processing2("input",_task1,"output1"); + + var loop=function() { + _proc1.do_process(); + requestAnimationFrame(loop); + } + loop(); + + </script> +</body> +</html> diff --git a/samples/meanshift_changing_circle_and_bg_color.html b/samples/meanshift_changing_circle_and_bg_color.html new file mode 100644 index 0000000000000000000000000000000000000000..cf37095199a78e480c2ca434b32a89a6b0cfc7e5 --- /dev/null +++ b/samples/meanshift_changing_circle_and_bg_color.html @@ -0,0 +1,66 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> +<script lang="js" src="../effects/focus.js"></script> +<script lang="js" src="../features/generic.js"></script> +<script lang="js" src="../features/pixels.js"></script> +<script lang="js" src="../metrics/generic.js"></script> +<script lang="js" src="../metrics/pixels.js"></script> +<script lang="js" src="../animation/moving_circle.js"></script> +<script lang="js" src="../tracking/appearance.js"></script> +</head> +<body><!-- style="background-color:black;"--> + + <canvas id="input" width="150" height="100" style="vertical-align:top" ></canvas> + <canvas id="output2" width="250" height="400"></canvas> + + + <script lang="javascript"> + var _opt_options={ + x0:10,y0:10,radius:10,step_x:2,step_y:2,random:true, + fillColor0:[255,0,0,255], + fillColor1:[127,0,0,255], + fillColorNbSteps:100, + bgColor0:[127,0,0,255], + bgColor1:[255,0,0,255], + bgColorNbSteps:100 + }; + var _task1=new animations.MovingCircleHChangingColorChangingBackground("input",_opt_options); + + var _bbox={x0:0,y0:0,dx:20,dy:20}; + + var _tracking_options={ + window_width:40, + window_height:40, + step_x:3, + step_y:3, + update_model:false + }; + + var _task2=new appearance_tracking.MeanShift(_bbox, + pixels_features.mean_rgb_afactor_per_region, + pixel_metrics.pixel_edist, + _tracking_options); + + var _proc2=new processing2("input",_task2,"output2"); + + + var count=0; + _task1.draw(); + var loop=function() { + _proc2.do_process(); + _task1.animate(); + + count++; + + if (count<1000) + setTimeout(loop,100); + } + + loop(); + + </script> +</body> +</html> diff --git a/samples/meanshift_changing_circle_color.html b/samples/meanshift_changing_circle_color.html new file mode 100644 index 0000000000000000000000000000000000000000..3b8ebfb8b8dc24ee11c8e967a00fb5a092e0af4d --- /dev/null +++ b/samples/meanshift_changing_circle_color.html @@ -0,0 +1,62 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> +<script lang="js" src="../effects/focus.js"></script> +<script lang="js" src="../features/generic.js"></script> +<script lang="js" src="../features/pixels.js"></script> +<script lang="js" src="../metrics/generic.js"></script> +<script lang="js" src="../metrics/pixels.js"></script> +<script lang="js" src="../animation/moving_circle.js"></script> +<script lang="js" src="../tracking/appearance.js"></script> +</head> +<body><!--style="background-color:black; vertical-align: top;"--> + + <canvas id="input" width="150" height="100" style="vertical-align: top;"></canvas> + <canvas id="output2" width="250" height="700" style="vertical-align: top;"></canvas> + + + <script lang="javascript"> + var _opt_options={ + x0:10,y0:10,radius:10,step_x:2,step_y:2,random:true, + fillColor0:[255,0,0,255], + fillColor1:[0,255,0,255], + fillColorNbSteps:100 + }; + var _task1=new animations.MovingCircleHChangingColor("input",_opt_options); + + var _bbox={x0:0,y0:0,dx:20,dy:20}; + + var _tracking_options={ + window_width:60, + window_height:60, + step_x:3, + step_y:3, + update_model:false + }; + + var _task2=new appearance_tracking.MeanShift(_bbox, + pixels_features.mean_rgb_afactor_per_region, + pixel_metrics.pixel_edist,_tracking_options); + + var _proc2=new processing2("input",_task2,"output2"); + + + var count=0; + _task1.draw(); + var loop=function() { + _proc2.do_process(); + _task1.animate(); + + count++; + + if (count<1000) + setTimeout(loop,100); + } + + loop(); + + </script> +</body> +</html> diff --git a/samples/meanshift_circle.html b/samples/meanshift_circle.html new file mode 100644 index 0000000000000000000000000000000000000000..8dbf138186d7941987615f62399cf665a86f6b5e --- /dev/null +++ b/samples/meanshift_circle.html @@ -0,0 +1,56 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> +<script lang="js" src="../effects/focus.js"></script> +<script lang="js" src="../features/generic.js"></script> +<script lang="js" src="../features/pixels.js"></script> +<script lang="js" src="../metrics/generic.js"></script> +<script lang="js" src="../metrics/pixels.js"></script> +<script lang="js" src="../animation/moving_circle.js"></script> +<script lang="js" src="../tracking/appearance.js"></script> +</head> +<body> <!--style="background-color:black;" --> + + <canvas id="input" width="150" height="100" style="vertical-align:top" ></canvas> + <canvas id="output2" width="250" height="700"></canvas> + + + <script lang="javascript"> + var _opt_options={x0:10,y0:10,radius:10,step_x:2,step_y:2,random:true,fillStyle:"red"}; + var _task1=new animations.MovingCircle("input",_opt_options); + + var _bbox={x0:0,y0:0,dx:20,dy:20}; + + var _tracking_options={ + window_width:40, + window_height:40, + step_x:3, + step_y:3 + }; + + var _task2=new appearance_tracking.MeanShift(_bbox, + pixels_features.mean_rgb_afactor_per_region, + pixel_metrics.pixel_edist,_tracking_options); + + var _proc2=new processing2("input",_task2,"output2"); + + + var count=0; + _task1.draw(); + var loop=function() { + _proc2.do_process(); + _task1.animate(); + + count++; + + if (count<1000) + setTimeout(loop,100); + } + + loop(); + + </script> +</body> +</html> diff --git a/samples/morpho_dilatation.html b/samples/morpho_dilatation.html new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/samples/morpho_dilatation_erosion.html b/samples/morpho_dilatation_erosion.html new file mode 100644 index 0000000000000000000000000000000000000000..663b1f65d2f20b0da7cb386c9930d9cd4454e575 --- /dev/null +++ b/samples/morpho_dilatation_erosion.html @@ -0,0 +1,49 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> +<script lang="js" src="../metrics/generic.js"></script> +<script lang="js" src="../metrics/pixels.js"></script> +<script lang="js" src="../filters/morpho.js"></script> +<script lang="js" src="../effects/generic.js"></script> +<script lang="js" src="../effects/morpho.js"></script> +</head> +<body> + + <img id="input" src="../data/red_circle.gif" width="320" height="200"></img> + <canvas id="output1"></canvas> + <canvas id="output2"></canvas> + <br></br> + <img id="input2" src="../data/16_03.jpg" width="320" height="200"></img> + <canvas id="output3"></canvas> + <canvas id="output4"></canvas> + + + + <script lang="javascript"> + Tools.copySize("input","output1"); + Tools.copySize("input","output2"); + + var _opt_options={window_width:10,window_height:10}; + var _task1=new morpho_effects.DilatationWindow(_opt_options); + var _proc1=new processing2("input",_task1,"output1"); + _proc1.do_process(); + var _task2=new morpho_effects.ErosionWindow(_opt_options); + var _proc2=new processing2("output1",_task2,"output2"); + _proc2.do_process(); + + Tools.copySize("input2","output3"); + Tools.copySize("input2","output4"); + + var _opt_options={window_width:10,window_height:10}; + var _task3=new morpho_effects.DilatationWindow(_opt_options); + var _proc3=new processing2("input2",_task3,"output3"); + _proc3.do_process(); + var _task4=new morpho_effects.ErosionWindow(_opt_options); + var _proc4=new processing2("output3",_task4,"output4"); + _proc4.do_process(); + + </script> +</body> +</html> diff --git a/samples/mvt_detection_region.html b/samples/mvt_detection_region.html new file mode 100644 index 0000000000000000000000000000000000000000..37b331d61cebb17cbe985e382d3f70e20807b4b5 --- /dev/null +++ b/samples/mvt_detection_region.html @@ -0,0 +1,62 @@ +<html> +<head> +<script lang="js" src="../tools.js"></script> +<script lang="js" src="../processing2.js"></script> +<script lang="js" src="../movement/difference.js"></script> +<script lang="js" src="../movement/detection.js"></script> +<script lang="js" src="../effects/focus.js"></script> +<script lang="js" src="../metrics/generic.js"></script> +<script lang="js" src="../metrics/pixels.js"></script> +<script lang="js" src="../segmentation/blobs.js"></script> +</head> +<body> + + <img id="input" src="../data/16_03.jpg" width="320" height="180"></img> + <canvas id="output1" width="320" height="180"></canvas><br></br> + <canvas id="output2" width="320" height="180"></canvas> + <canvas id="output3" width="320" height="180"></canvas> + + + + <script lang="javascript"> + var _focus_options={focus_x:50,focus_y:50,focus_radius:50,width:320,height:180}; + var _task1=new focus.MovingFocus(_focus_options); + var _proc1=new processing2("input",_task1,"output1"); + + var _opt_options={width:320,height:180}; + var _task2=new diff.NormalizedDifferenceImageRGB(_opt_options); + var _proc2=new processing2("output1",_task2,"output2"); + + var _opt_options={bbox:{x0:100,y0:100,dx:50,dy:50}, + mvt_threshold:50,diff_threshold:30}; + var _task3=new mvt_detection.SimpleMvtDetectorRegion(_opt_options); + var _proc3=new processing2("output1",_task3,"output3"); + + var img_elt=document.getElementById("input"); + img_elt.addEventListener("loadeddata",function(){ + _task2.set_first_frame_from_eltId("input"); + _task5.set_first_frame_from_eltId("input"); + }); + + var cvs3=document.getElementById("output3"); + var cvs3_ctxt=cvs3.getContext("2d"); + + var count=0; + var loop=function() { + _proc1.do_process(); + _task1.updateFocus(); + + _proc2.do_process(); + + _proc3.do_process(); + + count++; + if (count<100) + setTimeout(loop,100); + } + + loop(); + + </script> +</body> +</html> diff --git a/segmentation/blobs.js b/segmentation/blobs.js new file mode 100644 index 0000000000000000000000000000000000000000..c2eee114de3a4515bd41d84783ce5bd9d934d99a --- /dev/null +++ b/segmentation/blobs.js @@ -0,0 +1,93 @@ +var blobs={} + +blobs.Pixel8ConnectivityBlobs=function(opt_options) { + this.metric = opt_options.metric; + this.threshold=opt_options && opt_options.threshold ? opt_options.threshold : 0; +} + +blobs.Pixel8ConnectivityBlobs.prototype.analyse_neighbours=function(in_imgData, x0, y0) { + w0=((y0*in_imgData.width)+x0)<<2; + var pixel0=[in_imgData.data[w0],in_imgData.data[w0+1],in_imgData.data[w0+2]]; + var local_connectivity=[] + for (var dy=-1;dy<1;dy++) { + if (0>(dy+y0) || (dy+y0)>=in_imgData.height) continue; + for (var dx=-1;dx<(dy<0?2:1);dx++) { + if (0>(dx+x0) || (dx+x0)>=in_imgData.width) continue; + var w=((y0+dy)*in_imgData.width+x0+dx)<<2; + if (!this.component[w>>2]) continue; + var pixel=[in_imgData.data[w],in_imgData.data[w+1],in_imgData.data[w+2]] + if (this.metric(pixel0,pixel) <= this.threshold) { + var component=this.components[this.component[w>>2]]; + if (local_connectivity.indexOf(component)==-1) { + local_connectivity.push(component); + } + } + } + } + local_connectivity.sort(function(a,b){return a<b?-1:((a==b)?0:1)}); + return local_connectivity; +} + +blobs.Pixel8ConnectivityBlobs.prototype.update_bbox=function(id,bbox,x,y,mean) { + if (bbox.x0>x) {bbox.dx+=bbox.x0-x; bbox.x0=x;} + if (bbox.y0>y) {bbox.dy+=bbox.y0-y; bbox.y0=y;} + if (bbox.x0+bbox.dx<x) bbox.dx=x-bbox.x0; + if (bbox.y0+bbox.dy<y) bbox.dy=y-bbox.y0; + bbox.count++; + for (i=0;i<3;i++) + bbox.mean[i]=((bbox.count-1)*bbox.mean[i]+mean[i])/bbox.count; + return bbox; +} + +blobs.Pixel8ConnectivityBlobs.prototype.process=function(in_imgData,out_imgData) { + var blobs_arr=[] + this.component=[]; this.components=[]; + this.nb_components=0; + var w=0; + for (var y=0; y < in_imgData.height; y++) { + for (var x=0; x < in_imgData.width; x++) + { + w=(y*in_imgData.width)+x; + var neighbours_connectivity=this.analyse_neighbours(in_imgData, x, y); + if (neighbours_connectivity.length==1) { + this.component[w]=(neighbours_connectivity[0]); + } else if (neighbours_connectivity.length>1) { + for (var i=1;i<neighbours_connectivity.length;i++) { + this.components[neighbours_connectivity[i]]= + this.components[neighbours_connectivity[0]]; + } + this.component[w]=(this.components[neighbours_connectivity[0]]); + } else { + this.components[this.nb_components]=this.nb_components; + this.component[w]=(this.nb_components); + this.nb_components++; + } + } + } + + var w=0, w4=0; + var components_bbox=[]; + for (var y=0; y < in_imgData.height; y++) + for (var x=0; x < in_imgData.width; x++,w++,w4+=4) + { + this.component[w]=this.components[this.component[w]]; + if (components_bbox[this.component[w]]) + components_bbox[this.component[w]]= + this.update_bbox( + this.component[w], + components_bbox[this.component[w]], + x,y, + [in_imgData.data[w4],in_imgData.data[w4+1],in_imgData.data[w4+2]]); + else + components_bbox[this.component[w]]={ + x0:x,y0:y,dx:0,dy:0, + count:1,mean:[in_imgData.data[w4],in_imgData.data[w4+1], + in_imgData.data[w4+2]]}; + } + if (out_imgData.data) { + for (idx in components_bbox) { + Tools.strokeBBox_on_imageData(out_imgData,components_bbox[idx],components_bbox[idx].mean); + } + } + return components_bbox; +} diff --git a/tools.js b/tools.js index 40860ad494c3a8c5dc8e81e5e3fe97ed2b1ae75a..55320f514eaeb52acffb02558e913b2c5720bd6d 100644 --- a/tools.js +++ b/tools.js @@ -59,6 +59,21 @@ Tools.copy_imageData_into_imageData=function(src_imageData,dest_imageData,x0,y0) } } +Tools.copy_partial_imageData_into_imageData= +function(src_imageData,x0,y0,dx,dy,dest_imageData,x1,y1) { + + for (var src_y=y0; src_y<Math.min(y0+dy,src_imageData.height); src_y++) + for (var src_x=x0; src_x < Math.min(x0+dx,src_imageData.width); src_x++) { + var w_src=(src_y*src_imageData.width+src_x)<<2; + w_out=((y1+(src_y-y0))*dest_imageData.width+(x1+(src_x-x0)))<<2; + dest_imageData.data[w_out]=src_imageData.data[w_src]; + dest_imageData.data[w_out+1]=src_imageData.data[w_src+1]; + dest_imageData.data[w_out+2]=src_imageData.data[w_src+2]; + dest_imageData.data[w_out+3]=src_imageData.data[w_src+3]; + + } +} + Tools.get_region_from_imageData=function(src_imageData,x0,y0,dx,dy) { var reg_imageData=src_imageData.ctxt.getImageData( src_imageData.orig_x+x0,src_imageData.orig_y+y0, @@ -70,3 +85,35 @@ Tools.get_region_from_imageData=function(src_imageData,x0,y0,dx,dy) { return reg_imageData; } + +Tools.strokeBBox_on_imageData=function(imgData,bbox,col) { + Tools.strokeRect_on_imageData(imgData,bbox.x0,bbox.y0,bbox.dx,bbox.dy,col); +} + +Tools.strokeRect_on_imageData=function(imgData,x0,y0,width,height,col) { + var w; + + for (var x=x0;x<x0+width;x++) { + w=((y0)*imgData.width+x)<<2; + imgData.data[w]=col[0];imgData.data[w+1]=col[1];imgData.data[w+2]=col[2]; + imgData.data[w+3]=255; + w=((y0+height)*imgData.width+x)<<2; + imgData.data[w]=col[0];imgData.data[w+1]=col[1];imgData.data[w+2]=col[2]; + imgData.data[w+3]=255; + } + for (var y=y0;y<y0+height;y++) { + w=(y*imgData.width+x0)<<2; + imgData.data[w]=col[0];imgData.data[w+1]=col[1];imgData.data[w+2]=col[2]; + imgData.data[w+3]=255; + w=(y*imgData.width+x0+width)<<2; + imgData.data[w]=col[0];imgData.data[w+1]=col[1];imgData.data[w+2]=col[2]; + imgData.data[w+3]=255; + } +} + +Tools.copySize=function(elt_id_1,elt_id_2) { + var elt1=document.getElementById(elt_id_1); + var elt2=document.getElementById(elt_id_2); + elt2.width=elt1.width; + elt2.height=elt1.height; +} diff --git a/tracking/appearance.js b/tracking/appearance.js new file mode 100644 index 0000000000000000000000000000000000000000..0a98aecddfaddb460b90692279d59f269c5de12f --- /dev/null +++ b/tracking/appearance.js @@ -0,0 +1,87 @@ +var appearance_tracking={} + +appearance_tracking.MeanShift=function(bbox,per_region_feature_func,metric_func,opt_options){ + this.bbox=bbox; + this.window_width=opt_options&&opt_options.window_width?opt_options.window_width:3*bbox.dx; + this.window_height=opt_options&&opt_options.window_height?opt_options.window_height:3*bbox.dy; + this.step_x=opt_options&&opt_options.step_x?opt_options.step_x:bbox.dx/3; + this.step_y=opt_options&&opt_options.step_y?opt_options.step_y:bbox.dy/3; + this.per_region_feature_func=per_region_feature_func; + this.metric_func=metric_func; + this.threshold=opt_options&&opt_options.threshold?opt_options.threshold:Number.MAX_SAFE_INTEGER; + this.count=0; + this.update_model=opt_options&&opt_options.update_model?opt_options.update_model:false; +} + +appearance_tracking.MeanShift.prototype.process=function(in_imgData,out_imgData) { + var in_width=in_imgData.width, in_height=in_imgData.height; + + if (!this.bbox_feature) { + var _opt_options=this.bbox; + this.bbox_feature=this.per_region_feature_func(in_imgData,_opt_options); + } + + this.pan_x=Math.round(this.window_width/2-this.bbox.dx/2); + this.pan_y=Math.round(this.window_height/2-this.bbox.dy/2); + + var x_start=(this.bbox.x0-this.pan_x)>0?(this.bbox.x0-this.pan_x):0; + var y_start=(this.bbox.y0-this.pan_y)>0?(this.bbox.y0-this.pan_y):0; + var x_end=(this.bbox.x0+this.bbox.dx+this.pan_x) < in_width? + (this.bbox.x0+this.pan_x) + :(in_width-this.bbox.dx); + var y_end=(this.bbox.y0+this.bbox.dy+this.pan_y)<in_height? + this.bbox.y0+this.pan_y + :(in_height-this.bbox.dy); + + var min=Number.MAX_SAFE_INTEGER; + var min_bbox,min_bbox_feature; + var count_y=in_imgData.height; + + if (out_imgData) { + Tools.copy_partial_imageData_into_imageData(in_imgData, + 0,0,in_imgData.width,in_imgData.height, + out_imgData,0,0); + Tools.strokeBBox_on_imageData(out_imgData,{x0:x_start,y0:y_start,dx:x_end-x_start,dy:y_end-y_start},[64,64,64,255]); + } + + for (var y=y_start;y<y_end;y+=this.step_y) { + var count_x=0; + for (var x=x_start;x<x_end;x+=this.step_x) { + var local_bbox={ + x0:(x+this.bbox.dx<in_width)?x:in_width-this.bbox.dx, + y0:(y+this.bbox.dy<in_height)?y:in_height-this.bbox.dy, + dx:this.bbox.dx, + dy:this.bbox.dy + } + if (out_imgData) { + Tools.copy_partial_imageData_into_imageData(in_imgData, + local_bbox.x0,local_bbox.y0,local_bbox.dx,local_bbox.dy, + out_imgData,count_x,count_y); + Tools.strokeBBox_on_imageData(out_imgData,local_bbox,[0,64,64,255]); + } + + var local_bbox_feature=this.per_region_feature_func(in_imgData,local_bbox); + if (!local_bbox_feature) continue; + var fun=function(a,b) {return a-b;} + var diff=this.metric_func(this.bbox_feature,local_bbox_feature); + if (diff < min) { + min=diff; + min_bbox=local_bbox; + min_bbox_feature=local_bbox_feature; + } + count_x+=this.bbox.dx; + } + count_y+=this.bbox.dy; + } + + if (min < this.threshold && this.bbox!=min_bbox) { + this.count++; + this.bbox=min_bbox; + if (this.update_model) + this.bbox_feature=min_bbox_feature; + } + if (out_imgData) { + Tools.strokeBBox_on_imageData(out_imgData,this.bbox,[255,0,255,255]); + } + return this.bbox; +}