Image Filters

This example uses OpenLayers v4.0.1. The latest is v.

#### Image Filters

Apply a filter to imagery

Layer rendering can be manipulated in `precompose` and `postcompose` event listeners. These listeners get an event with a reference to the Canvas rendering context. In this example, the `postcompose` listener applies a filter to the image data.

Related API documentation:

- ol.Map
,
- ol.View
,
- ol.layer.Tile
,
- ol.proj
,
- ol.source.BingMaps

Copy Edit
Image Filters Apply a filter to imagery var key = 'Your Bing Maps Key from http://www.bingmapsportal.com/ here'; var imagery = new ol.layer.Tile({ source: new ol.source.BingMaps({key: key, imagerySet: 'Aerial'}) }); var map = new ol.Map({ layers: [imagery], target: 'map', view: new ol.View({ center: ol.proj.fromLonLat([-120, 50]), zoom: 6 }) }); var kernels = { none: [ 0, 0, 0, 0, 1, 0, 0, 0, 0 ], sharpen: [ 0, -1, 0, -1, 5, -1, 0, -1, 0 ], sharpenless: [ 0, -1, 0, -1, 10, -1, 0, -1, 0 ], blur: [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ], shadow: [ 1, 2, 1, 0, 1, 0, -1, -2, -1 ], emboss: [ -2, 1, 0, -1, 1, 1, 0, 1, 2 ], edge: [ 0, 1, 0, 1, -4, 1, 0, 1, 0 ] }; function normalize(kernel) { var len = kernel.length; var normal = new Array(len); var i, sum = 0; for (i = 0; i < len; ++i) { sum += kernel[i]; } if (sum <= 0) { normal.normalized = false; sum = 1; } else { normal.normalized = true; } for (i = 0; i < len; ++i) { normal[i] = kernel[i] / sum; } return normal; } var select = document.getElementById('kernel'); var selectedKernel = normalize(kernels[select.value]); /** * Update the kernel and re-render on change. */ select.onchange = function() { selectedKernel = normalize(kernels[select.value]); map.render(); }; /** * Apply a filter on "postcompose" events. */ imagery.on('postcompose', function(event) { convolve(event.context, selectedKernel); }); /** * Apply a convolution kernel to canvas. This works for any size kernel, but * performance starts degrading above 3 x 3. * @param {CanvasRenderingContext2D} context Canvas 2d context. * @param {Array.<number>} kernel Kernel. */ function convolve(context, kernel) { var canvas = context.canvas; var width = canvas.width; var height = canvas.height; var size = Math.sqrt(kernel.length); var half = Math.floor(size / 2); var inputData = context.getImageData(0, 0, width, height).data; var output = context.createImageData(width, height); var outputData = output.data; for (var pixelY = 0; pixelY < height; ++pixelY) { var pixelsAbove = pixelY * width; for (var pixelX = 0; pixelX < width; ++pixelX) { var r = 0, g = 0, b = 0, a = 0; for (var kernelY = 0; kernelY < size; ++kernelY) { for (var kernelX = 0; kernelX < size; ++kernelX) { var weight = kernel[kernelY * size + kernelX]; var neighborY = Math.min( height - 1, Math.max(0, pixelY + kernelY - half)); var neighborX = Math.min( width - 1, Math.max(0, pixelX + kernelX - half)); var inputIndex = (neighborY * width + neighborX) * 4; r += inputData[inputIndex] * weight; g += inputData[inputIndex + 1] * weight; b += inputData[inputIndex + 2] * weight; a += inputData[inputIndex + 3] * weight; } } var outputIndex = (pixelsAbove + pixelX) * 4; outputData[outputIndex] = r; outputData[outputIndex + 1] = g; outputData[outputIndex + 2] = b; outputData[outputIndex + 3] = kernel.normalized ? a : 255; } } context.putImageData(output, 0, 0); } <div id="map" class="map"></div> <select id="kernel" name="kernel"> <option>none</option> <option selected>sharpen</option> <option value="sharpenless">sharpen less</option> <option>blur</option> <option>shadow</option> <option>emboss</option> <option value="edge">edge detect</option> </select>

    <!DOCTYPE html>
    <html>
      <head>
        <title>Image Filters</title>
        <link rel="stylesheet" href="https://openlayers.org/en/v4.0.1/css/ol.css" type="text/css">
        <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
        <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
        <script src="https://openlayers.org/en/v4.0.1/build/ol.js"></script>
      </head>
      <body>
        <div id="map" class="map"></div>
        <select id="kernel" name="kernel">
          <option>none</option>
          <option selected>sharpen</option>
          <option value="sharpenless">sharpen less</option>
          <option>blur</option>
          <option>shadow</option>
          <option>emboss</option>
          <option value="edge">edge detect</option>
        </select>
        <script>
          var key = 'Your Bing Maps Key from http://www.bingmapsportal.com/ here';

          var imagery = new ol.layer.Tile({
            source: new ol.source.BingMaps({key: key, imagerySet: 'Aerial'})
          });

          var map = new ol.Map({
            layers: [imagery],
            target: 'map',
            view: new ol.View({
              center: ol.proj.fromLonLat([-120, 50]),
              zoom: 6
            })
          });

          var kernels = {
            none: [
              0, 0, 0,
              0, 1, 0,
              0, 0, 0
            ],
            sharpen: [
              0, -1, 0,
              -1, 5, -1,
              0, -1, 0
            ],
            sharpenless: [
              0, -1, 0,
              -1, 10, -1,
              0, -1, 0
            ],
            blur: [
              1, 1, 1,
              1, 1, 1,
              1, 1, 1
            ],
            shadow: [
              1, 2, 1,
              0, 1, 0,
              -1, -2, -1
            ],
            emboss: [
              -2, 1, 0,
              -1, 1, 1,
              0, 1, 2
            ],
            edge: [
              0, 1, 0,
              1, -4, 1,
              0, 1, 0
            ]
          };

          function normalize(kernel) {
            var len = kernel.length;
            var normal = new Array(len);
            var i, sum = 0;
            for (i = 0; i < len; ++i) {
              sum += kernel[i];
            }
            if (sum <= 0) {
              normal.normalized = false;
              sum = 1;
            } else {
              normal.normalized = true;
            }
            for (i = 0; i < len; ++i) {
              normal[i] = kernel[i] / sum;
            }
            return normal;
          }

          var select = document.getElementById('kernel');
          var selectedKernel = normalize(kernels[select.value]);

          /**
           * Update the kernel and re-render on change.
           */
          select.onchange = function() {
            selectedKernel = normalize(kernels[select.value]);
            map.render();
          };

          /**
           * Apply a filter on "postcompose" events.
           */
          imagery.on('postcompose', function(event) {
            convolve(event.context, selectedKernel);
          });

          /**
           * Apply a convolution kernel to canvas.  This works for any size kernel, but
           * performance starts degrading above 3 x 3.
           * @param {CanvasRenderingContext2D} context Canvas 2d context.
           * @param {Array.<number>} kernel Kernel.
           */
          function convolve(context, kernel) {
            var canvas = context.canvas;
            var width = canvas.width;
            var height = canvas.height;

            var size = Math.sqrt(kernel.length);
            var half = Math.floor(size / 2);

            var inputData = context.getImageData(0, 0, width, height).data;

            var output = context.createImageData(width, height);
            var outputData = output.data;

            for (var pixelY = 0; pixelY < height; ++pixelY) {
              var pixelsAbove = pixelY * width;
              for (var pixelX = 0; pixelX < width; ++pixelX) {
                var r = 0, g = 0, b = 0, a = 0;
                for (var kernelY = 0; kernelY < size; ++kernelY) {
                  for (var kernelX = 0; kernelX < size; ++kernelX) {
                    var weight = kernel[kernelY * size + kernelX];
                    var neighborY = Math.min(
                        height - 1, Math.max(0, pixelY + kernelY - half));
                    var neighborX = Math.min(
                        width - 1, Math.max(0, pixelX + kernelX - half));
                    var inputIndex = (neighborY * width + neighborX) * 4;
                    r += inputData[inputIndex] * weight;
                    g += inputData[inputIndex + 1] * weight;
                    b += inputData[inputIndex + 2] * weight;
                    a += inputData[inputIndex + 3] * weight;
                  }
                }
                var outputIndex = (pixelsAbove + pixelX) * 4;
                outputData[outputIndex] = r;
                outputData[outputIndex + 1] = g;
                outputData[outputIndex + 2] = b;
                outputData[outputIndex + 3] = kernel.normalized ? a : 255;
              }
            }
            context.putImageData(output, 0, 0);
          }
        </script>
      </body>
    </html>