1 /** 2 * @author jerome etienne jerome.etienne@gmail.com 3 * @author Djordje Lukic lukic.djordje@gmail.com 4 */ 5 6 /** 7 * @namespace 8 */ 9 var ImgProc = {}; 10 11 /** 12 * horizontal flip 13 */ 14 ImgProc.fliph = function(imageData) 15 { 16 var p = imageData.data; 17 var w = imageData.width; 18 var h = imageData.height; 19 var tmp; 20 for(var y = 0; y < h; y++){ 21 for(var x = 0; x < w/2; x++){ 22 var i1 = (x + y*w)*4; 23 var i2 = ((w-1-x) + y*w)*4; 24 // swap red 25 tmp = p[i1+0]; 26 p[i1+0] = p[i2+0]; 27 p[i2+0] = tmp; 28 // swap red 29 tmp = p[i1+1]; 30 p[i1+1] = p[i2+1]; 31 p[i2+1] = tmp; 32 // swap green 33 tmp = p[i1+2]; 34 p[i1+2] = p[i2+2]; 35 p[i2+2] = tmp; 36 // swap alpha 37 tmp = p[i1+3]; 38 p[i1+3] = p[i2+3]; 39 p[i2+3] = tmp; 40 } 41 } 42 } 43 44 /** 45 * Convert the image to luminance 46 */ 47 ImgProc.luminance = function(imageData, ratio) 48 { 49 ratio = ratio !== undefined ? ratio : 1.0; 50 var p = imageData.data; 51 var w = imageData.width; 52 var h = imageData.height; 53 for(var i = 0, y = 0; y < h; y++){ 54 for(var x = 0; x < w; x++, i += 4){ 55 var luminance = (0.2126*(p[i+0]/255)) + (0.7152*(p[i+1]/255)) + (0.0722*(p[i+2]/255)) 56 luminance = Math.floor(luminance*ratio*255); 57 p[i+0] = luminance; 58 p[i+1] = luminance; 59 p[i+2] = luminance; 60 } 61 } 62 } 63 64 /** 65 * Duplicate the ImgData 66 */ 67 ImgProc.duplicate = function(srcImgData, ctx) 68 { 69 return ImgProc.copy(srcImgData, ctx.createImageData(srcImgData)) 70 71 /** old but tested version 72 var dstImgData = ctx.createImageData(srcImgData); 73 var pSrc = srcImgData.data; 74 var pDst = dstImgData.data; 75 for(var i = 0; i < pSrc.length; i++){ 76 pDst[i] = pSrc[i]; 77 } 78 return dstImgData; 79 */ 80 } 81 82 /** 83 * Copy the ImgData 84 */ 85 ImgProc.copy = function(srcImgData, dstImgData) 86 { 87 console.assert(srcImgData.width === dstImgData.width ); 88 console.assert(srcImgData.height === dstImgData.height ); 89 var pSrc = srcImgData.data; 90 var pDst = dstImgData.data; 91 for(var i = 0; i < pSrc.length; i++){ 92 pDst[i] = pSrc[i]; 93 } 94 return dstImgData; 95 } 96 97 ////////////////////////////////////////////////////////////////////////////////// 98 // // 99 ////////////////////////////////////////////////////////////////////////////////// 100 101 /** 102 * Sobel operator 103 * 104 * see http://en.wikipedia.org/wiki/Sobel_operator 105 */ 106 ImgProc.sobel = function(srcImgProc, dstImgProc) 107 { 108 var pSrc= srcImgProc.data; 109 var w = srcImgProc.width; 110 var h = srcImgProc.height; 111 var pDst= dstImgProc.data; 112 // TODO make be optimized http://en.wikipedia.org/wiki/Sobel_operator#Technical_details 113 var sobel_x = [ [-1,0,1], 114 [-2,0,2], 115 [-1,0,1] 116 ]; 117 var sobel_y = [ [-1,-2,-1], 118 [ 0, 0, 0], 119 [ 1, 2, 1] 120 ]; 121 122 var greyAt = function(p, x, y) { 123 var offset = x * 4 + y * w * 4; 124 return p[offset] * 0.3 + p[offset + 1] * 0.59 + p[offset + 2] * 0.11; 125 } 126 // TODO instead of recomputing the index everytime, make it incremental 127 for(var x = 1; x < w - 2; x++) { 128 for(var y = 1; y < h - 2; y++) { 129 var px = (sobel_x[0][0] * greyAt(pSrc, x-1,y-1)) + (sobel_x[0][1] * greyAt(pSrc, x,y-1)) + (sobel_x[0][2] * greyAt(pSrc, x+1,y-1)) + 130 (sobel_x[1][0] * greyAt(pSrc, x-1,y)) + (sobel_x[1][1] * greyAt(pSrc, x,y)) + (sobel_x[1][2] * greyAt(pSrc, x+1,y)) + 131 (sobel_x[2][0] * greyAt(pSrc, x-1,y+1)) + (sobel_x[2][1] * greyAt(pSrc, x,y+1)) + (sobel_x[2][2] * greyAt(pSrc, x+1,y+1)) 132 133 var py = (sobel_y[0][0] * greyAt(pSrc, x-1,y-1)) + (sobel_y[0][1] * greyAt(pSrc, x,y-1)) + (sobel_y[0][2] * greyAt(pSrc, x+1,y-1)) + 134 (sobel_y[1][0] * greyAt(pSrc, x-1,y)) + (sobel_y[1][1] * greyAt(pSrc, x,y)) + (sobel_y[1][2] * greyAt(pSrc, x+1,y)) + 135 (sobel_y[2][0] * greyAt(pSrc, x-1,y+1)) + (sobel_y[2][1] * greyAt(pSrc, x,y+1)) + (sobel_y[2][2] * greyAt(pSrc, x+1,y+1)) 136 137 var val = Math.ceil(Math.sqrt(px * px + py * py)); 138 139 var offset = y * w * 4 + x * 4; 140 pDst[offset+0] = val; 141 pDst[offset+1] = val; 142 pDst[offset+2] = val; 143 } 144 } 145 } 146 147 ////////////////////////////////////////////////////////////////////////////////// 148 // // 149 ////////////////////////////////////////////////////////////////////////////////// 150 151 ImgProc.threshold = function(imageData, r, g, b) 152 { 153 var p = imageData.data; 154 var w = imageData.width; 155 var h = imageData.height; 156 for(var i = 0, y = 0; y < h; y++){ 157 for(var x = 0; x < w; x++, i += 4){ 158 if( (p[i+0] >= r.min && p[i+0] <= r.max) 159 && (p[i+1] >= g.min && p[i+1] <= g.max) 160 && (p[i+2] >= b.min && p[i+2] <= b.max) ){ 161 }else{ 162 p[i+0] = p[i+1] = p[i+2] = 0; 163 } 164 } 165 } 166 } 167 168 ImgProc.convert = function(imageData, callback) 169 { 170 var p = imageData.data; 171 var w = imageData.width; 172 var h = imageData.height; 173 var i = 0; 174 for(var y = 0; y < h; y++){ 175 for(var x = 0; x < w; x++, i += 4){ 176 callback(p, i, x, y, imageData); 177 } 178 } 179 } 180 181 182 ////////////////////////////////////////////////////////////////////////////////// 183 // // 184 ////////////////////////////////////////////////////////////////////////////////// 185 186 ImgProc.hline = function(imageData, y, r, g, b, a) 187 { 188 var p = imageData.data; 189 var w = imageData.width; 190 var i = y * w * 4; 191 r = r !== undefined ? r : 255; 192 g = g !== undefined ? g : 255; 193 b = b !== undefined ? b : 255; 194 a = a !== undefined ? a : 255; 195 for(var x = 0; x < w; x++, i += 4){ 196 p[i+0] = r; 197 p[i+1] = g; 198 p[i+2] = b; 199 p[i+3] = a; 200 } 201 } 202 203 ImgProc.vline = function(imageData, x, r, g, b, a) 204 { 205 var p = imageData.data; 206 var w = imageData.width; 207 var h = imageData.height; 208 r = r !== undefined ? r : 255; 209 g = g !== undefined ? g : 255; 210 b = b !== undefined ? b : 255; 211 a = a !== undefined ? a : 255; 212 for(var i = x*4, y = 0; y < h; y++, i += w*4){ 213 p[i+0] = r; 214 p[i+1] = g; 215 p[i+2] = b; 216 p[i+3] = a; 217 } 218 } 219 220 ////////////////////////////////////////////////////////////////////////////////// 221 // // 222 ////////////////////////////////////////////////////////////////////////////////// 223 224 ImgProc.smoothHistogram = function(hist, factor) 225 { 226 var value = 0; 227 for(var i = 0; i < hist.length; i++ ){ 228 value += (hist[i] - value) * factor; 229 hist[i] = value; 230 } 231 } 232 233 ImgProc.windowedAverageHistogram = function(hist, width) 234 { 235 var halfW = Math.floor(width/2); 236 var winSum = 0; 237 var winLen = 0; 238 var origHist = new Array(hist.length); 239 for(var i = 0; i < hist.length; i++) origHist[i] = hist[i]; 240 241 // init window 242 for(var i = 0; i < halfW; i++){ 243 winSum += hist[i]; 244 winLen ++; 245 } 246 247 for(var i = 0; i < hist.length; i++){ 248 // update window forward 249 if( i + halfW < hist.length ){ 250 winSum += origHist[i+halfW]; 251 winLen ++; 252 } 253 // update window backward 254 if( i-halfW >= 0 ){ 255 winSum -= origHist[i-halfW]; 256 winLen --; 257 } 258 // compute the actual value 259 hist[i] = winSum/winLen; 260 } 261 } 262 263 ImgProc.getMaxHistogram = function(hist, imageData) 264 { 265 var max = -Number.MAX_VALUE; 266 var idx = 0; 267 for(var i = 0; i < hist.length; i++ ){ 268 if( hist[i] > max ){ 269 max = hist[i]; 270 idx = i; 271 } 272 } 273 return {max: max, idx: idx} 274 } 275 276 ImgProc.filterHistogram = function(hist, filterFn) 277 { 278 for(var i = 0; i < hist.length; i++ ){ 279 var val = hist[i]; 280 filterFn(val, i, hist); 281 } 282 } 283 284 ////////////////////////////////////////////////////////////////////////////////// 285 // // 286 ////////////////////////////////////////////////////////////////////////////////// 287 288 ImgProc.computeHorizontalHistogram = function(imageData, filter) 289 { 290 var p = imageData.data; 291 var w = imageData.width; 292 var h = imageData.height; 293 var hist= new Float64Array(w); 294 console.assert(filter, "invalid parameter") 295 for(var x = 0; x < w; x++){ 296 for(var i = x*4, y = 0; y < h; y++, i += w*4){ 297 if( filter(p, i, x, y, imageData) ) hist[x]++; 298 } 299 } 300 return hist; 301 } 302 303 ImgProc.displayHorizontalHistogram = function(imageData, hist) 304 { 305 var p = imageData.data; 306 var w = imageData.width; 307 var h = imageData.height; 308 console.assert(hist.length === w); 309 // create temporary cancas 310 var canvas = document.createElement('canvas'); 311 canvas.width = w; 312 canvas.height = h; 313 var ctx = canvas.getContext("2d"); 314 // display the histogram in the canvas 315 ctx.fillStyle = 'red'; 316 for(var x = 0; x < w; x++ ){ 317 ctx.fillRect(x, h-hist[x], 1, hist[x]); 318 } 319 // copy the canvas in imageData 320 var srcImgData = ctx.getImageData(0,0, canvas.width, canvas.height); 321 var pSrc = srcImgData.data; 322 var pDst = imageData.data; 323 for(var i = 0; i < pSrc.length; i += 4){ 324 if( pSrc[i+0] !== 0 || pSrc[i+1] !== 0 || pSrc[i+2] !== 0 ){ 325 pDst[i+0] = pSrc[i+0]; 326 pDst[i+1] = pSrc[i+1]; 327 pDst[i+2] = pSrc[i+2]; 328 } 329 pDst[i+3] = 255; 330 } 331 } 332 333 334 335 ////////////////////////////////////////////////////////////////////////////////// 336 // // 337 ////////////////////////////////////////////////////////////////////////////////// 338 339 ImgProc.computeVerticalHistogram = function(imageData, filter) 340 { 341 var p = imageData.data; 342 var w = imageData.width; 343 var h = imageData.height; 344 var hist= new Float64Array(h); 345 console.assert(filter, "invalid parameter") 346 for(var i = 0, y = 0; y < h; y++){ 347 for(var x = 0; x < w; x++, i += 4){ 348 if( filter(p, i, x, y, imageData) ) hist[y]++; 349 } 350 } 351 return hist; 352 } 353 354 ImgProc.displayVerticalHistogram = function(imageData, hist) 355 { 356 var p = imageData.data; 357 var w = imageData.width; 358 var h = imageData.height; 359 console.assert(hist.length === h); 360 // create temporary cancas 361 var canvas = document.createElement('canvas'); 362 canvas.width = w; 363 canvas.height = h; 364 var ctx = canvas.getContext("2d"); 365 // display the histogram in the canvas 366 ctx.fillStyle = 'red'; 367 for(var y = 0; y < h; y++ ){ 368 ctx.fillRect(0, y, hist[y], 1); 369 } 370 // copy the canvas in imageData 371 var srcImgData = ctx.getImageData(0,0, canvas.width, canvas.height); 372 var pSrc = srcImgData.data; 373 var pDst = imageData.data; 374 for(var i = 0; i < pSrc.length; i += 4){ 375 if( pSrc[i+0] !== 0 || pSrc[i+1] !== 0 || pSrc[i+2] !== 0 ){ 376 pDst[i+0] = pSrc[i+0]; 377 pDst[i+1] = pSrc[i+1]; 378 pDst[i+2] = pSrc[i+2]; 379 } 380 pDst[i+3] = 255; 381 } 382 } 383 384 385 ////////////////////////////////////////////////////////////////////////////////// 386 // // 387 ////////////////////////////////////////////////////////////////////////////////// 388 389 ImgProc.computeColorHistogram = function(imageData) 390 { 391 var p = imageData.data; 392 var w = imageData.width; 393 var h = imageData.height; 394 var hist= { 395 r : new Float64Array(256), 396 g : new Float64Array(256), 397 b : new Float64Array(256) 398 }; 399 400 for(var i = 0, y = 0; y < h; y++){ 401 for(var x = 0; x < w; x++, i += 4){ 402 hist.r[ p[i+0] ]++; 403 hist.g[ p[i+1] ]++; 404 hist.b[ p[i+2] ]++; 405 } 406 } 407 return hist; 408 } 409 410 ImgProc.normalizeColorHistogram = function(colorHistogram) 411 { 412 var hist= colorHistogram; 413 414 // get the max value 415 var max = -Number.MAX_VALUE; 416 for (var i = 0; i < 256; i++ ){ 417 max = Math.max(max, hist.r[i]); 418 max = Math.max(max, hist.g[i]); 419 max = Math.max(max, hist.b[i]); 420 } 421 // normalize the histogram 422 for (var i = 0; i < 256; i++ ){ 423 hist.r[i] /= max; 424 hist.g[i] /= max; 425 hist.b[i] /= max; 426 } 427 428 var sumR = 0; 429 var sumG = 0; 430 var sumB = 0; 431 for (var i = 0; i < 256; i++ ){ 432 sumR += hist.r[i]; 433 sumG += hist.g[i]; 434 sumB += hist.b[i]; 435 } 436 console.log("sum", max, sumR, sumG, sumB) 437 } 438 439 ImgProc.displayColorHistogram = function(imageData, colorHistogram) 440 { 441 var p = imageData.data; 442 var w = imageData.width; 443 var h = imageData.height; 444 var hist= colorHistogram; 445 446 var canvas = document.createElement('canvas'); 447 canvas.width = w; 448 canvas.height = h; 449 var ctx = canvas.getContext("2d"); 450 451 452 var barW = canvas.width / 256; 453 var barH = canvas.height / 3; 454 455 ctx.fillStyle = 'red'; 456 var barYOffset = 0 * barH; 457 for (var i = 0; i < 256; i++ ){ 458 var valH = Math.floor(hist.r[i]*barH); 459 ctx.fillRect(i*barW, barYOffset+(barH-valH), barW, valH); 460 } 461 462 ctx.fillStyle = 'green'; 463 var barYOffset = 1 * barH; 464 for (var i = 0; i < 256; i++ ){ 465 var valH = Math.floor(hist.g[i]*barH); 466 ctx.fillRect(i*barW, barYOffset+(barH-valH), barW, valH); 467 } 468 469 ctx.fillStyle = 'blue'; 470 var barYOffset = 2 * barH; 471 for (var i = 0; i < 256; i++ ){ 472 var valH = Math.floor(hist.b[i]*barH); 473 ctx.fillRect(i*barW, barYOffset+(barH-valH), barW, valH); 474 } 475 476 var srcImgData = ctx.getImageData(0,0, canvas.width, canvas.height); 477 var pSrc = srcImgData.data; 478 var pDst = imageData.data; 479 for(var i = 0; i < pSrc.length; i += 4){ 480 if( pSrc[i+0] !== 0 || pSrc[i+1] !== 0 || pSrc[i+2] !== 0 ){ 481 pDst[i+0] = pSrc[i+0]; 482 pDst[i+1] = pSrc[i+1]; 483 pDst[i+2] = pSrc[i+2]; 484 } 485 } 486 }; 487