Canvas Image Filters

A filter changes the colour of a pixel based on a manipulation of the original colour of the pixel. Each pixel is processed independently of all other pixels. Therefore, the new colour of any pixel is only dependent on the original colour of that pixel. Common filters include:

Brightness

To make an image brighter, we need to increase each of the red, green and blue values. We do this by multiplying by a brightness factor, where:

Example of a brightness filter (Run Example)

<!DOCTYPE html>
<html>
    <head>
        <title>Course notes example code</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <style>
            img,
            canvas
            {
                width:500px;
                height:500px;
                border:thin solid black;
            }

            #loadingMessage
            {
                position:absolute;
                top:100px;
                left:100px;
                z-index:100;
                font-size:50px;
            }
        </style>

        <script>
            let canvas = null
            let ctx = null
            let width = null
            let height = null
            let originalImage = null

            window.onload = onAllAssetsLoaded
            document.write("<div id='loadingMessage'>Loading...</div>")
            function onAllAssetsLoaded()
            {
                // hide the webpage loading message
                document.getElementById('loadingMessage').style.visibility = "hidden"

                originalImage = document.getElementById('originalImage')
                canvas = document.getElementById('canvas')
                ctx = canvas.getContext('2d')
                width = originalImage.clientWidth
                height = originalImage.clientHeight
                canvas.width = width
                canvas.height = height

                renderCanvas()
            }

            let imageData = null
            let data = null
            let brightnessFactor = 2 /* A higher brightnessFactor value will make the image brighter */
            function renderCanvas()
            {
                ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height)
                imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
                data = imageData.data

                // Loop through the pixels, turning them grayscale
                for (let i = 0; i < data.length; i += 4)
                {
                    data[i] *= brightnessFactor
                    data[i + 1] *= brightnessFactor
                    data[i + 2] *= brightnessFactor
                }

                ctx.putImageData(imageData, 0, 0)
            }
        </script>
    </head>

    <body>
        <img id = 'originalImage' src = 'images/dancing.png'>
        <canvas id = 'canvas'></canvas>
    </body>
</html>

Modify the code above so that the user can use a slider to adjust the brightness of an image.

Modify the code above so that we can independently adjust the brightness of the red, green and blue colour.

Greyscale

By setting the red, green and blue of a pixel to be the same value, we produce a greyscale pixel. A good value to set as the greyscale is the average of the red, green and blue values, as shown in the example below.

Example of a greyscale filter (Run Example).

<!DOCTYPE html>
<html>
    <head>
        <title>Course notes example code</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <style>
            img,
            canvas
            {
                width:500px;
                height:500px;
                border:thin solid black;
            }

            #loadingMessage
            {
                position:absolute;
                top:100px;
                left:100px;
                z-index:100;
                font-size:50px;
            }
        </style>

        <script>
            let canvas = null
            let ctx = null
            let width = null
            let height = null
            let originalImage = null
            let imageData = null
            let data = null

            window.onload = onAllAssetsLoaded
            document.write("<div id='loadingMessage'>Loading...</div>")
            function onAllAssetsLoaded()
            {
                // hide the webpage loading message
                document.getElementById('loadingMessage').style.visibility = "hidden"

                originalImage = document.getElementById('originalImage')
                canvas = document.getElementById('canvas')
                ctx = canvas.getContext('2d')
                width = originalImage.clientWidth
                height = originalImage.clientHeight
                canvas.width = width
                canvas.height = height

                renderCanvas()
            }


            function renderCanvas()
            {
                ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height)
                imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
                data = imageData.data

                // convert to greyscale
                let brightnessFactor = 1  // we can add a brightness factor to the greyscale
                // Loop through the pixels, turning them into grayscale
                for (let i = 0; i < data.length; i += 4)
                {
                    // get the average value
                    grayScale = ((data[i] * brightnessFactor) + (data[i + 1] * brightnessFactor) + (data[i + 2] * brightnessFactor)) / 3
                    // assign the same value to red, green and blue to create grayScale
                    data[i] = grayScale
                    data[i + 1] = grayScale
                    data[i + 2] = grayScale
                }

                ctx.putImageData(imageData, 0, 0)
            }
        </script>
    </head>

    <body>
        <img id = 'originalImage' src = 'images/dancing.png'>
        <canvas id = 'canvas'></canvas>
    </body>
</html>

Modify the code above so that the image is in a blue-scale, as shown at this link.

The code above uses the average of the red, green and blue values. What other values could be used to produce the greyscale? What would be the effect of using these other values?

Sepia

Sepia is a reddish-brown colour. It is commonly used to make images look older.

Example of a sepia filter (Run example)

<!DOCTYPE html>
<html>
    <head>
        <title>Course notes example code</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <style>
            img,
            canvas
            {
                width:500px;
                height:500px;
                border:thin solid black;
            }

            #loadingMessage
            {
                position:absolute;
                top:100px;
                left:100px;
                z-index:100;
                font-size:50px;
            }
        </style>

        <script>
            let canvas = null
            let ctx = null
            let width = null
            let height = null
            let originalImage = null
            let imageData = null
            let data = null

            window.onload = onAllAssetsLoaded
            document.write("<div id='loadingMessage'>Loading...</div>")
            function onAllAssetsLoaded()
            {
                // hide the webpage loading message
                document.getElementById('loadingMessage').style.visibility = "hidden"

                originalImage = document.getElementById('originalImage')
                canvas = document.getElementById('canvas')
                ctx = canvas.getContext('2d')
                width = originalImage.clientWidth
                height = originalImage.clientHeight
                canvas.width = width
                canvas.height = height

                renderCanvas()
            }


            function renderCanvas()
            {
                ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height)
                imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
                data = imageData.data

                // Loop through the pixels, turning them into sepia
                for (let i = 0; i < data.length; i += 4)
                {
                    red = data[i]
                    green = data[i + 1]
                    blue = data[i + 2]

                    data[i] = (red * 0.393) + (green * 0.769) + (blue * 0.189)
                    data[i + 1] = (red * 0.349) + (green * 0.686) + (blue * 0.168)
                    data[i + 2] = (red * 0.272) + (green * 0.534) + (blue * 0.131)
                }

                ctx.putImageData(imageData, 0, 0)
            }
        </script>
    </head>

    <body>
        <img id = 'originalImage' src = 'images/dancing.png'>
        <canvas id = 'canvas'></canvas>
    </body>
</html>

Adjust the scale values in the code above to create other filters.

Invert

Example of an invert filter (Run Example)

<!DOCTYPE html>
<html>
    <head>
        <title>Course notes example code</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <style>
            img,
            canvas
            {
                width:500px;
                height:500px;
                border:thin solid black;
            }

            #loadingMessage
            {
                position:absolute;
                top:100px;
                left:100px;
                z-index:100;
                font-size:50px;
            }
        </style>

        <script>
            let canvas = null
            let ctx = null
            let width = null
            let height = null
            let originalImage = null
            let imageData = null
            let data = null

            window.onload = onAllAssetsLoaded
            document.write("<div id='loadingMessage'>Loading...</div>")
            function onAllAssetsLoaded()
            {
                // hide the webpage loading message
                document.getElementById('loadingMessage').style.visibility = "hidden"

                originalImage = document.getElementById('originalImage')
                canvas = document.getElementById('canvas')
                ctx = canvas.getContext('2d')
                width = originalImage.clientWidth
                height = originalImage.clientHeight
                canvas.width = width
                canvas.height = height

                renderCanvas()
            }


            function renderCanvas()
            {
                ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height)
                imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
                data = imageData.data

                // do the invert
                for (let i = 0; i < data.length; i += 4)
                {
                    data[i + 0] = 255 - data[i + 0]
                    data[i + 1] = 255 - data[i + 1]
                    data[i + 2] = 255 - data[i + 2]
                    data[i + 3] = 255
                }

                ctx.putImageData(imageData, 0, 0)
            }
        </script>
    </head>

    <body>
        <img id = 'originalImage' src = 'images/dancing.png'>
        <canvas id = 'canvas'></canvas>
    </body>
</html>

Modify the code above to invert only on red, as shown at this link.

Modify the code above to invert only on yellow, as shown at this link.

Posterise

A posterised image only uses some of the available colour set. The example below only uses red, green and blue values have a step size of 64.

Example of a posterise filter (Run Example)

<!DOCTYPE html>
<html>
    <head>
        <title>Course notes example code</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <style>
            img,
            canvas
            {
                width:500px;
                height:500px;
                border:thin solid black;
            }

            #loadingMessage
            {
                position:absolute;
                top:100px;
                left:100px;
                z-index:100;
                font-size:50px;
            }
        </style>

        <script>
            let canvas = null
            let ctx = null
            let width = null
            let height = null
            let originalImage = null
            let imageData = null
            let data = null

            window.onload = onAllAssetsLoaded
            document.write("<div id='loadingMessage'>Loading...</div>")
            function onAllAssetsLoaded()
            {
                // hide the webpage loading message
                document.getElementById('loadingMessage').style.visibility = "hidden"

                originalImage = document.getElementById('originalImage')
                canvas = document.getElementById('canvas')
                ctx = canvas.getContext('2d')
                width = originalImage.clientWidth
                height = originalImage.clientHeight
                canvas.width = width
                canvas.height = height

                renderCanvas()
            }


            function renderCanvas()
            {
                ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height)
                imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
                data = imageData.data

                // do the invert
                for (let i = 0; i < data.length; i += 4)
                {
                    data[i + 0] = data[i + 0] - data[i + 0] % 64
                    data[i + 1] = data[i + 1] - data[i + 1] % 64
                    data[i + 2] = data[i + 2] - data[i + 2] % 64
                    data[i + 3] = 255
                }

                ctx.putImageData(imageData, 0, 0)
            }
        </script>
    </head>

    <body>
        <img id = 'originalImage' src = 'images/dancing.png'>
        <canvas id = 'canvas'></canvas>
    </body>
</html>

Modify the code above so that the posterised colours have a step size of 128.

Threshold

A threshold filter maps each red, green and blue value to be either have zero or full saturation (ie values of 0 or 255).

Example of a threshold lookup filter (Run Example)

<!DOCTYPE html>
<html>
    <head>
        <title>Course notes example code</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <style>
            img,
            canvas
            {
                width:500px;
                height:500px;
                border:thin solid black;
            }

            #loadingMessage
            {
                position:absolute;
                top:100px;
                left:100px;
                z-index:100;
                font-size:50px;
            }
        </style>

        <script>
            let canvas = null
            let ctx = null
            let width = null
            let height = null
            let originalImage = null
            let imageData = null
            let data = null

            window.onload = onAllAssetsLoaded
            document.write("<div id='loadingMessage'>Loading...</div>")
            function onAllAssetsLoaded()
            {
                // hide the webpage loading message
                document.getElementById('loadingMessage').style.visibility = "hidden"

                originalImage = document.getElementById('originalImage')
                canvas = document.getElementById('canvas')
                ctx = canvas.getContext('2d')
                width = originalImage.clientWidth
                height = originalImage.clientHeight
                canvas.width = width
                canvas.height = height

                renderCanvas()
            }


            function renderCanvas()
            {

                ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height)
                imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
                data = imageData.data


                // do the threshold
                for (let i = 0; i < data.length; i += 4)
                {
                    for (let rgb = 0; rgb < 3; rgb++)
                    {
                        if (data[i + rgb] < 128)
                        {
                            data[i + rgb] = 0
                        }
                        else
                        {
                            data[i + rgb] = 255
                        }
                    }
                    data[i + 3] = 255
                }

                ctx.putImageData(imageData, 0, 0)
            }
        </script>
    </head>

    <body>
        <img id = 'originalImage' src = 'images/dancing.png'>
        <canvas id = 'canvas'></canvas>
    </body>
</html>

Modify the above code to only have a threshold in blue, as shown at this link.

 
<div align="center"><a href="../versionC/index.html" title="DKIT Lecture notes homepage for Derek O&#39; Reilly, Dundalk Institute of Technology (DKIT), Dundalk, County Louth, Ireland. Copyright Derek O&#39; Reilly, DKIT." target="_parent" style='font-size:0;color:white;background-color:white'>&nbsp;</a></div>