summaryrefslogtreecommitdiff
path: root/smartblur.js
blob: 1a6cebbb1f75fbdc7450bf8c8722b320ae4b20db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// http://asserttrue.blogspot.ca/2010/08/implementing-smart-blur-in-java.html
SmartBlurFilter = function () {
  var SENSITIVITY = 10
  var REGION_SIZE = 5
  var edge_zero_fill = true

  var kx = -((REGION_SIZE/2)|0), ky = -((REGION_SIZE/2)|0), klen = REGION_SIZE*REGION_SIZE

  var kernelArray = [
    1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1
  ]

  var kernel = new Kernel(9, 9, kernelArray)
  kernel.normalize()

  function lerp(a,b,amt) {
    return a + amt * (b - a);
  }

  function rmsError(src, i, size) {
    var ave = 0;

    var len = size*size, data = src.data

    var ix, iy
    var r,g,b,a
    var w = src.width, h = src.height
    ix = i%w
    iy = (i/w)|0
    it = i * 4
    r = g = b = a = 0
    for (var j = 0; j < klen; j++) {
      jx = ix +  j%size + kx
      jy = iy + ((j/size)|0) + ky
      jt = 4 * (jx + jy*w)
      if (0 > jx || jx > w || 0 > jy || jy > h) {
        if (edge_zero_fill) {
          continue
        }
        jt = it
      }
      ave += data[jt]
//       g += data[jt+1]
//       b += data[jt+2]
//       a += data[jt+3]
    }
    ave /= klen
//     g /= klen
//     b /= klen
//     a /= klen

    var diff = 0
    var accumulator = 0

    for (var j = 0; j < klen; j++) {
      jx = ix +  j%size + kx
      jy = iy + ((j/size)|0) + ky
      jt = 4 * (jx + jy*w)
      if (0 > jx || jx > w || 0 > jy || jy > h) {
        if (edge_zero_fill) {
          continue
        }
        jt = it
      }
      diff = data[ jt ] - ave
      diff *= diff
      accumulator += diff
    }

    var rms = accumulator / klen/10
    rms = Math.sqrt(rms) // / 255
    return rms
  }

  function lerpPixel( src, dest, blur, i, amt) {
    dest[i]   = lerp(src[i],   blur[i],   amt)
    dest[i+1] = lerp(src[i+1], blur[i+1], amt)
    dest[i+2] = lerp(src[i+2], blur[i+2], amt)
    dest[i+3] = lerp(src[i+3], blur[i+3], amt)
  }

  function blurImage( src, dest, blur, sensitivity ) {
    var newPixel = 0
    var amt = 0
    var size = REGION_SIZE
    var w = src.width, rms

    var srcData = src.data
    var destData = dest.data
    var blurData = blur.data
    for (var i = 0, len = srcData.length/4; i < len; i++) {

      rms = rmsError(src, i, size)
      amt = rms < sensitivity ? rms/sensitivity : 1.0
if (1000<i&&i<1100)console.log(rms,amt)
      lerpPixel( srcData, destData, blurData, i*4, rms)
    }
    
    return dest
  }

  this.blur = function (src_canvas, dest_canvas) {
    var convolver = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);

    // clone image into target
    if (! dest_canvas) dest_canvas = document.createElement("canvas")
    var w = dest_canvas.width = src_canvas.width
    var h = dest_canvas.height = src_canvas.height
    var srcctx = src_canvas.getContext('2d')
    var destctx = dest_canvas.getContext('2d')

    destctx.drawImage(src_canvas, 0, 0);

    // get source pixels
    var src = srcctx.getImageData(0,0,w,h)
    var dest = destctx.getImageData(0,0,w,h)

    // blur the cloned image
    dest = convolver.filter(src, dest);

    destctx.putImageData(dest, 0,0)
    var blurred = destctx.getImageData(0,0,w,h)
    var dest = srcctx.getImageData(0,0,w,h)
    dest = blurImage(src, dest, blurred, SENSITIVITY)
    destctx.putImageData(dest, 0,0)

    return dest_canvas
  }
}