title: How to implement Contrast-Limited Adaptive Histogram Equalization using CLAHE in opengl or webgl?
body: <p>I am trying to understand and apply CLAHE on my Grayscale and Color images. Also I found <a href="https://books.google.com.au/books?hl=en&amp;lr=&amp;id=CCqzMm_-WucC&amp;oi=fnd&amp;pg=PR11&amp;dq=Contrast+Limited+Adaptive+Histogram+Equalization+Graphics+Gems+IV&amp;ots=mtft15JJbl&amp;sig=acQg6XLt7jzqR0MjO6sYUa0Sjtc#v=onepage&amp;q=Contrast%20Limited%20Adaptive%20Histogram%20Equalization%20Graphics%20Gems%20IV&amp;f=false" rel="nofollow noreferrer">this book</a> on Page 478 , which helps in explaining CLAHE with some C++ code and <a href="http://www.cnblogs.com/mikewolf2002/archive/2012/04/10/2439870.html" rel="nofollow noreferrer">this tutorial</a>.</p>
  
  <p>I am very new to GLSL and unable to make a way with it . Can anyone help me to do that in OpenGL/WebGL? Also, I am not allowed to use OpenCV library.</p>
  
  <p><strong>Edit:</strong>
  <a href="http://www.sci.utah.edu/~cscheid/spr05/imageprocessing/project1/" rel="nofollow noreferrer">This link</a> is even more comprehensive.  </p>
  
  <p>Even <a href="https://stackoverflow.com/questions/37527102/how-do-you-compute-a-histogram-in-webgl">this</a> is another perfect solution to Adaptive histogram but I needed the image by getting the ability to change the histogram.</p>
  
  <p>Edit 2:
  Somehow I was able to get CLAHE working in canvas 2d which obviously uses CPU, referring to this can anybody hep me figure out how to do the same in GPU?</p>
  
  <p>Here's the <a href="https://jsfiddle.net/Subhasish2015/abwLtneb/1/" rel="nofollow noreferrer">fiddle</a></p>
  
  <p>Code :
  `</p>
  
  <pre><code>var iml = document.getElementById('imageLoader');
  iml.addEventListener('change', handleImage, false);
  
  var cnv = document.getElementById('imageCanvas');
  var ctx = cnv.getContext('2d');
  var cnvori = document.getElementById('imageOriginalCanvas')
  var ctxori = cnvori.getContext('2d');
  
  function handleImage(e){
      var reader = new FileReader();
      reader.onload = function(event){
          var img = new Image;
  
          img.onload = function(){
  
              cnvori.width = cnv.width = img.width;
              cnvori.height = cnv.height = img.height;
              ctx.drawImage(img,0,0,img.width,img.height);
              ctxori.drawImage(img,0,0,img.width,img.height);
  
              var imgData = ctx.getImageData(0,0,img.width,img.height);
  
  
              var min=255, max=0;
              var intens=[];
              var levels=[];
              var j=0;
  
              for (var i=0;i&lt;imgData.data.length;i+=4) {
                  intens[j] = (imgData.data[i]+imgData.data[i+1]+imgData.data[i+2] )/3;
                  j++;
              }
  
              var tilesize = [32, 32];
              var clipLimit = 0.1;
  
              // number of bins
              var num_bins = 256;
  
              var h = img.height;
  
              var w = img.width;
              // number of tiles in x and y direction
              var xtiles = Math.ceil(w / tilesize[0]);
              var ytiles = Math.ceil(h / tilesize[1]);
  
              var cdfs = new Array(ytiles);
              for(var i=0;i&lt;ytiles;i++)
                  cdfs[i] = new Array(xtiles);
  
              var inv_tile_size = [1.0 / tilesize[0], 1.0 / tilesize[1]];
  
              var cdf = [];
              // create histograms
              for(var i=0;i&lt;ytiles;i++){
                  var y0 = i * tilesize[1];
                  var y1 = y0 + tilesize[1];
                  for(var j=0;j&lt;xtiles;j++)
                  {
                      var x0 = j * tilesize[0];
                      var x1 = x0 + tilesize[0];
                      var hist = histogram(intens, x0, y0, x1, y1, num_bins, h, w);
  
                      cdfs[i][j] = buildcdf(hist, num_bins, tilesize[0] * tilesize[1], clipLimit);
                  }
              }
  
              var finalArray = [];
              var p = 0;
              for(var y=0, idx=0;y&lt;h;++y){
                  for(var x=0;x&lt;w;++x, idx+=4){
                      // intensity of current pixel
                      var I = Math.floor(intens[y*w+x]);
  
                      var tx = x * inv_tile_size[0] - 0.5;
                      var ty = y * inv_tile_size[1] - 0.5;
  
                      var xl = Math.max(Math.floor(tx), 0);
                      var xr = Math.min(xl+1, xtiles-1);
  
                      var yt = Math.max(Math.floor(ty), 0);
                      var yd = Math.min(yt+1, ytiles-1);
  
                      var fx = tx - xl;
                      var fy = ty - yt;
  
                      var cdf11 = (cdfs[yt][xl][I]) * 255 ;
                      var cdf12 = (cdfs[yd][xl][I]) * 255;
                      var cdf21 = (cdfs[yt][xr][I]) * 255;
                      var cdf22 = (cdfs[yd][xr][I]) * 255;
  
                      var Iout = (1 - fx) * (1 - fy) * cdf11
                          + (1 - fx) *       fy  * cdf12
                          +      fx  * (1 - fy) * cdf21
                          +      fx  *      fy  * cdf22;
  
                      finalArray[p] = Iout;
                      p++;
                  }
              }   
              var pixelLength = h * w;
  
              for (var index = 0, indexToFill = 3; index &lt; pixelLength; index++, indexToFill = indexToFill + 4) {
                  var result = finalArray[index];
                  imgData.data[indexToFill-3] = 0;
                  imgData.data[indexToFill-2] = 0;
                  imgData.data[indexToFill-1] = 0;
                  imgData.data[indexToFill] = 255 - result;
              }
  
              ctx.putImageData(imgData,0,0);
          }
          img.src = event.target.result;
      }
      reader.readAsDataURL(e.target.files[0]); 
  }
  
  var getInputArray = function(mArray, x, y, hDiff, vDiff, rows, columns){
          var inputArray = [];
          var k = 0;
          for(var i=x-vDiff;i&lt;=x+vDiff;i++){
              for(var j=y-hDiff;j&lt;=y+hDiff;j++){
                  if(i&gt;=0 &amp;&amp; j&gt;=0 &amp;&amp; i&lt;rows &amp;&amp; j&lt;columns){
                      inputArray[k] = mArray[i][j];
                  }else{
                      inputArray[k] = 0;
                  }
                  k++;
              }
          }
          var finalResult = getHEArray(inputArray, x, y, mArray);
          return finalResult;
      }
  
      // build cdf from given pdf
      function buildcdf(hist, num_bins, num_pixels, clipLimit){
          var excess = 0;
          for(var i=0;i&lt;num_bins;++i){
              hist[i] = hist[i] / num_pixels;
              if(hist[i]&gt;clipLimit){
                  excess = excess + hist[i] - clipLimit;
                  hist[i] = clipLimit;
              }
          }
          var addExcess = excess/num_bins;
          var cumuhist = [];
          cumuhist[0] = hist[0] + addExcess;
          for(var i=1;i&lt;num_bins;++i)
              cumuhist[i] = cumuhist[i-1] + hist[i] + addExcess;
  
          return cumuhist;
      }
  
      function histogram(intens, x1, y1, x2, y2, num_bins, h, w){
          var hist = [];
          for(var i=0;i&lt;num_bins;++i)
              hist[i] = 0;
  
          for(var y=y1;y&lt;y2;++y){
              for(var x=x1;x&lt;x2;++x){
                  var idx;
                  if(x&gt;=w &amp;&amp; y&lt;h){
                      idx = (y * w + (w - 1));
                  }else if(y&gt;=h &amp;&amp; x&lt;w){
                      idx = ((h-1) * w + x);
                  }else if(y&gt;=h &amp;&amp; x&gt;=w){
                      idx = (h * w) - 1;
                  }else{
                      idx = (y * w + x);
                  }
                  var val = Math.floor(intens[idx]);
                  hist[val]++;
              }
          }
          return hist;
      }
  </code></pre>
  
  <p>`</p>